diff --git a/AndroidKernel.mk b/AndroidKernel.mk new file mode 100644 index 00000000000..3ee63c06058 --- /dev/null +++ b/AndroidKernel.mk @@ -0,0 +1,91 @@ +#Android makefile to build kernel as a part of Android Build +PERL = perl + +ifeq ($(TARGET_PREBUILT_KERNEL),) + +KERNEL_OUT := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ +KERNEL_CONFIG := $(KERNEL_OUT)/.config +TARGET_PREBUILT_INT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/zImage +KERNEL_HEADERS_INSTALL := $(KERNEL_OUT)/usr +KERNEL_MODULES_INSTALL := system +KERNEL_MODULES_OUT := $(TARGET_OUT)/lib/modules +KERNEL_IMG=$(KERNEL_OUT)/arch/arm/boot/Image + +MSM_ARCH ?= $(shell $(PERL) -e 'while (<>) {$$a = $$1 if /CONFIG_ARCH_((?:MSM|QSD)[a-zA-Z0-9]+)=y/; $$r = $$1 if /CONFIG_MSM_SOC_REV_(?!NONE)(\w+)=y/;} print lc("$$a$$r\n");' $(KERNEL_CONFIG)) +KERNEL_USE_OF ?= $(shell $(PERL) -e '$$of = "n"; while (<>) { if (/CONFIG_USE_OF=y/) { $$of = "y"; break; } } print $$of;' kernel/arch/arm/configs/$(KERNEL_DEFCONFIG)) + +ifeq "$(KERNEL_USE_OF)" "y" +DTS_NAME ?= $(MSM_ARCH) +DTS_FILES = $(wildcard $(TOP)/kernel/arch/arm/boot/dts/$(DTS_NAME)*.dts) +DTS_FILE = $(lastword $(subst /, ,$(1))) +DTB_FILE = $(addprefix $(KERNEL_OUT)/arch/arm/boot/,$(patsubst %.dts,%.dtb,$(call DTS_FILE,$(1)))) +ZIMG_FILE = $(addprefix $(KERNEL_OUT)/arch/arm/boot/,$(patsubst %.dts,%-zImage,$(call DTS_FILE,$(1)))) +KERNEL_ZIMG = $(KERNEL_OUT)/arch/arm/boot/zImage +DTC = $(KERNEL_OUT)/scripts/dtc/dtc + +define append-dtb +mkdir -p $(KERNEL_OUT)/arch/arm/boot;\ +$(foreach d, $(DTS_FILES), \ + $(DTC) -p 1024 -O dtb -o $(call DTB_FILE,$(d)) $(d); \ + cat $(KERNEL_ZIMG) $(call DTB_FILE,$(d)) > $(call ZIMG_FILE,$(d));) +endef +else + +define append-dtb +endef +endif + +ifeq ($(TARGET_USES_UNCOMPRESSED_KERNEL),true) +$(info Using uncompressed kernel) +TARGET_PREBUILT_KERNEL := $(KERNEL_OUT)/piggy +else +TARGET_PREBUILT_KERNEL := $(TARGET_PREBUILT_INT_KERNEL) +endif + +define mv-modules +mdpath=`find $(KERNEL_MODULES_OUT) -type f -name modules.dep`;\ +if [ "$$mdpath" != "" ];then\ +mpath=`dirname $$mdpath`;\ +ko=`find $$mpath/kernel -type f -name *.ko`;\ +for i in $$ko; do mv $$i $(KERNEL_MODULES_OUT)/; done;\ +fi +endef + +define clean-module-folder +mdpath=`find $(KERNEL_MODULES_OUT) -type f -name modules.dep`;\ +if [ "$$mdpath" != "" ];then\ +mpath=`dirname $$mdpath`; rm -rf $$mpath;\ +fi +endef + +$(KERNEL_OUT): + mkdir -p $(KERNEL_OUT) + +$(KERNEL_CONFIG): $(KERNEL_OUT) + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- $(KERNEL_DEFCONFIG) + +$(KERNEL_OUT)/piggy : $(TARGET_PREBUILT_INT_KERNEL) + $(hide) gunzip -c $(KERNEL_OUT)/arch/arm/boot/compressed/piggy.gzip > $(KERNEL_OUT)/piggy + +$(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_CONFIG) $(KERNEL_HEADERS_INSTALL) + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- modules + $(MAKE) -C kernel O=../$(KERNEL_OUT) INSTALL_MOD_PATH=../../$(KERNEL_MODULES_INSTALL) ARCH=arm CROSS_COMPILE=arm-eabi- modules_install + $(mv-modules) + $(clean-module-folder) + $(append-dtb) + +$(KERNEL_HEADERS_INSTALL): $(KERNEL_OUT) $(KERNEL_CONFIG) + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- headers_install + +kerneltags: $(KERNEL_OUT) $(KERNEL_CONFIG) + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- tags + +kernelconfig: $(KERNEL_OUT) $(KERNEL_CONFIG) + env KCONFIG_NOTIMESTAMP=true \ + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- menuconfig + env KCONFIG_NOTIMESTAMP=true \ + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- savedefconfig + cp $(KERNEL_OUT)/defconfig kernel/arch/arm/configs/$(KERNEL_DEFCONFIG) + +endif diff --git a/Documentation/ABI/testing/sysfs-bus-pil b/Documentation/ABI/testing/sysfs-bus-pil new file mode 100644 index 00000000000..797b2ea29b7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-pil @@ -0,0 +1,18 @@ +What: /sys/bus/pil/devices/.../name +Date: March 2012 +Contact: Stephen Boyd +Description: + Shows the name of the peripheral used in pil_get(). + +What: /sys/bus/pil/devices/.../state +Date: March 2012 +Contact: Stephen Boyd +Description: + Shows the state state of a peripheral. Current states + supported are: + + OFFLINE - peripheral is offline + ONLINE - peripheral is online + + This file supports poll() to detect when a peripheral changes + state. diff --git a/Documentation/arm/msm/gpiomux.txt b/Documentation/arm/msm/gpiomux.txt index 67a81620adf..aaf0793be07 100644 --- a/Documentation/arm/msm/gpiomux.txt +++ b/Documentation/arm/msm/gpiomux.txt @@ -2,112 +2,79 @@ This document provides an overview of the msm_gpiomux interface, which is used to provide gpio pin multiplexing and configuration on mach-msm targets. -History -======= - -The first-generation API for gpio configuration & multiplexing on msm -is the function gpio_tlmm_config(). This function has a few notable -shortcomings, which led to its deprecation and replacement by gpiomux: - -The 'disable' parameter: Setting the second parameter to -gpio_tlmm_config to GPIO_CFG_DISABLE tells the peripheral -processor in charge of the subsystem to perform a look-up into a -low-power table and apply the low-power/sleep setting for the pin. -As the msm family evolved this became problematic. Not all pins -have sleep settings, not all peripheral processors will accept requests -to apply said sleep settings, and not all msm targets have their gpio -subsystems managed by a peripheral processor. In order to get consistent -behavior on all targets, drivers are forced to ignore this parameter, -rendering it useless. - -The 'direction' flag: for all mux-settings other than raw-gpio (0), -the output-enable bit of a gpio is hard-wired to a known -input (usually VDD or ground). For those settings, the direction flag -is meaningless at best, and deceptive at worst. In addition, using the -direction flag to change output-enable (OE) directly can cause trouble in -gpiolib, which has no visibility into gpio direction changes made -in this way. Direction control in gpio mode should be made through gpiolib. - -Key Features of gpiomux -======================= - -- A consistent interface across all generations of msm. Drivers can expect -the same results on every target. -- gpiomux plays nicely with gpiolib. Functions that should belong to gpiolib -are left to gpiolib and not duplicated here. gpiomux is written with the -intent that gpio_chips will call gpiomux reference-counting methods -from their request() and free() hooks, providing full integration. -- Tabular configuration. Instead of having to call gpio_tlmm_config -hundreds of times, gpio configuration is placed in a single table. -- Per-gpio sleep. Each gpio is individually reference counted, allowing only -those lines which are in use to be put in high-power states. -- 0 means 'do nothing': all flags are designed so that the default memset-zero -equates to a sensible default of 'no configuration', preventing users -from having to provide hundreds of 'no-op' configs for unused or -unwanted lines. - Usage ===== -To use gpiomux, provide configuration information for relevant gpio lines -in the msm_gpiomux_configs table. Since a 0 equates to "unconfigured", -only those lines to be managed by gpiomux need to be specified. Here -is a completely fictional example: +To use gpiomux, do the following before the msmgpio gpiochips probe: -struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = { - [12] = { - .active = GPIOMUX_VALID | GPIOMUX_DRV_8MA | GPIOMUX_FUNC_1, - .suspended = GPIOMUX_VALID | GPIOMUX_PULL_DOWN, - }, - [34] = { - .suspended = GPIOMUX_VALID | GPIOMUX_PULL_DOWN, +- Call msm_gpiomux_init to allocate needed resources. +- Install one or more sets of gpiomux configuration data via + msm_gpiomux_install and/or msm_gpiomux_write. + +Failing to finish these steps before the probe of msmgpio can result in calls +from msmgpio to gpiomux to try and activate lines which have not yet +been configured. + +A basic gpiomux setting is described by a gpiomux_setting structure. +A gpiomux configuration is a group of those settings (one for each power +state of the board) paired with a specific gpio, like so: + +struct msm_gpiomux_config gpio123_config __initdata = { + .gpio = 123, + .settings = { + [GPIOMUX_ACTIVE] = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_OUT_HIGH, + }, + [GPIOMUX_SUSPENDED] = { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, + }, }, }; -To indicate that a gpio is in use, call msm_gpiomux_get() to increase -its reference count. To decrease the reference count, call msm_gpiomux_put(). - The effect of this configuration is as follows: -When the system boots, gpios 12 and 34 will be initialized with their -'suspended' configurations. All other gpios, which were left unconfigured, -will not be touched. +- When the system boots, gpio 123 will be put into the SUSPENDED setting. +- When the reference count for gpio 123 rises above 0, the ACTIVE setting + will be applied. +- When the reference count falls back to 0, the SUSPENDED setting will be + reapplied. -When msm_gpiomux_get() is called on gpio 12 to raise its reference count -above 0, its active configuration will be applied. Since no other gpio -line has a valid active configuration, msm_gpiomux_get() will have no -effect on any other line. +The reference count rises when msm_gpiomux_get() is called and falls +when msm_gpiomux_put() is called. msmgpio has hooks to these functions +in its gpiolib implementation. This means that when you call gpio_request() +on an msmgpio, msm_gpiomux_get() is automatically called on your behalf. +Similarly, when you call gpio_free(), msm_gpiomux_put() is called for you. +This allows generic drivers to obtain low-level management of msmgpio lines +without having to be aware of the gpiomux layer. -When msm_gpiomux_put() is called on gpio 12 or 34 to drop their reference -count to 0, their suspended configurations will be applied. -Since no other gpio line has a valid suspended configuration, no other -gpio line will be effected by msm_gpiomux_put(). Since gpio 34 has no valid -active configuration, this is effectively a no-op for gpio 34 as well, -with one small caveat, see the section "About Output-Enable Settings". +Note that the .dir field is ignored if .func != GPIOMUX_FUNC_GPIO, since +software control of gpios is allowed only in GPIO mode. By selecting any +other .func, you assign the gpio to another piece of hardware and lose +control of it from gpiolib. You can still reserve such gpios with gpio_request +to prevent other modules from using them while they're in such a state, +but other gpiolib functions will not behave as you expect if .func != GPIO. -All of the GPIOMUX_VALID flags may seem like unnecessary overhead, but -they address some important issues. As unused entries (all those -except 12 and 34) are zero-filled, gpiomux needs a way to distinguish -the used fields from the unused. In addition, the all-zero pattern -is a valid configuration! Therefore, gpiomux defines an additional bit -which is used to indicate when a field is used. This has the pleasant -side-effect of allowing calls to msm_gpiomux_write to use '0' to indicate -that a value should not be changed: - - msm_gpiomux_write(0, GPIOMUX_VALID, 0); - -replaces the active configuration of gpio 0 with an all-zero configuration, -but leaves the suspended configuration as it was. +If a configuration is omitted, nothing will happen at the relevant transitions. +This allows for the creation of 'static configurations' which do not +change as the line is requested and freed. Static Configurations ===================== To install a static configuration, which is applied at boot and does not change after that, install a configuration with a suspended component -but no active component, as in the previous example: +but no active component: - [34] = { - .suspended = GPIOMUX_VALID | GPIOMUX_PULL_DOWN, + .gpio = ..., + .settings = { + [GPIOMUX_SUSPENDED] = { + ... + }, }, The suspended setting is applied during boot, and the lack of any valid @@ -153,24 +120,3 @@ This provides important functionality: This mechanism allows for "auto-request" of gpiomux lines via gpiolib when it is suitable. Drivers wishing more exact control are, of course, free to also use msm_gpiomux_set and msm_gpiomux_get. - -About Output-Enable Settings -============================ - -Some msm targets do not have the ability to query the current gpio -configuration setting. This means that changes made to the output-enable -(OE) bit by gpiolib cannot be consistently detected and preserved by gpiomux. -Therefore, when gpiomux applies a configuration setting, any direction -settings which may have been applied by gpiolib are lost and the default -input settings are re-applied. - -For this reason, drivers should not assume that gpio direction settings -continue to hold if they free and then re-request a gpio. This seems like -common sense - after all, anybody could have obtained the line in the -meantime - but it needs saying. - -This also means that calls to msm_gpiomux_write will reset the OE bit, -which means that if the gpio line is held by a client of gpiolib and -msm_gpiomux_write is called, the direction setting has been lost and -gpiolib's internal state has been broken. -Release gpio lines before reconfiguring them. diff --git a/Documentation/arm/msm/pil.txt b/Documentation/arm/msm/pil.txt new file mode 100644 index 00000000000..5b0b527a6aa --- /dev/null +++ b/Documentation/arm/msm/pil.txt @@ -0,0 +1,267 @@ +Introduction +============ + +The PIL (Peripheral Image Loader) driver loads peripheral images into memory +and interfaces with the Peripheral Authentication Service (PAS) to +authenticate and reset peripherals embedded in the SoC. + +The PAS could either be running under secure mode in the application +processor (secure boot support) or be running as a non-secure kernel driver +(non-secure boot support). + +The PIL driver also does housekeeping to handle cases where more than one +client driver is using the same peripheral. + +Some examples of peripherals are modem, DSP and sensors. + +Hardware description +==================== + +The memory used by the peripherals for code and data storage will be +accessible as normal memory to the application processor. + +The non-secure code (Linux kernel) will have read/write permissions to the +peripheral memory by default. + +The PAS will have access to a MPU (memory protection unit) that can lock away +the pages of memory from the Linux kernel. It will also have access to +registers that can reset each peripheral. + +Software description +==================== + +The PAS provides the following three APIs: + +* Init image - Takes as input the peripheral id and firmware metadata and + returns a status indicating the authenticity of the firmware metadata. The + firmware metadata consists of a standard ELF32 header followed by a program + header table and an optional blob of data used to authenticate the metadata + and the rest of the firmware. + +* Verify segment - Takes as input the firmware segment id and the length of + the segment. Authenticates whatever amount (specified by the "length" + parameter) of the firmware segment that has been loaded and removes + non-secure mode read/write permissions for the pages belonging to the + firmware segment. Allows multiple calls for the same firmware segment to + allow partial loading and authentication. + +* Auth and Reset - Verifies all the necessary firmware segments have been + loaded and authenticated and then resets the peripheral. + +The user space is expected to provide the firmware metadata and firmware +segments as separate files on persistent storage. See "Interface" section for +further details. + +The PIL driver will use the request_firmware API provided by the Linux kernel +to read the firmware and firmware metadata from persistent storage. + +When a client driver requests for a peripheral to be enabled, the PIL driver +increments the reference count for that peripheral, loads the firmware +metadata and calls the PAS Init Image API that initializes the authentication +state machine using the firmware metadata. + +If the initialization succeeds, the PIL driver loads the appropriate firmware +segments into their respective memory locations and call the PAS Verify +segment API on each of the loaded segments to authenticate and lock it. + +After all the firmware segments have been successfully loaded and +authenticated, the PAS Auth and Reset API is called to reset the peripheral +and initiate its boot sequence. + +A peripheral enable request to the PIL driver will block until it succeeds +(or fails) to initiate the peripheral boot sequence but will NOT block until +the peripheral is ready. It is not possible to block until a peripheral is +ready since the semantics of "ready" is subjective to the caller. + +The PIL driver will maintain a reference count for each of the peripherals. +So, if a peripheral is already under use and another client driver requests +for the peripheral to be enabled, the PIL driver will immediately return a +value to indicate success. + +When all the client drivers of a particular peripheral no longer need the +peripheral and the reference count reaches zero, the PIL driver can cleanly +shut down the peripheral. Since a lot of drivers in their current state can't +handle a peripheral restart, the PIL driver will never let the reference +count go back to zero. + +All information about a peripheral, like firmware filenames, peripheral ID +passed to PAS, etc, will be hard coded in the PIL driver. + +All the PIL APIs will execute in the context of the caller. This includes +calls from the PIL driver to the PAS driver. The PAS driver might decide to +switch into secure mode from a separate workqueue or in the same context as +the caller, but that shouldn't have any implications for the PIL API callers +since all the PIL APIs are blocking calls. + +Dependencies: +------------- +* Firmware class (CONFIG_FW_LOADER) for using the request_firmware API to + load firmware from persistent storage. +* PAS to authenticate firmware and bring a peripheral out of reset. + +Error cases: +------------ +The PIL driver could fail to enable a peripheral for several reasons like not +having enough memory to load firmware and metadata, being unable to +communicate with the PAS, the PAS returning with an error, etc. For all +possible error cases, the PIL driver does not perform any retries and returns +an appropriate error code. The client drivers should always check for success +before trying to access the peripheral. + +Design +====== + +Design goals: +------------- +* The PIL driver must be agnostic to the actual format and method used to + authenticate the firmware. +* Allow for future expansion to support demand loading of parts of firmware + for each peripheral. +* Move most of the work into the preprocessing/building stage of the firmware. +* Provide an API to the client drivers that absolves them from having to know + the structure or names of the firmware in persistent storage. +* Handle multiple client drivers wanting to enable the same peripheral. + + +Design reasons: +--------------- +The user space is expected to provide the firmware metadata and segments as +separate files for the following reasons: +* Don't need to load the whole ELF file if the authentication info is + invalid. +* Works better during low memory conditions since the amount of memory used + at any given instant when loading one segment at a time is smaller than + loading the whole ELF file. +* Since an ELF segment in memory can be much bigger than on file, having a + flat binary would waste a lot of space due to zero-fills. +* Allows for future enhancements to the loading procedure. + +Design tradeoffs: +----------------- +* With appropriate changes to the request_firmware API, the firmware blobs + could be directly loaded into the right memory location. But due to the + additional work and community approval that would be needed for modifying + the request_firmware API, we load the firmware blobs into kernel memory and + then copy them into the appropriate locations. + +Alternate designs: +------------------ +One of the alternate designs that were considered required the firmware to be +a flat binary. Although this design would simplify the PIL driver, it would +result in the waste of a lot of persistent storage space (due to large +zero-fills), prevent demand loading of segments in the future and use a lot +more memory while loading the firmware. + +Software layering: +------------------ +The peripheral authentication, reset and shutdown implementation is factored +away into a Peripheral Authentication Service driver to allow the PIL driver +to be agnostic of secure vs. non-secure boot and the mechanisms needed for +communicating with any code that might be running in secure mode. + +Power Management +================ + +Some of the peripherals might support being turned off when not in use. +Support for this might be disabled in the initial implementation of the PIL +driver since many of the existing drivers can not handle peripheral restart. + +SMP/multi-core +============== + +Will use mutexes to protected data that might be shared (reference count, +etc). + +Security +======== + +The PIL driver must validate the physical memory addresses specified in the +ELF and program header table before loading firmware segments to make sure +it's not overwriting any memory used by the kernel and possibly PMEM regions +(if it can be done without being an ugly hack). The PIL driver might need to +maintain a white list or black list of physical memory address ranges to +perform the address validation. + +Performance +=========== + +As mentioned in the design section, the loading of firmware segments is not +optimal and has room for improvement. + +Interface +========= + +In kernel APIs: +void * pil_get(char *peripheral_name) + - Enables (if not already enabled) a peripheral and returns a handle + that can be used to disable the peripheral at a later time. If + peripheral can't be enabled successfully, then returns an error + (use IS_ERR) indicating the reason. + +void pil_put(void *peripheral_handle) + - Inform PIL that this client no longer needs the peripheral to be + active. Does not necessarily mean that the peripheral would be + disabled or powered off. + + +User space APIs: +All firmware must be located in the path that is expected by the hotplug (or +compatible) daemon. A hotplug (or compatible) daemon should be running and be +able to handle events from the kernel requesting for a firmware file. + +The basename of the firmware files will depend on the peripheral. For a given +peripheral, the metadata filename should end with a ".mdt" and the firmware +segment files should end with ".bXX" where XX denotes the index of the +firmware segment starting from 0. + +Android hotplug compatible daemon expects the firmware files to be under +/etc/firmware. + +Driver parameters +================= + +No module or kernel command line parameters supported. + +Config options +============== + +This driver is enabled using the MSM_PIL kernel config option and will +depend on the CONFIG_FW_LOADER being available. + +Dependencies +============ + +Depends on firmware class module for the request_firmware API. + +Interacts with the PAS to authenticate the firmware and to initiate the boot +sequence of a peripheral. + +Doesn't communicate with other processors since the secure code, if any, will +be running on the application processor cores. + +User space utilities +==================== + +None. + +Other +===== + +The firmware_class driver might be changed in the future to directly load the +firmware into memory locations provided by the caller of request_firmware(). + +Known issues +============ + +Since support for cleanly shutting down peripherals is yet to be added, the +reference count of peripherals will never be allowed to go to zero once it +becomes non-zero. + +To do +===== + +* Add support for turning off peripherals when they are not in use. +* Modify request_firmware() to directly copy firmware blobs into the + appropriate memory locations. +* Add support for demand loading of firmware segments. +* Add support for forced peripheral restarts. diff --git a/Documentation/arm/msm/rpm.txt b/Documentation/arm/msm/rpm.txt new file mode 100644 index 00000000000..9c9511fb030 --- /dev/null +++ b/Documentation/arm/msm/rpm.txt @@ -0,0 +1,157 @@ +Introduction +============ + +Resource Power Manager (RPM) + +RPM is a dedicated hardware engine for managing shared SoC resources, +which includes buses, clocks, power rails, etc. The goal of RPM is +to achieve the maximum power savings while satisfying the SoC's +operational and performance requirements. RPM accepts resource +requests from multiple RPM masters. It arbitrates and aggregates the +requests, and configures the shared resources. The RPM masters are +the application processor, the modem processor, as well as some +hardware accelerators. + +The RPM driver provides an API for interacting with RPM. Kernel code +calls the RPM driver to request RPM-managed, shared resources. +Kernel code can also register with the driver for RPM notifications, +which are sent when the status of shared resources change. + +Hardware description +==================== + +RPM exposes a separate region of registers to each of the RPM masters. +In general, each register represents some shared resource(s). At a +very basic level, a master requests resources by writing to the +registers, then generating an interrupt to RPM. RPM processes the +request, writes acknowledgement to the registers, then generates an +interrupt to the master. + +In addition to the master-specific regions, RPM also exposes a shared +region that contains the current status of the shared resources. Only +RPM can write to the status region, but every master can read from it. + +RPM contains internal logics that aggregate and arbitrate among +requests from the various RPM masters. It interfaces with the PMIC, +the bus arbitration block, and the clock controller block in order to +configure the shared resources. + +Software description +==================== + +The RPM driver encapsulates the low level RPM interactions, which +rely on reading/writing registers and generating/processing +interrupts, and provides a higher level synchronuous set/clear/get +interface. Most functions take an array of id-value pairs. +The ids identify the RPM registers which would correspond to some +RPM resources, the values specify the new resource values. + +The RPM driver synchronizes accesses to RPM. It protects against +simultaneous accesses from multiple tasks, on SMP cores, in task +contexts, and in atomic contexts. + +Design +====== + +Design goals: +- Encapsulate low level RPM interactions. +- Provide a synchronuous set/clear/get interface. +- Synchronize simultaneous software accesses to RPM. + +Power Management +================ + +RPM is part of the power management architecture for MSM 8660. RPM +manages shared system resources to lower system power. + +SMP/multi-core +============== + +The RPM driver uses mutex to synchronize client accesses among tasks. +It uses spinlocks to synchronize accesses from atomic contexts and +SMP cores. + +Security +======== + +None. + +Performance +=========== + +None. + +Interface +========= + +msm_rpm_get_status(): +The function reads the shared status region and returns the current +resource values, which are the arbitrated/aggregated results across +all RPM masters. + +msm_rpm_set(): +The function makes a resource request to RPM. + +msm_rpm_set_noirq(): +The function is similar to msm_rpm_set() except that it must be +called with interrupts masked. If possible, use msm_rpm_set() +instead, to maximize CPU throughput. + +msm_rpm_clear(): +The function makes a resource request to RPM to clear resource values. +Once the values are cleared, the resources revert back to their default +values for this RPM master. RPM internally uses the default values as +the requests from this RPM master when arbitrating and aggregating with +requests from other RPM masters. + +msm_rpm_clear_noirq(): +The function is similar to msm_rpm_clear() except that it must be +called with interrupts masked. If possible, use msm_rpm_clear() +instead, to maximize CPU throughput. + +msm_rpm_register_notification(): +The function registers for RPM notification. When the specified +resources change their status on RPM, RPM sends out notifications +and the driver will "up" the semaphore in struct +msm_rpm_notification. + +msm_rpm_unregister_notification(): +The function unregisters a notification. + +msm_rpm_init(): +The function initializes the RPM driver with platform specific data. + +Driver parameters +================= + +None. + +Config options +============== + +MSM_RPM + +Dependencies +============ + +None. + +User space utilities +==================== + +None. + +Other +===== + +None. + +Known issues +============ + +None. + +To do +===== + +None. diff --git a/Documentation/arm/msm/tz_log.txt b/Documentation/arm/msm/tz_log.txt new file mode 100644 index 00000000000..280865d9566 --- /dev/null +++ b/Documentation/arm/msm/tz_log.txt @@ -0,0 +1,132 @@ +Introduction: +============= + +The tz_log driver is a platform device driver that exposes a debugfs +interface for accessing and displaying diagnostic information related +to secure code (Trustzone). + +The Secure code (Trustzone) will store the diagnostic data in 4KB of +IMEM. The address of this IMEM region varies from platform. The +diagnostic data encodes information related to secure code boot-up, +reset, interrupt and other attributes in a specific format as shown +below: + + ---------------------------- + | | + | General info | + | (Magic #, CPU cnt etc) | + | | + ---------------------------- + | | + | VMID info | + | | + ---------------------------- + | | + | Boot info (per CPU) | + | | + ---------------------------- + | | + | Reset info (per CPU) | + | | + ---------------------------- + | | + | Interrupt info (per CPU) | + | | + ---------------------------- + | | + | Data logged by TZ | + | | + ---------------------------- + +During the initialization of the driver module, this 4KB of IMEM +is remapped for access by kernel. Further more, an additonal 4KB +memory is allocated for storing the formatted data that will be +displayed by the debugfs interface. + +Once the device is booted up and HLOS is up, the standard debugfs +interface is used to read out and display this information that +was logged in by secure code in a specific format as shown below. + +Debugfs is typically mounted with a command like: + mount -t debugfs none /sys/kernel/debug +(Or an equivalent /etc/fstab line). + +Note that the debugfs API is exported GPL-only to modules. + +Software description +==================== + +The tz_log module is a Linux platform device driver with a debugfs +interface. The goal of this module is to provide a way to peek into +the Trustzone diagnostic information to help debug issues with +Trustzone. Although, this tz_log platform device driver will be +compiled into the kernel, the debugfs entries will not be exposed +unless Trustzone is supported by the platform. + + +On loading the tz_log driver, tzdbgfs_init() is invoked. tzdbgfs_init() +initializes the tz_log debugfs interface. The following is done in +this initialization call. + +(1) Create a directory "tzdbg", to hold a set of debugfs files + +(2) Create the following debugfs files in the "tzdbg" directory +- boot_info + Contains information on the warm boot jump address +- reset_info + Contains information on the cause of a CPU reset, number of + resets occurred on a specific CPU +- interrupt_info + Contains information on the number of IRQ and FIQ Interrupts + (with a brief description), interrupts fired and the number + of times it is fired on a specific CPU. +- general_info + Contains information on number of CPUs supported, magic number, + version number. +- vmid_info + Contains information on VMID supported, with a brief description +- log + Debug information (ASCII text) that is logged by Trustzone + +Following are the set of file operation defines and register +- read() +- open() + +(3) Remap the IMEM region where the secure code diagnostic information +is stored. + +(4) Allocate 4KB buffer for storing the formatted information +to be displayed + +When the tz_log driver is unloaded the tz_log debugfs entries are +explicitly removed. + + +Power Management +================ + +n/a + +Security +======== + +None + +Interface +========= + +This module will create debugfs files under sys/kernel/debug which +contains information that can be displayed by using the "cat" command. + + +Dependencies +============ + +This driver interacts with Trustzone operating environment, thus depends +on the TZBSP supported architecture. It also depends on debugfs. + + +To do +===== + +TBD diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt new file mode 100644 index 00000000000..35385d31630 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt @@ -0,0 +1,47 @@ +* Low Power Management Levels + +The application processor in MSM can do a variety of C-States for low power +management. These C-States are invoked by the CPUIdle framework when the core +becomes idle. But based on the time available until the next scheduled wakeup, +the system can do a combination of low power modes of different resources - +L2, XO, Vdd Dig and Vdd Mem. The combination is captured in the device tree as +lpm-level. The units for voltage are dependent on the PMIC used on the target +and are in uV. + +The required nodes for lpm-levels are: + +- compatible: "qcom,lpm-levels" +- reg: The numeric level id +- qcom,mode: The sleep mode of the processor +- qcom,xo: The state of XO clock. +- qcom,l2: The state of L2 cache. +- qcom,vdd-mem-upper-bound: The upper bound value of mem voltage in uV +- qcom,vdd-mem-lower-bound: The lower bound value of mem voltage in uV +- qcom,vdd-dig-upper-bound: The upper bound value of dig voltage in uV +- qcom,vdd-dig-lower-bound: The lower bound value of dig voltage in uV +- qcom,latency-us: The latency in handling the interrupt if this level was + chosen, in uSec +- qcom,ss-power: The steady state power expelled when the processor is in this + level in mWatts +- qcom,energy-overhead: The energy used up in entering and exiting this level + in mWatts.uSec +- qcom,time-overhead: The time spent in entering and exiting this level in uS + +Example: + +qcom,lpm-levels { + qcom,lpm-level@0 { + reg = <0>; + qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */ + qcom,xo = <1>; /* ON */ + qcom,l2 = <3>; /* ACTIVE */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <100>; + qcom,ss-power = <650>; + qcom,energy-overhead = <801>; + qcom,time-overhead = <200>; + }; +}; diff --git a/Documentation/devicetree/bindings/arm/msm/pm-boot.txt b/Documentation/devicetree/bindings/arm/msm/pm-boot.txt new file mode 100644 index 00000000000..cce9d0e9ba7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/pm-boot.txt @@ -0,0 +1,40 @@ +* Power Management boot configuration (pm-boot) + +Low power management drivers need to specify the warmboot entry path for the +application processors to resume from sleep/suspend. The boot configuration +can vary if the core does/does not support a secure boot mode. The secure +boot configuration boots the core sets up the core sub system registers and +calls into the kernel entry point. In the absence of a secure boot mode, the +core when powered on from reset will need to be configured with the warmboot +entry pointer. The physical and the virtual address for the entry pointer +need to provided to the driver. + + +The device tree parameters for pm-boot are: + +Required parameters: + +- compatible: Must be "qcom,pm-boot" +- qcom,mode: The mode that the target will use for booting + MSM_PM_BOOT_CONFIG_TZ = 0, + MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS = 1, + MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT = 2, + MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR = 3, + +Optional parameters (based on the mode chosen): + +- qcom,phy-addr: The physical address that will be used for warmboot entry if + the processor remap register can be programmed. + Needed for modes = { MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR } +- qcom,virt-addr: The virtual address at which the processor start booting from + Needed for modes = { MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT, + MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR } + + +Example: + + qcom,pm-boot { + compatible = "qcom,pm-boot"; + qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */ + }; diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt new file mode 100644 index 00000000000..786635fa27f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt @@ -0,0 +1,153 @@ +Qualcomm RPM Regulators + +rpm-regulator-smd is a regulator driver which supports regulators inside of +PMICs which are controlled by the RPM processor. Communication with the RPM +processor takes place over SMD. + +Required structure: +- RPM regulators must be described in two levels of devices nodes. The first + level describes the interface with the RPM. The second level describes + properties of one regulator framework interface (of potentially many) to + the regulator. + +[First Level Nodes] + +Required properties: +- compatible: Must be "qcom,rpm-regulator-smd-resource" +- qcom,resource-name: Resource name string for this regulator to be used in RPM + transactions. Length is 4 characters max. +- qcom,resource-id: Resource instance ID for this regulator to be used in RPM + transactions. +- qcom,regulator-type: Type of this regulator. Supported values are: + 0 = LDO + 1 = SMPS + 2 = VS + 3 = NCP + +Optional properties: +- qcom,allow-atomic: Flag specifying if atomic access is allowed for this + regulator. Supported values are: + 0 or not present = mutex locks used + 1 = spinlocks used +- qcom,enable-time: Time in us to delay after enabling the regulator +- qcom,hpm-min-load: Load current in uA which corresponds to the minimum load + which requires the regulator to be in high power mode. + +[Second Level Nodes] + +Required properties: +- compatible: Must be "qcom,rpm-regulator-smd" +- regulator-name: A string used as a descriptive name for regulator outputs +- qcom,set: Specifies which sets that requests made with this + regulator interface should be sent to. Regulator + requests sent in the active set take effect immediately. + Requests sent in the sleep set take effect when the Apps + processor transitions into RPM assisted power collapse. + Supported values are: + 1 = Active set only + 2 = Sleep set only + 3 = Both active and sleep sets + + + +Optional properties: +- parent-supply: phandle to the parent supply/regulator node +- qcom,system-load: Load in uA present on regulator that is not + captured by any consumer request +The following properties specify initial values for parameters to be sent to the +RPM in regulator requests. +- qcom,init-enable: 0 = regulator disabled + 1 = regulator enabled +- qcom,init-voltage: Voltage in uV +- qcom,init-current: Current in mA +- qcom,init-ldo-mode: Operating mode to be used with LDO regulators + Supported values are: + 0 = mode determined by current requests + 1 = force HPM (NPM) +- qcom,init-smps-mode: Operating mode to be used with SMPS regulators + Supported values are: + 0 = auto; hardware determines mode + 1 = mode determined by current requests + 2 = force HPM (PWM) +- qcom,init-pin-ctrl-enable: Bit mask specifying which hardware pins should be + used to enable the regulator, if any; supported + bits are: + 0 = ignore all hardware enable signals + BIT(0) = follow HW0_EN signal + BIT(1) = follow HW1_EN signal + BIT(2) = follow HW2_EN signal + BIT(3) = follow HW3_EN signal +- qcom,init-pin-ctrl-mode: Bit mask specifying which hardware pins should be + used to force the regulator into high power + mode, if any. Supported bits are: + 0 = ignore all hardware enable signals + BIT(0) = follow HW0_EN signal + BIT(1) = follow HW1_EN signal + BIT(2) = follow HW2_EN signal + BIT(3) = follow HW3_EN signal + BIT(4) = follow PMIC awake state +- qcom,init-frequency: Switching frequency in MHz for SMPS regulators. + Supported values are: + 0 = Don't care about frequency used + 1 = 19.20 + 2 = 9.60 + 3 = 6.40 + 4 = 4.80 + 5 = 3.84 + 6 = 3.20 + 7 = 2.74 + 8 = 2.40 + 9 = 2.13 + 10 = 1.92 + 11 = 1.75 + 12 = 1.60 + 13 = 1.48 + 14 = 1.37 + 15 = 1.28 + 16 = 1.20 +- qcom,init-head-room: Voltage head room in uV required for the + regulator +- qcom,init-quiet-mode: Specify that quiet mode is needed for an SMPS + regulator in order to have lower output noise. + Supported values are: + 0 = No quiet mode + 1 = Quiet mode + 2 = Super quiet mode +- qcom,init-freq-reason: Consumer requiring specified frequency for an + SMPS regulator. Supported values are: + 0 = None + 1 = Bluetooth + 2 = GPS + 4 = WLAN + 8 = WAN + +All properties specified within the core regulator framework can also be used in +second level nodes. These bindings can be found in: +Documentation/devicetree/bindings/regulator/regulator.txt. + +Example: + +rpm-regulator-smpb1 { + qcom,resource-name = "smpb"; + qcom,resource-id = <1>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + pm8841_s1: regulator-s1 { + regulator-name = "8841_s1"; + qcom,set = <3>; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1150000>; + qcom,init-voltage = <1150000>; + compatible = "qcom,rpm-regulator-smd"; + }; + pm8841_s1_ao: regulator-s1-ao { + regulator-name = "8841_s1_ao"; + qcom,set = <1>; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1150000>; + compatible = "qcom,rpm-regulator-smd"; + }; +}; diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt new file mode 100644 index 00000000000..8ebd3bac110 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt @@ -0,0 +1,30 @@ +Resource Power Manager(RPM) + +RPM is a dedicated hardware engine for managing shared SoC resources, +which includes buses, clocks, power rails, etc. The goal of RPM is +to achieve the maximum power savings while satisfying the SoC's +operational and performance requirements. RPM accepts resource +requests from multiple RPM masters. It arbitrates and aggregates the +requests, and configures the shared resources. The RPM masters are +the application processor, the modem processor, as well as hardware +accelerators. The RPM driver communicates with the hardware engine using +SMD. + +The devicetree representation of the SPM block should be: + +Required properties + +- compatible: "qcom,rpm-smd" +- rpm-channel-name: The string corresponding to the channel name of the + peripheral subsystem +- rpm-channel-type: The interal SMD edge for this subsystem found in + + +Example: + + qcom,rpm-smd { + compatible = "qcom,rpm-smd" + qcom,rpm-channel-name = "rpm_requests"; + qcom,rpm-channel-type = 15; /* SMD_APPS_RPM */ + } +} diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt new file mode 100644 index 00000000000..a33d8335e38 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt @@ -0,0 +1,66 @@ +* MSM Subsystem Power Manager (spm-v2) + +S4 generation of MSMs have SPM hardware blocks to control the Application +Processor Sub-System power. These SPM blocks run individual state machine +to determine what the core (L2 or Krait/Scorpion) would do when the WFI +instruction is executed by the core. The SAW hardware block handles SPM and +AVS functionality for the cores. + +The devicetree representation of the SPM block should be: + +Required properties + +- compatible: "qcom,spm-v2" +- reg: The physical address and the size of the SPM's memory mapped registers +- qcom, core-id: The core id the SPM block is attached to. + {0..n} for cores {0..n} + {0xffff} for L2 +- qcom,saw2-ver-reg: The location of the version register +- qcom,saw2-cfg: SAW2 configuration register +- qcom,saw2-avs-ctl: The AVS control register +- qcom,saw2-avs-hysterisis: The AVS hysterisis register to delay the AVS + controller requests +- qcom,saw2-spm-dly: Provides the values for the SPM delay command in the SPM + sequence +- qcom,saw2-spm-ctl: The SPM control register +- qcom,saw2-vctl-timeout-us: The timeout value to wait for voltage to change + after sending the voltage command to the PMIC + +Optional properties + +- qcom,saw2-avs-limit: The AVS limit register +- qcom,saw2-avs-dly: The AVS delay register is used to specify the delay values + between AVS controller requests +- qcom,saw2-pmic-dly: The delay values for waiting on PMIC response +- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS + index to send the PMIC data to +- qcom,saw2-vctl-port: The FTS port used for changing voltage +- qcom,saw2-phase-port: The FTS port used for changing the number of phases +- qcom,saw2-spm-cmd-wfi: The WFI command sequence +- qcom,saw2-spm-cmd-ret: The Retention command sequence +- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence +- qcom,saw2-spm-cmd-pc: The Power Collapse command sequence + +Example: + qcom,spm@f9089000 { + compatible = "qcom,spm-v2"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xf9089000 0x1000>; + qcom,core-id = <0>; + qcom,saw2-ver-reg = <0xfd0>; + qcom,saw2-cfg = <0x1b>; + qcom,saw2-avs-ctl = <0>; + qcom,saw2-avs-hysteresis = <0>; + qcom,saw2-avs-limit = <0>; + qcom,saw2-avs-dly= <0>; + qcom,saw2-spm-dly= <0x20000400>; + qcom,saw2-spm-ctl = <0x1>; + qcom,spm-cmd-wfi = [03 0b 0f]; + qcom,spm-cmd-spc = [00 20 50 80 60 70 10 92 + a0 b0 03 68 70 3b 92 a0 b0 + 82 2b 50 10 30 02 22 30 0f]; + qcom,spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92 + a0 b0 82 10 30 02 22 30 0f]; + }; + diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt new file mode 100644 index 00000000000..11af7a976dd --- /dev/null +++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt @@ -0,0 +1,15 @@ +* Qualcomm MSM VIDC + +Required properties: +- compatible : one of: + - "qcom,msm-vidc" +- reg : offset and length of the register set for the device. +- interrupts : should contain the vidc interrupt. + +Example: + + qcom,vidc@fdc00000 { + compatible = "qcom,msm-vidc"; + reg = <0xfdc00000 0xff000>; + interrupts = <0 44 0>; + }; diff --git a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt new file mode 100644 index 00000000000..0c1762d81ce --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt @@ -0,0 +1,41 @@ +Qualcomm Secure Digital Card Controller (SDCC) + +Secure Digital Card Controller provides host interface to +SD/MMC/SDIO cards. + +Required properties: + - compatible : should be "qcom,msm-sdcc" + - reg : should contain SDCC, BAM register map. + - interrupts : should contain SDCC core interrupt. + - qcom,sdcc-clk-rates : specifies supported SDCC clock frequencies, Units - Hz. + - qcom,sdcc-sup-voltages: specifies supported voltage ranges for card. Should always be + specified in pairs (min, max), Units - mV. + +Optional Properties: + - cell-index - defines slot ID. + - qcom,sdcc-bus-width - defines the bus I/O width that controller supports. + - qcom,sdcc-wp-gpio - defines write protect switch gpio. + - qcom,sdcc-wp-polarity - specifies the polarity of wp switch. + - qcom,sdcc-cd-gpio - defines card detect gpio number. + - qcom,sdcc-cd-polarity - specifies the polarity of cd gpio. + - qcom,sdcc-nonremovable - specifies whether the card in slot is + hot pluggable or hard wired. + - qcom,sdcc-disable_cmd23 - disable sending CMD23 to card when controller can't support it. + - qcom,sdcc-hs200 - enable eMMC4.5 HS200 bus speed mode + +Example: + + qcom,sdcc@f9600000 { + /* SDC1 used as eMMC slot */ + cell-index = <1>; + compatible = "qcom,msm-sdcc"; + reg = <0xf9600000 0x800 // SDCC register interface + 0xf9600800 0x1800 // DML register interface + 0xf9602000 0x2000> // BAM register interface + + interrupts = <123>; + qcom,sdcc-clk-rates = <400000 24000000 48000000>; + qcom,sdcc-sup-voltages = <2700 3300>; + qcom,sdcc-bus-width = <8>; //8-bit wide + qcom,sdcc-nonremovable; +}; diff --git a/Documentation/devicetree/bindings/pil/pil-mba.txt b/Documentation/devicetree/bindings/pil/pil-mba.txt new file mode 100644 index 00000000000..7aafd21970f --- /dev/null +++ b/Documentation/devicetree/bindings/pil/pil-mba.txt @@ -0,0 +1,27 @@ +Qualcomm Modem Boot Authenticator Peripheral Image Loader + +pil-mba is a peripheral image loader (PIL) driver. It is used for loading +modem images using the self-authenticating hardware and software features +of the Modem Boot Authenticator. + +Required properties: +- compatible: Must be "qcom,pil-mba" +- reg: Two pairs of physical base addresses and sizes. The + first corresponds to the Relay Message Buffer (RMB) + register base. The second specifies the address at which + the primary modem image metadata should be stored. +- qcom,firmware-name: Base name of the firmware image. Ex. "modem" + +Optional properties: +- qcom,depends-on: firmware-name of a prerequisite image that must already + be running. + +Example: + qcom,mba@fc820000 { + compatible = "qcom,pil-mba"; + reg = <0xfc820000 0x0020>, + <0x0d1f0000 0x4000>; + + qcom,firmware-name = "modem"; + qcom,depends-on = "mba"; + }; diff --git a/Documentation/devicetree/bindings/pil/pil-pronto.txt b/Documentation/devicetree/bindings/pil/pil-pronto.txt new file mode 100644 index 00000000000..6193b681b89 --- /dev/null +++ b/Documentation/devicetree/bindings/pil/pil-pronto.txt @@ -0,0 +1,25 @@ +* Qualcomm WCNSS Pronto Peripheral Image Loader + +pil-pronto is a peripheral image loading (PIL) driver. It is used for loading +Pronto firmware images for wireless connectivity subsystems into memory and +preparing the subsystem's processor to execute code. It is also used for +shutting down the processor when it's not needed. + +Required properties: +- compatible: "pil-pronto" +- reg: offset and length of the register set for the device. The first pair + corresponds to PRONTO_PMU, the second pair corresponds to CLK_CTL_WCNSS + the third pair corresponds to WCNSS_HALTREQ. +- vdd_pronto_pll-supply: regulator to supply pronto pll. +- qcom,firmware-name: Base name of the firmware image. Ex. "wcnss" + +Example: + qcom,pronto@fb21b000 { + compatible = "qcom,pil-pronto"; + reg = <0xfb21b000 0x3000>, + <0xfc401700 0x4>, + <0xfd485300 0xc>; + vdd_pronto_pll-supply = <&pm8941_l12>; + + qcom,firmware-name = "wcnss"; + }; diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt new file mode 100644 index 00000000000..308f99233eb --- /dev/null +++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt @@ -0,0 +1,22 @@ +Qualcomm LPASS QDSP6v5 Peripheral Image Loader + +pil-qdsp6v5-lpass is a peripheral image loader (PIL) driver. It is used for +loading QDSP6v5 (Hexagon) firmware images for Low Power Audio Subsystems +into memory and preparing the subsystem's processor to execute code. It's +also responsible for shutting down the processor when it's not needed. + +Required properties: +- compatible: Must be "qcom,pil-q6v5-lpass" +- reg: Two pairs of physical base addresses and region sizes + of memory mapped registers. The first region corresponds + to QDSP6SS_PUB, and the second to LPASS_HALTREQ. +- qcom,firmware-name: Base name of the firmware image. Ex. "lpass" + +Example: + qcom,lpass@fe200000 { + compatible = "qcom,pil-q6v5-lpass"; + reg = <0xfe200000 0x00100>, + <0xfd485100 0x00010>; + + qcom,firmware-name = "lpass"; + }; diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt new file mode 100644 index 00000000000..95e7f8882ff --- /dev/null +++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt @@ -0,0 +1,32 @@ +Qualcomm MSS QDSP6v5 Peripheral Image Loader + +pil-qdsp6v5-mss is a peripheral image loader (PIL) driver. It is used for +loading QDSP6v5 (Hexagon) firmware images for modem subsystems into memory and +preparing the subsystem's processor to execute code. It's also responsible for +shutting down the processor when it's not needed. + +Required properties: +- compatible: Must be "qcom,pil-q6v5-mss" +- reg: Four pairs of physical base addresses and region sizes of + memory mapped registers. The first region corresponds to + QDSP6SS_PUB, the second to the bus port halt register + base, the third to the MSS_RELAY_MSG_BUFFER base, and the + fourth to the MSS_RESTART register. +- vdd_mss-supply: Reference to the regulator that supplies the processor. +- qcom,firmware-name: Base name of the firmware image. Ex. "mdsp" +- qcom,pil-self-auth: <0> if the hardware does not require self-authenticating + images and self-authentication is not desired; + <1> if the hardware requires self-authenticating images. + +Example: + qcom,mss@fc880000 { + compatible = "qcom,pil-q6v5-mss"; + reg = <0xfc880000 0x100>, + <0xfd485000 0x400>, + <0xfc820000 0x020>, + <0xfc401680 0x004>; + vdd_mss-supply = <&pm8841_s3>; + + qcom,firmware-name = "mba"; + qcom,pil-self-auth = <1>; + }; diff --git a/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt new file mode 100644 index 00000000000..cd7bdce29f3 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/gdsc-regulator.txt @@ -0,0 +1,21 @@ +Qualcomm Global Distributed Switch Controller (GDSC) Regulator Driver + +The GDSC driver, implemented under the regulator framework, is responsible for +safely collapsing and restoring power to peripheral cores on chipsets like +msm-copper for power savings. + +Required properties: + - compatible: Must be "qcom,gdsc" + - regulator-name: A string used as a descriptive name for regulator outputs + - reg: The address of the GDSCR register + +Optional properties: + - parent-supply: phandle to the parent supply/regulator node + +Example: + gdsc_oxili_gx: qcom,gdsc@fd8c4024 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_oxili_gx"; + parent-supply = <&pm8841_s4>; + reg = <0xfd8c4024 0x4>; + }; diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index c1399aeb53f..95ddf34f08f 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -35,16 +35,16 @@ Optional properties : - qcom,hsusb-otg-pmic-id-irq: ID, routed to PMIC IRQ number Example HSUSB OTG controller device node : - usb@F9690000 { + usb@f9690000 { compatible = "qcom,hsusb-otg"; - reg = <0xF9690000 0x400>; + reg = <0xf9690000 0x400>; interrupts = <134>; qcom,hsusb-otg-phy-type = <2>; qcom,hsusb-otg-mode = <1>; qcom,hsusb-otg-otg-control = <1>; qcom,hsusb-otg-default-mode = <2>; - qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xFFFFFFFF>; + qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xffffffff>; qcom,hsusb-otg-power-budget = <500>; qcom,hsusb-otg-pclk-src-name = "dfab_usb_clk"; qcom,hsusb-otg-pmic-id-irq = <47> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c1601e5a8b7..64349f0a926 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2522,6 +2522,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. 1: Fast pin select (default) 2: ATC IRMode + snddev_icodec.msm_codec_i2s_slave_mode= [ARM-MSM] + 1, codec is I2S master + 0, MSM is I2S master (default) + softlockup_panic= [KNL] Should the soft-lockup detector generate panics. Format: diff --git a/Documentation/sound/alsa/compress/snd_compress_data.txt b/Documentation/sound/alsa/compress/snd_compress_data.txt new file mode 100644 index 00000000000..98e2cc98c34 --- /dev/null +++ b/Documentation/sound/alsa/compress/snd_compress_data.txt @@ -0,0 +1,187 @@ + snd_compress_data.txt + ===================== + Pierre-Louis.Bossart + Vinod Koul + +Overview + +Since its early days, the ALSA API was defined with PCM support or +constant bitrates payloads such as IEC61937 in mind. Arguments and +returned values in frames are the norm, making it a challenge to +extend the existing API to compressed data streams. + +In recent years, audio digital signal processors (DSP) were integrated +in system-on-chip designs, and DSPs are also integrated in audio +codecs. Processing compressed data on such DSPs results in a dramatic +reduction of power consumption compared to host-based +processing. Support for such hardware has not been very good in Linux, +mostly because of a lack of a generic API available in the mainline +kernel. + +Rather than requiring a compability break with an API change of the +ALSA PCM interface, a new 'Compressed Data' API is introduced to +provide a control and data-streaming interface for audio DSPs. + +The design of this API was inspired by the 2-year experience with the +Intel Moorestown SOC, with many corrections required to upstream the +API in the mainline kernel instead of the staging tree and make it +usable by others. + +Requirements + +The main requirements are: + +- separation between byte counts and time. Compressed formats may have + a header per file, per frame, or no header at all. The payload size + may vary from frame-to-frame. As a result, it is not possible to + estimate reliably the duration of audio buffers when handling + compressed data. Dedicated mechanisms are required to allow for + reliable audio-video synchronization, which requires precise + reporting of the number of samples rendered at any given time. + +- Handling of multiple formats. PCM data only requires a specification + of the sampling rate, number of channels and bits per sample. In + contrast, compressed data comes in a variety of formats. Audio DSPs + may also provide support for a limited number of audio encoders and + decoders embedded in firmware, or may support more choices through + dynamic download of libraries. + +- Focus on main formats. This API provides support for the most + popular formats used for audio and video capture and playback. It is + likely that as audio compression technology advances, new formats + will be added. + +- Handling of multiple configurations. Even for a given format like + AAC, some implementations may support AAC multichannel but HE-AAC + stereo. Likewise WMA10 level M3 may require too much memory and cpu + cycles. The new API needs to provide a generic way of listing these + formats. + +- Rendering/Grabbing only. This API does not provide any means of + hardware acceleration, where PCM samples are provided back to + user-space for additional processing. This API focuses instead on + streaming compressed data to a DSP, with the assumption that the + decoded samples are routed to a physical output or logical back-end. + + - Complexity hiding. Existing user-space multimedia frameworks all + have existing enums/structures for each compressed format. This new + API assumes the existence of a platform-specific compatibility layer + to expose, translate and make use of the capabilities of the audio + DSP, eg. Android HAL or PulseAudio sinks. By construction, regular + applications are not supposed to make use of this API. + + +Design + +The new API shares a number of concepts with with the PCM API for flow +control. Start, pause, resume, drain and stop commands have the same +semantics no matter what the content is. + +The concept of memory ring buffer divided in a set of fragments is +borrowed from the ALSA PCM API. However, only sizes in bytes can be +specified. + +Seeks/trick modes are assumed to be handled by the host. + +The notion of rewinds/forwards is not supported. Data committed to the +ring buffer cannot be invalidated, except when dropping all buffers. + +The Compressed Data API does not make any assumptions on how the data +is transmitted to the audio DSP. DMA transfers from main memory to an +embedded audio cluster or to a SPI interface for external DSPs are +possible. As in the ALSA PCM case, a core set of routines is exposed; +each driver implementer will have to write support for a set of +mandatory routines and possibly make use of optional ones. + +The main additions are + +- get_codecs +This routine returns the list of audio formats supported. Querying the +codecs on a capture stream will return encoders, decoders will be +listed for playback streams. + +- get_codec_caps +For each codec, this routine returns a list of capabilities. The +intent is to make sure all the capabilities correspond to valid +settings, and to minimize the risks of configuration failures. For +example, for a complex codec such as AAC, the number of channels +supported may depend on a specific profile. If the capabilities were +exposed with a single descriptor, it may happen that a specific +combination of profiles/channels/formats may not be +supported. Likewise, embedded DSPs have limited memory and cpu cycles, +it is likely that some implementations make the list of capabilities +dynamic and dependent on existing workloads. + +- set_params +This routine sets the configuration chosen for a specific codec. The +most important field in the parameters is the codec type; in most +cases decoders will ignore other fields, while encoders will strictly +comply to the settings + +- get_params +This routines returns the actual settings used by the DSP. Changes to +the settings should remain the exception. + +- get_timestamp +The timestamp becomes a multiple field structure. It lists the number +of bytes transferred, the number of samples processed and the number +of samples rendered/grabbed. All these values can be used to determine +the avarage bitrate, figure out if the ring buffer needs to be +refilled or the delay due to decoding/encoding/io on the DSP. + +Note that the list of codecs/profiles/modes was derived from the +OpenMAX AL specification instead of reinventing the wheel. +Modifications include: +- Addition of FLAC and IEC formats +- Merge of encoder/decoder capabilities +- Profiles/modes listed as bitmasks to make descriptors more compact +- Addition of set_params for decoders (missing in OpenMAX AL) +- Addition of AMR/AMR-WB encoding modes (missing in OpenMAX AL) +- Addition of format information for WMA +- Addition of encoding options when required (derived from OpenMAX IL) +- Addition of rateControlSupported (missing in OpenMAX AL) + +Not supported: + +- Support for VoIP/circuit-switched calls is not the target of this + API. Support for dynamic bit-rate changes would require a tight + coupling between the DSP and the host stack, limiting power savings. + +- Packet-loss concealment is not supported. This would require an + additional interface to let the decoder synthesize data when frames + are lost during transmission. This may be added in the future. + +- Volume control/routing is not handled by this API. Devices exposing a + compressed data interface will be considered as regular ALSA devices + +Instead, + offloaded processing will be considered as regular ALSA devices; + volume changes and routing information will be provided with regular + ALSA kcontrols. + +- Embedded audio effects. Such effects should be enabled in the same + manner, no matter if the input was PCM or compressed. + +- multichannel IEC encoding. Unclear if this is required. + +- Encoding/decoding acceleration is not supported as mentioned + above. It is possible to route the output of a decoder to a capture + stream, or even implement transcoding capabilities. This routing + would be enabled with ALSA kcontrols. + +- Audio policy/resource management. This API does not provide any + hooks to query the utilization of the audio DSP, nor any premption + mechanisms. + +- No notion of underun/overrun. Since the bytes written are compressed + in nature and data written/read doesn't translate directly to + rendered output in time, this does not deal with underrun/overun and + maybe dealt in user-library + +Credits: +- Mark Brown and Liam Girdwood for discussions on the need for this API +- Harsha Priya for her work on intel_sst compressed API +- Rakesh Ughreja for valuable feedback +- Sing Nallasellan, Sikkandar Madar and Prasanna Samaga for + demonstrating and quantifying the benefits of audio offload on a + real platform. diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b6c6252e31a..86e99477060 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -27,7 +27,7 @@ config ARM select HAVE_PERF_EVENTS select PERF_USE_VMALLOC select HAVE_REGS_AND_STACK_ACCESS_API - select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7)) + #select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7)) select HAVE_C_RECORDMCOUNT select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW @@ -741,8 +741,14 @@ config ARCH_MSM select GENERIC_CLOCKEVENTS select ARCH_REQUIRE_GPIOLIB select CLKDEV_LOOKUP + select ARCH_HAS_CPUFREQ + select GENERIC_GPIO + select GENERIC_TIME + select GENERIC_ALLOCATOR select HAVE_SCHED_CLOCK select HAVE_CLK_PREPARE + select NEED_MACH_MEMORY_H + select NEED_MACH_IO_H help Support for Qualcomm MSM/QSD based systems. This runs on the apps processor of the MSM/QSD and depends on a shared memory @@ -1638,7 +1644,7 @@ config LOCAL_TIMERS bool "Use local timer interrupts" depends on SMP default y - select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_MCT) + select HAVE_ARM_TWD if (!MSM_SMP && !EXYNOS4_MCT) help Enable support for local timers on SMP platforms, rather then the legacy IPI broadcast method. Local timers allows the system @@ -1732,7 +1738,7 @@ config AEABI config OABI_COMPAT bool "Allow old ABI binaries to run with this kernel (EXPERIMENTAL)" depends on AEABI && EXPERIMENTAL && !THUMB2_KERNEL - default y if !SMP + default y help This option preserves the old syscall interface along with the new (ARM EABI) one. It also provides a compatibility layer to @@ -2377,7 +2383,7 @@ menu "Power management options" source "kernel/power/Kconfig" config ARCH_SUSPEND_POSSIBLE - depends on !ARCH_S5PC100 + depends on !ARCH_S5PC100 && !ARCH_FSM9XXX depends on CPU_ARM920T || CPU_ARM926T || CPU_SA1100 || \ CPU_V6 || CPU_V6K || CPU_V7 || CPU_XSC3 || CPU_XSCALE def_bool y diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index bfd179f1cc5..ed18caeda74 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -208,43 +208,6 @@ choice Say Y here if you want kernel low-level debugging support on i.MX6Q UART4. - config DEBUG_MSM_UART1 - bool "Kernel low-level debugging messages via MSM UART1" - depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 - help - Say Y here if you want the debug print routines to direct - their output to the first serial port on MSM devices. - - config DEBUG_MSM_UART2 - bool "Kernel low-level debugging messages via MSM UART2" - depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 - help - Say Y here if you want the debug print routines to direct - their output to the second serial port on MSM devices. - - config DEBUG_MSM_UART3 - bool "Kernel low-level debugging messages via MSM UART3" - depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 - help - Say Y here if you want the debug print routines to direct - their output to the third serial port on MSM devices. - - config DEBUG_MSM8660_UART - bool "Kernel low-level debugging messages via MSM 8660 UART" - depends on ARCH_MSM8X60 - select MSM_HAS_DEBUG_UART_HS - help - Say Y here if you want the debug print routines to direct - their output to the serial port on MSM 8660 devices. - - config DEBUG_MSM8960_UART - bool "Kernel low-level debugging messages via MSM 8960 UART" - depends on ARCH_MSM8960 - select MSM_HAS_DEBUG_UART_HS - help - Say Y here if you want the debug print routines to direct - their output to the serial port on MSM 8960 devices. - config DEBUG_REALVIEW_STD_PORT bool "RealView Default UART" depends on ARCH_REALVIEW diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 92cfa2bba77..81b5dc9c829 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -128,9 +128,6 @@ textofs-$(CONFIG_PM_H1940) := 0x00108000 ifeq ($(CONFIG_ARCH_SA1100),y) textofs-$(CONFIG_SA1111) := 0x00208000 endif -textofs-$(CONFIG_ARCH_MSM7X30) := 0x00208000 -textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 -textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. diff --git a/arch/arm/boot/dts/msm-gdsc.dtsi b/arch/arm/boot/dts/msm-gdsc.dtsi new file mode 100644 index 00000000000..f83fe767efb --- /dev/null +++ b/arch/arm/boot/dts/msm-gdsc.dtsi @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "skeleton.dtsi" + +/ { + gdsc_venus: qcom,gdsc@fd8c1024 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_venus"; + reg = <0xfd8c1024 0x4>; + }; + + gdsc_mdss: qcom,gdsc@fd8c2304 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_mdss"; + reg = <0xfd8c2304 0x4>; + }; + + gdsc_jpeg: qcom,gdsc@fd8c35a4 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_jpeg"; + reg = <0xfd8c35a4 0x4>; + }; + + gdsc_vfe: qcom,gdsc@fd8c36a4 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_vfe"; + reg = <0xfd8c36a4 0x4>; + }; + + gdsc_oxili_gx: qcom,gdsc@fd8c4024 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_oxili_gx"; + reg = <0xfd8c4024 0x4>; + }; + + gdsc_oxili_cx: qcom,gdsc@fd8c4034 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_oxili_cx"; + reg = <0xfd8c4034 0x4>; + }; + + gdsc_usb_hsic: qcom,gdsc@fc400404 { + compatible = "qcom,gdsc"; + regulator-name = "gdsc_usb_hsic"; + reg = <0xfc400404 0x4>; + }; +}; diff --git a/arch/arm/boot/dts/msm-pm8841.dtsi b/arch/arm/boot/dts/msm-pm8841.dtsi new file mode 100644 index 00000000000..523c9b0fab3 --- /dev/null +++ b/arch/arm/boot/dts/msm-pm8841.dtsi @@ -0,0 +1,187 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + qcom,spmi@fc4c0000 { + #address-cells = <1>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <3>; + + qcom,pm8841@5 { + spmi-slave-container; + reg = <0x5>; + #address-cells = <1>; + #size-cells = <1>; + + regulator@1400 { + regulator-name = "8841_s1"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1400 0x300>; + status = "disabled"; + + qcom,ctl@1400 { + reg = <0x1400 0x100>; + }; + qcom,ps@1500 { + reg = <0x1500 0x100>; + }; + qcom,freq@1600 { + reg = <0x1600 0x100>; + }; + }; + + regulator@1700 { + regulator-name = "8841_s2"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1700 0x300>; + status = "disabled"; + + qcom,ctl@1700 { + reg = <0x1700 0x100>; + }; + qcom,ps@1800 { + reg = <0x1800 0x100>; + }; + qcom,freq@1900 { + reg = <0x1900 0x100>; + }; + }; + + regulator@1a00 { + regulator-name = "8841_s3"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1a00 0x300>; + status = "disabled"; + + qcom,ctl@1a00 { + reg = <0x1a00 0x100>; + }; + qcom,ps@1b00 { + reg = <0x1b00 0x100>; + }; + qcom,freq@1c00 { + reg = <0x1c00 0x100>; + }; + }; + + regulator@1d00 { + regulator-name = "8841_s4"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1d00 0x300>; + status = "disabled"; + + qcom,ctl@1d00 { + reg = <0x1d00 0x100>; + }; + qcom,ps@1e00 { + reg = <0x1e00 0x100>; + }; + qcom,freq@1f00 { + reg = <0x1f00 0x100>; + }; + }; + + regulator@2000 { + regulator-name = "8841_s5"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x2000 0x300>; + status = "disabled"; + + qcom,ctl@0 { + reg = <0x2000 0x100>; + }; + qcom,ps@100 { + reg = <0x2100 0x100>; + }; + qcom,freq@200 { + reg = <0x2200 0x100>; + }; + }; + + regulator@2300 { + regulator-name = "8841_s6"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x2300 0x300>; + status = "disabled"; + + qcom,ctl@2300 { + reg = <0x2300 0x100>; + }; + qcom,ps@2400 { + reg = <0x2400 0x100>; + }; + qcom,freq@2500 { + reg = <0x2500 0x100>; + }; + }; + + regulator@2600 { + regulator-name = "8841_s7"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x2600 0x300>; + status = "disabled"; + + qcom,ctl@2600 { + reg = <0x2300 0x100>; + }; + qcom,ps@2700 { + reg = <0x2400 0x100>; + }; + qcom,freq@2800 { + reg = <0x2500 0x100>; + }; + }; + + regulator@2900 { + regulator-name = "8841_s8"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x2900 0x300>; + status = "disabled"; + + qcom,ctl@2900 { + reg = <0x2900 0x100>; + }; + qcom,ps@2a000 { + reg = <0x2a00 0x100>; + }; + qcom,freq@2b00 { + reg = <0x2b00 0x100>; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi new file mode 100644 index 00000000000..e62dfbdb8b5 --- /dev/null +++ b/arch/arm/boot/dts/msm-pm8941.dtsi @@ -0,0 +1,529 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + qcom,spmi@fc4c0000 { + #address-cells = <1>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <3>; + + qcom,pm8941@0 { + spmi-slave-container; + reg = <0x0>; + #address-cells = <1>; + #size-cells = <1>; + + pm8941_gpios { + spmi-dev-container; + compatible = "qcom,qpnp-gpio"; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <1>; + + gpio@c000 { + reg = <0xc000 0x100>; + qcom,gpio-num = <1>; + status = "disabled"; + }; + + gpio@c100 { + reg = <0xc100 0x100>; + qcom,gpio-num = <2>; + status = "disabled"; + }; + + gpio@c200 { + reg = <0xc200 0x100>; + qcom,gpio-num = <3>; + status = "disabled"; + }; + + gpio@c300 { + reg = <0xc300 0x100>; + qcom,gpio-num = <4>; + status = "disabled"; + }; + + gpio@c400 { + reg = <0xc400 0x100>; + qcom,gpio-num = <5>; + status = "disabled"; + }; + + gpio@c500 { + reg = <0xc500 0x100>; + qcom,gpio-num = <6>; + status = "disabled"; + }; + + gpio@c600 { + reg = <0xc600 0x100>; + qcom,gpio-num = <7>; + status = "disabled"; + }; + + gpio@c700 { + reg = <0xc700 0x100>; + qcom,gpio-num = <8>; + status = "disabled"; + }; + + gpio@c800 { + reg = <0xc800 0x100>; + qcom,gpio-num = <9>; + status = "disabled"; + }; + + gpio@c900 { + reg = <0xc900 0x100>; + qcom,gpio-num = <10>; + status = "disabled"; + }; + + gpio@ca00 { + reg = <0xca00 0x100>; + qcom,gpio-num = <11>; + status = "disabled"; + }; + + gpio@cb00 { + reg = <0xcb00 0x100>; + qcom,gpio-num = <12>; + status = "disabled"; + }; + + gpio@cc00 { + reg = <0xcc00 0x100>; + qcom,gpio-num = <13>; + status = "disabled"; + }; + + gpio@cd00 { + reg = <0xcd00 0x100>; + qcom,gpio-num = <14>; + status = "disabled"; + }; + + gpio@ce00 { + reg = <0xce00 0x100>; + qcom,gpio-num = <15>; + status = "disabled"; + }; + + gpio@cf00 { + reg = <0xcf00 0x100>; + qcom,gpio-num = <16>; + status = "disabled"; + }; + + gpio@d000 { + reg = <0xd000 0x100>; + qcom,gpio-num = <17>; + status = "disabled"; + }; + + gpio@d100 { + reg = <0xd100 0x100>; + qcom,gpio-num = <18>; + status = "disabled"; + }; + + gpio@d200 { + reg = <0xd200 0x100>; + qcom,gpio-num = <19>; + status = "disabled"; + }; + + gpio@d300 { + reg = <0xd300 0x100>; + qcom,gpio-num = <20>; + status = "disabled"; + }; + + gpio@d400 { + reg = <0xd400 0x100>; + qcom,gpio-num = <21>; + status = "disabled"; + }; + + gpio@d500 { + reg = <0xd500 0x100>; + qcom,gpio-num = <22>; + status = "disabled"; + }; + + gpio@d600 { + reg = <0xd600 0x100>; + qcom,gpio-num = <23>; + status = "disabled"; + }; + + gpio@d700 { + reg = <0xd700 0x100>; + qcom,gpio-num = <24>; + status = "disabled"; + }; + + gpio@d800 { + reg = <0xd800 0x100>; + qcom,gpio-num = <25>; + status = "disabled"; + }; + + gpio@d900 { + reg = <0xd900 0x100>; + qcom,gpio-num = <26>; + status = "disabled"; + }; + + gpio@da00 { + reg = <0xda00 0x100>; + qcom,gpio-num = <27>; + status = "disabled"; + }; + + gpio@db00 { + reg = <0xdb00 0x100>; + qcom,gpio-num = <28>; + status = "disabled"; + }; + + gpio@dc00 { + reg = <0xdc00 0x100>; + qcom,gpio-num = <29>; + status = "disabled"; + }; + + gpio@dd00 { + reg = <0xdd00 0x100>; + qcom,gpio-num = <30>; + status = "disabled"; + }; + + gpio@de00 { + reg = <0xde00 0x100>; + qcom,gpio-num = <31>; + status = "disabled"; + }; + + gpio@df00 { + reg = <0xdf00 0x100>; + qcom,gpio-num = <32>; + status = "disabled"; + }; + + gpio@e000 { + reg = <0xe000 0x100>; + qcom,gpio-num = <33>; + status = "disabled"; + }; + + gpio@e100 { + reg = <0xe100 0x100>; + qcom,gpio-num = <34>; + status = "disabled"; + }; + + gpio@e200 { + reg = <0xe200 0x100>; + qcom,gpio-num = <35>; + status = "disabled"; + }; + + gpio@e300 { + reg = <0xe300 0x100>; + qcom,gpio-num = <36>; + status = "disabled"; + }; + }; + }; + + qcom,pm8941@1 { + spmi-slave-container; + reg = <0x1>; + #address-cells = <1>; + #size-cells = <1>; + + regulator@1400 { + regulator-name = "8941_s1"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1400 0x300>; + status = "disabled"; + + qcom,ctl@1400 { + reg = <0x1400 0x100>; + }; + qcom,ps@1500 { + reg = <0x1500 0x100>; + }; + qcom,freq@1600 { + reg = <0x1600 0x100>; + }; + }; + + regulator@1700 { + regulator-name = "8941_s2"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1700 0x300>; + status = "disabled"; + + qcom,ctl@1700 { + reg = <0x1700 0x100>; + }; + qcom,ps@1800 { + reg = <0x1800 0x100>; + }; + qcom,freq@1900 { + reg = <0x1900 0x100>; + }; + }; + + regulator@1a00 { + regulator-name = "8941_s3"; + spmi-dev-container; + #address-cells = <1>; + #size-cells = <1>; + compatible = "qcom,qpnp-regulator"; + reg = <0x1400 0x300>; + status = "disabled"; + + qcom,ctl@1a00 { + reg = <0x1a00 0x100>; + }; + qcom,ps@1b00 { + reg = <0x1b00 0x100>; + }; + qcom,freq@1c00 { + reg = <0x1c00 0x100>; + }; + }; + + regulator@a000 { + regulator-name = "8941_boost"; + reg = <0xa000 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4000 { + regulator-name = "8941_l1"; + reg = <0x4000 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4100 { + regulator-name = "8941_l2"; + reg = <0x4100 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4200 { + regulator-name = "8941_l3"; + reg = <0x4200 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4300 { + regulator-name = "8941_l4"; + reg = <0x4300 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4400 { + regulator-name = "8941_l5"; + reg = <0x4400 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4500 { + regulator-name = "8941_l6"; + reg = <0x4500 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4600 { + regulator-name = "8941_l7"; + reg = <0x4600 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4700 { + regulator-name = "8941_l8"; + reg = <0x4700 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4800 { + regulator-name = "8941_l9"; + reg = <0x4800 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4900 { + regulator-name = "8941_l10"; + reg = <0x4900 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4a00 { + regulator-name = "8941_l11"; + reg = <0x4a00 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4b00 { + regulator-name = "8941_l12"; + reg = <0x4b00 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4c00 { + regulator-name = "8941_l13"; + reg = <0x4c00 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4d00 { + regulator-name = "8941_l14"; + reg = <0x4d00 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4e00 { + regulator-name = "8941_l15"; + reg = <0x4e00 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@4f00 { + regulator-name = "8941_l16"; + reg = <0x4f00 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5000 { + regulator-name = "8941_l17"; + reg = <0x5000 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5100 { + regulator-name = "8941_l18"; + reg = <0x5100 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5200 { + regulator-name = "8941_l19"; + reg = <0x5200 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5300 { + regulator-name = "8941_l20"; + reg = <0x5300 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5400 { + regulator-name = "8941_l21"; + reg = <0x5400 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5500 { + regulator-name = "8941_l22"; + reg = <0x5500 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5600 { + regulator-name = "8941_l23"; + reg = <0x5600 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@5700 { + regulator-name = "8941_l24"; + reg = <0x5700 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@8000 { + regulator-name = "8941_lvs1"; + reg = <0x8000 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@8100 { + regulator-name = "8941_lvs2"; + reg = <0x8100 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@8200 { + regulator-name = "8941_lvs3"; + reg = <0x8200 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@8300 { + regulator-name = "8941_mvs1"; + reg = <0x8300 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + + regulator@8400 { + regulator-name = "8941_mvs2"; + reg = <0x8400 0x100>; + compatible = "qcom,qpnp-regulator"; + status = "disabled"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm-pm8x41-rpm-regulator.dtsi b/arch/arm/boot/dts/msm-pm8x41-rpm-regulator.dtsi new file mode 100644 index 00000000000..019112a6166 --- /dev/null +++ b/arch/arm/boot/dts/msm-pm8x41-rpm-regulator.dtsi @@ -0,0 +1,587 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + qcom,rpm-smd { + rpm-regulator-smpb1 { + qcom,resource-name = "smpb"; + qcom,resource-id = <1>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s1 { + regulator-name = "8841_s1"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-smpb2 { + qcom,resource-name = "smpb"; + qcom,resource-id = <2>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s2 { + regulator-name = "8841_s2"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-smpb3 { + qcom,resource-name = "smpb"; + qcom,resource-id = <3>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s3 { + regulator-name = "8841_s3"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-smpb4 { + qcom,resource-name = "smpb"; + qcom,resource-id = <4>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s4 { + regulator-name = "8841_s4"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-smpa1 { + qcom,resource-name = "smpa"; + qcom,resource-id = <1>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s1 { + regulator-name = "8941_s1"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-smpa2 { + qcom,resource-name = "smpa"; + qcom,resource-id = <2>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s2 { + regulator-name = "8941_s2"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-smpa3 { + qcom,resource-name = "smpa"; + qcom,resource-id = <3>; + qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-s3 { + regulator-name = "8941_s3"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa1 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <1>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l1 { + regulator-name = "8941_l1"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa2 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <2>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l2 { + regulator-name = "8941_l2"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa3 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <3>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l3 { + regulator-name = "8941_l3"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa4 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <4>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l4 { + regulator-name = "8941_l4"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa5 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <5>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l5 { + regulator-name = "8941_l5"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa6 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <6>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l6 { + regulator-name = "8941_l6"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa7 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <7>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l7 { + regulator-name = "8941_l7"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa8 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <8>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l8 { + regulator-name = "8941_l8"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa9 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <9>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l9 { + regulator-name = "8941_l9"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa10 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <10>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l10 { + regulator-name = "8941_l10"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa11 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <11>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l11 { + regulator-name = "8941_l11"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa12 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <12>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l12 { + regulator-name = "8941_l12"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa13 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <13>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l13 { + regulator-name = "8941_l13"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa14 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <14>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l14 { + regulator-name = "8941_l14"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa15 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <15>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l15 { + regulator-name = "8941_l15"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa16 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <16>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l16 { + regulator-name = "8941_l16"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa17 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <17>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l17 { + regulator-name = "8941_l17"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa18 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <18>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l18 { + regulator-name = "8941_l18"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa19 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <19>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l19 { + regulator-name = "8941_l19"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa20 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <20>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l20 { + regulator-name = "8941_l20"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa21 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <21>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l21 { + regulator-name = "8941_l21"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa22 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <22>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l22 { + regulator-name = "8941_l22"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa23 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <23>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l23 { + regulator-name = "8941_l23"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-ldoa24 { + qcom,resource-name = "ldoa"; + qcom,resource-id = <24>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-l24 { + regulator-name = "8941_l24"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + /* TODO: find out correct resource names for LVS vs MVS */ + rpm-regulator-vsa1 { + qcom,resource-name = "vsa"; + qcom,resource-id = <1>; + qcom,regulator-type = <2>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-lvs1 { + regulator-name = "8941_lvs1"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-vsa2 { + qcom,resource-name = "vsa"; + qcom,resource-id = <2>; + qcom,regulator-type = <2>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-lvs2 { + regulator-name = "8941_lvs2"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-vsa3 { + qcom,resource-name = "vsa"; + qcom,resource-id = <3>; + qcom,regulator-type = <2>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-lvs3 { + regulator-name = "8941_lvs3"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-vsa4 { + qcom,resource-name = "vsa"; + qcom,resource-id = <4>; + qcom,regulator-type = <2>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-mvs1 { + regulator-name = "8941_mvs1"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + + rpm-regulator-vsa5 { + qcom,resource-name = "vsa"; + qcom,resource-id = <5>; + qcom,regulator-type = <2>; + compatible = "qcom,rpm-regulator-smd-resource"; + status = "disabled"; + + regulator-mvs2 { + regulator-name = "8941_mvs2"; + qcom,set = <3>; + status = "disabled"; + compatible = "qcom,rpm-regulator-smd"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm9625.dts b/arch/arm/boot/dts/msm9625.dts new file mode 100644 index 00000000000..d5aed00d70d --- /dev/null +++ b/arch/arm/boot/dts/msm9625.dts @@ -0,0 +1,47 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; +/include/ "skeleton.dtsi" + +/ { + model = "Qualcomm MSM 9625"; + compatible = "qcom,msm9625"; + interrupt-parent = <&intc>; + + intc: interrupt-controller@F9000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0xF9000000 0x1000>, + <0xF9002000 0x1000>; + }; + + msmgpio: gpio@fd510000 { + compatible = "qcom,msm-gpio"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0xfd510000 0x4000>; + }; + + timer { + compatible = "qcom,msm-qtimer", "arm,armv7-timer"; + interrupts = <0 7 0>; + clock-frequency = <5000000>; + }; + + serial@f991f000 { + compatible = "qcom,msm-lsuart-v14"; + reg = <0xf991f000 0x1000>; + interrupts = <0 109 0>; + }; +}; diff --git a/arch/arm/boot/dts/msmcopper-gpio.dtsi b/arch/arm/boot/dts/msmcopper-gpio.dtsi new file mode 100644 index 00000000000..7c3f5ce000c --- /dev/null +++ b/arch/arm/boot/dts/msmcopper-gpio.dtsi @@ -0,0 +1,214 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + qcom,spmi@fc4c0000 { + + qcom,pm8941@0 { + + pm8941_gpios: pm8941_gpios { + + gpio@c000 { + qcom,gpio-num = <1>; + status = "ok"; + }; + + gpio@c100 { + qcom,gpio-num = <2>; + status = "ok"; + }; + + gpio@c200 { + qcom,gpio-num = <3>; + status = "ok"; + }; + + gpio@c300 { + qcom,gpio-num = <4>; + status = "ok"; + }; + + gpio@c400 { + qcom,gpio-num = <5>; + status = "ok"; + }; + + gpio@c500 { + qcom,gpio-num = <6>; + status = "ok"; + }; + + gpio@c600 { + qcom,gpio-num = <7>; + status = "ok"; + }; + + gpio@c700 { + qcom,gpio-num = <8>; + status = "ok"; + }; + + gpio@c800 { + qcom,gpio-num = <9>; + status = "ok"; + }; + + gpio@c900 { + qcom,gpio-num = <10>; + status = "ok"; + }; + + gpio@ca00 { + qcom,gpio-num = <11>; + status = "ok"; + }; + + gpio@cb00 { + qcom,gpio-num = <12>; + status = "ok"; + }; + + gpio@cc00 { + qcom,gpio-num = <13>; + status = "ok"; + }; + + gpio@cd00 { + qcom,gpio-num = <14>; + status = "ok"; + }; + + gpio@ce00 { + qcom,gpio-num = <15>; + status = "ok"; + }; + + gpio@cf00 { + qcom,gpio-num = <16>; + status = "ok"; + }; + + gpio@d000 { + qcom,gpio-num = <17>; + status = "ok"; + }; + + gpio@d100 { + qcom,gpio-num = <18>; + status = "ok"; + }; + + gpio@d200 { + qcom,gpio-num = <19>; + status = "ok"; + }; + + gpio@d300 { + qcom,gpio-num = <20>; + status = "ok"; + }; + + gpio@d400 { + qcom,gpio-num = <21>; + status = "ok"; + }; + + gpio@d500 { + qcom,gpio-num = <22>; + status = "ok"; + }; + + gpio@d600 { + qcom,gpio-num = <23>; + status = "ok"; + }; + + gpio@d700 { + qcom,gpio-num = <24>; + status = "ok"; + }; + + gpio@d800 { + qcom,gpio-num = <25>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@d900 { + qcom,gpio-num = <26>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@da00 { + qcom,gpio-num = <27>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@db00 { + qcom,gpio-num = <28>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@dc00 { + qcom,gpio-num = <29>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@dd00 { + qcom,gpio-num = <30>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@de00 { + qcom,gpio-num = <31>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@df00 { + qcom,gpio-num = <32>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@e000 { + qcom,gpio-num = <33>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@e100 { + qcom,gpio-num = <34>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@e200 { + qcom,gpio-num = <35>; + qcom,out-strength = <1>; + status = "ok"; + }; + + gpio@e300 { + qcom,gpio-num = <36>; + qcom,out-strength = <1>; + status = "ok"; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/msmcopper-regulator.dtsi b/arch/arm/boot/dts/msmcopper-regulator.dtsi new file mode 100644 index 00000000000..0d0c5871efc --- /dev/null +++ b/arch/arm/boot/dts/msmcopper-regulator.dtsi @@ -0,0 +1,394 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + qcom,spmi@fc4c0000 { + + qcom,pm8941@1 { + + pm8941_s1: regulator@1400 { + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8941_s2: regulator@1700 { + regulator-min-microvolt = <2150000>; + regulator-max-microvolt = <2150000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_s3: regulator@1a00 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8941_boost: regulator@a000 { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + qcom,enable-time = <500>; + status = "okay"; + }; + + pm8941_l1: regulator@4000 { + parent-supply = <&pm8941_s1>; + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1225000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8941_l2: regulator@4100 { + parent-supply = <&pm8941_s3>; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l3: regulator@4200 { + parent-supply = <&pm8941_s1>; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l4: regulator@4300 { + parent-supply = <&pm8941_s1>; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l5: regulator@4400 { + parent-supply = <&pm8941_s2>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l6: regulator@4500 { + parent-supply = <&pm8941_s2>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l7: regulator@4600 { + parent-supply = <&pm8941_s2>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l8: regulator@4700 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l9: regulator@4800 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l10: regulator@4900 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l11: regulator@4a00 { + parent-supply = <&pm8941_s1>; + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <1250000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l12: regulator@4b00 { + parent-supply = <&pm8941_s2>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l13: regulator@4c00 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l14: regulator@4d00 { + parent-supply = <&pm8941_s2>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l15: regulator@4e00 { + parent-supply = <&pm8941_s2>; + regulator-min-microvolt = <2050000>; + regulator-max-microvolt = <2050000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l16: regulator@4f00 { + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l17: regulator@5000 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l18: regulator@5100 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l19: regulator@5200 { + regulator-min-microvolt = <2900000>; + regulator-max-microvolt = <2900000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l20: regulator@5300 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l21: regulator@5400 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l22: regulator@5500 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l23: regulator@5600 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_l24: regulator@5700 { + regulator-min-microvolt = <3075000>; + regulator-max-microvolt = <3075000>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_lvs1: regulator@8000 { + parent-supply = <&pm8941_s3>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_lvs2: regulator@8100 { + parent-supply = <&pm8941_s3>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_lvs3: regulator@8200 { + parent-supply = <&pm8941_s3>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_mvs1: regulator@8300 { + parent-supply = <&pm8941_boost>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8941_mvs2: regulator@8400 { + parent-supply = <&pm8941_boost>; + qcom,enable-time = <200>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + }; + + qcom,pm8841@5 { + + pm8841_s1: regulator@1400 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1150000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8841_s2: regulator@1700 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1150000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8841_s3: regulator@1a00 { + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8841_s4: regulator@1d00 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8841_s5: regulator@2000 { + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1100000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + regulator-always-on; + status = "okay"; + }; + + pm8841_s6: regulator@2300 { + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1100000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8841_s7: regulator@2600 { + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1100000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + + pm8841_s8: regulator@2900 { + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1100000>; + qcom,enable-time = <500>; + qcom,pull-down-enable = <1>; + status = "okay"; + }; + }; + }; + + krait0_vreg: regulator@f9088000 { + compatible = "qcom,krait-regulator"; + regulator-name = "krait0"; + reg = <0xf9088000 0x1000>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1100000>; + }; + + krait1_vreg: regulator@f9098000 { + compatible = "qcom,krait-regulator"; + regulator-name = "krait1"; + reg = <0xf9098000 0x1000>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1100000>; + }; + + krait2_vreg: regulator@f90a8000 { + compatible = "qcom,krait-regulator"; + regulator-name = "krait2"; + reg = <0xf90a8000 0x1000>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1100000>; + }; + + krait3_vreg: regulator@f90b8000 { + compatible = "qcom,krait-regulator"; + regulator-name = "krait3"; + reg = <0xf90b8000 0x1000>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1100000>; + }; +}; diff --git a/arch/arm/boot/dts/msmcopper-rumi.dts b/arch/arm/boot/dts/msmcopper-rumi.dts new file mode 100644 index 00000000000..3f1a2ac62af --- /dev/null +++ b/arch/arm/boot/dts/msmcopper-rumi.dts @@ -0,0 +1,106 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +/include/ "msmcopper.dtsi" + +/ { + model = "Qualcomm MSM Copper RUMI"; + compatible = "qcom,msmcopper-rumi", "qcom,msmcopper"; + + timer { + clock-frequency = <5000000>; + }; + + serial@f991f000 { + status = "disable"; + }; + + usb@f9a55000 { + status = "disable"; + }; + + qcom,sdcc@f9824000 { + qcom,sdcc-clk-rates = <400000 19200000>; + }; + + qcom,sdcc@f98a4000 { + qcom,sdcc-clk-rates = <400000 19200000>; + }; + + qcom,sps@f998000 { + status = "disable"; + }; + + spi@f9924000 { + status = "disable"; + }; + + spi@f9923000 { + compatible = "qcom,spi-qup-v2"; + reg = <0xf9923000 0x1000>; + interrupts = <0 95 0>; + spi-max-frequency = <24000000>; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&msmgpio 3 0>, /* CLK */ + <&msmgpio 1 0>, /* MISO */ + <&msmgpio 0 0>; /* MOSI */ + cs-gpios = <&msmgpio 9 0>; + + ethernet-switch@2 { + compatible = "simtec,ks8851"; + reg = <2>; + interrupt-parent = <&msmgpio>; + interrupts = <90 0>; + spi-max-frequency = <5000000>; + }; + }; + + i2c@f9966000 { + status = "disable"; + }; + + i2c@f9967000 { + cell-index = <0>; + compatible = "qcom,i2c-qup"; + reg = <0Xf9967000 0x1000>; + reg-names = "qup_phys_addr"; + interrupts = <0 105 0>; + interrupt-names = "qup_err_intr"; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <24000000>; + gpios = <&msmgpio 83 0>, /* DAT */ + <&msmgpio 84 0>; /* CLK */ + }; + + slim@fe12f000 { + status = "disable"; + }; + + qcom,spmi@fc4c0000 { + status = "disable"; + }; + + qcom,ssusb@F9200000 { + status = "disable"; + }; + + qcom,lpass@fe200000 { + status = "disable"; + }; + + qcom,pronto@fb21b000 { + status = "disable"; + }; +}; diff --git a/arch/arm/boot/dts/msmcopper-sim.dts b/arch/arm/boot/dts/msmcopper-sim.dts new file mode 100644 index 00000000000..ab6b8ba417c --- /dev/null +++ b/arch/arm/boot/dts/msmcopper-sim.dts @@ -0,0 +1,20 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +/include/ "msmcopper.dtsi" + +/ { + model = "Qualcomm MSM Copper Simulator"; + compatible = "qcom,msmcopper-sim", "qcom,msmcopper"; +}; diff --git a/arch/arm/boot/dts/msmcopper.dtsi b/arch/arm/boot/dts/msmcopper.dtsi new file mode 100644 index 00000000000..eb781b73655 --- /dev/null +++ b/arch/arm/boot/dts/msmcopper.dtsi @@ -0,0 +1,339 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "skeleton.dtsi" +/include/ "msmcopper_pm.dtsi" +/include/ "msm-pm8x41-rpm-regulator.dtsi" +/include/ "msm-pm8841.dtsi" +/include/ "msm-pm8941.dtsi" +/include/ "msmcopper-regulator.dtsi" +/include/ "msmcopper-gpio.dtsi" +/include/ "msmcopper-iommu.dtsi" +/include/ "msm-gdsc.dtsi" + +/ { + model = "Qualcomm MSM Copper"; + compatible = "qcom,msmcopper"; + interrupt-parent = <&intc>; + + intc: interrupt-controller@F9000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0xF9000000 0x1000>, + <0xF9002000 0x1000>; + }; + + msmgpio: gpio@fd510000 { + compatible = "qcom,msm-gpio"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0xfd510000 0x4000>; + #gpio-cells = <2>; + }; + + timer { + compatible = "qcom,msm-qtimer", "arm,armv7-timer"; + interrupts = <1 2 0 1 3 0>; + clock-frequency = <19200000>; + }; + + qcom,vidc@fdc00000 { + compatible = "qcom,msm-vidc"; + reg = <0xfdc00000 0xff000>; + interrupts = <0 44 0>; + }; + + serial@f991f000 { + compatible = "qcom,msm-lsuart-v14"; + reg = <0xf991f000 0x1000>; + interrupts = <0 109 0>; + }; + + serial@f995e000 { + compatible = "qcom,msm-lsuart-v14"; + reg = <0xf995e000 0x1000>; + interrupts = <0 114 0>; + }; + + usb@f9a55000 { + compatible = "qcom,hsusb-otg"; + reg = <0xf9a55000 0x400>; + interrupts = <0 134 0>; + HSUSB_VDDCX-supply = <&pm8841_s2>; + HSUSB_1p8-supply = <&pm8941_l6>; + HSUSB_3p3-supply = <&pm8941_l24>; + + qcom,hsusb-otg-phy-type = <2>; + qcom,hsusb-otg-mode = <1>; + qcom,hsusb-otg-otg-control = <1>; + }; + + qcom,sdcc@f9824000 { + cell-index = <1>; + compatible = "qcom,msm-sdcc"; + reg = <0xf9824000 0x1000>; + interrupts = <0 123 0>; + + qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>; + qcom,sdcc-sup-voltages = <2950 2950>; + qcom,sdcc-bus-width = <8>; + qcom,sdcc-hs200; + qcom,sdcc-nonremovable; + }; + + qcom,sdcc@f98a4000 { + cell-index = <2>; + compatible = "qcom,msm-sdcc"; + reg = <0xf98a4000 0x1000>; + interrupts = <0 125 0>; + + qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>; + qcom,sdcc-sup-voltages = <2950 2950>; + qcom,sdcc-bus-width = <4>; + }; + + qcom,sdcc@f9864000 { + cell-index = <3>; + compatible = "qcom,msm-sdcc"; + reg = <0xf9864000 0x1000>; + interrupts = <0 127 0>; + + qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>; + qcom,sdcc-sup-voltages = <1800 1800>; + qcom,sdcc-bus-width = <4>; + status = "disable"; + }; + + qcom,sdcc@f98e4000 { + cell-index = <4>; + compatible = "qcom,msm-sdcc"; + reg = <0xf98e4000 0x1000>; + interrupts = <0 129 0>; + + qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>; + qcom,sdcc-sup-voltages = <1800 1800>; + qcom,sdcc-bus-width = <4>; + status = "disable"; + }; + + qcom,sps@f9980000 { + compatible = "qcom,msm_sps"; + reg = <0xf9984000 0x15000>, + <0xf9999000 0xb000>; + interrupts = <0 94 0>; + + qcom,bam-dma-res-pipes = <6>; + }; + + + spi@f9924000 { + compatible = "qcom,spi-qup-v2"; + reg = <0xf9924000 0x1000>; + interrupts = <0 96 0>; + spi-max-frequency = <25000000>; + }; + + slim@fe12f000 { + cell-index = <1>; + compatible = "qcom,slim-msm"; + reg = <0xfe12f000 0x35000>, + <0xfe104000 0x20000>; + reg-names = "slimbus_physical", "slimbus_bam_physical"; + interrupts = <0 163 0 0 164 0>; + interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + qcom,min-clk-gear = <10>; + }; + + qcom,spmi@fc4c0000 { + cell-index = <0>; + compatible = "qcom,spmi-pmic-arb"; + reg = <0xfc4cf000 0x1000>, + <0Xfc4cb000 0x1000>; + /* 190,ee0_krait_hlos_spmi_periph_irq */ + /* 187,channel_0_krait_hlos_trans_done_irq */ + interrupts = <0 190 0 0 187 0>; + qcom,pmic-arb-ee = <0>; + qcom,pmic-arb-channel = <0>; + qcom,pmic-arb-ppid-map = <0x13000000>, /* PM8941_LDO1 */ + <0x13100001>, /* PM8941_LDO2 */ + <0x13200002>, /* PM8941_LDO3 */ + <0x13300003>, /* PM8941_LDO4 */ + <0x13400004>, /* PM8941_LDO5 */ + <0x13500005>, /* PM8941_LDO6 */ + <0x13600006>, /* PM8941_LDO7 */ + <0x13700007>, /* PM8941_LDO8 */ + <0x13800008>, /* PM8941_LDO9 */ + <0x13900009>, /* PM8941_LDO10 */ + <0x13a0000a>, /* PM8941_LDO11 */ + <0x13b0000b>, /* PM8941_LDO12 */ + <0x13c0000c>, /* PM8941_LDO13 */ + <0x13d0000d>, /* PM8941_LDO14 */ + <0x13e0000e>, /* PM8941_LDO15 */ + <0x13f0000f>, /* PM8941_LDO16 */ + <0x14000010>, /* PM8941_LDO17 */ + <0x14100011>, /* PM8941_LDO18 */ + <0x14200012>, /* PM8941_LDO19 */ + <0x14300013>, /* PM8941_LDO20 */ + <0x14400014>, /* PM8941_LDO21 */ + <0x14500015>, /* PM8941_LDO22 */ + <0x14600016>, /* PM8941_LDO23 */ + <0x14700017>, /* PM8941_LDO24 */ + <0x14800018>, /* PM8941_LDO25 */ + <0x14900019>, /* PM8941_LDO26 */ + <0x0c00001a>, /* PM8941_GPIO1 */ + <0x0c10001b>, /* PM8941_GPIO2 */ + <0x0c20001c>, /* PM8941_GPIO3 */ + <0x0c30001d>, /* PM8941_GPIO4 */ + <0x0c40001e>, /* PM8941_GPIO5 */ + <0x0c50001f>, /* PM8941_GPIO6 */ + <0x0c600020>, /* PM8941_GPIO7 */ + <0x0c700021>, /* PM8941_GPIO8 */ + <0x0c800022>, /* PM8941_GPIO9 */ + <0x0c900023>, /* PM8941_GPIO10 */ + <0x0ca00024>, /* PM8941_GPIO11 */ + <0x0cb00025>, /* PM8941_GPIO12 */ + <0x0cc00026>, /* PM8941_GPIO13 */ + <0x0cd00027>, /* PM8941_GPIO14 */ + <0x0ce00028>, /* PM8941_GPIO15 */ + <0x0cf00029>, /* PM8941_GPIO16 */ + <0x0d00002a>, /* PM8941_GPIO17 */ + <0x0d10002b>, /* PM8941_GPIO18 */ + <0x0d20002c>, /* PM8941_GPIO19 */ + <0x0d30002d>, /* PM8941_GPIO20 */ + <0x0d40002e>, /* PM8941_GPIO21 */ + <0x0d50002f>, /* PM8941_GPIO22 */ + <0x0d600030>, /* PM8941_GPIO23 */ + <0x0d700031>, /* PM8941_GPIO24 */ + <0x0d800032>, /* PM8941_GPIO25 */ + <0x0d900033>, /* PM8941_GPIO26 */ + <0x0da00034>, /* PM8941_GPIO27 */ + <0x0db00035>, /* PM8941_GPIO28 */ + <0x0dc00036>, /* PM8941_GPIO29 */ + <0x0dd00037>, /* PM8941_GPIO30 */ + <0x0de00038>, /* PM8941_GPIO31 */ + <0x0df00039>, /* PM8941_GPIO32 */ + <0x0e00003a>, /* PM8941_GPIO33 */ + <0x0e10003b>, /* PM8941_GPIO34 */ + <0x0e20003c>, /* PM8941_GPIO35 */ + <0x0e30003d>, /* PM8941_GPIO36 */ + <0x0280003e>, /* COINCELL */ + <0x0100003f>, /* SMBC_OVP */ + <0x01100040>, /* SMBC_CHG */ + <0x01200041>, /* SMBC_BIF */ + <0x00500042>, /* INTERRUPT */ + <0x00100043>, /* PM8941_0 */ + <0x20100044>, /* PM8841_0 */ + <0x10100045>, /* PM8941_1 */ + <0x30100046>, /* PM8841_1 */ + <0x00800047>, /* PON0 */ + <0x20800048>, /* PON1 */ + <0x11000049>, /* PM8941_SMPS1 */ + <0x1110004a>, /* PM8941_SMPS2 */ + <0x1120004b>, /* PM8941_SMPS3 */ + <0x3100004c>, /* PM8841_SMPS1 */ + <0x3110004d>, /* PM8841_SMPS2 */ + <0x3120004e>, /* PM8841_SMPS3 */ + <0x3130004f>, /* PM8841_SMPS4 */ + <0x31400050>, /* PM8841_SMPS5 */ + <0x31500051>, /* PM8841_SMPS6 */ + <0x31600052>, /* PM8841_SMPS7 */ + <0x31700053>, /* PM8841_SMPS8 */ + <0x05000054>, /* SHARED_XO */ + <0x05100055>, /* BB_CLK1 */ + <0x05200056>, /* BB_CLK2 */ + <0x05900057>, /* SLEEP_CLK */ + <0x07000058>, /* PBS_CORE */ + <0x07100059>, /* PBS_CLIENT1 */ + <0x0720005a>; /* PBS_CLIENT2 */ + }; + + i2c@f9966000 { + cell-index = <0>; + compatible = "qcom,i2c-qup"; + reg = <0Xf9966000 0x1000>; + reg-names = "qup_phys_addr"; + interrupts = <0 104 0>; + interrupt-names = "qup_err_intr"; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <24000000>; + }; + + qcom,acpuclk@f9000000 { + compatible = "qcom,acpuclk-copper"; + }; + + qcom,ssusb@F9200000 { + compatible = "qcom,dwc-usb3-msm"; + reg = <0xF9200000 0xCCFF>; + interrupts = <0 131 0>; + qcom,dwc-usb3-msm-dbm-eps = <4>; + }; + + gdsc_oxili_gx: qcom,gdsc@fd8c4024 { + parent-supply = <&pm8841_s4>; + }; + + qcom,lpass@fe200000 { + compatible = "qcom,pil-q6v5-lpass"; + reg = <0xfe200000 0x00100>, + <0xfd485100 0x00010>; + + qcom,firmware-name = "adsp"; + }; + + qcom,mss@fc880000 { + compatible = "qcom,pil-q6v5-mss"; + reg = <0xfc880000 0x100>, + <0xfd485000 0x400>, + <0xfc820000 0x020>, + <0xfc401680 0x004>; + vdd_mss-supply = <&pm8841_s3>; + + qcom,firmware-name = "mba"; + qcom,pil-self-auth = <1>; + }; + + qcom,mba@fc820000 { + compatible = "qcom,pil-mba"; + reg = <0xfc820000 0x0020>, + <0x0d1fc000 0x4000>; + + qcom,firmware-name = "modem"; + qcom,depends-on = "mba"; + }; + + qcom,pronto@fb21b000 { + compatible = "qcom,pil-pronto"; + reg = <0xfb21b000 0x3000>, + <0xfc401700 0x4>, + <0xfd485300 0xc>; + vdd_pronto_pll-supply = <&pm8941_l12>; + + qcom,firmware-name = "wcnss"; + }; + + qcom,ocmem@fdd00000 { + compatible = "qcom,msm_ocmem"; + }; + + qcom,rpm-smd { + compatible = "qcom,rpm-smd"; + rpm-channel-name = "rpm_requests"; + rpm-channel-type = <15>; /* SMD_APPS_RPM */ + }; + + qcom,msm-rng@f9bff000 { + compatible = "qcom,msm-rng"; + reg = <0xf9bff000 0x200>; + }; +}; diff --git a/arch/arm/boot/dts/msmcopper_pm.dtsi b/arch/arm/boot/dts/msmcopper_pm.dtsi new file mode 100644 index 00000000000..0da320035e9 --- /dev/null +++ b/arch/arm/boot/dts/msmcopper_pm.dtsi @@ -0,0 +1,280 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "skeleton.dtsi" + +/ { + qcom,spm@f9089000 { + compatible = "qcom,spm-v2"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xf9089000 0x1000>; + qcom,core-id = <0>; + qcom,saw2-ver-reg = <0xfd0>; + qcom,saw2-cfg = <0x1b>; + qcom,saw2-avs-ctl = <0>; + qcom,saw2-avs-hysteresis = <0>; + qcom,saw2-avs-limit = <0>; + qcom,saw2-avs-dly= <0>; + qcom,saw2-spm-dly= <0x20000400>; + qcom,saw2-spm-ctl = <0x1>; + qcom,spm-cmd-wfi = [03 0b 0f]; + qcom,spm-cmd-spc = [00 20 50 80 60 70 10 92 + a0 b0 03 68 70 3b 92 a0 b0 + 82 2b 50 10 30 02 22 30 0f]; + qcom,spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92 + a0 b0 82 10 30 02 22 30 0f]; + }; + + qcom,spm@f9099000 { + compatible = "qcom,spm-v2"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xf9099000 0x1000>; + qcom,core-id = <1>; + qcom,saw2-ver-reg = <0xfd0>; + qcom,saw2-cfg = <0x1b>; + qcom,saw2-avs-ctl = <0>; + qcom,saw2-avs-hysteresis = <0>; + qcom,saw2-avs-limit = <0>; + qcom,saw2-avs-dly= <0>; + qcom,saw2-spm-dly= <0x20000400>; + qcom,saw2-spm-ctl = <0x1>; + qcom,spm-cmd-wfi = [03 0b 0f]; + qcom,spm-cmd-spc = [00 20 50 80 60 70 10 92 + a0 b0 03 68 70 3b 92 a0 b0 + 82 2b 50 10 30 02 22 30 0f]; + qcom,spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92 + a0 b0 82 10 30 02 22 30 0f]; + }; + + qcom,spm@f90a9000 { + compatible = "qcom,spm-v2"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xf90a9000 0x1000>; + qcom,core-id = <2>; + qcom,saw2-ver-reg = <0xfd0>; + qcom,saw2-cfg = <0x1b>; + qcom,saw2-avs-ctl = <0>; + qcom,saw2-avs-hysteresis = <0>; + qcom,saw2-avs-limit = <0>; + qcom,saw2-avs-dly= <0>; + qcom,saw2-spm-dly= <0x20000400>; + qcom,saw2-spm-ctl = <0x1>; + qcom,spm-cmd-wfi = [03 0b 0f]; + qcom,spm-cmd-spc = [00 20 50 80 60 70 10 92 + a0 b0 03 68 70 3b 92 a0 b0 + 82 2b 50 10 30 02 22 30 0f]; + qcom,spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92 + a0 b0 82 10 30 02 22 30 0f]; + }; + + qcom,spm@f90b9000 { + compatible = "qcom,spm-v2"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xf90b9000 0x1000>; + qcom,core-id = <3>; + qcom,saw2-ver-reg = <0xfd0>; + qcom,saw2-cfg = <0x1b>; + qcom,saw2-avs-ctl = <0>; + qcom,saw2-avs-hysteresis = <0>; + qcom,saw2-avs-limit = <0>; + qcom,saw2-avs-dly= <0>; + qcom,saw2-spm-dly= <0x20000400>; + qcom,saw2-spm-ctl = <0x1>; + qcom,spm-cmd-wfi = [03 0b 0f]; + qcom,spm-cmd-spc = [00 20 50 80 60 70 10 92 + a0 b0 03 68 70 3b 92 a0 b0 + 82 2b 50 10 30 02 22 30 0f]; + qcom,spm-cmd-pc = [00 20 10 92 a0 b0 07 3b 92 + a0 b0 82 10 30 02 22 30 0f]; + }; + + qcom,spm@f9012000 { + compatible = "qcom,spm-v2"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xf9012000 0x1000>; + qcom,core-id = <0xffff>; /* L2/APCS SAW */ + qcom,saw2-ver-reg = <0xfd0>; + qcom,saw2-cfg = <0x1a>; + qcom,saw2-avs-ctl = <0>; + qcom,saw2-avs-hysteresis = <0>; + qcom,saw2-avs-limit = <0>; + qcom,saw2-avs-dly= <0>; + qcom,saw2-spm-dly= <0x20000400>; + qcom,saw2-spm-ctl = <0x0>; /* TODO: Enable L2 SPM */ + qcom,saw2-pmic-dly = <0x02020204>; + qcom,saw2-pmic-data0 = <0x0400009c>; + qcom,saw2-pmic-data1 = <0x00000060>; + qcom,saw2-pmic-data2 = <0x0000001c>; + qcom,saw2-pmic-data3 = <0x04000000>; + qcom,vctl-timeout-us = <50>; + qcom,vctl-port = <0x0>; /* TODO: */ + qcom,phase-port = <0x1>; /* TODO: */ + qcom,spm-cmd-ret = [0b 00 20 03 22 00 0f]; + qcom,spm-cmd-spc = [00 20 32 60 70 80 42 03 + 78 80 44 22 50 3b 60 02 32 + 50 0f]; + qcom,spm-cmd-pc = [00 10 32 60 70 80 b0 11 42 + 07 01 b0 78 80 12 44 a0 50 + 3b 60 02 32 a0 50 0f]; + }; + + qcom,lpm-levels { + compatible = "qcom,lpm-levels"; + #address-cells = <1>; + #size-cells = <0>; + + qcom,lpm-level@0 { + reg = <0x0>; + qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */ + qcom,xo = <1>; /* ON */ + qcom,l2 = <3>; /* ACTIVE */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <100>; + qcom,ss-power = <650>; + qcom,energy-overhead = <801>; + qcom,time-overhead = <200>; + }; + + qcom,lpm-level@1 { + reg = <0x1>; + qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */ + qcom,xo = <1>; /* ON */ + qcom,l2 = <3>; /* ACTIVE */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <2000>; + qcom,ss-power = <200>; + qcom,energy-overhead = <576000>; + qcom,time-overhead = <2000>; + }; + + qcom,lpm-level@2 { + reg = <0x2>; + qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <1>; /* ON */ + qcom,l2 = <1>; /* GDHS */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <8500>; + qcom,ss-power = <51>; + qcom,energy-overhead = <1122000>; + qcom,time-overhead = <8500>; + }; + + qcom,lpm-level@3 { + reg = <0x3>; + qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <1>; /* ON */ + qcom,l2 = <0>; /* OFF */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <9000>; + qcom,ss-power = <51>; + qcom,energy-overhead = <1130300>; + qcom,time-overhead = <9000>; + }; + + qcom,lpm-level@4 { + reg = <0x4>; + qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <1>; /* ON */ + qcom,l2 = <0>; /* OFF */ + qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */ + qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */ + qcom,vdd-dig-upper-bound = <950000>; /* ACTIVE */ + qcom,vdd-dig-lower-bound = <750000>; /* RETENTION HIGH */ + qcom,latency-us = <10000>; + qcom,ss-power = <51>; + qcom,energy-overhead = <1130300>; + qcom,time-overhead = <10000>; + }; + + qcom,lpm-level@5 { + reg = <0x5>; + qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <0>; /* OFF */ + qcom,l2 = <1>; /* GDHS */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <12000>; + qcom,ss-power = <14>; + qcom,energy-overhead = <2205900>; + qcom,time-overhead = <12000>; + }; + + qcom,lpm-level@6 { + reg = <0x6>; + qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <0>; /* OFF */ + qcom,l2 = <0>; /* OFF */ + qcom,vdd-mem-upper-bound = <1150000>; /* MAX */ + qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */ + qcom,vdd-dig-upper-bound = <1150000>; /* MAX */ + qcom,vdd-dig-lower-bound = <950000>; /* ACTIVE */ + qcom,latency-us = <18000>; + qcom,ss-power = <12>; + qcom,energy-overhead = <2364250>; + qcom,time-overhead = <18000>; + }; + + qcom,lpm-level@7 { + reg = <0x7>; + qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <0>; /* OFF */ + qcom,l2 = <0>; /* OFF */ + qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */ + qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */ + qcom,vdd-dig-upper-bound = <950000>; /* ACTIVE */ + qcom,vdd-dig-lower-bound = <750000>; /* RETIONTION HIGH */ + qcom,latency-us = <23500>; + qcom,ss-power = <10>; + qcom,energy-overhead = <2667000>; + qcom,time-overhead = <23500>; + }; + + qcom,lpm-level@8 { + reg = <0x8>; + qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */ + qcom,xo = <0>; /* OFF */ + qcom,l2 = <0>; /* OFF */ + qcom,vdd-mem-upper-bound = <750000>; /* RETENTION HIGH */ + qcom,vdd-mem-lower-bound = <750000>; /* RETENTION LOW */ + qcom,vdd-dig-upper-bound = <750000>; /* RETENTION HIGH */ + qcom,vdd-dig-lower-bound = <500000>; /* RETENTION LOW */ + qcom,latency-us = <29700>; + qcom,ss-power = <5>; + qcom,energy-overhead = <2867000>; + qcom,time-overhead = <30000>; + }; + }; + + qcom,pm-boot { + compatible = "qcom,pm-boot"; + qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */ + }; +}; diff --git a/arch/arm/configs/fsm9xxx-perf_defconfig b/arch/arm/configs/fsm9xxx-perf_defconfig new file mode 100644 index 00000000000..18f6d7fff5a --- /dev/null +++ b/arch/arm/configs/fsm9xxx-perf_defconfig @@ -0,0 +1,184 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="$(KERNEL_LOCAL_VERSION)" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +# CONFIG_PERF_EVENTS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_FSM9XXX=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG3=y +# CONFIG_MSM_SMD_DEBUG is not set +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_ONCRPCROUTER_DEBUG is not set +# CONFIG_MSM_HW3D is not set +# CONFIG_QSD_AUDIO is not set +# CONFIG_SURF_FFA_GPIO_KEYPAD is not set +CONFIG_MSM_WATCHDOG=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M ip=dhcp" +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_PIMSM_V2=y +# CONFIG_NET_ACTIVITY_STATS is not set +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=8 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_PMIC8058_XOADC=y +CONFIG_QFP_FUSE=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_MSM_RMNET is not set +CONFIG_QFEC=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_DIAG_OVER_USB is not set +# CONFIG_HW_RANDOM is not set +CONFIG_DCC_TTY=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_SSBI=y +CONFIG_SPI=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_PMIC8058=y +# CONFIG_MFD_PM8XXX_PWM is not set +# CONFIG_MFD_PM8XXX_MISC is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_PM8058_XO=y +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DEBUG=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +# CONFIG_STACKTRACE is not set +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_CRYPTD=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/fsm9xxx_defconfig b/arch/arm/configs/fsm9xxx_defconfig new file mode 100644 index 00000000000..47075560d9e --- /dev/null +++ b/arch/arm/configs/fsm9xxx_defconfig @@ -0,0 +1,186 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="$(KERNEL_LOCAL_VERSION)" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +# CONFIG_PERF_EVENTS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_FSM9XXX=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG3=y +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_HW3D is not set +# CONFIG_QSD_AUDIO is not set +# CONFIG_SURF_FFA_GPIO_KEYPAD is not set +CONFIG_MSM_WATCHDOG=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M ip=dhcp" +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_PIMSM_V2=y +# CONFIG_NET_ACTIVITY_STATS is not set +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=8 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_PMIC8058_XOADC=y +CONFIG_QFP_FUSE=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_MSM_RMNET is not set +CONFIG_QFEC=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_DIAG_OVER_USB is not set +# CONFIG_HW_RANDOM is not set +CONFIG_DCC_TTY=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_SSBI=y +CONFIG_SPI=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_PMIC8058=y +# CONFIG_MFD_PM8XXX_PWM is not set +# CONFIG_MFD_PM8XXX_MISC is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_PM8058_XO=y +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DEBUG=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_SCHEDSTATS=y +# CONFIG_STACKTRACE is not set +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_INFO=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_CRYPTD=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig new file mode 100644 index 00000000000..473a7d57fad --- /dev/null +++ b/arch/arm/configs/msm-copper_defconfig @@ -0,0 +1,206 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +# CONFIG_FAIR_GROUP_SCHED is not set +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_KALLSYMS_ALL=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSMCOPPER=y +CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_KERNEL_PMEM_EBI_REGION=y +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_IPC_ROUTER=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_PIL_LPASS_QDSP6V5=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_MSM_PIL_MBA=y +CONFIG_MSM_PIL_PRONTO=y +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_DIRECT_SCLK_ACCESS=y +CONFIG_MSM_OCMEM=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +# CONFIG_SMP_ON_UP is not set +CONFIG_ARM_ARCH_TIMER=y +CONFIG_HOTPLUG_CPU=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_USE_OF=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_SUSPEND is not set +CONFIG_NET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_NETDEVICES=y +CONFIG_KS8851=m +# CONFIG_MSM_RMNET is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_DCC_TTY=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QUP=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB=y +CONFIG_MSM_QPNP=y +CONFIG_SLIMBUS=y +CONFIG_SLIMBUS_MSM_CTRL=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +# CONFIG_BATTERY_MSM is not set +# CONFIG_HWMON is not set +CONFIG_REGULATOR_STUB=y +CONFIG_REGULATOR_QPNP=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +# CONFIG_VIDEO_CAPTURE_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y +# CONFIG_HID_SUPPORT is not set +CONFIG_USB_GADGET=y +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_G_ANDROID=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_SWITCH=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_BAMDMA=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_MSM_IOMMU=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_LL=y +CONFIG_EARLY_PRINTK=y +CONFIG_KEYS=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRC_CCITT=y +CONFIG_LIBCRC32C=y diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig new file mode 100644 index 00000000000..447427ab7b8 --- /dev/null +++ b/arch/arm/configs/msm7627a-perf_defconfig @@ -0,0 +1,344 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="$(KERNEL_LOCAL_VERSION)-perf" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM7X27=y +CONFIG_ARCH_MSM8625=y +CONFIG_MSM_SOC_REV_A=y +# CONFIG_MACH_MSM7X27_SURF is not set +# CONFIG_MACH_MSM7X27_FFA is not set +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +# CONFIG_MSM_FIQ_SUPPORT is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +# CONFIG_MSM_SMD_DEBUG is not set +# CONFIG_MSM_RESET_MODEM is not set +# CONFIG_MSM_SMD_NMEA is not set +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM7X27A_AUDIO=y +CONFIG_MSM_DMA_TEST=y +CONFIG_MSM_SLEEP_STATS_DEVICE=y +CONFIG_BT_MSM_PINTEST=y +CONFIG_MSM_RPC_VIBRATOR=y +CONFIG_PM8XXX_RPC_VIBRATOR=y +CONFIG_MSM_SPM_V2=y +CONFIG_ARM_THUMBEE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0xC800000 +CONFIG_CP_ACCESS=y +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_IBS=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_LIBRA_SDIOIF=m +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_TOUCHSCREEN_FT5X06=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_DIAG_CHAR=y +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SX150X=y +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_MSM=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_MARIMBA_CORE=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA_V4L2=y +CONFIG_OV5647=y +CONFIG_OV8825=y +CONFIG_AD5046_ACT=y +CONFIG_WEBCAM_OV9726=y +CONFIG_MT9E013=y +CONFIG_S5K4E1=y +CONFIG_DW9712_ACT=y +CONFIG_MSM_CAMERA_FLASH_SC628A=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +CONFIG_OV7692=y +CONFIG_RADIO_TAVARUA=y +CONFIG_MSM_KGSL=y +CONFIG_FB=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP30=y +CONFIG_FB_MSM_MDP303=y +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL=y +CONFIG_FB_MSM_MIPI_PANEL_DETECT=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_MSM_SOC=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MSM_72K=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_MSM_PDM=y +CONFIG_LEDS_PMIC_MPP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SHIRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_INFO=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig new file mode 100644 index 00000000000..639eb105355 --- /dev/null +++ b/arch/arm/configs/msm7627a_defconfig @@ -0,0 +1,352 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="$(KERNEL_LOCAL_VERSION)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM7X27=y +CONFIG_ARCH_MSM8625=y +CONFIG_MSM_SOC_REV_A=y +# CONFIG_MACH_MSM7X27_SURF is not set +# CONFIG_MACH_MSM7X27_FFA is not set +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM7X00A_USE_DG_TIMER=y +# CONFIG_MSM_FIQ_SUPPORT is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +# CONFIG_MSM_SMD_DEBUG is not set +# CONFIG_MSM_RESET_MODEM is not set +# CONFIG_MSM_SMD_NMEA is not set +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM7X27A_AUDIO=y +CONFIG_MSM_DMA_TEST=y +CONFIG_MSM_SLEEP_STATS_DEVICE=y +CONFIG_BT_MSM_PINTEST=y +CONFIG_MSM_RPC_VIBRATOR=y +CONFIG_PM8XXX_RPC_VIBRATOR=y +CONFIG_MSM_SPM_V2=y +CONFIG_ARM_THUMBEE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0xC800000 +CONFIG_CP_ACCESS=y +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_IBS=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_LIBRA_SDIOIF=m +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C=y +CONFIG_TOUCHSCREEN_FT5X06=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_DIAG_CHAR=y +# CONFIG_HW_RANDOM is not set +CONFIG_DCC_TTY=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SX150X=y +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_MSM=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_MARIMBA_CORE=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA_V4L2=y +CONFIG_OV5647=y +CONFIG_OV8825=y +CONFIG_AD5046_ACT=y +CONFIG_WEBCAM_OV9726=y +CONFIG_MT9E013=y +CONFIG_S5K4E1=y +CONFIG_DW9712_ACT=y +CONFIG_MSM_CAMERA_FLASH_SC628A=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +CONFIG_OV7692=y +CONFIG_RADIO_TAVARUA=y +CONFIG_MSM_KGSL=y +CONFIG_FB=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP30=y +CONFIG_FB_MSM_MDP303=y +CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335_PT_PANEL=y +CONFIG_FB_MSM_MIPI_PANEL_DETECT=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_MSM_SOC=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MSM_72K=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +CONFIG_LEDS_MSM_PDM=y +CONFIG_LEDS_PMIC_MPP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SHIRQ=y +CONFIG_LOCKUP_DETECTOR=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_SLAB=y +CONFIG_DEBUG_SLAB_LEAK=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig new file mode 100644 index 00000000000..c39b4033308 --- /dev/null +++ b/arch/arm/configs/msm7630-perf_defconfig @@ -0,0 +1,378 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="-perf" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM7X30=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG3=y +CONFIG_MSM_SDIO_DMUX=y +CONFIG_MSM_SDIO_CMUX=y +CONFIG_MSM_SDIO_CTL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_RPC_WATCHDOG=y +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_HW3D is not set +# CONFIG_QSD_AUDIO is not set +CONFIG_MSM_MEMORY_LOW_POWER_MODE=y +CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION=y +CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN=y +CONFIG_MSM_IDLE_WAIT_ON_MODEM=2000 +CONFIG_MSM_STANDALONE_POWER_COLLAPSE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x1A000000 +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M ip=dhcp" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_BASIC=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_IBS=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=y +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=8 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_HAPTIC_ISA1200=y +CONFIG_PMIC8XXX_UPL=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_MSM_RMNET_SDIO=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_LIBRA_SDIOIF=m +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_PMIC8XXX=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_MSM=y +CONFIG_TOUCHSCREEN_TSC2007=y +CONFIG_TOUCHSCREEN_CY8C_TS=y +CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +CONFIG_BOSCH_BMA150=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_DIAG_CHAR=y +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QUP=y +CONFIG_I2C_SSBI=y +CONFIG_SPI=y +CONFIG_SPI_QSD=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_MSM=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_MSM_POPMEM=y +CONFIG_PMIC8058=y +CONFIG_MARIMBA_CORE=y +CONFIG_MARIMBA_CODEC=y +CONFIG_TIMPANI_CODEC=y +# CONFIG_MFD_PM8XXX_DEBUG is not set +# CONFIG_MFD_PM8XXX_PWM is not set +# CONFIG_MFD_PM8XXX_MISC is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_RADIO_TAVARUA=y +CONFIG_MSM_KGSL=y +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_LOGO=y +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP40=y +CONFIG_FB_MSM_OVERLAY=y +CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y +CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y +CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_SOUND=y +CONFIG_SND=y +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_SOC=y +CONFIG_SND_MSM7KV2_SOC=y +CONFIG_SND_MVS_SOC=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MSM_72K=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL="" +CONFIG_USB_MSM_ACA=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +# CONFIG_MMC_MSM_SDC1_SUPPORT is not set +CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC4_SUPPORT=y +CONFIG_LEDS_PMIC8058=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DEBUG=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_INFO=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm7630_defconfig b/arch/arm/configs/msm7630_defconfig new file mode 100644 index 00000000000..8dda15fdd59 --- /dev/null +++ b/arch/arm/configs/msm7630_defconfig @@ -0,0 +1,386 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM7X30=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG3=y +CONFIG_MSM_SDIO_DMUX=y +CONFIG_MSM_SDIO_CMUX=y +CONFIG_MSM_SDIO_CTL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_RPC_WATCHDOG=y +CONFIG_MSM_RMT_STORAGE_CLIENT=y +# CONFIG_MSM_HW3D is not set +# CONFIG_QSD_AUDIO is not set +CONFIG_MSM_MEMORY_LOW_POWER_MODE=y +CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION=y +CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN=y +CONFIG_MSM_IDLE_WAIT_ON_MODEM=2000 +CONFIG_MSM_STANDALONE_POWER_COLLAPSE=y +CONFIG_STRICT_MEMORY_RWX=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x1A000000 +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="init=/sbin/init root=/dev/ram rw initrd=0x11000000,16M console=ttyDCC0 mem=88M ip=dhcp" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_BASIC=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_IBS=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=y +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=8 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_HAPTIC_ISA1200=y +CONFIG_PMIC8XXX_UPL=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_MSM_RMNET_SDIO=y +CONFIG_SMC91X=y +CONFIG_SMSC911X=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_LIBRA_SDIOIF=m +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_PMIC8XXX=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_MSM=y +CONFIG_TOUCHSCREEN_TSC2007=y +CONFIG_TOUCHSCREEN_CY8C_TS=y +CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +CONFIG_BOSCH_BMA150=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_DIAG_CHAR=y +# CONFIG_HW_RANDOM is not set +CONFIG_DCC_TTY=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QUP=y +CONFIG_I2C_SSBI=y +CONFIG_SPI=y +CONFIG_SPI_QSD=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_MSM=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_MSM_POPMEM=y +CONFIG_PMIC8058=y +CONFIG_MARIMBA_CORE=y +CONFIG_MARIMBA_CODEC=y +CONFIG_TIMPANI_CODEC=y +# CONFIG_MFD_PM8XXX_DEBUG is not set +# CONFIG_MFD_PM8XXX_PWM is not set +# CONFIG_MFD_PM8XXX_MISC is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_RADIO_TAVARUA=y +CONFIG_MSM_KGSL=y +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_LOGO=y +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP40=y +CONFIG_FB_MSM_OVERLAY=y +CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y +CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y +CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_SOUND=y +CONFIG_SND=y +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_SOC=y +CONFIG_SND_MSM7KV2_SOC=y +CONFIG_SND_MVS_SOC=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MSM_72K=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +CONFIG_RMNET_SDIO_SMD_DATA_CHANNEL="" +CONFIG_USB_MSM_ACA=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +# CONFIG_MMC_MSM_SDC1_SUPPORT is not set +CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC4_SUPPORT=y +CONFIG_LEDS_PMIC8058=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DEBUG=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_LOCKUP_DETECTOR=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_SLAB=y +CONFIG_DEBUG_SLAB_LEAK=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_PROVE_LOCKING=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig new file mode 100644 index 00000000000..d5b4007c211 --- /dev/null +++ b/arch/arm/configs/msm8660-perf_defconfig @@ -0,0 +1,450 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="-perf" +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# 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_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_DEFAULT_DEADLINE=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM8X60=y +CONFIG_MACH_MSM8X60_RUMI3=y +CONFIG_MACH_MSM8X60_SIM=y +CONFIG_MACH_MSM8X60_SURF=y +CONFIG_MACH_MSM8X60_FFA=y +CONFIG_MACH_MSM8X60_FLUID=y +CONFIG_MACH_MSM8X60_FUSION=y +CONFIG_MACH_MSM8X60_FUSN_FFA=y +CONFIG_MACH_MSM8X60_DRAGON=y +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE=y +CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SDIO_DMUX=y +# CONFIG_MSM_RESET_MODEM is not set +# CONFIG_MSM_SMD_NMEA is not set +CONFIG_MSM_SDIO_TTY=y +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_SDIO_CMUX=y +CONFIG_MSM_DSPS=y +CONFIG_MSM_SDIO_CTL=y +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +# CONFIG_MSM_RPCSERVER_WATCHDOG is not set +# CONFIG_MSM_RPCSERVER_HANDSET is not set +CONFIG_MSM_RMT_STORAGE_CLIENT=y +CONFIG_MSM_SDIO_SMEM=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_PIL_MODEM=y +CONFIG_MSM_PIL_QDSP6V3=y +CONFIG_MSM_PIL_TZAPPS=y +CONFIG_MSM_PIL_DSPS=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_MSM_WATCHDOG=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_MSM_ETM=y +CONFIG_MSM_GSBI9_UART=y +CONFIG_STRICT_MEMORY_RWX=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_NR_CPUS=2 +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_CP_ACCESS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_BASIC=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_IBS=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=y +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_UID_STAT=y +CONFIG_TSIF=m +CONFIG_TSIF_CHRDEV=m +CONFIG_HAPTIC_ISA1200=y +CONFIG_PMIC8XXX_VIBRATOR=y +CONFIG_PMIC8XXX_UPL=y +CONFIG_PMIC8058_XOADC=y +CONFIG_QSEECOM=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_MSM_RMNET_SDIO=y +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_ASYNC=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_LIBRA_SDIOIF=m +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_MATRIX=y +CONFIG_KEYBOARD_PMIC8XXX=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CY8C_TS=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PMIC8XXX_PWRKEY=y +CONFIG_INPUT_UINPUT=y +CONFIG_PMIC8058_OTHC=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_I2C_SSBI=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SX150X=y +CONFIG_POWER_SUPPLY=y +# CONFIG_BATTERY_MSM is not set +CONFIG_BATTERY_MSM8X60=y +CONFIG_PM8058_CHARGER=y +CONFIG_ISL9519_CHARGER=y +CONFIG_SMB137B_CHARGER=y +CONFIG_BATTERY_BQ27520=y +CONFIG_BATTERY_BQ27541=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS=y +CONFIG_THERMAL_PM8XXX=y +CONFIG_PMIC8058=y +CONFIG_PMIC8901=y +CONFIG_MARIMBA_CORE=y +CONFIG_TIMPANI_CODEC=y +# CONFIG_MFD_PM8XXX_PWM is not set +CONFIG_MFD_PM8XXX_BATT_ALARM=y +CONFIG_REGULATOR_MSM_GPIO=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA_V4L2=y +CONFIG_IMX074=y +CONFIG_WEBCAM_OV9726=y +CONFIG_MT9E013=y +CONFIG_IMX074_ACT=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +CONFIG_MSM_GEMINI=y +CONFIG_OV7692=y +CONFIG_RADIO_TAVARUA=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=24 +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP40=y +CONFIG_FB_MSM_OVERLAY=y +CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y +CONFIG_FB_MSM_OVERLAY1_WRITEBACK=y +CONFIG_FB_MSM_WRITEBACK_MSM_PANEL=y +CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT=y +CONFIG_FB_MSM_HDMI_MSM_PANEL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MSM_72K=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC4_SUPPORT=y +CONFIG_MMC_MSM_SDC5_SUPPORT=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PMIC8058=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_SLEEP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_MSM_IOMMU=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_CIFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_INFO=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig new file mode 100644 index 00000000000..5e2c1a81a38 --- /dev/null +++ b/arch/arm/configs/msm8660_defconfig @@ -0,0 +1,462 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# 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_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_DEFAULT_DEADLINE=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM8X60=y +CONFIG_MACH_MSM8X60_RUMI3=y +CONFIG_MACH_MSM8X60_SIM=y +CONFIG_MACH_MSM8X60_SURF=y +CONFIG_MACH_MSM8X60_FFA=y +CONFIG_MACH_MSM8X60_FLUID=y +CONFIG_MACH_MSM8X60_FUSION=y +CONFIG_MACH_MSM8X60_FUSN_FFA=y +CONFIG_MACH_MSM8X60_DRAGON=y +CONFIG_MSM7X00A_USE_DG_TIMER=y +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE=y +CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SDIO_DMUX=y +# CONFIG_MSM_RESET_MODEM is not set +# CONFIG_MSM_SMD_NMEA is not set +CONFIG_MSM_SDIO_TTY=y +# CONFIG_MSM_SMD_QMI is not set +CONFIG_MSM_SDIO_CMUX=y +CONFIG_MSM_DSPS=y +CONFIG_MSM_SDIO_CTL=y +CONFIG_MSM_ONCRPCROUTER=y +# CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set +# CONFIG_MSM_RPCSERVER_WATCHDOG is not set +# CONFIG_MSM_RPCSERVER_HANDSET is not set +CONFIG_MSM_RMT_STORAGE_CLIENT=y +CONFIG_MSM_SDIO_SMEM=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_PIL_MODEM=y +CONFIG_MSM_PIL_QDSP6V3=y +CONFIG_MSM_PIL_TZAPPS=y +CONFIG_MSM_PIL_DSPS=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_MSM_WATCHDOG=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_MSM_ETM=y +CONFIG_MSM_SLEEP_STATS=y +CONFIG_MSM_GSBI9_UART=y +CONFIG_STRICT_MEMORY_RWX=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_NR_CPUS=2 +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_CP_ACCESS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_BASIC=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_IBS=y +CONFIG_MSM_BT_POWER=y +CONFIG_CFG80211=y +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_UID_STAT=y +CONFIG_TSIF=m +CONFIG_TSIF_CHRDEV=m +CONFIG_HAPTIC_ISA1200=y +CONFIG_PMIC8XXX_VIBRATOR=y +CONFIG_PMIC8XXX_UPL=y +CONFIG_PMIC8058_XOADC=y +CONFIG_QSEECOM=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_MSM_RMNET_SDIO=y +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_ASYNC=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_LIBRA_SDIOIF=m +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_MATRIX=y +CONFIG_KEYBOARD_PMIC8XXX=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CY8C_TS=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PMIC8XXX_PWRKEY=y +CONFIG_INPUT_UINPUT=y +CONFIG_PMIC8058_OTHC=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_DCC_TTY=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_I2C_SSBI=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SX150X=y +CONFIG_POWER_SUPPLY=y +# CONFIG_BATTERY_MSM is not set +CONFIG_BATTERY_MSM8X60=y +CONFIG_PM8058_CHARGER=y +CONFIG_ISL9519_CHARGER=y +CONFIG_SMB137B_CHARGER=y +CONFIG_BATTERY_BQ27520=y +CONFIG_BATTERY_BQ27541=y +CONFIG_SENSORS_MSM_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS=y +CONFIG_THERMAL_PM8XXX=y +CONFIG_PMIC8058=y +CONFIG_PMIC8901=y +CONFIG_MARIMBA_CORE=y +CONFIG_TIMPANI_CODEC=y +# CONFIG_MFD_PM8XXX_PWM is not set +CONFIG_MFD_PM8XXX_BATT_ALARM=y +CONFIG_REGULATOR_MSM_GPIO=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA_V4L2=y +CONFIG_IMX074=y +CONFIG_WEBCAM_OV9726=y +CONFIG_MT9E013=y +CONFIG_IMX074_ACT=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +CONFIG_MSM_GEMINI=y +CONFIG_OV7692=y +CONFIG_RADIO_TAVARUA=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=24 +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP40=y +CONFIG_FB_MSM_OVERLAY=y +CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y +CONFIG_FB_MSM_OVERLAY1_WRITEBACK=y +CONFIG_FB_MSM_WRITEBACK_MSM_PANEL=y +CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT=y +CONFIG_FB_MSM_HDMI_MSM_PANEL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_MSM_72K=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA40_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA40" +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT=y +CONFIG_MMC_MSM_SDC4_SUPPORT=y +CONFIG_MMC_MSM_SDC5_SUPPORT=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PMIC8058=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_SLEEP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_MSM_IOMMU=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_CIFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_SLUB_DEBUG_ON=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_VM=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_SG=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_LL=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig new file mode 100644 index 00000000000..74d31b34414 --- /dev/null +++ b/arch/arm/configs/msm8960-perf_defconfig @@ -0,0 +1,483 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="-perf" +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# 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_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM8960=y +CONFIG_ARCH_MSM8930=y +CONFIG_ARCH_APQ8064=y +CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y +CONFIG_MACH_MSM8960_SIM=y +CONFIG_MACH_MSM8960_RUMI3=y +CONFIG_MACH_MSM8960_CDP=y +CONFIG_MACH_MSM8960_MTP=y +CONFIG_MACH_MSM8960_FLUID=y +CONFIG_MACH_MSM8960_LIQUID=y +CONFIG_MACH_MSM8930_CDP=y +CONFIG_MACH_MSM8930_MTP=y +CONFIG_MACH_MSM8930_FLUID=y +CONFIG_MACH_MSM8627_CDP=y +CONFIG_MACH_MSM8627_MTP=y +CONFIG_MACH_APQ8064_SIM=y +CONFIG_MACH_APQ8064_RUMI3=y +CONFIG_MACH_APQ8064_CDP=y +CONFIG_MACH_APQ8064_MTP=y +CONFIG_MACH_APQ8064_LIQUID=y +CONFIG_MACH_MPQ8064_CDP=y +CONFIG_MACH_MPQ8064_HRD=y +CONFIG_MACH_MPQ8064_DTV=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_KERNEL_PMEM_EBI_REGION=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_PCIE=y +CONFIG_MSM_BAM_DMUX=y +CONFIG_MSM_RMNET_SMUX=y +CONFIG_MSM_DSPS=y +CONFIG_MSM_IPC_ROUTER=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_PIL_QDSP6V4=y +CONFIG_MSM_PIL_RIVA=y +CONFIG_MSM_PIL_TZAPPS=y +CONFIG_MSM_PIL_DSPS=y +CONFIG_MSM_PIL_VIDC=y +CONFIG_MSM_PIL_GSS=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_MODEM_8960=y +CONFIG_MSM_LPASS_8960=y +CONFIG_MSM_WCNSS_SSR_8960=y +CONFIG_MSM_GSS_SSR_8064=y +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_MSM_BUS_SCALING=y +CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y +CONFIG_MSM_WATCHDOG=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_MSM_QDSS=y +CONFIG_MSM_SLEEP_STATS=y +CONFIG_MSM_CACHE_ERP=y +CONFIG_MSM_L2_ERP_2BIT_PANIC=y +CONFIG_MSM_DCVS=y +CONFIG_MSM_HSIC_SYSMON=y +CONFIG_STRICT_MEMORY_RWX=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +# CONFIG_SMP_ON_UP is not set +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_CC_STACKPROTECTOR=y +CONFIG_CP_ACCESS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCISMD=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_HAPTIC_ISA1200=y +CONFIG_PMIC8XXX_VIBRATOR=y +CONFIG_QSEECOM=y +CONFIG_USB_HSIC_SMSC_HUB=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_KS8851=m +# CONFIG_MSM_RMNET is not set +CONFIG_MSM_RMNET_BAM=y +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_USB_USBNET=y +CONFIG_MSM_RMNET_USB=y +CONFIG_WCNSS_CORE=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_MATRIX=y +CONFIG_KEYBOARD_PMIC8XXX=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PMIC8XXX_PWRKEY=y +CONFIG_INPUT_UINPUT=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_N_SMUX=y +CONFIG_N_SMUX_LOOPBACK=y +CONFIG_SMUX_CTL=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_SLIMBUS_MSM_CTRL=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SX150X=y +CONFIG_POWER_SUPPLY=y +# CONFIG_BATTERY_MSM is not set +CONFIG_ISL9519_CHARGER=y +CONFIG_SMB349_CHARGER=y +CONFIG_PM8921_CHARGER=y +CONFIG_PM8921_BMS=y +CONFIG_PM8921_BCL=y +CONFIG_SENSORS_PM8XXX_ADC=y +CONFIG_SENSORS_EPM_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS8960=y +CONFIG_THERMAL_PM8XXX=y +CONFIG_THERMAL_MONITOR=y +CONFIG_MFD_PM8921_CORE=y +CONFIG_MFD_PM8821_CORE=y +CONFIG_MFD_PM8038_CORE=y +CONFIG_MFD_PM8XXX_SPK=y +CONFIG_MFD_PM8XXX_BATT_ALARM=y +CONFIG_WCD9304_CODEC=y +CONFIG_WCD9310_CODEC=y +CONFIG_REGULATOR_PM8XXX=y +CONFIG_REGULATOR_MSM_GPIO=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_USER_RC_INPUT=y +CONFIG_IR_GPIO_CIR=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA_V4L2=y +CONFIG_IMX074=y +CONFIG_MT9M114=y +CONFIG_IMX074_ACT=y +CONFIG_MSM_CAMERA_FLASH_SC628A=y +CONFIG_MSM_CAMERA_FLASH_TPS61310=y +CONFIG_OV2720=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +CONFIG_MSM_EEPROM=y +CONFIG_IMX074_EEPROM=y +CONFIG_IMX091_EEPROM=y +CONFIG_MSM_GEMINI=y +CONFIG_S5K3L1YX=y +CONFIG_IMX091=y +CONFIG_RADIO_IRIS=y +CONFIG_RADIO_IRIS_TRANSPORT=m +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=24 +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP40=y +CONFIG_FB_MSM_OVERLAY=y +CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y +CONFIG_FB_MSM_OVERLAY1_WRITEBACK=y +CONFIG_FB_MSM_WRITEBACK_MSM_PANEL=y +CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT=y +CONFIG_FB_MSM_HDMI_MSM_PANEL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +# CONFIG_SND_ARM is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8960=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_MSM=y +CONFIG_USB_EHCI_MSM_HSIC=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_QUALCOMM=y +CONFIG_USB_SERIAL_CSVT=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_QCOM_DIAG_BRIDGE=y +CONFIG_USB_QCOM_MDM_BRIDGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_G_ANDROID=y +CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT=y +# CONFIG_MMC_MSM_SDC2_SUPPORT is not set +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_WP_SUPPORT=y +CONFIG_MMC_MSM_SPS_SUPPORT=y +CONFIG_LEDS_PM8XXX=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_BAMDMA=y +CONFIG_MSM_IOMMU=y +CONFIG_MOBICORE_SUPPORT=m +CONFIG_MOBICORE_API=m +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_CIFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_PID_IN_CONTEXTIDR=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig new file mode 100644 index 00000000000..07a3f91239f --- /dev/null +++ b/arch/arm/configs/msm8960_defconfig @@ -0,0 +1,500 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# 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_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_ASHMEM=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM8960=y +CONFIG_ARCH_MSM8930=y +CONFIG_ARCH_APQ8064=y +CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y +CONFIG_MACH_MSM8960_SIM=y +CONFIG_MACH_MSM8960_RUMI3=y +CONFIG_MACH_MSM8960_CDP=y +CONFIG_MACH_MSM8960_MTP=y +CONFIG_MACH_MSM8960_FLUID=y +CONFIG_MACH_MSM8960_LIQUID=y +CONFIG_MACH_MSM8930_CDP=y +CONFIG_MACH_MSM8930_MTP=y +CONFIG_MACH_MSM8930_FLUID=y +CONFIG_MACH_MSM8627_CDP=y +CONFIG_MACH_MSM8627_MTP=y +CONFIG_MACH_APQ8064_SIM=y +CONFIG_MACH_APQ8064_RUMI3=y +CONFIG_MACH_APQ8064_CDP=y +CONFIG_MACH_APQ8064_MTP=y +CONFIG_MACH_APQ8064_LIQUID=y +CONFIG_MACH_MPQ8064_CDP=y +CONFIG_MACH_MPQ8064_HRD=y +CONFIG_MACH_MPQ8064_DTV=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_KERNEL_PMEM_EBI_REGION=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_PCIE=y +CONFIG_MSM_BAM_DMUX=y +CONFIG_MSM_DSPS=y +CONFIG_MSM_IPC_ROUTER=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_PIL_QDSP6V4=y +CONFIG_MSM_PIL_RIVA=y +CONFIG_MSM_PIL_TZAPPS=y +CONFIG_MSM_PIL_DSPS=y +CONFIG_MSM_PIL_VIDC=y +CONFIG_MSM_PIL_GSS=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_MODEM_8960=y +CONFIG_MSM_LPASS_8960=y +CONFIG_MSM_WCNSS_SSR_8960=y +CONFIG_MSM_GSS_SSR_8064=y +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_MSM_BUS_SCALING=y +CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y +CONFIG_MSM_WATCHDOG=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_MSM_QDSS=y +CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE=y +CONFIG_MSM_RTB=y +CONFIG_MSM_RTB_SEPARATE_CPUS=y +CONFIG_MSM_CACHE_ERP=y +CONFIG_MSM_L1_ERR_PANIC=y +CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y +CONFIG_MSM_L2_ERP_1BIT_PANIC=y +CONFIG_MSM_L2_ERP_2BIT_PANIC=y +CONFIG_MSM_DCVS=y +CONFIG_MSM_CACHE_DUMP=y +CONFIG_MSM_CACHE_DUMP_ON_PANIC=y +CONFIG_MSM_HSIC_SYSMON=y +CONFIG_STRICT_MEMORY_RWX=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +# CONFIG_SMP_ON_UP is not set +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_CC_STACKPROTECTOR=y +CONFIG_CP_ACCESS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_WAKELOCK=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCISMD=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_WEXT is not set +CONFIG_RFKILL=y +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_HAPTIC_ISA1200=y +CONFIG_PMIC8XXX_VIBRATOR=y +CONFIG_QSEECOM=y +CONFIG_USB_HSIC_SMSC_HUB=y +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_KS8851=m +# CONFIG_MSM_RMNET is not set +CONFIG_MSM_RMNET_BAM=y +CONFIG_MSM_RMNET_SMUX=y +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_USB_USBNET=y +CONFIG_MSM_RMNET_USB=y +CONFIG_WCNSS_CORE=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_MATRIX=y +CONFIG_KEYBOARD_PMIC8XXX=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PMIC8XXX_PWRKEY=y +CONFIG_INPUT_UINPUT=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_N_SMUX=y +CONFIG_N_SMUX_LOOPBACK=y +CONFIG_SMUX_CTL=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_SLIMBUS_MSM_CTRL=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SX150X=y +CONFIG_POWER_SUPPLY=y +# CONFIG_BATTERY_MSM is not set +CONFIG_ISL9519_CHARGER=y +CONFIG_SMB349_CHARGER=y +CONFIG_PM8921_CHARGER=y +CONFIG_PM8921_BMS=y +CONFIG_SENSORS_PM8XXX_ADC=y +CONFIG_SENSORS_EPM_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS8960=y +CONFIG_THERMAL_PM8XXX=y +CONFIG_THERMAL_MONITOR=y +CONFIG_MFD_PM8921_CORE=y +CONFIG_MFD_PM8821_CORE=y +CONFIG_MFD_PM8038_CORE=y +CONFIG_MFD_PM8XXX_SPK=y +CONFIG_MFD_PM8XXX_BATT_ALARM=y +CONFIG_WCD9304_CODEC=y +CONFIG_WCD9310_CODEC=y +CONFIG_REGULATOR_PM8XXX=y +CONFIG_REGULATOR_MSM_GPIO=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_USER_RC_INPUT=y +CONFIG_IR_GPIO_CIR=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA_V4L2=y +CONFIG_IMX074=y +CONFIG_MT9M114=y +CONFIG_IMX074_ACT=y +CONFIG_MSM_CAMERA_FLASH_SC628A=y +CONFIG_OV2720=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_ACTUATOR=y +CONFIG_MSM_EEPROM=y +CONFIG_IMX074_EEPROM=y +CONFIG_IMX091_EEPROM=y +CONFIG_MSM_GEMINI=y +CONFIG_S5K3L1YX=y +CONFIG_IMX091=y +CONFIG_RADIO_IRIS=y +CONFIG_RADIO_IRIS_TRANSPORT=m +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=24 +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_TRIPLE_BUFFER=y +CONFIG_FB_MSM_MDP40=y +CONFIG_FB_MSM_OVERLAY=y +CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y +CONFIG_FB_MSM_OVERLAY1_WRITEBACK=y +CONFIG_FB_MSM_WRITEBACK_MSM_PANEL=y +CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT=y +CONFIG_FB_MSM_HDMI_MSM_PANEL=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8960=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_MSM=y +CONFIG_USB_EHCI_MSM_HSIC=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_QUALCOMM=y +CONFIG_USB_SERIAL_CSVT=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_QCOM_DIAG_BRIDGE=y +CONFIG_USB_QCOM_MDM_BRIDGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_G_ANDROID=y +CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT=y +# CONFIG_MMC_MSM_SDC2_SUPPORT is not set +CONFIG_MMC_MSM_SDC3_SUPPORT=y +CONFIG_MMC_MSM_SDC3_WP_SUPPORT=y +CONFIG_MMC_MSM_SPS_SUPPORT=y +CONFIG_LEDS_PM8XXX=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_MSM_SSBI=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_BAMDMA=y +CONFIG_MSM_IOMMU=y +CONFIG_MOBICORE_SUPPORT=m +CONFIG_MOBICORE_API=m +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_CIFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_LOCKUP_DETECTOR=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_LIST=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAILSLAB=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_PID_IN_CONTEXTIDR=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig new file mode 100644 index 00000000000..37bc41615aa --- /dev/null +++ b/arch/arm/configs/msm9615_defconfig @@ -0,0 +1,297 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +# CONFIG_FAIR_GROUP_SCHED is not set +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_PERF_EVENTS is not set +CONFIG_PROFILING=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM9615=y +CONFIG_MACH_MSM9615_CDP=y +CONFIG_MACH_MSM9615_MTP=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_BAM_DMUX=y +# CONFIG_MSM_RESET_MODEM is not set +CONFIG_MSM_IPC_ROUTER=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +# CONFIG_MSM_SYSMON_COMM is not set +CONFIG_MSM_MODEM_8960=y +CONFIG_MSM_LPASS_8960=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_MSM_DIRECT_SCLK_ACCESS=y +CONFIG_MSM_BUS_SCALING=y +CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y +CONFIG_MSM_WATCHDOG=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_SWP_EMULATE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_IPV6=y +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_IPV6_SIT is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NETFILTER=y +CONFIG_NETFILTER_DEBUG=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_IP_SET=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_TTL=y +CONFIG_IP_NF_RAW=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_MTD=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +# CONFIG_ANDROID_PMEM is not set +CONFIG_SCSI=y +CONFIG_SCSI_TGT=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_NETDEVICES=y +# CONFIG_MSM_RMNET is not set +CONFIG_MSM_RMNET_BAM=y +CONFIG_ATH6K_LEGACY_EXT=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PMIC8XXX_PWRKEY=y +CONFIG_INPUT_UINPUT=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MSM is not set +CONFIG_I2C_QUP=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_SLIMBUS_MSM_CTRL=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +# CONFIG_BATTERY_MSM is not set +CONFIG_SENSORS_PM8XXX_ADC=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS8960=y +CONFIG_THERMAL_PM8XXX=y +CONFIG_MFD_PM8018_CORE=y +CONFIG_WCD9310_CODEC=y +CONFIG_REGULATOR_PM8XXX=y +CONFIG_REGULATOR_MSM_GPIO=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MDM9615=y +# CONFIG_HID_SUPPORT is not set +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EHSET=y +CONFIG_USB_EHCI_MSM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_ONETOUCH=y +CONFIG_USB_STORAGE_KARMA=y +CONFIG_USB_STORAGE_CYPRESS_ATACB=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_GADGET=y +CONFIG_USB_CI13XXX_MSM=m +CONFIG_USB_CI13XXX_MSM_HSIC=m +CONFIG_USB_G_ANDROID=y +CONFIG_RMNET_SMD_CTL_CHANNEL="DATA36_CNTL" +CONFIG_RMNET_SMD_DATA_CHANNEL="DATA36" +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_TEST=m +CONFIG_MMC_MSM=y +CONFIG_MMC_MSM_CARD_HW_DETECTION=y +CONFIG_MMC_MSM_SPS_SUPPORT=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_PM8XXX=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_INTF_ALARM is not set +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_LOGGER=y +CONFIG_MSM_SSBI=y +CONFIG_SPS=y +CONFIG_USB_BAM=y +CONFIG_SPS_SUPPORT_BAMDMA=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_KEYS=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_QCRYPTO=m +CONFIG_CRYPTO_DEV_QCE=m +CONFIG_CRYPTO_DEV_QCEDEV=m +CONFIG_CRC_CCITT=y +CONFIG_LIBCRC32C=y diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig new file mode 100644 index 00000000000..89fb8888f16 --- /dev/null +++ b/arch/arm/configs/msm9625_defconfig @@ -0,0 +1,107 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_SPARSE_IRQ=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +# CONFIG_FAIR_GROUP_SCHED is not set +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM9625=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_DIRECT_SCLK_ACCESS=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x19000000 +CONFIG_USE_OF=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_SUSPEND is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_MISC_DEVICES=y +# CONFIG_ANDROID_PMEM is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=m +CONFIG_SERIO_LIBPS2=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +# CONFIG_HWMON is not set +# CONFIG_MFD_SUPPORT is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_USER=y +CONFIG_KEYS=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_LIBCRC32C=y diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h index f77ffc1eb0c..87de9155726 100644 --- a/arch/arm/include/asm/localtimer.h +++ b/arch/arm/include/asm/localtimer.h @@ -14,6 +14,11 @@ struct clock_event_device; +/* + * Setup a per-cpu timer, whether it be a local timer or dummy broadcast + */ +void percpu_timer_setup(void); + struct local_timer_ops { int (*setup)(struct clock_event_device *); void (*stop)(struct clock_event_device *); diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index bca864ac945..0bdb0f14bad 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -7,6 +7,13 @@ #include #include #include +#include +#include + +#define SDC_DAT1_DISABLE 0 +#define SDC_DAT1_ENABLE 1 +#define SDC_DAT1_ENWAKE 2 +#define SDC_DAT1_DISWAKE 3 struct embedded_sdio_data { struct sdio_cis cis; @@ -15,6 +22,102 @@ struct embedded_sdio_data { int num_funcs; }; +/* This structure keeps information per regulator */ +struct msm_mmc_reg_data { + /* voltage regulator handle */ + struct regulator *reg; + /* regulator name */ + const char *name; + /* voltage level to be set */ + unsigned int low_vol_level; + unsigned int high_vol_level; + /* Load values for low power and high power mode */ + unsigned int lpm_uA; + unsigned int hpm_uA; + /* + * is set voltage supported for this regulator? + * false => set voltage is not supported + * true => set voltage is supported + * + * Some regulators (like gpio-regulators, LVS (low voltage swtiches) + * PMIC regulators) dont have the capability to call + * regulator_set_voltage or regulator_set_optimum_mode + * Use this variable to indicate if its a such regulator or not + */ + bool set_voltage_sup; + /* is this regulator enabled? */ + bool is_enabled; + /* is this regulator needs to be always on? */ + bool always_on; + /* is low power mode setting required for this regulator? */ + bool lpm_sup; +}; + +/* + * This structure keeps information for all the + * regulators required for a SDCC slot. + */ +struct msm_mmc_slot_reg_data { + struct msm_mmc_reg_data *vdd_data; /* keeps VDD/VCC regulator info */ + struct msm_mmc_reg_data *vdd_io_data; /* keeps VDD IO regulator info */ +}; + +struct msm_mmc_gpio { + u32 no; + const char *name; + bool is_always_on; + bool is_enabled; +}; + +struct msm_mmc_gpio_data { + struct msm_mmc_gpio *gpio; + u8 size; +}; + +struct msm_mmc_pad_pull { + enum msm_tlmm_pull_tgt no; + u32 val; +}; + +struct msm_mmc_pad_pull_data { + struct msm_mmc_pad_pull *on; + struct msm_mmc_pad_pull *off; + u8 size; +}; + +struct msm_mmc_pad_drv { + enum msm_tlmm_hdrive_tgt no; + u32 val; +}; + +struct msm_mmc_pad_drv_data { + struct msm_mmc_pad_drv *on; + struct msm_mmc_pad_drv *off; + u8 size; +}; + +struct msm_mmc_pad_data { + struct msm_mmc_pad_pull_data *pull; + struct msm_mmc_pad_drv_data *drv; +}; + +struct msm_mmc_pin_data { + /* + * = 1 if controller pins are using gpios + * = 0 if controller has dedicated MSM pads + */ + u8 is_gpio; + u8 cfg_sts; + struct msm_mmc_gpio_data *gpio_data; + struct msm_mmc_pad_data *pad_data; +}; + +struct msm_mmc_bus_voting_data { + struct msm_bus_scale_pdata *use_cases; + unsigned int *bw_vecs; + unsigned int bw_vecs_size; +}; + struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ int built_in; /* built-in device flag */ @@ -23,6 +126,40 @@ struct mmc_platform_data { unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + /* + * XPC controls the maximum current in the + * default speed mode of SDXC card. + */ + unsigned int xpc_cap; + /* Supported UHS-I Modes */ + unsigned int uhs_caps; + void (*sdio_lpm_gpio_setup)(struct device *, unsigned int); + unsigned int status_irq; + unsigned int status_gpio; + /* Indicates the polarity of the GPIO line when card is inserted */ + bool is_status_gpio_active_low; + unsigned int sdiowakeup_irq; + unsigned long irq_flags; + unsigned long mmc_bus_width; + int (*wpswitch) (struct device *); + unsigned int msmsdcc_fmin; + unsigned int msmsdcc_fmid; + unsigned int msmsdcc_fmax; + bool nonremovable; + bool pclk_src_dfab; + unsigned int mpm_sdiowakeup_int; + unsigned int wpswitch_gpio; + unsigned char wpswitch_polarity; + struct msm_mmc_slot_reg_data *vreg_data; + int is_sdio_al_client; + unsigned int *sup_clk_table; + unsigned char sup_clk_cnt; + struct msm_mmc_pin_data *pin_data; + bool disable_bam; + bool disable_runtime_pm; + bool disable_cmd23; + u32 cpu_dma_latency; + struct msm_mmc_bus_voting_data *msm_bus_voting_data; }; #endif diff --git a/arch/arm/include/asm/vfp.h b/arch/arm/include/asm/vfp.h index 76d3f6907cc..5a1e789352d 100644 --- a/arch/arm/include/asm/vfp.h +++ b/arch/arm/include/asm/vfp.h @@ -82,3 +82,8 @@ #define VFPOPDESC_UNUSED_BIT (24) #define VFPOPDESC_UNUSED_MASK (0xFF << VFPOPDESC_UNUSED_BIT) #define VFPOPDESC_OPDESC_MASK (~(VFPOPDESC_LENGTH_MASK | VFPOPDESC_UNUSED_MASK)) + +#ifndef __ASSEMBLY__ +int vfp_pm_suspend(void); +void vfp_pm_resume(void); +#endif diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 6deb73d639d..08b65dbb15e 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -1,147 +1,714 @@ if ARCH_MSM -choice - prompt "Qualcomm MSM SoC Type" - default ARCH_MSM7X00A +menu "MSM SoC Type" -config ARCH_MSM7X00A +config ARCH_MSM7X01A bool "MSM7x00A / MSM7x01A" - select MACH_TROUT if !MACH_HALIBUT select ARCH_MSM_ARM11 - select MSM_SMD - select MSM_SMD_PKG3 + select MSM_VIC select CPU_V6 select GPIO_MSM_V1 - select MSM_PROC_COMM + select MSM_REMOTE_SPINLOCK_SWP + +config ARCH_MSM7X25 + bool "MSM7x25" + select ARCH_MSM_ARM11 + select MSM_VIC + select CPU_V6 + select GPIO_MSM_V1 + select MSM_REMOTE_SPINLOCK_SWP + select MULTI_IRQ_HANDLER + +config ARCH_MSM7X27 + bool "MSM7x27" + select ARCH_MSM_ARM11 if MSM_SOC_REV_NONE + select ARCH_HAS_BARRIERS if MSM_SOC_REV_NONE + select ARCH_MSM_CORTEX_A5 if MSM_SOC_REV_A + select MSM_VIC + select CPU_V6 if MSM_SOC_REV_NONE + select CPU_V7 if MSM_SOC_REV_A + select GPIO_MSM_V1 + select MSM_REMOTE_SPINLOCK_SWP if MSM_SOC_REV_NONE + select MSM_GPIOMUX + select REGULATOR + select MULTI_IRQ_HANDLER + select MSM_PROC_COMM_REGULATOR + select CLEANCACHE + select QCACHE + select MSM_PM2 if PM + select MSM_RUN_QUEUE_STATS if MSM_SOC_REV_A + select DONT_MAP_HOLE_AFTER_MEMBANK0 config ARCH_MSM7X30 bool "MSM7x30" - select MACH_MSM7X30_SURF # if ! select ARCH_MSM_SCORPION - select MSM_SMD select MSM_VIC select CPU_V7 - select MSM_GPIOMUX select GPIO_MSM_V1 - select MSM_PROC_COMM + select MSM_REMOTE_SPINLOCK_DEKKERS select ARCH_SPARSEMEM_ENABLE + select ARCH_HAS_HOLES_MEMORYMODEL select MEMORY_HOTPLUG select MEMORY_HOTREMOVE + select ARCH_ENABLE_MEMORY_HOTPLUG + select ARCH_ENABLE_MEMORY_HOTREMOVE select MIGRATION select ARCH_MEMORY_PROBE select ARCH_MEMORY_REMOVE + select MSM_GPIOMUX + select RESERVE_FIRST_PAGE + select MSM_DALRPC + select MSM_SPM_V1 + select REGULATOR + select MSM_PROC_COMM_REGULATOR + select MULTI_IRQ_HANDLER + select MSM_PM2 if PM config ARCH_QSD8X50 bool "QSD8X50" - select MACH_QSD8X50_SURF if !MACH_QSD8X50A_ST1_5 select ARCH_MSM_SCORPION - select MSM_SMD select MSM_VIC select CPU_V7 - select MSM_GPIOMUX select GPIO_MSM_V1 - select MSM_PROC_COMM + select MSM_REMOTE_SPINLOCK_LDREX + select CPU_USE_DOMAINS + select EMULATE_DOMAIN_MANAGER_V7 + select MSM_GPIOMUX + select MSM_DALRPC + select MSM_PM2 if PM config ARCH_MSM8X60 bool "MSM8X60" - select MACH_MSM8X60_SURF if (!MACH_MSM8X60_RUMI3 && !MACH_MSM8X60_SIM \ - && !MACH_MSM8X60_FFA) select ARCH_MSM_SCORPIONMP + select SMP_PARALLEL_START if SMP select ARM_GIC select CPU_V7 - select MSM_V2_TLMM + select MSM_REMOTE_SPINLOCK_LDREX + select ARCH_REQUIRE_GPIOLIB + select MSM_ADM3 + select REGULATOR + select MSM_RPM_REGULATOR select GPIO_MSM_V2 + select MSM_PIL + select ARCH_HAS_CPU_IDLE_WAIT + select MSM_DIRECT_SCLK_ACCESS + select MSM_RPM + select MSM_XO select MSM_GPIOMUX + select MSM_BUS_SCALING + select MSM_SECURE_IO + select MSM_DALRPC + select MSM_QDSP6_APR + select MSM_QDSP6_CODECS + select MSM_NATIVE_RESTART + select ARCH_INLINE_SPIN_TRYLOCK + select ARCH_INLINE_SPIN_TRYLOCK_BH + select ARCH_INLINE_SPIN_LOCK + select ARCH_INLINE_SPIN_LOCK_BH + select ARCH_INLINE_SPIN_LOCK_IRQ + select ARCH_INLINE_SPIN_LOCK_IRQSAVE + select ARCH_INLINE_SPIN_UNLOCK + select ARCH_INLINE_SPIN_UNLOCK_BH + select ARCH_INLINE_SPIN_UNLOCK_IRQ + select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE + select ARCH_INLINE_READ_TRYLOCK + select ARCH_INLINE_READ_LOCK + select ARCH_INLINE_READ_LOCK_BH + select ARCH_INLINE_READ_LOCK_IRQ + select ARCH_INLINE_READ_LOCK_IRQSAVE + select ARCH_INLINE_READ_UNLOCK + select ARCH_INLINE_READ_UNLOCK_BH + select ARCH_INLINE_READ_UNLOCK_IRQ + select ARCH_INLINE_READ_UNLOCK_IRQRESTORE + select ARCH_INLINE_WRITE_TRYLOCK + select ARCH_INLINE_WRITE_LOCK + select ARCH_INLINE_WRITE_LOCK_BH + select ARCH_INLINE_WRITE_LOCK_IRQ + select ARCH_INLINE_WRITE_LOCK_IRQSAVE + select ARCH_INLINE_WRITE_UNLOCK + select ARCH_INLINE_WRITE_UNLOCK_BH + select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE + select CPU_HAS_L2_PMU + select MSM_SPM_V1 select MSM_SCM if SMP + select MULTI_IRQ_HANDLER + select MSM_MULTIMEDIA_USE_ION + select MSM_PM8X60 if PM + select MSM_RUN_QUEUE_STATS config ARCH_MSM8960 bool "MSM8960" - select ARCH_MSM_SCORPIONMP - select MACH_MSM8960_SIM if (!MACH_MSM8960_RUMI3) + select ARCH_MSM_KRAITMP select ARM_GIC select CPU_V7 - select MSM_V2_TLMM + select GPIO_MSM_V2 select MSM_GPIOMUX select MSM_SCM if SMP + select MSM_DIRECT_SCLK_ACCESS + select REGULATOR + select MSM_RPM_REGULATOR + select MSM_RPM + select MSM_XO + select MSM_QDSP6_APR + select MSM_QDSP6_CODECS + select MSM_PIL + select MSM_AUDIO_QDSP6 if SND_SOC + select CPU_HAS_L2_PMU + select MSM_SPM_V2 + select MSM_L2_SPM + select MSM_NATIVE_RESTART + select DONT_MAP_HOLE_AFTER_MEMBANK0 + select MSM_REMOTE_SPINLOCK_SFPB + select ARCH_SPARSEMEM_ENABLE + select ARCH_HAS_HOLES_MEMORYMODEL + select CLEANCACHE + select QCACHE + select MSM_MULTIMEDIA_USE_ION + select MULTI_IRQ_HANDLER + select MSM_PM8X60 if PM + select HOLES_IN_ZONE if SPARSEMEM + select MSM_RUN_QUEUE_STATS +config ARCH_MSM8930 + bool "MSM8930" + select ARCH_MSM_KRAITMP + select ARM_GIC + select CPU_V7 + select GPIO_MSM_V2 + select MSM_GPIOMUX + select MSM_SCM if SMP + select MSM_DIRECT_SCLK_ACCESS + select REGULATOR + select MSM_RPM_REGULATOR + select MSM_RPM + select MSM_XO + select MSM_QDSP6_APR + select MSM_QDSP6_CODECS + select MSM_PIL + select MSM_AUDIO_QDSP6 if SND_SOC + select CPU_HAS_L2_PMU + select MSM_SPM_V2 + select MSM_L2_SPM + select MSM_NATIVE_RESTART + select DONT_MAP_HOLE_AFTER_MEMBANK0 + select MSM_REMOTE_SPINLOCK_SFPB + select ARCH_SPARSEMEM_ENABLE + select ARCH_HAS_HOLES_MEMORYMODEL + select MSM_ULTRASOUND + select MULTI_IRQ_HANDLER + select MSM_PM8X60 if PM + select HOLES_IN_ZONE if SPARSEMEM + +config ARCH_APQ8064 + bool "APQ8064" + select ARCH_MSM_KRAITMP + select GPIO_MSM_V2 + select ARM_GIC + select CPU_V7 + select MSM_SCM if SMP + select MSM_GPIOMUX + select MSM_REMOTE_SPINLOCK_SFPB + select MSM_PIL + select MSM_QDSP6_APR + select MSM_QDSP6_CODECS + select MSM_AUDIO_QDSP6 if SND_SOC + select MULTI_IRQ_HANDLER + select MSM_RPM + select MSM_SPM_V2 + select MSM_L2_SPM + select MSM_PM8X60 if PM + select CPU_HAS_L2_PMU + select HOLES_IN_ZONE if SPARSEMEM + select CLEANCACHE + select QCACHE + select MIGHT_HAVE_PCI + select ARCH_SUPPORTS_MSI + +config ARCH_MSMCOPPER + bool "MSM Copper" + select ARCH_MSM_KRAITMP + select GPIO_MSM_V3 + select ARM_GIC + select CPU_V7 + select MSM_SCM if SMP + select MSM_GPIOMUX + select MULTI_IRQ_HANDLER + select MSM_MULTIMEDIA_USE_ION + select MSM_PIL + select MSM_SPM_V2 + select MSM_L2_SPM + select MSM_PM8X60 if PM + select MAY_HAVE_SPARSE_IRQ + select SPARSE_IRQ + select MSM_RPM_SMD + select REGULATOR + +config ARCH_FSM9XXX + bool "FSM9XXX" + select ARCH_MSM_SCORPION + select MSM_VIC + select CPU_V7 + select MSM_REMOTE_SPINLOCK_LDREX + select GPIO_FSM9XXX + select MULTI_IRQ_HANDLER + select MSM_DALRPC + +config ARCH_MSM9615 + bool "MSM9615" + select ARM_GIC + select GIC_SECURE + select ARCH_MSM_CORTEX_A5 + select CPU_V7 + select GPIO_MSM_V2 + select MSM_GPIOMUX + select MSM_RPM + select MSM_SPM_V2 + select MSM_NATIVE_RESTART + select REGULATOR + select MSM_RPM_REGULATOR + select MULTI_IRQ_HANDLER + select MSM_PM8X60 if PM + select MSM_XO + select MSM_MULTIMEDIA_USE_ION + select MSM_QDSP6_APR + select MSM_AUDIO_QDSP6 if SND_SOC + select FIQ + +config ARCH_MSM8625 + bool "MSM8625" + select ARCH_MSM_CORTEX_A5 + select CPU_V7 + select GPIO_MSM_V1 + select MSM_GPIOMUX + select ARM_GIC + select ARCH_MSM_CORTEXMP + select MULTI_IRQ_HANDLER + select ARM_TICKET_LOCKS + select MSM_RUN_QUEUE_STATS + +config ARCH_MSM9625 + bool "MSM9625" + select ARM_GIC + select GIC_SECURE + select ARCH_MSM_CORTEX_A5 + select SMP + select MSM_SMP + select CPU_V7 + select MSM_GPIOMUX + select MULTI_IRQ_HANDLER + select GPIO_MSM_V2 + +endmenu + +choice + prompt "MSM SoC Revision" + default MSM_SOC_REV_NONE +config MSM_SOC_REV_NONE + bool "N/A" + select EMULATE_DOMAIN_MANAGER_V7 if ARCH_QSD8X50 + select VERIFY_PERMISSION_FAULT if ARCH_QSD8X50 +config MSM_SOC_REV_A + bool "Rev. A" + select ARCH_MSM7X27A if ARCH_MSM7X27 endchoice -config MSM_HAS_DEBUG_UART_HS - bool +config MSM_KRAIT_TBB_ABORT_HANDLER + bool "Krait TBB/TBH data abort handler" + depends on ARCH_MSM_KRAIT + depends on ARM_THUMB + help + Certain early samples of the Krait processor may generate data + aborts for TBB / TBH instructions that fail their condition code + checks. Enabling this option will ignore these erroneous data aborts, + at the expense of a very small performance penalty. -config MSM_SOC_REV_A - bool -config ARCH_MSM_SCORPIONMP - bool - select HAVE_SMP + If unsure, say N. config ARCH_MSM_ARM11 bool + config ARCH_MSM_SCORPION bool +config ARCH_MSM_KRAIT + bool + select ARM_L1_CACHE_SHIFT_6 + +config MSM_SMP + select HAVE_SMP + bool + +config ARCH_MSM_SCORPIONMP + select ARCH_MSM_SCORPION + select MSM_SMP + select HAVE_ARCH_HAS_CURRENT_TIMER + bool + +config ARCH_MSM_KRAITMP + select ARCH_MSM_KRAIT + select MSM_SMP + select HAVE_ARCH_HAS_CURRENT_TIMER + bool + select HAVE_HW_BRKPT_RESERVED_RW_ACCESS + +config ARCH_MSM_CORTEXMP + select MSM_SMP + bool + +config ARCH_MSM_CORTEX_A5 + bool + select HAVE_HW_BRKPT_RESERVED_RW_ACCESS + +config ARCH_MSM7X27A + bool + select MSM_DALRPC + select MSM_PROC_COMM_REGULATOR + select MULTI_IRQ_HANDLER + select ARM_GIC + select ARCH_MSM_CORTEXMP + config MSM_VIC bool -menu "Qualcomm MSM Board Type" +config MSM_RPM + bool "Resource Power Manager" + select MSM_MPM + +config MSM_RPM_SMD + depends on MSM_SMD + bool "Support for using SMD as the transport layer for communicatons with RPM" + +config MSM_MPM + bool "Modem Power Manager" + +config MSM_XO + bool + +config MSM_REMOTE_SPINLOCK_DEKKERS + bool +config MSM_REMOTE_SPINLOCK_SWP + bool +config MSM_REMOTE_SPINLOCK_LDREX + bool +config MSM_REMOTE_SPINLOCK_SFPB + bool +config MSM_ADM3 + bool + +menu "MSM Board Selection" config MACH_HALIBUT - depends on ARCH_MSM - depends on ARCH_MSM7X00A + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y bool "Halibut Board (QCT SURF7201A)" help Support for the Qualcomm SURF7201A eval board. -config MACH_TROUT - depends on ARCH_MSM - depends on ARCH_MSM7X00A - bool "HTC Dream (aka trout)" +config MACH_MSM7201A_SURF + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "MSM7201A SURF" help - Support for the HTC Dream, T-Mobile G1, Android ADP1 devices. + Support for the Qualcomm MSM7201A SURF eval board. + +config MACH_MSM7201A_FFA + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "MSM7201A FFA" + help + Support for the Qualcomm MSM7201A FFA eval board. + +config MACH_TROUT + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "Trout" + +config MACH_MSM7X27_SURF + depends on ARCH_MSM7X27 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27 SURF" + help + Support for the Qualcomm MSM7x27 SURF eval board. + +config MACH_MSM7X27_FFA + depends on ARCH_MSM7X27 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27 FFA" + help + Support for the Qualcomm MSM7x27 FFA eval board. + +config MACH_MSM7X27A_RUMI3 + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27A RUMI3" + help + Support for the Qualcomm MSM7x27A RUMI3 Emulation Platform. + +config MACH_MSM7X27A_SURF + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27A SURF" + help + Support for the Qualcomm MSM7x27A SURF. + +config MACH_MSM7X27A_FFA + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27A FFA" + help + Support for the Qualcomm MSM7x27A FFA. + +config MACH_MSM7625A_SURF + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7625A SURF" + help + Support for the Qualcomm MSM7625A SURF. + +config MACH_MSM7625A_FFA + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7625A FFA" + help + Support for the Qualcomm MSM7625A FFA. + +config MACH_MSM7627A_QRD1 + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7627A QRD1" + help + Support for the Qualcomm MSM7627A Reference Design. + +config MACH_MSM7627A_QRD3 + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7627A QRD3" + help + Support for the Qualcomm MSM7627A Reference Design. + +config MACH_MSM7627A_EVB + depends on ARCH_MSM7X27A + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7627A EVB" + help + Support for the Qualcomm MSM7627A Reference Design. + +config MACH_MSM8625_RUMI3 + depends on ARCH_MSM8625 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8625 RUMI3" + help + Support for the Qualcomm MSM8625 RUMI3 Emulation Platform. + +config MACH_MSM8625_SURF + depends on ARCH_MSM8625 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8625 SURF" + help + Support for the Qualcomm MSM8625 SURF. + +config MACH_MSM8625_FFA + depends on ARCH_MSM8625 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8625 FFA" + help + Support for the Qualcomm MSM8625 FFA. + +config MACH_MSM8625_EVB + depends on ARCH_MSM8625 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8625 EVB" + help + Support for the Qualcomm MSM8625 Reference Design. + +config MACH_MSM8625_QRD7 + depends on ARCH_MSM8625 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8625 QRD7" + help + Support for the Qualcomm MSM8625 Reference Design. + +config MACH_MSM8625_EVT + depends on ARCH_MSM8625 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8625 EVT" + help + Support for the Qualcomm MSM8625 Reference Design. config MACH_MSM7X30_SURF - depends on ARCH_MSM7X30 - bool "MSM7x30 SURF" - help - Support for the Qualcomm MSM7x30 SURF eval board. + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x30 SURF" + help + Support for the Qualcomm MSM7x30 SURF eval board. + +config MACH_MSM7X30_FFA + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x30 FFA" + help + Support for the Qualcomm MSM7x30 FFA eval board. + +config MACH_MSM7X30_FLUID + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x30 FLUID" + help + Support for the Qualcomm MSM7x30 FLUID eval board. + +config MACH_SAPPHIRE + depends on ARCH_MSM7X01A + default n + bool "Sapphire" config MACH_QSD8X50_SURF depends on ARCH_QSD8X50 + depends on MSM_SOC_REV_NONE + depends on MSM_STACKED_MEMORY + default y bool "QSD8x50 SURF" help Support for the Qualcomm QSD8x50 SURF eval board. -config MACH_QSD8X50A_ST1_5 +config MACH_QSD8X50_FFA depends on ARCH_QSD8X50 - select MSM_SOC_REV_A - bool "QSD8x50A ST1.5" + depends on MSM_SOC_REV_NONE + depends on MSM_STACKED_MEMORY + default y + bool "QSD8x50 FFA" help - Support for the Qualcomm ST1.5. + Support for the Qualcomm QSD8x50 FFA eval board. + +config MACH_MSM7X25_SURF + depends on ARCH_MSM7X25 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x25 SURF" + help + Support for the Qualcomm MSM7x25 SURF eval board. + +config MACH_MSM7X25_FFA + depends on ARCH_MSM7X25 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x25 FFA" + help + Support for the Qualcomm MSM7x25 FFA eval board. + +config MACH_MSM8X55_SURF + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8X55 SURF" + help + Support for the Qualcomm MSM8x55 SURF eval board. + +config MACH_MSM8X55_FFA + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8X55 FFA" + help + Support for the Qualcomm MSM8x55 FFA eval board. + +config MACH_MSM8X55_SVLTE_FFA + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8X55 SVLTE FFA" + help + Support for the Qualcomm MSM8x55 SVLTE FFA eval board. + +config MACH_MSM8X55_SVLTE_SURF + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM8X55 SVLTE SURF" + help + Support for the Qualcomm MSM8x55 SVLTE SURF eval board. config MACH_MSM8X60_RUMI3 depends on ARCH_MSM8X60 + default n bool "MSM8x60 RUMI3" help Support for the Qualcomm MSM8x60 RUMI3 emulator. -config MACH_MSM8X60_SURF - depends on ARCH_MSM8X60 - bool "MSM8x60 SURF" - help - Support for the Qualcomm MSM8x60 SURF eval board. - config MACH_MSM8X60_SIM depends on ARCH_MSM8X60 + default n bool "MSM8x60 Simulator" help Support for the Qualcomm MSM8x60 simulator. +config MACH_MSM8X60_SURF + depends on ARCH_MSM8X60 + default n + bool "MSM8x60 SURF" + help + Support for the Qualcomm MSM8x60 SURF eval board. + config MACH_MSM8X60_FFA depends on ARCH_MSM8X60 + default n bool "MSM8x60 FFA" help Support for the Qualcomm MSM8x60 FFA eval board. +config MACH_MSM8X60_FLUID + depends on ARCH_MSM8X60 + default n + bool "MSM8x60 FLUID" + help + Support for the Qualcomm MSM8x60 FLUID platform. The FLUID is an + 8x60 target which has a form factor that is much closer to that + of a phone than other targets. It also has a new display and + touchscreen controller. + +config MACH_MSM8X60_FUSION + depends on ARCH_MSM8X60 + default n + bool "MSM8x60 FUSION" + help + Support for the Qualcomm MSM8x60 Fusion SURF device. + +config MACH_MSM8X60_FUSN_FFA + depends on ARCH_MSM8X60 + default n + bool "MSM8x60 FUSN FFA" + help + Support for the Qualcomm MSM8x60 Fusion FFA device. + +config MACH_MSM8X60_DRAGON + depends on ARCH_MSM8X60 + default n + bool "MSM8x60 DRAGON" + help + Support for the Qualcomm MSM8x60 Dragon board. + config MACH_MSM8960_SIM depends on ARCH_MSM8960 bool "MSM8960 Simulator" @@ -154,23 +721,1664 @@ config MACH_MSM8960_RUMI3 help Support for the Qualcomm MSM8960 RUMI3 emulator. +config MACH_MSM8960_CDP + depends on ARCH_MSM8960 + bool "MSM8960 CDP" + help + Support for the Qualcomm MSM8960 CDP device. + +config MACH_MSM8960_MTP + depends on ARCH_MSM8960 + bool "MSM8960 MTP" + help + Support for the Qualcomm MSM8960 MTP device. + +config MACH_MSM8960_FLUID + depends on ARCH_MSM8960 + bool "MSM8960 FLUID" + help + Support for the Qualcomm MSM8960 FLUID device. + +config MACH_MSM8960_LIQUID + depends on ARCH_MSM8960 + bool "MSM8960 LIQUID" + help + Support for the Qualcomm MSM8960 LIQUID device. + +config MACH_MSM8930_CDP + depends on ARCH_MSM8930 + bool "MSM8930 CDP" + help + Support for the Qualcomm MSM8930 CDP device. + +config MACH_MSM8930_MTP + depends on ARCH_MSM8930 + bool "MSM8930 MTP" + help + Support for the Qualcomm MSM8930 MTP device. + +config MACH_MSM8930_FLUID + depends on ARCH_MSM8930 + bool "MSM8930 FLUID" + help + Support for the Qualcomm MSM8930 FLUID device. + +config MACH_MSM8627_CDP + depends on ARCH_MSM8930 + bool "MSM8627 CDP" + help + Support for the Qualcomm MSM8627 CDP device. + +config MACH_MSM8627_MTP + depends on ARCH_MSM8930 + bool "MSM8627 MTP" + help + Support for the Qualcomm MSM8627 MTP device. + +config MACH_MSM9615_CDP + depends on ARCH_MSM9615 + bool "MSM9615 CDP" + help + Support for the Qualcomm MSM9615 CDP device. + +config MACH_MSM9615_MTP + depends on ARCH_MSM9615 + bool "MSM9615 MTP" + help + Support for the Qualcomm MSM9615 MTP device. + +config MSM_USE_TSIF1 + depends on ARCH_MSM8X60 + bool "MSM8x60 use TSIF1" + help + Selects TSIF1 core to be used rather than TSIF0. + The two TSIF cores share the same DM configuration + so they cannot be used simultaneously. + +config MACH_APQ8064_SIM + depends on ARCH_APQ8064 + bool "APQ8064 Simulator" + help + Support for the Qualcomm APQ8064 simulator. + +config MACH_APQ8064_RUMI3 + depends on ARCH_APQ8064 + bool "APQ8064 RUMI3" + help + Support for the Qualcomm APQ8064 RUMI3 emulator. + +config MACH_APQ8064_CDP + depends on ARCH_APQ8064 + bool "APQ8064 CDP" + help + Support for the Qualcomm APQ8064 CDP device. + +config MACH_APQ8064_MTP + depends on ARCH_APQ8064 + bool "APQ8064 MTP" + help + Support for the Qualcomm APQ8064 MTP device. + +config MACH_APQ8064_LIQUID + depends on ARCH_APQ8064 + bool "APQ8064 LIQUID" + help + Support for the Qualcomm APQ8064 LIQUID device. + +config MACH_MPQ8064_CDP + depends on ARCH_APQ8064 + bool "MPQ8064 CDP" + help + Support for the Qualcomm MPQ8064 CDP device. + +config MACH_MPQ8064_HRD + depends on ARCH_APQ8064 + bool "MPQ8064 HRD" + help + Support for the Qualcomm MPQ8064 HRD device. + +config MACH_MPQ8064_DTV + depends on ARCH_APQ8064 + bool "MPQ8064 DTV" + help + Support for the Qualcomm MPQ8064 DTV device. + +config MACH_FSM9XXX_SURF + depends on ARCH_FSM9XXX + depends on !MSM_STACKED_MEMORY + default y + bool "FSM9XXX SURF" + help + Support for the Qualcomm FSM9xxx femtocell + chipset based SURF evaluation board and + FFA board. + endmenu -config MSM_SMD_PKG3 +config MSM_STACKED_MEMORY + bool "Stacked Memory" + default y + help + This option is used to indicate the presence of on-die stacked + memory. When present this memory bank is used for a high speed + shared memory interface. When not present regular RAM is used. + +config PHYS_OFFSET + hex + default "0x40800000" if ARCH_MSM9615 + default "0x80200000" if ARCH_APQ8064 + default "0x80200000" if ARCH_MSM8960 + default "0x80200000" if ARCH_MSM8930 + default "0x00000000" if ARCH_MSMCOPPER + default "0x10000000" if ARCH_FSM9XXX + default "0x20200000" if ARCH_MSM9625 + default "0x00200000" if !MSM_STACKED_MEMORY + default "0x00000000" if ARCH_QSD8X50 && MSM_SOC_REV_A + default "0x20000000" if ARCH_QSD8X50 + default "0x40200000" if ARCH_MSM8X60 + default "0x10000000" + +config KERNEL_PMEM_EBI_REGION + bool "Enable in-kernel PMEM region for EBI" + default y if ARCH_MSM8X60 + depends on ANDROID_PMEM && (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSMCOPPER) + help + Enable the in-kernel PMEM allocator to use EBI memory. + +config KERNEL_PMEM_SMI_REGION + bool "Enable in-kernel PMEM region for SMI" + default y if ARCH_MSM8X60 + depends on ANDROID_PMEM && ((ARCH_QSD8X50 && !PMEM_GPU0) || (ARCH_MSM8X60 && !VCM)) + help + Enable the in-kernel PMEM allocator to use SMI memory. + +config PMEM_GPU0 + bool "Enable PMEM GPU0 region" + default y + depends on ARCH_QSD8X50 && ANDROID_PMEM + help + Enable the PMEM GPU0 device on SMI Memory. + +config MSM_AMSS_VERSION + int + default 6210 if MSM_AMSS_VERSION_6210 + default 6220 if MSM_AMSS_VERSION_6220 + default 6225 if MSM_AMSS_VERSION_6225 + +choice + prompt "AMSS modem firmware version" + + default MSM_AMSS_VERSION_6225 + + config MSM_AMSS_VERSION_6210 + bool "6.2.10" + + config MSM_AMSS_VERSION_6220 + bool "6.2.20" + + config MSM_AMSS_VERSION_6225 + bool "6.2.20 + New ADSP" +endchoice + +config MSM_HAS_DEBUG_UART_HS bool + help + Say Y here if high speed MSM UART is present. + +config MSM_HAS_DEBUG_UART_HS_V14 + bool + select MSM_HAS_DEBUG_UART_HS + help + Say Y here if high speed MSM UART v1.4 is present. + +config MSM_DEBUG_UART_PHYS + hex + default 0xA9A00000 if (ARCH_MSM7X27 || ARCH_QSD8X50) && DEBUG_MSM_UART1 + default 0xACA00000 if ARCH_MSM7X30 && DEBUG_MSM_UART1 + default 0x94000000 if ARCH_FSM9XXX && DEBUG_MSM_UART1 + default 0xA9B00000 if (ARCH_MSM7X27 || ARCH_QSD8X50) && DEBUG_MSM_UART2 + default 0xACB00000 if ARCH_MSM7X30 && DEBUG_MSM_UART2 + default 0x94100000 if ARCH_FSM9XXX && DEBUG_MSM_UART2 + default 0xA9C00000 if (ARCH_MSM7X27 || ARCH_QSD8X50) && DEBUG_MSM_UART3 + default 0xACC00000 if ARCH_MSM7X30 && DEBUG_MSM_UART3 + +choice + prompt "Debug UART" + depends on DEBUG_LL + + config DEBUG_MSM_UART1 + bool "Kernel low-level debugging messages via MSM UART1" + depends on ARCH_MSM7X27 || ARCH_MSM7X30 || ARCH_QSD8X50 || ARCH_FSM9XXX + help + Say Y here if you want the debug print routines to direct + their output to the first serial port on MSM devices. + + config DEBUG_MSM_UART2 + bool "Kernel low-level debugging messages via MSM UART2" + depends on ARCH_MSM7X27 || ARCH_MSM7X30 || ARCH_QSD8X50 || ARCH_FSM9XXX + help + Say Y here if you want the debug print routines to direct + their output to the second serial port on MSM devices. + + config DEBUG_MSM_UART3 + bool "Kernel low-level debugging messages via MSM UART3" + depends on ARCH_MSM7X27 || ARCH_MSM7X30 || ARCH_QSD8X50 + help + Say Y here if you want the debug print routines to direct + their output to the third serial port on MSM devices. + + config DEBUG_MSM8660_UART + bool "Kernel low-level debugging messages via MSM 8660 UART" + depends on ARCH_MSM8X60 + select MSM_HAS_DEBUG_UART_HS + help + Say Y here if you want the debug print routines to direct + their output to the serial port on MSM 8660 devices. + + config DEBUG_MSM8960_UART + bool "Kernel low-level debugging messages via MSM 8960 UART" + depends on ARCH_MSM8960 && DEBUG_LL + select MSM_HAS_DEBUG_UART_HS + help + Say Y here if you want the debug print routines to direct + their output to the serial port on MSM 8960 devices. + + config DEBUG_MSM8930_UART + bool "Kernel low-level debugging messages via MSM 8930 UART" + depends on ARCH_MSM8930 && DEBUG_LL + select MSM_HAS_DEBUG_UART_HS + help + Say Y here if you want the debug print routines to direct + their output to the serial port on MSM 8930 devices. + + config DEBUG_APQ8064_UART + bool "Kernel low-level debugging messages via APQ 8064 UART" + depends on ARCH_APQ8064 && DEBUG_LL + select MSM_HAS_DEBUG_UART_HS + help + Say Y here if you want the debug print routines to direct + their output to the serial port on APQ 8064 devices. + + config DEBUG_MSMCOPPER_UART + bool "Kernel low-level debugging messages via MSM Copper UART" + depends on ARCH_MSMCOPPER + select MSM_HAS_DEBUG_UART_HS_V14 + help + Say Y here if you want the debug print routines to direct + their output to the serial port on MSM Copper devices. +endchoice + +choice + prompt "Default Timer" + default MSM7X00A_USE_GP_TIMER + + config MSM7X00A_USE_GP_TIMER + bool "GP Timer" + help + Low resolution timer that allows power collapse from idle. + + config MSM7X00A_USE_DG_TIMER + bool "DG Timer" + help + High resolution timer. +endchoice + +choice + prompt "Suspend sleep mode" + default MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + help + Allows overriding the sleep mode used. Leave at power + collapse suspend unless the arm9 image has problems. + + config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + bool "Power collapse suspend" + help + Lowest sleep state. Returns through reset vector. + + config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE + bool "Power collapse" + help + Sleep state that returns through reset vector. + + config MSM7X00A_SLEEP_MODE_APPS_SLEEP + bool "Apps Sleep" + + config MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + bool "Ramp down cpu clock and wait for interrupt" + + config MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT + bool "Wait for interrupt" +endchoice + +config MSM7X00A_SLEEP_MODE + int + default 0 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + default 1 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE + default 2 if MSM7X00A_SLEEP_MODE_APPS_SLEEP + default 3 if MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + default 4 if MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT + +choice + prompt "Idle sleep mode" + default MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + help + Allows overriding the sleep mode used from idle. Leave at power + collapse suspend unless the arm9 image has problems. + + config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + bool "Power collapse suspend" + help + Lowest sleep state. Returns through reset vector. + + config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + bool "Power collapse" + help + Sleep state that returns through reset vector. + + config MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP + bool "Apps Sleep" + + config MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + bool "Ramp down cpu clock and wait for interrupt" + + config MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT + bool "Wait for interrupt" +endchoice + +config MSM7X00A_IDLE_SLEEP_MODE + int + default 0 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + default 1 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + default 2 if MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP + default 3 if MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + default 4 if MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT + +config MSM7X00A_IDLE_SLEEP_MIN_TIME + int "Minimum idle time before sleep" + default 20000000 + help + Minimum idle time in nanoseconds before entering low power mode. + +config MSM7X00A_IDLE_SPIN_TIME + int "Idle spin time before cpu ramp down" + default 80000 + help + Spin time in nanoseconds before ramping down cpu clock and entering + any low power state. + +menuconfig MSM_IDLE_STATS + bool "Collect idle statistics" + default y + help + Collect idle statistics and export them in proc/msm_pm_stats. + +if MSM_IDLE_STATS + +config MSM_IDLE_STATS_FIRST_BUCKET + int "First bucket time" + default 62500 + help + Upper time limit in nanoseconds of first bucket. + +config MSM_IDLE_STATS_BUCKET_SHIFT + int "Bucket shift" + default 2 + +config MSM_IDLE_STATS_BUCKET_COUNT + int "Bucket count" + default 10 + +config MSM_SUSPEND_STATS_FIRST_BUCKET + int "First bucket time for suspend" + default 1000000000 + help + Upper time limit in nanoseconds of first bucket of the + histogram. This is for collecting statistics on suspend. + +endif # MSM_IDLE_STATS + +config CPU_HAS_L2_PMU + bool "L2CC PMU Support" + help + Select this if the L2 cache controller has a Performance Monitoring Unit. + +config HTC_HEADSET + tristate "HTC 2 Wire detection driver" + default n + help + Provides support for detecting HTC 2 wire devices, such as wired + headset, on the trout platform. Can be used with the msm serial + debugger, but not with serial console. + +config TROUT_BATTCHG + depends on MACH_TROUT && POWER_SUPPLY + default y + bool "Trout battery / charger driver" + +config HTC_PWRSINK + depends on MSM_SMD + default n + bool "HTC Power Sink Driver" + +config QSD_SVS + bool "QSD Static Voltage Scaling" + depends on (MACH_QSD8X50_SURF || MACH_QSD8X50_FFA) + default y + select TPS65023 + help + Enables static voltage scaling using the TPS65023 PMIC. + +config QSD_PMIC_DEFAULT_DCDC1 + int "PMIC default output voltage" + depends on (MACH_QSD8X50_SURF || MACH_QSD8X50_FFA) + default 1250 + help + This is the PMIC voltage at Linux kernel boot. + +config MSM_FIQ_SUPPORT + default y + bool "Enable installation of an FIQ handler." + +config MSM_SERIAL_DEBUGGER + select MSM_FIQ_SUPPORT + select KERNEL_DEBUGGER_CORE + default n + bool "FIQ Mode Serial Debugger" + help + The FIQ serial debugger can accept commands even when the + kernel is unresponsive due to being stuck with interrupts + disabled. Depends on the kernel debugger core in drivers/misc. + +config MSM_SERIAL_DEBUGGER_CONSOLE + depends on MSM_SERIAL_DEBUGGER + default n + bool "Console on FIQ Serial Debugger port" + help + Enables a console so that printk messages are displayed on + the debugger serial port as the occur. config MSM_PROC_COMM - bool + default y + bool "Proc-Comm RPC Interface" + help + Enables a lightweight communications interface to the + baseband processor. config MSM_SMD + bool "MSM Shared Memory Driver (SMD)" + help + Support for the shared memory interface between the apps + processor and the baseband processor. Provides access to + the "shared heap", as well as virtual serial channels + used to communicate with various services on the baseband + processor. + +choice + prompt "MSM Shared memory interface version" + depends on MSM_SMD + default MSM_SMD_PKG3 if ARCH_MSM_ARM11 + default MSM_SMD_PKG4 if ARCH_MSM_SCORPION + + config MSM_SMD_PKG3 + bool + prompt "Package 3" + + config MSM_SMD_PKG4 + bool + prompt "Package 4" +endchoice + +config MSM_PCIE + bool "MSM PCIe Controller driver" + depends on PCI && PCI_MSI + help + Enables the PCIe functionality by configures PCIe core on + MSM chipset and by enabling the ARM PCI framework extension. + +config MSM_RPC_SDIO_XPRT + depends on MSM_SDIO_AL + default y + bool "MSM SDIO XPRT Layer" + help + SDIO Transport Layer for RPC Rouer + +config MSM_RPC_SDIO_DEBUG + depends on MSM_RPC_SDIO_XPRT + default y + bool "MSM SDIO XPRT debug support" + help + Support for debugging SDIO XPRT + +config MSM_SMD_DEBUG + depends on MSM_SMD + default y + bool "MSM SMD debug support" + help + Support for debugging the SMD for communication + between the ARM9 and ARM11 + +config MSM_SDIO_AL + depends on ((ARCH_MSM7X30 || MACH_MSM8X60_FUSN_FFA || MACH_TYPE_MSM8X60_FUSION) && HAS_WAKELOCK) + default y + tristate "SDIO-Abstraction-Layer" + help + Support MSM<->MDM Communication over SDIO bus. + MDM SDIO-Client should have pipes support. + +config MSM_SDIO_DMUX + bool "SDIO Data Mux Driver" + depends on MSM_SDIO_AL + default n + help + Support Muxed Data Channels over SDIO interface. + +config MSM_BAM_DMUX + bool "BAM Data Mux Driver" + depends on SPS + default n + help + Support Muxed Data Channels over BAM interface. + BAM has a limited number of pipes. This driver + provides a means to support more logical channels + via muxing than BAM could without muxing. + +config MSM_N_WAY_SMD + depends on (MSM_SMD && !(ARCH_MSM7X01A)) + default y + bool "MSM N-WAY SMD support" + help + Supports APPS-QDSP SMD communication along with + normal APPS-MODEM SMD communication. + +config MSM_N_WAY_SMSM + depends on (MSM_SMD && !(ARCH_MSM7X01A)) + default y + bool "MSM N-WAY SMSM support" + help + Supports APPS-QDSP SMSM communication along with + normal APPS-MODEM SMSM communication. + +config MSM_RESET_MODEM + tristate "Reset Modem Driver" + depends on MSM_SMD + default m + help + Allows the user to reset the modem through a device node. + +config MSM_SMD_LOGGING + depends on MSM_SMD + default y + bool "MSM Shared Memory Logger" + help + This option exposes the shared memory logger at /dev/smem_log + and a debugfs node named smem_log. + + If in doubt, say yes. + +config MSM_IPC_LOGGING + bool "MSM Debug Logging for IPC Drivers" + help + This option allows the debug logging for IPC Drivers. + + If in doubt, say no. + +config MSM_SMD_NMEA + bool "NMEA GPS Driver" + depends on MSM_SMD + default y + help + Enable this to support the NMEA GPS device. + + If in doubt, say yes. + +config MSM_SDIO_TTY + bool "SDIO TTY Driver" + depends on MSM_SDIO_AL + default n + help + Provides a TTY driver SDIO TTY + This driver can be used by user space + applications for passing data through the + SDIO interface. + +config MSM_SMD_TTY + bool "SMD TTY Driver" + depends on MSM_SMD + default y + help + Provides TTY interfaces to interact with the modem. + + If in doubt, say yes. + +config MSM_SMD_QMI + bool "SMD QMI Driver" + depends on MSM_SMD + default y + help + Manages network data connections. + + If in doubt, say yes. + +config MSM_SMD_PKT + bool "SMD Packet Driver" + depends on MSM_SMD + default y + help + Provides a binary SMD non-muxed packet port interface. + + If in doubt, say yes. + +config MSM_SDIO_CMUX + bool "SDIO CMUX Driver" + depends on MSM_SDIO_AL + default n + help + Provides a Muxed port interface over SDIO QMI + +config MSM_DSPS + bool "Sensors DSPS driver" + depends on (MSM_PIL && (ARCH_MSM8X60 || ARCH_MSM8960)) + default n + help + Provides user-space interface to the sensors manager + to turn on/off the Sensors Processor system clocks. + It is the DSPS responsibility to turn on/off the sensors + themself. + The number of clocks and their name may vary between targets. + It also triggers the PIL to load the DSPS firmware. + +config MSM_SDIO_CTL + bool "SDIO CTL Driver" + depends on MSM_SDIO_CMUX + default n + help + Provides a binary SDIO control port interface. + +config MSM_ONCRPCROUTER + depends on MSM_SMD + default n + bool "MSM ONCRPC router support" + help + Support for the MSM ONCRPC router for communication between + the ARM9 and ARM11 + +config MSM_IPC_ROUTER + depends on NET + default n + bool "MSM IPC Router support" + help + Support for the MSM IPC Router for communication between + the APPs and the MODEM + +config MSM_IPC_ROUTER_SMD_XPRT + depends on MSM_SMD + depends on MSM_IPC_ROUTER + default n + bool "MSM SMD XPRT Layer" + help + SMD Transport Layer for IPC Router + +config MSM_ONCRPCROUTER_DEBUG + depends on MSM_ONCRPCROUTER + default y + bool "MSM debug ONCRPC router support" + help + Support for debugging the ONCRPC router for communication + between the ARM9 and ARM11 + +config MSM_RPC_LOOPBACK_XPRT + depends on MSM_ONCRPCROUTER + default n + bool "MSM RPC local routing support" + help + Support for routing RPC messages between APPS clients + and APPS servers. Helps in testing APPS RPC framework. + +config MSM_RPCSERVER_TIME_REMOTE + depends on MSM_ONCRPCROUTER && RTC_HCTOSYS + default y + bool "Time remote RPC server" + help + The time remote server receives notification of time bases and + reports these events to registered callback functions. + +config MSM_RPCSERVER_WATCHDOG + depends on MSM_ONCRPCROUTER + default y + bool "Watchdog RPC server" + help + The dog_keepalive server handles watchdog events. + +config MSM_RPC_WATCHDOG + depends on MSM_ONCRPCROUTER + default n + bool "Watchdog RPC client" + help + The dog_keepalive client module. + +config MSM_RPC_PING + depends on MSM_ONCRPCROUTER && DEBUG_FS + default m + bool "MSM rpc ping" + help + Implements MSM rpc ping test module. + +config MSM_RPC_PROC_COMM_TEST + depends on DEBUG_FS && MSM_PROC_COMM + default m + bool "MSM rpc proc comm test" + help + Implements MSM rpc proc comm test module. + +config MSM_RPC_OEM_RAPI + depends on MSM_ONCRPCROUTER + default m + bool "MSM oem rapi" + help + Implements MSM oem rapi client module. + +config MSM_RPCSERVER_HANDSET + depends on MSM_ONCRPCROUTER + default y + bool "Handset events RPC server" + help + Support for receiving handset events like headset detect, + headset switch and clamshell state. + +config MSM_RMT_STORAGE_CLIENT + depends on (ARCH_MSM && MSM_ONCRPCROUTER) + default n + bool "Remote Storage RPC client" + help + Provide RPC mechanism for remote processors to access storage + device on apps processor. + +config MSM_RMT_STORAGE_CLIENT_STATS + depends on (MSM_RMT_STORAGE_CLIENT && DEBUG_FS) + default n + bool "Remote storage RPC client performance statistics" + help + Collects performance statistics and shows this information + through a debugfs file rmt_storage_stats. + +config MSM_SDIO_SMEM + depends on MSM_SDIO_AL + default n + bool "SDIO SMEM for remote storage" + help + Copies data from remote MDM9K memory to local MSM8x60 + memory. Used by remote storage client to shadow + MDM9K filesystem. + +config MSM_DALRPC + bool "DAL RPC support" + default n + help + Supports RPC calls to DAL devices on remote processor cores. + +config MSM_DALRPC_TEST + tristate "DAL RPC test module" + depends on (MSM_DALRPC && DEBUG_FS) + default m + help + Exercises DAL RPC calls to QDSP6. + +if CPU_FREQ_MSM + +config MSM_CPU_FREQ_SET_MIN_MAX + bool "Set Min/Max CPU frequencies." + default n + help + Allow setting min and max CPU frequencies. Sysfs can be used + to override these values. + +config MSM_CPU_FREQ_MAX + int "Max CPU Frequency" + depends on MSM_CPU_FREQ_SET_MIN_MAX + default 384000 + +config MSM_CPU_FREQ_MIN + int "Min CPU Frequency" + depends on MSM_CPU_FREQ_SET_MIN_MAX + default 245760 + +endif # CPU_FREQ_MSM + +config MSM_CPU_AVS + bool "Enable software controlled Adaptive Voltage Scaling (AVS)" + depends on (ARCH_MSM_SCORPION && QSD_SVS) + depends on ARCH_QSD8X50 + default n + select MSM_AVS_HW + help + This enables the s/w control of Adaptive Voltage Scaling feature + in Qualcomm ARMv7 CPUs. It adjusts the voltage for each frequency + based on feedback from three ring oscillators in the CPU. + +config MSM_AVS_HW + bool "Enable Adaptive Voltage Scaling (AVS)" + default n + help + Enable AVS hardware to fine tune voltage at each frequency. The + AVS hardware blocks associated with each Qualcomm ARMv7 cores can + fine tune the voltages based on the feedback from the ring + oscillators. + +config MSM_HW3D + tristate "MSM Hardware 3D Register Driver" + depends on ANDROID_PMEM + default y + help + Provides access to registers needed by the userspace OpenGL|ES + library. + +config MSM_ADSP + depends on (ARCH_MSM7X01A || ARCH_MSM7X25 || ARCH_MSM7X27) + tristate "MSM ADSP driver" + depends on ANDROID_PMEM + default y + help + Provides access to registers needed by the userspace aDSP library. + +config ADSP_RPC_VER + hex + default 0x30002 if (ARCH_MSM7X27 || (ARCH_MSM7X25 && AMSS_7X25_VERSION_2009)) + default 0x30001 if (ARCH_MSM7X01A || (ARCH_MSM7X25 && AMSS_7X25_VERSION_2008)) + depends on MSM_ADSP + help + Select proper ADSP RPC version +choice + prompt "ADSP RPC version" + + default AMSS_7X25_VERSION_2009 + + config AMSS_7X25_VERSION_2009 + bool "2.0.09" + + config AMSS_7X25_VERSION_2008 + bool "2.0.08" +endchoice + +config MSM7KV2_AUDIO + bool "MSM7K v2 audio" + depends on (ARCH_MSM7X30 && ANDROID_PMEM) + default y + help + Enables QDSP5V2-based audio drivers for audio playbacks and + voice call. + +config MSM_ADSP_REPORT_EVENTS + bool "Report modem events from the DSP" + default y + depends on (MSM_ADSP || MSM7KV2_AUDIO) + help + Normally, only messages from the aDSP are reported to userspace. + With this option, we report events from the aDSP as well. + +config MSM_QDSP6 + tristate "QDSP6 support" + depends on ARCH_QSD8X50 && ANDROID_PMEM + default y + help + Enable support for qdsp6. This provides audio and video functionality. + +config MSM8X60_AUDIO + tristate "MSM8X60 audio support" + depends on ARCH_MSM8X60 && ANDROID_PMEM + default y + help + Enable support for qdsp6v2. This provides audio functionality. + +config MSM8X60_FTM_AUDIO_DEVICES + bool "MSM8X60 audio factory test mode support" + depends on MSM8X60_AUDIO + help + Enable support audio factory test mode devices. This is used + in a production line environment. + +config RTAC + bool "MSM8K real-time audio calibration support" + default y + help + Enable support for rtac. This enables calibration during + audio operation + +config MSM7X27A_AUDIO + bool "MSM7X27A audio support" + depends on ARCH_MSM7X27A && MSM_ADSP + default n + help + Enable support for 7x27a. This provides audio functionality. + +config MSM_PROC_COMM_REGULATOR bool + depends on MSM_PROC_COMM && REGULATOR + help + Enable regulator framework support for regulators managed by PMLIB + on the modem, and controlled through proccomm calls. + +config MSM_VREG_SWITCH_INVERTED + bool "Reverse vreg switch polarity" + default n + help + Reverses the enable and disable for vreg switch. + +config MSM_DMA_TEST + tristate "MSM DMA test module" + default m + help + Intended to be compiled as a module. Provides a device node + and ioctls for testing the MSM dma system. + +config WIFI_CONTROL_FUNC + bool "Enable WiFi control function abstraction" + help + Enables Power/Reset/Carddetect function abstraction + +config WIFI_MEM_PREALLOC + depends on WIFI_CONTROL_FUNC + bool "Preallocate memory for WiFi buffers" + help + Preallocates memory buffers for WiFi driver + +config QSD_AUDIO + bool "QSD audio" + depends on ARCH_MSM_SCORPION && MSM_DALRPC && ANDROID_PMEM && !MSM_SMP + default y + help + Provides PCM, MP3, and AAC audio playback. + +config AUDIO_AAC_PLUS + depends on (MSM_ADSP || QSD_AUDIO || MSM7KV2_AUDIO) + bool "AAC+ Audio" + default y + help + Provides AAC+ decoding + +config AUDIO_ENHANCED_AAC_PLUS + depends on AUDIO_AAC_PLUS + bool "Enhanced AAC+ Audio" + default y + help + Provides Enhanced AAC+ decoding + +config SURF_FFA_GPIO_KEYPAD + bool "MSM SURF/FFA GPIO keypad" + depends on INPUT_GPIO = "y" + default y + help + Select if the GPIO keypad is attached. + +config MSM_SLEEP_TIME_OVERRIDE + bool "Allow overriding suspend/sleep time with PM module parameter" + default y + help + Enable the module parameter sleep_time_override. Specified + in units of seconds, it overwrites the normal sleep time of + suspend. The feature is required for automated power management + testing. + +config MSM_MEMORY_LOW_POWER_MODE + bool "Control the low power modes of memory" + default n + help + The application processor controls whether memory should enter + which low power mode. + +choice + prompt "Default Memory Low Power Mode during Idle" + depends on MSM_MEMORY_LOW_POWER_MODE + default MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE + help + Selects the default low power mode of the memory during idle + sleep. + + config MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE + bool "Memory active" + + config MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION + bool "Memory in retention" + + config MSM_MEMORY_LOW_POWER_MODE_IDLE_DEEP_POWER_DOWN + bool "Memory in deep power down" +endchoice + +choice + prompt "Default Memory Low Power Mode during Suspend" + depends on MSM_MEMORY_LOW_POWER_MODE + default MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE + help + Selects the default low power mode of the memory during suspend + sleep. + + config MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE + bool "Memory active" + + config MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION + bool "Memory in retention" + + config MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN + bool "Memory in deep power down" +endchoice + +choice + prompt "Power management timeout action" + default MSM_PM_TIMEOUT_HALT + help + Selects the Application Processor's action when Power Management + times out waiting for Modem's handshake. + + config MSM_PM_TIMEOUT_HALT + bool "Halt the Application Processor" + + config MSM_PM_TIMEOUT_RESET_MODEM + bool "Reset the Modem Processor" + + config MSM_PM_TIMEOUT_RESET_CHIP + bool "Reset the entire chip" +endchoice + +config MSM_IDLE_WAIT_ON_MODEM + int "Wait for Modem to become ready for idle power collapse" + default 0 + help + If Modem is not ready to handle Application Processor's request + for idle power collapse, wait the number of microseconds in case + Modem becomes ready soon. + +config MSM_RPM_REGULATOR + bool "RPM regulator driver" + depends on MSM_RPM && REGULATOR + help + Compile in support for the RPM regulator driver, used for setting + voltages and other parameters of the various power rails supplied + by some Qualcomm PMICs. + +config MSM_RPM_REGULATOR_SMD + bool "SMD RPM regulator driver" + depends on REGULATOR + depends on OF + depends on MSM_RPM_SMD + help + Compile in support for the SMD RPM regulator driver which is used for + setting voltages and other parameters of the various power rails + supplied by some Qualcomm PMICs. The SMD RPM regulator driver should + be used on systems which contain an RPM which communicates with the + application processor over SMD. + +config MSM_PIL + bool "Peripheral image loading" + select FW_LOADER + default n + help + Some peripherals need to be loaded into memory before they can be + brought out of reset. + + Say yes to support these devices. + +config MSM_PIL_MODEM + tristate "Modem (ARM11) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down ARM11 Modem processors. + +config MSM_PIL_QDSP6V3 + tristate "QDSP6v3 (Hexagon) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down QDSP6v3 processors (hexagon). + The QDSP6 is a low power DSP used in audio software applications. + +config MSM_PIL_QDSP6V4 + tristate "QDSP6v4 (Hexagon) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down QDSP6v4 processors (hexagon). + The QDSP6 is a low power DSP used in audio, modem firmware, and modem + software applications. + +config MSM_PIL_LPASS_QDSP6V5 + tristate "LPASS QDSP6v5 (Hexagon) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down QDSP6v5 (Hexagon) processors + in low power audio subsystems. + +config MSM_PIL_MSS_QDSP6V5 + tristate "MSS QDSP6v5 (Hexagon) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down QDSP6v5 (Hexagon) processors + in modem subsystems. + +config MSM_PIL_MBA + tristate "Support for modem self-authentication" + depends on MSM_PIL_MSS_QDSP6V5 + help + Support for booting self-authenticating modems using the Modem Boot + Authenticator. + +config MSM_PIL_RIVA + tristate "RIVA (WCNSS) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down the RIVA processor (WCNSS). + Riva is the wireless subsystem processor used in bluetooth, wireless + LAN, and FM software applications. + +config MSM_PIL_TZAPPS + tristate "TZApps Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down TZApps. + + TZApps is an image that runs in the secure processor state. It is + used to decrypt data and perform secure operations on the behalf of + the kernel. + +config MSM_PIL_DSPS + tristate "DSPS Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down ARM7 DSPS processors. + + DSPS is a sensors offloading processor used for applications such + as rotation detection, temperature, etc. + +config MSM_PIL_VIDC + tristate "Video Core Secure Boot Support" + depends on MSM_PIL + help + Support for authenticating the video core image. + +config MSM_PIL_GSS + tristate "GSS (Coretx A5) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down Cortex A5 processors which run + GPS subsystem firmware. + +config MSM_PIL_PRONTO + tristate "PRONTO (WCNSS) Boot Support" + depends on MSM_PIL + help + Support for booting and shutting down the PRONTO processor (WCNSS). + PRONTO is the wireless subsystem processor used in bluetooth, wireless + LAN, and FM software applications. + +config MSM_SCM + bool "Secure Channel Manager (SCM) support" + default n + +config MSM_SUBSYSTEM_RESTART + bool "MSM Subsystem Restart Driver" + depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615) + default n + help + This option enables the MSM subsystem restart driver, which provides + a framework to handle subsystem crashes. + +config MSM_SYSMON_COMM + bool "MSM System Monitor communication support" + depends on MSM_SMD && MSM_SUBSYSTEM_RESTART + default y + help + This option adds support for MSM System Monitor library, which + provides an API that may be used for notifying subsystems within + the SoC about other subsystems' power-up/down state-changes. + +config MSM_MODEM_8960 + bool "MSM 8960 Modem driver" + depends on (ARCH_MSM8960 || ARCH_MSM9615) + help + This option enables the modem driver for the MSM8960 and MSM9615, which monitors + modem hardware watchdog interrupt lines and plugs into the subsystem + restart and PIL drivers. For MSM9615, it only supports a full chip reset. + +config MSM_LPASS_8960 + tristate "MSM 8960 Lpass driver" + depends on (ARCH_MSM8960 || ARCH_MSM9615) + help + This option enables the lpass driver for the MSM8960 and MSM9615. This monitors + lpass hardware watchdog interrupt lines and plugs into the subsystem + restart and PIL drivers. For MSM9615, it only supports a full chip reset. + +config MSM_WCNSS_SSR_8960 + tristate "MSM 8960 WCNSS restart module" + depends on (ARCH_MSM8960) + help + This option enables the WCNSS restart module for MSM8960, which + monitors WCNSS hardware watchdog interrupt lines and plugs WCNSS + into the subsystem restart framework. + +config MSM_GSS_SSR_8064 + bool "MSM 8064 GSS restart driver" + depends on (ARCH_APQ8064) + help + This option enables the gps subsystem restart driver for APQ8064, which monitors + gss hardware watchdog interrupt lines and plugs into the subsystem + restart and PIL drivers. + +config SCORPION_Uni_45nm_BUG + bool "Scorpion Uni 45nm(SC45U): Workaround for ICIMVAU and BPIMVA" + depends on ARCH_MSM7X30 || (ARCH_QSD8X50 && MSM_SOC_REV_A) + default y + help + Invalidating the Instruction Cache by Modified Virtual Address to PoU and + invalidating the Branch Predictor Array by Modified Virtual Address can + create invalid entries in the TLB with the wrong ASID values on Scorpion + Uniprocessor 45nm (SC45U) cores. This option enables the recommended software + workaround for Scorpion Uniprocessor 45nm cores. + + This bug is not applicable to any ScorpionMP or Scorpion Uni 65nm(SC65U) cores. + +config MSM_BUSPM_DEV + tristate "MSM Bus Performance Monitor Kernel Module" + depends on (ARCH_MSM8X60 || ARCH_MSM8960) + default m + help + This kernel module is used to mmap() hardware registers for the + performance monitors, counters, etc. The module can also be used to + allocate physical memory which is used by bus performance hardware to + dump performance data. + +config MSM_TZ_LOG + tristate "MSM Trust Zone (TZ) Log Driver" + depends on DEBUG_FS + help + This option enables a driver with a debugfs interface for messages + produced by the Secure code (Trust zone). These messages provide + diagnostic information about TZ operation. + +config MSM_RPM_LOG + tristate "MSM Resource Power Manager Log Driver" + depends on DEBUG_FS + depends on MSM_RPM + default n + help + This option enables a driver which can read from a circular buffer + of messages produced by the RPM. These messages provide diagnostic + information about RPM operation. The driver outputs the messages + via a debugfs node. + +config MSM_RPM_STATS_LOG + tristate "MSM Resource Power Manager Stat Driver" + depends on DEBUG_FS + depends on MSM_RPM + default n + help + This option enables a driver which reads RPM messages from a shared + memory location. These messages provide statistical information about + the low power modes that RPM enters. The drivers outputs the message + via a debugfs node. + +config MSM_DIRECT_SCLK_ACCESS + bool "Direct access to the SCLK timer" + default n + +config IOMMU_API + bool config MSM_GPIOMUX bool -config MSM_V2_TLMM +config MSM_SECURE_IO bool -config MSM_SCM +config MSM_NATIVE_RESTART bool + +config MSM_PM2 + depends on PM + bool + +config MSM_PM8X60 + depends on PM + bool + +config MSM_NOPM + default y if !PM + bool + +config MSM_BUS_SCALING + bool "Bus scaling driver" + default n + +config MSM_BUS_RPM_MULTI_TIER_ENABLED + bool "RPM Multi-tiering Configuration" + depends on MSM_BUS_SCALING + +config MSM_WATCHDOG + bool "MSM Watchdog Support" + depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615 || ARCH_FSM9XXX + help + This enables the watchdog as is present on 8x60. Currently we use + core 0's watchdog, and reset the entire SoC if it times out. It does + not run during the bootup process, so it will not catch any early + lockups. + +config MSM_DLOAD_MODE + bool "Enable download mode on crashes" + depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615 + default n + help + This makes the SoC enter download mode when it resets + due to a kernel panic. Note that this doesn't by itself + make the kernel reboot on a kernel panic - that must be + enabled via another mechanism. + +config MSM_JTAG + bool "JTAG debug and trace support" + help + Add additional support for JTAG kernel debugging and tracing. + +config MSM_ETM + tristate "Enable MSM ETM and ETB" + depends on ARCH_MSM8X60 + select MSM_JTAG + help + Enables embedded trace collection on MSM8660 + +config MSM_QDSS + bool "Qualcomm Debug Subsystem" + select MSM_JTAG + help + Enables support for Qualcomm Debug Subsystem. + +config MSM_QDSS_ETM_DEFAULT_ENABLE + bool "Turn on QDSS ETM Tracing by Default" + depends on MSM_QDSS + help + Turns on QDSS ETM tracing by default. Otherwise, tracing is + disabled by default but can be enabled by other means. + +config MSM_SLEEP_STATS + bool "Enable exporting of MSM sleep stats to userspace" + depends on CPU_IDLE + default n + +config MSM_SLEEP_STATS_DEVICE + bool "Enable exporting of MSM sleep device stats to userspace" + +config MSM_RUN_QUEUE_STATS + bool "Enable collection and exporting of MSM Run Queue stats to userspace" + depends on (MSM_SOC_REV_A || ARCH_MSM8X60 || ARCH_MSM8960) + help + This option enalbes statistics collection on Run Queue. A daemon + in user mode, called MPDecision will be using this data to decide + on when to switch off/on the other cores. + +config MSM_STANDALONE_POWER_COLLAPSE + bool "Enable standalone power collapse" + default n + +config MSM_GSBI9_UART + bool "Enable GSBI9 UART device" + default n + help + This enables GSBI9 configured into UART. + +config MSM_SHARED_GPIO_FOR_UART2DM + bool "Use shared GPIOs into UART mode" + depends on (ARCH_MSM7X27A && !MMC_MSM_SDC3_8_BIT_SUPPORT && !MMC_MSM_SDC4_SUPPORT) + help + This option configures GPIO muxed with SDC4/MMC3 + 8-bit mode into UART mode. It is used for serial + console on UART2DM. Say Y if you want to have + serial console on UART2DM. + +config MSM_SHOW_RESUME_IRQ + bool "Enable logging of interrupts that could have caused resume" + depends on (ARM_GIC || PMIC8058) + default y if PMIC8058 + default n + help + This option logs wake up interrupts that have triggered just before + the resume loop unrolls. Say Y if you want to debug why the system + resumed. + +config BT_MSM_PINTEST + tristate "MSM Bluetooth Pin Connectivity Test" + depends on ((ARCH_MSM8X60 || ARCH_MSM7X27A) && DEBUG_FS) + default n + help + Bluetooth MSM Pin Connectivity test module. + This driver provides support for verifying the MSM to BT pin + connectivity. + +config MSM_FAKE_BATTERY + depends on POWER_SUPPLY + default n + bool "MSM Fake Battery" + help + Enables MSM fake battery driver. + +config MSM_QDSP6_APR + bool "Audio QDSP6 APR support" + depends on MSM_SMD + default n + help + Enable APR IPC protocol support between + application processor and QDSP6. APR is + used by audio driver to configure QDSP6's + ASM, ADM and AFE. + +config MSM_QDSP6_CODECS + bool "Audio Codecs on QDSP6 APR " + depends on MSM_SMD + default n + help + Enable Audio codecs with APR IPC protocol support between + application processor and QDSP6. APR is + used by audio driver to configure QDSP6's + ASM, ADM and AFE. + +config MSM_AUDIO_QDSP6 + bool "QDSP6 HW Audio support" + select SND_SOC_MSM_QDSP6_INTF + default n + help + Enable HW audio support in QDSP6. + QDSP6 can support HW encoder & decoder and audio processing + +config MSM_ULTRASOUND + bool "MSM ultrasound support" + depends on MSM_AUDIO_QDSP6 + help + Enable support for qdsp6/ultrasound. + +config MSM_RPC_VIBRATOR + bool "RPC based MSM Vibrator Support" + depends on MSM_ONCRPCROUTER + help + Enable the vibrator support on MSM over RPC. The vibrator + is connected on the PMIC. Say Y if you want to enable this + feature. + +config PM8XXX_RPC_VIBRATOR + bool "RPC based Vibrator on PM8xxx PMICs" + depends on MSM_RPC_VIBRATOR + help + Enable the vibrator support on MSM over RPC. The vibrator + is connected on the PM8XXX PMIC. Say Y if you want to enable + this feature. + +config MSM_SPM_V1 + bool "Driver support for SPM Version 1" + help + Enables the support for Version 1 of the SPM driver. SPM hardware is + used to manage the processor power during sleep. The driver allows + configuring SPM to allow different power modes. + +config MSM_SPM_V2 + bool "Driver support for SPM Version 2" + help + Enables the support for Version 2 of the SPM driver. SPM hardware is + used to manage the processor power during sleep. The driver allows + configuring SPM to allow different power modes. + +config MSM_L2_SPM + bool "SPM support for L2 cache" + depends on MSM_SPM_V2 + help + Enable SPM driver support for L2 cache. Some MSM chipsets allow + control of L2 cache low power mode with a Subsystem Power manager. + Enabling this driver allows configuring L2 SPM for low power modes + on supported chipsets. + +config MSM_MULTIMEDIA_USE_ION + bool "Multimedia suport using Ion" + depends on ION_MSM + help + Enable support for multimedia drivers using Ion for buffer management + instead of pmem. Selecting this may also involve userspace + dependencies as well. + +config MSM_OCMEM + bool "MSM On-Chip memory driver (OCMEM)" + help + Enable support for On-Chip Memory available on certain MSM chipsets. + OCMEM is a low latency, high performance pool shared by subsystems. + +config MSM_RTB + bool "Register tracing" + help + Add support for logging different events to a small uncached + region. This is designed to aid in debugging reset cases where the + caches may not be flushed before the target resets. + +config MSM_RTB_SEPARATE_CPUS + bool "Separate entries for each cpu" + depends on MSM_RTB + depends on SMP + help + Under some circumstances, it may be beneficial to give dedicated space + for each cpu to log accesses. Selecting this option will log each cpu + separately. This will guarantee that the last acesses for each cpu + will be logged but there will be fewer entries per cpu + +config MSM_CACHE_ERP + bool "Cache / CPU error reporting" + depends on ARCH_MSM_KRAIT + help + Say 'Y' here to enable reporting of cache and TLB errors to the kernel + log. Enabling this feature can be used as a system debugging technique + if cache corruption is suspected. Cache error statistics will also be + reported in /proc/cpu/msm_cache_erp. + + For production builds, you should probably say 'N' here. + +config MSM_L1_ERR_PANIC + bool "Panic on L1 cache errors" + depends on MSM_CACHE_ERP + help + To cause the kernel to panic whenever an L1 cache error is detected, say + 'Y' here. This may be useful as a debugging technique if general system + instability is suspected. + + For production builds, you should probably say 'N' here. + +config MSM_L1_ERR_LOG + bool "Log CPU ERP events to system memory" + depends on MSM_CACHE_ERP + help + Enable logging CPU ERP events to an area of memory that will be + preserved across a system reset. This may be useful for detecting and + troubleshooting ERP-related system crashes in the field. + + For production builds, you may want to say 'Y' here. + +config MSM_L2_ERP_PRINT_ACCESS_ERRORS + bool "Report L2 master port slave/decode errors in kernel log" + depends on MSM_CACHE_ERP + help + Master port errors can occur when a memory request is not properly + handled by the destination slave. This can occur if the destination + register does not exist or is inaccessible due to security + restrictions or (in some cases) clock configuration. Enabling this + option will cause a backtrace to be printed to the kernel log whenever + such an error is encountered. Note that the error is reported as an + interrupt rather than as an exception, meaning that the backtrace may + have some skid. This option may help with debugging, though production + builds should probably say 'N' here. + +config MSM_L2_ERP_PORT_PANIC + bool "Panic on L2 master port errors" + depends on MSM_CACHE_ERP && MSM_L2_ERP_PRINT_ACCESS_ERRORS + help + Master port errors can occur when a memory request is not properly + handled by the destination slave. Enable this option to catch drivers + which attempt to access bad areas of the address space, or access + hardware registers in an improper state (such as certain clocks not + being on). This option may help with debugging, though production + builds should probably say 'N' here. + +config MSM_L2_ERP_1BIT_PANIC + bool "Panic on recoverable L2 soft errors" + depends on MSM_CACHE_ERP + help + Enable this option to cause a kernel panic whenever the L2 cache + encounters a single-bit (correctable) soft error. This option should + only be enabled when doing low-level debugging where cache corruption + is suspected. + + For production builds, you should definitely say 'N' here. + +config MSM_L2_ERP_2BIT_PANIC + bool "Panic on unrecoverable L2 soft errors" + depends on MSM_CACHE_ERP + help + Enable this option to cause a kernel panic whenever the L2 cache + encounters a double-bit (non-correctable) soft error. Debug builds + will likely benefit from having this option enabled to catch cache + problems as soon as possible. + + For production builds, it may be acceptable to say 'N' here, since + an uncorrectable error might not necessarily cause further problems. + +config MSM_DCVS + bool "Use MSM DCVS for CPU/GPU Frequency control" + depends on MSM_SCM + help + Enable support for MSM DCVS to control all CPU and GPU core frequencies. + The DCVS manager allows idle driver to feed the idle information to the + algorithm and the algorithm returns a frequency for the core which is + passed to the frequency change driver. + +config HAVE_ARCH_HAS_CURRENT_TIMER + bool + +config MSM_CACHE_DUMP + bool "Cache dumping support" + help + Add infrastructure to dump the L1 and L2 caches to an allocated buffer. + This allows for analysis of the caches in case cache corruption is + suspected. + +config MSM_CACHE_DUMP_ON_PANIC + bool "Dump caches on panic" + depends on MSM_CACHE_DUMP + help + By default, the caches are flushed on panic. This means that trying to + look at them in a RAM dump will give useless data. Select this if you + want to dump the L1 and L2 caches on panic before any flush occurs. + If unsure, say N + +config MSM_HSIC_SYSMON + tristate "MSM HSIC system monitor driver" + depends on USB + help + Add support for bridging with the system monitor interface of MDM + over HSIC. This driver allows the local system monitor to + communicate with the remote system monitor interface. + +config MSM_HSIC_SYSMON_TEST + tristate "MSM HSIC system monitor bridge test" + depends on USB && MSM_HSIC_SYSMON && DEBUG_FS + help + Enable the test hook for the Qualcomm system monitor HSIC driver. + This will create a debugfs file entry named "hsic_sysmon_test" which + can be read and written to send character data to the sysmon port of + the modem over USB. + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 647308783ad..f316f3684d4 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,34 +1,372 @@ -obj-y += io.o idle.o timer.o -obj-y += clock.o -obj-y += subsystem_map.o -obj-$(CONFIG_DEBUG_FS) += clock-debug.o - -obj-$(CONFIG_MSM_VIC) += irq-vic.o -obj-$(CONFIG_MSM_IOMMU) += devices-iommu.o iommu_domains.o - -obj-$(CONFIG_ARCH_MSM7X00A) += dma.o irq.o acpuclock-arm11.o -obj-$(CONFIG_ARCH_MSM7X30) += dma.o -obj-$(CONFIG_ARCH_QSD8X50) += dma.o sirc.o - -obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o - -obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o -obj-$(CONFIG_MSM_SMD) += last_radio_log.o -obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o - CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +obj-y += io.o dma.o memory.o +ifndef CONFIG_ARM_ARCH_TIMER +obj-y += timer.o +endif +obj-y += clock.o clock-voter.o clock-dummy.o +obj-y += modem_notifier.o subsystem_map.o +obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o +obj-$(CONFIG_DEBUG_FS) += nohlt.o clock-debug.o +obj-$(CONFIG_KEXEC) += msm_kexec.o + +obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o +ifndef CONFIG_ARCH_MSM8X60 + obj-$(CONFIG_MSM_PROC_COMM) += clock-pcom.o + obj-$(CONFIG_MSM_PROC_COMM) += vreg.o mpp.o + ifdef CONFIG_MSM_PROC_COMM +ifndef CONFIG_ARCH_FSM9XXX + obj-$(CONFIG_REGULATOR) += footswitch-pcom.o +endif + obj-$(CONFIG_DEBUG_FS) += pmic_debugfs.o + endif +endif + +obj-y += acpuclock.o +obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7627.o clock-pll.o +obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o +obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o +obj-$(CONFIG_ARCH_MSM7X27A) += pmu.o + +ifndef CONFIG_MSM_SMP +obj-$(CONFIG_ARCH_MSM_SCORPION) += msm_fault_handlers.o +endif + +obj-$(CONFIG_MSM_VIC) += irq-vic.o + +ifdef CONFIG_ARCH_QSD8X50 + obj-$(CONFIG_MSM_SOC_REV_NONE) += acpuclock-8x50.o +endif + +obj-$(CONFIG_SMP) += headsmp.o +ifdef CONFIG_ARCH_MSM8625 + obj-$(CONFIG_SMP) += platsmp-8625.o +else + obj-$(CONFIG_SMP) += platsmp.o +endif obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_SMP) += headsmp.o platsmp.o -obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o -obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o -obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o -obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o -obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o -obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o -obj-$(CONFIG_ARCH_MSM8960) += board-msm8960.o devices-msm8960.o +obj-$(CONFIG_MSM_CPU_AVS) += avs.o +obj-$(CONFIG_MSM_AVS_HW) += avs_hw.o +obj-$(CONFIG_CPU_V6) += idle-v6.o +obj-$(CONFIG_CPU_V7) += idle-v7.o +obj-$(CONFIG_MSM_JTAG) += jtag.o -obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-v1.o gpiomux.o +msm-etm-objs := etm.o +obj-$(CONFIG_MSM_ETM) += msm-etm.o +obj-$(CONFIG_MSM_QDSS) += qdss.o qdss-etb.o qdss-tpiu.o qdss-funnel.o qdss-etm.o + +quiet_cmd_mkrpcsym = MKCAP $@ + cmd_mkrpcsym = $(PERL) $(srctree)/$(src)/mkrpcsym.pl $< $@ + +target += smd_rpc_sym.c +$(obj)/smd_rpc_sym.c: $(src)/smd_rpc_sym $(src)/mkrpcsym.pl + $(call if_changed,mkrpcsym) + +obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o +obj-$(CONFIG_MSM_SECURE_IO) += scm-io.o +obj-$(CONFIG_MSM_PIL) += peripheral-loader.o +obj-$(CONFIG_MSM_PIL) += scm-pas.o +obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o +obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.o +obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V5) += pil-q6v5.o pil-q6v5-lpass.o +obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-q6v5-mss.o +obj-$(CONFIG_MSM_PIL_MBA) += pil-mba.o +obj-$(CONFIG_MSM_PIL_RIVA) += pil-riva.o +obj-$(CONFIG_MSM_PIL_TZAPPS) += pil-tzapps.o +obj-$(CONFIG_MSM_PIL_VIDC) += pil-vidc.o +obj-$(CONFIG_MSM_PIL_MODEM) += pil-modem.o +obj-$(CONFIG_MSM_PIL_DSPS) += pil-dsps.o +obj-$(CONFIG_MSM_PIL_GSS) += pil-gss.o +obj-$(CONFIG_MSM_PIL_PRONTO) += pil-pronto.o +obj-$(CONFIG_ARCH_QSD8X50) += sirc.o +obj-$(CONFIG_ARCH_FSM9XXX) += sirc-fsm9xxx.o +obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o +obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o +obj-$(CONFIG_MSM_SDIO_AL) += sdio_al.o +obj-$(CONFIG_MSM_SDIO_AL) += sdio_al_test.o +obj-$(CONFIG_MSM_SDIO_AL) += sdio_al_dloader.o +obj-$(CONFIG_MSM_SDIO_DMUX) += sdio_dmux.o +obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o +obj-$(CONFIG_MSM_SMD_LOGGING) += smem_log.o +obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging.o +ifdef CONFIG_DEBUG_FS +obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging_debug.o +endif +obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o remote_spinlock.o smd_private.o +obj-y += socinfo.o +ifndef CONFIG_ARCH_MSM9615 +ifndef CONFIG_ARCH_APQ8064 +ifndef CONFIG_ARCH_MSM8960 +ifndef CONFIG_ARCH_MSM8X60 +ifndef CONFIG_ARCH_MSMCOPPER + obj-$(CONFIG_MSM_SMD) += pmic.o + obj-$(CONFIG_MSM_ONCRPCROUTER) += rpc_hsusb.o rpc_pmapp.o rpc_fsusb.o +endif +endif +endif +endif +endif +ifndef CONFIG_ARCH_MSM8960 +ifndef CONFIG_ARCH_MSM8X60 +ifndef CONFIG_ARCH_APQ8064 +ifndef CONFIG_ARCH_MSMCOPPER +ifndef CONFIG_ARCH_MSM9625 + obj-y += nand_partitions.o +endif +endif +endif +endif +endif +obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o +obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o +obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o +obj-$(CONFIG_MSM_SMD_PKT) += smd_pkt.o +obj-$(CONFIG_MSM_SDIO_CMUX) += sdio_cmux.o +obj-$(CONFIG_MSM_DSPS) += msm_dsps.o +obj-$(CONFIG_MSM_SDIO_CTL) += sdio_ctl.o +obj-$(CONFIG_MSM_SMD_NMEA) += smd_nmea.o +obj-$(CONFIG_MSM_RESET_MODEM) += reset_modem.o +obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o +obj-$(CONFIG_MSM_IPC_ROUTER) += ipc_router.o +obj-$(CONFIG_MSM_IPC_ROUTER)+= ipc_socket.o +obj-$(CONFIG_DEBUG_FS) += smd_rpc_sym.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_xdr.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += rpcrouter_smd_xprt.o +obj-$(CONFIG_MSM_RPC_SDIO_XPRT) += rpcrouter_sdio_xprt.o +obj-$(CONFIG_MSM_RPC_PING) += ping_mdm_rpc_client.o +obj-$(CONFIG_MSM_RPC_PROC_COMM_TEST) += proc_comm_test.o +obj-$(CONFIG_MSM_RPC_PING) += ping_mdm_rpc_client.o ping_apps_server.o +obj-$(CONFIG_MSM_RPC_OEM_RAPI) += oem_rapi_client.o +obj-$(CONFIG_MSM_RPC_WATCHDOG) += rpc_dog_keepalive.o +obj-$(CONFIG_MSM_RPCSERVER_WATCHDOG) += rpc_server_dog_keepalive.o +obj-$(CONFIG_MSM_RPCSERVER_TIME_REMOTE) += rpc_server_time_remote.o +obj-$(CONFIG_MSM_DALRPC) += dal.o +obj-$(CONFIG_MSM_DALRPC_TEST) += dal_remotetest.o +obj-$(CONFIG_ARCH_MSM7X30) += dal_axi.o +obj-$(CONFIG_ARCH_MSM7X27A) += dal_axi.o +obj-$(CONFIG_MSM_ADSP) += qdsp5/ +obj-$(CONFIG_MSM7KV2_AUDIO) += qdsp5v2/ +obj-$(CONFIG_MSM_RPCSERVER_HANDSET) += rpc_server_handset.o +obj-$(CONFIG_MSM_QDSP6) += qdsp6/ +obj-$(CONFIG_MSM8X60_AUDIO) += qdsp6v2/ +obj-$(CONFIG_MSM_AUDIO_QDSP6) += qdsp6v2/ +obj-$(CONFIG_MSM_HW3D) += hw3d.o +obj-$(CONFIG_PM) += pm-boot.o +obj-$(CONFIG_MSM_PM8X60) += pm-8x60.o pm-data.o +obj-$(CONFIG_MSM_IDLE_STATS) += pm-stats.o +obj-$(CONFIG_MSM_PM2) += pm2.o +obj-$(CONFIG_MSM_NOPM) += no-pm.o + +obj-$(CONFIG_MSM_PCIE) += pcie.o pcie_irq.o + +obj-$(CONFIG_MSM_SPM_V1) += spm.o +obj-$(CONFIG_MSM_SPM_V2) += spm-v2.o spm_devices.o + +obj-$(CONFIG_MSM_DMA_TEST) += dma_test.o +obj-$(CONFIG_SURF_FFA_GPIO_KEYPAD) += keypad-surf-ffa.o + +obj-$(CONFIG_ARCH_MSM7X01A) += board-halibut.o devices-msm7x01a.o clock-pcom-lookup.o +obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o +obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o board-trout-panel.o +obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o +obj-$(CONFIG_MACH_TROUT) += board-trout-mmc.o board-trout-wifi.o +obj-$(CONFIG_ARCH_QSD8X50) += devices-qsd8x50.o clock-pcom-lookup.o +obj-$(CONFIG_MACH_QSD8X50_SURF) += board-qsd8x50.o +obj-$(CONFIG_MACH_QSD8X50_FFA) += board-qsd8x50.o +obj-$(CONFIG_ARCH_MSM8X60) += devices-msm8x60.o clock-local.o clock-8x60.o acpuclock-8x60.o clock-pll.o +obj-$(CONFIG_ARCH_MSM8X60) += clock-rpm.o +obj-$(CONFIG_ARCH_MSM8X60) += saw-regulator.o +obj-$(CONFIG_ARCH_MSM8X60) += footswitch-8x60.o + +ifdef CONFIG_MSM_RPM_REGULATOR +obj-y += rpm-regulator.o +obj-$(CONFIG_ARCH_MSM8X60) += rpm-regulator-8660.o +obj-$(CONFIG_ARCH_MSM8960) += rpm-regulator-8960.o +obj-$(CONFIG_ARCH_MSM9615) += rpm-regulator-9615.o +obj-$(CONFIG_ARCH_MSM8930) += rpm-regulator-8930.o +obj-$(CONFIG_ARCH_APQ8064) += rpm-regulator-8960.o +endif + +obj-$(CONFIG_MSM_RPM_REGULATOR_SMD) += rpm-regulator-smd.o + +ifdef CONFIG_MSM_SUBSYSTEM_RESTART + obj-y += subsystem_notif.o + obj-y += subsystem_restart.o + obj-y += ramdump.o + obj-$(CONFIG_ARCH_MSM8X60) += modem-8660.o lpass-8660.o +endif +obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o +obj-$(CONFIG_MSM_MODEM_8960) += modem-8960.o +obj-$(CONFIG_MSM_LPASS_8960) += lpass-8960.o +obj-$(CONFIG_MSM_WCNSS_SSR_8960) += wcnss-ssr-8960.o +obj-$(CONFIG_MSM_GSS_SSR_8064) += gss-8064.o + +ifdef CONFIG_CPU_IDLE + obj-$(CONFIG_ARCH_APQ8064) += cpuidle.o + obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o + obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o + obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o + obj-$(CONFIG_ARCH_MSMCOPPER) += cpuidle.o +endif + +ifdef CONFIG_MSM_CAMERA_V4L2 + obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60-camera.o +endif +obj-$(CONFIG_ARCH_FSM9XXX) += devices-fsm9xxx.o +obj-$(CONFIG_ARCH_FSM9XXX) += clock-fsm9xxx.o clock-local.o acpuclock-fsm9xxx.o +obj-$(CONFIG_ARCH_FSM9XXX) += dfe-fsm9xxx.o rfic-fsm9xxx.o +obj-$(CONFIG_ARCH_FSM9XXX) += restart-fsm9xxx.o xo-fsm9xxx.o + +obj-$(CONFIG_MSM_WATCHDOG) += msm_watchdog.o +obj-$(CONFIG_MSM_WATCHDOG) += msm_watchdog_asm.o +obj-$(CONFIG_MACH_MSM8X60_RUMI3) += board-msm8x60.o +obj-$(CONFIG_MACH_MSM8X60_SIM) += board-msm8x60.o +obj-$(CONFIG_MACH_MSM8X60_SURF) += board-msm8x60.o +obj-$(CONFIG_MACH_MSM8X60_FFA) += board-msm8x60.o +obj-$(CONFIG_MACH_MSM8X60_FLUID) += board-msm8x60.o +obj-$(CONFIG_MACH_MSM8X60_DRAGON) += board-msm8x60.o +obj-$(CONFIG_MACH_TYPE_MSM8X60_FUSION) += board-msm8x60.o mdm.o +obj-$(CONFIG_MACH_MSM8X60_FUSN_FFA) += board-msm8x60.o mdm.o +obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o +obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o +obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o +obj-$(CONFIG_ARCH_MSM7X27) += clock-pcom-lookup.o +obj-$(CONFIG_MACH_MSM7X27_SURF) += board-msm7x27.o devices-msm7x27.o +obj-$(CONFIG_MACH_MSM7X27_FFA) += board-msm7x27.o devices-msm7x27.o +obj-$(CONFIG_ARCH_MSM7X27A) += clock-pcom-lookup.o devices-msm7x27a.o +board-7627a-all-objs += board-msm7627a-storage.o board-msm7627a-bt.o board-msm7627a-camera.o +board-7627a-all-objs += board-msm7627a-display.o board-msm7627a-wlan.o board-msm7627a-io.o +obj-$(CONFIG_MACH_MSM7X27A_RUMI3) += board-msm7x27a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM7X27A_SURF) += board-msm7x27a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM7X27A_FFA) += board-msm7x27a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM7627A_QRD1) += board-qrd7627a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM7627A_QRD3) += board-qrd7627a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM7627A_EVB) += board-qrd7627a.o board-7627a-all.o +obj-$(CONFIG_ARCH_MSM8625) += devices-msm7x27a.o clock-pcom-lookup.o mpm-8625.o +obj-$(CONFIG_MACH_MSM8625_RUMI3) += board-msm7x27a.o +obj-$(CONFIG_MACH_MSM8625_SURF) += board-msm7x27a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM8625_EVB) += board-qrd7627a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM8625_QRD7) += board-qrd7627a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM8625_FFA) += board-msm7x27a.o board-7627a-all.o +obj-$(CONFIG_MACH_MSM8625_EVT) += board-msm7x27a.o board-7627a-all.o +obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o +obj-$(CONFIG_ARCH_MSM7X30) += clock-local.o clock-7x30.o acpuclock-7x30.o clock-pll.o +obj-$(CONFIG_MACH_MSM7X25_SURF) += board-msm7x27.o devices-msm7x25.o +obj-$(CONFIG_MACH_MSM7X25_FFA) += board-msm7x27.o devices-msm7x25.o +obj-$(CONFIG_ARCH_MSM8960) += clock-local.o clock-dss-8960.o clock-8960.o clock-rpm.o clock-pll.o +obj-$(CONFIG_ARCH_MSM8960) += footswitch-8x60.o +obj-$(CONFIG_ARCH_MSM8960) += acpuclock-8960.o +obj-$(CONFIG_ARCH_MSM8960) += memory_topology.o +obj-$(CONFIG_ARCH_MSM8960) += saw-regulator.o +obj-$(CONFIG_ARCH_MSM8960) += devices-8960.o +obj-$(CONFIG_ARCH_APQ8064) += devices-8960.o devices-8064.o +board-8960-all-objs += board-8960.o board-8960-camera.o board-8960-display.o board-8960-pmic.o board-8960-storage.o board-8960-gpiomux.o +board-8930-all-objs += board-8930.o board-8930-camera.o board-8930-display.o board-8930-pmic.o board-8930-storage.o board-8930-gpiomux.o devices-8930.o board-8930-gpu.o +board-8064-all-objs += board-8064.o board-8064-pmic.o board-8064-storage.o board-8064-gpiomux.o board-8064-camera.o board-8064-display.o board-8064-gpu.o +obj-$(CONFIG_MACH_MSM8960_SIM) += board-8960-all.o board-8960-regulator.o +obj-$(CONFIG_MACH_MSM8960_RUMI3) += board-8960-all.o board-8960-regulator.o +obj-$(CONFIG_MACH_MSM8960_CDP) += board-8960-all.o board-8960-regulator.o +obj-$(CONFIG_MACH_MSM8960_MTP) += board-8960-all.o board-8960-regulator.o +obj-$(CONFIG_MACH_MSM8960_FLUID) += board-8960-all.o board-8960-regulator.o +obj-$(CONFIG_MACH_MSM8930_CDP) += board-8930-all.o board-8930-regulator.o +obj-$(CONFIG_MACH_MSM8930_MTP) += board-8930-all.o board-8930-regulator.o +obj-$(CONFIG_MACH_MSM8930_FLUID) += board-8930-all.o board-8930-regulator.o +obj-$(CONFIG_PM8921_BMS) += bms-batterydata.o bms-batterydata-desay.o +obj-$(CONFIG_MACH_APQ8064_SIM) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_MACH_APQ8064_RUMI3) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_MACH_APQ8064_CDP) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_MACH_APQ8064_MTP) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_MACH_APQ8064_LIQUID) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_MACH_MPQ8064_HRD) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_MACH_MPQ8064_DTV) += board-8064-all.o board-8064-regulator.o +obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o board-9615-storage.o board-9615-display.o +obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o clock-pll.o +obj-$(CONFIG_ARCH_MSMCOPPER) += board-copper.o board-dt.o board-copper-regulator.o board-copper-gpiomux.o +obj-$(CONFIG_ARCH_MSMCOPPER) += acpuclock-krait.o acpuclock-copper.o +obj-$(CONFIG_ARCH_MSMCOPPER) += clock-local2.o clock-pll.o clock-copper.o +obj-$(CONFIG_ARCH_MSMCOPPER) += gdsc.o +obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o + +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o + +CFLAGS_msm_vibrator.o += -Idrivers/staging/android + +obj-$(CONFIG_ARCH_FSM9XXX) += board-fsm9xxx.o + +obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o + +obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o +obj-$(CONFIG_HTC_HEADSET) += htc_headset.o +obj-$(CONFIG_MSM_RMT_STORAGE_CLIENT) += rmt_storage_client.o +obj-$(CONFIG_MSM_SDIO_SMEM) += sdio_smem.o +obj-$(CONFIG_MSM_RPM) += rpm.o +ifdef CONFIG_MSM_RPM + obj-$(CONFIG_ARCH_APQ8064) += rpm_resources.o + obj-$(CONFIG_ARCH_MSM8960) += rpm_resources.o + obj-$(CONFIG_ARCH_MSM8X60) += rpm_resources.o + obj-$(CONFIG_ARCH_MSM9615) += rpm_resources.o +endif +ifdef CONFIG_MSM_RPM_SMD + obj-$(CONFIG_ARCH_MSMCOPPER) += lpm_levels.o +endif +obj-$(CONFIG_MSM_MPM) += mpm.o +obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o +obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o +obj-$(CONFIG_MSM_TZ_LOG) += tz_log.o +obj-$(CONFIG_MSM_XO) += msm_xo.o +obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/ +obj-$(CONFIG_MSM_BUSPM_DEV) += msm-buspm-dev.o + +obj-$(CONFIG_MSM_IOMMU) += devices-iommu.o iommu_domains.o + +ifdef CONFIG_VCM +obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60-vcm.o +endif +obj-$(CONFIG_MSM_OCMEM) += ocmem.o ocmem_allocator.o ocmem_notifier.o + +obj-$(CONFIG_ARCH_MSM7X27) += gpiomux-7x27.o gpiomux-v1.o gpiomux.o +obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-7x30.o gpiomux-v1.o gpiomux.o obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o gpiomux-v1.o gpiomux.o obj-$(CONFIG_ARCH_MSM8X60) += gpiomux-8x60.o gpiomux-v2.o gpiomux.o +obj-$(CONFIG_ARCH_MSM8960) += gpiomux-v2.o gpiomux.o +obj-$(CONFIG_ARCH_APQ8064) += gpiomux-v2.o gpiomux.o +obj-$(CONFIG_ARCH_MSM9615) += gpiomux-v2.o gpiomux.o +obj-$(CONFIG_ARCH_MSMCOPPER) += gpiomux-v2.o gpiomux.o +obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o + + +obj-$(CONFIG_MSM_SLEEP_STATS) += idle_stats.o +obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o +obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_dcvs_idle.o +obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o +obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o +obj-$(CONFIG_BT_MSM_PINTEST) += btpintest.o +obj-$(CONFIG_MSM_FAKE_BATTERY) += fish_battery.o +obj-$(CONFIG_MSM_RPC_VIBRATOR) += msm_vibrator.o +obj-$(CONFIG_MSM_NATIVE_RESTART) += restart.o + +obj-$(CONFIG_MSM_PROC_COMM_REGULATOR) += proccomm-regulator.o +ifdef CONFIG_MSM_PROC_COMM_REGULATOR +obj-$(CONFIG_MACH_MSM7X27_SURF) += board-msm7627-regulator.o +obj-$(CONFIG_MACH_MSM7X27_FFA) += board-msm7627-regulator.o +obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30-regulator.o +obj-$(CONFIG_ARCH_MSM7X27A) += board-msm7x27a-regulator.o +endif + +obj-$(CONFIG_ARCH_MSM8960) += mdm2.o mdm_common.o +obj-$(CONFIG_MSM_RTB) += msm_rtb.o +obj-$(CONFIG_MSM_CACHE_ERP) += cache_erp.o +obj-$(CONFIG_MSM_CACHE_DUMP) += msm_cache_dump.o + +obj-$(CONFIG_MSM_HSIC_SYSMON) += hsic_sysmon.o +obj-$(CONFIG_MSM_HSIC_SYSMON_TEST) += hsic_sysmon_test.o + +obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 9b803a578b4..bd8d153b59a 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -1,3 +1,60 @@ - zreladdr-y += 0x10008000 -params_phys-y := 0x10000100 -initrd_phys-y := 0x10800000 +# MSM7x01A + zreladdr-$(CONFIG_ARCH_MSM7X01A) := 0x10008000 +params_phys-$(CONFIG_ARCH_MSM7X01A) := 0x10000100 +initrd_phys-$(CONFIG_ARCH_MSM7X01A) := 0x10800000 + +# MSM7x25 + zreladdr-$(CONFIG_ARCH_MSM7X25) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X25) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X25) := 0x0A000000 + +# MSM7x27 + zreladdr-$(CONFIG_ARCH_MSM7X27) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X27) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X27) := 0x0A000000 + +# MSM7x27A + zreladdr-$(CONFIG_ARCH_MSM7X27A) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X27A) := 0x00200100 + +# MSM8625 + zreladdr-$(CONFIG_ARCH_MSM8625) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM8625) := 0x00200100 + +# MSM7x30 + zreladdr-$(CONFIG_ARCH_MSM7X30) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X30) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x01200000 + +ifeq ($(CONFIG_MSM_SOC_REV_A),y) +# QSD8x50 + zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000 +params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100 +initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x24000000 +endif + +# MSM8x60 + zreladdr-$(CONFIG_ARCH_MSM8X60) := 0x40208000 + +# MSM8960 + zreladdr-$(CONFIG_ARCH_MSM8960) := 0x80208000 + +# MSM8930 + zreladdr-$(CONFIG_ARCH_MSM8930) := 0x80208000 + +# APQ8064 + zreladdr-$(CONFIG_ARCH_APQ8064) := 0x80208000 + +# MSMCOPPER + zreladdr-$(CONFIG_ARCH_MSMCOPPER) := 0x00008000 + +# MSM9615 + zreladdr-$(CONFIG_ARCH_MSM9615) := 0x40808000 + +# MSM9625 + zreladdr-$(CONFIG_ARCH_MSM9625) := 0x20208000 + +# FSM9XXX + zreladdr-$(CONFIG_ARCH_FSM9XXX) := 0x10008000 +params_phys-$(CONFIG_ARCH_FSM9XXX) := 0x10000100 +initrd_phys-$(CONFIG_ARCH_FSM9XXX) := 0x12000000 diff --git a/arch/arm/mach-msm/acpuclock-7627.c b/arch/arm/mach-msm/acpuclock-7627.c new file mode 100644 index 00000000000..7c2c556e16e --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-7627.c @@ -0,0 +1,992 @@ +/* + * MSM architecture clock driver + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "acpuclock.h" + +#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) +#define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) +#define A11S_VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124) + + +#define POWER_COLLAPSE_KHZ 19200 + +/* Max CPU frequency allowed by hardware while in standby waiting for an irq. */ +#define MAX_WAIT_FOR_IRQ_KHZ 128000 + +/** + * enum - For acpuclock PLL IDs + */ +enum { + ACPU_PLL_0 = 0, + ACPU_PLL_1, + ACPU_PLL_2, + ACPU_PLL_3, + ACPU_PLL_4, + ACPU_PLL_TCXO, + ACPU_PLL_END, +}; + +struct acpu_clk_src { + struct clk *clk; + const char *name; +}; + +static struct acpu_clk_src pll_clk[ACPU_PLL_END] = { + [ACPU_PLL_0] = { .name = "pll0_clk" }, + [ACPU_PLL_1] = { .name = "pll1_clk" }, + [ACPU_PLL_2] = { .name = "pll2_clk" }, + [ACPU_PLL_4] = { .name = "pll4_clk" }, +}; + +struct clock_state { + struct clkctl_acpu_speed *current_speed; + struct mutex lock; + uint32_t max_speed_delta_khz; + struct clk *ebi1_clk; +}; + +struct clkctl_acpu_speed { + unsigned int use_for_scaling; + unsigned int a11clk_khz; + int pll; + unsigned int a11clk_src_sel; + unsigned int a11clk_src_div; + unsigned int ahbclk_khz; + unsigned int ahbclk_div; + int vdd; + unsigned int axiclk_khz; + unsigned long lpj; /* loops_per_jiffy */ + /* Pointers in acpu_freq_tbl[] for max up/down steppings. */ + struct clkctl_acpu_speed *down[ACPU_PLL_END]; + struct clkctl_acpu_speed *up[ACPU_PLL_END]; +}; + +static struct clock_state drv_state = { 0 }; +static struct clkctl_acpu_speed *acpu_freq_tbl; + +/* + * ACPU freq tables used for different PLLs frequency combinations. The + * correct table is selected during init. + * + * Table stepping up/down entries are calculated during boot to choose the + * largest frequency jump that's less than max_speed_delta_khz on each PLL. + */ + +/* 7627 with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_0[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 }, + { 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 61440, 1, 3, 61440 }, + { 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 122880, 1, 4, 61440 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 160000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627 with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_0[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 98304, 0, 3, 49152 }, + { 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 65536, 2, 4, 98304 }, + { 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 98304 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 160000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627 with GSM capable modem - PLL2 @ 800 */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_800_pll4_0[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 }, + { 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 61440, 1, 3, 61440 }, + { 0, 200000, ACPU_PLL_2, 2, 3, 66667, 2, 4, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 122880, 1, 4, 61440 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 }, + { 0, 400000, ACPU_PLL_2, 2, 1, 133333, 2, 5, 160000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 }, + { 1, 800000, ACPU_PLL_2, 2, 0, 200000, 3, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627 with CDMA capable modem - PLL2 @ 800 */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_800_pll4_0[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 98304, 0, 3, 49152 }, + { 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 65536, 2, 4, 98304 }, + { 0, 200000, ACPU_PLL_2, 2, 3, 66667, 2, 4, 98304 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 }, + { 0, 400000, ACPU_PLL_2, 2, 1, 133333, 2, 5, 160000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 }, + { 1, 800000, ACPU_PLL_2, 2, 0, 200000, 3, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627a PLL2 @ 1200MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_800[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 800000, ACPU_PLL_4, 6, 0, 100000, 3, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627a PLL2 @ 1200MHz with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_800[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 }, + { 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 120000 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 120000 }, + { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 120000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 120000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 800000, ACPU_PLL_4, 6, 0, 100000, 3, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627aa PLL4 @ 1008MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1008[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 0, 504000, ACPU_PLL_4, 6, 1, 63000, 3, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627aa PLL4 @ 1008MHz with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1008[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 }, + { 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 0, 504000, ACPU_PLL_4, 6, 1, 63000, 3, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 8625 PLL4 @ 1209MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1209[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 0, 604800, ACPU_PLL_4, 6, 1, 75600, 3, 6, 160000 }, + { 1, 1209600, ACPU_PLL_4, 6, 0, 151200, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 8625 PLL4 @ 1209MHz with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1209[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 }, + { 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 0, 604800, ACPU_PLL_4, 6, 1, 75600, 3, 6, 160000 }, + { 1, 1209600, ACPU_PLL_4, 6, 0, 151200, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 8625 PLL4 @ 1152MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1152[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 8625 PLL4 @ 1115MHz with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1152[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 }, + { 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + + +/* 7625a PLL2 @ 1200MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_25a[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 50000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627a PLL2 @ 1200MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_737_pll2_1200_pll4_800[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 11, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 5, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 2, 30720, 3, 3, 61440 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 800000, ACPU_PLL_4, 6, 0, 100000, 3, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627a PLL2 @ 1200MHz with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_589_pll2_1200_pll4_800[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 }, + { 0, 65536, ACPU_PLL_1, 1, 8, 8192, 3, 1, 49152 }, + { 1, 98304, ACPU_PLL_1, 1, 5, 12288, 3, 2, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 2, 24576, 3, 3, 98304 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 120000 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 120000 }, + { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 120000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 120000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 800000, ACPU_PLL_4, 6, 0, 100000, 3, 7, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627aa PLL4 @ 1008MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_737_pll2_1200_pll4_1008[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 11, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 5, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 2, 30720, 3, 3, 61440 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 0, 504000, ACPU_PLL_4, 6, 1, 63000, 3, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7627aa PLL4 @ 1008MHz with CDMA capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_589_pll2_1200_pll4_1008[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 }, + { 0, 65536, ACPU_PLL_1, 1, 8, 8192, 3, 1, 49152 }, + { 1, 98304, ACPU_PLL_1, 1, 5, 12288, 3, 2, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 2, 24576, 3, 3, 98304 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 0, 504000, ACPU_PLL_4, 6, 1, 63000, 3, 6, 160000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 }, + { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 7, 200000}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +/* 7625a PLL2 @ 1200MHz with GSM capable modem */ +static struct clkctl_acpu_speed pll0_960_pll1_737_pll2_1200_25a[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 }, + { 0, 61440, ACPU_PLL_1, 1, 11, 7680, 3, 1, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 5, 15360, 3, 2, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 2, 30720, 3, 3, 61440 }, + { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 122880 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 50000, 3, 4, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +#define PLL_CONFIG(m0, m1, m2, m4) { \ + m0, m1, m2, m4, \ + pll0_##m0##_pll1_##m1##_pll2_##m2##_pll4_##m4 \ +} + +struct pll_freq_tbl_map { + unsigned int pll0_rate; + unsigned int pll1_rate; + unsigned int pll2_rate; + unsigned int pll4_rate; + struct clkctl_acpu_speed *tbl; +}; + +static struct pll_freq_tbl_map acpu_freq_tbl_list[] = { + PLL_CONFIG(960, 196, 1200, 0), + PLL_CONFIG(960, 245, 1200, 0), + PLL_CONFIG(960, 196, 800, 0), + PLL_CONFIG(960, 245, 800, 0), + PLL_CONFIG(960, 245, 1200, 800), + PLL_CONFIG(960, 196, 1200, 800), + PLL_CONFIG(960, 245, 1200, 1008), + PLL_CONFIG(960, 196, 1200, 1008), + PLL_CONFIG(960, 737, 1200, 800), + PLL_CONFIG(960, 589, 1200, 800), + PLL_CONFIG(960, 737, 1200, 1008), + PLL_CONFIG(960, 589, 1200, 1008), + PLL_CONFIG(960, 245, 1200, 1209), + PLL_CONFIG(960, 196, 1200, 1209), + PLL_CONFIG(960, 245, 1200, 1152), + PLL_CONFIG(960, 196, 1200, 1152), + { 0, 0, 0, 0, 0 } +}; + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[NR_CPUS][20]; + +static void __init cpufreq_table_init(void) +{ + int cpu; + for_each_possible_cpu(cpu) { + unsigned int i, freq_cnt = 0; + + /* Construct the freq_table table from acpu_freq_tbl since + * the freq_table values need to match frequencies specified + * in acpu_freq_tbl and acpu_freq_tbl needs to be fixed up + * during init. + */ + for (i = 0; acpu_freq_tbl[i].a11clk_khz != 0 + && freq_cnt < ARRAY_SIZE(*freq_table)-1; i++) { + if (acpu_freq_tbl[i].use_for_scaling) { + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency + = acpu_freq_tbl[i].a11clk_khz; + freq_cnt++; + } + } + + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].a11clk_khz != 0); + + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency = CPUFREQ_TABLE_END; + /* Register table with CPUFreq. */ + cpufreq_frequency_table_get_attr(freq_table[cpu], cpu); + pr_info("CPU%d: %d scaling frequencies supported.\n", + cpu, freq_cnt); + } +} +#endif + +static int acpuclk_set_vdd_level(int vdd) +{ + uint32_t current_vdd; + + /* + * NOTE: v1.0 of 7x27a/7x25a chip doesn't have working + * VDD switching support. + */ + if ((cpu_is_msm7x27a() || cpu_is_msm7x25a()) && + (SOCINFO_VERSION_MINOR(socinfo_get_version()) < 1)) + return 0; + + current_vdd = readl_relaxed(A11S_VDD_SVS_PLEVEL_ADDR) & 0x07; + + pr_debug("Switching VDD from %u mV -> %d mV\n", + current_vdd, vdd); + + writel_relaxed((1 << 7) | (vdd << 3), A11S_VDD_SVS_PLEVEL_ADDR); + mb(); + udelay(62); + if ((readl_relaxed(A11S_VDD_SVS_PLEVEL_ADDR) & 0x7) != vdd) { + pr_err("VDD set failed\n"); + return -EIO; + } + + pr_debug("VDD switched\n"); + + return 0; +} + +/* Set proper dividers for the given clock speed. */ +static void acpuclk_set_div(const struct clkctl_acpu_speed *hunt_s) +{ + uint32_t reg_clkctl, reg_clksel, clk_div, src_sel; + + reg_clksel = readl_relaxed(A11S_CLK_SEL_ADDR); + + /* AHB_CLK_DIV */ + clk_div = (reg_clksel >> 1) & 0x03; + /* CLK_SEL_SRC1NO */ + src_sel = reg_clksel & 1; + + /* + * If the new clock divider is higher than the previous, then + * program the divider before switching the clock + */ + if (hunt_s->ahbclk_div > clk_div) { + reg_clksel &= ~(0x3 << 1); + reg_clksel |= (hunt_s->ahbclk_div << 1); + writel_relaxed(reg_clksel, A11S_CLK_SEL_ADDR); + } + + /* Program clock source and divider */ + reg_clkctl = readl_relaxed(A11S_CLK_CNTL_ADDR); + reg_clkctl &= ~(0xFF << (8 * src_sel)); + reg_clkctl |= hunt_s->a11clk_src_sel << (4 + 8 * src_sel); + reg_clkctl |= hunt_s->a11clk_src_div << (0 + 8 * src_sel); + writel_relaxed(reg_clkctl, A11S_CLK_CNTL_ADDR); + + /* Program clock source selection */ + reg_clksel ^= 1; + writel_relaxed(reg_clksel, A11S_CLK_SEL_ADDR); + + /* Wait for the clock switch to complete */ + mb(); + udelay(50); + + /* + * If the new clock divider is lower than the previous, then + * program the divider after switching the clock + */ + if (hunt_s->ahbclk_div < clk_div) { + reg_clksel &= ~(0x3 << 1); + reg_clksel |= (hunt_s->ahbclk_div << 1); + writel_relaxed(reg_clksel, A11S_CLK_SEL_ADDR); + } +} + +static int acpuclk_7627_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + uint32_t reg_clkctl; + struct clkctl_acpu_speed *cur_s, *tgt_s, *strt_s; + int res, rc = 0; + unsigned int plls_enabled = 0, pll; + + if (reason == SETRATE_CPUFREQ) + mutex_lock(&drv_state.lock); + + strt_s = cur_s = drv_state.current_speed; + + WARN_ONCE(cur_s == NULL, "%s: not initialized\n", __func__); + if (cur_s == NULL) { + rc = -ENOENT; + goto out; + } + + if (rate == cur_s->a11clk_khz) + goto out; + + for (tgt_s = acpu_freq_tbl; tgt_s->a11clk_khz != 0; tgt_s++) { + if (tgt_s->a11clk_khz == rate) + break; + } + + if (tgt_s->a11clk_khz == 0) { + rc = -EINVAL; + goto out; + } + + /* Choose the highest speed at or below 'rate' with same PLL. */ + if (reason != SETRATE_CPUFREQ + && tgt_s->a11clk_khz < cur_s->a11clk_khz) { + while (tgt_s->pll != ACPU_PLL_TCXO && tgt_s->pll != cur_s->pll) + tgt_s--; + } + + if (strt_s->pll != ACPU_PLL_TCXO) + plls_enabled |= 1 << strt_s->pll; + + if (reason == SETRATE_CPUFREQ) { + if (strt_s->pll != tgt_s->pll && tgt_s->pll != ACPU_PLL_TCXO) { + rc = clk_enable(pll_clk[tgt_s->pll].clk); + if (rc < 0) { + pr_err("PLL%d enable failed (%d)\n", + tgt_s->pll, rc); + goto out; + } + plls_enabled |= 1 << tgt_s->pll; + } + } + /* Need to do this when coming out of power collapse since some modem + * firmwares reset the VDD when the application processor enters power + * collapse. */ + if (reason == SETRATE_CPUFREQ || reason == SETRATE_PC) { + /* Increase VDD if needed. */ + if (tgt_s->vdd > cur_s->vdd) { + rc = acpuclk_set_vdd_level(tgt_s->vdd); + if (rc < 0) { + pr_err("Unable to switch ACPU vdd (%d)\n", rc); + goto out; + } + } + } + + /* Set wait states for CPU inbetween frequency changes */ + reg_clkctl = readl_relaxed(A11S_CLK_CNTL_ADDR); + reg_clkctl |= (100 << 16); /* set WT_ST_CNT */ + writel_relaxed(reg_clkctl, A11S_CLK_CNTL_ADDR); + + pr_debug("Switching from ACPU rate %u KHz -> %u KHz\n", + strt_s->a11clk_khz, tgt_s->a11clk_khz); + + while (cur_s != tgt_s) { + /* + * Always jump to target freq if within max_speed_delta_khz, + * regardless of PLL. If differnece is greater, use the + * predefined steppings in the table. + */ + int d = abs((int)(cur_s->a11clk_khz - tgt_s->a11clk_khz)); + if (d > drv_state.max_speed_delta_khz) { + + if (tgt_s->a11clk_khz > cur_s->a11clk_khz) { + /* Step up: jump to target PLL as early as + * possible so indexing using TCXO (up[-1]) + * never occurs. */ + if (likely(cur_s->up[tgt_s->pll])) + cur_s = cur_s->up[tgt_s->pll]; + else + cur_s = cur_s->up[cur_s->pll]; + } else { + /* Step down: stay on current PLL as long as + * possible so indexing using TCXO (down[-1]) + * never occurs. */ + if (likely(cur_s->down[cur_s->pll])) + cur_s = cur_s->down[cur_s->pll]; + else + cur_s = cur_s->down[tgt_s->pll]; + } + + if (cur_s == NULL) { /* This should not happen. */ + pr_err("No stepping frequencies found. " + "strt_s:%u tgt_s:%u\n", + strt_s->a11clk_khz, tgt_s->a11clk_khz); + rc = -EINVAL; + goto out; + } + + } else { + cur_s = tgt_s; + } + + pr_debug("STEP khz = %u, pll = %d\n", + cur_s->a11clk_khz, cur_s->pll); + + if (cur_s->pll != ACPU_PLL_TCXO + && !(plls_enabled & (1 << cur_s->pll))) { + rc = clk_enable(pll_clk[cur_s->pll].clk); + if (rc < 0) { + pr_err("PLL%d enable failed (%d)\n", + cur_s->pll, rc); + goto out; + } + plls_enabled |= 1 << cur_s->pll; + } + + acpuclk_set_div(cur_s); + drv_state.current_speed = cur_s; + /* Re-adjust lpj for the new clock speed. */ +#ifdef CONFIG_SMP + for_each_possible_cpu(cpu) { + per_cpu(cpu_data, cpu).loops_per_jiffy = + cur_s->lpj; + } +#endif + /* Adjust the global one */ + loops_per_jiffy = cur_s->lpj; + + } + + /* Nothing else to do for SWFI. */ + if (reason == SETRATE_SWFI) + goto out; + + /* Change the AXI bus frequency if we can. */ + if (strt_s->axiclk_khz != tgt_s->axiclk_khz) { + res = clk_set_rate(drv_state.ebi1_clk, + tgt_s->axiclk_khz * 1000); + if (res < 0) + pr_warning("Setting AXI min rate failed (%d)\n", res); + } + + /* Disable PLLs we are not using anymore. */ + if (tgt_s->pll != ACPU_PLL_TCXO) + plls_enabled &= ~(1 << tgt_s->pll); + for (pll = ACPU_PLL_0; pll < ACPU_PLL_END; pll++) + if (plls_enabled & (1 << pll)) + clk_disable(pll_clk[pll].clk); + + /* Nothing else to do for power collapse. */ + if (reason == SETRATE_PC) + goto out; + + /* Drop VDD level if we can. */ + if (tgt_s->vdd < strt_s->vdd) { + res = acpuclk_set_vdd_level(tgt_s->vdd); + if (res < 0) + pr_warning("Unable to drop ACPU vdd (%d)\n", res); + } + + pr_debug("ACPU speed change complete\n"); +out: + if (reason == SETRATE_CPUFREQ) + mutex_unlock(&drv_state.lock); + return rc; +} + +static void __init acpuclk_hw_init(void) +{ + struct clkctl_acpu_speed *speed; + uint32_t div, sel, reg_clksel; + int res; + + /* + * Prepare all the PLLs because we enable/disable them + * from atomic context and can't always ensure they're + * all prepared in non-atomic context. Same goes for + * ebi1_acpu_clk. + */ + BUG_ON(clk_prepare(pll_clk[ACPU_PLL_0].clk)); + BUG_ON(clk_prepare(pll_clk[ACPU_PLL_1].clk)); + BUG_ON(clk_prepare(pll_clk[ACPU_PLL_2].clk)); + BUG_ON(clk_prepare(pll_clk[ACPU_PLL_4].clk)); + BUG_ON(clk_prepare(drv_state.ebi1_clk)); + + /* + * Determine the rate of ACPU clock + */ + + if (!(readl_relaxed(A11S_CLK_SEL_ADDR) & 0x01)) { /* CLK_SEL_SRC1N0 */ + /* CLK_SRC0_SEL */ + sel = (readl_relaxed(A11S_CLK_CNTL_ADDR) >> 12) & 0x7; + /* CLK_SRC0_DIV */ + div = (readl_relaxed(A11S_CLK_CNTL_ADDR) >> 8) & 0x0f; + } else { + /* CLK_SRC1_SEL */ + sel = (readl_relaxed(A11S_CLK_CNTL_ADDR) >> 4) & 0x07; + /* CLK_SRC1_DIV */ + div = readl_relaxed(A11S_CLK_CNTL_ADDR) & 0x0f; + } + + for (speed = acpu_freq_tbl; speed->a11clk_khz != 0; speed++) { + if (speed->a11clk_src_sel == sel + && (speed->a11clk_src_div == div)) + break; + } + if (speed->a11clk_khz == 0) { + pr_err("Error - ACPU clock reports invalid speed\n"); + return; + } + + drv_state.current_speed = speed; + if (speed->pll != ACPU_PLL_TCXO) { + if (clk_enable(pll_clk[speed->pll].clk)) + pr_warning("Failed to vote for boot PLL\n"); + } + + /* Fix div2 to 2 for 7x27/5a(aa) targets */ + if (!cpu_is_msm7x27()) { + reg_clksel = readl_relaxed(A11S_CLK_SEL_ADDR); + reg_clksel &= ~(0x3 << 14); + reg_clksel |= (0x1 << 14); + writel_relaxed(reg_clksel, A11S_CLK_SEL_ADDR); + } + + res = clk_set_rate(drv_state.ebi1_clk, speed->axiclk_khz * 1000); + if (res < 0) + pr_warning("Setting AXI min rate failed (%d)\n", res); + res = clk_enable(drv_state.ebi1_clk); + if (res < 0) + pr_warning("Enabling AXI clock failed (%d)\n", res); + + pr_info("ACPU running at %d KHz\n", speed->a11clk_khz); +} + +static unsigned long acpuclk_7627_get_rate(int cpu) +{ + WARN_ONCE(drv_state.current_speed == NULL, + "%s: not initialized\n", __func__); + if (drv_state.current_speed) + return drv_state.current_speed->a11clk_khz; + else + return 0; +} + +/*---------------------------------------------------------------------------- + * Clock driver initialization + *---------------------------------------------------------------------------*/ +#define MHZ 1000000 +static void __init select_freq_plan(void) +{ + unsigned long pll_mhz[ACPU_PLL_END]; + struct pll_freq_tbl_map *t; + int i; + + /* Get PLL clocks */ + for (i = 0; i < ACPU_PLL_END; i++) { + if (pll_clk[i].name) { + pll_clk[i].clk = clk_get_sys("acpu", pll_clk[i].name); + if (IS_ERR(pll_clk[i].clk)) { + pll_mhz[i] = 0; + continue; + } + /* Get PLL's Rate */ + pll_mhz[i] = clk_get_rate(pll_clk[i].clk)/MHZ; + } + } + + /* + * For the pll configuration used in acpuclock table e.g. + * pll0_960_pll1_245_pll2_1200" is same for 7627 and + * 7625a (as pll0,pll1,pll2) having same rates, but frequency + * table is different for both targets. + * + * Hence below for loop will not be able to select correct + * table based on PLL rates as rates are same. Hence we need + * to add this cpu check for selecting the correct acpuclock table. + */ + if (cpu_is_msm7x25a()) { + if (pll_mhz[ACPU_PLL_1] == 245) { + acpu_freq_tbl = + pll0_960_pll1_245_pll2_1200_25a; + } else if (pll_mhz[ACPU_PLL_1] == 737) { + acpu_freq_tbl = + pll0_960_pll1_737_pll2_1200_25a; + } + } else { + /* Select the right table to use. */ + for (t = acpu_freq_tbl_list; t->tbl != 0; t++) { + if (t->pll0_rate == pll_mhz[ACPU_PLL_0] + && t->pll1_rate == pll_mhz[ACPU_PLL_1] + && t->pll2_rate == pll_mhz[ACPU_PLL_2] + && t->pll4_rate == pll_mhz[ACPU_PLL_4]) { + acpu_freq_tbl = t->tbl; + break; + } + } + } + + if (acpu_freq_tbl == NULL) { + pr_crit("Unknown PLL configuration!\n"); + BUG(); + } +} + +/* + * Hardware requires the CPU to be dropped to less than MAX_WAIT_FOR_IRQ_KHZ + * before entering a wait for irq low-power mode. Find a suitable rate. + */ +static unsigned long __init find_wait_for_irq_khz(void) +{ + unsigned long found_khz = 0; + int i; + + for (i = 0; acpu_freq_tbl[i].a11clk_khz && + acpu_freq_tbl[i].a11clk_khz <= MAX_WAIT_FOR_IRQ_KHZ; i++) + found_khz = acpu_freq_tbl[i].a11clk_khz; + + return found_khz; +} + +static void __init lpj_init(void) +{ + int i = 0, cpu; + const struct clkctl_acpu_speed *base_clk = drv_state.current_speed; + unsigned long loops; + + for_each_possible_cpu(cpu) { +#ifdef CONFIG_SMP + loops = per_cpu(cpu_data, cpu).loops_per_jiffy; +#else + loops = loops_per_jiffy; +#endif + for (i = 0; acpu_freq_tbl[i].a11clk_khz; i++) { + acpu_freq_tbl[i].lpj = cpufreq_scale( + loops, + base_clk->a11clk_khz, + acpu_freq_tbl[i].a11clk_khz); + } + } +} + +static void __init precompute_stepping(void) +{ + int i, step_idx; + +#define cur_freq acpu_freq_tbl[i].a11clk_khz +#define step_freq acpu_freq_tbl[step_idx].a11clk_khz +#define cur_pll acpu_freq_tbl[i].pll +#define step_pll acpu_freq_tbl[step_idx].pll + + for (i = 0; acpu_freq_tbl[i].a11clk_khz; i++) { + + /* Calculate max "up" step for each destination PLL */ + step_idx = i + 1; + while (step_freq && (step_freq - cur_freq) + <= drv_state.max_speed_delta_khz) { + acpu_freq_tbl[i].up[step_pll] = + &acpu_freq_tbl[step_idx]; + step_idx++; + } + if (step_idx == (i + 1) && step_freq) { + pr_crit("Delta between freqs %u KHz and %u KHz is" + " too high!\n", cur_freq, step_freq); + BUG(); + } + + /* Calculate max "down" step for each destination PLL */ + step_idx = i - 1; + while (step_idx >= 0 && (cur_freq - step_freq) + <= drv_state.max_speed_delta_khz) { + acpu_freq_tbl[i].down[step_pll] = + &acpu_freq_tbl[step_idx]; + step_idx--; + } + if (step_idx == (i - 1) && i > 0) { + pr_crit("Delta between freqs %u KHz and %u KHz is" + " too high!\n", cur_freq, step_freq); + BUG(); + } + } +} + +static void __init print_acpu_freq_tbl(void) +{ + struct clkctl_acpu_speed *t; + short down_idx[ACPU_PLL_END]; + short up_idx[ACPU_PLL_END]; + int i, j; + +#define FREQ_IDX(freq_ptr) (freq_ptr - acpu_freq_tbl) + pr_info("Id CPU-KHz PLL DIV AHB-KHz ADIV AXI-KHz " + "D0 D1 D2 D4 U0 U1 U2 U4\n"); + + t = &acpu_freq_tbl[0]; + for (i = 0; t->a11clk_khz != 0; i++) { + + for (j = 0; j < ACPU_PLL_END; j++) { + down_idx[j] = t->down[j] ? FREQ_IDX(t->down[j]) : -1; + up_idx[j] = t->up[j] ? FREQ_IDX(t->up[j]) : -1; + } + + pr_info("%2d %7d %3d %3d %7d %4d %7d " + "%2d %2d %2d %2d %2d %2d %2d %2d\n", + i, t->a11clk_khz, t->pll, t->a11clk_src_div + 1, + t->ahbclk_khz, t->ahbclk_div + 1, t->axiclk_khz, + down_idx[0], down_idx[1], down_idx[2], down_idx[4], + up_idx[0], up_idx[1], up_idx[2], up_idx[4]); + + t++; + } +} + + +static struct acpuclk_data acpuclk_7627_data = { + .set_rate = acpuclk_7627_set_rate, + .get_rate = acpuclk_7627_get_rate, + .power_collapse_khz = POWER_COLLAPSE_KHZ, + .switch_time_us = 50, +}; + +static int __init acpuclk_7627_init(struct acpuclk_soc_data *soc_data) +{ + pr_info("%s()\n", __func__); + + drv_state.ebi1_clk = clk_get(NULL, "ebi1_acpu_clk"); + BUG_ON(IS_ERR(drv_state.ebi1_clk)); + + mutex_init(&drv_state.lock); + drv_state.max_speed_delta_khz = soc_data->max_speed_delta_khz; + select_freq_plan(); + acpuclk_7627_data.wait_for_irq_khz = find_wait_for_irq_khz(); + precompute_stepping(); + acpuclk_hw_init(); + lpj_init(); + print_acpu_freq_tbl(); + acpuclk_register(&acpuclk_7627_data); + +#ifdef CONFIG_CPU_FREQ_MSM + cpufreq_table_init(); +#endif + return 0; +} + +struct acpuclk_soc_data acpuclk_7x27_soc_data __initdata = { + .max_speed_delta_khz = 400000, + .init = acpuclk_7627_init, +}; + +struct acpuclk_soc_data acpuclk_7x27a_soc_data __initdata = { + .max_speed_delta_khz = 400000, + .init = acpuclk_7627_init, +}; + +struct acpuclk_soc_data acpuclk_7x27aa_soc_data __initdata = { + .max_speed_delta_khz = 504000, + .init = acpuclk_7627_init, +}; + +struct acpuclk_soc_data acpuclk_8625_soc_data __initdata = { + /* TODO: Need to update speed delta from H/w Team */ + .max_speed_delta_khz = 604800, + .init = acpuclk_7627_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-7x30.c b/arch/arm/mach-msm/acpuclock-7x30.c new file mode 100644 index 00000000000..29b00655c68 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-7x30.c @@ -0,0 +1,499 @@ +/* + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "clock.h" +#include "acpuclock.h" +#include "spm.h" + +#define SCSS_CLK_CTL_ADDR (MSM_ACC0_BASE + 0x04) +#define SCSS_CLK_SEL_ADDR (MSM_ACC0_BASE + 0x08) + +#define PLL2_L_VAL_ADDR (MSM_CLK_CTL_BASE + 0x33C) +#define PLL2_M_VAL_ADDR (MSM_CLK_CTL_BASE + 0x340) +#define PLL2_N_VAL_ADDR (MSM_CLK_CTL_BASE + 0x344) +#define PLL2_CONFIG_ADDR (MSM_CLK_CTL_BASE + 0x34C) + +#define VREF_SEL 1 /* 0: 0.625V (50mV step), 1: 0.3125V (25mV step). */ +#define V_STEP (25 * (2 - VREF_SEL)) /* Minimum voltage step size. */ +#define VREG_DATA (VREG_CONFIG | (VREF_SEL << 5)) +#define VREG_CONFIG (BIT(7) | BIT(6)) /* Enable VREG, pull-down if disabled. */ +/* Cause a compile error if the voltage is not a multiple of the step size. */ +#define MV(mv) ((mv) / (!((mv) % V_STEP))) +/* mv = (750mV + (raw * 25mV)) * (2 - VREF_SEL) */ +#define VDD_RAW(mv) (((MV(mv) / V_STEP) - 30) | VREG_DATA) + +#define MAX_AXI_KHZ 192000 + +struct clock_state { + struct clkctl_acpu_speed *current_speed; + struct mutex lock; + struct clk *ebi1_clk; +}; + +struct pll { + unsigned int l; + unsigned int m; + unsigned int n; + unsigned int pre_div; +}; + +struct clkctl_acpu_speed { + unsigned int use_for_scaling; + unsigned int acpu_clk_khz; + int src; + unsigned int acpu_src_sel; + unsigned int acpu_src_div; + unsigned int axi_clk_hz; + unsigned int vdd_mv; + unsigned int vdd_raw; + struct pll *pll_rate; + unsigned long lpj; /* loops_per_jiffy */ +}; + +static struct clock_state drv_state = { 0 }; + +/* Switch to this when reprogramming PLL2 */ +static struct clkctl_acpu_speed *backup_s; + +static struct pll pll2_tbl[] = { + { 42, 0, 1, 0 }, /* 806 MHz */ + { 53, 1, 3, 0 }, /* 1024 MHz */ + { 125, 0, 1, 1 }, /* 1200 MHz */ + { 73, 0, 1, 0 }, /* 1401 MHz */ +}; + +/* Use negative numbers for sources that can't be enabled/disabled */ + +enum acpuclk_source { + LPXO = -2, + AXI = -1, + PLL_0 = 0, + PLL_1, + PLL_2, + PLL_3, + MAX_SOURCE +}; + +static struct clk *acpuclk_sources[MAX_SOURCE]; + +/* + * Each ACPU frequency has a certain minimum MSMC1 voltage requirement + * that is implicitly met by voting for a specific minimum AXI frequency. + * Do NOT change the AXI frequency unless you are _absoulutely_ sure you + * know all the h/w requirements. + */ +static struct clkctl_acpu_speed acpu_freq_tbl[] = { + { 0, 24576, LPXO, 0, 0, 30720000, 900, VDD_RAW(900) }, + { 0, 61440, PLL_3, 5, 11, 61440000, 900, VDD_RAW(900) }, + { 1, 122880, PLL_3, 5, 5, 61440000, 900, VDD_RAW(900) }, + { 0, 184320, PLL_3, 5, 4, 61440000, 900, VDD_RAW(900) }, + { 0, MAX_AXI_KHZ, AXI, 1, 0, 61440000, 900, VDD_RAW(900) }, + { 1, 245760, PLL_3, 5, 2, 61440000, 900, VDD_RAW(900) }, + { 1, 368640, PLL_3, 5, 1, 122800000, 900, VDD_RAW(900) }, + /* AXI has MSMC1 implications. See above. */ + { 1, 768000, PLL_1, 2, 0, 153600000, 1050, VDD_RAW(1050) }, + /* + * AXI has MSMC1 implications. See above. + */ + { 1, 806400, PLL_2, 3, 0, UINT_MAX, 1100, VDD_RAW(1100), &pll2_tbl[0]}, + { 1, 1024000, PLL_2, 3, 0, UINT_MAX, 1200, VDD_RAW(1200), &pll2_tbl[1]}, + { 1, 1200000, PLL_2, 3, 0, UINT_MAX, 1200, VDD_RAW(1200), &pll2_tbl[2]}, + { 1, 1401600, PLL_2, 3, 0, UINT_MAX, 1250, VDD_RAW(1250), &pll2_tbl[3]}, + { 0 } +}; + +static int acpuclk_set_acpu_vdd(struct clkctl_acpu_speed *s) +{ + int ret = msm_spm_set_vdd(0, s->vdd_raw); + if (ret) + return ret; + + /* Wait for voltage to stabilize. */ + udelay(62); + return 0; +} + +/* Assumes PLL2 is off and the acpuclock isn't sourced from PLL2 */ +static void acpuclk_config_pll2(struct pll *pll) +{ + uint32_t config = readl_relaxed(PLL2_CONFIG_ADDR); + + /* Make sure write to disable PLL_2 has completed + * before reconfiguring that PLL. */ + mb(); + writel_relaxed(pll->l, PLL2_L_VAL_ADDR); + writel_relaxed(pll->m, PLL2_M_VAL_ADDR); + writel_relaxed(pll->n, PLL2_N_VAL_ADDR); + if (pll->pre_div) + config |= BIT(15); + else + config &= ~BIT(15); + writel_relaxed(config, PLL2_CONFIG_ADDR); + /* Make sure PLL is programmed before returning. */ + mb(); +} + +/* Set clock source and divider given a clock speed */ +static void acpuclk_set_src(const struct clkctl_acpu_speed *s) +{ + uint32_t reg_clksel, reg_clkctl, src_sel; + + reg_clksel = readl_relaxed(SCSS_CLK_SEL_ADDR); + + /* CLK_SEL_SRC1NO */ + src_sel = reg_clksel & 1; + + /* Program clock source and divider. */ + reg_clkctl = readl_relaxed(SCSS_CLK_CTL_ADDR); + reg_clkctl &= ~(0xFF << (8 * src_sel)); + reg_clkctl |= s->acpu_src_sel << (4 + 8 * src_sel); + reg_clkctl |= s->acpu_src_div << (0 + 8 * src_sel); + writel_relaxed(reg_clkctl, SCSS_CLK_CTL_ADDR); + + /* Toggle clock source. */ + reg_clksel ^= 1; + + /* Program clock source selection. */ + writel_relaxed(reg_clksel, SCSS_CLK_SEL_ADDR); + + /* Make sure switch to new source is complete. */ + mb(); +} + +static int acpuclk_7x30_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + struct clkctl_acpu_speed *tgt_s, *strt_s; + int res, rc = 0; + + if (reason == SETRATE_CPUFREQ) + mutex_lock(&drv_state.lock); + + strt_s = drv_state.current_speed; + + if (rate == strt_s->acpu_clk_khz) + goto out; + + for (tgt_s = acpu_freq_tbl; tgt_s->acpu_clk_khz != 0; tgt_s++) { + if (tgt_s->acpu_clk_khz == rate) + break; + } + if (tgt_s->acpu_clk_khz == 0) { + rc = -EINVAL; + goto out; + } + + if (reason == SETRATE_CPUFREQ) { + /* Increase VDD if needed. */ + if (tgt_s->vdd_mv > strt_s->vdd_mv) { + rc = acpuclk_set_acpu_vdd(tgt_s); + if (rc < 0) { + pr_err("ACPU VDD increase to %d mV failed " + "(%d)\n", tgt_s->vdd_mv, rc); + goto out; + } + } + } + + pr_debug("Switching from ACPU rate %u KHz -> %u KHz\n", + strt_s->acpu_clk_khz, tgt_s->acpu_clk_khz); + + /* Increase the AXI bus frequency if needed. This must be done before + * increasing the ACPU frequency, since voting for high AXI rates + * implicitly takes care of increasing the MSMC1 voltage, as needed. */ + if (tgt_s->axi_clk_hz > strt_s->axi_clk_hz) { + rc = clk_set_rate(drv_state.ebi1_clk, tgt_s->axi_clk_hz); + if (rc < 0) { + pr_err("Setting AXI min rate failed (%d)\n", rc); + goto out; + } + } + + /* Move off of PLL2 if we're reprogramming it */ + if (tgt_s->src == PLL_2 && strt_s->src == PLL_2) { + clk_enable(acpuclk_sources[backup_s->src]); + acpuclk_set_src(backup_s); + clk_disable(acpuclk_sources[strt_s->src]); + } + + /* Reconfigure PLL2 if we're moving to it */ + if (tgt_s->src == PLL_2) + acpuclk_config_pll2(tgt_s->pll_rate); + + /* Make sure target PLL is on. */ + if ((strt_s->src != tgt_s->src && tgt_s->src >= 0) || + (tgt_s->src == PLL_2 && strt_s->src == PLL_2)) { + pr_debug("Enabling PLL %d\n", tgt_s->src); + clk_enable(acpuclk_sources[tgt_s->src]); + } + + /* Perform the frequency switch */ + acpuclk_set_src(tgt_s); + drv_state.current_speed = tgt_s; + loops_per_jiffy = tgt_s->lpj; + + if (tgt_s->src == PLL_2 && strt_s->src == PLL_2) + clk_disable(acpuclk_sources[backup_s->src]); + + /* Nothing else to do for SWFI. */ + if (reason == SETRATE_SWFI) + goto out; + + /* Turn off previous PLL if not used. */ + if (strt_s->src != tgt_s->src && strt_s->src >= 0) { + pr_debug("Disabling PLL %d\n", strt_s->src); + clk_disable(acpuclk_sources[strt_s->src]); + } + + /* Decrease the AXI bus frequency if we can. */ + if (tgt_s->axi_clk_hz < strt_s->axi_clk_hz) { + res = clk_set_rate(drv_state.ebi1_clk, tgt_s->axi_clk_hz); + if (res < 0) + pr_warning("Setting AXI min rate failed (%d)\n", res); + } + + /* Nothing else to do for power collapse. */ + if (reason == SETRATE_PC) + goto out; + + /* Drop VDD level if we can. */ + if (tgt_s->vdd_mv < strt_s->vdd_mv) { + res = acpuclk_set_acpu_vdd(tgt_s); + if (res) + pr_warning("ACPU VDD decrease to %d mV failed (%d)\n", + tgt_s->vdd_mv, res); + } + + pr_debug("ACPU speed change complete\n"); +out: + if (reason == SETRATE_CPUFREQ) + mutex_unlock(&drv_state.lock); + + return rc; +} + +static unsigned long acpuclk_7x30_get_rate(int cpu) +{ + WARN_ONCE(drv_state.current_speed == NULL, + "acpuclk_get_rate: not initialized\n"); + if (drv_state.current_speed) + return drv_state.current_speed->acpu_clk_khz; + else + return 0; +} + +/*---------------------------------------------------------------------------- + * Clock driver initialization + *---------------------------------------------------------------------------*/ + +static void __init acpuclk_hw_init(void) +{ + struct clkctl_acpu_speed *s; + uint32_t div, sel, src_num; + uint32_t reg_clksel, reg_clkctl; + int res; + u8 pll2_l = readl_relaxed(PLL2_L_VAL_ADDR) & 0xFF; + + drv_state.ebi1_clk = clk_get(NULL, "ebi1_clk"); + BUG_ON(IS_ERR(drv_state.ebi1_clk)); + + reg_clksel = readl_relaxed(SCSS_CLK_SEL_ADDR); + + /* Determine the ACPU clock rate. */ + switch ((reg_clksel >> 1) & 0x3) { + case 0: /* Running off the output of the raw clock source mux. */ + reg_clkctl = readl_relaxed(SCSS_CLK_CTL_ADDR); + src_num = reg_clksel & 0x1; + sel = (reg_clkctl >> (12 - (8 * src_num))) & 0x7; + div = (reg_clkctl >> (8 - (8 * src_num))) & 0xF; + + /* Check frequency table for matching sel/div pair. */ + for (s = acpu_freq_tbl; s->acpu_clk_khz != 0; s++) { + if (s->acpu_src_sel == sel && s->acpu_src_div == div) + break; + } + if (s->acpu_clk_khz == 0) { + pr_err("Error - ACPU clock reports invalid speed\n"); + return; + } + break; + case 2: /* Running off of the SCPLL selected through the core mux. */ + /* Switch to run off of the SCPLL selected through the raw + * clock source mux. */ + for (s = acpu_freq_tbl; s->acpu_clk_khz != 0 + && s->src != PLL_2 && s->acpu_src_div == 0; s++) + ; + if (s->acpu_clk_khz != 0) { + /* Program raw clock source mux. */ + acpuclk_set_src(s); + + /* Switch to raw clock source input of the core mux. */ + reg_clksel = readl_relaxed(SCSS_CLK_SEL_ADDR); + reg_clksel &= ~(0x3 << 1); + writel_relaxed(reg_clksel, SCSS_CLK_SEL_ADDR); + break; + } + /* else fall through */ + default: + pr_err("Error - ACPU clock reports invalid source\n"); + return; + } + + /* Look at PLL2's L val to determine what speed PLL2 is running at */ + if (s->src == PLL_2) + for ( ; s->acpu_clk_khz; s++) + if (s->pll_rate && s->pll_rate->l == pll2_l) + break; + + /* Set initial ACPU VDD. */ + acpuclk_set_acpu_vdd(s); + + drv_state.current_speed = s; + + /* Initialize current PLL's reference count. */ + if (s->src >= 0) + clk_enable(acpuclk_sources[s->src]); + + res = clk_set_rate(drv_state.ebi1_clk, s->axi_clk_hz); + if (res < 0) + pr_warning("Setting AXI min rate failed!\n"); + + pr_info("ACPU running at %d KHz\n", s->acpu_clk_khz); + + return; +} + +/* Initalize the lpj field in the acpu_freq_tbl. */ +static void __init lpj_init(void) +{ + int i; + const struct clkctl_acpu_speed *base_clk = drv_state.current_speed; + + for (i = 0; acpu_freq_tbl[i].acpu_clk_khz; i++) { + acpu_freq_tbl[i].lpj = cpufreq_scale(loops_per_jiffy, + base_clk->acpu_clk_khz, + acpu_freq_tbl[i].acpu_clk_khz); + } +} + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table cpufreq_tbl[ARRAY_SIZE(acpu_freq_tbl)]; + +static void setup_cpufreq_table(void) +{ + unsigned i = 0; + const struct clkctl_acpu_speed *speed; + + for (speed = acpu_freq_tbl; speed->acpu_clk_khz; speed++) + if (speed->use_for_scaling) { + cpufreq_tbl[i].index = i; + cpufreq_tbl[i].frequency = speed->acpu_clk_khz; + i++; + } + cpufreq_tbl[i].frequency = CPUFREQ_TABLE_END; + + cpufreq_frequency_table_get_attr(cpufreq_tbl, smp_processor_id()); +} +#else +static inline void setup_cpufreq_table(void) { } +#endif + +/* + * Truncate the frequency table at the current PLL2 rate and determine the + * backup PLL to use when scaling PLL2. + */ +void __init pll2_fixup(void) +{ + struct clkctl_acpu_speed *speed = acpu_freq_tbl; + u8 pll2_l = readl_relaxed(PLL2_L_VAL_ADDR) & 0xFF; + + for ( ; speed->acpu_clk_khz; speed++) { + if (speed->src != PLL_2) + backup_s = speed; + if (speed->pll_rate && speed->pll_rate->l == pll2_l) { + speed++; + speed->acpu_clk_khz = 0; + return; + } + } + + pr_err("Unknown PLL2 lval %d\n", pll2_l); + BUG(); +} + +#define RPM_BYPASS_MASK (1 << 3) +#define PMIC_MODE_MASK (1 << 4) + +static void __init populate_plls(void) +{ + acpuclk_sources[PLL_1] = clk_get_sys("acpu", "pll1_clk"); + BUG_ON(IS_ERR(acpuclk_sources[PLL_1])); + acpuclk_sources[PLL_2] = clk_get_sys("acpu", "pll2_clk"); + BUG_ON(IS_ERR(acpuclk_sources[PLL_2])); + acpuclk_sources[PLL_3] = clk_get_sys("acpu", "pll3_clk"); + BUG_ON(IS_ERR(acpuclk_sources[PLL_3])); + /* + * Prepare all the PLLs because we enable/disable them + * from atomic context and can't always ensure they're + * all prepared in non-atomic context. + */ + BUG_ON(clk_prepare(acpuclk_sources[PLL_1])); + BUG_ON(clk_prepare(acpuclk_sources[PLL_2])); + BUG_ON(clk_prepare(acpuclk_sources[PLL_3])); +} + +static struct acpuclk_data acpuclk_7x30_data = { + .set_rate = acpuclk_7x30_set_rate, + .get_rate = acpuclk_7x30_get_rate, + .power_collapse_khz = MAX_AXI_KHZ, + .wait_for_irq_khz = MAX_AXI_KHZ, + .switch_time_us = 50, +}; + +static int __init acpuclk_7x30_init(struct acpuclk_soc_data *soc_data) +{ + pr_info("%s()\n", __func__); + + mutex_init(&drv_state.lock); + pll2_fixup(); + populate_plls(); + acpuclk_hw_init(); + lpj_init(); + setup_cpufreq_table(); + acpuclk_register(&acpuclk_7x30_data); + + return 0; +} + +struct acpuclk_soc_data acpuclk_7x30_soc_data __initdata = { + .init = acpuclk_7x30_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c new file mode 100644 index 00000000000..a58eb6e2779 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-8960.c @@ -0,0 +1,1619 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acpuclock.h" +#include "pm.h" + +/* + * Source IDs. + * These must be negative to not overlap with the source IDs + * used by the 8x60 local clock driver. + */ +#define PLL_8 0 +#define HFPLL -1 +#define QSB -2 + +/* Mux source selects. */ +#define PRI_SRC_SEL_SEC_SRC 0 +#define PRI_SRC_SEL_HFPLL 1 +#define PRI_SRC_SEL_HFPLL_DIV2 2 +#define SEC_SRC_SEL_QSB 0 +#define SEC_SRC_SEL_AUX 2 + +/* HFPLL registers offsets. */ +#define HFPLL_MODE 0x00 +#define HFPLL_CONFIG_CTL 0x04 +#define HFPLL_L_VAL 0x08 +#define HFPLL_M_VAL 0x0C +#define HFPLL_N_VAL 0x10 +#define HFPLL_DROOP_CTL 0x14 + +/* CP15 L2 indirect addresses. */ +#define L2CPMR_IADDR 0x500 +#define L2CPUCPMR_IADDR 0x501 + +#define STBY_KHZ 1 + +#define HFPLL_LOW_VDD_PLL_L_MAX 0x28 + +#define SECCLKAGD BIT(4) + +/* PTE EFUSE register. */ +#define QFPROM_PTE_EFUSE_ADDR (MSM_QFPROM_BASE + 0x00C0) + +/* Corner type vreg VDD values */ +#define LVL_NONE RPM_VREG_CORNER_NONE +#define LVL_LOW RPM_VREG_CORNER_LOW +#define LVL_NOM RPM_VREG_CORNER_NOMINAL +#define LVL_HIGH RPM_VREG_CORNER_HIGH + +enum scalables { + CPU0 = 0, + CPU1, + CPU2, + CPU3, + L2, + NUM_SCALABLES +}; + +enum vregs { + VREG_CORE, + VREG_MEM, + VREG_DIG, + VREG_HFPLL_A, + VREG_HFPLL_B, + NUM_VREG +}; + +enum hfpll_vdd_levels { + HFPLL_VDD_NONE, + HFPLL_VDD_LOW, + HFPLL_VDD_NOM +}; + +enum pvs { + PVS_SLOW, + PVS_NOM, + PVS_FAST, + PVS_FASTER, + NUM_PVS +}; + +struct vreg { + const char name[15]; + const unsigned int max_vdd; + const int rpm_vreg_voter; + const int rpm_vreg_id; + struct regulator *reg; + unsigned int cur_vdd; +}; + +struct core_speed { + unsigned int khz; + int src; + unsigned int pri_src_sel; + unsigned int sec_src_sel; + unsigned int pll_l_val; +}; + +struct l2_level { + struct core_speed speed; + unsigned int vdd_dig; + unsigned int vdd_mem; + unsigned int bw_level; +}; + +struct acpu_level { + unsigned int use_for_scaling; + struct core_speed speed; + struct l2_level *l2_level; + unsigned int vdd_core; +}; + +struct scalable { + void * __iomem const hfpll_base; + void * __iomem const aux_clk_sel; + const uint32_t l2cpmr_iaddr; + struct core_speed *current_speed; + struct l2_level *l2_vote; + struct vreg vreg[NUM_VREG]; + unsigned int *hfpll_vdd_tbl; +}; + +static unsigned int hfpll_vdd_tbl_8960[] = { + [HFPLL_VDD_NONE] = 0, + [HFPLL_VDD_LOW] = 850000, + [HFPLL_VDD_NOM] = 1050000 +}; + +static unsigned int hfpll_vdd_tbl_8064[] = { + [HFPLL_VDD_NONE] = 0, + [HFPLL_VDD_LOW] = 945000, + [HFPLL_VDD_NOM] = 1050000 +}; + +static unsigned int hfpll_vdd_dig_tbl_8930[] = { + [HFPLL_VDD_NONE] = LVL_NONE, + [HFPLL_VDD_LOW] = LVL_LOW, + [HFPLL_VDD_NOM] = LVL_NOM +}; + +static struct scalable scalable_8960[] = { + [CPU0] = { + .hfpll_base = MSM_HFPLL_BASE + 0x200, + .aux_clk_sel = MSM_ACC0_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait0", 1300000 }, + .vreg[VREG_MEM] = { "krait0_mem", 1150000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_L24 }, + .vreg[VREG_DIG] = { "krait0_dig", 1150000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_S3 }, + .vreg[VREG_HFPLL_A] = { "hfpll0_s8", 2100000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_S8 }, + .vreg[VREG_HFPLL_B] = { "hfpll0_l23", 1800000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_L23 }, + }, + [CPU1] = { + .hfpll_base = MSM_HFPLL_BASE + 0x300, + .aux_clk_sel = MSM_ACC1_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait1", 1300000 }, + .vreg[VREG_MEM] = { "krait1_mem", 1150000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_L24 }, + .vreg[VREG_DIG] = { "krait1_dig", 1150000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_S3 }, + .vreg[VREG_HFPLL_A] = { "hfpll1_s8", 2100000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_S8 }, + .vreg[VREG_HFPLL_B] = { "hfpll1_l23", 1800000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_L23 }, + }, + [L2] = { + .hfpll_base = MSM_HFPLL_BASE + 0x400, + .hfpll_vdd_tbl = hfpll_vdd_tbl_8960, + .aux_clk_sel = MSM_APCS_GCC_BASE + 0x028, + .l2cpmr_iaddr = L2CPMR_IADDR, + .vreg[VREG_HFPLL_A] = { "hfpll_l2_s8", 2100000, + RPM_VREG_VOTER6, + RPM_VREG_ID_PM8921_S8 }, + .vreg[VREG_HFPLL_B] = { "hfpll_l2_l23", 1800000, + RPM_VREG_VOTER6, + RPM_VREG_ID_PM8921_L23 }, + }, +}; + +static DEFINE_MUTEX(driver_lock); +static DEFINE_SPINLOCK(l2_lock); + +static struct scalable scalable_8064[] = { + [CPU0] = { + .hfpll_base = MSM_HFPLL_BASE + 0x200, + .aux_clk_sel = MSM_ACC0_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait0", 1300000 }, + .vreg[VREG_MEM] = { "krait0_mem", 1150000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_L24 }, + .vreg[VREG_DIG] = { "krait0_dig", 1150000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_S3 }, + .vreg[VREG_HFPLL_B] = { "hfpll0", 1800000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8921_LVS7 }, + }, + [CPU1] = { + .hfpll_base = MSM_HFPLL_BASE + 0x240, + .aux_clk_sel = MSM_ACC1_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait1", 1300000 }, + .vreg[VREG_MEM] = { "krait1_mem", 1150000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_L24 }, + .vreg[VREG_DIG] = { "krait1_dig", 1150000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_S3 }, + .vreg[VREG_HFPLL_B] = { "hfpll1", 1800000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8921_LVS7 }, + }, + [CPU2] = { + .hfpll_base = MSM_HFPLL_BASE + 0x280, + .aux_clk_sel = MSM_ACC2_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait2", 1300000 }, + .vreg[VREG_MEM] = { "krait2_mem", 1150000, + RPM_VREG_VOTER4, + RPM_VREG_ID_PM8921_L24 }, + .vreg[VREG_DIG] = { "krait2_dig", 1150000, + RPM_VREG_VOTER4, + RPM_VREG_ID_PM8921_S3 }, + .vreg[VREG_HFPLL_B] = { "hfpll2", 1800000, + RPM_VREG_VOTER4, + RPM_VREG_ID_PM8921_LVS7 }, + }, + [CPU3] = { + .hfpll_base = MSM_HFPLL_BASE + 0x2C0, + .aux_clk_sel = MSM_ACC3_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait3", 1300000 }, + .vreg[VREG_MEM] = { "krait3_mem", 1150000, + RPM_VREG_VOTER5, + RPM_VREG_ID_PM8921_L24 }, + .vreg[VREG_DIG] = { "krait3_dig", 1150000, + RPM_VREG_VOTER5, + RPM_VREG_ID_PM8921_S3 }, + .vreg[VREG_HFPLL_B] = { "hfpll3", 1800000, + RPM_VREG_VOTER5, + RPM_VREG_ID_PM8921_LVS7 }, + }, + [L2] = { + .hfpll_base = MSM_HFPLL_BASE + 0x300, + .hfpll_vdd_tbl = hfpll_vdd_tbl_8064, + .aux_clk_sel = MSM_APCS_GCC_BASE + 0x028, + .l2cpmr_iaddr = L2CPMR_IADDR, + .vreg[VREG_HFPLL_B] = { "hfpll_l2", 1800000, + RPM_VREG_VOTER6, + RPM_VREG_ID_PM8921_LVS7 }, + }, +}; + +static struct scalable scalable_8930[] = { + [CPU0] = { + .hfpll_base = MSM_HFPLL_BASE + 0x200, + .aux_clk_sel = MSM_ACC0_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait0", 1300000 }, + .vreg[VREG_MEM] = { "krait0_mem", 1150000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8038_L24 }, + .vreg[VREG_DIG] = { "krait0_dig", LVL_HIGH, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8038_VDD_DIG_CORNER + }, + .vreg[VREG_HFPLL_B] = { "hfpll0", 1800000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8038_L23 }, + }, + [CPU1] = { + .hfpll_base = MSM_HFPLL_BASE + 0x300, + .aux_clk_sel = MSM_ACC1_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait1", 1300000 }, + .vreg[VREG_MEM] = { "krait1_mem", 1150000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8038_L24 }, + .vreg[VREG_DIG] = { "krait1_dig", LVL_HIGH, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8038_VDD_DIG_CORNER + }, + .vreg[VREG_HFPLL_B] = { "hfpll1", 1800000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8038_L23 }, + }, + [L2] = { + .hfpll_base = MSM_HFPLL_BASE + 0x400, + .hfpll_vdd_tbl = hfpll_vdd_dig_tbl_8930, + .aux_clk_sel = MSM_APCS_GCC_BASE + 0x028, + .l2cpmr_iaddr = L2CPMR_IADDR, + .vreg[VREG_HFPLL_B] = { "hfpll_l2", 1800000, + RPM_VREG_VOTER6, + RPM_VREG_ID_PM8038_L23 }, + }, +}; + +/*TODO: Update the rpm vreg id when the rpm driver is ready */ +static struct scalable scalable_8627[] = { + [CPU0] = { + .hfpll_base = MSM_HFPLL_BASE + 0x200, + .aux_clk_sel = MSM_ACC0_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait0", 1300000 }, + .vreg[VREG_MEM] = { "krait0_mem", 1150000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8038_L24 }, + .vreg[VREG_DIG] = { "krait0_dig", LVL_HIGH, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8038_VDD_DIG_CORNER + }, + .vreg[VREG_HFPLL_B] = { "hfpll0", 1800000, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8038_L23 }, + }, + [CPU1] = { + .hfpll_base = MSM_HFPLL_BASE + 0x300, + .aux_clk_sel = MSM_ACC1_BASE + 0x014, + .l2cpmr_iaddr = L2CPUCPMR_IADDR, + .vreg[VREG_CORE] = { "krait1", 1300000 }, + .vreg[VREG_MEM] = { "krait1_mem", 1150000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8038_L24 }, + .vreg[VREG_DIG] = { "krait1_dig", LVL_HIGH, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8038_VDD_DIG_CORNER + }, + .vreg[VREG_HFPLL_B] = { "hfpll1", 1800000, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8038_L23 }, + }, + [L2] = { + .hfpll_base = MSM_HFPLL_BASE + 0x400, + .hfpll_vdd_tbl = hfpll_vdd_dig_tbl_8930, + .aux_clk_sel = MSM_APCS_GCC_BASE + 0x028, + .l2cpmr_iaddr = L2CPMR_IADDR, + .vreg[VREG_HFPLL_B] = { "hfpll_l2", 1800000, + RPM_VREG_VOTER6, + RPM_VREG_ID_PM8038_L23 }, + }, +}; + +static struct l2_level *l2_freq_tbl; +static struct acpu_level *acpu_freq_tbl; +static int l2_freq_tbl_size; +static struct scalable *scalable; +#define SCALABLE_TO_CPU(sc) ((sc) - scalable) + +/* Instantaneous bandwidth requests in MB/s. */ +#define BW_MBPS(_bw) \ + { \ + .vectors = (struct msm_bus_vectors[]){ \ + {\ + .src = MSM_BUS_MASTER_AMPSS_M0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_bw) * 1000000UL, \ + .ab = (_bw) * 100000UL, \ + }, \ + { \ + .src = MSM_BUS_MASTER_AMPSS_M1, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_bw) * 1000000UL, \ + .ab = (_bw) * 100000UL, \ + }, \ + }, \ + .num_paths = 2, \ + } +static struct msm_bus_paths bw_level_tbl[] = { + [0] = BW_MBPS(640), /* At least 80 MHz on bus. */ + [1] = BW_MBPS(1064), /* At least 133 MHz on bus. */ + [2] = BW_MBPS(1600), /* At least 200 MHz on bus. */ + [3] = BW_MBPS(2128), /* At least 266 MHz on bus. */ + [4] = BW_MBPS(3200), /* At least 400 MHz on bus. */ + [5] = BW_MBPS(3600), /* At least 450 MHz on bus. */ + [6] = BW_MBPS(3936), /* At least 492 MHz on bus. */ + [7] = BW_MBPS(4264), /* At least 533 MHz on bus. */ +}; + +static struct msm_bus_scale_pdata bus_client_pdata = { + .usecase = bw_level_tbl, + .num_usecases = ARRAY_SIZE(bw_level_tbl), + .active_only = 1, + .name = "acpuclock", +}; + +static uint32_t bus_perf_client; + +/* TODO: Update vdd_dig and vdd_mem when voltage data is available. */ +#define L2(x) (&l2_freq_tbl_8960_kraitv1[(x)]) +static struct l2_level l2_freq_tbl_8960_kraitv1[] = { + [0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, 1050000, 1050000, 0 }, + [1] = { { 384000, PLL_8, 0, 2, 0x00 }, 1050000, 1050000, 1 }, + [2] = { { 432000, HFPLL, 2, 0, 0x20 }, 1050000, 1050000, 1 }, + [3] = { { 486000, HFPLL, 2, 0, 0x24 }, 1050000, 1050000, 1 }, + [4] = { { 540000, HFPLL, 2, 0, 0x28 }, 1050000, 1050000, 1 }, + [5] = { { 594000, HFPLL, 1, 0, 0x16 }, 1050000, 1050000, 2 }, + [6] = { { 648000, HFPLL, 1, 0, 0x18 }, 1050000, 1050000, 2 }, + [7] = { { 702000, HFPLL, 1, 0, 0x1A }, 1050000, 1050000, 2 }, + [8] = { { 756000, HFPLL, 1, 0, 0x1C }, 1150000, 1150000, 2 }, + [9] = { { 810000, HFPLL, 1, 0, 0x1E }, 1150000, 1150000, 3 }, + [10] = { { 864000, HFPLL, 1, 0, 0x20 }, 1150000, 1150000, 3 }, + [11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 3 }, +}; + +static struct acpu_level acpu_freq_tbl_8960_kraitv1_slow[] = { + { 0, {STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 }, + { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 925000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 925000 }, + { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 937500 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 962500 }, + { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 987500 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1000000 }, + { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1025000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1062500 }, + { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1062500 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1087500 }, + { 0, { 0 } } +}; + +static struct acpu_level acpu_freq_tbl_8960_kraitv1_nom_fast[] = { + { 0, {STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 862500 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 862500 }, + { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 862500 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 887500 }, + { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 900000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 925000 }, + { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 925000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 937500 }, + { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 962500 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1012500 }, + { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1025000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1025000 }, + { 0, { 0 } } +}; + +#undef L2 +#define L2(x) (&l2_freq_tbl_8960_kraitv2[(x)]) +static struct l2_level l2_freq_tbl_8960_kraitv2[] = { + [0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, 1050000, 1050000, 0 }, + [1] = { { 384000, PLL_8, 0, 2, 0x00 }, 1050000, 1050000, 1 }, + [2] = { { 432000, HFPLL, 2, 0, 0x20 }, 1050000, 1050000, 2 }, + [3] = { { 486000, HFPLL, 2, 0, 0x24 }, 1050000, 1050000, 2 }, + [4] = { { 540000, HFPLL, 2, 0, 0x28 }, 1050000, 1050000, 2 }, + [5] = { { 594000, HFPLL, 1, 0, 0x16 }, 1050000, 1050000, 2 }, + [6] = { { 648000, HFPLL, 1, 0, 0x18 }, 1050000, 1050000, 4 }, + [7] = { { 702000, HFPLL, 1, 0, 0x1A }, 1050000, 1050000, 4 }, + [8] = { { 756000, HFPLL, 1, 0, 0x1C }, 1150000, 1150000, 4 }, + [9] = { { 810000, HFPLL, 1, 0, 0x1E }, 1150000, 1150000, 4 }, + [10] = { { 864000, HFPLL, 1, 0, 0x20 }, 1150000, 1150000, 4 }, + [11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 6 }, + [12] = { { 972000, HFPLL, 1, 0, 0x24 }, 1150000, 1150000, 6 }, + [13] = { { 1026000, HFPLL, 1, 0, 0x26 }, 1150000, 1150000, 6 }, + [14] = { { 1080000, HFPLL, 1, 0, 0x28 }, 1150000, 1150000, 6 }, + [15] = { { 1134000, HFPLL, 1, 0, 0x2A }, 1150000, 1150000, 6 }, + [16] = { { 1188000, HFPLL, 1, 0, 0x2C }, 1150000, 1150000, 6 }, + [17] = { { 1242000, HFPLL, 1, 0, 0x2E }, 1150000, 1150000, 6 }, + [18] = { { 1296000, HFPLL, 1, 0, 0x30 }, 1150000, 1150000, 6 }, + [19] = { { 1350000, HFPLL, 1, 0, 0x32 }, 1150000, 1150000, 6 }, +}; + +static struct acpu_level acpu_freq_tbl_8960_kraitv2_slow[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 950000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 950000 }, + { 0, { 432000, HFPLL, 2, 0, 0x20 }, L2(7), 975000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(7), 975000 }, + { 0, { 540000, HFPLL, 2, 0, 0x28 }, L2(7), 1000000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(7), 1000000 }, + { 0, { 648000, HFPLL, 1, 0, 0x18 }, L2(7), 1025000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(7), 1025000 }, + { 0, { 756000, HFPLL, 1, 0, 0x1C }, L2(7), 1075000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(7), 1075000 }, + { 0, { 864000, HFPLL, 1, 0, 0x20 }, L2(7), 1100000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1100000 }, + { 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1125000 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1125000 }, + { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(19), 1175000 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(19), 1175000 }, + { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(19), 1200000 }, + { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(19), 1200000 }, + { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(19), 1225000 }, + { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(19), 1225000 }, + { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(19), 1237500 }, + { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(19), 1237500 }, + { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(19), 1250000 }, + { 0, { 0 } } +}; + +static struct acpu_level acpu_freq_tbl_8960_kraitv2_nom[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 }, + { 0, { 432000, HFPLL, 2, 0, 0x20 }, L2(7), 925000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(7), 925000 }, + { 0, { 540000, HFPLL, 2, 0, 0x28 }, L2(7), 950000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(7), 950000 }, + { 0, { 648000, HFPLL, 1, 0, 0x18 }, L2(7), 975000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(7), 975000 }, + { 0, { 756000, HFPLL, 1, 0, 0x1C }, L2(7), 1025000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(7), 1025000 }, + { 0, { 864000, HFPLL, 1, 0, 0x20 }, L2(7), 1050000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1050000 }, + { 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1075000 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1075000 }, + { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(19), 1125000 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(19), 1125000 }, + { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(19), 1150000 }, + { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(19), 1150000 }, + { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(19), 1175000 }, + { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(19), 1175000 }, + { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(19), 1187500 }, + { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(19), 1187500 }, + { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(19), 1200000 }, + { 0, { 0 } } +}; + +static struct acpu_level acpu_freq_tbl_8960_kraitv2_fast[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 850000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 850000 }, + { 0, { 432000, HFPLL, 2, 0, 0x20 }, L2(7), 875000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(7), 875000 }, + { 0, { 540000, HFPLL, 2, 0, 0x28 }, L2(7), 900000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(7), 900000 }, + { 0, { 648000, HFPLL, 1, 0, 0x18 }, L2(7), 925000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(7), 925000 }, + { 0, { 756000, HFPLL, 1, 0, 0x1C }, L2(7), 975000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(7), 975000 }, + { 0, { 864000, HFPLL, 1, 0, 0x20 }, L2(7), 1000000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1000000 }, + { 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1025000 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1025000 }, + { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(19), 1075000 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(19), 1075000 }, + { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(19), 1100000 }, + { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(19), 1100000 }, + { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(19), 1125000 }, + { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(19), 1125000 }, + { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(19), 1137500 }, + { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(19), 1137500 }, + { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(19), 1150000 }, + { 0, { 0 } } +}; + +/* TODO: Update vdd_dig and vdd_mem when voltage data is available. */ +#undef L2 +#define L2(x) (&l2_freq_tbl_8064[(x)]) +static struct l2_level l2_freq_tbl_8064[] = { + [0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, 1050000, 1050000, 0 }, + [1] = { { 384000, PLL_8, 0, 2, 0x00 }, 1050000, 1050000, 1 }, + [2] = { { 432000, HFPLL, 2, 0, 0x20 }, 1050000, 1050000, 2 }, + [3] = { { 486000, HFPLL, 2, 0, 0x24 }, 1050000, 1050000, 2 }, + [4] = { { 540000, HFPLL, 2, 0, 0x28 }, 1050000, 1050000, 2 }, + [5] = { { 594000, HFPLL, 1, 0, 0x16 }, 1050000, 1050000, 2 }, + [6] = { { 648000, HFPLL, 1, 0, 0x18 }, 1050000, 1050000, 4 }, + [7] = { { 702000, HFPLL, 1, 0, 0x1A }, 1050000, 1050000, 4 }, + [8] = { { 756000, HFPLL, 1, 0, 0x1C }, 1150000, 1150000, 4 }, + [9] = { { 810000, HFPLL, 1, 0, 0x1E }, 1150000, 1150000, 4 }, + [10] = { { 864000, HFPLL, 1, 0, 0x20 }, 1150000, 1150000, 4 }, + [11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 7 }, + [12] = { { 972000, HFPLL, 1, 0, 0x24 }, 1150000, 1150000, 7 }, + [13] = { { 1026000, HFPLL, 1, 0, 0x26 }, 1150000, 1150000, 7 }, + [14] = { { 1080000, HFPLL, 1, 0, 0x28 }, 1150000, 1150000, 7 }, + [15] = { { 1134000, HFPLL, 1, 0, 0x2A }, 1150000, 1150000, 7 }, +}; + +/* TODO: Update core voltages when data is available. */ +static struct acpu_level acpu_freq_tbl_8064_slow[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 950000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 950000 }, + { 0, { 432000, HFPLL, 2, 0, 0x20 }, L2(7), 975000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(7), 975000 }, + { 0, { 540000, HFPLL, 2, 0, 0x28 }, L2(7), 1000000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(7), 1000000 }, + { 0, { 648000, HFPLL, 1, 0, 0x18 }, L2(7), 1025000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(7), 1025000 }, + { 0, { 756000, HFPLL, 1, 0, 0x1C }, L2(7), 1075000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(7), 1075000 }, + { 0, { 864000, HFPLL, 1, 0, 0x20 }, L2(7), 1100000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1100000 }, + { 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1125000 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1125000 }, + { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(15), 1175000 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(15), 1175000 }, + { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(15), 1200000 }, + { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(15), 1200000 }, + { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(15), 1225000 }, + { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1225000 }, + { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1237500 }, + { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1237500 }, + { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1250000 }, + { 0, { 0 } } +}; + +static struct acpu_level acpu_freq_tbl_8064_nom[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 }, + { 0, { 432000, HFPLL, 2, 0, 0x20 }, L2(7), 925000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(7), 925000 }, + { 0, { 540000, HFPLL, 2, 0, 0x28 }, L2(7), 950000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(7), 950000 }, + { 0, { 648000, HFPLL, 1, 0, 0x18 }, L2(7), 975000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(7), 975000 }, + { 0, { 756000, HFPLL, 1, 0, 0x1C }, L2(7), 1025000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(7), 1025000 }, + { 0, { 864000, HFPLL, 1, 0, 0x20 }, L2(7), 1050000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1050000 }, + { 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1075000 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1075000 }, + { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(15), 1125000 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(15), 1125000 }, + { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(15), 1150000 }, + { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(15), 1150000 }, + { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(15), 1175000 }, + { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1175000 }, + { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1187500 }, + { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1187500 }, + { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1212500 }, + { 0, { 0 } } +}; + +static struct acpu_level acpu_freq_tbl_8064_fast[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 850000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 850000 }, + { 0, { 432000, HFPLL, 2, 0, 0x20 }, L2(7), 875000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(7), 875000 }, + { 0, { 540000, HFPLL, 2, 0, 0x28 }, L2(7), 900000 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(7), 900000 }, + { 0, { 648000, HFPLL, 1, 0, 0x18 }, L2(7), 925000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(7), 925000 }, + { 0, { 756000, HFPLL, 1, 0, 0x1C }, L2(7), 975000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(7), 975000 }, + { 0, { 864000, HFPLL, 1, 0, 0x20 }, L2(7), 1000000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(7), 1000000 }, + { 0, { 972000, HFPLL, 1, 0, 0x24 }, L2(7), 1025000 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(7), 1025000 }, + { 0, { 1080000, HFPLL, 1, 0, 0x28 }, L2(15), 1075000 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(15), 1075000 }, + { 0, { 1188000, HFPLL, 1, 0, 0x2C }, L2(15), 1100000 }, + { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(15), 1100000 }, + { 0, { 1296000, HFPLL, 1, 0, 0x30 }, L2(15), 1125000 }, + { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1125000 }, + { 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1137500 }, + { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1137500 }, + { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1175000 }, + { 0, { 0 } } +}; + +/* TODO: Update vdd_dig, vdd_mem and bw when data is available. */ +#undef L2 +#define L2(x) (&l2_freq_tbl_8930[(x)]) +static struct l2_level l2_freq_tbl_8930[] = { + [0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, LVL_NOM, 1050000, 0 }, + [1] = { { 384000, PLL_8, 0, 2, 0x00 }, LVL_NOM, 1050000, 1 }, + [2] = { { 432000, HFPLL, 2, 0, 0x20 }, LVL_NOM, 1050000, 2 }, + [3] = { { 486000, HFPLL, 2, 0, 0x24 }, LVL_NOM, 1050000, 2 }, + [4] = { { 540000, HFPLL, 2, 0, 0x28 }, LVL_NOM, 1050000, 2 }, + [5] = { { 594000, HFPLL, 1, 0, 0x16 }, LVL_NOM, 1050000, 2 }, + [6] = { { 648000, HFPLL, 1, 0, 0x18 }, LVL_NOM, 1050000, 4 }, + [7] = { { 702000, HFPLL, 1, 0, 0x1A }, LVL_NOM, 1050000, 4 }, + [8] = { { 756000, HFPLL, 1, 0, 0x1C }, LVL_HIGH, 1150000, 4 }, + [9] = { { 810000, HFPLL, 1, 0, 0x1E }, LVL_HIGH, 1150000, 4 }, + [10] = { { 864000, HFPLL, 1, 0, 0x20 }, LVL_HIGH, 1150000, 4 }, + [11] = { { 918000, HFPLL, 1, 0, 0x22 }, LVL_HIGH, 1150000, 7 }, + [12] = { { 972000, HFPLL, 1, 0, 0x24 }, LVL_HIGH, 1150000, 7 }, + [13] = { { 1026000, HFPLL, 1, 0, 0x26 }, LVL_HIGH, 1150000, 7 }, + [14] = { { 1080000, HFPLL, 1, 0, 0x28 }, LVL_HIGH, 1150000, 7 }, + [15] = { { 1134000, HFPLL, 1, 0, 0x2A }, LVL_HIGH, 1150000, 7 }, + [16] = { { 1188000, HFPLL, 1, 0, 0x2C }, LVL_HIGH, 1150000, 7 }, +}; + +/* TODO: Update core voltages when data is available. */ +static struct acpu_level acpu_freq_tbl_8930[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 925000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 925000 }, + { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 937500 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 962500 }, + { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 987500 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 1000000 }, + { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 1025000 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1037500 }, + { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1062500 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1087500 }, + { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1100000 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1125000 }, + { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(16), 1137500 }, + { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(16), 1162500 }, + { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1187500 }, + { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1200000 }, + { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1225000 }, + { 0, { 0 } } +}; + +/* TODO: Update vdd_dig, vdd_mem and bw when data is available. */ +#undef L2 +#define L2(x) (&l2_freq_tbl_8627[(x)]) +static struct l2_level l2_freq_tbl_8627[] = { + [0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, LVL_NOM, 1050000, 0 }, + [1] = { { 384000, PLL_8, 0, 2, 0x00 }, LVL_NOM, 1050000, 1 }, + [2] = { { 432000, HFPLL, 2, 0, 0x20 }, LVL_NOM, 1050000, 1 }, + [3] = { { 486000, HFPLL, 2, 0, 0x24 }, LVL_NOM, 1050000, 1 }, + [4] = { { 540000, HFPLL, 2, 0, 0x28 }, LVL_NOM, 1050000, 2 }, + [5] = { { 594000, HFPLL, 1, 0, 0x16 }, LVL_NOM, 1050000, 2 }, + [6] = { { 648000, HFPLL, 1, 0, 0x18 }, LVL_NOM, 1050000, 2 }, + [7] = { { 702000, HFPLL, 1, 0, 0x1A }, LVL_NOM, 1050000, 3 }, + [8] = { { 756000, HFPLL, 1, 0, 0x1C }, LVL_HIGH, 1150000, 3 }, + [9] = { { 810000, HFPLL, 1, 0, 0x1E }, LVL_HIGH, 1150000, 3 }, + [10] = { { 864000, HFPLL, 1, 0, 0x20 }, LVL_HIGH, 1150000, 4 }, + [11] = { { 918000, HFPLL, 1, 0, 0x22 }, LVL_HIGH, 1150000, 4 }, + [12] = { { 972000, HFPLL, 1, 0, 0x24 }, LVL_HIGH, 1150000, 4 }, +}; + +/* TODO: Update core voltages when data is available. */ +static struct acpu_level acpu_freq_tbl_8627[] = { + { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 }, + { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 }, + { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(5), 925000 }, + { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(5), 925000 }, + { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(5), 937500 }, + { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(5), 962500 }, + { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(9), 987500 }, + { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(9), 1000000 }, + { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(9), 1025000 }, + { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(9), 1062500 }, + { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(12), 1062500 }, + { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(12), 1087500 }, + { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(12), 1100000 }, + { 0, { 0 } } +}; + +static struct acpu_level *acpu_freq_tbl_8960_v1[NUM_PVS] __initdata = { + [PVS_SLOW] = acpu_freq_tbl_8960_kraitv1_slow, + [PVS_NOM] = acpu_freq_tbl_8960_kraitv1_nom_fast, + [PVS_FAST] = acpu_freq_tbl_8960_kraitv1_nom_fast, +}; + +static struct acpu_level *acpu_freq_tbl_8960_v2[NUM_PVS] __initdata = { + [PVS_SLOW] = acpu_freq_tbl_8960_kraitv2_slow, + [PVS_NOM] = acpu_freq_tbl_8960_kraitv2_nom, + [PVS_FAST] = acpu_freq_tbl_8960_kraitv2_fast, +}; + +/* TODO: update the faster table when data is available */ +static struct acpu_level *acpu_freq_tbl_8064[NUM_PVS] __initdata = { + [PVS_SLOW] = acpu_freq_tbl_8064_slow, + [PVS_NOM] = acpu_freq_tbl_8064_nom, + [PVS_FAST] = acpu_freq_tbl_8064_fast, + [PVS_FASTER] = acpu_freq_tbl_8064_fast, +}; + +static unsigned long acpuclk_8960_get_rate(int cpu) +{ + return scalable[cpu].current_speed->khz; +} + +/* Get the selected source on primary MUX. */ +static int get_pri_clk_src(struct scalable *sc) +{ + uint32_t regval; + + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + return regval & 0x3; +} + +/* Set the selected source on primary MUX. */ +static void set_pri_clk_src(struct scalable *sc, uint32_t pri_src_sel) +{ + uint32_t regval; + + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + regval &= ~0x3; + regval |= (pri_src_sel & 0x3); + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + /* Wait for switch to complete. */ + mb(); + udelay(1); +} + +/* Get the selected source on secondary MUX. */ +static int get_sec_clk_src(struct scalable *sc) +{ + uint32_t regval; + + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + return (regval >> 2) & 0x3; +} + +/* Set the selected source on secondary MUX. */ +static void set_sec_clk_src(struct scalable *sc, uint32_t sec_src_sel) +{ + uint32_t regval; + + /* Disable secondary source clock gating during switch. */ + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + regval |= SECCLKAGD; + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + + /* Program the MUX. */ + regval &= ~(0x3 << 2); + regval |= ((sec_src_sel & 0x3) << 2); + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + + /* Wait for switch to complete. */ + mb(); + udelay(1); + + /* Re-enable secondary source clock gating. */ + regval &= ~SECCLKAGD; + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); +} + +/* Enable an already-configured HFPLL. */ +static void hfpll_enable(struct scalable *sc, bool skip_regulators) +{ + int rc; + + if (!skip_regulators) { + if (cpu_is_msm8960()) { + rc = rpm_vreg_set_voltage( + sc->vreg[VREG_HFPLL_A].rpm_vreg_id, + sc->vreg[VREG_HFPLL_A].rpm_vreg_voter, + 2050000, + sc->vreg[VREG_HFPLL_A].max_vdd, 0); + if (rc) + pr_err("%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_A].name, rc); + } + rc = rpm_vreg_set_voltage(sc->vreg[VREG_HFPLL_B].rpm_vreg_id, + sc->vreg[VREG_HFPLL_B].rpm_vreg_voter, 1800000, + sc->vreg[VREG_HFPLL_B].max_vdd, 0); + if (rc) + pr_err("%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_B].name, rc); + } + /* Disable PLL bypass mode. */ + writel_relaxed(0x2, sc->hfpll_base + HFPLL_MODE); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + mb(); + udelay(10); + + /* De-assert active-low PLL reset. */ + writel_relaxed(0x6, sc->hfpll_base + HFPLL_MODE); + + /* Wait for PLL to lock. */ + mb(); + udelay(60); + + /* Enable PLL output. */ + writel_relaxed(0x7, sc->hfpll_base + HFPLL_MODE); +} + +/* Disable a HFPLL for power-savings or while its being reprogrammed. */ +static void hfpll_disable(struct scalable *sc, bool skip_regulators) +{ + int rc; + + /* + * Disable the PLL output, disable test mode, enable + * the bypass mode, and assert the reset. + */ + writel_relaxed(0, sc->hfpll_base + HFPLL_MODE); + + if (!skip_regulators) { + rc = rpm_vreg_set_voltage(sc->vreg[VREG_HFPLL_B].rpm_vreg_id, + sc->vreg[VREG_HFPLL_B].rpm_vreg_voter, 0, + 0, 0); + if (rc) + pr_err("%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_B].name, rc); + + if (cpu_is_msm8960()) { + rc = rpm_vreg_set_voltage( + sc->vreg[VREG_HFPLL_A].rpm_vreg_id, + sc->vreg[VREG_HFPLL_A].rpm_vreg_voter, + 0, 0, 0); + if (rc) + pr_err("%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_A].name, rc); + } + } +} + +/* Program the HFPLL rate. Assumes HFPLL is already disabled. */ +static void hfpll_set_rate(struct scalable *sc, struct core_speed *tgt_s) +{ + writel_relaxed(tgt_s->pll_l_val, sc->hfpll_base + HFPLL_L_VAL); +} + +/* Return the L2 speed that should be applied. */ +static struct l2_level *compute_l2_level(struct scalable *sc, + struct l2_level *vote_l) +{ + struct l2_level *new_l; + int cpu; + + /* Bounds check. */ + BUG_ON(vote_l >= (l2_freq_tbl + l2_freq_tbl_size)); + + /* Find max L2 speed vote. */ + sc->l2_vote = vote_l; + new_l = l2_freq_tbl; + for_each_present_cpu(cpu) + new_l = max(new_l, scalable[cpu].l2_vote); + + return new_l; +} + +/* Update the bus bandwidth request. */ +static void set_bus_bw(unsigned int bw) +{ + int ret; + + /* Bounds check. */ + if (bw >= ARRAY_SIZE(bw_level_tbl)) { + pr_err("invalid bandwidth request (%d)\n", bw); + return; + } + + /* Update bandwidth if request has changed. This may sleep. */ + ret = msm_bus_scale_client_update_request(bus_perf_client, bw); + if (ret) + pr_err("bandwidth request failed (%d)\n", ret); +} + +/* Set the CPU or L2 clock speed. */ +static void set_speed(struct scalable *sc, struct core_speed *tgt_s, + enum setrate_reason reason) +{ + struct core_speed *strt_s = sc->current_speed; + + if (tgt_s == strt_s) + return; + + if (strt_s->src == HFPLL && tgt_s->src == HFPLL) { + /* + * Move to an always-on source running at a frequency that does + * not require an elevated CPU voltage. PLL8 is used here. + */ + set_sec_clk_src(sc, SEC_SRC_SEL_AUX); + set_pri_clk_src(sc, PRI_SRC_SEL_SEC_SRC); + + /* Program CPU HFPLL. */ + hfpll_disable(sc, 1); + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 1); + + /* Move CPU to HFPLL source. */ + set_pri_clk_src(sc, tgt_s->pri_src_sel); + } else if (strt_s->src == HFPLL && tgt_s->src != HFPLL) { + /* + * If responding to CPU_DEAD we must be running on another CPU. + * Therefore, we can't access the downed CPU's clock MUX CP15 + * registers from here and can't change clock sources. If the + * CPU is collapsed, however, it is still safe to turn off the + * PLL without switching the MUX away from it. + */ + if (reason != SETRATE_HOTPLUG || sc == &scalable[L2]) { + set_sec_clk_src(sc, tgt_s->sec_src_sel); + set_pri_clk_src(sc, tgt_s->pri_src_sel); + hfpll_disable(sc, 0); + } else if (reason == SETRATE_HOTPLUG + && msm_pm_verify_cpu_pc(SCALABLE_TO_CPU(sc))) { + hfpll_disable(sc, 0); + } + } else if (strt_s->src != HFPLL && tgt_s->src == HFPLL) { + /* + * If responding to CPU_UP_PREPARE, we can't change CP15 + * registers for the CPU that's coming up since we're not + * running on that CPU. That's okay though, since the MUX + * source was not changed on the way down, either. + */ + if (reason != SETRATE_HOTPLUG || sc == &scalable[L2]) { + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 0); + set_pri_clk_src(sc, tgt_s->pri_src_sel); + } else if (reason == SETRATE_HOTPLUG + && msm_pm_verify_cpu_pc(SCALABLE_TO_CPU(sc))) { + /* PLL was disabled during hot-unplug. Re-enable it. */ + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 0); + } + } else { + if (reason != SETRATE_HOTPLUG || sc == &scalable[L2]) + set_sec_clk_src(sc, tgt_s->sec_src_sel); + } + + sc->current_speed = tgt_s; +} + +/* Apply any per-cpu voltage increases. */ +static int increase_vdd(int cpu, unsigned int vdd_core, unsigned int vdd_mem, + unsigned int vdd_dig, enum setrate_reason reason) +{ + struct scalable *sc = &scalable[cpu]; + int rc = 0; + + /* + * Increase vdd_mem active-set before vdd_dig. + * vdd_mem should be >= vdd_dig. + */ + if (vdd_mem > sc->vreg[VREG_MEM].cur_vdd) { + rc = rpm_vreg_set_voltage(sc->vreg[VREG_MEM].rpm_vreg_id, + sc->vreg[VREG_MEM].rpm_vreg_voter, vdd_mem, + sc->vreg[VREG_MEM].max_vdd, 0); + if (rc) { + pr_err("%s increase failed (%d)\n", + sc->vreg[VREG_MEM].name, rc); + return rc; + } + sc->vreg[VREG_MEM].cur_vdd = vdd_mem; + } + + /* Increase vdd_dig active-set vote. */ + if (vdd_dig > sc->vreg[VREG_DIG].cur_vdd) { + rc = rpm_vreg_set_voltage(sc->vreg[VREG_DIG].rpm_vreg_id, + sc->vreg[VREG_DIG].rpm_vreg_voter, vdd_dig, + sc->vreg[VREG_DIG].max_vdd, 0); + if (rc) { + pr_err("%s increase failed (%d)\n", + sc->vreg[VREG_DIG].name, rc); + return rc; + } + sc->vreg[VREG_DIG].cur_vdd = vdd_dig; + } + + /* + * Update per-CPU core voltage. Don't do this for the hotplug path for + * which it should already be correct. Attempting to set it is bad + * because we don't know what CPU we are running on at this point, but + * the CPU regulator API requires we call it from the affected CPU. + */ + if (vdd_core > sc->vreg[VREG_CORE].cur_vdd + && reason != SETRATE_HOTPLUG) { + rc = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core, + sc->vreg[VREG_CORE].max_vdd); + if (rc) { + pr_err("%s increase failed (%d)\n", + sc->vreg[VREG_CORE].name, rc); + return rc; + } + sc->vreg[VREG_CORE].cur_vdd = vdd_core; + } + + return rc; +} + +/* Apply any per-cpu voltage decreases. */ +static void decrease_vdd(int cpu, unsigned int vdd_core, unsigned int vdd_mem, + unsigned int vdd_dig, enum setrate_reason reason) +{ + struct scalable *sc = &scalable[cpu]; + int ret; + + /* + * Update per-CPU core voltage. This must be called on the CPU + * that's being affected. Don't do this in the hotplug remove path, + * where the rail is off and we're executing on the other CPU. + */ + if (vdd_core < sc->vreg[VREG_CORE].cur_vdd + && reason != SETRATE_HOTPLUG) { + ret = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core, + sc->vreg[VREG_CORE].max_vdd); + if (ret) { + pr_err("%s decrease failed (%d)\n", + sc->vreg[VREG_CORE].name, ret); + return; + } + sc->vreg[VREG_CORE].cur_vdd = vdd_core; + } + + /* Decrease vdd_dig active-set vote. */ + if (vdd_dig < sc->vreg[VREG_DIG].cur_vdd) { + ret = rpm_vreg_set_voltage(sc->vreg[VREG_DIG].rpm_vreg_id, + sc->vreg[VREG_DIG].rpm_vreg_voter, vdd_dig, + sc->vreg[VREG_DIG].max_vdd, 0); + if (ret) { + pr_err("%s decrease failed (%d)\n", + sc->vreg[VREG_DIG].name, ret); + return; + } + sc->vreg[VREG_DIG].cur_vdd = vdd_dig; + } + + /* + * Decrease vdd_mem active-set after vdd_dig. + * vdd_mem should be >= vdd_dig. + */ + if (vdd_mem < sc->vreg[VREG_MEM].cur_vdd) { + ret = rpm_vreg_set_voltage(sc->vreg[VREG_MEM].rpm_vreg_id, + sc->vreg[VREG_MEM].rpm_vreg_voter, vdd_mem, + sc->vreg[VREG_MEM].max_vdd, 0); + if (ret) { + pr_err("%s decrease failed (%d)\n", + sc->vreg[VREG_MEM].name, ret); + return; + } + sc->vreg[VREG_MEM].cur_vdd = vdd_mem; + } +} + +static unsigned int calculate_vdd_mem(struct acpu_level *tgt) +{ + return tgt->l2_level->vdd_mem; +} + +static unsigned int calculate_vdd_dig(struct acpu_level *tgt) +{ + unsigned int pll_vdd_dig; + + if (tgt->l2_level->speed.src != HFPLL) + pll_vdd_dig = scalable[L2].hfpll_vdd_tbl[HFPLL_VDD_NONE]; + else if (tgt->l2_level->speed.pll_l_val > HFPLL_LOW_VDD_PLL_L_MAX) + pll_vdd_dig = scalable[L2].hfpll_vdd_tbl[HFPLL_VDD_NOM]; + else + pll_vdd_dig = scalable[L2].hfpll_vdd_tbl[HFPLL_VDD_LOW]; + + return max(tgt->l2_level->vdd_dig, pll_vdd_dig); +} + +static unsigned int calculate_vdd_core(struct acpu_level *tgt) +{ + return tgt->vdd_core; +} + +/* Set the CPU's clock rate and adjust the L2 rate, if appropriate. */ +static int acpuclk_8960_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + struct core_speed *strt_acpu_s, *tgt_acpu_s; + struct l2_level *tgt_l2_l; + struct acpu_level *tgt; + unsigned int vdd_mem, vdd_dig, vdd_core; + unsigned long flags; + int rc = 0; + + if (cpu > num_possible_cpus()) { + rc = -EINVAL; + goto out; + } + + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) + mutex_lock(&driver_lock); + + strt_acpu_s = scalable[cpu].current_speed; + + /* Return early if rate didn't change. */ + if (rate == strt_acpu_s->khz) + goto out; + + /* Find target frequency. */ + for (tgt = acpu_freq_tbl; tgt->speed.khz != 0; tgt++) { + if (tgt->speed.khz == rate) { + tgt_acpu_s = &tgt->speed; + break; + } + } + if (tgt->speed.khz == 0) { + rc = -EINVAL; + goto out; + } + + /* Calculate voltage requirements for the current CPU. */ + vdd_mem = calculate_vdd_mem(tgt); + vdd_dig = calculate_vdd_dig(tgt); + vdd_core = calculate_vdd_core(tgt); + + /* Increase VDD levels if needed. */ + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) { + rc = increase_vdd(cpu, vdd_core, vdd_mem, vdd_dig, reason); + if (rc) + goto out; + } + + pr_debug("Switching from ACPU%d rate %u KHz -> %u KHz\n", + cpu, strt_acpu_s->khz, tgt_acpu_s->khz); + + /* Set the CPU speed. */ + set_speed(&scalable[cpu], tgt_acpu_s, reason); + + /* + * Update the L2 vote and apply the rate change. A spinlock is + * necessary to ensure L2 rate is calulated and set atomically, + * even if acpuclk_8960_set_rate() is called from an atomic context + * and the driver_lock mutex is not acquired. + */ + spin_lock_irqsave(&l2_lock, flags); + tgt_l2_l = compute_l2_level(&scalable[cpu], tgt->l2_level); + set_speed(&scalable[L2], &tgt_l2_l->speed, reason); + spin_unlock_irqrestore(&l2_lock, flags); + + /* Nothing else to do for power collapse or SWFI. */ + if (reason == SETRATE_PC || reason == SETRATE_SWFI) + goto out; + + /* Update bus bandwith request. */ + set_bus_bw(tgt_l2_l->bw_level); + + /* Drop VDD levels if we can. */ + decrease_vdd(cpu, vdd_core, vdd_mem, vdd_dig, reason); + + pr_debug("ACPU%d speed change complete\n", cpu); + +out: + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) + mutex_unlock(&driver_lock); + return rc; +} + +/* Initialize a HFPLL at a given rate and enable it. */ +static void __init hfpll_init(struct scalable *sc, struct core_speed *tgt_s) +{ + pr_debug("Initializing HFPLL%d\n", sc - scalable); + + /* Disable the PLL for re-programming. */ + hfpll_disable(sc, 1); + + /* Configure PLL parameters for integer mode. */ + writel_relaxed(0x7845C665, sc->hfpll_base + HFPLL_CONFIG_CTL); + writel_relaxed(0, sc->hfpll_base + HFPLL_M_VAL); + writel_relaxed(1, sc->hfpll_base + HFPLL_N_VAL); + + /* Program droop controller. */ + writel_relaxed(0x0108C000, sc->hfpll_base + HFPLL_DROOP_CTL); + + /* Set an initial rate and enable the PLL. */ + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 0); +} + +/* Voltage regulator initialization. */ +static void __init regulator_init(struct acpu_level *lvl) +{ + int cpu, ret; + struct scalable *sc; + unsigned int vdd_mem, vdd_dig, vdd_core; + + vdd_mem = calculate_vdd_mem(lvl); + vdd_dig = calculate_vdd_dig(lvl); + + for_each_possible_cpu(cpu) { + sc = &scalable[cpu]; + + /* Set initial vdd_mem vote. */ + ret = rpm_vreg_set_voltage(sc->vreg[VREG_MEM].rpm_vreg_id, + sc->vreg[VREG_MEM].rpm_vreg_voter, vdd_mem, + sc->vreg[VREG_MEM].max_vdd, 0); + if (ret) { + pr_err("%s initialization failed (%d)\n", + sc->vreg[VREG_MEM].name, ret); + BUG(); + } + sc->vreg[VREG_MEM].cur_vdd = vdd_mem; + + /* Set initial vdd_dig vote. */ + ret = rpm_vreg_set_voltage(sc->vreg[VREG_DIG].rpm_vreg_id, + sc->vreg[VREG_DIG].rpm_vreg_voter, vdd_dig, + sc->vreg[VREG_DIG].max_vdd, 0); + if (ret) { + pr_err("%s initialization failed (%d)\n", + sc->vreg[VREG_DIG].name, ret); + BUG(); + } + sc->vreg[VREG_DIG].cur_vdd = vdd_dig; + + /* Setup Krait CPU regulators and initial core voltage. */ + sc->vreg[VREG_CORE].reg = regulator_get(NULL, + sc->vreg[VREG_CORE].name); + if (IS_ERR(sc->vreg[VREG_CORE].reg)) { + pr_err("regulator_get(%s) failed (%ld)\n", + sc->vreg[VREG_CORE].name, + PTR_ERR(sc->vreg[VREG_CORE].reg)); + BUG(); + } + vdd_core = calculate_vdd_core(lvl); + ret = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core, + sc->vreg[VREG_CORE].max_vdd); + if (ret) { + pr_err("%s initialization failed (%d)\n", + sc->vreg[VREG_CORE].name, ret); + BUG(); + } + sc->vreg[VREG_CORE].cur_vdd = vdd_core; + ret = regulator_enable(sc->vreg[VREG_CORE].reg); + if (ret) { + pr_err("regulator_enable(%s) failed (%d)\n", + sc->vreg[VREG_CORE].name, ret); + BUG(); + } + } +} + +/* Set initial rate for a given core. */ +static void __init init_clock_sources(struct scalable *sc, + struct core_speed *tgt_s) +{ + uint32_t regval; + + /* Select PLL8 as AUX source input to the secondary MUX. */ + writel_relaxed(0x3, sc->aux_clk_sel); + + /* Switch away from the HFPLL while it's re-initialized. */ + set_sec_clk_src(sc, SEC_SRC_SEL_AUX); + set_pri_clk_src(sc, PRI_SRC_SEL_SEC_SRC); + hfpll_init(sc, tgt_s); + + /* Set PRI_SRC_SEL_HFPLL_DIV2 divider to div-2. */ + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + regval &= ~(0x3 << 6); + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + + /* Switch to the target clock source. */ + set_sec_clk_src(sc, tgt_s->sec_src_sel); + set_pri_clk_src(sc, tgt_s->pri_src_sel); + sc->current_speed = tgt_s; +} + +static void __init per_cpu_init(void *data) +{ + struct acpu_level *max_acpu_level = data; + int cpu = smp_processor_id(); + + init_clock_sources(&scalable[cpu], &max_acpu_level->speed); + scalable[cpu].l2_vote = max_acpu_level->l2_level; +} + +/* Register with bus driver. */ +static void __init bus_init(unsigned int init_bw) +{ + int ret; + + bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata); + if (!bus_perf_client) { + pr_err("unable to register bus client\n"); + BUG(); + } + + ret = msm_bus_scale_client_update_request(bus_perf_client, init_bw); + if (ret) + pr_err("initial bandwidth request failed (%d)\n", ret); +} + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[NR_CPUS][30]; + +static void __init cpufreq_table_init(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + int i, freq_cnt = 0; + /* Construct the freq_table tables from acpu_freq_tbl. */ + for (i = 0; acpu_freq_tbl[i].speed.khz != 0 + && freq_cnt < ARRAY_SIZE(*freq_table); i++) { + if (acpu_freq_tbl[i].use_for_scaling) { + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency + = acpu_freq_tbl[i].speed.khz; + freq_cnt++; + } + } + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].speed.khz != 0); + + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency = CPUFREQ_TABLE_END; + + pr_info("CPU%d: %d scaling frequencies supported.\n", + cpu, freq_cnt); + + /* Register table with CPUFreq. */ + cpufreq_frequency_table_get_attr(freq_table[cpu], cpu); + } +} +#else +static void __init cpufreq_table_init(void) {} +#endif + +#define HOT_UNPLUG_KHZ STBY_KHZ +static int __cpuinit acpuclock_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + static int prev_khz[NR_CPUS]; + static int prev_pri_src[NR_CPUS]; + static int prev_sec_src[NR_CPUS]; + int cpu = (int)hcpu; + + switch (action) { + case CPU_DYING: + case CPU_DYING_FROZEN: + /* + * On Krait v1 and 8064v1, the primary and secondary muxes must + * be set to QSB before L2 power collapse and restored after. + */ + if (cpu_is_krait_v1() || cpu_is_apq8064()) { + prev_sec_src[cpu] = get_sec_clk_src(&scalable[cpu]); + prev_pri_src[cpu] = get_pri_clk_src(&scalable[cpu]); + set_sec_clk_src(&scalable[cpu], SEC_SRC_SEL_QSB); + set_pri_clk_src(&scalable[cpu], PRI_SRC_SEL_SEC_SRC); + } + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + prev_khz[cpu] = acpuclk_8960_get_rate(cpu); + /* Fall through. */ + case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: + acpuclk_8960_set_rate(cpu, HOT_UNPLUG_KHZ, SETRATE_HOTPLUG); + break; + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + if (WARN_ON(!prev_khz[cpu])) + return NOTIFY_BAD; + acpuclk_8960_set_rate(cpu, prev_khz[cpu], SETRATE_HOTPLUG); + break; + case CPU_STARTING: + case CPU_STARTING_FROZEN: + if (cpu_is_krait_v1() || cpu_is_apq8064()) { + set_sec_clk_src(&scalable[cpu], prev_sec_src[cpu]); + set_pri_clk_src(&scalable[cpu], prev_pri_src[cpu]); + } + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata acpuclock_cpu_notifier = { + .notifier_call = acpuclock_cpu_callback, +}; + +static const int krait_needs_vmin(void) +{ + switch (read_cpuid_id()) { + case 0x511F04D0: + case 0x511F04D1: + case 0x510F06F0: + return 1; + default: + return 0; + }; +} + +static void kraitv2_apply_vmin(struct acpu_level *tbl) +{ + for (; tbl->speed.khz != 0; tbl++) + if (tbl->vdd_core < 1150000) + tbl->vdd_core = 1150000; +} + +static enum pvs __init get_pvs(void) +{ + uint32_t pte_efuse, pvs; + + pte_efuse = readl_relaxed(QFPROM_PTE_EFUSE_ADDR); + pvs = (pte_efuse >> 10) & 0x7; + if (pvs == 0x7) + pvs = (pte_efuse >> 13) & 0x7; + + switch (pvs) { + case 0x0: + case 0x7: + pr_info("ACPU PVS: Slow\n"); + return PVS_SLOW; + case 0x1: + pr_info("ACPU PVS: Nominal\n"); + return PVS_NOM; + case 0x3: + pr_info("ACPU PVS: Fast\n"); + return PVS_FAST; + case 0x4: + if (cpu_is_apq8064()) { + pr_info("ACPU PVS: Faster\n"); + return PVS_FASTER; + } + default: + pr_warn("ACPU PVS: Unknown. Defaulting to slow\n"); + return PVS_SLOW; + } +} + +static struct acpu_level * __init select_freq_plan(void) +{ + struct acpu_level *l, *max_acpu_level = NULL; + + /* Select frequency tables. */ + if (cpu_is_msm8960()) { + enum pvs pvs_id = get_pvs(); + + scalable = scalable_8960; + if (cpu_is_krait_v1()) { + acpu_freq_tbl = acpu_freq_tbl_8960_v1[pvs_id]; + l2_freq_tbl = l2_freq_tbl_8960_kraitv1; + l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8960_kraitv1); + } else { + acpu_freq_tbl = acpu_freq_tbl_8960_v2[pvs_id]; + l2_freq_tbl = l2_freq_tbl_8960_kraitv2; + l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8960_kraitv2); + } + } else if (cpu_is_apq8064()) { + enum pvs pvs_id = get_pvs(); + + scalable = scalable_8064; + acpu_freq_tbl = acpu_freq_tbl_8064[pvs_id]; + l2_freq_tbl = l2_freq_tbl_8064; + l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8064); + } else if (cpu_is_msm8627()) { + scalable = scalable_8627; + acpu_freq_tbl = acpu_freq_tbl_8627; + l2_freq_tbl = l2_freq_tbl_8627; + l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8627); + } else if (cpu_is_msm8930()) { + scalable = scalable_8930; + acpu_freq_tbl = acpu_freq_tbl_8930; + l2_freq_tbl = l2_freq_tbl_8930; + l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8930); + } else { + BUG(); + } + BUG_ON(!acpu_freq_tbl); + if (krait_needs_vmin()) + kraitv2_apply_vmin(acpu_freq_tbl); + + /* Find the max supported scaling frequency. */ + for (l = acpu_freq_tbl; l->speed.khz != 0; l++) + if (l->use_for_scaling) + max_acpu_level = l; + BUG_ON(!max_acpu_level); + pr_info("Max ACPU freq: %u KHz\n", max_acpu_level->speed.khz); + + return max_acpu_level; +} + +static struct acpuclk_data acpuclk_8960_data = { + .set_rate = acpuclk_8960_set_rate, + .get_rate = acpuclk_8960_get_rate, + .power_collapse_khz = STBY_KHZ, + .wait_for_irq_khz = STBY_KHZ, +}; + +static int __init acpuclk_8960_init(struct acpuclk_soc_data *soc_data) +{ + struct acpu_level *max_acpu_level = select_freq_plan(); + + regulator_init(max_acpu_level); + bus_init(max_acpu_level->l2_level->bw_level); + + init_clock_sources(&scalable[L2], &max_acpu_level->l2_level->speed); + on_each_cpu(per_cpu_init, max_acpu_level, true); + + cpufreq_table_init(); + + acpuclk_register(&acpuclk_8960_data); + register_hotcpu_notifier(&acpuclock_cpu_notifier); + + return 0; +} + +struct acpuclk_soc_data acpuclk_8960_soc_data __initdata = { + .init = acpuclk_8960_init, +}; + +struct acpuclk_soc_data acpuclk_8930_soc_data __initdata = { + .init = acpuclk_8960_init, +}; + +struct acpuclk_soc_data acpuclk_8064_soc_data __initdata = { + .init = acpuclk_8960_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-8x50.c b/arch/arm/mach-msm/acpuclock-8x50.c new file mode 100644 index 00000000000..cde5a144ad1 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-8x50.c @@ -0,0 +1,741 @@ +/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "acpuclock.h" +#include "avs.h" + +#define SHOT_SWITCH 4 +#define HOP_SWITCH 5 +#define SIMPLE_SLEW 6 +#define COMPLEX_SLEW 7 + +#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) +#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) + +/* Scorpion PLL registers */ +#define SCPLL_CTL_ADDR (MSM_SCPLL_BASE + 0x4) +#define SCPLL_STATUS_ADDR (MSM_SCPLL_BASE + 0x18) +#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10) + +#ifdef CONFIG_QSD_SVS +#define TPS65023_MAX_DCDC1 1600 +#else +#define TPS65023_MAX_DCDC1 CONFIG_QSD_PMIC_DEFAULT_DCDC1 +#endif + +enum { + ACPU_PLL_TCXO = -1, + ACPU_PLL_0 = 0, + ACPU_PLL_1, + ACPU_PLL_2, + ACPU_PLL_3, + ACPU_PLL_END, +}; + +struct clkctl_acpu_speed { + unsigned int use_for_scaling; + unsigned int acpuclk_khz; + int pll; + unsigned int acpuclk_src_sel; + unsigned int acpuclk_src_div; + unsigned int ahbclk_khz; + unsigned int ahbclk_div; + unsigned int axiclk_khz; + unsigned int sc_core_src_sel_mask; + unsigned int sc_l_value; + int vdd; + unsigned long lpj; /* loops_per_jiffy */ +}; + +struct clkctl_acpu_speed acpu_freq_tbl_998[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 0, 0, 14000, 0, 0, 1000}, + { 0, 128000, ACPU_PLL_1, 1, 5, 0, 0, 14000, 2, 0, 1000}, + { 1, 245760, ACPU_PLL_0, 4, 0, 0, 0, 29000, 0, 0, 1000}, + /* Update AXI_S and PLL0_S macros if above row numbers change. */ + { 1, 384000, ACPU_PLL_3, 0, 0, 0, 0, 58000, 1, 0xA, 1000}, + { 0, 422400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xB, 1000}, + { 0, 460800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xC, 1000}, + { 0, 499200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xD, 1050}, + { 0, 537600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xE, 1050}, + { 1, 576000, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xF, 1050}, + { 0, 614400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x10, 1075}, + { 0, 652800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x11, 1100}, + { 0, 691200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x12, 1125}, + { 0, 729600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x13, 1150}, + { 1, 768000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x14, 1150}, + { 0, 806400, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x15, 1175}, + { 0, 844800, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x16, 1225}, + { 0, 883200, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x17, 1250}, + { 0, 921600, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x18, 1300}, + { 0, 960000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x19, 1300}, + { 1, 998400, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x1A, 1300}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +struct clkctl_acpu_speed acpu_freq_tbl_768[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 0, 0, 14000, 0, 0, 1000}, + { 0, 128000, ACPU_PLL_1, 1, 5, 0, 0, 14000, 2, 0, 1000}, + { 1, 245760, ACPU_PLL_0, 4, 0, 0, 0, 29000, 0, 0, 1000}, + /* Update AXI_S and PLL0_S macros if above row numbers change. */ + { 1, 384000, ACPU_PLL_3, 0, 0, 0, 0, 58000, 1, 0xA, 1075}, + { 0, 422400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xB, 1100}, + { 0, 460800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xC, 1125}, + { 0, 499200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xD, 1150}, + { 0, 537600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xE, 1150}, + { 1, 576000, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xF, 1150}, + { 0, 614400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x10, 1175}, + { 0, 652800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x11, 1200}, + { 0, 691200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x12, 1225}, + { 0, 729600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x13, 1250}, + { 1, 768000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x14, 1250}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +static struct clkctl_acpu_speed *acpu_freq_tbl = acpu_freq_tbl_998; +#define AXI_S (&acpu_freq_tbl[1]) +#define PLL0_S (&acpu_freq_tbl[2]) + +/* Use 128MHz for PC since ACPU will auto-switch to AXI (128MHz) before + * coming back up. This allows detection of return-from-PC, since 128MHz + * is only used for power collapse. */ +#define POWER_COLLAPSE_KHZ 128000 +/* Use 245MHz (not 128MHz) for SWFI to avoid unnecessary steps between + * 128MHz<->245MHz. Jumping to high frequencies from 128MHz directly + * is not allowed. */ +#define WAIT_FOR_IRQ_KHZ 245760 + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[20]; + +static void __init cpufreq_table_init(void) +{ + unsigned int i; + unsigned int freq_cnt = 0; + + /* Construct the freq_table table from acpu_freq_tbl since the + * freq_table values need to match frequencies specified in + * acpu_freq_tbl and acpu_freq_tbl needs to be fixed up during init. + */ + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0 + && freq_cnt < ARRAY_SIZE(freq_table)-1; i++) { + if (acpu_freq_tbl[i].use_for_scaling) { + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency + = acpu_freq_tbl[i].acpuclk_khz; + freq_cnt++; + } + } + + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].acpuclk_khz != 0); + + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END; + + pr_info("%d scaling frequencies supported.\n", freq_cnt); +} +#endif + +struct clock_state { + struct clkctl_acpu_speed *current_speed; + struct mutex lock; + struct clk *ebi1_clk; + int (*acpu_set_vdd) (int mvolts); +}; + +static struct clock_state drv_state = { 0 }; + +static void scpll_set_freq(uint32_t lval, unsigned freq_switch) +{ + uint32_t regval; + + if (lval > 33) + lval = 33; + if (lval < 10) + lval = 10; + + /* wait for any calibrations or frequency switches to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x3) + ; + + /* write the new L val and switch mode */ + regval = readl(SCPLL_FSM_CTL_EXT_ADDR); + regval &= ~(0x3f << 3); + regval |= (lval << 3); + if (freq_switch == SIMPLE_SLEW) + regval |= (0x1 << 9); + + regval &= ~(0x3 << 0); + regval |= (freq_switch << 0); + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + dmb(); + + /* put in normal mode */ + regval = readl(SCPLL_CTL_ADDR); + regval |= 0x7; + writel(regval, SCPLL_CTL_ADDR); + + dmb(); + + /* wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* status bit seems to clear early, using + * 100us to handle the worst case. */ + udelay(100); +} + +static void scpll_apps_enable(bool state) +{ + uint32_t regval; + + if (state) + pr_debug("Enabling PLL 3\n"); + else + pr_debug("Disabling PLL 3\n"); + + /* Wait for any frequency switches to finish. */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* put the pll in standby mode */ + regval = readl(SCPLL_CTL_ADDR); + regval &= ~(0x7); + regval |= (0x2); + writel(regval, SCPLL_CTL_ADDR); + + dmb(); + + if (state) { + /* put the pll in normal mode */ + regval = readl(SCPLL_CTL_ADDR); + regval |= (0x7); + writel(regval, SCPLL_CTL_ADDR); + udelay(200); + } else { + /* put the pll in power down mode */ + regval = readl(SCPLL_CTL_ADDR); + regval &= ~(0x7); + writel(regval, SCPLL_CTL_ADDR); + } + udelay(62); + + if (state) + pr_debug("PLL 3 Enabled\n"); + else + pr_debug("PLL 3 Disabled\n"); +} + +static void scpll_init(void) +{ + uint32_t regval; +#define L_VAL_384MHZ 0xA +#define L_VAL_768MHZ 0x14 + + pr_debug("Initializing PLL 3\n"); + + /* power down scpll */ + writel(0x0, SCPLL_CTL_ADDR); + + dmb(); + + /* set bypassnl, put into standby */ + writel(0x00400002, SCPLL_CTL_ADDR); + + /* set bypassnl, reset_n, full calibration */ + writel(0x00600004, SCPLL_CTL_ADDR); + + /* Ensure register write to initiate calibration has taken + effect before reading status flag */ + dmb(); + + /* wait for cal_all_done */ + while (readl(SCPLL_STATUS_ADDR) & 0x2) + ; + + /* Start: Set of experimentally derived steps + * to work around a h/w bug. */ + + /* Put the pll in normal mode */ + scpll_apps_enable(1); + + /* SHOT switch to 384 MHz */ + regval = readl(SCPLL_FSM_CTL_EXT_ADDR); + regval &= ~(0x3f << 3); + regval |= (L_VAL_384MHZ << 3); + + regval &= ~0x7; + regval |= SHOT_SWITCH; + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + /* Trigger the freq switch by putting pll in normal mode. */ + regval = readl(SCPLL_CTL_ADDR); + regval |= (0x7); + writel(regval, SCPLL_CTL_ADDR); + + /* Wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* Status bit seems to clear early, using + * 800 microseconds for the worst case. */ + udelay(800); + + /* HOP switch to 768 MHz. */ + regval = readl(SCPLL_FSM_CTL_EXT_ADDR); + regval &= ~(0x3f << 3); + regval |= (L_VAL_768MHZ << 3); + + regval &= ~0x7; + regval |= HOP_SWITCH; + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + /* Trigger the freq switch by putting pll in normal mode. */ + regval = readl(SCPLL_CTL_ADDR); + regval |= (0x7); + writel(regval, SCPLL_CTL_ADDR); + + /* Wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* Status bit seems to clear early, using + * 100 microseconds for the worst case. */ + udelay(100); + + /* End: Work around for h/w bug */ + + /* Power down scpll */ + scpll_apps_enable(0); +} + +static void config_pll(struct clkctl_acpu_speed *s) +{ + uint32_t regval; + + if (s->pll == ACPU_PLL_3) + scpll_set_freq(s->sc_l_value, HOP_SWITCH); + /* Configure the PLL divider mux if we plan to use it. */ + else if (s->sc_core_src_sel_mask == 0) { + /* get the current clock source selection */ + regval = readl(SPSS_CLK_SEL_ADDR) & 0x1; + + /* configure the other clock source, then switch to it, + * using the glitch free mux */ + switch (regval) { + case 0x0: + regval = readl(SPSS_CLK_CNTL_ADDR); + regval &= ~(0x7 << 4 | 0xf); + regval |= (s->acpuclk_src_sel << 4); + regval |= (s->acpuclk_src_div << 0); + writel(regval, SPSS_CLK_CNTL_ADDR); + + regval = readl(SPSS_CLK_SEL_ADDR); + regval |= 0x1; + writel(regval, SPSS_CLK_SEL_ADDR); + break; + + case 0x1: + regval = readl(SPSS_CLK_CNTL_ADDR); + regval &= ~(0x7 << 12 | 0xf << 8); + regval |= (s->acpuclk_src_sel << 12); + regval |= (s->acpuclk_src_div << 8); + writel(regval, SPSS_CLK_CNTL_ADDR); + + regval = readl(SPSS_CLK_SEL_ADDR); + regval &= ~0x1; + writel(regval, SPSS_CLK_SEL_ADDR); + break; + } + dmb(); + } + + regval = readl(SPSS_CLK_SEL_ADDR); + regval &= ~(0x3 << 1); + regval |= (s->sc_core_src_sel_mask << 1); + writel(regval, SPSS_CLK_SEL_ADDR); +} + +static int acpuclk_set_vdd_level(int vdd) +{ + if (drv_state.acpu_set_vdd) { + pr_debug("Switching VDD to %d mV\n", vdd); + return drv_state.acpu_set_vdd(vdd); + } else { + /* Assume that the PMIC supports scaling the processor + * to its maximum frequency at its default voltage. + */ + return 0; + } +} + +static int acpuclk_8x50_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + struct clkctl_acpu_speed *tgt_s, *strt_s; + int res, rc = 0; + int freq_index = 0; + + if (reason == SETRATE_CPUFREQ) + mutex_lock(&drv_state.lock); + + strt_s = drv_state.current_speed; + + if (rate == strt_s->acpuclk_khz) + goto out; + + for (tgt_s = acpu_freq_tbl; tgt_s->acpuclk_khz != 0; tgt_s++) { + if (tgt_s->acpuclk_khz == rate) + break; + freq_index++; + } + + if (tgt_s->acpuclk_khz == 0) { + rc = -EINVAL; + goto out; + } + + if (reason == SETRATE_CPUFREQ) { +#ifdef CONFIG_MSM_CPU_AVS + /* Notify avs before changing frequency */ + rc = avs_adjust_freq(freq_index, 1); + if (rc) { + pr_err("Unable to increase ACPU vdd (%d)\n", rc); + goto out; + } +#endif + /* Increase VDD if needed. */ + if (tgt_s->vdd > strt_s->vdd) { + rc = acpuclk_set_vdd_level(tgt_s->vdd); + if (rc) { + pr_err("Unable to increase ACPU vdd (%d)\n", + rc); + goto out; + } + } + } else if (reason == SETRATE_PC + && rate != POWER_COLLAPSE_KHZ) { + /* Returning from PC. ACPU is running on AXI source. + * Step up to PLL0 before ramping up higher. */ + config_pll(PLL0_S); + } + + pr_debug("Switching from ACPU rate %u KHz -> %u KHz\n", + strt_s->acpuclk_khz, tgt_s->acpuclk_khz); + + if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) { + config_pll(tgt_s); + } else if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll == ACPU_PLL_3) { + scpll_apps_enable(1); + config_pll(tgt_s); + } else if (strt_s->pll == ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) { + config_pll(tgt_s); + scpll_apps_enable(0); + } else { + /* Temporarily switch to PLL0 while reconfiguring PLL3. */ + config_pll(PLL0_S); + config_pll(tgt_s); + } + + /* Update the driver state with the new clock freq */ + drv_state.current_speed = tgt_s; + + /* Re-adjust lpj for the new clock speed. */ + loops_per_jiffy = tgt_s->lpj; + + /* Nothing else to do for SWFI. */ + if (reason == SETRATE_SWFI) + goto out; + + if (strt_s->axiclk_khz != tgt_s->axiclk_khz) { + res = clk_set_rate(drv_state.ebi1_clk, + tgt_s->axiclk_khz * 1000); + if (res < 0) + pr_warning("Setting AXI min rate failed (%d)\n", res); + } + + /* Nothing else to do for power collapse */ + if (reason == SETRATE_PC) + goto out; + +#ifdef CONFIG_MSM_CPU_AVS + /* notify avs after changing frequency */ + res = avs_adjust_freq(freq_index, 0); + if (res) + pr_warning("Unable to drop ACPU vdd (%d)\n", res); +#endif + + /* Drop VDD level if we can. */ + if (tgt_s->vdd < strt_s->vdd) { + res = acpuclk_set_vdd_level(tgt_s->vdd); + if (res) + pr_warning("Unable to drop ACPU vdd (%d)\n", res); + } + + pr_debug("ACPU speed change complete\n"); +out: + if (reason == SETRATE_CPUFREQ) + mutex_unlock(&drv_state.lock); + return rc; +} + +static void __init acpuclk_hw_init(void) +{ + struct clkctl_acpu_speed *speed; + uint32_t div, sel, regval; + int res; + + /* Determine the source of the Scorpion clock. */ + regval = readl(SPSS_CLK_SEL_ADDR); + switch ((regval & 0x6) >> 1) { + case 0: /* raw source clock */ + case 3: /* low jitter PLL1 (768Mhz) */ + if (regval & 0x1) { + sel = ((readl(SPSS_CLK_CNTL_ADDR) >> 4) & 0x7); + div = ((readl(SPSS_CLK_CNTL_ADDR) >> 0) & 0xf); + } else { + sel = ((readl(SPSS_CLK_CNTL_ADDR) >> 12) & 0x7); + div = ((readl(SPSS_CLK_CNTL_ADDR) >> 8) & 0xf); + } + + /* Find the matching clock rate. */ + for (speed = acpu_freq_tbl; speed->acpuclk_khz != 0; speed++) { + if (speed->acpuclk_src_sel == sel && + speed->acpuclk_src_div == div) + break; + } + break; + + case 1: /* unbuffered scorpion pll (384Mhz to 998.4Mhz) */ + sel = ((readl(SCPLL_FSM_CTL_EXT_ADDR) >> 3) & 0x3f); + + /* Find the matching clock rate. */ + for (speed = acpu_freq_tbl; speed->acpuclk_khz != 0; speed++) { + if (speed->sc_l_value == sel && + speed->sc_core_src_sel_mask == 1) + break; + } + break; + + case 2: /* AXI bus clock (128Mhz) */ + speed = AXI_S; + break; + default: + BUG(); + } + + /* Initialize scpll only if it wasn't already initialized by the boot + * loader. If the CPU is already running on scpll, then the scpll was + * initialized by the boot loader. */ + if (speed->pll != ACPU_PLL_3) + scpll_init(); + + if (speed->acpuclk_khz == 0) { + pr_err("Error - ACPU clock reports invalid speed\n"); + return; + } + + drv_state.current_speed = speed; + res = clk_set_rate(drv_state.ebi1_clk, speed->axiclk_khz * 1000); + if (res < 0) + pr_warning("Setting AXI min rate failed (%d)\n", res); + res = clk_enable(drv_state.ebi1_clk); + if (res < 0) + pr_warning("Enabling AXI clock failed (%d)\n", res); + + pr_info("ACPU running at %d KHz\n", speed->acpuclk_khz); +} + +static unsigned long acpuclk_8x50_get_rate(int cpu) +{ + return drv_state.current_speed->acpuclk_khz; +} + +/* Spare register populated with efuse data on max ACPU freq. */ +#define CT_CSR_PHYS 0xA8700000 +#define TCSR_SPARE2_ADDR (ct_csr_base + 0x60) + +#define PLL0_M_VAL_ADDR (MSM_CLK_CTL_BASE + 0x308) + +static void __init acpu_freq_tbl_fixup(void) +{ + void __iomem *ct_csr_base; + uint32_t tcsr_spare2, pll0_m_val; + unsigned int max_acpu_khz; + unsigned int i; + + ct_csr_base = ioremap(CT_CSR_PHYS, PAGE_SIZE); + BUG_ON(ct_csr_base == NULL); + + tcsr_spare2 = readl(TCSR_SPARE2_ADDR); + + /* Check if the register is supported and meaningful. */ + if ((tcsr_spare2 & 0xF000) != 0xA000) { + pr_info("Efuse data on Max ACPU freq not present.\n"); + goto skip_efuse_fixup; + } + + switch (tcsr_spare2 & 0xF0) { + case 0x70: + acpu_freq_tbl = acpu_freq_tbl_768; + max_acpu_khz = 768000; + break; + case 0x30: + case 0x00: + max_acpu_khz = 998400; + break; + case 0x10: + max_acpu_khz = 1267200; + break; + default: + pr_warning("Invalid efuse data (%x) on Max ACPU freq!\n", + tcsr_spare2); + goto skip_efuse_fixup; + } + + pr_info("Max ACPU freq from efuse data is %d KHz\n", max_acpu_khz); + + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0; i++) { + if (acpu_freq_tbl[i].acpuclk_khz > max_acpu_khz) { + acpu_freq_tbl[i].acpuclk_khz = 0; + break; + } + } + +skip_efuse_fixup: + iounmap(ct_csr_base); + + /* pll0_m_val will be 36 when PLL0 is run at 235MHz + * instead of the usual 245MHz. */ + pll0_m_val = readl(PLL0_M_VAL_ADDR) & 0x7FFFF; + if (pll0_m_val == 36) + PLL0_S->acpuclk_khz = 235930; + + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0; i++) { + if (acpu_freq_tbl[i].vdd > TPS65023_MAX_DCDC1) { + acpu_freq_tbl[i].acpuclk_khz = 0; + break; + } + } +} + +/* Initalize the lpj field in the acpu_freq_tbl. */ +static void __init lpj_init(void) +{ + int i; + const struct clkctl_acpu_speed *base_clk = drv_state.current_speed; + for (i = 0; acpu_freq_tbl[i].acpuclk_khz; i++) { + acpu_freq_tbl[i].lpj = cpufreq_scale(loops_per_jiffy, + base_clk->acpuclk_khz, + acpu_freq_tbl[i].acpuclk_khz); + } +} + +#ifdef CONFIG_MSM_CPU_AVS +static int __init acpu_avs_init(int (*set_vdd) (int), int khz) +{ + int i; + int freq_count = 0; + int freq_index = -1; + + for (i = 0; acpu_freq_tbl[i].acpuclk_khz; i++) { + freq_count++; + if (acpu_freq_tbl[i].acpuclk_khz == khz) + freq_index = i; + } + + return avs_init(set_vdd, freq_count, freq_index); +} +#endif + +static int qsd8x50_tps65023_set_dcdc1(int mVolts) +{ + int rc = 0; +#ifdef CONFIG_QSD_SVS + rc = tps65023_set_dcdc1_level(mVolts); + /* + * By default the TPS65023 will be initialized to 1.225V. + * So we can safely switch to any frequency within this + * voltage even if the device is not probed/ready. + */ + if (rc == -ENODEV && mVolts <= CONFIG_QSD_PMIC_DEFAULT_DCDC1) + rc = 0; +#else + /* + * Disallow frequencies not supported in the default PMIC + * output voltage. + */ + if (mVolts > CONFIG_QSD_PMIC_DEFAULT_DCDC1) + rc = -EFAULT; +#endif + return rc; +} + +static struct acpuclk_data acpuclk_8x50_data = { + .set_rate = acpuclk_8x50_set_rate, + .get_rate = acpuclk_8x50_get_rate, + .power_collapse_khz = POWER_COLLAPSE_KHZ, + .wait_for_irq_khz = WAIT_FOR_IRQ_KHZ, + .switch_time_us = 20, +}; + +static int __init acpuclk_8x50_init(struct acpuclk_soc_data *soc_data) +{ + mutex_init(&drv_state.lock); + drv_state.acpu_set_vdd = qsd8x50_tps65023_set_dcdc1; + + drv_state.ebi1_clk = clk_get(NULL, "ebi1_acpu_clk"); + BUG_ON(IS_ERR(drv_state.ebi1_clk)); + + acpu_freq_tbl_fixup(); + acpuclk_hw_init(); + lpj_init(); + /* Set a lower bound for ACPU rate for boot. This limits the + * maximum frequency hop caused by the first CPUFREQ switch. */ + if (drv_state.current_speed->acpuclk_khz < PLL0_S->acpuclk_khz) + acpuclk_set_rate(0, PLL0_S->acpuclk_khz, SETRATE_CPUFREQ); + + acpuclk_register(&acpuclk_8x50_data); + +#ifdef CONFIG_CPU_FREQ_MSM + cpufreq_table_init(); + cpufreq_frequency_table_get_attr(freq_table, smp_processor_id()); +#endif +#ifdef CONFIG_MSM_CPU_AVS + if (!acpu_avs_init(drv_state.acpu_set_vdd, + drv_state.current_speed->acpuclk_khz)) { + /* avs init successful. avs will handle voltage changes */ + drv_state.acpu_set_vdd = NULL; + } +#endif + return 0; +} + +struct acpuclk_soc_data acpuclk_8x50_soc_data __initdata = { + .init = acpuclk_8x50_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-8x60.c b/arch/arm/mach-msm/acpuclock-8x60.c new file mode 100644 index 00000000000..48efa18b46a --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-8x60.c @@ -0,0 +1,1096 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "acpuclock.h" +#include "avs.h" + +/* Frequency switch modes. */ +#define SHOT_SWITCH 4 +#define HOP_SWITCH 5 +#define SIMPLE_SLEW 6 +#define COMPLEX_SLEW 7 + +/* PLL calibration limits. + * The PLL hardware has a minimum frequency of 384MHz. + * Calibration should respect this limit. */ +#define L_VAL_SCPLL_CAL_MIN 0x08 /* = 432 MHz with 27MHz source */ + +#define MAX_VDD_SC 1325000 /* uV */ +#define MAX_VDD_MEM 1325000 /* uV */ +#define MAX_VDD_DIG 1200000 /* uV */ +#define MAX_AXI 310500 /* KHz */ +#define SCPLL_LOW_VDD_FMAX 594000 /* KHz */ +#define SCPLL_LOW_VDD 1000000 /* uV */ +#define SCPLL_NOMINAL_VDD 1100000 /* uV */ + +/* SCPLL Modes. */ +#define SCPLL_POWER_DOWN 0 +#define SCPLL_BYPASS 1 +#define SCPLL_STANDBY 2 +#define SCPLL_FULL_CAL 4 +#define SCPLL_HALF_CAL 5 +#define SCPLL_STEP_CAL 6 +#define SCPLL_NORMAL 7 + +#define SCPLL_DEBUG_NONE 0 +#define SCPLL_DEBUG_FULL 3 + +/* SCPLL registers offsets. */ +#define SCPLL_DEBUG_OFFSET 0x0 +#define SCPLL_CTL_OFFSET 0x4 +#define SCPLL_CAL_OFFSET 0x8 +#define SCPLL_STATUS_OFFSET 0x10 +#define SCPLL_CFG_OFFSET 0x1C +#define SCPLL_FSM_CTL_EXT_OFFSET 0x24 +#define SCPLL_LUT_OFFSET(l_val) (0x38 + (((l_val) / 4) * 4)) + +/* Clock registers. */ +#define SPSS0_CLK_CTL_ADDR (MSM_ACC0_BASE + 0x04) +#define SPSS0_CLK_SEL_ADDR (MSM_ACC0_BASE + 0x08) +#define SPSS1_CLK_CTL_ADDR (MSM_ACC1_BASE + 0x04) +#define SPSS1_CLK_SEL_ADDR (MSM_ACC1_BASE + 0x08) +#define SPSS_L2_CLK_SEL_ADDR (MSM_GCC_BASE + 0x38) + +/* PTE EFUSE register. */ +#define QFPROM_PTE_EFUSE_ADDR (MSM_QFPROM_BASE + 0x00C0) + +static const void * const clk_ctl_addr[] = {SPSS0_CLK_CTL_ADDR, + SPSS1_CLK_CTL_ADDR}; +static const void * const clk_sel_addr[] = {SPSS0_CLK_SEL_ADDR, + SPSS1_CLK_SEL_ADDR, SPSS_L2_CLK_SEL_ADDR}; + +static const int rpm_vreg_voter[] = { RPM_VREG_VOTER1, RPM_VREG_VOTER2 }; +static struct regulator *regulator_sc[NR_CPUS]; + +enum scplls { + CPU0 = 0, + CPU1, + L2, +}; + +static const void * const sc_pll_base[] = { + [CPU0] = MSM_SCPLL_BASE + 0x200, + [CPU1] = MSM_SCPLL_BASE + 0x300, + [L2] = MSM_SCPLL_BASE + 0x400, +}; + +enum sc_src { + ACPU_AFAB, + ACPU_PLL_8, + ACPU_SCPLL, +}; + +static struct clock_state { + struct clkctl_acpu_speed *current_speed[NR_CPUS]; + struct clkctl_l2_speed *current_l2_speed; + spinlock_t l2_lock; + struct mutex lock; +} drv_state; + +struct clkctl_l2_speed { + unsigned int khz; + unsigned int src_sel; + unsigned int l_val; + unsigned int vdd_dig; + unsigned int vdd_mem; + unsigned int bw_level; +}; + +static struct clkctl_l2_speed *l2_vote[NR_CPUS]; + +struct clkctl_acpu_speed { + unsigned int use_for_scaling[2]; /* One for each CPU. */ + unsigned int acpuclk_khz; + int pll; + unsigned int acpuclk_src_sel; + unsigned int acpuclk_src_div; + unsigned int core_src_sel; + unsigned int l_val; + struct clkctl_l2_speed *l2_level; + unsigned int vdd_sc; + unsigned int avsdscr_setting; +}; + +/* Instantaneous bandwidth requests in MB/s. */ +#define BW_MBPS(_bw) \ + { \ + .vectors = &(struct msm_bus_vectors){ \ + .src = MSM_BUS_MASTER_AMPSS_M0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_bw) * 1000000UL, \ + .ab = 0, \ + }, \ + .num_paths = 1, \ + } +static struct msm_bus_paths bw_level_tbl[] = { + [0] = BW_MBPS(824), /* At least 103 MHz on bus. */ + [1] = BW_MBPS(1336), /* At least 167 MHz on bus. */ + [2] = BW_MBPS(2008), /* At least 251 MHz on bus. */ + [3] = BW_MBPS(2480), /* At least 310 MHz on bus. */ +}; + +static struct msm_bus_scale_pdata bus_client_pdata = { + .usecase = bw_level_tbl, + .num_usecases = ARRAY_SIZE(bw_level_tbl), + .active_only = 1, + .name = "acpuclock", +}; + +static uint32_t bus_perf_client; + +/* L2 frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_l2_speed l2_freq_tbl_v2[] = { + [0] = { MAX_AXI, 0, 0, 1000000, 1100000, 0}, + [1] = { 432000, 1, 0x08, 1000000, 1100000, 0}, + [2] = { 486000, 1, 0x09, 1000000, 1100000, 0}, + [3] = { 540000, 1, 0x0A, 1000000, 1100000, 0}, + [4] = { 594000, 1, 0x0B, 1000000, 1100000, 0}, + [5] = { 648000, 1, 0x0C, 1000000, 1100000, 1}, + [6] = { 702000, 1, 0x0D, 1100000, 1100000, 1}, + [7] = { 756000, 1, 0x0E, 1100000, 1100000, 1}, + [8] = { 810000, 1, 0x0F, 1100000, 1100000, 1}, + [9] = { 864000, 1, 0x10, 1100000, 1100000, 1}, + [10] = { 918000, 1, 0x11, 1100000, 1100000, 2}, + [11] = { 972000, 1, 0x12, 1100000, 1100000, 2}, + [12] = {1026000, 1, 0x13, 1100000, 1100000, 2}, + [13] = {1080000, 1, 0x14, 1100000, 1200000, 2}, + [14] = {1134000, 1, 0x15, 1100000, 1200000, 2}, + [15] = {1188000, 1, 0x16, 1200000, 1200000, 3}, + [16] = {1242000, 1, 0x17, 1200000, 1212500, 3}, + [17] = {1296000, 1, 0x18, 1200000, 1225000, 3}, + [18] = {1350000, 1, 0x19, 1200000, 1225000, 3}, + [19] = {1404000, 1, 0x1A, 1200000, 1250000, 3}, +}; + +#define L2(x) (&l2_freq_tbl_v2[(x)]) +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1188mhz[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 812500, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 875000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 875000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 887500, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 912500, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 925000, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 937500, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 950000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 975000, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 1000000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 1012500, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 1037500, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 1062500, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1087500, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1125000, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1137500, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1162500, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1187500, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_slow[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 975000, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 975000, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 1000000, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1025000, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1025000, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1050000, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1075000, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1100000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1125000, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1150000, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1150000, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1175000, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1200000, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1225000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_nom[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 950000, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 975000, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 975000, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 1000000, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 1000000, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1025000, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1025000, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1050000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1075000, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1100000, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1125000, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1150000, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1150000, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1175000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1512mhz_fast[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 800000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 825000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 825000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 850000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 850000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 875000, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 875000, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 900000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 900000, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 925000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 925000, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 950000, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 950000, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 950000, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 975000, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 1000000, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1000000, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1025000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1050000, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1075000, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1100000, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1100000, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1100000, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1125000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_slower[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 825000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 837500, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 850000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 875000, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 900000, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 912500, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 937500, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 962500, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 987500, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 1012500, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1025000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1062500, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1087500, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1100000, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1125000, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1150000, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1187500, 0x03006000}, + { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1225000, 0x03006000}, + { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1262500, 0x03006000}, + { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1300000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_slow[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 825000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 837500, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 850000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 862500, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 887500, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 900000, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 925000, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 937500, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 962500, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 987500, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 1000000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 1025000, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1050000, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1062500, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1087500, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1112500, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1150000, 0x03006000}, + { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1175000, 0x03006000}, + { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1212500, 0x03006000}, + { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1250000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_nom[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 787500, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 800000, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 812500, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 825000, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 837500, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 850000, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 875000, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 887500, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 900000, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 912500, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 937500, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 950000, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 975000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 987500, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 1012500, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1025000, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1050000, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1075000, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1112500, 0x03006000}, + { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1137500, 0x03006000}, + { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1175000, 0x03006000}, + { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1200000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* SCPLL frequencies = 2 * 27 MHz * L_VAL */ +static struct clkctl_acpu_speed acpu_freq_tbl_1674mhz_fast[] = { + { {1, 1}, 192000, ACPU_PLL_8, 3, 1, 0, 0, L2(1), 775000, 0x03006000}, + /* MAX_AXI row is used to source CPU cores and L2 from the AFAB clock. */ + { {0, 0}, MAX_AXI, ACPU_AFAB, 1, 0, 0, 0, L2(0), 775000, 0x03006000}, + { {1, 1}, 384000, ACPU_PLL_8, 3, 0, 0, 0, L2(1), 775000, 0x03006000}, + { {1, 1}, 432000, ACPU_SCPLL, 0, 0, 1, 0x08, L2(1), 775000, 0x03006000}, + { {1, 1}, 486000, ACPU_SCPLL, 0, 0, 1, 0x09, L2(2), 775000, 0x03006000}, + { {1, 1}, 540000, ACPU_SCPLL, 0, 0, 1, 0x0A, L2(3), 775000, 0x03006000}, + { {1, 1}, 594000, ACPU_SCPLL, 0, 0, 1, 0x0B, L2(4), 787500, 0x03006000}, + { {1, 1}, 648000, ACPU_SCPLL, 0, 0, 1, 0x0C, L2(5), 800000, 0x03006000}, + { {1, 1}, 702000, ACPU_SCPLL, 0, 0, 1, 0x0D, L2(6), 812500, 0x03006000}, + { {1, 1}, 756000, ACPU_SCPLL, 0, 0, 1, 0x0E, L2(7), 825000, 0x03006000}, + { {1, 1}, 810000, ACPU_SCPLL, 0, 0, 1, 0x0F, L2(8), 837500, 0x03006000}, + { {1, 1}, 864000, ACPU_SCPLL, 0, 0, 1, 0x10, L2(9), 862500, 0x03006000}, + { {1, 1}, 918000, ACPU_SCPLL, 0, 0, 1, 0x11, L2(10), 875000, 0x03006000}, + { {1, 1}, 972000, ACPU_SCPLL, 0, 0, 1, 0x12, L2(11), 887500, 0x03006000}, + { {1, 1}, 1026000, ACPU_SCPLL, 0, 0, 1, 0x13, L2(12), 900000, 0x03006000}, + { {1, 1}, 1080000, ACPU_SCPLL, 0, 0, 1, 0x14, L2(13), 925000, 0x03006000}, + { {1, 1}, 1134000, ACPU_SCPLL, 0, 0, 1, 0x15, L2(14), 937500, 0x03006000}, + { {1, 1}, 1188000, ACPU_SCPLL, 0, 0, 1, 0x16, L2(15), 950000, 0x03006000}, + { {1, 1}, 1242000, ACPU_SCPLL, 0, 0, 1, 0x17, L2(16), 962500, 0x03006000}, + { {1, 1}, 1296000, ACPU_SCPLL, 0, 0, 1, 0x18, L2(17), 975000, 0x03006000}, + { {1, 1}, 1350000, ACPU_SCPLL, 0, 0, 1, 0x19, L2(18), 1000000, 0x03006000}, + { {1, 1}, 1404000, ACPU_SCPLL, 0, 0, 1, 0x1A, L2(19), 1025000, 0x03006000}, + { {1, 1}, 1458000, ACPU_SCPLL, 0, 0, 1, 0x1B, L2(19), 1050000, 0x03006000}, + { {1, 1}, 1512000, ACPU_SCPLL, 0, 0, 1, 0x1C, L2(19), 1075000, 0x03006000}, + { {1, 1}, 1566000, ACPU_SCPLL, 0, 0, 1, 0x1D, L2(19), 1100000, 0x03006000}, + { {1, 1}, 1620000, ACPU_SCPLL, 0, 0, 1, 0x1E, L2(19), 1125000, 0x03006000}, + { {1, 1}, 1674000, ACPU_SCPLL, 0, 0, 1, 0x1F, L2(19), 1150000, 0x03006000}, + { {0, 0}, 0 }, +}; + +/* acpu_freq_tbl row to use when reconfiguring SC/L2 PLLs. */ +#define CAL_IDX 1 + +static struct clkctl_acpu_speed *acpu_freq_tbl; +static struct clkctl_l2_speed *l2_freq_tbl = l2_freq_tbl_v2; +static unsigned int l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_v2); + +static unsigned long acpuclk_8x60_get_rate(int cpu) +{ + return drv_state.current_speed[cpu]->acpuclk_khz; +} + +static void select_core_source(unsigned int id, unsigned int src) +{ + uint32_t regval; + int shift; + + shift = (id == L2) ? 0 : 1; + regval = readl_relaxed(clk_sel_addr[id]); + regval &= ~(0x3 << shift); + regval |= (src << shift); + writel_relaxed(regval, clk_sel_addr[id]); +} + +static void select_clk_source_div(unsigned int id, struct clkctl_acpu_speed *s) +{ + uint32_t reg_clksel, reg_clkctl, src_sel; + + /* Configure the PLL divider mux if we plan to use it. */ + if (s->core_src_sel == 0) { + + reg_clksel = readl_relaxed(clk_sel_addr[id]); + + /* CLK_SEL_SRC1N0 (bank) bit. */ + src_sel = reg_clksel & 1; + + /* Program clock source and divider. */ + reg_clkctl = readl_relaxed(clk_ctl_addr[id]); + reg_clkctl &= ~(0xFF << (8 * src_sel)); + reg_clkctl |= s->acpuclk_src_sel << (4 + 8 * src_sel); + reg_clkctl |= s->acpuclk_src_div << (0 + 8 * src_sel); + writel_relaxed(reg_clkctl, clk_ctl_addr[id]); + + /* Toggle clock source. */ + reg_clksel ^= 1; + + /* Program clock source selection. */ + writel_relaxed(reg_clksel, clk_sel_addr[id]); + } +} + +static void scpll_enable(int sc_pll, uint32_t l_val) +{ + uint32_t regval; + + /* Power-up SCPLL into standby mode. */ + writel_relaxed(SCPLL_STANDBY, sc_pll_base[sc_pll] + SCPLL_CTL_OFFSET); + mb(); + udelay(10); + + /* Shot-switch to target frequency. */ + regval = (l_val << 3) | SHOT_SWITCH; + writel_relaxed(regval, sc_pll_base[sc_pll] + SCPLL_FSM_CTL_EXT_OFFSET); + writel_relaxed(SCPLL_NORMAL, sc_pll_base[sc_pll] + SCPLL_CTL_OFFSET); + mb(); + udelay(20); +} + +static void scpll_disable(int sc_pll) +{ + /* Power down SCPLL. */ + writel_relaxed(SCPLL_POWER_DOWN, + sc_pll_base[sc_pll] + SCPLL_CTL_OFFSET); +} + +static void scpll_change_freq(int sc_pll, uint32_t l_val) +{ + uint32_t regval; + const void *base_addr = sc_pll_base[sc_pll]; + + /* Complex-slew switch to target frequency. */ + regval = (l_val << 3) | COMPLEX_SLEW; + writel_relaxed(regval, base_addr + SCPLL_FSM_CTL_EXT_OFFSET); + writel_relaxed(SCPLL_NORMAL, base_addr + SCPLL_CTL_OFFSET); + + /* Wait for frequency switch to start. */ + while (((readl_relaxed(base_addr + SCPLL_CTL_OFFSET) >> 3) & 0x3F) + != l_val) + cpu_relax(); + /* Wait for frequency switch to finish. */ + while (readl_relaxed(base_addr + SCPLL_STATUS_OFFSET) & 0x1) + cpu_relax(); +} + +/* Vote for the L2 speed and return the speed that should be applied. */ +static struct clkctl_l2_speed *compute_l2_speed(unsigned int voting_cpu, + struct clkctl_l2_speed *tgt_s) +{ + struct clkctl_l2_speed *new_s; + int cpu; + + /* Bounds check. */ + BUG_ON(tgt_s >= (l2_freq_tbl + l2_freq_tbl_size)); + + /* Find max L2 speed vote. */ + l2_vote[voting_cpu] = tgt_s; + new_s = l2_freq_tbl; + for_each_present_cpu(cpu) + new_s = max(new_s, l2_vote[cpu]); + + return new_s; +} + +/* Set the L2's clock speed. */ +static void set_l2_speed(struct clkctl_l2_speed *tgt_s) +{ + if (tgt_s == drv_state.current_l2_speed) + return; + + if (drv_state.current_l2_speed->src_sel == 1 + && tgt_s->src_sel == 1) + scpll_change_freq(L2, tgt_s->l_val); + else { + if (tgt_s->src_sel == 1) { + scpll_enable(L2, tgt_s->l_val); + mb(); + select_core_source(L2, tgt_s->src_sel); + } else { + select_core_source(L2, tgt_s->src_sel); + mb(); + scpll_disable(L2); + } + } + drv_state.current_l2_speed = tgt_s; +} + +/* Update the bus bandwidth request. */ +static void set_bus_bw(unsigned int bw) +{ + int ret; + + /* Bounds check. */ + if (bw >= ARRAY_SIZE(bw_level_tbl)) { + pr_err("%s: invalid bandwidth request (%d)\n", __func__, bw); + return; + } + + /* Update bandwidth if requst has changed. This may sleep. */ + ret = msm_bus_scale_client_update_request(bus_perf_client, bw); + if (ret) + pr_err("%s: bandwidth request failed (%d)\n", __func__, ret); + + return; +} + +/* Apply any per-cpu voltage increases. */ +static int increase_vdd(int cpu, unsigned int vdd_sc, unsigned int vdd_mem, + unsigned int vdd_dig, enum setrate_reason reason) +{ + int rc = 0; + + /* Increase vdd_mem active-set before vdd_dig and vdd_sc. + * vdd_mem should be >= both vdd_sc and vdd_dig. */ + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8058_S0, rpm_vreg_voter[cpu], + vdd_mem, MAX_VDD_MEM, 0); + if (rc) { + pr_err("%s: vdd_mem (cpu%d) increase failed (%d)\n", + __func__, cpu, rc); + return rc; + } + + /* Increase vdd_dig active-set vote. */ + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8058_S1, rpm_vreg_voter[cpu], + vdd_dig, MAX_VDD_DIG, 0); + if (rc) { + pr_err("%s: vdd_dig (cpu%d) increase failed (%d)\n", + __func__, cpu, rc); + return rc; + } + + /* Don't update the Scorpion voltage in the hotplug path. It should + * already be correct. Attempting to set it is bad because we don't + * know what CPU we are running on at this point, but the Scorpion + * regulator API requires we call it from the affected CPU. */ + if (reason == SETRATE_HOTPLUG) + return rc; + + /* Update per-core Scorpion voltage. */ + rc = regulator_set_voltage(regulator_sc[cpu], vdd_sc, MAX_VDD_SC); + if (rc) { + pr_err("%s: vdd_sc (cpu%d) increase failed (%d)\n", + __func__, cpu, rc); + return rc; + } + + return rc; +} + +/* Apply any per-cpu voltage decreases. */ +static void decrease_vdd(int cpu, unsigned int vdd_sc, unsigned int vdd_mem, + unsigned int vdd_dig, enum setrate_reason reason) +{ + int ret; + + /* Update per-core Scorpion voltage. This must be called on the CPU + * that's being affected. Don't do this in the hotplug remove path, + * where the rail is off and we're executing on the other CPU. */ + if (reason != SETRATE_HOTPLUG) { + ret = regulator_set_voltage(regulator_sc[cpu], vdd_sc, + MAX_VDD_SC); + if (ret) { + pr_err("%s: vdd_sc (cpu%d) decrease failed (%d)\n", + __func__, cpu, ret); + return; + } + } + + /* Decrease vdd_dig active-set vote. */ + ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8058_S1, rpm_vreg_voter[cpu], + vdd_dig, MAX_VDD_DIG, 0); + if (ret) { + pr_err("%s: vdd_dig (cpu%d) decrease failed (%d)\n", + __func__, cpu, ret); + return; + } + + /* Decrease vdd_mem active-set after vdd_dig and vdd_sc. + * vdd_mem should be >= both vdd_sc and vdd_dig. */ + ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8058_S0, rpm_vreg_voter[cpu], + vdd_mem, MAX_VDD_MEM, 0); + if (ret) { + pr_err("%s: vdd_mem (cpu%d) decrease failed (%d)\n", + __func__, cpu, ret); + return; + } +} + +static void switch_sc_speed(int cpu, struct clkctl_acpu_speed *tgt_s) +{ + struct clkctl_acpu_speed *strt_s = drv_state.current_speed[cpu]; + + if (strt_s->pll != ACPU_SCPLL && tgt_s->pll != ACPU_SCPLL) { + select_clk_source_div(cpu, tgt_s); + /* Select core source because target may be AFAB. */ + select_core_source(cpu, tgt_s->core_src_sel); + } else if (strt_s->pll != ACPU_SCPLL && tgt_s->pll == ACPU_SCPLL) { + scpll_enable(cpu, tgt_s->l_val); + mb(); + select_core_source(cpu, tgt_s->core_src_sel); + } else if (strt_s->pll == ACPU_SCPLL && tgt_s->pll != ACPU_SCPLL) { + select_clk_source_div(cpu, tgt_s); + select_core_source(cpu, tgt_s->core_src_sel); + /* Core source switch must complete before disabling SCPLL. */ + mb(); + udelay(1); + scpll_disable(cpu); + } else + scpll_change_freq(cpu, tgt_s->l_val); + + /* Update the driver state with the new clock freq */ + drv_state.current_speed[cpu] = tgt_s; +} + +static int acpuclk_8x60_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + struct clkctl_acpu_speed *tgt_s, *strt_s; + struct clkctl_l2_speed *tgt_l2; + unsigned int vdd_mem, vdd_dig, pll_vdd_dig; + unsigned long flags; + int rc = 0; + + if (cpu > num_possible_cpus()) { + rc = -EINVAL; + goto out; + } + + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) + mutex_lock(&drv_state.lock); + + strt_s = drv_state.current_speed[cpu]; + + /* Return early if rate didn't change. */ + if (rate == strt_s->acpuclk_khz) + goto out; + + /* Find target frequency. */ + for (tgt_s = acpu_freq_tbl; tgt_s->acpuclk_khz != 0; tgt_s++) + if (tgt_s->acpuclk_khz == rate) + break; + if (tgt_s->acpuclk_khz == 0) { + rc = -EINVAL; + goto out; + } + + /* AVS needs SAW_VCTL to be intitialized correctly, before enable, + * and is not initialized at acpuclk_init(). + */ + if (reason == SETRATE_CPUFREQ) + AVS_DISABLE(cpu); + + /* Calculate vdd_mem and vdd_dig requirements. + * vdd_mem must be >= vdd_sc */ + vdd_mem = max(tgt_s->vdd_sc, tgt_s->l2_level->vdd_mem); + /* Factor-in PLL vdd_dig requirements. */ + if ((tgt_s->l2_level->khz > SCPLL_LOW_VDD_FMAX) || + (tgt_s->pll == ACPU_SCPLL + && tgt_s->acpuclk_khz > SCPLL_LOW_VDD_FMAX)) + pll_vdd_dig = SCPLL_NOMINAL_VDD; + else + pll_vdd_dig = SCPLL_LOW_VDD; + vdd_dig = max(tgt_s->l2_level->vdd_dig, pll_vdd_dig); + + /* Increase VDD levels if needed. */ + if ((reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG + || reason == SETRATE_INIT) + && (tgt_s->acpuclk_khz > strt_s->acpuclk_khz)) { + rc = increase_vdd(cpu, tgt_s->vdd_sc, vdd_mem, vdd_dig, reason); + if (rc) + goto out; + } + + pr_debug("Switching from ACPU%d rate %u KHz -> %u KHz\n", + cpu, strt_s->acpuclk_khz, tgt_s->acpuclk_khz); + + /* Switch CPU speed. */ + switch_sc_speed(cpu, tgt_s); + + /* Update the L2 vote and apply the rate change. */ + spin_lock_irqsave(&drv_state.l2_lock, flags); + tgt_l2 = compute_l2_speed(cpu, tgt_s->l2_level); + set_l2_speed(tgt_l2); + spin_unlock_irqrestore(&drv_state.l2_lock, flags); + + /* Nothing else to do for SWFI. */ + if (reason == SETRATE_SWFI) + goto out; + + /* Nothing else to do for power collapse. */ + if (reason == SETRATE_PC) + goto out; + + /* Update bus bandwith request. */ + set_bus_bw(tgt_l2->bw_level); + + /* Drop VDD levels if we can. */ + if (tgt_s->acpuclk_khz < strt_s->acpuclk_khz) + decrease_vdd(cpu, tgt_s->vdd_sc, vdd_mem, vdd_dig, reason); + + pr_debug("ACPU%d speed change complete\n", cpu); + + /* Re-enable AVS */ + if (reason == SETRATE_CPUFREQ) + AVS_ENABLE(cpu, tgt_s->avsdscr_setting); + +out: + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) + mutex_unlock(&drv_state.lock); + return rc; +} + +static void __init scpll_init(int pll, unsigned int max_l_val) +{ + uint32_t regval; + + pr_debug("Initializing SCPLL%d\n", pll); + + /* Clear calibration LUT registers containing max frequency entry. + * LUT registers are only writeable in debug mode. */ + writel_relaxed(SCPLL_DEBUG_FULL, sc_pll_base[pll] + SCPLL_DEBUG_OFFSET); + writel_relaxed(0x0, sc_pll_base[pll] + SCPLL_LUT_OFFSET(max_l_val)); + writel_relaxed(SCPLL_DEBUG_NONE, sc_pll_base[pll] + SCPLL_DEBUG_OFFSET); + + /* Power-up SCPLL into standby mode. */ + writel_relaxed(SCPLL_STANDBY, sc_pll_base[pll] + SCPLL_CTL_OFFSET); + mb(); + udelay(10); + + /* Calibrate the SCPLL for the frequency range needed. */ + regval = (max_l_val << 24) | (L_VAL_SCPLL_CAL_MIN << 16); + writel_relaxed(regval, sc_pll_base[pll] + SCPLL_CAL_OFFSET); + + /* Start calibration */ + writel_relaxed(SCPLL_FULL_CAL, sc_pll_base[pll] + SCPLL_CTL_OFFSET); + + /* Wait for proof that calibration has started before checking the + * 'calibration done' bit in the status register. Waiting for the + * LUT register we cleared to contain data accomplishes this. + * This is required since the 'calibration done' bit takes time to + * transition from 'done' to 'not done' when starting a calibration. + */ + while (!readl_relaxed(sc_pll_base[pll] + SCPLL_LUT_OFFSET(max_l_val))) + cpu_relax(); + + /* Wait for calibration to complete. */ + while (readl_relaxed(sc_pll_base[pll] + SCPLL_STATUS_OFFSET) & 0x2) + cpu_relax(); + + /* Power-down SCPLL. */ + scpll_disable(pll); +} + +/* Force ACPU core and L2 cache clocks to rates that don't require SCPLLs. */ +static void __init unselect_scplls(void) +{ + int cpu; + + /* Ensure CAL_IDX frequency uses AFAB sources for CPU cores and L2. */ + BUG_ON(acpu_freq_tbl[CAL_IDX].core_src_sel != 0); + BUG_ON(acpu_freq_tbl[CAL_IDX].l2_level->src_sel != 0); + + for_each_possible_cpu(cpu) { + select_clk_source_div(cpu, &acpu_freq_tbl[CAL_IDX]); + select_core_source(cpu, acpu_freq_tbl[CAL_IDX].core_src_sel); + drv_state.current_speed[cpu] = &acpu_freq_tbl[CAL_IDX]; + l2_vote[cpu] = acpu_freq_tbl[CAL_IDX].l2_level; + } + + select_core_source(L2, acpu_freq_tbl[CAL_IDX].l2_level->src_sel); + drv_state.current_l2_speed = acpu_freq_tbl[CAL_IDX].l2_level; +} + +/* Ensure SCPLLs use the 27MHz PXO. */ +static void __init scpll_set_refs(void) +{ + int cpu; + uint32_t regval; + + /* Bit 4 = 0:PXO, 1:MXO. */ + for_each_possible_cpu(cpu) { + regval = readl_relaxed(sc_pll_base[cpu] + SCPLL_CFG_OFFSET); + regval &= ~BIT(4); + writel_relaxed(regval, sc_pll_base[cpu] + SCPLL_CFG_OFFSET); + } + regval = readl_relaxed(sc_pll_base[L2] + SCPLL_CFG_OFFSET); + regval &= ~BIT(4); + writel_relaxed(regval, sc_pll_base[L2] + SCPLL_CFG_OFFSET); +} + +/* Voltage regulator initialization. */ +static void __init regulator_init(void) +{ + struct clkctl_acpu_speed **freq = drv_state.current_speed; + const char *regulator_sc_name[] = {"8901_s0", "8901_s1"}; + int cpu, ret; + + for_each_possible_cpu(cpu) { + /* VDD_SC0, VDD_SC1 */ + regulator_sc[cpu] = regulator_get(NULL, regulator_sc_name[cpu]); + if (IS_ERR(regulator_sc[cpu])) + goto err; + ret = regulator_set_voltage(regulator_sc[cpu], + freq[cpu]->vdd_sc, MAX_VDD_SC); + if (ret) + goto err; + ret = regulator_enable(regulator_sc[cpu]); + if (ret) + goto err; + } + + return; + +err: + pr_err("%s: Failed to initialize voltage regulators\n", __func__); + BUG(); +} + +/* Register with bus driver. */ +static void __init bus_init(void) +{ + bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata); + if (!bus_perf_client) { + pr_err("%s: unable register bus client\n", __func__); + BUG(); + } +} + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[NR_CPUS][30]; + +static void __init cpufreq_table_init(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + int i, freq_cnt = 0; + /* Construct the freq_table tables from acpu_freq_tbl. */ + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0 + && freq_cnt < ARRAY_SIZE(*freq_table); i++) { + if (acpu_freq_tbl[i].use_for_scaling[cpu]) { + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency + = acpu_freq_tbl[i].acpuclk_khz; + freq_cnt++; + } + } + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].acpuclk_khz != 0); + + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency = CPUFREQ_TABLE_END; + + pr_info("CPU%d: %d scaling frequencies supported.\n", + cpu, freq_cnt); + + /* Register table with CPUFreq. */ + cpufreq_frequency_table_get_attr(freq_table[cpu], cpu); + } +} +#else +static void __init cpufreq_table_init(void) {} +#endif + +#define HOT_UNPLUG_KHZ MAX_AXI +static int __cpuinit acpuclock_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + static int prev_khz[NR_CPUS]; + int cpu = (int)hcpu; + + switch (action) { + case CPU_DEAD: + case CPU_DEAD_FROZEN: + prev_khz[cpu] = acpuclk_8x60_get_rate(cpu); + /* Fall through. */ + case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: + acpuclk_8x60_set_rate(cpu, HOT_UNPLUG_KHZ, SETRATE_HOTPLUG); + break; + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + if (WARN_ON(!prev_khz[cpu])) + return NOTIFY_BAD; + acpuclk_8x60_set_rate(cpu, prev_khz[cpu], SETRATE_HOTPLUG); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata acpuclock_cpu_notifier = { + .notifier_call = acpuclock_cpu_callback, +}; + +static __init struct clkctl_acpu_speed *select_freq_plan(void) +{ + uint32_t pte_efuse, speed_bin, pvs; + struct clkctl_acpu_speed *f; + + pte_efuse = readl_relaxed(QFPROM_PTE_EFUSE_ADDR); + + speed_bin = pte_efuse & 0xF; + if (speed_bin == 0xF) + speed_bin = (pte_efuse >> 4) & 0xF; + + pvs = (pte_efuse >> 10) & 0x7; + if (pvs == 0x7) + pvs = (pte_efuse >> 13) & 0x7; + + if (speed_bin == 0x2) { + switch (pvs) { + case 0x7: + case 0x4: + acpu_freq_tbl = acpu_freq_tbl_1674mhz_slower; + pr_info("ACPU PVS: Slower\n"); + break; + case 0x0: + acpu_freq_tbl = acpu_freq_tbl_1674mhz_slow; + pr_info("ACPU PVS: Slow\n"); + break; + case 0x1: + acpu_freq_tbl = acpu_freq_tbl_1674mhz_nom; + pr_info("ACPU PVS: Nominal\n"); + break; + case 0x3: + acpu_freq_tbl = acpu_freq_tbl_1674mhz_fast; + pr_info("ACPU PVS: Fast\n"); + break; + default: + acpu_freq_tbl = acpu_freq_tbl_1674mhz_slower; + pr_warn("ACPU PVS: Unknown. Defaulting to slower.\n"); + break; + } + } else if (speed_bin == 0x1) { + switch (pvs) { + case 0x0: + case 0x7: + acpu_freq_tbl = acpu_freq_tbl_1512mhz_slow; + pr_info("ACPU PVS: Slow\n"); + break; + case 0x1: + acpu_freq_tbl = acpu_freq_tbl_1512mhz_nom; + pr_info("ACPU PVS: Nominal\n"); + break; + case 0x3: + acpu_freq_tbl = acpu_freq_tbl_1512mhz_fast; + pr_info("ACPU PVS: Fast\n"); + break; + default: + acpu_freq_tbl = acpu_freq_tbl_1512mhz_slow; + pr_warn("ACPU PVS: Unknown. Defaulting to slow.\n"); + break; + } + } else { + acpu_freq_tbl = acpu_freq_tbl_1188mhz; + } + + for (f = acpu_freq_tbl; f->acpuclk_khz != 0; f++) + ; + f--; + pr_info("Max ACPU freq: %u KHz\n", f->acpuclk_khz); + + return f; +} + +static struct acpuclk_data acpuclk_8x60_data = { + .set_rate = acpuclk_8x60_set_rate, + .get_rate = acpuclk_8x60_get_rate, + .power_collapse_khz = MAX_AXI, + .wait_for_irq_khz = MAX_AXI, +}; + +static int __init acpuclk_8x60_init(struct acpuclk_soc_data *soc_data) +{ + struct clkctl_acpu_speed *max_freq; + int cpu; + + mutex_init(&drv_state.lock); + spin_lock_init(&drv_state.l2_lock); + + /* Configure hardware. */ + max_freq = select_freq_plan(); + unselect_scplls(); + scpll_set_refs(); + for_each_possible_cpu(cpu) + scpll_init(cpu, max_freq->l_val); + scpll_init(L2, max_freq->l2_level->l_val); + regulator_init(); + bus_init(); + + /* Improve boot time by ramping up CPUs immediately. */ + for_each_online_cpu(cpu) + acpuclk_8x60_set_rate(cpu, max_freq->acpuclk_khz, SETRATE_INIT); + + acpuclk_register(&acpuclk_8x60_data); + cpufreq_table_init(); + register_hotcpu_notifier(&acpuclock_cpu_notifier); + + return 0; +} + +struct acpuclk_soc_data acpuclk_8x60_soc_data __initdata = { + .init = acpuclk_8x60_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-9615.c b/arch/arm/mach-msm/acpuclock-9615.c new file mode 100644 index 00000000000..8882f413e75 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-9615.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "acpuclock.h" + +#define REG_CLKSEL_0 (MSM_APCS_GLB_BASE + 0x08) +#define REG_CLKDIV_0 (MSM_APCS_GLB_BASE + 0x0C) +#define REG_CLKSEL_1 (MSM_APCS_GLB_BASE + 0x10) +#define REG_CLKDIV_1 (MSM_APCS_GLB_BASE + 0x14) +#define REG_CLKOUTSEL (MSM_APCS_GLB_BASE + 0x18) + +#define MAX_VDD_CPU 1150000 +#define MAX_VDD_MEM 1150000 + +enum clk_src { + SRC_CXO, + SRC_PLL0, + SRC_PLL8, + SRC_PLL9, + NUM_SRC, +}; + +struct src_clock { + struct clk *clk; + const char *name; +}; + +static struct src_clock clocks[NUM_SRC] = { + [SRC_PLL0].name = "pll0", + [SRC_PLL8].name = "pll8", + [SRC_PLL9].name = "pll9", +}; + +struct clkctl_acpu_speed { + bool use_for_scaling; + unsigned int khz; + int src; + unsigned int src_sel; + unsigned int src_div; + unsigned int vdd_cpu; + unsigned int vdd_mem; + unsigned int bw_level; +}; + +struct acpuclk_state { + struct mutex lock; + struct clkctl_acpu_speed *current_speed; +}; + +static struct acpuclk_state drv_state = { + .current_speed = &(struct clkctl_acpu_speed){ 0 }, +}; + +/* Instantaneous bandwidth requests in MB/s. */ +#define BW_MBPS(_bw) \ + { \ + .vectors = &(struct msm_bus_vectors){ \ + .src = MSM_BUS_MASTER_AMPSS_M0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_bw) * 1000000UL, \ + .ab = 0, \ + }, \ + .num_paths = 1, \ + } +static struct msm_bus_paths bw_level_tbl[] = { + [0] = BW_MBPS(152), /* At least 19 MHz on bus. */ + [1] = BW_MBPS(368), /* At least 46 MHz on bus. */ + [2] = BW_MBPS(552), /* At least 69 MHz on bus. */ + [3] = BW_MBPS(736), /* At least 92 MHz on bus. */ + [4] = BW_MBPS(1064), /* At least 133 MHz on bus. */ + [5] = BW_MBPS(1536), /* At least 192 MHz on bus. */ +}; + +static struct msm_bus_scale_pdata bus_client_pdata = { + .usecase = bw_level_tbl, + .num_usecases = ARRAY_SIZE(bw_level_tbl), + .active_only = 1, + .name = "acpuclock", +}; + +static uint32_t bus_perf_client; + +static struct clkctl_acpu_speed acpu_freq_tbl[] = { + { 0, 19200, SRC_CXO, 0, 0, 950000, 1050000, 0 }, + { 1, 138000, SRC_PLL0, 6, 1, 950000, 1050000, 2 }, + { 1, 276000, SRC_PLL0, 6, 0, 1050000, 1050000, 2 }, + { 1, 384000, SRC_PLL8, 3, 0, 1150000, 1150000, 4 }, + /* The row below may be changed at runtime depending on hw rev. */ + { 1, 440000, SRC_PLL9, 2, 0, 1150000, 1150000, 4 }, + { 0 } +}; + +static void select_clk_source_div(struct clkctl_acpu_speed *s) +{ + static void * __iomem const sel_reg[] = {REG_CLKSEL_0, REG_CLKSEL_1}; + static void * __iomem const div_reg[] = {REG_CLKDIV_0, REG_CLKDIV_1}; + uint32_t next_bank; + + next_bank = !(readl_relaxed(REG_CLKOUTSEL) & 1); + writel_relaxed(s->src_sel, sel_reg[next_bank]); + writel_relaxed(s->src_div, div_reg[next_bank]); + writel_relaxed(next_bank, REG_CLKOUTSEL); + + /* Wait for switch to complete. */ + mb(); + udelay(1); +} + +/* Update the bus bandwidth request. */ +static void set_bus_bw(unsigned int bw) +{ + int ret; + + /* Bounds check. */ + if (bw >= ARRAY_SIZE(bw_level_tbl)) { + pr_err("invalid bandwidth request (%d)\n", bw); + return; + } + + /* Update bandwidth if request has changed. This may sleep. */ + ret = msm_bus_scale_client_update_request(bus_perf_client, bw); + if (ret) + pr_err("bandwidth request failed (%d)\n", ret); + + return; +} + +/* Apply any per-cpu voltage increases. */ +static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem) +{ + int rc = 0; + + /* + * Increase vdd_mem active-set before vdd_cpu. + * vdd_mem should be >= vdd_cpu. + */ + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_L9, RPM_VREG_VOTER1, + vdd_mem, MAX_VDD_MEM, 0); + if (rc) { + pr_err("vdd_mem increase failed (%d)\n", rc); + return rc; + } + + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER1, + vdd_cpu, MAX_VDD_CPU, 0); + if (rc) + pr_err("vdd_cpu increase failed (%d)\n", rc); + + return rc; +} + +/* Apply any per-cpu voltage decreases. */ +static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem) +{ + int ret; + + /* Update CPU voltage. */ + ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER1, + vdd_cpu, MAX_VDD_CPU, 0); + if (ret) { + pr_err("vdd_cpu decrease failed (%d)\n", ret); + return; + } + + /* + * Decrease vdd_mem active-set after vdd_cpu. + * vdd_mem should be >= vdd_cpu. + */ + ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_L9, RPM_VREG_VOTER1, + vdd_mem, MAX_VDD_MEM, 0); + if (ret) + pr_err("vdd_mem decrease failed (%d)\n", ret); +} + +static int acpuclk_9615_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + struct clkctl_acpu_speed *tgt_s, *strt_s; + int rc = 0; + + if (reason == SETRATE_CPUFREQ) + mutex_lock(&drv_state.lock); + + strt_s = drv_state.current_speed; + + /* Return early if rate didn't change. */ + if (rate == strt_s->khz) + goto out; + + /* Find target frequency. */ + for (tgt_s = acpu_freq_tbl; tgt_s->khz != 0; tgt_s++) + if (tgt_s->khz == rate) + break; + if (tgt_s->khz == 0) { + rc = -EINVAL; + goto out; + } + + /* Increase VDD levels if needed. */ + if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT) + && (tgt_s->khz > strt_s->khz)) { + rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem); + if (rc) + goto out; + } + + pr_debug("Switching from CPU rate %u KHz -> %u KHz\n", + strt_s->khz, tgt_s->khz); + + /* Switch CPU speed. */ + clk_enable(clocks[tgt_s->src].clk); + select_clk_source_div(tgt_s); + clk_disable(clocks[strt_s->src].clk); + + drv_state.current_speed = tgt_s; + pr_debug("CPU speed change complete\n"); + + /* Nothing else to do for SWFI or power-collapse. */ + if (reason == SETRATE_SWFI || reason == SETRATE_PC) + goto out; + + /* Update bus bandwith request. */ + set_bus_bw(tgt_s->bw_level); + + /* Drop VDD levels if we can. */ + if (tgt_s->khz < strt_s->khz) + decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem); + +out: + if (reason == SETRATE_CPUFREQ) + mutex_unlock(&drv_state.lock); + return rc; +} + +static unsigned long acpuclk_9615_get_rate(int cpu) +{ + return drv_state.current_speed->khz; +} + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[30]; + +static void __init cpufreq_table_init(void) +{ + int i, freq_cnt = 0; + + /* Construct the freq_table tables from acpu_freq_tbl. */ + for (i = 0; acpu_freq_tbl[i].khz != 0 + && freq_cnt < ARRAY_SIZE(freq_table); i++) { + if (acpu_freq_tbl[i].use_for_scaling) { + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency + = acpu_freq_tbl[i].khz; + freq_cnt++; + } + } + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].khz != 0); + + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END; + + pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt); + + /* Register table with CPUFreq. */ + cpufreq_frequency_table_get_attr(freq_table, smp_processor_id()); +} +#else +static void __init cpufreq_table_init(void) {} +#endif + +static struct acpuclk_data acpuclk_9615_data = { + .set_rate = acpuclk_9615_set_rate, + .get_rate = acpuclk_9615_get_rate, + .power_collapse_khz = 19200, + .wait_for_irq_khz = 19200, +}; + +static int __init acpuclk_9615_init(struct acpuclk_soc_data *soc_data) +{ + unsigned long max_cpu_khz = 0; + int i; + + mutex_init(&drv_state.lock); + + bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata); + if (!bus_perf_client) { + pr_err("Unable to register bus client\n"); + BUG(); + } + + for (i = 0; i < NUM_SRC; i++) { + if (clocks[i].name) { + clocks[i].clk = clk_get_sys("acpu", clocks[i].name); + BUG_ON(IS_ERR(clocks[i].clk)); + /* + * Prepare the PLLs because we enable/disable them + * in atomic context during power collapse/restore. + */ + BUG_ON(clk_prepare(clocks[i].clk)); + } + } + + /* Determine the rate of PLL9 and fixup tables accordingly */ + if (clk_get_rate(clocks[SRC_PLL9].clk) == 550000000) { + for (i = 0; i < ARRAY_SIZE(acpu_freq_tbl); i++) + if (acpu_freq_tbl[i].src == SRC_PLL9) { + acpu_freq_tbl[i].khz = 550000; + acpu_freq_tbl[i].bw_level = 5; + } + } + + /* Improve boot time by ramping up CPU immediately. */ + for (i = 0; acpu_freq_tbl[i].khz != 0; i++) + max_cpu_khz = acpu_freq_tbl[i].khz; + acpuclk_9615_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT); + + acpuclk_register(&acpuclk_9615_data); + cpufreq_table_init(); + + return 0; +} + +struct acpuclk_soc_data acpuclk_9615_soc_data __initdata = { + .init = acpuclk_9615_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index 805d4ee53f7..6f900d39b47 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -29,8 +30,8 @@ #include #include #include +#include -#include "proc_comm.h" #include "acpuclock.h" @@ -97,7 +98,7 @@ struct clkctl_acpu_speed { /* * ACPU speed table. Complete table is shown but certain speeds are commented - * out to optimized speed switching. Initialize loops_per_jiffy to 0. + * out to optimized speed switching. Initalize loops_per_jiffy to 0. * * Table stepping up/down is optimized for 256mhz jumps while staying on the * same PLL. @@ -493,7 +494,7 @@ uint32_t acpuclk_get_switch_time(void) * Clock driver initialization *---------------------------------------------------------------------------*/ -/* Initialize the lpj field in the acpu_freq_tbl. */ +/* Initalize the lpj field in the acpu_freq_tbl. */ static void __init lpj_init(void) { int i; diff --git a/arch/arm/mach-msm/acpuclock-copper.c b/arch/arm/mach-msm/acpuclock-copper.c new file mode 100644 index 00000000000..f0da74c862a --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-copper.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "acpuclock.h" +#include "acpuclock-krait.h" + +/* Corner type vreg VDD values */ +#define LVL_NONE RPM_VREG_CORNER_NONE +#define LVL_LOW RPM_VREG_CORNER_LOW +#define LVL_NOM RPM_VREG_CORNER_NOMINAL +#define LVL_HIGH RPM_VREG_CORNER_HIGH + +static struct hfpll_data hfpll_data_cpu = { + .mode_offset = 0x00, + .l_offset = 0x04, + .m_offset = 0x08, + .n_offset = 0x0C, + .config_offset = 0x14, + /* TODO: Verify magic number for copper when available. */ + .config_val = 0x7845C665, + .low_vdd_l_max = 52, + .vdd[HFPLL_VDD_NONE] = 0, + .vdd[HFPLL_VDD_LOW] = 810000, + .vdd[HFPLL_VDD_NOM] = 900000, +}; + +static struct hfpll_data hfpll_data_l2 = { + .mode_offset = 0x00, + .l_offset = 0x04, + .m_offset = 0x08, + .n_offset = 0x0C, + .config_offset = 0x14, + /* TODO: Verify magic number for copper when available. */ + .config_val = 0x7845C665, + .low_vdd_l_max = 52, + .vdd[HFPLL_VDD_NONE] = LVL_NONE, + .vdd[HFPLL_VDD_LOW] = LVL_LOW, + .vdd[HFPLL_VDD_NOM] = LVL_NOM, +}; + +static struct scalable scalable[] = { + [CPU0] = { + .hfpll_phys_base = 0xF908A000, + .hfpll_data = &hfpll_data_cpu, + .l2cpmr_iaddr = 0x4501, + .vreg[VREG_CORE] = { "krait0", 1050000, 3200000 }, + .vreg[VREG_MEM] = { "krait0_mem", 1050000, 0, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8941_S1 }, + .vreg[VREG_DIG] = { "krait0_dig", 1050000, 0, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8941_S2 }, + .vreg[VREG_HFPLL_A] = { "hfpll", 1800000, 0, + RPM_VREG_VOTER1, + RPM_VREG_ID_PM8941_L12 }, + }, + [CPU1] = { + .hfpll_phys_base = 0xF909A000, + .hfpll_data = &hfpll_data_cpu, + .l2cpmr_iaddr = 0x5501, + .vreg[VREG_CORE] = { "krait1", 1050000, 3200000 }, + .vreg[VREG_MEM] = { "krait1_mem", 1050000, 0, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8941_S1 }, + .vreg[VREG_DIG] = { "krait1_dig", 1050000, 0, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8941_S2 }, + .vreg[VREG_HFPLL_A] = { "hfpll", 1800000, 0, + RPM_VREG_VOTER2, + RPM_VREG_ID_PM8941_L12 }, + }, + [CPU2] = { + .hfpll_phys_base = 0xF90AA000, + .hfpll_data = &hfpll_data_cpu, + .l2cpmr_iaddr = 0x6501, + .vreg[VREG_CORE] = { "krait2", 1050000, 3200000 }, + .vreg[VREG_MEM] = { "krait2_mem", 1050000, 0, + RPM_VREG_VOTER4, + RPM_VREG_ID_PM8921_S1 }, + .vreg[VREG_DIG] = { "krait2_dig", 1050000, 0, + RPM_VREG_VOTER4, + RPM_VREG_ID_PM8921_S2 }, + .vreg[VREG_HFPLL_A] = { "hfpll", 1800000, 0, + RPM_VREG_VOTER4, + RPM_VREG_ID_PM8941_L12 }, + }, + [CPU3] = { + .hfpll_phys_base = 0xF90BA000, + .hfpll_data = &hfpll_data_cpu, + .l2cpmr_iaddr = 0x7501, + .vreg[VREG_CORE] = { "krait3", 1050000, 3200000 }, + .vreg[VREG_MEM] = { "krait3_mem", 1050000, 0, + RPM_VREG_VOTER5, + RPM_VREG_ID_PM8941_S1 }, + .vreg[VREG_DIG] = { "krait3_dig", 1050000, 0, + RPM_VREG_VOTER5, + RPM_VREG_ID_PM8941_S2 }, + .vreg[VREG_HFPLL_A] = { "hfpll", 1800000, 0, + RPM_VREG_VOTER5, + RPM_VREG_ID_PM8941_L12 }, + }, + [L2] = { + .hfpll_phys_base = 0xF9016000, + .hfpll_data = &hfpll_data_l2, + .l2cpmr_iaddr = 0x0500, + .vreg[VREG_HFPLL_A] = { "hfpll", 1800000, 0, + RPM_VREG_VOTER6, + RPM_VREG_ID_PM8941_L12 }, + }, +}; + +static struct msm_bus_paths bw_level_tbl[] = { + [0] = BW_MBPS(400), /* At least 50 MHz on bus. */ + [1] = BW_MBPS(800), /* At least 100 MHz on bus. */ + [2] = BW_MBPS(1334), /* At least 167 MHz on bus. */ + [3] = BW_MBPS(2666), /* At least 200 MHz on bus. */ + [4] = BW_MBPS(3200), /* At least 333 MHz on bus. */ +}; + +static struct msm_bus_scale_pdata bus_scale_data = { + .usecase = bw_level_tbl, + .num_usecases = ARRAY_SIZE(bw_level_tbl), + .active_only = 1, + .name = "acpuclk-copper", +}; + +#define L2(x) (&l2_freq_tbl[(x)]) +static struct l2_level l2_freq_tbl[] = { + [0] = { {STBY_KHZ, QSB, 0, 0, 0 }, LVL_NOM, 1050000, 0 }, + [1] = { { 300000, PLL_0, 0, 2, 0 }, LVL_NOM, 1050000, 2 }, + [2] = { { 384000, HFPLL, 2, 0, 40 }, LVL_NOM, 1050000, 2 }, + [3] = { { 460800, HFPLL, 2, 0, 48 }, LVL_NOM, 1050000, 2 }, + [4] = { { 537600, HFPLL, 1, 0, 28 }, LVL_NOM, 1050000, 2 }, + [5] = { { 576000, HFPLL, 1, 0, 30 }, LVL_NOM, 1050000, 3 }, + [6] = { { 652800, HFPLL, 1, 0, 34 }, LVL_NOM, 1050000, 3 }, + [7] = { { 729600, HFPLL, 1, 0, 38 }, LVL_NOM, 1050000, 3 }, + [8] = { { 806400, HFPLL, 1, 0, 42 }, LVL_NOM, 1050000, 3 }, + [9] = { { 883200, HFPLL, 1, 0, 46 }, LVL_NOM, 1050000, 4 }, + [10] = { { 960000, HFPLL, 1, 0, 50 }, LVL_NOM, 1050000, 4 }, + [11] = { { 1036800, HFPLL, 1, 0, 54 }, LVL_NOM, 1050000, 4 }, +}; + +static struct acpu_level acpu_freq_tbl[] = { + { 0, {STBY_KHZ, QSB, 0, 0, 0 }, L2(0), 1050000 }, + { 1, { 300000, PLL_0, 0, 2, 0 }, L2(1), 1050000 }, + { 1, { 384000, HFPLL, 2, 0, 40 }, L2(2), 1050000 }, + { 1, { 460800, HFPLL, 2, 0, 48 }, L2(3), 1050000 }, + { 1, { 537600, HFPLL, 1, 0, 28 }, L2(4), 1050000 }, + { 1, { 576000, HFPLL, 1, 0, 30 }, L2(5), 1050000 }, + { 1, { 652800, HFPLL, 1, 0, 34 }, L2(6), 1050000 }, + { 1, { 729600, HFPLL, 1, 0, 38 }, L2(7), 1050000 }, + { 1, { 806400, HFPLL, 1, 0, 42 }, L2(8), 1050000 }, + { 1, { 883200, HFPLL, 1, 0, 46 }, L2(9), 1050000 }, + { 1, { 960000, HFPLL, 1, 0, 50 }, L2(10), 1050000 }, + { 1, { 1036800, HFPLL, 1, 0, 54 }, L2(11), 1050000 }, + { 0, { 0 } } +}; + +static struct acpuclk_krait_params acpuclk_copper_params = { + .scalable = scalable, + .pvs_acpu_freq_tbl[PVS_SLOW] = acpu_freq_tbl, + .pvs_acpu_freq_tbl[PVS_NOMINAL] = acpu_freq_tbl, + .pvs_acpu_freq_tbl[PVS_FAST] = acpu_freq_tbl, + .l2_freq_tbl = l2_freq_tbl, + .l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl), + .bus_scale_data = &bus_scale_data, + .qfprom_phys_base = 0xFC4A8000, +}; + +static int __init acpuclk_copper_probe(struct platform_device *pdev) +{ + return acpuclk_krait_init(&pdev->dev, &acpuclk_copper_params); +} + +static struct of_device_id acpuclk_copper_match_table[] = { + { .compatible = "qcom,acpuclk-copper" }, + {} +}; + +static struct platform_driver acpuclk_copper_driver = { + .driver = { + .name = "acpuclk-copper", + .of_match_table = acpuclk_copper_match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init acpuclk_8960_init(void) +{ + return platform_driver_probe(&acpuclk_copper_driver, + acpuclk_copper_probe); +} +device_initcall(acpuclk_8960_init); diff --git a/arch/arm/mach-msm/acpuclock-fsm9xxx.c b/arch/arm/mach-msm/acpuclock-fsm9xxx.c new file mode 100644 index 00000000000..3cdc58d4889 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-fsm9xxx.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +#include "acpuclock.h" + +/* Registers */ +#define PLL1_CTL_ADDR (MSM_CLK_CTL_BASE + 0x604) + +static unsigned long acpuclk_9xxx_get_rate(int cpu) +{ + unsigned int pll1_ctl; + unsigned int pll1_l, pll1_div2; + unsigned int pll1_khz; + + pll1_ctl = readl_relaxed(PLL1_CTL_ADDR); + pll1_l = ((pll1_ctl >> 3) & 0x3f) * 2; + pll1_div2 = pll1_ctl & 0x20000; + pll1_khz = 19200 * pll1_l; + if (pll1_div2) + pll1_khz >>= 1; + + return pll1_khz; +} + +static struct acpuclk_data acpuclk_9xxx_data = { + .get_rate = acpuclk_9xxx_get_rate, +}; + +static int __init acpuclk_9xxx_init(struct acpuclk_soc_data *soc_data) +{ + acpuclk_register(&acpuclk_9xxx_data); + pr_info("ACPU running at %lu KHz\n", acpuclk_get_rate(0)); + return 0; +} + +struct acpuclk_soc_data acpuclk_9xxx_soc_data __initdata = { + .init = acpuclk_9xxx_init, +}; diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c new file mode 100644 index 00000000000..5682ac32516 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-krait.c @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "acpuclock.h" +#include "acpuclock-krait.h" + +/* MUX source selects. */ +#define PRI_SRC_SEL_SEC_SRC 0 +#define PRI_SRC_SEL_HFPLL 1 +#define PRI_SRC_SEL_HFPLL_DIV2 2 +#define SEC_SRC_SEL_QSB 0 +#define SEC_SRC_SEL_L2PLL 1 +#define SEC_SRC_SEL_AUX 2 + +/* PTE EFUSE register offset. */ +#define PTE_EFUSE 0xC0 + +static DEFINE_MUTEX(driver_lock); +static DEFINE_SPINLOCK(l2_lock); + +static struct drv_data { + const struct acpu_level *acpu_freq_tbl; + const struct l2_level *l2_freq_tbl; + struct scalable *scalable; + u32 bus_perf_client; + struct device *dev; +} drv; + +static unsigned long acpuclk_krait_get_rate(int cpu) +{ + return drv.scalable[cpu].cur_speed->khz; +} + +/* Select a source on the primary MUX. */ +static void set_pri_clk_src(struct scalable *sc, u32 pri_src_sel) +{ + u32 regval; + + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + regval &= ~0x3; + regval |= (pri_src_sel & 0x3); + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + /* Wait for switch to complete. */ + mb(); + udelay(1); +} + +/* Select a source on the secondary MUX. */ +static void set_sec_clk_src(struct scalable *sc, u32 sec_src_sel) +{ + u32 regval; + + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + regval &= ~(0x3 << 2); + regval |= ((sec_src_sel & 0x3) << 2); + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + /* Wait for switch to complete. */ + mb(); + udelay(1); +} + +/* Enable an already-configured HFPLL. */ +static void hfpll_enable(struct scalable *sc, bool skip_regulators) +{ + int rc; + + if (!skip_regulators) { + /* Enable regulators required by the HFPLL. */ + if (sc->vreg[VREG_HFPLL_A].rpm_vreg_id) { + rc = rpm_vreg_set_voltage( + sc->vreg[VREG_HFPLL_A].rpm_vreg_id, + sc->vreg[VREG_HFPLL_A].rpm_vreg_voter, + sc->vreg[VREG_HFPLL_A].cur_vdd, + sc->vreg[VREG_HFPLL_A].max_vdd, 0); + if (rc) + dev_err(drv.dev, + "%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_A].name, rc); + } + if (sc->vreg[VREG_HFPLL_B].rpm_vreg_id) { + rc = rpm_vreg_set_voltage( + sc->vreg[VREG_HFPLL_B].rpm_vreg_id, + sc->vreg[VREG_HFPLL_B].rpm_vreg_voter, + sc->vreg[VREG_HFPLL_B].cur_vdd, + sc->vreg[VREG_HFPLL_B].max_vdd, 0); + if (rc) + dev_err(drv.dev, + "%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_B].name, rc); + } + } + + /* Disable PLL bypass mode. */ + writel_relaxed(0x2, sc->hfpll_base + sc->hfpll_data->mode_offset); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + mb(); + udelay(10); + + /* De-assert active-low PLL reset. */ + writel_relaxed(0x6, sc->hfpll_base + sc->hfpll_data->mode_offset); + + /* Wait for PLL to lock. */ + mb(); + udelay(60); + + /* Enable PLL output. */ + writel_relaxed(0x7, sc->hfpll_base + sc->hfpll_data->mode_offset); +} + +/* Disable a HFPLL for power-savings or while it's being reprogrammed. */ +static void hfpll_disable(struct scalable *sc, bool skip_regulators) +{ + int rc; + + /* + * Disable the PLL output, disable test mode, enable the bypass mode, + * and assert the reset. + */ + writel_relaxed(0, sc->hfpll_base + sc->hfpll_data->mode_offset); + + if (!skip_regulators) { + /* Remove voltage votes required by the HFPLL. */ + if (sc->vreg[VREG_HFPLL_B].rpm_vreg_id) { + rc = rpm_vreg_set_voltage( + sc->vreg[VREG_HFPLL_B].rpm_vreg_id, + sc->vreg[VREG_HFPLL_B].rpm_vreg_voter, + 0, 0, 0); + if (rc) + dev_err(drv.dev, + "%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_B].name, rc); + } + if (sc->vreg[VREG_HFPLL_A].rpm_vreg_id) { + rc = rpm_vreg_set_voltage( + sc->vreg[VREG_HFPLL_A].rpm_vreg_id, + sc->vreg[VREG_HFPLL_A].rpm_vreg_voter, + 0, 0, 0); + if (rc) + dev_err(drv.dev, + "%s regulator enable failed (%d)\n", + sc->vreg[VREG_HFPLL_A].name, rc); + } + } +} + +/* Program the HFPLL rate. Assumes HFPLL is already disabled. */ +static void hfpll_set_rate(struct scalable *sc, const struct core_speed *tgt_s) +{ + writel_relaxed(tgt_s->pll_l_val, + sc->hfpll_base + sc->hfpll_data->l_offset); +} + +/* Return the L2 speed that should be applied. */ +static const struct l2_level *compute_l2_level(struct scalable *sc, + const struct l2_level *vote_l) +{ + const struct l2_level *new_l; + int cpu; + + /* Find max L2 speed vote. */ + sc->l2_vote = vote_l; + new_l = drv.l2_freq_tbl; + for_each_present_cpu(cpu) + new_l = max(new_l, drv.scalable[cpu].l2_vote); + + return new_l; +} + +/* Update the bus bandwidth request. */ +static void set_bus_bw(unsigned int bw) +{ + int ret; + + /* Update bandwidth if request has changed. This may sleep. */ + ret = msm_bus_scale_client_update_request(drv.bus_perf_client, bw); + if (ret) + dev_err(drv.dev, "bandwidth request failed (%d)\n", ret); +} + +/* Set the CPU or L2 clock speed. */ +static void set_speed(struct scalable *sc, const struct core_speed *tgt_s) +{ + const struct core_speed *strt_s = sc->cur_speed; + + if (strt_s->src == HFPLL && tgt_s->src == HFPLL) { + /* + * Move to an always-on source running at a frequency + * that does not require an elevated CPU voltage. + */ + set_sec_clk_src(sc, SEC_SRC_SEL_AUX); + set_pri_clk_src(sc, PRI_SRC_SEL_SEC_SRC); + + /* Re-program HFPLL. */ + hfpll_disable(sc, 1); + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 1); + + /* Move to HFPLL. */ + set_pri_clk_src(sc, tgt_s->pri_src_sel); + } else if (strt_s->src == HFPLL && tgt_s->src != HFPLL) { + set_sec_clk_src(sc, tgt_s->sec_src_sel); + set_pri_clk_src(sc, tgt_s->pri_src_sel); + hfpll_disable(sc, 0); + } else if (strt_s->src != HFPLL && tgt_s->src == HFPLL) { + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 0); + set_pri_clk_src(sc, tgt_s->pri_src_sel); + } else { + set_sec_clk_src(sc, tgt_s->sec_src_sel); + } + + sc->cur_speed = tgt_s; +} + +/* Apply any per-cpu voltage increases. */ +static int increase_vdd(int cpu, int vdd_core, int vdd_mem, int vdd_dig, + enum setrate_reason reason) +{ + struct scalable *sc = &drv.scalable[cpu]; + int rc = 0; + + /* + * Increase vdd_mem active-set before vdd_dig. + * vdd_mem should be >= vdd_dig. + */ + if (vdd_mem > sc->vreg[VREG_MEM].cur_vdd) { + rc = rpm_vreg_set_voltage(sc->vreg[VREG_MEM].rpm_vreg_id, + sc->vreg[VREG_MEM].rpm_vreg_voter, vdd_mem, + sc->vreg[VREG_MEM].max_vdd, 0); + if (rc) { + dev_err(drv.dev, + "vdd_mem (cpu%d) increase failed (%d)\n", + cpu, rc); + return rc; + } + sc->vreg[VREG_MEM].cur_vdd = vdd_mem; + } + + /* Increase vdd_dig active-set vote. */ + if (vdd_dig > sc->vreg[VREG_DIG].cur_vdd) { + rc = rpm_vreg_set_voltage(sc->vreg[VREG_DIG].rpm_vreg_id, + sc->vreg[VREG_DIG].rpm_vreg_voter, vdd_dig, + sc->vreg[VREG_DIG].max_vdd, 0); + if (rc) { + dev_err(drv.dev, + "vdd_dig (cpu%d) increase failed (%d)\n", + cpu, rc); + return rc; + } + sc->vreg[VREG_DIG].cur_vdd = vdd_dig; + } + + /* + * Update per-CPU core voltage. Don't do this for the hotplug path for + * which it should already be correct. Attempting to set it is bad + * because we don't know what CPU we are running on at this point, but + * the CPU regulator API requires we call it from the affected CPU. + */ + if (vdd_core > sc->vreg[VREG_CORE].cur_vdd + && reason != SETRATE_HOTPLUG) { + rc = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core, + sc->vreg[VREG_CORE].max_vdd); + if (rc) { + dev_err(drv.dev, + "vdd_core (cpu%d) increase failed (%d)\n", + cpu, rc); + return rc; + } + sc->vreg[VREG_CORE].cur_vdd = vdd_core; + } + + return rc; +} + +/* Apply any per-cpu voltage decreases. */ +static void decrease_vdd(int cpu, int vdd_core, int vdd_mem, int vdd_dig, + enum setrate_reason reason) +{ + struct scalable *sc = &drv.scalable[cpu]; + int ret; + + /* + * Update per-CPU core voltage. This must be called on the CPU + * that's being affected. Don't do this in the hotplug remove path, + * where the rail is off and we're executing on the other CPU. + */ + if (vdd_core < sc->vreg[VREG_CORE].cur_vdd + && reason != SETRATE_HOTPLUG) { + ret = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core, + sc->vreg[VREG_CORE].max_vdd); + if (ret) { + dev_err(drv.dev, + "vdd_core (cpu%d) decrease failed (%d)\n", + cpu, ret); + return; + } + sc->vreg[VREG_CORE].cur_vdd = vdd_core; + } + + /* Decrease vdd_dig active-set vote. */ + if (vdd_dig < sc->vreg[VREG_DIG].cur_vdd) { + ret = rpm_vreg_set_voltage(sc->vreg[VREG_DIG].rpm_vreg_id, + sc->vreg[VREG_DIG].rpm_vreg_voter, vdd_dig, + sc->vreg[VREG_DIG].max_vdd, 0); + if (ret) { + dev_err(drv.dev, + "vdd_dig (cpu%d) decrease failed (%d)\n", + cpu, ret); + return; + } + sc->vreg[VREG_DIG].cur_vdd = vdd_dig; + } + + /* + * Decrease vdd_mem active-set after vdd_dig. + * vdd_mem should be >= vdd_dig. + */ + if (vdd_mem < sc->vreg[VREG_MEM].cur_vdd) { + ret = rpm_vreg_set_voltage(sc->vreg[VREG_MEM].rpm_vreg_id, + sc->vreg[VREG_MEM].rpm_vreg_voter, vdd_mem, + sc->vreg[VREG_MEM].max_vdd, 0); + if (ret) { + dev_err(drv.dev, + "vdd_mem (cpu%d) decrease failed (%d)\n", + cpu, ret); + return; + } + sc->vreg[VREG_MEM].cur_vdd = vdd_mem; + } +} + +static int calculate_vdd_mem(const struct acpu_level *tgt) +{ + return tgt->l2_level->vdd_mem; +} + +static int calculate_vdd_dig(const struct acpu_level *tgt) +{ + int pll_vdd_dig; + const int *hfpll_vdd = drv.scalable[L2].hfpll_data->vdd; + const u32 low_vdd_l_max = drv.scalable[L2].hfpll_data->low_vdd_l_max; + + if (tgt->l2_level->speed.src != HFPLL) + pll_vdd_dig = hfpll_vdd[HFPLL_VDD_NONE]; + else if (tgt->l2_level->speed.pll_l_val > low_vdd_l_max) + pll_vdd_dig = hfpll_vdd[HFPLL_VDD_NOM]; + else + pll_vdd_dig = hfpll_vdd[HFPLL_VDD_LOW]; + + return max(tgt->l2_level->vdd_dig, pll_vdd_dig); +} + +static int calculate_vdd_core(const struct acpu_level *tgt) +{ + return tgt->vdd_core; +} + +/* Set the CPU's clock rate and adjust the L2 rate, voltage and BW requests. */ +static int acpuclk_krait_set_rate(int cpu, unsigned long rate, + enum setrate_reason reason) +{ + const struct core_speed *strt_acpu_s, *tgt_acpu_s; + const struct l2_level *tgt_l2_l; + const struct acpu_level *tgt; + int vdd_mem, vdd_dig, vdd_core; + unsigned long flags; + int rc = 0; + + if (cpu > num_possible_cpus()) { + rc = -EINVAL; + goto out; + } + + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) + mutex_lock(&driver_lock); + + strt_acpu_s = drv.scalable[cpu].cur_speed; + + /* Return early if rate didn't change. */ + if (rate == strt_acpu_s->khz) + goto out; + + /* Find target frequency. */ + for (tgt = drv.acpu_freq_tbl; tgt->speed.khz != 0; tgt++) { + if (tgt->speed.khz == rate) { + tgt_acpu_s = &tgt->speed; + break; + } + } + if (tgt->speed.khz == 0) { + rc = -EINVAL; + goto out; + } + + /* Calculate voltage requirements for the current CPU. */ + vdd_mem = calculate_vdd_mem(tgt); + vdd_dig = calculate_vdd_dig(tgt); + vdd_core = calculate_vdd_core(tgt); + + /* Increase VDD levels if needed. */ + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) { + rc = increase_vdd(cpu, vdd_core, vdd_mem, vdd_dig, reason); + if (rc) + goto out; + } + + pr_debug("Switching from ACPU%d rate %lu KHz -> %lu KHz\n", + cpu, strt_acpu_s->khz, tgt_acpu_s->khz); + + /* Set the new CPU speed. */ + set_speed(&drv.scalable[cpu], tgt_acpu_s); + + /* + * Update the L2 vote and apply the rate change. A spinlock is + * necessary to ensure L2 rate is calculated and set atomically + * with the CPU frequency, even if acpuclk_krait_set_rate() is + * called from an atomic context and the driver_lock mutex is not + * acquired. + */ + spin_lock_irqsave(&l2_lock, flags); + tgt_l2_l = compute_l2_level(&drv.scalable[cpu], tgt->l2_level); + set_speed(&drv.scalable[L2], &tgt_l2_l->speed); + spin_unlock_irqrestore(&l2_lock, flags); + + /* Nothing else to do for power collapse or SWFI. */ + if (reason == SETRATE_PC || reason == SETRATE_SWFI) + goto out; + + /* Update bus bandwith request. */ + set_bus_bw(tgt_l2_l->bw_level); + + /* Drop VDD levels if we can. */ + decrease_vdd(cpu, vdd_core, vdd_mem, vdd_dig, reason); + + pr_debug("ACPU%d speed change complete\n", cpu); + +out: + if (reason == SETRATE_CPUFREQ || reason == SETRATE_HOTPLUG) + mutex_unlock(&driver_lock); + return rc; +} + +/* Initialize a HFPLL at a given rate and enable it. */ +static void __init hfpll_init(struct scalable *sc, + const struct core_speed *tgt_s) +{ + pr_debug("Initializing HFPLL%d\n", sc - drv.scalable); + + /* Disable the PLL for re-programming. */ + hfpll_disable(sc, 1); + + /* Configure PLL parameters for integer mode. */ + writel_relaxed(sc->hfpll_data->config_val, + sc->hfpll_base + sc->hfpll_data->config_offset); + writel_relaxed(0, sc->hfpll_base + sc->hfpll_data->m_offset); + writel_relaxed(1, sc->hfpll_base + sc->hfpll_data->n_offset); + + /* Set an initial rate and enable the PLL. */ + hfpll_set_rate(sc, tgt_s); + hfpll_enable(sc, 0); +} + +/* Voltage regulator initialization. */ +static void __init regulator_init(const struct acpu_level *lvl) +{ + int cpu, ret; + struct scalable *sc; + int vdd_mem, vdd_dig, vdd_core; + + vdd_mem = calculate_vdd_mem(lvl); + vdd_dig = calculate_vdd_dig(lvl); + + for_each_possible_cpu(cpu) { + sc = &drv.scalable[cpu]; + + /* Set initial vdd_mem vote. */ + ret = rpm_vreg_set_voltage(sc->vreg[VREG_MEM].rpm_vreg_id, + sc->vreg[VREG_MEM].rpm_vreg_voter, vdd_mem, + sc->vreg[VREG_MEM].max_vdd, 0); + if (ret) { + dev_err(drv.dev, "%s initialization failed (%d)\n", + sc->vreg[VREG_MEM].name, ret); + BUG(); + } + sc->vreg[VREG_MEM].cur_vdd = vdd_mem; + + /* Set initial vdd_dig vote. */ + ret = rpm_vreg_set_voltage(sc->vreg[VREG_DIG].rpm_vreg_id, + sc->vreg[VREG_DIG].rpm_vreg_voter, vdd_dig, + sc->vreg[VREG_DIG].max_vdd, 0); + if (ret) { + dev_err(drv.dev, "%s initialization failed (%d)\n", + sc->vreg[VREG_DIG].name, ret); + BUG(); + } + sc->vreg[VREG_DIG].cur_vdd = vdd_dig; + + /* Setup Krait CPU regulators and initial core voltage. */ + sc->vreg[VREG_CORE].reg = regulator_get(NULL, + sc->vreg[VREG_CORE].name); + if (IS_ERR(sc->vreg[VREG_CORE].reg)) { + dev_err(drv.dev, "regulator_get(%s) failed (%ld)\n", + sc->vreg[VREG_CORE].name, + PTR_ERR(sc->vreg[VREG_CORE].reg)); + BUG(); + } + vdd_core = calculate_vdd_core(lvl); + ret = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core, + sc->vreg[VREG_CORE].max_vdd); + if (ret) { + dev_err(drv.dev, "regulator_set_voltage(%s) (%d)\n", + sc->vreg[VREG_CORE].name, ret); + BUG(); + } + sc->vreg[VREG_CORE].cur_vdd = vdd_core; + ret = regulator_set_optimum_mode(sc->vreg[VREG_CORE].reg, + sc->vreg[VREG_CORE].peak_ua); + if (ret < 0) { + dev_err(drv.dev, "regulator_set_optimum_mode(%s) failed" + " (%d)\n", sc->vreg[VREG_CORE].name, ret); + BUG(); + } + ret = regulator_enable(sc->vreg[VREG_CORE].reg); + if (ret) { + dev_err(drv.dev, "regulator_enable(%s) failed (%d)\n", + sc->vreg[VREG_CORE].name, ret); + BUG(); + } + } +} + +/* Set initial rate for a given core. */ +static void __init init_clock_sources(struct scalable *sc, + const struct core_speed *tgt_s) +{ + u32 regval; + + /* Program AUX source input to the secondary MUX. */ + if (sc->aux_clk_sel_addr) + writel_relaxed(sc->aux_clk_sel, sc->aux_clk_sel_addr); + + /* Switch away from the HFPLL while it's re-initialized. */ + set_sec_clk_src(sc, SEC_SRC_SEL_AUX); + set_pri_clk_src(sc, PRI_SRC_SEL_SEC_SRC); + hfpll_init(sc, tgt_s); + + /* Set PRI_SRC_SEL_HFPLL_DIV2 divider to div-2. */ + regval = get_l2_indirect_reg(sc->l2cpmr_iaddr); + regval &= ~(0x3 << 6); + set_l2_indirect_reg(sc->l2cpmr_iaddr, regval); + + /* Switch to the target clock source. */ + set_sec_clk_src(sc, tgt_s->sec_src_sel); + set_pri_clk_src(sc, tgt_s->pri_src_sel); + sc->cur_speed = tgt_s; +} + +static void __init per_cpu_init(int cpu, const struct acpu_level *max_level) +{ + drv.scalable[cpu].hfpll_base = + ioremap(drv.scalable[cpu].hfpll_phys_base, SZ_32); + BUG_ON(!drv.scalable[cpu].hfpll_base); + + init_clock_sources(&drv.scalable[cpu], &max_level->speed); + drv.scalable[cpu].l2_vote = max_level->l2_level; +} + +/* Register with bus driver. */ +static void __init bus_init(struct msm_bus_scale_pdata *bus_scale_data, + unsigned int init_bw) +{ + int ret; + + drv.bus_perf_client = msm_bus_scale_register_client(bus_scale_data); + if (!drv.bus_perf_client) { + dev_err(drv.dev, "unable to register bus client\n"); + BUG(); + } + + ret = msm_bus_scale_client_update_request(drv.bus_perf_client, init_bw); + if (ret) + dev_err(drv.dev, "initial bandwidth req failed (%d)\n", ret); +} + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[NR_CPUS][35]; + +static void __init cpufreq_table_init(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + int i, freq_cnt = 0; + /* Construct the freq_table tables from acpu_freq_tbl. */ + for (i = 0; drv.acpu_freq_tbl[i].speed.khz != 0 + && freq_cnt < ARRAY_SIZE(*freq_table); i++) { + if (drv.acpu_freq_tbl[i].use_for_scaling) { + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency + = drv.acpu_freq_tbl[i].speed.khz; + freq_cnt++; + } + } + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(drv.acpu_freq_tbl[i].speed.khz != 0); + + freq_table[cpu][freq_cnt].index = freq_cnt; + freq_table[cpu][freq_cnt].frequency = CPUFREQ_TABLE_END; + + dev_info(drv.dev, "CPU%d: %d frequencies supported\n", + cpu, freq_cnt); + + /* Register table with CPUFreq. */ + cpufreq_frequency_table_get_attr(freq_table[cpu], cpu); + } +} +#else +static void __init cpufreq_table_init(void) {} +#endif + +#define HOT_UNPLUG_KHZ STBY_KHZ +static int __cpuinit acpuclk_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + static int prev_khz[NR_CPUS]; + int rc, cpu = (int)hcpu; + struct scalable *sc = &drv.scalable[cpu]; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_DEAD: + prev_khz[cpu] = acpuclk_krait_get_rate(cpu); + /* Fall through. */ + case CPU_UP_CANCELED: + acpuclk_krait_set_rate(cpu, HOT_UNPLUG_KHZ, SETRATE_HOTPLUG); + regulator_set_optimum_mode(sc->vreg[VREG_CORE].reg, 0); + break; + case CPU_UP_PREPARE: + if (WARN_ON(!prev_khz[cpu])) + return NOTIFY_BAD; + rc = regulator_set_optimum_mode(sc->vreg[VREG_CORE].reg, + sc->vreg[VREG_CORE].peak_ua); + if (rc < 0) + return NOTIFY_BAD; + acpuclk_krait_set_rate(cpu, prev_khz[cpu], SETRATE_HOTPLUG); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __cpuinitdata acpuclk_cpu_notifier = { + .notifier_call = acpuclk_cpu_callback, +}; + +static const struct acpu_level __init *select_freq_plan( + const struct acpu_level *const *pvs_tbl, u32 qfprom_phys) +{ + const struct acpu_level *l, *max_acpu_level = NULL; + void __iomem *qfprom_base; + u32 pte_efuse, pvs, tbl_idx; + char *pvs_names[] = { "Slow", "Nominal", "Fast", "Unknown" }; + + qfprom_base = ioremap(qfprom_phys, SZ_256); + /* Select frequency tables. */ + if (qfprom_base) { + pte_efuse = readl_relaxed(qfprom_base + PTE_EFUSE); + pvs = (pte_efuse >> 10) & 0x7; + iounmap(qfprom_base); + if (pvs == 0x7) + pvs = (pte_efuse >> 13) & 0x7; + + switch (pvs) { + case 0x0: + case 0x7: + tbl_idx = PVS_SLOW; + break; + case 0x1: + tbl_idx = PVS_NOMINAL; + break; + case 0x3: + tbl_idx = PVS_FAST; + break; + default: + tbl_idx = PVS_UNKNOWN; + break; + } + } else { + tbl_idx = PVS_UNKNOWN; + dev_err(drv.dev, "Unable to map QFPROM base\n"); + } + dev_info(drv.dev, "ACPU PVS: %s\n", pvs_names[tbl_idx]); + if (tbl_idx == PVS_UNKNOWN) { + tbl_idx = PVS_SLOW; + dev_warn(drv.dev, "ACPU PVS: Defaulting to %s\n", + pvs_names[tbl_idx]); + } + drv.acpu_freq_tbl = pvs_tbl[tbl_idx]; + + /* Find the max supported scaling frequency. */ + for (l = drv.acpu_freq_tbl; l->speed.khz != 0; l++) + if (l->use_for_scaling) + max_acpu_level = l; + BUG_ON(!max_acpu_level); + dev_info(drv.dev, "Max ACPU freq: %lu KHz\n", + max_acpu_level->speed.khz); + + return max_acpu_level; +} + +static struct acpuclk_data acpuclk_krait_data = { + .set_rate = acpuclk_krait_set_rate, + .get_rate = acpuclk_krait_get_rate, + .power_collapse_khz = STBY_KHZ, + .wait_for_irq_khz = STBY_KHZ, +}; + +int __init acpuclk_krait_init(struct device *dev, + const struct acpuclk_krait_params *params) +{ + const struct acpu_level *max_acpu_level; + int cpu; + + drv.scalable = params->scalable; + drv.l2_freq_tbl = params->l2_freq_tbl; + drv.dev = dev; + + drv.scalable[L2].hfpll_base = + ioremap(drv.scalable[L2].hfpll_phys_base, SZ_32); + BUG_ON(!drv.scalable[L2].hfpll_base); + + max_acpu_level = select_freq_plan(params->pvs_acpu_freq_tbl, + params->qfprom_phys_base); + regulator_init(max_acpu_level); + bus_init(params->bus_scale_data, max_acpu_level->l2_level->bw_level); + init_clock_sources(&drv.scalable[L2], &max_acpu_level->l2_level->speed); + for_each_online_cpu(cpu) + per_cpu_init(cpu, max_acpu_level); + + cpufreq_table_init(); + + acpuclk_register(&acpuclk_krait_data); + register_hotcpu_notifier(&acpuclk_cpu_notifier); + + return 0; +} diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h new file mode 100644 index 00000000000..fbf1f5f1bed --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-krait.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_ACPUCLOCK_KRAIT_H +#define __ARCH_ARM_MACH_MSM_ACPUCLOCK_KRAIT_H + +#define STBY_KHZ 1 + +#define BW_MBPS(_bw) \ + { \ + .vectors = (struct msm_bus_vectors[]){ \ + {\ + .src = MSM_BUS_MASTER_AMPSS_M0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_bw) * 1000000UL, \ + }, \ + { \ + .src = MSM_BUS_MASTER_AMPSS_M1, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_bw) * 1000000UL, \ + }, \ + }, \ + .num_paths = 2, \ + } + +/** + * src_id - Clock source IDs. + */ +enum src_id { + PLL_0 = 0, + HFPLL, + QSB, +}; + +/** + * enum pvs - IDs to distinguish between CPU frequency tables. + */ +enum pvs { + PVS_SLOW = 0, + PVS_NOMINAL, + PVS_FAST, + PVS_UNKNOWN, + NUM_PVS +}; + +/** + * enum scalables - IDs of frequency scalable hardware blocks. + */ +enum scalables { + CPU0 = 0, + CPU1, + CPU2, + CPU3, + L2, +}; + + +/** + * enum hfpll_vdd_level - IDs of HFPLL voltage levels. + */ +enum hfpll_vdd_levels { + HFPLL_VDD_NONE, + HFPLL_VDD_LOW, + HFPLL_VDD_NOM, + NUM_HFPLL_VDD +}; + +/** + * enum vregs - IDs of voltage regulators. + */ +enum vregs { + VREG_CORE, + VREG_MEM, + VREG_DIG, + VREG_HFPLL_A, + VREG_HFPLL_B, + NUM_VREG +}; + +/** + * struct vreg - Voltage regulator data. + * @name: Name of requlator. + * @max_vdd: Limit the maximum-settable voltage. + * @rpm_vreg_id: ID to use with rpm_vreg_*() APIs. + * @reg: Regulator handle. + * @cur_vdd: Last-set voltage in uV. + * @peak_ua: Maximum current draw expected in uA. + */ +struct vreg { + const char name[15]; + const int max_vdd; + const int peak_ua; + const int rpm_vreg_voter; + const int rpm_vreg_id; + struct regulator *reg; + int cur_vdd; +}; + +/** + * struct core_speed - Clock tree and configuration parameters. + * @khz: Clock rate in KHz. + * @src: Clock source ID. + * @pri_src_sel: Input to select on the primary MUX. + * @sec_src_sel: Input to select on the secondary MUX. + * @pll_l_val: HFPLL "L" value to be applied when an HFPLL source is selected. + */ +struct core_speed { + const unsigned long khz; + const int src; + const u32 pri_src_sel; + const u32 sec_src_sel; + const u32 pll_l_val; +}; + +/** + * struct l2_level - L2 clock rate and associated voltage and b/w requirements. + * @speed: L2 clock configuration. + * @vdd_dig: vdd_dig voltage in uV. + * @vdd_mem: vdd_mem voltage in uV. + * @bw_level: Bandwidth performance level number. + */ +struct l2_level { + const struct core_speed speed; + const int vdd_dig; + const int vdd_mem; + const unsigned int bw_level; +}; + +/** + * struct acpu_level - CPU clock rate and L2 rate and voltage requirements. + * @use_for_scaling: Flag indicating whether or not the level should be used. + * @speed: CPU clock configuration. + * @l2_level: L2 configuration to use. + * @vdd_core: CPU core voltage in uV. + */ +struct acpu_level { + const int use_for_scaling; + const struct core_speed speed; + const struct l2_level *l2_level; + const int vdd_core; +}; + +/** + * struct hfpll_data - Descriptive data of HFPLL hardware. + * @mode_offset: Mode register offset from base address. + * @l_offset: "L" value register offset from base address. + * @m_offset: "M" value register offset from base address. + * @n_offset: "N" value register offset from base address. + * @config_offset: Configuration register offset from base address. + * @config_val: Value to initialize the @config_offset register to. + * @vdd: voltage requirements for each VDD level. + */ +struct hfpll_data { + const u32 mode_offset; + const u32 l_offset; + const u32 m_offset; + const u32 n_offset; + const u32 config_offset; + const u32 config_val; + const u32 low_vdd_l_max; + const int vdd[NUM_HFPLL_VDD]; +}; + +/** + * struct scalable - Register locations and state associated with a scalable HW. + * @hfpll_phys_base: Physical base address of HFPLL register. + * @hfpll_base: Virtual base address of HFPLL registers. + * @aux_clk_sel_addr: Virtual address of auxiliary MUX. + * @aux_clk_sel: Auxiliary mux input to select at boot. + * @l2cpmr_iaddr: Indirect address of the CPMR MUX/divider CP15 register. + * @hfpll_data: Descriptive data of HFPLL hardware. + * @cur_speed: Pointer to currently-set speed. + * @l2_vote: L2 performance level vote associate with the current CPU speed. + * @vreg: Array of voltage regulators needed by the scalable. + */ +struct scalable { + const u32 hfpll_phys_base; + void __iomem *hfpll_base; + void __iomem *aux_clk_sel_addr; + const u32 aux_clk_sel; + const u32 l2cpmr_iaddr; + const struct hfpll_data *hfpll_data; + const struct core_speed *cur_speed; + const struct l2_level *l2_vote; + struct vreg vreg[NUM_VREG]; +}; + +/** + * struct acpuclk_krait_params - SoC specific driver parameters. + * @scalable: Array of scalables. + * @pvs_acpu_freq_tbl: Array of CPU frequency tables. + * @l2_freq_tbl: L2 frequency table. + * @l2_freq_tbl_size: Number of rows in @l2_freq_tbl. + * @qfprom_phys_base: Physical base address of QFPROM. + * @bus_scale_data: MSM bus driver parameters. + */ +struct acpuclk_krait_params { + struct scalable *scalable; + const struct acpu_level *pvs_acpu_freq_tbl[NUM_PVS]; + const struct l2_level *l2_freq_tbl; + const size_t l2_freq_tbl_size; + const u32 qfprom_phys_base; + struct msm_bus_scale_pdata *bus_scale_data; +}; + +/** + * acpuclk_krait_init - Initialize the Krait CPU clock driver give SoC params. + */ +extern int acpuclk_krait_init(struct device *dev, + const struct acpuclk_krait_params *params); + +#endif diff --git a/arch/arm/mach-msm/acpuclock.c b/arch/arm/mach-msm/acpuclock.c new file mode 100644 index 00000000000..91071c47259 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock.c @@ -0,0 +1,76 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "acpuclock.h" + +static struct acpuclk_data *acpuclk_data; + +unsigned long acpuclk_get_rate(int cpu) +{ + if (!acpuclk_data->get_rate) + return 0; + + return acpuclk_data->get_rate(cpu); +} + +int acpuclk_set_rate(int cpu, unsigned long rate, enum setrate_reason reason) +{ + if (!acpuclk_data->set_rate) + return 0; + + return acpuclk_data->set_rate(cpu, rate, reason); +} + +uint32_t acpuclk_get_switch_time(void) +{ + return acpuclk_data->switch_time_us; +} + +unsigned long acpuclk_power_collapse(void) +{ + unsigned long rate = acpuclk_get_rate(smp_processor_id()); + acpuclk_set_rate(smp_processor_id(), acpuclk_data->power_collapse_khz, + SETRATE_PC); + return rate; +} + +unsigned long acpuclk_wait_for_irq(void) +{ + unsigned long rate = acpuclk_get_rate(smp_processor_id()); + acpuclk_set_rate(smp_processor_id(), acpuclk_data->wait_for_irq_khz, + SETRATE_SWFI); + return rate; +} + +void __init acpuclk_register(struct acpuclk_data *data) +{ + acpuclk_data = data; +} + +int __init acpuclk_init(struct acpuclk_soc_data *soc_data) +{ + int rc; + + if (!soc_data->init) + return -EINVAL; + + rc = soc_data->init(soc_data); + if (rc) + return rc; + + if (!acpuclk_data) + return -ENODEV; + + return 0; +} diff --git a/arch/arm/mach-msm/acpuclock.h b/arch/arm/mach-msm/acpuclock.h index 415de2eb9a5..c5f0ee3005c 100644 --- a/arch/arm/mach-msm/acpuclock.h +++ b/arch/arm/mach-msm/acpuclock.h @@ -1,9 +1,8 @@ -/* arch/arm/mach-msm/acpuclock.h - * - * MSM architecture clock driver header +/* + * MSM architecture CPU clock driver header * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. * Author: San Mehat * * This software is licensed under the terms of the GNU General Public @@ -20,13 +19,97 @@ #ifndef __ARCH_ARM_MACH_MSM_ACPUCLOCK_H #define __ARCH_ARM_MACH_MSM_ACPUCLOCK_H -int acpuclk_set_rate(unsigned long rate, int for_power_collapse); -unsigned long acpuclk_get_rate(void); -uint32_t acpuclk_get_switch_time(void); -unsigned long acpuclk_wait_for_irq(void); -unsigned long acpuclk_power_collapse(void); -unsigned long acpuclk_get_wfi_rate(void); +/** + * enum setrate_reason - Reasons for use with acpuclk_set_rate() + */ +enum setrate_reason { + SETRATE_CPUFREQ = 0, + SETRATE_SWFI, + SETRATE_PC, + SETRATE_HOTPLUG, + SETRATE_INIT, +}; +/** + * struct acpuclk_soc_data - SoC data for acpuclk_init() + */ +struct acpuclk_soc_data { + unsigned long max_speed_delta_khz; + unsigned int max_axi_khz; + int (*init)(struct acpuclk_soc_data *); +}; + +/** + * struct acpuclk_data - Function pointers and data for function implementations + */ +struct acpuclk_data { + unsigned long (*get_rate)(int cpu); + int (*set_rate)(int cpu, unsigned long rate, enum setrate_reason); + uint32_t switch_time_us; + unsigned long power_collapse_khz; + unsigned long wait_for_irq_khz; +}; + +/** + * acpulock_get_rate() - Get a CPU's clock rate in KHz + * @cpu: CPU to query the rate of + */ +unsigned long acpuclk_get_rate(int cpu); + +/** + * acpuclk_set_rate() - Set a CPU's clock rate + * @cpu: CPU to set rate of + * @rate: Desired rate in KHz + * @setrate_reason: Reason for the rate switch + * + * Returns 0 for success. + */ +int acpuclk_set_rate(int cpu, unsigned long rate, enum setrate_reason); + +/** + * acpuclk_get_switch_time() - Query estimated time in us for a CPU rate switch + */ +uint32_t acpuclk_get_switch_time(void); + +/** + * acpuclk_power_collapse() - Prepare current CPU clocks for power-collapse + * + * Returns the previous rate of the CPU in KHz. + */ +unsigned long acpuclk_power_collapse(void); + +/** + * acpuclk_wait_for_irq() - Prepare current CPU clocks for SWFI + * + * Returns the previous rate of the CPU in KHz. + */ +unsigned long acpuclk_wait_for_irq(void); + +/** + * acpuclk_register() - Register acpuclk_data function implementations + * @data: acpuclock API implementations and data + */ +void acpuclk_register(struct acpuclk_data *data); + +/** + * acpuclk_init() - acpuclock driver initialization function + * + * Return 0 for success. + */ +int acpuclk_init(struct acpuclk_soc_data *); + +/* SoC-specific acpuclock initialization functions. */ +extern struct acpuclk_soc_data acpuclk_7x27_soc_data; +extern struct acpuclk_soc_data acpuclk_7x27a_soc_data; +extern struct acpuclk_soc_data acpuclk_7x27aa_soc_data; +extern struct acpuclk_soc_data acpuclk_7x30_soc_data; +extern struct acpuclk_soc_data acpuclk_8x50_soc_data; +extern struct acpuclk_soc_data acpuclk_8x60_soc_data; +extern struct acpuclk_soc_data acpuclk_8960_soc_data; +extern struct acpuclk_soc_data acpuclk_9xxx_soc_data; +extern struct acpuclk_soc_data acpuclk_9615_soc_data; +extern struct acpuclk_soc_data acpuclk_8930_soc_data; +extern struct acpuclk_soc_data acpuclk_8064_soc_data; +extern struct acpuclk_soc_data acpuclk_8625_soc_data; #endif - diff --git a/arch/arm/mach-msm/arch-init-scorpion.S b/arch/arm/mach-msm/arch-init-scorpion.S new file mode 100644 index 00000000000..82a6db8bb07 --- /dev/null +++ b/arch/arm/mach-msm/arch-init-scorpion.S @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2009, Google Inc. + * 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. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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. + */ + + +/* TODO: + * - style cleanup + * - do we need to do *all* of this at boot? + */ + +.text +.code 32 + +#define DSB .byte 0x4f, 0xf0, 0x7f, 0xf5 +#define ISB .byte 0x6f, 0xf0, 0x7f, 0xf5 + +.equ TCSR_SPARE2, 0xA8700060 + +SET_SA: + ldr r0, =TCSR_SPARE2 + ldr r12, [r0] + + /* pack bits 8,2,0 into 2,1,0 */ + and r0, r12, #0x001 + and r1, r12, #0x004 + and r2, r12, #0x100 + orr r0, r1, lsr #1 + orr r0, r2, lsr #6 + + adr r1, table_l1_acc + mov r0, r0, lsl #2 + ldr r3, [r1, r0] + + /* write 3800XXXX to PVR0F0 */ + orr r0, r3, #0x38000000 + mcr p15, 0, r0, c15, c15, 0 + + /* write XXXX0000 to PVR2F0 */ + mov r1, r3, lsl #16 + mcr p15, 2, r1, c15, c15, 0 + + adr r1, table_l2_acc + and r0, r12, #0x008 + and r2, r12, #0x002 + orr r0, r0, r2, lsl #1 + ldr r2, [r1, r0] + + /* write to L2VR3F1 */ + mcr p15, 3, r2, c15, c15, 1 + + bx lr + +table_l1_acc: + .word 0xFC00 + .word 0xFC00 + .word 0x7C00 + .word 0xFC00 + .word 0x3C00 + .word 0x0400 + .word 0x0C00 + .word 0x1C00 + +table_l2_acc: + .word 0x010102 + .word 0x010102 + .word 0x010101 + .word 0x212102 + +.globl __cpu_early_init +__cpu_early_init: + //; Zero out r0 for use throughout this code. All other GPRs + //; (r1-r3) are set throughout this code to help establish + //; a consistent startup state for any code that follows. + //; Users should add code at the end of this routine to establish + //; their own stack address (r13), add translation page tables, enable + //; the caches, etc. + MOV r0, #0x0 + + + //; Remove hardcoded cache settings. appsbl_handler.s calls Set_SA + //; API to dynamically configure cache for slow/nominal/fast parts + + //; DCIALL to invalidate L2 cache bank (needs to be run 4 times, once per bank) + //; This must be done early in code (prior to enabling the caches) + MOV r1, #0x2 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank D ([15:14] == 2'b00) + ORR r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank C ([15:14] == 2'b01) + ADD r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank B ([15:14] == 2'b10) + ADD r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank A ([15:14] == 2'b11) + + //; Initialize the BPCR - setup Global History Mask (GHRM) to all 1's + //; and have all address bits (AM) participate. + //; Different settings can be used to improve performance + // MOVW r1, #0x01FF +.word 0xe30011ff // hardcoded MOVW instruction due to lack of compiler support + // MOVT r1, #0x01FF +.word 0xe34011ff // hardcoded MOVT instruction due to lack of compiler support + MCR p15, 7, r1, c15, c0, 2 //; WCP15_BPCR + + + //; Initialize all I$ Victim Registers to 0 for startup + MCR p15, 0, r0, c9, c1, 0 //; WCP15_ICVIC0 r0 + MCR p15, 0, r0, c9, c1, 1 //; WCP15_ICVIC1 r0 + MCR p15, 0, r0, c9, c1, 2 //; WCP15_ICVIC2 r0 + MCR p15, 0, r0, c9, c1, 3 //; WCP15_ICVIC3 r0 + MCR p15, 0, r0, c9, c1, 4 //; WCP15_ICVIC4 r0 + MCR p15, 0, r0, c9, c1, 5 //; WCP15_ICVIC5 r0 + MCR p15, 0, r0, c9, c1, 6 //; WCP15_ICVIC5 r0 + MCR p15, 0, r0, c9, c1, 7 //; WCP15_ICVIC7 r0 + + //; Initialize all I$ Locked Victim Registers (Unlocked Floors) to 0 + MCR p15, 1, r0, c9, c1, 0 //; WCP15_ICFLOOR0 r0 + MCR p15, 1, r0, c9, c1, 1 //; WCP15_ICFLOOR1 r0 + MCR p15, 1, r0, c9, c1, 2 //; WCP15_ICFLOOR2 r0 + MCR p15, 1, r0, c9, c1, 3 //; WCP15_ICFLOOR3 r0 + MCR p15, 1, r0, c9, c1, 4 //; WCP15_ICFLOOR4 r0 + MCR p15, 1, r0, c9, c1, 5 //; WCP15_ICFLOOR5 r0 + MCR p15, 1, r0, c9, c1, 6 //; WCP15_ICFLOOR6 r0 + MCR p15, 1, r0, c9, c1, 7 //; WCP15_ICFLOOR7 r0 + + //; Initialize all D$ Victim Registers to 0 + MCR p15, 2, r0, c9, c1, 0 //; WP15_DCVIC0 r0 + MCR p15, 2, r0, c9, c1, 1 //; WP15_DCVIC1 r0 + MCR p15, 2, r0, c9, c1, 2 //; WP15_DCVIC2 r0 + MCR p15, 2, r0, c9, c1, 3 //; WP15_DCVIC3 r0 + MCR p15, 2, r0, c9, c1, 4 //; WP15_DCVIC4 r0 + MCR p15, 2, r0, c9, c1, 5 //; WP15_DCVIC5 r0 + MCR p15, 2, r0, c9, c1, 6 //; WP15_DCVIC6 r0 + MCR p15, 2, r0, c9, c1, 7 //; WP15_DCVIC7 r0 + + //; Initialize all D$ Locked VDCtim Registers (Unlocked Floors) to 0 + MCR p15, 3, r0, c9, c1, 0 //; WCP15_DCFLOOR0 r0 + MCR p15, 3, r0, c9, c1, 1 //; WCP15_DCFLOOR1 r0 + MCR p15, 3, r0, c9, c1, 2 //; WCP15_DCFLOOR2 r0 + MCR p15, 3, r0, c9, c1, 3 //; WCP15_DCFLOOR3 r0 + MCR p15, 3, r0, c9, c1, 4 //; WCP15_DCFLOOR4 r0 + MCR p15, 3, r0, c9, c1, 5 //; WCP15_DCFLOOR5 r0 + MCR p15, 3, r0, c9, c1, 6 //; WCP15_DCFLOOR6 r0 + MCR p15, 3, r0, c9, c1, 7 //; WCP15_DCFLOOR7 r0 + + //; Initialize ASID to zero + MCR p15, 0, r0, c13, c0, 1 //; WCP15_CONTEXTIDR r0 + + //; ICIALL to invalidate entire I-Cache + MCR p15, 0, r0, c7, c5, 0 //; ICIALLU + + //; DCIALL to invalidate entire D-Cache + MCR p15, 0, r0, c9, c0, 6 //; DCIALL r0 + + + //; The VBAR (Vector Base Address Register) should be initialized + //; early in your code. We are setting it to zero + MCR p15, 0, r0, c12, c0, 0 //; WCP15_VBAR r0 + + //; Ensure the MCR's above have completed their operation before continuing + DSB + ISB + + //;------------------------------------------------------------------- + //; There are a number of registers that must be set prior to enabling + //; the MMU. The DCAR is one of these registers. We are setting + //; it to zero (no access) to easily detect improper setup in subsequent + //; code sequences + //;------------------------------------------------------------------- + //; Setup DACR (Domain Access Control Register) to zero + MCR p15, 0, r0, c3, c0, 0 //; WCP15_DACR r0 + + //; Setup DCLKCR to allow normal D-Cache line fills + MCR p15, 1, r0, c9, c0, 7 //; WCP15_DCLKCR r0 + + //; Initialize the ADFSR and EFSR registers. + MCR p15, 0, r0, c5, c1, 0 //; ADFSR + MCR p15, 7, r0, c15, c0, 1 //; EFSR + + //; Setup the TLBLKCR + //; Victim = 6'b000000; Floor = 6'b000000; + //; IASIDCFG = 2'b00 (State-Machine); IALLCFG = 2'b01 (Flash); BNA = 1'b0; + MOV r1, #0x02 + MCR p15, 0, r1, c10, c1, 3 //; WCP15_TLBLKCR r1 + + //;Make sure TLBLKCR is complete before continuing + ISB + + //; Invalidate the UTLB + MCR p15, 0, r0, c8, c7, 0 //; UTLBIALL + + //; Make sure UTLB request has been presented to macro before continuing + ISB + + //; setup L2CR1 to some default Instruction and data prefetching values + //; Users may want specific settings for various performance enhancements + //; In Halcyon we do not have broadcasting barriers. So we need to turn + // ; on bit 8 of L2CR1; which DBB:( Disable barrier broadcast ) + MOV r2, #0x100 + MCR p15, 3, r2, c15, c0, 3 //; WCP15_L2CR1 r0 + + + //; Enable Z bit to enable branch prediction (default is off) + MRC p15, 0, r2, c1, c0, 0 //; RCP15_SCTLR r2 + ORR r2, r2, #0x00000800 + MCR p15, 0, r2, c1, c0, 0 //; WCP15_SCTLR r2 + +#ifdef CONFIG_ARCH_QSD8X50 + /* disable predecode repair cache for thumb2 (DPRC, set bit 4 in PVR0F2) */ + mrc p15, 0, r2, c15, c15, 2 + orr r2, r2, #0x10 + mcr p15, 0, r2, c15, c15, 2 +#endif + + mov r1, lr + //; Make sure Link stack is initialized with branch and links to sequential addresses + //; This aids in creating a predictable startup environment + BL SEQ1 +SEQ1: BL SEQ2 +SEQ2: BL SEQ3 +SEQ3: BL SEQ4 +SEQ4: BL SEQ5 +SEQ5: BL SEQ6 +SEQ6: BL SEQ7 +SEQ7: BL SEQ8 +SEQ8: + mov lr, r1 + + //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA + //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers + //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit + MCR p14, 0, r0, c1, c0, 4 //; WCP14_DBGOSLAR r0 + + + //; Read the DBGPRSR to clear the DBGPRSR[STICKYPD] + //; Any read to DBGPRSR clear the STICKYPD bit + //; ISB guarantees the read completes before attempting to + //; execute a CP14 instruction. + MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3 + ISB + + //; Initialize the Watchpoint Control Registers to zero (optional) + //;;; MCR p14, 0, r0, c0, c0, 7 ; WCP14_DBGWCR0 r0 + //;;; MCR p14, 0, r0, c0, c1, 7 ; WCP14_DBGWCR1 r0 + + + //;---------------------------------------------------------------------- + //; The saved Program Status Registers (SPSRs) should be setup + //; prior to any automatic mode switches. The following + //; code sets these registers up to a known state. Users will need to + //; customize these settings to meet their needs. + //;---------------------------------------------------------------------- + MOV r2, #0x1f + MOV r1, #0x17 //;ABT mode + msr cpsr_c, r1 //;ABT mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x1b //;UND mode + msr cpsr_c, r1 //;UND mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x11 //;FIQ mode + msr cpsr_c, r1 //;FIQ mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x12 //;IRQ mode + msr cpsr_c, r1 //;IRQ mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x16 //;Monitor mode + msr cpsr_c, r1 //;Monitor mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x13 //;SVC mode + msr cpsr_c, r1 //;SVC mode + msr spsr_cxfs, r2 //;clear the spsr + + + //;---------------------------------------------------------------------- + //; Enabling Error reporting is something users may want to do at + //; some other point in time. We have chosen some default settings + //; that should be reviewed. Most of these registers come up in an + //; unpredictable state after reset. + //;---------------------------------------------------------------------- +//;Start of error and control setting + + //; setup L2CR0 with various L2/TCM control settings + //; enable out of order bus attributes and error reporting + //; this register comes up unpredictable after reset + // MOVW r1, #0x0F0F +.word 0xe3001f0f // hardcoded MOVW instruction due to lack of compiler support + // MOVT r1, #0xC005 +.word 0xe34c1005 // hardcoded MOVW instruction due to lack of compiler support + MCR p15, 3, r1, c15, c0, 1 //; WCP15_L2CR0 r1 + + //; setup L2CPUCR + //; MOV r2, #0xFF + //; Enable I and D cache parity + //;L2CPUCR[7:5] = 3~Rh7 ~V enable parity error reporting for modified, + //;tag, and data parity errors + MOV r2, #0xe0 + MCR p15, 3, r2, c15, c0, 2 //; WCP15_L2CPUCR r2 + + //; setup SPCR + //; enable all error reporting (reset value is unpredicatble for most bits) + MOV r3, #0x0F + MCR p15, 0, r3, c9, c7, 0 //; WCP15_SPCR r3 + + //; setup DMACHCRs (reset value unpredictable) + //; control setting and enable all error reporting + MOV r1, #0x0F + + //; DMACHCR0 = 0000000F + MOV r2, #0x00 //; channel 0 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; DMACHCR1 = 0000000F + MOV r2, #0x01 //; channel 1 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; DMACHCR2 = 0000000F + MOV r2, #0x02 //; channel 2 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; DMACHCR3 = 0000000F + MOV r2, #0x03 //; channel 3 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; Set ACTLR (reset unpredictable) + //; Set AVIVT control, error reporting, etc. + //; MOV r3, #0x07 + //; Enable I and D cache parity + //;ACTLR[2:0] = 3'h7 - enable parity error reporting from L2/I$/D$) + //;ACTLR[5:4] = 2'h3 - enable parity + //;ACTLR[19:18] =2'h3 - always generate and check parity(when MMU disabled). + //;Value to be written #0xC0037 + // MOVW r3, #0x0037 +.word 0xe3003037 // hardcoded MOVW instruction due to lack of compiler support + // MOVT r3, #0x000C +.word 0xe340300c // hardcoded MOVW instruction due to lack of compiler support + //; read the version_id to determine if d-cache should be disabled + LDR r2, = 0xa8e00270 //;Read HW_REVISION_NUMBER, HWIO_HW_REVISION_NUMBER_ADDR + LDR r2,[r2] + AND r2,r2,#0xf0000000 //;hw_revision mask off bits 28-31 + //;if HW_revision is 1.0 or older, (revision==0) + CMP r2,#0 + //; Disable d-cache on older QSD8650 (Rev 1.0) silicon + orreq r3, r3, #0x4000 //;disable dcache + MCR p15, 0, r3, c1, c0, 1 //; WCP15_ACTLR r3 + +//;End of error and control setting + + //;---------------------------------------------------------------------- + //; Unlock ETM and read StickyPD to halt the ETM clocks from running. + //; This is required for power saving whether the ETM is used or not. + //;---------------------------------------------------------------------- + + //;Clear ETMOSLSR[LOCK] bit + MOV r1, #0x00000000 + MCR p14, 1, r1, c1, c0, 4 //; WCP14_ETMOSLAR r1 + + //;Clear ETMPDSR[STICKYPD] bit + MRC p14, 1, r2, c1, c5, 4 //; RCP14_ETMPDSR r2 + +/* +#ifdef APPSBL_ETM_ENABLE + ;---------------------------------------------------------------------- + ; Optionally Enable the ETM (Embedded Trace Macro) which is used for debug + ;---------------------------------------------------------------------- + + ; enable ETM clock if disabled + MRC p15, 7, r1, c15, c0, 5 ; RCP15_CPMR r1 + ORR r1, r1, #0x00000008 + MCR p15, 7, r1, c15, c0, 5 ; WCP15_CPMR r1 + ISB + + ; set trigger event to counter1 being zero + MOV r3, #0x00000040 + MCR p14, 1, r3, c0, c2, 0 ; WCP14_ETMTRIGGER r3 + + ; clear ETMSR + MOV r2, #0x00000000 + MCR p14, 1, r2, c0, c4, 0 ; WCP14_ETMSR r2 + + ; clear trace enable single address comparator usage + MCR p14, 1, r2, c0, c7, 0 ; WCP14_ETMTECR2 r2 + + ; set trace enable to always + MOV r2, #0x0000006F + MCR p14, 1, r2, c0, c8, 0 ; WCP14_ETMTEEVR r2 + + ; clear trace enable address range comparator usage and exclude nothing + MOV r2, #0x01000000 + MCR p14, 1, r2, c0, c9, 0 ; WCP14_ETMTECR1 r2 + + ; set view data to always + MOV r2, #0x0000006F + MCR p14, 1, r2, c0, c12, 0 ; WCP14_ETMVDEVR r2 + + ; clear view data single address comparator usage + MOV r2, #0x00000000 + MCR p14, 1, r2, c0, c13, 0 ; WCP14_ETMVDCR1 r2 + + ; clear view data address range comparator usage and exclude nothing + MOV r2, #0x00010000 + MCR p14, 1, r2, c0, c15, 0 ; WCP14_ETMVDCR3 r2 + + ; set counter1 to 194 + MOV r2, #0x000000C2 + MCR p14, 1, r2, c0, c0, 5 ; WCP14_ETMCNTRLDVR1 r2 + + ; set counter1 to never reload + MOV r2, #0x0000406F + MCR p14, 1, r2, c0, c8, 5 ; WCP14_ETMCNTRLDEVR1 r2 + + ; set counter1 to decrement every cycle + MOV r2, #0x0000006F + MCR p14, 1, r2, c0, c4, 5 ; WCP14_ETMCNTENR1 r2 + + ; Set trace synchronization frequency 1024 bytes + MOV r2, #0x00000400 + MCR p14, 1, r2, c0, c8, 7 ; WCP14_ETMSYNCFR r2 + + ; Program etm control register + ; - Set the CPU to ETM clock ratio to 1:1 + ; - Set the ETM to perform data address tracing + MOV r2, #0x00002008 + MCR p14, 1, r2, c0, c0, 0 ; WCP14_ETMCR r2 + ISB +#endif *//* APPSBL_ETM_ENABLE */ + +/* +#ifdef APPSBL_VFP_ENABLE + ;---------------------------------------------------------------------- + ; Perform the following operations if you intend to make use of + ; the VFP/Neon unit. Note that the FMXR instruction requires a CPU ID + ; indicating the VFP unit is present (i.e.Cortex-A8). . + ; Some tools will require full double precision floating point support + ; which will become available in Scorpion pass 2 + ;---------------------------------------------------------------------- + ; allow full access to CP 10 and 11 space for VFP/NEON use + MRC p15, 0, r1, c1, c0, 2 ; Read CP Access Control Register + ORR r1, r1, #0x00F00000 ; enable full access for p10,11 + MCR p15, 0, r1, c1, c0, 2 ; Write CPACR + + ;make sure the CPACR is complete before continuing + ISB + + ; Enable VFP itself (certain OSes may want to dynamically set/clear + ; the enable bit based on the application being executed + MOV r1, #0x40000000 + FMXR FPEXC, r1 +#endif *//* APPSBL_VFP_ENABLE */ + + /* we have no stack, so just tail-call into the SET_SA routine... */ + b SET_SA + +.ltorg diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c new file mode 100644 index 00000000000..827adab00e2 --- /dev/null +++ b/arch/arm/mach-msm/avs.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "avs.h" + +#define AVSDSCR_INPUT 0x01004860 /* magic # from circuit designer */ +#define TSCSR_INPUT 0x00000001 /* enable temperature sense */ + +#define TEMPRS 16 /* total number of temperature regions */ +#define GET_TEMPR() (avs_get_tscsr() >> 28) /* scale TSCSR[CTEMP] to regions */ + +struct mutex avs_lock; + +static struct avs_state_s +{ + u32 freq_cnt; /* Frequencies supported list */ + short *avs_v; /* Dyanmically allocated storage for + * 2D table of voltages over temp & + * freq. Used as a set of 1D tables. + * Each table is for a single temp. + * For usage see avs_get_voltage + */ + int (*set_vdd) (int); /* Function Ptr for setting voltage */ + int changing; /* Clock frequency is changing */ + u32 freq_idx; /* Current frequency index */ + int vdd; /* Current ACPU voltage */ +} avs_state; + +/* + * Update the AVS voltage vs frequency table, for current temperature + * Adjust based on the AVS delay circuit hardware status + */ +static void avs_update_voltage_table(short *vdd_table) +{ + u32 avscsr; + int cpu; + int vu; + int l2; + int i; + u32 cur_freq_idx; + short cur_voltage; + + cur_freq_idx = avs_state.freq_idx; + cur_voltage = avs_state.vdd; + + avscsr = avs_test_delays(); + AVSDEBUG("avscsr=%x, avsdscr=%x\n", avscsr, avs_get_avsdscr()); + + /* + * Read the results for the various unit's AVS delay circuits + * 2=> up, 1=>down, 0=>no-change + */ + cpu = ((avscsr >> 23) & 2) + ((avscsr >> 16) & 1); + vu = ((avscsr >> 28) & 2) + ((avscsr >> 21) & 1); + l2 = ((avscsr >> 29) & 2) + ((avscsr >> 22) & 1); + + if ((cpu == 3) || (vu == 3) || (l2 == 3)) { + printk(KERN_ERR "AVS: Dly Synth O/P error\n"); + } else if ((cpu == 2) || (l2 == 2) || (vu == 2)) { + /* + * even if one oscillator asks for up, increase the voltage, + * as its an indication we are running outside the + * critical acceptable range of v-f combination. + */ + AVSDEBUG("cpu=%d l2=%d vu=%d\n", cpu, l2, vu); + AVSDEBUG("Voltage up at %d\n", cur_freq_idx); + + if (cur_voltage >= VOLTAGE_MAX) + printk(KERN_ERR + "AVS: Voltage can not get high enough!\n"); + + /* Raise the voltage for all frequencies */ + for (i = 0; i < avs_state.freq_cnt; i++) { + vdd_table[i] = cur_voltage + VOLTAGE_STEP; + if (vdd_table[i] > VOLTAGE_MAX) + vdd_table[i] = VOLTAGE_MAX; + } + } else if ((cpu == 1) && (l2 == 1) && (vu == 1)) { + if ((cur_voltage - VOLTAGE_STEP >= VOLTAGE_MIN) && + (cur_voltage <= vdd_table[cur_freq_idx])) { + vdd_table[cur_freq_idx] = cur_voltage - VOLTAGE_STEP; + AVSDEBUG("Voltage down for %d and lower levels\n", + cur_freq_idx); + + /* clamp to this voltage for all lower levels */ + for (i = 0; i < cur_freq_idx; i++) { + if (vdd_table[i] > vdd_table[cur_freq_idx]) + vdd_table[i] = vdd_table[cur_freq_idx]; + } + } + } +} + +/* + * Return the voltage for the target performance freq_idx and optionally + * use AVS hardware to check the present voltage freq_idx + */ +static short avs_get_target_voltage(int freq_idx, bool update_table) +{ + unsigned cur_tempr = GET_TEMPR(); + unsigned temp_index = cur_tempr*avs_state.freq_cnt; + + /* Table of voltages vs frequencies for this temp */ + short *vdd_table = avs_state.avs_v + temp_index; + + if (update_table) + avs_update_voltage_table(vdd_table); + + return vdd_table[freq_idx]; +} + + +/* + * Set the voltage for the freq_idx and optionally + * use AVS hardware to update the voltage + */ +static int avs_set_target_voltage(int freq_idx, bool update_table) +{ + int rc = 0; + int new_voltage = avs_get_target_voltage(freq_idx, update_table); + if (avs_state.vdd != new_voltage) { + AVSDEBUG("AVS setting V to %d mV @%d\n", + new_voltage, freq_idx); + rc = avs_state.set_vdd(new_voltage); + if (rc) + return rc; + avs_state.vdd = new_voltage; + } + return rc; +} + +/* + * Notify avs of clk frquency transition begin & end + */ +int avs_adjust_freq(u32 freq_idx, int begin) +{ + int rc = 0; + + if (!avs_state.set_vdd) { + /* AVS not initialized */ + return 0; + } + + if (freq_idx >= avs_state.freq_cnt) { + AVSDEBUG("Out of range :%d\n", freq_idx); + return -EINVAL; + } + + mutex_lock(&avs_lock); + if ((begin && (freq_idx > avs_state.freq_idx)) || + (!begin && (freq_idx < avs_state.freq_idx))) { + /* Update voltage before increasing frequency & + * after decreasing frequency + */ + rc = avs_set_target_voltage(freq_idx, 0); + if (rc) + goto aaf_out; + + avs_state.freq_idx = freq_idx; + } + avs_state.changing = begin; +aaf_out: + mutex_unlock(&avs_lock); + + return rc; +} + + +static struct delayed_work avs_work; +static struct workqueue_struct *kavs_wq; +#define AVS_DELAY ((CONFIG_HZ * 50 + 999) / 1000) + +static void do_avs_timer(struct work_struct *work) +{ + int cur_freq_idx; + + mutex_lock(&avs_lock); + if (!avs_state.changing) { + /* Only adjust the voltage if clk is stable */ + cur_freq_idx = avs_state.freq_idx; + avs_set_target_voltage(cur_freq_idx, 1); + } + mutex_unlock(&avs_lock); + queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY); +} + + +static void __init avs_timer_init(void) +{ + INIT_DELAYED_WORK_DEFERRABLE(&avs_work, do_avs_timer); + queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY); +} + +static void __exit avs_timer_exit(void) +{ + cancel_delayed_work(&avs_work); +} + +static int __init avs_work_init(void) +{ + kavs_wq = create_workqueue("avs"); + if (!kavs_wq) { + printk(KERN_ERR "AVS initialization failed\n"); + return -EFAULT; + } + avs_timer_init(); + + return 1; +} + +static void __exit avs_work_exit(void) +{ + avs_timer_exit(); + destroy_workqueue(kavs_wq); +} + +int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx) +{ + int i; + + mutex_init(&avs_lock); + + if (freq_cnt == 0) + return -EINVAL; + + avs_state.freq_cnt = freq_cnt; + + if (freq_idx >= avs_state.freq_cnt) + return -EINVAL; + + avs_state.avs_v = kmalloc(TEMPRS * avs_state.freq_cnt * + sizeof(avs_state.avs_v[0]), GFP_KERNEL); + + if (avs_state.avs_v == 0) + return -ENOMEM; + + for (i = 0; i < TEMPRS*avs_state.freq_cnt; i++) + avs_state.avs_v[i] = VOLTAGE_MAX; + + avs_reset_delays(AVSDSCR_INPUT); + avs_set_tscsr(TSCSR_INPUT); + + avs_state.set_vdd = set_vdd; + avs_state.changing = 0; + avs_state.freq_idx = -1; + avs_state.vdd = -1; + avs_adjust_freq(freq_idx, 0); + + avs_work_init(); + + return 0; +} + +void __exit avs_exit() +{ + avs_work_exit(); + + kfree(avs_state.avs_v); +} + + diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h new file mode 100644 index 00000000000..a549e9de7f2 --- /dev/null +++ b/arch/arm/mach-msm/avs.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef AVS_H +#define AVS_H + +#define VOLTAGE_MIN 1000 /* mV */ +#define VOLTAGE_MAX 1250 +#define VOLTAGE_STEP 25 + +int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx); +void __exit avs_exit(void); + +int avs_adjust_freq(u32 freq_index, int begin); + +/* Routines exported from avs_hw.S */ +#ifdef CONFIG_MSM_CPU_AVS +u32 avs_test_delays(void); +#else +static inline u32 avs_test_delays(void) +{ return 0; } +#endif + +#ifdef CONFIG_MSM_AVS_HW +u32 avs_reset_delays(u32 avsdscr); +u32 avs_get_avscsr(void); +u32 avs_get_avsdscr(void); +u32 avs_get_tscsr(void); +void avs_set_tscsr(u32 to_tscsr); +void avs_disable(void); +#else +static inline u32 avs_reset_delays(u32 avsdscr) +{ return 0; } +static inline u32 avs_get_avscsr(void) +{ return 0; } +static inline u32 avs_get_avsdscr(void) +{ return 0; } +static inline u32 avs_get_tscsr(void) +{ return 0; } +static inline void avs_set_tscsr(u32 to_tscsr) {} +static inline void avs_disable(void) {} +#endif + +/*#define AVSDEBUG(x...) pr_info("AVS: " x);*/ +#define AVSDEBUG(...) + +#define AVS_DISABLE(cpu) do { \ + if (get_cpu() == (cpu)) \ + avs_disable(); \ + put_cpu(); \ + } while (0); + +#define AVS_ENABLE(cpu, x) do { \ + if (get_cpu() == (cpu)) \ + avs_reset_delays((x)); \ + put_cpu(); \ + } while (0); + +#endif /* AVS_H */ diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S new file mode 100644 index 00000000000..1cc3ce0b107 --- /dev/null +++ b/arch/arm/mach-msm/avs_hw.S @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + .text + +#ifdef CONFIG_MSM_CPU_AVS + .global avs_test_delays +avs_test_delays: + +/* Read r1=CPMR and enable Never Sleep for VSLPDLY */ + mrc p15, 7, r1, c15, c0, 5 + orr r12, r1, #3, 24 + mcr p15, 7, r12, c15, c0, 5 + +/* Read r2=CPACR and enable full access to CP10 and CP11 space */ + mrc p15, 0, r2, c1, c0, 2 + orr r12, r2, #(0xf << 20) + mcr p15, 0, r12, c1, c0, 2 + isb + +/* Read r3=FPEXC and or in FP enable, VFP/ASE enable = FPEXC[30]; */ + fmrx r3, fpexc + orr r12, r3, #1, 2 + fmxr fpexc, r12 + +/* + * Do floating-point operations to prime the VFP pipeline. Use + * fcpyd d0, d0 as a floating point nop. This avoids changing VFP + * state. + */ + fcpyd d0, d0 + fcpyd d0, d0 + fcpyd d0, d0 + +/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */ + mrc p15, 7, r0, c15, c1, 7 + +/* Restore FPEXC */ + fmxr fpexc, r3 + +/* Restore CPACR */ + MCR p15, 0, r2, c1, c0, 2 + +/* Restore CPMR */ + mcr p15, 7, r1, c15, c0, 5 + isb + + bx lr +#endif + + + .global avs_get_avscsr +/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */ + +avs_get_avscsr: + mrc p15, 7, r0, c15, c1, 7 + bx lr + + .global avs_get_avsdscr +/* Read r0=AVSDSCR to get the AVS Delay Synthesizer control settings */ + +avs_get_avsdscr: + mrc p15, 7, r0, c15, c0, 6 + bx lr + + + + + .global avs_get_tscsr +/* Read r0=TSCSR to get temperature sensor control and status */ + +avs_get_tscsr: + mrc p15, 7, r0, c15, c1, 0 + bx lr + + .global avs_set_tscsr +/* Write TSCSR=r0 to set temperature sensor control and status */ + +avs_set_tscsr: + mcr p15, 7, r0, c15, c1, 0 + bx lr + + + + + + .global avs_reset_delays +avs_reset_delays: + +/* AVSDSCR(dly) to program delay */ + mcr p15, 7, r0, c15, c0, 6 + +/* Read r0=AVSDSCR */ + mrc p15, 7, r0, c15, c0, 6 + +/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */ + mov r3, #0x61 + mcr p15, 7, r3, c15, c1, 7 + + bx lr + + + + .global avs_disable +avs_disable: + +/* Clear AVSCSR */ + mov r0, #0 + +/* Write AVSCSR */ + mcr p15, 7, r0, c15, c1, 7 + + bx lr + + .end + + diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c new file mode 100644 index 00000000000..d53e471536f --- /dev/null +++ b/arch/arm/mach-msm/bam_dmux.c @@ -0,0 +1,2326 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * BAM DMUX module. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define BAM_CH_LOCAL_OPEN 0x1 +#define BAM_CH_REMOTE_OPEN 0x2 +#define BAM_CH_IN_RESET 0x4 + +#define BAM_MUX_HDR_MAGIC_NO 0x33fc + +#define BAM_MUX_HDR_CMD_DATA 0 +#define BAM_MUX_HDR_CMD_OPEN 1 +#define BAM_MUX_HDR_CMD_CLOSE 2 +#define BAM_MUX_HDR_CMD_STATUS 3 /* unused */ +#define BAM_MUX_HDR_CMD_OPEN_NO_A2_PC 4 + +#define POLLING_MIN_SLEEP 950 /* 0.95 ms */ +#define POLLING_MAX_SLEEP 1050 /* 1.05 ms */ +#define POLLING_INACTIVITY 40 /* cycles before switch to intr mode */ + +#define LOW_WATERMARK 2 +#define HIGH_WATERMARK 4 + +static int msm_bam_dmux_debug_enable; +module_param_named(debug_enable, msm_bam_dmux_debug_enable, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(DEBUG) +static uint32_t bam_dmux_read_cnt; +static uint32_t bam_dmux_write_cnt; +static uint32_t bam_dmux_write_cpy_cnt; +static uint32_t bam_dmux_write_cpy_bytes; +static uint32_t bam_dmux_tx_sps_failure_cnt; +static uint32_t bam_dmux_tx_stall_cnt; +static atomic_t bam_dmux_ack_out_cnt = ATOMIC_INIT(0); +static atomic_t bam_dmux_ack_in_cnt = ATOMIC_INIT(0); +static atomic_t bam_dmux_a2_pwr_cntl_in_cnt = ATOMIC_INIT(0); + +#define DBG(x...) do { \ + if (msm_bam_dmux_debug_enable) \ + pr_debug(x); \ + } while (0) + +#define DBG_INC_READ_CNT(x) do { \ + bam_dmux_read_cnt += (x); \ + if (msm_bam_dmux_debug_enable) \ + pr_debug("%s: total read bytes %u\n", \ + __func__, bam_dmux_read_cnt); \ + } while (0) + +#define DBG_INC_WRITE_CNT(x) do { \ + bam_dmux_write_cnt += (x); \ + if (msm_bam_dmux_debug_enable) \ + pr_debug("%s: total written bytes %u\n", \ + __func__, bam_dmux_write_cnt); \ + } while (0) + +#define DBG_INC_WRITE_CPY(x) do { \ + bam_dmux_write_cpy_bytes += (x); \ + bam_dmux_write_cpy_cnt++; \ + if (msm_bam_dmux_debug_enable) \ + pr_debug("%s: total write copy cnt %u, bytes %u\n", \ + __func__, bam_dmux_write_cpy_cnt, \ + bam_dmux_write_cpy_bytes); \ + } while (0) + +#define DBG_INC_TX_SPS_FAILURE_CNT() do { \ + bam_dmux_tx_sps_failure_cnt++; \ +} while (0) + +#define DBG_INC_TX_STALL_CNT() do { \ + bam_dmux_tx_stall_cnt++; \ +} while (0) + +#define DBG_INC_ACK_OUT_CNT() \ + atomic_inc(&bam_dmux_ack_out_cnt) + +#define DBG_INC_A2_POWER_CONTROL_IN_CNT() \ + atomic_inc(&bam_dmux_a2_pwr_cntl_in_cnt) + +#define DBG_INC_ACK_IN_CNT() \ + atomic_inc(&bam_dmux_ack_in_cnt) +#else +#define DBG(x...) do { } while (0) +#define DBG_INC_READ_CNT(x...) do { } while (0) +#define DBG_INC_WRITE_CNT(x...) do { } while (0) +#define DBG_INC_WRITE_CPY(x...) do { } while (0) +#define DBG_INC_TX_SPS_FAILURE_CNT() do { } while (0) +#define DBG_INC_TX_STALL_CNT() do { } while (0) +#define DBG_INC_ACK_OUT_CNT() do { } while (0) +#define DBG_INC_A2_POWER_CONTROL_IN_CNT() \ + do { } while (0) +#define DBG_INC_ACK_IN_CNT() do { } while (0) +#endif + +struct bam_ch_info { + uint32_t status; + void (*notify)(void *, int, unsigned long); + void *priv; + spinlock_t lock; + struct platform_device *pdev; + char name[BAM_DMUX_CH_NAME_MAX_LEN]; + int num_tx_pkts; + int use_wm; +}; + +struct tx_pkt_info { + struct sk_buff *skb; + dma_addr_t dma_address; + char is_cmd; + uint32_t len; + struct work_struct work; + struct list_head list_node; + unsigned ts_sec; + unsigned long ts_nsec; +}; + +struct rx_pkt_info { + struct sk_buff *skb; + dma_addr_t dma_address; + struct work_struct work; + struct list_head list_node; +}; + +#define A2_NUM_PIPES 6 +#define A2_SUMMING_THRESHOLD 4096 +#define A2_DEFAULT_DESCRIPTORS 32 +#define A2_PHYS_BASE 0x124C2000 +#define A2_PHYS_SIZE 0x2000 +#define BUFFER_SIZE 2048 +#define NUM_BUFFERS 32 +static struct sps_bam_props a2_props; +static u32 a2_device_handle; +static struct sps_pipe *bam_tx_pipe; +static struct sps_pipe *bam_rx_pipe; +static struct sps_connect tx_connection; +static struct sps_connect rx_connection; +static struct sps_mem_buffer tx_desc_mem_buf; +static struct sps_mem_buffer rx_desc_mem_buf; +static struct sps_register_event tx_register_event; +static struct sps_register_event rx_register_event; + +static struct bam_ch_info bam_ch[BAM_DMUX_NUM_CHANNELS]; +static int bam_mux_initialized; + +static int polling_mode; + +static LIST_HEAD(bam_rx_pool); +static DEFINE_MUTEX(bam_rx_pool_mutexlock); +static int bam_rx_pool_len; +static LIST_HEAD(bam_tx_pool); +static DEFINE_SPINLOCK(bam_tx_pool_spinlock); + +struct bam_mux_hdr { + uint16_t magic_num; + uint8_t reserved; + uint8_t cmd; + uint8_t pad_len; + uint8_t ch_id; + uint16_t pkt_len; +}; + +static void notify_all(int event, unsigned long data); +static void bam_mux_write_done(struct work_struct *work); +static void handle_bam_mux_cmd(struct work_struct *work); +static void rx_timer_work_func(struct work_struct *work); + +static DECLARE_WORK(rx_timer_work, rx_timer_work_func); + +static struct workqueue_struct *bam_mux_rx_workqueue; +static struct workqueue_struct *bam_mux_tx_workqueue; + +/* A2 power collaspe */ +#define UL_TIMEOUT_DELAY 1000 /* in ms */ +#define ENABLE_DISCONNECT_ACK 0x1 +static void toggle_apps_ack(void); +static void reconnect_to_bam(void); +static void disconnect_to_bam(void); +static void ul_wakeup(void); +static void ul_timeout(struct work_struct *work); +static void vote_dfab(void); +static void unvote_dfab(void); +static void kickoff_ul_wakeup_func(struct work_struct *work); +static void grab_wakelock(void); +static void release_wakelock(void); + +static int bam_is_connected; +static DEFINE_MUTEX(wakeup_lock); +static struct completion ul_wakeup_ack_completion; +static struct completion bam_connection_completion; +static struct delayed_work ul_timeout_work; +static int ul_packet_written; +static atomic_t ul_ondemand_vote = ATOMIC_INIT(0); +static struct clk *dfab_clk, *xo_clk; +static DEFINE_RWLOCK(ul_wakeup_lock); +static DECLARE_WORK(kickoff_ul_wakeup, kickoff_ul_wakeup_func); +static int bam_connection_is_active; +static int wait_for_ack; +static struct wake_lock bam_wakelock; +static int a2_pc_disabled; +static DEFINE_MUTEX(dfab_status_lock); +static int dfab_is_on; +static int wait_for_dfab; +static struct completion dfab_unvote_completion; +static DEFINE_SPINLOCK(wakelock_reference_lock); +static int wakelock_reference_count; +static int a2_pc_disabled_wakelock_skipped; +static int disconnect_ack; +static LIST_HEAD(bam_other_notify_funcs); +static DEFINE_MUTEX(smsm_cb_lock); +static DEFINE_MUTEX(delayed_ul_vote_lock); +static int need_delayed_ul_vote; + +struct outside_notify_func { + void (*notify)(void *, int, unsigned long); + void *priv; + struct list_head list_node; +}; +/* End A2 power collaspe */ + +/* subsystem restart */ +static int restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data); + +static struct notifier_block restart_notifier = { + .notifier_call = restart_notifier_cb, +}; +static int in_global_reset; +/* end subsystem restart */ + +#define bam_ch_is_open(x) \ + (bam_ch[(x)].status == (BAM_CH_LOCAL_OPEN | BAM_CH_REMOTE_OPEN)) + +#define bam_ch_is_local_open(x) \ + (bam_ch[(x)].status & BAM_CH_LOCAL_OPEN) + +#define bam_ch_is_remote_open(x) \ + (bam_ch[(x)].status & BAM_CH_REMOTE_OPEN) + +#define bam_ch_is_in_reset(x) \ + (bam_ch[(x)].status & BAM_CH_IN_RESET) + +#define LOG_MESSAGE_MAX_SIZE 80 +struct kfifo bam_dmux_state_log; +static uint32_t bam_dmux_state_logging_disabled; +static DEFINE_SPINLOCK(bam_dmux_logging_spinlock); +static int bam_dmux_uplink_vote; +static int bam_dmux_power_state; + + +#define DMUX_LOG_KERR(fmt...) \ +do { \ + bam_dmux_log(fmt); \ + pr_err(fmt); \ +} while (0) + +/** + * Log a state change along with a small message. + * + * Complete size of messsage is limited to @todo. + */ +static void bam_dmux_log(const char *fmt, ...) +{ + char buff[LOG_MESSAGE_MAX_SIZE]; + unsigned long flags; + va_list arg_list; + unsigned long long t_now; + unsigned long nanosec_rem; + int len = 0; + + if (bam_dmux_state_logging_disabled) + return; + + t_now = sched_clock(); + nanosec_rem = do_div(t_now, 1000000000U); + + /* + * States + * D: 1 = Power collapse disabled + * R: 1 = in global reset + * P: 1 = BAM is powered up + * A: 1 = BAM initialized and ready for data + * + * V: 1 = Uplink vote for power + * U: 1 = Uplink active + * W: 1 = Uplink Wait-for-ack + * A: 1 = Uplink ACK received + * #: >=1 On-demand uplink vote + * D: 1 = Disconnect ACK active + */ + len += scnprintf(buff, sizeof(buff), + " %u.%09lu %c%c%c%c %c%c%c%c%d%c ", + (unsigned)t_now, nanosec_rem, + a2_pc_disabled ? 'D' : 'd', + in_global_reset ? 'R' : 'r', + bam_dmux_power_state ? 'P' : 'p', + bam_connection_is_active ? 'A' : 'a', + bam_dmux_uplink_vote ? 'V' : 'v', + bam_is_connected ? 'U' : 'u', + wait_for_ack ? 'W' : 'w', + ul_wakeup_ack_completion.done ? 'A' : 'a', + atomic_read(&ul_ondemand_vote), + disconnect_ack ? 'D' : 'd' + ); + + va_start(arg_list, fmt); + len += vscnprintf(buff + len, sizeof(buff) - len, fmt, arg_list); + va_end(arg_list); + memset(buff + len, 0x0, sizeof(buff) - len); + + spin_lock_irqsave(&bam_dmux_logging_spinlock, flags); + if (kfifo_avail(&bam_dmux_state_log) < LOG_MESSAGE_MAX_SIZE) { + char junk[LOG_MESSAGE_MAX_SIZE]; + int ret; + + ret = kfifo_out(&bam_dmux_state_log, junk, sizeof(junk)); + if (ret != LOG_MESSAGE_MAX_SIZE) { + pr_err("%s: unable to empty log %d\n", __func__, ret); + spin_unlock_irqrestore(&bam_dmux_logging_spinlock, + flags); + return; + } + } + kfifo_in(&bam_dmux_state_log, buff, sizeof(buff)); + spin_unlock_irqrestore(&bam_dmux_logging_spinlock, flags); +} + +static inline void set_tx_timestamp(struct tx_pkt_info *pkt) +{ + unsigned long long t_now; + + t_now = sched_clock(); + pkt->ts_nsec = do_div(t_now, 1000000000U); + pkt->ts_sec = (unsigned)t_now; +} + +static inline void verify_tx_queue_is_empty(const char *func) +{ + unsigned long flags; + struct tx_pkt_info *info; + int reported = 0; + + spin_lock_irqsave(&bam_tx_pool_spinlock, flags); + list_for_each_entry(info, &bam_tx_pool, list_node) { + if (!reported) { + bam_dmux_log("%s: tx pool not empty\n", func); + if (!in_global_reset) + pr_err("%s: tx pool not empty\n", func); + reported = 1; + } + bam_dmux_log("%s: node=%p ts=%u.%09lu\n", __func__, + &info->list_node, info->ts_sec, info->ts_nsec); + if (!in_global_reset) + pr_err("%s: node=%p ts=%u.%09lu\n", __func__, + &info->list_node, info->ts_sec, info->ts_nsec); + } + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); +} + +static void queue_rx(void) +{ + void *ptr; + struct rx_pkt_info *info; + int ret; + int rx_len_cached; + + mutex_lock(&bam_rx_pool_mutexlock); + rx_len_cached = bam_rx_pool_len; + mutex_unlock(&bam_rx_pool_mutexlock); + + while (rx_len_cached < NUM_BUFFERS) { + if (in_global_reset) + goto fail; + + info = kmalloc(sizeof(struct rx_pkt_info), GFP_KERNEL); + if (!info) { + pr_err("%s: unable to alloc rx_pkt_info\n", __func__); + goto fail; + } + + INIT_WORK(&info->work, handle_bam_mux_cmd); + + info->skb = __dev_alloc_skb(BUFFER_SIZE, GFP_KERNEL); + if (info->skb == NULL) { + DMUX_LOG_KERR("%s: unable to alloc skb\n", __func__); + goto fail_info; + } + ptr = skb_put(info->skb, BUFFER_SIZE); + + info->dma_address = dma_map_single(NULL, ptr, BUFFER_SIZE, + DMA_FROM_DEVICE); + if (info->dma_address == 0 || info->dma_address == ~0) { + DMUX_LOG_KERR("%s: dma_map_single failure %p for %p\n", + __func__, (void *)info->dma_address, ptr); + goto fail_skb; + } + + mutex_lock(&bam_rx_pool_mutexlock); + list_add_tail(&info->list_node, &bam_rx_pool); + rx_len_cached = ++bam_rx_pool_len; + mutex_unlock(&bam_rx_pool_mutexlock); + + ret = sps_transfer_one(bam_rx_pipe, info->dma_address, + BUFFER_SIZE, info, + SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT); + + if (ret) { + DMUX_LOG_KERR("%s: sps_transfer_one failed %d\n", + __func__, ret); + goto fail_transfer; + } + } + return; + +fail_transfer: + mutex_lock(&bam_rx_pool_mutexlock); + list_del(&info->list_node); + --bam_rx_pool_len; + rx_len_cached = bam_rx_pool_len; + mutex_unlock(&bam_rx_pool_mutexlock); + + dma_unmap_single(NULL, info->dma_address, BUFFER_SIZE, + DMA_FROM_DEVICE); + +fail_skb: + dev_kfree_skb_any(info->skb); + +fail_info: + kfree(info); + +fail: + if (rx_len_cached == 0) { + DMUX_LOG_KERR("%s: RX queue failure\n", __func__); + in_global_reset = 1; + } +} + +static void bam_mux_process_data(struct sk_buff *rx_skb) +{ + unsigned long flags; + struct bam_mux_hdr *rx_hdr; + unsigned long event_data; + + rx_hdr = (struct bam_mux_hdr *)rx_skb->data; + + rx_skb->data = (unsigned char *)(rx_hdr + 1); + rx_skb->tail = rx_skb->data + rx_hdr->pkt_len; + rx_skb->len = rx_hdr->pkt_len; + rx_skb->truesize = rx_hdr->pkt_len + sizeof(struct sk_buff); + + event_data = (unsigned long)(rx_skb); + + spin_lock_irqsave(&bam_ch[rx_hdr->ch_id].lock, flags); + if (bam_ch[rx_hdr->ch_id].notify) + bam_ch[rx_hdr->ch_id].notify( + bam_ch[rx_hdr->ch_id].priv, BAM_DMUX_RECEIVE, + event_data); + else + dev_kfree_skb_any(rx_skb); + spin_unlock_irqrestore(&bam_ch[rx_hdr->ch_id].lock, flags); + + queue_rx(); +} + +static inline void handle_bam_mux_cmd_open(struct bam_mux_hdr *rx_hdr) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&bam_ch[rx_hdr->ch_id].lock, flags); + bam_ch[rx_hdr->ch_id].status |= BAM_CH_REMOTE_OPEN; + bam_ch[rx_hdr->ch_id].num_tx_pkts = 0; + spin_unlock_irqrestore(&bam_ch[rx_hdr->ch_id].lock, flags); + queue_rx(); + ret = platform_device_add(bam_ch[rx_hdr->ch_id].pdev); + if (ret) + pr_err("%s: platform_device_add() error: %d\n", + __func__, ret); +} + +static void handle_bam_mux_cmd(struct work_struct *work) +{ + unsigned long flags; + struct bam_mux_hdr *rx_hdr; + struct rx_pkt_info *info; + struct sk_buff *rx_skb; + + info = container_of(work, struct rx_pkt_info, work); + rx_skb = info->skb; + dma_unmap_single(NULL, info->dma_address, BUFFER_SIZE, DMA_FROM_DEVICE); + kfree(info); + + rx_hdr = (struct bam_mux_hdr *)rx_skb->data; + + DBG_INC_READ_CNT(sizeof(struct bam_mux_hdr)); + DBG("%s: magic %x reserved %d cmd %d pad %d ch %d len %d\n", __func__, + rx_hdr->magic_num, rx_hdr->reserved, rx_hdr->cmd, + rx_hdr->pad_len, rx_hdr->ch_id, rx_hdr->pkt_len); + if (rx_hdr->magic_num != BAM_MUX_HDR_MAGIC_NO) { + DMUX_LOG_KERR("%s: dropping invalid hdr. magic %x" + " reserved %d cmd %d" + " pad %d ch %d len %d\n", __func__, + rx_hdr->magic_num, rx_hdr->reserved, rx_hdr->cmd, + rx_hdr->pad_len, rx_hdr->ch_id, rx_hdr->pkt_len); + dev_kfree_skb_any(rx_skb); + queue_rx(); + return; + } + + if (rx_hdr->ch_id >= BAM_DMUX_NUM_CHANNELS) { + DMUX_LOG_KERR("%s: dropping invalid LCID %d" + " reserved %d cmd %d" + " pad %d ch %d len %d\n", __func__, + rx_hdr->ch_id, rx_hdr->reserved, rx_hdr->cmd, + rx_hdr->pad_len, rx_hdr->ch_id, rx_hdr->pkt_len); + dev_kfree_skb_any(rx_skb); + queue_rx(); + return; + } + + switch (rx_hdr->cmd) { + case BAM_MUX_HDR_CMD_DATA: + DBG_INC_READ_CNT(rx_hdr->pkt_len); + bam_mux_process_data(rx_skb); + break; + case BAM_MUX_HDR_CMD_OPEN: + bam_dmux_log("%s: opening cid %d PC enabled\n", __func__, + rx_hdr->ch_id); + handle_bam_mux_cmd_open(rx_hdr); + if (rx_hdr->reserved & ENABLE_DISCONNECT_ACK) { + bam_dmux_log("%s: activating disconnect ack\n"); + disconnect_ack = 1; + } + dev_kfree_skb_any(rx_skb); + break; + case BAM_MUX_HDR_CMD_OPEN_NO_A2_PC: + bam_dmux_log("%s: opening cid %d PC disabled\n", __func__, + rx_hdr->ch_id); + + if (!a2_pc_disabled) { + a2_pc_disabled = 1; + ul_wakeup(); + } + + handle_bam_mux_cmd_open(rx_hdr); + dev_kfree_skb_any(rx_skb); + break; + case BAM_MUX_HDR_CMD_CLOSE: + /* probably should drop pending write */ + bam_dmux_log("%s: closing cid %d\n", __func__, + rx_hdr->ch_id); + spin_lock_irqsave(&bam_ch[rx_hdr->ch_id].lock, flags); + bam_ch[rx_hdr->ch_id].status &= ~BAM_CH_REMOTE_OPEN; + spin_unlock_irqrestore(&bam_ch[rx_hdr->ch_id].lock, flags); + queue_rx(); + platform_device_unregister(bam_ch[rx_hdr->ch_id].pdev); + bam_ch[rx_hdr->ch_id].pdev = + platform_device_alloc(bam_ch[rx_hdr->ch_id].name, 2); + if (!bam_ch[rx_hdr->ch_id].pdev) + pr_err("%s: platform_device_alloc failed\n", __func__); + dev_kfree_skb_any(rx_skb); + break; + default: + DMUX_LOG_KERR("%s: dropping invalid hdr. magic %x" + " reserved %d cmd %d pad %d ch %d len %d\n", + __func__, rx_hdr->magic_num, rx_hdr->reserved, + rx_hdr->cmd, rx_hdr->pad_len, rx_hdr->ch_id, + rx_hdr->pkt_len); + dev_kfree_skb_any(rx_skb); + queue_rx(); + return; + } +} + +static int bam_mux_write_cmd(void *data, uint32_t len) +{ + int rc; + struct tx_pkt_info *pkt; + dma_addr_t dma_address; + unsigned long flags; + + pkt = kmalloc(sizeof(struct tx_pkt_info), GFP_ATOMIC); + if (pkt == NULL) { + pr_err("%s: mem alloc for tx_pkt_info failed\n", __func__); + rc = -ENOMEM; + return rc; + } + + dma_address = dma_map_single(NULL, data, len, + DMA_TO_DEVICE); + if (!dma_address) { + pr_err("%s: dma_map_single() failed\n", __func__); + kfree(pkt); + rc = -ENOMEM; + return rc; + } + pkt->skb = (struct sk_buff *)(data); + pkt->len = len; + pkt->dma_address = dma_address; + pkt->is_cmd = 1; + set_tx_timestamp(pkt); + INIT_WORK(&pkt->work, bam_mux_write_done); + spin_lock_irqsave(&bam_tx_pool_spinlock, flags); + list_add_tail(&pkt->list_node, &bam_tx_pool); + rc = sps_transfer_one(bam_tx_pipe, dma_address, len, + pkt, SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT); + if (rc) { + DMUX_LOG_KERR("%s sps_transfer_one failed rc=%d\n", + __func__, rc); + list_del(&pkt->list_node); + DBG_INC_TX_SPS_FAILURE_CNT(); + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + dma_unmap_single(NULL, pkt->dma_address, + pkt->len, + DMA_TO_DEVICE); + kfree(pkt); + } else { + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + } + + ul_packet_written = 1; + return rc; +} + +static void bam_mux_write_done(struct work_struct *work) +{ + struct sk_buff *skb; + struct bam_mux_hdr *hdr; + struct tx_pkt_info *info; + struct tx_pkt_info *info_expected; + unsigned long event_data; + unsigned long flags; + + if (in_global_reset) + return; + + info = container_of(work, struct tx_pkt_info, work); + + spin_lock_irqsave(&bam_tx_pool_spinlock, flags); + info_expected = list_first_entry(&bam_tx_pool, + struct tx_pkt_info, list_node); + if (unlikely(info != info_expected)) { + struct tx_pkt_info *errant_pkt; + + DMUX_LOG_KERR("%s: bam_tx_pool mismatch .next=%p," + " list_node=%p, ts=%u.%09lu\n", + __func__, bam_tx_pool.next, &info->list_node, + info->ts_sec, info->ts_nsec + ); + + list_for_each_entry(errant_pkt, &bam_tx_pool, list_node) { + DMUX_LOG_KERR("%s: node=%p ts=%u.%09lu\n", __func__, + &errant_pkt->list_node, errant_pkt->ts_sec, + errant_pkt->ts_nsec); + + } + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + BUG(); + } + list_del(&info->list_node); + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + + if (info->is_cmd) { + kfree(info->skb); + kfree(info); + return; + } + skb = info->skb; + kfree(info); + hdr = (struct bam_mux_hdr *)skb->data; + DBG_INC_WRITE_CNT(skb->len); + event_data = (unsigned long)(skb); + spin_lock_irqsave(&bam_ch[hdr->ch_id].lock, flags); + bam_ch[hdr->ch_id].num_tx_pkts--; + spin_unlock_irqrestore(&bam_ch[hdr->ch_id].lock, flags); + if (bam_ch[hdr->ch_id].notify) + bam_ch[hdr->ch_id].notify( + bam_ch[hdr->ch_id].priv, BAM_DMUX_WRITE_DONE, + event_data); + else + dev_kfree_skb_any(skb); +} + +int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb) +{ + int rc = 0; + struct bam_mux_hdr *hdr; + unsigned long flags; + struct sk_buff *new_skb = NULL; + dma_addr_t dma_address; + struct tx_pkt_info *pkt; + + if (id >= BAM_DMUX_NUM_CHANNELS) + return -EINVAL; + if (!skb) + return -EINVAL; + if (!bam_mux_initialized) + return -ENODEV; + + DBG("%s: writing to ch %d len %d\n", __func__, id, skb->len); + spin_lock_irqsave(&bam_ch[id].lock, flags); + if (!bam_ch_is_open(id)) { + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status); + return -ENODEV; + } + + if (bam_ch[id].use_wm && + (bam_ch[id].num_tx_pkts >= HIGH_WATERMARK)) { + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + pr_err("%s: watermark exceeded: %d\n", __func__, id); + return -EAGAIN; + } + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + + read_lock(&ul_wakeup_lock); + if (!bam_is_connected) { + read_unlock(&ul_wakeup_lock); + ul_wakeup(); + if (unlikely(in_global_reset == 1)) + return -EFAULT; + read_lock(&ul_wakeup_lock); + notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL)); + } + + /* if skb do not have any tailroom for padding, + copy the skb into a new expanded skb */ + if ((skb->len & 0x3) && (skb_tailroom(skb) < (4 - (skb->len & 0x3)))) { + /* revisit, probably dev_alloc_skb and memcpy is effecient */ + new_skb = skb_copy_expand(skb, skb_headroom(skb), + 4 - (skb->len & 0x3), GFP_ATOMIC); + if (new_skb == NULL) { + pr_err("%s: cannot allocate skb\n", __func__); + goto write_fail; + } + dev_kfree_skb_any(skb); + skb = new_skb; + DBG_INC_WRITE_CPY(skb->len); + } + + hdr = (struct bam_mux_hdr *)skb_push(skb, sizeof(struct bam_mux_hdr)); + + /* caller should allocate for hdr and padding + hdr is fine, padding is tricky */ + hdr->magic_num = BAM_MUX_HDR_MAGIC_NO; + hdr->cmd = BAM_MUX_HDR_CMD_DATA; + hdr->reserved = 0; + hdr->ch_id = id; + hdr->pkt_len = skb->len - sizeof(struct bam_mux_hdr); + if (skb->len & 0x3) + skb_put(skb, 4 - (skb->len & 0x3)); + + hdr->pad_len = skb->len - (sizeof(struct bam_mux_hdr) + hdr->pkt_len); + + DBG("%s: data %p, tail %p skb len %d pkt len %d pad len %d\n", + __func__, skb->data, skb->tail, skb->len, + hdr->pkt_len, hdr->pad_len); + + pkt = kmalloc(sizeof(struct tx_pkt_info), GFP_ATOMIC); + if (pkt == NULL) { + pr_err("%s: mem alloc for tx_pkt_info failed\n", __func__); + goto write_fail2; + } + + dma_address = dma_map_single(NULL, skb->data, skb->len, + DMA_TO_DEVICE); + if (!dma_address) { + pr_err("%s: dma_map_single() failed\n", __func__); + goto write_fail3; + } + pkt->skb = skb; + pkt->dma_address = dma_address; + pkt->is_cmd = 0; + set_tx_timestamp(pkt); + INIT_WORK(&pkt->work, bam_mux_write_done); + spin_lock_irqsave(&bam_tx_pool_spinlock, flags); + list_add_tail(&pkt->list_node, &bam_tx_pool); + rc = sps_transfer_one(bam_tx_pipe, dma_address, skb->len, + pkt, SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT); + if (rc) { + DMUX_LOG_KERR("%s sps_transfer_one failed rc=%d\n", + __func__, rc); + list_del(&pkt->list_node); + DBG_INC_TX_SPS_FAILURE_CNT(); + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + dma_unmap_single(NULL, pkt->dma_address, + pkt->skb->len, DMA_TO_DEVICE); + kfree(pkt); + if (new_skb) + dev_kfree_skb_any(new_skb); + } else { + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + spin_lock_irqsave(&bam_ch[id].lock, flags); + bam_ch[id].num_tx_pkts++; + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + } + ul_packet_written = 1; + read_unlock(&ul_wakeup_lock); + return rc; + +write_fail3: + kfree(pkt); +write_fail2: + if (new_skb) + dev_kfree_skb_any(new_skb); +write_fail: + read_unlock(&ul_wakeup_lock); + return -ENOMEM; +} + +int msm_bam_dmux_open(uint32_t id, void *priv, + void (*notify)(void *, int, unsigned long)) +{ + struct bam_mux_hdr *hdr; + unsigned long flags; + int rc = 0; + + DBG("%s: opening ch %d\n", __func__, id); + if (!bam_mux_initialized) { + DBG("%s: not inititialized\n", __func__); + return -ENODEV; + } + if (id >= BAM_DMUX_NUM_CHANNELS) { + pr_err("%s: invalid channel id %d\n", __func__, id); + return -EINVAL; + } + if (notify == NULL) { + pr_err("%s: notify function is NULL\n", __func__); + return -EINVAL; + } + + hdr = kmalloc(sizeof(struct bam_mux_hdr), GFP_KERNEL); + if (hdr == NULL) { + pr_err("%s: hdr kmalloc failed. ch: %d\n", __func__, id); + return -ENOMEM; + } + spin_lock_irqsave(&bam_ch[id].lock, flags); + if (bam_ch_is_open(id)) { + DBG("%s: Already opened %d\n", __func__, id); + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + kfree(hdr); + goto open_done; + } + if (!bam_ch_is_remote_open(id)) { + DBG("%s: Remote not open; ch: %d\n", __func__, id); + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + kfree(hdr); + return -ENODEV; + } + + bam_ch[id].notify = notify; + bam_ch[id].priv = priv; + bam_ch[id].status |= BAM_CH_LOCAL_OPEN; + bam_ch[id].num_tx_pkts = 0; + bam_ch[id].use_wm = 0; + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + + read_lock(&ul_wakeup_lock); + if (!bam_is_connected) { + read_unlock(&ul_wakeup_lock); + ul_wakeup(); + if (unlikely(in_global_reset == 1)) + return -EFAULT; + read_lock(&ul_wakeup_lock); + notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL)); + } + + hdr->magic_num = BAM_MUX_HDR_MAGIC_NO; + hdr->cmd = BAM_MUX_HDR_CMD_OPEN; + hdr->reserved = 0; + hdr->ch_id = id; + hdr->pkt_len = 0; + hdr->pad_len = 0; + + rc = bam_mux_write_cmd((void *)hdr, sizeof(struct bam_mux_hdr)); + read_unlock(&ul_wakeup_lock); + +open_done: + DBG("%s: opened ch %d\n", __func__, id); + return rc; +} + +int msm_bam_dmux_close(uint32_t id) +{ + struct bam_mux_hdr *hdr; + unsigned long flags; + int rc; + + if (id >= BAM_DMUX_NUM_CHANNELS) + return -EINVAL; + DBG("%s: closing ch %d\n", __func__, id); + if (!bam_mux_initialized) + return -ENODEV; + + read_lock(&ul_wakeup_lock); + if (!bam_is_connected && !bam_ch_is_in_reset(id)) { + read_unlock(&ul_wakeup_lock); + ul_wakeup(); + if (unlikely(in_global_reset == 1)) + return -EFAULT; + read_lock(&ul_wakeup_lock); + notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL)); + } + + spin_lock_irqsave(&bam_ch[id].lock, flags); + bam_ch[id].notify = NULL; + bam_ch[id].priv = NULL; + bam_ch[id].status &= ~BAM_CH_LOCAL_OPEN; + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + + if (bam_ch_is_in_reset(id)) { + read_unlock(&ul_wakeup_lock); + bam_ch[id].status &= ~BAM_CH_IN_RESET; + return 0; + } + + hdr = kmalloc(sizeof(struct bam_mux_hdr), GFP_ATOMIC); + if (hdr == NULL) { + pr_err("%s: hdr kmalloc failed. ch: %d\n", __func__, id); + read_unlock(&ul_wakeup_lock); + return -ENOMEM; + } + hdr->magic_num = BAM_MUX_HDR_MAGIC_NO; + hdr->cmd = BAM_MUX_HDR_CMD_CLOSE; + hdr->reserved = 0; + hdr->ch_id = id; + hdr->pkt_len = 0; + hdr->pad_len = 0; + + rc = bam_mux_write_cmd((void *)hdr, sizeof(struct bam_mux_hdr)); + read_unlock(&ul_wakeup_lock); + + DBG("%s: closed ch %d\n", __func__, id); + return rc; +} + +int msm_bam_dmux_is_ch_full(uint32_t id) +{ + unsigned long flags; + int ret; + + if (id >= BAM_DMUX_NUM_CHANNELS) + return -EINVAL; + + spin_lock_irqsave(&bam_ch[id].lock, flags); + bam_ch[id].use_wm = 1; + ret = bam_ch[id].num_tx_pkts >= HIGH_WATERMARK; + DBG("%s: ch %d num tx pkts=%d, HWM=%d\n", __func__, + id, bam_ch[id].num_tx_pkts, ret); + if (!bam_ch_is_local_open(id)) { + ret = -ENODEV; + pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status); + } + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + + return ret; +} + +int msm_bam_dmux_is_ch_low(uint32_t id) +{ + unsigned long flags; + int ret; + + if (id >= BAM_DMUX_NUM_CHANNELS) + return -EINVAL; + + spin_lock_irqsave(&bam_ch[id].lock, flags); + bam_ch[id].use_wm = 1; + ret = bam_ch[id].num_tx_pkts <= LOW_WATERMARK; + DBG("%s: ch %d num tx pkts=%d, LWM=%d\n", __func__, + id, bam_ch[id].num_tx_pkts, ret); + if (!bam_ch_is_local_open(id)) { + ret = -ENODEV; + pr_err("%s: port not open: %d\n", __func__, bam_ch[id].status); + } + spin_unlock_irqrestore(&bam_ch[id].lock, flags); + + return ret; +} + +static void rx_switch_to_interrupt_mode(void) +{ + struct sps_connect cur_rx_conn; + struct sps_iovec iov; + struct rx_pkt_info *info; + int ret; + + /* + * Attempt to enable interrupts - if this fails, + * continue polling and we will retry later. + */ + ret = sps_get_config(bam_rx_pipe, &cur_rx_conn); + if (ret) { + pr_err("%s: sps_get_config() failed %d\n", __func__, ret); + goto fail; + } + + rx_register_event.options = SPS_O_EOT; + ret = sps_register_event(bam_rx_pipe, &rx_register_event); + if (ret) { + pr_err("%s: sps_register_event() failed %d\n", __func__, ret); + goto fail; + } + + cur_rx_conn.options = SPS_O_AUTO_ENABLE | + SPS_O_EOT | SPS_O_ACK_TRANSFERS; + ret = sps_set_config(bam_rx_pipe, &cur_rx_conn); + if (ret) { + pr_err("%s: sps_set_config() failed %d\n", __func__, ret); + goto fail; + } + polling_mode = 0; + release_wakelock(); + + /* handle any rx packets before interrupt was enabled */ + while (bam_connection_is_active && !polling_mode) { + ret = sps_get_iovec(bam_rx_pipe, &iov); + if (ret) { + pr_err("%s: sps_get_iovec failed %d\n", + __func__, ret); + break; + } + if (iov.addr == 0) + break; + + mutex_lock(&bam_rx_pool_mutexlock); + if (unlikely(list_empty(&bam_rx_pool))) { + mutex_unlock(&bam_rx_pool_mutexlock); + continue; + } + info = list_first_entry(&bam_rx_pool, struct rx_pkt_info, + list_node); + list_del(&info->list_node); + --bam_rx_pool_len; + mutex_unlock(&bam_rx_pool_mutexlock); + if (info->dma_address != iov.addr) + DMUX_LOG_KERR("%s: iovec %p != dma %p\n", + __func__, + (void *)info->dma_address, (void *)iov.addr); + handle_bam_mux_cmd(&info->work); + } + return; + +fail: + pr_err("%s: reverting to polling\n", __func__); + queue_work_on(0, bam_mux_rx_workqueue, &rx_timer_work); +} + +static void rx_timer_work_func(struct work_struct *work) +{ + struct sps_iovec iov; + struct rx_pkt_info *info; + int inactive_cycles = 0; + int ret; + + while (bam_connection_is_active) { /* timer loop */ + ++inactive_cycles; + while (bam_connection_is_active) { /* deplete queue loop */ + if (in_global_reset) + return; + + ret = sps_get_iovec(bam_rx_pipe, &iov); + if (ret) { + pr_err("%s: sps_get_iovec failed %d\n", + __func__, ret); + break; + } + if (iov.addr == 0) + break; + inactive_cycles = 0; + mutex_lock(&bam_rx_pool_mutexlock); + if (unlikely(list_empty(&bam_rx_pool))) { + mutex_unlock(&bam_rx_pool_mutexlock); + continue; + } + info = list_first_entry(&bam_rx_pool, + struct rx_pkt_info, list_node); + --bam_rx_pool_len; + list_del(&info->list_node); + mutex_unlock(&bam_rx_pool_mutexlock); + handle_bam_mux_cmd(&info->work); + } + + if (inactive_cycles == POLLING_INACTIVITY) { + rx_switch_to_interrupt_mode(); + break; + } + + usleep_range(POLLING_MIN_SLEEP, POLLING_MAX_SLEEP); + } +} + +static void bam_mux_tx_notify(struct sps_event_notify *notify) +{ + struct tx_pkt_info *pkt; + + DBG("%s: event %d notified\n", __func__, notify->event_id); + + if (in_global_reset) + return; + + switch (notify->event_id) { + case SPS_EVENT_EOT: + pkt = notify->data.transfer.user; + if (!pkt->is_cmd) + dma_unmap_single(NULL, pkt->dma_address, + pkt->skb->len, + DMA_TO_DEVICE); + else + dma_unmap_single(NULL, pkt->dma_address, + pkt->len, + DMA_TO_DEVICE); + queue_work(bam_mux_tx_workqueue, &pkt->work); + break; + default: + pr_err("%s: recieved unexpected event id %d\n", __func__, + notify->event_id); + } +} + +static void bam_mux_rx_notify(struct sps_event_notify *notify) +{ + int ret; + struct sps_connect cur_rx_conn; + + DBG("%s: event %d notified\n", __func__, notify->event_id); + + if (in_global_reset) + return; + + switch (notify->event_id) { + case SPS_EVENT_EOT: + /* attempt to disable interrupts in this pipe */ + if (!polling_mode) { + ret = sps_get_config(bam_rx_pipe, &cur_rx_conn); + if (ret) { + pr_err("%s: sps_get_config() failed %d, interrupts" + " not disabled\n", __func__, ret); + break; + } + cur_rx_conn.options = SPS_O_AUTO_ENABLE | + SPS_O_ACK_TRANSFERS | SPS_O_POLL; + ret = sps_set_config(bam_rx_pipe, &cur_rx_conn); + if (ret) { + pr_err("%s: sps_set_config() failed %d, interrupts" + " not disabled\n", __func__, ret); + break; + } + grab_wakelock(); + polling_mode = 1; + /* + * run on core 0 so that netif_rx() in rmnet uses only + * one queue + */ + queue_work_on(0, bam_mux_rx_workqueue, &rx_timer_work); + } + break; + default: + pr_err("%s: recieved unexpected event id %d\n", __func__, + notify->event_id); + } +} + +#ifdef CONFIG_DEBUG_FS + +static int debug_tbl(char *buf, int max) +{ + int i = 0; + int j; + + for (j = 0; j < BAM_DMUX_NUM_CHANNELS; ++j) { + i += scnprintf(buf + i, max - i, + "ch%02d local open=%s remote open=%s\n", + j, bam_ch_is_local_open(j) ? "Y" : "N", + bam_ch_is_remote_open(j) ? "Y" : "N"); + } + + return i; +} + +static int debug_ul_pkt_cnt(char *buf, int max) +{ + struct list_head *p; + unsigned long flags; + int n = 0; + + spin_lock_irqsave(&bam_tx_pool_spinlock, flags); + __list_for_each(p, &bam_tx_pool) { + ++n; + } + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + + return scnprintf(buf, max, "Number of UL packets in flight: %d\n", n); +} + +static int debug_stats(char *buf, int max) +{ + int i = 0; + + i += scnprintf(buf + i, max - i, + "skb read cnt: %u\n" + "skb write cnt: %u\n" + "skb copy cnt: %u\n" + "skb copy bytes: %u\n" + "sps tx failures: %u\n" + "sps tx stalls: %u\n" + "rx queue len: %d\n" + "a2 ack out cnt: %d\n" + "a2 ack in cnt: %d\n" + "a2 pwr cntl in: %d\n", + bam_dmux_read_cnt, + bam_dmux_write_cnt, + bam_dmux_write_cpy_cnt, + bam_dmux_write_cpy_bytes, + bam_dmux_tx_sps_failure_cnt, + bam_dmux_tx_stall_cnt, + bam_rx_pool_len, + atomic_read(&bam_dmux_ack_out_cnt), + atomic_read(&bam_dmux_ack_in_cnt), + atomic_read(&bam_dmux_a2_pwr_cntl_in_cnt) + ); + + return i; +} + +static int debug_log(char *buff, int max, loff_t *ppos) +{ + unsigned long flags; + int i = 0; + + if (bam_dmux_state_logging_disabled) { + i += scnprintf(buff - i, max - i, "Logging disabled\n"); + return i; + } + + if (*ppos == 0) { + i += scnprintf(buff - i, max - i, + " timestamp FLAGS [Message]\n" + "FLAGS:\n" + "\tD: 1 = Power collapse disabled\n" + "\tR: 1 = in global reset\n" + "\tP: 1 = BAM is powered up\n" + "\tA: 1 = BAM initialized and ready for data\n" + "\n" + "\tV: 1 = Uplink vote for power\n" + "\tU: 1 = Uplink active\n" + "\tW: 1 = Uplink Wait-for-ack\n" + "\tA: 1 = Uplink ACK received\n" + "\t#: >=1 On-demand uplink vote\n" + "\tD: 1 = Disconnect ACK active\n" + ); + buff += i; + } + + spin_lock_irqsave(&bam_dmux_logging_spinlock, flags); + while (kfifo_len(&bam_dmux_state_log) + && (i + LOG_MESSAGE_MAX_SIZE) < max) { + int k_len; + k_len = kfifo_out(&bam_dmux_state_log, + buff, LOG_MESSAGE_MAX_SIZE); + if (k_len != LOG_MESSAGE_MAX_SIZE) { + pr_err("%s: retrieve failure %d\n", __func__, k_len); + break; + } + + /* keep non-null portion of string and add line break */ + k_len = strnlen(buff, LOG_MESSAGE_MAX_SIZE); + buff += k_len; + i += k_len; + if (k_len && *(buff - 1) != '\n') { + *buff++ = '\n'; + ++i; + } + } + spin_unlock_irqrestore(&bam_dmux_logging_spinlock, flags); + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static ssize_t debug_read_multiple(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + int (*util_func)(char *buf, int max, loff_t *) = file->private_data; + char *buffer; + int bsize; + + buffer = kmalloc(count, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + bsize = util_func(buffer, count, ppos); + + if (bsize >= 0) { + if (copy_to_user(buff, buffer, bsize)) { + kfree(buffer); + return -EFAULT; + } + *ppos += bsize; + } + kfree(buffer); + return bsize; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static const struct file_operations debug_ops_multiple = { + .read = debug_read_multiple, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + struct dentry *file; + + file = debugfs_create_file(name, mode, dent, fill, &debug_ops); + if (IS_ERR(file)) + pr_err("%s: debugfs create failed %d\n", __func__, + (int)PTR_ERR(file)); +} + +static void debug_create_multiple(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max, loff_t *ppos)) +{ + struct dentry *file; + + file = debugfs_create_file(name, mode, dent, fill, &debug_ops_multiple); + if (IS_ERR(file)) + pr_err("%s: debugfs create failed %d\n", __func__, + (int)PTR_ERR(file)); +} +#endif + +static void notify_all(int event, unsigned long data) +{ + int i; + struct list_head *temp; + struct outside_notify_func *func; + + for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) { + if (bam_ch_is_open(i)) { + bam_ch[i].notify(bam_ch[i].priv, event, data); + bam_dmux_log("%s: cid=%d, event=%d, data=%lu\n", + __func__, i, event, data); + } + } + + __list_for_each(temp, &bam_other_notify_funcs) { + func = container_of(temp, struct outside_notify_func, + list_node); + func->notify(func->priv, event, data); + } +} + +static void kickoff_ul_wakeup_func(struct work_struct *work) +{ + read_lock(&ul_wakeup_lock); + if (!bam_is_connected) { + read_unlock(&ul_wakeup_lock); + ul_wakeup(); + if (unlikely(in_global_reset == 1)) + return; + read_lock(&ul_wakeup_lock); + ul_packet_written = 1; + notify_all(BAM_DMUX_UL_CONNECTED, (unsigned long)(NULL)); + } + read_unlock(&ul_wakeup_lock); +} + +int msm_bam_dmux_kickoff_ul_wakeup(void) +{ + int is_connected; + + read_lock(&ul_wakeup_lock); + ul_packet_written = 1; + is_connected = bam_is_connected; + if (!is_connected) + queue_work(bam_mux_tx_workqueue, &kickoff_ul_wakeup); + read_unlock(&ul_wakeup_lock); + + return is_connected; +} + +static void power_vote(int vote) +{ + bam_dmux_log("%s: curr=%d, vote=%d\n", __func__, + bam_dmux_uplink_vote, vote); + + if (bam_dmux_uplink_vote == vote) + bam_dmux_log("%s: warning - duplicate power vote\n", __func__); + + bam_dmux_uplink_vote = vote; + if (vote) + smsm_change_state(SMSM_APPS_STATE, 0, SMSM_A2_POWER_CONTROL); + else + smsm_change_state(SMSM_APPS_STATE, SMSM_A2_POWER_CONTROL, 0); +} + +/* + * @note: Must be called with ul_wakeup_lock locked. + */ +static inline void ul_powerdown(void) +{ + bam_dmux_log("%s: powerdown\n", __func__); + verify_tx_queue_is_empty(__func__); + + if (a2_pc_disabled) { + wait_for_dfab = 1; + INIT_COMPLETION(dfab_unvote_completion); + release_wakelock(); + } else { + wait_for_ack = 1; + INIT_COMPLETION(ul_wakeup_ack_completion); + power_vote(0); + } + bam_is_connected = 0; + notify_all(BAM_DMUX_UL_DISCONNECTED, (unsigned long)(NULL)); +} + +static inline void ul_powerdown_finish(void) +{ + if (a2_pc_disabled && wait_for_dfab) { + unvote_dfab(); + complete_all(&dfab_unvote_completion); + wait_for_dfab = 0; + } +} + +/* + * Votes for UL power and returns current power state. + * + * @returns true if currently connected + */ +int msm_bam_dmux_ul_power_vote(void) +{ + int is_connected; + + read_lock(&ul_wakeup_lock); + atomic_inc(&ul_ondemand_vote); + is_connected = bam_is_connected; + if (!is_connected) + queue_work(bam_mux_tx_workqueue, &kickoff_ul_wakeup); + read_unlock(&ul_wakeup_lock); + + return is_connected; +} + +/* + * Unvotes for UL power. + * + * @returns true if vote count is 0 (UL shutdown possible) + */ +int msm_bam_dmux_ul_power_unvote(void) +{ + int vote; + + read_lock(&ul_wakeup_lock); + vote = atomic_dec_return(&ul_ondemand_vote); + if (unlikely(vote) < 0) + DMUX_LOG_KERR("%s: invalid power vote %d\n", __func__, vote); + read_unlock(&ul_wakeup_lock); + + return vote == 0; +} + +int msm_bam_dmux_reg_notify(void *priv, + void (*notify)(void *priv, int event_type, + unsigned long data)) +{ + struct outside_notify_func *func; + + if (!notify) + return -EINVAL; + + func = kmalloc(sizeof(struct outside_notify_func), GFP_KERNEL); + if (!func) + return -ENOMEM; + + func->notify = notify; + func->priv = priv; + list_add(&func->list_node, &bam_other_notify_funcs); + + return 0; +} + +static void ul_timeout(struct work_struct *work) +{ + unsigned long flags; + int ret; + + if (in_global_reset) + return; + ret = write_trylock_irqsave(&ul_wakeup_lock, flags); + if (!ret) { /* failed to grab lock, reschedule and bail */ + schedule_delayed_work(&ul_timeout_work, + msecs_to_jiffies(UL_TIMEOUT_DELAY)); + return; + } + if (bam_is_connected) { + if (!ul_packet_written) { + spin_lock(&bam_tx_pool_spinlock); + if (!list_empty(&bam_tx_pool)) { + struct tx_pkt_info *info; + + info = list_first_entry(&bam_tx_pool, + struct tx_pkt_info, list_node); + DMUX_LOG_KERR("%s: UL delayed ts=%u.%09lu\n", + __func__, info->ts_sec, info->ts_nsec); + DBG_INC_TX_STALL_CNT(); + ul_packet_written = 1; + } + spin_unlock(&bam_tx_pool_spinlock); + } + + if (ul_packet_written || atomic_read(&ul_ondemand_vote)) { + bam_dmux_log("%s: pkt written %d\n", + __func__, ul_packet_written); + ul_packet_written = 0; + schedule_delayed_work(&ul_timeout_work, + msecs_to_jiffies(UL_TIMEOUT_DELAY)); + } else { + ul_powerdown(); + } + } + write_unlock_irqrestore(&ul_wakeup_lock, flags); + ul_powerdown_finish(); +} + +static int ssrestart_check(void) +{ + DMUX_LOG_KERR("%s: modem timeout: BAM DMUX disabled\n", __func__); + in_global_reset = 1; + if (get_restart_level() <= RESET_SOC) + DMUX_LOG_KERR("%s: ssrestart not enabled\n", __func__); + return 1; +} + +static void ul_wakeup(void) +{ + int ret; + int do_vote_dfab = 0; + + mutex_lock(&wakeup_lock); + if (bam_is_connected) { /* bam got connected before lock grabbed */ + bam_dmux_log("%s Already awake\n", __func__); + mutex_unlock(&wakeup_lock); + return; + } + + /* + * if someone is voting for UL before bam is inited (modem up first + * time), set flag for init to kickoff ul wakeup once bam is inited + */ + mutex_lock(&delayed_ul_vote_lock); + if (unlikely(!bam_mux_initialized)) { + need_delayed_ul_vote = 1; + mutex_unlock(&delayed_ul_vote_lock); + mutex_unlock(&wakeup_lock); + return; + } + mutex_unlock(&delayed_ul_vote_lock); + + if (a2_pc_disabled) { + /* + * don't grab the wakelock the first time because it is + * already grabbed when a2 powers on + */ + if (likely(a2_pc_disabled_wakelock_skipped)) { + grab_wakelock(); + do_vote_dfab = 1; /* vote must occur after wait */ + } else { + a2_pc_disabled_wakelock_skipped = 1; + } + if (wait_for_dfab) { + ret = wait_for_completion_timeout( + &dfab_unvote_completion, HZ); + BUG_ON(ret == 0); + } + if (likely(do_vote_dfab)) + vote_dfab(); + schedule_delayed_work(&ul_timeout_work, + msecs_to_jiffies(UL_TIMEOUT_DELAY)); + bam_is_connected = 1; + mutex_unlock(&wakeup_lock); + return; + } + + /* + * must wait for the previous power down request to have been acked + * chances are it already came in and this will just fall through + * instead of waiting + */ + if (wait_for_ack) { + bam_dmux_log("%s waiting for previous ack\n", __func__); + ret = wait_for_completion_timeout( + &ul_wakeup_ack_completion, HZ); + wait_for_ack = 0; + if (unlikely(ret == 0) && ssrestart_check()) { + mutex_unlock(&wakeup_lock); + bam_dmux_log("%s timeout previous ack\n", __func__); + return; + } + } + INIT_COMPLETION(ul_wakeup_ack_completion); + power_vote(1); + bam_dmux_log("%s waiting for wakeup ack\n", __func__); + ret = wait_for_completion_timeout(&ul_wakeup_ack_completion, HZ); + if (unlikely(ret == 0) && ssrestart_check()) { + mutex_unlock(&wakeup_lock); + bam_dmux_log("%s timeout wakeup ack\n", __func__); + return; + } + bam_dmux_log("%s waiting completion\n", __func__); + ret = wait_for_completion_timeout(&bam_connection_completion, HZ); + if (unlikely(ret == 0) && ssrestart_check()) { + mutex_unlock(&wakeup_lock); + bam_dmux_log("%s timeout power on\n", __func__); + return; + } + + bam_is_connected = 1; + bam_dmux_log("%s complete\n", __func__); + schedule_delayed_work(&ul_timeout_work, + msecs_to_jiffies(UL_TIMEOUT_DELAY)); + mutex_unlock(&wakeup_lock); +} + +static void reconnect_to_bam(void) +{ + int i; + + in_global_reset = 0; + vote_dfab(); + i = sps_device_reset(a2_device_handle); + if (i) + pr_err("%s: device reset failed rc = %d\n", __func__, i); + i = sps_connect(bam_tx_pipe, &tx_connection); + if (i) + pr_err("%s: tx connection failed rc = %d\n", __func__, i); + i = sps_connect(bam_rx_pipe, &rx_connection); + if (i) + pr_err("%s: rx connection failed rc = %d\n", __func__, i); + i = sps_register_event(bam_tx_pipe, &tx_register_event); + if (i) + pr_err("%s: tx event reg failed rc = %d\n", __func__, i); + i = sps_register_event(bam_rx_pipe, &rx_register_event); + if (i) + pr_err("%s: rx event reg failed rc = %d\n", __func__, i); + + bam_connection_is_active = 1; + + if (polling_mode) + rx_switch_to_interrupt_mode(); + + toggle_apps_ack(); + complete_all(&bam_connection_completion); + queue_rx(); +} + +static void disconnect_to_bam(void) +{ + struct list_head *node; + struct rx_pkt_info *info; + unsigned long flags; + + bam_connection_is_active = 0; + + /* handle disconnect during active UL */ + write_lock_irqsave(&ul_wakeup_lock, flags); + if (bam_is_connected) { + bam_dmux_log("%s: UL active - forcing powerdown\n", __func__); + ul_powerdown(); + } + write_unlock_irqrestore(&ul_wakeup_lock, flags); + ul_powerdown_finish(); + + /* tear down BAM connection */ + INIT_COMPLETION(bam_connection_completion); + sps_disconnect(bam_tx_pipe); + sps_disconnect(bam_rx_pipe); + unvote_dfab(); + __memzero(rx_desc_mem_buf.base, rx_desc_mem_buf.size); + __memzero(tx_desc_mem_buf.base, tx_desc_mem_buf.size); + + mutex_lock(&bam_rx_pool_mutexlock); + while (!list_empty(&bam_rx_pool)) { + node = bam_rx_pool.next; + list_del(node); + info = container_of(node, struct rx_pkt_info, list_node); + dma_unmap_single(NULL, info->dma_address, BUFFER_SIZE, + DMA_FROM_DEVICE); + dev_kfree_skb_any(info->skb); + kfree(info); + } + bam_rx_pool_len = 0; + mutex_unlock(&bam_rx_pool_mutexlock); + + if (disconnect_ack) + toggle_apps_ack(); + + verify_tx_queue_is_empty(__func__); +} + +static void vote_dfab(void) +{ + int rc; + + bam_dmux_log("%s\n", __func__); + mutex_lock(&dfab_status_lock); + if (dfab_is_on) { + bam_dmux_log("%s: dfab is already on\n", __func__); + mutex_unlock(&dfab_status_lock); + return; + } + rc = clk_prepare_enable(dfab_clk); + if (rc) + DMUX_LOG_KERR("bam_dmux vote for dfab failed rc = %d\n", rc); + rc = clk_prepare_enable(xo_clk); + if (rc) + DMUX_LOG_KERR("bam_dmux vote for xo failed rc = %d\n", rc); + dfab_is_on = 1; + mutex_unlock(&dfab_status_lock); +} + +static void unvote_dfab(void) +{ + bam_dmux_log("%s\n", __func__); + mutex_lock(&dfab_status_lock); + if (!dfab_is_on) { + DMUX_LOG_KERR("%s: dfab is already off\n", __func__); + dump_stack(); + mutex_unlock(&dfab_status_lock); + return; + } + clk_disable_unprepare(dfab_clk); + clk_disable_unprepare(xo_clk); + dfab_is_on = 0; + mutex_unlock(&dfab_status_lock); +} + +/* reference counting wrapper around wakelock */ +static void grab_wakelock(void) +{ + unsigned long flags; + + spin_lock_irqsave(&wakelock_reference_lock, flags); + bam_dmux_log("%s: ref count = %d\n", __func__, + wakelock_reference_count); + if (wakelock_reference_count == 0) + wake_lock(&bam_wakelock); + ++wakelock_reference_count; + spin_unlock_irqrestore(&wakelock_reference_lock, flags); +} + +static void release_wakelock(void) +{ + unsigned long flags; + + spin_lock_irqsave(&wakelock_reference_lock, flags); + if (wakelock_reference_count == 0) { + DMUX_LOG_KERR("%s: bam_dmux wakelock not locked\n", __func__); + dump_stack(); + spin_unlock_irqrestore(&wakelock_reference_lock, flags); + return; + } + bam_dmux_log("%s: ref count = %d\n", __func__, + wakelock_reference_count); + --wakelock_reference_count; + if (wakelock_reference_count == 0) + wake_unlock(&bam_wakelock); + spin_unlock_irqrestore(&wakelock_reference_lock, flags); +} + +static int restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data) +{ + int i; + struct list_head *node; + struct tx_pkt_info *info; + int temp_remote_status; + unsigned long flags; + + if (code != SUBSYS_AFTER_SHUTDOWN) + return NOTIFY_DONE; + + bam_dmux_log("%s: begin\n", __func__); + in_global_reset = 1; + + /* Handle uplink Powerdown */ + write_lock_irqsave(&ul_wakeup_lock, flags); + if (bam_is_connected) { + ul_powerdown(); + wait_for_ack = 0; + } + /* + * if modem crash during ul_wakeup(), power_vote is 1, needs to be + * reset to 0. harmless if bam_is_connected check above passes + */ + power_vote(0); + write_unlock_irqrestore(&ul_wakeup_lock, flags); + ul_powerdown_finish(); + a2_pc_disabled = 0; + a2_pc_disabled_wakelock_skipped = 0; + disconnect_ack = 0; + + /* Cleanup Channel States */ + for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) { + temp_remote_status = bam_ch_is_remote_open(i); + bam_ch[i].status &= ~BAM_CH_REMOTE_OPEN; + bam_ch[i].num_tx_pkts = 0; + if (bam_ch_is_local_open(i)) + bam_ch[i].status |= BAM_CH_IN_RESET; + if (temp_remote_status) { + platform_device_unregister(bam_ch[i].pdev); + bam_ch[i].pdev = platform_device_alloc( + bam_ch[i].name, 2); + } + } + + /* Cleanup pending UL data */ + spin_lock_irqsave(&bam_tx_pool_spinlock, flags); + while (!list_empty(&bam_tx_pool)) { + node = bam_tx_pool.next; + list_del(node); + info = container_of(node, struct tx_pkt_info, + list_node); + if (!info->is_cmd) { + dma_unmap_single(NULL, info->dma_address, + info->skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(info->skb); + } else { + dma_unmap_single(NULL, info->dma_address, + info->len, + DMA_TO_DEVICE); + kfree(info->skb); + } + kfree(info); + } + spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags); + + bam_dmux_log("%s: complete\n", __func__); + return NOTIFY_DONE; +} + +static int bam_init(void) +{ + u32 h; + dma_addr_t dma_addr; + int ret; + void *a2_virt_addr; + int skip_iounmap = 0; + + vote_dfab(); + /* init BAM */ + a2_virt_addr = ioremap_nocache(A2_PHYS_BASE, A2_PHYS_SIZE); + if (!a2_virt_addr) { + pr_err("%s: ioremap failed\n", __func__); + ret = -ENOMEM; + goto ioremap_failed; + } + a2_props.phys_addr = A2_PHYS_BASE; + a2_props.virt_addr = a2_virt_addr; + a2_props.virt_size = A2_PHYS_SIZE; + a2_props.irq = A2_BAM_IRQ; + a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP; + a2_props.num_pipes = A2_NUM_PIPES; + a2_props.summing_threshold = A2_SUMMING_THRESHOLD; + if (cpu_is_msm9615()) + a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE; + /* need to free on tear down */ + ret = sps_register_bam_device(&a2_props, &h); + if (ret < 0) { + pr_err("%s: register bam error %d\n", __func__, ret); + goto register_bam_failed; + } + a2_device_handle = h; + + bam_tx_pipe = sps_alloc_endpoint(); + if (bam_tx_pipe == NULL) { + pr_err("%s: tx alloc endpoint failed\n", __func__); + ret = -ENOMEM; + goto tx_alloc_endpoint_failed; + } + ret = sps_get_config(bam_tx_pipe, &tx_connection); + if (ret) { + pr_err("%s: tx get config failed %d\n", __func__, ret); + goto tx_get_config_failed; + } + + tx_connection.source = SPS_DEV_HANDLE_MEM; + tx_connection.src_pipe_index = 0; + tx_connection.destination = h; + tx_connection.dest_pipe_index = 4; + tx_connection.mode = SPS_MODE_DEST; + tx_connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT; + tx_desc_mem_buf.size = 0x800; /* 2k */ + tx_desc_mem_buf.base = dma_alloc_coherent(NULL, tx_desc_mem_buf.size, + &dma_addr, 0); + if (tx_desc_mem_buf.base == NULL) { + pr_err("%s: tx memory alloc failed\n", __func__); + ret = -ENOMEM; + goto tx_get_config_failed; + } + tx_desc_mem_buf.phys_base = dma_addr; + memset(tx_desc_mem_buf.base, 0x0, tx_desc_mem_buf.size); + tx_connection.desc = tx_desc_mem_buf; + tx_connection.event_thresh = 0x10; + + ret = sps_connect(bam_tx_pipe, &tx_connection); + if (ret < 0) { + pr_err("%s: tx connect error %d\n", __func__, ret); + goto tx_connect_failed; + } + + bam_rx_pipe = sps_alloc_endpoint(); + if (bam_rx_pipe == NULL) { + pr_err("%s: rx alloc endpoint failed\n", __func__); + ret = -ENOMEM; + goto rx_alloc_endpoint_failed; + } + ret = sps_get_config(bam_rx_pipe, &rx_connection); + if (ret) { + pr_err("%s: rx get config failed %d\n", __func__, ret); + goto rx_get_config_failed; + } + + rx_connection.source = h; + rx_connection.src_pipe_index = 5; + rx_connection.destination = SPS_DEV_HANDLE_MEM; + rx_connection.dest_pipe_index = 1; + rx_connection.mode = SPS_MODE_SRC; + rx_connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT | + SPS_O_ACK_TRANSFERS; + rx_desc_mem_buf.size = 0x800; /* 2k */ + rx_desc_mem_buf.base = dma_alloc_coherent(NULL, rx_desc_mem_buf.size, + &dma_addr, 0); + if (rx_desc_mem_buf.base == NULL) { + pr_err("%s: rx memory alloc failed\n", __func__); + ret = -ENOMEM; + goto rx_mem_failed; + } + rx_desc_mem_buf.phys_base = dma_addr; + memset(rx_desc_mem_buf.base, 0x0, rx_desc_mem_buf.size); + rx_connection.desc = rx_desc_mem_buf; + rx_connection.event_thresh = 0x10; + + ret = sps_connect(bam_rx_pipe, &rx_connection); + if (ret < 0) { + pr_err("%s: rx connect error %d\n", __func__, ret); + goto rx_connect_failed; + } + + tx_register_event.options = SPS_O_EOT; + tx_register_event.mode = SPS_TRIGGER_CALLBACK; + tx_register_event.xfer_done = NULL; + tx_register_event.callback = bam_mux_tx_notify; + tx_register_event.user = NULL; + ret = sps_register_event(bam_tx_pipe, &tx_register_event); + if (ret < 0) { + pr_err("%s: tx register event error %d\n", __func__, ret); + goto rx_event_reg_failed; + } + + rx_register_event.options = SPS_O_EOT; + rx_register_event.mode = SPS_TRIGGER_CALLBACK; + rx_register_event.xfer_done = NULL; + rx_register_event.callback = bam_mux_rx_notify; + rx_register_event.user = NULL; + ret = sps_register_event(bam_rx_pipe, &rx_register_event); + if (ret < 0) { + pr_err("%s: tx register event error %d\n", __func__, ret); + goto rx_event_reg_failed; + } + + mutex_lock(&delayed_ul_vote_lock); + bam_mux_initialized = 1; + if (need_delayed_ul_vote) { + need_delayed_ul_vote = 0; + msm_bam_dmux_kickoff_ul_wakeup(); + } + mutex_unlock(&delayed_ul_vote_lock); + toggle_apps_ack(); + bam_connection_is_active = 1; + complete_all(&bam_connection_completion); + queue_rx(); + return 0; + +rx_event_reg_failed: + sps_disconnect(bam_rx_pipe); +rx_connect_failed: + dma_free_coherent(NULL, rx_desc_mem_buf.size, rx_desc_mem_buf.base, + rx_desc_mem_buf.phys_base); +rx_mem_failed: +rx_get_config_failed: + sps_free_endpoint(bam_rx_pipe); +rx_alloc_endpoint_failed: + sps_disconnect(bam_tx_pipe); +tx_connect_failed: + dma_free_coherent(NULL, tx_desc_mem_buf.size, tx_desc_mem_buf.base, + tx_desc_mem_buf.phys_base); +tx_get_config_failed: + sps_free_endpoint(bam_tx_pipe); +tx_alloc_endpoint_failed: + sps_deregister_bam_device(h); + /* + * sps_deregister_bam_device() calls iounmap. calling iounmap on the + * same handle below will cause a crash, so skip it if we've freed + * the handle here. + */ + skip_iounmap = 1; +register_bam_failed: + if (!skip_iounmap) + iounmap(a2_virt_addr); +ioremap_failed: + /*destroy_workqueue(bam_mux_workqueue);*/ + return ret; +} + +static int bam_init_fallback(void) +{ + u32 h; + int ret; + void *a2_virt_addr; + + unvote_dfab(); + /* init BAM */ + a2_virt_addr = ioremap_nocache(A2_PHYS_BASE, A2_PHYS_SIZE); + if (!a2_virt_addr) { + pr_err("%s: ioremap failed\n", __func__); + ret = -ENOMEM; + goto ioremap_failed; + } + a2_props.phys_addr = A2_PHYS_BASE; + a2_props.virt_addr = a2_virt_addr; + a2_props.virt_size = A2_PHYS_SIZE; + a2_props.irq = A2_BAM_IRQ; + a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP; + a2_props.num_pipes = A2_NUM_PIPES; + a2_props.summing_threshold = A2_SUMMING_THRESHOLD; + if (cpu_is_msm9615()) + a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE; + ret = sps_register_bam_device(&a2_props, &h); + if (ret < 0) { + pr_err("%s: register bam error %d\n", __func__, ret); + goto register_bam_failed; + } + a2_device_handle = h; + + mutex_lock(&delayed_ul_vote_lock); + bam_mux_initialized = 1; + if (need_delayed_ul_vote) { + need_delayed_ul_vote = 0; + msm_bam_dmux_kickoff_ul_wakeup(); + } + mutex_unlock(&delayed_ul_vote_lock); + toggle_apps_ack(); + + return 0; + +register_bam_failed: + iounmap(a2_virt_addr); +ioremap_failed: + return ret; +} + +static void msm9615_bam_init(void) +{ + int ret = 0; + + ret = bam_init(); + if (ret) { + ret = bam_init_fallback(); + if (ret) + pr_err("%s: bam init fallback failed: %d", + __func__, ret); + } +} + +static void toggle_apps_ack(void) +{ + static unsigned int clear_bit; /* 0 = set the bit, else clear bit */ + + bam_dmux_log("%s: apps ack %d->%d\n", __func__, + clear_bit & 0x1, ~clear_bit & 0x1); + smsm_change_state(SMSM_APPS_STATE, + clear_bit & SMSM_A2_POWER_CONTROL_ACK, + ~clear_bit & SMSM_A2_POWER_CONTROL_ACK); + clear_bit = ~clear_bit; + DBG_INC_ACK_OUT_CNT(); +} + +static void bam_dmux_smsm_cb(void *priv, uint32_t old_state, uint32_t new_state) +{ + static int last_processed_state; + + mutex_lock(&smsm_cb_lock); + bam_dmux_power_state = new_state & SMSM_A2_POWER_CONTROL ? 1 : 0; + DBG_INC_A2_POWER_CONTROL_IN_CNT(); + bam_dmux_log("%s: 0x%08x -> 0x%08x\n", __func__, old_state, + new_state); + if (last_processed_state == (new_state & SMSM_A2_POWER_CONTROL)) { + bam_dmux_log("%s: already processed this state\n", __func__); + mutex_unlock(&smsm_cb_lock); + return; + } + + last_processed_state = new_state & SMSM_A2_POWER_CONTROL; + + if (bam_mux_initialized && new_state & SMSM_A2_POWER_CONTROL) { + bam_dmux_log("%s: reconnect\n", __func__); + grab_wakelock(); + reconnect_to_bam(); + } else if (bam_mux_initialized && + !(new_state & SMSM_A2_POWER_CONTROL)) { + bam_dmux_log("%s: disconnect\n", __func__); + disconnect_to_bam(); + release_wakelock(); + } else if (new_state & SMSM_A2_POWER_CONTROL) { + bam_dmux_log("%s: init\n", __func__); + grab_wakelock(); + if (cpu_is_msm9615()) + msm9615_bam_init(); + else + bam_init(); + } else { + bam_dmux_log("%s: bad state change\n", __func__); + pr_err("%s: unsupported state change\n", __func__); + } + mutex_unlock(&smsm_cb_lock); + +} + +static void bam_dmux_smsm_ack_cb(void *priv, uint32_t old_state, + uint32_t new_state) +{ + DBG_INC_ACK_IN_CNT(); + bam_dmux_log("%s: 0x%08x -> 0x%08x\n", __func__, old_state, + new_state); + complete_all(&ul_wakeup_ack_completion); +} + +static int bam_dmux_probe(struct platform_device *pdev) +{ + int rc; + + DBG("%s probe called\n", __func__); + if (bam_mux_initialized) + return 0; + + xo_clk = clk_get(&pdev->dev, "xo"); + if (IS_ERR(xo_clk)) { + pr_err("%s: did not get xo clock\n", __func__); + return PTR_ERR(xo_clk); + } + dfab_clk = clk_get(&pdev->dev, "bus_clk"); + if (IS_ERR(dfab_clk)) { + pr_err("%s: did not get dfab clock\n", __func__); + return -EFAULT; + } + + rc = clk_set_rate(dfab_clk, 64000000); + if (rc) + pr_err("%s: unable to set dfab clock rate\n", __func__); + + /* + * setup the workqueue so that it can be pinned to core 0 and not + * block the watchdog pet function, so that netif_rx() in rmnet + * only uses one queue. + */ + bam_mux_rx_workqueue = alloc_workqueue("bam_dmux_rx", + WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1); + if (!bam_mux_rx_workqueue) + return -ENOMEM; + + bam_mux_tx_workqueue = create_singlethread_workqueue("bam_dmux_tx"); + if (!bam_mux_tx_workqueue) { + destroy_workqueue(bam_mux_rx_workqueue); + return -ENOMEM; + } + + for (rc = 0; rc < BAM_DMUX_NUM_CHANNELS; ++rc) { + spin_lock_init(&bam_ch[rc].lock); + scnprintf(bam_ch[rc].name, BAM_DMUX_CH_NAME_MAX_LEN, + "bam_dmux_ch_%d", rc); + /* bus 2, ie a2 stream 2 */ + bam_ch[rc].pdev = platform_device_alloc(bam_ch[rc].name, 2); + if (!bam_ch[rc].pdev) { + pr_err("%s: platform device alloc failed\n", __func__); + destroy_workqueue(bam_mux_rx_workqueue); + destroy_workqueue(bam_mux_tx_workqueue); + return -ENOMEM; + } + } + + init_completion(&ul_wakeup_ack_completion); + init_completion(&bam_connection_completion); + init_completion(&dfab_unvote_completion); + INIT_DELAYED_WORK(&ul_timeout_work, ul_timeout); + wake_lock_init(&bam_wakelock, WAKE_LOCK_SUSPEND, "bam_dmux_wakelock"); + + rc = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_A2_POWER_CONTROL, + bam_dmux_smsm_cb, NULL); + + if (rc) { + destroy_workqueue(bam_mux_rx_workqueue); + destroy_workqueue(bam_mux_tx_workqueue); + pr_err("%s: smsm cb register failed, rc: %d\n", __func__, rc); + return -ENOMEM; + } + + rc = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_A2_POWER_CONTROL_ACK, + bam_dmux_smsm_ack_cb, NULL); + + if (rc) { + destroy_workqueue(bam_mux_rx_workqueue); + destroy_workqueue(bam_mux_tx_workqueue); + smsm_state_cb_deregister(SMSM_MODEM_STATE, + SMSM_A2_POWER_CONTROL, + bam_dmux_smsm_cb, NULL); + pr_err("%s: smsm ack cb register failed, rc: %d\n", __func__, + rc); + for (rc = 0; rc < BAM_DMUX_NUM_CHANNELS; ++rc) + platform_device_put(bam_ch[rc].pdev); + return -ENOMEM; + } + + if (smsm_get_state(SMSM_MODEM_STATE) & SMSM_A2_POWER_CONTROL) + bam_dmux_smsm_cb(NULL, 0, smsm_get_state(SMSM_MODEM_STATE)); + + return 0; +} + +static struct platform_driver bam_dmux_driver = { + .probe = bam_dmux_probe, + .driver = { + .name = "BAM_RMNT", + .owner = THIS_MODULE, + }, +}; + +static int __init bam_dmux_init(void) +{ + int ret; +#ifdef CONFIG_DEBUG_FS + struct dentry *dent; + + dent = debugfs_create_dir("bam_dmux", 0); + if (!IS_ERR(dent)) { + debug_create("tbl", 0444, dent, debug_tbl); + debug_create("ul_pkt_cnt", 0444, dent, debug_ul_pkt_cnt); + debug_create("stats", 0444, dent, debug_stats); + debug_create_multiple("log", 0444, dent, debug_log); + } +#endif + ret = kfifo_alloc(&bam_dmux_state_log, PAGE_SIZE, GFP_KERNEL); + if (ret) { + pr_err("%s: failed to allocate log %d\n", __func__, ret); + bam_dmux_state_logging_disabled = 1; + } + + subsys_notif_register_notifier("modem", &restart_notifier); + return platform_driver_register(&bam_dmux_driver); +} + +late_initcall(bam_dmux_init); /* needs to init after SMD */ +MODULE_DESCRIPTION("MSM BAM DMUX"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/bms-batterydata-desay.c b/arch/arm/mach-msm/bms-batterydata-desay.c new file mode 100644 index 00000000000..f362a72f325 --- /dev/null +++ b/arch/arm/mach-msm/bms-batterydata-desay.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +static struct single_row_lut desay_5200_fcc_temp = { + .x = {-20, 0, 25, 40}, + .y = {5690, 5722, 5722, 5727}, + .cols = 4 +}; + +static struct single_row_lut desay_5200_fcc_sf = { + .x = {0}, + .y = {100}, + .cols = 1 +}; + +static struct pc_temp_ocv_lut desay_5200_pc_temp_ocv = { + .rows = 29, + .cols = 4, + .temp = {-20, 0, 25, 40}, + .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, + 50, 45, 40, 35, 30, 25, 20, 15, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 + }, + .ocv = { + {4185, 4184, 4181, 4178}, + {4103, 4117, 4120, 4119}, + {4044, 4067, 4074, 4073}, + {3987, 4019, 4031, 4030}, + {3941, 3974, 3992, 3992}, + {3902, 3936, 3958, 3957}, + {3866, 3901, 3926, 3926}, + {3835, 3870, 3891, 3896}, + {3811, 3842, 3855, 3858}, + {3792, 3818, 3827, 3827}, + {3776, 3795, 3806, 3806}, + {3762, 3778, 3789, 3790}, + {3748, 3765, 3777, 3777}, + {3735, 3752, 3767, 3765}, + {3720, 3739, 3756, 3754}, + {3704, 3726, 3743, 3736}, + {3685, 3712, 3723, 3716}, + {3664, 3697, 3695, 3689}, + {3623, 3672, 3669, 3664}, + {3611, 3666, 3666, 3661}, + {3597, 3659, 3662, 3658}, + {3579, 3648, 3657, 3653}, + {3559, 3630, 3644, 3639}, + {3532, 3600, 3612, 3606}, + {3497, 3558, 3565, 3559}, + {3450, 3500, 3504, 3498}, + {3380, 3417, 3421, 3416}, + {3265, 3287, 3296, 3293}, + {3000, 3000, 3000, 3000} + }, +}; + +static struct sf_lut desay_5200_pc_sf = { + .rows = 1, + .cols = 1, + /* row_entries are cycles here */ + .row_entries = {0}, + .percent = {100}, + .sf = { + {100} + }, +}; + +struct pm8921_bms_battery_data desay_5200_data = { + .fcc = 5200, + .fcc_temp_lut = &desay_5200_fcc_temp, + .fcc_sf_lut = &desay_5200_fcc_sf, + .pc_temp_ocv_lut = &desay_5200_pc_temp_ocv, + .pc_sf_lut = &desay_5200_pc_sf, + .default_rbatt_mohm = 156, +}; diff --git a/arch/arm/mach-msm/bms-batterydata.c b/arch/arm/mach-msm/bms-batterydata.c new file mode 100644 index 00000000000..77e7dab816b --- /dev/null +++ b/arch/arm/mach-msm/bms-batterydata.c @@ -0,0 +1,127 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +static struct single_row_lut palladium_1500_fcc_temp = { + .x = {-30, -20, -10, 0, 10, 25, 40, 60}, + .y = {1103, 1179, 1284, 1330, 1420, 1511, 1541, 1571}, + .cols = 8, +}; + +static struct single_row_lut palladium_1500_fcc_sf = { + .x = {100, 200, 300, 400, 500}, + .y = {97, 93, 93, 90, 87}, + .cols = 5, +}; + +static struct sf_lut palladium_1500_pc_sf = { + .rows = 10, + .cols = 5, + /* row_entries are chargecycles */ + .row_entries = {100, 200, 300, 400, 500}, + .percent = {100, 90, 80, 70, 60, 50, 40, 30, 20, 10}, + .sf = { + {97, 93, 93, 90, 87}, + {97, 93, 93, 90, 87}, + {98, 94, 92, 89, 86}, + {98, 94, 92, 89, 86}, + {99, 94, 92, 88, 86}, + {99, 95, 92, 88, 87}, + {99, 95, 92, 88, 87}, + {99, 95, 92, 88, 87}, + {99, 95, 92, 88, 87}, + {99, 95, 92, 88, 87} + }, +}; + +static struct sf_lut palladium_1500_rbatt_sf = { + .rows = 19, + .cols = 5, + /* row_entries are temperature */ + .row_entries = {-20, 0, 20, 40, 65}, + .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, + 45, 40, 35, 30, 25, 20, 15, 10 + }, + .sf = { + {645, 301, 100, 80, 69}, + {616, 290, 100, 79, 69}, + {586, 279, 100, 78, 68}, + {564, 270, 100, 78, 68}, + {546, 262, 100, 78, 68}, + {537, 256, 100, 79, 68}, + {536, 253, 100, 79, 69}, + {552, 258, 100, 81, 71}, + {618, 284, 100, 80, 72}, + {643, 290, 100, 77, 68}, + {673, 294, 100, 77, 68}, + {720, 296, 100, 77, 69}, + {769, 294, 100, 76, 68}, + {821, 288, 100, 74, 67}, + {892, 284, 100, 74, 61}, + {1003, 290, 100, 71, 58}, + {1192, 307, 100, 70, 58}, + {1579, 345, 100, 68, 57}, + {1261, 324, 100, 68, 57}, + } +}; +static struct pc_temp_ocv_lut palladium_1500_pc_temp_ocv = { + .rows = 29, + .cols = 8, + .temp = {-30, -20, -10, 0, 10, 25, 40, 60}, + .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, + 50, 45, 40, 35, 30, 25, 20, 15, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, 0 + }, + .ocv = { + {3673, 3814, 3945, 4025, 4106, 4176, 4218, 4260}, + {3613, 3751, 3880, 3959, 4038, 4107, 4149, 4190}, + {3573, 3710, 3837, 3916, 3994, 4062, 4103, 4144}, + {3534, 3670, 3796, 3873, 3951, 4019, 4059, 4099}, + {3491, 3625, 3749, 3826, 3902, 3969, 4009, 4049}, + {3464, 3597, 3721, 3796, 3872, 3939, 3978, 4018}, + {3436, 3568, 3691, 3766, 3841, 3907, 3946, 3985}, + {3407, 3537, 3659, 3733, 3808, 3873, 3912, 3951}, + {3377, 3507, 3627, 3701, 3775, 3840, 3878, 3917}, + {3355, 3484, 3604, 3677, 3751, 3815, 3853, 3891}, + {3339, 3467, 3586, 3659, 3732, 3796, 3834, 3872}, + {3324, 3452, 3570, 3643, 3716, 3780, 3818, 3855}, + {3312, 3440, 3558, 3630, 3703, 3766, 3804, 3842}, + {3303, 3430, 3548, 3620, 3692, 3756, 3793, 3831}, + {3297, 3424, 3541, 3614, 3686, 3749, 3787, 3824}, + {3288, 3414, 3531, 3603, 3675, 3738, 3776, 3813}, + {3272, 3398, 3514, 3586, 3658, 3720, 3757, 3795}, + {3240, 3365, 3480, 3551, 3622, 3684, 3721, 3758}, + {3224, 3348, 3463, 3533, 3604, 3666, 3702, 3739}, + {3221, 3344, 3459, 3530, 3600, 3662, 3695, 3728}, + {3216, 3340, 3454, 3525, 3595, 3657, 3686, 3715}, + {3212, 3335, 3449, 3520, 3590, 3652, 3677, 3703}, + {3203, 3326, 3440, 3510, 3580, 3642, 3664, 3686}, + {3185, 3307, 3420, 3490, 3560, 3621, 3639, 3657}, + {3176, 3298, 3411, 3481, 3550, 3611, 3626, 3640}, + {3151, 3272, 3384, 3453, 3522, 3583, 3593, 3604}, + {3106, 3225, 3335, 3446, 3472, 3531, 3538, 3545}, + {3021, 3217, 3245, 3417, 3429, 3435, 3439, 3442}, + {3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000} + }, +}; + +struct pm8921_bms_battery_data palladium_1500_data = { + .fcc = 1500, + .fcc_temp_lut = &palladium_1500_fcc_temp, + .fcc_sf_lut = &palladium_1500_fcc_sf, + .pc_temp_ocv_lut = &palladium_1500_pc_temp_ocv, + .pc_sf_lut = &palladium_1500_pc_sf, + .rbatt_sf_lut = &palladium_1500_rbatt_sf, + .default_rbatt_mohm = 254, + .delta_rbatt_mohm = 60, +}; diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c new file mode 100644 index 00000000000..2d1f787c42c --- /dev/null +++ b/arch/arm/mach-msm/board-8064-camera.c @@ -0,0 +1,697 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include + +#include +#include +#include + +#include "devices.h" +#include "board-8064.h" + +#ifdef CONFIG_MSM_CAMERA + +static struct gpiomux_setting cam_settings[] = { + { + .func = GPIOMUX_FUNC_GPIO, /*suspend*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + + { + .func = GPIOMUX_FUNC_1, /*active 1*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_GPIO, /*active 2*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_2, /*active 3*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_5, /*active 4*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_6, /*active 5*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_2, /*active 6*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_3, /*active 7*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_GPIO, /*i2c suspend*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, + }, + + { + .func = GPIOMUX_FUNC_9, /*active 9*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, + }, + { + .func = GPIOMUX_FUNC_A, /*active 10*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, + }, + { + .func = GPIOMUX_FUNC_6, /*active 11*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, + }, + { + .func = GPIOMUX_FUNC_4, /*active 12*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + +}; + +static struct msm_gpiomux_config apq8064_cam_common_configs[] = { + { + .gpio = 1, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 2, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[12], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 3, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 4, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 5, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[1], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 34, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 107, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 10, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[9], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 11, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[10], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 12, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[11], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 13, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[11], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, +}; + + +#define VFE_CAMIF_TIMER1_GPIO 3 +#define VFE_CAMIF_TIMER2_GPIO 1 + +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT, + ._fsrc.ext_driver_src.led_en = VFE_CAMIF_TIMER1_GPIO, + ._fsrc.ext_driver_src.led_flash_en = VFE_CAMIF_TIMER2_GPIO, + ._fsrc.ext_driver_src.flash_id = MAM_CAMERA_EXT_LED_FLASH_SC628A, +}; + +static struct msm_gpiomux_config apq8064_cam_2d_configs[] = { +}; + +static struct msm_bus_vectors cam_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 27648000, + .ib = 110592000, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 140451840, + .ib = 561807360, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 206807040, + .ib = 488816640, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 274423680, + .ib = 1097694720, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 540000000, + .ib = 1350000000, + }, +}; + +static struct msm_bus_vectors cam_zsl_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 302071680, + .ib = 1208286720, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 540000000, + .ib = 1350000000, + }, +}; + +static struct msm_bus_paths cam_bus_client_config[] = { + { + ARRAY_SIZE(cam_init_vectors), + cam_init_vectors, + }, + { + ARRAY_SIZE(cam_preview_vectors), + cam_preview_vectors, + }, + { + ARRAY_SIZE(cam_video_vectors), + cam_video_vectors, + }, + { + ARRAY_SIZE(cam_snapshot_vectors), + cam_snapshot_vectors, + }, + { + ARRAY_SIZE(cam_zsl_vectors), + cam_zsl_vectors, + }, +}; + +static struct msm_bus_scale_pdata cam_bus_client_pdata = { + cam_bus_client_config, + ARRAY_SIZE(cam_bus_client_config), + .name = "msm_camera", +}; + +static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = { + { + .csid_core = 0, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, + { + .csid_core = 1, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, +}; + +static struct camera_vreg_t apq_8064_back_cam_vreg[] = { + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +static struct camera_vreg_t apq_8064_front_cam_vreg[] = { + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +#define CAML_RSTN PM8921_GPIO_PM_TO_SYS(28) +#define CAMR_RSTN 34 + +static struct gpio apq8064_common_cam_gpio[] = { +}; + +static struct gpio apq8064_back_cam_gpio[] = { + {5, GPIOF_DIR_IN, "CAMIF_MCLK"}, + {CAML_RSTN, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct msm_gpio_set_tbl apq8064_back_cam_gpio_set_tbl[] = { + {CAML_RSTN, GPIOF_OUT_INIT_LOW, 10000}, + {CAML_RSTN, GPIOF_OUT_INIT_HIGH, 10000}, +}; + +static struct msm_camera_gpio_conf apq8064_back_cam_gpio_conf = { + .cam_gpiomux_conf_tbl = apq8064_cam_2d_configs, + .cam_gpiomux_conf_tbl_size = ARRAY_SIZE(apq8064_cam_2d_configs), + .cam_gpio_common_tbl = apq8064_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(apq8064_common_cam_gpio), + .cam_gpio_req_tbl = apq8064_back_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(apq8064_back_cam_gpio), + .cam_gpio_set_tbl = apq8064_back_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(apq8064_back_cam_gpio_set_tbl), +}; + +static struct gpio apq8064_front_cam_gpio[] = { + {4, GPIOF_DIR_IN, "CAMIF_MCLK"}, + {12, GPIOF_DIR_IN, "CAMIF_I2C_DATA"}, + {13, GPIOF_DIR_IN, "CAMIF_I2C_CLK"}, + {CAMR_RSTN, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct msm_gpio_set_tbl apq8064_front_cam_gpio_set_tbl[] = { + {CAMR_RSTN, GPIOF_OUT_INIT_LOW, 10000}, + {CAMR_RSTN, GPIOF_OUT_INIT_HIGH, 10000}, +}; + +static struct msm_camera_gpio_conf apq8064_front_cam_gpio_conf = { + .cam_gpiomux_conf_tbl = apq8064_cam_2d_configs, + .cam_gpiomux_conf_tbl_size = ARRAY_SIZE(apq8064_cam_2d_configs), + .cam_gpio_common_tbl = apq8064_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(apq8064_common_cam_gpio), + .cam_gpio_req_tbl = apq8064_front_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(apq8064_front_cam_gpio), + .cam_gpio_set_tbl = apq8064_front_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(apq8064_front_cam_gpio_set_tbl), +}; + +static struct msm_camera_i2c_conf apq8064_back_cam_i2c_conf = { + .use_i2c_mux = 1, + .mux_dev = &msm8960_device_i2c_mux_gsbi4, + .i2c_mux_mode = MODE_L, +}; + +static struct i2c_board_info msm_act_main_cam_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x11), +}; + +static struct msm_actuator_info msm_act_main_cam_0_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_0, + .bus_id = APQ_8064_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + +static struct i2c_board_info msm_act_main_cam1_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x18), +}; + +static struct msm_actuator_info msm_act_main_cam_1_info = { + .board_info = &msm_act_main_cam1_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_1, + .bus_id = APQ_8064_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + + +static struct msm_camera_i2c_conf apq8064_front_cam_i2c_conf = { + .use_i2c_mux = 1, + .mux_dev = &msm8960_device_i2c_mux_gsbi4, + .i2c_mux_mode = MODE_L, +}; + +static struct msm_camera_sensor_flash_data flash_imx074 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_csi_lane_params imx074_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_imx074 = { + .mount_angle = 90, + .cam_vreg = apq_8064_back_cam_vreg, + .num_vreg = ARRAY_SIZE(apq_8064_back_cam_vreg), + .gpio_conf = &apq8064_back_cam_gpio_conf, + .i2c_conf = &apq8064_back_cam_i2c_conf, + .csi_lane_params = &imx074_csi_lane_params, +}; + +static struct i2c_board_info imx074_eeprom_i2c_info = { + I2C_BOARD_INFO("imx074_eeprom", 0x34 << 1), +}; + +static struct msm_eeprom_info imx074_eeprom_info = { + .board_info = &imx074_eeprom_i2c_info, + .bus_id = APQ_8064_GSBI4_QUP_I2C_BUS_ID, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx074_data = { + .sensor_name = "imx074", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_imx074, + .sensor_platform_info = &sensor_board_info_imx074, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_0_info, + .eeprom_info = &imx074_eeprom_info, +}; + +static struct msm_camera_csi_lane_params imx091_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct camera_vreg_t apq_8064_imx091_vreg[] = { + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vio", REG_VS, 0, 0, 0}, +}; + +static struct msm_camera_sensor_flash_data flash_imx091 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_imx091 = { + .mount_angle = 0, + .cam_vreg = apq_8064_imx091_vreg, + .num_vreg = ARRAY_SIZE(apq_8064_imx091_vreg), + .gpio_conf = &apq8064_back_cam_gpio_conf, + .i2c_conf = &apq8064_back_cam_i2c_conf, + .csi_lane_params = &imx091_csi_lane_params, +}; + +static struct i2c_board_info imx091_eeprom_i2c_info = { + I2C_BOARD_INFO("imx091_eeprom", 0x21), +}; + +static struct msm_eeprom_info imx091_eeprom_info = { + .board_info = &imx091_eeprom_i2c_info, + .bus_id = APQ_8064_GSBI4_QUP_I2C_BUS_ID, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx091_data = { + .sensor_name = "imx091", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_imx091, + .sensor_platform_info = &sensor_board_info_imx091, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_1_info, + .eeprom_info = &imx091_eeprom_info, +}; + +static struct camera_vreg_t apq_8064_s5k3l1yx_vreg[] = { + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +static struct msm_camera_sensor_flash_data flash_s5k3l1yx = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_csi_lane_params s5k3l1yx_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_s5k3l1yx = { + .mount_angle = 90, + .cam_vreg = apq_8064_s5k3l1yx_vreg, + .num_vreg = ARRAY_SIZE(apq_8064_s5k3l1yx_vreg), + .gpio_conf = &apq8064_back_cam_gpio_conf, + .i2c_conf = &apq8064_back_cam_i2c_conf, + .csi_lane_params = &s5k3l1yx_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3l1yx_data = { + .sensor_name = "s5k3l1yx", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_s5k3l1yx, + .sensor_platform_info = &sensor_board_info_s5k3l1yx, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, +}; + +static struct camera_vreg_t apq_8064_mt9m114_vreg[] = { + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +static struct msm_camera_sensor_flash_data flash_mt9m114 = { + .flash_type = MSM_CAMERA_FLASH_NONE +}; + +static struct msm_camera_csi_lane_params mt9m114_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0x1, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_mt9m114 = { + .mount_angle = 90, + .cam_vreg = apq_8064_mt9m114_vreg, + .num_vreg = ARRAY_SIZE(apq_8064_mt9m114_vreg), + .gpio_conf = &apq8064_front_cam_gpio_conf, + .i2c_conf = &apq8064_front_cam_i2c_conf, + .csi_lane_params = &mt9m114_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9m114_data = { + .sensor_name = "mt9m114", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_mt9m114, + .sensor_platform_info = &sensor_board_info_mt9m114, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = YUV_SENSOR, +}; + +static struct msm_camera_sensor_flash_data flash_ov2720 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_csi_lane_params ov2720_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0x3, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov2720 = { + .mount_angle = 0, + .cam_vreg = apq_8064_front_cam_vreg, + .num_vreg = ARRAY_SIZE(apq_8064_front_cam_vreg), + .gpio_conf = &apq8064_front_cam_gpio_conf, + .i2c_conf = &apq8064_front_cam_i2c_conf, + .csi_lane_params = &ov2720_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov2720_data = { + .sensor_name = "ov2720", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_ov2720, + .sensor_platform_info = &sensor_board_info_ov2720, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = BAYER_SENSOR, +}; + +static struct platform_device msm_camera_server = { + .name = "msm_cam_server", + .id = 0, +}; + +void __init apq8064_init_cam(void) +{ + msm_gpiomux_install(apq8064_cam_common_configs, + ARRAY_SIZE(apq8064_cam_common_configs)); + + if (machine_is_apq8064_cdp()) { + sensor_board_info_imx074.mount_angle = 0; + sensor_board_info_mt9m114.mount_angle = 0; + } else if (machine_is_apq8064_liquid()) + sensor_board_info_imx074.mount_angle = 180; + + platform_device_register(&msm_camera_server); + platform_device_register(&msm8960_device_i2c_mux_gsbi4); + platform_device_register(&msm8960_device_csiphy0); + platform_device_register(&msm8960_device_csiphy1); + platform_device_register(&msm8960_device_csid0); + platform_device_register(&msm8960_device_csid1); + platform_device_register(&msm8960_device_ispif); + platform_device_register(&msm8960_device_vfe); + platform_device_register(&msm8960_device_vpe); +} + +#ifdef CONFIG_I2C +static struct i2c_board_info apq8064_camera_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("imx074", 0x1A), + .platform_data = &msm_camera_sensor_imx074_data, + }, + { + I2C_BOARD_INFO("mt9m114", 0x48), + .platform_data = &msm_camera_sensor_mt9m114_data, + }, + { + I2C_BOARD_INFO("ov2720", 0x6C), + .platform_data = &msm_camera_sensor_ov2720_data, + }, + { + I2C_BOARD_INFO("sc628a", 0x6E), + }, + { + I2C_BOARD_INFO("imx091", 0x34), + .platform_data = &msm_camera_sensor_imx091_data, + }, + { + I2C_BOARD_INFO("s5k3l1yx", 0x20), + .platform_data = &msm_camera_sensor_s5k3l1yx_data, + }, +}; + +struct msm_camera_board_info apq8064_camera_board_info = { + .board_info = apq8064_camera_i2c_boardinfo, + .num_i2c_board_info = ARRAY_SIZE(apq8064_camera_i2c_boardinfo), +}; +#endif +#endif diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c new file mode 100644 index 00000000000..71ad49afc8b --- /dev/null +++ b/arch/arm/mach-msm/board-8064-display.c @@ -0,0 +1,995 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-8064.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +/* prim = 1366 x 768 x 3(bpp) x 3(pages) */ +#define MSM_FB_PRIM_BUF_SIZE roundup(1920 * 1088 * 4 * 3, 0x10000) +#else +/* prim = 1366 x 768 x 3(bpp) x 2(pages) */ +#define MSM_FB_PRIM_BUF_SIZE roundup(1920 * 1088 * 4 * 2, 0x10000) +#endif + +#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096) + +#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */ + +#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */ + + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +#define LVDS_CHIMEI_PANEL_NAME "lvds_chimei_wxga" +#define MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME "mipi_video_toshiba_wsvga" +#define MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME "mipi_video_chimei_wxga" +#define HDMI_PANEL_NAME "hdmi_msm" +#define TVOUT_PANEL_NAME "tvout_msm" + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +static unsigned char hdmi_is_primary = 1; +#else +static unsigned char hdmi_is_primary; +#endif + +unsigned char apq8064_hdmi_as_primary_selected(void) +{ + return hdmi_is_primary; +} + +static void set_mdp_clocks_for_wuxga(void); + +static int msm_fb_detect_panel(const char *name) +{ + u32 version; + if (machine_is_apq8064_liquid()) { + version = socinfo_get_platform_version(); + if ((SOCINFO_VERSION_MAJOR(version) == 1) && + (SOCINFO_VERSION_MINOR(version) == 1)) { + if (!strncmp(name, MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME, + strnlen(MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + } else { + if (!strncmp(name, LVDS_CHIMEI_PANEL_NAME, + strnlen(LVDS_CHIMEI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + } + } else if (machine_is_apq8064_mtp()) { + if (!strncmp(name, MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + } else if (machine_is_apq8064_cdp() || + machine_is_mpq8064_dtv()) { + if (!strncmp(name, LVDS_CHIMEI_PANEL_NAME, + strnlen(LVDS_CHIMEI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + } + + if (!strncmp(name, HDMI_PANEL_NAME, + strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + if (apq8064_hdmi_as_primary_selected()) + set_mdp_clocks_for_wuxga(); + return 0; + } + + + return -ENODEV; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev.platform_data = &msm_fb_pdata, +}; + +void __init apq8064_allocate_fb_region(void) +{ + void *addr; + unsigned long size; + + size = MSM_FB_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); +} + +#define MDP_VSYNC_GPIO 0 + +static struct msm_bus_vectors mdp_init_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors mdp_ui_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000 * 2, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_vga_vectors[] = { + /* VGA and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000 * 2, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_720p_vectors[] = { + /* 720p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 230400000 * 2, + .ib = 288000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_1080p_vectors[] = { + /* 1080p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 334080000 * 2, + .ib = 417600000 * 2, + }, +}; + +static struct msm_bus_paths mdp_bus_scale_usecases[] = { + { + ARRAY_SIZE(mdp_init_vectors), + mdp_init_vectors, + }, + { + ARRAY_SIZE(mdp_ui_vectors), + mdp_ui_vectors, + }, + { + ARRAY_SIZE(mdp_ui_vectors), + mdp_ui_vectors, + }, + { + ARRAY_SIZE(mdp_vga_vectors), + mdp_vga_vectors, + }, + { + ARRAY_SIZE(mdp_720p_vectors), + mdp_720p_vectors, + }, + { + ARRAY_SIZE(mdp_1080p_vectors), + mdp_1080p_vectors, + }, +}; + +static struct msm_bus_scale_pdata mdp_bus_scale_pdata = { + mdp_bus_scale_usecases, + ARRAY_SIZE(mdp_bus_scale_usecases), + .name = "mdp", +}; + +static int mdp_core_clk_rate_table[] = { + 59080000, + 128000000, + 160000000, + 200000000, +}; + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = MDP_VSYNC_GPIO, + .mdp_core_clk_rate = 59080000, + .mdp_core_clk_table = mdp_core_clk_rate_table, + .num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table), + .mdp_bus_scale_table = &mdp_bus_scale_pdata, + .mdp_rev = MDP_REV_44, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .mem_hid = BIT(ION_CP_MM_HEAP_ID), +#else + .mem_hid = MEMTYPE_EBI1, +#endif +}; + +void __init apq8064_mdp_writeback(struct memtype_reserve* reserve_table) +{ + mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE; + mdp_pdata.ov1_wb_size = MSM_FB_OVERLAY1_WRITEBACK_SIZE; +#if defined(CONFIG_ANDROID_PMEM) && !defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov0_wb_size; + reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov1_wb_size; +#endif +} + +static struct resource hdmi_msm_resources[] = { + { + .name = "hdmi_msm_qfprom_addr", + .start = 0x00700000, + .end = 0x007060FF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_hdmi_addr", + .start = 0x04A00000, + .end = 0x04A00FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_irq", + .start = HDMI_IRQ, + .end = HDMI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int hdmi_enable_5v(int on); +static int hdmi_core_power(int on, int show); +static int hdmi_cec_power(int on); + +static struct msm_hdmi_platform_data hdmi_msm_data = { + .irq = HDMI_IRQ, + .enable_5v = hdmi_enable_5v, + .core_power = hdmi_core_power, + .cec_power = hdmi_cec_power, +}; + +static struct platform_device hdmi_msm_device = { + .name = "hdmi_msm", + .id = 0, + .num_resources = ARRAY_SIZE(hdmi_msm_resources), + .resource = hdmi_msm_resources, + .dev.platform_data = &hdmi_msm_data, +}; + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +static struct platform_device wfd_panel_device = { + .name = "wfd_panel", + .id = 0, + .dev.platform_data = NULL, +}; + +static struct platform_device wfd_device = { + .name = "msm_wfd", + .id = -1, +}; +#endif + +/* HDMI related GPIOs */ +#define HDMI_CEC_VAR_GPIO 69 +#define HDMI_DDC_CLK_GPIO 70 +#define HDMI_DDC_DATA_GPIO 71 +#define HDMI_HPD_GPIO 72 + +static bool dsi_power_on; +static int mipi_dsi_panel_power(int on) +{ + static struct regulator *reg_lvs7, *reg_l2, *reg_l11, *reg_ext_3p3v; + static int gpio36, gpio25, gpio26, mpp3; + int rc; + + pr_debug("%s: on=%d\n", __func__, on); + + if (!dsi_power_on) { + reg_lvs7 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi1_vddio"); + if (IS_ERR_OR_NULL(reg_lvs7)) { + pr_err("could not get 8921_lvs7, rc = %ld\n", + PTR_ERR(reg_lvs7)); + return -ENODEV; + } + + reg_l2 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi1_pll_vdda"); + if (IS_ERR_OR_NULL(reg_l2)) { + pr_err("could not get 8921_l2, rc = %ld\n", + PTR_ERR(reg_l2)); + return -ENODEV; + } + + rc = regulator_set_voltage(reg_l2, 1200000, 1200000); + if (rc) { + pr_err("set_voltage l2 failed, rc=%d\n", rc); + return -EINVAL; + } + reg_l11 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi1_avdd"); + if (IS_ERR(reg_l11)) { + pr_err("could not get 8921_l11, rc = %ld\n", + PTR_ERR(reg_l11)); + return -ENODEV; + } + rc = regulator_set_voltage(reg_l11, 3000000, 3000000); + if (rc) { + pr_err("set_voltage l11 failed, rc=%d\n", rc); + return -EINVAL; + } + + if (machine_is_apq8064_liquid()) { + reg_ext_3p3v = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi1_vccs_3p3v"); + if (IS_ERR_OR_NULL(reg_ext_3p3v)) { + pr_err("could not get reg_ext_3p3v, rc = %ld\n", + PTR_ERR(reg_ext_3p3v)); + reg_ext_3p3v = NULL; + return -ENODEV; + } + mpp3 = PM8921_MPP_PM_TO_SYS(3); + rc = gpio_request(mpp3, "backlight_en"); + if (rc) { + pr_err("request mpp3 failed, rc=%d\n", rc); + return -ENODEV; + } + } + + gpio25 = PM8921_GPIO_PM_TO_SYS(25); + rc = gpio_request(gpio25, "disp_rst_n"); + if (rc) { + pr_err("request gpio 25 failed, rc=%d\n", rc); + return -ENODEV; + } + + gpio26 = PM8921_GPIO_PM_TO_SYS(26); + rc = gpio_request(gpio26, "pwm_backlight_ctrl"); + if (rc) { + pr_err("request gpio 26 failed, rc=%d\n", rc); + return -ENODEV; + } + + gpio36 = PM8921_GPIO_PM_TO_SYS(36); /* lcd1_pwr_en_n */ + rc = gpio_request(gpio36, "lcd1_pwr_en_n"); + if (rc) { + pr_err("request gpio 36 failed, rc=%d\n", rc); + return -ENODEV; + } + + dsi_power_on = true; + } + + if (on) { + rc = regulator_enable(reg_lvs7); + if (rc) { + pr_err("enable lvs7 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = regulator_set_optimum_mode(reg_l11, 110000); + if (rc < 0) { + pr_err("set_optimum_mode l11 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_l11); + if (rc) { + pr_err("enable l11 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = regulator_set_optimum_mode(reg_l2, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_l2); + if (rc) { + pr_err("enable l2 failed, rc=%d\n", rc); + return -ENODEV; + } + + if (machine_is_apq8064_liquid()) { + rc = regulator_enable(reg_ext_3p3v); + if (rc) { + pr_err("enable reg_ext_3p3v failed, rc=%d\n", + rc); + return -ENODEV; + } + gpio_set_value_cansleep(mpp3, 1); + } + + gpio_set_value_cansleep(gpio36, 0); + gpio_set_value_cansleep(gpio25, 1); + } else { + gpio_set_value_cansleep(gpio25, 0); + gpio_set_value_cansleep(gpio36, 1); + + if (machine_is_apq8064_liquid()) { + gpio_set_value_cansleep(mpp3, 0); + + rc = regulator_disable(reg_ext_3p3v); + if (rc) { + pr_err("disable reg_ext_3p3v failed, rc=%d\n", + rc); + return -ENODEV; + } + } + + rc = regulator_disable(reg_lvs7); + if (rc) { + pr_err("disable reg_lvs7 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_l2); + if (rc) { + pr_err("disable reg_l2 failed, rc=%d\n", rc); + return -ENODEV; + } + } + + return 0; +} + +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .dsi_power_save = mipi_dsi_panel_power, +}; + +static bool lvds_power_on; +static int lvds_panel_power(int on) +{ + static struct regulator *reg_lvs7, *reg_l2, *reg_ext_3p3v; + static int gpio36, gpio26, mpp3; + int rc; + + pr_debug("%s: on=%d\n", __func__, on); + + if (!lvds_power_on) { + reg_lvs7 = regulator_get(&msm_lvds_device.dev, + "lvds_vdda"); + if (IS_ERR_OR_NULL(reg_lvs7)) { + pr_err("could not get 8921_lvs7, rc = %ld\n", + PTR_ERR(reg_lvs7)); + return -ENODEV; + } + + reg_l2 = regulator_get(&msm_lvds_device.dev, + "lvds_pll_vdda"); + if (IS_ERR_OR_NULL(reg_l2)) { + pr_err("could not get 8921_l2, rc = %ld\n", + PTR_ERR(reg_l2)); + return -ENODEV; + } + + rc = regulator_set_voltage(reg_l2, 1200000, 1200000); + if (rc) { + pr_err("set_voltage l2 failed, rc=%d\n", rc); + return -EINVAL; + } + + reg_ext_3p3v = regulator_get(&msm_lvds_device.dev, + "lvds_vccs_3p3v"); + if (IS_ERR_OR_NULL(reg_ext_3p3v)) { + pr_err("could not get reg_ext_3p3v, rc = %ld\n", + PTR_ERR(reg_ext_3p3v)); + return -ENODEV; + } + + gpio26 = PM8921_GPIO_PM_TO_SYS(26); + rc = gpio_request(gpio26, "pwm_backlight_ctrl"); + if (rc) { + pr_err("request gpio 26 failed, rc=%d\n", rc); + return -ENODEV; + } + + gpio36 = PM8921_GPIO_PM_TO_SYS(36); /* lcd1_pwr_en_n */ + rc = gpio_request(gpio36, "lcd1_pwr_en_n"); + if (rc) { + pr_err("request gpio 36 failed, rc=%d\n", rc); + return -ENODEV; + } + + mpp3 = PM8921_MPP_PM_TO_SYS(3); + rc = gpio_request(mpp3, "backlight_en"); + if (rc) { + pr_err("request mpp3 failed, rc=%d\n", rc); + return -ENODEV; + } + + lvds_power_on = true; + } + + if (on) { + rc = regulator_enable(reg_lvs7); + if (rc) { + pr_err("enable lvs7 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = regulator_set_optimum_mode(reg_l2, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_l2); + if (rc) { + pr_err("enable l2 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = regulator_enable(reg_ext_3p3v); + if (rc) { + pr_err("enable reg_ext_3p3v failed, rc=%d\n", rc); + return -ENODEV; + } + + gpio_set_value_cansleep(gpio36, 0); + gpio_set_value_cansleep(mpp3, 1); + } else { + gpio_set_value_cansleep(mpp3, 0); + gpio_set_value_cansleep(gpio36, 1); + + rc = regulator_disable(reg_lvs7); + if (rc) { + pr_err("disable reg_lvs7 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_l2); + if (rc) { + pr_err("disable reg_l2 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_ext_3p3v); + if (rc) { + pr_err("disable reg_ext_3p3v failed, rc=%d\n", rc); + return -ENODEV; + } + } + + return 0; +} + +static int lvds_pixel_remap(void) +{ + if (machine_is_apq8064_cdp() || + machine_is_apq8064_liquid()) { + u32 ver = socinfo_get_version(); + if ((SOCINFO_VERSION_MAJOR(ver) == 1) && + (SOCINFO_VERSION_MINOR(ver) == 0)) + return 1; + } + return 0; +} + +static struct lcdc_platform_data lvds_pdata = { + .lcdc_power_save = lvds_panel_power, + .lvds_pixel_remap = lvds_pixel_remap +}; + +#define LPM_CHANNEL 2 +static int lvds_chimei_gpio[] = {LPM_CHANNEL}; + +static struct lvds_panel_platform_data lvds_chimei_pdata = { + .gpio = lvds_chimei_gpio, +}; + +static struct platform_device lvds_chimei_panel_device = { + .name = "lvds_chimei_wxga", + .id = 0, + .dev = { + .platform_data = &lvds_chimei_pdata, + } +}; + +static int dsi2lvds_gpio[2] = { + LPM_CHANNEL,/* Backlight PWM-ID=0 for PMIC-GPIO#24 */ + 0x1F08 /* DSI2LVDS Bridge GPIO Output, mask=0x1f, out=0x08 */ +}; +static struct msm_panel_common_pdata mipi_dsi2lvds_pdata = { + .gpio_num = dsi2lvds_gpio, +}; + +static struct platform_device mipi_dsi2lvds_bridge_device = { + .name = "mipi_tc358764", + .id = 0, + .dev.platform_data = &mipi_dsi2lvds_pdata, +}; + +static int toshiba_gpio[] = {LPM_CHANNEL}; +static struct mipi_dsi_panel_platform_data toshiba_pdata = { + .gpio = toshiba_gpio, +}; + +static struct platform_device mipi_dsi_toshiba_panel_device = { + .name = "mipi_toshiba", + .id = 0, + .dev = { + .platform_data = &toshiba_pdata, + } +}; + +static struct msm_bus_vectors dtv_bus_init_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors dtv_bus_def_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 566092800 * 2, + .ib = 707616000 * 2, + }, +}; + +static struct msm_bus_paths dtv_bus_scale_usecases[] = { + { + ARRAY_SIZE(dtv_bus_init_vectors), + dtv_bus_init_vectors, + }, + { + ARRAY_SIZE(dtv_bus_def_vectors), + dtv_bus_def_vectors, + }, +}; +static struct msm_bus_scale_pdata dtv_bus_scale_pdata = { + dtv_bus_scale_usecases, + ARRAY_SIZE(dtv_bus_scale_usecases), + .name = "dtv", +}; + +static struct lcdc_platform_data dtv_pdata = { + .bus_scale_table = &dtv_bus_scale_pdata, +}; + +static int hdmi_enable_5v(int on) +{ + /* TBD: PM8921 regulator instead of 8901 */ + static struct regulator *reg_8921_hdmi_mvs; /* HDMI_5V */ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_8921_hdmi_mvs) { + reg_8921_hdmi_mvs = regulator_get(&hdmi_msm_device.dev, + "hdmi_mvs"); + if (IS_ERR(reg_8921_hdmi_mvs)) { + pr_err("could not get reg_8921_hdmi_mvs, rc = %ld\n", + PTR_ERR(reg_8921_hdmi_mvs)); + reg_8921_hdmi_mvs = NULL; + return -ENODEV; + } + } + + if (on) { + rc = regulator_enable(reg_8921_hdmi_mvs); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "8921_hdmi_mvs", rc); + return rc; + } + pr_debug("%s(on): success\n", __func__); + } else { + rc = regulator_disable(reg_8921_hdmi_mvs); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "8921_hdmi_mvs", rc); + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +} + +static int hdmi_core_power(int on, int show) +{ + static struct regulator *reg_8921_lvs7, *reg_8921_s4, *reg_ext_3p3v; + static int prev_on; + int rc; + int pmic_gpio14 = PM8921_GPIO_PM_TO_SYS(14); + + if (on == prev_on) + return 0; + + /* TBD: PM8921 regulator instead of 8901 */ + if (!reg_ext_3p3v) { + reg_ext_3p3v = regulator_get(&hdmi_msm_device.dev, + "hdmi_mux_vdd"); + if (IS_ERR_OR_NULL(reg_ext_3p3v)) { + pr_err("could not get reg_ext_3p3v, rc = %ld\n", + PTR_ERR(reg_ext_3p3v)); + reg_ext_3p3v = NULL; + return -ENODEV; + } + } + + if (!reg_8921_lvs7) { + reg_8921_lvs7 = regulator_get(&hdmi_msm_device.dev, + "hdmi_vdda"); + if (IS_ERR(reg_8921_lvs7)) { + pr_err("could not get reg_8921_lvs7, rc = %ld\n", + PTR_ERR(reg_8921_lvs7)); + reg_8921_lvs7 = NULL; + return -ENODEV; + } + } + if (!reg_8921_s4) { + reg_8921_s4 = regulator_get(&hdmi_msm_device.dev, + "hdmi_lvl_tsl"); + if (IS_ERR(reg_8921_s4)) { + pr_err("could not get reg_8921_s4, rc = %ld\n", + PTR_ERR(reg_8921_s4)); + reg_8921_s4 = NULL; + return -ENODEV; + } + rc = regulator_set_voltage(reg_8921_s4, 1800000, 1800000); + if (rc) { + pr_err("set_voltage failed for 8921_s4, rc=%d\n", rc); + return -EINVAL; + } + } + + if (on) { + /* + * Configure 3P3V_BOOST_EN as GPIO, 8mA drive strength, + * pull none, out-high + */ + rc = regulator_set_optimum_mode(reg_ext_3p3v, 290000); + if (rc < 0) { + pr_err("set_optimum_mode ext_3p3v failed, rc=%d\n", rc); + return -EINVAL; + } + + rc = regulator_enable(reg_ext_3p3v); + if (rc) { + pr_err("enable reg_ext_3p3v failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_enable(reg_8921_lvs7); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "hdmi_vdda", rc); + return rc; + } + rc = regulator_enable(reg_8921_s4); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "hdmi_lvl_tsl", rc); + return rc; + } + rc = gpio_request(HDMI_DDC_CLK_GPIO, "HDMI_DDC_CLK"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_CLK", HDMI_DDC_CLK_GPIO, rc); + goto error1; + } + rc = gpio_request(HDMI_DDC_DATA_GPIO, "HDMI_DDC_DATA"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_DATA", HDMI_DDC_DATA_GPIO, rc); + goto error2; + } + rc = gpio_request(HDMI_HPD_GPIO, "HDMI_HPD"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_HPD", HDMI_HPD_GPIO, rc); + goto error3; + } + if (machine_is_apq8064_liquid()) { + rc = gpio_request(pmic_gpio14, "PMIC_HDMI_MUX_SEL"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "PMIC_HDMI_MUX_SEL", 14, rc); + goto error4; + } + gpio_set_value_cansleep(pmic_gpio14, 0); + } + pr_debug("%s(on): success\n", __func__); + } else { + gpio_free(HDMI_DDC_CLK_GPIO); + gpio_free(HDMI_DDC_DATA_GPIO); + gpio_free(HDMI_HPD_GPIO); + + if (machine_is_apq8064_liquid()) { + gpio_set_value_cansleep(pmic_gpio14, 1); + gpio_free(pmic_gpio14); + } + + rc = regulator_disable(reg_ext_3p3v); + if (rc) { + pr_err("disable reg_ext_3p3v failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_8921_lvs7); + if (rc) { + pr_err("disable reg_8921_l23 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_8921_s4); + if (rc) { + pr_err("disable reg_8921_s4 failed, rc=%d\n", rc); + return -ENODEV; + } + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; + +error4: + gpio_free(HDMI_HPD_GPIO); +error3: + gpio_free(HDMI_DDC_DATA_GPIO); +error2: + gpio_free(HDMI_DDC_CLK_GPIO); +error1: + regulator_disable(reg_8921_lvs7); + regulator_disable(reg_8921_s4); + return rc; +} + +static int hdmi_cec_power(int on) +{ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (on) { + rc = gpio_request(HDMI_CEC_VAR_GPIO, "HDMI_CEC_VAR"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_CEC_VAR", HDMI_CEC_VAR_GPIO, rc); + goto error; + } + pr_debug("%s(on): success\n", __func__); + } else { + gpio_free(HDMI_CEC_VAR_GPIO); + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +error: + return rc; +} + +void __init apq8064_init_fb(void) +{ + platform_device_register(&msm_fb_device); + platform_device_register(&lvds_chimei_panel_device); + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + platform_device_register(&wfd_panel_device); + platform_device_register(&wfd_device); +#endif + + if (machine_is_apq8064_liquid()) + platform_device_register(&mipi_dsi2lvds_bridge_device); + if (machine_is_apq8064_mtp()) + platform_device_register(&mipi_dsi_toshiba_panel_device); + + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("lvds", &lvds_pdata); + msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata); + platform_device_register(&hdmi_msm_device); + msm_fb_register_device("dtv", &dtv_pdata); +} + +/** + * Set MDP clocks to high frequency to avoid DSI underflow + * when using high resolution 1200x1920 WUXGA panels + */ +static void set_mdp_clocks_for_wuxga(void) +{ + int i; + + mdp_ui_vectors[0].ab = 2000000000; + mdp_ui_vectors[0].ib = 2000000000; + mdp_vga_vectors[0].ab = 2000000000; + mdp_vga_vectors[0].ib = 2000000000; + mdp_720p_vectors[0].ab = 2000000000; + mdp_720p_vectors[0].ib = 2000000000; + mdp_1080p_vectors[0].ab = 2000000000; + mdp_1080p_vectors[0].ib = 2000000000; + + mdp_pdata.mdp_core_clk_rate = 200000000; + + for (i = 0; i < ARRAY_SIZE(mdp_core_clk_rate_table); i++) + mdp_core_clk_rate_table[i] = 200000000; + + if (apq8064_hdmi_as_primary_selected()) { + dtv_bus_def_vectors[0].ab = 2000000000; + dtv_bus_def_vectors[0].ib = 2000000000; + } +} + +void __init apq8064_set_display_params(char *prim_panel, char *ext_panel) +{ + /* + * For certain MPQ boards, HDMI should be set as primary display + * by default, with the flexibility to specify any other panel + * as a primary panel through boot parameters. + */ + if (machine_is_mpq8064_hrd() || machine_is_mpq8064_cdp()) { + pr_debug("HDMI is the primary display by default for MPQ\n"); + if (!strnlen(prim_panel, PANEL_NAME_MAX_LEN)) + strlcpy(msm_fb_pdata.prim_panel_name, HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN); + } + + if (strnlen(prim_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.prim_panel_name, prim_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.prim_panel_name %s\n", + msm_fb_pdata.prim_panel_name); + + if (!strncmp((char *)msm_fb_pdata.prim_panel_name, + HDMI_PANEL_NAME, strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + pr_debug("HDMI is the primary display by" + " boot parameter\n"); + hdmi_is_primary = 1; + set_mdp_clocks_for_wuxga(); + } + } + if (strnlen(ext_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.ext_panel_name, ext_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.ext_panel_name %s\n", + msm_fb_pdata.ext_panel_name); + } +} diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c new file mode 100644 index 00000000000..b941bd468d5 --- /dev/null +++ b/arch/arm/mach-msm/board-8064-gpiomux.c @@ -0,0 +1,1262 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8064.h" + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static struct gpiomux_setting gpio_eth_config = { + .pull = GPIOMUX_PULL_NONE, + .drv = GPIOMUX_DRV_8MA, + .func = GPIOMUX_FUNC_GPIO, +}; + +/* The SPI configurations apply to GSBI 5*/ +static struct gpiomux_setting gpio_spi_config = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +/* The SPI configurations apply to GSBI 5 chip select 2*/ +static struct gpiomux_setting gpio_spi_cs2_config = { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +/* Chip selects for SPI clients */ +static struct gpiomux_setting gpio_spi_cs_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_UP, +}; + +/* Chip selects for EPM SPI clients */ +static struct gpiomux_setting gpio_epm_spi_cs_config = { + .func = GPIOMUX_FUNC_6, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_UP, +}; + +struct msm_gpiomux_config apq8064_ethernet_configs[] = { + { + .gpio = 43, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_eth_config, + [GPIOMUX_ACTIVE] = &gpio_eth_config, + } + }, +}; +#endif + +#ifdef CONFIG_MSM_VCAP +static struct gpiomux_setting gpio_vcap_config[] = { + { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_4, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_5, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_6, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_7, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_8, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_9, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + { + .func = GPIOMUX_FUNC_A, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, +}; + +struct msm_gpiomux_config vcap_configs[] = { + { + .gpio = 20, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[7], + } + }, + { + .gpio = 25, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 24, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[1], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[1], + } + }, + { + .gpio = 23, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 19, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[8], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[8], + } + }, + { + .gpio = 22, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 21, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[7], + } + }, + { + .gpio = 12, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[6], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[6], + } + }, + { + .gpio = 18, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[9], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[9], + } + }, + { + .gpio = 11, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[10], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[10], + } + }, + { + .gpio = 10, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[9], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[9], + } + }, + { + .gpio = 9, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 26, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[1], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[1], + } + }, + { + .gpio = 8, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[3], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[3], + } + }, + { + .gpio = 7, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[7], + } + }, + { + .gpio = 6, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[7], + } + }, + { + .gpio = 80, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 86, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[1], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[1], + } + }, + { + .gpio = 85, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[4], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[4], + } + }, + { + .gpio = 84, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[3], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[3], + } + }, + { + .gpio = 5, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 4, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[3], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[3], + } + }, + { + .gpio = 3, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[6], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[6], + } + }, + { + .gpio = 2, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[5], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[5], + } + }, + { + .gpio = 82, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[4], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[4], + } + }, + { + .gpio = 83, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[4], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[4], + } + }, + { + .gpio = 87, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[2], + } + }, + { + .gpio = 13, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_vcap_config[6], + [GPIOMUX_ACTIVE] = &gpio_vcap_config[6], + } + }, +}; +#endif + +static struct gpiomux_setting gpio_i2c_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gpio_i2c_config_sus = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting mbhc_hs_detect = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cdc_mclk = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting wcnss_5wire_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting wcnss_5wire_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + + +static struct gpiomux_setting slimbus = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting gsbi1_uart_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting ext_regulator_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_OUT_LOW, +}; + +static struct gpiomux_setting gsbi7_func1_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi7_func2_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi3_suspended_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting gsbi3_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting hdmi_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting hdmi_active_1_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting hdmi_active_2_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting gsbi5_suspended_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi5_active_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sx150x_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sx150x_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +#ifdef CONFIG_USB_EHCI_MSM_HSIC +static struct gpiomux_setting cyts_sleep_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cyts_sleep_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cyts_int_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting cyts_int_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct msm_gpiomux_config cyts_gpio_configs[] __initdata = { + { /* TS INTERRUPT */ + .gpio = 6, + .settings = { + [GPIOMUX_ACTIVE] = &cyts_int_act_cfg, + [GPIOMUX_SUSPENDED] = &cyts_int_sus_cfg, + }, + }, + { /* TS SLEEP */ + .gpio = 33, + .settings = { + [GPIOMUX_ACTIVE] = &cyts_sleep_act_cfg, + [GPIOMUX_SUSPENDED] = &cyts_sleep_sus_cfg, + }, + }, +}; + +static struct gpiomux_setting hsic_act_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting hsic_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + .dir = GPIOMUX_OUT_LOW, +}; + +static struct gpiomux_setting hsic_wakeup_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, + .dir = GPIOMUX_IN, +}; + +static struct gpiomux_setting hsic_wakeup_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + .dir = GPIOMUX_IN, +}; + +static struct msm_gpiomux_config apq8064_hsic_configs[] = { + { + .gpio = 88, /*HSIC_STROBE */ + .settings = { + [GPIOMUX_ACTIVE] = &hsic_act_cfg, + [GPIOMUX_SUSPENDED] = &hsic_sus_cfg, + }, + }, + { + .gpio = 89, /* HSIC_DATA */ + .settings = { + [GPIOMUX_ACTIVE] = &hsic_act_cfg, + [GPIOMUX_SUSPENDED] = &hsic_sus_cfg, + }, + }, + { + .gpio = 47, /* wake up */ + .settings = { + [GPIOMUX_ACTIVE] = &hsic_wakeup_act_cfg, + [GPIOMUX_SUSPENDED] = &hsic_wakeup_sus_cfg, + }, + }, +}; +#endif + +static struct gpiomux_setting mxt_reset_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mxt_reset_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting mxt_int_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mxt_int_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct msm_gpiomux_config apq8064_hdmi_configs[] __initdata = { + { + .gpio = 69, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 70, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 71, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 72, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_2_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config apq8064_gsbi_configs[] __initdata = { + { + .gpio = 8, /* GSBI3 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi3_active_cfg, + }, + }, + { + .gpio = 9, /* GSBI3 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi3_active_cfg, + }, + }, + { + .gpio = 18, /* GSBI1 UART TX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi1_uart_config, + }, + }, + { + .gpio = 19, /* GSBI1 UART RX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi1_uart_config, + }, + }, +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) + { + .gpio = 51, /* GSBI5 QUP SPI_DATA_MOSI */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, + { + .gpio = 52, /* GSBI5 QUP SPI_DATA_MISO */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, + { + .gpio = 53, /* Funny CS0 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, + { + .gpio = 31, /* GSBI5 QUP SPI_CS2_N */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_cs2_config, + }, + }, + { + .gpio = 54, /* GSBI5 QUP SPI_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, +#endif + { + .gpio = 30, /* FP CS */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_cs_config, + }, + }, + { + .gpio = 32, /* EPM CS */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_epm_spi_cs_config, + }, + }, + { + .gpio = 53, /* NOR CS */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_cs_config, + }, + }, + { + .gpio = 82, /* GSBI7 UART2 TX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi7_func2_cfg, + }, + }, + { + .gpio = 83, /* GSBI7 UART2 RX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi7_func1_cfg, + }, + }, + { + .gpio = 21, /* GSBI1 QUP I2C_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_i2c_config_sus, + [GPIOMUX_ACTIVE] = &gpio_i2c_config, + }, + }, + { + .gpio = 20, /* GSBI1 QUP I2C_DATA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_i2c_config_sus, + [GPIOMUX_ACTIVE] = &gpio_i2c_config, + }, + }, +}; + +static struct msm_gpiomux_config apq8064_slimbus_config[] __initdata = { + { + .gpio = 40, /* slimbus clk */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, + { + .gpio = 41, /* slimbus data */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, +}; + +static struct gpiomux_setting spkr_i2c = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct msm_gpiomux_config mpq8064_spkr_i2s_config[] __initdata = { + { + .gpio = 47, /* spkr i2c sck */ + .settings = { + [GPIOMUX_SUSPENDED] = &spkr_i2c, + }, + }, + { + .gpio = 48, /* spkr_i2s_ws */ + .settings = { + [GPIOMUX_SUSPENDED] = &spkr_i2c, + }, + }, + { + .gpio = 49, /* spkr_i2s_dout */ + .settings = { + [GPIOMUX_SUSPENDED] = &spkr_i2c, + }, + }, + { + .gpio = 50, /* spkr_i2s_mclk */ + .settings = { + [GPIOMUX_SUSPENDED] = &spkr_i2c, + }, + }, +}; + +static struct msm_gpiomux_config apq8064_audio_codec_configs[] __initdata = { + { + .gpio = 38, + .settings = { + [GPIOMUX_SUSPENDED] = &mbhc_hs_detect, + }, + }, + { + .gpio = 39, + .settings = { + [GPIOMUX_SUSPENDED] = &cdc_mclk, + }, + }, +}; + +/* External 3.3 V regulator enable */ +static struct msm_gpiomux_config apq8064_ext_regulator_configs[] __initdata = { + { + .gpio = APQ8064_EXT_3P3V_REG_EN_GPIO, + .settings = { + [GPIOMUX_SUSPENDED] = &ext_regulator_config, + }, + }, +}; + +static struct gpiomux_setting ap2mdm_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_status_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_errfatal_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ap2mdm_soft_reset_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ap2mdm_wakeup = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct msm_gpiomux_config mdm_configs[] __initdata = { + /* AP2MDM_STATUS */ + { + .gpio = 48, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* MDM2AP_STATUS */ + { + .gpio = 49, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_status_cfg, + } + }, + /* MDM2AP_ERRFATAL */ + { + .gpio = 19, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_errfatal_cfg, + } + }, + /* AP2MDM_ERRFATAL */ + { + .gpio = 18, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* AP2MDM_SOFT_RESET, aka AP2MDM_PON_RESET_N */ + { + .gpio = 27, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_soft_reset_cfg, + } + }, + /* AP2MDM_WAKEUP */ + { + .gpio = 35, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_wakeup, + } + }, +}; + +static struct gpiomux_setting mi2s_act_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mi2s_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct msm_gpiomux_config mpq8064_mi2s_configs[] __initdata = { + { + .gpio = 27, /* mi2s ws */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, + { + .gpio = 28, /* mi2s sclk */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, + { + .gpio = 29, /* mi2s dout3 */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, + { + .gpio = 30, /* mi2s dout2 */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, + + { + .gpio = 31, /* mi2s dout1 */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, + { + .gpio = 32, /* mi2s dout0 */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, + + { + .gpio = 33, /* mi2s mclk */ + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_act_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_sus_cfg, + }, + }, +}; +static struct msm_gpiomux_config apq8064_mxt_configs[] __initdata = { + { /* TS INTERRUPT */ + .gpio = 6, + .settings = { + [GPIOMUX_ACTIVE] = &mxt_int_act_cfg, + [GPIOMUX_SUSPENDED] = &mxt_int_sus_cfg, + }, + }, + { /* TS RESET */ + .gpio = 33, + .settings = { + [GPIOMUX_ACTIVE] = &mxt_reset_act_cfg, + [GPIOMUX_SUSPENDED] = &mxt_reset_sus_cfg, + }, + }, +}; + +static struct msm_gpiomux_config wcnss_5wire_interface[] = { + { + .gpio = 64, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 65, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 66, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 67, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 68, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config mpq8064_gsbi5_i2c_configs[] __initdata = { + { + .gpio = 53, /* GSBI5 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi5_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi5_active_cfg, + }, + }, + { + .gpio = 54, /* GSBI5 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi5_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi5_active_cfg, + }, + }, +}; + +static struct gpiomux_setting ir_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ir_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct msm_gpiomux_config mpq8064_ir_configs[] __initdata = { + { + .gpio = 88, /* GPIO IR */ + .settings = { + [GPIOMUX_SUSPENDED] = &ir_suspended_cfg, + [GPIOMUX_ACTIVE] = &ir_active_cfg, + }, + }, +}; + +static struct msm_gpiomux_config sx150x_int_configs[] __initdata = { + { + .gpio = 81, + .settings = { + [GPIOMUX_SUSPENDED] = &sx150x_suspended_cfg, + [GPIOMUX_ACTIVE] = &sx150x_active_cfg, + }, + }, +}; + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct gpiomux_setting sdc2_clk_active_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdc2_cmd_data_0_3_active_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdc2_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting sdc2_data_1_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct msm_gpiomux_config apq8064_sdc2_configs[] __initdata = { + { + .gpio = 59, + .settings = { + [GPIOMUX_ACTIVE] = &sdc2_clk_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg, + }, + }, + { + .gpio = 57, + .settings = { + [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg, + }, + + }, + { + .gpio = 62, + .settings = { + [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg, + }, + }, + { + .gpio = 61, + .settings = { + [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc2_data_1_suspended_cfg, + }, + }, + { + .gpio = 60, + .settings = { + [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg, + }, + }, + { + .gpio = 58, + .settings = { + [GPIOMUX_ACTIVE] = &sdc2_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc2_suspended_cfg, + }, + }, +}; +#endif + + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct gpiomux_setting sdc4_clk_active_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdc4_cmd_data_0_3_active_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdc4_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting sdc4_data_1_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct msm_gpiomux_config apq8064_sdc4_configs[] __initdata = { + { + .gpio = 68, + .settings = { + [GPIOMUX_ACTIVE] = &sdc4_clk_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg, + }, + }, + { + .gpio = 67, + .settings = { + [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg, + }, + + }, + { + .gpio = 66, + .settings = { + [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg, + }, + }, + { + .gpio = 65, + .settings = { + [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc4_data_1_suspended_cfg, + }, + }, + { + .gpio = 64, + .settings = { + [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg, + }, + }, + { + .gpio = 63, + .settings = { + [GPIOMUX_ACTIVE] = &sdc4_cmd_data_0_3_active_cfg, + [GPIOMUX_SUSPENDED] = &sdc4_suspended_cfg, + }, + }, +}; +#endif + +void __init apq8064_init_gpiomux(void) +{ + int rc; + + rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc); + return; + } + + msm_gpiomux_install(wcnss_5wire_interface, + ARRAY_SIZE(wcnss_5wire_interface)); + + if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() || + machine_is_mpq8064_dtv()) { + msm_gpiomux_install(mpq8064_gsbi5_i2c_configs, + ARRAY_SIZE(mpq8064_gsbi5_i2c_configs)); +#ifdef CONFIG_MSM_VCAP + msm_gpiomux_install(vcap_configs, + ARRAY_SIZE(vcap_configs)); +#endif + msm_gpiomux_install(sx150x_int_configs, + ARRAY_SIZE(sx150x_int_configs)); + } else { + #if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) + msm_gpiomux_install(apq8064_ethernet_configs, + ARRAY_SIZE(apq8064_ethernet_configs)); + #endif + + msm_gpiomux_install(apq8064_gsbi_configs, + ARRAY_SIZE(apq8064_gsbi_configs)); + } + + msm_gpiomux_install(apq8064_slimbus_config, + ARRAY_SIZE(apq8064_slimbus_config)); + + msm_gpiomux_install(apq8064_audio_codec_configs, + ARRAY_SIZE(apq8064_audio_codec_configs)); + + if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() || + machine_is_mpq8064_dtv()) { + msm_gpiomux_install(mpq8064_spkr_i2s_config, + ARRAY_SIZE(mpq8064_spkr_i2s_config)); + } + + pr_debug("%s(): audio-auxpcm: Include GPIO configs" + " as audio is not the primary user" + " for these GPIO Pins\n", __func__); + + if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() || + machine_is_mpq8064_dtv()) + msm_gpiomux_install(mpq8064_mi2s_configs, + ARRAY_SIZE(mpq8064_mi2s_configs)); + + msm_gpiomux_install(apq8064_ext_regulator_configs, + ARRAY_SIZE(apq8064_ext_regulator_configs)); + + if (machine_is_apq8064_mtp()) + msm_gpiomux_install(mdm_configs, + ARRAY_SIZE(mdm_configs)); + +#ifdef CONFIG_USB_EHCI_MSM_HSIC + if (machine_is_apq8064_mtp()) + msm_gpiomux_install(cyts_gpio_configs, + ARRAY_SIZE(cyts_gpio_configs)); + + if (machine_is_apq8064_mtp()) + msm_gpiomux_install(apq8064_hsic_configs, + ARRAY_SIZE(apq8064_hsic_configs)); +#endif + + if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid()) + msm_gpiomux_install(apq8064_mxt_configs, + ARRAY_SIZE(apq8064_mxt_configs)); + + msm_gpiomux_install(apq8064_hdmi_configs, + ARRAY_SIZE(apq8064_hdmi_configs)); + + if (machine_is_mpq8064_cdp()) + msm_gpiomux_install(mpq8064_ir_configs, + ARRAY_SIZE(mpq8064_ir_configs)); + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + msm_gpiomux_install(apq8064_sdc2_configs, + ARRAY_SIZE(apq8064_sdc2_configs)); +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + msm_gpiomux_install(apq8064_sdc4_configs, + ARRAY_SIZE(apq8064_sdc4_configs)); +#endif +} diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c new file mode 100644 index 00000000000..e24cac65816 --- /dev/null +++ b/arch/arm/mach-msm/board-8064-gpu.c @@ -0,0 +1,251 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-8064.h" + +#ifdef CONFIG_MSM_DCVS +static struct msm_dcvs_freq_entry grp3d_freq[] = { + {0, 0, 333932}, + {0, 0, 497532}, + {0, 0, 707610}, + {0, 0, 844545}, +}; + +static struct msm_dcvs_core_info grp3d_core_info = { + .freq_tbl = &grp3d_freq[0], + .core_param = { + .max_time_us = 100000, + .num_freq = ARRAY_SIZE(grp3d_freq), + }, + .algo_param = { + .slack_time_us = 39000, + .disable_pc_threshold = 86000, + .ss_window_size = 1000000, + .ss_util_pct = 95, + .em_max_util_pct = 97, + .ss_iobusy_conv = 100, + }, +}; +#endif /* CONFIG_MSM_DCVS */ + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors grp3d_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp3d_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(1000), + }, + { + .src = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(1000), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2000), + }, + { + .src = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2000), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_high_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(3200), + }, + { + .src = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(3200), + }, +}; + +static struct msm_bus_vectors grp3d_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(4264), + }, + { + .src = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(4264), + }, +}; + +static struct msm_bus_paths grp3d_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp3d_init_vectors), + grp3d_init_vectors, + }, + { + ARRAY_SIZE(grp3d_low_vectors), + grp3d_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_low_vectors), + grp3d_nominal_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_high_vectors), + grp3d_nominal_high_vectors, + }, + { + ARRAY_SIZE(grp3d_max_vectors), + grp3d_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata grp3d_bus_scale_pdata = { + grp3d_bus_scale_usecases, + ARRAY_SIZE(grp3d_bus_scale_usecases), + .name = "grp3d", +}; +#endif + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0x04300000, /* GFX3D address */ + .end = 0x0431ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = GFX3D_IRQ, + .end = GFX3D_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct kgsl_iommu_ctx kgsl_3d0_iommu0_ctxs[] = { + { "gfx3d_user", 0 }, + { "gfx3d_priv", 1 }, +}; + +static const struct kgsl_iommu_ctx kgsl_3d0_iommu1_ctxs[] = { + { "gfx3d1_user", 0 }, + { "gfx3d1_priv", 1 }, +}; + +static struct kgsl_device_iommu_data kgsl_3d0_iommu_data[] = { + { + .iommu_ctxs = kgsl_3d0_iommu0_ctxs, + .iommu_ctx_count = ARRAY_SIZE(kgsl_3d0_iommu0_ctxs), + .physstart = 0x07C00000, + .physend = 0x07C00000 + SZ_1M - 1, + }, + { + .iommu_ctxs = kgsl_3d0_iommu1_ctxs, + .iommu_ctx_count = ARRAY_SIZE(kgsl_3d0_iommu1_ctxs), + .physstart = 0x07D00000, + .physend = 0x07D00000 + SZ_1M - 1, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 400000000, + .bus_freq = 4, + .io_fraction = 0, + }, + { + .gpu_freq = 325000000, + .bus_freq = 3, + .io_fraction = 33, + }, + { + .gpu_freq = 200000000, + .bus_freq = 2, + .io_fraction = 100, + }, + { + .gpu_freq = 128000000, + .bus_freq = 1, + .io_fraction = 100, + }, + { + .gpu_freq = 27000000, + .bus_freq = 0, + }, + }, + .init_level = 1, + .num_levels = 5, + .set_grp_async = NULL, + .idle_timeout = HZ/10, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp3d_bus_scale_pdata, +#endif + .iommu_data = kgsl_3d0_iommu_data, + .iommu_count = ARRAY_SIZE(kgsl_3d0_iommu_data), +#ifdef CONFIG_MSM_DCVS + .core_info = &grp3d_core_info, +#endif +}; + +struct platform_device device_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +void __init apq8064_init_gpu(void) +{ + platform_device_register(&device_kgsl_3d0); +} diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c new file mode 100644 index 00000000000..2009584e214 --- /dev/null +++ b/arch/arm/mach-msm/board-8064-pmic.c @@ -0,0 +1,444 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8064.h" + +struct pm8xxx_gpio_init { + unsigned gpio; + struct pm_gpio config; +}; + +struct pm8xxx_mpp_init { + unsigned mpp; + struct pm8xxx_mpp_config_data config; +}; + +#define PM8921_GPIO_INIT(_gpio, _dir, _buf, _val, _pull, _vin, _out_strength, \ + _func, _inv, _disable) \ +{ \ + .gpio = PM8921_GPIO_PM_TO_SYS(_gpio), \ + .config = { \ + .direction = _dir, \ + .output_buffer = _buf, \ + .output_value = _val, \ + .pull = _pull, \ + .vin_sel = _vin, \ + .out_strength = _out_strength, \ + .function = _func, \ + .inv_int_pol = _inv, \ + .disable_pin = _disable, \ + } \ +} + +#define PM8921_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8921_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +#define PM8821_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8821_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +#define PM8921_GPIO_DISABLE(_gpio) \ + PM8921_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, 0, 0, 0, PM_GPIO_VIN_S4, \ + 0, 0, 0, 1) + +#define PM8921_GPIO_OUTPUT(_gpio, _val, _strength) \ + PM8921_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_##_strength, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8921_GPIO_OUTPUT_BUFCONF(_gpio, _val, _strength, _bufconf) \ + PM8921_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT,\ + PM_GPIO_OUT_BUF_##_bufconf, _val, \ + PM_GPIO_PULL_NO, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_##_strength, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8921_GPIO_INPUT(_gpio, _pull) \ + PM8921_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, PM_GPIO_OUT_BUF_CMOS, 0, \ + _pull, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_NO, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8921_GPIO_OUTPUT_FUNC(_gpio, _val, _func) \ + PM8921_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_HIGH, \ + _func, 0, 0) + +#define PM8921_GPIO_OUTPUT_VIN(_gpio, _val, _vin) \ + PM8921_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, _vin, \ + PM_GPIO_STRENGTH_HIGH, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +/* Initial PM8921 GPIO configurations */ +static struct pm8xxx_gpio_init pm8921_gpios[] __initdata = { + PM8921_GPIO_OUTPUT(14, 1, HIGH), /* HDMI Mux Selector */ + PM8921_GPIO_OUTPUT(23, 0, HIGH), /* touchscreen power FET */ + PM8921_GPIO_OUTPUT_BUFCONF(25, 0, LOW, CMOS), /* DISP_RESET_N */ + PM8921_GPIO_OUTPUT_FUNC(26, 0, PM_GPIO_FUNC_2), /* Bl: Off, PWM mode */ + PM8921_GPIO_OUTPUT_VIN(30, 1, PM_GPIO_VIN_VPH), /* SMB349 susp line */ + PM8921_GPIO_OUTPUT_BUFCONF(36, 1, LOW, OPEN_DRAIN), + PM8921_GPIO_OUTPUT_FUNC(44, 0, PM_GPIO_FUNC_2), + PM8921_GPIO_OUTPUT(33, 0, HIGH), + PM8921_GPIO_OUTPUT(20, 0, HIGH), + PM8921_GPIO_INPUT(35, PM_GPIO_PULL_UP_30), + PM8921_GPIO_INPUT(38, PM_GPIO_PULL_UP_30), + /* TABLA CODEC RESET */ + PM8921_GPIO_OUTPUT(34, 1, MED), + PM8921_GPIO_OUTPUT(13, 0, HIGH), /* PCIE_CLK_PWR_EN */ +}; + +static struct pm8xxx_gpio_init pm8921_mtp_kp_gpios[] __initdata = { + PM8921_GPIO_INPUT(3, PM_GPIO_PULL_UP_30), + PM8921_GPIO_INPUT(4, PM_GPIO_PULL_UP_30), +}; + +static struct pm8xxx_gpio_init pm8921_cdp_kp_gpios[] __initdata = { + PM8921_GPIO_INPUT(27, PM_GPIO_PULL_UP_30), + PM8921_GPIO_INPUT(42, PM_GPIO_PULL_UP_30), + PM8921_GPIO_INPUT(17, PM_GPIO_PULL_UP_1P5), /* SD_WP */ +}; + +/* Initial PM8XXX MPP configurations */ +static struct pm8xxx_mpp_init pm8xxx_mpps[] __initdata = { + PM8921_MPP_INIT(3, D_OUTPUT, PM8921_MPP_DIG_LEVEL_VPH, DOUT_CTRL_LOW), + PM8921_MPP_INIT(8, D_OUTPUT, PM8921_MPP_DIG_LEVEL_S4, DOUT_CTRL_LOW), + /*MPP9 is used to detect docking station connection/removal on Liquid*/ + PM8921_MPP_INIT(9, D_INPUT, PM8921_MPP_DIG_LEVEL_S4, DIN_TO_INT), + /* PCIE_RESET_N */ + PM8921_MPP_INIT(1, D_OUTPUT, PM8921_MPP_DIG_LEVEL_VPH, DOUT_CTRL_HIGH), +}; + +void __init apq8064_pm8xxx_gpio_mpp_init(void) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(pm8921_gpios); i++) { + rc = pm8xxx_gpio_config(pm8921_gpios[i].gpio, + &pm8921_gpios[i].config); + if (rc) { + pr_err("%s: pm8xxx_gpio_config: rc=%d\n", __func__, rc); + break; + } + } + + if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid()) + for (i = 0; i < ARRAY_SIZE(pm8921_cdp_kp_gpios); i++) { + rc = pm8xxx_gpio_config(pm8921_cdp_kp_gpios[i].gpio, + &pm8921_cdp_kp_gpios[i].config); + if (rc) { + pr_err("%s: pm8xxx_gpio_config: rc=%d\n", + __func__, rc); + break; + } + } + + if (machine_is_apq8064_mtp()) + for (i = 0; i < ARRAY_SIZE(pm8921_mtp_kp_gpios); i++) { + rc = pm8xxx_gpio_config(pm8921_mtp_kp_gpios[i].gpio, + &pm8921_mtp_kp_gpios[i].config); + if (rc) { + pr_err("%s: pm8xxx_gpio_config: rc=%d\n", + __func__, rc); + break; + } + } + + for (i = 0; i < ARRAY_SIZE(pm8xxx_mpps); i++) { + rc = pm8xxx_mpp_config(pm8xxx_mpps[i].mpp, + &pm8xxx_mpps[i].config); + if (rc) { + pr_err("%s: pm8xxx_mpp_config: rc=%d\n", __func__, rc); + break; + } + } +} + +static struct pm8xxx_pwrkey_platform_data apq8064_pm8921_pwrkey_pdata = { + .pull_up = 1, + .kpd_trigger_delay_us = 15625, + .wakeup = 1, +}; + +static struct pm8xxx_misc_platform_data apq8064_pm8921_misc_pdata = { + .priority = 0, +}; + +#define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */ +#define PM8921_LC_LED_LOW_CURRENT 1 /* I = 1mA */ +#define PM8XXX_LED_PWM_PERIOD 1000 +#define PM8XXX_LED_PWM_DUTY_MS 20 +/** + * PM8XXX_PWM_CHANNEL_NONE shall be used when LED shall not be + * driven using PWM feature. + */ +#define PM8XXX_PWM_CHANNEL_NONE -1 + +static struct led_info pm8921_led_info[] = { + [0] = { + .name = "led:red", + .default_trigger = "ac-online", + }, +}; + +static struct led_platform_data pm8921_led_core_pdata = { + .num_leds = ARRAY_SIZE(pm8921_led_info), + .leds = pm8921_led_info, +}; + +static int pm8921_led0_pwm_duty_pcts[56] = { + 1, 4, 8, 12, 16, 20, 24, 28, 32, 36, + 40, 44, 46, 52, 56, 60, 64, 68, 72, 76, + 80, 84, 88, 92, 96, 100, 100, 100, 98, 95, + 92, 88, 84, 82, 78, 74, 70, 66, 62, 58, + 58, 54, 50, 48, 42, 38, 34, 30, 26, 22, + 14, 10, 6, 4, 1 +}; + +static struct pm8xxx_pwm_duty_cycles pm8921_led0_pwm_duty_cycles = { + .duty_pcts = (int *)&pm8921_led0_pwm_duty_pcts, + .num_duty_pcts = ARRAY_SIZE(pm8921_led0_pwm_duty_pcts), + .duty_ms = PM8XXX_LED_PWM_DUTY_MS, + .start_idx = 0, +}; + +static struct pm8xxx_led_config pm8921_led_configs[] = { + [0] = { + .id = PM8XXX_ID_LED_0, + .mode = PM8XXX_LED_MODE_PWM2, + .max_current = PM8921_LC_LED_MAX_CURRENT, + .pwm_channel = 5, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD, + .pwm_duty_cycles = &pm8921_led0_pwm_duty_cycles, + }, +}; + +static struct pm8xxx_led_platform_data apq8064_pm8921_leds_pdata = { + .led_core = &pm8921_led_core_pdata, + .configs = pm8921_led_configs, + .num_configs = ARRAY_SIZE(pm8921_led_configs), +}; + +static struct pm8xxx_adc_amux apq8064_pm8921_adc_channels_data[] = { + {"vcoin", CHANNEL_VCOIN, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vbat", CHANNEL_VBAT, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"dcin", CHANNEL_DCIN, CHAN_PATH_SCALING4, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"ichg", CHANNEL_ICHG, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vph_pwr", CHANNEL_VPH_PWR, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"ibat", CHANNEL_IBAT, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"batt_therm", CHANNEL_BATT_THERM, CHAN_PATH_SCALING1, AMUX_RSV2, + ADC_DECIMATION_TYPE2, ADC_SCALE_BATT_THERM}, + {"batt_id", CHANNEL_BATT_ID, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"usbin", CHANNEL_USBIN, CHAN_PATH_SCALING3, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pmic_therm", CHANNEL_DIE_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PMIC_THERM}, + {"625mv", CHANNEL_625MV, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"125v", CHANNEL_125V, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"chg_temp", CHANNEL_CHG_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"xo_therm", CHANNEL_MUXOFF, CHAN_PATH_SCALING1, AMUX_RSV0, + ADC_DECIMATION_TYPE2, ADC_SCALE_XOTHERM}, +}; + +static struct pm8xxx_adc_properties apq8064_pm8921_adc_data = { + .adc_vdd_reference = 1800, /* milli-voltage for this adc */ + .bitresolution = 15, + .bipolar = 0, +}; + +static struct pm8xxx_adc_platform_data apq8064_pm8921_adc_pdata = { + .adc_channel = apq8064_pm8921_adc_channels_data, + .adc_num_board_channel = ARRAY_SIZE(apq8064_pm8921_adc_channels_data), + .adc_prop = &apq8064_pm8921_adc_data, + .adc_mpp_base = PM8921_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_mpp_platform_data +apq8064_pm8921_mpp_pdata __devinitdata = { + .mpp_base = PM8921_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_gpio_platform_data +apq8064_pm8921_gpio_pdata __devinitdata = { + .gpio_base = PM8921_GPIO_PM_TO_SYS(1), +}; + +static struct pm8xxx_irq_platform_data +apq8064_pm8921_irq_pdata __devinitdata = { + .irq_base = PM8921_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(74), + .irq_trigger_flag = IRQF_TRIGGER_LOW, + .dev_id = 0, +}; + +static struct pm8xxx_rtc_platform_data +apq8064_pm8921_rtc_pdata = { + .rtc_write_enable = false, + .rtc_alarm_powerup = false, +}; + +static int apq8064_pm8921_therm_mitigation[] = { + 1100, + 700, + 600, + 325, +}; + +#define MAX_VOLTAGE_MV 4200 +static struct pm8921_charger_platform_data +apq8064_pm8921_chg_pdata __devinitdata = { + .safety_time = 180, + .update_time = 60000, + .max_voltage = MAX_VOLTAGE_MV, + .min_voltage = 3200, + .resume_voltage_delta = 100, + .term_current = 100, + .cool_temp = 10, + .warm_temp = 40, + .temp_check_period = 1, + .max_bat_chg_current = 1100, + .cool_bat_chg_current = 350, + .warm_bat_chg_current = 350, + .cool_bat_voltage = 4100, + .warm_bat_voltage = 4100, + .thermal_mitigation = apq8064_pm8921_therm_mitigation, + .thermal_levels = ARRAY_SIZE(apq8064_pm8921_therm_mitigation), +}; + +static struct pm8xxx_ccadc_platform_data +apq8064_pm8xxx_ccadc_pdata = { + .r_sense = 10, +}; + +static struct pm8921_bms_platform_data +apq8064_pm8921_bms_pdata __devinitdata = { + .battery_type = BATT_UNKNOWN, + .r_sense = 10, + .i_test = 2500, + .v_failure = 3000, + .calib_delay_ms = 600000, + .max_voltage_uv = MAX_VOLTAGE_MV * 1000, +}; + +static struct pm8921_platform_data +apq8064_pm8921_platform_data __devinitdata = { + .regulator_pdatas = msm8064_pm8921_regulator_pdata, + .irq_pdata = &apq8064_pm8921_irq_pdata, + .gpio_pdata = &apq8064_pm8921_gpio_pdata, + .mpp_pdata = &apq8064_pm8921_mpp_pdata, + .rtc_pdata = &apq8064_pm8921_rtc_pdata, + .pwrkey_pdata = &apq8064_pm8921_pwrkey_pdata, + .misc_pdata = &apq8064_pm8921_misc_pdata, + .leds_pdata = &apq8064_pm8921_leds_pdata, + .adc_pdata = &apq8064_pm8921_adc_pdata, + .charger_pdata = &apq8064_pm8921_chg_pdata, + .bms_pdata = &apq8064_pm8921_bms_pdata, + .ccadc_pdata = &apq8064_pm8xxx_ccadc_pdata, +}; + +static struct pm8xxx_irq_platform_data +apq8064_pm8821_irq_pdata __devinitdata = { + .irq_base = PM8821_IRQ_BASE, + .devirq = PM8821_SEC_IRQ_N, + .irq_trigger_flag = IRQF_TRIGGER_HIGH, + .dev_id = 1, +}; + +static struct pm8xxx_mpp_platform_data +apq8064_pm8821_mpp_pdata __devinitdata = { + .mpp_base = PM8821_MPP_PM_TO_SYS(1), +}; + +static struct pm8821_platform_data +apq8064_pm8821_platform_data __devinitdata = { + .irq_pdata = &apq8064_pm8821_irq_pdata, + .mpp_pdata = &apq8064_pm8821_mpp_pdata, +}; + +static struct msm_ssbi_platform_data apq8064_ssbi_pm8921_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = "pm8921-core", + .platform_data = &apq8064_pm8921_platform_data, + }, +}; + +static struct msm_ssbi_platform_data apq8064_ssbi_pm8821_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = "pm8821-core", + .platform_data = &apq8064_pm8821_platform_data, + }, +}; + +void __init apq8064_init_pmic(void) +{ + pmic_reset_irq = PM8921_IRQ_BASE + PM8921_RESOUT_IRQ; + + apq8064_device_ssbi_pmic1.dev.platform_data = + &apq8064_ssbi_pm8921_pdata; + apq8064_device_ssbi_pmic2.dev.platform_data = + &apq8064_ssbi_pm8821_pdata; + apq8064_pm8921_platform_data.num_regulators = + msm8064_pm8921_regulator_pdata_len; + + if (machine_is_apq8064_rumi3()) { + apq8064_pm8921_irq_pdata.devirq = 0; + apq8064_pm8821_irq_pdata.devirq = 0; + } else if (machine_is_apq8064_mtp()) { + apq8064_pm8921_bms_pdata.battery_type = BATT_PALLADIUM; + } else if (machine_is_apq8064_liquid()) { + apq8064_pm8921_bms_pdata.battery_type = BATT_DESAY; + } +} diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c new file mode 100644 index 00000000000..f7d5403b9a5 --- /dev/null +++ b/arch/arm/mach-msm/board-8064-regulator.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include "board-8064.h" + +#define VREG_CONSUMERS(_id) \ + static struct regulator_consumer_supply vreg_consumers_##_id[] + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +VREG_CONSUMERS(L1) = { + REGULATOR_SUPPLY("8921_l1", NULL), +}; +VREG_CONSUMERS(L2) = { + REGULATOR_SUPPLY("8921_l2", NULL), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.0"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.1"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.2"), + REGULATOR_SUPPLY("lvds_pll_vdda", "lvds.0"), + REGULATOR_SUPPLY("dsi1_pll_vdda", "mipi_dsi.1"), +}; +VREG_CONSUMERS(L3) = { + REGULATOR_SUPPLY("8921_l3", NULL), + REGULATOR_SUPPLY("HSUSB_3p3", "msm_otg"), + REGULATOR_SUPPLY("HSUSB_3p3", "msm_ehci_host.0"), + REGULATOR_SUPPLY("HSUSB_3p3", "msm_ehci_host.1"), +}; +VREG_CONSUMERS(L4) = { + REGULATOR_SUPPLY("8921_l4", NULL), + REGULATOR_SUPPLY("HSUSB_1p8", "msm_otg"), + REGULATOR_SUPPLY("iris_vddxo", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L5) = { + REGULATOR_SUPPLY("8921_l5", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.1"), +}; +VREG_CONSUMERS(L6) = { + REGULATOR_SUPPLY("8921_l6", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.3"), +}; +VREG_CONSUMERS(L7) = { + REGULATOR_SUPPLY("8921_l7", NULL), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.3"), +}; +VREG_CONSUMERS(L8) = { + REGULATOR_SUPPLY("8921_l8", NULL), + REGULATOR_SUPPLY("cam_vana", "4-001a"), + REGULATOR_SUPPLY("cam_vana", "4-0048"), + REGULATOR_SUPPLY("cam_vana", "4-006c"), + REGULATOR_SUPPLY("cam_vana", "4-0034"), + REGULATOR_SUPPLY("cam_vana", "4-0020"), +}; +VREG_CONSUMERS(L9) = { + REGULATOR_SUPPLY("8921_l9", NULL), + REGULATOR_SUPPLY("vdd", "3-0024"), +}; +VREG_CONSUMERS(L10) = { + REGULATOR_SUPPLY("8921_l10", NULL), + REGULATOR_SUPPLY("iris_vddpa", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L11) = { + REGULATOR_SUPPLY("8921_l11", NULL), + REGULATOR_SUPPLY("dsi1_avdd", "mipi_dsi.1"), +}; +VREG_CONSUMERS(L12) = { + REGULATOR_SUPPLY("cam_vdig", "4-001a"), + REGULATOR_SUPPLY("cam_vdig", "4-0048"), + REGULATOR_SUPPLY("cam_vdig", "4-006c"), + REGULATOR_SUPPLY("cam_vdig", "4-0034"), + REGULATOR_SUPPLY("cam_vdig", "4-0020"), + REGULATOR_SUPPLY("8921_l12", NULL), +}; +VREG_CONSUMERS(L14) = { + REGULATOR_SUPPLY("8921_l14", NULL), +}; +VREG_CONSUMERS(L15) = { + REGULATOR_SUPPLY("8921_l15", NULL), +}; +VREG_CONSUMERS(L16) = { + REGULATOR_SUPPLY("8921_l16", NULL), + REGULATOR_SUPPLY("cam_vaf", "4-001a"), + REGULATOR_SUPPLY("cam_vaf", "4-0048"), + REGULATOR_SUPPLY("cam_vaf", "4-006c"), + REGULATOR_SUPPLY("cam_vaf", "4-0034"), + REGULATOR_SUPPLY("cam_vaf", "4-0020"), +}; +VREG_CONSUMERS(L17) = { + REGULATOR_SUPPLY("8921_l17", NULL), +}; +VREG_CONSUMERS(L18) = { + REGULATOR_SUPPLY("8921_l18", NULL), +}; +VREG_CONSUMERS(L21) = { + REGULATOR_SUPPLY("8921_l21", NULL), +}; +VREG_CONSUMERS(L22) = { + REGULATOR_SUPPLY("8921_l22", NULL), +}; +VREG_CONSUMERS(L23) = { + REGULATOR_SUPPLY("8921_l23", NULL), + REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"), + REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"), + REGULATOR_SUPPLY("HSUSB_1p8", "msm_ehci_host.0"), + REGULATOR_SUPPLY("HSUSB_1p8", "msm_ehci_host.1"), +}; +VREG_CONSUMERS(L24) = { + REGULATOR_SUPPLY("8921_l24", NULL), + REGULATOR_SUPPLY("riva_vddmx", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L25) = { + REGULATOR_SUPPLY("8921_l25", NULL), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla-slim"), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla2x-slim"), +}; +VREG_CONSUMERS(L26) = { + REGULATOR_SUPPLY("8921_l26", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"), +}; +VREG_CONSUMERS(L27) = { + REGULATOR_SUPPLY("8921_l27", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"), +}; +VREG_CONSUMERS(L28) = { + REGULATOR_SUPPLY("8921_l28", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"), +}; +VREG_CONSUMERS(L29) = { + REGULATOR_SUPPLY("8921_l29", NULL), +}; +VREG_CONSUMERS(S1) = { + REGULATOR_SUPPLY("8921_s1", NULL), +}; +VREG_CONSUMERS(S2) = { + REGULATOR_SUPPLY("8921_s2", NULL), + REGULATOR_SUPPLY("iris_vddrfa", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(S3) = { + REGULATOR_SUPPLY("8921_s3", NULL), + REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_otg"), + REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_ehci_host.0"), + REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_ehci_host.1"), + REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_host"), + REGULATOR_SUPPLY("riva_vddcx", "wcnss_wlan.0"), + REGULATOR_SUPPLY("vp_pcie", "msm_pcie"), + REGULATOR_SUPPLY("vptx_pcie", "msm_pcie"), +}; +VREG_CONSUMERS(S4) = { + REGULATOR_SUPPLY("8921_s4", NULL), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.1"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla-slim"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla2x-slim"), + REGULATOR_SUPPLY("riva_vddpx", "wcnss_wlan.0"), + REGULATOR_SUPPLY("vcc_i2c", "3-005b"), + REGULATOR_SUPPLY("vcc_i2c", "3-0024"), + REGULATOR_SUPPLY("vddp", "0-0048"), + REGULATOR_SUPPLY("hdmi_lvl_tsl", "hdmi_msm.0"), +}; +VREG_CONSUMERS(S5) = { + REGULATOR_SUPPLY("8921_s5", NULL), + REGULATOR_SUPPLY("krait0", NULL), +}; +VREG_CONSUMERS(S6) = { + REGULATOR_SUPPLY("8921_s6", NULL), + REGULATOR_SUPPLY("krait1", NULL), +}; +VREG_CONSUMERS(S7) = { + REGULATOR_SUPPLY("8921_s7", NULL), +}; +VREG_CONSUMERS(S8) = { + REGULATOR_SUPPLY("8921_s8", NULL), +}; +VREG_CONSUMERS(LVS1) = { + REGULATOR_SUPPLY("8921_lvs1", NULL), + REGULATOR_SUPPLY("iris_vddio", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(LVS2) = { + REGULATOR_SUPPLY("8921_lvs2", NULL), + REGULATOR_SUPPLY("iris_vdddig", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(LVS3) = { + REGULATOR_SUPPLY("8921_lvs3", NULL), +}; +VREG_CONSUMERS(LVS4) = { + REGULATOR_SUPPLY("8921_lvs4", NULL), +}; +VREG_CONSUMERS(LVS5) = { + REGULATOR_SUPPLY("8921_lvs5", NULL), + REGULATOR_SUPPLY("cam_vio", "4-001a"), + REGULATOR_SUPPLY("cam_vio", "4-0048"), + REGULATOR_SUPPLY("cam_vio", "4-006c"), + REGULATOR_SUPPLY("cam_vio", "4-0034"), + REGULATOR_SUPPLY("cam_vio", "4-0020"), +}; +VREG_CONSUMERS(LVS6) = { + REGULATOR_SUPPLY("8921_lvs6", NULL), + REGULATOR_SUPPLY("vdd_pcie_vph", "msm_pcie"), +}; +VREG_CONSUMERS(LVS7) = { + REGULATOR_SUPPLY("8921_lvs7", NULL), + REGULATOR_SUPPLY("pll_vdd", "pil_riva"), + REGULATOR_SUPPLY("lvds_vdda", "lvds.0"), + REGULATOR_SUPPLY("dsi1_vddio", "mipi_dsi.1"), + REGULATOR_SUPPLY("hdmi_vdda", "hdmi_msm.0"), +}; +VREG_CONSUMERS(USB_OTG) = { + REGULATOR_SUPPLY("8921_usb_otg", NULL), + REGULATOR_SUPPLY("vbus_otg", "msm_otg"), +}; +VREG_CONSUMERS(HDMI_MVS) = { + REGULATOR_SUPPLY("8921_hdmi_mvs", NULL), + REGULATOR_SUPPLY("hdmi_mvs", "hdmi_msm.0"), +}; +VREG_CONSUMERS(NCP) = { + REGULATOR_SUPPLY("8921_ncp", NULL), +}; +VREG_CONSUMERS(8821_S0) = { + REGULATOR_SUPPLY("8821_s0", NULL), + REGULATOR_SUPPLY("krait2", NULL), +}; +VREG_CONSUMERS(8821_S1) = { + REGULATOR_SUPPLY("8821_s1", NULL), + REGULATOR_SUPPLY("krait3", NULL), +}; +VREG_CONSUMERS(EXT_5V) = { + REGULATOR_SUPPLY("ext_5v", NULL), + REGULATOR_SUPPLY("ext_ddr3", NULL), + REGULATOR_SUPPLY("vbus", "msm_ehci_host.0"), +}; +VREG_CONSUMERS(EXT_MPP8) = { + REGULATOR_SUPPLY("ext_mpp8", NULL), + REGULATOR_SUPPLY("vbus", "msm_ehci_host.1"), +}; +VREG_CONSUMERS(EXT_3P3V) = { + REGULATOR_SUPPLY("ext_3p3v", NULL), + REGULATOR_SUPPLY("vdd_io", "spi0.2"), + REGULATOR_SUPPLY("mhl_ext_3p3v", "msm_otg"), + REGULATOR_SUPPLY("lvds_vccs_3p3v", "lvds.0"), + REGULATOR_SUPPLY("dsi1_vccs_3p3v", "mipi_dsi.1"), + REGULATOR_SUPPLY("hdmi_mux_vdd", "hdmi_msm.0"), + REGULATOR_SUPPLY("pcie_ext_3p3v", "msm_pcie"), +}; +VREG_CONSUMERS(EXT_TS_SW) = { + REGULATOR_SUPPLY("ext_ts_sw", NULL), + REGULATOR_SUPPLY("vdd_ana", "3-005b"), +}; +VREG_CONSUMERS(AVC_1P2V) = { + REGULATOR_SUPPLY("avc_1p2v", NULL), +}; +VREG_CONSUMERS(AVC_1P8V) = { + REGULATOR_SUPPLY("avc_1p8v", NULL), +}; +VREG_CONSUMERS(AVC_2P2V) = { + REGULATOR_SUPPLY("avc_2p2v", NULL), +}; +VREG_CONSUMERS(AVC_5V) = { + REGULATOR_SUPPLY("avc_5v", NULL), +}; +VREG_CONSUMERS(AVC_3P3V) = { + REGULATOR_SUPPLY("avc_3p3v", NULL), +}; + +#define PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, _modes, _ops, \ + _apply_uV, _pull_down, _always_on, _supply_regulator, \ + _system_uA, _enable_time, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _max_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pull_down_enable = _pull_down, \ + .system_uA = _system_uA, \ + .enable_time = _enable_time, \ + } + +#define PM8XXX_LDO(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_NLDO1200(_id, _name, _always_on, _pull_down, _min_uV, \ + _max_uV, _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_SMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_FTSMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS \ + | REGULATOR_CHANGE_MODE, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_VS(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +#define PM8XXX_VS300(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +#define PM8XXX_NCP(_id, _name, _always_on, _min_uV, _max_uV, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, 0, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, 0, 0, \ + _always_on, _supply_regulator, 0, _enable_time, _reg_id) + +/* Pin control initialization */ +#define PM8XXX_PC(_id, _name, _always_on, _pin_fn, _pin_ctrl, \ + _supply_regulator, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pin_fn = PM8XXX_VREG_PIN_FN_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +#define GPIO_VREG(_id, _reg_name, _gpio_label, _gpio, _supply_regulator) \ + [GPIO_VREG_ID_##_id] = { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .regulator_name = _reg_name, \ + .gpio_label = _gpio_label, \ + .gpio = _gpio, \ + } + +#define SAW_VREG_INIT(_id, _name, _min_uV, _max_uV) \ + { \ + .constraints = { \ + .name = _name, \ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + }, \ + .num_consumer_supplies = ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + } + +#define RPM_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, _default_uV, \ + _peak_uA, _avg_uA, _pull_down, _pin_ctrl, _freq, _pin_fn, \ + _force_mode, _sleep_set_force_mode, _power_mode, _state, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _min_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8921_##_id, \ + .default_uV = _default_uV, \ + .peak_uA = _peak_uA, \ + .avg_uA = _avg_uA, \ + .pull_down_enable = _pull_down, \ + .pin_ctrl = _pin_ctrl, \ + .freq = RPM_VREG_FREQ_##_freq, \ + .pin_fn = _pin_fn, \ + .force_mode = _force_mode, \ + .sleep_set_force_mode = _sleep_set_force_mode, \ + .power_mode = _power_mode, \ + .state = _state, \ + .sleep_selectable = _sleep_selectable, \ + .system_uA = _system_uA, \ + } + +#define RPM_LDO(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _init_peak_uA) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _init_peak_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, RPM_VREG_POWER_MODE_8960_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, _system_uA) + +#define RPM_SMPS(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _freq, _force_mode, \ + _sleep_set_force_mode) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _system_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_##_force_mode, \ + RPM_VREG_FORCE_MODE_8960_##_sleep_set_force_mode, \ + RPM_VREG_POWER_MODE_8960_PWM, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) + +#define RPM_VS(_id, _always_on, _pd, _sleep_selectable, _supply_regulator) \ + RPM_INIT(_id, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, 0, 1000, 1000, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, RPM_VREG_POWER_MODE_8960_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +#define RPM_NCP(_id, _always_on, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _freq) \ + RPM_INIT(_id, _min_uV, _max_uV, 0, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS, 0, _max_uV, 1000, 1000, 0, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, RPM_VREG_POWER_MODE_8960_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +/* Pin control initialization */ +#define RPM_PC_INIT(_id, _always_on, _pin_fn, _pin_ctrl, _supply_regulator) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8921_##_id##_PC, \ + .pin_fn = RPM_VREG_PIN_FN_8960_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +/* GPIO regulator constraints */ +struct gpio_regulator_platform_data +apq8064_gpio_regulator_pdata[] __devinitdata = { + /* ID vreg_name gpio_label gpio supply */ + GPIO_VREG(EXT_5V, "ext_5v", "ext_5v_en", PM8921_MPP_PM_TO_SYS(7), NULL), + GPIO_VREG(EXT_3P3V, "ext_3p3v", "ext_3p3v_en", + APQ8064_EXT_3P3V_REG_EN_GPIO, NULL), + GPIO_VREG(EXT_TS_SW, "ext_ts_sw", "ext_ts_sw_en", + PM8921_GPIO_PM_TO_SYS(23), "ext_3p3v"), + GPIO_VREG(EXT_MPP8, "ext_mpp8", "ext_mpp8_en", + PM8921_MPP_PM_TO_SYS(8), NULL), +}; + +struct gpio_regulator_platform_data +mpq8064_gpio_regulator_pdata[] __devinitdata = { + GPIO_VREG(AVC_1P2V, "avc_1p2v", "avc_1p2v_en", SX150X_GPIO(4, 2), NULL), + GPIO_VREG(AVC_1P8V, "avc_1p8v", "avc_1p8v_en", SX150X_GPIO(4, 4), NULL), + GPIO_VREG(AVC_2P2V, "avc_2p2v", "avc_2p2v_en", + SX150X_GPIO(4, 14), NULL), + GPIO_VREG(AVC_5V, "avc_5v", "avc_5v_en", SX150X_GPIO(4, 3), NULL), + GPIO_VREG(AVC_3P3V, "avc_3p3v", "avc_3p3v_en", + SX150X_GPIO(4, 15), "avc_5v"), +}; + +/* SAW regulator constraints */ +struct regulator_init_data msm8064_saw_regulator_pdata_8921_s5 = + /* ID vreg_name min_uV max_uV */ + SAW_VREG_INIT(S5, "8921_s5", 850000, 1300000); +struct regulator_init_data msm8064_saw_regulator_pdata_8921_s6 = + SAW_VREG_INIT(S6, "8921_s6", 850000, 1300000); + +struct regulator_init_data msm8064_saw_regulator_pdata_8821_s0 = + /* ID vreg_name min_uV max_uV */ + SAW_VREG_INIT(8821_S0, "8821_s0", 850000, 1300000); +struct regulator_init_data msm8064_saw_regulator_pdata_8821_s1 = + SAW_VREG_INIT(8821_S1, "8821_s1", 850000, 1300000); + +/* PM8921 regulator constraints */ +struct pm8xxx_regulator_platform_data +msm8064_pm8921_regulator_pdata[] __devinitdata = { + /* + * ID name always_on pd min_uV max_uV en_t supply + * system_uA reg_ID + */ + PM8XXX_NLDO1200(L26, "8921_l26", 0, 1, 375000, 1050000, 200, "8921_s7", + 0, 1), + + /* ID name always_on pd en_t supply reg_ID */ + PM8XXX_VS300(USB_OTG, "8921_usb_otg", 0, 0, 0, "ext_5v", 2), + PM8XXX_VS300(HDMI_MVS, "8921_hdmi_mvs", 0, 1, 0, "ext_5v", 3), +}; + +static struct rpm_regulator_init_data +apq8064_rpm_regulator_init_data[] __devinitdata = { + /* ID a_on pd ss min_uV max_uV supply sys_uA freq fm ss_fm */ + RPM_SMPS(S1, 1, 1, 0, 1225000, 1225000, NULL, 100000, 3p20, NONE, NONE), + RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL, 0, 1p60, NONE, NONE), + RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, NONE, NONE), + RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, AUTO), + RPM_SMPS(S7, 0, 1, 0, 1300000, 1300000, NULL, 100000, 3p20, NONE, NONE), + RPM_SMPS(S8, 0, 1, 0, 2200000, 2200000, NULL, 0, 1p60, NONE, NONE), + + /* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */ + RPM_LDO(L1, 1, 1, 0, 1100000, 1100000, "8921_s4", 0, 1000), + RPM_LDO(L2, 0, 1, 0, 1200000, 1200000, "8921_s4", 0, 0), + RPM_LDO(L3, 0, 1, 0, 3075000, 3075000, NULL, 0, 0), + RPM_LDO(L4, 1, 1, 0, 1800000, 1800000, NULL, 0, 10000), + RPM_LDO(L5, 0, 1, 0, 2950000, 2950000, NULL, 0, 0), + RPM_LDO(L6, 0, 1, 0, 2950000, 2950000, NULL, 0, 0), + RPM_LDO(L7, 0, 1, 0, 1850000, 2950000, NULL, 0, 0), + RPM_LDO(L8, 0, 1, 0, 2800000, 2800000, NULL, 0, 0), + RPM_LDO(L9, 0, 1, 0, 3000000, 3000000, NULL, 0, 0), + RPM_LDO(L10, 0, 1, 0, 2900000, 2900000, NULL, 0, 0), + RPM_LDO(L11, 0, 1, 0, 3000000, 3000000, NULL, 0, 0), + RPM_LDO(L12, 0, 1, 0, 1200000, 1200000, "8921_s4", 0, 0), + RPM_LDO(L14, 0, 1, 0, 1800000, 1800000, NULL, 0, 0), + RPM_LDO(L15, 0, 1, 0, 1800000, 2950000, NULL, 0, 0), + RPM_LDO(L16, 0, 1, 0, 2800000, 2800000, NULL, 0, 0), + RPM_LDO(L17, 0, 1, 0, 2000000, 2000000, NULL, 0, 0), + RPM_LDO(L18, 0, 1, 0, 1300000, 1800000, "8921_s4", 0, 0), + RPM_LDO(L21, 0, 1, 0, 1050000, 1050000, NULL, 0, 0), + RPM_LDO(L22, 0, 1, 0, 2600000, 2600000, NULL, 0, 0), + RPM_LDO(L23, 0, 1, 0, 1800000, 1800000, NULL, 0, 0), + RPM_LDO(L24, 0, 1, 1, 750000, 1150000, "8921_s1", 10000, 10000), + RPM_LDO(L25, 1, 1, 0, 1250000, 1250000, "8921_s1", 10000, 10000), + RPM_LDO(L27, 0, 1, 0, 1100000, 1100000, "8921_s7", 0, 0), + RPM_LDO(L28, 0, 1, 0, 1050000, 1050000, "8921_s7", 0, 0), + RPM_LDO(L29, 0, 1, 0, 2000000, 2000000, NULL, 0, 0), + + /* ID a_on pd ss supply */ + RPM_VS(LVS1, 0, 1, 0, "8921_s4"), + RPM_VS(LVS2, 0, 1, 0, "8921_s1"), + RPM_VS(LVS3, 0, 1, 0, "8921_s4"), + RPM_VS(LVS4, 0, 1, 0, "8921_s4"), + RPM_VS(LVS5, 0, 1, 0, "8921_s4"), + RPM_VS(LVS6, 0, 1, 0, "8921_s4"), + RPM_VS(LVS7, 0, 1, 1, "8921_s4"), + + /* ID a_on ss min_uV max_uV supply freq */ + RPM_NCP(NCP, 0, 0, 1800000, 1800000, "8921_l6", 1p60), +}; + +int msm8064_pm8921_regulator_pdata_len __devinitdata = + ARRAY_SIZE(msm8064_pm8921_regulator_pdata); + +struct rpm_regulator_platform_data apq8064_rpm_regulator_pdata __devinitdata = { + .init_data = apq8064_rpm_regulator_init_data, + .num_regulators = ARRAY_SIZE(apq8064_rpm_regulator_init_data), + .version = RPM_VREG_VERSION_8960, + .vreg_id_vdd_mem = RPM_VREG_ID_PM8921_L24, + .vreg_id_vdd_dig = RPM_VREG_ID_PM8921_S3, +}; diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c new file mode 100644 index 00000000000..5f74468e0b6 --- /dev/null +++ b/arch/arm/mach-msm/board-8064-storage.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8064.h" +#include "board-storage-common-a.h" + + +/* APQ8064 has 4 SDCC controllers */ +enum sdcc_controllers { + SDCC1, + SDCC2, + SDCC3, + SDCC4, + MAX_SDCC_CONTROLLER +}; + +/* All SDCC controllers require VDD/VCC voltage */ +static struct msm_mmc_reg_data mmc_vdd_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .name = "sdc_vdd", + .high_vol_level = 2950000, + .low_vol_level = 2950000, + .always_on = 1, + .lpm_sup = 1, + .lpm_uA = 9000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .name = "sdc_vdd", + .high_vol_level = 2950000, + .low_vol_level = 2950000, + .hpm_uA = 800000, /* 800mA */ + } +}; + +/* SDCC controllers may require voting for VDD IO voltage */ +static struct msm_mmc_reg_data mmc_vdd_io_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .name = "sdc_vdd_io", + .always_on = 1, + .high_vol_level = 1800000, + .low_vol_level = 1800000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .name = "sdc_vdd_io", + .high_vol_level = 2950000, + .low_vol_level = 1850000, + .always_on = 1, + .lpm_sup = 1, + /* Max. Active current required is 16 mA */ + .hpm_uA = 16000, + /* + * Sleep current required is ~300 uA. But min. vote can be + * in terms of mA (min. 1 mA). So let's vote for 2 mA + * during sleep. + */ + .lpm_uA = 2000, + } +}; + +static struct msm_mmc_slot_reg_data mmc_slot_vreg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .vdd_data = &mmc_vdd_reg_data[SDCC1], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC1], + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .vdd_data = &mmc_vdd_reg_data[SDCC3], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC3], + } +}; + +/* SDC1 pad data */ +static struct msm_mmc_pad_drv sdc1_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_16MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_10MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_10MA} +}; + +static struct msm_mmc_pad_drv sdc1_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +/* SDC3 pad data */ +static struct msm_mmc_pad_drv sdc3_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_8MA} +}; + +static struct msm_mmc_pad_drv sdc3_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc3_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC3_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc3_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC3_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull_data mmc_pad_pull_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_pull_on_cfg, + .off = sdc1_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_pull_on_cfg) + }, + [SDCC3] = { + .on = sdc3_pad_pull_on_cfg, + .off = sdc3_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc3_pad_pull_on_cfg) + }, +}; + +static struct msm_mmc_pad_drv_data mmc_pad_drv_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_drv_on_cfg, + .off = sdc1_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_drv_on_cfg) + }, + [SDCC3] = { + .on = sdc3_pad_drv_on_cfg, + .off = sdc3_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc3_pad_drv_on_cfg) + }, +}; + +static struct msm_mmc_pad_data mmc_pad_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pull = &mmc_pad_pull_data[SDCC1], + .drv = &mmc_pad_drv_data[SDCC1] + }, + [SDCC3] = { + .pull = &mmc_pad_pull_data[SDCC3], + .drv = &mmc_pad_drv_data[SDCC3] + }, +}; + +static struct msm_mmc_gpio sdc2_gpio[] = { + {59, "sdc2_clk"}, + {57, "sdc2_cmd"}, + {62, "sdc2_dat_0"}, + {61, "sdc2_dat_1"}, + {60, "sdc2_dat_2"}, + {58, "sdc2_dat_3"}, +}; + +static struct msm_mmc_gpio sdc4_gpio[] = { + {68, "sdc4_clk"}, + {67, "sdc4_cmd"}, + {66, "sdc4_dat_0"}, + {65, "sdc4_dat_1"}, + {64, "sdc4_dat_2"}, + {63, "sdc4_dat_3"}, +}; + +static struct msm_mmc_gpio_data mmc_gpio_data[MAX_SDCC_CONTROLLER] = { + [SDCC2] = { + .gpio = sdc2_gpio, + .size = ARRAY_SIZE(sdc2_gpio), + }, + [SDCC4] = { + .gpio = sdc4_gpio, + .size = ARRAY_SIZE(sdc4_gpio), + } +}; + +static struct msm_mmc_pin_data mmc_slot_pin_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pad_data = &mmc_pad_data[SDCC1], + }, + [SDCC2] = { + .is_gpio = 1, + .gpio_data = &mmc_gpio_data[SDCC2], + }, + [SDCC3] = { + .pad_data = &mmc_pad_data[SDCC3], + }, + [SDCC4] = { + .is_gpio = 1, + .gpio_data = &mmc_gpio_data[SDCC4], + }, +}; + +#define MSM_MPM_PIN_SDC1_DAT1 17 +#define MSM_MPM_PIN_SDC3_DAT1 21 + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static unsigned int sdc1_sup_clk_rates[] = { + 400000, 24000000, 48000000, 96000000 +}; + +static struct mmc_platform_data sdc1_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, +#ifdef CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .sup_clk_table = sdc1_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc1_sup_clk_rates), + .pclk_src_dfab = 1, + .nonremovable = 1, + .pin_data = &mmc_slot_pin_data[SDCC1], + .vreg_data = &mmc_slot_vreg_data[SDCC1], + .uhs_caps = MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50, + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC1_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +static struct mmc_platform_data *apq8064_sdc1_pdata = &sdc1_data; +#else +static struct mmc_platform_data *apq8064_sdc1_pdata; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static unsigned int sdc2_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +static struct mmc_platform_data sdc2_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc2_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc2_sup_clk_rates), + .pclk_src_dfab = 1, + .pin_data = &mmc_slot_pin_data[SDCC2], + .sdiowakeup_irq = MSM_GPIO_TO_INT(61), + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +static struct mmc_platform_data *apq8064_sdc2_pdata = &sdc2_data; +#else +static struct mmc_platform_data *apq8064_sdc2_pdata; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static unsigned int sdc3_sup_clk_rates[] = { + 400000, 24000000, 48000000, 96000000, 192000000 +}; + +static struct mmc_platform_data sdc3_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc3_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc3_sup_clk_rates), + .pclk_src_dfab = 1, + .pin_data = &mmc_slot_pin_data[SDCC3], + .vreg_data = &mmc_slot_vreg_data[SDCC3], + .wpswitch_gpio = PM8921_GPIO_PM_TO_SYS(17), + .wpswitch_polarity = 1, + .status_gpio = 26, + .status_irq = MSM_GPIO_TO_INT(26), + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + .is_status_gpio_active_low = 1, + .xpc_cap = 1, + .uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | + MMC_CAP_UHS_SDR104 | MMC_CAP_MAX_CURRENT_800), + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC3_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +static struct mmc_platform_data *apq8064_sdc3_pdata = &sdc3_data; +#else +static struct mmc_platform_data *apq8064_sdc3_pdata; +#endif + + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static unsigned int sdc4_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +static struct mmc_platform_data sdc4_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc4_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc4_sup_clk_rates), + .pclk_src_dfab = 1, + .pin_data = &mmc_slot_pin_data[SDCC4], + .sdiowakeup_irq = MSM_GPIO_TO_INT(65), + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +static struct mmc_platform_data *apq8064_sdc4_pdata = &sdc4_data; +#else +static struct mmc_platform_data *apq8064_sdc4_pdata; +#endif + +void __init apq8064_init_mmc(void) +{ + if ((machine_is_apq8064_rumi3()) || machine_is_apq8064_sim()) { + if (apq8064_sdc1_pdata) { + if (machine_is_apq8064_sim()) + apq8064_sdc1_pdata->disable_bam = true; + apq8064_sdc1_pdata->disable_runtime_pm = true; + apq8064_sdc1_pdata->disable_cmd23 = true; + } + if (apq8064_sdc3_pdata) { + if (machine_is_apq8064_sim()) + apq8064_sdc3_pdata->disable_bam = true; + apq8064_sdc3_pdata->disable_runtime_pm = true; + apq8064_sdc3_pdata->disable_cmd23 = true; + } + } + + if (apq8064_sdc1_pdata) + apq8064_add_sdcc(1, apq8064_sdc1_pdata); + + if (apq8064_sdc2_pdata) + apq8064_add_sdcc(2, apq8064_sdc2_pdata); + + if (apq8064_sdc3_pdata) { + if (!machine_is_apq8064_cdp()) { + apq8064_sdc3_pdata->wpswitch_gpio = 0; + apq8064_sdc3_pdata->wpswitch_polarity = 0; + } + if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() || + machine_is_mpq8064_dtv()) { + int rc; + struct pm_gpio sd_card_det_init_cfg = { + .direction = PM_GPIO_DIR_IN, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .pull = PM_GPIO_PULL_UP_30, + .vin_sel = PM_GPIO_VIN_S4, + .out_strength = PM_GPIO_STRENGTH_NO, + .function = PM_GPIO_FUNC_NORMAL, + }; + + apq8064_sdc3_pdata->status_gpio = + PM8921_GPIO_PM_TO_SYS(31); + apq8064_sdc3_pdata->status_irq = + PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 31); + rc = pm8xxx_gpio_config(apq8064_sdc3_pdata->status_gpio, + &sd_card_det_init_cfg); + if (rc) { + pr_info("%s: SD_CARD_DET GPIO%d config " + "failed(%d)\n", __func__, + apq8064_sdc3_pdata->status_gpio, rc); + apq8064_sdc3_pdata->status_gpio = 0; + apq8064_sdc3_pdata->status_irq = 0; + } + } + apq8064_add_sdcc(3, apq8064_sdc3_pdata); + } + + if (apq8064_sdc4_pdata) + apq8064_add_sdcc(4, apq8064_sdc4_pdata); +} diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c new file mode 100644 index 00000000000..5f1a57d555f --- /dev/null +++ b/arch/arm/mach-msm/board-8064.c @@ -0,0 +1,3071 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "timer.h" +#include "devices.h" +#include +#include +#ifdef CONFIG_ANDROID_PMEM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_watchdog.h" +#include "board-8064.h" +#include "acpuclock.h" +#include "spm.h" +#include +#include "rpm_resources.h" +#include "pm.h" +#include "pm-boot.h" +#include "devices-msm8x60.h" +#include "smd_private.h" + +#define MSM_PMEM_ADSP_SIZE 0x7800000 +#define MSM_PMEM_AUDIO_SIZE 0x4CF000 +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +#define MSM_PMEM_SIZE 0x4000000 /* 64 Mbytes */ +#else +#define MSM_PMEM_SIZE 0x4000000 /* 64 Mbytes */ +#endif + +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +#define HOLE_SIZE 0x20000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000 +#ifdef CONFIG_MSM_IOMMU +#define MSM_ION_MM_SIZE 0x3800000 +#define MSM_ION_SF_SIZE 0 +#define MSM_ION_QSECOM_SIZE 0x780000 /* (7.5MB) */ +#define MSM_ION_HEAP_NUM 7 +#else +#define MSM_ION_MM_SIZE MSM_PMEM_ADSP_SIZE +#define MSM_ION_SF_SIZE MSM_PMEM_SIZE +#define MSM_ION_QSECOM_SIZE 0x600000 /* (6MB) */ +#define MSM_ION_HEAP_NUM 8 +#endif +#define MSM_ION_MM_FW_SIZE (0x200000 - HOLE_SIZE) /* (2MB - 128KB) */ +#define MSM_ION_MFC_SIZE SZ_8K +#define MSM_ION_AUDIO_SIZE MSM_PMEM_AUDIO_SIZE +#else +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000 +#define MSM_ION_HEAP_NUM 1 +#endif + +#define APQ8064_FIXED_AREA_START (0xa0000000 - (MSM_ION_MM_FW_SIZE + \ + HOLE_SIZE)) +#define MAX_FIXED_AREA_SIZE 0x10000000 +#define MSM_MM_FW_SIZE (0x200000 - HOLE_SIZE) +#define APQ8064_FW_START APQ8064_FIXED_AREA_START + +/* PCIe power enable pmic gpio */ +#define PCIE_PWR_EN_PMIC_GPIO 13 +#define PCIE_RST_N_PMIC_MPP 1 + +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) +{ + pmem_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); +#endif + +#ifdef CONFIG_ANDROID_PMEM +static unsigned pmem_size = MSM_PMEM_SIZE; +static int __init pmem_size_setup(char *p) +{ + pmem_size = memparse(p, NULL); + return 0; +} +early_param("pmem_size", pmem_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; + +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; + +static int __init pmem_audio_size_setup(char *p) +{ + pmem_audio_size = memparse(p, NULL); + return 0; +} +early_param("pmem_audio_size", pmem_audio_size_setup); +#endif + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device apq8064_android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = {.platform_data = &android_pmem_pdata}, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; +static struct platform_device apq8064_android_pmem_adsp_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct android_pmem_platform_data android_pmem_audio_pdata = { + .name = "pmem_audio", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device apq8064_android_pmem_audio_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_audio_pdata }, +}; +#endif /* CONFIG_MSM_MULTIMEDIA_USE_ION */ +#endif /* CONFIG_ANDROID_PMEM */ + +struct fmem_platform_data apq8064_fmem_pdata = { +}; + +static struct memtype_reserve apq8064_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init reserve_rtb_memory(void) +{ +#if defined(CONFIG_MSM_RTB) + apq8064_reserve_table[MEMTYPE_EBI1].size += apq8064_rtb_pdata.size; +#endif +} + + +static void __init size_pmem_devices(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + android_pmem_adsp_pdata.size = pmem_adsp_size; + android_pmem_pdata.size = pmem_size; + android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +} + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + apq8064_reserve_table[p->memory_type].size += p->size; +} +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + reserve_memory_for(&android_pmem_adsp_pdata); + reserve_memory_for(&android_pmem_pdata); + reserve_memory_for(&android_pmem_audio_pdata); +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ + apq8064_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif /*CONFIG_ANDROID_PMEM*/ +} + +static int apq8064_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +#define FMEM_ENABLED 0 + +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_cp_heap_pdata cp_mm_apq8064_ion_pdata = { + .permission_type = IPT_TYPE_MM_CARVEOUT, + .align = PAGE_SIZE, + .reusable = FMEM_ENABLED, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_MIDDLE, +}; + +static struct ion_cp_heap_pdata cp_mfc_apq8064_ion_pdata = { + .permission_type = IPT_TYPE_MFC_SHAREDMEM, + .align = PAGE_SIZE, + .reusable = 0, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_HIGH, +}; + +static struct ion_co_heap_pdata co_apq8064_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, + .mem_is_fmem = 0, +}; + +static struct ion_co_heap_pdata fw_co_apq8064_ion_pdata = { + .adjacent_mem_id = ION_CP_MM_HEAP_ID, + .align = SZ_128K, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_LOW, +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. Due to + * video hardware restrictions and content protection the FW heap has to + * be allocated adjacent (below) the MM heap and the MFC heap has to be + * allocated after the MM heap to ensure MFC heap is not more than 256MB + * away from the base address of the FW heap. + * However, the order of FW heap and MM heap doesn't matter since these + * two heaps are taken care of by separate code to ensure they are adjacent + * to each other. + * Don't swap the order unless you know what you are doing! + */ +static struct ion_platform_data apq8064_ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + { + .id = ION_CP_MM_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MM_HEAP_NAME, + .size = MSM_ION_MM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mm_apq8064_ion_pdata, + }, + { + .id = ION_MM_FIRMWARE_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_MM_FIRMWARE_HEAP_NAME, + .size = MSM_ION_MM_FW_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &fw_co_apq8064_ion_pdata, + }, + { + .id = ION_CP_MFC_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MFC_HEAP_NAME, + .size = MSM_ION_MFC_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mfc_apq8064_ion_pdata, + }, +#ifndef CONFIG_MSM_IOMMU + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .size = MSM_ION_SF_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_apq8064_ion_pdata, + }, +#endif + { + .id = ION_IOMMU_HEAP_ID, + .type = ION_HEAP_TYPE_IOMMU, + .name = ION_IOMMU_HEAP_NAME, + }, + { + .id = ION_QSECOM_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_QSECOM_HEAP_NAME, + .size = MSM_ION_QSECOM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_apq8064_ion_pdata, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .size = MSM_ION_AUDIO_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_apq8064_ion_pdata, + }, +#endif + } +}; + +static struct platform_device apq8064_ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &apq8064_ion_pdata }, +}; +#endif + +static struct platform_device apq8064_fmem_device = { + .name = "fmem", + .id = 1, + .dev = { .platform_data = &apq8064_fmem_pdata }, +}; + +static void __init reserve_mem_for_ion(enum ion_memory_types mem_type, + unsigned long size) +{ + apq8064_reserve_table[mem_type].size += size; +} + +static void __init apq8064_reserve_fixed_area(unsigned long fixed_area_size) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + int ret; + + if (fixed_area_size > MAX_FIXED_AREA_SIZE) + panic("fixed area size is larger than %dM\n", + MAX_FIXED_AREA_SIZE >> 20); + + reserve_info->fixed_area_size = fixed_area_size; + reserve_info->fixed_area_start = APQ8064_FW_START; + + ret = memblock_remove(reserve_info->fixed_area_start, + reserve_info->fixed_area_size); + BUG_ON(ret); +#endif +} + +/** + * Reserve memory for ION and calculate amount of reusable memory for fmem. + * We only reserve memory for heaps that are not reusable. However, we only + * support one reusable heap at the moment so we ignore the reusable flag for + * other than the first heap with reusable flag set. Also handle special case + * for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be + * at a higher address than FW in addition to not more than 256MB away from the + * base address of the firmware. This means that if MM is reusable the other + * two heaps must be allocated in the same region as FW. This is handled by the + * mem_is_fmem flag in the platform data. In addition the MM heap must be + * adjacent to the FW heap for content protection purposes. + */ +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + unsigned int i; + unsigned int reusable_count = 0; + unsigned int fixed_size = 0; + unsigned int fixed_low_size, fixed_middle_size, fixed_high_size; + unsigned long fixed_low_start, fixed_middle_start, fixed_high_start; + + apq8064_fmem_pdata.size = 0; + apq8064_fmem_pdata.reserved_size_low = 0; + apq8064_fmem_pdata.reserved_size_high = 0; + apq8064_fmem_pdata.align = PAGE_SIZE; + fixed_low_size = 0; + fixed_middle_size = 0; + fixed_high_size = 0; + + /* We only support 1 reusable heap. Check if more than one heap + * is specified as reusable and set as non-reusable if found. + */ + for (i = 0; i < apq8064_ion_pdata.nr; ++i) { + const struct ion_platform_heap *heap = + &(apq8064_ion_pdata.heaps[i]); + + if (heap->type == ION_HEAP_TYPE_CP && heap->extra_data) { + struct ion_cp_heap_pdata *data = heap->extra_data; + + reusable_count += (data->reusable) ? 1 : 0; + + if (data->reusable && reusable_count > 1) { + pr_err("%s: Too many heaps specified as " + "reusable. Heap %s was not configured " + "as reusable.\n", __func__, heap->name); + data->reusable = 0; + } + } + } + + for (i = 0; i < apq8064_ion_pdata.nr; ++i) { + const struct ion_platform_heap *heap = + &(apq8064_ion_pdata.heaps[i]); + + if (heap->extra_data) { + int fixed_position = NOT_FIXED; + int mem_is_fmem = 0; + + switch (heap->type) { + case ION_HEAP_TYPE_CP: + mem_is_fmem = ((struct ion_cp_heap_pdata *) + heap->extra_data)->mem_is_fmem; + fixed_position = ((struct ion_cp_heap_pdata *) + heap->extra_data)->fixed_position; + break; + case ION_HEAP_TYPE_CARVEOUT: + mem_is_fmem = ((struct ion_co_heap_pdata *) + heap->extra_data)->mem_is_fmem; + fixed_position = ((struct ion_co_heap_pdata *) + heap->extra_data)->fixed_position; + break; + default: + break; + } + + if (fixed_position != NOT_FIXED) + fixed_size += heap->size; + else + reserve_mem_for_ion(MEMTYPE_EBI1, heap->size); + + if (fixed_position == FIXED_LOW) + fixed_low_size += heap->size; + else if (fixed_position == FIXED_MIDDLE) + fixed_middle_size += heap->size; + else if (fixed_position == FIXED_HIGH) + fixed_high_size += heap->size; + + if (mem_is_fmem) + apq8064_fmem_pdata.size += heap->size; + } + } + + if (!fixed_size) + return; + + if (apq8064_fmem_pdata.size) { + apq8064_fmem_pdata.reserved_size_low = fixed_low_size + + HOLE_SIZE; + apq8064_fmem_pdata.reserved_size_high = fixed_high_size; + } + + /* Since the fixed area may be carved out of lowmem, + * make sure the length is a multiple of 1M. + */ + fixed_size = (fixed_size + HOLE_SIZE + SECTION_SIZE - 1) + & SECTION_MASK; + apq8064_reserve_fixed_area(fixed_size); + + fixed_low_start = APQ8064_FIXED_AREA_START; + fixed_middle_start = fixed_low_start + fixed_low_size + HOLE_SIZE; + fixed_high_start = fixed_middle_start + fixed_middle_size; + + for (i = 0; i < apq8064_ion_pdata.nr; ++i) { + struct ion_platform_heap *heap = &(apq8064_ion_pdata.heaps[i]); + + if (heap->extra_data) { + int fixed_position = NOT_FIXED; + struct ion_cp_heap_pdata *pdata = NULL; + + switch (heap->type) { + case ION_HEAP_TYPE_CP: + pdata = + (struct ion_cp_heap_pdata *)heap->extra_data; + fixed_position = pdata->fixed_position; + break; + case ION_HEAP_TYPE_CARVEOUT: + fixed_position = ((struct ion_co_heap_pdata *) + heap->extra_data)->fixed_position; + break; + default: + break; + } + + switch (fixed_position) { + case FIXED_LOW: + heap->base = fixed_low_start; + break; + case FIXED_MIDDLE: + heap->base = fixed_middle_start; + pdata->secure_base = fixed_middle_start + - HOLE_SIZE; + pdata->secure_size = HOLE_SIZE + heap->size; + break; + case FIXED_HIGH: + heap->base = fixed_high_start; + break; + default: + break; + } + } + } +#endif +} + +static void __init reserve_mdp_memory(void) +{ + apq8064_mdp_writeback(apq8064_reserve_table); +} + +static void __init reserve_cache_dump_memory(void) +{ +#ifdef CONFIG_MSM_CACHE_DUMP + unsigned int total; + + total = apq8064_cache_dump_pdata.l1_size + + apq8064_cache_dump_pdata.l2_size; + apq8064_reserve_table[MEMTYPE_EBI1].size += total; +#endif +} + +static void __init apq8064_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); + reserve_ion_memory(); + reserve_mdp_memory(); + reserve_rtb_memory(); + reserve_cache_dump_memory(); +} + +static struct reserve_info apq8064_reserve_info __initdata = { + .memtype_reserve_table = apq8064_reserve_table, + .calculate_reserve_sizes = apq8064_calculate_reserve_sizes, + .reserve_fixed_area = apq8064_reserve_fixed_area, + .paddr_to_memtype = apq8064_paddr_to_memtype, +}; + +static int apq8064_memory_bank_size(void) +{ + return 1<<29; +} + +static void __init locate_unstable_memory(void) +{ + struct membank *mb = &meminfo.bank[meminfo.nr_banks - 1]; + unsigned long bank_size; + unsigned long low, high; + + bank_size = apq8064_memory_bank_size(); + low = meminfo.bank[0].start; + high = mb->start + mb->size; + + /* Check if 32 bit overflow occured */ + if (high < mb->start) + high = -PAGE_SIZE; + + low &= ~(bank_size - 1); + + if (high - low <= bank_size) + goto no_dmm; + +#ifdef CONFIG_ENABLE_DMM + apq8064_reserve_info.low_unstable_address = mb->start - + MIN_MEMORY_BLOCK_SIZE + mb->size; + apq8064_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE; + + apq8064_reserve_info.bank_size = bank_size; + pr_info("low unstable address %lx max size %lx bank size %lx\n", + apq8064_reserve_info.low_unstable_address, + apq8064_reserve_info.max_unstable_size, + apq8064_reserve_info.bank_size); + return; +#endif +no_dmm: + apq8064_reserve_info.low_unstable_address = high; + apq8064_reserve_info.max_unstable_size = 0; +} + +static int apq8064_change_memory_power(u64 start, u64 size, + int change_type) +{ + return soc_change_memory_power(start, size, change_type); +} + +static char prim_panel_name[PANEL_NAME_MAX_LEN]; +static char ext_panel_name[PANEL_NAME_MAX_LEN]; +static int __init prim_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(prim_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("prim_display", prim_display_setup); + +static int __init ext_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(ext_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("ext_display", ext_display_setup); + +static void __init apq8064_reserve(void) +{ + apq8064_set_display_params(prim_panel_name, ext_panel_name); + msm_reserve(); + if (apq8064_fmem_pdata.size) { +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + if (reserve_info->fixed_area_size) { + apq8064_fmem_pdata.phys = + reserve_info->fixed_area_start + MSM_MM_FW_SIZE; + pr_info("mm fw at %lx (fixed) size %x\n", + reserve_info->fixed_area_start, MSM_MM_FW_SIZE); + pr_info("fmem start %lx (fixed) size %lx\n", + apq8064_fmem_pdata.phys, + apq8064_fmem_pdata.size); + } +#endif + } +} + +static void __init place_movable_zone(void) +{ +#ifdef CONFIG_ENABLE_DMM + movable_reserved_start = apq8064_reserve_info.low_unstable_address; + movable_reserved_size = apq8064_reserve_info.max_unstable_size; + pr_info("movable zone start %lx size %lx\n", + movable_reserved_start, movable_reserved_size); +#endif +} + +static void __init apq8064_early_reserve(void) +{ + reserve_info = &apq8064_reserve_info; + locate_unstable_memory(); + place_movable_zone(); + +} +#ifdef CONFIG_USB_EHCI_MSM_HSIC +/* Bandwidth requests (zero) if no vote placed */ +static struct msm_bus_vectors hsic_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_SPS, + .ab = 0, + .ib = 0, + }, +}; + +/* Bus bandwidth requests in Bytes/sec */ +static struct msm_bus_vectors hsic_max_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 60000000, /* At least 480Mbps on bus. */ + .ib = 960000000, /* MAX bursts rate */ + }, + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_SPS, + .ab = 0, + .ib = 512000000, /*vote for 64Mhz dfab clk rate*/ + }, +}; + +static struct msm_bus_paths hsic_bus_scale_usecases[] = { + { + ARRAY_SIZE(hsic_init_vectors), + hsic_init_vectors, + }, + { + ARRAY_SIZE(hsic_max_vectors), + hsic_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata hsic_bus_scale_pdata = { + hsic_bus_scale_usecases, + ARRAY_SIZE(hsic_bus_scale_usecases), + .name = "hsic", +}; + +static struct msm_hsic_host_platform_data msm_hsic_pdata = { + .strobe = 88, + .data = 89, + .bus_scale_table = &hsic_bus_scale_pdata, +}; +#else +static struct msm_hsic_host_platform_data msm_hsic_pdata; +#endif + +#define PID_MAGIC_ID 0x71432909 +#define SERIAL_NUM_MAGIC_ID 0x61945374 +#define SERIAL_NUMBER_LENGTH 127 +#define DLOAD_USB_BASE_ADD 0x2A03F0C8 + +struct magic_num_struct { + uint32_t pid; + uint32_t serial_num; +}; + +struct dload_struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint16_t reserved4; + uint16_t pid; + char serial_number[SERIAL_NUMBER_LENGTH]; + uint16_t reserved5; + struct magic_num_struct magic_struct; +}; + +static int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) +{ + struct dload_struct __iomem *dload = 0; + + dload = ioremap(DLOAD_USB_BASE_ADD, sizeof(*dload)); + if (!dload) { + pr_err("%s: cannot remap I/O memory region: %08x\n", + __func__, DLOAD_USB_BASE_ADD); + return -ENXIO; + } + + pr_debug("%s: dload:%p pid:%x serial_num:%s\n", + __func__, dload, pid, snum); + /* update pid */ + dload->magic_struct.pid = PID_MAGIC_ID; + dload->pid = pid; + + /* update serial number */ + dload->magic_struct.serial_num = 0; + if (!snum) { + memset(dload->serial_number, 0, SERIAL_NUMBER_LENGTH); + goto out; + } + + dload->magic_struct.serial_num = SERIAL_NUM_MAGIC_ID; + strlcpy(dload->serial_number, snum, SERIAL_NUMBER_LENGTH); +out: + iounmap(dload); + return 0; +} + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +/* Bandwidth requests (zero) if no vote placed */ +static struct msm_bus_vectors usb_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +/* Bus bandwidth requests in Bytes/sec */ +static struct msm_bus_vectors usb_max_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 60000000, /* At least 480Mbps on bus. */ + .ib = 960000000, /* MAX bursts rate */ + }, +}; + +static struct msm_bus_paths usb_bus_scale_usecases[] = { + { + ARRAY_SIZE(usb_init_vectors), + usb_init_vectors, + }, + { + ARRAY_SIZE(usb_max_vectors), + usb_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata usb_bus_scale_pdata = { + usb_bus_scale_usecases, + ARRAY_SIZE(usb_bus_scale_usecases), + .name = "usb", +}; + +static int phy_init_seq[] = { + 0x38, 0x81, /* update DC voltage level */ + 0x24, 0x82, /* set pre-emphasis and rise/fall time */ + -1 +}; + +static struct msm_otg_platform_data msm_otg_pdata = { + .mode = USB_OTG, + .otg_control = OTG_PMIC_CONTROL, + .phy_type = SNPS_28NM_INTEGRATED_PHY, + .pmic_id_irq = PM8921_USB_ID_IN_IRQ(PM8921_IRQ_BASE), + .power_budget = 750, + .bus_scale_table = &usb_bus_scale_pdata, + .phy_init_seq = phy_init_seq, +}; + +static struct msm_usb_host_platform_data msm_ehci_host_pdata3 = { + .power_budget = 500, +}; + +#ifdef CONFIG_USB_EHCI_MSM_HOST4 +static struct msm_usb_host_platform_data msm_ehci_host_pdata4; +#endif + +static void __init apq8064_ehci_host_init(void) +{ + if (machine_is_apq8064_liquid() || machine_is_mpq8064_cdp() || + machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv()) { + if (machine_is_apq8064_liquid()) + msm_ehci_host_pdata3.dock_connect_irq = + PM8921_MPP_IRQ(PM8921_IRQ_BASE, 9); + + apq8064_device_ehci_host3.dev.platform_data = + &msm_ehci_host_pdata3; + platform_device_register(&apq8064_device_ehci_host3); + +#ifdef CONFIG_USB_EHCI_MSM_HOST4 + apq8064_device_ehci_host4.dev.platform_data = + &msm_ehci_host_pdata4; + platform_device_register(&apq8064_device_ehci_host4); +#endif + } +} + +static struct smb349_platform_data smb349_data __initdata = { + .en_n_gpio = PM8921_GPIO_PM_TO_SYS(37), + .chg_susp_gpio = PM8921_GPIO_PM_TO_SYS(30), + .chg_current_ma = 2200, +}; + +static struct i2c_board_info smb349_charger_i2c_info[] __initdata = { + { + I2C_BOARD_INFO(SMB349_NAME, 0x1B), + .platform_data = &smb349_data, + }, +}; + +struct sx150x_platform_data apq8064_sx150x_data[] = { + [SX150X_EPM] = { + .gpio_base = GPIO_EPM_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0, + .io_pulldn_ena = 0x0, + .io_open_drain_ena = 0x0, + .io_polarity = 0, + .irq_summary = -1, + }, +}; + +static struct epm_chan_properties ads_adc_channel_data[] = { + {10, 100}, {500, 50}, {1, 1}, {1, 1}, + {20, 50}, {10, 100}, {1, 1}, {1, 1}, + {10, 100}, {10, 100}, {100, 100}, {200, 100}, + {100, 50}, {2000, 50}, {1000, 50}, {200, 50}, + {200, 100}, {1, 1}, {20, 50}, {500, 50}, + {50, 50}, {200, 100}, {500, 100}, {20, 50}, + {200, 50}, {2000, 100}, {1000, 50}, {100, 50}, + {200, 100}, {500, 50}, {1000, 100}, {200, 50}, + {1000, 50}, {50, 50}, {100, 50}, {100, 50}, + {1, 1}, {1, 1}, {20, 100}, {20, 50}, + {500, 100}, {1000, 100}, {100, 50}, {1000, 50}, + {100, 50}, {1000, 100}, {100, 50}, {100, 50}, +}; + +static struct epm_adc_platform_data epm_adc_pdata = { + .channel = ads_adc_channel_data, + .bus_id = 0x0, + .epm_i2c_board_info = { + .type = "sx1509q", + .addr = 0x3e, + .platform_data = &apq8064_sx150x_data[SX150X_EPM], + }, + .gpio_expander_base_addr = GPIO_EPM_EXPANDER_BASE, +}; + +static struct platform_device epm_adc_device = { + .name = "epm_adc", + .id = -1, + .dev = { + .platform_data = &epm_adc_pdata, + }, +}; + +static void __init apq8064_epm_adc_init(void) +{ + epm_adc_pdata.num_channels = 32; + epm_adc_pdata.num_adc = 2; + epm_adc_pdata.chan_per_adc = 16; + epm_adc_pdata.chan_per_mux = 8; +}; + +/* Micbias setting is based on 8660 CDP/MTP/FLUID requirement + * 4 micbiases are used to power various analog and digital + * microphones operating at 1800 mV. Technically, all micbiases + * can source from single cfilter since all microphones operate + * at the same voltage level. The arrangement below is to make + * sure all cfilters are exercised. LDO_H regulator ouput level + * does not need to be as high as 2.85V. It is choosen for + * microphone sensitivity purpose. + */ +static struct wcd9xxx_pdata apq8064_tabla_platform_data = { + .slimbus_slave_device = { + .name = "tabla-slave", + .e_addr = {0, 0, 0x10, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(42), + .irq_base = TABLA_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = PM8921_GPIO_PM_TO_SYS(34), + .micbias = { + .ldoh_v = TABLA_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 1800, + .cfilt3_mv = 1800, + .bias1_cfilt_sel = TABLA_CFILT1_SEL, + .bias2_cfilt_sel = TABLA_CFILT2_SEL, + .bias3_cfilt_sel = TABLA_CFILT3_SEL, + .bias4_cfilt_sel = TABLA_CFILT3_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device apq8064_slim_tabla = { + .name = "tabla-slim", + .e_addr = {0, 1, 0x10, 0, 0x17, 2}, + .dev = { + .platform_data = &apq8064_tabla_platform_data, + }, +}; + +static struct wcd9xxx_pdata apq8064_tabla20_platform_data = { + .slimbus_slave_device = { + .name = "tabla-slave", + .e_addr = {0, 0, 0x60, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(42), + .irq_base = TABLA_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = PM8921_GPIO_PM_TO_SYS(34), + .micbias = { + .ldoh_v = TABLA_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 1800, + .cfilt3_mv = 1800, + .bias1_cfilt_sel = TABLA_CFILT1_SEL, + .bias2_cfilt_sel = TABLA_CFILT2_SEL, + .bias3_cfilt_sel = TABLA_CFILT3_SEL, + .bias4_cfilt_sel = TABLA_CFILT3_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device apq8064_slim_tabla20 = { + .name = "tabla2x-slim", + .e_addr = {0, 1, 0x60, 0, 0x17, 2}, + .dev = { + .platform_data = &apq8064_tabla20_platform_data, + }, +}; + +/* enable the level shifter for cs8427 to make sure the I2C + * clock is running at 100KHz and voltage levels are at 3.3 + * and 5 volts + */ +static int enable_100KHz_ls(int enable) +{ + int ret = 0; + if (enable) { + ret = gpio_request(SX150X_GPIO(1, 10), + "cs8427_100KHZ_ENABLE"); + if (ret) { + pr_err("%s: Failed to request gpio %d\n", __func__, + SX150X_GPIO(1, 10)); + return ret; + } + gpio_direction_output(SX150X_GPIO(1, 10), 1); + } else + gpio_free(SX150X_GPIO(1, 10)); + return ret; +} + +static struct cs8427_platform_data cs8427_i2c_platform_data = { + .irq = SX150X_GPIO(1, 4), + .reset_gpio = SX150X_GPIO(1, 6), + .enable = enable_100KHz_ls, +}; + +static struct i2c_board_info cs8427_device_info[] __initdata = { + { + I2C_BOARD_INFO("cs8427", CS8427_ADDR4), + .platform_data = &cs8427_i2c_platform_data, + }, +}; + +#define HAP_SHIFT_LVL_OE_GPIO PM8921_MPP_PM_TO_SYS(8) +#define ISA1200_HAP_EN_GPIO PM8921_GPIO_PM_TO_SYS(33) +#define ISA1200_HAP_LEN_GPIO PM8921_GPIO_PM_TO_SYS(20) +#define ISA1200_HAP_CLK PM8921_GPIO_PM_TO_SYS(44) + +static int isa1200_clk_enable(bool on) +{ + int rc = 0; + + gpio_set_value_cansleep(ISA1200_HAP_CLK, on); + + if (on) { + rc = pm8xxx_aux_clk_control(CLK_MP3_2, XO_DIV_1, true); + if (rc) { + pr_err("%s: unable to write aux clock register(%d)\n", + __func__, rc); + goto err_gpio_dis; + } + } else { + rc = pm8xxx_aux_clk_control(CLK_MP3_2, XO_DIV_NONE, true); + if (rc) + pr_err("%s: unable to write aux clock register(%d)\n", + __func__, rc); + } + + return rc; + +err_gpio_dis: + gpio_set_value_cansleep(ISA1200_HAP_CLK, !on); + return rc; +} + +static int isa1200_dev_setup(bool enable) +{ + int rc = 0; + + if (!enable) + goto free_gpio; + + rc = gpio_request(ISA1200_HAP_CLK, "haptics_clk"); + if (rc) { + pr_err("%s: unable to request gpio %d config(%d)\n", + __func__, ISA1200_HAP_CLK, rc); + return rc; + } + + rc = gpio_direction_output(ISA1200_HAP_CLK, 0); + if (rc) { + pr_err("%s: unable to set direction\n", __func__); + goto free_gpio; + } + + return 0; + +free_gpio: + gpio_free(ISA1200_HAP_CLK); + return rc; +} + +static struct isa1200_regulator isa1200_reg_data[] = { + { + .name = "vddp", + .min_uV = ISA_I2C_VTG_MIN_UV, + .max_uV = ISA_I2C_VTG_MAX_UV, + .load_uA = ISA_I2C_CURR_UA, + }, +}; + +static struct isa1200_platform_data isa1200_1_pdata = { + .name = "vibrator", + .dev_setup = isa1200_dev_setup, + .clk_enable = isa1200_clk_enable, + .hap_en_gpio = ISA1200_HAP_EN_GPIO, + .hap_len_gpio = ISA1200_HAP_LEN_GPIO, + .max_timeout = 15000, + .mode_ctrl = PWM_GEN_MODE, + .pwm_fd = { + .pwm_div = 256, + }, + .is_erm = false, + .smart_en = true, + .ext_clk_en = true, + .chip_en = 1, + .regulator_info = isa1200_reg_data, + .num_regulators = ARRAY_SIZE(isa1200_reg_data), +}; + +static struct i2c_board_info isa1200_board_info[] __initdata = { + { + I2C_BOARD_INFO("isa1200_1", 0x90>>1), + .platform_data = &isa1200_1_pdata, + }, +}; +/* configuration data for mxt1386e using V2.1 firmware */ +static const u8 mxt1386e_config_data_v2_1[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 14, 2, 0, 24, 5, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T7 Object */ + 100, 10, 50, + /* T8 Object */ + 25, 0, 20, 20, 0, 0, 0, 0, 0, 0, + /* T9 Object */ + 139, 0, 0, 26, 42, 0, 32, 80, 2, 5, + 0, 5, 5, 0, 10, 30, 10, 10, 255, 2, + 85, 5, 0, 5, 9, 5, 12, 35, 70, 40, + 20, 5, 0, 0, 0, + /* T18 Object */ + 0, 0, + /* T24 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T25 Object */ + 1, 0, 60, 115, 156, 99, + /* T27 Object */ + 0, 0, 0, 0, 0, 0, 0, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T42 Object */ + 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, + /* T43 Object */ + 0, 0, 0, 0, 0, 0, 0, 64, 0, 8, + 16, + /* T46 Object */ + 68, 0, 16, 16, 0, 0, 0, 0, 0, + /* T47 Object */ + 0, 0, 0, 0, 0, 0, 3, 64, 66, 0, + /* T48 Object */ + 1, 64, 64, 0, 0, 0, 0, 0, 0, 0, + 32, 40, 0, 10, 10, 0, 0, 100, 10, 90, + 0, 0, 0, 0, 0, 0, 0, 10, 1, 10, + 52, 10, 12, 0, 33, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T56 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +#define MXT_TS_GPIO_IRQ 6 +#define MXT_TS_PWR_EN_GPIO PM8921_GPIO_PM_TO_SYS(23) +#define MXT_TS_RESET_GPIO 33 + +static struct mxt_config_info mxt_config_array[] = { + { + .config = mxt1386e_config_data_v2_1, + .config_length = ARRAY_SIZE(mxt1386e_config_data_v2_1), + .family_id = 0xA0, + .variant_id = 0x7, + .version = 0x21, + .build = 0xAA, + .bootldr_id = MXT_BOOTLOADER_ID_1386E, + .fw_name = "atmel_8064_liquid_v2_2_AA.hex", + }, + { + /* The config data for V2.2.AA is the same as for V2.1.AA */ + .config = mxt1386e_config_data_v2_1, + .config_length = ARRAY_SIZE(mxt1386e_config_data_v2_1), + .family_id = 0xA0, + .variant_id = 0x7, + .version = 0x22, + .build = 0xAA, + .bootldr_id = MXT_BOOTLOADER_ID_1386E, + }, +}; + +static struct mxt_platform_data mxt_platform_data = { + .config_array = mxt_config_array, + .config_array_size = ARRAY_SIZE(mxt_config_array), + .panel_minx = 0, + .panel_maxx = 1365, + .panel_miny = 0, + .panel_maxy = 767, + .disp_minx = 0, + .disp_maxx = 1365, + .disp_miny = 0, + .disp_maxy = 767, + .irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + .i2c_pull_up = true, + .reset_gpio = MXT_TS_RESET_GPIO, + .irq_gpio = MXT_TS_GPIO_IRQ, +}; + +static struct i2c_board_info mxt_device_info[] __initdata = { + { + I2C_BOARD_INFO("atmel_mxt_ts", 0x5b), + .platform_data = &mxt_platform_data, + .irq = MSM_GPIO_TO_INT(MXT_TS_GPIO_IRQ), + }, +}; +#define CYTTSP_TS_GPIO_IRQ 6 +#define CYTTSP_TS_GPIO_SLEEP 33 + +static ssize_t tma340_vkeys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, 200, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":73:1120:97:97" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":230:1120:97:97" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":389:1120:97:97" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":544:1120:97:97" + "\n"); +} + +static struct kobj_attribute tma340_vkeys_attr = { + .attr = { + .mode = S_IRUGO, + }, + .show = &tma340_vkeys_show, +}; + +static struct attribute *tma340_properties_attrs[] = { + &tma340_vkeys_attr.attr, + NULL +}; + +static struct attribute_group tma340_properties_attr_group = { + .attrs = tma340_properties_attrs, +}; + +static int cyttsp_platform_init(struct i2c_client *client) +{ + int rc = 0; + static struct kobject *tma340_properties_kobj; + + tma340_vkeys_attr.attr.name = "virtualkeys.cyttsp-i2c"; + tma340_properties_kobj = kobject_create_and_add("board_properties", + NULL); + if (tma340_properties_kobj) + rc = sysfs_create_group(tma340_properties_kobj, + &tma340_properties_attr_group); + if (!tma340_properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", + __func__); + + return 0; +} + +static struct cyttsp_regulator cyttsp_regulator_data[] = { + { + .name = "vdd", + .min_uV = CY_TMA300_VTG_MIN_UV, + .max_uV = CY_TMA300_VTG_MAX_UV, + .hpm_load_uA = CY_TMA300_CURR_24HZ_UA, + .lpm_load_uA = CY_TMA300_CURR_24HZ_UA, + }, + { + .name = "vcc_i2c", + .min_uV = CY_I2C_VTG_MIN_UV, + .max_uV = CY_I2C_VTG_MAX_UV, + .hpm_load_uA = CY_I2C_CURR_UA, + .lpm_load_uA = CY_I2C_CURR_UA, + }, +}; + +static struct cyttsp_platform_data cyttsp_pdata = { + .panel_maxx = 634, + .panel_maxy = 1166, + .disp_maxx = 599, + .disp_maxy = 1023, + .disp_minx = 0, + .disp_miny = 0, + .flags = 0x01, + .gen = CY_GEN3, + .use_st = CY_USE_ST, + .use_mt = CY_USE_MT, + .use_hndshk = CY_SEND_HNDSHK, + .use_trk_id = CY_USE_TRACKING_ID, + .use_sleep = CY_USE_DEEP_SLEEP_SEL, + .use_gestures = CY_USE_GESTURES, + .fw_fname = "cyttsp_8064_mtp.hex", + /* change act_intrvl to customize the Active power state + * scanning/processing refresh interval for Operating mode + */ + .act_intrvl = CY_ACT_INTRVL_DFLT, + /* change tch_tmout to customize the touch timeout for the + * Active power state for Operating mode + */ + .tch_tmout = CY_TCH_TMOUT_DFLT, + /* change lp_intrvl to customize the Low Power power state + * scanning/processing refresh interval for Operating mode + */ + .lp_intrvl = CY_LP_INTRVL_DFLT, + .sleep_gpio = CYTTSP_TS_GPIO_SLEEP, + .resout_gpio = -1, + .irq_gpio = CYTTSP_TS_GPIO_IRQ, + .regulator_info = cyttsp_regulator_data, + .num_regulators = ARRAY_SIZE(cyttsp_regulator_data), + .init = cyttsp_platform_init, + .correct_fw_ver = 17, +}; + +static struct i2c_board_info cyttsp_info[] __initdata = { + { + I2C_BOARD_INFO(CY_I2C_NAME, 0x24), + .platform_data = &cyttsp_pdata, + .irq = MSM_GPIO_TO_INT(CYTTSP_TS_GPIO_IRQ), + }, +}; + +#define MSM_WCNSS_PHYS 0x03000000 +#define MSM_WCNSS_SIZE 0x280000 + +static struct resource resources_wcnss_wlan[] = { + { + .start = RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + .end = RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + .name = "wcnss_wlanrx_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, + .end = RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, + .name = "wcnss_wlantx_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_WCNSS_PHYS, + .end = MSM_WCNSS_PHYS + MSM_WCNSS_SIZE - 1, + .name = "wcnss_mmio", + .flags = IORESOURCE_MEM, + }, + { + .start = 64, + .end = 68, + .name = "wcnss_gpios_5wire", + .flags = IORESOURCE_IO, + }, +}; + +static struct qcom_wcnss_opts qcom_wcnss_pdata = { + .has_48mhz_xo = 1, +}; + +static struct platform_device msm_device_wcnss_wlan = { + .name = "wcnss_wlan", + .id = 0, + .num_resources = ARRAY_SIZE(resources_wcnss_wlan), + .resource = resources_wcnss_wlan, + .dev = {.platform_data = &qcom_wcnss_pdata}, +}; + +static struct platform_device msm_device_iris_fm __devinitdata = { + .name = "iris_fm", + .id = -1, +}; + +#ifdef CONFIG_QSEECOM +/* qseecom bus scaling */ +static struct msm_bus_vectors qseecom_clks_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 0, + .ab = 0, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = 0, + .ab = 0, + }, +}; + +static struct msm_bus_vectors qseecom_enable_dfab_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = (492 * 8) * 1000000UL, + .ab = (492 * 8) * 100000UL, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = 0, + .ab = 0, + }, +}; + +static struct msm_bus_vectors qseecom_enable_sfpb_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 0, + .ab = 0, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = (64 * 8) * 1000000UL, + .ab = (64 * 8) * 100000UL, + }, +}; + +static struct msm_bus_paths qseecom_hw_bus_scale_usecases[] = { + { + ARRAY_SIZE(qseecom_clks_init_vectors), + qseecom_clks_init_vectors, + }, + { + ARRAY_SIZE(qseecom_enable_dfab_vectors), + qseecom_enable_sfpb_vectors, + }, + { + ARRAY_SIZE(qseecom_enable_sfpb_vectors), + qseecom_enable_sfpb_vectors, + }, +}; + +static struct msm_bus_scale_pdata qseecom_bus_pdata = { + qseecom_hw_bus_scale_usecases, + ARRAY_SIZE(qseecom_hw_bus_scale_usecases), + .name = "qsee", +}; + +static struct platform_device qseecom_device = { + .name = "qseecom", + .id = 0, + .dev = { + .platform_data = &qseecom_bus_pdata, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +#define QCE_SIZE 0x10000 +#define QCE_0_BASE 0x11000000 + +#define QCE_HW_KEY_SUPPORT 0 +#define QCE_SHA_HMAC_SUPPORT 1 +#define QCE_SHARE_CE_RESOURCE 3 +#define QCE_CE_SHARED 0 + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV8064_CE_IN_CHAN, + .end = DMOV8064_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV8064_CE_IN_CRCI, + .end = DMOV8064_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV8064_CE_OUT_CRCI, + .end = DMOV8064_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV8064_CE_IN_CHAN, + .end = DMOV8064_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV8064_CE_IN_CRCI, + .end = DMOV8064_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV8064_CE_OUT_CRCI, + .end = DMOV8064_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +static struct platform_device qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +static struct platform_device qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; +#endif + +static struct mdm_platform_data mdm_platform_data = { + .mdm_version = "3.0", + .ramdump_delay_ms = 2000, + .early_power_on = 1, + .sfr_query = 1, + .peripheral_platform_device = &apq8064_device_hsic_host, +}; + +static struct tsens_platform_data apq_tsens_pdata = { + .tsens_factor = 1000, + .hw_type = APQ_8064, + .tsens_num_sensor = 11, + .slope = {1176, 1176, 1154, 1176, 1111, + 1132, 1132, 1199, 1132, 1199, 1132}, +}; + +static struct platform_device msm_tsens_device = { + .name = "tsens8960-tm", + .id = -1, +}; + +#define MSM_SHARED_RAM_PHYS 0x80000000 +static void __init apq8064_map_io(void) +{ + msm_shared_ram_phys = MSM_SHARED_RAM_PHYS; + msm_map_apq8064_io(); + if (socinfo_init() < 0) + pr_err("socinfo_init() failed!\n"); +} + +static void __init apq8064_init_irq(void) +{ + struct msm_mpm_device_data *data = NULL; + +#ifdef CONFIG_MSM_MPM + data = &apq8064_mpm_dev_data; +#endif + + msm_mpm_irq_extn_init(data); + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, + (void *)MSM_QGIC_CPU_BASE); +} + +static struct platform_device msm8064_device_saw_regulator_core0 = { + .name = "saw-regulator", + .id = 0, + .dev = { + .platform_data = &msm8064_saw_regulator_pdata_8921_s5, + }, +}; + +static struct platform_device msm8064_device_saw_regulator_core1 = { + .name = "saw-regulator", + .id = 1, + .dev = { + .platform_data = &msm8064_saw_regulator_pdata_8921_s6, + }, +}; + +static struct platform_device msm8064_device_saw_regulator_core2 = { + .name = "saw-regulator", + .id = 2, + .dev = { + .platform_data = &msm8064_saw_regulator_pdata_8821_s0, + }, +}; + +static struct platform_device msm8064_device_saw_regulator_core3 = { + .name = "saw-regulator", + .id = 3, + .dev = { + .platform_data = &msm8064_saw_regulator_pdata_8821_s1, + + }, +}; + +static struct msm_rpmrs_level msm_rpmrs_levels[] = { + { + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1, 784, 180000, 100, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1300, 228, 1200000, 2000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, GDHS, MAX, ACTIVE), + false, + 2000, 138, 1208400, 3200, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 6000, 119, 1850300, 9000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, GDHS, MAX, ACTIVE), + false, + 9200, 68, 2839200, 16400, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE), + false, + 10300, 63, 3128000, 18200, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 18000, 10, 4602600, 27000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW), + false, + 20000, 2, 5752000, 32000, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_TZ, +}; + +static struct msm_rpmrs_platform_data msm_rpmrs_data __initdata = { + .levels = &msm_rpmrs_levels[0], + .num_levels = ARRAY_SIZE(msm_rpmrs_levels), + .vdd_mem_levels = { + [MSM_RPMRS_VDD_MEM_RET_LOW] = 750000, + [MSM_RPMRS_VDD_MEM_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_MEM_ACTIVE] = 1050000, + [MSM_RPMRS_VDD_MEM_MAX] = 1150000, + }, + .vdd_dig_levels = { + [MSM_RPMRS_VDD_DIG_RET_LOW] = 500000, + [MSM_RPMRS_VDD_DIG_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_DIG_ACTIVE] = 950000, + [MSM_RPMRS_VDD_DIG_MAX] = 1150000, + }, + .vdd_mask = 0x7FFFFF, + .rpmrs_target_id = { + [MSM_RPMRS_ID_PXO_CLK] = MSM_RPM_ID_PXO_CLK, + [MSM_RPMRS_ID_L2_CACHE_CTL] = MSM_RPM_ID_LAST, + [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_PM8921_S3_0, + [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_PM8921_S3_1, + [MSM_RPMRS_ID_VDD_MEM_0] = MSM_RPM_ID_PM8921_L24_0, + [MSM_RPMRS_ID_VDD_MEM_1] = MSM_RPM_ID_PM8921_L24_1, + [MSM_RPMRS_ID_RPM_CTL] = MSM_RPM_ID_RPM_CTL, + }, +}; + +static uint8_t spm_wfi_cmd_sequence[] __initdata = { + 0x03, 0x0f, +}; + +static uint8_t spm_power_collapse_without_rpm[] __initdata = { + 0x00, 0x24, 0x54, 0x10, + 0x09, 0x03, 0x01, + 0x10, 0x54, 0x30, 0x0C, + 0x24, 0x30, 0x0f, +}; + +static uint8_t spm_power_collapse_with_rpm[] __initdata = { + 0x00, 0x24, 0x54, 0x10, + 0x09, 0x07, 0x01, 0x0B, + 0x10, 0x54, 0x30, 0x0C, + 0x24, 0x30, 0x0f, +}; + +static struct msm_spm_seq_entry msm_spm_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_MODE_CLOCK_GATING, + .notify_rpm = false, + .cmd = spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = false, + .cmd = spm_power_collapse_without_rpm, + }, + [2] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = spm_power_collapse_with_rpm, + }, +}; + +static uint8_t l2_spm_wfi_cmd_sequence[] __initdata = { + 0x00, 0x20, 0x03, 0x20, + 0x00, 0x0f, +}; + +static uint8_t l2_spm_gdhs_cmd_sequence[] __initdata = { + 0x00, 0x20, 0x34, 0x64, + 0x48, 0x07, 0x48, 0x20, + 0x50, 0x64, 0x04, 0x34, + 0x50, 0x0f, +}; +static uint8_t l2_spm_power_off_cmd_sequence[] __initdata = { + 0x00, 0x10, 0x34, 0x64, + 0x48, 0x07, 0x48, 0x10, + 0x50, 0x64, 0x04, 0x34, + 0x50, 0x0F, +}; + +static struct msm_spm_seq_entry msm_spm_l2_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_L2_MODE_RETENTION, + .notify_rpm = false, + .cmd = l2_spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_L2_MODE_GDHS, + .notify_rpm = true, + .cmd = l2_spm_gdhs_cmd_sequence, + }, + [2] = { + .mode = MSM_SPM_L2_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = l2_spm_power_off_cmd_sequence, + }, +}; + + +static struct msm_spm_platform_data msm_spm_l2_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW_L2_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x00A000AE, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x00A00020, + .modes = msm_spm_l2_seq_list, + .num_modes = ARRAY_SIZE(msm_spm_l2_seq_list), + }, +}; + +static struct msm_spm_platform_data msm_spm_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, + [1] = { + .reg_base_addr = MSM_SAW1_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, + [2] = { + .reg_base_addr = MSM_SAW2_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, + [3] = { + .reg_base_addr = MSM_SAW3_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, +}; + +static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = { + .base_addr = MSM_ACC0_BASE + 0x08, + .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE, + .mask = 1UL << 13, +}; + +static void __init apq8064_init_buses(void) +{ + msm_bus_rpm_set_mt_mask(); + msm_bus_8064_apps_fabric_pdata.rpm_enabled = 1; + msm_bus_8064_sys_fabric_pdata.rpm_enabled = 1; + msm_bus_8064_mm_fabric_pdata.rpm_enabled = 1; + msm_bus_8064_apps_fabric.dev.platform_data = + &msm_bus_8064_apps_fabric_pdata; + msm_bus_8064_sys_fabric.dev.platform_data = + &msm_bus_8064_sys_fabric_pdata; + msm_bus_8064_mm_fabric.dev.platform_data = + &msm_bus_8064_mm_fabric_pdata; + msm_bus_8064_sys_fpb.dev.platform_data = &msm_bus_8064_sys_fpb_pdata; + msm_bus_8064_cpss_fpb.dev.platform_data = &msm_bus_8064_cpss_fpb_pdata; +} + +/* PCIe gpios */ +static struct msm_pcie_gpio_info_t msm_pcie_gpio_info[MSM_PCIE_MAX_GPIO] = { + {"rst_n", PM8921_MPP_PM_TO_SYS(PCIE_RST_N_PMIC_MPP), 0}, + {"pwr_en", PM8921_GPIO_PM_TO_SYS(PCIE_PWR_EN_PMIC_GPIO), 1}, +}; + +static struct msm_pcie_platform msm_pcie_platform_data = { + .gpio = msm_pcie_gpio_info, +}; + +static void __init mpq8064_pcie_init(void) +{ + msm_device_pcie.dev.platform_data = &msm_pcie_platform_data; + platform_device_register(&msm_device_pcie); +} + +static struct platform_device apq8064_device_ext_5v_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_MPP_PM_TO_SYS(7), + .dev = { + .platform_data + = &apq8064_gpio_regulator_pdata[GPIO_VREG_ID_EXT_5V], + }, +}; + +static struct platform_device apq8064_device_ext_mpp8_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_MPP_PM_TO_SYS(8), + .dev = { + .platform_data + = &apq8064_gpio_regulator_pdata[GPIO_VREG_ID_EXT_MPP8], + }, +}; + +static struct platform_device apq8064_device_ext_3p3v_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = APQ8064_EXT_3P3V_REG_EN_GPIO, + .dev = { + .platform_data = + &apq8064_gpio_regulator_pdata[GPIO_VREG_ID_EXT_3P3V], + }, +}; + +static struct platform_device apq8064_device_ext_ts_sw_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_GPIO_PM_TO_SYS(23), + .dev = { + .platform_data + = &apq8064_gpio_regulator_pdata[GPIO_VREG_ID_EXT_TS_SW], + }, +}; + +static struct platform_device apq8064_device_rpm_regulator __devinitdata = { + .name = "rpm-regulator", + .id = -1, + .dev = { + .platform_data = &apq8064_rpm_regulator_pdata, + }, +}; + +static struct gpio_ir_recv_platform_data gpio_ir_recv_pdata = { + .gpio_nr = 88, + .active_low = 1, +}; + +static struct platform_device gpio_ir_recv_pdev = { + .name = "gpio-rc-recv", + .dev = { + .platform_data = &gpio_ir_recv_pdata, + }, +}; + +static struct platform_device *common_not_mpq_devices[] __initdata = { + &apq8064_device_qup_i2c_gsbi1, + &apq8064_device_qup_i2c_gsbi3, + &apq8064_device_qup_i2c_gsbi4, +}; + +static struct platform_device *common_devices[] __initdata = { + &apq8064_device_dmov, + &apq8064_device_qup_spi_gsbi5, + &apq8064_device_ext_5v_vreg, + &apq8064_device_ext_mpp8_vreg, + &apq8064_device_ext_3p3v_vreg, + &apq8064_device_ssbi_pmic1, + &apq8064_device_ssbi_pmic2, + &apq8064_device_ext_ts_sw_vreg, + &msm_device_smd_apq8064, + &apq8064_device_otg, + &apq8064_device_gadget_peripheral, + &apq8064_device_hsusb_host, + &android_usb_device, + &msm_device_wcnss_wlan, + &msm_device_iris_fm, + &apq8064_fmem_device, +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + &apq8064_android_pmem_device, + &apq8064_android_pmem_adsp_device, + &apq8064_android_pmem_audio_device, +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +#ifdef CONFIG_ION_MSM + &apq8064_ion_dev, +#endif + &msm8064_device_watchdog, + &msm8064_device_saw_regulator_core0, + &msm8064_device_saw_regulator_core1, + &msm8064_device_saw_regulator_core2, + &msm8064_device_saw_regulator_core3, +#if defined(CONFIG_QSEECOM) + &qseecom_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &qcedev_device, +#endif + +#ifdef CONFIG_HW_RANDOM_MSM + &apq8064_device_rng, +#endif + &apq_pcm, + &apq_pcm_routing, + &apq_cpudai0, + &apq_cpudai1, + &mpq_cpudai_sec_i2s_rx, + &mpq_cpudai_mi2s_tx, + &apq_cpudai_hdmi_rx, + &apq_cpudai_bt_rx, + &apq_cpudai_bt_tx, + &apq_cpudai_fm_rx, + &apq_cpudai_fm_tx, + &apq_cpu_fe, + &apq_stub_codec, + &apq_voice, + &apq_voip, + &apq_lpa_pcm, + &apq_compr_dsp, + &apq_multi_ch_pcm, + &apq_pcm_hostless, + &apq_cpudai_afe_01_rx, + &apq_cpudai_afe_01_tx, + &apq_cpudai_afe_02_rx, + &apq_cpudai_afe_02_tx, + &apq_pcm_afe, + &apq_cpudai_auxpcm_rx, + &apq_cpudai_auxpcm_tx, + &apq_cpudai_stub, + &apq_cpudai_slimbus_1_rx, + &apq_cpudai_slimbus_1_tx, + &apq_cpudai_slimbus_2_rx, + &apq_cpudai_slimbus_2_tx, + &apq_cpudai_slimbus_3_rx, + &apq_cpudai_slimbus_3_tx, + &apq8064_rpm_device, + &apq8064_rpm_log_device, + &apq8064_rpm_stat_device, + &apq_device_tz_log, + &msm_bus_8064_apps_fabric, + &msm_bus_8064_sys_fabric, + &msm_bus_8064_mm_fabric, + &msm_bus_8064_sys_fpb, + &msm_bus_8064_cpss_fpb, + &apq8064_msm_device_vidc, + &msm_pil_dsps, + &msm_8960_riva, + &msm_8960_q6_lpass, + &msm_pil_vidc, + &msm_gss, + &apq8064_rtb_device, + &apq8064_cpu_idle_device, + &apq8064_msm_gov_device, + &apq8064_device_cache_erp, + &epm_adc_device, + &apq8064_qdss_device, + &msm_etb_device, + &msm_tpiu_device, + &msm_funnel_device, + &apq8064_etm_device, + &apq_cpudai_slim_4_rx, + &apq_cpudai_slim_4_tx, +#ifdef CONFIG_MSM_GEMINI + &msm8960_gemini_device, +#endif + &apq8064_iommu_domain_device, + &msm_tsens_device, + &apq8064_cache_dump_device, +}; + +static struct platform_device *sim_devices[] __initdata = { + &apq8064_device_uart_gsbi3, + &msm_device_sps_apq8064, +}; + +static struct platform_device *rumi3_devices[] __initdata = { + &apq8064_device_uart_gsbi1, + &msm_device_sps_apq8064, +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif +}; + +static struct platform_device *cdp_devices[] __initdata = { + &apq8064_device_uart_gsbi1, + &apq8064_device_uart_gsbi7, + &msm_device_sps_apq8064, +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif +}; + +static struct platform_device +mpq8064_device_ext_1p2_buck_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = SX150X_GPIO(4, 2), + .dev = { + .platform_data = + &mpq8064_gpio_regulator_pdata[GPIO_VREG_ID_AVC_1P2V], + }, +}; + +static struct platform_device +mpq8064_device_ext_1p8_buck_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = SX150X_GPIO(4, 4), + .dev = { + .platform_data = + &mpq8064_gpio_regulator_pdata[GPIO_VREG_ID_AVC_1P8V], + }, +}; + +static struct platform_device +mpq8064_device_ext_2p2_buck_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = SX150X_GPIO(4, 14), + .dev = { + .platform_data = + &mpq8064_gpio_regulator_pdata[GPIO_VREG_ID_AVC_2P2V], + }, +}; + +static struct platform_device +mpq8064_device_ext_5v_buck_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = SX150X_GPIO(4, 3), + .dev = { + .platform_data = + &mpq8064_gpio_regulator_pdata[GPIO_VREG_ID_AVC_5V], + }, +}; + +static struct platform_device +mpq8064_device_ext_3p3v_ldo_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = SX150X_GPIO(4, 15), + .dev = { + .platform_data = + &mpq8064_gpio_regulator_pdata[GPIO_VREG_ID_AVC_3P3V], + }, +}; + +static struct platform_device rc_input_loopback_pdev = { + .name = "rc-user-input", + .id = -1, +}; + +static int rf4ce_gpio_init(void) +{ + if (!machine_is_mpq8064_cdp()) + return -EINVAL; + + /* CC2533 SRDY Input */ + if (!gpio_request(SX150X_GPIO(4, 6), "rf4ce_srdy")) { + gpio_direction_input(SX150X_GPIO(4, 6)); + gpio_export(SX150X_GPIO(4, 6), true); + } + + /* CC2533 MRDY Output */ + if (!gpio_request(SX150X_GPIO(4, 5), "rf4ce_mrdy")) { + gpio_direction_output(SX150X_GPIO(4, 5), 1); + gpio_export(SX150X_GPIO(4, 5), true); + } + + /* CC2533 Reset Output */ + if (!gpio_request(SX150X_GPIO(4, 7), "rf4ce_reset")) { + gpio_direction_output(SX150X_GPIO(4, 7), 0); + gpio_export(SX150X_GPIO(4, 7), true); + } + + return 0; +} +late_initcall(rf4ce_gpio_init); + +static struct platform_device *mpq_devices[] __initdata = { + &msm_device_sps_apq8064, + &mpq8064_device_qup_i2c_gsbi5, +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif + &gpio_ir_recv_pdev, + &mpq8064_device_ext_1p2_buck_vreg, + &mpq8064_device_ext_1p8_buck_vreg, + &mpq8064_device_ext_2p2_buck_vreg, + &mpq8064_device_ext_5v_buck_vreg, + &mpq8064_device_ext_3p3v_ldo_vreg, +#ifdef CONFIG_MSM_VCAP + &msm8064_device_vcap, +#endif + &rc_input_loopback_pdev, +}; + +static struct msm_spi_platform_data apq8064_qup_spi_gsbi5_pdata = { + .max_clock_speed = 1100000, +}; + +#define KS8851_IRQ_GPIO 43 + +static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "ks8851", + .irq = MSM_GPIO_TO_INT(KS8851_IRQ_GPIO), + .max_speed_hz = 19200000, + .bus_num = 0, + .chip_select = 2, + .mode = SPI_MODE_0, + }, + { + .modalias = "epm_adc", + .max_speed_hz = 1100000, + .bus_num = 0, + .chip_select = 3, + .mode = SPI_MODE_0, + }, +}; + +static struct slim_boardinfo apq8064_slim_devices[] = { + { + .bus_num = 1, + .slim_slave = &apq8064_slim_tabla, + }, + { + .bus_num = 1, + .slim_slave = &apq8064_slim_tabla20, + }, + /* add more slimbus slaves as needed */ +}; + +static struct msm_i2c_platform_data apq8064_i2c_qup_gsbi1_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data apq8064_i2c_qup_gsbi3_pdata = { + .clk_freq = 384000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data apq8064_i2c_qup_gsbi4_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data mpq8064_i2c_qup_gsbi5_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +#define GSBI_DUAL_MODE_CODE 0x60 +#define MSM_GSBI1_PHYS 0x12440000 +static void __init apq8064_i2c_init(void) +{ + void __iomem *gsbi_mem; + + apq8064_device_qup_i2c_gsbi1.dev.platform_data = + &apq8064_i2c_qup_gsbi1_pdata; + gsbi_mem = ioremap_nocache(MSM_GSBI1_PHYS, 4); + writel_relaxed(GSBI_DUAL_MODE_CODE, gsbi_mem); + /* Ensure protocol code is written before proceeding */ + wmb(); + iounmap(gsbi_mem); + apq8064_i2c_qup_gsbi1_pdata.use_gsbi_shared_mode = 1; + apq8064_device_qup_i2c_gsbi3.dev.platform_data = + &apq8064_i2c_qup_gsbi3_pdata; + apq8064_device_qup_i2c_gsbi1.dev.platform_data = + &apq8064_i2c_qup_gsbi1_pdata; + apq8064_device_qup_i2c_gsbi4.dev.platform_data = + &apq8064_i2c_qup_gsbi4_pdata; + mpq8064_device_qup_i2c_gsbi5.dev.platform_data = + &mpq8064_i2c_qup_gsbi5_pdata; +} + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static int ethernet_init(void) +{ + int ret; + ret = gpio_request(KS8851_IRQ_GPIO, "ks8851_irq"); + if (ret) { + pr_err("ks8851 gpio_request failed: %d\n", ret); + goto fail; + } + + return 0; +fail: + return ret; +} +#else +static int ethernet_init(void) +{ + return 0; +} +#endif + +#define GPIO_KEY_HOME PM8921_GPIO_PM_TO_SYS(27) +#define GPIO_KEY_VOLUME_UP PM8921_GPIO_PM_TO_SYS(35) +#define GPIO_KEY_VOLUME_DOWN PM8921_GPIO_PM_TO_SYS(38) +#define GPIO_KEY_CAM_FOCUS PM8921_GPIO_PM_TO_SYS(3) +#define GPIO_KEY_CAM_SNAP PM8921_GPIO_PM_TO_SYS(4) +#define GPIO_KEY_ROTATION PM8921_GPIO_PM_TO_SYS(42) + +static struct gpio_keys_button cdp_keys[] = { + { + .code = KEY_HOME, + .gpio = GPIO_KEY_HOME, + .desc = "home_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = KEY_VOLUMEUP, + .gpio = GPIO_KEY_VOLUME_UP, + .desc = "volume_up_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = KEY_VOLUMEDOWN, + .gpio = GPIO_KEY_VOLUME_DOWN, + .desc = "volume_down_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = SW_ROTATE_LOCK, + .gpio = GPIO_KEY_ROTATION, + .desc = "rotate_key", + .active_low = 1, + .type = EV_SW, + .debounce_interval = 15, + }, +}; + +static struct gpio_keys_platform_data cdp_keys_data = { + .buttons = cdp_keys, + .nbuttons = ARRAY_SIZE(cdp_keys), +}; + +static struct platform_device cdp_kp_pdev = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &cdp_keys_data, + }, +}; + +static struct gpio_keys_button mtp_keys[] = { + { + .code = KEY_CAMERA_FOCUS, + .gpio = GPIO_KEY_CAM_FOCUS, + .desc = "cam_focus_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = KEY_VOLUMEUP, + .gpio = GPIO_KEY_VOLUME_UP, + .desc = "volume_up_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = KEY_VOLUMEDOWN, + .gpio = GPIO_KEY_VOLUME_DOWN, + .desc = "volume_down_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = KEY_CAMERA_SNAPSHOT, + .gpio = GPIO_KEY_CAM_SNAP, + .desc = "cam_snap_key", + .active_low = 1, + .type = EV_KEY, + .debounce_interval = 15, + }, +}; + +static struct gpio_keys_platform_data mtp_keys_data = { + .buttons = mtp_keys, + .nbuttons = ARRAY_SIZE(mtp_keys), +}; + +static struct platform_device mtp_kp_pdev = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &mtp_keys_data, + }, +}; + +static struct gpio_keys_button mpq_keys[] = { + { + .code = KEY_VOLUMEDOWN, + .gpio = GPIO_KEY_VOLUME_DOWN, + .desc = "volume_down_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, + { + .code = KEY_VOLUMEUP, + .gpio = GPIO_KEY_VOLUME_UP, + .desc = "volume_up_key", + .active_low = 1, + .type = EV_KEY, + .wakeup = 1, + .debounce_interval = 15, + }, +}; + +static struct gpio_keys_platform_data mpq_keys_data = { + .buttons = mpq_keys, + .nbuttons = ARRAY_SIZE(mpq_keys), +}; + +static struct platform_device mpq_gpio_keys_pdev = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &mpq_keys_data, + }, +}; + +#define MPQ_KP_ROW_BASE SX150X_EXP2_GPIO_BASE +#define MPQ_KP_COL_BASE (SX150X_EXP2_GPIO_BASE + 4) + +static unsigned int mpq_row_gpios[] = {MPQ_KP_ROW_BASE, MPQ_KP_ROW_BASE + 1, + MPQ_KP_ROW_BASE + 2, MPQ_KP_ROW_BASE + 3}; +static unsigned int mpq_col_gpios[] = {MPQ_KP_COL_BASE, MPQ_KP_COL_BASE + 1, + MPQ_KP_COL_BASE + 2}; + +static const unsigned int mpq_keymap[] = { + KEY(0, 0, KEY_UP), + KEY(0, 1, KEY_ENTER), + KEY(0, 2, KEY_3), + + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_EXIT), + KEY(1, 2, KEY_4), + + KEY(2, 0, KEY_LEFT), + KEY(2, 1, KEY_1), + KEY(2, 2, KEY_5), + + KEY(3, 0, KEY_RIGHT), + KEY(3, 1, KEY_2), + KEY(3, 2, KEY_6), +}; + +static struct matrix_keymap_data mpq_keymap_data = { + .keymap_size = ARRAY_SIZE(mpq_keymap), + .keymap = mpq_keymap, +}; + +static struct matrix_keypad_platform_data mpq_keypad_data = { + .keymap_data = &mpq_keymap_data, + .row_gpios = mpq_row_gpios, + .col_gpios = mpq_col_gpios, + .num_row_gpios = ARRAY_SIZE(mpq_row_gpios), + .num_col_gpios = ARRAY_SIZE(mpq_col_gpios), + .col_scan_delay_us = 32000, + .debounce_ms = 20, + .wakeup = 1, + .active_low = 1, + .no_autorepeat = 1, +}; + +static struct platform_device mpq_keypad_device = { + .name = "matrix-keypad", + .id = -1, + .dev = { + .platform_data = &mpq_keypad_data, + }, +}; + +/* Sensors DSPS platform data */ +#define DSPS_PIL_GENERIC_NAME "dsps" +static void __init apq8064_init_dsps(void) +{ + struct msm_dsps_platform_data *pdata = + msm_dsps_device_8064.dev.platform_data; + pdata->pil_name = DSPS_PIL_GENERIC_NAME; + pdata->gpios = NULL; + pdata->gpios_num = 0; + + platform_device_register(&msm_dsps_device_8064); +} + +#define I2C_SURF 1 +#define I2C_FFA (1 << 1) +#define I2C_RUMI (1 << 2) +#define I2C_SIM (1 << 3) +#define I2C_LIQUID (1 << 4) +#define I2C_MPQ_CDP BIT(5) +#define I2C_MPQ_HRD BIT(6) +#define I2C_MPQ_DTV BIT(7) + +struct i2c_registry { + u8 machs; + int bus; + struct i2c_board_info *info; + int len; +}; + +static struct i2c_registry apq8064_i2c_devices[] __initdata = { + { + I2C_LIQUID, + APQ_8064_GSBI1_QUP_I2C_BUS_ID, + smb349_charger_i2c_info, + ARRAY_SIZE(smb349_charger_i2c_info) + }, + { + I2C_SURF | I2C_LIQUID, + APQ_8064_GSBI3_QUP_I2C_BUS_ID, + mxt_device_info, + ARRAY_SIZE(mxt_device_info), + }, + { + I2C_FFA, + APQ_8064_GSBI3_QUP_I2C_BUS_ID, + cyttsp_info, + ARRAY_SIZE(cyttsp_info), + }, + { + I2C_FFA | I2C_LIQUID, + APQ_8064_GSBI1_QUP_I2C_BUS_ID, + isa1200_board_info, + ARRAY_SIZE(isa1200_board_info), + }, + { + I2C_MPQ_CDP, + APQ_8064_GSBI5_QUP_I2C_BUS_ID, + cs8427_device_info, + ARRAY_SIZE(cs8427_device_info), + }, +}; + +#define SX150X_EXP1_INT_N PM8921_MPP_IRQ(PM8921_IRQ_BASE, 9) +#define SX150X_EXP2_INT_N MSM_GPIO_TO_INT(81) + +struct sx150x_platform_data mpq8064_sx150x_pdata[] = { + [SX150X_EXP1] = { + .gpio_base = SX150X_EXP1_GPIO_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0, + .io_pulldn_ena = 0x0, + .io_open_drain_ena = 0x0, + .io_polarity = 0, + .irq_summary = SX150X_EXP1_INT_N, + .irq_base = SX150X_EXP1_IRQ_BASE, + }, + [SX150X_EXP2] = { + .gpio_base = SX150X_EXP2_GPIO_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0f, + .io_pulldn_ena = 0x70, + .io_open_drain_ena = 0x0, + .io_polarity = 0, + .irq_summary = SX150X_EXP2_INT_N, + .irq_base = SX150X_EXP2_IRQ_BASE, + }, + [SX150X_EXP3] = { + .gpio_base = SX150X_EXP3_GPIO_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0, + .io_pulldn_ena = 0x0, + .io_open_drain_ena = 0x0, + .io_polarity = 0, + .irq_summary = -1, + }, + [SX150X_EXP4] = { + .gpio_base = SX150X_EXP4_GPIO_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0, + .io_pulldn_ena = 0x0, + .io_open_drain_ena = 0x0, + .io_polarity = 0, + .irq_summary = -1, + }, +}; + +static struct i2c_board_info sx150x_gpio_exp_info[] = { + { + I2C_BOARD_INFO("sx1509q", 0x70), + .platform_data = &mpq8064_sx150x_pdata[SX150X_EXP1], + }, + { + I2C_BOARD_INFO("sx1508q", 0x23), + .platform_data = &mpq8064_sx150x_pdata[SX150X_EXP2], + }, + { + I2C_BOARD_INFO("sx1508q", 0x22), + .platform_data = &mpq8064_sx150x_pdata[SX150X_EXP3], + }, + { + I2C_BOARD_INFO("sx1509q", 0x3E), + .platform_data = &mpq8064_sx150x_pdata[SX150X_EXP4], + }, +}; + +#define MPQ8064_I2C_GSBI5_BUS_ID 5 + +static struct i2c_registry mpq8064_i2c_devices[] __initdata = { + { + I2C_MPQ_CDP, + MPQ8064_I2C_GSBI5_BUS_ID, + sx150x_gpio_exp_info, + ARRAY_SIZE(sx150x_gpio_exp_info), + }, +}; + +static void __init register_i2c_devices(void) +{ + u8 mach_mask = 0; + int i; + +#ifdef CONFIG_MSM_CAMERA + struct i2c_registry apq8064_camera_i2c_devices = { + I2C_SURF | I2C_FFA | I2C_LIQUID | I2C_RUMI, + APQ_8064_GSBI4_QUP_I2C_BUS_ID, + apq8064_camera_board_info.board_info, + apq8064_camera_board_info.num_i2c_board_info, + }; +#endif + /* Build the matching 'supported_machs' bitmask */ + if (machine_is_apq8064_cdp()) + mach_mask = I2C_SURF; + else if (machine_is_apq8064_mtp()) + mach_mask = I2C_FFA; + else if (machine_is_apq8064_liquid()) + mach_mask = I2C_LIQUID; + else if (machine_is_apq8064_rumi3()) + mach_mask = I2C_RUMI; + else if (machine_is_apq8064_sim()) + mach_mask = I2C_SIM; + else if (PLATFORM_IS_MPQ8064()) + mach_mask = I2C_MPQ_CDP; + else + pr_err("unmatched machine ID in register_i2c_devices\n"); + + /* Run the array and install devices as appropriate */ + for (i = 0; i < ARRAY_SIZE(apq8064_i2c_devices); ++i) { + if (apq8064_i2c_devices[i].machs & mach_mask) + i2c_register_board_info(apq8064_i2c_devices[i].bus, + apq8064_i2c_devices[i].info, + apq8064_i2c_devices[i].len); + } +#ifdef CONFIG_MSM_CAMERA + if (apq8064_camera_i2c_devices.machs & mach_mask) + i2c_register_board_info(apq8064_camera_i2c_devices.bus, + apq8064_camera_i2c_devices.info, + apq8064_camera_i2c_devices.len); +#endif + + for (i = 0; i < ARRAY_SIZE(mpq8064_i2c_devices); ++i) { + if (mpq8064_i2c_devices[i].machs & mach_mask) + i2c_register_board_info( + mpq8064_i2c_devices[i].bus, + mpq8064_i2c_devices[i].info, + mpq8064_i2c_devices[i].len); + } +} + +static void enable_ddr3_regulator(void) +{ + static struct regulator *ext_ddr3; + + /* Use MPP7 output state as a flag for PCDDR3 presence. */ + if (gpio_get_value_cansleep(PM8921_MPP_PM_TO_SYS(7)) > 0) { + ext_ddr3 = regulator_get(NULL, "ext_ddr3"); + if (IS_ERR(ext_ddr3) || ext_ddr3 == NULL) + pr_err("Could not get MPP7 regulator\n"); + else + regulator_enable(ext_ddr3); + } +} + +static void enable_avc_i2c_bus(void) +{ + int avc_i2c_en_mpp = PM8921_MPP_PM_TO_SYS(8); + int rc; + + rc = gpio_request(avc_i2c_en_mpp, "avc_i2c_en"); + if (rc) + pr_err("request for avc_i2c_en mpp failed," + "rc=%d\n", rc); + else + gpio_set_value_cansleep(avc_i2c_en_mpp, 1); +} + +static void __init apq8064_common_init(void) +{ + msm_tsens_early_init(&apq_tsens_pdata); + if (socinfo_init() < 0) + pr_err("socinfo_init() failed!\n"); + BUG_ON(msm_rpm_init(&apq8064_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + regulator_suppress_info_printing(); + platform_device_register(&apq8064_device_rpm_regulator); + if (msm_xo_init()) + pr_err("Failed to initialize XO votes\n"); + msm_clock_init(&apq8064_clock_init_data); + apq8064_init_gpiomux(); + apq8064_i2c_init(); + register_i2c_devices(); + + apq8064_device_qup_spi_gsbi5.dev.platform_data = + &apq8064_qup_spi_gsbi5_pdata; + apq8064_init_pmic(); + if (machine_is_apq8064_liquid()) + msm_otg_pdata.mhl_enable = true; + + android_usb_pdata.swfi_latency = + msm_rpmrs_levels[0].latency_us; + + apq8064_device_otg.dev.platform_data = &msm_otg_pdata; + apq8064_ehci_host_init(); + apq8064_init_buses(); + platform_add_devices(common_devices, ARRAY_SIZE(common_devices)); + if (!(machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() || + machine_is_mpq8064_dtv())) + platform_add_devices(common_not_mpq_devices, + ARRAY_SIZE(common_not_mpq_devices)); + enable_ddr3_regulator(); + if (machine_is_apq8064_mtp()) { + apq8064_device_hsic_host.dev.platform_data = &msm_hsic_pdata; + device_initialize(&apq8064_device_hsic_host.dev); + } + apq8064_pm8xxx_gpio_mpp_init(); + apq8064_init_mmc(); + + if (machine_is_apq8064_mtp()) { + mdm_8064_device.dev.platform_data = &mdm_platform_data; + platform_device_register(&mdm_8064_device); + } + platform_device_register(&apq8064_slim_ctrl); + slim_register_board_info(apq8064_slim_devices, + ARRAY_SIZE(apq8064_slim_devices)); + apq8064_init_dsps(); + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + acpuclk_init(&acpuclk_8064_soc_data); + msm_spm_l2_init(msm_spm_l2_data); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data); + apq8064_epm_adc_init(); +} + +static void __init apq8064_allocate_memory_regions(void) +{ + apq8064_allocate_fb_region(); +} + +static void __init apq8064_sim_init(void) +{ + struct msm_watchdog_pdata *wdog_pdata = (struct msm_watchdog_pdata *) + &msm8064_device_watchdog.dev.platform_data; + + wdog_pdata->bark_time = 15000; + apq8064_common_init(); + platform_add_devices(sim_devices, ARRAY_SIZE(sim_devices)); +} + +static void __init apq8064_rumi3_init(void) +{ + apq8064_common_init(); + ethernet_init(); + platform_add_devices(rumi3_devices, ARRAY_SIZE(rumi3_devices)); + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); +} + +static void __init apq8064_cdp_init(void) +{ + if (meminfo_init(SYS_MEMORY, SZ_256M) < 0) + pr_err("meminfo_init() failed!\n"); + apq8064_common_init(); + if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() || + machine_is_mpq8064_dtv()) { + enable_avc_i2c_bus(); + platform_add_devices(mpq_devices, ARRAY_SIZE(mpq_devices)); + mpq8064_pcie_init(); + } else { + ethernet_init(); + platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices)); + spi_register_board_info(spi_board_info, + ARRAY_SIZE(spi_board_info)); + } + apq8064_init_fb(); + apq8064_init_gpu(); + platform_add_devices(apq8064_footswitch, apq8064_num_footswitch); +#ifdef CONFIG_MSM_CAMERA + apq8064_init_cam(); +#endif + + if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid()) + platform_device_register(&cdp_kp_pdev); + + if (machine_is_apq8064_mtp()) + platform_device_register(&mtp_kp_pdev); + + change_memory_power = &apq8064_change_memory_power; + + if (machine_is_mpq8064_cdp()) { + platform_device_register(&mpq_gpio_keys_pdev); + platform_device_register(&mpq_keypad_device); + } +} + +MACHINE_START(APQ8064_SIM, "QCT APQ8064 SIMULATOR") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_sim_init, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(APQ8064_RUMI3, "QCT APQ8064 RUMI3") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_rumi3_init, + .init_early = apq8064_allocate_memory_regions, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(APQ8064_CDP, "QCT APQ8064 CDP") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_cdp_init, + .init_early = apq8064_allocate_memory_regions, + .init_very_early = apq8064_early_reserve, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(APQ8064_MTP, "QCT APQ8064 MTP") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_cdp_init, + .init_early = apq8064_allocate_memory_regions, + .init_very_early = apq8064_early_reserve, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(APQ8064_LIQUID, "QCT APQ8064 LIQUID") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_cdp_init, + .init_early = apq8064_allocate_memory_regions, + .init_very_early = apq8064_early_reserve, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MPQ8064_CDP, "QCT MPQ8064 CDP") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_cdp_init, + .init_early = apq8064_allocate_memory_regions, + .init_very_early = apq8064_early_reserve, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MPQ8064_HRD, "QCT MPQ8064 HRD") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_cdp_init, + .init_early = apq8064_allocate_memory_regions, + .init_very_early = apq8064_early_reserve, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MPQ8064_DTV, "QCT MPQ8064 DTV") + .map_io = apq8064_map_io, + .reserve = apq8064_reserve, + .init_irq = apq8064_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = apq8064_cdp_init, + .init_early = apq8064_allocate_memory_regions, + .init_very_early = apq8064_early_reserve, + .restart = msm_restart, +MACHINE_END + diff --git a/arch/arm/mach-msm/board-8064.h b/arch/arm/mach-msm/board-8064.h new file mode 100644 index 00000000000..a241ab3e9fc --- /dev/null +++ b/arch/arm/mach-msm/board-8064.h @@ -0,0 +1,147 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_APQ8064_H +#define __ARCH_ARM_MACH_MSM_BOARD_APQ8064_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Macros assume PMIC GPIOs and MPPs start at 1 */ +#define PM8921_GPIO_BASE NR_GPIO_IRQS +#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE) +#define PM8921_MPP_BASE (PM8921_GPIO_BASE + PM8921_NR_GPIOS) +#define PM8921_MPP_PM_TO_SYS(pm_mpp) (pm_mpp - 1 + PM8921_MPP_BASE) +#define PM8921_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) + +#define PM8821_MPP_BASE (PM8921_MPP_BASE + PM8921_NR_MPPS) +#define PM8821_MPP_PM_TO_SYS(pm_mpp) (pm_mpp - 1 + PM8821_MPP_BASE) +#define PM8821_IRQ_BASE (PM8921_IRQ_BASE + PM8921_NR_IRQS) + +#define TABLA_INTERRUPT_BASE (PM8821_IRQ_BASE + PM8821_NR_IRQS) + +extern struct pm8xxx_regulator_platform_data + msm8064_pm8921_regulator_pdata[] __devinitdata; + +extern int msm8064_pm8921_regulator_pdata_len __devinitdata; + +#define GPIO_VREG_ID_EXT_5V 0 +#define GPIO_VREG_ID_EXT_3P3V 1 +#define GPIO_VREG_ID_EXT_TS_SW 2 +#define GPIO_VREG_ID_EXT_MPP8 3 + +#define GPIO_VREG_ID_AVC_1P2V 0 +#define GPIO_VREG_ID_AVC_1P8V 1 +#define GPIO_VREG_ID_AVC_2P2V 2 +#define GPIO_VREG_ID_AVC_5V 3 +#define GPIO_VREG_ID_AVC_3P3V 4 + +#define APQ8064_EXT_3P3V_REG_EN_GPIO 77 + +extern struct gpio_regulator_platform_data + apq8064_gpio_regulator_pdata[] __devinitdata; + +extern struct gpio_regulator_platform_data + mpq8064_gpio_regulator_pdata[] __devinitdata; + +extern struct rpm_regulator_platform_data + apq8064_rpm_regulator_pdata __devinitdata; + +extern struct regulator_init_data msm8064_saw_regulator_pdata_8921_s5; +extern struct regulator_init_data msm8064_saw_regulator_pdata_8921_s6; +extern struct regulator_init_data msm8064_saw_regulator_pdata_8821_s0; +extern struct regulator_init_data msm8064_saw_regulator_pdata_8821_s1; + +struct mmc_platform_data; +int __init apq8064_add_sdcc(unsigned int controller, + struct mmc_platform_data *plat); + +void apq8064_init_mmc(void); +void apq8064_init_gpiomux(void); +void apq8064_init_pmic(void); + +extern struct msm_camera_board_info apq8064_camera_board_info; +void apq8064_init_cam(void); + +#define APQ_8064_GSBI1_QUP_I2C_BUS_ID 0 +#define APQ_8064_GSBI3_QUP_I2C_BUS_ID 3 +#define APQ_8064_GSBI4_QUP_I2C_BUS_ID 4 +#define APQ_8064_GSBI5_QUP_I2C_BUS_ID 5 + +unsigned char apq8064_hdmi_as_primary_selected(void); +void apq8064_init_fb(void); +void apq8064_allocate_fb_region(void); +void apq8064_mdp_writeback(struct memtype_reserve *reserve_table); +void __init apq8064_set_display_params(char *prim_panel, char *ext_panel); + +void apq8064_init_gpu(void); +void apq8064_pm8xxx_gpio_mpp_init(void); + +#define PLATFORM_IS_MPQ8064() \ + (machine_is_mpq8064_hrd() || \ + machine_is_mpq8064_dtv() || \ + machine_is_mpq8064_cdp() \ + ) + + +#define GPIO_EXPANDER_IRQ_BASE (TABLA_INTERRUPT_BASE + \ + NR_TABLA_IRQS) +#define GPIO_EXPANDER_GPIO_BASE (PM8821_MPP_BASE + PM8821_NR_MPPS) + +#define GPIO_EPM_EXPANDER_BASE GPIO_EXPANDER_GPIO_BASE +#define SX150X_EPM_NR_GPIOS 16 +#define SX150X_EPM_NR_IRQS 8 + +#define SX150X_EXP1_GPIO_BASE (GPIO_EPM_EXPANDER_BASE + \ + SX150X_EPM_NR_GPIOS) +#define SX150X_EXP1_IRQ_BASE (GPIO_EXPANDER_IRQ_BASE + \ + SX150X_EPM_NR_IRQS) +#define SX150X_EXP1_NR_IRQS 16 +#define SX150X_EXP1_NR_GPIOS 16 + +#define SX150X_EXP2_GPIO_BASE (SX150X_EXP1_GPIO_BASE + \ + SX150X_EXP1_NR_GPIOS) +#define SX150X_EXP2_IRQ_BASE (SX150X_EXP1_IRQ_BASE + SX150X_EXP1_NR_IRQS) +#define SX150X_EXP2_NR_IRQS 8 +#define SX150X_EXP2_NR_GPIOS 8 + +#define SX150X_EXP3_GPIO_BASE (SX150X_EXP2_GPIO_BASE + \ + SX150X_EXP2_NR_GPIOS) +#define SX150X_EXP3_IRQ_BASE (SX150X_EXP2_IRQ_BASE + SX150X_EXP2_NR_IRQS) +#define SX150X_EXP3_NR_IRQS 8 +#define SX150X_EXP3_NR_GPIOS 8 + +#define SX150X_EXP4_GPIO_BASE (SX150X_EXP3_GPIO_BASE + \ + SX150X_EXP3_NR_GPIOS) +#define SX150X_EXP4_IRQ_BASE (SX150X_EXP3_IRQ_BASE + SX150X_EXP3_NR_IRQS) +#define SX150X_EXP4_NR_IRQS 16 +#define SX150X_EXP4_NR_GPIOS 16 + +#define SX150X_GPIO(_expander, _pin) (SX150X_EXP##_expander##_GPIO_BASE + _pin) + +enum { + SX150X_EPM, + SX150X_EXP1, + SX150X_EXP2, + SX150X_EXP3, + SX150X_EXP4, +}; + +extern struct msm_rtb_platform_data apq8064_rtb_pdata; +extern struct msm_cache_dump_platform_data apq8064_cache_dump_pdata; +#endif diff --git a/arch/arm/mach-msm/board-8930-camera.c b/arch/arm/mach-msm/board-8930-camera.c new file mode 100644 index 00000000000..e7f0e68a8ee --- /dev/null +++ b/arch/arm/mach-msm/board-8930-camera.c @@ -0,0 +1,636 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8930.h" + +#ifdef CONFIG_MSM_CAMERA + +#if (defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)) && \ + defined(CONFIG_I2C) + +static struct i2c_board_info cam_expander_i2c_info[] = { + { + I2C_BOARD_INFO("sx1508q", 0x22), + .platform_data = &msm8930_sx150x_data[SX150X_CAM] + }, +}; + +static struct msm_cam_expander_info cam_expander_info[] = { + { + cam_expander_i2c_info, + MSM_8930_GSBI4_QUP_I2C_BUS_ID, + }, +}; +#endif + +static struct gpiomux_setting cam_settings[] = { + { + .func = GPIOMUX_FUNC_GPIO, /*suspend*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + + { + .func = GPIOMUX_FUNC_1, /*active 1*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_GPIO, /*active 2*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_1, /*active 3*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_5, /*active 4*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_6, /*active 5*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_2, /*active 6*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_3, /*active 7*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_GPIO, /*i2c suspend*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, + }, + { + .func = GPIOMUX_FUNC_2, /*active 9*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + +}; + + +static struct msm_gpiomux_config msm8930_cam_common_configs[] = { + { + .gpio = 2, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 3, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[1], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 4, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[9], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 5, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[1], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 76, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 107, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 54, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, +}; + +static struct msm_gpiomux_config msm8930_cam_2d_configs[] = { + { + .gpio = 18, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 19, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 20, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 21, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, +}; + +#define VFE_CAMIF_TIMER1_GPIO 2 +#define VFE_CAMIF_TIMER2_GPIO 3 +#define VFE_CAMIF_TIMER3_GPIO_INT 4 +static struct msm_camera_sensor_strobe_flash_data strobe_flash_xenon = { + .flash_trigger = VFE_CAMIF_TIMER2_GPIO, + .flash_charge = VFE_CAMIF_TIMER1_GPIO, + .flash_charge_done = VFE_CAMIF_TIMER3_GPIO_INT, + .flash_recharge_duration = 50000, + .irq = MSM_GPIO_TO_INT(VFE_CAMIF_TIMER3_GPIO_INT), +}; + +#ifdef CONFIG_MSM_CAMERA_FLASH +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT, + ._fsrc.ext_driver_src.led_en = VFE_CAMIF_TIMER1_GPIO, + ._fsrc.ext_driver_src.led_flash_en = VFE_CAMIF_TIMER2_GPIO, + ._fsrc.ext_driver_src.flash_id = MAM_CAMERA_EXT_LED_FLASH_TPS61310, +}; +#endif + +static struct msm_bus_vectors cam_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 27648000, + .ib = 110592000, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 140451840, + .ib = 561807360, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 206807040, + .ib = 488816640, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 274423680, + .ib = 1097694720, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 540000000, + .ib = 1350000000, + }, +}; + +static struct msm_bus_vectors cam_zsl_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 302071680, + .ib = 1208286720, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 540000000, + .ib = 1350000000, + }, +}; + +static struct msm_bus_paths cam_bus_client_config[] = { + { + ARRAY_SIZE(cam_init_vectors), + cam_init_vectors, + }, + { + ARRAY_SIZE(cam_preview_vectors), + cam_preview_vectors, + }, + { + ARRAY_SIZE(cam_video_vectors), + cam_video_vectors, + }, + { + ARRAY_SIZE(cam_snapshot_vectors), + cam_snapshot_vectors, + }, + { + ARRAY_SIZE(cam_zsl_vectors), + cam_zsl_vectors, + }, +}; + +static struct msm_bus_scale_pdata cam_bus_client_pdata = { + cam_bus_client_config, + ARRAY_SIZE(cam_bus_client_config), + .name = "msm_camera", +}; + +static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = { + { + .csid_core = 0, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, + { + .csid_core = 1, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, +}; + +static struct camera_vreg_t msm_8930_back_cam_vreg[] = { + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +static struct camera_vreg_t msm_8930_front_cam_vreg[] = { + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, +}; + +static struct gpio msm8930_common_cam_gpio[] = { + {20, GPIOF_DIR_IN, "CAMIF_I2C_DATA"}, + {21, GPIOF_DIR_IN, "CAMIF_I2C_CLK"}, +}; + +static struct gpio msm8930_front_cam_gpio[] = { + {4, GPIOF_DIR_IN, "CAMIF_MCLK"}, + {76, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct gpio msm8930_back_cam_gpio[] = { + {5, GPIOF_DIR_IN, "CAMIF_MCLK"}, + {107, GPIOF_DIR_OUT, "CAM_RESET"}, + {54, GPIOF_DIR_OUT, "CAM_STBY_N"}, +}; + +static struct msm_gpio_set_tbl msm8930_front_cam_gpio_set_tbl[] = { + {76, GPIOF_OUT_INIT_LOW, 1000}, + {76, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_gpio_set_tbl msm8930_back_cam_gpio_set_tbl[] = { + {54, GPIOF_OUT_INIT_LOW, 1000}, + {54, GPIOF_OUT_INIT_HIGH, 4000}, + {107, GPIOF_OUT_INIT_LOW, 1000}, + {107, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_camera_gpio_conf msm_8930_front_cam_gpio_conf = { + .cam_gpiomux_conf_tbl = msm8930_cam_2d_configs, + .cam_gpiomux_conf_tbl_size = ARRAY_SIZE(msm8930_cam_2d_configs), + .cam_gpio_common_tbl = msm8930_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(msm8930_common_cam_gpio), + .cam_gpio_req_tbl = msm8930_front_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(msm8930_front_cam_gpio), + .cam_gpio_set_tbl = msm8930_front_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(msm8930_front_cam_gpio_set_tbl), +}; + +static struct msm_camera_gpio_conf msm_8930_back_cam_gpio_conf = { + .cam_gpiomux_conf_tbl = msm8930_cam_2d_configs, + .cam_gpiomux_conf_tbl_size = ARRAY_SIZE(msm8930_cam_2d_configs), + .cam_gpio_common_tbl = msm8930_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(msm8930_common_cam_gpio), + .cam_gpio_req_tbl = msm8930_back_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(msm8930_back_cam_gpio), + .cam_gpio_set_tbl = msm8930_back_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(msm8930_back_cam_gpio_set_tbl), +}; + +static struct i2c_board_info msm_act_main_cam_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x11), +}; + +static struct msm_actuator_info msm_act_main_cam_0_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_0, + .bus_id = MSM_8930_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + +static struct msm_camera_sensor_flash_data flash_imx074 = { + .flash_type = MSM_CAMERA_FLASH_LED, +#ifdef CONFIG_MSM_CAMERA_FLASH + .flash_src = &msm_flash_src +#endif +}; + +static struct msm_camera_csi_lane_params imx074_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_imx074 = { + .mount_angle = 90, + .cam_vreg = msm_8930_back_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8930_back_cam_vreg), + .gpio_conf = &msm_8930_back_cam_gpio_conf, + .csi_lane_params = &imx074_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx074_data = { + .sensor_name = "imx074", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_imx074, + .strobe_flash_data = &strobe_flash_xenon, + .sensor_platform_info = &sensor_board_info_imx074, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_0_info, +}; + +static struct camera_vreg_t msm_8930_mt9m114_vreg[] = { + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +static struct msm_camera_sensor_flash_data flash_mt9m114 = { + .flash_type = MSM_CAMERA_FLASH_NONE +}; + +static struct msm_camera_csi_lane_params mt9m114_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0x1, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_mt9m114 = { + .mount_angle = 90, + .cam_vreg = msm_8930_mt9m114_vreg, + .num_vreg = ARRAY_SIZE(msm_8930_mt9m114_vreg), + .gpio_conf = &msm_8930_front_cam_gpio_conf, + .csi_lane_params = &mt9m114_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9m114_data = { + .sensor_name = "mt9m114", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_mt9m114, + .sensor_platform_info = &sensor_board_info_mt9m114, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = YUV_SENSOR, +}; + +static struct msm_camera_sensor_flash_data flash_ov2720 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_csi_lane_params ov2720_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0x3, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov2720 = { + .mount_angle = 0, + .cam_vreg = msm_8930_front_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8930_front_cam_vreg), + .gpio_conf = &msm_8930_front_cam_gpio_conf, + .csi_lane_params = &ov2720_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov2720_data = { + .sensor_name = "ov2720", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_ov2720, + .sensor_platform_info = &sensor_board_info_ov2720, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = BAYER_SENSOR, +}; + +static struct camera_vreg_t msm_8930_s5k3l1yx_vreg[] = { + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vaf", REG_LDO, 2800000, 2850000, 300000}, +}; + +static struct msm_camera_sensor_flash_data flash_s5k3l1yx = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_csi_lane_params s5k3l1yx_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_s5k3l1yx = { + .mount_angle = 90, + .cam_vreg = msm_8930_s5k3l1yx_vreg, + .num_vreg = ARRAY_SIZE(msm_8930_s5k3l1yx_vreg), + .gpio_conf = &msm_8930_back_cam_gpio_conf, + .csi_lane_params = &s5k3l1yx_csi_lane_params, +}; + +static struct msm_actuator_info msm_act_main_cam_2_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_2, + .bus_id = MSM_8930_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3l1yx_data = { + .sensor_name = "s5k3l1yx", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_s5k3l1yx, + .sensor_platform_info = &sensor_board_info_s5k3l1yx, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_2_info, +}; + +static struct platform_device msm_camera_server = { + .name = "msm_cam_server", + .id = 0, +}; + +void __init msm8930_init_cam(void) +{ + msm_gpiomux_install(msm8930_cam_common_configs, + ARRAY_SIZE(msm8930_cam_common_configs)); + + if (machine_is_msm8930_cdp()) { + struct msm_camera_sensor_info *s_info; + s_info = &msm_camera_sensor_s5k3l1yx_data; + s_info->sensor_platform_info->mount_angle = 0; + msm_flash_src._fsrc.ext_driver_src.led_en = + GPIO_CAM_GP_LED_EN1; + msm_flash_src._fsrc.ext_driver_src.led_flash_en = + GPIO_CAM_GP_LED_EN2; +#if defined(CONFIG_I2C) && (defined(CONFIG_GPIO_SX150X) || \ + defined(CONFIG_GPIO_SX150X_MODULE)) + msm_flash_src._fsrc.ext_driver_src.expander_info = + cam_expander_info; +#endif + } + + platform_device_register(&msm_camera_server); + platform_device_register(&msm8960_device_csiphy0); + platform_device_register(&msm8960_device_csiphy1); + platform_device_register(&msm8960_device_csid0); + platform_device_register(&msm8960_device_csid1); + platform_device_register(&msm8960_device_ispif); + platform_device_register(&msm8960_device_vfe); + platform_device_register(&msm8960_device_vpe); +} + +#ifdef CONFIG_I2C +struct i2c_board_info msm8930_camera_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("imx074", 0x1A), + .platform_data = &msm_camera_sensor_imx074_data, + }, + { + I2C_BOARD_INFO("ov2720", 0x6C), + .platform_data = &msm_camera_sensor_ov2720_data, + }, + { + I2C_BOARD_INFO("mt9m114", 0x48), + .platform_data = &msm_camera_sensor_mt9m114_data, + }, + { + I2C_BOARD_INFO("s5k3l1yx", 0x20), + .platform_data = &msm_camera_sensor_s5k3l1yx_data, + }, + { + I2C_BOARD_INFO("tps61310", 0x66), + }, +}; + +struct msm_camera_board_info msm8930_camera_board_info = { + .board_info = msm8930_camera_i2c_boardinfo, + .num_i2c_board_info = ARRAY_SIZE(msm8930_camera_i2c_boardinfo), +}; +#endif +#endif diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c new file mode 100644 index 00000000000..e18e40d8481 --- /dev/null +++ b/arch/arm/mach-msm/board-8930-display.c @@ -0,0 +1,791 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-8930.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_PRIM_BUF_SIZE \ + (roundup((1920 * 1088 * 4), 4096) * 3) /* 4 bpp x 3 pages */ +#else +#define MSM_FB_PRIM_BUF_SIZE \ + (roundup((1920 * 1088 * 4), 4096) * 2) /* 4 bpp x 2 pages */ +#endif +/* Note: must be multiple of 4096 */ +#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096) + +#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */ + +#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */ + +#define MDP_VSYNC_GPIO 0 + +#define MIPI_CMD_NOVATEK_QHD_PANEL_NAME "mipi_cmd_novatek_qhd" +#define MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME "mipi_video_novatek_qhd" +#define MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME "mipi_video_toshiba_wsvga" +#define MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME "mipi_video_chimei_wxga" +#define MIPI_VIDEO_SIMULATOR_VGA_PANEL_NAME "mipi_video_simulator_vga" +#define MIPI_CMD_RENESAS_FWVGA_PANEL_NAME "mipi_cmd_renesas_fwvga" +#define HDMI_PANEL_NAME "hdmi_msm" +#define TVOUT_PANEL_NAME "tvout_msm" + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +static int msm_fb_detect_panel(const char *name) +{ + if (!strncmp(name, MIPI_CMD_NOVATEK_QHD_PANEL_NAME, + strnlen(MIPI_CMD_NOVATEK_QHD_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + +#if !defined(CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT) && \ + !defined(CONFIG_FB_MSM_MIPI_PANEL_DETECT) + if (!strncmp(name, MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME, + strnlen(MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_VIDEO_SIMULATOR_VGA_PANEL_NAME, + strnlen(MIPI_VIDEO_SIMULATOR_VGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_CMD_RENESAS_FWVGA_PANEL_NAME, + strnlen(MIPI_CMD_RENESAS_FWVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; +#endif + + if (!strncmp(name, HDMI_PANEL_NAME, + strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, TVOUT_PANEL_NAME, + strnlen(TVOUT_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + pr_warning("%s: not supported '%s'", __func__, name); + return -ENODEV; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev.platform_data = &msm_fb_pdata, +}; + +static bool dsi_power_on; + +/* + * TODO: When physical 8930/PM8038 hardware becomes + * available, replace mipi_dsi_cdp_panel_power with + * appropriate function. + */ +#define DISP_RST_GPIO 58 +static int mipi_dsi_cdp_panel_power(int on) +{ + static struct regulator *reg_l8, *reg_l23, *reg_l2; + int rc; + + pr_debug("%s: state : %d\n", __func__, on); + + if (!dsi_power_on) { + + reg_l8 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vdc"); + if (IS_ERR(reg_l8)) { + pr_err("could not get 8038_l8, rc = %ld\n", + PTR_ERR(reg_l8)); + return -ENODEV; + } + reg_l23 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vddio"); + if (IS_ERR(reg_l23)) { + pr_err("could not get 8038_l23, rc = %ld\n", + PTR_ERR(reg_l23)); + return -ENODEV; + } + reg_l2 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vdda"); + if (IS_ERR(reg_l2)) { + pr_err("could not get 8038_l2, rc = %ld\n", + PTR_ERR(reg_l2)); + return -ENODEV; + } + rc = regulator_set_voltage(reg_l8, 2800000, 3000000); + if (rc) { + pr_err("set_voltage l8 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_voltage(reg_l23, 1800000, 1800000); + if (rc) { + pr_err("set_voltage l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_voltage(reg_l2, 1200000, 1200000); + if (rc) { + pr_err("set_voltage l2 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = gpio_request(DISP_RST_GPIO, "disp_rst_n"); + if (rc) { + pr_err("request gpio DISP_RST_GPIO failed, rc=%d\n", + rc); + gpio_free(DISP_RST_GPIO); + return -ENODEV; + } + dsi_power_on = true; + } + if (on) { + rc = regulator_set_optimum_mode(reg_l8, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l8 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l23, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l2, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_l8); + if (rc) { + pr_err("enable l8 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_enable(reg_l23); + if (rc) { + pr_err("enable l8 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_enable(reg_l2); + if (rc) { + pr_err("enable l2 failed, rc=%d\n", rc); + return -ENODEV; + } + usleep(10000); + gpio_set_value(DISP_RST_GPIO, 1); + usleep(10); + gpio_set_value(DISP_RST_GPIO, 0); + usleep(20); + gpio_set_value(DISP_RST_GPIO, 1); + } else { + + gpio_set_value(DISP_RST_GPIO, 0); + + rc = regulator_disable(reg_l2); + if (rc) { + pr_err("disable reg_l2 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_l8); + if (rc) { + pr_err("disable reg_l8 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_l23); + if (rc) { + pr_err("disable reg_l23 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_set_optimum_mode(reg_l8, 100); + if (rc < 0) { + pr_err("set_optimum_mode l8 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l23, 100); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l2, 100); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + } + return 0; +} + +static int mipi_dsi_panel_power(int on) +{ + pr_debug("%s: on=%d\n", __func__, on); + + return mipi_dsi_cdp_panel_power(on); +} + +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .vsync_gpio = MDP_VSYNC_GPIO, + .dsi_power_save = mipi_dsi_panel_power, +}; + +#ifdef CONFIG_MSM_BUS_SCALING + +static struct msm_bus_vectors mdp_init_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +static struct msm_bus_vectors hdmi_as_primary_vectors[] = { + /* If HDMI is used as primary */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2000000000, + .ib = 2000000000, + }, +}; +static struct msm_bus_paths mdp_bus_scale_usecases[] = { + { + ARRAY_SIZE(mdp_init_vectors), + mdp_init_vectors, + }, + { + ARRAY_SIZE(hdmi_as_primary_vectors), + hdmi_as_primary_vectors, + }, + { + ARRAY_SIZE(hdmi_as_primary_vectors), + hdmi_as_primary_vectors, + }, + { + ARRAY_SIZE(hdmi_as_primary_vectors), + hdmi_as_primary_vectors, + }, + { + ARRAY_SIZE(hdmi_as_primary_vectors), + hdmi_as_primary_vectors, + }, + { + ARRAY_SIZE(hdmi_as_primary_vectors), + hdmi_as_primary_vectors, + }, +}; +#else +static struct msm_bus_vectors mdp_ui_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000 * 2, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_vga_vectors[] = { + /* VGA and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000 * 2, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_720p_vectors[] = { + /* 720p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 230400000 * 2, + .ib = 288000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_1080p_vectors[] = { + /* 1080p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 334080000 * 2, + .ib = 417600000 * 2, + }, +}; + +static struct msm_bus_paths mdp_bus_scale_usecases[] = { + { + ARRAY_SIZE(mdp_init_vectors), + mdp_init_vectors, + }, + { + ARRAY_SIZE(mdp_ui_vectors), + mdp_ui_vectors, + }, + { + ARRAY_SIZE(mdp_ui_vectors), + mdp_ui_vectors, + }, + { + ARRAY_SIZE(mdp_vga_vectors), + mdp_vga_vectors, + }, + { + ARRAY_SIZE(mdp_720p_vectors), + mdp_720p_vectors, + }, + { + ARRAY_SIZE(mdp_1080p_vectors), + mdp_1080p_vectors, + }, +}; +#endif + +static struct msm_bus_scale_pdata mdp_bus_scale_pdata = { + mdp_bus_scale_usecases, + ARRAY_SIZE(mdp_bus_scale_usecases), + .name = "mdp", +}; + +#endif + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +static int mdp_core_clk_rate_table[] = { + 200000000, + 200000000, + 200000000, + 200000000, +}; +#else +static int mdp_core_clk_rate_table[] = { + 85330000, + 128000000, + 160000000, + 200000000, +}; +#endif + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = MDP_VSYNC_GPIO, +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY + .mdp_core_clk_rate = 200000000, +#else + .mdp_core_clk_rate = 85330000, +#endif + .mdp_core_clk_table = mdp_core_clk_rate_table, + .num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table), +#ifdef CONFIG_MSM_BUS_SCALING + .mdp_bus_scale_table = &mdp_bus_scale_pdata, +#endif + .mdp_rev = MDP_REV_42, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .mem_hid = BIT(ION_CP_MM_HEAP_ID), +#else + .mem_hid = MEMTYPE_EBI1, +#endif +}; + +void __init msm8930_mdp_writeback(struct memtype_reserve* reserve_table) +{ + mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE; + mdp_pdata.ov1_wb_size = MSM_FB_OVERLAY1_WRITEBACK_SIZE; +#if defined(CONFIG_ANDROID_PMEM) && !defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov0_wb_size; + reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov1_wb_size; +#endif +} + +#define LPM_CHANNEL0 0 +static int toshiba_gpio[] = {LPM_CHANNEL0}; + +static struct mipi_dsi_panel_platform_data toshiba_pdata = { + .gpio = toshiba_gpio, +}; + +static struct platform_device mipi_dsi_toshiba_panel_device = { + .name = "mipi_toshiba", + .id = 0, + .dev = { + .platform_data = &toshiba_pdata, + } +}; + +#define FPGA_3D_GPIO_CONFIG_ADDR 0xB5 + +static struct mipi_dsi_phy_ctrl dsi_novatek_cmd_mode_phy_db = { + +/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */ + {0x0F, 0x0a, 0x04, 0x00, 0x20}, /* regulator */ + /* timing */ + {0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c, + 0x0c, 0x03, 0x04, 0xa0}, + {0x5f, 0x00, 0x00, 0x10}, /* phy ctrl */ + {0xff, 0x00, 0x06, 0x00}, /* strength */ + /* pll control */ + {0x40, 0xf9, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62, + 0x40, 0x07, 0x03, + 0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01}, +}; + +static struct mipi_dsi_panel_platform_data novatek_pdata = { + .fpga_3d_config_addr = FPGA_3D_GPIO_CONFIG_ADDR, + .fpga_ctrl_mode = FPGA_SPI_INTF, + .phy_ctrl_settings = &dsi_novatek_cmd_mode_phy_db, + .dlane_swap = 0x1, + .enable_wled_bl_ctrl = 0x1, +}; + +static struct platform_device mipi_dsi_novatek_panel_device = { + .name = "mipi_novatek", + .id = 0, + .dev = { + .platform_data = &novatek_pdata, + } +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct resource hdmi_msm_resources[] = { + { + .name = "hdmi_msm_qfprom_addr", + .start = 0x00700000, + .end = 0x007060FF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_hdmi_addr", + .start = 0x04A00000, + .end = 0x04A00FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_irq", + .start = HDMI_IRQ, + .end = HDMI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int hdmi_enable_5v(int on); +static int hdmi_core_power(int on, int show); +static int hdmi_cec_power(int on); + +static struct msm_hdmi_platform_data hdmi_msm_data = { + .irq = HDMI_IRQ, + .enable_5v = hdmi_enable_5v, + .core_power = hdmi_core_power, + .cec_power = hdmi_cec_power, +}; + +static struct platform_device hdmi_msm_device = { + .name = "hdmi_msm", + .id = 0, + .num_resources = ARRAY_SIZE(hdmi_msm_resources), + .resource = hdmi_msm_resources, + .dev.platform_data = &hdmi_msm_data, +}; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +static struct platform_device wfd_panel_device = { + .name = "wfd_panel", + .id = 0, + .dev.platform_data = NULL, +}; + +static struct platform_device wfd_device = { + .name = "msm_wfd", + .id = -1, +}; +#endif + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors dtv_bus_init_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +static struct msm_bus_vectors dtv_bus_def_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2000000000, + .ib = 2000000000, + }, +}; +#else +static struct msm_bus_vectors dtv_bus_def_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 566092800 * 2, + .ib = 707616000 * 2, + }, +}; +#endif + +static struct msm_bus_paths dtv_bus_scale_usecases[] = { + { + ARRAY_SIZE(dtv_bus_init_vectors), + dtv_bus_init_vectors, + }, + { + ARRAY_SIZE(dtv_bus_def_vectors), + dtv_bus_def_vectors, + }, +}; +static struct msm_bus_scale_pdata dtv_bus_scale_pdata = { + dtv_bus_scale_usecases, + ARRAY_SIZE(dtv_bus_scale_usecases), + .name = "dtv", +}; + +static struct lcdc_platform_data dtv_pdata = { + .bus_scale_table = &dtv_bus_scale_pdata, +}; +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static int hdmi_enable_5v(int on) +{ + static struct regulator *reg_ext_5v; /* HDMI_5V */ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_ext_5v) { + reg_ext_5v = regulator_get(&hdmi_msm_device.dev, "hdmi_mvs"); + if (IS_ERR(reg_ext_5v)) { + pr_err("'%s' regulator not found, rc=%ld\n", + "hdmi_mvs", IS_ERR(reg_ext_5v)); + reg_ext_5v = NULL; + return -ENODEV; + } + } + + if (on) { + rc = regulator_enable(reg_ext_5v); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "reg_ext_5v", rc); + return rc; + } + pr_debug("%s(on): success\n", __func__); + } else { + rc = regulator_disable(reg_ext_5v); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "reg_ext_5v", rc); + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +} + +static int hdmi_core_power(int on, int show) +{ + /* Both HDMI "avdd" and "vcc" are powered by 8038_l23 regulator */ + static struct regulator *reg_8038_l23; + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_8038_l23) { + reg_8038_l23 = regulator_get(&hdmi_msm_device.dev, "hdmi_avdd"); + if (IS_ERR(reg_8038_l23)) { + pr_err("could not get reg_8038_l23, rc = %ld\n", + PTR_ERR(reg_8038_l23)); + return -ENODEV; + } + rc = regulator_set_voltage(reg_8038_l23, 1800000, 1800000); + if (rc) { + pr_err("set_voltage failed for 8921_l23, rc=%d\n", rc); + return -EINVAL; + } + } + + if (on) { + rc = regulator_set_optimum_mode(reg_8038_l23, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_8038_l23); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "hdmi_avdd", rc); + return rc; + } + rc = gpio_request(100, "HDMI_DDC_CLK"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_CLK", 100, rc); + goto error1; + } + rc = gpio_request(101, "HDMI_DDC_DATA"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_DATA", 101, rc); + goto error2; + } + rc = gpio_request(102, "HDMI_HPD"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_HPD", 102, rc); + goto error3; + } + pr_debug("%s(on): success\n", __func__); + } else { + gpio_free(100); + gpio_free(101); + gpio_free(102); + + rc = regulator_disable(reg_8038_l23); + if (rc) { + pr_err("disable reg_8038_l23 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_set_optimum_mode(reg_8038_l23, 100); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; + +error3: + gpio_free(101); +error2: + gpio_free(100); +error1: + regulator_disable(reg_8038_l23); + return rc; +} + +static int hdmi_cec_power(int on) +{ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (on) { + rc = gpio_request(99, "HDMI_CEC_VAR"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_CEC_VAR", 99, rc); + goto error; + } + pr_debug("%s(on): success\n", __func__); + } else { + gpio_free(99); + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +error: + return rc; +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ + +void __init msm8930_init_fb(void) +{ + platform_device_register(&msm_fb_device); + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + platform_device_register(&wfd_panel_device); + platform_device_register(&wfd_device); +#endif + + platform_device_register(&mipi_dsi_novatek_panel_device); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + platform_device_register(&hdmi_msm_device); +#endif + + platform_device_register(&mipi_dsi_toshiba_panel_device); + + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata); +#ifdef CONFIG_MSM_BUS_SCALING + msm_fb_register_device("dtv", &dtv_pdata); +#endif +} + +void __init msm8930_allocate_fb_region(void) +{ + void *addr; + unsigned long size; + + size = MSM_FB_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); +} diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c new file mode 100644 index 00000000000..000f080a0cd --- /dev/null +++ b/arch/arm/mach-msm/board-8930-gpiomux.c @@ -0,0 +1,711 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "devices.h" +#include "board-8930.h" + +/* The SPI configurations apply to GSBI 1*/ +static struct gpiomux_setting spi_active = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting spi_suspended_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting gsbi3_suspended_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting gsbi3_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi5 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi9 = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi10 = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi12 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cdc_mclk = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting audio_auxpcm[] = { + /* Suspended state */ + { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + /* Active state */ + { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, +}; + +static struct gpiomux_setting audio_mbhc = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting audio_spkr_boost = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static struct gpiomux_setting gpio_eth_config = { + .pull = GPIOMUX_PULL_NONE, + .drv = GPIOMUX_DRV_8MA, + .func = GPIOMUX_FUNC_GPIO, +}; +#endif + +static struct gpiomux_setting slimbus = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting wcnss_5wire_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting wcnss_5wire_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting atmel_resout_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting atmel_resout_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting atmel_ldo_en_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting atmel_ldo_en_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting atmel_int_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting atmel_int_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; +#ifdef MSM8930_PHASE_2 +static struct gpiomux_setting hsusb_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_OUT_LOW, +}; +static struct msm_gpiomux_config msm8930_hsusb_configs[] = { + { + .gpio = 63, /* HSUSB_EXTERNAL_5V_LDO_EN */ + .settings = { + [GPIOMUX_SUSPENDED] = &hsusb_sus_cfg, + }, + }, + { + .gpio = 97, /* HSUSB_5V_EN */ + .settings = { + [GPIOMUX_SUSPENDED] = &hsusb_sus_cfg, + }, + }, +}; +#endif + +static struct gpiomux_setting hap_lvl_shft_suspended_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting hap_lvl_shft_active_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ap2mdm_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_status_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_errfatal_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ap2mdm_kpdpwr_n_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdp_vsync_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdp_vsync_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct gpiomux_setting hdmi_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting hdmi_active_1_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting hdmi_active_2_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; +#endif + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static struct msm_gpiomux_config msm8960_ethernet_configs[] = { + { + .gpio = 90, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_eth_config, + } + }, + { + .gpio = 89, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_eth_config, + } + }, +}; +#endif + +static struct msm_gpiomux_config msm8960_gsbi_configs[] __initdata = { + { + .gpio = 6, /* GSBI1 QUP SPI_DATA_MOSI */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 7, /* GSBI1 QUP SPI_DATA_MISO */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 8, /* GSBI1 QUP SPI_CS_N */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 9, /* GSBI1 QUP SPI_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 16, /* GSBI3 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi3_active_cfg, + }, + }, + { + .gpio = 17, /* GSBI3 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi3_active_cfg, + }, + }, + { + .gpio = 22, /* GSBI5 UART2 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi5, + }, + }, + { + .gpio = 23, /* GSBI5 UART2 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi5, + }, + }, + { + .gpio = 44, /* GSBI12 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi12, + }, + }, + { + .gpio = 95, /* GSBI9 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi9, + }, + }, + { + .gpio = 96, /* GSBI12 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi9, + }, + }, + { + .gpio = 45, /* GSBI12 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi12, + }, + }, + { + .gpio = 73, /* GSBI10 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi10, + }, + }, + { + .gpio = 74, /* GSBI10 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi10, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_slimbus_config[] __initdata = { + { + .gpio = 60, /* slimbus data */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, + { + .gpio = 61, /* slimbus clk */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_audio_codec_configs[] __initdata = { + { + .gpio = 59, + .settings = { + [GPIOMUX_SUSPENDED] = &cdc_mclk, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_audio_mbhc_configs[] __initdata = { + { + .gpio = 37, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_mbhc, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_audio_spkr_configs[] __initdata = { + { + .gpio = 15, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_spkr_boost, + }, + }, +}; + + +static struct msm_gpiomux_config msm8960_audio_auxpcm_configs[] __initdata = { + { + .gpio = 63, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, + { + .gpio = 64, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, + { + .gpio = 65, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, + { + .gpio = 66, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, +}; + +static struct msm_gpiomux_config wcnss_5wire_interface[] = { + { + .gpio = 84, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 85, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 86, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 87, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 88, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_atmel_configs[] __initdata = { + { /* TS INTERRUPT */ + .gpio = 11, + .settings = { + [GPIOMUX_ACTIVE] = &atmel_int_act_cfg, + [GPIOMUX_SUSPENDED] = &atmel_int_sus_cfg, + }, + }, + { /* TS LDO ENABLE */ + .gpio = 50, + .settings = { + [GPIOMUX_ACTIVE] = &atmel_ldo_en_act_cfg, + [GPIOMUX_SUSPENDED] = &atmel_ldo_en_sus_cfg, + }, + }, + { /* TS RESOUT */ + .gpio = 52, + .settings = { + [GPIOMUX_ACTIVE] = &atmel_resout_act_cfg, + [GPIOMUX_SUSPENDED] = &atmel_resout_sus_cfg, + }, + }, +}; + +static struct msm_gpiomux_config hap_lvl_shft_config[] __initdata = { + { + .gpio = 47, + .settings = { + [GPIOMUX_SUSPENDED] = &hap_lvl_shft_suspended_config, + [GPIOMUX_ACTIVE] = &hap_lvl_shft_active_config, + }, + }, +}; + +static struct msm_gpiomux_config mdm_configs[] __initdata = { + /* AP2MDM_STATUS */ + { + .gpio = 94, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* MDM2AP_STATUS */ + { + .gpio = 69, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_status_cfg, + } + }, + /* MDM2AP_ERRFATAL */ + { + .gpio = 70, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_errfatal_cfg, + } + }, + /* AP2MDM_ERRFATAL */ + { + .gpio = 95, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* AP2MDM_KPDPWR_N */ + { + .gpio = 81, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_kpdpwr_n_cfg, + } + }, + /* AP2MDM_PMIC_RESET_N */ + { + .gpio = 80, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_kpdpwr_n_cfg, + } + } +}; + +static struct msm_gpiomux_config msm8960_mdp_vsync_configs[] __initdata = { + { + .gpio = 0, + .settings = { + [GPIOMUX_ACTIVE] = &mdp_vsync_active_cfg, + [GPIOMUX_SUSPENDED] = &mdp_vsync_suspend_cfg, + }, + } +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct msm_gpiomux_config msm8960_hdmi_configs[] __initdata = { + { + .gpio = 99, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 100, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 101, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 102, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_2_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, +}; +#endif + +static struct gpiomux_setting haptics_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; +static struct gpiomux_setting haptics_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct msm_gpiomux_config msm8930_haptics_configs[] __initdata = { + { + .gpio = 77, + .settings = { + [GPIOMUX_ACTIVE] = &haptics_active_cfg, + [GPIOMUX_SUSPENDED] = &haptics_suspend_cfg, + }, + }, + { + .gpio = 78, + .settings = { + [GPIOMUX_ACTIVE] = &haptics_active_cfg, + [GPIOMUX_SUSPENDED] = &haptics_suspend_cfg, + }, + }, +}; + +static struct gpiomux_setting sd_det_line = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct msm_gpiomux_config msm8930_sd_det_config[] __initdata = { + { + .gpio = 94, /* SD Card Detect Line */ + .settings = { + [GPIOMUX_SUSPENDED] = &sd_det_line, + [GPIOMUX_ACTIVE] = &sd_det_line, + }, + }, +}; + +int __init msm8930_init_gpiomux(void) +{ + int rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc); + return rc; + } + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) + msm_gpiomux_install(msm8960_ethernet_configs, + ARRAY_SIZE(msm8960_ethernet_configs)); +#endif + + msm_gpiomux_install(msm8960_gsbi_configs, + ARRAY_SIZE(msm8960_gsbi_configs)); + + msm_gpiomux_install(msm8960_atmel_configs, + ARRAY_SIZE(msm8960_atmel_configs)); + + msm_gpiomux_install(msm8960_slimbus_config, + ARRAY_SIZE(msm8960_slimbus_config)); + + msm_gpiomux_install(msm8960_audio_codec_configs, + ARRAY_SIZE(msm8960_audio_codec_configs)); + + msm_gpiomux_install(msm8960_audio_mbhc_configs, + ARRAY_SIZE(msm8960_audio_mbhc_configs)); + + msm_gpiomux_install(msm8960_audio_spkr_configs, + ARRAY_SIZE(msm8960_audio_spkr_configs)); + + msm_gpiomux_install(msm8960_audio_auxpcm_configs, + ARRAY_SIZE(msm8960_audio_auxpcm_configs)); + + msm_gpiomux_install(wcnss_5wire_interface, + ARRAY_SIZE(wcnss_5wire_interface)); + + if (machine_is_msm8930_mtp() || machine_is_msm8930_fluid() || + machine_is_msm8930_cdp()) { + msm_gpiomux_install(hap_lvl_shft_config, + ARRAY_SIZE(hap_lvl_shft_config)); +#ifdef MSM8930_PHASE_2 + msm_gpiomux_install(msm8930_hsusb_configs, + ARRAY_SIZE(msm8930_hsusb_configs)); +#endif + } + + if (PLATFORM_IS_CHARM25()) + msm_gpiomux_install(mdm_configs, + ARRAY_SIZE(mdm_configs)); + + if (machine_is_msm8930_cdp() || machine_is_msm8930_mtp() + || machine_is_msm8930_fluid()) + msm_gpiomux_install(msm8930_haptics_configs, + ARRAY_SIZE(msm8930_haptics_configs)); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + msm_gpiomux_install(msm8960_hdmi_configs, + ARRAY_SIZE(msm8960_hdmi_configs)); +#endif + + msm_gpiomux_install(msm8960_mdp_vsync_configs, + ARRAY_SIZE(msm8960_mdp_vsync_configs)); + + msm_gpiomux_install(msm8930_sd_det_config, + ARRAY_SIZE(msm8930_sd_det_config)); + + return 0; +} diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c new file mode 100644 index 00000000000..e23b76c12c4 --- /dev/null +++ b/arch/arm/mach-msm/board-8930-gpu.c @@ -0,0 +1,163 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-8930.h" + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors grp3d_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp3d_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2000), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(3200), + }, +}; + +static struct msm_bus_vectors grp3d_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(4264), + }, +}; + +static struct msm_bus_paths grp3d_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp3d_init_vectors), + grp3d_init_vectors, + }, + { + ARRAY_SIZE(grp3d_low_vectors), + grp3d_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_vectors), + grp3d_nominal_vectors, + }, + { + ARRAY_SIZE(grp3d_max_vectors), + grp3d_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata grp3d_bus_scale_pdata = { + grp3d_bus_scale_usecases, + ARRAY_SIZE(grp3d_bus_scale_usecases), + .name = "grp3d", +}; +#endif + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0x04300000, /* GFX3D address */ + .end = 0x0431ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = GFX3D_IRQ, + .end = GFX3D_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct kgsl_iommu_ctx kgsl_3d0_iommu0_ctxs[] = { + { "gfx3d_user", 0 }, + { "gfx3d_priv", 1 }, +}; + +static struct kgsl_device_iommu_data kgsl_3d0_iommu_data[] = { + { + .iommu_ctxs = kgsl_3d0_iommu0_ctxs, + .iommu_ctx_count = ARRAY_SIZE(kgsl_3d0_iommu0_ctxs), + .physstart = 0x07C00000, + .physend = 0x07C00000 + SZ_1M - 1, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 400000000, + .bus_freq = 3, + .io_fraction = 0, + }, + { + .gpu_freq = 320000000, + .bus_freq = 2, + .io_fraction = 33, + }, + { + .gpu_freq = 192000000, + .bus_freq = 1, + .io_fraction = 100, + }, + { + .gpu_freq = 27000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = 4, + .set_grp_async = NULL, + .idle_timeout = HZ/12, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp3d_bus_scale_pdata, +#endif + .iommu_data = kgsl_3d0_iommu_data, + .iommu_count = ARRAY_SIZE(kgsl_3d0_iommu_data), +}; + +static struct platform_device device_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +void __init msm8930_init_gpu(void) +{ + platform_device_register(&device_kgsl_3d0); +} diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c new file mode 100644 index 00000000000..cf7a829ce1a --- /dev/null +++ b/arch/arm/mach-msm/board-8930-pmic.c @@ -0,0 +1,380 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8930.h" + +struct pm8xxx_gpio_init { + unsigned gpio; + struct pm_gpio config; +}; + +struct pm8xxx_mpp_init { + unsigned mpp; + struct pm8xxx_mpp_config_data config; +}; + +#define PM8XXX_GPIO_INIT(_gpio, _dir, _buf, _val, _pull, _vin, _out_strength, \ + _func, _inv, _disable) \ +{ \ + .gpio = PM8038_GPIO_PM_TO_SYS(_gpio), \ + .config = { \ + .direction = _dir, \ + .output_buffer = _buf, \ + .output_value = _val, \ + .pull = _pull, \ + .vin_sel = _vin, \ + .out_strength = _out_strength, \ + .function = _func, \ + .inv_int_pol = _inv, \ + .disable_pin = _disable, \ + } \ +} + +#define PM8XXX_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8038_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +#define PM8XXX_GPIO_DISABLE(_gpio) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, 0, 0, 0, PM8038_GPIO_VIN_L11, \ + 0, 0, 0, 1) + +#define PM8XXX_GPIO_OUTPUT(_gpio, _val) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM8038_GPIO_VIN_L11, \ + PM_GPIO_STRENGTH_HIGH, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8XXX_GPIO_INPUT(_gpio, _pull) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, PM_GPIO_OUT_BUF_CMOS, 0, \ + _pull, PM8038_GPIO_VIN_L11, \ + PM_GPIO_STRENGTH_NO, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8XXX_GPIO_OUTPUT_FUNC(_gpio, _val, _func) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM8038_GPIO_VIN_L11, \ + PM_GPIO_STRENGTH_HIGH, \ + _func, 0, 0) + +#define PM8XXX_GPIO_OUTPUT_VIN(_gpio, _val, _vin) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, _vin, \ + PM_GPIO_STRENGTH_HIGH, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +/* Initial pm8038 GPIO configurations */ +static struct pm8xxx_gpio_init pm8038_gpios[] __initdata = { + /* keys GPIOs */ + PM8XXX_GPIO_INPUT(3, PM_GPIO_PULL_UP_30), + PM8XXX_GPIO_INPUT(8, PM_GPIO_PULL_UP_30), + PM8XXX_GPIO_INPUT(10, PM_GPIO_PULL_UP_30), + PM8XXX_GPIO_INPUT(11, PM_GPIO_PULL_UP_30), + /* haptics gpio */ + PM8XXX_GPIO_OUTPUT_FUNC(7, 0, PM_GPIO_FUNC_1), +}; + +/* Initial pm8038 MPP configurations */ +static struct pm8xxx_mpp_init pm8038_mpps[] __initdata = { + /* External 5V regulator enable; shared by HDMI and USB_OTG switches. */ + PM8XXX_MPP_INIT(3, D_INPUT, PM8038_MPP_DIG_LEVEL_VPH, DIN_TO_INT), +}; + +void __init msm8930_pm8038_gpio_mpp_init(void) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(pm8038_gpios); i++) { + rc = pm8xxx_gpio_config(pm8038_gpios[i].gpio, + &pm8038_gpios[i].config); + if (rc) { + pr_err("%s: pm8xxx_gpio_config: rc=%d\n", __func__, rc); + break; + } + } + + /* Initial MPP configuration. */ + for (i = 0; i < ARRAY_SIZE(pm8038_mpps); i++) { + rc = pm8xxx_mpp_config(pm8038_mpps[i].mpp, + &pm8038_mpps[i].config); + if (rc) { + pr_err("%s: pm8xxx_mpp_config: rc=%d\n", __func__, rc); + break; + } + } +} + +static struct pm8xxx_adc_amux pm8xxx_adc_channels_data[] = { + {"vcoin", CHANNEL_VCOIN, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vbat", CHANNEL_VBAT, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"dcin", CHANNEL_DCIN, CHAN_PATH_SCALING4, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"ichg", CHANNEL_ICHG, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vph_pwr", CHANNEL_VPH_PWR, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"ibat", CHANNEL_IBAT, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"batt_therm", CHANNEL_BATT_THERM, CHAN_PATH_SCALING1, AMUX_RSV2, + ADC_DECIMATION_TYPE2, ADC_SCALE_BATT_THERM}, + {"batt_id", CHANNEL_BATT_ID, CHAN_PATH_SCALING1, AMUX_RSV2, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"usbin", CHANNEL_USBIN, CHAN_PATH_SCALING3, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pmic_therm", CHANNEL_DIE_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PMIC_THERM}, + {"625mv", CHANNEL_625MV, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"125v", CHANNEL_125V, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"chg_temp", CHANNEL_CHG_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pa_therm1", ADC_MPP_1_AMUX4, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PA_THERM}, + {"xo_therm", CHANNEL_MUXOFF, CHAN_PATH_SCALING1, AMUX_RSV0, + ADC_DECIMATION_TYPE2, ADC_SCALE_XOTHERM}, + {"pa_therm0", ADC_MPP_1_AMUX3, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PA_THERM}, +}; + +static struct pm8xxx_adc_properties pm8xxx_adc_data = { + .adc_vdd_reference = 1800, /* milli-voltage for this adc */ + .bitresolution = 15, + .bipolar = 0, +}; + +static struct pm8xxx_adc_platform_data pm8xxx_adc_pdata = { + .adc_channel = pm8xxx_adc_channels_data, + .adc_num_board_channel = ARRAY_SIZE(pm8xxx_adc_channels_data), + .adc_prop = &pm8xxx_adc_data, + .adc_mpp_base = PM8038_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_irq_platform_data pm8xxx_irq_pdata __devinitdata = { + .irq_base = PM8038_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(104), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_gpio_platform_data pm8xxx_gpio_pdata __devinitdata = { + .gpio_base = PM8038_GPIO_PM_TO_SYS(1), +}; + +static struct pm8xxx_mpp_platform_data pm8xxx_mpp_pdata __devinitdata = { + .mpp_base = PM8038_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_rtc_platform_data pm8xxx_rtc_pdata __devinitdata = { + .rtc_write_enable = false, + .rtc_alarm_powerup = false, +}; + +static struct pm8xxx_pwrkey_platform_data pm8xxx_pwrkey_pdata = { + .pull_up = 1, + .kpd_trigger_delay_us = 15625, + .wakeup = 1, +}; + +static int pm8921_therm_mitigation[] = { + 1100, + 700, + 600, + 325, +}; + +#define MAX_VOLTAGE_MV 4200 +static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = { + .safety_time = 180, + .update_time = 60000, + .max_voltage = MAX_VOLTAGE_MV, + .min_voltage = 3200, + .resume_voltage_delta = 100, + .term_current = 100, + .cool_temp = 10, + .warm_temp = 40, + .temp_check_period = 1, + .max_bat_chg_current = 1100, + .cool_bat_chg_current = 350, + .warm_bat_chg_current = 350, + .cool_bat_voltage = 4100, + .warm_bat_voltage = 4100, + .thermal_mitigation = pm8921_therm_mitigation, + .thermal_levels = ARRAY_SIZE(pm8921_therm_mitigation), + .led_src_config = LED_SRC_VPH_PWR, +}; + +#define PM8038_WLED_MAX_CURRENT 25 +#define PM8XXX_LED_PWM_PERIOD 1000 +#define PM8XXX_LED_PWM_DUTY_MS 20 +#define PM8038_RGB_LED_MAX_CURRENT 12 + +static struct led_info pm8038_led_info[] = { + [0] = { + .name = "wled", + .default_trigger = "bkl_trigger", + }, + [1] = { + .name = "led:rgb_red", + .default_trigger = "battery-charging", + }, + [2] = { + .name = "led:rgb_green", + }, + [3] = { + .name = "led:rgb_blue", + }, +}; + +static struct led_platform_data pm8038_led_core_pdata = { + .num_leds = ARRAY_SIZE(pm8038_led_info), + .leds = pm8038_led_info, +}; + +static struct wled_config_data wled_cfg = { + .dig_mod_gen_en = true, + .cs_out_en = true, + .ctrl_delay_us = 0, + .op_fdbck = true, + .ovp_val = WLED_OVP_32V, + .boost_curr_lim = WLED_CURR_LIMIT_525mA, + .num_strings = 1, +}; + +static int pm8038_led0_pwm_duty_pcts[56] = { + 1, 4, 8, 12, 16, 20, 24, 28, 32, 36, + 40, 44, 46, 52, 56, 60, 64, 68, 72, 76, + 80, 84, 88, 92, 96, 100, 100, 100, 98, 95, + 92, 88, 84, 82, 78, 74, 70, 66, 62, 58, + 58, 54, 50, 48, 42, 38, 34, 30, 26, 22, + 14, 10, 6, 4, 1 +}; + +static struct pm8xxx_pwm_duty_cycles pm8038_led0_pwm_duty_cycles = { + .duty_pcts = (int *)&pm8038_led0_pwm_duty_pcts, + .num_duty_pcts = ARRAY_SIZE(pm8038_led0_pwm_duty_pcts), + .duty_ms = PM8XXX_LED_PWM_DUTY_MS, + .start_idx = 0, +}; + +static struct pm8xxx_led_config pm8038_led_configs[] = { + [0] = { + .id = PM8XXX_ID_WLED, + .mode = PM8XXX_LED_MODE_MANUAL, + .max_current = PM8038_WLED_MAX_CURRENT, + .default_state = 0, + .wled_cfg = &wled_cfg, + }, + [1] = { + .id = PM8XXX_ID_RGB_LED_RED, + .mode = PM8XXX_LED_MODE_PWM1, + .max_current = PM8038_RGB_LED_MAX_CURRENT, + .pwm_channel = 5, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD, + .pwm_duty_cycles = &pm8038_led0_pwm_duty_cycles, + }, + [2] = { + .id = PM8XXX_ID_RGB_LED_GREEN, + .mode = PM8XXX_LED_MODE_PWM1, + .max_current = PM8038_RGB_LED_MAX_CURRENT, + .pwm_channel = 4, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD, + .pwm_duty_cycles = &pm8038_led0_pwm_duty_cycles, + }, + [3] = { + .id = PM8XXX_ID_RGB_LED_BLUE, + .mode = PM8XXX_LED_MODE_PWM1, + .max_current = PM8038_RGB_LED_MAX_CURRENT, + .pwm_channel = 3, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD, + .pwm_duty_cycles = &pm8038_led0_pwm_duty_cycles, + }, +}; + +static struct pm8xxx_led_platform_data pm8xxx_leds_pdata = { + .led_core = &pm8038_led_core_pdata, + .configs = pm8038_led_configs, + .num_configs = ARRAY_SIZE(pm8038_led_configs), +}; + +static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = { + .r_sense = 10, +}; + +static struct pm8xxx_misc_platform_data pm8xxx_misc_pdata = { + .priority = 0, +}; + +static struct pm8xxx_spk_platform_data pm8xxx_spk_pdata = { + .spk_add_enable = false, +}; + +static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = { + .battery_type = BATT_UNKNOWN, + .r_sense = 10, + .i_test = 2500, + .v_failure = 3000, + .calib_delay_ms = 600000, + .max_voltage_uv = MAX_VOLTAGE_MV * 1000, +}; + +static struct pm8038_platform_data pm8038_platform_data __devinitdata = { + .irq_pdata = &pm8xxx_irq_pdata, + .gpio_pdata = &pm8xxx_gpio_pdata, + .mpp_pdata = &pm8xxx_mpp_pdata, + .rtc_pdata = &pm8xxx_rtc_pdata, + .pwrkey_pdata = &pm8xxx_pwrkey_pdata, + .misc_pdata = &pm8xxx_misc_pdata, + .regulator_pdatas = msm8930_pm8038_regulator_pdata, + .charger_pdata = &pm8921_chg_pdata, + .bms_pdata = &pm8921_bms_pdata, + .adc_pdata = &pm8xxx_adc_pdata, + .leds_pdata = &pm8xxx_leds_pdata, + .ccadc_pdata = &pm8xxx_ccadc_pdata, + .spk_pdata = &pm8xxx_spk_pdata, +}; + +static struct msm_ssbi_platform_data msm8930_ssbi_pm8038_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = "pm8038-core", + .platform_data = &pm8038_platform_data, + }, +}; + +void __init msm8930_init_pmic(void) +{ + pmic_reset_irq = PM8038_IRQ_BASE + PM8038_RESOUT_IRQ; + msm8960_device_ssbi_pmic.dev.platform_data = + &msm8930_ssbi_pm8038_pdata; + pm8038_platform_data.num_regulators + = msm8930_pm8038_regulator_pdata_len; + if (machine_is_apq8064_mtp()) + pm8921_bms_pdata.battery_type = BATT_PALLADIUM; + else if (machine_is_apq8064_liquid()) + pm8921_bms_pdata.battery_type = BATT_DESAY; +} diff --git a/arch/arm/mach-msm/board-8930-regulator.c b/arch/arm/mach-msm/board-8930-regulator.c new file mode 100644 index 00000000000..cb8903c54c0 --- /dev/null +++ b/arch/arm/mach-msm/board-8930-regulator.c @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include "board-8930.h" + +#define VREG_CONSUMERS(_id) \ + static struct regulator_consumer_supply vreg_consumers_##_id[] + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +VREG_CONSUMERS(L1) = { + REGULATOR_SUPPLY("8038_l1", NULL), + REGULATOR_SUPPLY("iris_vddrfa", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L2) = { + REGULATOR_SUPPLY("8038_l2", NULL), + REGULATOR_SUPPLY("iris_vdddig", "wcnss_wlan.0"), + REGULATOR_SUPPLY("dsi_vdda", "mipi_dsi.1"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.0"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.1"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.2"), +}; +VREG_CONSUMERS(L3) = { + REGULATOR_SUPPLY("8038_l3", NULL), + REGULATOR_SUPPLY("HSUSB_3p3", "msm_otg"), +}; +VREG_CONSUMERS(L4) = { + REGULATOR_SUPPLY("8038_l4", NULL), + REGULATOR_SUPPLY("HSUSB_1p8", "msm_otg"), + REGULATOR_SUPPLY("iris_vddxo", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L5) = { + REGULATOR_SUPPLY("8038_l5", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.1"), +}; +VREG_CONSUMERS(L6) = { + REGULATOR_SUPPLY("8038_l6", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.3"), +}; +VREG_CONSUMERS(L7) = { + REGULATOR_SUPPLY("8038_l7", NULL), +}; +VREG_CONSUMERS(L8) = { + REGULATOR_SUPPLY("8038_l8", NULL), + REGULATOR_SUPPLY("dsi_vdc", "mipi_dsi.1"), +}; +VREG_CONSUMERS(L9) = { + REGULATOR_SUPPLY("8038_l9", NULL), + REGULATOR_SUPPLY("vdd_ana", "3-004a"), + REGULATOR_SUPPLY("vdd", "3-0024"), + REGULATOR_SUPPLY("cam_vana", "4-001a"), + REGULATOR_SUPPLY("cam_vana", "4-006c"), + REGULATOR_SUPPLY("cam_vana", "4-0048"), + REGULATOR_SUPPLY("cam_vaf", "4-001a"), + REGULATOR_SUPPLY("cam_vaf", "4-006c"), + REGULATOR_SUPPLY("cam_vaf", "4-0048"), + REGULATOR_SUPPLY("cam_vana", "4-0020"), + REGULATOR_SUPPLY("cam_vaf", "4-0020"), +}; +VREG_CONSUMERS(L10) = { + REGULATOR_SUPPLY("8038_l10", NULL), + REGULATOR_SUPPLY("iris_vddpa", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L11) = { + REGULATOR_SUPPLY("8038_l11", NULL), + REGULATOR_SUPPLY("vdd_dig", "3-004a"), + REGULATOR_SUPPLY("iris_vddio", "wcnss_wlan.0"), + REGULATOR_SUPPLY("riva_vddpx", "wcnss_wlan.0"), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.1"), + REGULATOR_SUPPLY("VDDIO_CDC", "sitar-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "sitar-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "sitar-slim"), + REGULATOR_SUPPLY("VDDIO_CDC", "sitar1p1-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "sitar1p1-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "sitar1p1-slim"), + REGULATOR_SUPPLY("vddp", "0-0048"), +}; +VREG_CONSUMERS(L12) = { + REGULATOR_SUPPLY("8038_l12", NULL), + REGULATOR_SUPPLY("cam_vdig", "4-001a"), + REGULATOR_SUPPLY("cam_vdig", "4-006c"), + REGULATOR_SUPPLY("cam_vdig", "4-0048"), + REGULATOR_SUPPLY("cam_vdig", "4-0020"), +}; +VREG_CONSUMERS(L14) = { + REGULATOR_SUPPLY("8038_l14", NULL), + REGULATOR_SUPPLY("pa_therm", "pm8xxx-adc"), +}; +VREG_CONSUMERS(L15) = { + REGULATOR_SUPPLY("8038_l15", NULL), +}; +VREG_CONSUMERS(L16) = { + REGULATOR_SUPPLY("8038_l16", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"), +}; +VREG_CONSUMERS(L17) = { + REGULATOR_SUPPLY("8038_l17", NULL), +}; +VREG_CONSUMERS(L18) = { + REGULATOR_SUPPLY("8038_l18", NULL), +}; +VREG_CONSUMERS(L19) = { + REGULATOR_SUPPLY("8038_l19", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"), +}; +VREG_CONSUMERS(L20) = { + REGULATOR_SUPPLY("8038_l20", NULL), + REGULATOR_SUPPLY("VDDD_CDC_D", "sitar-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "sitar-slim"), + REGULATOR_SUPPLY("VDDD_CDC_D", "sitar1p1-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "sitar1p1-slim"), +}; +VREG_CONSUMERS(L21) = { + REGULATOR_SUPPLY("8038_l21", NULL), +}; +VREG_CONSUMERS(L22) = { + REGULATOR_SUPPLY("8038_l22", NULL), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.3"), +}; +VREG_CONSUMERS(L23) = { + REGULATOR_SUPPLY("8038_l23", NULL), + REGULATOR_SUPPLY("dsi_vddio", "mipi_dsi.1"), + REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"), + REGULATOR_SUPPLY("hdmi_vcc", "hdmi_msm.0"), + REGULATOR_SUPPLY("pll_vdd", "pil_riva"), + REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"), + REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"), +}; +VREG_CONSUMERS(L24) = { + REGULATOR_SUPPLY("8038_l24", NULL), + REGULATOR_SUPPLY("riva_vddmx", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L26) = { + REGULATOR_SUPPLY("8038_l26", NULL), +}; +VREG_CONSUMERS(L27) = { + REGULATOR_SUPPLY("8038_l27", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"), +}; +VREG_CONSUMERS(S1) = { + REGULATOR_SUPPLY("8038_s1", NULL), + REGULATOR_SUPPLY("riva_vddcx", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(S2) = { + REGULATOR_SUPPLY("8038_s2", NULL), +}; +VREG_CONSUMERS(S3) = { + REGULATOR_SUPPLY("8038_s3", NULL), +}; +VREG_CONSUMERS(S4) = { + REGULATOR_SUPPLY("8038_s4", NULL), + REGULATOR_SUPPLY("CDC_VDD_CP", "sitar-slim"), + REGULATOR_SUPPLY("CDC_VDD_CP", "sitar1p1-slim"), +}; +VREG_CONSUMERS(S5) = { + REGULATOR_SUPPLY("8038_s5", NULL), + REGULATOR_SUPPLY("krait0", NULL), +}; +VREG_CONSUMERS(S6) = { + REGULATOR_SUPPLY("8038_s6", NULL), + REGULATOR_SUPPLY("krait1", NULL), +}; +VREG_CONSUMERS(LVS1) = { + REGULATOR_SUPPLY("8038_lvs1", NULL), + REGULATOR_SUPPLY("cam_vio", "4-001a"), + REGULATOR_SUPPLY("cam_vio", "4-006c"), + REGULATOR_SUPPLY("cam_vio", "4-0048"), + REGULATOR_SUPPLY("cam_vio", "4-0020"), +}; +VREG_CONSUMERS(LVS2) = { + REGULATOR_SUPPLY("8038_lvs2", NULL), + REGULATOR_SUPPLY("vcc_i2c", "3-004a"), + REGULATOR_SUPPLY("vcc_i2c", "3-0024"), + REGULATOR_SUPPLY("vcc_i2c", "0-0048"), +}; +VREG_CONSUMERS(EXT_5V) = { + REGULATOR_SUPPLY("ext_5v", NULL), + REGULATOR_SUPPLY("hdmi_mvs", "hdmi_msm.0"), +}; +VREG_CONSUMERS(EXT_OTG_SW) = { + REGULATOR_SUPPLY("ext_otg_sw", NULL), + REGULATOR_SUPPLY("vbus_otg", "msm_otg"), +}; +VREG_CONSUMERS(VDD_DIG_CORNER) = { + REGULATOR_SUPPLY("vdd_dig_corner", NULL), + REGULATOR_SUPPLY("hsusb_vdd_dig", "msm_otg"), +}; + +#define PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, _modes, _ops, \ + _apply_uV, _pull_down, _always_on, _supply_regulator, \ + _system_uA, _enable_time, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _max_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pull_down_enable = _pull_down, \ + .system_uA = _system_uA, \ + .enable_time = _enable_time, \ + } + +#define PM8XXX_LDO(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_NLDO1200(_id, _name, _always_on, _pull_down, _min_uV, \ + _max_uV, _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_SMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_FTSMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS \ + | REGULATOR_CHANGE_MODE, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_VS(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +#define PM8XXX_VS300(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +#define PM8XXX_NCP(_id, _name, _always_on, _min_uV, _max_uV, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, 0, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, 0, 0, \ + _always_on, _supply_regulator, 0, _enable_time, _reg_id) + +/* Pin control initialization */ +#define PM8XXX_PC(_id, _name, _always_on, _pin_fn, _pin_ctrl, \ + _supply_regulator, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pin_fn = PM8XXX_VREG_PIN_FN_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +#define RPM_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, _default_uV, \ + _peak_uA, _avg_uA, _pull_down, _pin_ctrl, _freq, _pin_fn, \ + _force_mode, _sleep_set_force_mode, _power_mode, _state, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _min_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8038_##_id, \ + .default_uV = _default_uV, \ + .peak_uA = _peak_uA, \ + .avg_uA = _avg_uA, \ + .pull_down_enable = _pull_down, \ + .pin_ctrl = _pin_ctrl, \ + .freq = RPM_VREG_FREQ_##_freq, \ + .pin_fn = _pin_fn, \ + .force_mode = _force_mode, \ + .sleep_set_force_mode = _sleep_set_force_mode, \ + .power_mode = _power_mode, \ + .state = _state, \ + .sleep_selectable = _sleep_selectable, \ + .system_uA = _system_uA, \ + } + +#define RPM_LDO(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _init_peak_uA) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _init_peak_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, RPM_VREG_POWER_MODE_8930_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, _system_uA) + +#define RPM_SMPS(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _freq, _force_mode, \ + _sleep_set_force_mode) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _min_uV, _system_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_##_force_mode, \ + RPM_VREG_FORCE_MODE_8930_##_sleep_set_force_mode, \ + RPM_VREG_POWER_MODE_8930_PWM, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) + +#define RPM_VS(_id, _always_on, _pd, _sleep_selectable, _supply_regulator) \ + RPM_INIT(_id, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, 0, 1000, 1000, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, RPM_VREG_POWER_MODE_8930_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +#define RPM_NCP(_id, _always_on, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _freq) \ + RPM_INIT(_id, _min_uV, _max_uV, 0, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS, 0, _max_uV, 1000, 1000, 0, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, RPM_VREG_POWER_MODE_8930_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +#define RPM_CORNER(_id, _always_on, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator) \ + RPM_INIT(_id, _min_uV, _max_uV, 0, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS, 0, _max_uV, 0, 0, 0, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, \ + RPM_VREG_FORCE_MODE_8930_NONE, RPM_VREG_POWER_MODE_8930_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +/* Pin control initialization */ +#define RPM_PC_INIT(_id, _always_on, _pin_fn, _pin_ctrl, _supply_regulator) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8038_##_id##_PC, \ + .pin_fn = RPM_VREG_PIN_FN_8930_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +#define GPIO_VREG(_id, _reg_name, _gpio_label, _gpio, _supply_regulator) \ + [MSM8930_GPIO_VREG_ID_##_id] = { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .regulator_name = _reg_name, \ + .gpio_label = _gpio_label, \ + .gpio = _gpio, \ + } + +#define SAW_VREG_INIT(_id, _name, _min_uV, _max_uV) \ + { \ + .constraints = { \ + .name = _name, \ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + }, \ + .num_consumer_supplies = ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + } + +/* GPIO regulator constraints */ +struct gpio_regulator_platform_data +msm8930_gpio_regulator_pdata[] __devinitdata = { + /* ID vreg_name gpio_label gpio supply */ + GPIO_VREG(EXT_5V, "ext_5v", "ext_5v_en", 63, NULL), + GPIO_VREG(EXT_OTG_SW, "ext_otg_sw", "ext_otg_sw_en", 97, "ext_5v"), +}; + +/* SAW regulator constraints */ +struct regulator_init_data msm8930_saw_regulator_core0_pdata = + /* ID vreg_name min_uV max_uV */ + SAW_VREG_INIT(S5, "8038_s5", 850000, 1300000); +struct regulator_init_data msm8930_saw_regulator_core1_pdata = + SAW_VREG_INIT(S6, "8038_s6", 850000, 1300000); + +/* PM8038 regulator constraints */ +struct pm8xxx_regulator_platform_data +msm8930_pm8038_regulator_pdata[] __devinitdata = { + /* + * ID name always_on pd min_uV max_uV en_t supply + * system_uA reg_ID + */ + PM8XXX_NLDO1200(L16, "8038_l16", 0, 1, 375000, 1050000, 200, "8038_s3", + 0, 0), + PM8XXX_NLDO1200(L19, "8038_l19", 0, 1, 375000, 1050000, 200, "8038_s3", + 0, 1), + PM8XXX_NLDO1200(L27, "8038_l27", 0, 1, 375000, 1050000, 200, "8038_s3", + 0, 2), +}; + +static struct rpm_regulator_init_data +msm8930_rpm_regulator_init_data[] __devinitdata = { + /* ID a_on pd ss min_uV max_uV supply sys_uA freq fm ss_fm */ + RPM_SMPS(S1, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, AUTO, LPM), + RPM_SMPS(S2, 1, 1, 1, 1400000, 1400000, NULL, 100000, 1p60, AUTO, LPM), + RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, LPM), + RPM_SMPS(S4, 1, 1, 1, 1950000, 2200000, NULL, 100000, 1p60, AUTO, LPM), + + /* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */ + RPM_LDO(L1, 0, 1, 0, 1300000, 1300000, "8038_s2", 0, 0), + RPM_LDO(L2, 0, 1, 0, 1200000, 1200000, "8038_s2", 0, 0), + RPM_LDO(L3, 0, 1, 0, 3075000, 3075000, NULL, 0, 0), + RPM_LDO(L4, 1, 1, 0, 1800000, 1800000, NULL, 10000, 10000), + RPM_LDO(L5, 0, 1, 0, 2950000, 2950000, NULL, 0, 0), + RPM_LDO(L6, 0, 1, 0, 2950000, 2950000, NULL, 0, 0), + RPM_LDO(L7, 0, 1, 0, 2050000, 2050000, "8038_s4", 0, 0), + RPM_LDO(L8, 0, 1, 0, 2800000, 2800000, NULL, 0, 0), + RPM_LDO(L9, 0, 1, 0, 2850000, 2850000, NULL, 0, 0), + RPM_LDO(L10, 0, 1, 0, 2900000, 2900000, NULL, 0, 0), + RPM_LDO(L11, 1, 1, 0, 1800000, 1800000, "8038_s4", 10000, 10000), + RPM_LDO(L12, 0, 1, 0, 1200000, 1200000, "8038_s2", 0, 0), + RPM_LDO(L14, 0, 1, 0, 1800000, 1800000, NULL, 0, 0), + RPM_LDO(L15, 0, 1, 0, 1800000, 2950000, NULL, 0, 0), + RPM_LDO(L17, 0, 1, 0, 1800000, 2950000, NULL, 0, 0), + RPM_LDO(L18, 0, 1, 0, 1800000, 1800000, NULL, 0, 0), + RPM_LDO(L20, 1, 1, 0, 1200000, 1200000, "8038_s2", 10000, 10000), + RPM_LDO(L21, 0, 1, 0, 1900000, 1900000, "8038_s4", 0, 0), + RPM_LDO(L22, 1, 1, 0, 1850000, 2950000, NULL, 10000, 10000), + RPM_LDO(L23, 1, 1, 1, 1800000, 1800000, "8038_s4", 0, 0), + RPM_LDO(L24, 0, 1, 1, 500000, 1150000, "8038_s2", 10000, 10000), + RPM_LDO(L26, 1, 1, 0, 1050000, 1050000, "8038_s2", 10000, 10000), + + /* ID a_on pd ss supply */ + RPM_VS(LVS1, 0, 1, 0, "8038_l11"), + RPM_VS(LVS2, 0, 1, 0, "8038_l11"), + + /* ID a_on ss min_corner max_corner supply */ + RPM_CORNER(VDD_DIG_CORNER, 0, 1, RPM_VREG_CORNER_NONE, + RPM_VREG_CORNER_HIGH, NULL), +}; + +int msm8930_pm8038_regulator_pdata_len __devinitdata = + ARRAY_SIZE(msm8930_pm8038_regulator_pdata); + +struct rpm_regulator_platform_data msm8930_rpm_regulator_pdata __devinitdata = { + .init_data = msm8930_rpm_regulator_init_data, + .num_regulators = ARRAY_SIZE(msm8930_rpm_regulator_init_data), + .version = RPM_VREG_VERSION_8930, + .vreg_id_vdd_mem = RPM_VREG_ID_PM8038_L24, + .vreg_id_vdd_dig = RPM_VREG_ID_PM8038_VDD_DIG_CORNER, +}; diff --git a/arch/arm/mach-msm/board-8930-storage.c b/arch/arm/mach-msm/board-8930-storage.c new file mode 100644 index 00000000000..bb35c958e42 --- /dev/null +++ b/arch/arm/mach-msm/board-8930-storage.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" + +#include "board-8930.h" +#include "board-storage-common-a.h" + +/* MSM8960 has 5 SDCC controllers */ +enum sdcc_controllers { + SDCC1, + SDCC2, + SDCC3, + SDCC4, + SDCC5, + MAX_SDCC_CONTROLLER +}; + +/* All SDCC controllers require VDD/VCC voltage */ +static struct msm_mmc_reg_data mmc_vdd_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .name = "sdc_vdd", + .high_vol_level = 2950000, + .low_vol_level = 2950000, + .always_on = 1, + .lpm_sup = 1, + .lpm_uA = 9000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .name = "sdc_vdd", + .high_vol_level = 2950000, + .low_vol_level = 2950000, + /* + * Normally this is not an always ON regulator. On this + * platform, unfortunately the sd detect line is connected + * to this via esd circuit and so turn this off/on while card + * is not present causes the sd detect line to toggle + * continuously. This is expected to be fixed in the newer + * hardware revisions - maybe once that is done, this can be + * reverted. + */ + .always_on = 1, + .lpm_sup = 1, + .hpm_uA = 800000, /* 800mA */ + .lpm_uA = 9000, + } +}; + +/* All SDCC controllers may require voting for VDD PAD voltage */ +static struct msm_mmc_reg_data mmc_vdd_io_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .name = "sdc_vdd_io", + .always_on = 1, + .high_vol_level = 1800000, + .low_vol_level = 1800000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .name = "sdc_vdd_io", + .high_vol_level = 2950000, + .low_vol_level = 1850000, + .always_on = 1, + .lpm_sup = 1, + /* Max. Active current required is 16 mA */ + .hpm_uA = 16000, + /* + * Sleep current required is ~300 uA. But min. vote can be + * in terms of mA (min. 1 mA). So let's vote for 2 mA + * during sleep. + */ + .lpm_uA = 2000, + } +}; + +static struct msm_mmc_slot_reg_data mmc_slot_vreg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .vdd_data = &mmc_vdd_reg_data[SDCC1], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC1], + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .vdd_data = &mmc_vdd_reg_data[SDCC3], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC3], + } +}; + +/* SDC1 pad data */ +static struct msm_mmc_pad_drv sdc1_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_16MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_10MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_10MA} +}; + +static struct msm_mmc_pad_drv sdc1_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +/* SDC3 pad data */ +static struct msm_mmc_pad_drv sdc3_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_8MA} +}; + +static struct msm_mmc_pad_drv sdc3_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc3_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC3_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc3_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC3_CLK, GPIO_CFG_NO_PULL}, + /* + * SDC3 CMD line should be PULLed UP otherwise fluid platform will + * see transitions (1 -> 0 and 0 -> 1) on card detection line, + * which would result in false card detection interrupts. + */ + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + /* + * Keeping DATA lines status to PULL UP will make sure that + * there is no current leak during sleep if external pull up + * is connected to DATA lines. + */ + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull_data mmc_pad_pull_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_pull_on_cfg, + .off = sdc1_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_pull_on_cfg) + }, + [SDCC3] = { + .on = sdc3_pad_pull_on_cfg, + .off = sdc3_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc3_pad_pull_on_cfg) + }, +}; + +static struct msm_mmc_pad_drv_data mmc_pad_drv_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_drv_on_cfg, + .off = sdc1_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_drv_on_cfg) + }, + [SDCC3] = { + .on = sdc3_pad_drv_on_cfg, + .off = sdc3_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc3_pad_drv_on_cfg) + }, +}; + +static struct msm_mmc_pad_data mmc_pad_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pull = &mmc_pad_pull_data[SDCC1], + .drv = &mmc_pad_drv_data[SDCC1] + }, + [SDCC3] = { + .pull = &mmc_pad_pull_data[SDCC3], + .drv = &mmc_pad_drv_data[SDCC3] + }, +}; + +static struct msm_mmc_pin_data mmc_slot_pin_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pad_data = &mmc_pad_data[SDCC1], + }, + [SDCC3] = { + .pad_data = &mmc_pad_data[SDCC3], + }, +}; + +#define MSM_MPM_PIN_SDC1_DAT1 17 +#define MSM_MPM_PIN_SDC3_DAT1 21 + +static unsigned int sdc1_sup_clk_rates[] = { + 400000, 24000000, 48000000, +}; + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static unsigned int sdc3_sup_clk_rates[] = { + 400000, 24000000, 48000000, 96000000, 192000000, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct mmc_platform_data msm8960_sdc1_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, +#ifdef CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .sup_clk_table = sdc1_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc1_sup_clk_rates), + .pclk_src_dfab = 1, + .nonremovable = 1, + .vreg_data = &mmc_slot_vreg_data[SDCC1], + .pin_data = &mmc_slot_pin_data[SDCC1], + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC1_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data msm8960_sdc3_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc3_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc3_sup_clk_rates), + .pclk_src_dfab = 1, +#ifdef CONFIG_MMC_MSM_SDC3_WP_SUPPORT +/*TODO: Insert right replacement for PM8038 */ +#ifndef MSM8930_PHASE_2 + .wpswitch_gpio = PM8921_GPIO_PM_TO_SYS(16), +#else + .wpswitch_gpio = 66, + .wpswitch_polarity = 1, +#endif +#endif + .vreg_data = &mmc_slot_vreg_data[SDCC3], + .pin_data = &mmc_slot_pin_data[SDCC3], +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION +/*TODO: Insert right replacement for PM8038 */ +#ifndef MSM8930_PHASE_2 + .status_gpio = PM8921_GPIO_PM_TO_SYS(26), + .status_irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 26), +#else + .status_gpio = 94, + .status_irq = MSM_GPIO_TO_INT(94), +#endif + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + .is_status_gpio_active_low = true, +#endif + .xpc_cap = 1, + .uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | + MMC_CAP_UHS_SDR104 | MMC_CAP_MAX_CURRENT_800), + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC3_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +void __init msm8930_init_mmc(void) +{ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + /* SDC1 : eMMC card connected */ + msm_add_sdcc(1, &msm8960_sdc1_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + /* SDC3: External card slot */ + if (!machine_is_msm8930_cdp()) { + msm8960_sdc3_data.wpswitch_gpio = 0; + msm8960_sdc3_data.wpswitch_polarity = 0; + } + msm_add_sdcc(3, &msm8960_sdc3_data); +#endif +} diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c new file mode 100644 index 00000000000..712b520ba3a --- /dev/null +++ b/arch/arm/mach-msm/board-8930.c @@ -0,0 +1,2456 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ANDROID_PMEM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef CONFIG_USB_MSM_OTG_72K +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "timer.h" +#include "devices.h" +#include "devices-msm8x60.h" +#include "spm.h" +#include "pm.h" +#include +#include "rpm_resources.h" +#include +#include "acpuclock.h" +#include "smd_private.h" +#include "pm-boot.h" +#include "msm_watchdog.h" +#include "board-8930.h" + +static struct platform_device msm_fm_platform_init = { + .name = "iris_fm", + .id = -1, +}; + +#define KS8851_RST_GPIO 89 +#define KS8851_IRQ_GPIO 90 +#define HAP_SHIFT_LVL_OE_GPIO 47 + +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) + +struct sx150x_platform_data msm8930_sx150x_data[] = { + [SX150X_CAM] = { + .gpio_base = GPIO_CAM_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0, + .io_pulldn_ena = 0xc0, + .io_open_drain_ena = 0x0, + .irq_summary = -1, + }, +}; + +#endif + +#define MSM_PMEM_ADSP_SIZE 0x7800000 +#define MSM_PMEM_AUDIO_SIZE 0x4CF000 +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +#define MSM_PMEM_SIZE 0x4000000 /* 64 Mbytes */ +#else +#define MSM_PMEM_SIZE 0x2800000 /* 40 Mbytes */ +#endif +#define MSM_LIQUID_PMEM_SIZE 0x4000000 /* 64 Mbytes */ + +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +#define HOLE_SIZE 0x20000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000 +#ifdef CONFIG_MSM_IOMMU +#define MSM_ION_MM_SIZE 0x3800000 /* Need to be multiple of 64K */ +#define MSM_ION_SF_SIZE 0x0 +#define MSM_ION_QSECOM_SIZE 0x780000 /* (7.5MB) */ +#define MSM_ION_HEAP_NUM 7 +#else +#define MSM_ION_SF_SIZE MSM_PMEM_SIZE +#define MSM_ION_MM_SIZE MSM_PMEM_ADSP_SIZE +#define MSM_ION_QSECOM_SIZE 0x600000 /* (6MB) */ +#define MSM_ION_HEAP_NUM 8 +#endif +#define MSM_ION_MM_FW_SIZE (0x200000 - HOLE_SIZE) /* 2MB - 128Kb */ +#define MSM_ION_MFC_SIZE SZ_8K +#define MSM_ION_AUDIO_SIZE MSM_PMEM_AUDIO_SIZE + +#define MSM_LIQUID_ION_MM_SIZE (MSM_ION_MM_SIZE + 0x600000) +#define MSM_LIQUID_ION_SF_SIZE MSM_LIQUID_PMEM_SIZE +#define MSM_HDMI_PRIM_ION_SF_SIZE MSM_HDMI_PRIM_PMEM_SIZE + +#define MSM_MM_FW_SIZE (0x200000 - HOLE_SIZE) /*2MB -128Kb */ +#define MSM8930_FIXED_AREA_START (0xa0000000 - (MSM_ION_MM_FW_SIZE + \ + HOLE_SIZE)) +#define MAX_FIXED_AREA_SIZE 0x10000000 +#define MSM8930_FW_START MSM8930_FIXED_AREA_START + +#else +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000 +#define MSM_ION_HEAP_NUM 1 +#endif + +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) +{ + pmem_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); +#endif + +#ifdef CONFIG_ANDROID_PMEM +static unsigned pmem_size = MSM_PMEM_SIZE; +static int __init pmem_size_setup(char *p) +{ + pmem_size = memparse(p, NULL); + return 0; +} +early_param("pmem_size", pmem_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; + +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; + +static int __init pmem_audio_size_setup(char *p) +{ + pmem_audio_size = memparse(p, NULL); + return 0; +} +early_param("pmem_audio_size", pmem_audio_size_setup); +#endif + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device msm8930_android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = {.platform_data = &android_pmem_pdata}, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; +static struct platform_device msm8930_android_pmem_adsp_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct android_pmem_platform_data android_pmem_audio_pdata = { + .name = "pmem_audio", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device msm8930_android_pmem_audio_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_audio_pdata }, +}; +#endif /* CONFIG_MSM_MULTIMEDIA_USE_ION */ +#endif /* CONFIG_ANDROID_PMEM */ + +struct fmem_platform_data msm8930_fmem_pdata = { +}; + +#define DSP_RAM_BASE_8960 0x8da00000 +#define DSP_RAM_SIZE_8960 0x1800000 +static int dspcrashd_pdata_8960 = 0xDEADDEAD; + +static struct resource resources_dspcrashd_8960[] = { + { + .name = "msm_dspcrashd", + .start = DSP_RAM_BASE_8960, + .end = DSP_RAM_BASE_8960 + DSP_RAM_SIZE_8960, + .flags = IORESOURCE_DMA, + }, +}; + +static struct platform_device msm_device_dspcrashd_8960 = { + .name = "msm_dspcrashd", + .num_resources = ARRAY_SIZE(resources_dspcrashd_8960), + .resource = resources_dspcrashd_8960, + .dev = { .platform_data = &dspcrashd_pdata_8960 }, +}; + +static struct memtype_reserve msm8930_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + + +static void __init reserve_rtb_memory(void) +{ +#if defined(CONFIG_MSM_RTB) + msm8930_reserve_table[MEMTYPE_EBI1].size += msm8930_rtb_pdata.size; +#endif +} + +static void __init size_pmem_devices(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + android_pmem_adsp_pdata.size = pmem_adsp_size; + android_pmem_pdata.size = pmem_size; + android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +} + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm8930_reserve_table[p->memory_type].size += p->size; +} +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + reserve_memory_for(&android_pmem_adsp_pdata); + reserve_memory_for(&android_pmem_pdata); + reserve_memory_for(&android_pmem_audio_pdata); +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ + msm8930_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif /*CONFIG_ANDROID_PMEM*/ +} + +static int msm8930_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +#define FMEM_ENABLED 0 +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_cp_heap_pdata cp_mm_msm8930_ion_pdata = { + .permission_type = IPT_TYPE_MM_CARVEOUT, + .align = PAGE_SIZE, + .reusable = FMEM_ENABLED, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_MIDDLE, +}; + +static struct ion_cp_heap_pdata cp_mfc_msm8930_ion_pdata = { + .permission_type = IPT_TYPE_MFC_SHAREDMEM, + .align = PAGE_SIZE, + .reusable = 0, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_HIGH, +}; + +static struct ion_co_heap_pdata co_msm8930_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, + .mem_is_fmem = 0, +}; + +static struct ion_co_heap_pdata fw_co_msm8930_ion_pdata = { + .adjacent_mem_id = ION_CP_MM_HEAP_ID, + .align = SZ_128K, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_LOW, +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. Due to + * video hardware restrictions and content protection the FW heap has to + * be allocated adjacent (below) the MM heap and the MFC heap has to be + * allocated after the MM heap to ensure MFC heap is not more than 256MB + * away from the base address of the FW heap. + * However, the order of FW heap and MM heap doesn't matter since these + * two heaps are taken care of by separate code to ensure they are adjacent + * to each other. + * Don't swap the order unless you know what you are doing! + */ +static struct ion_platform_data msm8930_ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + { + .id = ION_CP_MM_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MM_HEAP_NAME, + .size = MSM_ION_MM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mm_msm8930_ion_pdata, + }, + { + .id = ION_MM_FIRMWARE_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_MM_FIRMWARE_HEAP_NAME, + .size = MSM_ION_MM_FW_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &fw_co_msm8930_ion_pdata, + }, + { + .id = ION_CP_MFC_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MFC_HEAP_NAME, + .size = MSM_ION_MFC_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mfc_msm8930_ion_pdata, + }, +#ifndef CONFIG_MSM_IOMMU + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .size = MSM_ION_SF_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_msm8930_ion_pdata, + }, +#endif + { + .id = ION_IOMMU_HEAP_ID, + .type = ION_HEAP_TYPE_IOMMU, + .name = ION_IOMMU_HEAP_NAME, + }, + { + .id = ION_QSECOM_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_QSECOM_HEAP_NAME, + .size = MSM_ION_QSECOM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_msm8930_ion_pdata, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .size = MSM_ION_AUDIO_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_msm8930_ion_pdata, + }, +#endif + } +}; + +static struct platform_device msm8930_ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &msm8930_ion_pdata }, +}; +#endif + +struct platform_device msm8930_fmem_device = { + .name = "fmem", + .id = 1, + .dev = { .platform_data = &msm8930_fmem_pdata }, +}; + +static void __init reserve_mem_for_ion(enum ion_memory_types mem_type, + unsigned long size) +{ + msm8930_reserve_table[mem_type].size += size; +} + +static void __init msm8930_reserve_fixed_area(unsigned long fixed_area_size) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + int ret; + + if (fixed_area_size > MAX_FIXED_AREA_SIZE) + panic("fixed area size is larger than %dM\n", + MAX_FIXED_AREA_SIZE >> 20); + + reserve_info->fixed_area_size = fixed_area_size; + reserve_info->fixed_area_start = MSM8930_FW_START; + + ret = memblock_remove(reserve_info->fixed_area_start, + reserve_info->fixed_area_size); + BUG_ON(ret); +#endif +} + +/** + * Reserve memory for ION and calculate amount of reusable memory for fmem. + * We only reserve memory for heaps that are not reusable. However, we only + * support one reusable heap at the moment so we ignore the reusable flag for + * other than the first heap with reusable flag set. Also handle special case + * for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be + * at a higher address than FW in addition to not more than 256MB away from the + * base address of the firmware. This means that if MM is reusable the other + * two heaps must be allocated in the same region as FW. This is handled by the + * mem_is_fmem flag in the platform data. In addition the MM heap must be + * adjacent to the FW heap for content protection purposes. + */ +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + unsigned int i; + unsigned int reusable_count = 0; + unsigned int fixed_size = 0; + unsigned int fixed_low_size, fixed_middle_size, fixed_high_size; + unsigned long fixed_low_start, fixed_middle_start, fixed_high_start; + + msm8930_fmem_pdata.size = 0; + msm8930_fmem_pdata.reserved_size_low = 0; + msm8930_fmem_pdata.reserved_size_high = 0; + msm8930_fmem_pdata.align = PAGE_SIZE; + fixed_low_size = 0; + fixed_middle_size = 0; + fixed_high_size = 0; + + /* We only support 1 reusable heap. Check if more than one heap + * is specified as reusable and set as non-reusable if found. + */ + for (i = 0; i < msm8930_ion_pdata.nr; ++i) { + const struct ion_platform_heap *heap = + &(msm8930_ion_pdata.heaps[i]); + + if (heap->type == ION_HEAP_TYPE_CP && heap->extra_data) { + struct ion_cp_heap_pdata *data = heap->extra_data; + + reusable_count += (data->reusable) ? 1 : 0; + + if (data->reusable && reusable_count > 1) { + pr_err("%s: Too many heaps specified as " + "reusable. Heap %s was not configured " + "as reusable.\n", __func__, heap->name); + data->reusable = 0; + } + } + } + + for (i = 0; i < msm8930_ion_pdata.nr; ++i) { + const struct ion_platform_heap *heap = + &(msm8930_ion_pdata.heaps[i]); + + if (heap->extra_data) { + int fixed_position = NOT_FIXED; + int mem_is_fmem = 0; + + switch (heap->type) { + case ION_HEAP_TYPE_CP: + mem_is_fmem = ((struct ion_cp_heap_pdata *) + heap->extra_data)->mem_is_fmem; + fixed_position = ((struct ion_cp_heap_pdata *) + heap->extra_data)->fixed_position; + break; + case ION_HEAP_TYPE_CARVEOUT: + mem_is_fmem = ((struct ion_co_heap_pdata *) + heap->extra_data)->mem_is_fmem; + fixed_position = ((struct ion_co_heap_pdata *) + heap->extra_data)->fixed_position; + break; + default: + break; + } + + if (fixed_position != NOT_FIXED) + fixed_size += heap->size; + else + reserve_mem_for_ion(MEMTYPE_EBI1, heap->size); + + if (fixed_position == FIXED_LOW) + fixed_low_size += heap->size; + else if (fixed_position == FIXED_MIDDLE) + fixed_middle_size += heap->size; + else if (fixed_position == FIXED_HIGH) + fixed_high_size += heap->size; + + if (mem_is_fmem) + msm8930_fmem_pdata.size += heap->size; + } + } + + if (!fixed_size) + return; + + if (msm8930_fmem_pdata.size) { + msm8930_fmem_pdata.reserved_size_low = fixed_low_size + + HOLE_SIZE; + msm8930_fmem_pdata.reserved_size_high = fixed_high_size; + } + + /* Since the fixed area may be carved out of lowmem, + * make sure the length is a multiple of 1M. + */ + fixed_size = (fixed_size + MSM_MM_FW_SIZE + SECTION_SIZE - 1) + & SECTION_MASK; + msm8930_reserve_fixed_area(fixed_size); + + fixed_low_start = MSM8930_FIXED_AREA_START; + fixed_middle_start = fixed_low_start + fixed_low_size + HOLE_SIZE; + fixed_high_start = fixed_middle_start + fixed_middle_size; + + for (i = 0; i < msm8930_ion_pdata.nr; ++i) { + struct ion_platform_heap *heap = &(msm8930_ion_pdata.heaps[i]); + + if (heap->extra_data) { + int fixed_position = NOT_FIXED; + struct ion_cp_heap_pdata *pdata = NULL; + + switch (heap->type) { + case ION_HEAP_TYPE_CP: + pdata = + (struct ion_cp_heap_pdata *)heap->extra_data; + fixed_position = pdata->fixed_position; + break; + case ION_HEAP_TYPE_CARVEOUT: + fixed_position = ((struct ion_co_heap_pdata *) + heap->extra_data)->fixed_position; + break; + default: + break; + } + + switch (fixed_position) { + case FIXED_LOW: + heap->base = fixed_low_start; + break; + case FIXED_MIDDLE: + heap->base = fixed_middle_start; + pdata->secure_base = fixed_middle_start + - HOLE_SIZE; + pdata->secure_size = HOLE_SIZE + heap->size; + break; + case FIXED_HIGH: + heap->base = fixed_high_start; + break; + default: + break; + } + } + } +#endif +} + +static void __init reserve_mdp_memory(void) +{ + msm8930_mdp_writeback(msm8930_reserve_table); +} + +static void __init msm8930_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); + reserve_ion_memory(); + reserve_mdp_memory(); + reserve_rtb_memory(); +} + +static struct reserve_info msm8930_reserve_info __initdata = { + .memtype_reserve_table = msm8930_reserve_table, + .calculate_reserve_sizes = msm8930_calculate_reserve_sizes, + .reserve_fixed_area = msm8930_reserve_fixed_area, + .paddr_to_memtype = msm8930_paddr_to_memtype, +}; + +static int msm8930_memory_bank_size(void) +{ + return 1<<29; +} + +static void __init locate_unstable_memory(void) +{ + struct membank *mb = &meminfo.bank[meminfo.nr_banks - 1]; + unsigned long bank_size; + unsigned long low, high; + + bank_size = msm8930_memory_bank_size(); + low = meminfo.bank[0].start; + high = mb->start + mb->size; + + /* Check if 32 bit overflow occured */ + if (high < mb->start) + high -= PAGE_SIZE; + + if (high < MAX_FIXED_AREA_SIZE + MSM8930_FIXED_AREA_START) + panic("fixed area extends beyond end of memory\n"); + + low &= ~(bank_size - 1); + + if (high - low <= bank_size) + goto no_dmm; + + msm8930_reserve_info.bank_size = bank_size; +#ifdef CONFIG_ENABLE_DMM + msm8930_reserve_info.low_unstable_address = mb->start - + MIN_MEMORY_BLOCK_SIZE + mb->size; + msm8930_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE; + pr_info("low unstable address %lx max size %lx bank size %lx\n", + msm8930_reserve_info.low_unstable_address, + msm8930_reserve_info.max_unstable_size, + msm8930_reserve_info.bank_size); + return; +#endif +no_dmm: + msm8930_reserve_info.low_unstable_address = high; + msm8930_reserve_info.max_unstable_size = 0; +} + +static void __init place_movable_zone(void) +{ +#ifdef CONFIG_ENABLE_DMM + movable_reserved_start = msm8930_reserve_info.low_unstable_address; + movable_reserved_size = msm8930_reserve_info.max_unstable_size; + pr_info("movable zone start %lx size %lx\n", + movable_reserved_start, movable_reserved_size); +#endif +} + +static void __init msm8930_early_memory(void) +{ + reserve_info = &msm8930_reserve_info; + locate_unstable_memory(); + place_movable_zone(); +} + +static void __init msm8930_reserve(void) +{ + msm_reserve(); + if (msm8930_fmem_pdata.size) { +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + if (reserve_info->fixed_area_size) { + msm8930_fmem_pdata.phys = + reserve_info->fixed_area_start + MSM_MM_FW_SIZE; + pr_info("mm fw at %lx (fixed) size %x\n", + reserve_info->fixed_area_start, MSM_MM_FW_SIZE); + pr_info("fmem start %lx (fixed) size %lx\n", + msm8930_fmem_pdata.phys, msm8930_fmem_pdata.size); + } +#endif + } +} + +static int msm8930_change_memory_power(u64 start, u64 size, + int change_type) +{ + return soc_change_memory_power(start, size, change_type); +} + +static void __init msm8930_allocate_memory_regions(void) +{ + msm8930_allocate_fb_region(); +} + +#ifdef CONFIG_WCD9304_CODEC + +#define SITAR_INTERRUPT_BASE (NR_MSM_IRQS + NR_GPIO_IRQS + NR_PM8921_IRQS) + +/* Micbias setting is based on 8660 CDP/MTP/FLUID requirement + * 4 micbiases are used to power various analog and digital + * microphones operating at 1800 mV. Technically, all micbiases + * can source from single cfilter since all microphones operate + * at the same voltage level. The arrangement below is to make + * sure all cfilters are exercised. LDO_H regulator ouput level + * does not need to be as high as 2.85V. It is choosen for + * microphone sensitivity purpose. + */ +static struct wcd9xxx_pdata sitar_platform_data = { + .slimbus_slave_device = { + .name = "sitar-slave", + .e_addr = {0, 0, 0x00, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(62), + .irq_base = SITAR_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = 42, + .micbias = { + .ldoh_v = SITAR_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 1800, + .bias1_cfilt_sel = SITAR_CFILT1_SEL, + .bias2_cfilt_sel = SITAR_CFILT2_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1950000, + .max_uV = 2200000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1200000, + .max_uV = 1200000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1200000, + .max_uV = 1200000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device msm_slim_sitar = { + .name = "sitar-slim", + .e_addr = {0, 1, 0x00, 0, 0x17, 2}, + .dev = { + .platform_data = &sitar_platform_data, + }, +}; + +static struct wcd9xxx_pdata sitar1p1_platform_data = { + .slimbus_slave_device = { + .name = "sitar-slave", + .e_addr = {0, 0, 0x70, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(62), + .irq_base = SITAR_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = 42, + .micbias = { + .ldoh_v = SITAR_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 1800, + .bias1_cfilt_sel = SITAR_CFILT1_SEL, + .bias2_cfilt_sel = SITAR_CFILT2_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1950000, + .max_uV = 2200000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1200000, + .max_uV = 1200000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1200000, + .max_uV = 1200000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device msm_slim_sitar1p1 = { + .name = "sitar1p1-slim", + .e_addr = {0, 1, 0x70, 0, 0x17, 2}, + .dev = { + .platform_data = &sitar1p1_platform_data, + }, +}; +#endif + + +static struct slim_boardinfo msm_slim_devices[] = { +#ifdef CONFIG_WCD9304_CODEC + { + .bus_num = 1, + .slim_slave = &msm_slim_sitar, + }, + { + .bus_num = 1, + .slim_slave = &msm_slim_sitar1p1, + }, +#endif + /* add more slimbus slaves as needed */ +}; + +#define MSM_WCNSS_PHYS 0x03000000 +#define MSM_WCNSS_SIZE 0x280000 + +static struct resource resources_wcnss_wlan[] = { + { + .start = RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + .end = RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + .name = "wcnss_wlanrx_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, + .end = RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, + .name = "wcnss_wlantx_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_WCNSS_PHYS, + .end = MSM_WCNSS_PHYS + MSM_WCNSS_SIZE - 1, + .name = "wcnss_mmio", + .flags = IORESOURCE_MEM, + }, + { + .start = 84, + .end = 88, + .name = "wcnss_gpios_5wire", + .flags = IORESOURCE_IO, + }, +}; + +static struct qcom_wcnss_opts qcom_wcnss_pdata = { + .has_48mhz_xo = 1, +}; + +static struct platform_device msm_device_wcnss_wlan = { + .name = "wcnss_wlan", + .id = 0, + .num_resources = ARRAY_SIZE(resources_wcnss_wlan), + .resource = resources_wcnss_wlan, + .dev = {.platform_data = &qcom_wcnss_pdata}, +}; + +#ifdef CONFIG_QSEECOM +/* qseecom bus scaling */ +static struct msm_bus_vectors qseecom_clks_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 0, + .ab = 0, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = 0, + .ab = 0, + }, +}; + +static struct msm_bus_vectors qseecom_enable_dfab_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = (492 * 8) * 1000000UL, + .ab = (492 * 8) * 100000UL, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = 0, + .ab = 0, + }, +}; + +static struct msm_bus_vectors qseecom_enable_sfpb_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 0, + .ab = 0, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = (64 * 8) * 1000000UL, + .ab = (64 * 8) * 100000UL, + }, +}; + +static struct msm_bus_paths qseecom_hw_bus_scale_usecases[] = { + { + ARRAY_SIZE(qseecom_clks_init_vectors), + qseecom_clks_init_vectors, + }, + { + ARRAY_SIZE(qseecom_enable_dfab_vectors), + qseecom_enable_sfpb_vectors, + }, + { + ARRAY_SIZE(qseecom_enable_sfpb_vectors), + qseecom_enable_sfpb_vectors, + }, +}; + +static struct msm_bus_scale_pdata qseecom_bus_pdata = { + qseecom_hw_bus_scale_usecases, + ARRAY_SIZE(qseecom_hw_bus_scale_usecases), + .name = "qsee", +}; + +static struct platform_device qseecom_device = { + .name = "qseecom", + .id = 0, + .dev = { + .platform_data = &qseecom_bus_pdata, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +#define QCE_SIZE 0x10000 +#define QCE_0_BASE 0x18500000 + +#define QCE_HW_KEY_SUPPORT 0 +#define QCE_SHA_HMAC_SUPPORT 1 +#define QCE_SHARE_CE_RESOURCE 1 +#define QCE_CE_SHARED 0 + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, +}; + +static struct platform_device qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, +}; + +static struct platform_device qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; +#endif + +#define MDM2AP_ERRFATAL 70 +#define AP2MDM_ERRFATAL 95 +#define MDM2AP_STATUS 69 +#define AP2MDM_STATUS 94 +#define AP2MDM_PMIC_RESET_N 80 +#define AP2MDM_KPDPWR_N 81 + +static struct resource mdm_resources[] = { + { + .start = MDM2AP_ERRFATAL, + .end = MDM2AP_ERRFATAL, + .name = "MDM2AP_ERRFATAL", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_ERRFATAL, + .end = AP2MDM_ERRFATAL, + .name = "AP2MDM_ERRFATAL", + .flags = IORESOURCE_IO, + }, + { + .start = MDM2AP_STATUS, + .end = MDM2AP_STATUS, + .name = "MDM2AP_STATUS", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_STATUS, + .end = AP2MDM_STATUS, + .name = "AP2MDM_STATUS", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_PMIC_RESET_N, + .end = AP2MDM_PMIC_RESET_N, + .name = "AP2MDM_PMIC_RESET_N", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_KPDPWR_N, + .end = AP2MDM_KPDPWR_N, + .name = "AP2MDM_KPDPWR_N", + .flags = IORESOURCE_IO, + }, +}; + +static struct mdm_platform_data mdm_platform_data = { + .mdm_version = "2.5", +}; + +static struct platform_device mdm_device = { + .name = "mdm2_modem", + .id = -1, + .num_resources = ARRAY_SIZE(mdm_resources), + .resource = mdm_resources, + .dev = { + .platform_data = &mdm_platform_data, + }, +}; + +static struct platform_device *mdm_devices[] __initdata = { + &mdm_device, +}; + +#ifdef CONFIG_MSM_MPM +static uint16_t msm_mpm_irqs_m2a[MSM_MPM_NR_MPM_IRQS] __initdata = { + [1] = MSM_GPIO_TO_INT(46), + [2] = MSM_GPIO_TO_INT(150), + [4] = MSM_GPIO_TO_INT(103), + [5] = MSM_GPIO_TO_INT(104), + [6] = MSM_GPIO_TO_INT(105), + [7] = MSM_GPIO_TO_INT(106), + [8] = MSM_GPIO_TO_INT(107), + [9] = MSM_GPIO_TO_INT(7), + [10] = MSM_GPIO_TO_INT(11), + [11] = MSM_GPIO_TO_INT(15), + [12] = MSM_GPIO_TO_INT(19), + [13] = MSM_GPIO_TO_INT(23), + [14] = MSM_GPIO_TO_INT(27), + [15] = MSM_GPIO_TO_INT(31), + [16] = MSM_GPIO_TO_INT(35), + [19] = MSM_GPIO_TO_INT(90), + [20] = MSM_GPIO_TO_INT(92), + [23] = MSM_GPIO_TO_INT(85), + [24] = MSM_GPIO_TO_INT(83), + [25] = USB1_HS_IRQ, + [27] = HDMI_IRQ, + [29] = MSM_GPIO_TO_INT(10), + [30] = MSM_GPIO_TO_INT(102), + [31] = MSM_GPIO_TO_INT(81), + [32] = MSM_GPIO_TO_INT(78), + [33] = MSM_GPIO_TO_INT(94), + [34] = MSM_GPIO_TO_INT(72), + [35] = MSM_GPIO_TO_INT(39), + [36] = MSM_GPIO_TO_INT(43), + [37] = MSM_GPIO_TO_INT(61), + [38] = MSM_GPIO_TO_INT(50), + [39] = MSM_GPIO_TO_INT(42), + [41] = MSM_GPIO_TO_INT(62), + [42] = MSM_GPIO_TO_INT(76), + [43] = MSM_GPIO_TO_INT(75), + [44] = MSM_GPIO_TO_INT(70), + [45] = MSM_GPIO_TO_INT(69), + [46] = MSM_GPIO_TO_INT(67), + [47] = MSM_GPIO_TO_INT(65), + [48] = MSM_GPIO_TO_INT(58), + [49] = MSM_GPIO_TO_INT(54), + [50] = MSM_GPIO_TO_INT(52), + [51] = MSM_GPIO_TO_INT(49), + [52] = MSM_GPIO_TO_INT(40), + [53] = MSM_GPIO_TO_INT(37), + [54] = MSM_GPIO_TO_INT(24), + [55] = MSM_GPIO_TO_INT(14), +}; + +static uint16_t msm_mpm_bypassed_apps_irqs[] __initdata = { + TLMM_MSM_SUMMARY_IRQ, + RPM_APCC_CPU0_GP_HIGH_IRQ, + RPM_APCC_CPU0_GP_MEDIUM_IRQ, + RPM_APCC_CPU0_GP_LOW_IRQ, + RPM_APCC_CPU0_WAKE_UP_IRQ, + RPM_APCC_CPU1_GP_HIGH_IRQ, + RPM_APCC_CPU1_GP_MEDIUM_IRQ, + RPM_APCC_CPU1_GP_LOW_IRQ, + RPM_APCC_CPU1_WAKE_UP_IRQ, + MSS_TO_APPS_IRQ_0, + MSS_TO_APPS_IRQ_1, + MSS_TO_APPS_IRQ_2, + MSS_TO_APPS_IRQ_3, + MSS_TO_APPS_IRQ_4, + MSS_TO_APPS_IRQ_5, + MSS_TO_APPS_IRQ_6, + MSS_TO_APPS_IRQ_7, + MSS_TO_APPS_IRQ_8, + MSS_TO_APPS_IRQ_9, + LPASS_SCSS_GP_LOW_IRQ, + LPASS_SCSS_GP_MEDIUM_IRQ, + LPASS_SCSS_GP_HIGH_IRQ, + SPS_MTI_30, + SPS_MTI_31, + RIVA_APSS_SPARE_IRQ, + RIVA_APPS_WLAN_SMSM_IRQ, + RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, +}; + +struct msm_mpm_device_data msm8930_mpm_dev_data __initdata = { + .irqs_m2a = msm_mpm_irqs_m2a, + .irqs_m2a_size = ARRAY_SIZE(msm_mpm_irqs_m2a), + .bypassed_apps_irqs = msm_mpm_bypassed_apps_irqs, + .bypassed_apps_irqs_size = ARRAY_SIZE(msm_mpm_bypassed_apps_irqs), + .mpm_request_reg_base = MSM_RPM_BASE + 0x9d8, + .mpm_status_reg_base = MSM_RPM_BASE + 0xdf8, + .mpm_apps_ipc_reg = MSM_APCS_GCC_BASE + 0x008, + .mpm_apps_ipc_val = BIT(1), + .mpm_ipc_irq = RPM_APCC_CPU0_GP_MEDIUM_IRQ, + +}; +#endif + +#define MSM_SHARED_RAM_PHYS 0x80000000 + +static void __init msm8930_map_io(void) +{ + msm_shared_ram_phys = MSM_SHARED_RAM_PHYS; + msm_map_msm8930_io(); + + if (socinfo_init() < 0) + pr_err("socinfo_init() failed!\n"); +} + +static void __init msm8930_init_irq(void) +{ + struct msm_mpm_device_data *data = NULL; +#ifdef CONFIG_MSM_MPM + data = &msm8930_mpm_dev_data; +#endif + + msm_mpm_irq_extn_init(data); + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, + (void *)MSM_QGIC_CPU_BASE); +} + +static void __init msm8930_init_buses(void) +{ +#ifdef CONFIG_MSM_BUS_SCALING + msm_bus_rpm_set_mt_mask(); + msm_bus_8930_apps_fabric_pdata.rpm_enabled = 1; + msm_bus_8930_sys_fabric_pdata.rpm_enabled = 1; + msm_bus_8930_mm_fabric_pdata.rpm_enabled = 1; + msm_bus_8930_apps_fabric.dev.platform_data = + &msm_bus_8930_apps_fabric_pdata; + msm_bus_8930_sys_fabric.dev.platform_data = + &msm_bus_8930_sys_fabric_pdata; + msm_bus_8930_mm_fabric.dev.platform_data = + &msm_bus_8930_mm_fabric_pdata; + msm_bus_8930_sys_fpb.dev.platform_data = &msm_bus_8930_sys_fpb_pdata; + msm_bus_8930_cpss_fpb.dev.platform_data = &msm_bus_8930_cpss_fpb_pdata; +#endif +} + +static struct msm_spi_platform_data msm8960_qup_spi_gsbi1_pdata = { + .max_clock_speed = 15060000, +}; + +#ifdef CONFIG_USB_MSM_OTG_72K +static struct msm_otg_platform_data msm_otg_pdata; +#else +#ifdef CONFIG_MSM_BUS_SCALING +/* Bandwidth requests (zero) if no vote placed */ +static struct msm_bus_vectors usb_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +/* Bus bandwidth requests in Bytes/sec */ +static struct msm_bus_vectors usb_max_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 60000000, /* At least 480Mbps on bus. */ + .ib = 960000000, /* MAX bursts rate */ + }, +}; + +static struct msm_bus_paths usb_bus_scale_usecases[] = { + { + ARRAY_SIZE(usb_init_vectors), + usb_init_vectors, + }, + { + ARRAY_SIZE(usb_max_vectors), + usb_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata usb_bus_scale_pdata = { + usb_bus_scale_usecases, + ARRAY_SIZE(usb_bus_scale_usecases), + .name = "usb", +}; +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { + .mode = USB_OTG, + .otg_control = OTG_PMIC_CONTROL, + .phy_type = SNPS_28NM_INTEGRATED_PHY, + .pmic_id_irq = PM8038_USB_ID_IN_IRQ(PM8038_IRQ_BASE), + .power_budget = 750, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &usb_bus_scale_pdata, +#endif +}; +#endif + +#define PID_MAGIC_ID 0x71432909 +#define SERIAL_NUM_MAGIC_ID 0x61945374 +#define SERIAL_NUMBER_LENGTH 127 +#define DLOAD_USB_BASE_ADD 0x2A03F0C8 + +struct magic_num_struct { + uint32_t pid; + uint32_t serial_num; +}; + +struct dload_struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint16_t reserved4; + uint16_t pid; + char serial_number[SERIAL_NUMBER_LENGTH]; + uint16_t reserved5; + struct magic_num_struct magic_struct; +}; + +static int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) +{ + struct dload_struct __iomem *dload = 0; + + dload = ioremap(DLOAD_USB_BASE_ADD, sizeof(*dload)); + if (!dload) { + pr_err("%s: cannot remap I/O memory region: %08x\n", + __func__, DLOAD_USB_BASE_ADD); + return -ENXIO; + } + + pr_debug("%s: dload:%p pid:%x serial_num:%s\n", + __func__, dload, pid, snum); + /* update pid */ + dload->magic_struct.pid = PID_MAGIC_ID; + dload->pid = pid; + + /* update serial number */ + dload->magic_struct.serial_num = 0; + if (!snum) { + memset(dload->serial_number, 0, SERIAL_NUMBER_LENGTH); + goto out; + } + + dload->magic_struct.serial_num = SERIAL_NUM_MAGIC_ID; + strlcpy(dload->serial_number, snum, SERIAL_NUMBER_LENGTH); +out: + iounmap(dload); + return 0; +} + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static uint8_t spm_wfi_cmd_sequence[] __initdata = { + 0x03, 0x0f, +}; + +static uint8_t spm_power_collapse_without_rpm[] __initdata = { + 0x00, 0x24, 0x54, 0x10, + 0x09, 0x03, 0x01, + 0x10, 0x54, 0x30, 0x0C, + 0x24, 0x30, 0x0f, +}; + +static uint8_t spm_power_collapse_with_rpm[] __initdata = { + 0x00, 0x24, 0x54, 0x10, + 0x09, 0x07, 0x01, 0x0B, + 0x10, 0x54, 0x30, 0x0C, + 0x24, 0x30, 0x0f, +}; + +static struct msm_spm_seq_entry msm_spm_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_MODE_CLOCK_GATING, + .notify_rpm = false, + .cmd = spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = false, + .cmd = spm_power_collapse_without_rpm, + }, + [2] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = spm_power_collapse_with_rpm, + }, +}; + +static struct msm_spm_platform_data msm_spm_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, + [1] = { + .reg_base_addr = MSM_SAW1_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, +}; + +static uint8_t l2_spm_wfi_cmd_sequence[] __initdata = { + 0x00, 0x20, 0x03, 0x20, + 0x00, 0x0f, +}; + +static uint8_t l2_spm_gdhs_cmd_sequence[] __initdata = { + 0x00, 0x20, 0x34, 0x64, + 0x48, 0x07, 0x48, 0x20, + 0x50, 0x64, 0x04, 0x34, + 0x50, 0x0f, +}; +static uint8_t l2_spm_power_off_cmd_sequence[] __initdata = { + 0x00, 0x10, 0x34, 0x64, + 0x48, 0x07, 0x48, 0x10, + 0x50, 0x64, 0x04, 0x34, + 0x50, 0x0F, +}; + +static struct msm_spm_seq_entry msm_spm_l2_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_L2_MODE_RETENTION, + .notify_rpm = false, + .cmd = l2_spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_L2_MODE_GDHS, + .notify_rpm = true, + .cmd = l2_spm_gdhs_cmd_sequence, + }, + [2] = { + .mode = MSM_SPM_L2_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = l2_spm_power_off_cmd_sequence, + }, +}; + +static struct msm_spm_platform_data msm_spm_l2_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW_L2_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x00A000AE, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x00A00020, + .modes = msm_spm_l2_seq_list, + .num_modes = ARRAY_SIZE(msm_spm_l2_seq_list), + }, +}; + +#define ISA1200_HAP_EN_GPIO 77 +#define ISA1200_HAP_LEN_GPIO 78 +#define ISA1200_HAP_CLK PM8038_GPIO_PM_TO_SYS(7) + +static int isa1200_power(int on) +{ + int rc = 0; + + gpio_set_value_cansleep(ISA1200_HAP_CLK, !!on); + + if (on) + rc = pm8xxx_aux_clk_control(CLK_MP3_1, XO_DIV_1, true); + else + rc = pm8xxx_aux_clk_control(CLK_MP3_1, XO_DIV_NONE, true); + + if (rc) { + pr_err("%s: unable to write aux clock register(%d)\n", + __func__, rc); + } + + return rc; +} + +static int isa1200_dev_setup(bool enable) +{ + int rc = 0; + + if (!enable) + goto fail_gpio_dir; + + rc = gpio_request(ISA1200_HAP_CLK, "haptics_clk"); + if (rc) { + pr_err("%s: gpio_request for %d gpio failed rc(%d)\n", + __func__, ISA1200_HAP_CLK, rc); + goto fail_gpio_req; + } + + rc = gpio_direction_output(ISA1200_HAP_CLK, 0); + if (rc) { + pr_err("%s: gpio_direction_output failed for %d gpio rc(%d)\n", + __func__, ISA1200_HAP_CLK, rc); + goto fail_gpio_dir; + } + + return 0; + +fail_gpio_dir: + gpio_free(ISA1200_HAP_CLK); +fail_gpio_req: + return rc; + +} + +static struct isa1200_regulator isa1200_reg_data[] = { + { + .name = "vddp", + .min_uV = ISA_I2C_VTG_MIN_UV, + .max_uV = ISA_I2C_VTG_MAX_UV, + .load_uA = ISA_I2C_CURR_UA, + }, + { + .name = "vcc_i2c", + .min_uV = ISA_I2C_VTG_MIN_UV, + .max_uV = ISA_I2C_VTG_MAX_UV, + .load_uA = ISA_I2C_CURR_UA, + }, +}; + +static struct isa1200_platform_data isa1200_1_pdata = { + .name = "vibrator", + .dev_setup = isa1200_dev_setup, + .power_on = isa1200_power, + .hap_en_gpio = ISA1200_HAP_EN_GPIO, + .hap_len_gpio = ISA1200_HAP_LEN_GPIO, + .max_timeout = 15000, + .mode_ctrl = PWM_GEN_MODE, + .pwm_fd = { + .pwm_div = 256, + }, + .is_erm = false, + .smart_en = true, + .ext_clk_en = true, + .chip_en = 1, + .regulator_info = isa1200_reg_data, + .num_regulators = ARRAY_SIZE(isa1200_reg_data), +}; + +static struct i2c_board_info msm_isa1200_board_info[] __initdata = { + { + I2C_BOARD_INFO("isa1200_1", 0x90>>1), + .platform_data = &isa1200_1_pdata, + }, +}; + +#define MXT_TS_GPIO_IRQ 11 +#define MXT_TS_RESET_GPIO 52 + +static const u8 mxt_config_data_8930[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 15, 3, 0, 15, 12, 11, 0, 0, + /* T7 Object */ + 32, 16, 50, + /* T8 Object */ + 30, 0, 5, 1, 0, 0, 8, 8, 0, 0, + /* T9 Object */ + 131, 0, 0, 19, 11, 0, 16, 43, 2, 3, + 10, 7, 2, 0, 4, 5, 35, 10, 43, 4, + 54, 2, 15, 32, 38, 38, 143, 40, 143, 80, + 7, 9, 50, 50, 2, + /* T15 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, + /* T18 Object */ + 0, 0, + /* T19 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + /* T23 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /* T25 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T42 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* T46 Object */ + 0, 3, 8, 16, 0, 0, 1, 0, 0, + /* T47 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T48 Object */ + 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 100, 4, 64, + 0, 0, 5, 42, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +static ssize_t mxt224e_vkeys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, 200, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":57:1030:90:90" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":206:1030:90:90" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":366:1030:90:90" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":503:1030:90:90" + "\n"); +} + +static struct kobj_attribute mxt224e_vkeys_attr = { + .attr = { + .mode = S_IRUGO, + }, + .show = &mxt224e_vkeys_show, +}; + +static struct attribute *mxt224e_properties_attrs[] = { + &mxt224e_vkeys_attr.attr, + NULL +}; + +static struct attribute_group mxt224e_properties_attr_group = { + .attrs = mxt224e_properties_attrs, +}; + +static void mxt_init_vkeys_8930(void) +{ + int rc = 0; + static struct kobject *mxt224e_properties_kobj; + + mxt224e_vkeys_attr.attr.name = "virtualkeys.atmel_mxt_ts"; + mxt224e_properties_kobj = kobject_create_and_add("board_properties", + NULL); + if (mxt224e_properties_kobj) + rc = sysfs_create_group(mxt224e_properties_kobj, + &mxt224e_properties_attr_group); + if (!mxt224e_properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", + __func__); + + return; +} + +static struct mxt_config_info mxt_config_array[] = { + { + .config = mxt_config_data_8930, + .config_length = ARRAY_SIZE(mxt_config_data_8930), + .family_id = 0x81, + .variant_id = 0x01, + .version = 0x10, + .build = 0xAA, + }, +}; + +static struct mxt_platform_data mxt_platform_data_8930 = { + .config_array = mxt_config_array, + .config_array_size = ARRAY_SIZE(mxt_config_array), + .panel_minx = 0, + .panel_maxx = 566, + .panel_miny = 0, + .panel_maxy = 1067, + .disp_minx = 0, + .disp_maxx = 540, + .disp_miny = 0, + .disp_maxy = 960, + .irqflags = IRQF_TRIGGER_FALLING, +#ifdef MSM8930_PHASE_2 + .digital_pwr_regulator = true, +#endif + .i2c_pull_up = true, + .reset_gpio = MXT_TS_RESET_GPIO, + .irq_gpio = MXT_TS_GPIO_IRQ, +}; + +static struct i2c_board_info mxt_device_info_8930[] __initdata = { + { + I2C_BOARD_INFO("atmel_mxt_ts", 0x4a), + .platform_data = &mxt_platform_data_8930, + .irq = MSM_GPIO_TO_INT(MXT_TS_GPIO_IRQ), + }, +}; + +#ifdef MSM8930_PHASE_2 + +#define GPIO_VOLUME_UP PM8038_GPIO_PM_TO_SYS(3) +#define GPIO_VOLUME_DOWN PM8038_GPIO_PM_TO_SYS(8) +#define GPIO_CAMERA_SNAPSHOT PM8038_GPIO_PM_TO_SYS(10) +#define GPIO_CAMERA_FOCUS PM8038_GPIO_PM_TO_SYS(11) + +static struct gpio_keys_button keys_8930[] = { + { + .code = KEY_VOLUMEUP, + .type = EV_KEY, + .desc = "volume_up", + .gpio = GPIO_VOLUME_UP, + .wakeup = 1, + .active_low = 1, + .debounce_interval = 15, + }, + { + .code = KEY_VOLUMEDOWN, + .type = EV_KEY, + .desc = "volume_down", + .gpio = GPIO_VOLUME_DOWN, + .wakeup = 1, + .active_low = 1, + .debounce_interval = 15, + }, + { + .code = KEY_CAMERA_FOCUS, + .type = EV_KEY, + .desc = "camera_focus", + .gpio = GPIO_CAMERA_FOCUS, + .wakeup = 1, + .active_low = 1, + .debounce_interval = 15, + }, + { + .code = KEY_CAMERA_SNAPSHOT, + .type = EV_KEY, + .desc = "camera_snapshot", + .gpio = GPIO_CAMERA_SNAPSHOT, + .wakeup = 1, + .active_low = 1, + .debounce_interval = 15, + }, +}; + +/* Add GPIO keys for 8930 */ +static struct gpio_keys_platform_data gpio_keys_8930_pdata = { + .buttons = keys_8930, + .nbuttons = 4, +}; + +static struct platform_device gpio_keys_8930 = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_keys_8930_pdata, + }, +}; +#endif /* MSM8930_PHASE_2 */ + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi4_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi3_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi9_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi10_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi12_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + + +static struct ks8851_pdata spi_eth_pdata = { + .irq_gpio = KS8851_IRQ_GPIO, + .rst_gpio = KS8851_RST_GPIO, +}; + +static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "ks8851", + .irq = MSM_GPIO_TO_INT(KS8851_IRQ_GPIO), + .max_speed_hz = 19200000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = &spi_eth_pdata + }, + { + .modalias = "dsi_novatek_3d_panel_spi", + .max_speed_hz = 10800000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + }, +}; + +static struct platform_device msm_device_saw_core0 = { + .name = "saw-regulator", + .id = 0, + .dev = { + .platform_data = &msm8930_saw_regulator_core0_pdata, + }, +}; + +static struct platform_device msm_device_saw_core1 = { + .name = "saw-regulator", + .id = 1, + .dev = { + .platform_data = &msm8930_saw_regulator_core1_pdata, + }, +}; + +static struct tsens_platform_data msm_tsens_pdata = { + .tsens_factor = 1000, + .hw_type = APQ_8064, + .tsens_num_sensor = 10, + .slope = {1132, 1135, 1137, 1135, 1157, + 1142, 1124, 1153, 1175, 1166}, +}; + +static struct platform_device msm_tsens_device = { + .name = "tsens8960-tm", + .id = -1, +}; + +#ifdef CONFIG_MSM_FAKE_BATTERY +static struct platform_device fish_battery_device = { + .name = "fish_battery", +}; +#endif + +#ifndef MSM8930_PHASE_2 + +/* 8930 Phase 1 */ +static struct platform_device msm8930_device_ext_5v_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_MPP_PM_TO_SYS(7), + .dev = { + .platform_data = &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_5V], + }, +}; + +static struct platform_device msm8930_device_ext_l2_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 91, + .dev = { + .platform_data = &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_L2], + }, +}; + +#else + +/* 8930 Phase 2 */ +static struct platform_device msm8930_device_ext_5v_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 63, + .dev = { + .platform_data = + &msm8930_gpio_regulator_pdata[MSM8930_GPIO_VREG_ID_EXT_5V], + }, +}; + +static struct platform_device msm8930_device_ext_otg_sw_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 97, + .dev = { + .platform_data = + &msm8930_gpio_regulator_pdata[MSM8930_GPIO_VREG_ID_EXT_OTG_SW], + }, +}; + +#endif + +static struct platform_device msm8930_device_rpm_regulator __devinitdata = { + .name = "rpm-regulator", + .id = -1, + .dev = { +#ifndef MSM8930_PHASE_2 + .platform_data = &msm_rpm_regulator_pdata, +#else + .platform_data = &msm8930_rpm_regulator_pdata, +#endif + }, +}; + +static struct platform_device *common_devices[] __initdata = { + &msm8960_device_dmov, + &msm_device_smd, + &msm8960_device_uart_gsbi5, + &msm_device_uart_dm6, + &msm_device_saw_core0, + &msm_device_saw_core1, + &msm8930_device_ext_5v_vreg, +#ifndef MSM8930_PHASE_2 + &msm8930_device_ext_l2_vreg, +#endif + &msm8960_device_ssbi_pmic, +#ifdef MSM8930_PHASE_2 + &msm8930_device_ext_otg_sw_vreg, +#endif + &msm_8960_q6_lpass, + &msm_8960_q6_mss_fw, + &msm_8960_q6_mss_sw, + &msm_8960_riva, + &msm_pil_tzapps, + &msm_pil_vidc, + &msm8960_device_qup_spi_gsbi1, + &msm8960_device_qup_i2c_gsbi3, + &msm8960_device_qup_i2c_gsbi4, + &msm8960_device_qup_i2c_gsbi9, + &msm8960_device_qup_i2c_gsbi10, + &msm8960_device_qup_i2c_gsbi12, + &msm_slim_ctrl, + &msm_device_wcnss_wlan, +#if defined(CONFIG_QSEECOM) + &qseecom_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &qcedev_device, +#endif +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif + &msm_device_sps, +#ifdef CONFIG_MSM_FAKE_BATTERY + &fish_battery_device, +#endif +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + &msm8930_android_pmem_device, + &msm8930_android_pmem_adsp_device, + &msm8930_android_pmem_audio_device, +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + &msm8930_fmem_device, + &msm_device_bam_dmux, + &msm_fm_platform_init, + +#ifdef CONFIG_HW_RANDOM_MSM + &msm_device_rng, +#endif + &msm8930_rpm_device, + &msm8930_rpm_log_device, + &msm8930_rpm_stat_device, +#ifdef CONFIG_ION_MSM + &msm8930_ion_dev, +#endif + &msm_device_tz_log, + +#ifdef CONFIG_MSM_QDSS + &msm_qdss_device, + &msm_etb_device, + &msm_tpiu_device, + &msm_funnel_device, + &msm_etm_device, +#endif + &msm_device_dspcrashd_8960, + &msm8960_device_watchdog, +#ifdef MSM8930_PHASE_2 + &gpio_keys_8930, +#endif + &msm8930_rtb_device, + &msm8930_cpu_idle_device, + &msm8930_msm_gov_device, + &msm_bus_8930_apps_fabric, + &msm_bus_8930_sys_fabric, + &msm_bus_8930_mm_fabric, + &msm_bus_8930_sys_fpb, + &msm_bus_8930_cpss_fpb, + &msm8960_device_cache_erp, + &msm8930_iommu_domain_device, + &msm_tsens_device, +}; + +static struct platform_device *cdp_devices[] __initdata = { + &msm8960_device_otg, + &msm8960_device_gadget_peripheral, + &msm_device_hsusb_host, + &android_usb_device, + &msm_pcm, + &msm_pcm_routing, + &msm_cpudai0, + &msm_cpudai1, + &msm_cpudai_hdmi_rx, + &msm_cpudai_bt_rx, + &msm_cpudai_bt_tx, + &msm_cpudai_fm_rx, + &msm_cpudai_fm_tx, + &msm_cpudai_auxpcm_rx, + &msm_cpudai_auxpcm_tx, + &msm_cpu_fe, + &msm_stub_codec, +#ifdef CONFIG_MSM_GEMINI + &msm8960_gemini_device, +#endif + &msm_voice, + &msm_voip, + &msm_lpa_pcm, + &msm_cpudai_afe_01_rx, + &msm_cpudai_afe_01_tx, + &msm_cpudai_afe_02_rx, + &msm_cpudai_afe_02_tx, + &msm_pcm_afe, + &msm_compr_dsp, + &msm_cpudai_incall_music_rx, + &msm_cpudai_incall_record_rx, + &msm_cpudai_incall_record_tx, + &msm_pcm_hostless, +}; + +static void __init msm8930_i2c_init(void) +{ + msm8960_device_qup_i2c_gsbi4.dev.platform_data = + &msm8960_i2c_qup_gsbi4_pdata; + + msm8960_device_qup_i2c_gsbi3.dev.platform_data = + &msm8960_i2c_qup_gsbi3_pdata; + + msm8960_device_qup_i2c_gsbi9.dev.platform_data = + &msm8960_i2c_qup_gsbi9_pdata; + + msm8960_device_qup_i2c_gsbi10.dev.platform_data = + &msm8960_i2c_qup_gsbi10_pdata; + + msm8960_device_qup_i2c_gsbi12.dev.platform_data = + &msm8960_i2c_qup_gsbi12_pdata; +} + +static struct msm_rpmrs_level msm_rpmrs_levels[] __initdata = { + { + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1, 784, 180000, 100, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1300, 228, 1200000, 2000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, GDHS, MAX, ACTIVE), + false, + 2000, 138, 1208400, 3200, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 6000, 119, 1850300, 9000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, GDHS, MAX, ACTIVE), + false, + 9200, 68, 2839200, 16400, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE), + false, + 10300, 63, 3128000, 18200, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 18000, 10, 4602600, 27000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW), + false, + 20000, 2, 5752000, 32000, + }, +}; + +static struct msm_rpmrs_platform_data msm_rpmrs_data __initdata = { + .levels = &msm_rpmrs_levels[0], + .num_levels = ARRAY_SIZE(msm_rpmrs_levels), + .vdd_mem_levels = { + [MSM_RPMRS_VDD_MEM_RET_LOW] = 750000, + [MSM_RPMRS_VDD_MEM_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_MEM_ACTIVE] = 1050000, + [MSM_RPMRS_VDD_MEM_MAX] = 1150000, + }, + .vdd_dig_levels = { + [MSM_RPMRS_VDD_DIG_RET_LOW] = 0, + [MSM_RPMRS_VDD_DIG_RET_HIGH] = 0, + [MSM_RPMRS_VDD_DIG_ACTIVE] = 1, + [MSM_RPMRS_VDD_DIG_MAX] = 3, + }, + .vdd_mask = 0x7FFFFF, + .rpmrs_target_id = { + [MSM_RPMRS_ID_PXO_CLK] = MSM_RPM_ID_PXO_CLK, + [MSM_RPMRS_ID_L2_CACHE_CTL] = MSM_RPM_ID_LAST, + [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_VOLTAGE_CORNER, + [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_LAST, + [MSM_RPMRS_ID_VDD_MEM_0] = MSM_RPM_ID_PM8038_L24_0, + [MSM_RPMRS_ID_VDD_MEM_1] = MSM_RPM_ID_PM8038_L24_1, + [MSM_RPMRS_ID_RPM_CTL] = MSM_RPM_ID_RPM_CTL, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_TZ, +}; + +static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = { + .base_addr = MSM_ACC0_BASE + 0x08, + .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE, + .mask = 1UL << 13, +}; + +#ifdef CONFIG_I2C +#define I2C_SURF 1 +#define I2C_FFA (1 << 1) +#define I2C_RUMI (1 << 2) +#define I2C_SIM (1 << 3) +#define I2C_FLUID (1 << 4) +#define I2C_LIQUID (1 << 5) + +struct i2c_registry { + u8 machs; + int bus; + struct i2c_board_info *info; + int len; +}; + +#ifdef CONFIG_ISL9519_CHARGER +static struct isl_platform_data isl_data __initdata = { + .valid_n_gpio = 0, /* Not required when notify-by-pmic */ + .chg_detection_config = NULL, /* Not required when notify-by-pmic */ + .max_system_voltage = 4200, + .min_system_voltage = 3200, + .chgcurrent = 1000, /* 1900, */ + .term_current = 400, /* Need fine tuning */ + .input_current = 2048, +}; + +static struct i2c_board_info isl_charger_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("isl9519q", 0x9), + .irq = 0, /* Not required when notify-by-pmic */ + .platform_data = &isl_data, + }, +}; +#endif /* CONFIG_ISL9519_CHARGER */ + +static struct i2c_registry msm8960_i2c_devices[] __initdata = { +#ifdef CONFIG_ISL9519_CHARGER + { + I2C_LIQUID, + MSM_8930_GSBI10_QUP_I2C_BUS_ID, + isl_charger_i2c_info, + ARRAY_SIZE(isl_charger_i2c_info), + }, +#endif /* CONFIG_ISL9519_CHARGER */ + { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_8930_GSBI9_QUP_I2C_BUS_ID, + msm_isa1200_board_info, + ARRAY_SIZE(msm_isa1200_board_info), + }, + { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_8930_GSBI3_QUP_I2C_BUS_ID, + mxt_device_info_8930, + ARRAY_SIZE(mxt_device_info_8930), + }, +}; +#endif /* CONFIG_I2C */ + +static void __init register_i2c_devices(void) +{ +#ifdef CONFIG_I2C + u8 mach_mask = 0; + int i; +#ifdef CONFIG_MSM_CAMERA + struct i2c_registry msm8930_camera_i2c_devices = { + I2C_SURF | I2C_FFA | I2C_FLUID | I2C_LIQUID | I2C_RUMI, + MSM_8930_GSBI4_QUP_I2C_BUS_ID, + msm8930_camera_board_info.board_info, + msm8930_camera_board_info.num_i2c_board_info, + }; +#endif + + /* Build the matching 'supported_machs' bitmask */ + if (machine_is_msm8930_cdp() || machine_is_msm8627_cdp()) + mach_mask = I2C_SURF; + else if (machine_is_msm8930_fluid()) + mach_mask = I2C_FLUID; + else if (machine_is_msm8930_mtp() || machine_is_msm8627_mtp()) + mach_mask = I2C_FFA; + else + pr_err("unmatched machine ID in register_i2c_devices\n"); + + /* Run the array and install devices as appropriate */ + for (i = 0; i < ARRAY_SIZE(msm8960_i2c_devices); ++i) { + if (msm8960_i2c_devices[i].machs & mach_mask) + i2c_register_board_info(msm8960_i2c_devices[i].bus, + msm8960_i2c_devices[i].info, + msm8960_i2c_devices[i].len); + } +#ifdef CONFIG_MSM_CAMERA + if (msm8930_camera_i2c_devices.machs & mach_mask) + i2c_register_board_info(msm8930_camera_i2c_devices.bus, + msm8930_camera_i2c_devices.info, + msm8930_camera_i2c_devices.len); +#endif +#endif +} + +static void __init msm8930_cdp_init(void) +{ + if (meminfo_init(SYS_MEMORY, SZ_256M) < 0) + pr_err("meminfo_init() failed!\n"); + + msm_tsens_early_init(&msm_tsens_pdata); + BUG_ON(msm_rpm_init(&msm8930_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + + regulator_suppress_info_printing(); + if (msm_xo_init()) + pr_err("Failed to initialize XO votes\n"); + platform_device_register(&msm8930_device_rpm_regulator); + msm_clock_init(&msm8930_clock_init_data); + msm8960_device_otg.dev.platform_data = &msm_otg_pdata; + android_usb_pdata.swfi_latency = + msm_rpmrs_levels[0].latency_us; + msm8930_init_gpiomux(); + msm8960_device_qup_spi_gsbi1.dev.platform_data = + &msm8960_qup_spi_gsbi1_pdata; + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); + + /* + * TODO: When physical 8930/PM8038 hardware becomes + * available, remove this block or add the config + * option. + */ +#ifndef MSM8930_PHASE_2 + msm8960_init_pmic(); +#else + msm8930_init_pmic(); +#endif + msm8930_i2c_init(); + msm8930_init_gpu(); + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + msm_spm_l2_init(msm_spm_l2_data); + msm8930_init_buses(); + platform_add_devices(msm8930_footswitch, msm8930_num_footswitch); + platform_add_devices(common_devices, ARRAY_SIZE(common_devices)); + msm8930_add_vidc_device(); + /* + * TODO: When physical 8930/PM8038 hardware becomes + * available, remove this block or add the config + * option. + */ +#ifndef MSM8930_PHASE_2 + msm8960_pm8921_gpio_mpp_init(); +#else + msm8930_pm8038_gpio_mpp_init(); +#endif + platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices)); +#ifdef CONFIG_MSM_CAMERA + msm8930_init_cam(); +#endif + msm8930_init_mmc(); + acpuclk_init(&acpuclk_8930_soc_data); + mxt_init_vkeys_8930(); + register_i2c_devices(); + msm8930_init_fb(); + slim_register_board_info(msm_slim_devices, + ARRAY_SIZE(msm_slim_devices)); + change_memory_power = &msm8930_change_memory_power; + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data); + + if (PLATFORM_IS_CHARM25()) + platform_add_devices(mdm_devices, ARRAY_SIZE(mdm_devices)); +} + +MACHINE_START(MSM8930_CDP, "QCT MSM8930 CDP") + .map_io = msm8930_map_io, + .reserve = msm8930_reserve, + .init_irq = msm8930_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8930_cdp_init, + .init_early = msm8930_allocate_memory_regions, + .init_very_early = msm8930_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8930_MTP, "QCT MSM8930 MTP") + .map_io = msm8930_map_io, + .reserve = msm8930_reserve, + .init_irq = msm8930_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8930_cdp_init, + .init_early = msm8930_allocate_memory_regions, + .init_very_early = msm8930_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8930_FLUID, "QCT MSM8930 FLUID") + .map_io = msm8930_map_io, + .reserve = msm8930_reserve, + .init_irq = msm8930_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8930_cdp_init, + .init_early = msm8930_allocate_memory_regions, + .init_very_early = msm8930_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8627_CDP, "QCT MSM8627 CDP") + .map_io = msm8930_map_io, + .reserve = msm8930_reserve, + .init_irq = msm8930_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8930_cdp_init, + .init_early = msm8930_allocate_memory_regions, + .init_very_early = msm8930_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8627_MTP, "QCT MSM8627 MTP") + .map_io = msm8930_map_io, + .reserve = msm8930_reserve, + .init_irq = msm8930_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8930_cdp_init, + .init_early = msm8930_allocate_memory_regions, + .init_very_early = msm8930_early_memory, + .restart = msm_restart, +MACHINE_END diff --git a/arch/arm/mach-msm/board-8930.h b/arch/arm/mach-msm/board-8930.h new file mode 100644 index 00000000000..e564aff5b82 --- /dev/null +++ b/arch/arm/mach-msm/board-8930.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_MSM8930_H +#define __ARCH_ARM_MACH_MSM_BOARD_MSM8930_H + +#define MSM8930_PHASE_2 + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * TODO: When physical 8930/PM8038 hardware becomes + * available, remove this block. + */ +#ifndef MSM8930_PHASE_2 +#include +#define PM8921_GPIO_BASE NR_GPIO_IRQS +#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE) +#define PM8921_MPP_BASE (PM8921_GPIO_BASE + PM8921_NR_GPIOS) +#define PM8921_MPP_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_MPP_BASE) +#endif + +/* Macros assume PMIC GPIOs and MPPs start at 1 */ +#define PM8038_GPIO_BASE NR_GPIO_IRQS +#define PM8038_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8038_GPIO_BASE) +#define PM8038_MPP_BASE (PM8038_GPIO_BASE + PM8038_NR_GPIOS) +#define PM8038_MPP_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8038_MPP_BASE) +#define PM8038_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) + +/* + * TODO: When physical 8930/PM8038 hardware becomes + * available, replace this block with 8930/pm8038 regulator + * declarations. + */ +#ifndef MSM8930_PHASE_2 +extern struct pm8xxx_regulator_platform_data + msm_pm8921_regulator_pdata[] __devinitdata; + +extern int msm_pm8921_regulator_pdata_len __devinitdata; + +extern struct gpio_regulator_platform_data + msm_gpio_regulator_pdata[] __devinitdata; + +extern struct rpm_regulator_platform_data msm_rpm_regulator_pdata __devinitdata; + +#define GPIO_VREG_ID_EXT_5V 0 +#define GPIO_VREG_ID_EXT_L2 1 +#define GPIO_VREG_ID_EXT_3P3V 2 +#endif + +extern struct regulator_init_data msm8930_saw_regulator_core0_pdata; +extern struct regulator_init_data msm8930_saw_regulator_core1_pdata; + +extern struct pm8xxx_regulator_platform_data + msm8930_pm8038_regulator_pdata[] __devinitdata; + +extern int msm8930_pm8038_regulator_pdata_len __devinitdata; + +#define MSM8930_GPIO_VREG_ID_EXT_5V 0 +#define MSM8930_GPIO_VREG_ID_EXT_OTG_SW 1 + +extern struct gpio_regulator_platform_data + msm8930_gpio_regulator_pdata[] __devinitdata; + +extern struct rpm_regulator_platform_data + msm8930_rpm_regulator_pdata __devinitdata; + +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) +enum { + GPIO_EXPANDER_IRQ_BASE = (PM8038_IRQ_BASE + PM8038_NR_IRQS), + GPIO_EXPANDER_GPIO_BASE = (PM8038_MPP_BASE + PM8038_NR_MPPS), + /* CAM Expander */ + GPIO_CAM_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE, + GPIO_CAM_GP_STROBE_READY = GPIO_CAM_EXPANDER_BASE, + GPIO_CAM_GP_AFBUSY, + GPIO_CAM_GP_STROBE_CE, + GPIO_CAM_GP_CAM1MP_XCLR, + GPIO_CAM_GP_CAMIF_RESET_N, + GPIO_CAM_GP_XMT_FLASH_INT, + GPIO_CAM_GP_LED_EN1, + GPIO_CAM_GP_LED_EN2, + +}; +#endif + +enum { + SX150X_CAM, +}; + +#endif + +extern struct sx150x_platform_data msm8930_sx150x_data[]; +extern struct msm_camera_board_info msm8930_camera_board_info; +void msm8930_init_cam(void); +void msm8930_init_fb(void); +void msm8930_init_pmic(void); +extern void msm8930_add_vidc_device(void); + +/* + * TODO: When physical 8930/PM8038 hardware becomes + * available, remove this block or add the config + * option. + */ +#ifndef MSM8930_PHASE_2 +void msm8960_init_pmic(void); +void msm8960_pm8921_gpio_mpp_init(void); +#endif + +void msm8930_init_mmc(void); +int msm8930_init_gpiomux(void); +void msm8930_allocate_fb_region(void); +void msm8930_pm8038_gpio_mpp_init(void); +void msm8930_mdp_writeback(struct memtype_reserve *reserve_table); +void __init msm8930_init_gpu(void); + +#define PLATFORM_IS_CHARM25() \ + (machine_is_msm8930_cdp() && \ + (socinfo_get_platform_subtype() == 1) \ + ) + +#define MSM_8930_GSBI3_QUP_I2C_BUS_ID 3 +#define MSM_8930_GSBI4_QUP_I2C_BUS_ID 4 +#define MSM_8930_GSBI9_QUP_I2C_BUS_ID 0 +#define MSM_8930_GSBI10_QUP_I2C_BUS_ID 10 + +extern struct msm_rtb_platform_data msm8930_rtb_pdata; diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c new file mode 100644 index 00000000000..9c25b78f713 --- /dev/null +++ b/arch/arm/mach-msm/board-8960-camera.c @@ -0,0 +1,812 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8960.h" + +#ifdef CONFIG_MSM_CAMERA + +#if (defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)) && \ + defined(CONFIG_I2C) + +static struct i2c_board_info cam_expander_i2c_info[] = { + { + I2C_BOARD_INFO("sx1508q", 0x22), + .platform_data = &msm8960_sx150x_data[SX150X_CAM] + }, +}; + +static struct msm_cam_expander_info cam_expander_info[] = { + { + cam_expander_i2c_info, + MSM_8960_GSBI4_QUP_I2C_BUS_ID, + }, +}; +#endif + +static struct gpiomux_setting cam_settings[] = { + { + .func = GPIOMUX_FUNC_GPIO, /*suspend*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + + { + .func = GPIOMUX_FUNC_1, /*active 1*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_GPIO, /*active 2*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_1, /*active 3*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, + }, + + { + .func = GPIOMUX_FUNC_5, /*active 4*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_6, /*active 5*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_2, /*active 6*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_3, /*active 7*/ + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, + }, + + { + .func = GPIOMUX_FUNC_GPIO, /*i2c suspend*/ + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, + }, + +}; + +static struct msm_gpiomux_config msm8960_cdp_flash_configs[] = { + { + .gpio = 3, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[1], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, +}; + +static struct msm_gpiomux_config msm8960_cam_common_configs[] = { + { + .gpio = 2, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 3, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 4, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[1], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 5, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 76, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, + { + .gpio = 107, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[2], + [GPIOMUX_SUSPENDED] = &cam_settings[0], + }, + }, +}; + +static struct msm_gpiomux_config msm8960_cam_2d_configs[] = { + { + .gpio = 18, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 19, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 20, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, + { + .gpio = 21, + .settings = { + [GPIOMUX_ACTIVE] = &cam_settings[3], + [GPIOMUX_SUSPENDED] = &cam_settings[8], + }, + }, +}; + +#define VFE_CAMIF_TIMER1_GPIO 2 +#define VFE_CAMIF_TIMER2_GPIO 3 +#define VFE_CAMIF_TIMER3_GPIO_INT 4 +static struct msm_camera_sensor_strobe_flash_data strobe_flash_xenon = { + .flash_trigger = VFE_CAMIF_TIMER2_GPIO, + .flash_charge = VFE_CAMIF_TIMER1_GPIO, + .flash_charge_done = VFE_CAMIF_TIMER3_GPIO_INT, + .flash_recharge_duration = 50000, + .irq = MSM_GPIO_TO_INT(VFE_CAMIF_TIMER3_GPIO_INT), +}; + +#ifdef CONFIG_MSM_CAMERA_FLASH +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT, + ._fsrc.ext_driver_src.led_en = VFE_CAMIF_TIMER1_GPIO, + ._fsrc.ext_driver_src.led_flash_en = VFE_CAMIF_TIMER2_GPIO, + ._fsrc.ext_driver_src.flash_id = MAM_CAMERA_EXT_LED_FLASH_SC628A, +}; +#endif + +static struct msm_bus_vectors cam_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 27648000, + .ib = 110592000, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 154275840, + .ib = 617103360, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 206807040, + .ib = 488816640, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 274423680, + .ib = 1097694720, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 540000000, + .ib = 1350000000, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 43200000, + .ib = 69120000, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 43200000, + .ib = 69120000, + }, +}; + +static struct msm_bus_vectors cam_zsl_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 302071680, + .ib = 1208286720, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 540000000, + .ib = 1350000000, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 43200000, + .ib = 69120000, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_MM_IMEM, + .ab = 43200000, + .ib = 69120000, + }, +}; + +static struct msm_bus_paths cam_bus_client_config[] = { + { + ARRAY_SIZE(cam_init_vectors), + cam_init_vectors, + }, + { + ARRAY_SIZE(cam_preview_vectors), + cam_preview_vectors, + }, + { + ARRAY_SIZE(cam_video_vectors), + cam_video_vectors, + }, + { + ARRAY_SIZE(cam_snapshot_vectors), + cam_snapshot_vectors, + }, + { + ARRAY_SIZE(cam_zsl_vectors), + cam_zsl_vectors, + }, +}; + +static struct msm_bus_scale_pdata cam_bus_client_pdata = { + cam_bus_client_config, + ARRAY_SIZE(cam_bus_client_config), + .name = "msm_camera", +}; + +static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = { + { + .csid_core = 0, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, + { + .csid_core = 1, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, + { + .csid_core = 2, + .is_csiphy = 1, + .is_csid = 1, + .is_ispif = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + }, +}; + +static struct camera_vreg_t msm_8960_back_cam_vreg[] = { + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2800000, 300000}, +}; + +static struct camera_vreg_t msm_8960_front_cam_vreg[] = { + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, +}; + +static struct gpio msm8960_common_cam_gpio[] = { + {5, GPIOF_DIR_IN, "CAMIF_MCLK"}, + {20, GPIOF_DIR_IN, "CAMIF_I2C_DATA"}, + {21, GPIOF_DIR_IN, "CAMIF_I2C_CLK"}, +}; + +static struct gpio msm8960_front_cam_gpio[] = { + {76, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct gpio msm8960_back_cam_gpio[] = { + {107, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct msm_gpio_set_tbl msm8960_front_cam_gpio_set_tbl[] = { + {76, GPIOF_OUT_INIT_LOW, 1000}, + {76, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_gpio_set_tbl msm8960_back_cam_gpio_set_tbl[] = { + {107, GPIOF_OUT_INIT_LOW, 1000}, + {107, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_camera_gpio_conf msm_8960_front_cam_gpio_conf = { + .cam_gpiomux_conf_tbl = msm8960_cam_2d_configs, + .cam_gpiomux_conf_tbl_size = ARRAY_SIZE(msm8960_cam_2d_configs), + .cam_gpio_common_tbl = msm8960_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(msm8960_common_cam_gpio), + .cam_gpio_req_tbl = msm8960_front_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(msm8960_front_cam_gpio), + .cam_gpio_set_tbl = msm8960_front_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(msm8960_front_cam_gpio_set_tbl), +}; + +static struct msm_camera_gpio_conf msm_8960_back_cam_gpio_conf = { + .cam_gpiomux_conf_tbl = msm8960_cam_2d_configs, + .cam_gpiomux_conf_tbl_size = ARRAY_SIZE(msm8960_cam_2d_configs), + .cam_gpio_common_tbl = msm8960_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(msm8960_common_cam_gpio), + .cam_gpio_req_tbl = msm8960_back_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(msm8960_back_cam_gpio), + .cam_gpio_set_tbl = msm8960_back_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(msm8960_back_cam_gpio_set_tbl), +}; + +static struct i2c_board_info msm_act_main_cam_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x11), +}; + +static struct msm_actuator_info msm_act_main_cam_0_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_0, + .bus_id = MSM_8960_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + +static struct i2c_board_info msm_act_main_cam1_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x18), +}; + +static struct msm_actuator_info msm_act_main_cam_1_info = { + .board_info = &msm_act_main_cam1_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_1, + .bus_id = MSM_8960_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + +static struct msm_camera_sensor_flash_data flash_imx074 = { + .flash_type = MSM_CAMERA_FLASH_LED, +#ifdef CONFIG_MSM_CAMERA_FLASH + .flash_src = &msm_flash_src +#endif +}; + +static struct msm_camera_csi_lane_params imx074_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_imx074 = { + .mount_angle = 90, + .cam_vreg = msm_8960_back_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8960_back_cam_vreg), + .gpio_conf = &msm_8960_back_cam_gpio_conf, + .csi_lane_params = &imx074_csi_lane_params, +}; + +static struct i2c_board_info imx074_eeprom_i2c_info = { + I2C_BOARD_INFO("imx074_eeprom", 0x34 << 1), +}; + +static struct msm_eeprom_info imx074_eeprom_info = { + .board_info = &imx074_eeprom_i2c_info, + .bus_id = MSM_8960_GSBI4_QUP_I2C_BUS_ID, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx074_data = { + .sensor_name = "imx074", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_imx074, + .strobe_flash_data = &strobe_flash_xenon, + .sensor_platform_info = &sensor_board_info_imx074, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_0_info, + .eeprom_info = &imx074_eeprom_info, +}; + +static struct camera_vreg_t msm_8960_mt9m114_vreg[] = { + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2800000, 300000}, +}; + +static struct msm_camera_sensor_flash_data flash_mt9m114 = { + .flash_type = MSM_CAMERA_FLASH_NONE +}; + +static struct msm_camera_csi_lane_params mt9m114_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0x1, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_mt9m114 = { + .mount_angle = 90, + .cam_vreg = msm_8960_mt9m114_vreg, + .num_vreg = ARRAY_SIZE(msm_8960_mt9m114_vreg), + .gpio_conf = &msm_8960_front_cam_gpio_conf, + .csi_lane_params = &mt9m114_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9m114_data = { + .sensor_name = "mt9m114", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_mt9m114, + .sensor_platform_info = &sensor_board_info_mt9m114, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = YUV_SENSOR, +}; + +static struct msm_camera_sensor_flash_data flash_ov2720 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_csi_lane_params ov2720_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0x3, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov2720 = { + .mount_angle = 0, + .cam_vreg = msm_8960_front_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8960_front_cam_vreg), + .gpio_conf = &msm_8960_front_cam_gpio_conf, + .csi_lane_params = &ov2720_csi_lane_params, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov2720_data = { + .sensor_name = "ov2720", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_ov2720, + .sensor_platform_info = &sensor_board_info_ov2720, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = BAYER_SENSOR, +}; + +static struct camera_vreg_t msm_8960_s5k3l1yx_vreg[] = { + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vaf", REG_LDO, 2800000, 2800000, 300000}, +}; + +static struct msm_camera_sensor_flash_data flash_s5k3l1yx = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_csi_lane_params s5k3l1yx_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_s5k3l1yx = { + .mount_angle = 0, + .cam_vreg = msm_8960_s5k3l1yx_vreg, + .num_vreg = ARRAY_SIZE(msm_8960_s5k3l1yx_vreg), + .gpio_conf = &msm_8960_back_cam_gpio_conf, + .csi_lane_params = &s5k3l1yx_csi_lane_params, +}; + +static struct msm_actuator_info msm_act_main_cam_2_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_2, + .bus_id = MSM_8960_GSBI4_QUP_I2C_BUS_ID, + .vcm_pwd = 0, + .vcm_enable = 0, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3l1yx_data = { + .sensor_name = "s5k3l1yx", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_s5k3l1yx, + .sensor_platform_info = &sensor_board_info_s5k3l1yx, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_2_info, +}; + +static struct msm_camera_csi_lane_params imx091_csi_lane_params = { + .csi_lane_assign = 0xE4, + .csi_lane_mask = 0xF, +}; + +static struct camera_vreg_t msm_8960_imx091_vreg[] = { + {"cam_vana", REG_LDO, 2800000, 2850000, 85600}, + {"cam_vaf", REG_LDO, 2800000, 2800000, 300000}, + {"cam_vdig", REG_LDO, 1200000, 1200000, 105000}, + {"cam_vio", REG_VS, 0, 0, 0}, +}; + +static struct msm_camera_sensor_flash_data flash_imx091 = { + .flash_type = MSM_CAMERA_FLASH_LED, +#ifdef CONFIG_MSM_CAMERA_FLASH + .flash_src = &msm_flash_src +#endif +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_imx091 = { + .mount_angle = 0, + .cam_vreg = msm_8960_imx091_vreg, + .num_vreg = ARRAY_SIZE(msm_8960_imx091_vreg), + .gpio_conf = &msm_8960_back_cam_gpio_conf, + .csi_lane_params = &imx091_csi_lane_params, +}; + +static struct i2c_board_info imx091_eeprom_i2c_info = { + I2C_BOARD_INFO("imx091_eeprom", 0x21), +}; + +static struct msm_eeprom_info imx091_eeprom_info = { + .board_info = &imx091_eeprom_i2c_info, + .bus_id = MSM_8960_GSBI4_QUP_I2C_BUS_ID, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx091_data = { + .sensor_name = "imx091", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_imx091, + .sensor_platform_info = &sensor_board_info_imx091, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_1_info, + .eeprom_info = &imx091_eeprom_info, +}; + +static struct pm8xxx_mpp_config_data privacy_light_on_config = { + .type = PM8XXX_MPP_TYPE_SINK, + .level = PM8XXX_MPP_CS_OUT_5MA, + .control = PM8XXX_MPP_CS_CTRL_MPP_LOW_EN, +}; + +static struct pm8xxx_mpp_config_data privacy_light_off_config = { + .type = PM8XXX_MPP_TYPE_SINK, + .level = PM8XXX_MPP_CS_OUT_5MA, + .control = PM8XXX_MPP_CS_CTRL_DISABLE, +}; + +static int32_t msm_camera_8960_ext_power_ctrl(int enable) +{ + int rc = 0; + if (enable) { + rc = pm8xxx_mpp_config(PM8921_MPP_PM_TO_SYS(12), + &privacy_light_on_config); + } else { + rc = pm8xxx_mpp_config(PM8921_MPP_PM_TO_SYS(12), + &privacy_light_off_config); + } + return rc; +} + +static struct platform_device msm_camera_server = { + .name = "msm_cam_server", + .id = 0, +}; + +void __init msm8960_init_cam(void) +{ + msm_gpiomux_install(msm8960_cam_common_configs, + ARRAY_SIZE(msm8960_cam_common_configs)); + + if (machine_is_msm8960_cdp()) { + msm_gpiomux_install(msm8960_cdp_flash_configs, + ARRAY_SIZE(msm8960_cdp_flash_configs)); + msm_flash_src._fsrc.ext_driver_src.led_en = + GPIO_CAM_GP_LED_EN1; + msm_flash_src._fsrc.ext_driver_src.led_flash_en = + GPIO_CAM_GP_LED_EN2; + #if defined(CONFIG_I2C) && (defined(CONFIG_GPIO_SX150X) || \ + defined(CONFIG_GPIO_SX150X_MODULE)) + msm_flash_src._fsrc.ext_driver_src.expander_info = + cam_expander_info; + #endif + } + + if (machine_is_msm8960_liquid()) { + struct msm_camera_sensor_info *s_info; + s_info = &msm_camera_sensor_imx074_data; + s_info->sensor_platform_info->mount_angle = 180; + s_info = &msm_camera_sensor_ov2720_data; + s_info->sensor_platform_info->ext_power_ctrl = + msm_camera_8960_ext_power_ctrl; + } + + if (machine_is_msm8960_fluid()) { + msm_camera_sensor_imx091_data.sensor_platform_info-> + mount_angle = 270; + } + + platform_device_register(&msm_camera_server); + platform_device_register(&msm8960_device_csiphy0); + platform_device_register(&msm8960_device_csiphy1); + platform_device_register(&msm8960_device_csiphy2); + platform_device_register(&msm8960_device_csid0); + platform_device_register(&msm8960_device_csid1); + platform_device_register(&msm8960_device_csid2); + platform_device_register(&msm8960_device_ispif); + platform_device_register(&msm8960_device_vfe); + platform_device_register(&msm8960_device_vpe); +} + +#ifdef CONFIG_I2C +static struct i2c_board_info msm8960_camera_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("imx074", 0x1A), + .platform_data = &msm_camera_sensor_imx074_data, + }, + { + I2C_BOARD_INFO("ov2720", 0x6C), + .platform_data = &msm_camera_sensor_ov2720_data, + }, + { + I2C_BOARD_INFO("mt9m114", 0x48), + .platform_data = &msm_camera_sensor_mt9m114_data, + }, + { + I2C_BOARD_INFO("s5k3l1yx", 0x20), + .platform_data = &msm_camera_sensor_s5k3l1yx_data, + }, +#ifdef CONFIG_MSM_CAMERA_FLASH_SC628A + { + I2C_BOARD_INFO("sc628a", 0x6E), + }, +#endif + { + I2C_BOARD_INFO("imx091", 0x34), + .platform_data = &msm_camera_sensor_imx091_data, + }, +}; + +struct msm_camera_board_info msm8960_camera_board_info = { + .board_info = msm8960_camera_i2c_boardinfo, + .num_i2c_board_info = ARRAY_SIZE(msm8960_camera_i2c_boardinfo), +}; +#endif +#endif diff --git a/arch/arm/mach-msm/board-8960-display.c b/arch/arm/mach-msm/board-8960-display.c new file mode 100644 index 00000000000..a9b2a59d4ae --- /dev/null +++ b/arch/arm/mach-msm/board-8960-display.c @@ -0,0 +1,1095 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-8960.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_PRIM_BUF_SIZE \ + (roundup((roundup(1920, 32) * roundup(1200, 32) * 4), 4096) * 3) + /* 4 bpp x 3 pages */ +#else +#define MSM_FB_PRIM_BUF_SIZE \ + (roundup((roundup(1920, 32) * roundup(1200, 32) * 4), 4096) * 2) + /* 4 bpp x 2 pages */ +#endif + +/* Note: must be multiple of 4096 */ +#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096) + +#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE \ + roundup((roundup(1920, 32) * roundup(1200, 32) * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */ + +#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE \ + roundup((roundup(1920, 32) * roundup(1080, 32) * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */ + +#define MDP_VSYNC_GPIO 0 + +#define MIPI_CMD_NOVATEK_QHD_PANEL_NAME "mipi_cmd_novatek_qhd" +#define MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME "mipi_video_novatek_qhd" +#define MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME "mipi_video_toshiba_wsvga" +#define MIPI_VIDEO_TOSHIBA_WUXGA_PANEL_NAME "mipi_video_toshiba_wuxga" +#define MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME "mipi_video_chimei_wxga" +#define MIPI_VIDEO_CHIMEI_WUXGA_PANEL_NAME "mipi_video_chimei_wuxga" +#define MIPI_VIDEO_SIMULATOR_VGA_PANEL_NAME "mipi_video_simulator_vga" +#define MIPI_CMD_RENESAS_FWVGA_PANEL_NAME "mipi_cmd_renesas_fwvga" +#define MIPI_VIDEO_ORISE_720P_PANEL_NAME "mipi_video_orise_720p" +#define MIPI_CMD_ORISE_720P_PANEL_NAME "mipi_cmd_orise_720p" +#define HDMI_PANEL_NAME "hdmi_msm" +#define TVOUT_PANEL_NAME "tvout_msm" + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +static unsigned char hdmi_is_primary = 1; +#else +static unsigned char hdmi_is_primary; +#endif + +unsigned char msm8960_hdmi_as_primary_selected(void) +{ + return hdmi_is_primary; +} + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +static void set_mdp_clocks_for_wuxga(void); + +static int msm_fb_detect_panel(const char *name) +{ + if (machine_is_msm8960_liquid()) { + u32 ver = socinfo_get_platform_version(); + if (SOCINFO_VERSION_MAJOR(ver) == 3) { + if (!strncmp(name, MIPI_VIDEO_CHIMEI_WUXGA_PANEL_NAME, + strnlen(MIPI_VIDEO_CHIMEI_WUXGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + set_mdp_clocks_for_wuxga(); + return 0; + } + } else { + if (!strncmp(name, MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME, + strnlen(MIPI_VIDEO_CHIMEI_WXGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + } + } else { + if (!strncmp(name, MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + +#if !defined(CONFIG_FB_MSM_LVDS_MIPI_PANEL_DETECT) && \ + !defined(CONFIG_FB_MSM_MIPI_PANEL_DETECT) + if (!strncmp(name, MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME, + strnlen(MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_CMD_NOVATEK_QHD_PANEL_NAME, + strnlen(MIPI_CMD_NOVATEK_QHD_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_VIDEO_SIMULATOR_VGA_PANEL_NAME, + strnlen(MIPI_VIDEO_SIMULATOR_VGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_CMD_RENESAS_FWVGA_PANEL_NAME, + strnlen(MIPI_CMD_RENESAS_FWVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_VIDEO_TOSHIBA_WUXGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WUXGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + set_mdp_clocks_for_wuxga(); + return 0; + } + + if (!strncmp(name, MIPI_VIDEO_ORISE_720P_PANEL_NAME, + strnlen(MIPI_VIDEO_ORISE_720P_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_CMD_ORISE_720P_PANEL_NAME, + strnlen(MIPI_CMD_ORISE_720P_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; +#endif + } + + if (!strncmp(name, HDMI_PANEL_NAME, + strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + if (hdmi_is_primary) + set_mdp_clocks_for_wuxga(); + return 0; + } + + if (!strncmp(name, TVOUT_PANEL_NAME, + strnlen(TVOUT_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + pr_warning("%s: not supported '%s'", __func__, name); + return -ENODEV; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev.platform_data = &msm_fb_pdata, +}; + +static void mipi_dsi_panel_pwm_cfg(void) +{ + int rc; + static int mipi_dsi_panel_gpio_configured; + static struct pm_gpio pwm_enable = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 1, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM_GPIO_VIN_VPH, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + .disable_pin = 0, + }; + static struct pm_gpio pwm_mode = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM_GPIO_VIN_S4, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_2, + .inv_int_pol = 0, + .disable_pin = 0, + }; + + if (mipi_dsi_panel_gpio_configured == 0) { + /* pm8xxx: gpio-21, Backlight Enable */ + rc = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(21), + &pwm_enable); + if (rc != 0) + pr_err("%s: pwm_enabled failed\n", __func__); + + /* pm8xxx: gpio-24, Bl: Off, PWM mode */ + rc = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(24), + &pwm_mode); + if (rc != 0) + pr_err("%s: pwm_mode failed\n", __func__); + + mipi_dsi_panel_gpio_configured++; + } +} + +static bool dsi_power_on; + +/** + * LiQUID panel on/off + * + * @param on + * + * @return int + */ +static int mipi_dsi_liquid_panel_power(int on) +{ + static struct regulator *reg_l2, *reg_ext_3p3v; + static int gpio21, gpio24, gpio43; + int rc; + + mipi_dsi_panel_pwm_cfg(); + pr_debug("%s: on=%d\n", __func__, on); + + gpio21 = PM8921_GPIO_PM_TO_SYS(21); /* disp power enable_n */ + gpio43 = PM8921_GPIO_PM_TO_SYS(43); /* Displays Enable (rst_n)*/ + gpio24 = PM8921_GPIO_PM_TO_SYS(24); /* Backlight PWM */ + + if (!dsi_power_on) { + + reg_l2 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vdda"); + if (IS_ERR(reg_l2)) { + pr_err("could not get 8921_l2, rc = %ld\n", + PTR_ERR(reg_l2)); + return -ENODEV; + } + + rc = regulator_set_voltage(reg_l2, 1200000, 1200000); + if (rc) { + pr_err("set_voltage l2 failed, rc=%d\n", rc); + return -EINVAL; + } + + reg_ext_3p3v = regulator_get(&msm_mipi_dsi1_device.dev, + "vdd_lvds_3p3v"); + if (IS_ERR(reg_ext_3p3v)) { + pr_err("could not get reg_ext_3p3v, rc = %ld\n", + PTR_ERR(reg_ext_3p3v)); + return -ENODEV; + } + + rc = gpio_request(gpio21, "disp_pwr_en_n"); + if (rc) { + pr_err("request gpio 21 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = gpio_request(gpio43, "disp_rst_n"); + if (rc) { + pr_err("request gpio 43 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = gpio_request(gpio24, "disp_backlight_pwm"); + if (rc) { + pr_err("request gpio 24 failed, rc=%d\n", rc); + return -ENODEV; + } + + dsi_power_on = true; + } + + if (on) { + rc = regulator_set_optimum_mode(reg_l2, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_l2); + if (rc) { + pr_err("enable l2 failed, rc=%d\n", rc); + return -ENODEV; + } + + rc = regulator_enable(reg_ext_3p3v); + if (rc) { + pr_err("enable reg_ext_3p3v failed, rc=%d\n", rc); + return -ENODEV; + } + + /* set reset pin before power enable */ + gpio_set_value_cansleep(gpio43, 0); /* disp disable (resx=0) */ + + gpio_set_value_cansleep(gpio21, 0); /* disp power enable_n */ + msleep(20); + gpio_set_value_cansleep(gpio43, 1); /* disp enable */ + msleep(20); + gpio_set_value_cansleep(gpio43, 0); /* disp enable */ + msleep(20); + gpio_set_value_cansleep(gpio43, 1); /* disp enable */ + msleep(20); + } else { + gpio_set_value_cansleep(gpio43, 0); + gpio_set_value_cansleep(gpio21, 1); + + rc = regulator_disable(reg_l2); + if (rc) { + pr_err("disable reg_l2 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_ext_3p3v); + if (rc) { + pr_err("disable reg_ext_3p3v failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_set_optimum_mode(reg_l2, 100); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + } + + return 0; +} + +static int mipi_dsi_cdp_panel_power(int on) +{ + static struct regulator *reg_l8, *reg_l23, *reg_l2; + static int gpio43; + int rc; + + pr_debug("%s: state : %d\n", __func__, on); + + if (!dsi_power_on) { + + reg_l8 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vdc"); + if (IS_ERR(reg_l8)) { + pr_err("could not get 8921_l8, rc = %ld\n", + PTR_ERR(reg_l8)); + return -ENODEV; + } + reg_l23 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vddio"); + if (IS_ERR(reg_l23)) { + pr_err("could not get 8921_l23, rc = %ld\n", + PTR_ERR(reg_l23)); + return -ENODEV; + } + reg_l2 = regulator_get(&msm_mipi_dsi1_device.dev, + "dsi_vdda"); + if (IS_ERR(reg_l2)) { + pr_err("could not get 8921_l2, rc = %ld\n", + PTR_ERR(reg_l2)); + return -ENODEV; + } + rc = regulator_set_voltage(reg_l8, 2800000, 3000000); + if (rc) { + pr_err("set_voltage l8 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_voltage(reg_l23, 1800000, 1800000); + if (rc) { + pr_err("set_voltage l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_voltage(reg_l2, 1200000, 1200000); + if (rc) { + pr_err("set_voltage l2 failed, rc=%d\n", rc); + return -EINVAL; + } + gpio43 = PM8921_GPIO_PM_TO_SYS(43); + rc = gpio_request(gpio43, "disp_rst_n"); + if (rc) { + pr_err("request gpio 43 failed, rc=%d\n", rc); + return -ENODEV; + } + dsi_power_on = true; + } + if (on) { + rc = regulator_set_optimum_mode(reg_l8, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l8 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l23, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l2, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_l8); + if (rc) { + pr_err("enable l8 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_enable(reg_l23); + if (rc) { + pr_err("enable l8 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_enable(reg_l2); + if (rc) { + pr_err("enable l2 failed, rc=%d\n", rc); + return -ENODEV; + } + gpio_set_value_cansleep(gpio43, 1); + } else { + rc = regulator_disable(reg_l2); + if (rc) { + pr_err("disable reg_l2 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_l8); + if (rc) { + pr_err("disable reg_l8 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_l23); + if (rc) { + pr_err("disable reg_l23 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_set_optimum_mode(reg_l8, 100); + if (rc < 0) { + pr_err("set_optimum_mode l8 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l23, 100); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_set_optimum_mode(reg_l2, 100); + if (rc < 0) { + pr_err("set_optimum_mode l2 failed, rc=%d\n", rc); + return -EINVAL; + } + gpio_set_value_cansleep(gpio43, 0); + } + return 0; +} + +static char mipi_dsi_splash_is_enabled(void); +static int mipi_dsi_panel_power(int on) +{ + int ret; + + pr_debug("%s: on=%d\n", __func__, on); + + if (machine_is_msm8960_liquid()) + ret = mipi_dsi_liquid_panel_power(on); + else + ret = mipi_dsi_cdp_panel_power(on); + + return ret; +} + +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .vsync_gpio = MDP_VSYNC_GPIO, + .dsi_power_save = mipi_dsi_panel_power, + .splash_is_enabled = mipi_dsi_splash_is_enabled, +}; + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors mdp_init_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors mdp_ui_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000 * 2, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_vga_vectors[] = { + /* VGA and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000 * 2, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_720p_vectors[] = { + /* 720p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 230400000 * 2, + .ib = 288000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_1080p_vectors[] = { + /* 1080p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 334080000 * 2, + .ib = 417600000 * 2, + }, +}; + +static struct msm_bus_paths mdp_bus_scale_usecases[] = { + { + ARRAY_SIZE(mdp_init_vectors), + mdp_init_vectors, + }, + { + ARRAY_SIZE(mdp_ui_vectors), + mdp_ui_vectors, + }, + { + ARRAY_SIZE(mdp_ui_vectors), + mdp_ui_vectors, + }, + { + ARRAY_SIZE(mdp_vga_vectors), + mdp_vga_vectors, + }, + { + ARRAY_SIZE(mdp_720p_vectors), + mdp_720p_vectors, + }, + { + ARRAY_SIZE(mdp_1080p_vectors), + mdp_1080p_vectors, + }, +}; + +static struct msm_bus_scale_pdata mdp_bus_scale_pdata = { + mdp_bus_scale_usecases, + ARRAY_SIZE(mdp_bus_scale_usecases), + .name = "mdp", +}; + +#endif + +static int mdp_core_clk_rate_table[] = { + 85330000, + 128000000, + 160000000, + 200000000, +}; + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = MDP_VSYNC_GPIO, + .mdp_core_clk_rate = 85330000, + .mdp_core_clk_table = mdp_core_clk_rate_table, + .num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table), +#ifdef CONFIG_MSM_BUS_SCALING + .mdp_bus_scale_table = &mdp_bus_scale_pdata, +#endif + .mdp_rev = MDP_REV_42, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .mem_hid = BIT(ION_CP_MM_HEAP_ID), +#else + .mem_hid = MEMTYPE_EBI1, +#endif + .cont_splash_enabled = 0x01, +}; + +void __init msm8960_mdp_writeback(struct memtype_reserve* reserve_table) +{ + mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE; + mdp_pdata.ov1_wb_size = MSM_FB_OVERLAY1_WRITEBACK_SIZE; +#if defined(CONFIG_ANDROID_PMEM) && !defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov0_wb_size; + reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov1_wb_size; +#endif +} + +static char mipi_dsi_splash_is_enabled(void) +{ + return mdp_pdata.cont_splash_enabled; +} + +static struct platform_device mipi_dsi_renesas_panel_device = { + .name = "mipi_renesas", + .id = 0, +}; + +static struct platform_device mipi_dsi_simulator_panel_device = { + .name = "mipi_simulator", + .id = 0, +}; + +#define LPM_CHANNEL0 0 +static int toshiba_gpio[] = {LPM_CHANNEL0}; + +static struct mipi_dsi_panel_platform_data toshiba_pdata = { + .gpio = toshiba_gpio, + .dsi_pwm_cfg = mipi_dsi_panel_pwm_cfg, +}; + +static struct platform_device mipi_dsi_toshiba_panel_device = { + .name = "mipi_toshiba", + .id = 0, + .dev = { + .platform_data = &toshiba_pdata, + } +}; + +#define FPGA_3D_GPIO_CONFIG_ADDR 0xB5 +static int dsi2lvds_gpio[4] = { + 0,/* Backlight PWM-ID=0 for PMIC-GPIO#24 */ + 0x1F08, /* DSI2LVDS Bridge GPIO Output, mask=0x1f, out=0x08 */ + GPIO_LIQUID_EXPANDER_BASE+6, /* TN Enable */ + GPIO_LIQUID_EXPANDER_BASE+7, /* TN Mode */ + }; + +static struct msm_panel_common_pdata mipi_dsi2lvds_pdata = { + .gpio_num = dsi2lvds_gpio, +}; + +static struct mipi_dsi_phy_ctrl dsi_novatek_cmd_mode_phy_db = { + +/* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */ + {0x0F, 0x0a, 0x04, 0x00, 0x20}, /* regulator */ + /* timing */ + {0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c, + 0x0c, 0x03, 0x04, 0xa0}, + {0x5f, 0x00, 0x00, 0x10}, /* phy ctrl */ + {0xff, 0x00, 0x06, 0x00}, /* strength */ + /* pll control */ + {0x40, 0xf9, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62, + 0x40, 0x07, 0x03, + 0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01}, +}; + +static struct mipi_dsi_panel_platform_data novatek_pdata = { + .fpga_3d_config_addr = FPGA_3D_GPIO_CONFIG_ADDR, + .fpga_ctrl_mode = FPGA_SPI_INTF, + .phy_ctrl_settings = &dsi_novatek_cmd_mode_phy_db, +}; + +static struct platform_device mipi_dsi_novatek_panel_device = { + .name = "mipi_novatek", + .id = 0, + .dev = { + .platform_data = &novatek_pdata, + } +}; + +static struct platform_device mipi_dsi2lvds_bridge_device = { + .name = "mipi_tc358764", + .id = 0, + .dev.platform_data = &mipi_dsi2lvds_pdata, +}; + +static struct platform_device mipi_dsi_orise_panel_device = { + .name = "mipi_orise", + .id = 0, +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct resource hdmi_msm_resources[] = { + { + .name = "hdmi_msm_qfprom_addr", + .start = 0x00700000, + .end = 0x007060FF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_hdmi_addr", + .start = 0x04A00000, + .end = 0x04A00FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_irq", + .start = HDMI_IRQ, + .end = HDMI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int hdmi_enable_5v(int on); +static int hdmi_core_power(int on, int show); +static int hdmi_cec_power(int on); + +static struct msm_hdmi_platform_data hdmi_msm_data = { + .irq = HDMI_IRQ, + .enable_5v = hdmi_enable_5v, + .core_power = hdmi_core_power, + .cec_power = hdmi_cec_power, +}; + +static struct platform_device hdmi_msm_device = { + .name = "hdmi_msm", + .id = 0, + .num_resources = ARRAY_SIZE(hdmi_msm_resources), + .resource = hdmi_msm_resources, + .dev.platform_data = &hdmi_msm_data, +}; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL +static struct platform_device wfd_panel_device = { + .name = "wfd_panel", + .id = 0, + .dev.platform_data = NULL, +}; + +static struct platform_device wfd_device = { + .name = "msm_wfd", + .id = -1, +}; +#endif + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors dtv_bus_init_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors dtv_bus_def_vectors[] = { + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 566092800 * 2, + .ib = 707616000 * 2, + }, +}; + +static struct msm_bus_paths dtv_bus_scale_usecases[] = { + { + ARRAY_SIZE(dtv_bus_init_vectors), + dtv_bus_init_vectors, + }, + { + ARRAY_SIZE(dtv_bus_def_vectors), + dtv_bus_def_vectors, + }, +}; +static struct msm_bus_scale_pdata dtv_bus_scale_pdata = { + dtv_bus_scale_usecases, + ARRAY_SIZE(dtv_bus_scale_usecases), + .name = "dtv", +}; + +static struct lcdc_platform_data dtv_pdata = { + .bus_scale_table = &dtv_bus_scale_pdata, +}; +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static int hdmi_enable_5v(int on) +{ + /* TBD: PM8921 regulator instead of 8901 */ + static struct regulator *reg_8921_hdmi_mvs; /* HDMI_5V */ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_8921_hdmi_mvs) { + reg_8921_hdmi_mvs = regulator_get(&hdmi_msm_device.dev, + "hdmi_mvs"); + if (IS_ERR(reg_8921_hdmi_mvs)) { + pr_err("'%s' regulator not found, rc=%ld\n", + "hdmi_mvs", IS_ERR(reg_8921_hdmi_mvs)); + reg_8921_hdmi_mvs = NULL; + return -ENODEV; + } + } + + if (on) { + rc = regulator_enable(reg_8921_hdmi_mvs); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "8921_hdmi_mvs", rc); + return rc; + } + pr_debug("%s(on): success\n", __func__); + } else { + rc = regulator_disable(reg_8921_hdmi_mvs); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "8921_hdmi_mvs", rc); + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +} + +static int hdmi_core_power(int on, int show) +{ + static struct regulator *reg_8921_l23, *reg_8921_s4; + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + /* TBD: PM8921 regulator instead of 8901 */ + if (!reg_8921_l23) { + reg_8921_l23 = regulator_get(&hdmi_msm_device.dev, "hdmi_avdd"); + if (IS_ERR(reg_8921_l23)) { + pr_err("could not get reg_8921_l23, rc = %ld\n", + PTR_ERR(reg_8921_l23)); + return -ENODEV; + } + rc = regulator_set_voltage(reg_8921_l23, 1800000, 1800000); + if (rc) { + pr_err("set_voltage failed for 8921_l23, rc=%d\n", rc); + return -EINVAL; + } + } + if (!reg_8921_s4) { + reg_8921_s4 = regulator_get(&hdmi_msm_device.dev, "hdmi_vcc"); + if (IS_ERR(reg_8921_s4)) { + pr_err("could not get reg_8921_s4, rc = %ld\n", + PTR_ERR(reg_8921_s4)); + return -ENODEV; + } + rc = regulator_set_voltage(reg_8921_s4, 1800000, 1800000); + if (rc) { + pr_err("set_voltage failed for 8921_s4, rc=%d\n", rc); + return -EINVAL; + } + } + + if (on) { + rc = regulator_set_optimum_mode(reg_8921_l23, 100000); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + rc = regulator_enable(reg_8921_l23); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "hdmi_avdd", rc); + return rc; + } + rc = regulator_enable(reg_8921_s4); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "hdmi_vcc", rc); + return rc; + } + rc = gpio_request(100, "HDMI_DDC_CLK"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_CLK", 100, rc); + goto error1; + } + rc = gpio_request(101, "HDMI_DDC_DATA"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_DATA", 101, rc); + goto error2; + } + rc = gpio_request(102, "HDMI_HPD"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_HPD", 102, rc); + goto error3; + } + pr_debug("%s(on): success\n", __func__); + } else { + gpio_free(100); + gpio_free(101); + gpio_free(102); + + rc = regulator_disable(reg_8921_l23); + if (rc) { + pr_err("disable reg_8921_l23 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_disable(reg_8921_s4); + if (rc) { + pr_err("disable reg_8921_s4 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = regulator_set_optimum_mode(reg_8921_l23, 100); + if (rc < 0) { + pr_err("set_optimum_mode l23 failed, rc=%d\n", rc); + return -EINVAL; + } + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; + +error3: + gpio_free(101); +error2: + gpio_free(100); +error1: + regulator_disable(reg_8921_l23); + regulator_disable(reg_8921_s4); + return rc; +} + +static int hdmi_cec_power(int on) +{ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (on) { + rc = gpio_request(99, "HDMI_CEC_VAR"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_CEC_VAR", 99, rc); + goto error; + } + pr_debug("%s(on): success\n", __func__); + } else { + gpio_free(99); + pr_debug("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +error: + return rc; +} +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ + +void __init msm8960_init_fb(void) +{ + platform_device_register(&msm_fb_device); + +#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL + platform_device_register(&wfd_panel_device); + platform_device_register(&wfd_device); +#endif + + if (machine_is_msm8960_sim()) + platform_device_register(&mipi_dsi_simulator_panel_device); + + if (machine_is_msm8960_rumi3()) + platform_device_register(&mipi_dsi_renesas_panel_device); + + if (!machine_is_msm8960_sim() && !machine_is_msm8960_rumi3()) { + platform_device_register(&mipi_dsi_novatek_panel_device); + platform_device_register(&mipi_dsi_orise_panel_device); + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + platform_device_register(&hdmi_msm_device); +#endif + } + + if (machine_is_msm8960_liquid()) + platform_device_register(&mipi_dsi2lvds_bridge_device); + else + platform_device_register(&mipi_dsi_toshiba_panel_device); + + if (machine_is_msm8x60_rumi3()) { + msm_fb_register_device("mdp", NULL); + mipi_dsi_pdata.target_type = 1; + } else + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata); +#ifdef CONFIG_MSM_BUS_SCALING + msm_fb_register_device("dtv", &dtv_pdata); +#endif +} + +void __init msm8960_allocate_fb_region(void) +{ + void *addr; + unsigned long size; + + size = MSM_FB_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); +} + +/** + * Set MDP clocks to high frequency to avoid DSI underflow + * when using high resolution 1200x1920 WUXGA panels + */ +static void set_mdp_clocks_for_wuxga(void) +{ + int i; + + mdp_ui_vectors[0].ab = 2000000000; + mdp_ui_vectors[0].ib = 2000000000; + mdp_vga_vectors[0].ab = 2000000000; + mdp_vga_vectors[0].ib = 2000000000; + mdp_720p_vectors[0].ab = 2000000000; + mdp_720p_vectors[0].ib = 2000000000; + mdp_1080p_vectors[0].ab = 2000000000; + mdp_1080p_vectors[0].ib = 2000000000; + + mdp_pdata.mdp_core_clk_rate = 200000000; + + for (i = 0; i < ARRAY_SIZE(mdp_core_clk_rate_table); i++) + mdp_core_clk_rate_table[i] = 200000000; + + if (hdmi_is_primary) { + dtv_bus_def_vectors[0].ab = 2000000000; + dtv_bus_def_vectors[0].ib = 2000000000; + } +} + +void __init msm8960_set_display_params(char *prim_panel, char *ext_panel) +{ + int disable_splash = 0; + if (strnlen(prim_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.prim_panel_name, prim_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.prim_panel_name %s\n", + msm_fb_pdata.prim_panel_name); + + if (strncmp((char *)msm_fb_pdata.prim_panel_name, + MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WSVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + /* Disable splash for panels other than Toshiba WSVGA */ + disable_splash = 1; + } + + if (!strncmp((char *)msm_fb_pdata.prim_panel_name, + HDMI_PANEL_NAME, strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + pr_debug("HDMI is the primary display by" + " boot parameter\n"); + hdmi_is_primary = 1; + set_mdp_clocks_for_wuxga(); + } + if (!strncmp((char *)msm_fb_pdata.prim_panel_name, + MIPI_VIDEO_TOSHIBA_WUXGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WUXGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + set_mdp_clocks_for_wuxga(); + } + } + if (strnlen(ext_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.ext_panel_name, ext_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.ext_panel_name %s\n", + msm_fb_pdata.ext_panel_name); + } + + if (disable_splash) + mdp_pdata.cont_splash_enabled = 0; +} diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c new file mode 100644 index 00000000000..fd326f1921e --- /dev/null +++ b/arch/arm/mach-msm/board-8960-gpiomux.c @@ -0,0 +1,1004 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "devices.h" +#include "board-8960.h" + +/* The SPI configurations apply to GSBI 1*/ +static struct gpiomux_setting spi_active = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting spi_suspended_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting spi_active_config2 = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting spi_suspended_config2 = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting gsbi3_suspended_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting gsbi3_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting external_vfr[] = { + /* Suspended state */ + { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, + }, + /* Active state */ + { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_KEEPER, + }, +}; + +static struct gpiomux_setting gsbi_uart = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi9_active_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting gsbi9_suspended_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting gsbi10 = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi12 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cdc_mclk = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting audio_auxpcm[] = { + /* Suspended state */ + { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + }, + /* Active state */ + { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + }, +}; + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static struct gpiomux_setting gpio_eth_config = { + .pull = GPIOMUX_PULL_NONE, + .drv = GPIOMUX_DRV_8MA, + .func = GPIOMUX_FUNC_GPIO, +}; +#endif + +static struct gpiomux_setting slimbus = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting wcnss_5wire_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting wcnss_5wire_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cyts_resout_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting cyts_resout_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting cyts_sleep_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cyts_sleep_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cyts_int_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting cyts_int_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +#ifdef CONFIG_USB_EHCI_MSM_HSIC +static struct gpiomux_setting hsic_act_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting hsic_sus_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, + .dir = GPIOMUX_OUT_LOW, +}; + +static struct gpiomux_setting hsic_hub_act_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; +#endif + +static struct gpiomux_setting hap_lvl_shft_suspended_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting hap_lvl_shft_active_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ap2mdm_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_status_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_errfatal_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ap2mdm_kpdpwr_n_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdp_vsync_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdp_vsync_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct gpiomux_setting hdmi_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting hdmi_active_1_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting hdmi_active_2_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +#if defined(CONFIG_FB_MSM_HDMI_MHL_8334) || defined(CONFIG_FB_MSM_HDMI_MHL_9244) +static struct gpiomux_setting hdmi_active_3_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, + .dir = GPIOMUX_IN, +}; + +static struct gpiomux_setting hdmi_active_4_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, + .dir = GPIOMUX_OUT_HIGH, +}; +#endif +#endif + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static struct msm_gpiomux_config msm8960_ethernet_configs[] = { + { + .gpio = 90, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_eth_config, + } + }, + { + .gpio = 89, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_eth_config, + } + }, +}; +#endif + +static struct msm_gpiomux_config msm8960_fusion_gsbi_configs[] = { + { + .gpio = 93, + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi9_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi9_active_cfg, + } + }, + { + .gpio = 94, + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi9_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi9_active_cfg, + } + }, + { + .gpio = 95, + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi9_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi9_active_cfg, + } + }, + { + .gpio = 96, + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi9_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi9_active_cfg, + } + }, +}; + +static struct msm_gpiomux_config msm8960_gsbi_configs[] __initdata = { + { + .gpio = 6, /* GSBI1 QUP SPI_DATA_MOSI */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 7, /* GSBI1 QUP SPI_DATA_MISO */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 8, /* GSBI1 QUP SPI_CS_N */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 9, /* GSBI1 QUP SPI_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 14, /* GSBI1 SPI_CS_1 */ + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config2, + [GPIOMUX_ACTIVE] = &spi_active_config2, + }, + }, + { + .gpio = 16, /* GSBI3 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi3_active_cfg, + }, + }, + { + .gpio = 17, /* GSBI3 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_suspended_cfg, + [GPIOMUX_ACTIVE] = &gsbi3_active_cfg, + }, + }, + { + .gpio = 44, /* GSBI12 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi12, + }, + }, + { + .gpio = 45, /* GSBI12 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi12, + }, + }, + { + .gpio = 73, /* GSBI10 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi10, + }, + }, + { + .gpio = 74, /* GSBI10 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi10, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_gsbi5_uart_configs[] __initdata = { + { + .gpio = 22, /* GSBI5 UART2 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, + { + .gpio = 23, /* GSBI5 UART2 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, + { + .gpio = 24, /* GSBI5 UART2 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, + { + .gpio = 25, /* GSBI5 UART2 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_external_vfr_configs[] __initdata = { + { + .gpio = 23, /* EXTERNAL VFR */ + .settings = { + [GPIOMUX_SUSPENDED] = &external_vfr[0], + [GPIOMUX_ACTIVE] = &external_vfr[1], + }, + }, +}; + +static struct msm_gpiomux_config msm8960_gsbi8_uart_configs[] __initdata = { + { + .gpio = 34, /* GSBI8 UART3 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, + { + .gpio = 35, /* GSBI8 UART3 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, + { + .gpio = 36, /* GSBI8 UART3 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, + { + .gpio = 37, /* GSBI8 UART3 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi_uart, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_slimbus_config[] __initdata = { + { + .gpio = 60, /* slimbus data */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, + { + .gpio = 61, /* slimbus clk */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_audio_codec_configs[] __initdata = { + { + .gpio = 59, + .settings = { + [GPIOMUX_SUSPENDED] = &cdc_mclk, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_audio_auxpcm_configs[] __initdata = { + { + .gpio = 63, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, + { + .gpio = 64, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, + { + .gpio = 65, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, + { + .gpio = 66, + .settings = { + [GPIOMUX_SUSPENDED] = &audio_auxpcm[0], + [GPIOMUX_ACTIVE] = &audio_auxpcm[1], + }, + }, +}; + +static struct msm_gpiomux_config wcnss_5wire_interface[] = { + { + .gpio = 84, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 85, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 86, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 87, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, + { + .gpio = 88, + .settings = { + [GPIOMUX_ACTIVE] = &wcnss_5wire_active_cfg, + [GPIOMUX_SUSPENDED] = &wcnss_5wire_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_cyts_configs[] __initdata = { + { /* TS INTERRUPT */ + .gpio = 11, + .settings = { + [GPIOMUX_ACTIVE] = &cyts_int_act_cfg, + [GPIOMUX_SUSPENDED] = &cyts_int_sus_cfg, + }, + }, + { /* TS SLEEP */ + .gpio = 50, + .settings = { + [GPIOMUX_ACTIVE] = &cyts_sleep_act_cfg, + [GPIOMUX_SUSPENDED] = &cyts_sleep_sus_cfg, + }, + }, + { /* TS RESOUT */ + .gpio = 52, + .settings = { + [GPIOMUX_ACTIVE] = &cyts_resout_act_cfg, + [GPIOMUX_SUSPENDED] = &cyts_resout_sus_cfg, + }, + }, +}; + +#ifdef CONFIG_USB_EHCI_MSM_HSIC +static struct msm_gpiomux_config msm8960_hsic_configs[] = { + { + .gpio = 150, /*HSIC_STROBE */ + .settings = { + [GPIOMUX_ACTIVE] = &hsic_act_cfg, + [GPIOMUX_SUSPENDED] = &hsic_sus_cfg, + }, + }, + { + .gpio = 151, /* HSIC_DATA */ + .settings = { + [GPIOMUX_ACTIVE] = &hsic_act_cfg, + [GPIOMUX_SUSPENDED] = &hsic_sus_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8960_hsic_hub_configs[] = { + { + .gpio = 91, /* HSIC_HUB_RESET */ + .settings = { + [GPIOMUX_ACTIVE] = &hsic_hub_act_cfg, + [GPIOMUX_SUSPENDED] = &hsic_sus_cfg, + }, + }, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct gpiomux_setting sdcc4_clk_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdcc4_cmd_data_0_3_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc4_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting sdcc4_data_1_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct msm_gpiomux_config msm8960_sdcc4_configs[] __initdata = { + { + /* SDC4_DATA_3 */ + .gpio = 83, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc4_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc4_suspend_cfg, + }, + }, + { + /* SDC4_DATA_2 */ + .gpio = 84, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc4_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc4_suspend_cfg, + }, + }, + { + /* SDC4_DATA_1 */ + .gpio = 85, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc4_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc4_data_1_suspend_cfg, + }, + }, + { + /* SDC4_DATA_0 */ + .gpio = 86, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc4_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc4_suspend_cfg, + }, + }, + { + /* SDC4_CMD */ + .gpio = 87, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc4_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc4_suspend_cfg, + }, + }, + { + /* SDC4_CLK */ + .gpio = 88, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc4_clk_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc4_suspend_cfg, + }, + }, +}; +#endif + + +static struct msm_gpiomux_config hap_lvl_shft_config[] __initdata = { + { + .gpio = 47, + .settings = { + [GPIOMUX_SUSPENDED] = &hap_lvl_shft_suspended_config, + [GPIOMUX_ACTIVE] = &hap_lvl_shft_active_config, + }, + }, +}; + +static struct msm_gpiomux_config sglte_configs[] __initdata = { + /* AP2MDM_STATUS */ + { + .gpio = 77, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* MDM2AP_STATUS */ + { + .gpio = 24, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_status_cfg, + } + }, + /* MDM2AP_ERRFATAL */ + { + .gpio = 40, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_errfatal_cfg, + } + }, + /* AP2MDM_ERRFATAL */ + { + .gpio = 80, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* AP2MDM_KPDPWR_N */ + { + .gpio = 79, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_kpdpwr_n_cfg, + } + }, + /* AP2MDM_PMIC_PWR_EN */ + { + .gpio = 22, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_kpdpwr_n_cfg, + } + }, + /* AP2MDM_SOFT_RESET */ + { + .gpio = 78, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, +}; + +static struct msm_gpiomux_config msm8960_mdp_vsync_configs[] __initdata = { + { + .gpio = 0, + .settings = { + [GPIOMUX_ACTIVE] = &mdp_vsync_active_cfg, + [GPIOMUX_SUSPENDED] = &mdp_vsync_suspend_cfg, + }, + } +}; + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct msm_gpiomux_config msm8960_hdmi_configs[] __initdata = { + { + .gpio = 99, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 100, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 101, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 102, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_2_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, +#ifdef CONFIG_FB_MSM_HDMI_MHL_9244 + { + .gpio = 15, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_3_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 66, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_4_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, +#endif +#ifdef CONFIG_FB_MSM_HDMI_MHL_8334 + { + .gpio = 4, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_3_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 15, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_4_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, +#endif /* CONFIG_FB_MSM_HDMI_MHL */ +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct gpiomux_setting sdcc2_clk_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdcc2_cmd_data_0_3_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc2_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting sdcc2_data_1_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct msm_gpiomux_config msm8960_sdcc2_configs[] __initdata = { + { + /* DATA_3 */ + .gpio = 92, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* DATA_2 */ + .gpio = 91, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* DATA_1 */ + .gpio = 90, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_data_1_suspend_cfg, + }, + }, + { + /* DATA_0 */ + .gpio = 89, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* CMD */ + .gpio = 97, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* CLK */ + .gpio = 98, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_clk_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, +}; +#endif + +int __init msm8960_init_gpiomux(void) +{ + int rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc); + return rc; + } + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) + msm_gpiomux_install(msm8960_ethernet_configs, + ARRAY_SIZE(msm8960_ethernet_configs)); +#endif + + msm_gpiomux_install(msm8960_gsbi_configs, + ARRAY_SIZE(msm8960_gsbi_configs)); + + msm_gpiomux_install(msm8960_cyts_configs, + ARRAY_SIZE(msm8960_cyts_configs)); + + msm_gpiomux_install(msm8960_slimbus_config, + ARRAY_SIZE(msm8960_slimbus_config)); + + msm_gpiomux_install(msm8960_audio_codec_configs, + ARRAY_SIZE(msm8960_audio_codec_configs)); + + msm_gpiomux_install(msm8960_audio_auxpcm_configs, + ARRAY_SIZE(msm8960_audio_auxpcm_configs)); + + msm_gpiomux_install(wcnss_5wire_interface, + ARRAY_SIZE(wcnss_5wire_interface)); + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + msm_gpiomux_install(msm8960_sdcc4_configs, + ARRAY_SIZE(msm8960_sdcc4_configs)); +#endif + + if (machine_is_msm8960_mtp() || machine_is_msm8960_fluid() || + machine_is_msm8960_liquid() || machine_is_msm8960_cdp()) + msm_gpiomux_install(hap_lvl_shft_config, + ARRAY_SIZE(hap_lvl_shft_config)); + +#ifdef CONFIG_USB_EHCI_MSM_HSIC + if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 1) && + machine_is_msm8960_liquid()) + msm_gpiomux_install(msm8960_hsic_configs, + ARRAY_SIZE(msm8960_hsic_configs)); + + if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 1) && + machine_is_msm8960_liquid()) + msm_gpiomux_install(msm8960_hsic_hub_configs, + ARRAY_SIZE(msm8960_hsic_hub_configs)); +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + msm_gpiomux_install(msm8960_hdmi_configs, + ARRAY_SIZE(msm8960_hdmi_configs)); +#endif + + msm_gpiomux_install(msm8960_mdp_vsync_configs, + ARRAY_SIZE(msm8960_mdp_vsync_configs)); + + if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) + msm_gpiomux_install(msm8960_gsbi8_uart_configs, + ARRAY_SIZE(msm8960_gsbi8_uart_configs)); + else + msm_gpiomux_install(msm8960_gsbi5_uart_configs, + ARRAY_SIZE(msm8960_gsbi5_uart_configs)); + + if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) { + /* For 8960 Fusion 2.2 Primary IPC */ + msm_gpiomux_install(msm8960_fusion_gsbi_configs, + ARRAY_SIZE(msm8960_fusion_gsbi_configs)); + /* For SGLTE 8960 Fusion External VFR */ + msm_gpiomux_install(msm8960_external_vfr_configs, + ARRAY_SIZE(msm8960_external_vfr_configs)); + } + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + msm_gpiomux_install(msm8960_sdcc2_configs, + ARRAY_SIZE(msm8960_sdcc2_configs)); +#endif + + if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) + msm_gpiomux_install(sglte_configs, + ARRAY_SIZE(sglte_configs)); + + return 0; +} diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c new file mode 100644 index 00000000000..ea1ab5875a9 --- /dev/null +++ b/arch/arm/mach-msm/board-8960-pmic.c @@ -0,0 +1,624 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8960.h" + +struct pm8xxx_gpio_init { + unsigned gpio; + struct pm_gpio config; +}; + +struct pm8xxx_mpp_init { + unsigned mpp; + struct pm8xxx_mpp_config_data config; +}; + +#define PM8XXX_GPIO_INIT(_gpio, _dir, _buf, _val, _pull, _vin, _out_strength, \ + _func, _inv, _disable) \ +{ \ + .gpio = PM8921_GPIO_PM_TO_SYS(_gpio), \ + .config = { \ + .direction = _dir, \ + .output_buffer = _buf, \ + .output_value = _val, \ + .pull = _pull, \ + .vin_sel = _vin, \ + .out_strength = _out_strength, \ + .function = _func, \ + .inv_int_pol = _inv, \ + .disable_pin = _disable, \ + } \ +} + +#define PM8XXX_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8921_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +#define PM8XXX_GPIO_DISABLE(_gpio) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, 0, 0, 0, PM_GPIO_VIN_S4, \ + 0, 0, 0, 1) + +#define PM8XXX_GPIO_OUTPUT(_gpio, _val) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_HIGH, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8XXX_GPIO_INPUT(_gpio, _pull) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, PM_GPIO_OUT_BUF_CMOS, 0, \ + _pull, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_NO, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8XXX_GPIO_OUTPUT_FUNC(_gpio, _val, _func) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM_GPIO_VIN_S4, \ + PM_GPIO_STRENGTH_HIGH, \ + _func, 0, 0) + +#define PM8XXX_GPIO_OUTPUT_VIN(_gpio, _val, _vin) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, _vin, \ + PM_GPIO_STRENGTH_HIGH, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8XXX_GPIO_OUTPUT_STRENGTH(_gpio, _val, _out_strength) \ + PM8XXX_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM_GPIO_VIN_S4, \ + _out_strength, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +/* Initial PM8921 GPIO configurations */ +static struct pm8xxx_gpio_init pm8921_gpios[] __initdata = { + PM8XXX_GPIO_OUTPUT_VIN(6, 1, PM_GPIO_VIN_VPH), /* MHL power EN_N */ + PM8XXX_GPIO_DISABLE(7), /* Disable NFC */ + PM8XXX_GPIO_INPUT(16, PM_GPIO_PULL_UP_30), /* SD_CARD_WP */ + /* External regulator shared by display and touchscreen on LiQUID */ + PM8XXX_GPIO_OUTPUT(17, 0), /* DISP 3.3 V Boost */ + PM8XXX_GPIO_OUTPUT(18, 0), /* TABLA SPKR_LEFT_EN=off */ + PM8XXX_GPIO_OUTPUT(19, 0), /* TABLA SPKR_RIGHT_EN=off */ + PM8XXX_GPIO_DISABLE(22), /* Disable NFC */ + PM8XXX_GPIO_OUTPUT_FUNC(25, 0, PM_GPIO_FUNC_2), /* TN_CLK */ + PM8XXX_GPIO_INPUT(26, PM_GPIO_PULL_UP_30), /* SD_CARD_DET_N */ + PM8XXX_GPIO_OUTPUT(43, 1), /* DISP_RESET_N */ + PM8XXX_GPIO_OUTPUT(42, 0), /* USB 5V reg enable */ + /* TABLA CODEC RESET */ + PM8XXX_GPIO_OUTPUT_STRENGTH(34, 1, PM_GPIO_STRENGTH_MED) +}; + +/* Initial PM8921 MPP configurations */ +static struct pm8xxx_mpp_init pm8921_mpps[] __initdata = { + /* External 5V regulator enable; shared by HDMI and USB_OTG switches. */ + PM8XXX_MPP_INIT(7, D_INPUT, PM8921_MPP_DIG_LEVEL_VPH, DIN_TO_INT), + PM8XXX_MPP_INIT(PM8XXX_AMUX_MPP_8, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH8, + DOUT_CTRL_LOW), +}; + +void __init msm8960_pm8921_gpio_mpp_init(void) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(pm8921_gpios); i++) { + rc = pm8xxx_gpio_config(pm8921_gpios[i].gpio, + &pm8921_gpios[i].config); + if (rc) { + pr_err("%s: pm8xxx_gpio_config: rc=%d\n", __func__, rc); + break; + } + } + + for (i = 0; i < ARRAY_SIZE(pm8921_mpps); i++) { + rc = pm8xxx_mpp_config(pm8921_mpps[i].mpp, + &pm8921_mpps[i].config); + if (rc) { + pr_err("%s: pm8xxx_mpp_config: rc=%d\n", __func__, rc); + break; + } + } +} + +static struct pm8xxx_adc_amux pm8xxx_adc_channels_data[] = { + {"vcoin", CHANNEL_VCOIN, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vbat", CHANNEL_VBAT, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"dcin", CHANNEL_DCIN, CHAN_PATH_SCALING4, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"ichg", CHANNEL_ICHG, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vph_pwr", CHANNEL_VPH_PWR, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"ibat", CHANNEL_IBAT, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"batt_therm", CHANNEL_BATT_THERM, CHAN_PATH_SCALING1, AMUX_RSV2, + ADC_DECIMATION_TYPE2, ADC_SCALE_BATT_THERM}, + {"batt_id", CHANNEL_BATT_ID, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"usbin", CHANNEL_USBIN, CHAN_PATH_SCALING3, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pmic_therm", CHANNEL_DIE_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PMIC_THERM}, + {"625mv", CHANNEL_625MV, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"125v", CHANNEL_125V, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"chg_temp", CHANNEL_CHG_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pa_therm1", ADC_MPP_1_AMUX8, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PA_THERM}, + {"xo_therm", CHANNEL_MUXOFF, CHAN_PATH_SCALING1, AMUX_RSV0, + ADC_DECIMATION_TYPE2, ADC_SCALE_XOTHERM}, + {"pa_therm0", ADC_MPP_1_AMUX3, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PA_THERM}, +}; + +static struct pm8xxx_adc_properties pm8xxx_adc_data = { + .adc_vdd_reference = 1800, /* milli-voltage for this adc */ + .bitresolution = 15, + .bipolar = 0, +}; + +static struct pm8xxx_adc_platform_data pm8xxx_adc_pdata = { + .adc_channel = pm8xxx_adc_channels_data, + .adc_num_board_channel = ARRAY_SIZE(pm8xxx_adc_channels_data), + .adc_prop = &pm8xxx_adc_data, + .adc_mpp_base = PM8921_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_irq_platform_data pm8xxx_irq_pdata __devinitdata = { + .irq_base = PM8921_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(104), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_gpio_platform_data pm8xxx_gpio_pdata __devinitdata = { + .gpio_base = PM8921_GPIO_PM_TO_SYS(1), +}; + +static struct pm8xxx_mpp_platform_data pm8xxx_mpp_pdata __devinitdata = { + .mpp_base = PM8921_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_rtc_platform_data pm8xxx_rtc_pdata __devinitdata = { + .rtc_write_enable = false, + .rtc_alarm_powerup = false, +}; + +static struct pm8xxx_pwrkey_platform_data pm8xxx_pwrkey_pdata = { + .pull_up = 1, + .kpd_trigger_delay_us = 15625, + .wakeup = 1, +}; + +/* Rotate lock key is not available so use F1 */ +#define KEY_ROTATE_LOCK KEY_F1 + +static const unsigned int keymap_liquid[] = { + KEY(0, 0, KEY_VOLUMEUP), + KEY(0, 1, KEY_VOLUMEDOWN), + KEY(1, 3, KEY_ROTATE_LOCK), + KEY(1, 4, KEY_HOME), +}; + +static struct matrix_keymap_data keymap_data_liquid = { + .keymap_size = ARRAY_SIZE(keymap_liquid), + .keymap = keymap_liquid, +}; + +static struct pm8xxx_keypad_platform_data keypad_data_liquid = { + .input_name = "keypad_8960_liquid", + .input_phys_device = "keypad_8960/input0", + .num_rows = 2, + .num_cols = 5, + .rows_gpio_start = PM8921_GPIO_PM_TO_SYS(9), + .cols_gpio_start = PM8921_GPIO_PM_TO_SYS(1), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &keymap_data_liquid, +}; + + +static const unsigned int keymap[] = { + KEY(0, 0, KEY_VOLUMEUP), + KEY(0, 1, KEY_VOLUMEDOWN), + KEY(0, 2, KEY_CAMERA_SNAPSHOT), + KEY(0, 3, KEY_CAMERA_FOCUS), +}; + +static struct matrix_keymap_data keymap_data = { + .keymap_size = ARRAY_SIZE(keymap), + .keymap = keymap, +}; + +static struct pm8xxx_keypad_platform_data keypad_data = { + .input_name = "keypad_8960", + .input_phys_device = "keypad_8960/input0", + .num_rows = 1, + .num_cols = 5, + .rows_gpio_start = PM8921_GPIO_PM_TO_SYS(9), + .cols_gpio_start = PM8921_GPIO_PM_TO_SYS(1), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &keymap_data, +}; + +static const unsigned int keymap_sim[] = { + KEY(0, 0, KEY_7), + KEY(0, 1, KEY_DOWN), + KEY(0, 2, KEY_UP), + KEY(0, 3, KEY_RIGHT), + KEY(0, 4, KEY_ENTER), + KEY(0, 5, KEY_L), + KEY(0, 6, KEY_BACK), + KEY(0, 7, KEY_M), + + KEY(1, 0, KEY_LEFT), + KEY(1, 1, KEY_SEND), + KEY(1, 2, KEY_1), + KEY(1, 3, KEY_4), + KEY(1, 4, KEY_CLEAR), + KEY(1, 5, KEY_MSDOS), + KEY(1, 6, KEY_SPACE), + KEY(1, 7, KEY_COMMA), + + KEY(2, 0, KEY_6), + KEY(2, 1, KEY_5), + KEY(2, 2, KEY_8), + KEY(2, 3, KEY_3), + KEY(2, 4, KEY_NUMERIC_STAR), + KEY(2, 5, KEY_UP), + KEY(2, 6, KEY_DOWN), + KEY(2, 7, KEY_LEFTSHIFT), + + KEY(3, 0, KEY_9), + KEY(3, 1, KEY_NUMERIC_POUND), + KEY(3, 2, KEY_0), + KEY(3, 3, KEY_2), + KEY(3, 4, KEY_SLEEP), + KEY(3, 5, KEY_F1), + KEY(3, 6, KEY_F2), + KEY(3, 7, KEY_F3), + + KEY(4, 0, KEY_BACK), + KEY(4, 1, KEY_HOME), + KEY(4, 2, KEY_MENU), + KEY(4, 3, KEY_VOLUMEUP), + KEY(4, 4, KEY_VOLUMEDOWN), + KEY(4, 5, KEY_F4), + KEY(4, 6, KEY_F5), + KEY(4, 7, KEY_F6), + + KEY(5, 0, KEY_R), + KEY(5, 1, KEY_T), + KEY(5, 2, KEY_Y), + KEY(5, 3, KEY_LEFTALT), + KEY(5, 4, KEY_KPENTER), + KEY(5, 5, KEY_Q), + KEY(5, 6, KEY_W), + KEY(5, 7, KEY_E), + + KEY(6, 0, KEY_F), + KEY(6, 1, KEY_G), + KEY(6, 2, KEY_H), + KEY(6, 3, KEY_CAPSLOCK), + KEY(6, 4, KEY_PAGEUP), + KEY(6, 5, KEY_A), + KEY(6, 6, KEY_S), + KEY(6, 7, KEY_D), + + KEY(7, 0, KEY_V), + KEY(7, 1, KEY_B), + KEY(7, 2, KEY_N), + KEY(7, 3, KEY_MENU), + KEY(7, 4, KEY_PAGEDOWN), + KEY(7, 5, KEY_Z), + KEY(7, 6, KEY_X), + KEY(7, 7, KEY_C), + + KEY(8, 0, KEY_P), + KEY(8, 1, KEY_J), + KEY(8, 2, KEY_K), + KEY(8, 3, KEY_INSERT), + KEY(8, 4, KEY_LINEFEED), + KEY(8, 5, KEY_U), + KEY(8, 6, KEY_I), + KEY(8, 7, KEY_O), + + KEY(9, 0, KEY_4), + KEY(9, 1, KEY_5), + KEY(9, 2, KEY_6), + KEY(9, 3, KEY_7), + KEY(9, 4, KEY_8), + KEY(9, 5, KEY_1), + KEY(9, 6, KEY_2), + KEY(9, 7, KEY_3), + + KEY(10, 0, KEY_F7), + KEY(10, 1, KEY_F8), + KEY(10, 2, KEY_F9), + KEY(10, 3, KEY_F10), + KEY(10, 4, KEY_FN), + KEY(10, 5, KEY_9), + KEY(10, 6, KEY_0), + KEY(10, 7, KEY_DOT), + + KEY(11, 0, KEY_LEFTCTRL), + KEY(11, 1, KEY_F11), + KEY(11, 2, KEY_ENTER), + KEY(11, 3, KEY_SEARCH), + KEY(11, 4, KEY_DELETE), + KEY(11, 5, KEY_RIGHT), + KEY(11, 6, KEY_LEFT), + KEY(11, 7, KEY_RIGHTSHIFT), + KEY(0, 0, KEY_VOLUMEUP), + KEY(0, 1, KEY_VOLUMEDOWN), + KEY(0, 2, KEY_CAMERA_SNAPSHOT), + KEY(0, 3, KEY_CAMERA_FOCUS), +}; + +static struct matrix_keymap_data keymap_data_sim = { + .keymap_size = ARRAY_SIZE(keymap_sim), + .keymap = keymap_sim, +}; + +static struct pm8xxx_keypad_platform_data keypad_data_sim = { + .input_name = "keypad_8960", + .input_phys_device = "keypad_8960/input0", + .num_rows = 12, + .num_cols = 8, + .rows_gpio_start = PM8921_GPIO_PM_TO_SYS(9), + .cols_gpio_start = PM8921_GPIO_PM_TO_SYS(1), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &keymap_data_sim, +}; + +static int pm8921_therm_mitigation[] = { + 1100, + 700, + 600, + 325, +}; + +#define MAX_VOLTAGE_MV 4200 +static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = { + .safety_time = 180, + .update_time = 60000, + .max_voltage = MAX_VOLTAGE_MV, + .min_voltage = 3200, + .resume_voltage_delta = 100, + .term_current = 100, + .cool_temp = 10, + .warm_temp = 40, + .temp_check_period = 1, + .max_bat_chg_current = 1100, + .cool_bat_chg_current = 350, + .warm_bat_chg_current = 350, + .cool_bat_voltage = 4100, + .warm_bat_voltage = 4100, + .thermal_mitigation = pm8921_therm_mitigation, + .thermal_levels = ARRAY_SIZE(pm8921_therm_mitigation), + .rconn_mohm = 18, +}; + +static struct pm8xxx_misc_platform_data pm8xxx_misc_pdata = { + .priority = 0, +}; + +static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = { + .battery_type = BATT_UNKNOWN, + .r_sense = 10, + .i_test = 2500, + .v_failure = 3000, + .calib_delay_ms = 600000, + .max_voltage_uv = MAX_VOLTAGE_MV * 1000, + .rconn_mohm = 30, +}; + +#define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */ +#define PM8921_LC_LED_LOW_CURRENT 1 /* I = 1mA */ +#define PM8XXX_LED_PWM_PERIOD 1000 +#define PM8XXX_LED_PWM_DUTY_MS 20 +/** + * PM8XXX_PWM_CHANNEL_NONE shall be used when LED shall not be + * driven using PWM feature. + */ +#define PM8XXX_PWM_CHANNEL_NONE -1 + +static struct led_info pm8921_led_info_liquid[] = { + { + .name = "led:red", + .flags = PM8XXX_ID_LED_0, + .default_trigger = "battery-charging", + }, + { + .name = "led:green", + .flags = PM8XXX_ID_LED_0, + .default_trigger = "battery-full", + }, + { + .name = "led:blue", + .flags = PM8XXX_ID_LED_2, + .default_trigger = "notification", + }, +}; + +static struct pm8xxx_led_config pm8921_led_configs_liquid[] = { + [0] = { + .id = PM8XXX_ID_LED_0, + .mode = PM8XXX_LED_MODE_MANUAL, + .max_current = PM8921_LC_LED_MAX_CURRENT, + }, + [1] = { + .id = PM8XXX_ID_LED_1, + .mode = PM8XXX_LED_MODE_MANUAL, + .max_current = PM8921_LC_LED_LOW_CURRENT, + }, + [2] = { + .id = PM8XXX_ID_LED_2, + .mode = PM8XXX_LED_MODE_MANUAL, + .max_current = PM8921_LC_LED_MAX_CURRENT, + }, +}; + +static struct led_platform_data pm8xxx_leds_core_liquid = { + .num_leds = ARRAY_SIZE(pm8921_led_info_liquid), + .leds = pm8921_led_info_liquid, +}; + +static struct pm8xxx_led_platform_data pm8xxx_leds_pdata_liquid = { + .led_core = &pm8xxx_leds_core_liquid, + .configs = pm8921_led_configs_liquid, + .num_configs = ARRAY_SIZE(pm8921_led_configs_liquid), +}; + +static struct led_info pm8921_led_info[] = { + [0] = { + .name = "led:battery_charging", + .default_trigger = "battery-charging", + }, + [1] = { + .name = "led:battery_full", + .default_trigger = "battery-full", + }, +}; + +static struct led_platform_data pm8921_led_core_pdata = { + .num_leds = ARRAY_SIZE(pm8921_led_info), + .leds = pm8921_led_info, +}; + +static int pm8921_led0_pwm_duty_pcts[56] = { + 1, 4, 8, 12, 16, 20, 24, 28, 32, 36, + 40, 44, 46, 52, 56, 60, 64, 68, 72, 76, + 80, 84, 88, 92, 96, 100, 100, 100, 98, 95, + 92, 88, 84, 82, 78, 74, 70, 66, 62, 58, + 58, 54, 50, 48, 42, 38, 34, 30, 26, 22, + 14, 10, 6, 4, 1 +}; + +static struct pm8xxx_pwm_duty_cycles pm8921_led0_pwm_duty_cycles = { + .duty_pcts = (int *)&pm8921_led0_pwm_duty_pcts, + .num_duty_pcts = ARRAY_SIZE(pm8921_led0_pwm_duty_pcts), + .duty_ms = PM8XXX_LED_PWM_DUTY_MS, + .start_idx = 0, +}; + +static struct pm8xxx_led_config pm8921_led_configs[] = { + [0] = { + .id = PM8XXX_ID_LED_0, + .mode = PM8XXX_LED_MODE_PWM2, + .max_current = PM8921_LC_LED_MAX_CURRENT, + .pwm_channel = 5, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD, + .pwm_duty_cycles = &pm8921_led0_pwm_duty_cycles, + }, + [1] = { + .id = PM8XXX_ID_LED_1, + .mode = PM8XXX_LED_MODE_PWM1, + .max_current = PM8921_LC_LED_MAX_CURRENT, + .pwm_channel = 4, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD, + }, +}; + +static struct pm8xxx_led_platform_data pm8xxx_leds_pdata = { + .led_core = &pm8921_led_core_pdata, + .configs = pm8921_led_configs, + .num_configs = ARRAY_SIZE(pm8921_led_configs), +}; + +static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = { + .r_sense = 10, +}; + +/** + * PM8XXX_PWM_DTEST_CHANNEL_NONE shall be used when no LPG + * channel should be in DTEST mode. + */ + +#define PM8XXX_PWM_DTEST_CHANNEL_NONE (-1) + +static struct pm8xxx_pwm_platform_data pm8xxx_pwm_pdata = { + .dtest_channel = PM8XXX_PWM_DTEST_CHANNEL_NONE, +}; + +static struct pm8921_platform_data pm8921_platform_data __devinitdata = { + .irq_pdata = &pm8xxx_irq_pdata, + .gpio_pdata = &pm8xxx_gpio_pdata, + .mpp_pdata = &pm8xxx_mpp_pdata, + .rtc_pdata = &pm8xxx_rtc_pdata, + .pwrkey_pdata = &pm8xxx_pwrkey_pdata, + .keypad_pdata = &keypad_data, + .misc_pdata = &pm8xxx_misc_pdata, + .regulator_pdatas = msm_pm8921_regulator_pdata, + .charger_pdata = &pm8921_chg_pdata, + .bms_pdata = &pm8921_bms_pdata, + .adc_pdata = &pm8xxx_adc_pdata, + .leds_pdata = &pm8xxx_leds_pdata, + .ccadc_pdata = &pm8xxx_ccadc_pdata, + .pwm_pdata = &pm8xxx_pwm_pdata, +}; + +static struct msm_ssbi_platform_data msm8960_ssbi_pm8921_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = "pm8921-core", + .platform_data = &pm8921_platform_data, + }, +}; + +void __init msm8960_init_pmic(void) +{ + pmic_reset_irq = PM8921_IRQ_BASE + PM8921_RESOUT_IRQ; + msm8960_device_ssbi_pmic.dev.platform_data = + &msm8960_ssbi_pm8921_pdata; + pm8921_platform_data.num_regulators = msm_pm8921_regulator_pdata_len; + + /* Simulator supports a QWERTY keypad */ + if (machine_is_msm8960_sim()) + pm8921_platform_data.keypad_pdata = &keypad_data_sim; + + if (machine_is_msm8960_liquid()) { + pm8921_platform_data.keypad_pdata = &keypad_data_liquid; + pm8921_platform_data.leds_pdata = &pm8xxx_leds_pdata_liquid; + pm8921_platform_data.bms_pdata->battery_type = BATT_DESAY; + } else if (machine_is_msm8960_mtp()) { + pm8921_platform_data.bms_pdata->battery_type = BATT_PALLADIUM; + } + + if (machine_is_msm8960_fluid()) + pm8921_bms_pdata.rconn_mohm = 20; +} diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c new file mode 100644 index 00000000000..bc5a89287e0 --- /dev/null +++ b/arch/arm/mach-msm/board-8960-regulator.c @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "board-8960.h" + +#define VREG_CONSUMERS(_id) \ + static struct regulator_consumer_supply vreg_consumers_##_id[] + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +VREG_CONSUMERS(L1) = { + REGULATOR_SUPPLY("8921_l1", NULL), +}; +VREG_CONSUMERS(L2) = { + REGULATOR_SUPPLY("8921_l2", NULL), + REGULATOR_SUPPLY("dsi_vdda", "mipi_dsi.1"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.0"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.1"), + REGULATOR_SUPPLY("mipi_csi_vdd", "msm_csid.2"), +}; +VREG_CONSUMERS(L3) = { + REGULATOR_SUPPLY("8921_l3", NULL), + REGULATOR_SUPPLY("HSUSB_3p3", "msm_otg"), +}; +VREG_CONSUMERS(L4) = { + REGULATOR_SUPPLY("8921_l4", NULL), + REGULATOR_SUPPLY("HSUSB_1p8", "msm_otg"), + REGULATOR_SUPPLY("iris_vddxo", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L5) = { + REGULATOR_SUPPLY("8921_l5", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.1"), +}; +VREG_CONSUMERS(L6) = { + REGULATOR_SUPPLY("8921_l6", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.3"), +}; +VREG_CONSUMERS(L7) = { + REGULATOR_SUPPLY("8921_l7", NULL), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.3"), +}; +VREG_CONSUMERS(L8) = { + REGULATOR_SUPPLY("8921_l8", NULL), + REGULATOR_SUPPLY("dsi_vdc", "mipi_dsi.1"), +}; +VREG_CONSUMERS(L9) = { + REGULATOR_SUPPLY("8921_l9", NULL), + REGULATOR_SUPPLY("vdd", "3-0024"), + REGULATOR_SUPPLY("vdd_ana", "3-004a"), +}; +VREG_CONSUMERS(L10) = { + REGULATOR_SUPPLY("8921_l10", NULL), + REGULATOR_SUPPLY("iris_vddpa", "wcnss_wlan.0"), + +}; +VREG_CONSUMERS(L11) = { + REGULATOR_SUPPLY("8921_l11", NULL), + REGULATOR_SUPPLY("cam_vana", "4-001a"), + REGULATOR_SUPPLY("cam_vana", "4-006c"), + REGULATOR_SUPPLY("cam_vana", "4-0048"), + REGULATOR_SUPPLY("cam_vana", "4-0020"), + REGULATOR_SUPPLY("cam_vana", "4-0034"), +}; +VREG_CONSUMERS(L12) = { + REGULATOR_SUPPLY("8921_l12", NULL), + REGULATOR_SUPPLY("cam_vdig", "4-001a"), + REGULATOR_SUPPLY("cam_vdig", "4-006c"), + REGULATOR_SUPPLY("cam_vdig", "4-0048"), + REGULATOR_SUPPLY("cam_vdig", "4-0020"), + REGULATOR_SUPPLY("cam_vdig", "4-0034"), +}; +VREG_CONSUMERS(L14) = { + REGULATOR_SUPPLY("8921_l14", NULL), + REGULATOR_SUPPLY("pa_therm", "pm8xxx-adc"), +}; +VREG_CONSUMERS(L15) = { + REGULATOR_SUPPLY("8921_l15", NULL), +}; +VREG_CONSUMERS(L16) = { + REGULATOR_SUPPLY("8921_l16", NULL), + REGULATOR_SUPPLY("cam_vaf", "4-001a"), + REGULATOR_SUPPLY("cam_vaf", "4-006c"), + REGULATOR_SUPPLY("cam_vaf", "4-0048"), + REGULATOR_SUPPLY("cam_vaf", "4-0020"), + REGULATOR_SUPPLY("cam_vaf", "4-0034"), +}; +VREG_CONSUMERS(L17) = { + REGULATOR_SUPPLY("8921_l17", NULL), +}; +VREG_CONSUMERS(L18) = { + REGULATOR_SUPPLY("8921_l18", NULL), +}; +VREG_CONSUMERS(L21) = { + REGULATOR_SUPPLY("8921_l21", NULL), +}; +VREG_CONSUMERS(L22) = { + REGULATOR_SUPPLY("8921_l22", NULL), +}; +VREG_CONSUMERS(L23) = { + REGULATOR_SUPPLY("8921_l23", NULL), + REGULATOR_SUPPLY("dsi_vddio", "mipi_dsi.1"), + REGULATOR_SUPPLY("hdmi_avdd", "hdmi_msm.0"), + REGULATOR_SUPPLY("pll_vdd", "pil_riva"), + REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.1"), + REGULATOR_SUPPLY("pll_vdd", "pil_qdsp6v4.2"), +}; +VREG_CONSUMERS(L24) = { + REGULATOR_SUPPLY("8921_l24", NULL), + REGULATOR_SUPPLY("riva_vddmx", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(L25) = { + REGULATOR_SUPPLY("8921_l25", NULL), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla-slim"), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla2x-slim"), +}; +VREG_CONSUMERS(L26) = { + REGULATOR_SUPPLY("8921_l26", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.0"), +}; +VREG_CONSUMERS(L27) = { + REGULATOR_SUPPLY("8921_l27", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.2"), +}; +VREG_CONSUMERS(L28) = { + REGULATOR_SUPPLY("8921_l28", NULL), + REGULATOR_SUPPLY("core_vdd", "pil_qdsp6v4.1"), +}; +VREG_CONSUMERS(L29) = { + REGULATOR_SUPPLY("8921_l29", NULL), +}; +VREG_CONSUMERS(S1) = { + REGULATOR_SUPPLY("8921_s1", NULL), +}; +VREG_CONSUMERS(S2) = { + REGULATOR_SUPPLY("8921_s2", NULL), + REGULATOR_SUPPLY("iris_vddrfa", "wcnss_wlan.0"), + +}; +VREG_CONSUMERS(S3) = { + REGULATOR_SUPPLY("8921_s3", NULL), + REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_otg"), + REGULATOR_SUPPLY("riva_vddcx", "wcnss_wlan.0"), + REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_host"), +}; +VREG_CONSUMERS(S4) = { + REGULATOR_SUPPLY("8921_s4", NULL), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.1"), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.2"), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.4"), + REGULATOR_SUPPLY("riva_vddpx", "wcnss_wlan.0"), + REGULATOR_SUPPLY("hdmi_vcc", "hdmi_msm.0"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla-slim"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla2x-slim"), + REGULATOR_SUPPLY("vcc_i2c", "3-005b"), + REGULATOR_SUPPLY("EXT_HUB_VDDIO", "msm_smsc_hub"), + REGULATOR_SUPPLY("vcc_i2c", "10-0048"), +}; +VREG_CONSUMERS(S5) = { + REGULATOR_SUPPLY("8921_s5", NULL), + REGULATOR_SUPPLY("krait0", NULL), +}; +VREG_CONSUMERS(S6) = { + REGULATOR_SUPPLY("8921_s6", NULL), + REGULATOR_SUPPLY("krait1", NULL), +}; +VREG_CONSUMERS(S7) = { + REGULATOR_SUPPLY("8921_s7", NULL), +}; +VREG_CONSUMERS(S8) = { + REGULATOR_SUPPLY("8921_s8", NULL), +}; +VREG_CONSUMERS(LVS1) = { + REGULATOR_SUPPLY("8921_lvs1", NULL), + REGULATOR_SUPPLY("iris_vddio", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(LVS2) = { + REGULATOR_SUPPLY("8921_lvs2", NULL), + REGULATOR_SUPPLY("iris_vdddig", "wcnss_wlan.0"), +}; +VREG_CONSUMERS(LVS3) = { + REGULATOR_SUPPLY("8921_lvs3", NULL), +}; +VREG_CONSUMERS(LVS4) = { + REGULATOR_SUPPLY("8921_lvs4", NULL), + REGULATOR_SUPPLY("vcc_i2c", "3-0024"), + REGULATOR_SUPPLY("vcc_i2c", "3-004a"), +}; +VREG_CONSUMERS(LVS5) = { + REGULATOR_SUPPLY("8921_lvs5", NULL), + REGULATOR_SUPPLY("cam_vio", "4-001a"), + REGULATOR_SUPPLY("cam_vio", "4-006c"), + REGULATOR_SUPPLY("cam_vio", "4-0048"), + REGULATOR_SUPPLY("cam_vio", "4-0020"), + REGULATOR_SUPPLY("cam_vio", "4-0034"), +}; +VREG_CONSUMERS(LVS6) = { + REGULATOR_SUPPLY("8921_lvs6", NULL), + REGULATOR_SUPPLY("vdd_io", "spi0.0"), +}; +VREG_CONSUMERS(LVS7) = { + REGULATOR_SUPPLY("8921_lvs7", NULL), +}; +VREG_CONSUMERS(USB_OTG) = { + REGULATOR_SUPPLY("8921_usb_otg", NULL), +}; +VREG_CONSUMERS(HDMI_MVS) = { + REGULATOR_SUPPLY("8921_hdmi_mvs", NULL), + REGULATOR_SUPPLY("hdmi_mvs", "hdmi_msm.0"), +}; +VREG_CONSUMERS(NCP) = { + REGULATOR_SUPPLY("8921_ncp", NULL), +}; +VREG_CONSUMERS(EXT_5V) = { + REGULATOR_SUPPLY("ext_5v", NULL), +}; +VREG_CONSUMERS(EXT_L2) = { + REGULATOR_SUPPLY("ext_l2", NULL), + REGULATOR_SUPPLY("vdd_phy", "spi0.0"), +}; +VREG_CONSUMERS(EXT_3P3V) = { + REGULATOR_SUPPLY("ext_3p3v", NULL), + REGULATOR_SUPPLY("vdd_ana", "3-005b"), + REGULATOR_SUPPLY("vdd_lvds_3p3v", "mipi_dsi.1"), + REGULATOR_SUPPLY("mhl_ext_3p3v", "msm_otg"), +}; +VREG_CONSUMERS(EXT_OTG_SW) = { + REGULATOR_SUPPLY("ext_otg_sw", NULL), + REGULATOR_SUPPLY("vbus_otg", "msm_otg"), +}; + +#define PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, _modes, _ops, \ + _apply_uV, _pull_down, _always_on, _supply_regulator, \ + _system_uA, _enable_time, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _max_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pull_down_enable = _pull_down, \ + .system_uA = _system_uA, \ + .enable_time = _enable_time, \ + } + +#define PM8XXX_LDO(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_NLDO1200(_id, _name, _always_on, _pull_down, _min_uV, \ + _max_uV, _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_SMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_FTSMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS \ + | REGULATOR_CHANGE_MODE, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_VS(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +#define PM8XXX_VS300(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +#define PM8XXX_NCP(_id, _name, _always_on, _min_uV, _max_uV, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, 0, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, 0, 0, \ + _always_on, _supply_regulator, 0, _enable_time, _reg_id) + +/* Pin control initialization */ +#define PM8XXX_PC(_id, _name, _always_on, _pin_fn, _pin_ctrl, \ + _supply_regulator, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pin_fn = PM8XXX_VREG_PIN_FN_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +#define GPIO_VREG(_id, _reg_name, _gpio_label, _gpio, _supply_regulator) \ + [GPIO_VREG_ID_##_id] = { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .regulator_name = _reg_name, \ + .gpio_label = _gpio_label, \ + .gpio = _gpio, \ + } + +#define SAW_VREG_INIT(_id, _name, _min_uV, _max_uV) \ + { \ + .constraints = { \ + .name = _name, \ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + }, \ + .num_consumer_supplies = ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + } + +#define RPM_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, _default_uV, \ + _peak_uA, _avg_uA, _pull_down, _pin_ctrl, _freq, _pin_fn, \ + _force_mode, _sleep_set_force_mode, _power_mode, _state, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _min_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8921_##_id, \ + .default_uV = _default_uV, \ + .peak_uA = _peak_uA, \ + .avg_uA = _avg_uA, \ + .pull_down_enable = _pull_down, \ + .pin_ctrl = _pin_ctrl, \ + .freq = RPM_VREG_FREQ_##_freq, \ + .pin_fn = _pin_fn, \ + .force_mode = _force_mode, \ + .sleep_set_force_mode = _sleep_set_force_mode, \ + .power_mode = _power_mode, \ + .state = _state, \ + .sleep_selectable = _sleep_selectable, \ + .system_uA = _system_uA, \ + } + +#define RPM_LDO(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _init_peak_uA) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _init_peak_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, RPM_VREG_POWER_MODE_8960_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, _system_uA) + +#define RPM_SMPS(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _freq, _force_mode, \ + _sleep_set_force_mode) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _system_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_##_force_mode, \ + RPM_VREG_FORCE_MODE_8960_##_sleep_set_force_mode, \ + RPM_VREG_POWER_MODE_8960_PWM, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) + +#define RPM_VS(_id, _always_on, _pd, _sleep_selectable, _supply_regulator) \ + RPM_INIT(_id, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, 0, 1000, 1000, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, RPM_VREG_POWER_MODE_8960_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +#define RPM_NCP(_id, _always_on, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _freq) \ + RPM_INIT(_id, _min_uV, _max_uV, 0, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS, 0, _max_uV, 1000, 1000, 0, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, \ + RPM_VREG_FORCE_MODE_8960_NONE, RPM_VREG_POWER_MODE_8960_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +/* Pin control initialization */ +#define RPM_PC_INIT(_id, _always_on, _pin_fn, _pin_ctrl, _supply_regulator) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8921_##_id##_PC, \ + .pin_fn = RPM_VREG_PIN_FN_8960_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +/* GPIO regulator constraints */ +struct gpio_regulator_platform_data msm_gpio_regulator_pdata[] __devinitdata = { + /* ID vreg_name gpio_label gpio supply */ + GPIO_VREG(EXT_5V, "ext_5v", "ext_5v_en", PM8921_MPP_PM_TO_SYS(7), NULL), + GPIO_VREG(EXT_L2, "ext_l2", "ext_l2_en", 91, NULL), + GPIO_VREG(EXT_3P3V, "ext_3p3v", "ext_3p3v_en", + PM8921_GPIO_PM_TO_SYS(17), NULL), + GPIO_VREG(EXT_OTG_SW, "ext_otg_sw", "ext_otg_sw_en", + PM8921_GPIO_PM_TO_SYS(42), "8921_usb_otg"), +}; + +/* SAW regulator constraints */ +struct regulator_init_data msm_saw_regulator_pdata_s5 = + /* ID vreg_name min_uV max_uV */ + SAW_VREG_INIT(S5, "8921_s5", 850000, 1300000); +struct regulator_init_data msm_saw_regulator_pdata_s6 = + SAW_VREG_INIT(S6, "8921_s6", 850000, 1300000); + +/* PM8921 regulator constraints */ +struct pm8xxx_regulator_platform_data +msm_pm8921_regulator_pdata[] __devinitdata = { + /* + * ID name always_on pd min_uV max_uV en_t supply + * system_uA reg_ID + */ + PM8XXX_NLDO1200(L26, "8921_l26", 0, 1, 375000, 1050000, 200, "8921_s7", + 0, 1), + PM8XXX_NLDO1200(L27, "8921_l27", 0, 1, 375000, 1050000, 200, "8921_s7", + 0, 2), + PM8XXX_NLDO1200(L28, "8921_l28", 0, 1, 375000, 1050000, 200, "8921_s7", + 0, 3), + PM8XXX_LDO(L29, "8921_l29", 0, 1, 2050000, 2100000, 200, "8921_s8", + 0, 4), + + /* ID name always_on pd en_t supply reg_ID */ + PM8XXX_VS300(USB_OTG, "8921_usb_otg", 0, 1, 0, "ext_5v", 5), + PM8XXX_VS300(HDMI_MVS, "8921_hdmi_mvs", 0, 1, 0, "ext_5v", 6), +}; + +static struct rpm_regulator_init_data +msm_rpm_regulator_init_data[] __devinitdata = { + /* ID a_on pd ss min_uV max_uV supply sys_uA freq fm ss_fm */ + RPM_SMPS(S1, 1, 1, 0, 1225000, 1225000, NULL, 100000, 3p20, NONE, NONE), + RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL, 0, 1p60, NONE, NONE), + RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, NONE, NONE), + RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, AUTO), + RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, NONE, NONE), + RPM_SMPS(S8, 1, 1, 1, 2050000, 2050000, NULL, 100000, 1p60, NONE, NONE), + + /* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */ + RPM_LDO(L1, 1, 1, 0, 1050000, 1050000, "8921_s4", 0, 10000), + RPM_LDO(L2, 0, 1, 0, 1200000, 1200000, "8921_s4", 0, 0), + RPM_LDO(L3, 0, 1, 0, 3075000, 3075000, NULL, 0, 0), + RPM_LDO(L4, 1, 1, 0, 1800000, 1800000, NULL, 10000, 10000), + RPM_LDO(L5, 0, 1, 0, 2950000, 2950000, NULL, 0, 0), + RPM_LDO(L6, 0, 1, 0, 2950000, 2950000, NULL, 0, 0), + RPM_LDO(L7, 1, 1, 0, 1850000, 2950000, NULL, 10000, 10000), + RPM_LDO(L8, 0, 1, 0, 2800000, 3000000, NULL, 0, 0), + RPM_LDO(L9, 0, 1, 0, 3000000, 3000000, NULL, 0, 0), + RPM_LDO(L10, 0, 1, 0, 3000000, 3000000, NULL, 0, 0), + RPM_LDO(L11, 0, 1, 0, 2850000, 2850000, NULL, 0, 0), + RPM_LDO(L12, 0, 1, 0, 1200000, 1200000, "8921_s4", 0, 0), + RPM_LDO(L14, 0, 1, 0, 1800000, 1800000, NULL, 0, 0), + RPM_LDO(L15, 0, 1, 0, 1800000, 2950000, NULL, 0, 0), + RPM_LDO(L16, 0, 1, 0, 2800000, 2800000, NULL, 0, 0), + RPM_LDO(L17, 0, 1, 0, 1800000, 2950000, NULL, 0, 0), + RPM_LDO(L18, 0, 1, 0, 1300000, 1300000, "8921_s4", 0, 0), + RPM_LDO(L21, 0, 1, 0, 1900000, 1900000, "8921_s8", 0, 0), + RPM_LDO(L22, 0, 1, 0, 2750000, 2750000, NULL, 0, 0), + RPM_LDO(L23, 1, 1, 1, 1800000, 1800000, "8921_s8", 10000, 10000), + RPM_LDO(L24, 0, 1, 1, 750000, 1150000, "8921_s1", 10000, 10000), + RPM_LDO(L25, 1, 1, 0, 1250000, 1250000, "8921_s1", 10000, 10000), + + /* ID a_on pd ss supply */ + RPM_VS(LVS1, 0, 1, 0, "8921_s4"), + RPM_VS(LVS2, 0, 1, 0, "8921_s1"), + RPM_VS(LVS3, 0, 1, 0, "8921_s4"), + RPM_VS(LVS4, 0, 1, 0, "8921_s4"), + RPM_VS(LVS5, 0, 1, 0, "8921_s4"), + RPM_VS(LVS6, 0, 1, 0, "8921_s4"), + RPM_VS(LVS7, 0, 1, 0, "8921_s4"), + + /* ID a_on ss min_uV max_uV supply freq */ + RPM_NCP(NCP, 0, 0, 1800000, 1800000, "8921_l6", 1p60), +}; + +int msm_pm8921_regulator_pdata_len __devinitdata = + ARRAY_SIZE(msm_pm8921_regulator_pdata); + +struct rpm_regulator_platform_data msm_rpm_regulator_pdata __devinitdata = { + .init_data = msm_rpm_regulator_init_data, + .num_regulators = ARRAY_SIZE(msm_rpm_regulator_init_data), + .version = RPM_VREG_VERSION_8960, + .vreg_id_vdd_mem = RPM_VREG_ID_PM8921_L24, + .vreg_id_vdd_dig = RPM_VREG_ID_PM8921_S3, +}; diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c new file mode 100644 index 00000000000..85785fc5992 --- /dev/null +++ b/arch/arm/mach-msm/board-8960-storage.c @@ -0,0 +1,383 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-8960.h" +#include "board-storage-common-a.h" + +/* MSM8960 has 5 SDCC controllers */ +enum sdcc_controllers { + SDCC1, + SDCC2, + SDCC3, + SDCC4, + SDCC5, + MAX_SDCC_CONTROLLER +}; + +/* All SDCC controllers require VDD/VCC voltage */ +static struct msm_mmc_reg_data mmc_vdd_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .name = "sdc_vdd", + .high_vol_level = 2950000, + .low_vol_level = 2950000, + .always_on = 1, + .lpm_sup = 1, + .lpm_uA = 9000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC2 : SDIO slot connected */ + [SDCC2] = { + .name = "sdc_vdd", + .high_vol_level = 1800000, + .low_vol_level = 1800000, + .always_on = 1, + .lpm_sup = 1, + .lpm_uA = 9000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .name = "sdc_vdd", + .high_vol_level = 2950000, + .low_vol_level = 2950000, + .hpm_uA = 600000, /* 600mA */ + } +}; + +/* SDCC controllers may require voting for IO operating voltage */ +static struct msm_mmc_reg_data mmc_vdd_io_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .name = "sdc_vdd_io", + .always_on = 1, + .high_vol_level = 1800000, + .low_vol_level = 1800000, + .hpm_uA = 200000, /* 200mA */ + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .name = "sdc_vdd_io", + .high_vol_level = 2950000, + .low_vol_level = 1850000, + .always_on = 1, + .lpm_sup = 1, + /* Max. Active current required is 16 mA */ + .hpm_uA = 16000, + /* + * Sleep current required is ~300 uA. But min. vote can be + * in terms of mA (min. 1 mA). So let's vote for 2 mA + * during sleep. + */ + .lpm_uA = 2000, + }, + /* SDCC4 : SDIO slot connected */ + [SDCC4] = { + .name = "sdc_vdd_io", + .high_vol_level = 1800000, + .low_vol_level = 1800000, + .always_on = 1, + .lpm_sup = 1, + .hpm_uA = 200000, /* 200mA */ + .lpm_uA = 2000, + }, +}; + +static struct msm_mmc_slot_reg_data mmc_slot_vreg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : eMMC card connected */ + [SDCC1] = { + .vdd_data = &mmc_vdd_reg_data[SDCC1], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC1], + }, + /* SDCC2 : SDIO card slot connected */ + [SDCC2] = { + .vdd_data = &mmc_vdd_reg_data[SDCC2], + }, + /* SDCC3 : External card slot connected */ + [SDCC3] = { + .vdd_data = &mmc_vdd_reg_data[SDCC3], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC3], + }, + /* SDCC4 : SDIO card slot connected */ + [SDCC4] = { + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC4], + }, +}; + +/* SDC1 pad data */ +static struct msm_mmc_pad_drv sdc1_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_16MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_10MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_10MA} +}; + +static struct msm_mmc_pad_drv sdc1_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +/* SDC3 pad data */ +static struct msm_mmc_pad_drv sdc3_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_8MA} +}; + +static struct msm_mmc_pad_drv sdc3_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc3_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC3_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc3_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC3_CLK, GPIO_CFG_NO_PULL}, + /* + * SDC3 CMD line should be PULLed UP otherwise fluid platform will + * see transitions (1 -> 0 and 0 -> 1) on card detection line, + * which would result in false card detection interrupts. + */ + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + /* + * Keeping DATA lines status to PULL UP will make sure that + * there is no current leak during sleep if external pull up + * is connected to DATA lines. + */ + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull_data mmc_pad_pull_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_pull_on_cfg, + .off = sdc1_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_pull_on_cfg) + }, + [SDCC3] = { + .on = sdc3_pad_pull_on_cfg, + .off = sdc3_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc3_pad_pull_on_cfg) + }, +}; + +static struct msm_mmc_pad_drv_data mmc_pad_drv_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_drv_on_cfg, + .off = sdc1_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_drv_on_cfg) + }, + [SDCC3] = { + .on = sdc3_pad_drv_on_cfg, + .off = sdc3_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc3_pad_drv_on_cfg) + }, +}; + +struct msm_mmc_gpio sdc2_gpio[] = { + {92, "sdc2_dat_3"}, + {91, "sdc2_dat_2"}, + {90, "sdc2_dat_1"}, + {89, "sdc2_dat_0"}, + {97, "sdc2_cmd"}, + {98, "sdc2_clk"} +}; + +struct msm_mmc_gpio sdc4_gpio[] = { + {83, "sdc4_dat_3"}, + {84, "sdc4_dat_2"}, + {85, "sdc4_dat_1"}, + {86, "sdc4_dat_0"}, + {87, "sdc4_cmd"}, + {88, "sdc4_clk"} +}; + +struct msm_mmc_gpio_data mmc_gpio_data[MAX_SDCC_CONTROLLER] = { + [SDCC2] = { + .gpio = sdc2_gpio, + .size = ARRAY_SIZE(sdc2_gpio), + }, + [SDCC4] = { + .gpio = sdc4_gpio, + .size = ARRAY_SIZE(sdc4_gpio), + }, +}; + +static struct msm_mmc_pad_data mmc_pad_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pull = &mmc_pad_pull_data[SDCC1], + .drv = &mmc_pad_drv_data[SDCC1] + }, + [SDCC3] = { + .pull = &mmc_pad_pull_data[SDCC3], + .drv = &mmc_pad_drv_data[SDCC3] + }, +}; + +static struct msm_mmc_pin_data mmc_slot_pin_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pad_data = &mmc_pad_data[SDCC1], + }, + [SDCC2] = { + .is_gpio = 1, + .gpio_data = &mmc_gpio_data[SDCC2], + }, + [SDCC3] = { + .pad_data = &mmc_pad_data[SDCC3], + }, + [SDCC4] = { + .is_gpio = 1, + .gpio_data = &mmc_gpio_data[SDCC4], + }, +}; + +#define MSM_MPM_PIN_SDC1_DAT1 17 +#define MSM_MPM_PIN_SDC3_DAT1 21 + +static unsigned int sdc1_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static unsigned int sdc3_sup_clk_rates[] = { + 400000, 24000000, 48000000, 96000000, 192000000 +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct mmc_platform_data msm8960_sdc1_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, +#ifdef CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .sup_clk_table = sdc1_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc1_sup_clk_rates), + .pclk_src_dfab = 1, + .nonremovable = 1, + .vreg_data = &mmc_slot_vreg_data[SDCC1], + .pin_data = &mmc_slot_pin_data[SDCC1], + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC1_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static unsigned int sdc2_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +static struct mmc_platform_data msm8960_sdc2_data = { + .ocr_mask = MMC_VDD_165_195, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc2_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc2_sup_clk_rates), + .pclk_src_dfab = 1, + .vreg_data = &mmc_slot_vreg_data[SDCC2], + .pin_data = &mmc_slot_pin_data[SDCC2], + .sdiowakeup_irq = MSM_GPIO_TO_INT(90), + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data msm8960_sdc3_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc3_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc3_sup_clk_rates), + .pclk_src_dfab = 1, +#ifdef CONFIG_MMC_MSM_SDC3_WP_SUPPORT + .wpswitch_gpio = PM8921_GPIO_PM_TO_SYS(16), +#endif + .vreg_data = &mmc_slot_vreg_data[SDCC3], + .pin_data = &mmc_slot_pin_data[SDCC3], +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + .status_gpio = PM8921_GPIO_PM_TO_SYS(26), + .status_irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 26), + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + .is_status_gpio_active_low = true, +#endif + .xpc_cap = 1, + .uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | + MMC_CAP_UHS_SDR104 | MMC_CAP_MAX_CURRENT_600), + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC3_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static unsigned int sdc4_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +static struct mmc_platform_data msm8960_sdc4_data = { + .ocr_mask = MMC_VDD_165_195, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc4_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc4_sup_clk_rates), + .pclk_src_dfab = 1, + .vreg_data = &mmc_slot_vreg_data[SDCC4], + .pin_data = &mmc_slot_pin_data[SDCC4], + .sdiowakeup_irq = MSM_GPIO_TO_INT(85), + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +void __init msm8960_init_mmc(void) +{ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + /* SDC1 : eMMC card connected */ + msm_add_sdcc(1, &msm8960_sdc1_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + /* SDC2: SDIO slot for WLAN*/ + msm_add_sdcc(2, &msm8960_sdc2_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + /* SDC3: External card slot */ + msm_add_sdcc(3, &msm8960_sdc3_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + /* SDC4: SDIO slot for WLAN */ + msm_add_sdcc(4, &msm8960_sdc4_data); +#endif +} diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c new file mode 100644 index 00000000000..9c096b709ab --- /dev/null +++ b/arch/arm/mach-msm/board-8960.c @@ -0,0 +1,3261 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ANDROID_PMEM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_MSM_OTG_72K +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_WCD9310_CODEC +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "timer.h" +#include "devices.h" +#include "devices-msm8x60.h" +#include "spm.h" +#include "board-8960.h" +#include "pm.h" +#include +#include "rpm_resources.h" +#include +#include "acpuclock.h" +#include "smd_private.h" +#include "pm-boot.h" +#include "msm_watchdog.h" + +static struct platform_device msm_fm_platform_init = { + .name = "iris_fm", + .id = -1, +}; + +#define KS8851_RST_GPIO 89 +#define KS8851_IRQ_GPIO 90 +#define HAP_SHIFT_LVL_OE_GPIO 47 + +#define MHL_GPIO_INT 4 +#define MHL_GPIO_RESET 15 + +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) + +struct sx150x_platform_data msm8960_sx150x_data[] = { + [SX150X_CAM] = { + .gpio_base = GPIO_CAM_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0, + .io_pulldn_ena = 0xc0, + .io_open_drain_ena = 0x0, + .irq_summary = -1, + }, + [SX150X_LIQUID] = { + .gpio_base = GPIO_LIQUID_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0c08, + .io_pulldn_ena = 0x4060, + .io_open_drain_ena = 0x000c, + .io_polarity = 0, + .irq_summary = -1, + }, +}; + +#endif + +#define MSM_PMEM_ADSP_SIZE 0x7800000 +#define MSM_PMEM_AUDIO_SIZE 0x4CF000 +#define MSM_PMEM_SIZE 0x2800000 /* 40 Mbytes */ +#define MSM_LIQUID_PMEM_SIZE 0x4000000 /* 64 Mbytes */ +#define MSM_HDMI_PRIM_PMEM_SIZE 0x4000000 /* 64 Mbytes */ + +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +#define HOLE_SIZE 0x20000 +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x65000 +#ifdef CONFIG_MSM_IOMMU +#define MSM_ION_MM_SIZE 0x3800000 /* Need to be multiple of 64K */ +#define MSM_ION_SF_SIZE 0x0 +#define MSM_ION_QSECOM_SIZE 0x780000 /* (7.5MB) */ +#define MSM_ION_HEAP_NUM 7 +#else +#define MSM_ION_MM_SIZE MSM_PMEM_ADSP_SIZE +#define MSM_ION_SF_SIZE MSM_PMEM_SIZE +#define MSM_ION_QSECOM_SIZE 0x600000 /* (6MB) */ +#define MSM_ION_HEAP_NUM 8 +#endif +#define MSM_ION_MM_FW_SIZE (0x200000 - HOLE_SIZE) /* 128kb */ +#define MSM_ION_MFC_SIZE SZ_8K +#define MSM_ION_AUDIO_SIZE MSM_PMEM_AUDIO_SIZE + +#define MSM_LIQUID_ION_MM_SIZE (MSM_ION_MM_SIZE + 0x600000) +#define MSM_LIQUID_ION_SF_SIZE MSM_LIQUID_PMEM_SIZE +#define MSM_HDMI_PRIM_ION_SF_SIZE MSM_HDMI_PRIM_PMEM_SIZE + +#define MSM_MM_FW_SIZE (0x200000 - HOLE_SIZE) /* 2mb -128kb*/ +#define MSM8960_FIXED_AREA_START (0xa0000000 - (MSM_ION_MM_FW_SIZE + \ + HOLE_SIZE)) +#define MAX_FIXED_AREA_SIZE 0x10000000 +#define MSM8960_FW_START MSM8960_FIXED_AREA_START + +static unsigned msm_ion_sf_size = MSM_ION_SF_SIZE; +#else +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000 +#define MSM_ION_HEAP_NUM 1 +#endif + +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) +{ + pmem_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); +#endif + +#ifdef CONFIG_ANDROID_PMEM +static unsigned pmem_size = MSM_PMEM_SIZE; +static unsigned pmem_param_set; +static int __init pmem_size_setup(char *p) +{ + pmem_size = memparse(p, NULL); + pmem_param_set = 1; + return 0; +} +early_param("pmem_size", pmem_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; + +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; + +static int __init pmem_audio_size_setup(char *p) +{ + pmem_audio_size = memparse(p, NULL); + return 0; +} +early_param("pmem_audio_size", pmem_audio_size_setup); +#endif + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device msm8960_android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = {.platform_data = &android_pmem_pdata}, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; +static struct platform_device msm8960_android_pmem_adsp_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct android_pmem_platform_data android_pmem_audio_pdata = { + .name = "pmem_audio", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device msm8960_android_pmem_audio_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_audio_pdata }, +}; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + +struct fmem_platform_data msm8960_fmem_pdata = { +}; + +#define DSP_RAM_BASE_8960 0x8da00000 +#define DSP_RAM_SIZE_8960 0x1800000 +static int dspcrashd_pdata_8960 = 0xDEADDEAD; + +static struct resource resources_dspcrashd_8960[] = { + { + .name = "msm_dspcrashd", + .start = DSP_RAM_BASE_8960, + .end = DSP_RAM_BASE_8960 + DSP_RAM_SIZE_8960, + .flags = IORESOURCE_DMA, + }, +}; + +static struct platform_device msm_device_dspcrashd_8960 = { + .name = "msm_dspcrashd", + .num_resources = ARRAY_SIZE(resources_dspcrashd_8960), + .resource = resources_dspcrashd_8960, + .dev = { .platform_data = &dspcrashd_pdata_8960 }, +}; + +static struct memtype_reserve msm8960_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init reserve_rtb_memory(void) +{ +#if defined(CONFIG_MSM_RTB) + msm8960_reserve_table[MEMTYPE_EBI1].size += msm8960_rtb_pdata.size; +#endif +} + +static void __init size_pmem_devices(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + android_pmem_adsp_pdata.size = pmem_adsp_size; + + if (!pmem_param_set) { + if (machine_is_msm8960_liquid()) + pmem_size = MSM_LIQUID_PMEM_SIZE; + if (msm8960_hdmi_as_primary_selected()) + pmem_size = MSM_HDMI_PRIM_PMEM_SIZE; + } + + android_pmem_pdata.size = pmem_size; + android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +} + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm8960_reserve_table[p->memory_type].size += p->size; +} +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + reserve_memory_for(&android_pmem_adsp_pdata); + reserve_memory_for(&android_pmem_pdata); + reserve_memory_for(&android_pmem_audio_pdata); +#endif + msm8960_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif +} + +static int msm8960_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +#define FMEM_ENABLED 0 + +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_cp_heap_pdata cp_mm_msm8960_ion_pdata = { + .permission_type = IPT_TYPE_MM_CARVEOUT, + .align = SZ_64K, + .reusable = FMEM_ENABLED, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_MIDDLE, + .iommu_map_all = 1, + .iommu_2x_map_domain = VIDEO_DOMAIN, +}; + +static struct ion_cp_heap_pdata cp_mfc_msm8960_ion_pdata = { + .permission_type = IPT_TYPE_MFC_SHAREDMEM, + .align = PAGE_SIZE, + .reusable = 0, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_HIGH, +}; + +static struct ion_co_heap_pdata co_msm8960_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, + .mem_is_fmem = 0, +}; + +static struct ion_co_heap_pdata fw_co_msm8960_ion_pdata = { + .adjacent_mem_id = ION_CP_MM_HEAP_ID, + .align = SZ_128K, + .mem_is_fmem = FMEM_ENABLED, + .fixed_position = FIXED_LOW, +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. Due to + * video hardware restrictions and content protection the FW heap has to + * be allocated adjacent (below) the MM heap and the MFC heap has to be + * allocated after the MM heap to ensure MFC heap is not more than 256MB + * away from the base address of the FW heap. + * However, the order of FW heap and MM heap doesn't matter since these + * two heaps are taken care of by separate code to ensure they are adjacent + * to each other. + * Don't swap the order unless you know what you are doing! + */ +static struct ion_platform_data msm8960_ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + { + .id = ION_CP_MM_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MM_HEAP_NAME, + .size = MSM_ION_MM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mm_msm8960_ion_pdata, + }, + { + .id = ION_MM_FIRMWARE_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_MM_FIRMWARE_HEAP_NAME, + .size = MSM_ION_MM_FW_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &fw_co_msm8960_ion_pdata, + }, + { + .id = ION_CP_MFC_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MFC_HEAP_NAME, + .size = MSM_ION_MFC_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mfc_msm8960_ion_pdata, + }, +#ifndef CONFIG_MSM_IOMMU + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .size = MSM_ION_SF_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_msm8960_ion_pdata, + }, +#endif + { + .id = ION_IOMMU_HEAP_ID, + .type = ION_HEAP_TYPE_IOMMU, + .name = ION_IOMMU_HEAP_NAME, + }, + { + .id = ION_QSECOM_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_QSECOM_HEAP_NAME, + .size = MSM_ION_QSECOM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_msm8960_ion_pdata, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .size = MSM_ION_AUDIO_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_msm8960_ion_pdata, + }, +#endif + } +}; + +static struct platform_device msm8960_ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &msm8960_ion_pdata }, +}; +#endif + +struct platform_device msm8960_fmem_device = { + .name = "fmem", + .id = 1, + .dev = { .platform_data = &msm8960_fmem_pdata }, +}; + +static void __init adjust_mem_for_liquid(void) +{ + unsigned int i; + + if (!pmem_param_set) { + if (machine_is_msm8960_liquid()) + msm_ion_sf_size = MSM_LIQUID_ION_SF_SIZE; + + if (msm8960_hdmi_as_primary_selected()) + msm_ion_sf_size = MSM_HDMI_PRIM_ION_SF_SIZE; + + if (machine_is_msm8960_liquid() || + msm8960_hdmi_as_primary_selected()) { + for (i = 0; i < msm8960_ion_pdata.nr; i++) { + if (msm8960_ion_pdata.heaps[i].id == + ION_SF_HEAP_ID) { + msm8960_ion_pdata.heaps[i].size = + msm_ion_sf_size; + pr_debug("msm_ion_sf_size 0x%x\n", + msm_ion_sf_size); + break; + } + } + } + } +} + +static void __init reserve_mem_for_ion(enum ion_memory_types mem_type, + unsigned long size) +{ + msm8960_reserve_table[mem_type].size += size; +} + +static void __init msm8960_reserve_fixed_area(unsigned long fixed_area_size) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + int ret; + + if (fixed_area_size > MAX_FIXED_AREA_SIZE) + panic("fixed area size is larger than %dM\n", + MAX_FIXED_AREA_SIZE >> 20); + + reserve_info->fixed_area_size = fixed_area_size; + reserve_info->fixed_area_start = MSM8960_FW_START; + + ret = memblock_remove(reserve_info->fixed_area_start, + reserve_info->fixed_area_size); + BUG_ON(ret); +#endif +} + +/** + * Reserve memory for ION and calculate amount of reusable memory for fmem. + * We only reserve memory for heaps that are not reusable. However, we only + * support one reusable heap at the moment so we ignore the reusable flag for + * other than the first heap with reusable flag set. Also handle special case + * for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be + * at a higher address than FW in addition to not more than 256MB away from the + * base address of the firmware. This means that if MM is reusable the other + * two heaps must be allocated in the same region as FW. This is handled by the + * mem_is_fmem flag in the platform data. In addition the MM heap must be + * adjacent to the FW heap for content protection purposes. + */ +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + unsigned int i; + unsigned int reusable_count = 0; + unsigned int fixed_size = 0; + unsigned int fixed_low_size, fixed_middle_size, fixed_high_size; + unsigned long fixed_low_start, fixed_middle_start, fixed_high_start; + + adjust_mem_for_liquid(); + msm8960_fmem_pdata.size = 0; + msm8960_fmem_pdata.reserved_size_low = 0; + msm8960_fmem_pdata.reserved_size_high = 0; + msm8960_fmem_pdata.align = PAGE_SIZE; + fixed_low_size = 0; + fixed_middle_size = 0; + fixed_high_size = 0; + + /* We only support 1 reusable heap. Check if more than one heap + * is specified as reusable and set as non-reusable if found. + */ + for (i = 0; i < msm8960_ion_pdata.nr; ++i) { + const struct ion_platform_heap *heap = + &(msm8960_ion_pdata.heaps[i]); + + if (heap->type == ION_HEAP_TYPE_CP && heap->extra_data) { + struct ion_cp_heap_pdata *data = heap->extra_data; + + reusable_count += (data->reusable) ? 1 : 0; + + if (data->reusable && reusable_count > 1) { + pr_err("%s: Too many heaps specified as " + "reusable. Heap %s was not configured " + "as reusable.\n", __func__, heap->name); + data->reusable = 0; + } + } + } + + for (i = 0; i < msm8960_ion_pdata.nr; ++i) { + struct ion_platform_heap *heap = + &(msm8960_ion_pdata.heaps[i]); + int align = SZ_4K; + int iommu_map_all = 0; + int adjacent_mem_id = INVALID_HEAP_ID; + + if (heap->extra_data) { + int fixed_position = NOT_FIXED; + int mem_is_fmem = 0; + + switch (heap->type) { + case ION_HEAP_TYPE_CP: + mem_is_fmem = ((struct ion_cp_heap_pdata *) + heap->extra_data)->mem_is_fmem; + fixed_position = ((struct ion_cp_heap_pdata *) + heap->extra_data)->fixed_position; + align = ((struct ion_cp_heap_pdata *) + heap->extra_data)->align; + iommu_map_all = + ((struct ion_cp_heap_pdata *) + heap->extra_data)->iommu_map_all; + break; + case ION_HEAP_TYPE_CARVEOUT: + mem_is_fmem = ((struct ion_co_heap_pdata *) + heap->extra_data)->mem_is_fmem; + fixed_position = ((struct ion_co_heap_pdata *) + heap->extra_data)->fixed_position; + adjacent_mem_id = ((struct ion_co_heap_pdata *) + heap->extra_data)->adjacent_mem_id; + break; + default: + break; + } + + if (iommu_map_all) { + if (heap->size & (SZ_64K-1)) { + heap->size = ALIGN(heap->size, SZ_64K); + pr_info("Heap %s not aligned to 64K. Adjusting size to %x\n", + heap->name, heap->size); + } + } + + if (mem_is_fmem && adjacent_mem_id != INVALID_HEAP_ID) + msm8960_fmem_pdata.align = align; + + if (fixed_position != NOT_FIXED) + fixed_size += heap->size; + else + reserve_mem_for_ion(MEMTYPE_EBI1, heap->size); + + if (fixed_position == FIXED_LOW) + fixed_low_size += heap->size; + else if (fixed_position == FIXED_MIDDLE) + fixed_middle_size += heap->size; + else if (fixed_position == FIXED_HIGH) + fixed_high_size += heap->size; + + if (mem_is_fmem) + msm8960_fmem_pdata.size += heap->size; + } + } + + if (!fixed_size) + return; + + if (msm8960_fmem_pdata.size) { + msm8960_fmem_pdata.reserved_size_low = fixed_low_size + + HOLE_SIZE; + msm8960_fmem_pdata.reserved_size_high = fixed_high_size; + } + + /* Since the fixed area may be carved out of lowmem, + * make sure the length is a multiple of 1M. + */ + fixed_size = (fixed_size + MSM_MM_FW_SIZE + SECTION_SIZE - 1) + & SECTION_MASK; + msm8960_reserve_fixed_area(fixed_size); + + fixed_low_start = MSM8960_FIXED_AREA_START; + fixed_middle_start = fixed_low_start + fixed_low_size + HOLE_SIZE; + fixed_high_start = fixed_middle_start + fixed_middle_size; + + for (i = 0; i < msm8960_ion_pdata.nr; ++i) { + struct ion_platform_heap *heap = &(msm8960_ion_pdata.heaps[i]); + + if (heap->extra_data) { + int fixed_position = NOT_FIXED; + struct ion_cp_heap_pdata *pdata = NULL; + + switch (heap->type) { + case ION_HEAP_TYPE_CP: + pdata = + (struct ion_cp_heap_pdata *)heap->extra_data; + fixed_position = pdata->fixed_position; + break; + case ION_HEAP_TYPE_CARVEOUT: + fixed_position = ((struct ion_co_heap_pdata *) + heap->extra_data)->fixed_position; + break; + default: + break; + } + + switch (fixed_position) { + case FIXED_LOW: + heap->base = fixed_low_start; + break; + case FIXED_MIDDLE: + heap->base = fixed_middle_start; + pdata->secure_base = fixed_middle_start + - HOLE_SIZE; + pdata->secure_size = HOLE_SIZE + heap->size; + break; + case FIXED_HIGH: + heap->base = fixed_high_start; + break; + default: + break; + } + } + } +#endif +} + +static void __init reserve_mdp_memory(void) +{ + msm8960_mdp_writeback(msm8960_reserve_table); +} + +static void __init reserve_cache_dump_memory(void) +{ +#ifdef CONFIG_MSM_CACHE_DUMP + unsigned int total; + + total = msm8960_cache_dump_pdata.l1_size + + msm8960_cache_dump_pdata.l2_size; + msm8960_reserve_table[MEMTYPE_EBI1].size += total; +#endif +} + +static void __init msm8960_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); + reserve_ion_memory(); + reserve_mdp_memory(); + reserve_rtb_memory(); + reserve_cache_dump_memory(); +} + +static struct reserve_info msm8960_reserve_info __initdata = { + .memtype_reserve_table = msm8960_reserve_table, + .calculate_reserve_sizes = msm8960_calculate_reserve_sizes, + .reserve_fixed_area = msm8960_reserve_fixed_area, + .paddr_to_memtype = msm8960_paddr_to_memtype, +}; + +static int msm8960_memory_bank_size(void) +{ + return 1<<29; +} + +static void __init locate_unstable_memory(void) +{ + struct membank *mb = &meminfo.bank[meminfo.nr_banks - 1]; + unsigned long bank_size; + unsigned long low, high; + + bank_size = msm8960_memory_bank_size(); + msm8960_reserve_info.bank_size = bank_size; + + low = meminfo.bank[0].start; + high = mb->start + mb->size; + + /* Check if 32 bit overflow occured */ + if (high < mb->start) + high = ~0UL; + + if (high < MAX_FIXED_AREA_SIZE + MSM8960_FIXED_AREA_START) + panic("fixed area extends beyond end of memory\n"); + + low &= ~(bank_size - 1); + + if (high - low <= bank_size) + goto no_dmm; + +#ifdef CONFIG_ENABLE_DMM + msm8960_reserve_info.low_unstable_address = mb->start - + MIN_MEMORY_BLOCK_SIZE + mb->size; + msm8960_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE; + pr_info("low unstable address %lx max size %lx bank size %lx\n", + msm8960_reserve_info.low_unstable_address, + msm8960_reserve_info.max_unstable_size, + msm8960_reserve_info.bank_size); + return; +#endif +no_dmm: + msm8960_reserve_info.low_unstable_address = high; + msm8960_reserve_info.max_unstable_size = 0; +} + +static void __init place_movable_zone(void) +{ +#ifdef CONFIG_ENABLE_DMM + movable_reserved_start = msm8960_reserve_info.low_unstable_address; + movable_reserved_size = msm8960_reserve_info.max_unstable_size; + pr_info("movable zone start %lx size %lx\n", + movable_reserved_start, movable_reserved_size); +#endif +} + +static void __init msm8960_early_memory(void) +{ + reserve_info = &msm8960_reserve_info; + locate_unstable_memory(); + place_movable_zone(); +} + +static char prim_panel_name[PANEL_NAME_MAX_LEN]; +static char ext_panel_name[PANEL_NAME_MAX_LEN]; +static int __init prim_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(prim_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("prim_display", prim_display_setup); + +static int __init ext_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(ext_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("ext_display", ext_display_setup); + +static void __init msm8960_reserve(void) +{ + msm8960_set_display_params(prim_panel_name, ext_panel_name); + msm_reserve(); + if (msm8960_fmem_pdata.size) { +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + if (reserve_info->fixed_area_size) { + msm8960_fmem_pdata.phys = + reserve_info->fixed_area_start + MSM_MM_FW_SIZE; + pr_info("mm fw at %lx (fixed) size %x\n", + reserve_info->fixed_area_start, MSM_MM_FW_SIZE); + pr_info("fmem start %lx (fixed) size %lx\n", + msm8960_fmem_pdata.phys, + msm8960_fmem_pdata.size); + } +#endif + } +} + +static int msm8960_change_memory_power(u64 start, u64 size, + int change_type) +{ + return soc_change_memory_power(start, size, change_type); +} + +static void __init msm8960_allocate_memory_regions(void) +{ + msm8960_allocate_fb_region(); +} + +#ifdef CONFIG_WCD9310_CODEC + +#define TABLA_INTERRUPT_BASE (NR_MSM_IRQS + NR_GPIO_IRQS + NR_PM8921_IRQS) + +/* Micbias setting is based on 8660 CDP/MTP/FLUID requirement + * 4 micbiases are used to power various analog and digital + * microphones operating at 1800 mV. Technically, all micbiases + * can source from single cfilter since all microphones operate + * at the same voltage level. The arrangement below is to make + * sure all cfilters are exercised. LDO_H regulator ouput level + * does not need to be as high as 2.85V. It is choosen for + * microphone sensitivity purpose. + */ +static struct wcd9xxx_pdata tabla_platform_data = { + .slimbus_slave_device = { + .name = "tabla-slave", + .e_addr = {0, 0, 0x10, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(62), + .irq_base = TABLA_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = PM8921_GPIO_PM_TO_SYS(34), + .micbias = { + .ldoh_v = TABLA_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 2700, + .cfilt3_mv = 1800, + .bias1_cfilt_sel = TABLA_CFILT1_SEL, + .bias2_cfilt_sel = TABLA_CFILT2_SEL, + .bias3_cfilt_sel = TABLA_CFILT3_SEL, + .bias4_cfilt_sel = TABLA_CFILT3_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device msm_slim_tabla = { + .name = "tabla-slim", + .e_addr = {0, 1, 0x10, 0, 0x17, 2}, + .dev = { + .platform_data = &tabla_platform_data, + }, +}; + +static struct wcd9xxx_pdata tabla20_platform_data = { + .slimbus_slave_device = { + .name = "tabla-slave", + .e_addr = {0, 0, 0x60, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(62), + .irq_base = TABLA_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = PM8921_GPIO_PM_TO_SYS(34), + .micbias = { + .ldoh_v = TABLA_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 2700, + .cfilt3_mv = 1800, + .bias1_cfilt_sel = TABLA_CFILT1_SEL, + .bias2_cfilt_sel = TABLA_CFILT2_SEL, + .bias3_cfilt_sel = TABLA_CFILT3_SEL, + .bias4_cfilt_sel = TABLA_CFILT3_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1225000, + .max_uV = 1250000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device msm_slim_tabla20 = { + .name = "tabla2x-slim", + .e_addr = {0, 1, 0x60, 0, 0x17, 2}, + .dev = { + .platform_data = &tabla20_platform_data, + }, +}; +#endif + +static struct slim_boardinfo msm_slim_devices[] = { +#ifdef CONFIG_WCD9310_CODEC + { + .bus_num = 1, + .slim_slave = &msm_slim_tabla, + }, + { + .bus_num = 1, + .slim_slave = &msm_slim_tabla20, + }, +#endif + /* add more slimbus slaves as needed */ +}; + +#define MSM_WCNSS_PHYS 0x03000000 +#define MSM_WCNSS_SIZE 0x280000 + +static struct resource resources_wcnss_wlan[] = { + { + .start = RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + .end = RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + .name = "wcnss_wlanrx_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, + .end = RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, + .name = "wcnss_wlantx_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_WCNSS_PHYS, + .end = MSM_WCNSS_PHYS + MSM_WCNSS_SIZE - 1, + .name = "wcnss_mmio", + .flags = IORESOURCE_MEM, + }, + { + .start = 84, + .end = 88, + .name = "wcnss_gpios_5wire", + .flags = IORESOURCE_IO, + }, +}; + +static struct qcom_wcnss_opts qcom_wcnss_pdata = { + .has_48mhz_xo = 1, +}; + +static struct platform_device msm_device_wcnss_wlan = { + .name = "wcnss_wlan", + .id = 0, + .num_resources = ARRAY_SIZE(resources_wcnss_wlan), + .resource = resources_wcnss_wlan, + .dev = {.platform_data = &qcom_wcnss_pdata}, +}; + +#ifdef CONFIG_QSEECOM +/* qseecom bus scaling */ +static struct msm_bus_vectors qseecom_clks_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 0, + .ab = 0, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = 0, + .ab = 0, + }, +}; + +static struct msm_bus_vectors qseecom_enable_dfab_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = (492 * 8) * 1000000UL, + .ab = (492 * 8) * 100000UL, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = 0, + .ab = 0, + }, +}; + +static struct msm_bus_vectors qseecom_enable_sfpb_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 0, + .ab = 0, + }, + { + .src = MSM_BUS_MASTER_SPDM, + .dst = MSM_BUS_SLAVE_SPDM, + .ib = (64 * 8) * 1000000UL, + .ab = (64 * 8) * 100000UL, + }, +}; + +static struct msm_bus_paths qseecom_hw_bus_scale_usecases[] = { + { + ARRAY_SIZE(qseecom_clks_init_vectors), + qseecom_clks_init_vectors, + }, + { + ARRAY_SIZE(qseecom_enable_dfab_vectors), + qseecom_enable_sfpb_vectors, + }, + { + ARRAY_SIZE(qseecom_enable_sfpb_vectors), + qseecom_enable_sfpb_vectors, + }, +}; + +static struct msm_bus_scale_pdata qseecom_bus_pdata = { + qseecom_hw_bus_scale_usecases, + ARRAY_SIZE(qseecom_hw_bus_scale_usecases), + .name = "qsee", +}; + +static struct platform_device qseecom_device = { + .name = "qseecom", + .id = 0, + .dev = { + .platform_data = &qseecom_bus_pdata, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +#define QCE_SIZE 0x10000 +#define QCE_0_BASE 0x18500000 + +#define QCE_HW_KEY_SUPPORT 0 +#define QCE_SHA_HMAC_SUPPORT 1 +#define QCE_SHARE_CE_RESOURCE 1 +#define QCE_CE_SHARED 0 + +/* Begin Bus scaling definitions */ +static struct msm_bus_vectors crypto_hw_init_vectors[] = { + { + .src = MSM_BUS_MASTER_ADM_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_ADM_PORT1, + .dst = MSM_BUS_SLAVE_GSBI1_UART, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors crypto_hw_active_vectors[] = { + { + .src = MSM_BUS_MASTER_ADM_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 70000000UL, + .ib = 70000000UL, + }, + { + .src = MSM_BUS_MASTER_ADM_PORT1, + .dst = MSM_BUS_SLAVE_GSBI1_UART, + .ab = 2480000000UL, + .ib = 2480000000UL, + }, +}; + +static struct msm_bus_paths crypto_hw_bus_scale_usecases[] = { + { + ARRAY_SIZE(crypto_hw_init_vectors), + crypto_hw_init_vectors, + }, + { + ARRAY_SIZE(crypto_hw_active_vectors), + crypto_hw_active_vectors, + }, +}; + +static struct msm_bus_scale_pdata crypto_hw_bus_scale_pdata = { + crypto_hw_bus_scale_usecases, + ARRAY_SIZE(crypto_hw_bus_scale_usecases), + .name = "cryptohw", +}; +/* End Bus Scaling Definitions*/ + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = &crypto_hw_bus_scale_pdata, +}; + +static struct platform_device qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = &crypto_hw_bus_scale_pdata, +}; + +static struct platform_device qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; +#endif + +static struct mdm_platform_data sglte_platform_data = { + .mdm_version = "4.0", + .ramdump_delay_ms = 1000, + .soft_reset_inverted = 1, + .peripheral_platform_device = NULL, +}; + +#define MSM_TSIF0_PHYS (0x18200000) +#define MSM_TSIF1_PHYS (0x18201000) +#define MSM_TSIF_SIZE (0x200) +#define MSM_TSPP_PHYS (0x18202000) +#define MSM_TSPP_SIZE (0x1000) +#define MSM_TSPP_BAM_PHYS (0x18204000) +#define MSM_TSPP_BAM_SIZE (0x2000) + +#define TSIF_0_CLK GPIO_CFG(75, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_EN GPIO_CFG(76, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_DATA GPIO_CFG(77, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_SYNC GPIO_CFG(82, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_CLK GPIO_CFG(79, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_EN GPIO_CFG(80, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_DATA GPIO_CFG(81, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_SYNC GPIO_CFG(78, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) + +static const struct msm_gpio tsif_gpios[] = { + { .gpio_cfg = TSIF_0_CLK, .label = "tsif0_clk", }, + { .gpio_cfg = TSIF_0_EN, .label = "tsif0_en", }, + { .gpio_cfg = TSIF_0_DATA, .label = "tsif0_data", }, + { .gpio_cfg = TSIF_0_SYNC, .label = "tsif0_sync", }, + { .gpio_cfg = TSIF_1_CLK, .label = "tsif1_clk", }, + { .gpio_cfg = TSIF_1_EN, .label = "tsif1_en", }, + { .gpio_cfg = TSIF_1_DATA, .label = "tsif1_data", }, + { .gpio_cfg = TSIF_1_SYNC, .label = "tsif1_sync", }, +}; + +static struct resource tspp_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = TSIF_TSPP_IRQ, + .end = TSIF1_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF0_PHYS, + .end = MSM_TSIF0_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF1_PHYS, + .end = MSM_TSIF1_PHYS + MSM_TSIF_SIZE - 1, + }, + [3] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSPP_PHYS, + .end = MSM_TSPP_PHYS + MSM_TSPP_SIZE - 1, + }, + [4] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSPP_BAM_PHYS, + .end = MSM_TSPP_BAM_PHYS + MSM_TSPP_BAM_SIZE - 1, + }, +}; + +static struct msm_tspp_platform_data tspp_platform_data = { + .num_gpios = ARRAY_SIZE(tsif_gpios), + .gpios = tsif_gpios, + .tsif_pclk = "tsif_pclk", + .tsif_ref_clk = "tsif_ref_clk", +}; + +static struct platform_device msm_device_tspp = { + .name = "msm_tspp", + .id = 0, + .num_resources = ARRAY_SIZE(tspp_resources), + .resource = tspp_resources, + .dev = { + .platform_data = &tspp_platform_data + }, +}; + +#define MSM_SHARED_RAM_PHYS 0x80000000 + +static void __init msm8960_map_io(void) +{ + msm_shared_ram_phys = MSM_SHARED_RAM_PHYS; + msm_map_msm8960_io(); + + if (socinfo_init() < 0) + pr_err("socinfo_init() failed!\n"); +} + +static void __init msm8960_init_irq(void) +{ + struct msm_mpm_device_data *data = NULL; + +#ifdef CONFIG_MSM_MPM + data = &msm8960_mpm_dev_data; +#endif + + msm_mpm_irq_extn_init(data); + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, + (void *)MSM_QGIC_CPU_BASE); +} + +static void __init msm8960_init_buses(void) +{ +#ifdef CONFIG_MSM_BUS_SCALING + msm_bus_rpm_set_mt_mask(); + msm_bus_8960_apps_fabric_pdata.rpm_enabled = 1; + msm_bus_8960_sys_fabric_pdata.rpm_enabled = 1; + msm_bus_8960_mm_fabric_pdata.rpm_enabled = 1; + msm_bus_apps_fabric.dev.platform_data = + &msm_bus_8960_apps_fabric_pdata; + msm_bus_sys_fabric.dev.platform_data = &msm_bus_8960_sys_fabric_pdata; + msm_bus_mm_fabric.dev.platform_data = &msm_bus_8960_mm_fabric_pdata; + msm_bus_sys_fpb.dev.platform_data = &msm_bus_8960_sys_fpb_pdata; + msm_bus_cpss_fpb.dev.platform_data = &msm_bus_8960_cpss_fpb_pdata; +#endif +} + +static struct msm_spi_platform_data msm8960_qup_spi_gsbi1_pdata = { + .max_clock_speed = 15060000, +}; + +#ifdef CONFIG_USB_MSM_OTG_72K +static struct msm_otg_platform_data msm_otg_pdata; +#else +static int wr_phy_init_seq[] = { + 0x44, 0x80, /* set VBUS valid threshold + and disconnect valid threshold */ + 0x38, 0x81, /* update DC voltage level */ + 0x14, 0x82, /* set preemphasis and rise/fall time */ + 0x13, 0x83, /* set source impedance adjusment */ + -1}; + +static int liquid_v1_phy_init_seq[] = { + 0x44, 0x80,/* set VBUS valid threshold + and disconnect valid threshold */ + 0x3C, 0x81,/* update DC voltage level */ + 0x18, 0x82,/* set preemphasis and rise/fall time */ + 0x23, 0x83,/* set source impedance sdjusment */ + -1}; + +#ifdef CONFIG_MSM_BUS_SCALING +/* Bandwidth requests (zero) if no vote placed */ +static struct msm_bus_vectors usb_init_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +/* Bus bandwidth requests in Bytes/sec */ +static struct msm_bus_vectors usb_max_vectors[] = { + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 60000000, /* At least 480Mbps on bus. */ + .ib = 960000000, /* MAX bursts rate */ + }, +}; + +static struct msm_bus_paths usb_bus_scale_usecases[] = { + { + ARRAY_SIZE(usb_init_vectors), + usb_init_vectors, + }, + { + ARRAY_SIZE(usb_max_vectors), + usb_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata usb_bus_scale_pdata = { + usb_bus_scale_usecases, + ARRAY_SIZE(usb_bus_scale_usecases), + .name = "usb", +}; +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { + .mode = USB_OTG, + .otg_control = OTG_PMIC_CONTROL, + .phy_type = SNPS_28NM_INTEGRATED_PHY, + .pmic_id_irq = PM8921_USB_ID_IN_IRQ(PM8921_IRQ_BASE), + .power_budget = 750, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &usb_bus_scale_pdata, +#endif +}; +#endif + +#ifdef CONFIG_USB_EHCI_MSM_HSIC +#define HSIC_HUB_RESET_GPIO 91 +static struct msm_hsic_host_platform_data msm_hsic_pdata = { + .strobe = 150, + .data = 151, +}; + +static struct smsc_hub_platform_data hsic_hub_pdata = { + .hub_reset = HSIC_HUB_RESET_GPIO, +}; +#else +static struct msm_hsic_host_platform_data msm_hsic_pdata; +static struct smsc_hub_platform_data hsic_hub_pdata; +#endif + +static struct platform_device smsc_hub_device = { + .name = "msm_smsc_hub", + .id = -1, + .dev = { + .platform_data = &hsic_hub_pdata, + }, +}; + +#define PID_MAGIC_ID 0x71432909 +#define SERIAL_NUM_MAGIC_ID 0x61945374 +#define SERIAL_NUMBER_LENGTH 127 +#define DLOAD_USB_BASE_ADD 0x2A03F0C8 + +struct magic_num_struct { + uint32_t pid; + uint32_t serial_num; +}; + +struct dload_struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint16_t reserved4; + uint16_t pid; + char serial_number[SERIAL_NUMBER_LENGTH]; + uint16_t reserved5; + struct magic_num_struct magic_struct; +}; + +static int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) +{ + struct dload_struct __iomem *dload = 0; + + dload = ioremap(DLOAD_USB_BASE_ADD, sizeof(*dload)); + if (!dload) { + pr_err("%s: cannot remap I/O memory region: %08x\n", + __func__, DLOAD_USB_BASE_ADD); + return -ENXIO; + } + + pr_debug("%s: dload:%p pid:%x serial_num:%s\n", + __func__, dload, pid, snum); + /* update pid */ + dload->magic_struct.pid = PID_MAGIC_ID; + dload->pid = pid; + + /* update serial number */ + dload->magic_struct.serial_num = 0; + if (!snum) { + memset(dload->serial_number, 0, SERIAL_NUMBER_LENGTH); + goto out; + } + + dload->magic_struct.serial_num = SERIAL_NUM_MAGIC_ID; + strlcpy(dload->serial_number, snum, SERIAL_NUMBER_LENGTH); +out: + iounmap(dload); + return 0; +} + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static uint8_t spm_wfi_cmd_sequence[] __initdata = { + 0x03, 0x0f, +}; + +static uint8_t spm_power_collapse_without_rpm[] __initdata = { + 0x00, 0x24, 0x54, 0x10, + 0x09, 0x03, 0x01, + 0x10, 0x54, 0x30, 0x0C, + 0x24, 0x30, 0x0f, +}; + +static uint8_t spm_power_collapse_with_rpm[] __initdata = { + 0x00, 0x24, 0x54, 0x10, + 0x09, 0x07, 0x01, 0x0B, + 0x10, 0x54, 0x30, 0x0C, + 0x24, 0x30, 0x0f, +}; + +static struct msm_spm_seq_entry msm_spm_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_MODE_CLOCK_GATING, + .notify_rpm = false, + .cmd = spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = false, + .cmd = spm_power_collapse_without_rpm, + }, + [2] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = spm_power_collapse_with_rpm, + }, +}; + +static struct msm_spm_platform_data msm_spm_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, + [1] = { + .reg_base_addr = MSM_SAW1_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F, +#if defined(CONFIG_MSM_AVS_HW) + .reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00, +#endif + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x0060009C, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x0000001C, + .vctl_timeout_us = 50, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, +}; + +static uint8_t l2_spm_wfi_cmd_sequence[] __initdata = { + 0x00, 0x20, 0x03, 0x20, + 0x00, 0x0f, +}; + +static uint8_t l2_spm_gdhs_cmd_sequence[] __initdata = { + 0x00, 0x20, 0x34, 0x64, + 0x48, 0x07, 0x48, 0x20, + 0x50, 0x64, 0x04, 0x34, + 0x50, 0x0f, +}; +static uint8_t l2_spm_power_off_cmd_sequence[] __initdata = { + 0x00, 0x10, 0x34, 0x64, + 0x48, 0x07, 0x48, 0x10, + 0x50, 0x64, 0x04, 0x34, + 0x50, 0x0F, +}; + +static struct msm_spm_seq_entry msm_spm_l2_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_L2_MODE_RETENTION, + .notify_rpm = false, + .cmd = l2_spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_L2_MODE_GDHS, + .notify_rpm = true, + .cmd = l2_spm_gdhs_cmd_sequence, + }, + [2] = { + .mode = MSM_SPM_L2_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = l2_spm_power_off_cmd_sequence, + }, +}; + +static struct msm_spm_platform_data msm_spm_l2_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW_L2_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DLY] = 0x02020204, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x00A000AE, + .reg_init_values[MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x00A00020, + .modes = msm_spm_l2_seq_list, + .num_modes = ARRAY_SIZE(msm_spm_l2_seq_list), + }, +}; + +#define PM_HAP_EN_GPIO PM8921_GPIO_PM_TO_SYS(33) +#define PM_HAP_LEN_GPIO PM8921_GPIO_PM_TO_SYS(20) + +static struct msm_xo_voter *xo_handle_d1; + +static int isa1200_power(int on) +{ + int rc = 0; + + gpio_set_value(HAP_SHIFT_LVL_OE_GPIO, !!on); + + rc = on ? msm_xo_mode_vote(xo_handle_d1, MSM_XO_MODE_ON) : + msm_xo_mode_vote(xo_handle_d1, MSM_XO_MODE_OFF); + if (rc < 0) { + pr_err("%s: failed to %svote for TCXO D1 buffer%d\n", + __func__, on ? "" : "de-", rc); + goto err_xo_vote; + } + + return 0; + +err_xo_vote: + gpio_set_value(HAP_SHIFT_LVL_OE_GPIO, !on); + return rc; +} + +static int isa1200_dev_setup(bool enable) +{ + int rc = 0; + + struct pm_gpio hap_gpio_config = { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + .vin_sel = 2, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + }; + + if (enable == true) { + rc = pm8xxx_gpio_config(PM_HAP_EN_GPIO, &hap_gpio_config); + if (rc) { + pr_err("%s: pm8921 gpio %d config failed(%d)\n", + __func__, PM_HAP_EN_GPIO, rc); + return rc; + } + + rc = pm8xxx_gpio_config(PM_HAP_LEN_GPIO, &hap_gpio_config); + if (rc) { + pr_err("%s: pm8921 gpio %d config failed(%d)\n", + __func__, PM_HAP_LEN_GPIO, rc); + return rc; + } + + rc = gpio_request(HAP_SHIFT_LVL_OE_GPIO, "hap_shft_lvl_oe"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, HAP_SHIFT_LVL_OE_GPIO, rc); + return rc; + } + + rc = gpio_direction_output(HAP_SHIFT_LVL_OE_GPIO, 0); + if (rc) { + pr_err("%s: Unable to set direction\n", __func__); + goto free_gpio; + } + + xo_handle_d1 = msm_xo_get(MSM_XO_TCXO_D1, "isa1200"); + if (IS_ERR(xo_handle_d1)) { + rc = PTR_ERR(xo_handle_d1); + pr_err("%s: failed to get the handle for D1(%d)\n", + __func__, rc); + goto gpio_set_dir; + } + } else { + gpio_free(HAP_SHIFT_LVL_OE_GPIO); + + msm_xo_put(xo_handle_d1); + } + + return 0; + +gpio_set_dir: + gpio_set_value(HAP_SHIFT_LVL_OE_GPIO, 0); +free_gpio: + gpio_free(HAP_SHIFT_LVL_OE_GPIO); + return rc; +} + +static struct isa1200_regulator isa1200_reg_data[] = { + { + .name = "vcc_i2c", + .min_uV = ISA_I2C_VTG_MIN_UV, + .max_uV = ISA_I2C_VTG_MAX_UV, + .load_uA = ISA_I2C_CURR_UA, + }, +}; + +static struct isa1200_platform_data isa1200_1_pdata = { + .name = "vibrator", + .dev_setup = isa1200_dev_setup, + .power_on = isa1200_power, + .hap_en_gpio = PM_HAP_EN_GPIO, + .hap_len_gpio = PM_HAP_LEN_GPIO, + .max_timeout = 15000, + .mode_ctrl = PWM_GEN_MODE, + .pwm_fd = { + .pwm_div = 256, + }, + .is_erm = false, + .smart_en = true, + .ext_clk_en = true, + .chip_en = 1, + .regulator_info = isa1200_reg_data, + .num_regulators = ARRAY_SIZE(isa1200_reg_data), +}; + +static struct i2c_board_info msm_isa1200_board_info[] __initdata = { + { + I2C_BOARD_INFO("isa1200_1", 0x90>>1), + }, +}; + +#define CYTTSP_TS_GPIO_IRQ 11 +#define CYTTSP_TS_SLEEP_GPIO 50 +#define CYTTSP_TS_RESOUT_N_GPIO 52 + +/*virtual key support */ +static ssize_t tma340_vkeys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, 200, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":73:1120:97:97" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":230:1120:97:97" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":389:1120:97:97" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":544:1120:97:97" + "\n"); +} + +static struct kobj_attribute tma340_vkeys_attr = { + .attr = { + .mode = S_IRUGO, + }, + .show = &tma340_vkeys_show, +}; + +static struct attribute *tma340_properties_attrs[] = { + &tma340_vkeys_attr.attr, + NULL +}; + +static struct attribute_group tma340_properties_attr_group = { + .attrs = tma340_properties_attrs, +}; + + +static int cyttsp_platform_init(struct i2c_client *client) +{ + int rc = 0; + static struct kobject *tma340_properties_kobj; + + tma340_vkeys_attr.attr.name = "virtualkeys.cyttsp-i2c"; + tma340_properties_kobj = kobject_create_and_add("board_properties", + NULL); + if (tma340_properties_kobj) + rc = sysfs_create_group(tma340_properties_kobj, + &tma340_properties_attr_group); + if (!tma340_properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", + __func__); + + return 0; +} + +static struct cyttsp_regulator regulator_data[] = { + { + .name = "vdd", + .min_uV = CY_TMA300_VTG_MIN_UV, + .max_uV = CY_TMA300_VTG_MAX_UV, + .hpm_load_uA = CY_TMA300_CURR_24HZ_UA, + .lpm_load_uA = CY_TMA300_SLEEP_CURR_UA, + }, + /* TODO: Remove after runtime PM is enabled in I2C driver */ + { + .name = "vcc_i2c", + .min_uV = CY_I2C_VTG_MIN_UV, + .max_uV = CY_I2C_VTG_MAX_UV, + .hpm_load_uA = CY_I2C_CURR_UA, + .lpm_load_uA = CY_I2C_SLEEP_CURR_UA, + }, +}; + +static struct cyttsp_platform_data cyttsp_pdata = { + .panel_maxx = 634, + .panel_maxy = 1166, + .disp_maxx = 616, + .disp_maxy = 1023, + .disp_minx = 0, + .disp_miny = 16, + .flags = 0x01, + .gen = CY_GEN3, /* or */ + .use_st = CY_USE_ST, + .use_mt = CY_USE_MT, + .use_hndshk = CY_SEND_HNDSHK, + .use_trk_id = CY_USE_TRACKING_ID, + .use_sleep = CY_USE_DEEP_SLEEP_SEL | CY_USE_LOW_POWER_SEL, + .use_gestures = CY_USE_GESTURES, + .fw_fname = "cyttsp_8960_cdp.hex", + /* activate up to 4 groups + * and set active distance + */ + .gest_set = CY_GEST_GRP1 | CY_GEST_GRP2 | + CY_GEST_GRP3 | CY_GEST_GRP4 | + CY_ACT_DIST, + /* change act_intrvl to customize the Active power state + * scanning/processing refresh interval for Operating mode + */ + .act_intrvl = CY_ACT_INTRVL_DFLT, + /* change tch_tmout to customize the touch timeout for the + * Active power state for Operating mode + */ + .tch_tmout = CY_TCH_TMOUT_DFLT, + /* change lp_intrvl to customize the Low Power power state + * scanning/processing refresh interval for Operating mode + */ + .lp_intrvl = CY_LP_INTRVL_DFLT, + .sleep_gpio = CYTTSP_TS_SLEEP_GPIO, + .resout_gpio = CYTTSP_TS_RESOUT_N_GPIO, + .irq_gpio = CYTTSP_TS_GPIO_IRQ, + .regulator_info = regulator_data, + .num_regulators = ARRAY_SIZE(regulator_data), + .init = cyttsp_platform_init, + .correct_fw_ver = 9, +}; + +static struct i2c_board_info cyttsp_info[] __initdata = { + { + I2C_BOARD_INFO(CY_I2C_NAME, 0x24), + .platform_data = &cyttsp_pdata, +#ifndef CY_USE_TIMER + .irq = MSM_GPIO_TO_INT(CYTTSP_TS_GPIO_IRQ), +#endif /* CY_USE_TIMER */ + }, +}; + +/* configuration data for mxt1386 */ +static const u8 mxt1386_config_data[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 11, 2, 0, 11, 11, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T7 Object */ + 100, 16, 50, + /* T8 Object */ + 8, 0, 0, 0, 0, 0, 8, 14, 50, 215, + /* T9 Object */ + 131, 0, 0, 26, 42, 0, 32, 63, 3, 5, + 0, 2, 1, 113, 10, 10, 8, 10, 255, 2, + 85, 5, 0, 0, 20, 20, 75, 25, 202, 29, + 10, 10, 45, 46, + /* T15 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, + /* T18 Object */ + 0, 0, + /* T22 Object */ + 5, 0, 0, 0, 0, 0, 0, 0, 30, 0, + 0, 0, 5, 8, 10, 13, 0, + /* T24 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T25 Object */ + 3, 0, 188, 52, 52, 33, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T27 Object */ + 0, 0, 0, 0, 0, 0, 0, + /* T28 Object */ + 0, 0, 0, 8, 12, 60, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T41 Object */ + 0, 0, 0, 0, 0, 0, + /* T43 Object */ + 0, 0, 0, 0, 0, 0, +}; + +/* configuration data for mxt1386e using V1.0 firmware */ +static const u8 mxt1386e_config_data_v1_0[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 12, 1, 0, 17, 1, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T7 Object */ + 100, 16, 50, + /* T8 Object */ + 25, 0, 20, 20, 0, 0, 20, 50, 0, 0, + /* T9 Object */ + 131, 0, 0, 26, 42, 0, 32, 80, 2, 5, + 0, 5, 5, 0, 10, 30, 10, 10, 255, 2, + 85, 5, 10, 10, 10, 10, 135, 55, 70, 40, + 10, 5, 0, 0, 0, + /* T18 Object */ + 0, 0, + /* T24 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T25 Object */ + 3, 0, 60, 115, 156, 99, + /* T27 Object */ + 0, 0, 0, 0, 0, 0, 0, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T42 Object */ + 2, 0, 255, 0, 255, 0, 0, 0, 0, 0, + /* T43 Object */ + 0, 0, 0, 0, 0, 0, 0, + /* T46 Object */ + 64, 0, 20, 20, 0, 0, 0, 0, 0, + /* T47 Object */ + 0, 0, 0, 0, 0, 0, 3, 64, 66, 0, + /* T48 Object */ + 31, 64, 64, 0, 0, 0, 0, 0, 0, 0, + 48, 40, 0, 10, 10, 0, 0, 100, 10, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 52, 0, 12, 0, 17, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T56 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 99, 33, +}; + +/* configuration data for mxt1386e using V2.1 firmware */ +static const u8 mxt1386e_config_data_v2_1[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 12, 3, 0, 24, 5, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T7 Object */ + 100, 16, 50, + /* T8 Object */ + 25, 0, 20, 20, 0, 0, 20, 50, 0, 0, + /* T9 Object */ + 139, 0, 0, 26, 42, 0, 32, 80, 2, 5, + 0, 5, 5, 0, 10, 30, 10, 10, 255, 2, + 85, 5, 10, 10, 10, 10, 135, 55, 70, 40, + 10, 5, 0, 0, 0, + /* T18 Object */ + 0, 0, + /* T24 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T25 Object */ + 1, 0, 60, 115, 156, 99, + /* T27 Object */ + 0, 0, 0, 0, 0, 0, 0, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T42 Object */ + 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, + /* T43 Object */ + 0, 0, 0, 0, 0, 0, 0, 64, 0, 8, + 16, + /* T46 Object */ + 64, 0, 20, 20, 0, 0, 0, 0, 0, + /* T47 Object */ + 0, 0, 0, 0, 0, 0, 3, 64, 66, 0, + /* T48 Object */ + 1, 64, 64, 0, 0, 0, 0, 0, 0, 0, + 48, 40, 0, 10, 10, 0, 0, 100, 10, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 52, 0, 12, 0, 17, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T56 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 99, 33, 0, 149, 24, 193, 255, 255, 255, + 255, +}; + +/* configuration data for mxt1386e on 3D SKU using V2.1 firmware */ +static const u8 mxt1386e_config_data_3d[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 13, 1, 0, 23, 2, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T7 Object */ + 100, 10, 50, + /* T8 Object */ + 25, 0, 20, 20, 0, 0, 0, 0, 0, 0, + /* T9 Object */ + 131, 0, 0, 26, 42, 0, 32, 80, 2, 5, + 0, 5, 5, 0, 10, 30, 10, 10, 175, 4, + 127, 7, 26, 21, 17, 19, 143, 35, 207, 40, + 20, 5, 54, 49, 0, + /* T18 Object */ + 0, 0, + /* T24 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T25 Object */ + 0, 0, 72, 113, 168, 97, + /* T27 Object */ + 0, 0, 0, 0, 0, 0, 0, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T42 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T43 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, + /* T46 Object */ + 68, 0, 16, 16, 0, 0, 0, 0, 0, + /* T47 Object */ + 0, 0, 0, 0, 0, 0, 3, 64, 66, 0, + /* T48 Object */ + 31, 64, 64, 0, 0, 0, 0, 0, 0, 0, + 32, 50, 0, 10, 10, 0, 0, 100, 10, 90, + 0, 0, 0, 0, 0, 0, 0, 10, 1, 30, + 52, 10, 5, 0, 33, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T56 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +#define MXT_TS_GPIO_IRQ 11 +#define MXT_TS_LDO_EN_GPIO 50 +#define MXT_TS_RESET_GPIO 52 + +static void mxt_init_hw_liquid(void) +{ + int rc; + + rc = gpio_request(MXT_TS_LDO_EN_GPIO, "mxt_ldo_en_gpio"); + if (rc) { + pr_err("%s: unable to request mxt_ldo_en_gpio [%d]\n", + __func__, MXT_TS_LDO_EN_GPIO); + return; + } + + rc = gpio_direction_output(MXT_TS_LDO_EN_GPIO, 1); + if (rc) { + pr_err("%s: unable to set_direction for mxt_ldo_en_gpio [%d]\n", + __func__, MXT_TS_LDO_EN_GPIO); + goto err_ldo_gpio_req; + } + + return; + +err_ldo_gpio_req: + gpio_free(MXT_TS_LDO_EN_GPIO); +} + +static struct mxt_config_info mxt_config_array_2d[] = { + { + .config = mxt1386_config_data, + .config_length = ARRAY_SIZE(mxt1386_config_data), + .family_id = 0xA0, + .variant_id = 0x0, + .version = 0x10, + .build = 0xAA, + .bootldr_id = MXT_BOOTLOADER_ID_1386, + }, + { + .config = mxt1386e_config_data_v1_0, + .config_length = ARRAY_SIZE(mxt1386e_config_data_v1_0), + .family_id = 0xA0, + .variant_id = 0x2, + .version = 0x10, + .build = 0xAA, + .bootldr_id = MXT_BOOTLOADER_ID_1386E, + .fw_name = "atmel_8960_liquid_v2_2_AA.hex", + }, + { + .config = mxt1386e_config_data_v2_1, + .config_length = ARRAY_SIZE(mxt1386e_config_data_v2_1), + .family_id = 0xA0, + .variant_id = 0x7, + .version = 0x21, + .build = 0xAA, + .bootldr_id = MXT_BOOTLOADER_ID_1386E, + .fw_name = "atmel_8960_liquid_v2_2_AA.hex", + }, + { + /* The config data for V2.2.AA is the same as for V2.1.AA */ + .config = mxt1386e_config_data_v2_1, + .config_length = ARRAY_SIZE(mxt1386e_config_data_v2_1), + .family_id = 0xA0, + .variant_id = 0x7, + .version = 0x22, + .build = 0xAA, + .bootldr_id = MXT_BOOTLOADER_ID_1386E, + }, +}; + +static struct mxt_platform_data mxt_platform_data_2d = { + .config_array = mxt_config_array_2d, + .config_array_size = ARRAY_SIZE(mxt_config_array_2d), + .panel_minx = 0, + .panel_maxx = 1365, + .panel_miny = 0, + .panel_maxy = 767, + .disp_minx = 0, + .disp_maxx = 1365, + .disp_miny = 0, + .disp_maxy = 767, + .irqflags = IRQF_TRIGGER_FALLING, + .i2c_pull_up = true, + .reset_gpio = MXT_TS_RESET_GPIO, + .irq_gpio = MXT_TS_GPIO_IRQ, +}; + +static struct mxt_config_info mxt_config_array_3d[] = { + { + .config = mxt1386e_config_data_3d, + .config_length = ARRAY_SIZE(mxt1386e_config_data_3d), + .family_id = 0xA0, + .variant_id = 0x7, + .version = 0x21, + .build = 0xAA, + }, +}; + +static struct mxt_platform_data mxt_platform_data_3d = { + .config_array = mxt_config_array_3d, + .config_array_size = ARRAY_SIZE(mxt_config_array_3d), + .panel_minx = 0, + .panel_maxx = 1919, + .panel_miny = 0, + .panel_maxy = 1199, + .disp_minx = 0, + .disp_maxx = 1919, + .disp_miny = 0, + .disp_maxy = 1199, + .irqflags = IRQF_TRIGGER_FALLING, + .i2c_pull_up = true, + .reset_gpio = MXT_TS_RESET_GPIO, + .irq_gpio = MXT_TS_GPIO_IRQ, +}; + +static struct i2c_board_info mxt_device_info[] __initdata = { + { + I2C_BOARD_INFO("atmel_mxt_ts", 0x5b), + .irq = MSM_GPIO_TO_INT(MXT_TS_GPIO_IRQ), + }, +}; + +#ifdef CONFIG_FB_MSM_HDMI_MHL_8334 +static void mhl_sii_reset_gpio(int on) +{ + gpio_set_value(MHL_GPIO_RESET, on); + return; +} + +/* + * Request for GPIO allocations + * Set appropriate GPIO directions + */ +static int mhl_sii_gpio_setup(int on) +{ + int ret; + + if (on) { + ret = gpio_request(MHL_GPIO_RESET, "W_RST#"); + if (ret < 0) { + pr_err("GPIO RESET request failed: %d\n", ret); + return -EBUSY; + } + ret = gpio_direction_output(MHL_GPIO_RESET, 1); + if (ret < 0) { + pr_err("SET GPIO RESET direction failed: %d\n", ret); + gpio_free(MHL_GPIO_RESET); + return -EBUSY; + } + ret = gpio_request(MHL_GPIO_INT, "W_INT"); + if (ret < 0) { + pr_err("GPIO INT request failed: %d\n", ret); + gpio_free(MHL_GPIO_RESET); + return -EBUSY; + } + ret = gpio_direction_input(MHL_GPIO_INT); + if (ret < 0) { + pr_err("SET GPIO INTR direction failed: %d\n", ret); + gpio_free(MHL_GPIO_RESET); + gpio_free(MHL_GPIO_INT); + return -EBUSY; + } + } else { + gpio_free(MHL_GPIO_RESET); + gpio_free(MHL_GPIO_INT); + } + + return 0; +} + +static struct msm_mhl_platform_data mhl_platform_data = { + .irq = MSM_GPIO_TO_INT(4), + .gpio_setup = mhl_sii_gpio_setup, + .reset_pin = mhl_sii_reset_gpio, +}; +#endif + +static struct i2c_board_info sii_device_info[] __initdata = { + { +#ifdef CONFIG_FB_MSM_HDMI_MHL_8334 + /* + * keeps SI 8334 as the default + * MHL TX + */ + I2C_BOARD_INFO("sii8334", 0x39), + .platform_data = &mhl_platform_data, +#endif +#ifdef CONFIG_FB_MSM_HDMI_MHL_9244 + I2C_BOARD_INFO("Sil-9244", 0x39), + .irq = MSM_GPIO_TO_INT(15), +#endif /* CONFIG_MSM_HDMI_MHL */ + .flags = I2C_CLIENT_WAKE, + }, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi4_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi3_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi10_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_i2c_platform_data msm8960_i2c_qup_gsbi12_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +static struct msm_pm_sleep_status_data msm_pm_slp_sts_data = { + .base_addr = MSM_ACC0_BASE + 0x08, + .cpu_offset = MSM_ACC1_BASE - MSM_ACC0_BASE, + .mask = 1UL << 13, +}; + +static struct ks8851_pdata spi_eth_pdata = { + .irq_gpio = KS8851_IRQ_GPIO, + .rst_gpio = KS8851_RST_GPIO, +}; + +static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "ks8851", + .irq = MSM_GPIO_TO_INT(KS8851_IRQ_GPIO), + .max_speed_hz = 19200000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = &spi_eth_pdata + }, + { + .modalias = "dsi_novatek_3d_panel_spi", + .max_speed_hz = 10800000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + }, +}; + +static struct platform_device msm_device_saw_core0 = { + .name = "saw-regulator", + .id = 0, + .dev = { + .platform_data = &msm_saw_regulator_pdata_s5, + }, +}; + +static struct platform_device msm_device_saw_core1 = { + .name = "saw-regulator", + .id = 1, + .dev = { + .platform_data = &msm_saw_regulator_pdata_s6, + }, +}; + +static struct tsens_platform_data msm_tsens_pdata = { + .slope = {910, 910, 910, 910, 910}, + .tsens_factor = 1000, + .hw_type = MSM_8960, + .tsens_num_sensor = 5, +}; + +static struct platform_device msm_tsens_device = { + .name = "tsens8960-tm", + .id = -1, +}; + +#ifdef CONFIG_MSM_FAKE_BATTERY +static struct platform_device fish_battery_device = { + .name = "fish_battery", +}; +#endif + +static struct platform_device msm8960_device_ext_5v_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_MPP_PM_TO_SYS(7), + .dev = { + .platform_data = &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_5V], + }, +}; + +static struct platform_device msm8960_device_ext_l2_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 91, + .dev = { + .platform_data = &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_L2], + }, +}; + +static struct platform_device msm8960_device_ext_3p3v_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_GPIO_PM_TO_SYS(17), + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_3P3V], + }, +}; + +static struct platform_device msm8960_device_ext_otg_sw_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8921_GPIO_PM_TO_SYS(42), + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_OTG_SW], + }, +}; + +static struct platform_device msm8960_device_rpm_regulator __devinitdata = { + .name = "rpm-regulator", + .id = -1, + .dev = { + .platform_data = &msm_rpm_regulator_pdata, + }, +}; +#ifdef CONFIG_SERIAL_MSM_HS +static int configure_uart_gpios(int on) +{ + int ret = 0, i; + int uart_gpios[] = {93, 94, 95, 96}; + + for (i = 0; i < ARRAY_SIZE(uart_gpios); i++) { + if (on) { + ret = gpio_request(uart_gpios[i], NULL); + if (ret) { + pr_err("%s: unable to request uart gpio[%d]\n", + __func__, uart_gpios[i]); + break; + } + } else { + gpio_free(uart_gpios[i]); + } + } + + if (ret && on && i) + for (; i >= 0; i--) + gpio_free(uart_gpios[i]); + return ret; +} + +static struct msm_serial_hs_platform_data msm_uart_dm9_pdata = { + .gpio_config = configure_uart_gpios, +}; +#else +static struct msm_serial_hs_platform_data msm_uart_dm9_pdata; +#endif + +static struct platform_device *common_devices[] __initdata = { + &msm8960_device_dmov, + &msm_device_smd, + &msm_device_uart_dm6, + &msm_device_uart_dm9, + &msm_device_saw_core0, + &msm_device_saw_core1, + &msm8960_device_ext_5v_vreg, + &msm8960_device_ssbi_pmic, + &msm8960_device_ext_otg_sw_vreg, + &msm8960_device_qup_spi_gsbi1, + &msm8960_device_qup_i2c_gsbi3, + &msm8960_device_qup_i2c_gsbi4, + &msm8960_device_qup_i2c_gsbi10, +#ifndef CONFIG_MSM_DSPS + &msm8960_device_qup_i2c_gsbi12, +#endif + &msm_slim_ctrl, + &msm_device_wcnss_wlan, +#if defined(CONFIG_QSEECOM) + &qseecom_device, +#endif +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &qcedev_device, +#endif +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif + &msm_device_sps, +#ifdef CONFIG_MSM_FAKE_BATTERY + &fish_battery_device, +#endif + &msm8960_fmem_device, +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + &msm8960_android_pmem_device, + &msm8960_android_pmem_adsp_device, + &msm8960_android_pmem_audio_device, +#endif +#endif + &msm_device_vidc, + &msm_device_bam_dmux, + &msm_fm_platform_init, +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) +#ifdef CONFIG_MSM_USE_TSIF1 + &msm_device_tsif[1], +#else + &msm_device_tsif[0], +#endif +#endif + &msm_device_tspp, +#ifdef CONFIG_HW_RANDOM_MSM + &msm_device_rng, +#endif +#ifdef CONFIG_ION_MSM + &msm8960_ion_dev, +#endif + &msm8960_rpm_device, + &msm8960_rpm_log_device, + &msm8960_rpm_stat_device, + &msm_device_tz_log, +#ifdef CONFIG_MSM_QDSS + &msm_qdss_device, + &msm_etb_device, + &msm_tpiu_device, + &msm_funnel_device, + &msm_etm_device, +#endif + &msm_device_dspcrashd_8960, + &msm8960_device_watchdog, + &msm8960_rtb_device, + &msm8960_cpu_idle_device, + &msm8960_msm_gov_device, + &msm8960_device_cache_erp, + &msm8960_cache_dump_device, + &msm8960_iommu_domain_device, + &msm_tsens_device, +}; + +static struct platform_device *sim_devices[] __initdata = { + &msm8960_device_uart_gsbi5, + &msm8960_device_otg, + &msm8960_device_gadget_peripheral, + &msm_device_hsusb_host, + &msm_device_hsic_host, + &android_usb_device, + &msm_device_vidc, + &msm_bus_apps_fabric, + &msm_bus_sys_fabric, + &msm_bus_mm_fabric, + &msm_bus_sys_fpb, + &msm_bus_cpss_fpb, + &msm_pcm, + &msm_multi_ch_pcm, + &msm_pcm_routing, + &msm_cpudai0, + &msm_cpudai1, + &msm8960_cpudai_slimbus_2_rx, + &msm8960_cpudai_slimbus_2_tx, + &msm_cpudai_hdmi_rx, + &msm_cpudai_bt_rx, + &msm_cpudai_bt_tx, + &msm_cpudai_fm_rx, + &msm_cpudai_fm_tx, + &msm_cpudai_auxpcm_rx, + &msm_cpudai_auxpcm_tx, + &msm_cpu_fe, + &msm_stub_codec, + &msm_voice, + &msm_voip, + &msm_lpa_pcm, + &msm_compr_dsp, + &msm_cpudai_incall_music_rx, + &msm_cpudai_incall_record_rx, + &msm_cpudai_incall_record_tx, + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &qcedev_device, +#endif +}; + +static struct platform_device *rumi3_devices[] __initdata = { + &msm8960_device_uart_gsbi5, + &msm_kgsl_3d0, + &msm_kgsl_2d0, + &msm_kgsl_2d1, +#ifdef CONFIG_MSM_GEMINI + &msm8960_gemini_device, +#endif +#ifdef CONFIG_MSM_MERCURY + &msm8960_mercury_device, +#endif +}; + +static struct platform_device *cdp_devices[] __initdata = { + &msm_8960_q6_lpass, + &msm_8960_q6_mss_fw, + &msm_8960_q6_mss_sw, + &msm_8960_riva, + &msm_pil_tzapps, + &msm_pil_dsps, + &msm_pil_vidc, + &msm8960_device_otg, + &msm8960_device_gadget_peripheral, + &msm_device_hsusb_host, + &android_usb_device, + &msm_pcm, + &msm_multi_ch_pcm, + &msm_pcm_routing, + &msm_cpudai0, + &msm_cpudai1, + &msm8960_cpudai_slimbus_2_rx, + &msm8960_cpudai_slimbus_2_tx, + &msm_cpudai_hdmi_rx, + &msm_cpudai_bt_rx, + &msm_cpudai_bt_tx, + &msm_cpudai_fm_rx, + &msm_cpudai_fm_tx, + &msm_cpudai_auxpcm_rx, + &msm_cpudai_auxpcm_tx, + &msm_cpu_fe, + &msm_stub_codec, + &msm_kgsl_3d0, +#ifdef CONFIG_MSM_KGSL_2D + &msm_kgsl_2d0, + &msm_kgsl_2d1, +#endif +#ifdef CONFIG_MSM_GEMINI + &msm8960_gemini_device, +#endif +#ifdef CONFIG_MSM_MERCURY + &msm8960_mercury_device, +#endif + &msm_voice, + &msm_voip, + &msm_lpa_pcm, + &msm_cpudai_afe_01_rx, + &msm_cpudai_afe_01_tx, + &msm_cpudai_afe_02_rx, + &msm_cpudai_afe_02_tx, + &msm_pcm_afe, + &msm_compr_dsp, + &msm_cpudai_incall_music_rx, + &msm_cpudai_incall_record_rx, + &msm_cpudai_incall_record_tx, + &msm_pcm_hostless, + &msm_bus_apps_fabric, + &msm_bus_sys_fabric, + &msm_bus_mm_fabric, + &msm_bus_sys_fpb, + &msm_bus_cpss_fpb, +}; + +static void __init msm8960_i2c_init(void) +{ + msm8960_device_qup_i2c_gsbi4.dev.platform_data = + &msm8960_i2c_qup_gsbi4_pdata; + + msm8960_device_qup_i2c_gsbi3.dev.platform_data = + &msm8960_i2c_qup_gsbi3_pdata; + + msm8960_device_qup_i2c_gsbi10.dev.platform_data = + &msm8960_i2c_qup_gsbi10_pdata; + + msm8960_device_qup_i2c_gsbi12.dev.platform_data = + &msm8960_i2c_qup_gsbi12_pdata; +} + +static void __init msm8960_gfx_init(void) +{ + uint32_t soc_platform_version = socinfo_get_version(); + if (SOCINFO_VERSION_MAJOR(soc_platform_version) == 1) { + struct kgsl_device_platform_data *kgsl_3d0_pdata = + msm_kgsl_3d0.dev.platform_data; + kgsl_3d0_pdata->pwrlevel[0].gpu_freq = 320000000; + kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 266667000; + } +} + +static struct msm_rpmrs_level msm_rpmrs_levels[] = { + { + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1, 784, 180000, 100, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1300, 228, 1200000, 2000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, GDHS, MAX, ACTIVE), + false, + 2000, 138, 1208400, 3200, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 6000, 119, 1850300, 9000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, GDHS, MAX, ACTIVE), + false, + 9200, 68, 2839200, 16400, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE), + false, + 10300, 63, 3128000, 18200, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 18000, 10, 4602600, 27000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW), + false, + 20000, 2, 5752000, 32000, + }, +}; + +static struct msm_rpmrs_platform_data msm_rpmrs_data __initdata = { + .levels = &msm_rpmrs_levels[0], + .num_levels = ARRAY_SIZE(msm_rpmrs_levels), + .vdd_mem_levels = { + [MSM_RPMRS_VDD_MEM_RET_LOW] = 750000, + [MSM_RPMRS_VDD_MEM_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_MEM_ACTIVE] = 1050000, + [MSM_RPMRS_VDD_MEM_MAX] = 1150000, + }, + .vdd_dig_levels = { + [MSM_RPMRS_VDD_DIG_RET_LOW] = 500000, + [MSM_RPMRS_VDD_DIG_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_DIG_ACTIVE] = 950000, + [MSM_RPMRS_VDD_DIG_MAX] = 1150000, + }, + .vdd_mask = 0x7FFFFF, + .rpmrs_target_id = { + [MSM_RPMRS_ID_PXO_CLK] = MSM_RPM_ID_PXO_CLK, + [MSM_RPMRS_ID_L2_CACHE_CTL] = MSM_RPM_ID_LAST, + [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_PM8921_S3_0, + [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_PM8921_S3_1, + [MSM_RPMRS_ID_VDD_MEM_0] = MSM_RPM_ID_PM8921_L24_0, + [MSM_RPMRS_ID_VDD_MEM_1] = MSM_RPM_ID_PM8921_L24_1, + [MSM_RPMRS_ID_RPM_CTL] = MSM_RPM_ID_RPM_CTL, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_TZ, +}; + +#ifdef CONFIG_I2C +#define I2C_SURF 1 +#define I2C_FFA (1 << 1) +#define I2C_RUMI (1 << 2) +#define I2C_SIM (1 << 3) +#define I2C_FLUID (1 << 4) +#define I2C_LIQUID (1 << 5) + +struct i2c_registry { + u8 machs; + int bus; + struct i2c_board_info *info; + int len; +}; + +/* Sensors DSPS platform data */ +#ifdef CONFIG_MSM_DSPS +#define DSPS_PIL_GENERIC_NAME "dsps" +#endif /* CONFIG_MSM_DSPS */ + +static void __init msm8960_init_dsps(void) +{ +#ifdef CONFIG_MSM_DSPS + struct msm_dsps_platform_data *pdata = + msm_dsps_device.dev.platform_data; + pdata->pil_name = DSPS_PIL_GENERIC_NAME; + pdata->gpios = NULL; + pdata->gpios_num = 0; + + platform_device_register(&msm_dsps_device); +#endif /* CONFIG_MSM_DSPS */ +} + +static int hsic_peripheral_status = 1; +static DEFINE_MUTEX(hsic_status_lock); + +void peripheral_connect() +{ + mutex_lock(&hsic_status_lock); + if (hsic_peripheral_status) + goto out; + platform_device_add(&msm_device_hsic_host); + hsic_peripheral_status = 1; +out: + mutex_unlock(&hsic_status_lock); +} +EXPORT_SYMBOL(peripheral_connect); + +void peripheral_disconnect() +{ + mutex_lock(&hsic_status_lock); + if (!hsic_peripheral_status) + goto out; + platform_device_del(&msm_device_hsic_host); + hsic_peripheral_status = 0; +out: + mutex_unlock(&hsic_status_lock); +} +EXPORT_SYMBOL(peripheral_disconnect); + +static void __init msm8960_init_smsc_hub(void) +{ + uint32_t version = socinfo_get_version(); + + if (SOCINFO_VERSION_MAJOR(version) == 1) + return; + + if (machine_is_msm8960_liquid()) + platform_device_register(&smsc_hub_device); +} + +static void __init msm8960_init_hsic(void) +{ +#ifdef CONFIG_USB_EHCI_MSM_HSIC + uint32_t version = socinfo_get_version(); + + if (SOCINFO_VERSION_MAJOR(version) == 1) + return; + + if (machine_is_msm8960_liquid()) + platform_device_register(&msm_device_hsic_host); +#endif +} + +#ifdef CONFIG_ISL9519_CHARGER +static struct isl_platform_data isl_data __initdata = { + .valid_n_gpio = 0, /* Not required when notify-by-pmic */ + .chg_detection_config = NULL, /* Not required when notify-by-pmic */ + .max_system_voltage = 4200, + .min_system_voltage = 3200, + .chgcurrent = 1900, + .term_current = 0, + .input_current = 2048, +}; + +static struct i2c_board_info isl_charger_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("isl9519q", 0x9), + .irq = 0, /* Not required when notify-by-pmic */ + .platform_data = &isl_data, + }, +}; +#endif /* CONFIG_ISL9519_CHARGER */ + +static struct i2c_board_info liquid_io_expander_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1508q", 0x20), + .platform_data = &msm8960_sx150x_data[SX150X_LIQUID] + }, +}; + +static struct i2c_registry msm8960_i2c_devices[] __initdata = { +#ifdef CONFIG_ISL9519_CHARGER + { + I2C_LIQUID, + MSM_8960_GSBI10_QUP_I2C_BUS_ID, + isl_charger_i2c_info, + ARRAY_SIZE(isl_charger_i2c_info), + }, +#endif /* CONFIG_ISL9519_CHARGER */ + { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_8960_GSBI3_QUP_I2C_BUS_ID, + cyttsp_info, + ARRAY_SIZE(cyttsp_info), + }, + { + I2C_LIQUID, + MSM_8960_GSBI3_QUP_I2C_BUS_ID, + mxt_device_info, + ARRAY_SIZE(mxt_device_info), + }, + { + I2C_SURF | I2C_FFA | I2C_LIQUID, + MSM_8960_GSBI10_QUP_I2C_BUS_ID, + sii_device_info, + ARRAY_SIZE(sii_device_info), + }, + { + I2C_LIQUID, + MSM_8960_GSBI10_QUP_I2C_BUS_ID, + msm_isa1200_board_info, + ARRAY_SIZE(msm_isa1200_board_info), + }, + { + I2C_LIQUID, + MSM_8960_GSBI10_QUP_I2C_BUS_ID, + liquid_io_expander_i2c_info, + ARRAY_SIZE(liquid_io_expander_i2c_info), + }, +}; +#endif /* CONFIG_I2C */ + +static void __init register_i2c_devices(void) +{ +#ifdef CONFIG_I2C + u8 mach_mask = 0; + int i; +#ifdef CONFIG_MSM_CAMERA + struct i2c_registry msm8960_camera_i2c_devices = { + I2C_SURF | I2C_FFA | I2C_FLUID | I2C_LIQUID | I2C_RUMI, + MSM_8960_GSBI4_QUP_I2C_BUS_ID, + msm8960_camera_board_info.board_info, + msm8960_camera_board_info.num_i2c_board_info, + }; +#endif + + /* Build the matching 'supported_machs' bitmask */ + if (machine_is_msm8960_cdp()) + mach_mask = I2C_SURF; + else if (machine_is_msm8960_rumi3()) + mach_mask = I2C_RUMI; + else if (machine_is_msm8960_sim()) + mach_mask = I2C_SIM; + else if (machine_is_msm8960_fluid()) + mach_mask = I2C_FLUID; + else if (machine_is_msm8960_liquid()) + mach_mask = I2C_LIQUID; + else if (machine_is_msm8960_mtp()) + mach_mask = I2C_FFA; + else + pr_err("unmatched machine ID in register_i2c_devices\n"); + + if (machine_is_msm8960_liquid()) { + if (SOCINFO_VERSION_MAJOR(socinfo_get_platform_version()) == 3) + mxt_device_info[0].platform_data = + &mxt_platform_data_3d; + else + mxt_device_info[0].platform_data = + &mxt_platform_data_2d; + } + + /* Run the array and install devices as appropriate */ + for (i = 0; i < ARRAY_SIZE(msm8960_i2c_devices); ++i) { + if (msm8960_i2c_devices[i].machs & mach_mask) + i2c_register_board_info(msm8960_i2c_devices[i].bus, + msm8960_i2c_devices[i].info, + msm8960_i2c_devices[i].len); + } +#ifdef CONFIG_MSM_CAMERA + if (msm8960_camera_i2c_devices.machs & mach_mask) + i2c_register_board_info(msm8960_camera_i2c_devices.bus, + msm8960_camera_i2c_devices.info, + msm8960_camera_i2c_devices.len); +#endif +#endif +} + +static void __init msm8960_sim_init(void) +{ + struct msm_watchdog_pdata *wdog_pdata = (struct msm_watchdog_pdata *) + &msm8960_device_watchdog.dev.platform_data; + + wdog_pdata->bark_time = 15000; + msm_tsens_early_init(&msm_tsens_pdata); + BUG_ON(msm_rpm_init(&msm8960_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + regulator_suppress_info_printing(); + platform_device_register(&msm8960_device_rpm_regulator); + msm_clock_init(&msm8960_clock_init_data); + msm8960_init_pmic(); + + msm8960_device_otg.dev.platform_data = &msm_otg_pdata; + msm8960_init_gpiomux(); + msm8960_i2c_init(); + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + msm_spm_l2_init(msm_spm_l2_data); + msm8960_init_buses(); + platform_add_devices(common_devices, ARRAY_SIZE(common_devices)); + msm8960_pm8921_gpio_mpp_init(); + platform_add_devices(sim_devices, ARRAY_SIZE(sim_devices)); + acpuclk_init(&acpuclk_8960_soc_data); + + msm8960_device_qup_spi_gsbi1.dev.platform_data = + &msm8960_qup_spi_gsbi1_pdata; + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); + + msm8960_init_mmc(); + msm8960_init_fb(); + slim_register_board_info(msm_slim_devices, + ARRAY_SIZE(msm_slim_devices)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data); +} + +static void __init msm8960_rumi3_init(void) +{ + msm_tsens_early_init(&msm_tsens_pdata); + BUG_ON(msm_rpm_init(&msm8960_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + regulator_suppress_info_printing(); + platform_device_register(&msm8960_device_rpm_regulator); + msm8960_init_gpiomux(); + msm8960_init_pmic(); + msm8960_device_qup_spi_gsbi1.dev.platform_data = + &msm8960_qup_spi_gsbi1_pdata; + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); + msm8960_i2c_init(); + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + msm_spm_l2_init(msm_spm_l2_data); + platform_add_devices(common_devices, ARRAY_SIZE(common_devices)); + msm8960_pm8921_gpio_mpp_init(); + platform_add_devices(rumi3_devices, ARRAY_SIZE(rumi3_devices)); + msm8960_init_mmc(); + register_i2c_devices(); + + + msm8960_init_fb(); + slim_register_board_info(msm_slim_devices, + ARRAY_SIZE(msm_slim_devices)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data); +} + +static void __init msm8960_cdp_init(void) +{ + if (meminfo_init(SYS_MEMORY, SZ_256M) < 0) + pr_err("meminfo_init() failed!\n"); + + msm_tsens_early_init(&msm_tsens_pdata); + BUG_ON(msm_rpm_init(&msm8960_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + + regulator_suppress_info_printing(); + if (msm_xo_init()) + pr_err("Failed to initialize XO votes\n"); + platform_device_register(&msm8960_device_rpm_regulator); + msm_clock_init(&msm8960_clock_init_data); + if (machine_is_msm8960_liquid()) + msm_otg_pdata.mhl_enable = true; + msm8960_device_otg.dev.platform_data = &msm_otg_pdata; + if (machine_is_msm8960_mtp() || machine_is_msm8960_fluid() || + machine_is_msm8960_cdp()) { + msm_otg_pdata.phy_init_seq = wr_phy_init_seq; + } else if (machine_is_msm8960_liquid()) { + msm_otg_pdata.phy_init_seq = + liquid_v1_phy_init_seq; + } + android_usb_pdata.swfi_latency = + msm_rpmrs_levels[0].latency_us; + msm_device_hsic_host.dev.platform_data = &msm_hsic_pdata; + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2 && + machine_is_msm8960_liquid()) + msm_device_hsic_host.dev.parent = &smsc_hub_device.dev; + msm8960_init_gpiomux(); + msm8960_device_qup_spi_gsbi1.dev.platform_data = + &msm8960_qup_spi_gsbi1_pdata; + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); + + msm8960_init_pmic(); + if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2 && + (machine_is_msm8960_mtp())) || machine_is_msm8960_liquid()) + msm_isa1200_board_info[0].platform_data = &isa1200_1_pdata; + msm8960_i2c_init(); + msm8960_gfx_init(); + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + msm_spm_l2_init(msm_spm_l2_data); + msm8960_init_buses(); + platform_add_devices(msm8960_footswitch, msm8960_num_footswitch); + if (machine_is_msm8960_liquid()) + platform_device_register(&msm8960_device_ext_3p3v_vreg); + if (machine_is_msm8960_cdp()) + platform_device_register(&msm8960_device_ext_l2_vreg); + + if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) + platform_device_register(&msm8960_device_uart_gsbi8); + else + platform_device_register(&msm8960_device_uart_gsbi5); + + /* For 8960 Fusion 2.2 Primary IPC */ + if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) { + msm_uart_dm9_pdata.wakeup_irq = gpio_to_irq(94); /* GSBI9(2) */ + msm_device_uart_dm9.dev.platform_data = &msm_uart_dm9_pdata; + } + + platform_add_devices(common_devices, ARRAY_SIZE(common_devices)); + msm8960_pm8921_gpio_mpp_init(); + platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices)); + msm8960_init_smsc_hub(); + msm8960_init_hsic(); +#ifdef CONFIG_MSM_CAMERA + msm8960_init_cam(); +#endif + msm8960_init_mmc(); + acpuclk_init(&acpuclk_8960_soc_data); + if (machine_is_msm8960_liquid()) + mxt_init_hw_liquid(); + register_i2c_devices(); + msm8960_init_fb(); + slim_register_board_info(msm_slim_devices, + ARRAY_SIZE(msm_slim_devices)); + msm8960_init_dsps(); + change_memory_power = &msm8960_change_memory_power; + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data); + if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) { + mdm_sglte_device.dev.platform_data = &sglte_platform_data; + platform_device_register(&mdm_sglte_device); + } +} + +MACHINE_START(MSM8960_SIM, "QCT MSM8960 SIMULATOR") + .map_io = msm8960_map_io, + .reserve = msm8960_reserve, + .init_irq = msm8960_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8960_sim_init, + .init_early = msm8960_allocate_memory_regions, + .init_very_early = msm8960_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8960_RUMI3, "QCT MSM8960 RUMI3") + .map_io = msm8960_map_io, + .reserve = msm8960_reserve, + .init_irq = msm8960_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8960_rumi3_init, + .init_early = msm8960_allocate_memory_regions, + .init_very_early = msm8960_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8960_CDP, "QCT MSM8960 CDP") + .map_io = msm8960_map_io, + .reserve = msm8960_reserve, + .init_irq = msm8960_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8960_cdp_init, + .init_early = msm8960_allocate_memory_regions, + .init_very_early = msm8960_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8960_MTP, "QCT MSM8960 MTP") + .map_io = msm8960_map_io, + .reserve = msm8960_reserve, + .init_irq = msm8960_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8960_cdp_init, + .init_early = msm8960_allocate_memory_regions, + .init_very_early = msm8960_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8960_FLUID, "QCT MSM8960 FLUID") + .map_io = msm8960_map_io, + .reserve = msm8960_reserve, + .init_irq = msm8960_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8960_cdp_init, + .init_early = msm8960_allocate_memory_regions, + .init_very_early = msm8960_early_memory, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8960_LIQUID, "QCT MSM8960 LIQUID") + .map_io = msm8960_map_io, + .reserve = msm8960_reserve, + .init_irq = msm8960_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm8960_cdp_init, + .init_early = msm8960_allocate_memory_regions, + .init_very_early = msm8960_early_memory, + .restart = msm_restart, +MACHINE_END diff --git a/arch/arm/mach-msm/board-8960.h b/arch/arm/mach-msm/board-8960.h new file mode 100644 index 00000000000..d98667007a4 --- /dev/null +++ b/arch/arm/mach-msm/board-8960.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_MSM8960_H +#define __ARCH_ARM_MACH_MSM_BOARD_MSM8960_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Macros assume PMIC GPIOs and MPPs start at 1 */ +#define PM8921_GPIO_BASE NR_GPIO_IRQS +#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE) +#define PM8921_MPP_BASE (PM8921_GPIO_BASE + PM8921_NR_GPIOS) +#define PM8921_MPP_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_MPP_BASE) +#define PM8921_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) + +extern struct pm8xxx_regulator_platform_data + msm_pm8921_regulator_pdata[] __devinitdata; + +extern int msm_pm8921_regulator_pdata_len __devinitdata; + +#define GPIO_VREG_ID_EXT_5V 0 +#define GPIO_VREG_ID_EXT_L2 1 +#define GPIO_VREG_ID_EXT_3P3V 2 +#define GPIO_VREG_ID_EXT_OTG_SW 3 + +extern struct gpio_regulator_platform_data + msm_gpio_regulator_pdata[] __devinitdata; + +extern struct regulator_init_data msm_saw_regulator_pdata_s5; +extern struct regulator_init_data msm_saw_regulator_pdata_s6; + +extern struct rpm_regulator_platform_data msm_rpm_regulator_pdata __devinitdata; + +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) +enum { + GPIO_EXPANDER_IRQ_BASE = (PM8921_IRQ_BASE + PM8921_NR_IRQS), + GPIO_EXPANDER_GPIO_BASE = (PM8921_MPP_BASE + PM8921_NR_MPPS), + /* CAM Expander */ + GPIO_CAM_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE, + GPIO_CAM_GP_STROBE_READY = GPIO_CAM_EXPANDER_BASE, + GPIO_CAM_GP_AFBUSY, + GPIO_CAM_GP_STROBE_CE, + GPIO_CAM_GP_CAM1MP_XCLR, + GPIO_CAM_GP_CAMIF_RESET_N, + GPIO_CAM_GP_XMT_FLASH_INT, + GPIO_CAM_GP_LED_EN1, + GPIO_CAM_GP_LED_EN2, + GPIO_LIQUID_EXPANDER_BASE = GPIO_CAM_EXPANDER_BASE + 8, +}; +#endif + +enum { + SX150X_CAM, + SX150X_LIQUID, +}; + +#endif + +extern struct sx150x_platform_data msm8960_sx150x_data[]; +extern struct msm_camera_board_info msm8960_camera_board_info; + +void msm8960_init_cam(void); +void msm8960_init_fb(void); +void msm8960_init_pmic(void); +void msm8960_init_mmc(void); +int msm8960_init_gpiomux(void); +unsigned char msm8960_hdmi_as_primary_selected(void); +void msm8960_allocate_fb_region(void); +void msm8960_set_display_params(char *prim_panel, char *ext_panel); +void msm8960_pm8921_gpio_mpp_init(void); +void msm8960_mdp_writeback(struct memtype_reserve *reserve_table); +#define MSM_8960_GSBI4_QUP_I2C_BUS_ID 4 +#define MSM_8960_GSBI3_QUP_I2C_BUS_ID 3 +#define MSM_8960_GSBI10_QUP_I2C_BUS_ID 10 + +extern struct msm_rtb_platform_data msm8960_rtb_pdata; +extern struct msm_cache_dump_platform_data msm8960_cache_dump_pdata; diff --git a/arch/arm/mach-msm/board-9615-display.c b/arch/arm/mach-msm/board-9615-display.c new file mode 100644 index 00000000000..74bc984cf17 --- /dev/null +++ b/arch/arm/mach-msm/board-9615-display.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-9615.h" + +/* prim = 240 x 320 x 4(bpp) x 2(pages) */ +#define MSM_FB_PRIM_BUF_SIZE roundup(240 * 320 * 4 * 2, 0x10000) +#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096) + +#define GPIO_PIN_EBI2_LCD_A_D 21 +#define GPIO_PIN_EBI2_LCD_CS 22 +#define GPIO_PIN_EBI2_LCD_RS 24 + + +#ifdef CONFIG_FB_MSM + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_MEM, + } +}; + +static int msm_fb_detect_panel(const char *name) +{ + return 0; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev.platform_data = &msm_fb_pdata, +}; + +void __init mdm9615_allocate_fb_region(void) +{ + void *addr; + unsigned long size; + + size = MSM_FB_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); +} + + +static bool ebi2_power_init; +static int ebi2_panel_power(int on) +{ + static struct regulator *panel_power; + int rc; + + pr_debug("%s: on=%d\n", __func__, on); + + if (!ebi2_power_init) { + panel_power = regulator_get(&msm_ebi2_lcdc_device.dev, + "VDDI2"); + if (IS_ERR_OR_NULL(panel_power)) { + pr_err("could not get L14, rc = %ld\n", + PTR_ERR(panel_power)); + return -ENODEV; + } + + rc = regulator_set_voltage(panel_power, 2800000, 3800000); + if (rc) { + pr_err("set_voltage L14 failed, rc=%d\n", rc); + return -EINVAL; + } + + ebi2_power_init = true; + } + + if (on) { + rc = regulator_enable(panel_power); + if (rc) { + pr_err("enable L14 failed, rc=%d\n", rc); + return -ENODEV; + } + rc = gpio_request(GPIO_PIN_EBI2_LCD_A_D, "disp_a_d"); + if (rc) { + pr_err("request gpio EBI2_LCD_A_D failed, rc=%d\n", rc); + goto error1; + } + rc = gpio_request(GPIO_PIN_EBI2_LCD_CS, "disp_cs"); + if (rc) { + pr_err("request gpio EBI2_LCD_CS failed, rc=%d\n", rc); + goto error2; + } + rc = gpio_request(GPIO_PIN_EBI2_LCD_RS, "disp_rs"); + if (rc) { + pr_err("request gpio EBI2_LCD_RS failed, rc=%d\n", rc); + goto error3; + } + } else { + gpio_free(GPIO_PIN_EBI2_LCD_RS); + gpio_free(GPIO_PIN_EBI2_LCD_CS); + gpio_free(GPIO_PIN_EBI2_LCD_A_D); + + rc = regulator_disable(panel_power); + if (rc) { + pr_err("disable L14 failed, rc=%d\n", rc); + return -ENODEV; + } + } + + return 0; +error3: + gpio_free(GPIO_PIN_EBI2_LCD_CS); +error2: + gpio_free(GPIO_PIN_EBI2_LCD_A_D); +error1: + regulator_disable(panel_power); + return rc; + +} + +static struct lcdc_platform_data ebi2_lcdc_pdata = { + .lcdc_power_save = ebi2_panel_power, +}; + +static struct lvds_panel_platform_data ebi2_epson_s1d_pdata; + +static struct platform_device ebi2_epson_s1d_panel_device = { + .name = "ebi2_epson_s1d_qvga", + .id = 0, + .dev = { + .platform_data = &ebi2_epson_s1d_pdata, + } +}; + +void __init mdm9615_init_fb(void) +{ + platform_device_register(&msm_fb_device); + platform_device_register(&ebi2_epson_s1d_panel_device); + + msm_fb_register_device("ebi2_lcd", &ebi2_lcdc_pdata); +} +#endif diff --git a/arch/arm/mach-msm/board-9615-gpiomux.c b/arch/arm/mach-msm/board-9615-gpiomux.c new file mode 100644 index 00000000000..47a9835ffcb --- /dev/null +++ b/arch/arm/mach-msm/board-9615-gpiomux.c @@ -0,0 +1,370 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "board-9615.h" + +static struct gpiomux_setting ps_hold = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting slimbus = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_KEEPER, +}; + +static struct gpiomux_setting gsbi4 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi5 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi3 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi3_cs1_config = { + .func = GPIOMUX_FUNC_4, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +#ifdef CONFIG_LTC4088_CHARGER +static struct gpiomux_setting ltc4088_chg_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; +#endif + +static struct gpiomux_setting sdcc2_clk_actv_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdcc2_cmd_data_0_3_actv_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc2_suspend_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cdc_mclk = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +#ifdef CONFIG_FB_MSM_EBI2 +static struct gpiomux_setting ebi2_lcdc_a_d = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ebi2_lcdc_cs = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ebi2_lcdc_rs = { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_DOWN, +}; +#endif + +static struct gpiomux_setting wlan_active_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_OUT_LOW, +}; + +static struct gpiomux_setting wlan_suspend_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_IN, +}; + +static struct msm_gpiomux_config msm9615_audio_codec_configs[] __initdata = { + { + .gpio = 24, + .settings = { + [GPIOMUX_SUSPENDED] = &cdc_mclk, + }, + }, +}; + +static struct msm_gpiomux_config msm9615_sdcc2_configs[] __initdata = { + { + /* SDC2_DATA_0 */ + .gpio = 25, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* SDC2_DATA_1 */ + .gpio = 26, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_cmd_data_0_3_actv_cfg, + }, + }, + { + /* SDC2_DATA_2 */ + .gpio = 27, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* SDC2_DATA_3 */ + .gpio = 28, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* SDC2_CMD */ + .gpio = 29, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, + { + /* SDC2_CLK */ + .gpio = 30, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_clk_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg, + }, + }, +}; + +struct msm_gpiomux_config msm9615_ps_hold_config[] __initdata = { + { + .gpio = 83, + .settings = { + [GPIOMUX_SUSPENDED] = &ps_hold, + }, + }, +}; + +#ifdef CONFIG_LTC4088_CHARGER +static struct msm_gpiomux_config + msm9615_ltc4088_charger_config[] __initdata = { + { + .gpio = 4, + .settings = { + [GPIOMUX_SUSPENDED] = <c4088_chg_cfg, + }, + }, + { + .gpio = 6, + .settings = { + [GPIOMUX_SUSPENDED] = <c4088_chg_cfg, + }, + }, + { + .gpio = 7, + .settings = { + [GPIOMUX_SUSPENDED] = <c4088_chg_cfg, + }, + }, +}; +#endif + +struct msm_gpiomux_config msm9615_gsbi_configs[] __initdata = { + { + .gpio = 8, /* GSBI3 QUP SPI_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3, + }, + }, + { + .gpio = 9, /* GSBI3 QUP SPI_CS_N */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3, + }, + }, + { + .gpio = 10, /* GSBI3 QUP SPI_DATA_MISO */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3, + }, + }, + { + .gpio = 11, /* GSBI3 QUP SPI_DATA_MOSI */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3, + }, + }, + { + .gpio = 12, /* GSBI4 UART */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi4, + }, + }, + { + .gpio = 13, /* GSBI4 UART */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi4, + }, + }, + { + .gpio = 14, /* GSBI4 UART */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi4, + }, + }, + { + .gpio = 15, /* GSBI4 UART */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi4, + }, + }, + { + .gpio = 16, /* GSBI5 I2C QUP SCL */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi5, + [GPIOMUX_ACTIVE] = &gsbi5, + }, + }, + { + .gpio = 17, /* GSBI5 I2C QUP SDA */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi5, + }, + }, + { + /* GPIO 19 can be used for I2C/UART on GSBI5 */ + .gpio = 19, /* GSBI3 QUP SPI_CS_1 */ + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi3_cs1_config, + }, + }, +}; + +static struct msm_gpiomux_config msm9615_slimbus_configs[] __initdata = { + { + .gpio = 20, /* Slimbus data */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, + { + .gpio = 23, /* Slimbus clk */ + .settings = { + [GPIOMUX_SUSPENDED] = &slimbus, + }, + }, +}; + +#ifdef CONFIG_FB_MSM_EBI2 +static struct msm_gpiomux_config msm9615_ebi2_lcdc_configs[] __initdata = { + { + .gpio = 21, /* a_d */ + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_lcdc_a_d, + }, + }, + { + .gpio = 22, /* cs */ + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_lcdc_cs, + }, + }, + { + .gpio = 24, /* rs */ + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_lcdc_rs, + }, + }, +}; +#endif + +static struct msm_gpiomux_config msm9615_wlan_configs[] __initdata = { + { + .gpio = 21,/* WLAN_RESET_N */ + .settings = { + [GPIOMUX_ACTIVE] = &wlan_active_config, + [GPIOMUX_SUSPENDED] = &wlan_suspend_config, + }, + }, +}; + + +int __init msm9615_init_gpiomux(void) +{ + int rc; + + rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc); + return rc; + } + msm_gpiomux_install(msm9615_gsbi_configs, + ARRAY_SIZE(msm9615_gsbi_configs)); + + msm_gpiomux_install(msm9615_slimbus_configs, + ARRAY_SIZE(msm9615_slimbus_configs)); + + msm_gpiomux_install(msm9615_ps_hold_config, + ARRAY_SIZE(msm9615_ps_hold_config)); + msm_gpiomux_install(msm9615_sdcc2_configs, + ARRAY_SIZE(msm9615_sdcc2_configs)); +#ifdef CONFIG_LTC4088_CHARGER + msm_gpiomux_install(msm9615_ltc4088_charger_config, + ARRAY_SIZE(msm9615_ltc4088_charger_config)); +#endif + msm_gpiomux_install(msm9615_audio_codec_configs, + ARRAY_SIZE(msm9615_audio_codec_configs)); + + msm_gpiomux_install(msm9615_wlan_configs, + ARRAY_SIZE(msm9615_wlan_configs)); + +#ifdef CONFIG_FB_MSM_EBI2 + msm_gpiomux_install(msm9615_ebi2_lcdc_configs, + ARRAY_SIZE(msm9615_ebi2_lcdc_configs)); +#endif + + return 0; +} diff --git a/arch/arm/mach-msm/board-9615-regulator.c b/arch/arm/mach-msm/board-9615-regulator.c new file mode 100644 index 00000000000..1122ed902ce --- /dev/null +++ b/arch/arm/mach-msm/board-9615-regulator.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "board-9615.h" + +#define VREG_CONSUMERS(_id) \ + static struct regulator_consumer_supply vreg_consumers_##_id[] + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +VREG_CONSUMERS(L2) = { + REGULATOR_SUPPLY("8018_l2", NULL), + REGULATOR_SUPPLY("HSUSB_1p8", "msm_otg"), +}; +VREG_CONSUMERS(L3) = { + REGULATOR_SUPPLY("8018_l3", NULL), +}; +VREG_CONSUMERS(L4) = { + REGULATOR_SUPPLY("8018_l4", NULL), + REGULATOR_SUPPLY("HSUSB_3p3", "msm_otg"), +}; +VREG_CONSUMERS(L5) = { + REGULATOR_SUPPLY("8018_l5", NULL), +}; +VREG_CONSUMERS(L6) = { + REGULATOR_SUPPLY("8018_l6", NULL), +}; +VREG_CONSUMERS(L7) = { + REGULATOR_SUPPLY("8018_l7", NULL), +}; +VREG_CONSUMERS(L8) = { + REGULATOR_SUPPLY("8018_l8", NULL), +}; +VREG_CONSUMERS(L9) = { + REGULATOR_SUPPLY("8018_l9", NULL), +}; +VREG_CONSUMERS(L10) = { + REGULATOR_SUPPLY("8018_l10", NULL), +}; +VREG_CONSUMERS(L11) = { + REGULATOR_SUPPLY("8018_l11", NULL), +}; +VREG_CONSUMERS(L12) = { + REGULATOR_SUPPLY("8018_l12", NULL), +}; +VREG_CONSUMERS(L13) = { + REGULATOR_SUPPLY("8018_l13", NULL), + REGULATOR_SUPPLY("sdc_vdd_io", "msm_sdcc.1"), +}; +VREG_CONSUMERS(L14) = { + REGULATOR_SUPPLY("8018_l14", NULL), + REGULATOR_SUPPLY("VDDI2", "ebi2_lcd.0"), +}; +VREG_CONSUMERS(S1) = { + REGULATOR_SUPPLY("8018_s1", NULL), + REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_otg"), + REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_peripheral"), + REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_host"), +}; +VREG_CONSUMERS(S2) = { + REGULATOR_SUPPLY("8018_s2", NULL), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla2x-slim"), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla-slim"), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla2x-slim"), + REGULATOR_SUPPLY("VDDD_CDC_D", "0-000d"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "0-000d"), + REGULATOR_SUPPLY("VDDD_CDC_D", "tabla top level"), + REGULATOR_SUPPLY("CDC_VDDA_A_1P2V", "tabla top level"), +}; +VREG_CONSUMERS(S3) = { + REGULATOR_SUPPLY("8018_s3", NULL), + REGULATOR_SUPPLY("wlan_vreg", "wlan_ar6000_pm_dev"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla2x-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla-slim"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla2x-slim"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla-slim"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla2x-slim"), + REGULATOR_SUPPLY("VDDIO_CDC", "tabla top level"), + REGULATOR_SUPPLY("CDC_VDD_CP", "tabla top level"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "tabla top level"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "tabla top level"), + REGULATOR_SUPPLY("VDDIO_CDC", "0-000d"), + REGULATOR_SUPPLY("CDC_VDD_CP", "0-000d"), + REGULATOR_SUPPLY("CDC_VDDA_TX", "0-000d"), + REGULATOR_SUPPLY("CDC_VDDA_RX", "0-000d"), +}; +VREG_CONSUMERS(S4) = { + REGULATOR_SUPPLY("8018_s4", NULL), +}; +VREG_CONSUMERS(S5) = { + REGULATOR_SUPPLY("8018_s5", NULL), +}; +VREG_CONSUMERS(LVS1) = { + REGULATOR_SUPPLY("8018_lvs1", NULL), +}; +VREG_CONSUMERS(EXT_2P95V) = { + REGULATOR_SUPPLY("ext_2p95v", NULL), + REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.1"), +}; + +#define PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, _modes, _ops, \ + _apply_uV, _pull_down, _always_on, _supply_regulator, \ + _system_uA, _enable_time, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _max_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pull_down_enable = _pull_down, \ + .system_uA = _system_uA, \ + .enable_time = _enable_time, \ + } + +#define PM8XXX_LDO(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_NLDO1200(_id, _name, _always_on, _pull_down, _min_uV, \ + _max_uV, _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_SMPS(_id, _name, _always_on, _pull_down, _min_uV, _max_uV, \ + _enable_time, _supply_regulator, _system_uA, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _pull_down, _always_on, \ + _supply_regulator, _system_uA, _enable_time, _reg_id) + +#define PM8XXX_VS(_id, _name, _always_on, _pull_down, _enable_time, \ + _supply_regulator, _reg_id) \ + PM8XXX_VREG_INIT(_id, _name, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, \ + _pull_down, _always_on, _supply_regulator, 0, _enable_time, \ + _reg_id) + +/* Pin control initialization */ +#define PM8XXX_PC(_id, _name, _always_on, _pin_fn, _pin_ctrl, \ + _supply_regulator, _reg_id) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = _reg_id, \ + .pin_fn = PM8XXX_VREG_PIN_FN_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +#define RPM_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, _default_uV, \ + _peak_uA, _avg_uA, _pull_down, _pin_ctrl, _freq, _pin_fn, \ + _force_mode, _sleep_set_force_mode, _power_mode, _state, \ + _sleep_selectable, _always_on, _supply_regulator, _system_uA) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _min_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8018_##_id, \ + .default_uV = _default_uV, \ + .peak_uA = _peak_uA, \ + .avg_uA = _avg_uA, \ + .pull_down_enable = _pull_down, \ + .pin_ctrl = _pin_ctrl, \ + .freq = RPM_VREG_FREQ_##_freq, \ + .pin_fn = _pin_fn, \ + .force_mode = _force_mode, \ + .sleep_set_force_mode = _sleep_set_force_mode, \ + .power_mode = _power_mode, \ + .state = _state, \ + .sleep_selectable = _sleep_selectable, \ + .system_uA = _system_uA, \ + } + +#define RPM_LDO(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _init_peak_uA) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _init_peak_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_9615_NONE, \ + RPM_VREG_FORCE_MODE_9615_NONE, \ + RPM_VREG_FORCE_MODE_9615_NONE, RPM_VREG_POWER_MODE_9615_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, _system_uA) + +#define RPM_SMPS(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _supply_regulator, _system_uA, _freq) \ + RPM_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE \ + | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE \ + | REGULATOR_CHANGE_DRMS, 0, _max_uV, _system_uA, 0, _pd, \ + RPM_VREG_PIN_CTRL_NONE, _freq, RPM_VREG_PIN_FN_9615_NONE, \ + RPM_VREG_FORCE_MODE_9615_NONE, \ + RPM_VREG_FORCE_MODE_9615_NONE, RPM_VREG_POWER_MODE_9615_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, _system_uA) + +#define RPM_VS(_id, _always_on, _pd, _sleep_selectable, _supply_regulator) \ + RPM_INIT(_id, 0, 0, 0, REGULATOR_CHANGE_STATUS, 0, 0, 1000, 1000, _pd, \ + RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_9615_NONE, \ + RPM_VREG_FORCE_MODE_9615_NONE, \ + RPM_VREG_FORCE_MODE_9615_NONE, RPM_VREG_POWER_MODE_9615_PWM, \ + RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \ + _supply_regulator, 0) + +/* Pin control initialization */ +#define RPM_PC_INIT(_id, _always_on, _pin_fn, _pin_ctrl, _supply_regulator) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + .supply_regulator = _supply_regulator, \ + }, \ + .id = RPM_VREG_ID_PM8018_##_id##_PC, \ + .pin_fn = RPM_VREG_PIN_FN_9615_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +#define GPIO_VREG_INIT(_id, _reg_name, _gpio_label, _gpio) \ + [GPIO_VREG_ID_##_id] = { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + }, \ + .regulator_name = _reg_name, \ + .gpio_label = _gpio_label, \ + .gpio = _gpio, \ + } + +/* GPIO regulator constraints */ +struct gpio_regulator_platform_data msm_gpio_regulator_pdata[] = { + GPIO_VREG_INIT(EXT_2P95V, "ext_2p95v", "ext_2p95_en", 18), +}; + +/* PM8018 regulator constraints */ +struct pm8xxx_regulator_platform_data +msm_pm8018_regulator_pdata[] __devinitdata = { +}; + +static struct rpm_regulator_init_data +msm_rpm_regulator_init_data[] __devinitdata = { + /* ID a_on pd ss min_uV max_uV supply sys_uA freq */ + RPM_SMPS(S1, 0, 1, 1, 500000, 1150000, NULL, 100000, 1p60), + RPM_SMPS(S2, 0, 1, 0, 1225000, 1300000, NULL, 0, 1p60), + RPM_SMPS(S3, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60), + RPM_SMPS(S4, 0, 1, 0, 2100000, 2200000, NULL, 0, 1p60), + RPM_SMPS(S5, 1, 1, 0, 1350000, 1350000, NULL, 100000, 1p60), + + /* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */ + RPM_LDO(L2, 1, 1, 0, 1800000, 1800000, NULL, 0, 10000), + RPM_LDO(L3, 1, 1, 0, 1800000, 1800000, NULL, 0, 0), + RPM_LDO(L4, 0, 1, 0, 3075000, 3075000, NULL, 0, 0), + RPM_LDO(L5, 0, 1, 0, 2850000, 2850000, NULL, 0, 0), + RPM_LDO(L6, 0, 1, 0, 1800000, 2850000, NULL, 0, 0), + RPM_LDO(L7, 0, 1, 0, 1850000, 1900000, "8018_s4", 0, 0), + RPM_LDO(L8, 0, 1, 0, 1200000, 1200000, "8018_s3", 0, 0), + RPM_LDO(L9, 0, 1, 1, 750000, 1150000, "8018_s5", 10000, 10000), + RPM_LDO(L10, 0, 1, 0, 1050000, 1050000, "8018_s5", 0, 0), + RPM_LDO(L11, 0, 1, 0, 1050000, 1050000, "8018_s5", 0, 0), + RPM_LDO(L12, 0, 1, 0, 1050000, 1050000, "8018_s5", 0, 0), + RPM_LDO(L13, 0, 1, 0, 1850000, 2950000, NULL, 0, 0), + RPM_LDO(L14, 0, 1, 0, 2850000, 2850000, NULL, 0, 0), + + /* ID a_on pd ss supply */ + RPM_VS(LVS1, 0, 1, 0, "8018_s3"), +}; + +int msm_pm8018_regulator_pdata_len __devinitdata = + ARRAY_SIZE(msm_pm8018_regulator_pdata); + +struct rpm_regulator_platform_data +msm_rpm_regulator_9615_pdata __devinitdata = { + .init_data = msm_rpm_regulator_init_data, + .num_regulators = ARRAY_SIZE(msm_rpm_regulator_init_data), + .version = RPM_VREG_VERSION_9615, + .vreg_id_vdd_mem = RPM_VREG_ID_PM8018_L9, + .vreg_id_vdd_dig = RPM_VREG_ID_PM8018_S1, +}; diff --git a/arch/arm/mach-msm/board-9615-storage.c b/arch/arm/mach-msm/board-9615-storage.c new file mode 100644 index 00000000000..51e24322e1f --- /dev/null +++ b/arch/arm/mach-msm/board-9615-storage.c @@ -0,0 +1,232 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" + +#include "board-9615.h" +#include "board-storage-common-a.h" + +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT) \ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)) + +#define GPIO_SDC1_HW_DET 80 +#define GPIO_SDC2_DAT1_WAKEUP 26 + +/* MDM9x15 has 2 SDCC controllers */ +enum sdcc_controllers { + SDCC1, + SDCC2, + MAX_SDCC_CONTROLLER +}; + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +/* All SDCC controllers requires VDD/VCC voltage */ +static struct msm_mmc_reg_data mmc_vdd_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : External card slot connected */ + [SDCC1] = { + .name = "sdc_vdd", + /* + * This is a gpio-regulator and does not support + * regulator_set_voltage and regulator_set_optimum_mode + */ + .high_vol_level = 2950000, + .low_vol_level = 2950000, + .hpm_uA = 600000, /* 600mA */ + } +}; + +/* All SDCC controllers may require voting for VDD PAD voltage */ +static struct msm_mmc_reg_data mmc_vdd_io_reg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : External card slot connected */ + [SDCC1] = { + .name = "sdc_vdd_io", + .high_vol_level = 2950000, + .low_vol_level = 1850000, + .always_on = true, + .lpm_sup = true, + /* Max. Active current required is 16 mA */ + .hpm_uA = 16000, + /* + * Sleep current required is ~300 uA. But min. vote can be + * in terms of mA (min. 1 mA). So let's vote for 2 mA + * during sleep. + */ + .lpm_uA = 2000, + } +}; + +static struct msm_mmc_slot_reg_data mmc_slot_vreg_data[MAX_SDCC_CONTROLLER] = { + /* SDCC1 : External card slot connected */ + [SDCC1] = { + .vdd_data = &mmc_vdd_reg_data[SDCC1], + .vdd_io_data = &mmc_vdd_io_reg_data[SDCC1], + } +}; + +/* SDC1 pad data */ +static struct msm_mmc_pad_drv sdc1_pad_drv_on_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_16MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_10MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_10MA} +}; + +static struct msm_mmc_pad_drv sdc1_pad_drv_off_cfg[] = { + {TLMM_HDRV_SDC1_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC1_DATA, GPIO_CFG_2MA} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_on_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_mmc_pad_pull sdc1_pad_pull_off_cfg[] = { + {TLMM_PULL_SDC1_CLK, GPIO_CFG_NO_PULL}, + {TLMM_PULL_SDC1_CMD, GPIO_CFG_PULL_DOWN}, + {TLMM_PULL_SDC1_DATA, GPIO_CFG_PULL_DOWN} +}; + +static struct msm_mmc_pad_pull_data mmc_pad_pull_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_pull_on_cfg, + .off = sdc1_pad_pull_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_pull_on_cfg) + }, +}; + +static struct msm_mmc_pad_drv_data mmc_pad_drv_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .on = sdc1_pad_drv_on_cfg, + .off = sdc1_pad_drv_off_cfg, + .size = ARRAY_SIZE(sdc1_pad_drv_on_cfg) + }, +}; + +static struct msm_mmc_pad_data mmc_pad_data[MAX_SDCC_CONTROLLER] = { + [SDCC1] = { + .pull = &mmc_pad_pull_data[SDCC1], + .drv = &mmc_pad_drv_data[SDCC1] + }, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + +static struct msm_mmc_gpio sdc2_gpio_cfg[] = { + {25, "sdc2_dat_0"}, + {26, "sdc2_dat_1"}, + {27, "sdc2_dat_2"}, + {28, "sdc2_dat_3"}, + {29, "sdc2_cmd"}, + {30, "sdc2_clk"}, +}; + +static struct msm_mmc_gpio_data mmc_gpio_data[MAX_SDCC_CONTROLLER] = { + [SDCC2] = { + .gpio = sdc2_gpio_cfg, + .size = ARRAY_SIZE(sdc2_gpio_cfg), + }, +}; +#endif + +static struct msm_mmc_pin_data mmc_slot_pin_data[MAX_SDCC_CONTROLLER] = { +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + [SDCC1] = { + .is_gpio = 0, + .pad_data = &mmc_pad_data[SDCC1], + }, +#endif +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + [SDCC2] = { + .is_gpio = 1, + .gpio_data = &mmc_gpio_data[SDCC2], + }, +#endif +}; + +#define MSM_MPM_PIN_SDC1_DAT1 17 + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static unsigned int sdc1_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +static struct mmc_platform_data sdc1_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc1_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc1_sup_clk_rates), + .pclk_src_dfab = true, + .vreg_data = &mmc_slot_vreg_data[SDCC1], + .pin_data = &mmc_slot_pin_data[SDCC1], +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + .status_gpio = GPIO_SDC1_HW_DET, + .status_irq = MSM_GPIO_TO_INT(GPIO_SDC1_HW_DET), + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +#endif + .xpc_cap = 1, + .uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_MAX_CURRENT_400), + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC1_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +static struct mmc_platform_data *msm9615_sdc1_pdata = &sdc1_data; +#else +static struct mmc_platform_data *msm9615_sdc1_pdata; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static unsigned int sdc2_sup_clk_rates[] = { + 400000, 24000000, 48000000 +}; + +static struct mmc_platform_data sdc2_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sup_clk_table = sdc2_sup_clk_rates, + .sup_clk_cnt = ARRAY_SIZE(sdc2_sup_clk_rates), + .pclk_src_dfab = 1, + .pin_data = &mmc_slot_pin_data[SDCC2], + .sdiowakeup_irq = MSM_GPIO_TO_INT(GPIO_SDC2_DAT1_WAKEUP), + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +static struct mmc_platform_data *msm9615_sdc2_pdata = &sdc2_data; +#else +static struct mmc_platform_data *msm9615_sdc2_pdata; +#endif + +void __init msm9615_init_mmc(void) +{ + if (msm9615_sdc1_pdata) + /* SDC1: External card slot for SD/MMC cards */ + msm_add_sdcc(1, msm9615_sdc1_pdata); + + if (msm9615_sdc2_pdata) + /* SDC2: External card slot used for WLAN */ + msm_add_sdcc(2, msm9615_sdc2_pdata); +} +#else +void __init msm9615_init_mmc(void) +{ +} +#endif diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c new file mode 100644 index 00000000000..dc376b594b6 --- /dev/null +++ b/arch/arm/mach-msm/board-9615.c @@ -0,0 +1,1038 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#ifdef CONFIG_WCD9310_CODEC +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timer.h" +#include "devices.h" +#include "board-9615.h" +#include "pm.h" +#include "acpuclock.h" +#include "pm-boot.h" +#include + +#ifdef CONFIG_ION_MSM +#define MSM_ION_AUDIO_SIZE 0xAF000 +#define MSM_ION_HEAP_NUM 3 +#define MSM_KERNEL_EBI_SIZE 0x51000 + +static struct memtype_reserve msm9615_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static int msm9615_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static struct ion_platform_data ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + { + .id = ION_IOMMU_HEAP_ID, + .type = ION_HEAP_TYPE_IOMMU, + .name = ION_IOMMU_HEAP_NAME, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .size = MSM_ION_AUDIO_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_ion_pdata, + }, + } +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; + +static void __init reserve_ion_memory(void) +{ + msm9615_reserve_table[MEMTYPE_EBI1].size += MSM_ION_AUDIO_SIZE; +} + +static void __init msm9615_calculate_reserve_sizes(void) +{ + reserve_ion_memory(); + msm9615_reserve_table[MEMTYPE_EBI1].size += MSM_KERNEL_EBI_SIZE; +} + +static struct reserve_info msm9615_reserve_info __initdata = { + .memtype_reserve_table = msm9615_reserve_table, + .calculate_reserve_sizes = msm9615_calculate_reserve_sizes, + .paddr_to_memtype = msm9615_paddr_to_memtype, +}; +#endif + +struct pm8xxx_gpio_init { + unsigned gpio; + struct pm_gpio config; +}; + +struct pm8xxx_mpp_init { + unsigned mpp; + struct pm8xxx_mpp_config_data config; +}; + +#define PM8018_GPIO_INIT(_gpio, _dir, _buf, _val, _pull, _vin, _out_strength, \ + _func, _inv, _disable) \ +{ \ + .gpio = PM8018_GPIO_PM_TO_SYS(_gpio), \ + .config = { \ + .direction = _dir, \ + .output_buffer = _buf, \ + .output_value = _val, \ + .pull = _pull, \ + .vin_sel = _vin, \ + .out_strength = _out_strength, \ + .function = _func, \ + .inv_int_pol = _inv, \ + .disable_pin = _disable, \ + } \ +} + +#define PM8018_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8018_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +#define PM8018_GPIO_DISABLE(_gpio) \ + PM8018_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, 0, 0, 0, PM8018_GPIO_VIN_S3, \ + 0, 0, 0, 1) + +#define PM8018_GPIO_OUTPUT(_gpio, _val, _strength) \ + PM8018_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM8018_GPIO_VIN_S3, \ + PM_GPIO_STRENGTH_##_strength, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8018_GPIO_INPUT(_gpio, _pull) \ + PM8018_GPIO_INIT(_gpio, PM_GPIO_DIR_IN, PM_GPIO_OUT_BUF_CMOS, 0, \ + _pull, PM8018_GPIO_VIN_S3, \ + PM_GPIO_STRENGTH_NO, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +#define PM8018_GPIO_OUTPUT_FUNC(_gpio, _val, _func) \ + PM8018_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, PM8018_GPIO_VIN_S3, \ + PM_GPIO_STRENGTH_HIGH, \ + _func, 0, 0) + +#define PM8018_GPIO_OUTPUT_VIN(_gpio, _val, _vin) \ + PM8018_GPIO_INIT(_gpio, PM_GPIO_DIR_OUT, PM_GPIO_OUT_BUF_CMOS, _val, \ + PM_GPIO_PULL_NO, _vin, \ + PM_GPIO_STRENGTH_HIGH, \ + PM_GPIO_FUNC_NORMAL, 0, 0) + +/* Initial PM8018 GPIO configurations */ +static struct pm8xxx_gpio_init pm8018_gpios[] __initdata = { + PM8018_GPIO_OUTPUT(2, 0, HIGH) /* EXT_LDO_EN_WLAN */ +}; + +/* Initial PM8018 MPP configurations */ +static struct pm8xxx_mpp_init pm8018_mpps[] __initdata = { +}; + +void __init msm9615_pm8xxx_gpio_mpp_init(void) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(pm8018_gpios); i++) { + rc = pm8xxx_gpio_config(pm8018_gpios[i].gpio, + &pm8018_gpios[i].config); + if (rc) { + pr_err("%s: pm8018_gpio_config: rc=%d\n", __func__, rc); + break; + } + } + + for (i = 0; i < ARRAY_SIZE(pm8018_mpps); i++) { + rc = pm8xxx_mpp_config(pm8018_mpps[i].mpp, + &pm8018_mpps[i].config); + if (rc) { + pr_err("%s: pm8018_mpp_config: rc=%d\n", __func__, rc); + break; + } + } +} + +static struct pm8xxx_adc_amux pm8018_adc_channels_data[] = { + {"vcoin", CHANNEL_VCOIN, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vbat", CHANNEL_VBAT, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"vph_pwr", CHANNEL_VPH_PWR, CHAN_PATH_SCALING2, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + /* AMUX8 is used to read either Batt_id/Batt_therm. + * Current configuration is to support Batt_id. If clients + * want to read the Batt_therm, the scaling function needs to be + * updated to use ADC_SCALE_BATT_THERM instead of ADC_SCALE_DEFAULT. + * E.g. + * {"batt_therm", CHANNEL_BATT_ID_THERM, CHAN_PATH_SCALING1, + * AMUX_RSV2, ADC_DECIMATION_TYPE2, ADC_SCALE_BATT_THERM}, + */ + {"batt_id", CHANNEL_BATT_ID_THERM, CHAN_PATH_SCALING1, + AMUX_RSV2, ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pmic_therm", CHANNEL_DIE_TEMP, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PMIC_THERM}, + {"625mv", CHANNEL_625MV, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"125v", CHANNEL_125V, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT}, + {"pa_therm0", ADC_MPP_1_AMUX3, CHAN_PATH_SCALING1, AMUX_RSV1, + ADC_DECIMATION_TYPE2, ADC_SCALE_PA_THERM}, +}; + +static struct pm8xxx_adc_properties pm8018_adc_data = { + .adc_vdd_reference = 1800, /* milli-voltage for this adc */ + .bitresolution = 15, + .bipolar = 0, +}; + +static struct pm8xxx_adc_platform_data pm8018_adc_pdata = { + .adc_channel = pm8018_adc_channels_data, + .adc_num_board_channel = ARRAY_SIZE(pm8018_adc_channels_data), + .adc_prop = &pm8018_adc_data, +}; + +static struct pm8xxx_irq_platform_data pm8xxx_irq_pdata __devinitdata = { + .irq_base = PM8018_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(87), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_gpio_platform_data pm8xxx_gpio_pdata __devinitdata = { + .gpio_base = PM8018_GPIO_PM_TO_SYS(1), +}; + +static struct pm8xxx_mpp_platform_data pm8xxx_mpp_pdata __devinitdata = { + .mpp_base = PM8018_MPP_PM_TO_SYS(1), +}; + +static struct pm8xxx_rtc_platform_data pm8xxx_rtc_pdata __devinitdata = { + .rtc_write_enable = false, + .rtc_alarm_powerup = false, +}; + +static struct pm8xxx_pwrkey_platform_data pm8xxx_pwrkey_pdata = { + .pull_up = 1, + .kpd_trigger_delay_us = 15625, + .wakeup = 1, +}; + +static struct pm8xxx_misc_platform_data pm8xxx_misc_pdata = { + .priority = 0, +}; + +#define PM8018_LED_KB_MAX_CURRENT 20 /* I = 20mA */ +#define PM8XXX_LED_PWM_PERIOD_US 1000 + +/** + * PM8XXX_PWM_CHANNEL_NONE shall be used when LED shall not be + * driven using PWM feature. + */ +#define PM8XXX_PWM_CHANNEL_NONE -1 + +static struct led_info pm8018_led_info[] = { + [0] = { + .name = "led:kb", + }, +}; + +static struct led_platform_data pm8018_led_core_pdata = { + .num_leds = ARRAY_SIZE(pm8018_led_info), + .leds = pm8018_led_info, +}; + +static struct pm8xxx_led_config pm8018_led_configs[] = { + [0] = { + .id = PM8XXX_ID_LED_KB_LIGHT, + .mode = PM8XXX_LED_MODE_PWM3, + .max_current = PM8018_LED_KB_MAX_CURRENT, + .pwm_channel = 2, + .pwm_period_us = PM8XXX_LED_PWM_PERIOD_US, + }, +}; + +static struct pm8xxx_led_platform_data pm8xxx_leds_pdata = { + .led_core = &pm8018_led_core_pdata, + .configs = pm8018_led_configs, + .num_configs = ARRAY_SIZE(pm8018_led_configs), +}; + +#ifdef CONFIG_LTC4088_CHARGER +static struct ltc4088_charger_platform_data ltc4088_chg_pdata = { + .gpio_mode_select_d0 = 7, + .gpio_mode_select_d1 = 6, + .gpio_mode_select_d2 = 4, +}; +#endif + +static struct pm8018_platform_data pm8018_platform_data __devinitdata = { + .irq_pdata = &pm8xxx_irq_pdata, + .gpio_pdata = &pm8xxx_gpio_pdata, + .mpp_pdata = &pm8xxx_mpp_pdata, + .rtc_pdata = &pm8xxx_rtc_pdata, + .pwrkey_pdata = &pm8xxx_pwrkey_pdata, + .misc_pdata = &pm8xxx_misc_pdata, + .regulator_pdatas = msm_pm8018_regulator_pdata, + .adc_pdata = &pm8018_adc_pdata, + .leds_pdata = &pm8xxx_leds_pdata, +}; + +static struct msm_ssbi_platform_data msm9615_ssbi_pm8018_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = PM8018_CORE_DEV_NAME, + .platform_data = &pm8018_platform_data, + }, +}; + +static struct platform_device msm9615_device_rpm_regulator __devinitdata = { + .name = "rpm-regulator", + .id = -1, + .dev = { + .platform_data = &msm_rpm_regulator_9615_pdata, + }, +}; + +static struct platform_device msm9615_device_ext_2p95v_vreg = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = 18, + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_2P95V], + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, + .v_addr = MSM_APCS_GLB_BASE + 0x24, +}; + +static void __init msm9615_init_buses(void) +{ +#ifdef CONFIG_MSM_BUS_SCALING + msm_bus_rpm_set_mt_mask(); + msm_bus_9615_sys_fabric_pdata.rpm_enabled = 1; + msm_bus_9615_sys_fabric.dev.platform_data = + &msm_bus_9615_sys_fabric_pdata; + msm_bus_def_fab.dev.platform_data = &msm_bus_9615_def_fab_pdata; +#endif +} + +#ifdef CONFIG_WCD9310_CODEC + +#define TABLA_INTERRUPT_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) + +/* + * MDM9x15 I2S. + */ +static struct wcd9xxx_pdata wcd9xxx_i2c_platform_data = { + .irq = MSM_GPIO_TO_INT(85), + .irq_base = TABLA_INTERRUPT_BASE, + .num_irqs = NR_TABLA_IRQS, + .reset_gpio = 84, + .micbias = { + .ldoh_v = TABLA_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 1800, + .cfilt3_mv = 1800, + .bias1_cfilt_sel = TABLA_CFILT1_SEL, + .bias2_cfilt_sel = TABLA_CFILT2_SEL, + .bias3_cfilt_sel = TABLA_CFILT3_SEL, + .bias4_cfilt_sel = TABLA_CFILT3_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1225000, + .max_uV = 1225000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1225000, + .max_uV = 1225000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + } + }, +}; + +static struct i2c_board_info wcd9xxx_device_info[] __initdata = { + { + I2C_BOARD_INFO("tabla top level", TABLA_I2C_SLAVE_ADDR), + .platform_data = &wcd9xxx_i2c_platform_data, + }, + { + I2C_BOARD_INFO("tabla analog", TABLA_ANALOG_I2C_SLAVE_ADDR), + .platform_data = &wcd9xxx_i2c_platform_data, + }, + { + I2C_BOARD_INFO("tabla digital1", TABLA_DIGITAL1_I2C_SLAVE_ADDR), + .platform_data = &wcd9xxx_i2c_platform_data, + }, + { + I2C_BOARD_INFO("tabla digital2", TABLA_DIGITAL2_I2C_SLAVE_ADDR), + .platform_data = &wcd9xxx_i2c_platform_data, + }, +}; + +/* + * MDM9x15 I2S. + */ + +/* Micbias setting is based on 8660 CDP/MTP/FLUID requirement + * 4 micbiases are used to power various analog and digital + * microphones operating at 1800 mV. Technically, all micbiases + * can source from single cfilter since all microphones operate + * at the same voltage level. The arrangement below is to make + * sure all cfilters are exercised. LDO_H regulator ouput level + * does not need to be as high as 2.85V. It is choosen for + * microphone sensitivity purpose. + */ + +static struct wcd9xxx_pdata tabla20_platform_data = { + .slimbus_slave_device = { + .name = "tabla-slave", + .e_addr = {0, 0, 0x60, 0, 0x17, 2}, + }, + .irq = MSM_GPIO_TO_INT(85), + .irq_base = TABLA_INTERRUPT_BASE, + .num_irqs = NR_WCD9XXX_IRQS, + .reset_gpio = 84, + .micbias = { + .ldoh_v = TABLA_LDOH_2P85_V, + .cfilt1_mv = 1800, + .cfilt2_mv = 1800, + .cfilt3_mv = 1800, + .bias1_cfilt_sel = TABLA_CFILT1_SEL, + .bias2_cfilt_sel = TABLA_CFILT2_SEL, + .bias3_cfilt_sel = TABLA_CFILT3_SEL, + .bias4_cfilt_sel = TABLA_CFILT3_SEL, + }, + .regulator = { + { + .name = "CDC_VDD_CP", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX, + }, + { + .name = "CDC_VDDA_RX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_RX_CUR_MAX, + }, + { + .name = "CDC_VDDA_TX", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_CDC_VDDA_TX_CUR_MAX, + }, + { + .name = "VDDIO_CDC", + .min_uV = 1800000, + .max_uV = 1800000, + .optimum_uA = WCD9XXX_VDDIO_CDC_CUR_MAX, + }, + { + .name = "VDDD_CDC_D", + .min_uV = 1225000, + .max_uV = 1225000, + .optimum_uA = WCD9XXX_VDDD_CDC_D_CUR_MAX, + }, + { + .name = "CDC_VDDA_A_1P2V", + .min_uV = 1225000, + .max_uV = 1225000, + .optimum_uA = WCD9XXX_VDDD_CDC_A_CUR_MAX, + }, + }, +}; + +static struct slim_device msm_slim_tabla20 = { + .name = "tabla2x-slim", + .e_addr = {0, 1, 0x60, 0, 0x17, 2}, + .dev = { + .platform_data = &tabla20_platform_data, + }, +}; +#endif + +static struct i2c_registry msm9615_i2c_devices[] __initdata = { +#ifdef CONFIG_WCD9310_CODEC + { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_9615_GSBI5_QUP_I2C_BUS_ID, + wcd9xxx_device_info, + ARRAY_SIZE(wcd9xxx_device_info), + }, +#endif +}; + +static struct slim_boardinfo msm_slim_devices[] = { + /* add slimbus slaves as needed */ +#ifdef CONFIG_WCD9310_CODEC + { + .bus_num = 1, + .slim_slave = &msm_slim_tabla20, + }, +#endif +}; + +static struct msm_spi_platform_data msm9615_qup_spi_gsbi3_pdata = { + .max_clock_speed = 24000000, +}; + +static struct msm_i2c_platform_data msm9615_i2c_qup_gsbi5_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, +}; + +#define USB_5V_EN 3 +#define PM_USB_5V_EN PM8018_GPIO_PM_TO_SYS(USB_5V_EN) + +static int msm_hsusb_vbus_power(bool on) +{ + int rc; + struct pm_gpio usb_vbus = { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .vin_sel = 2, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }; + + usb_vbus.output_value = on; + + rc = pm8xxx_gpio_config(PM_USB_5V_EN, &usb_vbus); + if (rc) + pr_err("failed to config usb_5v_en gpio\n"); + + return rc; +} + +static int shelby_phy_init_seq[] = { + 0x44, 0x80,/* set VBUS valid threshold and + disconnect valid threshold */ + 0x38, 0x81, /* update DC voltage level */ + 0x24, 0x82,/* set preemphasis and rise/fall time */ + 0x13, 0x83,/* set source impedance adjustment */ + -1}; + +#define USB_BAM_PHY_BASE 0x12502000 +#define HSIC_BAM_PHY_BASE 0x12542000 +#define A2_BAM_PHY_BASE 0x124C2000 +static struct usb_bam_pipe_connect msm_usb_bam_connections[2][4][2] = { + [0][0][USB_TO_PEER_PERIPHERAL] = { + .src_phy_addr = USB_BAM_PHY_BASE, + .src_pipe_index = 11, + .dst_phy_addr = A2_BAM_PHY_BASE, + .dst_pipe_index = 0, + .data_fifo_base_offset = 0x1100, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x1700, + .desc_fifo_size = 0x300, + }, + [0][0][PEER_PERIPHERAL_TO_USB] = { + .src_phy_addr = A2_BAM_PHY_BASE, + .src_pipe_index = 1, + .dst_phy_addr = USB_BAM_PHY_BASE, + .dst_pipe_index = 10, + .data_fifo_base_offset = 0xa00, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x1000, + .desc_fifo_size = 0x100, + }, + [0][1][USB_TO_PEER_PERIPHERAL] = { + .src_phy_addr = USB_BAM_PHY_BASE, + .src_pipe_index = 13, + .dst_phy_addr = A2_BAM_PHY_BASE, + .dst_pipe_index = 2, + .data_fifo_base_offset = 0x2100, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x2700, + .desc_fifo_size = 0x300, + }, + [0][1][PEER_PERIPHERAL_TO_USB] = { + .src_phy_addr = A2_BAM_PHY_BASE, + .src_pipe_index = 3, + .dst_phy_addr = USB_BAM_PHY_BASE, + .dst_pipe_index = 12, + .data_fifo_base_offset = 0x1a00, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x2000, + .desc_fifo_size = 0x100, + }, + [0][2][USB_TO_PEER_PERIPHERAL] = { + .src_phy_addr = USB_BAM_PHY_BASE, + .src_pipe_index = 15, + .dst_phy_addr = A2_BAM_PHY_BASE, + .dst_pipe_index = 4, + .data_fifo_base_offset = 0x3100, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x3700, + .desc_fifo_size = 0x300, + }, + [0][2][PEER_PERIPHERAL_TO_USB] = { + .src_phy_addr = A2_BAM_PHY_BASE, + .src_pipe_index = 5, + .dst_phy_addr = USB_BAM_PHY_BASE, + .dst_pipe_index = 14, + .data_fifo_base_offset = 0x2a00, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x3000, + .desc_fifo_size = 0x100, + }, + [1][0][USB_TO_PEER_PERIPHERAL] = { + .src_phy_addr = HSIC_BAM_PHY_BASE, + .src_pipe_index = 1, + .dst_phy_addr = A2_BAM_PHY_BASE, + .dst_pipe_index = 0, + .data_fifo_base_offset = 0x1100, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x1700, + .desc_fifo_size = 0x300, + }, + [1][0][PEER_PERIPHERAL_TO_USB] = { + .src_phy_addr = A2_BAM_PHY_BASE, + .src_pipe_index = 1, + .dst_phy_addr = HSIC_BAM_PHY_BASE, + .dst_pipe_index = 0, + .data_fifo_base_offset = 0xa00, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x1000, + .desc_fifo_size = 0x100, + }, + [1][1][USB_TO_PEER_PERIPHERAL] = { + .src_phy_addr = HSIC_BAM_PHY_BASE, + .src_pipe_index = 3, + .dst_phy_addr = A2_BAM_PHY_BASE, + .dst_pipe_index = 2, + .data_fifo_base_offset = 0x2100, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x2700, + .desc_fifo_size = 0x300, + }, + [1][1][PEER_PERIPHERAL_TO_USB] = { + .src_phy_addr = A2_BAM_PHY_BASE, + .src_pipe_index = 3, + .dst_phy_addr = HSIC_BAM_PHY_BASE, + .dst_pipe_index = 2, + .data_fifo_base_offset = 0x1a00, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x2000, + .desc_fifo_size = 0x100, + }, + [1][2][USB_TO_PEER_PERIPHERAL] = { + .src_phy_addr = HSIC_BAM_PHY_BASE, + .src_pipe_index = 5, + .dst_phy_addr = A2_BAM_PHY_BASE, + .dst_pipe_index = 4, + .data_fifo_base_offset = 0x3100, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x3700, + .desc_fifo_size = 0x300, + }, + [1][2][PEER_PERIPHERAL_TO_USB] = { + .src_phy_addr = A2_BAM_PHY_BASE, + .src_pipe_index = 5, + .dst_phy_addr = HSIC_BAM_PHY_BASE, + .dst_pipe_index = 4, + .data_fifo_base_offset = 0x2a00, + .data_fifo_size = 0x600, + .desc_fifo_base_offset = 0x3000, + .desc_fifo_size = 0x100, + } +}; + +static struct msm_usb_bam_platform_data msm_usb_bam_pdata = { + .connections = &msm_usb_bam_connections[0][0][0], +#ifndef CONFIG_USB_CI13XXX_MSM_HSIC + .usb_active_bam = HSUSB_BAM, +#else + .usb_active_bam = HSIC_BAM, +#endif + .usb_bam_num_pipes = 16, +}; + +static struct msm_otg_platform_data msm_otg_pdata = { + .mode = USB_OTG, + .otg_control = OTG_PHY_CONTROL, + .phy_type = SNPS_28NM_INTEGRATED_PHY, + .vbus_power = msm_hsusb_vbus_power, + .disable_reset_on_disconnect = true, + .enable_lpm_on_dev_suspend = true, +}; + +static struct msm_hsic_peripheral_platform_data msm_hsic_peripheral_pdata = { + .keep_core_clk_on_suspend_workaround = true, +}; + +#define PID_MAGIC_ID 0x71432909 +#define SERIAL_NUM_MAGIC_ID 0x61945374 +#define SERIAL_NUMBER_LENGTH 127 +#define DLOAD_USB_BASE_ADD 0x2B0000C8 + +struct magic_num_struct { + uint32_t pid; + uint32_t serial_num; +}; + +struct dload_struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint16_t reserved4; + uint16_t pid; + char serial_number[SERIAL_NUMBER_LENGTH]; + uint16_t reserved5; + struct magic_num_struct magic_struct; +}; + +static int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) +{ + struct dload_struct __iomem *dload = 0; + + dload = ioremap(DLOAD_USB_BASE_ADD, sizeof(*dload)); + if (!dload) { + pr_err("%s: cannot remap I/O memory region: %08x\n", + __func__, DLOAD_USB_BASE_ADD); + return -ENXIO; + } + + pr_debug("%s: dload:%p pid:%x serial_num:%s\n", + __func__, dload, pid, snum); + /* update pid */ + dload->magic_struct.pid = PID_MAGIC_ID; + dload->pid = pid; + + /* update serial number */ + dload->magic_struct.serial_num = 0; + if (!snum) { + memset(dload->serial_number, 0, SERIAL_NUMBER_LENGTH); + goto out; + } + + dload->magic_struct.serial_num = SERIAL_NUM_MAGIC_ID; + strlcpy(dload->serial_number, snum, SERIAL_NUMBER_LENGTH); +out: + iounmap(dload); + return 0; +} + +static struct platform_device msm_wlan_ar6000_pm_device = { + .name = "wlan_ar6000_pm_dev", + .id = -1, +}; + +static int __init msm9615_init_ar6000pm(void) +{ + return platform_device_register(&msm_wlan_ar6000_pm_device); +} + +#ifdef CONFIG_LTC4088_CHARGER +static struct platform_device msm_device_charger = { + .name = LTC4088_CHARGER_DEV_NAME, + .id = -1, + .dev = { + .platform_data = <c4088_chg_pdata, + }, +}; +#endif + +static struct tsens_platform_data msm_tsens_pdata = { + .tsens_factor = 1000, + .hw_type = MDM_9615, + .tsens_num_sensor = 5, + .slope = {1176, 1162, 1162, 1149, 1176}, +}; + +static struct platform_device msm_tsens_device = { + .name = "tsens8960-tm", + .id = -1, +}; + +static struct platform_device *common_devices[] = { + &msm9615_device_dmov, + &msm_device_smd, +#ifdef CONFIG_LTC4088_CHARGER + &msm_device_charger, +#endif + &msm_device_otg, + &msm_device_hsic_peripheral, + &msm_device_gadget_peripheral, + &msm_device_hsusb_host, + &msm_device_hsic_host, + &msm_device_usb_bam, + &msm_android_usb_device, + &msm9615_device_uart_gsbi4, + &msm9615_device_ext_2p95v_vreg, + &msm9615_device_ssbi_pmic1, + &msm9615_device_qup_i2c_gsbi5, + &msm9615_device_qup_spi_gsbi3, + &msm_device_sps, + &msm9615_slim_ctrl, + &msm_device_nand, + &msm_device_bam_dmux, + &msm9615_rpm_device, +#ifdef CONFIG_HW_RANDOM_MSM + &msm_device_rng, +#endif +#ifdef CONFIG_ION_MSM + &ion_dev, +#endif + + &msm_pcm, + &msm_multi_ch_pcm, + &msm_pcm_routing, + &msm_cpudai0, + &msm_cpudai1, + &msm_cpudai_bt_rx, + &msm_cpudai_bt_tx, + &msm_cpu_fe, + &msm_stub_codec, + &msm_voice, + &msm_voip, + &msm_i2s_cpudai0, + &msm_i2s_cpudai1, + &msm_pcm_hostless, + &msm_cpudai_afe_01_rx, + &msm_cpudai_afe_01_tx, + &msm_cpudai_afe_02_rx, + &msm_cpudai_afe_02_tx, + &msm_pcm_afe, + &msm_cpudai_auxpcm_rx, + &msm_cpudai_auxpcm_tx, + &msm_cpudai_sec_auxpcm_rx, + &msm_cpudai_sec_auxpcm_tx, + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &msm9615_qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &msm9615_qcedev_device, +#endif + &msm9615_device_watchdog, + &msm_bus_9615_sys_fabric, + &msm_bus_def_fab, + &msm9615_rpm_log_device, + &msm9615_rpm_stat_device, + &msm_tsens_device, +}; + +static void __init msm9615_i2c_init(void) +{ + u8 mach_mask = 0; + int i; + /* Mask is hardcoded to SURF (CDP). + * works on MTP with same configuration. + */ + mach_mask = I2C_SURF; + if (machine_is_msm9615_cdp()) + mach_mask = I2C_SURF; + else if (machine_is_msm9615_mtp()) + mach_mask = I2C_FFA; + else + pr_err("unmatched machine ID in register_i2c_devices\n"); + msm9615_device_qup_i2c_gsbi5.dev.platform_data = + &msm9615_i2c_qup_gsbi5_pdata; + for (i = 0; i < ARRAY_SIZE(msm9615_i2c_devices); ++i) { + if (msm9615_i2c_devices[i].machs & mach_mask) { + i2c_register_board_info(msm9615_i2c_devices[i].bus, + msm9615_i2c_devices[i].info, + msm9615_i2c_devices[i].len); + } + } +} + +static void __init msm9615_reserve(void) +{ +#ifdef CONFIG_ION_MSM + reserve_info = &msm9615_reserve_info; + msm_reserve(); +#endif +} + +static void __init msm9615_common_init(void) +{ + struct android_usb_platform_data *android_pdata = + msm_android_usb_device.dev.platform_data; + + msm9615_device_init(); + msm9615_init_gpiomux(); + msm9615_i2c_init(); + regulator_suppress_info_printing(); + platform_device_register(&msm9615_device_rpm_regulator); + msm_xo_init(); + msm_clock_init(&msm9615_clock_init_data); + msm9615_init_buses(); + msm9615_device_qup_spi_gsbi3.dev.platform_data = + &msm9615_qup_spi_gsbi3_pdata; + msm9615_device_ssbi_pmic1.dev.platform_data = + &msm9615_ssbi_pm8018_pdata; + pm8018_platform_data.num_regulators = msm_pm8018_regulator_pdata_len; + + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_otg_pdata.phy_init_seq = shelby_phy_init_seq; + msm_device_hsic_peripheral.dev.platform_data = + &msm_hsic_peripheral_pdata; + msm_device_usb_bam.dev.platform_data = &msm_usb_bam_pdata; + platform_add_devices(common_devices, ARRAY_SIZE(common_devices)); + msm9615_pm8xxx_gpio_mpp_init(); + acpuclk_init(&acpuclk_9615_soc_data); + + /* Ensure ar6000pm device is registered before MMC/SDC */ + msm9615_init_ar6000pm(); + + msm9615_init_mmc(); + slim_register_board_info(msm_slim_devices, + ARRAY_SIZE(msm_slim_devices)); + android_pdata->update_pid_and_serial_num = + usb_diag_update_pid_and_serial_num; + msm_pm_boot_pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_tsens_early_init(&msm_tsens_pdata); +} + +static void __init msm9615_cdp_init(void) +{ + msm9615_common_init(); +#ifdef CONFIG_FB_MSM + mdm9615_init_fb(); +#endif +} + +static void __init msm9615_mtp_init(void) +{ + msm9615_common_init(); +} + +#ifdef CONFIG_FB_MSM +static void __init mdm9615_allocate_memory_regions(void) +{ + mdm9615_allocate_fb_region(); +} +#endif + +MACHINE_START(MSM9615_CDP, "QCT MSM9615 CDP") + .map_io = msm9615_map_io, + .init_irq = msm9615_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm9615_cdp_init, + .reserve = msm9615_reserve, +#ifdef CONFIG_FB_MSM + .init_early = mdm9615_allocate_memory_regions, +#endif + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM9615_MTP, "QCT MSM9615 MTP") + .map_io = msm9615_map_io, + .init_irq = msm9615_init_irq, + .handle_irq = gic_handle_irq, + .timer = &msm_timer, + .init_machine = msm9615_mtp_init, + .reserve = msm9615_reserve, + .restart = msm_restart, +MACHINE_END diff --git a/arch/arm/mach-msm/board-9615.h b/arch/arm/mach-msm/board-9615.h new file mode 100644 index 00000000000..68d9951c182 --- /dev/null +++ b/arch/arm/mach-msm/board-9615.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_9615_H +#define __ARCH_ARM_MACH_MSM_BOARD_9615_H + +#include +#include +#include + +/* + * MDM9x15 I2S. + */ +#ifdef CONFIG_I2C +#define I2C_SURF 1 +#define I2C_FFA (1 << 1) +#define I2C_RUMI (1 << 2) +#define I2C_SIM (1 << 3) +#define I2C_FLUID (1 << 4) +#define I2C_LIQUID (1 << 5) + +struct i2c_registry { + u8 machs; + int bus; + struct i2c_board_info *info; + int len; +}; +#endif +/* Tabla slave address for I2C */ +#define TABLA_I2C_SLAVE_ADDR 0x0d +#define TABLA_ANALOG_I2C_SLAVE_ADDR 0x77 +#define TABLA_DIGITAL1_I2C_SLAVE_ADDR 0x66 +#define TABLA_DIGITAL2_I2C_SLAVE_ADDR 0x55 +#define MSM_9615_GSBI5_QUP_I2C_BUS_ID 0 +/* + * MDM9x15 I2S. + */ + +/* Macros assume PMIC GPIOs and MPPs start at 1 */ +#define PM8018_GPIO_BASE NR_GPIO_IRQS +#define PM8018_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8018_GPIO_BASE) +#define PM8018_MPP_BASE (PM8018_GPIO_BASE + PM8018_NR_GPIOS) +#define PM8018_MPP_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8018_MPP_BASE) +#define PM8018_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) +#define PM8018_MPP_IRQ_BASE (PM8018_IRQ_BASE + NR_GPIO_IRQS) + +extern struct pm8xxx_regulator_platform_data + msm_pm8018_regulator_pdata[] __devinitdata; + +extern int msm_pm8018_regulator_pdata_len __devinitdata; + +extern struct rpm_regulator_platform_data +msm_rpm_regulator_9615_pdata __devinitdata; + +#define GPIO_VREG_ID_EXT_2P95V 0 + +extern struct gpio_regulator_platform_data msm_gpio_regulator_pdata[]; +uint32_t msm9615_rpm_get_swfi_latency(void); +int msm9615_init_gpiomux(void); +void msm9615_init_mmc(void); +void mdm9615_allocate_fb_region(void); +void mdm9615_init_fb(void); +#endif diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c new file mode 100644 index 00000000000..e28c734a719 --- /dev/null +++ b/arch/arm/mach-msm/board-9625-gpiomux.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +static struct gpiomux_setting gpio_uart_config = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_OUT_HIGH, +}; + +static struct msm_gpiomux_config msm_blsp_configs[] __initdata = { + { + .gpio = 45, /* BLSP1 UART TX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_uart_config, + }, + }, + { + .gpio = 46, /* BLSP1 UART RX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_uart_config, + }, + }, +}; + +void __init msm9625_init_gpiomux(void) +{ + int rc; + + rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err(KERN_ERR "msm9625_init_gpiomux failed %d\n", rc); + return; + } + + msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs)); +} diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c new file mode 100644 index 00000000000..60dfe3c287f --- /dev/null +++ b/arch/arm/mach-msm/board-9625.c @@ -0,0 +1,104 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" + +static struct clk_lookup msm_clocks_dummy[] = { + CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF), + CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF), + CLK_DUMMY("phy_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("core_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("alt_core_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("iface_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("xo", NULL, "msm_otg", OFF), + CLK_DUMMY("dfab_clk", DFAB_CLK, NULL, 0), + CLK_DUMMY("dma_bam_pclk", DMA_BAM_P_CLK, NULL, 0), + CLK_DUMMY("mem_clk", NULL, NULL, 0), + CLK_DUMMY("core_clk", NULL, "spi_qsd.1", OFF), + CLK_DUMMY("iface_clk", NULL, "spi_qsd.1", OFF), + CLK_DUMMY("core_clk", NULL, "f9966000.i2c", 0), + CLK_DUMMY("iface_clk", NULL, "f9966000.i2c", 0), + CLK_DUMMY("core_clk", NULL, "fe12f000.slim", OFF), +}; + +struct clock_init_data msm_dummy_clock_init_data __initdata = { + .table = msm_clocks_dummy, + .size = ARRAY_SIZE(msm_clocks_dummy), +}; + +static struct of_device_id irq_match[] __initdata = { + { .compatible = "qcom,msm-qgic2", .data = gic_of_init, }, + { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, }, + {} +}; + +static const char *msm9625_dt_match[] __initconst = { + "qcom,msm9625", + NULL +}; + +static struct of_dev_auxdata msm9625_auxdata_lookup[] __initdata = { + OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \ + "msm_serial_hsl.0", NULL), + {} +}; + +void __init msm9625_init_irq(void) +{ + of_irq_init(irq_match); +} + +static void __init msm_dt_timer_init(void) +{ + arch_timer_of_register(); +} + +static struct sys_timer msm_dt_timer = { + .init = msm_dt_timer_init +}; + +void __init msm9625_init(void) +{ + if (socinfo_init() < 0) + pr_err("%s: socinfo_init() failed\n", __func__); + msm_clock_init(&msm_dummy_clock_init_data); + of_platform_populate(NULL, of_default_bus_match_table, + msm9625_auxdata_lookup, NULL); +} + +DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)") + .map_io = msm_map_msm9625_io, + .init_irq = msm9625_init_irq, + .init_machine = msm9625_init, + .handle_irq = gic_handle_irq, + .timer = &msm_dt_timer, + .dt_compat = msm9625_dt_match, + .nr_irqs = -1, +MACHINE_END diff --git a/arch/arm/mach-msm/board-copper-gpiomux.c b/arch/arm/mach-msm/board-copper-gpiomux.c new file mode 100644 index 00000000000..caba698438c --- /dev/null +++ b/arch/arm/mach-msm/board-copper-gpiomux.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#define KS8851_IRQ_GPIO 90 + +static struct gpiomux_setting gpio_uart_config = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, + .dir = GPIOMUX_OUT_HIGH, +}; + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) +static struct gpiomux_setting gpio_eth_config = { + .pull = GPIOMUX_PULL_NONE, + .drv = GPIOMUX_DRV_8MA, + .func = GPIOMUX_FUNC_GPIO, +}; + +static struct gpiomux_setting gpio_spi_cs_config = { + .func = GPIOMUX_FUNC_4, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gpio_spi_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct msm_gpiomux_config msm_eth_configs[] = { + { + .gpio = KS8851_IRQ_GPIO, + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_eth_config, + } + }, +}; +#endif +static struct gpiomux_setting gpio_i2c_config = { + .func = GPIOMUX_FUNC_3, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + + +static struct msm_gpiomux_config msm_blsp_configs[] __initdata = { +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) + { + .gpio = 0, /* BLSP1 QUP SPI_DATA_MOSI */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, + { + .gpio = 1, /* BLSP1 QUP SPI_DATA_MISO */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, + { + .gpio = 3, /* BLSP1 QUP SPI_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_config, + }, + }, + { + .gpio = 9, /* BLSP1 QUP SPI_CS_N */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_spi_cs_config, + }, + }, +#endif + { + .gpio = 83, /* BLSP11 QUP I2C_DAT */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_i2c_config, + }, + }, + { + .gpio = 84, /* BLSP11 QUP I2C_CLK */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_i2c_config, + }, + }, + { + .gpio = 45, /* BLSP8 UART TX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_uart_config, + }, + }, + { + .gpio = 46, /* BLSP8 UART RX */ + .settings = { + [GPIOMUX_SUSPENDED] = &gpio_uart_config, + }, + }, +}; + +void __init msm_copper_init_gpiomux(void) +{ + int rc; + + rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err(KERN_ERR "msmcopper_init_gpiomux failed %d\n", rc); + return; + } + +#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE) + msm_gpiomux_install(msm_eth_configs, ARRAY_SIZE(msm_eth_configs)); +#endif + msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs)); +} diff --git a/arch/arm/mach-msm/board-copper-regulator.c b/arch/arm/mach-msm/board-copper-regulator.c new file mode 100644 index 00000000000..75438725124 --- /dev/null +++ b/arch/arm/mach-msm/board-copper-regulator.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#define VREG_CONSUMERS(_name) \ + static struct regulator_consumer_supply vreg_consumers_##_name[] + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +VREG_CONSUMERS(K0) = { + REGULATOR_SUPPLY("krait0", NULL), +}; +VREG_CONSUMERS(K1) = { + REGULATOR_SUPPLY("krait1", NULL), +}; +VREG_CONSUMERS(K2) = { + REGULATOR_SUPPLY("krait2", NULL), +}; +VREG_CONSUMERS(K3) = { + REGULATOR_SUPPLY("krait3", NULL), +}; + +#define PM8X41_VREG_INIT(_id, _name, _min_uV, _max_uV, _modes, _ops, \ + _always_on, _supply_regulator, _hpm_min, _system_uA) \ + struct stub_regulator_pdata vreg_dev_##_id##_pdata __devinitdata = { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _max_uV, \ + .apply_uV = 0, \ + .always_on = _always_on, \ + .name = _name, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + .supply_regulator = _supply_regulator, \ + }, \ + .hpm_min_load = _hpm_min, \ + .system_uA = _system_uA, \ + } + +#define KRAIT_PWR(_id, _name, _always_on, _min_uV, _max_uV, \ + _supply_regulator, _hpm_min, _system_uA) \ + PM8X41_VREG_INIT(_id, _name, _min_uV, _max_uV, REGULATOR_MODE_NORMAL \ + | REGULATOR_MODE_IDLE, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, _always_on, \ + _supply_regulator, _hpm_min, _system_uA) + +/* ID name a_on min_uV max_uV supply hpm_min sys_uA */ +KRAIT_PWR(K0, "krait0", 0, 850000, 1100000, NULL, 100000, 0); +KRAIT_PWR(K1, "krait1", 0, 850000, 1100000, NULL, 100000, 0); +KRAIT_PWR(K2, "krait2", 0, 850000, 1100000, NULL, 100000, 0); +KRAIT_PWR(K3, "krait3", 0, 850000, 1100000, NULL, 100000, 0); + +#define VREG_DEVICE(_name, _devid) \ + vreg_device_##_name __devinitdata = \ + { \ + .name = STUB_REGULATOR_DRIVER_NAME, \ + .id = _devid, \ + .dev = { .platform_data = &vreg_dev_##_name##_pdata }, \ + } + +static struct platform_device VREG_DEVICE(K0, 0); +static struct platform_device VREG_DEVICE(K1, 1); +static struct platform_device VREG_DEVICE(K2, 2); +static struct platform_device VREG_DEVICE(K3, 3); + +struct platform_device *msm_copper_stub_regulator_devices[] __devinitdata = { + &vreg_device_K0, + &vreg_device_K1, + &vreg_device_K2, + &vreg_device_K3, +}; + +int msm_copper_stub_regulator_devices_len __devinitdata = + ARRAY_SIZE(msm_copper_stub_regulator_devices); diff --git a/arch/arm/mach-msm/board-copper.c b/arch/arm/mach-msm/board-copper.c new file mode 100644 index 00000000000..2afefa61f85 --- /dev/null +++ b/arch/arm/mach-msm/board-copper.c @@ -0,0 +1,526 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ION_MSM +#include +#endif +#include +#ifdef CONFIG_ANDROID_PMEM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ION_MSM +#include +#endif +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "devices.h" +#include "spm.h" + +#define MSM_KERNEL_EBI1_MEM_SIZE 0x280000 +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +#define MSM_ION_SF_SIZE 0x4000000 /* 64 Mbytes */ +#else +#define MSM_ION_SF_SIZE 0x2800000 /* 40 Mbytes */ +#endif +#define MSM_ION_MM_FW_SIZE 0xa00000 /* (10MB) */ +#define MSM_ION_MM_SIZE 0x7800000 /* (120MB) */ +#define MSM_ION_QSECOM_SIZE 0x100000 /* (1MB) */ +#define MSM_ION_MFC_SIZE SZ_8K +#define MSM_ION_AUDIO_SIZE 0x2B4000 +#define MSM_ION_HEAP_NUM 8 + +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION +static unsigned kernel_ebi1_mem_size = MSM_KERNEL_EBI1_MEM_SIZE; +static int __init kernel_ebi1_mem_size_setup(char *p) +{ + kernel_ebi1_mem_size = memparse(p, NULL); + return 0; +} +early_param("kernel_ebi1_mem_size", kernel_ebi1_mem_size_setup); +#endif + +static struct memtype_reserve msm_copper_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static int msm_copper_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +#ifdef CONFIG_ION_MSM +static struct ion_cp_heap_pdata cp_mm_ion_pdata = { + .permission_type = IPT_TYPE_MM_CARVEOUT, + .align = PAGE_SIZE, +}; + +static struct ion_cp_heap_pdata cp_mfc_ion_pdata = { + .permission_type = IPT_TYPE_MFC_SHAREDMEM, + .align = PAGE_SIZE, +}; + +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static struct ion_co_heap_pdata fw_co_ion_pdata = { + .adjacent_mem_id = ION_CP_MM_HEAP_ID, + .align = SZ_128K, +}; + +/** + * These heaps are listed in the order they will be allocated. Due to + * video hardware restrictions and content protection the FW heap has to + * be allocated adjacent (below) the MM heap and the MFC heap has to be + * allocated after the MM heap to ensure MFC heap is not more than 256MB + * away from the base address of the FW heap. + * However, the order of FW heap and MM heap doesn't matter since these + * two heaps are taken care of by separate code to ensure they are adjacent + * to each other. + * Don't swap the order unless you know what you are doing! + */ +static struct ion_platform_data ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + { + .id = ION_CP_MM_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MM_HEAP_NAME, + .size = MSM_ION_MM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mm_ion_pdata, + }, + { + .id = ION_MM_FIRMWARE_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_MM_FIRMWARE_HEAP_NAME, + .size = MSM_ION_MM_FW_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &fw_co_ion_pdata, + }, + { + .id = ION_CP_MFC_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MFC_HEAP_NAME, + .size = MSM_ION_MFC_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_mfc_ion_pdata, + }, + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .size = MSM_ION_SF_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_ion_pdata, + }, + { + .id = ION_IOMMU_HEAP_ID, + .type = ION_HEAP_TYPE_IOMMU, + .name = ION_IOMMU_HEAP_NAME, + }, + { + .id = ION_QSECOM_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_QSECOM_HEAP_NAME, + .size = MSM_ION_QSECOM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_ion_pdata, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .size = MSM_ION_AUDIO_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_ion_pdata, + }, + } +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; + +static void __init reserve_ion_memory(void) +{ + msm_copper_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MM_SIZE; + msm_copper_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MM_FW_SIZE; + msm_copper_reserve_table[MEMTYPE_EBI1].size += MSM_ION_SF_SIZE; + msm_copper_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MFC_SIZE; + msm_copper_reserve_table[MEMTYPE_EBI1].size += MSM_ION_QSECOM_SIZE; + msm_copper_reserve_table[MEMTYPE_EBI1].size += MSM_ION_AUDIO_SIZE; +#ifdef CONFIG_KERNEL_PMEM_EBI_REGION + msm_copper_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size; +#endif +} +#endif + +static struct resource smd_resource[] = { + { + .name = "modem_smd_in", + .start = 32 + 17, /* mss_sw_to_kpss_ipc_irq0 */ + .flags = IORESOURCE_IRQ, + }, + { + .name = "modem_smsm_in", + .start = 32 + 18, /* mss_sw_to_kpss_ipc_irq1 */ + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_smd_in", + .start = 32 + 156, /* lpass_to_kpss_ipc_irq0 */ + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_smsm_in", + .start = 32 + 157, /* lpass_to_kpss_ipc_irq1 */ + .flags = IORESOURCE_IRQ, + }, + { + .name = "wcnss_smd_in", + .start = 32 + 142, /* WcnssAppsSmdMedIrq */ + .flags = IORESOURCE_IRQ, + }, + { + .name = "wcnss_smsm_in", + .start = 32 + 144, /* RivaAppsWlanSmsmIrq */ + .flags = IORESOURCE_IRQ, + }, + { + .name = "rpm_smd_in", + .start = 32 + 168, /* rpm_to_kpss_ipc_irq4 */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smd_subsystem_config smd_config_list[] = { + { + .irq_config_id = SMD_MODEM, + .subsys_name = "modem", + .edge = SMD_APPS_MODEM, + + .smd_int.irq_name = "modem_smd_in", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 12, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "modem_smsm_in", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smsm_dev", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 13, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_Q6, + .subsys_name = "q6", + .edge = SMD_APPS_QDSP, + + .smd_int.irq_name = "adsp_smd_in", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 8, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "adsp_smsm_in", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smsm_dev", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 9, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_WCNSS, + .subsys_name = "wcnss", + .edge = SMD_APPS_WCNSS, + + .smd_int.irq_name = "wcnss_smd_in", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 17, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "wcnss_smsm_in", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smsm_dev", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 19, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_RPM, + .subsys_name = NULL, /* do not use PIL to load RPM */ + .edge = SMD_APPS_RPM, + + .smd_int.irq_name = "rpm_smd_in", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 0, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = NULL, /* RPM does not support SMSM */ + .smsm_int.flags = 0, + .smsm_int.irq_id = 0, + .smsm_int.device_name = NULL, + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 0, + .smsm_int.out_base = NULL, + .smsm_int.out_offset = 0, + }, +}; + +static struct smd_smem_regions aux_smem_areas[] = { + { + .phys_addr = (void *)(0xfc428000), + .size = 0x4000, + }, +}; + +static struct smd_subsystem_restart_config smd_ssr_cfg = { + .disable_smsm_reset_handshake = 1, +}; + +static struct smd_platform smd_platform_data = { + .num_ss_configs = ARRAY_SIZE(smd_config_list), + .smd_ss_configs = smd_config_list, + .smd_ssr_config = &smd_ssr_cfg, + .num_smem_areas = ARRAY_SIZE(aux_smem_areas), + .smd_smem_areas = aux_smem_areas, +}; + +struct platform_device msm_device_smd_copper = { + .name = "msm_smd", + .id = -1, + .resource = smd_resource, + .num_resources = ARRAY_SIZE(smd_resource), + .dev = { + .platform_data = &smd_platform_data, + } +}; + +static void __init msm_copper_calculate_reserve_sizes(void) +{ +#ifdef CONFIG_ION_MSM + reserve_ion_memory(); +#endif +} + +static struct reserve_info msm_copper_reserve_info __initdata = { + .memtype_reserve_table = msm_copper_reserve_table, + .calculate_reserve_sizes = msm_copper_calculate_reserve_sizes, + .paddr_to_memtype = msm_copper_paddr_to_memtype, +}; + +static void __init msm_copper_early_memory(void) +{ + reserve_info = &msm_copper_reserve_info; +} + +void __init msm_copper_reserve(void) +{ + msm_reserve(); +} + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, +}; + +#define SHARED_IMEM_TZ_BASE 0xFE805720 +static struct resource copper_tzlog_resources[] = { + { + .start = SHARED_IMEM_TZ_BASE, + .end = SHARED_IMEM_TZ_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device copper_device_tz_log = { + .name = "tz_log", + .id = 0, + .num_resources = ARRAY_SIZE(copper_tzlog_resources), + .resource = copper_tzlog_resources, +}; + + +void __init msm_copper_add_devices(void) +{ +#ifdef CONFIG_ION_MSM + platform_device_register(&ion_dev); +#endif + platform_device_register(&msm_device_smd_copper); + platform_device_register(&android_usb_device); + platform_add_devices(msm_copper_stub_regulator_devices, + msm_copper_stub_regulator_devices_len); + platform_device_register(&copper_device_tz_log); +} + +/* + * Used to satisfy dependencies for devices that need to be + * run early or in a particular order. Most likely your device doesn't fall + * into this category, and thus the driver should not be added here. The + * EPROBE_DEFER can satisfy most dependency problems. + */ +void __init msm_copper_add_drivers(void) +{ + msm_smd_init(); + msm_rpm_driver_init(); + rpm_regulator_smd_driver_init(); + msm_spm_device_init(); + regulator_stub_init(); +} + +static struct of_device_id irq_match[] __initdata = { + { .compatible = "qcom,msm-qgic2", .data = gic_of_init, }, + { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, }, + { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, }, + {} +}; + +void __init msm_copper_init_irq(void) +{ + of_irq_init(irq_match); +} + +static struct clk_lookup msm_clocks_dummy[] = { + CLK_DUMMY("xo", XO_CLK, NULL, OFF), + CLK_DUMMY("xo", XO_CLK, "pil_pronto", OFF), + CLK_DUMMY("core_clk", BLSP2_UART_CLK, "msm_serial_hsl.0", OFF), + CLK_DUMMY("iface_clk", BLSP2_UART_CLK, "msm_serial_hsl.0", OFF), + CLK_DUMMY("core_clk", SDC1_CLK, NULL, OFF), + CLK_DUMMY("iface_clk", SDC1_P_CLK, NULL, OFF), + CLK_DUMMY("core_clk", SDC3_CLK, NULL, OFF), + CLK_DUMMY("iface_clk", SDC3_P_CLK, NULL, OFF), + CLK_DUMMY("phy_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("core_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("iface_clk", NULL, "msm_otg", OFF), + CLK_DUMMY("xo", NULL, "msm_otg", OFF), + CLK_DUMMY("dfab_clk", DFAB_CLK, NULL, 0), + CLK_DUMMY("dma_bam_pclk", DMA_BAM_P_CLK, NULL, 0), + CLK_DUMMY("mem_clk", NULL, NULL, 0), + CLK_DUMMY("core_clk", SPI_CLK, "spi_qsd.1", OFF), + CLK_DUMMY("iface_clk", SPI_P_CLK, "spi_qsd.1", OFF), + CLK_DUMMY("core_clk", NULL, "f9966000.i2c", 0), + CLK_DUMMY("iface_clk", NULL, "f9966000.i2c", 0), + CLK_DUMMY("core_clk", NULL, "fe12f000.slim", OFF), +}; + +struct clock_init_data msm_dummy_clock_init_data __initdata = { + .table = msm_clocks_dummy, + .size = ARRAY_SIZE(msm_clocks_dummy), +}; + +static struct of_dev_auxdata msm_copper_auxdata_lookup[] __initdata = { + OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \ + "msm_serial_hsl.0", NULL), + OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \ + "msm_otg", NULL), + OF_DEV_AUXDATA("qcom,dwc-usb3-msm", 0xF9200000, \ + "msm_dwc3", NULL), + OF_DEV_AUXDATA("qcom,spi-qup-v2", 0xF9924000, \ + "spi_qsd.1", NULL), + OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \ + "spmi-pmic-arb.0", NULL), + OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \ + "msm_sdcc.1", NULL), + OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \ + "msm_sdcc.2", NULL), + OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9864000, \ + "msm_sdcc.3", NULL), + OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98E4000, \ + "msm_sdcc.4", NULL), + OF_DEV_AUXDATA("qcom,pil-q6v5-lpass", 0xFE200000, \ + "pil-q6v5-lpass", NULL), + OF_DEV_AUXDATA("qcom,pil-q6v5-mss", 0xFC880000, "pil-q6v5-mss", NULL), + OF_DEV_AUXDATA("qcom,pil-mba", 0xFC820000, "pil-mba", NULL), + OF_DEV_AUXDATA("qcom,pil-pronto", 0xFB21B000, \ + "pil_pronto", NULL), + OF_DEV_AUXDATA("qcom,msm-rng", 0xF9BFF000, \ + "msm_rng", NULL), + {} +}; + +void __init msm_copper_init(struct of_dev_auxdata **adata) +{ + msm_copper_init_gpiomux(); + + if (machine_is_copper_rumi()) + msm_clock_init(&msm_dummy_clock_init_data); + else + msm_clock_init(&msmcopper_clock_init_data); + + *adata = msm_copper_auxdata_lookup; + + regulator_has_full_constraints(); +} + +void __init msm_copper_very_early(void) +{ + msm_copper_early_memory(); +} diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c new file mode 100644 index 00000000000..674df0929df --- /dev/null +++ b/arch/arm/mach-msm/board-dt.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init msm_dt_timer_init(void) +{ + arch_timer_of_register(); +} + +static struct sys_timer msm_dt_timer = { + .init = msm_dt_timer_init +}; + +static void __init msm_dt_init_irq(void) +{ + if (machine_is_copper()) + msm_copper_init_irq(); +} + +static void __init msm_dt_map_io(void) +{ + if (early_machine_is_copper()) + msm_map_copper_io(); + if (socinfo_init() < 0) + pr_err("%s: socinfo_init() failed\n", __func__); +} + +static void __init msm_dt_init(void) +{ + struct of_dev_auxdata *adata = NULL; + + if (machine_is_copper()) + msm_copper_init(&adata); + + of_platform_populate(NULL, of_default_bus_match_table, adata, NULL); + if (machine_is_copper()) { + msm_copper_add_devices(); + msm_copper_add_drivers(); + } +} + +static const char *msm_dt_match[] __initconst = { + "qcom,msmcopper", + NULL +}; + +static void __init msm_dt_reserve(void) +{ + if (early_machine_is_copper()) + msm_copper_reserve(); +} + +static void __init msm_dt_init_very_early(void) +{ + if (early_machine_is_copper()) + msm_copper_very_early(); +} + +DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)") + .map_io = msm_dt_map_io, + .init_irq = msm_dt_init_irq, + .init_machine = msm_dt_init, + .handle_irq = gic_handle_irq, + .timer = &msm_dt_timer, + .dt_compat = msm_dt_match, + .nr_irqs = -1, + .reserve = msm_dt_reserve, + .init_very_early = msm_dt_init_very_early, +MACHINE_END diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c new file mode 100644 index 00000000000..6ad3cefe482 --- /dev/null +++ b/arch/arm/mach-msm/board-fsm9xxx.c @@ -0,0 +1,928 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "devices.h" +#include "timer.h" +#include "acpuclock.h" +#include "pm.h" +#include "spm.h" +#include +#include +#include +#include +#include + +#define PMIC_GPIO_INT 144 +#define PMIC_VREG_WLAN_LEVEL 2900 +#define PMIC_GPIO_SD_DET 165 + +#define GPIO_EPHY_RST_N 37 +#define GPIO_MAC_TXD_3 119 +#define GPIO_MAC_TXD_2 120 +#define GPIO_MAC_TXD_1 121 +#define GPIO_MAC_TXD_0 122 +#define GPIO_MAC_TX_EN 123 +#define GPIO_MAC_MDIO 127 +#define GPIO_MAC_MDC 128 +#define GPIO_MAC_TX_CLK 133 +#define GPIO_GRFC_FTR0_0 136 /* GRFC 20 */ +#define GPIO_GRFC_FTR0_1 137 /* GRFC 21 */ +#define GPIO_GRFC_FTR1_0 145 /* GRFC 22 */ +#define GPIO_GRFC_FTR1_1 93 /* GRFC 19 */ +#define GPIO_GRFC_2 110 +#define GPIO_GRFC_3 109 +#define GPIO_GRFC_4 108 +#define GPIO_GRFC_5 107 +#define GPIO_GRFC_6 106 +#define GPIO_GRFC_7 105 +#define GPIO_GRFC_8 104 +#define GPIO_GRFC_9 103 +#define GPIO_GRFC_10 102 +#define GPIO_GRFC_11 101 +#define GPIO_GRFC_13 99 +#define GPIO_GRFC_14 98 +#define GPIO_GRFC_15 97 +#define GPIO_GRFC_16 96 +#define GPIO_GRFC_17 95 +#define GPIO_GRFC_18 94 +#define GPIO_GRFC_24 150 +#define GPIO_GRFC_25 151 +#define GPIO_GRFC_26 152 +#define GPIO_GRFC_27 153 +#define GPIO_GRFC_28 154 +#define GPIO_GRFC_29 155 + +#define GPIO_USER_FIRST 58 +#define GPIO_USER_LAST 63 + +#define FPGA_SDCC_STATUS 0x8E0001A8 + +/* Macros assume PMIC GPIOs start at 0 */ +#define PM8058_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + NR_MSM_GPIOS) +#define PM8058_GPIO_SYS_TO_PM(sys_gpio) (sys_gpio - NR_MSM_GPIOS) +#define PM8058_MPP_BASE (NR_MSM_GPIOS + PM8058_GPIOS) +#define PM8058_MPP_PM_TO_SYS(pm_gpio) (pm_gpio + PM8058_MPP_BASE) +#define PM8058_MPP_SYS_TO_PM(sys_gpio) (sys_gpio - PM8058_MPP_BASE) + +#define PMIC_GPIO_5V_PA_PWR 21 /* PMIC GPIO Number 22 */ +#define PMIC_GPIO_4_2V_PA_PWR 22 /* PMIC GPIO Number 23 */ +#define PMIC_MPP_3 2 /* PMIC MPP Number 3 */ +#define PMIC_MPP_6 5 /* PMIC MPP Number 6 */ +#define PMIC_MPP_7 6 /* PMIC MPP Number 7 */ +#define PMIC_MPP_10 9 /* PMIC MPP Number 10 */ + +/* + * PM8058 + */ +struct pm8xxx_mpp_init_info { + unsigned mpp; + struct pm8xxx_mpp_config_data config; +}; + +#define PM8XXX_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8058_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +static int pm8058_gpios_init(void) +{ + int i; + int rc; + struct pm8058_gpio_cfg { + int gpio; + struct pm_gpio cfg; + }; + + struct pm8058_gpio_cfg gpio_cfgs[] = { + { /* 5V PA Power */ + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_5V_PA_PWR), + { + .vin_sel = 0, + .direction = PM_GPIO_DIR_BOTH, + .output_value = 1, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .pull = PM_GPIO_PULL_DN, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, + { /* 4.2V PA Power */ + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_4_2V_PA_PWR), + { + .vin_sel = 0, + .direction = PM_GPIO_DIR_BOTH, + .output_value = 1, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .pull = PM_GPIO_PULL_DN, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, + }; + + for (i = 0; i < ARRAY_SIZE(gpio_cfgs); ++i) { + rc = pm8xxx_gpio_config(gpio_cfgs[i].gpio, &gpio_cfgs[i].cfg); + if (rc < 0) { + pr_err("%s pmic gpio config failed\n", __func__); + return rc; + } + } + + return 0; +} + +static int pm8058_mpps_init(void) +{ + int rc, i; + + struct pm8xxx_mpp_init_info pm8058_mpps[] = { + PM8XXX_MPP_INIT(PMIC_MPP_3, A_OUTPUT, + PM8XXX_MPP_AOUT_LVL_1V25_2, AOUT_CTRL_ENABLE), + PM8XXX_MPP_INIT(PMIC_MPP_6, A_OUTPUT, + PM8XXX_MPP_AOUT_LVL_1V25_2, AOUT_CTRL_ENABLE), + }; + + for (i = 0; i < ARRAY_SIZE(pm8058_mpps); i++) { + rc = pm8xxx_mpp_config(pm8058_mpps[i].mpp, + &pm8058_mpps[i].config); + if (rc) { + pr_err("%s: Config %d mpp pm 8058 failed\n", + __func__, pm8058_mpps[i].mpp); + return rc; + } + } + + return 0; +} + +static struct regulator_consumer_supply pm8058_vreg_supply[PM8058_VREG_MAX] = { + [PM8058_VREG_ID_L3] = REGULATOR_SUPPLY("8058_l3", NULL), + [PM8058_VREG_ID_L8] = REGULATOR_SUPPLY("8058_l8", NULL), + [PM8058_VREG_ID_L9] = REGULATOR_SUPPLY("8058_l9", NULL), + [PM8058_VREG_ID_L14] = REGULATOR_SUPPLY("8058_l14", NULL), + [PM8058_VREG_ID_L15] = REGULATOR_SUPPLY("8058_l15", NULL), + [PM8058_VREG_ID_L18] = REGULATOR_SUPPLY("8058_l18", NULL), + [PM8058_VREG_ID_S4] = REGULATOR_SUPPLY("8058_s4", NULL), + + [PM8058_VREG_ID_LVS0] = REGULATOR_SUPPLY("8058_lvs0", NULL), +}; + +#define PM8058_VREG_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, \ + _always_on, _pull_down) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = 1, \ + .consumer_supplies = &pm8058_vreg_supply[_id], \ + }, \ + .id = _id, \ + .pull_down_enable = _pull_down, \ + .pin_ctrl = 0, \ + .pin_fn = PM8058_VREG_PIN_FN_ENABLE, \ + } + +#define PM8058_VREG_INIT_LDO(_id, _min_uV, _max_uV) \ + PM8058_VREG_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL | \ + REGULATOR_MODE_IDLE | REGULATOR_MODE_STANDBY, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS | \ + REGULATOR_CHANGE_MODE, 1, 1, 1) + +#define PM8058_VREG_INIT_SMPS(_id, _min_uV, _max_uV) \ + PM8058_VREG_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL | \ + REGULATOR_MODE_IDLE | REGULATOR_MODE_STANDBY, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS | \ + REGULATOR_CHANGE_MODE, 1, 1, 1) + +#define PM8058_VREG_INIT_LVS(_id, _min_uV, _max_uV) \ + PM8058_VREG_INIT(_id, _min_uV, _min_uV, REGULATOR_MODE_NORMAL, \ + REGULATOR_CHANGE_STATUS, 0, 0, 1) + +static struct pm8058_vreg_pdata pm8058_vreg_init[] = { + PM8058_VREG_INIT_LDO(PM8058_VREG_ID_L3, 1800000, 1800000), + PM8058_VREG_INIT_LDO(PM8058_VREG_ID_L8, 2200000, 2200000), + PM8058_VREG_INIT_LDO(PM8058_VREG_ID_L9, 2050000, 2050000), + PM8058_VREG_INIT_LDO(PM8058_VREG_ID_L14, 2850000, 2850000), + PM8058_VREG_INIT_LDO(PM8058_VREG_ID_L15, 2200000, 2200000), + PM8058_VREG_INIT_LDO(PM8058_VREG_ID_L18, 2200000, 2200000), + PM8058_VREG_INIT_LVS(PM8058_VREG_ID_LVS0, 1800000, 1800000), + PM8058_VREG_INIT_SMPS(PM8058_VREG_ID_S4, 1300000, 1300000), +}; + +#ifdef CONFIG_SENSORS_MSM_ADC +static struct adc_access_fn xoadc_fn = { + pm8058_xoadc_select_chan_and_start_conv, + pm8058_xoadc_read_adc_code, + pm8058_xoadc_get_properties, + pm8058_xoadc_slot_request, + pm8058_xoadc_restore_slot, + pm8058_xoadc_calibrate, +}; + +static struct msm_adc_channels msm_adc_channels_data[] = { + {"pmic_therm", CHANNEL_ADC_DIE_TEMP, 0, &xoadc_fn, CHAN_PATH_TYPE12, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE1, scale_pmic_therm}, + {"ref_1250mv", CHANNEL_ADC_1250_REF, 0, &xoadc_fn, CHAN_PATH_TYPE13, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_default}, + {"xo_therm", CHANNEL_ADC_XOTHERM, 0, &xoadc_fn, CHAN_PATH_TYPE_NONE, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE5, tdkntcgtherm}, + {"fsm_therm", CHANNEL_ADC_FSM_THERM, 0, &xoadc_fn, CHAN_PATH_TYPE6, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE5, tdkntcgtherm}, + {"pa_therm", CHANNEL_ADC_PA_THERM, 0, &xoadc_fn, CHAN_PATH_TYPE7, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE5, tdkntcgtherm}, +}; + +static struct msm_adc_platform_data msm_adc_pdata = { + .channel = msm_adc_channels_data, + .num_chan_supported = ARRAY_SIZE(msm_adc_channels_data), + .target_hw = FSM_9xxx, +}; + +static struct platform_device msm_adc_device = { + .name = "msm_adc", + .id = -1, + .dev = { + .platform_data = &msm_adc_pdata, + }, +}; + +static void pmic8058_xoadc_mpp_config(void) +{ + int rc, i; + struct pm8xxx_mpp_init_info xoadc_mpps[] = { + PM8XXX_MPP_INIT(PMIC_MPP_7, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH5, + AOUT_CTRL_DISABLE), + PM8XXX_MPP_INIT(PMIC_MPP_10, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH6, + AOUT_CTRL_DISABLE), + }; + for (i = 0; i < ARRAY_SIZE(xoadc_mpps); i++) { + rc = pm8xxx_mpp_config(xoadc_mpps[i].mpp, + &xoadc_mpps[i].config); + if (rc) { + pr_err("%s: Config MPP %d of PM8058 failed\n", + __func__, xoadc_mpps[i].mpp); + } + } +} + +static struct regulator *vreg_ldo18_adc; + +static int pmic8058_xoadc_vreg_config(int on) +{ + int rc; + + if (on) { + rc = regulator_enable(vreg_ldo18_adc); + if (rc) + pr_err("%s: Enable of regulator ldo18_adc " + "failed\n", __func__); + } else { + rc = regulator_disable(vreg_ldo18_adc); + if (rc) + pr_err("%s: Disable of regulator ldo18_adc " + "failed\n", __func__); + } + + return rc; +} + +static int pmic8058_xoadc_vreg_setup(void) +{ + int rc; + + vreg_ldo18_adc = regulator_get(NULL, "8058_l18"); + if (IS_ERR(vreg_ldo18_adc)) { + pr_err("%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_ldo18_adc)); + rc = PTR_ERR(vreg_ldo18_adc); + goto fail; + } + + rc = regulator_set_voltage(vreg_ldo18_adc, 2200000, 2200000); + if (rc) { + pr_err("%s: unable to set ldo18 voltage to 2.2V\n", __func__); + goto fail; + } + + return rc; +fail: + regulator_put(vreg_ldo18_adc); + return rc; +} + +static void pmic8058_xoadc_vreg_shutdown(void) +{ + regulator_put(vreg_ldo18_adc); +} + +/* usec. For this ADC, + * this time represents clk rate @ txco w/ 1024 decimation ratio. + * Each channel has different configuration, thus at the time of starting + * the conversion, xoadc will return actual conversion time + * */ +static struct adc_properties pm8058_xoadc_data = { + .adc_reference = 2200, /* milli-voltage for this adc */ + .bitresolution = 15, + .bipolar = 0, + .conversiontime = 54, +}; + +static struct xoadc_platform_data pm8058_xoadc_pdata = { + .xoadc_prop = &pm8058_xoadc_data, + .xoadc_mpp_config = pmic8058_xoadc_mpp_config, + .xoadc_vreg_set = pmic8058_xoadc_vreg_config, + .xoadc_num = XOADC_PMIC_0, + .xoadc_vreg_setup = pmic8058_xoadc_vreg_setup, + .xoadc_vreg_shutdown = pmic8058_xoadc_vreg_shutdown, +}; +#endif + +#define XO_CONSUMERS(_id) \ + static struct regulator_consumer_supply xo_consumers_##_id[] + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +XO_CONSUMERS(A0) = { + REGULATOR_SUPPLY("8058_xo_a0", NULL), + REGULATOR_SUPPLY("a0_clk_buffer", "fsm_xo_driver"), +}; +XO_CONSUMERS(A1) = { + REGULATOR_SUPPLY("8058_xo_a1", NULL), + REGULATOR_SUPPLY("a1_clk_buffer", "fsm_xo_driver"), +}; + +#define PM8058_XO_INIT(_id, _modes, _ops, _always_on) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .boot_on = 1, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(xo_consumers_##_id),\ + .consumer_supplies = xo_consumers_##_id, \ + }, \ + .id = PM8058_XO_ID_##_id, \ + } + +#define PM8058_XO_INIT_AX(_id) \ + PM8058_XO_INIT(_id, REGULATOR_MODE_NORMAL, REGULATOR_CHANGE_STATUS, 0) + +static struct pm8058_xo_pdata pm8058_xo_init_pdata[] = { + PM8058_XO_INIT_AX(A0), + PM8058_XO_INIT_AX(A1), +}; + +#define PM8058_GPIO_INT 47 + +static struct pm8xxx_irq_platform_data pm8xxx_irq_pdata = { + .irq_base = PMIC8058_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(PM8058_GPIO_INT), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_gpio_platform_data pm8xxx_gpio_pdata = { + .gpio_base = PM8058_GPIO_PM_TO_SYS(0), +}; + +static struct pm8xxx_mpp_platform_data pm8xxx_mpp_pdata = { + .mpp_base = PM8058_MPP_PM_TO_SYS(0), +}; + +static struct pm8058_platform_data pm8058_fsm9xxx_data = { + .irq_pdata = &pm8xxx_irq_pdata, + .gpio_pdata = &pm8xxx_gpio_pdata, + .mpp_pdata = &pm8xxx_mpp_pdata, + .regulator_pdatas = pm8058_vreg_init, + .num_regulators = ARRAY_SIZE(pm8058_vreg_init), + .xo_buffer_pdata = pm8058_xo_init_pdata, + .num_xo_buffers = ARRAY_SIZE(pm8058_xo_init_pdata), +#ifdef CONFIG_SENSORS_MSM_ADC + .xoadc_pdata = &pm8058_xoadc_pdata, +#endif +}; + +#ifdef CONFIG_MSM_SSBI +static struct msm_ssbi_platform_data fsm9xxx_ssbi_pm8058_pdata = { + .controller_type = FSM_SBI_CTRL_SSBI, + .slave = { + .name = "pm8058-core", + .platform_data = &pm8058_fsm9xxx_data, + }, +}; +#endif + +static int __init buses_init(void) +{ + if (gpio_tlmm_config(GPIO_CFG(PMIC_GPIO_INT, 5, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE)) + pr_err("%s: gpio_tlmm_config (gpio=%d) failed\n", + __func__, PMIC_GPIO_INT); + + return 0; +} + +/* + * EPHY + */ + +static struct msm_gpio phy_config_data[] = { + { GPIO_CFG(GPIO_EPHY_RST_N, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_RST_N" }, + { GPIO_CFG(GPIO_MAC_TXD_3, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_3"}, + { GPIO_CFG(GPIO_MAC_TXD_2, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_2"}, + { GPIO_CFG(GPIO_MAC_TXD_1, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_1"}, + { GPIO_CFG(GPIO_MAC_TXD_0, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_0"}, + { GPIO_CFG(GPIO_MAC_TX_EN, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TX_EN"}, + { GPIO_CFG(GPIO_MAC_TX_CLK, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_10MA), "MAC_TX_CLK"}, + { GPIO_CFG(GPIO_MAC_MDIO, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "MDIO_MAC_MDIO"}, + { GPIO_CFG(GPIO_MAC_MDC, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "MDC_MAC_MDC"}, +}; + +static int __init phy_init(void) +{ + msm_gpios_request_enable(phy_config_data, ARRAY_SIZE(phy_config_data)); + gpio_direction_output(GPIO_EPHY_RST_N, 0); + udelay(100); + gpio_set_value(GPIO_EPHY_RST_N, 1); + + return 0; +} + +/* + * RF + */ + +static struct msm_gpio grfc_config_data[] = { + { GPIO_CFG(GPIO_GRFC_FTR0_0, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "HH_RFMODE1_0" }, + { GPIO_CFG(GPIO_GRFC_FTR0_1, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "HH_RFMODE1_1" }, + { GPIO_CFG(GPIO_GRFC_FTR1_0, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "HH_RFMODE2_0" }, + { GPIO_CFG(GPIO_GRFC_FTR1_1, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "HH_RFMODE2_1" }, + { GPIO_CFG(GPIO_GRFC_2, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_2" }, + { GPIO_CFG(GPIO_GRFC_3, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_3" }, + { GPIO_CFG(GPIO_GRFC_4, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_4" }, + { GPIO_CFG(GPIO_GRFC_5, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_5" }, + { GPIO_CFG(GPIO_GRFC_6, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_6" }, + { GPIO_CFG(GPIO_GRFC_7, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_7" }, + { GPIO_CFG(GPIO_GRFC_8, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_8" }, + { GPIO_CFG(GPIO_GRFC_9, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_9" }, + { GPIO_CFG(GPIO_GRFC_10, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_10" }, + { GPIO_CFG(GPIO_GRFC_11, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_11" }, + { GPIO_CFG(GPIO_GRFC_13, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_13" }, + { GPIO_CFG(GPIO_GRFC_14, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_14" }, + { GPIO_CFG(GPIO_GRFC_15, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_15" }, + { GPIO_CFG(GPIO_GRFC_16, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_16" }, + { GPIO_CFG(GPIO_GRFC_17, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_17" }, + { GPIO_CFG(GPIO_GRFC_18, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_18" }, + { GPIO_CFG(GPIO_GRFC_24, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_24" }, + { GPIO_CFG(GPIO_GRFC_25, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_25" }, + { GPIO_CFG(GPIO_GRFC_26, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_26" }, + { GPIO_CFG(GPIO_GRFC_27, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_27" }, + { GPIO_CFG(GPIO_GRFC_28, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_28" }, + { GPIO_CFG(GPIO_GRFC_29, 7, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), "GPIO_GRFC_29" }, + { GPIO_CFG(39, 1, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "PP2S_EXT_SYNC" }, +}; + +static int __init grfc_init(void) +{ + msm_gpios_request_enable(grfc_config_data, + ARRAY_SIZE(grfc_config_data)); + + return 0; +} + +/* + * UART + */ + +#ifdef CONFIG_SERIAL_MSM_CONSOLE +static struct msm_gpio uart1_config_data[] = { + { GPIO_CFG(138, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1_Rx" }, + { GPIO_CFG(139, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1_Tx" }, +}; + +static void fsm9xxx_init_uart1(void) +{ + msm_gpios_request_enable(uart1_config_data, + ARRAY_SIZE(uart1_config_data)); + +} +#endif + +/* + * SSBI + */ + +#ifdef CONFIG_I2C_SSBI +static struct msm_i2c_ssbi_platform_data msm_i2c_ssbi2_pdata = { + .controller_type = FSM_SBI_CTRL_SSBI, +}; + +static struct msm_i2c_ssbi_platform_data msm_i2c_ssbi3_pdata = { + .controller_type = FSM_SBI_CTRL_SSBI, +}; +#endif + +#if defined(CONFIG_I2C_SSBI) || defined(CONFIG_MSM_SSBI) +/* Intialize GPIO configuration for SSBI */ +static struct msm_gpio ssbi_gpio_config_data[] = { + { GPIO_CFG(140, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), + "SSBI_1" }, + { GPIO_CFG(141, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), + "SSBI_2" }, + { GPIO_CFG(92, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_4MA), + "SSBI_3" }, +}; + +static void +fsm9xxx_init_ssbi_gpio(void) +{ + msm_gpios_request_enable(ssbi_gpio_config_data, + ARRAY_SIZE(ssbi_gpio_config_data)); + +} +#endif + +/* + * User GPIOs + */ + +static void user_gpios_init(void) +{ + unsigned int gpio; + + for (gpio = GPIO_USER_FIRST; gpio <= GPIO_USER_LAST; ++gpio) + gpio_tlmm_config(GPIO_CFG(gpio, 0, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE); +} + +/* + * Crypto + */ + +#define QCE_SIZE 0x10000 + +#define QCE_0_BASE 0x80C00000 +#define QCE_1_BASE 0x80E00000 +#define QCE_2_BASE 0x81000000 + +#define QCE_NO_HW_KEY_SUPPORT 0 /* No shared HW key with external */ +#define QCE_NO_SHARE_CE_RESOURCE 0 /* No CE resource shared with TZ */ +#define QCE_NO_CE_SHARED 0 /* CE not shared with TZ */ +#define QCE_NO_SHA_HMAC_SUPPORT 0 /* No SHA-HMAC by SHA operation */ + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE1_IN_CHAN, + .end = DMOV_CE1_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE1_IN_CRCI, + .end = DMOV_CE1_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE1_OUT_CRCI, + .end = DMOV_CE1_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE1_HASH_CRCI, + .end = DMOV_CE1_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_NO_CE_SHARED, + .shared_ce_resource = QCE_NO_SHARE_CE_RESOURCE, + .hw_key_support = QCE_NO_HW_KEY_SUPPORT, + .sha_hmac = QCE_NO_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +struct platform_device qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE1_IN_CHAN, + .end = DMOV_CE1_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE1_IN_CRCI, + .end = DMOV_CE1_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE1_OUT_CRCI, + .end = DMOV_CE1_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE1_HASH_CRCI, + .end = DMOV_CE1_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_NO_CE_SHARED, + .shared_ce_resource = QCE_NO_SHARE_CE_RESOURCE, + .hw_key_support = QCE_NO_HW_KEY_SUPPORT, + .sha_hmac = QCE_NO_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +static struct platform_device qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; + +static struct resource ota_qcrypto_resources[] = { + [0] = { + .start = QCE_1_BASE, + .end = QCE_1_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE2_IN_CHAN, + .end = DMOV_CE2_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE2_IN_CRCI, + .end = DMOV_CE2_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE2_OUT_CRCI, + .end = DMOV_CE2_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE2_HASH_CRCI, + .end = DMOV_CE2_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device ota_qcrypto_device = { + .name = "qcota", + .id = 0, + .num_resources = ARRAY_SIZE(ota_qcrypto_resources), + .resource = ota_qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +/* + * Devices + */ + +static struct platform_device *devices[] __initdata = { + &msm_device_smd, + &msm_device_dmov, + &msm_device_nand, +#ifdef CONFIG_MSM_SSBI + &msm_device_ssbi_pmic1, +#endif +#ifdef CONFIG_I2C_SSBI + &msm_device_ssbi2, + &msm_device_ssbi3, +#endif +#ifdef CONFIG_SENSORS_MSM_ADC + &msm_adc_device, +#endif +#ifdef CONFIG_I2C_QUP + &msm_gsbi1_qup_i2c_device, +#endif +#if defined(CONFIG_SERIAL_MSM) || defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart1, +#endif +#if defined(CONFIG_QFP_FUSE) + &fsm_qfp_fuse_device, +#endif + &qfec_device, + &qcrypto_device, + &qcedev_device, + &ota_qcrypto_device, + &fsm_xo_device, + &fsm9xxx_device_watchdog, +}; + +static void __init fsm9xxx_init_irq(void) +{ + msm_init_irq(); + msm_init_sirc(); +} + +#ifdef CONFIG_MSM_SPM +static struct msm_spm_platform_data msm_spm_data __initdata = { + .reg_base_addr = MSM_SAW_BASE, + + .reg_init_values[MSM_SPM_REG_SAW_CFG] = 0x05, + .reg_init_values[MSM_SPM_REG_SAW_SPM_CTL] = 0x18, + .reg_init_values[MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0x00006666, + .reg_init_values[MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0xFF000666, + + .reg_init_values[MSM_SPM_REG_SAW_SPM_PMIC_CTL] = 0xE0F272, + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x03, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x00, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_RST_EN] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x00, + + .awake_vlevel = 0xF2, + .retention_vlevel = 0xE0, + .collapse_vlevel = 0x72, + .retention_mid_vlevel = 0xE0, + .collapse_mid_vlevel = 0xE0, +}; +#endif + +static void __init fsm9xxx_init(void) +{ + acpuclk_init(&acpuclk_9xxx_soc_data); + + regulator_has_full_constraints(); + +#if defined(CONFIG_I2C_SSBI) || defined(CONFIG_MSM_SSBI) + fsm9xxx_init_ssbi_gpio(); +#endif +#ifdef CONFIG_MSM_SSBI + msm_device_ssbi_pmic1.dev.platform_data = + &fsm9xxx_ssbi_pm8058_pdata; +#endif + buses_init(); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + +#ifdef CONFIG_MSM_SPM + msm_spm_init(&msm_spm_data, 1); +#endif + pm8058_gpios_init(); + pm8058_mpps_init(); + phy_init(); + grfc_init(); + user_gpios_init(); + +#ifdef CONFIG_SERIAL_MSM_CONSOLE + fsm9xxx_init_uart1(); +#endif +#ifdef CONFIG_I2C_SSBI + msm_device_ssbi2.dev.platform_data = &msm_i2c_ssbi2_pdata; + msm_device_ssbi3.dev.platform_data = &msm_i2c_ssbi3_pdata; +#endif +} + +static void __init fsm9xxx_map_io(void) +{ + msm_shared_ram_phys = 0x00100000; + msm_map_fsm9xxx_io(); + msm_clock_init(&fsm9xxx_clock_init_data); + if (socinfo_init() < 0) + pr_err("%s: socinfo_init() failed!\n", + __func__); + +} + +MACHINE_START(FSM9XXX_SURF, "QCT FSM9XXX") + .atag_offset = 0x100, + .map_io = fsm9xxx_map_io, + .init_irq = fsm9xxx_init_irq, + .handle_irq = vic_handle_irq, + .init_machine = fsm9xxx_init, + .timer = &msm_timer, + .restart = fsm_restart, +MACHINE_END diff --git a/arch/arm/mach-msm/board-halibut-keypad.c b/arch/arm/mach-msm/board-halibut-keypad.c new file mode 100644 index 00000000000..49c1075627d --- /dev/null +++ b/arch/arm/mach-msm/board-halibut-keypad.c @@ -0,0 +1,177 @@ +/* linux/arch/arm/mach-msm/board-halibut-keypad.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_halibut." +static int halibut_ffa; +module_param_named(ffa, halibut_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */ + +static unsigned int halibut_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int halibut_col_gpios[] = { 36, 37, 38, 39, 40 }; + +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(halibut_col_gpios) + (col)) + +static const unsigned short halibut_keymap[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static const unsigned short halibut_keymap_ffa[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +static struct gpio_event_matrix_info halibut_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = halibut_keymap, + .output_gpios = halibut_row_gpios, + .input_gpios = halibut_col_gpios, + .noutputs = ARRAY_SIZE(halibut_row_gpios), + .ninputs = ARRAY_SIZE(halibut_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +struct gpio_event_info *halibut_keypad_info[] = { + &halibut_matrix_info.info +}; + +static struct gpio_event_platform_data halibut_keypad_data = { + .name = "halibut_keypad", + .info = halibut_keypad_info, + .info_count = ARRAY_SIZE(halibut_keypad_info) +}; + +static struct platform_device halibut_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &halibut_keypad_data, + }, +}; + +static int __init halibut_init_keypad(void) +{ + if (!machine_is_halibut()) + return 0; + if (halibut_ffa) + halibut_matrix_info.keymap = halibut_keymap_ffa; + return platform_device_register(&halibut_keypad_device); +} + +device_initcall(halibut_init_keypad); diff --git a/arch/arm/mach-msm/board-halibut-panel.c b/arch/arm/mach-msm/board-halibut-panel.c new file mode 100644 index 00000000000..2a6a6ed0204 --- /dev/null +++ b/arch/arm/mach-msm/board-halibut-panel.c @@ -0,0 +1,73 @@ +/* linux/arch/arm/mach-msm/board-halibut-mddi.c +** Author: Brian Swetland +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "devices.h" +#include "board-halibut.h" + +static void halibut_mddi_power_client(struct msm_mddi_client_data *mddi, + int on) +{ +} + +static struct resource resources_msm_fb = { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct msm_fb_data fb_data = { + .xres = 800, + .yres = 480, + .output_format = 0, +}; + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 122880000, + .power_client = halibut_mddi_power_client, + .fb_resource = &resources_msm_fb, + .num_clients = 1, + .client_platform_data = { + { + .product_id = (0x4474 << 16 | 0xc065), + .name = "mddi_c_dummy", + .id = 0, + .client_data = &fb_data, + .clk_rate = 0, + }, + }, +}; + +int __init halibut_init_panel(void) +{ + int rc; + + if (!machine_is_halibut()) + return 0; + + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + + msm_device_mddi0.dev.platform_data = &mddi_pdata; + return platform_device_register(&msm_device_mddi0); +} + +device_initcall(halibut_init_panel); diff --git a/arch/arm/mach-msm/board-halibut.h b/arch/arm/mach-msm/board-halibut.h new file mode 100644 index 00000000000..edcdacb34c2 --- /dev/null +++ b/arch/arm/mach-msm/board-halibut.h @@ -0,0 +1,20 @@ +/* linux/arch/arm/mach-msm/board-trout.h + * ** Author: Brian Swetland + * */ +#ifndef __ARCH_ARM_MACH_MSM_BOARD_HALIBUT_H +#define __ARCH_ARM_MACH_MSM_BOARD_HALIBUT_H + +#define MSM_PMEM_GPU0_BASE (0x10000000 + 64*SZ_1M) +#define MSM_PMEM_GPU0_SIZE 0x800000 +#define MSM_PMEM_MDP_BASE (MSM_PMEM_GPU0_BASE + MSM_PMEM_GPU0_SIZE) +#define MSM_PMEM_MDP_SIZE 0x800000 +#define MSM_PMEM_ADSP_BASE (MSM_PMEM_MDP_BASE + MSM_PMEM_MDP_SIZE) +#define MSM_PMEM_ADSP_SIZE 0x800000 +#define MSM_PMEM_GPU1_BASE (MSM_PMEM_ADSP_BASE + MSM_PMEM_ADSP_SIZE) +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_FB_BASE (MSM_PMEM_GPU1_BASE + MSM_PMEM_GPU1_SIZE) +#define MSM_FB_SIZE 0x200000 +#define MSM_PMEM_CAMERA_BASE (MSM_FB_BASE + MSM_FB_SIZE) +#define MSM_PMEM_CAMERA_SIZE 0xA00000 + +#endif diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c new file mode 100644 index 00000000000..523d187794c --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -0,0 +1,283 @@ +/* arch/arm/mach-msm/board-mahimahi-audio.c + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 Google Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "board-mahimahi.h" +#include "pmic.h" +#include "board-mahimahi-tpa2018d1.h" + +#if 0 +#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +static struct mutex mic_lock; +static struct mutex bt_sco_lock; + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_HEADSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1500, + .max_gain = 0, + }, + [Q6_HW_TTY] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 0, + }, +}; + +void mahimahi_headset_enable(int en) +{ + D("%s %d\n", __func__, en); + /* enable audio amp */ + if (en) mdelay(15); + gpio_set_value(MAHIMAHI_AUD_JACKHP_EN, !!en); +} + +void mahimahi_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 0; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + pmic_spkr_en(RIGHT_SPKR, 0); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + + if (is_cdma_version(system_rev)) + tpa2018d1_set_speaker_amp(en); +} + +void mahimahi_receiver_enable(int en) +{ + if (is_cdma_version(system_rev) && + ((system_rev == 0xC1) || (system_rev == 0xC2))) { + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 0; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + } +} + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static uint32_t bt_sco_enable[] = { + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 1, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_IN, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_SYNC, 2, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_CLK, 2, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +static uint32_t bt_sco_disable[] = { + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_IN, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_SYNC, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_CLK, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +void mahimahi_bt_sco_enable(int en) +{ + static int bt_sco_refcount; + D("%s %d\n", __func__, en); + + mutex_lock(&bt_sco_lock); + if (en) { + if (++bt_sco_refcount == 1) + config_gpio_table(bt_sco_enable, + ARRAY_SIZE(bt_sco_enable)); + } else { + if (--bt_sco_refcount == 0) { + config_gpio_table(bt_sco_disable, + ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(MAHIMAHI_BT_PCM_OUT, 0); + } + } + mutex_unlock(&bt_sco_lock); +} + +void mahimahi_mic_enable(int en) +{ + static int old_state = 0, new_state = 0; + + D("%s %d\n", __func__, en); + + mutex_lock(&mic_lock); + if (!!en) + new_state++; + else + new_state--; + + if (new_state == 1 && old_state == 0) { + gpio_set_value(MAHIMAHI_AUD_2V5_EN, 1); + mdelay(60); + } else if (new_state == 0 && old_state == 1) + gpio_set_value(MAHIMAHI_AUD_2V5_EN, 0); + else + D("%s: do nothing %d %d\n", __func__, old_state, new_state); + + old_state = new_state; + mutex_unlock(&mic_lock); +} + +void mahimahi_analog_init(void) +{ + D("%s\n", __func__); + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_en_right_chan(OFF_CMD); + pmic_spkr_en_left_chan(OFF_CMD); + pmic_spkr_add_right_left_chan(OFF_CMD); + pmic_spkr_en_stereo(OFF_CMD); + pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD); + pmic_spkr_bypass_mux(OFF_CMD); + pmic_spkr_en_hpf(ON_CMD); + pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD); + pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ); + pmic_mic_set_volt(MIC_VOLT_1_80V); + + gpio_request(MAHIMAHI_AUD_JACKHP_EN, "aud_jackhp_en"); + gpio_request(MAHIMAHI_BT_PCM_OUT, "bt_pcm_out"); + + gpio_direction_output(MAHIMAHI_AUD_JACKHP_EN, 0); + + mutex_lock(&bt_sco_lock); + config_gpio_table(bt_sco_disable, + ARRAY_SIZE(bt_sco_disable)); + gpio_direction_output(MAHIMAHI_BT_PCM_OUT, 0); + mutex_unlock(&bt_sco_lock); +} + +int mahimahi_get_rx_vol(uint8_t hw, int level) +{ + int vol; + + if (level > 100) + level = 100; + else if (level < 0) + level = 0; + + if (is_cdma_version(system_rev) && hw == Q6_HW_HANDSET) { + int handset_volume[6] = { -1600, -1300, -1000, -600, -300, 0 }; + vol = handset_volume[5 * level / 100]; + } else { + struct q6_hw_info *info; + info = &q6_audio_hw[hw]; + vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100; + } + + D("%s %d\n", __func__, vol); + return vol; +} + +static struct qsd_acoustic_ops acoustic = { + .enable_mic_bias = mahimahi_mic_enable, +}; + +static struct q6audio_analog_ops ops = { + .init = mahimahi_analog_init, + .speaker_enable = mahimahi_speaker_enable, + .headset_enable = mahimahi_headset_enable, + .receiver_enable = mahimahi_receiver_enable, + .bt_sco_enable = mahimahi_bt_sco_enable, + .int_mic_enable = mahimahi_mic_enable, + .ext_mic_enable = mahimahi_mic_enable, + .get_rx_vol = mahimahi_get_rx_vol, +}; + +void __init mahimahi_audio_init(void) +{ + mutex_init(&mic_lock); + mutex_init(&bt_sco_lock); + q6audio_register_analog_ops(&ops); + acoustic_register_ops(&acoustic); + if (is_cdma_version(system_rev) && + ((system_rev == 0xC1) || (system_rev == 0xC2))) + q6audio_set_acdb_file("default_PMIC.acdb"); +} diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.c b/arch/arm/mach-msm/board-mahimahi-flashlight.c new file mode 100644 index 00000000000..829b1f11cf2 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-flashlight.c @@ -0,0 +1,279 @@ +/* + * arch/arm/mach-msm/flashlight.c - flashlight driver + * + * Copyright (C) 2009 zion huang + * + * 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. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi-flashlight.h" + +struct flashlight_struct { + struct led_classdev fl_lcdev; + struct early_suspend early_suspend_flashlight; + spinlock_t spin_lock; + struct hrtimer timer; + int brightness; + int gpio_torch; + int gpio_flash; + int flash_duration_ms; +}; + +static struct flashlight_struct the_fl; + +static inline void toggle(void) +{ + gpio_direction_output(the_fl.gpio_torch, 0); + udelay(2); + gpio_direction_output(the_fl.gpio_torch, 1); + udelay(2); +} + +static void flashlight_hw_command(uint8_t addr, uint8_t data) +{ + int i; + + for (i = 0; i < addr + 17; i++) + toggle(); + udelay(500); + + for (i = 0; i < data; i++) + toggle(); + udelay(500); +} + +static enum hrtimer_restart flashlight_timeout(struct hrtimer *timer) +{ + unsigned long flags; + + pr_debug("%s\n", __func__); + + spin_lock_irqsave(&the_fl.spin_lock, flags); + gpio_direction_output(the_fl.gpio_flash, 0); + the_fl.brightness = LED_OFF; + spin_unlock_irqrestore(&the_fl.spin_lock, flags); + + return HRTIMER_NORESTART; +} + +int flashlight_control(int mode) +{ + int ret = 0; + unsigned long flags; + + pr_debug("%s: mode %d -> %d\n", __func__, + the_fl.brightness, mode); + + spin_lock_irqsave(&the_fl.spin_lock, flags); + + the_fl.brightness = mode; + + switch (mode) { + case FLASHLIGHT_TORCH: + pr_info("%s: half\n", __func__); + /* If we are transitioning from flash to torch, make sure to + * cancel the flash timeout timer, otherwise when it expires, + * the torch will go off as well. + */ + hrtimer_cancel(&the_fl.timer); + flashlight_hw_command(2, 4); + break; + + case FLASHLIGHT_FLASH: + pr_info("%s: full\n", __func__); + hrtimer_cancel(&the_fl.timer); + gpio_direction_output(the_fl.gpio_flash, 0); + udelay(40); + gpio_direction_output(the_fl.gpio_flash, 1); + hrtimer_start(&the_fl.timer, + ktime_set(the_fl.flash_duration_ms / 1000, + (the_fl.flash_duration_ms % 1000) * + NSEC_PER_MSEC), + HRTIMER_MODE_REL); + /* Flash overrides torch mode, and after the flash period, the + * flash LED will turn off. + */ + mode = LED_OFF; + break; + + case FLASHLIGHT_OFF: + pr_info("%s: off\n", __func__); + gpio_direction_output(the_fl.gpio_flash, 0); + gpio_direction_output(the_fl.gpio_torch, 0); + break; + + default: + pr_err("%s: unknown flash_light flags: %d\n", __func__, mode); + ret = -EINVAL; + goto done; + } + +done: + spin_unlock_irqrestore(&the_fl.spin_lock, flags); + return ret; +} +EXPORT_SYMBOL(flashlight_control); + +static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int level; + switch (brightness) { + case LED_HALF: + level = FLASHLIGHT_TORCH; + break; + case LED_FULL: + level = FLASHLIGHT_FLASH; + break; + case LED_OFF: + default: + level = FLASHLIGHT_OFF; + }; + + flashlight_control(level); +} + +static void flashlight_early_suspend(struct early_suspend *handler) +{ + flashlight_control(FLASHLIGHT_OFF); +} + +static int flashlight_setup_gpio(struct flashlight_platform_data *fl_pdata) +{ + int ret; + + pr_debug("%s\n", __func__); + + if (fl_pdata->gpio_init) { + ret = fl_pdata->gpio_init(); + if (ret < 0) { + pr_err("%s: gpio init failed: %d\n", __func__, + ret); + return ret; + } + } + + if (fl_pdata->torch) { + ret = gpio_request(fl_pdata->torch, "flashlight_torch"); + if (ret < 0) { + pr_err("%s: gpio_request failed\n", __func__); + return ret; + } + } + + if (fl_pdata->flash) { + ret = gpio_request(fl_pdata->flash, "flashlight_flash"); + if (ret < 0) { + pr_err("%s: gpio_request failed\n", __func__); + gpio_free(fl_pdata->torch); + return ret; + } + } + + the_fl.gpio_torch = fl_pdata->torch; + the_fl.gpio_flash = fl_pdata->flash; + the_fl.flash_duration_ms = fl_pdata->flash_duration_ms; + return 0; +} + +static int flashlight_probe(struct platform_device *pdev) +{ + struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data; + int err = 0; + + pr_debug("%s\n", __func__); + + err = flashlight_setup_gpio(fl_pdata); + if (err < 0) { + pr_err("%s: setup GPIO failed\n", __func__); + goto fail_free_mem; + } + + spin_lock_init(&the_fl.spin_lock); + the_fl.fl_lcdev.name = pdev->name; + the_fl.fl_lcdev.brightness_set = fl_lcdev_brightness_set; + the_fl.fl_lcdev.brightness = LED_OFF; + err = led_classdev_register(&pdev->dev, &the_fl.fl_lcdev); + if (err < 0) { + pr_err("failed on led_classdev_register\n"); + goto fail_free_gpio; + } + + hrtimer_init(&the_fl.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + the_fl.timer.function = flashlight_timeout; + +#ifdef CONFIG_HAS_EARLYSUSPEND + the_fl.early_suspend_flashlight.suspend = flashlight_early_suspend; + the_fl.early_suspend_flashlight.resume = NULL; + register_early_suspend(&the_fl.early_suspend_flashlight); +#endif + + return 0; + +fail_free_gpio: + if (fl_pdata->torch) + gpio_free(fl_pdata->torch); + if (fl_pdata->flash) + gpio_free(fl_pdata->flash); +fail_free_mem: + return err; +} + +static int flashlight_remove(struct platform_device *pdev) +{ + struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data; + + pr_debug("%s\n", __func__); + + hrtimer_cancel(&the_fl.timer); + unregister_early_suspend(&the_fl.early_suspend_flashlight); + flashlight_control(FLASHLIGHT_OFF); + led_classdev_unregister(&the_fl.fl_lcdev); + if (fl_pdata->torch) + gpio_free(fl_pdata->torch); + if (fl_pdata->flash) + gpio_free(fl_pdata->flash); + return 0; +} + +static struct platform_driver flashlight_driver = { + .probe = flashlight_probe, + .remove = flashlight_remove, + .driver = { + .name = FLASHLIGHT_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init flashlight_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&flashlight_driver); +} + +static void __exit flashlight_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&flashlight_driver); +} + +module_init(flashlight_init); +module_exit(flashlight_exit); + +MODULE_AUTHOR("Zion Huang "); +MODULE_DESCRIPTION("flash light driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.h b/arch/arm/mach-msm/board-mahimahi-flashlight.h new file mode 100644 index 00000000000..93b4095edaa --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-flashlight.h @@ -0,0 +1,20 @@ +#ifndef __ASM_ARM_ARCH_FLASHLIGHT_H +#define __ASM_ARM_ARCH_FLASHLIGHT_H + +#define FLASHLIGHT_NAME "flashlight" + +#define FLASHLIGHT_OFF 0 +#define FLASHLIGHT_TORCH 1 +#define FLASHLIGHT_FLASH 2 +#define FLASHLIGHT_NUM 3 + +struct flashlight_platform_data { + int (*gpio_init) (void); + int torch; + int flash; + int flash_duration_ms; +}; + +int flashlight_control(int level); + +#endif /*__ASM_ARM_ARCH_FLASHLIGHT_H*/ diff --git a/arch/arm/mach-msm/board-mahimahi-keypad.c b/arch/arm/mach-msm/board-mahimahi-keypad.c new file mode 100644 index 00000000000..ab38847d15e --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-keypad.c @@ -0,0 +1,265 @@ +/* arch/arm/mach-msm/board-mahimahi-keypad.c + * + * Copyright (C) 2009 Google, Inc + * Copyright (C) 2009 HTC Corporation. + * + * Author: Dima Zavin + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "board-mahimahi.h" + +struct jog_axis_info { + struct gpio_event_axis_info info; + uint16_t in_state; + uint16_t out_state; +}; + +static struct vreg *jog_vreg; +static bool jog_just_on; +static unsigned long jog_on_jiffies; + +static unsigned int mahimahi_col_gpios[] = { 33, 32, 31 }; +static unsigned int mahimahi_row_gpios[] = { 42, 41, 40 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(mahimahi_row_gpios) + (row)) +#define KEYMAP_SIZE (ARRAY_SIZE(mahimahi_col_gpios) * \ + ARRAY_SIZE(mahimahi_row_gpios)) + +/* keypad */ +static const unsigned short mahimahi_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE), +}; + +static const unsigned short mahimahi_cdma_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE), + + /* Key (2, 2) is not a physical key on mahimahi. The purpose of + * registering the unused matrix key as a dummy key is to make + * userland able to send/receive the key event for some requested tests + * in lab. of some CDMA carriers (e.g. Verizon). + */ + [KEYMAP_INDEX(2, 2)] = KEY_END, +}; + +static struct gpio_event_matrix_info mahimahi_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = mahimahi_keymap, + .output_gpios = mahimahi_col_gpios, + .input_gpios = mahimahi_row_gpios, + .noutputs = ARRAY_SIZE(mahimahi_col_gpios), + .ninputs = ARRAY_SIZE(mahimahi_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = (GPIOKPF_LEVEL_TRIGGERED_IRQ | + GPIOKPF_REMOVE_PHANTOM_KEYS | + GPIOKPF_PRINT_UNMAPPED_KEYS), +}; + +static struct gpio_event_direct_entry mahimahi_keypad_key_map[] = { + { + .gpio = MAHIMAHI_GPIO_POWER_KEY, + .code = KEY_POWER, + }, +}; + +static struct gpio_event_input_info mahimahi_keypad_key_info = { + .info.func = gpio_event_input_func, + .info.no_suspend = true, + .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC, + .flags = 0, + .type = EV_KEY, + .keymap = mahimahi_keypad_key_map, + .keymap_size = ARRAY_SIZE(mahimahi_keypad_key_map) +}; + +/* jogball */ +static uint16_t jogball_axis_map(struct gpio_event_axis_info *info, uint16_t in) +{ + struct jog_axis_info *ai = + container_of(info, struct jog_axis_info, info); + uint16_t out = ai->out_state; + + if (jog_just_on) { + if (jiffies == jog_on_jiffies || jiffies == jog_on_jiffies + 1) + goto ignore; + jog_just_on = 0; + } + if((ai->in_state ^ in) & 1) + out--; + if((ai->in_state ^ in) & 2) + out++; + ai->out_state = out; +ignore: + ai->in_state = in; + return out; +} + +static int jogball_power(const struct gpio_event_platform_data *pdata, bool on) +{ + if (on) { + vreg_enable(jog_vreg); + jog_just_on = 1; + jog_on_jiffies = jiffies; + } else { + vreg_disable(jog_vreg); + } + + return 0; +} + +static int jogball_power_cdma(const struct gpio_event_platform_data *pdata, bool on) +{ + if (on) { + gpio_set_value(MAHIMAHI_CDMA_JOG_2V6_EN, 1); + jog_just_on = 1; + jog_on_jiffies = jiffies; + } else { + gpio_set_value(MAHIMAHI_CDMA_JOG_2V6_EN, 0); + } + + return 0; +} + +static uint32_t jogball_x_gpios[] = { + MAHIMAHI_GPIO_BALL_LEFT, MAHIMAHI_GPIO_BALL_RIGHT, +}; +static uint32_t jogball_y_gpios[] = { + MAHIMAHI_GPIO_BALL_UP, MAHIMAHI_GPIO_BALL_DOWN, +}; + +static struct jog_axis_info jogball_x_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(jogball_x_gpios), + .dev = 1, + .type = EV_REL, + .code = REL_X, + .decoded_size = 1U << ARRAY_SIZE(jogball_x_gpios), + .map = jogball_axis_map, + .gpio = jogball_x_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION, + } +}; + +static struct jog_axis_info jogball_y_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(jogball_y_gpios), + .dev = 1, + .type = EV_REL, + .code = REL_Y, + .decoded_size = 1U << ARRAY_SIZE(jogball_y_gpios), + .map = jogball_axis_map, + .gpio = jogball_y_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION, + } +}; + +static struct gpio_event_info *mahimahi_input_info[] = { + &mahimahi_keypad_matrix_info.info, + &mahimahi_keypad_key_info.info, + &jogball_x_axis.info.info, + &jogball_y_axis.info.info, +}; + +static struct gpio_event_platform_data mahimahi_input_data = { + .names = { + "mahimahi-keypad", + "mahimahi-nav", + NULL, + }, + .info = mahimahi_input_info, + .info_count = ARRAY_SIZE(mahimahi_input_info), + .power = jogball_power, +}; + +static struct platform_device mahimahi_input_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &mahimahi_input_data, + }, +}; + +static int mahimahi_reset_keys_up[] = { + KEY_VOLUMEUP, + 0, +}; + +static struct keyreset_platform_data mahimahi_reset_keys_pdata = { + .keys_up = mahimahi_reset_keys_up, + .keys_down = { + KEY_POWER, + KEY_VOLUMEDOWN, + BTN_MOUSE, + 0 + }, +}; + +struct platform_device mahimahi_reset_keys_device = { + .name = KEYRESET_NAME, + .dev = { + .platform_data = &mahimahi_reset_keys_pdata, + }, +}; + + +static int __init mahimahi_init_keypad_jogball(void) +{ + int ret; + + if (!machine_is_mahimahi()) + return 0; + + ret = platform_device_register(&mahimahi_reset_keys_device); + if (ret != 0) + return ret; + + if (is_cdma_version(system_rev)) { + mahimahi_keypad_matrix_info.keymap = mahimahi_cdma_keymap; + /* In the CDMA version, jogball power is supplied by a gpio. */ + ret = gpio_request(MAHIMAHI_CDMA_JOG_2V6_EN, "jog_en"); + if (ret < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_CDMA_JOG_2V6_EN, ret); + return ret; + } + mahimahi_input_data.power = jogball_power_cdma; + } else { + /* in UMTS version, jogball power is supplied by pmic */ + jog_vreg = vreg_get(&mahimahi_input_device.dev, "gp2"); + if (jog_vreg == NULL) + return -ENOENT; + } + + ret = platform_device_register(&mahimahi_input_device); + if (ret != 0) + return ret; + + return 0; +} + +device_initcall(mahimahi_init_keypad_jogball); diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c new file mode 100644 index 00000000000..da30672d26a --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -0,0 +1,2261 @@ +/* board-mahimahi-microp.c + * Copyright (C) 2009 Google. + * Copyright (C) 2009 HTC Corporation. + * + * The Microp on mahimahi is an i2c device that supports + * the following functions + * - LEDs (Green, Amber, Jogball backlight) + * - Lightsensor + * - Headset remotekeys + * - G-sensor + * - Interrupts + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi.h" + + +#define MICROP_I2C_NAME "mahimahi-microp" + +#define MICROP_LSENSOR_ADC_CHAN 6 +#define MICROP_REMOTE_KEY_ADC_CHAN 7 + +#define MICROP_I2C_WCMD_MISC 0x20 +#define MICROP_I2C_WCMD_SPI_EN 0x21 +#define MICROP_I2C_WCMD_AUTO_BL_CTL 0x23 +#define MICROP_I2C_RCMD_SPI_BL_STATUS 0x24 +#define MICROP_I2C_WCMD_BUTTONS_LED_CTRL 0x25 +#define MICROP_I2C_RCMD_VERSION 0x30 +#define MICROP_I2C_WCMD_ADC_TABLE 0x42 +#define MICROP_I2C_WCMD_LED_MODE 0x53 +#define MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME 0x54 +#define MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME 0x55 +#define MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME 0x57 +#define MICROP_I2C_WCMD_JOGBALL_LED_MODE 0x5A +#define MICROP_I2C_RCMD_JOGBALL_LED_REMAIN_TIME 0x5B +#define MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET 0x5C +#define MICROP_I2C_WCMD_JOGBALL_LED_PERIOD_SET 0x5D +#define MICROP_I2C_WCMD_READ_ADC_VALUE_REQ 0x60 +#define MICROP_I2C_RCMD_ADC_VALUE 0x62 +#define MICROP_I2C_WCMD_REMOTEKEY_TABLE 0x63 +#define MICROP_I2C_WCMD_LCM_REGISTER 0x70 +#define MICROP_I2C_WCMD_GSENSOR_REG 0x73 +#define MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ 0x74 +#define MICROP_I2C_RCMD_GSENSOR_REG_DATA 0x75 +#define MICROP_I2C_WCMD_GSENSOR_DATA_REQ 0x76 +#define MICROP_I2C_RCMD_GSENSOR_X_DATA 0x77 +#define MICROP_I2C_RCMD_GSENSOR_Y_DATA 0x78 +#define MICROP_I2C_RCMD_GSENSOR_Z_DATA 0x79 +#define MICROP_I2C_RCMD_GSENSOR_DATA 0x7A +#define MICROP_I2C_WCMD_OJ_REG 0x7B +#define MICROP_I2C_WCMD_OJ_REG_DATA_REQ 0x7C +#define MICROP_I2C_RCMD_OJ_REG_DATA 0x7D +#define MICROP_I2C_WCMD_OJ_POS_DATA_REQ 0x7E +#define MICROP_I2C_RCMD_OJ_POS_DATA 0x7F +#define MICROP_I2C_WCMD_GPI_INT_CTL_EN 0x80 +#define MICROP_I2C_WCMD_GPI_INT_CTL_DIS 0x81 +#define MICROP_I2C_RCMD_GPI_INT_STATUS 0x82 +#define MICROP_I2C_RCMD_GPI_STATUS 0x83 +#define MICROP_I2C_WCMD_GPI_INT_STATUS_CLR 0x84 +#define MICROP_I2C_RCMD_GPI_INT_SETTING 0x85 +#define MICROP_I2C_RCMD_REMOTE_KEYCODE 0x87 +#define MICROP_I2C_WCMD_REMOTE_KEY_DEBN_TIME 0x88 +#define MICROP_I2C_WCMD_REMOTE_PLUG_DEBN_TIME 0x89 +#define MICROP_I2C_WCMD_SIMCARD_DEBN_TIME 0x8A +#define MICROP_I2C_WCMD_GPO_LED_STATUS_EN 0x90 +#define MICROP_I2C_WCMD_GPO_LED_STATUS_DIS 0x91 + +#define IRQ_GSENSOR (1<<10) +#define IRQ_LSENSOR (1<<9) +#define IRQ_REMOTEKEY (1<<7) +#define IRQ_HEADSETIN (1<<2) +#define IRQ_SDCARD (1<<0) + +#define READ_GPI_STATE_HPIN (1<<2) +#define READ_GPI_STATE_SDCARD (1<<0) + +#define ALS_CALIBRATE_MODE 147 + +/* Check pattern, to check if ALS has been calibrated */ +#define ALS_CALIBRATED 0x6DA5 + +/* delay for deferred light sensor read */ +#define LS_READ_DELAY (HZ/2) + +/*#define DEBUG_BMA150 */ +#ifdef DEBUG_BMA150 +/* Debug logging of accelleration data */ +#define GSENSOR_LOG_MAX 2048 /* needs to be power of 2 */ +#define GSENSOR_LOG_MASK (GSENSOR_LOG_MAX - 1) + +struct gsensor_log { + ktime_t timestamp; + short x; + short y; + short z; +}; + +static DEFINE_MUTEX(gsensor_log_lock); +static struct gsensor_log gsensor_log[GSENSOR_LOG_MAX]; +static unsigned gsensor_log_head; +static unsigned gsensor_log_tail; + +void gsensor_log_status(ktime_t time, short x, short y, short z) +{ + unsigned n; + mutex_lock(&gsensor_log_lock); + n = gsensor_log_head; + gsensor_log[n].timestamp = time; + gsensor_log[n].x = x; + gsensor_log[n].y = y; + gsensor_log[n].z = z; + n = (n + 1) & GSENSOR_LOG_MASK; + if (n == gsensor_log_tail) + gsensor_log_tail = (gsensor_log_tail + 1) & GSENSOR_LOG_MASK; + gsensor_log_head = n; + mutex_unlock(&gsensor_log_lock); +} + +static int gsensor_log_print(struct seq_file *sf, void *private) +{ + unsigned n; + + mutex_lock(&gsensor_log_lock); + seq_printf(sf, "timestamp X Y Z\n"); + for (n = gsensor_log_tail; + n != gsensor_log_head; + n = (n + 1) & GSENSOR_LOG_MASK) { + seq_printf(sf, "%10d.%010d %6d %6d %6d\n", + gsensor_log[n].timestamp.tv.sec, + gsensor_log[n].timestamp.tv.nsec, + gsensor_log[n].x, gsensor_log[n].y, + gsensor_log[n].z); + } + mutex_unlock(&gsensor_log_lock); + return 0; +} + +static int gsensor_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, gsensor_log_print, NULL); +} + +static struct file_operations gsensor_log_fops = { + .open = gsensor_log_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* def DEBUG_BMA150 */ + +static int microp_headset_has_mic(void); +static int microp_enable_headset_plug_event(void); +static int microp_enable_key_event(void); +static int microp_disable_key_event(void); + +static struct h35mm_platform_data mahimahi_h35mm_data = { + .plug_event_enable = microp_enable_headset_plug_event, + .headset_has_mic = microp_headset_has_mic, + .key_event_enable = microp_enable_key_event, + .key_event_disable = microp_disable_key_event, +}; + +static struct platform_device mahimahi_h35mm = { + .name = "htc_headset", + .id = -1, + .dev = { + .platform_data = &mahimahi_h35mm_data, + }, +}; + +enum led_type { + GREEN_LED, + AMBER_LED, + RED_LED, + BLUE_LED, + JOGBALL_LED, + BUTTONS_LED, + NUM_LEDS, +}; + +static uint16_t lsensor_adc_table[10] = { + 0x000, 0x001, 0x00F, 0x01E, 0x03C, 0x121, 0x190, 0x2BA, 0x26E, 0x3FF +}; + +static uint16_t remote_key_adc_table[6] = { + 0, 33, 43, 110, 129, 220 +}; + +static uint32_t golden_adc = 0xC0; +static uint32_t als_kadc; + +static struct wake_lock microp_i2c_wakelock; + +static struct i2c_client *private_microp_client; + +struct microp_int_pin { + uint16_t int_gsensor; + uint16_t int_lsensor; + uint16_t int_reset; + uint16_t int_simcard; + uint16_t int_hpin; + uint16_t int_remotekey; +}; + +struct microp_led_data { + int type; + struct led_classdev ldev; + struct mutex led_data_mutex; + struct work_struct brightness_work; + spinlock_t brightness_lock; + enum led_brightness brightness; + uint8_t mode; + uint8_t blink; +}; + +struct microp_i2c_work { + struct work_struct work; + struct i2c_client *client; + int (*intr_debounce)(uint8_t *pin_status); + void (*intr_function)(uint8_t *pin_status); +}; + +struct microp_i2c_client_data { + struct microp_led_data leds[NUM_LEDS]; + uint16_t version; + struct microp_i2c_work work; + struct delayed_work hpin_debounce_work; + struct delayed_work ls_read_work; + struct early_suspend early_suspend; + uint8_t enable_early_suspend; + uint8_t enable_reset_button; + int microp_is_suspend; + int auto_backlight_enabled; + uint8_t light_sensor_enabled; + uint8_t force_light_sensor_read; + uint8_t button_led_value; + int headset_is_in; + int is_hpin_pin_stable; + struct input_dev *ls_input_dev; + uint32_t als_kadc; + uint32_t als_gadc; + uint8_t als_calibrating; +}; + +static char *hex2string(uint8_t *data, int len) +{ + static char buf[101]; + int i; + + i = (sizeof(buf) - 1) / 4; + if (len > i) + len = i; + + for (i = 0; i < len; i++) + sprintf(buf + i * 4, "[%02X]", data[i]); + + return buf; +} + +#define I2C_READ_RETRY_TIMES 10 +#define I2C_WRITE_RETRY_TIMES 10 + +static int i2c_read_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + + mdelay(1); + for (retry = 0; retry <= I2C_READ_RETRY_TIMES; retry++) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) { + dev_dbg(&client->dev, "R [%02X] = %s\n", addr, + hex2string(data, length)); + return 0; + } + msleep(10); + } + + dev_err(&client->dev, "i2c_read_block retry over %d\n", + I2C_READ_RETRY_TIMES); + return -EIO; +} + +#define MICROP_I2C_WRITE_BLOCK_SIZE 21 +static int i2c_write_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + uint8_t buf[MICROP_I2C_WRITE_BLOCK_SIZE]; + int ret; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + dev_dbg(&client->dev, "W [%02X] = %s\n", addr, + hex2string(data, length)); + + if (length + 1 > MICROP_I2C_WRITE_BLOCK_SIZE) { + dev_err(&client->dev, "i2c_write_block length too long\n"); + return -E2BIG; + } + + buf[0] = addr; + memcpy((void *)&buf[1], (void *)data, length); + + mdelay(1); + for (retry = 0; retry <= I2C_WRITE_RETRY_TIMES; retry++) { + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) + return 0; + msleep(10); + } + dev_err(&client->dev, "i2c_write_block retry over %d\n", + I2C_WRITE_RETRY_TIMES); + return -EIO; +} + +static int microp_read_adc(uint8_t channel, uint16_t *value) +{ + struct i2c_client *client; + int ret; + uint8_t cmd[2], data[2]; + + client = private_microp_client; + cmd[0] = 0; + cmd[1] = channel; + ret = i2c_write_block(client, MICROP_I2C_WCMD_READ_ADC_VALUE_REQ, + cmd, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: request adc fail\n", __func__); + return -EIO; + } + + ret = i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read adc fail\n", __func__); + return -EIO; + } + *value = data[0] << 8 | data[1]; + return 0; +} + +static int microp_read_gpi_status(struct i2c_client *client, uint16_t *status) +{ + uint8_t data[2]; + int ret; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_STATUS, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read failed\n", __func__); + return -EIO; + } + *status = (data[0] << 8) | data[1]; + return 0; +} + +static int microp_interrupt_enable(struct i2c_client *client, + uint16_t interrupt_mask) +{ + uint8_t data[2]; + int ret = -1; + + data[0] = interrupt_mask >> 8; + data[1] = interrupt_mask & 0xFF; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_EN, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: enable 0x%x interrupt failed\n", + __func__, interrupt_mask); + return ret; +} + +static int microp_interrupt_disable(struct i2c_client *client, + uint16_t interrupt_mask) +{ + uint8_t data[2]; + int ret = -1; + + data[0] = interrupt_mask >> 8; + data[1] = interrupt_mask & 0xFF; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_DIS, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: disable 0x%x interrupt failed\n", + __func__, interrupt_mask); + return ret; +} + + +/* + * SD slot card-detect support + */ +static unsigned int sdslot_cd = 0; +static void (*sdslot_status_cb)(int card_present, void *dev_id); +static void *sdslot_mmc_dev; + +int mahimahi_microp_sdslot_status_register( + void (*cb)(int card_present, void *dev_id), + void *dev_id) +{ + if (sdslot_status_cb) + return -EBUSY; + sdslot_status_cb = cb; + sdslot_mmc_dev = dev_id; + return 0; +} + +unsigned int mahimahi_microp_sdslot_status(struct device *dev) +{ + return sdslot_cd; +} + +static void mahimahi_microp_sdslot_update_status(int status) +{ + sdslot_cd = !(status & READ_GPI_STATE_SDCARD); + if (sdslot_status_cb) + sdslot_status_cb(sdslot_cd, sdslot_mmc_dev); +} + +/* + *Headset Support +*/ +static void hpin_debounce_do_work(struct work_struct *work) +{ + uint16_t gpi_status = 0; + struct microp_i2c_client_data *cdata; + int insert = 0; + struct i2c_client *client; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + microp_read_gpi_status(client, &gpi_status); + insert = (gpi_status & READ_GPI_STATE_HPIN) ? 0 : 1; + if (insert != cdata->headset_is_in) { + cdata->headset_is_in = insert; + pr_debug("headset %s\n", insert ? "inserted" : "removed"); + htc_35mm_jack_plug_event(cdata->headset_is_in, + &cdata->is_hpin_pin_stable); + } +} + +static int microp_enable_headset_plug_event(void) +{ + int ret; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint16_t stat; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + /* enable microp interrupt to detect changes */ + ret = microp_interrupt_enable(client, IRQ_HEADSETIN); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable irqs\n", + __func__); + return 0; + } + /* see if headset state has changed */ + microp_read_gpi_status(client, &stat); + stat = !(stat & READ_GPI_STATE_HPIN); + if(cdata->headset_is_in != stat) { + cdata->headset_is_in = stat; + pr_debug("Headset state changed\n"); + htc_35mm_jack_plug_event(stat, &cdata->is_hpin_pin_stable); + } + + return 1; +} + +static int microp_headset_detect_mic(void) +{ + uint16_t data; + + microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &data); + if (data >= 200) + return 1; + else + return 0; +} + +static int microp_headset_has_mic(void) +{ + int mic1 = -1; + int mic2 = -1; + int count = 0; + + mic2 = microp_headset_detect_mic(); + + /* debounce the detection wait until 2 consecutive read are equal */ + while ((mic1 != mic2) && (count < 10)) { + mic1 = mic2; + msleep(600); + mic2 = microp_headset_detect_mic(); + count++; + } + + pr_info("%s: microphone (%d) %s\n", __func__, count, + mic1 ? "present" : "not present"); + + return mic1; +} + +static int microp_enable_key_event(void) +{ + int ret; + struct i2c_client *client; + + client = private_microp_client; + + if (!is_cdma_version(system_rev)) + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 1); + + /* turn on key interrupt */ + /* enable microp interrupt to detect changes */ + ret = microp_interrupt_enable(client, IRQ_REMOTEKEY); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable irqs\n", + __func__); + return ret; + } + return 0; +} + +static int microp_disable_key_event(void) +{ + int ret; + struct i2c_client *client; + + client = private_microp_client; + + /* shutdown key interrupt */ + if (!is_cdma_version(system_rev)) + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0); + + /* disable microp interrupt to detect changes */ + ret = microp_interrupt_disable(client, IRQ_REMOTEKEY); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to disable irqs\n", + __func__); + return ret; + } + return 0; +} + +static int get_remote_keycode(int *keycode) +{ + struct i2c_client *client = private_microp_client; + int ret; + uint8_t data[2]; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_REMOTE_KEYCODE, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read remote keycode fail\n", + __func__); + return -EIO; + } + pr_debug("%s: key = 0x%x\n", __func__, data[1]); + if (!data[1]) { + *keycode = 0; + return 1; /* no keycode */ + } else { + *keycode = data[1]; + } + return 0; +} + +static ssize_t microp_i2c_remotekey_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + uint16_t value; + int i, button = 0; + int ret; + + client = to_i2c_client(dev); + + microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &value); + + for (i = 0; i < 3; i++) { + if ((value >= remote_key_adc_table[2 * i]) && + (value <= remote_key_adc_table[2 * i + 1])) { + button = i + 1; + } + + } + + ret = sprintf(buf, "Remote Key[0x%03X] => button %d\n", + value, button); + + return ret; +} + +static DEVICE_ATTR(key_adc, 0644, microp_i2c_remotekey_adc_show, NULL); + +/* + * LED support +*/ +static int microp_i2c_write_led_mode(struct i2c_client *client, + struct led_classdev *led_cdev, + uint8_t mode, uint16_t off_timer) +{ + struct microp_i2c_client_data *cdata; + struct microp_led_data *ldata; + uint8_t data[7]; + int ret; + + cdata = i2c_get_clientdata(client); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + + if (ldata->type == GREEN_LED) { + data[0] = 0x01; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } else if (ldata->type == AMBER_LED) { + data[0] = 0x02; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = mode; + data[5] = off_timer >> 8; + data[6] = off_timer & 0xFF; + } else if (ldata->type == RED_LED) { + data[0] = 0x02; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = mode? 5: 0; + data[5] = off_timer >> 8; + data[6] = off_timer & 0xFF; + } else if (ldata->type == BLUE_LED) { + data[0] = 0x04; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } + + ret = i2c_write_block(client, MICROP_I2C_WCMD_LED_MODE, data, 7); + if (ret == 0) { + mutex_lock(&ldata->led_data_mutex); + if (mode > 1) + ldata->blink = mode; + else + ldata->mode = mode; + mutex_unlock(&ldata->led_data_mutex); + } + return ret; +} + +static ssize_t microp_i2c_led_blink_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + int ret; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + mutex_lock(&ldata->led_data_mutex); + ret = sprintf(buf, "%d\n", ldata->blink ? ldata->blink - 1 : 0); + mutex_unlock(&ldata->led_data_mutex); + + return ret; +} + +static ssize_t microp_i2c_led_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int val, ret; + uint8_t mode; + + val = -1; + sscanf(buf, "%u", &val); + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + mutex_lock(&ldata->led_data_mutex); + switch (val) { + case 0: /* stop flashing */ + mode = ldata->mode; + ldata->blink = 0; + break; + case 1: + case 2: + case 3: + mode = val + 1; + break; + + default: + mutex_unlock(&ldata->led_data_mutex); + return -EINVAL; + } + mutex_unlock(&ldata->led_data_mutex); + + ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff); + if (ret) + dev_err(&client->dev, "%s set blink failed\n", led_cdev->name); + + return count; +} + +static DEVICE_ATTR(blink, 0644, microp_i2c_led_blink_show, + microp_i2c_led_blink_store); + +static ssize_t microp_i2c_led_off_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct microp_i2c_client_data *cdata; + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + uint8_t data[2]; + int ret, offtime; + + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + cdata = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "Getting %s remaining time\n", led_cdev->name); + + if (ldata->type == GREEN_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME, data, 2); + } else if (ldata->type == AMBER_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME, + data, 2); + } else if (ldata->type == RED_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME, + data, 2); + } else if (ldata->type == BLUE_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME, data, 2); + } else { + dev_err(&client->dev, "Unknown led %s\n", ldata->ldev.name); + return -EINVAL; + } + + if (ret) { + dev_err(&client->dev, + "%s get off_timer failed\n", led_cdev->name); + } + offtime = (int)((data[1] | data[0] << 8) * 2); + + ret = sprintf(buf, "Time remains %d:%d\n", offtime / 60, offtime % 60); + return ret; +} + +static ssize_t microp_i2c_led_off_timer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int min, sec, ret; + uint16_t off_timer; + + min = -1; + sec = -1; + sscanf(buf, "%d %d", &min, &sec); + + if (min < 0 || min > 255) + return -EINVAL; + if (sec < 0 || sec > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_dbg(&client->dev, "Setting %s off_timer to %d min %d sec\n", + led_cdev->name, min, sec); + + if (!min && !sec) + off_timer = 0xFFFF; + else + off_timer = (min * 60 + sec) / 2; + + ret = microp_i2c_write_led_mode(client, led_cdev, + ldata->mode, off_timer); + if (ret) { + dev_err(&client->dev, + "%s set off_timer %d min %d sec failed\n", + led_cdev->name, min, sec); + } + return count; +} + +static DEVICE_ATTR(off_timer, 0644, microp_i2c_led_off_timer_show, + microp_i2c_led_off_timer_store); + +static ssize_t microp_i2c_jogball_color_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int rpwm, gpwm, bpwm, ret; + uint8_t data[4]; + + rpwm = -1; + gpwm = -1; + bpwm = -1; + sscanf(buf, "%d %d %d", &rpwm, &gpwm, &bpwm); + + if (rpwm < 0 || rpwm > 255) + return -EINVAL; + if (gpwm < 0 || gpwm > 255) + return -EINVAL; + if (bpwm < 0 || bpwm > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_dbg(&client->dev, "Setting %s color to R=%d, G=%d, B=%d\n", + led_cdev->name, rpwm, gpwm, bpwm); + + data[0] = rpwm; + data[1] = gpwm; + data[2] = bpwm; + data[3] = 0x00; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET, + data, 4); + if (ret) { + dev_err(&client->dev, + "%s set color R=%d G=%d B=%d failed\n", + led_cdev->name, rpwm, gpwm, bpwm); + } + return count; +} + +static DEVICE_ATTR(color, 0644, NULL, microp_i2c_jogball_color_store); + +static ssize_t microp_i2c_jogball_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int period = -1; + int ret; + uint8_t data[4]; + + sscanf(buf, "%d", &period); + + if (period < 2 || period > 12) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_info(&client->dev, "Setting Jogball flash period to %d\n", period); + + data[0] = 0x00; + data[1] = period; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_PERIOD_SET, + data, 2); + if (ret) { + dev_err(&client->dev, "%s set period=%d failed\n", + led_cdev->name, period); + } + return count; +} + +static DEVICE_ATTR(period, 0644, NULL, microp_i2c_jogball_period_store); + +static void microp_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + unsigned long flags; + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + struct microp_led_data *ldata = + container_of(led_cdev, struct microp_led_data, ldev); + + dev_dbg(&client->dev, "Setting %s brightness current %d new %d\n", + led_cdev->name, led_cdev->brightness, brightness); + + if (brightness > 255) + brightness = 255; + led_cdev->brightness = brightness; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + schedule_work(&ldata->brightness_work); +} + +static void microp_led_brightness_set_work(struct work_struct *work) +{ + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + + enum led_brightness brightness; + int ret; + uint8_t mode; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + if (brightness) + mode = 1; + else + mode = 0; + + ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff); + if (ret) { + dev_err(&client->dev, + "led_brightness_set failed to set mode\n"); + } +} + +struct device_attribute *green_amber_attrs[] = { + &dev_attr_blink, + &dev_attr_off_timer, +}; + +struct device_attribute *jogball_attrs[] = { + &dev_attr_color, + &dev_attr_period, +}; + +static void microp_led_buttons_brightness_set_work(struct work_struct *work) +{ + + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + struct microp_i2c_client_data *cdata = i2c_get_clientdata(client); + + + uint8_t data[4] = {0, 0, 0}; + int ret = 0; + enum led_brightness brightness; + uint8_t value; + + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + value = brightness >= 255 ? 0x20 : 0; + + /* avoid a flicker that can occur when writing the same value */ + if (cdata->button_led_value == value) + return; + cdata->button_led_value = value; + + /* in 40ms */ + data[0] = 0x05; + /* duty cycle 0-255 */ + data[1] = value; + /* bit2 == change brightness */ + data[3] = 0x04; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_BUTTONS_LED_CTRL, + data, 4); + if (ret < 0) + dev_err(&client->dev, "%s failed on set buttons\n", __func__); +} + +static void microp_led_jogball_brightness_set_work(struct work_struct *work) +{ + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + uint8_t data[3] = {0, 0, 0}; + int ret = 0; + enum led_brightness brightness; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + switch (brightness) { + case 0: + data[0] = 0; + break; + case 3: + data[0] = 1; + data[1] = data[2] = 0xFF; + break; + case 7: + data[0] = 2; + data[1] = 0; + data[2] = 60; + break; + default: + dev_warn(&client->dev, "%s: unknown value: %d\n", + __func__, brightness); + break; + } + ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_MODE, + data, 3); + if (ret < 0) + dev_err(&client->dev, "%s failed on set jogball mode:0x%2.2X\n", + __func__, data[0]); +} + +/* + * Light Sensor Support + */ +static int microp_i2c_auto_backlight_mode(struct i2c_client *client, + uint8_t enabled) +{ + uint8_t data[2]; + int ret = 0; + + data[0] = 0; + if (enabled) + data[1] = 1; + else + data[1] = 0; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_AUTO_BL_CTL, data, 2); + if (ret != 0) + pr_err("%s: set auto light sensor fail\n", __func__); + + return ret; +} + +static int lightsensor_enable(void) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int ret; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + if (cdata->microp_is_suspend) { + pr_err("%s: abort, uP is going to suspend after #\n", + __func__); + return -EIO; + } + + disable_irq(client->irq); + ret = microp_i2c_auto_backlight_mode(client, 1); + if (ret < 0) { + pr_err("%s: set auto light sensor fail\n", __func__); + enable_irq(client->irq); + return ret; + } + + cdata->auto_backlight_enabled = 1; + /* TEMPORARY HACK: schedule a deferred light sensor read + * to work around sensor manager race condition + */ + schedule_delayed_work(&cdata->ls_read_work, LS_READ_DELAY); + schedule_work(&cdata->work.work); + + return 0; +} + +static int lightsensor_disable(void) +{ + /* update trigger data when done */ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int ret; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + if (cdata->microp_is_suspend) { + pr_err("%s: abort, uP is going to suspend after #\n", + __func__); + return -EIO; + } + + cancel_delayed_work(&cdata->ls_read_work); + + ret = microp_i2c_auto_backlight_mode(client, 0); + if (ret < 0) + pr_err("%s: disable auto light sensor fail\n", + __func__); + else + cdata->auto_backlight_enabled = 0; + return 0; +} + +static int microp_lightsensor_read(uint16_t *adc_value, + uint8_t *adc_level) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t i; + int ret; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + ret = microp_read_adc(MICROP_LSENSOR_ADC_CHAN, adc_value); + if (ret != 0) + return -1; + + if (*adc_value > 0x3FF) { + pr_warning("%s: get wrong value: 0x%X\n", + __func__, *adc_value); + return -1; + } else { + if (!cdata->als_calibrating) { + *adc_value = *adc_value + * cdata->als_gadc / cdata->als_kadc; + if (*adc_value > 0x3FF) + *adc_value = 0x3FF; + } + + *adc_level = ARRAY_SIZE(lsensor_adc_table) - 1; + for (i = 0; i < ARRAY_SIZE(lsensor_adc_table); i++) { + if (*adc_value <= lsensor_adc_table[i]) { + *adc_level = i; + break; + } + } + pr_debug("%s: ADC value: 0x%X, level: %d #\n", + __func__, *adc_value, *adc_level); + } + + return 0; +} + +static ssize_t microp_i2c_lightsensor_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t adc_level = 0; + uint16_t adc_value = 0; + int ret; + + ret = microp_lightsensor_read(&adc_value, &adc_level); + + ret = sprintf(buf, "ADC[0x%03X] => level %d\n", adc_value, adc_level); + + return ret; +} + +static DEVICE_ATTR(ls_adc, 0644, microp_i2c_lightsensor_adc_show, NULL); + +static ssize_t microp_i2c_ls_auto_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + uint8_t data[2] = {0, 0}; + int ret; + + client = to_i2c_client(dev); + + i2c_read_block(client, MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2); + ret = sprintf(buf, "Light sensor Auto = %d, SPI enable = %d\n", + data[0], data[1]); + + return ret; +} + +static ssize_t microp_i2c_ls_auto_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t enable = 0; + int ls_auto; + + ls_auto = -1; + sscanf(buf, "%d", &ls_auto); + + if (ls_auto != 0 && ls_auto != 1 && ls_auto != ALS_CALIBRATE_MODE) + return -EINVAL; + + client = to_i2c_client(dev); + cdata = i2c_get_clientdata(client); + + if (ls_auto) { + enable = 1; + cdata->als_calibrating = (ls_auto == ALS_CALIBRATE_MODE) ? 1 : 0; + cdata->auto_backlight_enabled = 1; + } else { + enable = 0; + cdata->als_calibrating = 0; + cdata->auto_backlight_enabled = 0; + } + + microp_i2c_auto_backlight_mode(client, enable); + + return count; +} + +static DEVICE_ATTR(ls_auto, 0644, microp_i2c_ls_auto_show, + microp_i2c_ls_auto_store); + +DEFINE_MUTEX(api_lock); +static int lightsensor_opened; + +static int lightsensor_open(struct inode *inode, struct file *file) +{ + int rc = 0; + pr_debug("%s\n", __func__); + mutex_lock(&api_lock); + if (lightsensor_opened) { + pr_err("%s: already opened\n", __func__); + rc = -EBUSY; + } + lightsensor_opened = 1; + mutex_unlock(&api_lock); + return rc; +} + +static int lightsensor_release(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + mutex_lock(&api_lock); + lightsensor_opened = 0; + mutex_unlock(&api_lock); + return 0; +} + +static long lightsensor_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, val; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + + mutex_lock(&api_lock); + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + pr_debug("%s cmd %d\n", __func__, _IOC_NR(cmd)); + + switch (cmd) { + case LIGHTSENSOR_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) { + rc = -EFAULT; + break; + } + rc = val ? lightsensor_enable() : lightsensor_disable(); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + val = cdata->auto_backlight_enabled; + pr_debug("%s enabled %d\n", __func__, val); + rc = put_user(val, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); + return rc; +} + +static struct file_operations lightsensor_fops = { + .owner = THIS_MODULE, + .open = lightsensor_open, + .release = lightsensor_release, + .unlocked_ioctl = lightsensor_ioctl +}; + +struct miscdevice lightsensor_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lightsensor", + .fops = &lightsensor_fops +}; + +/* + * G-sensor + */ +static int microp_spi_enable(uint8_t on) +{ + struct i2c_client *client; + int ret; + + client = private_microp_client; + ret = i2c_write_block(client, MICROP_I2C_WCMD_SPI_EN, &on, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + msleep(10); + return ret; +} + +static int gsensor_read_reg(uint8_t reg, uint8_t *data) +{ + struct i2c_client *client; + int ret; + uint8_t tmp[2]; + + client = private_microp_client; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ, + ®, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + msleep(10); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_REG_DATA, tmp, 2); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_read_block fail\n", __func__); + return ret; + } + *data = tmp[1]; + return ret; +} + +static int gsensor_write_reg(uint8_t reg, uint8_t data) +{ + struct i2c_client *client; + int ret; + uint8_t tmp[2]; + + client = private_microp_client; + + tmp[0] = reg; + tmp[1] = data; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG, tmp, 2); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + + return ret; +} + +static int gsensor_read_acceleration(short *buf) +{ + struct i2c_client *client; + int ret; + uint8_t tmp[6]; + struct microp_i2c_client_data *cdata; + + client = private_microp_client; + + cdata = i2c_get_clientdata(client); + + tmp[0] = 1; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_DATA_REQ, + tmp, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + + msleep(10); + + if (cdata->version <= 0x615) { + /* + * Note the data is a 10bit signed value from the chip. + */ + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_X_DATA, + tmp, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[0] = (short)(tmp[0] << 8 | tmp[1]); + buf[0] >>= 6; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_Y_DATA, + tmp, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[1] = (short)(tmp[0] << 8 | tmp[1]); + buf[1] >>= 6; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_Z_DATA, + tmp, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[2] = (short)(tmp[0] << 8 | tmp[1]); + buf[2] >>= 6; + } else { + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_DATA, + tmp, 6); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[0] = (short)(tmp[0] << 8 | tmp[1]); + buf[0] >>= 6; + buf[1] = (short)(tmp[2] << 8 | tmp[3]); + buf[1] >>= 6; + buf[2] = (short)(tmp[4] << 8 | tmp[5]); + buf[2] >>= 6; + } + +#ifdef DEBUG_BMA150 + /* Log this to debugfs */ + gsensor_log_status(ktime_get(), buf[0], buf[1], buf[2]); +#endif + return 1; +} + +static int gsensor_init_hw(void) +{ + uint8_t reg; + int ret; + + pr_debug("%s\n", __func__); + + microp_spi_enable(1); + + ret = gsensor_read_reg(RANGE_BWIDTH_REG, ®); + if (ret < 0 ) + return -EIO; + reg &= 0xe0; + ret = gsensor_write_reg(RANGE_BWIDTH_REG, reg); + if (ret < 0 ) + return -EIO; + + ret = gsensor_read_reg(SMB150_CONF2_REG, ®); + if (ret < 0 ) + return -EIO; + reg |= (1 << 3); + ret = gsensor_write_reg(SMB150_CONF2_REG, reg); + + return ret; +} + +static int bma150_set_mode(char mode) +{ + uint8_t reg; + int ret; + + pr_debug("%s mode = %d\n", __func__, mode); + if (mode == BMA_MODE_NORMAL) + microp_spi_enable(1); + + + ret = gsensor_read_reg(SMB150_CTRL_REG, ®); + if (ret < 0 ) + return -EIO; + reg = (reg & 0xfe) | mode; + ret = gsensor_write_reg(SMB150_CTRL_REG, reg); + + if (mode == BMA_MODE_SLEEP) + microp_spi_enable(0); + + return ret; +} +static int gsensor_read(uint8_t *data) +{ + int ret; + uint8_t reg = data[0]; + + ret = gsensor_read_reg(reg, &data[1]); + pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]); + return ret; +} + +static int gsensor_write(uint8_t *data) +{ + int ret; + uint8_t reg = data[0]; + + pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]); + ret = gsensor_write_reg(reg, data[1]); + return ret; +} + +static int bma150_open(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + return nonseekable_open(inode, file); +} + +static int bma150_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int bma150_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[8]; + int ret = -1; + short buf[8], temp; + + switch (cmd) { + case BMA_IOCTL_READ: + case BMA_IOCTL_WRITE: + case BMA_IOCTL_SET_MODE: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_from_user(&buf, argp, sizeof(buf))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case BMA_IOCTL_INIT: + ret = gsensor_init_hw(); + if (ret < 0) + return ret; + break; + + case BMA_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + ret = gsensor_read(rwbuf); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + ret = gsensor_write(rwbuf); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_READ_ACCELERATION: + ret = gsensor_read_acceleration(&buf[0]); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_SET_MODE: + bma150_set_mode(rwbuf[0]); + break; + case BMA_IOCTL_GET_INT: + temp = 0; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case BMA_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_to_user(argp, &buf, sizeof(buf))) + return -EFAULT; + break; + case BMA_IOCTL_GET_INT: + if (copy_to_user(argp, &temp, sizeof(temp))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static struct file_operations bma_fops = { + .owner = THIS_MODULE, + .open = bma150_open, + .release = bma150_release, + .ioctl = bma150_ioctl, +}; + +static struct miscdevice spi_bma_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = BMA150_G_SENSOR_NAME, + .fops = &bma_fops, +}; + +/* + * Interrupt + */ +static irqreturn_t microp_i2c_intr_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + + client = to_i2c_client(dev_id); + cdata = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "intr_irq_handler\n"); + + disable_irq_nosync(client->irq); + schedule_work(&cdata->work.work); + return IRQ_HANDLED; +} + +static void microp_i2c_intr_work_func(struct work_struct *work) +{ + struct microp_i2c_work *up_work; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t data[3], adc_level; + uint16_t intr_status = 0, adc_value, gpi_status = 0; + int keycode = 0, ret = 0; + + up_work = container_of(work, struct microp_i2c_work, work); + client = up_work->client; + cdata = i2c_get_clientdata(client); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_INT_STATUS, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read interrupt status fail\n", + __func__); + } + + intr_status = data[0]<<8 | data[1]; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_STATUS_CLR, + data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: clear interrupt status fail\n", + __func__); + } + pr_debug("intr_status=0x%02x\n", intr_status); + + if ((intr_status & IRQ_LSENSOR) || cdata->force_light_sensor_read) { + ret = microp_lightsensor_read(&adc_value, &adc_level); + if (cdata->force_light_sensor_read) { + /* report an invalid value first to ensure we trigger an event + * when adc_level is zero. + */ + input_report_abs(cdata->ls_input_dev, ABS_MISC, -1); + input_sync(cdata->ls_input_dev); + cdata->force_light_sensor_read = 0; + } + input_report_abs(cdata->ls_input_dev, ABS_MISC, (int)adc_level); + input_sync(cdata->ls_input_dev); + } + + if (intr_status & IRQ_SDCARD) { + microp_read_gpi_status(client, &gpi_status); + mahimahi_microp_sdslot_update_status(gpi_status); + } + + if (intr_status & IRQ_HEADSETIN) { + cdata->is_hpin_pin_stable = 0; + wake_lock_timeout(µp_i2c_wakelock, 3*HZ); + if (!cdata->headset_is_in) + schedule_delayed_work(&cdata->hpin_debounce_work, + msecs_to_jiffies(500)); + else + schedule_delayed_work(&cdata->hpin_debounce_work, + msecs_to_jiffies(300)); + } + if (intr_status & IRQ_REMOTEKEY) { + if ((get_remote_keycode(&keycode) == 0) && + (cdata->is_hpin_pin_stable)) { + htc_35mm_key_event(keycode, &cdata->is_hpin_pin_stable); + } + } + + enable_irq(client->irq); +} + +static void ls_read_do_work(struct work_struct *work) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata = i2c_get_clientdata(client); + + /* force a light sensor reading */ + disable_irq(client->irq); + cdata->force_light_sensor_read = 1; + schedule_work(&cdata->work.work); +} + +static int microp_function_initialize(struct i2c_client *client) +{ + struct microp_i2c_client_data *cdata; + uint8_t data[20]; + uint16_t stat, interrupts = 0; + int i; + int ret; + struct led_classdev *led_cdev; + + cdata = i2c_get_clientdata(client); + + /* Light Sensor */ + if (als_kadc >> 16 == ALS_CALIBRATED) + cdata->als_kadc = als_kadc & 0xFFFF; + else { + cdata->als_kadc = 0; + pr_info("%s: no ALS calibrated\n", __func__); + } + + if (cdata->als_kadc && golden_adc) { + cdata->als_kadc = + (cdata->als_kadc > 0 && cdata->als_kadc < 0x400) + ? cdata->als_kadc : golden_adc; + cdata->als_gadc = + (golden_adc > 0) + ? golden_adc : cdata->als_kadc; + } else { + cdata->als_kadc = 1; + cdata->als_gadc = 1; + } + pr_info("%s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, cdata->als_kadc, cdata->als_gadc); + + for (i = 0; i < 10; i++) { + data[i] = (uint8_t)(lsensor_adc_table[i] + * cdata->als_kadc / cdata->als_gadc >> 8); + data[i + 10] = (uint8_t)(lsensor_adc_table[i] + * cdata->als_kadc / cdata->als_gadc); + } + ret = i2c_write_block(client, MICROP_I2C_WCMD_ADC_TABLE, data, 20); + if (ret) + goto exit; + + ret = gpio_request(MAHIMAHI_GPIO_LS_EN_N, "microp_i2c"); + if (ret < 0) { + dev_err(&client->dev, "failed on request gpio ls_on\n"); + goto exit; + } + ret = gpio_direction_output(MAHIMAHI_GPIO_LS_EN_N, 0); + if (ret < 0) { + dev_err(&client->dev, "failed on gpio_direction_output" + "ls_on\n"); + goto err_gpio_ls; + } + cdata->light_sensor_enabled = 1; + + /* Headset */ + for (i = 0; i < 6; i++) { + data[i] = (uint8_t)(remote_key_adc_table[i] >> 8); + data[i + 6] = (uint8_t)(remote_key_adc_table[i]); + } + ret = i2c_write_block(client, + MICROP_I2C_WCMD_REMOTEKEY_TABLE, data, 12); + if (ret) + goto exit; + + INIT_DELAYED_WORK( + &cdata->hpin_debounce_work, hpin_debounce_do_work); + INIT_DELAYED_WORK( + &cdata->ls_read_work, ls_read_do_work); + + /* SD Card */ + interrupts |= IRQ_SDCARD; + + /* set LED initial state */ + for (i = 0; i < BLUE_LED; i++) { + led_cdev = &cdata->leds[i].ldev; + microp_i2c_write_led_mode(client, led_cdev, 0, 0xffff); + } + + /* enable the interrupts */ + ret = microp_interrupt_enable(client, interrupts); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable gpi irqs\n", + __func__); + goto err_irq_en; + } + + microp_read_gpi_status(client, &stat); + mahimahi_microp_sdslot_update_status(stat); + + return 0; + +err_irq_en: +err_gpio_ls: + gpio_free(MAHIMAHI_GPIO_LS_EN_N); +exit: + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void microp_early_suspend(struct early_suspend *h) +{ + struct microp_i2c_client_data *cdata; + struct i2c_client *client = private_microp_client; + int ret; + + if (!client) { + pr_err("%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + + cdata->microp_is_suspend = 1; + + disable_irq(client->irq); + ret = cancel_work_sync(&cdata->work.work); + if (ret != 0) { + enable_irq(client->irq); + } + + if (cdata->auto_backlight_enabled) + microp_i2c_auto_backlight_mode(client, 0); + if (cdata->light_sensor_enabled == 1) { + gpio_set_value(MAHIMAHI_GPIO_LS_EN_N, 1); + cdata->light_sensor_enabled = 0; + } +} + +void microp_early_resume(struct early_suspend *h) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + + if (!client) { + pr_err("%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + + gpio_set_value(MAHIMAHI_GPIO_LS_EN_N, 0); + cdata->light_sensor_enabled = 1; + + if (cdata->auto_backlight_enabled) + microp_i2c_auto_backlight_mode(client, 1); + + cdata->microp_is_suspend = 0; + enable_irq(client->irq); +} +#endif + +static int microp_i2c_suspend(struct i2c_client *client, + pm_message_t mesg) +{ + return 0; +} + +static int microp_i2c_resume(struct i2c_client *client) +{ + return 0; +} + +static struct { + const char *name; + void (*led_set_work)(struct work_struct *); + struct device_attribute **attrs; + int attr_cnt; +} microp_leds[] = { + [GREEN_LED] = { + .name = "green", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [AMBER_LED] = { + .name = "amber", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [RED_LED] = { + .name = "red", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [BLUE_LED] = { + .name = "blue", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [JOGBALL_LED] = { + .name = "jogball-backlight", + .led_set_work = microp_led_jogball_brightness_set_work, + .attrs = jogball_attrs, + .attr_cnt = ARRAY_SIZE(jogball_attrs) + }, + [BUTTONS_LED] = { + .name = "button-backlight", + .led_set_work = microp_led_buttons_brightness_set_work + }, +}; + +static int microp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct microp_i2c_client_data *cdata; + uint8_t data[6]; + int ret; + int i; + int j; + + private_microp_client = client; + ret = i2c_read_block(client, MICROP_I2C_RCMD_VERSION, data, 2); + if (ret || !(data[0] && data[1])) { + ret = -ENODEV; + dev_err(&client->dev, "failed on get microp version\n"); + goto err_exit; + } + dev_info(&client->dev, "microp version [%02X][%02X]\n", + data[0], data[1]); + + ret = gpio_request(MAHIMAHI_GPIO_UP_RESET_N, "microp_i2c_wm"); + if (ret < 0) { + dev_err(&client->dev, "failed on request gpio reset\n"); + goto err_exit; + } + ret = gpio_direction_output(MAHIMAHI_GPIO_UP_RESET_N, 1); + if (ret < 0) { + dev_err(&client->dev, + "failed on gpio_direction_output reset\n"); + goto err_gpio_reset; + } + + cdata = kzalloc(sizeof(struct microp_i2c_client_data), GFP_KERNEL); + if (!cdata) { + ret = -ENOMEM; + dev_err(&client->dev, "failed on allocat cdata\n"); + goto err_cdata; + } + + i2c_set_clientdata(client, cdata); + cdata->version = data[0] << 8 | data[1]; + cdata->microp_is_suspend = 0; + cdata->auto_backlight_enabled = 0; + cdata->light_sensor_enabled = 0; + + wake_lock_init(µp_i2c_wakelock, WAKE_LOCK_SUSPEND, + "microp_i2c_present"); + + /* Light Sensor */ + ret = device_create_file(&client->dev, &dev_attr_ls_adc); + ret = device_create_file(&client->dev, &dev_attr_ls_auto); + cdata->ls_input_dev = input_allocate_device(); + if (!cdata->ls_input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + ret = -ENOMEM; + goto err_request_input_dev; + } + cdata->ls_input_dev->name = "lightsensor-level"; + set_bit(EV_ABS, cdata->ls_input_dev->evbit); + input_set_abs_params(cdata->ls_input_dev, ABS_MISC, 0, 9, 0, 0); + + ret = input_register_device(cdata->ls_input_dev); + if (ret < 0) { + dev_err(&client->dev, "%s: can not register input device\n", + __func__); + goto err_register_input_dev; + } + + ret = misc_register(&lightsensor_misc); + if (ret < 0) { + dev_err(&client->dev, "%s: can not register misc device\n", + __func__); + goto err_register_misc_register; + } + + /* LEDs */ + ret = 0; + for (i = 0; i < ARRAY_SIZE(microp_leds) && !ret; ++i) { + struct microp_led_data *ldata = &cdata->leds[i]; + + ldata->type = i; + ldata->ldev.name = microp_leds[i].name; + ldata->ldev.brightness_set = microp_brightness_set; + mutex_init(&ldata->led_data_mutex); + INIT_WORK(&ldata->brightness_work, microp_leds[i].led_set_work); + spin_lock_init(&ldata->brightness_lock); + ret = led_classdev_register(&client->dev, &ldata->ldev); + if (ret) { + ldata->ldev.name = NULL; + break; + } + + for (j = 0; j < microp_leds[i].attr_cnt && !ret; ++j) + ret = device_create_file(ldata->ldev.dev, + microp_leds[i].attrs[j]); + } + if (ret) { + dev_err(&client->dev, "failed to add leds\n"); + goto err_add_leds; + } + + /* Headset */ + cdata->headset_is_in = 0; + cdata->is_hpin_pin_stable = 1; + platform_device_register(&mahimahi_h35mm); + + ret = device_create_file(&client->dev, &dev_attr_key_adc); + + /* G-sensor */ + ret = misc_register(&spi_bma_device); + if (ret < 0) { + pr_err("%s: init bma150 misc_register fail\n", + __func__); + goto err_register_bma150; + } +#ifdef DEBUG_BMA150 + debugfs_create_file("gsensor_log", 0444, NULL, NULL, &gsensor_log_fops); +#endif + /* Setup IRQ handler */ + INIT_WORK(&cdata->work.work, microp_i2c_intr_work_func); + cdata->work.client = client; + + ret = request_irq(client->irq, + microp_i2c_intr_irq_handler, + IRQF_TRIGGER_LOW, + "microp_interrupt", + &client->dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_intr; + } + ret = set_irq_wake(client->irq, 1); + if (ret) { + dev_err(&client->dev, "set_irq_wake failed\n"); + goto err_intr; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (cdata->enable_early_suspend) { + cdata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + cdata->early_suspend.suspend = microp_early_suspend; + cdata->early_suspend.resume = microp_early_resume; + register_early_suspend(&cdata->early_suspend); + } +#endif + + ret = microp_function_initialize(client); + if (ret) { + dev_err(&client->dev, "failed on microp function initialize\n"); + goto err_fun_init; + } + + return 0; + +err_fun_init: +err_intr: + misc_deregister(&spi_bma_device); + +err_register_bma150: + platform_device_unregister(&mahimahi_h35mm); + device_remove_file(&client->dev, &dev_attr_key_adc); + +err_add_leds: + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + if (!cdata->leds[i].ldev.name) + continue; + led_classdev_unregister(&cdata->leds[i].ldev); + for (j = 0; j < microp_leds[i].attr_cnt; ++j) + device_remove_file(cdata->leds[i].ldev.dev, + microp_leds[i].attrs[j]); + } + + misc_deregister(&lightsensor_misc); + +err_register_misc_register: + input_unregister_device(cdata->ls_input_dev); + +err_register_input_dev: + input_free_device(cdata->ls_input_dev); + +err_request_input_dev: + wake_lock_destroy(µp_i2c_wakelock); + device_remove_file(&client->dev, &dev_attr_ls_adc); + device_remove_file(&client->dev, &dev_attr_ls_auto); + kfree(cdata); + i2c_set_clientdata(client, NULL); + +err_cdata: +err_gpio_reset: + gpio_free(MAHIMAHI_GPIO_UP_RESET_N); +err_exit: + return ret; +} + +static int __devexit microp_i2c_remove(struct i2c_client *client) +{ + struct microp_i2c_client_data *cdata; + int i; + int j; + + cdata = i2c_get_clientdata(client); + + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + struct microp_led_data *ldata = &cdata->leds[i]; + cancel_work_sync(&ldata->brightness_work); + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (cdata->enable_early_suspend) { + unregister_early_suspend(&cdata->early_suspend); + } +#endif + + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + if (!cdata->leds[i].ldev.name) + continue; + led_classdev_unregister(&cdata->leds[i].ldev); + for (j = 0; j < microp_leds[i].attr_cnt; ++j) + device_remove_file(cdata->leds[i].ldev.dev, + microp_leds[i].attrs[j]); + } + + free_irq(client->irq, &client->dev); + + gpio_free(MAHIMAHI_GPIO_UP_RESET_N); + + misc_deregister(&lightsensor_misc); + input_unregister_device(cdata->ls_input_dev); + input_free_device(cdata->ls_input_dev); + device_remove_file(&client->dev, &dev_attr_ls_adc); + device_remove_file(&client->dev, &dev_attr_key_adc); + device_remove_file(&client->dev, &dev_attr_ls_auto); + + platform_device_unregister(&mahimahi_h35mm); + + /* G-sensor */ + misc_deregister(&spi_bma_device); + + kfree(cdata); + + return 0; +} + +#define ATAG_ALS 0x5441001b +static int __init parse_tag_als_kadc(const struct tag *tags) +{ + int found = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ALS) { + found = 1; + break; + } + } + + if (found) + als_kadc = t->u.revision.rev; + pr_debug("%s: als_kadc = 0x%x\n", __func__, als_kadc); + return 0; +} +__tagtable(ATAG_ALS, parse_tag_als_kadc); + +static const struct i2c_device_id microp_i2c_id[] = { + { MICROP_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver microp_i2c_driver = { + .driver = { + .name = MICROP_I2C_NAME, + }, + .id_table = microp_i2c_id, + .probe = microp_i2c_probe, + .suspend = microp_i2c_suspend, + .resume = microp_i2c_resume, + .remove = __devexit_p(microp_i2c_remove), +}; + + +static int __init microp_i2c_init(void) +{ + return i2c_add_driver(µp_i2c_driver); +} + +static void __exit microp_i2c_exit(void) +{ + i2c_del_driver(µp_i2c_driver); +} + +module_init(microp_i2c_init); +module_exit(microp_i2c_exit); + +MODULE_AUTHOR("Eric Olsen "); +MODULE_DESCRIPTION("MicroP I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c new file mode 100644 index 00000000000..dc577814750 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -0,0 +1,454 @@ +/* linux/arch/arm/mach-msm/board-mahimahi-mmc.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "board-mahimahi.h" +#include "devices.h" + +#undef MAHIMAHI_DEBUG_MMC + +static bool opt_disable_sdcard; +static int __init mahimahi_disablesdcard_setup(char *str) +{ + opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0); + return 1; +} + +__setup("board_mahimahi.disable_sdcard=", mahimahi_disablesdcard_setup); + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static struct vreg *sdslot_vreg; +static uint32_t sdslot_vdd = 0xffffffff; +static uint32_t sdslot_vreg_enabled; + +static struct { + int mask; + int level; +} mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static uint32_t mahimahi_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i; + int ret; + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(sdslot_vreg); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + ret = vreg_enable(sdslot_vreg); + if (ret) + pr_err("%s: Error enabling vreg (%d)\n", __func__, ret); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; + ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level); + if (ret) + pr_err("%s: Error setting level (%d)\n", __func__, ret); + return 0; + } + + pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd); + return 0; +} + +static uint32_t mahimahi_cdma_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + if (!vdd == !sdslot_vdd) + return 0; + + /* In CDMA version, the vdd of sdslot is not configurable, and it is + * fixed in 2.85V by hardware design. + */ + + sdslot_vdd = vdd ? MMC_VDD_28_29 : 0; + + if (vdd) { + gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 1); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + } else { + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 0); + } + + sdslot_vreg_enabled = !!vdd; + + return 0; +} + +static unsigned int mahimahi_sdslot_status_rev0(struct device *dev) +{ + return !gpio_get_value(MAHIMAHI_GPIO_SDMC_CD_REV0_N); +} + +#define MAHIMAHI_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | \ + MMC_VDD_21_22 | MMC_VDD_22_23 | \ + MMC_VDD_23_24 | MMC_VDD_24_25 | \ + MMC_VDD_25_26 | MMC_VDD_26_27 | \ + MMC_VDD_27_28 | MMC_VDD_28_29 | \ + MMC_VDD_29_30) + +int mahimahi_microp_sdslot_status_register(void (*cb)(int, void *), void *); +unsigned int mahimahi_microp_sdslot_status(struct device *); + +static struct mmc_platform_data mahimahi_sdslot_data = { + .ocr_mask = MAHIMAHI_MMC_VDD, + .status = mahimahi_microp_sdslot_status, + .register_status_notify = mahimahi_microp_sdslot_status_register, + .translate_vdd = mahimahi_sdslot_switchvdd, +}; + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +/* BCM4329 returns wrong sdio_vsn(1) when we read cccr, + * we use predefined value (sdio_vsn=2) here to initial sdio driver well + */ +static struct embedded_sdio_data mahimahi_wifi_emb_data = { + .cccr = { + .sdio_vsn = 2, + .multi_block = 1, + .low_speed = 0, + .wide_bus = 0, + .high_power = 1, + .high_speed = 1, + }, +}; + +static int mahimahi_wifi_cd = 0; /* WIFI virtual 'card detect' status */ +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int mahimahi_wifi_status_register( + void (*callback)(int card_present, void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int mahimahi_wifi_status(struct device *dev) +{ + return mahimahi_wifi_cd; +} + +static struct mmc_platform_data mahimahi_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .built_in = 1, + .status = mahimahi_wifi_status, + .register_status_notify = mahimahi_wifi_status_register, + .embedded_sdio = &mahimahi_wifi_emb_data, +}; + +int mahimahi_wifi_set_carddetect(int val) +{ + pr_info("%s: %d\n", __func__, val); + mahimahi_wifi_cd = val; + if (wifi_status_cb) { + wifi_status_cb(val, wifi_status_cb_devid); + } else + pr_warning("%s: Nobody to notify\n", __func__); + return 0; +} + +static int mahimahi_wifi_power_state; + +int mahimahi_wifi_power(int on) +{ + printk("%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + mdelay(50); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + } + + mdelay(100); + gpio_set_value(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ + mdelay(200); + + mahimahi_wifi_power_state = on; + return 0; +} + +static int mahimahi_wifi_reset_state; + +int mahimahi_wifi_reset(int on) +{ + printk("%s: do nothing\n", __func__); + mahimahi_wifi_reset_state = on; + return 0; +} + +int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + +int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart) +{ + uint32_t id; + + printk("%s()+\n", __func__); + + /* initial WIFI_SHUTDOWN# */ + id = PCOM_GPIO_CFG(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + + msm_add_sdcc(1, &mahimahi_wifi_data, 0, 0); + + if (debug_uart) { + pr_info("%s: sdcard disabled due to debug uart\n", __func__); + goto done; + } + if (opt_disable_sdcard) { + pr_info("%s: sdcard disabled on cmdline\n", __func__); + goto done; + } + + sdslot_vreg_enabled = 0; + + if (is_cdma_version(sys_rev)) { + /* In the CDMA version, sdslot is supplied by a gpio. */ + int rc = gpio_request(MAHIMAHI_CDMA_SD_2V85_EN, "sdslot_en"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_CDMA_SD_2V85_EN, rc); + return rc; + } + mahimahi_sdslot_data.translate_vdd = mahimahi_cdma_sdslot_switchvdd; + } else { + /* in UMTS version, sdslot is supplied by pmic */ + sdslot_vreg = vreg_get(0, "gp6"); + if (IS_ERR(sdslot_vreg)) + return PTR_ERR(sdslot_vreg); + } + + if (system_rev > 0) + msm_add_sdcc(2, &mahimahi_sdslot_data, 0, 0); + else { + mahimahi_sdslot_data.status = mahimahi_sdslot_status_rev0; + mahimahi_sdslot_data.register_status_notify = NULL; + set_irq_wake(MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), 1); + msm_add_sdcc(2, &mahimahi_sdslot_data, + MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), + IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE); + } + +done: + printk("%s()-\n", __func__); + return 0; +} + +#if defined(MAHIMAHI_DEBUG_MMC) && defined(CONFIG_DEBUG_FS) + +static int mahimahimmc_dbg_wifi_reset_set(void *data, u64 val) +{ + mahimahi_wifi_reset((int) val); + return 0; +} + +static int mahimahimmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = mahimahi_wifi_reset_state; + return 0; +} + +static int mahimahimmc_dbg_wifi_cd_set(void *data, u64 val) +{ + mahimahi_wifi_set_carddetect((int) val); + return 0; +} + +static int mahimahimmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = mahimahi_wifi_cd; + return 0; +} + +static int mahimahimmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + mahimahi_wifi_power((int) val); + return 0; +} + +static int mahimahimmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + *val = mahimahi_wifi_power_state; + return 0; +} + +static int mahimahimmc_dbg_sd_pwr_set(void *data, u64 val) +{ + mahimahi_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int mahimahimmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int mahimahimmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int mahimahimmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = mahimahi_sdslot_data.status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_reset_fops, + mahimahimmc_dbg_wifi_reset_get, + mahimahimmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_cd_fops, + mahimahimmc_dbg_wifi_cd_get, + mahimahimmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_pwr_fops, + mahimahimmc_dbg_wifi_pwr_get, + mahimahimmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_pwr_fops, + mahimahimmc_dbg_sd_pwr_get, + mahimahimmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_cd_fops, + mahimahimmc_dbg_sd_cd_get, + mahimahimmc_dbg_sd_cd_set, "%llu\n"); + +static int __init mahimahimmc_dbg_init(void) +{ + struct dentry *dent; + + if (!machine_is_mahimahi()) + return 0; + + dent = debugfs_create_dir("mahimahi_mmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &mahimahimmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &mahimahimmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &mahimahimmc_dbg_wifi_pwr_fops); + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &mahimahimmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &mahimahimmc_dbg_sd_cd_fops); + return 0; +} + +device_initcall(mahimahimmc_dbg_init); +#endif diff --git a/arch/arm/mach-msm/board-mahimahi-panel.c b/arch/arm/mach-msm/board-mahimahi-panel.c new file mode 100644 index 00000000000..31ec742293e --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-panel.c @@ -0,0 +1,998 @@ +/* linux/arch/arm/mach-msm/board-mahimahi-panel.c + * + * Copyright (c) 2009 Google Inc. + * Author: Dima Zavin + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "board-mahimahi.h" +#include "devices.h" + + +#define SPI_CONFIG (0x00000000) +#define SPI_IO_CONTROL (0x00000004) +#define SPI_OPERATIONAL (0x00000030) +#define SPI_ERROR_FLAGS_EN (0x00000038) +#define SPI_ERROR_FLAGS (0x00000038) +#define SPI_OUTPUT_FIFO (0x00000100) + +static void __iomem *spi_base; +static struct clk *spi_clk ; +static struct vreg *vreg_lcm_rftx_2v6; +static struct vreg *vreg_lcm_aux_2v6; + +static int qspi_send(uint32_t id, uint8_t data) +{ + uint32_t err; + + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + if ((err = readl(spi_base + SPI_ERROR_FLAGS))) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int qspi_send_9bit(uint32_t id, uint8_t data) +{ + uint32_t err; + + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + err = readl(spi_base + SPI_ERROR_FLAGS); + if (err) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int lcm_writeb(uint8_t reg, uint8_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val); + return 0; +} + +static int lcm_writew(uint8_t reg, uint16_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val >> 8); + qspi_send(0x1, val & 0xff); + return 0; +} + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct lcm_tbl { + uint8_t reg; + uint8_t val; +}; + +static struct lcm_tbl samsung_oled_rgb565_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x02 }, + { 0x39, 0x24 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, +}; + +static struct lcm_tbl samsung_oled_rgb666_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x01 }, + { 0x39, 0x24 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, +}; + +static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table; +static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table); + +#define OLED_GAMMA_TABLE_SIZE (7 * 3) +static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { + /* level 10 */ + { + /* Gamma-R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x3f }, + { 0x43, 0x35 }, + { 0x44, 0x30 }, + { 0x45, 0x2c }, + { 0x46, 0x13 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x0 }, + { 0x54, 0x27 }, + { 0x55, 0x2b }, + { 0x56, 0x12 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x3f }, + { 0x63, 0x34 }, + { 0x64, 0x2f }, + { 0x65, 0x2b }, + { 0x66, 0x1b }, + }, + + /* level 40 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x3e }, + { 0x43, 0x2e }, + { 0x44, 0x2d }, + { 0x45, 0x28 }, + { 0x46, 0x21 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x21 }, + { 0x54, 0x2a }, + { 0x55, 0x28 }, + { 0x56, 0x20 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x3e }, + { 0x63, 0x2d }, + { 0x64, 0x2b }, + { 0x65, 0x26 }, + { 0x66, 0x2d }, + }, + + /* level 70 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x35 }, + { 0x43, 0x2c }, + { 0x44, 0x2b }, + { 0x45, 0x26 }, + { 0x46, 0x29 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x25 }, + { 0x54, 0x29 }, + { 0x55, 0x26 }, + { 0x56, 0x28 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x34 }, + { 0x63, 0x2b }, + { 0x64, 0x2a }, + { 0x65, 0x23 }, + { 0x66, 0x37 }, + }, + + /* level 100 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x30 }, + { 0x43, 0x2a }, + { 0x44, 0x2b }, + { 0x45, 0x24 }, + { 0x46, 0x2f }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x25 }, + { 0x54, 0x29 }, + { 0x55, 0x24 }, + { 0x56, 0x2e }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2f }, + { 0x63, 0x29 }, + { 0x64, 0x29 }, + { 0x65, 0x21 }, + { 0x66, 0x3f }, + }, + + /* level 130 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2e }, + { 0x43, 0x29 }, + { 0x44, 0x2a }, + { 0x45, 0x23 }, + { 0x46, 0x34 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0xa }, + { 0x53, 0x25 }, + { 0x54, 0x28 }, + { 0x55, 0x23 }, + { 0x56, 0x33 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2d }, + { 0x63, 0x28 }, + { 0x64, 0x27 }, + { 0x65, 0x20 }, + { 0x66, 0x46 }, + }, + + /* level 160 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2b }, + { 0x43, 0x29 }, + { 0x44, 0x28 }, + { 0x45, 0x23 }, + { 0x46, 0x38 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0xb }, + { 0x53, 0x25 }, + { 0x54, 0x27 }, + { 0x55, 0x23 }, + { 0x56, 0x37 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x29 }, + { 0x63, 0x28 }, + { 0x64, 0x25 }, + { 0x65, 0x20 }, + { 0x66, 0x4b }, + }, + + /* level 190 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x29 }, + { 0x43, 0x29 }, + { 0x44, 0x27 }, + { 0x45, 0x22 }, + { 0x46, 0x3c }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x10 }, + { 0x53, 0x26 }, + { 0x54, 0x26 }, + { 0x55, 0x22 }, + { 0x56, 0x3b }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x28 }, + { 0x63, 0x28 }, + { 0x64, 0x24 }, + { 0x65, 0x1f }, + { 0x66, 0x50 }, + }, + + /* level 220 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x28 }, + { 0x43, 0x28 }, + { 0x44, 0x28 }, + { 0x45, 0x20 }, + { 0x46, 0x40 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x11 }, + { 0x53, 0x25 }, + { 0x54, 0x27 }, + { 0x55, 0x20 }, + { 0x56, 0x3f }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x27 }, + { 0x63, 0x26 }, + { 0x64, 0x26 }, + { 0x65, 0x1c }, + { 0x66, 0x56 }, + }, + + /* level 250 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2a }, + { 0x43, 0x27 }, + { 0x44, 0x27 }, + { 0x45, 0x1f }, + { 0x46, 0x44 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x17 }, + { 0x53, 0x24 }, + { 0x54, 0x26 }, + { 0x55, 0x1f }, + { 0x56, 0x43 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2a }, + { 0x63, 0x25 }, + { 0x64, 0x24 }, + { 0x65, 0x1b }, + { 0x66, 0x5c }, + }, +}; +#define SAMSUNG_OLED_NUM_LEVELS ARRAY_SIZE(samsung_oled_gamma_table) + +#define SAMSUNG_OLED_MIN_VAL 10 +#define SAMSUNG_OLED_MAX_VAL 250 +#define SAMSUNG_OLED_DEFAULT_VAL (SAMSUNG_OLED_MIN_VAL + \ + (SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / 2) + +#define SAMSUNG_OLED_LEVEL_STEP ((SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / \ + (SAMSUNG_OLED_NUM_LEVELS - 1)) + + +#define SONY_TFT_DEF_USER_VAL 102 +#define SONY_TFT_MIN_USER_VAL 30 +#define SONY_TFT_MAX_USER_VAL 255 +#define SONY_TFT_DEF_PANEL_VAL 155 +#define SONY_TFT_MIN_PANEL_VAL 26 +#define SONY_TFT_MAX_PANEL_VAL 255 + + +static DEFINE_MUTEX(panel_lock); +static struct work_struct brightness_delayed_work; +static DEFINE_SPINLOCK(brightness_lock); +static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t table_sel_vals[] = { 0x43, 0x34 }; +static int table_sel_idx = 0; +static uint8_t tft_panel_on; + +static void gamma_table_bank_select(void) +{ + lcm_writeb(0x39, table_sel_vals[table_sel_idx]); + table_sel_idx ^= 1; +} + +static void samsung_oled_set_gamma_val(int val) +{ + int i; + int level; + int frac; + + val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL); + val = (val / 2) * 2; + + level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP; + frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP; + + clk_enable(spi_clk); + + for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) { + unsigned int v1; + unsigned int v2 = 0; + u8 v; + if (frac == 0) { + v = samsung_oled_gamma_table[level][i].val; + } else { + + v1 = samsung_oled_gamma_table[level][i].val; + v2 = samsung_oled_gamma_table[level+1][i].val; + v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) + + v2 * frac) / SAMSUNG_OLED_LEVEL_STEP; + } + lcm_writeb(samsung_oled_gamma_table[level][i].reg, v); + } + + gamma_table_bank_select(); + clk_disable(spi_clk); + last_val = val; +} + +static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + /* Set the gamma write target to 4, leave the current gamma set at 2 */ + lcm_writeb(0x39, 0x24); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + int i; + + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + udelay(50); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + udelay(20); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + msleep(20); + + clk_enable(spi_clk); + + for (i = 0; i < init_table_sz; i++) + lcm_writeb(init_tablep[i].reg, init_tablep[i].val); + + lcm_writew(0xef, 0xd0e8); + lcm_writeb(0x1d, 0xa0); + table_sel_idx = 0; + gamma_table_bank_select(); + samsung_oled_set_gamma_val(last_val); + msleep(250); + lcm_writeb(0x14, 0x03); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + lcm_writeb(0x14, 0x0); + mdelay(1); + lcm_writeb(0x1d, 0xa1); + clk_disable(spi_clk); + msleep(200); + + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +struct lcm_cmd { + int reg; + uint32_t val; + unsigned delay; +}; + +#define LCM_GPIO_CFG(gpio, func, str) \ + PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str) + +static uint32_t sony_tft_display_on_gpio_table[] = { + LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 1, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 1, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 1, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 1, GPIO_4MA), +}; + +static uint32_t sony_tft_display_off_gpio_table[] = { + LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 0, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 0, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 0, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 0, GPIO_4MA), +}; + +#undef LCM_GPIO_CFG + +#define SONY_TFT_DEF_PANEL_DELTA \ + (SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL) +#define SONY_TFT_DEF_USER_DELTA \ + (SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL) + +static void sony_tft_set_pwm_val(int val) +{ + pr_info("%s: %d\n", __func__, val); + + last_val = val; + + if (!tft_panel_on) + return; + + if (val <= SONY_TFT_DEF_USER_VAL) { + if (val <= SONY_TFT_MIN_USER_VAL) + val = SONY_TFT_MIN_PANEL_VAL; + else + val = SONY_TFT_DEF_PANEL_DELTA * + (val - SONY_TFT_MIN_USER_VAL) / + SONY_TFT_DEF_USER_DELTA + + SONY_TFT_MIN_PANEL_VAL; + } else + val = (SONY_TFT_MAX_PANEL_VAL - SONY_TFT_DEF_PANEL_VAL) * + (val - SONY_TFT_DEF_USER_VAL) / + (SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) + + SONY_TFT_DEF_PANEL_VAL; + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x51); + qspi_send_9bit(0x1, val); + qspi_send_9bit(0x0, 0x53); + qspi_send_9bit(0x1, 0x24); + clk_disable(spi_clk); +} + +#undef SONY_TFT_DEF_PANEL_DELTA +#undef SONY_TFT_DEF_USER_DELTA + +static void sony_tft_panel_config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + + +static int sony_tft_panel_power(int on) +{ + unsigned id, on_off; + + if (on) { + on_off = 0; + + vreg_enable(vreg_lcm_aux_2v6); + vreg_enable(vreg_lcm_rftx_2v6); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + mdelay(10); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + mdelay(10); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + udelay(500); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + mdelay(10); + sony_tft_panel_config_gpio_table( + sony_tft_display_on_gpio_table, + ARRAY_SIZE(sony_tft_display_on_gpio_table)); + } else { + on_off = 1; + + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + + mdelay(120); + + vreg_disable(vreg_lcm_rftx_2v6); + vreg_disable(vreg_lcm_aux_2v6); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + sony_tft_panel_config_gpio_table( + sony_tft_display_off_gpio_table, + ARRAY_SIZE(sony_tft_display_off_gpio_table)); + } + return 0; +} + +static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops) +{ + return 0; +} + +static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + if (tft_panel_on) { + pr_info("%s: -() already unblanked\n", __func__); + goto done; + } + + sony_tft_panel_power(1); + msleep(45); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x11); + msleep(5); + qspi_send_9bit(0x0, 0x3a); + qspi_send_9bit(0x1, 0x05); + msleep(100); + qspi_send_9bit(0x0, 0x29); + /* unlock register page for pwm setting */ + qspi_send_9bit(0x0, 0xf0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xf1); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xd0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + + qspi_send_9bit(0x0, 0xc2); + qspi_send_9bit(0x1, 0x53); + qspi_send_9bit(0x1, 0x12); + clk_disable(spi_clk); + msleep(100); + tft_panel_on = 1; + sony_tft_set_pwm_val(last_val); + + pr_info("%s: -()\n", __func__); +done: + mutex_unlock(&panel_lock); + return 0; +} + +static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x28); + qspi_send_9bit(0x0, 0x10); + clk_disable(spi_clk); + + msleep(40); + sony_tft_panel_power(0); + tft_panel_on = 0; + + mutex_unlock(&panel_lock); + + pr_info("%s: -()\n", __func__); + return 0; +} + +static struct msm_lcdc_panel_ops mahimahi_lcdc_amoled_panel_ops = { + .init = samsung_oled_panel_init, + .blank = samsung_oled_panel_blank, + .unblank = samsung_oled_panel_unblank, +}; + +static struct msm_lcdc_panel_ops mahimahi_lcdc_tft_panel_ops = { + .init = sony_tft_panel_init, + .blank = sony_tft_panel_blank, + .unblank = sony_tft_panel_unblank, +}; + + +static struct msm_lcdc_timing mahimahi_lcdc_amoled_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 4, + .hsync_back_porch = 8, + .hsync_front_porch = 8, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 8, + .vsync_front_porch = 8, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 1, +}; + +static struct msm_lcdc_timing mahimahi_lcdc_tft_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 2, + .hsync_back_porch = 20, + .hsync_front_porch = 20, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 6, + .vsync_front_porch = 4, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, +}; + +static struct msm_fb_data mahimahi_lcdc_fb_data = { + .xres = 480, + .yres = 800, + .width = 48, + .height = 80, + .output_format = MSM_MDP_OUT_IF_FMT_RGB565, +}; + +static struct msm_lcdc_platform_data mahimahi_lcdc_amoled_platform_data = { + .panel_ops = &mahimahi_lcdc_amoled_panel_ops, + .timing = &mahimahi_lcdc_amoled_timing, + .fb_id = 0, + .fb_data = &mahimahi_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct msm_lcdc_platform_data mahimahi_lcdc_tft_platform_data = { + .panel_ops = &mahimahi_lcdc_tft_panel_ops, + .timing = &mahimahi_lcdc_tft_timing, + .fb_id = 0, + .fb_data = &mahimahi_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct platform_device mahimahi_lcdc_amoled_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &mahimahi_lcdc_amoled_platform_data, + }, +}; + +static struct platform_device mahimahi_lcdc_tft_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &mahimahi_lcdc_tft_platform_data, + }, +}; + +static int mahimahi_init_spi_hack(void) +{ + int ret; + + spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE); + if (!spi_base) + return -1; + + spi_clk = clk_get(&msm_device_spi.dev, "spi_clk"); + if (IS_ERR(spi_clk)) { + pr_err("%s: unable to get spi_clk\n", __func__); + ret = PTR_ERR(spi_clk); + goto err_clk_get; + } + + clk_enable(spi_clk); + + printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG)); + printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL)); + printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL)); + printk("spi: SPI_ERROR_FLAGS_EN=%x\n", + readl(spi_base + SPI_ERROR_FLAGS_EN)); + printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS)); + printk("-%s()\n", __FUNCTION__); + clk_disable(spi_clk); + + return 0; + +err_clk_get: + iounmap(spi_base); + return ret; +} + +static void mahimahi_brightness_set(struct led_classdev *led_cdev, + enum led_brightness val) +{ + unsigned long flags; + led_cdev->brightness = val; + + spin_lock_irqsave(&brightness_lock, flags); + new_val = val; + spin_unlock_irqrestore(&brightness_lock, flags); + + schedule_work(&brightness_delayed_work); +} + +static void mahimahi_brightness_amoled_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + samsung_oled_set_gamma_val(val); + mutex_unlock(&panel_lock); +} + +static void mahimahi_brightness_tft_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + sony_tft_set_pwm_val(val); + mutex_unlock(&panel_lock); +} + +static struct led_classdev mahimahi_brightness_led = { + .name = "lcd-backlight", + .brightness = LED_FULL, + .brightness_set = mahimahi_brightness_set, +}; + +int __init mahimahi_init_panel(void) +{ + int ret; + + if (!machine_is_mahimahi()) + return 0; + + if (system_rev > 0xC0) { + /* CDMA version (except for EVT1) supports RGB666 */ + init_tablep = samsung_oled_rgb666_init_table; + init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table); + mahimahi_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666; + } + + ret = platform_device_register(&msm_device_mdp); + if (ret != 0) + return ret; + + ret = mahimahi_init_spi_hack(); + if (ret != 0) + return ret; + + if (gpio_get_value(MAHIMAHI_GPIO_LCD_ID0)) { + pr_info("%s: tft panel\n", __func__); + vreg_lcm_rftx_2v6 = vreg_get(0, "rftx"); + if (IS_ERR(vreg_lcm_rftx_2v6)) + return PTR_ERR(vreg_lcm_rftx_2v6); + vreg_set_level(vreg_lcm_rftx_2v6, 2600); + + vreg_lcm_aux_2v6 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcm_aux_2v6)) + return PTR_ERR(vreg_lcm_aux_2v6); + + if (gpio_get_value(MAHIMAHI_GPIO_LCD_RST_N)) + tft_panel_on = 1; + ret = platform_device_register(&mahimahi_lcdc_tft_device); + INIT_WORK(&brightness_delayed_work, mahimahi_brightness_tft_set_work); + } else { + pr_info("%s: amoled panel\n", __func__); + ret = platform_device_register(&mahimahi_lcdc_amoled_device); + INIT_WORK(&brightness_delayed_work, mahimahi_brightness_amoled_set_work); + } + + if (ret != 0) + return ret; + + ret = led_classdev_register(NULL, &mahimahi_brightness_led); + if (ret != 0) { + pr_err("%s: Cannot register brightness led\n", __func__); + return ret; + } + + return 0; +} + +device_initcall(mahimahi_init_panel); diff --git a/arch/arm/mach-msm/board-mahimahi-rfkill.c b/arch/arm/mach-msm/board-mahimahi-rfkill.c new file mode 100644 index 00000000000..05c9bb0b4d5 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-rfkill.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi.h" + +static struct rfkill *bt_rfk; +static const char bt_name[] = "bcm4329"; + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + gpio_direction_output(MAHIMAHI_GPIO_BT_RESET_N, 1); + gpio_direction_output(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 1); + } else { + gpio_direction_output(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0); + gpio_direction_output(MAHIMAHI_GPIO_BT_RESET_N, 0); + } + return 0; +} + +static struct rfkill_ops mahimahi_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int mahimahi_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + rc = gpio_request(MAHIMAHI_GPIO_BT_RESET_N, "bt_reset"); + if (rc) + goto err_gpio_reset; + rc = gpio_request(MAHIMAHI_GPIO_BT_SHUTDOWN_N, "bt_shutdown"); + if (rc) + goto err_gpio_shutdown; + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &mahimahi_rfkill_ops, NULL); + if (!bt_rfk) { + rc = -ENOMEM; + goto err_rfkill_alloc; + } + + rfkill_set_states(bt_rfk, default_state, false); + + /* userspace cannot take exclusive control */ + + rc = rfkill_register(bt_rfk); + if (rc) + goto err_rfkill_reg; + + return 0; + +err_rfkill_reg: + rfkill_destroy(bt_rfk); +err_rfkill_alloc: + gpio_free(MAHIMAHI_GPIO_BT_SHUTDOWN_N); +err_gpio_shutdown: + gpio_free(MAHIMAHI_GPIO_BT_RESET_N); +err_gpio_reset: + return rc; +} + +static int mahimahi_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + gpio_free(MAHIMAHI_GPIO_BT_SHUTDOWN_N); + gpio_free(MAHIMAHI_GPIO_BT_RESET_N); + + return 0; +} + +static struct platform_driver mahimahi_rfkill_driver = { + .probe = mahimahi_rfkill_probe, + .remove = mahimahi_rfkill_remove, + .driver = { + .name = "mahimahi_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init mahimahi_rfkill_init(void) +{ + if (!machine_is_mahimahi()) + return 0; + + return platform_driver_register(&mahimahi_rfkill_driver); +} + +static void __exit mahimahi_rfkill_exit(void) +{ + platform_driver_unregister(&mahimahi_rfkill_driver); +} + +module_init(mahimahi_rfkill_init); +module_exit(mahimahi_rfkill_exit); +MODULE_DESCRIPTION("mahimahi rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-smb329.c b/arch/arm/mach-msm/board-mahimahi-smb329.c new file mode 100644 index 00000000000..b80db78491e --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-smb329.c @@ -0,0 +1,177 @@ +/* drivers/i2c/chips/smb329.c + * + * SMB329B Switch Charger (SUMMIT Microelectronics) + * + * Copyright (C) 2009 HTC Corporation + * Author: Justin Lin + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi-smb329.h" + +static struct smb329_data { + struct i2c_client *client; + uint8_t version; + struct work_struct work; + struct mutex state_lock; + int chg_state; +} smb329; + +static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg; + + /* write the first byte of buffer as the register address */ + value[0] = reg; + msg.addr = smb329.client->addr; + msg.len = num_bytes + 1; + msg.flags = 0; + msg.buf = value; + + ret = i2c_transfer(smb329.client->adapter, &msg, 1); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg[2]; + + /* setup the address to read */ + msg[0].addr = smb329.client->addr; + msg[0].len = 1; + msg[0].flags = 0; + msg[0].buf = ® + + /* setup the read buffer */ + msg[1].addr = smb329.client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = num_bytes; + msg[1].buf = value; + + ret = i2c_transfer(smb329.client->adapter, msg, 2); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_write_byte(uint8_t value, uint8_t reg) +{ + int ret; + uint8_t buf[2] = { 0 }; + + buf[1] = value; + ret = smb329_i2c_write(buf, reg, 1); + if (ret) + pr_err("smb329: write byte error (%d)\n", ret); + + return ret; +} + +static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg) +{ + int ret = smb329_i2c_read(value, reg, 1); + if (ret) + pr_err("smb329: read byte error (%d)\n", ret); + + return ret; +} + +int smb329_set_charger_ctrl(uint32_t ctl) +{ + mutex_lock(&smb329.state_lock); + smb329.chg_state = ctl; + schedule_work(&smb329.work); + mutex_unlock(&smb329.state_lock); + return 0; +} + +static void smb329_work_func(struct work_struct *work) +{ + mutex_lock(&smb329.state_lock); + + switch (smb329.chg_state) { + case SMB329_ENABLE_FAST_CHG: + pr_info("smb329: charger on (fast)\n"); + smb329_i2c_write_byte(0x84, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + if ((smb329.version & 0x18) == 0x0) + smb329_i2c_write_byte(0xA9, 0x00); + break; + + case SMB329_DISABLE_CHG: + case SMB329_ENABLE_SLOW_CHG: + pr_info("smb329: charger off/slow\n"); + smb329_i2c_write_byte(0x88, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + break; + default: + pr_err("smb329: unknown charger state %d\n", + smb329.chg_state); + } + + mutex_unlock(&smb329.state_lock); +} + +static int smb329_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "[SMB329]:I2C fail\n"); + return -EIO; + } + + smb329.client = client; + mutex_init(&smb329.state_lock); + INIT_WORK(&smb329.work, smb329_work_func); + + smb329_i2c_read_byte(&smb329.version, 0x3B); + pr_info("smb329 version: 0x%02x\n", smb329.version); + + return 0; +} + +static const struct i2c_device_id smb329_id[] = { + { "smb329", 0 }, + { }, +}; + +static struct i2c_driver smb329_driver = { + .driver.name = "smb329", + .id_table = smb329_id, + .probe = smb329_probe, +}; + +static int __init smb329_init(void) +{ + int ret = i2c_add_driver(&smb329_driver); + if (ret) + pr_err("smb329_init: failed\n"); + + return ret; +} + +module_init(smb329_init); + +MODULE_AUTHOR("Justin Lin "); +MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-smb329.h b/arch/arm/mach-msm/board-mahimahi-smb329.h new file mode 100644 index 00000000000..13b326fa71d --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-smb329.h @@ -0,0 +1,32 @@ +/* include/linux/smb329.h - smb329 switch charger driver + * + * Copyright (C) 2009 HTC Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_SMB329_H +#define _LINUX_SMB329_H + +#ifdef __KERNEL__ + +enum { + SMB329_DISABLE_CHG, + SMB329_ENABLE_SLOW_CHG, + SMB329_ENABLE_FAST_CHG, +}; + +extern int smb329_set_charger_ctrl(uint32_t ctl); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SMB329_H */ + diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c new file mode 100644 index 00000000000..78919b9b195 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c @@ -0,0 +1,368 @@ +/* drivers/i2c/chips/tpa2018d1.c + * + * TI TPA2018D1 Speaker Amplifier + * + * Copyright (C) 2009 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* TODO: content validation in TPA2018_SET_CONFIG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi-tpa2018d1.h" + +static struct i2c_client *this_client; +static struct tpa2018d1_platform_data *pdata; +static int is_on; +static char spk_amp_cfg[8]; +static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ + 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 +}; +static const char spk_amp_off[] = {0x01, 0xa2}; + +static DEFINE_MUTEX(spk_amp_lock); +static int tpa2018d1_opened; +static char *config_data; +static int tpa2018d1_num_modes; + +#define DEBUG 0 + +static int tpa2018_i2c_write(const char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } else + return 0; +} + +static int tpa2018_i2c_read(char *rxData, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } + +#if DEBUG + do { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: rx[%d] = %2x\n", + __func__, i, rxData[i]); + } while(0); +#endif + + return 0; +} + +static int tpa2018d1_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + mutex_lock(&spk_amp_lock); + + if (tpa2018d1_opened) { + pr_err("%s: busy\n", __func__); + rc = -EBUSY; + goto done; + } + + tpa2018d1_opened = 1; +done: + mutex_unlock(&spk_amp_lock); + return rc; +} + +static int tpa2018d1_release(struct inode *inode, struct file *file) +{ + mutex_lock(&spk_amp_lock); + tpa2018d1_opened = 0; + mutex_unlock(&spk_amp_lock); + + return 0; +} + +static int tpa2018d1_read_config(void __user *argp) +{ + int rc = 0; + unsigned char reg_idx = 0x01; + unsigned char tmp[7]; + + if (!is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + } + + rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); + if (rc < 0) + goto err; + + rc = tpa2018_i2c_read(tmp, sizeof(tmp)); + if (rc < 0) + goto err; + + if (copy_to_user(argp, &tmp, sizeof(tmp))) + rc = -EFAULT; + +err: + if (!is_on) + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + return rc; +} + +static int tpa2018d1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc = 0; + int mode = -1; + int offset = 0; + struct tpa2018d1_config_data cfg; + + mutex_lock(&spk_amp_lock); + + switch (cmd) { + case TPA2018_SET_CONFIG: + if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) + rc = -EFAULT; + break; + + case TPA2018_READ_CONFIG: + rc = tpa2018d1_read_config(argp); + break; + + case TPA2018_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) { + rc = -EFAULT; + break; + } + if (mode >= tpa2018d1_num_modes || mode < 0) { + pr_err("%s: unsupported tpa2018d1 mode %d\n", + __func__, mode); + rc = -EINVAL; + break; + } + if (!config_data) { + pr_err("%s: no config data!\n", __func__); + rc = -EIO; + break; + } + memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, + TPA2018D1_CMD_LEN); + break; + + case TPA2018_SET_PARAM: + if (copy_from_user(&cfg, argp, sizeof(cfg))) { + pr_err("%s: copy from user failed.\n", __func__); + rc = -EFAULT; + break; + } + tpa2018d1_num_modes = cfg.mode_num; + if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { + pr_err("%s: invalid number of modes %d\n", __func__, + tpa2018d1_num_modes); + rc = -EINVAL; + break; + } + if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { + pr_err("%s: invalid data length %d, expecting %d\n", + __func__, cfg.data_len, + tpa2018d1_num_modes * TPA2018D1_CMD_LEN); + rc = -EINVAL; + break; + } + /* Free the old data */ + if (config_data) + kfree(config_data); + config_data = kmalloc(cfg.data_len, GFP_KERNEL); + if (!config_data) { + pr_err("%s: out of memory\n", __func__); + rc = -ENOMEM; + break; + } + if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { + pr_err("%s: copy data from user failed.\n", __func__); + kfree(config_data); + config_data = NULL; + rc = -EFAULT; + break; + } + /* replace default setting with playback setting */ + if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { + offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; + memcpy(spk_amp_cfg, config_data + offset, + TPA2018D1_CMD_LEN); + } + break; + + default: + pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + break; + } + mutex_unlock(&spk_amp_lock); + return rc; +} + +static struct file_operations tpa2018d1_fops = { + .owner = THIS_MODULE, + .open = tpa2018d1_open, + .release = tpa2018d1_release, + .ioctl = tpa2018d1_ioctl, +}; + +static struct miscdevice tpa2018d1_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tpa2018d1", + .fops = &tpa2018d1_fops, +}; + +void tpa2018d1_set_speaker_amp(int on) +{ + if (!pdata) { + pr_err("%s: no platform data!\n", __func__); + return; + } + mutex_lock(&spk_amp_lock); + if (on && !is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + + if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { + is_on = 1; + pr_info("%s: ON\n", __func__); + } + } else if (!on && is_on) { + if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { + is_on = 0; + msleep(2); + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + pr_info("%s: OFF\n", __func__); + } + } + mutex_unlock(&spk_amp_lock); +} + +static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data is NULL\n", __func__); + goto err_no_pdata; + } + + this_client = client; + + ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); + if (ret < 0) { + pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); + goto err_free_gpio; + } + + ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); + if (ret < 0) { + pr_err("%s: request aud_spk_en gpio direction failed\n", + __func__); + goto err_free_gpio; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_free_gpio; + } + + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ + + ret = misc_register(&tpa2018d1_device); + if (ret) { + pr_err("%s: tpa2018d1_device register failed\n", __func__); + goto err_free_gpio; + } + memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); + return 0; + +err_free_gpio: + gpio_free(pdata->gpio_tpa2018_spk_en); +err_no_pdata: + return ret; +} + +static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int tpa2018d1_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tpa2018d1_id[] = { + { TPA2018D1_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa2018d1_driver = { + .probe = tpa2018d1_probe, + .suspend = tpa2018d1_suspend, + .resume = tpa2018d1_resume, + .id_table = tpa2018d1_id, + .driver = { + .name = TPA2018D1_I2C_NAME, + }, +}; + +static int __init tpa2018d1_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&tpa2018d1_driver); +} + +module_init(tpa2018d1_init); + +MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h new file mode 100644 index 00000000000..dc110122094 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h @@ -0,0 +1,35 @@ +/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver + * + * Copyright (C) 2009 HTC Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#ifndef __ASM_ARM_ARCH_TPA2018D1_H +#define __ASM_ARM_ARCH_TPA2018D1_H + +#define TPA2018D1_I2C_NAME "tpa2018d1" +#define TPA2018D1_CMD_LEN 8 + +struct tpa2018d1_platform_data { + uint32_t gpio_tpa2018_spk_en; +}; + +struct tpa2018d1_config_data { + unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */ + unsigned int mode_num; + unsigned int data_len; +}; + +extern void tpa2018d1_set_speaker_amp(int on); + +#endif /* __ASM_ARM_ARCH_TPA2018D1_H */ diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c new file mode 100644 index 00000000000..8cd24766b03 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-wifi.c @@ -0,0 +1,146 @@ +/* linux/arch/arm/mach-msm/board-mahimahi-wifi.c +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi.h" + +int mahimahi_wifi_power(int on); +int mahimahi_wifi_reset(int on); +int mahimahi_wifi_set_carddetect(int on); + +#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 +#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 +#define PREALLOC_WLAN_SECTION_HEADER 24 + +#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512) +#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024) + +#define WLAN_SKB_BUF_NUM 16 + +static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; + +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = { + { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) } +}; + +static void *mahimahi_wifi_mem_prealloc(int section, unsigned long size) +{ + if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS) + return wlan_static_skb; + if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init mahimahi_init_wifi_mem(void) +{ + int i; + + for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { + if (i < (WLAN_SKB_BUF_NUM/2)) + wlan_static_skb[i] = dev_alloc_skb(4096); + else + wlan_static_skb[i] = dev_alloc_skb(8192); + } + for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size, + GFP_KERNEL); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} + +static struct resource mahimahi_wifi_resources[] = { + [0] = { + .name = "bcm4329_wlan_irq", + .start = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), + .end = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct wifi_platform_data mahimahi_wifi_control = { + .set_power = mahimahi_wifi_power, + .set_reset = mahimahi_wifi_reset, + .set_carddetect = mahimahi_wifi_set_carddetect, + .mem_prealloc = mahimahi_wifi_mem_prealloc, +}; + +static struct platform_device mahimahi_wifi_device = { + .name = "bcm4329_wlan", + .id = 1, + .num_resources = ARRAY_SIZE(mahimahi_wifi_resources), + .resource = mahimahi_wifi_resources, + .dev = { + .platform_data = &mahimahi_wifi_control, + }, +}; + +extern unsigned char *get_wifi_nvs_ram(void); +extern int wifi_calibration_size_set(void); + +static unsigned mahimahi_wifi_update_nvs(char *str, int add_flag) +{ +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + unsigned char *ptr; + unsigned len; + + if (!str) + return -EINVAL; + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + /* if the last byte in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len - 1] == 0) + len -= 1; + if (add_flag) { + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + } else { + if (strnstr(ptr + NVS_DATA_OFFSET, str, len)) + len -= strlen(str); + } + memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + wifi_calibration_size_set(); + return 0; +} + +static int __init mahimahi_wifi_init(void) +{ + int ret; + + if (!machine_is_mahimahi()) + return 0; + + printk("%s: start\n", __func__); + mahimahi_wifi_update_nvs("sd_oobonly=1\r\n", 0); + mahimahi_wifi_update_nvs("btc_params70=0x32\r\n", 1); + mahimahi_init_wifi_mem(); + ret = platform_device_register(&mahimahi_wifi_device); + return ret; +} + +late_initcall(mahimahi_wifi_init); diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 5a4882fc6f7..ec87a0dd1f1 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -31,10 +31,10 @@ #include #include #include +#include #include "board-mahimahi.h" #include "devices.h" -#include "proc_comm.h" static uint debug_uart; diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h new file mode 100644 index 00000000000..9696a47c400 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -0,0 +1,175 @@ +/* arch/arm/mach-msm/board-mahimahi.h + * + * Copyright (C) 2009 HTC Corporation. + * Author: Haley Teng + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H +#define __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H + +#include + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#define MSM_RAM_CONSOLE_BASE 0x03A00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#define MSM_FB_BASE 0x03B00000 +#define MSM_FB_SIZE 0x00465000 + +#define MSM_EBI1_BANK0_BASE 0x20000000 +#define MSM_EBI1_BANK0_SIZE 0x0E000000 + +#define MSM_GPU_MEM_BASE 0x2DB00000 +#define MSM_GPU_MEM_SIZE 0x00500000 + +#define MSM_EBI1_BANK1_BASE 0x30000000 +#define MSM_EBI1_BANK1_SIZE 0x10000000 + +#define MSM_PMEM_MDP_BASE 0x30000000 +#define MSM_PMEM_MDP_SIZE 0x02000000 + +#define MSM_PMEM_ADSP_BASE 0x32000000 +#define MSM_PMEM_ADSP_SIZE 0x02900000 + +#define MSM_PMEM_CAMERA_BASE 0x34900000 +#define MSM_PMEM_CAMERA_SIZE 0x00800000 + +#define MSM_HIGHMEM_BASE 0x35100000 +#define MSM_HIGHMEM_SIZE 0x0AF00000 + +#define MAHIMAHI_GPIO_PS_HOLD 25 + +#define MAHIMAHI_GPIO_UP_INT_N 35 +#define MAHIMAHI_GPIO_UP_RESET_N 82 +#define MAHIMAHI_GPIO_LS_EN_N 119 + +#define MAHIMAHI_GPIO_TP_INT_N 92 +#define MAHIMAHI_GPIO_TP_LS_EN 93 +#define MAHIMAHI_GPIO_TP_EN 160 + +#define MAHIMAHI_GPIO_POWER_KEY 94 +#define MAHIMAHI_GPIO_SDMC_CD_REV0_N 153 + +#define MAHIMAHI_GPIO_WIFI_SHUTDOWN_N 127 +#define MAHIMAHI_GPIO_WIFI_IRQ 152 + +#define MAHIMAHI_GPIO_BALL_UP 38 +#define MAHIMAHI_GPIO_BALL_DOWN 37 +#define MAHIMAHI_GPIO_BALL_LEFT 145 +#define MAHIMAHI_GPIO_BALL_RIGHT 21 + +#define MAHIMAHI_GPIO_BT_UART1_RTS 43 +#define MAHIMAHI_GPIO_BT_UART1_CTS 44 +#define MAHIMAHI_GPIO_BT_UART1_RX 45 +#define MAHIMAHI_GPIO_BT_UART1_TX 46 +#define MAHIMAHI_GPIO_BT_RESET_N 146 +#define MAHIMAHI_GPIO_BT_SHUTDOWN_N 128 + +#define MAHIMAHI_GPIO_BT_WAKE 57 +#define MAHIMAHI_GPIO_BT_HOST_WAKE 86 + +#define MAHIMAHI_GPIO_PROXIMITY_INT_N 90 +#define MAHIMAHI_GPIO_PROXIMITY_EN 120 + +#define MAHIMAHI_GPIO_DS2482_SLP_N 87 +#define MAHIMAHI_GPIO_VIBRATOR_ON 89 +/* Compass */ +#define MAHIMAHI_REV0_GPIO_COMPASS_INT_N 36 + +#define MAHIMAHI_GPIO_COMPASS_INT_N 153 +#define MAHIMAHI_GPIO_COMPASS_RST_N 107 +#define MAHIMAHI_PROJECT_NAME "mahimahi" +#define MAHIMAHI_LAYOUTS { \ + { {-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} }, \ + { { 0, -1, 0}, { 1, 0, 0}, {0, 0, -1} }, \ + { { 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} }, \ + { {-1, 0, 0}, { 0, 0, -1}, {0, 1, 0} } \ +} + +/* Audio */ +#define MAHIMAHI_AUD_JACKHP_EN 157 +#define MAHIMAHI_AUD_2V5_EN 158 +#define MAHIMAHI_AUD_MICPATH_SEL 111 +#define MAHIMAHI_AUD_A1026_INT 112 +#define MAHIMAHI_AUD_A1026_WAKEUP 113 +#define MAHIMAHI_AUD_A1026_RESET 129 +#define MAHIMAHI_AUD_A1026_CLK -1 +#define MAHIMAHI_CDMA_XA_AUD_A1026_CLK 105 +/* NOTE: MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP on CDMA is the same GPIO as + * MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT on UMTS. Also, + * MAHIMAHI_CDMA_XB_AUD_A1026_RESET is the same as + * GPIO MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN on UMTS. + */ +#define MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP 16 +#define MAHIMAHI_CDMA_XB_AUD_A1026_RESET 19 +#define MAHIMAHI_CDMA_XB_AUD_A1026_CLK -1 + +/* Bluetooth PCM */ +#define MAHIMAHI_BT_PCM_OUT 68 +#define MAHIMAHI_BT_PCM_IN 69 +#define MAHIMAHI_BT_PCM_SYNC 70 +#define MAHIMAHI_BT_PCM_CLK 71 +/* flash light */ +#define MAHIMAHI_GPIO_FLASHLIGHT_TORCH 58 +#define MAHIMAHI_GPIO_FLASHLIGHT_FLASH 84 + +#define MAHIMAHI_GPIO_LED_3V3_EN 85 +#define MAHIMAHI_GPIO_LCD_RST_N 29 +#define MAHIMAHI_GPIO_LCD_ID0 147 + +/* 3.5mm remote control key interrupt shutdown signal */ +#define MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN 19 + +#define MAHIMAHI_GPIO_DOCK 106 + +/* speaker amplifier enable pin for mahimahi CDMA version */ +#define MAHIMAHI_CDMA_GPIO_AUD_SPK_AMP_EN 104 + +#define MAHIMAHI_GPIO_BATTERY_DETECTION 39 +#define MAHIMAHI_GPIO_BATTERY_CHARGER_EN 22 +#define MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT 16 + +#define MAHIMAHI_CDMA_GPIO_BT_WAKE 28 +#define MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH 26 + +#define MAHIMAHI_CDMA_SD_2V85_EN 100 +#define MAHIMAHI_CDMA_JOG_2V6_EN 150 +/* display relative */ +#define MAHIMAHI_LCD_SPI_CLK (17) +#define MAHIMAHI_LCD_SPI_DO (18) +#define MAHIMAHI_LCD_SPI_CSz (20) +#define MAHIMAHI_LCD_RSTz (29) +#define MAHIMAHI_LCD_R1 (114) +#define MAHIMAHI_LCD_R2 (115) +#define MAHIMAHI_LCD_R3 (116) +#define MAHIMAHI_LCD_R4 (117) +#define MAHIMAHI_LCD_R5 (118) +#define MAHIMAHI_LCD_G0 (121) +#define MAHIMAHI_LCD_G1 (122) +#define MAHIMAHI_LCD_G2 (123) +#define MAHIMAHI_LCD_G3 (124) +#define MAHIMAHI_LCD_G4 (125) +#define MAHIMAHI_LCD_G5 (126) +#define MAHIMAHI_LCD_B1 (130) +#define MAHIMAHI_LCD_B2 (131) +#define MAHIMAHI_LCD_B3 (132) +#define MAHIMAHI_LCD_B4 (133) +#define MAHIMAHI_LCD_B5 (134) +#define MAHIMAHI_LCD_PCLK (135) +#define MAHIMAHI_LCD_VSYNC (136) +#define MAHIMAHI_LCD_HSYNC (137) +#define MAHIMAHI_LCD_DE (138) +#define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ diff --git a/arch/arm/mach-msm/board-msm7627-regulator.c b/arch/arm/mach-msm/board-msm7627-regulator.c new file mode 100644 index 00000000000..7437911befe --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627-regulator.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "board-msm7627-regulator.h" + +#define PCOM_VREG_CONSUMERS(name) \ + static struct regulator_consumer_supply __pcom_vreg_supply_##name[] + +#define PCOM_VREG_CONSTRAINT_LVSW(_name, _always_on, _boot_on, _supply_uV) \ +{ \ + .name = #_name, \ + .min_uV = 0, \ + .max_uV = 0, \ + .input_uV = _supply_uV, \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .apply_uV = 0, \ + .boot_on = _boot_on, \ + .always_on = _always_on \ +} + +#define PCOM_VREG_CONSTRAINT_DYN(_name, _min_uV, _max_uV, _always_on, \ + _boot_on, _apply_uV, _supply_uV) \ +{ \ + .name = #_name, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, \ + .input_uV = _supply_uV, \ + .apply_uV = _apply_uV, \ + .boot_on = _boot_on, \ + .always_on = _always_on \ +} + + +#define PCOM_VREG_INIT(_name, _supply, _constraints)\ +{ \ + .supply_regulator = _supply, \ + .consumer_supplies = __pcom_vreg_supply_##_name, \ + .num_consumer_supplies = ARRAY_SIZE(__pcom_vreg_supply_##_name), \ + .constraints = _constraints \ +} + +#define PCOM_VREG_SMP(_name, _id, _supply, _min_uV, _max_uV, _rise_time, \ + _pulldown, _always_on, _boot_on, _apply_uV, _supply_uV) \ +{ \ + .init_data = PCOM_VREG_INIT(_name, _supply, \ + PCOM_VREG_CONSTRAINT_DYN(_name, _min_uV, _max_uV, _always_on, \ + _boot_on, _apply_uV, _supply_uV)), \ + .id = _id, \ + .rise_time = _rise_time, \ + .pulldown = _pulldown, \ + .negative = 0, \ +} + +#define PCOM_VREG_LDO PCOM_VREG_SMP + +PCOM_VREG_CONSUMERS(smps0) = { + REGULATOR_SUPPLY("smps0", NULL), + REGULATOR_SUPPLY("msmc1", NULL), +}; + +PCOM_VREG_CONSUMERS(smps1) = { + REGULATOR_SUPPLY("smps1", NULL), + REGULATOR_SUPPLY("msmc2", NULL), +}; + +PCOM_VREG_CONSUMERS(smps2) = { + REGULATOR_SUPPLY("smps2", NULL), + REGULATOR_SUPPLY("pa", NULL), +}; + +PCOM_VREG_CONSUMERS(smps3) = { + REGULATOR_SUPPLY("smps3", NULL), + REGULATOR_SUPPLY("msme1", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo00) = { + REGULATOR_SUPPLY("ldo00", NULL), + REGULATOR_SUPPLY("gp3", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo01) = { + REGULATOR_SUPPLY("ldo01", NULL), + REGULATOR_SUPPLY("msma", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo02) = { + REGULATOR_SUPPLY("ldo02", NULL), + REGULATOR_SUPPLY("msmp", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo03) = { + REGULATOR_SUPPLY("ldo03", NULL), + REGULATOR_SUPPLY("ruim", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo04) = { + REGULATOR_SUPPLY("ldo04", NULL), + REGULATOR_SUPPLY("tcxo", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo05) = { + REGULATOR_SUPPLY("ldo05", NULL), + REGULATOR_SUPPLY("mmc", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo06) = { + REGULATOR_SUPPLY("ldo06", NULL), + REGULATOR_SUPPLY("usb", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo07) = { + REGULATOR_SUPPLY("ldo07", NULL), + REGULATOR_SUPPLY("rfrx1", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo08) = { + REGULATOR_SUPPLY("ldo08", NULL), + REGULATOR_SUPPLY("synt", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo09) = { + REGULATOR_SUPPLY("ldo09", NULL), + REGULATOR_SUPPLY("gp1", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo10) = { + REGULATOR_SUPPLY("ldo10", NULL), + REGULATOR_SUPPLY("gp4", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo11) = { + REGULATOR_SUPPLY("ldo11", NULL), + REGULATOR_SUPPLY("gp2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo12) = { + REGULATOR_SUPPLY("ldo12", NULL), + REGULATOR_SUPPLY("rftx", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo13) = { + REGULATOR_SUPPLY("ldo13", NULL), + REGULATOR_SUPPLY("wlan", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo14) = { + REGULATOR_SUPPLY("ldo14", NULL), + REGULATOR_SUPPLY("rf", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo15) = { + REGULATOR_SUPPLY("ldo15", NULL), + REGULATOR_SUPPLY("gp6", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo16) = { + REGULATOR_SUPPLY("ldo16", NULL), + REGULATOR_SUPPLY("gp5", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo17) = { + REGULATOR_SUPPLY("ldo17", NULL), + REGULATOR_SUPPLY("msme2", NULL), +}; + +/** + * Minimum and Maximum range for the regulators is as per the + * device Datasheet. Actual value used by consumer is between + * the provided range. + */ +static struct proccomm_regulator_info msm7627_pcom_vreg_info[] = { + /* Standard regulators (SMPS and LDO) + * R = rise time (us) + * P = pulldown (1 = pull down, 0 = float, -1 = don't care) + * A = always on + * B = boot on + * V = automatic voltage set (meaningful for single-voltage regs only) + * S = supply voltage (uV) + * name id supp min uV max uV R P A B V S */ + PCOM_VREG_SMP(smps0, 3, NULL, 750000, 3050000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps1, 4, NULL, 750000, 3050000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps2, 10, NULL, 750000, 3050000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps3, 2, NULL, 750000, 3050000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo00, 5, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo01, 0, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo02, 1, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo03, 19, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo04, 9, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo05, 18, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo06, 16, NULL, 3300000, 3300000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo07, 12, NULL, 2700000, 2700000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo08, 14, NULL, 2700000, 2700000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo09, 8, NULL, 2900000, 2900000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo10, 7, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo11, 21, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo12, 11, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo13, 15, NULL, 1800000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo14, 24, NULL, 2700000, 2700000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo15, 23, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo16, 22, NULL, 2850000, 3000000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo17, 6, NULL, 1300000, 1300000, 0, -1, 0, 0, 0, 0), + +}; + +struct proccomm_regulator_platform_data msm7627_proccomm_regulator_data = { + .regs = msm7627_pcom_vreg_info, + .nregs = ARRAY_SIZE(msm7627_pcom_vreg_info) +}; diff --git a/arch/arm/mach-msm/board-msm7627-regulator.h b/arch/arm/mach-msm/board-msm7627-regulator.h new file mode 100644 index 00000000000..d82c5c09bae --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627-regulator.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_7627_REGULATOR_H__ +#define __ARCH_ARM_MACH_MSM_BOARD_7627_REGULATOR_H__ + +#include "proccomm-regulator.h" + +extern struct proccomm_regulator_platform_data msm7627_proccomm_regulator_data; + +#endif diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c new file mode 100644 index 00000000000..e4edf9bae2e --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a-bt.c @@ -0,0 +1,1018 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-msm7627a.h" +#include "devices-msm7x2xa.h" + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) + + +static struct bt_vreg_info bt_vregs[] = { + {"msme1", 2, 1800000, 1800000, 0, NULL}, + {"bt", 21, 2900000, 3300000, 1, NULL} +}; + +static struct platform_device msm_bt_power_device = { + .name = "bt_power", +}; + +static unsigned bt_config_power_on[] = { + /*RFR*/ + GPIO_CFG(43, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*CTS*/ + GPIO_CFG(44, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*RX*/ + GPIO_CFG(45, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*TX*/ + GPIO_CFG(46, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; +static unsigned bt_config_pcm_on[] = { + /*PCM_DOUT*/ + GPIO_CFG(68, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*PCM_DIN*/ + GPIO_CFG(69, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*PCM_SYNC*/ + GPIO_CFG(70, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*PCM_CLK*/ + GPIO_CFG(71, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; +static unsigned bt_config_power_off[] = { + /*RFR*/ + GPIO_CFG(43, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*CTS*/ + GPIO_CFG(44, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*RX*/ + GPIO_CFG(45, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*TX*/ + GPIO_CFG(46, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; +static unsigned bt_config_pcm_off[] = { + /*PCM_DOUT*/ + GPIO_CFG(68, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*PCM_DIN*/ + GPIO_CFG(69, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*PCM_SYNC*/ + GPIO_CFG(70, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*PCM_CLK*/ + GPIO_CFG(71, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; + +static unsigned fm_i2s_config_power_on[] = { + /*FM_I2S_SD*/ + GPIO_CFG(68, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*FM_I2S_WS*/ + GPIO_CFG(70, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + /*FM_I2S_SCK*/ + GPIO_CFG(71, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; + +static unsigned fm_i2s_config_power_off[] = { + /*FM_I2S_SD*/ + GPIO_CFG(68, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*FM_I2S_WS*/ + GPIO_CFG(70, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /*FM_I2S_SCK*/ + GPIO_CFG(71, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; + +int gpio_bt_sys_rest_en = 133; +static void gpio_bt_config(void) +{ + u32 socinfo = socinfo_get_platform_version(); + if (machine_is_msm7627a_qrd1()) + gpio_bt_sys_rest_en = 114; + if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() + || machine_is_msm8625_evt()) + gpio_bt_sys_rest_en = 16; + if (machine_is_msm8625_qrd7()) + gpio_bt_sys_rest_en = 88; + if (machine_is_msm7627a_qrd3()) { + if (socinfo == 0x70002) + gpio_bt_sys_rest_en = 88; + else + gpio_bt_sys_rest_en = 85; + } +} + +static int bt_set_gpio(int on) +{ + int rc = 0; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + + pr_debug("%s: Setting SYS_RST_PIN(%d) to %d\n", + __func__, gpio_bt_sys_rest_en, on); + if (on) { + + if (machine_is_msm7627a_evb() || machine_is_msm8625_qrd7()) { + rc = gpio_tlmm_config(GPIO_CFG(gpio_bt_sys_rest_en, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + gpio_set_value(gpio_bt_sys_rest_en, 1); + } else { + rc = gpio_direction_output(gpio_bt_sys_rest_en, 1); + } + msleep(100); + } else { + + if (!marimba_get_fm_status(&config) && + !marimba_get_bt_status(&config)) { + if (machine_is_msm7627a_evb() || + machine_is_msm8625_qrd7()) { + gpio_set_value(gpio_bt_sys_rest_en, 0); + rc = gpio_tlmm_config(GPIO_CFG( + gpio_bt_sys_rest_en, 0, + GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + } else { + gpio_set_value_cansleep(gpio_bt_sys_rest_en, 0); + rc = gpio_direction_input(gpio_bt_sys_rest_en); + } + msleep(100); + } + } + if (rc) + pr_err("%s: BT sys_reset_en GPIO : Error", __func__); + + return rc; +} + +static struct regulator *fm_regulator; +static int fm_radio_setup(struct marimba_fm_platform_data *pdata) +{ + int rc = 0; + const char *id = "FMPW"; + uint32_t irqcfg; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + u8 value; + + /* Voting for 1.8V Regulator */ + fm_regulator = regulator_get(NULL , "msme1"); + if (IS_ERR(fm_regulator)) { + rc = PTR_ERR(fm_regulator); + pr_err("%s: could not get regulator: %d\n", __func__, rc); + goto out; + } + + /* Set the voltage level to 1.8V */ + rc = regulator_set_voltage(fm_regulator, 1800000, 1800000); + if (rc < 0) { + pr_err("%s: could not set voltage: %d\n", __func__, rc); + goto reg_free; + } + + /* Enabling the 1.8V regulator */ + rc = regulator_enable(fm_regulator); + if (rc) { + pr_err("%s: could not enable regulator: %d\n", __func__, rc); + goto reg_free; + } + + /* Voting for 19.2MHz clock */ + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_VOTE_ON); + if (rc < 0) { + pr_err("%s: clock vote failed with :(%d)\n", + __func__, rc); + goto reg_disable; + } + + rc = bt_set_gpio(1); + if (rc) { + pr_err("%s: bt_set_gpio = %d", __func__, rc); + goto gpio_deconfig; + } + /*re-write FM Slave Id, after reset*/ + value = BAHAMA_SLAVE_ID_FM_ADDR; + rc = marimba_write_bit_mask(&config, + BAHAMA_SLAVE_ID_FM_REG, &value, 1, 0xFF); + if (rc < 0) { + pr_err("%s: FM Slave ID rewrite Failed = %d", __func__, rc); + goto gpio_deconfig; + } + /* Configuring the FM GPIO */ + irqcfg = GPIO_CFG(FM_GPIO, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA); + + rc = gpio_tlmm_config(irqcfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, irqcfg, rc); + goto gpio_deconfig; + } + + return 0; + +gpio_deconfig: + pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_VOTE_OFF); + bt_set_gpio(0); +reg_disable: + regulator_disable(fm_regulator); +reg_free: + regulator_put(fm_regulator); + fm_regulator = NULL; +out: + return rc; +}; + +static void fm_radio_shutdown(struct marimba_fm_platform_data *pdata) +{ + int rc; + const char *id = "FMPW"; + + /* Releasing the GPIO line used by FM */ + uint32_t irqcfg = GPIO_CFG(FM_GPIO, 0, GPIO_CFG_INPUT, + GPIO_CFG_PULL_UP, GPIO_CFG_2MA); + + rc = gpio_tlmm_config(irqcfg, GPIO_CFG_ENABLE); + if (rc) + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, irqcfg, rc); + + /* Releasing the 1.8V Regulator */ + if (!IS_ERR_OR_NULL(fm_regulator)) { + rc = regulator_disable(fm_regulator); + if (rc) + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + regulator_put(fm_regulator); + fm_regulator = NULL; + } + + /* Voting off the clock */ + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_VOTE_OFF); + if (rc < 0) + pr_err("%s: voting off failed with :(%d)\n", + __func__, rc); + rc = bt_set_gpio(0); + if (rc) + pr_err("%s: bt_set_gpio = %d", __func__, rc); +} +static int switch_pcm_i2s_reg_mode(int mode) +{ + unsigned char reg = 0; + int rc = -1; + unsigned char set = I2C_PIN_CTL; /*SET PIN CTL mode*/ + unsigned char unset = I2C_NORMAL; /* UNSET PIN CTL MODE*/ + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + + if (mode == 0) { + /* as we need to switch path to FM we need to move + BT AUX PCM lines to PIN CONTROL mode then move + FM to normal mode.*/ + for (reg = BT_PCM_BCLK_MODE; reg <= BT_PCM_SYNC_MODE; reg++) { + rc = marimba_write(&config, reg, &set, 1); + if (rc < 0) { + pr_err("pcm pinctl failed = %d", rc); + goto err_all; + } + } + for (reg = FM_I2S_SD_MODE; reg <= FM_I2S_SCK_MODE; reg++) { + rc = marimba_write(&config, reg, &unset, 1); + if (rc < 0) { + pr_err("i2s normal failed = %d", rc); + goto err_all; + } + } + } else { + /* as we need to switch path to AUXPCM we need to move + FM I2S lines to PIN CONTROL mode then move + BT AUX_PCM to normal mode.*/ + for (reg = FM_I2S_SD_MODE; reg <= FM_I2S_SCK_MODE; reg++) { + rc = marimba_write(&config, reg, &set, 1); + if (rc < 0) { + pr_err("i2s pinctl failed = %d", rc); + goto err_all; + } + } + for (reg = BT_PCM_BCLK_MODE; reg <= BT_PCM_SYNC_MODE; reg++) { + rc = marimba_write(&config, reg, &unset, 1); + if (rc < 0) { + pr_err("pcm normal failed = %d", rc); + goto err_all; + } + } + } + + return 0; + +err_all: + return rc; +} + + +static void config_pcm_i2s_mode(int mode) +{ + void __iomem *cfg_ptr; + u8 reg2; + + cfg_ptr = ioremap_nocache(FPGA_MSM_CNTRL_REG2, sizeof(char)); + + if (!cfg_ptr) + return; + if (mode) { + /*enable the pcm mode in FPGA*/ + reg2 = readb_relaxed(cfg_ptr); + if (reg2 == 0) { + reg2 = 1; + writeb_relaxed(reg2, cfg_ptr); + } + } else { + /*enable i2s mode in FPGA*/ + reg2 = readb_relaxed(cfg_ptr); + if (reg2 == 1) { + reg2 = 0; + writeb_relaxed(reg2, cfg_ptr); + } + } + iounmap(cfg_ptr); +} + +static int config_i2s(int mode) +{ + int pin, rc = 0; + + if (mode == FM_I2S_ON) { + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() + || machine_is_msm8625_surf()) + config_pcm_i2s_mode(0); + pr_err("%s mode = FM_I2S_ON", __func__); + + rc = switch_pcm_i2s_reg_mode(0); + if (rc) { + pr_err("switch mode failed"); + return rc; + } + for (pin = 0; pin < ARRAY_SIZE(fm_i2s_config_power_on); + pin++) { + rc = gpio_tlmm_config( + fm_i2s_config_power_on[pin], + GPIO_CFG_ENABLE + ); + if (rc < 0) + return rc; + } + } else if (mode == FM_I2S_OFF) { + pr_err("%s mode = FM_I2S_OFF", __func__); + rc = switch_pcm_i2s_reg_mode(1); + if (rc) { + pr_err("switch mode failed"); + return rc; + } + for (pin = 0; pin < ARRAY_SIZE(fm_i2s_config_power_off); + pin++) { + rc = gpio_tlmm_config( + fm_i2s_config_power_off[pin], + GPIO_CFG_ENABLE + ); + if (rc < 0) + return rc; + } + } + return rc; +} + +static int config_pcm(int mode) +{ + int pin, rc = 0; + + if (mode == BT_PCM_ON) { + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() + || machine_is_msm8625_surf()) + config_pcm_i2s_mode(1); + pr_err("%s mode =BT_PCM_ON", __func__); + rc = switch_pcm_i2s_reg_mode(1); + if (rc) { + pr_err("switch mode failed"); + return rc; + } + for (pin = 0; pin < ARRAY_SIZE(bt_config_pcm_on); + pin++) { + rc = gpio_tlmm_config(bt_config_pcm_on[pin], + GPIO_CFG_ENABLE); + if (rc < 0) + return rc; + } + } else if (mode == BT_PCM_OFF) { + pr_err("%s mode =BT_PCM_OFF", __func__); + rc = switch_pcm_i2s_reg_mode(0); + if (rc) { + pr_err("switch mode failed"); + return rc; + } + for (pin = 0; pin < ARRAY_SIZE(bt_config_pcm_off); + pin++) { + rc = gpio_tlmm_config(bt_config_pcm_off[pin], + GPIO_CFG_ENABLE); + if (rc < 0) + return rc; + } + + } + + return rc; +} + +static int msm_bahama_setup_pcm_i2s(int mode) +{ + int fm_state = 0, bt_state = 0; + int rc = 0; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + + fm_state = marimba_get_fm_status(&config); + bt_state = marimba_get_bt_status(&config); + + switch (mode) { + case BT_PCM_ON: + case BT_PCM_OFF: + if (!fm_state) + rc = config_pcm(mode); + break; + case FM_I2S_ON: + rc = config_i2s(mode); + break; + case FM_I2S_OFF: + if (bt_state) + rc = config_pcm(BT_PCM_ON); + else + rc = config_i2s(mode); + break; + default: + rc = -EIO; + pr_err("%s:Unsupported mode", __func__); + } + return rc; +} + +static int bahama_bt(int on) +{ + int rc = 0; + int i; + + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + + struct bahama_variant_register { + const size_t size; + const struct bahama_config_register *set; + }; + + const struct bahama_config_register *p; + + u8 version; + + const struct bahama_config_register v10_bt_on[] = { + { 0xE9, 0x00, 0xFF }, + { 0xF4, 0x80, 0xFF }, + { 0xE4, 0x00, 0xFF }, + { 0xE5, 0x00, 0x0F }, +#ifdef CONFIG_WLAN + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF }, + { 0x01, 0x0C, 0x1F }, + { 0x01, 0x08, 0x1F }, + }; + + const struct bahama_config_register v20_bt_on_fm_off[] = { + { 0x11, 0x0C, 0xFF }, + { 0x13, 0x01, 0xFF }, + { 0xF4, 0x80, 0xFF }, + { 0xF0, 0x00, 0xFF }, + { 0xE9, 0x00, 0xFF }, +#ifdef CONFIG_WLAN + { 0x81, 0x00, 0x7F }, + { 0x82, 0x00, 0xFF }, + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0x8E, 0x15, 0xFF }, + { 0x8F, 0x15, 0xFF }, + { 0x90, 0x15, 0xFF }, + + { 0xE9, 0x21, 0xFF }, + }; + + const struct bahama_config_register v20_bt_on_fm_on[] = { + { 0x11, 0x0C, 0xFF }, + { 0x13, 0x01, 0xFF }, + { 0xF4, 0x86, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE9, 0x00, 0xFF }, +#ifdef CONFIG_WLAN + { 0x81, 0x00, 0x7F }, + { 0x82, 0x00, 0xFF }, + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF }, + }; + + const struct bahama_config_register v10_bt_off[] = { + { 0xE9, 0x00, 0xFF }, + }; + + const struct bahama_config_register v20_bt_off_fm_off[] = { + { 0xF4, 0x84, 0xFF }, + { 0xF0, 0x04, 0xFF }, + { 0xE9, 0x00, 0xFF } + }; + + const struct bahama_config_register v20_bt_off_fm_on[] = { + { 0xF4, 0x86, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE9, 0x00, 0xFF } + }; + + const struct bahama_variant_register bt_bahama[2][3] = { + { + { ARRAY_SIZE(v10_bt_off), v10_bt_off }, + { ARRAY_SIZE(v20_bt_off_fm_off), v20_bt_off_fm_off }, + { ARRAY_SIZE(v20_bt_off_fm_on), v20_bt_off_fm_on } + }, + { + { ARRAY_SIZE(v10_bt_on), v10_bt_on }, + { ARRAY_SIZE(v20_bt_on_fm_off), v20_bt_on_fm_off }, + { ARRAY_SIZE(v20_bt_on_fm_on), v20_bt_on_fm_on } + } + }; + + u8 offset = 0; /* index into bahama configs */ + on = on ? 1 : 0; + version = marimba_read_bahama_ver(&config); + if ((int)version < 0 || version == BAHAMA_VER_UNSUPPORTED) { + dev_err(&msm_bt_power_device.dev, "%s : Bahama " + "version read Error, version = %d\n", + __func__, version); + return -EIO; + } + + if (version == BAHAMA_VER_2_0) { + if (marimba_get_fm_status(&config)) + offset = 0x01; + } + + p = bt_bahama[on][version + offset].set; + + dev_info(&msm_bt_power_device.dev, + "%s: found version %d\n", __func__, version); + + for (i = 0; i < bt_bahama[on][version + offset].size; i++) { + u8 value = (p+i)->value; + rc = marimba_write_bit_mask(&config, + (p+i)->reg, + &value, + sizeof((p+i)->value), + (p+i)->mask); + if (rc < 0) { + dev_err(&msm_bt_power_device.dev, + "%s: reg %x write failed: %d\n", + __func__, (p+i)->reg, rc); + return rc; + } + dev_dbg(&msm_bt_power_device.dev, + "%s: reg 0x%02x write value 0x%02x mask 0x%02x\n", + __func__, (p+i)->reg, + value, (p+i)->mask); + value = 0; + rc = marimba_read_bit_mask(&config, + (p+i)->reg, &value, + sizeof((p+i)->value), (p+i)->mask); + if (rc < 0) + dev_err(&msm_bt_power_device.dev, + "%s marimba_read_bit_mask- error", + __func__); + dev_dbg(&msm_bt_power_device.dev, + "%s: reg 0x%02x read value 0x%02x mask 0x%02x\n", + __func__, (p+i)->reg, + value, (p+i)->mask); + } + /* Update BT Status */ + if (on) + marimba_set_bt_status(&config, true); + else + marimba_set_bt_status(&config, false); + return rc; +} + +static int bluetooth_switch_regulators(int on) +{ + int i, rc = 0; + const char *id = "BTPW"; + + for (i = 0; i < ARRAY_SIZE(bt_vregs); i++) { + if (IS_ERR_OR_NULL(bt_vregs[i].reg)) { + bt_vregs[i].reg = + regulator_get(&msm_bt_power_device.dev, + bt_vregs[i].name); + if (IS_ERR(bt_vregs[i].reg)) { + rc = PTR_ERR(bt_vregs[i].reg); + dev_err(&msm_bt_power_device.dev, + "%s: could not get regulator %s: %d\n", + __func__, bt_vregs[i].name, rc); + goto reg_disable; + } + } + + rc = on ? regulator_set_voltage(bt_vregs[i].reg, + bt_vregs[i].min_level, + bt_vregs[i].max_level) : 0; + if (rc) { + dev_err(&msm_bt_power_device.dev, + "%s: could not set voltage for %s: %d\n", + __func__, bt_vregs[i].name, rc); + goto reg_disable; + } + + rc = on ? regulator_enable(bt_vregs[i].reg) : 0; + if (rc) { + dev_err(&msm_bt_power_device.dev, + "%s: could not %sable regulator %s: %d\n", + __func__, "en", bt_vregs[i].name, rc); + goto reg_disable; + } + + if (bt_vregs[i].is_pin_controlled) { + rc = pmapp_vreg_lpm_pincntrl_vote(id, + bt_vregs[i].pmapp_id, + PMAPP_CLOCK_ID_D1, + on ? PMAPP_CLOCK_VOTE_ON : + PMAPP_CLOCK_VOTE_OFF); + if (rc) { + dev_err(&msm_bt_power_device.dev, + "%s: pin control failed for %s: %d\n", + __func__, bt_vregs[i].name, rc); + goto pin_cnt_fail; + } + } + rc = on ? 0 : regulator_disable(bt_vregs[i].reg); + + if (rc) { + dev_err(&msm_bt_power_device.dev, + "%s: could not %sable regulator %s: %d\n", + __func__, "dis", bt_vregs[i].name, rc); + goto reg_disable; + } + } + + return rc; +pin_cnt_fail: + if (on) + regulator_disable(bt_vregs[i].reg); +reg_disable: + while (i) { + if (on) { + i--; + regulator_disable(bt_vregs[i].reg); + regulator_put(bt_vregs[i].reg); + bt_vregs[i].reg = NULL; + } + } + return rc; +} + +static struct regulator *reg_s3; +static unsigned int msm_bahama_setup_power(void) +{ + int rc = 0; + + reg_s3 = regulator_get(NULL, "msme1"); + if (IS_ERR(reg_s3)) { + rc = PTR_ERR(reg_s3); + pr_err("%s: could not get regulator: %d\n", __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_s3, 1800000, 1800000); + if (rc < 0) { + pr_err("%s: could not set voltage: %d\n", __func__, rc); + goto reg_fail; + } + + rc = regulator_enable(reg_s3); + if (rc < 0) { + pr_err("%s: could not enable regulator: %d\n", __func__, rc); + goto reg_fail; + } + gpio_tlmm_config(GPIO_CFG(gpio_bt_sys_rest_en, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + + /*setup Bahama_sys_reset_n*/ + rc = gpio_request(gpio_bt_sys_rest_en, "bahama sys_rst_n"); + if (rc < 0) { + pr_err("%s: gpio_request %d = %d\n", __func__, + gpio_bt_sys_rest_en, rc); + goto reg_disable; + } + + rc = bt_set_gpio(1); + if (rc < 0) { + pr_err("%s: bt_set_gpio %d = %d\n", __func__, + gpio_bt_sys_rest_en, rc); + goto gpio_fail; + } + + return rc; + +gpio_fail: + gpio_free(gpio_bt_sys_rest_en); +reg_disable: + regulator_disable(reg_s3); +reg_fail: + regulator_put(reg_s3); +out: + reg_s3 = NULL; + return rc; +} + +static unsigned int msm_bahama_shutdown_power(int value) +{ + int rc = 0; + + if (IS_ERR_OR_NULL(reg_s3)) { + rc = reg_s3 ? PTR_ERR(reg_s3) : -ENODEV; + goto out; + } + + rc = regulator_disable(reg_s3); + if (rc) { + pr_err("%s: could not disable regulator: %d\n", __func__, rc); + goto out; + } + + if (value == BAHAMA_ID) { + rc = bt_set_gpio(0); + if (rc) { + pr_err("%s: bt_set_gpio = %d\n", + __func__, rc); + goto reg_enable; + } + gpio_free(gpio_bt_sys_rest_en); + } + + regulator_put(reg_s3); + reg_s3 = NULL; + + return 0; + +reg_enable: + regulator_enable(reg_s3); +out: + return rc; +} + +static unsigned int msm_bahama_core_config(int type) +{ + int rc = 0; + + if (type == BAHAMA_ID) { + int i; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + const struct bahama_config_register v20_init[] = { + /* reg, value, mask */ + { 0xF4, 0x84, 0xFF }, /* AREG */ + { 0xF0, 0x04, 0xFF } /* DREG */ + }; + if (marimba_read_bahama_ver(&config) == BAHAMA_VER_2_0) { + for (i = 0; i < ARRAY_SIZE(v20_init); i++) { + u8 value = v20_init[i].value; + rc = marimba_write_bit_mask(&config, + v20_init[i].reg, + &value, + sizeof(v20_init[i].value), + v20_init[i].mask); + if (rc < 0) { + pr_err("%s: reg %d write failed: %d\n", + __func__, v20_init[i].reg, rc); + return rc; + } + pr_debug("%s: reg 0x%02x value 0x%02x" + " mask 0x%02x\n", + __func__, v20_init[i].reg, + v20_init[i].value, v20_init[i].mask); + } + } + } + rc = bt_set_gpio(0); + if (rc) { + pr_err("%s: bt_set_gpio = %d\n", + __func__, rc); + } + pr_debug("core type: %d\n", type); + return rc; +} + +static int bluetooth_power(int on) +{ + int pin, rc = 0; + const char *id = "BTPW"; + int cid = 0; + + cid = adie_get_detected_connectivity_type(); + if (cid != BAHAMA_ID) { + pr_err("%s: unexpected adie connectivity type: %d\n", + __func__, cid); + return -ENODEV; + } + if (on) { + /*setup power for BT SOC*/ + rc = bt_set_gpio(on); + if (rc) { + pr_err("%s: bt_set_gpio = %d\n", + __func__, rc); + goto exit; + } + rc = bluetooth_switch_regulators(on); + if (rc < 0) { + pr_err("%s: bluetooth_switch_regulators rc = %d", + __func__, rc); + goto exit; + } + /*setup BT GPIO lines*/ + for (pin = 0; pin < ARRAY_SIZE(bt_config_power_on); + pin++) { + rc = gpio_tlmm_config(bt_config_power_on[pin], + GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, + bt_config_power_on[pin], + rc); + goto fail_power; + } + } + /*Setup BT clocks*/ + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_VOTE_ON); + if (rc < 0) { + pr_err("Failed to vote for TCXO_D1 ON\n"); + goto fail_clock; + } + msleep(20); + + /*I2C config for Bahama*/ + rc = bahama_bt(1); + if (rc < 0) { + pr_err("%s: bahama_bt rc = %d", __func__, rc); + goto fail_i2c; + } + msleep(20); + + /*setup BT PCM lines*/ + rc = msm_bahama_setup_pcm_i2s(BT_PCM_ON); + if (rc < 0) { + pr_err("%s: msm_bahama_setup_pcm_i2s , rc =%d\n", + __func__, rc); + goto fail_power; + } + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_VOTE_PIN_CTRL); + if (rc < 0) + pr_err("%s:Pin Control Failed, rc = %d", + __func__, rc); + + } else { + rc = bahama_bt(0); + if (rc < 0) + pr_err("%s: bahama_bt rc = %d", __func__, rc); + + rc = msm_bahama_setup_pcm_i2s(BT_PCM_OFF); + if (rc < 0) { + pr_err("%s: msm_bahama_setup_pcm_i2s, rc =%d\n", + __func__, rc); + } + rc = bt_set_gpio(on); + if (rc) { + pr_err("%s: bt_set_gpio = %d\n", + __func__, rc); + } +fail_i2c: + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_VOTE_OFF); + if (rc < 0) + pr_err("%s: Failed to vote Off D1\n", __func__); +fail_clock: + for (pin = 0; pin < ARRAY_SIZE(bt_config_power_off); + pin++) { + rc = gpio_tlmm_config(bt_config_power_off[pin], + GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s:" + " gpio_tlmm_config(%#x)=%d\n", + __func__, + bt_config_power_off[pin], rc); + } + } +fail_power: + rc = bluetooth_switch_regulators(0); + if (rc < 0) { + pr_err("%s: switch_regulators : rc = %d",\ + __func__, rc); + goto exit; + } + } + return rc; +exit: + pr_err("%s: failed with rc = %d", __func__, rc); + return rc; +} + +static struct marimba_fm_platform_data marimba_fm_pdata = { + .fm_setup = fm_radio_setup, + .fm_shutdown = fm_radio_shutdown, + .irq = MSM_GPIO_TO_INT(FM_GPIO), + .vreg_s2 = NULL, + .vreg_xo_out = NULL, + /* Configuring the FM SoC as I2S Master */ + .is_fm_soc_i2s_master = true, + .config_i2s_gpio = msm_bahama_setup_pcm_i2s, +}; + +static struct marimba_platform_data marimba_pdata = { + .slave_id[SLAVE_ID_BAHAMA_FM] = BAHAMA_SLAVE_ID_FM_ADDR, + .slave_id[SLAVE_ID_BAHAMA_QMEMBIST] = BAHAMA_SLAVE_ID_QMEMBIST_ADDR, + .bahama_setup = msm_bahama_setup_power, + .bahama_shutdown = msm_bahama_shutdown_power, + .bahama_core_config = msm_bahama_core_config, + .fm = &marimba_fm_pdata, +}; + +static struct i2c_board_info bahama_devices[] = { +{ + I2C_BOARD_INFO("marimba", 0xc), + .platform_data = &marimba_pdata, +}, +}; + +void __init msm7627a_bt_power_init(void) +{ + int i, rc = 0; + struct device *dev; + + + gpio_bt_config(); + + rc = i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + bahama_devices, + ARRAY_SIZE(bahama_devices)); + if (rc < 0) { + pr_err("%s: I2C Register failed\n", __func__); + return; + } + + rc = platform_device_register(&msm_bt_power_device); + if (rc < 0) { + pr_err("%s: device register failed\n", __func__); + return; + } + + dev = &msm_bt_power_device.dev; + + for (i = 0; i < ARRAY_SIZE(bt_vregs); i++) { + bt_vregs[i].reg = regulator_get(dev, bt_vregs[i].name); + if (IS_ERR(bt_vregs[i].reg)) { + rc = PTR_ERR(bt_vregs[i].reg); + dev_err(dev, "%s: could not get regulator %s: %d\n", + __func__, bt_vregs[i].name, rc); + goto reg_get_fail; + } + } + + dev->platform_data = &bluetooth_power; + + return; + +reg_get_fail: + while (i--) { + regulator_put(bt_vregs[i].reg); + bt_vregs[i].reg = NULL; + } + platform_device_unregister(&msm_bt_power_device); +} +#endif diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c new file mode 100644 index 00000000000..2873fc02d5f --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a-camera.c @@ -0,0 +1,1223 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices-msm7x2xa.h" +#include "board-msm7627a.h" +#include + +#define GPIO_SKU1_CAM_VGA_SHDN 18 +#define GPIO_SKU1_CAM_VGA_RESET_N 29 +#define GPIO_SKU3_CAM_5MP_SHDN_N 5 /* PWDN */ +#define GPIO_SKU3_CAM_5MP_CAMIF_RESET 6 /* (board_is(EVT))?123:121 RESET */ +#define GPIO_SKU3_CAM_5MP_CAM_DRIVER_PWDN 30 +#define GPIO_SKU7_CAM_VGA_SHDN 91 +#define GPIO_SKU7_CAM_5MP_SHDN_N 93 /* PWDN */ +#define GPIO_SKU7_CAM_5MP_CAMIF_RESET 23 /* (board_is(EVT))?123:121 RESET */ + +#ifdef CONFIG_MSM_CAMERA_V4L2 +static uint32_t camera_off_gpio_table[] = { + GPIO_CFG(15, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; + +static uint32_t camera_on_gpio_table[] = { + GPIO_CFG(15, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; + +static struct gpio s5k4e1_cam_req_gpio[] = { + {GPIO_CAM_GP_CAMIF_RESET_N, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct msm_gpio_set_tbl s5k4e1_cam_gpio_set_tbl[] = { + {GPIO_CAM_GP_CAMIF_RESET_N, GPIOF_OUT_INIT_LOW, 1000}, + {GPIO_CAM_GP_CAMIF_RESET_N, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_camera_gpio_conf gpio_conf_s5k4e1 = { + .camera_off_table = camera_off_gpio_table, + .camera_off_table_size = ARRAY_SIZE(camera_off_gpio_table), + .camera_on_table = camera_on_gpio_table, + .camera_on_table_size = ARRAY_SIZE(camera_on_gpio_table), + .cam_gpio_req_tbl = s5k4e1_cam_req_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(s5k4e1_cam_req_gpio), + .cam_gpio_set_tbl = s5k4e1_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(s5k4e1_cam_gpio_set_tbl), + .gpio_no_mux = 1, +}; + +static struct msm_camera_gpio_conf gpio_conf_mt9e013 = { + .camera_off_table = camera_off_gpio_table, + .camera_on_table = camera_on_gpio_table, + .gpio_no_mux = 1, +}; + +static struct msm_camera_gpio_conf gpio_conf_ov9726 = { + .camera_off_table = camera_off_gpio_table, + .camera_on_table = camera_on_gpio_table, + .gpio_no_mux = 1, +}; + +#ifdef CONFIG_OV7692 +static struct gpio ov7692_cam_req_gpio[] = { + {GPIO_SKU1_CAM_VGA_SHDN, GPIOF_DIR_OUT, "CAM_VGA_SHDN"}, + {GPIO_SKU1_CAM_VGA_RESET_N, GPIOF_DIR_OUT, "CAM_VGA_RESET"}, +}; + +static struct msm_gpio_set_tbl ov7692_cam_gpio_set_tbl[] = { + {GPIO_SKU1_CAM_VGA_SHDN, GPIOF_OUT_INIT_HIGH, 5000}, + {GPIO_SKU1_CAM_VGA_SHDN, GPIOF_OUT_INIT_LOW, 5000}, + {GPIO_SKU1_CAM_VGA_RESET_N, GPIOF_OUT_INIT_HIGH, 5000}, + {GPIO_SKU1_CAM_VGA_RESET_N, GPIOF_OUT_INIT_LOW, 5000}, +}; + +static struct msm_camera_gpio_conf gpio_conf_ov7692 = { + .cam_gpio_req_tbl = ov7692_cam_req_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(ov7692_cam_req_gpio), + .cam_gpio_set_tbl = ov7692_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(ov7692_cam_gpio_set_tbl), + .gpio_no_mux = 1, +}; +#endif + +#ifdef CONFIG_OV5647 +static struct msm_camera_gpio_conf gpio_conf_ov5647 = { + .camera_off_table = camera_off_gpio_table, + .camera_on_table = camera_on_gpio_table, + .gpio_no_mux = 1, +}; +#endif + +#ifdef CONFIG_MSM_CAMERA_FLASH +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT, + ._fsrc.ext_driver_src.led_en = GPIO_CAM_GP_LED_EN1, + ._fsrc.ext_driver_src.led_flash_en = GPIO_CAM_GP_LED_EN2, +}; +#endif + +static struct camera_vreg_t msm_cam_vreg[] = { + {"msme1", REG_LDO, 1800000, 1800000, 0}, + {"gp2", REG_LDO, 2850000, 2850000, 0}, + {"usb2", REG_LDO, 1800000, 1800000, 0}, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k4e1_data; + +struct msm_camera_device_platform_data msm_camera_device_data_csi1[] = { + { + .csid_core = 1, + .is_csic = 1, + .ioclk = { + .vfe_clk_rate = 192000000, + }, + }, + { + .csid_core = 1, + .is_csic = 1, + .ioclk = { + .vfe_clk_rate = 266667000, + }, + }, +}; + +struct msm_camera_device_platform_data msm_camera_device_data_csi0[] = { + { + .csid_core = 0, + .is_csic = 1, + .ioclk = { + .vfe_clk_rate = 192000000, + }, + }, + { + .csid_core = 0, + .is_csic = 1, + .ioclk = { + .vfe_clk_rate = 266667000, + }, + }, +}; + +static struct i2c_board_info msm_act_main_cam_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x11), +}; + +static struct msm_actuator_info msm_act_main_cam_4_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_4, + .bus_id = MSM_GSBI0_QUP_I2C_BUS_ID, + .vcm_pwd = GPIO_CAM_GP_CAM_PWDN, + .vcm_enable = 1, +}; + +#ifdef CONFIG_S5K4E1 +static struct msm_camera_sensor_flash_data flash_s5k4e1 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_s5k4e1 = { + .mount_angle = 90, + .cam_vreg = msm_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_cam_vreg), + .gpio_conf = &gpio_conf_s5k4e1, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k4e1_data = { + .sensor_name = "s5k4e1", + .sensor_reset_enable = 1, + .pmic_gpio_enable = 0, + .pdata = &msm_camera_device_data_csi1[0], + .flash_data = &flash_s5k4e1, + .sensor_platform_info = &sensor_board_info_s5k4e1, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_4_info, +}; +#endif + +#ifdef CONFIG_OV7692 +static struct msm_camera_sensor_platform_info sensor_board_info_ov7692 = { + .mount_angle = 90, + .cam_vreg = msm_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_cam_vreg), + .gpio_conf = &gpio_conf_ov7692, +}; + +static struct msm_camera_sensor_flash_data flash_ov7692 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov7692_data = { + .sensor_name = "ov7692", + .sensor_reset_enable = 0, + .pmic_gpio_enable = 1, + .sensor_lcd_gpio_onoff = lcd_camera_power_onoff, + .sensor_reset = GPIO_SKU1_CAM_VGA_RESET_N, + .sensor_pwd = GPIO_SKU1_CAM_VGA_SHDN, + .pdata = &msm_camera_device_data_csi0[0], + .flash_data = &flash_ov7692, + .sensor_platform_info = &sensor_board_info_ov7692, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = YUV_SENSOR, +}; +#endif + +#ifdef CONFIG_OV5647 + +static struct msm_actuator_info msm_act_main_cam_5_info = { + .board_info = &msm_act_main_cam_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_5, + .bus_id = MSM_GSBI0_QUP_I2C_BUS_ID, + .vcm_pwd = GPIO_SKU3_CAM_5MP_CAM_DRIVER_PWDN, + .vcm_enable = 1, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov5647 = { + .mount_angle = 90, + .cam_vreg = msm_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_cam_vreg), + .gpio_conf = &gpio_conf_ov5647, +}; + +static struct msm_camera_sensor_flash_src msm_flash_src_ov5647 = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_LED1, + ._fsrc.ext_driver_src.led_en = 13, + ._fsrc.ext_driver_src.led_flash_en = 32, +}; + +static struct msm_camera_sensor_flash_data flash_ov5647 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_ov5647, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov5647_data = { + .sensor_name = "ov5647", + .sensor_reset_enable = 1, + .pmic_gpio_enable = 1, + .sensor_lcd_gpio_onoff = lcd_camera_power_onoff, + .sensor_reset = GPIO_SKU3_CAM_5MP_CAMIF_RESET, + .sensor_pwd = GPIO_SKU3_CAM_5MP_SHDN_N, + .pdata = &msm_camera_device_data_csi1[0], + .flash_data = &flash_ov5647, + .sensor_platform_info = &sensor_board_info_ov5647, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, + .actuator_info = &msm_act_main_cam_5_info, +}; + +#endif + +static struct msm_camera_gpio_conf gpio_conf_ov8825 = { + .camera_off_table = camera_off_gpio_table, + .camera_on_table = camera_on_gpio_table, + .gpio_no_mux = 1, +}; + +static struct msm_camera_sensor_flash_data flash_ov8825 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov8825 = { + .mount_angle = 90, + .cam_vreg = msm_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_cam_vreg), + .gpio_conf = &gpio_conf_ov8825, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov8825_data = { + .sensor_name = "ov8825", + .sensor_reset_enable = 1, + .pmic_gpio_enable = 1, + .sensor_reset = GPIO_SKU3_CAM_5MP_CAMIF_RESET, + .sensor_pwd = GPIO_SKU3_CAM_5MP_SHDN_N, + .pdata = &msm_camera_device_data_csi1[1], + .flash_data = &flash_ov8825, + .sensor_platform_info = &sensor_board_info_ov8825, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, +}; + +#ifdef CONFIG_MT9E013 +static struct msm_camera_sensor_flash_data flash_mt9e013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_mt9e013 = { + .mount_angle = 90, + .cam_vreg = msm_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_cam_vreg), + .gpio_conf = &gpio_conf_mt9e013, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = { + .sensor_name = "mt9e013", + .sensor_reset_enable = 1, + .pmic_gpio_enable = 0, + .pdata = &msm_camera_device_data_csi1[1], + .flash_data = &flash_mt9e013, + .sensor_platform_info = &sensor_board_info_mt9e013, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .sensor_type = BAYER_SENSOR, +}; +#endif + +#ifdef CONFIG_WEBCAM_OV9726 +static struct msm_camera_sensor_flash_data flash_ov9726 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov9726 = { + .mount_angle = 90, + .cam_vreg = msm_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_cam_vreg), + .gpio_conf = &gpio_conf_ov9726, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov9726_data = { + .sensor_name = "ov9726", + .sensor_reset_enable = 0, + .pmic_gpio_enable = 0, + .pdata = &msm_camera_device_data_csi0[0], + .flash_data = &flash_ov9726, + .sensor_platform_info = &sensor_board_info_ov9726, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, + .sensor_type = BAYER_SENSOR, +}; +#endif + +static struct platform_device msm_camera_server = { + .name = "msm_cam_server", + .id = 0, +}; + +static void __init msm7x27a_init_cam(void) +{ + if (!(machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm7627a_qrd1() + || machine_is_msm8625_ffa())) { + sensor_board_info_s5k4e1.cam_vreg = NULL; + sensor_board_info_s5k4e1.num_vreg = 0; + sensor_board_info_mt9e013.cam_vreg = NULL; + sensor_board_info_mt9e013.num_vreg = 0; + sensor_board_info_ov9726.cam_vreg = NULL; + sensor_board_info_ov9726.num_vreg = 0; + sensor_board_info_ov7692.cam_vreg = NULL; + sensor_board_info_ov7692.num_vreg = 0; + sensor_board_info_ov5647.cam_vreg = NULL; + sensor_board_info_ov5647.num_vreg = 0; + sensor_board_info_ov8825.cam_vreg = NULL; + sensor_board_info_ov8825.num_vreg = 0; + + } + platform_device_register(&msm_camera_server); + if (machine_is_msm8625_surf() || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm8625_qrd7()) { + platform_device_register(&msm8625_device_csic0); + platform_device_register(&msm8625_device_csic1); + } else { + platform_device_register(&msm7x27a_device_csic0); + platform_device_register(&msm7x27a_device_csic1); + } + if (machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm8625_qrd7()) + *(int *) msm7x27a_device_clkctl.dev.platform_data = 1; + platform_device_register(&msm7x27a_device_clkctl); + platform_device_register(&msm7x27a_device_vfe); +} + +static struct i2c_board_info i2c_camera_devices[] = { + { + I2C_BOARD_INFO("s5k4e1", 0x36), + .platform_data = &msm_camera_sensor_s5k4e1_data, + }, + { + I2C_BOARD_INFO("ov9726", 0x10), + .platform_data = &msm_camera_sensor_ov9726_data, + }, + { + I2C_BOARD_INFO("mt9e013", 0x6C >> 2), + .platform_data = &msm_camera_sensor_mt9e013_data, + }, + { + I2C_BOARD_INFO("ov7692", 0x78), + .platform_data = &msm_camera_sensor_ov7692_data, + }, + { + I2C_BOARD_INFO("ov5647", 0x36 << 1), + .platform_data = &msm_camera_sensor_ov5647_data, + }, + { + I2C_BOARD_INFO("ov8825", 0x6C >> 3), + .platform_data = &msm_camera_sensor_ov8825_data, + }, + { + I2C_BOARD_INFO("sc628a", 0x6E), + }, +}; +#else +static uint32_t camera_off_gpio_table[] = { + GPIO_CFG(15, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; + +static uint32_t camera_on_gpio_table[] = { + GPIO_CFG(15, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), +}; + +#ifdef CONFIG_MSM_CAMERA_FLASH +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT, + ._fsrc.ext_driver_src.led_en = GPIO_CAM_GP_LED_EN1, + ._fsrc.ext_driver_src.led_flash_en = GPIO_CAM_GP_LED_EN2, +}; +#endif + +static struct regulator_bulk_data regs_camera[] = { + { .supply = "msme1", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "gp2", .min_uV = 2850000, .max_uV = 2850000 }, + { .supply = "usb2", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +static void qrd1_camera_gpio_cfg(void) +{ + + int rc = 0; + + rc = gpio_request(QRD_GPIO_CAM_5MP_SHDN_EN, "ov5640"); + if (rc < 0) + pr_err("%s: gpio_request---GPIO_CAM_5MP_SHDN_EN failed!", + __func__); + + + rc = gpio_tlmm_config(GPIO_CFG(QRD_GPIO_CAM_5MP_SHDN_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: unable to enable Power Down gpio for main" + "camera!\n", __func__); + gpio_free(QRD_GPIO_CAM_5MP_SHDN_EN); + } + + + rc = gpio_request(QRD_GPIO_CAM_5MP_RESET, "ov5640"); + if (rc < 0) { + pr_err("%s: gpio_request---GPIO_CAM_5MP_RESET failed!", + __func__); + gpio_free(QRD_GPIO_CAM_5MP_SHDN_EN); + } + + + rc = gpio_tlmm_config(GPIO_CFG(QRD_GPIO_CAM_5MP_RESET, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: unable to enable reset gpio for main camera!\n", + __func__); + gpio_free(QRD_GPIO_CAM_5MP_RESET); + } + + rc = gpio_request(QRD_GPIO_CAM_3MP_PWDN, "ov7692"); + if (rc < 0) + pr_err("%s: gpio_request---GPIO_CAM_3MP_PWDN failed!", + __func__); + + rc = gpio_tlmm_config(GPIO_CFG(QRD_GPIO_CAM_3MP_PWDN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: unable to enable Power Down gpio for front" + "camera!\n", __func__); + gpio_free(QRD_GPIO_CAM_3MP_PWDN); + } + + gpio_direction_output(QRD_GPIO_CAM_5MP_SHDN_EN, 1); + gpio_direction_output(QRD_GPIO_CAM_5MP_RESET, 1); + gpio_direction_output(QRD_GPIO_CAM_3MP_PWDN, 1); +} +#endif + +static void evb_camera_gpio_cfg(void) +{ + int rc = 0; + + rc = gpio_request(msm_camera_sensor_ov5647_data.sensor_pwd, "ov5647"); + if (rc < 0) + pr_err("%s: gpio_request OV5647 sensor_pwd: %d failed!", + __func__, msm_camera_sensor_ov5647_data.sensor_pwd); + + rc = gpio_tlmm_config(GPIO_CFG(msm_camera_sensor_ov5647_data.sensor_pwd, + 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s:unable to enable Powr Dwn gpio for main camera!\n", + __func__); + gpio_free(msm_camera_sensor_ov5647_data.sensor_pwd); + } + + rc = gpio_direction_output(msm_camera_sensor_ov5647_data.sensor_pwd, 1); + if (rc < 0) + pr_err("%s: unable to set gpio: %d direction for ov5647 camera\n", + __func__, msm_camera_sensor_ov5647_data.sensor_pwd); + + rc = gpio_request(msm_camera_sensor_ov5647_data.sensor_reset, "ov5647"); + if (rc < 0) + pr_err("%s: gpio_request OV5647 sensor_reset: %d failed!", + __func__, msm_camera_sensor_ov5647_data.sensor_reset); + + rc = gpio_tlmm_config(GPIO_CFG( + msm_camera_sensor_ov5647_data.sensor_reset, + 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: unable to enable reset gpio for main camera!\n", + __func__); + gpio_free(msm_camera_sensor_ov5647_data.sensor_reset); + } + + rc = gpio_direction_output( + msm_camera_sensor_ov5647_data.sensor_reset, 1); + if (rc < 0) + pr_err("%s: unable to set gpio: %d direction for ov5647 camera\n", + __func__, msm_camera_sensor_ov5647_data.sensor_reset); + +} + +#ifndef CONFIG_MSM_CAMERA_V4L2 + +static void msm_camera_vreg_config(int vreg_en) +{ + int rc = vreg_en ? + regulator_bulk_enable(ARRAY_SIZE(regs_camera), regs_camera) : + regulator_bulk_disable(ARRAY_SIZE(regs_camera), regs_camera); + + if (rc) + pr_err("%s: could not %sable regulators: %d\n", + __func__, vreg_en ? "en" : "dis", rc); +} + +static int config_gpio_table(uint32_t *table, int len) +{ + int rc = 0, i = 0; + + for (i = 0; i < len; i++) { + rc = gpio_tlmm_config(table[i], GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s not able to get gpio\n", __func__); + for (i--; i >= 0; i--) + gpio_tlmm_config(camera_off_gpio_table[i], + GPIO_CFG_ENABLE); + break; + } + } + return rc; +} + +static int config_camera_on_gpios_rear(void) +{ + int rc = 0; + + if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm7627a_qrd1() + || machine_is_msm8625_ffa()) + msm_camera_vreg_config(1); + + rc = config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + if (rc < 0) { + pr_err("%s: CAMSENSOR gpio table request" + "failed\n", __func__); + return rc; + } + + return rc; +} + +static void config_camera_off_gpios_rear(void) +{ + if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm7627a_qrd1() + || machine_is_msm8625_ffa()) + msm_camera_vreg_config(0); + + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static int config_camera_on_gpios_front(void) +{ + int rc = 0; + + if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm7627a_qrd1() + || machine_is_msm8625_ffa()) + msm_camera_vreg_config(1); + + rc = config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + if (rc < 0) { + pr_err("%s: CAMSENSOR gpio table request" + "failed\n", __func__); + return rc; + } + + return rc; +} + +static void config_camera_off_gpios_front(void) +{ + if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm7627a_qrd1() + || machine_is_msm8625_ffa()) + msm_camera_vreg_config(0); + + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +struct msm_camera_device_platform_data msm_camera_device_data_rear = { + .camera_gpio_on = config_camera_on_gpios_rear, + .camera_gpio_off = config_camera_off_gpios_rear, + .ioext.csiphy = 0xA1000000, + .ioext.csisz = 0x00100000, + .ioext.csiirq = INT_CSI_IRQ_1, + .ioclk.mclk_clk_rate = 24000000, + .ioclk.vfe_clk_rate = 192000000, + .ioext.appphy = MSM7XXX_CLK_CTL_PHYS, + .ioext.appsz = MSM7XXX_CLK_CTL_SIZE, +}; + +struct msm_camera_device_platform_data msm_camera_device_data_front = { + .camera_gpio_on = config_camera_on_gpios_front, + .camera_gpio_off = config_camera_off_gpios_front, + .ioext.csiphy = 0xA0F00000, + .ioext.csisz = 0x00100000, + .ioext.csiirq = INT_CSI_IRQ_0, + .ioclk.mclk_clk_rate = 24000000, + .ioclk.vfe_clk_rate = 192000000, + .ioext.appphy = MSM7XXX_CLK_CTL_PHYS, + .ioext.appsz = MSM7XXX_CLK_CTL_SIZE, +}; + +#ifdef CONFIG_OV5647 + +static struct msm_camera_sensor_platform_info ov5647_sensor_7627a_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_src msm_flash_src_ov5647 = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_LED, + ._fsrc.led_src.led_name = "flashlight", + ._fsrc.led_src.led_name_len = 10, +}; + +static struct msm_camera_sensor_flash_data flash_ov5647 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_ov5647, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov5647_data = { + .sensor_name = "ov5647", + .sensor_reset_enable = 1, + .sensor_reset = GPIO_SKU3_CAM_5MP_CAMIF_RESET, + .pmic_gpio_enable = 1, + .sensor_pwd = GPIO_SKU3_CAM_5MP_SHDN_N, + .vcm_pwd = GPIO_SKU3_CAM_5MP_CAM_DRIVER_PWDN, + .vcm_enable = 1, + .pdata = &msm_camera_device_data_rear, + .flash_data = &flash_ov5647, + .sensor_platform_info = &ov5647_sensor_7627a_info, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_ov5647 = { + .name = "msm_camera_ov5647", + .dev = { + .platform_data = &msm_camera_sensor_ov5647_data, + }, +}; +#endif + +#ifdef CONFIG_S5K4E1 +static struct msm_camera_sensor_platform_info s5k4e1_sensor_7627a_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_data flash_s5k4e1 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k4e1_data = { + .sensor_name = "s5k4e1", + .sensor_reset_enable = 1, + .sensor_reset = GPIO_CAM_GP_CAMIF_RESET_N, + .pmic_gpio_enable = 0, + .sensor_pwd = 85, + .vcm_pwd = GPIO_CAM_GP_CAM_PWDN, + .vcm_enable = 1, + .pdata = &msm_camera_device_data_rear, + .flash_data = &flash_s5k4e1, + .sensor_platform_info = &s5k4e1_sensor_7627a_info, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_s5k4e1 = { + .name = "msm_camera_s5k4e1", + .dev = { + .platform_data = &msm_camera_sensor_s5k4e1_data, + }, +}; +#endif + +#ifdef CONFIG_IMX072 +static struct msm_camera_sensor_platform_info imx072_sensor_7627a_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_data flash_imx072 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx072_data = { + .sensor_name = "imx072", + .sensor_reset_enable = 1, + .sensor_reset = GPIO_CAM_GP_CAMIF_RESET_N, /* TODO 106,*/ + .pmic_gpio_enable = 0, + .sensor_pwd = 85, + .vcm_pwd = GPIO_CAM_GP_CAM_PWDN, + .vcm_enable = 1, + .pdata = &msm_camera_device_data_rear, + .flash_data = &flash_imx072, + .sensor_platform_info = &imx072_sensor_7627a_info, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_imx072 = { + .name = "msm_camera_imx072", + .dev = { + .platform_data = &msm_camera_sensor_imx072_data, + }, +}; +#endif + +#ifdef CONFIG_WEBCAM_OV9726 +static struct msm_camera_sensor_info msm_camera_sensor_ov9726_data; +static struct msm_camera_sensor_platform_info ov9726_sensor_7627a_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_data flash_ov9726 = { + .flash_type = MSM_CAMERA_FLASH_NONE, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov9726_data = { + .sensor_name = "ov9726", + .sensor_reset_enable = 0, + .sensor_reset = GPIO_CAM_GP_CAM1MP_XCLR, + .pmic_gpio_enable = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_front, + .flash_data = &flash_ov9726, + .sensor_platform_info = &ov9726_sensor_7627a_info, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_ov9726 = { + .name = "msm_camera_ov9726", + .dev = { + .platform_data = &msm_camera_sensor_ov9726_data, + }, +}; +#else +static inline void msm_camera_vreg_init(void) { } +#endif + +#ifdef CONFIG_MT9E013 +static struct msm_camera_sensor_platform_info mt9e013_sensor_7627a_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_data flash_mt9e013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = { + .sensor_name = "mt9e013", + .sensor_reset = 0, + .sensor_reset_enable = 1, + .pmic_gpio_enable = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_rear, + .flash_data = &flash_mt9e013, + .sensor_platform_info = &mt9e013_sensor_7627a_info, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_mt9e013 = { + .name = "msm_camera_mt9e013", + .dev = { + .platform_data = &msm_camera_sensor_mt9e013_data, + }, +}; +#endif + +#ifdef CONFIG_OV5640 +static struct msm_camera_sensor_platform_info ov5640_sensor_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_src msm_flash_src_ov5640 = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_LED, + ._fsrc.led_src.led_name = "flashlight", + ._fsrc.led_src.led_name_len = 10, +}; + +static struct msm_camera_sensor_flash_data flash_ov5640 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_ov5640, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov5640_data = { + .sensor_name = "ov5640", + .sensor_reset_enable = 1, + .pmic_gpio_enable = 0, + .sensor_reset = QRD_GPIO_CAM_5MP_RESET, + .sensor_pwd = QRD_GPIO_CAM_5MP_SHDN_EN, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_rear, + .flash_data = &flash_ov5640, + .sensor_platform_info = &ov5640_sensor_info, + .csi_if = 1, +}; + +static struct platform_device msm_camera_sensor_ov5640 = { + .name = "msm_camera_ov5640", + .dev = { + .platform_data = &msm_camera_sensor_ov5640_data, + }, +}; +#endif + +#ifdef CONFIG_WEBCAM_OV7692_QRD +static struct msm_camera_sensor_platform_info ov7692_sensor_7627a_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_data flash_ov7692 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov7692_data = { + .sensor_name = "ov7692", + .sensor_reset_enable = 0, + .pmic_gpio_enable = 1, + .sensor_reset = GPIO_SKU1_CAM_VGA_RESET_N, + .sensor_pwd = GPIO_SKU1_CAM_VGA_SHDN, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_front, + .flash_data = &flash_ov7692, + .sensor_platform_info = &ov7692_sensor_7627a_info, + .csi_if = 1, +}; + +static struct platform_device msm_camera_sensor_ov7692 = { + .name = "msm_camera_ov7692", + .dev = { + .platform_data = &msm_camera_sensor_ov7692_data, + }, +}; +#endif + +static struct i2c_board_info i2c_camera_devices[] = { + #ifdef CONFIG_S5K4E1 + { + I2C_BOARD_INFO("s5k4e1", 0x36), + }, + { + I2C_BOARD_INFO("s5k4e1_af", 0x8c >> 1), + }, + #endif + #ifdef CONFIG_WEBCAM_OV9726 + { + I2C_BOARD_INFO("ov9726", 0x10), + }, + #endif + #ifdef CONFIG_IMX072 + { + I2C_BOARD_INFO("imx072", 0x34), + }, + #endif + #ifdef CONFIG_MT9E013 + { + I2C_BOARD_INFO("mt9e013", 0x6C >> 2), + }, + #endif + { + I2C_BOARD_INFO("sc628a", 0x6E), + }, +}; + +static struct i2c_board_info i2c_camera_devices_qrd[] = { + #ifdef CONFIG_OV5640 + { + I2C_BOARD_INFO("ov5640", 0x78 >> 1), + }, + #endif + #ifdef CONFIG_WEBCAM_OV7692_QRD + { + I2C_BOARD_INFO("ov7692", 0x78), + }, + #endif +}; + +static struct i2c_board_info i2c_camera_devices_evb[] = { + #ifdef CONFIG_OV5647 + { + I2C_BOARD_INFO("ov5647", 0x36 << 1), + }, + { + I2C_BOARD_INFO("ov5647_af", 0x18 >> 1), + }, + #endif + #ifdef CONFIG_WEBCAM_OV7692_QRD + { + I2C_BOARD_INFO("ov7692", 0x78), + }, + #endif +}; + +static struct platform_device *camera_devices_msm[] __initdata = { +#ifdef CONFIG_S5K4E1 + &msm_camera_sensor_s5k4e1, +#endif +#ifdef CONFIG_IMX072 + &msm_camera_sensor_imx072, +#endif +#ifdef CONFIG_WEBCAM_OV9726 + &msm_camera_sensor_ov9726, +#endif +#ifdef CONFIG_MT9E013 + &msm_camera_sensor_mt9e013, +#endif +}; + +static struct platform_device *camera_devices_qrd[] __initdata = { +#ifdef CONFIG_OV5640 + &msm_camera_sensor_ov5640, +#endif +#ifdef CONFIG_WEBCAM_OV7692_QRD + &msm_camera_sensor_ov7692, +#endif +}; + +static struct platform_device *camera_devices_evb[] __initdata = { +#ifdef CONFIG_OV5647 + &msm_camera_sensor_ov5647, +#endif +#ifdef CONFIG_WEBCAM_OV7692_QRD + &msm_camera_sensor_ov7692, +#endif + &msm_camera_sensor_ov8825, +}; +#endif + +enum { + SX150X_CAM, +}; + +static struct sx150x_platform_data sx150x_data[] __initdata = { + [SX150X_CAM] = { + .gpio_base = GPIO_CAM_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0, + .io_pulldn_ena = 0, + .io_open_drain_ena = 0x23, + .irq_summary = -1, + }, +}; + +static struct i2c_board_info cam_exp_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1508q", 0x22), + .platform_data = &sx150x_data[SX150X_CAM], + }, +}; + +static void __init register_i2c_devices(void) +{ + i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID, + cam_exp_i2c_info, + ARRAY_SIZE(cam_exp_i2c_info)); +} + +#define LCD_CAMERA_LDO_2V8 35 /* SKU1&SKU3 2.8V LDO */ +#define SKU3_LCD_CAMERA_LDO_1V8 40 /* SKU3 1.8V LDO */ +#define SKU7_LCD_CAMERA_LDO_1V8 58 /* SKU7 1.8V LDO */ + +static int lcd_camera_ldo_1v8 = SKU3_LCD_CAMERA_LDO_1V8; + +static void lcd_camera_power_init(void) +{ + int rc = 0; + + pr_debug("lcd_camera_power_init\n"); + + if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) + lcd_camera_ldo_1v8 = SKU7_LCD_CAMERA_LDO_1V8; + else + lcd_camera_ldo_1v8 = SKU3_LCD_CAMERA_LDO_1V8; + + /* LDO_EXT2V8 */ + if (gpio_request(LCD_CAMERA_LDO_2V8, "lcd_camera_ldo_2v8")) { + pr_err("failed to request gpio lcd_camera_ldo_2v8\n"); + return; + } + + rc = gpio_tlmm_config(GPIO_CFG(LCD_CAMERA_LDO_2V8, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: unable to enable lcd_camera_ldo_2v8!\n", __func__); + goto fail_gpio2; + } + + /* LDO_EVT1V8 */ + if (gpio_request(lcd_camera_ldo_1v8, "lcd_camera_ldo_1v8")) { + pr_err("failed to request gpio lcd_camera_ldo_1v8\n"); + goto fail_gpio2; + } + + rc = gpio_tlmm_config(GPIO_CFG(lcd_camera_ldo_1v8, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("%s: unable to enable lcd_camera_ldo_1v8!\n", __func__); + goto fail_gpio1; + } + + return; + +fail_gpio1: + gpio_free(lcd_camera_ldo_1v8); +fail_gpio2: + gpio_free(LCD_CAMERA_LDO_2V8); + + return; +} + +static int lcd_camera_power_on_sku3(void) +{ + int rc = 0; + + pr_debug("turn on sku3 lcd_camera_ldo_1v8\n"); + gpio_set_value_cansleep(lcd_camera_ldo_1v8, 1); + + pr_debug("turn on sku3 lcd_camera_ldo\n"); + gpio_set_value_cansleep(LCD_CAMERA_LDO_2V8, 1); + + return rc; +} + +static int lcd_camera_power_off_sku3(void) +{ + int rc = 0; + + pr_debug("turn off sku3 lcd_camera_ldo_1v8\n"); + gpio_set_value_cansleep(lcd_camera_ldo_1v8, 0); + + pr_debug("turn off sku3 lcd_camera_ldo\n"); + gpio_set_value_cansleep(LCD_CAMERA_LDO_2V8, 0); + + gpio_free(lcd_camera_ldo_1v8); + gpio_free(LCD_CAMERA_LDO_2V8); + + return rc; +} + +int lcd_camera_power_onoff(int on) +{ + int rc = 0; + + pr_debug("lcd_camera_power_onoff on = %d,\n", on); + + if (on) + rc = lcd_camera_power_on_sku3(); + else + rc = lcd_camera_power_off_sku3(); + + return rc; +} +EXPORT_SYMBOL(lcd_camera_power_onoff); + +void __init msm7627a_camera_init(void) +{ + +#ifndef CONFIG_MSM_CAMERA_V4L2 + int rc; +#endif + + pr_debug("msm7627a_camera_init Entered\n"); + + if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) { + ov7692_cam_req_gpio[0].gpio = + GPIO_SKU7_CAM_VGA_SHDN; + ov7692_cam_gpio_set_tbl[0].gpio = GPIO_SKU7_CAM_VGA_SHDN; + ov7692_cam_gpio_set_tbl[1].gpio = GPIO_SKU7_CAM_VGA_SHDN; + + msm_camera_sensor_ov5647_data.sensor_pwd = + GPIO_SKU7_CAM_5MP_SHDN_N; + msm_camera_sensor_ov5647_data.sensor_reset = + GPIO_SKU7_CAM_5MP_CAMIF_RESET; + + } + + /* LCD and camera power (VREG & LDO) init */ + if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) { + + lcd_camera_power_init(); + evb_camera_gpio_cfg(); + } + +#ifndef CONFIG_MSM_CAMERA_V4L2 + if (machine_is_msm7627a_qrd1()) { + qrd1_camera_gpio_cfg(); + platform_add_devices(camera_devices_qrd, + ARRAY_SIZE(camera_devices_qrd)); + } else if (machine_is_msm7627a_evb() + || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) { + platform_add_devices(camera_devices_evb, + ARRAY_SIZE(camera_devices_evb)); + } else if (machine_is_msm7627a_qrd3()) + return; + else + platform_add_devices(camera_devices_msm, + ARRAY_SIZE(camera_devices_msm)); +#endif + if (!machine_is_msm7627a_qrd1() || !machine_is_msm7627a_evb() + || !machine_is_msm8625_evb() + || !machine_is_msm8625_evt() + || !machine_is_msm7627a_qrd3() + || !machine_is_msm8625_qrd7()) + register_i2c_devices(); +#ifndef CONFIG_MSM_CAMERA_V4L2 + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_camera), regs_camera); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + return; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_camera), regs_camera); + + if (rc) { + pr_err("%s: could not set voltages: %d\n", __func__, rc); + return; + } +#endif + +#if defined(CONFIG_MSM_CAMERA_V4L2) + msm7x27a_init_cam(); +#endif +#ifndef CONFIG_MSM_CAMERA_V4L2 + if (machine_is_msm7627a_qrd1()) { + i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID, + i2c_camera_devices_qrd, + ARRAY_SIZE(i2c_camera_devices_qrd)); + } else if (machine_is_msm7627a_evb() + || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) { + pr_debug("machine_is_msm7627a_evb i2c_register_board_info\n"); + i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID, + i2c_camera_devices_evb, + ARRAY_SIZE(i2c_camera_devices_evb)); + } else +#endif + pr_debug("i2c_register_board_info\n"); + i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID, + i2c_camera_devices, + ARRAY_SIZE(i2c_camera_devices)); +} diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c new file mode 100644 index 00000000000..bd38f30214c --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a-display.c @@ -0,0 +1,1326 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "board-msm7627a.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_SIZE 0x4BF000 +#define MSM7x25A_MSM_FB_SIZE 0x1C2000 +#define MSM8x25_MSM_FB_SIZE 0x5FA000 +#else +#define MSM_FB_SIZE 0x32A000 +#define MSM7x25A_MSM_FB_SIZE 0x12C000 +#define MSM8x25_MSM_FB_SIZE 0x3FC000 +#endif + +/* + * Reserve enough v4l2 space for a double buffered full screen + * res image (864x480x1.5x2) + */ +#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 1244160 + +static unsigned fb_size = MSM_FB_SIZE; +static int __init fb_size_setup(char *p) +{ + fb_size = memparse(p, NULL); + return 0; +} + +early_param("fb_size", fb_size_setup); + +static uint32_t lcdc_truly_gpio_initialized; +static struct regulator_bulk_data regs_truly_lcdc[] = { + { .supply = "rfrx1", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +#define SKU3_LCDC_GPIO_DISPLAY_RESET 90 +#define SKU3_LCDC_GPIO_SPI_MOSI 19 +#define SKU3_LCDC_GPIO_SPI_CLK 20 +#define SKU3_LCDC_GPIO_SPI_CS0_N 21 +#define SKU3_LCDC_LCD_CAMERA_LDO_2V8 35 /*LCD_CAMERA_LDO_2V8*/ +#define SKU3_LCDC_LCD_CAMERA_LDO_1V8 34 /*LCD_CAMERA_LDO_1V8*/ +#define SKU3_1_LCDC_LCD_CAMERA_LDO_1V8 58 /*LCD_CAMERA_LDO_1V8*/ + +static uint32_t lcdc_truly_gpio_table[] = { + 19, + 20, + 21, + 89, + 90, +}; + +static char *lcdc_gpio_name_table[5] = { + "spi_mosi", + "spi_clk", + "spi_cs", + "gpio_bkl_en", + "gpio_disp_reset", +}; + +static int lcdc_truly_gpio_init(void) +{ + int i; + int rc = 0; + + if (!lcdc_truly_gpio_initialized) { + for (i = 0; i < ARRAY_SIZE(lcdc_truly_gpio_table); i++) { + rc = gpio_request(lcdc_truly_gpio_table[i], + lcdc_gpio_name_table[i]); + if (rc < 0) { + pr_err("Error request gpio %s\n", + lcdc_gpio_name_table[i]); + goto truly_gpio_fail; + } + rc = gpio_tlmm_config(GPIO_CFG(lcdc_truly_gpio_table[i], + 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("Error config lcdc gpio:%d\n", + lcdc_truly_gpio_table[i]); + goto truly_gpio_fail; + } + rc = gpio_direction_output(lcdc_truly_gpio_table[i], 0); + if (rc < 0) { + pr_err("Error direct lcdc gpio:%d\n", + lcdc_truly_gpio_table[i]); + goto truly_gpio_fail; + } + } + + lcdc_truly_gpio_initialized = 1; + } + + return rc; + +truly_gpio_fail: + for (; i >= 0; i--) { + pr_err("Freeing GPIO: %d", lcdc_truly_gpio_table[i]); + gpio_free(lcdc_truly_gpio_table[i]); + } + + lcdc_truly_gpio_initialized = 0; + return rc; +} + + +void sku3_lcdc_lcd_camera_power_init(void) +{ + int rc = 0; + u32 socinfo = socinfo_get_platform_type(); + + /* LDO_EXT2V8 */ + if (gpio_request(SKU3_LCDC_LCD_CAMERA_LDO_2V8, "lcd_camera_ldo_2v8")) { + pr_err("failed to request gpio lcd_camera_ldo_2v8\n"); + return; + } + + rc = gpio_tlmm_config(GPIO_CFG(SKU3_LCDC_LCD_CAMERA_LDO_2V8, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + if (rc < 0) { + pr_err("%s:unable to enable lcd_camera_ldo_2v8!\n", __func__); + goto fail_gpio2; + } + + /* LDO_EVT1V8 */ + if (socinfo == 0x0B) { + if (gpio_request(SKU3_LCDC_LCD_CAMERA_LDO_1V8, + "lcd_camera_ldo_1v8")) { + pr_err("failed to request gpio lcd_camera_ldo_1v8\n"); + goto fail_gpio1; + } + + rc = gpio_tlmm_config(GPIO_CFG(SKU3_LCDC_LCD_CAMERA_LDO_1V8, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + if (rc < 0) { + pr_err("%s: unable to enable lcdc_camera_ldo_1v8!\n", + __func__); + goto fail_gpio1; + } + } else if (socinfo == 0x0F || machine_is_msm8625_qrd7()) { + if (gpio_request(SKU3_1_LCDC_LCD_CAMERA_LDO_1V8, + "lcd_camera_ldo_1v8")) { + pr_err("failed to request gpio lcd_camera_ldo_1v8\n"); + goto fail_gpio1; + } + + rc = gpio_tlmm_config(GPIO_CFG(SKU3_1_LCDC_LCD_CAMERA_LDO_1V8, + 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + + if (rc < 0) { + pr_err("%s: unable to enable lcdc_camera_ldo_1v8!\n", + __func__); + goto fail_gpio1; + } + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_truly_lcdc), + regs_truly_lcdc); + if (rc) + pr_err("%s: could not get regulators: %d\n", __func__, rc); + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_truly_lcdc), + regs_truly_lcdc); + if (rc) + pr_err("%s: could not set voltages: %d\n", __func__, rc); + + return; + +fail_gpio1: + if (socinfo == 0x0B) + gpio_free(SKU3_LCDC_LCD_CAMERA_LDO_1V8); + else if (socinfo == 0x0F || machine_is_msm8625_qrd7()) + gpio_free(SKU3_1_LCDC_LCD_CAMERA_LDO_1V8); +fail_gpio2: + gpio_free(SKU3_LCDC_LCD_CAMERA_LDO_2V8); + return; +} + +int sku3_lcdc_lcd_camera_power_onoff(int on) +{ + int rc = 0; + u32 socinfo = socinfo_get_platform_type(); + + if (on) { + if (socinfo == 0x0B) + gpio_set_value_cansleep(SKU3_LCDC_LCD_CAMERA_LDO_1V8, + 1); + else if (socinfo == 0x0F || machine_is_msm8625_qrd7()) + gpio_set_value_cansleep(SKU3_1_LCDC_LCD_CAMERA_LDO_1V8, + 1); + + gpio_set_value_cansleep(SKU3_LCDC_LCD_CAMERA_LDO_2V8, 1); + + rc = regulator_bulk_enable(ARRAY_SIZE(regs_truly_lcdc), + regs_truly_lcdc); + if (rc) + pr_err("%s: could not enable regulators: %d\n", + __func__, rc); + } else { + if (socinfo == 0x0B) + gpio_set_value_cansleep(SKU3_LCDC_LCD_CAMERA_LDO_1V8, + 0); + else if (socinfo == 0x0F || machine_is_msm8625_qrd7()) + gpio_set_value_cansleep(SKU3_1_LCDC_LCD_CAMERA_LDO_1V8, + 0); + + gpio_set_value_cansleep(SKU3_LCDC_LCD_CAMERA_LDO_2V8, 0); + + rc = regulator_bulk_disable(ARRAY_SIZE(regs_truly_lcdc), + regs_truly_lcdc); + if (rc) + pr_err("%s: could not disable regulators: %d\n", + __func__, rc); + } + + return rc; +} + +static int sku3_lcdc_power_save(int on) +{ + int rc = 0; + + if (on) { + sku3_lcdc_lcd_camera_power_onoff(1); + rc = lcdc_truly_gpio_init(); + if (rc < 0) { + pr_err("%s(): Truly GPIO initializations failed", + __func__); + return rc; + } + + if (lcdc_truly_gpio_initialized) { + /*LCD reset*/ + gpio_set_value(SKU3_LCDC_GPIO_DISPLAY_RESET, 1); + msleep(20); + gpio_set_value(SKU3_LCDC_GPIO_DISPLAY_RESET, 0); + msleep(20); + gpio_set_value(SKU3_LCDC_GPIO_DISPLAY_RESET, 1); + msleep(20); + } + } else { + /* pull down LCD IO to avoid current leakage */ + gpio_set_value(SKU3_LCDC_GPIO_SPI_MOSI, 0); + gpio_set_value(SKU3_LCDC_GPIO_SPI_CLK, 0); + gpio_set_value(SKU3_LCDC_GPIO_SPI_CS0_N, 0); + gpio_set_value(SKU3_LCDC_GPIO_DISPLAY_RESET, 0); + + sku3_lcdc_lcd_camera_power_onoff(0); + } + return rc; +} + +static struct msm_panel_common_pdata lcdc_truly_panel_data = { + .panel_config_gpio = NULL, + .gpio_num = lcdc_truly_gpio_table, +}; + +static struct platform_device lcdc_truly_panel_device = { + .name = "lcdc_truly_hvga_ips3p2335_pt", + .id = 0, + .dev = { + .platform_data = &lcdc_truly_panel_data, + } +}; + +static struct regulator_bulk_data regs_lcdc[] = { + { .supply = "gp2", .min_uV = 2850000, .max_uV = 2850000 }, + { .supply = "msme1", .min_uV = 1800000, .max_uV = 1800000 }, +}; +static uint32_t lcdc_gpio_initialized; + +static void lcdc_toshiba_gpio_init(void) +{ + int rc = 0; + if (!lcdc_gpio_initialized) { + if (gpio_request(GPIO_SPI_CLK, "spi_clk")) { + pr_err("failed to request gpio spi_clk\n"); + return; + } + if (gpio_request(GPIO_SPI_CS0_N, "spi_cs")) { + pr_err("failed to request gpio spi_cs0_N\n"); + goto fail_gpio6; + } + if (gpio_request(GPIO_SPI_MOSI, "spi_mosi")) { + pr_err("failed to request gpio spi_mosi\n"); + goto fail_gpio5; + } + if (gpio_request(GPIO_SPI_MISO, "spi_miso")) { + pr_err("failed to request gpio spi_miso\n"); + goto fail_gpio4; + } + if (gpio_request(GPIO_DISPLAY_PWR_EN, "gpio_disp_pwr")) { + pr_err("failed to request gpio_disp_pwr\n"); + goto fail_gpio3; + } + if (gpio_request(GPIO_BACKLIGHT_EN, "gpio_bkl_en")) { + pr_err("failed to request gpio_bkl_en\n"); + goto fail_gpio2; + } + pmapp_disp_backlight_init(); + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_lcdc), + regs_lcdc); + if (rc) { + pr_err("%s: could not get regulators: %d\n", + __func__, rc); + goto fail_gpio1; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_lcdc), + regs_lcdc); + if (rc) { + pr_err("%s: could not set voltages: %d\n", + __func__, rc); + goto fail_vreg; + } + lcdc_gpio_initialized = 1; + } + return; +fail_vreg: + regulator_bulk_free(ARRAY_SIZE(regs_lcdc), regs_lcdc); +fail_gpio1: + gpio_free(GPIO_BACKLIGHT_EN); +fail_gpio2: + gpio_free(GPIO_DISPLAY_PWR_EN); +fail_gpio3: + gpio_free(GPIO_SPI_MISO); +fail_gpio4: + gpio_free(GPIO_SPI_MOSI); +fail_gpio5: + gpio_free(GPIO_SPI_CS0_N); +fail_gpio6: + gpio_free(GPIO_SPI_CLK); + lcdc_gpio_initialized = 0; +} + +static uint32_t lcdc_gpio_table[] = { + GPIO_SPI_CLK, + GPIO_SPI_CS0_N, + GPIO_SPI_MOSI, + GPIO_DISPLAY_PWR_EN, + GPIO_BACKLIGHT_EN, + GPIO_SPI_MISO, +}; + +static void config_lcdc_gpio_table(uint32_t *table, int len, unsigned enable) +{ + int n; + + if (lcdc_gpio_initialized) { + /* All are IO Expander GPIOs */ + for (n = 0; n < (len - 1); n++) + gpio_direction_output(table[n], 1); + } +} + +static void lcdc_toshiba_config_gpios(int enable) +{ + config_lcdc_gpio_table(lcdc_gpio_table, + ARRAY_SIZE(lcdc_gpio_table), enable); +} + +static int msm_fb_lcdc_power_save(int on) +{ + int rc = 0; + /* Doing the init of the LCDC GPIOs very late as they are from + an I2C-controlled IO Expander */ + lcdc_toshiba_gpio_init(); + + if (lcdc_gpio_initialized) { + gpio_set_value_cansleep(GPIO_DISPLAY_PWR_EN, on); + gpio_set_value_cansleep(GPIO_BACKLIGHT_EN, on); + + rc = on ? regulator_bulk_enable( + ARRAY_SIZE(regs_lcdc), regs_lcdc) : + regulator_bulk_disable( + ARRAY_SIZE(regs_lcdc), regs_lcdc); + + if (rc) + pr_err("%s: could not %sable regulators: %d\n", + __func__, on ? "en" : "dis", rc); + } + + return rc; +} + +static int lcdc_toshiba_set_bl(int level) +{ + int ret; + + ret = pmapp_disp_backlight_set_brightness(level); + if (ret) + pr_err("%s: can't set lcd backlight!\n", __func__); + + return ret; +} + + +static int msm_lcdc_power_save(int on) +{ + int rc = 0; + if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) + rc = sku3_lcdc_power_save(on); + else + rc = msm_fb_lcdc_power_save(on); + + return rc; +} + +static struct lcdc_platform_data lcdc_pdata = { + .lcdc_gpio_config = NULL, + .lcdc_power_save = msm_lcdc_power_save, +}; + +static int lcd_panel_spi_gpio_num[] = { + GPIO_SPI_MOSI, /* spi_sdi */ + GPIO_SPI_MISO, /* spi_sdoi */ + GPIO_SPI_CLK, /* spi_clk */ + GPIO_SPI_CS0_N, /* spi_cs */ +}; + +static struct msm_panel_common_pdata lcdc_toshiba_panel_data = { + .panel_config_gpio = lcdc_toshiba_config_gpios, + .pmic_backlight = lcdc_toshiba_set_bl, + .gpio_num = lcd_panel_spi_gpio_num, +}; + +static struct platform_device lcdc_toshiba_panel_device = { + .name = "lcdc_toshiba_fwvga_pt", + .id = 0, + .dev = { + .platform_data = &lcdc_toshiba_panel_data, + } +}; + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE +static struct resource msm_v4l2_video_overlay_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; +#endif + +#define LCDC_TOSHIBA_FWVGA_PANEL_NAME "lcdc_toshiba_fwvga_pt" +#define MIPI_CMD_RENESAS_FWVGA_PANEL_NAME "mipi_cmd_renesas_fwvga" + +static int msm_fb_detect_panel(const char *name) +{ + int ret = -ENODEV; + + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) { + if (!strncmp(name, "lcdc_toshiba_fwvga_pt", 21) || + !strncmp(name, "mipi_cmd_renesas_fwvga", 22)) + ret = 0; + } else if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm8625_ffa()) { + if (!strncmp(name, "mipi_cmd_renesas_fwvga", 22)) + ret = 0; + } else if (machine_is_msm7627a_qrd1()) { + if (!strncmp(name, "mipi_video_truly_wvga", 21)) + ret = 0; + } else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) { + if (!strncmp(name, "lcdc_truly_hvga_ips3p2335_pt", 28)) + ret = 0; + } else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()) { + if (!strncmp(name, "mipi_cmd_nt35510_wvga", 21)) + ret = 0; + } else if (machine_is_msm8625_evt()) { + if (!strncmp(name, "mipi_video_nt35510_wvga", 23)) + ret = 0; + } + +#if !defined(CONFIG_FB_MSM_LCDC_AUTO_DETECT) && \ + !defined(CONFIG_FB_MSM_MIPI_PANEL_AUTO_DETECT) && \ + !defined(CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT) + if (machine_is_msm7x27a_surf() || + machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) { + if (!strncmp(name, LCDC_TOSHIBA_FWVGA_PANEL_NAME, + strnlen(LCDC_TOSHIBA_FWVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + } +#endif + + return ret; +} + +static int mipi_truly_set_bl(int on) +{ + gpio_set_value_cansleep(QRD_GPIO_BACKLIGHT_EN, !!on); + + return 1; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev = { + .platform_data = &msm_fb_pdata, + } +}; + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE +static struct platform_device msm_v4l2_video_overlay_device = { + .name = "msm_v4l2_overlay_pd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_v4l2_video_overlay_resources), + .resource = msm_v4l2_video_overlay_resources, + }; +#endif + + +#ifdef CONFIG_FB_MSM_MIPI_DSI +static int mipi_renesas_set_bl(int level) +{ + int ret; + + ret = pmapp_disp_backlight_set_brightness(level); + + if (ret) + pr_err("%s: can't set lcd backlight!\n", __func__); + + return ret; +} + +static struct msm_panel_common_pdata mipi_renesas_pdata = { + .pmic_backlight = mipi_renesas_set_bl, +}; + + +static struct platform_device mipi_dsi_renesas_panel_device = { + .name = "mipi_renesas", + .id = 0, + .dev = { + .platform_data = &mipi_renesas_pdata, + } +}; +#endif + +static int evb_backlight_control(int level) +{ + + int i = 0; + int remainder; + /* device address byte = 0x72 */ + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + gpio_set_value(96, 0); + udelay(33); + gpio_set_value(96, 1); + udelay(67); + gpio_set_value(96, 0); + udelay(33); + gpio_set_value(96, 1); + udelay(67); + gpio_set_value(96, 0); + udelay(33); + gpio_set_value(96, 1); + udelay(67); + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + gpio_set_value(96, 0); + udelay(33); + gpio_set_value(96, 1); + udelay(67); + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + + /* t-EOS and t-start */ + gpio_set_value(96, 0); + ndelay(4200); + gpio_set_value(96, 1); + ndelay(9000); + + /* data byte */ + /* RFA = 0 */ + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + + /* Address bits */ + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + + /* Data bits */ + for (i = 0; i < 5; i++) { + remainder = (level) & (16); + if (remainder) { + gpio_set_value(96, 0); + udelay(33); + gpio_set_value(96, 1); + udelay(67); + } else { + gpio_set_value(96, 0); + udelay(67); + gpio_set_value(96, 1); + udelay(33); + } + level = level << 1; + } + + /* t-EOS */ + gpio_set_value(96, 0); + ndelay(12000); + gpio_set_value(96, 1); + return 0; +} + +static int mipi_NT35510_rotate_panel(void) +{ + int rotate = 0; + if (machine_is_msm8625_evt()) + rotate = 1; + + return rotate; +} + +static struct msm_panel_common_pdata mipi_truly_pdata = { + .pmic_backlight = mipi_truly_set_bl, +}; + +static struct platform_device mipi_dsi_truly_panel_device = { + .name = "mipi_truly", + .id = 0, + .dev = { + .platform_data = &mipi_truly_pdata, + } +}; + +static struct msm_panel_common_pdata mipi_NT35510_pdata = { + .pmic_backlight = evb_backlight_control, + .rotate_panel = mipi_NT35510_rotate_panel, +}; + +static struct platform_device mipi_dsi_NT35510_panel_device = { + .name = "mipi_NT35510", + .id = 0, + .dev = { + .platform_data = &mipi_NT35510_pdata, + } +}; + +static struct msm_panel_common_pdata mipi_NT35516_pdata = { + .pmic_backlight = evb_backlight_control, +}; + +static struct platform_device mipi_dsi_NT35516_panel_device = { + .name = "mipi_truly_tft540960_1_e", + .id = 0, + .dev = { + .platform_data = &mipi_NT35516_pdata, + } +}; + +static struct platform_device *msm_fb_devices[] __initdata = { + &msm_fb_device, + &lcdc_toshiba_panel_device, +#ifdef CONFIG_FB_MSM_MIPI_DSI + &mipi_dsi_renesas_panel_device, +#endif +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE + &msm_v4l2_video_overlay_device, +#endif +}; + +static struct platform_device *qrd_fb_devices[] __initdata = { + &msm_fb_device, + &mipi_dsi_truly_panel_device, +}; + +static struct platform_device *qrd3_fb_devices[] __initdata = { + &msm_fb_device, + &lcdc_truly_panel_device, +}; + +static struct platform_device *evb_fb_devices[] __initdata = { + &msm_fb_device, + &mipi_dsi_NT35510_panel_device, + &mipi_dsi_NT35516_panel_device, +}; + +void __init msm_msm7627a_allocate_memory_regions(void) +{ + void *addr; + unsigned long fb_size; + + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) + fb_size = MSM7x25A_MSM_FB_SIZE; + else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() + || machine_is_msm8625_evt()) + fb_size = MSM8x25_MSM_FB_SIZE; + else + fb_size = MSM_FB_SIZE; + + addr = alloc_bootmem_align(fb_size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + fb_size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", fb_size, + addr, __pa(addr)); + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE + fb_size = MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE; + addr = alloc_bootmem_align(fb_size, 0x1000); + msm_v4l2_video_overlay_resources[0].start = __pa(addr); + msm_v4l2_video_overlay_resources[0].end = + msm_v4l2_video_overlay_resources[0].start + fb_size - 1; + pr_debug("allocating %lu bytes at %p (%lx physical) for v4l2\n", + fb_size, addr, __pa(addr)); +#endif + +} + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = 97, + .mdp_rev = MDP_REV_303, +}; + +#define GPIO_LCDC_BRDG_PD 128 +#define GPIO_LCDC_BRDG_RESET_N 129 +#define GPIO_LCD_DSI_SEL 125 +#define LCDC_RESET_PHYS 0x90008014 + +static void __iomem *lcdc_reset_ptr; + +static unsigned mipi_dsi_gpio[] = { + GPIO_CFG(GPIO_LCDC_BRDG_RESET_N, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* LCDC_BRDG_RESET_N */ + GPIO_CFG(GPIO_LCDC_BRDG_PD, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* LCDC_BRDG_PD */ +}; + +static unsigned lcd_dsi_sel_gpio[] = { + GPIO_CFG(GPIO_LCD_DSI_SEL, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_2MA), +}; + +enum { + DSI_SINGLE_LANE = 1, + DSI_TWO_LANES, +}; + +static int msm_fb_get_lane_config(void) +{ + /* For MSM7627A SURF/FFA and QRD */ + int rc = DSI_TWO_LANES; + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { + rc = DSI_SINGLE_LANE; + pr_info("DSI_SINGLE_LANES\n"); + } else { + pr_info("DSI_TWO_LANES\n"); + } + return rc; +} + +static int msm_fb_dsi_client_msm_reset(void) +{ + int rc = 0; + + rc = gpio_request(GPIO_LCDC_BRDG_RESET_N, "lcdc_brdg_reset_n"); + if (rc < 0) { + pr_err("failed to request lcd brdg reset_n\n"); + return rc; + } + + rc = gpio_request(GPIO_LCDC_BRDG_PD, "lcdc_brdg_pd"); + if (rc < 0) { + pr_err("failed to request lcd brdg pd\n"); + return rc; + } + + rc = gpio_tlmm_config(mipi_dsi_gpio[0], GPIO_CFG_ENABLE); + if (rc) { + pr_err("Failed to enable LCDC Bridge reset enable\n"); + goto gpio_error; + } + + rc = gpio_tlmm_config(mipi_dsi_gpio[1], GPIO_CFG_ENABLE); + if (rc) { + pr_err("Failed to enable LCDC Bridge pd enable\n"); + goto gpio_error2; + } + + rc = gpio_direction_output(GPIO_LCDC_BRDG_RESET_N, 1); + rc |= gpio_direction_output(GPIO_LCDC_BRDG_PD, 1); + gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 0); + + if (!rc) { + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() + || machine_is_msm8625_surf()) { + lcdc_reset_ptr = ioremap_nocache(LCDC_RESET_PHYS, + sizeof(uint32_t)); + + if (!lcdc_reset_ptr) + return 0; + } + return rc; + } else { + goto gpio_error; + } + +gpio_error2: + pr_err("Failed GPIO bridge pd\n"); + gpio_free(GPIO_LCDC_BRDG_PD); + +gpio_error: + pr_err("Failed GPIO bridge reset\n"); + gpio_free(GPIO_LCDC_BRDG_RESET_N); + return rc; +} + +static int mipi_truly_sel_mode(int video_mode) +{ + int rc = 0; + + rc = gpio_request(GPIO_LCD_DSI_SEL, "lcd_dsi_sel"); + if (rc < 0) + goto gpio_error; + + rc = gpio_tlmm_config(lcd_dsi_sel_gpio[0], GPIO_CFG_ENABLE); + if (rc) + goto gpio_error; + + rc = gpio_direction_output(GPIO_LCD_DSI_SEL, 1); + if (!rc) { + gpio_set_value_cansleep(GPIO_LCD_DSI_SEL, video_mode); + return rc; + } else { + goto gpio_error; + } + +gpio_error: + pr_err("mipi_truly_sel_mode failed\n"); + gpio_free(GPIO_LCD_DSI_SEL); + return rc; +} + +static int msm_fb_dsi_client_qrd1_reset(void) +{ + int rc = 0; + + rc = gpio_request(GPIO_LCDC_BRDG_RESET_N, "lcdc_brdg_reset_n"); + if (rc < 0) { + pr_err("failed to request lcd brdg reset_n\n"); + return rc; + } + + rc = gpio_tlmm_config(mipi_dsi_gpio[0], GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("Failed to enable LCDC Bridge reset enable\n"); + return rc; + } + + rc = gpio_direction_output(GPIO_LCDC_BRDG_RESET_N, 1); + if (rc < 0) { + pr_err("Failed GPIO bridge pd\n"); + gpio_free(GPIO_LCDC_BRDG_RESET_N); + return rc; + } + + mipi_truly_sel_mode(1); + + return rc; +} + +#define GPIO_QRD3_LCD_BRDG_RESET_N 85 +#define GPIO_QRD3_LCD_BACKLIGHT_EN 96 +#define GPIO_QRD3_LCD_EXT_2V85_EN 35 +#define GPIO_QRD3_LCD_EXT_1V8_EN 40 + +static unsigned qrd3_mipi_dsi_gpio[] = { + GPIO_CFG(GPIO_QRD3_LCD_BRDG_RESET_N, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), /* GPIO_QRD3_LCD_BRDG_RESET_N */ + GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), /* GPIO_QRD3_LCD_BACKLIGHT_EN */ + GPIO_CFG(GPIO_QRD3_LCD_EXT_2V85_EN, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), /* GPIO_QRD3_LCD_EXT_2V85_EN */ + GPIO_CFG(GPIO_QRD3_LCD_EXT_1V8_EN, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), /* GPIO_QRD3_LCD_EXT_1V8_EN */ +}; + +static int msm_fb_dsi_client_qrd3_reset(void) +{ + int rc = 0; + + rc = gpio_request(GPIO_QRD3_LCD_BRDG_RESET_N, "qrd3_lcd_brdg_reset_n"); + if (rc < 0) { + pr_err("failed to request qrd3 lcd brdg reset_n\n"); + return rc; + } + + return rc; +} + +static int msm_fb_dsi_client_reset(void) +{ + int rc = 0; + + if (machine_is_msm7627a_qrd1()) + rc = msm_fb_dsi_client_qrd1_reset(); + else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() + || machine_is_msm8625_evt()) + rc = msm_fb_dsi_client_qrd3_reset(); + else + rc = msm_fb_dsi_client_msm_reset(); + + return rc; + +} + +static struct regulator_bulk_data regs_dsi[] = { + { .supply = "gp2", .min_uV = 2850000, .max_uV = 2850000 }, + { .supply = "msme1", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +static int dsi_gpio_initialized; + +static int mipi_dsi_panel_msm_power(int on) +{ + int rc = 0; + uint32_t lcdc_reset_cfg; + + /* I2C-controlled GPIO Expander -init of the GPIOs very late */ + if (unlikely(!dsi_gpio_initialized)) { + pmapp_disp_backlight_init(); + + rc = gpio_request(GPIO_DISPLAY_PWR_EN, "gpio_disp_pwr"); + if (rc < 0) { + pr_err("failed to request gpio_disp_pwr\n"); + return rc; + } + + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() + || machine_is_msm8625_surf()) { + rc = gpio_direction_output(GPIO_DISPLAY_PWR_EN, 1); + if (rc < 0) { + pr_err("failed to enable display pwr\n"); + goto fail_gpio1; + } + + rc = gpio_request(GPIO_BACKLIGHT_EN, "gpio_bkl_en"); + if (rc < 0) { + pr_err("failed to request gpio_bkl_en\n"); + goto fail_gpio1; + } + + rc = gpio_direction_output(GPIO_BACKLIGHT_EN, 1); + if (rc < 0) { + pr_err("failed to enable backlight\n"); + goto fail_gpio2; + } + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_dsi), regs_dsi); + if (rc) { + pr_err("%s: could not get regulators: %d\n", + __func__, rc); + goto fail_gpio2; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_dsi), + regs_dsi); + if (rc) { + pr_err("%s: could not set voltages: %d\n", + __func__, rc); + goto fail_vreg; + } + if (pmapp_disp_backlight_set_brightness(100)) + pr_err("backlight set brightness failed\n"); + + dsi_gpio_initialized = 1; + } + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) { + gpio_set_value_cansleep(GPIO_DISPLAY_PWR_EN, on); + gpio_set_value_cansleep(GPIO_BACKLIGHT_EN, on); + } else if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm8625_ffa()) { + if (on) { + /* This line drives an active low pin on FFA */ + rc = gpio_direction_output(GPIO_DISPLAY_PWR_EN, !on); + if (rc < 0) + pr_err("failed to set direction for " + "display pwr\n"); + } else { + gpio_set_value_cansleep(GPIO_DISPLAY_PWR_EN, !on); + rc = gpio_direction_input(GPIO_DISPLAY_PWR_EN); + if (rc < 0) + pr_err("failed to set direction for " + "display pwr\n"); + } + } + + if (on) { + gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 0); + + if (machine_is_msm7x27a_surf() || + machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) { + lcdc_reset_cfg = readl_relaxed(lcdc_reset_ptr); + rmb(); + lcdc_reset_cfg &= ~1; + + writel_relaxed(lcdc_reset_cfg, lcdc_reset_ptr); + msleep(20); + wmb(); + lcdc_reset_cfg |= 1; + writel_relaxed(lcdc_reset_cfg, lcdc_reset_ptr); + } else { + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1); + } + } else { + gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 1); + } + + rc = on ? regulator_bulk_enable(ARRAY_SIZE(regs_dsi), regs_dsi) : + regulator_bulk_disable(ARRAY_SIZE(regs_dsi), regs_dsi); + + if (rc) + pr_err("%s: could not %sable regulators: %d\n", + __func__, on ? "en" : "dis", rc); + + return rc; +fail_vreg: + regulator_bulk_free(ARRAY_SIZE(regs_dsi), regs_dsi); +fail_gpio2: + gpio_free(GPIO_BACKLIGHT_EN); +fail_gpio1: + gpio_free(GPIO_DISPLAY_PWR_EN); + dsi_gpio_initialized = 0; + return rc; +} + +static int mipi_dsi_panel_qrd1_power(int on) +{ + int rc = 0; + + if (!dsi_gpio_initialized) { + rc = gpio_request(QRD_GPIO_BACKLIGHT_EN, "gpio_bkl_en"); + if (rc < 0) + return rc; + + rc = gpio_tlmm_config(GPIO_CFG(QRD_GPIO_BACKLIGHT_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("failed GPIO_BACKLIGHT_EN tlmm config\n"); + return rc; + } + + rc = gpio_direction_output(QRD_GPIO_BACKLIGHT_EN, 1); + if (rc < 0) { + pr_err("failed to enable backlight\n"); + gpio_free(QRD_GPIO_BACKLIGHT_EN); + return rc; + } + dsi_gpio_initialized = 1; + } + + gpio_set_value_cansleep(QRD_GPIO_BACKLIGHT_EN, !!on); + + if (on) { + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1); + msleep(20); + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1); + + } + + return rc; +} + +static int qrd3_dsi_gpio_initialized; + +static int mipi_dsi_panel_qrd3_power(int on) +{ + int rc = 0; + + if (!qrd3_dsi_gpio_initialized) { + rc = gpio_request(GPIO_QRD3_LCD_BACKLIGHT_EN, + "qrd3_gpio_bkl_en"); + if (rc < 0) + return rc; + + rc = gpio_request(GPIO_QRD3_LCD_EXT_2V85_EN, + "qrd3_gpio_ext_2v85_en"); + if (rc < 0) + return rc; + + rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_EXT_2V85_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("failed QRD3 GPIO_QRD3_LCD_EXT_2V85_EN tlmm config\n"); + return rc; + } + + rc = gpio_direction_output(GPIO_QRD3_LCD_EXT_2V85_EN, 1); + if (rc < 0) { + pr_err("failed to enable external 2V85\n"); + gpio_free(GPIO_QRD3_LCD_EXT_2V85_EN); + return rc; + } + + rc = gpio_request(GPIO_QRD3_LCD_EXT_1V8_EN, + "qrd3_gpio_ext_1v8_en"); + if (rc < 0) + return rc; + + rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_EXT_1V8_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("failed QRD3 GPIO_QRD3_LCD_EXT_1V8_EN tlmm config\n"); + return rc; + } + + rc = gpio_direction_output(GPIO_QRD3_LCD_EXT_1V8_EN, 1); + if (rc < 0) { + pr_err("failed to enable external 1v8\n"); + gpio_free(GPIO_QRD3_LCD_EXT_1V8_EN); + return rc; + } + + qrd3_dsi_gpio_initialized = 1; + } + + if (on) { + rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + if (rc < 0) { + pr_err("failed QRD3 GPIO_BACKLIGHT_EN tlmm config\n"); + return rc; + } + rc = gpio_direction_output(GPIO_QRD3_LCD_BACKLIGHT_EN, 1); + if (rc < 0) { + pr_err("failed to enable backlight\n"); + gpio_free(GPIO_QRD3_LCD_BACKLIGHT_EN); + return rc; + } + + gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 1); + udelay(190); + gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 0); + udelay(286); + gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 1); + /* 1 wire mode starts from this low to high transition */ + udelay(50); + } else { + gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0, + GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + GPIO_CFG_DISABLE); + } + + gpio_set_value_cansleep(GPIO_QRD3_LCD_EXT_2V85_EN, !!on); + gpio_set_value_cansleep(GPIO_QRD3_LCD_EXT_1V8_EN, !!on); + + if (on) { + rc = gpio_tlmm_config(qrd3_mipi_dsi_gpio[0], GPIO_CFG_ENABLE); + + if (rc < 0) { + pr_err("Failed to enable LCD Bridge reset enable\n"); + return rc; + } + + rc = gpio_direction_output(GPIO_QRD3_LCD_BRDG_RESET_N, 1); + + if (rc < 0) { + pr_err("Failed GPIO bridge Reset\n"); + gpio_free(GPIO_QRD3_LCD_BRDG_RESET_N); + return rc; + } + + msleep(20); + gpio_set_value_cansleep(GPIO_QRD3_LCD_BRDG_RESET_N, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_QRD3_LCD_BRDG_RESET_N, 1); + msleep(20); + } else { + gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BRDG_RESET_N, 0, + GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG_DISABLE); + } + + return rc; +} + +static int mipi_dsi_panel_power(int on) +{ + int rc = 0; + + if (machine_is_msm7627a_qrd1()) + rc = mipi_dsi_panel_qrd1_power(on); + else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() + || machine_is_msm8625_evt()) + rc = mipi_dsi_panel_qrd3_power(on); + else + rc = mipi_dsi_panel_msm_power(on); + return rc; +} + +#define MDP_303_VSYNC_GPIO 97 + +#ifdef CONFIG_FB_MSM_MIPI_DSI +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .vsync_gpio = MDP_303_VSYNC_GPIO, + .dsi_power_save = mipi_dsi_panel_power, + .dsi_client_reset = msm_fb_dsi_client_reset, + .get_lane_config = msm_fb_get_lane_config, +}; +#endif + +static char prim_panel_name[PANEL_NAME_MAX_LEN]; +static int __init prim_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(prim_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("prim_display", prim_display_setup); + +void msm7x27a_set_display_params(char *prim_panel) +{ + if (strnlen(prim_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.prim_panel_name, prim_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.prim_panel_name %s\n", + msm_fb_pdata.prim_panel_name); + } +} + +void __init msm_fb_add_devices(void) +{ + msm7x27a_set_display_params(prim_panel_name); + if (machine_is_msm7627a_qrd1()) + platform_add_devices(qrd_fb_devices, + ARRAY_SIZE(qrd_fb_devices)); + else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() + || machine_is_msm8625_evt()) { + mipi_NT35510_pdata.bl_lock = 1; + mipi_NT35516_pdata.bl_lock = 1; + platform_add_devices(evb_fb_devices, + ARRAY_SIZE(evb_fb_devices)); + } else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) { + sku3_lcdc_lcd_camera_power_init(); + platform_add_devices(qrd3_fb_devices, + ARRAY_SIZE(qrd3_fb_devices)); + } else + platform_add_devices(msm_fb_devices, + ARRAY_SIZE(msm_fb_devices)); + + msm_fb_register_device("mdp", &mdp_pdata); + if (machine_is_msm7625a_surf() || machine_is_msm7x27a_surf() || + machine_is_msm8625_surf() || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) + msm_fb_register_device("lcdc", &lcdc_pdata); +#ifdef CONFIG_FB_MSM_MIPI_DSI + msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata); +#endif +} diff --git a/arch/arm/mach-msm/board-msm7627a-io.c b/arch/arm/mach-msm/board-msm7627a-io.c new file mode 100644 index 00000000000..ec168f920bc --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a-io.c @@ -0,0 +1,891 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "board-msm7627a.h" +#include "devices-msm7x2xa.h" + +#define ATMEL_TS_I2C_NAME "maXTouch" +#define ATMEL_X_OFFSET 13 +#define ATMEL_Y_OFFSET 0 + +#if defined(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C) || \ +defined(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C_MODULE) + +#ifndef CLEARPAD3000_ATTEN_GPIO +#define CLEARPAD3000_ATTEN_GPIO (48) +#endif + +#ifndef CLEARPAD3000_RESET_GPIO +#define CLEARPAD3000_RESET_GPIO (26) +#endif + +#define KP_INDEX(row, col) ((row)*ARRAY_SIZE(kp_col_gpios) + (col)) + +static unsigned int kp_row_gpios[] = {31, 32, 33, 34, 35}; +static unsigned int kp_col_gpios[] = {36, 37, 38, 39, 40}; + +static const unsigned short keymap[ARRAY_SIZE(kp_col_gpios) * + ARRAY_SIZE(kp_row_gpios)] = { + [KP_INDEX(0, 0)] = KEY_7, + [KP_INDEX(0, 1)] = KEY_DOWN, + [KP_INDEX(0, 2)] = KEY_UP, + [KP_INDEX(0, 3)] = KEY_RIGHT, + [KP_INDEX(0, 4)] = KEY_ENTER, + + [KP_INDEX(1, 0)] = KEY_LEFT, + [KP_INDEX(1, 1)] = KEY_SEND, + [KP_INDEX(1, 2)] = KEY_1, + [KP_INDEX(1, 3)] = KEY_4, + [KP_INDEX(1, 4)] = KEY_CLEAR, + + [KP_INDEX(2, 0)] = KEY_6, + [KP_INDEX(2, 1)] = KEY_5, + [KP_INDEX(2, 2)] = KEY_8, + [KP_INDEX(2, 3)] = KEY_3, + [KP_INDEX(2, 4)] = KEY_NUMERIC_STAR, + + [KP_INDEX(3, 0)] = KEY_9, + [KP_INDEX(3, 1)] = KEY_NUMERIC_POUND, + [KP_INDEX(3, 2)] = KEY_0, + [KP_INDEX(3, 3)] = KEY_2, + [KP_INDEX(3, 4)] = KEY_SLEEP, + + [KP_INDEX(4, 0)] = KEY_BACK, + [KP_INDEX(4, 1)] = KEY_HOME, + [KP_INDEX(4, 2)] = KEY_MENU, + [KP_INDEX(4, 3)] = KEY_VOLUMEUP, + [KP_INDEX(4, 4)] = KEY_VOLUMEDOWN, +}; + +/* SURF keypad platform device information */ +static struct gpio_event_matrix_info kp_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = keymap, + .output_gpios = kp_row_gpios, + .input_gpios = kp_col_gpios, + .noutputs = ARRAY_SIZE(kp_row_gpios), + .ninputs = ARRAY_SIZE(kp_col_gpios), + .settle_time.tv64 = 40 * NSEC_PER_USEC, + .poll_time.tv64 = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS, +}; + +static struct gpio_event_info *kp_info[] = { + &kp_matrix_info.info +}; + +static struct gpio_event_platform_data kp_pdata = { + .name = "7x27a_kp", + .info = kp_info, + .info_count = ARRAY_SIZE(kp_info) +}; + +static struct platform_device kp_pdev = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &kp_pdata, + }, +}; + +/* 8625 keypad device information */ +static unsigned int kp_row_gpios_8625[] = {31}; +static unsigned int kp_col_gpios_8625[] = {36, 37}; + +static const unsigned short keymap_8625[] = { + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, +}; + +static const unsigned short keymap_8625_evt[] = { + KEY_VOLUMEDOWN, + KEY_VOLUMEUP, +}; + +static struct gpio_event_matrix_info kp_matrix_info_8625 = { + .info.func = gpio_event_matrix_func, + .keymap = keymap_8625, + .output_gpios = kp_row_gpios_8625, + .input_gpios = kp_col_gpios_8625, + .noutputs = ARRAY_SIZE(kp_row_gpios_8625), + .ninputs = ARRAY_SIZE(kp_col_gpios_8625), + .settle_time.tv64 = 40 * NSEC_PER_USEC, + .poll_time.tv64 = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS, +}; + +static struct gpio_event_info *kp_info_8625[] = { + &kp_matrix_info_8625.info, +}; + +static struct gpio_event_platform_data kp_pdata_8625 = { + .name = "7x27a_kp", + .info = kp_info_8625, + .info_count = ARRAY_SIZE(kp_info_8625) +}; + +static struct platform_device kp_pdev_8625 = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &kp_pdata_8625, + }, +}; + +#define LED_GPIO_PDM 96 +#define LED_RED_GPIO_8625 49 +#define LED_GREEN_GPIO_8625 34 + +static struct gpio_led gpio_leds_config_8625[] = { + { + .name = "green", + .gpio = LED_GREEN_GPIO_8625, + }, + { + .name = "red", + .gpio = LED_RED_GPIO_8625, + }, +}; + +static struct gpio_led_platform_data gpio_leds_pdata_8625 = { + .num_leds = ARRAY_SIZE(gpio_leds_config_8625), + .leds = gpio_leds_config_8625, +}; + +static struct platform_device gpio_leds_8625 = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_leds_pdata_8625, + }, +}; + +#define MXT_TS_IRQ_GPIO 48 +#define MXT_TS_RESET_GPIO 26 +#define MAX_VKEY_LEN 100 + +static ssize_t mxt_virtual_keys_register(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *virtual_keys = __stringify(EV_KEY) ":" __stringify(KEY_MENU) \ + ":60:840:120:80" ":" __stringify(EV_KEY) \ + ":" __stringify(KEY_HOME) ":180:840:120:80" \ + ":" __stringify(EV_KEY) ":" \ + __stringify(KEY_BACK) ":300:840:120:80" \ + ":" __stringify(EV_KEY) ":" \ + __stringify(KEY_SEARCH) ":420:840:120:80" "\n"; + + return snprintf(buf, strnlen(virtual_keys, MAX_VKEY_LEN) + 1 , "%s", + virtual_keys); +} + +static struct kobj_attribute mxt_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.atmel_mxt_ts", + .mode = S_IRUGO, + }, + .show = &mxt_virtual_keys_register, +}; + +static struct attribute *mxt_virtual_key_properties_attrs[] = { + &mxt_virtual_keys_attr.attr, + NULL, +}; + +static struct attribute_group mxt_virtual_key_properties_attr_group = { + .attrs = mxt_virtual_key_properties_attrs, +}; + +struct kobject *mxt_virtual_key_properties_kobj; + +static int mxt_vkey_setup(void) +{ + int retval; + + mxt_virtual_key_properties_kobj = + kobject_create_and_add("board_properties", NULL); + if (mxt_virtual_key_properties_kobj) + retval = sysfs_create_group(mxt_virtual_key_properties_kobj, + &mxt_virtual_key_properties_attr_group); + if (!mxt_virtual_key_properties_kobj || retval) + pr_err("failed to create mxt board_properties\n"); + + return retval; +} + +static const u8 mxt_config_data[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 16, 1, 0, 0, 0, 0, 0, 0, + /* T7 Object */ + 32, 16, 50, + /* T8 Object */ + 30, 0, 20, 20, 0, 0, 20, 0, 50, 0, + /* T9 Object */ + 3, 0, 0, 18, 11, 0, 32, 75, 3, 3, + 0, 1, 1, 0, 10, 10, 10, 10, 31, 3, + 223, 1, 11, 11, 15, 15, 151, 43, 145, 80, + 100, 15, 0, 0, 0, + /* T15 Object */ + 131, 0, 11, 11, 1, 1, 0, 45, 3, 0, + 0, + /* T18 Object */ + 0, 0, + /* T19 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + /* T23 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /* T25 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T40 Object */ + 0, 0, 0, 0, 0, + /* T42 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* T46 Object */ + 0, 2, 32, 48, 0, 0, 0, 0, 0, + /* T47 Object */ + 1, 20, 60, 5, 2, 50, 40, 0, 0, 40, + /* T48 Object */ + 1, 12, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 6, 0, 0, 100, 4, 64, + 10, 0, 20, 5, 0, 38, 0, 20, 0, 0, + 0, 0, 0, 0, 16, 65, 3, 1, 1, 0, + 10, 10, 10, 0, 0, 15, 15, 154, 58, 145, + 80, 100, 15, 3, +}; + +static const u8 mxt_config_data_evt[] = { + /* T6 Object */ + 0, 0, 0, 0, 0, 0, + /* T38 Object */ + 20, 0, 0, 0, 0, 0, 0, 0, + /* T7 Object */ + 24, 12, 10, + /* T8 Object */ + 30, 0, 20, 20, 0, 0, 9, 45, 10, 192, + /* T9 Object */ + 3, 0, 0, 18, 11, 0, 16, 60, 3, 1, + 0, 1, 1, 0, 10, 10, 10, 10, 107, 3, + 223, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 20, 15, 0, 0, 2, + /* T15 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, + /* T18 Object */ + 0, 0, + /* T19 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + /* T23 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /* T25 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + /* T40 Object */ + 17, 0, 0, 30, 30, + /* T42 Object */ + 3, 20, 45, 40, 128, 0, 0, 0, + /* T46 Object */ + 0, 2, 16, 16, 0, 0, 0, 0, 0, + /* T47 Object */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* T48 Object */ + 1, 128, 96, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 6, 0, 0, 63, 4, 64, + 10, 0, 32, 5, 0, 38, 0, 8, 0, 0, + 0, 0, 0, 0, 16, 65, 3, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +static struct mxt_config_info mxt_config_array[] = { + { + .config = mxt_config_data, + .config_length = ARRAY_SIZE(mxt_config_data), + .family_id = 0x81, + .variant_id = 0x01, + .version = 0x10, + .build = 0xAA, + }, +}; + +static int mxt_key_codes[MXT_KEYARRAY_MAX_KEYS] = { + [0] = KEY_HOME, + [1] = KEY_MENU, + [9] = KEY_BACK, + [10] = KEY_SEARCH, +}; + +static struct mxt_platform_data mxt_platform_data = { + .config_array = mxt_config_array, + .config_array_size = ARRAY_SIZE(mxt_config_array), + .panel_minx = 0, + .panel_maxx = 479, + .panel_miny = 0, + .panel_maxy = 799, + .disp_minx = 0, + .disp_maxx = 479, + .disp_miny = 0, + .disp_maxy = 799, + .irqflags = IRQF_TRIGGER_FALLING, + .i2c_pull_up = true, + .reset_gpio = MXT_TS_RESET_GPIO, + .irq_gpio = MXT_TS_IRQ_GPIO, + .key_codes = mxt_key_codes, +}; + +static struct i2c_board_info mxt_device_info[] __initdata = { + { + I2C_BOARD_INFO("atmel_mxt_ts", 0x4a), + .platform_data = &mxt_platform_data, + .irq = MSM_GPIO_TO_INT(MXT_TS_IRQ_GPIO), + }, +}; + +static int synaptics_touchpad_setup(void); + +static struct msm_gpio clearpad3000_cfg_data[] = { + {GPIO_CFG(CLEARPAD3000_ATTEN_GPIO, 0, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "rmi4_attn"}, + {GPIO_CFG(CLEARPAD3000_RESET_GPIO, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_8MA), "rmi4_reset"}, +}; + +static struct rmi_XY_pair rmi_offset = {.x = 0, .y = 0}; +static struct rmi_range rmi_clipx = {.min = 48, .max = 980}; +static struct rmi_range rmi_clipy = {.min = 7, .max = 1647}; +static struct rmi_f11_functiondata synaptics_f11_data = { + .swap_axes = false, + .flipX = false, + .flipY = false, + .offset = &rmi_offset, + .button_height = 113, + .clipX = &rmi_clipx, + .clipY = &rmi_clipy, +}; + +#define MAX_LEN 100 + +static ssize_t clearpad3000_virtual_keys_register(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *virtual_keys = __stringify(EV_KEY) ":" __stringify(KEY_MENU) \ + ":60:830:120:60" ":" __stringify(EV_KEY) \ + ":" __stringify(KEY_HOME) ":180:830:120:60" \ + ":" __stringify(EV_KEY) ":" \ + __stringify(KEY_SEARCH) ":300:830:120:60" \ + ":" __stringify(EV_KEY) ":" \ + __stringify(KEY_BACK) ":420:830:120:60" "\n"; + + return snprintf(buf, strnlen(virtual_keys, MAX_LEN) + 1 , "%s", + virtual_keys); +} + +static struct kobj_attribute clearpad3000_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.sensor00fn11", + .mode = S_IRUGO, + }, + .show = &clearpad3000_virtual_keys_register, +}; + +static struct attribute *virtual_key_properties_attrs[] = { + &clearpad3000_virtual_keys_attr.attr, + NULL +}; + +static struct attribute_group virtual_key_properties_attr_group = { + .attrs = virtual_key_properties_attrs, +}; + +struct kobject *virtual_key_properties_kobj; + +static struct rmi_functiondata synaptics_functiondata[] = { + { + .function_index = RMI_F11_INDEX, + .data = &synaptics_f11_data, + }, +}; + +static struct rmi_functiondata_list synaptics_perfunctiondata = { + .count = ARRAY_SIZE(synaptics_functiondata), + .functiondata = synaptics_functiondata, +}; + +static struct rmi_sensordata synaptics_sensordata = { + .perfunctiondata = &synaptics_perfunctiondata, + .rmi_sensor_setup = synaptics_touchpad_setup, +}; + +static struct rmi_i2c_platformdata synaptics_platformdata = { + .i2c_address = 0x2c, + .irq_type = IORESOURCE_IRQ_LOWLEVEL, + .sensordata = &synaptics_sensordata, +}; + +static struct i2c_board_info synaptic_i2c_clearpad3k[] = { + { + I2C_BOARD_INFO("rmi4_ts", 0x2c), + .platform_data = &synaptics_platformdata, + }, +}; + +static int synaptics_touchpad_setup(void) +{ + int retval = 0; + + virtual_key_properties_kobj = + kobject_create_and_add("board_properties", NULL); + if (virtual_key_properties_kobj) + retval = sysfs_create_group(virtual_key_properties_kobj, + &virtual_key_properties_attr_group); + if (!virtual_key_properties_kobj || retval) + pr_err("failed to create ft5202 board_properties\n"); + + retval = msm_gpios_request_enable(clearpad3000_cfg_data, + sizeof(clearpad3000_cfg_data)/sizeof(struct msm_gpio)); + if (retval) { + pr_err("%s:Failed to obtain touchpad GPIO %d. Code: %d.", + __func__, CLEARPAD3000_ATTEN_GPIO, retval); + retval = 0; /* ignore the err */ + } + synaptics_platformdata.irq = gpio_to_irq(CLEARPAD3000_ATTEN_GPIO); + + gpio_set_value(CLEARPAD3000_RESET_GPIO, 0); + usleep(10000); + gpio_set_value(CLEARPAD3000_RESET_GPIO, 1); + usleep(50000); + + return retval; +} +#endif + +static struct regulator_bulk_data regs_atmel[] = { + { .supply = "ldo12", .min_uV = 2700000, .max_uV = 3300000 }, + { .supply = "smps3", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +#define ATMEL_TS_GPIO_IRQ 82 + +static int atmel_ts_power_on(bool on) +{ + int rc = on ? + regulator_bulk_enable(ARRAY_SIZE(regs_atmel), regs_atmel) : + regulator_bulk_disable(ARRAY_SIZE(regs_atmel), regs_atmel); + + if (rc) + pr_err("%s: could not %sable regulators: %d\n", + __func__, on ? "en" : "dis", rc); + else + msleep(50); + + return rc; +} + +static int atmel_ts_platform_init(struct i2c_client *client) +{ + int rc; + struct device *dev = &client->dev; + + rc = regulator_bulk_get(dev, ARRAY_SIZE(regs_atmel), regs_atmel); + if (rc) { + dev_err(dev, "%s: could not get regulators: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_atmel), regs_atmel); + if (rc) { + dev_err(dev, "%s: could not set voltages: %d\n", + __func__, rc); + goto reg_free; + } + + rc = gpio_tlmm_config(GPIO_CFG(ATMEL_TS_GPIO_IRQ, 0, + GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) { + dev_err(dev, "%s: gpio_tlmm_config for %d failed\n", + __func__, ATMEL_TS_GPIO_IRQ); + goto reg_free; + } + + /* configure touchscreen interrupt gpio */ + rc = gpio_request(ATMEL_TS_GPIO_IRQ, "atmel_maxtouch_gpio"); + if (rc) { + dev_err(dev, "%s: unable to request gpio %d\n", + __func__, ATMEL_TS_GPIO_IRQ); + goto ts_gpio_tlmm_unconfig; + } + + rc = gpio_direction_input(ATMEL_TS_GPIO_IRQ); + if (rc < 0) { + dev_err(dev, "%s: unable to set the direction of gpio %d\n", + __func__, ATMEL_TS_GPIO_IRQ); + goto free_ts_gpio; + } + return 0; + +free_ts_gpio: + gpio_free(ATMEL_TS_GPIO_IRQ); +ts_gpio_tlmm_unconfig: + gpio_tlmm_config(GPIO_CFG(ATMEL_TS_GPIO_IRQ, 0, + GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_DISABLE); +reg_free: + regulator_bulk_free(ARRAY_SIZE(regs_atmel), regs_atmel); +out: + return rc; +} + +static int atmel_ts_platform_exit(struct i2c_client *client) +{ + gpio_free(ATMEL_TS_GPIO_IRQ); + gpio_tlmm_config(GPIO_CFG(ATMEL_TS_GPIO_IRQ, 0, + GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_DISABLE); + regulator_bulk_free(ARRAY_SIZE(regs_atmel), regs_atmel); + return 0; +} + +static u8 atmel_ts_read_chg(void) +{ + return gpio_get_value(ATMEL_TS_GPIO_IRQ); +} + +static u8 atmel_ts_valid_interrupt(void) +{ + return !atmel_ts_read_chg(); +} + + +static struct maxtouch_platform_data atmel_ts_pdata = { + .numtouch = 4, + .init_platform_hw = atmel_ts_platform_init, + .exit_platform_hw = atmel_ts_platform_exit, + .power_on = atmel_ts_power_on, + .display_res_x = 480, + .display_res_y = 864, + .min_x = ATMEL_X_OFFSET, + .max_x = (505 - ATMEL_X_OFFSET), + .min_y = ATMEL_Y_OFFSET, + .max_y = (863 - ATMEL_Y_OFFSET), + .valid_interrupt = atmel_ts_valid_interrupt, + .read_chg = atmel_ts_read_chg, +}; + +static struct i2c_board_info atmel_ts_i2c_info[] __initdata = { + { + I2C_BOARD_INFO(ATMEL_TS_I2C_NAME, 0x4a), + .platform_data = &atmel_ts_pdata, + .irq = MSM_GPIO_TO_INT(ATMEL_TS_GPIO_IRQ), + }, +}; + +static struct msm_handset_platform_data hs_platform_data = { + .hs_name = "7k_handset", + .pwr_key_delay_ms = 500, /* 0 will disable end key */ +}; + +static struct platform_device hs_pdev = { + .name = "msm-handset", + .id = -1, + .dev = { + .platform_data = &hs_platform_data, + }, +}; + +#define FT5X06_IRQ_GPIO 48 +#define FT5X06_RESET_GPIO 26 + +static ssize_t +ft5x06_virtual_keys_register(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return snprintf(buf, 200, + __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":40:510:80:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":120:510:80:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":200:510:80:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":280:510:80:60" + "\n"); +} + +static struct kobj_attribute ft5x06_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.ft5x06_ts", + .mode = S_IRUGO, + }, + .show = &ft5x06_virtual_keys_register, +}; + +static struct attribute *ft5x06_virtual_key_properties_attrs[] = { + &ft5x06_virtual_keys_attr.attr, + NULL, +}; + +static struct attribute_group ft5x06_virtual_key_properties_attr_group = { + .attrs = ft5x06_virtual_key_properties_attrs, +}; + +struct kobject *ft5x06_virtual_key_properties_kobj; + +static struct ft5x06_ts_platform_data ft5x06_platformdata = { + .x_max = 320, + .y_max = 480, + .reset_gpio = FT5X06_RESET_GPIO, + .irq_gpio = FT5X06_IRQ_GPIO, + .irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +}; + +static struct i2c_board_info ft5x06_device_info[] __initdata = { + { + I2C_BOARD_INFO("ft5x06_ts", 0x38), + .platform_data = &ft5x06_platformdata, + .irq = MSM_GPIO_TO_INT(FT5X06_IRQ_GPIO), + }, +}; + +static void __init ft5x06_touchpad_setup(void) +{ + int rc; + + rc = gpio_tlmm_config(GPIO_CFG(FT5X06_IRQ_GPIO, 0, + GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, FT5X06_IRQ_GPIO); + + rc = gpio_tlmm_config(GPIO_CFG(FT5X06_RESET_GPIO, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, FT5X06_RESET_GPIO); + + ft5x06_virtual_key_properties_kobj = + kobject_create_and_add("board_properties", NULL); + + if (ft5x06_virtual_key_properties_kobj) + rc = sysfs_create_group(ft5x06_virtual_key_properties_kobj, + &ft5x06_virtual_key_properties_attr_group); + + if (!ft5x06_virtual_key_properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", __func__); + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + ft5x06_device_info, + ARRAY_SIZE(ft5x06_device_info)); +} + +/* SKU3/SKU7 keypad device information */ +#define KP_INDEX_SKU3(row, col) ((row)*ARRAY_SIZE(kp_col_gpios_sku3) + (col)) +static unsigned int kp_row_gpios_sku3[] = {31, 32}; +static unsigned int kp_col_gpios_sku3[] = {36, 37}; + +static const unsigned short keymap_sku3[] = { + [KP_INDEX_SKU3(0, 0)] = KEY_VOLUMEUP, + [KP_INDEX_SKU3(0, 1)] = KEY_VOLUMEDOWN, + [KP_INDEX_SKU3(1, 1)] = KEY_CAMERA, +}; + +static struct gpio_event_matrix_info kp_matrix_info_sku3 = { + .info.func = gpio_event_matrix_func, + .keymap = keymap_sku3, + .output_gpios = kp_row_gpios_sku3, + .input_gpios = kp_col_gpios_sku3, + .noutputs = ARRAY_SIZE(kp_row_gpios_sku3), + .ninputs = ARRAY_SIZE(kp_col_gpios_sku3), + .settle_time.tv64 = 40 * NSEC_PER_USEC, + .poll_time.tv64 = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS, +}; + +static struct gpio_event_info *kp_info_sku3[] = { + &kp_matrix_info_sku3.info, +}; +static struct gpio_event_platform_data kp_pdata_sku3 = { + .name = "7x27a_kp", + .info = kp_info_sku3, + .info_count = ARRAY_SIZE(kp_info_sku3) +}; + +static struct platform_device kp_pdev_sku3 = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &kp_pdata_sku3, + }, +}; + +static struct led_info ctp_backlight_info = { + .name = "button-backlight", + .flags = PM_MPP__I_SINK__LEVEL_40mA << 16 | PM_MPP_7, +}; + +static struct led_platform_data ctp_backlight_pdata = { + .leds = &ctp_backlight_info, + .num_leds = 1, +}; + +static struct platform_device pmic_mpp_leds_pdev = { + .name = "pmic-mpp-leds", + .id = -1, + .dev = { + .platform_data = &ctp_backlight_pdata, + }, +}; + +void __init msm7627a_add_io_devices(void) +{ + /* touchscreen */ + if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa()) { + atmel_ts_pdata.min_x = 0; + atmel_ts_pdata.max_x = 480; + atmel_ts_pdata.min_y = 0; + atmel_ts_pdata.max_y = 320; + } + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + atmel_ts_i2c_info, + ARRAY_SIZE(atmel_ts_i2c_info)); + /* keypad */ + platform_device_register(&kp_pdev); + + /* headset */ + platform_device_register(&hs_pdev); + + /* LED: configure it as a pdm function */ + if (gpio_tlmm_config(GPIO_CFG(LED_GPIO_PDM, 3, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_8MA), GPIO_CFG_ENABLE)) + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, LED_GPIO_PDM); + else + platform_device_register(&led_pdev); + + /* Vibrator */ + if (machine_is_msm7x27a_ffa() || machine_is_msm7625a_ffa() + || machine_is_msm8625_ffa()) + msm_init_pmic_vibrator(); +} + +void __init qrd7627a_add_io_devices(void) +{ + int rc; + + /* touchscreen */ + if (machine_is_msm7627a_qrd1()) { + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + synaptic_i2c_clearpad3k, + ARRAY_SIZE(synaptic_i2c_clearpad3k)); + } else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() || + machine_is_msm8625_evt()) { + /* Use configuration data for EVT */ + if (machine_is_msm8625_evt()) { + mxt_config_array[0].config = mxt_config_data_evt; + mxt_config_array[0].config_length = + ARRAY_SIZE(mxt_config_data_evt); + mxt_platform_data.panel_maxy = 875; + mxt_vkey_setup(); + } + + rc = gpio_tlmm_config(GPIO_CFG(MXT_TS_IRQ_GPIO, 0, + GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, MXT_TS_IRQ_GPIO); + } + + rc = gpio_tlmm_config(GPIO_CFG(MXT_TS_RESET_GPIO, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_8MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, MXT_TS_RESET_GPIO); + } + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + mxt_device_info, + ARRAY_SIZE(mxt_device_info)); + } else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) { + ft5x06_touchpad_setup(); + } + + /* headset */ + platform_device_register(&hs_pdev); + + /* vibrator */ +#ifdef CONFIG_MSM_RPC_VIBRATOR + msm_init_pmic_vibrator(); +#endif + + /* keypad */ + if (machine_is_msm8625_evt()) + kp_matrix_info_8625.keymap = keymap_8625_evt; + + if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() || + machine_is_msm8625_evt()) + platform_device_register(&kp_pdev_8625); + else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) + platform_device_register(&kp_pdev_sku3); + + /* leds */ + if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()) { + rc = gpio_tlmm_config(GPIO_CFG(LED_RED_GPIO_8625, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_16MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, LED_RED_GPIO_8625); + } + + rc = gpio_tlmm_config(GPIO_CFG(LED_GREEN_GPIO_8625, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_16MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config for %d failed\n", + __func__, LED_GREEN_GPIO_8625); + } + + platform_device_register(&gpio_leds_8625); + platform_device_register(&pmic_mpp_leds_pdev); + } +} diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c new file mode 100644 index 00000000000..e2184f4c7e5 --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a-storage.c @@ -0,0 +1,412 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "pm.h" +#include "board-msm7627a.h" + +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC4_SUPPORT)) + +#define MAX_SDCC_CONTROLLER 4 +static unsigned long vreg_sts, gpio_sts; + +struct sdcc_gpio { + struct msm_gpio *cfg_data; + uint32_t size; + struct msm_gpio *sleep_cfg_data; +}; + +/** + * Due to insufficient drive strengths for SDC GPIO lines some old versioned + * SD/MMC cards may cause data CRC errors. Hence, set optimal values + * for SDC slots based on timing closure and marginality. SDC1 slot + * require higher value since it should handle bad signal quality due + * to size of T-flash adapters. + */ +static struct msm_gpio sdc1_cfg_data[] = { + {GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA), + "sdc1_dat_3"}, + {GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA), + "sdc1_dat_2"}, + {GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA), + "sdc1_dat_1"}, + {GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA), + "sdc1_dat_0"}, + {GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA), + "sdc1_cmd"}, + {GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_14MA), + "sdc1_clk"}, +}; + +static struct msm_gpio sdc2_cfg_data[] = { + {GPIO_CFG(62, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "sdc2_clk"}, + {GPIO_CFG(63, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc2_cmd"}, + {GPIO_CFG(64, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc2_dat_3"}, + {GPIO_CFG(65, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc2_dat_2"}, + {GPIO_CFG(66, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc2_dat_1"}, + {GPIO_CFG(67, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc2_dat_0"}, +}; + +static struct msm_gpio sdc2_sleep_cfg_data[] = { + {GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc2_clk"}, + {GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), + "sdc2_cmd"}, + {GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), + "sdc2_dat_3"}, + {GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), + "sdc2_dat_2"}, + {GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), + "sdc2_dat_1"}, + {GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), + "sdc2_dat_0"}, +}; +static struct msm_gpio sdc3_cfg_data[] = { + {GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "sdc3_clk"}, + {GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_cmd"}, + {GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_3"}, + {GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_2"}, + {GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_1"}, + {GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_0"}, +#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT + {GPIO_CFG(19, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_7"}, + {GPIO_CFG(20, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_6"}, + {GPIO_CFG(21, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_5"}, + {GPIO_CFG(108, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc3_dat_4"}, +#endif +}; + +static struct msm_gpio sdc4_cfg_data[] = { + {GPIO_CFG(19, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc4_dat_3"}, + {GPIO_CFG(20, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc4_dat_2"}, + {GPIO_CFG(21, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc4_dat_1"}, + {GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc4_cmd"}, + {GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA), + "sdc4_dat_0"}, + {GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "sdc4_clk"}, +}; + +static struct sdcc_gpio sdcc_cfg_data[] = { + { + .cfg_data = sdc1_cfg_data, + .size = ARRAY_SIZE(sdc1_cfg_data), + }, + { + .cfg_data = sdc2_cfg_data, + .size = ARRAY_SIZE(sdc2_cfg_data), + .sleep_cfg_data = sdc2_sleep_cfg_data, + }, + { + .cfg_data = sdc3_cfg_data, + .size = ARRAY_SIZE(sdc3_cfg_data), + }, + { + .cfg_data = sdc4_cfg_data, + .size = ARRAY_SIZE(sdc4_cfg_data), + }, +}; + +static int gpio_sdc1_hw_det = 85; +static void gpio_sdc1_config(void) +{ + if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() + || machine_is_msm8625_evb() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) + gpio_sdc1_hw_det = 42; +} + +static struct regulator *sdcc_vreg_data[MAX_SDCC_CONTROLLER]; +static int msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int rc = 0; + struct sdcc_gpio *curr; + + curr = &sdcc_cfg_data[dev_id - 1]; + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return rc; + + if (enable) { + set_bit(dev_id, &gpio_sts); + rc = msm_gpios_request_enable(curr->cfg_data, curr->size); + if (rc) + pr_err("%s: Failed to turn on GPIOs for slot %d\n", + __func__, dev_id); + } else { + clear_bit(dev_id, &gpio_sts); + if (curr->sleep_cfg_data) { + rc = msm_gpios_enable(curr->sleep_cfg_data, curr->size); + msm_gpios_free(curr->sleep_cfg_data, curr->size); + return rc; + } + msm_gpios_disable_free(curr->cfg_data, curr->size); + } + return rc; +} + +static int msm_sdcc_setup_vreg(int dev_id, unsigned int enable) +{ + int rc = 0; + struct regulator *curr = sdcc_vreg_data[dev_id - 1]; + + if (test_bit(dev_id, &vreg_sts) == enable) + return 0; + + if (!curr) + return -ENODEV; + + if (IS_ERR(curr)) + return PTR_ERR(curr); + + if (enable) { + set_bit(dev_id, &vreg_sts); + + rc = regulator_enable(curr); + if (rc) + pr_err("%s: could not enable regulator: %d\n", + __func__, rc); + } else { + clear_bit(dev_id, &vreg_sts); + + rc = regulator_disable(curr); + if (rc) + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + } + return rc; +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + + rc = msm_sdcc_setup_gpio(pdev->id, !!vdd); + if (rc) + goto out; + + rc = msm_sdcc_setup_vreg(pdev->id, !!vdd); +out: + return rc; +} + +#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) \ + && defined(CONFIG_MMC_MSM_CARD_HW_DETECTION) +static unsigned int msm7627a_sdcc_slot_status(struct device *dev) +{ + int status; + + status = gpio_tlmm_config(GPIO_CFG(gpio_sdc1_hw_det, 2, GPIO_CFG_INPUT, + GPIO_CFG_PULL_UP, GPIO_CFG_8MA), + GPIO_CFG_ENABLE); + if (status) + pr_err("%s:Failed to configure tlmm for GPIO %d\n", __func__, + gpio_sdc1_hw_det); + + status = gpio_request(gpio_sdc1_hw_det, "SD_HW_Detect"); + if (status) { + pr_err("%s:Failed to request GPIO %d\n", __func__, + gpio_sdc1_hw_det); + } else { + status = gpio_direction_input(gpio_sdc1_hw_det); + if (!status) { + if (machine_is_msm7627a_qrd1() || + machine_is_msm7627a_evb() || + machine_is_msm8625_evb() || + machine_is_msm7627a_qrd3() || + machine_is_msm8625_qrd7()) + status = !gpio_get_value(gpio_sdc1_hw_det); + else + status = gpio_get_value(gpio_sdc1_hw_det); + } + gpio_free(gpio_sdc1_hw_det); + } + return status; +} +#endif + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct mmc_platform_data sdc1_plat_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + .status = msm7627a_sdcc_slot_status, + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +#endif +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct mmc_platform_data sdc2_plat_data = { + /* + * SDC2 supports only 1.8V, claim for 2.85V range is just + * for allowing buggy cards who advertise 2.8V even though + * they can operate at 1.8V supply. + */ + .ocr_mask = MMC_VDD_28_29 | MMC_VDD_165_195, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sdiowakeup_irq = MSM_GPIO_TO_INT(66), + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, +#ifdef CONFIG_MMC_MSM_SDC2_DUMMY52_REQUIRED + .dummy52_required = 1, +#endif +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data sdc3_plat_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 1, +}; +#endif + +#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\ + && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT)) +static struct mmc_platform_data sdc4_plat_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, +}; +#endif + +static int __init mmc_regulator_init(int sdcc_no, const char *supply, int uV) +{ + int rc; + + BUG_ON(sdcc_no < 1 || sdcc_no > 4); + + sdcc_no--; + + sdcc_vreg_data[sdcc_no] = regulator_get(NULL, supply); + + if (IS_ERR(sdcc_vreg_data[sdcc_no])) { + rc = PTR_ERR(sdcc_vreg_data[sdcc_no]); + pr_err("%s: could not get regulator \"%s\": %d\n", + __func__, supply, rc); + goto out; + } + + rc = regulator_set_voltage(sdcc_vreg_data[sdcc_no], uV, uV); + + if (rc) { + pr_err("%s: could not set voltage for \"%s\" to %d uV: %d\n", + __func__, supply, uV, rc); + goto reg_free; + } + + return rc; + +reg_free: + regulator_put(sdcc_vreg_data[sdcc_no]); +out: + sdcc_vreg_data[sdcc_no] = NULL; + return rc; +} + +void __init msm7627a_init_mmc(void) +{ + /* eMMC slot */ +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + + /* There is no eMMC on SDC3 for QRD3 based devices */ + if (!(machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7())) { + if (mmc_regulator_init(3, "emmc", 3000000)) + return; + msm_add_sdcc(3, &sdc3_plat_data); + } +#endif + /* Micro-SD slot */ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + gpio_sdc1_config(); + if (mmc_regulator_init(1, "mmc", 2850000)) + return; +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + /* 8x25 EVT do not use hw detector */ + if (!(machine_is_msm8625_evt())) + sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det); + if (machine_is_msm8625_evt()) + sdc1_plat_data.status = NULL; +#endif + + msm_add_sdcc(1, &sdc1_plat_data); +#endif + /* SDIO WLAN slot */ +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + if (mmc_regulator_init(2, "smps3", 1800000)) + return; + msm_add_sdcc(2, &sdc2_plat_data); +#endif + /* Not Used */ +#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\ + && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT)) + /* There is no SDC4 for QRD3/7 based devices */ + if (!(machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7())) { + if (mmc_regulator_init(4, "smps3", 1800000)) + return; + msm_add_sdcc(4, &sdc4_plat_data); + } +#endif +} +#endif diff --git a/arch/arm/mach-msm/board-msm7627a-wlan.c b/arch/arm/mach-msm/board-msm7627a-wlan.c new file mode 100644 index 00000000000..07b7ab0385e --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a-wlan.c @@ -0,0 +1,385 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "board-msm7627a.h" +#include "devices-msm7x2xa.h" +#include "timer.h" + +#define GPIO_WLAN_3V3_EN 119 +static const char *id = "WLAN"; + +enum { + WLAN_VREG_S3 = 0, + WLAN_VREG_L17, + WLAN_VREG_L19 +}; + +struct wlan_vreg_info { + const char *vreg_id; + unsigned int level_min; + unsigned int level_max; + unsigned int pmapp_id; + unsigned int is_vreg_pin_controlled; + struct regulator *reg; +}; + +static struct wlan_vreg_info vreg_info[] = { + {"msme1", 1800000, 1800000, 2, 0, NULL}, + {"bt", 3300000, 3300000, 21, 1, NULL}, + {"wlan4", 1800000, 1800000, 23, 1, NULL} +}; + +int gpio_wlan_sys_rest_en = 134; +static void gpio_wlan_config(void) +{ + if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() + || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) + gpio_wlan_sys_rest_en = 124; +} + +static unsigned int qrf6285_init_regs(void) +{ + struct regulator_bulk_data regs[ARRAY_SIZE(vreg_info)]; + int i = 0, rc = 0; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + regs[i].supply = vreg_info[i].vreg_id; + regs[i].min_uV = vreg_info[i].level_min; + regs[i].max_uV = vreg_info[i].level_max; + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs); + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + goto out; + } + + for (i = 0; i < ARRAY_SIZE(regs); i++) + vreg_info[i].reg = regs[i].consumer; + +out: + return rc; +} + +static unsigned int setup_wlan_gpio(bool on) +{ + int rc = 0; + + if (on) { + rc = gpio_direction_output(gpio_wlan_sys_rest_en, 1); + msleep(100); + } else { + gpio_set_value_cansleep(gpio_wlan_sys_rest_en, 0); + rc = gpio_direction_input(gpio_wlan_sys_rest_en); + msleep(100); + } + + if (rc) + pr_err("%s: WLAN sys_reset_en GPIO: Error", __func__); + + return rc; +} + +static unsigned int setup_wlan_clock(bool on) +{ + int rc = 0; + + if (on) { + /* Vote for A0 clock */ + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0, + PMAPP_CLOCK_VOTE_ON); + } else { + /* Vote against A0 clock */ + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0, + PMAPP_CLOCK_VOTE_OFF); + } + + if (rc) + pr_err("%s: Configuring A0 clock for WLAN: Error", __func__); + + return rc; +} + +static unsigned int wlan_switch_regulators(int on) +{ + int rc = 0, index = 0; + + if (machine_is_msm7627a_qrd1()) + index = 2; + + for ( ; index < ARRAY_SIZE(vreg_info); index++) { + if (on) { + rc = regulator_set_voltage(vreg_info[index].reg, + vreg_info[index].level_min, + vreg_info[index].level_max); + if (rc) { + pr_err("%s:%s set voltage failed %d\n", + __func__, vreg_info[index].vreg_id, rc); + goto reg_disable; + } + + rc = regulator_enable(vreg_info[index].reg); + if (rc) { + pr_err("%s:%s vreg enable failed %d\n", + __func__, vreg_info[index].vreg_id, rc); + goto reg_disable; + } + + if (vreg_info[index].is_vreg_pin_controlled) { + rc = pmapp_vreg_lpm_pincntrl_vote(id, + vreg_info[index].pmapp_id, + PMAPP_CLOCK_ID_A0, 1); + if (rc) { + pr_err("%s:%s pincntrl failed %d\n", + __func__, + vreg_info[index].vreg_id, rc); + goto pin_cnt_fail; + } + } + } else { + if (vreg_info[index].is_vreg_pin_controlled) { + rc = pmapp_vreg_lpm_pincntrl_vote(id, + vreg_info[index].pmapp_id, + PMAPP_CLOCK_ID_A0, 0); + if (rc) { + pr_err("%s:%s pincntrl failed %d\n", + __func__, + vreg_info[index].vreg_id, rc); + goto pin_cnt_fail; + } + } + + rc = regulator_disable(vreg_info[index].reg); + if (rc) { + pr_err("%s:%s vreg disable failed %d\n", + __func__, + vreg_info[index].vreg_id, rc); + goto reg_disable; + } + } + } + return 0; +pin_cnt_fail: + if (on) + regulator_disable(vreg_info[index].reg); +reg_disable: + if (!machine_is_msm7627a_qrd1()) { + while (index) { + if (on) { + index--; + regulator_disable(vreg_info[index].reg); + regulator_put(vreg_info[index].reg); + } + } + } + return rc; +} + +static unsigned int msm_AR600X_setup_power(bool on) +{ + int rc = 0; + static bool init_done; + + if (unlikely(!init_done)) { + gpio_wlan_config(); + rc = qrf6285_init_regs(); + if (rc) { + pr_err("%s: qrf6285 init failed = %d\n", __func__, rc); + return rc; + } else { + init_done = true; + } + } + + rc = wlan_switch_regulators(on); + if (rc) { + pr_err("%s: wlan_switch_regulators error = %d\n", __func__, rc); + goto out; + } + + /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */ + if (machine_is_msm7627a_qrd1()) { + rc = gpio_tlmm_config(GPIO_CFG(GPIO_WLAN_3V3_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s gpio_tlmm_config 119 failed,error = %d\n", + __func__, rc); + goto reg_disable; + } + gpio_set_value(GPIO_WLAN_3V3_EN, 1); + } + + /* + * gpio_wlan_sys_rest_en is not from the GPIO expander for QRD7627a, + * EVB1.0 and QRD8625,so the below step is required for those devices. + */ + if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() + || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) { + rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s gpio_tlmm_config 119 failed,error = %d\n", + __func__, rc); + goto qrd_gpio_fail; + } + gpio_set_value(gpio_wlan_sys_rest_en, 1); + } else { + rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N"); + if (rc) { + pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n", + __func__, + gpio_wlan_sys_rest_en, rc); + goto qrd_gpio_fail; + } + rc = setup_wlan_gpio(on); + if (rc) { + pr_err("%s: wlan_set_gpio = %d\n", __func__, rc); + goto gpio_fail; + } + } + + /* Enable the A0 clock */ + rc = setup_wlan_clock(on); + if (rc) { + pr_err("%s: setup_wlan_clock = %d\n", __func__, rc); + goto set_gpio_fail; + } + + /* Configure A0 clock to be slave to WLAN_CLK_PWR_REQ */ + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0, + PMAPP_CLOCK_VOTE_PIN_CTRL); + if (rc) { + pr_err("%s: Configuring A0 to Pin controllable failed %d\n", + __func__, rc); + goto set_clock_fail; + } + + pr_info("WLAN power-up success\n"); + return 0; +set_clock_fail: + setup_wlan_clock(0); +set_gpio_fail: + setup_wlan_gpio(0); +gpio_fail: + gpio_free(gpio_wlan_sys_rest_en); +qrd_gpio_fail: + /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */ + if (machine_is_msm7627a_qrd1()) + gpio_free(GPIO_WLAN_3V3_EN); +reg_disable: + wlan_switch_regulators(0); +out: + pr_info("WLAN power-up failed\n"); + return rc; +} + +static unsigned int msm_AR600X_shutdown_power(bool on) +{ + int rc = 0; + + /* Disable the A0 clock */ + rc = setup_wlan_clock(on); + if (rc) { + pr_err("%s: setup_wlan_clock = %d\n", __func__, rc); + goto set_clock_fail; + } + + /* + * gpio_wlan_sys_rest_en is not from the GPIO expander for QRD7627a, + * EVB1.0 and QRD8625,so the below step is required for those devices. + */ + if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() + || machine_is_msm8625_evb() + || machine_is_msm8625_evt() + || machine_is_msm7627a_qrd3() + || machine_is_msm8625_qrd7()) { + rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s gpio_tlmm_config 119 failed,error = %d\n", + __func__, rc); + goto gpio_fail; + } + gpio_set_value(gpio_wlan_sys_rest_en, 0); + } else { + gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N"); + rc = setup_wlan_gpio(on); + if (rc) { + pr_err("%s: wlan_set_gpio = %d\n", __func__, rc); + goto set_gpio_fail; + } + gpio_free(gpio_wlan_sys_rest_en); + } + + /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */ + if (machine_is_msm7627a_qrd1()) { + rc = gpio_tlmm_config(GPIO_CFG(GPIO_WLAN_3V3_EN, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s gpio_tlmm_config 119 failed,error = %d\n", + __func__, rc); + goto qrd_gpio_fail; + } + gpio_set_value(GPIO_WLAN_3V3_EN, 0); + } + + rc = wlan_switch_regulators(on); + if (rc) { + pr_err("%s: wlan_switch_regulators error = %d\n", + __func__, rc); + goto reg_disable; + } + + pr_info("WLAN power-down success\n"); + return 0; +set_clock_fail: + setup_wlan_clock(0); +set_gpio_fail: + setup_wlan_gpio(0); +gpio_fail: + gpio_free(gpio_wlan_sys_rest_en); +qrd_gpio_fail: + /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */ + if (machine_is_msm7627a_qrd1()) + gpio_free(GPIO_WLAN_3V3_EN); +reg_disable: + wlan_switch_regulators(0); + pr_info("WLAN power-down failed\n"); + return rc; +} + +int ar600x_wlan_power(bool on) +{ + if (on) + msm_AR600X_setup_power(on); + else + msm_AR600X_shutdown_power(on); + + return 0; +} diff --git a/arch/arm/mach-msm/board-msm7627a.h b/arch/arm/mach-msm/board-msm7627a.h new file mode 100644 index 00000000000..4357e01fd9e --- /dev/null +++ b/arch/arm/mach-msm/board-msm7627a.h @@ -0,0 +1,110 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ARCH_ARM_MACH_MSM_BOARD_7627A__ +#define __ARCH_ARM_MACH_MSM_BOARD_7627A__ + +#include "pm.h" +void __init msm7627a_init_mmc(void); + +void __init msm_msm7627a_allocate_memory_regions(void); +void __init msm_fb_add_devices(void); + +enum { + GPIO_EXPANDER_IRQ_BASE = NR_MSM_IRQS + NR_GPIO_IRQS, + GPIO_EXPANDER_GPIO_BASE = NR_MSM_GPIOS, + /* SURF expander */ + GPIO_CORE_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE, + GPIO_BT_SYS_REST_EN = GPIO_CORE_EXPANDER_BASE, + GPIO_WLAN_EXT_POR_N, + GPIO_DISPLAY_PWR_EN, + GPIO_BACKLIGHT_EN, + GPIO_PRESSURE_XCLR, + GPIO_VREG_S3_EXP, + GPIO_UBM2M_PWRDWN, + GPIO_ETM_MODE_CS_N, + GPIO_HOST_VBUS_EN, + GPIO_SPI_MOSI, + GPIO_SPI_MISO, + GPIO_SPI_CLK, + GPIO_SPI_CS0_N, + GPIO_CORE_EXPANDER_IO13, + GPIO_CORE_EXPANDER_IO14, + GPIO_CORE_EXPANDER_IO15, + /* Camera expander */ + GPIO_CAM_EXPANDER_BASE = GPIO_CORE_EXPANDER_BASE + 16, + GPIO_CAM_GP_STROBE_READY = GPIO_CAM_EXPANDER_BASE, + GPIO_CAM_GP_AFBUSY, + GPIO_CAM_GP_CAM_PWDN, + GPIO_CAM_GP_CAM1MP_XCLR, + GPIO_CAM_GP_CAMIF_RESET_N, + GPIO_CAM_GP_STROBE_CE, + GPIO_CAM_GP_LED_EN1, + GPIO_CAM_GP_LED_EN2, +}; + +enum { + QRD_GPIO_HOST_VBUS_EN = 107, + QRD_GPIO_BT_SYS_REST_EN = 114, + QRD_GPIO_WAKE_ON_WIRELESS, + QRD_GPIO_BACKLIGHT_EN, + QRD_GPIO_NC, + QRD_GPIO_CAM_3MP_PWDN, /* CAM_VGA */ + QRD_GPIO_WLAN_EN, + QRD_GPIO_CAM_5MP_SHDN_EN, + QRD_GPIO_CAM_5MP_RESET, + QRD_GPIO_TP, + QRD_GPIO_CAM_GP_CAMIF_RESET, +}; + +#define ADSP_RPC_PROG 0x3000000a +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) + +#define FPGA_MSM_CNTRL_REG2 0x90008010 +#define BAHAMA_SLAVE_ID_FM_REG 0x02 +#define BAHAMA_SLAVE_ID_FM_ADDR 0x2A +#define BAHAMA_SLAVE_ID_QMEMBIST_ADDR 0x7B +#define FM_GPIO 83 +#define BT_PCM_BCLK_MODE 0x88 +#define BT_PCM_DIN_MODE 0x89 +#define BT_PCM_DOUT_MODE 0x8A +#define BT_PCM_SYNC_MODE 0x8B +#define FM_I2S_SD_MODE 0x8E +#define FM_I2S_WS_MODE 0x8F +#define FM_I2S_SCK_MODE 0x90 +#define I2C_PIN_CTL 0x15 +#define I2C_NORMAL 0x40 + +struct bahama_config_register { + u8 reg; + u8 value; + u8 mask; +}; + +struct bt_vreg_info { + const char *name; + unsigned int pmapp_id; + unsigned int min_level; + unsigned int max_level; + unsigned int is_pin_controlled; + struct regulator *reg; +}; + +void __init msm7627a_bt_power_init(void); +#endif + +void __init msm7627a_camera_init(void); +int lcd_camera_power_onoff(int on); + +void __init msm7627a_add_io_devices(void); +void __init qrd7627a_add_io_devices(void); +#endif diff --git a/arch/arm/mach-msm/board-msm7x27.c b/arch/arm/mach-msm/board-msm7x27.c index 6d84ee740df..d42458f2539 100644 --- a/arch/arm/mach-msm/board-msm7x27.c +++ b/arch/arm/mach-msm/board-msm7x27.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -13,15 +13,17 @@ * GNU General Public License for more details. * */ -#include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -32,17 +34,64 @@ #include #endif +#include #include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#ifdef CONFIG_USB_G_ANDROID +#include +#include +#endif + +#include "board-msm7627-regulator.h" #include "devices.h" -#include "socinfo.h" #include "clock.h" +#include "acpuclock.h" +#include "msm-keypad-devices.h" +#include "pm.h" +#include "pm-boot.h" + +#ifdef CONFIG_ARCH_MSM7X25 +#define MSM_PMEM_MDP_SIZE 0xb21000 +#define MSM_PMEM_ADSP_SIZE 0x97b000 +#define MSM_PMEM_AUDIO_SIZE 0x121000 +#define MSM_FB_SIZE 0x200000 +#define PMEM_KERNEL_EBI1_SIZE 0x64000 +#endif + +#ifdef CONFIG_ARCH_MSM7X27 +#define MSM_PMEM_MDP_SIZE 0x1B76000 +#define MSM_PMEM_ADSP_SIZE 0xC8A000 +#define MSM_PMEM_AUDIO_SIZE 0x5B000 + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_SIZE 0x233000 +#else +#define MSM_FB_SIZE 0x177000 +#endif + +#define PMEM_KERNEL_EBI1_SIZE 0x1C000 +#endif +#define ADSP_RPC_PROG 0x3000000a static struct resource smc91x_resources[] = { [0] = { @@ -64,14 +113,1226 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; +#ifdef CONFIG_USB_G_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + if (on) + msm_hsusb_vbus_powerup(); + else + msm_hsusb_vbus_shutdown(); +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_65NM), +}; + +static void __init msm7x2x_init_host(void) +{ + if (machine_is_msm7x25_ffa() || machine_is_msm7x27_ffa()) + return; + + msm_add_host(0, &msm_usb_host_pdata); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int msm_hsusb_ldo_init(int init) +{ + static struct regulator *reg_hsusb; + int rc; + if (init) { + reg_hsusb = regulator_get(NULL, "usb"); + if (IS_ERR(reg_hsusb)) { + rc = PTR_ERR(reg_hsusb); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_hsusb, 3300000, 3300000); + if (rc < 0) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto usb_reg_fail; + } + + rc = regulator_enable(reg_hsusb); + if (rc < 0) { + pr_err("%s: could not enable regulator: %d\n", + __func__, rc); + goto usb_reg_fail; + } + + /* + * PHY 3.3V analog domain(VDDA33) is powered up by + * an always enabled power supply (LP5900TL-3.3). + * USB VREG default source is VBUS line. Turning + * on USB VREG has a side effect on the USB suspend + * current. Hence USB VREG is explicitly turned + * off here. + */ + + rc = regulator_disable(reg_hsusb); + if (rc < 0) { + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + goto usb_reg_fail; + } + + regulator_put(reg_hsusb); + } + + return 0; +usb_reg_fail: + regulator_put(reg_hsusb); +out: + return rc; +} + +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret; + + if (init) { + ret = msm_pm_app_rpc_init(callback); + } else { + msm_pm_app_rpc_deinit(callback); + ret = 0; + } + return ret; +} + +static int msm_otg_rpc_phy_reset(void __iomem *regs) +{ + return msm_hsusb_phy_reset(); +} + +static struct msm_otg_platform_data msm_otg_pdata = { + .rpc_connect = hsusb_rpc_connect, + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, + .chg_vbus_draw = hsusb_chg_vbus_draw, + .chg_connected = hsusb_chg_connected, + .chg_init = hsusb_chg_init, +#ifdef CONFIG_USB_EHCI_MSM_72K + .vbus_power = msm_hsusb_vbus_power, +#endif + .ldo_init = msm_hsusb_ldo_init, + .pclk_required_during_lpm = 1, +}; + +#ifdef CONFIG_USB_GADGET +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata; +#endif +#endif + +#define SND(desc, num) { .name = #desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(HANDSET, 0), + SND(MONO_HEADSET, 2), + SND(HEADSET, 3), + SND(SPEAKER, 6), + SND(TTY_HEADSET, 8), + SND(TTY_VCO, 9), + SND(TTY_HCO, 10), + SND(BT, 12), + SND(IN_S_SADC_OUT_HANDSET, 16), + SND(IN_S_SADC_OUT_SPEAKER_PHONE, 25), + SND(CURRENT, 27), +}; +#undef SND + +static struct msm_snd_endpoints msm_device_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = sizeof(snd_endpoints_list) / sizeof(struct snd_endpoint) +}; + +static struct platform_device msm_device_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &msm_device_snd_endpoints + }, +}; + +#define DEC0_FORMAT ((1< 0; i--) + regulator_put(vreg[i - 1]); + return rc; + +vreg_lcdc_fail: + if (on) { + for (; i > 0; i--) + regulator_disable(vreg[i - 1]); + } else { + for (; i > 0; i--) + regulator_enable(vreg[i - 1]); + } + + return rc; +} + +static struct lcdc_platform_data lcdc_pdata = { + .lcdc_gpio_config = msm_fb_lcdc_config, + .lcdc_power_save = msm_fb_lcdc_power_save, +}; + +static struct msm_panel_common_pdata lcdc_gordon_panel_data = { + .panel_config_gpio = lcdc_gordon_config_gpios, + .gpio_num = gpio_array_num, +}; + +static struct platform_device lcdc_gordon_panel_device = { + .name = "lcdc_gordon_vga", + .id = 0, + .dev = { + .platform_data = &lcdc_gordon_panel_data, + } +}; + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +static int msm_fb_detect_panel(const char *name) +{ + int ret = -EPERM; + + if (machine_is_msm7x25_ffa() || machine_is_msm7x27_ffa()) { + if (!strcmp(name, "lcdc_gordon_vga")) + ret = 0; + else + ret = -ENODEV; + } + + return ret; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, + .mddi_prescan = 1, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev = { + .platform_data = &msm_fb_pdata, + } +}; + +#ifdef CONFIG_BT +static struct platform_device msm_bt_power_device = { + .name = "bt_power", +}; + +enum { + BT_WAKE, + BT_RFR, + BT_CTS, + BT_RX, + BT_TX, + BT_PCM_DOUT, + BT_PCM_DIN, + BT_PCM_SYNC, + BT_PCM_CLK, + BT_HOST_WAKE, +}; + +static unsigned bt_config_power_on[] = { + GPIO_CFG(42, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* WAKE */ + GPIO_CFG(43, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* RFR */ + GPIO_CFG(44, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* CTS */ + GPIO_CFG(45, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* Rx */ + GPIO_CFG(46, 3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* Tx */ + GPIO_CFG(68, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_DOUT */ + GPIO_CFG(69, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_DIN */ + GPIO_CFG(70, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_SYNC */ + GPIO_CFG(71, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_CLK */ + GPIO_CFG(83, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* HOST_WAKE */ +}; +static unsigned bt_config_power_off[] = { + GPIO_CFG(42, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* WAKE */ + GPIO_CFG(43, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* RFR */ + GPIO_CFG(44, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* CTS */ + GPIO_CFG(45, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* Rx */ + GPIO_CFG(46, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* Tx */ + GPIO_CFG(68, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCM_DOUT */ + GPIO_CFG(69, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCM_DIN */ + GPIO_CFG(70, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCM_SYNC */ + GPIO_CFG(71, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCM_CLK */ + GPIO_CFG(83, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* HOST_WAKE */ +}; + +static int bluetooth_power(int on) +{ + int pin, rc; + static struct regulator *vreg_bt; + + printk(KERN_DEBUG "%s\n", __func__); + + /* do not have vreg bt defined, gp6 is the same */ + /* vreg_get parameter 1 (struct device *) is ignored */ + + if (on) { + for (pin = 0; pin < ARRAY_SIZE(bt_config_power_on); pin++) { + rc = gpio_tlmm_config(bt_config_power_on[pin], + GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, bt_config_power_on[pin], rc); + return -EIO; + } + } + vreg_bt = regulator_get(NULL, "gp6"); + + if (IS_ERR(vreg_bt)) { + rc = PTR_ERR(vreg_bt); + pr_err("%s: could get not regulator: %d\n", + __func__, rc); + goto out; + } + + /* units of mV, steps of 50 mV */ + rc = regulator_set_voltage(vreg_bt, 2600000, 2600000); + if (rc < 0) { + pr_err("%s: could not set voltage: %d\n", __func__, rc); + goto bt_vreg_fail; + } + rc = regulator_enable(vreg_bt); + if (rc < 0) { + pr_err("%s: could not enable regulator: %d\n", + __func__, rc); + goto bt_vreg_fail; + } + } else { + rc = regulator_disable(vreg_bt); + if (rc < 0) { + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + goto bt_vreg_fail; + } + regulator_put(vreg_bt); + for (pin = 0; pin < ARRAY_SIZE(bt_config_power_off); pin++) { + rc = gpio_tlmm_config(bt_config_power_off[pin], + GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, bt_config_power_off[pin], rc); + return -EIO; + } + } + } + return 0; + +bt_vreg_fail: + regulator_put(vreg_bt); +out: + return rc; +} + +static void __init bt_power_init(void) +{ + msm_bt_power_device.dev.platform_data = &bluetooth_power; +} +#else +#define bt_power_init(x) do {} while (0) +#endif + +static struct platform_device msm_device_pmic_leds = { + .name = "pmic-leds", + .id = -1, +}; + +static struct resource bluesleep_resources[] = { + { + .name = "gpio_host_wake", + .start = 83, + .end = 83, + .flags = IORESOURCE_IO, + }, + { + .name = "gpio_ext_wake", + .start = 42, + .end = 42, + .flags = IORESOURCE_IO, + }, + { + .name = "host_wake", + .start = MSM_GPIO_TO_INT(83), + .end = MSM_GPIO_TO_INT(83), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_bluesleep_device = { + .name = "bluesleep", + .id = -1, + .num_resources = ARRAY_SIZE(bluesleep_resources), + .resource = bluesleep_resources, +}; + +static struct i2c_board_info i2c_devices[] = { +#ifdef CONFIG_MT9D112 + { + I2C_BOARD_INFO("mt9d112", 0x78 >> 1), + }, +#endif +#ifdef CONFIG_S5K3E2FX + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, +#endif +#ifdef CONFIG_MT9P012 + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, +#endif +#ifdef CONFIG_MT9P012_KM + { + I2C_BOARD_INFO("mt9p012_km", 0x6C >> 2), + }, +#endif +#if defined(CONFIG_MT9T013) || defined(CONFIG_SENSORS_MT9T013) + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +#endif +#ifdef CONFIG_VB6801 + { + I2C_BOARD_INFO("vb6801", 0x20), + }, +#endif +}; + +#ifdef CONFIG_MSM_CAMERA +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT0 */ + GPIO_CFG(1, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT1 */ + GPIO_CFG(2, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT2 */ + GPIO_CFG(3, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT3 */ + GPIO_CFG(4, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT4 */ + GPIO_CFG(5, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT5 */ + GPIO_CFG(6, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT6 */ + GPIO_CFG(7, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT7 */ + GPIO_CFG(8, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT8 */ + GPIO_CFG(9, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT9 */ + GPIO_CFG(10, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT10 */ + GPIO_CFG(11, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT11 */ + GPIO_CFG(12, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCLK */ + GPIO_CFG(13, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT0 */ + GPIO_CFG(1, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT1 */ + GPIO_CFG(2, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT2 */ + GPIO_CFG(3, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT3 */ + GPIO_CFG(4, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT4 */ + GPIO_CFG(5, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT5 */ + GPIO_CFG(6, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT6 */ + GPIO_CFG(7, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT7 */ + GPIO_CFG(8, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT8 */ + GPIO_CFG(9, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT9 */ + GPIO_CFG(10, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT10 */ + GPIO_CFG(11, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT11 */ + GPIO_CFG(12, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_16MA), /* PCLK */ + GPIO_CFG(13, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_16MA), /* MCLK */ + }; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} + +static void msm_camera_vreg_config(int vreg_en) +{ + int rc; + static struct regulator *vreg_gp2; + static struct regulator *vreg_gp3; + + if (vreg_gp2 == NULL && vreg_gp3 == NULL) { + vreg_gp2 = regulator_get(NULL, "gp2"); + if (IS_ERR(vreg_gp2)) { + rc = PTR_ERR(vreg_gp2); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + return; + } + + rc = regulator_set_voltage(vreg_gp2, 1800000, 1800000); + if (rc < 0) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto cam_vreg_fail; + } + + vreg_gp3 = regulator_get(NULL, "gp3"); + if (IS_ERR(vreg_gp3)) { + rc = PTR_ERR(vreg_gp3); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto cam_vreg_fail; + } + + rc = regulator_set_voltage(vreg_gp3, 2850000, 2850000); + if (rc < 0) { + pr_err("%s: could not set voltage: %d\n", __func__, rc); + goto cam_vreg2_fail; + } + + return; + + } + + if (vreg_gp2 == NULL || vreg_gp3 == NULL) { + pr_err("Camera Regulators are not initialized\n"); + return; + } + + if (vreg_en) { + rc = regulator_enable(vreg_gp2); + if (rc) { + pr_err("%s: could not enable regulator: %d\n", + __func__, rc); + goto cam_vreg2_fail; + } + + rc = regulator_enable(vreg_gp3); + if (rc) { + pr_err("%s: could not enable regulator: %d\n", + __func__, rc); + goto vreg_gp3_fail; + } + } else { + rc = regulator_disable(vreg_gp2); + if (rc) { + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + return; + } + + rc = regulator_disable(vreg_gp3); + if (rc) { + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + goto cam_vreg2_fail; + } + } + + return; + +vreg_gp3_fail: + if (vreg_en) + regulator_disable(vreg_gp2); + +cam_vreg2_fail: + regulator_put(vreg_gp3); +cam_vreg_fail: + regulator_put(vreg_gp2); + vreg_gp3 = NULL; + vreg_gp2 = NULL; +} + +static int config_camera_on_gpios(void) +{ + int vreg_en = 1; + + if (machine_is_msm7x25_ffa() || + machine_is_msm7x27_ffa()) + msm_camera_vreg_config(vreg_en); + + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + return 0; +} + +static void config_camera_off_gpios(void) +{ + int vreg_en = 0; + + if (machine_is_msm7x25_ffa() || + machine_is_msm7x27_ffa()) + msm_camera_vreg_config(vreg_en); + + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM7XXX_MDC_PHYS, + .ioext.mdcsz = MSM7XXX_MDC_SIZE, + .ioext.appphy = MSM7XXX_CLK_CTL_PHYS, + .ioext.appsz = MSM7XXX_CLK_CTL_SIZE, +}; + +int pmic_set_flash_led_current(enum pmic8058_leds id, unsigned mA) +{ + int rc; + rc = pmic_flash_led_set_current(mA); + return rc; +} + +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_PMIC, + ._fsrc.pmic_src.num_of_src = 1, + ._fsrc.pmic_src.low_current = 30, + ._fsrc.pmic_src.high_current = 100, + ._fsrc.pmic_src.led_src_1 = 0, + ._fsrc.pmic_src.led_src_2 = 0, + ._fsrc.pmic_src.pmic_set_current = pmic_set_flash_led_current, +}; + +#ifdef CONFIG_MT9D112 +static struct msm_camera_sensor_flash_data flash_mt9d112 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = { + .sensor_name = "mt9d112", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .flash_data = &flash_mt9d112 +}; + +static struct platform_device msm_camera_sensor_mt9d112 = { + .name = "msm_camera_mt9d112", + .dev = { + .platform_data = &msm_camera_sensor_mt9d112_data, + }, +}; +#endif + +#ifdef CONFIG_S5K3E2FX +static struct msm_camera_sensor_flash_data flash_s5k3e2fx = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { + .sensor_name = "s5k3e2fx", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .flash_data = &flash_s5k3e2fx +}; + +static struct platform_device msm_camera_sensor_s5k3e2fx = { + .name = "msm_camera_s5k3e2fx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3e2fx_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012 +static struct msm_camera_sensor_flash_data flash_mt9p012 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { + .sensor_name = "mt9p012", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 88, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .flash_data = &flash_mt9p012 +}; + +static struct platform_device msm_camera_sensor_mt9p012 = { + .name = "msm_camera_mt9p012", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012_KM +static struct msm_camera_sensor_flash_data flash_mt9p012_km = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_km_data = { + .sensor_name = "mt9p012_km", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 88, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .flash_data = &flash_mt9p012_km +}; + +static struct platform_device msm_camera_sensor_mt9p012_km = { + .name = "msm_camera_mt9p012_km", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_km_data, + }, +}; +#endif + +#ifdef CONFIG_MT9T013 +static struct msm_camera_sensor_flash_data flash_mt9t013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { + .sensor_name = "mt9t013", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .flash_data = &flash_mt9t013 +}; + +static struct platform_device msm_camera_sensor_mt9t013 = { + .name = "msm_camera_mt9t013", + .dev = { + .platform_data = &msm_camera_sensor_mt9t013_data, + }, +}; +#endif + +#ifdef CONFIG_VB6801 +static struct msm_camera_sensor_flash_data flash_vb6801 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_vb6801_data = { + .sensor_name = "vb6801", + .sensor_reset = 89, + .sensor_pwd = 88, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .flash_data = &flash_vb6801 +}; + +static struct platform_device msm_camera_sensor_vb6801 = { + .name = "msm_camera_vb6801", + .dev = { + .platform_data = &msm_camera_sensor_vb6801_data, + }, +}; +#endif +#endif + +static u32 msm_calculate_batt_capacity(u32 current_voltage); + +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .voltage_min_design = 2800, + .voltage_max_design = 4300, + .avail_chg_sources = AC_CHG | USB_CHG , + .batt_technology = POWER_SUPPLY_TECHNOLOGY_LION, + .calculate_capacity = &msm_calculate_batt_capacity, +}; + +static u32 msm_calculate_batt_capacity(u32 current_voltage) +{ + u32 low_voltage = msm_psy_batt_data.voltage_min_design; + u32 high_voltage = msm_psy_batt_data.voltage_max_design; + + return (current_voltage - low_voltage) * 100 + / (high_voltage - low_voltage); +} + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; + + static struct platform_device *devices[] __initdata = { - &msm_device_uart3, + &asoc_msm_pcm, + &asoc_msm_dai0, + &asoc_msm_dai1, + &msm_device_smd, &msm_device_dmov, &msm_device_nand, + +#ifdef CONFIG_USB_MSM_OTG_72K + &msm_device_otg, +#ifdef CONFIG_USB_GADGET + &msm_device_gadget_peripheral, +#endif +#endif + +#ifdef CONFIG_USB_G_ANDROID + &android_usb_device, +#endif + + &msm_device_i2c, &smc91x_device, + &msm_device_tssc, + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_audio_device, + &msm_fb_device, + &lcdc_gordon_panel_device, + &msm_device_uart_dm1, +#ifdef CONFIG_BT + &msm_bt_power_device, +#endif + &msm_device_pmic_leds, + &msm_device_snd, + &msm_device_adspdec, +#ifdef CONFIG_MT9T013 + &msm_camera_sensor_mt9t013, +#endif +#ifdef CONFIG_MT9D112 + &msm_camera_sensor_mt9d112, +#endif +#ifdef CONFIG_S5K3E2FX + &msm_camera_sensor_s5k3e2fx, +#endif +#ifdef CONFIG_MT9P012 + &msm_camera_sensor_mt9p012, +#endif +#ifdef CONFIG_MT9P012_KM + &msm_camera_sensor_mt9p012_km, +#endif +#ifdef CONFIG_VB6801 + &msm_camera_sensor_vb6801, +#endif + &msm_bluesleep_device, +#ifdef CONFIG_ARCH_MSM7X27 + &msm_kgsl_3d0, +#endif +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + &msm_device_tsif, +#endif + &hs_device, + &msm_batt_device, }; +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = 97, + .mdp_rev = MDP_REV_30, +}; + +static void __init msm_fb_add_devices(void) +{ + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("pmdh", 0); + msm_fb_register_device("lcdc", &lcdc_pdata); +} + extern struct sys_timer msm_timer; static void __init msm7x2x_init_irq(void) @@ -79,51 +1340,646 @@ static void __init msm7x2x_init_irq(void) msm_init_irq(); } +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC4_SUPPORT)) + +static unsigned long vreg_sts, gpio_sts; +static struct regulator *vreg_mmc; +static unsigned mpp_mmc = 2; + +struct sdcc_gpio { + struct msm_gpio *cfg_data; + uint32_t size; + struct msm_gpio *sleep_cfg_data; +}; + +static struct msm_gpio sdc1_cfg_data[] = { + {GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_3"}, + {GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_2"}, + {GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_1"}, + {GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_0"}, + {GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_cmd"}, + {GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc1_clk"}, +}; + +static struct msm_gpio sdc2_cfg_data[] = { + {GPIO_CFG(62, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc2_clk"}, + {GPIO_CFG(63, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_cmd"}, + {GPIO_CFG(64, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_3"}, + {GPIO_CFG(65, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_2"}, + {GPIO_CFG(66, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_1"}, + {GPIO_CFG(67, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_0"}, +}; + +static struct msm_gpio sdc2_sleep_cfg_data[] = { + {GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "sdc2_clk"}, + {GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "sdc2_cmd"}, + {GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "sdc2_dat_3"}, + {GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "sdc2_dat_2"}, + {GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "sdc2_dat_1"}, + {GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "sdc2_dat_0"}, +}; +static struct msm_gpio sdc3_cfg_data[] = { + {GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc3_clk"}, + {GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_cmd"}, + {GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_3"}, + {GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_2"}, + {GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_1"}, + {GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_0"}, +}; + +static struct msm_gpio sdc4_cfg_data[] = { + {GPIO_CFG(19, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_3"}, + {GPIO_CFG(20, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_2"}, + {GPIO_CFG(21, 4, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_1"}, + {GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_cmd"}, + {GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_0"}, + {GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc4_clk"}, +}; + +static struct sdcc_gpio sdcc_cfg_data[] = { + { + .cfg_data = sdc1_cfg_data, + .size = ARRAY_SIZE(sdc1_cfg_data), + .sleep_cfg_data = NULL, + }, + { + .cfg_data = sdc2_cfg_data, + .size = ARRAY_SIZE(sdc2_cfg_data), + .sleep_cfg_data = sdc2_sleep_cfg_data, + }, + { + .cfg_data = sdc3_cfg_data, + .size = ARRAY_SIZE(sdc3_cfg_data), + .sleep_cfg_data = NULL, + }, + { + .cfg_data = sdc4_cfg_data, + .size = ARRAY_SIZE(sdc4_cfg_data), + .sleep_cfg_data = NULL, + }, +}; + +static void msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int rc = 0; + struct sdcc_gpio *curr; + + curr = &sdcc_cfg_data[dev_id - 1]; + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return; + + if (enable) { + set_bit(dev_id, &gpio_sts); + rc = msm_gpios_request_enable(curr->cfg_data, curr->size); + if (rc) + printk(KERN_ERR "%s: Failed to turn on GPIOs for slot %d\n", + __func__, dev_id); + } else { + clear_bit(dev_id, &gpio_sts); + if (curr->sleep_cfg_data) { + msm_gpios_enable(curr->sleep_cfg_data, curr->size); + msm_gpios_free(curr->sleep_cfg_data, curr->size); + return; + } + msm_gpios_disable_free(curr->cfg_data, curr->size); + } +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + msm_sdcc_setup_gpio(pdev->id, !!vdd); + + if (vdd == 0) { + if (!vreg_sts) + return 0; + + clear_bit(pdev->id, &vreg_sts); + + if (!vreg_sts) { + if (machine_is_msm7x25_ffa() || + machine_is_msm7x27_ffa()) { + rc = mpp_config_digital_out(mpp_mmc, + MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_LOW)); + } else + rc = regulator_disable(vreg_mmc); + if (rc) { + pr_err("%s: return val: %d\n", + __func__, rc); + } + } + return 0; + } + + if (!vreg_sts) { + if (machine_is_msm7x25_ffa() || machine_is_msm7x27_ffa()) { + rc = mpp_config_digital_out(mpp_mmc, + MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_HIGH)); + } else { + rc = regulator_set_voltage(vreg_mmc, 2850000, 2850000); + if (!rc) + rc = regulator_enable(vreg_mmc); + } + if (rc) { + pr_err("%s: return val: %d\n", + __func__, rc); + } + } + set_bit(pdev->id, &vreg_sts); + return 0; +} + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct mmc_platform_data msm7x2x_sdc1_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct mmc_platform_data msm7x2x_sdc2_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sdiowakeup_irq = MSM_GPIO_TO_INT(66), + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data msm7x2x_sdc3_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct mmc_platform_data msm7x2x_sdc4_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +static void __init msm7x2x_init_mmc(void) +{ + if (!machine_is_msm7x25_ffa() && !machine_is_msm7x27_ffa()) { + vreg_mmc = regulator_get(NULL, "mmc"); + if (IS_ERR(vreg_mmc)) { + pr_err("%s: could not get regulator: %ld\n", + __func__, PTR_ERR(vreg_mmc)); + } + } + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + msm_add_sdcc(1, &msm7x2x_sdc1_data); +#endif + + if (machine_is_msm7x25_surf() || machine_is_msm7x27_surf() || + machine_is_msm7x27_ffa()) { +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + msm_sdcc_setup_gpio(2, 1); + msm_add_sdcc(2, &msm7x2x_sdc2_data); +#endif + } + + if (machine_is_msm7x25_surf() || machine_is_msm7x27_surf()) { +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + msm_add_sdcc(3, &msm7x2x_sdc3_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + msm_add_sdcc(4, &msm7x2x_sdc4_data); +#endif + } +} +#else +#define msm7x2x_init_mmc() do {} while (0) +#endif + + +static struct msm_pm_platform_data msm7x25_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)].latency = 16000, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] + .latency = 12000, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT)] + .latency = 2000, +}; + +static struct msm_pm_platform_data msm7x27_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 16000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 12000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2000, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + .p_addr = 0, +}; + +static void +msm_i2c_gpio_config(int iface, int config_type) +{ + int gpio_scl; + int gpio_sda; + if (iface) { + gpio_scl = 95; + gpio_sda = 96; + } else { + gpio_scl = 60; + gpio_sda = 61; + } + if (config_type) { + gpio_tlmm_config(GPIO_CFG(gpio_scl, 1, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + gpio_tlmm_config(GPIO_CFG(gpio_sda, 1, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + } else { + gpio_tlmm_config(GPIO_CFG(gpio_scl, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + gpio_tlmm_config(GPIO_CFG(gpio_sda, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + } +} + +static struct msm_i2c_platform_data msm_i2c_pdata = { + .clk_freq = 100000, + .rmutex = 0, + .pri_clk = 60, + .pri_dat = 61, + .aux_clk = 95, + .aux_dat = 96, + .msm_i2c_config_gpio = msm_i2c_gpio_config, +}; +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7627_proccomm_regulator_data + } +}; + +static void __init msm7627_init_regulators(void) +{ + int rc = platform_device_register(&msm_proccomm_regulator_dev); + if (rc) + pr_err("%s: could not register regulator device: %d\n", + __func__, rc); +} +static void __init msm_device_i2c_init(void) +{ + if (gpio_request(60, "i2c_pri_clk")) + pr_err("failed to request gpio i2c_pri_clk\n"); + if (gpio_request(61, "i2c_pri_dat")) + pr_err("failed to request gpio i2c_pri_dat\n"); + if (gpio_request(95, "i2c_sec_clk")) + pr_err("failed to request gpio i2c_sec_clk\n"); + if (gpio_request(96, "i2c_sec_dat")) + pr_err("failed to request gpio i2c_sec_dat\n"); + + if (cpu_is_msm7x27()) + msm_i2c_pdata.pm_lat = + msm7x27_pm_data[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] + .latency; + else + msm_i2c_pdata.pm_lat = + msm7x25_pm_data[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] + .latency; + + msm_device_i2c.dev.platform_data = &msm_i2c_pdata; +} + +static void usb_mpp_init(void) +{ + unsigned rc; + unsigned mpp_usb = 7; + + if (machine_is_msm7x25_ffa() || machine_is_msm7x27_ffa()) { + rc = mpp_config_digital_out(mpp_usb, + MPP_CFG(MPP_DLOGIC_LVL_VDD, + MPP_DLOGIC_OUT_CTRL_HIGH)); + if (rc) + pr_err("%s: configuring mpp pin" + "to enable 3.3V LDO failed\n", __func__); + } +} + +static void msm7x27_wlan_init(void) +{ + int rc = 0; + /* TBD: if (machine_is_msm7x27_ffa_with_wcn1312()) */ + if (machine_is_msm7x27_ffa()) { + rc = mpp_config_digital_out(3, MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_LOW)); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } +} + +static void msm_adsp_add_pdev(void) +{ + int rc = 0; + struct rpc_board_dev *rpc_adsp_pdev; + + rpc_adsp_pdev = kzalloc(sizeof(struct rpc_board_dev), GFP_KERNEL); + if (rpc_adsp_pdev == NULL) { + pr_err("%s: Memory Allocation failure\n", __func__); + return; + } + rpc_adsp_pdev->prog = ADSP_RPC_PROG; + rpc_adsp_pdev->pdev = msm_adsp_device; + rc = msm_rpc_add_board_dev(rpc_adsp_pdev, 1); + if (rc < 0) { + pr_err("%s: return val: %d\n", __func__, rc); + kfree(rpc_adsp_pdev); + } +} + static void __init msm7x2x_init(void) { - if (socinfo_init() < 0) - BUG(); + msm7627_init_regulators(); +#ifdef CONFIG_ARCH_MSM7X25 + msm_clock_init(msm_clocks_7x25, msm_num_clocks_7x25); +#elif defined(CONFIG_ARCH_MSM7X27) + msm_clock_init(&msm7x27_clock_init_data); +#endif + +#if defined(CONFIG_SMC91X) if (machine_is_msm7x25_ffa() || machine_is_msm7x27_ffa()) { smc91x_resources[0].start = 0x98000300; smc91x_resources[0].end = 0x980003ff; smc91x_resources[1].start = MSM_GPIO_TO_INT(85); smc91x_resources[1].end = MSM_GPIO_TO_INT(85); if (gpio_tlmm_config(GPIO_CFG(85, 0, - GPIO_INPUT, - GPIO_PULL_DOWN, - GPIO_2MA), - GPIO_ENABLE)) { + GPIO_CFG_INPUT, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_2MA), + GPIO_CFG_ENABLE)) { printk(KERN_ERR "%s: Err: Config GPIO-85 INT\n", __func__); } } +#endif + acpuclk_init(&acpuclk_7x27_soc_data); + usb_mpp_init(); + + +#ifdef CONFIG_USB_MSM_OTG_72K + msm_device_otg.dev.platform_data = &msm_otg_pdata; + if (machine_is_msm7x25_surf() || machine_is_msm7x25_ffa()) { + msm_otg_pdata.pemp_level = + PRE_EMPHASIS_WITH_20_PERCENT; + msm_otg_pdata.drv_ampl = HS_DRV_AMPLITUDE_5_PERCENT; + msm_otg_pdata.cdr_autoreset = CDR_AUTO_RESET_ENABLE; + msm_otg_pdata.phy_reset = msm_otg_rpc_phy_reset; + } + if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) { + msm_otg_pdata.pemp_level = + PRE_EMPHASIS_WITH_10_PERCENT; + msm_otg_pdata.drv_ampl = HS_DRV_AMPLITUDE_5_PERCENT; + msm_otg_pdata.cdr_autoreset = CDR_AUTO_RESET_DISABLE; + msm_otg_pdata.phy_reset_sig_inverted = 1; + } + +#ifdef CONFIG_USB_GADGET + msm_otg_pdata.swfi_latency = + msm7x27_pm_data + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_gadget_peripheral.dev.platform_data = &msm_gadget_pdata; + msm_gadget_pdata.is_phy_status_timer_on = 1; +#endif +#endif +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + msm_device_tsif.dev.platform_data = &tsif_platform_data; +#endif + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); platform_add_devices(devices, ARRAY_SIZE(devices)); +#ifdef CONFIG_MSM_CAMERA + config_camera_off_gpios(); /* might not be necessary */ +#endif + msm_adsp_add_pdev(); + msm_device_i2c_init(); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + +#ifdef CONFIG_SURF_FFA_GPIO_KEYPAD + if (machine_is_msm7x25_ffa() || machine_is_msm7x27_ffa()) + platform_device_register(&keypad_device_7k_ffa); + else + platform_device_register(&keypad_device_surf); +#endif + lcdc_gordon_gpio_init(); + msm_fb_add_devices(); +#ifdef CONFIG_USB_EHCI_MSM_72K + msm7x2x_init_host(); +#endif + msm7x2x_init_mmc(); + bt_power_init(); + + if (cpu_is_msm7x27()) + msm_pm_set_platform_data(msm7x27_pm_data, + ARRAY_SIZE(msm7x27_pm_data)); + else + msm_pm_set_platform_data(msm7x25_pm_data, + ARRAY_SIZE(msm7x25_pm_data)); + + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + + msm7x27_wlan_init(); +} + +static unsigned pmem_kernel_ebi1_size = PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) +{ + pmem_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); + +static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE; +static int __init pmem_mdp_size_setup(char *p) +{ + pmem_mdp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_mdp_size", pmem_mdp_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; +static int __init pmem_audio_size_setup(char *p) +{ + pmem_audio_size = memparse(p, NULL); + return 0; +} +early_param("pmem_audio_size", pmem_audio_size_setup); + +static unsigned fb_size = MSM_FB_SIZE; +static int __init fb_size_setup(char *p) +{ + fb_size = memparse(p, NULL); + return 0; +} +early_param("fb_size", fb_size_setup); + +static void __init msm_msm7x2x_allocate_memory_regions(void) +{ + void *addr; + unsigned long size; + + size = fb_size ? : MSM_FB_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); +} + +static struct memtype_reserve msm7x27_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init size_pmem_devices(void) +{ +#ifdef CONFIG_ANDROID_PMEM + android_pmem_adsp_pdata.size = pmem_adsp_size; + android_pmem_pdata.size = pmem_mdp_size; + android_pmem_audio_pdata.size = pmem_audio_size; +#endif +} + +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm7x27_reserve_table[p->memory_type].size += p->size; +} + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM + reserve_memory_for(&android_pmem_adsp_pdata); + reserve_memory_for(&android_pmem_pdata); + reserve_memory_for(&android_pmem_audio_pdata); + msm7x27_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif +} + +static void __init msm7x27_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); +} + +static int msm7x27_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct reserve_info msm7x27_reserve_info __initdata = { + .memtype_reserve_table = msm7x27_reserve_table, + .calculate_reserve_sizes = msm7x27_calculate_reserve_sizes, + .paddr_to_memtype = msm7x27_paddr_to_memtype, +}; + +static void __init msm7x27_reserve(void) +{ + reserve_info = &msm7x27_reserve_info; + msm_reserve(); +} + +static void __init msm7x27_init_early(void) +{ + msm_msm7x2x_allocate_memory_regions(); } static void __init msm7x2x_map_io(void) { msm_map_common_io(); - /* Technically dependent on the SoC but using machine_is - * macros since socinfo is not available this early and there - * are plans to restructure the code which will eliminate the - * need for socinfo. - */ - if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) - msm_clock_init(msm_clocks_7x27, msm_num_clocks_7x27); - if (machine_is_msm7x25_surf() || machine_is_msm7x25_ffa()) - msm_clock_init(msm_clocks_7x25, msm_num_clocks_7x25); + if (socinfo_init() < 0) + BUG(); #ifdef CONFIG_CACHE_L2X0 if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) { /* 7x27 has 256KB L2 cache: 64Kb/Way and 4-Way Associativity; - R/W latency: 3 cycles; evmon/parity/share disabled. */ - l2x0_init(MSM_L2CC_BASE, 0x00068012, 0xfe000000); + if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) > 1) + || ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) + && (SOCINFO_VERSION_MINOR(socinfo_get_version()) >= 3))) + /* R/W latency: 4 cycles; */ + l2x0_init(MSM_L2CC_BASE, 0x0006801B, 0xfe000000); + else + /* R/W latency: 3 cycles; */ + l2x0_init(MSM_L2CC_BASE, 0x00068012, 0xfe000000); } #endif } @@ -131,31 +1987,43 @@ static void __init msm7x2x_map_io(void) MACHINE_START(MSM7X27_SURF, "QCT MSM7x27 SURF") .atag_offset = 0x100, .map_io = msm7x2x_map_io, + .reserve = msm7x27_reserve, .init_irq = msm7x2x_init_irq, .init_machine = msm7x2x_init, .timer = &msm_timer, + .init_early = msm7x27_init_early, + .handle_irq = vic_handle_irq, MACHINE_END MACHINE_START(MSM7X27_FFA, "QCT MSM7x27 FFA") .atag_offset = 0x100, .map_io = msm7x2x_map_io, + .reserve = msm7x27_reserve, .init_irq = msm7x2x_init_irq, .init_machine = msm7x2x_init, .timer = &msm_timer, + .init_early = msm7x27_init_early, + .handle_irq = vic_handle_irq, MACHINE_END MACHINE_START(MSM7X25_SURF, "QCT MSM7x25 SURF") .atag_offset = 0x100, .map_io = msm7x2x_map_io, + .reserve = msm7x27_reserve, .init_irq = msm7x2x_init_irq, .init_machine = msm7x2x_init, .timer = &msm_timer, + .init_early = msm7x27_init_early, + .handle_irq = vic_handle_irq, MACHINE_END MACHINE_START(MSM7X25_FFA, "QCT MSM7x25 FFA") .atag_offset = 0x100, .map_io = msm7x2x_map_io, + .reserve = msm7x27_reserve, .init_irq = msm7x2x_init_irq, .init_machine = msm7x2x_init, .timer = &msm_timer, + .init_early = msm7x27_init_early, + .handle_irq = vic_handle_irq, MACHINE_END diff --git a/arch/arm/mach-msm/board-msm7x27a-regulator.c b/arch/arm/mach-msm/board-msm7x27a-regulator.c new file mode 100644 index 00000000000..c67ab7f65fd --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x27a-regulator.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "board-msm7x27a-regulator.h" + +#define VOLTAGE_RANGE(min_uV, max_uV, step_uV) ((max_uV - min_uV) / step_uV) + +/* Physically available PMIC regulator voltage setpoint ranges */ +#define p_ranges VOLTAGE_RANGE(1500000, 3300000, 25000) + +#define n_ranges VOLTAGE_RANGE(750000, 1525000, 12500) + +#define s_ranges (VOLTAGE_RANGE(700000, 1500000, 12500) + \ + VOLTAGE_RANGE(1500000, 3050000, 25000)) + +#define PCOM_VREG_CONSUMERS(name) \ + static struct regulator_consumer_supply __pcom_vreg_supply_##name[] + +#define PCOM_VREG_INIT_DATA(_name, _supply, _min_uV, _max_uV, _always_on, \ + _boot_on, _apply_uV, _supply_uV)\ +{ \ + .supply_regulator = _supply, \ + .consumer_supplies = __pcom_vreg_supply_##_name, \ + .num_consumer_supplies = ARRAY_SIZE(__pcom_vreg_supply_##_name), \ + .constraints = { \ + .name = #_name, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS, \ + .input_uV = _supply_uV, \ + .apply_uV = _apply_uV, \ + .boot_on = _boot_on, \ + .always_on = _always_on \ + } \ +} + +#define PCOM_VREG_SMP(_name, _id, _supply, _min_uV, _max_uV, _rise_time, \ + _pulldown, _always_on, _boot_on, _apply_uV, _supply_uV, _range) \ +{ \ + .init_data = PCOM_VREG_INIT_DATA(_name, _supply, _min_uV, _max_uV, \ + _always_on, _boot_on, _apply_uV, _supply_uV), \ + .id = _id, \ + .rise_time = _rise_time, \ + .pulldown = _pulldown, \ + .negative = 0, \ + .n_voltages = _range##_ranges, \ +} + +#define PCOM_VREG_LDO PCOM_VREG_SMP + +#define PCOM_VREG_NCP(_name, _id, _supply, _min_uV, _max_uV, _rise_time, \ + _always_on, _boot_on, _apply_uV, _supply_uV) \ +{ \ + .init_data = PCOM_VREG_INIT_DATA(_name, _supply, -(_min_uV), \ + -(_max_uV), _always_on, _boot_on, _apply_uV, _supply_uV), \ + .id = _id, \ + .rise_time = _rise_time, \ + .pulldown = -1, \ + .negative = 1, \ +} + +PCOM_VREG_CONSUMERS(smps1) = { + REGULATOR_SUPPLY("smps1", NULL), + REGULATOR_SUPPLY("msmc1", NULL), +}; + +PCOM_VREG_CONSUMERS(smps2) = { + REGULATOR_SUPPLY("smps2", NULL), + REGULATOR_SUPPLY("msmc2", NULL), +}; + +PCOM_VREG_CONSUMERS(smps3) = { + REGULATOR_SUPPLY("smps3", NULL), + REGULATOR_SUPPLY("msme1", NULL), + REGULATOR_SUPPLY("vcc_i2c", "1-004a"), + REGULATOR_SUPPLY("vcc_i2c", "1-0038"), +}; + +PCOM_VREG_CONSUMERS(smps4) = { + REGULATOR_SUPPLY("smps4", NULL), + REGULATOR_SUPPLY("rf", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo01) = { + REGULATOR_SUPPLY("ldo01", NULL), + REGULATOR_SUPPLY("ldo1", NULL), + REGULATOR_SUPPLY("rfrx1", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo02) = { + REGULATOR_SUPPLY("ldo02", NULL), + REGULATOR_SUPPLY("ldo2", NULL), + REGULATOR_SUPPLY("rfrx2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo03) = { + REGULATOR_SUPPLY("ldo03", NULL), + REGULATOR_SUPPLY("ldo3", NULL), + REGULATOR_SUPPLY("mddi", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo04) = { + REGULATOR_SUPPLY("ldo04", NULL), + REGULATOR_SUPPLY("ldo4", NULL), + REGULATOR_SUPPLY("pllx", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo05) = { + REGULATOR_SUPPLY("ldo05", NULL), + REGULATOR_SUPPLY("ldo5", NULL), + REGULATOR_SUPPLY("wlan2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo06) = { + REGULATOR_SUPPLY("ldo06", NULL), + REGULATOR_SUPPLY("ldo6", NULL), + REGULATOR_SUPPLY("wlan3", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo07) = { + REGULATOR_SUPPLY("ldo07", NULL), + REGULATOR_SUPPLY("ldo7", NULL), + REGULATOR_SUPPLY("msma", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo08) = { + REGULATOR_SUPPLY("ldo08", NULL), + REGULATOR_SUPPLY("ldo8", NULL), + REGULATOR_SUPPLY("tcxo", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo09) = { + REGULATOR_SUPPLY("ldo09", NULL), + REGULATOR_SUPPLY("ldo9", NULL), + REGULATOR_SUPPLY("usb2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo10) = { + REGULATOR_SUPPLY("ldo10", NULL), + REGULATOR_SUPPLY("emmc", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo11) = { + REGULATOR_SUPPLY("ldo11", NULL), + REGULATOR_SUPPLY("wlan_tcx0", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo12) = { + REGULATOR_SUPPLY("ldo12", NULL), + REGULATOR_SUPPLY("gp2", NULL), + REGULATOR_SUPPLY("vdd_ana", "1-004a"), + REGULATOR_SUPPLY("vdd", "1-0038"), +}; + +PCOM_VREG_CONSUMERS(ldo13) = { + REGULATOR_SUPPLY("ldo13", NULL), + REGULATOR_SUPPLY("mmc", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo14) = { + REGULATOR_SUPPLY("ldo14", NULL), + REGULATOR_SUPPLY("usb", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo15) = { + REGULATOR_SUPPLY("ldo15", NULL), + REGULATOR_SUPPLY("usim2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo16) = { + REGULATOR_SUPPLY("ldo16", NULL), + REGULATOR_SUPPLY("ruim", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo17) = { + REGULATOR_SUPPLY("ldo17", NULL), + REGULATOR_SUPPLY("bt", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo18) = { + REGULATOR_SUPPLY("ldo18", NULL), + REGULATOR_SUPPLY("rftx", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo19) = { + REGULATOR_SUPPLY("ldo19", NULL), + REGULATOR_SUPPLY("wlan4", NULL), +}; + +PCOM_VREG_CONSUMERS(ncp) = { + REGULATOR_SUPPLY("ncp", NULL), +}; + +static struct proccomm_regulator_info msm7x27a_pcom_vreg_info[] = { + /* Standard regulators (SMPS and LDO) + * R = rise time (us) + * P = pulldown (1 = pull down, 0 = float, -1 = don't care) + * A = always on + * B = boot on + * V = automatic voltage set (meaningful for single-voltage regs only) + * S = supply voltage (uV) + * T = type of regulator (smps, pldo, nldo) + * name id supp min uV max uV R P A B V S T*/ + PCOM_VREG_SMP(smps1, 3, NULL, 1100000, 1100000, 0, -1, 0, 0, 0, 0, s), + PCOM_VREG_SMP(smps2, 4, NULL, 1100000, 1100000, 0, -1, 0, 0, 0, 0, s), + PCOM_VREG_SMP(smps3, 2, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0, s), + PCOM_VREG_SMP(smps4, 24, NULL, 2100000, 2100000, 0, -1, 0, 0, 0, 0, s), + PCOM_VREG_LDO(ldo01, 12, NULL, 1800000, 2100000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo02, 13, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo03, 49, NULL, 1200000, 1200000, 0, -1, 0, 0, 0, 0, n), + PCOM_VREG_LDO(ldo04, 50, NULL, 1100000, 1100000, 0, -1, 0, 0, 0, 0, n), + PCOM_VREG_LDO(ldo05, 45, NULL, 1300000, 1350000, 0, -1, 0, 0, 0, 0, n), + PCOM_VREG_LDO(ldo06, 51, NULL, 1200000, 1200000, 0, -1, 0, 0, 0, 0, n), + PCOM_VREG_LDO(ldo07, 0, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo08, 9, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo09, 44, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo10, 52, NULL, 1800000, 3000000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo11, 53, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo12, 21, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo13, 18, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo14, 16, NULL, 3300000, 3300000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo15, 54, NULL, 1800000, 2850000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo16, 19, NULL, 1800000, 2850000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo17, 56, NULL, 2900000, 3300000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo18, 11, NULL, 2700000, 2700000, 0, -1, 0, 0, 0, 0, p), + PCOM_VREG_LDO(ldo19, 57, NULL, 1200000, 1800000, 0, -1, 0, 0, 0, 0, p), + + PCOM_VREG_NCP(ncp, 31, NULL, -1800000, -1800000, 0, 0, 0, 0, 0), +}; + +struct proccomm_regulator_platform_data msm7x27a_proccomm_regulator_data = { + .regs = msm7x27a_pcom_vreg_info, + .nregs = ARRAY_SIZE(msm7x27a_pcom_vreg_info) +}; diff --git a/arch/arm/mach-msm/board-msm7x27a-regulator.h b/arch/arm/mach-msm/board-msm7x27a-regulator.h new file mode 100644 index 00000000000..01dc70e987d --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x27a-regulator.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_7X27A_REGULATOR_H__ +#define __ARCH_ARM_MACH_MSM_BOARD_7X27A_REGULATOR_H__ + +#include "proccomm-regulator.h" + +extern struct proccomm_regulator_platform_data msm7x27a_proccomm_regulator_data; + +#endif diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c new file mode 100644 index 00000000000..1c32b5e5d8f --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x27a.c @@ -0,0 +1,1262 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" +#include "timer.h" +#include "board-msm7x27a-regulator.h" +#include "devices-msm7x2xa.h" +#include "pm.h" +#include +#include +#include "pm-boot.h" +#include "board-msm7627a.h" + +#define PMEM_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_PMEM_AUDIO_SIZE 0x1F4000 + +#if defined(CONFIG_GPIO_SX150X) +enum { + SX150X_CORE, +}; + +static struct sx150x_platform_data sx150x_data[] __initdata = { + [SX150X_CORE] = { + .gpio_base = GPIO_CORE_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0, + .io_pulldn_ena = 0x02, + .io_open_drain_ena = 0xfef8, + .irq_summary = -1, + }, +}; +#endif + + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) +static struct platform_device msm_wlan_ar6000_pm_device = { + .name = "wlan_ar6000_pm_dev", + .id = -1, +}; +#endif + +#if defined(CONFIG_I2C) && defined(CONFIG_GPIO_SX150X) +static struct i2c_board_info core_exp_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x3e), + }, +}; + +static void __init register_i2c_devices(void) +{ + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf() || + machine_is_msm8625_surf()) + sx150x_data[SX150X_CORE].io_open_drain_ena = 0xe0f0; + + core_exp_i2c_info[0].platform_data = + &sx150x_data[SX150X_CORE]; + + i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, + core_exp_i2c_info, + ARRAY_SIZE(core_exp_i2c_info)); +} +#endif + +static struct msm_gpio qup_i2c_gpios_io[] = { + { GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static struct msm_gpio qup_i2c_gpios_hw[] = { + { GPIO_CFG(60, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static void gsbi_qup_i2c_gpio_config(int adap_id, int config_type) +{ + int rc; + + if (adap_id < 0 || adap_id > 1) + return; + + /* Each adapter gets 2 lines from the table */ + if (config_type) + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + else + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + if (rc < 0) + pr_err("QUP GPIO request/enable failed: %d\n", rc); +} + +static struct msm_i2c_platform_data msm_gsbi0_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +#ifdef CONFIG_ARCH_MSM7X27A +#define MSM_PMEM_MDP_SIZE 0x2300000 +#define MSM7x25A_MSM_PMEM_MDP_SIZE 0x1500000 + +#define MSM_PMEM_ADSP_SIZE 0x1100000 +#define MSM7x25A_MSM_PMEM_ADSP_SIZE 0xB91000 + +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + int rc = 0; + unsigned gpio; + + gpio = GPIO_HOST_VBUS_EN; + + rc = gpio_request(gpio, "i2c_host_vbus_en"); + if (rc < 0) { + pr_err("failed to request %d GPIO\n", gpio); + return; + } + gpio_direction_output(gpio, !!on); + gpio_set_value_cansleep(gpio, !!on); + gpio_free(gpio); +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), +}; + +static void __init msm7x2x_init_host(void) +{ + msm_add_host(0, &msm_usb_host_pdata); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} + +static struct regulator *reg_hsusb; +static int msm_hsusb_ldo_init(int init) +{ + int rc = 0; + + if (init) { + reg_hsusb = regulator_get(NULL, "usb"); + if (IS_ERR(reg_hsusb)) { + rc = PTR_ERR(reg_hsusb); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_hsusb, 3300000, 3300000); + if (rc) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto reg_free; + } + + return 0; + } + /* else fall through */ +reg_free: + regulator_put(reg_hsusb); +out: + reg_hsusb = NULL; + return rc; +} + +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + + if (IS_ERR_OR_NULL(reg_hsusb)) + return reg_hsusb ? PTR_ERR(reg_hsusb) : -ENODEV; + + if (ldo_status == enable) + return 0; + + ldo_status = enable; + + return enable ? + regulator_enable(reg_hsusb) : + regulator_disable(reg_hsusb); +} + +#ifndef CONFIG_USB_EHCI_MSM_72K +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret = 0; + + if (init) + ret = msm_pm_app_rpc_init(callback); + else + msm_pm_app_rpc_deinit(callback); + + return ret; +} +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { +#ifndef CONFIG_USB_EHCI_MSM_72K + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, +#else + .vbus_power = msm_hsusb_vbus_power, +#endif + .rpc_connect = hsusb_rpc_connect, + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .drv_ampl = HS_DRV_AMPLITUDE_DEFAULT, + .se1_gating = SE1_GATING_DISABLE, + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, + .chg_init = hsusb_chg_init, + .chg_connected = hsusb_chg_connected, + .chg_vbus_draw = hsusb_chg_vbus_draw, +}; +#endif + +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x90000300, + .end = 0x900003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(4), + .end = MSM_GPIO_TO_INT(4), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, +}; +#endif +static struct msm_pm_platform_data msm7x27a_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 16000, + .residency = 20000, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 12000, + .residency = 20000, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 1, + .latency = 2000, + .residency = 0, + }, + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + .p_addr = 0, +}; + +/* 8625 PM platform data */ +static struct msm_pm_platform_data msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * 2] = { + /* CORE0 entries */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 16000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 12000, + .residency = 20000, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + +}; + +static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, + .v_addr = MSM_CFG_CTL_BASE, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 1, + .memory_type = MEMTYPE_EBI1, + .request_region = request_fmem_c_region, + .release_region = release_fmem_c_region, + .reusable = 1, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE; +static int __init pmem_mdp_size_setup(char *p) +{ + pmem_mdp_size = memparse(p, NULL); + return 0; +} + +early_param("pmem_mdp_size", pmem_mdp_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} + +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +#define SND(desc, num) { .name = #desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(HANDSET, 0), + SND(MONO_HEADSET, 2), + SND(HEADSET, 3), + SND(SPEAKER, 6), + SND(TTY_HEADSET, 8), + SND(TTY_VCO, 9), + SND(TTY_HCO, 10), + SND(BT, 12), + SND(IN_S_SADC_OUT_HANDSET, 16), + SND(IN_S_SADC_OUT_SPEAKER_PHONE, 25), + SND(FM_DIGITAL_STEREO_HEADSET, 26), + SND(FM_DIGITAL_SPEAKER_PHONE, 27), + SND(FM_DIGITAL_BT_A2DP_HEADSET, 28), + SND(STEREO_HEADSET_AND_SPEAKER, 31), + SND(CURRENT, 0x7FFFFFFE), + SND(FM_ANALOG_STEREO_HEADSET, 35), + SND(FM_ANALOG_STEREO_HEADSET_CODEC, 36), +}; +#undef SND + +static struct msm_snd_endpoints msm_device_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = sizeof(snd_endpoints_list) / sizeof(struct snd_endpoint) +}; + +static struct platform_device msm_device_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &msm_device_snd_endpoints + }, +}; + +#define DEC0_FORMAT ((1<reusable) + fmem_pdata.size += pdata->size; + + reusable_count += (pdata->reusable) ? 1 : 0; + + if (pdata->reusable && reusable_count > 1) { + pr_err("%s: Too many PMEM devices specified as reusable. PMEM device %s was not configured as reusable.\n", + __func__, pdata->name); + pdata->reusable = 0; + } + } +#endif + +} + +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm7x27a_reserve_table[p->memory_type].size += p->size; +} + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM + unsigned int i; + for (i = 0; i < ARRAY_SIZE(pmem_pdata_array); ++i) + reserve_memory_for(pmem_pdata_array[i]); + + msm7x27a_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif +} + +static void __init msm7x27a_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); +} + +static int msm7x27a_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct reserve_info msm7x27a_reserve_info __initdata = { + .memtype_reserve_table = msm7x27a_reserve_table, + .calculate_reserve_sizes = msm7x27a_calculate_reserve_sizes, + .paddr_to_memtype = msm7x27a_paddr_to_memtype, +}; + +static void __init msm7x27a_reserve(void) +{ + reserve_info = &msm7x27a_reserve_info; + msm_reserve(); +} + +static void __init msm8625_reserve(void) +{ + msm7x27a_reserve(); + memblock_remove(MSM8625_SECONDARY_PHYS, SZ_8); + memblock_remove(MSM8625_WARM_BOOT_PHYS, SZ_32); +} + +static void __init msm7x27a_device_i2c_init(void) +{ + msm_gsbi0_qup_i2c_device.dev.platform_data = &msm_gsbi0_qup_i2c_pdata; + msm_gsbi1_qup_i2c_device.dev.platform_data = &msm_gsbi1_qup_i2c_pdata; +} + +static void __init msm8625_device_i2c_init(void) +{ + msm8625_gsbi0_qup_i2c_device.dev.platform_data = + &msm_gsbi0_qup_i2c_pdata; + msm8625_gsbi1_qup_i2c_device.dev.platform_data = + &msm_gsbi1_qup_i2c_pdata; +} + +#define MSM_EBI2_PHYS 0xa0d00000 +#define MSM_EBI2_XMEM_CS2_CFG1 0xa0d10030 + +static void __init msm7x27a_init_ebi2(void) +{ + uint32_t ebi2_cfg; + void __iomem *ebi2_cfg_ptr; + + ebi2_cfg_ptr = ioremap_nocache(MSM_EBI2_PHYS, sizeof(uint32_t)); + if (!ebi2_cfg_ptr) + return; + + ebi2_cfg = readl(ebi2_cfg_ptr); + if (machine_is_msm7x27a_rumi3() || machine_is_msm7x27a_surf() || + machine_is_msm7625a_surf() || machine_is_msm8625_surf()) + ebi2_cfg |= (1 << 4); /* CS2 */ + + writel(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); + + /* Enable A/D MUX[bit 31] from EBI2_XMEM_CS2_CFG1 */ + ebi2_cfg_ptr = ioremap_nocache(MSM_EBI2_XMEM_CS2_CFG1, + sizeof(uint32_t)); + if (!ebi2_cfg_ptr) + return; + + ebi2_cfg = readl(ebi2_cfg_ptr); + if (machine_is_msm7x27a_surf() || machine_is_msm7625a_surf()) + ebi2_cfg |= (1 << 31); + + writel(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); +} + +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7x27a_proccomm_regulator_data + } +}; + +static void msm_adsp_add_pdev(void) +{ + int rc = 0; + struct rpc_board_dev *rpc_adsp_pdev; + + rpc_adsp_pdev = kzalloc(sizeof(struct rpc_board_dev), GFP_KERNEL); + if (rpc_adsp_pdev == NULL) { + pr_err("%s: Memory Allocation failure\n", __func__); + return; + } + rpc_adsp_pdev->prog = ADSP_RPC_PROG; + + if (cpu_is_msm8625()) + rpc_adsp_pdev->pdev = msm8625_device_adsp; + else + rpc_adsp_pdev->pdev = msm_adsp_device; + rc = msm_rpc_add_board_dev(rpc_adsp_pdev, 1); + if (rc < 0) { + pr_err("%s: return val: %d\n", __func__, rc); + kfree(rpc_adsp_pdev); + } +} + +static void __init msm7627a_rumi3_init(void) +{ + msm7x27a_init_ebi2(); + platform_add_devices(rumi_sim_devices, + ARRAY_SIZE(rumi_sim_devices)); +} + +static void __init msm8625_rumi3_init(void) +{ + msm7x2x_misc_init(); + msm_adsp_add_pdev(); + msm8625_device_i2c_init(); + platform_add_devices(msm8625_rumi3_devices, + ARRAY_SIZE(msm8625_rumi3_devices)); + + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); +} + +#define UART1DM_RX_GPIO 45 + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) +static int __init msm7x27a_init_ar6000pm(void) +{ + msm_wlan_ar6000_pm_device.dev.platform_data = &ar600x_wlan_power; + return platform_device_register(&msm_wlan_ar6000_pm_device); +} +#else +static int __init msm7x27a_init_ar6000pm(void) { return 0; } +#endif + +static void __init msm7x27a_init_regulators(void) +{ + int rc = platform_device_register(&msm_proccomm_regulator_dev); + if (rc) + pr_err("%s: could not register regulator device: %d\n", + __func__, rc); +} + +static void __init msm7x27a_add_footswitch_devices(void) +{ + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); +} + +static void __init msm7x27a_add_platform_devices(void) +{ + if (machine_is_msm8625_surf() || machine_is_msm8625_ffa()) { + platform_add_devices(msm8625_surf_devices, + ARRAY_SIZE(msm8625_surf_devices)); + } else { + platform_add_devices(msm7627a_surf_ffa_devices, + ARRAY_SIZE(msm7627a_surf_ffa_devices)); + } + + platform_add_devices(common_devices, + ARRAY_SIZE(common_devices)); +} + +static void __init msm7x27a_uartdm_config(void) +{ + msm7x27a_cfg_uart2dm_serial(); + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(UART1DM_RX_GPIO); + if (cpu_is_msm8625()) + msm8625_device_uart_dm1.dev.platform_data = + &msm_uart_dm1_pdata; + else + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +} + +static void __init msm7x27a_otg_gadget(void) +{ + if (cpu_is_msm8625()) { + msm_otg_pdata.swfi_latency = + msm8625_pm_data[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency; + msm8625_device_otg.dev.platform_data = &msm_otg_pdata; + msm8625_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } else { + msm_otg_pdata.swfi_latency = + msm7x27a_pm_data[ + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } +} + +static void __init msm7x27a_pm_init(void) +{ + if (machine_is_msm8625_surf() || machine_is_msm8625_ffa()) { + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + } else { + msm_pm_set_platform_data(msm7x27a_pm_data, + ARRAY_SIZE(msm7x27a_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + } + + msm_pm_register_irqs(); +} + +static void __init msm7x2x_init(void) +{ + msm7x2x_misc_init(); + + /* Initialize regulators first so that other devices can use them */ + msm7x27a_init_regulators(); + msm_adsp_add_pdev(); + if (cpu_is_msm8625()) + msm8625_device_i2c_init(); + else + msm7x27a_device_i2c_init(); + msm7x27a_init_ebi2(); + msm7x27a_uartdm_config(); + + msm7x27a_otg_gadget(); + msm7x27a_cfg_smsc911x(); + + msm7x27a_add_footswitch_devices(); + msm7x27a_add_platform_devices(); + /* Ensure ar6000pm device is registered before MMC/SDC */ + msm7x27a_init_ar6000pm(); + msm7627a_init_mmc(); + msm_fb_add_devices(); + msm7x2x_init_host(); + msm7x27a_pm_init(); + register_i2c_devices(); +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) + msm7627a_bt_power_init(); +#endif + msm7627a_camera_init(); + msm7627a_add_io_devices(); + /*7x25a kgsl initializations*/ + msm7x25a_kgsl_3d0_init(); + /*8x25 kgsl initializations*/ + msm8x25_kgsl_3d0_init(); +} + +static void __init msm7x2x_init_early(void) +{ + msm_msm7627a_allocate_memory_regions(); +} + +MACHINE_START(MSM7X27A_RUMI3, "QCT MSM7x27a RUMI3") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7627a_rumi3_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7X27A_SURF, "QCT MSM7x27a SURF") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7X27A_FFA, "QCT MSM7x27a FFA") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7625A_SURF, "QCT MSM7625a SURF") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7625A_FFA, "QCT MSM7625a FFA") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7x27a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_RUMI3, "QCT MSM8625 RUMI3") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm8625_rumi3_init, + .timer = &msm_timer, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_SURF, "QCT MSM8625 SURF") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_FFA, "QCT MSM8625 FFA") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm7x2x_init, + .timer = &msm_timer, + .init_early = msm7x2x_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END diff --git a/arch/arm/mach-msm/board-msm7x30-regulator.c b/arch/arm/mach-msm/board-msm7x30-regulator.c new file mode 100644 index 00000000000..a3f5ee12d23 --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x30-regulator.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "board-msm7x30-regulator.h" + +#define PCOM_VREG_CONSUMERS(name) \ + static struct regulator_consumer_supply __pcom_vreg_supply_##name[] + +#define PCOM_VREG_CONSTRAINT_LVSW(_name, _always_on, _boot_on, _supply_uV) \ +{ \ + .name = #_name, \ + .min_uV = 0, \ + .max_uV = 0, \ + .input_uV = _supply_uV, \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .apply_uV = 0, \ + .boot_on = _boot_on, \ + .always_on = _always_on \ +} + +#define PCOM_VREG_CONSTRAINT_DYN(_name, _min_uV, _max_uV, _always_on, \ + _boot_on, _apply_uV, _supply_uV) \ +{ \ + .name = #_name, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, \ + .input_uV = _supply_uV, \ + .apply_uV = _apply_uV, \ + .boot_on = _boot_on, \ + .always_on = _always_on \ +} + + +#define PCOM_VREG_INIT(_name, _supply, _constraints)\ +{ \ + .supply_regulator = _supply, \ + .consumer_supplies = __pcom_vreg_supply_##_name, \ + .num_consumer_supplies = ARRAY_SIZE(__pcom_vreg_supply_##_name), \ + .constraints = _constraints \ +} + +#define PCOM_VREG_SMP(_name, _id, _supply, _min_uV, _max_uV, _rise_time, \ + _pulldown, _always_on, _boot_on, _apply_uV, _supply_uV) \ +{ \ + .init_data = PCOM_VREG_INIT(_name, _supply, \ + PCOM_VREG_CONSTRAINT_DYN(_name, _min_uV, _max_uV, _always_on, \ + _boot_on, _apply_uV, _supply_uV)), \ + .id = _id, \ + .rise_time = _rise_time, \ + .pulldown = _pulldown, \ + .negative = 0, \ +} + +#define PCOM_VREG_LDO PCOM_VREG_SMP + +#define PCOM_VREG_LVS(_name, _id, _supply, _rise_time, _pulldown, _always_on, \ + _boot_on) \ +{ \ + .init_data = PCOM_VREG_INIT(_name, _supply, \ + PCOM_VREG_CONSTRAINT_LVSW(_name, _always_on, _boot_on, 0)), \ + .id = _id, \ + .rise_time = _rise_time, \ + .pulldown = _pulldown, \ + .negative = 0, \ +} + +#define PCOM_VREG_NCP(_name, _id, _supply, _min_uV, _max_uV, _rise_time, \ + _always_on, _boot_on, _apply_uV, _supply_uV) \ +{ \ + .init_data = PCOM_VREG_INIT(_name, _supply, \ + PCOM_VREG_CONSTRAINT_DYN(_name, -(_min_uV), -(_max_uV), \ + _always_on, _boot_on, _apply_uV, _supply_uV)), \ + .id = _id, \ + .rise_time = _rise_time, \ + .pulldown = -1, \ + .negative = 1, \ +} + +PCOM_VREG_CONSUMERS(smps0) = { + REGULATOR_SUPPLY("smps0", NULL), + REGULATOR_SUPPLY("msmc1", NULL), +}; + +PCOM_VREG_CONSUMERS(smps1) = { + REGULATOR_SUPPLY("smps1", NULL), + REGULATOR_SUPPLY("msmc2", NULL), +}; + +PCOM_VREG_CONSUMERS(smps2) = { + REGULATOR_SUPPLY("smps2", NULL), + REGULATOR_SUPPLY("s2", NULL), +}; + +PCOM_VREG_CONSUMERS(smps3) = { + REGULATOR_SUPPLY("smps3", NULL), + REGULATOR_SUPPLY("s3", NULL), +}; + +PCOM_VREG_CONSUMERS(smps4) = { + REGULATOR_SUPPLY("smps4", NULL), + REGULATOR_SUPPLY("s4", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo00) = { + REGULATOR_SUPPLY("ldo00", NULL), + REGULATOR_SUPPLY("ldo0", NULL), + REGULATOR_SUPPLY("gp3", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo02) = { + REGULATOR_SUPPLY("ldo02", NULL), + REGULATOR_SUPPLY("ldo2", NULL), + REGULATOR_SUPPLY("xo_out", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo03) = { + REGULATOR_SUPPLY("ldo03", NULL), + REGULATOR_SUPPLY("ldo3", NULL), + REGULATOR_SUPPLY("ruim", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo04) = { + REGULATOR_SUPPLY("ldo04", NULL), + REGULATOR_SUPPLY("ldo4", NULL), + REGULATOR_SUPPLY("tcxo", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo05) = { + REGULATOR_SUPPLY("ldo05", NULL), + REGULATOR_SUPPLY("ldo5", NULL), + REGULATOR_SUPPLY("mmc", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo06) = { + REGULATOR_SUPPLY("ldo06", NULL), + REGULATOR_SUPPLY("ldo6", NULL), + REGULATOR_SUPPLY("usb", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo07) = { + REGULATOR_SUPPLY("ldo07", NULL), + REGULATOR_SUPPLY("ldo7", NULL), + REGULATOR_SUPPLY("usb2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo08) = { + REGULATOR_SUPPLY("ldo08", NULL), + REGULATOR_SUPPLY("ldo8", NULL), + REGULATOR_SUPPLY("gp7", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo09) = { + REGULATOR_SUPPLY("ldo09", NULL), + REGULATOR_SUPPLY("ldo9", NULL), + REGULATOR_SUPPLY("gp1", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo10) = { + REGULATOR_SUPPLY("ldo10", NULL), + REGULATOR_SUPPLY("gp4", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo11) = { + REGULATOR_SUPPLY("ldo11", NULL), + REGULATOR_SUPPLY("gp2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo12) = { + REGULATOR_SUPPLY("ldo12", NULL), + REGULATOR_SUPPLY("gp9", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo13) = { + REGULATOR_SUPPLY("ldo13", NULL), + REGULATOR_SUPPLY("wlan", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo14) = { + REGULATOR_SUPPLY("ldo14", NULL), + REGULATOR_SUPPLY("rf", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo15) = { + REGULATOR_SUPPLY("ldo15", NULL), + REGULATOR_SUPPLY("gp6", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo16) = { + REGULATOR_SUPPLY("ldo16", NULL), + REGULATOR_SUPPLY("gp10", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo17) = { + REGULATOR_SUPPLY("ldo17", NULL), + REGULATOR_SUPPLY("gp11", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo18) = { + REGULATOR_SUPPLY("ldo18", NULL), + REGULATOR_SUPPLY("gp12", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo19) = { + REGULATOR_SUPPLY("ldo19", NULL), + REGULATOR_SUPPLY("wlan2", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo20) = { + REGULATOR_SUPPLY("ldo20", NULL), + REGULATOR_SUPPLY("gp13", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo21) = { + REGULATOR_SUPPLY("ldo21", NULL), + REGULATOR_SUPPLY("gp14", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo22) = { + REGULATOR_SUPPLY("ldo22", NULL), + REGULATOR_SUPPLY("gp15", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo23) = { + REGULATOR_SUPPLY("ldo23", NULL), + REGULATOR_SUPPLY("gp5", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo24) = { + REGULATOR_SUPPLY("ldo24", NULL), + REGULATOR_SUPPLY("gp16", NULL), +}; + +PCOM_VREG_CONSUMERS(ldo25) = { + REGULATOR_SUPPLY("ldo25", NULL), + REGULATOR_SUPPLY("gp17", NULL), +}; + +PCOM_VREG_CONSUMERS(lvsw0) = { + REGULATOR_SUPPLY("lvsw0", NULL), + REGULATOR_SUPPLY("lvs0", NULL), +}; + +PCOM_VREG_CONSUMERS(lvsw1) = { + REGULATOR_SUPPLY("lvsw1", NULL), + REGULATOR_SUPPLY("lvs1", NULL), +}; + +PCOM_VREG_CONSUMERS(ncp) = { + REGULATOR_SUPPLY("ncp", NULL), +}; + +/* This list needs to be verified against actual 7x30 hardware requirements. */ +static struct proccomm_regulator_info msm7x30_pcom_vreg_info[] = { + /* Standard regulators (SMPS and LDO) + * R = rise time (us) + * P = pulldown (1 = pull down, 0 = float, -1 = don't care) + * A = always on + * B = boot on + * V = automatic voltage set (meaningful for single-voltage regs only) + * S = supply voltage (uV) + * name id supp min uV max uV R P A B V S */ + PCOM_VREG_SMP(smps0, 3, NULL, 500000, 1500000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps1, 4, NULL, 500000, 1500000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps2, 28, NULL, 1300000, 1300000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps3, 29, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_SMP(smps4, 43, NULL, 2200000, 2200000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo00, 5, NULL, 1200000, 1200000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo02, 46, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo03, 19, NULL, 1800000, 3000000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo04, 9, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo05, 18, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo06, 16, NULL, 3075000, 3400000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo07, 44, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo08, 32, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo09, 8, NULL, 2050000, 2050000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo10, 7, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo11, 21, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo12, 34, NULL, 1800000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo13, 15, NULL, 2900000, 3050000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo14, 24, NULL, 2850000, 2850000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo15, 23, NULL, 3050000, 3100000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo16, 35, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo17, 36, NULL, 2600000, 2600000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo18, 37, NULL, 2200000, 2200000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo19, 45, NULL, 2400000, 2500000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo20, 38, NULL, 1500000, 1800000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo21, 39, NULL, 1100000, 1100000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo22, 40, NULL, 1200000, 1300000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo23, 22, NULL, 1350000, 1350000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo24, 41, NULL, 1200000, 1200000, 0, -1, 0, 0, 0, 0), + PCOM_VREG_LDO(ldo25, 42, NULL, 1200000, 1200000, 0, -1, 0, 0, 0, 0), + + /* Low-voltage switches */ + PCOM_VREG_LVS(lvsw0, 47, NULL, 0, -1, 0, 0), + PCOM_VREG_LVS(lvsw1, 48, NULL, 0, -1, 0, 0), + + PCOM_VREG_NCP(ncp, 31, NULL, -1800000, -1800000, 0, 0, 0, 0, 0), +}; + +struct proccomm_regulator_platform_data msm7x30_proccomm_regulator_data = { + .regs = msm7x30_pcom_vreg_info, + .nregs = ARRAY_SIZE(msm7x30_pcom_vreg_info) +}; diff --git a/arch/arm/mach-msm/board-msm7x30-regulator.h b/arch/arm/mach-msm/board-msm7x30-regulator.h new file mode 100644 index 00000000000..bd9b02da98c --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x30-regulator.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_7X30_REGULATOR_H__ +#define __ARCH_ARM_MACH_MSM_BOARD_7X30_REGULATOR_H__ + +#include "proccomm-regulator.h" + +extern struct proccomm_regulator_platform_data msm7x30_proccomm_regulator_data; + +#endif diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index db81ed53103..7ac0c9a5f84 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -9,143 +9,7352 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ -#include + #include #include #include #include #include +#include #include +#ifdef CONFIG_SPI_QSD +#include +#endif +#include +#include +#include +#include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include +#include #include +#include +#include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include + #include "devices.h" -#include "gpiomux.h" -#include "proc_comm.h" +#include "timer.h" +#ifdef CONFIG_USB_G_ANDROID +#include +#include +#endif +#include "pm.h" +#include "pm-boot.h" +#include "spm.h" +#include "acpuclock.h" +#include +#include +#include +#include +#include +#include "smd_private.h" +#include -extern struct sys_timer msm_timer; +#include "board-msm7x30-regulator.h" +#include "pm.h" -static void __init msm7x30_fixup(struct tag *tag, char **cmdline, - struct meminfo *mi) +#define MSM_PMEM_SF_SIZE 0x1700000 +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_PRIM_BUF_SIZE (864 * 480 * 4 * 3) /* 4bpp * 3 Pages */ +#else +#define MSM_FB_PRIM_BUF_SIZE (864 * 480 * 4 * 2) /* 4bpp * 2 Pages */ +#endif +/* + * Reserve space for double buffered full screen + * res V4L2 video overlay - i.e. 1280x720x1.5x2 + */ +#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 2764800 + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +#define MSM_FB_EXT_BUF_SIZE (1280 * 720 * 2 * 1) /* 2 bpp x 1 page */ +#else +#define MSM_FB_EXT_BUF_SIZE 0 +#endif + +#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK +/* width x height x 3 bpp x 2 frame buffer */ +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((864 * 480 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE 0 +#endif + +#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096) + +#define MSM_PMEM_ADSP_SIZE 0x1E00000 +#define MSM_FLUID_PMEM_ADSP_SIZE 0x2800000 +#define PMEM_KERNEL_EBI0_SIZE 0x600000 +#define MSM_PMEM_AUDIO_SIZE 0x200000 + +#define PMIC_GPIO_INT 27 +#define PMIC_VREG_WLAN_LEVEL 2900 +#define PMIC_GPIO_SD_DET 36 +#define PMIC_GPIO_SDC4_EN_N 17 /* PMIC GPIO Number 18 */ +#define PMIC_GPIO_HDMI_5V_EN_V3 32 /* PMIC GPIO for V3 H/W */ +#define PMIC_GPIO_HDMI_5V_EN_V2 39 /* PMIC GPIO for V2 H/W */ + +#define ADV7520_I2C_ADDR 0x39 + +#define FPGA_SDCC_STATUS 0x8E0001A8 + +#define FPGA_OPTNAV_GPIO_ADDR 0x8E000026 +#define OPTNAV_I2C_SLAVE_ADDR (0xB0 >> 1) +#define OPTNAV_IRQ 20 +#define OPTNAV_CHIP_SELECT 19 +#define PMIC_GPIO_SDC4_PWR_EN_N 24 /* PMIC GPIO Number 25 */ + +/* Macros assume PMIC GPIOs start at 0 */ +#define PM8058_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + NR_GPIO_IRQS) +#define PM8058_GPIO_SYS_TO_PM(sys_gpio) (sys_gpio - NR_GPIO_IRQS) +#define PM8058_MPP_BASE PM8058_GPIO_PM_TO_SYS(PM8058_GPIOS) +#define PM8058_MPP_PM_TO_SYS(pm_gpio) (pm_gpio + PM8058_MPP_BASE) + +#define PMIC_GPIO_FLASH_BOOST_ENABLE 15 /* PMIC GPIO Number 16 */ +#define PMIC_GPIO_HAP_ENABLE 16 /* PMIC GPIO Number 17 */ + +#define PMIC_GPIO_WLAN_EXT_POR 22 /* PMIC GPIO NUMBER 23 */ + +#define BMA150_GPIO_INT 1 + +#define HAP_LVL_SHFT_MSM_GPIO 24 + +#define PMIC_GPIO_QUICKVX_CLK 37 /* PMIC GPIO 38 */ + +#define PM_FLIP_MPP 5 /* PMIC MPP 06 */ + +#define DDR1_BANK_BASE 0X20000000 +#define DDR2_BANK_BASE 0X40000000 + +static unsigned int phys_add = DDR2_BANK_BASE; +unsigned long ebi1_phys_offset = DDR2_BANK_BASE; +EXPORT_SYMBOL(ebi1_phys_offset); + +struct pm8xxx_gpio_init_info { + unsigned gpio; + struct pm_gpio config; +}; + +static int pm8058_gpios_init(void) { - for (; tag->hdr.size; tag = tag_next(tag)) - if (tag->hdr.tag == ATAG_MEM && tag->u.mem.start == 0x200000) { - tag->u.mem.start = 0; - tag->u.mem.size += SZ_2M; + int rc; + + struct pm8xxx_gpio_init_info sdc4_en = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC4_EN_N), + { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_L5, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + .out_strength = PM_GPIO_STRENGTH_LOW, + .output_value = 0, + }, + }; + + struct pm8xxx_gpio_init_info sdc4_pwr_en = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC4_PWR_EN_N), + { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_L5, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + .out_strength = PM_GPIO_STRENGTH_LOW, + .output_value = 0, + }, + }; + + struct pm8xxx_gpio_init_info haptics_enable = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HAP_ENABLE), + { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + .vin_sel = 2, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + }, + }; + + struct pm8xxx_gpio_init_info hdmi_5V_en = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HDMI_5V_EN_V3), + { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_VPH, + .function = PM_GPIO_FUNC_NORMAL, + .out_strength = PM_GPIO_STRENGTH_LOW, + .output_value = 0, + }, + }; + + struct pm8xxx_gpio_init_info flash_boost_enable = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_FLASH_BOOST_ENABLE), + { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_2, + }, + }; + + struct pm8xxx_gpio_init_info gpio23 = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_WLAN_EXT_POR), + { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = 2, + .out_strength = PM_GPIO_STRENGTH_LOW, + .function = PM_GPIO_FUNC_NORMAL, } + }; + +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + struct pm8xxx_gpio_init_info sdcc_det = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SD_DET - 1), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_1P5, + .vin_sel = 2, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }; + + if (machine_is_msm7x30_fluid()) + sdcc_det.config.inv_int_pol = 1; + + rc = pm8xxx_gpio_config(sdcc_det.gpio, &sdcc_det.config); + if (rc) { + pr_err("%s PMIC_GPIO_SD_DET config failed\n", __func__); + return rc; + } +#endif + + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa() || + machine_is_msm7x30_fluid()) + hdmi_5V_en.gpio = PMIC_GPIO_HDMI_5V_EN_V2; + else + hdmi_5V_en.gpio = PMIC_GPIO_HDMI_5V_EN_V3; + + hdmi_5V_en.gpio = PM8058_GPIO_PM_TO_SYS(hdmi_5V_en.gpio); + + rc = pm8xxx_gpio_config(hdmi_5V_en.gpio, &hdmi_5V_en.config); + if (rc) { + pr_err("%s PMIC_GPIO_HDMI_5V_EN config failed\n", __func__); + return rc; + } + + /* Deassert GPIO#23 (source for Ext_POR on WLAN-Volans) */ + rc = pm8xxx_gpio_config(gpio23.gpio, &gpio23.config); + if (rc) { + pr_err("%s PMIC_GPIO_WLAN_EXT_POR config failed\n", __func__); + return rc; + } + + if (machine_is_msm7x30_fluid()) { + /* Haptics gpio */ + rc = pm8xxx_gpio_config(haptics_enable.gpio, + &haptics_enable.config); + if (rc) { + pr_err("%s: PMIC GPIO %d write failed\n", __func__, + haptics_enable.gpio); + return rc; + } + /* Flash boost gpio */ + rc = pm8xxx_gpio_config(flash_boost_enable.gpio, + &flash_boost_enable.config); + if (rc) { + pr_err("%s: PMIC GPIO %d write failed\n", __func__, + flash_boost_enable.gpio); + return rc; + } + /* SCD4 gpio */ + rc = pm8xxx_gpio_config(sdc4_en.gpio, &sdc4_en.config); + if (rc) { + pr_err("%s PMIC_GPIO_SDC4_EN_N config failed\n", + __func__); + return rc; + } + rc = gpio_request(sdc4_en.gpio, "sdc4_en"); + if (rc) { + pr_err("%s PMIC_GPIO_SDC4_EN_N gpio_request failed\n", + __func__); + return rc; + } + gpio_set_value_cansleep(sdc4_en.gpio, 0); + } + /* FFA -> gpio_25 controls vdd of sdcc4 */ + else { + /* SCD4 gpio_25 */ + rc = pm8xxx_gpio_config(sdc4_pwr_en.gpio, &sdc4_pwr_en.config); + if (rc) { + pr_err("%s PMIC_GPIO_SDC4_PWR_EN_N config failed: %d\n", + __func__, rc); + return rc; + } + + rc = gpio_request(sdc4_pwr_en.gpio, "sdc4_pwr_en"); + if (rc) { + pr_err("PMIC_GPIO_SDC4_PWR_EN_N gpio_req failed: %d\n", + rc); + return rc; + } + } + + return 0; } -static void __init msm7x30_reserve(void) +/* Regulator API support */ + +#ifdef CONFIG_MSM_PROC_COMM_REGULATOR +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7x30_proccomm_regulator_data + } +}; +#endif + +/*virtual key support */ +static ssize_t tma300_vkeys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { - memblock_remove(0x0, SZ_2M); + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":50:842:80:100" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":170:842:80:100" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":290:842:80:100" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":410:842:80:100" + "\n"); } -static int hsusb_phy_init_seq[] = { - 0x30, 0x32, /* Enable and set Pre-Emphasis Depth to 20% */ - 0x02, 0x36, /* Disable CDR Auto Reset feature */ - -1 +static struct kobj_attribute tma300_vkeys_attr = { + .attr = { + .mode = S_IRUGO, + }, + .show = &tma300_vkeys_show, }; -static struct msm_otg_platform_data msm_otg_pdata = { - .phy_init_seq = hsusb_phy_init_seq, - .mode = USB_PERIPHERAL, - .otg_control = OTG_PHY_CONTROL, +static struct attribute *tma300_properties_attrs[] = { + &tma300_vkeys_attr.attr, + NULL }; -struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = { -#ifdef CONFIG_SERIAL_MSM_CONSOLE - [49] = { /* UART2 RFR */ - .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN | - GPIOMUX_FUNC_2 | GPIOMUX_VALID, +static struct attribute_group tma300_properties_attr_group = { + .attrs = tma300_properties_attrs, +}; + +static struct kobject *properties_kobj; +static struct regulator_bulk_data cyttsp_regs[] = { + { .supply = "ldo8", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "ldo15", .min_uV = 3050000, .max_uV = 3100000 }, +}; + +#define CYTTSP_TS_GPIO_IRQ 150 +static int cyttsp_platform_init(struct i2c_client *client) +{ + int rc = -EINVAL; + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(cyttsp_regs), cyttsp_regs); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(cyttsp_regs), cyttsp_regs); + + if (rc) { + pr_err("%s: could not set regulator voltages: %d\n", __func__, + rc); + goto regs_free; + } + + rc = regulator_bulk_enable(ARRAY_SIZE(cyttsp_regs), cyttsp_regs); + + if (rc) { + pr_err("%s: could not enable regulators: %d\n", __func__, rc); + goto regs_free; + } + + /* check this device active by reading first byte/register */ + rc = i2c_smbus_read_byte_data(client, 0x01); + if (rc < 0) { + pr_err("%s: i2c sanity check failed\n", __func__); + goto regs_disable; + } + + rc = gpio_tlmm_config(GPIO_CFG(CYTTSP_TS_GPIO_IRQ, 0, GPIO_CFG_INPUT, + GPIO_CFG_PULL_UP, GPIO_CFG_6MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: Could not configure gpio %d\n", + __func__, CYTTSP_TS_GPIO_IRQ); + goto regs_disable; + } + + /* virtual keys */ + tma300_vkeys_attr.attr.name = "virtualkeys.cyttsp-i2c"; + properties_kobj = kobject_create_and_add("board_properties", + NULL); + if (properties_kobj) + rc = sysfs_create_group(properties_kobj, + &tma300_properties_attr_group); + if (!properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", + __func__); + + return CY_OK; + +regs_disable: + regulator_bulk_disable(ARRAY_SIZE(cyttsp_regs), cyttsp_regs); +regs_free: + regulator_bulk_free(ARRAY_SIZE(cyttsp_regs), cyttsp_regs); +out: + return rc; +} + +/* TODO: Put the regulator to LPM / HPM in suspend/resume*/ +static int cyttsp_platform_suspend(struct i2c_client *client) +{ + msleep(20); + + return CY_OK; +} + +static int cyttsp_platform_resume(struct i2c_client *client) +{ + /* add any special code to strobe a wakeup pin or chip reset */ + mdelay(10); + + return CY_OK; +} + +static struct cyttsp_platform_data cyttsp_data = { + .fw_fname = "cyttsp_7630_fluid.hex", + .panel_maxx = 479, + .panel_maxy = 799, + .disp_maxx = 469, + .disp_maxy = 799, + .disp_minx = 10, + .disp_miny = 0, + .flags = 0, + .gen = CY_GEN3, /* or */ + .use_st = CY_USE_ST, + .use_mt = CY_USE_MT, + .use_hndshk = CY_SEND_HNDSHK, + .use_trk_id = CY_USE_TRACKING_ID, + .use_sleep = CY_USE_DEEP_SLEEP_SEL | CY_USE_LOW_POWER_SEL, + .use_gestures = CY_USE_GESTURES, + /* activate up to 4 groups + * and set active distance + */ + .gest_set = CY_GEST_GRP1 | CY_GEST_GRP2 | + CY_GEST_GRP3 | CY_GEST_GRP4 | + CY_ACT_DIST, + /* change act_intrvl to customize the Active power state + * scanning/processing refresh interval for Operating mode + */ + .act_intrvl = CY_ACT_INTRVL_DFLT, + /* change tch_tmout to customize the touch timeout for the + * Active power state for Operating mode + */ + .tch_tmout = CY_TCH_TMOUT_DFLT, + /* change lp_intrvl to customize the Low Power power state + * scanning/processing refresh interval for Operating mode + */ + .lp_intrvl = CY_LP_INTRVL_DFLT, + .resume = cyttsp_platform_resume, + .suspend = cyttsp_platform_suspend, + .init = cyttsp_platform_init, + .sleep_gpio = -1, + .resout_gpio = -1, + .irq_gpio = CYTTSP_TS_GPIO_IRQ, + .correct_fw_ver = 2, +}; + +static int pm8058_pwm_config(struct pwm_device *pwm, int ch, int on) +{ + struct pm_gpio pwm_gpio_config = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_2, + }; + int rc = -EINVAL; + int id, mode, max_mA; + + id = mode = max_mA = 0; + switch (ch) { + case 0: + case 1: + case 2: + if (on) { + id = 24 + ch; + rc = pm8xxx_gpio_config(PM8058_GPIO_PM_TO_SYS(id - 1), + &pwm_gpio_config); + if (rc) + pr_err("%s: pm8xxx_gpio_config(%d): rc=%d\n", + __func__, id, rc); + } + break; + + case 3: + id = PM_PWM_LED_KPD; + mode = PM_PWM_CONF_DTEST3; + max_mA = 200; + break; + + case 4: + id = PM_PWM_LED_0; + mode = PM_PWM_CONF_PWM1; + max_mA = 40; + break; + + case 5: + id = PM_PWM_LED_2; + mode = PM_PWM_CONF_PWM2; + max_mA = 40; + break; + + case 6: + id = PM_PWM_LED_FLASH; + mode = PM_PWM_CONF_DTEST3; + max_mA = 200; + break; + + default: + break; + } + + if (ch >= 3 && ch <= 6) { + if (!on) { + mode = PM_PWM_CONF_NONE; + max_mA = 0; + } + rc = pm8058_pwm_config_led(pwm, id, mode, max_mA); + if (rc) + pr_err("%s: pm8058_pwm_config_led(ch=%d): rc=%d\n", + __func__, ch, rc); + } + + return rc; +} + +static int pm8058_pwm_enable(struct pwm_device *pwm, int ch, int on) +{ + int rc; + + switch (ch) { + case 7: + rc = pm8058_pwm_set_dtest(pwm, on); + if (rc) + pr_err("%s: pwm_set_dtest(%d): rc=%d\n", + __func__, on, rc); + break; + default: + rc = -EINVAL; + break; + } + return rc; +} + +static const unsigned int fluid_keymap[] = { + KEY(0, 0, KEY_7), + KEY(0, 1, KEY_ENTER), + KEY(0, 2, KEY_UP), + /* drop (0,3) as it always shows up in pair with(0,2) */ + KEY(0, 4, KEY_DOWN), + + KEY(1, 0, KEY_CAMERA_SNAPSHOT), + KEY(1, 1, KEY_SELECT), + KEY(1, 2, KEY_1), + KEY(1, 3, KEY_VOLUMEUP), + KEY(1, 4, KEY_VOLUMEDOWN), +}; + +static const unsigned int surf_keymap[] = { + KEY(0, 0, KEY_7), + KEY(0, 1, KEY_DOWN), + KEY(0, 2, KEY_UP), + KEY(0, 3, KEY_RIGHT), + KEY(0, 4, KEY_ENTER), + KEY(0, 5, KEY_L), + KEY(0, 6, KEY_BACK), + KEY(0, 7, KEY_M), + + KEY(1, 0, KEY_LEFT), + KEY(1, 1, KEY_SEND), + KEY(1, 2, KEY_1), + KEY(1, 3, KEY_4), + KEY(1, 4, KEY_CLEAR), + KEY(1, 5, KEY_MSDOS), + KEY(1, 6, KEY_SPACE), + KEY(1, 7, KEY_COMMA), + + KEY(2, 0, KEY_6), + KEY(2, 1, KEY_5), + KEY(2, 2, KEY_8), + KEY(2, 3, KEY_3), + KEY(2, 4, KEY_NUMERIC_STAR), + KEY(2, 5, KEY_UP), + KEY(2, 6, KEY_DOWN), /* SYN */ + KEY(2, 7, KEY_LEFTSHIFT), + + KEY(3, 0, KEY_9), + KEY(3, 1, KEY_NUMERIC_POUND), + KEY(3, 2, KEY_0), + KEY(3, 3, KEY_2), + KEY(3, 4, KEY_SLEEP), + KEY(3, 5, KEY_F1), + KEY(3, 6, KEY_F2), + KEY(3, 7, KEY_F3), + + KEY(4, 0, KEY_BACK), + KEY(4, 1, KEY_HOME), + KEY(4, 2, KEY_MENU), + KEY(4, 3, KEY_VOLUMEUP), + KEY(4, 4, KEY_VOLUMEDOWN), + KEY(4, 5, KEY_F4), + KEY(4, 6, KEY_F5), + KEY(4, 7, KEY_F6), + + KEY(5, 0, KEY_R), + KEY(5, 1, KEY_T), + KEY(5, 2, KEY_Y), + KEY(5, 3, KEY_LEFTALT), + KEY(5, 4, KEY_KPENTER), + KEY(5, 5, KEY_Q), + KEY(5, 6, KEY_W), + KEY(5, 7, KEY_E), + + KEY(6, 0, KEY_F), + KEY(6, 1, KEY_G), + KEY(6, 2, KEY_H), + KEY(6, 3, KEY_CAPSLOCK), + KEY(6, 4, KEY_PAGEUP), + KEY(6, 5, KEY_A), + KEY(6, 6, KEY_S), + KEY(6, 7, KEY_D), + + KEY(7, 0, KEY_V), + KEY(7, 1, KEY_B), + KEY(7, 2, KEY_N), + KEY(7, 3, KEY_MENU), /* REVISIT - SYM */ + KEY(7, 4, KEY_PAGEDOWN), + KEY(7, 5, KEY_Z), + KEY(7, 6, KEY_X), + KEY(7, 7, KEY_C), + + KEY(8, 0, KEY_P), + KEY(8, 1, KEY_J), + KEY(8, 2, KEY_K), + KEY(8, 3, KEY_INSERT), + KEY(8, 4, KEY_LINEFEED), + KEY(8, 5, KEY_U), + KEY(8, 6, KEY_I), + KEY(8, 7, KEY_O), + + KEY(9, 0, KEY_4), + KEY(9, 1, KEY_5), + KEY(9, 2, KEY_6), + KEY(9, 3, KEY_7), + KEY(9, 4, KEY_8), + KEY(9, 5, KEY_1), + KEY(9, 6, KEY_2), + KEY(9, 7, KEY_3), + + KEY(10, 0, KEY_F7), + KEY(10, 1, KEY_F8), + KEY(10, 2, KEY_F9), + KEY(10, 3, KEY_F10), + KEY(10, 4, KEY_FN), + KEY(10, 5, KEY_9), + KEY(10, 6, KEY_0), + KEY(10, 7, KEY_DOT), + + KEY(11, 0, KEY_LEFTCTRL), + KEY(11, 1, KEY_F11), /* START */ + KEY(11, 2, KEY_ENTER), + KEY(11, 3, KEY_SEARCH), + KEY(11, 4, KEY_DELETE), + KEY(11, 5, KEY_RIGHT), + KEY(11, 6, KEY_LEFT), + KEY(11, 7, KEY_RIGHTSHIFT), +}; + +static struct matrix_keymap_data surf_keymap_data = { + .keymap_size = ARRAY_SIZE(surf_keymap), + .keymap = surf_keymap, +}; + +static struct pm8xxx_keypad_platform_data surf_keypad_data = { + .input_name = "surf_keypad", + .input_phys_device = "surf_keypad/input0", + .num_rows = 12, + .num_cols = 8, + .rows_gpio_start = PM8058_GPIO_PM_TO_SYS(8), + .cols_gpio_start = PM8058_GPIO_PM_TO_SYS(0), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &surf_keymap_data, +}; + +static struct matrix_keymap_data fluid_keymap_data = { + .keymap_size = ARRAY_SIZE(fluid_keymap), + .keymap = fluid_keymap, +}; + +static struct pm8xxx_keypad_platform_data fluid_keypad_data = { + .input_name = "fluid-keypad", + .input_phys_device = "fluid-keypad/input0", + .num_rows = 5, + .num_cols = 5, + .rows_gpio_start = PM8058_GPIO_PM_TO_SYS(8), + .cols_gpio_start = PM8058_GPIO_PM_TO_SYS(0), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &fluid_keymap_data, +}; + +static struct pm8058_pwm_pdata pm8058_pwm_data = { + .config = pm8058_pwm_config, + .enable = pm8058_pwm_enable, +}; + +static struct pmic8058_led pmic8058_ffa_leds[] = { + [0] = { + .name = "keyboard-backlight", + .max_brightness = 15, + .id = PMIC8058_ID_LED_KB_LIGHT, }, - [50] = { /* UART2 CTS */ - .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN | - GPIOMUX_FUNC_2 | GPIOMUX_VALID, +}; + +static struct pmic8058_leds_platform_data pm8058_ffa_leds_data = { + .num_leds = ARRAY_SIZE(pmic8058_ffa_leds), + .leds = pmic8058_ffa_leds, +}; + +static struct pmic8058_led pmic8058_surf_leds[] = { + [0] = { + .name = "keyboard-backlight", + .max_brightness = 15, + .id = PMIC8058_ID_LED_KB_LIGHT, }, - [51] = { /* UART2 RX */ - .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN | - GPIOMUX_FUNC_2 | GPIOMUX_VALID, + [1] = { + .name = "voice:red", + .max_brightness = 20, + .id = PMIC8058_ID_LED_0, }, - [52] = { /* UART2 TX */ - .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN | - GPIOMUX_FUNC_2 | GPIOMUX_VALID, + [2] = { + .name = "wlan:green", + .max_brightness = 20, + .id = PMIC8058_ID_LED_2, + }, +}; + +static struct pmic8058_leds_platform_data pm8058_surf_leds_data = { + .num_leds = ARRAY_SIZE(pmic8058_surf_leds), + .leds = pmic8058_surf_leds, +}; + +static struct pmic8058_led pmic8058_fluid_leds[] = { + [0] = { + .name = "keyboard-backlight", + .max_brightness = 15, + .id = PMIC8058_ID_LED_KB_LIGHT, + }, + [1] = { + .name = "flash:led_0", + .max_brightness = 15, + .id = PMIC8058_ID_FLASH_LED_0, + }, + [2] = { + .name = "flash:led_1", + .max_brightness = 15, + .id = PMIC8058_ID_FLASH_LED_1, + }, +}; + +static struct pmic8058_leds_platform_data pm8058_fluid_leds_data = { + .num_leds = ARRAY_SIZE(pmic8058_fluid_leds), + .leds = pmic8058_fluid_leds, +}; + +static struct pm8xxx_irq_platform_data pm8xxx_irq_pdata = { + .irq_base = PMIC8058_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(PMIC_GPIO_INT), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_gpio_platform_data pm8xxx_gpio_pdata = { + .gpio_base = PM8058_GPIO_PM_TO_SYS(0), +}; + +static struct pm8xxx_mpp_platform_data pm8xxx_mpp_pdata = { + .mpp_base = PM8058_MPP_PM_TO_SYS(0), +}; + +static struct pm8058_platform_data pm8058_7x30_data = { + .irq_pdata = &pm8xxx_irq_pdata, + .gpio_pdata = &pm8xxx_gpio_pdata, + .mpp_pdata = &pm8xxx_mpp_pdata, + .pwm_pdata = &pm8058_pwm_data, +}; + +#ifdef CONFIG_MSM_SSBI +static struct msm_ssbi_platform_data msm7x30_ssbi_pm8058_pdata = { + .rsl_id = "D:PMIC_SSBI", + .controller_type = MSM_SBI_CTRL_SSBI2, + .slave = { + .name = "pm8058-core", + .platform_data = &pm8058_7x30_data, + }, +}; +#endif + +static struct i2c_board_info cy8info[] __initdata = { + { + I2C_BOARD_INFO(CY_I2C_NAME, 0x24), + .platform_data = &cyttsp_data, +#ifndef CY_USE_TIMER + .irq = MSM_GPIO_TO_INT(CYTTSP_TS_GPIO_IRQ), +#endif /* CY_USE_TIMER */ + }, +}; + +#ifdef CONFIG_MSM_CAMERA_V4L2 +static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = { + { + .csid_core = 0, + .is_csic = 1, + .is_vpe = 1, + .ioclk = { + .vfe_clk_rate = 153600000, + }, + }, + { + .csid_core = 0, + .is_vpe = 1, + .ioclk = { + .vfe_clk_rate = 153600000, + }, + }, +}; + +static struct camera_vreg_t msm_7x30_back_cam_vreg[] = { + {"gp2", REG_LDO, 2600000, 2600000, -1}, + {"lvsw1", REG_VS, 0, 0, 0}, +}; + +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + /* RST */ + GPIO_CFG(0, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT2 */ + GPIO_CFG(2, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT3 */ + GPIO_CFG(3, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT4 */ + GPIO_CFG(4, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT5 */ + GPIO_CFG(5, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT6 */ + GPIO_CFG(6, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT7 */ + GPIO_CFG(7, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT8 */ + GPIO_CFG(8, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT9 */ + GPIO_CFG(9, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT10 */ + GPIO_CFG(10, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT11 */ + GPIO_CFG(11, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* PCLK */ + GPIO_CFG(12, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* HSYNC_IN */ + GPIO_CFG(13, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* VSYNC_IN */ + GPIO_CFG(14, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* MCLK */ + GPIO_CFG(15, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + /* RST */ + GPIO_CFG(0, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT2 */ + GPIO_CFG(2, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT3 */ + GPIO_CFG(3, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT4 */ + GPIO_CFG(4, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT5 */ + GPIO_CFG(5, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT6 */ + GPIO_CFG(6, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT7 */ + GPIO_CFG(7, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT8 */ + GPIO_CFG(8, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT9 */ + GPIO_CFG(9, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT10 */ + GPIO_CFG(10, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT11 */ + GPIO_CFG(11, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* PCLK */ + GPIO_CFG(12, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* HSYNC_IN */ + GPIO_CFG(13, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* VSYNC_IN */ + GPIO_CFG(14, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* MCLK */ + GPIO_CFG(15, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; + +static struct gpio msm7x30_back_cam_gpio[] = { + {0, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct msm_gpio_set_tbl msm7x30_back_cam_gpio_set_tbl[] = { + {0, GPIOF_OUT_INIT_LOW, 1000}, + {0, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_camera_gpio_conf msm_7x30_back_cam_gpio_conf = { + .cam_gpio_req_tbl = msm7x30_back_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(msm7x30_back_cam_gpio), + .cam_gpio_set_tbl = msm7x30_back_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(msm7x30_back_cam_gpio_set_tbl), + .camera_off_table = camera_off_gpio_table, + .camera_off_table_size = ARRAY_SIZE(camera_off_gpio_table), + .camera_on_table = camera_on_gpio_table, + .camera_on_table_size = ARRAY_SIZE(camera_on_gpio_table), + .gpio_no_mux = 1, +}; + +static struct msm_camera_sensor_flash_data flash_vx6953 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_vx6953 = { + .mount_angle = 0, + .cam_vreg = msm_7x30_back_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_7x30_back_cam_vreg), + .gpio_conf = &msm_7x30_back_cam_gpio_conf, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_vx6953_data = { + .sensor_name = "vx6953", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_vx6953, + .sensor_platform_info = &sensor_board_info_vx6953, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, +}; + +static struct platform_device msm_camera_server = { + .name = "msm_cam_server", + .id = 0, +}; + +void __init msm7x30_init_cam(void) +{ + platform_device_register(&msm_camera_server); + platform_device_register(&msm_device_csic0); + platform_device_register(&msm_device_vfe); + platform_device_register(&msm_device_vpe); +} + +#ifdef CONFIG_I2C +static struct i2c_board_info msm_camera_boardinfo[] = { + { + I2C_BOARD_INFO("vx6953", 0x20), + .platform_data = &msm_camera_sensor_vx6953_data, + }, +}; +#endif +#else +static struct i2c_board_info msm_camera_boardinfo[] __initdata = { +#ifdef CONFIG_MT9D112 + { + I2C_BOARD_INFO("mt9d112", 0x78 >> 1), }, #endif +#ifdef CONFIG_WEBCAM_OV9726 + { + I2C_BOARD_INFO("ov9726", 0x10), + }, +#endif +#ifdef CONFIG_S5K3E2FX + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, +#endif +#ifdef CONFIG_MT9P012 + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, +#endif +#ifdef CONFIG_VX6953 + { + I2C_BOARD_INFO("vx6953", 0x20), + }, +#endif +#ifdef CONFIG_MT9E013 + { + I2C_BOARD_INFO("mt9e013", 0x6C >> 2), + }, +#endif +#ifdef CONFIG_SN12M0PZ + { + I2C_BOARD_INFO("sn12m0pz", 0x34 >> 1), + }, +#endif +#if defined(CONFIG_MT9T013) || defined(CONFIG_SENSORS_MT9T013) + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +#endif + }; +#ifdef CONFIG_MSM_CAMERA +#define CAM_STNDBY 143 +static uint32_t camera_off_vcm_gpio_table[] = { +GPIO_CFG(1, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* VCM */ +}; + +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + /* RST */ + GPIO_CFG(0, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT2 */ + GPIO_CFG(2, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT3 */ + GPIO_CFG(3, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT4 */ + GPIO_CFG(4, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT5 */ + GPIO_CFG(5, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT6 */ + GPIO_CFG(6, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT7 */ + GPIO_CFG(7, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT8 */ + GPIO_CFG(8, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT9 */ + GPIO_CFG(9, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT10 */ + GPIO_CFG(10, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT11 */ + GPIO_CFG(11, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* PCLK */ + GPIO_CFG(12, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* HSYNC_IN */ + GPIO_CFG(13, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* VSYNC_IN */ + GPIO_CFG(14, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* MCLK */ + GPIO_CFG(15, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; + +static uint32_t camera_on_vcm_gpio_table[] = { +GPIO_CFG(1, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA), /* VCM */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + /* RST */ + GPIO_CFG(0, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT2 */ + GPIO_CFG(2, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT3 */ + GPIO_CFG(3, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT4 */ + GPIO_CFG(4, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT5 */ + GPIO_CFG(5, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT6 */ + GPIO_CFG(6, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT7 */ + GPIO_CFG(7, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT8 */ + GPIO_CFG(8, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT9 */ + GPIO_CFG(9, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT10 */ + GPIO_CFG(10, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* DAT11 */ + GPIO_CFG(11, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* PCLK */ + GPIO_CFG(12, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* HSYNC_IN */ + GPIO_CFG(13, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* VSYNC_IN */ + GPIO_CFG(14, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* MCLK */ + GPIO_CFG(15, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; + +static uint32_t camera_off_gpio_fluid_table[] = { + /* FLUID: CAM_VGA_RST_N */ + GPIO_CFG(31, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* FLUID: CAMIF_STANDBY */ + GPIO_CFG(CAM_STNDBY, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA) +}; + +static uint32_t camera_on_gpio_fluid_table[] = { + /* FLUID: CAM_VGA_RST_N */ + GPIO_CFG(31, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + /* FLUID: CAMIF_STANDBY */ + GPIO_CFG(CAM_STNDBY, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA) +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} +static int config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + + if (adie_get_detected_codec_type() != TIMPANI_ID) + /* GPIO1 is shared also used in Timpani RF card so + only configure it for non-Timpani RF card */ + config_gpio_table(camera_on_vcm_gpio_table, + ARRAY_SIZE(camera_on_vcm_gpio_table)); + + if (machine_is_msm7x30_fluid()) { + config_gpio_table(camera_on_gpio_fluid_table, + ARRAY_SIZE(camera_on_gpio_fluid_table)); + /* FLUID: turn on 5V booster */ + gpio_set_value( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_FLASH_BOOST_ENABLE), 1); + /* FLUID: drive high to put secondary sensor to STANDBY */ + gpio_set_value(CAM_STNDBY, 1); + } + return 0; +} + +static void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); + + if (adie_get_detected_codec_type() != TIMPANI_ID) + /* GPIO1 is shared also used in Timpani RF card so + only configure it for non-Timpani RF card */ + config_gpio_table(camera_off_vcm_gpio_table, + ARRAY_SIZE(camera_off_vcm_gpio_table)); + + if (machine_is_msm7x30_fluid()) { + config_gpio_table(camera_off_gpio_fluid_table, + ARRAY_SIZE(camera_off_gpio_fluid_table)); + /* FLUID: turn off 5V booster */ + gpio_set_value( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_FLASH_BOOST_ENABLE), 0); + } +} + +struct resource msm_camera_resources[] = { + { + .start = 0xA6000000, + .end = 0xA6000000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VFE, + .end = INT_VFE, + .flags = IORESOURCE_IRQ, + }, + { + .flags = IORESOURCE_DMA, + } +}; + +struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.camifpadphy = 0xAB000000, + .ioext.camifpadsz = 0x00000400, + .ioext.csiphy = 0xA6100000, + .ioext.csisz = 0x00000400, + .ioext.csiirq = INT_CSI, + .ioclk.mclk_clk_rate = 24000000, + .ioclk.vfe_clk_rate = 147456000, +}; + +static struct msm_camera_sensor_flash_src msm_flash_src_pwm = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_PWM, + ._fsrc.pwm_src.freq = 1000, + ._fsrc.pwm_src.max_load = 300, + ._fsrc.pwm_src.low_load = 30, + ._fsrc.pwm_src.high_load = 100, + ._fsrc.pwm_src.channel = 7, +}; + +#ifdef CONFIG_MT9D112 +static struct msm_camera_sensor_flash_data flash_mt9d112 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = { + .sensor_name = "mt9d112", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9d112, + .csi_if = 0 +}; + +static struct platform_device msm_camera_sensor_mt9d112 = { + .name = "msm_camera_mt9d112", + .dev = { + .platform_data = &msm_camera_sensor_mt9d112_data, + }, +}; +#endif + +#ifdef CONFIG_WEBCAM_OV9726 + +static struct msm_camera_sensor_platform_info ov9726_sensor_7630_info = { + .mount_angle = 90 +}; + +static struct msm_camera_sensor_flash_data flash_ov9726 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm +}; +static struct msm_camera_sensor_info msm_camera_sensor_ov9726_data = { + .sensor_name = "ov9726", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_ov9726, + .sensor_platform_info = &ov9726_sensor_7630_info, + .csi_if = 1 +}; +struct platform_device msm_camera_sensor_ov9726 = { + .name = "msm_camera_ov9726", + .dev = { + .platform_data = &msm_camera_sensor_ov9726_data, + }, +}; +#endif + +#ifdef CONFIG_S5K3E2FX +static struct msm_camera_sensor_flash_data flash_s5k3e2fx = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { + .sensor_name = "s5k3e2fx", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_s5k3e2fx, + .csi_if = 0 +}; + +static struct platform_device msm_camera_sensor_s5k3e2fx = { + .name = "msm_camera_s5k3e2fx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3e2fx_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012 +static struct msm_camera_sensor_flash_data flash_mt9p012 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { + .sensor_name = "mt9p012", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 1, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9p012, + .csi_if = 0 +}; + +static struct platform_device msm_camera_sensor_mt9p012 = { + .name = "msm_camera_mt9p012", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_data, + }, +}; +#endif + +#ifdef CONFIG_MT9E013 +static struct msm_camera_sensor_platform_info mt9e013_sensor_7630_info = { + .mount_angle = 0 +}; + +static struct msm_camera_sensor_flash_data flash_mt9e013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = { + .sensor_name = "mt9e013", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 1, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9e013, + .sensor_platform_info = &mt9e013_sensor_7630_info, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_mt9e013 = { + .name = "msm_camera_mt9e013", + .dev = { + .platform_data = &msm_camera_sensor_mt9e013_data, + }, +}; +#endif + +#ifdef CONFIG_VX6953 +static struct msm_camera_sensor_platform_info vx6953_sensor_7630_info = { + .mount_angle = 0 +}; + +static struct msm_camera_sensor_flash_data flash_vx6953 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm +}; +static struct msm_camera_sensor_info msm_camera_sensor_vx6953_data = { + .sensor_name = "vx6953", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .sensor_platform_info = &vx6953_sensor_7630_info, + .flash_data = &flash_vx6953, + .csi_if = 1 +}; +static struct platform_device msm_camera_sensor_vx6953 = { + .name = "msm_camera_vx6953", + .dev = { + .platform_data = &msm_camera_sensor_vx6953_data, + }, +}; +#endif + +#ifdef CONFIG_SN12M0PZ +static struct msm_camera_sensor_flash_src msm_flash_src_current_driver = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_CURRENT_DRIVER, + ._fsrc.current_driver_src.low_current = 210, + ._fsrc.current_driver_src.high_current = 700, + ._fsrc.current_driver_src.driver_channel = &pm8058_fluid_leds_data, +}; + +static struct msm_camera_sensor_flash_data flash_sn12m0pz = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_current_driver +}; +static struct msm_camera_sensor_info msm_camera_sensor_sn12m0pz_data = { + .sensor_name = "sn12m0pz", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 1, + .pdata = &msm_camera_device_data, + .flash_data = &flash_sn12m0pz, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .csi_if = 0 +}; + +static struct platform_device msm_camera_sensor_sn12m0pz = { + .name = "msm_camera_sn12m0pz", + .dev = { + .platform_data = &msm_camera_sensor_sn12m0pz_data, + }, +}; +#endif + +#ifdef CONFIG_MT9T013 +static struct msm_camera_sensor_flash_data flash_mt9t013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src_pwm +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { + .sensor_name = "mt9t013", + .sensor_reset = 0, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9t013, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_mt9t013 = { + .name = "msm_camera_mt9t013", + .dev = { + .platform_data = &msm_camera_sensor_mt9t013_data, + }, +}; +#endif + +#ifdef CONFIG_MSM_VPE +static struct resource msm_vpe_resources[] = { + { + .start = 0xAD200000, + .end = 0xAD200000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VPE, + .end = INT_VPE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_vpe_device = { + .name = "msm_vpe", + .id = 0, + .num_resources = ARRAY_SIZE(msm_vpe_resources), + .resource = msm_vpe_resources, +}; +#endif + +#endif /*CONFIG_MSM_CAMERA*/ +#endif + +#ifdef CONFIG_MSM_GEMINI +static struct resource msm_gemini_resources[] = { + { + .start = 0xA3A00000, + .end = 0xA3A00000 + 0x0150 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_JPEG, + .end = INT_JPEG, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_gemini_device = { + .name = "msm_gemini", + .resource = msm_gemini_resources, + .num_resources = ARRAY_SIZE(msm_gemini_resources), +}; +#endif + +#ifdef CONFIG_MSM7KV2_AUDIO +static uint32_t audio_pamp_gpio_config = + GPIO_CFG(82, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + +static uint32_t audio_fluid_icodec_tx_config = + GPIO_CFG(85, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + +static int __init snddev_poweramp_gpio_init(void) +{ + int rc; + + pr_info("snddev_poweramp_gpio_init \n"); + rc = gpio_tlmm_config(audio_pamp_gpio_config, GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, audio_pamp_gpio_config, rc); + } + return rc; +} + +void msm_snddev_tx_route_config(void) +{ + int rc; + + pr_debug("%s()\n", __func__); + + if (machine_is_msm7x30_fluid()) { + rc = gpio_tlmm_config(audio_fluid_icodec_tx_config, + GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, audio_fluid_icodec_tx_config, rc); + } else + gpio_set_value(85, 0); + } +} + +void msm_snddev_tx_route_deconfig(void) +{ + int rc; + + pr_debug("%s()\n", __func__); + + if (machine_is_msm7x30_fluid()) { + rc = gpio_tlmm_config(audio_fluid_icodec_tx_config, + GPIO_CFG_DISABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, audio_fluid_icodec_tx_config, rc); + } + } +} + +void msm_snddev_poweramp_on(void) +{ + gpio_set_value(82, 1); /* enable spkr poweramp */ + pr_info("%s: power on amplifier\n", __func__); +} + +void msm_snddev_poweramp_off(void) +{ + gpio_set_value(82, 0); /* disable spkr poweramp */ + pr_info("%s: power off amplifier\n", __func__); +} + +static struct regulator_bulk_data snddev_regs[] = { + { .supply = "gp4", .min_uV = 2600000, .max_uV = 2600000 }, + { .supply = "ncp", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +static int __init snddev_hsed_voltage_init(void) +{ + int rc; + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(snddev_regs), snddev_regs); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(snddev_regs), snddev_regs); + + if (rc) { + pr_err("%s: could not set regulator voltages: %d\n", + __func__, rc); + goto regs_free; + } + + return 0; + +regs_free: + regulator_bulk_free(ARRAY_SIZE(snddev_regs), snddev_regs); +out: + return rc; +} + + +void msm_snddev_hsed_voltage_on(void) +{ + int rc = regulator_bulk_enable(ARRAY_SIZE(snddev_regs), snddev_regs); + + if (rc) + pr_err("%s: could not enable regulators: %d\n", __func__, rc); +} + +void msm_snddev_hsed_voltage_off(void) +{ + int rc = regulator_bulk_disable(ARRAY_SIZE(snddev_regs), snddev_regs); + + if (rc) { + pr_err("%s: could not disable regulators: %d\n", __func__, rc); + } +} + +static unsigned aux_pcm_gpio_on[] = { + GPIO_CFG(138, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_DOUT */ + GPIO_CFG(139, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_DIN */ + GPIO_CFG(140, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_SYNC */ + GPIO_CFG(141, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_CLK */ +}; + +static int __init aux_pcm_gpio_init(void) +{ + int pin, rc; + + pr_info("aux_pcm_gpio_init \n"); + for (pin = 0; pin < ARRAY_SIZE(aux_pcm_gpio_on); pin++) { + rc = gpio_tlmm_config(aux_pcm_gpio_on[pin], + GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, aux_pcm_gpio_on[pin], rc); + } + } + return rc; +} + +static struct msm_gpio mi2s_clk_gpios[] = { + { GPIO_CFG(145, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_SCLK"}, + { GPIO_CFG(144, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_WS"}, + { GPIO_CFG(120, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_MCLK_A"}, +}; + +static struct msm_gpio mi2s_rx_data_lines_gpios[] = { + { GPIO_CFG(121, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_DATA_SD0_A"}, + { GPIO_CFG(122, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_DATA_SD1_A"}, + { GPIO_CFG(123, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_DATA_SD2_A"}, + { GPIO_CFG(146, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_DATA_SD3"}, +}; + +static struct msm_gpio mi2s_tx_data_lines_gpios[] = { + { GPIO_CFG(146, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MI2S_DATA_SD3"}, +}; + +int mi2s_config_clk_gpio(void) +{ + int rc = 0; + + rc = msm_gpios_request_enable(mi2s_clk_gpios, + ARRAY_SIZE(mi2s_clk_gpios)); + if (rc) { + pr_err("%s: enable mi2s clk gpios failed\n", + __func__); + return rc; + } + return 0; +} + +int mi2s_unconfig_data_gpio(u32 direction, u8 sd_line_mask) +{ + int i, rc = 0; + sd_line_mask &= MI2S_SD_LINE_MASK; + + switch (direction) { + case DIR_TX: + msm_gpios_disable_free(mi2s_tx_data_lines_gpios, 1); + break; + case DIR_RX: + i = 0; + while (sd_line_mask) { + if (sd_line_mask & 0x1) + msm_gpios_disable_free( + mi2s_rx_data_lines_gpios + i , 1); + sd_line_mask = sd_line_mask >> 1; + i++; + } + break; + default: + pr_err("%s: Invaild direction direction = %u\n", + __func__, direction); + rc = -EINVAL; + break; + } + return rc; +} + +int mi2s_config_data_gpio(u32 direction, u8 sd_line_mask) +{ + int i , rc = 0; + u8 sd_config_done_mask = 0; + + sd_line_mask &= MI2S_SD_LINE_MASK; + + switch (direction) { + case DIR_TX: + if ((sd_line_mask & MI2S_SD_0) || (sd_line_mask & MI2S_SD_1) || + (sd_line_mask & MI2S_SD_2) || !(sd_line_mask & MI2S_SD_3)) { + pr_err("%s: can not use SD0 or SD1 or SD2 for TX" + ".only can use SD3. sd_line_mask = 0x%x\n", + __func__ , sd_line_mask); + rc = -EINVAL; + } else { + rc = msm_gpios_request_enable(mi2s_tx_data_lines_gpios, + 1); + if (rc) + pr_err("%s: enable mi2s gpios for TX failed\n", + __func__); + } + break; + case DIR_RX: + i = 0; + while (sd_line_mask && (rc == 0)) { + if (sd_line_mask & 0x1) { + rc = msm_gpios_request_enable( + mi2s_rx_data_lines_gpios + i , 1); + if (rc) { + pr_err("%s: enable mi2s gpios for" + "RX failed. SD line = %s\n", + __func__, + (mi2s_rx_data_lines_gpios + i)->label); + mi2s_unconfig_data_gpio(DIR_RX, + sd_config_done_mask); + } else + sd_config_done_mask |= (1 << i); + } + sd_line_mask = sd_line_mask >> 1; + i++; + } + break; + default: + pr_err("%s: Invaild direction direction = %u\n", + __func__, direction); + rc = -EINVAL; + break; + } + return rc; +} + +int mi2s_unconfig_clk_gpio(void) +{ + msm_gpios_disable_free(mi2s_clk_gpios, ARRAY_SIZE(mi2s_clk_gpios)); + return 0; +} + +#endif /* CONFIG_MSM7KV2_AUDIO */ + +static int __init buses_init(void) +{ + if (gpio_tlmm_config(GPIO_CFG(PMIC_GPIO_INT, 1, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE)) + pr_err("%s: gpio_tlmm_config (gpio=%d) failed\n", + __func__, PMIC_GPIO_INT); + + if (machine_is_msm8x60_fluid()) + pm8058_7x30_data.keypad_pdata = &fluid_keypad_data; + else + pm8058_7x30_data.keypad_pdata = &surf_keypad_data; + + return 0; +} + +#define TIMPANI_RESET_GPIO 1 + +struct bahama_config_register{ + u8 reg; + u8 value; + u8 mask; +}; + +enum version{ + VER_1_0, + VER_2_0, + VER_UNSUPPORTED = 0xFF +}; + +static struct regulator *vreg_marimba_1; +static struct regulator *vreg_marimba_2; +static struct regulator *vreg_bahama; + +static struct msm_gpio timpani_reset_gpio_cfg[] = { +{ GPIO_CFG(TIMPANI_RESET_GPIO, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "timpani_reset"} }; + +static u8 read_bahama_ver(void) +{ + int rc; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA }; + u8 bahama_version; + + rc = marimba_read_bit_mask(&config, 0x00, &bahama_version, 1, 0x1F); + if (rc < 0) { + printk(KERN_ERR + "%s: version read failed: %d\n", + __func__, rc); + return rc; + } else { + printk(KERN_INFO + "%s: version read got: 0x%x\n", + __func__, bahama_version); + } + + switch (bahama_version) { + case 0x08: /* varient of bahama v1 */ + case 0x10: + case 0x00: + return VER_1_0; + case 0x09: /* variant of bahama v2 */ + return VER_2_0; + default: + return VER_UNSUPPORTED; + } +} + +static int config_timpani_reset(void) +{ + int rc; + + rc = msm_gpios_request_enable(timpani_reset_gpio_cfg, + ARRAY_SIZE(timpani_reset_gpio_cfg)); + if (rc < 0) { + printk(KERN_ERR + "%s: msm_gpios_request_enable failed (%d)\n", + __func__, rc); + } + return rc; +} + +static unsigned int msm_timpani_setup_power(void) +{ + int rc; + + rc = config_timpani_reset(); + if (rc < 0) + goto out; + + rc = regulator_enable(vreg_marimba_1); + if (rc) { + pr_err("%s: regulator_enable failed (%d)\n", __func__, rc); + goto out; + } + + rc = regulator_enable(vreg_marimba_2); + if (rc) { + pr_err("%s: regulator_enable failed (%d)\n", __func__, rc); + goto disable_marimba_1; + } + + rc = gpio_direction_output(TIMPANI_RESET_GPIO, 1); + if (rc < 0) { + pr_err("%s: gpio_direction_output failed (%d)\n", + __func__, rc); + msm_gpios_free(timpani_reset_gpio_cfg, + ARRAY_SIZE(timpani_reset_gpio_cfg)); + goto disable_marimba_2; + } + + return 0; + +disable_marimba_2: + regulator_disable(vreg_marimba_2); +disable_marimba_1: + regulator_disable(vreg_marimba_1); +out: + return rc; +}; + +static void msm_timpani_shutdown_power(void) +{ + int rc; + + rc = regulator_disable(vreg_marimba_2); + if (rc) + pr_err("%s: regulator_disable failed (%d)\n", __func__, rc); + + rc = regulator_disable(vreg_marimba_1); + if (rc) + pr_err("%s: regulator_disable failed (%d)\n", __func__, rc); + + rc = gpio_direction_output(TIMPANI_RESET_GPIO, 0); + if (rc < 0) + pr_err("%s: gpio_direction_output failed (%d)\n", + __func__, rc); + + msm_gpios_free(timpani_reset_gpio_cfg, + ARRAY_SIZE(timpani_reset_gpio_cfg)); +}; + +static unsigned int msm_bahama_core_config(int type) +{ + int rc = 0; + + if (type == BAHAMA_ID) { + + int i; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA }; + + const struct bahama_config_register v20_init[] = { + /* reg, value, mask */ + { 0xF4, 0x84, 0xFF }, /* AREG */ + { 0xF0, 0x04, 0xFF } /* DREG */ + }; + + if (read_bahama_ver() == VER_2_0) { + for (i = 0; i < ARRAY_SIZE(v20_init); i++) { + u8 value = v20_init[i].value; + rc = marimba_write_bit_mask(&config, + v20_init[i].reg, + &value, + sizeof(v20_init[i].value), + v20_init[i].mask); + if (rc < 0) { + printk(KERN_ERR + "%s: reg %d write failed: %d\n", + __func__, v20_init[i].reg, rc); + return rc; + } + printk(KERN_INFO "%s: reg 0x%02x value 0x%02x" + " mask 0x%02x\n", + __func__, v20_init[i].reg, + v20_init[i].value, v20_init[i].mask); + } + } + } + printk(KERN_INFO "core type: %d\n", type); + + return rc; +} + +static unsigned int msm_bahama_setup_power(void) +{ + int rc = regulator_enable(vreg_bahama); + + if (rc) + pr_err("%s: regulator_enable failed (%d)\n", __func__, rc); + + return rc; +}; + +static unsigned int msm_bahama_shutdown_power(int value) +{ + int rc = 0; + + if (value != BAHAMA_ID) { + rc = regulator_disable(vreg_bahama); + + if (rc) + pr_err("%s: regulator_disable failed (%d)\n", + __func__, rc); + } + + return rc; +}; + +static struct msm_gpio marimba_svlte_config_clock[] = { + { GPIO_CFG(34, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "MARIMBA_SVLTE_CLOCK_ENABLE" }, +}; + +static unsigned int msm_marimba_gpio_config_svlte(int gpio_cfg_marimba) +{ + if (machine_is_msm8x55_svlte_surf() || + machine_is_msm8x55_svlte_ffa()) { + if (gpio_cfg_marimba) + gpio_set_value(GPIO_PIN + (marimba_svlte_config_clock->gpio_cfg), 1); + else + gpio_set_value(GPIO_PIN + (marimba_svlte_config_clock->gpio_cfg), 0); + } + + return 0; +}; + +static unsigned int msm_marimba_setup_power(void) +{ + int rc; + + rc = regulator_enable(vreg_marimba_1); + if (rc) { + pr_err("%s: regulator_enable failed (%d)\n", __func__, rc); + goto out; + } + + rc = regulator_enable(vreg_marimba_2); + if (rc) { + pr_err("%s: regulator_enable failed (%d)\n", __func__, rc); + goto disable_marimba_1; + } + + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa()) { + rc = msm_gpios_request_enable(marimba_svlte_config_clock, + ARRAY_SIZE(marimba_svlte_config_clock)); + if (rc < 0) { + pr_err("%s: msm_gpios_request_enable failed (%d)\n", + __func__, rc); + goto disable_marimba_2; + } + + rc = gpio_direction_output(GPIO_PIN + (marimba_svlte_config_clock->gpio_cfg), 0); + if (rc < 0) { + pr_err("%s: gpio_direction_output failed (%d)\n", + __func__, rc); + goto disable_marimba_2; + } + } + + return 0; + +disable_marimba_2: + regulator_disable(vreg_marimba_2); +disable_marimba_1: + regulator_disable(vreg_marimba_1); +out: + return rc; +}; + +static void msm_marimba_shutdown_power(void) +{ + int rc; + + rc = regulator_disable(vreg_marimba_2); + if (rc) + pr_err("%s: regulator_disable failed (%d)\n", __func__, rc); + + rc = regulator_disable(vreg_marimba_1); + if (rc) + pr_err("%s: regulator_disable failed (%d)\n", __func__, rc); +}; + +static int bahama_present(void) +{ + int id; + switch (id = adie_get_detected_connectivity_type()) { + case BAHAMA_ID: + return 1; + + case MARIMBA_ID: + return 0; + + case TIMPANI_ID: + default: + printk(KERN_ERR "%s: unexpected adie connectivity type: %d\n", + __func__, id); + return -ENODEV; + } +} + +struct regulator *fm_regulator; +static int fm_radio_setup(struct marimba_fm_platform_data *pdata) +{ + int rc, voltage; + uint32_t irqcfg; + const char *id = "FMPW"; + + int bahama_not_marimba = bahama_present(); + + if (bahama_not_marimba < 0) { + pr_warn("%s: bahama_present: %d\n", + __func__, bahama_not_marimba); + rc = -ENODEV; + goto out; + } + if (bahama_not_marimba) { + fm_regulator = regulator_get(NULL, "s3"); + voltage = 1800000; + } else { + fm_regulator = regulator_get(NULL, "s2"); + voltage = 1300000; + } + + if (IS_ERR(fm_regulator)) { + rc = PTR_ERR(fm_regulator); + pr_err("%s: regulator_get failed (%d)\n", __func__, rc); + goto out; + } + + rc = regulator_set_voltage(fm_regulator, voltage, voltage); + + if (rc) { + pr_err("%s: regulator_set_voltage failed (%d)\n", __func__, rc); + goto regulator_free; + } + + rc = regulator_enable(fm_regulator); + + if (rc) { + pr_err("%s: regulator_enable failed (%d)\n", __func__, rc); + goto regulator_free; + } + + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_DO, PMAPP_CLOCK_VOTE_ON); + + if (rc < 0) { + pr_err("%s: clock vote failed (%d)\n", __func__, rc); + goto regulator_disable; + } + + /*Request the Clock Using GPIO34/AP2MDM_MRMBCK_EN in case + of svlte*/ + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa()) { + rc = marimba_gpio_config(1); + if (rc < 0) { + pr_err("%s: clock enable for svlte : %d\n", + __func__, rc); + goto clock_devote; + } + } + irqcfg = GPIO_CFG(147, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA); + rc = gpio_tlmm_config(irqcfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", __func__, irqcfg, rc); + rc = -EIO; + goto gpio_deconfig; + + } + return 0; + +gpio_deconfig: + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa()) + marimba_gpio_config(0); +clock_devote: + pmapp_clock_vote(id, PMAPP_CLOCK_ID_DO, PMAPP_CLOCK_VOTE_OFF); +regulator_disable: + regulator_disable(fm_regulator); +regulator_free: + regulator_put(fm_regulator); + fm_regulator = NULL; +out: + return rc; +}; + +static void fm_radio_shutdown(struct marimba_fm_platform_data *pdata) +{ + int rc; + const char *id = "FMPW"; + uint32_t irqcfg = GPIO_CFG(147, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, + GPIO_CFG_2MA); + + int bahama_not_marimba = bahama_present(); + if (bahama_not_marimba == -1) { + pr_warn("%s: bahama_present: %d\n", + __func__, bahama_not_marimba); + return; + } + + rc = gpio_tlmm_config(irqcfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", __func__, irqcfg, rc); + } + if (!IS_ERR_OR_NULL(fm_regulator)) { + rc = regulator_disable(fm_regulator); + + if (rc) + pr_err("%s: return val: %d\n", __func__, rc); + + regulator_put(fm_regulator); + fm_regulator = NULL; + } + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_DO, + PMAPP_CLOCK_VOTE_OFF); + if (rc < 0) + pr_err("%s: clock_vote return val: %d\n", __func__, rc); + + /*Disable the Clock Using GPIO34/AP2MDM_MRMBCK_EN in case + of svlte*/ + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa()) { + rc = marimba_gpio_config(0); + if (rc < 0) + pr_err("%s: clock disable for svlte : %d\n", + __func__, rc); + } +} + +static struct marimba_fm_platform_data marimba_fm_pdata = { + .fm_setup = fm_radio_setup, + .fm_shutdown = fm_radio_shutdown, + .irq = MSM_GPIO_TO_INT(147), + .vreg_s2 = NULL, + .vreg_xo_out = NULL, + .is_fm_soc_i2s_master = false, + .config_i2s_gpio = NULL, +}; + + +/* Slave id address for FM/CDC/QMEMBIST + * Values can be programmed using Marimba slave id 0 + * should there be a conflict with other I2C devices + * */ +#define MARIMBA_SLAVE_ID_FM_ADDR 0x2A +#define MARIMBA_SLAVE_ID_CDC_ADDR 0x77 +#define MARIMBA_SLAVE_ID_QMEMBIST_ADDR 0X66 + +#define BAHAMA_SLAVE_ID_FM_ADDR 0x2A +#define BAHAMA_SLAVE_ID_QMEMBIST_ADDR 0x7B + +static const char *tsadc_id = "MADC"; + +static struct regulator_bulk_data regs_tsadc_marimba[] = { + { .supply = "gp12", .min_uV = 2200000, .max_uV = 2200000 }, + { .supply = "s2", .min_uV = 1300000, .max_uV = 1300000 }, +}; + +static struct regulator_bulk_data regs_tsadc_timpani[] = { + { .supply = "s3", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "gp12", .min_uV = 2200000, .max_uV = 2200000 }, + { .supply = "gp16", .min_uV = 1200000, .max_uV = 1200000 }, +}; + +static struct regulator_bulk_data *regs_tsadc; +static int regs_tsadc_count; + +static int marimba_tsadc_power(int vreg_on) +{ + int rc = 0; + int tsadc_adie_type = adie_get_detected_codec_type(); + + switch (tsadc_adie_type) { + case TIMPANI_ID: + rc = pmapp_clock_vote(tsadc_id, PMAPP_CLOCK_ID_D1, + vreg_on ? PMAPP_CLOCK_VOTE_ON : PMAPP_CLOCK_VOTE_OFF); + if (rc) { + pr_err("%s: unable to %svote for d1 clk\n", + __func__, vreg_on ? "" : "de-"); + goto D1_vote_fail; + } + + /* fall through */ + case MARIMBA_ID: + rc = pmapp_clock_vote(tsadc_id, PMAPP_CLOCK_ID_DO, + vreg_on ? PMAPP_CLOCK_VOTE_ON : PMAPP_CLOCK_VOTE_OFF); + if (rc) { + pr_err("%s: unable to %svote for d1 clk\n", + __func__, vreg_on ? "" : "de-"); + goto D0_vote_fail; + } + + WARN_ON(regs_tsadc_count == 0); + + rc = vreg_on ? + regulator_bulk_enable(regs_tsadc_count, regs_tsadc) : + regulator_bulk_disable(regs_tsadc_count, regs_tsadc); + + if (rc) { + pr_err("%s: regulator %sable failed: %d\n", + __func__, vreg_on ? "en" : "dis", rc); + goto regulator_switch_fail; + } + + break; + default: + pr_err("%s:Adie %d not supported\n", + __func__, tsadc_adie_type); + return -ENODEV; + } + + msleep(5); /* ensure power is stable */ + + return 0; + +regulator_switch_fail: + pmapp_clock_vote(tsadc_id, PMAPP_CLOCK_ID_DO, + vreg_on ? PMAPP_CLOCK_VOTE_OFF : PMAPP_CLOCK_VOTE_ON); +D0_vote_fail: + if (tsadc_adie_type == TIMPANI_ID) + pmapp_clock_vote(tsadc_id, PMAPP_CLOCK_ID_D1, + vreg_on ? PMAPP_CLOCK_VOTE_OFF : PMAPP_CLOCK_VOTE_ON); +D1_vote_fail: + return rc; +} + +static int marimba_tsadc_init(void) +{ + int rc = 0; + int tsadc_adie_type = adie_get_detected_codec_type(); + + switch (tsadc_adie_type) { + case MARIMBA_ID: + regs_tsadc = regs_tsadc_marimba; + regs_tsadc_count = ARRAY_SIZE(regs_tsadc_marimba); + break; + case TIMPANI_ID: + regs_tsadc = regs_tsadc_timpani; + regs_tsadc_count = ARRAY_SIZE(regs_tsadc_timpani); + break; + default: + pr_err("%s:Adie %d not supported\n", + __func__, tsadc_adie_type); + rc = -ENODEV; + goto out; + } + + rc = regulator_bulk_get(NULL, regs_tsadc_count, regs_tsadc); + if (rc) { + pr_err("%s: could not get regulators: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(regs_tsadc_count, regs_tsadc); + if (rc) { + pr_err("%s: could not set regulator voltages: %d\n", + __func__, rc); + goto vreg_free; + } + + return 0; + +vreg_free: + regulator_bulk_free(regs_tsadc_count, regs_tsadc); +out: + regs_tsadc = NULL; + regs_tsadc_count = 0; + return rc; +} + +static int marimba_tsadc_exit(void) +{ + regulator_bulk_free(regs_tsadc_count, regs_tsadc); + regs_tsadc_count = 0; + regs_tsadc = NULL; + + return 0; +} + + +static struct msm_ts_platform_data msm_ts_data = { + .min_x = 284, + .max_x = 3801, + .min_y = 155, + .max_y = 3929, + .min_press = 0, + .max_press = 255, + .inv_x = 4096, + .inv_y = 4096, + .can_wakeup = false, +}; + +static struct marimba_tsadc_platform_data marimba_tsadc_pdata = { + .marimba_tsadc_power = marimba_tsadc_power, + .init = marimba_tsadc_init, + .exit = marimba_tsadc_exit, + .tsadc_prechg_en = true, + .can_wakeup = false, + .setup = { + .pen_irq_en = true, + .tsadc_en = true, + }, + .params2 = { + .input_clk_khz = 2400, + .sample_prd = TSADC_CLK_3, + }, + .params3 = { + .prechg_time_nsecs = 6400, + .stable_time_nsecs = 6400, + .tsadc_test_mode = 0, + }, + .tssc_data = &msm_ts_data, +}; + +static struct regulator_bulk_data codec_regs[] = { + { .supply = "s4", .min_uV = 2200000, .max_uV = 2200000 }, +}; + +static int __init msm_marimba_codec_init(void) +{ + int rc = regulator_bulk_get(NULL, ARRAY_SIZE(codec_regs), codec_regs); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(codec_regs), codec_regs); + if (rc) { + pr_err("%s: could not set regulator voltages: %d\n", + __func__, rc); + goto reg_free; + } + + return rc; + +reg_free: + regulator_bulk_free(ARRAY_SIZE(codec_regs), codec_regs); +out: + return rc; +} + +static int msm_marimba_codec_power(int vreg_on) +{ + int rc = vreg_on ? + regulator_bulk_enable(ARRAY_SIZE(codec_regs), codec_regs) : + regulator_bulk_disable(ARRAY_SIZE(codec_regs), codec_regs); + + if (rc) { + pr_err("%s: could not %sable regulators: %d", + __func__, vreg_on ? "en" : "dis", rc); + return rc; + } + + return 0; +} + +static struct marimba_codec_platform_data mariba_codec_pdata = { + .marimba_codec_power = msm_marimba_codec_power, +#ifdef CONFIG_MARIMBA_CODEC + .snddev_profile_init = msm_snddev_init, +#endif +}; + +static struct marimba_platform_data marimba_pdata = { + .slave_id[MARIMBA_SLAVE_ID_FM] = MARIMBA_SLAVE_ID_FM_ADDR, + .slave_id[MARIMBA_SLAVE_ID_CDC] = MARIMBA_SLAVE_ID_CDC_ADDR, + .slave_id[MARIMBA_SLAVE_ID_QMEMBIST] = MARIMBA_SLAVE_ID_QMEMBIST_ADDR, + .slave_id[SLAVE_ID_BAHAMA_FM] = BAHAMA_SLAVE_ID_FM_ADDR, + .slave_id[SLAVE_ID_BAHAMA_QMEMBIST] = BAHAMA_SLAVE_ID_QMEMBIST_ADDR, + .marimba_setup = msm_marimba_setup_power, + .marimba_shutdown = msm_marimba_shutdown_power, + .bahama_setup = msm_bahama_setup_power, + .bahama_shutdown = msm_bahama_shutdown_power, + .marimba_gpio_config = msm_marimba_gpio_config_svlte, + .bahama_core_config = msm_bahama_core_config, + .fm = &marimba_fm_pdata, + .codec = &mariba_codec_pdata, + .tsadc_ssbi_adap = MARIMBA_SSBI_ADAP, +}; + +static void __init msm7x30_init_marimba(void) +{ + int rc; + + struct regulator_bulk_data regs[] = { + { .supply = "s3", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "gp16", .min_uV = 1200000, .max_uV = 1200000 }, + { .supply = "usb2", .min_uV = 1800000, .max_uV = 1800000 }, + }; + + rc = msm_marimba_codec_init(); + + if (rc) { + pr_err("%s: msm_marimba_codec_init failed (%d)\n", + __func__, rc); + return; + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + return; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs), regs); + + if (rc) { + pr_err("%s: could not set voltages: %d\n", __func__, rc); + regulator_bulk_free(ARRAY_SIZE(regs), regs); + return; + } + + vreg_marimba_1 = regs[0].consumer; + vreg_marimba_2 = regs[1].consumer; + vreg_bahama = regs[2].consumer; +} + +static struct marimba_codec_platform_data timpani_codec_pdata = { + .marimba_codec_power = msm_marimba_codec_power, +#ifdef CONFIG_TIMPANI_CODEC + .snddev_profile_init = msm_snddev_init_timpani, +#endif +}; + +static struct marimba_platform_data timpani_pdata = { + .slave_id[MARIMBA_SLAVE_ID_CDC] = MARIMBA_SLAVE_ID_CDC_ADDR, + .slave_id[MARIMBA_SLAVE_ID_QMEMBIST] = MARIMBA_SLAVE_ID_QMEMBIST_ADDR, + .marimba_setup = msm_timpani_setup_power, + .marimba_shutdown = msm_timpani_shutdown_power, + .codec = &timpani_codec_pdata, + .tsadc = &marimba_tsadc_pdata, + .tsadc_ssbi_adap = MARIMBA_SSBI_ADAP, +}; + +#define TIMPANI_I2C_SLAVE_ADDR 0xD + +static struct i2c_board_info msm_i2c_gsbi7_timpani_info[] = { + { + I2C_BOARD_INFO("timpani", TIMPANI_I2C_SLAVE_ADDR), + .platform_data = &timpani_pdata, + }, +}; + +#ifdef CONFIG_MSM7KV2_AUDIO +static struct resource msm_aictl_resources[] = { + { + .name = "aictl", + .start = 0xa5000100, + .end = 0xa5000100, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_mi2s_resources[] = { + { + .name = "hdmi", + .start = 0xac900000, + .end = 0xac900038, + .flags = IORESOURCE_MEM, + }, + { + .name = "codec_rx", + .start = 0xac940040, + .end = 0xac940078, + .flags = IORESOURCE_MEM, + }, + { + .name = "codec_tx", + .start = 0xac980080, + .end = 0xac9800B8, + .flags = IORESOURCE_MEM, + } + +}; + +static struct msm_lpa_platform_data lpa_pdata = { + .obuf_hlb_size = 0x2BFF8, + .dsp_proc_id = 0, + .app_proc_id = 2, + .nosb_config = { + .llb_min_addr = 0, + .llb_max_addr = 0x3ff8, + .sb_min_addr = 0, + .sb_max_addr = 0, + }, + .sb_config = { + .llb_min_addr = 0, + .llb_max_addr = 0x37f8, + .sb_min_addr = 0x3800, + .sb_max_addr = 0x3ff8, + } +}; + +static struct resource msm_lpa_resources[] = { + { + .name = "lpa", + .start = 0xa5000000, + .end = 0xa50000a0, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_aux_pcm_resources[] = { + + { + .name = "aux_codec_reg_addr", + .start = 0xac9c00c0, + .end = 0xac9c00c8, + .flags = IORESOURCE_MEM, + }, + { + .name = "aux_pcm_dout", + .start = 138, + .end = 138, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_din", + .start = 139, + .end = 139, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_syncout", + .start = 140, + .end = 140, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_clkin_a", + .start = 141, + .end = 141, + .flags = IORESOURCE_IO, + }, +}; + +static struct platform_device msm_aux_pcm_device = { + .name = "msm_aux_pcm", + .id = 0, + .num_resources = ARRAY_SIZE(msm_aux_pcm_resources), + .resource = msm_aux_pcm_resources, +}; + +struct platform_device msm_aictl_device = { + .name = "audio_interct", + .id = 0, + .num_resources = ARRAY_SIZE(msm_aictl_resources), + .resource = msm_aictl_resources, +}; + +struct platform_device msm_mi2s_device = { + .name = "mi2s", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mi2s_resources), + .resource = msm_mi2s_resources, +}; + +struct platform_device msm_lpa_device = { + .name = "lpa", + .id = 0, + .num_resources = ARRAY_SIZE(msm_lpa_resources), + .resource = msm_lpa_resources, + .dev = { + .platform_data = &lpa_pdata, + }, +}; +#endif /* CONFIG_MSM7KV2_AUDIO */ + +#define DEC0_FORMAT ((1<> 12; + + qsd_spi_resources[4].start = DMOV_USB_CHAN; + qsd_spi_resources[4].end = DMOV_TSIF_CHAN; + + switch (spi_mux) { + case (1): + qsd_spi_resources[5].start = DMOV_HSUART1_RX_CRCI; + qsd_spi_resources[5].end = DMOV_HSUART1_TX_CRCI; + break; + case (2): + qsd_spi_resources[5].start = DMOV_HSUART2_RX_CRCI; + qsd_spi_resources[5].end = DMOV_HSUART2_TX_CRCI; + break; + case (3): + qsd_spi_resources[5].start = DMOV_CE_OUT_CRCI; + qsd_spi_resources[5].end = DMOV_CE_IN_CRCI; + break; + default: + ret = -ENOENT; + } + + iounmap(ct_adm_base); + + return ret; +} + +static struct platform_device qsd_device_spi = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(qsd_spi_resources), + .resource = qsd_spi_resources, +}; + +#ifdef CONFIG_SPI_QSD +static struct spi_board_info lcdc_sharp_spi_board_info[] __initdata = { + { + .modalias = "lcdc_sharp_ls038y7dx01", + .mode = SPI_MODE_1, + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 26331429, + } +}; +static struct spi_board_info lcdc_toshiba_spi_board_info[] __initdata = { + { + .modalias = "lcdc_toshiba_ltm030dd40", + .mode = SPI_MODE_3|SPI_CS_HIGH, + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 9963243, + } +}; +#endif + +static struct msm_gpio qsd_spi_gpio_config_data[] = { + { GPIO_CFG(45, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_clk" }, + { GPIO_CFG(46, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_cs0" }, + { GPIO_CFG(47, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "spi_mosi" }, + { GPIO_CFG(48, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_miso" }, +}; + +static int msm_qsd_spi_gpio_config(void) +{ + return msm_gpios_request_enable(qsd_spi_gpio_config_data, + ARRAY_SIZE(qsd_spi_gpio_config_data)); +} + +static void msm_qsd_spi_gpio_release(void) +{ + msm_gpios_disable_free(qsd_spi_gpio_config_data, + ARRAY_SIZE(qsd_spi_gpio_config_data)); +} + +static struct msm_spi_platform_data qsd_spi_pdata = { + .max_clock_speed = 26331429, + .gpio_config = msm_qsd_spi_gpio_config, + .gpio_release = msm_qsd_spi_gpio_release, + .dma_config = msm_qsd_spi_dma_config, +}; + +static void __init msm_qsd_spi_init(void) +{ + qsd_device_spi.dev.platform_data = &qsd_spi_pdata; +} + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + int rc; + static int vbus_is_on; + struct pm8xxx_gpio_init_info usb_vbus = { + PM8058_GPIO_PM_TO_SYS(36), + { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 1, + .vin_sel = 2, + .out_strength = PM_GPIO_STRENGTH_MED, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }; + + /* If VBUS is already on (or off), do nothing. */ + if (unlikely(on == vbus_is_on)) + return; + + if (on) { + rc = pm8xxx_gpio_config(usb_vbus.gpio, &usb_vbus.config); + if (rc) { + pr_err("%s PMIC GPIO 36 write failed\n", __func__); + return; + } + } else { + gpio_set_value_cansleep(PM8058_GPIO_PM_TO_SYS(36), 0); + } + + vbus_is_on = on; +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), + .vbus_power = msm_hsusb_vbus_power, + .power_budget = 180, +}; +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static struct regulator *vreg_3p3; +static int msm_hsusb_ldo_init(int init) +{ + uint32_t version = 0; + int def_vol = 3400000; + + version = socinfo_get_version(); + + if (SOCINFO_VERSION_MAJOR(version) >= 2 && + SOCINFO_VERSION_MINOR(version) >= 1) { + def_vol = 3075000; + pr_debug("%s: default voltage:%d\n", __func__, def_vol); + } + + if (init) { + vreg_3p3 = regulator_get(NULL, "usb"); + if (IS_ERR(vreg_3p3)) + return PTR_ERR(vreg_3p3); + regulator_set_voltage(vreg_3p3, def_vol, def_vol); + } else + regulator_put(vreg_3p3); + + return 0; +} + +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + + if (!vreg_3p3 || IS_ERR(vreg_3p3)) + return -ENODEV; + + if (ldo_status == enable) + return 0; + + ldo_status = enable; + + if (enable) + return regulator_enable(vreg_3p3); + else + return regulator_disable(vreg_3p3); +} + +static int msm_hsusb_ldo_set_voltage(int mV) +{ + static int cur_voltage; + + if (!vreg_3p3 || IS_ERR(vreg_3p3)) + return -ENODEV; + + if (cur_voltage == mV) + return 0; + + cur_voltage = mV; + + pr_debug("%s: (%d)\n", __func__, mV); + + return regulator_set_voltage(vreg_3p3, mV*1000, mV*1000); +} +#endif + +#ifndef CONFIG_USB_EHCI_MSM_72K +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init); +#endif +static struct msm_otg_platform_data msm_otg_pdata = { + .rpc_connect = hsusb_rpc_connect, + +#ifndef CONFIG_USB_EHCI_MSM_72K + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, +#else + .vbus_power = msm_hsusb_vbus_power, +#endif + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .drv_ampl = HS_DRV_AMPLITUDE_DEFAULT, + .se1_gating = SE1_GATING_DISABLE, + .chg_vbus_draw = hsusb_chg_vbus_draw, + .chg_connected = hsusb_chg_connected, + .chg_init = hsusb_chg_init, + .ldo_enable = msm_hsusb_ldo_enable, + .ldo_init = msm_hsusb_ldo_init, + .ldo_set_voltage = msm_hsusb_ldo_set_voltage, +}; + +#ifdef CONFIG_USB_GADGET +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, +}; +#endif +#ifndef CONFIG_USB_EHCI_MSM_72K +typedef void (*notify_vbus_state) (int); +notify_vbus_state notify_vbus_state_func_ptr; +int vbus_on_irq; +static irqreturn_t pmic_vbus_on_irq(int irq, void *data) +{ + pr_info("%s: vbus notification from pmic\n", __func__); + + (*notify_vbus_state_func_ptr) (1); + + return IRQ_HANDLED; +} +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret; + + if (init) { + if (!callback) + return -ENODEV; + + notify_vbus_state_func_ptr = callback; + vbus_on_irq = platform_get_irq_byname(&msm_device_otg, + "vbus_on"); + if (vbus_on_irq <= 0) { + pr_err("%s: unable to get vbus on irq\n", __func__); + return -ENODEV; + } + + ret = request_any_context_irq(vbus_on_irq, pmic_vbus_on_irq, + IRQF_TRIGGER_RISING, "msm_otg_vbus_on", NULL); + if (ret < 0) { + pr_info("%s: request_irq for vbus_on" + "interrupt failed\n", __func__); + return ret; + } + msm_otg_pdata.pmic_vbus_irq = vbus_on_irq; + return 0; + } else { + free_irq(vbus_on_irq, 0); + notify_vbus_state_func_ptr = NULL; + return 0; + } +} +#endif + +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, + .memory_type = MEMTYPE_EBI0, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &android_pmem_pdata }, +}; + +#ifndef CONFIG_SPI_QSD +static int lcdc_gpio_array_num[] = { + 45, /* spi_clk */ + 46, /* spi_cs */ + 47, /* spi_mosi */ + 48, /* spi_miso */ + }; + +static struct msm_gpio lcdc_gpio_config_data[] = { + { GPIO_CFG(45, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_clk" }, + { GPIO_CFG(46, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_cs0" }, + { GPIO_CFG(47, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_mosi" }, + { GPIO_CFG(48, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_miso" }, +}; + +static void lcdc_config_gpios(int enable) +{ + if (enable) { + msm_gpios_request_enable(lcdc_gpio_config_data, + ARRAY_SIZE( + lcdc_gpio_config_data)); + } else + msm_gpios_disable_free(lcdc_gpio_config_data, + ARRAY_SIZE( + lcdc_gpio_config_data)); +} +#endif + +static struct msm_panel_common_pdata lcdc_sharp_panel_data = { +#ifndef CONFIG_SPI_QSD + .panel_config_gpio = lcdc_config_gpios, + .gpio_num = lcdc_gpio_array_num, +#endif + .gpio = 2, /* LPG PMIC_GPIO26 channel number */ +}; + +static struct platform_device lcdc_sharp_panel_device = { + .name = "lcdc_sharp_wvga", + .id = 0, + .dev = { + .platform_data = &lcdc_sharp_panel_data, + } +}; + +static struct msm_gpio dtv_panel_irq_gpios[] = { + { GPIO_CFG(18, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), + "hdmi_int" }, +}; + +static struct msm_gpio dtv_panel_gpios[] = { + { GPIO_CFG(120, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "wca_mclk" }, + { GPIO_CFG(121, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "wca_sd0" }, + { GPIO_CFG(122, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "wca_sd1" }, + { GPIO_CFG(123, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "wca_sd2" }, + { GPIO_CFG(124, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "dtv_pclk" }, + { GPIO_CFG(125, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_en" }, + { GPIO_CFG(126, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_vsync" }, + { GPIO_CFG(127, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_hsync" }, + { GPIO_CFG(128, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data0" }, + { GPIO_CFG(129, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data1" }, + { GPIO_CFG(130, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data2" }, + { GPIO_CFG(131, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data3" }, + { GPIO_CFG(132, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data4" }, + { GPIO_CFG(160, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data5" }, + { GPIO_CFG(161, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data6" }, + { GPIO_CFG(162, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data7" }, + { GPIO_CFG(163, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data8" }, + { GPIO_CFG(164, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_data9" }, + { GPIO_CFG(165, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat10" }, + { GPIO_CFG(166, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat11" }, + { GPIO_CFG(167, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat12" }, + { GPIO_CFG(168, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat13" }, + { GPIO_CFG(169, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat14" }, + { GPIO_CFG(170, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat15" }, + { GPIO_CFG(171, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat16" }, + { GPIO_CFG(172, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat17" }, + { GPIO_CFG(173, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat18" }, + { GPIO_CFG(174, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat19" }, + { GPIO_CFG(175, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat20" }, + { GPIO_CFG(176, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat21" }, + { GPIO_CFG(177, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat22" }, + { GPIO_CFG(178, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_4MA), "dtv_dat23" }, +}; + + +#ifdef HDMI_RESET +static unsigned dtv_reset_gpio = + GPIO_CFG(37, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); +#endif + +static struct regulator_bulk_data hdmi_core_regs[] = { + { .supply = "ldo8", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +static struct regulator_bulk_data hdmi_comm_regs[] = { + { .supply = "ldo8", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "ldo10", .min_uV = 2600000, .max_uV = 2600000 }, +}; + +static struct regulator_bulk_data hdmi_cec_regs[] = { + { .supply = "ldo17", .min_uV = 2600000, .max_uV = 2600000 }, +}; + +static int __init hdmi_init_regs(void) +{ + int rc; + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(hdmi_core_regs), + hdmi_core_regs); + + if (rc) { + pr_err("%s: could not get %s regulators: %d\n", + __func__, "core", rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(hdmi_core_regs), + hdmi_core_regs); + + if (rc) { + pr_err("%s: could not set %s voltages: %d\n", + __func__, "core", rc); + goto free_core; + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(hdmi_comm_regs), + hdmi_comm_regs); + + if (rc) { + pr_err("%s: could not get %s regulators: %d\n", + __func__, "comm", rc); + goto free_core; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(hdmi_comm_regs), + hdmi_comm_regs); + + if (rc) { + pr_err("%s: could not set %s voltages: %d\n", + __func__, "comm", rc); + goto free_comm; + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(hdmi_cec_regs), + hdmi_cec_regs); + + if (rc) { + pr_err("%s: could not get %s regulators: %d\n", + __func__, "cec", rc); + goto free_comm; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(hdmi_cec_regs), + hdmi_cec_regs); + + if (rc) { + pr_err("%s: could not set %s voltages: %d\n", + __func__, "cec", rc); + goto free_cec; + } + + return 0; + +free_cec: + regulator_bulk_free(ARRAY_SIZE(hdmi_cec_regs), hdmi_cec_regs); +free_comm: + regulator_bulk_free(ARRAY_SIZE(hdmi_comm_regs), hdmi_comm_regs); +free_core: + regulator_bulk_free(ARRAY_SIZE(hdmi_core_regs), hdmi_core_regs); +out: + return rc; +} + +static int hdmi_init_irq(void) +{ + int rc = msm_gpios_enable(dtv_panel_irq_gpios, + ARRAY_SIZE(dtv_panel_irq_gpios)); + if (rc < 0) { + pr_err("%s: gpio enable failed: %d\n", __func__, rc); + return rc; + } + pr_info("%s\n", __func__); + + return 0; +} + +static int hdmi_enable_5v(int on) +{ + int pmic_gpio_hdmi_5v_en ; + + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa() || + machine_is_msm7x30_fluid()) + pmic_gpio_hdmi_5v_en = PMIC_GPIO_HDMI_5V_EN_V2 ; + else + pmic_gpio_hdmi_5v_en = PMIC_GPIO_HDMI_5V_EN_V3 ; + + pr_info("%s: %d\n", __func__, on); + if (on) { + int rc; + rc = gpio_request(PM8058_GPIO_PM_TO_SYS(pmic_gpio_hdmi_5v_en), + "hdmi_5V_en"); + if (rc) { + pr_err("%s PMIC_GPIO_HDMI_5V_EN gpio_request failed\n", + __func__); + return rc; + } + gpio_set_value_cansleep( + PM8058_GPIO_PM_TO_SYS(pmic_gpio_hdmi_5v_en), 1); + } else { + gpio_set_value_cansleep( + PM8058_GPIO_PM_TO_SYS(pmic_gpio_hdmi_5v_en), 0); + gpio_free(PM8058_GPIO_PM_TO_SYS(pmic_gpio_hdmi_5v_en)); + } + return 0; +} + +static int hdmi_comm_power(int on, int show) +{ + if (show) + pr_info("%s: i2c comm: %d \n", __func__, on); + return on ? + regulator_bulk_enable(ARRAY_SIZE(hdmi_comm_regs), + hdmi_comm_regs) : + regulator_bulk_disable(ARRAY_SIZE(hdmi_comm_regs), + hdmi_comm_regs); +} + +static int hdmi_core_power(int on, int show) +{ + if (show) + pr_info("%s: %d \n", __func__, on); + return on ? + regulator_bulk_enable(ARRAY_SIZE(hdmi_core_regs), + hdmi_core_regs) : + regulator_bulk_disable(ARRAY_SIZE(hdmi_core_regs), + hdmi_core_regs); +} + +static int hdmi_cec_power(int on) +{ + pr_info("%s: %d \n", __func__, on); + return on ? regulator_bulk_enable(ARRAY_SIZE(hdmi_cec_regs), + hdmi_cec_regs) : + regulator_bulk_disable(ARRAY_SIZE(hdmi_cec_regs), + hdmi_cec_regs); +} + +#if defined(CONFIG_FB_MSM_HDMI_ADV7520_PANEL) || defined(CONFIG_BOSCH_BMA150) +/* there is an i2c address conflict between adv7520 and bma150 sensor after + * power up on fluid. As a solution, the default address of adv7520's packet + * memory is changed as soon as possible + */ +static int __init fluid_i2c_address_fixup(void) +{ + unsigned char wBuff[16]; + unsigned char rBuff[16]; + struct i2c_msg msgs[3]; + int res; + int rc = -EINVAL; + struct i2c_adapter *adapter; + + if (machine_is_msm7x30_fluid()) { + adapter = i2c_get_adapter(0); + if (!adapter) { + pr_err("%s: invalid i2c adapter\n", __func__); + return PTR_ERR(adapter); + } + + /* turn on LDO8 */ + rc = hdmi_core_power(1, 0); + if (rc) { + pr_err("%s: could not enable hdmi core regs: %d", + __func__, rc); + goto adapter_put; + } + + /* change packet memory address to 0x74 */ + wBuff[0] = 0x45; + wBuff[1] = 0x74; + + msgs[0].addr = ADV7520_I2C_ADDR; + msgs[0].flags = 0; + msgs[0].buf = (unsigned char *) wBuff; + msgs[0].len = 2; + + res = i2c_transfer(adapter, msgs, 1); + if (res != 1) { + pr_err("%s: error writing adv7520\n", __func__); + goto ldo8_disable; + } + + /* powerdown adv7520 using bit 6 */ + /* i2c read first */ + wBuff[0] = 0x41; + + msgs[0].addr = ADV7520_I2C_ADDR; + msgs[0].flags = 0; + msgs[0].buf = (unsigned char *) wBuff; + msgs[0].len = 1; + + msgs[1].addr = ADV7520_I2C_ADDR; + msgs[1].flags = I2C_M_RD; + msgs[1].buf = rBuff; + msgs[1].len = 1; + res = i2c_transfer(adapter, msgs, 2); + if (res != 2) { + pr_err("%s: error reading adv7520\n", __func__); + goto ldo8_disable; + } + + /* i2c write back */ + wBuff[0] = 0x41; + wBuff[1] = rBuff[0] | 0x40; + + msgs[0].addr = ADV7520_I2C_ADDR; + msgs[0].flags = 0; + msgs[0].buf = (unsigned char *) wBuff; + msgs[0].len = 2; + + res = i2c_transfer(adapter, msgs, 1); + if (res != 1) { + pr_err("%s: error writing adv7520\n", __func__); + goto ldo8_disable; + } + + /* for successful fixup, we release the i2c adapter */ + /* but leave ldo8 on so that the adv7520 is not repowered */ + i2c_put_adapter(adapter); + pr_info("%s: fluid i2c address conflict resolved\n", __func__); + } + return 0; + +ldo8_disable: + hdmi_core_power(0, 0); +adapter_put: + i2c_put_adapter(adapter); + return rc; +} +fs_initcall_sync(fluid_i2c_address_fixup); +#endif + +static bool hdmi_check_hdcp_hw_support(void) +{ + if (machine_is_msm7x30_fluid()) + return false; + else + return true; +} + +static int dtv_panel_power(int on) +{ + int flag_on = !!on; + static int dtv_power_save_on; + int rc; + + if (dtv_power_save_on == flag_on) + return 0; + + dtv_power_save_on = flag_on; + pr_info("%s: %d\n", __func__, on); + +#ifdef HDMI_RESET + if (on) { + /* reset Toshiba WeGA chip -- toggle reset pin -- gpio_180 */ + rc = gpio_tlmm_config(dtv_reset_gpio, GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, dtv_reset_gpio, rc); + return rc; + } + + /* bring reset line low to hold reset*/ + gpio_set_value(37, 0); + } +#endif + + if (on) { + rc = msm_gpios_enable(dtv_panel_gpios, + ARRAY_SIZE(dtv_panel_gpios)); + if (rc < 0) { + printk(KERN_ERR "%s: gpio enable failed: %d\n", + __func__, rc); + return rc; + } + } else { + rc = msm_gpios_disable(dtv_panel_gpios, + ARRAY_SIZE(dtv_panel_gpios)); + if (rc < 0) { + printk(KERN_ERR "%s: gpio disable failed: %d\n", + __func__, rc); + return rc; + } + } + + mdelay(5); /* ensure power is stable */ + +#ifdef HDMI_RESET + if (on) { + gpio_set_value(37, 1); /* bring reset line high */ + mdelay(10); /* 10 msec before IO can be accessed */ + } +#endif + + return rc; +} + +static struct lcdc_platform_data dtv_pdata = { + .lcdc_power_save = dtv_panel_power, +}; + +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, +}; + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE +static struct resource msm_v4l2_video_overlay_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; +#endif + +static int msm_fb_detect_panel(const char *name) +{ + if (machine_is_msm7x30_fluid()) { + if (!strcmp(name, "lcdc_sharp_wvga_pt")) + return 0; + } else { + if (!strncmp(name, "mddi_toshiba_wvga_pt", 20)) + return -EPERM; + else if (!strncmp(name, "lcdc_toshiba_wvga_pt", 20)) + return 0; + else if (!strcmp(name, "mddi_orise")) + return -EPERM; + else if (!strcmp(name, "mddi_quickvx")) + return -EPERM; + } + return -ENODEV; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, + .mddi_prescan = 1, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev = { + .platform_data = &msm_fb_pdata, + } +}; + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE + +static struct platform_device msm_v4l2_video_overlay_device = { + .name = "msm_v4l2_overlay_pd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_v4l2_video_overlay_resources), + .resource = msm_v4l2_video_overlay_resources, +}; +#endif + +static struct platform_device msm_migrate_pages_device = { + .name = "msm_migrate_pages", + .id = -1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI0, +}; + +static struct android_pmem_platform_data android_pmem_audio_pdata = { + .name = "pmem_audio", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI0, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct platform_device android_pmem_audio_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_audio_pdata }, +}; + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +#define QCE_SIZE 0x10000 +#define QCE_0_BASE 0xA8400000 + +#define QCE_HW_KEY_SUPPORT 1 +#define QCE_SHA_HMAC_SUPPORT 0 +#define QCE_SHARE_CE_RESOURCE 0 +#define QCE_CE_SHARED 0 + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE_HASH_CRCI, + .end = DMOV_CE_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE_HASH_CRCI, + .end = DMOV_CE_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + /* Bus Scaling declaration*/ + .bus_scale_table = NULL, +}; + +static struct platform_device qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + /* Bus Scaling declaration*/ + .bus_scale_table = NULL, +}; +static struct platform_device qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; +#endif + +static int mddi_toshiba_pmic_bl(int level) +{ + int ret = -EPERM; + + ret = pmic_set_led_intensity(LED_LCD, level); + + if (ret) + printk(KERN_WARNING "%s: can't set lcd backlight!\n", + __func__); + return ret; +} + +static struct msm_panel_common_pdata mddi_toshiba_pdata = { + .pmic_backlight = mddi_toshiba_pmic_bl, +}; + +static struct platform_device mddi_toshiba_device = { + .name = "mddi_toshiba", + .id = 0, + .dev = { + .platform_data = &mddi_toshiba_pdata, + } +}; + +static unsigned wega_reset_gpio = + GPIO_CFG(180, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + +static struct msm_gpio fluid_vee_reset_gpio[] = { + { GPIO_CFG(20, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "vee_reset" }, +}; + +static unsigned char quickvx_mddi_client = 1, other_mddi_client = 1; +static unsigned char quickvx_ldo_enabled; + +static unsigned quickvx_vlp_gpio = + GPIO_CFG(97, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + +static struct pm8xxx_gpio_init_info pmic_quickvx_clk_gpio = { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_QUICKVX_CLK), + { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 1, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_2, + }, +}; + +static struct regulator *mddi_ldo20; +static struct regulator *mddi_ldo12; +static struct regulator *mddi_ldo16; +static struct regulator *mddi_ldo6; +static struct regulator *mddi_lcd; + +static int display_common_init(void) +{ + struct regulator_bulk_data regs[5] = { + { .supply = "ldo20", /* voltage set in display_common_power */}, + { .supply = "ldo12", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "ldo6", .min_uV = 3075000, .max_uV = 3400000 }, + { .supply = "ldo16", .min_uV = 2600000, .max_uV = 2600000 }, + { .supply = NULL, /* mddi_lcd, initialized below */ }, + }; + + int rc = 0; + + if (machine_is_msm7x30_fluid()) { + /* lcd: LDO8 @1.8V */ + regs[4].supply = "ldo8"; + regs[4].min_uV = 1800000; + regs[4].max_uV = 1800000; + } else { + /* lcd: LDO15 @3.1V */ + regs[4].supply = "ldo15"; + regs[4].min_uV = 3100000; + regs[4].max_uV = 3100000; + } + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs); + if (rc) { + pr_err("%s: regulator_bulk_get failed: %d\n", + __func__, rc); + goto bail; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs), regs); + if (rc) { + pr_err("%s: regulator_bulk_set_voltage failed: %d\n", + __func__, rc); + goto put_regs; + } + + mddi_ldo20 = regs[0].consumer; + mddi_ldo12 = regs[1].consumer; + mddi_ldo6 = regs[2].consumer; + mddi_ldo16 = regs[3].consumer; + mddi_lcd = regs[4].consumer; + + return rc; + +put_regs: + regulator_bulk_free(ARRAY_SIZE(regs), regs); +bail: + return rc; +} + +static int display_common_power(int on) +{ + int rc = 0, flag_on = !!on; + static int display_common_power_save_on; + static bool display_regs_initialized; + + if (display_common_power_save_on == flag_on) + return 0; + + display_common_power_save_on = flag_on; + + if (unlikely(!display_regs_initialized)) { + rc = display_common_init(); + if (rc) { + pr_err("%s: regulator init failed: %d\n", + __func__, rc); + return rc; + } + display_regs_initialized = true; + } + + + if (on) { + /* reset Toshiba WeGA chip -- toggle reset pin -- gpio_180 */ + rc = gpio_tlmm_config(wega_reset_gpio, GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, wega_reset_gpio, rc); + return rc; + } + + /* bring reset line low to hold reset*/ + gpio_set_value(180, 0); + + if (quickvx_mddi_client) { + /* QuickVX chip -- VLP pin -- gpio 97 */ + rc = gpio_tlmm_config(quickvx_vlp_gpio, + GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, quickvx_vlp_gpio, rc); + return rc; + } + + /* bring QuickVX VLP line low */ + gpio_set_value(97, 0); + + rc = pm8xxx_gpio_config(pmic_quickvx_clk_gpio.gpio, + &pmic_quickvx_clk_gpio.config); + if (rc) { + pr_err("%s: pm8xxx_gpio_config(%#x)=%d\n", + __func__, pmic_quickvx_clk_gpio.gpio, + rc); + return rc; + } + + gpio_set_value_cansleep(PM8058_GPIO_PM_TO_SYS( + PMIC_GPIO_QUICKVX_CLK), 0); + } + } + + if (quickvx_mddi_client) + rc = regulator_set_voltage(mddi_ldo20, 1800000, 1800000); + else + rc = regulator_set_voltage(mddi_ldo20, 1500000, 1500000); + + if (rc) { + pr_err("%s: could not set voltage for ldo20: %d\n", + __func__, rc); + return rc; + } + + if (on) { + rc = regulator_enable(mddi_ldo20); + if (rc) { + pr_err("%s: LDO20 regulator enable failed (%d)\n", + __func__, rc); + return rc; + } + + rc = regulator_enable(mddi_ldo12); + if (rc) { + pr_err("%s: LDO12 regulator enable failed (%d)\n", + __func__, rc); + return rc; + } + + if (other_mddi_client) { + rc = regulator_enable(mddi_ldo16); + if (rc) { + pr_err("%s: LDO16 regulator enable failed (%d)\n", + __func__, rc); + return rc; + } + } + + if (quickvx_ldo_enabled) { + /* Disable LDO6 during display ON */ + rc = regulator_disable(mddi_ldo6); + if (rc) { + pr_err("%s: LDO6 regulator disable failed (%d)\n", + __func__, rc); + return rc; + } + quickvx_ldo_enabled = 0; + } + + rc = regulator_enable(mddi_lcd); + if (rc) { + pr_err("%s: LCD regulator enable failed (%d)\n", + __func__, rc); + return rc; + } + + mdelay(5); /* ensure power is stable */ + + if (machine_is_msm7x30_fluid()) { + rc = msm_gpios_request_enable(fluid_vee_reset_gpio, + ARRAY_SIZE(fluid_vee_reset_gpio)); + if (rc) + pr_err("%s gpio_request_enable failed rc=%d\n", + __func__, rc); + else { + /* assert vee reset_n */ + gpio_set_value(20, 1); + gpio_set_value(20, 0); + mdelay(1); + gpio_set_value(20, 1); + } + } + + gpio_set_value(180, 1); /* bring reset line high */ + mdelay(10); /* 10 msec before IO can be accessed */ + + if (quickvx_mddi_client) { + gpio_set_value(97, 1); + msleep(2); + gpio_set_value_cansleep(PM8058_GPIO_PM_TO_SYS( + PMIC_GPIO_QUICKVX_CLK), 1); + msleep(2); + } + + rc = pmapp_display_clock_config(1); + if (rc) { + pr_err("%s pmapp_display_clock_config rc=%d\n", + __func__, rc); + return rc; + } + + } else { + rc = regulator_disable(mddi_ldo20); + if (rc) { + pr_err("%s: LDO20 regulator disable failed (%d)\n", + __func__, rc); + return rc; + } + + + if (other_mddi_client) { + rc = regulator_disable(mddi_ldo16); + if (rc) { + pr_err("%s: LDO16 regulator disable failed (%d)\n", + __func__, rc); + return rc; + } + } + + if (quickvx_mddi_client && !quickvx_ldo_enabled) { + /* Enable LDO6 during display OFF for + Quicklogic chip to sleep with data retention */ + rc = regulator_enable(mddi_ldo6); + if (rc) { + pr_err("%s: LDO6 regulator enable failed (%d)\n", + __func__, rc); + return rc; + } + quickvx_ldo_enabled = 1; + } + + gpio_set_value(180, 0); /* bring reset line low */ + + if (quickvx_mddi_client) { + gpio_set_value(97, 0); + gpio_set_value_cansleep(PM8058_GPIO_PM_TO_SYS( + PMIC_GPIO_QUICKVX_CLK), 0); + } + + rc = regulator_disable(mddi_lcd); + if (rc) { + pr_err("%s: LCD regulator disable failed (%d)\n", + __func__, rc); + return rc; + } + + mdelay(5); /* ensure power is stable */ + + rc = regulator_disable(mddi_ldo12); + if (rc) { + pr_err("%s: LDO12 regulator disable failed (%d)\n", + __func__, rc); + return rc; + } + + if (machine_is_msm7x30_fluid()) { + msm_gpios_disable_free(fluid_vee_reset_gpio, + ARRAY_SIZE(fluid_vee_reset_gpio)); + } + + rc = pmapp_display_clock_config(0); + if (rc) { + pr_err("%s pmapp_display_clock_config rc=%d\n", + __func__, rc); + return rc; + } + } + + return rc; +} + +static int msm_fb_mddi_sel_clk(u32 *clk_rate) +{ + *clk_rate *= 2; + return 0; +} + +static int msm_fb_mddi_client_power(u32 client_id) +{ + int rc; + printk(KERN_NOTICE "\n client_id = 0x%x", client_id); + /* Check if it is Quicklogic client */ + if (client_id == 0xc5835800) { + printk(KERN_NOTICE "\n Quicklogic MDDI client"); + other_mddi_client = 0; + if (IS_ERR(mddi_ldo16)) { + rc = PTR_ERR(mddi_ldo16); + pr_err("%s: gp10 vreg get failed (%d)\n", __func__, rc); + return rc; + } + rc = regulator_disable(mddi_ldo16); + if (rc) { + pr_err("%s: LDO16 vreg enable failed (%d)\n", + __func__, rc); + return rc; + } + + } else { + printk(KERN_NOTICE "\n Non-Quicklogic MDDI client"); + quickvx_mddi_client = 0; + gpio_set_value(97, 0); + gpio_set_value_cansleep(PM8058_GPIO_PM_TO_SYS( + PMIC_GPIO_QUICKVX_CLK), 0); + } + + return 0; +} + +static struct mddi_platform_data mddi_pdata = { + .mddi_power_save = display_common_power, + .mddi_sel_clk = msm_fb_mddi_sel_clk, + .mddi_client_power = msm_fb_mddi_client_power, +}; + +int mdp_core_clk_rate_table[] = { + 122880000, + 122880000, + 192000000, + 192000000, +}; + +static struct msm_panel_common_pdata mdp_pdata = { + .hw_revision_addr = 0xac001270, + .gpio = 30, + .mdp_core_clk_rate = 122880000, + .mdp_core_clk_table = mdp_core_clk_rate_table, + .num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table), + .mdp_rev = MDP_REV_40, + .mem_hid = MEMTYPE_EBI0, +}; + +static int lcd_panel_spi_gpio_num[] = { + 45, /* spi_clk */ + 46, /* spi_cs */ + 47, /* spi_mosi */ + 48, /* spi_miso */ + }; + +static struct msm_gpio lcd_panel_gpios[] = { +/* Workaround, since HDMI_INT is using the same GPIO line (18), and is used as + * input. if there is a hardware revision; we should reassign this GPIO to a + * new open line; and removing it will just ensure that this will be missed in + * the future. + { GPIO_CFG(18, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn0" }, + */ + { GPIO_CFG(19, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn1" }, + { GPIO_CFG(20, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu0" }, + { GPIO_CFG(21, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu1" }, + { GPIO_CFG(22, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu2" }, + { GPIO_CFG(23, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red0" }, + { GPIO_CFG(24, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red1" }, + { GPIO_CFG(25, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red2" }, +#ifndef CONFIG_SPI_QSD + { GPIO_CFG(45, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_clk" }, + { GPIO_CFG(46, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_cs0" }, + { GPIO_CFG(47, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_mosi" }, + { GPIO_CFG(48, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_miso" }, +#endif + { GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_pclk" }, + { GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_en" }, + { GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_vsync" }, + { GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_hsync" }, + { GPIO_CFG(94, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn2" }, + { GPIO_CFG(95, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn3" }, + { GPIO_CFG(96, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn4" }, + { GPIO_CFG(97, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn5" }, + { GPIO_CFG(98, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn6" }, + { GPIO_CFG(99, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn7" }, + { GPIO_CFG(100, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu3" }, + { GPIO_CFG(101, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu4" }, + { GPIO_CFG(102, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu5" }, + { GPIO_CFG(103, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu6" }, + { GPIO_CFG(104, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu7" }, + { GPIO_CFG(105, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red3" }, + { GPIO_CFG(106, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red4" }, + { GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red5" }, + { GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red6" }, + { GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red7" }, +}; + +static struct msm_gpio lcd_sharp_panel_gpios[] = { + { GPIO_CFG(22, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu2" }, + { GPIO_CFG(25, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red2" }, + { GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_pclk" }, + { GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_en" }, + { GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_vsync" }, + { GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_hsync" }, + { GPIO_CFG(94, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn2" }, + { GPIO_CFG(95, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn3" }, + { GPIO_CFG(96, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn4" }, + { GPIO_CFG(97, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn5" }, + { GPIO_CFG(98, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn6" }, + { GPIO_CFG(99, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_grn7" }, + { GPIO_CFG(100, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu3" }, + { GPIO_CFG(101, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu4" }, + { GPIO_CFG(102, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu5" }, + { GPIO_CFG(103, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu6" }, + { GPIO_CFG(104, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_blu7" }, + { GPIO_CFG(105, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red3" }, + { GPIO_CFG(106, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red4" }, + { GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red5" }, + { GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red6" }, + { GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "lcdc_red7" }, +}; + +static int lcdc_toshiba_panel_power(int on) +{ + int rc, i; + struct msm_gpio *gp; + + rc = display_common_power(on); + if (rc < 0) { + printk(KERN_ERR "%s display_common_power failed: %d\n", + __func__, rc); + return rc; + } + + if (on) { + rc = msm_gpios_enable(lcd_panel_gpios, + ARRAY_SIZE(lcd_panel_gpios)); + if (rc < 0) { + printk(KERN_ERR "%s: gpio enable failed: %d\n", + __func__, rc); + } + } else { /* off */ + gp = lcd_panel_gpios; + for (i = 0; i < ARRAY_SIZE(lcd_panel_gpios); i++) { + /* ouput low */ + gpio_set_value(GPIO_PIN(gp->gpio_cfg), 0); + gp++; + } + } + + return rc; +} + +static int lcdc_sharp_panel_power(int on) +{ + int rc, i; + struct msm_gpio *gp; + + rc = display_common_power(on); + if (rc < 0) { + printk(KERN_ERR "%s display_common_power failed: %d\n", + __func__, rc); + return rc; + } + + if (on) { + rc = msm_gpios_enable(lcd_sharp_panel_gpios, + ARRAY_SIZE(lcd_sharp_panel_gpios)); + if (rc < 0) { + printk(KERN_ERR "%s: gpio enable failed: %d\n", + __func__, rc); + } + } else { /* off */ + gp = lcd_sharp_panel_gpios; + for (i = 0; i < ARRAY_SIZE(lcd_sharp_panel_gpios); i++) { + /* ouput low */ + gpio_set_value(GPIO_PIN(gp->gpio_cfg), 0); + gp++; + } + } + + return rc; +} + +static int lcdc_panel_power(int on) +{ + int flag_on = !!on; + static int lcdc_power_save_on, lcdc_power_initialized; + + if (lcdc_power_save_on == flag_on) + return 0; + + lcdc_power_save_on = flag_on; + + if (unlikely(!lcdc_power_initialized)) { + quickvx_mddi_client = 0; + display_common_init(); + lcdc_power_initialized = 1; + } + + if (machine_is_msm7x30_fluid()) + return lcdc_sharp_panel_power(on); + else + return lcdc_toshiba_panel_power(on); +} + +static struct lcdc_platform_data lcdc_pdata = { + .lcdc_power_save = lcdc_panel_power, +}; + +static struct regulator *atv_s4, *atv_ldo9; + +static int __init atv_dac_power_init(void) +{ + int rc; + struct regulator_bulk_data regs[] = { + { .supply = "smps4", .min_uV = 2200000, .max_uV = 2200000 }, + { .supply = "ldo9", .min_uV = 2050000, .max_uV = 2050000 }, + }; + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + goto bail; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs), regs); + + if (rc) { + pr_err("%s: could not set voltages: %d\n", __func__, rc); + goto reg_free; + } + + atv_s4 = regs[0].consumer; + atv_ldo9 = regs[1].consumer; + +reg_free: + regulator_bulk_free(ARRAY_SIZE(regs), regs); +bail: + return rc; +} + +static int atv_dac_power(int on) +{ + int rc = 0; + + if (on) { + rc = regulator_enable(atv_s4); + if (rc) { + pr_err("%s: s4 vreg enable failed (%d)\n", + __func__, rc); + return rc; + } + rc = regulator_enable(atv_ldo9); + if (rc) { + pr_err("%s: ldo9 vreg enable failed (%d)\n", + __func__, rc); + return rc; + } + } else { + rc = regulator_disable(atv_ldo9); + if (rc) { + pr_err("%s: ldo9 vreg disable failed (%d)\n", + __func__, rc); + return rc; + } + rc = regulator_disable(atv_s4); + if (rc) { + pr_err("%s: s4 vreg disable failed (%d)\n", + __func__, rc); + return rc; + } + } + return rc; +} + +static struct tvenc_platform_data atv_pdata = { + .poll = 1, + .pm_vid_en = atv_dac_power, +}; + +static void __init msm_fb_add_devices(void) +{ + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("pmdh", &mddi_pdata); + msm_fb_register_device("lcdc", &lcdc_pdata); + msm_fb_register_device("dtv", &dtv_pdata); + msm_fb_register_device("tvenc", &atv_pdata); +#ifdef CONFIG_FB_MSM_TVOUT + msm_fb_register_device("tvout_device", NULL); +#endif +} + +static struct msm_panel_common_pdata lcdc_toshiba_panel_data = { + .gpio_num = lcd_panel_spi_gpio_num, +}; + +static struct platform_device lcdc_toshiba_panel_device = { + .name = "lcdc_toshiba_wvga", + .id = 0, + .dev = { + .platform_data = &lcdc_toshiba_panel_data, + } +}; + +#if defined(CONFIG_MARIMBA_CORE) && \ + (defined(CONFIG_MSM_BT_POWER) || defined(CONFIG_MSM_BT_POWER_MODULE)) +static struct platform_device msm_bt_power_device = { + .name = "bt_power", + .id = -1 +}; + +enum { + BT_RFR, + BT_CTS, + BT_RX, + BT_TX, +}; + +static struct msm_gpio bt_config_power_on[] = { + { GPIO_CFG(134, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_RFR" }, + { GPIO_CFG(135, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_CTS" }, + { GPIO_CFG(136, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_Rx" }, + { GPIO_CFG(137, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_Tx" } +}; + +static struct msm_gpio bt_config_power_off[] = { + { GPIO_CFG(134, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_RFR" }, + { GPIO_CFG(135, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_CTS" }, + { GPIO_CFG(136, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_Rx" }, + { GPIO_CFG(137, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_Tx" } +}; + +static u8 bahama_version; + +static struct regulator_bulk_data regs_bt_marimba[] = { + { .supply = "smps3", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "smps2", .min_uV = 1300000, .max_uV = 1300000 }, + { .supply = "ldo24", .min_uV = 1200000, .max_uV = 1200000 }, + { .supply = "ldo13", .min_uV = 2900000, .max_uV = 3050000 }, +}; + +static struct regulator_bulk_data regs_bt_bahama_v1[] = { + { .supply = "smps3", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "ldo7", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "smps2", .min_uV = 1300000, .max_uV = 1300000 }, + { .supply = "ldo13", .min_uV = 2900000, .max_uV = 3050000 }, +}; + +static struct regulator_bulk_data regs_bt_bahama_v2[] = { + { .supply = "smps3", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "ldo7", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "ldo13", .min_uV = 2900000, .max_uV = 3050000 }, +}; + +static struct regulator_bulk_data *regs_bt; +static int regs_bt_count; + +static int marimba_bt(int on) +{ + int rc; + int i; + struct marimba config = { .mod_id = MARIMBA_SLAVE_ID_MARIMBA }; + + struct marimba_config_register { + u8 reg; + u8 value; + u8 mask; + }; + + struct marimba_variant_register { + const size_t size; + const struct marimba_config_register *set; + }; + + const struct marimba_config_register *p; + + u8 version; + + const struct marimba_config_register v10_bt_on[] = { + { 0xE5, 0x0B, 0x0F }, + { 0x05, 0x02, 0x07 }, + { 0x06, 0x88, 0xFF }, + { 0xE7, 0x21, 0x21 }, + { 0xE3, 0x38, 0xFF }, + { 0xE4, 0x06, 0xFF }, + }; + + const struct marimba_config_register v10_bt_off[] = { + { 0xE5, 0x0B, 0x0F }, + { 0x05, 0x08, 0x0F }, + { 0x06, 0x88, 0xFF }, + { 0xE7, 0x00, 0x21 }, + { 0xE3, 0x00, 0xFF }, + { 0xE4, 0x00, 0xFF }, + }; + + const struct marimba_config_register v201_bt_on[] = { + { 0x05, 0x08, 0x07 }, + { 0x06, 0x88, 0xFF }, + { 0xE7, 0x21, 0x21 }, + { 0xE3, 0x38, 0xFF }, + { 0xE4, 0x06, 0xFF }, + }; + + const struct marimba_config_register v201_bt_off[] = { + { 0x05, 0x08, 0x07 }, + { 0x06, 0x88, 0xFF }, + { 0xE7, 0x00, 0x21 }, + { 0xE3, 0x00, 0xFF }, + { 0xE4, 0x00, 0xFF }, + }; + + const struct marimba_config_register v210_bt_on[] = { + { 0xE9, 0x01, 0x01 }, + { 0x06, 0x88, 0xFF }, + { 0xE7, 0x21, 0x21 }, + { 0xE3, 0x38, 0xFF }, + { 0xE4, 0x06, 0xFF }, + }; + + const struct marimba_config_register v210_bt_off[] = { + { 0x06, 0x88, 0xFF }, + { 0xE7, 0x00, 0x21 }, + { 0xE9, 0x00, 0x01 }, + { 0xE3, 0x00, 0xFF }, + { 0xE4, 0x00, 0xFF }, + }; + + const struct marimba_variant_register bt_marimba[2][4] = { + { + { ARRAY_SIZE(v10_bt_off), v10_bt_off }, + { 0, NULL }, + { ARRAY_SIZE(v201_bt_off), v201_bt_off }, + { ARRAY_SIZE(v210_bt_off), v210_bt_off } + }, + { + { ARRAY_SIZE(v10_bt_on), v10_bt_on }, + { 0, NULL }, + { ARRAY_SIZE(v201_bt_on), v201_bt_on }, + { ARRAY_SIZE(v210_bt_on), v210_bt_on } + } + }; + + on = on ? 1 : 0; + + rc = marimba_read_bit_mask(&config, 0x11, &version, 1, 0x1F); + if (rc < 0) { + printk(KERN_ERR + "%s: version read failed: %d\n", + __func__, rc); + return rc; + } + + if ((version >= ARRAY_SIZE(bt_marimba[on])) || + (bt_marimba[on][version].size == 0)) { + printk(KERN_ERR + "%s: unsupported version\n", + __func__); + return -EIO; + } + + p = bt_marimba[on][version].set; + + printk(KERN_INFO "%s: found version %d\n", __func__, version); + + for (i = 0; i < bt_marimba[on][version].size; i++) { + u8 value = (p+i)->value; + rc = marimba_write_bit_mask(&config, + (p+i)->reg, + &value, + sizeof((p+i)->value), + (p+i)->mask); + if (rc < 0) { + printk(KERN_ERR + "%s: reg %d write failed: %d\n", + __func__, (p+i)->reg, rc); + return rc; + } + printk(KERN_INFO "%s: reg 0x%02x value 0x%02x mask 0x%02x\n", + __func__, (p+i)->reg, + value, (p+i)->mask); + } + return 0; +} + +static int bahama_bt(int on) +{ + int rc; + int i; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA }; + + struct bahama_variant_register { + const size_t size; + const struct bahama_config_register *set; + }; + + const struct bahama_config_register *p; + + + const struct bahama_config_register v10_bt_on[] = { + { 0xE9, 0x00, 0xFF }, + { 0xF4, 0x80, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE4, 0x00, 0xFF }, + { 0xE5, 0x00, 0x0F }, +#ifdef CONFIG_WLAN + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0x11, 0x13, 0xFF }, + { 0xE9, 0x21, 0xFF }, + { 0x01, 0x0C, 0x1F }, + { 0x01, 0x08, 0x1F }, + }; + + const struct bahama_config_register v20_bt_on_fm_off[] = { + { 0x11, 0x0C, 0xFF }, + { 0x13, 0x01, 0xFF }, + { 0xF4, 0x80, 0xFF }, + { 0xF0, 0x00, 0xFF }, + { 0xE9, 0x00, 0xFF }, +#ifdef CONFIG_WLAN + { 0x81, 0x00, 0xFF }, + { 0x82, 0x00, 0xFF }, + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF } + }; + + const struct bahama_config_register v20_bt_on_fm_on[] = { + { 0x11, 0x0C, 0xFF }, + { 0x13, 0x01, 0xFF }, + { 0xF4, 0x86, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE9, 0x00, 0xFF }, +#ifdef CONFIG_WLAN + { 0x81, 0x00, 0xFF }, + { 0x82, 0x00, 0xFF }, + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF } + }; + + const struct bahama_config_register v10_bt_off[] = { + { 0xE9, 0x00, 0xFF }, + }; + + const struct bahama_config_register v20_bt_off_fm_off[] = { + { 0xF4, 0x84, 0xFF }, + { 0xF0, 0x04, 0xFF }, + { 0xE9, 0x00, 0xFF } + }; + + const struct bahama_config_register v20_bt_off_fm_on[] = { + { 0xF4, 0x86, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE9, 0x00, 0xFF } + }; + + const struct bahama_variant_register bt_bahama[2][3] = { + { + { ARRAY_SIZE(v10_bt_off), v10_bt_off }, + { ARRAY_SIZE(v20_bt_off_fm_off), v20_bt_off_fm_off }, + { ARRAY_SIZE(v20_bt_off_fm_on), v20_bt_off_fm_on } + }, + { + { ARRAY_SIZE(v10_bt_on), v10_bt_on }, + { ARRAY_SIZE(v20_bt_on_fm_off), v20_bt_on_fm_off }, + { ARRAY_SIZE(v20_bt_on_fm_on), v20_bt_on_fm_on } + } + }; + + u8 offset = 0; /* index into bahama configs */ + + on = on ? 1 : 0; + + + if (bahama_version == VER_2_0) { + if (marimba_get_fm_status(&config)) + offset = 0x01; + } + + p = bt_bahama[on][bahama_version + offset].set; + + dev_info(&msm_bt_power_device.dev, + "%s: found version %d\n", __func__, bahama_version); + + for (i = 0; i < bt_bahama[on][bahama_version + offset].size; i++) { + u8 value = (p+i)->value; + rc = marimba_write_bit_mask(&config, + (p+i)->reg, + &value, + sizeof((p+i)->value), + (p+i)->mask); + if (rc < 0) { + dev_err(&msm_bt_power_device.dev, + "%s: reg %d write failed: %d\n", + __func__, (p+i)->reg, rc); + return rc; + } + dev_info(&msm_bt_power_device.dev, + "%s: reg 0x%02x write value 0x%02x mask 0x%02x\n", + __func__, (p+i)->reg, + value, (p+i)->mask); + } + /* Update BT status */ + if (on) + marimba_set_bt_status(&config, true); + else + marimba_set_bt_status(&config, false); + + return 0; +} + +static int bluetooth_regs_init(int bahama_not_marimba) +{ + int rc = 0; + struct device *const dev = &msm_bt_power_device.dev; + + if (bahama_not_marimba) { + bahama_version = read_bahama_ver(); + + switch (bahama_version) { + case VER_1_0: + regs_bt = regs_bt_bahama_v1; + regs_bt_count = ARRAY_SIZE(regs_bt_bahama_v1); + break; + case VER_2_0: + regs_bt = regs_bt_bahama_v2; + regs_bt_count = ARRAY_SIZE(regs_bt_bahama_v2); + break; + case VER_UNSUPPORTED: + default: + dev_err(dev, + "%s: i2c failure or unsupported version: %d\n", + __func__, bahama_version); + rc = -EIO; + goto out; + } + } else { + regs_bt = regs_bt_marimba; + regs_bt_count = ARRAY_SIZE(regs_bt_marimba); + } + + rc = regulator_bulk_get(&msm_bt_power_device.dev, + regs_bt_count, regs_bt); + if (rc) { + dev_err(dev, "%s: could not get regulators: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(regs_bt_count, regs_bt); + if (rc) { + dev_err(dev, "%s: could not set voltages: %d\n", + __func__, rc); + goto reg_free; + } + + return 0; + +reg_free: + regulator_bulk_free(regs_bt_count, regs_bt); +out: + regs_bt_count = 0; + regs_bt = NULL; + return rc; +} + +static int bluetooth_power(int on) +{ + int rc; + const char *id = "BTPW"; + + int bahama_not_marimba = bahama_present(); + + if (bahama_not_marimba == -1) { + printk(KERN_WARNING "%s: bahama_present: %d\n", + __func__, bahama_not_marimba); + return -ENODEV; + } + + if (unlikely(regs_bt_count == 0)) { + rc = bluetooth_regs_init(bahama_not_marimba); + if (rc) + return rc; + } + + if (on) { + rc = regulator_bulk_enable(regs_bt_count, regs_bt); + if (rc) + return rc; + + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_DO, + PMAPP_CLOCK_VOTE_ON); + if (rc < 0) + return -EIO; + + if (machine_is_msm8x55_svlte_surf() || + machine_is_msm8x55_svlte_ffa()) { + rc = marimba_gpio_config(1); + if (rc < 0) + return -EIO; + } + + rc = (bahama_not_marimba ? bahama_bt(on) : marimba_bt(on)); + if (rc < 0) + return -EIO; + + msleep(10); + + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_DO, + PMAPP_CLOCK_VOTE_PIN_CTRL); + if (rc < 0) + return -EIO; + + if (machine_is_msm8x55_svlte_surf() || + machine_is_msm8x55_svlte_ffa()) { + rc = marimba_gpio_config(0); + if (rc < 0) + return -EIO; + } + + rc = msm_gpios_enable(bt_config_power_on, + ARRAY_SIZE(bt_config_power_on)); + + if (rc < 0) + return rc; + + } else { + rc = msm_gpios_enable(bt_config_power_off, + ARRAY_SIZE(bt_config_power_off)); + if (rc < 0) + return rc; + + /* check for initial RFKILL block (power off) */ + if (platform_get_drvdata(&msm_bt_power_device) == NULL) + goto out; + + rc = (bahama_not_marimba ? bahama_bt(on) : marimba_bt(on)); + if (rc < 0) + return -EIO; + + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_DO, + PMAPP_CLOCK_VOTE_OFF); + if (rc < 0) + return -EIO; + + rc = regulator_bulk_disable(regs_bt_count, regs_bt); + if (rc) + return rc; + + } + +out: + printk(KERN_DEBUG "Bluetooth power switch: %d\n", on); + + return 0; +} + +static void __init bt_power_init(void) +{ + msm_bt_power_device.dev.platform_data = &bluetooth_power; +} +#else +#define bt_power_init(x) do {} while (0) +#endif + +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .voltage_min_design = 2800, + .voltage_max_design = 4300, + .avail_chg_sources = AC_CHG | USB_CHG , + .batt_technology = POWER_SUPPLY_TECHNOLOGY_LION, +}; + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; + +static char *msm_adc_fluid_device_names[] = { + "LTC_ADC1", + "LTC_ADC2", + "LTC_ADC3", +}; + +static char *msm_adc_surf_device_names[] = { + "XO_ADC", +}; + +static struct msm_adc_platform_data msm_adc_pdata; + +static struct platform_device msm_adc_device = { + .name = "msm_adc", + .id = -1, + .dev = { + .platform_data = &msm_adc_pdata, + }, +}; + +#ifdef CONFIG_MSM_SDIO_AL +static struct msm_gpio mdm2ap_status = { + GPIO_CFG(77, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "mdm2ap_status" +}; + + +static int configure_mdm2ap_status(int on) +{ + if (on) + return msm_gpios_request_enable(&mdm2ap_status, 1); + else { + msm_gpios_disable_free(&mdm2ap_status, 1); + return 0; + } +} + +static int get_mdm2ap_status(void) +{ + return gpio_get_value(GPIO_PIN(mdm2ap_status.gpio_cfg)); +} + +static struct sdio_al_platform_data sdio_al_pdata = { + .config_mdm2ap_status = configure_mdm2ap_status, + .get_mdm2ap_status = get_mdm2ap_status, + .allow_sdioc_version_major_2 = 1, + .peer_sdioc_version_minor = 0x0001, + .peer_sdioc_version_major = 0x0003, + .peer_sdioc_boot_version_minor = 0x0001, + .peer_sdioc_boot_version_major = 0x0003, +}; + +struct platform_device msm_device_sdio_al = { + .name = "msm_sdio_al", + .id = -1, + .dev = { + .platform_data = &sdio_al_pdata, + }, +}; + +#endif /* CONFIG_MSM_SDIO_AL */ + static struct platform_device *devices[] __initdata = { #if defined(CONFIG_SERIAL_MSM) || defined(CONFIG_MSM_SERIAL_DEBUGGER) - &msm_device_uart2, + &msm_device_uart2, +#endif +#ifdef CONFIG_MSM_PROC_COMM_REGULATOR + &msm_proccomm_regulator_dev, +#endif + &asoc_msm_pcm, + &asoc_msm_dai0, + &asoc_msm_dai1, +#if defined (CONFIG_SND_MSM_MVS_DAI_SOC) + &asoc_msm_mvs, + &asoc_mvs_dai0, + &asoc_mvs_dai1, #endif &msm_device_smd, + &msm_device_dmov, + &smc91x_device, + &smsc911x_device, + &msm_device_nand, +#ifdef CONFIG_USB_MSM_OTG_72K &msm_device_otg, - &msm_device_hsusb, - &msm_device_hsusb_host, +#ifdef CONFIG_USB_GADGET + &msm_device_gadget_peripheral, +#endif +#endif +#ifdef CONFIG_USB_G_ANDROID + &android_usb_device, +#endif + &qsd_device_spi, + +#ifdef CONFIG_MSM_SSBI + &msm_device_ssbi_pmic1, +#endif +#ifdef CONFIG_I2C_SSBI + &msm_device_ssbi7, +#endif + &android_pmem_device, + &msm_fb_device, +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE + &msm_v4l2_video_overlay_device, +#endif + &msm_migrate_pages_device, + &mddi_toshiba_device, + &lcdc_toshiba_panel_device, +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif + &lcdc_sharp_panel_device, + &android_pmem_adsp_device, + &android_pmem_audio_device, + &msm_device_i2c, + &msm_device_i2c_2, + &msm_device_uart_dm1, + &hs_device, +#ifdef CONFIG_MSM7KV2_AUDIO + &msm_aictl_device, + &msm_mi2s_device, + &msm_lpa_device, + &msm_aux_pcm_device, +#endif + &msm_device_adspdec, + &qup_device_i2c, +#if defined(CONFIG_MARIMBA_CORE) && \ + (defined(CONFIG_MSM_BT_POWER) || defined(CONFIG_MSM_BT_POWER_MODULE)) + &msm_bt_power_device, +#endif + &msm_kgsl_3d0, + &msm_kgsl_2d0, +#ifndef CONFIG_MSM_CAMERA_V4L2 +#ifdef CONFIG_MT9T013 + &msm_camera_sensor_mt9t013, +#endif +#ifdef CONFIG_MT9D112 + &msm_camera_sensor_mt9d112, +#endif +#ifdef CONFIG_WEBCAM_OV9726 + &msm_camera_sensor_ov9726, +#endif +#ifdef CONFIG_S5K3E2FX + &msm_camera_sensor_s5k3e2fx, +#endif +#ifdef CONFIG_MT9P012 + &msm_camera_sensor_mt9p012, +#endif +#ifdef CONFIG_MT9E013 + &msm_camera_sensor_mt9e013, +#endif +#ifdef CONFIG_VX6953 + &msm_camera_sensor_vx6953, +#endif +#ifdef CONFIG_SN12M0PZ + &msm_camera_sensor_sn12m0pz, +#endif +#endif + &msm_device_vidc_720p, +#ifdef CONFIG_MSM_GEMINI + &msm_gemini_device, +#endif +#ifndef CONFIG_MSM_CAMERA_V4L2 +#ifdef CONFIG_MSM_VPE + &msm_vpe_device, +#endif +#endif +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + &msm_device_tsif, +#endif +#ifdef CONFIG_MSM_SDIO_AL + &msm_device_sdio_al, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &qcedev_device, +#endif + + &msm_batt_device, + &msm_adc_device, + &msm_ebi0_thermal, + &msm_ebi1_thermal, + &msm_adsp_device }; +static struct msm_gpio msm_i2c_gpios_hw[] = { + { GPIO_CFG(70, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "i2c_scl" }, + { GPIO_CFG(71, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "i2c_sda" }, +}; + +static struct msm_gpio msm_i2c_gpios_io[] = { + { GPIO_CFG(70, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "i2c_scl" }, + { GPIO_CFG(71, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "i2c_sda" }, +}; + +static struct msm_gpio qup_i2c_gpios_io[] = { + { GPIO_CFG(16, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "qup_scl" }, + { GPIO_CFG(17, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "qup_sda" }, +}; +static struct msm_gpio qup_i2c_gpios_hw[] = { + { GPIO_CFG(16, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "qup_scl" }, + { GPIO_CFG(17, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "qup_sda" }, +}; + +static void +msm_i2c_gpio_config(int adap_id, int config_type) +{ + struct msm_gpio *msm_i2c_table; + + /* Each adapter gets 2 lines from the table */ + if (adap_id > 0) + return; + if (config_type) + msm_i2c_table = &msm_i2c_gpios_hw[adap_id*2]; + else + msm_i2c_table = &msm_i2c_gpios_io[adap_id*2]; + msm_gpios_enable(msm_i2c_table, 2); +} +/*This needs to be enabled only for OEMS*/ +#ifndef CONFIG_QUP_EXCLUSIVE_TO_CAMERA +static struct regulator *qup_vreg; +#endif +static void +qup_i2c_gpio_config(int adap_id, int config_type) +{ + int rc = 0; + struct msm_gpio *qup_i2c_table; + /* Each adapter gets 2 lines from the table */ + if (adap_id != 4) + return; + if (config_type) + qup_i2c_table = qup_i2c_gpios_hw; + else + qup_i2c_table = qup_i2c_gpios_io; + rc = msm_gpios_enable(qup_i2c_table, 2); + if (rc < 0) + printk(KERN_ERR "QUP GPIO enable failed: %d\n", rc); + /*This needs to be enabled only for OEMS*/ +#ifndef CONFIG_QUP_EXCLUSIVE_TO_CAMERA + if (!IS_ERR_OR_NULL(qup_vreg)) { + rc = regulator_enable(qup_vreg); + if (rc) { + pr_err("%s: regulator_enable failed: %d\n", + __func__, rc); + } + } +#endif +} + +static struct msm_i2c_platform_data msm_i2c_pdata = { + .clk_freq = 100000, + .pri_clk = 70, + .pri_dat = 71, + .rmutex = 1, + .rsl_id = "D:I2C02000021", + .msm_i2c_config_gpio = msm_i2c_gpio_config, +}; + +static void __init msm_device_i2c_init(void) +{ + if (msm_gpios_request(msm_i2c_gpios_hw, ARRAY_SIZE(msm_i2c_gpios_hw))) + pr_err("failed to request I2C gpios\n"); + + msm_device_i2c.dev.platform_data = &msm_i2c_pdata; +} + +static struct msm_i2c_platform_data msm_i2c_2_pdata = { + .clk_freq = 100000, + .rmutex = 1, + .rsl_id = "D:I2C02000022", + .msm_i2c_config_gpio = msm_i2c_gpio_config, +}; + +static void __init msm_device_i2c_2_init(void) +{ + msm_device_i2c_2.dev.platform_data = &msm_i2c_2_pdata; +} + +static struct msm_i2c_platform_data qup_i2c_pdata = { + .clk_freq = 384000, + .msm_i2c_config_gpio = qup_i2c_gpio_config, +}; + +static void __init qup_device_i2c_init(void) +{ + if (msm_gpios_request(qup_i2c_gpios_hw, ARRAY_SIZE(qup_i2c_gpios_hw))) + pr_err("failed to request I2C gpios\n"); + + qup_device_i2c.dev.platform_data = &qup_i2c_pdata; + /*This needs to be enabled only for OEMS*/ +#ifndef CONFIG_QUP_EXCLUSIVE_TO_CAMERA + qup_vreg = regulator_get(&qup_device_i2c.dev, "lvsw1"); + if (IS_ERR(qup_vreg)) { + dev_err(&qup_device_i2c.dev, + "%s: regulator_get failed: %ld\n", + __func__, PTR_ERR(qup_vreg)); + } +#endif +} + +#ifdef CONFIG_I2C_SSBI +static struct msm_i2c_ssbi_platform_data msm_i2c_ssbi7_pdata = { + .rsl_id = "D:CODEC_SSBI", + .controller_type = MSM_SBI_CTRL_SSBI, +}; +#endif + static void __init msm7x30_init_irq(void) { msm_init_irq(); } +static struct msm_gpio msm_nand_ebi2_cfg_data[] = { + {GPIO_CFG(86, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "ebi2_cs1"}, + {GPIO_CFG(115, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "ebi2_busy1"}, +}; + +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC4_SUPPORT)) + +struct sdcc_gpio { + struct msm_gpio *cfg_data; + uint32_t size; + struct msm_gpio *sleep_cfg_data; +}; +#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) +static struct msm_gpio sdc1_lvlshft_cfg_data[] = { + {GPIO_CFG(35, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_16MA), "sdc1_lvlshft"}, +}; +#endif +static struct msm_gpio sdc1_cfg_data[] = { + {GPIO_CFG(38, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "sdc1_clk"}, + {GPIO_CFG(39, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_cmd"}, + {GPIO_CFG(40, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_3"}, + {GPIO_CFG(41, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_2"}, + {GPIO_CFG(42, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_1"}, + {GPIO_CFG(43, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_0"}, +}; + +static struct msm_gpio sdc2_cfg_data[] = { + {GPIO_CFG(64, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "sdc2_clk"}, + {GPIO_CFG(65, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_cmd"}, + {GPIO_CFG(66, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_3"}, + {GPIO_CFG(67, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_2"}, + {GPIO_CFG(68, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_1"}, + {GPIO_CFG(69, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_0"}, + +#ifdef CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT + {GPIO_CFG(115, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_4"}, + {GPIO_CFG(114, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_5"}, + {GPIO_CFG(113, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_6"}, + {GPIO_CFG(112, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_7"}, +#endif +}; + +static struct msm_gpio sdc3_cfg_data[] = { + {GPIO_CFG(110, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "sdc3_clk"}, + {GPIO_CFG(111, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_cmd"}, + {GPIO_CFG(116, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_3"}, + {GPIO_CFG(117, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_2"}, + {GPIO_CFG(118, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_1"}, + {GPIO_CFG(119, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_0"}, +}; + +static struct msm_gpio sdc3_sleep_cfg_data[] = { + {GPIO_CFG(110, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc3_clk"}, + {GPIO_CFG(111, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc3_cmd"}, + {GPIO_CFG(116, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc3_dat_3"}, + {GPIO_CFG(117, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc3_dat_2"}, + {GPIO_CFG(118, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc3_dat_1"}, + {GPIO_CFG(119, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "sdc3_dat_0"}, +}; + +static struct msm_gpio sdc4_cfg_data[] = { + {GPIO_CFG(58, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), "sdc4_clk"}, + {GPIO_CFG(59, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_cmd"}, + {GPIO_CFG(60, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_3"}, + {GPIO_CFG(61, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_2"}, + {GPIO_CFG(62, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_1"}, + {GPIO_CFG(63, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_0"}, +}; + +static struct sdcc_gpio sdcc_cfg_data[] = { + { + .cfg_data = sdc1_cfg_data, + .size = ARRAY_SIZE(sdc1_cfg_data), + .sleep_cfg_data = NULL, + }, + { + .cfg_data = sdc2_cfg_data, + .size = ARRAY_SIZE(sdc2_cfg_data), + .sleep_cfg_data = NULL, + }, + { + .cfg_data = sdc3_cfg_data, + .size = ARRAY_SIZE(sdc3_cfg_data), + .sleep_cfg_data = sdc3_sleep_cfg_data, + }, + { + .cfg_data = sdc4_cfg_data, + .size = ARRAY_SIZE(sdc4_cfg_data), + .sleep_cfg_data = NULL, + }, +}; + +static struct regulator *sdcc_vreg_data[ARRAY_SIZE(sdcc_cfg_data)]; + +static unsigned long vreg_sts, gpio_sts; + +static uint32_t msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int rc = 0; + struct sdcc_gpio *curr; + + curr = &sdcc_cfg_data[dev_id - 1]; + + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return rc; + + if (enable) { + set_bit(dev_id, &gpio_sts); + rc = msm_gpios_request_enable(curr->cfg_data, curr->size); + if (rc) + printk(KERN_ERR "%s: Failed to turn on GPIOs for slot %d\n", + __func__, dev_id); + } else { + clear_bit(dev_id, &gpio_sts); + if (curr->sleep_cfg_data) { + msm_gpios_enable(curr->sleep_cfg_data, curr->size); + msm_gpios_free(curr->sleep_cfg_data, curr->size); + } else { + msm_gpios_disable_free(curr->cfg_data, curr->size); + } + } + + return rc; +} + +static uint32_t msm_sdcc_setup_vreg(int dev_id, unsigned int enable) +{ + int rc = 0; + struct regulator *curr = sdcc_vreg_data[dev_id - 1]; + static int enabled_once[] = {0, 0, 0, 0}; + + if (test_bit(dev_id, &vreg_sts) == enable) + return rc; + + if (dev_id == 4) { + if (enable) { + pr_debug("Enable Vdd dev_%d\n", dev_id); + gpio_set_value_cansleep( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC4_PWR_EN_N), + 0); + set_bit(dev_id, &vreg_sts); + } else { + pr_debug("Disable Vdd dev_%d\n", dev_id); + gpio_set_value_cansleep( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC4_PWR_EN_N), + 1); + clear_bit(dev_id, &vreg_sts); + } + } + + if (!enable || enabled_once[dev_id - 1]) + return 0; + if (!curr) + return -ENODEV; + + if (IS_ERR(curr)) + return PTR_ERR(curr); + + if (enable) { + set_bit(dev_id, &vreg_sts); + + rc = regulator_enable(curr); + if (rc) + pr_err("%s: could not enable regulator: %d\n", + __func__, rc); + enabled_once[dev_id - 1] = 1; + } else { + clear_bit(dev_id, &vreg_sts); + + rc = regulator_disable(curr); + if (rc) + pr_err("%s: could not disable regulator: %d\n", + __func__, rc); + } + return rc; +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + rc = msm_sdcc_setup_gpio(pdev->id, (vdd ? 1 : 0)); + if (rc) + goto out; + + if (pdev->id == 4) /* S3 is always ON and cannot be disabled */ + rc = msm_sdcc_setup_vreg(pdev->id, (vdd ? 1 : 0)); +out: + return rc; +} + +#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) && \ + defined(CONFIG_CSDIO_VENDOR_ID) && \ + defined(CONFIG_CSDIO_DEVICE_ID) && \ + (CONFIG_CSDIO_VENDOR_ID == 0x70 && CONFIG_CSDIO_DEVICE_ID == 0x1117) + +#define MBP_ON 1 +#define MBP_OFF 0 + +#define MBP_RESET_N \ + GPIO_CFG(44, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA) +#define MBP_INT0 \ + GPIO_CFG(46, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA) + +#define MBP_MODE_CTRL_0 \ + GPIO_CFG(35, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA) +#define MBP_MODE_CTRL_1 \ + GPIO_CFG(36, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA) +#define MBP_MODE_CTRL_2 \ + GPIO_CFG(34, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA) +#define TSIF_EN \ + GPIO_CFG(35, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_DATA \ + GPIO_CFG(36, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_CLK \ + GPIO_CFG(34, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) + +static struct msm_gpio mbp_cfg_data[] = { + {GPIO_CFG(44, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), + "mbp_reset"}, + {GPIO_CFG(85, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), + "mbp_io_voltage"}, +}; + +static int mbp_config_gpios_pre_init(int enable) +{ + int rc = 0; + + if (enable) { + rc = msm_gpios_request_enable(mbp_cfg_data, + ARRAY_SIZE(mbp_cfg_data)); + if (rc) { + printk(KERN_ERR + "%s: Failed to turnon GPIOs for mbp chip(%d)\n", + __func__, rc); + } + } else + msm_gpios_disable_free(mbp_cfg_data, ARRAY_SIZE(mbp_cfg_data)); + return rc; +} + +static struct regulator_bulk_data mbp_regs_io[2]; +static struct regulator_bulk_data mbp_regs_rf[2]; +static struct regulator_bulk_data mbp_regs_adc[1]; +static struct regulator_bulk_data mbp_regs_core[1]; + +static int mbp_init_regs(struct device *dev) +{ + struct regulator_bulk_data regs[] = { + /* Analog and I/O regs */ + { .supply = "gp4", .min_uV = 2600000, .max_uV = 2600000 }, + { .supply = "s3", .min_uV = 1800000, .max_uV = 1800000 }, + /* RF regs */ + { .supply = "s2", .min_uV = 1300000, .max_uV = 1300000 }, + { .supply = "rf", .min_uV = 2600000, .max_uV = 2600000 }, + /* ADC regs */ + { .supply = "s4", .min_uV = 2200000, .max_uV = 2200000 }, + /* Core regs */ + { .supply = "gp16", .min_uV = 1200000, .max_uV = 1200000 }, + }; + + struct regulator_bulk_data *regptr = regs; + int rc; + + rc = regulator_bulk_get(dev, ARRAY_SIZE(regs), regs); + + if (rc) { + dev_err(dev, "%s: could not get regulators: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs), regs); + + if (rc) { + dev_err(dev, "%s: could not set voltages: %d\n", + __func__, rc); + goto reg_free; + } + + memcpy(mbp_regs_io, regptr, sizeof(mbp_regs_io)); + regptr += ARRAY_SIZE(mbp_regs_io); + + memcpy(mbp_regs_rf, regptr, sizeof(mbp_regs_rf)); + regptr += ARRAY_SIZE(mbp_regs_rf); + + memcpy(mbp_regs_adc, regptr, sizeof(mbp_regs_adc)); + regptr += ARRAY_SIZE(mbp_regs_adc); + + memcpy(mbp_regs_core, regptr, sizeof(mbp_regs_core)); + + return 0; + +reg_free: + regulator_bulk_free(ARRAY_SIZE(regs), regs); +out: + return rc; +} + +static int mbp_setup_rf_vregs(int state) +{ + return state ? + regulator_bulk_enable(ARRAY_SIZE(mbp_regs_rf), mbp_regs_rf) : + regulator_bulk_disable(ARRAY_SIZE(mbp_regs_rf), mbp_regs_rf); +} + +static int mbp_setup_vregs(int state) +{ + return state ? + regulator_bulk_enable(ARRAY_SIZE(mbp_regs_io), mbp_regs_io) : + regulator_bulk_disable(ARRAY_SIZE(mbp_regs_io), mbp_regs_io); +} + +static int mbp_set_tcxo_en(int enable) +{ + int rc; + const char *id = "UBMC"; + struct vreg *vreg_analog = NULL; + + rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A1, + enable ? PMAPP_CLOCK_VOTE_ON : PMAPP_CLOCK_VOTE_OFF); + if (rc < 0) { + printk(KERN_ERR "%s: unable to %svote for a1 clk\n", + __func__, enable ? "" : "de-"); + return -EIO; + } + return rc; +} + +static void mbp_set_freeze_io(int state) +{ + if (state) + gpio_set_value(85, 0); + else + gpio_set_value(85, 1); +} + +static int mbp_set_core_voltage_en(int enable) +{ + static bool is_enabled; + int rc = 0; + + if (enable && !is_enabled) { + rc = regulator_bulk_enable(ARRAY_SIZE(mbp_regs_core), + mbp_regs_core); + if (rc) { + pr_err("%s: could not enable regulators: %d\n", + __func__, rc); + } else { + is_enabled = true; + } + } + + return rc; +} + +static void mbp_set_reset(int state) +{ + if (state) + gpio_set_value(GPIO_PIN(MBP_RESET_N), 0); + else + gpio_set_value(GPIO_PIN(MBP_RESET_N), 1); +} + +static int mbp_config_interface_mode(int state) +{ + if (state) { + gpio_tlmm_config(MBP_MODE_CTRL_0, GPIO_CFG_ENABLE); + gpio_tlmm_config(MBP_MODE_CTRL_1, GPIO_CFG_ENABLE); + gpio_tlmm_config(MBP_MODE_CTRL_2, GPIO_CFG_ENABLE); + gpio_set_value(GPIO_PIN(MBP_MODE_CTRL_0), 0); + gpio_set_value(GPIO_PIN(MBP_MODE_CTRL_1), 1); + gpio_set_value(GPIO_PIN(MBP_MODE_CTRL_2), 0); + } else { + gpio_tlmm_config(MBP_MODE_CTRL_0, GPIO_CFG_DISABLE); + gpio_tlmm_config(MBP_MODE_CTRL_1, GPIO_CFG_DISABLE); + gpio_tlmm_config(MBP_MODE_CTRL_2, GPIO_CFG_DISABLE); + } + return 0; +} + +static int mbp_setup_adc_vregs(int state) +{ + return state ? + regulator_bulk_enable(ARRAY_SIZE(mbp_regs_adc), mbp_regs_adc) : + regulator_bulk_disable(ARRAY_SIZE(mbp_regs_adc), mbp_regs_adc); +} + +static int mbp_power_up(void) +{ + int rc; + + rc = mbp_config_gpios_pre_init(MBP_ON); + if (rc) + goto exit; + pr_debug("%s: mbp_config_gpios_pre_init() done\n", __func__); + + rc = mbp_setup_vregs(MBP_ON); + if (rc) + goto exit; + pr_debug("%s: gp4 (2.6) and s3 (1.8) done\n", __func__); + + rc = mbp_set_tcxo_en(MBP_ON); + if (rc) + goto exit; + pr_debug("%s: tcxo clock done\n", __func__); + + mbp_set_freeze_io(MBP_OFF); + pr_debug("%s: set gpio 85 to 1 done\n", __func__); + + udelay(100); + mbp_set_reset(MBP_ON); + + udelay(300); + rc = mbp_config_interface_mode(MBP_ON); + if (rc) + goto exit; + pr_debug("%s: mbp_config_interface_mode() done\n", __func__); + + udelay(100 + mbp_set_core_voltage_en(MBP_ON)); + pr_debug("%s: power gp16 1.2V done\n", __func__); + + mbp_set_freeze_io(MBP_ON); + pr_debug("%s: set gpio 85 to 0 done\n", __func__); + + udelay(100); + + rc = mbp_setup_rf_vregs(MBP_ON); + if (rc) + goto exit; + pr_debug("%s: s2 1.3V and rf 2.6V done\n", __func__); + + rc = mbp_setup_adc_vregs(MBP_ON); + if (rc) + goto exit; + pr_debug("%s: s4 2.2V done\n", __func__); + + udelay(200); + + mbp_set_reset(MBP_OFF); + pr_debug("%s: close gpio 44 done\n", __func__); + + msleep(20); +exit: + return rc; +} + +static int mbp_power_down(void) +{ + int rc; + + mbp_set_reset(MBP_ON); + pr_debug("%s: mbp_set_reset(MBP_ON) done\n", __func__); + + udelay(100); + + rc = mbp_setup_adc_vregs(MBP_OFF); + if (rc) + goto exit; + pr_debug("%s: vreg_disable(vreg_adc) done\n", __func__); + + udelay(5); + + rc = mbp_setup_rf_vregs(MBP_OFF); + if (rc) + goto exit; + pr_debug("%s: mbp_setup_rf_vregs(MBP_OFF) done\n", __func__); + + udelay(5); + + mbp_set_freeze_io(MBP_OFF); + pr_debug("%s: mbp_set_freeze_io(MBP_OFF) done\n", __func__); + + udelay(100); + rc = mbp_set_core_voltage_en(MBP_OFF); + if (rc) + goto exit; + pr_debug("%s: mbp_set_core_voltage_en(MBP_OFF) done\n", __func__); + + rc = mbp_set_tcxo_en(MBP_OFF); + if (rc) + goto exit; + pr_debug("%s: mbp_set_tcxo_en(MBP_OFF) done\n", __func__); + + rc = mbp_setup_vregs(MBP_OFF); + if (rc) + goto exit; + pr_debug("%s: mbp_setup_vregs(MBP_OFF) done\n", __func__); + + rc = mbp_config_gpios_pre_init(MBP_OFF); + if (rc) + goto exit; +exit: + return rc; +} + +static void (*mbp_status_notify_cb)(int card_present, void *dev_id); +static void *mbp_status_notify_cb_devid; +static int mbp_power_status; +static int mbp_power_init_done; + +static uint32_t mbp_setup_power(struct device *dv, + unsigned int power_status) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + + if (power_status == mbp_power_status) + goto exit; + if (power_status) { + pr_debug("turn on power of mbp slot"); + rc = mbp_power_up(); + mbp_power_status = 1; + } else { + pr_debug("turn off power of mbp slot"); + rc = mbp_power_down(); + mbp_power_status = 0; + } +exit: + return rc; +}; + +int mbp_register_status_notify(void (*callback)(int, void *), + void *dev_id) +{ + mbp_status_notify_cb = callback; + mbp_status_notify_cb_devid = dev_id; + return 0; +} + +static unsigned int mbp_status(struct device *dev) +{ + return mbp_power_status; +} + +static uint32_t msm_sdcc_setup_power_mbp(struct device *dv, unsigned int vdd) +{ + struct platform_device *pdev; + uint32_t rc = 0; + + pdev = container_of(dv, struct platform_device, dev); + rc = msm_sdcc_setup_power(dv, vdd); + if (rc) { + pr_err("%s: Failed to setup power (%d)\n", + __func__, rc); + goto out; + } + if (!mbp_power_init_done) { + rc = mbp_init_regs(dv); + if (rc) { + dev_err(dv, "%s: regulator init failed: %d\n", + __func__, rc); + goto out; + } + mbp_setup_power(dv, 1); + mbp_setup_power(dv, 0); + mbp_power_init_done = 1; + } + if (vdd >= 0x8000) { + rc = mbp_setup_power(dv, (0x8000 == vdd) ? 0 : 1); + if (rc) { + pr_err("%s: Failed to config mbp chip power (%d)\n", + __func__, rc); + goto out; + } + if (mbp_status_notify_cb) { + mbp_status_notify_cb(mbp_power_status, + mbp_status_notify_cb_devid); + } + } +out: + /* should return 0 only */ + return 0; +} + +#endif + +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION +static unsigned int msm7x30_sdcc_slot_status(struct device *dev) +{ + return (unsigned int) + gpio_get_value_cansleep( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SD_DET - 1)); +} +#endif + +static int msm_sdcc_get_wpswitch(struct device *dv) +{ + void __iomem *wp_addr = 0; + uint32_t ret = 0; + struct platform_device *pdev; + + if (!(machine_is_msm7x30_surf())) + return -1; + pdev = container_of(dv, struct platform_device, dev); + + wp_addr = ioremap(FPGA_SDCC_STATUS, 4); + if (!wp_addr) { + pr_err("%s: Could not remap %x\n", __func__, FPGA_SDCC_STATUS); + return -ENOMEM; + } + + ret = (((readl(wp_addr) >> 4) >> (pdev->id-1)) & 0x01); + pr_info("%s: WP Status for Slot %d = 0x%x \n", __func__, + pdev->id, ret); + iounmap(wp_addr); + + return ret; +} +#endif + +#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) +#if defined(CONFIG_CSDIO_VENDOR_ID) && \ + defined(CONFIG_CSDIO_DEVICE_ID) && \ + (CONFIG_CSDIO_VENDOR_ID == 0x70 && CONFIG_CSDIO_DEVICE_ID == 0x1117) +static struct mmc_platform_data msm7x30_sdc1_data = { + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power_mbp, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .status = mbp_status, + .register_status_notify = mbp_register_status_notify, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 24576000, + .nonremovable = 0, +}; +#else +static struct mmc_platform_data msm7x30_sdc1_data = { + .ocr_mask = MMC_VDD_165_195, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct mmc_platform_data msm7x30_sdc2_data = { + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_27_28, + .translate_vdd = msm_sdcc_setup_power, +#ifdef CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 1, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data msm7x30_sdc3_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .sdiowakeup_irq = MSM_GPIO_TO_INT(118), + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct mmc_platform_data msm7x30_sdc4_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + .status = msm7x30_sdcc_slot_status, + .status_irq = PM8058_GPIO_IRQ(PMIC8058_IRQ_BASE, PMIC_GPIO_SD_DET - 1), + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +#endif + .wpswitch = msm_sdcc_get_wpswitch, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 24576000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static int msm_sdc1_lvlshft_enable(void) +{ + static struct regulator *ldo5; + int rc; + + /* Enable LDO5, an input to the FET that powers slot 1 */ + + ldo5 = regulator_get(NULL, "ldo5"); + + if (IS_ERR(ldo5)) { + rc = PTR_ERR(ldo5); + pr_err("%s: could not get ldo5: %d\n", __func__, rc); + goto out; + } + + rc = regulator_set_voltage(ldo5, 2850000, 2850000); + if (rc) { + pr_err("%s: could not set ldo5 voltage: %d\n", __func__, rc); + goto ldo5_free; + } + + rc = regulator_enable(ldo5); + if (rc) { + pr_err("%s: could not enable ldo5: %d\n", __func__, rc); + goto ldo5_free; + } + + /* Enable GPIO 35, to turn on the FET that powers slot 1 */ + rc = msm_gpios_request_enable(sdc1_lvlshft_cfg_data, + ARRAY_SIZE(sdc1_lvlshft_cfg_data)); + if (rc) + printk(KERN_ERR "%s: Failed to enable GPIO 35\n", __func__); + + rc = gpio_direction_output(GPIO_PIN(sdc1_lvlshft_cfg_data[0].gpio_cfg), + 1); + if (rc) + printk(KERN_ERR "%s: Failed to turn on GPIO 35\n", __func__); + + return 0; + +ldo5_free: + regulator_put(ldo5); +out: + ldo5 = NULL; + return rc; +} +#endif + +static int mmc_regulator_init(int sdcc_no, const char *supply, int uV) +{ + int rc; + + BUG_ON(sdcc_no < 1 || sdcc_no > 4); + + sdcc_no--; + + sdcc_vreg_data[sdcc_no] = regulator_get(NULL, supply); + + if (IS_ERR(sdcc_vreg_data[sdcc_no])) { + rc = PTR_ERR(sdcc_vreg_data[sdcc_no]); + pr_err("%s: could not get regulator \"%s\": %d\n", + __func__, supply, rc); + goto out; + } + + rc = regulator_set_voltage(sdcc_vreg_data[sdcc_no], uV, uV); + + if (rc) { + pr_err("%s: could not set voltage for \"%s\" to %d uV: %d\n", + __func__, supply, uV, rc); + goto reg_free; + } + + return rc; + +reg_free: + regulator_put(sdcc_vreg_data[sdcc_no]); +out: + sdcc_vreg_data[sdcc_no] = NULL; + return rc; +} + +static void __init msm7x30_init_mmc(void) +{ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + if (mmc_regulator_init(1, "s3", 1800000)) + goto out1; + + if (machine_is_msm7x30_fluid()) { + msm7x30_sdc1_data.ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29; + if (msm_sdc1_lvlshft_enable()) { + pr_err("%s: could not enable level shift\n"); + goto out1; + } + } + + msm_add_sdcc(1, &msm7x30_sdc1_data); +out1: +#endif +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + if (mmc_regulator_init(2, "s3", 1800000)) + goto out2; + + if (machine_is_msm8x55_svlte_surf()) + msm7x30_sdc2_data.msmsdcc_fmax = 24576000; + if (machine_is_msm8x55_svlte_surf() || + machine_is_msm8x55_svlte_ffa()) { + msm7x30_sdc2_data.sdiowakeup_irq = MSM_GPIO_TO_INT(68); + msm7x30_sdc2_data.is_sdio_al_client = 1; + } + + msm_add_sdcc(2, &msm7x30_sdc2_data); +out2: +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + if (mmc_regulator_init(3, "s3", 1800000)) + goto out3; + + msm_sdcc_setup_gpio(3, 1); + msm_add_sdcc(3, &msm7x30_sdc3_data); +out3: +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + if (mmc_regulator_init(4, "mmc", 2850000)) + return; + + msm_add_sdcc(4, &msm7x30_sdc4_data); +#endif + +} + +static void __init msm7x30_init_nand(void) +{ + char *build_id; + struct flash_platform_data *plat_data; + + build_id = socinfo_get_build_id(); + if (build_id == NULL) { + pr_err("%s: Build ID not available from socinfo\n", __func__); + return; + } + + if (build_id[8] == 'C' && + !msm_gpios_request_enable(msm_nand_ebi2_cfg_data, + ARRAY_SIZE(msm_nand_ebi2_cfg_data))) { + plat_data = msm_device_nand.dev.platform_data; + plat_data->interleave = 1; + printk(KERN_INFO "%s: Interleave mode Build ID found\n", + __func__); + } +} + +#ifdef CONFIG_SERIAL_MSM_CONSOLE +static struct msm_gpio uart2_config_data[] = { + { GPIO_CFG(49, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "UART2_RFR"}, + { GPIO_CFG(50, 2, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "UART2_CTS"}, + { GPIO_CFG(51, 2, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "UART2_Rx"}, + { GPIO_CFG(52, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), "UART2_Tx"}, +}; + +static void msm7x30_init_uart2(void) +{ + msm_gpios_request_enable(uart2_config_data, + ARRAY_SIZE(uart2_config_data)); + +} +#endif + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define TSIF_B_SYNC GPIO_CFG(37, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_B_DATA GPIO_CFG(36, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_B_EN GPIO_CFG(35, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_B_CLK GPIO_CFG(34, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) + +static const struct msm_gpio tsif_gpios[] = { + { .gpio_cfg = TSIF_B_CLK, .label = "tsif_clk", }, + { .gpio_cfg = TSIF_B_EN, .label = "tsif_en", }, + { .gpio_cfg = TSIF_B_DATA, .label = "tsif_data", }, + { .gpio_cfg = TSIF_B_SYNC, .label = "tsif_sync", }, +}; + +static struct msm_tsif_platform_data tsif_platform_data = { + .num_gpios = ARRAY_SIZE(tsif_gpios), + .gpios = tsif_gpios, + .tsif_pclk = "iface_clk", + .tsif_ref_clk = "ref_clk", +}; +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + +static void __init pmic8058_leds_init(void) +{ + if (machine_is_msm7x30_surf()) + pm8058_7x30_data.leds_pdata = &pm8058_surf_leds_data; + else if (!machine_is_msm7x30_fluid()) + pm8058_7x30_data.leds_pdata = &pm8058_ffa_leds_data; + else if (machine_is_msm7x30_fluid()) + pm8058_7x30_data.leds_pdata = &pm8058_fluid_leds_data; +} + +static struct msm_spm_platform_data msm_spm_data __initdata = { + .reg_base_addr = MSM_SAW0_BASE, + + .reg_init_values[MSM_SPM_REG_SAW_CFG] = 0x05, + .reg_init_values[MSM_SPM_REG_SAW_SPM_CTL] = 0x18, + .reg_init_values[MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0x00006666, + .reg_init_values[MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0xFF000666, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x03, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x00, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_RST_EN] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x00, + + .awake_vlevel = 0xF2, + .retention_vlevel = 0xE0, + .collapse_vlevel = 0x72, + .retention_mid_vlevel = 0xE0, + .collapse_mid_vlevel = 0xE0, + + .vctl_timeout_us = 50, +}; + +#if defined(CONFIG_TOUCHSCREEN_TSC2007) || \ + defined(CONFIG_TOUCHSCREEN_TSC2007_MODULE) + +#define TSC2007_TS_PEN_INT 20 + +static struct msm_gpio tsc2007_config_data[] = { + { GPIO_CFG(TSC2007_TS_PEN_INT, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "tsc2007_irq" }, +}; + +static struct regulator_bulk_data tsc2007_regs[] = { + { .supply = "s3", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "s2", .min_uV = 1300000, .max_uV = 1300000 }, +}; + +static int tsc2007_init(void) +{ + int rc; + + rc = regulator_bulk_get(NULL, ARRAY_SIZE(tsc2007_regs), tsc2007_regs); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); + + if (rc) { + pr_err("%s: could not set voltages: %d\n", __func__, rc); + goto reg_free; + } + + rc = regulator_bulk_enable(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); + + if (rc) { + pr_err("%s: could not enable regulators: %d\n", __func__, rc); + goto reg_free; + } + + rc = msm_gpios_request_enable(tsc2007_config_data, + ARRAY_SIZE(tsc2007_config_data)); + if (rc) { + pr_err("%s: Unable to request gpios\n", __func__); + goto reg_disable; + } + + return 0; + +reg_disable: + regulator_bulk_disable(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); +reg_free: + regulator_bulk_free(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); +out: + return rc; +} + +static int tsc2007_get_pendown_state(void) +{ + int rc; + + rc = gpio_get_value(TSC2007_TS_PEN_INT); + if (rc < 0) { + pr_err("%s: MSM GPIO %d read failed\n", __func__, + TSC2007_TS_PEN_INT); + return rc; + } + + return (rc == 0 ? 1 : 0); +} + +static void tsc2007_exit(void) +{ + + regulator_bulk_disable(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); + regulator_bulk_free(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); + + msm_gpios_disable_free(tsc2007_config_data, + ARRAY_SIZE(tsc2007_config_data)); +} + +static int tsc2007_power_shutdown(bool enable) +{ + int rc; + + rc = (enable == false) ? + regulator_bulk_enable(ARRAY_SIZE(tsc2007_regs), tsc2007_regs) : + regulator_bulk_disable(ARRAY_SIZE(tsc2007_regs), tsc2007_regs); + + if (rc) { + pr_err("%s: could not %sable regulators: %d\n", + __func__, enable ? "dis" : "en", rc); + return rc; + } + + if (enable == false) + msleep(20); + + return 0; +} + +static struct tsc2007_platform_data tsc2007_ts_data = { + .model = 2007, + .x_plate_ohms = 300, + .min_x = 210, + .max_x = 3832, + .min_y = 150, + .max_y = 3936, + .irq_flags = IRQF_TRIGGER_LOW, + .init_platform_hw = tsc2007_init, + .exit_platform_hw = tsc2007_exit, + .power_shutdown = tsc2007_power_shutdown, + .invert_x = true, + .invert_y = true, + /* REVISIT: Temporary fix for reversed pressure */ + .invert_z1 = true, + .invert_z2 = true, + .get_pendown_state = tsc2007_get_pendown_state, +}; + +static struct i2c_board_info tsc_i2c_board_info[] = { + { + I2C_BOARD_INFO("tsc2007", 0x48), + .irq = MSM_GPIO_TO_INT(TSC2007_TS_PEN_INT), + .platform_data = &tsc2007_ts_data, + }, +}; +#endif + +static struct regulator_bulk_data regs_isa1200[] = { + { .supply = "gp7", .min_uV = 1800000, .max_uV = 1800000 }, + { .supply = "gp10", .min_uV = 2600000, .max_uV = 2600000 }, +}; + +static int isa1200_power(int vreg_on) +{ + int rc = 0; + + rc = vreg_on ? + regulator_bulk_enable(ARRAY_SIZE(regs_isa1200), regs_isa1200) : + regulator_bulk_disable(ARRAY_SIZE(regs_isa1200), regs_isa1200); + + if (rc) { + pr_err("%s: could not %sable regulators: %d\n", + __func__, vreg_on ? "en" : "dis", rc); + goto out; + } + + /* vote for DO buffer */ + rc = pmapp_clock_vote("VIBR", PMAPP_CLOCK_ID_DO, + vreg_on ? PMAPP_CLOCK_VOTE_ON : PMAPP_CLOCK_VOTE_OFF); + if (rc) { + pr_err("%s: unable to %svote for d0 clk\n", + __func__, vreg_on ? "" : "de-"); + goto vreg_fail; + } + + return 0; + +vreg_fail: + if (vreg_on) + regulator_bulk_disable(ARRAY_SIZE(regs_isa1200), regs_isa1200); + else + regulator_bulk_enable(ARRAY_SIZE(regs_isa1200), regs_isa1200); +out: + return rc; +} + +static int isa1200_dev_setup(bool enable) +{ + int rc; + + if (enable == true) { + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_isa1200), + regs_isa1200); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_isa1200), + regs_isa1200); + if (rc) { + pr_err("%s: could not set voltages: %d\n", + __func__, rc); + goto reg_free; + } + + rc = gpio_tlmm_config(GPIO_CFG(HAP_LVL_SHFT_MSM_GPIO, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: Could not configure gpio %d\n", + __func__, HAP_LVL_SHFT_MSM_GPIO); + goto reg_free; + } + + rc = gpio_request(HAP_LVL_SHFT_MSM_GPIO, "haptics_shft_lvl_oe"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, HAP_LVL_SHFT_MSM_GPIO, rc); + goto reg_free; + } + + gpio_set_value(HAP_LVL_SHFT_MSM_GPIO, 1); + } else { + regulator_bulk_free(ARRAY_SIZE(regs_isa1200), regs_isa1200); + gpio_free(HAP_LVL_SHFT_MSM_GPIO); + } + + return 0; + +reg_free: + regulator_bulk_free(ARRAY_SIZE(regs_isa1200), regs_isa1200); +out: + return rc; +} +static struct isa1200_platform_data isa1200_1_pdata = { + .name = "vibrator", + .power_on = isa1200_power, + .dev_setup = isa1200_dev_setup, + .pwm_ch_id = 1, /*channel id*/ + /*gpio to enable haptic*/ + .hap_en_gpio = PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HAP_ENABLE), + .hap_len_gpio = -1, + .max_timeout = 15000, + .mode_ctrl = PWM_GEN_MODE, + .pwm_fd = { + .pwm_div = 256, + }, + .is_erm = false, + .smart_en = true, + .ext_clk_en = true, + .chip_en = 1, +}; + +static struct i2c_board_info msm_isa1200_board_info[] = { + { + I2C_BOARD_INFO("isa1200_1", 0x90>>1), + .platform_data = &isa1200_1_pdata, + }, +}; + + +static int kp_flip_mpp_config(void) +{ + struct pm8xxx_mpp_config_data kp_flip_mpp = { + .type = PM8XXX_MPP_TYPE_D_INPUT, + .level = PM8018_MPP_DIG_LEVEL_S3, + .control = PM8XXX_MPP_DIN_TO_INT, + }; + + return pm8xxx_mpp_config(PM8058_MPP_PM_TO_SYS(PM_FLIP_MPP), + &kp_flip_mpp); +} + +static struct flip_switch_pdata flip_switch_data = { + .name = "kp_flip_switch", + .flip_gpio = PM8058_GPIO_PM_TO_SYS(PM8058_GPIOS) + PM_FLIP_MPP, + .left_key = KEY_OPEN, + .right_key = KEY_CLOSE, + .active_low = 0, + .wakeup = 1, + .flip_mpp_config = kp_flip_mpp_config, +}; + +static struct platform_device flip_switch_device = { + .name = "kp_flip_switch", + .id = -1, + .dev = { + .platform_data = &flip_switch_data, + } +}; + +static struct regulator_bulk_data regs_tma300[] = { + { .supply = "gp6", .min_uV = 3050000, .max_uV = 3100000 }, + { .supply = "gp7", .min_uV = 1800000, .max_uV = 1800000 }, +}; + +static int tma300_power(int vreg_on) +{ + int rc; + + rc = vreg_on ? + regulator_bulk_enable(ARRAY_SIZE(regs_tma300), regs_tma300) : + regulator_bulk_disable(ARRAY_SIZE(regs_tma300), regs_tma300); + + if (rc) + pr_err("%s: could not %sable regulators: %d\n", + __func__, vreg_on ? "en" : "dis", rc); + return rc; +} + +#define TS_GPIO_IRQ 150 + +static int tma300_dev_setup(bool enable) +{ + int rc; + + if (enable) { + rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_tma300), + regs_tma300); + + if (rc) { + pr_err("%s: could not get regulators: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_bulk_set_voltage(ARRAY_SIZE(regs_tma300), + regs_tma300); + + if (rc) { + pr_err("%s: could not set voltages: %d\n", + __func__, rc); + goto reg_free; + } + + /* enable interrupt gpio */ + rc = gpio_tlmm_config(GPIO_CFG(TS_GPIO_IRQ, 0, GPIO_CFG_INPUT, + GPIO_CFG_PULL_UP, GPIO_CFG_6MA), GPIO_CFG_ENABLE); + if (rc) { + pr_err("%s: Could not configure gpio %d\n", + __func__, TS_GPIO_IRQ); + goto reg_free; + } + + /* virtual keys */ + tma300_vkeys_attr.attr.name = "virtualkeys.msm_tma300_ts"; + properties_kobj = kobject_create_and_add("board_properties", + NULL); + if (!properties_kobj) { + pr_err("%s: failed to create a kobject " + "for board_properties\n", __func__); + rc = -ENOMEM; + goto reg_free; + } + rc = sysfs_create_group(properties_kobj, + &tma300_properties_attr_group); + if (rc) { + pr_err("%s: failed to create a sysfs entry %s\n", + __func__, tma300_vkeys_attr.attr.name); + goto kobj_free; + } + } else { + regulator_bulk_free(ARRAY_SIZE(regs_tma300), regs_tma300); + /* destroy virtual keys */ + if (properties_kobj) { + sysfs_remove_group(properties_kobj, + &tma300_properties_attr_group); + kobject_put(properties_kobj); + } + } + return 0; + +kobj_free: + kobject_put(properties_kobj); + properties_kobj = NULL; +reg_free: + regulator_bulk_free(ARRAY_SIZE(regs_tma300), regs_tma300); +out: + return rc; +} + +static struct cy8c_ts_platform_data cy8ctma300_pdata = { + .power_on = tma300_power, + .dev_setup = tma300_dev_setup, + .ts_name = "msm_tma300_ts", + .dis_min_x = 0, + .dis_max_x = 479, + .dis_min_y = 0, + .dis_max_y = 799, + .res_x = 479, + .res_y = 1009, + .min_tid = 1, + .max_tid = 255, + .min_touch = 0, + .max_touch = 255, + .min_width = 0, + .max_width = 255, + .invert_y = 1, + .nfingers = 4, + .irq_gpio = TS_GPIO_IRQ, + .resout_gpio = -1, +}; + +static struct i2c_board_info cy8ctma300_board_info[] = { + { + I2C_BOARD_INFO("cy8ctma300", 0x2), + .platform_data = &cy8ctma300_pdata, + } +}; + static void __init msm7x30_init(void) { - msm_device_otg.dev.platform_data = &msm_otg_pdata; - msm_device_hsusb.dev.parent = &msm_device_otg.dev; - msm_device_hsusb_host.dev.parent = &msm_device_otg.dev; + int rc; + unsigned smem_size; + uint32_t usb_hub_gpio_cfg_value = GPIO_CFG(56, + 0, + GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, + GPIO_CFG_2MA); + uint32_t soc_version = 0; + soc_version = socinfo_get_version(); + + msm_clock_init(&msm7x30_clock_init_data); +#ifdef CONFIG_SERIAL_MSM_CONSOLE + msm7x30_init_uart2(); +#endif + msm_spm_init(&msm_spm_data, 1); + acpuclk_init(&acpuclk_7x30_soc_data); + if (machine_is_msm7x30_surf() || machine_is_msm7x30_fluid()) + msm7x30_cfg_smsc911x(); + +#ifdef CONFIG_USB_MSM_OTG_72K + if (SOCINFO_VERSION_MAJOR(soc_version) >= 2 && + SOCINFO_VERSION_MINOR(soc_version) >= 1) { + pr_debug("%s: SOC Version:2.(1 or more)\n", __func__); + msm_otg_pdata.ldo_set_voltage = 0; + } + + msm_device_otg.dev.platform_data = &msm_otg_pdata; +#ifdef CONFIG_USB_GADGET + msm_otg_pdata.swfi_latency = + msm_pm_data + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_gadget_peripheral.dev.platform_data = &msm_gadget_pdata; +#endif +#endif + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(136); + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + msm_device_tsif.dev.platform_data = &tsif_platform_data; +#endif + if (machine_is_msm7x30_fluid()) { + msm_adc_pdata.dev_names = msm_adc_fluid_device_names; + msm_adc_pdata.num_adc = ARRAY_SIZE(msm_adc_fluid_device_names); + } else { + msm_adc_pdata.dev_names = msm_adc_surf_device_names; + msm_adc_pdata.num_adc = ARRAY_SIZE(msm_adc_surf_device_names); + } + + pmic8058_leds_init(); + + buses_init(); + +#ifdef CONFIG_MSM_SSBI + msm_device_ssbi_pmic1.dev.platform_data = + &msm7x30_ssbi_pm8058_pdata; +#endif + + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); platform_add_devices(devices, ARRAY_SIZE(devices)); +#ifdef CONFIG_USB_EHCI_MSM_72K + msm_add_host(0, &msm_usb_host_pdata); +#endif +#ifdef CONFIG_MSM_CAMERA_V4L2 + msm7x30_init_cam(); +#endif + msm7x30_init_mmc(); + msm7x30_init_nand(); + msm_qsd_spi_init(); + +#ifdef CONFIG_SPI_QSD + if (machine_is_msm7x30_fluid()) + spi_register_board_info(lcdc_sharp_spi_board_info, + ARRAY_SIZE(lcdc_sharp_spi_board_info)); + else + spi_register_board_info(lcdc_toshiba_spi_board_info, + ARRAY_SIZE(lcdc_toshiba_spi_board_info)); +#endif + + atv_dac_power_init(); + sensors_ldo_init(); + hdmi_init_regs(); + msm_fb_add_devices(); + msm_pm_set_platform_data(msm_pm_data, ARRAY_SIZE(msm_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_register_irqs(); + msm_device_i2c_init(); + msm_device_i2c_2_init(); + qup_device_i2c_init(); + msm7x30_init_marimba(); +#ifdef CONFIG_MSM7KV2_AUDIO + snddev_poweramp_gpio_init(); + snddev_hsed_voltage_init(); + aux_pcm_gpio_init(); +#endif + + i2c_register_board_info(0, msm_i2c_board_info, + ARRAY_SIZE(msm_i2c_board_info)); + + if (!machine_is_msm8x55_svlte_ffa() && !machine_is_msm7x30_fluid()) + marimba_pdata.tsadc = &marimba_tsadc_pdata; + + if (machine_is_msm7x30_fluid()) + i2c_register_board_info(0, cy8info, + ARRAY_SIZE(cy8info)); +#ifdef CONFIG_BOSCH_BMA150 + if (machine_is_msm7x30_fluid()) + i2c_register_board_info(0, bma150_board_info, + ARRAY_SIZE(bma150_board_info)); +#endif + + i2c_register_board_info(2, msm_marimba_board_info, + ARRAY_SIZE(msm_marimba_board_info)); + + i2c_register_board_info(2, msm_i2c_gsbi7_timpani_info, + ARRAY_SIZE(msm_i2c_gsbi7_timpani_info)); + + i2c_register_board_info(4 /* QUP ID */, msm_camera_boardinfo, + ARRAY_SIZE(msm_camera_boardinfo)); + + bt_power_init(); +#ifdef CONFIG_I2C_SSBI + msm_device_ssbi7.dev.platform_data = &msm_i2c_ssbi7_pdata; +#endif + if (machine_is_msm7x30_fluid()) + i2c_register_board_info(0, msm_isa1200_board_info, + ARRAY_SIZE(msm_isa1200_board_info)); + +#if defined(CONFIG_TOUCHSCREEN_TSC2007) || \ + defined(CONFIG_TOUCHSCREEN_TSC2007_MODULE) + if (machine_is_msm8x55_svlte_ffa()) + i2c_register_board_info(2, tsc_i2c_board_info, + ARRAY_SIZE(tsc_i2c_board_info)); +#endif + + if (machine_is_msm7x30_surf()) + platform_device_register(&flip_switch_device); + + pm8058_gpios_init(); + + if (machine_is_msm7x30_fluid()) { + /* Initialize platform data for fluid v2 hardware */ + if (SOCINFO_VERSION_MAJOR( + socinfo_get_platform_version()) == 2) { + cy8ctma300_pdata.res_y = 920; + cy8ctma300_pdata.invert_y = 0; + } + i2c_register_board_info(0, cy8ctma300_board_info, + ARRAY_SIZE(cy8ctma300_board_info)); + } + + if (machine_is_msm8x55_svlte_surf() || machine_is_msm8x55_svlte_ffa()) { + rc = gpio_tlmm_config(usb_hub_gpio_cfg_value, GPIO_CFG_ENABLE); + if (rc) + pr_err("%s: gpio_tlmm_config(%#x)=%d\n", + __func__, usb_hub_gpio_cfg_value, rc); + } + + boot_reason = *(unsigned int *) + (smem_get_entry(SMEM_POWER_ON_STATUS_INFO, &smem_size)); + printk(KERN_NOTICE "Boot Reason = 0x%02x\n", boot_reason); +} + +static unsigned pmem_sf_size = MSM_PMEM_SF_SIZE; +static int __init pmem_sf_size_setup(char *p) +{ + pmem_sf_size = memparse(p, NULL); + return 0; +} +early_param("pmem_sf_size", pmem_sf_size_setup); + +static unsigned fb_size; +static int __init fb_size_setup(char *p) +{ + fb_size = memparse(p, NULL); + return 0; +} +early_param("fb_size", fb_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +static unsigned fluid_pmem_adsp_size = MSM_FLUID_PMEM_ADSP_SIZE; +static int __init fluid_pmem_adsp_size_setup(char *p) +{ + fluid_pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("fluid_pmem_adsp_size", fluid_pmem_adsp_size_setup); + +static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; +static int __init pmem_audio_size_setup(char *p) +{ + pmem_audio_size = memparse(p, NULL); + return 0; +} +early_param("pmem_audio_size", pmem_audio_size_setup); + +static unsigned pmem_kernel_ebi0_size = PMEM_KERNEL_EBI0_SIZE; +static int __init pmem_kernel_ebi0_size_setup(char *p) +{ + pmem_kernel_ebi0_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi0_size", pmem_kernel_ebi0_size_setup); + +static struct memtype_reserve msm7x30_reserve_table[] __initdata = { + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init size_pmem_devices(void) +{ +#ifdef CONFIG_ANDROID_PMEM + unsigned long size; + + if machine_is_msm7x30_fluid() + size = fluid_pmem_adsp_size; + else + size = pmem_adsp_size; + android_pmem_adsp_pdata.size = size; + android_pmem_audio_pdata.size = pmem_audio_size; + android_pmem_pdata.size = pmem_sf_size; +#endif +} + +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm7x30_reserve_table[p->memory_type].size += p->size; +} + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM + reserve_memory_for(&android_pmem_adsp_pdata); + reserve_memory_for(&android_pmem_audio_pdata); + reserve_memory_for(&android_pmem_pdata); + msm7x30_reserve_table[MEMTYPE_EBI0].size += pmem_kernel_ebi0_size; +#endif +} + +static void __init reserve_mdp_memory(void) +{ + mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE; + msm7x30_reserve_table[mdp_pdata.mem_hid].size += mdp_pdata.ov0_wb_size; +} + +static void __init msm7x30_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); + reserve_mdp_memory(); +} + +static int msm7x30_paddr_to_memtype(unsigned int paddr) +{ + if (paddr < phys_add) + return MEMTYPE_EBI0; + if (paddr >= phys_add && paddr < 0x80000000) + return MEMTYPE_EBI1; + return MEMTYPE_NONE; +} + +static struct reserve_info msm7x30_reserve_info __initdata = { + .memtype_reserve_table = msm7x30_reserve_table, + .calculate_reserve_sizes = msm7x30_calculate_reserve_sizes, + .paddr_to_memtype = msm7x30_paddr_to_memtype, +}; + +static void __init msm7x30_reserve(void) +{ + reserve_info = &msm7x30_reserve_info; + msm_reserve(); +} + +static void __init msm7x30_allocate_memory_regions(void) +{ + void *addr; + unsigned long size; + + size = fb_size ? : MSM_FB_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); + +#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE + size = MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE; + addr = alloc_bootmem_align(size, 0x1000); + msm_v4l2_video_overlay_resources[0].start = __pa(addr); + msm_v4l2_video_overlay_resources[0].end = + msm_v4l2_video_overlay_resources[0].start + size - 1; + pr_debug("allocating %lu bytes at %p (%lx physical) for v4l2\n", + size, addr, __pa(addr)); +#endif } static void __init msm7x30_map_io(void) { + msm_shared_ram_phys = 0x00100000; msm_map_msm7x30_io(); - msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n", + __func__); +} + +static void __init msm7x30_init_early(void) +{ + msm7x30_allocate_memory_regions(); +} + +static void __init msm7x30_fixup(struct tag *tags, char **cmdline, + struct meminfo *mi) +{ + for (; tags->hdr.size; tags = tag_next(tags)) { + if (tags->hdr.tag == ATAG_MEM && tags->u.mem.start == + DDR1_BANK_BASE) { + ebi1_phys_offset = DDR1_BANK_BASE; + phys_add = DDR1_BANK_BASE; + break; + } + } } MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") .atag_offset = 0x100, - .fixup = msm7x30_fixup, - .reserve = msm7x30_reserve, .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, MACHINE_END MACHINE_START(MSM7X30_FFA, "QCT MSM7X30 FFA") .atag_offset = 0x100, - .fixup = msm7x30_fixup, - .reserve = msm7x30_reserve, .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, MACHINE_END MACHINE_START(MSM7X30_FLUID, "QCT MSM7X30 FLUID") .atag_offset = 0x100, - .fixup = msm7x30_fixup, - .reserve = msm7x30_reserve, .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, +MACHINE_END + +MACHINE_START(MSM8X55_SURF, "QCT MSM8X55 SURF") + .atag_offset = 0x100, + .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, +MACHINE_END + +MACHINE_START(MSM8X55_FFA, "QCT MSM8X55 FFA") + .atag_offset = 0x100, + .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, +MACHINE_END +MACHINE_START(MSM8X55_SVLTE_SURF, "QCT MSM8X55 SVLTE SURF") + .atag_offset = 0x100, + .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, +MACHINE_END +MACHINE_START(MSM8X55_SVLTE_FFA, "QCT MSM8X55 SVLTE FFA") + .atag_offset = 0x100, + .map_io = msm7x30_map_io, + .reserve = msm7x30_reserve, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, + .init_early = msm7x30_init_early, + .handle_irq = vic_handle_irq, + .fixup = msm7x30_fixup, MACHINE_END diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c deleted file mode 100644 index ed359812853..00000000000 --- a/arch/arm/mach-msm/board-msm8960.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "devices.h" - -static void __init msm8960_fixup(struct tag *tag, char **cmdline, - struct meminfo *mi) -{ - for (; tag->hdr.size; tag = tag_next(tag)) - if (tag->hdr.tag == ATAG_MEM && - tag->u.mem.start == 0x40200000) { - tag->u.mem.start = 0x40000000; - tag->u.mem.size += SZ_2M; - } -} - -static void __init msm8960_reserve(void) -{ - memblock_remove(0x40000000, SZ_2M); -} - -static void __init msm8960_map_io(void) -{ - msm_map_msm8960_io(); -} - -static void __init msm8960_init_irq(void) -{ - unsigned int i; - gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, - (void *)MSM_QGIC_CPU_BASE); - - /* Edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */ - writel(0xFFFFD7FF, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4); - - if (machine_is_msm8960_rumi3()) - writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET); - - /* FIXME: Not installing AVS_SVICINT and AVS_SVICINTSWDONE yet - * as they are configured as level, which does not play nice with - * handle_percpu_irq. - */ - for (i = GIC_PPI_START; i < GIC_SPI_START; i++) { - if (i != AVS_SVICINT && i != AVS_SVICINTSWDONE) - irq_set_handler(i, handle_percpu_irq); - } -} - -static struct platform_device *sim_devices[] __initdata = { - &msm8960_device_uart_gsbi2, -}; - -static struct platform_device *rumi3_devices[] __initdata = { - &msm8960_device_uart_gsbi5, -}; - -static void __init msm8960_sim_init(void) -{ - platform_add_devices(sim_devices, ARRAY_SIZE(sim_devices)); -} - -static void __init msm8960_rumi3_init(void) -{ - platform_add_devices(rumi3_devices, ARRAY_SIZE(rumi3_devices)); -} - -MACHINE_START(MSM8960_SIM, "QCT MSM8960 SIMULATOR") - .fixup = msm8960_fixup, - .reserve = msm8960_reserve, - .map_io = msm8960_map_io, - .init_irq = msm8960_init_irq, - .timer = &msm_timer, - .handle_irq = gic_handle_irq, - .init_machine = msm8960_sim_init, -MACHINE_END - -MACHINE_START(MSM8960_RUMI3, "QCT MSM8960 RUMI3") - .fixup = msm8960_fixup, - .reserve = msm8960_reserve, - .map_io = msm8960_map_io, - .init_irq = msm8960_init_irq, - .timer = &msm_timer, - .handle_irq = gic_handle_irq, - .init_machine = msm8960_rumi3_init, -MACHINE_END - diff --git a/arch/arm/mach-msm/board-msm8x60-camera.c b/arch/arm/mach-msm/board-msm8x60-camera.c new file mode 100644 index 00000000000..32d5530f287 --- /dev/null +++ b/arch/arm/mach-msm/board-msm8x60-camera.c @@ -0,0 +1,543 @@ +/* Copyright (c) 2012 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices-msm8x60.h" +#include "devices.h" + +#define GPIO_EXT_CAMIF_PWR_EN1 (PM8901_MPP_BASE + PM8901_MPPS + 13) +#define GPIO_WEB_CAMIF_STANDBY1 (PM8901_MPP_BASE + PM8901_MPPS + 60) +#ifdef CONFIG_MSM_CAMERA_FLASH +#define VFE_CAMIF_TIMER1_GPIO 29 +#define VFE_CAMIF_TIMER2_GPIO 30 +#define VFE_CAMIF_TIMER3_GPIO_INT 31 +#define FUSION_VFE_CAMIF_TIMER1_GPIO 42 + +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_PMIC, + ._fsrc.pmic_src.num_of_src = 2, + ._fsrc.pmic_src.low_current = 100, + ._fsrc.pmic_src.high_current = 300, + ._fsrc.pmic_src.led_src_1 = PMIC8058_ID_FLASH_LED_0, + ._fsrc.pmic_src.led_src_2 = PMIC8058_ID_FLASH_LED_1, + ._fsrc.pmic_src.pmic_set_current = pm8058_set_flash_led_current, +}; +static struct msm_camera_sensor_strobe_flash_data strobe_flash_xenon = { + .flash_trigger = VFE_CAMIF_TIMER2_GPIO, + .flash_charge = VFE_CAMIF_TIMER1_GPIO, + .flash_charge_done = VFE_CAMIF_TIMER3_GPIO_INT, + .flash_recharge_duration = 50000, + .irq = MSM_GPIO_TO_INT(VFE_CAMIF_TIMER3_GPIO_INT), +}; +#endif + +static struct msm_bus_vectors cam_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 283115520, + .ib = 452984832, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 283115520, + .ib = 452984832, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 283115520, + .ib = 452984832, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 319610880, + .ib = 511377408, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 566231040, + .ib = 905969664, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 69984000, + .ib = 111974400, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 320864256, + .ib = 513382810, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 320864256, + .ib = 513382810, + }, +}; + +static struct msm_bus_vectors cam_zsl_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 566231040, + .ib = 905969664, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 706199040, + .ib = 1129918464, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 320864256, + .ib = 513382810, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 320864256, + .ib = 513382810, + }, +}; + +static struct msm_bus_vectors cam_stereo_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 212336640, + .ib = 339738624, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 25090560, + .ib = 40144896, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 239708160, + .ib = 383533056, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 79902720, + .ib = 127844352, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_stereo_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 300902400, + .ib = 481443840, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 230307840, + .ib = 368492544, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 245113344, + .ib = 392181351, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 106536960, + .ib = 170459136, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 106536960, + .ib = 170459136, + }, +}; + +static struct msm_bus_paths cam_bus_client_config[] = { + { + ARRAY_SIZE(cam_init_vectors), + cam_zsl_vectors, + }, + { + ARRAY_SIZE(cam_preview_vectors), + cam_zsl_vectors, + }, + { + ARRAY_SIZE(cam_video_vectors), + cam_zsl_vectors, + }, + { + ARRAY_SIZE(cam_snapshot_vectors), + cam_snapshot_vectors, + }, + { + ARRAY_SIZE(cam_zsl_vectors), + cam_zsl_vectors, + }, + { + ARRAY_SIZE(cam_stereo_video_vectors), + cam_stereo_video_vectors, + }, + { + ARRAY_SIZE(cam_stereo_snapshot_vectors), + cam_stereo_snapshot_vectors, + }, +}; + +static struct msm_bus_scale_pdata cam_bus_client_pdata = { + cam_bus_client_config, + ARRAY_SIZE(cam_bus_client_config), + .name = "msm_camera", +}; + +static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = { + { + .csid_core = 0, + .is_csic = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + .ioclk = { + .vfe_clk_rate = 228570000, + }, + }, + { + .csid_core = 1, + .is_csic = 1, + .is_vpe = 1, + .cam_bus_scale_table = &cam_bus_client_pdata, + .ioclk = { + .vfe_clk_rate = 228570000, + }, + }, +}; +static struct camera_vreg_t msm_8x60_back_cam_vreg[] = { + {"cam_vana", REG_LDO, 2850000, 2850000, -1}, + {"cam_vio", REG_VS, 0, 0, 0}, + {"cam_vdig", REG_LDO, 1200000, 1200000, -1}, +}; + +static struct gpio msm8x60_common_cam_gpio[] = { + {32, GPIOF_DIR_IN, "CAMIF_MCLK"}, + {47, GPIOF_DIR_IN, "CAMIF_I2C_DATA"}, + {48, GPIOF_DIR_IN, "CAMIF_I2C_CLK"}, + {105, GPIOF_DIR_IN, "STANDBY"}, +}; + +static struct gpio msm8x60_back_cam_gpio[] = { + {GPIO_EXT_CAMIF_PWR_EN1, GPIOF_DIR_OUT, "CAMIF_PWR_EN"}, + {106, GPIOF_DIR_OUT, "CAM_RESET"}, +}; + +static struct msm_gpio_set_tbl msm8x60_back_cam_gpio_set_tbl[] = { + {GPIO_EXT_CAMIF_PWR_EN1, GPIOF_OUT_INIT_LOW, 10000}, + {GPIO_EXT_CAMIF_PWR_EN1, GPIOF_OUT_INIT_HIGH, 5000}, + {106, GPIOF_OUT_INIT_LOW, 1000}, + {106, GPIOF_OUT_INIT_HIGH, 4000}, +}; + +static struct msm_camera_gpio_conf msm_8x60_back_cam_gpio_conf = { + .cam_gpio_common_tbl = msm8x60_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(msm8x60_common_cam_gpio), + .cam_gpio_req_tbl = msm8x60_back_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(msm8x60_back_cam_gpio), + .cam_gpio_set_tbl = msm8x60_back_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(msm8x60_back_cam_gpio_set_tbl), +}; + + +static struct i2c_board_info imx074_actuator_i2c_info = { + I2C_BOARD_INFO("msm_actuator", 0x11), +}; + +static struct msm_actuator_info imx074_actuator_info = { + .board_info = &imx074_actuator_i2c_info, + .cam_name = MSM_ACTUATOR_MAIN_CAM_0, + .bus_id = MSM_GSBI4_QUP_I2C_BUS_ID, + .vcm_enable = 0, +}; + +static struct msm_camera_sensor_flash_data flash_imx074 = { + .flash_type = MSM_CAMERA_FLASH_LED, +#ifdef CONFIG_MSM_CAMERA_FLASH + .flash_src = &msm_flash_src, +#endif +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_imx074 = { + .mount_angle = 180, + .cam_vreg = msm_8x60_back_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8x60_back_cam_vreg), + .gpio_conf = &msm_8x60_back_cam_gpio_conf, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx074_data = { + .sensor_name = "imx074", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_imx074, + .strobe_flash_data = &strobe_flash_xenon, + .sensor_platform_info = &sensor_board_info_imx074, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, + .actuator_info = &imx074_actuator_info +}; + +static struct msm_camera_sensor_flash_data flash_mt9e013 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_mt9e013 = { + .mount_angle = 0, + .cam_vreg = msm_8x60_back_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8x60_back_cam_vreg), + .gpio_conf = &msm_8x60_back_cam_gpio_conf, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = { + .sensor_name = "mt9e013", + .pdata = &msm_camera_csi_device_data[0], + .flash_data = &flash_mt9e013, + .sensor_platform_info = &sensor_board_info_mt9e013, + .csi_if = 1, + .camera_type = BACK_CAMERA_2D, +}; + +static struct gpio ov7692_cam_gpio[] = { + {GPIO_WEB_CAMIF_STANDBY1, GPIOF_DIR_OUT, "CAM_EN"}, +}; + +static struct msm_gpio_set_tbl ov7692_cam_gpio_set_tbl[] = { + {GPIO_WEB_CAMIF_STANDBY1, GPIOF_OUT_INIT_LOW, 10000}, +}; + +static struct msm_camera_gpio_conf ov7692_cam_gpio_conf = { + .cam_gpio_common_tbl = msm8x60_common_cam_gpio, + .cam_gpio_common_tbl_size = ARRAY_SIZE(msm8x60_common_cam_gpio), + .cam_gpio_req_tbl = ov7692_cam_gpio, + .cam_gpio_req_tbl_size = ARRAY_SIZE(ov7692_cam_gpio), + .cam_gpio_set_tbl = ov7692_cam_gpio_set_tbl, + .cam_gpio_set_tbl_size = ARRAY_SIZE(ov7692_cam_gpio_set_tbl), +}; + +static struct msm_camera_sensor_flash_data flash_ov7692 = { + .flash_type = MSM_CAMERA_FLASH_NONE, +}; + +static struct msm_camera_sensor_platform_info sensor_board_info_ov7692 = { + .mount_angle = 0, + .cam_vreg = msm_8x60_back_cam_vreg, + .num_vreg = ARRAY_SIZE(msm_8x60_back_cam_vreg), + .gpio_conf = &ov7692_cam_gpio_conf, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov7692_data = { + .sensor_name = "ov7692", + .pdata = &msm_camera_csi_device_data[1], + .flash_data = &flash_ov7692, + .sensor_platform_info = &sensor_board_info_ov7692, + .csi_if = 1, + .camera_type = FRONT_CAMERA_2D, +}; + +static struct platform_device msm_camera_server = { + .name = "msm_cam_server", + .id = 0, +}; + +void __init msm8x60_init_cam(void) +{ + platform_device_register(&msm_camera_server); + platform_device_register(&msm_device_csic0); + platform_device_register(&msm_device_csic1); + platform_device_register(&msm_device_vfe); + platform_device_register(&msm_device_vpe); +} + +#ifdef CONFIG_I2C +static struct i2c_board_info msm8x60_camera_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("imx074", 0x1A), + .platform_data = &msm_camera_sensor_imx074_data, + }, + { + I2C_BOARD_INFO("mt9e013", 0x6C), + .platform_data = &msm_camera_sensor_mt9e013_data, + }, + { + I2C_BOARD_INFO("ov7692", 0x78), + .platform_data = &msm_camera_sensor_ov7692_data, + }, +}; + +struct msm_camera_board_info msm8x60_camera_board_info = { + .board_info = msm8x60_camera_i2c_boardinfo, + .num_i2c_board_info = ARRAY_SIZE(msm8x60_camera_i2c_boardinfo), +}; +#endif diff --git a/arch/arm/mach-msm/board-msm8x60-vcm.c b/arch/arm/mach-msm/board-msm8x60-vcm.c new file mode 100644 index 00000000000..6078367c05f --- /dev/null +++ b/arch/arm/mach-msm/board-msm8x60-vcm.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include + +#define MSM_SMI_BASE 0x38000000 +#define MSM_SMI_SIZE 0x04000000 + +#define SMI_16M 0 +#define SMI_1M 1 +#define SMI_64K 2 +#define SMI_4K 3 +#define EBI_16M 4 +#define EBI_1M 5 +#define EBI_64K 6 +#define EBI_4K 7 + +static void free_ebi_pools(void); + +static struct physmem_region memory[] = { + { /* SMI 16M */ + .addr = MSM_SMI_BASE, + .size = SZ_16M, + .chunk_size = SZ_16M + }, + { /* SMI 1M */ + .addr = MSM_SMI_BASE + SZ_16M, + .size = SZ_8M, + .chunk_size = SZ_1M + }, + { /* SMI 64K */ + .addr = MSM_SMI_BASE + SZ_16M + SZ_8M, + .size = SZ_4M, + .chunk_size = SZ_64K + }, + { /* SMI 4K */ + .addr = MSM_SMI_BASE + SZ_16M + SZ_8M + SZ_4M, + .size = SZ_4M, + .chunk_size = SZ_4K + }, + + { /* EBI 16M */ + .addr = 0, + .size = SZ_16M, + .chunk_size = SZ_16M + }, + { /* EBI 1M */ + .addr = 0, + .size = SZ_8M, + .chunk_size = SZ_1M + }, + { /* EBI 64K */ + .addr = 0, + .size = SZ_4M, + .chunk_size = SZ_64K + }, + { /* EBI 4K */ + .addr = 0, + .size = SZ_4M, + .chunk_size = SZ_4K + } +}; + + +/* The pool priority MUST be in descending order of size */ +static struct vcm_memtype_map mt_map[] __initdata = { + { + /* MEMTYPE_0 */ + .pool_id = {SMI_16M, SMI_1M, SMI_64K, SMI_4K}, + .num_pools = 4, + }, + { + /* MEMTYPE_1 */ + .pool_id = {SMI_16M, SMI_1M, SMI_64K, EBI_4K}, + .num_pools = 4, + }, + { /* MEMTYPE_2 */ + .pool_id = {EBI_16M, EBI_1M, EBI_64K, EBI_4K}, + .num_pools = 4, + }, + { + /* MEMTYPE_3 */ + .pool_id = {SMI_16M, SMI_1M, EBI_1M, SMI_64K, EBI_64K, EBI_4K}, + .num_pools = 6, + } +}; + +static int __init msm8x60_vcm_init(void) +{ + int ret, i; + void *ebi_chunk; + + + for (i = 0; i < ARRAY_SIZE(memory); i++) { + if (memory[i].addr == 0) { + ebi_chunk = __alloc_bootmem(memory[i].size, + memory[i].size, 0); + if (!ebi_chunk) { + pr_err("Could not allocate VCM-managed physical" + " memory\n"); + ret = -ENOMEM; + goto fail; + } + memory[i].addr = __pa(ebi_chunk); + } + } + + ret = vcm_sys_init(memory, ARRAY_SIZE(memory), + mt_map, ARRAY_SIZE(mt_map), + (void *)MSM_SMI_BASE + MSM_SMI_SIZE - SZ_8M, SZ_8M); + + if (ret != 0) { + pr_err("vcm_sys_init() ret %i\n", ret); + goto fail; + } + + return 0; +fail: + free_ebi_pools(); + return ret; +}; + +static void free_ebi_pools(void) +{ + int i; + phys_addr_t r; + for (i = 0; i < ARRAY_SIZE(memory); i++) { + r = memory[i].addr; + if (r > MSM_SMI_BASE + MSM_SMI_SIZE) + free_bootmem((unsigned long)__va(r), memory[i].size); + } +} + + +/* Useful for testing, and if VCM is ever unloaded */ +static void __exit msm8x60_vcm_exit(void) +{ + int ret; + + ret = vcm_sys_destroy(); + if (ret != 0) { + pr_err("vcm_sys_destroy() ret %i\n", ret); + goto fail; + } + free_ebi_pools(); +fail: + return; +} + + +subsys_initcall(msm8x60_vcm_init); +module_exit(msm8x60_vcm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Stepan Moskovchenko "); diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c index fb3496a52ef..73a900cd823 100644 --- a/arch/arm/mach-msm/board-msm8x60.c +++ b/arch/arm/mach-msm/board-msm8x60.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,149 +8,10636 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. + * */ #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ANDROID_PMEM +#include +#endif + +#if defined(CONFIG_SMB137B_CHARGER) || defined(CONFIG_SMB137B_CHARGER_MODULE) +#include +#endif +#ifdef CONFIG_SND_SOC_WM8903 +#include +#endif #include #include -#include #include +#include +#include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#ifdef CONFIG_MSM_DSPS +#include +#endif +#include +#include +#include +#include +#ifdef CONFIG_USB_G_ANDROID +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include -static void __init msm8x60_fixup(struct tag *tag, char **cmdline, - struct meminfo *mi) -{ - for (; tag->hdr.size; tag = tag_next(tag)) - if (tag->hdr.tag == ATAG_MEM && - tag->u.mem.start == 0x40200000) { - tag->u.mem.start = 0x40000000; - tag->u.mem.size += SZ_2M; - } +#include "devices.h" +#include "devices-msm8x60.h" +#include +#include "pm.h" +#include +#include "spm.h" +#include "rpm_log.h" +#include "timer.h" +#include "gpiomux-8x60.h" +#include "rpm_stats.h" +#include "peripheral-loader.h" +#include +#include "rpm_resources.h" +#include "acpuclock.h" +#include "pm-boot.h" +#include "board-storage-common-a.h" + +#include +#include +#include + +#define MSM_SHARED_RAM_PHYS 0x40000000 +#define MDM2AP_SYNC 129 + +#define GPIO_ETHERNET_RESET_N_DRAGON 30 +#define LCDC_SPI_GPIO_CLK 73 +#define LCDC_SPI_GPIO_CS 72 +#define LCDC_SPI_GPIO_MOSI 70 +#define LCDC_AUO_PANEL_NAME "lcdc_auo_wvga" +#define LCDC_SAMSUNG_OLED_PANEL_NAME "lcdc_samsung_oled" +#define LCDC_SAMSUNG_WSVGA_PANEL_NAME "lcdc_samsung_wsvga" +#define LCDC_SAMSUNG_SPI_DEVICE_NAME "lcdc_samsung_ams367pe02" +#define LCDC_AUO_SPI_DEVICE_NAME "lcdc_auo_nt35582" +#define LCDC_NT35582_PANEL_NAME "lcdc_nt35582_wvga" + +#define MIPI_CMD_NOVATEK_QHD_PANEL_NAME "mipi_cmd_novatek_qhd" +#define MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME "mipi_video_novatek_qhd" +#define MIPI_VIDEO_TOSHIBA_WVGA_PANEL_NAME "mipi_video_toshiba_wvga" +#define HDMI_PANEL_NAME "hdmi_msm" +#define TVOUT_PANEL_NAME "tvout_msm" + +#define DSPS_PIL_GENERIC_NAME "dsps" +#define DSPS_PIL_FLUID_NAME "dsps_fluid" + +#ifdef CONFIG_ION_MSM +static struct platform_device ion_dev; +#endif + +enum { + GPIO_EXPANDER_IRQ_BASE = PM8901_IRQ_BASE + NR_PMIC8901_IRQS, + GPIO_EXPANDER_GPIO_BASE = PM8901_MPP_BASE + PM8901_MPPS, + /* CORE expander */ + GPIO_CORE_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE, + GPIO_CLASS_D1_EN = GPIO_CORE_EXPANDER_BASE, + GPIO_WLAN_DEEP_SLEEP_N, + GPIO_LVDS_SHUTDOWN_N, + GPIO_DISP_RESX_N = GPIO_LVDS_SHUTDOWN_N, + GPIO_MS_SYS_RESET_N, + GPIO_CAP_TS_RESOUT_N, + GPIO_CAP_GAUGE_BI_TOUT, + GPIO_ETHERNET_PME, + GPIO_EXT_GPS_LNA_EN, + GPIO_MSM_WAKES_BT, + GPIO_ETHERNET_RESET_N, + GPIO_HEADSET_DET_N, + GPIO_USB_UICC_EN, + GPIO_BACKLIGHT_EN, + GPIO_EXT_CAMIF_PWR_EN, + GPIO_BATT_GAUGE_INT_N, + GPIO_BATT_GAUGE_EN, + /* DOCKING expander */ + GPIO_DOCKING_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE + 16, + GPIO_MIPI_DSI_RST_N = GPIO_DOCKING_EXPANDER_BASE, + GPIO_AUX_JTAG_DET_N, + GPIO_DONGLE_DET_N, + GPIO_SVIDEO_LOAD_DET, + GPIO_SVID_AMP_SHUTDOWN1_N, + GPIO_SVID_AMP_SHUTDOWN0_N, + GPIO_SDC_WP, + GPIO_IRDA_PWDN, + GPIO_IRDA_RESET_N, + GPIO_DONGLE_GPIO0, + GPIO_DONGLE_GPIO1, + GPIO_DONGLE_GPIO2, + GPIO_DONGLE_GPIO3, + GPIO_DONGLE_PWR_EN, + GPIO_EMMC_RESET_N, + GPIO_TP_EXP2_IO15, + /* SURF expander */ + GPIO_SURF_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE + (16 * 2), + GPIO_SD_CARD_DET_1 = GPIO_SURF_EXPANDER_BASE, + GPIO_SD_CARD_DET_2, + GPIO_SD_CARD_DET_4, + GPIO_SD_CARD_DET_5, + GPIO_UIM3_RST, + GPIO_SURF_EXPANDER_IO5, + GPIO_SURF_EXPANDER_IO6, + GPIO_ADC_I2C_EN, + GPIO_SURF_EXPANDER_IO8, + GPIO_SURF_EXPANDER_IO9, + GPIO_SURF_EXPANDER_IO10, + GPIO_SURF_EXPANDER_IO11, + GPIO_SURF_EXPANDER_IO12, + GPIO_SURF_EXPANDER_IO13, + GPIO_SURF_EXPANDER_IO14, + GPIO_SURF_EXPANDER_IO15, + /* LEFT KB IO expander */ + GPIO_LEFT_KB_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE + (16 * 3), + GPIO_LEFT_LED_1 = GPIO_LEFT_KB_EXPANDER_BASE, + GPIO_LEFT_LED_2, + GPIO_LEFT_LED_3, + GPIO_LEFT_LED_WLAN, + GPIO_JOYSTICK_EN, + GPIO_CAP_TS_SLEEP, + GPIO_LEFT_KB_IO6, + GPIO_LEFT_LED_5, + /* RIGHT KB IO expander */ + GPIO_RIGHT_KB_EXPANDER_BASE = GPIO_EXPANDER_GPIO_BASE + (16 * 3) + 8, + GPIO_RIGHT_LED_1 = GPIO_RIGHT_KB_EXPANDER_BASE, + GPIO_RIGHT_LED_2, + GPIO_RIGHT_LED_3, + GPIO_RIGHT_LED_BT, + GPIO_WEB_CAMIF_STANDBY, + GPIO_COMPASS_RST_N, + GPIO_WEB_CAMIF_RESET_N, + GPIO_RIGHT_LED_5, + GPIO_R_ALTIMETER_RESET_N, + /* FLUID S IO expander */ + GPIO_SOUTH_EXPANDER_BASE, + GPIO_MIC2_ANCR_SEL = GPIO_SOUTH_EXPANDER_BASE, + GPIO_MIC1_ANCL_SEL, + GPIO_HS_MIC4_SEL, + GPIO_FML_MIC3_SEL, + GPIO_FMR_MIC5_SEL, + GPIO_TS_SLEEP, + GPIO_HAP_SHIFT_LVL_OE, + GPIO_HS_SW_DIR, + /* FLUID N IO expander */ + GPIO_NORTH_EXPANDER_BASE, + GPIO_EPM_3_3V_EN = GPIO_NORTH_EXPANDER_BASE, + GPIO_EPM_5V_BOOST_EN, + GPIO_AUX_CAM_2P7_EN, + GPIO_LED_FLASH_EN, + GPIO_LED1_GREEN_N, + GPIO_LED2_RED_N, + GPIO_FRONT_CAM_RESET_N, + GPIO_EPM_LVLSFT_EN, + GPIO_N_ALTIMETER_RESET_N, + /* EPM expander */ + GPIO_EPM_EXPANDER_BASE, + GPIO_PWR_MON_START = GPIO_EPM_EXPANDER_BASE, + GPIO_PWR_MON_RESET_N, + GPIO_ADC1_PWDN_N, + GPIO_ADC2_PWDN_N, + GPIO_EPM_EXPANDER_IO4, + GPIO_ADC1_MUX_SPI_INT_N_3_3V, + GPIO_ADC2_MUX_SPI_INT_N, + GPIO_EPM_EXPANDER_IO7, + GPIO_PWR_MON_ENABLE, + GPIO_EPM_SPI_ADC1_CS_N, + GPIO_EPM_SPI_ADC2_CS_N, + GPIO_EPM_EXPANDER_IO11, + GPIO_EPM_EXPANDER_IO12, + GPIO_EPM_EXPANDER_IO13, + GPIO_EPM_EXPANDER_IO14, + GPIO_EPM_EXPANDER_IO15, +}; + +struct pm8xxx_mpp_init_info { + unsigned mpp; + struct pm8xxx_mpp_config_data config; +}; + +#define PM8058_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8058_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ } +#define PM8901_MPP_INIT(_mpp, _type, _level, _control) \ +{ \ + .mpp = PM8901_MPP_PM_TO_SYS(_mpp), \ + .config = { \ + .type = PM8XXX_MPP_TYPE_##_type, \ + .level = _level, \ + .control = PM8XXX_MPP_##_control, \ + } \ +} + +/* + * The UI_INTx_N lines are pmic gpio lines which connect i2c + * gpio expanders to the pm8058. + */ +#define UI_INT1_N 25 +#define UI_INT2_N 34 +#define UI_INT3_N 14 +/* +FM GPIO is GPIO 18 on PMIC 8058. +As the index starts from 0 in the PMIC driver, and hence 17 +corresponds to GPIO 18 on PMIC 8058. +*/ +#define FM_GPIO 17 + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static void (*sdc2_status_notify_cb)(int card_present, void *dev_id); +static void *sdc2_status_notify_cb_devid; +#endif + +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT +static void (*sdc5_status_notify_cb)(int card_present, void *dev_id); +static void *sdc5_status_notify_cb_devid; +#endif + +static struct msm_spm_platform_data msm_spm_data_v1[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + +#ifdef CONFIG_MSM_AVS_HW + .reg_init_values[MSM_SPM_REG_SAW_AVS_CTL] = 0x586020FF, +#endif + .reg_init_values[MSM_SPM_REG_SAW_CFG] = 0x0F, + .reg_init_values[MSM_SPM_REG_SAW_SPM_CTL] = 0x68, + .reg_init_values[MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0xFFFFFFFF, + .reg_init_values[MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0xFFFFFFFF, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x07, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x00, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_RST_EN] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x00, + + .awake_vlevel = 0x94, + .retention_vlevel = 0x81, + .collapse_vlevel = 0x20, + .retention_mid_vlevel = 0x94, + .collapse_mid_vlevel = 0x8C, + + .vctl_timeout_us = 50, + }, + + [1] = { + .reg_base_addr = MSM_SAW1_BASE, + +#ifdef CONFIG_MSM_AVS_HW + .reg_init_values[MSM_SPM_REG_SAW_AVS_CTL] = 0x586020FF, +#endif + .reg_init_values[MSM_SPM_REG_SAW_CFG] = 0x0F, + .reg_init_values[MSM_SPM_REG_SAW_SPM_CTL] = 0x68, + .reg_init_values[MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0xFFFFFFFF, + .reg_init_values[MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0xFFFFFFFF, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x13, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x07, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x00, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_RST_EN] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x00, + + .awake_vlevel = 0x94, + .retention_vlevel = 0x81, + .collapse_vlevel = 0x20, + .retention_mid_vlevel = 0x94, + .collapse_mid_vlevel = 0x8C, + + .vctl_timeout_us = 50, + }, +}; + +static struct msm_spm_platform_data msm_spm_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + +#ifdef CONFIG_MSM_AVS_HW + .reg_init_values[MSM_SPM_REG_SAW_AVS_CTL] = 0x586020FF, +#endif + .reg_init_values[MSM_SPM_REG_SAW_CFG] = 0x1C, + .reg_init_values[MSM_SPM_REG_SAW_SPM_CTL] = 0x68, + .reg_init_values[MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0x0C0CFFFF, + .reg_init_values[MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0x78780FFF, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x07, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x00, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_RST_EN] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x00, + + .awake_vlevel = 0xA0, + .retention_vlevel = 0x89, + .collapse_vlevel = 0x20, + .retention_mid_vlevel = 0x89, + .collapse_mid_vlevel = 0x89, + + .vctl_timeout_us = 50, + }, + + [1] = { + .reg_base_addr = MSM_SAW1_BASE, + +#ifdef CONFIG_MSM_AVS_HW + .reg_init_values[MSM_SPM_REG_SAW_AVS_CTL] = 0x586020FF, +#endif + .reg_init_values[MSM_SPM_REG_SAW_CFG] = 0x1C, + .reg_init_values[MSM_SPM_REG_SAW_SPM_CTL] = 0x68, + .reg_init_values[MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0x0C0CFFFF, + .reg_init_values[MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0x78780FFF, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x13, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x07, + .reg_init_values[MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x00, + + .reg_init_values[MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW_SLP_RST_EN] = 0x00, + .reg_init_values[MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x00, + + .awake_vlevel = 0xA0, + .retention_vlevel = 0x89, + .collapse_vlevel = 0x20, + .retention_mid_vlevel = 0x89, + .collapse_mid_vlevel = 0x89, + + .vctl_timeout_us = 50, + }, +}; + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +static struct regulator_consumer_supply vreg_consumers_8901_S0[] = { + REGULATOR_SUPPLY("8901_s0", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_8901_S1[] = { + REGULATOR_SUPPLY("8901_s1", NULL), +}; + +static struct regulator_init_data saw_s0_init_data = { + .constraints = { + .name = "8901_s0", + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .min_uV = 800000, + .max_uV = 1325000, + }, + .consumer_supplies = vreg_consumers_8901_S0, + .num_consumer_supplies = ARRAY_SIZE(vreg_consumers_8901_S0), +}; + +static struct regulator_init_data saw_s1_init_data = { + .constraints = { + .name = "8901_s1", + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .min_uV = 800000, + .max_uV = 1325000, + }, + .consumer_supplies = vreg_consumers_8901_S1, + .num_consumer_supplies = ARRAY_SIZE(vreg_consumers_8901_S1), +}; + +static struct platform_device msm_device_saw_s0 = { + .name = "saw-regulator", + .id = 0, + .dev = { + .platform_data = &saw_s0_init_data, + }, +}; + +static struct platform_device msm_device_saw_s1 = { + .name = "saw-regulator", + .id = 1, + .dev = { + .platform_data = &saw_s1_init_data, + }, +}; + +/* + * The smc91x configuration varies depending on platform. + * The resources data structure is filled in at runtime. + */ +static struct resource smc91x_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct resource smsc911x_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + .start = 0x1b800000, + .end = 0x1b8000ff + }, + [1] = { + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, + }, +}; + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_16BIT, + .has_reset_gpio = 1, + .reset_gpio = GPIO_ETHERNET_RESET_N +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = 0, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config + } +}; + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +#define QCE_SIZE 0x10000 +#define QCE_0_BASE 0x18500000 + +#define QCE_HW_KEY_SUPPORT 0 +#define QCE_SHA_HMAC_SUPPORT 0 +#define QCE_SHARE_CE_RESOURCE 2 +#define QCE_CE_SHARED 1 + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE_HASH_CRCI, + .end = DMOV_CE_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, + [4] = { + .name = "crypto_crci_hash", + .start = DMOV_CE_HASH_CRCI, + .end = DMOV_CE_HASH_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +static struct platform_device qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +static struct platform_device qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_HAPTIC_ISA1200) || \ + defined(CONFIG_HAPTIC_ISA1200_MODULE) + +static const char *vregs_isa1200_name[] = { + "8058_s3", + "8901_l4", +}; + +static const int vregs_isa1200_val[] = { + 1800000,/* uV */ + 2600000, +}; +static struct regulator *vregs_isa1200[ARRAY_SIZE(vregs_isa1200_name)]; +static struct msm_xo_voter *xo_handle_a1; + +static int isa1200_power(int vreg_on) +{ + int i, rc = 0; + + for (i = 0; i < ARRAY_SIZE(vregs_isa1200_name); i++) { + rc = vreg_on ? regulator_enable(vregs_isa1200[i]) : + regulator_disable(vregs_isa1200[i]); + if (rc < 0) { + pr_err("%s: vreg %s %s failed (%d)\n", + __func__, vregs_isa1200_name[i], + vreg_on ? "enable" : "disable", rc); + goto vreg_fail; + } + } + + rc = vreg_on ? msm_xo_mode_vote(xo_handle_a1, MSM_XO_MODE_ON) : + msm_xo_mode_vote(xo_handle_a1, MSM_XO_MODE_OFF); + if (rc < 0) { + pr_err("%s: failed to %svote for TCXO A1 buffer%d\n", + __func__, vreg_on ? "" : "de-", rc); + goto vreg_fail; + } + return 0; + +vreg_fail: + while (i--) + !vreg_on ? regulator_enable(vregs_isa1200[i]) : + regulator_disable(vregs_isa1200[i]); + return rc; +} + +static int isa1200_dev_setup(bool enable) +{ + int i, rc; + + if (enable == true) { + for (i = 0; i < ARRAY_SIZE(vregs_isa1200_name); i++) { + vregs_isa1200[i] = regulator_get(NULL, + vregs_isa1200_name[i]); + if (IS_ERR(vregs_isa1200[i])) { + pr_err("%s: regulator get of %s failed (%ld)\n", + __func__, vregs_isa1200_name[i], + PTR_ERR(vregs_isa1200[i])); + rc = PTR_ERR(vregs_isa1200[i]); + goto vreg_get_fail; + } + rc = regulator_set_voltage(vregs_isa1200[i], + vregs_isa1200_val[i], vregs_isa1200_val[i]); + if (rc) { + pr_err("%s: regulator_set_voltage(%s) failed\n", + __func__, vregs_isa1200_name[i]); + goto vreg_get_fail; + } + } + + rc = gpio_request(GPIO_HAP_SHIFT_LVL_OE, "haptics_shft_lvl_oe"); + if (rc) { + pr_err("%s: unable to request gpio %d (%d)\n", + __func__, GPIO_HAP_SHIFT_LVL_OE, rc); + goto vreg_get_fail; + } + + rc = gpio_direction_output(GPIO_HAP_SHIFT_LVL_OE, 1); + if (rc) { + pr_err("%s: Unable to set direction\n", __func__);; + goto free_gpio; + } + + xo_handle_a1 = msm_xo_get(MSM_XO_TCXO_A1, "isa1200"); + if (IS_ERR(xo_handle_a1)) { + rc = PTR_ERR(xo_handle_a1); + pr_err("%s: failed to get the handle for A1(%d)\n", + __func__, rc); + goto gpio_set_dir; + } + } else { + gpio_set_value(GPIO_HAP_SHIFT_LVL_OE, 0); + gpio_free(GPIO_HAP_SHIFT_LVL_OE); + + for (i = 0; i < ARRAY_SIZE(vregs_isa1200_name); i++) + regulator_put(vregs_isa1200[i]); + + msm_xo_put(xo_handle_a1); + } + + return 0; +gpio_set_dir: + gpio_set_value(GPIO_HAP_SHIFT_LVL_OE, 0); +free_gpio: + gpio_free(GPIO_HAP_SHIFT_LVL_OE); +vreg_get_fail: + while (i) + regulator_put(vregs_isa1200[--i]); + return rc; +} + +#define PMIC_GPIO_HAP_ENABLE 18 /* PMIC GPIO Number 19 */ +#define PMIC_GPIO_HAP_LDO_ENABLE 5 /* PMIC GPIO Number 6 */ +static struct isa1200_platform_data isa1200_1_pdata = { + .name = "vibrator", + .power_on = isa1200_power, + .dev_setup = isa1200_dev_setup, + /*gpio to enable haptic*/ + .hap_en_gpio = PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HAP_ENABLE), + .hap_len_gpio = PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HAP_LDO_ENABLE), + .max_timeout = 15000, + .mode_ctrl = PWM_GEN_MODE, + .pwm_fd = { + .pwm_div = 256, + }, + .is_erm = false, + .smart_en = true, + .ext_clk_en = true, + .chip_en = 1, +}; + +static struct i2c_board_info msm_isa1200_board_info[] = { + { + I2C_BOARD_INFO("isa1200_1", 0x90>>1), + .platform_data = &isa1200_1_pdata, + }, +}; +#endif + +#if defined(CONFIG_BATTERY_BQ27520) || \ + defined(CONFIG_BATTERY_BQ27520_MODULE) +static struct bq27520_platform_data bq27520_pdata = { + .name = "fuel-gauge", + .vreg_name = "8058_s3", + .vreg_value = 1800000, + .soc_int = GPIO_BATT_GAUGE_INT_N, + .bi_tout = GPIO_CAP_GAUGE_BI_TOUT, + .chip_en = GPIO_BATT_GAUGE_EN, + .enable_dlog = 0, /* if enable coulomb counter logger */ +}; + +static struct i2c_board_info msm_bq27520_board_info[] = { + { + I2C_BOARD_INFO("bq27520", 0xaa>>1), + .platform_data = &bq27520_pdata, + }, +}; +#endif + +static struct msm_rpmrs_level msm_rpmrs_levels[] __initdata = { + { + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1, 8000, 100000, 1, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 1500, 5000, 60100000, 3000, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + false, + 1800, 5000, 60350000, 3500, + }, + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, ACTIVE, MAX, ACTIVE), + false, + 3800, 4500, 65350000, 5500, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, HSFS_OPEN, MAX, ACTIVE), + false, + 2800, 2500, 66850000, 4800, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE), + false, + 4800, 2000, 71850000, 6800, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, ACTIVE, RET_HIGH), + false, + 6800, 500, 75850000, 8800, + }, + + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW), + false, + 7800, 0, 76350000, 9800, + }, +}; + +static struct msm_rpmrs_platform_data msm_rpmrs_data __initdata = { + .levels = &msm_rpmrs_levels[0], + .num_levels = ARRAY_SIZE(msm_rpmrs_levels), + .vdd_mem_levels = { + [MSM_RPMRS_VDD_MEM_RET_LOW] = 500, + [MSM_RPMRS_VDD_MEM_RET_HIGH] = 750, + [MSM_RPMRS_VDD_MEM_ACTIVE] = 1000, + [MSM_RPMRS_VDD_MEM_MAX] = 1325, + }, + .vdd_dig_levels = { + [MSM_RPMRS_VDD_DIG_RET_LOW] = 500, + [MSM_RPMRS_VDD_DIG_RET_HIGH] = 750, + [MSM_RPMRS_VDD_DIG_ACTIVE] = 1000, + [MSM_RPMRS_VDD_DIG_MAX] = 1250, + }, + .vdd_mask = 0xFFF, + .rpmrs_target_id = { + [MSM_RPMRS_ID_PXO_CLK] = MSM_RPM_ID_PXO_CLK, + [MSM_RPMRS_ID_L2_CACHE_CTL] = MSM_RPM_ID_APPS_L2_CACHE_CTL, + [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_SMPS1_0, + [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_SMPS1_1, + [MSM_RPMRS_ID_VDD_MEM_0] = MSM_RPM_ID_SMPS0_0, + [MSM_RPMRS_ID_VDD_MEM_1] = MSM_RPM_ID_SMPS0_1, + [MSM_RPMRS_ID_RPM_CTL] = MSM_RPM_ID_TRIGGER_SET_FROM, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_TZ, +}; + +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + +#define ISP1763_INT_GPIO 117 +#define ISP1763_RST_GPIO 152 +static struct resource isp1763_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + .start = 0x1D000000, + .end = 0x1D005FFF, /* 24KB */ + }, + [1] = { + .flags = IORESOURCE_IRQ, + }, +}; +static void __init msm8x60_cfg_isp1763(void) +{ + isp1763_resources[1].start = gpio_to_irq(ISP1763_INT_GPIO); + isp1763_resources[1].end = gpio_to_irq(ISP1763_INT_GPIO); +} + +static int isp1763_setup_gpio(int enable) +{ + int status = 0; + + if (enable) { + status = gpio_request(ISP1763_INT_GPIO, "isp1763_usb"); + if (status) { + pr_err("%s:Failed to request GPIO %d\n", + __func__, ISP1763_INT_GPIO); + return status; + } + status = gpio_direction_input(ISP1763_INT_GPIO); + if (status) { + pr_err("%s:Failed to configure GPIO %d\n", + __func__, ISP1763_INT_GPIO); + goto gpio_free_int; + } + status = gpio_request(ISP1763_RST_GPIO, "isp1763_usb"); + if (status) { + pr_err("%s:Failed to request GPIO %d\n", + __func__, ISP1763_RST_GPIO); + goto gpio_free_int; + } + status = gpio_direction_output(ISP1763_RST_GPIO, 1); + if (status) { + pr_err("%s:Failed to configure GPIO %d\n", + __func__, ISP1763_RST_GPIO); + goto gpio_free_rst; + } + pr_debug("\nISP GPIO configuration done\n"); + return status; + } + +gpio_free_rst: + gpio_free(ISP1763_RST_GPIO); +gpio_free_int: + gpio_free(ISP1763_INT_GPIO); + + return status; +} +static struct isp1763_platform_data isp1763_pdata = { + .reset_gpio = ISP1763_RST_GPIO, + .setup_gpio = isp1763_setup_gpio +}; + +static struct platform_device isp1763_device = { + .name = "isp1763_usb", + .num_resources = ARRAY_SIZE(isp1763_resources), + .resource = isp1763_resources, + .dev = { + .platform_data = &isp1763_pdata + } +}; +#endif + +#if defined(CONFIG_USB_MSM_72K) || defined(CONFIG_USB_EHCI_MSM_72K) +static struct msm_otg_platform_data msm_otg_pdata; +static struct regulator *ldo6_3p3; +static struct regulator *ldo7_1p8; +static struct regulator *vdd_cx; +#define PMICID_INT PM8058_GPIO_IRQ(PM8058_IRQ_BASE, 36) +#define PMIC_ID_GPIO 36 +notify_vbus_state notify_vbus_state_func_ptr; +static int usb_phy_susp_dig_vol = 750000; +static int pmic_id_notif_supported; + +#ifdef CONFIG_USB_EHCI_MSM_72K +#define USB_PMIC_ID_DET_DELAY msecs_to_jiffies(100) +struct delayed_work pmic_id_det; + +static int __init usb_id_pin_rework_setup(char *support) +{ + if (strncmp(support, "true", 4) == 0) + pmic_id_notif_supported = 1; + + return 1; +} +__setup("usb_id_pin_rework=", usb_id_pin_rework_setup); + +static void pmic_id_detect(struct work_struct *w) +{ + int val = gpio_get_value_cansleep(PM8058_GPIO_PM_TO_SYS(36)); + pr_debug("%s(): gpio_read_value = %d\n", __func__, val); + + if (notify_vbus_state_func_ptr) + (*notify_vbus_state_func_ptr) (val); +} + +static irqreturn_t pmic_id_on_irq(int irq, void *data) +{ + /* + * Spurious interrupts are observed on pmic gpio line + * even though there is no state change on USB ID. Schedule the + * work to to allow debounce on gpio + */ + schedule_delayed_work(&pmic_id_det, USB_PMIC_ID_DET_DELAY); + + return IRQ_HANDLED; +} + +static int msm_hsusb_phy_id_setup_init(int init) +{ + unsigned ret; + + struct pm8xxx_mpp_config_data hsusb_phy_mpp = { + .type = PM8XXX_MPP_TYPE_D_OUTPUT, + .level = PM8901_MPP_DIG_LEVEL_L5, + }; + + if (init) { + hsusb_phy_mpp.control = PM8XXX_MPP_DOUT_CTRL_HIGH; + ret = pm8xxx_mpp_config(PM8901_MPP_PM_TO_SYS(1), + &hsusb_phy_mpp); + if (ret < 0) + pr_err("%s:MPP2 configuration failed\n", __func__); + } else { + hsusb_phy_mpp.control = PM8XXX_MPP_DOUT_CTRL_LOW; + ret = pm8xxx_mpp_config(PM8901_MPP_PM_TO_SYS(1), + &hsusb_phy_mpp); + if (ret < 0) + pr_err("%s:MPP2 un config failed\n", __func__); + } + return ret; +} + +static int msm_hsusb_pmic_id_notif_init(void (*callback)(int online), int init) +{ + unsigned ret = -ENODEV; + + struct pm_gpio pmic_id_cfg = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_1P5, + .function = PM_GPIO_FUNC_NORMAL, + .vin_sel = 2, + .inv_int_pol = 0, + }; + struct pm_gpio pmic_id_uncfg = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_NO, + .function = PM_GPIO_FUNC_NORMAL, + .vin_sel = 2, + .inv_int_pol = 0, + }; + if (!callback) + return -EINVAL; + + if (machine_is_msm8x60_fluid()) + return -ENOTSUPP; + + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 2) { + pr_debug("%s: USB_ID pin is not routed to PMIC" + "on V1 surf/ffa\n", __func__); + return -ENOTSUPP; + } + + if ((machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa() || + machine_is_msm8x60_ffa()) && !pmic_id_notif_supported) { + pr_debug("%s: USB_ID is not routed to PMIC" + "on V2 ffa\n", __func__); + return -ENOTSUPP; + } + + usb_phy_susp_dig_vol = 500000; + + if (init) { + notify_vbus_state_func_ptr = callback; + INIT_DELAYED_WORK(&pmic_id_det, pmic_id_detect); + ret = pm8xxx_gpio_config(PM8058_GPIO_PM_TO_SYS(PMIC_ID_GPIO), + &pmic_id_cfg); + if (ret) { + pr_err("%s:return val of pm8xxx_gpio_config: %d\n", + __func__, ret); + return ret; + } + ret = request_threaded_irq(PMICID_INT, NULL, pmic_id_on_irq, + (IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING), + "msm_otg_id", NULL); + if (ret) { + pr_err("%s:pmic_usb_id interrupt registration failed", + __func__); + return ret; + } + msm_otg_pdata.pmic_id_irq = PMICID_INT; + } else { + usb_phy_susp_dig_vol = 750000; + free_irq(PMICID_INT, 0); + ret = pm8xxx_gpio_config(PM8058_GPIO_PM_TO_SYS(PMIC_ID_GPIO), + &pmic_id_uncfg); + if (ret) { + pr_err("%s: return val of pm8xxx_gpio_config: %d\n", + __func__, ret); + return ret; + } + msm_otg_pdata.pmic_id_irq = 0; + cancel_delayed_work_sync(&pmic_id_det); + notify_vbus_state_func_ptr = NULL; + } + return 0; +} +#endif + +#define USB_PHY_OPERATIONAL_MIN_VDD_DIG_VOL 1000000 +#define USB_PHY_MAX_VDD_DIG_VOL 1320000 +static int msm_hsusb_init_vddcx(int init) +{ + int ret = 0; + + if (init) { + vdd_cx = regulator_get(NULL, "8058_s1"); + if (IS_ERR(vdd_cx)) { + return PTR_ERR(vdd_cx); + } + + ret = regulator_set_voltage(vdd_cx, + USB_PHY_OPERATIONAL_MIN_VDD_DIG_VOL, + USB_PHY_MAX_VDD_DIG_VOL); + if (ret) { + pr_err("%s: unable to set the voltage for regulator" + "vdd_cx\n", __func__); + regulator_put(vdd_cx); + return ret; + } + + ret = regulator_enable(vdd_cx); + if (ret) { + pr_err("%s: unable to enable regulator" + "vdd_cx\n", __func__); + regulator_put(vdd_cx); + } + } else { + ret = regulator_disable(vdd_cx); + if (ret) { + pr_err("%s: Unable to disable the regulator:" + "vdd_cx\n", __func__); + return ret; + } + + regulator_put(vdd_cx); + } + + return ret; +} + +static int msm_hsusb_config_vddcx(int high) +{ + int max_vol = USB_PHY_MAX_VDD_DIG_VOL; + int min_vol; + int ret; + + if (high) + min_vol = USB_PHY_OPERATIONAL_MIN_VDD_DIG_VOL; + else + min_vol = usb_phy_susp_dig_vol; + + ret = regulator_set_voltage(vdd_cx, min_vol, max_vol); + if (ret) { + pr_err("%s: unable to set the voltage for regulator" + "vdd_cx\n", __func__); + return ret; + } + + pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); + + return ret; +} + +#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ +#define USB_PHY_3P3_VOL_MAX 3050000 /* uV */ +#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ +#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ +#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ +#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ +#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ +static int msm_hsusb_ldo_init(int init) +{ + int rc = 0; + + if (init) { + ldo6_3p3 = regulator_get(NULL, "8058_l6"); + if (IS_ERR(ldo6_3p3)) + return PTR_ERR(ldo6_3p3); + + ldo7_1p8 = regulator_get(NULL, "8058_l7"); + if (IS_ERR(ldo7_1p8)) { + rc = PTR_ERR(ldo7_1p8); + goto put_3p3; + } + + rc = regulator_set_voltage(ldo6_3p3, USB_PHY_3P3_VOL_MIN, + USB_PHY_3P3_VOL_MAX); + if (rc) { + pr_err("%s: Unable to set voltage level for" + "ldo6_3p3 regulator\n", __func__); + goto put_1p8; + } + rc = regulator_enable(ldo6_3p3); + if (rc) { + pr_err("%s: Unable to enable the regulator:" + "ldo6_3p3\n", __func__); + goto put_1p8; + } + rc = regulator_set_voltage(ldo7_1p8, USB_PHY_1P8_VOL_MIN, + USB_PHY_1P8_VOL_MAX); + if (rc) { + pr_err("%s: Unable to set voltage level for" + "ldo7_1p8 regulator\n", __func__); + goto disable_3p3; + } + rc = regulator_enable(ldo7_1p8); + if (rc) { + pr_err("%s: Unable to enable the regulator:" + "ldo7_1p8\n", __func__); + goto disable_3p3; + } + + return 0; + } + + regulator_disable(ldo7_1p8); +disable_3p3: + regulator_disable(ldo6_3p3); +put_1p8: + regulator_put(ldo7_1p8); +put_3p3: + regulator_put(ldo6_3p3); + return rc; +} + +static int msm_hsusb_ldo_enable(int on) +{ + int ret = 0; + + if (!ldo7_1p8 || IS_ERR(ldo7_1p8)) { + pr_err("%s: ldo7_1p8 is not initialized\n", __func__); + return -ENODEV; + } + + if (!ldo6_3p3 || IS_ERR(ldo6_3p3)) { + pr_err("%s: ldo6_3p3 is not initialized\n", __func__); + return -ENODEV; + } + + if (on) { + ret = regulator_set_optimum_mode(ldo7_1p8, + USB_PHY_1P8_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator:" + "ldo7_1p8\n", __func__); + return ret; + } + ret = regulator_set_optimum_mode(ldo6_3p3, + USB_PHY_3P3_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator:" + "ldo6_3p3\n", __func__); + regulator_set_optimum_mode(ldo7_1p8, + USB_PHY_1P8_LPM_LOAD); + return ret; + } + } else { + ret = regulator_set_optimum_mode(ldo7_1p8, + USB_PHY_1P8_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator:" + "ldo7_1p8\n", __func__); + ret = regulator_set_optimum_mode(ldo6_3p3, + USB_PHY_3P3_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator:" + "ldo6_3p3\n", __func__); + } + + pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); + return ret < 0 ? ret : 0; + } +#endif +#ifdef CONFIG_USB_EHCI_MSM_72K +#if defined(CONFIG_SMB137B_CHARGER) || defined(CONFIG_SMB137B_CHARGER_MODULE) +static void msm_hsusb_smb137b_vbus_power(unsigned phy_info, int on) +{ + static int vbus_is_on; + + /* If VBUS is already on (or off), do nothing. */ + if (on == vbus_is_on) + return; + smb137b_otg_power(on); + vbus_is_on = on; +} +#endif +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + static struct regulator *votg_5v_switch; + static struct regulator *ext_5v_reg; + static int vbus_is_on; + + /* If VBUS is already on (or off), do nothing. */ + if (on == vbus_is_on) + return; + + if (!votg_5v_switch) { + votg_5v_switch = regulator_get(NULL, "8901_usb_otg"); + if (IS_ERR(votg_5v_switch)) { + pr_err("%s: unable to get votg_5v_switch\n", __func__); + return; + } + } + if (!ext_5v_reg) { + ext_5v_reg = regulator_get(NULL, "8901_mpp0"); + if (IS_ERR(ext_5v_reg)) { + pr_err("%s: unable to get ext_5v_reg\n", __func__); + return; + } + } + if (on) { + if (regulator_enable(ext_5v_reg)) { + pr_err("%s: Unable to enable the regulator:" + " ext_5v_reg\n", __func__); + return; + } + if (regulator_enable(votg_5v_switch)) { + pr_err("%s: Unable to enable the regulator:" + " votg_5v_switch\n", __func__); + return; + } + } else { + if (regulator_disable(votg_5v_switch)) + pr_err("%s: Unable to enable the regulator:" + " votg_5v_switch\n", __func__); + if (regulator_disable(ext_5v_reg)) + pr_err("%s: Unable to enable the regulator:" + " ext_5v_reg\n", __func__); + } + + vbus_is_on = on; +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), + .power_budget = 390, +}; +#endif + +#ifdef CONFIG_BATTERY_MSM8X60 +static int msm_hsusb_pmic_vbus_notif_init(void (*callback)(int online), + int init) +{ + int ret = -ENOTSUPP; + +#if defined(CONFIG_SMB137B_CHARGER) || defined(CONFIG_SMB137B_CHARGER_MODULE) + if (machine_is_msm8x60_fluid()) { + if (init) + msm_charger_register_vbus_sn(callback); + else + msm_charger_unregister_vbus_sn(callback); + return 0; + } +#endif + /* ID and VBUS lines are connected to pmic on 8660.V2.SURF, + * hence, irrespective of either peripheral only mode or + * OTG (host and peripheral) modes, can depend on pmic for + * vbus notifications + */ + if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) + && (machine_is_msm8x60_surf() || + pmic_id_notif_supported)) { + if (init) + ret = msm_charger_register_vbus_sn(callback); + else { + msm_charger_unregister_vbus_sn(callback); + ret = 0; + } + } else { +#if !defined(CONFIG_USB_EHCI_MSM_72K) + if (init) + ret = msm_charger_register_vbus_sn(callback); + else { + msm_charger_unregister_vbus_sn(callback); + ret = 0; + } +#endif + } + return ret; +} +#endif + +#if defined(CONFIG_USB_MSM_72K) || defined(CONFIG_USB_EHCI_MSM_72K) +static struct msm_otg_platform_data msm_otg_pdata = { + /* if usb link is in sps there is no need for + * usb pclk as dayatona fabric clock will be + * used instead + */ + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .se1_gating = SE1_GATING_DISABLE, + .bam_disable = 1, +#ifdef CONFIG_USB_EHCI_MSM_72K + .pmic_id_notif_init = msm_hsusb_pmic_id_notif_init, + .phy_id_setup_init = msm_hsusb_phy_id_setup_init, +#endif +#ifdef CONFIG_USB_EHCI_MSM_72K + .vbus_power = msm_hsusb_vbus_power, +#endif +#ifdef CONFIG_BATTERY_MSM8X60 + .pmic_vbus_notif_init = msm_hsusb_pmic_vbus_notif_init, +#endif + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, + .config_vddcx = msm_hsusb_config_vddcx, + .init_vddcx = msm_hsusb_init_vddcx, +#ifdef CONFIG_BATTERY_MSM8X60 + .chg_vbus_draw = msm_charger_vbus_draw, +#endif +}; +#endif + +#ifdef CONFIG_USB_MSM_72K +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, +}; +#endif + +#ifdef CONFIG_USB_G_ANDROID + +#define PID_MAGIC_ID 0x71432909 +#define SERIAL_NUM_MAGIC_ID 0x61945374 +#define SERIAL_NUMBER_LENGTH 127 +#define DLOAD_USB_BASE_ADD 0x2A05F0C8 + +struct magic_num_struct { + uint32_t pid; + uint32_t serial_num; +}; + +struct dload_struct { + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint16_t reserved4; + uint16_t pid; + char serial_number[SERIAL_NUMBER_LENGTH]; + uint16_t reserved5; + struct magic_num_struct + magic_struct; +}; + +static int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) +{ + struct dload_struct __iomem *dload = 0; + + dload = ioremap(DLOAD_USB_BASE_ADD, sizeof(*dload)); + if (!dload) { + pr_err("%s: cannot remap I/O memory region: %08x\n", + __func__, DLOAD_USB_BASE_ADD); + return -ENXIO; + } + + pr_debug("%s: dload:%p pid:%x serial_num:%s\n", + __func__, dload, pid, snum); + /* update pid */ + dload->magic_struct.pid = PID_MAGIC_ID; + dload->pid = pid; + + /* update serial number */ + dload->magic_struct.serial_num = 0; + if (!snum) + return 0; + + dload->magic_struct.serial_num = SERIAL_NUM_MAGIC_ID; + strncpy(dload->serial_number, snum, SERIAL_NUMBER_LENGTH); + dload->serial_number[SERIAL_NUMBER_LENGTH - 1] = '\0'; + + iounmap(dload); + + return 0; +} + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + + +#endif + +#ifdef CONFIG_MSM_VPE +#ifndef CONFIG_MSM_CAMERA_V4L2 +static struct resource msm_vpe_resources[] = { + { + .start = 0x05300000, + .end = 0x05300000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VPE, + .end = INT_VPE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_vpe_device = { + .name = "msm_vpe", + .id = 0, + .num_resources = ARRAY_SIZE(msm_vpe_resources), + .resource = msm_vpe_resources, +}; +#endif +#endif + +#ifdef CONFIG_MSM_CAMERA +#ifndef CONFIG_MSM_CAMERA_V4L2 +#ifdef CONFIG_MSM_CAMERA_FLASH +#define VFE_CAMIF_TIMER1_GPIO 29 +#define VFE_CAMIF_TIMER2_GPIO 30 +#define VFE_CAMIF_TIMER3_GPIO_INT 31 +#define FUSION_VFE_CAMIF_TIMER1_GPIO 42 +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_PMIC, + ._fsrc.pmic_src.num_of_src = 2, + ._fsrc.pmic_src.low_current = 100, + ._fsrc.pmic_src.high_current = 300, + ._fsrc.pmic_src.led_src_1 = PMIC8058_ID_FLASH_LED_0, + ._fsrc.pmic_src.led_src_2 = PMIC8058_ID_FLASH_LED_1, + ._fsrc.pmic_src.pmic_set_current = pm8058_set_flash_led_current, +}; +#ifdef CONFIG_IMX074 +static struct msm_camera_sensor_strobe_flash_data strobe_flash_xenon = { + .flash_trigger = VFE_CAMIF_TIMER2_GPIO, + .flash_charge = VFE_CAMIF_TIMER1_GPIO, + .flash_charge_done = VFE_CAMIF_TIMER3_GPIO_INT, + .flash_recharge_duration = 50000, + .irq = MSM_GPIO_TO_INT(VFE_CAMIF_TIMER3_GPIO_INT), +}; +#endif +#endif + +int msm_cam_gpio_tbl[] = { + 32,/*CAMIF_MCLK*/ + 47,/*CAMIF_I2C_DATA*/ + 48,/*CAMIF_I2C_CLK*/ + 105,/*STANDBY*/ +}; + +enum msm_cam_stat{ + MSM_CAM_OFF, + MSM_CAM_ON, +}; + +static int config_gpio_table(enum msm_cam_stat stat) +{ + int rc = 0, i = 0; + if (stat == MSM_CAM_ON) { + for (i = 0; i < ARRAY_SIZE(msm_cam_gpio_tbl); i++) { + rc = gpio_request(msm_cam_gpio_tbl[i], "CAM_GPIO"); + if (unlikely(rc < 0)) { + pr_err("%s not able to get gpio\n", __func__); + for (i--; i >= 0; i--) + gpio_free(msm_cam_gpio_tbl[i]); + break; + } + } + } else { + for (i = 0; i < ARRAY_SIZE(msm_cam_gpio_tbl); i++) + gpio_free(msm_cam_gpio_tbl[i]); + } + return rc; +} + +static struct msm_camera_sensor_platform_info sensor_board_info = { + .mount_angle = 0 +}; + +/*external regulator VREG_5V*/ +static struct regulator *reg_flash_5V; + +static int config_camera_on_gpios_fluid(void) +{ + int rc = 0; + + reg_flash_5V = regulator_get(NULL, "8901_mpp0"); + if (IS_ERR(reg_flash_5V)) { + pr_err("'%s' regulator not found, rc=%ld\n", + "8901_mpp0", IS_ERR(reg_flash_5V)); + return -ENODEV; + } + + rc = regulator_enable(reg_flash_5V); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "8901_mpp0", rc); + regulator_put(reg_flash_5V); + return rc; + } + +#ifdef CONFIG_IMX074 + sensor_board_info.mount_angle = 90; +#endif + rc = config_gpio_table(MSM_CAM_ON); + if (rc < 0) { + printk(KERN_ERR "%s: CAMSENSOR gpio table request" + "failed\n", __func__); + return rc; + } + + rc = gpio_request(GPIO_EXT_CAMIF_PWR_EN, "CAM_EN"); + if (rc < 0) { + printk(KERN_ERR "%s: CAMSENSOR gpio %d request" + "failed\n", __func__, GPIO_EXT_CAMIF_PWR_EN); + regulator_disable(reg_flash_5V); + regulator_put(reg_flash_5V); + return rc; + } + gpio_direction_output(GPIO_EXT_CAMIF_PWR_EN, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_EXT_CAMIF_PWR_EN, 1); + + + /*Enable LED_FLASH_EN*/ + rc = gpio_request(GPIO_LED_FLASH_EN, "LED_FLASH_EN"); + if (rc < 0) { + printk(KERN_ERR "%s: CAMSENSOR gpio %d request" + "failed\n", __func__, GPIO_LED_FLASH_EN); + + regulator_disable(reg_flash_5V); + regulator_put(reg_flash_5V); + config_gpio_table(MSM_CAM_OFF); + gpio_set_value_cansleep(GPIO_EXT_CAMIF_PWR_EN, 0); + gpio_free(GPIO_EXT_CAMIF_PWR_EN); + return rc; + } + gpio_direction_output(GPIO_LED_FLASH_EN, 1); + msleep(20); + return rc; +} + + +static void config_camera_off_gpios_fluid(void) +{ + regulator_disable(reg_flash_5V); + regulator_put(reg_flash_5V); + + gpio_direction_output(GPIO_LED_FLASH_EN, 0); + gpio_free(GPIO_LED_FLASH_EN); + + config_gpio_table(MSM_CAM_OFF); + + gpio_set_value_cansleep(GPIO_EXT_CAMIF_PWR_EN, 0); + gpio_free(GPIO_EXT_CAMIF_PWR_EN); +} +static int config_camera_on_gpios(void) +{ + int rc = 0; + + if (machine_is_msm8x60_fluid()) + return config_camera_on_gpios_fluid(); + + rc = config_gpio_table(MSM_CAM_ON); + if (rc < 0) { + printk(KERN_ERR "%s: CAMSENSOR gpio table request" + "failed\n", __func__); + return rc; + } + + if (!machine_is_msm8x60_dragon()) { + rc = gpio_request(GPIO_EXT_CAMIF_PWR_EN, "CAM_EN"); + if (rc < 0) { + config_gpio_table(MSM_CAM_OFF); + pr_err("%s: CAMSENSOR gpio %d request" + "failed\n", __func__, GPIO_EXT_CAMIF_PWR_EN); + return rc; + } + gpio_direction_output(GPIO_EXT_CAMIF_PWR_EN, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_EXT_CAMIF_PWR_EN, 1); + } + +#ifdef CONFIG_MSM_CAMERA_FLASH +#ifdef CONFIG_IMX074 + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) + strobe_flash_xenon.flash_charge = FUSION_VFE_CAMIF_TIMER1_GPIO; +#endif +#endif + return rc; +} + +static void config_camera_off_gpios(void) +{ + if (machine_is_msm8x60_fluid()) + return config_camera_off_gpios_fluid(); + + + config_gpio_table(MSM_CAM_OFF); + + if (!machine_is_msm8x60_dragon()) { + gpio_set_value_cansleep(GPIO_EXT_CAMIF_PWR_EN, 0); + gpio_free(GPIO_EXT_CAMIF_PWR_EN); + } +} + +#ifdef CONFIG_QS_S5K4E1 + +#define QS_CAM_HC37_CAM_PD PM8058_GPIO_PM_TO_SYS(26) + +static int config_camera_on_gpios_qs_cam_fluid(void) +{ + int rc = 0; + + /* request QS_CAM_HC37_CAM_PD as an output to HC37 ASIC pin CAM_PD */ + rc = gpio_request(QS_CAM_HC37_CAM_PD, "QS_CAM_HC37_CAM_PD"); + if (rc < 0) { + printk(KERN_ERR "%s: QS_CAM_HC37_CAM_PD gpio %d request" + " failed\n", __func__, QS_CAM_HC37_CAM_PD); + return rc; + } + gpio_direction_output(QS_CAM_HC37_CAM_PD, 0); + msleep(20); + gpio_set_value_cansleep(QS_CAM_HC37_CAM_PD, 1); + msleep(20); + + /* + * Set GPIO_AUX_CAM_2P7_EN to 1 on North Expander IO2 + * to enable 2.7V power to Camera + */ + rc = gpio_request(GPIO_AUX_CAM_2P7_EN, "CAM_2P7_EN"); + if (rc < 0) { + printk(KERN_ERR "%s: CAMSENSOR gpio %d request" + " failed\n", __func__, GPIO_AUX_CAM_2P7_EN); + gpio_set_value_cansleep(QS_CAM_HC37_CAM_PD, 0); + gpio_free(QS_CAM_HC37_CAM_PD); + return rc; + } + gpio_direction_output(GPIO_AUX_CAM_2P7_EN, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_AUX_CAM_2P7_EN, 1); + msleep(20); + + rc = config_camera_on_gpios_fluid(); + if (rc < 0) { + printk(KERN_ERR "%s: config_camera_on_gpios_fluid" + " failed\n", __func__); + gpio_set_value_cansleep(QS_CAM_HC37_CAM_PD, 0); + gpio_free(QS_CAM_HC37_CAM_PD); + gpio_set_value_cansleep(GPIO_AUX_CAM_2P7_EN, 0); + gpio_free(GPIO_AUX_CAM_2P7_EN); + return rc; + } + return rc; +} + +static void config_camera_off_gpios_qs_cam_fluid(void) +{ + /* + * Set GPIO_AUX_CAM_2P7_EN to 0 on North Expander IO2 + * to disable 2.7V power to Camera + */ + gpio_set_value_cansleep(GPIO_AUX_CAM_2P7_EN, 0); + gpio_free(GPIO_AUX_CAM_2P7_EN); + + /* set QS_CAM_HC37_CAM_PD to 0 to power off HC37 ASIC*/ + gpio_set_value_cansleep(QS_CAM_HC37_CAM_PD, 0); + gpio_free(QS_CAM_HC37_CAM_PD); + + config_camera_off_gpios_fluid(); + return; +} + +static int config_camera_on_gpios_qs_cam(void) +{ + int rc = 0; + + if (machine_is_msm8x60_fluid()) + return config_camera_on_gpios_qs_cam_fluid(); + + rc = config_camera_on_gpios(); + return rc; +} + +static void config_camera_off_gpios_qs_cam(void) +{ + if (machine_is_msm8x60_fluid()) + return config_camera_off_gpios_qs_cam_fluid(); + + config_camera_off_gpios(); + return; +} +#endif + +static int config_camera_on_gpios_web_cam(void) +{ + int rc = 0; + rc = config_gpio_table(MSM_CAM_ON); + if (rc < 0) { + printk(KERN_ERR "%s: CAMSENSOR gpio table request" + "failed\n", __func__); + return rc; + } + + if (!(machine_is_msm8x60_fluid() || machine_is_msm8x60_dragon())) { + rc = gpio_request(GPIO_WEB_CAMIF_STANDBY, "CAM_EN"); + if (rc < 0) { + config_gpio_table(MSM_CAM_OFF); + pr_err(KERN_ERR "%s: CAMSENSOR gpio %d request" + "failed\n", __func__, GPIO_WEB_CAMIF_STANDBY); + return rc; + } + gpio_direction_output(GPIO_WEB_CAMIF_STANDBY, 0); + } + return rc; +} + +static void config_camera_off_gpios_web_cam(void) +{ + config_gpio_table(MSM_CAM_OFF); + if (!(machine_is_msm8x60_fluid() || machine_is_msm8x60_dragon())) { + gpio_set_value_cansleep(GPIO_WEB_CAMIF_STANDBY, 1); + gpio_free(GPIO_WEB_CAMIF_STANDBY); + } + return; +} + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors cam_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 283115520, + .ib = 452984832, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 283115520, + .ib = 452984832, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 283115520, + .ib = 452984832, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 319610880, + .ib = 511377408, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 566231040, + .ib = 905969664, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 69984000, + .ib = 111974400, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 320864256, + .ib = 513382810, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 320864256, + .ib = 513382810, + }, +}; + +static struct msm_bus_vectors cam_zsl_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 566231040, + .ib = 905969664, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 706199040, + .ib = 1129918464, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 320864256, + .ib = 513382810, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 320864256, + .ib = 513382810, + }, +}; + +static struct msm_bus_vectors cam_stereo_video_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 212336640, + .ib = 339738624, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 25090560, + .ib = 40144896, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 239708160, + .ib = 383533056, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 79902720, + .ib = 127844352, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors cam_stereo_snapshot_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 300902400, + .ib = 481443840, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 230307840, + .ib = 368492544, + }, + { + .src = MSM_BUS_MASTER_VPE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 245113344, + .ib = 392181351, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 106536960, + .ib = 170459136, + }, + { + .src = MSM_BUS_MASTER_JPEG_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 106536960, + .ib = 170459136, + }, +}; + +static struct msm_bus_paths cam_bus_client_config[] = { + { + ARRAY_SIZE(cam_init_vectors), + cam_init_vectors, + }, + { + ARRAY_SIZE(cam_preview_vectors), + cam_preview_vectors, + }, + { + ARRAY_SIZE(cam_video_vectors), + cam_video_vectors, + }, + { + ARRAY_SIZE(cam_snapshot_vectors), + cam_snapshot_vectors, + }, + { + ARRAY_SIZE(cam_zsl_vectors), + cam_zsl_vectors, + }, + { + ARRAY_SIZE(cam_stereo_video_vectors), + cam_stereo_video_vectors, + }, + { + ARRAY_SIZE(cam_stereo_snapshot_vectors), + cam_stereo_snapshot_vectors, + }, +}; + +static struct msm_bus_scale_pdata cam_bus_client_pdata = { + cam_bus_client_config, + ARRAY_SIZE(cam_bus_client_config), + .name = "msm_camera", +}; +#endif + +struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.csiphy = 0x04800000, + .ioext.csisz = 0x00000400, + .ioext.csiirq = CSI_0_IRQ, + .ioclk.mclk_clk_rate = 24000000, + .ioclk.vfe_clk_rate = 228570000, +#ifdef CONFIG_MSM_BUS_SCALING + .cam_bus_scale_table = &cam_bus_client_pdata, +#endif +}; + +#ifdef CONFIG_QS_S5K4E1 +struct msm_camera_device_platform_data msm_camera_device_data_qs_cam = { + .camera_gpio_on = config_camera_on_gpios_qs_cam, + .camera_gpio_off = config_camera_off_gpios_qs_cam, + .ioext.csiphy = 0x04800000, + .ioext.csisz = 0x00000400, + .ioext.csiirq = CSI_0_IRQ, + .ioclk.mclk_clk_rate = 24000000, + .ioclk.vfe_clk_rate = 228570000, +#ifdef CONFIG_MSM_BUS_SCALING + .cam_bus_scale_table = &cam_bus_client_pdata, +#endif +}; +#endif + +struct msm_camera_device_platform_data msm_camera_device_data_web_cam = { + .camera_gpio_on = config_camera_on_gpios_web_cam, + .camera_gpio_off = config_camera_off_gpios_web_cam, + .ioext.csiphy = 0x04900000, + .ioext.csisz = 0x00000400, + .ioext.csiirq = CSI_1_IRQ, + .ioclk.mclk_clk_rate = 24000000, + .ioclk.vfe_clk_rate = 228570000, +#ifdef CONFIG_MSM_BUS_SCALING + .cam_bus_scale_table = &cam_bus_client_pdata, +#endif +}; + +struct resource msm_camera_resources[] = { + { + .start = 0x04500000, + .end = 0x04500000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = VFE_IRQ, + .end = VFE_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; +#ifdef CONFIG_MT9E013 +static struct msm_camera_sensor_platform_info mt9e013_sensor_8660_info = { + .mount_angle = 0 +}; + +static struct msm_camera_sensor_flash_data flash_mt9e013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = { + .sensor_name = "mt9e013", + .sensor_reset = 106, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9e013, + .strobe_flash_data = &strobe_flash_xenon, + .sensor_platform_info = &mt9e013_sensor_8660_info, + .csi_if = 1 +}; +struct platform_device msm_camera_sensor_mt9e013 = { + .name = "msm_camera_mt9e013", + .dev = { + .platform_data = &msm_camera_sensor_mt9e013_data, + }, +}; +#endif + +#ifdef CONFIG_IMX074 +static struct msm_camera_sensor_platform_info imx074_sensor_board_info = { + .mount_angle = 180 +}; + +static struct msm_camera_sensor_flash_data flash_imx074 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_imx074_data = { + .sensor_name = "imx074", + .sensor_reset = 106, + .sensor_pwd = 85, + .vcm_pwd = GPIO_AUX_CAM_2P7_EN, + .vcm_enable = 1, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_imx074, + .strobe_flash_data = &strobe_flash_xenon, + .sensor_platform_info = &imx074_sensor_board_info, + .csi_if = 1 +}; +struct platform_device msm_camera_sensor_imx074 = { + .name = "msm_camera_imx074", + .dev = { + .platform_data = &msm_camera_sensor_imx074_data, + }, +}; +#endif +#ifdef CONFIG_WEBCAM_OV9726 + +static struct msm_camera_sensor_platform_info ov9726_sensor_8660_info = { + .mount_angle = 0 +}; + +static struct msm_camera_sensor_flash_data flash_ov9726 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; +static struct msm_camera_sensor_info msm_camera_sensor_ov9726_data = { + .sensor_name = "ov9726", + .sensor_reset_enable = 1, + .sensor_reset = GPIO_FRONT_CAM_RESET_N, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_web_cam, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_ov9726, + .sensor_platform_info = &ov9726_sensor_8660_info, + .csi_if = 1 +}; +struct platform_device msm_camera_sensor_webcam_ov9726 = { + .name = "msm_camera_ov9726", + .dev = { + .platform_data = &msm_camera_sensor_ov9726_data, + }, +}; +#endif +#ifdef CONFIG_WEBCAM_OV7692 +static struct msm_camera_sensor_flash_data flash_ov7692 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; +static struct msm_camera_sensor_info msm_camera_sensor_ov7692_data = { + .sensor_name = "ov7692", + .sensor_reset = GPIO_WEB_CAMIF_RESET_N, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_web_cam, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_ov7692, + .csi_if = 1 +}; + +static struct platform_device msm_camera_sensor_webcam_ov7692 = { + .name = "msm_camera_ov7692", + .dev = { + .platform_data = &msm_camera_sensor_ov7692_data, + }, +}; +#endif +#ifdef CONFIG_VX6953 +static struct msm_camera_sensor_platform_info vx6953_sensor_8660_info = { + .mount_angle = 270 +}; + +static struct msm_camera_sensor_flash_data flash_vx6953 = { + .flash_type = MSM_CAMERA_FLASH_NONE, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_vx6953_data = { + .sensor_name = "vx6953", + .sensor_reset = 63, + .sensor_pwd = 63, + .vcm_pwd = GPIO_AUX_CAM_2P7_EN, + .vcm_enable = 1, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_vx6953, + .sensor_platform_info = &vx6953_sensor_8660_info, + .csi_if = 1 +}; +struct platform_device msm_camera_sensor_vx6953 = { + .name = "msm_camera_vx6953", + .dev = { + .platform_data = &msm_camera_sensor_vx6953_data, + }, +}; +#endif +#ifdef CONFIG_QS_S5K4E1 + +static struct msm_camera_sensor_platform_info qs_s5k4e1_sensor_8660_info = { +#ifdef CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT + .mount_angle = 90 +#else + .mount_angle = 0 +#endif +}; + +static char eeprom_data[864]; +static struct msm_camera_sensor_flash_data flash_qs_s5k4e1 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_qs_s5k4e1_data = { + .sensor_name = "qs_s5k4e1", + .sensor_reset = 106, + .sensor_pwd = 85, + .vcm_pwd = 1, + .vcm_enable = 0, + .pdata = &msm_camera_device_data_qs_cam, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_qs_s5k4e1, + .strobe_flash_data = &strobe_flash_xenon, + .sensor_platform_info = &qs_s5k4e1_sensor_8660_info, + .csi_if = 1, + .eeprom_data = eeprom_data, +}; +struct platform_device msm_camera_sensor_qs_s5k4e1 = { + .name = "msm_camera_qs_s5k4e1", + .dev = { + .platform_data = &msm_camera_sensor_qs_s5k4e1_data, + }, +}; +#endif +static struct i2c_board_info msm_camera_boardinfo[] __initdata = { + #ifdef CONFIG_MT9E013 + { + I2C_BOARD_INFO("mt9e013", 0x6C >> 2), + }, + #endif + #ifdef CONFIG_IMX074 + { + I2C_BOARD_INFO("imx074", 0x1A), + }, + #endif + #ifdef CONFIG_WEBCAM_OV7692 + { + I2C_BOARD_INFO("ov7692", 0x78), + }, + #endif + #ifdef CONFIG_WEBCAM_OV9726 + { + I2C_BOARD_INFO("ov9726", 0x10), + }, + #endif + #ifdef CONFIG_QS_S5K4E1 + { + I2C_BOARD_INFO("qs_s5k4e1", 0x20), + }, + #endif +}; + +static struct i2c_board_info msm_camera_dragon_boardinfo[] __initdata = { + #ifdef CONFIG_WEBCAM_OV9726 + { + I2C_BOARD_INFO("ov9726", 0x10), + }, + #endif + #ifdef CONFIG_VX6953 + { + I2C_BOARD_INFO("vx6953", 0x20), + }, + #endif +}; +#endif +#endif + +#ifdef CONFIG_MSM_GEMINI +static struct resource msm_gemini_resources[] = { + { + .start = 0x04600000, + .end = 0x04600000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_JPEG, + .end = INT_JPEG, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_gemini_device = { + .name = "msm_gemini", + .resource = msm_gemini_resources, + .num_resources = ARRAY_SIZE(msm_gemini_resources), +}; +#endif + +#ifdef CONFIG_I2C_QUP +static void gsbi_qup_i2c_gpio_config(int adap_id, int config_type) +{ +} + +static struct msm_i2c_platform_data msm_gsbi3_qup_i2c_pdata = { + .clk_freq = 384000, + .src_clk_rate = 24000000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi4_qup_i2c_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi7_qup_i2c_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi8_qup_i2c_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi9_qup_i2c_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi12_qup_i2c_pdata = { + .clk_freq = 100000, + .src_clk_rate = 24000000, + .use_gsbi_shared_mode = 1, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; +#endif + +#if defined(CONFIG_SPI_QUP) || defined(CONFIG_SPI_QUP_MODULE) +static struct msm_spi_platform_data msm_gsbi1_qup_spi_pdata = { + .max_clock_speed = 24000000, +}; + +static struct msm_spi_platform_data msm_gsbi10_qup_spi_pdata = { + .max_clock_speed = 24000000, +}; +#endif + +#ifdef CONFIG_I2C_SSBI +/* CODEC/TSSC SSBI */ +static struct msm_i2c_ssbi_platform_data msm_ssbi3_pdata = { + .controller_type = MSM_SBI_CTRL_SSBI, +}; +#endif + +#ifdef CONFIG_BATTERY_MSM +/* Use basic value for fake MSM battery */ +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .avail_chg_sources = AC_CHG, +}; + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; +#endif + +#ifdef CONFIG_FB_MSM_LCDC_DSUB +/* VGA = 1440 x 900 x 4(bpp) x 2(pages) + prim = 1024 x 600 x 4(bpp) x 2(pages) + This is the difference. */ +#define MSM_FB_DSUB_PMEM_ADDER (0xA32000-0x4B0000) +#else +#define MSM_FB_DSUB_PMEM_ADDER (0) +#endif + +/* Sensors DSPS platform data */ +#ifdef CONFIG_MSM_DSPS + +static struct dsps_gpio_info dsps_surf_gpios[] = { + { + .name = "compass_rst_n", + .num = GPIO_COMPASS_RST_N, + .on_val = 1, /* device not in reset */ + .off_val = 0, /* device in reset */ + }, + { + .name = "gpio_r_altimeter_reset_n", + .num = GPIO_R_ALTIMETER_RESET_N, + .on_val = 1, /* device not in reset */ + .off_val = 0, /* device in reset */ + } +}; + +static struct dsps_gpio_info dsps_fluid_gpios[] = { + { + .name = "gpio_n_altimeter_reset_n", + .num = GPIO_N_ALTIMETER_RESET_N, + .on_val = 1, /* device not in reset */ + .off_val = 0, /* device in reset */ + } +}; + +static void __init msm8x60_init_dsps(void) +{ + struct msm_dsps_platform_data *pdata = + msm_dsps_device.dev.platform_data; + /* + * On Fluid the Compass sensor Chip-Select (CS) is directly connected + * to the power supply and not controled via GPIOs. Fluid uses a + * different IO-Expender (north) than used on surf/ffa. + */ + if (machine_is_msm8x60_fluid()) { + /* fluid has different firmware, gpios */ + pdata->pil_name = DSPS_PIL_FLUID_NAME; + msm_pil_dsps.dev.platform_data = DSPS_PIL_FLUID_NAME; + pdata->gpios = dsps_fluid_gpios; + pdata->gpios_num = ARRAY_SIZE(dsps_fluid_gpios); + } else { + pdata->pil_name = DSPS_PIL_GENERIC_NAME; + msm_pil_dsps.dev.platform_data = DSPS_PIL_GENERIC_NAME; + pdata->gpios = dsps_surf_gpios; + pdata->gpios_num = ARRAY_SIZE(dsps_surf_gpios); + } + + platform_device_register(&msm_dsps_device); +} +#endif /* CONFIG_MSM_DSPS */ + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MSM_FB_PRIM_BUF_SIZE \ + (roundup((1024 * 600 * 4), 4096) * 3) /* 4 bpp x 3 pages */ +#else +#define MSM_FB_PRIM_BUF_SIZE \ + (roundup((1024 * 600 * 4), 4096) * 2) /* 4 bpp x 2 pages */ +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +#define MSM_FB_EXT_BUF_SIZE \ + (roundup((1920 * 1080 * 2), 4096) * 1) /* 2 bpp x 1 page */ +#elif defined(CONFIG_FB_MSM_TVOUT) +#define MSM_FB_EXT_BUF_SIZE \ + (roundup((720 * 576 * 2), 4096) * 2) /* 2 bpp x 2 pages */ +#else +#define MSM_FB_EXT_BUFT_SIZE 0 +#endif + +/* Note: must be multiple of 4096 */ +#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE + \ + MSM_FB_DSUB_PMEM_ADDER, 4096) + +#define MSM_PMEM_SF_SIZE 0x4000000 /* 64 Mbytes */ +#define MSM_HDMI_PRIM_PMEM_SF_SIZE 0x8000000 /* 128 Mbytes */ + +#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY +unsigned char hdmi_is_primary = 1; +#else +unsigned char hdmi_is_primary; +#endif + +#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */ + +#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096) +#else +#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0) +#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */ + +#define MSM_PMEM_KERNEL_EBI1_SIZE 0x3BC000 +#define MSM_PMEM_ADSP_SIZE 0x4200000 +#define MSM_PMEM_AUDIO_SIZE 0x4CF000 + +#define MSM_SMI_BASE 0x38000000 +#define MSM_SMI_SIZE 0x4000000 + +#define KERNEL_SMI_BASE (MSM_SMI_BASE) +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) +#define KERNEL_SMI_SIZE 0x000000 +#else +#define KERNEL_SMI_SIZE 0x600000 +#endif + +#define USER_SMI_BASE (KERNEL_SMI_BASE + KERNEL_SMI_SIZE) +#define USER_SMI_SIZE (MSM_SMI_SIZE - KERNEL_SMI_SIZE) +#define MSM_PMEM_SMIPOOL_SIZE USER_SMI_SIZE + +#define MSM_ION_HOLE_SIZE SZ_128K /* (128KB) */ +#define MSM_MM_FW_SIZE (0x200000 - MSM_ION_HOLE_SIZE) /*(2MB-128KB)*/ +#define MSM_ION_MM_SIZE 0x3800000 /* (56MB) */ +#define MSM_ION_MFC_SIZE SZ_8K + +#define MSM_MM_FW_BASE MSM_SMI_BASE +#define MSM_ION_HOLE_BASE (MSM_MM_FW_BASE + MSM_MM_FW_SIZE) +#define MSM_ION_MM_BASE (MSM_ION_HOLE_BASE + MSM_ION_HOLE_SIZE) +#define MSM_ION_MFC_BASE (MSM_ION_MM_BASE + MSM_ION_MM_SIZE) + +#define MSM_ION_SF_SIZE 0x4000000 /* 64MB */ +#define MSM_ION_CAMERA_SIZE MSM_PMEM_ADSP_SIZE + +#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK +#define MSM_ION_WB_SIZE 0xC00000 /* 12MB */ +#else +#define MSM_ION_WB_SIZE 0x600000 /* 6MB */ +#endif + +#define MSM_ION_QSECOM_SIZE 0x600000 /* (6MB) */ + +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +#define MSM_ION_AUDIO_SIZE MSM_PMEM_AUDIO_SIZE +#define MSM_ION_HEAP_NUM 9 +#define MSM_HDMI_PRIM_ION_SF_SIZE MSM_HDMI_PRIM_PMEM_SF_SIZE +static unsigned msm_ion_sf_size = MSM_ION_SF_SIZE; +#else +#define MSM_ION_HEAP_NUM 1 +#endif + +static unsigned fb_size; +static int __init fb_size_setup(char *p) +{ + fb_size = memparse(p, NULL); + return 0; +} +early_param("fb_size", fb_size_setup); + +static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) +{ + pmem_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); + +#ifdef CONFIG_ANDROID_PMEM +static unsigned pmem_sf_size = MSM_PMEM_SF_SIZE; +static int __init pmem_sf_size_setup(char *p) +{ + pmem_sf_size = memparse(p, NULL); + return 0; +} +early_param("pmem_sf_size", pmem_sf_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; + +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +static unsigned pmem_audio_size = MSM_PMEM_AUDIO_SIZE; + +static int __init pmem_audio_size_setup(char *p) +{ + pmem_audio_size = memparse(p, NULL); + return 0; +} +early_param("pmem_audio_size", pmem_audio_size_setup); +#endif + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +static void set_mdp_clocks_for_wuxga(void); + +static int msm_fb_detect_panel(const char *name) +{ + if (machine_is_msm8x60_fluid()) { + uint32_t soc_platform_version = socinfo_get_platform_version(); + if (SOCINFO_VERSION_MAJOR(soc_platform_version) < 3) { +#ifdef CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT + if (!strncmp(name, LCDC_SAMSUNG_OLED_PANEL_NAME, + strnlen(LCDC_SAMSUNG_OLED_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; +#endif + } else { /*P3 and up use AUO panel */ +#ifdef CONFIG_FB_MSM_LCDC_AUO_WVGA + if (!strncmp(name, LCDC_AUO_PANEL_NAME, + strnlen(LCDC_AUO_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; +#endif + } +#ifdef CONFIG_FB_MSM_LCDC_NT35582_WVGA + } else if machine_is_msm8x60_dragon() { + if (!strncmp(name, LCDC_NT35582_PANEL_NAME, + strnlen(LCDC_NT35582_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; +#endif + } else { + if (!strncmp(name, LCDC_SAMSUNG_WSVGA_PANEL_NAME, + strnlen(LCDC_SAMSUNG_WSVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + +#if !defined(CONFIG_FB_MSM_LCDC_AUTO_DETECT) && \ + !defined(CONFIG_FB_MSM_MIPI_PANEL_AUTO_DETECT) && \ + !defined(CONFIG_FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT) + if (!strncmp(name, MIPI_VIDEO_TOSHIBA_WVGA_PANEL_NAME, + strnlen(MIPI_VIDEO_TOSHIBA_WVGA_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME, + strnlen(MIPI_VIDEO_NOVATEK_QHD_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + if (!strncmp(name, MIPI_CMD_NOVATEK_QHD_PANEL_NAME, + strnlen(MIPI_CMD_NOVATEK_QHD_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; +#endif + } + + if (!strncmp(name, HDMI_PANEL_NAME, + strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + if (hdmi_is_primary) + set_mdp_clocks_for_wuxga(); + return 0; + } + + if (!strncmp(name, TVOUT_PANEL_NAME, + strnlen(TVOUT_PANEL_NAME, + PANEL_NAME_MAX_LEN))) + return 0; + + pr_warning("%s: not supported '%s'", __func__, name); + return -ENODEV; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev.platform_data = &msm_fb_pdata, +}; + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = {.platform_data = &android_pmem_pdata}, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct android_pmem_platform_data android_pmem_audio_pdata = { + .name = "pmem_audio", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_EBI1, +}; + +static struct platform_device android_pmem_audio_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_audio_pdata }, +}; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#define PMEM_BUS_WIDTH(_bw) \ + { \ + .vectors = &(struct msm_bus_vectors){ \ + .src = MSM_BUS_MASTER_AMPSS_M0, \ + .dst = MSM_BUS_SLAVE_SMI, \ + .ib = (_bw), \ + .ab = 0, \ + }, \ + .num_paths = 1, \ + } + +static struct msm_bus_paths mem_smi_table[] = { + [0] = PMEM_BUS_WIDTH(0), /* Off */ + [1] = PMEM_BUS_WIDTH(1), /* On */ +}; + +static struct msm_bus_scale_pdata smi_client_pdata = { + .usecase = mem_smi_table, + .num_usecases = ARRAY_SIZE(mem_smi_table), + .name = "mem_smi", +}; + +int request_smi_region(void *data) +{ + int bus_id = (int) data; + + msm_bus_scale_client_update_request(bus_id, 1); + return 0; +} + +int release_smi_region(void *data) +{ + int bus_id = (int) data; + + msm_bus_scale_client_update_request(bus_id, 0); + return 0; +} + +void *setup_smi_region(void) +{ + return (void *)msm_bus_scale_register_client(&smi_client_pdata); +} +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct android_pmem_platform_data android_pmem_smipool_pdata = { + .name = "pmem_smipool", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, + .memory_type = MEMTYPE_SMI, + .request_region = request_smi_region, + .release_region = release_smi_region, + .setup_region = setup_smi_region, + .map_on_demand = 1, +}; +static struct platform_device android_pmem_smipool_device = { + .name = "android_pmem", + .id = 7, + .dev = { .platform_data = &android_pmem_smipool_pdata }, +}; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + +#define GPIO_DONGLE_PWR_EN 258 +static void setup_display_power(void); +static int lcdc_vga_enabled; +static int vga_enable_request(int enable) +{ + if (enable) + lcdc_vga_enabled = 1; + else + lcdc_vga_enabled = 0; + setup_display_power(); + + return 0; +} + +#define GPIO_BACKLIGHT_PWM0 0 +#define GPIO_BACKLIGHT_PWM1 1 + +static int pmic_backlight_gpio[2] + = { GPIO_BACKLIGHT_PWM0, GPIO_BACKLIGHT_PWM1 }; +static struct msm_panel_common_pdata lcdc_samsung_panel_data = { + .gpio_num = pmic_backlight_gpio, /* two LPG CHANNELS for backlight */ + .vga_switch = vga_enable_request, +}; + +static struct platform_device lcdc_samsung_panel_device = { + .name = LCDC_SAMSUNG_WSVGA_PANEL_NAME, + .id = 0, + .dev = { + .platform_data = &lcdc_samsung_panel_data, + } +}; +#if (!defined(CONFIG_SPI_QUP)) && \ + (defined(CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT) || \ + defined(CONFIG_FB_MSM_LCDC_AUO_WVGA)) + +static int lcdc_spi_gpio_array_num[] = { + LCDC_SPI_GPIO_CLK, + LCDC_SPI_GPIO_CS, + LCDC_SPI_GPIO_MOSI, +}; + +static uint32_t lcdc_spi_gpio_config_data[] = { + GPIO_CFG(LCDC_SPI_GPIO_CLK, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(LCDC_SPI_GPIO_CS, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + GPIO_CFG(LCDC_SPI_GPIO_MOSI, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), +}; + +static void lcdc_config_spi_gpios(int enable) +{ + int n; + for (n = 0; n < ARRAY_SIZE(lcdc_spi_gpio_config_data); ++n) + gpio_tlmm_config(lcdc_spi_gpio_config_data[n], 0); +} +#endif + +#ifdef CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT +#ifdef CONFIG_SPI_QUP +static struct spi_board_info lcdc_samsung_spi_board_info[] __initdata = { + { + .modalias = LCDC_SAMSUNG_SPI_DEVICE_NAME, + .mode = SPI_MODE_3, + .bus_num = 1, + .chip_select = 0, + .max_speed_hz = 10800000, + } +}; +#endif /* CONFIG_SPI_QUP */ + +static struct msm_panel_common_pdata lcdc_samsung_oled_panel_data = { +#ifndef CONFIG_SPI_QUP + .panel_config_gpio = lcdc_config_spi_gpios, + .gpio_num = lcdc_spi_gpio_array_num, +#endif +}; + +static struct platform_device lcdc_samsung_oled_panel_device = { + .name = LCDC_SAMSUNG_OLED_PANEL_NAME, + .id = 0, + .dev.platform_data = &lcdc_samsung_oled_panel_data, +}; +#endif /*CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT */ + +#ifdef CONFIG_FB_MSM_LCDC_AUO_WVGA +#ifdef CONFIG_SPI_QUP +static struct spi_board_info lcdc_auo_spi_board_info[] __initdata = { + { + .modalias = LCDC_AUO_SPI_DEVICE_NAME, + .mode = SPI_MODE_3, + .bus_num = 1, + .chip_select = 0, + .max_speed_hz = 10800000, + } +}; +#endif + +static struct msm_panel_common_pdata lcdc_auo_wvga_panel_data = { +#ifndef CONFIG_SPI_QUP + .panel_config_gpio = lcdc_config_spi_gpios, + .gpio_num = lcdc_spi_gpio_array_num, +#endif +}; + +static struct platform_device lcdc_auo_wvga_panel_device = { + .name = LCDC_AUO_PANEL_NAME, + .id = 0, + .dev.platform_data = &lcdc_auo_wvga_panel_data, +}; +#endif /*CONFIG_FB_MSM_LCDC_AUO_WVGA*/ + +#ifdef CONFIG_FB_MSM_LCDC_NT35582_WVGA + +#define GPIO_NT35582_RESET 94 +#define GPIO_NT35582_BL_EN_HW_PIN 24 +#define GPIO_NT35582_BL_EN \ + PM8058_GPIO_PM_TO_SYS(GPIO_NT35582_BL_EN_HW_PIN - 1) + +static int lcdc_nt35582_pmic_gpio[] = {GPIO_NT35582_BL_EN }; + +static struct msm_panel_common_pdata lcdc_nt35582_panel_data = { + .gpio_num = lcdc_nt35582_pmic_gpio, +}; + +static struct platform_device lcdc_nt35582_panel_device = { + .name = LCDC_NT35582_PANEL_NAME, + .id = 0, + .dev = { + .platform_data = &lcdc_nt35582_panel_data, + } +}; + +static struct spi_board_info lcdc_nt35582_spi_board_info[] __initdata = { + { + .modalias = "lcdc_nt35582_spi", + .mode = SPI_MODE_0, + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 1100000, + } +}; +#endif + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +static struct resource hdmi_msm_resources[] = { + { + .name = "hdmi_msm_qfprom_addr", + .start = 0x00700000, + .end = 0x007060FF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_hdmi_addr", + .start = 0x04A00000, + .end = 0x04A00FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = "hdmi_msm_irq", + .start = HDMI_IRQ, + .end = HDMI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int hdmi_enable_5v(int on); +static int hdmi_core_power(int on, int show); +static int hdmi_cec_power(int on); + +static struct msm_hdmi_platform_data hdmi_msm_data = { + .irq = HDMI_IRQ, + .enable_5v = hdmi_enable_5v, + .core_power = hdmi_core_power, + .cec_power = hdmi_cec_power, +}; + +static struct platform_device hdmi_msm_device = { + .name = "hdmi_msm", + .id = 0, + .num_resources = ARRAY_SIZE(hdmi_msm_resources), + .resource = hdmi_msm_resources, + .dev.platform_data = &hdmi_msm_data, +}; +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ + +#ifdef CONFIG_FB_MSM_MIPI_DSI +static struct platform_device mipi_dsi_toshiba_panel_device = { + .name = "mipi_toshiba", + .id = 0, +}; + +#define FPGA_3D_GPIO_CONFIG_ADDR 0x1D00017A + +static struct mipi_dsi_panel_platform_data novatek_pdata = { + .fpga_3d_config_addr = FPGA_3D_GPIO_CONFIG_ADDR, + .fpga_ctrl_mode = FPGA_EBI2_INTF, +}; + +static struct platform_device mipi_dsi_novatek_panel_device = { + .name = "mipi_novatek", + .id = 0, + .dev = { + .platform_data = &novatek_pdata, + } +}; +#endif + +static void __init msm8x60_allocate_memory_regions(void) +{ + void *addr; + unsigned long size; + + if (hdmi_is_primary) + size = roundup((1920 * 1088 * 4 * 2), 4096); + else + size = MSM_FB_SIZE; + + addr = alloc_bootmem_align(size, 0x1000); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); + +} + +void __init msm8x60_set_display_params(char *prim_panel, char *ext_panel) +{ + if (strnlen(prim_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.prim_panel_name, prim_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.prim_panel_name %s\n", + msm_fb_pdata.prim_panel_name); + + if (!strncmp((char *)msm_fb_pdata.prim_panel_name, + HDMI_PANEL_NAME, strnlen(HDMI_PANEL_NAME, + PANEL_NAME_MAX_LEN))) { + pr_debug("HDMI is the primary display by" + " boot parameter\n"); + hdmi_is_primary = 1; + set_mdp_clocks_for_wuxga(); + } + } + if (strnlen(ext_panel, PANEL_NAME_MAX_LEN)) { + strlcpy(msm_fb_pdata.ext_panel_name, ext_panel, + PANEL_NAME_MAX_LEN); + pr_debug("msm_fb_pdata.ext_panel_name %s\n", + msm_fb_pdata.ext_panel_name); + } +} + +#if defined(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC) || \ + defined(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC_MODULE) +/*virtual key support */ +static ssize_t tma300_vkeys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":60:900:90:120" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":180:900:90:120" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":300:900:90:120" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":420:900:90:120" + "\n"); +} + +static struct kobj_attribute tma300_vkeys_attr = { + .attr = { + .mode = S_IRUGO, + }, + .show = &tma300_vkeys_show, +}; + +static struct attribute *tma300_properties_attrs[] = { + &tma300_vkeys_attr.attr, + NULL +}; + +static struct attribute_group tma300_properties_attr_group = { + .attrs = tma300_properties_attrs, +}; + +static struct kobject *properties_kobj; + + + +#define CYTTSP_TS_GPIO_IRQ 61 +static int cyttsp_platform_init(struct i2c_client *client) +{ + int rc = -EINVAL; + struct regulator *pm8058_l5 = NULL, *pm8058_s3; + + if (machine_is_msm8x60_fluid()) { + pm8058_l5 = regulator_get(NULL, "8058_l5"); + if (IS_ERR(pm8058_l5)) { + pr_err("%s: regulator get of 8058_l5 failed (%ld)\n", + __func__, PTR_ERR(pm8058_l5)); + rc = PTR_ERR(pm8058_l5); + return rc; + } + rc = regulator_set_voltage(pm8058_l5, 2850000, 2850000); + if (rc) { + pr_err("%s: regulator_set_voltage of 8058_l5 failed(%d)\n", + __func__, rc); + goto reg_l5_put; + } + + rc = regulator_enable(pm8058_l5); + if (rc) { + pr_err("%s: regulator_enable of 8058_l5 failed(%d)\n", + __func__, rc); + goto reg_l5_put; + } + } + /* vote for s3 to enable i2c communication lines */ + pm8058_s3 = regulator_get(NULL, "8058_s3"); + if (IS_ERR(pm8058_s3)) { + pr_err("%s: regulator get of 8058_s3 failed (%ld)\n", + __func__, PTR_ERR(pm8058_s3)); + rc = PTR_ERR(pm8058_s3); + goto reg_l5_disable; + } + + rc = regulator_set_voltage(pm8058_s3, 1800000, 1800000); + if (rc) { + pr_err("%s: regulator_set_voltage() = %d\n", + __func__, rc); + goto reg_s3_put; + } + + rc = regulator_enable(pm8058_s3); + if (rc) { + pr_err("%s: regulator_enable of 8058_l5 failed(%d)\n", + __func__, rc); + goto reg_s3_put; + } + + /* wait for vregs to stabilize */ + usleep_range(10000, 10000); + + /* check this device active by reading first byte/register */ + rc = i2c_smbus_read_byte_data(client, 0x01); + if (rc < 0) { + pr_err("%s: i2c sanity check failed\n", __func__); + goto reg_s3_disable; + } + + /* virtual keys */ + if (machine_is_msm8x60_fluid()) { + properties_kobj = kobject_create_and_add("board_properties", + NULL); + if (properties_kobj); + if (!properties_kobj || rc) + pr_err("%s: failed to create board_properties\n", + __func__); + } + return CY_OK; + +reg_s3_disable: + regulator_disable(pm8058_s3); +reg_s3_put: + regulator_put(pm8058_s3); +reg_l5_disable: + if (machine_is_msm8x60_fluid()) + regulator_disable(pm8058_l5); +reg_l5_put: + if (machine_is_msm8x60_fluid()) + regulator_put(pm8058_l5); + return rc; +} + +/* TODO: Put the regulator to LPM / HPM in suspend/resume*/ +static int cyttsp_platform_suspend(struct i2c_client *client) +{ + msleep(20); + + return CY_OK; +} + +static int cyttsp_platform_resume(struct i2c_client *client) +{ + /* add any special code to strobe a wakeup pin or chip reset */ + msleep(10); + + return CY_OK; +} + +static struct cyttsp_platform_data cyttsp_fluid_pdata = { + .flags = 0x04, + .gen = CY_GEN3, /* or */ + .use_st = CY_USE_ST, + .use_mt = CY_USE_MT, + .use_hndshk = CY_SEND_HNDSHK, + .use_trk_id = CY_USE_TRACKING_ID, + .use_sleep = CY_USE_DEEP_SLEEP_SEL | CY_USE_LOW_POWER_SEL, + .use_gestures = CY_USE_GESTURES, + /* activate up to 4 groups + * and set active distance + */ + .gest_set = CY_GEST_GRP1 | CY_GEST_GRP2 | + CY_GEST_GRP3 | CY_GEST_GRP4 | + CY_ACT_DIST, + /* change act_intrvl to customize the Active power state + * scanning/processing refresh interval for Operating mode + */ + .act_intrvl = CY_ACT_INTRVL_DFLT, + /* change tch_tmout to customize the touch timeout for the + * Active power state for Operating mode + */ + .tch_tmout = CY_TCH_TMOUT_DFLT, + /* change lp_intrvl to customize the Low Power power state + * scanning/processing refresh interval for Operating mode + */ + .lp_intrvl = CY_LP_INTRVL_DFLT, + .sleep_gpio = -1, + .resout_gpio = -1, + .irq_gpio = CYTTSP_TS_GPIO_IRQ, + .resume = cyttsp_platform_resume, + .suspend = cyttsp_platform_suspend, + .init = cyttsp_platform_init, +}; + +static struct cyttsp_platform_data cyttsp_tmg240_pdata = { + .panel_maxx = 1083, + .panel_maxy = 659, + .disp_minx = 30, + .disp_maxx = 1053, + .disp_miny = 30, + .disp_maxy = 629, + .correct_fw_ver = 8, + .fw_fname = "cyttsp_8660_ffa.hex", + .flags = 0x00, + .gen = CY_GEN2, /* or */ + .use_st = CY_USE_ST, + .use_mt = CY_USE_MT, + .use_hndshk = CY_SEND_HNDSHK, + .use_trk_id = CY_USE_TRACKING_ID, + .use_sleep = CY_USE_DEEP_SLEEP_SEL | CY_USE_LOW_POWER_SEL, + .use_gestures = CY_USE_GESTURES, + /* activate up to 4 groups + * and set active distance + */ + .gest_set = CY_GEST_GRP1 | CY_GEST_GRP2 | + CY_GEST_GRP3 | CY_GEST_GRP4 | + CY_ACT_DIST, + /* change act_intrvl to customize the Active power state + * scanning/processing refresh interval for Operating mode + */ + .act_intrvl = CY_ACT_INTRVL_DFLT, + /* change tch_tmout to customize the touch timeout for the + * Active power state for Operating mode + */ + .tch_tmout = CY_TCH_TMOUT_DFLT, + /* change lp_intrvl to customize the Low Power power state + * scanning/processing refresh interval for Operating mode + */ + .lp_intrvl = CY_LP_INTRVL_DFLT, + .sleep_gpio = -1, + .resout_gpio = -1, + .irq_gpio = CYTTSP_TS_GPIO_IRQ, + .resume = cyttsp_platform_resume, + .suspend = cyttsp_platform_suspend, + .init = cyttsp_platform_init, + .disable_ghost_det = true, +}; +static void cyttsp_set_params(void) +{ + if (SOCINFO_VERSION_MAJOR(socinfo_get_platform_version()) < 3) { + cyttsp_fluid_pdata.fw_fname = "cyttsp_8660_fluid_p2.hex"; + cyttsp_fluid_pdata.panel_maxx = 539; + cyttsp_fluid_pdata.panel_maxy = 994; + cyttsp_fluid_pdata.disp_minx = 30; + cyttsp_fluid_pdata.disp_maxx = 509; + cyttsp_fluid_pdata.disp_miny = 60; + cyttsp_fluid_pdata.disp_maxy = 859; + cyttsp_fluid_pdata.correct_fw_ver = 4; + } else { + cyttsp_fluid_pdata.fw_fname = "cyttsp_8660_fluid_p3.hex"; + cyttsp_fluid_pdata.panel_maxx = 550; + cyttsp_fluid_pdata.panel_maxy = 1013; + cyttsp_fluid_pdata.disp_minx = 35; + cyttsp_fluid_pdata.disp_maxx = 515; + cyttsp_fluid_pdata.disp_miny = 69; + cyttsp_fluid_pdata.disp_maxy = 869; + cyttsp_fluid_pdata.correct_fw_ver = 5; + } + +} + +static struct i2c_board_info cyttsp_fluid_info[] __initdata = { + { + I2C_BOARD_INFO(CY_I2C_NAME, 0x24), + .platform_data = &cyttsp_fluid_pdata, +#ifndef CY_USE_TIMER + .irq = MSM_GPIO_TO_INT(CYTTSP_TS_GPIO_IRQ), +#endif /* CY_USE_TIMER */ + }, +}; + +static struct i2c_board_info cyttsp_ffa_info[] __initdata = { + { + I2C_BOARD_INFO(CY_I2C_NAME, 0x3b), + .platform_data = &cyttsp_tmg240_pdata, +#ifndef CY_USE_TIMER + .irq = MSM_GPIO_TO_INT(CYTTSP_TS_GPIO_IRQ), +#endif /* CY_USE_TIMER */ + }, +}; +#endif + +static struct regulator *vreg_tmg200; + +#define TS_PEN_IRQ_GPIO 61 +static int tmg200_power(int vreg_on) +{ + int rc = -EINVAL; + + if (!vreg_tmg200) { + printk(KERN_ERR "%s: regulator 8058_s3 not found (%d)\n", + __func__, rc); + return rc; + } + + rc = vreg_on ? regulator_enable(vreg_tmg200) : + regulator_disable(vreg_tmg200); + if (rc < 0) + printk(KERN_ERR "%s: vreg 8058_s3 %s failed (%d)\n", + __func__, vreg_on ? "enable" : "disable", rc); + + /* wait for vregs to stabilize */ + msleep(20); + + return rc; +} + +static int tmg200_dev_setup(bool enable) +{ + int rc; + + if (enable) { + vreg_tmg200 = regulator_get(NULL, "8058_s3"); + if (IS_ERR(vreg_tmg200)) { + pr_err("%s: regulator get of 8058_s3 failed (%ld)\n", + __func__, PTR_ERR(vreg_tmg200)); + rc = PTR_ERR(vreg_tmg200); + return rc; + } + + rc = regulator_set_voltage(vreg_tmg200, 1800000, 1800000); + if (rc) { + pr_err("%s: regulator_set_voltage() = %d\n", + __func__, rc); + goto reg_put; + } + } else { + /* put voltage sources */ + regulator_put(vreg_tmg200); + } + return 0; +reg_put: + regulator_put(vreg_tmg200); + return rc; +} + +static struct cy8c_ts_platform_data cy8ctmg200_pdata = { + .ts_name = "msm_tmg200_ts", + .dis_min_x = 0, + .dis_max_x = 1023, + .dis_min_y = 0, + .dis_max_y = 599, + .min_tid = 0, + .max_tid = 255, + .min_touch = 0, + .max_touch = 255, + .min_width = 0, + .max_width = 255, + .power_on = tmg200_power, + .dev_setup = tmg200_dev_setup, + .nfingers = 2, + .irq_gpio = TS_PEN_IRQ_GPIO, + .resout_gpio = GPIO_CAP_TS_RESOUT_N, +}; + +static struct i2c_board_info cy8ctmg200_board_info[] = { + { + I2C_BOARD_INFO("cy8ctmg200", 0x2), + .platform_data = &cy8ctmg200_pdata, + } +}; + +static struct regulator *vreg_tma340; + +static int tma340_power(int vreg_on) +{ + int rc = -EINVAL; + + if (!vreg_tma340) { + pr_err("%s: regulator 8901_l2 not found (%d)\n", + __func__, rc); + return rc; + } + + rc = vreg_on ? regulator_enable(vreg_tma340) : + regulator_disable(vreg_tma340); + if (rc < 0) + pr_err("%s: vreg 8901_l2 %s failed (%d)\n", + __func__, vreg_on ? "enable" : "disable", rc); + + /* wait for vregs to stabilize */ + msleep(100); + + return rc; +} + +static struct kobject *tma340_prop_kobj; + +static int tma340_dragon_dev_setup(bool enable) +{ + int rc; + + if (enable) { + vreg_tma340 = regulator_get(NULL, "8901_l2"); + if (IS_ERR(vreg_tma340)) { + pr_err("%s: regulator get of 8901_l2 failed (%ld)\n", + __func__, PTR_ERR(vreg_tma340)); + rc = PTR_ERR(vreg_tma340); + return rc; + } + + rc = regulator_set_voltage(vreg_tma340, 3300000, 3300000); + if (rc) { + pr_err("%s: regulator_set_voltage() = %d\n", + __func__, rc); + goto reg_put; + } + tma340_prop_kobj = kobject_create_and_add("board_properties", + NULL); + if (tma340_prop_kobj) { + ; + if (rc) { + kobject_put(tma340_prop_kobj); + pr_err("%s: failed to create board_properties\n", + __func__); + goto reg_put; + } + } + + } else { + /* put voltage sources */ + regulator_put(vreg_tma340); + /* destroy virtual keys */ + if (tma340_prop_kobj) { + kobject_put(tma340_prop_kobj); + } + } + return 0; +reg_put: + regulator_put(vreg_tma340); + return rc; +} + + +static struct cy8c_ts_platform_data cy8ctma340_dragon_pdata = { + .ts_name = "cy8ctma340", + .dis_min_x = 0, + .dis_max_x = 479, + .dis_min_y = 0, + .dis_max_y = 799, + .min_tid = 0, + .max_tid = 255, + .min_touch = 0, + .max_touch = 255, + .min_width = 0, + .max_width = 255, + .power_on = tma340_power, + .dev_setup = tma340_dragon_dev_setup, + .nfingers = 2, + .irq_gpio = TS_PEN_IRQ_GPIO, + .resout_gpio = -1, +}; + +static struct i2c_board_info cy8ctma340_dragon_board_info[] = { + { + I2C_BOARD_INFO("cy8ctma340", 0x24), + .platform_data = &cy8ctma340_dragon_pdata, + } +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static int configure_uart_gpios(int on) +{ + int ret = 0, i; + int uart_gpios[] = {53, 54, 55, 56}; + for (i = 0; i < ARRAY_SIZE(uart_gpios); i++) { + if (on) { + ret = msm_gpiomux_get(uart_gpios[i]); + if (unlikely(ret)) + break; + } else { + ret = msm_gpiomux_put(uart_gpios[i]); + if (unlikely(ret)) + return ret; + } + } + if (ret) + for (; i >= 0; i--) + msm_gpiomux_put(uart_gpios[i]); + return ret; +} +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, + .gpio_config = configure_uart_gpios, +}; +#endif + + +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) + +static struct gpio_led gpio_exp_leds_config[] = { + { + .name = "left_led1:green", + .gpio = GPIO_LEFT_LED_1, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "left_led2:red", + .gpio = GPIO_LEFT_LED_2, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "left_led3:green", + .gpio = GPIO_LEFT_LED_3, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "wlan_led:orange", + .gpio = GPIO_LEFT_LED_WLAN, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "left_led5:green", + .gpio = GPIO_LEFT_LED_5, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "right_led1:green", + .gpio = GPIO_RIGHT_LED_1, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "right_led2:red", + .gpio = GPIO_RIGHT_LED_2, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "right_led3:green", + .gpio = GPIO_RIGHT_LED_3, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "bt_led:blue", + .gpio = GPIO_RIGHT_LED_BT, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + { + .name = "right_led5:green", + .gpio = GPIO_RIGHT_LED_5, + .active_low = 1, + .retain_state_suspended = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, +}; + +static struct gpio_led_platform_data gpio_leds_pdata = { + .num_leds = ARRAY_SIZE(gpio_exp_leds_config), + .leds = gpio_exp_leds_config, +}; + +static struct platform_device gpio_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_leds_pdata, + }, +}; + +static struct gpio_led fluid_gpio_leds[] = { + { + .name = "dual_led:green", + .gpio = GPIO_LED1_GREEN_N, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + .active_low = 1, + .retain_state_suspended = 0, + }, + { + .name = "dual_led:red", + .gpio = GPIO_LED2_RED_N, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + .active_low = 1, + .retain_state_suspended = 0, + }, +}; + +static struct gpio_led_platform_data gpio_led_pdata = { + .leds = fluid_gpio_leds, + .num_leds = ARRAY_SIZE(fluid_gpio_leds), +}; + +static struct platform_device fluid_leds_gpio = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_led_pdata, + }, +}; + +#endif + +#ifdef CONFIG_BATTERY_MSM8X60 +static struct msm_charger_platform_data msm_charger_data = { + .safety_time = 180, + .update_time = 1, + .max_voltage = 4200, + .min_voltage = 3200, +}; + +static struct platform_device msm_charger_device = { + .name = "msm-charger", + .id = -1, + .dev = { + .platform_data = &msm_charger_data, + } +}; +#endif + +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +static struct regulator_consumer_supply vreg_consumers_PM8058_L0[] = { + REGULATOR_SUPPLY("8058_l0", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L1[] = { + REGULATOR_SUPPLY("8058_l1", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L2[] = { + REGULATOR_SUPPLY("8058_l2", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L3[] = { + REGULATOR_SUPPLY("8058_l3", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L4[] = { + REGULATOR_SUPPLY("8058_l4", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L5[] = { + REGULATOR_SUPPLY("8058_l5", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L6[] = { + REGULATOR_SUPPLY("8058_l6", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L7[] = { + REGULATOR_SUPPLY("8058_l7", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L8[] = { + REGULATOR_SUPPLY("8058_l8", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L9[] = { + REGULATOR_SUPPLY("8058_l9", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L10[] = { + REGULATOR_SUPPLY("8058_l10", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L11[] = { + REGULATOR_SUPPLY("8058_l11", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L12[] = { + REGULATOR_SUPPLY("8058_l12", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L13[] = { + REGULATOR_SUPPLY("8058_l13", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L14[] = { + REGULATOR_SUPPLY("8058_l14", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L15[] = { + REGULATOR_SUPPLY("8058_l15", NULL), + REGULATOR_SUPPLY("cam_vana", "1-001a"), + REGULATOR_SUPPLY("cam_vana", "1-006c"), + REGULATOR_SUPPLY("cam_vana", "1-0078"), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L16[] = { + REGULATOR_SUPPLY("8058_l16", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L17[] = { + REGULATOR_SUPPLY("8058_l17", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L18[] = { + REGULATOR_SUPPLY("8058_l18", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L19[] = { + REGULATOR_SUPPLY("8058_l19", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L20[] = { + REGULATOR_SUPPLY("8058_l20", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L21[] = { + REGULATOR_SUPPLY("8058_l21", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L22[] = { + REGULATOR_SUPPLY("8058_l22", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L23[] = { + REGULATOR_SUPPLY("8058_l23", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L24[] = { + REGULATOR_SUPPLY("8058_l24", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L25[] = { + REGULATOR_SUPPLY("8058_l25", NULL), + REGULATOR_SUPPLY("cam_vdig", "1-001a"), + REGULATOR_SUPPLY("cam_vdig", "1-006c"), + REGULATOR_SUPPLY("cam_vdig", "1-0078"), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_S0[] = { + REGULATOR_SUPPLY("8058_s0", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_S1[] = { + REGULATOR_SUPPLY("8058_s1", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_S2[] = { + REGULATOR_SUPPLY("8058_s2", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_S3[] = { + REGULATOR_SUPPLY("8058_s3", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_S4[] = { + REGULATOR_SUPPLY("8058_s4", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_LVS0[] = { + REGULATOR_SUPPLY("8058_lvs0", NULL), + REGULATOR_SUPPLY("cam_vio", "1-001a"), + REGULATOR_SUPPLY("cam_vio", "1-006c"), + REGULATOR_SUPPLY("cam_vio", "1-0078"), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_LVS1[] = { + REGULATOR_SUPPLY("8058_lvs1", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_NCP[] = { + REGULATOR_SUPPLY("8058_ncp", NULL), +}; + +static struct regulator_consumer_supply vreg_consumers_PM8901_L0[] = { + REGULATOR_SUPPLY("8901_l0", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L1[] = { + REGULATOR_SUPPLY("8901_l1", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L2[] = { + REGULATOR_SUPPLY("8901_l2", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L3[] = { + REGULATOR_SUPPLY("8901_l3", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L4[] = { + REGULATOR_SUPPLY("8901_l4", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L5[] = { + REGULATOR_SUPPLY("8901_l5", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L6[] = { + REGULATOR_SUPPLY("8901_l6", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_S2[] = { + REGULATOR_SUPPLY("8901_s2", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_S3[] = { + REGULATOR_SUPPLY("8901_s3", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_S4[] = { + REGULATOR_SUPPLY("8901_s4", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_LVS0[] = { + REGULATOR_SUPPLY("8901_lvs0", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_LVS1[] = { + REGULATOR_SUPPLY("8901_lvs1", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_LVS2[] = { + REGULATOR_SUPPLY("8901_lvs2", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_LVS3[] = { + REGULATOR_SUPPLY("8901_lvs3", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_MVS0[] = { + REGULATOR_SUPPLY("8901_mvs0", NULL), +}; + +/* Pin control regulators */ +static struct regulator_consumer_supply vreg_consumers_PM8058_L8_PC[] = { + REGULATOR_SUPPLY("8058_l8_pc", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L20_PC[] = { + REGULATOR_SUPPLY("8058_l20_pc", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_L21_PC[] = { + REGULATOR_SUPPLY("8058_l21_pc", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8058_S2_PC[] = { + REGULATOR_SUPPLY("8058_s2_pc", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_L0_PC[] = { + REGULATOR_SUPPLY("8901_l0_pc", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_PM8901_S4_PC[] = { + REGULATOR_SUPPLY("8901_s4_pc", NULL), +}; + +#define RPM_VREG_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, \ + _default_uV, _peak_uA, _avg_uA, _pull_down, _pin_ctrl, \ + _freq, _pin_fn, _force_mode, _sleep_set_force_mode, \ + _state, _sleep_selectable, _always_on) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _min_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .consumer_supplies = vreg_consumers_##_id, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + }, \ + .id = RPM_VREG_ID_##_id, \ + .default_uV = _default_uV, \ + .peak_uA = _peak_uA, \ + .avg_uA = _avg_uA, \ + .pull_down_enable = _pull_down, \ + .pin_ctrl = _pin_ctrl, \ + .freq = RPM_VREG_FREQ_##_freq, \ + .pin_fn = _pin_fn, \ + .force_mode = _force_mode, \ + .sleep_set_force_mode = _sleep_set_force_mode, \ + .state = _state, \ + .sleep_selectable = _sleep_selectable, \ + } + +/* Pin control initialization */ +#define RPM_PC(_id, _always_on, _pin_fn, _pin_ctrl) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + .always_on = _always_on, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id##_PC), \ + .consumer_supplies = vreg_consumers_##_id##_PC, \ + }, \ + .id = RPM_VREG_ID_##_id##_PC, \ + .pin_fn = RPM_VREG_PIN_FN_8660_##_pin_fn, \ + .pin_ctrl = _pin_ctrl, \ + } + +/* + * The default LPM/HPM state of an RPM controlled regulator can be controlled + * via the peak_uA value specified in the table below. If the value is less + * than the high power min threshold for the regulator, then the regulator will + * be set to LPM. Otherwise, it will be set to HPM. + * + * This value can be further overridden by specifying an initial mode via + * .init_data.constraints.initial_mode. + */ + +#define RPM_LDO(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _init_peak_uA) \ + RPM_VREG_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_FAST | \ + REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE | \ + REGULATOR_MODE_STANDBY, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _min_uV, _init_peak_uA, \ + _init_peak_uA, _pd, RPM_VREG_PIN_CTRL_NONE, NONE, \ + RPM_VREG_PIN_FN_8660_ENABLE, \ + RPM_VREG_FORCE_MODE_8660_NONE, \ + RPM_VREG_FORCE_MODE_8660_NONE, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on) + +#define RPM_SMPS(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV, \ + _init_peak_uA, _freq) \ + RPM_VREG_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_FAST | \ + REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE | \ + REGULATOR_MODE_STANDBY, REGULATOR_CHANGE_VOLTAGE | \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE | \ + REGULATOR_CHANGE_DRMS, 0, _min_uV, _init_peak_uA, \ + _init_peak_uA, _pd, RPM_VREG_PIN_CTRL_NONE, _freq, \ + RPM_VREG_PIN_FN_8660_ENABLE, \ + RPM_VREG_FORCE_MODE_8660_NONE, \ + RPM_VREG_FORCE_MODE_8660_NONE, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on) + +#define RPM_VS(_id, _always_on, _pd, _sleep_selectable) \ + RPM_VREG_INIT(_id, 0, 0, REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE, \ + REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE, 0, 0, \ + 1000, 1000, _pd, RPM_VREG_PIN_CTRL_NONE, NONE, \ + RPM_VREG_PIN_FN_8660_ENABLE, \ + RPM_VREG_FORCE_MODE_8660_NONE, \ + RPM_VREG_FORCE_MODE_8660_NONE, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on) + +#define RPM_NCP(_id, _always_on, _pd, _sleep_selectable, _min_uV, _max_uV) \ + RPM_VREG_INIT(_id, _min_uV, _max_uV, REGULATOR_MODE_NORMAL, \ + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, 0, \ + _min_uV, 1000, 1000, _pd, RPM_VREG_PIN_CTRL_NONE, NONE, \ + RPM_VREG_PIN_FN_8660_ENABLE, \ + RPM_VREG_FORCE_MODE_8660_NONE, \ + RPM_VREG_FORCE_MODE_8660_NONE, RPM_VREG_STATE_OFF, \ + _sleep_selectable, _always_on) + +#define LDO50HMIN RPM_VREG_8660_LDO_50_HPM_MIN_LOAD +#define LDO150HMIN RPM_VREG_8660_LDO_150_HPM_MIN_LOAD +#define LDO300HMIN RPM_VREG_8660_LDO_300_HPM_MIN_LOAD +#define SMPS_HMIN RPM_VREG_8660_SMPS_HPM_MIN_LOAD +#define FTS_HMIN RPM_VREG_8660_FTSMPS_HPM_MIN_LOAD + +/* RPM early regulator constraints */ +static struct rpm_regulator_init_data rpm_regulator_early_init_data[] = { + /* ID a_on pd ss min_uV max_uV init_ip freq */ + RPM_SMPS(PM8058_S0, 0, 1, 1, 500000, 1325000, SMPS_HMIN, 1p60), + RPM_SMPS(PM8058_S1, 0, 1, 1, 500000, 1250000, SMPS_HMIN, 1p60), +}; + +/* RPM regulator constraints */ +static struct rpm_regulator_init_data rpm_regulator_init_data[] = { + /* ID a_on pd ss min_uV max_uV init_ip */ + RPM_LDO(PM8058_L0, 0, 1, 0, 1200000, 1200000, LDO150HMIN), + RPM_LDO(PM8058_L1, 0, 1, 0, 1200000, 1200000, LDO300HMIN), + RPM_LDO(PM8058_L2, 0, 1, 0, 1800000, 2600000, LDO300HMIN), + RPM_LDO(PM8058_L3, 0, 1, 0, 1800000, 1800000, LDO150HMIN), + RPM_LDO(PM8058_L4, 0, 1, 0, 2850000, 2850000, LDO50HMIN), + RPM_LDO(PM8058_L5, 0, 1, 0, 2850000, 2850000, LDO300HMIN), + RPM_LDO(PM8058_L6, 0, 1, 0, 3000000, 3600000, LDO50HMIN), + RPM_LDO(PM8058_L7, 0, 1, 0, 1800000, 1800000, LDO50HMIN), + RPM_LDO(PM8058_L8, 0, 1, 0, 2900000, 3050000, LDO300HMIN), + RPM_LDO(PM8058_L9, 0, 1, 0, 1800000, 1800000, LDO300HMIN), + RPM_LDO(PM8058_L10, 0, 1, 0, 2600000, 2600000, LDO300HMIN), + RPM_LDO(PM8058_L11, 0, 1, 0, 1500000, 1500000, LDO150HMIN), + RPM_LDO(PM8058_L12, 0, 1, 0, 2900000, 2900000, LDO150HMIN), + RPM_LDO(PM8058_L13, 0, 1, 0, 2050000, 2050000, LDO300HMIN), + RPM_LDO(PM8058_L14, 0, 0, 0, 2850000, 2850000, LDO300HMIN), + RPM_LDO(PM8058_L15, 0, 1, 0, 2850000, 2850000, LDO300HMIN), + RPM_LDO(PM8058_L16, 1, 1, 0, 1800000, 1800000, LDO300HMIN), + RPM_LDO(PM8058_L17, 0, 1, 0, 2600000, 2600000, LDO150HMIN), + RPM_LDO(PM8058_L18, 0, 1, 0, 2200000, 2200000, LDO150HMIN), + RPM_LDO(PM8058_L19, 0, 1, 0, 2500000, 2500000, LDO150HMIN), + RPM_LDO(PM8058_L20, 0, 1, 0, 1800000, 1800000, LDO150HMIN), + RPM_LDO(PM8058_L21, 1, 1, 0, 1200000, 1200000, LDO150HMIN), + RPM_LDO(PM8058_L22, 0, 1, 0, 1150000, 1150000, LDO300HMIN), + RPM_LDO(PM8058_L23, 0, 1, 0, 1200000, 1200000, LDO300HMIN), + RPM_LDO(PM8058_L24, 0, 1, 0, 1200000, 1200000, LDO150HMIN), + RPM_LDO(PM8058_L25, 0, 1, 0, 1200000, 1200000, LDO150HMIN), + + /* ID a_on pd ss min_uV max_uV init_ip freq */ + RPM_SMPS(PM8058_S2, 0, 1, 1, 1200000, 1400000, SMPS_HMIN, 1p60), + RPM_SMPS(PM8058_S3, 1, 1, 0, 1800000, 1800000, SMPS_HMIN, 1p60), + RPM_SMPS(PM8058_S4, 1, 1, 0, 2200000, 2200000, SMPS_HMIN, 1p60), + + /* ID a_on pd ss */ + RPM_VS(PM8058_LVS0, 0, 1, 0), + RPM_VS(PM8058_LVS1, 0, 1, 0), + + /* ID a_on pd ss min_uV max_uV */ + RPM_NCP(PM8058_NCP, 0, 1, 0, 1800000, 1800000), + + /* ID a_on pd ss min_uV max_uV init_ip */ + RPM_LDO(PM8901_L0, 0, 1, 0, 1200000, 1200000, LDO300HMIN), + RPM_LDO(PM8901_L1, 0, 1, 0, 3300000, 3300000, LDO300HMIN), + RPM_LDO(PM8901_L2, 0, 1, 0, 2850000, 3300000, LDO300HMIN), + RPM_LDO(PM8901_L3, 0, 1, 0, 3300000, 3300000, LDO300HMIN), + RPM_LDO(PM8901_L4, 0, 1, 0, 2600000, 2600000, LDO300HMIN), + RPM_LDO(PM8901_L5, 0, 1, 0, 2850000, 2850000, LDO300HMIN), + RPM_LDO(PM8901_L6, 0, 1, 0, 2200000, 2200000, LDO300HMIN), + + /* ID a_on pd ss min_uV max_uV init_ip freq */ + RPM_SMPS(PM8901_S2, 0, 1, 0, 1300000, 1300000, FTS_HMIN, 1p60), + RPM_SMPS(PM8901_S3, 0, 1, 0, 1100000, 1100000, FTS_HMIN, 1p60), + RPM_SMPS(PM8901_S4, 0, 1, 0, 1225000, 1225000, FTS_HMIN, 1p60), + + /* ID a_on pd ss */ + RPM_VS(PM8901_LVS0, 1, 1, 0), + RPM_VS(PM8901_LVS1, 0, 1, 0), + RPM_VS(PM8901_LVS2, 0, 1, 0), + RPM_VS(PM8901_LVS3, 0, 1, 0), + RPM_VS(PM8901_MVS0, 0, 1, 0), + + /* ID a_on pin_func pin_ctrl */ + RPM_PC(PM8058_L8, 0, SLEEP_B, RPM_VREG_PIN_CTRL_NONE), + RPM_PC(PM8058_L20, 0, SLEEP_B, RPM_VREG_PIN_CTRL_NONE), + RPM_PC(PM8058_L21, 1, SLEEP_B, RPM_VREG_PIN_CTRL_NONE), + RPM_PC(PM8058_S2, 0, ENABLE, RPM_VREG_PIN_CTRL_PM8058_A0), + RPM_PC(PM8901_L0, 0, ENABLE, RPM_VREG_PIN_CTRL_PM8901_A0), + RPM_PC(PM8901_S4, 0, ENABLE, RPM_VREG_PIN_CTRL_PM8901_A0), +}; + +static struct rpm_regulator_platform_data rpm_regulator_early_pdata = { + .init_data = rpm_regulator_early_init_data, + .num_regulators = ARRAY_SIZE(rpm_regulator_early_init_data), + .version = RPM_VREG_VERSION_8660, + .vreg_id_vdd_mem = RPM_VREG_ID_PM8058_S0, + .vreg_id_vdd_dig = RPM_VREG_ID_PM8058_S1, +}; + +static struct rpm_regulator_platform_data rpm_regulator_pdata = { + .init_data = rpm_regulator_init_data, + .num_regulators = ARRAY_SIZE(rpm_regulator_init_data), + .version = RPM_VREG_VERSION_8660, +}; + +static struct platform_device rpm_regulator_early_device = { + .name = "rpm-regulator", + .id = 0, + .dev = { + .platform_data = &rpm_regulator_early_pdata, + }, +}; + +static struct platform_device rpm_regulator_device = { + .name = "rpm-regulator", + .id = 1, + .dev = { + .platform_data = &rpm_regulator_pdata, + }, +}; + +static struct platform_device *early_regulators[] __initdata = { + &msm_device_saw_s0, + &msm_device_saw_s1, + &rpm_regulator_early_device, +}; + +static struct platform_device *early_devices[] __initdata = { +#ifdef CONFIG_MSM_BUS_SCALING + &msm_bus_apps_fabric, + &msm_bus_sys_fabric, + &msm_bus_mm_fabric, + &msm_bus_sys_fpb, + &msm_bus_cpss_fpb, +#endif + &msm_device_dmov_adm0, + &msm_device_dmov_adm1, +}; + +#if (defined(CONFIG_MARIMBA_CORE)) && \ + (defined(CONFIG_MSM_BT_POWER) || defined(CONFIG_MSM_BT_POWER_MODULE)) + +static int bluetooth_power(int); +static struct platform_device msm_bt_power_device = { + .name = "bt_power", + .id = -1, + .dev = { + .platform_data = &bluetooth_power, + }, +}; +#endif + +static struct platform_device msm_tsens_device = { + .name = "tsens-tm", + .id = -1, +}; + +static struct platform_device *rumi_sim_devices[] __initdata = { + &smc91x_device, + &msm_device_uart_dm12, +#ifdef CONFIG_I2C_QUP + &msm_gsbi3_qup_i2c_device, + &msm_gsbi4_qup_i2c_device, + &msm_gsbi7_qup_i2c_device, + &msm_gsbi8_qup_i2c_device, + &msm_gsbi9_qup_i2c_device, + &msm_gsbi12_qup_i2c_device, +#endif +#ifdef CONFIG_I2C_SSBI + &msm_device_ssbi3, +#endif +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_smipool_device, + &android_pmem_audio_device, +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif + &msm_fb_device, + &msm_kgsl_3d0, + &msm_kgsl_2d0, + &msm_kgsl_2d1, + &lcdc_samsung_panel_device, +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + &hdmi_msm_device, +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ +#ifdef CONFIG_MSM_CAMERA +#ifndef CONFIG_MSM_CAMERA_V4L2 +#ifdef CONFIG_MT9E013 + &msm_camera_sensor_mt9e013, +#endif +#ifdef CONFIG_IMX074 + &msm_camera_sensor_imx074, +#endif +#ifdef CONFIG_VX6953 + &msm_camera_sensor_vx6953, +#endif +#ifdef CONFIG_WEBCAM_OV7692 + &msm_camera_sensor_webcam_ov7692, +#endif +#ifdef CONFIG_WEBCAM_OV9726 + &msm_camera_sensor_webcam_ov9726, +#endif +#ifdef CONFIG_QS_S5K4E1 + &msm_camera_sensor_qs_s5k4e1, +#endif +#endif +#endif +#ifdef CONFIG_MSM_GEMINI + &msm_gemini_device, +#endif +#ifdef CONFIG_MSM_VPE +#ifndef CONFIG_MSM_CAMERA_V4L2 + &msm_vpe_device, +#endif +#endif + &msm_device_vidc, +}; + +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) +enum { + SX150X_CORE, + SX150X_DOCKING, + SX150X_SURF, + SX150X_LEFT_FHA, + SX150X_RIGHT_FHA, + SX150X_SOUTH, + SX150X_NORTH, + SX150X_CORE_FLUID, +}; + +static struct sx150x_platform_data sx150x_data[] __initdata = { + [SX150X_CORE] = { + .gpio_base = GPIO_CORE_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0c08, + .io_pulldn_ena = 0x4060, + .io_open_drain_ena = 0x000c, + .io_polarity = 0, + .irq_summary = -1, /* see fixup_i2c_configs() */ + .irq_base = GPIO_EXPANDER_IRQ_BASE, + }, + [SX150X_DOCKING] = { + .gpio_base = GPIO_DOCKING_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x5e06, + .io_pulldn_ena = 0x81b8, + .io_open_drain_ena = 0, + .io_polarity = 0, + .irq_summary = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, + UI_INT2_N), + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_DOCKING_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + }, + [SX150X_SURF] = { + .gpio_base = GPIO_SURF_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0, + .io_pulldn_ena = 0, + .io_open_drain_ena = 0, + .io_polarity = 0, + .irq_summary = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, + UI_INT1_N), + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_SURF_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + }, + [SX150X_LEFT_FHA] = { + .gpio_base = GPIO_LEFT_KB_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0, + .io_pulldn_ena = 0x40, + .io_open_drain_ena = 0, + .io_polarity = 0, + .irq_summary = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, + UI_INT3_N), + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_LEFT_KB_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + }, + [SX150X_RIGHT_FHA] = { + .gpio_base = GPIO_RIGHT_KB_EXPANDER_BASE, + .oscio_is_gpo = true, + .io_pullup_ena = 0, + .io_pulldn_ena = 0, + .io_open_drain_ena = 0, + .io_polarity = 0, + .irq_summary = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, + UI_INT3_N), + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_RIGHT_KB_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + }, + [SX150X_SOUTH] = { + .gpio_base = GPIO_SOUTH_EXPANDER_BASE, + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_SOUTH_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + .irq_summary = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, UI_INT3_N), + }, + [SX150X_NORTH] = { + .gpio_base = GPIO_NORTH_EXPANDER_BASE, + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_NORTH_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + .irq_summary = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, UI_INT3_N), + .oscio_is_gpo = true, + .io_open_drain_ena = 0x30, + }, + [SX150X_CORE_FLUID] = { + .gpio_base = GPIO_CORE_EXPANDER_BASE, + .oscio_is_gpo = false, + .io_pullup_ena = 0x0408, + .io_pulldn_ena = 0x4060, + .io_open_drain_ena = 0x0008, + .io_polarity = 0, + .irq_summary = -1, /* see fixup_i2c_configs() */ + .irq_base = GPIO_EXPANDER_IRQ_BASE, + }, +}; + +#ifdef CONFIG_SENSORS_MSM_ADC +/* Configuration of EPM expander is done when client + * request an adc read + */ +static struct sx150x_platform_data sx150x_epmdata = { + .gpio_base = GPIO_EPM_EXPANDER_BASE, + .irq_base = GPIO_EXPANDER_IRQ_BASE + + GPIO_EPM_EXPANDER_BASE - + GPIO_EXPANDER_GPIO_BASE, + .irq_summary = -1, +}; +#endif + +/* sx150x_low_power_cfg + * + * This data and init function are used to put unused gpio-expander output + * lines into their low-power states at boot. The init + * function must be deferred until a later init stage because the i2c + * gpio expander drivers do not probe until after they are registered + * (see register_i2c_devices) and the work-queues for those registrations + * are processed. Because these lines are unused, there is no risk of + * competing with a device driver for the gpio. + * + * gpio lines whose low-power states are input are naturally in their low- + * power configurations once probed, see the platform data structures above. + */ +struct sx150x_low_power_cfg { + unsigned gpio; + unsigned val; +}; + +static struct sx150x_low_power_cfg +common_sx150x_lp_cfgs[] __initdata = { + {GPIO_WLAN_DEEP_SLEEP_N, 0}, + {GPIO_EXT_GPS_LNA_EN, 0}, + {GPIO_MSM_WAKES_BT, 0}, + {GPIO_USB_UICC_EN, 0}, + {GPIO_BATT_GAUGE_EN, 0}, +}; + +static struct sx150x_low_power_cfg +surf_ffa_sx150x_lp_cfgs[] __initdata = { + {GPIO_MIPI_DSI_RST_N, 0}, + {GPIO_DONGLE_PWR_EN, 0}, + {GPIO_CAP_TS_SLEEP, 1}, + {GPIO_WEB_CAMIF_RESET_N, 0}, +}; + +static void __init +cfg_gpio_low_power(struct sx150x_low_power_cfg *cfgs, unsigned nelems) +{ + unsigned n; + int rc; + + for (n = 0; n < nelems; ++n) { + rc = gpio_request(cfgs[n].gpio, NULL); + if (!rc) { + rc = gpio_direction_output(cfgs[n].gpio, cfgs[n].val); + gpio_free(cfgs[n].gpio); + } + + if (rc) { + printk(KERN_NOTICE "%s: failed to sleep gpio %d: %d\n", + __func__, cfgs[n].gpio, rc); + } + } +} + +static int __init cfg_sx150xs_low_power(void) +{ + cfg_gpio_low_power(common_sx150x_lp_cfgs, + ARRAY_SIZE(common_sx150x_lp_cfgs)); + if (!machine_is_msm8x60_fluid()) + cfg_gpio_low_power(surf_ffa_sx150x_lp_cfgs, + ARRAY_SIZE(surf_ffa_sx150x_lp_cfgs)); + return 0; +} +module_init(cfg_sx150xs_low_power); + +#ifdef CONFIG_I2C +static struct i2c_board_info core_expander_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x3e), + .platform_data = &sx150x_data[SX150X_CORE] + }, +}; + +static struct i2c_board_info docking_expander_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x3f), + .platform_data = &sx150x_data[SX150X_DOCKING] + }, +}; + +static struct i2c_board_info surf_expanders_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x70), + .platform_data = &sx150x_data[SX150X_SURF] + } +}; + +static struct i2c_board_info fha_expanders_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1508q", 0x21), + .platform_data = &sx150x_data[SX150X_LEFT_FHA] + }, + { + I2C_BOARD_INFO("sx1508q", 0x22), + .platform_data = &sx150x_data[SX150X_RIGHT_FHA] + } +}; + +static struct i2c_board_info fluid_expanders_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1508q", 0x23), + .platform_data = &sx150x_data[SX150X_SOUTH] + }, + { + I2C_BOARD_INFO("sx1508q", 0x20), + .platform_data = &sx150x_data[SX150X_NORTH] + } +}; + +static struct i2c_board_info fluid_core_expander_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("sx1509q", 0x3e), + .platform_data = &sx150x_data[SX150X_CORE_FLUID] + }, +}; + +#ifdef CONFIG_SENSORS_MSM_ADC +static struct i2c_board_info fluid_expanders_i2c_epm_info[] = { + { + I2C_BOARD_INFO("sx1509q", 0x3e), + .platform_data = &sx150x_epmdata + }, +}; +#endif +#endif +#endif + +#ifdef CONFIG_SENSORS_MSM_ADC + +static struct adc_access_fn xoadc_fn = { + pm8058_xoadc_select_chan_and_start_conv, + pm8058_xoadc_read_adc_code, + pm8058_xoadc_get_properties, + pm8058_xoadc_slot_request, + pm8058_xoadc_restore_slot, + pm8058_xoadc_calibrate, +}; + +#if defined(CONFIG_I2C) && \ + (defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)) +static struct regulator *vreg_adc_epm1; + +static struct i2c_client *epm_expander_i2c_register_board(void) + +{ + struct i2c_adapter *i2c_adap; + struct i2c_client *client = NULL; + i2c_adap = i2c_get_adapter(0x0); + + if (i2c_adap == NULL) + printk(KERN_ERR "\nepm_expander_i2c_adapter is NULL\n"); + + if (i2c_adap != NULL) + client = i2c_new_device(i2c_adap, + &fluid_expanders_i2c_epm_info[0]); + return client; + +} + +static unsigned int msm_adc_gpio_configure_expander_enable(void) +{ + int rc = 0; + static struct i2c_client *epm_i2c_client; + + printk(KERN_DEBUG "Enter msm_adc_gpio_configure_expander_enable\n"); + + vreg_adc_epm1 = regulator_get(NULL, "8058_s3"); + + if (IS_ERR(vreg_adc_epm1)) { + printk(KERN_ERR "%s: Unable to get 8058_s3\n", __func__); + return 0; + } + + rc = regulator_set_voltage(vreg_adc_epm1, 1800000, 1800000); + if (rc) + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable: " + "regulator set voltage failed\n"); + + rc = regulator_enable(vreg_adc_epm1); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable: " + "Error while enabling regulator for epm s3 %d\n", rc); + return rc; + } + + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_enable: Start" + " setting the value of the EPM 3.3, 5v and lvlsft\n"); + + msleep(1000); + + rc = gpio_request(GPIO_EPM_5V_BOOST_EN, "boost_epm_5v"); + if (!rc) { + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_enable: " + "Configure 5v boost\n"); + gpio_direction_output(GPIO_EPM_5V_BOOST_EN, 1); + } else { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable: " + "Error for epm 5v boost en\n"); + goto exit_vreg_epm; + } + + msleep(500); + + rc = gpio_request(GPIO_EPM_3_3V_EN, "epm_3_3v"); + if (!rc) { + gpio_direction_output(GPIO_EPM_3_3V_EN, 1); + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_enable: " + "Configure epm 3.3v\n"); + } else { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable: " + "Error for gpio 3.3ven\n"); + goto exit_vreg_epm; + } + msleep(500); + + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_enable: " + "Trying to request EPM LVLSFT_EN\n"); + rc = gpio_request(GPIO_EPM_LVLSFT_EN, "lvsft_en"); + if (!rc) { + gpio_direction_output(GPIO_EPM_LVLSFT_EN, 1); + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_enable: " + "Configure the lvlsft\n"); + } else { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable: " + "Error for epm lvlsft_en\n"); + goto exit_vreg_epm; + } + + msleep(500); + + if (!epm_i2c_client) + epm_i2c_client = epm_expander_i2c_register_board(); + + rc = gpio_request(GPIO_PWR_MON_ENABLE, "pwr_mon_enable"); + if (!rc) + rc = gpio_direction_output(GPIO_PWR_MON_ENABLE, 1); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + ": GPIO PWR MON Enable issue\n"); + goto exit_vreg_epm; + } + + msleep(1000); + + rc = gpio_request(GPIO_ADC1_PWDN_N, "adc1_pwdn"); + if (!rc) { + rc = gpio_direction_output(GPIO_ADC1_PWDN_N, 1); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + ": ADC1_PWDN error direction out\n"); + goto exit_vreg_epm; + } + } + + msleep(100); + + rc = gpio_request(GPIO_ADC2_PWDN_N, "adc2_pwdn"); + if (!rc) { + rc = gpio_direction_output(GPIO_ADC2_PWDN_N, 1); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + ": ADC2_PWD error direction out\n"); + goto exit_vreg_epm; + } + } + + msleep(1000); + + rc = gpio_request(GPIO_PWR_MON_START, "pwr_mon_start"); + if (!rc) { + rc = gpio_direction_output(GPIO_PWR_MON_START, 0); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + "Gpio request problem %d\n", rc); + goto exit_vreg_epm; + } + } + + rc = gpio_request(GPIO_EPM_SPI_ADC1_CS_N, "spi_adc1_cs"); + if (!rc) { + rc = gpio_direction_output(GPIO_EPM_SPI_ADC1_CS_N, 0); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + ": EPM_SPI_ADC1_CS_N error\n"); + goto exit_vreg_epm; + } + } + + rc = gpio_request(GPIO_EPM_SPI_ADC2_CS_N, "spi_adc2_cs"); + if (!rc) { + rc = gpio_direction_output(GPIO_EPM_SPI_ADC2_CS_N, 0); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + ": EPM_SPI_ADC2_Cs_N error\n"); + goto exit_vreg_epm; + } + } + + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_enable: Set " + "the power monitor reset for epm\n"); + + rc = gpio_request(GPIO_PWR_MON_RESET_N, "pwr_mon_reset_n"); + if (!rc) { + gpio_direction_output(GPIO_PWR_MON_RESET_N, 0); + if (rc) { + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable" + ": Error in the power mon reset\n"); + goto exit_vreg_epm; + } + } + + msleep(1000); + + gpio_set_value_cansleep(GPIO_PWR_MON_RESET_N, 1); + + msleep(500); + + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC1_CS_N, 1); + + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC2_CS_N, 1); + + return rc; + +exit_vreg_epm: + regulator_disable(vreg_adc_epm1); + + printk(KERN_ERR "msm_adc_gpio_configure_expander_enable: Exit." + " rc = %d.\n", rc); + return rc; +}; + +static unsigned int msm_adc_gpio_configure_expander_disable(void) +{ + int rc = 0; + + gpio_set_value_cansleep(GPIO_PWR_MON_RESET_N, 0); + gpio_free(GPIO_PWR_MON_RESET_N); + + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC1_CS_N, 0); + gpio_free(GPIO_EPM_SPI_ADC1_CS_N); + + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC2_CS_N, 0); + gpio_free(GPIO_EPM_SPI_ADC2_CS_N); + + gpio_set_value_cansleep(GPIO_PWR_MON_START, 0); + gpio_free(GPIO_PWR_MON_START); + + gpio_direction_output(GPIO_ADC1_PWDN_N, 0); + gpio_free(GPIO_ADC1_PWDN_N); + + gpio_direction_output(GPIO_ADC2_PWDN_N, 0); + gpio_free(GPIO_ADC2_PWDN_N); + + gpio_set_value_cansleep(GPIO_PWR_MON_ENABLE, 0); + gpio_free(GPIO_PWR_MON_ENABLE); + + gpio_set_value_cansleep(GPIO_EPM_LVLSFT_EN, 0); + gpio_free(GPIO_EPM_LVLSFT_EN); + + gpio_set_value_cansleep(GPIO_EPM_5V_BOOST_EN, 0); + gpio_free(GPIO_EPM_5V_BOOST_EN); + + gpio_set_value_cansleep(GPIO_EPM_3_3V_EN, 0); + gpio_free(GPIO_EPM_3_3V_EN); + + rc = regulator_disable(vreg_adc_epm1); + if (rc) + printk(KERN_DEBUG "msm_adc_gpio_configure_expander_disable: " + "Error while enabling regulator for epm s3 %d\n", rc); + regulator_put(vreg_adc_epm1); + + printk(KERN_DEBUG "Exi msm_adc_gpio_configure_expander_disable\n"); + return rc; +}; + +unsigned int msm_adc_gpio_expander_enable(int cs_enable) +{ + int rc = 0; + + printk(KERN_DEBUG "msm_adc_gpio_expander_enable: cs_enable = %d", + cs_enable); + + if (cs_enable < 16) { + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC1_CS_N, 0); + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC2_CS_N, 1); + } else { + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC2_CS_N, 0); + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC1_CS_N, 1); + } + return rc; +}; + +unsigned int msm_adc_gpio_expander_disable(int cs_disable) +{ + int rc = 0; + + printk(KERN_DEBUG "Enter msm_adc_gpio_expander_disable.\n"); + + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC1_CS_N, 1); + + gpio_set_value_cansleep(GPIO_EPM_SPI_ADC2_CS_N, 1); + + return rc; +}; +#endif + +static struct msm_adc_channels msm_adc_channels_data[] = { + {"vbatt", CHANNEL_ADC_VBATT, 0, &xoadc_fn, CHAN_PATH_TYPE2, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE3, scale_default}, + {"vcoin", CHANNEL_ADC_VCOIN, 0, &xoadc_fn, CHAN_PATH_TYPE1, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_default}, + {"vcharger_channel", CHANNEL_ADC_VCHG, 0, &xoadc_fn, CHAN_PATH_TYPE3, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE4, scale_default}, + {"charger_current_monitor", CHANNEL_ADC_CHG_MONITOR, 0, &xoadc_fn, + CHAN_PATH_TYPE4, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE1, scale_default}, + {"vph_pwr", CHANNEL_ADC_VPH_PWR, 0, &xoadc_fn, CHAN_PATH_TYPE5, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE3, scale_default}, + {"usb_vbus", CHANNEL_ADC_USB_VBUS, 0, &xoadc_fn, CHAN_PATH_TYPE11, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE3, scale_default}, + {"pmic_therm", CHANNEL_ADC_DIE_TEMP, 0, &xoadc_fn, CHAN_PATH_TYPE12, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE1, scale_pmic_therm}, + {"pmic_therm_4K", CHANNEL_ADC_DIE_TEMP_4K, 0, &xoadc_fn, + CHAN_PATH_TYPE12, + ADC_CONFIG_TYPE1, ADC_CALIB_CONFIG_TYPE7, scale_pmic_therm}, + {"xo_therm", CHANNEL_ADC_XOTHERM, 0, &xoadc_fn, CHAN_PATH_TYPE_NONE, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE5, tdkntcgtherm}, + {"xo_therm_4K", CHANNEL_ADC_XOTHERM_4K, 0, &xoadc_fn, + CHAN_PATH_TYPE_NONE, + ADC_CONFIG_TYPE1, ADC_CALIB_CONFIG_TYPE6, tdkntcgtherm}, + {"hdset_detect", CHANNEL_ADC_HDSET, 0, &xoadc_fn, CHAN_PATH_TYPE6, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE1, scale_default}, + {"chg_batt_amon", CHANNEL_ADC_BATT_AMON, 0, &xoadc_fn, CHAN_PATH_TYPE10, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE1, + scale_xtern_chgr_cur}, + {"msm_therm", CHANNEL_ADC_MSM_THERM, 0, &xoadc_fn, CHAN_PATH_TYPE8, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_msm_therm}, + {"batt_therm", CHANNEL_ADC_BATT_THERM, 0, &xoadc_fn, CHAN_PATH_TYPE7, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_batt_therm}, + {"batt_id", CHANNEL_ADC_BATT_ID, 0, &xoadc_fn, CHAN_PATH_TYPE9, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_default}, + {"ref_625mv", CHANNEL_ADC_625_REF, 0, &xoadc_fn, CHAN_PATH_TYPE15, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_default}, + {"ref_1250mv", CHANNEL_ADC_1250_REF, 0, &xoadc_fn, CHAN_PATH_TYPE13, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_default}, + {"ref_325mv", CHANNEL_ADC_325_REF, 0, &xoadc_fn, CHAN_PATH_TYPE14, + ADC_CONFIG_TYPE2, ADC_CALIB_CONFIG_TYPE2, scale_default}, +}; + +static char *msm_adc_fluid_device_names[] = { + "ADS_ADC1", + "ADS_ADC2", +}; + +static struct msm_adc_platform_data msm_adc_pdata = { + .channel = msm_adc_channels_data, + .num_chan_supported = ARRAY_SIZE(msm_adc_channels_data), +#if defined(CONFIG_I2C) && \ + (defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)) + .adc_gpio_enable = msm_adc_gpio_expander_enable, + .adc_gpio_disable = msm_adc_gpio_expander_disable, + .adc_fluid_enable = msm_adc_gpio_configure_expander_enable, + .adc_fluid_disable = msm_adc_gpio_configure_expander_disable, +#endif +}; + +static struct platform_device msm_adc_device = { + .name = "msm_adc", + .id = -1, + .dev = { + .platform_data = &msm_adc_pdata, + }, +}; + +static struct msm_rtb_platform_data msm_rtb_pdata = { + .size = SZ_1M, +}; + +static int __init msm_rtb_set_buffer_size(char *p) +{ + int s; + + s = memparse(p, NULL); + msm_rtb_pdata.size = ALIGN(s, SZ_4K); + return 0; +} +early_param("msm_rtb_size", msm_rtb_set_buffer_size); + + +static struct platform_device msm_rtb_device = { + .name = "msm_rtb", + .id = -1, + .dev = { + .platform_data = &msm_rtb_pdata, + }, +}; + +static void pmic8058_xoadc_mpp_config(void) +{ + int rc, i; + struct pm8xxx_mpp_init_info xoadc_mpps[] = { + PM8058_MPP_INIT(XOADC_MPP_3, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH5, + AOUT_CTRL_DISABLE), + PM8058_MPP_INIT(XOADC_MPP_5, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH9, + AOUT_CTRL_DISABLE), + PM8058_MPP_INIT(XOADC_MPP_7, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH6, + AOUT_CTRL_DISABLE), + PM8058_MPP_INIT(XOADC_MPP_8, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH8, + AOUT_CTRL_DISABLE), + PM8058_MPP_INIT(XOADC_MPP_10, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH7, + AOUT_CTRL_DISABLE), + PM8901_MPP_INIT(XOADC_MPP_4, D_OUTPUT, PM8901_MPP_DIG_LEVEL_S4, + DOUT_CTRL_LOW), + }; + + for (i = 0; i < ARRAY_SIZE(xoadc_mpps); i++) { + rc = pm8xxx_mpp_config(xoadc_mpps[i].mpp, + &xoadc_mpps[i].config); + if (rc) { + pr_err("%s: Config MPP %d of PM8058 failed\n", + __func__, xoadc_mpps[i].mpp); + } + } +} + +static struct regulator *vreg_ldo18_adc; + +static int pmic8058_xoadc_vreg_config(int on) +{ + int rc; + + if (on) { + rc = regulator_enable(vreg_ldo18_adc); + if (rc) + pr_err("%s: Enable of regulator ldo18_adc " + "failed\n", __func__); + } else { + rc = regulator_disable(vreg_ldo18_adc); + if (rc) + pr_err("%s: Disable of regulator ldo18_adc " + "failed\n", __func__); + } + + return rc; +} + +static int pmic8058_xoadc_vreg_setup(void) +{ + int rc; + + vreg_ldo18_adc = regulator_get(NULL, "8058_l18"); + if (IS_ERR(vreg_ldo18_adc)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_ldo18_adc)); + rc = PTR_ERR(vreg_ldo18_adc); + goto fail; + } + + rc = regulator_set_voltage(vreg_ldo18_adc, 2200000, 2200000); + if (rc) { + pr_err("%s: unable to set ldo18 voltage to 2.2V\n", __func__); + goto fail; + } + + return rc; +fail: + regulator_put(vreg_ldo18_adc); + return rc; +} + +static void pmic8058_xoadc_vreg_shutdown(void) +{ + regulator_put(vreg_ldo18_adc); +} + +/* usec. For this ADC, + * this time represents clk rate @ txco w/ 1024 decimation ratio. + * Each channel has different configuration, thus at the time of starting + * the conversion, xoadc will return actual conversion time + * */ +static struct adc_properties pm8058_xoadc_data = { + .adc_reference = 2200, /* milli-voltage for this adc */ + .bitresolution = 15, + .bipolar = 0, + .conversiontime = 54, +}; + +static struct xoadc_platform_data pm8058_xoadc_pdata = { + .xoadc_prop = &pm8058_xoadc_data, + .xoadc_mpp_config = pmic8058_xoadc_mpp_config, + .xoadc_vreg_set = pmic8058_xoadc_vreg_config, + .xoadc_num = XOADC_PMIC_0, + .xoadc_vreg_setup = pmic8058_xoadc_vreg_setup, + .xoadc_vreg_shutdown = pmic8058_xoadc_vreg_shutdown, +}; +#endif + +#ifdef CONFIG_MSM_SDIO_AL + +static unsigned mdm2ap_status = 140; + +static int configure_mdm2ap_status(int on) +{ + int ret = 0; + if (on) + ret = msm_gpiomux_get(mdm2ap_status); + else + ret = msm_gpiomux_put(mdm2ap_status); + + if (ret) + pr_err("%s: mdm2ap_status config failed, on = %d\n", __func__, + on); + + return ret; +} + + +static int get_mdm2ap_status(void) +{ + return gpio_get_value(mdm2ap_status); +} + +static struct sdio_al_platform_data sdio_al_pdata = { + .config_mdm2ap_status = configure_mdm2ap_status, + .get_mdm2ap_status = get_mdm2ap_status, + .allow_sdioc_version_major_2 = 0, + .peer_sdioc_version_minor = 0x0202, + .peer_sdioc_version_major = 0x0004, + .peer_sdioc_boot_version_minor = 0x0001, + .peer_sdioc_boot_version_major = 0x0003 +}; + +struct platform_device msm_device_sdio_al = { + .name = "msm_sdio_al", + .id = -1, + .dev = { + .parent = &msm_charm_modem.dev, + .platform_data = &sdio_al_pdata, + }, +}; + +#endif /* CONFIG_MSM_SDIO_AL */ + +#define GPIO_VREG_ID_EXT_5V 0 + +static struct regulator_consumer_supply vreg_consumers_EXT_5V[] = { + REGULATOR_SUPPLY("ext_5v", NULL), + REGULATOR_SUPPLY("8901_mpp0", NULL), +}; + +#define GPIO_VREG_INIT(_id, _reg_name, _gpio_label, _gpio, _active_low) \ + [GPIO_VREG_ID_##_id] = { \ + .init_data = { \ + .constraints = { \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_##_id), \ + .consumer_supplies = vreg_consumers_##_id, \ + }, \ + .regulator_name = _reg_name, \ + .active_low = _active_low, \ + .gpio_label = _gpio_label, \ + .gpio = _gpio, \ + } + +/* GPIO regulator constraints */ +static struct gpio_regulator_platform_data msm_gpio_regulator_pdata[] = { + GPIO_VREG_INIT(EXT_5V, "ext_5v", "ext_5v_en", + PM8901_MPP_PM_TO_SYS(0), 0), +}; + +/* GPIO regulator */ +static struct platform_device msm8x60_8901_mpp_vreg __devinitdata = { + .name = GPIO_REGULATOR_DEV_NAME, + .id = PM8901_MPP_PM_TO_SYS(0), + .dev = { + .platform_data = + &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_5V], + }, +}; + +static void __init pm8901_vreg_mpp0_init(void) +{ + int rc; + + struct pm8xxx_mpp_init_info pm8901_vreg_mpp0 = { + .mpp = PM8901_MPP_PM_TO_SYS(0), + .config = { + .type = PM8XXX_MPP_TYPE_D_OUTPUT, + .level = PM8901_MPP_DIG_LEVEL_VPH, + }, + }; + + /* + * Set PMIC 8901 MPP0 active_high to 0 for surf and charm_surf. This + * implies that the regulator connected to MPP0 is enabled when + * MPP0 is low. + */ + if (machine_is_msm8x60_surf() || machine_is_msm8x60_fusion()) { + msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_5V].active_low = 1; + pm8901_vreg_mpp0.config.control = PM8XXX_MPP_DOUT_CTRL_HIGH; + } else { + msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_5V].active_low = 0; + pm8901_vreg_mpp0.config.control = PM8XXX_MPP_DOUT_CTRL_LOW; + } + + rc = pm8xxx_mpp_config(pm8901_vreg_mpp0.mpp, &pm8901_vreg_mpp0.config); + if (rc) + pr_err("%s: pm8xxx_mpp_config: rc=%d\n", __func__, rc); +} + +static struct platform_device *charm_devices[] __initdata = { + &msm_charm_modem, +#ifdef CONFIG_MSM_SDIO_AL + &msm_device_sdio_al, +#endif +}; + +#ifdef CONFIG_SND_SOC_MSM8660_APQ +static struct platform_device *dragon_alsa_devices[] __initdata = { + &msm_pcm, + &msm_pcm_routing, + &msm_cpudai0, + &msm_cpudai1, + &msm_cpudai_hdmi_rx, + &msm_cpudai_bt_rx, + &msm_cpudai_bt_tx, + &msm_cpudai_fm_rx, + &msm_cpudai_fm_tx, + &msm_cpu_fe, + &msm_stub_codec, + &msm_lpa_pcm, +}; +#endif + +static struct platform_device *asoc_devices[] __initdata = { + &asoc_msm_pcm, + &asoc_msm_dai0, + &asoc_msm_dai1, +}; + +static struct platform_device *surf_devices[] __initdata = { + &msm_device_smd, + &msm_device_uart_dm12, + &msm_pil_q6v3, + &msm_pil_modem, + &msm_pil_tzapps, + &msm_pil_dsps, +#ifdef CONFIG_I2C_QUP + &msm_gsbi3_qup_i2c_device, + &msm_gsbi4_qup_i2c_device, + &msm_gsbi7_qup_i2c_device, + &msm_gsbi8_qup_i2c_device, + &msm_gsbi9_qup_i2c_device, + &msm_gsbi12_qup_i2c_device, +#endif +#ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, +#endif +#ifdef CONFIG_MSM_SSBI + &msm_device_ssbi_pmic1, + &msm_device_ssbi_pmic2, +#endif +#ifdef CONFIG_I2C_SSBI + &msm_device_ssbi3, +#endif +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + &isp1763_device, +#endif + +#if defined (CONFIG_MSM_8x60_VOIP) + &asoc_msm_mvs, + &asoc_mvs_dai0, + &asoc_mvs_dai1, +#endif + +#if defined(CONFIG_USB_MSM_72K) || defined(CONFIG_USB_EHCI_HCD) + &msm_device_otg, +#endif +#ifdef CONFIG_USB_MSM_72K + &msm_device_gadget_peripheral, +#endif +#ifdef CONFIG_USB_G_ANDROID + &android_usb_device, +#endif +#ifdef CONFIG_BATTERY_MSM + &msm_batt_device, +#endif +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_smipool_device, + &android_pmem_audio_device, +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +#ifdef CONFIG_MSM_ROTATOR + &msm_rotator_device, +#endif + &msm_fb_device, + &msm_kgsl_3d0, + &msm_kgsl_2d0, + &msm_kgsl_2d1, + &lcdc_samsung_panel_device, +#ifdef CONFIG_FB_MSM_LCDC_NT35582_WVGA + &lcdc_nt35582_panel_device, +#endif +#ifdef CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT + &lcdc_samsung_oled_panel_device, +#endif +#ifdef CONFIG_FB_MSM_LCDC_AUO_WVGA + &lcdc_auo_wvga_panel_device, +#endif +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL + &hdmi_msm_device, +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ +#ifdef CONFIG_FB_MSM_MIPI_DSI + &mipi_dsi_toshiba_panel_device, + &mipi_dsi_novatek_panel_device, +#endif +#ifdef CONFIG_MSM_CAMERA +#ifndef CONFIG_MSM_CAMERA_V4L2 +#ifdef CONFIG_MT9E013 + &msm_camera_sensor_mt9e013, +#endif +#ifdef CONFIG_IMX074 + &msm_camera_sensor_imx074, +#endif +#ifdef CONFIG_WEBCAM_OV7692 + &msm_camera_sensor_webcam_ov7692, +#endif +#ifdef CONFIG_WEBCAM_OV9726 + &msm_camera_sensor_webcam_ov9726, +#endif +#ifdef CONFIG_QS_S5K4E1 + &msm_camera_sensor_qs_s5k4e1, +#endif +#ifdef CONFIG_VX6953 + &msm_camera_sensor_vx6953, +#endif +#endif +#endif +#ifdef CONFIG_MSM_GEMINI + &msm_gemini_device, +#endif +#ifdef CONFIG_MSM_VPE +#ifndef CONFIG_MSM_CAMERA_V4L2 + &msm_vpe_device, +#endif +#endif + +#if defined(CONFIG_MSM_RPM_LOG) || defined(CONFIG_MSM_RPM_LOG_MODULE) + &msm8660_rpm_log_device, +#endif +#if defined(CONFIG_MSM_RPM_STATS_LOG) + &msm8660_rpm_stat_device, +#endif + &msm_device_vidc, +#if (defined(CONFIG_MARIMBA_CORE)) && \ + (defined(CONFIG_MSM_BT_POWER) || defined(CONFIG_MSM_BT_POWER_MODULE)) + &msm_bt_power_device, +#endif +#ifdef CONFIG_SENSORS_MSM_ADC + &msm_adc_device, +#endif + &rpm_regulator_device, + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + &qcrypto_device, +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + &qcedev_device, +#endif + + +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) +#ifdef CONFIG_MSM_USE_TSIF1 + &msm_device_tsif[1], +#else + &msm_device_tsif[0], +#endif /* CONFIG_MSM_USE_TSIF1 */ +#endif /* CONFIG_TSIF */ + +#ifdef CONFIG_HW_RANDOM_MSM + &msm_device_rng, +#endif + + &msm_tsens_device, + &msm8660_rpm_device, +#ifdef CONFIG_ION_MSM + &ion_dev, +#endif + &msm8660_device_watchdog, + &msm_device_tz_log, + &msm_rtb_device, +}; + +#ifdef CONFIG_ION_MSM +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION +static struct ion_cp_heap_pdata cp_mm_ion_pdata = { + .permission_type = IPT_TYPE_MM_CARVEOUT, + .align = SZ_64K, + .request_region = request_smi_region, + .release_region = release_smi_region, + .setup_region = setup_smi_region, + .secure_base = MSM_ION_HOLE_BASE, + .secure_size = MSM_ION_HOLE_SIZE + MSM_ION_MM_SIZE, + .iommu_map_all = 1, + .iommu_2x_map_domain = VIDEO_DOMAIN, +}; + +static struct ion_cp_heap_pdata cp_mfc_ion_pdata = { + .permission_type = IPT_TYPE_MFC_SHAREDMEM, + .align = PAGE_SIZE, + .request_region = request_smi_region, + .release_region = release_smi_region, + .setup_region = setup_smi_region, +}; + +static struct ion_cp_heap_pdata cp_wb_ion_pdata = { + .permission_type = IPT_TYPE_MDP_WRITEBACK, + .align = PAGE_SIZE, +}; + +static struct ion_co_heap_pdata hole_co_ion_pdata = { + .adjacent_mem_id = ION_CP_MM_HEAP_ID, +}; + +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; +#endif + +/** + * These heaps are listed in the order they will be allocated. Due to + * video hardware restrictions and content protection the FW heap has to + * be allocated adjacent (below) the MM heap and the MFC heap has to be + * allocated after the MM heap to ensure MFC heap is not more than 256MB + * away from the base address of the FW heap. + * However, the order of FW heap and MM heap doesn't matter since these + * two heaps are taken care of by separate code to ensure they are adjacent + * to each other. + * Don't swap the order unless you know what you are doing! + */ +static struct ion_platform_data ion_pdata = { + .nr = MSM_ION_HEAP_NUM, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + { + .id = ION_CP_MM_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MM_HEAP_NAME, + .base = MSM_ION_MM_BASE, + .size = MSM_ION_MM_SIZE, + .memory_type = ION_SMI_TYPE, + .extra_data = (void *) &cp_mm_ion_pdata, + }, + { + .id = ION_MM_FIRMWARE_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_MM_FIRMWARE_HEAP_NAME, + .base = MSM_ION_HOLE_BASE, + .size = MSM_ION_HOLE_SIZE, + .memory_type = ION_SMI_TYPE, + .extra_data = (void *) &hole_co_ion_pdata, + }, + { + .id = ION_CP_MFC_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_MFC_HEAP_NAME, + .base = MSM_ION_MFC_BASE, + .size = MSM_ION_MFC_SIZE, + .memory_type = ION_SMI_TYPE, + .extra_data = (void *) &cp_mfc_ion_pdata, + }, + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .size = MSM_ION_SF_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + { + .id = ION_CAMERA_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_CAMERA_HEAP_NAME, + .size = MSM_ION_CAMERA_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = &co_ion_pdata, + }, + { + .id = ION_CP_WB_HEAP_ID, + .type = ION_HEAP_TYPE_CP, + .name = ION_WB_HEAP_NAME, + .size = MSM_ION_WB_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &cp_wb_ion_pdata, + }, + { + .id = ION_QSECOM_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_QSECOM_HEAP_NAME, + .size = MSM_ION_QSECOM_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *) &co_ion_pdata, + }, + { + .id = ION_AUDIO_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_AUDIO_HEAP_NAME, + .size = MSM_ION_AUDIO_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, +#endif + } +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; +#endif + + +static struct memtype_reserve msm8x60_reserve_table[] __initdata = { + /* Kernel SMI memory pool for video core, used for firmware */ + /* and encoder, decoder scratch buffers */ + /* Kernel SMI memory pool should always precede the user space */ + /* SMI memory pool, as the video core will use offset address */ + /* from the Firmware base */ + [MEMTYPE_SMI_KERNEL] = { + .start = KERNEL_SMI_BASE, + .limit = KERNEL_SMI_SIZE, + .size = KERNEL_SMI_SIZE, + .flags = MEMTYPE_FLAGS_FIXED, + }, + [MEMTYPE_SMI] = { + }, + [MEMTYPE_EBI0] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, + [MEMTYPE_EBI1] = { + .flags = MEMTYPE_FLAGS_1M_ALIGN, + }, +}; + +static void __init reserve_ion_memory(void) +{ +#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + unsigned int i; + + if (hdmi_is_primary) { + msm_ion_sf_size = MSM_HDMI_PRIM_ION_SF_SIZE; + for (i = 0; i < ion_pdata.nr; i++) { + if (ion_pdata.heaps[i].id == ION_SF_HEAP_ID) { + ion_pdata.heaps[i].size = msm_ion_sf_size; + pr_debug("msm_ion_sf_size 0x%x\n", + msm_ion_sf_size); + break; + } + } + } + + /* Verify size of heap is a multiple of 64K */ + for (i = 0; i < ion_pdata.nr; i++) { + struct ion_platform_heap *heap = &(ion_pdata.heaps[i]); + + if (heap->extra_data && heap->type == ION_HEAP_TYPE_CP) { + int map_all = ((struct ion_cp_heap_pdata *) + heap->extra_data)->iommu_map_all; + + if (map_all && (heap->size & (SZ_64K-1))) { + heap->size = ALIGN(heap->size, SZ_64K); + pr_err("Heap %s size is not a multiple of 64K. Adjusting size to %x\n", + heap->name, heap->size); + + } + } + } + + msm8x60_reserve_table[MEMTYPE_EBI1].size += msm_ion_sf_size; + msm8x60_reserve_table[MEMTYPE_EBI1].size += MSM_ION_CAMERA_SIZE; + msm8x60_reserve_table[MEMTYPE_EBI1].size += MSM_ION_WB_SIZE; + msm8x60_reserve_table[MEMTYPE_EBI1].size += MSM_ION_AUDIO_SIZE; + msm8x60_reserve_table[MEMTYPE_EBI1].size += MSM_ION_QSECOM_SIZE; +#endif +} + +static void __init size_pmem_devices(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + android_pmem_adsp_pdata.size = pmem_adsp_size; + android_pmem_smipool_pdata.size = MSM_PMEM_SMIPOOL_SIZE; + + if (hdmi_is_primary) + pmem_sf_size = MSM_HDMI_PRIM_PMEM_SF_SIZE; + android_pmem_pdata.size = pmem_sf_size; + android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE; +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ +} + +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm8x60_reserve_table[p->memory_type].size += p->size; +} +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ +#endif /*CONFIG_ANDROID_PMEM*/ + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM +#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION + reserve_memory_for(&android_pmem_adsp_pdata); + reserve_memory_for(&android_pmem_smipool_pdata); + reserve_memory_for(&android_pmem_pdata); + reserve_memory_for(&android_pmem_audio_pdata); +#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/ + msm8x60_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif /*CONFIG_ANDROID_PMEM*/ +} + +static void __init reserve_mdp_memory(void); + +static void __init reserve_rtb_memory(void) +{ +#if defined(CONFIG_MSM_RTB) + msm8x60_reserve_table[MEMTYPE_EBI1].size += msm_rtb_pdata.size; +#endif +} + +static void __init msm8x60_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); + reserve_ion_memory(); + reserve_mdp_memory(); + reserve_rtb_memory(); +} + +static int msm8x60_paddr_to_memtype(unsigned int paddr) +{ + if (paddr >= 0x40000000 && paddr < 0x60000000) + return MEMTYPE_EBI1; + if (paddr >= 0x38000000 && paddr < 0x40000000) + return MEMTYPE_SMI; + return MEMTYPE_NONE; +} + +static struct reserve_info msm8x60_reserve_info __initdata = { + .memtype_reserve_table = msm8x60_reserve_table, + .calculate_reserve_sizes = msm8x60_calculate_reserve_sizes, + .paddr_to_memtype = msm8x60_paddr_to_memtype, +}; + +static char prim_panel_name[PANEL_NAME_MAX_LEN]; +static char ext_panel_name[PANEL_NAME_MAX_LEN]; +static int __init prim_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(prim_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("prim_display", prim_display_setup); + +static int __init ext_display_setup(char *param) +{ + if (strnlen(param, PANEL_NAME_MAX_LEN)) + strlcpy(ext_panel_name, param, PANEL_NAME_MAX_LEN); + return 0; +} +early_param("ext_display", ext_display_setup); + static void __init msm8x60_reserve(void) { - memblock_remove(0x40000000, SZ_2M); + msm8x60_set_display_params(prim_panel_name, ext_panel_name); + reserve_info = &msm8x60_reserve_info; + msm_reserve(); +} + +#define EXT_CHG_VALID_MPP 10 +#define EXT_CHG_VALID_MPP_2 11 + +static struct pm8xxx_mpp_init_info isl_mpp[] = { + PM8058_MPP_INIT(EXT_CHG_VALID_MPP, D_INPUT, + PM8058_MPP_DIG_LEVEL_S3, DIN_TO_INT), + PM8058_MPP_INIT(EXT_CHG_VALID_MPP_2, D_BI_DIR, + PM8058_MPP_DIG_LEVEL_S3, BI_PULLUP_10KOHM), +}; + +#ifdef CONFIG_ISL9519_CHARGER +static int isl_detection_setup(void) +{ + int ret = 0, i; + + for (i = 0; i < ARRAY_SIZE(isl_mpp); i++) { + ret = pm8xxx_mpp_config(isl_mpp[i].mpp, + &isl_mpp[i].config); + if (ret) { + pr_err("%s: Config MPP %d of PM8058 failed\n", + __func__, isl_mpp[i].mpp); + return ret; + } + } + + return ret; +} + +static struct isl_platform_data isl_data __initdata = { + .chgcurrent = 700, + .valid_n_gpio = PM8058_MPP_PM_TO_SYS(10), + .chg_detection_config = isl_detection_setup, + .max_system_voltage = 4200, + .min_system_voltage = 3200, + .term_current = 120, + .input_current = 2048, +}; + +static struct i2c_board_info isl_charger_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("isl9519q", 0x9), + .irq = PM8058_IRQ_BASE + PM8058_CBLPWR_IRQ, + .platform_data = &isl_data, + }, +}; +#endif + +#if defined(CONFIG_SMB137B_CHARGER) || defined(CONFIG_SMB137B_CHARGER_MODULE) +static int smb137b_detection_setup(void) +{ + int ret = 0, i; + + for (i = 0; i < ARRAY_SIZE(isl_mpp); i++) { + ret = pm8xxx_mpp_config(isl_mpp[i].mpp, + &isl_mpp[i].config); + if (ret) { + pr_err("%s: Config MPP %d of PM8058 failed\n", + __func__, isl_mpp[i].mpp); + return ret; + } + } + + return ret; +} + +static struct smb137b_platform_data smb137b_data __initdata = { + .chg_detection_config = smb137b_detection_setup, + .valid_n_gpio = PM8058_MPP_PM_TO_SYS(10), + .batt_mah_rating = 950, +}; + +static struct i2c_board_info smb137b_charger_i2c_info[] __initdata = { + { + I2C_BOARD_INFO("smb137b", 0x08), + .irq = PM8058_IRQ_BASE + PM8058_CBLPWR_IRQ, + .platform_data = &smb137b_data, + }, +}; +#endif + +#ifdef CONFIG_PMIC8058 +#define PMIC_GPIO_SDC3_DET 22 +#define PMIC_GPIO_TOUCH_DISC_INTR 5 + +static int pm8058_gpios_init(void) +{ + int i; + int rc; + struct pm8058_gpio_cfg { + int gpio; + struct pm_gpio cfg; + }; + + struct pm8058_gpio_cfg gpio_cfgs[] = { + { /* FFA ethernet */ + PM8058_GPIO_PM_TO_SYS(6), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_DN, + .vin_sel = 2, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + { + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC3_DET - 1), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_30, + .vin_sel = 2, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, +#endif + { /* core&surf gpio expander */ + PM8058_GPIO_PM_TO_SYS(UI_INT1_N), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, + { /* docking gpio expander */ + PM8058_GPIO_PM_TO_SYS(UI_INT2_N), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, + { /* FHA/keypad gpio expanders */ + PM8058_GPIO_PM_TO_SYS(UI_INT3_N), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }, + }, + { /* Timpani Reset */ + PM8058_GPIO_PM_TO_SYS(20), + { + .direction = PM_GPIO_DIR_OUT, + .output_value = 1, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .pull = PM_GPIO_PULL_DN, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .vin_sel = 2, + .inv_int_pol = 0, + } + }, + { /* PMIC ID interrupt */ + PM8058_GPIO_PM_TO_SYS(36), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_NO, + .function = PM_GPIO_FUNC_NORMAL, + .vin_sel = 2, + .inv_int_pol = 0, + } + }, + }; + +#if defined(CONFIG_TOUCHDISC_VTD518_SHINETSU) || \ + defined(CONFIG_TOUCHDISC_VTD518_SHINETSU_MODULE) + struct pm_gpio touchdisc_intr_gpio_cfg = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_1P5, + .vin_sel = 2, + .function = PM_GPIO_FUNC_NORMAL, + }; +#endif + +#if defined(CONFIG_HAPTIC_ISA1200) || \ + defined(CONFIG_HAPTIC_ISA1200_MODULE) + struct pm_gpio en_hap_gpio_cfg = { + .direction = PM_GPIO_DIR_OUT, + .pull = PM_GPIO_PULL_NO, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + .vin_sel = 2, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + }; +#endif + +#if defined(CONFIG_PMIC8058_OTHC) || defined(CONFIG_PMIC8058_OTHC_MODULE) + struct pm8058_gpio_cfg line_in_gpio_cfg = { + PM8058_GPIO_PM_TO_SYS(18), + { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_1P5, + .vin_sel = 2, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + } + }; +#endif + +#if defined(CONFIG_QS_S5K4E1) + { + struct pm8058_gpio_cfg qs_hc37_cam_pd_gpio_cfg = { + PM8058_GPIO_PM_TO_SYS(26), + { + .direction = PM_GPIO_DIR_OUT, + .output_value = 0, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .pull = PM_GPIO_PULL_DN, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .vin_sel = 2, + .inv_int_pol = 0, + } + }; +#endif +#ifdef CONFIG_FB_MSM_LCDC_NT35582_WVGA + struct pm8058_gpio_cfg pmic_lcdc_nt35582_gpio_cfg = { + PM8058_GPIO_PM_TO_SYS(GPIO_NT35582_BL_EN_HW_PIN - 1), + { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 1, + .pull = PM_GPIO_PULL_UP_30, + /* 2.9V PM_GPIO_VIN_L2, which gives 2.6V */ + .vin_sel = PM8058_GPIO_VIN_L5, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + } + }; +#endif +#if defined(CONFIG_HAPTIC_ISA1200) || \ + defined(CONFIG_HAPTIC_ISA1200_MODULE) + if (machine_is_msm8x60_fluid()) { + rc = pm8xxx_gpio_config( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HAP_ENABLE), + &en_hap_gpio_cfg); + if (rc < 0) { + pr_err("%s: pmic haptics gpio config failed\n", + __func__); + } + rc = pm8xxx_gpio_config( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_HAP_LDO_ENABLE), + &en_hap_gpio_cfg); + if (rc < 0) { + pr_err("%s: pmic haptics ldo gpio config failed\n", + __func__); + } + + } +#endif + +#if defined(CONFIG_TOUCHDISC_VTD518_SHINETSU) || \ + defined(CONFIG_TOUCHDISC_VTD518_SHINETSU_MODULE) + if (machine_is_msm8x60_ffa() || machine_is_msm8x60_surf() || + machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { + rc = pm8xxx_gpio_config( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_TOUCH_DISC_INTR), + &touchdisc_intr_gpio_cfg); + if (rc < 0) { + pr_err("%s: Touchdisc interrupt gpio config failed\n", + __func__); + } + } +#endif + +#if defined(CONFIG_PMIC8058_OTHC) || defined(CONFIG_PMIC8058_OTHC_MODULE) + /* Line_in only for 8660 ffa & surf */ + if (machine_is_msm8x60_ffa() || machine_is_msm8x60_surf() || + machine_is_msm8x60_fusion() || machine_is_msm8x60_dragon() || + machine_is_msm8x60_fusn_ffa()) { + rc = pm8xxx_gpio_config(line_in_gpio_cfg.gpio, + &line_in_gpio_cfg.cfg); + if (rc < 0) { + pr_err("%s pmic line_in gpio config failed\n", + __func__); + return rc; + } + } +#endif + +#ifdef CONFIG_FB_MSM_LCDC_NT35582_WVGA + if (machine_is_msm8x60_dragon()) { + rc = pm8xxx_gpio_config(pmic_lcdc_nt35582_gpio_cfg.gpio, + &pmic_lcdc_nt35582_gpio_cfg.cfg); + if (rc < 0) { + pr_err("%s pmic gpio config failed\n", __func__); + return rc; + } + } +#endif + +#if defined(CONFIG_QS_S5K4E1) + /* qs_cam_hc37_cam_pd only for 8660 fluid qs camera*/ + if (machine_is_msm8x60_fluid()) { + rc = pm8xxx_gpio_config(qs_hc37_cam_pd_gpio_cfg.gpio, + &qs_hc37_cam_pd_gpio_cfg.cfg); + if (rc < 0) { + pr_err("%s pmic qs_hc37_cam_pd gpio config failed\n", + __func__); + return rc; + } + } + } +#endif + + for (i = 0; i < ARRAY_SIZE(gpio_cfgs); ++i) { + rc = pm8xxx_gpio_config(gpio_cfgs[i].gpio, + &gpio_cfgs[i].cfg); + if (rc < 0) { + pr_err("%s pmic gpio config failed\n", + __func__); + return rc; + } + } + + return 0; +} + +static const unsigned int ffa_keymap[] = { + KEY(0, 0, KEY_FN_F1), /* LS - PUSH1 */ + KEY(0, 1, KEY_UP), /* NAV - UP */ + KEY(0, 2, KEY_LEFT), /* NAV - LEFT */ + KEY(0, 3, KEY_VOLUMEUP), /* Shuttle SW_UP */ + + KEY(1, 0, KEY_FN_F2), /* LS - PUSH2 */ + KEY(1, 1, KEY_RIGHT), /* NAV - RIGHT */ + KEY(1, 2, KEY_DOWN), /* NAV - DOWN */ + KEY(1, 3, KEY_VOLUMEDOWN), + + KEY(2, 3, KEY_ENTER), /* SW_PUSH key */ + + KEY(4, 0, KEY_CAMERA_FOCUS), /* RS - PUSH1 */ + KEY(4, 1, KEY_UP), /* USER_UP */ + KEY(4, 2, KEY_LEFT), /* USER_LEFT */ + KEY(4, 3, KEY_HOME), /* Right switch: MIC Bd */ + KEY(4, 4, KEY_FN_F3), /* Reserved MIC */ + + KEY(5, 0, KEY_CAMERA), /* RS - PUSH2 */ + KEY(5, 1, KEY_RIGHT), /* USER_RIGHT */ + KEY(5, 2, KEY_DOWN), /* USER_DOWN */ + KEY(5, 3, KEY_BACK), /* Left switch: MIC */ + KEY(5, 4, KEY_MENU), /* Center switch: MIC */ +}; + +static const unsigned int dragon_keymap[] = { + KEY(0, 0, KEY_MENU), + KEY(0, 2, KEY_1), + KEY(0, 3, KEY_4), + KEY(0, 4, KEY_7), + + KEY(1, 0, KEY_UP), + KEY(1, 1, KEY_LEFT), + KEY(1, 2, KEY_DOWN), + KEY(1, 3, KEY_5), + KEY(1, 4, KEY_8), + + KEY(2, 0, KEY_HOME), + KEY(2, 1, KEY_REPLY), + KEY(2, 2, KEY_2), + KEY(2, 3, KEY_6), + KEY(2, 4, KEY_0), + + KEY(3, 0, KEY_VOLUMEUP), + KEY(3, 1, KEY_RIGHT), + KEY(3, 2, KEY_3), + KEY(3, 3, KEY_9), + KEY(3, 4, KEY_SWITCHVIDEOMODE), + + KEY(4, 0, KEY_VOLUMEDOWN), + KEY(4, 1, KEY_BACK), + KEY(4, 2, KEY_CAMERA), + KEY(4, 3, KEY_KBDILLUMTOGGLE), +}; + +static struct matrix_keymap_data ffa_keymap_data = { + .keymap_size = ARRAY_SIZE(ffa_keymap), + .keymap = ffa_keymap, +}; + +static struct pm8xxx_keypad_platform_data ffa_keypad_data = { + .input_name = "ffa-keypad", + .input_phys_device = "ffa-keypad/input0", + .num_rows = 6, + .num_cols = 5, + .rows_gpio_start = PM8058_GPIO_PM_TO_SYS(8), + .cols_gpio_start = PM8058_GPIO_PM_TO_SYS(0), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &ffa_keymap_data, +}; + +static struct matrix_keymap_data dragon_keymap_data = { + .keymap_size = ARRAY_SIZE(dragon_keymap), + .keymap = dragon_keymap, +}; + +static struct pm8xxx_keypad_platform_data dragon_keypad_data = { + .input_name = "dragon-keypad", + .input_phys_device = "dragon-keypad/input0", + .num_rows = 6, + .num_cols = 5, + .rows_gpio_start = PM8058_GPIO_PM_TO_SYS(8), + .cols_gpio_start = PM8058_GPIO_PM_TO_SYS(0), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &dragon_keymap_data, +}; + +static const unsigned int fluid_keymap[] = { + KEY(0, 0, KEY_FN_F1), /* LS - PUSH1 */ + KEY(0, 1, KEY_UP), /* NAV - UP */ + KEY(0, 2, KEY_LEFT), /* NAV - LEFT */ + KEY(0, 3, KEY_VOLUMEDOWN), /* Shuttle SW_UP */ + + KEY(1, 0, KEY_FN_F2), /* LS - PUSH2 */ + KEY(1, 1, KEY_RIGHT), /* NAV - RIGHT */ + KEY(1, 2, KEY_DOWN), /* NAV - DOWN */ + KEY(1, 3, KEY_VOLUMEUP), + + KEY(2, 3, KEY_ENTER), /* SW_PUSH key */ + + KEY(4, 0, KEY_CAMERA_FOCUS), /* RS - PUSH1 */ + KEY(4, 1, KEY_UP), /* USER_UP */ + KEY(4, 2, KEY_LEFT), /* USER_LEFT */ + KEY(4, 3, KEY_HOME), /* Right switch: MIC Bd */ + KEY(4, 4, KEY_FN_F3), /* Reserved MIC */ + + KEY(5, 0, KEY_CAMERA), /* RS - PUSH2 */ + KEY(5, 1, KEY_RIGHT), /* USER_RIGHT */ + KEY(5, 2, KEY_DOWN), /* USER_DOWN */ + KEY(5, 3, KEY_BACK), /* Left switch: MIC */ + KEY(5, 4, KEY_MENU), /* Center switch: MIC */ +}; + +static struct matrix_keymap_data fluid_keymap_data = { + .keymap_size = ARRAY_SIZE(fluid_keymap), + .keymap = fluid_keymap, +}; + +static struct pm8xxx_keypad_platform_data fluid_keypad_data = { + .input_name = "fluid-keypad", + .input_phys_device = "fluid-keypad/input0", + .num_rows = 6, + .num_cols = 5, + .rows_gpio_start = PM8058_GPIO_PM_TO_SYS(8), + .cols_gpio_start = PM8058_GPIO_PM_TO_SYS(0), + .debounce_ms = 15, + .scan_delay_ms = 32, + .row_hold_ns = 91500, + .wakeup = 1, + .keymap_data = &fluid_keymap_data, +}; + +static struct pm8xxx_vibrator_platform_data pm8058_vib_pdata = { + .initial_vibrate_ms = 500, + .level_mV = 3000, + .max_timeout_ms = 15000, +}; + +static struct pm8xxx_rtc_platform_data pm8058_rtc_pdata = { + .rtc_write_enable = false, + .rtc_alarm_powerup = false, +}; + +static struct pm8xxx_pwrkey_platform_data pm8058_pwrkey_pdata = { + .pull_up = 1, + .kpd_trigger_delay_us = 15625, + .wakeup = 1, +}; + +#define PM8058_LINE_IN_DET_GPIO PM8058_GPIO_PM_TO_SYS(18) + +static struct othc_accessory_info othc_accessories[] = { + { + .accessory = OTHC_SVIDEO_OUT, + .detect_flags = OTHC_MICBIAS_DETECT | OTHC_SWITCH_DETECT + | OTHC_ADC_DETECT, + .key_code = SW_VIDEOOUT_INSERT, + .enabled = false, + .adc_thres = { + .min_threshold = 20, + .max_threshold = 40, + }, + }, + { + .accessory = OTHC_ANC_HEADPHONE, + .detect_flags = OTHC_MICBIAS_DETECT | OTHC_GPIO_DETECT | + OTHC_SWITCH_DETECT, + .gpio = PM8058_LINE_IN_DET_GPIO, + .active_low = 1, + .key_code = SW_HEADPHONE_INSERT, + .enabled = true, + }, + { + .accessory = OTHC_ANC_HEADSET, + .detect_flags = OTHC_MICBIAS_DETECT | OTHC_GPIO_DETECT, + .gpio = PM8058_LINE_IN_DET_GPIO, + .active_low = 1, + .key_code = SW_HEADPHONE_INSERT, + .enabled = true, + }, + { + .accessory = OTHC_HEADPHONE, + .detect_flags = OTHC_MICBIAS_DETECT | OTHC_SWITCH_DETECT, + .key_code = SW_HEADPHONE_INSERT, + .enabled = true, + }, + { + .accessory = OTHC_MICROPHONE, + .detect_flags = OTHC_GPIO_DETECT, + .gpio = PM8058_LINE_IN_DET_GPIO, + .active_low = 1, + .key_code = SW_MICROPHONE_INSERT, + .enabled = true, + }, + { + .accessory = OTHC_HEADSET, + .detect_flags = OTHC_MICBIAS_DETECT, + .key_code = SW_HEADPHONE_INSERT, + .enabled = true, + }, +}; + +static struct othc_switch_info switch_info[] = { + { + .min_adc_threshold = 0, + .max_adc_threshold = 100, + .key_code = KEY_PLAYPAUSE, + }, + { + .min_adc_threshold = 100, + .max_adc_threshold = 200, + .key_code = KEY_REWIND, + }, + { + .min_adc_threshold = 200, + .max_adc_threshold = 500, + .key_code = KEY_FASTFORWARD, + }, +}; + +static struct othc_n_switch_config switch_config = { + .voltage_settling_time_ms = 0, + .num_adc_samples = 3, + .adc_channel = CHANNEL_ADC_HDSET, + .switch_info = switch_info, + .num_keys = ARRAY_SIZE(switch_info), + .default_sw_en = true, + .default_sw_idx = 0, +}; + +static struct hsed_bias_config hsed_bias_config = { + /* HSED mic bias config info */ + .othc_headset = OTHC_HEADSET_NO, + .othc_lowcurr_thresh_uA = 100, + .othc_highcurr_thresh_uA = 600, + .othc_hyst_prediv_us = 7800, + .othc_period_clkdiv_us = 62500, + .othc_hyst_clk_us = 121000, + .othc_period_clk_us = 312500, + .othc_wakeup = 1, +}; + +static struct othc_hsed_config hsed_config_1 = { + .hsed_bias_config = &hsed_bias_config, + /* + * The detection delay and switch reporting delay are + * required to encounter a hardware bug (spurious switch + * interrupts on slow insertion/removal of the headset). + * This will introduce a delay in reporting the accessory + * insertion and removal to the userspace. + */ + .detection_delay_ms = 1500, + /* Switch info */ + .switch_debounce_ms = 1500, + .othc_support_n_switch = false, + .switch_config = &switch_config, + .ir_gpio = -1, + /* Accessory info */ + .accessories_support = true, + .accessories = othc_accessories, + .othc_num_accessories = ARRAY_SIZE(othc_accessories), +}; + +static struct othc_regulator_config othc_reg = { + .regulator = "8058_l5", + .max_uV = 2850000, + .min_uV = 2850000, +}; + +/* MIC_BIAS0 is configured as normal MIC BIAS */ +static struct pmic8058_othc_config_pdata othc_config_pdata_0 = { + .micbias_select = OTHC_MICBIAS_0, + .micbias_capability = OTHC_MICBIAS, + .micbias_enable = OTHC_SIGNAL_OFF, + .micbias_regulator = &othc_reg, +}; + +/* MIC_BIAS1 is configured as HSED_BIAS for OTHC */ +static struct pmic8058_othc_config_pdata othc_config_pdata_1 = { + .micbias_select = OTHC_MICBIAS_1, + .micbias_capability = OTHC_MICBIAS_HSED, + .micbias_enable = OTHC_SIGNAL_PWM_TCXO, + .micbias_regulator = &othc_reg, + .hsed_config = &hsed_config_1, + .hsed_name = "8660_handset", +}; + +/* MIC_BIAS2 is configured as normal MIC BIAS */ +static struct pmic8058_othc_config_pdata othc_config_pdata_2 = { + .micbias_select = OTHC_MICBIAS_2, + .micbias_capability = OTHC_MICBIAS, + .micbias_enable = OTHC_SIGNAL_OFF, + .micbias_regulator = &othc_reg, +}; + + +static void __init msm8x60_init_pm8058_othc(void) +{ + int i; + + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2 || + machine_is_msm8x60_fluid() || machine_is_msm8x60_fusion() || + machine_is_msm8x60_fusn_ffa()) { + /* 3-switch headset supported only by V2 FFA and FLUID */ + hsed_config_1.accessories_adc_support = true, + /* ADC based accessory detection works only on V2 and FLUID */ + hsed_config_1.accessories_adc_channel = CHANNEL_ADC_HDSET, + hsed_config_1.othc_support_n_switch = true; + } + + /* IR GPIO is absent on FLUID */ + if (machine_is_msm8x60_fluid()) + hsed_config_1.ir_gpio = -1; + + for (i = 0; i < ARRAY_SIZE(othc_accessories); i++) { + if (machine_is_msm8x60_fluid()) { + switch (othc_accessories[i].accessory) { + case OTHC_ANC_HEADPHONE: + case OTHC_ANC_HEADSET: + othc_accessories[i].gpio = GPIO_HEADSET_DET_N; + break; + case OTHC_MICROPHONE: + othc_accessories[i].enabled = false; + break; + case OTHC_SVIDEO_OUT: + othc_accessories[i].enabled = true; + hsed_config_1.video_out_gpio = GPIO_HS_SW_DIR; + break; + } + } + } +} + + +static int pm8058_pwm_config(struct pwm_device *pwm, int ch, int on) +{ + struct pm_gpio pwm_gpio_config = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_CMOS, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_VPH, + .out_strength = PM_GPIO_STRENGTH_HIGH, + .function = PM_GPIO_FUNC_2, + }; + + int rc = -EINVAL; + int id, mode, max_mA; + + id = mode = max_mA = 0; + switch (ch) { + case 0: + case 1: + case 2: + if (on) { + id = 24 + ch; + rc = pm8xxx_gpio_config(PM8058_GPIO_PM_TO_SYS(id - 1), + &pwm_gpio_config); + if (rc) + pr_err("%s: pm8xxx_gpio_config(%d): rc=%d\n", + __func__, id, rc); + } + break; + + case 6: + id = PM_PWM_LED_FLASH; + mode = PM_PWM_CONF_PWM1; + max_mA = 300; + break; + + case 7: + id = PM_PWM_LED_FLASH1; + mode = PM_PWM_CONF_PWM1; + max_mA = 300; + break; + + default: + break; + } + + if (ch >= 6 && ch <= 7) { + if (!on) { + mode = PM_PWM_CONF_NONE; + max_mA = 0; + } + rc = pm8058_pwm_config_led(pwm, id, mode, max_mA); + if (rc) + pr_err("%s: pm8058_pwm_config_led(ch=%d): rc=%d\n", + __func__, ch, rc); + } + return rc; + +} + +static struct pm8058_pwm_pdata pm8058_pwm_data = { + .config = pm8058_pwm_config, +}; + +#define PM8058_GPIO_INT 88 + +static struct pmic8058_led pmic8058_flash_leds[] = { + [0] = { + .name = "camera:flash0", + .max_brightness = 15, + .id = PMIC8058_ID_FLASH_LED_0, + }, + [1] = { + .name = "camera:flash1", + .max_brightness = 15, + .id = PMIC8058_ID_FLASH_LED_1, + }, +}; + +static struct pmic8058_leds_platform_data pm8058_flash_leds_data = { + .num_leds = ARRAY_SIZE(pmic8058_flash_leds), + .leds = pmic8058_flash_leds, +}; + +static struct pmic8058_led pmic8058_dragon_leds[] = { + [0] = { + /* RED */ + .name = "led_drv0", + .max_brightness = 15, + .id = PMIC8058_ID_LED_0, + },/* 300 mA flash led0 drv sink */ + [1] = { + /* Yellow */ + .name = "led_drv1", + .max_brightness = 15, + .id = PMIC8058_ID_LED_1, + },/* 300 mA flash led0 drv sink */ + [2] = { + /* Green */ + .name = "led_drv2", + .max_brightness = 15, + .id = PMIC8058_ID_LED_2, + },/* 300 mA flash led0 drv sink */ + [3] = { + .name = "led_psensor", + .max_brightness = 15, + .id = PMIC8058_ID_LED_KB_LIGHT, + },/* 300 mA flash led0 drv sink */ +}; + +static struct pmic8058_leds_platform_data pm8058_dragon_leds_data = { + .num_leds = ARRAY_SIZE(pmic8058_dragon_leds), + .leds = pmic8058_dragon_leds, +}; + +static struct pmic8058_led pmic8058_fluid_flash_leds[] = { + [0] = { + .name = "led:drv0", + .max_brightness = 15, + .id = PMIC8058_ID_FLASH_LED_0, + },/* 300 mA flash led0 drv sink */ + [1] = { + .name = "led:drv1", + .max_brightness = 15, + .id = PMIC8058_ID_FLASH_LED_1, + },/* 300 mA flash led1 sink */ + [2] = { + .name = "led:drv2", + .max_brightness = 20, + .id = PMIC8058_ID_LED_0, + },/* 40 mA led0 sink */ + [3] = { + .name = "keypad:drv", + .max_brightness = 15, + .id = PMIC8058_ID_LED_KB_LIGHT, + },/* 300 mA keypad drv sink */ +}; + +static struct pmic8058_leds_platform_data pm8058_fluid_flash_leds_data = { + .num_leds = ARRAY_SIZE(pmic8058_fluid_flash_leds), + .leds = pmic8058_fluid_flash_leds, +}; + +static struct pmic8058_charger_data pmic8058_charger_dragon = { + .charger_data_valid = true, + .max_source_current = 1800, + .charger_type = CHG_TYPE_AC, +}; + +static struct pmic8058_charger_data pmic8058_charger_ffa_surf = { + .charger_data_valid = false, +}; + +static struct pm8xxx_misc_platform_data pm8058_misc_pdata = { + .priority = 0, +}; + +static struct pm8xxx_irq_platform_data pm8058_irq_pdata = { + .irq_base = PM8058_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(PM8058_GPIO_INT), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_gpio_platform_data pm8058_gpio_pdata = { + .gpio_base = PM8058_GPIO_PM_TO_SYS(0), +}; + +static struct pm8xxx_mpp_platform_data pm8058_mpp_pdata = { + .mpp_base = PM8058_MPP_PM_TO_SYS(0), +}; + +static struct pm8058_platform_data pm8058_platform_data = { + .irq_pdata = &pm8058_irq_pdata, + .gpio_pdata = &pm8058_gpio_pdata, + .mpp_pdata = &pm8058_mpp_pdata, + .rtc_pdata = &pm8058_rtc_pdata, + .pwrkey_pdata = &pm8058_pwrkey_pdata, + .othc0_pdata = &othc_config_pdata_0, + .othc1_pdata = &othc_config_pdata_1, + .othc2_pdata = &othc_config_pdata_2, + .pwm_pdata = &pm8058_pwm_data, + .misc_pdata = &pm8058_misc_pdata, +#ifdef CONFIG_SENSORS_MSM_ADC + .xoadc_pdata = &pm8058_xoadc_pdata, +#endif +}; + +#ifdef CONFIG_MSM_SSBI +static struct msm_ssbi_platform_data msm8x60_ssbi_pm8058_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = "pm8058-core", + .platform_data = &pm8058_platform_data, + }, +}; +#endif +#endif /* CONFIG_PMIC8058 */ + +#if defined(CONFIG_TOUCHDISC_VTD518_SHINETSU) || \ + defined(CONFIG_TOUCHDISC_VTD518_SHINETSU_MODULE) +#define TDISC_I2C_SLAVE_ADDR 0x67 +#define PMIC_GPIO_TDISC PM8058_GPIO_PM_TO_SYS(5) +#define TDISC_INT PM8058_GPIO_IRQ(PM8058_IRQ_BASE, 5) + +static const char *vregs_tdisc_name[] = { + "8058_l5", + "8058_s3", +}; + +static const int vregs_tdisc_val[] = { + 2850000,/* uV */ + 1800000, +}; +static struct regulator *vregs_tdisc[ARRAY_SIZE(vregs_tdisc_name)]; + +static int tdisc_shinetsu_setup(void) +{ + int rc, i; + + rc = gpio_request(PMIC_GPIO_TDISC, "tdisc_interrupt"); + if (rc) { + pr_err("%s: gpio_request failed for PMIC_GPIO_TDISC\n", + __func__); + return rc; + } + + rc = gpio_request(GPIO_JOYSTICK_EN, "tdisc_oe"); + if (rc) { + pr_err("%s: gpio_request failed for GPIO_JOYSTICK_EN\n", + __func__); + goto fail_gpio_oe; + } + + rc = gpio_direction_output(GPIO_JOYSTICK_EN, 1); + if (rc) { + pr_err("%s: gpio_direction_output failed for GPIO_JOYSTICK_EN\n", + __func__); + gpio_free(GPIO_JOYSTICK_EN); + goto fail_gpio_oe; + } + + for (i = 0; i < ARRAY_SIZE(vregs_tdisc_name); i++) { + vregs_tdisc[i] = regulator_get(NULL, vregs_tdisc_name[i]); + if (IS_ERR(vregs_tdisc[i])) { + printk(KERN_ERR "%s: regulator get %s failed (%ld)\n", + __func__, vregs_tdisc_name[i], + PTR_ERR(vregs_tdisc[i])); + rc = PTR_ERR(vregs_tdisc[i]); + goto vreg_get_fail; + } + + rc = regulator_set_voltage(vregs_tdisc[i], + vregs_tdisc_val[i], vregs_tdisc_val[i]); + if (rc) { + printk(KERN_ERR "%s: regulator_set_voltage() = %d\n", + __func__, rc); + goto vreg_set_voltage_fail; + } + } + + return rc; +vreg_set_voltage_fail: + i++; +vreg_get_fail: + while (i) + regulator_put(vregs_tdisc[--i]); +fail_gpio_oe: + gpio_free(PMIC_GPIO_TDISC); + return rc; +} + +static void tdisc_shinetsu_release(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vregs_tdisc_name); i++) + regulator_put(vregs_tdisc[i]); + + gpio_free(PMIC_GPIO_TDISC); + gpio_free(GPIO_JOYSTICK_EN); +} + +static int tdisc_shinetsu_enable(void) +{ + int i, rc = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(vregs_tdisc_name); i++) { + rc = regulator_enable(vregs_tdisc[i]); + if (rc < 0) { + printk(KERN_ERR "%s: vreg %s enable failed (%d)\n", + __func__, vregs_tdisc_name[i], rc); + goto vreg_fail; + } + } + + /* Enable the OE (output enable) gpio */ + gpio_set_value_cansleep(GPIO_JOYSTICK_EN, 1); + /* voltage and gpio stabilization delay */ + msleep(50); + + return 0; +vreg_fail: + while (i) + regulator_disable(vregs_tdisc[--i]); + return rc; +} + +static int tdisc_shinetsu_disable(void) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(vregs_tdisc_name); i++) { + rc = regulator_disable(vregs_tdisc[i]); + if (rc < 0) { + printk(KERN_ERR "%s: vreg %s disable failed (%d)\n", + __func__, vregs_tdisc_name[i], rc); + goto tdisc_reg_fail; + } + } + + /* Disable the OE (output enable) gpio */ + gpio_set_value_cansleep(GPIO_JOYSTICK_EN, 0); + + return 0; + +tdisc_reg_fail: + while (i) + regulator_enable(vregs_tdisc[--i]); + return rc; +} + +static struct tdisc_abs_values tdisc_abs = { + .x_max = 32, + .y_max = 32, + .x_min = -32, + .y_min = -32, + .pressure_max = 32, + .pressure_min = 0, +}; + +static struct tdisc_platform_data tdisc_data = { + .tdisc_setup = tdisc_shinetsu_setup, + .tdisc_release = tdisc_shinetsu_release, + .tdisc_enable = tdisc_shinetsu_enable, + .tdisc_disable = tdisc_shinetsu_disable, + .tdisc_wakeup = 0, + .tdisc_gpio = PMIC_GPIO_TDISC, + .tdisc_report_keys = true, + .tdisc_report_relative = true, + .tdisc_report_absolute = false, + .tdisc_report_wheel = false, + .tdisc_reverse_x = false, + .tdisc_reverse_y = true, + .tdisc_abs = &tdisc_abs, +}; + +static struct i2c_board_info msm_i2c_gsbi3_tdisc_info[] = { + { + I2C_BOARD_INFO("vtd518", TDISC_I2C_SLAVE_ADDR), + .irq = TDISC_INT, + .platform_data = &tdisc_data, + }, +}; +#endif + +#define PM_GPIO_CDC_RST_N 20 +#define GPIO_CDC_RST_N PM8058_GPIO_PM_TO_SYS(PM_GPIO_CDC_RST_N) + +static struct regulator *vreg_timpani_1; +static struct regulator *vreg_timpani_2; + +static unsigned int msm_timpani_setup_power(void) +{ + int rc; + + vreg_timpani_1 = regulator_get(NULL, "8058_l0"); + if (IS_ERR(vreg_timpani_1)) { + pr_err("%s: Unable to get 8058_l0\n", __func__); + return -ENODEV; + } + + vreg_timpani_2 = regulator_get(NULL, "8058_s3"); + if (IS_ERR(vreg_timpani_2)) { + pr_err("%s: Unable to get 8058_s3\n", __func__); + regulator_put(vreg_timpani_1); + return -ENODEV; + } + + rc = regulator_set_voltage(vreg_timpani_1, 1200000, 1200000); + if (rc) { + pr_err("%s: unable to set L0 voltage to 1.2V\n", __func__); + goto fail; + } + + rc = regulator_set_voltage(vreg_timpani_2, 1800000, 1800000); + if (rc) { + pr_err("%s: unable to set S3 voltage to 1.8V\n", __func__); + goto fail; + } + + rc = regulator_enable(vreg_timpani_1); + if (rc) { + pr_err("%s: Enable regulator 8058_l0 failed\n", __func__); + goto fail; + } + + /* The settings for LDO0 should be set such that + * it doesn't require to reset the timpani. */ + rc = regulator_set_optimum_mode(vreg_timpani_1, 5000); + if (rc < 0) { + pr_err("Timpani regulator optimum mode setting failed\n"); + goto fail; + } + + rc = regulator_enable(vreg_timpani_2); + if (rc) { + pr_err("%s: Enable regulator 8058_s3 failed\n", __func__); + regulator_disable(vreg_timpani_1); + goto fail; + } + + rc = gpio_request(GPIO_CDC_RST_N, "CDC_RST_N"); + if (rc) { + pr_err("%s: GPIO Request %d failed\n", __func__, + GPIO_CDC_RST_N); + regulator_disable(vreg_timpani_1); + regulator_disable(vreg_timpani_2); + goto fail; + } else { + gpio_direction_output(GPIO_CDC_RST_N, 1); + usleep_range(1000, 1050); + gpio_direction_output(GPIO_CDC_RST_N, 0); + usleep_range(1000, 1050); + gpio_direction_output(GPIO_CDC_RST_N, 1); + gpio_free(GPIO_CDC_RST_N); + } + return rc; + +fail: + regulator_put(vreg_timpani_1); + regulator_put(vreg_timpani_2); + return rc; +} + +static void msm_timpani_shutdown_power(void) +{ + int rc; + + rc = regulator_disable(vreg_timpani_1); + if (rc) + pr_err("%s: Disable regulator 8058_l0 failed\n", __func__); + + regulator_put(vreg_timpani_1); + + rc = regulator_disable(vreg_timpani_2); + if (rc) + pr_err("%s: Disable regulator 8058_s3 failed\n", __func__); + + regulator_put(vreg_timpani_2); +} + +/* Power analog function of codec */ +static struct regulator *vreg_timpani_cdc_apwr; +static int msm_timpani_codec_power(int vreg_on) +{ + int rc = 0; + + if (!vreg_timpani_cdc_apwr) { + + vreg_timpani_cdc_apwr = regulator_get(NULL, "8058_s4"); + + if (IS_ERR(vreg_timpani_cdc_apwr)) { + pr_err("%s: vreg_get failed (%ld)\n", + __func__, PTR_ERR(vreg_timpani_cdc_apwr)); + rc = PTR_ERR(vreg_timpani_cdc_apwr); + return rc; + } + } + + if (vreg_on) { + + rc = regulator_set_voltage(vreg_timpani_cdc_apwr, + 2200000, 2200000); + if (rc) { + pr_err("%s: unable to set 8058_s4 voltage to 2.2 V\n", + __func__); + goto vreg_fail; + } + + rc = regulator_enable(vreg_timpani_cdc_apwr); + if (rc) { + pr_err("%s: vreg_enable failed %d\n", __func__, rc); + goto vreg_fail; + } + } else { + rc = regulator_disable(vreg_timpani_cdc_apwr); + if (rc) { + pr_err("%s: vreg_disable failed %d\n", + __func__, rc); + goto vreg_fail; + } + } + + return 0; + +vreg_fail: + regulator_put(vreg_timpani_cdc_apwr); + vreg_timpani_cdc_apwr = NULL; + return rc; +} + +static struct marimba_codec_platform_data timpani_codec_pdata = { + .marimba_codec_power = msm_timpani_codec_power, +}; + +#define TIMPANI_SLAVE_ID_CDC_ADDR 0X77 +#define TIMPANI_SLAVE_ID_QMEMBIST_ADDR 0X66 + +static struct marimba_platform_data timpani_pdata = { + .slave_id[MARIMBA_SLAVE_ID_CDC] = TIMPANI_SLAVE_ID_CDC_ADDR, + .slave_id[MARIMBA_SLAVE_ID_QMEMBIST] = TIMPANI_SLAVE_ID_QMEMBIST_ADDR, + .marimba_setup = msm_timpani_setup_power, + .marimba_shutdown = msm_timpani_shutdown_power, + .codec = &timpani_codec_pdata, + .tsadc_ssbi_adap = MARIMBA_SSBI_ADAP, +}; + +#define TIMPANI_I2C_SLAVE_ADDR 0xD + +static struct i2c_board_info msm_i2c_gsbi7_timpani_info[] = { + { + I2C_BOARD_INFO("timpani", TIMPANI_I2C_SLAVE_ADDR), + .platform_data = &timpani_pdata, + }, +}; + +#ifdef CONFIG_SND_SOC_WM8903 +static struct wm8903_platform_data wm8903_pdata = { + .gpio_cfg[2] = 0x3A8, +}; + +#define WM8903_I2C_SLAVE_ADDR 0x34 +static struct i2c_board_info wm8903_codec_i2c_info[] = { + { + I2C_BOARD_INFO("wm8903", WM8903_I2C_SLAVE_ADDR >> 1), + .platform_data = &wm8903_pdata, + }, +}; +#endif +#ifdef CONFIG_PMIC8901 + +#define PM8901_GPIO_INT 91 +/* + * Consumer specific regulator names: + * regulator name consumer dev_name + */ +static struct regulator_consumer_supply vreg_consumers_8901_USB_OTG[] = { + REGULATOR_SUPPLY("8901_usb_otg", NULL), +}; +static struct regulator_consumer_supply vreg_consumers_8901_HDMI_MVS[] = { + REGULATOR_SUPPLY("8901_hdmi_mvs", NULL), +}; + +#define PM8901_VREG_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, \ + _always_on) \ + { \ + .init_data = { \ + .constraints = { \ + .valid_modes_mask = _modes, \ + .valid_ops_mask = _ops, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .input_uV = _min_uV, \ + .apply_uV = _apply_uV, \ + .always_on = _always_on, \ + }, \ + .consumer_supplies = vreg_consumers_8901_##_id, \ + .num_consumer_supplies = \ + ARRAY_SIZE(vreg_consumers_8901_##_id), \ + }, \ + .id = PM8901_VREG_ID_##_id, \ + } + +#define PM8901_VREG_INIT_VS(_id) \ + PM8901_VREG_INIT(_id, 0, 0, REGULATOR_MODE_NORMAL, \ + REGULATOR_CHANGE_STATUS, 0, 0) + +static struct pm8901_vreg_pdata pm8901_vreg_init[] = { + PM8901_VREG_INIT_VS(USB_OTG), + PM8901_VREG_INIT_VS(HDMI_MVS), +}; + +static struct pm8xxx_misc_platform_data pm8901_misc_pdata = { + .priority = 1, +}; + +static struct pm8xxx_irq_platform_data pm8901_irq_pdata = { + .irq_base = PM8901_IRQ_BASE, + .devirq = MSM_GPIO_TO_INT(PM8901_GPIO_INT), + .irq_trigger_flag = IRQF_TRIGGER_LOW, +}; + +static struct pm8xxx_mpp_platform_data pm8901_mpp_pdata = { + .mpp_base = PM8901_MPP_PM_TO_SYS(0), +}; + +static struct pm8901_platform_data pm8901_platform_data = { + .irq_pdata = &pm8901_irq_pdata, + .mpp_pdata = &pm8901_mpp_pdata, + .regulator_pdatas = pm8901_vreg_init, + .num_regulators = ARRAY_SIZE(pm8901_vreg_init), + .misc_pdata = &pm8901_misc_pdata, +}; + +static struct msm_ssbi_platform_data msm8x60_ssbi_pm8901_pdata __devinitdata = { + .controller_type = MSM_SBI_CTRL_PMIC_ARBITER, + .slave = { + .name = "pm8901-core", + .platform_data = &pm8901_platform_data, + }, +}; +#endif /* CONFIG_PMIC8901 */ + +#if defined(CONFIG_MARIMBA_CORE) && (defined(CONFIG_GPIO_SX150X) \ + || defined(CONFIG_GPIO_SX150X_MODULE)) + +static struct regulator *vreg_bahama; +static int msm_bahama_sys_rst = GPIO_MS_SYS_RESET_N; + +struct bahama_config_register{ + u8 reg; + u8 value; + u8 mask; +}; + +enum version{ + VER_1_0, + VER_2_0, + VER_UNSUPPORTED = 0xFF +}; + +static u8 read_bahama_ver(void) +{ + int rc; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA }; + u8 bahama_version; + + rc = marimba_read_bit_mask(&config, 0x00, &bahama_version, 1, 0x1F); + if (rc < 0) { + printk(KERN_ERR + "%s: version read failed: %d\n", + __func__, rc); + return VER_UNSUPPORTED; + } else { + printk(KERN_INFO + "%s: version read got: 0x%x\n", + __func__, bahama_version); + } + + switch (bahama_version) { + case 0x08: /* varient of bahama v1 */ + case 0x10: + case 0x00: + return VER_1_0; + case 0x09: /* variant of bahama v2 */ + return VER_2_0; + default: + return VER_UNSUPPORTED; + } +} + +static int msm_bahama_setup_power_enable; +static unsigned int msm_bahama_setup_power(void) +{ + int rc = 0; + const char *msm_bahama_regulator = "8058_s3"; + + if (machine_is_msm8x60_dragon()) + msm_bahama_sys_rst = GPIO_CDC_RST_N; + + vreg_bahama = regulator_get(NULL, msm_bahama_regulator); + + if (IS_ERR(vreg_bahama)) { + rc = PTR_ERR(vreg_bahama); + pr_err("%s: regulator_get %s = %d\n", __func__, + msm_bahama_regulator, rc); + return rc; + } + + rc = regulator_set_voltage(vreg_bahama, 1800000, 1800000); + if (rc) { + pr_err("%s: regulator_set_voltage %s = %d\n", __func__, + msm_bahama_regulator, rc); + goto unget; + } + + rc = regulator_enable(vreg_bahama); + if (rc) { + pr_err("%s: regulator_enable %s = %d\n", __func__, + msm_bahama_regulator, rc); + goto unget; + } + + rc = gpio_request(msm_bahama_sys_rst, "bahama sys_rst_n"); + if (rc) { + pr_err("%s: gpio_request %d = %d\n", __func__, + msm_bahama_sys_rst, rc); + goto unenable; + } + + gpio_direction_output(msm_bahama_sys_rst, 0); + usleep_range(1000, 1050); + gpio_set_value_cansleep(msm_bahama_sys_rst, 1); + usleep_range(1000, 1050); + msm_bahama_setup_power_enable = 1; + return rc; + +unenable: + regulator_disable(vreg_bahama); +unget: + regulator_put(vreg_bahama); + return rc; +}; + +static unsigned int msm_bahama_shutdown_power(int value) +{ + if (msm_bahama_setup_power_enable) { + gpio_set_value_cansleep(msm_bahama_sys_rst, 0); + gpio_free(msm_bahama_sys_rst); + regulator_disable(vreg_bahama); + regulator_put(vreg_bahama); + msm_bahama_setup_power_enable = 0; + } + + return 0; +}; + +static unsigned int msm_bahama_core_config(int type) +{ + int rc = 0; + + if (type == BAHAMA_ID) { + + int i; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA }; + + const struct bahama_config_register v20_init[] = { + /* reg, value, mask */ + { 0xF4, 0x84, 0xFF }, /* AREG */ + { 0xF0, 0x04, 0xFF } /* DREG */ + }; + + if (read_bahama_ver() == VER_2_0) { + for (i = 0; i < ARRAY_SIZE(v20_init); i++) { + u8 value = v20_init[i].value; + rc = marimba_write_bit_mask(&config, + v20_init[i].reg, + &value, + sizeof(v20_init[i].value), + v20_init[i].mask); + if (rc < 0) { + printk(KERN_ERR + "%s: reg %d write failed: %d\n", + __func__, v20_init[i].reg, rc); + return rc; + } + printk(KERN_INFO "%s: reg 0x%02x value 0x%02x" + " mask 0x%02x\n", + __func__, v20_init[i].reg, + v20_init[i].value, v20_init[i].mask); + } + } + } + printk(KERN_INFO "core type: %d\n", type); + + return rc; +} + +static struct regulator *fm_regulator_s3; +static struct msm_xo_voter *fm_clock; + +static int fm_radio_setup(struct marimba_fm_platform_data *pdata) +{ + int rc = 0; + struct pm_gpio cfg = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM8058_GPIO_VIN_S3, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 0, + }; + + if (!fm_regulator_s3) { + fm_regulator_s3 = regulator_get(NULL, "8058_s3"); + if (IS_ERR(fm_regulator_s3)) { + rc = PTR_ERR(fm_regulator_s3); + printk(KERN_ERR "%s: regulator get s3 (%d)\n", + __func__, rc); + goto out; + } + } + + + rc = regulator_set_voltage(fm_regulator_s3, 1800000, 1800000); + if (rc < 0) { + printk(KERN_ERR "%s: regulator set voltage failed (%d)\n", + __func__, rc); + goto fm_fail_put; + } + + rc = regulator_enable(fm_regulator_s3); + if (rc < 0) { + printk(KERN_ERR "%s: regulator s3 enable failed (%d)\n", + __func__, rc); + goto fm_fail_put; + } + + /*Vote for XO clock*/ + fm_clock = msm_xo_get(MSM_XO_TCXO_D0, "fm_power"); + + if (IS_ERR(fm_clock)) { + rc = PTR_ERR(fm_clock); + printk(KERN_ERR "%s: Couldn't get TCXO_D0 vote for FM (%d)\n", + __func__, rc); + goto fm_fail_switch; + } + + rc = msm_xo_mode_vote(fm_clock, MSM_XO_MODE_ON); + if (rc < 0) { + printk(KERN_ERR "%s: Failed to vote for TCX0_D0 ON (%d)\n", + __func__, rc); + goto fm_fail_vote; + } + + /*GPIO 18 on PMIC is FM_IRQ*/ + rc = pm8xxx_gpio_config(PM8058_GPIO_PM_TO_SYS(FM_GPIO), &cfg); + if (rc) { + printk(KERN_ERR "%s: return val of pm8xxx_gpio_config: %d\n", + __func__, rc); + goto fm_fail_clock; + } + goto out; + +fm_fail_clock: + msm_xo_mode_vote(fm_clock, MSM_XO_MODE_OFF); +fm_fail_vote: + msm_xo_put(fm_clock); +fm_fail_switch: + regulator_disable(fm_regulator_s3); +fm_fail_put: + regulator_put(fm_regulator_s3); +out: + return rc; +}; + +static void fm_radio_shutdown(struct marimba_fm_platform_data *pdata) +{ + int rc = 0; + if (fm_regulator_s3 != NULL) { + rc = regulator_disable(fm_regulator_s3); + if (rc < 0) { + printk(KERN_ERR "%s: regulator s3 disable (%d)\n", + __func__, rc); + } + regulator_put(fm_regulator_s3); + fm_regulator_s3 = NULL; + } + printk(KERN_ERR "%s: Voting off for XO", __func__); + + if (fm_clock != NULL) { + rc = msm_xo_mode_vote(fm_clock, MSM_XO_MODE_OFF); + if (rc < 0) { + printk(KERN_ERR "%s: Voting off XO clock (%d)\n", + __func__, rc); + } + msm_xo_put(fm_clock); + } + printk(KERN_ERR "%s: coming out of fm_radio_shutdown", __func__); +} + +/* Slave id address for FM/CDC/QMEMBIST + * Values can be programmed using Marimba slave id 0 + * should there be a conflict with other I2C devices + * */ +#define BAHAMA_SLAVE_ID_FM_ADDR 0x2A +#define BAHAMA_SLAVE_ID_QMEMBIST_ADDR 0x7B + +static struct marimba_fm_platform_data marimba_fm_pdata = { + .fm_setup = fm_radio_setup, + .fm_shutdown = fm_radio_shutdown, + .irq = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, FM_GPIO), + .is_fm_soc_i2s_master = false, + .config_i2s_gpio = NULL, +}; + +/* +Just initializing the BAHAMA related slave +*/ +static struct marimba_platform_data marimba_pdata = { + .slave_id[SLAVE_ID_BAHAMA_FM] = BAHAMA_SLAVE_ID_FM_ADDR, + .slave_id[SLAVE_ID_BAHAMA_QMEMBIST] = BAHAMA_SLAVE_ID_QMEMBIST_ADDR, + .bahama_setup = msm_bahama_setup_power, + .bahama_shutdown = msm_bahama_shutdown_power, + .bahama_core_config = msm_bahama_core_config, + .fm = &marimba_fm_pdata, + .tsadc_ssbi_adap = MARIMBA_SSBI_ADAP, +}; + + +static struct i2c_board_info msm_marimba_board_info[] = { + { + I2C_BOARD_INFO("marimba", 0xc), + .platform_data = &marimba_pdata, + } +}; +#endif /* CONFIG_MAIMBA_CORE */ + +#ifdef CONFIG_I2C +#define I2C_SURF 1 +#define I2C_FFA (1 << 1) +#define I2C_RUMI (1 << 2) +#define I2C_SIM (1 << 3) +#define I2C_FLUID (1 << 4) +#define I2C_DRAGON (1 << 5) + +struct i2c_registry { + u8 machs; + int bus; + struct i2c_board_info *info; + int len; +}; + +static struct i2c_registry msm8x60_i2c_devices[] __initdata = { +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) + { + I2C_SURF | I2C_FFA | I2C_DRAGON, + MSM_GSBI8_QUP_I2C_BUS_ID, + core_expander_i2c_info, + ARRAY_SIZE(core_expander_i2c_info), + }, + { + I2C_SURF | I2C_FFA | I2C_DRAGON, + MSM_GSBI8_QUP_I2C_BUS_ID, + docking_expander_i2c_info, + ARRAY_SIZE(docking_expander_i2c_info), + }, + { + I2C_SURF, + MSM_GSBI8_QUP_I2C_BUS_ID, + surf_expanders_i2c_info, + ARRAY_SIZE(surf_expanders_i2c_info), + }, + { + I2C_SURF | I2C_FFA | I2C_DRAGON, + MSM_GSBI3_QUP_I2C_BUS_ID, + fha_expanders_i2c_info, + ARRAY_SIZE(fha_expanders_i2c_info), + }, + { + I2C_FLUID, + MSM_GSBI3_QUP_I2C_BUS_ID, + fluid_expanders_i2c_info, + ARRAY_SIZE(fluid_expanders_i2c_info), + }, + { + I2C_FLUID, + MSM_GSBI8_QUP_I2C_BUS_ID, + fluid_core_expander_i2c_info, + ARRAY_SIZE(fluid_core_expander_i2c_info), + }, +#endif +#if defined(CONFIG_TOUCHDISC_VTD518_SHINETSU) || \ + defined(CONFIG_TOUCHDISC_VTD518_SHINETSU_MODULE) + { + I2C_SURF | I2C_FFA | I2C_FLUID | I2C_DRAGON, + MSM_GSBI3_QUP_I2C_BUS_ID, + msm_i2c_gsbi3_tdisc_info, + ARRAY_SIZE(msm_i2c_gsbi3_tdisc_info), + }, +#endif + { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_GSBI3_QUP_I2C_BUS_ID, + cy8ctmg200_board_info, + ARRAY_SIZE(cy8ctmg200_board_info), + }, + { + I2C_DRAGON, + MSM_GSBI3_QUP_I2C_BUS_ID, + cy8ctma340_dragon_board_info, + ARRAY_SIZE(cy8ctma340_dragon_board_info), + }, +#if defined(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC) || \ + defined(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC_MODULE) + { + I2C_FLUID, + MSM_GSBI3_QUP_I2C_BUS_ID, + cyttsp_fluid_info, + ARRAY_SIZE(cyttsp_fluid_info), + }, + { + I2C_FFA | I2C_SURF, + MSM_GSBI3_QUP_I2C_BUS_ID, + cyttsp_ffa_info, + ARRAY_SIZE(cyttsp_ffa_info), + }, +#endif +#ifdef CONFIG_MSM_CAMERA +#ifndef CONFIG_MSM_CAMERA_V4L2 + { + I2C_SURF | I2C_FFA | I2C_FLUID , + MSM_GSBI4_QUP_I2C_BUS_ID, + msm_camera_boardinfo, + ARRAY_SIZE(msm_camera_boardinfo), + }, + { + I2C_DRAGON, + MSM_GSBI4_QUP_I2C_BUS_ID, + msm_camera_dragon_boardinfo, + ARRAY_SIZE(msm_camera_dragon_boardinfo), + }, +#endif +#endif + { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_GSBI7_QUP_I2C_BUS_ID, + msm_i2c_gsbi7_timpani_info, + ARRAY_SIZE(msm_i2c_gsbi7_timpani_info), + }, +#if defined(CONFIG_MARIMBA_CORE) + { + I2C_SURF | I2C_FFA | I2C_FLUID | I2C_DRAGON, + MSM_GSBI7_QUP_I2C_BUS_ID, + msm_marimba_board_info, + ARRAY_SIZE(msm_marimba_board_info), + }, +#endif /* CONFIG_MARIMBA_CORE */ +#ifdef CONFIG_ISL9519_CHARGER + { + I2C_SURF | I2C_FFA, + MSM_GSBI8_QUP_I2C_BUS_ID, + isl_charger_i2c_info, + ARRAY_SIZE(isl_charger_i2c_info), + }, +#endif +#if defined(CONFIG_HAPTIC_ISA1200) || \ + defined(CONFIG_HAPTIC_ISA1200_MODULE) + { + I2C_FLUID, + MSM_GSBI8_QUP_I2C_BUS_ID, + msm_isa1200_board_info, + ARRAY_SIZE(msm_isa1200_board_info), + }, +#endif +#if defined(CONFIG_SMB137B_CHARGER) || defined(CONFIG_SMB137B_CHARGER_MODULE) + { + I2C_FLUID, + MSM_GSBI8_QUP_I2C_BUS_ID, + smb137b_charger_i2c_info, + ARRAY_SIZE(smb137b_charger_i2c_info), + }, +#endif +#if defined(CONFIG_BATTERY_BQ27520) || \ + defined(CONFIG_BATTERY_BQ27520_MODULE) + { + I2C_FLUID, + MSM_GSBI8_QUP_I2C_BUS_ID, + msm_bq27520_board_info, + ARRAY_SIZE(msm_bq27520_board_info), + }, +#endif +#if defined(CONFIG_SND_SOC_WM8903) || defined(CONFIG_SND_SOC_WM8903_MODULE) + { + I2C_DRAGON, + MSM_GSBI8_QUP_I2C_BUS_ID, + wm8903_codec_i2c_info, + ARRAY_SIZE(wm8903_codec_i2c_info), + }, +#endif +}; +#endif /* CONFIG_I2C */ + +static void __init fixup_i2c_configs(void) +{ +#ifdef CONFIG_I2C +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) + if (machine_is_msm8x60_surf() || machine_is_msm8x60_fusion()) + sx150x_data[SX150X_CORE].irq_summary = + PM8058_GPIO_IRQ(PM8058_IRQ_BASE, UI_INT2_N); + else if (machine_is_msm8x60_ffa() || machine_is_msm8x60_fusn_ffa() || + machine_is_msm8x60_dragon()) + sx150x_data[SX150X_CORE].irq_summary = + PM8058_GPIO_IRQ(PM8058_IRQ_BASE, UI_INT1_N); + else if (machine_is_msm8x60_fluid()) + sx150x_data[SX150X_CORE_FLUID].irq_summary = + PM8058_GPIO_IRQ(PM8058_IRQ_BASE, UI_INT1_N); +#endif +#endif +} + +static void __init register_i2c_devices(void) +{ +#ifdef CONFIG_I2C + u8 mach_mask = 0; + int i; +#ifdef CONFIG_MSM_CAMERA_V4L2 + struct i2c_registry msm8x60_camera_i2c_devices = { + I2C_SURF | I2C_FFA | I2C_FLUID, + MSM_GSBI4_QUP_I2C_BUS_ID, + msm8x60_camera_board_info.board_info, + msm8x60_camera_board_info.num_i2c_board_info, + }; +#endif + + /* Build the matching 'supported_machs' bitmask */ + if (machine_is_msm8x60_surf() || machine_is_msm8x60_fusion()) + mach_mask = I2C_SURF; + else if (machine_is_msm8x60_ffa() || machine_is_msm8x60_fusn_ffa()) + mach_mask = I2C_FFA; + else if (machine_is_msm8x60_rumi3()) + mach_mask = I2C_RUMI; + else if (machine_is_msm8x60_sim()) + mach_mask = I2C_SIM; + else if (machine_is_msm8x60_fluid()) + mach_mask = I2C_FLUID; + else if (machine_is_msm8x60_dragon()) + mach_mask = I2C_DRAGON; + else + pr_err("unmatched machine ID in register_i2c_devices\n"); + + /* Run the array and install devices as appropriate */ + for (i = 0; i < ARRAY_SIZE(msm8x60_i2c_devices); ++i) { + if (msm8x60_i2c_devices[i].machs & mach_mask) + i2c_register_board_info(msm8x60_i2c_devices[i].bus, + msm8x60_i2c_devices[i].info, + msm8x60_i2c_devices[i].len); + } +#ifdef CONFIG_MSM_CAMERA_V4L2 + if (msm8x60_camera_i2c_devices.machs & mach_mask) + i2c_register_board_info(msm8x60_camera_i2c_devices.bus, + msm8x60_camera_i2c_devices.info, + msm8x60_camera_i2c_devices.len); +#endif +#endif +} + +static void __init msm8x60_init_uart12dm(void) +{ +#if !defined(CONFIG_USB_PEHCI_HCD) && !defined(CONFIG_USB_PEHCI_HCD_MODULE) + /* 0x1D000000 now belongs to EBI2:CS3 i.e. USB ISP Controller */ + void *fpga_mem = ioremap_nocache(0x1D000000, SZ_4K); + + if (!fpga_mem) + pr_err("%s(): Error getting memory\n", __func__); + + /* Advanced mode */ + writew(0xFFFF, fpga_mem + 0x15C); + /* FPGA_UART_SEL */ + writew(0, fpga_mem + 0x172); + /* FPGA_GPIO_CONFIG_117 */ + writew(1, fpga_mem + 0xEA); + /* FPGA_GPIO_CONFIG_118 */ + writew(1, fpga_mem + 0xEC); + mb(); + iounmap(fpga_mem); +#endif +} + +#define MSM_GSBI9_PHYS 0x19900000 +#define GSBI_DUAL_MODE_CODE 0x60 + +static void __init msm8x60_init_buses(void) +{ +#ifdef CONFIG_I2C_QUP + void *gsbi_mem = ioremap_nocache(0x19C00000, 4); + /* Setting protocol code to 0x60 for dual UART/I2C in GSBI12 */ + writel_relaxed(0x6 << 4, gsbi_mem); + /* Ensure protocol code is written before proceeding further */ + mb(); + iounmap(gsbi_mem); + + msm_gsbi3_qup_i2c_device.dev.platform_data = &msm_gsbi3_qup_i2c_pdata; + msm_gsbi4_qup_i2c_device.dev.platform_data = &msm_gsbi4_qup_i2c_pdata; + msm_gsbi7_qup_i2c_device.dev.platform_data = &msm_gsbi7_qup_i2c_pdata; + msm_gsbi8_qup_i2c_device.dev.platform_data = &msm_gsbi8_qup_i2c_pdata; + +#ifdef CONFIG_MSM_GSBI9_UART + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { + /* Setting protocol code to 0x60 for dual UART/I2C in GSBI9 */ + gsbi_mem = ioremap_nocache(MSM_GSBI9_PHYS, 4); + writel_relaxed(GSBI_DUAL_MODE_CODE, gsbi_mem); + iounmap(gsbi_mem); + msm_gsbi9_qup_i2c_pdata.use_gsbi_shared_mode = 1; + } +#endif + msm_gsbi9_qup_i2c_device.dev.platform_data = &msm_gsbi9_qup_i2c_pdata; + msm_gsbi12_qup_i2c_device.dev.platform_data = &msm_gsbi12_qup_i2c_pdata; +#endif +#if defined(CONFIG_SPI_QUP) || defined(CONFIG_SPI_QUP_MODULE) + msm_gsbi1_qup_spi_device.dev.platform_data = &msm_gsbi1_qup_spi_pdata; +#endif +#ifdef CONFIG_I2C_SSBI + msm_device_ssbi3.dev.platform_data = &msm_ssbi3_pdata; +#endif + +#ifdef CONFIG_MSM_SSBI + msm_device_ssbi_pmic1.dev.platform_data = + &msm8x60_ssbi_pm8058_pdata; + msm_device_ssbi_pmic2.dev.platform_data = + &msm8x60_ssbi_pm8901_pdata; +#endif + + if (machine_is_msm8x60_fluid()) { +#if (defined(CONFIG_USB_EHCI_MSM_72K) && \ + (defined(CONFIG_SMB137B_CHARGER) || \ + defined(CONFIG_SMB137B_CHARGER_MODULE))) + msm_otg_pdata.vbus_power = msm_hsusb_smb137b_vbus_power; +#endif +#if defined(CONFIG_SPI_QUP) || defined(CONFIG_SPI_QUP_MODULE) + msm_gsbi10_qup_spi_device.dev.platform_data = + &msm_gsbi10_qup_spi_pdata; +#endif + } + +#if defined(CONFIG_USB_MSM_72K) || defined(CONFIG_USB_EHCI_HCD) + /* + * We can not put USB regulators (8058_l6 and 8058_l7) in LPM + * when we depend on USB PHY for VBUS/ID notifications. VBUS + * and ID notifications are available only on V2 surf and FFA + * with a hardware workaround. + */ + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2 && + (machine_is_msm8x60_surf() || + (machine_is_msm8x60_ffa() && + pmic_id_notif_supported))) + msm_otg_pdata.phy_can_powercollapse = 1; + msm_device_otg.dev.platform_data = &msm_otg_pdata; +#endif + +#ifdef CONFIG_USB_MSM_72K + msm_device_gadget_peripheral.dev.platform_data = &msm_gadget_pdata; +#endif + +#ifdef CONFIG_SERIAL_MSM_HS + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(54); /* GSBI6(2) */ + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +#endif +#ifdef CONFIG_MSM_GSBI9_UART + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { + msm_device_uart_gsbi9 = msm_add_gsbi9_uart(); + if (IS_ERR(msm_device_uart_gsbi9)) + pr_err("%s(): Failed to create uart gsbi9 device\n", + __func__); + } +#endif + +#ifdef CONFIG_MSM_BUS_SCALING + + /* RPM calls are only enabled on V2 */ + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) { + msm_bus_apps_fabric_pdata.rpm_enabled = 1; + msm_bus_sys_fabric_pdata.rpm_enabled = 1; + msm_bus_mm_fabric_pdata.rpm_enabled = 1; + msm_bus_sys_fpb_pdata.rpm_enabled = 1; + msm_bus_cpss_fpb_pdata.rpm_enabled = 1; + } + + msm_bus_apps_fabric.dev.platform_data = &msm_bus_apps_fabric_pdata; + msm_bus_sys_fabric.dev.platform_data = &msm_bus_sys_fabric_pdata; + msm_bus_mm_fabric.dev.platform_data = &msm_bus_mm_fabric_pdata; + msm_bus_sys_fpb.dev.platform_data = &msm_bus_sys_fpb_pdata; + msm_bus_cpss_fpb.dev.platform_data = &msm_bus_cpss_fpb_pdata; +#endif } static void __init msm8x60_map_io(void) { + msm_shared_ram_phys = MSM_SHARED_RAM_PHYS; msm_map_msm8x60_io(); + + if (socinfo_init() < 0) + pr_err("socinfo_init() failed!\n"); } -#ifdef CONFIG_OF -static struct of_device_id msm_dt_gic_match[] __initdata = { - { .compatible = "qcom,msm-8660-qgic", .data = gic_of_init }, - {} -}; -#endif - -static void __init msm8x60_init_irq(void) +/* + * Most segments of the EBI2 bus are disabled by default. + */ +static void __init msm8x60_init_ebi2(void) { - if (!of_have_populated_dt()) - gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, - (void *)MSM_QGIC_CPU_BASE); -#ifdef CONFIG_OF - else - of_irq_init(msm_dt_gic_match); -#endif + uint32_t ebi2_cfg; + void *ebi2_cfg_ptr; + struct clk *mem_clk = clk_get_sys("msm_ebi2", "mem_clk"); - /* Edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */ - writel(0xFFFFD7FF, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4); + if (IS_ERR(mem_clk)) { + pr_err("%s: clk_get_sys(%s,%s), failed", __func__, + "msm_ebi2", "mem_clk"); + return; + } + clk_prepare_enable(mem_clk); + clk_put(mem_clk); - /* RUMI does not adhere to GIC spec by enabling STIs by default. - * Enable/clear is supposed to be RO for STIs, but is RW on RUMI. - */ - if (!machine_is_msm8x60_sim()) - writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET); -} + ebi2_cfg_ptr = ioremap_nocache(0x1a100000, sizeof(uint32_t)); + if (ebi2_cfg_ptr != 0) { + ebi2_cfg = readl_relaxed(ebi2_cfg_ptr); -static void __init msm8x60_init(void) -{ -} + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_fluid() || + machine_is_msm8x60_dragon()) + ebi2_cfg |= (1 << 4) | (1 << 5); /* CS2, CS3 */ + else if (machine_is_msm8x60_sim()) + ebi2_cfg |= (1 << 4); /* CS2 */ + else if (machine_is_msm8x60_rumi3()) + ebi2_cfg |= (1 << 5); /* CS3 */ -#ifdef CONFIG_OF -static struct of_dev_auxdata msm_auxdata_lookup[] __initdata = { - {} -}; - -static void __init msm8x60_dt_init(void) -{ - if (of_machine_is_compatible("qcom,msm8660-surf")) { - printk(KERN_INFO "Init surf UART registers\n"); - msm8x60_init_uart12dm(); + writel_relaxed(ebi2_cfg, ebi2_cfg_ptr); + iounmap(ebi2_cfg_ptr); } - of_platform_populate(NULL, of_default_bus_match_table, - msm_auxdata_lookup, NULL); + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_fluid() || machine_is_msm8x60_dragon()) { + ebi2_cfg_ptr = ioremap_nocache(0x1a110000, SZ_4K); + if (ebi2_cfg_ptr != 0) { + /* EBI2_XMEM_CFG:PWRSAVE_MODE off */ + writel_relaxed(0UL, ebi2_cfg_ptr); + + /* CS2: Delay 9 cycles (140ns@64MHz) between SMSC + * LAN9221 Ethernet controller reads and writes. + * The lowest 4 bits are the read delay, the next + * 4 are the write delay. */ + writel_relaxed(0x031F1C99, ebi2_cfg_ptr + 0x10); +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + /* + * RECOVERY=5, HOLD_WR=1 + * INIT_LATENCY_WR=1, INIT_LATENCY_RD=1 + * WAIT_WR=1, WAIT_RD=2 + */ + writel_relaxed(0x51010112, ebi2_cfg_ptr + 0x14); + /* + * HOLD_RD=1 + * ADV_OE_RECOVERY=0, ADDR_HOLD_ENA=1 + */ + writel_relaxed(0x01000020, ebi2_cfg_ptr + 0x34); +#else + /* EBI2 CS3 muxed address/data, + * two cyc addr enable */ + writel_relaxed(0xA3030020, ebi2_cfg_ptr + 0x34); + +#endif + iounmap(ebi2_cfg_ptr); + } + } } -static const char *msm8x60_fluid_match[] __initdata = { - "qcom,msm8660-fluid", - "qcom,msm8660-surf", - NULL +static void __init msm8x60_configure_smc91x(void) +{ + if (machine_is_msm8x60_sim()) { + + smc91x_resources[0].start = 0x1b800300; + smc91x_resources[0].end = 0x1b8003ff; + + smc91x_resources[1].start = (NR_MSM_IRQS + 40); + smc91x_resources[1].end = (NR_MSM_IRQS + 40); + + } else if (machine_is_msm8x60_rumi3()) { + + smc91x_resources[0].start = 0x1d000300; + smc91x_resources[0].end = 0x1d0003ff; + + smc91x_resources[1].start = TLMM_MSM_DIR_CONN_IRQ_0; + smc91x_resources[1].end = TLMM_MSM_DIR_CONN_IRQ_0; + } +} + +static void __init msm8x60_init_tlmm(void) +{ + if (machine_is_msm8x60_rumi3()) + msm_gpio_install_direct_irq(0, 0, 1); +} + +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC5_SUPPORT)) + +/* 8x60 has 5 SDCC controllers */ +#define MAX_SDCC_CONTROLLER 5 + +struct msm_sdcc_gpio { + /* maximum 10 GPIOs per SDCC controller */ + s16 no; + /* name of this GPIO */ + const char *name; + bool always_on; + bool is_enabled; }; -#endif /* CONFIG_OF */ + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct msm_sdcc_gpio sdc1_gpio_cfg[] = { + {159, "sdc1_dat_0"}, + {160, "sdc1_dat_1"}, + {161, "sdc1_dat_2"}, + {162, "sdc1_dat_3"}, +#ifdef CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT + {163, "sdc1_dat_4"}, + {164, "sdc1_dat_5"}, + {165, "sdc1_dat_6"}, + {166, "sdc1_dat_7"}, +#endif + {167, "sdc1_clk"}, + {168, "sdc1_cmd"} +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct msm_sdcc_gpio sdc2_gpio_cfg[] = { + {143, "sdc2_dat_0"}, + {144, "sdc2_dat_1", 1}, + {145, "sdc2_dat_2"}, + {146, "sdc2_dat_3"}, +#ifdef CONFIG_MMC_MSM_SDC2_8_BIT_SUPPORT + {147, "sdc2_dat_4"}, + {148, "sdc2_dat_5"}, + {149, "sdc2_dat_6"}, + {150, "sdc2_dat_7"}, +#endif + {151, "sdc2_cmd"}, + {152, "sdc2_clk", 1} +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT +static struct msm_sdcc_gpio sdc5_gpio_cfg[] = { + {95, "sdc5_cmd"}, + {96, "sdc5_dat_3"}, + {97, "sdc5_clk", 1}, + {98, "sdc5_dat_2"}, + {99, "sdc5_dat_1", 1}, + {100, "sdc5_dat_0"} +}; +#endif + +struct msm_sdcc_pad_pull_cfg { + enum msm_tlmm_pull_tgt pull; + u32 pull_val; +}; + +struct msm_sdcc_pad_drv_cfg { + enum msm_tlmm_hdrive_tgt drv; + u32 drv_val; +}; + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct msm_sdcc_pad_drv_cfg sdc3_pad_on_drv_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_8MA} +}; + +static struct msm_sdcc_pad_pull_cfg sdc3_pad_on_pull_cfg[] = { + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_sdcc_pad_drv_cfg sdc3_pad_off_drv_cfg[] = { + {TLMM_HDRV_SDC3_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC3_DATA, GPIO_CFG_2MA} +}; + +static struct msm_sdcc_pad_pull_cfg sdc3_pad_off_pull_cfg[] = { + {TLMM_PULL_SDC3_CMD, GPIO_CFG_PULL_DOWN}, + {TLMM_PULL_SDC3_DATA, GPIO_CFG_PULL_DOWN} +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct msm_sdcc_pad_drv_cfg sdc4_pad_on_drv_cfg[] = { + {TLMM_HDRV_SDC4_CLK, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC4_CMD, GPIO_CFG_8MA}, + {TLMM_HDRV_SDC4_DATA, GPIO_CFG_8MA} +}; + +static struct msm_sdcc_pad_pull_cfg sdc4_pad_on_pull_cfg[] = { + {TLMM_PULL_SDC4_CMD, GPIO_CFG_PULL_UP}, + {TLMM_PULL_SDC4_DATA, GPIO_CFG_PULL_UP} +}; + +static struct msm_sdcc_pad_drv_cfg sdc4_pad_off_drv_cfg[] = { + {TLMM_HDRV_SDC4_CLK, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC4_CMD, GPIO_CFG_2MA}, + {TLMM_HDRV_SDC4_DATA, GPIO_CFG_2MA} +}; + +static struct msm_sdcc_pad_pull_cfg sdc4_pad_off_pull_cfg[] = { + {TLMM_PULL_SDC4_CMD, GPIO_CFG_PULL_DOWN}, + {TLMM_PULL_SDC4_DATA, GPIO_CFG_PULL_DOWN} +}; +#endif + +struct msm_sdcc_pin_cfg { + /* + * = 1 if controller pins are using gpios + * = 0 if controller has dedicated MSM pins + */ + u8 is_gpio; + u8 cfg_sts; + u8 gpio_data_size; + struct msm_sdcc_gpio *gpio_data; + struct msm_sdcc_pad_drv_cfg *pad_drv_on_data; + struct msm_sdcc_pad_drv_cfg *pad_drv_off_data; + struct msm_sdcc_pad_pull_cfg *pad_pull_on_data; + struct msm_sdcc_pad_pull_cfg *pad_pull_off_data; + u8 pad_drv_data_size; + u8 pad_pull_data_size; + u8 sdio_lpm_gpio_cfg; +}; + + +static struct msm_sdcc_pin_cfg sdcc_pin_cfg_data[MAX_SDCC_CONTROLLER] = { +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + [0] = { + .is_gpio = 1, + .gpio_data_size = ARRAY_SIZE(sdc1_gpio_cfg), + .gpio_data = sdc1_gpio_cfg + }, +#endif +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + [1] = { + .is_gpio = 1, + .gpio_data_size = ARRAY_SIZE(sdc2_gpio_cfg), + .gpio_data = sdc2_gpio_cfg + }, +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + [2] = { + .is_gpio = 0, + .pad_drv_on_data = sdc3_pad_on_drv_cfg, + .pad_drv_off_data = sdc3_pad_off_drv_cfg, + .pad_pull_on_data = sdc3_pad_on_pull_cfg, + .pad_pull_off_data = sdc3_pad_off_pull_cfg, + .pad_drv_data_size = ARRAY_SIZE(sdc3_pad_on_drv_cfg), + .pad_pull_data_size = ARRAY_SIZE(sdc3_pad_on_pull_cfg) + }, +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + [3] = { + .is_gpio = 0, + .pad_drv_on_data = sdc4_pad_on_drv_cfg, + .pad_drv_off_data = sdc4_pad_off_drv_cfg, + .pad_pull_on_data = sdc4_pad_on_pull_cfg, + .pad_pull_off_data = sdc4_pad_off_pull_cfg, + .pad_drv_data_size = ARRAY_SIZE(sdc4_pad_on_drv_cfg), + .pad_pull_data_size = ARRAY_SIZE(sdc4_pad_on_pull_cfg) + }, +#endif +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT + [4] = { + .is_gpio = 1, + .gpio_data_size = ARRAY_SIZE(sdc5_gpio_cfg), + .gpio_data = sdc5_gpio_cfg + } +#endif +}; + +static int msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int rc = 0; + struct msm_sdcc_pin_cfg *curr; + int n; + + curr = &sdcc_pin_cfg_data[dev_id - 1]; + if (!curr->gpio_data) + goto out; + + for (n = 0; n < curr->gpio_data_size; n++) { + if (enable) { + + if (curr->gpio_data[n].always_on && + curr->gpio_data[n].is_enabled) + continue; + pr_debug("%s: enable: %s\n", __func__, + curr->gpio_data[n].name); + rc = gpio_request(curr->gpio_data[n].no, + curr->gpio_data[n].name); + if (rc) { + pr_err("%s: gpio_request(%d, %s)" + "failed", __func__, + curr->gpio_data[n].no, + curr->gpio_data[n].name); + goto free_gpios; + } + /* set direction as output for all GPIOs */ + rc = gpio_direction_output( + curr->gpio_data[n].no, 1); + if (rc) { + pr_err("%s: gpio_direction_output" + "(%d, 1) failed\n", __func__, + curr->gpio_data[n].no); + goto free_gpios; + } + curr->gpio_data[n].is_enabled = 1; + } else { + /* + * now free this GPIO which will put GPIO + * in low power mode and will also put GPIO + * in input mode + */ + if (curr->gpio_data[n].always_on) + continue; + pr_debug("%s: disable: %s\n", __func__, + curr->gpio_data[n].name); + gpio_free(curr->gpio_data[n].no); + curr->gpio_data[n].is_enabled = 0; + } + } + curr->cfg_sts = enable; + goto out; + +free_gpios: + for (; n >= 0; n--) + gpio_free(curr->gpio_data[n].no); +out: + return rc; +} + +static int msm_sdcc_setup_pad(int dev_id, unsigned int enable) +{ + int rc = 0; + struct msm_sdcc_pin_cfg *curr; + int n; + + curr = &sdcc_pin_cfg_data[dev_id - 1]; + if (!curr->pad_drv_on_data || !curr->pad_pull_on_data) + goto out; + + if (enable) { + /* + * set up the normal driver strength and + * pull config for pads + */ + for (n = 0; n < curr->pad_drv_data_size; n++) { + if (curr->sdio_lpm_gpio_cfg) { + if (curr->pad_drv_on_data[n].drv == + TLMM_HDRV_SDC4_DATA) + continue; + } + msm_tlmm_set_hdrive(curr->pad_drv_on_data[n].drv, + curr->pad_drv_on_data[n].drv_val); + } + for (n = 0; n < curr->pad_pull_data_size; n++) { + if (curr->sdio_lpm_gpio_cfg) { + if (curr->pad_pull_on_data[n].pull == + TLMM_PULL_SDC4_DATA) + continue; + } + msm_tlmm_set_pull(curr->pad_pull_on_data[n].pull, + curr->pad_pull_on_data[n].pull_val); + } + } else { + /* set the low power config for pads */ + for (n = 0; n < curr->pad_drv_data_size; n++) { + if (curr->sdio_lpm_gpio_cfg) { + if (curr->pad_drv_off_data[n].drv == + TLMM_HDRV_SDC4_DATA) + continue; + } + msm_tlmm_set_hdrive( + curr->pad_drv_off_data[n].drv, + curr->pad_drv_off_data[n].drv_val); + } + for (n = 0; n < curr->pad_pull_data_size; n++) { + if (curr->sdio_lpm_gpio_cfg) { + if (curr->pad_pull_off_data[n].pull == + TLMM_PULL_SDC4_DATA) + continue; + } + msm_tlmm_set_pull( + curr->pad_pull_off_data[n].pull, + curr->pad_pull_off_data[n].pull_val); + } + } + curr->cfg_sts = enable; +out: + return rc; +} + +struct sdcc_reg { + /* VDD/VCC/VCCQ regulator name on PMIC8058/PMIC8089*/ + const char *reg_name; + /* + * is set voltage supported for this regulator? + * 0 = not supported, 1 = supported + */ + unsigned char set_voltage_sup; + /* voltage level to be set */ + unsigned int level; + /* VDD/VCC/VCCQ voltage regulator handle */ + struct regulator *reg; + /* is this regulator enabled? */ + bool enabled; + /* is this regulator needs to be always on? */ + bool always_on; + /* is operating power mode setting required for this regulator? */ + bool op_pwr_mode_sup; + /* Load values for low power and high power mode */ + unsigned int lpm_uA; + unsigned int hpm_uA; +}; +/* all SDCC controllers require VDD/VCC voltage */ +static struct sdcc_reg sdcc_vdd_reg_data[MAX_SDCC_CONTROLLER]; +/* only SDCC1 requires VCCQ voltage */ +static struct sdcc_reg sdcc_vccq_reg_data[1]; +/* all SDCC controllers may require voting for VDD PAD voltage */ +static struct sdcc_reg sdcc_vddp_reg_data[MAX_SDCC_CONTROLLER]; + +struct sdcc_reg_data { + struct sdcc_reg *vdd_data; /* keeps VDD/VCC regulator info */ + struct sdcc_reg *vccq_data; /* keeps VCCQ regulator info */ + struct sdcc_reg *vddp_data; /* keeps VDD Pad regulator info */ + unsigned char sts; /* regulator enable/disable status */ +}; +/* msm8x60 has 5 SDCC controllers */ +static struct sdcc_reg_data sdcc_vreg_data[MAX_SDCC_CONTROLLER]; + +static int msm_sdcc_vreg_init_reg(struct sdcc_reg *vreg) +{ + int rc = 0; + + /* Get the regulator handle */ + vreg->reg = regulator_get(NULL, vreg->reg_name); + if (IS_ERR(vreg->reg)) { + rc = PTR_ERR(vreg->reg); + pr_err("%s: regulator_get(%s) failed. rc=%d\n", + __func__, vreg->reg_name, rc); + goto out; + } + + /* Set the voltage level if required */ + if (vreg->set_voltage_sup) { + rc = regulator_set_voltage(vreg->reg, vreg->level, + vreg->level); + if (rc) { + pr_err("%s: regulator_set_voltage(%s) failed rc=%d\n", + __func__, vreg->reg_name, rc); + goto vreg_put; + } + } + goto out; + +vreg_put: + regulator_put(vreg->reg); +out: + return rc; +} + +static inline void msm_sdcc_vreg_deinit_reg(struct sdcc_reg *vreg) +{ + regulator_put(vreg->reg); +} + +/* this init function should be called only once for each SDCC */ +static int msm_sdcc_vreg_init(int dev_id, unsigned char init) +{ + int rc = 0; + struct sdcc_reg *curr_vdd_reg, *curr_vccq_reg, *curr_vddp_reg; + struct sdcc_reg_data *curr; + + curr = &sdcc_vreg_data[dev_id - 1]; + curr_vdd_reg = curr->vdd_data; + curr_vccq_reg = curr->vccq_data; + curr_vddp_reg = curr->vddp_data; + + if (init) { + /* + * get the regulator handle from voltage regulator framework + * and then try to set the voltage level for the regulator + */ + if (curr_vdd_reg) { + rc = msm_sdcc_vreg_init_reg(curr_vdd_reg); + if (rc) + goto out; + } + if (curr_vccq_reg) { + rc = msm_sdcc_vreg_init_reg(curr_vccq_reg); + if (rc) + goto vdd_reg_deinit; + } + if (curr_vddp_reg) { + rc = msm_sdcc_vreg_init_reg(curr_vddp_reg); + if (rc) + goto vccq_reg_deinit; + } + goto out; + } else + /* deregister with all regulators from regulator framework */ + goto vddp_reg_deinit; + +vddp_reg_deinit: + if (curr_vddp_reg) + msm_sdcc_vreg_deinit_reg(curr_vddp_reg); +vccq_reg_deinit: + if (curr_vccq_reg) + msm_sdcc_vreg_deinit_reg(curr_vccq_reg); +vdd_reg_deinit: + if (curr_vdd_reg) + msm_sdcc_vreg_deinit_reg(curr_vdd_reg); +out: + return rc; +} + +static int msm_sdcc_vreg_enable(struct sdcc_reg *vreg) +{ + int rc; + + if (!vreg->enabled) { + rc = regulator_enable(vreg->reg); + if (rc) { + pr_err("%s: regulator_enable(%s) failed. rc=%d\n", + __func__, vreg->reg_name, rc); + goto out; + } + vreg->enabled = 1; + } + + /* Put always_on regulator in HPM (high power mode) */ + if (vreg->always_on && vreg->op_pwr_mode_sup) { + rc = regulator_set_optimum_mode(vreg->reg, vreg->hpm_uA); + if (rc < 0) { + pr_err("%s: reg=%s: HPM setting failed" + " hpm_uA=%d, rc=%d\n", + __func__, vreg->reg_name, + vreg->hpm_uA, rc); + goto vreg_disable; + } + rc = 0; + } + goto out; + +vreg_disable: + regulator_disable(vreg->reg); + vreg->enabled = 0; +out: + return rc; +} + +static int msm_sdcc_vreg_disable(struct sdcc_reg *vreg) +{ + int rc; + + /* Never disable always_on regulator */ + if (!vreg->always_on) { + rc = regulator_disable(vreg->reg); + if (rc) { + pr_err("%s: regulator_disable(%s) failed. rc=%d\n", + __func__, vreg->reg_name, rc); + goto out; + } + vreg->enabled = 0; + } + + /* Put always_on regulator in LPM (low power mode) */ + if (vreg->always_on && vreg->op_pwr_mode_sup) { + rc = regulator_set_optimum_mode(vreg->reg, vreg->lpm_uA); + if (rc < 0) { + pr_err("%s: reg=%s: LPM setting failed" + " lpm_uA=%d, rc=%d\n", + __func__, + vreg->reg_name, + vreg->lpm_uA, rc); + goto out; + } + rc = 0; + } + +out: + return rc; +} + +static int msm_sdcc_setup_vreg(int dev_id, unsigned char enable) +{ + int rc = 0; + struct sdcc_reg *curr_vdd_reg, *curr_vccq_reg, *curr_vddp_reg; + struct sdcc_reg_data *curr; + + curr = &sdcc_vreg_data[dev_id - 1]; + curr_vdd_reg = curr->vdd_data; + curr_vccq_reg = curr->vccq_data; + curr_vddp_reg = curr->vddp_data; + + /* check if regulators are initialized or not? */ + if ((curr_vdd_reg && !curr_vdd_reg->reg) || + (curr_vccq_reg && !curr_vccq_reg->reg) || + (curr_vddp_reg && !curr_vddp_reg->reg)) { + /* initialize voltage regulators required for this SDCC */ + rc = msm_sdcc_vreg_init(dev_id, 1); + if (rc) { + pr_err("%s: regulator init failed = %d\n", + __func__, rc); + goto out; + } + } + + if (curr->sts == enable) + goto out; + + if (curr_vdd_reg) { + if (enable) + rc = msm_sdcc_vreg_enable(curr_vdd_reg); + else + rc = msm_sdcc_vreg_disable(curr_vdd_reg); + if (rc) + goto out; + } + + if (curr_vccq_reg) { + if (enable) + rc = msm_sdcc_vreg_enable(curr_vccq_reg); + else + rc = msm_sdcc_vreg_disable(curr_vccq_reg); + if (rc) + goto out; + } + + if (curr_vddp_reg) { + if (enable) + rc = msm_sdcc_vreg_enable(curr_vddp_reg); + else + rc = msm_sdcc_vreg_disable(curr_vddp_reg); + if (rc) + goto out; + } + curr->sts = enable; + +out: + return rc; +} + +static u32 msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + u32 rc_pin_cfg = 0; + u32 rc_vreg_cfg = 0; + u32 rc = 0; + struct platform_device *pdev; + struct msm_sdcc_pin_cfg *curr_pin_cfg; + + pdev = container_of(dv, struct platform_device, dev); + + /* setup gpio/pad */ + curr_pin_cfg = &sdcc_pin_cfg_data[pdev->id - 1]; + if (curr_pin_cfg->cfg_sts == !!vdd) + goto setup_vreg; + + if (curr_pin_cfg->is_gpio) + rc_pin_cfg = msm_sdcc_setup_gpio(pdev->id, !!vdd); + else + rc_pin_cfg = msm_sdcc_setup_pad(pdev->id, !!vdd); + +setup_vreg: + /* setup voltage regulators */ + rc_vreg_cfg = msm_sdcc_setup_vreg(pdev->id, !!vdd); + + if (rc_pin_cfg || rc_vreg_cfg) + rc = rc_pin_cfg ? rc_pin_cfg : rc_vreg_cfg; + + return rc; +} + +static void msm_sdcc_sdio_lpm_gpio(struct device *dv, unsigned int active) +{ + struct msm_sdcc_pin_cfg *curr_pin_cfg; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + /* setup gpio/pad */ + curr_pin_cfg = &sdcc_pin_cfg_data[pdev->id - 1]; + + if (curr_pin_cfg->cfg_sts == active) + return; + + curr_pin_cfg->sdio_lpm_gpio_cfg = 1; + if (curr_pin_cfg->is_gpio) + msm_sdcc_setup_gpio(pdev->id, active); + else + msm_sdcc_setup_pad(pdev->id, active); + curr_pin_cfg->sdio_lpm_gpio_cfg = 0; +} + +static int msm_sdc3_get_wpswitch(struct device *dev) +{ + struct platform_device *pdev; + int status; + pdev = container_of(dev, struct platform_device, dev); + + status = gpio_request(GPIO_SDC_WP, "SD_WP_Switch"); + if (status) { + pr_err("%s:Failed to request GPIO %d\n", + __func__, GPIO_SDC_WP); + } else { + status = gpio_direction_input(GPIO_SDC_WP); + if (!status) { + status = gpio_get_value_cansleep(GPIO_SDC_WP); + pr_info("%s: WP Status for Slot %d = %d\n", + __func__, pdev->id, status); + } + gpio_free(GPIO_SDC_WP); + } + return status; +} + +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT +int sdc5_register_status_notify(void (*callback)(int, void *), + void *dev_id) +{ + sdc5_status_notify_cb = callback; + sdc5_status_notify_cb_devid = dev_id; + return 0; +} +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +int sdc2_register_status_notify(void (*callback)(int, void *), + void *dev_id) +{ + sdc2_status_notify_cb = callback; + sdc2_status_notify_cb_devid = dev_id; + return 0; +} +#endif + +/* Interrupt handler for SDC2 and SDC5 detection + * This function uses dual-edge interrputs settings in order + * to get SDIO detection when the GPIO is rising and SDIO removal + * when the GPIO is falling */ +static irqreturn_t msm8x60_multi_sdio_slot_status_irq(int irq, void *dev_id) +{ + int status; + + if (!machine_is_msm8x60_fusion() && + !machine_is_msm8x60_fusn_ffa()) + return IRQ_NONE; + + status = gpio_get_value(MDM2AP_SYNC); + pr_info("%s: MDM2AP_SYNC Status = %d\n", + __func__, status); + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + if (sdc2_status_notify_cb) { + pr_info("%s: calling sdc2_status_notify_cb\n", __func__); + sdc2_status_notify_cb(status, + sdc2_status_notify_cb_devid); + } +#endif + +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT + if (sdc5_status_notify_cb) { + pr_info("%s: calling sdc5_status_notify_cb\n", __func__); + sdc5_status_notify_cb(status, + sdc5_status_notify_cb_devid); + } +#endif + return IRQ_HANDLED; +} + +static int msm8x60_multi_sdio_init(void) +{ + int ret, irq_num; + + if (!machine_is_msm8x60_fusion() && + !machine_is_msm8x60_fusn_ffa()) + return 0; + + ret = msm_gpiomux_get(MDM2AP_SYNC); + if (ret) { + pr_err("%s:Failed to request GPIO %d, ret=%d\n", + __func__, MDM2AP_SYNC, ret); + return ret; + } + + irq_num = gpio_to_irq(MDM2AP_SYNC); + + ret = request_irq(irq_num, + msm8x60_multi_sdio_slot_status_irq, + IRQ_TYPE_EDGE_BOTH, + "sdio_multidetection", NULL); + + if (ret) { + pr_err("%s:Failed to request irq, ret=%d\n", + __func__, ret); + return ret; + } + + return ret; +} + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION +static unsigned int msm8x60_sdcc_slot_status(struct device *dev) +{ + int status; + + status = gpio_request(PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC3_DET - 1) + , "SD_HW_Detect"); + if (status) { + pr_err("%s:Failed to request GPIO %d\n", __func__, + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC3_DET - 1)); + } else { + status = gpio_direction_input( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC3_DET - 1)); + if (!status) + status = !(gpio_get_value_cansleep( + PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC3_DET - 1))); + gpio_free(PM8058_GPIO_PM_TO_SYS(PMIC_GPIO_SDC3_DET - 1)); + } + return (unsigned int) status; +} +#endif +#endif +#endif + +#define MSM_MPM_PIN_SDC3_DAT1 21 +#define MSM_MPM_PIN_SDC4_DAT1 23 + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct mmc_platform_data msm8x60_sdc1_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +#ifdef CONFIG_MMC_MSM_SDC1_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .msmsdcc_fmin = 400000, + .msmsdcc_fmid = 24000000, + .msmsdcc_fmax = 48000000, + .nonremovable = 1, + .pclk_src_dfab = 1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct mmc_platform_data msm8x60_sdc2_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_165_195, + .translate_vdd = msm_sdcc_setup_power, + .sdio_lpm_gpio_setup = msm_sdcc_sdio_lpm_gpio, + .mmc_bus_width = MMC_CAP_8_BIT_DATA, + .msmsdcc_fmin = 400000, + .msmsdcc_fmid = 24000000, + .msmsdcc_fmax = 48000000, + .nonremovable = 0, + .pclk_src_dfab = 1, + .register_status_notify = sdc2_register_status_notify, +#ifdef CONFIG_MSM_SDIO_AL + .is_sdio_al_client = 1, +#endif + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data msm8x60_sdc3_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .wpswitch = msm_sdc3_get_wpswitch, +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + .status = msm8x60_sdcc_slot_status, + .status_irq = PM8058_GPIO_IRQ(PM8058_IRQ_BASE, + PMIC_GPIO_SDC3_DET - 1), + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +#endif + .msmsdcc_fmin = 400000, + .msmsdcc_fmid = 24000000, + .msmsdcc_fmax = 48000000, + .nonremovable = 0, + .pclk_src_dfab = 1, + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC3_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct mmc_platform_data msm8x60_sdc4_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 400000, + .msmsdcc_fmid = 24000000, + .msmsdcc_fmax = 48000000, + .nonremovable = 0, + .pclk_src_dfab = 1, + .mpm_sdiowakeup_int = MSM_MPM_PIN_SDC4_DAT1, + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT +static struct mmc_platform_data msm8x60_sdc5_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_165_195, + .translate_vdd = msm_sdcc_setup_power, + .sdio_lpm_gpio_setup = msm_sdcc_sdio_lpm_gpio, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .msmsdcc_fmin = 400000, + .msmsdcc_fmid = 24000000, + .msmsdcc_fmax = 48000000, + .nonremovable = 0, + .pclk_src_dfab = 1, + .register_status_notify = sdc5_register_status_notify, +#ifdef CONFIG_MSM_SDIO_AL + .is_sdio_al_client = 1, +#endif + .msm_bus_voting_data = &sps_to_ddr_bus_voting_data, +}; +#endif + +static void __init msm8x60_init_mmc(void) +{ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + /* SDCC1 : eMMC card connected */ + sdcc_vreg_data[0].vdd_data = &sdcc_vdd_reg_data[0]; + sdcc_vreg_data[0].vdd_data->reg_name = "8901_l5"; + sdcc_vreg_data[0].vdd_data->set_voltage_sup = 1; + sdcc_vreg_data[0].vdd_data->level = 2850000; + sdcc_vreg_data[0].vdd_data->always_on = 1; + sdcc_vreg_data[0].vdd_data->op_pwr_mode_sup = 1; + sdcc_vreg_data[0].vdd_data->lpm_uA = 9000; + sdcc_vreg_data[0].vdd_data->hpm_uA = 200000; + + sdcc_vreg_data[0].vccq_data = &sdcc_vccq_reg_data[0]; + sdcc_vreg_data[0].vccq_data->reg_name = "8901_lvs0"; + sdcc_vreg_data[0].vccq_data->set_voltage_sup = 0; + sdcc_vreg_data[0].vccq_data->always_on = 1; + + msm_add_sdcc(1, &msm8x60_sdc1_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + /* + * MDM SDIO client is connected to SDC2 on charm SURF/FFA + * and no card is connected on 8660 SURF/FFA/FLUID. + */ + sdcc_vreg_data[1].vdd_data = &sdcc_vdd_reg_data[1]; + sdcc_vreg_data[1].vdd_data->reg_name = "8058_s3"; + sdcc_vreg_data[1].vdd_data->set_voltage_sup = 1; + sdcc_vreg_data[1].vdd_data->level = 1800000; + + sdcc_vreg_data[1].vccq_data = NULL; + + if (machine_is_msm8x60_fusion()) + msm8x60_sdc2_data.msmsdcc_fmax = 24000000; + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { + msm8x60_sdc2_data.sdiowakeup_irq = gpio_to_irq(144); + msm_sdcc_setup_gpio(2, 1); + msm_add_sdcc(2, &msm8x60_sdc2_data); + } +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + /* SDCC3 : External card slot connected */ + sdcc_vreg_data[2].vdd_data = &sdcc_vdd_reg_data[2]; + sdcc_vreg_data[2].vdd_data->reg_name = "8058_l14"; + sdcc_vreg_data[2].vdd_data->set_voltage_sup = 1; + sdcc_vreg_data[2].vdd_data->level = 2850000; + sdcc_vreg_data[2].vdd_data->always_on = 1; + sdcc_vreg_data[2].vdd_data->op_pwr_mode_sup = 1; + sdcc_vreg_data[2].vdd_data->lpm_uA = 9000; + sdcc_vreg_data[2].vdd_data->hpm_uA = 200000; + + sdcc_vreg_data[2].vccq_data = NULL; + + sdcc_vreg_data[2].vddp_data = &sdcc_vddp_reg_data[2]; + sdcc_vreg_data[2].vddp_data->reg_name = "8058_l5"; + sdcc_vreg_data[2].vddp_data->set_voltage_sup = 1; + sdcc_vreg_data[2].vddp_data->level = 2850000; + sdcc_vreg_data[2].vddp_data->always_on = 1; + sdcc_vreg_data[2].vddp_data->op_pwr_mode_sup = 1; + /* Sleep current required is ~300 uA. But min. RPM + * vote can be in terms of mA (min. 1 mA). + * So let's vote for 2 mA during sleep. + */ + sdcc_vreg_data[2].vddp_data->lpm_uA = 2000; + /* Max. Active current required is 16 mA */ + sdcc_vreg_data[2].vddp_data->hpm_uA = 16000; + + if (machine_is_msm8x60_fluid()) + msm8x60_sdc3_data.wpswitch = NULL; + msm_add_sdcc(3, &msm8x60_sdc3_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + /* SDCC4 : WLAN WCN1314 chip is connected */ + sdcc_vreg_data[3].vdd_data = &sdcc_vdd_reg_data[3]; + sdcc_vreg_data[3].vdd_data->reg_name = "8058_s3"; + sdcc_vreg_data[3].vdd_data->set_voltage_sup = 1; + sdcc_vreg_data[3].vdd_data->level = 1800000; + + sdcc_vreg_data[3].vccq_data = NULL; + + msm_add_sdcc(4, &msm8x60_sdc4_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC5_SUPPORT + /* + * MDM SDIO client is connected to SDC5 on charm SURF/FFA + * and no card is connected on 8660 SURF/FFA/FLUID. + */ + sdcc_vreg_data[4].vdd_data = &sdcc_vdd_reg_data[4]; + sdcc_vreg_data[4].vdd_data->reg_name = "8058_s3"; + sdcc_vreg_data[4].vdd_data->set_voltage_sup = 1; + sdcc_vreg_data[4].vdd_data->level = 1800000; + + sdcc_vreg_data[4].vccq_data = NULL; + + if (machine_is_msm8x60_fusion()) + msm8x60_sdc5_data.msmsdcc_fmax = 24000000; + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { + msm8x60_sdc5_data.sdiowakeup_irq = gpio_to_irq(99); + msm_sdcc_setup_gpio(5, 1); + msm_add_sdcc(5, &msm8x60_sdc5_data); + } +#endif +} + +#if !defined(CONFIG_GPIO_SX150X) && !defined(CONFIG_GPIO_SX150X_MODULE) +static inline void display_common_power(int on) {} +#else + +#define _GET_REGULATOR(var, name) do { \ + if (var == NULL) { \ + var = regulator_get(NULL, name); \ + if (IS_ERR(var)) { \ + pr_err("'%s' regulator not found, rc=%ld\n", \ + name, PTR_ERR(var)); \ + var = NULL; \ + } \ + } \ +} while (0) + +static int dsub_regulator(int on) +{ + static struct regulator *dsub_reg; + static struct regulator *mpp0_reg; + static int dsub_reg_enabled; + int rc = 0; + + _GET_REGULATOR(dsub_reg, "8901_l3"); + if (IS_ERR(dsub_reg)) { + printk(KERN_ERR "%s: failed to get reg 8901_l3 err=%ld", + __func__, PTR_ERR(dsub_reg)); + return PTR_ERR(dsub_reg); + } + + _GET_REGULATOR(mpp0_reg, "8901_mpp0"); + if (IS_ERR(mpp0_reg)) { + printk(KERN_ERR "%s: failed to get reg 8901_mpp0 err=%ld", + __func__, PTR_ERR(mpp0_reg)); + return PTR_ERR(mpp0_reg); + } + + if (on && !dsub_reg_enabled) { + rc = regulator_set_voltage(dsub_reg, 3300000, 3300000); + if (rc) { + printk(KERN_ERR "%s: failed to set reg 8901_l3 voltage" + " err=%d", __func__, rc); + goto dsub_regulator_err; + } + rc = regulator_enable(dsub_reg); + if (rc) { + printk(KERN_ERR "%s: failed to enable reg 8901_l3" + " err=%d", __func__, rc); + goto dsub_regulator_err; + } + rc = regulator_enable(mpp0_reg); + if (rc) { + printk(KERN_ERR "%s: failed to enable reg 8901_mpp0" + " err=%d", __func__, rc); + goto dsub_regulator_err; + } + dsub_reg_enabled = 1; + } else if (!on && dsub_reg_enabled) { + rc = regulator_disable(dsub_reg); + if (rc) + printk(KERN_WARNING "%s: failed to disable reg 8901_l3" + " err=%d", __func__, rc); + rc = regulator_disable(mpp0_reg); + if (rc) + printk(KERN_WARNING "%s: failed to disable reg " + "8901_mpp0 err=%d", __func__, rc); + dsub_reg_enabled = 0; + } + + return rc; + +dsub_regulator_err: + regulator_put(mpp0_reg); + regulator_put(dsub_reg); + return rc; +} + +static int display_power_on; +static void setup_display_power(void) +{ + if (display_power_on) + if (lcdc_vga_enabled) { + dsub_regulator(1); + gpio_set_value_cansleep(GPIO_LVDS_SHUTDOWN_N, 0); + gpio_set_value_cansleep(GPIO_BACKLIGHT_EN, 0); + if (machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusn_ffa()) + gpio_set_value_cansleep(GPIO_DONGLE_PWR_EN, 1); + } else { + dsub_regulator(0); + gpio_set_value_cansleep(GPIO_LVDS_SHUTDOWN_N, 1); + gpio_set_value_cansleep(GPIO_BACKLIGHT_EN, 1); + if (machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusn_ffa()) + gpio_set_value_cansleep(GPIO_DONGLE_PWR_EN, 0); + } + else { + dsub_regulator(0); + if (machine_is_msm8x60_ffa() || machine_is_msm8x60_fusn_ffa()) + gpio_set_value_cansleep(GPIO_DONGLE_PWR_EN, 0); + /* BACKLIGHT */ + gpio_set_value_cansleep(GPIO_BACKLIGHT_EN, 0); + /* LVDS */ + gpio_set_value_cansleep(GPIO_LVDS_SHUTDOWN_N, 0); + } +} + +#define _GET_REGULATOR(var, name) do { \ + if (var == NULL) { \ + var = regulator_get(NULL, name); \ + if (IS_ERR(var)) { \ + pr_err("'%s' regulator not found, rc=%ld\n", \ + name, PTR_ERR(var)); \ + var = NULL; \ + } \ + } \ +} while (0) + +#define GPIO_RESX_N (GPIO_EXPANDER_GPIO_BASE + 2) + +static void display_common_power(int on) +{ + int rc; + static struct regulator *display_reg; + + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) { + if (on) { + /* LVDS */ + _GET_REGULATOR(display_reg, "8901_l2"); + if (!display_reg) + return; + rc = regulator_set_voltage(display_reg, + 3300000, 3300000); + if (rc) + goto out; + rc = regulator_enable(display_reg); + if (rc) + goto out; + rc = gpio_request(GPIO_LVDS_SHUTDOWN_N, + "LVDS_STDN_OUT_N"); + if (rc) { + printk(KERN_ERR "%s: LVDS gpio %d request" + "failed\n", __func__, + GPIO_LVDS_SHUTDOWN_N); + goto out2; + } + + /* BACKLIGHT */ + rc = gpio_request(GPIO_BACKLIGHT_EN, "BACKLIGHT_EN"); + if (rc) { + printk(KERN_ERR "%s: BACKLIGHT gpio %d request" + "failed\n", __func__, + GPIO_BACKLIGHT_EN); + goto out3; + } + + if (machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusn_ffa()) { + rc = gpio_request(GPIO_DONGLE_PWR_EN, + "DONGLE_PWR_EN"); + if (rc) { + printk(KERN_ERR "%s: DONGLE_PWR_EN gpio" + " %d request failed\n", __func__, + GPIO_DONGLE_PWR_EN); + goto out4; + } + } + + gpio_direction_output(GPIO_LVDS_SHUTDOWN_N, 0); + gpio_direction_output(GPIO_BACKLIGHT_EN, 0); + if (machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusn_ffa()) + gpio_direction_output(GPIO_DONGLE_PWR_EN, 0); + mdelay(20); + display_power_on = 1; + setup_display_power(); + } else { + if (display_power_on) { + display_power_on = 0; + setup_display_power(); + mdelay(20); + if (machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusn_ffa()) + gpio_free(GPIO_DONGLE_PWR_EN); + goto out4; + } + } + } +#if defined(CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT) || \ + defined(CONFIG_FB_MSM_LCDC_AUO_WVGA) + else if (machine_is_msm8x60_fluid()) { + static struct regulator *fluid_reg; + static struct regulator *fluid_reg2; + + if (on) { + _GET_REGULATOR(fluid_reg, "8901_l2"); + if (!fluid_reg) + return; + _GET_REGULATOR(fluid_reg2, "8058_s3"); + if (!fluid_reg2) { + regulator_put(fluid_reg); + return; + } + rc = gpio_request(GPIO_RESX_N, "RESX_N"); + if (rc) { + regulator_put(fluid_reg2); + regulator_put(fluid_reg); + return; + } + regulator_set_voltage(fluid_reg, 2850000, 2850000); + regulator_set_voltage(fluid_reg2, 1800000, 1800000); + regulator_enable(fluid_reg); + regulator_enable(fluid_reg2); + msleep(20); + gpio_direction_output(GPIO_RESX_N, 0); + udelay(10); + gpio_set_value_cansleep(GPIO_RESX_N, 1); + display_power_on = 1; + setup_display_power(); + } else { + gpio_set_value_cansleep(GPIO_RESX_N, 0); + gpio_free(GPIO_RESX_N); + msleep(20); + regulator_disable(fluid_reg2); + regulator_disable(fluid_reg); + regulator_put(fluid_reg2); + regulator_put(fluid_reg); + display_power_on = 0; + setup_display_power(); + fluid_reg = NULL; + fluid_reg2 = NULL; + } + } +#endif +#if defined(CONFIG_FB_MSM_LCDC_NT35582_WVGA) + else if (machine_is_msm8x60_dragon()) { + static struct regulator *dragon_reg; + static struct regulator *dragon_reg2; + + if (on) { + _GET_REGULATOR(dragon_reg, "8901_l2"); + if (!dragon_reg) + return; + _GET_REGULATOR(dragon_reg2, "8058_l16"); + if (!dragon_reg2) { + regulator_put(dragon_reg); + dragon_reg = NULL; + return; + } + + rc = gpio_request(GPIO_NT35582_BL_EN, "lcdc_bl_en"); + if (rc) { + pr_err("%s: gpio %d request failed with rc=%d\n", + __func__, GPIO_NT35582_BL_EN, rc); + regulator_put(dragon_reg); + regulator_put(dragon_reg2); + dragon_reg = NULL; + dragon_reg2 = NULL; + return; + } + + if (gpio_tlmm_config(GPIO_CFG(GPIO_NT35582_RESET, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, + GPIO_CFG_16MA), GPIO_CFG_ENABLE)) { + pr_err("%s: config gpio '%d' failed!\n", + __func__, GPIO_NT35582_RESET); + gpio_free(GPIO_NT35582_BL_EN); + regulator_put(dragon_reg); + regulator_put(dragon_reg2); + dragon_reg = NULL; + dragon_reg2 = NULL; + return; + } + + rc = gpio_request(GPIO_NT35582_RESET, "lcdc_reset"); + if (rc) { + pr_err("%s: unable to request gpio %d (rc=%d)\n", + __func__, GPIO_NT35582_RESET, rc); + gpio_free(GPIO_NT35582_BL_EN); + regulator_put(dragon_reg); + regulator_put(dragon_reg2); + dragon_reg = NULL; + dragon_reg2 = NULL; + return; + } + + regulator_set_voltage(dragon_reg, 3300000, 3300000); + regulator_set_voltage(dragon_reg2, 1800000, 1800000); + regulator_enable(dragon_reg); + regulator_enable(dragon_reg2); + msleep(20); + + gpio_set_value_cansleep(GPIO_NT35582_RESET, 1); + msleep(20); + gpio_set_value_cansleep(GPIO_NT35582_RESET, 0); + msleep(20); + gpio_set_value_cansleep(GPIO_NT35582_RESET, 1); + msleep(50); + + gpio_set_value_cansleep(GPIO_NT35582_BL_EN, 1); + + display_power_on = 1; + } else if ((dragon_reg != NULL) && (dragon_reg2 != NULL)) { + gpio_free(GPIO_NT35582_RESET); + gpio_free(GPIO_NT35582_BL_EN); + regulator_disable(dragon_reg2); + regulator_disable(dragon_reg); + regulator_put(dragon_reg2); + regulator_put(dragon_reg); + display_power_on = 0; + dragon_reg = NULL; + dragon_reg2 = NULL; + } + } +#endif + return; + +out4: + gpio_free(GPIO_BACKLIGHT_EN); +out3: + gpio_free(GPIO_LVDS_SHUTDOWN_N); +out2: + regulator_disable(display_reg); +out: + regulator_put(display_reg); + display_reg = NULL; +} +#undef _GET_REGULATOR +#endif + +static int mipi_dsi_panel_power(int on); + +#define LCDC_NUM_GPIO 28 +#define LCDC_GPIO_START 0 + +static void lcdc_samsung_panel_power(int on) +{ + int n, ret = 0; + + display_common_power(on); + + for (n = 0; n < LCDC_NUM_GPIO; n++) { + if (on) { + ret = gpio_request(LCDC_GPIO_START + n, "LCDC_GPIO"); + if (unlikely(ret)) { + pr_err("%s not able to get gpio\n", __func__); + break; + } + } else + gpio_free(LCDC_GPIO_START + n); + } + + if (ret) { + for (n--; n >= 0; n--) + gpio_free(LCDC_GPIO_START + n); + } + + mipi_dsi_panel_power(0); /* set 8058_ldo0 to LPM */ +} + +#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL +#define _GET_REGULATOR(var, name) do { \ + var = regulator_get(NULL, name); \ + if (IS_ERR(var)) { \ + pr_err("'%s' regulator not found, rc=%ld\n", \ + name, IS_ERR(var)); \ + var = NULL; \ + return -ENODEV; \ + } \ +} while (0) + +static int hdmi_enable_5v(int on) +{ + static struct regulator *reg_8901_hdmi_mvs; /* HDMI_5V */ + static struct regulator *reg_8901_mpp0; /* External 5V */ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_8901_hdmi_mvs) + _GET_REGULATOR(reg_8901_hdmi_mvs, "8901_hdmi_mvs"); + if (!reg_8901_mpp0) + _GET_REGULATOR(reg_8901_mpp0, "8901_mpp0"); + + if (on) { + rc = regulator_enable(reg_8901_mpp0); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "reg_8901_mpp0", rc); + return rc; + } + rc = regulator_enable(reg_8901_hdmi_mvs); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "8901_hdmi_mvs", rc); + return rc; + } + pr_info("%s(on): success\n", __func__); + } else { + rc = regulator_disable(reg_8901_hdmi_mvs); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "8901_hdmi_mvs", rc); + rc = regulator_disable(reg_8901_mpp0); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "reg_8901_mpp0", rc); + pr_info("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +} + +static int hdmi_core_power(int on, int show) +{ + static struct regulator *reg_8058_l16; /* VDD_HDMI */ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_8058_l16) + _GET_REGULATOR(reg_8058_l16, "8058_l16"); + + if (on) { + rc = regulator_set_voltage(reg_8058_l16, 1800000, 1800000); + if (!rc) + rc = regulator_enable(reg_8058_l16); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "8058_l16", rc); + return rc; + } + rc = gpio_request(170, "HDMI_DDC_CLK"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_CLK", 170, rc); + goto error1; + } + rc = gpio_request(171, "HDMI_DDC_DATA"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_DDC_DATA", 171, rc); + goto error2; + } + rc = gpio_request(172, "HDMI_HPD"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_HPD", 172, rc); + goto error3; + } + pr_info("%s(on): success\n", __func__); + } else { + gpio_free(170); + gpio_free(171); + gpio_free(172); + rc = regulator_disable(reg_8058_l16); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "8058_l16", rc); + pr_info("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; + +error3: + gpio_free(171); +error2: + gpio_free(170); +error1: + regulator_disable(reg_8058_l16); + return rc; +} + +static int hdmi_cec_power(int on) +{ + static struct regulator *reg_8901_l3; /* HDMI_CEC */ + static int prev_on; + int rc; + + if (on == prev_on) + return 0; + + if (!reg_8901_l3) + _GET_REGULATOR(reg_8901_l3, "8901_l3"); + + if (on) { + rc = regulator_set_voltage(reg_8901_l3, 3300000, 3300000); + if (!rc) + rc = regulator_enable(reg_8901_l3); + if (rc) { + pr_err("'%s' regulator enable failed, rc=%d\n", + "8901_l3", rc); + return rc; + } + rc = gpio_request(169, "HDMI_CEC_VAR"); + if (rc) { + pr_err("'%s'(%d) gpio_request failed, rc=%d\n", + "HDMI_CEC_VAR", 169, rc); + goto error; + } + pr_info("%s(on): success\n", __func__); + } else { + gpio_free(169); + rc = regulator_disable(reg_8901_l3); + if (rc) + pr_warning("'%s' regulator disable failed, rc=%d\n", + "8901_l3", rc); + pr_info("%s(off): success\n", __func__); + } + + prev_on = on; + + return 0; +error: + regulator_disable(reg_8901_l3); + return rc; +} + +#undef _GET_REGULATOR + +#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL */ + +static int lcdc_panel_power(int on) +{ + int flag_on = !!on; + static int lcdc_power_save_on; + + if (lcdc_power_save_on == flag_on) + return 0; + + lcdc_power_save_on = flag_on; + + lcdc_samsung_panel_power(on); + + return 0; +} + +#ifdef CONFIG_MSM_BUS_SCALING + +static struct msm_bus_vectors rotator_init_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors rotator_ui_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (1024 * 600 * 4 * 2 * 60), + .ib = (1024 * 600 * 4 * 2 * 60 * 1.5), + }, +}; + +static struct msm_bus_vectors rotator_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_SMI, + .ab = (640 * 480 * 2 * 2 * 30), + .ib = (640 * 480 * 2 * 2 * 30 * 1.5), + }, + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (640 * 480 * 2 * 2 * 30), + .ib = (640 * 480 * 2 * 2 * 30 * 1.5), + }, +}; + +static struct msm_bus_vectors rotator_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_SMI, + .ab = (1280 * 736 * 2 * 2 * 30), + .ib = (1280 * 736 * 2 * 2 * 30 * 1.5), + }, + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (1280 * 736 * 2 * 2 * 30), + .ib = (1280 * 736 * 2 * 2 * 30 * 1.5), + }, +}; + +static struct msm_bus_vectors rotator_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_SMI, + .ab = (1920 * 1088 * 2 * 2 * 30), + .ib = (1920 * 1088 * 2 * 2 * 30 * 1.5), + }, + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (1920 * 1088 * 2 * 2 * 30), + .ib = (1920 * 1088 * 2 * 2 * 30 * 1.5), + }, +}; + +static struct msm_bus_paths rotator_bus_scale_usecases[] = { + { + ARRAY_SIZE(rotator_init_vectors), + rotator_init_vectors, + }, + { + ARRAY_SIZE(rotator_ui_vectors), + rotator_ui_vectors, + }, + { + ARRAY_SIZE(rotator_vga_vectors), + rotator_vga_vectors, + }, + { + ARRAY_SIZE(rotator_720p_vectors), + rotator_720p_vectors, + }, + { + ARRAY_SIZE(rotator_1080p_vectors), + rotator_1080p_vectors, + }, +}; + +struct msm_bus_scale_pdata rotator_bus_scale_pdata = { + rotator_bus_scale_usecases, + ARRAY_SIZE(rotator_bus_scale_usecases), + .name = "rotator", +}; + +static struct msm_bus_vectors mdp_init_vectors[] = { + /* For now, 0th array entry is reserved. + * Please leave 0 as is and don't use it + */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +#ifdef CONFIG_FB_MSM_LCDC_DSUB +static struct msm_bus_vectors mdp_sd_smi_vectors[] = { + /* Default case static display/UI/2d/3d if FB SMI */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 388800000, + .ib = 486000000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors mdp_sd_ebi_vectors[] = { + /* Default case static display/UI/2d/3d if FB SMI */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 388800000, + .ib = 486000000 * 2, + }, +}; +static struct msm_bus_vectors mdp_vga_vectors[] = { + /* VGA and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 458092800, + .ib = 572616000, + }, + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 458092800, + .ib = 572616000 * 2, + }, +}; +static struct msm_bus_vectors mdp_720p_vectors[] = { + /* 720p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 471744000, + .ib = 589680000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 471744000, + .ib = 589680000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_1080p_vectors[] = { + /* 1080p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 575424000, + .ib = 719280000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 575424000, + .ib = 719280000 * 2, + }, +}; + +#else +static struct msm_bus_vectors mdp_sd_smi_vectors[] = { + /* Default case static display/UI/2d/3d if FB SMI */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 175110000, + .ib = 218887500, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors mdp_sd_ebi_vectors[] = { + /* Default case static display/UI/2d/3d if FB SMI */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000, + .ib = 270000000 * 2, + }, +}; +static struct msm_bus_vectors mdp_vga_vectors[] = { + /* VGA and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 216000000, + .ib = 270000000, + }, + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 216000000, + .ib = 270000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_720p_vectors[] = { + /* 720p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 230400000, + .ib = 288000000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 230400000, + .ib = 288000000 * 2, + }, +}; + +static struct msm_bus_vectors mdp_1080p_vectors[] = { + /* 1080p and less video */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 334080000, + .ib = 417600000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 334080000, + .ib = 550000000 * 2, + }, +}; + +#endif +static struct msm_bus_paths mdp_bus_scale_usecases[] = { + { + ARRAY_SIZE(mdp_init_vectors), + mdp_init_vectors, + }, + { + ARRAY_SIZE(mdp_sd_smi_vectors), + mdp_sd_smi_vectors, + }, + { + ARRAY_SIZE(mdp_sd_ebi_vectors), + mdp_sd_ebi_vectors, + }, + { + ARRAY_SIZE(mdp_vga_vectors), + mdp_vga_vectors, + }, + { + ARRAY_SIZE(mdp_720p_vectors), + mdp_720p_vectors, + }, + { + ARRAY_SIZE(mdp_1080p_vectors), + mdp_1080p_vectors, + }, +}; +static struct msm_bus_scale_pdata mdp_bus_scale_pdata = { + mdp_bus_scale_usecases, + ARRAY_SIZE(mdp_bus_scale_usecases), + .name = "mdp", +}; + +#endif +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors dtv_bus_init_vectors[] = { + /* For now, 0th array entry is reserved. + * Please leave 0 as is and don't use it + */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors dtv_bus_def_vectors[] = { + /* For now, 0th array entry is reserved. + * Please leave 0 as is and don't use it + */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 566092800, + .ib = 707616000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 566092800, + .ib = 707616000, + }, +}; + +static struct msm_bus_vectors dtv_bus_hdmi_prim_vectors[] = { + /* For now, 0th array entry is reserved. + * Please leave 0 as is and don't use it + */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 2000000000, + .ib = 2000000000, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2000000000, + .ib = 2000000000, + }, +}; + +static struct msm_bus_paths dtv_bus_scale_usecases[] = { + { + ARRAY_SIZE(dtv_bus_init_vectors), + dtv_bus_init_vectors, + }, + { + ARRAY_SIZE(dtv_bus_def_vectors), + dtv_bus_def_vectors, + }, +}; + +static struct msm_bus_scale_pdata dtv_bus_scale_pdata = { + dtv_bus_scale_usecases, + ARRAY_SIZE(dtv_bus_scale_usecases), + .name = "dtv", +}; + +static struct lcdc_platform_data dtv_pdata = { + .bus_scale_table = &dtv_bus_scale_pdata, +}; + +static struct msm_bus_paths dtv_hdmi_prim_bus_scale_usecases[] = { + { + ARRAY_SIZE(dtv_bus_init_vectors), + dtv_bus_init_vectors, + }, + { + ARRAY_SIZE(dtv_bus_hdmi_prim_vectors), + dtv_bus_hdmi_prim_vectors, + }, +}; + +static struct msm_bus_scale_pdata dtv_hdmi_prim_bus_scale_pdata = { + dtv_hdmi_prim_bus_scale_usecases, + ARRAY_SIZE(dtv_hdmi_prim_bus_scale_usecases), + .name = "dtv", +}; + +static struct lcdc_platform_data dtv_hdmi_prim_pdata = { + .bus_scale_table = &dtv_hdmi_prim_bus_scale_pdata, +}; +#endif + + +static struct lcdc_platform_data lcdc_pdata = { + .lcdc_power_save = lcdc_panel_power, +}; + + +#define MDP_VSYNC_GPIO 28 + +/* + * MIPI_DSI only use 8058_LDO0 which need always on + * therefore it need to be put at low power mode if + * it was not used instead of turn it off. + */ +static int mipi_dsi_panel_power(int on) +{ + int flag_on = !!on; + static int mipi_dsi_power_save_on; + static struct regulator *ldo0; + int rc = 0; + + if (mipi_dsi_power_save_on == flag_on) + return 0; + + mipi_dsi_power_save_on = flag_on; + + if (ldo0 == NULL) { /* init */ + ldo0 = regulator_get(NULL, "8058_l0"); + if (IS_ERR(ldo0)) { + pr_debug("%s: LDO0 failed\n", __func__); + rc = PTR_ERR(ldo0); + return rc; + } + + rc = regulator_set_voltage(ldo0, 1200000, 1200000); + if (rc) + goto out; + + rc = regulator_enable(ldo0); + if (rc) + goto out; + } + + if (on) { + /* set ldo0 to HPM */ + rc = regulator_set_optimum_mode(ldo0, 100000); + if (rc < 0) + goto out; + } else { + /* set ldo0 to LPM */ + rc = regulator_set_optimum_mode(ldo0, 1000); + if (rc < 0) + goto out; + } + + return 0; +out: + regulator_disable(ldo0); + regulator_put(ldo0); + ldo0 = NULL; + return rc; +} + +static struct mipi_dsi_platform_data mipi_dsi_pdata = { + .vsync_gpio = MDP_VSYNC_GPIO, + .dsi_power_save = mipi_dsi_panel_power, +}; + +#ifdef CONFIG_FB_MSM_TVOUT +static struct regulator *reg_8058_l13; + +static int atv_dac_power(int on) +{ + int rc = 0; + #define _GET_REGULATOR(var, name) do { \ + var = regulator_get(NULL, name); \ + if (IS_ERR(var)) { \ + pr_info("'%s' regulator not found, rc=%ld\n", \ + name, IS_ERR(var)); \ + var = NULL; \ + return -ENODEV; \ + } \ + } while (0) + + if (!reg_8058_l13) + _GET_REGULATOR(reg_8058_l13, "8058_l13"); + #undef _GET_REGULATOR + + if (on) { + rc = regulator_set_voltage(reg_8058_l13, 2050000, 2050000); + if (rc) { + pr_info("%s: '%s' regulator set voltage failed,\ + rc=%d\n", __func__, "8058_l13", rc); + return rc; + } + + rc = regulator_enable(reg_8058_l13); + if (rc) { + pr_err("%s: '%s' regulator enable failed,\ + rc=%d\n", __func__, "8058_l13", rc); + return rc; + } + } else { + rc = regulator_force_disable(reg_8058_l13); + if (rc) + pr_warning("%s: '%s' regulator disable failed, rc=%d\n", + __func__, "8058_l13", rc); + } + return rc; + +} +#endif + +#ifdef CONFIG_FB_MSM_MIPI_DSI +int mdp_core_clk_rate_table[] = { + 85330000, + 128000000, + 160000000, + 200000000, +}; +#else +int mdp_core_clk_rate_table[] = { + 59080000, + 128000000, + 128000000, + 200000000, +}; +#endif + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = MDP_VSYNC_GPIO, + .mdp_core_clk_rate = 59080000, + .mdp_core_clk_table = mdp_core_clk_rate_table, + .num_mdp_clk = ARRAY_SIZE(mdp_core_clk_rate_table), +#ifdef CONFIG_MSM_BUS_SCALING + .mdp_bus_scale_table = &mdp_bus_scale_pdata, +#endif + .mdp_rev = MDP_REV_41, +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .mem_hid = BIT(ION_CP_WB_HEAP_ID), +#else + .mem_hid = MEMTYPE_EBI1, +#endif +}; + +static void __init reserve_mdp_memory(void) +{ + mdp_pdata.ov0_wb_size = MSM_FB_OVERLAY0_WRITEBACK_SIZE; + mdp_pdata.ov1_wb_size = MSM_FB_OVERLAY1_WRITEBACK_SIZE; +#if defined(CONFIG_ANDROID_PMEM) && !defined(CONFIG_MSM_MULTIMEDIA_USE_ION) + msm8x60_reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov0_wb_size; + msm8x60_reserve_table[mdp_pdata.mem_hid].size += + mdp_pdata.ov1_wb_size; +#endif +} + +#ifdef CONFIG_FB_MSM_TVOUT + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors atv_bus_init_vectors[] = { + /* For now, 0th array entry is reserved. + * Please leave 0 as is and don't use it + */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; +static struct msm_bus_vectors atv_bus_def_vectors[] = { + /* For now, 0th array entry is reserved. + * Please leave 0 as is and don't use it + */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 236390400, + .ib = 265939200, + }, + /* Master and slaves can be from different fabrics */ + { + .src = MSM_BUS_MASTER_MDP_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 236390400, + .ib = 265939200, + }, +}; +static struct msm_bus_paths atv_bus_scale_usecases[] = { + { + ARRAY_SIZE(atv_bus_init_vectors), + atv_bus_init_vectors, + }, + { + ARRAY_SIZE(atv_bus_def_vectors), + atv_bus_def_vectors, + }, +}; +static struct msm_bus_scale_pdata atv_bus_scale_pdata = { + atv_bus_scale_usecases, + ARRAY_SIZE(atv_bus_scale_usecases), + .name = "atv", +}; +#endif + +static struct tvenc_platform_data atv_pdata = { + .poll = 0, + .pm_vid_en = atv_dac_power, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &atv_bus_scale_pdata, +#endif +}; +#endif + +static void __init msm_fb_add_devices(void) +{ +#ifdef CONFIG_FB_MSM_LCDC_DSUB + mdp_pdata.mdp_core_clk_table = NULL; + mdp_pdata.num_mdp_clk = 0; + mdp_pdata.mdp_core_clk_rate = 200000000; +#endif + if (machine_is_msm8x60_rumi3()) + msm_fb_register_device("mdp", NULL); + else + msm_fb_register_device("mdp", &mdp_pdata); + + msm_fb_register_device("lcdc", &lcdc_pdata); + msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata); +#ifdef CONFIG_MSM_BUS_SCALING + if (hdmi_is_primary) + msm_fb_register_device("dtv", &dtv_hdmi_prim_pdata); + else + msm_fb_register_device("dtv", &dtv_pdata); +#endif +#ifdef CONFIG_FB_MSM_TVOUT + msm_fb_register_device("tvenc", &atv_pdata); + msm_fb_register_device("tvout_device", NULL); +#endif +} + +/** + * Set MDP clocks to high frequency to avoid underflow when + * using high resolution 1200x1920 WUXGA/HDMI as primary panels + */ +static void set_mdp_clocks_for_wuxga(void) +{ + int i; + + mdp_sd_smi_vectors[0].ab = 2000000000; + mdp_sd_smi_vectors[0].ib = 2000000000; + mdp_sd_smi_vectors[1].ab = 2000000000; + mdp_sd_smi_vectors[1].ib = 2000000000; + + mdp_sd_ebi_vectors[0].ab = 2000000000; + mdp_sd_ebi_vectors[0].ib = 2000000000; + mdp_sd_ebi_vectors[1].ab = 2000000000; + mdp_sd_ebi_vectors[1].ib = 2000000000; + + mdp_vga_vectors[0].ab = 2000000000; + mdp_vga_vectors[0].ib = 2000000000; + mdp_vga_vectors[1].ab = 2000000000; + mdp_vga_vectors[1].ib = 2000000000; + + mdp_720p_vectors[0].ab = 2000000000; + mdp_720p_vectors[0].ib = 2000000000; + mdp_720p_vectors[1].ab = 2000000000; + mdp_720p_vectors[1].ib = 2000000000; + + mdp_1080p_vectors[0].ab = 2000000000; + mdp_1080p_vectors[0].ib = 2000000000; + mdp_1080p_vectors[1].ab = 2000000000; + mdp_1080p_vectors[1].ib = 2000000000; + + mdp_pdata.mdp_core_clk_rate = 200000000; + + for (i = 0; i < ARRAY_SIZE(mdp_core_clk_rate_table); i++) + mdp_core_clk_rate_table[i] = 200000000; +} + +#if (defined(CONFIG_MARIMBA_CORE)) && \ + (defined(CONFIG_MSM_BT_POWER) || defined(CONFIG_MSM_BT_POWER_MODULE)) + +static const struct { + char *name; + int vmin; + int vmax; +} bt_regs_info[] = { + { "8058_s3", 1800000, 1800000 }, + { "8058_s2", 1300000, 1300000 }, + { "8058_l8", 2900000, 3050000 }, +}; + +static struct { + bool enabled; +} bt_regs_status[] = { + { false }, + { false }, + { false }, +}; +static struct regulator *bt_regs[ARRAY_SIZE(bt_regs_info)]; + +static int bahama_bt(int on) +{ + int rc; + int i; + struct marimba config = { .mod_id = SLAVE_ID_BAHAMA}; + + struct bahama_variant_register { + const size_t size; + const struct bahama_config_register *set; + }; + + const struct bahama_config_register *p; + + u8 version; + + const struct bahama_config_register v10_bt_on[] = { + { 0xE9, 0x00, 0xFF }, + { 0xF4, 0x80, 0xFF }, + { 0xE4, 0x00, 0xFF }, + { 0xE5, 0x00, 0x0F }, +#ifdef CONFIG_WLAN + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF }, + { 0x01, 0x0C, 0x1F }, + { 0x01, 0x08, 0x1F }, + }; + + const struct bahama_config_register v20_bt_on_fm_off[] = { + { 0x11, 0x0C, 0xFF }, + { 0x13, 0x01, 0xFF }, + { 0xF4, 0x80, 0xFF }, + { 0xF0, 0x00, 0xFF }, + { 0xE9, 0x00, 0xFF }, +#ifdef CONFIG_WLAN + { 0x81, 0x00, 0x7F }, + { 0x82, 0x00, 0xFF }, + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF }, + }; + + const struct bahama_config_register v20_bt_on_fm_on[] = { + { 0x11, 0x0C, 0xFF }, + { 0x13, 0x01, 0xFF }, + { 0xF4, 0x86, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE9, 0x00, 0xFF }, +#ifdef CONFIG_WLAN + { 0x81, 0x00, 0x7F }, + { 0x82, 0x00, 0xFF }, + { 0xE6, 0x38, 0x7F }, + { 0xE7, 0x06, 0xFF }, +#endif + { 0xE9, 0x21, 0xFF }, + }; + + const struct bahama_config_register v10_bt_off[] = { + { 0xE9, 0x00, 0xFF }, + }; + + const struct bahama_config_register v20_bt_off_fm_off[] = { + { 0xF4, 0x84, 0xFF }, + { 0xF0, 0x04, 0xFF }, + { 0xE9, 0x00, 0xFF } + }; + + const struct bahama_config_register v20_bt_off_fm_on[] = { + { 0xF4, 0x86, 0xFF }, + { 0xF0, 0x06, 0xFF }, + { 0xE9, 0x00, 0xFF } + }; + const struct bahama_variant_register bt_bahama[2][3] = { + { + { ARRAY_SIZE(v10_bt_off), v10_bt_off }, + { ARRAY_SIZE(v20_bt_off_fm_off), v20_bt_off_fm_off }, + { ARRAY_SIZE(v20_bt_off_fm_on), v20_bt_off_fm_on } + }, + { + { ARRAY_SIZE(v10_bt_on), v10_bt_on }, + { ARRAY_SIZE(v20_bt_on_fm_off), v20_bt_on_fm_off }, + { ARRAY_SIZE(v20_bt_on_fm_on), v20_bt_on_fm_on } + } + }; + + u8 offset = 0; /* index into bahama configs */ + + on = on ? 1 : 0; + version = read_bahama_ver(); + + if (version == VER_UNSUPPORTED) { + dev_err(&msm_bt_power_device.dev, + "%s: unsupported version\n", + __func__); + return -EIO; + } + + if (version == VER_2_0) { + if (marimba_get_fm_status(&config)) + offset = 0x01; + } + + /* Voting off 1.3V S2 Regulator,BahamaV2 used in Normal mode */ + if (on && (version == VER_2_0)) { + for (i = 0; i < ARRAY_SIZE(bt_regs_info); i++) { + if ((!strcmp(bt_regs_info[i].name, "8058_s2")) + && (bt_regs_status[i].enabled == true)) { + if (regulator_disable(bt_regs[i])) { + dev_err(&msm_bt_power_device.dev, + "%s: regulator disable failed", + __func__); + } + bt_regs_status[i].enabled = false; + break; + } + } + } + + p = bt_bahama[on][version + offset].set; + + dev_info(&msm_bt_power_device.dev, + "%s: found version %d\n", __func__, version); + + for (i = 0; i < bt_bahama[on][version + offset].size; i++) { + u8 value = (p+i)->value; + rc = marimba_write_bit_mask(&config, + (p+i)->reg, + &value, + sizeof((p+i)->value), + (p+i)->mask); + if (rc < 0) { + dev_err(&msm_bt_power_device.dev, + "%s: reg %d write failed: %d\n", + __func__, (p+i)->reg, rc); + return rc; + } + dev_dbg(&msm_bt_power_device.dev, + "%s: reg 0x%02x write value 0x%02x mask 0x%02x\n", + __func__, (p+i)->reg, + value, (p+i)->mask); + } + /* Update BT Status */ + if (on) + marimba_set_bt_status(&config, true); + else + marimba_set_bt_status(&config, false); + + return 0; +} + +static int bluetooth_use_regulators(int on) +{ + int i, recover = -1, rc = 0; + + for (i = 0; i < ARRAY_SIZE(bt_regs_info); i++) { + bt_regs[i] = on ? regulator_get(&msm_bt_power_device.dev, + bt_regs_info[i].name) : + (regulator_put(bt_regs[i]), NULL); + if (IS_ERR(bt_regs[i])) { + rc = PTR_ERR(bt_regs[i]); + dev_err(&msm_bt_power_device.dev, + "regulator %s get failed (%d)\n", + bt_regs_info[i].name, rc); + recover = i - 1; + bt_regs[i] = NULL; + break; + } + + if (!on) + continue; + + rc = regulator_set_voltage(bt_regs[i], + bt_regs_info[i].vmin, + bt_regs_info[i].vmax); + if (rc < 0) { + dev_err(&msm_bt_power_device.dev, + "regulator %s voltage set (%d)\n", + bt_regs_info[i].name, rc); + recover = i; + break; + } + } + + if (on && (recover > -1)) + for (i = recover; i >= 0; i--) { + regulator_put(bt_regs[i]); + bt_regs[i] = NULL; + } + + return rc; +} + +static int bluetooth_switch_regulators(int on) +{ + int i, rc = 0; + + for (i = 0; i < ARRAY_SIZE(bt_regs_info); i++) { + if (on && (bt_regs_status[i].enabled == false)) { + rc = regulator_enable(bt_regs[i]); + if (rc < 0) { + dev_err(&msm_bt_power_device.dev, + "regulator %s %s failed (%d)\n", + bt_regs_info[i].name, + "enable", rc); + if (i > 0) { + while (--i) { + regulator_disable(bt_regs[i]); + bt_regs_status[i].enabled + = false; + } + break; + } + } + bt_regs_status[i].enabled = true; + } else if (!on && (bt_regs_status[i].enabled == true)) { + rc = regulator_disable(bt_regs[i]); + if (rc < 0) { + dev_err(&msm_bt_power_device.dev, + "regulator %s %s failed (%d)\n", + bt_regs_info[i].name, + "disable", rc); + break; + } + bt_regs_status[i].enabled = false; + } + } + return rc; +} + +static struct msm_xo_voter *bt_clock; + +static int bluetooth_power(int on) +{ + int rc = 0; + int id; + + /* In case probe function fails, cur_connv_type would be -1 */ + id = adie_get_detected_connectivity_type(); + if (id != BAHAMA_ID) { + pr_err("%s: unexpected adie connectivity type: %d\n", + __func__, id); + return -ENODEV; + } + + if (on) { + + rc = bluetooth_use_regulators(1); + if (rc < 0) + goto out; + + rc = bluetooth_switch_regulators(1); + + if (rc < 0) + goto fail_put; + + bt_clock = msm_xo_get(MSM_XO_TCXO_D0, "bt_power"); + + if (IS_ERR(bt_clock)) { + pr_err("Couldn't get TCXO_D0 voter\n"); + goto fail_switch; + } + + rc = msm_xo_mode_vote(bt_clock, MSM_XO_MODE_ON); + + if (rc < 0) { + pr_err("Failed to vote for TCXO_DO ON\n"); + goto fail_vote; + } + + rc = bahama_bt(1); + + if (rc < 0) + goto fail_clock; + + msleep(10); + + rc = msm_xo_mode_vote(bt_clock, MSM_XO_MODE_PIN_CTRL); + + if (rc < 0) { + pr_err("Failed to vote for TCXO_DO pin control\n"); + goto fail_vote; + } + } else { + /* check for initial RFKILL block (power off) */ + /* some RFKILL versions/configurations rfkill_register */ + /* calls here for an initial set_block */ + /* avoid calling i2c and regulator before unblock (on) */ + if (platform_get_drvdata(&msm_bt_power_device) == NULL) { + dev_info(&msm_bt_power_device.dev, + "%s: initialized OFF/blocked\n", __func__); + goto out; + } + + bahama_bt(0); + +fail_clock: + msm_xo_mode_vote(bt_clock, MSM_XO_MODE_OFF); +fail_vote: + msm_xo_put(bt_clock); +fail_switch: + bluetooth_switch_regulators(0); +fail_put: + bluetooth_use_regulators(0); + } + +out: + if (rc < 0) + on = 0; + dev_info(&msm_bt_power_device.dev, + "Bluetooth power switch: state %d result %d\n", on, rc); + + return rc; +} + +#endif /*CONFIG_MARIMBA_CORE, CONFIG_MSM_BT_POWER, CONFIG_MSM_BT_POWER_MODULE*/ + +static void __init msm8x60_cfg_smsc911x(void) +{ + smsc911x_resources[1].start = + PM8058_GPIO_IRQ(PM8058_IRQ_BASE, 6); + smsc911x_resources[1].end = + PM8058_GPIO_IRQ(PM8058_IRQ_BASE, 6); +} + +void msm_fusion_setup_pinctrl(void) +{ + struct msm_xo_voter *a1; + + if (socinfo_get_platform_subtype() == 0x3) { + /* + * Vote for the A1 clock to be in pin control mode before + * the external images are loaded. + */ + a1 = msm_xo_get(MSM_XO_TCXO_A1, "mdm"); + BUG_ON(!a1); + msm_xo_mode_vote(a1, MSM_XO_MODE_PIN_CTRL); + } +} + +struct msm_board_data { + struct msm_gpiomux_configs *gpiomux_cfgs; +}; + +static struct msm_board_data msm8x60_rumi3_board_data __initdata = { + .gpiomux_cfgs = msm8x60_surf_ffa_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_sim_board_data __initdata = { + .gpiomux_cfgs = msm8x60_surf_ffa_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_surf_board_data __initdata = { + .gpiomux_cfgs = msm8x60_surf_ffa_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_ffa_board_data __initdata = { + .gpiomux_cfgs = msm8x60_surf_ffa_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_fluid_board_data __initdata = { + .gpiomux_cfgs = msm8x60_fluid_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_charm_surf_board_data __initdata = { + .gpiomux_cfgs = msm8x60_charm_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_charm_ffa_board_data __initdata = { + .gpiomux_cfgs = msm8x60_charm_gpiomux_cfgs, +}; + +static struct msm_board_data msm8x60_dragon_board_data __initdata = { + .gpiomux_cfgs = msm8x60_dragon_gpiomux_cfgs, +}; + +static void __init msm8x60_init(struct msm_board_data *board_data) +{ + uint32_t soc_platform_version; +#ifdef CONFIG_USB_EHCI_MSM_72K + struct pm8xxx_mpp_config_data hsusb_phy_mpp = { + .type = PM8XXX_MPP_TYPE_D_OUTPUT, + .level = PM8901_MPP_DIG_LEVEL_L5, + .control = PM8XXX_MPP_DOUT_CTRL_HIGH, + }; +#endif + pmic_reset_irq = PM8058_IRQ_BASE + PM8058_RESOUT_IRQ; + + /* + * Initialize RPM first as other drivers and devices may need + * it for their initialization. + */ + BUG_ON(msm_rpm_init(&msm8660_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + if (msm_xo_init()) + pr_err("Failed to initialize XO votes\n"); + + msm8x60_check_2d_hardware(); + + /* Change SPM handling of core 1 if PMM 8160 is present. */ + soc_platform_version = socinfo_get_platform_version(); + if (SOCINFO_VERSION_MAJOR(soc_platform_version) == 1 && + SOCINFO_VERSION_MINOR(soc_platform_version) >= 2) { + struct msm_spm_platform_data *spm_data; + + spm_data = &msm_spm_data_v1[1]; + spm_data->reg_init_values[MSM_SPM_REG_SAW_CFG] &= ~0x0F00UL; + spm_data->reg_init_values[MSM_SPM_REG_SAW_CFG] |= 0x0100UL; + + spm_data = &msm_spm_data[1]; + spm_data->reg_init_values[MSM_SPM_REG_SAW_CFG] &= ~0x0F00UL; + spm_data->reg_init_values[MSM_SPM_REG_SAW_CFG] |= 0x0100UL; + } + + /* + * Initialize SPM before acpuclock as the latter calls into SPM + * driver to set ACPU voltages. + */ + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 1) + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + else + msm_spm_init(msm_spm_data_v1, ARRAY_SIZE(msm_spm_data_v1)); + + /* + * Set regulators 8901_l4 and 8901_l6 to be always on in HPM for SURF + * devices so that the RPM doesn't drop into a low power mode that an + * un-reworked SURF cannot resume from. + */ + if (machine_is_msm8x60_surf()) { + int i; + + for (i = 0; i < ARRAY_SIZE(rpm_regulator_init_data); i++) + if (rpm_regulator_init_data[i].id + == RPM_VREG_ID_PM8901_L4 + || rpm_regulator_init_data[i].id + == RPM_VREG_ID_PM8901_L6) + rpm_regulator_init_data[i] + .init_data.constraints.always_on = 1; + } + + /* + * Disable regulator info printing so that regulator registration + * messages do not enter the kmsg log. + */ + regulator_suppress_info_printing(); + + /* Initialize regulators needed for clock_init. */ + platform_add_devices(early_regulators, ARRAY_SIZE(early_regulators)); + + msm_clock_init(&msm8x60_clock_init_data); + + /* Buses need to be initialized before early-device registration + * to get the platform data for fabrics. + */ + msm8x60_init_buses(); + platform_add_devices(early_devices, ARRAY_SIZE(early_devices)); + /* CPU frequency control is not supported on simulated targets. */ + if (!machine_is_msm8x60_rumi3() && !machine_is_msm8x60_sim()) + acpuclk_init(&acpuclk_8x60_soc_data); + + /* + * Enable EBI2 only for boards which make use of it. Leave + * it disabled for all others for additional power savings. + */ + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_rumi3() || + machine_is_msm8x60_sim() || + machine_is_msm8x60_fluid() || + machine_is_msm8x60_dragon()) + msm8x60_init_ebi2(); + msm8x60_init_tlmm(); + msm8x60_init_gpiomux(board_data->gpiomux_cfgs); + msm8x60_init_uart12dm(); +#ifdef CONFIG_MSM_CAMERA_V4L2 + msm8x60_init_cam(); +#endif + msm8x60_init_mmc(); + + +#if defined(CONFIG_PMIC8058_OTHC) || defined(CONFIG_PMIC8058_OTHC_MODULE) + msm8x60_init_pm8058_othc(); +#endif + + if (machine_is_msm8x60_fluid()) + pm8058_platform_data.keypad_pdata = &fluid_keypad_data; + else if (machine_is_msm8x60_dragon()) + pm8058_platform_data.keypad_pdata = &dragon_keypad_data; + else + pm8058_platform_data.keypad_pdata = &ffa_keypad_data; +#if !defined(CONFIG_MSM_CAMERA_V4L2) && defined(CONFIG_WEBCAM_OV9726) + /* Specify reset pin for OV9726 */ + if (machine_is_msm8x60_dragon()) { + msm_camera_sensor_ov9726_data.sensor_reset = 62; + ov9726_sensor_8660_info.mount_angle = 270; + } +#endif +#ifdef CONFIG_BATTERY_MSM8X60 + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusion() || machine_is_msm8x60_dragon() || + machine_is_msm8x60_fusn_ffa() || machine_is_msm8x60_fluid()) + platform_device_register(&msm_charger_device); +#endif + + if (machine_is_msm8x60_dragon()) + pm8058_platform_data.charger_pdata = &pmic8058_charger_dragon; + if (!machine_is_msm8x60_fluid()) + pm8058_platform_data.charger_pdata = &pmic8058_charger_ffa_surf; + + /* configure pmic leds */ + if (machine_is_msm8x60_fluid()) + pm8058_platform_data.leds_pdata = &pm8058_fluid_flash_leds_data; + else if (machine_is_msm8x60_dragon()) + pm8058_platform_data.leds_pdata = &pm8058_dragon_leds_data; + else + pm8058_platform_data.leds_pdata = &pm8058_flash_leds_data; + + if (machine_is_msm8x60_ffa() || machine_is_msm8x60_fusn_ffa() || + machine_is_msm8x60_dragon()) { + pm8058_platform_data.vibrator_pdata = &pm8058_vib_pdata; + } + + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_fluid() || machine_is_msm8x60_fusion() || + machine_is_msm8x60_fusn_ffa() || machine_is_msm8x60_dragon()) { + msm8x60_cfg_smsc911x(); + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 1) + platform_add_devices(msm8660_footswitch, + msm8660_num_footswitch); + platform_add_devices(surf_devices, + ARRAY_SIZE(surf_devices)); + +#ifdef CONFIG_MSM_DSPS + if (machine_is_msm8x60_fluid()) { + platform_device_unregister(&msm_gsbi12_qup_i2c_device); + msm8x60_init_dsps(); + } +#endif + + pm8901_vreg_mpp0_init(); + + platform_device_register(&msm8x60_8901_mpp_vreg); + +#ifdef CONFIG_USB_EHCI_MSM_72K + /* + * Drive MPP2 pin HIGH for PHY to generate ID interrupts on 8660 + * fluid + */ + if (machine_is_msm8x60_fluid()) + pm8xxx_mpp_config(PM8901_MPP_PM_TO_SYS(1), &hsusb_phy_mpp); + msm_add_host(0, &msm_usb_host_pdata); +#endif + +#ifdef CONFIG_SND_SOC_MSM8660_APQ + if (machine_is_msm8x60_dragon()) + platform_add_devices(dragon_alsa_devices, + ARRAY_SIZE(dragon_alsa_devices)); + else +#endif + platform_add_devices(asoc_devices, + ARRAY_SIZE(asoc_devices)); + } else { + msm8x60_configure_smc91x(); + platform_add_devices(rumi_sim_devices, + ARRAY_SIZE(rumi_sim_devices)); + } +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() || + machine_is_msm8x60_dragon()) + msm8x60_cfg_isp1763(); +#endif + + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) + platform_add_devices(charm_devices, ARRAY_SIZE(charm_devices)); + + +#if defined(CONFIG_SPI_QUP) || defined(CONFIG_SPI_QUP_MODULE) + if (machine_is_msm8x60_fluid()) + platform_device_register(&msm_gsbi10_qup_spi_device); + else + platform_device_register(&msm_gsbi1_qup_spi_device); +#endif + +#if defined(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC) || \ + defined(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC_MODULE) + if (machine_is_msm8x60_fluid()) + cyttsp_set_params(); +#endif + if (!machine_is_msm8x60_sim()) + msm_fb_add_devices(); + fixup_i2c_configs(); + register_i2c_devices(); + + if (machine_is_msm8x60_dragon()) + smsc911x_config.reset_gpio + = GPIO_ETHERNET_RESET_N_DRAGON; + + platform_device_register(&smsc911x_device); + +#if (defined(CONFIG_SPI_QUP)) && \ + (defined(CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT) || \ + defined(CONFIG_FB_MSM_LCDC_AUO_WVGA) || \ + defined(CONFIG_FB_MSM_LCDC_NT35582_WVGA)) + + if (machine_is_msm8x60_fluid()) { +#ifdef CONFIG_FB_MSM_LCDC_SAMSUNG_OLED_PT + if (SOCINFO_VERSION_MAJOR(soc_platform_version) < 3) { + spi_register_board_info(lcdc_samsung_spi_board_info, + ARRAY_SIZE(lcdc_samsung_spi_board_info)); + } else +#endif + { +#ifdef CONFIG_FB_MSM_LCDC_AUO_WVGA + spi_register_board_info(lcdc_auo_spi_board_info, + ARRAY_SIZE(lcdc_auo_spi_board_info)); +#endif + } +#ifdef CONFIG_FB_MSM_LCDC_NT35582_WVGA + } else if (machine_is_msm8x60_dragon()) { + spi_register_board_info(lcdc_nt35582_spi_board_info, + ARRAY_SIZE(lcdc_nt35582_spi_board_info)); +#endif + } +#endif + + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + + pm8058_gpios_init(); + +#ifdef CONFIG_SENSORS_MSM_ADC + if (machine_is_msm8x60_fluid()) { + msm_adc_pdata.dev_names = msm_adc_fluid_device_names; + msm_adc_pdata.num_adc = ARRAY_SIZE(msm_adc_fluid_device_names); + if (SOCINFO_VERSION_MAJOR(soc_platform_version) < 3) + msm_adc_pdata.gpio_config = APROC_CONFIG; + else + msm_adc_pdata.gpio_config = MPROC_CONFIG; + } + msm_adc_pdata.target_hw = MSM_8x60; +#endif +#ifdef CONFIG_MSM8X60_AUDIO + msm_snddev_init(); +#endif +#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE) + if (machine_is_msm8x60_fluid()) + platform_device_register(&fluid_leds_gpio); + else + platform_device_register(&gpio_leds); +#endif + + msm8x60_multi_sdio_init(); + + if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) + msm_fusion_setup_pinctrl(); +} + +static void __init msm8x60_rumi3_init(void) +{ + msm8x60_init(&msm8x60_rumi3_board_data); +} + +static void __init msm8x60_sim_init(void) +{ + msm8x60_init(&msm8x60_sim_board_data); +} + +static void __init msm8x60_surf_init(void) +{ + msm8x60_init(&msm8x60_surf_board_data); +} + +static void __init msm8x60_ffa_init(void) +{ + msm8x60_init(&msm8x60_ffa_board_data); +} + +static void __init msm8x60_fluid_init(void) +{ + msm8x60_init(&msm8x60_fluid_board_data); +} + +static void __init msm8x60_charm_surf_init(void) +{ + msm8x60_init(&msm8x60_charm_surf_board_data); +} + +static void __init msm8x60_charm_ffa_init(void) +{ + msm8x60_init(&msm8x60_charm_ffa_board_data); +} + +static void __init msm8x60_charm_init_early(void) +{ + msm8x60_allocate_memory_regions(); +} + +static void __init msm8x60_dragon_init(void) +{ + msm8x60_init(&msm8x60_dragon_board_data); +} MACHINE_START(MSM8X60_RUMI3, "QCT MSM8X60 RUMI3") - .fixup = msm8x60_fixup, - .reserve = msm8x60_reserve, .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, .init_irq = msm8x60_init_irq, .handle_irq = gic_handle_irq, - .init_machine = msm8x60_init, - .timer = &msm_timer, -MACHINE_END - -MACHINE_START(MSM8X60_SURF, "QCT MSM8X60 SURF") - .fixup = msm8x60_fixup, - .reserve = msm8x60_reserve, - .map_io = msm8x60_map_io, - .init_irq = msm8x60_init_irq, - .handle_irq = gic_handle_irq, - .init_machine = msm8x60_init, + .init_machine = msm8x60_rumi3_init, .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, MACHINE_END MACHINE_START(MSM8X60_SIM, "QCT MSM8X60 SIMULATOR") - .fixup = msm8x60_fixup, - .reserve = msm8x60_reserve, .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, .init_irq = msm8x60_init_irq, .handle_irq = gic_handle_irq, - .init_machine = msm8x60_init, + .init_machine = msm8x60_sim_init, .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8X60_SURF, "QCT MSM8X60 SURF") + .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, + .init_irq = msm8x60_init_irq, + .handle_irq = gic_handle_irq, + .init_machine = msm8x60_surf_init, + .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, MACHINE_END MACHINE_START(MSM8X60_FFA, "QCT MSM8X60 FFA") - .fixup = msm8x60_fixup, - .reserve = msm8x60_reserve, .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, .init_irq = msm8x60_init_irq, .handle_irq = gic_handle_irq, - .init_machine = msm8x60_init, + .init_machine = msm8x60_ffa_init, .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, MACHINE_END -#ifdef CONFIG_OF -/* TODO: General device tree support for all MSM. */ -DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)") +MACHINE_START(MSM8X60_FLUID, "QCT MSM8X60 FLUID") .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, .init_irq = msm8x60_init_irq, - .init_machine = msm8x60_dt_init, + .handle_irq = gic_handle_irq, + .init_machine = msm8x60_fluid_init, .timer = &msm_timer, - .dt_compat = msm8x60_fluid_match, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8X60_FUSION, "QCT MSM8X60 FUSION SURF") + .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, + .init_irq = msm8x60_init_irq, + .handle_irq = gic_handle_irq, + .init_machine = msm8x60_charm_surf_init, + .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8X60_FUSN_FFA, "QCT MSM8X60 FUSION FFA") + .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, + .init_irq = msm8x60_init_irq, + .handle_irq = gic_handle_irq, + .init_machine = msm8x60_charm_ffa_init, + .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, +MACHINE_END + +MACHINE_START(MSM8X60_DRAGON, "QCT MSM8X60 DRAGON") + .map_io = msm8x60_map_io, + .reserve = msm8x60_reserve, + .init_irq = msm8x60_init_irq, + .handle_irq = gic_handle_irq, + .init_machine = msm8x60_dragon_init, + .timer = &msm_timer, + .init_early = msm8x60_charm_init_early, + .restart = msm_restart, MACHINE_END -#endif /* CONFIG_OF */ diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c new file mode 100644 index 00000000000..761a3c98e8f --- /dev/null +++ b/arch/arm/mach-msm/board-qrd7627a.c @@ -0,0 +1,1085 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board-msm7x27a-regulator.h" +#include "devices.h" +#include "devices-msm7x2xa.h" +#include "pm.h" +#include "timer.h" +#include "pm-boot.h" +#include "board-msm7x27a-regulator.h" +#include "board-msm7627a.h" + +#define PMEM_KERNEL_EBI1_SIZE 0x3A000 +#define MSM_PMEM_AUDIO_SIZE 0x1F4000 +#define BAHAMA_SLAVE_ID_FM_REG 0x02 +#define FM_GPIO 83 +#define BT_PCM_BCLK_MODE 0x88 +#define BT_PCM_DIN_MODE 0x89 +#define BT_PCM_DOUT_MODE 0x8A +#define BT_PCM_SYNC_MODE 0x8B +#define FM_I2S_SD_MODE 0x8E +#define FM_I2S_WS_MODE 0x8F +#define FM_I2S_SCK_MODE 0x90 +#define I2C_PIN_CTL 0x15 +#define I2C_NORMAL 0x40 + +static struct platform_device msm_wlan_ar6000_pm_device = { + .name = "wlan_ar6000_pm_dev", + .id = -1, +}; + +static struct msm_gpio qup_i2c_gpios_io[] = { + { GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static struct msm_gpio qup_i2c_gpios_hw[] = { + { GPIO_CFG(60, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(61, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, + { GPIO_CFG(131, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_scl" }, + { GPIO_CFG(132, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), + "qup_sda" }, +}; + +static void gsbi_qup_i2c_gpio_config(int adap_id, int config_type) +{ + int rc; + + if (adap_id < 0 || adap_id > 1) + return; + + /* Each adapter gets 2 lines from the table */ + if (config_type) + rc = msm_gpios_request_enable(&qup_i2c_gpios_hw[adap_id*2], 2); + else + rc = msm_gpios_request_enable(&qup_i2c_gpios_io[adap_id*2], 2); + if (rc < 0) + pr_err("QUP GPIO request/enable failed: %d\n", rc); +} + +static struct msm_i2c_platform_data msm_gsbi0_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +static struct msm_i2c_platform_data msm_gsbi1_qup_i2c_pdata = { + .clk_freq = 100000, + .msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config, +}; + +#ifdef CONFIG_ARCH_MSM7X27A +#define MSM_PMEM_MDP_SIZE 0x2300000 +#define MSM_PMEM_ADSP_SIZE 0x1100000 +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +#ifdef CONFIG_USB_EHCI_MSM_72K +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + int rc = 0; + unsigned gpio; + + gpio = QRD_GPIO_HOST_VBUS_EN; + + rc = gpio_request(gpio, "i2c_host_vbus_en"); + if (rc < 0) { + pr_err("failed to request %d GPIO\n", gpio); + return; + } + gpio_direction_output(gpio, !!on); + gpio_set_value_cansleep(gpio, !!on); + gpio_free(gpio); +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM), +}; + +static void __init msm7627a_init_host(void) +{ + msm_add_host(0, &msm_usb_host_pdata); +} +#endif + +#ifdef CONFIG_USB_MSM_OTG_72K +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} + +static struct regulator *reg_hsusb; +static int msm_hsusb_ldo_init(int init) +{ + int rc = 0; + + if (init) { + reg_hsusb = regulator_get(NULL, "usb"); + if (IS_ERR(reg_hsusb)) { + rc = PTR_ERR(reg_hsusb); + pr_err("%s: could not get regulator: %d\n", + __func__, rc); + goto out; + } + + rc = regulator_set_voltage(reg_hsusb, 3300000, 3300000); + if (rc) { + pr_err("%s: could not set voltage: %d\n", + __func__, rc); + goto reg_free; + } + + return 0; + } + /* else fall through */ +reg_free: + regulator_put(reg_hsusb); +out: + reg_hsusb = NULL; + return rc; +} + +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + + if (IS_ERR_OR_NULL(reg_hsusb)) + return reg_hsusb ? PTR_ERR(reg_hsusb) : -ENODEV; + + if (ldo_status == enable) + return 0; + + ldo_status = enable; + + return enable ? + regulator_enable(reg_hsusb) : + regulator_disable(reg_hsusb); +} + +#ifndef CONFIG_USB_EHCI_MSM_72K +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret = 0; + + if (init) + ret = msm_pm_app_rpc_init(callback); + else + msm_pm_app_rpc_deinit(callback); + + return ret; +} +#endif + +static struct msm_otg_platform_data msm_otg_pdata = { +#ifndef CONFIG_USB_EHCI_MSM_72K + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, +#else + .vbus_power = msm_hsusb_vbus_power, +#endif + .rpc_connect = hsusb_rpc_connect, + .pemp_level = PRE_EMPHASIS_WITH_20_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DISABLE, + .drv_ampl = HS_DRV_AMPLITUDE_DEFAULT, + .se1_gating = SE1_GATING_DISABLE, + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, + .chg_init = hsusb_chg_init, + .chg_connected = hsusb_chg_connected, + .chg_vbus_draw = hsusb_chg_vbus_draw, +}; +#endif + +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata = { + .is_phy_status_timer_on = 1, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0xFD, +}; +#endif +static struct msm_pm_platform_data msm7627a_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 16000, + .residency = 20000, + }, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 12000, + .residency = 20000, + }, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 1, + .latency = 2000, + .residency = 0, + }, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, + .p_addr = 0, +}; + +/* 8625 PM platform data */ +static struct msm_pm_platform_data msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * 2] = { + /* CORE0 entries */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 16000, + .residency = 20000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 12000, + .residency = 20000, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + + /* picked latency & redisdency values from 7x30 */ + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + .latency = 500, + .residency = 6000, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 10, + }, + +}; + +static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, + .v_addr = MSM_CFG_CTL_BASE, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 1, + .memory_type = MEMTYPE_EBI1, + .request_region = request_fmem_c_region, + .release_region = release_fmem_c_region, + .reusable = 1, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static unsigned pmem_mdp_size = MSM_PMEM_MDP_SIZE; +static int __init pmem_mdp_size_setup(char *p) +{ + pmem_mdp_size = memparse(p, NULL); + return 0; +} + +early_param("pmem_mdp_size", pmem_mdp_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} + +early_param("pmem_adsp_size", pmem_adsp_size_setup); + +#define SND(desc, num) { .name = #desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(HANDSET, 0), + SND(MONO_HEADSET, 2), + SND(HEADSET, 3), + SND(SPEAKER, 6), + SND(TTY_HEADSET, 8), + SND(TTY_VCO, 9), + SND(TTY_HCO, 10), + SND(BT, 12), + SND(IN_S_SADC_OUT_HANDSET, 16), + SND(IN_S_SADC_OUT_SPEAKER_PHONE, 25), + SND(FM_DIGITAL_STEREO_HEADSET, 26), + SND(FM_DIGITAL_SPEAKER_PHONE, 27), + SND(FM_DIGITAL_BT_A2DP_HEADSET, 28), + SND(STEREO_HEADSET_AND_SPEAKER, 31), + SND(CURRENT, 0x7FFFFFFE), + SND(FM_ANALOG_STEREO_HEADSET, 35), + SND(FM_ANALOG_STEREO_HEADSET_CODEC, 36), +}; +#undef SND + +static struct msm_snd_endpoints msm_device_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = sizeof(snd_endpoints_list) / sizeof(struct snd_endpoint) +}; + +static struct platform_device msm_device_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &msm_device_snd_endpoints + }, +}; + +#define DEC0_FORMAT ((1<reusable) + fmem_pdata.size += pdata->size; + + reusable_count += (pdata->reusable) ? 1 : 0; + + if (pdata->reusable && reusable_count > 1) { + pr_err("%s: Too many PMEM devices specified as reusable. PMEM device %s was not configured as reusable.\n", + __func__, pdata->name); + pdata->reusable = 0; + } + } + +#endif +} + +static void __init reserve_memory_for(struct android_pmem_platform_data *p) +{ + msm7627a_reserve_table[p->memory_type].size += p->size; +} + +static void __init reserve_pmem_memory(void) +{ +#ifdef CONFIG_ANDROID_PMEM + unsigned int i; + for (i = 0; i < ARRAY_SIZE(pmem_pdata_array); ++i) + reserve_memory_for(pmem_pdata_array[i]); + + msm7627a_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size; +#endif +} + +static void __init msm7627a_calculate_reserve_sizes(void) +{ + size_pmem_devices(); + reserve_pmem_memory(); +} + +static int msm7627a_paddr_to_memtype(unsigned int paddr) +{ + return MEMTYPE_EBI1; +} + +static struct reserve_info msm7627a_reserve_info __initdata = { + .memtype_reserve_table = msm7627a_reserve_table, + .calculate_reserve_sizes = msm7627a_calculate_reserve_sizes, + .paddr_to_memtype = msm7627a_paddr_to_memtype, +}; + +static void __init msm7627a_reserve(void) +{ + reserve_info = &msm7627a_reserve_info; + msm_reserve(); + memblock_remove(MSM8625_WARM_BOOT_PHYS, SZ_32); +} + +static void __init msm8625_reserve(void) +{ + memblock_remove(MSM8625_SECONDARY_PHYS, SZ_8); + msm7627a_reserve(); +} + +static void msmqrd_adsp_add_pdev(void) +{ + int rc = 0; + struct rpc_board_dev *rpc_adsp_pdev; + + rpc_adsp_pdev = kzalloc(sizeof(struct rpc_board_dev), GFP_KERNEL); + if (rpc_adsp_pdev == NULL) { + pr_err("%s: Memory Allocation failure\n", __func__); + return; + } + rpc_adsp_pdev->prog = ADSP_RPC_PROG; + + if (cpu_is_msm8625()) + rpc_adsp_pdev->pdev = msm8625_device_adsp; + else + rpc_adsp_pdev->pdev = msm_adsp_device; + rc = msm_rpc_add_board_dev(rpc_adsp_pdev, 1); + if (rc < 0) { + pr_err("%s: return val: %d\n", __func__, rc); + kfree(rpc_adsp_pdev); + } +} + +static void __init msm7627a_device_i2c_init(void) +{ + msm_gsbi0_qup_i2c_device.dev.platform_data = &msm_gsbi0_qup_i2c_pdata; + msm_gsbi1_qup_i2c_device.dev.platform_data = &msm_gsbi1_qup_i2c_pdata; +} + +static void __init msm8625_device_i2c_init(void) +{ + msm8625_gsbi0_qup_i2c_device.dev.platform_data + = &msm_gsbi0_qup_i2c_pdata; + msm8625_gsbi1_qup_i2c_device.dev.platform_data + = &msm_gsbi1_qup_i2c_pdata; +} + +static struct platform_device msm_proccomm_regulator_dev = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &msm7x27a_proccomm_regulator_data + } +}; + +static void __init msm7627a_init_regulators(void) +{ + int rc = platform_device_register(&msm_proccomm_regulator_dev); + if (rc) + pr_err("%s: could not register regulator device: %d\n", + __func__, rc); +} + +static int __init msm_qrd_init_ar6000pm(void) +{ + msm_wlan_ar6000_pm_device.dev.platform_data = &ar600x_wlan_power; + return platform_device_register(&msm_wlan_ar6000_pm_device); +} + +static void __init msm_add_footswitch_devices(void) +{ + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); +} + +static void __init add_platform_devices(void) +{ + if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7() + || machine_is_msm8625_evt()) { + platform_add_devices(msm8625_evb_devices, + ARRAY_SIZE(msm8625_evb_devices)); + platform_add_devices(qrd3_devices, + ARRAY_SIZE(qrd3_devices)); + } else { + platform_add_devices(qrd7627a_devices, + ARRAY_SIZE(qrd7627a_devices)); + } + + if (machine_is_msm7627a_qrd3() || machine_is_msm7627a_evb()) + platform_add_devices(qrd3_devices, + ARRAY_SIZE(qrd3_devices)); + + platform_add_devices(common_devices, + ARRAY_SIZE(common_devices)); +} + +#define UART1DM_RX_GPIO 45 +static void __init qrd7627a_uart1dm_config(void) +{ + msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(UART1DM_RX_GPIO); + if (cpu_is_msm8625()) + msm8625_device_uart_dm1.dev.platform_data = + &msm_uart_dm1_pdata; + else + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +} + +static void __init qrd7627a_otg_gadget(void) +{ + if (cpu_is_msm8625()) { + msm_otg_pdata.swfi_latency = msm8625_pm_data + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency; + msm8625_device_otg.dev.platform_data = &msm_otg_pdata; + msm8625_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + + } else { + msm_otg_pdata.swfi_latency = msm7627a_pm_data + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_device_gadget_peripheral.dev.platform_data = + &msm_gadget_pdata; + } +} + +static void __init msm_pm_init(void) +{ + + if (!cpu_is_msm8625()) { + msm_pm_set_platform_data(msm7627a_pm_data, + ARRAY_SIZE(msm7627a_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + } else { + msm_pm_set_platform_data(msm8625_pm_data, + ARRAY_SIZE(msm8625_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_8625_boot_pdata)); + msm8x25_spm_device_init(); + } +} + +static void __init msm_qrd_init(void) +{ + msm7x2x_misc_init(); + msm7627a_init_regulators(); + msmqrd_adsp_add_pdev(); + + if (cpu_is_msm8625()) + msm8625_device_i2c_init(); + else + msm7627a_device_i2c_init(); + + /* uart1dm*/ + qrd7627a_uart1dm_config(); + /*OTG gadget*/ + qrd7627a_otg_gadget(); + + msm_add_footswitch_devices(); + add_platform_devices(); + + /* Ensure ar6000pm device is registered before MMC/SDC */ + msm_qrd_init_ar6000pm(); + msm7627a_init_mmc(); + +#ifdef CONFIG_USB_EHCI_MSM_72K + msm7627a_init_host(); +#endif + msm_pm_init(); + + msm_pm_register_irqs(); + msm_fb_add_devices(); + +#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE) + msm7627a_bt_power_init(); +#endif + + msm7627a_camera_init(); + qrd7627a_add_io_devices(); + msm7x25a_kgsl_3d0_init(); + msm8x25_kgsl_3d0_init(); +} + +static void __init qrd7627a_init_early(void) +{ + msm_msm7627a_allocate_memory_regions(); +} + +MACHINE_START(MSM7627A_QRD1, "QRD MSM7627a QRD1") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7627A_QRD3, "QRD MSM7627a QRD3") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM7627A_EVB, "QRD MSM7627a EVB") + .atag_offset = 0x100, + .map_io = msm_common_io_init, + .reserve = msm7627a_reserve, + .init_irq = msm_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = vic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_EVB, "QRD MSM8625 EVB") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_QRD7, "QRD MSM8625 QRD7") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END +MACHINE_START(MSM8625_EVT, "QRD MSM8625 EVT") + .atag_offset = 0x100, + .map_io = msm8625_map_io, + .reserve = msm8625_reserve, + .init_irq = msm8625_init_irq, + .init_machine = msm_qrd_init, + .timer = &msm_timer, + .init_early = qrd7627a_init_early, + .handle_irq = gic_handle_irq, +MACHINE_END diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c index 7e8909c978c..6a39316464b 100644 --- a/arch/arm/mach-msm/board-qsd8x50.c +++ b/arch/arm/mach-msm/board-qsd8x50.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -9,52 +9,260 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ -#include + #include #include #include #include +#include +#include +#include +#include #include -#include -#include -#include +#include +#include +#include #include #include #include #include -#include -#include -#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "devices.h" +#include "timer.h" +#include "msm-keypad-devices.h" +#include "acpuclock.h" +#include "pm.h" +#include "irq.h" +#include "pm-boot.h" +#ifdef CONFIG_USB_ANDROID +#include +#endif -extern struct sys_timer msm_timer; +#define TOUCHPAD_SUSPEND 34 +#define TOUCHPAD_IRQ 38 -static const resource_size_t qsd8x50_surf_smc91x_base __initdata = 0x70000300; -static const unsigned qsd8x50_surf_smc91x_gpio __initdata = 156; +#define MSM_PMEM_SF_SIZE 0x1700000 + +#define SMEM_SPINLOCK_I2C "S:6" + +#define MSM_PMEM_ADSP_SIZE 0x2A05000 +#define MSM_FB_SIZE 0x2EE000 +#define MSM_AUDIO_SIZE 0x80000 + +#ifdef CONFIG_MSM_SOC_REV_A +#define MSM_SMI_BASE 0xE0000000 +#else +#define MSM_SMI_BASE 0x00000000 +#endif + +#define MSM_SHARED_RAM_PHYS (MSM_SMI_BASE + 0x00100000) + +#define MSM_PMEM_SMI_BASE (MSM_SMI_BASE + 0x02B00000) +#define MSM_PMEM_SMI_SIZE 0x01500000 + +#define MSM_FB_BASE MSM_PMEM_SMI_BASE +#define MSM_PMEM_SMIPOOL_BASE (MSM_FB_BASE + MSM_FB_SIZE) +#define MSM_PMEM_SMIPOOL_SIZE (MSM_PMEM_SMI_SIZE - MSM_FB_SIZE) + +#define PMEM_KERNEL_EBI1_SIZE 0x28000 + +#define PMIC_VREG_WLAN_LEVEL 2600 +#define PMIC_VREG_GP6_LEVEL 2900 + +#define FPGA_SDCC_STATUS 0x70000280 -/* Leave smc91x resources empty here, as we'll fill them in - * at run-time: they vary from board to board, and the true - * configuration won't be known until boot. - */ static struct resource smc91x_resources[] = { [0] = { - .flags = IORESOURCE_MEM, + .flags = IORESOURCE_MEM, }, [1] = { - .flags = IORESOURCE_IRQ, + .flags = IORESOURCE_IRQ, }, }; +#ifdef CONFIG_USB_FUNCTION +static struct usb_mass_storage_platform_data usb_mass_storage_pdata = { + .nluns = 0x02, + .buf_size = 16384, + .vendor = "GOOGLE", + .product = "Mass storage", + .release = 0xffff, +}; + +static struct platform_device mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &usb_mass_storage_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_ANDROID +static char *usb_functions_default[] = { + "diag", + "modem", + "nmea", + "rmnet", + "usb_mass_storage", +}; + +static char *usb_functions_default_adb[] = { + "diag", + "adb", + "modem", + "nmea", + "rmnet", + "usb_mass_storage", +}; + +static char *usb_functions_rndis[] = { + "rndis", +}; + +static char *usb_functions_rndis_adb[] = { + "rndis", + "adb", +}; + +static char *usb_functions_all[] = { +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif + "adb", +#ifdef CONFIG_USB_F_SERIAL + "modem", + "nmea", +#endif +#ifdef CONFIG_USB_ANDROID_RMNET + "rmnet", +#endif + "usb_mass_storage", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x9026, + .num_functions = ARRAY_SIZE(usb_functions_default), + .functions = usb_functions_default, + }, + { + .product_id = 0x9025, + .num_functions = ARRAY_SIZE(usb_functions_default_adb), + .functions = usb_functions_default_adb, + }, + { + .product_id = 0xf00e, + .num_functions = ARRAY_SIZE(usb_functions_rndis), + .functions = usb_functions_rndis, + }, + { + .product_id = 0x9024, + .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), + .functions = usb_functions_rndis_adb, + }, +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Qualcomm Incorporated", + .product = "Mass storage", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +static struct usb_ether_platform_data rndis_pdata = { + /* ethaddr is filled by board_serialno_setup */ + .vendorID = 0x05C6, + .vendorDescr = "Qualcomm Incorporated", +}; + +static struct platform_device rndis_device = { + .name = "rndis", + .id = -1, + .dev = { + .platform_data = &rndis_pdata, + }, +}; + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x05C6, + .product_id = 0x9026, + .version = 0x0100, + .product_name = "Qualcomm HSUSB Device", + .manufacturer_name = "Qualcomm Incorporated", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, + .serial_number = "1234567890ABCDEF", +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static int __init board_serialno_setup(char *serialno) +{ + int i; + char *src = serialno; + + /* create a fake MAC address from our serial number. + * first byte is 0x02 to signify locally administered. + */ + rndis_pdata.ethaddr[0] = 0x02; + for (i = 0; *src; i++) { + /* XOR the USB serial across the remaining bytes */ + rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; + } + + android_usb_pdata.serial_number = serialno; + return 1; +} +__setup("androidboot.serialno=", board_serialno_setup); +#endif + static struct platform_device smc91x_device = { .name = "smc91x", .id = 0, @@ -62,53 +270,1816 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; -static int __init msm_init_smc91x(void) +#ifdef CONFIG_USB_FUNCTION +static struct usb_function_map usb_functions_map[] = { + {"diag", 0}, + {"adb", 1}, + {"modem", 2}, + {"nmea", 3}, + {"mass_storage", 4}, + {"ethernet", 5}, +}; + +/* dynamic composition */ +static struct usb_composition usb_func_composition[] = { + { + .product_id = 0x9012, + .functions = 0x5, /* 0101 */ + }, + + { + .product_id = 0x9013, + .functions = 0x15, /* 10101 */ + }, + + { + .product_id = 0x9014, + .functions = 0x30, /* 110000 */ + }, + + { + .product_id = 0x9015, + .functions = 0x12, /* 10010 */ + }, + + { + .product_id = 0x9016, + .functions = 0xD, /* 01101 */ + }, + + { + .product_id = 0x9017, + .functions = 0x1D, /* 11101 */ + }, + + { + .product_id = 0xF000, + .functions = 0x10, /* 10000 */ + }, + + { + .product_id = 0xF009, + .functions = 0x20, /* 100000 */ + }, + + { + .product_id = 0x9018, + .functions = 0x1F, /* 011111 */ + }, + + { + .product_id = 0x901A, + .functions = 0x0F, /* 01111 */ + }, +}; +#endif + +static struct msm_handset_platform_data hs_platform_data = { + .hs_name = "8k_handset", + .pwr_key_delay_ms = 500, /* 0 will disable end key */ +}; + +static struct platform_device hs_device = { + .name = "msm-handset", + .id = -1, + .dev = { + .platform_data = &hs_platform_data, + }, +}; + +#ifdef CONFIG_USB_FS_HOST +static struct msm_gpio fsusb_config[] = { + { GPIO_CFG(139, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "fs_dat" }, + { GPIO_CFG(140, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "fs_se0" }, + { GPIO_CFG(141, 3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "fs_oe_n" }, +}; + +static int fsusb_gpio_init(void) { - if (machine_is_qsd8x50_surf()) { - smc91x_resources[0].start = qsd8x50_surf_smc91x_base; - smc91x_resources[0].end = qsd8x50_surf_smc91x_base + 0xff; - smc91x_resources[1].start = - gpio_to_irq(qsd8x50_surf_smc91x_gpio); - smc91x_resources[1].end = - gpio_to_irq(qsd8x50_surf_smc91x_gpio); - platform_device_register(&smc91x_device); + return msm_gpios_request(fsusb_config, ARRAY_SIZE(fsusb_config)); +} + +static void msm_fsusb_setup_gpio(unsigned int enable) +{ + if (enable) + msm_gpios_enable(fsusb_config, ARRAY_SIZE(fsusb_config)); + else + msm_gpios_disable(fsusb_config, ARRAY_SIZE(fsusb_config)); + +} +#endif + +#define MSM_USB_BASE ((unsigned)addr) + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { +#ifdef CONFIG_USB_FUNCTION + .version = 0x0100, + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_180NM), + .vendor_id = 0x5c6, + .product_name = "Qualcomm HSUSB Device", + .serial_number = "1234567890ABCDEF", + .manufacturer_name = "Qualcomm Incorporated", + .compositions = usb_func_composition, + .num_compositions = ARRAY_SIZE(usb_func_composition), + .function_map = usb_functions_map, + .num_functions = ARRAY_SIZE(usb_functions_map), + .config_gpio = NULL, + +#endif +}; + +static struct vreg *vreg_usb; +static void msm_hsusb_vbus_power(unsigned phy_info, int on) +{ + + switch (PHY_TYPE(phy_info)) { + case USB_PHY_INTEGRATED: + if (on) + msm_hsusb_vbus_powerup(); + else + msm_hsusb_vbus_shutdown(); + break; + case USB_PHY_SERIAL_PMIC: + if (on) + vreg_enable(vreg_usb); + else + vreg_disable(vreg_usb); + break; + default: + pr_err("%s: undefined phy type ( %X ) \n", __func__, + phy_info); + } + +} + +static struct msm_usb_host_platform_data msm_usb_host_pdata = { + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_180NM), +}; + +#ifdef CONFIG_USB_FS_HOST +static struct msm_usb_host_platform_data msm_usb_host2_pdata = { + .phy_info = USB_PHY_SERIAL_PMIC, + .config_gpio = msm_fsusb_setup_gpio, + .vbus_power = msm_hsusb_vbus_power, +}; +#endif + +static struct android_pmem_platform_data android_pmem_kernel_ebi1_pdata = { + .name = PMEM_KERNEL_EBI1_DATA_NAME, + /* if no allocator_type, defaults to PMEM_ALLOCATORTYPE_BITMAP, + * the only valid choice at this time. The board structure is + * set to all zeros by the C runtime initialization and that is now + * the enum value of PMEM_ALLOCATORTYPE_BITMAP, now forced to 0 in + * include/linux/android_pmem.h. + */ + .cached = 0, +}; + +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION + +static struct android_pmem_platform_data android_pmem_kernel_smi_pdata = { + .name = PMEM_KERNEL_SMI_DATA_NAME, + /* if no allocator_type, defaults to PMEM_ALLOCATORTYPE_BITMAP, + * the only valid choice at this time. The board structure is + * set to all zeros by the C runtime initialization and that is now + * the enum value of PMEM_ALLOCATORTYPE_BITMAP, now forced to 0 in + * include/linux/android_pmem.h. + */ + .cached = 0, +}; + +#endif + +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_smipool_pdata = { + .name = "pmem_smipool", + .size = MSM_PMEM_SMIPOOL_SIZE, + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .cached = 0, +}; + + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &android_pmem_pdata }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct platform_device android_pmem_smipool_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_smipool_pdata }, +}; + + +static struct platform_device android_pmem_kernel_ebi1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &android_pmem_kernel_ebi1_pdata }, +}; + +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION +static struct platform_device android_pmem_kernel_smi_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_kernel_smi_pdata }, +}; +#endif + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +static int msm_fb_detect_panel(const char *name) +{ + int ret = -EPERM; + + if (machine_is_qsd8x50_ffa()) { + if (!strncmp(name, "mddi_toshiba_wvga_pt", 20)) + ret = 0; + else + ret = -ENODEV; + } else if ((machine_is_qsd8x50_surf()) + && !strcmp(name, "lcdc_external")) + ret = 0; + + return ret; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev = { + .platform_data = &msm_fb_pdata, + } +}; + +static struct msm_gpio bma_spi_gpio_config_data[] = { + { GPIO_CFG(22, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "bma_irq" }, +}; + +static int msm_bma_gpio_setup(struct device *dev) +{ + int rc; + + rc = msm_gpios_request_enable(bma_spi_gpio_config_data, + ARRAY_SIZE(bma_spi_gpio_config_data)); + + return rc; +} + +static void msm_bma_gpio_teardown(struct device *dev) +{ + msm_gpios_disable_free(bma_spi_gpio_config_data, + ARRAY_SIZE(bma_spi_gpio_config_data)); +} + +static struct bma150_platform_data bma_pdata = { + .setup = msm_bma_gpio_setup, + .teardown = msm_bma_gpio_teardown, +}; + +static struct resource qsd_spi_resources[] = { + { + .name = "spi_irq_in", + .start = INT_SPI_INPUT, + .end = INT_SPI_INPUT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_irq_out", + .start = INT_SPI_OUTPUT, + .end = INT_SPI_OUTPUT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_irq_err", + .start = INT_SPI_ERROR, + .end = INT_SPI_ERROR, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_base", + .start = 0xA1200000, + .end = 0xA1200000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spidm_channels", + .flags = IORESOURCE_DMA, + }, + { + .name = "spidm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static struct platform_device qsd_device_spi = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(qsd_spi_resources), + .resource = qsd_spi_resources, +}; + +static struct spi_board_info msm_spi_board_info[] __initdata = { + { + .modalias = "bma150", + .mode = SPI_MODE_3, + .irq = MSM_GPIO_TO_INT(22), + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 10000000, + .platform_data = &bma_pdata, + }, +}; + +#define CT_CSR_PHYS 0xA8700000 +#define TCSR_SPI_MUX (ct_csr_base + 0x54) +static int msm_qsd_spi_dma_config(void) +{ + void __iomem *ct_csr_base = 0; + u32 spi_mux; + int ret = 0; + + ct_csr_base = ioremap(CT_CSR_PHYS, PAGE_SIZE); + if (!ct_csr_base) { + pr_err("%s: Could not remap %x\n", __func__, CT_CSR_PHYS); + return -1; + } + + spi_mux = readl(TCSR_SPI_MUX); + switch (spi_mux) { + case (1): + qsd_spi_resources[4].start = DMOV_HSUART1_RX_CHAN; + qsd_spi_resources[4].end = DMOV_HSUART1_TX_CHAN; + qsd_spi_resources[5].start = DMOV_HSUART1_RX_CRCI; + qsd_spi_resources[5].end = DMOV_HSUART1_TX_CRCI; + break; + case (2): + qsd_spi_resources[4].start = DMOV_HSUART2_RX_CHAN; + qsd_spi_resources[4].end = DMOV_HSUART2_TX_CHAN; + qsd_spi_resources[5].start = DMOV_HSUART2_RX_CRCI; + qsd_spi_resources[5].end = DMOV_HSUART2_TX_CRCI; + break; + case (3): + qsd_spi_resources[4].start = DMOV_CE_OUT_CHAN; + qsd_spi_resources[4].end = DMOV_CE_IN_CHAN; + qsd_spi_resources[5].start = DMOV_CE_OUT_CRCI; + qsd_spi_resources[5].end = DMOV_CE_IN_CRCI; + break; + default: + ret = -1; + } + + iounmap(ct_csr_base); + return ret; +} + +static struct msm_gpio qsd_spi_gpio_config_data[] = { + { GPIO_CFG(17, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_clk" }, + { GPIO_CFG(18, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_mosi" }, + { GPIO_CFG(19, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_miso" }, + { GPIO_CFG(20, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "spi_cs0" }, + { GPIO_CFG(21, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_16MA), "spi_pwr" }, +}; + +static int msm_qsd_spi_gpio_config(void) +{ + int rc; + + rc = msm_gpios_request_enable(qsd_spi_gpio_config_data, + ARRAY_SIZE(qsd_spi_gpio_config_data)); + if (rc) + return rc; + + /* Set direction for SPI_PWR */ + gpio_direction_output(21, 1); + + return 0; +} + +static void msm_qsd_spi_gpio_release(void) +{ + msm_gpios_disable_free(qsd_spi_gpio_config_data, + ARRAY_SIZE(qsd_spi_gpio_config_data)); +} + +static struct msm_spi_platform_data qsd_spi_pdata = { + .max_clock_speed = 19200000, + .gpio_config = msm_qsd_spi_gpio_config, + .gpio_release = msm_qsd_spi_gpio_release, + .dma_config = msm_qsd_spi_dma_config, +}; + +static void __init msm_qsd_spi_init(void) +{ + qsd_device_spi.dev.platform_data = &qsd_spi_pdata; +} + +static int mddi_toshiba_pmic_bl(int level) +{ + int ret = -EPERM; + + if (machine_is_qsd8x50_ffa()) { + ret = pmic_set_led_intensity(LED_LCD, level); + + if (ret) + printk(KERN_WARNING "%s: can't set lcd backlight!\n", + __func__); + } + + return ret; +} + +static struct msm_panel_common_pdata mddi_toshiba_pdata = { + .pmic_backlight = mddi_toshiba_pmic_bl, +}; + +static struct platform_device mddi_toshiba_device = { + .name = "mddi_toshiba", + .id = 0, + .dev = { + .platform_data = &mddi_toshiba_pdata, + } +}; + +static void msm_fb_vreg_config(const char *name, int on) +{ + struct vreg *vreg; + int ret = 0; + + vreg = vreg_get(NULL, name); + if (IS_ERR(vreg)) { + printk(KERN_ERR "%s: vreg_get(%s) failed (%ld)\n", + __func__, name, PTR_ERR(vreg)); + return; + } + + ret = (on) ? vreg_enable(vreg) : vreg_disable(vreg); + if (ret) + printk(KERN_ERR "%s: %s(%s) failed!\n", + __func__, (on) ? "vreg_enable" : "vreg_disable", name); +} + +#define MDDI_RST_OUT_GPIO 100 + +static int mddi_power_save_on; +static int msm_fb_mddi_power_save(int on) +{ + int flag_on = !!on; + int ret = 0; + + + if (mddi_power_save_on == flag_on) + return ret; + + mddi_power_save_on = flag_on; + + if (!flag_on && machine_is_qsd8x50_ffa()) { + gpio_set_value(MDDI_RST_OUT_GPIO, 0); + mdelay(1); + } + + ret = pmic_lp_mode_control(flag_on ? OFF_CMD : ON_CMD, + PM_VREG_LP_MSME2_ID); + if (ret) + printk(KERN_ERR "%s: pmic_lp_mode_control failed!\n", __func__); + + msm_fb_vreg_config("gp5", flag_on); + msm_fb_vreg_config("boost", flag_on); + + if (flag_on && machine_is_qsd8x50_ffa()) { + gpio_set_value(MDDI_RST_OUT_GPIO, 0); + mdelay(1); + gpio_set_value(MDDI_RST_OUT_GPIO, 1); + gpio_set_value(MDDI_RST_OUT_GPIO, 1); + mdelay(1); + } + + return ret; +} + +static int msm_fb_mddi_sel_clk(u32 *clk_rate) +{ + *clk_rate *= 2; + return 0; +} + +static struct mddi_platform_data mddi_pdata = { + .mddi_power_save = msm_fb_mddi_power_save, + .mddi_sel_clk = msm_fb_mddi_sel_clk, +}; + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = 98, + .mdp_rev = MDP_REV_31, +}; + +static void __init msm_fb_add_devices(void) +{ + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("pmdh", &mddi_pdata); + msm_fb_register_device("emdh", &mddi_pdata); + msm_fb_register_device("tvenc", 0); + msm_fb_register_device("lcdc", 0); +} + +static struct resource msm_audio_resources[] = { + { + .flags = IORESOURCE_DMA, + }, + { + .name = "aux_pcm_dout", + .start = 68, + .end = 68, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_din", + .start = 69, + .end = 69, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_syncout", + .start = 70, + .end = 70, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_clkin_a", + .start = 71, + .end = 71, + .flags = IORESOURCE_IO, + }, + { + .name = "sdac_din", + .start = 144, + .end = 144, + .flags = IORESOURCE_IO, + }, + { + .name = "sdac_dout", + .start = 145, + .end = 145, + .flags = IORESOURCE_IO, + }, + { + .name = "sdac_wsout", + .start = 143, + .end = 143, + .flags = IORESOURCE_IO, + }, + { + .name = "cc_i2s_clk", + .start = 142, + .end = 142, + .flags = IORESOURCE_IO, + }, + { + .name = "audio_master_clkout", + .start = 146, + .end = 146, + .flags = IORESOURCE_IO, + }, + { + .name = "audio_base_addr", + .start = 0xa0700000, + .end = 0xa0700000 + 4, + .flags = IORESOURCE_MEM, + }, + +}; + +static unsigned audio_gpio_on[] = { + GPIO_CFG(68, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_DOUT */ + GPIO_CFG(69, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_DIN */ + GPIO_CFG(70, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_SYNC */ + GPIO_CFG(71, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* PCM_CLK */ + GPIO_CFG(142, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* CC_I2S_CLK */ + GPIO_CFG(143, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* SADC_WSOUT */ + GPIO_CFG(144, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* SADC_DIN */ + GPIO_CFG(145, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* SDAC_DOUT */ + GPIO_CFG(146, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* MA_CLK_OUT */ +}; + +static void __init audio_gpio_init(void) +{ + int pin, rc; + + for (pin = 0; pin < ARRAY_SIZE(audio_gpio_on); pin++) { + rc = gpio_tlmm_config(audio_gpio_on[pin], + GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, audio_gpio_on[pin], rc); + return; + } + } +} + +static struct platform_device msm_audio_device = { + .name = "msm_audio", + .id = 0, + .num_resources = ARRAY_SIZE(msm_audio_resources), + .resource = msm_audio_resources, +}; + +static struct resource bluesleep_resources[] = { + { + .name = "gpio_host_wake", + .start = 21, + .end = 21, + .flags = IORESOURCE_IO, + }, + { + .name = "gpio_ext_wake", + .start = 19, + .end = 19, + .flags = IORESOURCE_IO, + }, + { + .name = "host_wake", + .start = MSM_GPIO_TO_INT(21), + .end = MSM_GPIO_TO_INT(21), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_bluesleep_device = { + .name = "bluesleep", + .id = -1, + .num_resources = ARRAY_SIZE(bluesleep_resources), + .resource = bluesleep_resources, +}; + +#ifdef CONFIG_BT +static struct platform_device msm_bt_power_device = { + .name = "bt_power", +}; + +enum { + BT_SYSRST, + BT_WAKE, + BT_HOST_WAKE, + BT_VDD_IO, + BT_RFR, + BT_CTS, + BT_RX, + BT_TX, + BT_VDD_FREG +}; + +static struct msm_gpio bt_config_power_off[] = { + { GPIO_CFG(18, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "BT SYSRST" }, + { GPIO_CFG(19, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "BT WAKE" }, + { GPIO_CFG(21, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "HOST WAKE" }, + { GPIO_CFG(22, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "BT VDD_IO" }, + { GPIO_CFG(43, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_RFR" }, + { GPIO_CFG(44, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_CTS" }, + { GPIO_CFG(45, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_RX" }, + { GPIO_CFG(46, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "UART1DM_TX" } +}; + +static struct msm_gpio bt_config_power_on[] = { + { GPIO_CFG(18, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "BT SYSRST" }, + { GPIO_CFG(19, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "BT WAKE" }, + { GPIO_CFG(21, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "HOST WAKE" }, + { GPIO_CFG(22, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "BT VDD_IO" }, + { GPIO_CFG(43, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_RFR" }, + { GPIO_CFG(44, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_CTS" }, + { GPIO_CFG(45, 2, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_RX" }, + { GPIO_CFG(46, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "UART1DM_TX" } +}; + +static struct msm_gpio wlan_config_power_off[] = { + { GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "SDC2_CLK" }, + { GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "SDC2_CMD" }, + { GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "SDC2_D3" }, + { GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "SDC2_D2" }, + { GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "SDC2_D1" }, + { GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "SDC2_D0" }, + { GPIO_CFG(113, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "VDD_WLAN" }, + { GPIO_CFG(138, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + "WLAN_PWD" } +}; + +static struct msm_gpio wlan_config_power_on[] = { + { GPIO_CFG(62, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "SDC2_CLK" }, + { GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "SDC2_CMD" }, + { GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "SDC2_D3" }, + { GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "SDC2_D2" }, + { GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "SDC2_D1" }, + { GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "SDC2_D0" }, + { GPIO_CFG(113, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "VDD_WLAN" }, + { GPIO_CFG(138, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), + "WLAN_PWD" } +}; + +static int bluetooth_power(int on) +{ + int rc; + struct vreg *vreg_wlan; + + vreg_wlan = vreg_get(NULL, "wlan"); + + if (IS_ERR(vreg_wlan)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_wlan)); + return PTR_ERR(vreg_wlan); + } + + if (on) { + /* units of mV, steps of 50 mV */ + rc = vreg_set_level(vreg_wlan, PMIC_VREG_WLAN_LEVEL); + if (rc) { + printk(KERN_ERR "%s: vreg wlan set level failed (%d)\n", + __func__, rc); + return -EIO; + } + rc = vreg_enable(vreg_wlan); + if (rc) { + printk(KERN_ERR "%s: vreg wlan enable failed (%d)\n", + __func__, rc); + return -EIO; + } + + rc = msm_gpios_enable(bt_config_power_on, + ARRAY_SIZE(bt_config_power_on)); + if (rc < 0) { + printk(KERN_ERR + "%s: bt power on gpio config failed: %d\n", + __func__, rc); + return rc; + } + + if (machine_is_qsd8x50_ffa()) { + rc = msm_gpios_enable + (wlan_config_power_on, + ARRAY_SIZE(wlan_config_power_on)); + if (rc < 0) { + printk + (KERN_ERR + "%s: wlan power on gpio config failed: %d\n", + __func__, rc); + return rc; + } + } + + gpio_set_value(22, on); /* VDD_IO */ + gpio_set_value(18, on); /* SYSRST */ + + if (machine_is_qsd8x50_ffa()) { + gpio_set_value(138, 0); /* WLAN: CHIP_PWD */ + gpio_set_value(113, on); /* WLAN */ + } + } else { + if (machine_is_qsd8x50_ffa()) { + gpio_set_value(138, on); /* WLAN: CHIP_PWD */ + gpio_set_value(113, on); /* WLAN */ + } + + gpio_set_value(18, on); /* SYSRST */ + gpio_set_value(22, on); /* VDD_IO */ + + rc = vreg_disable(vreg_wlan); + if (rc) { + printk(KERN_ERR "%s: vreg wlan disable failed (%d)\n", + __func__, rc); + return -EIO; + } + + rc = msm_gpios_enable(bt_config_power_off, + ARRAY_SIZE(bt_config_power_off)); + if (rc < 0) { + printk(KERN_ERR + "%s: bt power off gpio config failed: %d\n", + __func__, rc); + return rc; + } + + if (machine_is_qsd8x50_ffa()) { + rc = msm_gpios_enable + (wlan_config_power_off, + ARRAY_SIZE(wlan_config_power_off)); + if (rc < 0) { + printk + (KERN_ERR + "%s: wlan power off gpio config failed: %d\n", + __func__, rc); + return rc; + } + } + } + + printk(KERN_DEBUG "Bluetooth power switch: %d\n", on); + + return 0; +} + +static void __init bt_power_init(void) +{ + struct vreg *vreg_bt; + int rc; + + if (machine_is_qsd8x50_ffa()) { + gpio_set_value(138, 0); /* WLAN: CHIP_PWD */ + gpio_set_value(113, 0); /* WLAN */ + } + + gpio_set_value(18, 0); /* SYSRST */ + gpio_set_value(22, 0); /* VDD_IO */ + + /* do not have vreg bt defined, gp6 is the same */ + /* vreg_get parameter 1 (struct device *) is ignored */ + vreg_bt = vreg_get(NULL, "gp6"); + + if (IS_ERR(vreg_bt)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_bt)); + goto exit; + } + + /* units of mV, steps of 50 mV */ + rc = vreg_set_level(vreg_bt, PMIC_VREG_GP6_LEVEL); + if (rc) { + printk(KERN_ERR "%s: vreg bt set level failed (%d)\n", + __func__, rc); + goto exit; + } + rc = vreg_enable(vreg_bt); + if (rc) { + printk(KERN_ERR "%s: vreg bt enable failed (%d)\n", + __func__, rc); + goto exit; + } + + if (bluetooth_power(0)) + goto exit; + + msm_bt_power_device.dev.platform_data = &bluetooth_power; + + printk(KERN_DEBUG "Bluetooth power switch: initialized\n"); + +exit: + return; +} +#else +#define bt_power_init(x) do {} while (0) +#endif + +static struct platform_device msm_device_pmic_leds = { + .name = "pmic-leds", + .id = -1, +}; + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define TSIF_A_SYNC GPIO_CFG(106, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_A_DATA GPIO_CFG(107, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_A_EN GPIO_CFG(108, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_A_CLK GPIO_CFG(109, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) + +static const struct msm_gpio tsif_gpios[] = { + { .gpio_cfg = TSIF_A_CLK, .label = "tsif_clk", }, + { .gpio_cfg = TSIF_A_EN, .label = "tsif_en", }, + { .gpio_cfg = TSIF_A_DATA, .label = "tsif_data", }, + { .gpio_cfg = TSIF_A_SYNC, .label = "tsif_sync", }, +}; + +static struct msm_tsif_platform_data tsif_platform_data = { + .num_gpios = ARRAY_SIZE(tsif_gpios), + .gpios = tsif_gpios, + .tsif_clk = "core_clk", + .tsif_ref_clk = "ref_clk", +}; + +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + +static void touchpad_gpio_release(void) +{ + gpio_free(TOUCHPAD_IRQ); + gpio_free(TOUCHPAD_SUSPEND); +} + +static int touchpad_gpio_setup(void) +{ + int rc; + int suspend_pin = TOUCHPAD_SUSPEND; + int irq_pin = TOUCHPAD_IRQ; + unsigned suspend_cfg = + GPIO_CFG(suspend_pin, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + unsigned irq_cfg = + GPIO_CFG(irq_pin, 1, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + + rc = gpio_request(irq_pin, "msm_touchpad_irq"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + irq_pin, rc); + goto err_gpioconfig; + } + rc = gpio_request(suspend_pin, "msm_touchpad_suspend"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + suspend_pin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(suspend_cfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + suspend_pin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(irq_cfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + irq_pin, rc); + goto err_gpioconfig; + } + return rc; + +err_gpioconfig: + touchpad_gpio_release(); + return rc; +} + +static struct msm_touchpad_platform_data msm_touchpad_data = { + .gpioirq = TOUCHPAD_IRQ, + .gpiosuspend = TOUCHPAD_SUSPEND, + .gpio_setup = touchpad_gpio_setup, + .gpio_shutdown = touchpad_gpio_release +}; + +#define KBD_RST 35 +#define KBD_IRQ 36 + +static void kbd_gpio_release(void) +{ + gpio_free(KBD_IRQ); + gpio_free(KBD_RST); +} + +static int kbd_gpio_setup(void) +{ + int rc; + int respin = KBD_RST; + int irqpin = KBD_IRQ; + unsigned rescfg = + GPIO_CFG(respin, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA); + unsigned irqcfg = + GPIO_CFG(irqpin, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA); + + rc = gpio_request(irqpin, "gpio_keybd_irq"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + irqpin, rc); + goto err_gpioconfig; + } + rc = gpio_request(respin, "gpio_keybd_reset"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + respin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(rescfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + respin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(irqcfg, GPIO_CFG_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + irqpin, rc); + goto err_gpioconfig; + } + return rc; + +err_gpioconfig: + kbd_gpio_release(); + return rc; +} + +/* use gpio output pin to toggle keyboard external reset pin */ +static void kbd_hwreset(int kbd_mclrpin) +{ + gpio_direction_output(kbd_mclrpin, 0); + gpio_direction_output(kbd_mclrpin, 1); +} + +static struct msm_i2ckbd_platform_data msm_kybd_data = { + .hwrepeat = 0, + .scanset1 = 1, + .gpioreset = KBD_RST, + .gpioirq = KBD_IRQ, + .gpio_setup = kbd_gpio_setup, + .gpio_shutdown = kbd_gpio_release, + .hw_reset = kbd_hwreset, +}; + +static struct i2c_board_info msm_i2c_board_info[] __initdata = { + { + I2C_BOARD_INFO("glidesensor", 0x2A), + .irq = MSM_GPIO_TO_INT(TOUCHPAD_IRQ), + .platform_data = &msm_touchpad_data + }, + { + I2C_BOARD_INFO("msm-i2ckbd", 0x3A), + .type = "msm-i2ckbd", + .irq = MSM_GPIO_TO_INT(KBD_IRQ), + .platform_data = &msm_kybd_data + }, +#ifdef CONFIG_MT9D112 + { + I2C_BOARD_INFO("mt9d112", 0x78 >> 1), + }, +#endif +#ifdef CONFIG_S5K3E2FX + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, +#endif +#ifdef CONFIG_MT9P012 + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, +#endif +#ifdef CONFIG_MT9P012_KM + { + I2C_BOARD_INFO("mt9p012_km", 0x6C >> 2), + }, +#endif +#if defined(CONFIG_MT9T013) || defined(CONFIG_SENSORS_MT9T013) + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +#endif + { + I2C_BOARD_INFO("tps65023", 0x48), + }, +}; + +#ifdef CONFIG_MSM_CAMERA +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT0 */ + GPIO_CFG(1, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT1 */ + GPIO_CFG(2, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT2 */ + GPIO_CFG(3, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT3 */ + GPIO_CFG(4, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT4 */ + GPIO_CFG(5, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT5 */ + GPIO_CFG(6, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT6 */ + GPIO_CFG(7, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT7 */ + GPIO_CFG(8, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT8 */ + GPIO_CFG(9, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT9 */ + GPIO_CFG(10, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT10 */ + GPIO_CFG(11, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT11 */ + GPIO_CFG(12, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCLK */ + GPIO_CFG(13, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT0 */ + GPIO_CFG(1, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT1 */ + GPIO_CFG(2, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT2 */ + GPIO_CFG(3, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT3 */ + GPIO_CFG(4, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT4 */ + GPIO_CFG(5, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT5 */ + GPIO_CFG(6, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT6 */ + GPIO_CFG(7, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT7 */ + GPIO_CFG(8, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT8 */ + GPIO_CFG(9, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT9 */ + GPIO_CFG(10, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT10 */ + GPIO_CFG(11, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* DAT11 */ + GPIO_CFG(12, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* PCLK */ + GPIO_CFG(13, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_16MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_ffa_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(95, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_16MA), /* I2C_SCL */ + GPIO_CFG(96, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_16MA), /* I2C_SDA */ + /* FFA front Sensor Reset */ + GPIO_CFG(137, 1, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_16MA), +}; + +static uint32_t camera_off_gpio_ffa_table[] = { + /* FFA front Sensor Reset */ + GPIO_CFG(137, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_16MA), +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} + +static struct vreg *vreg_gp2; +static struct vreg *vreg_gp3; + +static void msm_camera_vreg_config(int vreg_en) +{ + int rc; + + if (vreg_gp2 == NULL) { + vreg_gp2 = vreg_get(NULL, "gp2"); + if (IS_ERR(vreg_gp2)) { + printk(KERN_ERR "%s: vreg_get(%s) failed (%ld)\n", + __func__, "gp2", PTR_ERR(vreg_gp2)); + return; + } + + rc = vreg_set_level(vreg_gp2, 1800); + if (rc) { + printk(KERN_ERR "%s: GP2 set_level failed (%d)\n", + __func__, rc); + } + } + + if (vreg_gp3 == NULL) { + vreg_gp3 = vreg_get(NULL, "gp3"); + if (IS_ERR(vreg_gp3)) { + printk(KERN_ERR "%s: vreg_get(%s) failed (%ld)\n", + __func__, "gp3", PTR_ERR(vreg_gp3)); + return; + } + + rc = vreg_set_level(vreg_gp3, 2800); + if (rc) { + printk(KERN_ERR "%s: GP3 set level failed (%d)\n", + __func__, rc); + } + } + + if (vreg_en) { + rc = vreg_enable(vreg_gp2); + if (rc) { + printk(KERN_ERR "%s: GP2 enable failed (%d)\n", + __func__, rc); + } + + rc = vreg_enable(vreg_gp3); + if (rc) { + printk(KERN_ERR "%s: GP3 enable failed (%d)\n", + __func__, rc); + } + } else { + rc = vreg_disable(vreg_gp2); + if (rc) { + printk(KERN_ERR "%s: GP2 disable failed (%d)\n", + __func__, rc); + } + + rc = vreg_disable(vreg_gp3); + if (rc) { + printk(KERN_ERR "%s: GP3 disable failed (%d)\n", + __func__, rc); + } + } +} + +static int config_camera_on_gpios(void) +{ + int vreg_en = 1; + + if (machine_is_qsd8x50_ffa()) { + config_gpio_table(camera_on_gpio_ffa_table, + ARRAY_SIZE(camera_on_gpio_ffa_table)); + + msm_camera_vreg_config(vreg_en); + gpio_set_value(137, 0); + } + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + return 0; +} + +static void config_camera_off_gpios(void) +{ + int vreg_en = 0; + + if (machine_is_qsd8x50_ffa()) { + config_gpio_table(camera_off_gpio_ffa_table, + ARRAY_SIZE(camera_off_gpio_ffa_table)); + + msm_camera_vreg_config(vreg_en); + } + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static struct resource msm_camera_resources[] = { + { + .start = 0xA0F00000, + .end = 0xA0F00000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VFE, + .end = INT_VFE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +int pmic_set_flash_led_current(enum pmic8058_leds id, unsigned mA) +{ + int rc; + rc = pmic_flash_led_set_current(mA); + return rc; +} +static struct msm_camera_sensor_flash_src msm_flash_src = { + .flash_sr_type = MSM_CAMERA_FLASH_SRC_PMIC, + ._fsrc.pmic_src.num_of_src = 1, + ._fsrc.pmic_src.low_current = 30, + ._fsrc.pmic_src.high_current = 100, + ._fsrc.pmic_src.led_src_1 = 0, + ._fsrc.pmic_src.led_src_2 = 0, + ._fsrc.pmic_src.pmic_set_current = pmic_set_flash_led_current, +}; + +#ifdef CONFIG_MT9D112 +static struct msm_camera_sensor_flash_data flash_mt9d112 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = { + .sensor_name = "mt9d112", + .sensor_reset = 17, + .sensor_pwd = 85, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9d112 +}; + +static struct platform_device msm_camera_sensor_mt9d112 = { + .name = "msm_camera_mt9d112", + .dev = { + .platform_data = &msm_camera_sensor_mt9d112_data, + }, +}; +#endif + +#ifdef CONFIG_S5K3E2FX +static struct msm_camera_sensor_flash_data flash_s5k3e2fx = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { + .sensor_name = "s5k3e2fx", + .sensor_reset = 17, + .sensor_pwd = 85, + /*.vcm_pwd = 31, */ /* CAM1_VCM_EN, enabled in a9 */ + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_s5k3e2fx +}; + +static struct platform_device msm_camera_sensor_s5k3e2fx = { + .name = "msm_camera_s5k3e2fx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3e2fx_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012 +static struct msm_camera_sensor_flash_data flash_mt9p012 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { + .sensor_name = "mt9p012", + .sensor_reset = 17, + .sensor_pwd = 85, + .vcm_pwd = 88, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9p012 +}; + +static struct platform_device msm_camera_sensor_mt9p012 = { + .name = "msm_camera_mt9p012", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012_KM +static struct msm_camera_sensor_flash_data flash_mt9p012_km = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_km_data = { + .sensor_name = "mt9p012_km", + .sensor_reset = 17, + .sensor_pwd = 85, + .vcm_pwd = 88, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9p012_km +}; + +static struct platform_device msm_camera_sensor_mt9p012_km = { + .name = "msm_camera_mt9p012_km", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_km_data, + }, +}; +#endif + +#ifdef CONFIG_MT9T013 +static struct msm_camera_sensor_flash_data flash_mt9t013 = { + .flash_type = MSM_CAMERA_FLASH_LED, + .flash_src = &msm_flash_src +}; + +static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { + .sensor_name = "mt9t013", + .sensor_reset = 17, + .sensor_pwd = 85, + .vcm_pwd = 0, + .vcm_enable = 0, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_data = &flash_mt9t013 +}; + +static struct platform_device msm_camera_sensor_mt9t013 = { + .name = "msm_camera_mt9t013", + .dev = { + .platform_data = &msm_camera_sensor_mt9t013_data, + }, +}; +#endif +#endif /*CONFIG_MSM_CAMERA*/ + +static u32 msm_calculate_batt_capacity(u32 current_voltage); + +static struct msm_psy_batt_pdata msm_psy_batt_data = { + .voltage_min_design = 3200, + .voltage_max_design = 4200, + .avail_chg_sources = AC_CHG | USB_CHG , + .batt_technology = POWER_SUPPLY_TECHNOLOGY_LION, + .calculate_capacity = &msm_calculate_batt_capacity, +}; + +static u32 msm_calculate_batt_capacity(u32 current_voltage) +{ + u32 low_voltage = msm_psy_batt_data.voltage_min_design; + u32 high_voltage = msm_psy_batt_data.voltage_max_design; + + return (current_voltage - low_voltage) * 100 + / (high_voltage - low_voltage); +} + +static struct platform_device msm_batt_device = { + .name = "msm-battery", + .id = -1, + .dev.platform_data = &msm_psy_batt_data, +}; + +static int hsusb_rpc_connect(int connect) +{ + if (connect) + return msm_hsusb_rpc_connect(); + else + return msm_hsusb_rpc_close(); +} + +static int msm_hsusb_pmic_notif_init(void (*callback)(int online), int init) +{ + int ret; + + if (init) { + ret = msm_pm_app_rpc_init(callback); + } else { + msm_pm_app_rpc_deinit(callback); + ret = 0; + } + return ret; +} +static int msm_hsusb_ldo_init(int init); +static int msm_hsusb_ldo_enable(int enable); + +static struct msm_otg_platform_data msm_otg_pdata = { + .rpc_connect = hsusb_rpc_connect, + .pmic_vbus_notif_init = msm_hsusb_pmic_notif_init, + .pemp_level = PRE_EMPHASIS_WITH_10_PERCENT, + .cdr_autoreset = CDR_AUTO_RESET_DEFAULT, + .drv_ampl = HS_DRV_AMPLITUDE_5_PERCENT, + .vbus_power = msm_hsusb_vbus_power, + .chg_vbus_draw = hsusb_chg_vbus_draw, + .chg_connected = hsusb_chg_connected, + .chg_init = hsusb_chg_init, + .phy_can_powercollapse = 1, + .ldo_init = msm_hsusb_ldo_init, + .ldo_enable = msm_hsusb_ldo_enable, +}; + +static struct msm_hsusb_gadget_platform_data msm_gadget_pdata; + +static struct platform_device *devices[] __initdata = { + &msm_fb_device, + &mddi_toshiba_device, + &smc91x_device, + &msm_device_smd, + &msm_device_dmov, + &android_pmem_kernel_ebi1_device, +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION + &android_pmem_kernel_smi_device, +#endif + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_smipool_device, + &msm_device_nand, + &msm_device_i2c, + &qsd_device_spi, +#ifdef CONFIG_USB_FUNCTION + &mass_storage_device, +#endif +#ifdef CONFIG_USB_ANDROID + &usb_mass_storage_device, + &rndis_device, +#ifdef CONFIG_USB_ANDROID_DIAG + &usb_diag_device, +#endif +#ifdef CONFIG_USB_F_SERIAL + &usb_gadget_fserial_device, +#endif + &android_usb_device, +#endif + &msm_device_tssc, + &msm_audio_device, + &msm_device_uart_dm1, + &msm_bluesleep_device, +#ifdef CONFIG_BT + &msm_bt_power_device, +#endif +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart3, +#endif + &msm_device_pmic_leds, + &msm_kgsl_3d0, + &hs_device, +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + &msm_device_tsif, +#endif +#ifdef CONFIG_MT9T013 + &msm_camera_sensor_mt9t013, +#endif +#ifdef CONFIG_MT9D112 + &msm_camera_sensor_mt9d112, +#endif +#ifdef CONFIG_S5K3E2FX + &msm_camera_sensor_s5k3e2fx, +#endif +#ifdef CONFIG_MT9P012 + &msm_camera_sensor_mt9p012, +#endif +#ifdef CONFIG_MT9P012_KM + &msm_camera_sensor_mt9p012_km, +#endif + &msm_batt_device, +}; + +static void __init qsd8x50_init_irq(void) +{ + msm_init_irq(); + msm_init_sirc(); +} + +static void usb_mpp_init(void) +{ + unsigned rc; + unsigned mpp_usb = 20; + + if (machine_is_qsd8x50_ffa()) { + rc = mpp_config_digital_out(mpp_usb, + MPP_CFG(MPP_DLOGIC_LVL_VDD, + MPP_DLOGIC_OUT_CTRL_HIGH)); + if (rc) + pr_err("%s: configuring mpp pin" + "to enable 3.3V LDO failed\n", __func__); + } +} + +/* TBD: 8x50 FFAs have internal 3p3 voltage regulator as opposed to + * external 3p3 voltage regulator on Surf platform. There is no way + * s/w can detect fi concerned regulator is internal or external to + * to MSM. Internal 3p3 regulator is powered through boost voltage + * regulator where as external 3p3 regulator is powered through VPH. + * So for internal voltage regulator it is required to power on + * boost voltage regulator first. Unfortunately some of the FFAs are + * re-worked to install external 3p3 regulator. For now, assuming all + * FFAs have 3p3 internal regulators and all SURFs have external 3p3 + * regulator as there is no way s/w can determine if theregulator is + * internal or external. May be, we can implement this flag as kernel + * boot parameters so that we can change code behaviour dynamically + */ +static int regulator_3p3_is_internal; +static struct vreg *vreg_5v; +static struct vreg *vreg_3p3; +static int msm_hsusb_ldo_init(int init) +{ + if (init) { + if (regulator_3p3_is_internal) { + vreg_5v = vreg_get(NULL, "boost"); + if (IS_ERR(vreg_5v)) + return PTR_ERR(vreg_5v); + vreg_set_level(vreg_5v, 5000); + } + + vreg_3p3 = vreg_get(NULL, "usb"); + if (IS_ERR(vreg_3p3)) + return PTR_ERR(vreg_3p3); + vreg_set_level(vreg_3p3, 3300); + } else { + if (regulator_3p3_is_internal) + vreg_put(vreg_5v); + vreg_put(vreg_3p3); } return 0; } -module_init(msm_init_smc91x); -static int hsusb_phy_init_seq[] = { - 0x08, 0x31, /* Increase HS Driver Amplitude */ - 0x20, 0x32, /* Enable and set Pre-Emphasis Depth to 10% */ - -1 -}; +static int msm_hsusb_ldo_enable(int enable) +{ + static int ldo_status; + int ret; -static struct msm_otg_platform_data msm_otg_pdata = { - .phy_init_seq = hsusb_phy_init_seq, - .mode = USB_PERIPHERAL, - .otg_control = OTG_PHY_CONTROL, -}; + if (ldo_status == enable) + return 0; -static struct platform_device *devices[] __initdata = { - &msm_device_uart3, - &msm_device_smd, - &msm_device_otg, - &msm_device_hsusb, - &msm_device_hsusb_host, -}; + if (regulator_3p3_is_internal && (!vreg_5v || IS_ERR(vreg_5v))) + return -ENODEV; + if (!vreg_3p3 || IS_ERR(vreg_3p3)) + return -ENODEV; -static struct msm_mmc_gpio sdc1_gpio_cfg[] = { - {51, "sdc1_dat_3"}, - {52, "sdc1_dat_2"}, - {53, "sdc1_dat_1"}, - {54, "sdc1_dat_0"}, - {55, "sdc1_cmd"}, - {56, "sdc1_clk"} -}; + ldo_status = enable; + + if (enable) { + if (regulator_3p3_is_internal) { + ret = vreg_enable(vreg_5v); + if (ret) + return ret; + + /* power supply to 3p3 regulator can vary from + * USB VBUS or VREG 5V. If the power supply is + * USB VBUS cable disconnection cannot be + * deteted. Select power supply to VREG 5V + */ + /* TBD: comeup with a better name */ + ret = pmic_vote_3p3_pwr_sel_switch(1); + if (ret) + return ret; + } + ret = vreg_enable(vreg_3p3); + + return ret; + } else { + if (regulator_3p3_is_internal) { + ret = vreg_disable(vreg_5v); + if (ret) + return ret; + ret = pmic_vote_3p3_pwr_sel_switch(0); + if (ret) + return ret; + } + ret = vreg_disable(vreg_3p3); + + return ret; + } +} + +static void __init qsd8x50_init_usb(void) +{ + usb_mpp_init(); + + if (machine_is_qsd8x50_ffa()) + regulator_3p3_is_internal = 1; + +#ifdef CONFIG_USB_MSM_OTG_72K + platform_device_register(&msm_device_otg); +#endif + +#ifdef CONFIG_USB_FUNCTION_MSM_HSUSB + platform_device_register(&msm_device_hsusb_peripheral); +#endif + +#ifdef CONFIG_USB_MSM_72K + platform_device_register(&msm_device_gadget_peripheral); +#endif + + if (machine_is_qsd8x50_ffa()) + return; + + vreg_usb = vreg_get(NULL, "boost"); + + if (IS_ERR(vreg_usb)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_usb)); + return; + } + + platform_device_register(&msm_device_hsusb_otg); + msm_add_host(0, &msm_usb_host_pdata); +#ifdef CONFIG_USB_FS_HOST + if (fsusb_gpio_init()) + return; + msm_add_host(1, &msm_usb_host2_pdata); +#endif +} static struct vreg *vreg_mmc; -static unsigned long vreg_sts; + +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC4_SUPPORT)) + +struct sdcc_gpio { + struct msm_gpio *cfg_data; + uint32_t size; +}; + +static struct msm_gpio sdc1_cfg_data[] = { + {GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_3"}, + {GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_2"}, + {GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_1"}, + {GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_dat_0"}, + {GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc1_cmd"}, + {GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc1_clk"}, +}; + +static struct msm_gpio sdc2_cfg_data[] = { + {GPIO_CFG(62, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc2_clk"}, + {GPIO_CFG(63, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_cmd"}, + {GPIO_CFG(64, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_3"}, + {GPIO_CFG(65, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_2"}, + {GPIO_CFG(66, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_1"}, + {GPIO_CFG(67, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc2_dat_0"}, +}; + +static struct msm_gpio sdc3_cfg_data[] = { + {GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc3_clk"}, + {GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_cmd"}, + {GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_3"}, + {GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_2"}, + {GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_1"}, + {GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_0"}, + +#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT + {GPIO_CFG(158, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_4"}, + {GPIO_CFG(159, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_5"}, + {GPIO_CFG(160, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_6"}, + {GPIO_CFG(161, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc3_dat_7"}, +#endif +}; + +static struct msm_gpio sdc4_cfg_data[] = { + {GPIO_CFG(142, 3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "sdc4_clk"}, + {GPIO_CFG(143, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_cmd"}, + {GPIO_CFG(144, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_0"}, + {GPIO_CFG(145, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_1"}, + {GPIO_CFG(146, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_2"}, + {GPIO_CFG(147, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_8MA), "sdc4_dat_3"}, +}; + +static struct sdcc_gpio sdcc_cfg_data[] = { + { + .cfg_data = sdc1_cfg_data, + .size = ARRAY_SIZE(sdc1_cfg_data), + }, + { + .cfg_data = sdc2_cfg_data, + .size = ARRAY_SIZE(sdc2_cfg_data), + }, + { + .cfg_data = sdc3_cfg_data, + .size = ARRAY_SIZE(sdc3_cfg_data), + }, + { + .cfg_data = sdc4_cfg_data, + .size = ARRAY_SIZE(sdc4_cfg_data), + }, +}; + +static unsigned long vreg_sts, gpio_sts; + +static void msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int rc = 0; + struct sdcc_gpio *curr; + + curr = &sdcc_cfg_data[dev_id - 1]; + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return; + + if (enable) { + set_bit(dev_id, &gpio_sts); + rc = msm_gpios_request_enable(curr->cfg_data, curr->size); + if (rc) + printk(KERN_ERR "%s: Failed to turn on GPIOs for slot %d\n", + __func__, dev_id); + } else { + clear_bit(dev_id, &gpio_sts); + msm_gpios_disable_free(curr->cfg_data, curr->size); + } +} static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) { @@ -116,6 +2087,7 @@ static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) struct platform_device *pdev; pdev = container_of(dv, struct platform_device, dev); + msm_sdcc_setup_gpio(pdev->id, !!vdd); if (vdd == 0) { if (!vreg_sts) @@ -126,69 +2098,431 @@ static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) if (!vreg_sts) { rc = vreg_disable(vreg_mmc); if (rc) - pr_err("vreg_mmc disable failed for slot " - "%d: %d\n", pdev->id, rc); + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); } return 0; } if (!vreg_sts) { - rc = vreg_set_level(vreg_mmc, 2900); + rc = vreg_set_level(vreg_mmc, PMIC_VREG_GP6_LEVEL); + if (!rc) + rc = vreg_enable(vreg_mmc); if (rc) - pr_err("vreg_mmc set level failed for slot %d: %d\n", - pdev->id, rc); - rc = vreg_enable(vreg_mmc); - if (rc) - pr_err("vreg_mmc enable failed for slot %d: %d\n", - pdev->id, rc); + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); } set_bit(pdev->id, &vreg_sts); return 0; } -static struct msm_mmc_gpio_data sdc1_gpio = { - .gpio = sdc1_gpio_cfg, - .size = ARRAY_SIZE(sdc1_gpio_cfg), -}; +#endif +#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\ + || defined(CONFIG_MMC_MSM_SDC4_SUPPORT)) -static struct msm_mmc_platform_data qsd8x50_sdc1_data = { +static int msm_sdcc_get_wpswitch(struct device *dv) +{ + void __iomem *wp_addr = 0; + uint32_t ret = 0; + struct platform_device *pdev; + + if (!machine_is_qsd8x50_surf()) + return -1; + + pdev = container_of(dv, struct platform_device, dev); + + wp_addr = ioremap(FPGA_SDCC_STATUS, 4); + if (!wp_addr) { + pr_err("%s: Could not remap %x\n", __func__, FPGA_SDCC_STATUS); + return -ENOMEM; + } + + ret = (readl(wp_addr) >> ((pdev->id - 1) << 1)) & (0x03); + pr_info("%s: WP/CD Status for Slot %d = 0x%x \n", __func__, + pdev->id, ret); + iounmap(wp_addr); + return ((ret == 0x02) ? 1 : 0); + +} +#endif + +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT +static struct mmc_platform_data qsd8x50_sdc1_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, .translate_vdd = msm_sdcc_setup_power, - .gpio_data = &sdc1_gpio, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .wpswitch = msm_sdcc_get_wpswitch, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 25000000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, }; +#endif + +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT +static struct mmc_platform_data qsd8x50_sdc2_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .wpswitch = msm_sdcc_get_wpswitch, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 25000000, + .msmsdcc_fmax = 49152000, + .nonremovable = 1, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT +static struct mmc_platform_data qsd8x50_sdc3_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT + .mmc_bus_width = MMC_CAP_8_BIT_DATA, +#else + .mmc_bus_width = MMC_CAP_4_BIT_DATA, +#endif + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 25000000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif + +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT +static struct mmc_platform_data qsd8x50_sdc4_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, + .mmc_bus_width = MMC_CAP_4_BIT_DATA, + .wpswitch = msm_sdcc_get_wpswitch, + .msmsdcc_fmin = 144000, + .msmsdcc_fmid = 25000000, + .msmsdcc_fmax = 49152000, + .nonremovable = 0, +}; +#endif static void __init qsd8x50_init_mmc(void) { - vreg_mmc = vreg_get(NULL, "gp5"); + if (machine_is_qsd8x50_ffa()) + vreg_mmc = vreg_get(NULL, "gp6"); + else + vreg_mmc = vreg_get(NULL, "gp5"); if (IS_ERR(vreg_mmc)) { - pr_err("vreg get for vreg_mmc failed (%ld)\n", - PTR_ERR(vreg_mmc)); + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_mmc)); return; } - msm_add_sdcc(1, &qsd8x50_sdc1_data, 0, 0); +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + msm_add_sdcc(1, &qsd8x50_sdc1_data); +#endif + + if (machine_is_qsd8x50_surf()) { +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + msm_add_sdcc(2, &qsd8x50_sdc2_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + msm_add_sdcc(3, &qsd8x50_sdc3_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + msm_add_sdcc(4, &qsd8x50_sdc4_data); +#endif + } + +} + +static void __init qsd8x50_cfg_smc91x(void) +{ + int rc = 0; + + if (machine_is_qsd8x50_surf()) { + smc91x_resources[0].start = 0x70000300; + smc91x_resources[0].end = 0x700003ff; + smc91x_resources[1].start = MSM_GPIO_TO_INT(156); + smc91x_resources[1].end = MSM_GPIO_TO_INT(156); + } else if (machine_is_qsd8x50_ffa()) { + smc91x_resources[0].start = 0x84000300; + smc91x_resources[0].end = 0x840003ff; + smc91x_resources[1].start = MSM_GPIO_TO_INT(87); + smc91x_resources[1].end = MSM_GPIO_TO_INT(87); + + rc = gpio_tlmm_config(GPIO_CFG(87, 0, GPIO_CFG_INPUT, + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), + GPIO_CFG_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config=%d\n", + __func__, rc); + } + } else + printk(KERN_ERR "%s: invalid machine type\n", __func__); +} + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 8594, + .residency = 23740, + }, + + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 4594, + .residency = 23740, + }, + + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 1, + .latency = 443, + .residency = 1098, + }, + + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + .latency = 2, + .residency = 0, + }, +}; + +static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = { + .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT, + .v_addr = (unsigned int *)PAGE_OFFSET, +}; + +static void +msm_i2c_gpio_config(int iface, int config_type) +{ + int gpio_scl; + int gpio_sda; + if (iface) { + gpio_scl = 60; + gpio_sda = 61; + } else { + gpio_scl = 95; + gpio_sda = 96; + } + if (config_type) { + gpio_tlmm_config(GPIO_CFG(gpio_scl, 1, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + gpio_tlmm_config(GPIO_CFG(gpio_sda, 1, GPIO_CFG_INPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + } else { + gpio_tlmm_config(GPIO_CFG(gpio_scl, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + gpio_tlmm_config(GPIO_CFG(gpio_sda, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_16MA), GPIO_CFG_ENABLE); + } +} + +static struct msm_i2c_platform_data msm_i2c_pdata = { + .clk_freq = 100000, + .rsl_id = SMEM_SPINLOCK_I2C, + .pri_clk = 95, + .pri_dat = 96, + .aux_clk = 60, + .aux_dat = 61, + .msm_i2c_config_gpio = msm_i2c_gpio_config, +}; + +static void __init msm_device_i2c_init(void) +{ + if (gpio_request(95, "i2c_pri_clk")) + pr_err("failed to request gpio i2c_pri_clk\n"); + if (gpio_request(96, "i2c_pri_dat")) + pr_err("failed to request gpio i2c_pri_dat\n"); + if (gpio_request(60, "i2c_sec_clk")) + pr_err("failed to request gpio i2c_sec_clk\n"); + if (gpio_request(61, "i2c_sec_dat")) + pr_err("failed to request gpio i2c_sec_dat\n"); + + msm_i2c_pdata.rmutex = 1; + msm_i2c_pdata.pm_lat = + msm_pm_data[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] + .latency; + msm_device_i2c.dev.platform_data = &msm_i2c_pdata; +} + +static unsigned pmem_kernel_ebi1_size = PMEM_KERNEL_EBI1_SIZE; +static int __init pmem_kernel_ebi1_size_setup(char *p) +{ + pmem_kernel_ebi1_size = memparse(p, NULL); + return 0; +} +early_param("pmem_kernel_ebi1_size", pmem_kernel_ebi1_size_setup); + +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION +static unsigned pmem_kernel_smi_size = MSM_PMEM_SMIPOOL_SIZE; +static int __init pmem_kernel_smi_size_setup(char *p) +{ + pmem_kernel_smi_size = memparse(p, NULL); + + /* Make sure that we don't allow more SMI memory then is + available - the kernel mapping code has no way of knowing + if it has gone over the edge */ + + if (pmem_kernel_smi_size > MSM_PMEM_SMIPOOL_SIZE) + pmem_kernel_smi_size = MSM_PMEM_SMIPOOL_SIZE; + return 0; +} +early_param("pmem_kernel_smi_size", pmem_kernel_smi_size_setup); +#endif + +static unsigned pmem_sf_size = MSM_PMEM_SF_SIZE; +static int __init pmem_sf_size_setup(char *p) +{ + pmem_sf_size = memparse(p, NULL); + return 0; +} +early_param("pmem_sf_size", pmem_sf_size_setup); + +static unsigned pmem_adsp_size = MSM_PMEM_ADSP_SIZE; +static int __init pmem_adsp_size_setup(char *p) +{ + pmem_adsp_size = memparse(p, NULL); + return 0; +} +early_param("pmem_adsp_size", pmem_adsp_size_setup); + + +static unsigned audio_size = MSM_AUDIO_SIZE; +static int __init audio_size_setup(char *p) +{ + audio_size = memparse(p, NULL); + return 0; +} +early_param("audio_size", audio_size_setup); + +static void __init qsd8x50_init(void) +{ + msm_clock_init(&qds8x50_clock_init_data); + qsd8x50_cfg_smc91x(); + acpuclk_init(&acpuclk_8x50_soc_data); + + msm_hsusb_pdata.swfi_latency = + msm_pm_data + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_hsusb_peripheral.dev.platform_data = &msm_hsusb_pdata; + + msm_otg_pdata.swfi_latency = + msm_pm_data + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency; + msm_device_otg.dev.platform_data = &msm_otg_pdata; + msm_device_gadget_peripheral.dev.platform_data = &msm_gadget_pdata; + msm_gadget_pdata.is_phy_status_timer_on = 1; + +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + msm_device_tsif.dev.platform_data = &tsif_platform_data; +#endif + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_fb_add_devices(); +#ifdef CONFIG_MSM_CAMERA + config_camera_off_gpios(); /* might not be necessary */ +#endif + qsd8x50_init_usb(); + qsd8x50_init_mmc(); + bt_power_init(); + audio_gpio_init(); + msm_device_i2c_init(); + msm_qsd_spi_init(); + i2c_register_board_info(0, msm_i2c_board_info, + ARRAY_SIZE(msm_i2c_board_info)); + spi_register_board_info(msm_spi_board_info, + ARRAY_SIZE(msm_spi_board_info)); + msm_pm_set_platform_data(msm_pm_data, ARRAY_SIZE(msm_pm_data)); + BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata)); + msm_pm_register_irqs(); + +#ifdef CONFIG_SURF_FFA_GPIO_KEYPAD + if (machine_is_qsd8x50_ffa()) + platform_device_register(&keypad_device_8k_ffa); + else + platform_device_register(&keypad_device_surf); +#endif +} + +static void __init qsd8x50_allocate_memory_regions(void) +{ + void *addr; + unsigned long size; + + size = pmem_kernel_ebi1_size; + if (size) { + addr = alloc_bootmem_align(size, 0x100000); + android_pmem_kernel_ebi1_pdata.size = size; + pr_info("allocating %lu bytes at %p (%lx physical) for kernel" + " ebi1 pmem arena\n", size, addr, __pa(addr)); + } + +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION + size = pmem_kernel_smi_size; + if (size > MSM_PMEM_SMIPOOL_SIZE) { + printk(KERN_ERR "pmem kernel smi arena size %lu is too big\n", + size); + + size = MSM_PMEM_SMIPOOL_SIZE; + } + + android_pmem_kernel_smi_pdata.size = size; + + pr_info("allocating %lu bytes at %lx (%lx physical)" + "for pmem kernel smi arena\n", size, + (long unsigned int) MSM_PMEM_SMIPOOL_BASE, + __pa(MSM_PMEM_SMIPOOL_BASE)); +#endif + + size = pmem_sf_size; + if (size) { + addr = alloc_bootmem(size); + android_pmem_pdata.size = size; + pr_info("allocating %lu bytes at %p (%lx physical) for sf " + "pmem arena\n", size, addr, __pa(addr)); + } + + size = pmem_adsp_size; + if (size) { + addr = alloc_bootmem(size); + android_pmem_adsp_pdata.size = size; + pr_info("allocating %lu bytes at %p (%lx physical) for adsp " + "pmem arena\n", size, addr, __pa(addr)); + } + + + size = MSM_FB_SIZE; + addr = (void *)MSM_FB_BASE; + msm_fb_resources[0].start = (unsigned long)addr; + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + pr_info("using %lu bytes of SMI at %lx physical for fb\n", + size, (unsigned long)addr); + + size = audio_size ? : MSM_AUDIO_SIZE; + addr = alloc_bootmem(size); + msm_audio_resources[0].start = __pa(addr); + msm_audio_resources[0].end = msm_audio_resources[0].start + size - 1; + pr_info("allocating %lu bytes at %p (%lx physical) for audio\n", + size, addr, __pa(addr)); } static void __init qsd8x50_map_io(void) { + msm_shared_ram_phys = MSM_SHARED_RAM_PHYS; msm_map_qsd8x50_io(); - msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); -} - -static void __init qsd8x50_init_irq(void) -{ - msm_init_irq(); - msm_init_sirc(); -} - -static void __init qsd8x50_init(void) -{ - msm_device_otg.dev.platform_data = &msm_otg_pdata; - msm_device_hsusb.dev.parent = &msm_device_otg.dev; - msm_device_hsusb_host.dev.parent = &msm_device_otg.dev; - platform_add_devices(devices, ARRAY_SIZE(devices)); - qsd8x50_init_mmc(); + qsd8x50_allocate_memory_regions(); + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n", + __func__); } MACHINE_START(QSD8X50_SURF, "QCT QSD8X50 SURF") @@ -199,7 +2533,7 @@ MACHINE_START(QSD8X50_SURF, "QCT QSD8X50 SURF") .timer = &msm_timer, MACHINE_END -MACHINE_START(QSD8X50A_ST1_5, "QCT QSD8X50A ST1.5") +MACHINE_START(QSD8X50_FFA, "QCT QSD8X50 FFA") .atag_offset = 0x100, .map_io = qsd8x50_map_io, .init_irq = qsd8x50_init_irq, diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c new file mode 100644 index 00000000000..375440c5afb --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-gpio.c @@ -0,0 +1,326 @@ +/* arch/arm/mach-msm/board-sapphire-gpio.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gpio_chip.h" +#include "board-sapphire.h" + +#ifdef DEBUG_SAPPHIRE_GPIO +#define DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ## arg) +#else +#define DBG(fmt, arg...) do {} while (0) +#endif + +#define SAPPHIRE_CPLD_INT_STATUS (SAPPHIRE_CPLD_BASE + 0x0E) +#define SAPPHIRE_CPLD_INT_LEVEL (SAPPHIRE_CPLD_BASE + 0x08) +#define SAPPHIRE_CPLD_INT_MASK (SAPPHIRE_CPLD_BASE + 0x0C) + +/*CPLD misc reg offset*/ +static const int _g_CPLD_MISCn_Offset[] = { 0x0A, /*misc1 reg*/ + 0x00, /*misc2 reg*/ + 0x02, /*misc3 reg*/ + 0x04, /*misc4 reg*/ + 0x06}; /*misc5 reg*/ +/*CPLD INT Bank*/ +/*BANK0: int1 status, int2 level, int3 mask*/ +static const int _g_INT_BANK_Offset[][3] = {{0x0E, 0x08, 0x0C} }; + +static uint8_t sapphire_cpld_initdata[4] = { + [0] = 0x80, /* for serial debug UART3, low current misc2*/ + [1] = 0x34, /* jog & tp enable, I2C pull misc3*/ + [3] = 0x04, /* mmdi 32k en misc5*/ +}; + +/*save current working int mask, so the value can be restored after resume. +Sapphire has only bank0.*/ +static uint8_t sapphire_int_mask[] = { + [0] = 0xfb, /* enable all interrupts, bit 2 is not used */ +}; + +/*Sleep have to prepare the wake up source in advance. +default to disable all wakeup sources when suspend.*/ +static uint8_t sapphire_sleep_int_mask[] = { + [0] = 0x00, /* bit2 is not used */ +}; + +static int sapphire_suspended; + +static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n) +{ + if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/ + return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n)); + else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/ + return !!(readb(CPLD_INT_LEVEL_REG_G(n)) & + CPLD_GPIO_BIT_POS_MASK(n)); + return 0; +} + +/*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6 +Reading from write-only registers is undefined, so the writing value +should be kept in shadow for later usage.*/ +int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +{ + unsigned long flags; + uint8_t reg_val; + if (n > SAPPHIRE_GPIO_END) + return -1; + + local_irq_save(flags); + reg_val = readb(CPLD_GPIO_REG(n)); + if (on) + reg_val |= CPLD_GPIO_BIT_POS_MASK(n); + else + reg_val &= ~CPLD_GPIO_BIT_POS_MASK(n); + writeb(reg_val, CPLD_GPIO_REG(n)); + + DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); + + return 0; +} + +static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio, + unsigned long flags) +{ + if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) + sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); + + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); + + return 0; +} + +static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, + unsigned int *irqp, unsigned long *irqnumflagsp) +{ + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); + DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n", + SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT); + if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) || + (gpio > SAPPHIRE_GPIO_LAST_INT)) + return -ENOENT; + *irqp = SAPPHIRE_GPIO_TO_INT(gpio); + DBG("*irqp=%d\r\n", *irqp); + if (irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + +/*write 1 to clear INT status bit.*/ +static void sapphire_gpio_irq_ack(unsigned int irq) +{ + /*write 1 to clear*/ + writeb(SAPPHIRE_INT_BIT_MASK(irq), CPLD_INT_STATUS_REG(irq)); +} + +/*unmask/enable the INT +static void sapphire_gpio_irq_unmask(unsigned int irq)*/ +static void sapphire_gpio_irq_enable(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + + local_irq_save(flags); /*disabling all interrupts*/ + + reg_val = readb(CPLD_INT_MASK_REG(irq)) | SAPPHIRE_INT_BIT_MASK(irq); + DBG("(irq=%d,0x%x, 0x%x)\r\n", irq, CPLD_INT_MASK_REG(irq), + SAPPHIRE_INT_BIT_MASK(irq)); + DBG("sapphire_suspended=%d\r\n", sapphire_suspended); + /*printk(KERN_INFO "sapphire_gpio_irq_mask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + if (!sapphire_suspended) + writeb(reg_val, CPLD_INT_MASK_REG(irq)); + + reg_val = readb(CPLD_INT_MASK_REG(irq)); + DBG("reg_val= 0x%x\r\n", reg_val); + DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); /*restore the interrupts*/ +} + +/*mask/disable INT +static void sapphire_gpio_irq_mask(unsigned int irq)*/ +static void sapphire_gpio_irq_disable(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + + local_irq_save(flags); + reg_val = readb(CPLD_INT_MASK_REG(irq)) & ~SAPPHIRE_INT_BIT_MASK(irq); + /*CPLD INT MASK is r/w now.*/ + + /*printk(KERN_INFO "sapphire_gpio_irq_unmask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + DBG("(%d,0x%x, 0x%x, 0x%x)\r\n", irq, reg_val, CPLD_INT_MASK_REG(irq), + SAPPHIRE_INT_BIT_MASK(irq)); + DBG("sapphire_suspended=%d\r\n", sapphire_suspended); + if (!sapphire_suspended) + writeb(reg_val, CPLD_INT_MASK_REG(irq)); + + reg_val = readb(CPLD_INT_MASK_REG(irq)); + DBG("reg_val= 0x%x\r\n", reg_val); + DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); +} + +/*preparing enable/disable wake source before sleep*/ +int sapphire_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + unsigned long flags; + uint8_t mask = SAPPHIRE_INT_BIT_MASK(irq); + + local_irq_save(flags); + + if (on) /*wake on -> mask the bit*/ + sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] |= mask; + else /*no wake -> unmask the bit*/ + sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] &= ~mask; + local_irq_restore(flags); + return 0; +} + +/*Sapphire has only one INT Bank.*/ +static void sapphire_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + int j; + unsigned v; + int int_base = SAPPHIRE_INT_START; + + v = readb(SAPPHIRE_CPLD_INT_STATUS); /*INT1 status reg, BANK0*/ + + for (j = 0; j < 8 ; j++) { /*8 bit per bank*/ + if (v & (1U << j)) { /*got the INT Bit*/ + DBG("generic_handle_irq j=0x%x\r\n", j); + generic_handle_irq(int_base + j); + } + } + + desc->chip->ack(irq); /*clear CPLD INT in SOC side.*/ + DBG("irq=%d, l=0x%x\r\n", irq, readb(SAPPHIRE_CPLD_INT_LEVEL)); +} + +/*Save current working sources before sleep, so we can restore it after + * resume.*/ +static int sapphire_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + sapphire_suspended = 1; + /*save current masking*/ + sapphire_int_mask[0] = readb(SAPPHIRE_CPLD_BASE + + SAPPHIRE_GPIO_INT_B0_MASK_REG); + + /*set waking source before sleep.*/ + writeb(sapphire_sleep_int_mask[0], + SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG); + + return 0; +} + +/*All the registers will be kept till a power loss...*/ +int sapphire_sysdev_resume(struct sys_device *dev) +{ + /*restore the working mask saved before sleep*/ + writeb(sapphire_int_mask[0], SAPPHIRE_CPLD_BASE + + SAPPHIRE_GPIO_INT_B0_MASK_REG); + sapphire_suspended = 0; + return 0; +} + +/** + * linux/irq.h :: struct irq_chip + * @enable: enable the interrupt (defaults to chip->unmask if NULL) + * @disable: disable the interrupt (defaults to chip->mask if NULL) + * @ack: start of a new interrupt + * @mask: mask an interrupt source + * @mask_ack: ack and mask an interrupt source + * @unmask: unmask an interrupt source + */ +static struct irq_chip sapphire_gpio_irq_chip = { + .name = "sapphiregpio", + .ack = sapphire_gpio_irq_ack, + .mask = sapphire_gpio_irq_disable, /*sapphire_gpio_irq_mask,*/ + .unmask = sapphire_gpio_irq_enable, /*sapphire_gpio_irq_unmask,*/ + .set_wake = sapphire_gpio_irq_set_wake, + /*.set_type = sapphire_gpio_irq_set_type,*/ +}; + +/*Thomas:For CPLD*/ +static struct gpio_chip sapphire_gpio_chip = { + .start = SAPPHIRE_GPIO_START, + .end = SAPPHIRE_GPIO_END, + .configure = sapphire_gpio_configure, + .get_irq_num = sapphire_gpio_get_irq_num, + .read = sapphire_gpio_read, + .write = sapphire_gpio_write, +/* .read_detect_status = sapphire_gpio_read_detect_status, + .clear_detect_status = sapphire_gpio_clear_detect_status */ +}; + +struct sysdev_class sapphire_sysdev_class = { + .name = "sapphiregpio_irq", + .suspend = sapphire_sysdev_suspend, + .resume = sapphire_sysdev_resume, +}; + +static struct sys_device sapphire_irq_device = { + .cls = &sapphire_sysdev_class, +}; + +int sapphire_init_gpio(void) +{ + int i; + if (!machine_is_sapphire()) + return 0; + + DBG("%d,%d\r\n", SAPPHIRE_INT_START, SAPPHIRE_INT_END); + DBG("NR_MSM_IRQS=%d, NR_GPIO_IRQS=%d\r\n", NR_MSM_IRQS, NR_GPIO_IRQS); + for (i = SAPPHIRE_INT_START; i <= SAPPHIRE_INT_END; i++) { + set_irq_chip(i, &sapphire_gpio_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + register_gpio_chip(&sapphire_gpio_chip); + + /*setup CPLD INT connecting to SOC's gpio 17 */ + set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); + set_irq_chained_handler(MSM_GPIO_TO_INT(17), sapphire_gpio_irq_handler); + set_irq_wake(MSM_GPIO_TO_INT(17), 1); + + if (sysdev_class_register(&sapphire_sysdev_class) == 0) + sysdev_register(&sapphire_irq_device); + + return 0; +} + +int sapphire_init_cpld(unsigned int sys_rev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sapphire_cpld_initdata); i++) + writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2); + return 0; +} + +postcore_initcall(sapphire_init_gpio); diff --git a/arch/arm/mach-msm/board-sapphire-h2w.c b/arch/arm/mach-msm/board-sapphire-h2w.c new file mode 100644 index 00000000000..aa83e216974 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-h2w.c @@ -0,0 +1,545 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * Authors: + * Laurence Chen + * Nick Pelly + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on sapphire. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board-sapphire.h" + +#ifdef CONFIG_DEBUG_SAPPHIRE_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\n", __FUNCTION__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); +enum { + NO_DEVICE = 0, + HTC_HEADSET = 1, +}; + +enum { + UART3 = 0, + GPIO = 1, +}; + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; +}; +static struct h2w_info *hi; + +static ssize_t sapphire_h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case NO_DEVICE: + return sprintf(buf, "No Device\n"); + case HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void configure_cpld(int route) +{ + H2W_DBG(" route = %s", route == UART3 ? "UART3" : "GPIO"); + switch (route) { + case UART3: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + break; + case GPIO: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + break; + } +} + +static void button_pressed(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, HTC_HEADSET); + configure_cpld(GPIO); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + + + /* On some non-standard headset adapters (usually those without a + * button) the btn line is pulled down at the same time as the detect + * line. We can check here by sampling the button line, if it is + * low then it is probably a bad adapter so ignore the button. + * If the button is released then we stop ignoring the button, so that + * the user can recover from the situation where a headset is plugged + * in with button held down. + */ + hi->ignore_btn = !gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + hi->debounce_time = ktime_set(0, 20000000); /* 20 ms */ +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, NO_DEVICE); + configure_cpld(UART3); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int clk, cable_in1; + + H2W_DBG(""); + + if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + configure_cpld(GPIO); + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Set GPIO_CABLE_IN1 as output high */ + gpio_direction_output(SAPPHIRE_GPIO_CABLE_IN1, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Save H2W_CLK */ + clk = gpio_get_value(SAPPHIRE_GPIO_H2W_CLK_GPI); + /* Set GPIO_CABLE_IN1 as input */ + gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + cable_in1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + + if (cable_in1 == 0 && clk == 0) { + if (switch_get_state(&hi->sdev) == NO_DEVICE) + insert_headset(); + } else { + configure_cpld(UART3); + H2W_DBG("CABLE_IN1 was low, but not a headset " + "(recent cable_in1 = %d, clk = %d)", cable_in1, clk); + } +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == HTC_HEADSET) { + if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) + button_pressed(); + } + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + if ((switch_get_state(&hi->sdev) == NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static void h2w_debug_set(void *data, u64 val) +{ + switch_set_state(&hi->sdev, (int)val); +} + +static u64 h2w_debug_get(void *data) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int sapphire_h2w_probe(struct platform_device *pdev) +{ + int ret; + unsigned long irq_flags; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + hi->sdev.name = "h2w"; + hi->sdev.print_name = sapphire_h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + configure_cpld(UART3); + /* Set the CPLD connected H2W GPIO's to input */ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + hi->input->evbit[0] = BIT_MASK(EV_KEY); + hi->input->keybit[BIT_WORD(KEY_MEDIA)] = BIT_MASK(KEY_MEDIA); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(SAPPHIRE_GPIO_CABLE_IN2); +err_request_button_gpio: + gpio_free(SAPPHIRE_GPIO_CABLE_IN1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int sapphire_h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(SAPPHIRE_GPIO_CABLE_IN2); + gpio_free(SAPPHIRE_GPIO_CABLE_IN1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + +static struct platform_device sapphire_h2w_device = { + .name = "sapphire-h2w", +}; + +static struct platform_driver sapphire_h2w_driver = { + .probe = sapphire_h2w_probe, + .remove = sapphire_h2w_remove, + .driver = { + .name = "sapphire-h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init sapphire_h2w_init(void) +{ + if (!machine_is_sapphire()) + return 0; + int ret; + H2W_DBG(""); + ret = platform_driver_register(&sapphire_h2w_driver); + if (ret) + return ret; + return platform_device_register(&sapphire_h2w_device); +} + +static void __exit sapphire_h2w_exit(void) +{ + platform_device_unregister(&sapphire_h2w_device); + platform_driver_unregister(&sapphire_h2w_driver); +} + +module_init(sapphire_h2w_init); +module_exit(sapphire_h2w_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC 2 Wire detection driver for sapphire"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-sapphire-keypad.c b/arch/arm/mach-msm/board-sapphire-keypad.c new file mode 100644 index 00000000000..5c8fc37649a --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-keypad.c @@ -0,0 +1,132 @@ +/* arch/arm/mach-msm/board-sapphire-keypad.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include "gpio_chip.h" +#include "board-sapphire.h" +static char *keycaps = "--qwerty"; +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_sapphire." +module_param_named(keycaps, keycaps, charp, 0); + + +static unsigned int sapphire_col_gpios[] = { 35, 34 }; + +/* KP_MKIN2 (GPIO40) is not used? */ +static unsigned int sapphire_row_gpios[] = { 42, 41 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(sapphire_row_gpios) + (row)) + +/*scan matrix key*/ +/* HOME(up) MENU (up) Back Search */ +static const unsigned short sapphire_keymap2[ARRAY_SIZE(sapphire_col_gpios) * ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_COMPOSE, + [KEYMAP_INDEX(0, 1)] = KEY_BACK, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +/* HOME(up) + MENU (down)*/ +static const unsigned short sapphire_keymap1[ARRAY_SIZE(sapphire_col_gpios) * + ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_MENU, + + [KEYMAP_INDEX(1, 0)] = KEY_HOME, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +/* MENU(up) + HOME (down)*/ +static const unsigned short sapphire_keymap0[ARRAY_SIZE(sapphire_col_gpios) * + ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +static struct gpio_event_matrix_info sapphire_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = sapphire_keymap2, + .output_gpios = sapphire_col_gpios, + .input_gpios = sapphire_row_gpios, + .noutputs = ARRAY_SIZE(sapphire_col_gpios), + .ninputs = ARRAY_SIZE(sapphire_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .debounce_delay.tv.nsec = 50 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | + GPIOKPF_REMOVE_PHANTOM_KEYS | + GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_direct_entry sapphire_keypad_nav_map[] = { + { SAPPHIRE_POWER_KEY, KEY_END }, + { SAPPHIRE_VOLUME_UP, KEY_VOLUMEUP }, + { SAPPHIRE_VOLUME_DOWN, KEY_VOLUMEDOWN }, +}; + +static struct gpio_event_input_info sapphire_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = sapphire_keypad_nav_map, + .debounce_time.tv.nsec = 20 * NSEC_PER_MSEC, + .keymap_size = ARRAY_SIZE(sapphire_keypad_nav_map) +}; + +static struct gpio_event_info *sapphire_keypad_info[] = { + &sapphire_keypad_matrix_info.info, + &sapphire_keypad_nav_info.info, +}; + +static struct gpio_event_platform_data sapphire_keypad_data = { + .name = "sapphire-keypad", + .info = sapphire_keypad_info, + .info_count = ARRAY_SIZE(sapphire_keypad_info) +}; + +static struct platform_device sapphire_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &sapphire_keypad_data, + }, +}; + +static int __init sapphire_init_keypad(void) +{ + if (!machine_is_sapphire()) + return 0; + + switch (sapphire_get_hwid()) { + case 0: + sapphire_keypad_matrix_info.keymap = sapphire_keymap0; + break; + default: + if(system_rev != 0x80) + sapphire_keypad_matrix_info.keymap = sapphire_keymap1; + break; + } + return platform_device_register(&sapphire_keypad_device); +} + +device_initcall(sapphire_init_keypad); + diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c new file mode 100644 index 00000000000..8cb913fade1 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -0,0 +1,486 @@ +/* linux/arch/arm/mach-msm/board-sapphire-mmc.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "devices.h" +#include "gpio_chip.h" +#include "board-sapphire.h" + +#define DEBUG_SDSLOT_VDD 0 + +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + +/* ---- COMMON ---- */ +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +/* ---- SDCARD ---- */ + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint opt_disable_sdcard; + +static int __init sapphire_disablesdcard_setup(char *str) +{ + int cal = simple_strtol(str, NULL, 0); + + opt_disable_sdcard = cal; + return 1; +} + +__setup("board_sapphire.disable_sdcard=", sapphire_disablesdcard_setup); + +static struct vreg *vreg_sdslot; /* SD slot power */ + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static unsigned int sdslot_vdd = 0xffffffff; +static unsigned int sdslot_vreg_enabled; + +static uint32_t sapphire_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i, rc; + + BUG_ON(!vreg_sdslot); + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk(KERN_DEBUG "%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(vreg_sdslot); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + rc = vreg_enable(vreg_sdslot); + if (rc) { + printk(KERN_ERR "%s: Error enabling vreg (%d)\n", + __func__, rc); + } + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask == (1 << vdd)) { +#if DEBUG_SDSLOT_VDD + printk(KERN_DEBUG "%s: Setting level to %u\n", + __func__, mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdslot, + mmc_vdd_table[i].level); + if (rc) { + printk(KERN_ERR + "%s: Error setting vreg level (%d)\n", + __func__, rc); + } + return 0; + } + } + + printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int sapphire_sdslot_status(struct device *dev) +{ + unsigned int status; + + status = (unsigned int) gpio_get_value(SAPPHIRE_GPIO_SDMC_CD_N); + return !status; +} + +#define SAPPHIRE_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30) + +static struct mmc_platform_data sapphire_sdslot_data = { + .ocr_mask = SAPPHIRE_MMC_VDD, + .status = sapphire_sdslot_status, + .translate_vdd = sapphire_sdslot_switchvdd, +}; + +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */ +static int sapphire_wifi_cd = 0; /* WIFI virtual 'card detect' status */ + +static struct sdio_embedded_func wifi_func = { + .f_class = SDIO_CLASS_WLAN, + .f_maxblksize = 512, +}; + +static struct embedded_sdio_data sapphire_wifi_emb_data = { + .cis = { + .vendor = 0x104c, + .device = 0x9066, + .blksize = 512, + .max_dtr = 20000000, + }, + .cccr = { + .multi_block = 0, + .low_speed = 0, + .wide_bus = 1, + .high_power = 0, + .high_speed = 0, + }, + .funcs = &wifi_func, + .num_funcs = 1, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int sapphire_wifi_status_register(void (*callback)(int card_present, + void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int sapphire_wifi_status(struct device *dev) +{ + return sapphire_wifi_cd; +} + +int sapphire_wifi_set_carddetect(int val) +{ + printk(KERN_DEBUG "%s: %d\n", __func__, val); + sapphire_wifi_cd = val; + if (wifi_status_cb) + wifi_status_cb(val, wifi_status_cb_devid); + else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_set_carddetect); +#endif + +int sapphire_wifi_power_state=0; +int sapphire_bt_power_state=0; + +int sapphire_wifi_power(int on) +{ + int rc; + + printk(KERN_DEBUG "%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + rc = vreg_enable(vreg_wifi_osc); + if (rc) + return rc; + htc_pwrsink_set(PWRSINK_WIFI, 70); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + htc_pwrsink_set(PWRSINK_WIFI, 0); + } + gpio_set_value(SAPPHIRE_GPIO_MAC_32K_EN, on); + mdelay(100); + gpio_set_value(SAPPHIRE_GPIO_WIFI_EN, on); + mdelay(100); + if (!on) { + if(!sapphire_bt_power_state) + { + vreg_disable(vreg_wifi_osc); + printk("WiFi disable vreg_wifi_osc.\n"); + } + else + printk("WiFi shouldn't disable vreg_wifi_osc. BT is using it!!\n"); + } + sapphire_wifi_power_state = on; + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_power); +#endif + +/* Eenable VREG_MMC pin to turn on fastclock oscillator : colin */ +int sapphire_bt_fastclock_power(int on) +{ + int rc; + + printk(KERN_DEBUG "sapphire_bt_fastclock_power on = %d\n", on); + if (vreg_wifi_osc) { + if (on) { + rc = vreg_enable(vreg_wifi_osc); + printk(KERN_DEBUG "BT vreg_enable vreg_mmc, rc=%d\n", + rc); + if (rc) { + printk("Error turn sapphire_bt_fastclock_power rc=%d\n", rc); + return rc; + } + } else { + if (!sapphire_wifi_power_state) { + vreg_disable(vreg_wifi_osc); + printk(KERN_DEBUG "BT disable vreg_wifi_osc.\n"); + } else + printk(KERN_DEBUG "BT shouldn't disable vreg_wifi_osc. WiFi is using it!!\n"); + } + } + sapphire_bt_power_state = on; + return 0; +} +EXPORT_SYMBOL(sapphire_bt_fastclock_power); + +static int sapphire_wifi_reset_state; +void sapphire_wifi_reset(int on) +{ + printk(KERN_DEBUG "%s: %d\n", __func__, on); + gpio_set_value(SAPPHIRE_GPIO_WIFI_PA_RESETX, !on); + sapphire_wifi_reset_state = on; + mdelay(50); +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_reset); +#endif + +static struct mmc_platform_data sapphire_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = sapphire_wifi_status, + .register_status_notify = sapphire_wifi_status_register, + .embedded_sdio = &sapphire_wifi_emb_data, +}; + +int __init sapphire_init_mmc(unsigned int sys_rev) +{ + wifi_status_cb = NULL; + + sdslot_vreg_enabled = 0; + + vreg_sdslot = vreg_get(0, "gp6"); + if (IS_ERR(vreg_sdslot)) + return PTR_ERR(vreg_sdslot); + vreg_wifi_osc = vreg_get(0, "mmc"); + if (IS_ERR(vreg_wifi_osc)) + return PTR_ERR(vreg_wifi_osc); + + set_irq_wake(SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 1); + + msm_add_sdcc(1, &sapphire_wifi_data, 0, 0); + + if (!opt_disable_sdcard) + msm_add_sdcc(2, &sapphire_sdslot_data, + SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 0); + else + printk(KERN_INFO "sapphire: SD-Card interface disabled\n"); + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int sapphiremmc_dbg_wifi_reset_set(void *data, u64 val) +{ + sapphire_wifi_reset((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = sapphire_wifi_reset_state; + return 0; +} + +static int sapphiremmc_dbg_wifi_cd_set(void *data, u64 val) +{ + sapphire_wifi_set_carddetect((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = sapphire_wifi_cd; + return 0; +} + +static int sapphiremmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + sapphire_wifi_power((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + + *val = sapphire_wifi_power_state; + return 0; +} + +static int sapphiremmc_dbg_sd_pwr_set(void *data, u64 val) +{ + sapphire_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int sapphiremmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int sapphiremmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int sapphiremmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = sapphire_sdslot_status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_reset_fops, + sapphiremmc_dbg_wifi_reset_get, + sapphiremmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_cd_fops, + sapphiremmc_dbg_wifi_cd_get, + sapphiremmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_pwr_fops, + sapphiremmc_dbg_wifi_pwr_get, + sapphiremmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_pwr_fops, + sapphiremmc_dbg_sd_pwr_get, + sapphiremmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_cd_fops, + sapphiremmc_dbg_sd_cd_get, + sapphiremmc_dbg_sd_cd_set, "%llu\n"); + +static int __init sapphiremmc_dbg_init(void) +{ + struct dentry *dent; + + if (!machine_is_sapphire()) + return 0; + + dent = debugfs_create_dir("sapphiremmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_pwr_fops); + + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &sapphiremmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &sapphiremmc_dbg_sd_cd_fops); + + return 0; +} + +device_initcall(sapphiremmc_dbg_init); + +#endif diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c new file mode 100644 index 00000000000..1a5f63acec5 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -0,0 +1,1272 @@ +/* linux/arch/arm/mach-msm/board-sapphire-panel.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "gpio_chip.h" +#include "board-sapphire.h" +#include "devices.h" + +#define DEBUG_SAPPHIRE_PANEL 0 +#define userid 0xD10 + +#define VSYNC_GPIO 97 + +enum sapphire_panel_type { + SAPPHIRE_PANEL_SHARP = 0, + SAPPHIRE_PANEL_TOPPOLY, + NUM_OF_SAPPHIRE_PANELS, +}; +static int g_panel_id = -1 ; +static int g_panel_inited = 0 ; + +#define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132 +#define GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS 102 +#define SDBB SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS +#define GDBB GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS + +static int sapphire_backlight_off; +static int sapphire_backlight_brightness = + SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS; + +static uint8_t sapphire_backlight_last_level = 33; +static DEFINE_MUTEX(sapphire_backlight_lock); + +/* Divide dimming level into 12 sections, and restrict maximum level to 27 */ +#define DIMMING_STEPS 12 +static unsigned dimming_levels[NUM_OF_SAPPHIRE_PANELS][DIMMING_STEPS] = { + {0, 1, 2, 3, 6, 9, 11, 13, 16, 19, 22, 25}, /* Sharp */ + {0, 1, 2, 4, 7, 10, 13, 15, 18, 21, 24, 27}, /* Toppolly */ +}; +static unsigned pwrsink_percents[] = {0, 6, 8, 15, 26, 34, 46, 54, 65, 77, 87, + 100}; + +static void sapphire_set_backlight_level(uint8_t level) +{ + unsigned dimming_factor = 255/DIMMING_STEPS + 1; + int index, new_level ; + unsigned percent; + unsigned long flags; + int i = 0; + + /* Non-linear transform for the difference between two + * kind of default backlight settings. + */ + new_level = level<=GDBB ? + level*SDBB/GDBB : (SDBB + (level-GDBB)*(255-SDBB) / (255-GDBB)) ; + index = new_level/dimming_factor ; + +#if DEBUG_SAPPHIRE_PANEL + printk(KERN_INFO "level=%d, new level=%d, dimming_levels[%d]=%d\n", + level, new_level, index, dimming_levels[g_panel_id][index]); +#endif + percent = pwrsink_percents[index]; + level = dimming_levels[g_panel_id][index]; + + if (sapphire_backlight_last_level == level) + return; + + if (level == 0) { + gpio_set_value(27, 0); + msleep(2); + } else { + local_irq_save(flags); + if (sapphire_backlight_last_level == 0) { + gpio_set_value(27, 1); + udelay(40); + sapphire_backlight_last_level = 33; + } + i = (sapphire_backlight_last_level - level + 33) % 33; + while (i-- > 0) { + gpio_set_value(27, 0); + udelay(1); + gpio_set_value(27, 1); + udelay(1); + } + local_irq_restore(flags); + } + sapphire_backlight_last_level = level; + htc_pwrsink_set(PWRSINK_BACKLIGHT, percent); +} + +#define MDDI_CLIENT_CORE_BASE 0x108000 +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define SPI_BLOCK_BASE 0x120000 +#define I2C_BLOCK_BASE 0x130000 +#define PWM_BLOCK_BASE 0x140000 +#define GPIO_BLOCK_BASE 0x150000 +#define SYSTEM_BLOCK1_BASE 0x160000 +#define SYSTEM_BLOCK2_BASE 0x170000 + + +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define SYSCLKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define PWM0OFF (PWM_BLOCK_BASE|0x1C) + +#define V_VDDE2E_VDD2_GPIO 0 +#define V_VDDE2E_VDD2_GPIO_5M 89 +#define MDDI_RST_N 82 + +#define MDDICAP0 (MDDI_CLIENT_CORE_BASE|0x00) +#define MDDICAP1 (MDDI_CLIENT_CORE_BASE|0x04) +#define MDDICAP2 (MDDI_CLIENT_CORE_BASE|0x08) +#define MDDICAP3 (MDDI_CLIENT_CORE_BASE|0x0C) +#define MDCAPCHG (MDDI_CLIENT_CORE_BASE|0x10) +#define MDCRCERC (MDDI_CLIENT_CORE_BASE|0x14) +#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18) +#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C) +#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20) +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28) +#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define TESTMODE (MDDI_CLIENT_CORE_BASE|0x30) +#define FIFOMONI (MDDI_CLIENT_CORE_BASE|0x34) +#define INTMONI (MDDI_CLIENT_CORE_BASE|0x38) +#define MDIOBIST (MDDI_CLIENT_CORE_BASE|0x3C) +#define MDIOPSET (MDDI_CLIENT_CORE_BASE|0x40) +#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44) +#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48) +#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C) +#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50) +#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54) + +#define SRST (LCD_CONTROL_BLOCK_BASE|0x00) +#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04) +#define START (LCD_CONTROL_BLOCK_BASE|0x08) +#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C) +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define GAMMA (LCD_CONTROL_BLOCK_BASE|0x14) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define HDE_LEFT (LCD_CONTROL_BLOCK_BASE|0x24) +#define VDE_TOP (LCD_CONTROL_BLOCK_BASE|0x28) +#define PXL (LCD_CONTROL_BLOCK_BASE|0x30) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HSW (LCD_CONTROL_BLOCK_BASE|0x38) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40) +#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44) +#define VSW (LCD_CONTROL_BLOCK_BASE|0x48) +#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C) +#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) +#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60) +#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64) +#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68) +#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C) +#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70) +#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74) +#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78) +#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C) +#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80) +#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84) +#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88) +#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C) + +#define SSICTL (SPI_BLOCK_BASE|0x00) +#define SSITIME (SPI_BLOCK_BASE|0x04) +#define SSITX (SPI_BLOCK_BASE|0x08) +#define SSIRX (SPI_BLOCK_BASE|0x0C) +#define SSIINTC (SPI_BLOCK_BASE|0x10) +#define SSIINTS (SPI_BLOCK_BASE|0x14) +#define SSIDBG1 (SPI_BLOCK_BASE|0x18) +#define SSIDBG2 (SPI_BLOCK_BASE|0x1C) +#define SSIID (SPI_BLOCK_BASE|0x20) + +#define WKREQ (SYSTEM_BLOCK1_BASE|0x00) +#define CLKENB (SYSTEM_BLOCK1_BASE|0x04) +#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08) +#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C) +#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00) + +#define GPIODATA (GPIO_BLOCK_BASE|0x00) +#define GPIODIR (GPIO_BLOCK_BASE|0x04) +#define GPIOIS (GPIO_BLOCK_BASE|0x08) +#define GPIOIBE (GPIO_BLOCK_BASE|0x0C) +#define GPIOIEV (GPIO_BLOCK_BASE|0x10) +#define GPIOIE (GPIO_BLOCK_BASE|0x14) +#define GPIORIS (GPIO_BLOCK_BASE|0x18) +#define GPIOMIS (GPIO_BLOCK_BASE|0x1C) +#define GPIOIC (GPIO_BLOCK_BASE|0x20) +#define GPIOOMS (GPIO_BLOCK_BASE|0x24) +#define GPIOPC (GPIO_BLOCK_BASE|0x28) +#define GPIOID (GPIO_BLOCK_BASE|0x30) + +#define SPI_WRITE(reg, val) \ + { SSITX, 0x00010000 | (((reg) & 0xff) << 8) | ((val) & 0xff) }, \ + { 0, 5 }, + +#define SPI_WRITE1(reg) \ + { SSITX, (reg) & 0xff }, \ + { 0, 5 }, + +struct mddi_table { + uint32_t reg; + uint32_t value; +}; +static struct mddi_table mddi_toshiba_init_table[] = { + { DPSET0, 0x09e90046 }, + { DPSET1, 0x00000118 }, + { DPSUS, 0x00000000 }, + { DPRUN, 0x00000001 }, + { 1, 14 }, /* msleep 14 */ + { SYSCKENA, 0x00000001 }, + /*{ CLKENB, 0x000000EF } */ + { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */ + /*{ CLKENB, 0x000025CB }, Clock enable register */ + + { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */ + { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */ + { GPIOSEL, 0/*0x00000173*/}, /* # SYS.GPIOSEL # GPIO port multiplexing control */ + { GPIOPC, 0x03C300C0 }, /* # GPI .GPIOPC # GPIO2,3 PD cut */ + { WKREQ, 0x00000000 }, /* # SYS.WKREQ # Wake-up request event is VSYNC alignment */ + + { GPIOIBE, 0x000003FF }, + { GPIOIS, 0x00000000 }, + { GPIOIC, 0x000003FF }, + { GPIOIE, 0x00000000 }, + + { GPIODATA, 0x00040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x02040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { DRAMPWR, 0x00000001 }, /* eDRAM power */ +}; + +static struct mddi_table mddi_toshiba_panel_init_table[] = { + { SRST, 0x00000003 }, /* FIFO/LCDC not reset */ + { PORT_ENB, 0x00000001 }, /* Enable sync. Port */ + { START, 0x00000000 }, /* To stop operation */ + /*{ START, 0x00000001 }, To start operation */ + { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */ + { CMN, 0x00000000 }, + { GAMMA, 0x00000000 }, /* No Gamma correction */ + { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */ + { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */ + { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */ + { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */ + { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */ + { PXL, 0x00000001 }, /* 1. RGB666 */ + /* 2. Data is valid from 1st frame of beginning. */ + { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */ + { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */ + { HSW, 0x00000004 }, /* HSW= 10 PCLK */ + { VSW, 0x00000001 }, /* VSW=2 HCYCLE */ + { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */ + { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */ + { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */ + { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */ + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { CLKENB, 0x000025CB }, /* Clock enable register */ + + { SSICTL, 0x00000170 }, /* SSI control register */ + { SSITIME, 0x00000250 }, /* SSI timing control register */ + { SSICTL, 0x00000172 }, /* SSI control register */ +}; + + +static struct mddi_table mddi_sharp_init_table[] = { + { VCYCLE, 0x000001eb }, + { HCYCLE, 0x000000ae }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { 1, 10 }, /* msleep 10 */ + SPI_WRITE(0x5f, 0x01) + SPI_WRITE1(0x11) + { 1, 200 }, /* msleep 200 */ + SPI_WRITE1(0x29) + SPI_WRITE1(0xde) + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_sharp_deinit_table[] = { + { 1, 200 }, /* msleep 200 */ + SPI_WRITE(0x10, 0x1) + { 1, 100 }, /* msleep 100 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 1, 10 }, /* msleep 10 */ +}; + +static struct mddi_table mddi_tpo_init_table[] = { + { VCYCLE, 0x000001e5 }, + { HCYCLE, 0x000000ac }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { 0, 20 }, /* udelay 20 */ + { GPIODATA, 0x00000004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 0, 20 }, /* udelay 20 */ + + SPI_WRITE(0x08, 0x01) + { 0, 500 }, /* udelay 500 */ + SPI_WRITE(0x08, 0x00) + SPI_WRITE(0x02, 0x00) + SPI_WRITE(0x03, 0x04) + SPI_WRITE(0x04, 0x0e) + SPI_WRITE(0x09, 0x02) + SPI_WRITE(0x0b, 0x08) + SPI_WRITE(0x0c, 0x53) + SPI_WRITE(0x0d, 0x01) + SPI_WRITE(0x0e, 0xe0) + SPI_WRITE(0x0f, 0x01) + SPI_WRITE(0x10, 0x58) + SPI_WRITE(0x20, 0x1e) + SPI_WRITE(0x21, 0x0a) + SPI_WRITE(0x22, 0x0a) + SPI_WRITE(0x23, 0x1e) + SPI_WRITE(0x25, 0x32) + SPI_WRITE(0x26, 0x00) + SPI_WRITE(0x27, 0xac) + SPI_WRITE(0x29, 0x06) + SPI_WRITE(0x2a, 0xa4) + SPI_WRITE(0x2b, 0x45) + SPI_WRITE(0x2c, 0x45) + SPI_WRITE(0x2d, 0x15) + SPI_WRITE(0x2e, 0x5a) + SPI_WRITE(0x2f, 0xff) + SPI_WRITE(0x30, 0x6b) + SPI_WRITE(0x31, 0x0d) + SPI_WRITE(0x32, 0x48) + SPI_WRITE(0x33, 0x82) + SPI_WRITE(0x34, 0xbd) + SPI_WRITE(0x35, 0xe7) + SPI_WRITE(0x36, 0x18) + SPI_WRITE(0x37, 0x94) + SPI_WRITE(0x38, 0x01) + SPI_WRITE(0x39, 0x5d) + SPI_WRITE(0x3a, 0xae) + SPI_WRITE(0x3b, 0xff) + SPI_WRITE(0x07, 0x09) + { 0, 10 }, /* udelay 10 */ + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_tpo_deinit_table[] = { + SPI_WRITE(0x07, 0x19) + { START, 0x00000000 }, /* To stop operation */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 0, 5 }, /* usleep 5 */ +}; + + +#define GPIOSEL_VWAKEINT (1U << 0) +#define INTMASK_VWAKEOUT (1U << 0) + +static void sapphire_process_mddi_table( + struct msm_mddi_client_data *client_data, + const struct mddi_table *table, + size_t count) +{ + int i; + for (i = 0; i < count; i++) { + uint32_t reg = table[i].reg; + uint32_t value = table[i].value; + + if (reg == 0) + udelay(value); + else if (reg == 1) + msleep(value); + else + client_data->remote_write(client_data, value, reg); + } +} + +static struct vreg *vreg_lcm_2v85; + +static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, + int on) +{ + unsigned id, on_off; +#if DEBUG_SAPPHIRE_PANEL + printk(KERN_INFO "sapphire_mddi_client_power:%d\r\n", on); +#endif + if (on) { + on_off = 0; + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 1); + mdelay(5); /* delay time >5ms and <10ms */ + + if (is_12pin_camera()) + gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 1); + else + gpio_set_value(V_VDDE2E_VDD2_GPIO, 1); + + gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 1); + msleep(3); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcm_2v85); + msleep(3); + } else { + gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0); + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + vreg_disable(vreg_lcm_2v85); + on_off = 1; + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + msleep(5); + if (is_12pin_camera()) + gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 0); + else + gpio_set_value(V_VDDE2E_VDD2_GPIO, 0); + + msleep(200); + gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 0); + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + } +} + +static int sapphire_mddi_toshiba_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id; + + /* Set the MDDI_RST_N accroding to MDDI client repectively( + * been set in sapphire_mddi_power_client() originally) + */ + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, mddi_toshiba_init_table, + ARRAY_SIZE(mddi_toshiba_init_table)); + client_data->auto_hibernate(client_data, 1); + g_panel_id = panel_id = + (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + if (panel_id > 1) { +#if DEBUG_SAPPHIRE_PANEL + printk(KERN_ERR "unknown panel id at mddi_enable\n"); +#endif + return -1; + } + return 0; +} + +static int sapphire_mddi_toshiba_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + + return 0; +} + +static int sapphire_mddi_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id, ret = 0; + + sapphire_set_backlight_level(0); + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, mddi_toshiba_panel_init_table, + ARRAY_SIZE(mddi_toshiba_panel_init_table)); + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + switch (panel_id) { + case 0: +#if DEBUG_SAPPHIRE_PANEL + printk(KERN_DEBUG "init sharp panel\n"); +#endif + sapphire_process_mddi_table(client_data, + mddi_sharp_init_table, + ARRAY_SIZE(mddi_sharp_init_table)); + break; + case 1: +#if DEBUG_SAPPHIRE_PANEL + printk(KERN_DEBUG "init tpo panel\n"); +#endif + sapphire_process_mddi_table(client_data, + mddi_tpo_init_table, + ARRAY_SIZE(mddi_tpo_init_table)); + break; + default: + + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(sapphire_backlight_brightness); + sapphire_backlight_off = 0; + mutex_unlock(&sapphire_backlight_lock); + client_data->auto_hibernate(client_data, 1); + /* reenable vsync */ + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, + GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, + INTMASK); + return ret; + +} + +static int sapphire_mddi_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id, ret = 0; + + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + client_data->auto_hibernate(client_data, 0); + switch (panel_id) { + case 0: + printk(KERN_DEBUG "deinit sharp panel\n"); + sapphire_process_mddi_table(client_data, + mddi_sharp_deinit_table, + ARRAY_SIZE(mddi_sharp_deinit_table)); + break; + case 1: + printk(KERN_DEBUG "deinit tpo panel\n"); + sapphire_process_mddi_table(client_data, + mddi_tpo_deinit_table, + ARRAY_SIZE(mddi_tpo_deinit_table)); + break; + default: + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); + ret = -1; + }; + client_data->auto_hibernate(client_data, 1); + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(0); + sapphire_backlight_off = 1; + mutex_unlock(&sapphire_backlight_lock); + client_data->remote_write(client_data, 0, SYSCLKENA); + client_data->remote_write(client_data, 1, DPSUS); + + return ret; +} + + +/* Initial sequence of sharp panel with Novatek NT35399 MDDI client */ +static const struct mddi_table sharp2_init_table[] = { + { 0x02A0, 0x00 }, + { 0x02A1, 0x00 }, + { 0x02A2, 0x3F }, + { 0x02A3, 0x01 }, + { 0x02B0, 0x00 }, + { 0x02B1, 0x00 }, + { 0x02B2, 0xDF }, + { 0x02B3, 0x01 }, + { 0x02D0, 0x00 }, + { 0x02D1, 0x00 }, + { 0x02D2, 0x00 }, + { 0x02D3, 0x00 }, + { 0x0350, 0x80 }, /* Set frame tearing effect(FTE) position */ + { 0x0351, 0x00 }, + { 0x0360, 0x30 }, + { 0x0361, 0xC1 }, + { 0x0362, 0x00 }, + { 0x0370, 0x00 }, + { 0x0371, 0xEF }, + { 0x0372, 0x01 }, + + { 0x0B00, 0x10 }, + + { 0x0B10, 0x00 }, + { 0x0B20, 0x22 }, + { 0x0B30, 0x46 }, + { 0x0B40, 0x07 }, + { 0x0B41, 0x1C }, + { 0x0B50, 0x0F }, + { 0x0B51, 0x7A }, + { 0x0B60, 0x16 }, + { 0x0B70, 0x0D }, + { 0x0B80, 0x04 }, + { 0x0B90, 0x07 }, + { 0x0BA0, 0x04 }, + { 0x0BA1, 0x86 }, + { 0x0BB0, 0xFF }, + { 0x0BB1, 0x01 }, + { 0x0BB2, 0xF7 }, + { 0x0BB3, 0x01 }, + { 0x0BC0, 0x00 }, + { 0x0BC1, 0x00 }, + { 0x0BC2, 0x00 }, + { 0x0BC3, 0x00 }, + { 0x0BE0, 0x01 }, + { 0x0BE1, 0x3F }, + + { 0x0BF0, 0x03 }, + + { 0x0C10, 0x02 }, + + { 0x0C30, 0x22 }, + { 0x0C31, 0x20 }, + { 0x0C40, 0x48 }, + { 0x0C41, 0x06 }, + + { 0xE00, 0x0028}, + { 0xE01, 0x002F}, + { 0xE02, 0x0032}, + { 0xE03, 0x000A}, + { 0xE04, 0x0023}, + { 0xE05, 0x0024}, + { 0xE06, 0x0022}, + { 0xE07, 0x0012}, + { 0xE08, 0x000D}, + { 0xE09, 0x0035}, + { 0xE0A, 0x000E}, + { 0xE0B, 0x001A}, + { 0xE0C, 0x003C}, + { 0xE0D, 0x003A}, + { 0xE0E, 0x0050}, + { 0xE0F, 0x0069}, + { 0xE10, 0x0006}, + { 0xE11, 0x001F}, + { 0xE12, 0x0035}, + { 0xE13, 0x0020}, + { 0xE14, 0x0043}, + { 0xE15, 0x0030}, + { 0xE16, 0x003C}, + { 0xE17, 0x0010}, + { 0xE18, 0x0009}, + { 0xE19, 0x0051}, + { 0xE1A, 0x001D}, + { 0xE1B, 0x003C}, + { 0xE1C, 0x0053}, + { 0xE1D, 0x0041}, + { 0xE1E, 0x0045}, + { 0xE1F, 0x004B}, + { 0xE20, 0x000A}, + { 0xE21, 0x0014}, + { 0xE22, 0x001C}, + { 0xE23, 0x0013}, + { 0xE24, 0x002E}, + { 0xE25, 0x0029}, + { 0xE26, 0x001B}, + { 0xE27, 0x0014}, + { 0xE28, 0x000E}, + { 0xE29, 0x0032}, + { 0xE2A, 0x000D}, + { 0xE2B, 0x001B}, + { 0xE2C, 0x0033}, + { 0xE2D, 0x0033}, + { 0xE2E, 0x005B}, + { 0xE2F, 0x0069}, + { 0xE30, 0x0006}, + { 0xE31, 0x0014}, + { 0xE32, 0x003D}, + { 0xE33, 0x0029}, + { 0xE34, 0x0042}, + { 0xE35, 0x0032}, + { 0xE36, 0x003F}, + { 0xE37, 0x000E}, + { 0xE38, 0x0008}, + { 0xE39, 0x0059}, + { 0xE3A, 0x0015}, + { 0xE3B, 0x002E}, + { 0xE3C, 0x0049}, + { 0xE3D, 0x0058}, + { 0xE3E, 0x0061}, + { 0xE3F, 0x006B}, + { 0xE40, 0x000A}, + { 0xE41, 0x001A}, + { 0xE42, 0x0022}, + { 0xE43, 0x0014}, + { 0xE44, 0x002F}, + { 0xE45, 0x002A}, + { 0xE46, 0x001A}, + { 0xE47, 0x0014}, + { 0xE48, 0x000E}, + { 0xE49, 0x002F}, + { 0xE4A, 0x000F}, + { 0xE4B, 0x001B}, + { 0xE4C, 0x0030}, + { 0xE4D, 0x002C}, + { 0xE4E, 0x0051}, + { 0xE4F, 0x0069}, + { 0xE50, 0x0006}, + { 0xE51, 0x001E}, + { 0xE52, 0x0043}, + { 0xE53, 0x002F}, + { 0xE54, 0x0043}, + { 0xE55, 0x0032}, + { 0xE56, 0x0043}, + { 0xE57, 0x000D}, + { 0xE58, 0x0008}, + { 0xE59, 0x0059}, + { 0xE5A, 0x0016}, + { 0xE5B, 0x0030}, + { 0xE5C, 0x004B}, + { 0xE5D, 0x0051}, + { 0xE5E, 0x005A}, + { 0xE5F, 0x006B}, + + { 0x0290, 0x01 }, +}; + +#undef TPO2_ONE_GAMMA +/* Initial sequence of TPO panel with Novatek NT35399 MDDI client */ + +static const struct mddi_table tpo2_init_table[] = { + /* Panel interface control */ + { 0xB30, 0x44 }, + { 0xB40, 0x00 }, + { 0xB41, 0x87 }, + { 0xB50, 0x06 }, + { 0xB51, 0x7B }, + { 0xB60, 0x0E }, + { 0xB70, 0x0F }, + { 0xB80, 0x03 }, + { 0xB90, 0x00 }, + { 0x350, 0x70 }, /* FTE is at line 0x70 */ + + /* Entry Mode */ + { 0x360, 0x30 }, + { 0x361, 0xC1 }, + { 0x362, 0x04 }, + +/* 0x2 for gray scale gamma correction, 0x12 for RGB gamma correction */ +#ifdef TPO2_ONE_GAMMA + { 0xB00, 0x02 }, +#else + { 0xB00, 0x12 }, +#endif + /* Driver output control */ + { 0x371, 0xEF }, + { 0x372, 0x03 }, + + /* DCDC on glass control */ + { 0xC31, 0x10 }, + { 0xBA0, 0x00 }, + { 0xBA1, 0x86 }, + + /* VCOMH voltage control */ + { 0xC50, 0x3b }, + + /* Special function control */ + { 0xC10, 0x82 }, + + /* Power control */ + { 0xC40, 0x44 }, + { 0xC41, 0x02 }, + + /* Source output control */ + { 0xBE0, 0x01 }, + { 0xBE1, 0x00 }, + + /* Windows address setting */ + { 0x2A0, 0x00 }, + { 0x2A1, 0x00 }, + { 0x2A2, 0x3F }, + { 0x2A3, 0x01 }, + { 0x2B0, 0x00 }, + { 0x2B1, 0x00 }, + { 0x2B2, 0xDF }, + { 0x2B3, 0x01 }, + + /* RAM address setting */ + { 0x2D0, 0x00 }, + { 0x2D1, 0x00 }, + { 0x2D2, 0x00 }, + { 0x2D3, 0x00 }, + + { 0xF20, 0x55 }, + { 0xF21, 0xAA }, + { 0xF22, 0x66 }, + { 0xF57, 0x45 }, + +/* + * The NT35399 provides gray or RGB gamma correction table, + * which determinated by register-0xb00, and following table + */ +#ifdef TPO2_ONE_GAMMA + /* Positive Gamma setting */ + { 0xE00, 0x04 }, + { 0xE01, 0x12 }, + { 0xE02, 0x18 }, + { 0xE03, 0x10 }, + { 0xE04, 0x29 }, + { 0xE05, 0x26 }, + { 0xE06, 0x1f }, + { 0xE07, 0x11 }, + { 0xE08, 0x0c }, + { 0xE09, 0x3a }, + { 0xE0A, 0x0d }, + { 0xE0B, 0x28 }, + { 0xE0C, 0x40 }, + { 0xE0D, 0x4e }, + { 0xE0E, 0x6f }, + { 0xE0F, 0x5E }, + + /* Negative Gamma setting */ + { 0xE10, 0x0B }, + { 0xE11, 0x00 }, + { 0xE12, 0x00 }, + { 0xE13, 0x1F }, + { 0xE14, 0x4b }, + { 0xE15, 0x33 }, + { 0xE16, 0x13 }, + { 0xE17, 0x12 }, + { 0xE18, 0x0d }, + { 0xE19, 0x2f }, + { 0xE1A, 0x16 }, + { 0xE1B, 0x2e }, + { 0xE1C, 0x49 }, + { 0xE1D, 0x41 }, + { 0xE1E, 0x46 }, + { 0xE1F, 0x55 }, +#else + /* Red Positive Gamma */ + { 0xE00, 0x0f }, + { 0xE01, 0x19 }, + { 0xE02, 0x22 }, + { 0xE03, 0x0b }, + { 0xE04, 0x23 }, + { 0xE05, 0x23 }, + { 0xE06, 0x14 }, + { 0xE07, 0x13 }, + { 0xE08, 0x0f }, + { 0xE09, 0x2a }, + { 0xE0A, 0x0d }, + { 0xE0B, 0x26 }, + { 0xE0C, 0x43 }, + { 0xE0D, 0x20 }, + { 0xE0E, 0x2a }, + { 0xE0F, 0x5c }, + + /* Red Negative Gamma */ + { 0xE10, 0x0d }, + { 0xE11, 0x45 }, + { 0xE12, 0x4c }, + { 0xE13, 0x1c }, + { 0xE14, 0x4d }, + { 0xE15, 0x33 }, + { 0xE16, 0x23 }, + { 0xE17, 0x0f }, + { 0xE18, 0x0b }, + { 0xE19, 0x3a }, + { 0xE1A, 0x19 }, + { 0xE1B, 0x32 }, + { 0xE1C, 0x4e }, + { 0xE1D, 0x37 }, + { 0xE1E, 0x38 }, + { 0xE1F, 0x3b }, + + /* Green Positive Gamma */ + { 0xE20, 0x00 }, + { 0xE21, 0x09 }, + { 0xE22, 0x10 }, + { 0xE23, 0x0f }, + { 0xE24, 0x29 }, + { 0xE25, 0x23 }, + { 0xE26, 0x0b }, + { 0xE27, 0x14 }, + { 0xE28, 0x12 }, + { 0xE29, 0x25 }, + { 0xE2A, 0x12 }, + { 0xE2B, 0x2f }, + { 0xE2C, 0x43 }, + { 0xE2D, 0x2d }, + { 0xE2E, 0x52 }, + { 0xE2F, 0x61 }, + + /* Green Negative Gamma */ + { 0xE30, 0x08 }, + { 0xE31, 0x1d }, + { 0xE32, 0x3f }, + { 0xE33, 0x1c }, + { 0xE34, 0x44 }, + { 0xE35, 0x2e }, + { 0xE36, 0x28 }, + { 0xE37, 0x0c }, + { 0xE38, 0x0a }, + { 0xE39, 0x42 }, + { 0xE3A, 0x17 }, + { 0xE3B, 0x30 }, + { 0xE3C, 0x4b }, + { 0xE3D, 0x3f }, + { 0xE3E, 0x43 }, + { 0xE3F, 0x45 }, + + /* Blue Positive Gamma */ + { 0xE40, 0x32 }, + { 0xE41, 0x32 }, + { 0xE42, 0x31 }, + { 0xE43, 0x06 }, + { 0xE44, 0x08 }, + { 0xE45, 0x0d }, + { 0xE46, 0x04 }, + { 0xE47, 0x14 }, + { 0xE48, 0x0f }, + { 0xE49, 0x1d }, + { 0xE4A, 0x1a }, + { 0xE4B, 0x39 }, + { 0xE4C, 0x4c }, + { 0xE4D, 0x1e }, + { 0xE4E, 0x43 }, + { 0xE4F, 0x61 }, + + /* Blue Negative Gamma */ + { 0xE50, 0x08 }, + { 0xE51, 0x2c }, + { 0xE52, 0x4e }, + { 0xE53, 0x13 }, + { 0xE54, 0x3a }, + { 0xE55, 0x26 }, + { 0xE56, 0x30 }, + { 0xE57, 0x0f }, + { 0xE58, 0x0a }, + { 0xE59, 0x49 }, + { 0xE5A, 0x34 }, + { 0xE5B, 0x4a }, + { 0xE5C, 0x53 }, + { 0xE5D, 0x28 }, + { 0xE5E, 0x26 }, + { 0xE5F, 0x27 }, + +#endif + /* Sleep in mode */ + { 0x110, 0x00 }, + { 0x1, 0x23 }, + /* Display on mode */ + { 0x290, 0x00 }, + { 0x1, 0x27 }, + /* Driver output control */ + { 0x372, 0x01 }, + { 0x1, 0x40 }, + /* Display on mode */ + { 0x290, 0x01 }, +}; + +static const struct mddi_table tpo2_display_on[] = { + { 0x290, 0x01 }, +}; + +static const struct mddi_table tpo2_display_off[] = { + { 0x110, 0x01 }, + { 0x290, 0x00 }, + { 0x1, 100 }, +}; + +static const struct mddi_table tpo2_power_off[] = { + { 0x0110, 0x01 }, +}; + +static int nt35399_detect_panel(struct msm_mddi_client_data *client_data) +{ + int id = -1, i ; + + /* If the MDDI client is failed to report the panel ID, + * perform retrial 5 times. + */ + for( i=0; i < 5; i++ ) { + client_data->remote_write(client_data, 0, 0x110); + msleep(5); + id = client_data->remote_read(client_data, userid) ; + if( id == 0 || id == 1 ) { + if(i==0) { + printk(KERN_ERR "%s: got valid panel ID=%d, " + "without retry\n", + __FUNCTION__, id); + } + else { + printk(KERN_ERR "%s: got valid panel ID=%d, " + "after %d retry\n", + __FUNCTION__, id, i+1); + } + break ; + } + printk(KERN_ERR "%s: got invalid panel ID:%d, trial #%d\n", + __FUNCTION__, id, i+1); + + gpio_set_value(MDDI_RST_N, 0); + msleep(5); + + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + gpio_set_value(MDDI_RST_N, 0); + udelay(100); + gpio_set_value(MDDI_RST_N, 1); + mdelay(10); + } + printk(KERN_INFO "%s: final panel id=%d\n", __FUNCTION__, id); + + switch(id) { + case 0: + return SAPPHIRE_PANEL_TOPPOLY; + case 1: + return SAPPHIRE_PANEL_SHARP; + default : + printk(KERN_ERR "%s(): Invalid panel ID: %d, " + "treat as sharp panel.", __FUNCTION__, id); + return SAPPHIRE_PANEL_SHARP; + } +} + +static int nt35399_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id; + + if (g_panel_inited == 0) { + g_panel_id = panel_id = nt35399_detect_panel(client_data); + g_panel_inited = 1 ; + } else { + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + gpio_set_value(MDDI_RST_N, 0); + udelay(100); + gpio_set_value(MDDI_RST_N, 1); + mdelay(10); + + g_panel_id = panel_id = nt35399_detect_panel(client_data); + if (panel_id == -1) { + printk("Invalid panel id\n"); + return -1; + } + + client_data->auto_hibernate(client_data, 0); + if (panel_id == SAPPHIRE_PANEL_TOPPOLY) { + sapphire_process_mddi_table(client_data, tpo2_init_table, + ARRAY_SIZE(tpo2_init_table)); + } else if(panel_id == SAPPHIRE_PANEL_SHARP) { + sapphire_process_mddi_table(client_data, sharp2_init_table, + ARRAY_SIZE(sharp2_init_table)); + } + + client_data->auto_hibernate(client_data, 1); + } + + return 0; +} + +static int nt35399_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *cdata) +{ + return 0; +} + +static int nt35399_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int ret = 0; + + mdelay(20); + sapphire_set_backlight_level(0); + client_data->auto_hibernate(client_data, 0); + + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(sapphire_backlight_brightness); + sapphire_backlight_off = 0; + mutex_unlock(&sapphire_backlight_lock); + + client_data->auto_hibernate(client_data, 1); + + return ret; +} + +static int nt35399_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int ret = 0; + + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, tpo2_display_off, + ARRAY_SIZE(tpo2_display_off)); + client_data->auto_hibernate(client_data, 1); + + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(0); + sapphire_backlight_off = 1; + mutex_unlock(&sapphire_backlight_lock); + + return ret; +} + +static void sapphire_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + mutex_lock(&sapphire_backlight_lock); + sapphire_backlight_brightness = value; + if (!sapphire_backlight_off) + sapphire_set_backlight_level(sapphire_backlight_brightness); + mutex_unlock(&sapphire_backlight_lock); +} + +static struct led_classdev sapphire_backlight_led = { + .name = "lcd-backlight", + .brightness = SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS, + .brightness_set = sapphire_brightness_set, +}; + +static int sapphire_backlight_probe(struct platform_device *pdev) +{ + led_classdev_register(&pdev->dev, &sapphire_backlight_led); + return 0; +} + +static int sapphire_backlight_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&sapphire_backlight_led); + return 0; +} + +static struct platform_driver sapphire_backlight_driver = { + .probe = sapphire_backlight_probe, + .remove = sapphire_backlight_remove, + .driver = { + .name = "sapphire-backlight", + .owner = THIS_MODULE, + }, +}; + +static struct resource resources_msm_fb[] = { + { + .start = SMI64_MSM_FB_BASE, + .end = SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_mddi_bridge_platform_data toshiba_client_data = { + .init = sapphire_mddi_toshiba_client_init, + .uninit = sapphire_mddi_toshiba_client_uninit, + .blank = sapphire_mddi_panel_blank, + .unblank = sapphire_mddi_panel_unblank, + .fb_data = { + .xres = 320, + .yres = 480, + .width = 45, + .height = 67, + .output_format = 0, + }, +}; + +#define NT35399_MFR_NAME 0x0bda +#define NT35399_PRODUCT_CODE 0x8a47 + +static void nt35399_fixup(uint16_t * mfr_name, uint16_t * product_code) +{ + printk(KERN_DEBUG "%s: enter.\n", __func__); + *mfr_name = NT35399_MFR_NAME ; + *product_code= NT35399_PRODUCT_CODE ; +} + +static struct msm_mddi_bridge_platform_data nt35399_client_data = { + + .init = nt35399_client_init, + .uninit = nt35399_client_uninit, + .blank = nt35399_panel_blank, + .unblank = nt35399_panel_unblank, + .fb_data = { + .xres = 320, + .yres = 480, + .output_format = 0, + }, +}; + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 122880000, + .power_client = sapphire_mddi_power_client, + .fixup = nt35399_fixup, + .vsync_irq = MSM_GPIO_TO_INT(VSYNC_GPIO), + .fb_resource = resources_msm_fb, + .num_clients = 2, + .client_platform_data = { + { + .product_id = (0xd263 << 16 | 0), + .name = "mddi_c_d263_0000", + .id = 0, + .client_data = &toshiba_client_data, + .clk_rate = 0, + }, + { + .product_id = + (NT35399_MFR_NAME << 16 | NT35399_PRODUCT_CODE), + .name = "mddi_c_simple" , + .id = 0, + .client_data = &nt35399_client_data, + .clk_rate = 0, + }, + }, +}; + +static struct platform_device sapphire_backlight = { + .name = "sapphire-backlight", +}; + +int __init sapphire_init_panel(void) +{ + int rc = -1; + uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA); /* GPIO27 */ + + if (!machine_is_sapphire()) + return 0; + + /* checking board as soon as possible */ + printk("sapphire_init_panel:machine_is_sapphire=%d, machine_arch_type=%d, MACH_TYPE_SAPPHIRE=%d\r\n", machine_is_sapphire(), machine_arch_type, MACH_TYPE_SAPPHIRE); + if (!machine_is_sapphire()) + return 0; + + vreg_lcm_2v85 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcm_2v85)) + return PTR_ERR(vreg_lcm_2v85); + + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + + /* setup FB by SMI size */ + if (sapphire_get_smi_size() == 32) { + resources_msm_fb[0].start = SMI32_MSM_FB_BASE; + resources_msm_fb[0].end = SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE - 1; + } + + rc = gpio_request(VSYNC_GPIO, "vsync"); + if (rc) + return rc; + rc = gpio_direction_input(VSYNC_GPIO); + if (rc) + return rc; + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + platform_device_register(&sapphire_backlight); + return platform_driver_register(&sapphire_backlight_driver); +} + +device_initcall(sapphire_init_panel); diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c new file mode 100644 index 00000000000..2fd6ea198e3 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-rfkill.c @@ -0,0 +1,105 @@ +/* linux/arch/arm/mach-msm/board-sapphire-rfkill.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. +*/ + +/* Control bluetooth power for sapphire platform */ + +#include +#include +#include +#include +#include +#include +#include +#include "gpio_chip.h" +#include "board-sapphire.h" + +static struct rfkill *bt_rfk; +static const char bt_name[] = "brf6300"; + +extern int sapphire_bt_fastclock_power(int on); + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + sapphire_bt_fastclock_power(1); + gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_direction_output(101, 1); + } else { + gpio_direction_output(101, 0); + gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 0); + sapphire_bt_fastclock_power(0); + } + return 0; +} + +static struct rfkill_ops sapphire_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int sapphire_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &sapphire_rfkill_ops, NULL); + if (!bt_rfk) + return -ENOMEM; + + /* userspace cannot take exclusive control */ + + rfkill_set_states(bt_rfk, default_state, false); + + rc = rfkill_register(bt_rfk); + + if (rc) + rfkill_destroy(bt_rfk); + return rc; +} + +static int sapphire_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + + return 0; +} + +static struct platform_driver sapphire_rfkill_driver = { + .probe = sapphire_rfkill_probe, + .remove = sapphire_rfkill_remove, + .driver = { + .name = "sapphire_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init sapphire_rfkill_init(void) +{ + return platform_driver_register(&sapphire_rfkill_driver); +} + +static void __exit sapphire_rfkill_exit(void) +{ + platform_driver_unregister(&sapphire_rfkill_driver); +} + +module_init(sapphire_rfkill_init); +module_exit(sapphire_rfkill_exit); +MODULE_DESCRIPTION("sapphire rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-sapphire-wifi.c b/arch/arm/mach-msm/board-sapphire-wifi.c new file mode 100644 index 00000000000..43f827c60f1 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-wifi.c @@ -0,0 +1,74 @@ +/* arch/arm/mach-msm/board-sapphire-wifi.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include +#include +#include +#include +#include +#include + +extern int sapphire_wifi_set_carddetect(int val); +extern int sapphire_wifi_power(int on); +extern int sapphire_wifi_reset(int on); + +#ifdef CONFIG_WIFI_MEM_PREALLOC +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = { + { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) } +}; + +static void *sapphire_wifi_mem_prealloc(int section, unsigned long size) +{ + if ((section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init sapphire_init_wifi_mem (void) +{ + int i; + + for (i = 0; (i < WMPA_NUMBER_OF_SECTIONS); i++) { + wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} +#endif + +struct wifi_platform_data sapphire_wifi_control = { + .set_power = sapphire_wifi_power, + .set_reset = sapphire_wifi_reset, + .set_carddetect = sapphire_wifi_set_carddetect, +#ifdef CONFIG_WIFI_MEM_PREALLOC + .mem_prealloc = sapphire_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif +}; + +#endif diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 4a8ea0d40b6..abb61577e2d 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,6 @@ #include "gpio_chip.h" #include "board-sapphire.h" -#include "proc_comm.h" #include "devices.h" void msm_init_irq(void); diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h new file mode 100644 index 00000000000..d96760a25ef --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire.h @@ -0,0 +1,224 @@ +/* linux/arch/arm/mach-msm/board-sapphire.h + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ +#ifndef __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H +#define __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H + +#include + +#define MSM_SMI_BASE 0x00000000 +#define MSM_SMI_SIZE 0x00800000 + +#define MSM_EBI_BASE 0x10000000 +#define MSM_EBI_SIZE 0x07100000 + +#define MSM_PMEM_GPU0_BASE 0x00000000 +#define MSM_PMEM_GPU0_SIZE 0x00700000 + +#define SMI64_MSM_PMEM_MDP_BASE 0x15900000 +#define SMI64_MSM_PMEM_MDP_SIZE 0x00800000 + +#define SMI64_MSM_PMEM_ADSP_BASE 0x16100000 +#define SMI64_MSM_PMEM_ADSP_SIZE 0x00800000 + +#define SMI64_MSM_PMEM_CAMERA_BASE 0x15400000 +#define SMI64_MSM_PMEM_CAMERA_SIZE 0x00500000 + +#define SMI64_MSM_FB_BASE 0x00700000 +#define SMI64_MSM_FB_SIZE 0x00100000 + +#define SMI64_MSM_LINUX_BASE MSM_EBI_BASE +#define SMI64_MSM_LINUX_SIZE 0x068e0000 + +#define SMI64_MSM_LINUX_BASE_1 0x02000000 +#define SMI64_MSM_LINUX_SIZE_1 0x02000000 + +#define SMI64_MSM_LINUX_BASE_2 MSM_EBI_BASE +#define SMI64_MSM_LINUX_SIZE_2 0x05400000 + +#define SMI32_MSM_LINUX_BASE MSM_EBI_BASE +#define SMI32_MSM_LINUX_SIZE 0x5400000 + +#define SMI32_MSM_PMEM_MDP_BASE SMI32_MSM_LINUX_BASE + SMI32_MSM_LINUX_SIZE +#define SMI32_MSM_PMEM_MDP_SIZE 0x800000 + +#define SMI32_MSM_PMEM_ADSP_BASE SMI32_MSM_PMEM_MDP_BASE + SMI32_MSM_PMEM_MDP_SIZE +#define SMI32_MSM_PMEM_ADSP_SIZE 0x800000 + +#define SMI32_MSM_FB_BASE SMI32_MSM_PMEM_ADSP_BASE + SMI32_MSM_PMEM_ADSP_SIZE +#define SMI32_MSM_FB_SIZE 0x9b000 + + +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_PMEM_GPU1_BASE (MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE) + +#define MSM_RAM_CONSOLE_BASE 0x169E0000 +#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K + +#if (SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#if (SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#define DECLARE_MSM_IOMAP +#include + +/* +** SOC GPIO +*/ +#define SAPPHIRE_BALL_UP_0 94 +#define SAPPHIRE_BALL_LEFT_0 18 +#define SAPPHIRE_BALL_DOWN_0 49 +#define SAPPHIRE_BALL_RIGHT_0 19 + +#define SAPPHIRE_POWER_KEY 20 +#define SAPPHIRE_VOLUME_UP 36 +#define SAPPHIRE_VOLUME_DOWN 39 + +#define SAPPHIRE_GPIO_PS_HOLD (25) +#define SAPPHIRE_MDDI_1V5_EN (28) +#define SAPPHIRE_BL_PWM (27) +#define SAPPHIRE_TP_LS_EN (1) +#define SAPPHIRE20_TP_LS_EN (88) + +/* H2W */ +#define SAPPHIRE_GPIO_CABLE_IN1 (83) +#define SAPPHIRE_GPIO_CABLE_IN2 (37) +#define SAPPHIRE_GPIO_UART3_RX (86) +#define SAPPHIRE_GPIO_UART3_TX (87) +#define SAPPHIRE_GPIO_H2W_DATA (86) +#define SAPPHIRE_GPIO_H2W_CLK (87) + +#define SAPPHIRE_GPIO_UART1_RTS (43) +#define SAPPHIRE_GPIO_UART1_CTS (44) + +/* +** CPLD GPIO +** +** Sapphire Altera CPLD can keep the registers value and +** doesn't need a shadow to backup. +**/ +#define SAPPHIRE_CPLD_BASE 0xFA000000 /* VA */ +#define SAPPHIRE_CPLD_START 0x98000000 /* PA */ +#define SAPPHIRE_CPLD_SIZE SZ_4K + +#define SAPPHIRE_GPIO_START (128) /* Pseudo GPIO number */ + +/* Sapphire has one INT BANK only. */ +#define SAPPHIRE_GPIO_INT_B0_MASK_REG (0x0c) /*INT3 MASK*/ +#define SAPPHIRE_GPIO_INT_B0_STAT_REG (0x0e) /*INT1 STATUS*/ + +/* LED control register */ +#define SAPPHIRE_CPLD_LED_BASE (SAPPHIRE_CPLD_BASE + 0x10) /* VA */ +#define SAPPHIRE_CPLD_LED_START (SAPPHIRE_CPLD_START + 0x10) /* PA */ +#define SAPPHIRE_CPLD_LED_SIZE 0x08 + +/* MISCn: GPO pin to Enable/Disable some functions. */ +#define SAPPHIRE_GPIO_MISC1_BASE (SAPPHIRE_GPIO_START + 0x00) +#define SAPPHIRE_GPIO_MISC2_BASE (SAPPHIRE_GPIO_START + 0x08) +#define SAPPHIRE_GPIO_MISC3_BASE (SAPPHIRE_GPIO_START + 0x10) +#define SAPPHIRE_GPIO_MISC4_BASE (SAPPHIRE_GPIO_START + 0x18) +#define SAPPHIRE_GPIO_MISC5_BASE (SAPPHIRE_GPIO_START + 0x20) + +/* INT BANK0: INT1: int status, INT2: int level, INT3: int Mask */ +#define SAPPHIRE_GPIO_INT_B0_BASE (SAPPHIRE_GPIO_START + 0x28) + +/* MISCn GPIO: */ +#define SAPPHIRE_GPIO_CPLD128_VER_0 (SAPPHIRE_GPIO_MISC1_BASE + 4) +#define SAPPHIRE_GPIO_CPLD128_VER_1 (SAPPHIRE_GPIO_MISC1_BASE + 5) +#define SAPPHIRE_GPIO_CPLD128_VER_2 (SAPPHIRE_GPIO_MISC1_BASE + 6) +#define SAPPHIRE_GPIO_CPLD128_VER_3 (SAPPHIRE_GPIO_MISC1_BASE + 7) + +#define SAPPHIRE_GPIO_H2W_DAT_DIR (SAPPHIRE_GPIO_MISC2_BASE + 2) +#define SAPPHIRE_GPIO_H2W_CLK_DIR (SAPPHIRE_GPIO_MISC2_BASE + 3) +#define SAPPHIRE_GPIO_H2W_SEL0 (SAPPHIRE_GPIO_MISC2_BASE + 6) +#define SAPPHIRE_GPIO_H2W_SEL1 (SAPPHIRE_GPIO_MISC2_BASE + 7) + +#define SAPPHIRE_GPIO_I2C_PULL (SAPPHIRE_GPIO_MISC3_BASE + 2) +#define SAPPHIRE_GPIO_TP_EN (SAPPHIRE_GPIO_MISC3_BASE + 4) +#define SAPPHIRE_GPIO_JOG_EN (SAPPHIRE_GPIO_MISC3_BASE + 5) +#define SAPPHIRE_GPIO_JOG_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 6) +#define SAPPHIRE_GPIO_APKEY_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 7) + +#define SAPPHIRE_GPIO_VCM_PWDN (SAPPHIRE_GPIO_MISC4_BASE + 0) +#define SAPPHIRE_GPIO_USB_H2W_SW (SAPPHIRE_GPIO_MISC4_BASE + 1) +#define SAPPHIRE_GPIO_COMPASS_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 2) +#define SAPPHIRE_GPIO_USB_PHY_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 5) +#define SAPPHIRE_GPIO_WIFI_PA_RESETX (SAPPHIRE_GPIO_MISC4_BASE + 6) +#define SAPPHIRE_GPIO_WIFI_EN (SAPPHIRE_GPIO_MISC4_BASE + 7) + +#define SAPPHIRE_GPIO_BT_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 0) +#define SAPPHIRE_GPIO_MAC_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 1) +#define SAPPHIRE_GPIO_MDDI_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 2) +#define SAPPHIRE_GPIO_COMPASS_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 3) + +/* INT STATUS/LEVEL/MASK : INT GPIO should be the last. */ +#define SAPPHIRE_GPIO_NAVI_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 0) +#define SAPPHIRE_GPIO_COMPASS_IRQ (SAPPHIRE_GPIO_INT_B0_BASE + 1) +#define SAPPHIRE_GPIO_SEARCH_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 2) +#define SAPPHIRE_GPIO_AUD_HSMIC_DET_N (SAPPHIRE_GPIO_INT_B0_BASE + 3) +#define SAPPHIRE_GPIO_SDMC_CD_N (SAPPHIRE_GPIO_INT_B0_BASE + 4) +#define SAPPHIRE_GPIO_CAM_BTN_STEP1_N (SAPPHIRE_GPIO_INT_B0_BASE + 5) +#define SAPPHIRE_GPIO_CAM_BTN_STEP2_N (SAPPHIRE_GPIO_INT_B0_BASE + 6) +#define SAPPHIRE_GPIO_TP_ATT_N (SAPPHIRE_GPIO_INT_B0_BASE + 7) + +#define SAPPHIRE_GPIO_END SAPPHIRE_GPIO_TP_ATT_N +#define SAPPHIRE_GPIO_LAST_INT (SAPPHIRE_GPIO_TP_ATT_N) + +/* Bit position in the CPLD MISCn by the CPLD GPIOn: only bit0-7 is used. */ +#define CPLD_GPIO_BIT_POS_MASK(n) (1U << ((n) & 7)) +#define CPLD_GPIO_REG_OFFSET(n) _g_CPLD_MISCn_Offset[((n)-SAPPHIRE_GPIO_START) >> 3] +#define CPLD_GPIO_REG(n) (CPLD_GPIO_REG_OFFSET(n) + SAPPHIRE_CPLD_BASE) + +/* +** CPLD INT Start +*/ +#define SAPPHIRE_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS) /* pseudo number for CPLD INT */ +/* Using INT status/Bank0 for GPIO to INT */ +#define SAPPHIRE_GPIO_TO_INT(n) ((n-SAPPHIRE_GPIO_INT_B0_BASE) + SAPPHIRE_INT_START) +#define SAPPHIRE_INT_END (SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_END)) + +/* get the INT reg by GPIO number */ +#define CPLD_INT_GPIO_TO_BANK(n) (((n)-SAPPHIRE_GPIO_INT_B0_BASE) >> 3) +#define CPLD_INT_STATUS_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][0] +#define CPLD_INT_LEVEL_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][1] +#define CPLD_INT_MASK_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][2] +#define CPLD_INT_STATUS_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET_G(n)) +#define CPLD_INT_LEVEL_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET_G(n)) +#define CPLD_INT_MASK_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET_G(n)) + +/* get the INT reg by INT number */ +#define CPLD_INT_TO_BANK(i) ((i-SAPPHIRE_INT_START) >> 3) +#define CPLD_INT_STATUS_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][0] +#define CPLD_INT_LEVEL_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][1] +#define CPLD_INT_MASK_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][2] +#define CPLD_INT_STATUS_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET(i)) +#define CPLD_INT_LEVEL_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET(i)) +#define CPLD_INT_MASK_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET(i) ) + +/* return the bit mask by INT number */ +#define SAPPHIRE_INT_BIT_MASK(i) (1U << ((i - SAPPHIRE_INT_START) & 7)) + +void config_sapphire_camera_on_gpios(void); +void config_sapphire_camera_off_gpios(void); +int sapphire_get_smi_size(void); +unsigned int sapphire_get_hwid(void); +unsigned int sapphire_get_skuid(void); +unsigned int is_12pin_camera(void); +int sapphire_is_5M_camera(void); +int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on); + +#endif /* GUARD */ diff --git a/arch/arm/mach-msm/board-storage-common-a.h b/arch/arm/mach-msm/board-storage-common-a.h new file mode 100644 index 00000000000..7737819bf4c --- /dev/null +++ b/arch/arm/mach-msm/board-storage-common-a.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _BOARD_STORAGE_A_H +#define _BOARD_STORAGE_A_H + +#include +#include +#include + +#define MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(num, _ib) \ +static struct msm_bus_vectors sps_to_ddr_perf_vectors_##num[] = { \ + { \ + .src = MSM_BUS_MASTER_SPS, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ib = (_ib), \ + .ab = ((_ib) / 2), \ + } \ +} + +#define MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(num) \ + { \ + ARRAY_SIZE(sps_to_ddr_perf_vectors_##num), \ + sps_to_ddr_perf_vectors_##num, \ + } + +/* no bandwidth required */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(0, 0); +/* + * 13 MB/s bandwidth + * 4-bit MMC_TIMING_LEGACY + * 4-bit MMC_TIMING_UHS_SDR12 + */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(1, 13 * 1024 * 1024); +/* + * 26 MB/s bandwidth + * 8-bit MMC_TIMING_LEGACY + * 4-bit MMC_TIMING_MMC_HS / MMC_TIMING_SD_HS / + * MMC_TIMING_UHS_SDR25 + */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(2, 26 * 1024 * 1024); +/* + * 52 MB/s bandwidth + * 8-bit MMC_TIMING_MMC_HS + * 4-bit MMC_TIMING_UHS_SDR50 / MMC_TIMING_UHS_DDR50 + */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(3, 52 * 1024 * 1024); +/* + * 104 MB/s bandwidth + * 8-bit MMC_TIMING_UHS_DDR50 + * 4-bit MMC_TIMING_UHS_SDR104 / MMC_TIMING_MMC_HS200 + */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(4, 104 * 1024 * 1024); +/* + * 200 MB/s bandwidth + * 8-bit MMC_TIMING_MMC_HS200 + */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(5, 200 * 1024 * 1024); +/* max. possible bandwidth */ +MSM_BUS_SPS_TO_DDR_VOTE_VECTOR(6, UINT_MAX); + +static unsigned int sdcc_bw_vectors[] = {0, (13 * 1024 * 1024), + (26 * 1024 * 1024), (52 * 1024 * 1024), + (104 * 1024 * 1024), (200 * 1024 * 1024), + UINT_MAX}; + +static struct msm_bus_paths sps_to_ddr_bus_scale_usecases[] = { + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(0), + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(1), + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(2), + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(3), + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(4), + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(5), + MSM_BUS_SPS_TO_DDR_VOTE_VECTOR_USECASE(6), +}; + +static struct msm_bus_scale_pdata sps_to_ddr_bus_scale_data = { + sps_to_ddr_bus_scale_usecases, + ARRAY_SIZE(sps_to_ddr_bus_scale_usecases), + .name = "msm_sdcc", +}; + +static struct msm_mmc_bus_voting_data sps_to_ddr_bus_voting_data = { + .use_cases = &sps_to_ddr_bus_scale_data, + .bw_vecs = sdcc_bw_vectors, + .bw_vecs_size = sizeof(sdcc_bw_vectors), +}; + +#endif /* _BOARD_STORAGE_A_H */ diff --git a/arch/arm/mach-msm/board-swordfish-keypad.c b/arch/arm/mach-msm/board-swordfish-keypad.c new file mode 100644 index 00000000000..f2c2f3962f6 --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish-keypad.c @@ -0,0 +1,177 @@ +/* linux/arch/arm/mach-msm/board-swordfish-keypad.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_swordfish." +static int swordfish_ffa; +module_param_named(ffa, swordfish_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */ + +static unsigned int swordfish_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int swordfish_col_gpios[] = { 36, 37, 38, 39, 40 }; + +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(swordfish_col_gpios) + (col)) + +static const unsigned short swordfish_keymap[ARRAY_SIZE(swordfish_col_gpios) * ARRAY_SIZE(swordfish_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static const unsigned short swordfish_keymap_ffa[ARRAY_SIZE(swordfish_col_gpios) * ARRAY_SIZE(swordfish_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +static struct gpio_event_matrix_info swordfish_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = swordfish_keymap, + .output_gpios = swordfish_row_gpios, + .input_gpios = swordfish_col_gpios, + .noutputs = ARRAY_SIZE(swordfish_row_gpios), + .ninputs = ARRAY_SIZE(swordfish_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +struct gpio_event_info *swordfish_keypad_info[] = { + &swordfish_matrix_info.info +}; + +static struct gpio_event_platform_data swordfish_keypad_data = { + .name = "swordfish_keypad", + .info = swordfish_keypad_info, + .info_count = ARRAY_SIZE(swordfish_keypad_info) +}; + +static struct platform_device swordfish_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &swordfish_keypad_data, + }, +}; + +static int __init swordfish_init_keypad(void) +{ + if (!machine_is_swordfish()) + return 0; + if (swordfish_ffa) + swordfish_matrix_info.keymap = swordfish_keymap_ffa; + return platform_device_register(&swordfish_keypad_device); +} + +device_initcall(swordfish_init_keypad); diff --git a/arch/arm/mach-msm/board-swordfish-mmc.c b/arch/arm/mach-msm/board-swordfish-mmc.c new file mode 100644 index 00000000000..e4a2a64df0d --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish-mmc.c @@ -0,0 +1,263 @@ +/* linux/arch/arm/mach-msm/board-swordfish-mmc.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "devices.h" + +#define FPGA_BASE 0x70000000 +#define FPGA_SDIO_STATUS 0x280 + +static void __iomem *fpga_base; + +#define DEBUG_SWORDFISH_MMC 1 + +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + +static int config_gpio_table(unsigned *table, int len, int enable) +{ + int n; + int rc = 0; + + for (n = 0; n < len; n++) { + unsigned dis = !enable; + unsigned id = table[n]; + + if (msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, &dis)) { + pr_err("%s: id=0x%08x dis=%d\n", __func__, table[n], + dis); + rc = -1; + } + } + + return rc; +} + +static unsigned sdc1_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), +}; + +static unsigned sdc2_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), +}; + +static unsigned sdc3_gpio_table[] = { + PCOM_GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), + PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), +}; + +static unsigned sdc4_gpio_table[] = { + PCOM_GPIO_CFG(142, 3, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), + PCOM_GPIO_CFG(143, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(144, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(145, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(146, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(147, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), +}; + +struct sdc_info { + unsigned *table; + unsigned len; +}; + +static struct sdc_info sdcc_gpio_tables[] = { + [0] = { + .table = sdc1_gpio_table, + .len = ARRAY_SIZE(sdc1_gpio_table), + }, + [1] = { + .table = sdc2_gpio_table, + .len = ARRAY_SIZE(sdc2_gpio_table), + }, + [2] = { + .table = sdc3_gpio_table, + .len = ARRAY_SIZE(sdc3_gpio_table), + }, + [3] = { + .table = sdc4_gpio_table, + .len = ARRAY_SIZE(sdc4_gpio_table), + }, +}; + +static int swordfish_sdcc_setup_gpio(int dev_id, unsigned enable) +{ + struct sdc_info *info; + + if (dev_id < 1 || dev_id > 4) + return -1; + + info = &sdcc_gpio_tables[dev_id - 1]; + return config_gpio_table(info->table, info->len, enable); +} + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static struct vreg *vreg_sdcc; +static unsigned int vreg_sdcc_enabled; +static unsigned int sdcc_vdd = 0xffffffff; + +static uint32_t sdcc_translate_vdd(struct device *dev, unsigned int vdd) +{ + int i; + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dev, struct platform_device, dev); + BUG_ON(!vreg_sdcc); + + if (vdd == sdcc_vdd) + return 0; + + sdcc_vdd = vdd; + + /* enable/disable the signals to the slot */ + swordfish_sdcc_setup_gpio(pdev->id, !!vdd); + + /* power down */ + if (vdd == 0) { +#if DEBUG_SWORDFISH_MMC + pr_info("%s: disable sdcc power\n", __func__); +#endif + vreg_disable(vreg_sdcc); + vreg_sdcc_enabled = 0; + return 0; + } + + if (!vreg_sdcc_enabled) { + rc = vreg_enable(vreg_sdcc); + if (rc) + pr_err("%s: Error enabling vreg (%d)\n", __func__, rc); + vreg_sdcc_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; +#if DEBUG_SWORDFISH_MMC + pr_info("%s: Setting level to %u\n", __func__, + mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdcc, mmc_vdd_table[i].level); + if (rc) + pr_err("%s: Error setting vreg level (%d)\n", __func__, rc); + return 0; + } + + pr_err("%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int swordfish_sdcc_slot_status (struct device *dev) +{ + struct platform_device *pdev; + uint32_t sdcc_stat; + + pdev = container_of(dev, struct platform_device, dev); + + sdcc_stat = readl(fpga_base + FPGA_SDIO_STATUS); + + /* bit 0 - sdcc1 crd_det + * bit 1 - sdcc1 wr_prt + * bit 2 - sdcc2 crd_det + * bit 3 - sdcc2 wr_prt + * etc... + */ + + /* crd_det is active low */ + return !(sdcc_stat & (1 << ((pdev->id - 1) << 1))); +} + +#define SWORDFISH_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30) + +static struct mmc_platform_data swordfish_sdcc_data = { + .ocr_mask = SWORDFISH_MMC_VDD/*MMC_VDD_27_28 | MMC_VDD_28_29*/, + .status = swordfish_sdcc_slot_status, + .translate_vdd = sdcc_translate_vdd, +}; + +int __init swordfish_init_mmc(void) +{ + vreg_sdcc_enabled = 0; + vreg_sdcc = vreg_get(NULL, "gp5"); + if (IS_ERR(vreg_sdcc)) { + pr_err("%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_sdcc)); + return PTR_ERR(vreg_sdcc); + } + + fpga_base = ioremap(FPGA_BASE, SZ_4K); + if (!fpga_base) { + pr_err("%s: Can't ioremap FPGA base address (0x%08x)\n", + __func__, FPGA_BASE); + vreg_put(vreg_sdcc); + return -EIO; + } + + msm_add_sdcc(1, &swordfish_sdcc_data, 0, 0); + msm_add_sdcc(4, &swordfish_sdcc_data, 0, 0); + + return 0; +} + diff --git a/arch/arm/mach-msm/board-swordfish-panel.c b/arch/arm/mach-msm/board-swordfish-panel.c new file mode 100644 index 00000000000..cf5f3f62b76 --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish-panel.c @@ -0,0 +1,116 @@ +/* linux/arch/arm/mach-msm/board-swordfish-panel.c + * + * Copyright (c) 2009 Google Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Dima Zavin + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "board-swordfish.h" +#include "devices.h" + +#define CLK_NS_TO_RATE(ns) (1000000000UL / (ns)) + +int swordfish_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + /* TODO: Turn backlight off? */ + return 0; +} + +int swordfish_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + /* TODO: Turn backlight on? */ + return 0; +} + +int swordfish_panel_init(struct msm_lcdc_panel_ops *ops) +{ + return 0; +} + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_lcdc_timing swordfish_lcdc_timing = { + .clk_rate = CLK_NS_TO_RATE(26), + .hsync_pulse_width = 60, + .hsync_back_porch = 81, + .hsync_front_porch = 81, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 20, + .vsync_front_porch = 27, + .vsync_act_low = 0, + .hsync_act_low = 0, + .den_act_low = 0, +}; + +static struct msm_fb_data swordfish_lcdc_fb_data = { + .xres = 800, + .yres = 480, + .width = 94, + .height = 57, + .output_format = 0, +}; + +static struct msm_lcdc_panel_ops swordfish_lcdc_panel_ops = { + .init = swordfish_panel_init, + .blank = swordfish_panel_blank, + .unblank = swordfish_panel_unblank, +}; + +static struct msm_lcdc_platform_data swordfish_lcdc_platform_data = { + .panel_ops = &swordfish_lcdc_panel_ops, + .timing = &swordfish_lcdc_timing, + .fb_id = 0, + .fb_data = &swordfish_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct platform_device swordfish_lcdc_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &swordfish_lcdc_platform_data, + }, +}; + +int __init swordfish_init_panel(void) +{ + int rc; + if (!machine_is_swordfish()) + return 0; + + if ((rc = platform_device_register(&msm_device_mdp)) != 0) + return rc; + + if ((rc = platform_device_register(&swordfish_lcdc_device)) != 0) + return rc; + + return 0; +} + +device_initcall(swordfish_init_panel); diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c new file mode 100644 index 00000000000..45d5bb07adf --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish.c @@ -0,0 +1,366 @@ +/* linux/arch/arm/mach-msm/board-swordfish.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "board-swordfish.h" +#include "devices.h" + +extern int swordfish_init_mmc(void); + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x70000300, + .end = 0x70000400, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(156), + .end = MSM_GPIO_TO_INT(156), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static int swordfish_phy_init_seq[] = { + 0x0C, 0x31, + 0x1D, 0x0D, + 0x1D, 0x10, + -1 +}; + +static void swordfish_usb_phy_reset(void) +{ + u32 id; + int ret; + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } + + msleep(1); + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +static void swordfish_usb_hw_reset(bool enable) +{ + u32 id; + int ret; + u32 func; + + id = PCOM_CLKRGM_APPS_RESET_USBH; + if (enable) + func = PCOM_CLK_REGIME_SEC_RESET_ASSERT; + else + func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT; + ret = msm_proc_comm(func, &id, NULL); + if (ret) + pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, + ret); +} + + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = swordfish_phy_init_seq, + .phy_reset = swordfish_usb_phy_reset, + .hw_reset = swordfish_usb_hw_reset, +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Qualcomm", + .product = "Swordfish", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_phys_memory", + .start = MSM_GPU_MEM_BASE, + .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + +static struct android_pmem_platform_data mdp_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .start = MSM_PMEM_GPU0_BASE, + .size = MSM_PMEM_GPU0_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .start = MSM_PMEM_GPU1_BASE, + .size = MSM_PMEM_GPU1_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + +static struct platform_device android_pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { + .platform_data = &android_pmem_gpu0_pdata, + }, +}; + +static struct platform_device android_pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { + .platform_data = &android_pmem_gpu1_pdata, + }, +}; + +static char *usb_functions[] = { "usb_mass_storage" }; +static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0c01, + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { + .product_id = 0x0c02, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +}; + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x18d1, + .product_id = 0x0d01, + .version = 0x0100, + .serial_number = "42", + .product_name = "Swordfishdroid", + .manufacturer_name = "Qualcomm", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static struct platform_device fish_battery_device = { + .name = "fish_battery", +}; + +static struct msm_ts_platform_data swordfish_ts_pdata = { + .min_x = 296, + .max_x = 3800, + .min_y = 296, + .max_y = 3800, + .min_press = 0, + .max_press = 256, + .inv_x = 4096, + .inv_y = 4096, +}; + +static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart3, +#endif + &msm_device_smd, + &msm_device_dmov, + &msm_device_nand, + &msm_device_hsusb, + &usb_mass_storage_device, + &android_usb_device, + &fish_battery_device, + &smc91x_device, + &msm_device_touchscreen, + &android_pmem_mdp_device, + &android_pmem_adsp_device, + &android_pmem_gpu0_device, + &android_pmem_gpu1_device, + &msm_kgsl_device, +}; + +extern struct sys_timer msm_timer; + +static struct msm_acpu_clock_platform_data swordfish_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 128000000, + .wait_for_irq_khz = 128000000, +}; + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static void __init swordfish_init(void) +{ + int rc; + + msm_acpu_clock_init(&swordfish_clock_data); +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + msm_device_touchscreen.dev.platform_data = &swordfish_ts_pdata; + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_hsusb_set_vbus_state(1); + rc = swordfish_init_mmc(); + if (rc) + pr_crit("%s: MMC init failure (%d)\n", __func__, rc); +} + +static void __init swordfish_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + mi->bank[0].size = (101*1024*1024); +} + +static void __init swordfish_map_io(void) +{ + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +MACHINE_START(SWORDFISH, "Swordfish Board (QCT SURF8250)") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .atag_offset = 0x100, + .fixup = swordfish_fixup, + .map_io = swordfish_map_io, + .init_irq = msm_init_irq, + .init_machine = swordfish_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(QSD8X50_FFA, "qsd8x50 FFA Board (QCT FFA8250)") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .atag_offset = 0x100, + .fixup = swordfish_fixup, + .map_io = swordfish_map_io, + .init_irq = msm_init_irq, + .init_machine = swordfish_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-swordfish.h b/arch/arm/mach-msm/board-swordfish.h new file mode 100644 index 00000000000..b9ea54f680d --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish.h @@ -0,0 +1,48 @@ +/* arch/arm/mach-msm/board-swordfish.h + * + * Copyright (C) 2009 Google Inc. + * Author: Dima Zavin + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H +#define __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H + +#include + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#define MSM_PMEM_MDP_BASE 0x03000000 +#define MSM_PMEM_MDP_SIZE 0x01000000 + +#define MSM_EBI1_BASE 0x20000000 +#define MSM_EBI1_SIZE 0x0E000000 + +#define MSM_PMEM_ADSP_BASE 0x2A300000 +#define MSM_PMEM_ADSP_SIZE 0x02000000 + +#define MSM_PMEM_GPU1_BASE 0x2C300000 +#define MSM_PMEM_GPU1_SIZE 0x01400000 + +#define MSM_PMEM_GPU0_BASE 0x2D700000 +#define MSM_PMEM_GPU0_SIZE 0x00400000 + +#define MSM_GPU_MEM_BASE 0x2DB00000 +#define MSM_GPU_MEM_SIZE 0x00200000 + +#define MSM_RAM_CONSOLE_BASE 0x2DD00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#define MSM_FB_BASE 0x2DE00000 +#define MSM_FB_SIZE 0x00200000 + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H */ diff --git a/arch/arm/mach-msm/board-trout-keypad.c b/arch/arm/mach-msm/board-trout-keypad.c new file mode 100644 index 00000000000..0299d0686de --- /dev/null +++ b/arch/arm/mach-msm/board-trout-keypad.c @@ -0,0 +1,345 @@ +/* arch/arm/mach-msm/board-trout-keypad.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "board-trout.h" + +static char *keycaps = "--qwerty"; +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_trout." +module_param_named(keycaps, keycaps, charp, 0); + + +static unsigned int trout_col_gpios[] = { 35, 34, 33, 32, 31, 23, 30, 78 }; +static unsigned int trout_row_gpios[] = { 42, 41, 40, 39, 38, 37, 36 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(trout_row_gpios) + (row)) + +static const unsigned short trout_keymap[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTALT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, +// [KEYMAP_INDEX(1, 0)] = 229, // SOFT1 + [KEYMAP_INDEX(1, 1)] = KEY_SEND, + [KEYMAP_INDEX(1, 2)] = KEY_END, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTALT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_SLASH, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, + //[KEYMAP_INDEX(4, 5)] = KEY_, + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_COMPOSE, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_DOT, + [KEYMAP_INDEX(6, 5)] = KEY_COMMA, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static unsigned int trout_col_gpios_evt2[] = { 35, 34, 33, 32, 31, 23, 30, 109 }; +static unsigned int trout_row_gpios_evt2[] = { 42, 41, 40, 39, 38, 37, 36 }; + +static const unsigned short trout_keymap_evt2_1[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +// [KEYMAP_INDEX(1, 2)] = KEY_, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_COMPOSE, + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_SLASH, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, +// [KEYMAP_INDEX(4, 5)] = KEY_, + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_COMMA, + [KEYMAP_INDEX(6, 5)] = KEY_DOT, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static const unsigned short trout_keymap_evt2_2[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, /* external menu key */ + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +// [KEYMAP_INDEX(1, 2)] = KEY_, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_F1, /* qwerty menu key */ + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_DOT, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, + [KEYMAP_INDEX(4, 5)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_COMMA, + [KEYMAP_INDEX(6, 5)] = KEY_RIGHTALT, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_COMPOSE, + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static struct gpio_event_matrix_info trout_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = trout_keymap, + .output_gpios = trout_col_gpios, + .input_gpios = trout_row_gpios, + .noutputs = ARRAY_SIZE(trout_col_gpios), + .ninputs = ARRAY_SIZE(trout_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_direct_entry trout_keypad_nav_map[] = { + { TROUT_POWER_KEY, KEY_POWER }, + { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP + { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA }, +}; + +static struct gpio_event_direct_entry trout_keypad_nav_map_evt2[] = { + { TROUT_POWER_KEY, KEY_END }, + { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP + { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA }, +}; + +static struct gpio_event_input_info trout_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = trout_keypad_nav_map, + .keymap_size = ARRAY_SIZE(trout_keypad_nav_map) +}; + +static struct gpio_event_direct_entry trout_keypad_switch_map[] = { + { TROUT_GPIO_SLIDING_DET, SW_LID } +}; + +static struct gpio_event_input_info trout_keypad_switch_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_SW, + .keymap = trout_keypad_switch_map, + .keymap_size = ARRAY_SIZE(trout_keypad_switch_map) +}; + +static struct gpio_event_info *trout_keypad_info[] = { + &trout_keypad_matrix_info.info, + &trout_keypad_nav_info.info, + &trout_keypad_switch_info.info, +}; + +static struct gpio_event_platform_data trout_keypad_data = { + .name = "trout-keypad", + .info = trout_keypad_info, + .info_count = ARRAY_SIZE(trout_keypad_info) +}; + +static struct platform_device trout_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &trout_keypad_data, + }, +}; + +static int __init trout_init_keypad(void) +{ + if (!machine_is_trout()) + return 0; + + switch (system_rev) { + case 0: + /* legacy default keylayout */ + break; + case 1: + /* v1 has a new keyboard layout */ + trout_keypad_matrix_info.keymap = trout_keymap_evt2_1; + trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2; + trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2; + + /* v1 has new direct keys */ + trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2; + trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2); + + /* userspace needs to know about these changes as well */ + trout_keypad_data.name = "trout-keypad-v2"; + break; + default: /* 2, 3, 4 currently */ + /* v2 has a new keyboard layout */ + trout_keypad_matrix_info.keymap = trout_keymap_evt2_2; + trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2; + trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2; + + /* v2 has new direct keys */ + trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2; + trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2); + + /* userspace needs to know about these changes as well */ + if (!strcmp(keycaps, "qwertz")) { + trout_keypad_data.name = "trout-keypad-qwertz"; + } else { + trout_keypad_data.name = "trout-keypad-v3"; + } + break; + } + return platform_device_register(&trout_keypad_device); +} + +device_initcall(trout_init_keypad); + diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index 8650342b749..f7109d5f4ab 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -14,15 +14,13 @@ #include #include - +#include #include #include "devices.h" #include "board-trout.h" -#include "proc_comm.h" - #define DEBUG_SDSLOT_VDD 1 /* ---- COMMON ---- */ diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index 89bf6b42669..52948b2602a 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -16,9 +16,9 @@ #include #include +#include #include "board-trout.h" -#include "proc_comm.h" #include "devices.h" #define TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS 255 diff --git a/arch/arm/mach-msm/board-trout-rfkill.c b/arch/arm/mach-msm/board-trout-rfkill.c new file mode 100644 index 00000000000..e68eb2ae4c5 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-rfkill.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Author: Nick Pelly + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* Control bluetooth power for trout platform */ + +#include +#include +#include +#include +#include +#include + +#include "board-trout.h" + +static struct rfkill *bt_rfk; +static const char bt_name[] = "brf6300"; + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_direction_output(101, 1); + } else { + gpio_direction_output(101, 0); + gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); + } + return 0; +} + +static struct rfkill_ops trout_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int trout_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &trout_rfkill_ops, NULL); + if (!bt_rfk) + return -ENOMEM; + + rfkill_set_states(bt_rfk, default_state, false); + + /* userspace cannot take exclusive control */ + + rc = rfkill_register(bt_rfk); + + if (rc) + rfkill_destroy(bt_rfk); + return rc; +} + +static int trout_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + + return 0; +} + +static struct platform_driver trout_rfkill_driver = { + .probe = trout_rfkill_probe, + .remove = trout_rfkill_remove, + .driver = { + .name = "trout_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init trout_rfkill_init(void) +{ + return platform_driver_register(&trout_rfkill_driver); +} + +static void __exit trout_rfkill_exit(void) +{ + platform_driver_unregister(&trout_rfkill_driver); +} + +module_init(trout_rfkill_init); +module_exit(trout_rfkill_exit); +MODULE_DESCRIPTION("trout rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-trout-wifi.c b/arch/arm/mach-msm/board-trout-wifi.c new file mode 100644 index 00000000000..51b26a40536 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-wifi.c @@ -0,0 +1,74 @@ +/* arch/arm/mach-msm/board-trout-wifi.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include +#include +#include +#include +#include +#include + +extern int trout_wifi_set_carddetect(int val); +extern int trout_wifi_power(int on); +extern int trout_wifi_reset(int on); + +#ifdef CONFIG_WIFI_MEM_PREALLOC +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = { + { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) } +}; + +static void *trout_wifi_mem_prealloc(int section, unsigned long size) +{ + if( (section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS) ) + return NULL; + if( wifi_mem_array[section].size < size ) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init trout_init_wifi_mem( void ) +{ + int i; + + for(i=0;( i < WMPA_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size); + if( wifi_mem_array[i].mem_ptr == NULL ) + return -ENOMEM; + } + return 0; +} +#endif + +struct wifi_platform_data trout_wifi_control = { + .set_power = trout_wifi_power, + .set_reset = trout_wifi_reset, + .set_carddetect = trout_wifi_set_carddetect, +#ifdef CONFIG_WIFI_MEM_PREALLOC + .mem_prealloc = trout_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif +}; + +#endif diff --git a/arch/arm/mach-msm/btpintest.c b/arch/arm/mach-msm/btpintest.c new file mode 100644 index 00000000000..97a511e80ed --- /dev/null +++ b/arch/arm/mach-msm/btpintest.c @@ -0,0 +1,234 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "1.0" +struct dentry *pin_debugfs_dent; + +/* UART GPIO lines for 8660 */ +enum uartpins { + UARTDM_TX = 53, + UARTDM_RX = 54, + UARTDM_CTS = 55, + UARTDM_RFR = 56 +}; + +/* Aux PCM GPIO lines for 8660 */ +enum auxpcmpins { + AUX_PCM_CLK = 114, + AUX_PCM_SYNC = 113, + AUX_PCM_DIN = 112, + AUX_PCM_DOUT = 111 +}; +/*Number of UART and PCM pins */ +#define PIN_COUNT 8 + +static struct gpiomux_setting pin_test_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; +/* Static array to intialise the return config */ +static struct gpiomux_setting currentconfig[2*PIN_COUNT]; + +static struct msm_gpiomux_config pin_test_configs[] = { + { + .gpio = AUX_PCM_DOUT, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = AUX_PCM_DIN, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = AUX_PCM_SYNC, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = AUX_PCM_CLK, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = UARTDM_TX, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = UARTDM_RX, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = UARTDM_CTS, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, + { + .gpio = UARTDM_RFR, + .settings = { + [GPIOMUX_ACTIVE] = &pin_test_config, + [GPIOMUX_SUSPENDED] = &pin_test_config, + }, + }, +}; +static struct msm_gpiomux_config pin_config[PIN_COUNT]; + +static int pintest_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int pintest_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int configure_pins(struct msm_gpiomux_config *config, + struct msm_gpiomux_config *oldconfig, + unsigned int num_configs) +{ + int rc = 0, j, i; + for (i = 0; i < num_configs; i++) { + for (j = 0; j < GPIOMUX_NSETTINGS; j++) { + (oldconfig + i)->gpio = (config + i)->gpio; + rc = msm_gpiomux_write((config + i)->gpio, + j, + (config + i)->settings[j], + (oldconfig + i)->settings[j]); + if (rc < 0) + break; + } + + } + return rc; +} + +static void init_current_config_pointers(void) +{ + int i = 0, j = 0; + /* The current config variables will hold the current configuration + * which is getting overwritten during a msm_gpiomux_write call + */ + for (i = 0, j = 0; i < PIN_COUNT; i += 1, j += 2) { + pin_config[i].settings[GPIOMUX_ACTIVE] = ¤tconfig[j]; + pin_config[i].settings[GPIOMUX_SUSPENDED] = + ¤tconfig[j + 1]; + } + +} + +static ssize_t pintest_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + char mode; + int rc = 0; + + if (count < 1) + return -EINVAL; + + if (buff == NULL) + return -EINVAL; + + if (copy_from_user(&mode, buff, count)) + return -EFAULT; + mode = mode - '0'; + + init_current_config_pointers(); + + if (mode) { + /* Configure all pin test gpios for the custom settings */ + rc = configure_pins(pin_test_configs, pin_config, + ARRAY_SIZE(pin_test_configs)); + if (rc < 0) + return rc; + } else { + /* Configure all pin test gpios for the original settings */ + rc = configure_pins(pin_config, pin_test_configs, + ARRAY_SIZE(pin_test_configs)); + if (rc < 0) + return rc; + } + return rc; +} + +static const struct file_operations pintest_debugfs_fops = { + .open = pintest_open, + .release = pintest_release, + .write = pintest_write, +}; + +static int __init bluepintest_init(void) +{ + pin_debugfs_dent = debugfs_create_dir("btpintest", NULL); + + if (IS_ERR(pin_debugfs_dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(pin_debugfs_dent)); + return -ENOMEM; + } + + if (debugfs_create_file("enable", 0644, pin_debugfs_dent, + 0, &pintest_debugfs_fops) == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -ENOMEM; + } + return 0; +} + +static void __exit bluepintest_exit(void) +{ + debugfs_remove_recursive(pin_debugfs_dent); +} + +module_init(bluepintest_init); +module_exit(bluepintest_exit); + +MODULE_DESCRIPTION("Bluetooth Pin Connectivty Test Driver ver %s " VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/cache_erp.c b/arch/arm/mach-msm/cache_erp.c new file mode 100644 index 00000000000..4d7ce12df0a --- /dev/null +++ b/arch/arm/mach-msm/cache_erp.c @@ -0,0 +1,576 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "acpuclock.h" + +#define CESR_DCTPE BIT(0) +#define CESR_DCDPE BIT(1) +#define CESR_ICTPE BIT(2) +#define CESR_ICDPE BIT(3) +#define CESR_DCTE (BIT(4) | BIT(5)) +#define CESR_ICTE (BIT(6) | BIT(7)) +#define CESR_TLBMH BIT(16) +#define CESR_I_MASK 0x000000CC + +/* Print a message for everything but TLB MH events */ +#define CESR_PRINT_MASK 0x000000FF + +/* Log everything but TLB MH events */ +#define CESR_LOG_EVENT_MASK 0x000000FF + +#define L2ESR_IND_ADDR 0x204 +#define L2ESYNR0_IND_ADDR 0x208 +#define L2ESYNR1_IND_ADDR 0x209 +#define L2EAR0_IND_ADDR 0x20C +#define L2EAR1_IND_ADDR 0x20D + +#define L2ESR_MPDCD BIT(0) +#define L2ESR_MPSLV BIT(1) +#define L2ESR_TSESB BIT(2) +#define L2ESR_TSEDB BIT(3) +#define L2ESR_DSESB BIT(4) +#define L2ESR_DSEDB BIT(5) +#define L2ESR_MSE BIT(6) +#define L2ESR_MPLDREXNOK BIT(8) + +#define L2ESR_ACCESS_ERR_MASK 0xFFFC + +#define L2ESR_CPU_MASK 0x0F +#define L2ESR_CPU_SHIFT 16 + +#ifdef CONFIG_MSM_L1_ERR_PANIC +#define ERP_L1_ERR(a) panic(a) +#else +#define ERP_L1_ERR(a) do { } while (0) +#endif + +#ifdef CONFIG_MSM_L2_ERP_PORT_PANIC +#define ERP_PORT_ERR(a) panic(a) +#else +#define ERP_PORT_ERR(a) WARN(1, a) +#endif + +#ifdef CONFIG_MSM_L2_ERP_1BIT_PANIC +#define ERP_1BIT_ERR(a) panic(a) +#else +#define ERP_1BIT_ERR(a) do { } while (0) +#endif + +#ifdef CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS +#define print_access_errors() 1 +#else +#define print_access_errors() 0 +#endif + +#ifdef CONFIG_MSM_L2_ERP_2BIT_PANIC +#define ERP_2BIT_ERR(a) panic(a) +#else +#define ERP_2BIT_ERR(a) do { } while (0) +#endif + +#define MODULE_NAME "msm_cache_erp" + +#define ERP_LOG_MAGIC_ADDR 0x748 +#define ERP_LOG_MAGIC 0x11C39893 + +struct msm_l1_err_stats { + unsigned int dctpe; + unsigned int dcdpe; + unsigned int ictpe; + unsigned int icdpe; + unsigned int dcte; + unsigned int icte; + unsigned int tlbmh; +}; + +struct msm_l2_err_stats { + unsigned int mpdcd; + unsigned int mpslv; + unsigned int tsesb; + unsigned int tsedb; + unsigned int dsesb; + unsigned int dsedb; + unsigned int mse; + unsigned int mplxrexnok; +}; + +static DEFINE_PER_CPU(struct msm_l1_err_stats, msm_l1_erp_stats); +static struct msm_l2_err_stats msm_l2_erp_stats; + +static int l1_erp_irq, l2_erp_irq; +static struct proc_dir_entry *procfs_entry; + +#ifdef CONFIG_MSM_L1_ERR_LOG +static struct proc_dir_entry *procfs_log_entry; +#endif + +static inline unsigned int read_cesr(void) +{ + unsigned int cesr; + asm volatile ("mrc p15, 7, %0, c15, c0, 1" : "=r" (cesr)); + return cesr; +} + +static inline void write_cesr(unsigned int cesr) +{ + asm volatile ("mcr p15, 7, %[cesr], c15, c0, 1" : : [cesr]"r" (cesr)); +} + +static inline unsigned int read_cesynr(void) +{ + unsigned int cesynr; + asm volatile ("mrc p15, 7, %0, c15, c0, 3" : "=r" (cesynr)); + return cesynr; +} + +static int proc_read_status(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct msm_l1_err_stats *l1_stats; + char *p = page; + int len, cpu, ret, bytes_left = PAGE_SIZE; + + for_each_present_cpu(cpu) { + l1_stats = &per_cpu(msm_l1_erp_stats, cpu); + + ret = snprintf(p, bytes_left, + "CPU %d:\n" \ + "\tD-cache tag parity errors:\t%u\n" \ + "\tD-cache data parity errors:\t%u\n" \ + "\tI-cache tag parity errors:\t%u\n" \ + "\tI-cache data parity errors:\t%u\n" \ + "\tD-cache timing errors:\t\t%u\n" \ + "\tI-cache timing errors:\t\t%u\n" \ + "\tTLB multi-hit errors:\t\t%u\n\n", \ + cpu, + l1_stats->dctpe, + l1_stats->dcdpe, + l1_stats->ictpe, + l1_stats->icdpe, + l1_stats->dcte, + l1_stats->icte, + l1_stats->tlbmh); + p += ret; + bytes_left -= ret; + } + + p += snprintf(p, bytes_left, + "L2 master port decode errors:\t\t%u\n" \ + "L2 master port slave errors:\t\t%u\n" \ + "L2 tag soft errors, single-bit:\t\t%u\n" \ + "L2 tag soft errors, double-bit:\t\t%u\n" \ + "L2 data soft errors, single-bit:\t%u\n" \ + "L2 data soft errors, double-bit:\t%u\n" \ + "L2 modified soft errors:\t\t%u\n" \ + "L2 master port LDREX NOK errors:\t%u\n", + msm_l2_erp_stats.mpdcd, + msm_l2_erp_stats.mpslv, + msm_l2_erp_stats.tsesb, + msm_l2_erp_stats.tsedb, + msm_l2_erp_stats.dsesb, + msm_l2_erp_stats.dsedb, + msm_l2_erp_stats.mse, + msm_l2_erp_stats.mplxrexnok); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +#ifdef CONFIG_MSM_L1_ERR_LOG +static int proc_read_log(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *p = page; + int len, log_value; + log_value = __raw_readl(MSM_IMEM_BASE + ERP_LOG_MAGIC_ADDR) == + ERP_LOG_MAGIC ? 1 : 0; + + p += snprintf(p, PAGE_SIZE, "%d\n", log_value); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static void log_cpu_event(void) +{ + __raw_writel(ERP_LOG_MAGIC, MSM_IMEM_BASE + ERP_LOG_MAGIC_ADDR); + mb(); +} + +static int procfs_event_log_init(void) +{ + procfs_log_entry = create_proc_entry("cpu/msm_erp_log", S_IRUGO, NULL); + + if (!procfs_log_entry) + return -ENODEV; + procfs_log_entry->read_proc = proc_read_log; + return 0; +} + +#else +static inline void log_cpu_event(void) { } +static inline int procfs_event_log_init(void) { return 0; } +#endif + +static irqreturn_t msm_l1_erp_irq(int irq, void *dev_id) +{ + struct msm_l1_err_stats *l1_stats = dev_id; + unsigned int cesr = read_cesr(); + unsigned int i_cesynr, d_cesynr; + unsigned int cpu = smp_processor_id(); + int print_regs = cesr & CESR_PRINT_MASK; + int log_event = cesr & CESR_LOG_EVENT_MASK; + + void *const saw_bases[] = { + MSM_SAW0_BASE, + MSM_SAW1_BASE, + MSM_SAW2_BASE, + MSM_SAW3_BASE, + }; + + if (print_regs) { + pr_alert("L1 / TLB Error detected on CPU %d!\n", cpu); + pr_alert("\tCESR = 0x%08x\n", cesr); + pr_alert("\tCPU speed = %lu\n", acpuclk_get_rate(cpu)); + pr_alert("\tMIDR = 0x%08x\n", read_cpuid_id()); + pr_alert("\tPTE fuses = 0x%08x\n", + readl_relaxed(MSM_QFPROM_BASE + 0xC0)); + pr_alert("\tPMIC_VREG = 0x%08x\n", + readl_relaxed(saw_bases[cpu] + 0x14)); + } + + if (cesr & CESR_DCTPE) { + pr_alert("D-cache tag parity error\n"); + l1_stats->dctpe++; + } + + if (cesr & CESR_DCDPE) { + pr_alert("D-cache data parity error\n"); + l1_stats->dcdpe++; + } + + if (cesr & CESR_ICTPE) { + pr_alert("I-cache tag parity error\n"); + l1_stats->ictpe++; + } + + if (cesr & CESR_ICDPE) { + pr_alert("I-cache data parity error\n"); + l1_stats->icdpe++; + } + + if (cesr & CESR_DCTE) { + pr_alert("D-cache timing error\n"); + l1_stats->dcte++; + } + + if (cesr & CESR_ICTE) { + pr_alert("I-cache timing error\n"); + l1_stats->icte++; + } + + if (cesr & CESR_TLBMH) { + asm ("mcr p15, 0, r0, c8, c7, 0"); + l1_stats->tlbmh++; + } + + if (cesr & (CESR_ICTPE | CESR_ICDPE | CESR_ICTE)) { + i_cesynr = read_cesynr(); + pr_alert("I-side CESYNR = 0x%08x\n", i_cesynr); + write_cesr(CESR_I_MASK); + + /* + * Clear the I-side bits from the captured CESR value so that we + * don't accidentally clear any new I-side errors when we do + * the CESR write-clear operation. + */ + cesr &= ~CESR_I_MASK; + } + + if (cesr & (CESR_DCTPE | CESR_DCDPE | CESR_DCTE)) { + d_cesynr = read_cesynr(); + pr_alert("D-side CESYNR = 0x%08x\n", d_cesynr); + } + + if (log_event) + log_cpu_event(); + + /* Clear the interrupt bits we processed */ + write_cesr(cesr); + + if (print_regs) + ERP_L1_ERR("L1 cache error detected"); + + return IRQ_HANDLED; +} + +static irqreturn_t msm_l2_erp_irq(int irq, void *dev_id) +{ + unsigned int l2esr; + unsigned int l2esynr0; + unsigned int l2esynr1; + unsigned int l2ear0; + unsigned int l2ear1; + int soft_error = 0; + int port_error = 0; + int unrecoverable = 0; + int print_alert; + + l2esr = get_l2_indirect_reg(L2ESR_IND_ADDR); + l2esynr0 = get_l2_indirect_reg(L2ESYNR0_IND_ADDR); + l2esynr1 = get_l2_indirect_reg(L2ESYNR1_IND_ADDR); + l2ear0 = get_l2_indirect_reg(L2EAR0_IND_ADDR); + l2ear1 = get_l2_indirect_reg(L2EAR1_IND_ADDR); + + print_alert = print_access_errors() || (l2esr & L2ESR_ACCESS_ERR_MASK); + + if (print_alert) { + pr_alert("L2 Error detected!\n"); + pr_alert("\tL2ESR = 0x%08x\n", l2esr); + pr_alert("\tL2ESYNR0 = 0x%08x\n", l2esynr0); + pr_alert("\tL2ESYNR1 = 0x%08x\n", l2esynr1); + pr_alert("\tL2EAR0 = 0x%08x\n", l2ear0); + pr_alert("\tL2EAR1 = 0x%08x\n", l2ear1); + pr_alert("\tCPU bitmap = 0x%x\n", (l2esr >> L2ESR_CPU_SHIFT) & + L2ESR_CPU_MASK); + } + + if (l2esr & L2ESR_MPDCD) { + if (print_alert) + pr_alert("L2 master port decode error\n"); + port_error++; + msm_l2_erp_stats.mpdcd++; + } + + if (l2esr & L2ESR_MPSLV) { + if (print_alert) + pr_alert("L2 master port slave error\n"); + port_error++; + msm_l2_erp_stats.mpslv++; + } + + if (l2esr & L2ESR_TSESB) { + pr_alert("L2 tag soft error, single-bit\n"); + soft_error++; + msm_l2_erp_stats.tsesb++; + } + + if (l2esr & L2ESR_TSEDB) { + pr_alert("L2 tag soft error, double-bit\n"); + soft_error++; + unrecoverable++; + msm_l2_erp_stats.tsedb++; + } + + if (l2esr & L2ESR_DSESB) { + pr_alert("L2 data soft error, single-bit\n"); + soft_error++; + msm_l2_erp_stats.dsesb++; + } + + if (l2esr & L2ESR_DSEDB) { + pr_alert("L2 data soft error, double-bit\n"); + soft_error++; + unrecoverable++; + msm_l2_erp_stats.dsedb++; + } + + if (l2esr & L2ESR_MSE) { + pr_alert("L2 modified soft error\n"); + soft_error++; + msm_l2_erp_stats.mse++; + } + + if (l2esr & L2ESR_MPLDREXNOK) { + pr_alert("L2 master port LDREX received Normal OK response\n"); + port_error++; + msm_l2_erp_stats.mplxrexnok++; + } + + if (port_error && print_alert) + ERP_PORT_ERR("L2 master port error detected"); + + if (soft_error && !unrecoverable) + ERP_1BIT_ERR("L2 single-bit error detected"); + + if (unrecoverable) + ERP_2BIT_ERR("L2 double-bit error detected, trouble ahead"); + + set_l2_indirect_reg(L2ESR_IND_ADDR, l2esr); + return IRQ_HANDLED; +} + +static void enable_erp_irq_callback(void *info) +{ + enable_percpu_irq(l1_erp_irq, IRQ_TYPE_LEVEL_HIGH); +} + +static void disable_erp_irq_callback(void *info) +{ + disable_percpu_irq(l1_erp_irq); +} + +static int cache_erp_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action & (~CPU_TASKS_FROZEN)) { + case CPU_STARTING: + enable_erp_irq_callback(NULL); + break; + + case CPU_DYING: + disable_erp_irq_callback(NULL); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block cache_erp_cpu_notifier = { + .notifier_call = cache_erp_cpu_callback, +}; + +static int msm_cache_erp_probe(struct platform_device *pdev) +{ + struct resource *r; + int ret, cpu; + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l1_irq"); + + if (!r) { + pr_err("Could not get L1 resource\n"); + ret = -ENODEV; + goto fail; + } + + l1_erp_irq = r->start; + + ret = request_percpu_irq(l1_erp_irq, msm_l1_erp_irq, "MSM_L1", + &msm_l1_erp_stats); + + if (ret) { + pr_err("Failed to request the L1 cache error interrupt\n"); + goto fail; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l2_irq"); + + if (!r) { + pr_err("Could not get L2 resource\n"); + ret = -ENODEV; + goto fail_l1; + } + + l2_erp_irq = r->start; + ret = request_irq(l2_erp_irq, msm_l2_erp_irq, 0, "MSM_L2", NULL); + + if (ret) { + pr_err("Failed to request the L2 cache error interrupt\n"); + goto fail_l1; + } + + procfs_entry = create_proc_entry("cpu/msm_cache_erp", S_IRUGO, NULL); + + if (!procfs_entry) { + pr_err("Failed to create procfs node for cache error reporting\n"); + ret = -ENODEV; + goto fail_l2; + } + + get_online_cpus(); + register_hotcpu_notifier(&cache_erp_cpu_notifier); + for_each_cpu(cpu, cpu_online_mask) + smp_call_function_single(cpu, enable_erp_irq_callback, NULL, 1); + put_online_cpus(); + + procfs_entry->read_proc = proc_read_status; + + ret = procfs_event_log_init(); + if (ret) + pr_err("Failed to create procfs node for ERP log access\n"); + + return 0; + +fail_l2: + free_irq(l2_erp_irq, NULL); +fail_l1: + free_percpu_irq(l1_erp_irq, NULL); +fail: + return ret; +} + +static int msm_cache_erp_remove(struct platform_device *pdev) +{ + int cpu; + if (procfs_entry) + remove_proc_entry("cpu/msm_cache_erp", NULL); + + get_online_cpus(); + unregister_hotcpu_notifier(&cache_erp_cpu_notifier); + for_each_cpu(cpu, cpu_online_mask) + smp_call_function_single(cpu, disable_erp_irq_callback, NULL, + 1); + put_online_cpus(); + + free_percpu_irq(l1_erp_irq, NULL); + + disable_irq(l2_erp_irq); + free_irq(l2_erp_irq, NULL); + return 0; +} + +static struct platform_driver msm_cache_erp_driver = { + .probe = msm_cache_erp_probe, + .remove = msm_cache_erp_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init msm_cache_erp_init(void) +{ + return platform_driver_register(&msm_cache_erp_driver); +} + +static void __exit msm_cache_erp_exit(void) +{ + platform_driver_unregister(&msm_cache_erp_driver); +} + + +module_init(msm_cache_erp_init); +module_exit(msm_cache_erp_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM cache error reporting driver"); diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c new file mode 100644 index 00000000000..aa94be64dd5 --- /dev/null +++ b/arch/arm/mach-msm/clock-7x30.c @@ -0,0 +1,3024 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "clock.h" +#include "clock-local.h" +#include "clock-pcom.h" +#include "clock-voter.h" +#include "clock-pll.h" + +#define REG_BASE(off) (MSM_CLK_CTL_BASE + (off)) +#define REG(off) (MSM_CLK_CTL_SH2_BASE + (off)) + +/* Shadow-region 2 (SH2) registers. */ +#define QUP_I2C_NS_REG REG(0x04F0) +#define CAM_NS_REG REG(0x0374) +#define CAM_VFE_NS_REG REG(0x0044) +#define CLK_HALT_STATEA_REG REG(0x0108) +#define CLK_HALT_STATEB_REG REG(0x010C) +#define CLK_HALT_STATEC_REG REG(0x02D4) +#define CSI_NS_REG REG(0x0174) +#define EMDH_NS_REG REG(0x0050) +#define GLBL_CLK_ENA_2_SC_REG REG(0x03C0) +#define GLBL_CLK_ENA_SC_REG REG(0x03BC) +#define GLBL_CLK_STATE_2_REG REG(0x037C) +#define GLBL_CLK_STATE_REG REG(0x0004) +#define GRP_2D_NS_REG REG(0x0034) +#define GRP_NS_REG REG(0x0084) +#define HDMI_NS_REG REG(0x0484) +#define I2C_2_NS_REG REG(0x02D8) +#define I2C_NS_REG REG(0x0068) +#define JPEG_NS_REG REG(0x0164) +#define LPA_CORE_CLK_MA0_REG REG(0x04F4) +#define LPA_CORE_CLK_MA2_REG REG(0x04FC) +#define LPA_NS_REG REG(0x02E8) +#define MDC_NS_REG REG(0x007C) +#define MDP_LCDC_NS_REG REG(0x0390) +#define MDP_NS_REG REG(0x014C) +#define MDP_VSYNC_REG REG(0x0460) +#define MFC_NS_REG REG(0x0154) +#define MI2S_CODEC_RX_DIV_REG REG(0x02EC) +#define MI2S_CODEC_TX_DIV_REG REG(0x02F0) +#define MI2S_DIV_REG REG(0x02E4) +#define MI2S_NS_REG REG(0x02E0) +#define MI2S_RX_NS_REG REG(0x0070) +#define MI2S_TX_NS_REG REG(0x0078) +#define PLL_ENA_REG REG(0x0264) +#define PMDH_NS_REG REG(0x008C) +#define SDAC_NS_REG REG(0x009C) +#define SDCn_NS_REG(n) REG(0x00A4+(0x8*((n)-1))) +#define SPI_NS_REG REG(0x02C8) +#define TSIF_NS_REG REG(0x00C4) +#define TV_NS_REG REG(0x00CC) +#define UART1DM_NS_REG REG(0x00D4) +#define UART2DM_NS_REG REG(0x00DC) +#define UART2_NS_REG REG(0x0464) +#define UART_NS_REG REG(0x00E0) +#define USBH2_NS_REG REG(0x046C) +#define USBH3_NS_REG REG(0x0470) +#define USBH_MD_REG REG(0x02BC) +#define USBH_NS_REG REG(0x02C0) +#define VPE_NS_REG REG(0x015C) + +/* Registers in the base (non-shadow) region. */ +#define CLK_TEST_BASE_REG REG_BASE(0x011C) +#define CLK_TEST_2_BASE_REG REG_BASE(0x0384) +#define MISC_CLK_CTL_BASE_REG REG_BASE(0x0110) +#define PRPH_WEB_NS_BASE_REG REG_BASE(0x0080) +#define PLL0_STATUS_BASE_REG REG_BASE(0x0318) +#define PLL1_STATUS_BASE_REG REG_BASE(0x0334) +#define PLL2_STATUS_BASE_REG REG_BASE(0x0350) +#define PLL3_STATUS_BASE_REG REG_BASE(0x036C) +#define PLL4_STATUS_BASE_REG REG_BASE(0x0254) +#define PLL5_STATUS_BASE_REG REG_BASE(0x0258) +#define PLL6_STATUS_BASE_REG REG_BASE(0x04EC) +#define RINGOSC_CNT_BASE_REG REG_BASE(0x00FC) +#define SH2_OWN_APPS1_BASE_REG REG_BASE(0x040C) +#define SH2_OWN_APPS2_BASE_REG REG_BASE(0x0414) +#define SH2_OWN_APPS3_BASE_REG REG_BASE(0x0444) +#define SH2_OWN_GLBL_BASE_REG REG_BASE(0x0404) +#define SH2_OWN_ROW1_BASE_REG REG_BASE(0x041C) +#define SH2_OWN_ROW2_BASE_REG REG_BASE(0x0424) +#define TCXO_CNT_BASE_REG REG_BASE(0x00F8) +#define TCXO_CNT_DONE_BASE_REG REG_BASE(0x00F8) + + +/* MUX source input identifiers. */ +#define SRC_SEL_pll0 4 /* Modem PLL */ +#define SRC_SEL_pll1 1 /* Global PLL */ +#define SRC_SEL_pll3 3 /* Multimedia/Peripheral PLL or Backup PLL1 */ +#define SRC_SEL_pll4 2 /* Display PLL */ +#define SRC_SEL_SDAC_lpxo 5 /* Low-power XO for SDAC */ +#define SRC_SEL_lpxo 6 /* Low-power XO */ +#define SRC_SEL_tcxo 0 /* Used for rates from TCXO */ +#define SRC_SEL_axi 0 /* Used for rates that sync to AXI */ +#define SRC_SEL_gnd 7 /* No clock */ + +/* Clock declaration macros. */ +#define N8(msb, lsb, m, n) (BVAL(msb, lsb, ~(n-m)) | BVAL(6, 5, \ + (MN_MODE_DUAL_EDGE * !!(n)))) +#define N16(m, n) (BVAL(31, 16, ~(n-m)) | BVAL(6, 5, \ + (MN_MODE_DUAL_EDGE * !!(n)))) +#define SPDIV(s, d) (BVAL(4, 3, d-1) | BVAL(2, 0, s)) +#define SDIV(s, d) (BVAL(6, 3, d-1) | BVAL(2, 0, s)) +#define F_MASK_BASIC (BM(6, 3)|BM(2, 0)) +#define F_MASK_MND16 (BM(31, 16)|BM(6, 5)|BM(4, 3)|BM(2, 0)) +#define F_MASK_MND8(m, l) (BM(m, l)|BM(6, 5)|BM(4, 3)|BM(2, 0)) + +/* + * Clock frequency definitions and macros + */ +#define F_BASIC(f, s, div) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = SDIV(SRC_SEL_##s, div), \ + } + +#define F_MND16(f, s, div, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = N16(m, n) | SPDIV(SRC_SEL_##s, div), \ + } + +#define F_MND8(f, nmsb, nlsb, s, div, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = N8(nmsb, nlsb, m, n) | SPDIV(SRC_SEL_##s, div), \ + } + +enum vdd_dig_levels { + VDD_DIG_NONE, + VDD_DIG_LOW, + VDD_DIG_NOMINAL, + VDD_DIG_HIGH +}; + +static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level) +{ + int rc, target_mv; + + static const int mv[] = { + [VDD_DIG_NONE] = 1000, + [VDD_DIG_LOW] = 1000, + [VDD_DIG_NOMINAL] = 1100, + [VDD_DIG_HIGH] = 1200 + }; + + target_mv = mv[level]; + rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_MSMC1, &target_mv, NULL); + if (rc) + return rc; + if (target_mv) + rc = -EINVAL; + + return rc; +} + +static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig); + +#define VDD_DIG_FMAX_MAP1(l1, f1) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1) +#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2) + +#define PCOM_XO_DISABLE 0 +#define PCOM_XO_ENABLE 1 +#define PCOM_XO_TCXO 0 +#define PCOM_XO_LPXO 1 + +static bool pcom_is_local(struct clk *clk) +{ + return false; +} + +static int pcom_xo_enable(unsigned pcom_id, unsigned enable) +{ + /* TODO: Check return code in pcom_id */ + return msm_proc_comm(PCOM_CLKCTL_RPC_SRC_REQUEST, &pcom_id, &enable); +} + +static int tcxo_clk_enable(struct clk *clk) +{ + return pcom_xo_enable(PCOM_XO_TCXO, PCOM_XO_ENABLE); +} + +static void tcxo_clk_disable(struct clk *clk) +{ + pcom_xo_enable(PCOM_XO_TCXO, PCOM_XO_DISABLE); +} + +static enum handoff xo_clk_handoff(struct clk *clk) +{ + return HANDOFF_ENABLED_CLK; +} + +static struct clk_ops clk_ops_tcxo = { + .enable = tcxo_clk_enable, + .disable = tcxo_clk_disable, + .handoff = xo_clk_handoff, + .is_local = pcom_is_local, +}; + +static struct fixed_clk tcxo_clk = { + .c = { + .dbg_name = "tcxo_clk", + .rate = 19200000, + .ops = &clk_ops_tcxo, + CLK_INIT(tcxo_clk.c), + .warned = true, + }, +}; + +static int lpxo_clk_enable(struct clk *clk) +{ + return pcom_xo_enable(PCOM_XO_LPXO, PCOM_XO_ENABLE); +} + +static void lpxo_clk_disable(struct clk *clk) +{ + pcom_xo_enable(PCOM_XO_LPXO, PCOM_XO_DISABLE); +} + +static struct clk_ops clk_ops_lpxo = { + .enable = lpxo_clk_enable, + .disable = lpxo_clk_disable, + .handoff = xo_clk_handoff, + .is_local = pcom_is_local, +}; + +static struct fixed_clk lpxo_clk = { + .c = { + .dbg_name = "lpxo_clk", + .rate = 24576000, + .ops = &clk_ops_lpxo, + CLK_INIT(lpxo_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll1_clk = { + .en_reg = PLL_ENA_REG, + .en_mask = BIT(1), + .status_reg = PLL1_STATUS_BASE_REG, + .status_mask = BIT(16), + .parent = &tcxo_clk.c, + .c = { + .dbg_name = "pll1_clk", + .rate = 768000000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll1_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll2_clk = { + .en_reg = PLL_ENA_REG, + .en_mask = BIT(2), + .status_reg = PLL2_STATUS_BASE_REG, + .status_mask = BIT(16), + .parent = &tcxo_clk.c, + .c = { + .dbg_name = "pll2_clk", + .rate = 806400000, /* TODO: Support scaling */ + .ops = &clk_ops_pll_vote, + CLK_INIT(pll2_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll3_clk = { + .en_reg = PLL_ENA_REG, + .en_mask = BIT(3), + .status_reg = PLL3_STATUS_BASE_REG, + .status_mask = BIT(16), + .parent = &lpxo_clk.c, + .c = { + .dbg_name = "pll3_clk", + .rate = 737280000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll3_clk.c), + }, +}; + +static struct pll_vote_clk pll4_clk = { + .en_reg = PLL_ENA_REG, + .en_mask = BIT(4), + .status_reg = PLL4_STATUS_BASE_REG, + .status_mask = BIT(16), + .parent = &lpxo_clk.c, + .c = { + .dbg_name = "pll4_clk", + .rate = 891000000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll4_clk.c), + .warned = true, + }, +}; + +static struct clk_freq_tbl clk_tbl_axi[] = { + F_RAW(1, &lpxo_clk.c, 0, 0, 0, NULL), + F_END, +}; + +/* For global clocks to be on we must have GLBL_ROOT_ENA set */ +static struct rcg_clk glbl_root_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(29), + .halt_check = NOCHECK, + }, + .freq_tbl = clk_tbl_axi, + .set_rate = set_rate_nop, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_nop, + .c = { + .dbg_name = "glbl_root_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 1), + CLK_INIT(glbl_root_clk.c), + }, +}; + +/* AXI bridge clocks. */ +static struct branch_clk axi_li_apps_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(2), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 2, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "axi_li_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_li_apps_clk.c), + }, +}; + +static struct branch_clk axi_li_adsp_a_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(14), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 14, + }, + .parent = &axi_li_apps_clk.c, + .c = { + .dbg_name = "axi_li_adsp_a_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_li_adsp_a_clk.c), + }, +}; + +static struct branch_clk axi_li_jpeg_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(19), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 19, + }, + .parent = &axi_li_apps_clk.c, + .c = { + .dbg_name = "axi_li_jpeg_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_li_jpeg_clk.c), + }, +}; + +static struct branch_clk axi_li_vfe_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(23), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 23, + }, + .parent = &axi_li_apps_clk.c, + .c = { + .dbg_name = "axi_li_vfe_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_li_vfe_clk.c), + }, +}; + +static struct branch_clk axi_mdp_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(29), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 29, + }, + .parent = &axi_li_apps_clk.c, + .c = { + .dbg_name = "axi_mdp_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_mdp_clk.c), + }, +}; + +static struct branch_clk axi_li_vg_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(3), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 3, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "axi_li_vg_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_li_vg_clk.c), + }, +}; + +static struct branch_clk axi_grp_2d_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(21), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 21, + }, + .parent = &axi_li_vg_clk.c, + .c = { + .dbg_name = "axi_grp_2d_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_grp_2d_clk.c), + }, +}; + +static struct branch_clk axi_li_grp_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(22), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 22, + }, + .parent = &axi_li_vg_clk.c, + .c = { + .dbg_name = "axi_li_grp_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_li_grp_clk.c), + }, +}; + +static struct branch_clk axi_mfc_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(20), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 20, + }, + .parent = &axi_li_vg_clk.c, + .c = { + .dbg_name = "axi_mfc_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_mfc_clk.c), + }, +}; + +static struct branch_clk axi_rotator_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(22), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 22, + .reset_mask = P_AXI_ROTATOR_CLK, + }, + .parent = &axi_li_vg_clk.c, + .c = { + .dbg_name = "axi_rotator_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_rotator_clk.c), + }, +}; + +static struct branch_clk axi_vpe_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(21), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 21, + }, + .parent = &axi_li_vg_clk.c, + .c = { + .dbg_name = "axi_vpe_clk", + .ops = &clk_ops_branch, + CLK_INIT(axi_vpe_clk.c), + }, +}; + +/* Peripheral bus clocks. */ +static struct branch_clk adm_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(5), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 5, + .reset_mask = P_ADM_CLK, + }, + .parent = &axi_li_apps_clk.c, + .c = { + .dbg_name = "adm_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm_clk.c), + }, +}; + +static struct branch_clk adm_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(15), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 15, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "adm_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm_p_clk.c), + }, +}; + +static struct branch_clk ce_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(6), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 6, + .reset_mask = P_CE_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "ce_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce_clk.c), + }, +}; + +static struct branch_clk camif_pad_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(9), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 9, + .reset_mask = P_CAMIF_PAD_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "camif_pad_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(camif_pad_p_clk.c), + }, +}; + +static struct branch_clk csi0_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(30), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 30, + .reset_mask = P_CSI0_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "csi0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_p_clk.c), + }, +}; + +static struct branch_clk emdh_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(3), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 3, + .reset_mask = P_EMDH_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "emdh_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(emdh_p_clk.c), + }, +}; + +static struct branch_clk grp_2d_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(24), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 24, + .reset_mask = P_GRP_2D_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "grp_2d_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(grp_2d_p_clk.c), + }, +}; + +static struct branch_clk grp_3d_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(17), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 17, + .reset_mask = P_GRP_3D_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "grp_3d_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(grp_3d_p_clk.c), + }, +}; + +static struct branch_clk jpeg_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(24), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 24, + .reset_mask = P_JPEG_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "jpeg_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(jpeg_p_clk.c), + }, +}; + +static struct branch_clk lpa_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(7), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 7, + .reset_mask = P_LPA_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "lpa_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(lpa_p_clk.c), + }, +}; + +static struct branch_clk mdp_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(6), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 6, + .reset_mask = P_MDP_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "mdp_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_p_clk.c), + }, +}; + +static struct branch_clk mfc_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(26), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 26, + .reset_mask = P_MFC_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "mfc_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(mfc_p_clk.c), + }, +}; + +static struct branch_clk pmdh_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(4), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 4, + .reset_mask = P_PMDH_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "pmdh_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmdh_p_clk.c), + }, +}; + +static struct branch_clk rotator_imem_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(23), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 23, + .reset_mask = P_ROTATOR_IMEM_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "rotator_imem_clk", + .ops = &clk_ops_branch, + CLK_INIT(rotator_imem_clk.c), + }, +}; + +static struct branch_clk rotator_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(25), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 25, + .reset_mask = P_ROTATOR_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "rotator_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(rotator_p_clk.c), + }, +}; + +static struct branch_clk sdc1_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(7), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 7, + .reset_mask = P_SDC1_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "sdc1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc1_p_clk.c), + }, +}; + +static struct branch_clk sdc2_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(8), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 8, + .reset_mask = P_SDC2_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "sdc2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc2_p_clk.c), + }, +}; + +static struct branch_clk sdc3_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(27), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 27, + .reset_mask = P_SDC3_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "sdc3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc3_p_clk.c), + }, +}; + +static struct branch_clk sdc4_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(28), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 28, + .reset_mask = P_SDC4_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "sdc4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc4_p_clk.c), + }, +}; + +static struct branch_clk spi_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(10), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 10, + .reset_mask = P_SPI_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "spi_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(spi_p_clk.c), + }, +}; + +static struct branch_clk tsif_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(18), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 18, + .reset_mask = P_TSIF_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "tsif_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(tsif_p_clk.c), + }, +}; + +static struct branch_clk uart1dm_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(17), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 17, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "uart1dm_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(uart1dm_p_clk.c), + }, +}; + +static struct branch_clk uart2dm_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(26), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 26, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "uart2dm_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(uart2dm_p_clk.c), + }, +}; + +static struct branch_clk usb_hs2_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(8), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 8, + .reset_mask = P_USB_HS2_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "usb_hs2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs2_p_clk.c), + }, +}; + +static struct branch_clk usb_hs3_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(9), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 9, + .reset_mask = P_USB_HS3_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "usb_hs3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs3_p_clk.c), + }, +}; + +static struct branch_clk usb_hs_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_SC_REG, + .en_mask = BIT(25), + .halt_reg = GLBL_CLK_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 25, + .reset_mask = P_USB_HS_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "usb_hs_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs_p_clk.c), + }, +}; + +static struct branch_clk vfe_p_clk = { + .b = { + .ctl_reg = GLBL_CLK_ENA_2_SC_REG, + .en_mask = BIT(27), + .halt_reg = GLBL_CLK_STATE_2_REG, + .halt_check = HALT_VOTED, + .halt_bit = 27, + .reset_mask = P_VFE_P_CLK, + }, + .parent = &glbl_root_clk.c, + .c = { + .dbg_name = "vfe_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_p_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_csi[] = { + F_MND8( 0, 0, 0, gnd, 1, 0, 0), + F_MND8(153600000, 24, 17, pll1, 2, 2, 5), + F_MND8(192000000, 24, 17, pll1, 4, 0, 0), + F_MND8(384000000, 24, 17, pll1, 2, 0, 0), + F_END, +}; + +static struct rcg_clk csi0_clk = { + .b = { + .ctl_reg = CSI_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 17, + .reset_mask = P_CSI0_CLK, + }, + .ns_reg = CSI_NS_REG, + .md_reg = CSI_NS_REG - 4, + .ns_mask = F_MASK_MND8(24, 17), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_csi, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "csi0_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 384000000), + CLK_INIT(csi0_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_tcxo[] = { + F_RAW(19200000, &tcxo_clk.c, 0, 0, 0, NULL), + F_END, +}; + +static struct rcg_clk i2c_clk = { + .b = { + .ctl_reg = I2C_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 15, + .reset_mask = P_I2C_CLK, + }, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_tcxo, + .root_en_mask = BIT(11), + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "i2c_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 19200000), + CLK_INIT(i2c_clk.c), + }, +}; + +static struct rcg_clk i2c_2_clk = { + .b = { + .ctl_reg = I2C_2_NS_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 2, + .reset_mask = P_I2C_2_CLK, + }, + .root_en_mask = BIT(2), + .freq_tbl = clk_tbl_tcxo, + .set_rate = set_rate_nop, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "i2c_2_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 19200000), + CLK_INIT(i2c_2_clk.c), + }, +}; + +static struct rcg_clk qup_i2c_clk = { + .b = { + .ctl_reg = QUP_I2C_NS_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 31, + .reset_mask = P_QUP_I2C_CLK, + }, + .root_en_mask = BIT(2), + .freq_tbl = clk_tbl_tcxo, + .set_rate = set_rate_nop, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "qup_i2c_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 19200000), + CLK_INIT(qup_i2c_clk.c), + }, +}; + +static struct rcg_clk uart1_clk = { + .b = { + .ctl_reg = UART_NS_REG, + .en_mask = BIT(5), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 7, + .reset_mask = P_UART1_CLK, + }, + .root_en_mask = BIT(4), + .freq_tbl = clk_tbl_tcxo, + .set_rate = set_rate_nop, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "uart1_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 19200000), + CLK_INIT(uart1_clk.c), + }, +}; + +static struct rcg_clk uart2_clk = { + .b = { + .ctl_reg = UART2_NS_REG, + .en_mask = BIT(5), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 5, + .reset_mask = P_UART2_CLK, + }, + .root_en_mask = BIT(4), + .freq_tbl = clk_tbl_tcxo, + .set_rate = set_rate_nop, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "uart2_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 19200000), + CLK_INIT(uart2_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_uartdm[] = { + F_MND16( 0, gnd, 1, 0, 0), + F_MND16( 3686400, pll3, 3, 3, 200), + F_MND16( 7372800, pll3, 3, 3, 100), + F_MND16(14745600, pll3, 3, 3, 50), + F_MND16(32000000, pll3, 3, 25, 192), + F_MND16(40000000, pll3, 3, 125, 768), + F_MND16(46400000, pll3, 3, 145, 768), + F_MND16(48000000, pll3, 3, 25, 128), + F_MND16(51200000, pll3, 3, 5, 24), + F_MND16(56000000, pll3, 3, 175, 768), + F_MND16(58982400, pll3, 3, 6, 25), + F_MND16(64000000, pll1, 4, 1, 3), + F_END, +}; + +static struct rcg_clk uart1dm_clk = { + .b = { + .ctl_reg = UART1DM_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 6, + .reset_mask = P_UART1DM_CLK, + }, + .ns_reg = UART1DM_NS_REG, + .md_reg = UART1DM_NS_REG - 4, + .root_en_mask = BIT(11), + .mnd_en_mask = BIT(8), + .freq_tbl = clk_tbl_uartdm, + .ns_mask = F_MASK_MND16, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "uart1dm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 64000000), + CLK_INIT(uart1dm_clk.c), + }, +}; + +static struct rcg_clk uart2dm_clk = { + .b = { + .ctl_reg = UART2DM_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 23, + .reset_mask = P_UART2DM_CLK, + }, + .ns_reg = UART2DM_NS_REG, + .md_reg = UART2DM_NS_REG - 4, + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_uartdm, + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "uart2dm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 64000000), + CLK_INIT(uart2dm_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_mdh[] = { + F_BASIC( 0, gnd, 1), + F_BASIC( 49150000, pll3, 15), + F_BASIC( 92160000, pll3, 8), + F_BASIC(122880000, pll3, 6), + F_BASIC(184320000, pll3, 4), + F_BASIC(245760000, pll3, 3), + F_BASIC(368640000, pll3, 2), + F_BASIC(384000000, pll1, 2), + F_BASIC(445500000, pll4, 2), + F_END, +}; + +static struct rcg_clk emdh_clk = { + .b = { + .ctl_reg = EMDH_NS_REG, + .halt_check = DELAY, + .reset_mask = P_EMDH_CLK, + }, + .root_en_mask = BIT(11), + .ns_reg = EMDH_NS_REG, + .ns_mask = F_MASK_BASIC, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_mdh, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "emdh_clk", + .flags = CLKFLAG_MIN | CLKFLAG_MAX, + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 445500000), + CLK_INIT(emdh_clk.c), + .depends = &axi_li_adsp_a_clk.c, + }, +}; + +static struct rcg_clk pmdh_clk = { + .b = { + .ctl_reg = PMDH_NS_REG, + .halt_check = DELAY, + .reset_mask = P_PMDH_CLK, + }, + .root_en_mask = BIT(11), + .ns_reg = PMDH_NS_REG, + .ns_mask = F_MASK_BASIC, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_mdh, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pmdh_clk", + .flags = CLKFLAG_MIN | CLKFLAG_MAX, + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 445500000), + CLK_INIT(pmdh_clk.c), + .depends = &axi_li_adsp_a_clk.c, + }, +}; + +static struct clk_freq_tbl clk_tbl_grp[] = { + F_BASIC( 24576000, lpxo, 1), + F_BASIC( 46080000, pll3, 16), + F_BASIC( 49152000, pll3, 15), + F_BASIC( 52662875, pll3, 14), + F_BASIC( 56713846, pll3, 13), + F_BASIC( 61440000, pll3, 12), + F_BASIC( 67025454, pll3, 11), + F_BASIC( 73728000, pll3, 10), + F_BASIC( 81920000, pll3, 9), + F_BASIC( 92160000, pll3, 8), + F_BASIC(105325714, pll3, 7), + F_BASIC(122880000, pll3, 6), + F_BASIC(147456000, pll3, 5), + F_BASIC(184320000, pll3, 4), + F_BASIC(192000000, pll1, 4), + F_BASIC(245760000, pll3, 3), + /* Sync to AXI. Hence this "rate" is not fixed. */ + F_RAW(1, &lpxo_clk.c, 0, BIT(14), 0, NULL), + F_END, +}; + +static struct rcg_clk grp_2d_clk = { + .b = { + .ctl_reg = GRP_2D_NS_REG, + .en_mask = BIT(7), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 31, + .reset_mask = P_GRP_2D_CLK, + }, + .ns_reg = GRP_2D_NS_REG, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_BASIC | (7 << 12), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_grp, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "grp_2d_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(NOMINAL, 192000000, HIGH, 245760000), + CLK_INIT(grp_2d_clk.c), + .depends = &axi_grp_2d_clk.c, + }, +}; + +static struct rcg_clk grp_3d_src_clk = { + .ns_reg = GRP_NS_REG, + .b = { + .ctl_reg = GRP_NS_REG, + .halt_check = NOCHECK, + }, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_BASIC | (7 << 12), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_grp, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "grp_3d_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(NOMINAL, 192000000, HIGH, 245760000), + CLK_INIT(grp_3d_src_clk.c), + .depends = &axi_li_grp_clk.c, + }, +}; + +static struct branch_clk grp_3d_clk = { + .b = { + .ctl_reg = GRP_NS_REG, + .en_mask = BIT(7), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 18, + .reset_mask = P_GRP_3D_CLK, + }, + .parent = &grp_3d_src_clk.c, + .c = { + .dbg_name = "grp_3d_clk", + .ops = &clk_ops_branch, + CLK_INIT(grp_3d_clk.c), + }, +}; + +static struct branch_clk imem_clk = { + .b = { + .ctl_reg = GRP_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 19, + .reset_mask = P_IMEM_CLK, + }, + .parent = &grp_3d_src_clk.c, + .c = { + .dbg_name = "imem_clk", + .ops = &clk_ops_branch, + CLK_INIT(imem_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_sdc1_3[] = { + F_MND8( 0, 0, 0, gnd, 1, 0, 0), + F_MND8( 144000, 19, 12, lpxo, 1, 1, 171), + F_MND8( 400000, 19, 12, lpxo, 1, 2, 123), + F_MND8(16027000, 19, 12, pll3, 3, 14, 215), + F_MND8(17000000, 19, 12, pll3, 4, 19, 206), + F_MND8(20480000, 19, 12, pll3, 4, 23, 212), + F_MND8(24576000, 19, 12, lpxo, 1, 0, 0), + F_MND8(49152000, 19, 12, pll3, 3, 1, 5), + F_END, +}; + +static struct rcg_clk sdc1_clk = { + .b = { + .ctl_reg = SDCn_NS_REG(1), + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 1, + .reset_mask = P_SDC1_CLK, + }, + .ns_reg = SDCn_NS_REG(1), + .md_reg = SDCn_NS_REG(1) - 4, + .ns_mask = F_MASK_MND8(19, 12), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_sdc1_3, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "sdc1_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 49152000), + CLK_INIT(sdc1_clk.c), + }, +}; + +static struct rcg_clk sdc3_clk = { + .b = { + .ctl_reg = SDCn_NS_REG(3), + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 24, + .reset_mask = P_SDC3_CLK, + }, + .ns_reg = SDCn_NS_REG(3), + .md_reg = SDCn_NS_REG(3) - 4, + .ns_mask = F_MASK_MND8(19, 12), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_sdc1_3, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "sdc3_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 49152000), + CLK_INIT(sdc3_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_sdc2_4[] = { + F_MND8( 0, 0, 0, gnd, 1, 0, 0), + F_MND8( 144000, 20, 13, lpxo, 1, 1, 171), + F_MND8( 400000, 20, 13, lpxo, 1, 2, 123), + F_MND8(16027000, 20, 13, pll3, 3, 14, 215), + F_MND8(17000000, 20, 13, pll3, 4, 19, 206), + F_MND8(20480000, 20, 13, pll3, 4, 23, 212), + F_MND8(24576000, 20, 13, lpxo, 1, 0, 0), + F_MND8(49152000, 20, 13, pll3, 3, 1, 5), + F_END, +}; + +static struct rcg_clk sdc2_clk = { + .b = { + .ctl_reg = SDCn_NS_REG(2), + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 0, + .reset_mask = P_SDC2_CLK, + }, + .ns_reg = SDCn_NS_REG(2), + .md_reg = SDCn_NS_REG(2) - 4, + .ns_mask = F_MASK_MND8(20, 13), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_sdc2_4, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "sdc2_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 49152000), + CLK_INIT(sdc2_clk.c), + }, +}; + +static struct rcg_clk sdc4_clk = { + .b = { + .ctl_reg = SDCn_NS_REG(4), + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 25, + .reset_mask = P_SDC4_CLK, + }, + .ns_reg = SDCn_NS_REG(4), + .md_reg = SDCn_NS_REG(4) - 4, + .ns_mask = F_MASK_MND8(20, 13), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_sdc2_4, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "sdc4_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 49152000), + CLK_INIT(sdc4_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_mdp_core[] = { + F_BASIC( 24576000, lpxo, 1), + F_BASIC( 46080000, pll3, 16), + F_BASIC( 49152000, pll3, 15), + F_BASIC( 52663000, pll3, 14), + F_BASIC( 92160000, pll3, 8), + F_BASIC(122880000, pll3, 6), + F_BASIC(147456000, pll3, 5), + F_BASIC(153600000, pll1, 5), + F_BASIC(192000000, pll1, 4), + F_END, +}; + +static struct rcg_clk mdp_clk = { + .b = { + .ctl_reg = MDP_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 16, + .reset_mask = P_MDP_CLK, + }, + .ns_reg = MDP_NS_REG, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_BASIC, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_mdp_core, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(NOMINAL, 153600000, HIGH, 192000000), + CLK_INIT(mdp_clk.c), + .depends = &axi_mdp_clk.c, + }, +}; + +static struct clk_freq_tbl clk_tbl_mdp_lcdc[] = { + F_MND16( 0, gnd, 1, 0, 0), + F_MND16(24576000, lpxo, 1, 0, 0), + F_MND16(30720000, pll3, 4, 1, 6), + F_MND16(32768000, pll3, 3, 2, 15), + F_MND16(40960000, pll3, 2, 1, 9), + F_MND16(73728000, pll3, 2, 1, 5), + F_END, +}; + +static struct rcg_clk mdp_lcdc_pclk_clk = { + .b = { + .ctl_reg = MDP_LCDC_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 28, + .reset_mask = P_MDP_LCDC_PCLK_CLK, + }, + .ns_reg = MDP_LCDC_NS_REG, + .md_reg = MDP_LCDC_NS_REG - 4, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_mdp_lcdc, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_lcdc_pclk_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 73728000), + CLK_INIT(mdp_lcdc_pclk_clk.c), + }, +}; + +static struct branch_clk mdp_lcdc_pad_pclk_clk = { + .b = { + .ctl_reg = MDP_LCDC_NS_REG, + .en_mask = BIT(12), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 29, + .reset_mask = P_MDP_LCDC_PAD_PCLK_CLK, + }, + .parent = &mdp_lcdc_pclk_clk.c, + .c = { + .dbg_name = "mdp_lcdc_pad_pclk_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_lcdc_pad_pclk_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_mdp_vsync[] = { + F_RAW( 0, &gnd_clk.c, 0, (0x3<<2), 0, NULL), + F_RAW(24576000, &lpxo_clk.c, 0, (0x1<<2), 0, NULL), + F_END, +}; + +static struct rcg_clk mdp_vsync_clk = { + .b = { + .ctl_reg = MDP_VSYNC_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 30, + .reset_mask = P_MDP_VSYNC_CLK, + }, + .ns_reg = MDP_VSYNC_REG, + .ns_mask = BM(3, 2), + .freq_tbl = clk_tbl_mdp_vsync, + .set_rate = set_rate_nop, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_vsync_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 24576000), + CLK_INIT(mdp_vsync_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_mi2s_codec[] = { + F_MND16( 0, gnd, 1, 0, 0), + F_MND16( 2048000, lpxo, 4, 1, 3), + F_MND16(12288000, lpxo, 2, 0, 0), + F_END, +}; + +static struct rcg_clk mi2s_codec_rx_m_clk = { + .b = { + .ctl_reg = MI2S_RX_NS_REG, + .en_mask = BIT(12), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 12, + .reset_mask = P_MI2S_CODEC_RX_M_CLK, + }, + .ns_reg = MI2S_RX_NS_REG, + .md_reg = MI2S_RX_NS_REG - 4, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_mi2s_codec, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mi2s_codec_rx_m_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 12288000), + CLK_INIT(mi2s_codec_rx_m_clk.c), + }, +}; + +static struct branch_clk mi2s_codec_rx_s_clk = { + .b = { + .ctl_reg = MI2S_RX_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 13, + .reset_mask = P_MI2S_CODEC_RX_S_CLK, + }, + .parent = &mi2s_codec_rx_m_clk.c, + .c = { + .dbg_name = "mi2s_codec_rx_s_clk", + .ops = &clk_ops_branch, + CLK_INIT(mi2s_codec_rx_s_clk.c), + }, +}; + +static struct rcg_clk mi2s_codec_tx_m_clk = { + .b = { + .ctl_reg = MI2S_TX_NS_REG, + .en_mask = BIT(12), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 8, + .reset_mask = P_MI2S_CODEC_TX_M_CLK, + }, + .ns_reg = MI2S_TX_NS_REG, + .md_reg = MI2S_TX_NS_REG - 4, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_mi2s_codec, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mi2s_codec_tx_m_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 12288000), + CLK_INIT(mi2s_codec_tx_m_clk.c), + }, +}; + +static struct branch_clk mi2s_codec_tx_s_clk = { + .b = { + .ctl_reg = MI2S_TX_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 11, + .reset_mask = P_MI2S_CODEC_TX_S_CLK, + }, + .parent = &mi2s_codec_tx_m_clk.c, + .c = { + .dbg_name = "mi2s_codec_tx_s_clk", + .ops = &clk_ops_branch, + CLK_INIT(mi2s_codec_tx_s_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_mi2s[] = { + F_MND16( 0, gnd, 1, 0, 0), + F_MND16(12288000, lpxo, 2, 0, 0), + F_END, +}; + +static struct rcg_clk mi2s_m_clk = { + .b = { + .ctl_reg = MI2S_NS_REG, + .en_mask = BIT(12), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 4, + .reset_mask = P_MI2S_M_CLK, + }, + .ns_reg = MI2S_NS_REG, + .md_reg = MI2S_NS_REG - 4, + .root_en_mask = BIT(11), + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_mi2s, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mi2s_m_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 12288000), + CLK_INIT(mi2s_m_clk.c), + }, +}; + +static struct branch_clk mi2s_s_clk = { + .b = { + .ctl_reg = MI2S_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 3, + .reset_mask = P_MI2S_S_CLK, + }, + .parent = &mi2s_m_clk.c, + .c = { + .dbg_name = "mi2s_s_clk", + .ops = &clk_ops_branch, + CLK_INIT(mi2s_s_clk.c), + }, +}; + +#define F_SDAC(f, s, div, m, n) \ + { \ + .freq_hz = f, \ + .md_val = MD16(m, n), \ + .ns_val = N16(m, n) | SPDIV(SRC_SEL_SDAC_##s, div), \ + .src_clk = &s##_clk.c, \ + } + +static struct clk_freq_tbl clk_tbl_sdac[] = { + F_SDAC( 256000, lpxo, 4, 1, 24), + F_SDAC( 352800, lpxo, 1, 147, 10240), + F_SDAC( 384000, lpxo, 4, 1, 16), + F_SDAC( 512000, lpxo, 4, 1, 12), + F_SDAC( 705600, lpxo, 1, 147, 5120), + F_SDAC( 768000, lpxo, 4, 1, 8), + F_SDAC(1024000, lpxo, 4, 1, 6), + F_SDAC(1411200, lpxo, 1, 147, 2560), + F_SDAC(1536000, lpxo, 4, 1, 4), + F_END, +}; + +static struct rcg_clk sdac_clk = { + .b = { + .ctl_reg = SDAC_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 2, + .reset_mask = P_SDAC_CLK, + }, + .ns_reg = SDAC_NS_REG, + .md_reg = SDAC_NS_REG - 4, + .root_en_mask = BIT(11), + .mnd_en_mask = BIT(8), + .freq_tbl = clk_tbl_sdac, + .ns_mask = F_MASK_MND16, + .set_rate = set_rate_mnd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "sdac_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 1536000), + CLK_INIT(sdac_clk.c), + }, +}; + +static struct branch_clk sdac_m_clk = { + .b = { + .ctl_reg = SDAC_NS_REG, + .en_mask = BIT(12), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 17, + .reset_mask = P_SDAC_M_CLK, + }, + .parent = &sdac_clk.c, + .c = { + .dbg_name = "sdac_m_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdac_m_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_tv[] = { + F_MND8( 0, 0, 0, gnd, 1, 0, 0), + F_MND8(27000000, 23, 16, pll4, 2, 2, 33), + F_MND8(74250000, 23, 16, pll4, 2, 1, 6), + F_END, +}; + +static struct rcg_clk tv_clk = { + .ns_reg = TV_NS_REG, + .b = { + .ctl_reg = TV_NS_REG, + .halt_check = NOCHECK, + }, + .md_reg = TV_NS_REG - 4, + .ns_mask = F_MASK_MND8(23, 16), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_tv, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "tv_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 74250000), + CLK_INIT(tv_clk.c), + }, +}; + +static struct branch_clk hdmi_clk = { + .b = { + .ctl_reg = HDMI_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 7, + .reset_mask = P_HDMI_CLK, + }, + .parent = &tv_clk.c, + .c = { + .dbg_name = "hdmi_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_clk.c), + }, +}; + +static struct branch_clk tv_dac_clk = { + .b = { + .ctl_reg = TV_NS_REG, + .en_mask = BIT(12), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 27, + .reset_mask = P_TV_DAC_CLK, + }, + .parent = &tv_clk.c, + .c = { + .dbg_name = "tv_dac_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_dac_clk.c), + }, +}; + +static struct branch_clk tv_enc_clk = { + .b = { + .ctl_reg = TV_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 10, + .reset_mask = P_TV_ENC_CLK, + }, + .parent = &tv_clk.c, + .c = { + .dbg_name = "tv_enc_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_enc_clk.c), + }, +}; + +/* Hacking root & branch into one param. */ +static struct branch_clk tsif_ref_clk = { + .b = { + .ctl_reg = TSIF_NS_REG, + .en_mask = BIT(9)|BIT(11), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 11, + .reset_mask = P_TSIF_REF_CLK, + }, + .parent = &tv_clk.c, + .c = { + .dbg_name = "tsif_ref_clk", + .ops = &clk_ops_branch, + CLK_INIT(tsif_ref_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_usb[] = { + F_MND8( 0, 0, 0, gnd, 1, 0, 0), + F_MND8(60000000, 23, 16, pll1, 2, 5, 32), + F_END, +}; + +static struct rcg_clk usb_hs_src_clk = { + .ns_reg = USBH_NS_REG, + .b = { + .ctl_reg = USBH_NS_REG, + .halt_check = NOCHECK, + }, + .md_reg = USBH_NS_REG - 4, + .ns_mask = F_MASK_MND8(23, 16), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_usb, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "usb_hs_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 60000000), + CLK_INIT(usb_hs_src_clk.c), + .depends = &axi_li_adsp_a_clk.c, + }, +}; + +static struct branch_clk usb_hs_clk = { + .b = { + .ctl_reg = USBH_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 26, + .reset_mask = P_USB_HS_CLK, + }, + .c = { + .dbg_name = "usb_hs_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs_clk.c), + }, +}; + +static struct branch_clk usb_hs_core_clk = { + .b = { + .ctl_reg = USBH_NS_REG, + .en_mask = BIT(13), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 27, + .reset_mask = P_USB_HS_CORE_CLK, + }, + .parent = &usb_hs_src_clk.c, + .c = { + .dbg_name = "usb_hs_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs_core_clk.c), + }, +}; + +static struct branch_clk usb_hs2_clk = { + .b = { + .ctl_reg = USBH2_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 3, + .reset_mask = P_USB_HS2_CLK, + }, + .parent = &usb_hs_src_clk.c, + .c = { + .dbg_name = "usb_hs2_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs2_clk.c), + }, +}; + +static struct branch_clk usb_hs2_core_clk = { + .b = { + .ctl_reg = USBH2_NS_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 28, + .reset_mask = P_USB_HS2_CORE_CLK, + }, + .parent = &usb_hs_src_clk.c, + .c = { + .dbg_name = "usb_hs2_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs2_core_clk.c), + }, +}; + +static struct branch_clk usb_hs3_clk = { + .b = { + .ctl_reg = USBH3_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 2, + .reset_mask = P_USB_HS3_CLK, + }, + .parent = &usb_hs_src_clk.c, + .c = { + .dbg_name = "usb_hs3_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs3_clk.c), + }, +}; + +static struct branch_clk usb_hs3_core_clk = { + .b = { + .ctl_reg = USBH3_NS_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 29, + .reset_mask = P_USB_HS3_CORE_CLK, + }, + .parent = &usb_hs_src_clk.c, + .c = { + .dbg_name = "usb_hs3_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs3_core_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_vfe_jpeg[] = { + F_MND16( 24576000, lpxo, 1, 0, 0), + F_MND16( 36864000, pll3, 4, 1, 5), + F_MND16( 46080000, pll3, 4, 1, 4), + F_MND16( 61440000, pll3, 4, 1, 3), + F_MND16( 73728000, pll3, 2, 1, 5), + F_MND16( 81920000, pll3, 3, 1, 3), + F_MND16( 92160000, pll3, 4, 1, 2), + F_MND16( 98304000, pll3, 3, 2, 5), + F_MND16(105326000, pll3, 2, 2, 7), + F_MND16(122880000, pll3, 2, 1, 3), + F_MND16(147456000, pll3, 2, 2, 5), + F_MND16(153600000, pll1, 2, 2, 5), + F_MND16(192000000, pll1, 4, 0, 0), + F_END, +}; + +static struct rcg_clk jpeg_clk = { + .b = { + .ctl_reg = JPEG_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 1, + .reset_mask = P_JPEG_CLK, + }, + .ns_reg = JPEG_NS_REG, + .md_reg = JPEG_NS_REG - 4, + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_vfe_jpeg, + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "jpeg_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(NOMINAL, 153600000, HIGH, 192000000), + CLK_INIT(jpeg_clk.c), + .depends = &axi_li_jpeg_clk.c, + }, +}; + +static struct rcg_clk vfe_clk = { + .b = { + .ctl_reg = CAM_VFE_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEB_REG, + .halt_bit = 0, + .reset_mask = P_VFE_CLK, + }, + .ns_reg = CAM_VFE_NS_REG, + .md_reg = CAM_VFE_NS_REG - 4, + .root_en_mask = BIT(13), + .freq_tbl = clk_tbl_vfe_jpeg, + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vfe_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(NOMINAL, 153600000, HIGH, 192000000), + CLK_INIT(vfe_clk.c), + .depends = &axi_li_vfe_clk.c, + }, +}; + +static struct branch_clk vfe_mdc_clk = { + .b = { + .ctl_reg = CAM_VFE_NS_REG, + .en_mask = BIT(11), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 9, + .reset_mask = P_VFE_MDC_CLK, + }, + .parent = &vfe_clk.c, + .c = { + .dbg_name = "vfe_mdc_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_mdc_clk.c), + }, +}; + +static struct branch_clk vfe_camif_clk = { + .b = { + .ctl_reg = CAM_VFE_NS_REG, + .en_mask = BIT(15), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 13, + .reset_mask = P_VFE_CAMIF_CLK, + }, + .parent = &vfe_clk.c, + .c = { + .dbg_name = "vfe_camif_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_camif_clk.c), + }, +}; + +static struct branch_clk csi0_vfe_clk = { + .b = { + .ctl_reg = CSI_NS_REG, + .en_mask = BIT(15), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 16, + .reset_mask = P_CSI0_VFE_CLK, + }, + .parent = &vfe_clk.c, + .c = { + .dbg_name = "csi0_vfe_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_vfe_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_cam[] = { + F_MND16( 0, gnd, 1, 0, 0), + F_MND16( 6000000, pll1, 4, 1, 32), + F_MND16( 8000000, pll1, 4, 1, 24), + F_MND16(12000000, pll1, 4, 1, 16), + F_MND16(16000000, pll1, 4, 1, 12), + F_MND16(19200000, pll1, 4, 1, 10), + F_MND16(24000000, pll1, 4, 1, 8), + F_MND16(32000000, pll1, 4, 1, 6), + F_MND16(48000000, pll1, 4, 1, 4), + F_MND16(64000000, pll1, 4, 1, 3), + F_END, +}; + +static struct rcg_clk cam_m_clk = { + .b = { + .ctl_reg = CAM_NS_REG, + .halt_check = DELAY, + .reset_mask = P_CAM_M_CLK, + }, + .ns_reg = CAM_NS_REG, + .md_reg = CAM_NS_REG - 4, + .root_en_mask = BIT(9), + .freq_tbl = clk_tbl_cam, + .ns_mask = F_MASK_MND16, + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "cam_m_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 64000000), + CLK_INIT(cam_m_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_vpe[] = { + F_MND8( 24576000, 22, 15, lpxo, 1, 0, 0), + F_MND8( 30720000, 22, 15, pll3, 4, 1, 6), + F_MND8( 61440000, 22, 15, pll3, 4, 1, 3), + F_MND8( 81920000, 22, 15, pll3, 3, 1, 3), + F_MND8(122880000, 22, 15, pll3, 3, 1, 2), + F_MND8(147456000, 22, 15, pll3, 1, 1, 5), + F_MND8(153600000, 22, 15, pll1, 1, 1, 5), + F_END, +}; + +static struct rcg_clk vpe_clk = { + .b = { + .ctl_reg = VPE_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 10, + .reset_mask = P_VPE_CLK, + }, + .ns_reg = VPE_NS_REG, + .md_reg = VPE_NS_REG - 4, + .ns_mask = F_MASK_MND8(22, 15), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_vpe, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "vpe_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 153600000), + CLK_INIT(vpe_clk.c), + .depends = &axi_vpe_clk.c, + }, +}; + +static struct clk_freq_tbl clk_tbl_mfc[] = { + F_MND8( 24576000, 24, 17, lpxo, 1, 0, 0), + F_MND8( 30720000, 24, 17, pll3, 4, 1, 6), + F_MND8( 61440000, 24, 17, pll3, 4, 1, 3), + F_MND8( 81920000, 24, 17, pll3, 3, 1, 3), + F_MND8(122880000, 24, 17, pll3, 3, 1, 2), + F_MND8(147456000, 24, 17, pll3, 1, 1, 5), + F_MND8(153600000, 24, 17, pll1, 1, 1, 5), + F_MND8(170667000, 24, 17, pll1, 1, 2, 9), + F_END, +}; + +static struct rcg_clk mfc_clk = { + .b = { + .ctl_reg = MFC_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 12, + .reset_mask = P_MFC_CLK, + }, + .ns_reg = MFC_NS_REG, + .md_reg = MFC_NS_REG - 4, + .ns_mask = F_MASK_MND8(24, 17), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_mfc, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "mfc_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 170667000), + CLK_INIT(mfc_clk.c), + .depends = &axi_mfc_clk.c, + }, +}; + +static struct branch_clk mfc_div2_clk = { + .b = { + .ctl_reg = MFC_NS_REG, + .en_mask = BIT(15), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 11, + .reset_mask = P_MFC_DIV2_CLK, + }, + .parent = &mfc_clk.c, + .c = { + .dbg_name = "mfc_div2_clk", + .ops = &clk_ops_branch, + CLK_INIT(mfc_div2_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_spi[] = { + F_MND8( 0, 0, 0, gnd, 1, 0, 0), + F_MND8( 9963243, 19, 12, pll3, 4, 2, 37), + F_MND8(24576000, 19, 12, lpxo, 1, 0, 0), + F_MND8(26331429, 19, 12, pll3, 4, 1, 7), + F_END, +}; + +static struct rcg_clk spi_clk = { + .b = { + .ctl_reg = SPI_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 0, + .reset_mask = P_SPI_CLK, + }, + .ns_reg = SPI_NS_REG, + .md_reg = SPI_NS_REG - 4, + .ns_mask = F_MASK_MND8(19, 12), + .mnd_en_mask = BIT(8), + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_spi, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_mnd, + .c = { + .dbg_name = "spi_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 26331429), + CLK_INIT(spi_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_lpa_codec[] = { + F_RAW(1, NULL, 0, 0, 0, NULL), /* src MI2S_CODEC_RX */ + F_RAW(2, NULL, 0, 1, 0, NULL), /* src ECODEC_CIF */ + F_RAW(3, NULL, 0, 2, 0, NULL), /* src MI2S */ + F_RAW(4, NULL, 0, 3, 0, NULL), /* src SDAC */ + F_END, +}; + +static struct rcg_clk lpa_codec_clk = { + .b = { + .ctl_reg = LPA_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 6, + .reset_mask = P_LPA_CODEC_CLK, + }, + .ns_reg = LPA_NS_REG, + .ns_mask = BM(1, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_lpa_codec, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "lpa_codec_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 4), + CLK_INIT(lpa_codec_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_mdc[] = { + F_RAW(1, NULL, 0, 0, 0, NULL), + F_END +}; + +static struct rcg_clk mdc_clk = { + .b = { + .ctl_reg = MDC_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_STATEA_REG, + .halt_bit = 10, + .reset_mask = P_MDC_CLK, + }, + .ns_reg = MDC_NS_REG, + .root_en_mask = BIT(11), + .freq_tbl = clk_tbl_mdc, + .current_freq = &rcg_dummy_freq, + .set_rate = set_rate_nop, + .c = { + .dbg_name = "mdc_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 1), + CLK_INIT(mdc_clk.c), + }, +}; + +static struct branch_clk lpa_core_clk = { + .b = { + .ctl_reg = LPA_NS_REG, + .en_mask = BIT(5), + .halt_reg = CLK_HALT_STATEC_REG, + .halt_bit = 5, + .reset_mask = P_LPA_CORE_CLK, + }, + .c = { + .dbg_name = "lpa_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(lpa_core_clk.c), + }, +}; + +static DEFINE_CLK_PCOM(adsp_clk, ADSP_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(codec_ssbi_clk, CODEC_SSBI_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(ebi1_clk, EBI1_CLK, CLKFLAG_SKIP_AUTO_OFF | CLKFLAG_MIN); +static DEFINE_CLK_PCOM(ebi1_fixed_clk, EBI1_FIXED_CLK, CLKFLAG_MIN | + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(ecodec_clk, ECODEC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(gp_clk, GP_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(uart3_clk, UART3_CLK, 0); +static DEFINE_CLK_PCOM(usb_phy_clk, USB_PHY_CLK, CLKFLAG_MIN); + +static DEFINE_CLK_PCOM(p_grp_2d_clk, GRP_2D_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_grp_2d_p_clk, GRP_2D_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_hdmi_clk, HDMI_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_jpeg_clk, JPEG_CLK, CLKFLAG_MIN); +static DEFINE_CLK_PCOM(p_jpeg_p_clk, JPEG_P_CLK, 0); +static DEFINE_CLK_PCOM(p_lpa_codec_clk, LPA_CODEC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_lpa_core_clk, LPA_CORE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_lpa_p_clk, LPA_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mi2s_m_clk, MI2S_M_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mi2s_s_clk, MI2S_S_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mi2s_codec_rx_m_clk, MI2S_CODEC_RX_M_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mi2s_codec_rx_s_clk, MI2S_CODEC_RX_S_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mi2s_codec_tx_m_clk, MI2S_CODEC_TX_M_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mi2s_codec_tx_s_clk, MI2S_CODEC_TX_S_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_sdac_clk, SDAC_CLK, 0); +static DEFINE_CLK_PCOM(p_sdac_m_clk, SDAC_M_CLK, 0); +static DEFINE_CLK_PCOM(p_vfe_clk, VFE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_vfe_camif_clk, VFE_CAMIF_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_vfe_mdc_clk, VFE_MDC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_vfe_p_clk, VFE_P_CLK, 0); +static DEFINE_CLK_PCOM(p_grp_3d_clk, GRP_3D_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_grp_3d_p_clk, GRP_3D_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_imem_clk, IMEM_CLK, 0); +static DEFINE_CLK_PCOM(p_mdp_lcdc_pad_pclk_clk, MDP_LCDC_PAD_PCLK_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mdp_lcdc_pclk_clk, MDP_LCDC_PCLK_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mdp_p_clk, MDP_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mdp_vsync_clk, MDP_VSYNC_CLK, 0); +static DEFINE_CLK_PCOM(p_tsif_ref_clk, TSIF_REF_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_tsif_p_clk, TSIF_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_tv_dac_clk, TV_DAC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_tv_enc_clk, TV_ENC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_emdh_clk, EMDH_CLK, CLKFLAG_MIN | CLKFLAG_MAX); +static DEFINE_CLK_PCOM(p_emdh_p_clk, EMDH_P_CLK, 0); +static DEFINE_CLK_PCOM(p_i2c_clk, I2C_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_i2c_2_clk, I2C_2_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mdc_clk, MDC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_pmdh_clk, PMDH_CLK, CLKFLAG_MIN | CLKFLAG_MAX); +static DEFINE_CLK_PCOM(p_pmdh_p_clk, PMDH_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_sdc1_clk, SDC1_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc1_p_clk, SDC1_P_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc2_clk, SDC2_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc2_p_clk, SDC2_P_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc3_clk, SDC3_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc3_p_clk, SDC3_P_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc4_clk, SDC4_CLK, 0); +static DEFINE_CLK_PCOM(p_sdc4_p_clk, SDC4_P_CLK, 0); +static DEFINE_CLK_PCOM(p_uart2_clk, UART2_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_usb_hs2_clk, USB_HS2_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs2_core_clk, USB_HS2_CORE_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs2_p_clk, USB_HS2_P_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs3_clk, USB_HS3_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs3_core_clk, USB_HS3_CORE_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs3_p_clk, USB_HS3_P_CLK, 0); +static DEFINE_CLK_PCOM(p_qup_i2c_clk, QUP_I2C_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_spi_clk, SPI_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_spi_p_clk, SPI_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_uart1_clk, UART1_CLK, 0); +static DEFINE_CLK_PCOM(p_uart1dm_clk, UART1DM_CLK, 0); +static DEFINE_CLK_PCOM(p_uart2dm_clk, UART2DM_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_usb_hs_clk, USB_HS_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs_core_clk, USB_HS_CORE_CLK, 0); +static DEFINE_CLK_PCOM(p_usb_hs_p_clk, USB_HS_P_CLK, 0); +static DEFINE_CLK_PCOM(p_cam_m_clk, CAM_M_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_camif_pad_p_clk, CAMIF_PAD_P_CLK, 0); +static DEFINE_CLK_PCOM(p_csi0_clk, CSI0_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_csi0_vfe_clk, CSI0_VFE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_csi0_p_clk, CSI0_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mdp_clk, MDP_CLK, CLKFLAG_MIN); +static DEFINE_CLK_PCOM(p_mfc_clk, MFC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mfc_div2_clk, MFC_DIV2_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_mfc_p_clk, MFC_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_vpe_clk, VPE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_adm_clk, ADM_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_ce_clk, CE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_axi_rotator_clk, AXI_ROTATOR_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(p_rotator_imem_clk, ROTATOR_IMEM_CLK, 0); +static DEFINE_CLK_PCOM(p_rotator_p_clk, ROTATOR_P_CLK, 0); + +static DEFINE_CLK_VOTER(ebi_dtv_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_grp_3d_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_grp_2d_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_lcdc_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_mddi_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_tv_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_vcd_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_vfe_clk, &ebi1_fixed_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_adm_clk, &ebi1_fixed_clk.c, 0); + +#ifdef CONFIG_DEBUG_FS + +#define CLK_TEST_2(s) (s) +#define CLK_TEST_HS(s) (0x4000 | ((s) << 8)) +#define CLK_TEST_LS(s) (0x4D40 | (s)) + +struct measure_sel { + u32 test_vector; + struct clk *clk; +}; + +static struct measure_sel measure_mux[] = { + { CLK_TEST_2(0x03), &emdh_p_clk.c }, + { CLK_TEST_2(0x04), &pmdh_p_clk.c }, + { CLK_TEST_2(0x06), &mdp_p_clk.c }, + { CLK_TEST_2(0x07), &lpa_p_clk.c }, + { CLK_TEST_2(0x08), &usb_hs2_p_clk.c }, + { CLK_TEST_2(0x09), &spi_clk.c }, + { CLK_TEST_2(0x0B), &i2c_2_clk.c }, + { CLK_TEST_2(0x0D), &mi2s_m_clk.c }, + { CLK_TEST_2(0x0E), &lpa_core_clk.c }, + { CLK_TEST_2(0x0F), &lpa_codec_clk.c }, + { CLK_TEST_2(0x10), &usb_hs3_p_clk.c }, + { CLK_TEST_2(0x11), &adm_p_clk.c }, + { CLK_TEST_2(0x13), &hdmi_clk.c }, + { CLK_TEST_2(0x14), &usb_hs_core_clk.c }, + { CLK_TEST_2(0x15), &usb_hs2_core_clk.c }, + { CLK_TEST_2(0x16), &usb_hs3_core_clk.c }, + { CLK_TEST_2(0x17), &mi2s_codec_tx_s_clk.c }, + { CLK_TEST_2(0x18), &spi_p_clk.c }, + { CLK_TEST_2(0x1A), &camif_pad_p_clk.c }, + { CLK_TEST_2(0x1C), &qup_i2c_clk.c }, + { CLK_TEST_2(0x1F), &mfc_div2_clk.c }, + { CLK_TEST_2(0x38), &mfc_clk.c }, + + { CLK_TEST_HS(0x00), &adm_clk.c }, + { CLK_TEST_HS(0x01), &mdp_lcdc_pad_pclk_clk.c }, + { CLK_TEST_HS(0x02), &mdp_lcdc_pclk_clk.c }, + { CLK_TEST_HS(0x03), &axi_rotator_clk.c }, + { CLK_TEST_HS(0x07), &axi_li_vg_clk.c }, + { CLK_TEST_HS(0x09), &axi_li_apps_clk.c }, + { CLK_TEST_HS(0x0E), &axi_li_jpeg_clk.c }, + { CLK_TEST_HS(0x0F), &emdh_clk.c }, + { CLK_TEST_HS(0x14), &mdp_clk.c }, + { CLK_TEST_HS(0x15), &pmdh_clk.c }, + { CLK_TEST_HS(0x19), &axi_grp_2d_clk.c }, + { CLK_TEST_HS(0x1A), &axi_li_grp_clk.c }, + { CLK_TEST_HS(0x1B), &axi_li_vfe_clk.c }, + { CLK_TEST_HS(0x1C), &grp_2d_clk.c }, + { CLK_TEST_HS(0x1E), &grp_3d_clk.c }, + { CLK_TEST_HS(0x1F), &imem_clk.c }, + { CLK_TEST_HS(0x20), &jpeg_clk.c }, + { CLK_TEST_HS(0x24), &axi_li_adsp_a_clk.c }, + { CLK_TEST_HS(0x26), &rotator_imem_clk.c }, + { CLK_TEST_HS(0x27), &axi_vpe_clk.c }, + { CLK_TEST_HS(0x2A), &axi_mfc_clk.c }, + { CLK_TEST_HS(0x2B), &axi_mdp_clk.c }, + { CLK_TEST_HS(0x2C), &vpe_clk.c }, + { CLK_TEST_HS(0x30), &vfe_camif_clk.c }, + { CLK_TEST_HS(0x31), &csi0_clk.c }, + { CLK_TEST_HS(0x32), &csi0_vfe_clk.c }, + { CLK_TEST_HS(0x33), &csi0_p_clk.c }, + + { CLK_TEST_LS(0x03), &ce_clk.c }, + { CLK_TEST_LS(0x04), &cam_m_clk.c }, + { CLK_TEST_LS(0x0C), &grp_2d_p_clk.c }, + { CLK_TEST_LS(0x0D), &i2c_clk.c }, + { CLK_TEST_LS(0x0E), &mi2s_codec_rx_m_clk.c }, + { CLK_TEST_LS(0x0F), &mi2s_codec_rx_s_clk.c }, + { CLK_TEST_LS(0x10), &mi2s_codec_tx_m_clk.c }, + { CLK_TEST_LS(0x13), &mdp_vsync_clk.c }, + { CLK_TEST_LS(0x15), &vfe_p_clk.c }, + { CLK_TEST_LS(0x16), &mdc_clk.c }, + { CLK_TEST_LS(0x17), &vfe_mdc_clk.c }, + { CLK_TEST_LS(0x18), &usb_hs_p_clk.c }, + { CLK_TEST_LS(0x1C), &uart1dm_p_clk.c }, + { CLK_TEST_LS(0x1E), &jpeg_p_clk.c }, + { CLK_TEST_LS(0x20), &sdac_clk.c }, + { CLK_TEST_LS(0x21), &sdc1_p_clk.c }, + { CLK_TEST_LS(0x22), &sdc1_clk.c }, + { CLK_TEST_LS(0x23), &sdc2_p_clk.c }, + { CLK_TEST_LS(0x24), &sdc2_clk.c }, + { CLK_TEST_LS(0x25), &tsif_p_clk.c }, + { CLK_TEST_LS(0x26), &sdac_m_clk.c }, + { CLK_TEST_LS(0x27), &grp_3d_p_clk.c }, + { CLK_TEST_LS(0x2A), &tsif_ref_clk.c }, + { CLK_TEST_LS(0x2B), &tv_enc_clk.c }, + { CLK_TEST_LS(0x2C), &tv_dac_clk.c }, + { CLK_TEST_LS(0x2D), &rotator_p_clk.c }, + { CLK_TEST_LS(0x2F), &uart1_clk.c }, + { CLK_TEST_LS(0x30), &uart1dm_clk.c }, + { CLK_TEST_LS(0x31), &uart2_clk.c }, + { CLK_TEST_LS(0x33), &usb_hs2_clk.c }, + { CLK_TEST_LS(0x34), &usb_hs3_clk.c }, + { CLK_TEST_LS(0x35), &mfc_p_clk.c }, + { CLK_TEST_LS(0x36), &vfe_clk.c }, + { CLK_TEST_LS(0x39), &sdc3_p_clk.c }, + { CLK_TEST_LS(0x3A), &sdc3_clk.c }, + { CLK_TEST_LS(0x3B), &sdc4_p_clk.c }, + { CLK_TEST_LS(0x3C), &sdc4_clk.c }, + { CLK_TEST_LS(0x3D), &uart2dm_clk.c }, + { CLK_TEST_LS(0x3E), &uart2dm_p_clk.c }, + { CLK_TEST_LS(0x3F), &usb_hs_clk.c }, +}; + +static struct measure_sel *find_measure_sel(struct clk *clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(measure_mux); i++) + if (measure_mux[i].clk == clk) + return &measure_mux[i]; + return NULL; +} + +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct measure_sel *p; + unsigned long flags; + + if (!parent) + return -EINVAL; + + p = find_measure_sel(parent); + if (!p) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Program test vector. */ + if (p->test_vector <= 0xFF) { + /* Select CLK_TEST_2 */ + writel_relaxed(0x4D40, CLK_TEST_BASE_REG); + writel_relaxed(p->test_vector, CLK_TEST_2_BASE_REG); + } else + writel_relaxed(p->test_vector, CLK_TEST_BASE_REG); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +/* Sample clock for 'tcxo4_ticks' reference clock ticks. */ +static unsigned long run_measurement(unsigned tcxo4_ticks) +{ + /* TCXO4_CNT_EN and RINGOSC_CNT_EN register values. */ + u32 reg_val_enable = readl_relaxed(MISC_CLK_CTL_BASE_REG) | 0x3; + u32 reg_val_disable = reg_val_enable & ~0x3; + + /* Stop counters and set the TCXO4 counter start value. */ + writel_relaxed(reg_val_disable, MISC_CLK_CTL_BASE_REG); + writel_relaxed(tcxo4_ticks, TCXO_CNT_BASE_REG); + + /* Run measurement and wait for completion. */ + writel_relaxed(reg_val_enable, MISC_CLK_CTL_BASE_REG); + while (readl_relaxed(TCXO_CNT_DONE_BASE_REG) == 0) + cpu_relax(); + + /* Stop counters. */ + writel_relaxed(reg_val_disable, MISC_CLK_CTL_BASE_REG); + + return readl_relaxed(RINGOSC_CNT_BASE_REG); +} + +/* Perform a hardware rate measurement for a given clock. + FOR DEBUG USE ONLY: Measurements take ~15 ms! */ +static unsigned long measure_clk_get_rate(struct clk *clk) +{ + unsigned long flags; + u32 regval, prph_web_reg_old; + u64 raw_count_short, raw_count_full; + unsigned ret; + + clk_prepare_enable(&tcxo_clk.c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable TCXO4 clock branch and root. */ + prph_web_reg_old = readl_relaxed(PRPH_WEB_NS_BASE_REG); + regval = prph_web_reg_old | BIT(9) | BIT(11); + writel_relaxed(regval, PRPH_WEB_NS_BASE_REG); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(0x10000); + + /* Disable TCXO4 clock branch and root. */ + writel_relaxed(prph_web_reg_old, PRPH_WEB_NS_BASE_REG); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, ((0x10000 * 10) + 35)); + ret = raw_count_full; + } + + clk_disable_unprepare(&tcxo_clk.c); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned long measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops clk_ops_measure = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, +}; + +static struct clk measure_clk = { + .dbg_name = "measure_clk", + .ops = &clk_ops_measure, + CLK_INIT(measure_clk), +}; + +/* Implementation for clk_set_flags(). */ +int soc_clk_set_flags(struct clk *clk, unsigned clk_flags) +{ + uint32_t regval, ret = 0; + unsigned long flags; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + if (clk == &vfe_clk.c) { + regval = readl_relaxed(CAM_VFE_NS_REG); + /* Flag values chosen for backward compatibility + * with proc_comm remote clock control. */ + if (clk_flags == 0x00000100) { + /* Select external source. */ + regval |= BIT(14); + } else if (clk_flags == 0x00000200) { + /* Select internal source. */ + regval &= ~BIT(14); + } else + ret = -EINVAL; + + writel_relaxed(regval, CAM_VFE_NS_REG); + /* Make sure write is issued before returning. */ + mb(); + } else + ret = -EPERM; + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} + +static int msm7x30_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + /* reset_mask is actually a proc_comm id */ + unsigned id = to_rcg_clk(clk)->b.reset_mask; + return pc_clk_reset(id, action); +} + +static int soc_branch_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + unsigned id = to_branch_clk(clk)->b.reset_mask; + return pc_clk_reset(id, action); +} + +/* + * Clock ownership detection code + */ + +enum { + SH2_OWN_GLBL, + SH2_OWN_APPS1, + SH2_OWN_APPS2, + SH2_OWN_ROW1, + SH2_OWN_ROW2, + SH2_OWN_APPS3, + NUM_OWNERSHIP +}; +static __initdata uint32_t ownership_regs[NUM_OWNERSHIP]; + +static void __init cache_ownership(void) +{ + ownership_regs[SH2_OWN_GLBL] = readl_relaxed(SH2_OWN_GLBL_BASE_REG); + ownership_regs[SH2_OWN_APPS1] = readl_relaxed(SH2_OWN_APPS1_BASE_REG); + ownership_regs[SH2_OWN_APPS2] = readl_relaxed(SH2_OWN_APPS2_BASE_REG); + ownership_regs[SH2_OWN_ROW1] = readl_relaxed(SH2_OWN_ROW1_BASE_REG); + ownership_regs[SH2_OWN_ROW2] = readl_relaxed(SH2_OWN_ROW2_BASE_REG); + ownership_regs[SH2_OWN_APPS3] = readl_relaxed(SH2_OWN_APPS3_BASE_REG); +} + +static void __init print_ownership(void) +{ + pr_info("Clock ownership\n"); + pr_info(" GLBL : %08x\n", ownership_regs[SH2_OWN_GLBL]); + pr_info(" APPS : %08x %08x %08x\n", ownership_regs[SH2_OWN_APPS1], + ownership_regs[SH2_OWN_APPS2], ownership_regs[SH2_OWN_APPS3]); + pr_info(" ROW : %08x %08x\n", ownership_regs[SH2_OWN_ROW1], + ownership_regs[SH2_OWN_ROW2]); +} + +#define O(x) (&ownership_regs[(SH2_OWN_##x)]) +#define OWN(r, b, name, clk, dev) \ + { \ + .lk = CLK_LOOKUP(name, clk.c, dev), \ + .remote = &p_##clk.c, \ + .reg = O(r), \ + .bit = BIT(b), \ + } + +static struct clk_local_ownership { + struct clk_lookup lk; + const u32 *reg; + const u32 bit; + struct clk *remote; +} ownership_map[] __initdata = { + /* Sources */ + { CLK_LOOKUP("pll1_clk", pll1_clk.c, "acpu") }, + { CLK_LOOKUP("pll2_clk", pll2_clk.c, "acpu") }, + { CLK_LOOKUP("pll3_clk", pll3_clk.c, "acpu") }, + { CLK_LOOKUP("measure", measure_clk, "debug") }, + + /* PCOM */ + { CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL) }, + { CLK_LOOKUP("codec_ssbi_clk", codec_ssbi_clk.c, NULL) }, + { CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL) }, + { CLK_LOOKUP("ebi1_fixed_clk", ebi1_fixed_clk.c, NULL) }, + { CLK_LOOKUP("ecodec_clk", ecodec_clk.c, NULL) }, + { CLK_LOOKUP("core_clk", gp_clk.c, "") }, + { CLK_LOOKUP("core_clk", uart3_clk.c, "msm_serial.2") }, + { CLK_LOOKUP("phy_clk", usb_phy_clk.c, "msm_otg") }, + + /* Voters */ + { CLK_LOOKUP("mem_clk", ebi_dtv_clk.c, "dtv.0") }, + { CLK_LOOKUP("bus_clk", ebi_grp_2d_clk.c, "kgsl-2d0.0") }, + { CLK_LOOKUP("bus_clk", ebi_grp_3d_clk.c, "kgsl-3d0.0") }, + { CLK_LOOKUP("mem_clk", ebi_lcdc_clk.c, "lcdc.0") }, + { CLK_LOOKUP("mem_clk", ebi_mddi_clk.c, "mddi.0") }, + { CLK_LOOKUP("mem_clk", ebi_tv_clk.c, "tvenc.0") }, + { CLK_LOOKUP("mem_clk", ebi_vcd_clk.c, "msm_vidc.0") }, + { CLK_LOOKUP("ebi1_vfe_clk", ebi_vfe_clk.c, NULL) }, + { CLK_LOOKUP("mem_clk", ebi_adm_clk.c, "msm_dmov") }, + + /* + * This is a many-to-one mapping because we don't know how the remote + * clock code has decided to handle the dependencies between clocks for + * a particular hardware block. We determine the ownership for all the + * clocks going into a block by checking the ownership bit of one + * register (usually the ns register). + */ + OWN(APPS1, 6, "core_clk", grp_2d_clk, "kgsl-2d0.0"), + OWN(APPS1, 6, "core_clk", grp_2d_clk, "footswitch-pcom.0"), + OWN(APPS1, 6, "iface_clk", grp_2d_p_clk, "kgsl-2d0.0"), + OWN(APPS1, 6, "iface_clk", grp_2d_p_clk, "footswitch-pcom.0"), + OWN(APPS1, 31, "hdmi_clk", hdmi_clk, "dtv.0"), + OWN(APPS1, 0, "core_clk", jpeg_clk, "msm_gemini.0"), + OWN(APPS1, 0, "iface_clk", jpeg_p_clk, "msm_gemini.0"), + OWN(APPS1, 23, "lpa_codec_clk", lpa_codec_clk, NULL), + OWN(APPS1, 23, "lpa_core_clk", lpa_core_clk, NULL), + OWN(APPS1, 23, "lpa_pclk", lpa_p_clk, NULL), + OWN(APPS1, 28, "mi2s_m_clk", mi2s_m_clk, NULL), + OWN(APPS1, 28, "mi2s_s_clk", mi2s_s_clk, NULL), + OWN(APPS1, 12, "mi2s_codec_rx_m_clk", mi2s_codec_rx_m_clk, NULL), + OWN(APPS1, 12, "mi2s_codec_rx_s_clk", mi2s_codec_rx_s_clk, NULL), + OWN(APPS1, 14, "mi2s_codec_tx_m_clk", mi2s_codec_tx_m_clk, NULL), + OWN(APPS1, 14, "mi2s_codec_tx_s_clk", mi2s_codec_tx_s_clk, NULL), + OWN(APPS1, 26, "sdac_clk", sdac_clk, NULL), + OWN(APPS1, 26, "sdac_m_clk", sdac_m_clk, NULL), + OWN(APPS1, 8, "vfe_clk", vfe_clk, NULL), + OWN(APPS1, 8, "core_clk", vfe_clk, "footswitch-pcom.8"), + OWN(APPS1, 8, "vfe_camif_clk", vfe_camif_clk, NULL), + OWN(APPS1, 8, "vfe_mdc_clk", vfe_mdc_clk, NULL), + OWN(APPS1, 8, "vfe_pclk", vfe_p_clk, NULL), + OWN(APPS1, 8, "iface_clk", vfe_p_clk, "footswitch-pcom.8"), + + OWN(APPS2, 0, "core_clk", grp_3d_clk, "kgsl-3d0.0"), + OWN(APPS2, 0, "core_clk", grp_3d_clk, "footswitch-pcom.2"), + OWN(APPS2, 0, "iface_clk", grp_3d_p_clk, "kgsl-3d0.0"), + OWN(APPS2, 0, "iface_clk", grp_3d_p_clk, "footswitch-pcom.2"), + { CLK_LOOKUP("src_clk", grp_3d_src_clk.c, "kgsl-3d0.0"), + O(APPS2), BIT(0), &p_grp_3d_clk.c }, + { CLK_LOOKUP("src_clk", grp_3d_src_clk.c, "footswitch-pcom.2"), + O(APPS2), BIT(0), &p_grp_3d_clk.c }, + OWN(APPS2, 0, "mem_clk", imem_clk, "kgsl-3d0.0"), + OWN(APPS2, 4, "lcdc_clk", mdp_lcdc_pad_pclk_clk, "lcdc.0"), + OWN(APPS2, 4, "mdp_clk", mdp_lcdc_pclk_clk, "lcdc.0"), + OWN(APPS2, 4, "iface_clk", mdp_p_clk, "mdp.0"), + OWN(APPS2, 4, "iface_clk", mdp_p_clk, "footswitch-pcom.4"), + OWN(APPS2, 28, "vsync_clk", mdp_vsync_clk, "mdp.0"), + OWN(APPS2, 5, "ref_clk", tsif_ref_clk, "msm_tsif.0"), + OWN(APPS2, 5, "iface_clk", tsif_p_clk, "msm_tsif.0"), + { CLK_LOOKUP("src_clk", tv_clk.c, "dtv.0"), + O(APPS2), BIT(2), &p_tv_enc_clk.c }, + OWN(APPS2, 2, "tv_dac_clk", tv_dac_clk, NULL), + OWN(APPS2, 2, "tv_enc_clk", tv_enc_clk, NULL), + + OWN(ROW1, 7, "core_clk", emdh_clk, "msm_mddi.1"), + OWN(ROW1, 7, "iface_clk", emdh_p_clk, "msm_mddi.1"), + OWN(ROW1, 11, "core_clk", i2c_clk, "msm_i2c.0"), + OWN(ROW1, 12, "core_clk", i2c_2_clk, "msm_i2c.2"), + OWN(ROW1, 17, "mdc_clk", mdc_clk, NULL), + OWN(ROW1, 19, "core_clk", pmdh_clk, "mddi.0"), + OWN(ROW1, 19, "iface_clk", pmdh_p_clk, "mddi.0"), + OWN(ROW1, 23, "core_clk", sdc1_clk, "msm_sdcc.1"), + OWN(ROW1, 23, "iface_clk", sdc1_p_clk, "msm_sdcc.1"), + OWN(ROW1, 25, "core_clk", sdc2_clk, "msm_sdcc.2"), + OWN(ROW1, 25, "iface_clk", sdc2_p_clk, "msm_sdcc.2"), + OWN(ROW1, 27, "core_clk", sdc3_clk, "msm_sdcc.3"), + OWN(ROW1, 27, "iface_clk", sdc3_p_clk, "msm_sdcc.3"), + OWN(ROW1, 29, "core_clk", sdc4_clk, "msm_sdcc.4"), + OWN(ROW1, 29, "iface_clk", sdc4_p_clk, "msm_sdcc.4"), + OWN(ROW1, 0, "core_clk", uart2_clk, "msm_serial.1"), + OWN(ROW1, 2, "alt_core_clk", usb_hs2_clk, "msm_hsusb_host.0"), + OWN(ROW1, 2, "core_clk", usb_hs2_core_clk, "msm_hsusb_host.0"), + OWN(ROW1, 2, "iface_clk", usb_hs2_p_clk, "msm_hsusb_host.0"), + OWN(ROW1, 4, "alt_core_clk", usb_hs3_clk, ""), + OWN(ROW1, 4, "core_clk", usb_hs3_core_clk, ""), + OWN(ROW1, 4, "iface_clk", usb_hs3_p_clk, ""), + + OWN(ROW2, 3, "core_clk", qup_i2c_clk, "qup_i2c.4"), + OWN(ROW2, 1, "core_clk", spi_clk, "spi_qsd.0"), + OWN(ROW2, 1, "iface_clk", spi_p_clk, "spi_qsd.0"), + OWN(ROW2, 9, "core_clk", uart1_clk, "msm_serial.0"), + OWN(ROW2, 6, "core_clk", uart1dm_clk, "msm_serial_hs.0"), + OWN(ROW2, 8, "core_clk", uart2dm_clk, "msm_serial_hs.1"), + OWN(ROW2, 11, "alt_core_clk", usb_hs_clk, "msm_otg"), + OWN(ROW2, 11, "core_clk", usb_hs_core_clk, "msm_otg"), + OWN(ROW2, 11, "iface_clk", usb_hs_p_clk, "msm_otg"), + + OWN(APPS3, 6, "cam_m_clk", cam_m_clk, NULL), + OWN(APPS3, 6, "cam_clk", cam_m_clk, "4-0020"), + OWN(APPS3, 6, "camif_pad_pclk", camif_pad_p_clk, NULL), + OWN(APPS3, 6, "iface_clk", camif_pad_p_clk, "qup_i2c.4"), + OWN(APPS3, 11, "csi_clk", csi0_clk, NULL), + OWN(APPS3, 11, "csi_vfe_clk", csi0_vfe_clk, NULL), + OWN(APPS3, 11, "csi_pclk", csi0_p_clk, NULL), + OWN(APPS3, 0, "core_clk", mdp_clk, "mdp.0"), + OWN(APPS3, 0, "core_clk", mdp_clk, "footswitch-pcom.4"), + OWN(APPS3, 2, "core_clk", mfc_clk, "msm_vidc.0"), + OWN(APPS3, 2, "core_clk", mfc_clk, "footswitch-pcom.5"), + OWN(APPS3, 2, "core_div2_clk", mfc_div2_clk, "msm_vidc.0"), + OWN(APPS3, 2, "iface_clk", mfc_p_clk, "msm_vidc.0"), + OWN(APPS3, 2, "iface_clk", mfc_p_clk, "footswitch-pcom.5"), + OWN(APPS3, 4, "vpe_clk", vpe_clk, NULL), + OWN(APPS3, 4, "core_clk", vpe_clk, "footswitch-pcom.9"), + + OWN(GLBL, 8, "core_clk", adm_clk, "msm_dmov"), + { CLK_LOOKUP("iface_clk", adm_p_clk.c, "msm_dmov"), + O(GLBL), BIT(13), &dummy_clk }, + OWN(GLBL, 8, "core_clk", ce_clk, "qce.0"), + OWN(GLBL, 8, "core_clk", ce_clk, "crypto.0"), + OWN(GLBL, 13, "core_clk", axi_rotator_clk, "msm_rotator.0"), + OWN(GLBL, 13, "core_clk", axi_rotator_clk, "footswitch-pcom.6"), + OWN(GLBL, 13, "mem_clk", rotator_imem_clk, "msm_rotator.0"), + OWN(GLBL, 13, "iface_clk", rotator_p_clk, "msm_rotator.0"), + OWN(GLBL, 13, "iface_clk", rotator_p_clk, "footswitch-pcom.6"), + { CLK_LOOKUP("iface_clk", uart1dm_p_clk.c, "msm_serial_hs.0"), + O(GLBL), BIT(8), &dummy_clk }, + { CLK_LOOKUP("iface_clk", uart2dm_p_clk.c, "msm_serial_hs.1"), + O(GLBL), BIT(8), &dummy_clk }, +}; + +static struct clk_lookup msm_clocks_7x30[ARRAY_SIZE(ownership_map)]; + +static void __init set_clock_ownership(void) +{ + unsigned i; + struct clk_lookup *lk; + + for (i = 0; i < ARRAY_SIZE(ownership_map); i++) { + const u32 *reg = ownership_map[i].reg; + u32 bit = ownership_map[i].bit; + struct clk *remote = ownership_map[i].remote; + + lk = &ownership_map[i].lk; + memcpy(&msm_clocks_7x30[i], lk, sizeof(*lk)); + + if (reg && !(*reg & bit)) + msm_clocks_7x30[i].clk = remote; + } +} + +/* + * Miscellaneous clock register initializations + */ +static const struct reg_init { + const void __iomem *reg; + uint32_t mask; + uint32_t val; +} ri_list[] __initconst = { + /* Enable UMDX_P clock. Known to causes issues, so never turn off. */ + {GLBL_CLK_ENA_2_SC_REG, BIT(2), BIT(2)}, + + /* Disable all the child clocks of USB_HS_SRC. */ + { USBH_NS_REG, BIT(13) | BIT(9), 0 }, + { USBH2_NS_REG, BIT(9) | BIT(4), 0 }, + { USBH3_NS_REG, BIT(9) | BIT(4), 0 }, + + {EMDH_NS_REG, BM(18, 17) , BVAL(18, 17, 0x3)}, /* RX div = div-4. */ + {PMDH_NS_REG, BM(18, 17), BVAL(18, 17, 0x3)}, /* RX div = div-4. */ + /* MI2S_CODEC_RX_S src = MI2S_CODEC_RX_M. */ + {MI2S_RX_NS_REG, BIT(14), 0x0}, + /* MI2S_CODEC_TX_S src = MI2S_CODEC_TX_M. */ + {MI2S_TX_NS_REG, BIT(14), 0x0}, + {MI2S_NS_REG, BIT(14), 0x0}, /* MI2S_S src = MI2S_M. */ + /* Allow DSP to decide the LPA CORE src. */ + {LPA_CORE_CLK_MA0_REG, BIT(0), BIT(0)}, + {LPA_CORE_CLK_MA2_REG, BIT(0), BIT(0)}, + {MI2S_CODEC_RX_DIV_REG, 0xF, 0xD}, /* MI2S_CODEC_RX_S div = div-8. */ + {MI2S_CODEC_TX_DIV_REG, 0xF, 0xD}, /* MI2S_CODEC_TX_S div = div-8. */ + {MI2S_DIV_REG, 0xF, 0x7}, /* MI2S_S div = div-8. */ + {MDC_NS_REG, 0x3, 0x3}, /* MDC src = external MDH src. */ + {SDAC_NS_REG, BM(15, 14), 0x0}, /* SDAC div = div-1. */ + /* Disable sources TCXO/5 & TCXO/6. UART1 src = TCXO*/ + {UART_NS_REG, BM(26, 25) | BM(2, 0), 0x0}, + /* HDMI div = div-1, non-inverted. tv_enc_src = tv_clk_src */ + {HDMI_NS_REG, 0x7, 0x0}, + {TV_NS_REG, BM(15, 14), 0x0}, /* tv_clk_src_div2 = div-1 */ + + /* USBH core clocks src = USB_HS_SRC. */ + {USBH_NS_REG, BIT(15), BIT(15)}, + {USBH2_NS_REG, BIT(6), BIT(6)}, + {USBH3_NS_REG, BIT(6), BIT(6)}, +}; + +static void __init msm7x30_clock_pre_init(void) +{ + int i; + uint32_t val; + + clk_ops_branch.reset = soc_branch_clk_reset; + clk_ops_rcg.reset = msm7x30_clk_reset; + clk_ops_rcg.set_flags = soc_clk_set_flags; + + cache_ownership(); + print_ownership(); + set_clock_ownership(); + + /* When we have no local clock control, the rest of the code in this + * function is a NOP since writes to shadow regions that we don't own + * are ignored. */ + + for (i = 0; i < ARRAY_SIZE(ri_list); i++) { + val = readl_relaxed(ri_list[i].reg); + val &= ~ri_list[i].mask; + val |= ri_list[i].val; + writel_relaxed(val, ri_list[i].reg); + } +} + +static void __init msm7x30_clock_post_init(void) +{ + clk_set_rate(&usb_hs_src_clk.c, 60000000); + clk_set_rate(&i2c_clk.c, 19200000); + clk_set_rate(&i2c_2_clk.c, 19200000); + clk_set_rate(&qup_i2c_clk.c, 19200000); + clk_set_rate(&uart1_clk.c, 19200000); + clk_set_rate(&uart2_clk.c, 19200000); + clk_set_rate(&mi2s_m_clk.c, 12288000); + clk_set_rate(&mdp_vsync_clk.c, 24576000); + clk_set_rate(&glbl_root_clk.c, 1); + clk_set_rate(&mdc_clk.c, 1); + /* Sync the LPA_CODEC clock to MI2S_CODEC_RX */ + clk_set_rate(&lpa_codec_clk.c, 1); + /* Sync the GRP2D clock to AXI */ + clk_set_rate(&grp_2d_clk.c, 1); +} + +struct clock_init_data msm7x30_clock_init_data __initdata = { + .table = msm_clocks_7x30, + .size = ARRAY_SIZE(msm_clocks_7x30), + .pre_init = msm7x30_clock_pre_init, + .post_init = msm7x30_clock_post_init, +}; diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c new file mode 100644 index 00000000000..c4ada1ebfcc --- /dev/null +++ b/arch/arm/mach-msm/clock-8960.c @@ -0,0 +1,6398 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "clock-local.h" +#include "clock-rpm.h" +#include "clock-voter.h" +#include "clock-dss-8960.h" +#include "devices.h" +#include "clock-pll.h" + +#define REG(off) (MSM_CLK_CTL_BASE + (off)) +#define REG_MM(off) (MSM_MMSS_CLK_CTL_BASE + (off)) +#define REG_LPA(off) (MSM_LPASS_CLK_CTL_BASE + (off)) +#define REG_GCC(off) (MSM_APCS_GCC_BASE + (off)) + +/* Peripheral clock registers. */ +#define ADM0_PBUS_CLK_CTL_REG REG(0x2208) +#define SFAB_SATA_S_HCLK_CTL_REG REG(0x2480) +#define CE1_HCLK_CTL_REG REG(0x2720) +#define CE1_CORE_CLK_CTL_REG REG(0x2724) +#define PRNG_CLK_NS_REG REG(0x2E80) +#define CE3_HCLK_CTL_REG REG(0x36C4) +#define CE3_CORE_CLK_CTL_REG REG(0x36CC) +#define CE3_CLK_SRC_NS_REG REG(0x36C0) +#define DMA_BAM_HCLK_CTL REG(0x25C0) +#define CLK_HALT_AFAB_SFAB_STATEA_REG REG(0x2FC0) +#define CLK_HALT_AFAB_SFAB_STATEB_REG REG(0x2FC4) +#define CLK_HALT_CFPB_STATEA_REG REG(0x2FCC) +#define CLK_HALT_CFPB_STATEB_REG REG(0x2FD0) +#define CLK_HALT_CFPB_STATEC_REG REG(0x2FD4) +#define CLK_HALT_DFAB_STATE_REG REG(0x2FC8) +/* 8064 name CLK_HALT_GSS_KPSS_MISC_STATE_REG */ +#define CLK_HALT_MSS_SMPSS_MISC_STATE_REG REG(0x2FDC) +#define CLK_HALT_SFPB_MISC_STATE_REG REG(0x2FD8) +#define CLK_HALT_AFAB_SFAB_STATEB_REG REG(0x2FC4) +#define CLK_TEST_REG REG(0x2FA0) +#define GPn_MD_REG(n) REG(0x2D00+(0x20*(n))) +#define GPn_NS_REG(n) REG(0x2D24+(0x20*(n))) +#define GSBIn_HCLK_CTL_REG(n) REG(0x29C0+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_MD_REG(n) REG(0x29C8+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_NS_REG(n) REG(0x29CC+(0x20*((n)-1))) +#define GSBIn_RESET_REG(n) REG(0x29DC+(0x20*((n)-1))) +#define GSBIn_UART_APPS_MD_REG(n) REG(0x29D0+(0x20*((n)-1))) +#define GSBIn_UART_APPS_NS_REG(n) REG(0x29D4+(0x20*((n)-1))) +#define PDM_CLK_NS_REG REG(0x2CC0) +/* 8064 name BB_PLL_ENA_APCS_REG */ +#define BB_PLL_ENA_SC0_REG REG(0x34C0) +#define BB_PLL_ENA_RPM_REG REG(0x34A0) +#define BB_PLL0_STATUS_REG REG(0x30D8) +#define BB_PLL5_STATUS_REG REG(0x30F8) +#define BB_PLL6_STATUS_REG REG(0x3118) +#define BB_PLL7_STATUS_REG REG(0x3138) +#define BB_PLL8_L_VAL_REG REG(0x3144) +#define BB_PLL8_M_VAL_REG REG(0x3148) +#define BB_PLL8_MODE_REG REG(0x3140) +#define BB_PLL8_N_VAL_REG REG(0x314C) +#define BB_PLL8_STATUS_REG REG(0x3158) +#define BB_PLL8_CONFIG_REG REG(0x3154) +#define BB_PLL8_TEST_CTL_REG REG(0x3150) +#define BB_MMCC_PLL2_MODE_REG REG(0x3160) +#define BB_MMCC_PLL2_TEST_CTL_REG REG(0x3170) +#define BB_PLL14_MODE_REG REG(0x31C0) +#define BB_PLL14_L_VAL_REG REG(0x31C4) +#define BB_PLL14_M_VAL_REG REG(0x31C8) +#define BB_PLL14_N_VAL_REG REG(0x31CC) +#define BB_PLL14_TEST_CTL_REG REG(0x31D0) +#define BB_PLL14_CONFIG_REG REG(0x31D4) +#define BB_PLL14_STATUS_REG REG(0x31D8) +#define PLLTEST_PAD_CFG_REG REG(0x2FA4) +#define PMEM_ACLK_CTL_REG REG(0x25A0) +#define RINGOSC_NS_REG REG(0x2DC0) +#define RINGOSC_STATUS_REG REG(0x2DCC) +#define RINGOSC_TCXO_CTL_REG REG(0x2DC4) +#define RPM_MSG_RAM_HCLK_CTL_REG REG(0x27E0) +#define SC0_U_CLK_BRANCH_ENA_VOTE_REG REG(0x3080) +#define SDCn_APPS_CLK_MD_REG(n) REG(0x2828+(0x20*((n)-1))) +#define SDCn_APPS_CLK_NS_REG(n) REG(0x282C+(0x20*((n)-1))) +#define SDCn_HCLK_CTL_REG(n) REG(0x2820+(0x20*((n)-1))) +#define SDCn_RESET_REG(n) REG(0x2830+(0x20*((n)-1))) +#define SLIMBUS_XO_SRC_CLK_CTL_REG REG(0x2628) +#define TSIF_HCLK_CTL_REG REG(0x2700) +#define TSIF_REF_CLK_MD_REG REG(0x270C) +#define TSIF_REF_CLK_NS_REG REG(0x2710) +#define TSSC_CLK_CTL_REG REG(0x2CA0) +#define SATA_HCLK_CTL_REG REG(0x2C00) +#define SATA_CLK_SRC_NS_REG REG(0x2C08) +#define SATA_RXOOB_CLK_CTL_REG REG(0x2C0C) +#define SATA_PMALIVE_CLK_CTL_REG REG(0x2C10) +#define SATA_PHY_REF_CLK_CTL_REG REG(0x2C14) +#define SATA_ACLK_CTL_REG REG(0x2C20) +#define SATA_PHY_CFG_CLK_CTL_REG REG(0x2C40) +#define USB_FSn_HCLK_CTL_REG(n) REG(0x2960+(0x20*((n)-1))) +#define USB_FSn_RESET_REG(n) REG(0x2974+(0x20*((n)-1))) +#define USB_FSn_SYSTEM_CLK_CTL_REG(n) REG(0x296C+(0x20*((n)-1))) +#define USB_FSn_XCVR_FS_CLK_MD_REG(n) REG(0x2964+(0x20*((n)-1))) +#define USB_FSn_XCVR_FS_CLK_NS_REG(n) REG(0x2968+(0x20*((n)-1))) +#define USB_HS1_HCLK_CTL_REG REG(0x2900) +#define USB_HS1_HCLK_FS_REG REG(0x2904) +#define USB_HS1_RESET_REG REG(0x2910) +#define USB_HS1_XCVR_FS_CLK_MD_REG REG(0x2908) +#define USB_HS1_XCVR_FS_CLK_NS_REG REG(0x290C) +#define USB_HS3_HCLK_CTL_REG REG(0x3700) +#define USB_HS3_HCLK_FS_REG REG(0x3704) +#define USB_HS3_RESET_REG REG(0x3710) +#define USB_HS3_XCVR_FS_CLK_MD_REG REG(0X3708) +#define USB_HS3_XCVR_FS_CLK_NS_REG REG(0X370C) +#define USB_HS4_HCLK_CTL_REG REG(0x3720) +#define USB_HS4_HCLK_FS_REG REG(0x3724) +#define USB_HS4_RESET_REG REG(0x3730) +#define USB_HS4_XCVR_FS_CLK_MD_REG REG(0X3728) +#define USB_HS4_XCVR_FS_CLK_NS_REG REG(0X372C) +#define USB_HSIC_HCLK_CTL_REG REG(0x2920) +#define USB_HSIC_HSIC_CLK_CTL_REG REG(0x2B44) +#define USB_HSIC_HSIC_CLK_SRC_CTL_REG REG(0x2B40) +#define USB_HSIC_HSIO_CAL_CLK_CTL_REG REG(0x2B48) +#define USB_HSIC_RESET_REG REG(0x2934) +#define USB_HSIC_SYSTEM_CLK_CTL_REG REG(0x292C) +#define USB_HSIC_XCVR_FS_CLK_MD_REG REG(0x2924) +#define USB_HSIC_XCVR_FS_CLK_NS_REG REG(0x2928) +#define USB_PHY0_RESET_REG REG(0x2E20) +#define PCIE_ALT_REF_CLK_NS_REG REG(0x3860) +#define PCIE_ACLK_CTL_REG REG(0x22C0) +#define PCIE_HCLK_CTL_REG REG(0x22CC) +#define PCIE_PCLK_CTL_REG REG(0x22D0) +#define GPLL1_MODE_REG REG(0x3160) +#define GPLL1_L_VAL_REG REG(0x3164) +#define GPLL1_M_VAL_REG REG(0x3168) +#define GPLL1_N_VAL_REG REG(0x316C) +#define GPLL1_CONFIG_REG REG(0x3174) +#define GPLL1_STATUS_REG REG(0x3178) +#define PXO_SRC_CLK_CTL_REG REG(0x2EA0) + +/* Multimedia clock registers. */ +#define AHB_EN_REG REG_MM(0x0008) +#define AHB_EN2_REG REG_MM(0x0038) +#define AHB_EN3_REG REG_MM(0x0248) +#define AHB_NS_REG REG_MM(0x0004) +#define AXI_NS_REG REG_MM(0x0014) +#define CAMCLK0_NS_REG REG_MM(0x0148) +#define CAMCLK0_CC_REG REG_MM(0x0140) +#define CAMCLK0_MD_REG REG_MM(0x0144) +#define CAMCLK1_NS_REG REG_MM(0x015C) +#define CAMCLK1_CC_REG REG_MM(0x0154) +#define CAMCLK1_MD_REG REG_MM(0x0158) +#define CAMCLK2_NS_REG REG_MM(0x0228) +#define CAMCLK2_CC_REG REG_MM(0x0220) +#define CAMCLK2_MD_REG REG_MM(0x0224) +#define CSI0_NS_REG REG_MM(0x0048) +#define CSI0_CC_REG REG_MM(0x0040) +#define CSI0_MD_REG REG_MM(0x0044) +#define CSI1_NS_REG REG_MM(0x0010) +#define CSI1_CC_REG REG_MM(0x0024) +#define CSI1_MD_REG REG_MM(0x0028) +#define CSI2_NS_REG REG_MM(0x0234) +#define CSI2_CC_REG REG_MM(0x022C) +#define CSI2_MD_REG REG_MM(0x0230) +#define CSIPHYTIMER_CC_REG REG_MM(0x0160) +#define CSIPHYTIMER_MD_REG REG_MM(0x0164) +#define CSIPHYTIMER_NS_REG REG_MM(0x0168) +#define DSI1_BYTE_NS_REG REG_MM(0x00B0) +#define DSI1_BYTE_CC_REG REG_MM(0x0090) +#define DSI2_BYTE_NS_REG REG_MM(0x00BC) +#define DSI2_BYTE_CC_REG REG_MM(0x00B4) +#define DSI1_ESC_NS_REG REG_MM(0x011C) +#define DSI1_ESC_CC_REG REG_MM(0x00CC) +#define DSI2_ESC_NS_REG REG_MM(0x0150) +#define DSI2_ESC_CC_REG REG_MM(0x013C) +#define DSI_PIXEL_CC_REG REG_MM(0x0130) +#define DSI2_PIXEL_CC_REG REG_MM(0x0094) +#define DBG_BUS_VEC_A_REG REG_MM(0x01C8) +#define DBG_BUS_VEC_B_REG REG_MM(0x01CC) +#define DBG_BUS_VEC_C_REG REG_MM(0x01D0) +#define DBG_BUS_VEC_D_REG REG_MM(0x01D4) +#define DBG_BUS_VEC_E_REG REG_MM(0x01D8) +#define DBG_BUS_VEC_F_REG REG_MM(0x01DC) +#define DBG_BUS_VEC_G_REG REG_MM(0x01E0) +#define DBG_BUS_VEC_H_REG REG_MM(0x01E4) +#define DBG_BUS_VEC_I_REG REG_MM(0x01E8) +#define DBG_BUS_VEC_J_REG REG_MM(0x0240) +#define DBG_CFG_REG_HS_REG REG_MM(0x01B4) +#define DBG_CFG_REG_LS_REG REG_MM(0x01B8) +#define GFX2D0_CC_REG REG_MM(0x0060) +#define GFX2D0_MD0_REG REG_MM(0x0064) +#define GFX2D0_MD1_REG REG_MM(0x0068) +#define GFX2D0_NS_REG REG_MM(0x0070) +#define GFX2D1_CC_REG REG_MM(0x0074) +#define GFX2D1_MD0_REG REG_MM(0x0078) +#define GFX2D1_MD1_REG REG_MM(0x006C) +#define GFX2D1_NS_REG REG_MM(0x007C) +#define GFX3D_CC_REG REG_MM(0x0080) +#define GFX3D_MD0_REG REG_MM(0x0084) +#define GFX3D_MD1_REG REG_MM(0x0088) +#define GFX3D_NS_REG REG_MM(0x008C) +#define IJPEG_CC_REG REG_MM(0x0098) +#define IJPEG_MD_REG REG_MM(0x009C) +#define IJPEG_NS_REG REG_MM(0x00A0) +#define JPEGD_CC_REG REG_MM(0x00A4) +#define JPEGD_NS_REG REG_MM(0x00AC) +#define VCAP_CC_REG REG_MM(0x0178) +#define VCAP_NS_REG REG_MM(0x021C) +#define VCAP_MD0_REG REG_MM(0x01EC) +#define VCAP_MD1_REG REG_MM(0x0218) +#define MAXI_EN_REG REG_MM(0x0018) +#define MAXI_EN2_REG REG_MM(0x0020) +#define MAXI_EN3_REG REG_MM(0x002C) +#define MAXI_EN4_REG REG_MM(0x0114) +#define MAXI_EN5_REG REG_MM(0x0244) +#define MDP_CC_REG REG_MM(0x00C0) +#define MDP_LUT_CC_REG REG_MM(0x016C) +#define MDP_MD0_REG REG_MM(0x00C4) +#define MDP_MD1_REG REG_MM(0x00C8) +#define MDP_NS_REG REG_MM(0x00D0) +#define MISC_CC_REG REG_MM(0x0058) +#define MISC_CC2_REG REG_MM(0x005C) +#define MISC_CC3_REG REG_MM(0x0238) +#define MM_PLL1_MODE_REG REG_MM(0x031C) +#define MM_PLL1_L_VAL_REG REG_MM(0x0320) +#define MM_PLL1_M_VAL_REG REG_MM(0x0324) +#define MM_PLL1_N_VAL_REG REG_MM(0x0328) +#define MM_PLL1_CONFIG_REG REG_MM(0x032C) +#define MM_PLL1_TEST_CTL_REG REG_MM(0x0330) +#define MM_PLL1_STATUS_REG REG_MM(0x0334) +#define MM_PLL3_MODE_REG REG_MM(0x0338) +#define MM_PLL3_L_VAL_REG REG_MM(0x033C) +#define MM_PLL3_M_VAL_REG REG_MM(0x0340) +#define MM_PLL3_N_VAL_REG REG_MM(0x0344) +#define MM_PLL3_CONFIG_REG REG_MM(0x0348) +#define MM_PLL3_TEST_CTL_REG REG_MM(0x034C) +#define MM_PLL3_STATUS_REG REG_MM(0x0350) +#define ROT_CC_REG REG_MM(0x00E0) +#define ROT_NS_REG REG_MM(0x00E8) +#define SAXI_EN_REG REG_MM(0x0030) +#define SW_RESET_AHB_REG REG_MM(0x020C) +#define SW_RESET_AHB2_REG REG_MM(0x0200) +#define SW_RESET_ALL_REG REG_MM(0x0204) +#define SW_RESET_AXI_REG REG_MM(0x0208) +#define SW_RESET_CORE_REG REG_MM(0x0210) +#define SW_RESET_CORE2_REG REG_MM(0x0214) +#define TV_CC_REG REG_MM(0x00EC) +#define TV_CC2_REG REG_MM(0x0124) +#define TV_MD_REG REG_MM(0x00F0) +#define TV_NS_REG REG_MM(0x00F4) +#define VCODEC_CC_REG REG_MM(0x00F8) +#define VCODEC_MD0_REG REG_MM(0x00FC) +#define VCODEC_MD1_REG REG_MM(0x0128) +#define VCODEC_NS_REG REG_MM(0x0100) +#define VFE_CC_REG REG_MM(0x0104) +#define VFE_MD_REG REG_MM(0x0108) +#define VFE_NS_REG REG_MM(0x010C) +#define VFE_CC2_REG REG_MM(0x023C) +#define VPE_CC_REG REG_MM(0x0110) +#define VPE_NS_REG REG_MM(0x0118) + +/* Low-power Audio clock registers. */ +#define LCC_CLK_HS_DEBUG_CFG_REG REG_LPA(0x00A4) +#define LCC_CLK_LS_DEBUG_CFG_REG REG_LPA(0x00A8) +#define LCC_CODEC_I2S_MIC_MD_REG REG_LPA(0x0064) +#define LCC_CODEC_I2S_MIC_NS_REG REG_LPA(0x0060) +#define LCC_CODEC_I2S_MIC_STATUS_REG REG_LPA(0x0068) +#define LCC_CODEC_I2S_SPKR_MD_REG REG_LPA(0x0070) +#define LCC_CODEC_I2S_SPKR_NS_REG REG_LPA(0x006C) +#define LCC_CODEC_I2S_SPKR_STATUS_REG REG_LPA(0x0074) +#define LCC_MI2S_MD_REG REG_LPA(0x004C) +#define LCC_MI2S_NS_REG REG_LPA(0x0048) +#define LCC_MI2S_STATUS_REG REG_LPA(0x0050) +#define LCC_PCM_MD_REG REG_LPA(0x0058) +#define LCC_PCM_NS_REG REG_LPA(0x0054) +#define LCC_PCM_STATUS_REG REG_LPA(0x005C) +#define LCC_PLL0_MODE_REG REG_LPA(0x0000) +#define LCC_PLL0_L_VAL_REG REG_LPA(0x0004) +#define LCC_PLL0_M_VAL_REG REG_LPA(0x0008) +#define LCC_PLL0_N_VAL_REG REG_LPA(0x000C) +#define LCC_PLL0_CONFIG_REG REG_LPA(0x0014) +#define LCC_PLL0_STATUS_REG REG_LPA(0x0018) +#define LCC_SPARE_I2S_MIC_MD_REG REG_LPA(0x007C) +#define LCC_SPARE_I2S_MIC_NS_REG REG_LPA(0x0078) +#define LCC_SPARE_I2S_MIC_STATUS_REG REG_LPA(0x0080) +#define LCC_SPARE_I2S_SPKR_MD_REG REG_LPA(0x0088) +#define LCC_SPARE_I2S_SPKR_NS_REG REG_LPA(0x0084) +#define LCC_SPARE_I2S_SPKR_STATUS_REG REG_LPA(0x008C) +#define LCC_SLIMBUS_NS_REG REG_LPA(0x00CC) +#define LCC_SLIMBUS_MD_REG REG_LPA(0x00D0) +#define LCC_SLIMBUS_STATUS_REG REG_LPA(0x00D4) +#define LCC_AHBEX_BRANCH_CTL_REG REG_LPA(0x00E4) +#define LCC_PRI_PLL_CLK_CTL_REG REG_LPA(0x00C4) + +#define GCC_APCS_CLK_DIAG REG_GCC(0x001C) + +/* MUX source input identifiers. */ +#define pxo_to_bb_mux 0 +#define cxo_to_bb_mux 5 +#define pll0_to_bb_mux 2 +#define pll8_to_bb_mux 3 +#define pll6_to_bb_mux 4 +#define gnd_to_bb_mux 5 +#define pll3_to_bb_mux 6 +#define pxo_to_mm_mux 0 +#define pll1_to_mm_mux 1 +#define pll2_to_mm_mux 1 /* or MMCC_PLL1 */ +#define pll8_to_mm_mux 2 /* or GCC_PERF */ +#define pll0_to_mm_mux 3 +#define pll15_to_mm_mux 3 /* or MM_PLL3 */ +#define gnd_to_mm_mux 4 +#define pll3_to_mm_mux 3 /* or MMCC_PLL2 */ +#define hdmi_pll_to_mm_mux 3 +#define cxo_to_xo_mux 0 +#define pxo_to_xo_mux 1 +#define gnd_to_xo_mux 3 +#define pxo_to_lpa_mux 0 +#define cxo_to_lpa_mux 1 +#define pll4_to_lpa_mux 2 +#define gnd_to_lpa_mux 6 +#define pxo_to_pcie_mux 0 +#define pll3_to_pcie_mux 1 + +/* Test Vector Macros */ +#define TEST_TYPE_PER_LS 1 +#define TEST_TYPE_PER_HS 2 +#define TEST_TYPE_MM_LS 3 +#define TEST_TYPE_MM_HS 4 +#define TEST_TYPE_LPA 5 +#define TEST_TYPE_CPUL2 6 +#define TEST_TYPE_LPA_HS 7 +#define TEST_TYPE_SHIFT 24 +#define TEST_CLK_SEL_MASK BM(23, 0) +#define TEST_VECTOR(s, t) (((t) << TEST_TYPE_SHIFT) | BVAL(23, 0, (s))) +#define TEST_PER_LS(s) TEST_VECTOR((s), TEST_TYPE_PER_LS) +#define TEST_PER_HS(s) TEST_VECTOR((s), TEST_TYPE_PER_HS) +#define TEST_MM_LS(s) TEST_VECTOR((s), TEST_TYPE_MM_LS) +#define TEST_MM_HS(s) TEST_VECTOR((s), TEST_TYPE_MM_HS) +#define TEST_LPA(s) TEST_VECTOR((s), TEST_TYPE_LPA) +#define TEST_LPA_HS(s) TEST_VECTOR((s), TEST_TYPE_LPA_HS) +#define TEST_CPUL2(s) TEST_VECTOR((s), TEST_TYPE_CPUL2) + +#define MN_MODE_DUAL_EDGE 0x2 + +struct pll_rate { + const uint32_t l_val; + const uint32_t m_val; + const uint32_t n_val; + const uint32_t vco; + const uint32_t post_div; + const uint32_t i_bits; +}; +#define PLL_RATE(l, m, n, v, d, i) { l, m, n, v, (d>>1), i } + +enum vdd_dig_levels { + VDD_DIG_NONE, + VDD_DIG_LOW, + VDD_DIG_NOMINAL, + VDD_DIG_HIGH +}; + +static int set_vdd_dig_8960(struct clk_vdd_class *vdd_class, int level) +{ + static const int vdd_uv[] = { + [VDD_DIG_NONE] = 0, + [VDD_DIG_LOW] = 945000, + [VDD_DIG_NOMINAL] = 1050000, + [VDD_DIG_HIGH] = 1150000 + }; + return rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_S3, RPM_VREG_VOTER3, + vdd_uv[level], 1150000, 1); +} + +static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960); + +static int set_vdd_dig_8930(struct clk_vdd_class *vdd_class, int level) +{ + static const int vdd_corner[] = { + [VDD_DIG_NONE] = RPM_VREG_CORNER_NONE, + [VDD_DIG_LOW] = RPM_VREG_CORNER_LOW, + [VDD_DIG_NOMINAL] = RPM_VREG_CORNER_NOMINAL, + [VDD_DIG_HIGH] = RPM_VREG_CORNER_HIGH, + }; + return rpm_vreg_set_voltage(RPM_VREG_ID_PM8038_VDD_DIG_CORNER, + RPM_VREG_VOTER3, + vdd_corner[level], + RPM_VREG_CORNER_HIGH, 1); +} + +#define VDD_DIG_FMAX_MAP1(l1, f1) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1) +#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2) +#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2), \ + .fmax[VDD_DIG_##l3] = (f3) + +enum vdd_sr2_pll_levels { + VDD_SR2_PLL_OFF, + VDD_SR2_PLL_ON +}; + +static int set_vdd_sr2_pll_8960(struct clk_vdd_class *vdd_class, int level) +{ + int rc = 0; + + if (level == VDD_SR2_PLL_OFF) { + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_L23, + RPM_VREG_VOTER3, 0, 0, 1); + if (rc) + return rc; + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_S8, + RPM_VREG_VOTER3, 0, 0, 1); + if (rc) + rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_L23, + RPM_VREG_VOTER3, 1800000, 1800000, 1); + } else { + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_S8, + RPM_VREG_VOTER3, 2050000, 2100000, 1); + if (rc) + return rc; + rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_L23, + RPM_VREG_VOTER3, 1800000, 1800000, 1); + if (rc) + rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_S8, + RPM_VREG_VOTER3, 0, 0, 1); + } + + return rc; +} + +static DEFINE_VDD_CLASS(vdd_sr2_pll, set_vdd_sr2_pll_8960); + +static int sr2_lreg_uv[] = { + [VDD_SR2_PLL_OFF] = 0, + [VDD_SR2_PLL_ON] = 1800000, +}; + +static int set_vdd_sr2_pll_8064(struct clk_vdd_class *vdd_class, int level) +{ + return rpm_vreg_set_voltage(RPM_VREG_ID_PM8921_LVS7, RPM_VREG_VOTER3, + sr2_lreg_uv[level], sr2_lreg_uv[level], 1); +} + +static int set_vdd_sr2_pll_8930(struct clk_vdd_class *vdd_class, int level) +{ + return rpm_vreg_set_voltage(RPM_VREG_ID_PM8038_L23, RPM_VREG_VOTER3, + sr2_lreg_uv[level], sr2_lreg_uv[level], 1); +} + +/* + * Clock Descriptions + */ + +DEFINE_CLK_RPM_BRANCH(pxo_clk, pxo_a_clk, PXO, 27000000); +DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000); + +static struct pll_clk pll2_clk = { + .mode_reg = MM_PLL1_MODE_REG, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll2_clk", + .rate = 800000000, + .ops = &clk_ops_local_pll, + CLK_INIT(pll2_clk.c), + .warned = true, + }, +}; + +static struct pll_clk pll3_clk = { + .mode_reg = BB_MMCC_PLL2_MODE_REG, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll3_clk", + .rate = 1200000000, + .ops = &clk_ops_local_pll, + .vdd_class = &vdd_sr2_pll, + .fmax[VDD_SR2_PLL_ON] = ULONG_MAX, + CLK_INIT(pll3_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll4_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(4), + .status_reg = LCC_PLL0_STATUS_REG, + .status_mask = BIT(16), + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll4_clk", + .rate = 393216000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll4_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll8_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(8), + .status_reg = BB_PLL8_STATUS_REG, + .status_mask = BIT(16), + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll8_clk", + .rate = 384000000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll8_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll14_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(14), + .status_reg = BB_PLL14_STATUS_REG, + .status_mask = BIT(16), + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll14_clk", + .rate = 480000000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll14_clk.c), + .warned = true, + }, +}; + +static struct pll_clk pll15_clk = { + .mode_reg = MM_PLL3_MODE_REG, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll15_clk", + .rate = 975000000, + .ops = &clk_ops_local_pll, + CLK_INIT(pll15_clk.c), + .warned = true, + }, +}; + +/* AXI Interfaces */ +static struct branch_clk gmem_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(24), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 6, + .retain_reg = MAXI_EN2_REG, + .retain_mask = BIT(21), + }, + .c = { + .dbg_name = "gmem_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(gmem_axi_clk.c), + }, +}; + +static struct branch_clk ijpeg_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(21), + .hwcg_reg = MAXI_EN_REG, + .hwcg_mask = BIT(11), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 4, + }, + .c = { + .dbg_name = "ijpeg_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(ijpeg_axi_clk.c), + }, +}; + +static struct branch_clk imem_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(22), + .hwcg_reg = MAXI_EN_REG, + .hwcg_mask = BIT(15), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(10), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 7, + .retain_reg = MAXI_EN2_REG, + .retain_mask = BIT(10), + }, + .c = { + .dbg_name = "imem_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(imem_axi_clk.c), + }, +}; + +static struct branch_clk jpegd_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(25), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 5, + }, + .c = { + .dbg_name = "jpegd_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(jpegd_axi_clk.c), + }, +}; + +static struct branch_clk vcodec_axi_b_clk = { + .b = { + .ctl_reg = MAXI_EN4_REG, + .en_mask = BIT(23), + .hwcg_reg = MAXI_EN4_REG, + .hwcg_mask = BIT(22), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 25, + .retain_reg = MAXI_EN4_REG, + .retain_mask = BIT(21), + }, + .c = { + .dbg_name = "vcodec_axi_b_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcodec_axi_b_clk.c), + }, +}; + +static struct branch_clk vcodec_axi_a_clk = { + .b = { + .ctl_reg = MAXI_EN4_REG, + .en_mask = BIT(25), + .hwcg_reg = MAXI_EN4_REG, + .hwcg_mask = BIT(24), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 26, + .retain_reg = MAXI_EN4_REG, + .retain_mask = BIT(10), + }, + .c = { + .dbg_name = "vcodec_axi_a_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcodec_axi_a_clk.c), + .depends = &vcodec_axi_b_clk.c, + }, +}; + +static struct branch_clk vcodec_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(19), + .hwcg_reg = MAXI_EN_REG, + .hwcg_mask = BIT(13), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(4)|BIT(5)|BIT(7), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 3, + .retain_reg = MAXI_EN2_REG, + .retain_mask = BIT(28), + }, + .c = { + .dbg_name = "vcodec_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcodec_axi_clk.c), + .depends = &vcodec_axi_a_clk.c, + }, +}; + +static struct branch_clk vfe_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(18), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 0, + }, + .c = { + .dbg_name = "vfe_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_axi_clk.c), + }, +}; + +static struct branch_clk mdp_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(23), + .hwcg_reg = MAXI_EN_REG, + .hwcg_mask = BIT(16), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(13), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 8, + .retain_reg = MAXI_EN_REG, + .retain_mask = BIT(0), + }, + .c = { + .dbg_name = "mdp_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_axi_clk.c), + }, +}; + +static struct branch_clk rot_axi_clk = { + .b = { + .ctl_reg = MAXI_EN2_REG, + .en_mask = BIT(24), + .hwcg_reg = MAXI_EN2_REG, + .hwcg_mask = BIT(25), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(6), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 2, + .retain_reg = MAXI_EN3_REG, + .retain_mask = BIT(10), + }, + .c = { + .dbg_name = "rot_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(rot_axi_clk.c), + }, +}; + +static struct branch_clk vpe_axi_clk = { + .b = { + .ctl_reg = MAXI_EN2_REG, + .en_mask = BIT(26), + .hwcg_reg = MAXI_EN2_REG, + .hwcg_mask = BIT(27), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 1, + .retain_reg = MAXI_EN3_REG, + .retain_mask = BIT(21), + + }, + .c = { + .dbg_name = "vpe_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vpe_axi_clk.c), + }, +}; + +static struct branch_clk vcap_axi_clk = { + .b = { + .ctl_reg = MAXI_EN5_REG, + .en_mask = BIT(12), + .hwcg_reg = MAXI_EN5_REG, + .hwcg_mask = BIT(11), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(16), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 20, + }, + .c = { + .dbg_name = "vcap_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcap_axi_clk.c), + }, +}; + +/* gfx3d_axi_clk is set as a dependency of gmem_axi_clk at runtime */ +static struct branch_clk gfx3d_axi_clk_8064 = { + .b = { + .ctl_reg = MAXI_EN5_REG, + .en_mask = BIT(25), + .hwcg_reg = MAXI_EN5_REG, + .hwcg_mask = BIT(24), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(17), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 30, + }, + .c = { + .dbg_name = "gfx3d_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx3d_axi_clk_8064.c), + }, +}; + +static struct branch_clk gfx3d_axi_clk_8930 = { + .b = { + .ctl_reg = MAXI_EN5_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(16), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "gfx3d_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx3d_axi_clk_8930.c), + }, +}; + +/* AHB Interfaces */ +static struct branch_clk amp_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(24), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(20), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 18, + }, + .c = { + .dbg_name = "amp_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(amp_p_clk.c), + }, +}; + +static struct branch_clk csi_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(7), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(17), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 16, + }, + .c = { + .dbg_name = "csi_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi_p_clk.c), + }, +}; + +static struct branch_clk dsi1_m_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(9), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(6), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 19, + }, + .c = { + .dbg_name = "dsi1_m_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi1_m_p_clk.c), + }, +}; + +static struct branch_clk dsi1_s_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(18), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(20), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(5), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 21, + }, + .c = { + .dbg_name = "dsi1_s_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi1_s_p_clk.c), + }, +}; + +static struct branch_clk dsi2_m_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(17), + .reset_reg = SW_RESET_AHB2_REG, + .reset_mask = BIT(1), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 18, + }, + .c = { + .dbg_name = "dsi2_m_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi2_m_p_clk.c), + }, +}; + +static struct branch_clk dsi2_s_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(22), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(15), + .reset_reg = SW_RESET_AHB2_REG, + .reset_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 20, + }, + .c = { + .dbg_name = "dsi2_s_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi2_s_p_clk.c), + }, +}; + +static struct branch_clk gfx2d0_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(19), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(28), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(12), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 2, + }, + .c = { + .dbg_name = "gfx2d0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx2d0_p_clk.c), + }, +}; + +static struct branch_clk gfx2d1_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(2), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(29), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(11), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gfx2d1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx2d1_p_clk.c), + }, +}; + +static struct branch_clk gfx3d_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(3), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(27), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(10), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 4, + }, + .c = { + .dbg_name = "gfx3d_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx3d_p_clk.c), + }, +}; + +static struct branch_clk hdmi_m_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(14), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(21), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 5, + }, + .c = { + .dbg_name = "hdmi_m_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_m_p_clk.c), + }, +}; + +static struct branch_clk hdmi_s_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(4), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(22), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 6, + }, + .c = { + .dbg_name = "hdmi_s_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_s_p_clk.c), + }, +}; + +static struct branch_clk ijpeg_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(5), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(7), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 9, + }, + .c = { + .dbg_name = "ijpeg_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ijpeg_p_clk.c), + }, +}; + +static struct branch_clk imem_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(6), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(12), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(8), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 10, + }, + .c = { + .dbg_name = "imem_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(imem_p_clk.c), + }, +}; + +static struct branch_clk jpegd_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(21), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(4), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "jpegd_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(jpegd_p_clk.c), + }, +}; + +static struct branch_clk mdp_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(10), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(3), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "mdp_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_p_clk.c), + }, +}; + +static struct branch_clk rot_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(2), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 13, + }, + .c = { + .dbg_name = "rot_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(rot_p_clk.c), + }, +}; + +static struct branch_clk smmu_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(15), + .hwcg_reg = AHB_EN_REG, + .hwcg_mask = BIT(26), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 22, + }, + .c = { + .dbg_name = "smmu_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(smmu_p_clk.c), + }, +}; + +static struct branch_clk tv_enc_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(25), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 23, + }, + .c = { + .dbg_name = "tv_enc_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_enc_p_clk.c), + }, +}; + +static struct branch_clk vcodec_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(11), + .hwcg_reg = AHB_EN2_REG, + .hwcg_mask = BIT(26), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(1), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "vcodec_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcodec_p_clk.c), + }, +}; + +static struct branch_clk vfe_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(13), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 14, + .retain_reg = AHB_EN2_REG, + .retain_mask = BIT(0), + }, + .c = { + .dbg_name = "vfe_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_p_clk.c), + }, +}; + +static struct branch_clk vpe_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(16), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 15, + }, + .c = { + .dbg_name = "vpe_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vpe_p_clk.c), + }, +}; + +static struct branch_clk vcap_p_clk = { + .b = { + .ctl_reg = AHB_EN3_REG, + .en_mask = BIT(1), + .hwcg_reg = AHB_EN3_REG, + .hwcg_mask = BIT(0), + .reset_reg = SW_RESET_AHB2_REG, + .reset_mask = BIT(2), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 23, + }, + .c = { + .dbg_name = "vcap_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcap_p_clk.c), + }, +}; + +/* + * Peripheral Clocks + */ +#define CLK_GP(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GPn_NS_REG(n), \ + .en_mask = BIT(9), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GPn_NS_REG(n), \ + .md_reg = GPn_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gp, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gp[] = { + F_GP( 0, gnd, 1, 0, 0), + F_GP( 9600000, cxo, 2, 0, 0), + F_GP( 13500000, pxo, 2, 0, 0), + F_GP( 19200000, cxo, 1, 0, 0), + F_GP( 27000000, pxo, 1, 0, 0), + F_GP( 64000000, pll8, 2, 1, 3), + F_GP( 76800000, pll8, 1, 1, 5), + F_GP( 96000000, pll8, 4, 0, 0), + F_GP(128000000, pll8, 3, 0, 0), + F_GP(192000000, pll8, 2, 0, 0), + F_END +}; + +static CLK_GP(gp0, 0, CLK_HALT_SFPB_MISC_STATE_REG, 7); +static CLK_GP(gp1, 1, CLK_HALT_SFPB_MISC_STATE_REG, 6); +static CLK_GP(gp2, 2, CLK_HALT_SFPB_MISC_STATE_REG, 5); + +#define CLK_GSBI_UART(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GSBIn_UART_APPS_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = GSBIn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GSBIn_UART_APPS_NS_REG(n), \ + .md_reg = GSBIn_UART_APPS_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(31, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gsbi_uart, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 32000000, NOMINAL, 64000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GSBI_UART(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gsbi_uart[] = { + F_GSBI_UART( 0, gnd, 1, 0, 0), + F_GSBI_UART( 1843200, pll8, 2, 6, 625), + F_GSBI_UART( 3686400, pll8, 2, 12, 625), + F_GSBI_UART( 7372800, pll8, 2, 24, 625), + F_GSBI_UART(14745600, pll8, 2, 48, 625), + F_GSBI_UART(16000000, pll8, 4, 1, 6), + F_GSBI_UART(24000000, pll8, 4, 1, 4), + F_GSBI_UART(32000000, pll8, 4, 1, 3), + F_GSBI_UART(40000000, pll8, 1, 5, 48), + F_GSBI_UART(46400000, pll8, 1, 29, 240), + F_GSBI_UART(48000000, pll8, 4, 1, 2), + F_GSBI_UART(51200000, pll8, 1, 2, 15), + F_GSBI_UART(56000000, pll8, 1, 7, 48), + F_GSBI_UART(58982400, pll8, 1, 96, 625), + F_GSBI_UART(64000000, pll8, 2, 1, 3), + F_END +}; + +static CLK_GSBI_UART(gsbi1_uart, 1, CLK_HALT_CFPB_STATEA_REG, 10); +static CLK_GSBI_UART(gsbi2_uart, 2, CLK_HALT_CFPB_STATEA_REG, 6); +static CLK_GSBI_UART(gsbi3_uart, 3, CLK_HALT_CFPB_STATEA_REG, 2); +static CLK_GSBI_UART(gsbi4_uart, 4, CLK_HALT_CFPB_STATEB_REG, 26); +static CLK_GSBI_UART(gsbi5_uart, 5, CLK_HALT_CFPB_STATEB_REG, 22); +static CLK_GSBI_UART(gsbi6_uart, 6, CLK_HALT_CFPB_STATEB_REG, 18); +static CLK_GSBI_UART(gsbi7_uart, 7, CLK_HALT_CFPB_STATEB_REG, 14); +static CLK_GSBI_UART(gsbi8_uart, 8, CLK_HALT_CFPB_STATEB_REG, 10); +static CLK_GSBI_UART(gsbi9_uart, 9, CLK_HALT_CFPB_STATEB_REG, 6); +static CLK_GSBI_UART(gsbi10_uart, 10, CLK_HALT_CFPB_STATEB_REG, 2); +static CLK_GSBI_UART(gsbi11_uart, 11, CLK_HALT_CFPB_STATEC_REG, 17); +static CLK_GSBI_UART(gsbi12_uart, 12, CLK_HALT_CFPB_STATEC_REG, 13); + +#define CLK_GSBI_QUP(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GSBIn_QUP_APPS_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = GSBIn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GSBIn_QUP_APPS_NS_REG(n), \ + .md_reg = GSBIn_QUP_APPS_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gsbi_qup, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 24000000, NOMINAL, 52000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GSBI_QUP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gsbi_qup[] = { + F_GSBI_QUP( 0, gnd, 1, 0, 0), + F_GSBI_QUP( 1100000, pxo, 1, 2, 49), + F_GSBI_QUP( 5400000, pxo, 1, 1, 5), + F_GSBI_QUP(10800000, pxo, 1, 2, 5), + F_GSBI_QUP(15060000, pll8, 1, 2, 51), + F_GSBI_QUP(24000000, pll8, 4, 1, 4), + F_GSBI_QUP(25600000, pll8, 1, 1, 15), + F_GSBI_QUP(27000000, pxo, 1, 0, 0), + F_GSBI_QUP(48000000, pll8, 4, 1, 2), + F_GSBI_QUP(51200000, pll8, 1, 2, 15), + F_END +}; + +static CLK_GSBI_QUP(gsbi1_qup, 1, CLK_HALT_CFPB_STATEA_REG, 9); +static CLK_GSBI_QUP(gsbi2_qup, 2, CLK_HALT_CFPB_STATEA_REG, 4); +static CLK_GSBI_QUP(gsbi3_qup, 3, CLK_HALT_CFPB_STATEA_REG, 0); +static CLK_GSBI_QUP(gsbi4_qup, 4, CLK_HALT_CFPB_STATEB_REG, 24); +static CLK_GSBI_QUP(gsbi5_qup, 5, CLK_HALT_CFPB_STATEB_REG, 20); +static CLK_GSBI_QUP(gsbi6_qup, 6, CLK_HALT_CFPB_STATEB_REG, 16); +static CLK_GSBI_QUP(gsbi7_qup, 7, CLK_HALT_CFPB_STATEB_REG, 12); +static CLK_GSBI_QUP(gsbi8_qup, 8, CLK_HALT_CFPB_STATEB_REG, 8); +static CLK_GSBI_QUP(gsbi9_qup, 9, CLK_HALT_CFPB_STATEB_REG, 4); +static CLK_GSBI_QUP(gsbi10_qup, 10, CLK_HALT_CFPB_STATEB_REG, 0); +static CLK_GSBI_QUP(gsbi11_qup, 11, CLK_HALT_CFPB_STATEC_REG, 15); +static CLK_GSBI_QUP(gsbi12_qup, 12, CLK_HALT_CFPB_STATEC_REG, 11); + +#define F_PDM(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(1, 0, s##_to_xo_mux), \ + } +static struct clk_freq_tbl clk_tbl_pdm[] = { + F_PDM( 0, gnd, 1), + F_PDM(27000000, pxo, 1), + F_END +}; + +static struct rcg_clk pdm_clk = { + .b = { + .ctl_reg = PDM_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = PDM_CLK_NS_REG, + .reset_mask = BIT(12), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 3, + }, + .ns_reg = PDM_CLK_NS_REG, + .root_en_mask = BIT(11), + .ns_mask = BM(1, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_pdm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pdm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 27000000), + CLK_INIT(pdm_clk.c), + }, +}; + +static struct branch_clk pmem_clk = { + .b = { + .ctl_reg = PMEM_ACLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = PMEM_ACLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 20, + }, + .c = { + .dbg_name = "pmem_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmem_clk.c), + }, +}; + +#define F_PRNG(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + } +static struct clk_freq_tbl clk_tbl_prng_32[] = { + F_PRNG(32000000, pll8), + F_END +}; + +static struct clk_freq_tbl clk_tbl_prng_64[] = { + F_PRNG(64000000, pll8), + F_END +}; + +static struct rcg_clk prng_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(10), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 10, + }, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_prng_32, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "prng_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 32000000, NOMINAL, 64000000), + CLK_INIT(prng_clk.c), + }, +}; + +#define CLK_SDC(name, n, h_b, fmax_low, fmax_nom) \ + struct rcg_clk name = { \ + .b = { \ + .ctl_reg = SDCn_APPS_CLK_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = SDCn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = CLK_HALT_DFAB_STATE_REG, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = SDCn_APPS_CLK_NS_REG(n), \ + .md_reg = SDCn_APPS_CLK_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_sdc, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #name, \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, fmax_low, NOMINAL, fmax_nom), \ + CLK_INIT(name.c), \ + }, \ + } +#define F_SDC(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_sdc[] = { + F_SDC( 0, gnd, 1, 0, 0), + F_SDC( 144000, pxo, 3, 2, 125), + F_SDC( 400000, pll8, 4, 1, 240), + F_SDC( 16000000, pll8, 4, 1, 6), + F_SDC( 17070000, pll8, 1, 2, 45), + F_SDC( 20210000, pll8, 1, 1, 19), + F_SDC( 24000000, pll8, 4, 1, 4), + F_SDC( 48000000, pll8, 4, 1, 2), + F_SDC( 64000000, pll8, 3, 1, 2), + F_SDC( 96000000, pll8, 4, 0, 0), + F_SDC(192000000, pll8, 2, 0, 0), + F_END +}; + +static CLK_SDC(sdc1_clk, 1, 6, 52000000, 104000000); +static CLK_SDC(sdc2_clk, 2, 5, 52000000, 104000000); +static CLK_SDC(sdc3_clk, 3, 4, 104000000, 208000000); +static CLK_SDC(sdc4_clk, 4, 3, 33000000, 67000000); +static CLK_SDC(sdc5_clk, 5, 2, 33000000, 67000000); + +#define F_TSIF_REF(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_tsif_ref[] = { + F_TSIF_REF( 0, gnd, 1, 0, 0), + F_TSIF_REF(105000, pxo, 1, 1, 256), + F_END +}; + +static struct rcg_clk tsif_ref_clk = { + .b = { + .ctl_reg = TSIF_REF_CLK_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 5, + }, + .ns_reg = TSIF_REF_CLK_NS_REG, + .md_reg = TSIF_REF_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(31, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_tsif_ref, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "tsif_ref_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 27000000, NOMINAL, 54000000), + CLK_INIT(tsif_ref_clk.c), + }, +}; + +#define F_TSSC(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(1, 0, s##_to_xo_mux), \ + } +static struct clk_freq_tbl clk_tbl_tssc[] = { + F_TSSC( 0, gnd), + F_TSSC(27000000, pxo), + F_END +}; + +static struct rcg_clk tssc_clk = { + .b = { + .ctl_reg = TSSC_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 4, + }, + .ns_reg = TSSC_CLK_CTL_REG, + .ns_mask = BM(1, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_tssc, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "tssc_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 27000000), + CLK_INIT(tssc_clk.c), + }, +}; + +#define CLK_USB_HS(name, n, h_b) \ + static struct rcg_clk name = { \ + .b = { \ + .ctl_reg = USB_HS##n##_XCVR_FS_CLK_NS_REG, \ + .en_mask = BIT(9), \ + .reset_reg = USB_HS##n##_RESET_REG, \ + .reset_mask = BIT(0), \ + .halt_reg = CLK_HALT_DFAB_STATE_REG, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = USB_HS##n##_XCVR_FS_CLK_NS_REG, \ + .md_reg = USB_HS##n##_XCVR_FS_CLK_MD_REG, \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_usb, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #name, \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(NOMINAL, 64000000), \ + CLK_INIT(name.c), \ + }, \ +} + +#define F_USB(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_usb[] = { + F_USB( 0, gnd, 1, 0, 0), + F_USB(60000000, pll8, 1, 5, 32), + F_END +}; + +CLK_USB_HS(usb_hs1_xcvr_clk, 1, 0); +CLK_USB_HS(usb_hs3_xcvr_clk, 3, 30); +CLK_USB_HS(usb_hs4_xcvr_clk, 4, 2); + +static struct clk_freq_tbl clk_tbl_usb_hsic[] = { + F_USB( 0, gnd, 1, 0, 0), + F_USB(60000000, pll8, 1, 5, 32), + F_END +}; + +static struct rcg_clk usb_hsic_xcvr_fs_clk = { + .b = { + .ctl_reg = USB_HSIC_XCVR_FS_CLK_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 26, + }, + .ns_reg = USB_HSIC_XCVR_FS_CLK_NS_REG, + .md_reg = USB_HSIC_XCVR_FS_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb_hsic, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hsic_xcvr_fs_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 60000000), + CLK_INIT(usb_hsic_xcvr_fs_clk.c), + }, +}; + +static struct branch_clk usb_hsic_system_clk = { + .b = { + .ctl_reg = USB_HSIC_SYSTEM_CLK_CTL_REG, + .en_mask = BIT(4), + .reset_reg = USB_HSIC_RESET_REG, + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 24, + }, + .parent = &usb_hsic_xcvr_fs_clk.c, + .c = { + .dbg_name = "usb_hsic_system_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hsic_system_clk.c), + }, +}; + +#define F_USB_HSIC(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + } +static struct clk_freq_tbl clk_tbl_usb2_hsic[] = { + F_USB_HSIC(480000000, pll14), + F_END +}; + +static struct rcg_clk usb_hsic_hsic_src_clk = { + .b = { + .ctl_reg = USB_HSIC_HSIC_CLK_SRC_CTL_REG, + .halt_check = NOCHECK, + }, + .root_en_mask = BIT(0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_usb2_hsic, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hsic_hsic_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 480000000), + CLK_INIT(usb_hsic_hsic_src_clk.c), + }, +}; + +static struct branch_clk usb_hsic_hsic_clk = { + .b = { + .ctl_reg = USB_HSIC_HSIC_CLK_CTL_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 19, + }, + .parent = &usb_hsic_hsic_src_clk.c, + .c = { + .dbg_name = "usb_hsic_hsic_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hsic_hsic_clk.c), + }, +}; + +#define F_USB_HSIO_CAL(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + } +static struct clk_freq_tbl clk_tbl_usb_hsio_cal[] = { + F_USB_HSIO_CAL(9000000, pxo), + F_END +}; + +static struct rcg_clk usb_hsic_hsio_cal_clk = { + .b = { + .ctl_reg = USB_HSIC_HSIO_CAL_CLK_CTL_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 23, + }, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_usb_hsio_cal, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hsic_hsio_cal_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 10000000), + CLK_INIT(usb_hsic_hsio_cal_clk.c), + }, +}; + +static struct branch_clk usb_phy0_clk = { + .b = { + .reset_reg = USB_PHY0_RESET_REG, + .reset_mask = BIT(0), + }, + .c = { + .dbg_name = "usb_phy0_clk", + .ops = &clk_ops_reset, + CLK_INIT(usb_phy0_clk.c), + }, +}; + +#define CLK_USB_FS(i, n, fmax_nom) \ + struct rcg_clk i##_clk = { \ + .ns_reg = USB_FSn_XCVR_FS_CLK_NS_REG(n), \ + .b = { \ + .ctl_reg = USB_FSn_XCVR_FS_CLK_NS_REG(n), \ + .halt_check = NOCHECK, \ + }, \ + .md_reg = USB_FSn_XCVR_FS_CLK_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_usb, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(NOMINAL, fmax_nom), \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +static CLK_USB_FS(usb_fs1_src, 1, 64000000); +static struct branch_clk usb_fs1_xcvr_clk = { + .b = { + .ctl_reg = USB_FSn_XCVR_FS_CLK_NS_REG(1), + .en_mask = BIT(9), + .reset_reg = USB_FSn_RESET_REG(1), + .reset_mask = BIT(1), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 15, + }, + .parent = &usb_fs1_src_clk.c, + .c = { + .dbg_name = "usb_fs1_xcvr_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs1_xcvr_clk.c), + }, +}; + +static struct branch_clk usb_fs1_sys_clk = { + .b = { + .ctl_reg = USB_FSn_SYSTEM_CLK_CTL_REG(1), + .en_mask = BIT(4), + .reset_reg = USB_FSn_RESET_REG(1), + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 16, + }, + .parent = &usb_fs1_src_clk.c, + .c = { + .dbg_name = "usb_fs1_sys_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs1_sys_clk.c), + }, +}; + +static CLK_USB_FS(usb_fs2_src, 2, 60000000); +static struct branch_clk usb_fs2_xcvr_clk = { + .b = { + .ctl_reg = USB_FSn_XCVR_FS_CLK_NS_REG(2), + .en_mask = BIT(9), + .reset_reg = USB_FSn_RESET_REG(2), + .reset_mask = BIT(1), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 12, + }, + .parent = &usb_fs2_src_clk.c, + .c = { + .dbg_name = "usb_fs2_xcvr_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs2_xcvr_clk.c), + }, +}; + +static struct branch_clk usb_fs2_sys_clk = { + .b = { + .ctl_reg = USB_FSn_SYSTEM_CLK_CTL_REG(2), + .en_mask = BIT(4), + .reset_reg = USB_FSn_RESET_REG(2), + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 13, + }, + .parent = &usb_fs2_src_clk.c, + .c = { + .dbg_name = "usb_fs2_sys_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs2_sys_clk.c), + }, +}; + +/* Fast Peripheral Bus Clocks */ +static struct branch_clk ce1_core_clk = { + .b = { + .ctl_reg = CE1_CORE_CLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = CE1_CORE_CLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 27, + }, + .c = { + .dbg_name = "ce1_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce1_core_clk.c), + }, +}; + +static struct branch_clk ce1_p_clk = { + .b = { + .ctl_reg = CE1_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 1, + }, + .c = { + .dbg_name = "ce1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce1_p_clk.c), + }, +}; + +#define F_CE3(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(6, 3, d, 2, 0, s##_to_bb_mux), \ + } + +static struct clk_freq_tbl clk_tbl_ce3[] = { + F_CE3( 0, gnd, 1), + F_CE3( 48000000, pll8, 8), + F_CE3(100000000, pll3, 12), + F_END +}; + +static struct rcg_clk ce3_src_clk = { + .b = { + .ctl_reg = CE3_CLK_SRC_NS_REG, + .halt_check = NOCHECK, + }, + .ns_reg = CE3_CLK_SRC_NS_REG, + .root_en_mask = BIT(7), + .ns_mask = BM(6, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_ce3, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "ce3_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000), + CLK_INIT(ce3_src_clk.c), + }, +}; + +static struct branch_clk ce3_core_clk = { + .b = { + .ctl_reg = CE3_CORE_CLK_CTL_REG, + .en_mask = BIT(4), + .reset_reg = CE3_CORE_CLK_CTL_REG, + .reset_mask = BIT(7), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_bit = 5, + }, + .parent = &ce3_src_clk.c, + .c = { + .dbg_name = "ce3_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce3_core_clk.c), + } +}; + +static struct branch_clk ce3_p_clk = { + .b = { + .ctl_reg = CE3_HCLK_CTL_REG, + .en_mask = BIT(4), + .reset_reg = CE3_HCLK_CTL_REG, + .reset_mask = BIT(7), + .halt_reg = CLK_HALT_AFAB_SFAB_STATEB_REG, + .halt_bit = 16, + }, + .parent = &ce3_src_clk.c, + .c = { + .dbg_name = "ce3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce3_p_clk.c), + } +}; + +#define F_SATA(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(6, 3, d, 2, 0, s##_to_bb_mux), \ + } + +static struct clk_freq_tbl clk_tbl_sata[] = { + F_SATA( 0, gnd, 1), + F_SATA( 48000000, pll8, 8), + F_SATA(100000000, pll3, 12), + F_END +}; + +static struct rcg_clk sata_src_clk = { + .b = { + .ctl_reg = SATA_CLK_SRC_NS_REG, + .halt_check = NOCHECK, + }, + .ns_reg = SATA_CLK_SRC_NS_REG, + .root_en_mask = BIT(7), + .ns_mask = BM(6, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_sata, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "sata_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000), + CLK_INIT(sata_src_clk.c), + }, +}; + +static struct branch_clk sata_rxoob_clk = { + .b = { + .ctl_reg = SATA_RXOOB_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_bit = 26, + }, + .parent = &sata_src_clk.c, + .c = { + .dbg_name = "sata_rxoob_clk", + .ops = &clk_ops_branch, + CLK_INIT(sata_rxoob_clk.c), + }, +}; + +static struct branch_clk sata_pmalive_clk = { + .b = { + .ctl_reg = SATA_PMALIVE_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_bit = 25, + }, + .parent = &sata_src_clk.c, + .c = { + .dbg_name = "sata_pmalive_clk", + .ops = &clk_ops_branch, + CLK_INIT(sata_pmalive_clk.c), + }, +}; + +static struct branch_clk sata_phy_ref_clk = { + .b = { + .ctl_reg = SATA_PHY_REF_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_bit = 24, + }, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "sata_phy_ref_clk", + .ops = &clk_ops_branch, + CLK_INIT(sata_phy_ref_clk.c), + }, +}; + +static struct branch_clk sata_a_clk = { + .b = { + .ctl_reg = SATA_ACLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_AFAB_SFAB_STATEA_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "sata_a_clk", + .ops = &clk_ops_branch, + CLK_INIT(sata_a_clk.c), + }, +}; + +static struct branch_clk sata_p_clk = { + .b = { + .ctl_reg = SATA_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_bit = 27, + }, + .c = { + .dbg_name = "sata_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sata_p_clk.c), + }, +}; + +static struct branch_clk sfab_sata_s_p_clk = { + .b = { + .ctl_reg = SFAB_SATA_S_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_AFAB_SFAB_STATEB_REG, + .halt_bit = 14, + }, + .c = { + .dbg_name = "sfab_sata_s_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sfab_sata_s_p_clk.c), + }, +}; +static struct branch_clk pcie_p_clk = { + .b = { + .ctl_reg = PCIE_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 8, + }, + .c = { + .dbg_name = "pcie_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pcie_p_clk.c), + }, +}; + +static struct branch_clk pcie_phy_ref_clk = { + .b = { + .ctl_reg = PCIE_PCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_bit = 29, + }, + .c = { + .dbg_name = "pcie_phy_ref_clk", + .ops = &clk_ops_branch, + CLK_INIT(pcie_phy_ref_clk.c), + }, +}; + +static struct branch_clk pcie_a_clk = { + .b = { + .ctl_reg = PCIE_ACLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_AFAB_SFAB_STATEA_REG, + .halt_bit = 13, + }, + .c = { + .dbg_name = "pcie_a_clk", + .ops = &clk_ops_branch, + CLK_INIT(pcie_a_clk.c), + }, +}; + +static struct branch_clk dma_bam_p_clk = { + .b = { + .ctl_reg = DMA_BAM_HCLK_CTL, + .en_mask = BIT(4), + .hwcg_reg = DMA_BAM_HCLK_CTL, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "dma_bam_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dma_bam_p_clk.c), + }, +}; + +static struct branch_clk gsbi1_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(1), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "gsbi1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi1_p_clk.c), + }, +}; + +static struct branch_clk gsbi2_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(2), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "gsbi2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi2_p_clk.c), + }, +}; + +static struct branch_clk gsbi3_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(3), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(3), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gsbi3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi3_p_clk.c), + }, +}; + +static struct branch_clk gsbi4_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(4), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(4), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 27, + }, + .c = { + .dbg_name = "gsbi4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi4_p_clk.c), + }, +}; + +static struct branch_clk gsbi5_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(5), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(5), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 23, + }, + .c = { + .dbg_name = "gsbi5_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi5_p_clk.c), + }, +}; + +static struct branch_clk gsbi6_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(6), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(6), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 19, + }, + .c = { + .dbg_name = "gsbi6_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi6_p_clk.c), + }, +}; + +static struct branch_clk gsbi7_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(7), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(7), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 15, + }, + .c = { + .dbg_name = "gsbi7_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi7_p_clk.c), + }, +}; + +static struct branch_clk gsbi8_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(8), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(8), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "gsbi8_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi8_p_clk.c), + }, +}; + +static struct branch_clk gsbi9_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(9), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(9), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "gsbi9_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi9_p_clk.c), + }, +}; + +static struct branch_clk gsbi10_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(10), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(10), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gsbi10_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi10_p_clk.c), + }, +}; + +static struct branch_clk gsbi11_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(11), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(11), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 18, + }, + .c = { + .dbg_name = "gsbi11_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi11_p_clk.c), + }, +}; + +static struct branch_clk gsbi12_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(12), + .en_mask = BIT(4), + .hwcg_reg = GSBIn_HCLK_CTL_REG(12), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 14, + }, + .c = { + .dbg_name = "gsbi12_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi12_p_clk.c), + }, +}; + +static struct branch_clk sata_phy_cfg_clk = { + .b = { + .ctl_reg = SATA_PHY_CFG_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "sata_phy_cfg_clk", + .ops = &clk_ops_branch, + CLK_INIT(sata_phy_cfg_clk.c), + }, +}; + +static struct branch_clk tsif_p_clk = { + .b = { + .ctl_reg = TSIF_HCLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = TSIF_HCLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "tsif_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(tsif_p_clk.c), + }, +}; + +static struct branch_clk usb_fs1_p_clk = { + .b = { + .ctl_reg = USB_FSn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 17, + }, + .c = { + .dbg_name = "usb_fs1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs1_p_clk.c), + }, +}; + +static struct branch_clk usb_fs2_p_clk = { + .b = { + .ctl_reg = USB_FSn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 14, + }, + .c = { + .dbg_name = "usb_fs2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs2_p_clk.c), + }, +}; + +static struct branch_clk usb_hs1_p_clk = { + .b = { + .ctl_reg = USB_HS1_HCLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = USB_HS1_HCLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 1, + }, + .c = { + .dbg_name = "usb_hs1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs1_p_clk.c), + }, +}; + +static struct branch_clk usb_hs3_p_clk = { + .b = { + .ctl_reg = USB_HS3_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 31, + }, + .c = { + .dbg_name = "usb_hs3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs3_p_clk.c), + }, +}; + +static struct branch_clk usb_hs4_p_clk = { + .b = { + .ctl_reg = USB_HS4_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "usb_hs4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs4_p_clk.c), + }, +}; + +static struct branch_clk usb_hsic_p_clk = { + .b = { + .ctl_reg = USB_HSIC_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 28, + }, + .c = { + .dbg_name = "usb_hsic_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hsic_p_clk.c), + }, +}; + +static struct branch_clk sdc1_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(1), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "sdc1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc1_p_clk.c), + }, +}; + +static struct branch_clk sdc2_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(2), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 10, + }, + .c = { + .dbg_name = "sdc2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc2_p_clk.c), + }, +}; + +static struct branch_clk sdc3_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(3), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(3), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 9, + }, + .c = { + .dbg_name = "sdc3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc3_p_clk.c), + }, +}; + +static struct branch_clk sdc4_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(4), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(4), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 8, + }, + .c = { + .dbg_name = "sdc4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc4_p_clk.c), + }, +}; + +static struct branch_clk sdc5_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(5), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(5), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "sdc5_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc5_p_clk.c), + }, +}; + +/* HW-Voteable Clocks */ +static struct branch_clk adm0_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(2), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 14, + }, + .c = { + .dbg_name = "adm0_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm0_clk.c), + }, +}; + +static struct branch_clk adm0_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(3), + .hwcg_reg = ADM0_PBUS_CLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 13, + }, + .c = { + .dbg_name = "adm0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm0_p_clk.c), + }, +}; + +static struct branch_clk pmic_arb0_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(8), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 22, + }, + .c = { + .dbg_name = "pmic_arb0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_arb0_p_clk.c), + }, +}; + +static struct branch_clk pmic_arb1_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 21, + }, + .c = { + .dbg_name = "pmic_arb1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_arb1_p_clk.c), + }, +}; + +static struct branch_clk pmic_ssbi2_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(7), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 23, + }, + .c = { + .dbg_name = "pmic_ssbi2_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_ssbi2_clk.c), + }, +}; + +static struct branch_clk rpm_msg_ram_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(6), + .hwcg_reg = RPM_MSG_RAM_HCLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 12, + }, + .c = { + .dbg_name = "rpm_msg_ram_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(rpm_msg_ram_p_clk.c), + }, +}; + +/* + * Multimedia Clocks + */ + +#define CLK_CAM(name, n, hb) \ + struct rcg_clk name = { \ + .b = { \ + .ctl_reg = CAMCLK##n##_CC_REG, \ + .en_mask = BIT(0), \ + .halt_reg = DBG_BUS_VEC_I_REG, \ + .halt_bit = hb, \ + }, \ + .ns_reg = CAMCLK##n##_NS_REG, \ + .md_reg = CAMCLK##n##_MD_REG, \ + .root_en_mask = BIT(2), \ + .ns_mask = BM(31, 24) | BM(15, 14) | BM(2, 0), \ + .mnd_en_mask = BIT(5), \ + .ctl_mask = BM(7, 6), \ + .set_rate = set_rate_mnd_8, \ + .freq_tbl = clk_tbl_cam, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #name, \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 64000000, NOMINAL, 128000000), \ + CLK_INIT(name.c), \ + }, \ + } +#define F_CAM(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(31, 24, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_cam[] = { + F_CAM( 0, gnd, 1, 0, 0), + F_CAM( 6000000, pll8, 4, 1, 16), + F_CAM( 8000000, pll8, 4, 1, 12), + F_CAM( 12000000, pll8, 4, 1, 8), + F_CAM( 16000000, pll8, 4, 1, 6), + F_CAM( 19200000, pll8, 4, 1, 5), + F_CAM( 24000000, pll8, 4, 1, 4), + F_CAM( 32000000, pll8, 4, 1, 3), + F_CAM( 48000000, pll8, 4, 1, 2), + F_CAM( 64000000, pll8, 3, 1, 2), + F_CAM( 96000000, pll8, 4, 0, 0), + F_CAM(128000000, pll8, 3, 0, 0), + F_END +}; + +static CLK_CAM(cam0_clk, 0, 15); +static CLK_CAM(cam1_clk, 1, 16); +static CLK_CAM(cam2_clk, 2, 31); + +#define F_CSI(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(31, 24, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_csi[] = { + F_CSI( 0, gnd, 1, 0, 0), + F_CSI( 27000000, pxo, 1, 0, 0), + F_CSI( 85330000, pll8, 1, 2, 9), + F_CSI(177780000, pll2, 1, 2, 9), + F_END +}; + +static struct rcg_clk csi0_src_clk = { + .ns_reg = CSI0_NS_REG, + .b = { + .ctl_reg = CSI0_CC_REG, + .halt_check = NOCHECK, + }, + .md_reg = CSI0_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(31, 24) | BM(15, 14) | BM(2, 0), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_csi, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "csi0_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 86000000, NOMINAL, 178000000), + CLK_INIT(csi0_src_clk.c), + }, +}; + +static struct branch_clk csi0_clk = { + .b = { + .ctl_reg = CSI0_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(8), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 13, + }, + .parent = &csi0_src_clk.c, + .c = { + .dbg_name = "csi0_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_clk.c), + }, +}; + +static struct branch_clk csi0_phy_clk = { + .b = { + .ctl_reg = CSI0_CC_REG, + .en_mask = BIT(8), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(29), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 9, + }, + .parent = &csi0_src_clk.c, + .c = { + .dbg_name = "csi0_phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_phy_clk.c), + }, +}; + +static struct rcg_clk csi1_src_clk = { + .ns_reg = CSI1_NS_REG, + .b = { + .ctl_reg = CSI1_CC_REG, + .halt_check = NOCHECK, + }, + .md_reg = CSI1_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(31, 24) | BM(15, 14) | BM(2, 0), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_csi, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "csi1_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 86000000, NOMINAL, 178000000), + CLK_INIT(csi1_src_clk.c), + }, +}; + +static struct branch_clk csi1_clk = { + .b = { + .ctl_reg = CSI1_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(18), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 14, + }, + .parent = &csi1_src_clk.c, + .c = { + .dbg_name = "csi1_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi1_clk.c), + }, +}; + +static struct branch_clk csi1_phy_clk = { + .b = { + .ctl_reg = CSI1_CC_REG, + .en_mask = BIT(8), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(28), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 10, + }, + .parent = &csi1_src_clk.c, + .c = { + .dbg_name = "csi1_phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi1_phy_clk.c), + }, +}; + +static struct rcg_clk csi2_src_clk = { + .ns_reg = CSI2_NS_REG, + .b = { + .ctl_reg = CSI2_CC_REG, + .halt_check = NOCHECK, + }, + .md_reg = CSI2_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(31, 24) | BM(15, 14) | BM(2, 0), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_csi, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "csi2_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 86000000, NOMINAL, 178000000), + CLK_INIT(csi2_src_clk.c), + }, +}; + +static struct branch_clk csi2_clk = { + .b = { + .ctl_reg = CSI2_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE2_REG, + .reset_mask = BIT(2), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 29, + }, + .parent = &csi2_src_clk.c, + .c = { + .dbg_name = "csi2_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi2_clk.c), + }, +}; + +static struct branch_clk csi2_phy_clk = { + .b = { + .ctl_reg = CSI2_CC_REG, + .en_mask = BIT(8), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(31), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 29, + }, + .parent = &csi2_src_clk.c, + .c = { + .dbg_name = "csi2_phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi2_phy_clk.c), + }, +}; + +static struct clk *pix_rdi_mux_map[] = { + [0] = &csi0_clk.c, + [1] = &csi1_clk.c, + [2] = &csi2_clk.c, + NULL, +}; + +struct pix_rdi_clk { + bool enabled; + unsigned long cur_rate; + + void __iomem *const s_reg; + u32 s_mask; + + void __iomem *const s2_reg; + u32 s2_mask; + + struct branch b; + struct clk c; +}; + +static inline struct pix_rdi_clk *to_pix_rdi_clk(struct clk *clk) +{ + return container_of(clk, struct pix_rdi_clk, c); +} + +static int pix_rdi_clk_set_rate(struct clk *c, unsigned long rate) +{ + int ret, i; + u32 reg; + unsigned long flags; + struct pix_rdi_clk *clk = to_pix_rdi_clk(c); + struct clk **mux_map = pix_rdi_mux_map; + + /* + * These clocks select three inputs via two muxes. One mux selects + * between csi0 and csi1 and the second mux selects between that mux's + * output and csi2. The source and destination selections for each + * mux must be clocking for the switch to succeed so just turn on + * all three sources because it's easier than figuring out what source + * needs to be on at what time. + */ + for (i = 0; mux_map[i]; i++) { + ret = clk_enable(mux_map[i]); + if (ret) + goto err; + } + if (rate >= i) { + ret = -EINVAL; + goto err; + } + /* Keep the new source on when switching inputs of an enabled clock */ + if (clk->enabled) { + clk_disable(mux_map[clk->cur_rate]); + clk_enable(mux_map[rate]); + } + spin_lock_irqsave(&local_clock_reg_lock, flags); + reg = readl_relaxed(clk->s2_reg); + reg &= ~clk->s2_mask; + reg |= rate == 2 ? clk->s2_mask : 0; + writel_relaxed(reg, clk->s2_reg); + /* + * Wait at least 6 cycles of slowest clock + * for the glitch-free MUX to fully switch sources. + */ + mb(); + udelay(1); + reg = readl_relaxed(clk->s_reg); + reg &= ~clk->s_mask; + reg |= rate == 1 ? clk->s_mask : 0; + writel_relaxed(reg, clk->s_reg); + /* + * Wait at least 6 cycles of slowest clock + * for the glitch-free MUX to fully switch sources. + */ + mb(); + udelay(1); + clk->cur_rate = rate; + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +err: + for (i--; i >= 0; i--) + clk_disable(mux_map[i]); + + return 0; +} + +static unsigned long pix_rdi_clk_get_rate(struct clk *c) +{ + return to_pix_rdi_clk(c)->cur_rate; +} + +static int pix_rdi_clk_enable(struct clk *c) +{ + unsigned long flags; + struct pix_rdi_clk *clk = to_pix_rdi_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __branch_clk_enable_reg(&clk->b, clk->c.dbg_name); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + clk->enabled = true; + + return 0; +} + +static void pix_rdi_clk_disable(struct clk *c) +{ + unsigned long flags; + struct pix_rdi_clk *clk = to_pix_rdi_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __branch_clk_disable_reg(&clk->b, clk->c.dbg_name); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + clk->enabled = false; +} + +static int pix_rdi_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + return branch_reset(&to_pix_rdi_clk(clk)->b, action); +} + +static struct clk *pix_rdi_clk_get_parent(struct clk *c) +{ + struct pix_rdi_clk *clk = to_pix_rdi_clk(c); + + return pix_rdi_mux_map[clk->cur_rate]; +} + +static int pix_rdi_clk_list_rate(struct clk *c, unsigned n) +{ + if (pix_rdi_mux_map[n]) + return n; + return -ENXIO; +} + +static enum handoff pix_rdi_clk_handoff(struct clk *c) +{ + u32 reg; + struct pix_rdi_clk *clk = to_pix_rdi_clk(c); + enum handoff ret; + + ret = branch_handoff(&clk->b, &clk->c); + if (ret == HANDOFF_DISABLED_CLK) + return ret; + + reg = readl_relaxed(clk->s_reg); + clk->cur_rate = reg & clk->s_mask ? 1 : 0; + reg = readl_relaxed(clk->s2_reg); + clk->cur_rate = reg & clk->s2_mask ? 2 : clk->cur_rate; + + return HANDOFF_ENABLED_CLK; +} + +static struct clk_ops clk_ops_pix_rdi_8960 = { + .enable = pix_rdi_clk_enable, + .disable = pix_rdi_clk_disable, + .auto_off = pix_rdi_clk_disable, + .handoff = pix_rdi_clk_handoff, + .set_rate = pix_rdi_clk_set_rate, + .get_rate = pix_rdi_clk_get_rate, + .list_rate = pix_rdi_clk_list_rate, + .reset = pix_rdi_clk_reset, + .get_parent = pix_rdi_clk_get_parent, +}; + +static struct pix_rdi_clk csi_pix_clk = { + .b = { + .ctl_reg = MISC_CC_REG, + .en_mask = BIT(26), + .halt_check = DELAY, + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(26), + }, + .s_reg = MISC_CC_REG, + .s_mask = BIT(25), + .s2_reg = MISC_CC3_REG, + .s2_mask = BIT(13), + .c = { + .dbg_name = "csi_pix_clk", + .ops = &clk_ops_pix_rdi_8960, + CLK_INIT(csi_pix_clk.c), + }, +}; + +static struct pix_rdi_clk csi_pix1_clk = { + .b = { + .ctl_reg = MISC_CC3_REG, + .en_mask = BIT(10), + .halt_check = DELAY, + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(30), + }, + .s_reg = MISC_CC3_REG, + .s_mask = BIT(8), + .s2_reg = MISC_CC3_REG, + .s2_mask = BIT(9), + .c = { + .dbg_name = "csi_pix1_clk", + .ops = &clk_ops_pix_rdi_8960, + CLK_INIT(csi_pix1_clk.c), + }, +}; + +static struct pix_rdi_clk csi_rdi_clk = { + .b = { + .ctl_reg = MISC_CC_REG, + .en_mask = BIT(13), + .halt_check = DELAY, + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(27), + }, + .s_reg = MISC_CC_REG, + .s_mask = BIT(12), + .s2_reg = MISC_CC3_REG, + .s2_mask = BIT(12), + .c = { + .dbg_name = "csi_rdi_clk", + .ops = &clk_ops_pix_rdi_8960, + CLK_INIT(csi_rdi_clk.c), + }, +}; + +static struct pix_rdi_clk csi_rdi1_clk = { + .b = { + .ctl_reg = MISC_CC3_REG, + .en_mask = BIT(2), + .halt_check = DELAY, + .reset_reg = SW_RESET_CORE2_REG, + .reset_mask = BIT(1), + }, + .s_reg = MISC_CC3_REG, + .s_mask = BIT(0), + .s2_reg = MISC_CC3_REG, + .s2_mask = BIT(1), + .c = { + .dbg_name = "csi_rdi1_clk", + .ops = &clk_ops_pix_rdi_8960, + CLK_INIT(csi_rdi1_clk.c), + }, +}; + +static struct pix_rdi_clk csi_rdi2_clk = { + .b = { + .ctl_reg = MISC_CC3_REG, + .en_mask = BIT(6), + .halt_check = DELAY, + .reset_reg = SW_RESET_CORE2_REG, + .reset_mask = BIT(0), + }, + .s_reg = MISC_CC3_REG, + .s_mask = BIT(4), + .s2_reg = MISC_CC3_REG, + .s2_mask = BIT(5), + .c = { + .dbg_name = "csi_rdi2_clk", + .ops = &clk_ops_pix_rdi_8960, + CLK_INIT(csi_rdi2_clk.c), + }, +}; + +#define F_CSI_PHYTIMER(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(31, 24, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_csi_phytimer[] = { + F_CSI_PHYTIMER( 0, gnd, 1, 0, 0), + F_CSI_PHYTIMER( 85330000, pll8, 1, 2, 9), + F_CSI_PHYTIMER(177780000, pll2, 1, 2, 9), + F_END +}; + +static struct rcg_clk csiphy_timer_src_clk = { + .ns_reg = CSIPHYTIMER_NS_REG, + .b = { + .ctl_reg = CSIPHYTIMER_CC_REG, + .halt_check = NOCHECK, + }, + .md_reg = CSIPHYTIMER_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(31, 24) | BM(15, 14) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd_8, + .freq_tbl = clk_tbl_csi_phytimer, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "csiphy_timer_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 86000000, NOMINAL, 178000000), + CLK_INIT(csiphy_timer_src_clk.c), + }, +}; + +static struct branch_clk csi0phy_timer_clk = { + .b = { + .ctl_reg = CSIPHYTIMER_CC_REG, + .en_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 17, + }, + .parent = &csiphy_timer_src_clk.c, + .c = { + .dbg_name = "csi0phy_timer_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0phy_timer_clk.c), + }, +}; + +static struct branch_clk csi1phy_timer_clk = { + .b = { + .ctl_reg = CSIPHYTIMER_CC_REG, + .en_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 18, + }, + .parent = &csiphy_timer_src_clk.c, + .c = { + .dbg_name = "csi1phy_timer_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi1phy_timer_clk.c), + }, +}; + +static struct branch_clk csi2phy_timer_clk = { + .b = { + .ctl_reg = CSIPHYTIMER_CC_REG, + .en_mask = BIT(11), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 30, + }, + .parent = &csiphy_timer_src_clk.c, + .c = { + .dbg_name = "csi2phy_timer_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi2phy_timer_clk.c), + }, +}; + +#define F_DSI(d) \ + { \ + .freq_hz = d, \ + .ns_val = BVAL(15, 12, (d-1)), \ + } +/* + * The DSI_BYTE/ESC clock is sourced from the DSI PHY PLL, which may change rate + * without this clock driver knowing. So, overload the clk_set_rate() to set + * the divider (1 to 16) of the clock with respect to the PLL rate. + */ +static struct clk_freq_tbl clk_tbl_dsi_byte[] = { + F_DSI(1), F_DSI(2), F_DSI(3), F_DSI(4), + F_DSI(5), F_DSI(6), F_DSI(7), F_DSI(8), + F_DSI(9), F_DSI(10), F_DSI(11), F_DSI(12), + F_DSI(13), F_DSI(14), F_DSI(15), F_DSI(16), + F_END +}; + +static struct rcg_clk dsi1_byte_clk = { + .b = { + .ctl_reg = DSI1_BYTE_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(7), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 21, + .retain_reg = DSI1_BYTE_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = DSI1_BYTE_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(15, 12), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_dsi_byte, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "dsi1_byte_clk", + .ops = &clk_ops_rcg, + CLK_INIT(dsi1_byte_clk.c), + }, +}; + +static struct rcg_clk dsi2_byte_clk = { + .b = { + .ctl_reg = DSI2_BYTE_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(25), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 20, + .retain_reg = DSI2_BYTE_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = DSI2_BYTE_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(15, 12), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_dsi_byte, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "dsi2_byte_clk", + .ops = &clk_ops_rcg, + CLK_INIT(dsi2_byte_clk.c), + }, +}; + +static struct rcg_clk dsi1_esc_clk = { + .b = { + .ctl_reg = DSI1_ESC_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 1, + }, + .ns_reg = DSI1_ESC_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(15, 12), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_dsi_byte, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "dsi1_esc_clk", + .ops = &clk_ops_rcg, + CLK_INIT(dsi1_esc_clk.c), + }, +}; + +static struct rcg_clk dsi2_esc_clk = { + .b = { + .ctl_reg = DSI2_ESC_CC_REG, + .en_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 3, + }, + .ns_reg = DSI2_ESC_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(15, 12), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_dsi_byte, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "dsi2_esc_clk", + .ops = &clk_ops_rcg, + CLK_INIT(dsi2_esc_clk.c), + }, +}; + +#define F_GFX2D(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD4(4, m, 0, n), \ + .ns_val = NS_MND_BANKED4(20, 16, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } +static struct clk_freq_tbl clk_tbl_gfx2d[] = { + F_GFX2D( 0, gnd, 0, 0), + F_GFX2D( 27000000, pxo, 0, 0), + F_GFX2D( 48000000, pll8, 1, 8), + F_GFX2D( 54857000, pll8, 1, 7), + F_GFX2D( 64000000, pll8, 1, 6), + F_GFX2D( 76800000, pll8, 1, 5), + F_GFX2D( 96000000, pll8, 1, 4), + F_GFX2D(128000000, pll8, 1, 3), + F_GFX2D(145455000, pll2, 2, 11), + F_GFX2D(160000000, pll2, 1, 5), + F_GFX2D(177778000, pll2, 2, 9), + F_GFX2D(200000000, pll2, 1, 4), + F_GFX2D(228571000, pll2, 2, 7), + F_END +}; + +static struct bank_masks bmnd_info_gfx2d0 = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = GFX2D0_MD0_REG, + .ns_mask = BM(23, 20) | BM(5, 3), + .rst_mask = BIT(25), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = GFX2D0_MD1_REG, + .ns_mask = BM(19, 16) | BM(2, 0), + .rst_mask = BIT(24), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk gfx2d0_clk = { + .b = { + .ctl_reg = GFX2D0_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 9, + .retain_reg = GFX2D0_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = GFX2D0_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_gfx2d, + .bank_info = &bmnd_info_gfx2d0, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "gfx2d0_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 100000000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(gfx2d0_clk.c), + }, +}; + +static struct bank_masks bmnd_info_gfx2d1 = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = GFX2D1_MD0_REG, + .ns_mask = BM(23, 20) | BM(5, 3), + .rst_mask = BIT(25), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = GFX2D1_MD1_REG, + .ns_mask = BM(19, 16) | BM(2, 0), + .rst_mask = BIT(24), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk gfx2d1_clk = { + .b = { + .ctl_reg = GFX2D1_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(13), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 14, + .retain_reg = GFX2D1_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = GFX2D1_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_gfx2d, + .bank_info = &bmnd_info_gfx2d1, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "gfx2d1_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 100000000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(gfx2d1_clk.c), + }, +}; + +#define F_GFX3D(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD4(4, m, 0, n), \ + .ns_val = NS_MND_BANKED4(18, 14, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } + +static struct clk_freq_tbl clk_tbl_gfx3d_8960[] = { + F_GFX3D( 0, gnd, 0, 0), + F_GFX3D( 27000000, pxo, 0, 0), + F_GFX3D( 48000000, pll8, 1, 8), + F_GFX3D( 54857000, pll8, 1, 7), + F_GFX3D( 64000000, pll8, 1, 6), + F_GFX3D( 76800000, pll8, 1, 5), + F_GFX3D( 96000000, pll8, 1, 4), + F_GFX3D(128000000, pll8, 1, 3), + F_GFX3D(145455000, pll2, 2, 11), + F_GFX3D(160000000, pll2, 1, 5), + F_GFX3D(177778000, pll2, 2, 9), + F_GFX3D(200000000, pll2, 1, 4), + F_GFX3D(228571000, pll2, 2, 7), + F_GFX3D(266667000, pll2, 1, 3), + F_GFX3D(300000000, pll3, 1, 4), + F_GFX3D(320000000, pll2, 2, 5), + F_GFX3D(400000000, pll2, 1, 2), + F_END +}; + +static struct clk_freq_tbl clk_tbl_gfx3d_8064[] = { + F_GFX3D( 0, gnd, 0, 0), + F_GFX3D( 27000000, pxo, 0, 0), + F_GFX3D( 48000000, pll8, 1, 8), + F_GFX3D( 54857000, pll8, 1, 7), + F_GFX3D( 64000000, pll8, 1, 6), + F_GFX3D( 76800000, pll8, 1, 5), + F_GFX3D( 96000000, pll8, 1, 4), + F_GFX3D(128000000, pll8, 1, 3), + F_GFX3D(145455000, pll2, 2, 11), + F_GFX3D(160000000, pll2, 1, 5), + F_GFX3D(177778000, pll2, 2, 9), + F_GFX3D(200000000, pll2, 1, 4), + F_GFX3D(228571000, pll2, 2, 7), + F_GFX3D(266667000, pll2, 1, 3), + F_GFX3D(325000000, pll15, 1, 3), + F_GFX3D(400000000, pll2, 1, 2), + F_END +}; + +static struct clk_freq_tbl clk_tbl_gfx3d_8930[] = { + F_GFX3D( 0, gnd, 0, 0), + F_GFX3D( 27000000, pxo, 0, 0), + F_GFX3D( 48000000, pll8, 1, 8), + F_GFX3D( 54857000, pll8, 1, 7), + F_GFX3D( 64000000, pll8, 1, 6), + F_GFX3D( 76800000, pll8, 1, 5), + F_GFX3D( 96000000, pll8, 1, 4), + F_GFX3D(128000000, pll8, 1, 3), + F_GFX3D(145455000, pll2, 2, 11), + F_GFX3D(160000000, pll2, 1, 5), + F_GFX3D(177778000, pll2, 2, 9), + F_GFX3D(192000000, pll8, 1, 2), + F_GFX3D(200000000, pll2, 1, 4), + F_GFX3D(228571000, pll2, 2, 7), + F_GFX3D(266667000, pll2, 1, 3), + F_GFX3D(320000000, pll2, 2, 5), + F_GFX3D(400000000, pll2, 1, 2), + F_GFX3D(450000000, pll15, 1, 2), + F_END +}; + +static unsigned long fmax_gfx3d_8064[MAX_VDD_LEVELS] __initdata = { + [VDD_DIG_LOW] = 128000000, + [VDD_DIG_NOMINAL] = 325000000, + [VDD_DIG_HIGH] = 400000000 +}; + +static unsigned long fmax_gfx3d_8930[MAX_VDD_LEVELS] __initdata = { + [VDD_DIG_LOW] = 192000000, + [VDD_DIG_NOMINAL] = 320000000, + [VDD_DIG_HIGH] = 450000000 +}; + +static struct bank_masks bmnd_info_gfx3d = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = GFX3D_MD0_REG, + .ns_mask = BM(21, 18) | BM(5, 3), + .rst_mask = BIT(23), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = GFX3D_MD1_REG, + .ns_mask = BM(17, 14) | BM(2, 0), + .rst_mask = BIT(22), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk gfx3d_clk = { + .b = { + .ctl_reg = GFX3D_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(12), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 4, + .retain_reg = GFX3D_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = GFX3D_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_gfx3d_8960, + .bank_info = &bmnd_info_gfx3d, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "gfx3d_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 128000000, NOMINAL, 300000000, + HIGH, 400000000), + CLK_INIT(gfx3d_clk.c), + .depends = &gmem_axi_clk.c, + }, +}; + +#define F_VCAP(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD4(4, m, 0, n), \ + .ns_val = NS_MND_BANKED4(18, 14, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } + +static struct clk_freq_tbl clk_tbl_vcap[] = { + F_VCAP( 0, gnd, 0, 0), + F_VCAP( 27000000, pxo, 0, 0), + F_VCAP( 54860000, pll8, 1, 7), + F_VCAP( 64000000, pll8, 1, 6), + F_VCAP( 76800000, pll8, 1, 5), + F_VCAP(128000000, pll8, 1, 3), + F_VCAP(160000000, pll2, 1, 5), + F_VCAP(200000000, pll2, 1, 4), + F_END +}; + +static struct bank_masks bmnd_info_vcap = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = VCAP_MD0_REG, + .ns_mask = BM(21, 18) | BM(5, 3), + .rst_mask = BIT(23), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = VCAP_MD1_REG, + .ns_mask = BM(17, 14) | BM(2, 0), + .rst_mask = BIT(22), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk vcap_clk = { + .b = { + .ctl_reg = VCAP_CC_REG, + .en_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 15, + }, + .ns_reg = VCAP_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_vcap, + .bank_info = &bmnd_info_vcap, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vcap_clk", + .ops = &clk_ops_rcg, + .depends = &vcap_axi_clk.c, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(vcap_clk.c), + }, +}; + +static struct branch_clk vcap_npl_clk = { + .b = { + .ctl_reg = VCAP_CC_REG, + .en_mask = BIT(13), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 25, + }, + .parent = &vcap_clk.c, + .c = { + .dbg_name = "vcap_npl_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcap_npl_clk.c), + }, +}; + +#define F_IJPEG(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 15, 12, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } + +static struct clk_freq_tbl clk_tbl_ijpeg[] = { + F_IJPEG( 0, gnd, 1, 0, 0), + F_IJPEG( 27000000, pxo, 1, 0, 0), + F_IJPEG( 36570000, pll8, 1, 2, 21), + F_IJPEG( 54860000, pll8, 7, 0, 0), + F_IJPEG( 96000000, pll8, 4, 0, 0), + F_IJPEG(109710000, pll8, 1, 2, 7), + F_IJPEG(128000000, pll8, 3, 0, 0), + F_IJPEG(153600000, pll8, 1, 2, 5), + F_IJPEG(200000000, pll2, 4, 0, 0), + F_IJPEG(228571000, pll2, 1, 2, 7), + F_IJPEG(266667000, pll2, 1, 1, 3), + F_IJPEG(320000000, pll2, 1, 2, 5), + F_END +}; + +static unsigned long fmax_ijpeg_8064[MAX_VDD_LEVELS] __initdata = { + [VDD_DIG_LOW] = 128000000, + [VDD_DIG_NOMINAL] = 266667000, + [VDD_DIG_HIGH] = 320000000 +}; + +static struct rcg_clk ijpeg_clk = { + .b = { + .ctl_reg = IJPEG_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 24, + .retain_reg = IJPEG_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = IJPEG_NS_REG, + .md_reg = IJPEG_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(23, 16) | BM(15, 12) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_ijpeg, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "ijpeg_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 110000000, NOMINAL, 266667000, + HIGH, 320000000), + CLK_INIT(ijpeg_clk.c), + .depends = &ijpeg_axi_clk.c, + }, +}; + +#define F_JPEGD(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(15, 12, d, 2, 0, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_jpegd[] = { + F_JPEGD( 0, gnd, 1), + F_JPEGD( 64000000, pll8, 6), + F_JPEGD( 76800000, pll8, 5), + F_JPEGD( 96000000, pll8, 4), + F_JPEGD(160000000, pll2, 5), + F_JPEGD(200000000, pll2, 4), + F_END +}; + +static struct rcg_clk jpegd_clk = { + .b = { + .ctl_reg = JPEGD_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(19), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 19, + .retain_reg = JPEGD_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = JPEGD_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(15, 12) | BM(2, 0)), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_jpegd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "jpegd_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 96000000, NOMINAL, 200000000), + CLK_INIT(jpegd_clk.c), + .depends = &jpegd_axi_clk.c, + }, +}; + +#define F_MDP(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MND_BANKED8(22, 14, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } +static struct clk_freq_tbl clk_tbl_mdp[] = { + F_MDP( 0, gnd, 0, 0), + F_MDP( 9600000, pll8, 1, 40), + F_MDP( 13710000, pll8, 1, 28), + F_MDP( 27000000, pxo, 0, 0), + F_MDP( 29540000, pll8, 1, 13), + F_MDP( 34910000, pll8, 1, 11), + F_MDP( 38400000, pll8, 1, 10), + F_MDP( 59080000, pll8, 2, 13), + F_MDP( 76800000, pll8, 1, 5), + F_MDP( 85330000, pll8, 2, 9), + F_MDP( 96000000, pll8, 1, 4), + F_MDP(128000000, pll8, 1, 3), + F_MDP(160000000, pll2, 1, 5), + F_MDP(177780000, pll2, 2, 9), + F_MDP(200000000, pll2, 1, 4), + F_MDP(266667000, pll2, 1, 3), + F_END +}; + +static unsigned long fmax_mdp_8064[MAX_VDD_LEVELS] __initdata = { + [VDD_DIG_LOW] = 128000000, + [VDD_DIG_NOMINAL] = 266667000 +}; + +static struct bank_masks bmnd_info_mdp = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = MDP_MD0_REG, + .ns_mask = BM(29, 22) | BM(5, 3), + .rst_mask = BIT(31), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = MDP_MD1_REG, + .ns_mask = BM(21, 14) | BM(2, 0), + .rst_mask = BIT(30), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk mdp_clk = { + .b = { + .ctl_reg = MDP_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(21), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 10, + .retain_reg = MDP_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = MDP_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_mdp, + .bank_info = &bmnd_info_mdp, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 96000000, NOMINAL, 200000000), + CLK_INIT(mdp_clk.c), + .depends = &mdp_axi_clk.c, + }, +}; + +static struct branch_clk lut_mdp_clk = { + .b = { + .ctl_reg = MDP_LUT_CC_REG, + .en_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 13, + .retain_reg = MDP_LUT_CC_REG, + .retain_mask = BIT(31), + }, + .parent = &mdp_clk.c, + .c = { + .dbg_name = "lut_mdp_clk", + .ops = &clk_ops_branch, + CLK_INIT(lut_mdp_clk.c), + }, +}; + +#define F_MDP_VSYNC(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(13, 13, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_mdp_vsync[] = { + F_MDP_VSYNC(27000000, pxo), + F_END +}; + +static struct rcg_clk mdp_vsync_clk = { + .b = { + .ctl_reg = MISC_CC_REG, + .en_mask = BIT(6), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(3), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 22, + }, + .ns_reg = MISC_CC2_REG, + .ns_mask = BIT(13), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_mdp_vsync, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_vsync_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 27000000), + CLK_INIT(mdp_vsync_clk.c), + }, +}; + +#define F_ROT(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC_BANKED(29, 26, 25, 22, d, \ + 21, 19, 18, 16, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_rot[] = { + F_ROT( 0, gnd, 1), + F_ROT( 27000000, pxo, 1), + F_ROT( 29540000, pll8, 13), + F_ROT( 32000000, pll8, 12), + F_ROT( 38400000, pll8, 10), + F_ROT( 48000000, pll8, 8), + F_ROT( 54860000, pll8, 7), + F_ROT( 64000000, pll8, 6), + F_ROT( 76800000, pll8, 5), + F_ROT( 96000000, pll8, 4), + F_ROT(100000000, pll2, 8), + F_ROT(114290000, pll2, 7), + F_ROT(133330000, pll2, 6), + F_ROT(160000000, pll2, 5), + F_ROT(200000000, pll2, 4), + F_END +}; + +static struct bank_masks bdiv_info_rot = { + .bank_sel_mask = BIT(30), + .bank0_mask = { + .ns_mask = BM(25, 22) | BM(18, 16), + }, + .bank1_mask = { + .ns_mask = BM(29, 26) | BM(21, 19), + }, +}; + +static struct rcg_clk rot_clk = { + .b = { + .ctl_reg = ROT_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(2), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 15, + .retain_reg = ROT_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = ROT_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_div_banked, + .freq_tbl = clk_tbl_rot, + .bank_info = &bdiv_info_rot, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "rot_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 96000000, NOMINAL, 200000000), + CLK_INIT(rot_clk.c), + .depends = &rot_axi_clk.c, + }, +}; + +static int hdmi_pll_clk_enable(struct clk *clk) +{ + int ret; + unsigned long flags; + spin_lock_irqsave(&local_clock_reg_lock, flags); + ret = hdmi_pll_enable(); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + return ret; +} + +static void hdmi_pll_clk_disable(struct clk *clk) +{ + unsigned long flags; + spin_lock_irqsave(&local_clock_reg_lock, flags); + hdmi_pll_disable(); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +static unsigned long hdmi_pll_clk_get_rate(struct clk *clk) +{ + return hdmi_pll_get_rate(); +} + +static struct clk *hdmi_pll_clk_get_parent(struct clk *clk) +{ + return &pxo_clk.c; +} + +static struct clk_ops clk_ops_hdmi_pll = { + .enable = hdmi_pll_clk_enable, + .disable = hdmi_pll_clk_disable, + .get_rate = hdmi_pll_clk_get_rate, + .get_parent = hdmi_pll_clk_get_parent, +}; + +static struct clk hdmi_pll_clk = { + .dbg_name = "hdmi_pll_clk", + .ops = &clk_ops_hdmi_pll, + CLK_INIT(hdmi_pll_clk), +}; + +#define F_TV_GND(f, s, p_r, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +#define F_TV(f, s, p_r, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + .extra_freq_data = (void *)p_r, \ + } +/* Switching TV freqs requires PLL reconfiguration. */ +static struct clk_freq_tbl clk_tbl_tv[] = { + F_TV_GND( 0, gnd, 0, 1, 0, 0), + F_TV( 25200000, hdmi_pll, 25200000, 1, 0, 0), + F_TV( 27000000, hdmi_pll, 27000000, 1, 0, 0), + F_TV( 27030000, hdmi_pll, 27030000, 1, 0, 0), + F_TV( 74250000, hdmi_pll, 74250000, 1, 0, 0), + F_TV(148500000, hdmi_pll, 148500000, 1, 0, 0), + F_END +}; + +static unsigned long fmax_tv_src_8064[MAX_VDD_LEVELS] __initdata = { + [VDD_DIG_LOW] = 74250000, + [VDD_DIG_NOMINAL] = 149000000 +}; + +/* + * Unlike other clocks, the TV rate is adjusted through PLL + * re-programming. It is also routed through an MND divider. + */ +void set_rate_tv(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + unsigned long pll_rate = (unsigned long)nf->extra_freq_data; + if (pll_rate) + hdmi_pll_set_rate(pll_rate); + set_rate_mnd(clk, nf); +} + +static struct rcg_clk tv_src_clk = { + .ns_reg = TV_NS_REG, + .b = { + .ctl_reg = TV_CC_REG, + .halt_check = NOCHECK, + .retain_reg = TV_CC_REG, + .retain_mask = BIT(31), + }, + .md_reg = TV_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(23, 16) | BM(15, 14) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_tv, + .freq_tbl = clk_tbl_tv, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "tv_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 27030000, NOMINAL, 149000000), + CLK_INIT(tv_src_clk.c), + }, +}; + +static struct cdiv_clk tv_src_div_clk = { + .b = { + .ctl_reg = TV_NS_REG, + .halt_check = NOCHECK, + }, + .ns_reg = TV_NS_REG, + .div_offset = 6, + .max_div = 2, + .c = { + .dbg_name = "tv_src_div_clk", + .ops = &clk_ops_cdiv, + CLK_INIT(tv_src_div_clk.c), + }, +}; + +static struct branch_clk tv_enc_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(8), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 9, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "tv_enc_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_enc_clk.c), + }, +}; + +static struct branch_clk tv_dac_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(10), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 10, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "tv_dac_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_dac_clk.c), + }, +}; + +static struct branch_clk mdp_tv_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(4), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 12, + .retain_reg = TV_CC2_REG, + .retain_mask = BIT(10), + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "mdp_tv_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_tv_clk.c), + }, +}; + +static struct branch_clk hdmi_tv_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(1), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 11, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "hdmi_tv_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_tv_clk.c), + }, +}; + +static struct branch_clk rgb_tv_clk = { + .b = { + .ctl_reg = TV_CC2_REG, + .en_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 27, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "rgb_tv_clk", + .ops = &clk_ops_branch, + CLK_INIT(rgb_tv_clk.c), + }, +}; + +static struct branch_clk npl_tv_clk = { + .b = { + .ctl_reg = TV_CC2_REG, + .en_mask = BIT(16), + .halt_reg = DBG_BUS_VEC_J_REG, + .halt_bit = 26, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "npl_tv_clk", + .ops = &clk_ops_branch, + CLK_INIT(npl_tv_clk.c), + }, +}; + +static struct branch_clk hdmi_app_clk = { + .b = { + .ctl_reg = MISC_CC2_REG, + .en_mask = BIT(11), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(11), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 25, + }, + .c = { + .dbg_name = "hdmi_app_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_app_clk.c), + }, +}; + +static struct bank_masks bmnd_info_vcodec = { + .bank_sel_mask = BIT(13), + .bank0_mask = { + .md_reg = VCODEC_MD0_REG, + .ns_mask = BM(18, 11) | BM(2, 0), + .rst_mask = BIT(31), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, + .bank1_mask = { + .md_reg = VCODEC_MD1_REG, + .ns_mask = BM(26, 19) | BM(29, 27), + .rst_mask = BIT(30), + .mnd_en_mask = BIT(10), + .mode_mask = BM(12, 11), + }, +}; +#define F_VCODEC(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MND_BANKED8(11, 19, n, m, 0, 27, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(6, 11, n), \ + } +static struct clk_freq_tbl clk_tbl_vcodec[] = { + F_VCODEC( 0, gnd, 0, 0), + F_VCODEC( 27000000, pxo, 0, 0), + F_VCODEC( 32000000, pll8, 1, 12), + F_VCODEC( 48000000, pll8, 1, 8), + F_VCODEC( 54860000, pll8, 1, 7), + F_VCODEC( 96000000, pll8, 1, 4), + F_VCODEC(133330000, pll2, 1, 6), + F_VCODEC(200000000, pll2, 1, 4), + F_VCODEC(228570000, pll2, 2, 7), + F_END +}; + +static struct rcg_clk vcodec_clk = { + .b = { + .ctl_reg = VCODEC_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(6), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 29, + .retain_reg = VCODEC_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = VCODEC_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .bank_info = &bmnd_info_vcodec, + .freq_tbl = clk_tbl_vcodec, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vcodec_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 100000000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(vcodec_clk.c), + .depends = &vcodec_axi_clk.c, + }, +}; + +#define F_VPE(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(15, 12, d, 2, 0, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_vpe[] = { + F_VPE( 0, gnd, 1), + F_VPE( 27000000, pxo, 1), + F_VPE( 34909000, pll8, 11), + F_VPE( 38400000, pll8, 10), + F_VPE( 64000000, pll8, 6), + F_VPE( 76800000, pll8, 5), + F_VPE( 96000000, pll8, 4), + F_VPE(100000000, pll2, 8), + F_VPE(160000000, pll2, 5), + F_END +}; + +static struct rcg_clk vpe_clk = { + .b = { + .ctl_reg = VPE_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(17), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 28, + .retain_reg = VPE_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = VPE_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(15, 12) | BM(2, 0)), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_vpe, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vpe_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 76800000, NOMINAL, 160000000), + CLK_INIT(vpe_clk.c), + .depends = &vpe_axi_clk.c, + }, +}; + +#define F_VFE(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 11, 10, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } + +static struct clk_freq_tbl clk_tbl_vfe[] = { + F_VFE( 0, gnd, 1, 0, 0), + F_VFE( 13960000, pll8, 1, 2, 55), + F_VFE( 27000000, pxo, 1, 0, 0), + F_VFE( 36570000, pll8, 1, 2, 21), + F_VFE( 38400000, pll8, 2, 1, 5), + F_VFE( 45180000, pll8, 1, 2, 17), + F_VFE( 48000000, pll8, 2, 1, 4), + F_VFE( 54860000, pll8, 1, 1, 7), + F_VFE( 64000000, pll8, 2, 1, 3), + F_VFE( 76800000, pll8, 1, 1, 5), + F_VFE( 96000000, pll8, 2, 1, 2), + F_VFE(109710000, pll8, 1, 2, 7), + F_VFE(128000000, pll8, 1, 1, 3), + F_VFE(153600000, pll8, 1, 2, 5), + F_VFE(200000000, pll2, 2, 1, 2), + F_VFE(228570000, pll2, 1, 2, 7), + F_VFE(266667000, pll2, 1, 1, 3), + F_VFE(320000000, pll2, 1, 2, 5), + F_END +}; + +static unsigned long fmax_vfe_8064[MAX_VDD_LEVELS] __initdata = { + [VDD_DIG_LOW] = 128000000, + [VDD_DIG_NOMINAL] = 266667000, + [VDD_DIG_HIGH] = 320000000 +}; + +static struct rcg_clk vfe_clk = { + .b = { + .ctl_reg = VFE_CC_REG, + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 6, + .en_mask = BIT(0), + .retain_reg = VFE_CC2_REG, + .retain_mask = BIT(31), + }, + .ns_reg = VFE_NS_REG, + .md_reg = VFE_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(23, 16) | BM(11, 10) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_vfe, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vfe_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 110000000, NOMINAL, 266667000, + HIGH, 320000000), + CLK_INIT(vfe_clk.c), + .depends = &vfe_axi_clk.c, + }, +}; + +static struct branch_clk csi_vfe_clk = { + .b = { + .ctl_reg = VFE_CC_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(24), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 8, + }, + .parent = &vfe_clk.c, + .c = { + .dbg_name = "csi_vfe_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi_vfe_clk.c), + }, +}; + +/* + * Low Power Audio Clocks + */ +#define F_AIF_OSR(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS(31, 24, n, m, 5, 4, 3, d, 2, 0, s##_to_lpa_mux), \ + } +static struct clk_freq_tbl clk_tbl_aif_osr[] = { + F_AIF_OSR( 0, gnd, 1, 0, 0), + F_AIF_OSR( 512000, pll4, 4, 1, 192), + F_AIF_OSR( 768000, pll4, 4, 1, 128), + F_AIF_OSR( 1024000, pll4, 4, 1, 96), + F_AIF_OSR( 1536000, pll4, 4, 1, 64), + F_AIF_OSR( 2048000, pll4, 4, 1, 48), + F_AIF_OSR( 3072000, pll4, 4, 1, 32), + F_AIF_OSR( 4096000, pll4, 4, 1, 24), + F_AIF_OSR( 6144000, pll4, 4, 1, 16), + F_AIF_OSR( 8192000, pll4, 4, 1, 12), + F_AIF_OSR(12288000, pll4, 4, 1, 8), + F_AIF_OSR(24576000, pll4, 4, 1, 4), + F_END +}; + +#define CLK_AIF_OSR(i, ns, md, h_r) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(17), \ + .reset_reg = ns, \ + .reset_mask = BIT(19), \ + .halt_reg = h_r, \ + .halt_check = ENABLE, \ + .halt_bit = 1, \ + }, \ + .ns_reg = ns, \ + .md_reg = md, \ + .root_en_mask = BIT(9), \ + .ns_mask = (BM(31, 24) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_aif_osr, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(LOW, 24576000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define CLK_AIF_OSR_DIV(i, ns, md, h_r) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(21), \ + .reset_reg = ns, \ + .reset_mask = BIT(23), \ + .halt_reg = h_r, \ + .halt_check = ENABLE, \ + .halt_bit = 1, \ + }, \ + .ns_reg = ns, \ + .md_reg = md, \ + .root_en_mask = BIT(9), \ + .ns_mask = (BM(31, 24) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_aif_osr, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(LOW, 24576000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +#define CLK_AIF_BIT(i, ns, h_r) \ + struct cdiv_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(15), \ + .halt_reg = h_r, \ + .halt_check = DELAY, \ + }, \ + .ns_reg = ns, \ + .ext_mask = BIT(14), \ + .div_offset = 10, \ + .max_div = 16, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_cdiv, \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +#define CLK_AIF_BIT_DIV(i, ns, h_r) \ + struct cdiv_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(19), \ + .halt_reg = h_r, \ + .halt_check = DELAY, \ + }, \ + .ns_reg = ns, \ + .ext_mask = BIT(18), \ + .div_offset = 10, \ + .max_div = 256, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_cdiv, \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +static CLK_AIF_OSR(mi2s_osr, LCC_MI2S_NS_REG, LCC_MI2S_MD_REG, + LCC_MI2S_STATUS_REG); +static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG); + +static CLK_AIF_OSR_DIV(codec_i2s_mic_osr, LCC_CODEC_I2S_MIC_NS_REG, + LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG); +static CLK_AIF_BIT_DIV(codec_i2s_mic_bit, LCC_CODEC_I2S_MIC_NS_REG, + LCC_CODEC_I2S_MIC_STATUS_REG); + +static CLK_AIF_OSR_DIV(spare_i2s_mic_osr, LCC_SPARE_I2S_MIC_NS_REG, + LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG); +static CLK_AIF_BIT_DIV(spare_i2s_mic_bit, LCC_SPARE_I2S_MIC_NS_REG, + LCC_SPARE_I2S_MIC_STATUS_REG); + +static CLK_AIF_OSR_DIV(codec_i2s_spkr_osr, LCC_CODEC_I2S_SPKR_NS_REG, + LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG); +static CLK_AIF_BIT_DIV(codec_i2s_spkr_bit, LCC_CODEC_I2S_SPKR_NS_REG, + LCC_CODEC_I2S_SPKR_STATUS_REG); + +static CLK_AIF_OSR_DIV(spare_i2s_spkr_osr, LCC_SPARE_I2S_SPKR_NS_REG, + LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG); +static CLK_AIF_BIT_DIV(spare_i2s_spkr_bit, LCC_SPARE_I2S_SPKR_NS_REG, + LCC_SPARE_I2S_SPKR_STATUS_REG); + +#define F_PCM(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_lpa_mux), \ + } +static struct clk_freq_tbl clk_tbl_pcm[] = { + { .ns_val = BIT(10) /* external input */ }, + F_PCM( 512000, pll4, 4, 1, 192), + F_PCM( 768000, pll4, 4, 1, 128), + F_PCM( 1024000, pll4, 4, 1, 96), + F_PCM( 1536000, pll4, 4, 1, 64), + F_PCM( 2048000, pll4, 4, 1, 48), + F_PCM( 3072000, pll4, 4, 1, 32), + F_PCM( 4096000, pll4, 4, 1, 24), + F_PCM( 6144000, pll4, 4, 1, 16), + F_PCM( 8192000, pll4, 4, 1, 12), + F_PCM(12288000, pll4, 4, 1, 8), + F_PCM(24576000, pll4, 4, 1, 4), + F_END +}; + +static struct rcg_clk pcm_clk = { + .b = { + .ctl_reg = LCC_PCM_NS_REG, + .en_mask = BIT(11), + .reset_reg = LCC_PCM_NS_REG, + .reset_mask = BIT(13), + .halt_reg = LCC_PCM_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 0, + }, + .ns_reg = LCC_PCM_NS_REG, + .md_reg = LCC_PCM_MD_REG, + .root_en_mask = BIT(9), + .ns_mask = BM(31, 16) | BIT(10) | BM(6, 0), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_pcm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pcm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 24576000), + CLK_INIT(pcm_clk.c), + }, +}; + +static struct rcg_clk audio_slimbus_clk = { + .b = { + .ctl_reg = LCC_SLIMBUS_NS_REG, + .en_mask = BIT(10), + .reset_reg = LCC_AHBEX_BRANCH_CTL_REG, + .reset_mask = BIT(5), + .halt_reg = LCC_SLIMBUS_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 0, + }, + .ns_reg = LCC_SLIMBUS_NS_REG, + .md_reg = LCC_SLIMBUS_MD_REG, + .root_en_mask = BIT(9), + .ns_mask = (BM(31, 24) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_aif_osr, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "audio_slimbus_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 24576000), + CLK_INIT(audio_slimbus_clk.c), + }, +}; + +static struct branch_clk sps_slimbus_clk = { + .b = { + .ctl_reg = LCC_SLIMBUS_NS_REG, + .en_mask = BIT(12), + .halt_reg = LCC_SLIMBUS_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 1, + }, + .parent = &audio_slimbus_clk.c, + .c = { + .dbg_name = "sps_slimbus_clk", + .ops = &clk_ops_branch, + CLK_INIT(sps_slimbus_clk.c), + }, +}; + +static struct branch_clk slimbus_xo_src_clk = { + .b = { + .ctl_reg = SLIMBUS_XO_SRC_CLK_CTL_REG, + .en_mask = BIT(2), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 28, + }, + .parent = &sps_slimbus_clk.c, + .c = { + .dbg_name = "slimbus_xo_src_clk", + .ops = &clk_ops_branch, + CLK_INIT(slimbus_xo_src_clk.c), + }, +}; + +DEFINE_CLK_RPM(afab_clk, afab_a_clk, APPS_FABRIC, NULL); +DEFINE_CLK_RPM(cfpb_clk, cfpb_a_clk, CFPB, NULL); +DEFINE_CLK_RPM(dfab_clk, dfab_a_clk, DAYTONA_FABRIC, NULL); +DEFINE_CLK_RPM(ebi1_clk, ebi1_a_clk, EBI1, NULL); +DEFINE_CLK_RPM(mmfab_clk, mmfab_a_clk, MM_FABRIC, NULL); +DEFINE_CLK_RPM(mmfpb_clk, mmfpb_a_clk, MMFPB, NULL); +DEFINE_CLK_RPM(sfab_clk, sfab_a_clk, SYSTEM_FABRIC, NULL); +DEFINE_CLK_RPM(sfpb_clk, sfpb_a_clk, SFPB, NULL); + +static DEFINE_CLK_VOTER(sfab_msmbus_a_clk, &sfab_a_clk.c, 0); +static DEFINE_CLK_VOTER(sfab_tmr_a_clk, &sfab_a_clk.c, 0); + +static DEFINE_CLK_VOTER(dfab_dsps_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_usb_hs_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_usb_hs3_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_usb_hs4_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc1_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc2_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc3_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc4_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc5_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sps_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_bam_dmux_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_scm_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_qseecom_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_tzcom_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_msmbus_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_msmbus_a_clk, &dfab_a_clk.c, 0); + +static DEFINE_CLK_VOTER(ebi1_msmbus_clk, &ebi1_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_adm_clk, &ebi1_clk.c, 0); + +static DEFINE_CLK_VOTER(ebi1_acpu_a_clk, &ebi1_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_msmbus_a_clk, &ebi1_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(afab_acpu_a_clk, &afab_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(afab_msmbus_a_clk, &afab_a_clk.c, LONG_MAX); + +#ifdef CONFIG_DEBUG_FS +struct measure_sel { + u32 test_vector; + struct clk *clk; +}; + +static DEFINE_CLK_MEASURE(l2_m_clk); +static DEFINE_CLK_MEASURE(krait0_m_clk); +static DEFINE_CLK_MEASURE(krait1_m_clk); +static DEFINE_CLK_MEASURE(krait2_m_clk); +static DEFINE_CLK_MEASURE(krait3_m_clk); +static DEFINE_CLK_MEASURE(q6sw_clk); +static DEFINE_CLK_MEASURE(q6fw_clk); +static DEFINE_CLK_MEASURE(q6_func_clk); + +static struct measure_sel measure_mux[] = { + { TEST_PER_LS(0x08), &slimbus_xo_src_clk.c }, + { TEST_PER_LS(0x12), &sdc1_p_clk.c }, + { TEST_PER_LS(0x13), &sdc1_clk.c }, + { TEST_PER_LS(0x14), &sdc2_p_clk.c }, + { TEST_PER_LS(0x15), &sdc2_clk.c }, + { TEST_PER_LS(0x16), &sdc3_p_clk.c }, + { TEST_PER_LS(0x17), &sdc3_clk.c }, + { TEST_PER_LS(0x18), &sdc4_p_clk.c }, + { TEST_PER_LS(0x19), &sdc4_clk.c }, + { TEST_PER_LS(0x1A), &sdc5_p_clk.c }, + { TEST_PER_LS(0x1B), &sdc5_clk.c }, + { TEST_PER_LS(0x1F), &gp0_clk.c }, + { TEST_PER_LS(0x20), &gp1_clk.c }, + { TEST_PER_LS(0x21), &gp2_clk.c }, + { TEST_PER_LS(0x25), &dfab_clk.c }, + { TEST_PER_LS(0x25), &dfab_a_clk.c }, + { TEST_PER_LS(0x26), &pmem_clk.c }, + { TEST_PER_LS(0x32), &dma_bam_p_clk.c }, + { TEST_PER_LS(0x33), &cfpb_clk.c }, + { TEST_PER_LS(0x33), &cfpb_a_clk.c }, + { TEST_PER_LS(0x3D), &gsbi1_p_clk.c }, + { TEST_PER_LS(0x3E), &gsbi1_uart_clk.c }, + { TEST_PER_LS(0x3F), &gsbi1_qup_clk.c }, + { TEST_PER_LS(0x41), &gsbi2_p_clk.c }, + { TEST_PER_LS(0x42), &gsbi2_uart_clk.c }, + { TEST_PER_LS(0x44), &gsbi2_qup_clk.c }, + { TEST_PER_LS(0x45), &gsbi3_p_clk.c }, + { TEST_PER_LS(0x46), &gsbi3_uart_clk.c }, + { TEST_PER_LS(0x48), &gsbi3_qup_clk.c }, + { TEST_PER_LS(0x49), &gsbi4_p_clk.c }, + { TEST_PER_LS(0x4A), &gsbi4_uart_clk.c }, + { TEST_PER_LS(0x4C), &gsbi4_qup_clk.c }, + { TEST_PER_LS(0x4D), &gsbi5_p_clk.c }, + { TEST_PER_LS(0x4E), &gsbi5_uart_clk.c }, + { TEST_PER_LS(0x50), &gsbi5_qup_clk.c }, + { TEST_PER_LS(0x51), &gsbi6_p_clk.c }, + { TEST_PER_LS(0x52), &gsbi6_uart_clk.c }, + { TEST_PER_LS(0x54), &gsbi6_qup_clk.c }, + { TEST_PER_LS(0x55), &gsbi7_p_clk.c }, + { TEST_PER_LS(0x56), &gsbi7_uart_clk.c }, + { TEST_PER_LS(0x58), &gsbi7_qup_clk.c }, + { TEST_PER_LS(0x59), &gsbi8_p_clk.c }, + { TEST_PER_LS(0x59), &sfab_sata_s_p_clk.c }, + { TEST_PER_LS(0x5A), &gsbi8_uart_clk.c }, + { TEST_PER_LS(0x5A), &sata_p_clk.c }, + { TEST_PER_LS(0x5B), &sata_rxoob_clk.c }, + { TEST_PER_LS(0x5C), &sata_pmalive_clk.c }, + { TEST_PER_LS(0x5C), &gsbi8_qup_clk.c }, + { TEST_PER_LS(0x5D), &gsbi9_p_clk.c }, + { TEST_PER_LS(0x5E), &gsbi9_uart_clk.c }, + { TEST_PER_LS(0x60), &gsbi9_qup_clk.c }, + { TEST_PER_LS(0x61), &gsbi10_p_clk.c }, + { TEST_PER_LS(0x62), &gsbi10_uart_clk.c }, + { TEST_PER_LS(0x64), &gsbi10_qup_clk.c }, + { TEST_PER_LS(0x65), &gsbi11_p_clk.c }, + { TEST_PER_LS(0x66), &gsbi11_uart_clk.c }, + { TEST_PER_LS(0x68), &gsbi11_qup_clk.c }, + { TEST_PER_LS(0x69), &gsbi12_p_clk.c }, + { TEST_PER_LS(0x6A), &gsbi12_uart_clk.c }, + { TEST_PER_LS(0x6C), &gsbi12_qup_clk.c }, + { TEST_PER_LS(0x5E), &pcie_p_clk.c }, + { TEST_PER_LS(0x5F), &ce3_p_clk.c }, + { TEST_PER_LS(0x60), &ce3_core_clk.c }, + { TEST_PER_LS(0x63), &usb_hs3_p_clk.c }, + { TEST_PER_LS(0x64), &usb_hs3_xcvr_clk.c }, + { TEST_PER_LS(0x65), &usb_hs4_p_clk.c }, + { TEST_PER_LS(0x66), &usb_hs4_xcvr_clk.c }, + { TEST_PER_LS(0x6B), &sata_phy_ref_clk.c }, + { TEST_PER_LS(0x6C), &sata_phy_cfg_clk.c }, + { TEST_PER_LS(0x78), &sfpb_clk.c }, + { TEST_PER_LS(0x78), &sfpb_a_clk.c }, + { TEST_PER_LS(0x7A), &pmic_ssbi2_clk.c }, + { TEST_PER_LS(0x7B), &pmic_arb0_p_clk.c }, + { TEST_PER_LS(0x7C), &pmic_arb1_p_clk.c }, + { TEST_PER_LS(0x7D), &prng_clk.c }, + { TEST_PER_LS(0x7F), &rpm_msg_ram_p_clk.c }, + { TEST_PER_LS(0x80), &adm0_p_clk.c }, + { TEST_PER_LS(0x84), &usb_hs1_p_clk.c }, + { TEST_PER_LS(0x85), &usb_hs1_xcvr_clk.c }, + { TEST_PER_LS(0x86), &usb_hsic_p_clk.c }, + { TEST_PER_LS(0x87), &usb_hsic_system_clk.c }, + { TEST_PER_LS(0x88), &usb_hsic_xcvr_fs_clk.c }, + { TEST_PER_LS(0x89), &usb_fs1_p_clk.c }, + { TEST_PER_LS(0x8A), &usb_fs1_sys_clk.c }, + { TEST_PER_LS(0x8B), &usb_fs1_xcvr_clk.c }, + { TEST_PER_LS(0x8C), &usb_fs2_p_clk.c }, + { TEST_PER_LS(0x8D), &usb_fs2_sys_clk.c }, + { TEST_PER_LS(0x8E), &usb_fs2_xcvr_clk.c }, + { TEST_PER_LS(0x8F), &tsif_p_clk.c }, + { TEST_PER_LS(0x91), &tsif_ref_clk.c }, + { TEST_PER_LS(0x92), &ce1_p_clk.c }, + { TEST_PER_LS(0x94), &tssc_clk.c }, + { TEST_PER_LS(0x9D), &usb_hsic_hsio_cal_clk.c }, + { TEST_PER_LS(0xA4), &ce1_core_clk.c }, + + { TEST_PER_HS(0x07), &afab_clk.c }, + { TEST_PER_HS(0x07), &afab_a_clk.c }, + { TEST_PER_HS(0x18), &sfab_clk.c }, + { TEST_PER_HS(0x18), &sfab_a_clk.c }, + { TEST_PER_HS(0x26), &q6sw_clk }, + { TEST_PER_HS(0x27), &q6fw_clk }, + { TEST_PER_HS(0x2A), &adm0_clk.c }, + { TEST_PER_HS(0x31), &sata_a_clk.c }, + { TEST_PER_HS(0x2D), &pcie_phy_ref_clk.c }, + { TEST_PER_HS(0x32), &pcie_a_clk.c }, + { TEST_PER_HS(0x34), &ebi1_clk.c }, + { TEST_PER_HS(0x34), &ebi1_a_clk.c }, + { TEST_PER_HS(0x50), &usb_hsic_hsic_clk.c }, + + { TEST_MM_LS(0x00), &dsi1_byte_clk.c }, + { TEST_MM_LS(0x01), &dsi2_byte_clk.c }, + { TEST_MM_LS(0x02), &cam1_clk.c }, + { TEST_MM_LS(0x06), &_p_clk.c }, + { TEST_MM_LS(0x07), &csi_p_clk.c }, + { TEST_MM_LS(0x08), &dsi2_s_p_clk.c }, + { TEST_MM_LS(0x09), &dsi1_m_p_clk.c }, + { TEST_MM_LS(0x0A), &dsi1_s_p_clk.c }, + { TEST_MM_LS(0x0C), &gfx2d0_p_clk.c }, + { TEST_MM_LS(0x0D), &gfx2d1_p_clk.c }, + { TEST_MM_LS(0x0E), &gfx3d_p_clk.c }, + { TEST_MM_LS(0x0F), &hdmi_m_p_clk.c }, + { TEST_MM_LS(0x10), &hdmi_s_p_clk.c }, + { TEST_MM_LS(0x11), &ijpeg_p_clk.c }, + { TEST_MM_LS(0x12), &imem_p_clk.c }, + { TEST_MM_LS(0x13), &jpegd_p_clk.c }, + { TEST_MM_LS(0x14), &mdp_p_clk.c }, + { TEST_MM_LS(0x16), &rot_p_clk.c }, + { TEST_MM_LS(0x17), &dsi1_esc_clk.c }, + { TEST_MM_LS(0x18), &smmu_p_clk.c }, + { TEST_MM_LS(0x19), &tv_enc_p_clk.c }, + { TEST_MM_LS(0x1A), &vcodec_p_clk.c }, + { TEST_MM_LS(0x1B), &vfe_p_clk.c }, + { TEST_MM_LS(0x1C), &vpe_p_clk.c }, + { TEST_MM_LS(0x1D), &cam0_clk.c }, + { TEST_MM_LS(0x1F), &hdmi_app_clk.c }, + { TEST_MM_LS(0x20), &mdp_vsync_clk.c }, + { TEST_MM_LS(0x21), &tv_dac_clk.c }, + { TEST_MM_LS(0x22), &tv_enc_clk.c }, + { TEST_MM_LS(0x23), &dsi2_esc_clk.c }, + { TEST_MM_LS(0x25), &mmfpb_clk.c }, + { TEST_MM_LS(0x25), &mmfpb_a_clk.c }, + { TEST_MM_LS(0x26), &dsi2_m_p_clk.c }, + { TEST_MM_LS(0x27), &cam2_clk.c }, + { TEST_MM_LS(0x28), &vcap_p_clk.c }, + + { TEST_MM_HS(0x00), &csi0_clk.c }, + { TEST_MM_HS(0x01), &csi1_clk.c }, + { TEST_MM_HS(0x04), &csi_vfe_clk.c }, + { TEST_MM_HS(0x05), &ijpeg_clk.c }, + { TEST_MM_HS(0x06), &vfe_clk.c }, + { TEST_MM_HS(0x07), &gfx2d0_clk.c }, + { TEST_MM_HS(0x08), &gfx2d1_clk.c }, + { TEST_MM_HS(0x09), &gfx3d_clk.c }, + { TEST_MM_HS(0x0A), &jpegd_clk.c }, + { TEST_MM_HS(0x0B), &vcodec_clk.c }, + { TEST_MM_HS(0x0F), &mmfab_clk.c }, + { TEST_MM_HS(0x0F), &mmfab_a_clk.c }, + { TEST_MM_HS(0x11), &gmem_axi_clk.c }, + { TEST_MM_HS(0x12), &ijpeg_axi_clk.c }, + { TEST_MM_HS(0x13), &imem_axi_clk.c }, + { TEST_MM_HS(0x14), &jpegd_axi_clk.c }, + { TEST_MM_HS(0x15), &mdp_axi_clk.c }, + { TEST_MM_HS(0x16), &rot_axi_clk.c }, + { TEST_MM_HS(0x17), &vcodec_axi_clk.c }, + { TEST_MM_HS(0x18), &vfe_axi_clk.c }, + { TEST_MM_HS(0x19), &vpe_axi_clk.c }, + { TEST_MM_HS(0x1A), &mdp_clk.c }, + { TEST_MM_HS(0x1B), &rot_clk.c }, + { TEST_MM_HS(0x1C), &vpe_clk.c }, + { TEST_MM_HS(0x1E), &hdmi_tv_clk.c }, + { TEST_MM_HS(0x1F), &mdp_tv_clk.c }, + { TEST_MM_HS(0x24), &csi0_phy_clk.c }, + { TEST_MM_HS(0x25), &csi1_phy_clk.c }, + { TEST_MM_HS(0x26), &csi_pix_clk.c }, + { TEST_MM_HS(0x27), &csi_rdi_clk.c }, + { TEST_MM_HS(0x28), &lut_mdp_clk.c }, + { TEST_MM_HS(0x29), &vcodec_axi_a_clk.c }, + { TEST_MM_HS(0x2A), &vcodec_axi_b_clk.c }, + { TEST_MM_HS(0x2B), &csi1phy_timer_clk.c }, + { TEST_MM_HS(0x2C), &csi0phy_timer_clk.c }, + { TEST_MM_HS(0x2D), &csi2_clk.c }, + { TEST_MM_HS(0x2E), &csi2_phy_clk.c }, + { TEST_MM_HS(0x2F), &csi2phy_timer_clk.c }, + { TEST_MM_HS(0x30), &csi_pix1_clk.c }, + { TEST_MM_HS(0x31), &csi_rdi1_clk.c }, + { TEST_MM_HS(0x32), &csi_rdi2_clk.c }, + { TEST_MM_HS(0x33), &vcap_clk.c }, + { TEST_MM_HS(0x34), &vcap_npl_clk.c }, + { TEST_MM_HS(0x34), &gfx3d_axi_clk_8930.c }, + { TEST_MM_HS(0x35), &vcap_axi_clk.c }, + { TEST_MM_HS(0x36), &rgb_tv_clk.c }, + { TEST_MM_HS(0x37), &npl_tv_clk.c }, + { TEST_MM_HS(0x38), &gfx3d_axi_clk_8064.c }, + + { TEST_LPA(0x0F), &mi2s_bit_clk.c }, + { TEST_LPA(0x10), &codec_i2s_mic_bit_clk.c }, + { TEST_LPA(0x11), &codec_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x12), &spare_i2s_mic_bit_clk.c }, + { TEST_LPA(0x13), &spare_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x14), &pcm_clk.c }, + { TEST_LPA(0x1D), &audio_slimbus_clk.c }, + + { TEST_LPA_HS(0x00), &q6_func_clk }, + + { TEST_CPUL2(0x2), &l2_m_clk }, + { TEST_CPUL2(0x0), &krait0_m_clk }, + { TEST_CPUL2(0x1), &krait1_m_clk }, + { TEST_CPUL2(0x4), &krait2_m_clk }, + { TEST_CPUL2(0x5), &krait3_m_clk }, +}; + +static struct measure_sel *find_measure_sel(struct clk *clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(measure_mux); i++) + if (measure_mux[i].clk == clk) + return &measure_mux[i]; + return NULL; +} + +static int measure_clk_set_parent(struct clk *c, struct clk *parent) +{ + int ret = 0; + u32 clk_sel; + struct measure_sel *p; + struct measure_clk *clk = to_measure_clk(c); + unsigned long flags; + + if (!parent) + return -EINVAL; + + p = find_measure_sel(parent); + if (!p) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* + * Program the test vector, measurement period (sample_ticks) + * and scaling multiplier. + */ + clk->sample_ticks = 0x10000; + clk_sel = p->test_vector & TEST_CLK_SEL_MASK; + clk->multiplier = 1; + switch (p->test_vector >> TEST_TYPE_SHIFT) { + case TEST_TYPE_PER_LS: + writel_relaxed(0x4030D00|BVAL(7, 0, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_PER_HS: + writel_relaxed(0x4020000|BVAL(16, 10, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_MM_LS: + writel_relaxed(0x4030D97, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), DBG_CFG_REG_LS_REG); + break; + case TEST_TYPE_MM_HS: + writel_relaxed(0x402B800, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), DBG_CFG_REG_HS_REG); + break; + case TEST_TYPE_LPA: + writel_relaxed(0x4030D98, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), + LCC_CLK_LS_DEBUG_CFG_REG); + break; + case TEST_TYPE_LPA_HS: + writel_relaxed(0x402BC00, CLK_TEST_REG); + writel_relaxed(BVAL(2, 1, clk_sel)|BIT(0), + LCC_CLK_HS_DEBUG_CFG_REG); + break; + case TEST_TYPE_CPUL2: + writel_relaxed(0x4030400, CLK_TEST_REG); + writel_relaxed(0x80|BVAL(5, 3, clk_sel), GCC_APCS_CLK_DIAG); + clk->sample_ticks = 0x4000; + clk->multiplier = 2; + break; + default: + ret = -EPERM; + } + /* Make sure test vector is set before starting measurements. */ + mb(); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} + +/* Sample clock for 'ticks' reference clock ticks. */ +static u32 run_measurement(unsigned ticks) +{ + /* Stop counters and set the XO4 counter start value. */ + writel_relaxed(ticks, RINGOSC_TCXO_CTL_REG); + + /* Wait for timer to become ready. */ + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) != 0) + cpu_relax(); + + /* Run measurement and wait for completion. */ + writel_relaxed(BIT(20)|ticks, RINGOSC_TCXO_CTL_REG); + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) == 0) + cpu_relax(); + + /* Stop counters. */ + writel_relaxed(0x0, RINGOSC_TCXO_CTL_REG); + + /* Return measured ticks. */ + return readl_relaxed(RINGOSC_STATUS_REG) & BM(24, 0); +} + + +/* Perform a hardware rate measurement for a given clock. + FOR DEBUG USE ONLY: Measurements take ~15 ms! */ +static unsigned long measure_clk_get_rate(struct clk *c) +{ + unsigned long flags; + u32 pdm_reg_backup, ringosc_reg_backup; + u64 raw_count_short, raw_count_full; + struct measure_clk *clk = to_measure_clk(c); + unsigned ret; + + ret = clk_prepare_enable(&cxo_clk.c); + if (ret) { + pr_warning("CXO clock failed to enable. Can't measure\n"); + return 0; + } + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable CXO/4 and RINGOSC branch and root. */ + pdm_reg_backup = readl_relaxed(PDM_CLK_NS_REG); + ringosc_reg_backup = readl_relaxed(RINGOSC_NS_REG); + writel_relaxed(0x2898, PDM_CLK_NS_REG); + writel_relaxed(0xA00, RINGOSC_NS_REG); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(clk->sample_ticks); + + writel_relaxed(ringosc_reg_backup, RINGOSC_NS_REG); + writel_relaxed(pdm_reg_backup, PDM_CLK_NS_REG); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, ((clk->sample_ticks * 10) + 35)); + ret = (raw_count_full * clk->multiplier); + } + + /* Route dbg_hs_clk to PLLTEST. 300mV single-ended amplitude. */ + writel_relaxed(0x38F8, PLLTEST_PAD_CFG_REG); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + clk_disable_unprepare(&cxo_clk.c); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned long measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops clk_ops_measure = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, +}; + +static struct measure_clk measure_clk = { + .c = { + .dbg_name = "measure_clk", + .ops = &clk_ops_measure, + CLK_INIT(measure_clk.c), + }, + .multiplier = 1, +}; + +static struct clk_lookup msm_clocks_8064[] = { + CLK_LOOKUP("xo", cxo_a_clk.c, ""), + CLK_LOOKUP("xo", pxo_a_clk.c, ""), + CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"), + CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"), + CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_gss"), + CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"), + CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"), + CLK_LOOKUP("pll2", pll2_clk.c, NULL), + CLK_LOOKUP("pll8", pll8_clk.c, NULL), + CLK_LOOKUP("pll4", pll4_clk.c, NULL), + CLK_LOOKUP("measure", measure_clk.c, "debug"), + + CLK_LOOKUP("bus_clk", afab_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_a_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_a_clk.c, ""), + + CLK_LOOKUP("bus_clk", afab_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_a_clk", afab_msmbus_a_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_clk", cfpb_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_a_clk", sfab_msmbus_a_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_clk", sfpb_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_clk", mmfab_clk.c, "msm_mm_fab"), + CLK_LOOKUP("bus_a_clk", mmfab_a_clk.c, "msm_mm_fab"), + CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("mem_a_clk", ebi1_msmbus_a_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_clk", dfab_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_a_clk", dfab_msmbus_a_clk.c, "msm_bus"), + + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, ""), + CLK_LOOKUP("mmfpb_clk", mmfpb_clk.c, ""), + CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, "clock-8960"), + CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, "clock-8960"), + + CLK_LOOKUP("core_clk", gp0_clk.c, ""), + CLK_LOOKUP("core_clk", gp1_clk.c, ""), + CLK_LOOKUP("core_clk", gp2_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi1_uart_clk.c, "msm_serial_hsl.1"), + CLK_LOOKUP("core_clk", gsbi2_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi5_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi6_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi7_uart_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", gsbi1_qup_clk.c, "qup_i2c.0"), + CLK_LOOKUP("core_clk", gsbi2_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_qup_clk.c, "qup_i2c.3"), + CLK_LOOKUP("core_clk", gsbi4_qup_clk.c, "qup_i2c.4"), + CLK_LOOKUP("core_clk", gsbi5_qup_clk.c, "spi_qsd.0"), + CLK_LOOKUP("core_clk", gsbi5_qup_clk.c, "qup_i2c.5"), + CLK_LOOKUP("core_clk", gsbi6_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi7_qup_clk.c, ""), + CLK_LOOKUP("core_clk", pdm_clk.c, ""), + CLK_LOOKUP("mem_clk", pmem_clk.c, "msm_sps"), + CLK_LOOKUP("core_clk", prng_clk.c, "msm_rng.0"), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, ""), + CLK_LOOKUP("core_clk", tssc_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_hs1_xcvr_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs3_xcvr_clk.c, "msm_ehci_host.0"), + CLK_LOOKUP("alt_core_clk", usb_hs4_xcvr_clk.c, "msm_ehci_host.1"), + CLK_LOOKUP("src_clk", usb_fs1_src_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_fs1_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs1_sys_clk.c, ""), + CLK_LOOKUP("ref_clk", sata_phy_ref_clk.c, ""), + CLK_LOOKUP("cfg_clk", sata_phy_cfg_clk.c, ""), + CLK_LOOKUP("src_clk", sata_src_clk.c, ""), + CLK_LOOKUP("core_rxoob_clk", sata_rxoob_clk.c, ""), + CLK_LOOKUP("core_pmalive_clk", sata_pmalive_clk.c, ""), + CLK_LOOKUP("bus_clk", sata_a_clk.c, ""), + CLK_LOOKUP("iface_clk", sata_p_clk.c, ""), + CLK_LOOKUP("slave_iface_clk", sfab_sata_s_p_clk.c, ""), + CLK_LOOKUP("iface_clk", ce3_p_clk.c, "qce.0"), + CLK_LOOKUP("iface_clk", ce3_p_clk.c, "qcrypto.0"), + CLK_LOOKUP("core_clk", ce3_core_clk.c, "qce.0"), + CLK_LOOKUP("core_clk", ce3_core_clk.c, "qcrypto.0"), + CLK_LOOKUP("ce3_core_src_clk", ce3_src_clk.c, "qce.0"), + CLK_LOOKUP("ce3_core_src_clk", ce3_src_clk.c, "qcrypto.0"), + CLK_LOOKUP("dma_bam_pclk", dma_bam_p_clk.c, NULL), + CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "msm_serial_hsl.1"), + CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "qup_i2c.0"), + CLK_LOOKUP("iface_clk", gsbi2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "qup_i2c.3"), + CLK_LOOKUP("iface_clk", gsbi4_p_clk.c, "qup_i2c.4"), + CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, "spi_qsd.0"), + CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, "qup_i2c.5"), + CLK_LOOKUP("iface_clk", gsbi6_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi7_p_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_fs1_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_hs1_p_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", usb_hs3_p_clk.c, "msm_ehci_host.0"), + CLK_LOOKUP("iface_clk", usb_hs4_p_clk.c, "msm_ehci_host.1"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", pcie_p_clk.c, "msm_pcie"), + CLK_LOOKUP("ref_clk", pcie_phy_ref_clk.c, "msm_pcie"), + CLK_LOOKUP("bus_clk", pcie_a_clk.c, "msm_pcie"), + CLK_LOOKUP("core_clk", adm0_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", adm0_p_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", pmic_arb0_p_clk.c, ""), + CLK_LOOKUP("iface_clk", pmic_arb1_p_clk.c, ""), + CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""), + CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-001a"), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0034"), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"), + CLK_LOOKUP("cam_clk", cam1_clk.c, "4-0048"), + CLK_LOOKUP("cam_clk", cam1_clk.c, "4-006c"), + CLK_LOOKUP("csi_src_clk", csi0_src_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_src_clk", csi1_src_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_clk", csi0_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_clk", csi2_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_phy_clk", csi0_phy_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_phy_clk", csi1_phy_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_phy_clk", csi2_phy_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_pix_clk", csi_pix_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_pix1_clk", csi_pix1_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi_clk", csi_rdi_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi1_clk", csi_rdi1_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi2_clk", csi_rdi2_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.0"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.1"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.2"), + CLK_LOOKUP("csiphy_timer_clk", csi0phy_timer_clk.c, "msm_csiphy.0"), + CLK_LOOKUP("csiphy_timer_clk", csi1phy_timer_clk.c, "msm_csiphy.1"), + CLK_LOOKUP("csiphy_timer_clk", csi2phy_timer_clk.c, "msm_csiphy.2"), + CLK_LOOKUP("byte_clk", dsi1_byte_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("byte_clk", dsi2_byte_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("esc_clk", dsi1_esc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("esc_clk", dsi2_esc_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("rgb_clk", rgb_tv_clk.c, ""), + CLK_LOOKUP("npl_clk", npl_tv_clk.c, ""), + + CLK_LOOKUP("core_clk", gfx3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("bus_clk", + gfx3d_axi_clk_8064.c, "footswitch-8x60.2"), + CLK_LOOKUP("iface_clk", vcap_p_clk.c, ""), + CLK_LOOKUP("iface_clk", vcap_p_clk.c, "msm_vcap.0"), + CLK_LOOKUP("iface_clk", vcap_p_clk.c, "footswitch-8x60.10"), + CLK_LOOKUP("bus_clk", vcap_axi_clk.c, "footswitch-8x60.10"), + CLK_LOOKUP("core_clk", vcap_clk.c, ""), + CLK_LOOKUP("core_clk", vcap_clk.c, "msm_vcap.0"), + CLK_LOOKUP("core_clk", vcap_clk.c, "footswitch-8x60.10"), + CLK_LOOKUP("vcap_npl_clk", vcap_npl_clk.c, ""), + CLK_LOOKUP("vcap_npl_clk", vcap_npl_clk.c, "msm_vcap.0"), + CLK_LOOKUP("bus_clk", ijpeg_axi_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("mem_clk", imem_axi_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("core_clk", jpegd_clk.c, ""), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("lut_clk", lut_mdp_clk.c, "mdp.0"), + CLK_LOOKUP("lut_clk", lut_mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("core_clk", rot_clk.c, "msm_rotator.0"), + CLK_LOOKUP("core_clk", rot_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("tv_src_clk", tv_src_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("src_clk", tv_src_clk.c, "dtv.0"), + CLK_LOOKUP("div_clk", tv_src_div_clk.c, ""), + CLK_LOOKUP("core_clk", vcodec_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("mdp_clk", mdp_tv_clk.c, "dtv.0"), + CLK_LOOKUP("tv_clk", mdp_tv_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("hdmi_clk", hdmi_tv_clk.c, "dtv.0"), + CLK_LOOKUP("core_clk", hdmi_app_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("vpe_clk", vpe_clk.c, "msm_vpe.0"), + CLK_LOOKUP("core_clk", vpe_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("vfe_clk", vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("core_clk", vfe_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("csi_vfe_clk", csi_vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("bus_clk", vfe_axi_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("bus_clk", mdp_axi_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("bus_clk", rot_axi_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("bus_clk", vcodec_axi_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_a_clk", vcodec_axi_a_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_b_clk", vcodec_axi_b_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_clk", vpe_axi_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("arb_clk", amp_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("arb_clk", amp_p_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.2"), + CLK_LOOKUP("master_iface_clk", dsi1_m_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("slave_iface_clk", dsi1_s_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("master_iface_clk", dsi2_m_p_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("slave_iface_clk", dsi2_s_p_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("master_iface_clk", hdmi_m_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("slave_iface_clk", hdmi_s_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "msm_gemini.0"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("iface_clk", jpegd_p_clk.c, ""), + CLK_LOOKUP("mem_iface_clk", imem_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "mdp.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("iface_clk", smmu_p_clk.c, "msm_iommu"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "msm_rotator.0"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("vfe_pclk", vfe_p_clk.c, "msm_vfe.0"), + CLK_LOOKUP("iface_clk", vfe_p_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("vpe_pclk", vpe_p_clk.c, "msm_vpe.0"), + CLK_LOOKUP("iface_clk", vpe_p_clk.c, "footswitch-8x60.9"), + + CLK_LOOKUP("bit_clk", mi2s_bit_clk.c, + "msm-dai-q6-mi2s"), + CLK_LOOKUP("osr_clk", mi2s_osr_clk.c, + "msm-dai-q6-mi2s"), + CLK_LOOKUP("bit_clk", codec_i2s_mic_bit_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("osr_clk", codec_i2s_mic_osr_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("bit_clk", spare_i2s_mic_bit_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("osr_clk", spare_i2s_mic_osr_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("bit_clk", codec_i2s_spkr_bit_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("osr_clk", codec_i2s_spkr_osr_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("bit_clk", spare_i2s_spkr_bit_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("osr_clk", spare_i2s_spkr_osr_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.2"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.3"), + CLK_LOOKUP("sps_slimbus_clk", sps_slimbus_clk.c, ""), + CLK_LOOKUP("core_clk", audio_slimbus_clk.c, "msm_slim_ctrl.1"), + CLK_LOOKUP("core_clk", jpegd_axi_clk.c, ""), + CLK_LOOKUP("core_clk", vpe_axi_clk.c, ""), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, ""), + CLK_LOOKUP("core_clk", vcap_axi_clk.c, ""), + CLK_LOOKUP("core_clk", rot_axi_clk.c, ""), + CLK_LOOKUP("core_clk", ijpeg_axi_clk.c, ""), + CLK_LOOKUP("core_clk", vfe_axi_clk.c, ""), + CLK_LOOKUP("core_clk", vcodec_axi_a_clk.c, ""), + CLK_LOOKUP("core_clk", vcodec_axi_b_clk.c, ""), + CLK_LOOKUP("core_clk", gfx3d_axi_clk_8064.c, ""), + + CLK_LOOKUP("dfab_dsps_clk", dfab_dsps_clk.c, NULL), + CLK_LOOKUP("core_clk", dfab_usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("core_clk", dfab_usb_hs3_clk.c, "msm_ehci_host.0"), + CLK_LOOKUP("core_clk", dfab_usb_hs3_clk.c, "msm_ehci_host.1"), + CLK_LOOKUP("bus_clk", dfab_sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("bus_clk", dfab_sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("bus_clk", dfab_sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("bus_clk", dfab_sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("dfab_clk", dfab_sps_clk.c, "msm_sps"), + CLK_LOOKUP("bus_clk", dfab_bam_dmux_clk.c, "BAM_RMNT"), + CLK_LOOKUP("bus_clk", dfab_scm_clk.c, "scm"), + CLK_LOOKUP("bus_clk", dfab_qseecom_clk.c, "qseecom"), + CLK_LOOKUP("bus_clk", dfab_tzcom_clk.c, "tzcom"), + + CLK_LOOKUP("alt_core_clk", usb_hsic_xcvr_fs_clk.c, "msm_hsic_host"), + CLK_LOOKUP("phy_clk", usb_hsic_hsic_clk.c, "msm_hsic_host"), + CLK_LOOKUP("cal_clk", usb_hsic_hsio_cal_clk.c, "msm_hsic_host"), + CLK_LOOKUP("core_clk", usb_hsic_system_clk.c, "msm_hsic_host"), + CLK_LOOKUP("iface_clk", usb_hsic_p_clk.c, "msm_hsic_host"), + + CLK_LOOKUP("core_clk", jpegd_axi_clk.c, "msm_iommu.0"), + CLK_LOOKUP("core_clk", vpe_axi_clk.c, "msm_iommu.1"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.2"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.3"), + CLK_LOOKUP("core_clk", rot_axi_clk.c, "msm_iommu.4"), + CLK_LOOKUP("core_clk", ijpeg_axi_clk.c, "msm_iommu.5"), + CLK_LOOKUP("core_clk", vfe_axi_clk.c, "msm_iommu.6"), + CLK_LOOKUP("core_clk", vcodec_axi_a_clk.c, "msm_iommu.7"), + CLK_LOOKUP("core_clk", vcodec_axi_b_clk.c, "msm_iommu.8"), + CLK_LOOKUP("core_clk", gfx3d_axi_clk_8064.c, "msm_iommu.9"), + CLK_LOOKUP("core_clk", gfx3d_axi_clk_8064.c, "msm_iommu.10"), + CLK_LOOKUP("core_clk", vcap_axi_clk.c, "msm_iommu.11"), + + CLK_LOOKUP("mdp_iommu_clk", mdp_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("rot_iommu_clk", rot_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu0_clk", vcodec_axi_a_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu1_clk", vcodec_axi_b_clk.c, "msm_vidc.0"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_axi_clk.c, "pil_vidc"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "pil_vidc"), + + CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"), + CLK_LOOKUP("mem_clk", ebi1_acpu_a_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_acpu_a_clk.c, ""), + + CLK_LOOKUP("l2_mclk", l2_m_clk, ""), + CLK_LOOKUP("krait0_mclk", krait0_m_clk, ""), + CLK_LOOKUP("krait1_mclk", krait1_m_clk, ""), + CLK_LOOKUP("krait2_mclk", krait2_m_clk, ""), + CLK_LOOKUP("krait3_mclk", krait3_m_clk, ""), +}; + +static struct clk_lookup msm_clocks_8960[] = { + CLK_LOOKUP("xo", cxo_a_clk.c, ""), + CLK_LOOKUP("xo", pxo_a_clk.c, ""), + CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"), + CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"), + CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"), + CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"), + CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"), + CLK_LOOKUP("pll2", pll2_clk.c, NULL), + CLK_LOOKUP("pll8", pll8_clk.c, NULL), + CLK_LOOKUP("pll4", pll4_clk.c, NULL), + CLK_LOOKUP("measure", measure_clk.c, "debug"), + + CLK_LOOKUP("bus_clk", afab_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_a_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_a_clk.c, ""), + + CLK_LOOKUP("bus_clk", afab_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_a_clk", afab_msmbus_a_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_clk", cfpb_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_a_clk", sfab_msmbus_a_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_clk", sfpb_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_clk", mmfab_clk.c, "msm_mm_fab"), + CLK_LOOKUP("bus_a_clk", mmfab_a_clk.c, "msm_mm_fab"), + CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("mem_a_clk", ebi1_msmbus_a_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_clk", dfab_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_a_clk", dfab_msmbus_a_clk.c, "msm_bus"), + + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("mmfpb_clk", mmfpb_clk.c, NULL), + CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, "clock-8960"), + CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, "clock-8960"), + + CLK_LOOKUP("core_clk", gp0_clk.c, ""), + CLK_LOOKUP("core_clk", gp1_clk.c, ""), + CLK_LOOKUP("core_clk", gp2_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi1_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi2_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi5_uart_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", gsbi6_uart_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", gsbi7_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi8_uart_clk.c, "msm_serial_hsl.1"), + CLK_LOOKUP("core_clk", gsbi9_uart_clk.c, "msm_serial_hs.1"), + CLK_LOOKUP("core_clk", gsbi10_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi11_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi12_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi1_qup_clk.c, "spi_qsd.0"), + CLK_LOOKUP("core_clk", gsbi2_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_qup_clk.c, "qup_i2c.3"), + CLK_LOOKUP("core_clk", gsbi4_qup_clk.c, "qup_i2c.4"), + CLK_LOOKUP("core_clk", gsbi5_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi6_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi7_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi8_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi9_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi10_qup_clk.c, "qup_i2c.10"), + CLK_LOOKUP("core_clk", gsbi11_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi12_qup_clk.c, "qup_i2c.12"), + CLK_LOOKUP("core_clk", pdm_clk.c, ""), + CLK_LOOKUP("mem_clk", pmem_clk.c, "msm_sps"), + CLK_LOOKUP("core_clk", prng_clk.c, "msm_rng.0"), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", sdc5_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("slimbus_xo_src_clk", slimbus_xo_src_clk.c, NULL), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, ""), + CLK_LOOKUP("core_clk", tssc_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_hs1_xcvr_clk.c, "msm_otg"), + CLK_LOOKUP("phy_clk", usb_phy0_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_fs1_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs1_sys_clk.c, ""), + CLK_LOOKUP("src_clk", usb_fs1_src_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_fs2_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs2_sys_clk.c, ""), + CLK_LOOKUP("src_clk", usb_fs2_src_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_hsic_xcvr_fs_clk.c, "msm_hsic_host"), + CLK_LOOKUP("phy_clk", usb_hsic_hsic_clk.c, "msm_hsic_host"), + CLK_LOOKUP("cal_clk", usb_hsic_hsio_cal_clk.c, "msm_hsic_host"), + CLK_LOOKUP("core_clk", usb_hsic_system_clk.c, "msm_hsic_host"), + CLK_LOOKUP("iface_clk", usb_hsic_p_clk.c, "msm_hsic_host"), + CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qce.0"), + CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qcrypto.0"), + CLK_LOOKUP("core_clk", ce1_core_clk.c, "qce.0"), + CLK_LOOKUP("core_clk", ce1_core_clk.c, "qcrypto.0"), + CLK_LOOKUP("dma_bam_pclk", dma_bam_p_clk.c, NULL), + CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "spi_qsd.0"), + CLK_LOOKUP("iface_clk", gsbi2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "qup_i2c.3"), + CLK_LOOKUP("iface_clk", gsbi4_p_clk.c, "qup_i2c.4"), + CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("iface_clk", gsbi6_p_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("iface_clk", gsbi7_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi8_p_clk.c, "msm_serial_hsl.1"), + CLK_LOOKUP("iface_clk", gsbi9_p_clk.c, "msm_serial_hs.1"), + CLK_LOOKUP("iface_clk", gsbi10_p_clk.c, "qup_i2c.10"), + CLK_LOOKUP("iface_clk", gsbi11_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi12_p_clk.c, "qup_i2c.12"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_fs1_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_fs2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_hs1_p_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc5_p_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("core_clk", adm0_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", adm0_p_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", pmic_arb0_p_clk.c, ""), + CLK_LOOKUP("iface_clk", pmic_arb1_p_clk.c, ""), + CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""), + CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-001a"), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-006c"), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0048"), + CLK_LOOKUP("cam_clk", cam2_clk.c, NULL), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0034"), + CLK_LOOKUP("csi_src_clk", csi0_src_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_src_clk", csi1_src_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_clk", csi0_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_clk", csi2_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_phy_clk", csi0_phy_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_phy_clk", csi1_phy_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_phy_clk", csi2_phy_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_pix_clk", csi_pix_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi_clk", csi_rdi_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, NULL), + CLK_LOOKUP("csi_clk", csi2_clk.c, NULL), + CLK_LOOKUP("csi_pix1_clk", csi_pix1_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi1_clk", csi_rdi1_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi2_clk", csi_rdi2_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_phy_clk", csi2_phy_clk.c, NULL), + CLK_LOOKUP("csi2phy_timer_clk", csi2phy_timer_clk.c, NULL), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.0"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.1"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.2"), + CLK_LOOKUP("csiphy_timer_clk", csi0phy_timer_clk.c, "msm_csiphy.0"), + CLK_LOOKUP("csiphy_timer_clk", csi1phy_timer_clk.c, "msm_csiphy.1"), + CLK_LOOKUP("csiphy_timer_clk", csi2phy_timer_clk.c, "msm_csiphy.2"), + CLK_LOOKUP("byte_clk", dsi1_byte_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("byte_clk", dsi2_byte_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("esc_clk", dsi1_esc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("esc_clk", dsi2_esc_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("core_clk", gfx2d0_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("core_clk", gfx2d0_clk.c, "footswitch-8x60.0"), + CLK_LOOKUP("core_clk", gfx2d1_clk.c, "kgsl-2d1.1"), + CLK_LOOKUP("core_clk", gfx2d1_clk.c, "footswitch-8x60.1"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("bus_clk", ijpeg_axi_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("mem_clk", imem_axi_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("core_clk", jpegd_clk.c, "msm_mercury.0"), + CLK_LOOKUP("iface_clk", jpegd_p_clk.c, "msm_mercury.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("lut_clk", lut_mdp_clk.c, "mdp.0"), + CLK_LOOKUP("lut_clk", lut_mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("core_clk", rot_clk.c, "msm_rotator.0"), + CLK_LOOKUP("core_clk", rot_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("src_clk", tv_src_clk.c, "dtv.0"), + CLK_LOOKUP("src_clk", tv_src_clk.c, "tvenc.0"), + CLK_LOOKUP("tv_src_clk", tv_src_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("enc_clk", tv_enc_clk.c, "tvenc.0"), + CLK_LOOKUP("dac_clk", tv_dac_clk.c, "tvenc.0"), + CLK_LOOKUP("core_clk", vcodec_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("mdp_clk", mdp_tv_clk.c, "dtv.0"), + CLK_LOOKUP("mdp_clk", mdp_tv_clk.c, "tvenc.0"), + CLK_LOOKUP("tv_clk", mdp_tv_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("hdmi_clk", hdmi_tv_clk.c, "dtv.0"), + CLK_LOOKUP("core_clk", hdmi_app_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("vpe_clk", vpe_clk.c, "msm_vpe.0"), + CLK_LOOKUP("core_clk", vpe_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("vfe_clk", vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("core_clk", vfe_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("csi_vfe_clk", csi_vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("bus_clk", vfe_axi_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("bus_clk", mdp_axi_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("bus_clk", rot_axi_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("bus_clk", vcodec_axi_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_a_clk", vcodec_axi_a_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_b_clk", vcodec_axi_b_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_clk", vpe_axi_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("arb_clk", amp_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("arb_clk", amp_p_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.2"), + CLK_LOOKUP("master_iface_clk", dsi1_m_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("slave_iface_clk", dsi1_s_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("master_iface_clk", dsi2_m_p_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("slave_iface_clk", dsi2_s_p_clk.c, "mipi_dsi.2"), + CLK_LOOKUP("iface_clk", gfx2d0_p_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("iface_clk", gfx2d0_p_clk.c, "footswitch-8x60.0"), + CLK_LOOKUP("iface_clk", gfx2d1_p_clk.c, "kgsl-2d1.1"), + CLK_LOOKUP("iface_clk", gfx2d1_p_clk.c, "footswitch-8x60.1"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("master_iface_clk", hdmi_m_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("slave_iface_clk", hdmi_s_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "msm_gemini.0"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("iface_clk", jpegd_p_clk.c, ""), + CLK_LOOKUP("mem_iface_clk", imem_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "mdp.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("iface_clk", smmu_p_clk.c, "msm_iommu"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "msm_rotator.0"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("iface_clk", tv_enc_p_clk.c, "tvenc.0"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("vfe_pclk", vfe_p_clk.c, "msm_vfe.0"), + CLK_LOOKUP("iface_clk", vfe_p_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("vpe_pclk", vpe_p_clk.c, "msm_vpe.0"), + CLK_LOOKUP("iface_clk", vpe_p_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("bit_clk", mi2s_bit_clk.c, + "msm-dai-q6-mi2s"), + CLK_LOOKUP("osr_clk", mi2s_osr_clk.c, + "msm-dai-q6-mi2s"), + CLK_LOOKUP("bit_clk", codec_i2s_mic_bit_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("osr_clk", codec_i2s_mic_osr_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("bit_clk", spare_i2s_mic_bit_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("osr_clk", spare_i2s_mic_osr_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("bit_clk", codec_i2s_spkr_bit_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("osr_clk", codec_i2s_spkr_osr_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("bit_clk", spare_i2s_spkr_bit_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("osr_clk", spare_i2s_spkr_osr_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.2"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.3"), + CLK_LOOKUP("sps_slimbus_clk", sps_slimbus_clk.c, NULL), + CLK_LOOKUP("core_clk", audio_slimbus_clk.c, "msm_slim_ctrl.1"), + CLK_LOOKUP("core_clk", jpegd_axi_clk.c, "msm_iommu.0"), + CLK_LOOKUP("core_clk", vpe_axi_clk.c, "msm_iommu.1"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.2"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.3"), + CLK_LOOKUP("core_clk", rot_axi_clk.c, "msm_iommu.4"), + CLK_LOOKUP("core_clk", ijpeg_axi_clk.c, "msm_iommu.5"), + CLK_LOOKUP("core_clk", vfe_axi_clk.c, "msm_iommu.6"), + CLK_LOOKUP("core_clk", vcodec_axi_a_clk.c, "msm_iommu.7"), + CLK_LOOKUP("core_clk", vcodec_axi_b_clk.c, "msm_iommu.8"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "msm_iommu.9"), + CLK_LOOKUP("core_clk", gfx2d0_clk.c, "msm_iommu.10"), + CLK_LOOKUP("core_clk", gfx2d1_clk.c, "msm_iommu.11"), + + CLK_LOOKUP("mdp_iommu_clk", mdp_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("rot_iommu_clk", rot_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu0_clk", vcodec_axi_a_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu1_clk", vcodec_axi_b_clk.c, "msm_vidc.0"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_axi_clk.c, "pil_vidc"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "pil_vidc"), + + CLK_LOOKUP("dfab_dsps_clk", dfab_dsps_clk.c, NULL), + CLK_LOOKUP("core_clk", dfab_usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("bus_clk", dfab_sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("bus_clk", dfab_sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("bus_clk", dfab_sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("bus_clk", dfab_sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("bus_clk", dfab_sdc5_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("dfab_clk", dfab_sps_clk.c, "msm_sps"), + CLK_LOOKUP("bus_clk", dfab_bam_dmux_clk.c, "BAM_RMNT"), + CLK_LOOKUP("bus_clk", dfab_scm_clk.c, "scm"), + CLK_LOOKUP("bus_clk", dfab_qseecom_clk.c, "qseecom"), + CLK_LOOKUP("bus_clk", dfab_tzcom_clk.c, "tzcom"), + + CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"), + CLK_LOOKUP("mem_clk", ebi1_acpu_a_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_acpu_a_clk.c, ""), + + CLK_LOOKUP("l2_mclk", l2_m_clk, ""), + CLK_LOOKUP("krait0_mclk", krait0_m_clk, ""), + CLK_LOOKUP("krait1_mclk", krait1_m_clk, ""), + CLK_LOOKUP("q6sw_clk", q6sw_clk, ""), + CLK_LOOKUP("q6fw_clk", q6fw_clk, ""), + CLK_LOOKUP("q6_func_clk", q6_func_clk, ""), +}; + +static struct clk_lookup msm_clocks_8930[] = { + CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"), + CLK_LOOKUP("cxo", cxo_clk.c, "wcnss_wlan.0"), + CLK_LOOKUP("cxo", cxo_clk.c, "pil_riva"), + CLK_LOOKUP("xo", pxo_clk.c, "pil_qdsp6v4.0"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.1"), + CLK_LOOKUP("xo", cxo_clk.c, "pil_qdsp6v4.2"), + CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"), + CLK_LOOKUP("pll2", pll2_clk.c, NULL), + CLK_LOOKUP("pll8", pll8_clk.c, NULL), + CLK_LOOKUP("pll4", pll4_clk.c, NULL), + CLK_LOOKUP("measure", measure_clk.c, "debug"), + + CLK_LOOKUP("bus_clk", afab_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_a_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_a_clk.c, ""), + + CLK_LOOKUP("bus_clk", afab_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_a_clk", afab_msmbus_a_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_clk", cfpb_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_a_clk", sfab_msmbus_a_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_clk", sfpb_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_clk", mmfab_clk.c, "msm_mm_fab"), + CLK_LOOKUP("bus_a_clk", mmfab_a_clk.c, "msm_mm_fab"), + CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("mem_a_clk", ebi1_msmbus_a_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_clk", dfab_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_a_clk", dfab_msmbus_a_clk.c, "msm_bus"), + + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("mmfpb_clk", mmfpb_clk.c, NULL), + CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, "clock-8960"), + CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, "clock-8960"), + + CLK_LOOKUP("core_clk", gp0_clk.c, ""), + CLK_LOOKUP("core_clk", gp1_clk.c, ""), + CLK_LOOKUP("core_clk", gp2_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi1_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi2_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi5_uart_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", gsbi6_uart_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", gsbi7_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi8_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi9_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi10_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi11_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi12_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi1_qup_clk.c, "spi_qsd.0"), + CLK_LOOKUP("core_clk", gsbi2_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_qup_clk.c, "qup_i2c.3"), + CLK_LOOKUP("core_clk", gsbi4_qup_clk.c, "qup_i2c.4"), + CLK_LOOKUP("core_clk", gsbi5_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi6_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi7_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi8_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi9_qup_clk.c, "qup_i2c.0"), + CLK_LOOKUP("core_clk", gsbi10_qup_clk.c, "qup_i2c.10"), + CLK_LOOKUP("core_clk", gsbi11_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi12_qup_clk.c, "qup_i2c.12"), + CLK_LOOKUP("core_clk", pdm_clk.c, ""), + CLK_LOOKUP("mem_clk", pmem_clk.c, "msm_sps"), + CLK_LOOKUP("core_clk", prng_clk.c, "msm_rng.0"), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", sdc5_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, ""), + CLK_LOOKUP("core_clk", tssc_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_hs1_xcvr_clk.c, "msm_otg"), + CLK_LOOKUP("phy_clk", usb_phy0_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_fs1_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs1_sys_clk.c, ""), + CLK_LOOKUP("src_clk", usb_fs1_src_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_fs2_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs2_sys_clk.c, ""), + CLK_LOOKUP("src_clk", usb_fs2_src_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_hsic_xcvr_fs_clk.c, "msm_hsic_host"), + CLK_LOOKUP("phy_clk", usb_hsic_hsic_clk.c, "msm_hsic_host"), + CLK_LOOKUP("cal_clk", usb_hsic_hsio_cal_clk.c, "msm_hsic_host"), + CLK_LOOKUP("core_clk", usb_hsic_system_clk.c, "msm_hsic_host"), + CLK_LOOKUP("iface_clk", usb_hsic_p_clk.c, "msm_hsic_host"), + CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qce.0"), + CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qcrypto.0"), + CLK_LOOKUP("core_clk", ce1_core_clk.c, "qce.0"), + CLK_LOOKUP("core_clk", ce1_core_clk.c, "qcrypto.0"), + CLK_LOOKUP("dma_bam_pclk", dma_bam_p_clk.c, NULL), + CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "spi_qsd.0"), + CLK_LOOKUP("iface_clk", gsbi2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "qup_i2c.3"), + CLK_LOOKUP("iface_clk", gsbi4_p_clk.c, "qup_i2c.4"), + CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("iface_clk", gsbi6_p_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("iface_clk", gsbi7_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi8_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi9_p_clk.c, "qup_i2c.0"), + CLK_LOOKUP("iface_clk", gsbi10_p_clk.c, "qup_i2c.10"), + CLK_LOOKUP("iface_clk", gsbi11_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi12_p_clk.c, "qup_i2c.12"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_fs1_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_fs2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_hs1_p_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc5_p_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("core_clk", adm0_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", adm0_p_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", pmic_arb0_p_clk.c, ""), + CLK_LOOKUP("iface_clk", pmic_arb1_p_clk.c, ""), + CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""), + CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-001a"), + CLK_LOOKUP("cam_clk", cam1_clk.c, "4-006c"), + CLK_LOOKUP("cam_clk", cam1_clk.c, "4-0048"), + CLK_LOOKUP("cam_clk", cam2_clk.c, NULL), + CLK_LOOKUP("cam_clk", cam0_clk.c, "4-0020"), + CLK_LOOKUP("csi_src_clk", csi0_src_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_src_clk", csi1_src_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_clk", csi0_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_clk", csi2_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_phy_clk", csi0_phy_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_phy_clk", csi1_phy_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_phy_clk", csi2_phy_clk.c, "msm_csid.2"), + CLK_LOOKUP("csi_pix_clk", csi_pix_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi_clk", csi_rdi_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_src_clk", csi2_src_clk.c, NULL), + CLK_LOOKUP("csi_clk", csi2_clk.c, NULL), + CLK_LOOKUP("csi_pix1_clk", csi_pix1_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi1_clk", csi_rdi1_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_rdi2_clk", csi_rdi2_clk.c, "msm_ispif.0"), + CLK_LOOKUP("csi_phy_clk", csi2_phy_clk.c, NULL), + CLK_LOOKUP("csi2phy_timer_clk", csi2phy_timer_clk.c, NULL), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.0"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.1"), + CLK_LOOKUP("csiphy_timer_src_clk", + csiphy_timer_src_clk.c, "msm_csiphy.2"), + CLK_LOOKUP("csiphy_timer_clk", csi0phy_timer_clk.c, "msm_csiphy.0"), + CLK_LOOKUP("csiphy_timer_clk", csi1phy_timer_clk.c, "msm_csiphy.1"), + CLK_LOOKUP("csiphy_timer_clk", csi2phy_timer_clk.c, "msm_csiphy.2"), + CLK_LOOKUP("byte_clk", dsi1_byte_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("esc_clk", dsi1_esc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("bus_clk", + gfx3d_axi_clk_8930.c, "footswitch-8x60.2"), + CLK_LOOKUP("bus_clk", ijpeg_axi_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("mem_clk", imem_axi_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("lut_clk", lut_mdp_clk.c, "mdp.0"), + CLK_LOOKUP("lut_clk", lut_mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("core_clk", rot_clk.c, "msm_rotator.0"), + CLK_LOOKUP("core_clk", rot_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("src_clk", tv_src_clk.c, "dtv.0"), + CLK_LOOKUP("src_clk", tv_src_clk.c, "tvenc.0"), + CLK_LOOKUP("tv_src_clk", tv_src_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("dac_clk", tv_dac_clk.c, "tvenc.0"), + CLK_LOOKUP("core_clk", vcodec_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("mdp_clk", mdp_tv_clk.c, "dtv.0"), + CLK_LOOKUP("mdp_clk", mdp_tv_clk.c, "tvenc.0"), + CLK_LOOKUP("tv_clk", mdp_tv_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("hdmi_clk", hdmi_tv_clk.c, "dtv.0"), + CLK_LOOKUP("core_clk", hdmi_app_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("vpe_clk", vpe_clk.c, "msm_vpe.0"), + CLK_LOOKUP("core_clk", vpe_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("vfe_clk", vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("core_clk", vfe_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("csi_vfe_clk", csi_vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("bus_clk", vfe_axi_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("bus_clk", mdp_axi_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("bus_clk", rot_axi_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("bus_clk", vcodec_axi_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_a_clk", vcodec_axi_a_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_b_clk", vcodec_axi_b_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_clk", vpe_axi_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("arb_clk", amp_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.0"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.1"), + CLK_LOOKUP("csi_pclk", csi_p_clk.c, "msm_csid.2"), + CLK_LOOKUP("master_iface_clk", dsi1_m_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("slave_iface_clk", dsi1_s_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("master_iface_clk", hdmi_m_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("slave_iface_clk", hdmi_s_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "msm_gemini.0"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("mem_iface_clk", imem_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "mdp.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("iface_clk", smmu_p_clk.c, "msm_iommu"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "msm_rotator.0"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("vfe_pclk", vfe_p_clk.c, "msm_vfe.0"), + CLK_LOOKUP("iface_clk", vfe_p_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("vpe_pclk", vpe_p_clk.c, "msm_vpe.0"), + CLK_LOOKUP("iface_clk", vpe_p_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("bit_clk", mi2s_bit_clk.c, "msm-dai-q6.6"), + CLK_LOOKUP("osr_clk", mi2s_osr_clk.c, "msm-dai-q6.6"), + CLK_LOOKUP("bit_clk", codec_i2s_mic_bit_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("osr_clk", codec_i2s_mic_osr_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("bit_clk", spare_i2s_mic_bit_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("osr_clk", spare_i2s_mic_osr_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("bit_clk", codec_i2s_spkr_bit_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("osr_clk", codec_i2s_spkr_osr_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("bit_clk", spare_i2s_spkr_bit_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("osr_clk", spare_i2s_spkr_osr_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.2"), + CLK_LOOKUP("sps_slimbus_clk", sps_slimbus_clk.c, NULL), + CLK_LOOKUP("core_clk", audio_slimbus_clk.c, "msm_slim_ctrl.1"), + CLK_LOOKUP("core_clk", vpe_axi_clk.c, "msm_iommu.1"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.2"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.3"), + CLK_LOOKUP("core_clk", rot_axi_clk.c, "msm_iommu.4"), + CLK_LOOKUP("core_clk", ijpeg_axi_clk.c, "msm_iommu.5"), + CLK_LOOKUP("core_clk", vfe_axi_clk.c, "msm_iommu.6"), + CLK_LOOKUP("core_clk", vcodec_axi_a_clk.c, "msm_iommu.7"), + CLK_LOOKUP("core_clk", vcodec_axi_b_clk.c, "msm_iommu.8"), + CLK_LOOKUP("core_clk", gfx3d_axi_clk_8930.c, "msm_iommu.9"), + CLK_LOOKUP("core_clk", gfx3d_axi_clk_8930.c, "msm_iommu.10"), + + CLK_LOOKUP("mdp_iommu_clk", mdp_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("rot_iommu_clk", rot_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu0_clk", vcodec_axi_a_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu1_clk", vcodec_axi_b_clk.c, "msm_vidc.0"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_axi_clk.c, "pil_vidc"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "pil_vidc"), + + CLK_LOOKUP("dfab_dsps_clk", dfab_dsps_clk.c, NULL), + CLK_LOOKUP("core_clk", dfab_usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("bus_clk", dfab_sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("bus_clk", dfab_sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("bus_clk", dfab_sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("bus_clk", dfab_sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("bus_clk", dfab_sdc5_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("dfab_clk", dfab_sps_clk.c, "msm_sps"), + CLK_LOOKUP("bus_clk", dfab_bam_dmux_clk.c, "BAM_RMNT"), + CLK_LOOKUP("bus_clk", dfab_scm_clk.c, "scm"), + CLK_LOOKUP("bus_clk", dfab_qseecom_clk.c, "qseecom"), + + CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"), + CLK_LOOKUP("mem_clk", ebi1_acpu_a_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_acpu_a_clk.c, ""), + + CLK_LOOKUP("l2_mclk", l2_m_clk, ""), + CLK_LOOKUP("krait0_mclk", krait0_m_clk, ""), + CLK_LOOKUP("krait1_mclk", krait1_m_clk, ""), + CLK_LOOKUP("q6sw_clk", q6sw_clk, ""), + CLK_LOOKUP("q6fw_clk", q6fw_clk, ""), + CLK_LOOKUP("q6_func_clk", q6_func_clk, ""), +}; +/* + * Miscellaneous clock register initializations + */ + +/* Read, modify, then write-back a register. */ +static void __init rmwreg(uint32_t val, void *reg, uint32_t mask) +{ + uint32_t regval = readl_relaxed(reg); + regval &= ~mask; + regval |= val; + writel_relaxed(regval, reg); +} + +static struct pll_config_regs pll4_regs __initdata = { + .l_reg = LCC_PLL0_L_VAL_REG, + .m_reg = LCC_PLL0_M_VAL_REG, + .n_reg = LCC_PLL0_N_VAL_REG, + .config_reg = LCC_PLL0_CONFIG_REG, + .mode_reg = LCC_PLL0_MODE_REG, +}; + +static struct pll_config pll4_config __initdata = { + .l = 0xE, + .m = 0x27A, + .n = 0x465, + .vco_val = 0x0, + .vco_mask = BM(17, 16), + .pre_div_val = 0x0, + .pre_div_mask = BIT(19), + .post_div_val = 0x0, + .post_div_mask = BM(21, 20), + .mn_ena_val = BIT(22), + .mn_ena_mask = BIT(22), + .main_output_val = BIT(23), + .main_output_mask = BIT(23), +}; + +static struct pll_config_regs pll15_regs __initdata = { + .l_reg = MM_PLL3_L_VAL_REG, + .m_reg = MM_PLL3_M_VAL_REG, + .n_reg = MM_PLL3_N_VAL_REG, + .config_reg = MM_PLL3_CONFIG_REG, + .mode_reg = MM_PLL3_MODE_REG, +}; + +static struct pll_config pll15_config __initdata = { + .l = (0x24 | BVAL(31, 7, 0x620)), + .m = 0x1, + .n = 0x9, + .vco_val = BVAL(17, 16, 0x2), + .vco_mask = BM(17, 16), + .pre_div_val = 0x0, + .pre_div_mask = BIT(19), + .post_div_val = 0x0, + .post_div_mask = BM(21, 20), + .mn_ena_val = BIT(22), + .mn_ena_mask = BIT(22), + .main_output_val = BIT(23), + .main_output_mask = BIT(23), +}; + +static struct pll_config_regs pll14_regs __initdata = { + .l_reg = BB_PLL14_L_VAL_REG, + .m_reg = BB_PLL14_M_VAL_REG, + .n_reg = BB_PLL14_N_VAL_REG, + .config_reg = BB_PLL14_CONFIG_REG, + .mode_reg = BB_PLL14_MODE_REG, +}; + +static struct pll_config pll14_config __initdata = { + .l = (0x11 | BVAL(31, 7, 0x620)), + .m = 0x7, + .n = 0x9, + .vco_val = 0x0, + .vco_mask = BM(17, 16), + .pre_div_val = 0x0, + .pre_div_mask = BIT(19), + .post_div_val = 0x0, + .post_div_mask = BM(21, 20), + .mn_ena_val = BIT(22), + .mn_ena_mask = BIT(22), + .main_output_val = BIT(23), + .main_output_mask = BIT(23), +}; + +static void __init reg_init(void) +{ + void __iomem *imem_reg; + + /* Deassert MM SW_RESET_ALL signal. */ + writel_relaxed(0, SW_RESET_ALL_REG); + + /* + * Some bits are only used on 8960 or 8064 or 8930 and are marked as + * reserved bits on the other SoCs. Writing to these reserved bits + * should have no effect. + */ + /* + * Initialize MM AHB registers: Enable the FPB clock and disable HW + * gating on non-8960 for all clocks. Also set VFE_AHB's + * FORCE_CORE_ON bit to prevent its memory from being collapsed when + * the clock is halted. The sleep and wake-up delays are set to safe + * values. + */ + if (cpu_is_msm8960() || cpu_is_apq8064()) { + rmwreg(0x44000000, AHB_EN_REG, 0x6C000103); + writel_relaxed(0x3C7097F9, AHB_EN2_REG); + } else { + rmwreg(0x00000003, AHB_EN_REG, 0x6C000103); + writel_relaxed(0x000007F9, AHB_EN2_REG); + } + if (cpu_is_apq8064()) + rmwreg(0x00000001, AHB_EN3_REG, 0x00000001); + + /* Deassert all locally-owned MM AHB resets. */ + rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF); + rmwreg(0, SW_RESET_AHB2_REG, 0x0000000F); + + /* Initialize MM AXI registers: Enable HW gating for all clocks that + * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up + * delays to safe values. */ + if ((cpu_is_msm8960() && + SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) || + cpu_is_apq8064()) { + rmwreg(0x0003AFF9, MAXI_EN_REG, 0x0803FFFF); + rmwreg(0x3A27FCFF, MAXI_EN2_REG, 0x3A3FFFFF); + rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF); + } else { + rmwreg(0x000007F9, MAXI_EN_REG, 0x0803FFFF); + rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF); + rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF); + } + rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF); + if (cpu_is_apq8064()) + rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF); + if (cpu_is_msm8930()) + rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF); + if (cpu_is_msm8960() || cpu_is_apq8064()) + rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF); + else + rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF); + + /* Enable IMEM's clk_on signal */ + imem_reg = ioremap(0x04b00040, 4); + if (imem_reg) { + writel_relaxed(0x3, imem_reg); + iounmap(imem_reg); + } + + /* Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core + * memories retain state even when not clocked. Also, set sleep and + * wake-up delays to safe values. */ + rmwreg(0x00000000, CSI0_CC_REG, 0x00000410); + rmwreg(0x00000000, CSI1_CC_REG, 0x00000410); + rmwreg(0x80FF0000, DSI1_BYTE_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, DSI_PIXEL_CC_REG, 0xE0FF0010); + rmwreg(0xC0FF0000, GFX3D_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, IJPEG_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, MDP_CC_REG, 0xE1FF0010); + rmwreg(0x80FF0000, MDP_LUT_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, ROT_CC_REG, 0xE0FF0010); + rmwreg(0x000004FF, TV_CC2_REG, 0x000007FF); + rmwreg(0xC0FF0000, VCODEC_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, VFE_CC_REG, 0xE0FF4010); + rmwreg(0x800000FF, VFE_CC2_REG, 0xE00000FF); + rmwreg(0x80FF0000, VPE_CC_REG, 0xE0FF0010); + if (cpu_is_msm8960() || cpu_is_apq8064()) { + rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010); + } + if (cpu_is_msm8960() || cpu_is_msm8930()) + rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010); + + if (cpu_is_msm8960()) { + rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010); + } + if (cpu_is_apq8064()) { + rmwreg(0x00000000, TV_CC_REG, 0x00004010); + rmwreg(0x80FF0000, VCAP_CC_REG, 0xE0FF1010); + } + + /* + * Initialize USB_HS_HCLK_FS registers: Set FORCE_C_ON bits so that + * core remain active during halt state of the clk. Also, set sleep + * and wake-up value to max. + */ + rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F); + if (cpu_is_apq8064()) { + rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F); + rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F); + } + + /* De-assert MM AXI resets to all hardware blocks. */ + writel_relaxed(0, SW_RESET_AXI_REG); + + /* Deassert all MM core resets. */ + writel_relaxed(0, SW_RESET_CORE_REG); + writel_relaxed(0, SW_RESET_CORE2_REG); + + /* Enable TSSC and PDM PXO sources. */ + writel_relaxed(BIT(11), TSSC_CLK_CTL_REG); + writel_relaxed(BIT(15), PDM_CLK_NS_REG); + + /* Source SLIMBus xo src from slimbus reference clock */ + if (cpu_is_msm8960()) + writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG); + + /* Source the dsi_byte_clks from the DSI PHY PLLs */ + rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7); + if (cpu_is_msm8960() || cpu_is_apq8064()) + rmwreg(0x2, DSI2_BYTE_NS_REG, 0x7); + + /* Source the dsi1_esc_clk from the DSI1 PHY PLLs */ + rmwreg(0x1, DSI1_ESC_NS_REG, 0x7); + + /* + * Source the sata_phy_ref_clk from PXO and set predivider of + * sata_pmalive_clk to 1. + */ + if (cpu_is_apq8064()) { + rmwreg(0, SATA_PHY_REF_CLK_CTL_REG, 0x1); + rmwreg(0, SATA_PMALIVE_CLK_CTL_REG, 0x3); + } + + /* + * TODO: Programming below PLLs and prng_clk is temporary and + * needs to be removed after bootloaders program them. + */ + if (cpu_is_apq8064()) { + u32 is_pll_enabled; + + /* Program pxo_src_clk to source from PXO */ + rmwreg(0x1, PXO_SRC_CLK_CTL_REG, 0x7); + + /* Check if PLL14 is active */ + is_pll_enabled = readl_relaxed(BB_PLL14_STATUS_REG) & BIT(16); + if (!is_pll_enabled) + /* Ref clk = 27MHz and program pll14 to 480MHz */ + configure_pll(&pll14_config, &pll14_regs, 1); + + /* Program PLL15 to 975MHz with ref clk = 27MHz */ + configure_pll(&pll15_config, &pll15_regs, 0); + + /* Check if PLL4 is active */ + is_pll_enabled = readl_relaxed(LCC_PLL0_STATUS_REG) & BIT(16); + if (!is_pll_enabled) + /* Ref clk = 27MHz and program pll4 to 393.2160MHz */ + configure_pll(&pll4_config, &pll4_regs, 1); + + /* Enable PLL4 source on the LPASS Primary PLL Mux */ + writel_relaxed(0x1, LCC_PRI_PLL_CLK_CTL_REG); + + /* Program prng_clk to 64MHz if it isn't configured */ + if (!readl_relaxed(PRNG_CLK_NS_REG)) + writel_relaxed(0x2B, PRNG_CLK_NS_REG); + } + + /* + * Program PLL15 to 900MHz with ref clk = 27MHz and + * only enable PLL main output. + */ + if (cpu_is_msm8930()) { + pll15_config.l = 0x21 | BVAL(31, 7, 0x600); + pll15_config.m = 0x1; + pll15_config.n = 0x3; + configure_pll(&pll15_config, &pll15_regs, 0); + /* Disable AUX and BIST outputs */ + writel_relaxed(0, MM_PLL3_TEST_CTL_REG); + } +} + +static void __init msm8960_clock_pre_init(void) +{ + if (cpu_is_apq8064()) { + vdd_sr2_pll.set_vdd = set_vdd_sr2_pll_8064; + } else if (cpu_is_msm8930() || cpu_is_msm8627()) { + vdd_dig.set_vdd = set_vdd_dig_8930; + vdd_sr2_pll.set_vdd = set_vdd_sr2_pll_8930; + } + + /* + * Change the freq tables for and voltage requirements for + * clocks which differ between 8960 and 8064. + */ + if (cpu_is_apq8064()) { + gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064; + + memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064, + sizeof(gfx3d_clk.c.fmax)); + memcpy(ijpeg_clk.c.fmax, fmax_ijpeg_8064, + sizeof(ijpeg_clk.c.fmax)); + memcpy(mdp_clk.c.fmax, fmax_mdp_8064, + sizeof(ijpeg_clk.c.fmax)); + memcpy(tv_src_clk.c.fmax, fmax_tv_src_8064, + sizeof(tv_src_clk.c.fmax)); + memcpy(vfe_clk.c.fmax, fmax_vfe_8064, + sizeof(vfe_clk.c.fmax)); + + gmem_axi_clk.c.depends = &gfx3d_axi_clk_8064.c; + } + + /* + * Change the freq tables and voltage requirements for + * clocks which differ between 8960 and 8930. + */ + if (cpu_is_msm8930()) { + gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930; + + memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930, + sizeof(gfx3d_clk.c.fmax)); + + pll15_clk.c.rate = 900000000; + gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c; + } + if ((readl_relaxed(PRNG_CLK_NS_REG) & 0x7F) == 0x2B) + prng_clk.freq_tbl = clk_tbl_prng_64; + + vote_vdd_level(&vdd_dig, VDD_DIG_HIGH); + + clk_ops_local_pll.enable = sr_pll_clk_enable; + + /* Initialize clock registers. */ + reg_init(); +} + +static void __init msm8960_clock_post_init(void) +{ + /* Keep PXO on whenever APPS cpu is active */ + clk_prepare_enable(&pxo_a_clk.c); + + /* Reset 3D core while clocked to ensure it resets completely. */ + clk_set_rate(&gfx3d_clk.c, 27000000); + clk_prepare_enable(&gfx3d_clk.c); + clk_reset(&gfx3d_clk.c, CLK_RESET_ASSERT); + udelay(5); + clk_reset(&gfx3d_clk.c, CLK_RESET_DEASSERT); + clk_disable_unprepare(&gfx3d_clk.c); + + /* Initialize rates for clocks that only support one. */ + clk_set_rate(&pdm_clk.c, 27000000); + clk_set_rate(&prng_clk.c, prng_clk.freq_tbl->freq_hz); + clk_set_rate(&mdp_vsync_clk.c, 27000000); + clk_set_rate(&tsif_ref_clk.c, 105000); + clk_set_rate(&tssc_clk.c, 27000000); + clk_set_rate(&usb_hs1_xcvr_clk.c, 60000000); + if (cpu_is_apq8064()) { + clk_set_rate(&usb_hs3_xcvr_clk.c, 60000000); + clk_set_rate(&usb_hs4_xcvr_clk.c, 60000000); + } + clk_set_rate(&usb_fs1_src_clk.c, 60000000); + if (cpu_is_msm8960() || cpu_is_msm8930()) + clk_set_rate(&usb_fs2_src_clk.c, 60000000); + clk_set_rate(&usb_hsic_xcvr_fs_clk.c, 60000000); + clk_set_rate(&usb_hsic_hsic_src_clk.c, 480000000); + clk_set_rate(&usb_hsic_hsio_cal_clk.c, 9000000); + clk_set_rate(&usb_hsic_system_clk.c, 60000000); + /* + * Set the CSI rates to a safe default to avoid warnings when + * switching csi pix and rdi clocks. + */ + clk_set_rate(&csi0_src_clk.c, 27000000); + clk_set_rate(&csi1_src_clk.c, 27000000); + clk_set_rate(&csi2_src_clk.c, 27000000); + + /* + * The halt status bits for these clocks may be incorrect at boot. + * Toggle these clocks on and off to refresh them. + */ + clk_prepare_enable(&pdm_clk.c); + clk_disable_unprepare(&pdm_clk.c); + clk_prepare_enable(&tssc_clk.c); + clk_disable_unprepare(&tssc_clk.c); + clk_prepare_enable(&usb_hsic_hsic_clk.c); + clk_disable_unprepare(&usb_hsic_hsic_clk.c); + + /* + * Keep sfab floor @ 54MHz so that Krait AHB is at least 27MHz at all + * times when Apps CPU is active. This ensures the timer's requirement + * of Krait AHB running 4 times as fast as the timer itself. + */ + clk_set_rate(&sfab_tmr_a_clk.c, 54000000); + clk_prepare_enable(&sfab_tmr_a_clk.c); +} + +static int __init msm8960_clock_late_init(void) +{ + int rc; + struct clk *mmfpb_a_clk = clk_get_sys("clock-8960", "mmfpb_a_clk"); + struct clk *cfpb_a_clk = clk_get_sys("clock-8960", "cfpb_a_clk"); + + /* Vote for MMFPB to be at least 76.8MHz when an Apps CPU is active. */ + if (WARN(IS_ERR(mmfpb_a_clk), "mmfpb_a_clk not found (%ld)\n", + PTR_ERR(mmfpb_a_clk))) + return PTR_ERR(mmfpb_a_clk); + rc = clk_set_rate(mmfpb_a_clk, 76800000); + if (WARN(rc, "mmfpb_a_clk rate was not set (%d)\n", rc)) + return rc; + rc = clk_prepare_enable(mmfpb_a_clk); + if (WARN(rc, "mmfpb_a_clk not enabled (%d)\n", rc)) + return rc; + + /* Vote for CFPB to be at least 64MHz when an Apps CPU is active. */ + if (WARN(IS_ERR(cfpb_a_clk), "cfpb_a_clk not found (%ld)\n", + PTR_ERR(cfpb_a_clk))) + return PTR_ERR(cfpb_a_clk); + rc = clk_set_rate(cfpb_a_clk, 64000000); + if (WARN(rc, "cfpb_a_clk rate was not set (%d)\n", rc)) + return rc; + rc = clk_prepare_enable(cfpb_a_clk); + if (WARN(rc, "cfpb_a_clk not enabled (%d)\n", rc)) + return rc; + + return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH); +} + +struct clock_init_data msm8960_clock_init_data __initdata = { + .table = msm_clocks_8960, + .size = ARRAY_SIZE(msm_clocks_8960), + .pre_init = msm8960_clock_pre_init, + .post_init = msm8960_clock_post_init, + .late_init = msm8960_clock_late_init, +}; + +struct clock_init_data apq8064_clock_init_data __initdata = { + .table = msm_clocks_8064, + .size = ARRAY_SIZE(msm_clocks_8064), + .pre_init = msm8960_clock_pre_init, + .post_init = msm8960_clock_post_init, + .late_init = msm8960_clock_late_init, +}; + +struct clock_init_data msm8930_clock_init_data __initdata = { + .table = msm_clocks_8930, + .size = ARRAY_SIZE(msm_clocks_8930), + .pre_init = msm8960_clock_pre_init, + .post_init = msm8960_clock_post_init, + .late_init = msm8960_clock_late_init, +}; diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c new file mode 100644 index 00000000000..975587dade6 --- /dev/null +++ b/arch/arm/mach-msm/clock-8x60.c @@ -0,0 +1,3889 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "clock-local.h" +#include "clock-rpm.h" +#include "clock-voter.h" +#include "clock-pll.h" + +#ifdef CONFIG_MSM_SECURE_IO +#undef readl_relaxed +#undef writel_relaxed +#define readl_relaxed secure_readl +#define writel_relaxed secure_writel +#endif + +#define REG(off) (MSM_CLK_CTL_BASE + (off)) +#define REG_MM(off) (MSM_MMSS_CLK_CTL_BASE + (off)) +#define REG_LPA(off) (MSM_LPASS_CLK_CTL_BASE + (off)) + +/* Peripheral clock registers. */ +#define CE2_HCLK_CTL_REG REG(0x2740) +#define CLK_HALT_CFPB_STATEA_REG REG(0x2FCC) +#define CLK_HALT_CFPB_STATEB_REG REG(0x2FD0) +#define CLK_HALT_CFPB_STATEC_REG REG(0x2FD4) +#define CLK_HALT_DFAB_STATE_REG REG(0x2FC8) +#define CLK_HALT_MSS_SMPSS_MISC_STATE_REG REG(0x2FDC) +#define CLK_HALT_SFPB_MISC_STATE_REG REG(0x2FD8) +#define CLK_TEST_REG REG(0x2FA0) +#define EBI2_2X_CLK_CTL_REG REG(0x2660) +#define EBI2_CLK_CTL_REG REG(0x2664) +#define GPn_MD_REG(n) REG(0x2D00+(0x20*(n))) +#define GPn_NS_REG(n) REG(0x2D24+(0x20*(n))) +#define GSBIn_HCLK_CTL_REG(n) REG(0x29C0+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_MD_REG(n) REG(0x29C8+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_NS_REG(n) REG(0x29CC+(0x20*((n)-1))) +#define GSBIn_RESET_REG(n) REG(0x29DC+(0x20*((n)-1))) +#define GSBIn_UART_APPS_MD_REG(n) REG(0x29D0+(0x20*((n)-1))) +#define GSBIn_UART_APPS_NS_REG(n) REG(0x29D4+(0x20*((n)-1))) +#define PDM_CLK_NS_REG REG(0x2CC0) +#define BB_PLL_ENA_SC0_REG REG(0x34C0) +#define BB_PLL0_STATUS_REG REG(0x30D8) +#define BB_PLL6_STATUS_REG REG(0x3118) +#define BB_PLL8_L_VAL_REG REG(0x3144) +#define BB_PLL8_M_VAL_REG REG(0x3148) +#define BB_PLL8_MODE_REG REG(0x3140) +#define BB_PLL8_N_VAL_REG REG(0x314C) +#define BB_PLL8_STATUS_REG REG(0x3158) +#define PLLTEST_PAD_CFG_REG REG(0x2FA4) +#define PMEM_ACLK_CTL_REG REG(0x25A0) +#define PPSS_HCLK_CTL_REG REG(0x2580) +#define PRNG_CLK_NS_REG REG(0x2E80) +#define RINGOSC_NS_REG REG(0x2DC0) +#define RINGOSC_STATUS_REG REG(0x2DCC) +#define RINGOSC_TCXO_CTL_REG REG(0x2DC4) +#define SC0_U_CLK_BRANCH_ENA_VOTE_REG REG(0x3080) +#define SC1_U_CLK_BRANCH_ENA_VOTE_REG REG(0x30A0) +#define SC0_U_CLK_SLEEP_ENA_VOTE_REG REG(0x3084) +#define SC1_U_CLK_SLEEP_ENA_VOTE_REG REG(0x30A4) +#define SDCn_APPS_CLK_MD_REG(n) REG(0x2828+(0x20*((n)-1))) +#define SDCn_APPS_CLK_NS_REG(n) REG(0x282C+(0x20*((n)-1))) +#define SDCn_HCLK_CTL_REG(n) REG(0x2820+(0x20*((n)-1))) +#define SDCn_RESET_REG(n) REG(0x2830+(0x20*((n)-1))) +#define TSIF_HCLK_CTL_REG REG(0x2700) +#define TSIF_REF_CLK_MD_REG REG(0x270C) +#define TSIF_REF_CLK_NS_REG REG(0x2710) +#define TSSC_CLK_CTL_REG REG(0x2CA0) +#define USB_FSn_HCLK_CTL_REG(n) REG(0x2960+(0x20*((n)-1))) +#define USB_FSn_RESET_REG(n) REG(0x2974+(0x20*((n)-1))) +#define USB_FSn_SYSTEM_CLK_CTL_REG(n) REG(0x296C+(0x20*((n)-1))) +#define USB_FSn_XCVR_FS_CLK_MD_REG(n) REG(0x2964+(0x20*((n)-1))) +#define USB_FSn_XCVR_FS_CLK_NS_REG(n) REG(0x2968+(0x20*((n)-1))) +#define USB_HS1_HCLK_CTL_REG REG(0x2900) +#define USB_HS1_RESET_REG REG(0x2910) +#define USB_HS1_XCVR_FS_CLK_MD_REG REG(0x2908) +#define USB_HS1_XCVR_FS_CLK_NS_REG REG(0x290C) +#define USB_PHY0_RESET_REG REG(0x2E20) + +/* Multimedia clock registers. */ +#define AHB_EN_REG REG_MM(0x0008) +#define AHB_EN2_REG REG_MM(0x0038) +#define AHB_NS_REG REG_MM(0x0004) +#define AXI_NS_REG REG_MM(0x0014) +#define CAMCLK_CC_REG REG_MM(0x0140) +#define CAMCLK_MD_REG REG_MM(0x0144) +#define CAMCLK_NS_REG REG_MM(0x0148) +#define CSI_CC_REG REG_MM(0x0040) +#define CSI_NS_REG REG_MM(0x0048) +#define DBG_BUS_VEC_A_REG REG_MM(0x01C8) +#define DBG_BUS_VEC_B_REG REG_MM(0x01CC) +#define DBG_BUS_VEC_C_REG REG_MM(0x01D0) +#define DBG_BUS_VEC_D_REG REG_MM(0x01D4) +#define DBG_BUS_VEC_E_REG REG_MM(0x01D8) +#define DBG_BUS_VEC_F_REG REG_MM(0x01DC) +#define DBG_BUS_VEC_H_REG REG_MM(0x01E4) +#define DBG_BUS_VEC_I_REG REG_MM(0x01E8) +#define DBG_CFG_REG_HS_REG REG_MM(0x01B4) +#define DBG_CFG_REG_LS_REG REG_MM(0x01B8) +#define GFX2D0_CC_REG REG_MM(0x0060) +#define GFX2D0_MD0_REG REG_MM(0x0064) +#define GFX2D0_MD1_REG REG_MM(0x0068) +#define GFX2D0_NS_REG REG_MM(0x0070) +#define GFX2D1_CC_REG REG_MM(0x0074) +#define GFX2D1_MD0_REG REG_MM(0x0078) +#define GFX2D1_MD1_REG REG_MM(0x006C) +#define GFX2D1_NS_REG REG_MM(0x007C) +#define GFX3D_CC_REG REG_MM(0x0080) +#define GFX3D_MD0_REG REG_MM(0x0084) +#define GFX3D_MD1_REG REG_MM(0x0088) +#define GFX3D_NS_REG REG_MM(0x008C) +#define IJPEG_CC_REG REG_MM(0x0098) +#define IJPEG_MD_REG REG_MM(0x009C) +#define IJPEG_NS_REG REG_MM(0x00A0) +#define JPEGD_CC_REG REG_MM(0x00A4) +#define JPEGD_NS_REG REG_MM(0x00AC) +#define MAXI_EN_REG REG_MM(0x0018) +#define MAXI_EN2_REG REG_MM(0x0020) +#define MAXI_EN3_REG REG_MM(0x002C) +#define MDP_CC_REG REG_MM(0x00C0) +#define MDP_MD0_REG REG_MM(0x00C4) +#define MDP_MD1_REG REG_MM(0x00C8) +#define MDP_NS_REG REG_MM(0x00D0) +#define MISC_CC_REG REG_MM(0x0058) +#define MISC_CC2_REG REG_MM(0x005C) +#define PIXEL_CC_REG REG_MM(0x00D4) +#define PIXEL_CC2_REG REG_MM(0x0120) +#define PIXEL_MD_REG REG_MM(0x00D8) +#define PIXEL_NS_REG REG_MM(0x00DC) +#define MM_PLL0_MODE_REG REG_MM(0x0300) +#define MM_PLL1_MODE_REG REG_MM(0x031C) +#define MM_PLL2_CONFIG_REG REG_MM(0x0348) +#define MM_PLL2_L_VAL_REG REG_MM(0x033C) +#define MM_PLL2_M_VAL_REG REG_MM(0x0340) +#define MM_PLL2_MODE_REG REG_MM(0x0338) +#define MM_PLL2_N_VAL_REG REG_MM(0x0344) +#define ROT_CC_REG REG_MM(0x00E0) +#define ROT_NS_REG REG_MM(0x00E8) +#define SAXI_EN_REG REG_MM(0x0030) +#define SW_RESET_AHB_REG REG_MM(0x020C) +#define SW_RESET_ALL_REG REG_MM(0x0204) +#define SW_RESET_AXI_REG REG_MM(0x0208) +#define SW_RESET_CORE_REG REG_MM(0x0210) +#define TV_CC_REG REG_MM(0x00EC) +#define TV_CC2_REG REG_MM(0x0124) +#define TV_MD_REG REG_MM(0x00F0) +#define TV_NS_REG REG_MM(0x00F4) +#define VCODEC_CC_REG REG_MM(0x00F8) +#define VCODEC_MD0_REG REG_MM(0x00FC) +#define VCODEC_MD1_REG REG_MM(0x0128) +#define VCODEC_NS_REG REG_MM(0x0100) +#define VFE_CC_REG REG_MM(0x0104) +#define VFE_MD_REG REG_MM(0x0108) +#define VFE_NS_REG REG_MM(0x010C) +#define VPE_CC_REG REG_MM(0x0110) +#define VPE_NS_REG REG_MM(0x0118) + +/* Low-power Audio clock registers. */ +#define LCC_CLK_LS_DEBUG_CFG_REG REG_LPA(0x00A8) +#define LCC_CODEC_I2S_MIC_MD_REG REG_LPA(0x0064) +#define LCC_CODEC_I2S_MIC_NS_REG REG_LPA(0x0060) +#define LCC_CODEC_I2S_MIC_STATUS_REG REG_LPA(0x0068) +#define LCC_CODEC_I2S_SPKR_MD_REG REG_LPA(0x0070) +#define LCC_CODEC_I2S_SPKR_NS_REG REG_LPA(0x006C) +#define LCC_CODEC_I2S_SPKR_STATUS_REG REG_LPA(0x0074) +#define LCC_MI2S_MD_REG REG_LPA(0x004C) +#define LCC_MI2S_NS_REG REG_LPA(0x0048) +#define LCC_MI2S_STATUS_REG REG_LPA(0x0050) +#define LCC_PCM_MD_REG REG_LPA(0x0058) +#define LCC_PCM_NS_REG REG_LPA(0x0054) +#define LCC_PCM_STATUS_REG REG_LPA(0x005C) +#define LCC_PLL0_CONFIG_REG REG_LPA(0x0014) +#define LCC_PLL0_L_VAL_REG REG_LPA(0x0004) +#define LCC_PLL0_M_VAL_REG REG_LPA(0x0008) +#define LCC_PLL0_MODE_REG REG_LPA(0x0000) +#define LCC_PLL0_N_VAL_REG REG_LPA(0x000C) +#define LCC_PRI_PLL_CLK_CTL_REG REG_LPA(0x00C4) +#define LCC_SPARE_I2S_MIC_MD_REG REG_LPA(0x007C) +#define LCC_SPARE_I2S_MIC_NS_REG REG_LPA(0x0078) +#define LCC_SPARE_I2S_MIC_STATUS_REG REG_LPA(0x0080) +#define LCC_SPARE_I2S_SPKR_MD_REG REG_LPA(0x0088) +#define LCC_SPARE_I2S_SPKR_NS_REG REG_LPA(0x0084) +#define LCC_SPARE_I2S_SPKR_STATUS_REG REG_LPA(0x008C) + +/* MUX source input identifiers. */ +#define pxo_to_bb_mux 0 +#define mxo_to_bb_mux 1 +#define cxo_to_bb_mux 5 +#define pll0_to_bb_mux 2 +#define pll8_to_bb_mux 3 +#define pll6_to_bb_mux 4 +#define gnd_to_bb_mux 6 +#define pxo_to_mm_mux 0 +#define pll1_to_mm_mux 1 /* or MMSS_PLL0 */ +#define pll2_to_mm_mux 1 /* or MMSS_PLL1 */ +#define pll3_to_mm_mux 3 /* or MMSS_PLL2 */ +#define pll8_to_mm_mux 2 /* or MMSS_GPERF */ +#define pll0_to_mm_mux 3 /* or MMSS_GPLL0 */ +#define mxo_to_mm_mux 4 +#define gnd_to_mm_mux 6 +#define cxo_to_xo_mux 0 +#define pxo_to_xo_mux 1 +#define mxo_to_xo_mux 2 +#define gnd_to_xo_mux 3 +#define pxo_to_lpa_mux 0 +#define cxo_to_lpa_mux 1 +#define pll4_to_lpa_mux 2 /* or LPA_PLL0 */ +#define gnd_to_lpa_mux 6 + +/* Test Vector Macros */ +#define TEST_TYPE_PER_LS 1 +#define TEST_TYPE_PER_HS 2 +#define TEST_TYPE_MM_LS 3 +#define TEST_TYPE_MM_HS 4 +#define TEST_TYPE_LPA 5 +#define TEST_TYPE_SC 6 +#define TEST_TYPE_MM_HS2X 7 +#define TEST_TYPE_SHIFT 24 +#define TEST_CLK_SEL_MASK BM(23, 0) +#define TEST_VECTOR(s, t) (((t) << TEST_TYPE_SHIFT) | BVAL(23, 0, (s))) +#define TEST_PER_LS(s) TEST_VECTOR((s), TEST_TYPE_PER_LS) +#define TEST_PER_HS(s) TEST_VECTOR((s), TEST_TYPE_PER_HS) +#define TEST_MM_LS(s) TEST_VECTOR((s), TEST_TYPE_MM_LS) +#define TEST_MM_HS(s) TEST_VECTOR((s), TEST_TYPE_MM_HS) +#define TEST_LPA(s) TEST_VECTOR((s), TEST_TYPE_LPA) +#define TEST_SC(s) TEST_VECTOR((s), TEST_TYPE_SC) +#define TEST_MM_HS2X(s) TEST_VECTOR((s), TEST_TYPE_MM_HS2X) + +struct pll_rate { + const uint32_t l_val; + const uint32_t m_val; + const uint32_t n_val; + const uint32_t vco; + const uint32_t post_div; + const uint32_t i_bits; +}; +#define PLL_RATE(l, m, n, v, d, i) { l, m, n, v, (d>>1), i } +/* + * Clock frequency definitions and macros + */ + +enum vdd_dig_levels { + VDD_DIG_NONE, + VDD_DIG_LOW, + VDD_DIG_NOMINAL, + VDD_DIG_HIGH +}; + +static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level) +{ + static const int vdd_uv[] = { + [VDD_DIG_NONE] = 500000, + [VDD_DIG_LOW] = 1000000, + [VDD_DIG_NOMINAL] = 1100000, + [VDD_DIG_HIGH] = 1200000 + }; + + return rpm_vreg_set_voltage(RPM_VREG_ID_PM8058_S1, RPM_VREG_VOTER3, + vdd_uv[level], 1200000, 1); +} + +static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig); + +#define VDD_DIG_FMAX_MAP1(l1, f1) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1) +#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2) +#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2), \ + .fmax[VDD_DIG_##l3] = (f3) + +DEFINE_CLK_RPM_BRANCH(pxo_clk, pxo_a_clk, PXO, 27000000); +DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000); + +static struct pll_vote_clk pll8_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(8), + .status_reg = BB_PLL8_STATUS_REG, + .status_mask = BIT(16), + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll8_clk", + .rate = 384000000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll8_clk.c), + .warned = true, + }, +}; + +static struct pll_clk pll2_clk = { + .mode_reg = MM_PLL1_MODE_REG, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll2_clk", + .rate = 800000000, + .ops = &clk_ops_local_pll, + CLK_INIT(pll2_clk.c), + .warned = true, + }, +}; + +static struct pll_clk pll3_clk = { + .mode_reg = MM_PLL2_MODE_REG, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "pll3_clk", + .rate = 0, /* TODO: Detect rate dynamically */ + .ops = &clk_ops_local_pll, + CLK_INIT(pll3_clk.c), + .warned = true, + }, +}; + +static int pll4_clk_enable(struct clk *clk) +{ + struct msm_rpm_iv_pair iv = { MSM_RPM_ID_PLL_4, 1 }; + return msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1); +} + +static void pll4_clk_disable(struct clk *clk) +{ + struct msm_rpm_iv_pair iv = { MSM_RPM_ID_PLL_4, 0 }; + msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1); +} + +static struct clk *pll4_clk_get_parent(struct clk *clk) +{ + return &pxo_clk.c; +} + +static bool pll4_clk_is_local(struct clk *clk) +{ + return false; +} + +static enum handoff pll4_clk_handoff(struct clk *clk) +{ + struct msm_rpm_iv_pair iv = { MSM_RPM_ID_PLL_4 }; + int rc = msm_rpm_get_status(&iv, 1); + if (rc < 0 || !iv.value) + return HANDOFF_DISABLED_CLK; + + return HANDOFF_ENABLED_CLK; +} + +static struct clk_ops clk_ops_pll4 = { + .enable = pll4_clk_enable, + .disable = pll4_clk_disable, + .get_parent = pll4_clk_get_parent, + .is_local = pll4_clk_is_local, + .handoff = pll4_clk_handoff, +}; + +static struct fixed_clk pll4_clk = { + .c = { + .dbg_name = "pll4_clk", + .rate = 540672000, + .ops = &clk_ops_pll4, + CLK_INIT(pll4_clk.c), + .warned = true, + }, +}; + +/* + * SoC-specific Set-Rate Functions + */ + +/* Unlike other clocks, the TV rate is adjusted through PLL + * re-programming. It is also routed through an MND divider. */ +static void set_rate_tv(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + struct pll_rate *rate = nf->extra_freq_data; + uint32_t pll_mode, pll_config, misc_cc2; + + /* Disable PLL output. */ + pll_mode = readl_relaxed(MM_PLL2_MODE_REG); + pll_mode &= ~BIT(0); + writel_relaxed(pll_mode, MM_PLL2_MODE_REG); + + /* Assert active-low PLL reset. */ + pll_mode &= ~BIT(2); + writel_relaxed(pll_mode, MM_PLL2_MODE_REG); + + /* Program L, M and N values. */ + writel_relaxed(rate->l_val, MM_PLL2_L_VAL_REG); + writel_relaxed(rate->m_val, MM_PLL2_M_VAL_REG); + writel_relaxed(rate->n_val, MM_PLL2_N_VAL_REG); + + /* Configure MN counter, post-divide, VCO, and i-bits. */ + pll_config = readl_relaxed(MM_PLL2_CONFIG_REG); + pll_config &= ~(BM(22, 20) | BM(18, 0)); + pll_config |= rate->n_val ? BIT(22) : 0; + pll_config |= BVAL(21, 20, rate->post_div); + pll_config |= BVAL(17, 16, rate->vco); + pll_config |= rate->i_bits; + writel_relaxed(pll_config, MM_PLL2_CONFIG_REG); + + /* Configure MND. */ + set_rate_mnd(clk, nf); + + /* Configure hdmi_ref_clk to be equal to the TV clock rate. */ + misc_cc2 = readl_relaxed(MISC_CC2_REG); + misc_cc2 &= ~(BIT(28)|BM(21, 18)); + misc_cc2 |= (BIT(28)|BVAL(21, 18, (nf->ns_val >> 14) & 0x3)); + writel_relaxed(misc_cc2, MISC_CC2_REG); + + /* De-assert active-low PLL reset. */ + pll_mode |= BIT(2); + writel_relaxed(pll_mode, MM_PLL2_MODE_REG); + + /* Enable PLL output. */ + pll_mode |= BIT(0); + writel_relaxed(pll_mode, MM_PLL2_MODE_REG); +} + +/* + * Clock Descriptions + */ + +/* AXI Interfaces */ +static struct branch_clk gmem_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(24), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 6, + }, + .c = { + .dbg_name = "gmem_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(gmem_axi_clk.c), + }, +}; + +static struct branch_clk ijpeg_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(21), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 4, + }, + .c = { + .dbg_name = "ijpeg_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(ijpeg_axi_clk.c), + }, +}; + +static struct branch_clk imem_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(22), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(10), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 7, + .retain_reg = MAXI_EN2_REG, + .retain_mask = BIT(10), + }, + .c = { + .dbg_name = "imem_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(imem_axi_clk.c), + }, +}; + +static struct branch_clk jpegd_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(25), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 5, + }, + .c = { + .dbg_name = "jpegd_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(jpegd_axi_clk.c), + }, +}; + +static struct branch_clk mdp_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(23), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(13), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 8, + .retain_reg = MAXI_EN_REG, + .retain_mask = BIT(0), + }, + .c = { + .dbg_name = "mdp_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_axi_clk.c), + }, +}; + +static struct branch_clk vcodec_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(19), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(4)|BIT(5), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "vcodec_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcodec_axi_clk.c), + }, +}; + +static struct branch_clk vfe_axi_clk = { + .b = { + .ctl_reg = MAXI_EN_REG, + .en_mask = BIT(18), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 0, + }, + .c = { + .dbg_name = "vfe_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_axi_clk.c), + }, +}; + +static struct branch_clk rot_axi_clk = { + .b = { + .ctl_reg = MAXI_EN2_REG, + .en_mask = BIT(24), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(6), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 2, + }, + .c = { + .dbg_name = "rot_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(rot_axi_clk.c), + }, +}; + +static struct branch_clk vpe_axi_clk = { + .b = { + .ctl_reg = MAXI_EN2_REG, + .en_mask = BIT(26), + .reset_reg = SW_RESET_AXI_REG, + .reset_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_E_REG, + .halt_bit = 1, + }, + .c = { + .dbg_name = "vpe_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(vpe_axi_clk.c), + }, +}; + +static struct branch_clk smi_2x_axi_clk = { + .b = { + .ctl_reg = MAXI_EN2_REG, + .en_mask = BIT(30), + .halt_reg = DBG_BUS_VEC_I_REG, + .halt_bit = 0, + }, + .c = { + .dbg_name = "smi_2x_axi_clk", + .ops = &clk_ops_branch, + .flags = CLKFLAG_SKIP_AUTO_OFF, + CLK_INIT(smi_2x_axi_clk.c), + }, +}; + +/* AHB Interfaces */ +static struct branch_clk amp_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(24), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(20), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 18, + }, + .c = { + .dbg_name = "amp_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(amp_p_clk.c), + }, +}; + +static struct branch_clk csi0_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(7), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(17), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 16, + }, + .c = { + .dbg_name = "csi0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_p_clk.c), + }, +}; + +static struct branch_clk csi1_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(20), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(16), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 17, + }, + .c = { + .dbg_name = "csi1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi1_p_clk.c), + }, +}; + +static struct branch_clk dsi_m_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(9), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(6), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 19, + }, + .c = { + .dbg_name = "dsi_m_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi_m_p_clk.c), + }, +}; + +static struct branch_clk dsi_s_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(18), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(5), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 20, + }, + .c = { + .dbg_name = "dsi_s_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi_s_p_clk.c), + }, +}; + +static struct branch_clk gfx2d0_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(19), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(12), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 2, + }, + .c = { + .dbg_name = "gfx2d0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx2d0_p_clk.c), + }, +}; + +static struct branch_clk gfx2d1_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(2), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(11), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gfx2d1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx2d1_p_clk.c), + }, +}; + +static struct branch_clk gfx3d_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(3), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(10), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 4, + }, + .c = { + .dbg_name = "gfx3d_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gfx3d_p_clk.c), + }, +}; + +static struct branch_clk hdmi_m_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(14), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 5, + }, + .c = { + .dbg_name = "hdmi_m_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_m_p_clk.c), + }, +}; + +static struct branch_clk hdmi_s_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(4), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 6, + }, + .c = { + .dbg_name = "hdmi_s_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_s_p_clk.c), + }, +}; + +static struct branch_clk ijpeg_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(5), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(7), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 9, + }, + .c = { + .dbg_name = "ijpeg_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ijpeg_p_clk.c), + }, +}; + +static struct branch_clk imem_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(6), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(8), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 10, + }, + .c = { + .dbg_name = "imem_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(imem_p_clk.c), + }, +}; + +static struct branch_clk jpegd_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(21), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(4), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "jpegd_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(jpegd_p_clk.c), + }, +}; + +static struct branch_clk mdp_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(10), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(3), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "mdp_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_p_clk.c), + }, +}; + +static struct branch_clk rot_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(2), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 13, + }, + .c = { + .dbg_name = "rot_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(rot_p_clk.c), + }, +}; + +static struct branch_clk smmu_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 22, + }, + .c = { + .dbg_name = "smmu_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(smmu_p_clk.c), + }, +}; + +static struct branch_clk tv_enc_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(25), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 23, + }, + .c = { + .dbg_name = "tv_enc_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_enc_p_clk.c), + }, +}; + +static struct branch_clk vcodec_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(11), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(1), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "vcodec_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vcodec_p_clk.c), + }, +}; + +static struct branch_clk vfe_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(13), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 14, + .retain_reg = AHB_EN2_REG, + .retain_mask = BIT(0), + }, + .c = { + .dbg_name = "vfe_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vfe_p_clk.c), + }, +}; + +static struct branch_clk vpe_p_clk = { + .b = { + .ctl_reg = AHB_EN_REG, + .en_mask = BIT(16), + .reset_reg = SW_RESET_AHB_REG, + .reset_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_F_REG, + .halt_bit = 15, + }, + .c = { + .dbg_name = "vpe_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(vpe_p_clk.c), + }, +}; + +/* + * Peripheral Clocks + */ +#define CLK_GP(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GPn_NS_REG(n), \ + .en_mask = BIT(9), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GPn_NS_REG(n), \ + .md_reg = GPn_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gp, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(LOW, 27000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gp[] = { + F_GP( 0, gnd, 1, 0, 0), + F_GP( 9600000, cxo, 2, 0, 0), + F_GP( 13500000, pxo, 2, 0, 0), + F_GP( 19200000, cxo, 1, 0, 0), + F_GP( 27000000, pxo, 1, 0, 0), + F_END +}; + +static CLK_GP(gp0, 0, CLK_HALT_SFPB_MISC_STATE_REG, 7); +static CLK_GP(gp1, 1, CLK_HALT_SFPB_MISC_STATE_REG, 6); +static CLK_GP(gp2, 2, CLK_HALT_SFPB_MISC_STATE_REG, 5); + +#define CLK_GSBI_UART(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GSBIn_UART_APPS_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = GSBIn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GSBIn_UART_APPS_NS_REG(n), \ + .md_reg = GSBIn_UART_APPS_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(31, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gsbi_uart, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 32000000, NOMINAL, 64000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GSBI_UART(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gsbi_uart[] = { + F_GSBI_UART( 0, gnd, 1, 0, 0), + F_GSBI_UART( 1843200, pll8, 1, 3, 625), + F_GSBI_UART( 3686400, pll8, 1, 6, 625), + F_GSBI_UART( 7372800, pll8, 1, 12, 625), + F_GSBI_UART(14745600, pll8, 1, 24, 625), + F_GSBI_UART(16000000, pll8, 4, 1, 6), + F_GSBI_UART(24000000, pll8, 4, 1, 4), + F_GSBI_UART(32000000, pll8, 4, 1, 3), + F_GSBI_UART(40000000, pll8, 1, 5, 48), + F_GSBI_UART(46400000, pll8, 1, 29, 240), + F_GSBI_UART(48000000, pll8, 4, 1, 2), + F_GSBI_UART(51200000, pll8, 1, 2, 15), + F_GSBI_UART(56000000, pll8, 1, 7, 48), + F_GSBI_UART(58982400, pll8, 1, 96, 625), + F_GSBI_UART(64000000, pll8, 2, 1, 3), + F_END +}; + +static CLK_GSBI_UART(gsbi1_uart, 1, CLK_HALT_CFPB_STATEA_REG, 10); +static CLK_GSBI_UART(gsbi2_uart, 2, CLK_HALT_CFPB_STATEA_REG, 6); +static CLK_GSBI_UART(gsbi3_uart, 3, CLK_HALT_CFPB_STATEA_REG, 2); +static CLK_GSBI_UART(gsbi4_uart, 4, CLK_HALT_CFPB_STATEB_REG, 26); +static CLK_GSBI_UART(gsbi5_uart, 5, CLK_HALT_CFPB_STATEB_REG, 22); +static CLK_GSBI_UART(gsbi6_uart, 6, CLK_HALT_CFPB_STATEB_REG, 18); +static CLK_GSBI_UART(gsbi7_uart, 7, CLK_HALT_CFPB_STATEB_REG, 14); +static CLK_GSBI_UART(gsbi8_uart, 8, CLK_HALT_CFPB_STATEB_REG, 10); +static CLK_GSBI_UART(gsbi9_uart, 9, CLK_HALT_CFPB_STATEB_REG, 6); +static CLK_GSBI_UART(gsbi10_uart, 10, CLK_HALT_CFPB_STATEB_REG, 2); +static CLK_GSBI_UART(gsbi11_uart, 11, CLK_HALT_CFPB_STATEC_REG, 17); +static CLK_GSBI_UART(gsbi12_uart, 12, CLK_HALT_CFPB_STATEC_REG, 13); + +#define CLK_GSBI_QUP(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GSBIn_QUP_APPS_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = GSBIn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GSBIn_QUP_APPS_NS_REG(n), \ + .md_reg = GSBIn_QUP_APPS_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gsbi_qup, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 24000000, NOMINAL, 52000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GSBI_QUP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gsbi_qup[] = { + F_GSBI_QUP( 0, gnd, 1, 0, 0), + F_GSBI_QUP( 1100000, pxo, 1, 2, 49), + F_GSBI_QUP( 5400000, pxo, 1, 1, 5), + F_GSBI_QUP(10800000, pxo, 1, 2, 5), + F_GSBI_QUP(15060000, pll8, 1, 2, 51), + F_GSBI_QUP(24000000, pll8, 4, 1, 4), + F_GSBI_QUP(25600000, pll8, 1, 1, 15), + F_GSBI_QUP(27000000, pxo, 1, 0, 0), + F_GSBI_QUP(48000000, pll8, 4, 1, 2), + F_GSBI_QUP(51200000, pll8, 1, 2, 15), + F_END +}; + +static CLK_GSBI_QUP(gsbi1_qup, 1, CLK_HALT_CFPB_STATEA_REG, 9); +static CLK_GSBI_QUP(gsbi2_qup, 2, CLK_HALT_CFPB_STATEA_REG, 4); +static CLK_GSBI_QUP(gsbi3_qup, 3, CLK_HALT_CFPB_STATEA_REG, 0); +static CLK_GSBI_QUP(gsbi4_qup, 4, CLK_HALT_CFPB_STATEB_REG, 24); +static CLK_GSBI_QUP(gsbi5_qup, 5, CLK_HALT_CFPB_STATEB_REG, 20); +static CLK_GSBI_QUP(gsbi6_qup, 6, CLK_HALT_CFPB_STATEB_REG, 16); +static CLK_GSBI_QUP(gsbi7_qup, 7, CLK_HALT_CFPB_STATEB_REG, 12); +static CLK_GSBI_QUP(gsbi8_qup, 8, CLK_HALT_CFPB_STATEB_REG, 8); +static CLK_GSBI_QUP(gsbi9_qup, 9, CLK_HALT_CFPB_STATEB_REG, 4); +static CLK_GSBI_QUP(gsbi10_qup, 10, CLK_HALT_CFPB_STATEB_REG, 0); +static CLK_GSBI_QUP(gsbi11_qup, 11, CLK_HALT_CFPB_STATEC_REG, 15); +static CLK_GSBI_QUP(gsbi12_qup, 12, CLK_HALT_CFPB_STATEC_REG, 11); + +#define F_PDM(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(1, 0, s##_to_xo_mux), \ + } +static struct clk_freq_tbl clk_tbl_pdm[] = { + F_PDM( 0, gnd, 1), + F_PDM(27000000, pxo, 1), + F_END +}; + +static struct rcg_clk pdm_clk = { + .b = { + .ctl_reg = PDM_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = PDM_CLK_NS_REG, + .reset_mask = BIT(12), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 3, + }, + .ns_reg = PDM_CLK_NS_REG, + .root_en_mask = BIT(11), + .ns_mask = BM(1, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_pdm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pdm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 27000000), + CLK_INIT(pdm_clk.c), + }, +}; + +static struct branch_clk pmem_clk = { + .b = { + .ctl_reg = PMEM_ACLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 20, + }, + .c = { + .dbg_name = "pmem_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmem_clk.c), + }, +}; + +#define F_PRNG(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + } +static struct clk_freq_tbl clk_tbl_prng_32[] = { + F_PRNG(32000000, pll8), + F_END +}; + +static struct clk_freq_tbl clk_tbl_prng_64[] = { + F_PRNG(64000000, pll8), + F_END +}; + +static struct rcg_clk prng_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(10), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 10, + }, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_prng_32, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "prng_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 32000000, NOMINAL, 64000000), + CLK_INIT(prng_clk.c), + }, +}; + +#define CLK_SDC(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = SDCn_APPS_CLK_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = SDCn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = SDCn_APPS_CLK_NS_REG(n), \ + .md_reg = SDCn_APPS_CLK_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_sdc, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_SDC(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_sdc[] = { + F_SDC( 0, gnd, 1, 0, 0), + F_SDC( 144000, pxo, 3, 2, 125), + F_SDC( 400000, pll8, 4, 1, 240), + F_SDC(16000000, pll8, 4, 1, 6), + F_SDC(17070000, pll8, 1, 2, 45), + F_SDC(20210000, pll8, 1, 1, 19), + F_SDC(24000000, pll8, 4, 1, 4), + F_SDC(48000000, pll8, 4, 1, 2), + F_END +}; + +static CLK_SDC(sdc1, 1, CLK_HALT_DFAB_STATE_REG, 6); +static CLK_SDC(sdc2, 2, CLK_HALT_DFAB_STATE_REG, 5); +static CLK_SDC(sdc3, 3, CLK_HALT_DFAB_STATE_REG, 4); +static CLK_SDC(sdc4, 4, CLK_HALT_DFAB_STATE_REG, 3); +static CLK_SDC(sdc5, 5, CLK_HALT_DFAB_STATE_REG, 2); + +#define F_TSIF_REF(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_tsif_ref[] = { + F_TSIF_REF( 0, gnd, 1, 0, 0), + F_TSIF_REF(105000, pxo, 1, 1, 256), + F_END +}; + +static struct rcg_clk tsif_ref_clk = { + .b = { + .ctl_reg = TSIF_REF_CLK_NS_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 5, + }, + .ns_reg = TSIF_REF_CLK_NS_REG, + .md_reg = TSIF_REF_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(31, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_tsif_ref, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "tsif_ref_clk", + .ops = &clk_ops_rcg, + CLK_INIT(tsif_ref_clk.c), + }, +}; + +#define F_TSSC(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(1, 0, s##_to_xo_mux), \ + } +static struct clk_freq_tbl clk_tbl_tssc[] = { + F_TSSC( 0, gnd), + F_TSSC(27000000, pxo), + F_END +}; + +static struct rcg_clk tssc_clk = { + .b = { + .ctl_reg = TSSC_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 4, + }, + .ns_reg = TSSC_CLK_CTL_REG, + .ns_mask = BM(1, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_tssc, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "tssc_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 27000000), + CLK_INIT(tssc_clk.c), + }, +}; + +#define F_USB(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_usb[] = { + F_USB( 0, gnd, 1, 0, 0), + F_USB(60000000, pll8, 1, 5, 32), + F_END +}; + +static struct rcg_clk usb_hs1_xcvr_clk = { + .b = { + .ctl_reg = USB_HS1_XCVR_FS_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = USB_HS1_RESET_REG, + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 0, + }, + .ns_reg = USB_HS1_XCVR_FS_CLK_NS_REG, + .md_reg = USB_HS1_XCVR_FS_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hs1_xcvr_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 60000000), + CLK_INIT(usb_hs1_xcvr_clk.c), + }, +}; + +static struct branch_clk usb_phy0_clk = { + .b = { + .reset_reg = USB_PHY0_RESET_REG, + .reset_mask = BIT(0), + }, + .c = { + .dbg_name = "usb_phy0_clk", + .ops = &clk_ops_reset, + CLK_INIT(usb_phy0_clk.c), + }, +}; + +#define CLK_USB_FS(i, n) \ + struct rcg_clk i##_clk = { \ + .ns_reg = USB_FSn_XCVR_FS_CLK_NS_REG(n), \ + .b = { \ + .ctl_reg = USB_FSn_XCVR_FS_CLK_NS_REG(n), \ + .halt_check = NOCHECK, \ + }, \ + .md_reg = USB_FSn_XCVR_FS_CLK_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_usb, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(NOMINAL, 60000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +static CLK_USB_FS(usb_fs1_src, 1); +static struct branch_clk usb_fs1_xcvr_clk = { + .b = { + .ctl_reg = USB_FSn_XCVR_FS_CLK_NS_REG(1), + .en_mask = BIT(9), + .reset_reg = USB_FSn_RESET_REG(1), + .reset_mask = BIT(1), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 15, + }, + .parent = &usb_fs1_src_clk.c, + .c = { + .dbg_name = "usb_fs1_xcvr_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs1_xcvr_clk.c), + }, +}; + +static struct branch_clk usb_fs1_sys_clk = { + .b = { + .ctl_reg = USB_FSn_SYSTEM_CLK_CTL_REG(1), + .en_mask = BIT(4), + .reset_reg = USB_FSn_RESET_REG(1), + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 16, + }, + .parent = &usb_fs1_src_clk.c, + .c = { + .dbg_name = "usb_fs1_sys_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs1_sys_clk.c), + }, +}; + +static CLK_USB_FS(usb_fs2_src, 2); +static struct branch_clk usb_fs2_xcvr_clk = { + .b = { + .ctl_reg = USB_FSn_XCVR_FS_CLK_NS_REG(2), + .en_mask = BIT(9), + .reset_reg = USB_FSn_RESET_REG(2), + .reset_mask = BIT(1), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 12, + }, + .parent = &usb_fs2_src_clk.c, + .c = { + .dbg_name = "usb_fs2_xcvr_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs2_xcvr_clk.c), + }, +}; + +static struct branch_clk usb_fs2_sys_clk = { + .b = { + .ctl_reg = USB_FSn_SYSTEM_CLK_CTL_REG(2), + .en_mask = BIT(4), + .reset_reg = USB_FSn_RESET_REG(2), + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 13, + }, + .parent = &usb_fs2_src_clk.c, + .c = { + .dbg_name = "usb_fs2_sys_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs2_sys_clk.c), + }, +}; + +/* Fast Peripheral Bus Clocks */ +static struct branch_clk ce2_p_clk = { + .b = { + .ctl_reg = CE2_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 0, + }, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "ce2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce2_p_clk.c), + }, +}; + +static struct branch_clk gsbi1_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "gsbi1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi1_p_clk.c), + }, +}; + +static struct branch_clk gsbi2_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "gsbi2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi2_p_clk.c), + }, +}; + +static struct branch_clk gsbi3_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(3), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gsbi3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi3_p_clk.c), + }, +}; + +static struct branch_clk gsbi4_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(4), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 27, + }, + .c = { + .dbg_name = "gsbi4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi4_p_clk.c), + }, +}; + +static struct branch_clk gsbi5_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(5), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 23, + }, + .c = { + .dbg_name = "gsbi5_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi5_p_clk.c), + }, +}; + +static struct branch_clk gsbi6_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(6), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 19, + }, + .c = { + .dbg_name = "gsbi6_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi6_p_clk.c), + }, +}; + +static struct branch_clk gsbi7_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(7), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 15, + }, + .c = { + .dbg_name = "gsbi7_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi7_p_clk.c), + }, +}; + +static struct branch_clk gsbi8_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(8), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "gsbi8_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi8_p_clk.c), + }, +}; + +static struct branch_clk gsbi9_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(9), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "gsbi9_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi9_p_clk.c), + }, +}; + +static struct branch_clk gsbi10_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(10), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gsbi10_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi10_p_clk.c), + }, +}; + +static struct branch_clk gsbi11_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(11), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 18, + }, + .c = { + .dbg_name = "gsbi11_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi11_p_clk.c), + }, +}; + +static struct branch_clk gsbi12_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(12), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 14, + }, + .c = { + .dbg_name = "gsbi12_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi12_p_clk.c), + }, +}; + +static struct branch_clk ppss_p_clk = { + .b = { + .ctl_reg = PPSS_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 19, + }, + .c = { + .dbg_name = "ppss_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ppss_p_clk.c), + }, +}; + +static struct branch_clk tsif_p_clk = { + .b = { + .ctl_reg = TSIF_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "tsif_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(tsif_p_clk.c), + }, +}; + +static struct branch_clk usb_fs1_p_clk = { + .b = { + .ctl_reg = USB_FSn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 17, + }, + .c = { + .dbg_name = "usb_fs1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs1_p_clk.c), + }, +}; + +static struct branch_clk usb_fs2_p_clk = { + .b = { + .ctl_reg = USB_FSn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 14, + }, + .c = { + .dbg_name = "usb_fs2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_fs2_p_clk.c), + }, +}; + +static struct branch_clk usb_hs1_p_clk = { + .b = { + .ctl_reg = USB_HS1_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 1, + }, + .c = { + .dbg_name = "usb_hs1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs1_p_clk.c), + }, +}; + +static struct branch_clk sdc1_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "sdc1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc1_p_clk.c), + }, +}; + +static struct branch_clk sdc2_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 10, + }, + .c = { + .dbg_name = "sdc2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc2_p_clk.c), + }, +}; + +static struct branch_clk sdc3_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(3), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 9, + }, + .c = { + .dbg_name = "sdc3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc3_p_clk.c), + }, +}; + +static struct branch_clk sdc4_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(4), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 8, + }, + .c = { + .dbg_name = "sdc4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc4_p_clk.c), + }, +}; + +static struct branch_clk sdc5_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(5), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "sdc5_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc5_p_clk.c), + }, +}; + +static struct branch_clk ebi2_2x_clk = { + .b = { + .ctl_reg = EBI2_2X_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 18, + }, + .c = { + .dbg_name = "ebi2_2x_clk", + .ops = &clk_ops_branch, + CLK_INIT(ebi2_2x_clk.c), + }, +}; + +static struct branch_clk ebi2_clk = { + .b = { + .ctl_reg = EBI2_CLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 19, + }, + .c = { + .dbg_name = "ebi2_clk", + .ops = &clk_ops_branch, + CLK_INIT(ebi2_clk.c), + .depends = &ebi2_2x_clk.c, + }, +}; + +/* HW-Voteable Clocks */ +static struct branch_clk adm0_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(2), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 14, + }, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "adm0_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm0_clk.c), + }, +}; + +static struct branch_clk adm0_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(3), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 13, + }, + .c = { + .dbg_name = "adm0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm0_p_clk.c), + }, +}; + +static struct branch_clk adm1_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 12, + }, + .parent = &pxo_clk.c, + .c = { + .dbg_name = "adm1_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm1_clk.c), + }, +}; + +static struct branch_clk adm1_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(5), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 11, + }, + .c = { + .dbg_name = "adm1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm1_p_clk.c), + }, +}; + +static struct branch_clk modem_ahb1_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 8, + }, + .c = { + .dbg_name = "modem_ahb1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(modem_ahb1_p_clk.c), + }, +}; + +static struct branch_clk modem_ahb2_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(1), + .halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 7, + }, + .c = { + .dbg_name = "modem_ahb2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(modem_ahb2_p_clk.c), + }, +}; + +static struct branch_clk pmic_arb0_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(8), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 22, + }, + .c = { + .dbg_name = "pmic_arb0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_arb0_p_clk.c), + }, +}; + +static struct branch_clk pmic_arb1_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 21, + }, + .c = { + .dbg_name = "pmic_arb1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_arb1_p_clk.c), + }, +}; + +static struct branch_clk pmic_ssbi2_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(7), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 23, + }, + .c = { + .dbg_name = "pmic_ssbi2_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_ssbi2_clk.c), + }, +}; + +static struct branch_clk rpm_msg_ram_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(6), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 12, + }, + .c = { + .dbg_name = "rpm_msg_ram_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(rpm_msg_ram_p_clk.c), + }, +}; + +/* + * Multimedia Clocks + */ + +#define F_CAM(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(31, 24, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_cam[] = { + F_CAM( 0, gnd, 1, 0, 0), + F_CAM( 6000000, pll8, 4, 1, 16), + F_CAM( 8000000, pll8, 4, 1, 12), + F_CAM( 12000000, pll8, 4, 1, 8), + F_CAM( 16000000, pll8, 4, 1, 6), + F_CAM( 19200000, pll8, 4, 1, 5), + F_CAM( 24000000, pll8, 4, 1, 4), + F_CAM( 32000000, pll8, 4, 1, 3), + F_CAM( 48000000, pll8, 4, 1, 2), + F_CAM( 64000000, pll8, 3, 1, 2), + F_CAM( 96000000, pll8, 4, 0, 0), + F_CAM(128000000, pll8, 3, 0, 0), + F_END +}; + +static struct rcg_clk cam_clk = { + .b = { + .ctl_reg = CAMCLK_CC_REG, + .en_mask = BIT(0), + .halt_check = DELAY, + }, + .ns_reg = CAMCLK_NS_REG, + .md_reg = CAMCLK_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(31, 24) | BM(15, 14) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd_8, + .freq_tbl = clk_tbl_cam, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "cam_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 64000000, NOMINAL, 128000000), + CLK_INIT(cam_clk.c), + }, +}; + +#define F_CSI(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(15, 12, d, 2, 0, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_csi[] = { + F_CSI( 0, gnd, 1), + F_CSI(192000000, pll8, 2), + F_CSI(384000000, pll8, 1), + F_END +}; + +static struct rcg_clk csi_src_clk = { + .ns_reg = CSI_NS_REG, + .b = { + .ctl_reg = CSI_CC_REG, + .halt_check = NOCHECK, + }, + .root_en_mask = BIT(2), + .ns_mask = (BM(15, 12) | BM(2, 0)), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_csi, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "csi_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 192000000, NOMINAL, 384000000), + CLK_INIT(csi_src_clk.c), + }, +}; + +static struct branch_clk csi0_clk = { + .b = { + .ctl_reg = CSI_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(8), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 13, + }, + .parent = &csi_src_clk.c, + .c = { + .dbg_name = "csi0_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_clk.c), + }, +}; + +static struct branch_clk csi1_clk = { + .b = { + .ctl_reg = CSI_CC_REG, + .en_mask = BIT(7), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(18), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 14, + }, + .parent = &csi_src_clk.c, + .c = { + .dbg_name = "csi1_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi1_clk.c), + }, +}; + +#define F_DSI(d) \ + { \ + .freq_hz = d, \ + .ns_val = BVAL(27, 24, (d-1)), \ + } +/* The DSI_BYTE clock is sourced from the DSI PHY PLL, which may change rate + * without this clock driver knowing. So, overload the clk_set_rate() to set + * the divider (1 to 16) of the clock with respect to the PLL rate. */ +static struct clk_freq_tbl clk_tbl_dsi_byte[] = { + F_DSI(1), F_DSI(2), F_DSI(3), F_DSI(4), + F_DSI(5), F_DSI(6), F_DSI(7), F_DSI(8), + F_DSI(9), F_DSI(10), F_DSI(11), F_DSI(12), + F_DSI(13), F_DSI(14), F_DSI(15), F_DSI(16), + F_END +}; + + +static struct rcg_clk dsi_byte_clk = { + .b = { + .ctl_reg = MISC_CC_REG, + .halt_check = DELAY, + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(7), + .retain_reg = MISC_CC2_REG, + .retain_mask = BIT(10), + }, + .ns_reg = MISC_CC2_REG, + .root_en_mask = BIT(2), + .ns_mask = BM(27, 24), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_dsi_byte, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "dsi_byte_clk", + .ops = &clk_ops_rcg, + CLK_INIT(dsi_byte_clk.c), + }, +}; + +static struct branch_clk dsi_esc_clk = { + .b = { + .ctl_reg = MISC_CC_REG, + .en_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 24, + }, + .c = { + .dbg_name = "dsi_esc_clk", + .ops = &clk_ops_branch, + CLK_INIT(dsi_esc_clk.c), + }, +}; + +#define F_GFX2D(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD4(4, m, 0, n), \ + .ns_val = NS_MND_BANKED4(20, 16, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } +static struct clk_freq_tbl clk_tbl_gfx2d[] = { + F_GFX2D( 0, gnd, 0, 0), + F_GFX2D( 27000000, pxo, 0, 0), + F_GFX2D( 48000000, pll8, 1, 8), + F_GFX2D( 54857000, pll8, 1, 7), + F_GFX2D( 64000000, pll8, 1, 6), + F_GFX2D( 76800000, pll8, 1, 5), + F_GFX2D( 96000000, pll8, 1, 4), + F_GFX2D(128000000, pll8, 1, 3), + F_GFX2D(145455000, pll2, 2, 11), + F_GFX2D(160000000, pll2, 1, 5), + F_GFX2D(177778000, pll2, 2, 9), + F_GFX2D(200000000, pll2, 1, 4), + F_GFX2D(228571000, pll2, 2, 7), + F_END +}; + +static struct bank_masks bmnd_info_gfx2d0 = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = GFX2D0_MD0_REG, + .ns_mask = BM(23, 20) | BM(5, 3), + .rst_mask = BIT(25), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = GFX2D0_MD1_REG, + .ns_mask = BM(19, 16) | BM(2, 0), + .rst_mask = BIT(24), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk gfx2d0_clk = { + .b = { + .ctl_reg = GFX2D0_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(14), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 9, + .retain_reg = GFX2D0_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = GFX2D0_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_gfx2d, + .bank_info = &bmnd_info_gfx2d0, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "gfx2d0_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 100000000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(gfx2d0_clk.c), + }, +}; + +static struct bank_masks bmnd_info_gfx2d1 = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = GFX2D1_MD0_REG, + .ns_mask = BM(23, 20) | BM(5, 3), + .rst_mask = BIT(25), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = GFX2D1_MD1_REG, + .ns_mask = BM(19, 16) | BM(2, 0), + .rst_mask = BIT(24), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk gfx2d1_clk = { + .b = { + .ctl_reg = GFX2D1_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(13), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 14, + .retain_reg = GFX2D1_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = GFX2D1_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_gfx2d, + .bank_info = &bmnd_info_gfx2d1, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "gfx2d1_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 100000000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(gfx2d1_clk.c), + }, +}; + +#define F_GFX3D(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD4(4, m, 0, n), \ + .ns_val = NS_MND_BANKED4(18, 14, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } +static struct clk_freq_tbl clk_tbl_gfx3d[] = { + F_GFX3D( 0, gnd, 0, 0), + F_GFX3D( 27000000, pxo, 0, 0), + F_GFX3D( 48000000, pll8, 1, 8), + F_GFX3D( 54857000, pll8, 1, 7), + F_GFX3D( 64000000, pll8, 1, 6), + F_GFX3D( 76800000, pll8, 1, 5), + F_GFX3D( 96000000, pll8, 1, 4), + F_GFX3D(128000000, pll8, 1, 3), + F_GFX3D(145455000, pll2, 2, 11), + F_GFX3D(160000000, pll2, 1, 5), + F_GFX3D(177778000, pll2, 2, 9), + F_GFX3D(200000000, pll2, 1, 4), + F_GFX3D(228571000, pll2, 2, 7), + F_GFX3D(266667000, pll2, 1, 3), + F_GFX3D(320000000, pll2, 2, 5), + F_END +}; + +static struct bank_masks bmnd_info_gfx3d = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = GFX3D_MD0_REG, + .ns_mask = BM(21, 18) | BM(5, 3), + .rst_mask = BIT(23), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = GFX3D_MD1_REG, + .ns_mask = BM(17, 14) | BM(2, 0), + .rst_mask = BIT(22), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk gfx3d_clk = { + .b = { + .ctl_reg = GFX3D_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(12), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 4, + .retain_reg = GFX3D_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = GFX3D_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_gfx3d, + .bank_info = &bmnd_info_gfx3d, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "gfx3d_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 96000000, NOMINAL, 200000000, + HIGH, 320000000), + CLK_INIT(gfx3d_clk.c), + .depends = &gmem_axi_clk.c, + }, +}; + +#define F_IJPEG(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 15, 12, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_ijpeg[] = { + F_IJPEG( 0, gnd, 1, 0, 0), + F_IJPEG( 27000000, pxo, 1, 0, 0), + F_IJPEG( 36570000, pll8, 1, 2, 21), + F_IJPEG( 54860000, pll8, 7, 0, 0), + F_IJPEG( 96000000, pll8, 4, 0, 0), + F_IJPEG(109710000, pll8, 1, 2, 7), + F_IJPEG(128000000, pll8, 3, 0, 0), + F_IJPEG(153600000, pll8, 1, 2, 5), + F_IJPEG(200000000, pll2, 4, 0, 0), + F_IJPEG(228571000, pll2, 1, 2, 7), + F_END +}; + +static struct rcg_clk ijpeg_clk = { + .b = { + .ctl_reg = IJPEG_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(9), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 24, + .retain_reg = IJPEG_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = IJPEG_NS_REG, + .md_reg = IJPEG_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(23, 16) | BM(15, 12) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_ijpeg, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "ijpeg_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 110000000, NOMINAL, 228571000), + CLK_INIT(ijpeg_clk.c), + .depends = &ijpeg_axi_clk.c, + }, +}; + +#define F_JPEGD(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(15, 12, d, 2, 0, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_jpegd[] = { + F_JPEGD( 0, gnd, 1), + F_JPEGD( 64000000, pll8, 6), + F_JPEGD( 76800000, pll8, 5), + F_JPEGD( 96000000, pll8, 4), + F_JPEGD(160000000, pll2, 5), + F_JPEGD(200000000, pll2, 4), + F_END +}; + +static struct rcg_clk jpegd_clk = { + .b = { + .ctl_reg = JPEGD_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(19), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 19, + .retain_reg = JPEGD_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = JPEGD_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(15, 12) | BM(2, 0)), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_jpegd, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "jpegd_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 96000000, NOMINAL, 200000000), + CLK_INIT(jpegd_clk.c), + .depends = &jpegd_axi_clk.c, + }, +}; + +#define F_MDP(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MND_BANKED8(22, 14, n, m, 3, 0, s##_to_mm_mux), \ + .ctl_val = CC_BANKED(9, 6, n), \ + } +static struct clk_freq_tbl clk_tbl_mdp[] = { + F_MDP( 0, gnd, 0, 0), + F_MDP( 9600000, pll8, 1, 40), + F_MDP( 13710000, pll8, 1, 28), + F_MDP( 27000000, pxo, 0, 0), + F_MDP( 29540000, pll8, 1, 13), + F_MDP( 34910000, pll8, 1, 11), + F_MDP( 38400000, pll8, 1, 10), + F_MDP( 59080000, pll8, 2, 13), + F_MDP( 76800000, pll8, 1, 5), + F_MDP( 85330000, pll8, 2, 9), + F_MDP( 96000000, pll8, 1, 4), + F_MDP(128000000, pll8, 1, 3), + F_MDP(160000000, pll2, 1, 5), + F_MDP(177780000, pll2, 2, 9), + F_MDP(200000000, pll2, 1, 4), + F_END +}; + +static struct bank_masks bmnd_info_mdp = { + .bank_sel_mask = BIT(11), + .bank0_mask = { + .md_reg = MDP_MD0_REG, + .ns_mask = BM(29, 22) | BM(5, 3), + .rst_mask = BIT(31), + .mnd_en_mask = BIT(8), + .mode_mask = BM(10, 9), + }, + .bank1_mask = { + .md_reg = MDP_MD1_REG, + .ns_mask = BM(21, 14) | BM(2, 0), + .rst_mask = BIT(30), + .mnd_en_mask = BIT(5), + .mode_mask = BM(7, 6), + }, +}; + +static struct rcg_clk mdp_clk = { + .b = { + .ctl_reg = MDP_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(21), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 10, + .retain_reg = MDP_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = MDP_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_mnd_banked, + .freq_tbl = clk_tbl_mdp, + .bank_info = &bmnd_info_mdp, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 85330000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(mdp_clk.c), + .depends = &mdp_axi_clk.c, + }, +}; + +#define F_MDP_VSYNC(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(13, 13, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_mdp_vsync[] = { + F_MDP_VSYNC(27000000, pxo), + F_END +}; + +static struct rcg_clk mdp_vsync_clk = { + .b = { + .ctl_reg = MISC_CC_REG, + .en_mask = BIT(6), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(3), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 22, + }, + .ns_reg = MISC_CC2_REG, + .ns_mask = BIT(13), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_mdp_vsync, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "mdp_vsync_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 27000000), + CLK_INIT(mdp_vsync_clk.c), + }, +}; + +#define F_PIXEL_MDP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS_MM(31, 16, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_pixel_mdp[] = { + F_PIXEL_MDP( 0, gnd, 1, 0, 0), + F_PIXEL_MDP( 25600000, pll8, 3, 1, 5), + F_PIXEL_MDP( 42667000, pll8, 1, 1, 9), + F_PIXEL_MDP( 43192000, pll8, 1, 64, 569), + F_PIXEL_MDP( 48000000, pll8, 4, 1, 2), + F_PIXEL_MDP( 53990000, pll8, 2, 169, 601), + F_PIXEL_MDP( 64000000, pll8, 2, 1, 3), + F_PIXEL_MDP( 69300000, pll8, 1, 231, 1280), + F_PIXEL_MDP( 76800000, pll8, 1, 1, 5), + F_PIXEL_MDP( 85333000, pll8, 1, 2, 9), + F_PIXEL_MDP(106500000, pll8, 1, 71, 256), + F_PIXEL_MDP(109714000, pll8, 1, 2, 7), + F_END +}; + +static struct rcg_clk pixel_mdp_clk = { + .ns_reg = PIXEL_NS_REG, + .md_reg = PIXEL_MD_REG, + .b = { + .ctl_reg = PIXEL_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(5), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 23, + .retain_reg = PIXEL_CC_REG, + .retain_mask = BIT(31), + }, + .root_en_mask = BIT(2), + .ns_mask = (BM(31, 16) | BM(15, 14) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_pixel_mdp, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pixel_mdp_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 85333000, NOMINAL, 170000000), + CLK_INIT(pixel_mdp_clk.c), + }, +}; + +static struct branch_clk pixel_lcdc_clk = { + .b = { + .ctl_reg = PIXEL_CC_REG, + .en_mask = BIT(8), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 21, + }, + .parent = &pixel_mdp_clk.c, + .c = { + .dbg_name = "pixel_lcdc_clk", + .ops = &clk_ops_branch, + CLK_INIT(pixel_lcdc_clk.c), + }, +}; + +#define F_ROT(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC_BANKED(29, 26, 25, 22, d, \ + 21, 19, 18, 16, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_rot[] = { + F_ROT( 0, gnd, 1), + F_ROT( 27000000, pxo, 1), + F_ROT( 29540000, pll8, 13), + F_ROT( 32000000, pll8, 12), + F_ROT( 38400000, pll8, 10), + F_ROT( 48000000, pll8, 8), + F_ROT( 54860000, pll8, 7), + F_ROT( 64000000, pll8, 6), + F_ROT( 76800000, pll8, 5), + F_ROT( 96000000, pll8, 4), + F_ROT(100000000, pll2, 8), + F_ROT(114290000, pll2, 7), + F_ROT(133330000, pll2, 6), + F_ROT(160000000, pll2, 5), + F_END +}; + +static struct bank_masks bdiv_info_rot = { + .bank_sel_mask = BIT(30), + .bank0_mask = { + .ns_mask = BM(25, 22) | BM(18, 16), + }, + .bank1_mask = { + .ns_mask = BM(29, 26) | BM(21, 19), + }, +}; + +static struct rcg_clk rot_clk = { + .b = { + .ctl_reg = ROT_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(2), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 15, + .retain_reg = ROT_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = ROT_NS_REG, + .root_en_mask = BIT(2), + .set_rate = set_rate_div_banked, + .freq_tbl = clk_tbl_rot, + .bank_info = &bdiv_info_rot, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "rot_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 80000000, NOMINAL, 160000000), + CLK_INIT(rot_clk.c), + .depends = &rot_axi_clk.c, + }, +}; + +#define F_TV(f, s, p_r, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 15, 14, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + .extra_freq_data = p_r, \ + } +/* Switching TV freqs requires PLL reconfiguration. */ +static struct pll_rate mm_pll2_rate[] = { + [0] = PLL_RATE( 7, 6301, 13500, 0, 4, 0x4248B), /* 50400500 Hz */ + [1] = PLL_RATE( 8, 0, 0, 0, 4, 0x4248B), /* 54000000 Hz */ + [2] = PLL_RATE(16, 2, 125, 0, 4, 0x5248F), /* 108108000 Hz */ + [3] = PLL_RATE(22, 0, 0, 2, 4, 0x6248B), /* 148500000 Hz */ + [4] = PLL_RATE(44, 0, 0, 2, 4, 0x6248F), /* 297000000 Hz */ +}; +static struct clk_freq_tbl clk_tbl_tv[] = { + F_TV( 0, gnd, &mm_pll2_rate[0], 1, 0, 0), + F_TV( 25200000, pll3, &mm_pll2_rate[0], 2, 0, 0), + F_TV( 27000000, pll3, &mm_pll2_rate[1], 2, 0, 0), + F_TV( 27030000, pll3, &mm_pll2_rate[2], 4, 0, 0), + F_TV( 74250000, pll3, &mm_pll2_rate[3], 2, 0, 0), + F_TV(148500000, pll3, &mm_pll2_rate[4], 2, 0, 0), + F_END +}; + +static struct rcg_clk tv_src_clk = { + .ns_reg = TV_NS_REG, + .b = { + .ctl_reg = TV_CC_REG, + .halt_check = NOCHECK, + .retain_reg = TV_CC_REG, + .retain_mask = BIT(31), + }, + .md_reg = TV_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(23, 16) | BM(15, 14) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_tv, + .freq_tbl = clk_tbl_tv, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "tv_src_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 27030000, NOMINAL, 149000000), + CLK_INIT(tv_src_clk.c), + }, +}; + +static struct branch_clk tv_enc_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(8), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(0), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 8, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "tv_enc_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_enc_clk.c), + }, +}; + +static struct branch_clk tv_dac_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(10), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 9, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "tv_dac_clk", + .ops = &clk_ops_branch, + CLK_INIT(tv_dac_clk.c), + }, +}; + +static struct branch_clk mdp_tv_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(4), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 11, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "mdp_tv_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdp_tv_clk.c), + }, +}; + +static struct branch_clk hdmi_tv_clk = { + .b = { + .ctl_reg = TV_CC_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(1), + .halt_reg = DBG_BUS_VEC_D_REG, + .halt_bit = 10, + }, + .parent = &tv_src_clk.c, + .c = { + .dbg_name = "hdmi_tv_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_tv_clk.c), + }, +}; + +static struct branch_clk hdmi_app_clk = { + .b = { + .ctl_reg = MISC_CC2_REG, + .en_mask = BIT(11), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(11), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 25, + }, + .c = { + .dbg_name = "hdmi_app_clk", + .ops = &clk_ops_branch, + CLK_INIT(hdmi_app_clk.c), + }, +}; + +#define F_VCODEC(f, s, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(18, 11, n, m, 0, 0, 1, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_vcodec[] = { + F_VCODEC( 0, gnd, 0, 0), + F_VCODEC( 27000000, pxo, 0, 0), + F_VCODEC( 32000000, pll8, 1, 12), + F_VCODEC( 48000000, pll8, 1, 8), + F_VCODEC( 54860000, pll8, 1, 7), + F_VCODEC( 96000000, pll8, 1, 4), + F_VCODEC(133330000, pll2, 1, 6), + F_VCODEC(200000000, pll2, 1, 4), + F_VCODEC(228570000, pll2, 2, 7), + F_END +}; + +static struct rcg_clk vcodec_clk = { + .b = { + .ctl_reg = VCODEC_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(6), + .halt_reg = DBG_BUS_VEC_C_REG, + .halt_bit = 29, + .retain_reg = VCODEC_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = VCODEC_NS_REG, + .md_reg = VCODEC_MD0_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(18, 11) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_vcodec, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vcodec_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 100000000, NOMINAL, 200000000, + HIGH, 228571000), + CLK_INIT(vcodec_clk.c), + .depends = &vcodec_axi_clk.c, + }, +}; + +#define F_VPE(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_DIVSRC(15, 12, d, 2, 0, s##_to_mm_mux), \ + } +static struct clk_freq_tbl clk_tbl_vpe[] = { + F_VPE( 0, gnd, 1), + F_VPE( 27000000, pxo, 1), + F_VPE( 34909000, pll8, 11), + F_VPE( 38400000, pll8, 10), + F_VPE( 64000000, pll8, 6), + F_VPE( 76800000, pll8, 5), + F_VPE( 96000000, pll8, 4), + F_VPE(100000000, pll2, 8), + F_VPE(160000000, pll2, 5), + F_VPE(200000000, pll2, 4), + F_END +}; + +static struct rcg_clk vpe_clk = { + .b = { + .ctl_reg = VPE_CC_REG, + .en_mask = BIT(0), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(17), + .halt_reg = DBG_BUS_VEC_A_REG, + .halt_bit = 28, + .retain_reg = VPE_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = VPE_NS_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(15, 12) | BM(2, 0)), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_vpe, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vpe_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 76800000, NOMINAL, 160000000, + HIGH, 200000000), + CLK_INIT(vpe_clk.c), + .depends = &vpe_axi_clk.c, + }, +}; + +#define F_VFE(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS_MM(23, 16, n, m, 11, 10, d, 2, 0, s##_to_mm_mux), \ + .ctl_val = CC(6, n), \ + } +static struct clk_freq_tbl clk_tbl_vfe[] = { + F_VFE( 0, gnd, 1, 0, 0), + F_VFE( 13960000, pll8, 1, 2, 55), + F_VFE( 27000000, pxo, 1, 0, 0), + F_VFE( 36570000, pll8, 1, 2, 21), + F_VFE( 38400000, pll8, 2, 1, 5), + F_VFE( 45180000, pll8, 1, 2, 17), + F_VFE( 48000000, pll8, 2, 1, 4), + F_VFE( 54860000, pll8, 1, 1, 7), + F_VFE( 64000000, pll8, 2, 1, 3), + F_VFE( 76800000, pll8, 1, 1, 5), + F_VFE( 96000000, pll8, 2, 1, 2), + F_VFE(109710000, pll8, 1, 2, 7), + F_VFE(128000000, pll8, 1, 1, 3), + F_VFE(153600000, pll8, 1, 2, 5), + F_VFE(200000000, pll2, 2, 1, 2), + F_VFE(228570000, pll2, 1, 2, 7), + F_VFE(266667000, pll2, 1, 1, 3), + F_END +}; + +static struct rcg_clk vfe_clk = { + .b = { + .ctl_reg = VFE_CC_REG, + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(15), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 6, + .en_mask = BIT(0), + .retain_reg = VFE_CC_REG, + .retain_mask = BIT(31), + }, + .ns_reg = VFE_NS_REG, + .md_reg = VFE_MD_REG, + .root_en_mask = BIT(2), + .ns_mask = (BM(23, 16) | BM(11, 10) | BM(2, 0)), + .mnd_en_mask = BIT(5), + .ctl_mask = BM(7, 6), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_vfe, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "vfe_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 110000000, NOMINAL, 228570000, + HIGH, 266667000), + CLK_INIT(vfe_clk.c), + .depends = &vfe_axi_clk.c, + }, +}; + +static struct branch_clk csi0_vfe_clk = { + .b = { + .ctl_reg = VFE_CC_REG, + .en_mask = BIT(12), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(24), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 7, + }, + .parent = &vfe_clk.c, + .c = { + .dbg_name = "csi0_vfe_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi0_vfe_clk.c), + }, +}; + +static struct branch_clk csi1_vfe_clk = { + .b = { + .ctl_reg = VFE_CC_REG, + .en_mask = BIT(10), + .reset_reg = SW_RESET_CORE_REG, + .reset_mask = BIT(23), + .halt_reg = DBG_BUS_VEC_B_REG, + .halt_bit = 8, + }, + .parent = &vfe_clk.c, + .c = { + .dbg_name = "csi1_vfe_clk", + .ops = &clk_ops_branch, + CLK_INIT(csi1_vfe_clk.c), + }, +}; + +/* + * Low Power Audio Clocks + */ +#define F_AIF_OSR(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS(31, 24, n, m, 5, 4, 3, d, 2, 0, s##_to_lpa_mux), \ + } +static struct clk_freq_tbl clk_tbl_aif_osr[] = { + F_AIF_OSR( 0, gnd, 1, 0, 0), + F_AIF_OSR( 768000, pll4, 4, 1, 176), + F_AIF_OSR( 1024000, pll4, 4, 1, 132), + F_AIF_OSR( 1536000, pll4, 4, 1, 88), + F_AIF_OSR( 2048000, pll4, 4, 1, 66), + F_AIF_OSR( 3072000, pll4, 4, 1, 44), + F_AIF_OSR( 4096000, pll4, 4, 1, 33), + F_AIF_OSR( 6144000, pll4, 4, 1, 22), + F_AIF_OSR( 8192000, pll4, 2, 1, 33), + F_AIF_OSR(12288000, pll4, 4, 1, 11), + F_AIF_OSR(24576000, pll4, 2, 1, 11), + F_END +}; + +#define CLK_AIF_OSR(i, ns, md, h_r) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(17), \ + .reset_reg = ns, \ + .reset_mask = BIT(19), \ + .halt_reg = h_r, \ + .halt_check = ENABLE, \ + .halt_bit = 1, \ + }, \ + .ns_reg = ns, \ + .md_reg = md, \ + .root_en_mask = BIT(9), \ + .ns_mask = (BM(31, 24) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_aif_osr, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(LOW, 24576000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +#define CLK_AIF_BIT(i, ns, h_r) \ + struct cdiv_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(15), \ + .halt_reg = h_r, \ + .halt_check = DELAY, \ + }, \ + .ns_reg = ns, \ + .ext_mask = BIT(14), \ + .div_offset = 10, \ + .max_div = 16, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_cdiv, \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +static CLK_AIF_OSR(mi2s_osr, LCC_MI2S_NS_REG, LCC_MI2S_MD_REG, + LCC_MI2S_STATUS_REG); +static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG); + +static CLK_AIF_OSR(codec_i2s_mic_osr, LCC_CODEC_I2S_MIC_NS_REG, + LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG); +static CLK_AIF_BIT(codec_i2s_mic_bit, LCC_CODEC_I2S_MIC_NS_REG, + LCC_CODEC_I2S_MIC_STATUS_REG); + +static CLK_AIF_OSR(spare_i2s_mic_osr, LCC_SPARE_I2S_MIC_NS_REG, + LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG); +static CLK_AIF_BIT(spare_i2s_mic_bit, LCC_SPARE_I2S_MIC_NS_REG, + LCC_SPARE_I2S_MIC_STATUS_REG); + +static CLK_AIF_OSR(codec_i2s_spkr_osr, LCC_CODEC_I2S_SPKR_NS_REG, + LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG); +static CLK_AIF_BIT(codec_i2s_spkr_bit, LCC_CODEC_I2S_SPKR_NS_REG, + LCC_CODEC_I2S_SPKR_STATUS_REG); + +static CLK_AIF_OSR(spare_i2s_spkr_osr, LCC_SPARE_I2S_SPKR_NS_REG, + LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG); +static CLK_AIF_BIT(spare_i2s_spkr_bit, LCC_SPARE_I2S_SPKR_NS_REG, + LCC_SPARE_I2S_SPKR_STATUS_REG); + +#define F_PCM(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_lpa_mux), \ + } +static struct clk_freq_tbl clk_tbl_pcm[] = { + { .ns_val = BIT(10) /* external input */ }, + F_PCM( 512000, pll4, 4, 1, 264), + F_PCM( 768000, pll4, 4, 1, 176), + F_PCM( 1024000, pll4, 4, 1, 132), + F_PCM( 1536000, pll4, 4, 1, 88), + F_PCM( 2048000, pll4, 4, 1, 66), + F_PCM( 3072000, pll4, 4, 1, 44), + F_PCM( 4096000, pll4, 4, 1, 33), + F_PCM( 6144000, pll4, 4, 1, 22), + F_PCM( 8192000, pll4, 2, 1, 33), + F_PCM(12288000, pll4, 4, 1, 11), + F_PCM(24580000, pll4, 2, 1, 11), + F_END +}; + +static struct rcg_clk pcm_clk = { + .b = { + .ctl_reg = LCC_PCM_NS_REG, + .en_mask = BIT(11), + .reset_reg = LCC_PCM_NS_REG, + .reset_mask = BIT(13), + .halt_reg = LCC_PCM_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 0, + }, + .ns_reg = LCC_PCM_NS_REG, + .md_reg = LCC_PCM_MD_REG, + .root_en_mask = BIT(9), + .ns_mask = BM(31, 16) | BIT(10) | BM(6, 0), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_pcm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pcm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 24580000), + CLK_INIT(pcm_clk.c), + }, +}; + +DEFINE_CLK_RPM(afab_clk, afab_a_clk, APPS_FABRIC, NULL); +DEFINE_CLK_RPM(cfpb_clk, cfpb_a_clk, CFPB, NULL); +DEFINE_CLK_RPM(dfab_clk, dfab_a_clk, DAYTONA_FABRIC, NULL); +DEFINE_CLK_RPM(ebi1_clk, ebi1_a_clk, EBI1, NULL); +DEFINE_CLK_RPM(mmfab_clk, mmfab_a_clk, MM_FABRIC, NULL); +DEFINE_CLK_RPM(mmfpb_clk, mmfpb_a_clk, MMFPB, NULL); +DEFINE_CLK_RPM(sfab_clk, sfab_a_clk, SYSTEM_FABRIC, NULL); +DEFINE_CLK_RPM(sfpb_clk, sfpb_a_clk, SFPB, NULL); +DEFINE_CLK_RPM(smi_clk, smi_a_clk, SMI, &smi_2x_axi_clk.c); + +static DEFINE_CLK_VOTER(dfab_dsps_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_usb_hs_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc1_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc2_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc3_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc4_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc5_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_scm_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_qseecom_clk, &dfab_clk.c, 0); + +static DEFINE_CLK_VOTER(ebi1_msmbus_clk, &ebi1_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_adm0_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi1_adm1_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi1_acpu_a_clk, &ebi1_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_msmbus_a_clk, &ebi1_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(afab_acpu_a_clk, &afab_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(afab_msmbus_a_clk, &afab_a_clk.c, LONG_MAX); + +static DEFINE_CLK_MEASURE(sc0_m_clk); +static DEFINE_CLK_MEASURE(sc1_m_clk); +static DEFINE_CLK_MEASURE(l2_m_clk); + +#ifdef CONFIG_DEBUG_FS +struct measure_sel { + u32 test_vector; + struct clk *clk; +}; + +static struct measure_sel measure_mux[] = { + { TEST_PER_LS(0x08), &modem_ahb1_p_clk.c }, + { TEST_PER_LS(0x09), &modem_ahb2_p_clk.c }, + { TEST_PER_LS(0x12), &sdc1_p_clk.c }, + { TEST_PER_LS(0x13), &sdc1_clk.c }, + { TEST_PER_LS(0x14), &sdc2_p_clk.c }, + { TEST_PER_LS(0x15), &sdc2_clk.c }, + { TEST_PER_LS(0x16), &sdc3_p_clk.c }, + { TEST_PER_LS(0x17), &sdc3_clk.c }, + { TEST_PER_LS(0x18), &sdc4_p_clk.c }, + { TEST_PER_LS(0x19), &sdc4_clk.c }, + { TEST_PER_LS(0x1A), &sdc5_p_clk.c }, + { TEST_PER_LS(0x1B), &sdc5_clk.c }, + { TEST_PER_LS(0x1D), &ebi2_2x_clk.c }, + { TEST_PER_LS(0x1E), &ebi2_clk.c }, + { TEST_PER_LS(0x1F), &gp0_clk.c }, + { TEST_PER_LS(0x20), &gp1_clk.c }, + { TEST_PER_LS(0x21), &gp2_clk.c }, + { TEST_PER_LS(0x25), &dfab_clk.c }, + { TEST_PER_LS(0x25), &dfab_a_clk.c }, + { TEST_PER_LS(0x26), &pmem_clk.c }, + { TEST_PER_LS(0x2B), &ppss_p_clk.c }, + { TEST_PER_LS(0x33), &cfpb_clk.c }, + { TEST_PER_LS(0x33), &cfpb_a_clk.c }, + { TEST_PER_LS(0x3D), &gsbi1_p_clk.c }, + { TEST_PER_LS(0x3E), &gsbi1_uart_clk.c }, + { TEST_PER_LS(0x3F), &gsbi1_qup_clk.c }, + { TEST_PER_LS(0x41), &gsbi2_p_clk.c }, + { TEST_PER_LS(0x42), &gsbi2_uart_clk.c }, + { TEST_PER_LS(0x44), &gsbi2_qup_clk.c }, + { TEST_PER_LS(0x45), &gsbi3_p_clk.c }, + { TEST_PER_LS(0x46), &gsbi3_uart_clk.c }, + { TEST_PER_LS(0x48), &gsbi3_qup_clk.c }, + { TEST_PER_LS(0x49), &gsbi4_p_clk.c }, + { TEST_PER_LS(0x4A), &gsbi4_uart_clk.c }, + { TEST_PER_LS(0x4C), &gsbi4_qup_clk.c }, + { TEST_PER_LS(0x4D), &gsbi5_p_clk.c }, + { TEST_PER_LS(0x4E), &gsbi5_uart_clk.c }, + { TEST_PER_LS(0x50), &gsbi5_qup_clk.c }, + { TEST_PER_LS(0x51), &gsbi6_p_clk.c }, + { TEST_PER_LS(0x52), &gsbi6_uart_clk.c }, + { TEST_PER_LS(0x54), &gsbi6_qup_clk.c }, + { TEST_PER_LS(0x55), &gsbi7_p_clk.c }, + { TEST_PER_LS(0x56), &gsbi7_uart_clk.c }, + { TEST_PER_LS(0x58), &gsbi7_qup_clk.c }, + { TEST_PER_LS(0x59), &gsbi8_p_clk.c }, + { TEST_PER_LS(0x5A), &gsbi8_uart_clk.c }, + { TEST_PER_LS(0x5C), &gsbi8_qup_clk.c }, + { TEST_PER_LS(0x5D), &gsbi9_p_clk.c }, + { TEST_PER_LS(0x5E), &gsbi9_uart_clk.c }, + { TEST_PER_LS(0x60), &gsbi9_qup_clk.c }, + { TEST_PER_LS(0x61), &gsbi10_p_clk.c }, + { TEST_PER_LS(0x62), &gsbi10_uart_clk.c }, + { TEST_PER_LS(0x64), &gsbi10_qup_clk.c }, + { TEST_PER_LS(0x65), &gsbi11_p_clk.c }, + { TEST_PER_LS(0x66), &gsbi11_uart_clk.c }, + { TEST_PER_LS(0x68), &gsbi11_qup_clk.c }, + { TEST_PER_LS(0x69), &gsbi12_p_clk.c }, + { TEST_PER_LS(0x6A), &gsbi12_uart_clk.c }, + { TEST_PER_LS(0x6C), &gsbi12_qup_clk.c }, + { TEST_PER_LS(0x78), &sfpb_clk.c }, + { TEST_PER_LS(0x78), &sfpb_a_clk.c }, + { TEST_PER_LS(0x7A), &pmic_ssbi2_clk.c }, + { TEST_PER_LS(0x7B), &pmic_arb0_p_clk.c }, + { TEST_PER_LS(0x7C), &pmic_arb1_p_clk.c }, + { TEST_PER_LS(0x7D), &prng_clk.c }, + { TEST_PER_LS(0x7F), &rpm_msg_ram_p_clk.c }, + { TEST_PER_LS(0x80), &adm0_p_clk.c }, + { TEST_PER_LS(0x81), &adm1_p_clk.c }, + { TEST_PER_LS(0x84), &usb_hs1_p_clk.c }, + { TEST_PER_LS(0x85), &usb_hs1_xcvr_clk.c }, + { TEST_PER_LS(0x89), &usb_fs1_p_clk.c }, + { TEST_PER_LS(0x8A), &usb_fs1_sys_clk.c }, + { TEST_PER_LS(0x8B), &usb_fs1_xcvr_clk.c }, + { TEST_PER_LS(0x8C), &usb_fs2_p_clk.c }, + { TEST_PER_LS(0x8D), &usb_fs2_sys_clk.c }, + { TEST_PER_LS(0x8E), &usb_fs2_xcvr_clk.c }, + { TEST_PER_LS(0x8F), &tsif_p_clk.c }, + { TEST_PER_LS(0x91), &tsif_ref_clk.c }, + { TEST_PER_LS(0x93), &ce2_p_clk.c }, + { TEST_PER_LS(0x94), &tssc_clk.c }, + + { TEST_PER_HS(0x07), &afab_clk.c }, + { TEST_PER_HS(0x07), &afab_a_clk.c }, + { TEST_PER_HS(0x18), &sfab_clk.c }, + { TEST_PER_HS(0x18), &sfab_a_clk.c }, + { TEST_PER_HS(0x2A), &adm0_clk.c }, + { TEST_PER_HS(0x2B), &adm1_clk.c }, + { TEST_PER_HS(0x34), &ebi1_clk.c }, + { TEST_PER_HS(0x34), &ebi1_a_clk.c }, + + { TEST_MM_LS(0x00), &dsi_byte_clk.c }, + { TEST_MM_LS(0x01), &pixel_lcdc_clk.c }, + { TEST_MM_LS(0x04), &pixel_mdp_clk.c }, + { TEST_MM_LS(0x06), &_p_clk.c }, + { TEST_MM_LS(0x07), &csi0_p_clk.c }, + { TEST_MM_LS(0x08), &csi1_p_clk.c }, + { TEST_MM_LS(0x09), &dsi_m_p_clk.c }, + { TEST_MM_LS(0x0A), &dsi_s_p_clk.c }, + { TEST_MM_LS(0x0C), &gfx2d0_p_clk.c }, + { TEST_MM_LS(0x0D), &gfx2d1_p_clk.c }, + { TEST_MM_LS(0x0E), &gfx3d_p_clk.c }, + { TEST_MM_LS(0x0F), &hdmi_m_p_clk.c }, + { TEST_MM_LS(0x10), &hdmi_s_p_clk.c }, + { TEST_MM_LS(0x11), &ijpeg_p_clk.c }, + { TEST_MM_LS(0x12), &imem_p_clk.c }, + { TEST_MM_LS(0x13), &jpegd_p_clk.c }, + { TEST_MM_LS(0x14), &mdp_p_clk.c }, + { TEST_MM_LS(0x16), &rot_p_clk.c }, + { TEST_MM_LS(0x18), &smmu_p_clk.c }, + { TEST_MM_LS(0x19), &tv_enc_p_clk.c }, + { TEST_MM_LS(0x1A), &vcodec_p_clk.c }, + { TEST_MM_LS(0x1B), &vfe_p_clk.c }, + { TEST_MM_LS(0x1C), &vpe_p_clk.c }, + { TEST_MM_LS(0x1D), &cam_clk.c }, + { TEST_MM_LS(0x1F), &hdmi_app_clk.c }, + { TEST_MM_LS(0x20), &mdp_vsync_clk.c }, + { TEST_MM_LS(0x21), &tv_dac_clk.c }, + { TEST_MM_LS(0x22), &tv_enc_clk.c }, + { TEST_MM_LS(0x23), &dsi_esc_clk.c }, + { TEST_MM_LS(0x25), &mmfpb_clk.c }, + { TEST_MM_LS(0x25), &mmfpb_a_clk.c }, + + { TEST_MM_HS(0x00), &csi0_clk.c }, + { TEST_MM_HS(0x01), &csi1_clk.c }, + { TEST_MM_HS(0x03), &csi0_vfe_clk.c }, + { TEST_MM_HS(0x04), &csi1_vfe_clk.c }, + { TEST_MM_HS(0x05), &ijpeg_clk.c }, + { TEST_MM_HS(0x06), &vfe_clk.c }, + { TEST_MM_HS(0x07), &gfx2d0_clk.c }, + { TEST_MM_HS(0x08), &gfx2d1_clk.c }, + { TEST_MM_HS(0x09), &gfx3d_clk.c }, + { TEST_MM_HS(0x0A), &jpegd_clk.c }, + { TEST_MM_HS(0x0B), &vcodec_clk.c }, + { TEST_MM_HS(0x0F), &mmfab_clk.c }, + { TEST_MM_HS(0x0F), &mmfab_a_clk.c }, + { TEST_MM_HS(0x11), &gmem_axi_clk.c }, + { TEST_MM_HS(0x12), &ijpeg_axi_clk.c }, + { TEST_MM_HS(0x13), &imem_axi_clk.c }, + { TEST_MM_HS(0x14), &jpegd_axi_clk.c }, + { TEST_MM_HS(0x15), &mdp_axi_clk.c }, + { TEST_MM_HS(0x16), &rot_axi_clk.c }, + { TEST_MM_HS(0x17), &vcodec_axi_clk.c }, + { TEST_MM_HS(0x18), &vfe_axi_clk.c }, + { TEST_MM_HS(0x19), &vpe_axi_clk.c }, + { TEST_MM_HS(0x1A), &mdp_clk.c }, + { TEST_MM_HS(0x1B), &rot_clk.c }, + { TEST_MM_HS(0x1C), &vpe_clk.c }, + { TEST_MM_HS(0x1E), &hdmi_tv_clk.c }, + { TEST_MM_HS(0x1F), &mdp_tv_clk.c }, + { TEST_MM_HS(0x24), &smi_2x_axi_clk.c }, + + { TEST_MM_HS2X(0x24), &smi_clk.c }, + { TEST_MM_HS2X(0x24), &smi_a_clk.c }, + + { TEST_LPA(0x0A), &mi2s_osr_clk.c }, + { TEST_LPA(0x0B), &mi2s_bit_clk.c }, + { TEST_LPA(0x0C), &codec_i2s_mic_osr_clk.c }, + { TEST_LPA(0x0D), &codec_i2s_mic_bit_clk.c }, + { TEST_LPA(0x0E), &codec_i2s_spkr_osr_clk.c }, + { TEST_LPA(0x0F), &codec_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x10), &spare_i2s_mic_osr_clk.c }, + { TEST_LPA(0x11), &spare_i2s_mic_bit_clk.c }, + { TEST_LPA(0x12), &spare_i2s_spkr_osr_clk.c }, + { TEST_LPA(0x13), &spare_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x14), &pcm_clk.c }, + + { TEST_SC(0x40), &sc0_m_clk }, + { TEST_SC(0x41), &sc1_m_clk }, + { TEST_SC(0x42), &l2_m_clk }, +}; + +static struct measure_sel *find_measure_sel(struct clk *clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(measure_mux); i++) + if (measure_mux[i].clk == clk) + return &measure_mux[i]; + return NULL; +} + +static int measure_clk_set_parent(struct clk *c, struct clk *parent) +{ + int ret = 0; + u32 clk_sel; + struct measure_sel *p; + struct measure_clk *clk = to_measure_clk(c); + unsigned long flags; + + if (!parent) + return -EINVAL; + + p = find_measure_sel(parent); + if (!p) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* + * Program the test vector, measurement period (sample_ticks) + * and scaling factors (multiplier, divider). + */ + clk_sel = p->test_vector & TEST_CLK_SEL_MASK; + clk->sample_ticks = 0x10000; + clk->multiplier = 1; + clk->divider = 1; + switch (p->test_vector >> TEST_TYPE_SHIFT) { + case TEST_TYPE_PER_LS: + writel_relaxed(0x4030D00|BVAL(7, 0, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_PER_HS: + writel_relaxed(0x4020000|BVAL(16, 10, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_MM_LS: + writel_relaxed(0x4030D97, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), DBG_CFG_REG_LS_REG); + break; + case TEST_TYPE_MM_HS2X: + clk->divider = 2; + case TEST_TYPE_MM_HS: + writel_relaxed(0x402B800, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), DBG_CFG_REG_HS_REG); + break; + case TEST_TYPE_LPA: + writel_relaxed(0x4030D98, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), + LCC_CLK_LS_DEBUG_CFG_REG); + break; + case TEST_TYPE_SC: + writel_relaxed(0x5020000|BVAL(16, 10, clk_sel), CLK_TEST_REG); + clk->sample_ticks = 0x4000; + clk->multiplier = 2; + break; + default: + ret = -EPERM; + } + /* Make sure test vector is set before starting measurements. */ + mb(); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} + +/* Sample clock for 'ticks' reference clock ticks. */ +static u32 run_measurement(unsigned ticks) +{ + /* Stop counters and set the XO4 counter start value. */ + writel_relaxed(ticks, RINGOSC_TCXO_CTL_REG); + + /* Wait for timer to become ready. */ + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) != 0) + cpu_relax(); + + /* Run measurement and wait for completion. */ + writel_relaxed(BIT(20)|ticks, RINGOSC_TCXO_CTL_REG); + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) == 0) + cpu_relax(); + + /* Stop counters. */ + writel_relaxed(0x0, RINGOSC_TCXO_CTL_REG); + + /* Return measured ticks. */ + return readl_relaxed(RINGOSC_STATUS_REG) & BM(24, 0); +} + +/* Perform a hardware rate measurement for a given clock. + FOR DEBUG USE ONLY: Measurements take ~15 ms! */ +static unsigned long measure_clk_get_rate(struct clk *c) +{ + unsigned long flags; + u32 pdm_reg_backup, ringosc_reg_backup; + u64 raw_count_short, raw_count_full; + struct measure_clk *clk = to_measure_clk(c); + unsigned ret; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable CXO/4 and RINGOSC branch and root. */ + pdm_reg_backup = readl_relaxed(PDM_CLK_NS_REG); + ringosc_reg_backup = readl_relaxed(RINGOSC_NS_REG); + writel_relaxed(0x2898, PDM_CLK_NS_REG); + writel_relaxed(0xA00, RINGOSC_NS_REG); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(clk->sample_ticks); + + writel_relaxed(ringosc_reg_backup, RINGOSC_NS_REG); + writel_relaxed(pdm_reg_backup, PDM_CLK_NS_REG); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, + (((clk->sample_ticks * 10) + 35) * clk->divider)); + ret = (raw_count_full * clk->multiplier); + } + + /* Route dbg_hs_clk to PLLTEST. 300mV single-ended amplitude. */ + writel_relaxed(0x3CF8, PLLTEST_PAD_CFG_REG); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned long measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops clk_ops_measure = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, +}; + +static struct measure_clk measure_clk = { + .c = { + .dbg_name = "measure_clk", + .ops = &clk_ops_measure, + CLK_INIT(measure_clk.c), + }, + .multiplier = 1, + .divider = 1, +}; + +static struct clk_lookup msm_clocks_8x60[] = { + CLK_LOOKUP("xo", cxo_clk.c, ""), + CLK_LOOKUP("xo", cxo_a_clk.c, ""), + CLK_LOOKUP("xo", pxo_a_clk.c, ""), + CLK_LOOKUP("xo", pxo_clk.c, "pil_modem"), + CLK_LOOKUP("pll4", pll4_clk.c, "pil_qdsp6v3"), + CLK_LOOKUP("measure", measure_clk.c, "debug"), + + CLK_LOOKUP("bus_clk", afab_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_a_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", mmfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_a_clk.c, ""), + CLK_LOOKUP("mem_clk", smi_clk.c, ""), + CLK_LOOKUP("mem_clk", smi_a_clk.c, ""), + + CLK_LOOKUP("bus_clk", afab_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_a_clk", afab_msmbus_a_clk.c, "msm_apps_fab"), + CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_a_clk", sfab_a_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_clk", sfpb_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, "msm_sys_fpb"), + CLK_LOOKUP("bus_clk", mmfab_clk.c, "msm_mm_fab"), + CLK_LOOKUP("bus_a_clk", mmfab_a_clk.c, "msm_mm_fab"), + CLK_LOOKUP("bus_clk", cfpb_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, "msm_cpss_fpb"), + CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("mem_a_clk", ebi1_msmbus_a_clk.c, "msm_bus"), + CLK_LOOKUP("smi_clk", smi_clk.c, "msm_bus"), + CLK_LOOKUP("smi_a_clk", smi_a_clk.c, "msm_bus"), + + CLK_LOOKUP("core_clk", gp0_clk.c, ""), + CLK_LOOKUP("core_clk", gp1_clk.c, ""), + CLK_LOOKUP("core_clk", gp2_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi1_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi2_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_uart_clk.c, "msm_serial_hsl.2"), + CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi5_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi6_uart_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", gsbi7_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi8_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi9_uart_clk.c, "msm_serial_hsl.1"), + CLK_LOOKUP("core_clk", gsbi10_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi11_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi12_uart_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", gsbi1_qup_clk.c, "spi_qsd.0"), + CLK_LOOKUP("core_clk", gsbi2_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi3_qup_clk.c, "qup_i2c.0"), + CLK_LOOKUP("core_clk", gsbi4_qup_clk.c, "qup_i2c.1"), + CLK_LOOKUP("core_clk", gsbi5_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi6_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi7_qup_clk.c, "qup_i2c.4"), + CLK_LOOKUP("core_clk", gsbi8_qup_clk.c, "qup_i2c.3"), + CLK_LOOKUP("core_clk", gsbi9_qup_clk.c, "qup_i2c.2"), + CLK_LOOKUP("core_clk", gsbi10_qup_clk.c, "spi_qsd.1"), + CLK_LOOKUP("core_clk", gsbi11_qup_clk.c, ""), + CLK_LOOKUP("gsbi_qup_clk", gsbi12_qup_clk.c, "msm_dsps"), + CLK_LOOKUP("core_clk", gsbi12_qup_clk.c, "qup_i2c.5"), + CLK_LOOKUP("core_clk", pdm_clk.c, ""), + CLK_LOOKUP("mem_clk", pmem_clk.c, "msm_dsps"), + CLK_LOOKUP("core_clk", prng_clk.c, "msm_rng.0"), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", sdc5_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tsif.0"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tsif.1"), + CLK_LOOKUP("core_clk", tssc_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_hs1_xcvr_clk.c, "msm_otg"), + CLK_LOOKUP("phy_clk", usb_phy0_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_fs1_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs1_sys_clk.c, ""), + CLK_LOOKUP("src_clk", usb_fs1_src_clk.c, ""), + CLK_LOOKUP("alt_core_clk", usb_fs2_xcvr_clk.c, ""), + CLK_LOOKUP("sys_clk", usb_fs2_sys_clk.c, ""), + CLK_LOOKUP("src_clk", usb_fs2_src_clk.c, ""), + CLK_LOOKUP("core_clk", ce2_p_clk.c, "qce.0"), + CLK_LOOKUP("core_clk", ce2_p_clk.c, "qcrypto.0"), + CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "spi_qsd.0"), + CLK_LOOKUP("iface_clk", gsbi2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "msm_serial_hsl.2"), + CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "qup_i2c.0"), + CLK_LOOKUP("iface_clk", gsbi4_p_clk.c, "qup_i2c.1"), + CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi6_p_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("iface_clk", gsbi7_p_clk.c, "qup_i2c.4"), + CLK_LOOKUP("iface_clk", gsbi8_p_clk.c, "qup_i2c.3"), + CLK_LOOKUP("iface_clk", gsbi9_p_clk.c, "msm_serial_hsl.1"), + CLK_LOOKUP("iface_clk", gsbi9_p_clk.c, "qup_i2c.2"), + CLK_LOOKUP("iface_clk", gsbi10_p_clk.c, "spi_qsd.1"), + CLK_LOOKUP("iface_clk", gsbi11_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi12_p_clk.c, ""), + CLK_LOOKUP("iface_clk", gsbi12_p_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("iface_clk", gsbi12_p_clk.c, "qup_i2c.5"), + CLK_LOOKUP("iface_clk", ppss_p_clk.c, "msm_dsps"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, "msm_tsif.0"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, "msm_tsif.1"), + CLK_LOOKUP("iface_clk", usb_fs1_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_fs2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_hs1_p_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc5_p_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("mem_clk", ebi2_2x_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi2_clk.c, "msm_ebi2"), + CLK_LOOKUP("core_clk", adm0_clk.c, "msm_dmov.0"), + CLK_LOOKUP("iface_clk", adm0_p_clk.c, "msm_dmov.0"), + CLK_LOOKUP("core_clk", adm1_clk.c, "msm_dmov.1"), + CLK_LOOKUP("iface_clk", adm1_p_clk.c, "msm_dmov.1"), + CLK_LOOKUP("iface_clk", modem_ahb1_p_clk.c, ""), + CLK_LOOKUP("iface_clk", modem_ahb2_p_clk.c, ""), + CLK_LOOKUP("iface_clk", pmic_arb0_p_clk.c, ""), + CLK_LOOKUP("iface_clk", pmic_arb1_p_clk.c, ""), + CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""), + CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""), + CLK_LOOKUP("cam_clk", cam_clk.c, NULL), + CLK_LOOKUP("csi_clk", csi0_clk.c, NULL), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_camera_ov7692.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_camera_ov9726.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_csic.1"), + CLK_LOOKUP("csi_src_clk", csi_src_clk.c, NULL), + CLK_LOOKUP("byte_clk", dsi_byte_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("esc_clk", dsi_esc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("core_clk", gfx2d0_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("core_clk", gfx2d0_clk.c, "footswitch-8x60.0"), + CLK_LOOKUP("core_clk", gfx2d1_clk.c, "kgsl-2d1.1"), + CLK_LOOKUP("core_clk", gfx2d1_clk.c, "footswitch-8x60.1"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "msm_gemini.0"), + CLK_LOOKUP("core_clk", ijpeg_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("core_clk", jpegd_clk.c, NULL), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("lcdc_clk", pixel_lcdc_clk.c, "lcdc.0"), + CLK_LOOKUP("pixel_lcdc_clk", pixel_lcdc_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("mdp_clk", pixel_mdp_clk.c, "lcdc.0"), + CLK_LOOKUP("pixel_mdp_clk", pixel_mdp_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("core_clk", rot_clk.c, "msm_rotator.0"), + CLK_LOOKUP("core_clk", rot_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("tv_enc_clk", tv_enc_clk.c, NULL), + CLK_LOOKUP("tv_dac_clk", tv_dac_clk.c, NULL), + CLK_LOOKUP("core_clk", vcodec_clk.c, "msm_vidc.0"), + CLK_LOOKUP("core_clk", vcodec_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("mdp_clk", mdp_tv_clk.c, "dtv.0"), + CLK_LOOKUP("tv_clk", mdp_tv_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("hdmi_clk", hdmi_tv_clk.c, "dtv.0"), + CLK_LOOKUP("src_clk", tv_src_clk.c, "dtv.0"), + CLK_LOOKUP("tv_src_clk", tv_src_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("core_clk", hdmi_app_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("vpe_clk", vpe_clk.c, NULL), + CLK_LOOKUP("core_clk", vpe_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("csi_vfe_clk", csi0_vfe_clk.c, NULL), + CLK_LOOKUP("csi_vfe_clk", csi1_vfe_clk.c, "msm_camera_ov7692.0"), + CLK_LOOKUP("csi_vfe_clk", csi1_vfe_clk.c, "msm_camera_ov9726.0"), + CLK_LOOKUP("csi_vfe_clk", csi1_vfe_clk.c, "msm_csic.1"), + CLK_LOOKUP("vfe_clk", vfe_clk.c, NULL), + CLK_LOOKUP("core_clk", vfe_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("bus_clk", vfe_axi_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("bus_clk", ijpeg_axi_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("mem_clk", imem_axi_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("bus_clk", mdp_axi_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("bus_clk", rot_axi_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("bus_clk", vcodec_axi_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("bus_clk", vpe_axi_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("arb_clk", amp_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("csi_pclk", csi0_p_clk.c, NULL), + CLK_LOOKUP("csi_pclk", csi1_p_clk.c, "msm_camera_ov7692.0"), + CLK_LOOKUP("csi_pclk", csi1_p_clk.c, "msm_camera_ov9726.0"), + CLK_LOOKUP("csi_pclk", csi1_p_clk.c, "msm_csic.1"), + CLK_LOOKUP("master_iface_clk", dsi_m_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("slave_iface_clk", dsi_s_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("iface_clk", gfx2d0_p_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("iface_clk", gfx2d0_p_clk.c, "footswitch-8x60.0"), + CLK_LOOKUP("iface_clk", gfx2d1_p_clk.c, "kgsl-2d1.1"), + CLK_LOOKUP("iface_clk", gfx2d1_p_clk.c, "footswitch-8x60.1"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", gfx3d_p_clk.c, "footswitch-8x60.2"), + CLK_LOOKUP("master_iface_clk", hdmi_m_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("slave_iface_clk", hdmi_s_p_clk.c, "hdmi_msm.1"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "msm_gemini.0"), + CLK_LOOKUP("iface_clk", ijpeg_p_clk.c, "footswitch-8x60.3"), + CLK_LOOKUP("iface_clk", jpegd_p_clk.c, NULL), + CLK_LOOKUP("mem_iface_clk", imem_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "mdp.0"), + CLK_LOOKUP("iface_clk", mdp_p_clk.c, "footswitch-8x60.4"), + CLK_LOOKUP("iface_clk", smmu_p_clk.c, "msm_iommu"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "msm_rotator.0"), + CLK_LOOKUP("iface_clk", rot_p_clk.c, "footswitch-8x60.6"), + CLK_LOOKUP("tv_enc_pclk", tv_enc_p_clk.c, NULL), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "msm_vidc.0"), + CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "footswitch-8x60.7"), + CLK_LOOKUP("vfe_pclk", vfe_p_clk.c, NULL), + CLK_LOOKUP("iface_clk", vfe_p_clk.c, "footswitch-8x60.8"), + CLK_LOOKUP("vpe_pclk", vpe_p_clk.c, NULL), + CLK_LOOKUP("iface_clk", vpe_p_clk.c, "footswitch-8x60.9"), + CLK_LOOKUP("mi2s_osr_clk", mi2s_osr_clk.c, NULL), + CLK_LOOKUP("mi2s_bit_clk", mi2s_bit_clk.c, NULL), + CLK_LOOKUP("i2s_mic_osr_clk", codec_i2s_mic_osr_clk.c, NULL), + CLK_LOOKUP("i2s_mic_bit_clk", codec_i2s_mic_bit_clk.c, NULL), + CLK_LOOKUP("i2s_mic_osr_clk", spare_i2s_mic_osr_clk.c, NULL), + CLK_LOOKUP("i2s_mic_bit_clk", spare_i2s_mic_bit_clk.c, NULL), + CLK_LOOKUP("i2s_spkr_osr_clk", codec_i2s_spkr_osr_clk.c, NULL), + CLK_LOOKUP("i2s_spkr_bit_clk", codec_i2s_spkr_bit_clk.c, NULL), + CLK_LOOKUP("i2s_spkr_osr_clk", spare_i2s_spkr_osr_clk.c, NULL), + CLK_LOOKUP("i2s_spkr_bit_clk", spare_i2s_spkr_bit_clk.c, NULL), + CLK_LOOKUP("pcm_clk", pcm_clk.c, NULL), + CLK_LOOKUP("core_clk", jpegd_axi_clk.c, "msm_iommu.0"), + CLK_LOOKUP("core_clk", vpe_axi_clk.c, "msm_iommu.1"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.2"), + CLK_LOOKUP("core_clk", mdp_axi_clk.c, "msm_iommu.3"), + CLK_LOOKUP("core_clk", rot_axi_clk.c, "msm_iommu.4"), + CLK_LOOKUP("core_clk", ijpeg_axi_clk.c, "msm_iommu.5"), + CLK_LOOKUP("core_clk", vfe_axi_clk.c, "msm_iommu.6"), + CLK_LOOKUP("core_clk", vcodec_axi_clk.c, "msm_iommu.7"), + CLK_LOOKUP("core_clk", vcodec_axi_clk.c, "msm_iommu.8"), + CLK_LOOKUP("core_clk", gfx3d_clk.c, "msm_iommu.9"), + CLK_LOOKUP("core_clk", gfx2d0_clk.c, "msm_iommu.10"), + CLK_LOOKUP("core_clk", gfx2d1_clk.c, "msm_iommu.11"), + + CLK_LOOKUP("mdp_iommu_clk", mdp_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("rot_iommu_clk", rot_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu0_clk", vcodec_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("vcodec_iommu1_clk", vcodec_axi_clk.c, "msm_vidc.0"), + CLK_LOOKUP("smmu_iface_clk", smmu_p_clk.c, "msm_vidc.0"), + + CLK_LOOKUP("dfab_dsps_clk", dfab_dsps_clk.c, NULL), + CLK_LOOKUP("core_clk", dfab_usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("bus_clk", dfab_sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("bus_clk", dfab_sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("bus_clk", dfab_sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("bus_clk", dfab_sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("bus_clk", dfab_sdc5_clk.c, "msm_sdcc.5"), + CLK_LOOKUP("bus_clk", dfab_scm_clk.c, "scm"), + CLK_LOOKUP("bus_clk", dfab_qseecom_clk.c, "qseecom"), + + CLK_LOOKUP("mem_clk", ebi1_adm0_clk.c, "msm_dmov.0"), + CLK_LOOKUP("mem_clk", ebi1_adm1_clk.c, "msm_dmov.1"), + CLK_LOOKUP("mem_clk", ebi1_acpu_a_clk.c, ""), + CLK_LOOKUP("bus_clk", afab_acpu_a_clk.c, ""), + + CLK_LOOKUP("sc0_mclk", sc0_m_clk, ""), + CLK_LOOKUP("sc1_mclk", sc1_m_clk, ""), + CLK_LOOKUP("l2_mclk", l2_m_clk, ""), +}; + +/* + * Miscellaneous clock register initializations + */ + +/* Read, modify, then write-back a register. */ +static void __init rmwreg(uint32_t val, void *reg, uint32_t mask) +{ + uint32_t regval = readl_relaxed(reg); + regval &= ~mask; + regval |= val; + writel_relaxed(regval, reg); +} + +static void __init msm8660_clock_pre_init(void) +{ + vote_vdd_level(&vdd_dig, VDD_DIG_HIGH); + + /* Setup MM_PLL2 (PLL3), but turn it off. Rate set by set_rate_tv(). */ + rmwreg(0, MM_PLL2_MODE_REG, BIT(0)); /* Disable output */ + /* Set ref, bypass, assert reset, disable output, disable test mode */ + writel_relaxed(0, MM_PLL2_MODE_REG); /* PXO */ + writel_relaxed(0x00800000, MM_PLL2_CONFIG_REG); /* Enable main out. */ + + /* The clock driver doesn't use SC1's voting register to control + * HW-voteable clocks. Clear its bits so that disabling bits in the + * SC0 register will cause the corresponding clocks to be disabled. */ + rmwreg(BIT(12)|BIT(11), SC0_U_CLK_BRANCH_ENA_VOTE_REG, BM(12, 11)); + writel_relaxed(BIT(12)|BIT(11), SC1_U_CLK_BRANCH_ENA_VOTE_REG); + /* Let sc_aclk and sc_clk halt when both Scorpions are collapsed. */ + writel_relaxed(BIT(12)|BIT(11), SC0_U_CLK_SLEEP_ENA_VOTE_REG); + writel_relaxed(BIT(12)|BIT(11), SC1_U_CLK_SLEEP_ENA_VOTE_REG); + + /* Deassert MM SW_RESET_ALL signal. */ + writel_relaxed(0, SW_RESET_ALL_REG); + + /* Initialize MM AHB registers: Enable the FPB clock and disable HW + * gating for all clocks. Also set VFE_AHB's FORCE_CORE_ON bit to + * prevent its memory from being collapsed when the clock is halted. + * The sleep and wake-up delays are set to safe values. */ + rmwreg(0x00000003, AHB_EN_REG, 0x6C000003); + writel_relaxed(0x000007F9, AHB_EN2_REG); + + /* Deassert all locally-owned MM AHB resets. */ + rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF); + + /* Initialize MM AXI registers: Enable HW gating for all clocks that + * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up + * delays to safe values. */ + rmwreg(0x100207F9, MAXI_EN_REG, 0x1803FFFF); + rmwreg(0x7027FCFF, MAXI_EN2_REG, 0x7A3FFFFF); + writel_relaxed(0x3FE7FCFF, MAXI_EN3_REG); + writel_relaxed(0x000001D8, SAXI_EN_REG); + + /* Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core + * memories retain state even when not clocked. Also, set sleep and + * wake-up delays to safe values. */ + rmwreg(0x00000000, CSI_CC_REG, 0x00000018); + rmwreg(0x00000400, MISC_CC_REG, 0x017C0400); + rmwreg(0x000007FD, MISC_CC2_REG, 0x70C2E7FF); + rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, GFX3D_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, IJPEG_CC_REG, 0xE0FF0018); + rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0018); + rmwreg(0x80FF0000, MDP_CC_REG, 0xE1FF0010); + rmwreg(0x80FF0000, PIXEL_CC_REG, 0xE1FF0010); + rmwreg(0x000004FF, PIXEL_CC2_REG, 0x000007FF); + rmwreg(0x80FF0000, ROT_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010); + rmwreg(0x000004FF, TV_CC2_REG, 0x000027FF); + rmwreg(0xC0FF0000, VCODEC_CC_REG, 0xE0FF0010); + rmwreg(0x80FF0000, VFE_CC_REG, 0xE0FFC010); + rmwreg(0x80FF0000, VPE_CC_REG, 0xE0FF0010); + + /* De-assert MM AXI resets to all hardware blocks. */ + writel_relaxed(0, SW_RESET_AXI_REG); + + /* Deassert all MM core resets. */ + writel_relaxed(0, SW_RESET_CORE_REG); + + /* Enable TSSC and PDM PXO sources. */ + writel_relaxed(BIT(11), TSSC_CLK_CTL_REG); + writel_relaxed(BIT(15), PDM_CLK_NS_REG); + /* Set the dsi_byte_clk src to the DSI PHY PLL, + * dsi_esc_clk to PXO/2, and the hdmi_app_clk src to PXO */ + rmwreg(0x400001, MISC_CC2_REG, 0x424003); + + if ((readl_relaxed(PRNG_CLK_NS_REG) & 0x7F) == 0x2B) + prng_clk.freq_tbl = clk_tbl_prng_64; +} + +static void __init msm8660_clock_post_init(void) +{ + /* Keep PXO on whenever APPS cpu is active */ + clk_prepare_enable(&pxo_a_clk.c); + + /* Reset 3D core while clocked to ensure it resets completely. */ + clk_set_rate(&gfx3d_clk.c, 27000000); + clk_prepare_enable(&gfx3d_clk.c); + clk_reset(&gfx3d_clk.c, CLK_RESET_ASSERT); + udelay(5); + clk_reset(&gfx3d_clk.c, CLK_RESET_DEASSERT); + clk_disable_unprepare(&gfx3d_clk.c); + + /* Initialize rates for clocks that only support one. */ + clk_set_rate(&pdm_clk.c, 27000000); + clk_set_rate(&prng_clk.c, prng_clk.freq_tbl->freq_hz); + clk_set_rate(&mdp_vsync_clk.c, 27000000); + clk_set_rate(&tsif_ref_clk.c, 105000); + clk_set_rate(&tssc_clk.c, 27000000); + clk_set_rate(&usb_hs1_xcvr_clk.c, 60000000); + clk_set_rate(&usb_fs1_src_clk.c, 60000000); + clk_set_rate(&usb_fs2_src_clk.c, 60000000); + + /* The halt status bits for PDM and TSSC may be incorrect at boot. + * Toggle these clocks on and off to refresh them. */ + clk_prepare_enable(&pdm_clk.c); + clk_disable_unprepare(&pdm_clk.c); + clk_prepare_enable(&tssc_clk.c); + clk_disable_unprepare(&tssc_clk.c); +} + +static int __init msm8660_clock_late_init(void) +{ + int rc; + + /* Vote for MMFPB to be at least 64MHz when an Apps CPU is active. */ + struct clk *mmfpb_a_clk = clk_get(NULL, "mmfpb_a_clk"); + if (WARN(IS_ERR(mmfpb_a_clk), "mmfpb_a_clk not found (%ld)\n", + PTR_ERR(mmfpb_a_clk))) + return PTR_ERR(mmfpb_a_clk); + rc = clk_set_rate(mmfpb_a_clk, 64000000); + if (WARN(rc, "mmfpb_a_clk rate was not set (%d)\n", rc)) + return rc; + rc = clk_prepare_enable(mmfpb_a_clk); + if (WARN(rc, "mmfpb_a_clk not enabled (%d)\n", rc)) + return rc; + + return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH); +} + +struct clock_init_data msm8x60_clock_init_data __initdata = { + .table = msm_clocks_8x60, + .size = ARRAY_SIZE(msm_clocks_8x60), + .pre_init = msm8660_clock_pre_init, + .post_init = msm8660_clock_post_init, + .late_init = msm8660_clock_late_init, +}; diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c new file mode 100644 index 00000000000..834deb61007 --- /dev/null +++ b/arch/arm/mach-msm/clock-9615.c @@ -0,0 +1,1879 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "clock-local.h" +#include "clock-voter.h" +#include "clock-rpm.h" +#include "devices.h" +#include "clock-pll.h" + +#define REG(off) (MSM_CLK_CTL_BASE + (off)) +#define REG_LPA(off) (MSM_LPASS_CLK_CTL_BASE + (off)) +#define REG_GCC(off) (MSM_APCS_GCC_BASE + (off)) + +/* Peripheral clock registers. */ +#define CE1_HCLK_CTL_REG REG(0x2720) +#define CE1_CORE_CLK_CTL_REG REG(0x2724) +#define DMA_BAM_HCLK_CTL REG(0x25C0) +#define CLK_HALT_CFPB_STATEA_REG REG(0x2FCC) +#define CLK_HALT_CFPB_STATEB_REG REG(0x2FD0) +#define CLK_HALT_CFPB_STATEC_REG REG(0x2FD4) +#define CLK_HALT_DFAB_STATE_REG REG(0x2FC8) + +#define CLK_HALT_MSS_KPSS_MISC_STATE_REG REG(0x2FDC) +#define CLK_HALT_SFPB_MISC_STATE_REG REG(0x2FD8) +#define CLK_TEST_REG REG(0x2FA0) +#define GPn_MD_REG(n) REG(0x2D00+(0x20*(n))) +#define GPn_NS_REG(n) REG(0x2D24+(0x20*(n))) +#define GSBIn_HCLK_CTL_REG(n) REG(0x29C0+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_MD_REG(n) REG(0x29C8+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_NS_REG(n) REG(0x29CC+(0x20*((n)-1))) +#define GSBIn_RESET_REG(n) REG(0x29DC+(0x20*((n)-1))) +#define GSBIn_UART_APPS_MD_REG(n) REG(0x29D0+(0x20*((n)-1))) +#define GSBIn_UART_APPS_NS_REG(n) REG(0x29D4+(0x20*((n)-1))) +#define PDM_CLK_NS_REG REG(0x2CC0) +#define BB_PLL_ENA_SC0_REG REG(0x34C0) + +#define BB_PLL0_L_VAL_REG REG(0x30C4) +#define BB_PLL0_M_VAL_REG REG(0x30C8) +#define BB_PLL0_MODE_REG REG(0x30C0) +#define BB_PLL0_N_VAL_REG REG(0x30CC) +#define BB_PLL0_STATUS_REG REG(0x30D8) +#define BB_PLL0_CONFIG_REG REG(0x30D4) +#define BB_PLL0_TEST_CTL_REG REG(0x30D0) + +#define BB_PLL8_L_VAL_REG REG(0x3144) +#define BB_PLL8_M_VAL_REG REG(0x3148) +#define BB_PLL8_MODE_REG REG(0x3140) +#define BB_PLL8_N_VAL_REG REG(0x314C) +#define BB_PLL8_STATUS_REG REG(0x3158) +#define BB_PLL8_CONFIG_REG REG(0x3154) +#define BB_PLL8_TEST_CTL_REG REG(0x3150) + +#define BB_PLL14_L_VAL_REG REG(0x31C4) +#define BB_PLL14_M_VAL_REG REG(0x31C8) +#define BB_PLL14_MODE_REG REG(0x31C0) +#define BB_PLL14_N_VAL_REG REG(0x31CC) +#define BB_PLL14_STATUS_REG REG(0x31D8) +#define BB_PLL14_CONFIG_REG REG(0x31D4) +#define BB_PLL14_TEST_CTL_REG REG(0x31D0) + +#define SC_PLL0_L_VAL_REG REG(0x3208) +#define SC_PLL0_M_VAL_REG REG(0x320C) +#define SC_PLL0_MODE_REG REG(0x3200) +#define SC_PLL0_N_VAL_REG REG(0x3210) +#define SC_PLL0_STATUS_REG REG(0x321C) +#define SC_PLL0_CONFIG_REG REG(0x3204) +#define SC_PLL0_TEST_CTL_REG REG(0x3218) + +#define PLLTEST_PAD_CFG_REG REG(0x2FA4) +#define PMEM_ACLK_CTL_REG REG(0x25A0) +#define RINGOSC_NS_REG REG(0x2DC0) +#define RINGOSC_STATUS_REG REG(0x2DCC) +#define RINGOSC_TCXO_CTL_REG REG(0x2DC4) +#define SC0_U_CLK_BRANCH_ENA_VOTE_REG REG(0x3080) +#define SDCn_APPS_CLK_MD_REG(n) REG(0x2828+(0x20*((n)-1))) +#define SDCn_APPS_CLK_NS_REG(n) REG(0x282C+(0x20*((n)-1))) +#define SDCn_HCLK_CTL_REG(n) REG(0x2820+(0x20*((n)-1))) +#define SDCn_RESET_REG(n) REG(0x2830+(0x20*((n)-1))) +#define USB_HS1_HCLK_CTL_REG REG(0x2900) +#define USB_HS1_RESET_REG REG(0x2910) +#define USB_HS1_XCVR_FS_CLK_MD_REG REG(0x2908) +#define USB_HS1_XCVR_FS_CLK_NS_REG REG(0x290C) +#define USB_HS1_SYS_CLK_MD_REG REG(0x36A0) +#define USB_HS1_SYS_CLK_NS_REG REG(0x36A4) +#define USB_HSIC_HCLK_CTL_REG REG(0x2920) +#define USB_HSIC_XCVR_FS_CLK_MD_REG REG(0x2924) +#define USB_HSIC_XCVR_FS_CLK_NS_REG REG(0x2928) +#define USB_HSIC_RESET_REG REG(0x2934) +#define USB_HSIC_HSIO_CAL_CLK_CTL_REG REG(0x2B48) +#define USB_HSIC_CLK_MD_REG REG(0x2B4C) +#define USB_HSIC_CLK_NS_REG REG(0x2B50) +#define USB_HSIC_SYSTEM_CLK_MD_REG REG(0x2B54) +#define USB_HSIC_SYSTEM_CLK_NS_REG REG(0x2B58) +#define SLIMBUS_XO_SRC_CLK_CTL_REG REG(0x2628) + +/* Low-power Audio clock registers. */ +#define LCC_CLK_HS_DEBUG_CFG_REG REG_LPA(0x00A4) +#define LCC_CLK_LS_DEBUG_CFG_REG REG_LPA(0x00A8) +#define LCC_CODEC_I2S_MIC_MD_REG REG_LPA(0x0064) +#define LCC_CODEC_I2S_MIC_NS_REG REG_LPA(0x0060) +#define LCC_CODEC_I2S_MIC_STATUS_REG REG_LPA(0x0068) +#define LCC_CODEC_I2S_SPKR_MD_REG REG_LPA(0x0070) +#define LCC_CODEC_I2S_SPKR_NS_REG REG_LPA(0x006C) +#define LCC_CODEC_I2S_SPKR_STATUS_REG REG_LPA(0x0074) +#define LCC_MI2S_MD_REG REG_LPA(0x004C) +#define LCC_MI2S_NS_REG REG_LPA(0x0048) +#define LCC_MI2S_STATUS_REG REG_LPA(0x0050) +#define LCC_PCM_MD_REG REG_LPA(0x0058) +#define LCC_PCM_NS_REG REG_LPA(0x0054) +#define LCC_PCM_STATUS_REG REG_LPA(0x005C) +#define LCC_SEC_PCM_MD_REG REG_LPA(0x00F4) +#define LCC_SEC_PCM_NS_REG REG_LPA(0x00F0) +#define LCC_SEC_PCM_STATUS_REG REG_LPA(0x00F8) +#define LCC_PLL0_STATUS_REG REG_LPA(0x0018) +#define LCC_SPARE_I2S_MIC_MD_REG REG_LPA(0x007C) +#define LCC_SPARE_I2S_MIC_NS_REG REG_LPA(0x0078) +#define LCC_SPARE_I2S_MIC_STATUS_REG REG_LPA(0x0080) +#define LCC_SPARE_I2S_SPKR_MD_REG REG_LPA(0x0088) +#define LCC_SPARE_I2S_SPKR_NS_REG REG_LPA(0x0084) +#define LCC_SPARE_I2S_SPKR_STATUS_REG REG_LPA(0x008C) +#define LCC_SLIMBUS_NS_REG REG_LPA(0x00CC) +#define LCC_SLIMBUS_MD_REG REG_LPA(0x00D0) +#define LCC_SLIMBUS_STATUS_REG REG_LPA(0x00D4) +#define LCC_AHBEX_BRANCH_CTL_REG REG_LPA(0x00E4) +#define LCC_PRI_PLL_CLK_CTL_REG REG_LPA(0x00C4) + +#define GCC_APCS_CLK_DIAG REG_GCC(0x001C) + +/* MUX source input identifiers. */ +#define cxo_to_bb_mux 0 +#define pll8_to_bb_mux 3 +#define pll8_acpu_to_bb_mux 3 +#define pll14_to_bb_mux 4 +#define gnd_to_bb_mux 6 +#define cxo_to_xo_mux 0 +#define gnd_to_xo_mux 3 +#define cxo_to_lpa_mux 1 +#define pll4_to_lpa_mux 2 +#define gnd_to_lpa_mux 6 + +/* Test Vector Macros */ +#define TEST_TYPE_PER_LS 1 +#define TEST_TYPE_PER_HS 2 +#define TEST_TYPE_LPA 5 +#define TEST_TYPE_LPA_HS 6 +#define TEST_TYPE_SHIFT 24 +#define TEST_CLK_SEL_MASK BM(23, 0) +#define TEST_VECTOR(s, t) (((t) << TEST_TYPE_SHIFT) | BVAL(23, 0, (s))) +#define TEST_PER_LS(s) TEST_VECTOR((s), TEST_TYPE_PER_LS) +#define TEST_PER_HS(s) TEST_VECTOR((s), TEST_TYPE_PER_HS) +#define TEST_LPA(s) TEST_VECTOR((s), TEST_TYPE_LPA) +#define TEST_LPA_HS(s) TEST_VECTOR((s), TEST_TYPE_LPA_HS) + +enum vdd_dig_levels { + VDD_DIG_NONE, + VDD_DIG_LOW, + VDD_DIG_NOMINAL, + VDD_DIG_HIGH +}; + +static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level) +{ + static const int vdd_uv[] = { + [VDD_DIG_NONE] = 0, + [VDD_DIG_LOW] = 945000, + [VDD_DIG_NOMINAL] = 1050000, + [VDD_DIG_HIGH] = 1150000 + }; + + return rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER3, + vdd_uv[level], vdd_uv[VDD_DIG_HIGH], 1); +} + +static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig); + +#define VDD_DIG_FMAX_MAP1(l1, f1) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1) +#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2) + +/* + * Clock Descriptions + */ + +DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000); + +static DEFINE_SPINLOCK(soft_vote_lock); + +static int pll_acpu_vote_clk_enable(struct clk *clk) +{ + int ret = 0; + unsigned long flags; + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + + spin_lock_irqsave(&soft_vote_lock, flags); + + if (!*pll->soft_vote) + ret = pll_vote_clk_enable(clk); + if (ret == 0) + *pll->soft_vote |= (pll->soft_vote_mask); + + spin_unlock_irqrestore(&soft_vote_lock, flags); + return ret; +} + +static void pll_acpu_vote_clk_disable(struct clk *clk) +{ + unsigned long flags; + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + + spin_lock_irqsave(&soft_vote_lock, flags); + + *pll->soft_vote &= ~(pll->soft_vote_mask); + if (!*pll->soft_vote) + pll_vote_clk_disable(clk); + + spin_unlock_irqrestore(&soft_vote_lock, flags); +} + +static struct clk_ops clk_ops_pll_acpu_vote = { + .enable = pll_acpu_vote_clk_enable, + .disable = pll_acpu_vote_clk_disable, + .auto_off = pll_acpu_vote_clk_disable, + .is_enabled = pll_vote_clk_is_enabled, + .get_parent = pll_vote_clk_get_parent, +}; + +#define PLL_SOFT_VOTE_PRIMARY BIT(0) +#define PLL_SOFT_VOTE_ACPU BIT(1) + +static unsigned int soft_vote_pll0; + +static struct pll_vote_clk pll0_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(0), + .status_reg = BB_PLL0_STATUS_REG, + .status_mask = BIT(16), + .parent = &cxo_clk.c, + .soft_vote = &soft_vote_pll0, + .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY, + .c = { + .dbg_name = "pll0_clk", + .rate = 276000000, + .ops = &clk_ops_pll_acpu_vote, + CLK_INIT(pll0_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll0_acpu_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(0), + .status_reg = BB_PLL0_STATUS_REG, + .status_mask = BIT(16), + .soft_vote = &soft_vote_pll0, + .soft_vote_mask = PLL_SOFT_VOTE_ACPU, + .c = { + .dbg_name = "pll0_acpu_clk", + .rate = 276000000, + .ops = &clk_ops_pll_acpu_vote, + CLK_INIT(pll0_acpu_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll4_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(4), + .status_reg = LCC_PLL0_STATUS_REG, + .status_mask = BIT(16), + .parent = &cxo_clk.c, + .c = { + .dbg_name = "pll4_clk", + .rate = 393216000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll4_clk.c), + .warned = true, + }, +}; + +static unsigned int soft_vote_pll8; + +static struct pll_vote_clk pll8_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(8), + .status_reg = BB_PLL8_STATUS_REG, + .status_mask = BIT(16), + .parent = &cxo_clk.c, + .soft_vote = &soft_vote_pll8, + .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY, + .c = { + .dbg_name = "pll8_clk", + .rate = 384000000, + .ops = &clk_ops_pll_acpu_vote, + CLK_INIT(pll8_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll8_acpu_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(8), + .status_reg = BB_PLL8_STATUS_REG, + .status_mask = BIT(16), + .soft_vote = &soft_vote_pll8, + .soft_vote_mask = PLL_SOFT_VOTE_ACPU, + .c = { + .dbg_name = "pll8_acpu_clk", + .rate = 384000000, + .ops = &clk_ops_pll_acpu_vote, + CLK_INIT(pll8_acpu_clk.c), + .warned = true, + }, +}; + +static struct pll_clk pll9_acpu_clk = { + .mode_reg = SC_PLL0_MODE_REG, + .c = { + .dbg_name = "pll9_acpu_clk", + .rate = 440000000, + .ops = &clk_ops_local_pll, + CLK_INIT(pll9_acpu_clk.c), + .warned = true, + }, +}; + +static struct pll_vote_clk pll14_clk = { + .en_reg = BB_PLL_ENA_SC0_REG, + .en_mask = BIT(11), + .status_reg = BB_PLL14_STATUS_REG, + .status_mask = BIT(16), + .parent = &cxo_clk.c, + .c = { + .dbg_name = "pll14_clk", + .rate = 480000000, + .ops = &clk_ops_pll_vote, + CLK_INIT(pll14_clk.c), + .warned = true, + }, +}; + +/* + * Peripheral Clocks + */ +#define CLK_GP(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GPn_NS_REG(n), \ + .en_mask = BIT(9), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GPn_NS_REG(n), \ + .md_reg = GPn_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gp, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP1(LOW, 27000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gp[] = { + F_GP( 0, gnd, 1, 0, 0), + F_GP( 9600000, cxo, 2, 0, 0), + F_GP( 19200000, cxo, 1, 0, 0), + F_END +}; + +static CLK_GP(gp0, 0, CLK_HALT_SFPB_MISC_STATE_REG, 7); +static CLK_GP(gp1, 1, CLK_HALT_SFPB_MISC_STATE_REG, 6); +static CLK_GP(gp2, 2, CLK_HALT_SFPB_MISC_STATE_REG, 5); + +#define CLK_GSBI_UART(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GSBIn_UART_APPS_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = GSBIn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GSBIn_UART_APPS_NS_REG(n), \ + .md_reg = GSBIn_UART_APPS_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(31, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gsbi_uart, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 32000000, NOMINAL, 64000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GSBI_UART(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gsbi_uart[] = { + F_GSBI_UART( 0, gnd, 1, 0, 0), + F_GSBI_UART( 3686400, pll8, 2, 12, 625), + F_GSBI_UART( 7372800, pll8, 2, 24, 625), + F_GSBI_UART(14745600, pll8, 2, 48, 625), + F_GSBI_UART(16000000, pll8, 4, 1, 6), + F_GSBI_UART(24000000, pll8, 4, 1, 4), + F_GSBI_UART(32000000, pll8, 4, 1, 3), + F_GSBI_UART(40000000, pll8, 1, 5, 48), + F_GSBI_UART(46400000, pll8, 1, 29, 240), + F_GSBI_UART(48000000, pll8, 4, 1, 2), + F_GSBI_UART(51200000, pll8, 1, 2, 15), + F_GSBI_UART(56000000, pll8, 1, 7, 48), + F_GSBI_UART(58982400, pll8, 1, 96, 625), + F_GSBI_UART(64000000, pll8, 2, 1, 3), + F_END +}; + +static CLK_GSBI_UART(gsbi1_uart, 1, CLK_HALT_CFPB_STATEA_REG, 10); +static CLK_GSBI_UART(gsbi2_uart, 2, CLK_HALT_CFPB_STATEA_REG, 6); +static CLK_GSBI_UART(gsbi3_uart, 3, CLK_HALT_CFPB_STATEA_REG, 2); +static CLK_GSBI_UART(gsbi4_uart, 4, CLK_HALT_CFPB_STATEB_REG, 26); +static CLK_GSBI_UART(gsbi5_uart, 5, CLK_HALT_CFPB_STATEB_REG, 22); + +#define CLK_GSBI_QUP(i, n, h_r, h_b) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = GSBIn_QUP_APPS_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = GSBIn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = h_r, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = GSBIn_QUP_APPS_NS_REG(n), \ + .md_reg = GSBIn_QUP_APPS_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_gsbi_qup, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 24000000, NOMINAL, 52000000), \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define F_GSBI_QUP(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_gsbi_qup[] = { + F_GSBI_QUP( 0, gnd, 1, 0, 0), + F_GSBI_QUP( 960000, cxo, 4, 1, 5), + F_GSBI_QUP( 4800000, cxo, 4, 0, 1), + F_GSBI_QUP( 9600000, cxo, 2, 0, 1), + F_GSBI_QUP(15058800, pll8, 1, 2, 51), + F_GSBI_QUP(24000000, pll8, 4, 1, 4), + F_GSBI_QUP(25600000, pll8, 1, 1, 15), + F_GSBI_QUP(48000000, pll8, 4, 1, 2), + F_GSBI_QUP(51200000, pll8, 1, 2, 15), + F_END +}; + +static CLK_GSBI_QUP(gsbi1_qup, 1, CLK_HALT_CFPB_STATEA_REG, 9); +static CLK_GSBI_QUP(gsbi2_qup, 2, CLK_HALT_CFPB_STATEA_REG, 4); +static CLK_GSBI_QUP(gsbi3_qup, 3, CLK_HALT_CFPB_STATEA_REG, 0); +static CLK_GSBI_QUP(gsbi4_qup, 4, CLK_HALT_CFPB_STATEB_REG, 24); +static CLK_GSBI_QUP(gsbi5_qup, 5, CLK_HALT_CFPB_STATEB_REG, 20); + +#define F_PDM(f, s, d) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .ns_val = NS_SRC_SEL(1, 0, s##_to_xo_mux), \ + } +static struct clk_freq_tbl clk_tbl_pdm[] = { + F_PDM( 0, gnd, 1), + F_PDM(19200000, cxo, 1), + F_END +}; + +static struct rcg_clk pdm_clk = { + .b = { + .ctl_reg = PDM_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = PDM_CLK_NS_REG, + .reset_mask = BIT(12), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 3, + }, + .ns_reg = PDM_CLK_NS_REG, + .root_en_mask = BIT(11), + .ns_mask = BM(1, 0), + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_pdm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pdm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 19200000), + CLK_INIT(pdm_clk.c), + }, +}; + +static struct branch_clk pmem_clk = { + .b = { + .ctl_reg = PMEM_ACLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = PMEM_ACLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 20, + }, + .c = { + .dbg_name = "pmem_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmem_clk.c), + }, +}; + +#define F_PRNG(f, s) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + } +static struct clk_freq_tbl clk_tbl_prng[] = { + F_PRNG(32000000, pll8), + F_END +}; + +static struct rcg_clk prng_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(10), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 10, + }, + .set_rate = set_rate_nop, + .freq_tbl = clk_tbl_prng, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "prng_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 32000000, NOMINAL, 65000000), + CLK_INIT(prng_clk.c), + }, +}; + +#define CLK_SDC(name, n, h_b, f_table) \ + struct rcg_clk name = { \ + .b = { \ + .ctl_reg = SDCn_APPS_CLK_NS_REG(n), \ + .en_mask = BIT(9), \ + .reset_reg = SDCn_RESET_REG(n), \ + .reset_mask = BIT(0), \ + .halt_reg = CLK_HALT_DFAB_STATE_REG, \ + .halt_bit = h_b, \ + }, \ + .ns_reg = SDCn_APPS_CLK_NS_REG(n), \ + .md_reg = SDCn_APPS_CLK_MD_REG(n), \ + .root_en_mask = BIT(11), \ + .ns_mask = (BM(23, 16) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = f_table, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #name, \ + .ops = &clk_ops_rcg, \ + VDD_DIG_FMAX_MAP2(LOW, 26000000, NOMINAL, 52000000), \ + CLK_INIT(name.c), \ + }, \ + } +#define F_SDC(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_sdc1_2[] = { + F_SDC( 0, gnd, 1, 0, 0), + F_SDC( 144300, cxo, 1, 1, 133), + F_SDC( 400000, pll8, 4, 1, 240), + F_SDC( 16000000, pll8, 4, 1, 6), + F_SDC( 17070000, pll8, 1, 2, 45), + F_SDC( 20210000, pll8, 1, 1, 19), + F_SDC( 24000000, pll8, 4, 1, 4), + F_SDC( 38400000, pll8, 2, 1, 5), + F_SDC( 48000000, pll8, 4, 1, 2), + F_SDC( 64000000, pll8, 3, 1, 2), + F_SDC( 76800000, pll8, 1, 1, 5), + F_END +}; + +static CLK_SDC(sdc1_clk, 1, 6, clk_tbl_sdc1_2); +static CLK_SDC(sdc2_clk, 2, 5, clk_tbl_sdc1_2); + +#define F_USB(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(16, m, 0, n), \ + .ns_val = NS(23, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_bb_mux), \ + } +static struct clk_freq_tbl clk_tbl_usb[] = { + F_USB( 0, gnd, 1, 0, 0), + F_USB(60000000, pll8, 1, 5, 32), + F_END +}; + +static struct clk_freq_tbl clk_tbl_usb_hsic_sys[] = { + F_USB( 0, gnd, 1, 0, 0), + F_USB(64000000, pll8_acpu, 1, 1, 6), + F_END +}; + +static struct rcg_clk usb_hs1_xcvr_clk = { + .b = { + .ctl_reg = USB_HS1_XCVR_FS_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = USB_HS1_RESET_REG, + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 0, + }, + .ns_reg = USB_HS1_XCVR_FS_CLK_NS_REG, + .md_reg = USB_HS1_XCVR_FS_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hs1_xcvr_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 60000000), + CLK_INIT(usb_hs1_xcvr_clk.c), + }, +}; + +static struct rcg_clk usb_hs1_sys_clk = { + .b = { + .ctl_reg = USB_HS1_SYS_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = USB_HS1_RESET_REG, + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 4, + }, + .ns_reg = USB_HS1_SYS_CLK_NS_REG, + .md_reg = USB_HS1_SYS_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hs1_sys_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 60000000), + CLK_INIT(usb_hs1_sys_clk.c), + }, +}; + +static struct rcg_clk usb_hsic_xcvr_clk = { + .b = { + .ctl_reg = USB_HSIC_XCVR_FS_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = USB_HSIC_RESET_REG, + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 9, + }, + .ns_reg = USB_HSIC_XCVR_FS_CLK_NS_REG, + .md_reg = USB_HSIC_XCVR_FS_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hsic_xcvr_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 60000000), + CLK_INIT(usb_hsic_xcvr_clk.c), + }, +}; + +static struct rcg_clk usb_hsic_sys_clk = { + .b = { + .ctl_reg = USB_HSIC_SYSTEM_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = USB_HSIC_RESET_REG, + .reset_mask = BIT(0), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 7, + }, + .ns_reg = USB_HSIC_SYSTEM_CLK_NS_REG, + .md_reg = USB_HSIC_SYSTEM_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb_hsic_sys, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hsic_sys_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 64000000), + CLK_INIT(usb_hsic_sys_clk.c), + }, +}; + +static struct clk_freq_tbl clk_tbl_usb_hsic[] = { + F_USB( 0, gnd, 1, 0, 0), + F_USB(480000000, pll14, 1, 0, 0), + F_END +}; + +static struct rcg_clk usb_hsic_clk = { + .b = { + .ctl_reg = USB_HSIC_CLK_NS_REG, + .en_mask = BIT(9), + .reset_reg = USB_HSIC_RESET_REG, + .reset_mask = BIT(0), + .halt_check = DELAY, + }, + .ns_reg = USB_HSIC_CLK_NS_REG, + .md_reg = USB_HSIC_CLK_MD_REG, + .root_en_mask = BIT(11), + .ns_mask = (BM(23, 16) | BM(6, 0)), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_usb_hsic, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "usb_hsic_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 480000000), + CLK_INIT(usb_hsic_clk.c), + }, +}; + +static struct branch_clk usb_hsic_hsio_cal_clk = { + .b = { + .ctl_reg = USB_HSIC_HSIO_CAL_CLK_CTL_REG, + .en_mask = BIT(0), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 8, + }, + .parent = &cxo_clk.c, + .c = { + .dbg_name = "usb_hsic_hsio_cal_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hsic_hsio_cal_clk.c), + }, +}; + +/* Fast Peripheral Bus Clocks */ +static struct branch_clk ce1_core_clk = { + .b = { + .ctl_reg = CE1_CORE_CLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = CE1_CORE_CLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 27, + }, + .c = { + .dbg_name = "ce1_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce1_core_clk.c), + }, +}; +static struct branch_clk ce1_p_clk = { + .b = { + .ctl_reg = CE1_HCLK_CTL_REG, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEC_REG, + .halt_bit = 1, + }, + .c = { + .dbg_name = "ce1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(ce1_p_clk.c), + }, +}; + +static struct branch_clk dma_bam_p_clk = { + .b = { + .ctl_reg = DMA_BAM_HCLK_CTL, + .en_mask = BIT(4), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 12, + }, + .c = { + .dbg_name = "dma_bam_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(dma_bam_p_clk.c), + }, +}; + +static struct branch_clk gsbi1_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "gsbi1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi1_p_clk.c), + }, +}; + +static struct branch_clk gsbi2_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 7, + }, + .c = { + .dbg_name = "gsbi2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi2_p_clk.c), + }, +}; + +static struct branch_clk gsbi3_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(3), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEA_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "gsbi3_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi3_p_clk.c), + }, +}; + +static struct branch_clk gsbi4_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(4), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 27, + }, + .c = { + .dbg_name = "gsbi4_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi4_p_clk.c), + }, +}; + +static struct branch_clk gsbi5_p_clk = { + .b = { + .ctl_reg = GSBIn_HCLK_CTL_REG(5), + .en_mask = BIT(4), + .halt_reg = CLK_HALT_CFPB_STATEB_REG, + .halt_bit = 23, + }, + .c = { + .dbg_name = "gsbi5_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(gsbi5_p_clk.c), + }, +}; + +static struct branch_clk usb_hs1_p_clk = { + .b = { + .ctl_reg = USB_HS1_HCLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = USB_HS1_HCLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 1, + }, + .c = { + .dbg_name = "usb_hs1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hs1_p_clk.c), + }, +}; + +static struct branch_clk usb_hsic_p_clk = { + .b = { + .ctl_reg = USB_HSIC_HCLK_CTL_REG, + .en_mask = BIT(4), + .hwcg_reg = USB_HSIC_HCLK_CTL_REG, + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 3, + }, + .c = { + .dbg_name = "usb_hsic_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(usb_hsic_p_clk.c), + }, +}; + +static struct branch_clk sdc1_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(1), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(1), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 11, + }, + .c = { + .dbg_name = "sdc1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc1_p_clk.c), + }, +}; + +static struct branch_clk sdc2_p_clk = { + .b = { + .ctl_reg = SDCn_HCLK_CTL_REG(2), + .en_mask = BIT(4), + .hwcg_reg = SDCn_HCLK_CTL_REG(2), + .hwcg_mask = BIT(6), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 10, + }, + .c = { + .dbg_name = "sdc2_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(sdc2_p_clk.c), + }, +}; + +/* HW-Voteable Clocks */ +static struct branch_clk adm0_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(2), + .halt_reg = CLK_HALT_MSS_KPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 14, + }, + .c = { + .dbg_name = "adm0_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm0_clk.c), + }, +}; + +static struct branch_clk adm0_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(3), + .halt_reg = CLK_HALT_MSS_KPSS_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 13, + }, + .c = { + .dbg_name = "adm0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(adm0_p_clk.c), + }, +}; + +static struct branch_clk pmic_arb0_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(8), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 22, + }, + .c = { + .dbg_name = "pmic_arb0_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_arb0_p_clk.c), + }, +}; + +static struct branch_clk pmic_arb1_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(9), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 21, + }, + .c = { + .dbg_name = "pmic_arb1_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_arb1_p_clk.c), + }, +}; + +static struct branch_clk pmic_ssbi2_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(7), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 23, + }, + .c = { + .dbg_name = "pmic_ssbi2_clk", + .ops = &clk_ops_branch, + CLK_INIT(pmic_ssbi2_clk.c), + }, +}; + +static struct branch_clk rpm_msg_ram_p_clk = { + .b = { + .ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG, + .en_mask = BIT(6), + .halt_reg = CLK_HALT_SFPB_MISC_STATE_REG, + .halt_check = HALT_VOTED, + .halt_bit = 12, + }, + .c = { + .dbg_name = "rpm_msg_ram_p_clk", + .ops = &clk_ops_branch, + CLK_INIT(rpm_msg_ram_p_clk.c), + }, +}; + +/* + * Low Power Audio Clocks + */ +#define F_AIF_OSR(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD8(8, m, 0, n), \ + .ns_val = NS(31, 24, n, m, 5, 4, 3, d, 2, 0, s##_to_lpa_mux), \ + } +static struct clk_freq_tbl clk_tbl_aif_osr[] = { + F_AIF_OSR( 0, gnd, 1, 0, 0), + F_AIF_OSR( 512000, pll4, 4, 1, 192), + F_AIF_OSR( 768000, pll4, 4, 1, 128), + F_AIF_OSR( 1024000, pll4, 4, 1, 96), + F_AIF_OSR( 1536000, pll4, 4, 1, 64), + F_AIF_OSR( 2048000, pll4, 4, 1, 48), + F_AIF_OSR( 3072000, pll4, 4, 1, 32), + F_AIF_OSR( 4096000, pll4, 4, 1, 24), + F_AIF_OSR( 6144000, pll4, 4, 1, 16), + F_AIF_OSR( 8192000, pll4, 4, 1, 12), + F_AIF_OSR(12288000, pll4, 4, 1, 8), + F_AIF_OSR(24576000, pll4, 4, 1, 4), + F_END +}; + +#define CLK_AIF_OSR(i, ns, md, h_r) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(17), \ + .reset_reg = ns, \ + .reset_mask = BIT(19), \ + .halt_reg = h_r, \ + .halt_check = ENABLE, \ + .halt_bit = 1, \ + }, \ + .ns_reg = ns, \ + .md_reg = md, \ + .root_en_mask = BIT(9), \ + .ns_mask = (BM(31, 24) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_aif_osr, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + CLK_INIT(i##_clk.c), \ + }, \ + } +#define CLK_AIF_OSR_DIV(i, ns, md, h_r) \ + struct rcg_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(21), \ + .reset_reg = ns, \ + .reset_mask = BIT(23), \ + .halt_reg = h_r, \ + .halt_check = ENABLE, \ + .halt_bit = 1, \ + }, \ + .ns_reg = ns, \ + .md_reg = md, \ + .root_en_mask = BIT(9), \ + .ns_mask = (BM(31, 24) | BM(6, 0)), \ + .mnd_en_mask = BIT(8), \ + .set_rate = set_rate_mnd, \ + .freq_tbl = clk_tbl_aif_osr, \ + .current_freq = &rcg_dummy_freq, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_rcg, \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +#define CLK_AIF_BIT(i, ns, h_r) \ + struct cdiv_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(15), \ + .halt_reg = h_r, \ + .halt_check = DELAY, \ + }, \ + .ns_reg = ns, \ + .ext_mask = BIT(14), \ + .div_offset = 10, \ + .max_div = 16, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_cdiv, \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +#define CLK_AIF_BIT_DIV(i, ns, h_r) \ + struct cdiv_clk i##_clk = { \ + .b = { \ + .ctl_reg = ns, \ + .en_mask = BIT(19), \ + .halt_reg = h_r, \ + .halt_check = DELAY, \ + }, \ + .ns_reg = ns, \ + .ext_mask = BIT(18), \ + .div_offset = 10, \ + .max_div = 256, \ + .c = { \ + .dbg_name = #i "_clk", \ + .ops = &clk_ops_cdiv, \ + CLK_INIT(i##_clk.c), \ + }, \ + } + +static CLK_AIF_OSR(mi2s_osr, LCC_MI2S_NS_REG, LCC_MI2S_MD_REG, + LCC_MI2S_STATUS_REG); +static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG); + +static CLK_AIF_OSR_DIV(codec_i2s_mic_osr, LCC_CODEC_I2S_MIC_NS_REG, + LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG); +static CLK_AIF_BIT_DIV(codec_i2s_mic_bit, LCC_CODEC_I2S_MIC_NS_REG, + LCC_CODEC_I2S_MIC_STATUS_REG); + +static CLK_AIF_OSR_DIV(spare_i2s_mic_osr, LCC_SPARE_I2S_MIC_NS_REG, + LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG); +static CLK_AIF_BIT_DIV(spare_i2s_mic_bit, LCC_SPARE_I2S_MIC_NS_REG, + LCC_SPARE_I2S_MIC_STATUS_REG); + +static CLK_AIF_OSR_DIV(codec_i2s_spkr_osr, LCC_CODEC_I2S_SPKR_NS_REG, + LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG); +static CLK_AIF_BIT_DIV(codec_i2s_spkr_bit, LCC_CODEC_I2S_SPKR_NS_REG, + LCC_CODEC_I2S_SPKR_STATUS_REG); + +static CLK_AIF_OSR_DIV(spare_i2s_spkr_osr, LCC_SPARE_I2S_SPKR_NS_REG, + LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG); +static CLK_AIF_BIT_DIV(spare_i2s_spkr_bit, LCC_SPARE_I2S_SPKR_NS_REG, + LCC_SPARE_I2S_SPKR_STATUS_REG); + +#define F_PCM(f, s, d, m, n) \ + { \ + .freq_hz = f, \ + .src_clk = &s##_clk.c, \ + .md_val = MD16(m, n), \ + .ns_val = NS(31, 16, n, m, 5, 4, 3, d, 2, 0, s##_to_lpa_mux), \ + } +static struct clk_freq_tbl clk_tbl_pcm[] = { + { .ns_val = BIT(10) /* external input */ }, + F_PCM( 512000, pll4, 4, 1, 192), + F_PCM( 768000, pll4, 4, 1, 128), + F_PCM( 1024000, pll4, 4, 1, 96), + F_PCM( 1536000, pll4, 4, 1, 64), + F_PCM( 2048000, pll4, 4, 1, 48), + F_PCM( 3072000, pll4, 4, 1, 32), + F_PCM( 4096000, pll4, 4, 1, 24), + F_PCM( 6144000, pll4, 4, 1, 16), + F_PCM( 8192000, pll4, 4, 1, 12), + F_PCM(12288000, pll4, 4, 1, 8), + F_PCM(24576000, pll4, 4, 1, 4), + F_END +}; + +static struct rcg_clk pcm_clk = { + .b = { + .ctl_reg = LCC_PCM_NS_REG, + .en_mask = BIT(11), + .reset_reg = LCC_PCM_NS_REG, + .reset_mask = BIT(13), + .halt_reg = LCC_PCM_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 0, + }, + .ns_reg = LCC_PCM_NS_REG, + .md_reg = LCC_PCM_MD_REG, + .root_en_mask = BIT(9), + .ns_mask = BM(31, 16) | BIT(10) | BM(6, 0), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_pcm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "pcm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 24576000), + CLK_INIT(pcm_clk.c), + }, +}; + +static struct rcg_clk sec_pcm_clk = { + .b = { + .ctl_reg = LCC_SEC_PCM_NS_REG, + .en_mask = BIT(11), + .reset_reg = LCC_SEC_PCM_NS_REG, + .reset_mask = BIT(13), + .halt_reg = LCC_SEC_PCM_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 0, + }, + .ns_reg = LCC_SEC_PCM_NS_REG, + .md_reg = LCC_SEC_PCM_MD_REG, + .root_en_mask = BIT(9), + .ns_mask = BM(31, 16) | BIT(10) | BM(6, 0), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_pcm, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "sec_pcm_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 24576000), + CLK_INIT(sec_pcm_clk.c), + }, +}; + +static struct rcg_clk audio_slimbus_clk = { + .b = { + .ctl_reg = LCC_SLIMBUS_NS_REG, + .en_mask = BIT(10), + .reset_reg = LCC_AHBEX_BRANCH_CTL_REG, + .reset_mask = BIT(5), + .halt_reg = LCC_SLIMBUS_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 0, + }, + .ns_reg = LCC_SLIMBUS_NS_REG, + .md_reg = LCC_SLIMBUS_MD_REG, + .root_en_mask = BIT(9), + .ns_mask = (BM(31, 24) | BM(6, 0)), + .mnd_en_mask = BIT(8), + .set_rate = set_rate_mnd, + .freq_tbl = clk_tbl_aif_osr, + .current_freq = &rcg_dummy_freq, + .c = { + .dbg_name = "audio_slimbus_clk", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 24576000), + CLK_INIT(audio_slimbus_clk.c), + }, +}; + +static struct branch_clk sps_slimbus_clk = { + .b = { + .ctl_reg = LCC_SLIMBUS_NS_REG, + .en_mask = BIT(12), + .halt_reg = LCC_SLIMBUS_STATUS_REG, + .halt_check = ENABLE, + .halt_bit = 1, + }, + .parent = &audio_slimbus_clk.c, + .c = { + .dbg_name = "sps_slimbus_clk", + .ops = &clk_ops_branch, + CLK_INIT(sps_slimbus_clk.c), + }, +}; + +static struct branch_clk slimbus_xo_src_clk = { + .b = { + .ctl_reg = SLIMBUS_XO_SRC_CLK_CTL_REG, + .en_mask = BIT(2), + .halt_reg = CLK_HALT_DFAB_STATE_REG, + .halt_bit = 28, + }, + .parent = &sps_slimbus_clk.c, + .c = { + .dbg_name = "slimbus_xo_src_clk", + .ops = &clk_ops_branch, + CLK_INIT(slimbus_xo_src_clk.c), + }, +}; + +DEFINE_CLK_RPM(cfpb_clk, cfpb_a_clk, CFPB, NULL); +DEFINE_CLK_RPM(dfab_clk, dfab_a_clk, DAYTONA_FABRIC, NULL); +DEFINE_CLK_RPM(ebi1_clk, ebi1_a_clk, EBI1, NULL); +DEFINE_CLK_RPM(sfab_clk, sfab_a_clk, SYSTEM_FABRIC, NULL); +DEFINE_CLK_RPM(sfpb_clk, sfpb_a_clk, SFPB, NULL); + +static DEFINE_CLK_VOTER(dfab_usb_hs_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc1_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sdc2_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_sps_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_bam_dmux_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_msmbus_clk, &dfab_clk.c, 0); +static DEFINE_CLK_VOTER(dfab_msmbus_a_clk, &dfab_a_clk.c, 0); +static DEFINE_CLK_VOTER(ebi1_msmbus_clk, &ebi1_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_msmbus_a_clk, &ebi1_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_acpu_a_clk, &ebi1_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(ebi1_adm_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(sfab_msmbus_a_clk, &sfab_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(sfab_acpu_a_clk, &sfab_a_clk.c, LONG_MAX); + +#ifdef CONFIG_DEBUG_FS +struct measure_sel { + u32 test_vector; + struct clk *clk; +}; + +static DEFINE_CLK_MEASURE(q6sw_clk); +static DEFINE_CLK_MEASURE(q6fw_clk); +static DEFINE_CLK_MEASURE(q6_func_clk); + +static struct measure_sel measure_mux[] = { + { TEST_PER_LS(0x08), &slimbus_xo_src_clk.c }, + { TEST_PER_LS(0x12), &sdc1_p_clk.c }, + { TEST_PER_LS(0x13), &sdc1_clk.c }, + { TEST_PER_LS(0x14), &sdc2_p_clk.c }, + { TEST_PER_LS(0x15), &sdc2_clk.c }, + { TEST_PER_LS(0x1F), &gp0_clk.c }, + { TEST_PER_LS(0x20), &gp1_clk.c }, + { TEST_PER_LS(0x21), &gp2_clk.c }, + { TEST_PER_LS(0x26), &pmem_clk.c }, + { TEST_PER_LS(0x25), &dfab_clk.c }, + { TEST_PER_LS(0x25), &dfab_a_clk.c }, + { TEST_PER_LS(0x32), &dma_bam_p_clk.c }, + { TEST_PER_LS(0x33), &cfpb_clk.c }, + { TEST_PER_LS(0x33), &cfpb_a_clk.c }, + { TEST_PER_LS(0x3E), &gsbi1_uart_clk.c }, + { TEST_PER_LS(0x3F), &gsbi1_qup_clk.c }, + { TEST_PER_LS(0x41), &gsbi2_p_clk.c }, + { TEST_PER_LS(0x42), &gsbi2_uart_clk.c }, + { TEST_PER_LS(0x44), &gsbi2_qup_clk.c }, + { TEST_PER_LS(0x45), &gsbi3_p_clk.c }, + { TEST_PER_LS(0x46), &gsbi3_uart_clk.c }, + { TEST_PER_LS(0x48), &gsbi3_qup_clk.c }, + { TEST_PER_LS(0x49), &gsbi4_p_clk.c }, + { TEST_PER_LS(0x4A), &gsbi4_uart_clk.c }, + { TEST_PER_LS(0x4C), &gsbi4_qup_clk.c }, + { TEST_PER_LS(0x4D), &gsbi5_p_clk.c }, + { TEST_PER_LS(0x4E), &gsbi5_uart_clk.c }, + { TEST_PER_LS(0x50), &gsbi5_qup_clk.c }, + { TEST_PER_LS(0x78), &sfpb_clk.c }, + { TEST_PER_LS(0x78), &sfpb_a_clk.c }, + { TEST_PER_LS(0x7A), &pmic_ssbi2_clk.c }, + { TEST_PER_LS(0x7B), &pmic_arb0_p_clk.c }, + { TEST_PER_LS(0x7C), &pmic_arb1_p_clk.c }, + { TEST_PER_LS(0x7D), &prng_clk.c }, + { TEST_PER_LS(0x7F), &rpm_msg_ram_p_clk.c }, + { TEST_PER_LS(0x80), &adm0_p_clk.c }, + { TEST_PER_LS(0x84), &usb_hs1_p_clk.c }, + { TEST_PER_LS(0x85), &usb_hs1_xcvr_clk.c }, + { TEST_PER_LS(0x86), &usb_hsic_sys_clk.c }, + { TEST_PER_LS(0x87), &usb_hsic_p_clk.c }, + { TEST_PER_LS(0x88), &usb_hsic_xcvr_clk.c }, + { TEST_PER_LS(0x8B), &usb_hsic_hsio_cal_clk.c }, + { TEST_PER_LS(0x8D), &usb_hs1_sys_clk.c }, + { TEST_PER_LS(0x92), &ce1_p_clk.c }, + { TEST_PER_HS(0x18), &sfab_clk.c }, + { TEST_PER_HS(0x18), &sfab_a_clk.c }, + { TEST_PER_HS(0x26), &q6sw_clk }, + { TEST_PER_HS(0x27), &q6fw_clk }, + { TEST_PER_LS(0xA4), &ce1_core_clk.c }, + { TEST_PER_HS(0x2A), &adm0_clk.c }, + { TEST_PER_HS(0x34), &ebi1_clk.c }, + { TEST_PER_HS(0x34), &ebi1_a_clk.c }, + { TEST_PER_HS(0x3E), &usb_hsic_clk.c }, + { TEST_LPA(0x0F), &mi2s_bit_clk.c }, + { TEST_LPA(0x10), &codec_i2s_mic_bit_clk.c }, + { TEST_LPA(0x11), &codec_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x12), &spare_i2s_mic_bit_clk.c }, + { TEST_LPA(0x13), &spare_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x14), &pcm_clk.c }, + { TEST_LPA(0x1D), &audio_slimbus_clk.c }, + { TEST_LPA_HS(0x00), &q6_func_clk }, +}; + +static struct measure_sel *find_measure_sel(struct clk *clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(measure_mux); i++) + if (measure_mux[i].clk == clk) + return &measure_mux[i]; + return NULL; +} + +static int measure_clk_set_parent(struct clk *c, struct clk *parent) +{ + int ret = 0; + u32 clk_sel; + struct measure_sel *p; + struct measure_clk *clk = to_measure_clk(c); + unsigned long flags; + + if (!parent) + return -EINVAL; + + p = find_measure_sel(parent); + if (!p) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* + * Program the test vector, measurement period (sample_ticks) + * and scaling multiplier. + */ + clk->sample_ticks = 0x10000; + clk_sel = p->test_vector & TEST_CLK_SEL_MASK; + clk->multiplier = 1; + switch (p->test_vector >> TEST_TYPE_SHIFT) { + case TEST_TYPE_PER_LS: + writel_relaxed(0x4030D00|BVAL(7, 0, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_PER_HS: + writel_relaxed(0x4020000|BVAL(16, 10, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_LPA: + writel_relaxed(0x4030D98, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), + LCC_CLK_LS_DEBUG_CFG_REG); + break; + case TEST_TYPE_LPA_HS: + writel_relaxed(0x402BC00, CLK_TEST_REG); + writel_relaxed(BVAL(2, 1, clk_sel)|BIT(0), + LCC_CLK_HS_DEBUG_CFG_REG); + break; + default: + ret = -EPERM; + } + /* Make sure test vector is set before starting measurements. */ + mb(); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} + +/* Sample clock for 'ticks' reference clock ticks. */ +static unsigned long run_measurement(unsigned ticks) +{ + /* Stop counters and set the XO4 counter start value. */ + writel_relaxed(ticks, RINGOSC_TCXO_CTL_REG); + + /* Wait for timer to become ready. */ + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) != 0) + cpu_relax(); + + /* Run measurement and wait for completion. */ + writel_relaxed(BIT(28)|ticks, RINGOSC_TCXO_CTL_REG); + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) == 0) + cpu_relax(); + + /* Stop counters. */ + writel_relaxed(0x0, RINGOSC_TCXO_CTL_REG); + + /* Return measured ticks. */ + return readl_relaxed(RINGOSC_STATUS_REG) & BM(24, 0); +} + + +/* Perform a hardware rate measurement for a given clock. + FOR DEBUG USE ONLY: Measurements take ~15 ms! */ +static unsigned long measure_clk_get_rate(struct clk *c) +{ + unsigned long flags; + u32 pdm_reg_backup, ringosc_reg_backup; + u64 raw_count_short, raw_count_full; + struct measure_clk *clk = to_measure_clk(c); + unsigned ret; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable CXO/4 and RINGOSC branch and root. */ + pdm_reg_backup = readl_relaxed(PDM_CLK_NS_REG); + ringosc_reg_backup = readl_relaxed(RINGOSC_NS_REG); + writel_relaxed(0x2898, PDM_CLK_NS_REG); + writel_relaxed(0xA00, RINGOSC_NS_REG); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(clk->sample_ticks); + + writel_relaxed(ringosc_reg_backup, RINGOSC_NS_REG); + writel_relaxed(pdm_reg_backup, PDM_CLK_NS_REG); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, ((clk->sample_ticks * 10) + 35)); + ret = (raw_count_full * clk->multiplier); + } + + /* Route dbg_hs_clk to PLLTEST. 300mV single-ended amplitude. */ + writel_relaxed(0x38F8, PLLTEST_PAD_CFG_REG); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned long measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops clk_ops_measure = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, +}; + +static struct measure_clk measure_clk = { + .c = { + .dbg_name = "measure_clk", + .ops = &clk_ops_measure, + CLK_INIT(measure_clk.c), + }, + .multiplier = 1, +}; + +static struct clk_lookup msm_clocks_9615[] = { + CLK_LOOKUP("xo", cxo_a_clk.c, ""), + CLK_LOOKUP("xo", cxo_clk.c, "BAM_RMNT"), + CLK_LOOKUP("xo", cxo_clk.c, "msm_xo"), + CLK_LOOKUP("pll0", pll0_clk.c, NULL), + CLK_LOOKUP("pll8", pll8_clk.c, NULL), + CLK_LOOKUP("pll14", pll14_clk.c, NULL), + + CLK_LOOKUP("pll0", pll0_acpu_clk.c, "acpu"), + CLK_LOOKUP("pll8", pll8_acpu_clk.c, "acpu"), + CLK_LOOKUP("pll9", pll9_acpu_clk.c, "acpu"), + + CLK_LOOKUP("measure", measure_clk.c, "debug"), + + CLK_LOOKUP("bus_clk", cfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", cfpb_a_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_clk.c, ""), + CLK_LOOKUP("bus_clk", dfab_a_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_clk.c, ""), + CLK_LOOKUP("mem_clk", ebi1_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_clk.c, ""), + CLK_LOOKUP("bus_clk", sfpb_a_clk.c, ""), + + CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"), + CLK_LOOKUP("bus_a_clk", sfab_msmbus_a_clk.c, "msm_sys_fab"), + CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("mem_a_clk", ebi1_msmbus_a_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_clk", dfab_msmbus_clk.c, "msm_bus"), + CLK_LOOKUP("dfab_a_clk", dfab_msmbus_a_clk.c, "msm_bus"), + + CLK_LOOKUP("core_clk", gp0_clk.c, ""), + CLK_LOOKUP("core_clk", gp1_clk.c, ""), + CLK_LOOKUP("core_clk", gp2_clk.c, ""), + + CLK_LOOKUP("core_clk", gsbi3_uart_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", gsbi5_uart_clk.c, ""), + + CLK_LOOKUP("core_clk", gsbi3_qup_clk.c, "spi_qsd.0"), + CLK_LOOKUP("core_clk", gsbi4_qup_clk.c, ""), + CLK_LOOKUP("core_clk", gsbi5_qup_clk.c, "qup_i2c.0"), + + CLK_LOOKUP("core_clk", pdm_clk.c, ""), + CLK_LOOKUP("mem_clk", pmem_clk.c, "msm_sps"), + CLK_LOOKUP("core_clk", prng_clk.c, "msm_rng.0"), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", ce1_p_clk.c, ""), + CLK_LOOKUP("core_clk", ce1_core_clk.c, ""), + CLK_LOOKUP("dma_bam_pclk", dma_bam_p_clk.c, NULL), + + CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "spi_qsd.0"), + CLK_LOOKUP("iface_clk", gsbi4_p_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, "qup_i2c.0"), + + CLK_LOOKUP("iface_clk", usb_hs1_p_clk.c, "msm_otg"), + CLK_LOOKUP("core_clk", usb_hs1_sys_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs1_xcvr_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hsic_xcvr_clk.c, "msm_hsic_host"), + CLK_LOOKUP("cal_clk", usb_hsic_hsio_cal_clk.c, "msm_hsic_host"), + CLK_LOOKUP("core_clk", usb_hsic_sys_clk.c, "msm_hsic_host"), + CLK_LOOKUP("iface_clk", usb_hsic_p_clk.c, "msm_hsic_host"), + CLK_LOOKUP("phy_clk", usb_hsic_clk.c, "msm_hsic_host"), + CLK_LOOKUP("alt_core_clk", usb_hsic_xcvr_clk.c, "msm_hsic_peripheral"), + CLK_LOOKUP("cal_clk", usb_hsic_hsio_cal_clk.c, "msm_hsic_peripheral"), + CLK_LOOKUP("core_clk", usb_hsic_sys_clk.c, "msm_hsic_peripheral"), + CLK_LOOKUP("iface_clk", usb_hsic_p_clk.c, "msm_hsic_peripheral"), + CLK_LOOKUP("phy_clk", usb_hsic_clk.c, "msm_hsic_peripheral"), + + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", adm0_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", adm0_p_clk.c, "msm_dmov"), + CLK_LOOKUP("iface_clk", pmic_arb0_p_clk.c, ""), + CLK_LOOKUP("iface_clk", pmic_arb1_p_clk.c, ""), + CLK_LOOKUP("core_clk", pmic_ssbi2_clk.c, ""), + CLK_LOOKUP("mem_clk", rpm_msg_ram_p_clk.c, ""), + CLK_LOOKUP("bit_clk", mi2s_bit_clk.c, "msm-dai-q6.6"), + CLK_LOOKUP("osr_clk", mi2s_osr_clk.c, "msm-dai-q6.6"), + + CLK_LOOKUP("bit_clk", codec_i2s_mic_bit_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("osr_clk", codec_i2s_mic_osr_clk.c, + "msm-dai-q6.1"), + CLK_LOOKUP("bit_clk", codec_i2s_spkr_bit_clk.c, + "msm-dai-q6.0"), + CLK_LOOKUP("osr_clk", codec_i2s_spkr_osr_clk.c, + "msm-dai-q6.0"), + CLK_LOOKUP("bit_clk", spare_i2s_mic_bit_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("osr_clk", spare_i2s_mic_osr_clk.c, + "msm-dai-q6.5"), + CLK_LOOKUP("bit_clk", codec_i2s_spkr_bit_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("osr_clk", codec_i2s_spkr_osr_clk.c, + "msm-dai-q6.16384"), + CLK_LOOKUP("bit_clk", spare_i2s_spkr_bit_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("osr_clk", spare_i2s_spkr_osr_clk.c, + "msm-dai-q6.4"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.2"), + CLK_LOOKUP("pcm_clk", pcm_clk.c, "msm-dai-q6.3"), + CLK_LOOKUP("sec_pcm_clk", sec_pcm_clk.c, "msm-dai-q6.12"), + CLK_LOOKUP("sec_pcm_clk", sec_pcm_clk.c, "msm-dai-q6.13"), + + CLK_LOOKUP("sps_slimbus_clk", sps_slimbus_clk.c, NULL), + CLK_LOOKUP("core_clk", audio_slimbus_clk.c, "msm_slim_ctrl.1"), + CLK_LOOKUP("core_clk", dfab_usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("bus_clk", dfab_sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("bus_clk", dfab_sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("dfab_clk", dfab_sps_clk.c, "msm_sps"), + CLK_LOOKUP("bus_clk", dfab_bam_dmux_clk.c, "BAM_RMNT"), + CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"), + CLK_LOOKUP("mem_clk", ebi1_acpu_a_clk.c, ""), + CLK_LOOKUP("bus_clk", sfab_acpu_a_clk.c, ""), + + CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qce.0"), + CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qcrypto.0"), + CLK_LOOKUP("core_clk", ce1_core_clk.c, "qce.0"), + CLK_LOOKUP("core_clk", ce1_core_clk.c, "qcrypto.0"), + + CLK_LOOKUP("q6sw_clk", q6sw_clk, NULL), + CLK_LOOKUP("q6fw_clk", q6fw_clk, NULL), + CLK_LOOKUP("q6_func_clk", q6_func_clk, NULL), +}; + +static struct pll_config_regs pll0_regs __initdata = { + .l_reg = BB_PLL0_L_VAL_REG, + .m_reg = BB_PLL0_M_VAL_REG, + .n_reg = BB_PLL0_N_VAL_REG, + .config_reg = BB_PLL0_CONFIG_REG, + .mode_reg = BB_PLL0_MODE_REG, +}; + +static struct pll_config pll0_config __initdata = { + .l = 0xE, + .m = 0x3, + .n = 0x8, + .vco_val = 0x0, + .vco_mask = BM(17, 16), + .pre_div_val = 0x0, + .pre_div_mask = BIT(19), + .post_div_val = 0x0, + .post_div_mask = BM(21, 20), + .mn_ena_val = BIT(22), + .mn_ena_mask = BIT(22), + .main_output_val = BIT(23), + .main_output_mask = BIT(23), +}; + +static struct pll_config_regs pll14_regs __initdata = { + .l_reg = BB_PLL14_L_VAL_REG, + .m_reg = BB_PLL14_M_VAL_REG, + .n_reg = BB_PLL14_N_VAL_REG, + .config_reg = BB_PLL14_CONFIG_REG, + .mode_reg = BB_PLL14_MODE_REG, +}; + +static struct pll_config pll14_config __initdata = { + .l = 0x19, + .m = 0x0, + .n = 0x1, + .vco_val = 0x0, + .vco_mask = BM(17, 16), + .pre_div_val = 0x0, + .pre_div_mask = BIT(19), + .post_div_val = 0x0, + .post_div_mask = BM(21, 20), + .main_output_val = BIT(23), + .main_output_mask = BIT(23), +}; + +/* + * Miscellaneous clock register initializations + */ +static void __init msm9615_clock_pre_init(void) +{ + u32 regval, is_pll_enabled, pll9_lval; + + vote_vdd_level(&vdd_dig, VDD_DIG_HIGH); + + clk_ops_local_pll.enable = sr_pll_clk_enable; + + /* Enable PDM CXO source. */ + regval = readl_relaxed(PDM_CLK_NS_REG); + writel_relaxed(BIT(13) | regval, PDM_CLK_NS_REG); + + /* Check if PLL0 is active */ + is_pll_enabled = readl_relaxed(BB_PLL0_STATUS_REG) & BIT(16); + + if (!is_pll_enabled) { + /* Enable AUX output */ + regval = readl_relaxed(BB_PLL0_TEST_CTL_REG); + regval |= BIT(12); + writel_relaxed(regval, BB_PLL0_TEST_CTL_REG); + + configure_pll(&pll0_config, &pll0_regs, 1); + } + + /* Check if PLL14 is enabled in FSM mode */ + is_pll_enabled = readl_relaxed(BB_PLL14_STATUS_REG) & BIT(16); + + if (!is_pll_enabled) + configure_pll(&pll14_config, &pll14_regs, 1); + else if (!(readl_relaxed(BB_PLL14_MODE_REG) & BIT(20))) + WARN(1, "PLL14 enabled in non-FSM mode!\n"); + + /* Detect PLL9 rate and fixup structure accordingly */ + pll9_lval = readl_relaxed(SC_PLL0_L_VAL_REG); + + if (pll9_lval == 0x1C) + pll9_acpu_clk.c.rate = 550000000; + + /* Enable PLL4 source on the LPASS Primary PLL Mux */ + regval = readl_relaxed(LCC_PRI_PLL_CLK_CTL_REG); + writel_relaxed(regval | BIT(0), LCC_PRI_PLL_CLK_CTL_REG); + + /* + * Disable hardware clock gating for pmem_clk. Leaving it enabled + * results in the clock staying on. + */ + regval = readl_relaxed(PMEM_ACLK_CTL_REG); + regval &= ~BIT(6); + writel_relaxed(regval, PMEM_ACLK_CTL_REG); + + /* + * Disable hardware clock gating for dma_bam_p_clk, which does + * not have working support for the feature. + */ + regval = readl_relaxed(DMA_BAM_HCLK_CTL); + regval &= ~BIT(6); + writel_relaxed(regval, DMA_BAM_HCLK_CTL); +} + +static void __init msm9615_clock_post_init(void) +{ + /* Keep CXO on whenever APPS cpu is active */ + clk_prepare_enable(&cxo_a_clk.c); + + /* Initialize rates for clocks that only support one. */ + clk_set_rate(&pdm_clk.c, 19200000); + clk_set_rate(&prng_clk.c, 32000000); + clk_set_rate(&usb_hs1_xcvr_clk.c, 60000000); + clk_set_rate(&usb_hs1_sys_clk.c, 60000000); + clk_set_rate(&usb_hsic_xcvr_clk.c, 60000000); + clk_set_rate(&usb_hsic_sys_clk.c, 64000000); + clk_set_rate(&usb_hsic_clk.c, 480000000); + + /* + * The halt status bits for PDM may be incorrect at boot. + * Toggle these clocks on and off to refresh them. + */ + clk_prepare_enable(&pdm_clk.c); + clk_disable_unprepare(&pdm_clk.c); +} + +static int __init msm9615_clock_late_init(void) +{ + return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH); +} + +struct clock_init_data msm9615_clock_init_data __initdata = { + .table = msm_clocks_9615, + .size = ARRAY_SIZE(msm_clocks_9615), + .pre_init = msm9615_clock_pre_init, + .post_init = msm9615_clock_post_init, + .late_init = msm9615_clock_late_init, +}; diff --git a/arch/arm/mach-msm/clock-copper.c b/arch/arm/mach-msm/clock-copper.c new file mode 100644 index 00000000000..778301bd4bd --- /dev/null +++ b/arch/arm/mach-msm/clock-copper.c @@ -0,0 +1,5056 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clock-local2.h" +#include "clock-pll.h" + +enum { + GCC_BASE, + MMSS_BASE, + LPASS_BASE, + MSS_BASE, + N_BASES, +}; + +static void __iomem *virt_bases[N_BASES]; + +#define GCC_REG_BASE(x) (void __iomem *)(virt_bases[GCC_BASE] + (x)) +#define MMSS_REG_BASE(x) (void __iomem *)(virt_bases[MMSS_BASE] + (x)) +#define LPASS_REG_BASE(x) (void __iomem *)(virt_bases[LPASS_BASE] + (x)) +#define MSS_REG_BASE(x) (void __iomem *)(virt_bases[MSS_BASE] + (x)) + +#define GPLL0_MODE_REG 0x0000 +#define GPLL0_L_REG 0x0004 +#define GPLL0_M_REG 0x0008 +#define GPLL0_N_REG 0x000C +#define GPLL0_USER_CTL_REG 0x0010 +#define GPLL0_CONFIG_CTL_REG 0x0014 +#define GPLL0_TEST_CTL_REG 0x0018 +#define GPLL0_STATUS_REG 0x001C + +#define GPLL1_MODE_REG 0x0040 +#define GPLL1_L_REG 0x0044 +#define GPLL1_M_REG 0x0048 +#define GPLL1_N_REG 0x004C +#define GPLL1_USER_CTL_REG 0x0050 +#define GPLL1_CONFIG_CTL_REG 0x0054 +#define GPLL1_TEST_CTL_REG 0x0058 +#define GPLL1_STATUS_REG 0x005C + +#define MMPLL0_MODE_REG 0x0000 +#define MMPLL0_L_REG 0x0004 +#define MMPLL0_M_REG 0x0008 +#define MMPLL0_N_REG 0x000C +#define MMPLL0_USER_CTL_REG 0x0010 +#define MMPLL0_CONFIG_CTL_REG 0x0014 +#define MMPLL0_TEST_CTL_REG 0x0018 +#define MMPLL0_STATUS_REG 0x001C + +#define MMPLL1_MODE_REG 0x0040 +#define MMPLL1_L_REG 0x0044 +#define MMPLL1_M_REG 0x0048 +#define MMPLL1_N_REG 0x004C +#define MMPLL1_USER_CTL_REG 0x0050 +#define MMPLL1_CONFIG_CTL_REG 0x0054 +#define MMPLL1_TEST_CTL_REG 0x0058 +#define MMPLL1_STATUS_REG 0x005C + +#define MMPLL3_MODE_REG 0x0080 +#define MMPLL3_L_REG 0x0084 +#define MMPLL3_M_REG 0x0088 +#define MMPLL3_N_REG 0x008C +#define MMPLL3_USER_CTL_REG 0x0090 +#define MMPLL3_CONFIG_CTL_REG 0x0094 +#define MMPLL3_TEST_CTL_REG 0x0098 +#define MMPLL3_STATUS_REG 0x009C + +#define LPAPLL_MODE_REG 0x0000 +#define LPAPLL_L_REG 0x0004 +#define LPAPLL_M_REG 0x0008 +#define LPAPLL_N_REG 0x000C +#define LPAPLL_USER_CTL_REG 0x0010 +#define LPAPLL_CONFIG_CTL_REG 0x0014 +#define LPAPLL_TEST_CTL_REG 0x0018 +#define LPAPLL_STATUS_REG 0x001C + +#define GCC_DEBUG_CLK_CTL_REG 0x1880 +#define CLOCK_FRQ_MEASURE_CTL_REG 0x1884 +#define CLOCK_FRQ_MEASURE_STATUS_REG 0x1888 +#define GCC_XO_DIV4_CBCR_REG 0x10C8 +#define APCS_GPLL_ENA_VOTE_REG 0x1480 +#define MMSS_PLL_VOTE_APCS_REG 0x0100 +#define MMSS_DEBUG_CLK_CTL_REG 0x0900 +#define LPASS_DEBUG_CLK_CTL_REG 0x29000 +#define LPASS_LPA_PLL_VOTE_APPS_REG 0x2000 +#define MSS_DEBUG_CLK_CTL_REG 0x0078 + +#define USB30_MASTER_CMD_RCGR 0x03D4 +#define USB30_MOCK_UTMI_CMD_RCGR 0x03E8 +#define USB_HSIC_SYSTEM_CMD_RCGR 0x041C +#define USB_HSIC_CMD_RCGR 0x0440 +#define USB_HSIC_IO_CAL_CMD_RCGR 0x0458 +#define USB_HS_SYSTEM_CMD_RCGR 0x0490 +#define SDCC1_APPS_CMD_RCGR 0x04D0 +#define SDCC2_APPS_CMD_RCGR 0x0510 +#define SDCC3_APPS_CMD_RCGR 0x0550 +#define SDCC4_APPS_CMD_RCGR 0x0590 +#define BLSP1_QUP1_SPI_APPS_CMD_RCGR 0x064C +#define BLSP1_UART1_APPS_CMD_RCGR 0x068C +#define BLSP1_QUP2_SPI_APPS_CMD_RCGR 0x06CC +#define BLSP1_UART2_APPS_CMD_RCGR 0x070C +#define BLSP1_QUP3_SPI_APPS_CMD_RCGR 0x074C +#define BLSP1_UART3_APPS_CMD_RCGR 0x078C +#define BLSP1_QUP4_SPI_APPS_CMD_RCGR 0x07CC +#define BLSP1_UART4_APPS_CMD_RCGR 0x080C +#define BLSP1_QUP5_SPI_APPS_CMD_RCGR 0x084C +#define BLSP1_UART5_APPS_CMD_RCGR 0x088C +#define BLSP1_QUP6_SPI_APPS_CMD_RCGR 0x08CC +#define BLSP1_UART6_APPS_CMD_RCGR 0x090C +#define BLSP2_QUP1_SPI_APPS_CMD_RCGR 0x098C +#define BLSP2_UART1_APPS_CMD_RCGR 0x09CC +#define BLSP2_QUP2_SPI_APPS_CMD_RCGR 0x0A0C +#define BLSP2_UART2_APPS_CMD_RCGR 0x0A4C +#define BLSP2_QUP3_SPI_APPS_CMD_RCGR 0x0A8C +#define BLSP2_UART3_APPS_CMD_RCGR 0x0ACC +#define BLSP2_QUP4_SPI_APPS_CMD_RCGR 0x0B0C +#define BLSP2_UART4_APPS_CMD_RCGR 0x0B4C +#define BLSP2_QUP5_SPI_APPS_CMD_RCGR 0x0B8C +#define BLSP2_UART5_APPS_CMD_RCGR 0x0BCC +#define BLSP2_QUP6_SPI_APPS_CMD_RCGR 0x0C0C +#define BLSP2_UART6_APPS_CMD_RCGR 0x0C4C +#define PDM2_CMD_RCGR 0x0CD0 +#define TSIF_REF_CMD_RCGR 0x0D90 +#define CE1_CMD_RCGR 0x1050 +#define CE2_CMD_RCGR 0x1090 +#define GP1_CMD_RCGR 0x1904 +#define GP2_CMD_RCGR 0x1944 +#define GP3_CMD_RCGR 0x1984 +#define LPAIF_SPKR_CMD_RCGR 0xA000 +#define LPAIF_PRI_CMD_RCGR 0xB000 +#define LPAIF_SEC_CMD_RCGR 0xC000 +#define LPAIF_TER_CMD_RCGR 0xD000 +#define LPAIF_QUAD_CMD_RCGR 0xE000 +#define LPAIF_PCM0_CMD_RCGR 0xF000 +#define LPAIF_PCM1_CMD_RCGR 0x10000 +#define RESAMPLER_CMD_RCGR 0x11000 +#define SLIMBUS_CMD_RCGR 0x12000 +#define LPAIF_PCMOE_CMD_RCGR 0x13000 +#define AHBFABRIC_CMD_RCGR 0x18000 +#define VCODEC0_CMD_RCGR 0x1000 +#define PCLK0_CMD_RCGR 0x2000 +#define PCLK1_CMD_RCGR 0x2020 +#define MDP_CMD_RCGR 0x2040 +#define EXTPCLK_CMD_RCGR 0x2060 +#define VSYNC_CMD_RCGR 0x2080 +#define EDPPIXEL_CMD_RCGR 0x20A0 +#define EDPLINK_CMD_RCGR 0x20C0 +#define EDPAUX_CMD_RCGR 0x20E0 +#define HDMI_CMD_RCGR 0x2100 +#define BYTE0_CMD_RCGR 0x2120 +#define BYTE1_CMD_RCGR 0x2140 +#define ESC0_CMD_RCGR 0x2160 +#define ESC1_CMD_RCGR 0x2180 +#define CSI0PHYTIMER_CMD_RCGR 0x3000 +#define CSI1PHYTIMER_CMD_RCGR 0x3030 +#define CSI2PHYTIMER_CMD_RCGR 0x3060 +#define CSI0_CMD_RCGR 0x3090 +#define CSI1_CMD_RCGR 0x3100 +#define CSI2_CMD_RCGR 0x3160 +#define CSI3_CMD_RCGR 0x31C0 +#define CCI_CMD_RCGR 0x3300 +#define MCLK0_CMD_RCGR 0x3360 +#define MCLK1_CMD_RCGR 0x3390 +#define MCLK2_CMD_RCGR 0x33C0 +#define MCLK3_CMD_RCGR 0x33F0 +#define MMSS_GP0_CMD_RCGR 0x3420 +#define MMSS_GP1_CMD_RCGR 0x3450 +#define JPEG0_CMD_RCGR 0x3500 +#define JPEG1_CMD_RCGR 0x3520 +#define JPEG2_CMD_RCGR 0x3540 +#define VFE0_CMD_RCGR 0x3600 +#define VFE1_CMD_RCGR 0x3620 +#define CPP_CMD_RCGR 0x3640 +#define GFX3D_CMD_RCGR 0x4000 +#define RBCPR_CMD_RCGR 0x4060 +#define AHB_CMD_RCGR 0x5000 +#define AXI_CMD_RCGR 0x5040 +#define OCMEMNOC_CMD_RCGR 0x5090 + +#define MMSS_BCR 0x0240 +#define USB_30_BCR 0x03C0 +#define USB3_PHY_BCR 0x03FC +#define USB_HS_HSIC_BCR 0x0400 +#define USB_HS_BCR 0x0480 +#define SDCC1_BCR 0x04C0 +#define SDCC2_BCR 0x0500 +#define SDCC3_BCR 0x0540 +#define SDCC4_BCR 0x0580 +#define BLSP1_BCR 0x05C0 +#define BLSP1_QUP1_BCR 0x0640 +#define BLSP1_UART1_BCR 0x0680 +#define BLSP1_QUP2_BCR 0x06C0 +#define BLSP1_UART2_BCR 0x0700 +#define BLSP1_QUP3_BCR 0x0740 +#define BLSP1_UART3_BCR 0x0780 +#define BLSP1_QUP4_BCR 0x07C0 +#define BLSP1_UART4_BCR 0x0800 +#define BLSP1_QUP5_BCR 0x0840 +#define BLSP1_UART5_BCR 0x0880 +#define BLSP1_QUP6_BCR 0x08C0 +#define BLSP1_UART6_BCR 0x0900 +#define BLSP2_BCR 0x0940 +#define BLSP2_QUP1_BCR 0x0980 +#define BLSP2_UART1_BCR 0x09C0 +#define BLSP2_QUP2_BCR 0x0A00 +#define BLSP2_UART2_BCR 0x0A40 +#define BLSP2_QUP3_BCR 0x0A80 +#define BLSP2_UART3_BCR 0x0AC0 +#define BLSP2_QUP4_BCR 0x0B00 +#define BLSP2_UART4_BCR 0x0B40 +#define BLSP2_QUP5_BCR 0x0B80 +#define BLSP2_UART5_BCR 0x0BC0 +#define BLSP2_QUP6_BCR 0x0C00 +#define BLSP2_UART6_BCR 0x0C40 +#define BOOT_ROM_BCR 0x0E00 +#define PDM_BCR 0x0CC0 +#define PRNG_BCR 0x0D00 +#define BAM_DMA_BCR 0x0D40 +#define TSIF_BCR 0x0D80 +#define CE1_BCR 0x1040 +#define CE2_BCR 0x1080 +#define AUDIO_CORE_BCR 0x4000 +#define VENUS0_BCR 0x1020 +#define MDSS_BCR 0x2300 +#define CAMSS_PHY0_BCR 0x3020 +#define CAMSS_PHY1_BCR 0x3050 +#define CAMSS_PHY2_BCR 0x3080 +#define CAMSS_CSI0_BCR 0x30B0 +#define CAMSS_CSI0PHY_BCR 0x30C0 +#define CAMSS_CSI0RDI_BCR 0x30D0 +#define CAMSS_CSI0PIX_BCR 0x30E0 +#define CAMSS_CSI1_BCR 0x3120 +#define CAMSS_CSI1PHY_BCR 0x3130 +#define CAMSS_CSI1RDI_BCR 0x3140 +#define CAMSS_CSI1PIX_BCR 0x3150 +#define CAMSS_CSI2_BCR 0x3180 +#define CAMSS_CSI2PHY_BCR 0x3190 +#define CAMSS_CSI2RDI_BCR 0x31A0 +#define CAMSS_CSI2PIX_BCR 0x31B0 +#define CAMSS_CSI3_BCR 0x31E0 +#define CAMSS_CSI3PHY_BCR 0x31F0 +#define CAMSS_CSI3RDI_BCR 0x3200 +#define CAMSS_CSI3PIX_BCR 0x3210 +#define CAMSS_ISPIF_BCR 0x3220 +#define CAMSS_CCI_BCR 0x3340 +#define CAMSS_MCLK0_BCR 0x3380 +#define CAMSS_MCLK1_BCR 0x33B0 +#define CAMSS_MCLK2_BCR 0x33E0 +#define CAMSS_MCLK3_BCR 0x3410 +#define CAMSS_GP0_BCR 0x3440 +#define CAMSS_GP1_BCR 0x3470 +#define CAMSS_TOP_BCR 0x3480 +#define CAMSS_MICRO_BCR 0x3490 +#define CAMSS_JPEG_BCR 0x35A0 +#define CAMSS_VFE_BCR 0x36A0 +#define CAMSS_CSI_VFE0_BCR 0x3700 +#define CAMSS_CSI_VFE1_BCR 0x3710 +#define OCMEMNOC_BCR 0x50B0 +#define MMSSNOCAHB_BCR 0x5020 +#define MMSSNOCAXI_BCR 0x5060 +#define OXILI_GFX3D_CBCR 0x4028 +#define OXILICX_AHB_CBCR 0x403C +#define OXILICX_AXI_CBCR 0x4038 +#define OXILI_BCR 0x4020 +#define OXILICX_BCR 0x4030 +#define LPASS_Q6SS_BCR 0x6000 +#define MSS_Q6SS_BCR 0x1068 + +#define OCMEM_SYS_NOC_AXI_CBCR 0x0244 +#define OCMEM_NOC_CFG_AHB_CBCR 0x0248 +#define MMSS_NOC_CFG_AHB_CBCR 0x024C + +#define USB30_MASTER_CBCR 0x03C8 +#define USB30_MOCK_UTMI_CBCR 0x03D0 +#define USB_HSIC_AHB_CBCR 0x0408 +#define USB_HSIC_SYSTEM_CBCR 0x040C +#define USB_HSIC_CBCR 0x0410 +#define USB_HSIC_IO_CAL_CBCR 0x0414 +#define USB_HS_SYSTEM_CBCR 0x0484 +#define USB_HS_AHB_CBCR 0x0488 +#define SDCC1_APPS_CBCR 0x04C4 +#define SDCC1_AHB_CBCR 0x04C8 +#define SDCC2_APPS_CBCR 0x0504 +#define SDCC2_AHB_CBCR 0x0508 +#define SDCC3_APPS_CBCR 0x0544 +#define SDCC3_AHB_CBCR 0x0548 +#define SDCC4_APPS_CBCR 0x0584 +#define SDCC4_AHB_CBCR 0x0588 +#define BLSP1_AHB_CBCR 0x05C4 +#define BLSP1_QUP1_SPI_APPS_CBCR 0x0644 +#define BLSP1_QUP1_I2C_APPS_CBCR 0x0648 +#define BLSP1_UART1_APPS_CBCR 0x0684 +#define BLSP1_UART1_SIM_CBCR 0x0688 +#define BLSP1_QUP2_SPI_APPS_CBCR 0x06C4 +#define BLSP1_QUP2_I2C_APPS_CBCR 0x06C8 +#define BLSP1_UART2_APPS_CBCR 0x0704 +#define BLSP1_UART2_SIM_CBCR 0x0708 +#define BLSP1_QUP3_SPI_APPS_CBCR 0x0744 +#define BLSP1_QUP3_I2C_APPS_CBCR 0x0748 +#define BLSP1_UART3_APPS_CBCR 0x0784 +#define BLSP1_UART3_SIM_CBCR 0x0788 +#define BLSP1_QUP4_SPI_APPS_CBCR 0x07C4 +#define BLSP1_QUP4_I2C_APPS_CBCR 0x07C8 +#define BLSP1_UART4_APPS_CBCR 0x0804 +#define BLSP1_UART4_SIM_CBCR 0x0808 +#define BLSP1_QUP5_SPI_APPS_CBCR 0x0844 +#define BLSP1_QUP5_I2C_APPS_CBCR 0x0848 +#define BLSP1_UART5_APPS_CBCR 0x0884 +#define BLSP1_UART5_SIM_CBCR 0x0888 +#define BLSP1_QUP6_SPI_APPS_CBCR 0x08C4 +#define BLSP1_QUP6_I2C_APPS_CBCR 0x08C8 +#define BLSP1_UART6_APPS_CBCR 0x0904 +#define BLSP1_UART6_SIM_CBCR 0x0908 +#define BLSP2_AHB_CBCR 0x0944 +#define BOOT_ROM_AHB_CBCR 0x0E04 +#define BLSP2_QUP1_SPI_APPS_CBCR 0x0984 +#define BLSP2_QUP1_I2C_APPS_CBCR 0x0988 +#define BLSP2_UART1_APPS_CBCR 0x09C4 +#define BLSP2_UART1_SIM_CBCR 0x09C8 +#define BLSP2_QUP2_SPI_APPS_CBCR 0x0A04 +#define BLSP2_QUP2_I2C_APPS_CBCR 0x0A08 +#define BLSP2_UART2_APPS_CBCR 0x0A44 +#define BLSP2_UART2_SIM_CBCR 0x0A48 +#define BLSP2_QUP3_SPI_APPS_CBCR 0x0A84 +#define BLSP2_QUP3_I2C_APPS_CBCR 0x0A88 +#define BLSP2_UART3_APPS_CBCR 0x0AC4 +#define BLSP2_UART3_SIM_CBCR 0x0AC8 +#define BLSP2_QUP4_SPI_APPS_CBCR 0x0B04 +#define BLSP2_QUP4_I2C_APPS_CBCR 0x0B08 +#define BLSP2_UART4_APPS_CBCR 0x0B44 +#define BLSP2_UART4_SIM_CBCR 0x0B48 +#define BLSP2_QUP5_SPI_APPS_CBCR 0x0B84 +#define BLSP2_QUP5_I2C_APPS_CBCR 0x0B88 +#define BLSP2_UART5_APPS_CBCR 0x0BC4 +#define BLSP2_UART5_SIM_CBCR 0x0BC8 +#define BLSP2_QUP6_SPI_APPS_CBCR 0x0C04 +#define BLSP2_QUP6_I2C_APPS_CBCR 0x0C08 +#define BLSP2_UART6_APPS_CBCR 0x0C44 +#define BLSP2_UART6_SIM_CBCR 0x0C48 +#define PDM_AHB_CBCR 0x0CC4 +#define PDM_XO4_CBCR 0x0CC8 +#define PDM2_CBCR 0x0CCC +#define PRNG_AHB_CBCR 0x0D04 +#define BAM_DMA_AHB_CBCR 0x0D44 +#define TSIF_AHB_CBCR 0x0D84 +#define TSIF_REF_CBCR 0x0D88 +#define MSG_RAM_AHB_CBCR 0x0E44 +#define CE1_CBCR 0x1044 +#define CE1_AXI_CBCR 0x1048 +#define CE1_AHB_CBCR 0x104C +#define CE2_CBCR 0x1084 +#define CE2_AXI_CBCR 0x1088 +#define CE2_AHB_CBCR 0x108C +#define GCC_AHB_CBCR 0x10C0 +#define GP1_CBCR 0x1900 +#define GP2_CBCR 0x1940 +#define GP3_CBCR 0x1980 +#define AUDIO_CORE_LPAIF_CODEC_SPKR_OSR_CBCR 0xA014 +#define AUDIO_CORE_LPAIF_CODEC_SPKR_IBIT_CBCR 0xA018 +#define AUDIO_CORE_LPAIF_CODEC_SPKR_EBIT_CBCR 0xA01C +#define AUDIO_CORE_LPAIF_PRI_OSR_CBCR 0xB014 +#define AUDIO_CORE_LPAIF_PRI_IBIT_CBCR 0xB018 +#define AUDIO_CORE_LPAIF_PRI_EBIT_CBCR 0xB01C +#define AUDIO_CORE_LPAIF_SEC_OSR_CBCR 0xC014 +#define AUDIO_CORE_LPAIF_SEC_IBIT_CBCR 0xC018 +#define AUDIO_CORE_LPAIF_SEC_EBIT_CBCR 0xC01C +#define AUDIO_CORE_LPAIF_TER_OSR_CBCR 0xD014 +#define AUDIO_CORE_LPAIF_TER_IBIT_CBCR 0xD018 +#define AUDIO_CORE_LPAIF_TER_EBIT_CBCR 0xD01C +#define AUDIO_CORE_LPAIF_QUAD_OSR_CBCR 0xE014 +#define AUDIO_CORE_LPAIF_QUAD_IBIT_CBCR 0xE018 +#define AUDIO_CORE_LPAIF_QUAD_EBIT_CBCR 0xE01C +#define AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR 0xF014 +#define AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR 0xF018 +#define AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR 0x10014 +#define AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR 0x10018 +#define AUDIO_CORE_RESAMPLER_CORE_CBCR 0x11014 +#define AUDIO_CORE_RESAMPLER_LFABIF_CBCR 0x11018 +#define AUDIO_CORE_SLIMBUS_CORE_CBCR 0x12014 +#define AUDIO_CORE_SLIMBUS_LFABIF_CBCR 0x12018 +#define AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR 0x13014 +#define VENUS0_VCODEC0_CBCR 0x1028 +#define VENUS0_AHB_CBCR 0x1030 +#define VENUS0_AXI_CBCR 0x1034 +#define VENUS0_OCMEMNOC_CBCR 0x1038 +#define MDSS_AHB_CBCR 0x2308 +#define MDSS_HDMI_AHB_CBCR 0x230C +#define MDSS_AXI_CBCR 0x2310 +#define MDSS_PCLK0_CBCR 0x2314 +#define MDSS_PCLK1_CBCR 0x2318 +#define MDSS_MDP_CBCR 0x231C +#define MDSS_MDP_LUT_CBCR 0x2320 +#define MDSS_EXTPCLK_CBCR 0x2324 +#define MDSS_VSYNC_CBCR 0x2328 +#define MDSS_EDPPIXEL_CBCR 0x232C +#define MDSS_EDPLINK_CBCR 0x2330 +#define MDSS_EDPAUX_CBCR 0x2334 +#define MDSS_HDMI_CBCR 0x2338 +#define MDSS_BYTE0_CBCR 0x233C +#define MDSS_BYTE1_CBCR 0x2340 +#define MDSS_ESC0_CBCR 0x2344 +#define MDSS_ESC1_CBCR 0x2348 +#define CAMSS_PHY0_CSI0PHYTIMER_CBCR 0x3024 +#define CAMSS_PHY1_CSI1PHYTIMER_CBCR 0x3054 +#define CAMSS_PHY2_CSI2PHYTIMER_CBCR 0x3084 +#define CAMSS_CSI0_CBCR 0x30B4 +#define CAMSS_CSI0_AHB_CBCR 0x30BC +#define CAMSS_CSI0PHY_CBCR 0x30C4 +#define CAMSS_CSI0RDI_CBCR 0x30D4 +#define CAMSS_CSI0PIX_CBCR 0x30E4 +#define CAMSS_CSI1_CBCR 0x3124 +#define CAMSS_CSI1_AHB_CBCR 0x3128 +#define CAMSS_CSI1PHY_CBCR 0x3134 +#define CAMSS_CSI1RDI_CBCR 0x3144 +#define CAMSS_CSI1PIX_CBCR 0x3154 +#define CAMSS_CSI2_CBCR 0x3184 +#define CAMSS_CSI2_AHB_CBCR 0x3188 +#define CAMSS_CSI2PHY_CBCR 0x3194 +#define CAMSS_CSI2RDI_CBCR 0x31A4 +#define CAMSS_CSI2PIX_CBCR 0x31B4 +#define CAMSS_CSI3_CBCR 0x31E4 +#define CAMSS_CSI3_AHB_CBCR 0x31E8 +#define CAMSS_CSI3PHY_CBCR 0x31F4 +#define CAMSS_CSI3RDI_CBCR 0x3204 +#define CAMSS_CSI3PIX_CBCR 0x3214 +#define CAMSS_ISPIF_AHB_CBCR 0x3224 +#define CAMSS_CCI_CCI_CBCR 0x3344 +#define CAMSS_CCI_CCI_AHB_CBCR 0x3348 +#define CAMSS_MCLK0_CBCR 0x3384 +#define CAMSS_MCLK1_CBCR 0x33B4 +#define CAMSS_MCLK2_CBCR 0x33E4 +#define CAMSS_MCLK3_CBCR 0x3414 +#define CAMSS_GP0_CBCR 0x3444 +#define CAMSS_GP1_CBCR 0x3474 +#define CAMSS_TOP_AHB_CBCR 0x3484 +#define CAMSS_MICRO_AHB_CBCR 0x3494 +#define CAMSS_JPEG_JPEG0_CBCR 0x35A8 +#define CAMSS_JPEG_JPEG1_CBCR 0x35AC +#define CAMSS_JPEG_JPEG2_CBCR 0x35B0 +#define CAMSS_JPEG_JPEG_AHB_CBCR 0x35B4 +#define CAMSS_JPEG_JPEG_AXI_CBCR 0x35B8 +#define CAMSS_JPEG_JPEG_OCMEMNOC_CBCR 0x35BC +#define CAMSS_VFE_VFE0_CBCR 0x36A8 +#define CAMSS_VFE_VFE1_CBCR 0x36AC +#define CAMSS_VFE_CPP_CBCR 0x36B0 +#define CAMSS_VFE_CPP_AHB_CBCR 0x36B4 +#define CAMSS_VFE_VFE_AHB_CBCR 0x36B8 +#define CAMSS_VFE_VFE_AXI_CBCR 0x36BC +#define CAMSS_VFE_VFE_OCMEMNOC_CBCR 0x36C0 +#define CAMSS_CSI_VFE0_CBCR 0x3704 +#define CAMSS_CSI_VFE1_CBCR 0x3714 +#define MMSS_MMSSNOC_AXI_CBCR 0x506C +#define MMSS_MMSSNOC_AHB_CBCR 0x5024 +#define MMSS_MMSSNOC_BTO_AHB_CBCR 0x5028 +#define MMSS_MISC_AHB_CBCR 0x502C +#define MMSS_S0_AXI_CBCR 0x5064 +#define OCMEMNOC_CBCR 0x50B4 +#define LPASS_Q6SS_AHB_LFABIF_CBCR 0x22000 +#define LPASS_Q6SS_XO_CBCR 0x26000 +#define MSS_XO_Q6_CBCR 0x108C +#define MSS_BUS_Q6_CBCR 0x10A4 +#define MSS_CFG_AHB_CBCR 0x0280 + +#define APCS_CLOCK_BRANCH_ENA_VOTE 0x1484 +#define APCS_CLOCK_SLEEP_ENA_VOTE 0x1488 + +/* Mux source select values */ +#define cxo_source_val 0 +#define gpll0_source_val 1 +#define gpll1_source_val 2 +#define gnd_source_val 5 +#define mmpll0_mm_source_val 1 +#define mmpll1_mm_source_val 2 +#define mmpll3_mm_source_val 3 +#define gpll0_mm_source_val 5 +#define cxo_mm_source_val 0 +#define mm_gnd_source_val 6 +#define gpll1_hsic_source_val 4 +#define cxo_lpass_source_val 0 +#define lpapll0_lpass_source_val 1 +#define gpll0_lpass_source_val 5 +#define edppll_270_mm_source_val 4 +#define edppll_350_mm_source_val 4 +#define dsipll_750_mm_source_val 1 +#define dsipll_250_mm_source_val 2 +#define hdmipll_297_mm_source_val 3 + +#define F(f, s, div, m, n) \ + { \ + .freq_hz = (f), \ + .src_clk = &s##_clk_src.c, \ + .m_val = (m), \ + .n_val = ~((n)-(m)), \ + .d_val = ~(n),\ + .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \ + | BVAL(10, 8, s##_source_val), \ + } + +#define F_MM(f, s, div, m, n) \ + { \ + .freq_hz = (f), \ + .src_clk = &s##_clk_src.c, \ + .m_val = (m), \ + .n_val = ~((n)-(m)), \ + .d_val = ~(n),\ + .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \ + | BVAL(10, 8, s##_mm_source_val), \ + } + +#define F_MDSS(f, s, div, m, n) \ + { \ + .freq_hz = (f), \ + .m_val = (m), \ + .n_val = ~((n)-(m)), \ + .d_val = ~(n),\ + .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \ + | BVAL(10, 8, s##_mm_source_val), \ + } + +#define F_HSIC(f, s, div, m, n) \ + { \ + .freq_hz = (f), \ + .src_clk = &s##_clk_src.c, \ + .m_val = (m), \ + .n_val = ~((n)-(m)), \ + .d_val = ~(n),\ + .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \ + | BVAL(10, 8, s##_hsic_source_val), \ + } + +#define F_LPASS(f, s, div, m, n) \ + { \ + .freq_hz = (f), \ + .src_clk = &s##_clk_src.c, \ + .m_val = (m), \ + .n_val = ~((n)-(m)), \ + .d_val = ~(n),\ + .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \ + | BVAL(10, 8, s##_lpass_source_val), \ + } + +#define VDD_DIG_FMAX_MAP1(l1, f1) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1) +#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2) +#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \ + .vdd_class = &vdd_dig, \ + .fmax[VDD_DIG_##l1] = (f1), \ + .fmax[VDD_DIG_##l2] = (f2), \ + .fmax[VDD_DIG_##l3] = (f3) + +enum vdd_dig_levels { + VDD_DIG_NONE, + VDD_DIG_LOW, + VDD_DIG_NOMINAL, + VDD_DIG_HIGH +}; + +static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level) +{ + /* TODO: Actually call into regulator APIs to set VDD_DIG here. */ + return 0; +} + +static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig); + +static int cxo_clk_enable(struct clk *clk) +{ + /* TODO: Remove from here once the rpm xo clock is ready. */ + return 0; +} + +static void cxo_clk_disable(struct clk *clk) +{ + /* TODO: Remove from here once the rpm xo clock is ready. */ + return; +} + +static enum handoff cxo_clk_handoff(struct clk *clk) +{ + /* TODO: Remove from here once the rpm xo clock is ready. */ + return HANDOFF_ENABLED_CLK; +} + +static struct clk_ops clk_ops_cxo = { + .enable = cxo_clk_enable, + .disable = cxo_clk_disable, + .handoff = cxo_clk_handoff, +}; + +static struct fixed_clk cxo_clk_src = { + .c = { + .rate = 19200000, + .dbg_name = "cxo_clk_src", + .ops = &clk_ops_cxo, + .warned = true, + CLK_INIT(cxo_clk_src.c), + }, +}; + +static struct pll_vote_clk gpll0_clk_src = { + .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG, + .en_mask = BIT(0), + .status_reg = (void __iomem *)GPLL0_STATUS_REG, + .status_mask = BIT(17), + .parent = &cxo_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .rate = 600000000, + .dbg_name = "gpll0_clk_src", + .ops = &clk_ops_pll_vote, + .warned = true, + CLK_INIT(gpll0_clk_src.c), + }, +}; + +static struct pll_vote_clk gpll1_clk_src = { + .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG, + .en_mask = BIT(1), + .status_reg = (void __iomem *)GPLL1_STATUS_REG, + .status_mask = BIT(17), + .parent = &cxo_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .rate = 480000000, + .dbg_name = "gpll1_clk_src", + .ops = &clk_ops_pll_vote, + .warned = true, + CLK_INIT(gpll1_clk_src.c), + }, +}; + +static struct pll_vote_clk lpapll0_clk_src = { + .en_reg = (void __iomem *)LPASS_LPA_PLL_VOTE_APPS_REG, + .en_mask = BIT(0), + .status_reg = (void __iomem *)LPAPLL_STATUS_REG, + .status_mask = BIT(17), + .parent = &cxo_clk_src.c, + .base = &virt_bases[LPASS_BASE], + .c = { + .rate = 491520000, + .dbg_name = "lpapll0_clk_src", + .ops = &clk_ops_pll_vote, + .warned = true, + CLK_INIT(lpapll0_clk_src.c), + }, +}; + +static struct pll_vote_clk mmpll0_clk_src = { + .en_reg = (void __iomem *)MMSS_PLL_VOTE_APCS_REG, + .en_mask = BIT(0), + .status_reg = (void __iomem *)MMPLL0_STATUS_REG, + .status_mask = BIT(17), + .parent = &cxo_clk_src.c, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmpll0_clk_src", + .rate = 800000000, + .ops = &clk_ops_pll_vote, + .warned = true, + CLK_INIT(mmpll0_clk_src.c), + }, +}; + +static struct pll_vote_clk mmpll1_clk_src = { + .en_reg = (void __iomem *)MMSS_PLL_VOTE_APCS_REG, + .en_mask = BIT(1), + .status_reg = (void __iomem *)MMPLL1_STATUS_REG, + .status_mask = BIT(17), + .parent = &cxo_clk_src.c, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmpll1_clk_src", + .rate = 1000000000, + .ops = &clk_ops_pll_vote, + .warned = true, + CLK_INIT(mmpll1_clk_src.c), + }, +}; + +static struct pll_clk mmpll3_clk_src = { + .mode_reg = (void __iomem *)MMPLL3_MODE_REG, + .status_reg = (void __iomem *)MMPLL3_STATUS_REG, + .parent = &cxo_clk_src.c, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmpll3_clk_src", + .rate = 1000000000, + .ops = &clk_ops_local_pll, + CLK_INIT(mmpll3_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = { + F(125000000, gpll0, 1, 5, 24), + F_END +}; + +static struct rcg_clk usb30_master_clk_src = { + .cmd_rcgr_reg = USB30_MASTER_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_usb30_master_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "usb30_master_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP1(NOMINAL, 125000000), + CLK_INIT(usb30_master_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk[] = { + F( 960000, cxo, 10, 1, 2), + F( 4800000, cxo, 4, 0, 0), + F( 9600000, cxo, 2, 0, 0), + F(15000000, gpll0, 10, 1, 4), + F(19200000, cxo, 1, 0, 0), + F(25000000, gpll0, 12, 1, 2), + F(50000000, gpll0, 12, 0, 0), + F_END +}; + +static struct rcg_clk blsp1_qup1_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_QUP1_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_qup1_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp1_qup1_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_qup2_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_QUP2_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_qup2_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp1_qup2_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_qup3_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_QUP3_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_qup3_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp1_qup3_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_qup4_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_QUP4_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_qup4_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp1_qup4_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_qup5_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_QUP5_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_qup5_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp1_qup5_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_qup6_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_QUP6_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_qup6_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp1_qup6_spi_apps_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_blsp1_2_uart1_6_apps_clk[] = { + F( 3686400, gpll0, 1, 96, 15625), + F( 7372800, gpll0, 1, 192, 15625), + F(14745600, gpll0, 1, 384, 15625), + F(16000000, gpll0, 5, 2, 15), + F(19200000, cxo, 1, 0, 0), + F(24000000, gpll0, 5, 1, 5), + F(32000000, gpll0, 1, 4, 75), + F(40000000, gpll0, 15, 0, 0), + F(46400000, gpll0, 1, 29, 375), + F(48000000, gpll0, 12.5, 0, 0), + F(51200000, gpll0, 1, 32, 375), + F(56000000, gpll0, 1, 7, 75), + F(58982400, gpll0, 1, 1536, 15625), + F(60000000, gpll0, 10, 0, 0), + F_END +}; + +static struct rcg_clk blsp1_uart1_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_UART1_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_uart1_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp1_uart1_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_uart2_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_UART2_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_uart2_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp1_uart2_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_uart3_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_UART3_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_uart3_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp1_uart3_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_uart4_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_UART4_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_uart4_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp1_uart4_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_uart5_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_UART5_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_uart5_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp1_uart5_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp1_uart6_apps_clk_src = { + .cmd_rcgr_reg = BLSP1_UART6_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp1_uart6_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp1_uart6_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_qup1_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_QUP1_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_qup1_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp2_qup1_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_qup2_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_QUP2_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_qup2_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp2_qup2_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_qup3_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_QUP3_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_qup3_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp2_qup3_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_qup4_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_QUP4_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_qup4_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp2_qup4_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_qup5_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_QUP5_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_qup5_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp2_qup5_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_qup6_spi_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_QUP6_SPI_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_qup6_spi_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000), + CLK_INIT(blsp2_qup6_spi_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_uart1_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_UART1_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_uart1_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp2_uart1_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_uart2_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_UART2_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_uart2_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp2_uart2_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_uart3_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_UART3_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_uart3_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp2_uart3_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_uart4_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_UART4_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_uart4_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp2_uart4_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_uart5_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_UART5_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_uart5_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp2_uart5_apps_clk_src.c), + }, +}; + +static struct rcg_clk blsp2_uart6_apps_clk_src = { + .cmd_rcgr_reg = BLSP2_UART6_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "blsp2_uart6_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000), + CLK_INIT(blsp2_uart6_apps_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_ce1_clk[] = { + F( 50000000, gpll0, 12, 0, 0), + F(100000000, gpll0, 6, 0, 0), + F_END +}; + +static struct rcg_clk ce1_clk_src = { + .cmd_rcgr_reg = CE1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_ce1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "ce1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000), + CLK_INIT(ce1_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_ce2_clk[] = { + F( 50000000, gpll0, 12, 0, 0), + F(100000000, gpll0, 6, 0, 0), + F_END +}; + +static struct rcg_clk ce2_clk_src = { + .cmd_rcgr_reg = CE2_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_ce2_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "ce2_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000), + CLK_INIT(ce2_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_gp_clk[] = { + F(19200000, cxo, 1, 0, 0), + F_END +}; + +static struct rcg_clk gp1_clk_src = { + .cmd_rcgr_reg = GP1_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_gp_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gp1_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(gp1_clk_src.c), + }, +}; + +static struct rcg_clk gp2_clk_src = { + .cmd_rcgr_reg = GP2_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_gp_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gp2_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(gp2_clk_src.c), + }, +}; + +static struct rcg_clk gp3_clk_src = { + .cmd_rcgr_reg = GP3_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_gp_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gp3_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(gp3_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_pdm2_clk[] = { + F(60000000, gpll0, 10, 0, 0), + F_END +}; + +static struct rcg_clk pdm2_clk_src = { + .cmd_rcgr_reg = PDM2_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_pdm2_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "pdm2_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 60000000), + CLK_INIT(pdm2_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_sdcc1_2_apps_clk[] = { + F( 144000, cxo, 16, 3, 25), + F( 400000, cxo, 12, 1, 4), + F( 20000000, gpll0, 15, 1, 2), + F( 25000000, gpll0, 12, 1, 2), + F( 50000000, gpll0, 12, 0, 0), + F(100000000, gpll0, 6, 0, 0), + F(200000000, gpll0, 3, 0, 0), + F_END +}; + +static struct clk_freq_tbl ftbl_gcc_sdcc3_4_apps_clk[] = { + F( 144000, cxo, 16, 3, 25), + F( 400000, cxo, 12, 1, 4), + F( 20000000, gpll0, 15, 1, 2), + F( 25000000, gpll0, 12, 1, 2), + F( 50000000, gpll0, 12, 0, 0), + F(100000000, gpll0, 6, 0, 0), + F_END +}; + +static struct rcg_clk sdcc1_apps_clk_src = { + .cmd_rcgr_reg = SDCC1_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_sdcc1_2_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "sdcc1_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(sdcc1_apps_clk_src.c), + }, +}; + +static struct rcg_clk sdcc2_apps_clk_src = { + .cmd_rcgr_reg = SDCC2_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_sdcc1_2_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "sdcc2_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(sdcc2_apps_clk_src.c), + }, +}; + +static struct rcg_clk sdcc3_apps_clk_src = { + .cmd_rcgr_reg = SDCC3_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_sdcc3_4_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "sdcc3_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000), + CLK_INIT(sdcc3_apps_clk_src.c), + }, +}; + +static struct rcg_clk sdcc4_apps_clk_src = { + .cmd_rcgr_reg = SDCC4_APPS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_sdcc3_4_apps_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "sdcc4_apps_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000), + CLK_INIT(sdcc4_apps_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_tsif_ref_clk[] = { + F(105000, cxo, 2, 1, 91), + F_END +}; + +static struct rcg_clk tsif_ref_clk_src = { + .cmd_rcgr_reg = TSIF_REF_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_gcc_tsif_ref_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "tsif_ref_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP1(LOW, 105500), + CLK_INIT(tsif_ref_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_usb30_mock_utmi_clk[] = { + F(60000000, gpll0, 10, 0, 0), + F_END +}; + +static struct rcg_clk usb30_mock_utmi_clk_src = { + .cmd_rcgr_reg = USB30_MOCK_UTMI_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_usb30_mock_utmi_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "usb30_mock_utmi_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(NOMINAL, 60000000), + CLK_INIT(usb30_mock_utmi_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_usb_hs_system_clk[] = { + F(75000000, gpll0, 8, 0, 0), + F_END +}; + +static struct rcg_clk usb_hs_system_clk_src = { + .cmd_rcgr_reg = USB_HS_SYSTEM_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_usb_hs_system_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "usb_hs_system_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 37500000, NOMINAL, 75000000), + CLK_INIT(usb_hs_system_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_usb_hsic_clk[] = { + F_HSIC(480000000, gpll1, 1, 0, 0), + F_END +}; + +static struct rcg_clk usb_hsic_clk_src = { + .cmd_rcgr_reg = USB_HSIC_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_usb_hsic_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "usb_hsic_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 480000000), + CLK_INIT(usb_hsic_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_usb_hsic_io_cal_clk[] = { + F(9600000, cxo, 2, 0, 0), + F_END +}; + +static struct rcg_clk usb_hsic_io_cal_clk_src = { + .cmd_rcgr_reg = USB_HSIC_IO_CAL_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_usb_hsic_io_cal_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "usb_hsic_io_cal_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 9600000), + CLK_INIT(usb_hsic_io_cal_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_gcc_usb_hsic_system_clk[] = { + F(75000000, gpll0, 8, 0, 0), + F_END +}; + +static struct rcg_clk usb_hsic_system_clk_src = { + .cmd_rcgr_reg = USB_HSIC_SYSTEM_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_gcc_usb_hsic_system_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "usb_hsic_system_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 37500000, NOMINAL, 75000000), + CLK_INIT(usb_hsic_system_clk_src.c), + }, +}; + +static struct local_vote_clk gcc_bam_dma_ahb_clk = { + .cbcr_reg = BAM_DMA_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(12), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_bam_dma_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_bam_dma_ahb_clk.c), + }, +}; + +static struct local_vote_clk gcc_blsp1_ahb_clk = { + .cbcr_reg = BLSP1_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(17), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_blsp1_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup1_i2c_apps_clk = { + .cbcr_reg = BLSP1_QUP1_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup1_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup1_spi_apps_clk = { + .cbcr_reg = BLSP1_QUP1_SPI_APPS_CBCR, + .parent = &blsp1_qup1_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup1_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup2_i2c_apps_clk = { + .cbcr_reg = BLSP1_QUP2_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup2_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup2_spi_apps_clk = { + .cbcr_reg = BLSP1_QUP2_SPI_APPS_CBCR, + .parent = &blsp1_qup2_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup2_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = { + .cbcr_reg = BLSP1_QUP3_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup3_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup3_spi_apps_clk = { + .cbcr_reg = BLSP1_QUP3_SPI_APPS_CBCR, + .parent = &blsp1_qup3_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup3_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = { + .cbcr_reg = BLSP1_QUP4_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup4_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = { + .cbcr_reg = BLSP1_QUP4_SPI_APPS_CBCR, + .parent = &blsp1_qup4_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup4_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup5_i2c_apps_clk = { + .cbcr_reg = BLSP1_QUP5_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup5_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup5_spi_apps_clk = { + .cbcr_reg = BLSP1_QUP5_SPI_APPS_CBCR, + .parent = &blsp1_qup5_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup5_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup6_i2c_apps_clk = { + .cbcr_reg = BLSP1_QUP6_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup6_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_qup6_spi_apps_clk = { + .cbcr_reg = BLSP1_QUP6_SPI_APPS_CBCR, + .parent = &blsp1_qup6_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_qup6_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_uart1_apps_clk = { + .cbcr_reg = BLSP1_UART1_APPS_CBCR, + .parent = &blsp1_uart1_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_uart1_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_uart1_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_uart2_apps_clk = { + .cbcr_reg = BLSP1_UART2_APPS_CBCR, + .parent = &blsp1_uart2_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_uart2_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_uart2_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_uart3_apps_clk = { + .cbcr_reg = BLSP1_UART3_APPS_CBCR, + .parent = &blsp1_uart3_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_uart3_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_uart3_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_uart4_apps_clk = { + .cbcr_reg = BLSP1_UART4_APPS_CBCR, + .parent = &blsp1_uart4_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_uart4_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_uart4_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_uart5_apps_clk = { + .cbcr_reg = BLSP1_UART5_APPS_CBCR, + .parent = &blsp1_uart5_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_uart5_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_uart5_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp1_uart6_apps_clk = { + .cbcr_reg = BLSP1_UART6_APPS_CBCR, + .parent = &blsp1_uart6_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp1_uart6_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp1_uart6_apps_clk.c), + }, +}; + +static struct local_vote_clk gcc_boot_rom_ahb_clk = { + .cbcr_reg = BOOT_ROM_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(10), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_boot_rom_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_boot_rom_ahb_clk.c), + }, +}; + +static struct local_vote_clk gcc_blsp2_ahb_clk = { + .cbcr_reg = BLSP2_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(15), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_blsp2_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup1_i2c_apps_clk = { + .cbcr_reg = BLSP2_QUP1_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup1_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup1_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup1_spi_apps_clk = { + .cbcr_reg = BLSP2_QUP1_SPI_APPS_CBCR, + .parent = &blsp2_qup1_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup1_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup1_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup2_i2c_apps_clk = { + .cbcr_reg = BLSP2_QUP2_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup2_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup2_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup2_spi_apps_clk = { + .cbcr_reg = BLSP2_QUP2_SPI_APPS_CBCR, + .parent = &blsp2_qup2_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup2_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup2_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup3_i2c_apps_clk = { + .cbcr_reg = BLSP2_QUP3_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup3_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup3_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup3_spi_apps_clk = { + .cbcr_reg = BLSP2_QUP3_SPI_APPS_CBCR, + .parent = &blsp2_qup3_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup3_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup3_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup4_i2c_apps_clk = { + .cbcr_reg = BLSP2_QUP4_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup4_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup4_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup4_spi_apps_clk = { + .cbcr_reg = BLSP2_QUP4_SPI_APPS_CBCR, + .parent = &blsp2_qup4_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup4_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup4_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup5_i2c_apps_clk = { + .cbcr_reg = BLSP2_QUP5_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup5_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup5_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup5_spi_apps_clk = { + .cbcr_reg = BLSP2_QUP5_SPI_APPS_CBCR, + .parent = &blsp2_qup5_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup5_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup5_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup6_i2c_apps_clk = { + .cbcr_reg = BLSP2_QUP6_I2C_APPS_CBCR, + .parent = &cxo_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup6_i2c_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup6_i2c_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_qup6_spi_apps_clk = { + .cbcr_reg = BLSP2_QUP6_SPI_APPS_CBCR, + .parent = &blsp2_qup6_spi_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_qup6_spi_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_qup6_spi_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_uart1_apps_clk = { + .cbcr_reg = BLSP2_UART1_APPS_CBCR, + .parent = &blsp2_uart1_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_uart1_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_uart1_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_uart2_apps_clk = { + .cbcr_reg = BLSP2_UART2_APPS_CBCR, + .parent = &blsp2_uart2_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_uart2_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_uart2_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_uart3_apps_clk = { + .cbcr_reg = BLSP2_UART3_APPS_CBCR, + .parent = &blsp2_uart3_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_uart3_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_uart3_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_uart4_apps_clk = { + .cbcr_reg = BLSP2_UART4_APPS_CBCR, + .parent = &blsp2_uart4_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_uart4_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_uart4_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_uart5_apps_clk = { + .cbcr_reg = BLSP2_UART5_APPS_CBCR, + .parent = &blsp2_uart5_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_uart5_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_uart5_apps_clk.c), + }, +}; + +static struct branch_clk gcc_blsp2_uart6_apps_clk = { + .cbcr_reg = BLSP2_UART6_APPS_CBCR, + .parent = &blsp2_uart6_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_blsp2_uart6_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_blsp2_uart6_apps_clk.c), + }, +}; + +static struct local_vote_clk gcc_ce1_clk = { + .cbcr_reg = CE1_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(5), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_ce1_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_ce1_clk.c), + }, +}; + +static struct local_vote_clk gcc_ce1_ahb_clk = { + .cbcr_reg = CE1_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(3), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_ce1_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_ce1_ahb_clk.c), + }, +}; + +static struct local_vote_clk gcc_ce1_axi_clk = { + .cbcr_reg = CE1_AXI_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(4), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_ce1_axi_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_ce1_axi_clk.c), + }, +}; + +static struct local_vote_clk gcc_ce2_clk = { + .cbcr_reg = CE2_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(2), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_ce2_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_ce2_clk.c), + }, +}; + +static struct local_vote_clk gcc_ce2_ahb_clk = { + .cbcr_reg = CE2_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(0), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_ce1_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_ce1_ahb_clk.c), + }, +}; + +static struct local_vote_clk gcc_ce2_axi_clk = { + .cbcr_reg = CE2_AXI_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(1), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_ce1_axi_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_ce2_axi_clk.c), + }, +}; + +static struct branch_clk gcc_gp1_clk = { + .cbcr_reg = GP1_CBCR, + .parent = &gp1_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_gp1_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_gp1_clk.c), + }, +}; + +static struct branch_clk gcc_gp2_clk = { + .cbcr_reg = GP2_CBCR, + .parent = &gp2_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_gp2_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_gp2_clk.c), + }, +}; + +static struct branch_clk gcc_gp3_clk = { + .cbcr_reg = GP3_CBCR, + .parent = &gp3_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_gp3_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_gp3_clk.c), + }, +}; + +static struct branch_clk gcc_pdm2_clk = { + .cbcr_reg = PDM2_CBCR, + .parent = &pdm2_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_pdm2_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_pdm2_clk.c), + }, +}; + +static struct branch_clk gcc_pdm_ahb_clk = { + .cbcr_reg = PDM_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_pdm_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_pdm_ahb_clk.c), + }, +}; + +static struct local_vote_clk gcc_prng_ahb_clk = { + .cbcr_reg = PRNG_AHB_CBCR, + .vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE, + .en_mask = BIT(13), + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_prng_ahb_clk", + .ops = &clk_ops_vote, + CLK_INIT(gcc_prng_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc1_ahb_clk = { + .cbcr_reg = SDCC1_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc1_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc1_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc1_apps_clk = { + .cbcr_reg = SDCC1_APPS_CBCR, + .parent = &sdcc1_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc1_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc1_apps_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc2_ahb_clk = { + .cbcr_reg = SDCC2_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc2_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc2_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc2_apps_clk = { + .cbcr_reg = SDCC2_APPS_CBCR, + .parent = &sdcc2_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc2_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc2_apps_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc3_ahb_clk = { + .cbcr_reg = SDCC3_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc3_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc3_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc3_apps_clk = { + .cbcr_reg = SDCC3_APPS_CBCR, + .parent = &sdcc3_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc3_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc3_apps_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc4_ahb_clk = { + .cbcr_reg = SDCC4_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc4_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc4_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_sdcc4_apps_clk = { + .cbcr_reg = SDCC4_APPS_CBCR, + .parent = &sdcc4_apps_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_sdcc4_apps_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_sdcc4_apps_clk.c), + }, +}; + +static struct branch_clk gcc_tsif_ahb_clk = { + .cbcr_reg = TSIF_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_tsif_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_tsif_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_tsif_ref_clk = { + .cbcr_reg = TSIF_REF_CBCR, + .parent = &tsif_ref_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_tsif_ref_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_tsif_ref_clk.c), + }, +}; + +static struct branch_clk gcc_usb30_master_clk = { + .cbcr_reg = USB30_MASTER_CBCR, + .parent = &usb30_master_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb30_master_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb30_master_clk.c), + }, +}; + +static struct branch_clk gcc_usb30_mock_utmi_clk = { + .cbcr_reg = USB30_MOCK_UTMI_CBCR, + .parent = &usb30_mock_utmi_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb30_mock_utmi_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb30_mock_utmi_clk.c), + }, +}; + +static struct branch_clk gcc_usb_hs_ahb_clk = { + .cbcr_reg = USB_HS_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb_hs_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb_hs_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_usb_hs_system_clk = { + .cbcr_reg = USB_HS_SYSTEM_CBCR, + .parent = &usb_hs_system_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb_hs_system_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb_hs_system_clk.c), + }, +}; + +static struct branch_clk gcc_usb_hsic_ahb_clk = { + .cbcr_reg = USB_HSIC_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb_hsic_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb_hsic_ahb_clk.c), + }, +}; + +static struct branch_clk gcc_usb_hsic_clk = { + .cbcr_reg = USB_HSIC_CBCR, + .parent = &usb_hsic_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb_hsic_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb_hsic_clk.c), + }, +}; + +static struct branch_clk gcc_usb_hsic_io_cal_clk = { + .cbcr_reg = USB_HSIC_IO_CAL_CBCR, + .parent = &usb_hsic_io_cal_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb_hsic_io_cal_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb_hsic_io_cal_clk.c), + }, +}; + +static struct branch_clk gcc_usb_hsic_system_clk = { + .cbcr_reg = USB_HSIC_SYSTEM_CBCR, + .parent = &usb_hsic_system_clk_src.c, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_usb_hsic_system_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_usb_hsic_system_clk.c), + }, +}; + +static struct branch_clk gcc_mss_cfg_ahb_clk = { + .cbcr_reg = MSS_CFG_AHB_CBCR, + .has_sibling = 1, + .base = &virt_bases[GCC_BASE], + .c = { + .dbg_name = "gcc_mss_cfg_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(gcc_mss_cfg_ahb_clk.c), + }, +}; + +static struct clk_freq_tbl ftbl_mmss_ahb_clk[] = { + F_MM(19200000, cxo, 1, 0, 0), + F_MM(40000000, gpll0, 15, 0, 0), + F_MM(80000000, mmpll0, 10, 0, 0), + F_END, +}; + +/* TODO: This may go away (may be controlled by the RPM). */ +static struct rcg_clk ahb_clk_src = { + .cmd_rcgr_reg = 0x5000, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mmss_ahb_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "ahb_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 40000000, NOMINAL, 80000000), + CLK_INIT(ahb_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mmss_axi_clk[] = { + F_MM( 19200000, cxo, 1, 0, 0), + F_MM(150000000, gpll0, 4, 0, 0), + F_MM(333330000, mmpll1, 3, 0, 0), + F_MM(400000000, mmpll0, 2, 0, 0), + F_END +}; + +static struct rcg_clk axi_clk_src = { + .cmd_rcgr_reg = 0x5040, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mmss_axi_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "axi_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 150000000, NOMINAL, 333330000, + HIGH, 400000000), + CLK_INIT(axi_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_csi0_3_clk[] = { + F_MM(100000000, gpll0, 6, 0, 0), + F_MM(200000000, mmpll0, 4, 0, 0), + F_END +}; + +static struct rcg_clk csi0_clk_src = { + .cmd_rcgr_reg = CSI0_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_csi0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi0_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi0_clk_src.c), + }, +}; + +static struct rcg_clk csi1_clk_src = { + .cmd_rcgr_reg = CSI1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_csi0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi1_clk_src.c), + }, +}; + +static struct rcg_clk csi2_clk_src = { + .cmd_rcgr_reg = CSI2_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_csi0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi2_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi2_clk_src.c), + }, +}; + +static struct rcg_clk csi3_clk_src = { + .cmd_rcgr_reg = CSI3_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_csi0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi3_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi3_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_vfe_vfe0_1_clk[] = { + F_MM( 37500000, gpll0, 16, 0, 0), + F_MM( 50000000, gpll0, 12, 0, 0), + F_MM( 60000000, gpll0, 10, 0, 0), + F_MM( 80000000, gpll0, 7.5, 0, 0), + F_MM(100000000, gpll0, 6, 0, 0), + F_MM(109090000, gpll0, 5.5, 0, 0), + F_MM(150000000, gpll0, 4, 0, 0), + F_MM(200000000, gpll0, 3, 0, 0), + F_MM(228570000, mmpll0, 3.5, 0, 0), + F_MM(266670000, mmpll0, 3, 0, 0), + F_MM(320000000, mmpll0, 2.5, 0, 0), + F_END +}; + +static struct rcg_clk vfe0_clk_src = { + .cmd_rcgr_reg = VFE0_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_vfe_vfe0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "vfe0_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(vfe0_clk_src.c), + }, +}; + +static struct rcg_clk vfe1_clk_src = { + .cmd_rcgr_reg = VFE1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_vfe_vfe0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "vfe1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(vfe1_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_mdp_clk[] = { + F_MM( 37500000, gpll0, 16, 0, 0), + F_MM( 60000000, gpll0, 10, 0, 0), + F_MM( 75000000, gpll0, 8, 0, 0), + F_MM( 85710000, gpll0, 7, 0, 0), + F_MM(100000000, gpll0, 6, 0, 0), + F_MM(133330000, mmpll0, 6, 0, 0), + F_MM(160000000, mmpll0, 5, 0, 0), + F_MM(200000000, mmpll0, 4, 0, 0), + F_MM(266670000, mmpll0, 3, 0, 0), + F_MM(320000000, mmpll0, 2.5, 0, 0), + F_END +}; + +static struct rcg_clk mdp_clk_src = { + .cmd_rcgr_reg = MDP_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_mdp_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdp_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(mdp_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_cci_cci_clk[] = { + F_MM(19200000, cxo, 1, 0, 0), + F_END +}; + +static struct rcg_clk cci_clk_src = { + .cmd_rcgr_reg = CCI_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_cci_cci_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "cci_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 20000000, NOMINAL, 40000000), + CLK_INIT(cci_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_gp0_1_clk[] = { + F_MM( 10000, cxo, 16, 1, 120), + F_MM( 20000, cxo, 16, 1, 50), + F_MM( 6000000, gpll0, 10, 1, 10), + F_MM(12000000, gpll0, 10, 1, 5), + F_MM(13000000, gpll0, 10, 13, 60), + F_MM(24000000, gpll0, 5, 1, 5), + F_END +}; + +static struct rcg_clk mmss_gp0_clk_src = { + .cmd_rcgr_reg = MMSS_GP0_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_camss_gp0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_gp0_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(mmss_gp0_clk_src.c), + }, +}; + +static struct rcg_clk mmss_gp1_clk_src = { + .cmd_rcgr_reg = MMSS_GP1_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_camss_gp0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_gp1_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(mmss_gp1_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_jpeg_jpeg0_2_clk[] = { + F_MM( 75000000, gpll0, 8, 0, 0), + F_MM(150000000, gpll0, 4, 0, 0), + F_MM(200000000, gpll0, 3, 0, 0), + F_MM(228570000, mmpll0, 3.5, 0, 0), + F_MM(266670000, mmpll0, 3, 0, 0), + F_MM(320000000, mmpll0, 2.5, 0, 0), + F_END +}; + +static struct rcg_clk jpeg0_clk_src = { + .cmd_rcgr_reg = JPEG0_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_jpeg_jpeg0_2_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "jpeg0_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(jpeg0_clk_src.c), + }, +}; + +static struct rcg_clk jpeg1_clk_src = { + .cmd_rcgr_reg = JPEG1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_jpeg_jpeg0_2_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "jpeg1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(jpeg1_clk_src.c), + }, +}; + +static struct rcg_clk jpeg2_clk_src = { + .cmd_rcgr_reg = JPEG2_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_jpeg_jpeg0_2_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "jpeg2_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(jpeg2_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_mclk0_3_clk[] = { + F_MM(66670000, gpll0, 9, 0, 0), + F_END +}; + +static struct rcg_clk mclk0_clk_src = { + .cmd_rcgr_reg = MCLK0_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mclk0_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 66670000), + CLK_INIT(mclk0_clk_src.c), + }, +}; + +static struct rcg_clk mclk1_clk_src = { + .cmd_rcgr_reg = MCLK1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mclk1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 66670000), + CLK_INIT(mclk1_clk_src.c), + }, +}; + +static struct rcg_clk mclk2_clk_src = { + .cmd_rcgr_reg = MCLK2_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mclk2_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 66670000), + CLK_INIT(mclk2_clk_src.c), + }, +}; + +static struct rcg_clk mclk3_clk_src = { + .cmd_rcgr_reg = MCLK3_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mclk3_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP1(LOW, 66670000), + CLK_INIT(mclk3_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_phy0_2_csi0_2phytimer_clk[] = { + F_MM(100000000, gpll0, 6, 0, 0), + F_MM(200000000, mmpll0, 4, 0, 0), + F_END +}; + +static struct rcg_clk csi0phytimer_clk_src = { + .cmd_rcgr_reg = CSI0PHYTIMER_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_phy0_2_csi0_2phytimer_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi0phytimer_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi0phytimer_clk_src.c), + }, +}; + +static struct rcg_clk csi1phytimer_clk_src = { + .cmd_rcgr_reg = CSI1PHYTIMER_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_phy0_2_csi0_2phytimer_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi1phytimer_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi1phytimer_clk_src.c), + }, +}; + +static struct rcg_clk csi2phytimer_clk_src = { + .cmd_rcgr_reg = CSI2PHYTIMER_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_phy0_2_csi0_2phytimer_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "csi2phytimer_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000), + CLK_INIT(csi2phytimer_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_camss_vfe_cpp_clk[] = { + F_MM(150000000, gpll0, 4, 0, 0), + F_MM(266670000, mmpll0, 3, 0, 0), + F_MM(320000000, mmpll0, 2.5, 0, 0), + F_END +}; + +static struct rcg_clk cpp_clk_src = { + .cmd_rcgr_reg = CPP_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_camss_vfe_cpp_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "cpp_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 320000000), + CLK_INIT(cpp_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_byte0_1_clk[] = { + F_MDSS( 93750000, dsipll_750, 8, 0, 0), + F_MDSS(187500000, dsipll_750, 4, 0, 0), + F_END +}; + +static struct rcg_clk byte0_clk_src = { + .cmd_rcgr_reg = BYTE0_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_byte0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "byte0_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 93800000, NOMINAL, 187500000, + HIGH, 188000000), + CLK_INIT(byte0_clk_src.c), + }, +}; + +static struct rcg_clk byte1_clk_src = { + .cmd_rcgr_reg = BYTE1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_byte0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "byte1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP3(LOW, 93800000, NOMINAL, 187500000, + HIGH, 188000000), + CLK_INIT(byte1_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_edpaux_clk[] = { + F_MM(19200000, cxo, 1, 0, 0), + F_END +}; + +static struct rcg_clk edpaux_clk_src = { + .cmd_rcgr_reg = EDPAUX_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_edpaux_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "edpaux_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 20000000, NOMINAL, 40000000), + CLK_INIT(edpaux_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_edplink_clk[] = { + F_MDSS(135000000, edppll_270, 2, 0, 0), + F_MDSS(270000000, edppll_270, 11, 0, 0), + F_END +}; + +static struct rcg_clk edplink_clk_src = { + .cmd_rcgr_reg = EDPLINK_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_edplink_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "edplink_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 135000000, NOMINAL, 270000000), + CLK_INIT(edplink_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = { + F_MDSS(175000000, edppll_350, 2, 0, 0), + F_MDSS(350000000, edppll_350, 11, 0, 0), + F_END +}; + +static struct rcg_clk edppixel_clk_src = { + .cmd_rcgr_reg = EDPPIXEL_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_mdss_edppixel_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "edppixel_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 175000000, NOMINAL, 350000000), + CLK_INIT(edppixel_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_esc0_1_clk[] = { + F_MM(19200000, cxo, 1, 0, 0), + F_END +}; + +static struct rcg_clk esc0_clk_src = { + .cmd_rcgr_reg = ESC0_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "esc0_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 20000000, NOMINAL, 40000000), + CLK_INIT(esc0_clk_src.c), + }, +}; + +static struct rcg_clk esc1_clk_src = { + .cmd_rcgr_reg = ESC1_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "esc1_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 20000000, NOMINAL, 40000000), + CLK_INIT(esc1_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_extpclk_clk[] = { + F_MDSS(148500000, hdmipll_297, 2, 0, 0), + F_END +}; + +static struct rcg_clk extpclk_clk_src = { + .cmd_rcgr_reg = EXTPCLK_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_extpclk_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "extpclk_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 148500000, NOMINAL, 297000000), + CLK_INIT(extpclk_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_hdmi_clk[] = { + F_MDSS(19200000, cxo, 1, 0, 0), + F_END +}; + +static struct rcg_clk hdmi_clk_src = { + .cmd_rcgr_reg = HDMI_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_hdmi_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "hdmi_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 20000000, NOMINAL, 40000000), + CLK_INIT(hdmi_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_pclk0_1_clk[] = { + F_MDSS(125000000, dsipll_250, 2, 0, 0), + F_MDSS(250000000, dsipll_250, 1, 0, 0), + F_END +}; + +static struct rcg_clk pclk0_clk_src = { + .cmd_rcgr_reg = PCLK0_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_mdss_pclk0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "pclk0_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 125000000, NOMINAL, 250000000), + CLK_INIT(pclk0_clk_src.c), + }, +}; + +static struct rcg_clk pclk1_clk_src = { + .cmd_rcgr_reg = PCLK1_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_mdss_pclk0_1_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "pclk1_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 125000000, NOMINAL, 250000000), + CLK_INIT(pclk1_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_mdss_vsync_clk[] = { + F_MDSS(19200000, cxo, 1, 0, 0), + F_END +}; + +static struct rcg_clk vsync_clk_src = { + .cmd_rcgr_reg = VSYNC_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_mdss_vsync_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "vsync_clk_src", + .ops = &clk_ops_rcg, + VDD_DIG_FMAX_MAP2(LOW, 20000000, NOMINAL, 40000000), + CLK_INIT(vsync_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_venus0_vcodec0_clk[] = { + F_MM( 50000000, gpll0, 12, 0, 0), + F_MM(100000000, gpll0, 6, 0, 0), + F_MM(133330000, mmpll0, 6, 0, 0), + F_MM(200000000, mmpll0, 4, 0, 0), + F_MM(266670000, mmpll0, 3, 0, 0), + F_MM(410000000, mmpll3, 2, 0, 0), + F_END +}; + +static struct rcg_clk vcodec0_clk_src = { + .cmd_rcgr_reg = VCODEC0_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_venus0_vcodec0_clk, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "vcodec0_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP3(LOW, 133330000, NOMINAL, 266670000, + HIGH, 410000000), + CLK_INIT(vcodec0_clk_src.c), + }, +}; + +static struct branch_clk camss_cci_cci_ahb_clk = { + .cbcr_reg = CAMSS_CCI_CCI_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_cci_cci_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_cci_cci_ahb_clk.c), + }, +}; + +static struct branch_clk camss_cci_cci_clk = { + .cbcr_reg = CAMSS_CCI_CCI_CBCR, + .parent = &cci_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_cci_cci_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_cci_cci_clk.c), + }, +}; + +static struct branch_clk camss_csi0_ahb_clk = { + .cbcr_reg = CAMSS_CSI0_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi0_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi0_ahb_clk.c), + }, +}; + +static struct branch_clk camss_csi0_clk = { + .cbcr_reg = CAMSS_CSI0_CBCR, + .parent = &csi0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi0_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi0_clk.c), + }, +}; + +static struct branch_clk camss_csi0phy_clk = { + .cbcr_reg = CAMSS_CSI0PHY_CBCR, + .parent = &csi0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi0phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi0phy_clk.c), + }, +}; + +static struct branch_clk camss_csi0pix_clk = { + .cbcr_reg = CAMSS_CSI0PIX_CBCR, + .parent = &csi0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi0pix_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi0pix_clk.c), + }, +}; + +static struct branch_clk camss_csi0rdi_clk = { + .cbcr_reg = CAMSS_CSI0RDI_CBCR, + .parent = &csi0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi0rdi_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi0rdi_clk.c), + }, +}; + +static struct branch_clk camss_csi1_ahb_clk = { + .cbcr_reg = CAMSS_CSI1_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi1_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi1_ahb_clk.c), + }, +}; + +static struct branch_clk camss_csi1_clk = { + .cbcr_reg = CAMSS_CSI1_CBCR, + .parent = &csi1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi1_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi1_clk.c), + }, +}; + +static struct branch_clk camss_csi1phy_clk = { + .cbcr_reg = CAMSS_CSI1PHY_CBCR, + .parent = &csi1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi1phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi1phy_clk.c), + }, +}; + +static struct branch_clk camss_csi1pix_clk = { + .cbcr_reg = CAMSS_CSI1PIX_CBCR, + .parent = &csi1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi1pix_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi1pix_clk.c), + }, +}; + +static struct branch_clk camss_csi1rdi_clk = { + .cbcr_reg = CAMSS_CSI1RDI_CBCR, + .parent = &csi1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi1rdi_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi1rdi_clk.c), + }, +}; + +static struct branch_clk camss_csi2_ahb_clk = { + .cbcr_reg = CAMSS_CSI2_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi2_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi2_ahb_clk.c), + }, +}; + +static struct branch_clk camss_csi2_clk = { + .cbcr_reg = CAMSS_CSI2_CBCR, + .parent = &csi2_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi2_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi2_clk.c), + }, +}; + +static struct branch_clk camss_csi2phy_clk = { + .cbcr_reg = CAMSS_CSI2PHY_CBCR, + .parent = &csi2_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi2phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi2phy_clk.c), + }, +}; + +static struct branch_clk camss_csi2pix_clk = { + .cbcr_reg = CAMSS_CSI2PIX_CBCR, + .parent = &csi2_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi2pix_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi2pix_clk.c), + }, +}; + +static struct branch_clk camss_csi2rdi_clk = { + .cbcr_reg = CAMSS_CSI2RDI_CBCR, + .parent = &csi2_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi2rdi_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi2rdi_clk.c), + }, +}; + +static struct branch_clk camss_csi3_ahb_clk = { + .cbcr_reg = CAMSS_CSI3_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi3_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi3_ahb_clk.c), + }, +}; + +static struct branch_clk camss_csi3_clk = { + .cbcr_reg = CAMSS_CSI3_CBCR, + .parent = &csi3_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi3_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi3_clk.c), + }, +}; + +static struct branch_clk camss_csi3phy_clk = { + .cbcr_reg = CAMSS_CSI3PHY_CBCR, + .parent = &csi3_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi3phy_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi3phy_clk.c), + }, +}; + +static struct branch_clk camss_csi3pix_clk = { + .cbcr_reg = CAMSS_CSI3PIX_CBCR, + .parent = &csi3_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi3pix_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi3pix_clk.c), + }, +}; + +static struct branch_clk camss_csi3rdi_clk = { + .cbcr_reg = CAMSS_CSI3RDI_CBCR, + .parent = &csi3_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi3rdi_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi3rdi_clk.c), + }, +}; + +static struct branch_clk camss_csi_vfe0_clk = { + .cbcr_reg = CAMSS_CSI_VFE0_CBCR, + .parent = &vfe0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi_vfe0_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi_vfe0_clk.c), + }, +}; + +static struct branch_clk camss_csi_vfe1_clk = { + .cbcr_reg = CAMSS_CSI_VFE1_CBCR, + .parent = &vfe1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_csi_vfe1_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_csi_vfe1_clk.c), + }, +}; + +static struct branch_clk camss_gp0_clk = { + .cbcr_reg = CAMSS_GP0_CBCR, + .parent = &mmss_gp0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_gp0_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_gp0_clk.c), + }, +}; + +static struct branch_clk camss_gp1_clk = { + .cbcr_reg = CAMSS_GP1_CBCR, + .parent = &mmss_gp1_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_gp1_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_gp1_clk.c), + }, +}; + +static struct branch_clk camss_ispif_ahb_clk = { + .cbcr_reg = CAMSS_ISPIF_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_ispif_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_ispif_ahb_clk.c), + }, +}; + +static struct branch_clk camss_jpeg_jpeg0_clk = { + .cbcr_reg = CAMSS_JPEG_JPEG0_CBCR, + .parent = &jpeg0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_jpeg_jpeg0_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_jpeg_jpeg0_clk.c), + }, +}; + +static struct branch_clk camss_jpeg_jpeg1_clk = { + .cbcr_reg = CAMSS_JPEG_JPEG1_CBCR, + .parent = &jpeg1_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_jpeg_jpeg1_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_jpeg_jpeg1_clk.c), + }, +}; + +static struct branch_clk camss_jpeg_jpeg2_clk = { + .cbcr_reg = CAMSS_JPEG_JPEG2_CBCR, + .parent = &jpeg2_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_jpeg_jpeg2_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_jpeg_jpeg2_clk.c), + }, +}; + +static struct branch_clk camss_jpeg_jpeg_ahb_clk = { + .cbcr_reg = CAMSS_JPEG_JPEG_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_jpeg_jpeg_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_jpeg_jpeg_ahb_clk.c), + }, +}; + +static struct branch_clk camss_jpeg_jpeg_axi_clk = { + .cbcr_reg = CAMSS_JPEG_JPEG_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_jpeg_jpeg_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_jpeg_jpeg_axi_clk.c), + }, +}; + +static struct branch_clk camss_jpeg_jpeg_ocmemnoc_clk = { + .cbcr_reg = CAMSS_JPEG_JPEG_OCMEMNOC_CBCR, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_jpeg_jpeg_ocmemnoc_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_jpeg_jpeg_ocmemnoc_clk.c), + }, +}; + +static struct branch_clk camss_mclk0_clk = { + .cbcr_reg = CAMSS_MCLK0_CBCR, + .parent = &mclk0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_mclk0_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_mclk0_clk.c), + }, +}; + +static struct branch_clk camss_mclk1_clk = { + .cbcr_reg = CAMSS_MCLK1_CBCR, + .parent = &mclk1_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_mclk1_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_mclk1_clk.c), + }, +}; + +static struct branch_clk camss_mclk2_clk = { + .cbcr_reg = CAMSS_MCLK2_CBCR, + .parent = &mclk2_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_mclk2_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_mclk2_clk.c), + }, +}; + +static struct branch_clk camss_mclk3_clk = { + .cbcr_reg = CAMSS_MCLK3_CBCR, + .parent = &mclk3_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_mclk3_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_mclk3_clk.c), + }, +}; + +static struct branch_clk camss_micro_ahb_clk = { + .cbcr_reg = CAMSS_MICRO_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_micro_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_micro_ahb_clk.c), + }, +}; + +static struct branch_clk camss_phy0_csi0phytimer_clk = { + .cbcr_reg = CAMSS_PHY0_CSI0PHYTIMER_CBCR, + .parent = &csi0phytimer_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_phy0_csi0phytimer_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_phy0_csi0phytimer_clk.c), + }, +}; + +static struct branch_clk camss_phy1_csi1phytimer_clk = { + .cbcr_reg = CAMSS_PHY1_CSI1PHYTIMER_CBCR, + .parent = &csi1phytimer_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_phy1_csi1phytimer_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_phy1_csi1phytimer_clk.c), + }, +}; + +static struct branch_clk camss_phy2_csi2phytimer_clk = { + .cbcr_reg = CAMSS_PHY2_CSI2PHYTIMER_CBCR, + .parent = &csi2phytimer_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_phy2_csi2phytimer_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_phy2_csi2phytimer_clk.c), + }, +}; + +static struct branch_clk camss_top_ahb_clk = { + .cbcr_reg = CAMSS_TOP_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_top_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_top_ahb_clk.c), + }, +}; + +static struct branch_clk camss_vfe_cpp_ahb_clk = { + .cbcr_reg = CAMSS_VFE_CPP_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_cpp_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_cpp_ahb_clk.c), + }, +}; + +static struct branch_clk camss_vfe_cpp_clk = { + .cbcr_reg = CAMSS_VFE_CPP_CBCR, + .parent = &cpp_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_cpp_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_cpp_clk.c), + }, +}; + +static struct branch_clk camss_vfe_vfe0_clk = { + .cbcr_reg = CAMSS_VFE_VFE0_CBCR, + .parent = &vfe0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_vfe0_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_vfe0_clk.c), + }, +}; + +static struct branch_clk camss_vfe_vfe1_clk = { + .cbcr_reg = CAMSS_VFE_VFE1_CBCR, + .parent = &vfe1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_vfe1_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_vfe1_clk.c), + }, +}; + +static struct branch_clk camss_vfe_vfe_ahb_clk = { + .cbcr_reg = CAMSS_VFE_VFE_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_vfe_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_vfe_ahb_clk.c), + }, +}; + +static struct branch_clk camss_vfe_vfe_axi_clk = { + .cbcr_reg = CAMSS_VFE_VFE_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_vfe_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_vfe_axi_clk.c), + }, +}; + +static struct branch_clk camss_vfe_vfe_ocmemnoc_clk = { + .cbcr_reg = CAMSS_VFE_VFE_OCMEMNOC_CBCR, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "camss_vfe_vfe_ocmemnoc_clk", + .ops = &clk_ops_branch, + CLK_INIT(camss_vfe_vfe_ocmemnoc_clk.c), + }, +}; + +static struct branch_clk mdss_ahb_clk = { + .cbcr_reg = MDSS_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_ahb_clk.c), + }, +}; + +static struct branch_clk mdss_axi_clk = { + .cbcr_reg = MDSS_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_axi_clk.c), + }, +}; + +static struct branch_clk mdss_byte0_clk = { + .cbcr_reg = MDSS_BYTE0_CBCR, + .parent = &byte0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_byte0_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_byte0_clk.c), + }, +}; + +static struct branch_clk mdss_byte1_clk = { + .cbcr_reg = MDSS_BYTE1_CBCR, + .parent = &byte1_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_byte1_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_byte1_clk.c), + }, +}; + +static struct branch_clk mdss_edpaux_clk = { + .cbcr_reg = MDSS_EDPAUX_CBCR, + .parent = &edpaux_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_edpaux_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_edpaux_clk.c), + }, +}; + +static struct branch_clk mdss_edplink_clk = { + .cbcr_reg = MDSS_EDPLINK_CBCR, + .parent = &edplink_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_edplink_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_edplink_clk.c), + }, +}; + +static struct branch_clk mdss_edppixel_clk = { + .cbcr_reg = MDSS_EDPPIXEL_CBCR, + .parent = &edppixel_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_edppixel_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_edppixel_clk.c), + }, +}; + +static struct branch_clk mdss_esc0_clk = { + .cbcr_reg = MDSS_ESC0_CBCR, + .parent = &esc0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_esc0_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_esc0_clk.c), + }, +}; + +static struct branch_clk mdss_esc1_clk = { + .cbcr_reg = MDSS_ESC1_CBCR, + .parent = &esc1_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_esc1_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_esc1_clk.c), + }, +}; + +static struct branch_clk mdss_extpclk_clk = { + .cbcr_reg = MDSS_EXTPCLK_CBCR, + .parent = &extpclk_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_extpclk_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_extpclk_clk.c), + }, +}; + +static struct branch_clk mdss_hdmi_ahb_clk = { + .cbcr_reg = MDSS_HDMI_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_hdmi_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_hdmi_ahb_clk.c), + }, +}; + +static struct branch_clk mdss_hdmi_clk = { + .cbcr_reg = MDSS_HDMI_CBCR, + .parent = &hdmi_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_hdmi_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_hdmi_clk.c), + }, +}; + +static struct branch_clk mdss_mdp_clk = { + .cbcr_reg = MDSS_MDP_CBCR, + .parent = &mdp_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_mdp_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_mdp_clk.c), + }, +}; + +static struct branch_clk mdss_mdp_lut_clk = { + .cbcr_reg = MDSS_MDP_LUT_CBCR, + .parent = &mdp_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_mdp_lut_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_mdp_lut_clk.c), + }, +}; + +static struct branch_clk mdss_pclk0_clk = { + .cbcr_reg = MDSS_PCLK0_CBCR, + .parent = &pclk0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_pclk0_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_pclk0_clk.c), + }, +}; + +static struct branch_clk mdss_pclk1_clk = { + .cbcr_reg = MDSS_PCLK1_CBCR, + .parent = &pclk1_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_pclk1_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_pclk1_clk.c), + }, +}; + +static struct branch_clk mdss_vsync_clk = { + .cbcr_reg = MDSS_VSYNC_CBCR, + .parent = &vsync_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mdss_vsync_clk", + .ops = &clk_ops_branch, + CLK_INIT(mdss_vsync_clk.c), + }, +}; + +static struct branch_clk mmss_misc_ahb_clk = { + .cbcr_reg = MMSS_MISC_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_misc_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(mmss_misc_ahb_clk.c), + }, +}; + +static struct branch_clk mmss_mmssnoc_ahb_clk = { + .cbcr_reg = MMSS_MMSSNOC_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_mmssnoc_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(mmss_mmssnoc_ahb_clk.c), + }, +}; + +static struct branch_clk mmss_mmssnoc_bto_ahb_clk = { + .cbcr_reg = MMSS_MMSSNOC_BTO_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_mmssnoc_bto_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(mmss_mmssnoc_bto_ahb_clk.c), + }, +}; + +static struct branch_clk mmss_mmssnoc_axi_clk = { + .cbcr_reg = MMSS_MMSSNOC_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_mmssnoc_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(mmss_mmssnoc_axi_clk.c), + }, +}; + +static struct branch_clk mmss_s0_axi_clk = { + .cbcr_reg = MMSS_S0_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "mmss_s0_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(mmss_s0_axi_clk.c), + }, +}; + +static struct branch_clk venus0_ahb_clk = { + .cbcr_reg = VENUS0_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "venus0_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(venus0_ahb_clk.c), + }, +}; + +static struct branch_clk venus0_axi_clk = { + .cbcr_reg = VENUS0_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "venus0_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(venus0_axi_clk.c), + }, +}; + +static struct branch_clk venus0_ocmemnoc_clk = { + .cbcr_reg = VENUS0_OCMEMNOC_CBCR, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "venus0_ocmemnoc_clk", + .ops = &clk_ops_branch, + CLK_INIT(venus0_ocmemnoc_clk.c), + }, +}; + +static struct branch_clk venus0_vcodec0_clk = { + .cbcr_reg = VENUS0_VCODEC0_CBCR, + .parent = &vcodec0_clk_src.c, + .has_sibling = 0, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "venus0_vcodec0_clk", + .ops = &clk_ops_branch, + CLK_INIT(venus0_vcodec0_clk.c), + }, +}; + +static struct branch_clk oxili_gfx3d_clk = { + .cbcr_reg = OXILI_GFX3D_CBCR, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "oxili_gfx3d_clk", + .ops = &clk_ops_branch, + CLK_INIT(oxili_gfx3d_clk.c), + }, +}; + +static struct branch_clk oxilicx_ahb_clk = { + .cbcr_reg = OXILICX_AHB_CBCR, + .parent = &ahb_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "oxilicx_ahb_clk", + .ops = &clk_ops_branch, + CLK_INIT(oxilicx_ahb_clk.c), + }, +}; + +static struct branch_clk oxilicx_axi_clk = { + .cbcr_reg = OXILICX_AXI_CBCR, + .parent = &axi_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[MMSS_BASE], + .c = { + .dbg_name = "oxilicx_axi_clk", + .ops = &clk_ops_branch, + CLK_INIT(oxilicx_axi_clk.c), + }, +}; + +static struct clk_freq_tbl ftbl_audio_core_slimbus_core_clock[] = { + F_LPASS(28800000, lpapll0, 1, 15, 256), + F_END +}; + +static struct rcg_clk audio_core_slimbus_core_clk_src = { + .cmd_rcgr_reg = SLIMBUS_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_slimbus_core_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_slimbus_core_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 70000000, NOMINAL, 140000000), + CLK_INIT(audio_core_slimbus_core_clk_src.c), + }, +}; + +static struct branch_clk audio_core_slimbus_core_clk = { + .cbcr_reg = AUDIO_CORE_SLIMBUS_CORE_CBCR, + .parent = &audio_core_slimbus_core_clk_src.c, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_slimbus_core_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_slimbus_core_clk.c), + }, +}; + +static struct branch_clk audio_core_slimbus_lfabif_clk = { + .cbcr_reg = AUDIO_CORE_SLIMBUS_LFABIF_CBCR, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_slimbus_lfabif_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_slimbus_lfabif_clk.c), + }, +}; + +static struct clk_freq_tbl ftbl_audio_core_lpaif_clock[] = { + F_LPASS( 512000, lpapll0, 16, 1, 60), + F_LPASS( 768000, lpapll0, 16, 1, 40), + F_LPASS( 1024000, lpapll0, 16, 1, 30), + F_LPASS( 1536000, lpapll0, 16, 1, 10), + F_LPASS( 2048000, lpapll0, 16, 1, 15), + F_LPASS( 3072000, lpapll0, 16, 1, 10), + F_LPASS( 4096000, lpapll0, 15, 1, 8), + F_LPASS( 6144000, lpapll0, 10, 1, 8), + F_LPASS( 8192000, lpapll0, 15, 1, 4), + F_LPASS(12288000, lpapll0, 10, 1, 4), + F_END +}; + +static struct rcg_clk audio_core_lpaif_codec_spkr_clk_src = { + .cmd_rcgr_reg = LPAIF_SPKR_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_codec_spkr_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_codec_spkr_clk_src.c), + }, +}; + +static struct rcg_clk audio_core_lpaif_pri_clk_src = { + .cmd_rcgr_reg = LPAIF_PRI_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pri_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_pri_clk_src.c), + }, +}; + +static struct rcg_clk audio_core_lpaif_sec_clk_src = { + .cmd_rcgr_reg = LPAIF_SEC_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_sec_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_sec_clk_src.c), + }, +}; + +static struct rcg_clk audio_core_lpaif_ter_clk_src = { + .cmd_rcgr_reg = LPAIF_TER_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_ter_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_ter_clk_src.c), + }, +}; + +static struct rcg_clk audio_core_lpaif_quad_clk_src = { + .cmd_rcgr_reg = LPAIF_QUAD_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_quad_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_quad_clk_src.c), + }, +}; + +static struct rcg_clk audio_core_lpaif_pcm0_clk_src = { + .cmd_rcgr_reg = LPAIF_PCM0_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pcm0_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_pcm0_clk_src.c), + }, +}; + +static struct rcg_clk audio_core_lpaif_pcm1_clk_src = { + .cmd_rcgr_reg = LPAIF_PCM1_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_audio_core_lpaif_clock, + .current_freq = &rcg_dummy_freq, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pcm1_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP2(LOW, 12000000, NOMINAL, 25000000), + CLK_INIT(audio_core_lpaif_pcm1_clk_src.c), + }, +}; + +static struct branch_clk audio_core_lpaif_codec_spkr_osr_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_OSR_CBCR, + .parent = &audio_core_lpaif_codec_spkr_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_codec_spkr_clk_src", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_codec_spkr_clk_src.c), + }, +}; + +static struct branch_clk audio_core_lpaif_codec_spkr_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_EBIT_CBCR, + .parent = &audio_core_lpaif_codec_spkr_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_codec_spkr_clk_src", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_codec_spkr_clk_src.c), + }, +}; + +static struct branch_clk audio_core_lpaif_codec_spkr_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_IBIT_CBCR, + .parent = &audio_core_lpaif_codec_spkr_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_codec_spkr_clk_src", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_codec_spkr_clk_src.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pri_osr_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PRI_OSR_CBCR, + .parent = &audio_core_lpaif_pri_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pri_osr_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pri_osr_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pri_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PRI_EBIT_CBCR, + .parent = &audio_core_lpaif_pri_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pri_ebit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pri_ebit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pri_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PRI_IBIT_CBCR, + .parent = &audio_core_lpaif_pri_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pri_ibit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pri_ibit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_sec_osr_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_SEC_OSR_CBCR, + .parent = &audio_core_lpaif_sec_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_sec_osr_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_sec_osr_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_sec_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_SEC_EBIT_CBCR, + .parent = &audio_core_lpaif_sec_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_sec_ebit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_sec_ebit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_sec_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_SEC_IBIT_CBCR, + .parent = &audio_core_lpaif_sec_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_sec_ibit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_sec_ibit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_ter_osr_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_TER_OSR_CBCR, + .parent = &audio_core_lpaif_ter_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_ter_osr_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_ter_osr_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_ter_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_TER_EBIT_CBCR, + .parent = &audio_core_lpaif_ter_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_ter_ebit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_ter_ebit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_ter_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_TER_IBIT_CBCR, + .parent = &audio_core_lpaif_ter_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_ter_ibit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_ter_ibit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_quad_osr_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_QUAD_OSR_CBCR, + .parent = &audio_core_lpaif_quad_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_quad_osr_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_quad_osr_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_quad_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_QUAD_EBIT_CBCR, + .parent = &audio_core_lpaif_quad_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_quad_ebit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_quad_ebit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_quad_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_QUAD_IBIT_CBCR, + .parent = &audio_core_lpaif_quad_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_quad_ibit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_quad_ibit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pcm0_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR, + .parent = &audio_core_lpaif_pcm0_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pcm0_ebit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pcm0_ebit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pcm0_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR, + .parent = &audio_core_lpaif_pcm0_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pcm0_ibit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pcm0_ibit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pcm1_ebit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR, + .parent = &audio_core_lpaif_pcm1_clk_src.c, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pcm1_ebit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pcm1_ebit_clk.c), + }, +}; + +static struct branch_clk audio_core_lpaif_pcm1_ibit_clk = { + .cbcr_reg = AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR, + .parent = &audio_core_lpaif_pcm1_clk_src.c, + .has_sibling = 1, + .max_div = 16, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "audio_core_lpaif_pcm1_ibit_clk", + .ops = &clk_ops_branch, + CLK_INIT(audio_core_lpaif_pcm1_ibit_clk.c), + }, +}; + +static struct branch_clk q6ss_ahb_lfabif_clk = { + .cbcr_reg = LPASS_Q6SS_AHB_LFABIF_CBCR, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "q6ss_ahb_lfabif_clk", + .ops = &clk_ops_branch, + CLK_INIT(q6ss_ahb_lfabif_clk.c), + }, +}; + +static struct branch_clk q6ss_xo_clk = { + .cbcr_reg = LPASS_Q6SS_XO_CBCR, + .bcr_reg = LPASS_Q6SS_BCR, + .has_sibling = 1, + .base = &virt_bases[LPASS_BASE], + .c = { + .dbg_name = "q6ss_xo_clk", + .ops = &clk_ops_branch, + CLK_INIT(q6ss_xo_clk.c), + }, +}; + +static struct branch_clk mss_xo_q6_clk = { + .cbcr_reg = MSS_XO_Q6_CBCR, + .bcr_reg = MSS_Q6SS_BCR, + .has_sibling = 1, + .base = &virt_bases[MSS_BASE], + .c = { + .dbg_name = "mss_xo_q6_clk", + .ops = &clk_ops_branch, + CLK_INIT(mss_xo_q6_clk.c), + .depends = &gcc_mss_cfg_ahb_clk.c, + }, +}; + +static struct branch_clk mss_bus_q6_clk = { + .cbcr_reg = MSS_BUS_Q6_CBCR, + .has_sibling = 1, + .base = &virt_bases[MSS_BASE], + .c = { + .dbg_name = "mss_bus_q6_clk", + .ops = &clk_ops_branch, + CLK_INIT(mss_bus_q6_clk.c), + .depends = &gcc_mss_cfg_ahb_clk.c, + }, +}; + +#ifdef CONFIG_DEBUG_FS + +struct measure_mux_entry { + struct clk *c; + int base; + u32 debug_mux; +}; + +struct measure_mux_entry measure_mux[] = { + {&gcc_bam_dma_ahb_clk.c, GCC_BASE, 0x00e8}, + {&gcc_blsp1_ahb_clk.c, GCC_BASE, 0x0090}, + {&gcc_blsp1_qup1_i2c_apps_clk.c, GCC_BASE, 0x0093}, + {&gcc_blsp1_qup1_spi_apps_clk.c, GCC_BASE, 0x0092}, + {&gcc_blsp1_qup2_i2c_apps_clk.c, GCC_BASE, 0x0098}, + {&gcc_blsp1_qup2_spi_apps_clk.c, GCC_BASE, 0x0096}, + {&gcc_blsp1_qup3_i2c_apps_clk.c, GCC_BASE, 0x009c}, + {&gcc_blsp1_qup3_spi_apps_clk.c, GCC_BASE, 0x009b}, + {&gcc_blsp1_qup4_i2c_apps_clk.c, GCC_BASE, 0x00a1}, + {&gcc_blsp1_qup4_spi_apps_clk.c, GCC_BASE, 0x00a0}, + {&gcc_blsp1_qup5_i2c_apps_clk.c, GCC_BASE, 0x00a5}, + {&gcc_blsp1_qup5_spi_apps_clk.c, GCC_BASE, 0x00a4}, + {&gcc_blsp1_qup6_i2c_apps_clk.c, GCC_BASE, 0x00aa}, + {&gcc_blsp1_qup6_spi_apps_clk.c, GCC_BASE, 0x00a9}, + {&gcc_blsp1_uart1_apps_clk.c, GCC_BASE, 0x0094}, + {&gcc_blsp1_uart2_apps_clk.c, GCC_BASE, 0x0099}, + {&gcc_blsp1_uart3_apps_clk.c, GCC_BASE, 0x009d}, + {&gcc_blsp1_uart4_apps_clk.c, GCC_BASE, 0x00a2}, + {&gcc_blsp1_uart5_apps_clk.c, GCC_BASE, 0x00a6}, + {&gcc_blsp1_uart6_apps_clk.c, GCC_BASE, 0x00ab}, + {&gcc_blsp2_ahb_clk.c, GCC_BASE, 0x00b0}, + {&gcc_blsp2_qup1_i2c_apps_clk.c, GCC_BASE, 0x00b3}, + {&gcc_blsp2_qup1_spi_apps_clk.c, GCC_BASE, 0x00b2}, + {&gcc_blsp2_qup2_i2c_apps_clk.c, GCC_BASE, 0x00b8}, + {&gcc_blsp2_qup2_spi_apps_clk.c, GCC_BASE, 0x00b6}, + {&gcc_blsp2_qup3_i2c_apps_clk.c, GCC_BASE, 0x00bc}, + {&gcc_blsp2_qup3_spi_apps_clk.c, GCC_BASE, 0x00bb}, + {&gcc_blsp2_qup4_i2c_apps_clk.c, GCC_BASE, 0x00c1}, + {&gcc_blsp2_qup4_spi_apps_clk.c, GCC_BASE, 0x00c0}, + {&gcc_blsp2_qup5_i2c_apps_clk.c, GCC_BASE, 0x00c5}, + {&gcc_blsp2_qup5_spi_apps_clk.c, GCC_BASE, 0x00c4}, + {&gcc_blsp2_qup6_i2c_apps_clk.c, GCC_BASE, 0x00ca}, + {&gcc_blsp2_qup6_spi_apps_clk.c, GCC_BASE, 0x00c9}, + {&gcc_blsp2_uart1_apps_clk.c, GCC_BASE, 0x00b4}, + {&gcc_blsp2_uart2_apps_clk.c, GCC_BASE, 0x00b9}, + {&gcc_blsp2_uart3_apps_clk.c, GCC_BASE, 0x00bd}, + {&gcc_blsp2_uart4_apps_clk.c, GCC_BASE, 0x00c2}, + {&gcc_blsp2_uart5_apps_clk.c, GCC_BASE, 0x00c6}, + {&gcc_blsp2_uart6_apps_clk.c, GCC_BASE, 0x00cb}, + {&gcc_boot_rom_ahb_clk.c, GCC_BASE, 0x0100}, + {&gcc_mss_cfg_ahb_clk.c, GCC_BASE, 0x0030}, + {&gcc_ce1_clk.c, GCC_BASE, 0x0140}, + {&gcc_ce2_clk.c, GCC_BASE, 0x0148}, + {&gcc_pdm2_clk.c, GCC_BASE, 0x00da}, + {&gcc_pdm_ahb_clk.c, GCC_BASE, 0x00d8}, + {&gcc_prng_ahb_clk.c, GCC_BASE, 0x00e0}, + {&gcc_sdcc1_ahb_clk.c, GCC_BASE, 0x0071}, + {&gcc_sdcc1_apps_clk.c, GCC_BASE, 0x0070}, + {&gcc_sdcc2_ahb_clk.c, GCC_BASE, 0x0079}, + {&gcc_sdcc2_apps_clk.c, GCC_BASE, 0x0078}, + {&gcc_sdcc3_ahb_clk.c, GCC_BASE, 0x0081}, + {&gcc_sdcc3_apps_clk.c, GCC_BASE, 0x0080}, + {&gcc_sdcc4_ahb_clk.c, GCC_BASE, 0x0089}, + {&gcc_sdcc4_apps_clk.c, GCC_BASE, 0x0088}, + {&gcc_tsif_ahb_clk.c, GCC_BASE, 0x00f0}, + {&gcc_tsif_ref_clk.c, GCC_BASE, 0x00f1}, + {&gcc_usb30_master_clk.c, GCC_BASE, 0x0050}, + {&gcc_usb30_mock_utmi_clk.c, GCC_BASE, 0x0052}, + {&gcc_usb_hs_ahb_clk.c, GCC_BASE, 0x0069}, + {&gcc_usb_hs_system_clk.c, GCC_BASE, 0x0068}, + {&gcc_usb_hsic_ahb_clk.c, GCC_BASE, 0x0060}, + {&gcc_usb_hsic_clk.c, GCC_BASE, 0x0062}, + {&gcc_usb_hsic_io_cal_clk.c, GCC_BASE, 0x0063}, + {&gcc_usb_hsic_system_clk.c, GCC_BASE, 0x0061}, + {&mmss_mmssnoc_ahb_clk.c, MMSS_BASE, 0x0001}, + {&mmss_mmssnoc_axi_clk.c, MMSS_BASE, 0x0004}, + {&camss_cci_cci_ahb_clk.c, MMSS_BASE, 0x002e}, + {&camss_cci_cci_clk.c, MMSS_BASE, 0x002d}, + {&camss_csi0_ahb_clk.c, MMSS_BASE, 0x0042}, + {&camss_csi0_clk.c, MMSS_BASE, 0x0041}, + {&camss_csi0phy_clk.c, MMSS_BASE, 0x0043}, + {&camss_csi0pix_clk.c, MMSS_BASE, 0x0045}, + {&camss_csi0rdi_clk.c, MMSS_BASE, 0x0044}, + {&camss_csi1_ahb_clk.c, MMSS_BASE, 0x0047}, + {&camss_csi1_clk.c, MMSS_BASE, 0x0046}, + {&camss_csi1phy_clk.c, MMSS_BASE, 0x0048}, + {&camss_csi1pix_clk.c, MMSS_BASE, 0x004a}, + {&camss_csi1rdi_clk.c, MMSS_BASE, 0x0049}, + {&camss_csi2_ahb_clk.c, MMSS_BASE, 0x004c}, + {&camss_csi2_clk.c, MMSS_BASE, 0x004b}, + {&camss_csi2phy_clk.c, MMSS_BASE, 0x004d}, + {&camss_csi2pix_clk.c, MMSS_BASE, 0x004f}, + {&camss_csi2rdi_clk.c, MMSS_BASE, 0x004e}, + {&camss_csi3_ahb_clk.c, MMSS_BASE, 0x0051}, + {&camss_csi3_clk.c, MMSS_BASE, 0x0050}, + {&camss_csi3phy_clk.c, MMSS_BASE, 0x0052}, + {&camss_csi3pix_clk.c, MMSS_BASE, 0x0054}, + {&camss_csi3rdi_clk.c, MMSS_BASE, 0x0053}, + {&camss_csi_vfe0_clk.c, MMSS_BASE, 0x003f}, + {&camss_csi_vfe1_clk.c, MMSS_BASE, 0x0040}, + {&camss_gp0_clk.c, MMSS_BASE, 0x0027}, + {&camss_gp1_clk.c, MMSS_BASE, 0x0028}, + {&camss_ispif_ahb_clk.c, MMSS_BASE, 0x0055}, + {&camss_jpeg_jpeg0_clk.c, MMSS_BASE, 0x0032}, + {&camss_jpeg_jpeg1_clk.c, MMSS_BASE, 0x0033}, + {&camss_jpeg_jpeg2_clk.c, MMSS_BASE, 0x0034}, + {&camss_jpeg_jpeg_ahb_clk.c, MMSS_BASE, 0x0035}, + {&camss_jpeg_jpeg_axi_clk.c, MMSS_BASE, 0x0036}, + {&camss_jpeg_jpeg_ocmemnoc_clk.c, MMSS_BASE, 0x0037}, + {&camss_mclk0_clk.c, MMSS_BASE, 0x0029}, + {&camss_mclk1_clk.c, MMSS_BASE, 0x002a}, + {&camss_mclk2_clk.c, MMSS_BASE, 0x002b}, + {&camss_mclk3_clk.c, MMSS_BASE, 0x002c}, + {&camss_micro_ahb_clk.c, MMSS_BASE, 0x0026}, + {&camss_phy0_csi0phytimer_clk.c, MMSS_BASE, 0x002f}, + {&camss_phy1_csi1phytimer_clk.c, MMSS_BASE, 0x0030}, + {&camss_phy2_csi2phytimer_clk.c, MMSS_BASE, 0x0031}, + {&camss_top_ahb_clk.c, MMSS_BASE, 0x0025}, + {&camss_vfe_cpp_ahb_clk.c, MMSS_BASE, 0x003b}, + {&camss_vfe_cpp_clk.c, MMSS_BASE, 0x003a}, + {&camss_vfe_vfe0_clk.c, MMSS_BASE, 0x0038}, + {&camss_vfe_vfe1_clk.c, MMSS_BASE, 0x0039}, + {&camss_vfe_vfe_ahb_clk.c, MMSS_BASE, 0x003c}, + {&camss_vfe_vfe_axi_clk.c, MMSS_BASE, 0x003d}, + {&camss_vfe_vfe_ocmemnoc_clk.c, MMSS_BASE, 0x003e}, + {&mdss_ahb_clk.c, MMSS_BASE, 0x0022}, + {&mdss_hdmi_clk.c, MMSS_BASE, 0x001d}, + {&mdss_mdp_clk.c, MMSS_BASE, 0x0014}, + {&mdss_mdp_lut_clk.c, MMSS_BASE, 0x0015}, + {&mdss_axi_clk.c, MMSS_BASE, 0x0024}, + {&mdss_vsync_clk.c, MMSS_BASE, 0x001c}, + {&mdss_esc0_clk.c, MMSS_BASE, 0x0020}, + {&mdss_esc1_clk.c, MMSS_BASE, 0x0021}, + {&mdss_edpaux_clk.c, MMSS_BASE, 0x001b}, + {&mdss_byte0_clk.c, MMSS_BASE, 0x001e}, + {&mdss_byte1_clk.c, MMSS_BASE, 0x001f}, + {&mdss_edplink_clk.c, MMSS_BASE, 0x001a}, + {&mdss_edppixel_clk.c, MMSS_BASE, 0x0019}, + {&mdss_extpclk_clk.c, MMSS_BASE, 0x0018}, + {&mdss_hdmi_ahb_clk.c, MMSS_BASE, 0x0023}, + {&mdss_pclk0_clk.c, MMSS_BASE, 0x0016}, + {&mdss_pclk1_clk.c, MMSS_BASE, 0x0017}, + {&audio_core_lpaif_pri_clk_src.c, LPASS_BASE, 0x0017}, + {&audio_core_lpaif_sec_clk_src.c, LPASS_BASE, 0x0016}, + {&audio_core_lpaif_ter_clk_src.c, LPASS_BASE, 0x0015}, + {&audio_core_lpaif_quad_clk_src.c, LPASS_BASE, 0x0014}, + {&audio_core_lpaif_pcm0_clk_src.c, LPASS_BASE, 0x0013}, + {&audio_core_lpaif_pcm1_clk_src.c, LPASS_BASE, 0x0012}, + {&audio_core_slimbus_core_clk.c, LPASS_BASE, 0x003d}, + {&audio_core_slimbus_lfabif_clk.c, LPASS_BASE, 0x003e}, + {&q6ss_xo_clk.c, LPASS_BASE, 0x002b}, + {&q6ss_ahb_lfabif_clk.c, LPASS_BASE, 0x001e}, + {&mss_bus_q6_clk.c, MSS_BASE, 0x003c}, + {&mss_xo_q6_clk.c, MSS_BASE, 0x0007}, + + {&dummy_clk, N_BASES, 0x0000}, +}; + +static int measure_clk_set_parent(struct clk *c, struct clk *parent) +{ + struct measure_clk *clk = to_measure_clk(c); + unsigned long flags; + u32 regval, clk_sel, i; + + if (!parent) + return -EINVAL; + + for (i = 0; i < (ARRAY_SIZE(measure_mux) - 1); i++) + if (measure_mux[i].c == parent) + break; + + if (measure_mux[i].c == &dummy_clk) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + /* + * Program the test vector, measurement period (sample_ticks) + * and scaling multiplier. + */ + clk->sample_ticks = 0x10000; + clk->multiplier = 1; + + writel_relaxed(0, MSS_REG_BASE(MSS_DEBUG_CLK_CTL_REG)); + writel_relaxed(0, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG)); + writel_relaxed(0, MMSS_REG_BASE(MMSS_DEBUG_CLK_CTL_REG)); + writel_relaxed(0, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG)); + + switch (measure_mux[i].base) { + + case GCC_BASE: + clk_sel = measure_mux[i].debug_mux; + break; + + case MMSS_BASE: + clk_sel = 0x02C; + regval = BVAL(11, 0, measure_mux[i].debug_mux); + writel_relaxed(regval, MMSS_REG_BASE(MMSS_DEBUG_CLK_CTL_REG)); + + /* Activate debug clock output */ + regval |= BIT(16); + writel_relaxed(regval, MMSS_REG_BASE(MMSS_DEBUG_CLK_CTL_REG)); + break; + + case LPASS_BASE: + clk_sel = 0x169; + regval = BVAL(11, 0, measure_mux[i].debug_mux); + writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG)); + + /* Activate debug clock output */ + regval |= BIT(16); + writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG)); + break; + + case MSS_BASE: + clk_sel = 0x32; + regval = BVAL(5, 0, measure_mux[i].debug_mux); + writel_relaxed(regval, MSS_REG_BASE(MSS_DEBUG_CLK_CTL_REG)); + break; + + default: + return -EINVAL; + } + + /* Set debug mux clock index */ + regval = BVAL(8, 0, clk_sel); + writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG)); + + /* Activate debug clock output */ + regval |= BIT(16); + writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG)); + + /* Make sure test vector is set before starting measurements. */ + mb(); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +/* Sample clock for 'ticks' reference clock ticks. */ +static u32 run_measurement(unsigned ticks) +{ + /* Stop counters and set the XO4 counter start value. */ + writel_relaxed(ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG)); + + /* Wait for timer to become ready. */ + while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) & + BIT(25)) != 0) + cpu_relax(); + + /* Run measurement and wait for completion. */ + writel_relaxed(BIT(20)|ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG)); + while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) & + BIT(25)) == 0) + cpu_relax(); + + /* Return measured ticks. */ + return readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) & + BM(24, 0); +} + +/* + * Perform a hardware rate measurement for a given clock. + * FOR DEBUG USE ONLY: Measurements take ~15 ms! + */ +static unsigned long measure_clk_get_rate(struct clk *c) +{ + unsigned long flags; + u32 gcc_xo4_reg_backup; + u64 raw_count_short, raw_count_full; + struct measure_clk *clk = to_measure_clk(c); + unsigned ret; + + ret = clk_prepare_enable(&cxo_clk_src.c); + if (ret) { + pr_warning("CXO clock failed to enable. Can't measure\n"); + return 0; + } + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable CXO/4 and RINGOSC branch. */ + gcc_xo4_reg_backup = readl_relaxed(GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG)); + writel_relaxed(0x1, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG)); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(clk->sample_ticks); + + writel_relaxed(gcc_xo4_reg_backup, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG)); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) { + ret = 0; + } else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, ((clk->sample_ticks * 10) + 35)); + ret = (raw_count_full * clk->multiplier); + } + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + clk_disable_unprepare(&cxo_clk_src.c); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned long measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops clk_ops_measure = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, +}; + +static struct measure_clk measure_clk = { + .c = { + .dbg_name = "measure_clk", + .ops = &clk_ops_measure, + CLK_INIT(measure_clk.c), + }, + .multiplier = 1, +}; + +static struct clk_lookup msm_clocks_copper[] = { + CLK_LOOKUP("xo", cxo_clk_src.c, "msm_otg"), + CLK_LOOKUP("xo", cxo_clk_src.c, "pil-q6v5-lpass"), + CLK_LOOKUP("xo", cxo_clk_src.c, "pil-q6v5-mss"), + CLK_LOOKUP("xo", cxo_clk_src.c, "pil-mba"), + CLK_LOOKUP("xo", cxo_clk_src.c, "pil_pronto"), + CLK_LOOKUP("measure", measure_clk.c, "debug"), + + CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"), + CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "spi_qsd.1"), + CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, "spi_qsd.1"), + CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup5_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup6_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_qup6_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_uart1_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_uart2_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_uart3_apps_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", gcc_blsp1_uart4_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_uart5_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp1_uart6_apps_clk.c, ""), + + CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f9966000.i2c"), + CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f995e000.serial"), + CLK_LOOKUP("core_clk", gcc_blsp2_qup1_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup1_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup2_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup2_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup3_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup3_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup4_i2c_apps_clk.c, "f9966000.i2c"), + CLK_LOOKUP("core_clk", gcc_blsp2_qup4_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup5_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup5_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup6_i2c_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_qup6_spi_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_uart1_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_uart2_apps_clk.c, "f995e000.serial"), + CLK_LOOKUP("core_clk", gcc_blsp2_uart3_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_uart4_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_uart5_apps_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_blsp2_uart6_apps_clk.c, ""), + + CLK_LOOKUP("core_clk", gcc_ce1_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_ce2_clk.c, ""), + CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, ""), + CLK_LOOKUP("iface_clk", gcc_ce2_ahb_clk.c, ""), + CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, ""), + CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, ""), + + CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""), + CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""), + + CLK_LOOKUP("core_clk", gcc_pdm2_clk.c, ""), + CLK_LOOKUP("iface_clk", gcc_pdm_ahb_clk.c, ""), + CLK_LOOKUP("iface_clk", gcc_prng_ahb_clk.c, ""), + + CLK_LOOKUP("iface_clk", gcc_sdcc1_ahb_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", gcc_sdcc1_apps_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", gcc_sdcc4_ahb_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", gcc_sdcc4_apps_clk.c, "msm_sdcc.4"), + + CLK_LOOKUP("iface_clk", gcc_tsif_ahb_clk.c, ""), + CLK_LOOKUP("ref_clk", gcc_tsif_ref_clk.c, ""), + + CLK_LOOKUP("core_clk", gcc_usb30_master_clk.c, "msm_dwc3"), + CLK_LOOKUP("utmi_clk", gcc_usb30_mock_utmi_clk.c, "msm_dwc3"), + CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c, "msm_otg"), + CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", gcc_usb_hsic_ahb_clk.c, "msm_hsic_host"), + CLK_LOOKUP("phy_clk", gcc_usb_hsic_clk.c, "msm_hsic_host"), + CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c, "msm_hsic_host"), + CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "msm_hsic_host"), + + /* Multimedia clocks */ + CLK_LOOKUP("bus_clk_src", axi_clk_src.c, ""), + CLK_LOOKUP("bus_clk_src", ahb_clk_src.c, ""), + CLK_LOOKUP("bus_clk", mmss_mmssnoc_ahb_clk.c, ""), + CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_edppixel_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_esc0_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_esc1_clk.c, ""), + CLK_LOOKUP("iface_clk", mdss_hdmi_ahb_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_hdmi_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_mdp_clk.c, ""), + CLK_LOOKUP("core_clk", mdss_mdp_lut_clk.c, ""), + CLK_LOOKUP("core_clk", mdp_clk_src.c, ""), + CLK_LOOKUP("core_clk", mdss_vsync_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_cci_cci_ahb_clk.c, ""), + CLK_LOOKUP("core_clk", camss_cci_cci_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_csi0_ahb_clk.c, ""), + CLK_LOOKUP("camss_csi0_clk", camss_csi0_clk.c, ""), + CLK_LOOKUP("camss_csi0phy_clk", camss_csi0phy_clk.c, ""), + CLK_LOOKUP("camss_csi0pix_clk", camss_csi0pix_clk.c, ""), + CLK_LOOKUP("camss_csi0rdi_clk", camss_csi0rdi_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_csi1_ahb_clk.c, ""), + CLK_LOOKUP("camss_csi1_clk", camss_csi1_clk.c, ""), + CLK_LOOKUP("camss_csi1phy_clk", camss_csi1phy_clk.c, ""), + CLK_LOOKUP("camss_csi1pix_clk", camss_csi1pix_clk.c, ""), + CLK_LOOKUP("camss_csi1rdi_clk", camss_csi1rdi_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_csi2_ahb_clk.c, ""), + CLK_LOOKUP("camss_csi2_clk", camss_csi2_clk.c, ""), + CLK_LOOKUP("camss_csi2phy_clk", camss_csi2phy_clk.c, ""), + CLK_LOOKUP("camss_csi2pix_clk", camss_csi2pix_clk.c, ""), + CLK_LOOKUP("camss_csi2rdi_clk", camss_csi2rdi_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_csi3_ahb_clk.c, ""), + CLK_LOOKUP("camss_csi3_clk", camss_csi3_clk.c, ""), + CLK_LOOKUP("camss_csi3phy_clk", camss_csi3phy_clk.c, ""), + CLK_LOOKUP("camss_csi3pix_clk", camss_csi3pix_clk.c, ""), + CLK_LOOKUP("camss_csi3rdi_clk", camss_csi3rdi_clk.c, ""), + CLK_LOOKUP("camss_csi0_clk_src", csi0_clk_src.c, ""), + CLK_LOOKUP("camss_csi1_clk_src", csi1_clk_src.c, ""), + CLK_LOOKUP("camss_csi2_clk_src", csi2_clk_src.c, ""), + CLK_LOOKUP("camss_csi3_clk_src", csi3_clk_src.c, ""), + CLK_LOOKUP("camss_csi_vfe0_clk", camss_csi_vfe0_clk.c, ""), + CLK_LOOKUP("camss_csi_vfe1_clk", camss_csi_vfe1_clk.c, ""), + CLK_LOOKUP("core_clk", camss_gp0_clk.c, ""), + CLK_LOOKUP("core_clk", camss_gp1_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_ispif_ahb_clk.c, ""), + CLK_LOOKUP("core_clk", camss_jpeg_jpeg0_clk.c, ""), + CLK_LOOKUP("core_clk", camss_jpeg_jpeg1_clk.c, ""), + CLK_LOOKUP("core_clk", camss_jpeg_jpeg2_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_jpeg_jpeg_ahb_clk.c, + "fda64000.qcom,iommu"), + CLK_LOOKUP("core_clk", camss_jpeg_jpeg_axi_clk.c, + "fda64000.qcom,iommu"), + CLK_LOOKUP("bus_clk", camss_jpeg_jpeg_axi_clk.c, ""), + CLK_LOOKUP("bus_clk", camss_jpeg_jpeg_ocmemnoc_clk.c, ""), + CLK_LOOKUP("core_clk", camss_mclk0_clk.c, ""), + CLK_LOOKUP("core_clk", camss_mclk1_clk.c, ""), + CLK_LOOKUP("core_clk", camss_mclk2_clk.c, ""), + CLK_LOOKUP("core_clk", camss_mclk3_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_micro_ahb_clk.c, ""), + CLK_LOOKUP("core_clk", camss_phy0_csi0phytimer_clk.c, ""), + CLK_LOOKUP("core_clk", camss_phy1_csi1phytimer_clk.c, ""), + CLK_LOOKUP("core_clk", camss_phy2_csi2phytimer_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_top_ahb_clk.c, ""), + CLK_LOOKUP("iface_clk", camss_vfe_cpp_ahb_clk.c, ""), + CLK_LOOKUP("core_clk", camss_vfe_cpp_clk.c, ""), + CLK_LOOKUP("camss_vfe_vfe0_clk", camss_vfe_vfe0_clk.c, ""), + CLK_LOOKUP("camss_vfe_vfe1_clk", camss_vfe_vfe1_clk.c, ""), + CLK_LOOKUP("vfe0_clk_src", vfe0_clk_src.c, ""), + CLK_LOOKUP("vfe1_clk_src", vfe1_clk_src.c, ""), + CLK_LOOKUP("iface_clk", camss_vfe_vfe_ahb_clk.c, ""), + CLK_LOOKUP("bus_clk", camss_vfe_vfe_axi_clk.c, ""), + CLK_LOOKUP("bus_clk", camss_vfe_vfe_ocmemnoc_clk.c, ""), + CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"), + CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"), + CLK_LOOKUP("bus_clk", mdss_axi_clk.c, ""), + CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, ""), + CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, ""), + CLK_LOOKUP("bus_clk", oxilicx_axi_clk.c, ""), + CLK_LOOKUP("iface_clk", venus0_ahb_clk.c, "fdc84000.qcom,iommu"), + CLK_LOOKUP("core_clk", venus0_axi_clk.c, "fdc84000.qcom,iommu"), + CLK_LOOKUP("bus_clk", venus0_axi_clk.c, ""), + + /* LPASS clocks */ + CLK_LOOKUP("core_clk", audio_core_slimbus_core_clk.c, "fe12f000.slim"), + CLK_LOOKUP("iface_clk", audio_core_slimbus_lfabif_clk.c, + "fe12f000.slim"), + CLK_LOOKUP("core_clk", audio_core_lpaif_codec_spkr_clk_src.c, ""), + CLK_LOOKUP("osr_clk", audio_core_lpaif_codec_spkr_osr_clk.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_codec_spkr_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_codec_spkr_ibit_clk.c, ""), + CLK_LOOKUP("core_clk", audio_core_lpaif_pri_clk_src.c, ""), + CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c, ""), + CLK_LOOKUP("core_clk", audio_core_lpaif_sec_clk_src.c, ""), + CLK_LOOKUP("osr_clk", audio_core_lpaif_sec_osr_clk.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_sec_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_sec_ibit_clk.c, ""), + CLK_LOOKUP("core_clk", audio_core_lpaif_ter_clk_src.c, ""), + CLK_LOOKUP("osr_clk", audio_core_lpaif_ter_osr_clk.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_ter_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_ter_ibit_clk.c, ""), + CLK_LOOKUP("core_clk", audio_core_lpaif_quad_clk_src.c, ""), + CLK_LOOKUP("osr_clk", audio_core_lpaif_quad_osr_clk.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_quad_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_quad_ibit_clk.c, ""), + CLK_LOOKUP("core_clk", audio_core_lpaif_pcm0_clk_src.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm0_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm0_ibit_clk.c, ""), + CLK_LOOKUP("core_clk", audio_core_lpaif_pcm1_clk_src.c, ""), + CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm1_ebit_clk.c, ""), + CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm1_ibit_clk.c, ""), + + CLK_LOOKUP("core_clk", mss_xo_q6_clk.c, "pil-q6v5-mss"), + CLK_LOOKUP("bus_clk", mss_bus_q6_clk.c, "pil-q6v5-mss"), + CLK_LOOKUP("bus_clk", gcc_mss_cfg_ahb_clk.c, ""), + CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "pil-q6v5-mss"), + CLK_LOOKUP("core_clk", q6ss_xo_clk.c, "pil-q6v5-lpass"), + CLK_LOOKUP("bus_clk", q6ss_ahb_lfabif_clk.c, "pil-q6v5-lpass"), + CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng"), + + /* TODO: Remove dummy clocks as soon as they become unnecessary */ + CLK_DUMMY("dfab_clk", DFAB_CLK, "msm_sps", OFF), + CLK_DUMMY("mem_clk", NULL, "msm_sps", OFF), + CLK_DUMMY("bus_clk", NULL, "scm", OFF), +}; + +static struct pll_config_regs gpll0_regs __initdata = { + .l_reg = (void __iomem *)GPLL0_L_REG, + .m_reg = (void __iomem *)GPLL0_M_REG, + .n_reg = (void __iomem *)GPLL0_N_REG, + .config_reg = (void __iomem *)GPLL0_USER_CTL_REG, + .mode_reg = (void __iomem *)GPLL0_MODE_REG, + .base = &virt_bases[GCC_BASE], +}; + +/* GPLL0 at 600 MHz, main output enabled. */ +static struct pll_config gpll0_config __initdata = { + .l = 0x1f, + .m = 0x1, + .n = 0x4, + .vco_val = 0x0, + .vco_mask = BM(21, 20), + .pre_div_val = 0x0, + .pre_div_mask = BM(14, 12), + .post_div_val = 0x0, + .post_div_mask = BM(9, 8), + .mn_ena_val = BIT(24), + .mn_ena_mask = BIT(24), + .main_output_val = BIT(0), + .main_output_mask = BIT(0), +}; + +static struct pll_config_regs gpll1_regs __initdata = { + .l_reg = (void __iomem *)GPLL1_L_REG, + .m_reg = (void __iomem *)GPLL1_M_REG, + .n_reg = (void __iomem *)GPLL1_N_REG, + .config_reg = (void __iomem *)GPLL1_USER_CTL_REG, + .mode_reg = (void __iomem *)GPLL1_MODE_REG, + .base = &virt_bases[GCC_BASE], +}; + +/* GPLL1 at 480 MHz, main output enabled. */ +static struct pll_config gpll1_config __initdata = { + .l = 0x19, + .m = 0x0, + .n = 0x1, + .vco_val = 0x0, + .vco_mask = BM(21, 20), + .pre_div_val = 0x0, + .pre_div_mask = BM(14, 12), + .post_div_val = 0x0, + .post_div_mask = BM(9, 8), + .main_output_val = BIT(0), + .main_output_mask = BIT(0), +}; + +static struct pll_config_regs mmpll0_regs __initdata = { + .l_reg = (void __iomem *)MMPLL0_L_REG, + .m_reg = (void __iomem *)MMPLL0_M_REG, + .n_reg = (void __iomem *)MMPLL0_N_REG, + .config_reg = (void __iomem *)MMPLL0_USER_CTL_REG, + .mode_reg = (void __iomem *)MMPLL0_MODE_REG, + .base = &virt_bases[MMSS_BASE], +}; + +/* MMPLL0 at 800 MHz, main output enabled. */ +static struct pll_config mmpll0_config __initdata = { + .l = 0x29, + .m = 0x2, + .n = 0x3, + .vco_val = 0x0, + .vco_mask = BM(21, 20), + .pre_div_val = 0x0, + .pre_div_mask = BM(14, 12), + .post_div_val = 0x0, + .post_div_mask = BM(9, 8), + .mn_ena_val = BIT(24), + .mn_ena_mask = BIT(24), + .main_output_val = BIT(0), + .main_output_mask = BIT(0), +}; + +static struct pll_config_regs mmpll1_regs __initdata = { + .l_reg = (void __iomem *)MMPLL1_L_REG, + .m_reg = (void __iomem *)MMPLL1_M_REG, + .n_reg = (void __iomem *)MMPLL1_N_REG, + .config_reg = (void __iomem *)MMPLL1_USER_CTL_REG, + .mode_reg = (void __iomem *)MMPLL1_MODE_REG, + .base = &virt_bases[MMSS_BASE], +}; + +/* MMPLL1 at 1000 MHz, main output enabled. */ +static struct pll_config mmpll1_config __initdata = { + .l = 0x34, + .m = 0x1, + .n = 0xC, + .vco_val = 0x0, + .vco_mask = BM(21, 20), + .pre_div_val = 0x0, + .pre_div_mask = BM(14, 12), + .post_div_val = 0x0, + .post_div_mask = BM(9, 8), + .mn_ena_val = BIT(24), + .mn_ena_mask = BIT(24), + .main_output_val = BIT(0), + .main_output_mask = BIT(0), +}; + +static struct pll_config_regs mmpll3_regs __initdata = { + .l_reg = (void __iomem *)MMPLL3_L_REG, + .m_reg = (void __iomem *)MMPLL3_M_REG, + .n_reg = (void __iomem *)MMPLL3_N_REG, + .config_reg = (void __iomem *)MMPLL3_USER_CTL_REG, + .mode_reg = (void __iomem *)MMPLL3_MODE_REG, + .base = &virt_bases[MMSS_BASE], +}; + +/* MMPLL3 at 820 MHz, main output enabled. */ +static struct pll_config mmpll3_config __initdata = { + .l = 0x2A, + .m = 0x11, + .n = 0x18, + .vco_val = 0x0, + .vco_mask = BM(21, 20), + .pre_div_val = 0x0, + .pre_div_mask = BM(14, 12), + .post_div_val = 0x0, + .post_div_mask = BM(9, 8), + .mn_ena_val = BIT(24), + .mn_ena_mask = BIT(24), + .main_output_val = BIT(0), + .main_output_mask = BIT(0), +}; + +static struct pll_config_regs lpapll0_regs __initdata = { + .l_reg = (void __iomem *)LPAPLL_L_REG, + .m_reg = (void __iomem *)LPAPLL_M_REG, + .n_reg = (void __iomem *)LPAPLL_N_REG, + .config_reg = (void __iomem *)LPAPLL_USER_CTL_REG, + .mode_reg = (void __iomem *)LPAPLL_MODE_REG, + .base = &virt_bases[LPASS_BASE], +}; + +/* LPAPLL0 at 491.52 MHz, main output enabled. */ +static struct pll_config lpapll0_config __initdata = { + .l = 0x33, + .m = 0x1, + .n = 0x5, + .vco_val = 0x0, + .vco_mask = BM(21, 20), + .pre_div_val = BVAL(14, 12, 0x1), + .pre_div_mask = BM(14, 12), + .post_div_val = 0x0, + .post_div_mask = BM(9, 8), + .mn_ena_val = BIT(24), + .mn_ena_mask = BIT(24), + .main_output_val = BIT(0), + .main_output_mask = BIT(0), +}; + +#define PLL_AUX_OUTPUT BIT(1) + +static void __init reg_init(void) +{ + u32 regval; + + if (!(readl_relaxed(GCC_REG_BASE(GPLL0_STATUS_REG)) + & gpll0_clk_src.status_mask)) + configure_pll(&gpll0_config, &gpll0_regs, 1); + + if (!(readl_relaxed(GCC_REG_BASE(GPLL1_STATUS_REG)) + & gpll1_clk_src.status_mask)) + configure_pll(&gpll1_config, &gpll1_regs, 1); + + configure_pll(&mmpll0_config, &mmpll0_regs, 1); + configure_pll(&mmpll1_config, &mmpll1_regs, 1); + configure_pll(&mmpll3_config, &mmpll3_regs, 0); + configure_pll(&lpapll0_config, &lpapll0_regs, 1); + + /* Active GPLL0's aux output. This is needed by acpuclock. */ + regval = readl_relaxed(GCC_REG_BASE(GPLL0_USER_CTL_REG)); + regval |= BIT(PLL_AUX_OUTPUT); + writel_relaxed(regval, GCC_REG_BASE(GPLL0_USER_CTL_REG)); + + /* Vote for GPLL0 to turn on. Needed by acpuclock. */ + regval = readl_relaxed(GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG)); + regval |= BIT(0); + writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG)); + + /* + * TODO: Confirm that no clocks need to be voted on in this sleep vote + * register. + */ + writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE)); +} + +static void __init msmcopper_clock_post_init(void) +{ + clk_set_rate(&ahb_clk_src.c, 80000000); + clk_set_rate(&axi_clk_src.c, 333330000); + + /* Set rates for single-rate clocks. */ + clk_set_rate(&usb30_master_clk_src.c, + usb30_master_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&tsif_ref_clk_src.c, + tsif_ref_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&usb_hs_system_clk_src.c, + usb_hs_system_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&usb_hsic_clk_src.c, + usb_hsic_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&usb_hsic_io_cal_clk_src.c, + usb_hsic_io_cal_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&usb_hsic_system_clk_src.c, + usb_hsic_system_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&usb30_mock_utmi_clk_src.c, + usb30_mock_utmi_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&pdm2_clk_src.c, pdm2_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&cci_clk_src.c, cci_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&mclk0_clk_src.c, mclk0_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&mclk1_clk_src.c, mclk1_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&mclk2_clk_src.c, mclk2_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&edpaux_clk_src.c, edpaux_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&esc0_clk_src.c, esc0_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&esc1_clk_src.c, esc1_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&hdmi_clk_src.c, hdmi_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&vsync_clk_src.c, vsync_clk_src.freq_tbl[0].freq_hz); + clk_set_rate(&audio_core_slimbus_core_clk_src.c, + audio_core_slimbus_core_clk_src.freq_tbl[0].freq_hz); +} + +#define GCC_CC_PHYS 0xFC400000 +#define GCC_CC_SIZE SZ_16K + +#define MMSS_CC_PHYS 0xFD8C0000 +#define MMSS_CC_SIZE SZ_256K + +#define LPASS_CC_PHYS 0xFE000000 +#define LPASS_CC_SIZE SZ_256K + +#define MSS_CC_PHYS 0xFC980000 +#define MSS_CC_SIZE SZ_16K + +static void __init msmcopper_clock_pre_init(void) +{ + virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE); + if (!virt_bases[GCC_BASE]) + panic("clock-copper: Unable to ioremap GCC memory!"); + + virt_bases[MMSS_BASE] = ioremap(MMSS_CC_PHYS, MMSS_CC_SIZE); + if (!virt_bases[MMSS_BASE]) + panic("clock-copper: Unable to ioremap MMSS_CC memory!"); + + virt_bases[LPASS_BASE] = ioremap(LPASS_CC_PHYS, LPASS_CC_SIZE); + if (!virt_bases[LPASS_BASE]) + panic("clock-copper: Unable to ioremap LPASS_CC memory!"); + + virt_bases[MSS_BASE] = ioremap(MSS_CC_PHYS, MSS_CC_SIZE); + if (!virt_bases[MSS_BASE]) + panic("clock-copper: Unable to ioremap MSS_CC memory!"); + + clk_ops_local_pll.enable = copper_pll_clk_enable; + + reg_init(); +} + +struct clock_init_data msmcopper_clock_init_data __initdata = { + .table = msm_clocks_copper, + .size = ARRAY_SIZE(msm_clocks_copper), + .pre_init = msmcopper_clock_pre_init, + .post_init = msmcopper_clock_post_init, +}; diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c index 4886404d42f..e8c3e0552bc 100644 --- a/arch/arm/mach-msm/clock-debug.c +++ b/arch/arm/mach-msm/clock-debug.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -17,7 +17,11 @@ #include #include #include +#include #include +#include +#include + #include "clock.h" static int clock_debug_rate_set(void *data, u64 val) @@ -27,15 +31,12 @@ static int clock_debug_rate_set(void *data, u64 val) /* Only increases to max rate will succeed, but that's actually good * for debugging purposes so we don't check for error. */ - if (clock->flags & CLK_MAX) + if (clock->flags & CLKFLAG_MAX) clk_set_max_rate(clock, val); - if (clock->flags & CLK_MIN) - ret = clk_set_min_rate(clock, val); - else - ret = clk_set_rate(clock, val); - if (ret != 0) - printk(KERN_ERR "clk_set%s_rate failed (%d)\n", - (clock->flags & CLK_MIN) ? "_min" : "", ret); + ret = clk_set_rate(clock, val); + if (ret) + pr_err("clk_set_rate failed (%d)\n", ret); + return ret; } @@ -49,15 +50,51 @@ static int clock_debug_rate_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, clock_debug_rate_set, "%llu\n"); +static struct clk *measure; + +static int clock_debug_measure_get(void *data, u64 *val) +{ + struct clk *clock = data; + int ret, is_hw_gated; + + /* Check to see if the clock is in hardware gating mode */ + if (clock->flags & CLKFLAG_HWCG) + is_hw_gated = clock->ops->in_hwcg_mode(clock); + else + is_hw_gated = 0; + + ret = clk_set_parent(measure, clock); + if (!ret) { + /* + * Disable hw gating to get accurate rate measurements. Only do + * this if the clock is explictly enabled by software. This + * allows us to detect errors where clocks are on even though + * software is not requesting them to be on due to broken + * hardware gating signals. + */ + if (is_hw_gated && clock->count) + clock->ops->disable_hwcg(clock); + *val = clk_get_rate(measure); + /* Reenable hwgating if it was disabled */ + if (is_hw_gated && clock->count) + clock->ops->enable_hwcg(clock); + } + + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_measure_fops, clock_debug_measure_get, + NULL, "%lld\n"); + static int clock_debug_enable_set(void *data, u64 val) { struct clk *clock = data; int rc = 0; if (val) - rc = clock->ops->enable(clock->id); + rc = clk_prepare_enable(clock); else - clock->ops->disable(clock->id); + clk_disable_unprepare(clock); return rc; } @@ -65,20 +102,28 @@ static int clock_debug_enable_set(void *data, u64 val) static int clock_debug_enable_get(void *data, u64 *val) { struct clk *clock = data; + int enabled; - *val = clock->ops->is_enabled(clock->id); + if (clock->ops->is_enabled) + enabled = clock->ops->is_enabled(clock); + else + enabled = !!(clock->count); + *val = enabled; return 0; } DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get, - clock_debug_enable_set, "%llu\n"); + clock_debug_enable_set, "%lld\n"); static int clock_debug_local_get(void *data, u64 *val) { struct clk *clock = data; - *val = clock->ops->is_local(clock->id); + if (!clock->ops->is_local) + *val = true; + else + *val = clock->ops->is_local(clock); return 0; } @@ -86,16 +131,114 @@ static int clock_debug_local_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get, NULL, "%llu\n"); -static struct dentry *debugfs_base; +static int clock_debug_hwcg_get(void *data, u64 *val) +{ + struct clk *clock = data; + *val = !!(clock->flags & CLKFLAG_HWCG); + return 0; +} -int __init clock_debug_init(void) +DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get, + NULL, "%llu\n"); + +static struct dentry *debugfs_base; +static u32 debug_suspend; +static struct clk_lookup *msm_clocks; +static size_t num_msm_clocks; + +int __init clock_debug_init(struct clock_init_data *data) { debugfs_base = debugfs_create_dir("clk", NULL); if (!debugfs_base) return -ENOMEM; + if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR, + debugfs_base, &debug_suspend)) { + debugfs_remove_recursive(debugfs_base); + return -ENOMEM; + } + msm_clocks = data->table; + num_msm_clocks = data->size; + + measure = clk_get_sys("debug", "measure"); + if (IS_ERR(measure)) + measure = NULL; + return 0; } + +static int clock_debug_print_clock(struct clk *c) +{ + size_t ln = 0; + char s[128]; + + if (!c || !c->count) + return 0; + + ln += snprintf(s, sizeof(s), "\t%s", c->dbg_name); + while (ln < sizeof(s) && (c = clk_get_parent(c))) + ln += snprintf(s + ln, sizeof(s) - ln, " -> %s", c->dbg_name); + pr_info("%s\n", s); + return 1; +} + +void clock_debug_print_enabled(void) +{ + unsigned i; + int cnt = 0; + + if (likely(!debug_suspend)) + return; + + pr_info("Enabled clocks:\n"); + for (i = 0; i < num_msm_clocks; i++) + cnt += clock_debug_print_clock(msm_clocks[i].clk); + + if (cnt) + pr_info("Enabled clock count: %d\n", cnt); + else + pr_info("No clocks enabled.\n"); + +} + +static int list_rates_show(struct seq_file *m, void *unused) +{ + struct clk *clock = m->private; + int rate, level, fmax = 0, i = 0; + + /* Find max frequency supported within voltage constraints. */ + if (!clock->vdd_class) { + fmax = INT_MAX; + } else { + for (level = 0; level < ARRAY_SIZE(clock->fmax); level++) + if (clock->fmax[level]) + fmax = clock->fmax[level]; + } + + /* + * List supported frequencies <= fmax. Higher frequencies may appear in + * the frequency table, but are not valid and should not be listed. + */ + while ((rate = clock->ops->list_rate(clock, i++)) >= 0) { + if (rate <= fmax) + seq_printf(m, "%u\n", rate); + } + + return 0; +} + +static int list_rates_open(struct inode *inode, struct file *file) +{ + return single_open(file, list_rates_show, inode->i_private); +} + +static const struct file_operations list_rates_fops = { + .open = list_rates_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + int __init clock_debug_add(struct clk *clock) { char temp[50], *ptr; @@ -104,7 +247,7 @@ int __init clock_debug_add(struct clk *clock) if (!debugfs_base) return -ENOMEM; - strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1); + strlcpy(temp, clock->dbg_name, ARRAY_SIZE(temp)); for (ptr = temp; *ptr; ptr++) *ptr = tolower(*ptr); @@ -123,6 +266,22 @@ int __init clock_debug_add(struct clk *clock) if (!debugfs_create_file("is_local", S_IRUGO, clk_dir, clock, &clock_local_fops)) goto error; + + if (!debugfs_create_file("has_hw_gating", S_IRUGO, clk_dir, clock, + &clock_hwcg_fops)) + goto error; + + if (measure && + !clk_set_parent(measure, clock) && + !debugfs_create_file("measure", S_IRUGO, clk_dir, clock, + &clock_measure_fops)) + goto error; + + if (clock->ops->list_rate) + if (!debugfs_create_file("list_rates", + S_IRUGO, clk_dir, clock, &list_rates_fops)) + goto error; + return 0; error: debugfs_remove_recursive(clk_dir); diff --git a/arch/arm/mach-msm/clock-dss-8960.c b/arch/arm/mach-msm/clock-dss-8960.c new file mode 100644 index 00000000000..8331899a96f --- /dev/null +++ b/arch/arm/mach-msm/clock-dss-8960.c @@ -0,0 +1,330 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "clock-dss-8960.h" + +/* HDMI PLL macros */ +#define HDMI_PHY_PLL_REFCLK_CFG (MSM_HDMI_BASE + 0x00000500) +#define HDMI_PHY_PLL_CHRG_PUMP_CFG (MSM_HDMI_BASE + 0x00000504) +#define HDMI_PHY_PLL_LOOP_FLT_CFG0 (MSM_HDMI_BASE + 0x00000508) +#define HDMI_PHY_PLL_LOOP_FLT_CFG1 (MSM_HDMI_BASE + 0x0000050c) +#define HDMI_PHY_PLL_IDAC_ADJ_CFG (MSM_HDMI_BASE + 0x00000510) +#define HDMI_PHY_PLL_I_VI_KVCO_CFG (MSM_HDMI_BASE + 0x00000514) +#define HDMI_PHY_PLL_PWRDN_B (MSM_HDMI_BASE + 0x00000518) +#define HDMI_PHY_PLL_SDM_CFG0 (MSM_HDMI_BASE + 0x0000051c) +#define HDMI_PHY_PLL_SDM_CFG1 (MSM_HDMI_BASE + 0x00000520) +#define HDMI_PHY_PLL_SDM_CFG2 (MSM_HDMI_BASE + 0x00000524) +#define HDMI_PHY_PLL_SDM_CFG3 (MSM_HDMI_BASE + 0x00000528) +#define HDMI_PHY_PLL_SDM_CFG4 (MSM_HDMI_BASE + 0x0000052c) +#define HDMI_PHY_PLL_SSC_CFG0 (MSM_HDMI_BASE + 0x00000530) +#define HDMI_PHY_PLL_SSC_CFG1 (MSM_HDMI_BASE + 0x00000534) +#define HDMI_PHY_PLL_SSC_CFG2 (MSM_HDMI_BASE + 0x00000538) +#define HDMI_PHY_PLL_SSC_CFG3 (MSM_HDMI_BASE + 0x0000053c) +#define HDMI_PHY_PLL_LOCKDET_CFG0 (MSM_HDMI_BASE + 0x00000540) +#define HDMI_PHY_PLL_LOCKDET_CFG1 (MSM_HDMI_BASE + 0x00000544) +#define HDMI_PHY_PLL_LOCKDET_CFG2 (MSM_HDMI_BASE + 0x00000548) +#define HDMI_PHY_PLL_VCOCAL_CFG0 (MSM_HDMI_BASE + 0x0000054c) +#define HDMI_PHY_PLL_VCOCAL_CFG1 (MSM_HDMI_BASE + 0x00000550) +#define HDMI_PHY_PLL_VCOCAL_CFG2 (MSM_HDMI_BASE + 0x00000554) +#define HDMI_PHY_PLL_VCOCAL_CFG3 (MSM_HDMI_BASE + 0x00000558) +#define HDMI_PHY_PLL_VCOCAL_CFG4 (MSM_HDMI_BASE + 0x0000055c) +#define HDMI_PHY_PLL_VCOCAL_CFG5 (MSM_HDMI_BASE + 0x00000560) +#define HDMI_PHY_PLL_VCOCAL_CFG6 (MSM_HDMI_BASE + 0x00000564) +#define HDMI_PHY_PLL_VCOCAL_CFG7 (MSM_HDMI_BASE + 0x00000568) +#define HDMI_PHY_PLL_DEBUG_SEL (MSM_HDMI_BASE + 0x0000056c) +#define HDMI_PHY_PLL_MISC0 (MSM_HDMI_BASE + 0x00000570) +#define HDMI_PHY_PLL_MISC1 (MSM_HDMI_BASE + 0x00000574) +#define HDMI_PHY_PLL_MISC2 (MSM_HDMI_BASE + 0x00000578) +#define HDMI_PHY_PLL_MISC3 (MSM_HDMI_BASE + 0x0000057c) +#define HDMI_PHY_PLL_MISC4 (MSM_HDMI_BASE + 0x00000580) +#define HDMI_PHY_PLL_MISC5 (MSM_HDMI_BASE + 0x00000584) +#define HDMI_PHY_PLL_MISC6 (MSM_HDMI_BASE + 0x00000588) +#define HDMI_PHY_PLL_DEBUG_BUS0 (MSM_HDMI_BASE + 0x0000058c) +#define HDMI_PHY_PLL_DEBUG_BUS1 (MSM_HDMI_BASE + 0x00000590) +#define HDMI_PHY_PLL_DEBUG_BUS2 (MSM_HDMI_BASE + 0x00000594) +#define HDMI_PHY_PLL_STATUS0 (MSM_HDMI_BASE + 0x00000598) +#define HDMI_PHY_PLL_STATUS1 (MSM_HDMI_BASE + 0x0000059c) +#define HDMI_PHY_CTRL (MSM_HDMI_BASE + 0x000002D4) +#define HDMI_PHY_REG_0 (MSM_HDMI_BASE + 0x00000400) +#define HDMI_PHY_REG_1 (MSM_HDMI_BASE + 0x00000404) +#define HDMI_PHY_REG_2 (MSM_HDMI_BASE + 0x00000408) +#define HDMI_PHY_REG_3 (MSM_HDMI_BASE + 0x0000040c) +#define HDMI_PHY_REG_4 (MSM_HDMI_BASE + 0x00000410) +#define HDMI_PHY_REG_5 (MSM_HDMI_BASE + 0x00000414) +#define HDMI_PHY_REG_6 (MSM_HDMI_BASE + 0x00000418) +#define HDMI_PHY_REG_7 (MSM_HDMI_BASE + 0x0000041c) +#define HDMI_PHY_REG_8 (MSM_HDMI_BASE + 0x00000420) +#define HDMI_PHY_REG_9 (MSM_HDMI_BASE + 0x00000424) +#define HDMI_PHY_REG_10 (MSM_HDMI_BASE + 0x00000428) +#define HDMI_PHY_REG_11 (MSM_HDMI_BASE + 0x0000042c) +#define HDMI_PHY_REG_12 (MSM_HDMI_BASE + 0x00000430) +#define HDMI_PHY_REG_BIST_CFG (MSM_HDMI_BASE + 0x00000434) +#define HDMI_PHY_DEBUG_BUS_SEL (MSM_HDMI_BASE + 0x00000438) +#define HDMI_PHY_REG_MISC0 (MSM_HDMI_BASE + 0x0000043c) +#define HDMI_PHY_REG_13 (MSM_HDMI_BASE + 0x00000440) +#define HDMI_PHY_REG_14 (MSM_HDMI_BASE + 0x00000444) +#define HDMI_PHY_REG_15 (MSM_HDMI_BASE + 0x00000448) + +#define AHB_EN_REG (MSM_MMSS_CLK_CTL_BASE + 0x0008) + +/* HDMI PHY/PLL bit field macros */ +#define SW_RESET BIT(2) +#define SW_RESET_PLL BIT(0) +#define PWRDN_B BIT(7) + +#define PLL_PWRDN_B BIT(3) +#define PD_PLL BIT(1) + +static unsigned current_rate; +static unsigned hdmi_pll_on; + +int hdmi_pll_enable(void) +{ + unsigned int val; + u32 ahb_en_reg, ahb_enabled; + + ahb_en_reg = readl_relaxed(AHB_EN_REG); + ahb_enabled = ahb_en_reg & BIT(4); + if (!ahb_enabled) { + writel_relaxed(ahb_en_reg | BIT(4), AHB_EN_REG); + /* Make sure iface clock is enabled before register access */ + mb(); + } + + /* Assert PLL S/W reset */ + writel_relaxed(0x8D, HDMI_PHY_PLL_LOCKDET_CFG2); + writel_relaxed(0x10, HDMI_PHY_PLL_LOCKDET_CFG0); + writel_relaxed(0x1A, HDMI_PHY_PLL_LOCKDET_CFG1); + /* De-assert PLL S/W reset */ + writel_relaxed(0x0D, HDMI_PHY_PLL_LOCKDET_CFG2); + + val = readl_relaxed(HDMI_PHY_REG_12); + val |= BIT(5); + /* Assert PHY S/W reset */ + writel_relaxed(val, HDMI_PHY_REG_12); + val &= ~BIT(5); + /* De-assert PHY S/W reset */ + writel_relaxed(val, HDMI_PHY_REG_12); + writel_relaxed(0x3f, HDMI_PHY_REG_2); + + val = readl_relaxed(HDMI_PHY_REG_12); + val |= PWRDN_B; + writel_relaxed(val, HDMI_PHY_REG_12); + /* Wait 10 us for enabling global power for PHY */ + mb(); + udelay(10); + + val = readl_relaxed(HDMI_PHY_PLL_PWRDN_B); + val |= PLL_PWRDN_B; + val &= ~PD_PLL; + writel_relaxed(val, HDMI_PHY_PLL_PWRDN_B); + writel_relaxed(0x80, HDMI_PHY_REG_2); + + while (!(readl_relaxed(HDMI_PHY_PLL_STATUS0) & BIT(0))) + cpu_relax(); + + if (!ahb_enabled) + writel_relaxed(ahb_en_reg & ~BIT(4), AHB_EN_REG); + hdmi_pll_on = 1; + return 0; +} + +void hdmi_pll_disable(void) +{ + unsigned int val; + u32 ahb_en_reg, ahb_enabled; + + ahb_en_reg = readl_relaxed(AHB_EN_REG); + ahb_enabled = ahb_en_reg & BIT(4); + if (!ahb_enabled) { + writel_relaxed(ahb_en_reg | BIT(4), AHB_EN_REG); + mb(); + } + + val = readl_relaxed(HDMI_PHY_REG_12); + val &= (~PWRDN_B); + writel_relaxed(val, HDMI_PHY_REG_12); + + val = readl_relaxed(HDMI_PHY_PLL_PWRDN_B); + val |= PD_PLL; + val &= (~PLL_PWRDN_B); + writel_relaxed(val, HDMI_PHY_PLL_PWRDN_B); + /* Make sure HDMI PHY/PLL are powered down */ + mb(); + + if (!ahb_enabled) + writel_relaxed(ahb_en_reg & ~BIT(4), AHB_EN_REG); + hdmi_pll_on = 0; +} + +unsigned hdmi_pll_get_rate(void) +{ + return current_rate; +} + +int hdmi_pll_set_rate(unsigned rate) +{ + unsigned int set_power_dwn = 0; + u32 ahb_en_reg = readl_relaxed(AHB_EN_REG); + u32 ahb_enabled = ahb_en_reg & BIT(4); + + if (!ahb_enabled) { + writel_relaxed(ahb_en_reg | BIT(4), AHB_EN_REG); + /* Make sure iface clock is enabled before register access */ + mb(); + } + + if (hdmi_pll_on) { + hdmi_pll_disable(); + set_power_dwn = 1; + } + + switch (rate) { + case 27030000: + /* 480p60/480i60 case */ + writel_relaxed(0x32, HDMI_PHY_PLL_REFCLK_CFG); + writel_relaxed(0x2, HDMI_PHY_PLL_CHRG_PUMP_CFG); + writel_relaxed(0x08, HDMI_PHY_PLL_LOOP_FLT_CFG0); + writel_relaxed(0x77, HDMI_PHY_PLL_LOOP_FLT_CFG1); + writel_relaxed(0x2C, HDMI_PHY_PLL_IDAC_ADJ_CFG); + writel_relaxed(0x6, HDMI_PHY_PLL_I_VI_KVCO_CFG); + writel_relaxed(0x7b, HDMI_PHY_PLL_SDM_CFG0); + writel_relaxed(0x01, HDMI_PHY_PLL_SDM_CFG1); + writel_relaxed(0x4C, HDMI_PHY_PLL_SDM_CFG2); + writel_relaxed(0xC0, HDMI_PHY_PLL_SDM_CFG3); + writel_relaxed(0x00, HDMI_PHY_PLL_SDM_CFG4); + writel_relaxed(0x9A, HDMI_PHY_PLL_SSC_CFG0); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG1); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG3); + writel_relaxed(0x2A, HDMI_PHY_PLL_VCOCAL_CFG0); + writel_relaxed(0x03, HDMI_PHY_PLL_VCOCAL_CFG1); + writel_relaxed(0x2B, HDMI_PHY_PLL_VCOCAL_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG3); + writel_relaxed(0x86, HDMI_PHY_PLL_VCOCAL_CFG4); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG5); + writel_relaxed(0x33, HDMI_PHY_PLL_VCOCAL_CFG6); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG7); + break; + + case 25200000: + /* 640x480p60 */ + writel_relaxed(0x32, HDMI_PHY_PLL_REFCLK_CFG); + writel_relaxed(0x2, HDMI_PHY_PLL_CHRG_PUMP_CFG); + writel_relaxed(0x01, HDMI_PHY_PLL_LOOP_FLT_CFG0); + writel_relaxed(0x33, HDMI_PHY_PLL_LOOP_FLT_CFG1); + writel_relaxed(0x2C, HDMI_PHY_PLL_IDAC_ADJ_CFG); + writel_relaxed(0x6, HDMI_PHY_PLL_I_VI_KVCO_CFG); + writel_relaxed(0x77, HDMI_PHY_PLL_SDM_CFG0); + writel_relaxed(0x4C, HDMI_PHY_PLL_SDM_CFG1); + writel_relaxed(0x00, HDMI_PHY_PLL_SDM_CFG2); + writel_relaxed(0xC0, HDMI_PHY_PLL_SDM_CFG3); + writel_relaxed(0x00, HDMI_PHY_PLL_SDM_CFG4); + writel_relaxed(0x9A, HDMI_PHY_PLL_SSC_CFG0); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG1); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG2); + writel_relaxed(0x20, HDMI_PHY_PLL_SSC_CFG3); + writel_relaxed(0xF4, HDMI_PHY_PLL_VCOCAL_CFG0); + writel_relaxed(0x02, HDMI_PHY_PLL_VCOCAL_CFG1); + writel_relaxed(0x2B, HDMI_PHY_PLL_VCOCAL_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG3); + writel_relaxed(0x86, HDMI_PHY_PLL_VCOCAL_CFG4); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG5); + writel_relaxed(0x33, HDMI_PHY_PLL_VCOCAL_CFG6); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG7); + break; + + case 27000000: + /* 576p50/576i50 case */ + writel_relaxed(0x32, HDMI_PHY_PLL_REFCLK_CFG); + writel_relaxed(0x2, HDMI_PHY_PLL_CHRG_PUMP_CFG); + writel_relaxed(0x01, HDMI_PHY_PLL_LOOP_FLT_CFG0); + writel_relaxed(0x33, HDMI_PHY_PLL_LOOP_FLT_CFG1); + writel_relaxed(0x2C, HDMI_PHY_PLL_IDAC_ADJ_CFG); + writel_relaxed(0x6, HDMI_PHY_PLL_I_VI_KVCO_CFG); + writel_relaxed(0x7B, HDMI_PHY_PLL_SDM_CFG0); + writel_relaxed(0x01, HDMI_PHY_PLL_SDM_CFG1); + writel_relaxed(0x4C, HDMI_PHY_PLL_SDM_CFG2); + writel_relaxed(0xC0, HDMI_PHY_PLL_SDM_CFG3); + writel_relaxed(0x00, HDMI_PHY_PLL_SDM_CFG4); + writel_relaxed(0x9A, HDMI_PHY_PLL_SSC_CFG0); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG1); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG3); + writel_relaxed(0x2a, HDMI_PHY_PLL_VCOCAL_CFG0); + writel_relaxed(0x03, HDMI_PHY_PLL_VCOCAL_CFG1); + writel_relaxed(0x2B, HDMI_PHY_PLL_VCOCAL_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG3); + writel_relaxed(0x86, HDMI_PHY_PLL_VCOCAL_CFG4); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG5); + writel_relaxed(0x33, HDMI_PHY_PLL_VCOCAL_CFG6); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG7); + break; + + case 74250000: + /* 720p60/720p50/1080i60/1080i50 + * 1080p24/1080p30/1080p25 case + */ + writel_relaxed(0x12, HDMI_PHY_PLL_REFCLK_CFG); + writel_relaxed(0x01, HDMI_PHY_PLL_LOOP_FLT_CFG0); + writel_relaxed(0x33, HDMI_PHY_PLL_LOOP_FLT_CFG1); + writel_relaxed(0x76, HDMI_PHY_PLL_SDM_CFG0); + writel_relaxed(0xE6, HDMI_PHY_PLL_VCOCAL_CFG0); + writel_relaxed(0x02, HDMI_PHY_PLL_VCOCAL_CFG1); + break; + + case 148500000: + /* 1080p60/1080p50 case */ + writel_relaxed(0x2, HDMI_PHY_PLL_REFCLK_CFG); + writel_relaxed(0x2, HDMI_PHY_PLL_CHRG_PUMP_CFG); + writel_relaxed(0x01, HDMI_PHY_PLL_LOOP_FLT_CFG0); + writel_relaxed(0x33, HDMI_PHY_PLL_LOOP_FLT_CFG1); + writel_relaxed(0x2C, HDMI_PHY_PLL_IDAC_ADJ_CFG); + writel_relaxed(0x6, HDMI_PHY_PLL_I_VI_KVCO_CFG); + writel_relaxed(0x76, HDMI_PHY_PLL_SDM_CFG0); + writel_relaxed(0x01, HDMI_PHY_PLL_SDM_CFG1); + writel_relaxed(0x4C, HDMI_PHY_PLL_SDM_CFG2); + writel_relaxed(0xC0, HDMI_PHY_PLL_SDM_CFG3); + writel_relaxed(0x00, HDMI_PHY_PLL_SDM_CFG4); + writel_relaxed(0x9A, HDMI_PHY_PLL_SSC_CFG0); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG1); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_SSC_CFG3); + writel_relaxed(0xe6, HDMI_PHY_PLL_VCOCAL_CFG0); + writel_relaxed(0x02, HDMI_PHY_PLL_VCOCAL_CFG1); + writel_relaxed(0x2B, HDMI_PHY_PLL_VCOCAL_CFG2); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG3); + writel_relaxed(0x86, HDMI_PHY_PLL_VCOCAL_CFG4); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG5); + writel_relaxed(0x33, HDMI_PHY_PLL_VCOCAL_CFG6); + writel_relaxed(0x00, HDMI_PHY_PLL_VCOCAL_CFG7); + break; + } + + /* Make sure writes complete before disabling iface clock */ + mb(); + + if (set_power_dwn) + hdmi_pll_enable(); + + current_rate = rate; + if (!ahb_enabled) + writel_relaxed(ahb_en_reg & ~BIT(4), AHB_EN_REG); + + return 0; +} diff --git a/arch/arm/mach-msm/clock-dss-8960.h b/arch/arm/mach-msm/clock-dss-8960.h new file mode 100644 index 00000000000..4734cdea9ba --- /dev/null +++ b/arch/arm/mach-msm/clock-dss-8960.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_DSS_8960 +#define __ARCH_ARM_MACH_MSM_CLOCK_DSS_8960 + +int hdmi_pll_enable(void); +void hdmi_pll_disable(void); +unsigned hdmi_pll_get_rate(void); +int hdmi_pll_set_rate(unsigned rate); + +#endif diff --git a/arch/arm/mach-msm/clock-dummy.c b/arch/arm/mach-msm/clock-dummy.c new file mode 100644 index 00000000000..54c9de80916 --- /dev/null +++ b/arch/arm/mach-msm/clock-dummy.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "clock.h" + +static int dummy_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + return 0; +} + +static int dummy_clk_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static int dummy_clk_set_max_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static int dummy_clk_set_flags(struct clk *clk, unsigned flags) +{ + return 0; +} + +static unsigned long dummy_clk_get_rate(struct clk *clk) +{ + return 0; +} + +static long dummy_clk_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} + +static struct clk_ops clk_ops_dummy = { + .reset = dummy_clk_reset, + .set_rate = dummy_clk_set_rate, + .set_max_rate = dummy_clk_set_max_rate, + .set_flags = dummy_clk_set_flags, + .get_rate = dummy_clk_get_rate, + .round_rate = dummy_clk_round_rate, +}; + +struct clk dummy_clk = { + .dbg_name = "dummy_clk", + .ops = &clk_ops_dummy, + CLK_INIT(dummy_clk), +}; diff --git a/arch/arm/mach-msm/clock-fsm9xxx.c b/arch/arm/mach-msm/clock-fsm9xxx.c new file mode 100644 index 00000000000..13a5b65829f --- /dev/null +++ b/arch/arm/mach-msm/clock-fsm9xxx.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include + +#include "clock.h" + +/* + * Clocks + */ + +static struct clk_lookup msm_clocks_fsm9xxx[] = { + CLK_DUMMY("core_clk", ADM0_CLK, "msm_dmov", OFF), + CLK_DUMMY("core_clk", UART1_CLK, "msm_serial.0", OFF), + CLK_DUMMY("core_clk", CE_CLK, "qce.0", OFF), + CLK_DUMMY("core_clk", CE_CLK, "qcota.0", OFF), + CLK_DUMMY("core_clk", CE_CLK, "qcrypto.0", OFF), +}; + +struct clock_init_data fsm9xxx_clock_init_data __initdata = { + .table = msm_clocks_fsm9xxx, + .size = ARRAY_SIZE(msm_clocks_fsm9xxx), +}; diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c new file mode 100644 index 00000000000..02e103d4f6c --- /dev/null +++ b/arch/arm/mach-msm/clock-local.c @@ -0,0 +1,966 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "clock.h" +#include "clock-local.h" + +#ifdef CONFIG_MSM_SECURE_IO +#undef readl_relaxed +#undef writel_relaxed +#define readl_relaxed secure_readl +#define writel_relaxed secure_writel +#endif + +/* + * When enabling/disabling a clock, check the halt bit up to this number + * number of times (with a 1 us delay in between) before continuing. + */ +#define HALT_CHECK_MAX_LOOPS 200 +/* For clock without halt checking, wait this long after enables/disables. */ +#define HALT_CHECK_DELAY_US 10 + +DEFINE_SPINLOCK(local_clock_reg_lock); +struct clk_freq_tbl rcg_dummy_freq = F_END; + +/* + * Common Set-Rate Functions + */ + +/* For clocks with MND dividers. */ +void set_rate_mnd(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + uint32_t ns_reg_val, ctl_reg_val; + + /* Assert MND reset. */ + ns_reg_val = readl_relaxed(clk->ns_reg); + ns_reg_val |= BIT(7); + writel_relaxed(ns_reg_val, clk->ns_reg); + + /* Program M and D values. */ + writel_relaxed(nf->md_val, clk->md_reg); + + /* If the clock has a separate CC register, program it. */ + if (clk->ns_reg != clk->b.ctl_reg) { + ctl_reg_val = readl_relaxed(clk->b.ctl_reg); + ctl_reg_val &= ~(clk->ctl_mask); + ctl_reg_val |= nf->ctl_val; + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); + } + + /* Deassert MND reset. */ + ns_reg_val &= ~BIT(7); + writel_relaxed(ns_reg_val, clk->ns_reg); +} + +void set_rate_nop(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + /* + * Nothing to do for fixed-rate or integer-divider clocks. Any settings + * in NS registers are applied in the enable path, since power can be + * saved by leaving an un-clocked or slowly-clocked source selected + * until the clock is enabled. + */ +} + +void set_rate_mnd_8(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + uint32_t ctl_reg_val; + + /* Assert MND reset. */ + ctl_reg_val = readl_relaxed(clk->b.ctl_reg); + ctl_reg_val |= BIT(8); + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); + + /* Program M and D values. */ + writel_relaxed(nf->md_val, clk->md_reg); + + /* Program MN counter Enable and Mode. */ + ctl_reg_val &= ~(clk->ctl_mask); + ctl_reg_val |= nf->ctl_val; + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); + + /* Deassert MND reset. */ + ctl_reg_val &= ~BIT(8); + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); +} + +void set_rate_mnd_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + struct bank_masks *banks = clk->bank_info; + const struct bank_mask_info *new_bank_masks; + const struct bank_mask_info *old_bank_masks; + uint32_t ns_reg_val, ctl_reg_val; + uint32_t bank_sel; + + /* + * Determine active bank and program the other one. If the clock is + * off, program the active bank since bank switching won't work if + * both banks aren't running. + */ + ctl_reg_val = readl_relaxed(clk->b.ctl_reg); + bank_sel = !!(ctl_reg_val & banks->bank_sel_mask); + /* If clock isn't running, don't switch banks. */ + bank_sel ^= (!clk->enabled || clk->current_freq->freq_hz == 0); + if (bank_sel == 0) { + new_bank_masks = &banks->bank1_mask; + old_bank_masks = &banks->bank0_mask; + } else { + new_bank_masks = &banks->bank0_mask; + old_bank_masks = &banks->bank1_mask; + } + + ns_reg_val = readl_relaxed(clk->ns_reg); + + /* Assert bank MND reset. */ + ns_reg_val |= new_bank_masks->rst_mask; + writel_relaxed(ns_reg_val, clk->ns_reg); + + /* + * Program NS only if the clock is enabled, since the NS will be set + * as part of the enable procedure and should remain with a low-power + * MUX input selected until then. + */ + if (clk->enabled) { + ns_reg_val &= ~(new_bank_masks->ns_mask); + ns_reg_val |= (nf->ns_val & new_bank_masks->ns_mask); + writel_relaxed(ns_reg_val, clk->ns_reg); + } + + writel_relaxed(nf->md_val, new_bank_masks->md_reg); + + /* Enable counter only if clock is enabled. */ + if (clk->enabled) + ctl_reg_val |= new_bank_masks->mnd_en_mask; + else + ctl_reg_val &= ~(new_bank_masks->mnd_en_mask); + + ctl_reg_val &= ~(new_bank_masks->mode_mask); + ctl_reg_val |= (nf->ctl_val & new_bank_masks->mode_mask); + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); + + /* Deassert bank MND reset. */ + ns_reg_val &= ~(new_bank_masks->rst_mask); + writel_relaxed(ns_reg_val, clk->ns_reg); + + /* + * Switch to the new bank if clock is running. If it isn't, then + * no switch is necessary since we programmed the active bank. + */ + if (clk->enabled && clk->current_freq->freq_hz) { + ctl_reg_val ^= banks->bank_sel_mask; + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); + /* + * Wait at least 6 cycles of slowest bank's clock + * for the glitch-free MUX to fully switch sources. + */ + mb(); + udelay(1); + + /* Disable old bank's MN counter. */ + ctl_reg_val &= ~(old_bank_masks->mnd_en_mask); + writel_relaxed(ctl_reg_val, clk->b.ctl_reg); + + /* Program old bank to a low-power source and divider. */ + ns_reg_val &= ~(old_bank_masks->ns_mask); + ns_reg_val |= (clk->freq_tbl->ns_val & old_bank_masks->ns_mask); + writel_relaxed(ns_reg_val, clk->ns_reg); + } + + /* Update the MND_EN and NS masks to match the current bank. */ + clk->mnd_en_mask = new_bank_masks->mnd_en_mask; + clk->ns_mask = new_bank_masks->ns_mask; +} + +void set_rate_div_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf) +{ + struct bank_masks *banks = clk->bank_info; + const struct bank_mask_info *new_bank_masks; + const struct bank_mask_info *old_bank_masks; + uint32_t ns_reg_val, bank_sel; + + /* + * Determine active bank and program the other one. If the clock is + * off, program the active bank since bank switching won't work if + * both banks aren't running. + */ + ns_reg_val = readl_relaxed(clk->ns_reg); + bank_sel = !!(ns_reg_val & banks->bank_sel_mask); + /* If clock isn't running, don't switch banks. */ + bank_sel ^= (!clk->enabled || clk->current_freq->freq_hz == 0); + if (bank_sel == 0) { + new_bank_masks = &banks->bank1_mask; + old_bank_masks = &banks->bank0_mask; + } else { + new_bank_masks = &banks->bank0_mask; + old_bank_masks = &banks->bank1_mask; + } + + /* + * Program NS only if the clock is enabled, since the NS will be set + * as part of the enable procedure and should remain with a low-power + * MUX input selected until then. + */ + if (clk->enabled) { + ns_reg_val &= ~(new_bank_masks->ns_mask); + ns_reg_val |= (nf->ns_val & new_bank_masks->ns_mask); + writel_relaxed(ns_reg_val, clk->ns_reg); + } + + /* + * Switch to the new bank if clock is running. If it isn't, then + * no switch is necessary since we programmed the active bank. + */ + if (clk->enabled && clk->current_freq->freq_hz) { + ns_reg_val ^= banks->bank_sel_mask; + writel_relaxed(ns_reg_val, clk->ns_reg); + /* + * Wait at least 6 cycles of slowest bank's clock + * for the glitch-free MUX to fully switch sources. + */ + mb(); + udelay(1); + + /* Program old bank to a low-power source and divider. */ + ns_reg_val &= ~(old_bank_masks->ns_mask); + ns_reg_val |= (clk->freq_tbl->ns_val & old_bank_masks->ns_mask); + writel_relaxed(ns_reg_val, clk->ns_reg); + } + + /* Update the NS mask to match the current bank. */ + clk->ns_mask = new_bank_masks->ns_mask; +} + +/* + * Clock enable/disable functions + */ + +/* Return non-zero if a clock status registers shows the clock is halted. */ +static int branch_clk_is_halted(const struct branch *clk) +{ + int invert = (clk->halt_check == ENABLE); + int status_bit = readl_relaxed(clk->halt_reg) & BIT(clk->halt_bit); + return invert ? !status_bit : status_bit; +} + +static int branch_in_hwcg_mode(const struct branch *b) +{ + if (!b->hwcg_mask) + return 0; + + return !!(readl_relaxed(b->hwcg_reg) & b->hwcg_mask); +} + +void __branch_clk_enable_reg(const struct branch *clk, const char *name) +{ + u32 reg_val; + + if (clk->en_mask) { + reg_val = readl_relaxed(clk->ctl_reg); + reg_val |= clk->en_mask; + writel_relaxed(reg_val, clk->ctl_reg); + } + + /* + * Use a memory barrier since some halt status registers are + * not within the same 1K segment as the branch/root enable + * registers. It's also needed in the udelay() case to ensure + * the delay starts after the branch enable. + */ + mb(); + + /* Skip checking halt bit if the clock is in hardware gated mode */ + if (branch_in_hwcg_mode(clk)) + return; + + /* Wait for clock to enable before returning. */ + if (clk->halt_check == DELAY) + udelay(HALT_CHECK_DELAY_US); + else if (clk->halt_check == ENABLE || clk->halt_check == HALT + || clk->halt_check == ENABLE_VOTED + || clk->halt_check == HALT_VOTED) { + int count; + + /* Wait up to HALT_CHECK_MAX_LOOPS for clock to enable. */ + for (count = HALT_CHECK_MAX_LOOPS; branch_clk_is_halted(clk) + && count > 0; count--) + udelay(1); + WARN(count == 0, "%s status stuck at 'off'", name); + } +} + +/* Perform any register operations required to enable the clock. */ +static void __rcg_clk_enable_reg(struct rcg_clk *clk) +{ + u32 reg_val; + void __iomem *const reg = clk->b.ctl_reg; + + WARN(clk->current_freq == &rcg_dummy_freq, + "Attempting to enable %s before setting its rate. " + "Set the rate first!\n", clk->c.dbg_name); + + /* + * Program the NS register, if applicable. NS registers are not + * set in the set_rate path because power can be saved by deferring + * the selection of a clocked source until the clock is enabled. + */ + if (clk->ns_mask) { + reg_val = readl_relaxed(clk->ns_reg); + reg_val &= ~(clk->ns_mask); + reg_val |= (clk->current_freq->ns_val & clk->ns_mask); + writel_relaxed(reg_val, clk->ns_reg); + } + + /* Enable MN counter, if applicable. */ + reg_val = readl_relaxed(reg); + if (clk->current_freq->md_val) { + reg_val |= clk->mnd_en_mask; + writel_relaxed(reg_val, reg); + } + /* Enable root. */ + if (clk->root_en_mask) { + reg_val |= clk->root_en_mask; + writel_relaxed(reg_val, reg); + } + __branch_clk_enable_reg(&clk->b, clk->c.dbg_name); +} + +/* Perform any register operations required to disable the branch. */ +u32 __branch_clk_disable_reg(const struct branch *clk, const char *name) +{ + u32 reg_val; + + reg_val = readl_relaxed(clk->ctl_reg); + if (clk->en_mask) { + reg_val &= ~(clk->en_mask); + writel_relaxed(reg_val, clk->ctl_reg); + } + + /* + * Use a memory barrier since some halt status registers are + * not within the same K segment as the branch/root enable + * registers. It's also needed in the udelay() case to ensure + * the delay starts after the branch disable. + */ + mb(); + + /* Skip checking halt bit if the clock is in hardware gated mode */ + if (branch_in_hwcg_mode(clk)) + return reg_val; + + /* Wait for clock to disable before continuing. */ + if (clk->halt_check == DELAY || clk->halt_check == ENABLE_VOTED + || clk->halt_check == HALT_VOTED) + udelay(HALT_CHECK_DELAY_US); + else if (clk->halt_check == ENABLE || clk->halt_check == HALT) { + int count; + + /* Wait up to HALT_CHECK_MAX_LOOPS for clock to disable. */ + for (count = HALT_CHECK_MAX_LOOPS; !branch_clk_is_halted(clk) + && count > 0; count--) + udelay(1); + WARN(count == 0, "%s status stuck at 'on'", name); + } + + return reg_val; +} + +/* Perform any register operations required to disable the generator. */ +static void __rcg_clk_disable_reg(struct rcg_clk *clk) +{ + void __iomem *const reg = clk->b.ctl_reg; + uint32_t reg_val; + + reg_val = __branch_clk_disable_reg(&clk->b, clk->c.dbg_name); + /* Disable root. */ + if (clk->root_en_mask) { + reg_val &= ~(clk->root_en_mask); + writel_relaxed(reg_val, reg); + } + /* Disable MN counter, if applicable. */ + if (clk->current_freq->md_val) { + reg_val &= ~(clk->mnd_en_mask); + writel_relaxed(reg_val, reg); + } + /* + * Program NS register to low-power value with an un-clocked or + * slowly-clocked source selected. + */ + if (clk->ns_mask) { + reg_val = readl_relaxed(clk->ns_reg); + reg_val &= ~(clk->ns_mask); + reg_val |= (clk->freq_tbl->ns_val & clk->ns_mask); + writel_relaxed(reg_val, clk->ns_reg); + } +} + +/* Enable a rate-settable clock. */ +static int rcg_clk_enable(struct clk *c) +{ + unsigned long flags; + struct rcg_clk *clk = to_rcg_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __rcg_clk_enable_reg(clk); + clk->enabled = true; + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +/* Disable a rate-settable clock. */ +static void rcg_clk_disable(struct clk *c) +{ + unsigned long flags; + struct rcg_clk *clk = to_rcg_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __rcg_clk_disable_reg(clk); + clk->enabled = false; + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +/* + * Frequency-related functions + */ + +/* Set a clock to an exact rate. */ +static int rcg_clk_set_rate(struct clk *c, unsigned long rate) +{ + struct rcg_clk *clk = to_rcg_clk(c); + struct clk_freq_tbl *nf, *cf; + struct clk *chld; + int rc = 0; + + for (nf = clk->freq_tbl; nf->freq_hz != FREQ_END + && nf->freq_hz != rate; nf++) + ; + + if (nf->freq_hz == FREQ_END) + return -EINVAL; + + cf = clk->current_freq; + + if (clk->enabled) { + /* Enable source clock dependency for the new freq. */ + rc = clk_enable(nf->src_clk); + if (rc) + return rc; + } + + spin_lock(&local_clock_reg_lock); + + /* Disable branch if clock isn't dual-banked with a glitch-free MUX. */ + if (!clk->bank_info) { + /* Disable all branches to prevent glitches. */ + list_for_each_entry(chld, &clk->c.children, siblings) { + struct branch_clk *x = to_branch_clk(chld); + /* + * We don't need to grab the child's lock because + * we hold the local_clock_reg_lock and 'enabled' is + * only modified within lock. + */ + if (x->enabled) + __branch_clk_disable_reg(&x->b, x->c.dbg_name); + } + if (clk->enabled) + __rcg_clk_disable_reg(clk); + } + + /* Perform clock-specific frequency switch operations. */ + BUG_ON(!clk->set_rate); + clk->set_rate(clk, nf); + + /* + * Current freq must be updated before __rcg_clk_enable_reg() + * is called to make sure the MNCNTR_EN bit is set correctly. + */ + clk->current_freq = nf; + + /* Enable any clocks that were disabled. */ + if (!clk->bank_info) { + if (clk->enabled) + __rcg_clk_enable_reg(clk); + /* Enable only branches that were ON before. */ + list_for_each_entry(chld, &clk->c.children, siblings) { + struct branch_clk *x = to_branch_clk(chld); + if (x->enabled) + __branch_clk_enable_reg(&x->b, x->c.dbg_name); + } + } + + spin_unlock(&local_clock_reg_lock); + + /* Release source requirements of the old freq. */ + if (clk->enabled) + clk_disable(cf->src_clk); + + return rc; +} + +/* Check if a clock is currently enabled. */ +static int rcg_clk_is_enabled(struct clk *clk) +{ + return to_rcg_clk(clk)->enabled; +} + +/* Return a supported rate that's at least the specified rate. */ +static long rcg_clk_round_rate(struct clk *c, unsigned long rate) +{ + struct rcg_clk *clk = to_rcg_clk(c); + struct clk_freq_tbl *f; + + for (f = clk->freq_tbl; f->freq_hz != FREQ_END; f++) + if (f->freq_hz >= rate) + return f->freq_hz; + + return -EPERM; +} + +/* Return the nth supported frequency for a given clock. */ +static int rcg_clk_list_rate(struct clk *c, unsigned n) +{ + struct rcg_clk *clk = to_rcg_clk(c); + + if (!clk->freq_tbl || clk->freq_tbl->freq_hz == FREQ_END) + return -ENXIO; + + return (clk->freq_tbl + n)->freq_hz; +} + +static struct clk *rcg_clk_get_parent(struct clk *clk) +{ + return to_rcg_clk(clk)->current_freq->src_clk; +} + +/* Disable hw clock gating if not set at boot */ +enum handoff branch_handoff(struct branch *clk, struct clk *c) +{ + if (!branch_in_hwcg_mode(clk)) { + clk->hwcg_mask = 0; + c->flags &= ~CLKFLAG_HWCG; + if (readl_relaxed(clk->ctl_reg) & clk->en_mask) + return HANDOFF_ENABLED_CLK; + } else { + c->flags |= CLKFLAG_HWCG; + } + return HANDOFF_DISABLED_CLK; +} + +static enum handoff branch_clk_handoff(struct clk *c) +{ + struct branch_clk *clk = to_branch_clk(c); + return branch_handoff(&clk->b, &clk->c); +} + +static enum handoff rcg_clk_handoff(struct clk *c) +{ + struct rcg_clk *clk = to_rcg_clk(c); + uint32_t ctl_val, ns_val, md_val, ns_mask; + struct clk_freq_tbl *freq; + enum handoff ret; + + ctl_val = readl_relaxed(clk->b.ctl_reg); + ret = branch_handoff(&clk->b, &clk->c); + if (ret == HANDOFF_DISABLED_CLK) + return HANDOFF_DISABLED_CLK; + + if (clk->bank_info) { + const struct bank_masks *bank_masks = clk->bank_info; + const struct bank_mask_info *bank_info; + if (!(ctl_val & bank_masks->bank_sel_mask)) + bank_info = &bank_masks->bank0_mask; + else + bank_info = &bank_masks->bank1_mask; + + ns_mask = bank_info->ns_mask; + md_val = bank_info->md_reg ? + readl_relaxed(bank_info->md_reg) : 0; + } else { + ns_mask = clk->ns_mask; + md_val = clk->md_reg ? readl_relaxed(clk->md_reg) : 0; + } + if (!ns_mask) + return HANDOFF_UNKNOWN_RATE; + ns_val = readl_relaxed(clk->ns_reg) & ns_mask; + for (freq = clk->freq_tbl; freq->freq_hz != FREQ_END; freq++) { + if ((freq->ns_val & ns_mask) == ns_val && + (!freq->md_val || freq->md_val == md_val)) { + pr_info("%s rate=%d\n", clk->c.dbg_name, freq->freq_hz); + break; + } + } + if (freq->freq_hz == FREQ_END) + return HANDOFF_UNKNOWN_RATE; + + clk->current_freq = freq; + c->rate = freq->freq_hz; + + return HANDOFF_ENABLED_CLK; +} + +struct clk_ops clk_ops_empty; + +struct fixed_clk gnd_clk = { + .c = { + .dbg_name = "ground_clk", + .ops = &clk_ops_empty, + CLK_INIT(gnd_clk.c), + }, +}; + +static int branch_clk_enable(struct clk *clk) +{ + unsigned long flags; + struct branch_clk *branch = to_branch_clk(clk); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __branch_clk_enable_reg(&branch->b, branch->c.dbg_name); + branch->enabled = true; + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +static void branch_clk_disable(struct clk *clk) +{ + unsigned long flags; + struct branch_clk *branch = to_branch_clk(clk); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __branch_clk_disable_reg(&branch->b, branch->c.dbg_name); + branch->enabled = false; + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +static struct clk *branch_clk_get_parent(struct clk *clk) +{ + struct branch_clk *branch = to_branch_clk(clk); + return branch->parent; +} + +static int branch_clk_is_enabled(struct clk *clk) +{ + struct branch_clk *branch = to_branch_clk(clk); + return branch->enabled; +} + +static void branch_enable_hwcg(struct branch *b) +{ + unsigned long flags; + u32 reg_val; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + reg_val = readl_relaxed(b->hwcg_reg); + reg_val |= b->hwcg_mask; + writel_relaxed(reg_val, b->hwcg_reg); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +static void branch_disable_hwcg(struct branch *b) +{ + unsigned long flags; + u32 reg_val; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + reg_val = readl_relaxed(b->hwcg_reg); + reg_val &= ~b->hwcg_mask; + writel_relaxed(reg_val, b->hwcg_reg); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +static void branch_clk_enable_hwcg(struct clk *clk) +{ + struct branch_clk *branch = to_branch_clk(clk); + branch_enable_hwcg(&branch->b); +} + +static void branch_clk_disable_hwcg(struct clk *clk) +{ + struct branch_clk *branch = to_branch_clk(clk); + branch_disable_hwcg(&branch->b); +} + +static int branch_set_flags(struct branch *b, unsigned flags) +{ + unsigned long irq_flags; + u32 reg_val; + int ret = 0; + + if (!b->retain_reg) + return -EPERM; + + spin_lock_irqsave(&local_clock_reg_lock, irq_flags); + reg_val = readl_relaxed(b->retain_reg); + switch (flags) { + case CLKFLAG_RETAIN: + reg_val |= b->retain_mask; + break; + case CLKFLAG_NORETAIN: + reg_val &= ~b->retain_mask; + break; + default: + ret = -EINVAL; + } + writel_relaxed(reg_val, b->retain_reg); + spin_unlock_irqrestore(&local_clock_reg_lock, irq_flags); + + return ret; +} + +static int branch_clk_set_flags(struct clk *clk, unsigned flags) +{ + return branch_set_flags(&to_branch_clk(clk)->b, flags); +} + +static int branch_clk_in_hwcg_mode(struct clk *c) +{ + struct branch_clk *clk = to_branch_clk(c); + return branch_in_hwcg_mode(&clk->b); +} + +static void rcg_clk_enable_hwcg(struct clk *clk) +{ + struct rcg_clk *rcg = to_rcg_clk(clk); + branch_enable_hwcg(&rcg->b); +} + +static void rcg_clk_disable_hwcg(struct clk *clk) +{ + struct rcg_clk *rcg = to_rcg_clk(clk); + branch_disable_hwcg(&rcg->b); +} + +static int rcg_clk_in_hwcg_mode(struct clk *c) +{ + struct rcg_clk *clk = to_rcg_clk(c); + return branch_in_hwcg_mode(&clk->b); +} + +static int rcg_clk_set_flags(struct clk *clk, unsigned flags) +{ + return branch_set_flags(&to_rcg_clk(clk)->b, flags); +} + +int branch_reset(struct branch *b, enum clk_reset_action action) +{ + int ret = 0; + u32 reg_val; + unsigned long flags; + + if (!b->reset_reg) + return -EPERM; + + /* Disable hw gating when asserting a reset */ + if (b->hwcg_mask && action == CLK_RESET_ASSERT) + branch_disable_hwcg(b); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + /* Assert/Deassert reset */ + reg_val = readl_relaxed(b->reset_reg); + switch (action) { + case CLK_RESET_ASSERT: + reg_val |= b->reset_mask; + break; + case CLK_RESET_DEASSERT: + reg_val &= ~b->reset_mask; + break; + default: + ret = -EINVAL; + } + writel_relaxed(reg_val, b->reset_reg); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Enable hw gating when deasserting a reset */ + if (b->hwcg_mask && action == CLK_RESET_DEASSERT) + branch_enable_hwcg(b); + /* Make sure write is issued before returning. */ + mb(); + return ret; +} + +static int branch_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + return branch_reset(&to_branch_clk(clk)->b, action); +} + +struct clk_ops clk_ops_branch = { + .enable = branch_clk_enable, + .disable = branch_clk_disable, + .enable_hwcg = branch_clk_enable_hwcg, + .disable_hwcg = branch_clk_disable_hwcg, + .in_hwcg_mode = branch_clk_in_hwcg_mode, + .auto_off = branch_clk_disable, + .is_enabled = branch_clk_is_enabled, + .reset = branch_clk_reset, + .get_parent = branch_clk_get_parent, + .handoff = branch_clk_handoff, + .set_flags = branch_clk_set_flags, +}; + +struct clk_ops clk_ops_reset = { + .reset = branch_clk_reset, +}; + +static int rcg_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + return branch_reset(&to_rcg_clk(clk)->b, action); +} + +struct clk_ops clk_ops_rcg = { + .enable = rcg_clk_enable, + .disable = rcg_clk_disable, + .enable_hwcg = rcg_clk_enable_hwcg, + .disable_hwcg = rcg_clk_disable_hwcg, + .in_hwcg_mode = rcg_clk_in_hwcg_mode, + .auto_off = rcg_clk_disable, + .handoff = rcg_clk_handoff, + .set_rate = rcg_clk_set_rate, + .list_rate = rcg_clk_list_rate, + .is_enabled = rcg_clk_is_enabled, + .round_rate = rcg_clk_round_rate, + .reset = rcg_clk_reset, + .get_parent = rcg_clk_get_parent, + .set_flags = rcg_clk_set_flags, +}; + +static int cdiv_clk_enable(struct clk *c) +{ + unsigned long flags; + struct cdiv_clk *clk = to_cdiv_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __branch_clk_enable_reg(&clk->b, clk->c.dbg_name); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +static void cdiv_clk_disable(struct clk *c) +{ + unsigned long flags; + struct cdiv_clk *clk = to_cdiv_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + __branch_clk_disable_reg(&clk->b, clk->c.dbg_name); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +static int cdiv_clk_set_rate(struct clk *c, unsigned long rate) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + u32 reg_val; + + if (rate > clk->max_div) + return -EINVAL; + + spin_lock(&local_clock_reg_lock); + reg_val = readl_relaxed(clk->ns_reg); + reg_val &= ~(clk->ext_mask | (clk->max_div - 1) << clk->div_offset); + /* Non-zero rates mean set a divider, zero means use external input */ + if (rate) + reg_val |= (rate - 1) << clk->div_offset; + else + reg_val |= clk->ext_mask; + writel_relaxed(reg_val, clk->ns_reg); + spin_unlock(&local_clock_reg_lock); + + clk->cur_div = rate; + return 0; +} + +static unsigned long cdiv_clk_get_rate(struct clk *c) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + return clk->cur_div; +} + +static long cdiv_clk_round_rate(struct clk *c, unsigned long rate) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + return rate > clk->max_div ? -EPERM : rate; +} + +static int cdiv_clk_list_rate(struct clk *c, unsigned n) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + return n > clk->max_div ? -ENXIO : n; +} + +static enum handoff cdiv_clk_handoff(struct clk *c) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + enum handoff ret; + u32 reg_val; + + ret = branch_handoff(&clk->b, &clk->c); + if (ret == HANDOFF_DISABLED_CLK) + return ret; + + reg_val = readl_relaxed(clk->ns_reg); + if (reg_val & clk->ext_mask) { + clk->cur_div = 0; + } else { + reg_val >>= clk->div_offset; + clk->cur_div = (reg_val & (clk->max_div - 1)) + 1; + } + + return HANDOFF_ENABLED_CLK; +} + +static void cdiv_clk_enable_hwcg(struct clk *c) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + branch_enable_hwcg(&clk->b); +} + +static void cdiv_clk_disable_hwcg(struct clk *c) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + branch_disable_hwcg(&clk->b); +} + +static int cdiv_clk_in_hwcg_mode(struct clk *c) +{ + struct cdiv_clk *clk = to_cdiv_clk(c); + return branch_in_hwcg_mode(&clk->b); +} + +struct clk_ops clk_ops_cdiv = { + .enable = cdiv_clk_enable, + .disable = cdiv_clk_disable, + .in_hwcg_mode = cdiv_clk_in_hwcg_mode, + .enable_hwcg = cdiv_clk_enable_hwcg, + .disable_hwcg = cdiv_clk_disable_hwcg, + .auto_off = cdiv_clk_disable, + .handoff = cdiv_clk_handoff, + .set_rate = cdiv_clk_set_rate, + .get_rate = cdiv_clk_get_rate, + .list_rate = cdiv_clk_list_rate, + .round_rate = cdiv_clk_round_rate, +}; diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h new file mode 100644 index 00000000000..ffc7057406a --- /dev/null +++ b/arch/arm/mach-msm/clock-local.h @@ -0,0 +1,290 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H +#define __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H + +#include +#include "clock.h" + +#define MN_MODE_DUAL_EDGE 0x2 + +/* MD Registers */ +#define MD4(m_lsb, m, n_lsb, n) \ + ((BVAL((m_lsb+3), m_lsb, m) | BVAL((n_lsb+3), n_lsb, ~(n))) \ + * !!(n)) +#define MD8(m_lsb, m, n_lsb, n) \ + ((BVAL((m_lsb+7), m_lsb, m) | BVAL((n_lsb+7), n_lsb, ~(n))) \ + * !!(n)) +#define MD16(m, n) ((BVAL(31, 16, m) | BVAL(15, 0, ~(n))) * !!(n)) + +/* NS Registers */ +#define NS(n_msb, n_lsb, n, m, mde_lsb, d_msb, d_lsb, d, s_msb, s_lsb, s) \ + (BVAL(n_msb, n_lsb, ~(n-m) * !!(n)) \ + | (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) \ + | BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s)) + +#define NS_MM(n_msb, n_lsb, n, m, d_msb, d_lsb, d, s_msb, s_lsb, s) \ + (BVAL(n_msb, n_lsb, ~(n-m) * !!(n))|BVAL(d_msb, d_lsb, (d-1)) \ + | BVAL(s_msb, s_lsb, s)) + +#define NS_DIVSRC(d_msb, d_lsb, d, s_msb, s_lsb, s) \ + (BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s)) + +#define NS_DIV(d_msb, d_lsb, d) \ + BVAL(d_msb, d_lsb, (d-1)) + +#define NS_SRC_SEL(s_msb, s_lsb, s) \ + BVAL(s_msb, s_lsb, s) + +#define NS_MND_BANKED4(n0_lsb, n1_lsb, n, m, s0_lsb, s1_lsb, s) \ + (BVAL((n0_lsb+3), n0_lsb, ~(n-m) * !!(n)) \ + | BVAL((n1_lsb+3), n1_lsb, ~(n-m) * !!(n)) \ + | BVAL((s0_lsb+2), s0_lsb, s) \ + | BVAL((s1_lsb+2), s1_lsb, s)) + +#define NS_MND_BANKED8(n0_lsb, n1_lsb, n, m, s0_lsb, s1_lsb, s) \ + (BVAL((n0_lsb+7), n0_lsb, ~(n-m) * !!(n)) \ + | BVAL((n1_lsb+7), n1_lsb, ~(n-m) * !!(n)) \ + | BVAL((s0_lsb+2), s0_lsb, s) \ + | BVAL((s1_lsb+2), s1_lsb, s)) + +#define NS_DIVSRC_BANKED(d0_msb, d0_lsb, d1_msb, d1_lsb, d, \ + s0_msb, s0_lsb, s1_msb, s1_lsb, s) \ + (BVAL(d0_msb, d0_lsb, (d-1)) | BVAL(d1_msb, d1_lsb, (d-1)) \ + | BVAL(s0_msb, s0_lsb, s) \ + | BVAL(s1_msb, s1_lsb, s)) + +/* CC Registers */ +#define CC(mde_lsb, n) (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) +#define CC_BANKED(mde0_lsb, mde1_lsb, n) \ + ((BVAL((mde0_lsb+1), mde0_lsb, MN_MODE_DUAL_EDGE) \ + | BVAL((mde1_lsb+1), mde1_lsb, MN_MODE_DUAL_EDGE)) \ + * !!(n)) + +/* + * Clock Definition Macros + */ +#define DEFINE_CLK_MEASURE(name) \ + struct clk name = { \ + .ops = &clk_ops_empty, \ + .dbg_name = #name, \ + CLK_INIT(name), \ + }; \ + +/* + * Generic frequency-definition structs and macros + */ +struct clk_freq_tbl { + const uint32_t freq_hz; + struct clk *const src_clk; + const uint32_t md_val; + const uint32_t ns_val; + const uint32_t ctl_val; + void *const extra_freq_data; +}; + +/* Some clocks have two banks to avoid glitches when switching frequencies. + * The unused bank is programmed while running on the other bank, and + * switched to afterwards. The following two structs describe the banks. */ +struct bank_mask_info { + void *const md_reg; + const uint32_t ns_mask; + const uint32_t rst_mask; + const uint32_t mnd_en_mask; + const uint32_t mode_mask; +}; + +struct bank_masks { + const uint32_t bank_sel_mask; + const struct bank_mask_info bank0_mask; + const struct bank_mask_info bank1_mask; +}; + +#define F_RAW(f, sc, m_v, n_v, c_v, e) { \ + .freq_hz = f, \ + .src_clk = sc, \ + .md_val = m_v, \ + .ns_val = n_v, \ + .ctl_val = c_v, \ + .extra_freq_data = e, \ + } +#define FREQ_END (UINT_MAX-1) +#define F_END { .freq_hz = FREQ_END } + +/** + * struct branch - branch on/off + * @ctl_reg: clock control register + * @en_mask: ORed with @ctl_reg to enable the clock + * @hwcg_reg: hardware clock gating register + * @hwcg_mask: ORed with @hwcg_reg to enable hardware clock gating + * @halt_reg: halt register + * @halt_check: type of halt check to perform + * @halt_bit: ANDed with @halt_reg to test for clock halted + * @reset_reg: reset register + * @reset_mask: ORed with @reset_reg to reset the clock domain + */ +struct branch { + void __iomem *const ctl_reg; + const u32 en_mask; + + void __iomem *hwcg_reg; + u32 hwcg_mask; + + void __iomem *const halt_reg; + const u16 halt_check; + const u16 halt_bit; + + void __iomem *const reset_reg; + const u32 reset_mask; + + void __iomem *const retain_reg; + const u32 retain_mask; +}; + +extern struct clk_ops clk_ops_branch; +extern struct clk_ops clk_ops_reset; + +int branch_reset(struct branch *b, enum clk_reset_action action); +void __branch_clk_enable_reg(const struct branch *clk, const char *name); +u32 __branch_clk_disable_reg(const struct branch *clk, const char *name); +enum handoff branch_handoff(struct branch *clk, struct clk *c); + +/* + * Generic clock-definition struct and macros + */ +struct rcg_clk { + bool enabled; + void *const ns_reg; + void *const md_reg; + + const uint32_t root_en_mask; + uint32_t ns_mask; + const uint32_t ctl_mask; + uint32_t mnd_en_mask; + + void *bank_info; + void (*set_rate)(struct rcg_clk *, struct clk_freq_tbl *); + + struct clk_freq_tbl *freq_tbl; + struct clk_freq_tbl *current_freq; + + struct branch b; + struct clk c; +}; + +static inline struct rcg_clk *to_rcg_clk(struct clk *clk) +{ + return container_of(clk, struct rcg_clk, c); +} + +extern struct clk_ops clk_ops_rcg; + +extern struct clk_freq_tbl rcg_dummy_freq; + +/** + * struct cdiv_clk - integer divider clock with external source selection + * @ns_reg: source select and divider settings register + * @ext_mask: bit to set to select an external source + * @cur_div: current divider setting (or 0 for external source) + * @max_div: maximum divider value supported (must be power of 2) + * @div_offset: number of bits to shift divider left by in @ns_reg + * @b: branch + * @c: clock + */ +struct cdiv_clk { + void __iomem *const ns_reg; + u32 ext_mask; + + unsigned long cur_div; + u8 div_offset; + u32 max_div; + + struct branch b; + struct clk c; +}; + +static inline struct cdiv_clk *to_cdiv_clk(struct clk *clk) +{ + return container_of(clk, struct cdiv_clk, c); +} + +extern struct clk_ops clk_ops_cdiv; + +/** + * struct fixed_clk - fixed rate clock (used for crystal oscillators) + * @c: clk + */ +struct fixed_clk { + struct clk c; +}; + +/** + * struct branch_clk - branch + * @enabled: true if clock is on, false otherwise + * @b: branch + * @parent: clock source + * @c: clk + * + * An on/off switch with a rate derived from the parent. + */ +struct branch_clk { + bool enabled; + struct branch b; + struct clk *parent; + struct clk c; +}; + +static inline struct branch_clk *to_branch_clk(struct clk *clk) +{ + return container_of(clk, struct branch_clk, c); +} + +/** + * struct measure_clk - for rate measurement debug use + * @sample_ticks: sample period in reference clock ticks + * @multiplier: measurement scale-up factor + * @divider: measurement scale-down factor + * @c: clk +*/ +struct measure_clk { + u64 sample_ticks; + u32 multiplier; + u32 divider; + struct clk c; +}; + +extern struct clk_ops clk_ops_empty; + +static inline struct measure_clk *to_measure_clk(struct clk *clk) +{ + return container_of(clk, struct measure_clk, c); +} + +/* + * Variables from clock-local driver + */ +extern spinlock_t local_clock_reg_lock; +extern struct fixed_clk gnd_clk; + +/* + * Generic set-rate implementations + */ +void set_rate_mnd(struct rcg_clk *clk, struct clk_freq_tbl *nf); +void set_rate_nop(struct rcg_clk *clk, struct clk_freq_tbl *nf); +void set_rate_mnd_8(struct rcg_clk *clk, struct clk_freq_tbl *nf); +void set_rate_mnd_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf); +void set_rate_div_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf); + +#endif /* __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H */ + diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c new file mode 100644 index 00000000000..c84cfb89a3c --- /dev/null +++ b/arch/arm/mach-msm/clock-local2.c @@ -0,0 +1,590 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clock.h" +#include "clock-local2.h" + +/* + * When enabling/disabling a clock, check the halt bit up to this number + * number of times (with a 1 us delay in between) before continuing. + */ +#define HALT_CHECK_MAX_LOOPS 200 +/* For clock without halt checking, wait this long after enables/disables. */ +#define HALT_CHECK_DELAY_US 10 + +/* + * When updating an RCG configuration, check the update bit up to this number + * number of times (with a 1 us delay in between) before continuing. + */ +#define UPDATE_CHECK_MAX_LOOPS 200 + +DEFINE_SPINLOCK(local_clock_reg_lock); +struct clk_freq_tbl rcg_dummy_freq = F_END; + +#define CMD_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg) +#define CFG_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x4) +#define M_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x8) +#define N_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0xC) +#define D_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x10) +#define CBCR_REG(x) (*(x)->base + (x)->cbcr_reg) +#define BCR_REG(x) (*(x)->base + (x)->bcr_reg) +#define VOTE_REG(x) (*(x)->base + (x)->vote_reg) + +/* + * Important clock bit positions and masks + */ +#define CMD_RCGR_ROOT_ENABLE_BIT BIT(1) +#define CBCR_BRANCH_ENABLE_BIT BIT(0) +#define CBCR_BRANCH_OFF_BIT BIT(31) +#define CMD_RCGR_CONFIG_UPDATE_BIT BIT(0) +#define CMD_RCGR_ROOT_STATUS_BIT BIT(31) +#define BCR_BLK_ARES_BIT BIT(0) +#define CBCR_HW_CTL_BIT BIT(1) +#define CFG_RCGR_DIV_MASK BM(4, 0) +#define CFG_RCGR_SRC_SEL_MASK BM(10, 8) +#define MND_MODE_MASK BM(13, 12) +#define MND_DUAL_EDGE_MODE_BVAL BVAL(13, 12, 0x2) +#define CMD_RCGR_CONFIG_DIRTY_MASK BM(7, 4) +#define CBCR_BRANCH_CDIV_MASK BM(24, 16) +#define CBCR_BRANCH_CDIV_MASKED(val) BVAL(24, 16, (val)); + +enum branch_state { + BRANCH_ON, + BRANCH_OFF, +}; + +/* + * RCG functions + */ + +/* + * Update an RCG with a new configuration. This may include a new M, N, or D + * value, source selection or pre-divider value. + * + */ +static void rcg_update_config(struct rcg_clk *rcg) +{ + u32 cmd_rcgr_regval, count; + + cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg)); + cmd_rcgr_regval |= CMD_RCGR_CONFIG_UPDATE_BIT; + writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg)); + + /* Wait for update to take effect */ + for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) { + if (!(readl_relaxed(CMD_RCGR_REG(rcg)) & + CMD_RCGR_CONFIG_UPDATE_BIT)) + return; + udelay(1); + } + + WARN(count == 0, "%s: rcg didn't update its configuration.", + rcg->c.dbg_name); +} + +/* RCG set rate function for clocks with Half Integer Dividers. */ +void set_rate_hid(struct rcg_clk *rcg, struct clk_freq_tbl *nf) +{ + u32 cfg_regval; + + cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); + cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK); + cfg_regval |= nf->div_src_val; + writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg)); + + rcg_update_config(rcg); +} + +/* RCG set rate function for clocks with MND & Half Integer Dividers. */ +void set_rate_mnd(struct rcg_clk *rcg, struct clk_freq_tbl *nf) +{ + u32 cfg_regval; + + writel_relaxed(nf->m_val, M_REG(rcg)); + writel_relaxed(nf->n_val, N_REG(rcg)); + writel_relaxed(nf->d_val, D_REG(rcg)); + + cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); + cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK); + cfg_regval |= nf->div_src_val; + + /* Activate or disable the M/N:D divider as necessary */ + cfg_regval &= ~MND_MODE_MASK; + if (nf->n_val != 0) + cfg_regval |= MND_DUAL_EDGE_MODE_BVAL; + writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg)); + + rcg_update_config(rcg); +} + +static int rcg_clk_enable(struct clk *c) +{ + struct rcg_clk *rcg = to_rcg_clk(c); + + WARN(rcg->current_freq == &rcg_dummy_freq, + "Attempting to enable %s before setting its rate. " + "Set the rate first!\n", rcg->c.dbg_name); + + return 0; +} + +static int rcg_clk_set_rate(struct clk *c, unsigned long rate) +{ + struct clk_freq_tbl *cf, *nf; + struct rcg_clk *rcg = to_rcg_clk(c); + int rc = 0; + unsigned long flags; + + for (nf = rcg->freq_tbl; nf->freq_hz != FREQ_END + && nf->freq_hz != rate; nf++) + ; + + if (nf->freq_hz == FREQ_END) + return -EINVAL; + + cf = rcg->current_freq; + + if (rcg->c.count) { + /* TODO: Modify to use the prepare API */ + /* Enable source clock dependency for the new freq. */ + rc = clk_enable(nf->src_clk); + if (rc) + goto out; + } + + BUG_ON(!rcg->set_rate); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Perform clock-specific frequency switch operations. */ + rcg->set_rate(rcg, nf); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Release source requirements of the old freq. */ + if (rcg->c.count) + clk_disable(cf->src_clk); + + rcg->current_freq = nf; +out: + return rc; +} + +/* Return a supported rate that's at least the specified rate. */ +static long rcg_clk_round_rate(struct clk *c, unsigned long rate) +{ + struct rcg_clk *rcg = to_rcg_clk(c); + struct clk_freq_tbl *f; + + for (f = rcg->freq_tbl; f->freq_hz != FREQ_END; f++) + if (f->freq_hz >= rate) + return f->freq_hz; + + return -EPERM; +} + +/* Return the nth supported frequency for a given clock. */ +static int rcg_clk_list_rate(struct clk *c, unsigned n) +{ + struct rcg_clk *rcg = to_rcg_clk(c); + + if (!rcg->freq_tbl || rcg->freq_tbl->freq_hz == FREQ_END) + return -ENXIO; + + return (rcg->freq_tbl + n)->freq_hz; +} + +static struct clk *rcg_clk_get_parent(struct clk *c) +{ + return to_rcg_clk(c)->current_freq->src_clk; +} + +static enum handoff _rcg_clk_handoff(struct rcg_clk *rcg, int has_mnd) +{ + u32 n_regval = 0, m_regval = 0, d_regval = 0; + u32 cfg_regval; + struct clk_freq_tbl *freq; + u32 cmd_rcgr_regval; + + /* Is the root enabled? */ + cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg)); + if ((cmd_rcgr_regval & CMD_RCGR_ROOT_STATUS_BIT)) + return HANDOFF_DISABLED_CLK; + + /* Is there a pending configuration? */ + if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK) + return HANDOFF_UNKNOWN_RATE; + + /* Get values of m, n, d, div and src_sel registers. */ + if (has_mnd) { + m_regval = readl_relaxed(M_REG(rcg)); + n_regval = readl_relaxed(N_REG(rcg)); + d_regval = readl_relaxed(D_REG(rcg)); + + /* + * The n and d values stored in the frequency tables are sign + * extended to 32 bits. The n and d values in the registers are + * sign extended to 8 or 16 bits. Sign extend the values read + * from the registers so that they can be compared to the + * values in the frequency tables. + */ + n_regval |= (n_regval >> 8) ? BM(31, 16) : BM(31, 8); + d_regval |= (d_regval >> 8) ? BM(31, 16) : BM(31, 8); + } + + cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); + cfg_regval &= CFG_RCGR_SRC_SEL_MASK | CFG_RCGR_DIV_MASK + | MND_MODE_MASK; + + /* If mnd counter is present, check if it's in use. */ + has_mnd = (has_mnd) && + ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL); + + /* + * Clear out the mn counter mode bits since we now want to compare only + * the source mux selection and pre-divider values in the registers. + */ + cfg_regval &= ~MND_MODE_MASK; + + /* Figure out what rate the rcg is running at */ + for (freq = rcg->freq_tbl; freq->freq_hz != FREQ_END; freq++) { + if (freq->div_src_val != cfg_regval) + continue; + if (has_mnd) { + if (freq->m_val != m_regval) + continue; + if (freq->n_val != n_regval) + continue; + if (freq->d_val != d_regval) + continue; + } + pr_info("%s rate=%lu\n", rcg->c.dbg_name, freq->freq_hz); + break; + } + + /* No known frequency found */ + if (freq->freq_hz == FREQ_END) + return HANDOFF_UNKNOWN_RATE; + + rcg->current_freq = freq; + rcg->c.rate = freq->freq_hz; + + return HANDOFF_ENABLED_CLK; +} + +static enum handoff rcg_mnd_clk_handoff(struct clk *c) +{ + return _rcg_clk_handoff(to_rcg_clk(c), 1); +} + +static enum handoff rcg_clk_handoff(struct clk *c) +{ + return _rcg_clk_handoff(to_rcg_clk(c), 0); +} + +/* + * Branch clock functions + */ +static void branch_clk_halt_check(u32 halt_check, const char *clk_name, + void __iomem *cbcr_reg, + enum branch_state br_status) +{ + char *status_str = (br_status == BRANCH_ON) ? "off" : "on"; + + /* + * Use a memory barrier since some halt status registers are + * not within the same 1K segment as the branch/root enable + * registers. It's also needed in the udelay() case to ensure + * the delay starts after the branch disable. + */ + mb(); + + if (halt_check == DELAY || halt_check == HALT_VOTED) { + udelay(HALT_CHECK_DELAY_US); + } else if (halt_check == HALT) { + int count; + for (count = HALT_CHECK_MAX_LOOPS; count > 0; count--) { + if (br_status == BRANCH_ON + && !(readl_relaxed(cbcr_reg) + & CBCR_BRANCH_OFF_BIT)) + return; + if (br_status == BRANCH_OFF + && (readl_relaxed(cbcr_reg) + & CBCR_BRANCH_OFF_BIT)) + return; + udelay(1); + } + WARN(count == 0, "%s status stuck %s", clk_name, status_str); + } +} + +static int branch_clk_enable(struct clk *c) +{ + unsigned long flags; + u32 cbcr_val; + struct branch_clk *branch = to_branch_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + cbcr_val = readl_relaxed(CBCR_REG(branch)); + cbcr_val |= CBCR_BRANCH_ENABLE_BIT; + writel_relaxed(cbcr_val, CBCR_REG(branch)); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Wait for clock to enable before continuing. */ + branch_clk_halt_check(branch->halt_check, branch->c.dbg_name, + CBCR_REG(branch), BRANCH_ON); + + return 0; +} + +static void branch_clk_disable(struct clk *c) +{ + unsigned long flags; + struct branch_clk *branch = to_branch_clk(c); + u32 reg_val; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + reg_val = readl_relaxed(CBCR_REG(branch)); + reg_val &= ~CBCR_BRANCH_ENABLE_BIT; + writel_relaxed(reg_val, CBCR_REG(branch)); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Wait for clock to disable before continuing. */ + branch_clk_halt_check(branch->halt_check, branch->c.dbg_name, + CBCR_REG(branch), BRANCH_OFF); +} + +static int branch_cdiv_set_rate(struct branch_clk *branch, unsigned long rate) +{ + unsigned long flags; + u32 regval; + + if (rate > branch->max_div) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + regval = readl_relaxed(CBCR_REG(branch)); + regval &= ~CBCR_BRANCH_CDIV_MASK; + regval |= CBCR_BRANCH_CDIV_MASKED(rate); + writel_relaxed(regval, CBCR_REG(branch)); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +static int branch_clk_set_rate(struct clk *c, unsigned long rate) +{ + struct branch_clk *branch = to_branch_clk(c); + + if (branch->max_div) + return branch_cdiv_set_rate(branch, rate); + + if (!branch->has_sibling) + return clk_set_rate(branch->parent, rate); + + return -EPERM; +} + +static unsigned long branch_clk_get_rate(struct clk *c) +{ + struct branch_clk *branch = to_branch_clk(c); + + if (branch->max_div) + return branch->c.rate; + + if (!branch->has_sibling) + return clk_get_rate(branch->parent); + + return 0; +} + +static struct clk *branch_clk_get_parent(struct clk *c) +{ + return to_branch_clk(c)->parent; +} + +static int branch_clk_list_rate(struct clk *c, unsigned n) +{ + struct branch_clk *branch = to_branch_clk(c); + + if (branch->has_sibling == 1) + return -ENXIO; + + if (branch->parent) + return rcg_clk_list_rate(branch->parent, n); + else + return 0; +} + +static enum handoff branch_clk_handoff(struct clk *c) +{ + struct branch_clk *branch = to_branch_clk(c); + u32 cbcr_regval; + + cbcr_regval = readl_relaxed(CBCR_REG(branch)); + if ((cbcr_regval & CBCR_BRANCH_OFF_BIT)) + return HANDOFF_DISABLED_CLK; + pr_info("%s enabled.\n", branch->c.dbg_name); + + if (branch->parent) { + if (branch->parent->ops->handoff) + return branch->parent->ops->handoff(branch->parent); + } + + return HANDOFF_ENABLED_CLK; +} + +static int __branch_clk_reset(void __iomem *bcr_reg, + enum clk_reset_action action, const char *name) +{ + int ret = 0; + unsigned long flags; + u32 reg_val; + + if (!bcr_reg) { + WARN("clk_reset called on an unsupported clock (%s)\n", name); + return -EPERM; + } + + spin_lock_irqsave(&local_clock_reg_lock, flags); + reg_val = readl_relaxed(bcr_reg); + switch (action) { + case CLK_RESET_ASSERT: + reg_val |= BCR_BLK_ARES_BIT; + break; + case CLK_RESET_DEASSERT: + reg_val &= ~BCR_BLK_ARES_BIT; + break; + default: + ret = -EINVAL; + } + writel_relaxed(reg_val, bcr_reg); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Make sure write is issued before returning. */ + mb(); + + return ret; +} + +static int branch_clk_reset(struct clk *c, enum clk_reset_action action) +{ + struct branch_clk *branch = to_branch_clk(c); + return __branch_clk_reset(BCR_REG(branch), action, c->dbg_name); +} + +/* + * Voteable clock functions + */ +static int local_vote_clk_reset(struct clk *c, enum clk_reset_action action) +{ + struct local_vote_clk *vclk = to_local_vote_clk(c); + return __branch_clk_reset(BCR_REG(vclk), action, c->dbg_name); +} + +static int local_vote_clk_enable(struct clk *c) +{ + unsigned long flags; + u32 ena; + struct local_vote_clk *vclk = to_local_vote_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + ena = readl_relaxed(VOTE_REG(vclk)); + ena |= vclk->en_mask; + writel_relaxed(ena, VOTE_REG(vclk)); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + branch_clk_halt_check(vclk->halt_check, c->dbg_name, CBCR_REG(vclk), + BRANCH_ON); + + return 0; +} + +static void local_vote_clk_disable(struct clk *c) +{ + unsigned long flags; + u32 ena; + struct local_vote_clk *vclk = to_local_vote_clk(c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + ena = readl_relaxed(VOTE_REG(vclk)); + ena &= ~vclk->en_mask; + writel_relaxed(ena, VOTE_REG(vclk)); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); +} + +static enum handoff local_vote_clk_handoff(struct clk *c) +{ + struct local_vote_clk *vclk = to_local_vote_clk(c); + u32 vote_regval; + + /* Is the branch voted on by apps? */ + vote_regval = readl_relaxed(VOTE_REG(vclk)); + if (!(vote_regval & vclk->en_mask)) + return HANDOFF_DISABLED_CLK; + pr_info("%s enabled.\n", vclk->c.dbg_name); + + return HANDOFF_ENABLED_CLK; +} + +struct clk_ops clk_ops_rcg = { + .enable = rcg_clk_enable, + .set_rate = rcg_clk_set_rate, + .list_rate = rcg_clk_list_rate, + .round_rate = rcg_clk_round_rate, + .get_parent = rcg_clk_get_parent, + .handoff = rcg_clk_handoff, +}; + +struct clk_ops clk_ops_rcg_mnd = { + .enable = rcg_clk_enable, + .set_rate = rcg_clk_set_rate, + .list_rate = rcg_clk_list_rate, + .round_rate = rcg_clk_round_rate, + .get_parent = rcg_clk_get_parent, + .handoff = rcg_mnd_clk_handoff, +}; + +struct clk_ops clk_ops_branch = { + .enable = branch_clk_enable, + .disable = branch_clk_disable, + .auto_off = branch_clk_disable, + .set_rate = branch_clk_set_rate, + .get_rate = branch_clk_get_rate, + .list_rate = branch_clk_list_rate, + .reset = branch_clk_reset, + .get_parent = branch_clk_get_parent, + .handoff = branch_clk_handoff, +}; + +struct clk_ops clk_ops_vote = { + .enable = local_vote_clk_enable, + .disable = local_vote_clk_disable, + .auto_off = local_vote_clk_disable, + .reset = local_vote_clk_reset, + .handoff = local_vote_clk_handoff, +}; diff --git a/arch/arm/mach-msm/clock-local2.h b/arch/arm/mach-msm/clock-local2.h new file mode 100644 index 00000000000..547e633e636 --- /dev/null +++ b/arch/arm/mach-msm/clock-local2.h @@ -0,0 +1,178 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_2_H +#define __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_2_H + +#include +#include "clock.h" + +/* + * Generic frequency-definition structs and macros + */ + +/** + * @freq_hz: output rate + * @src_clk: source clock for freq_hz + * @m_val: M value corresponding to freq_hz + * @n_val: N value corresponding to freq_hz + * @d_val: D value corresponding to freq_hz + * @div_src_val: Pre divider value and source selection mux index for freq_hz + * @sys_vdd: Voltage level required for freq_hz + */ +struct clk_freq_tbl { + unsigned long freq_hz; + struct clk *src_clk; + const u32 m_val; + const u32 n_val; + const u32 d_val; + const u32 div_src_val; + const unsigned sys_vdd; +}; + +#define FREQ_END (UINT_MAX-1) +#define F_END { .freq_hz = FREQ_END } + +/* + * Generic clock-definition struct and macros + */ +/** + * struct rcg_clk - root clock generator + * @cmd_rcgr_reg: command register + * @set_rate: function to set frequency + * @freq_tbl: frequency table for this RCG + * @current_freq: current RCG frequency + * @c: generic clock data + * @base: pointer to base address of ioremapped registers. + */ +struct rcg_clk { + const u32 cmd_rcgr_reg; + + void (*set_rate)(struct rcg_clk *, struct clk_freq_tbl *); + + struct clk_freq_tbl *freq_tbl; + struct clk_freq_tbl *current_freq; + struct clk c; + + void *const __iomem *base; +}; + +static inline struct rcg_clk *to_rcg_clk(struct clk *clk) +{ + return container_of(clk, struct rcg_clk, c); +} + +extern struct clk_freq_tbl rcg_dummy_freq; + +/** + * struct fixed_clk - fixed rate clock (used for crystal oscillators) + * @rate: output rate + * @c: clk + */ +struct fixed_clk { + struct clk c; +}; + +/** + * struct branch_clk - branch clock + * @set_rate: Set the frequency of this branch clock. + * @parent: clock source + * @c: clk + * @cbcr_reg: branch control register + * @bcr_reg: block reset register + * @has_sibling: true if other branches are derived from this branch's source + * @cur_div: current branch divider value + * @max_div: maximum branch divider value (if zero, no divider exists) + * @halt_check: halt checking type + * @base: pointer to base address of ioremapped registers. + */ +struct branch_clk { + void (*set_rate)(struct branch_clk *, struct clk_freq_tbl *); + struct clk *parent; + struct clk c; + const u32 cbcr_reg; + const u32 bcr_reg; + int has_sibling; + u32 cur_div; + const u32 max_div; + const u32 halt_check; + void *const __iomem *base; +}; + +static inline struct branch_clk *to_branch_clk(struct clk *clk) +{ + return container_of(clk, struct branch_clk, c); +} + +/** + * struct local_vote_clk - Voteable branch clock + * @c: clk + * @cbcr_reg: branch control register + * @vote_reg: voting register + * @en_mask: enable mask + * @halt_check: halt checking type + * @base: pointer to base address of ioremapped registers. + * An on/off switch with a rate derived from the parent. + */ +struct local_vote_clk { + struct clk c; + const u32 cbcr_reg; + const u32 vote_reg; + const u32 bcr_reg; + const u32 en_mask; + const u32 halt_check; + void *const __iomem *base; +}; + +static inline struct local_vote_clk *to_local_vote_clk(struct clk *clk) +{ + return container_of(clk, struct local_vote_clk, c); +} + +/** + * struct measure_clk - for rate measurement debug use + * @sample_ticks: sample period in reference clock ticks + * @multiplier: measurement scale-up factor + * @divider: measurement scale-down factor + * @c: clk +*/ +struct measure_clk { + u64 sample_ticks; + u32 multiplier; + u32 divider; + struct clk c; +}; + +static inline struct measure_clk *to_measure_clk(struct clk *clk) +{ + return container_of(clk, struct measure_clk, c); +} + +/* + * Generic set-rate implementations + */ +void set_rate_mnd(struct rcg_clk *clk, struct clk_freq_tbl *nf); +void set_rate_hid(struct rcg_clk *clk, struct clk_freq_tbl *nf); + +/* + * Variables from the clock-local driver + */ +extern spinlock_t local_clock_reg_lock; + +extern struct clk_ops clk_ops_rcg; +extern struct clk_ops clk_ops_rcg_mnd; +extern struct clk_ops clk_ops_branch; +extern struct clk_ops clk_ops_vote; + +#endif /* __ARCH_ARM_MACH_MSM_COPPER_CLOCK_LOCAL_H */ + diff --git a/arch/arm/mach-msm/clock-pcom-lookup.c b/arch/arm/mach-msm/clock-pcom-lookup.c new file mode 100644 index 00000000000..83940cf10a2 --- /dev/null +++ b/arch/arm/mach-msm/clock-pcom-lookup.c @@ -0,0 +1,517 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "clock.h" +#include "clock-pll.h" +#include "clock-pcom.h" +#include "clock-voter.h" +#include + +#include +#include + +#define PLLn_MODE(n) (MSM_CLK_CTL_BASE + 0x300 + 28 * (n)) +#define PLL4_MODE (MSM_CLK_CTL_BASE + 0x374) + +static DEFINE_CLK_PCOM(adm_clk, ADM_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(adsp_clk, ADSP_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(ahb_m_clk, AHB_M_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(ahb_s_clk, AHB_S_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(cam_m_clk, CAM_M_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(axi_rotator_clk, AXI_ROTATOR_CLK, 0); +static DEFINE_CLK_PCOM(ce_clk, CE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(csi0_clk, CSI0_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(csi0_p_clk, CSI0_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(csi0_vfe_clk, CSI0_VFE_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(csi1_clk, CSI1_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(csi1_p_clk, CSI1_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(csi1_vfe_clk, CSI1_VFE_CLK, CLKFLAG_SKIP_AUTO_OFF); + +static struct pll_shared_clk pll0_clk = { + .id = PLL_0, + .mode_reg = PLLn_MODE(0), + .c = { + .ops = &clk_ops_pll, + .dbg_name = "pll0_clk", + CLK_INIT(pll0_clk.c), + }, +}; + +static struct pll_shared_clk pll1_clk = { + .id = PLL_1, + .mode_reg = PLLn_MODE(1), + .c = { + .ops = &clk_ops_pll, + .dbg_name = "pll1_clk", + CLK_INIT(pll1_clk.c), + }, +}; + +static struct pll_shared_clk pll2_clk = { + .id = PLL_2, + .mode_reg = PLLn_MODE(2), + .c = { + .ops = &clk_ops_pll, + .dbg_name = "pll2_clk", + CLK_INIT(pll2_clk.c), + }, +}; + +static struct pll_shared_clk pll4_clk = { + .id = PLL_4, + .mode_reg = PLL4_MODE, + .c = { + .ops = &clk_ops_pll, + .dbg_name = "pll4_clk", + CLK_INIT(pll4_clk.c), + }, +}; + +static struct pcom_clk dsi_byte_clk = { + .id = P_DSI_BYTE_CLK, + .c = { + .ops = &clk_ops_pcom_ext_config, + .dbg_name = "dsi_byte_clk", + CLK_INIT(dsi_byte_clk.c), + }, +}; + +static struct pcom_clk dsi_clk = { + .id = P_DSI_CLK, + .c = { + .ops = &clk_ops_pcom_ext_config, + .dbg_name = "dsi_clk", + CLK_INIT(dsi_clk.c), + }, +}; + +static struct pcom_clk dsi_esc_clk = { + .id = P_DSI_ESC_CLK, + .c = { + .ops = &clk_ops_pcom_ext_config, + .dbg_name = "dsi_esc_clk", + CLK_INIT(dsi_esc_clk.c), + }, +}; + +static struct pcom_clk dsi_pixel_clk = { + .id = P_DSI_PIXEL_CLK, + .c = { + .ops = &clk_ops_pcom_ext_config, + .dbg_name = "dsi_pixel_clk", + CLK_INIT(dsi_pixel_clk.c), + }, +}; + +static DEFINE_CLK_PCOM(dsi_ref_clk, DSI_REF_CLK, 0); +static DEFINE_CLK_PCOM(ebi1_clk, EBI1_CLK, + CLKFLAG_SKIP_AUTO_OFF | CLKFLAG_MIN); +static DEFINE_CLK_PCOM(ebi2_clk, EBI2_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(ecodec_clk, ECODEC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(emdh_clk, EMDH_CLK, CLKFLAG_MIN | CLKFLAG_MAX); +static DEFINE_CLK_PCOM(gp_clk, GP_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(grp_2d_clk, GRP_2D_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(grp_2d_p_clk, GRP_2D_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(grp_3d_clk, GRP_3D_CLK, 0); +static DEFINE_CLK_PCOM(grp_3d_p_clk, GRP_3D_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(gsbi1_qup_clk, GSBI1_QUP_CLK, 0); +static DEFINE_CLK_PCOM(gsbi1_qup_p_clk, GSBI1_QUP_P_CLK, 0); +static DEFINE_CLK_PCOM(gsbi2_qup_clk, GSBI2_QUP_CLK, 0); +static DEFINE_CLK_PCOM(gsbi2_qup_p_clk, GSBI2_QUP_P_CLK, 0); +static DEFINE_CLK_PCOM(gsbi_clk, GSBI_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(gsbi_p_clk, GSBI_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(hdmi_clk, HDMI_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(i2c_clk, I2C_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(icodec_rx_clk, ICODEC_RX_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(icodec_tx_clk, ICODEC_TX_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(imem_clk, IMEM_CLK, 0); +static DEFINE_CLK_PCOM(mdc_clk, MDC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(mdp_clk, MDP_CLK, CLKFLAG_MIN); +static DEFINE_CLK_PCOM(mdp_lcdc_pad_pclk_clk, MDP_LCDC_PAD_PCLK_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(mdp_lcdc_pclk_clk, MDP_LCDC_PCLK_CLK, + CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(mdp_vsync_clk, MDP_VSYNC_CLK, 0); +static DEFINE_CLK_PCOM(mdp_dsi_p_clk, MDP_DSI_P_CLK, 0); +static DEFINE_CLK_PCOM(pbus_clk, PBUS_CLK, + CLKFLAG_SKIP_AUTO_OFF | CLKFLAG_MIN); +static DEFINE_CLK_PCOM(pcm_clk, PCM_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(pmdh_clk, PMDH_CLK, CLKFLAG_MIN | CLKFLAG_MAX); +static DEFINE_CLK_PCOM(sdac_clk, SDAC_CLK, 0); +static DEFINE_CLK_PCOM(sdc1_clk, SDC1_CLK, 0); +static DEFINE_CLK_PCOM(sdc1_p_clk, SDC1_P_CLK, 0); +static DEFINE_CLK_PCOM(sdc2_clk, SDC2_CLK, 0); +static DEFINE_CLK_PCOM(sdc2_p_clk, SDC2_P_CLK, 0); +static DEFINE_CLK_PCOM(sdc3_clk, SDC3_CLK, 0); +static DEFINE_CLK_PCOM(sdc3_p_clk, SDC3_P_CLK, 0); +static DEFINE_CLK_PCOM(sdc4_clk, SDC4_CLK, 0); +static DEFINE_CLK_PCOM(sdc4_p_clk, SDC4_P_CLK, 0); +static DEFINE_CLK_PCOM(spi_clk, SPI_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(tsif_clk, TSIF_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(tsif_p_clk, TSIF_P_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(tsif_ref_clk, TSIF_REF_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(tv_dac_clk, TV_DAC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(tv_enc_clk, TV_ENC_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(uart1_clk, UART1_CLK, 0); +static DEFINE_CLK_PCOM(uart1dm_clk, UART1DM_CLK, 0); +static DEFINE_CLK_PCOM(uart2_clk, UART2_CLK, 0); +static DEFINE_CLK_PCOM(uart2dm_clk, UART2DM_CLK, 0); +static DEFINE_CLK_PCOM(uart3_clk, UART3_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs2_clk, USB_HS2_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs2_p_clk, USB_HS2_P_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs3_clk, USB_HS3_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs3_p_clk, USB_HS3_P_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs_clk, USB_HS_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs_core_clk, USB_HS_CORE_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs_p_clk, USB_HS_P_CLK, 0); +static DEFINE_CLK_PCOM(usb_otg_clk, USB_OTG_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(usb_phy_clk, USB_PHY_CLK, CLKFLAG_SKIP_AUTO_OFF); +static DEFINE_CLK_PCOM(vdc_clk, VDC_CLK, CLKFLAG_MIN); +static DEFINE_CLK_PCOM(vfe_axi_clk, VFE_AXI_CLK, 0); +static DEFINE_CLK_PCOM(vfe_clk, VFE_CLK, 0); +static DEFINE_CLK_PCOM(vfe_mdc_clk, VFE_MDC_CLK, 0); + +static DEFINE_CLK_VOTER(ebi_acpu_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_grp_3d_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_grp_2d_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_lcdc_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_mddi_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_tv_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_usb_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_vfe_clk, &ebi1_clk.c, 0); +static DEFINE_CLK_VOTER(ebi_adm_clk, &ebi1_clk.c, 0); + +static struct clk_lookup msm_clocks_7x01a[] = { + CLK_LOOKUP("core_clk", adm_clk.c, "msm_dmov"), + CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL), + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("ebi2_clk", ebi2_clk.c, NULL), + CLK_LOOKUP("ecodec_clk", ecodec_clk.c, NULL), + CLK_LOOKUP("core_clk", emdh_clk.c, "msm_mddi.1"), + CLK_LOOKUP("core_clk", gp_clk.c, ""), + CLK_LOOKUP("core_clk", grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", i2c_clk.c, "msm_i2c.0"), + CLK_LOOKUP("icodec_rx_clk", icodec_rx_clk.c, NULL), + CLK_LOOKUP("icodec_tx_clk", icodec_tx_clk.c, NULL), + CLK_LOOKUP("mem_clk", imem_clk.c, NULL), + CLK_LOOKUP("mdc_clk", mdc_clk.c, NULL), + CLK_LOOKUP("core_clk", pmdh_clk.c, "mddi.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("pbus_clk", pbus_clk.c, NULL), + CLK_LOOKUP("pcm_clk", pcm_clk.c, NULL), + CLK_LOOKUP("sdac_clk", sdac_clk.c, NULL), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", tsif_clk.c, "msm_tsif.0"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tsif.0"), + CLK_LOOKUP("tv_dac_clk", tv_dac_clk.c, NULL), + CLK_LOOKUP("tv_enc_clk", tv_enc_clk.c, NULL), + CLK_LOOKUP("core_clk", uart1_clk.c, "msm_serial.0"), + CLK_LOOKUP("core_clk", uart2_clk.c, "msm_serial.1"), + CLK_LOOKUP("core_clk", uart3_clk.c, "msm_serial.2"), + CLK_LOOKUP("core_clk", uart1dm_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", uart2dm_clk.c, "msm_serial_hs.1"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_hsusb_otg"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_hsusb_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_hsusb_peripheral"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_hsusb_peripheral"), + CLK_LOOKUP("alt_core_clk", usb_otg_clk.c, NULL), + CLK_LOOKUP("vdc_clk", vdc_clk.c, NULL), + CLK_LOOKUP("vfe_clk", vfe_clk.c, NULL), + CLK_LOOKUP("vfe_mdc_clk", vfe_mdc_clk.c, NULL), +}; + +struct clock_init_data msm7x01a_clock_init_data __initdata = { + .table = msm_clocks_7x01a, + .size = ARRAY_SIZE(msm_clocks_7x01a), +}; + +static struct clk_lookup msm_clocks_7x27[] = { + CLK_LOOKUP("core_clk", adm_clk.c, "msm_dmov"), + CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL), + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("ebi2_clk", ebi2_clk.c, NULL), + CLK_LOOKUP("ecodec_clk", ecodec_clk.c, NULL), + CLK_LOOKUP("core_clk", gp_clk.c, ""), + CLK_LOOKUP("core_clk", grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", grp_3d_clk.c, "footswitch-pcom.2"), + CLK_LOOKUP("iface_clk", grp_3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", i2c_clk.c, "msm_i2c.0"), + CLK_LOOKUP("iface_clk", grp_3d_p_clk.c, "footswitch-pcom.2"), + CLK_LOOKUP("icodec_rx_clk", icodec_rx_clk.c, NULL), + CLK_LOOKUP("icodec_tx_clk", icodec_tx_clk.c, NULL), + CLK_LOOKUP("mem_clk", imem_clk.c, NULL), + CLK_LOOKUP("mdc_clk", mdc_clk.c, NULL), + CLK_LOOKUP("core_clk", pmdh_clk.c, "mddi.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("mdp_clk", mdp_lcdc_pclk_clk.c, "lcdc.0"), + CLK_LOOKUP("lcdc_clk", mdp_lcdc_pad_pclk_clk.c, "lcdc.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("pbus_clk", pbus_clk.c, NULL), + CLK_LOOKUP("pcm_clk", pcm_clk.c, NULL), + CLK_LOOKUP("sdac_clk", sdac_clk.c, NULL), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", tsif_clk.c, "msm_tsif.0"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tsif.0"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, "msm_tsif.0"), + CLK_LOOKUP("core_clk", uart1_clk.c, "msm_serial.0"), + CLK_LOOKUP("core_clk", uart2_clk.c, "msm_serial.1"), + CLK_LOOKUP("core_clk", uart1dm_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", uart2dm_clk.c, "msm_serial_hs.1"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_otg_clk.c, NULL), + CLK_LOOKUP("phy_clk", usb_phy_clk.c, "msm_otg"), + CLK_LOOKUP("vdc_clk", vdc_clk.c, NULL), + CLK_LOOKUP("core_clk", vdc_clk.c, "footswitch-pcom.7"), + CLK_LOOKUP("vfe_clk", vfe_clk.c, NULL), + CLK_LOOKUP("core_clk", vfe_clk.c, "footswitch-pcom.8"), + CLK_LOOKUP("vfe_mdc_clk", vfe_mdc_clk.c, NULL), + + CLK_LOOKUP("ebi1_acpu_clk", ebi_acpu_clk.c, NULL), + CLK_LOOKUP("bus_clk", ebi_grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("mem_clk", ebi_lcdc_clk.c, "lcdc.0"), + CLK_LOOKUP("mem_clk", ebi_mddi_clk.c, "mddi.0"), + CLK_LOOKUP("core_clk", ebi_usb_clk.c, "msm_otg"), + CLK_LOOKUP("ebi1_vfe_clk", ebi_vfe_clk.c, NULL), + CLK_LOOKUP("mem_clk", ebi_adm_clk.c, "msm_dmov"), + + CLK_LOOKUP("pll0_clk", pll0_clk.c, "acpu"), + CLK_LOOKUP("pll1_clk", pll1_clk.c, "acpu"), + CLK_LOOKUP("pll2_clk", pll2_clk.c, "acpu"), +}; + +struct clock_init_data msm7x27_clock_init_data __initdata = { + .table = msm_clocks_7x27, + .size = ARRAY_SIZE(msm_clocks_7x27), + .pre_init = msm_shared_pll_control_init, +}; + +/* Clock table for common clocks between 7627a and 7625a */ +static struct clk_lookup msm_cmn_clk_7625a_7627a[] __initdata = { + CLK_LOOKUP("core_clk", adm_clk.c, "msm_dmov"), + CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL), + CLK_LOOKUP("master_iface_clk", ahb_m_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("slave_iface_clk", ahb_s_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("cam_m_clk", cam_m_clk.c, NULL), + CLK_LOOKUP("cam_clk", cam_m_clk.c, "0-0036"), + CLK_LOOKUP("cam_clk", cam_m_clk.c, "0-001b"), + CLK_LOOKUP("cam_clk", cam_m_clk.c, "0-0010"), + CLK_LOOKUP("cam_clk", cam_m_clk.c, "0-0078"), + CLK_LOOKUP("cam_clk", cam_m_clk.c, "0-006c"), + CLK_LOOKUP("cam_clk", cam_m_clk.c, "0-000d"), + CLK_LOOKUP("csi_clk", csi0_clk.c, "msm_camera_ov9726.0"), + CLK_LOOKUP("csi_pclk", csi0_p_clk.c, "msm_camera_ov9726.0"), + CLK_LOOKUP("csi_vfe_clk", csi0_vfe_clk.c, "msm_camera_ov9726.0"), + CLK_LOOKUP("csi_clk", csi0_clk.c, "msm_camera_ov7692.0"), + CLK_LOOKUP("csi_pclk", csi0_p_clk.c, "msm_camera_ov7692.0"), + CLK_LOOKUP("csi_vfe_clk", csi0_vfe_clk.c, "msm_camera_ov7692.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, NULL), + CLK_LOOKUP("csi_pclk", csi1_p_clk.c, NULL), + CLK_LOOKUP("csi_vfe_clk", csi1_vfe_clk.c, NULL), + CLK_LOOKUP("csi_clk", csi0_clk.c, "msm_csic.0"), + CLK_LOOKUP("csi_pclk", csi0_p_clk.c, "msm_csic.0"), + CLK_LOOKUP("csi_vfe_clk", csi0_vfe_clk.c, "msm_csic.0"), + CLK_LOOKUP("csi_clk", csi1_clk.c, "msm_csic.1"), + CLK_LOOKUP("csi_pclk", csi1_p_clk.c, "msm_csic.1"), + CLK_LOOKUP("csi_vfe_clk", csi1_vfe_clk.c, "msm_csic.1"), + CLK_LOOKUP("byte_clk", dsi_byte_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("core_clk", dsi_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("esc_clk", dsi_esc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("pixel_clk", dsi_pixel_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("ref_clk", dsi_ref_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("ebi2_clk", ebi2_clk.c, NULL), + CLK_LOOKUP("ecodec_clk", ecodec_clk.c, NULL), + CLK_LOOKUP("core_clk", gp_clk.c, ""), + CLK_LOOKUP("core_clk", grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", grp_3d_clk.c, "footswitch-pcom.2"), + CLK_LOOKUP("iface_clk", grp_3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("iface_clk", grp_3d_p_clk.c, "footswitch-pcom.2"), + CLK_LOOKUP("core_clk", gsbi1_qup_clk.c, "qup_i2c.0"), + CLK_LOOKUP("core_clk", gsbi2_qup_clk.c, "qup_i2c.1"), + CLK_LOOKUP("iface_clk", gsbi1_qup_p_clk.c, "qup_i2c.0"), + CLK_LOOKUP("iface_clk", gsbi2_qup_p_clk.c, "qup_i2c.1"), + CLK_LOOKUP("icodec_rx_clk", icodec_rx_clk.c, NULL), + CLK_LOOKUP("icodec_tx_clk", icodec_tx_clk.c, NULL), + CLK_LOOKUP("mem_clk", imem_clk.c, NULL), + CLK_LOOKUP("core_clk", pmdh_clk.c, "mddi.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("mdp_clk", mdp_lcdc_pclk_clk.c, "lcdc.0"), + CLK_LOOKUP("lcdc_clk", mdp_lcdc_pad_pclk_clk.c, "lcdc.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("mdp_clk", mdp_dsi_p_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("pbus_clk", pbus_clk.c, NULL), + CLK_LOOKUP("pcm_clk", pcm_clk.c, NULL), + CLK_LOOKUP("sdac_clk", sdac_clk.c, NULL), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tsif.0"), + CLK_LOOKUP("iface_clk", tsif_p_clk.c, "msm_tsif.0"), + CLK_LOOKUP("core_clk", uart1_clk.c, "msm_serial.0"), + CLK_LOOKUP("core_clk", uart2_clk.c, "msm_serial.1"), + CLK_LOOKUP("core_clk", uart1dm_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", uart2dm_clk.c, "msm_serial_hsl.0"), + CLK_LOOKUP("core_clk", usb_hs_core_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_otg"), + CLK_LOOKUP("phy_clk", usb_phy_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs2_clk.c, "msm_hsusb_host.0"), + CLK_LOOKUP("vdc_clk", vdc_clk.c, NULL), + CLK_LOOKUP("core_clk", vdc_clk.c, "footswitch-pcom.7"), + CLK_LOOKUP("vfe_clk", vfe_clk.c, NULL), + CLK_LOOKUP("vfe_clk", vfe_clk.c, "msm_vfe.0"), + CLK_LOOKUP("core_clk", vfe_clk.c, "footswitch-pcom.8"), + CLK_LOOKUP("vfe_mdc_clk", vfe_mdc_clk.c, NULL), + + CLK_LOOKUP("ebi1_acpu_clk", ebi_acpu_clk.c, NULL), + CLK_LOOKUP("bus_clk", ebi_grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("mem_clk", ebi_lcdc_clk.c, "lcdc.0"), + CLK_LOOKUP("mem_clk", ebi_lcdc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("mem_clk", ebi_mddi_clk.c, "mddi.0"), + CLK_LOOKUP("ebi1_vfe_clk", ebi_vfe_clk.c, NULL), + CLK_LOOKUP("mem_clk", ebi_adm_clk.c, "msm_dmov"), + + CLK_LOOKUP("pll0_clk", pll0_clk.c, "acpu"), + CLK_LOOKUP("pll1_clk", pll1_clk.c, "acpu"), + CLK_LOOKUP("pll2_clk", pll2_clk.c, "acpu"), + +}; + +/* PLL 4 clock is available for 7627a target. */ +static struct clk_lookup msm_clk_7627a[] __initdata = { + CLK_LOOKUP("pll4_clk", pll4_clk.c, "acpu"), +}; + +static struct clk_lookup msm_clk_7627a_7625a[ARRAY_SIZE(msm_cmn_clk_7625a_7627a) + + ARRAY_SIZE(msm_clk_7627a)]; + +static void __init msm7627a_clock_pre_init(void) +{ + int size = ARRAY_SIZE(msm_cmn_clk_7625a_7627a); + + /* Intialize shared PLL control structure */ + msm_shared_pll_control_init(); + + memcpy(&msm_clk_7627a_7625a, &msm_cmn_clk_7625a_7627a, + sizeof(msm_cmn_clk_7625a_7627a)); + if (!cpu_is_msm7x25a()) { + memcpy(&msm_clk_7627a_7625a[size], + &msm_clk_7627a, sizeof(msm_clk_7627a)); + size += ARRAY_SIZE(msm_clk_7627a); + } + msm7x27a_clock_init_data.size = size; +} + +struct clock_init_data msm7x27a_clock_init_data __initdata = { + .table = msm_clk_7627a_7625a, + .pre_init = msm7627a_clock_pre_init, +}; + +static struct clk_lookup msm_clocks_8x50[] = { + CLK_LOOKUP("core_clk", adm_clk.c, "msm_dmov"), + CLK_LOOKUP("core_clk", ce_clk.c, "qce.0"), + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("ebi2_clk", ebi2_clk.c, NULL), + CLK_LOOKUP("ecodec_clk", ecodec_clk.c, NULL), + CLK_LOOKUP("core_clk", emdh_clk.c, "msm_mddi.1"), + CLK_LOOKUP("core_clk", gp_clk.c, ""), + CLK_LOOKUP("core_clk", grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", i2c_clk.c, "msm_i2c.0"), + CLK_LOOKUP("icodec_rx_clk", icodec_rx_clk.c, NULL), + CLK_LOOKUP("icodec_tx_clk", icodec_tx_clk.c, NULL), + CLK_LOOKUP("mem_clk", imem_clk.c, NULL), + CLK_LOOKUP("mdc_clk", mdc_clk.c, NULL), + CLK_LOOKUP("core_clk", pmdh_clk.c, "mddi.0"), + CLK_LOOKUP("core_clk", mdp_clk.c, "mdp.0"), + CLK_LOOKUP("mdp_clk", mdp_lcdc_pclk_clk.c, "lcdc.0"), + CLK_LOOKUP("lcdc_clk", mdp_lcdc_pad_pclk_clk.c, "lcdc.0"), + CLK_LOOKUP("vsync_clk", mdp_vsync_clk.c, "mdp.0"), + CLK_LOOKUP("pbus_clk", pbus_clk.c, NULL), + CLK_LOOKUP("pcm_clk", pcm_clk.c, NULL), + CLK_LOOKUP("sdac_clk", sdac_clk.c, NULL), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", spi_clk.c, "spi_qsd.0"), + CLK_DUMMY("iface_clk", SPI_P_CLK, "spi_qsd.0", 0), + CLK_LOOKUP("core_clk", tsif_clk.c, "msm_tsif.0"), + CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tsif.0"), + CLK_LOOKUP("tv_dac_clk", tv_dac_clk.c, NULL), + CLK_LOOKUP("tv_enc_clk", tv_enc_clk.c, NULL), + CLK_LOOKUP("core_clk", uart1_clk.c, "msm_serial.0"), + CLK_LOOKUP("core_clk", uart2_clk.c, "msm_serial.1"), + CLK_LOOKUP("core_clk", uart3_clk.c, "msm_serial.2"), + CLK_LOOKUP("core_clk", uart1dm_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", uart2dm_clk.c, "msm_serial_hs.1"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_otg_clk.c, NULL), + CLK_LOOKUP("vdc_clk", vdc_clk.c, NULL), + CLK_LOOKUP("vfe_clk", vfe_clk.c, NULL), + CLK_LOOKUP("vfe_mdc_clk", vfe_mdc_clk.c, NULL), + CLK_LOOKUP("vfe_axi_clk", vfe_axi_clk.c, NULL), + CLK_LOOKUP("alt_core_clk", usb_hs2_clk.c, "msm_hsusb_host.0"), + CLK_LOOKUP("iface_clk", usb_hs2_p_clk.c, "msm_hsusb_host.0"), + CLK_LOOKUP("alt_core_clk", usb_hs3_clk.c, ""), + CLK_LOOKUP("iface_clk", usb_hs3_p_clk.c, ""), + CLK_LOOKUP("phy_clk", usb_phy_clk.c, "msm_otg"), + + CLK_LOOKUP("ebi1_acpu_clk", ebi_acpu_clk.c, NULL), + CLK_LOOKUP("bus_clk", ebi_grp_3d_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("bus_clk", ebi_grp_2d_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("mem_clk", ebi_lcdc_clk.c, "lcdc.0"), + CLK_LOOKUP("mem_clk", ebi_lcdc_clk.c, "mipi_dsi.1"), + CLK_LOOKUP("mem_clk", ebi_mddi_clk.c, "mddi.0"), + CLK_LOOKUP("mem_clk", ebi_tv_clk.c, "tvenc.0"), + CLK_LOOKUP("core_clk", ebi_usb_clk.c, "msm_otg"), + CLK_LOOKUP("core_clk", ebi_usb_clk.c, "msm_hsusb_host.0"), + CLK_LOOKUP("ebi1_vfe_clk", ebi_vfe_clk.c, NULL), + CLK_LOOKUP("mem_clk", ebi_adm_clk.c, "msm_dmov"), + + CLK_LOOKUP("iface_clk", grp_3d_p_clk.c, "kgsl-3d0.0"), + CLK_LOOKUP("core_clk", grp_2d_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("iface_clk", grp_2d_p_clk.c, "kgsl-2d0.0"), + CLK_LOOKUP("core_clk", gsbi_clk.c, "qup_i2c.4"), + CLK_LOOKUP("iface_clk", gsbi_p_clk.c, "qup_i2c.4"), +}; + +struct clock_init_data qds8x50_clock_init_data __initdata = { + .table = msm_clocks_8x50, + .size = ARRAY_SIZE(msm_clocks_8x50), +}; diff --git a/arch/arm/mach-msm/clock-pcom.c b/arch/arm/mach-msm/clock-pcom.c index 63b71131108..02c8765312c 100644 --- a/arch/arm/mach-msm/clock-pcom.c +++ b/arch/arm/mach-msm/clock-pcom.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -13,29 +13,43 @@ * */ +#include #include -#include -#include -#include -#include "proc_comm.h" +#include +#include +#include + #include "clock.h" #include "clock-pcom.h" /* * glue for the proc_comm interface */ -int pc_clk_enable(unsigned id) +static int pc_clk_enable(struct clk *clk) { - int rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); + int rc; + int id = to_pcom_clk(clk)->id; + + /* Ignore clocks that are always on */ + if (id == P_EBI1_CLK || id == P_EBI1_FIXED_CLK) + return 0; + + rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); if (rc < 0) return rc; else return (int)id < 0 ? -EINVAL : 0; } -void pc_clk_disable(unsigned id) +static void pc_clk_disable(struct clk *clk) { + int id = to_pcom_clk(clk)->id; + + /* Ignore clocks that are always on */ + if (id == P_EBI1_CLK || id == P_EBI1_FIXED_CLK) + return; + msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); } @@ -54,39 +68,65 @@ int pc_clk_reset(unsigned id, enum clk_reset_action action) return (int)id < 0 ? -EINVAL : 0; } -int pc_clk_set_rate(unsigned id, unsigned rate) +static int pc_reset(struct clk *clk, enum clk_reset_action action) +{ + int id = to_pcom_clk(clk)->id; + return pc_clk_reset(id, action); +} + +static int _pc_clk_set_rate(struct clk *clk, unsigned long rate) { /* The rate _might_ be rounded off to the nearest KHz value by the * remote function. So a return value of 0 doesn't necessarily mean * that the exact rate was set successfully. */ - int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); + unsigned r = rate; + int id = to_pcom_clk(clk)->id; + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &r); if (rc < 0) return rc; else return (int)id < 0 ? -EINVAL : 0; } -int pc_clk_set_min_rate(unsigned id, unsigned rate) +static int _pc_clk_set_min_rate(struct clk *clk, unsigned long rate) { - int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); + int rc; + int id = to_pcom_clk(clk)->id; + bool ignore_error = (cpu_is_msm7x27() && id == P_EBI1_CLK && + rate >= INT_MAX); + unsigned r = rate; + rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &r); + if (rc < 0) + return rc; + else if (ignore_error) + return 0; + else + return (int)id < 0 ? -EINVAL : 0; +} + +static int pc_clk_set_rate(struct clk *clk, unsigned long rate) +{ + if (clk->flags & CLKFLAG_MIN) + return _pc_clk_set_min_rate(clk, rate); + else + return _pc_clk_set_rate(clk, rate); +} + +static int pc_clk_set_max_rate(struct clk *clk, unsigned long rate) +{ + int id = to_pcom_clk(clk)->id; + unsigned r = rate; + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &r); if (rc < 0) return rc; else return (int)id < 0 ? -EINVAL : 0; } -int pc_clk_set_max_rate(unsigned id, unsigned rate) -{ - int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate); - if (rc < 0) - return rc; - else - return (int)id < 0 ? -EINVAL : 0; -} - -int pc_clk_set_flags(unsigned id, unsigned flags) +static int pc_clk_set_flags(struct clk *clk, unsigned flags) { + int id = to_pcom_clk(clk)->id; int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags); if (rc < 0) return rc; @@ -94,45 +134,86 @@ int pc_clk_set_flags(unsigned id, unsigned flags) return (int)id < 0 ? -EINVAL : 0; } -unsigned pc_clk_get_rate(unsigned id) +static int pc_clk_set_ext_config(struct clk *clk, unsigned long config) { + int id = to_pcom_clk(clk)->id; + unsigned c = config; + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_EXT_CONFIG, &id, &c); + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +static unsigned long pc_clk_get_rate(struct clk *clk) +{ + int id = to_pcom_clk(clk)->id; if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) return 0; else return id; } -unsigned pc_clk_is_enabled(unsigned id) +static int pc_clk_is_enabled(struct clk *clk) { + int id = to_pcom_clk(clk)->id; if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) return 0; else return id; } -long pc_clk_round_rate(unsigned id, unsigned rate) +static long pc_clk_round_rate(struct clk *clk, unsigned long rate) { /* Not really supported; pc_clk_set_rate() does rounding on it's own. */ return rate; } -static bool pc_clk_is_local(unsigned id) +static bool pc_clk_is_local(struct clk *clk) { return false; } +static enum handoff pc_clk_handoff(struct clk *clk) +{ + /* + * Handoff clock state only since querying and caching the rate here + * would incur more overhead than it would ever save. + */ + if (pc_clk_is_enabled(clk)) + return HANDOFF_ENABLED_CLK; + + return HANDOFF_DISABLED_CLK; +} + struct clk_ops clk_ops_pcom = { .enable = pc_clk_enable, .disable = pc_clk_disable, .auto_off = pc_clk_disable, - .reset = pc_clk_reset, + .reset = pc_reset, .set_rate = pc_clk_set_rate, - .set_min_rate = pc_clk_set_min_rate, .set_max_rate = pc_clk_set_max_rate, .set_flags = pc_clk_set_flags, .get_rate = pc_clk_get_rate, .is_enabled = pc_clk_is_enabled, .round_rate = pc_clk_round_rate, .is_local = pc_clk_is_local, + .handoff = pc_clk_handoff, }; + +struct clk_ops clk_ops_pcom_ext_config = { + .enable = pc_clk_enable, + .disable = pc_clk_disable, + .auto_off = pc_clk_disable, + .reset = pc_reset, + .set_rate = pc_clk_set_ext_config, + .set_max_rate = pc_clk_set_max_rate, + .set_flags = pc_clk_set_flags, + .get_rate = pc_clk_get_rate, + .is_enabled = pc_clk_is_enabled, + .round_rate = pc_clk_round_rate, + .is_local = pc_clk_is_local, + .handoff = pc_clk_handoff, +}; + diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h index 974d0032f3a..723c53c7d75 100644 --- a/arch/arm/mach-msm/clock-pcom.h +++ b/arch/arm/mach-msm/clock-pcom.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -117,24 +117,54 @@ #define P_GSBI_P_CLK 99 #define P_CE_CLK 100 /* Crypto engine */ #define P_CODEC_SSBI_CLK 101 +#define P_TCXO_DIV4_CLK 102 +#define P_GSBI1_QUP_CLK 103 +#define P_GSBI2_QUP_CLK 104 +#define P_GSBI1_QUP_P_CLK 105 +#define P_GSBI2_QUP_P_CLK 106 +#define P_DSI_CLK 107 +#define P_DSI_ESC_CLK 108 +#define P_DSI_PIXEL_CLK 109 +#define P_DSI_BYTE_CLK 110 +#define P_EBI1_FIXED_CLK 111 /* Not dropped during power-collapse */ +#define P_DSI_REF_CLK 112 +#define P_MDP_DSI_P_CLK 113 +#define P_AHB_M_CLK 114 +#define P_AHB_S_CLK 115 -#define P_NR_CLKS 102 +#define P_NR_CLKS 116 + +extern int pc_clk_reset(unsigned id, enum clk_reset_action action); struct clk_ops; extern struct clk_ops clk_ops_pcom; +extern struct clk_ops clk_ops_pcom_div2; +extern struct clk_ops clk_ops_pcom_ext_config; -int pc_clk_reset(unsigned id, enum clk_reset_action action); +/* + * struct pcom_clk - proc_comm controlled clock + * @id: proc_comm identifier + * @c: + */ +struct pcom_clk { + unsigned id; + struct clk c; +}; -#define CLK_PCOM(clk_name, clk_id, clk_dev, clk_flags) { \ - .con_id = clk_name, \ - .dev_id = clk_dev, \ - .clk = &(struct clk){ \ +static inline struct pcom_clk *to_pcom_clk(struct clk *clk) +{ + return container_of(clk, struct pcom_clk, c); +} + +#define DEFINE_CLK_PCOM(clk_name, clk_id, clk_flags) \ + struct pcom_clk clk_name = { \ .id = P_##clk_id, \ - .remote_id = P_##clk_id, \ - .ops = &clk_ops_pcom, \ - .flags = clk_flags, \ - .dbg_name = #clk_id, \ - }, \ + .c = { \ + .ops = &clk_ops_pcom, \ + .flags = clk_flags, \ + .dbg_name = #clk_id, \ + CLK_INIT(clk_name.c), \ + }, \ } #endif diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c new file mode 100644 index 00000000000..d8399110cc1 --- /dev/null +++ b/arch/arm/mach-msm/clock-pll.c @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "clock.h" +#include "clock-pll.h" +#include "smd_private.h" + +#ifdef CONFIG_MSM_SECURE_IO +#undef readl_relaxed +#undef writel_relaxed +#define readl_relaxed secure_readl +#define writel_relaxed secure_writel +#endif + +#define PLL_OUTCTRL BIT(0) +#define PLL_BYPASSNL BIT(1) +#define PLL_RESET_N BIT(2) +#define PLL_MODE_MASK BM(3, 0) + +#define PLL_EN_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->en_reg)) : \ + ((x)->en_reg)) +#define PLL_STATUS_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->status_reg)) : \ + ((x)->status_reg)) +#define PLL_MODE_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->mode_reg)) : \ + ((x)->mode_reg)) +#define PLL_L_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->l_reg)) : \ + ((x)->l_reg)) +#define PLL_M_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->m_reg)) : \ + ((x)->m_reg)) +#define PLL_N_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->n_reg)) : \ + ((x)->n_reg)) +#define PLL_CONFIG_REG(x) ((x)->base ? (*(x)->base + (u32)((x)->config_reg)) : \ + ((x)->config_reg)) + +static DEFINE_SPINLOCK(pll_reg_lock); + +#define ENABLE_WAIT_MAX_LOOPS 200 + +int pll_vote_clk_enable(struct clk *clk) +{ + u32 ena, count; + unsigned long flags; + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + + spin_lock_irqsave(&pll_reg_lock, flags); + ena = readl_relaxed(PLL_EN_REG(pll)); + ena |= pll->en_mask; + writel_relaxed(ena, PLL_EN_REG(pll)); + spin_unlock_irqrestore(&pll_reg_lock, flags); + + /* + * Use a memory barrier since some PLL status registers are + * not within the same 1K segment as the voting registers. + */ + mb(); + + /* Wait for pll to enable. */ + for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) { + if (readl_relaxed(PLL_STATUS_REG(pll)) & pll->status_mask) + return 0; + udelay(1); + } + + WARN("PLL %s didn't enable after voting for it!\n", clk->dbg_name); + + return -ETIMEDOUT; +} + +void pll_vote_clk_disable(struct clk *clk) +{ + u32 ena; + unsigned long flags; + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + + spin_lock_irqsave(&pll_reg_lock, flags); + ena = readl_relaxed(PLL_EN_REG(pll)); + ena &= ~(pll->en_mask); + writel_relaxed(ena, PLL_EN_REG(pll)); + spin_unlock_irqrestore(&pll_reg_lock, flags); +} + +struct clk *pll_vote_clk_get_parent(struct clk *clk) +{ + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + return pll->parent; +} + +int pll_vote_clk_is_enabled(struct clk *clk) +{ + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + return !!(readl_relaxed(PLL_STATUS_REG(pll)) & pll->status_mask); +} + +static enum handoff pll_vote_clk_handoff(struct clk *clk) +{ + struct pll_vote_clk *pll = to_pll_vote_clk(clk); + if (readl_relaxed(PLL_EN_REG(pll)) & pll->en_mask) + return HANDOFF_ENABLED_CLK; + + return HANDOFF_DISABLED_CLK; +} + +struct clk_ops clk_ops_pll_vote = { + .enable = pll_vote_clk_enable, + .disable = pll_vote_clk_disable, + .auto_off = pll_vote_clk_disable, + .is_enabled = pll_vote_clk_is_enabled, + .get_parent = pll_vote_clk_get_parent, + .handoff = pll_vote_clk_handoff, +}; + +static void __pll_clk_enable_reg(void __iomem *mode_reg) +{ + u32 mode = readl_relaxed(mode_reg); + /* Disable PLL bypass mode. */ + mode |= PLL_BYPASSNL; + writel_relaxed(mode, mode_reg); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + mb(); + udelay(10); + + /* De-assert active-low PLL reset. */ + mode |= PLL_RESET_N; + writel_relaxed(mode, mode_reg); + + /* Wait until PLL is locked. */ + mb(); + udelay(50); + + /* Enable PLL output. */ + mode |= PLL_OUTCTRL; + writel_relaxed(mode, mode_reg); + + /* Ensure that the write above goes through before returning. */ + mb(); +} + +static int local_pll_clk_enable(struct clk *clk) +{ + unsigned long flags; + struct pll_clk *pll = to_pll_clk(clk); + + spin_lock_irqsave(&pll_reg_lock, flags); + __pll_clk_enable_reg(PLL_MODE_REG(pll)); + spin_unlock_irqrestore(&pll_reg_lock, flags); + + return 0; +} + +static void __pll_clk_disable_reg(void __iomem *mode_reg) +{ + u32 mode = readl_relaxed(mode_reg); + mode &= ~PLL_MODE_MASK; + writel_relaxed(mode, mode_reg); +} + +static void local_pll_clk_disable(struct clk *clk) +{ + unsigned long flags; + struct pll_clk *pll = to_pll_clk(clk); + + /* + * Disable the PLL output, disable test mode, enable + * the bypass mode, and assert the reset. + */ + spin_lock_irqsave(&pll_reg_lock, flags); + __pll_clk_disable_reg(PLL_MODE_REG(pll)); + spin_unlock_irqrestore(&pll_reg_lock, flags); +} + +static enum handoff local_pll_clk_handoff(struct clk *clk) +{ + struct pll_clk *pll = to_pll_clk(clk); + u32 mode = readl_relaxed(PLL_MODE_REG(pll)); + u32 mask = PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL; + + if ((mode & mask) == mask) + return HANDOFF_ENABLED_CLK; + + return HANDOFF_DISABLED_CLK; +} + +static struct clk *local_pll_clk_get_parent(struct clk *clk) +{ + struct pll_clk *pll = to_pll_clk(clk); + return pll->parent; +} + +int sr_pll_clk_enable(struct clk *clk) +{ + u32 mode; + unsigned long flags; + struct pll_clk *pll = to_pll_clk(clk); + + spin_lock_irqsave(&pll_reg_lock, flags); + mode = readl_relaxed(PLL_MODE_REG(pll)); + /* De-assert active-low PLL reset. */ + mode |= PLL_RESET_N; + writel_relaxed(mode, PLL_MODE_REG(pll)); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + mb(); + udelay(10); + + /* Disable PLL bypass mode. */ + mode |= PLL_BYPASSNL; + writel_relaxed(mode, PLL_MODE_REG(pll)); + + /* Wait until PLL is locked. */ + mb(); + udelay(60); + + /* Enable PLL output. */ + mode |= PLL_OUTCTRL; + writel_relaxed(mode, PLL_MODE_REG(pll)); + + /* Ensure that the write above goes through before returning. */ + mb(); + + spin_unlock_irqrestore(&pll_reg_lock, flags); + + return 0; +} + +#define PLL_LOCKED_BIT BIT(16) + +int copper_pll_clk_enable(struct clk *clk) +{ + unsigned long flags; + struct pll_clk *pll = to_pll_clk(clk); + u32 count, mode; + int ret = 0; + + spin_lock_irqsave(&pll_reg_lock, flags); + mode = readl_relaxed(PLL_MODE_REG(pll)); + /* Disable PLL bypass mode. */ + mode |= PLL_BYPASSNL; + writel_relaxed(mode, PLL_MODE_REG(pll)); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + mb(); + udelay(10); + + /* De-assert active-low PLL reset. */ + mode |= PLL_RESET_N; + writel_relaxed(mode, PLL_MODE_REG(pll)); + + /* Wait for pll to enable. */ + for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) { + if (readl_relaxed(PLL_STATUS_REG(pll)) & PLL_LOCKED_BIT) + break; + udelay(1); + } + + if (!(readl_relaxed(PLL_STATUS_REG(pll)) & PLL_LOCKED_BIT)) { + WARN("PLL %s didn't lock after enabling it!\n", clk->dbg_name); + ret = -ETIMEDOUT; + goto out; + } + + /* Enable PLL output. */ + mode |= PLL_OUTCTRL; + writel_relaxed(mode, PLL_MODE_REG(pll)); + + /* Ensure the write above goes through before returning. */ + mb(); + +out: + spin_unlock_irqrestore(&pll_reg_lock, flags); + return ret; +} + +struct clk_ops clk_ops_local_pll = { + .enable = local_pll_clk_enable, + .disable = local_pll_clk_disable, + .auto_off = local_pll_clk_disable, + .handoff = local_pll_clk_handoff, + .get_parent = local_pll_clk_get_parent, +}; + +struct pll_rate { + unsigned int lvalue; + unsigned long rate; +}; + +static struct pll_rate pll_l_rate[] = { + {10, 196000000}, + {12, 245760000}, + {30, 589820000}, + {38, 737280000}, + {41, 800000000}, + {50, 960000000}, + {52, 1008000000}, + {60, 1152000000}, + {62, 1200000000}, + {63, 1209600000}, + {0, 0}, +}; + +#define PLL_BASE 7 + +struct shared_pll_control { + uint32_t version; + struct { + /* + * Denotes if the PLL is ON. Technically, this can be read + * directly from the PLL registers, but this feild is here, + * so let's use it. + */ + uint32_t on; + /* + * One bit for each processor core. The application processor + * is allocated bit position 1. All other bits should be + * considered as votes from other processors. + */ + uint32_t votes; + } pll[PLL_BASE + PLL_END]; +}; + +static remote_spinlock_t pll_lock; +static struct shared_pll_control *pll_control; + +void __init msm_shared_pll_control_init(void) +{ +#define PLL_REMOTE_SPINLOCK_ID "S:7" + unsigned smem_size; + + remote_spin_lock_init(&pll_lock, PLL_REMOTE_SPINLOCK_ID); + + pll_control = smem_get_entry(SMEM_CLKREGIM_SOURCES, &smem_size); + if (!pll_control) { + pr_err("Can't find shared PLL control data structure!\n"); + BUG(); + /* + * There might be more PLLs than what the application processor knows + * about. But the index used for each PLL is guaranteed to remain the + * same. + */ + } else if (smem_size < sizeof(struct shared_pll_control)) { + pr_err("Shared PLL control data" + "structure too small!\n"); + BUG(); + } else if (pll_control->version != 0xCCEE0001) { + pr_err("Shared PLL control version mismatch!\n"); + BUG(); + } else { + pr_info("Shared PLL control available.\n"); + return; + } + +} + +static int pll_clk_enable(struct clk *clk) +{ + struct pll_shared_clk *pll = to_pll_shared_clk(clk); + unsigned int pll_id = pll->id; + + remote_spin_lock(&pll_lock); + + pll_control->pll[PLL_BASE + pll_id].votes |= BIT(1); + if (!pll_control->pll[PLL_BASE + pll_id].on) { + __pll_clk_enable_reg(PLL_MODE_REG(pll)); + pll_control->pll[PLL_BASE + pll_id].on = 1; + } + + remote_spin_unlock(&pll_lock); + return 0; +} + +static void pll_clk_disable(struct clk *clk) +{ + struct pll_shared_clk *pll = to_pll_shared_clk(clk); + unsigned int pll_id = pll->id; + + remote_spin_lock(&pll_lock); + + pll_control->pll[PLL_BASE + pll_id].votes &= ~BIT(1); + if (pll_control->pll[PLL_BASE + pll_id].on + && !pll_control->pll[PLL_BASE + pll_id].votes) { + __pll_clk_disable_reg(PLL_MODE_REG(pll)); + pll_control->pll[PLL_BASE + pll_id].on = 0; + } + + remote_spin_unlock(&pll_lock); +} + +static int pll_clk_is_enabled(struct clk *clk) +{ + struct pll_shared_clk *pll = to_pll_shared_clk(clk); + + return readl_relaxed(PLL_MODE_REG(pll)) & BIT(0); +} + +static enum handoff pll_clk_handoff(struct clk *clk) +{ + struct pll_shared_clk *pll = to_pll_shared_clk(clk); + unsigned int pll_lval; + struct pll_rate *l; + + /* + * Wait for the PLLs to be initialized and then read their frequency. + */ + do { + pll_lval = readl_relaxed(PLL_MODE_REG(pll) + 4) & 0x3ff; + cpu_relax(); + udelay(50); + } while (pll_lval == 0); + + /* Convert PLL L values to PLL Output rate */ + for (l = pll_l_rate; l->rate != 0; l++) { + if (l->lvalue == pll_lval) { + clk->rate = l->rate; + break; + } + } + + if (!clk->rate) { + pr_crit("Unknown PLL's L value!\n"); + BUG(); + } + + return HANDOFF_ENABLED_CLK; +} + +struct clk_ops clk_ops_pll = { + .enable = pll_clk_enable, + .disable = pll_clk_disable, + .handoff = pll_clk_handoff, + .is_enabled = pll_clk_is_enabled, +}; + +static void __init __set_fsm_mode(void __iomem *mode_reg) +{ + u32 regval = readl_relaxed(mode_reg); + + /* De-assert reset to FSM */ + regval &= ~BIT(21); + writel_relaxed(regval, mode_reg); + + /* Program bias count */ + regval &= ~BM(19, 14); + regval |= BVAL(19, 14, 0x1); + writel_relaxed(regval, mode_reg); + + /* Program lock count */ + regval &= ~BM(13, 8); + regval |= BVAL(13, 8, 0x8); + writel_relaxed(regval, mode_reg); + + /* Enable PLL FSM voting */ + regval |= BIT(20); + writel_relaxed(regval, mode_reg); +} + +void __init configure_pll(struct pll_config *config, + struct pll_config_regs *regs, u32 ena_fsm_mode) +{ + u32 regval; + + writel_relaxed(config->l, PLL_L_REG(regs)); + writel_relaxed(config->m, PLL_M_REG(regs)); + writel_relaxed(config->n, PLL_N_REG(regs)); + + regval = readl_relaxed(PLL_CONFIG_REG(regs)); + + /* Enable the MN accumulator */ + if (config->mn_ena_mask) { + regval &= ~config->mn_ena_mask; + regval |= config->mn_ena_val; + } + + /* Enable the main output */ + if (config->main_output_mask) { + regval &= ~config->main_output_mask; + regval |= config->main_output_val; + } + + /* Set pre-divider and post-divider values */ + regval &= ~config->pre_div_mask; + regval |= config->pre_div_val; + regval &= ~config->post_div_mask; + regval |= config->post_div_val; + + /* Select VCO setting */ + regval &= ~config->vco_mask; + regval |= config->vco_val; + writel_relaxed(regval, PLL_CONFIG_REG(regs)); + + /* Configure in FSM mode if necessary */ + if (ena_fsm_mode) + __set_fsm_mode(PLL_MODE_REG(regs)); +} diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h new file mode 100644 index 00000000000..a8c642f2cc3 --- /dev/null +++ b/arch/arm/mach-msm/clock-pll.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_PLL_H +#define __ARCH_ARM_MACH_MSM_CLOCK_PLL_H + +/** + * enum - For PLL IDs + */ +enum { + PLL_TCXO = -1, + PLL_0 = 0, + PLL_1, + PLL_2, + PLL_3, + PLL_4, + PLL_END, +}; + +/** + * struct pll_shared_clk - PLL shared with other processors without + * any HW voting + * @id: PLL ID + * @mode_reg: enable register + * @parent: clock source + * @c: clk + */ +struct pll_shared_clk { + unsigned int id; + void __iomem *const mode_reg; + struct clk c; + void *const __iomem *base; +}; + +extern struct clk_ops clk_ops_pll; + +static inline struct pll_shared_clk *to_pll_shared_clk(struct clk *clk) +{ + return container_of(clk, struct pll_shared_clk, c); +} + +/** + * msm_shared_pll_control_init() - Initialize shared pll control structure + */ +void msm_shared_pll_control_init(void); + +/** + * struct pll_vote_clk - phase locked loop (HW voteable) + * @soft_vote: soft voting variable for multiple PLL software instances + * @soft_vote_mask: soft voting mask for multiple PLL software instances + * @en_reg: enable register + * @en_mask: ORed with @en_reg to enable the clock + * @status_mask: ANDed with @status_reg to determine if PLL is active. + * @status_reg: status register + * @parent: clock source + * @c: clk + */ +struct pll_vote_clk { + u32 *soft_vote; + const u32 soft_vote_mask; + void __iomem *const en_reg; + const u32 en_mask; + void __iomem *const status_reg; + const u32 status_mask; + + struct clk *parent; + struct clk c; + void *const __iomem *base; +}; + +extern struct clk_ops clk_ops_pll_vote; + +static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *clk) +{ + return container_of(clk, struct pll_vote_clk, c); +} + +/** + * struct pll_clk - phase locked loop + * @mode_reg: enable register + * @status_reg: status register, contains the lock detection bit + * @parent: clock source + * @c: clk + * @base: pointer to base address of ioremapped registers. + */ +struct pll_clk { + void __iomem *const mode_reg; + void __iomem *const status_reg; + + struct clk *parent; + struct clk c; + void *const __iomem *base; +}; + +extern struct clk_ops clk_ops_local_pll; + +static inline struct pll_clk *to_pll_clk(struct clk *clk) +{ + return container_of(clk, struct pll_clk, c); +} + +int sr_pll_clk_enable(struct clk *clk); +int copper_pll_clk_enable(struct clk *clk); + +/* + * PLL vote clock APIs + */ +int pll_vote_clk_enable(struct clk *clk); +void pll_vote_clk_disable(struct clk *clk); +struct clk *pll_vote_clk_get_parent(struct clk *clk); +int pll_vote_clk_is_enabled(struct clk *clk); + +struct pll_config { + u32 l; + u32 m; + u32 n; + u32 vco_val; + u32 vco_mask; + u32 pre_div_val; + u32 pre_div_mask; + u32 post_div_val; + u32 post_div_mask; + u32 mn_ena_val; + u32 mn_ena_mask; + u32 main_output_val; + u32 main_output_mask; +}; + +struct pll_config_regs { + void __iomem *l_reg; + void __iomem *m_reg; + void __iomem *n_reg; + void __iomem *config_reg; + void __iomem *mode_reg; + void *const __iomem *base; +}; + +void __init configure_pll(struct pll_config *, struct pll_config_regs *, u32); + +#endif diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c new file mode 100644 index 00000000000..45398281006 --- /dev/null +++ b/arch/arm/mach-msm/clock-rpm.c @@ -0,0 +1,200 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include "rpm_resources.h" +#include "clock.h" +#include "clock-rpm.h" + +static DEFINE_SPINLOCK(rpm_clock_lock); + +static int rpm_clk_enable(struct clk *clk) +{ + unsigned long flags; + struct rpm_clk *r = to_rpm_clk(clk); + struct msm_rpm_iv_pair iv = { .id = r->rpm_clk_id }; + int rc = 0; + unsigned long this_khz, this_sleep_khz; + unsigned long peer_khz = 0, peer_sleep_khz = 0; + struct rpm_clk *peer = r->peer; + + spin_lock_irqsave(&rpm_clock_lock, flags); + + this_khz = r->last_set_khz; + /* Don't send requests to the RPM if the rate has not been set. */ + if (this_khz == 0) + goto out; + + this_sleep_khz = r->last_set_sleep_khz; + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) { + peer_khz = peer->last_set_khz; + peer_sleep_khz = peer->last_set_sleep_khz; + } + + iv.value = max(this_khz, peer_khz); + if (r->branch) + iv.value = !!iv.value; + + rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1); + if (rc) + goto out; + + iv.value = max(this_sleep_khz, peer_sleep_khz); + if (r->branch) + iv.value = !!iv.value; + rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &iv, 1); + if (rc) { + iv.value = peer_khz; + msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1); + } + +out: + if (!rc) + r->enabled = true; + + spin_unlock_irqrestore(&rpm_clock_lock, flags); + + return rc; +} + +static void rpm_clk_disable(struct clk *clk) +{ + unsigned long flags; + struct rpm_clk *r = to_rpm_clk(clk); + + spin_lock_irqsave(&rpm_clock_lock, flags); + + if (r->last_set_khz) { + struct msm_rpm_iv_pair iv = { .id = r->rpm_clk_id }; + struct rpm_clk *peer = r->peer; + unsigned long peer_khz = 0, peer_sleep_khz = 0; + int rc; + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) { + peer_khz = peer->last_set_khz; + peer_sleep_khz = peer->last_set_sleep_khz; + } + + iv.value = r->branch ? !!peer_khz : peer_khz; + rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1); + if (rc) + goto out; + + iv.value = r->branch ? !!peer_sleep_khz : peer_sleep_khz; + rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &iv, 1); + } + r->enabled = false; +out: + spin_unlock_irqrestore(&rpm_clock_lock, flags); + + return; +} + +static int rpm_clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long flags; + struct rpm_clk *r = to_rpm_clk(clk); + unsigned long this_khz, this_sleep_khz; + int rc = 0; + + this_khz = DIV_ROUND_UP(rate, 1000); + + spin_lock_irqsave(&rpm_clock_lock, flags); + + /* Active-only clocks don't care what the rate is during sleep. So, + * they vote for zero. */ + if (r->active_only) + this_sleep_khz = 0; + else + this_sleep_khz = this_khz; + + if (r->enabled) { + struct msm_rpm_iv_pair iv; + struct rpm_clk *peer = r->peer; + unsigned long peer_khz = 0, peer_sleep_khz = 0; + + iv.id = r->rpm_clk_id; + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) { + peer_khz = peer->last_set_khz; + peer_sleep_khz = peer->last_set_sleep_khz; + } + + iv.value = max(this_khz, peer_khz); + rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1); + if (rc) + goto out; + + iv.value = max(this_sleep_khz, peer_sleep_khz); + rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &iv, 1); + } + if (!rc) { + r->last_set_khz = this_khz; + r->last_set_sleep_khz = this_sleep_khz; + } + +out: + spin_unlock_irqrestore(&rpm_clock_lock, flags); + + return rc; +} + +static unsigned long rpm_clk_get_rate(struct clk *clk) +{ + struct rpm_clk *r = to_rpm_clk(clk); + struct msm_rpm_iv_pair iv = { r->rpm_status_id }; + int rc; + + rc = msm_rpm_get_status(&iv, 1); + if (rc < 0) + return rc; + return iv.value * 1000; +} + +static int rpm_clk_is_enabled(struct clk *clk) +{ + return !!(rpm_clk_get_rate(clk)); +} + +static long rpm_clk_round_rate(struct clk *clk, unsigned long rate) +{ + /* Not supported. */ + return rate; +} + +static bool rpm_clk_is_local(struct clk *clk) +{ + return false; +} + +struct clk_ops clk_ops_rpm = { + .enable = rpm_clk_enable, + .disable = rpm_clk_disable, + .set_rate = rpm_clk_set_rate, + .get_rate = rpm_clk_get_rate, + .is_enabled = rpm_clk_is_enabled, + .round_rate = rpm_clk_round_rate, + .is_local = rpm_clk_is_local, +}; + +struct clk_ops clk_ops_rpm_branch = { + .enable = rpm_clk_enable, + .disable = rpm_clk_disable, + .is_local = rpm_clk_is_local, +}; diff --git a/arch/arm/mach-msm/clock-rpm.h b/arch/arm/mach-msm/clock-rpm.h new file mode 100644 index 00000000000..b0d5693c70e --- /dev/null +++ b/arch/arm/mach-msm/clock-rpm.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_RPM_H +#define __ARCH_ARM_MACH_MSM_CLOCK_RPM_H + +#include + +struct clk_ops; +extern struct clk_ops clk_ops_rpm; +extern struct clk_ops clk_ops_rpm_branch; + +struct rpm_clk { + const int rpm_clk_id; + const int rpm_status_id; + const bool active_only; + unsigned last_set_khz; + /* 0 if active_only. Otherwise, same as last_set_khz. */ + unsigned last_set_sleep_khz; + bool enabled; + bool branch; /* true: RPM only accepts 1 for ON and 0 for OFF */ + + struct rpm_clk *peer; + struct clk c; +}; + +static inline struct rpm_clk *to_rpm_clk(struct clk *clk) +{ + return container_of(clk, struct rpm_clk, c); +} + +#define DEFINE_CLK_RPM(name, active, r_id, dep) \ + static struct rpm_clk active; \ + static struct rpm_clk name = { \ + .rpm_clk_id = MSM_RPM_ID_##r_id##_CLK, \ + .rpm_status_id = MSM_RPM_STATUS_ID_##r_id##_CLK, \ + .peer = &active, \ + .c = { \ + .ops = &clk_ops_rpm, \ + .flags = CLKFLAG_SKIP_AUTO_OFF, \ + .dbg_name = #name, \ + CLK_INIT(name.c), \ + .depends = dep, \ + }, \ + }; \ + static struct rpm_clk active = { \ + .rpm_clk_id = MSM_RPM_ID_##r_id##_CLK, \ + .rpm_status_id = MSM_RPM_STATUS_ID_##r_id##_CLK, \ + .peer = &name, \ + .active_only = true, \ + .c = { \ + .ops = &clk_ops_rpm, \ + .flags = CLKFLAG_SKIP_AUTO_OFF, \ + .dbg_name = #active, \ + CLK_INIT(active.c), \ + .depends = dep, \ + }, \ + }; + +#define DEFINE_CLK_RPM_BRANCH(name, active, r_id, r) \ + static struct rpm_clk active; \ + static struct rpm_clk name = { \ + .rpm_clk_id = MSM_RPM_ID_##r_id##_CLK, \ + .rpm_status_id = MSM_RPM_STATUS_ID_##r_id##_CLK, \ + .peer = &active, \ + .last_set_khz = ((r) / 1000), \ + .last_set_sleep_khz = ((r) / 1000), \ + .branch = true, \ + .c = { \ + .ops = &clk_ops_rpm_branch, \ + .flags = CLKFLAG_SKIP_AUTO_OFF, \ + .dbg_name = #name, \ + .rate = (r), \ + CLK_INIT(name.c), \ + .warned = true, \ + }, \ + }; \ + static struct rpm_clk active = { \ + .rpm_clk_id = MSM_RPM_ID_##r_id##_CLK, \ + .rpm_status_id = MSM_RPM_STATUS_ID_##r_id##_CLK, \ + .peer = &name, \ + .last_set_khz = ((r) / 1000), \ + .active_only = true, \ + .branch = true, \ + .c = { \ + .ops = &clk_ops_rpm_branch, \ + .flags = CLKFLAG_SKIP_AUTO_OFF, \ + .dbg_name = #active, \ + .rate = (r), \ + CLK_INIT(active.c), \ + .warned = true, \ + }, \ + }; + +#endif diff --git a/arch/arm/mach-msm/clock-voter.c b/arch/arm/mach-msm/clock-voter.c new file mode 100644 index 00000000000..4cd9b1c8662 --- /dev/null +++ b/arch/arm/mach-msm/clock-voter.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "clock.h" +#include "clock-voter.h" + +static DEFINE_SPINLOCK(voter_clk_lock); + +/* Aggregate the rate of clocks that are currently on. */ +static unsigned long voter_clk_aggregate_rate(const struct clk *parent) +{ + struct clk *clk; + unsigned long rate = 0; + + list_for_each_entry(clk, &parent->children, siblings) { + struct clk_voter *v = to_clk_voter(clk); + if (v->enabled) + rate = max(clk->rate, rate); + } + return rate; +} + +static int voter_clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = 0; + unsigned long flags; + struct clk *clkp; + struct clk_voter *clkh, *v = to_clk_voter(clk); + unsigned long cur_rate, new_rate, other_rate = 0; + + spin_lock_irqsave(&voter_clk_lock, flags); + + if (v->enabled) { + struct clk *parent = v->parent; + + /* + * Get the aggregate rate without this clock's vote and update + * if the new rate is different than the current rate + */ + list_for_each_entry(clkp, &parent->children, siblings) { + clkh = to_clk_voter(clkp); + if (clkh->enabled && clkh != v) + other_rate = max(clkp->rate, other_rate); + } + + cur_rate = max(other_rate, clk->rate); + new_rate = max(other_rate, rate); + + if (new_rate != cur_rate) { + ret = clk_set_rate(parent, new_rate); + if (ret) + goto unlock; + } + } + clk->rate = rate; +unlock: + spin_unlock_irqrestore(&voter_clk_lock, flags); + + return ret; +} + +static int voter_clk_enable(struct clk *clk) +{ + int ret = 0; + unsigned long flags; + unsigned long cur_rate; + struct clk *parent; + struct clk_voter *v = to_clk_voter(clk); + + spin_lock_irqsave(&voter_clk_lock, flags); + parent = v->parent; + + /* + * Increase the rate if this clock is voting for a higher rate + * than the current rate. + */ + cur_rate = voter_clk_aggregate_rate(parent); + if (clk->rate > cur_rate) { + ret = clk_set_rate(parent, clk->rate); + if (ret) + goto out; + } + v->enabled = true; +out: + spin_unlock_irqrestore(&voter_clk_lock, flags); + + return ret; +} + +static void voter_clk_disable(struct clk *clk) +{ + unsigned long flags, cur_rate, new_rate; + struct clk *parent; + struct clk_voter *v = to_clk_voter(clk); + + spin_lock_irqsave(&voter_clk_lock, flags); + parent = v->parent; + + /* + * Decrease the rate if this clock was the only one voting for + * the highest rate. + */ + v->enabled = false; + new_rate = voter_clk_aggregate_rate(parent); + cur_rate = max(new_rate, clk->rate); + + if (new_rate < cur_rate) + clk_set_rate(parent, new_rate); + + spin_unlock_irqrestore(&voter_clk_lock, flags); +} + +static int voter_clk_is_enabled(struct clk *clk) +{ + struct clk_voter *v = to_clk_voter(clk); + return v->enabled; +} + +static long voter_clk_round_rate(struct clk *clk, unsigned long rate) +{ + struct clk_voter *v = to_clk_voter(clk); + return clk_round_rate(v->parent, rate); +} + +static struct clk *voter_clk_get_parent(struct clk *clk) +{ + struct clk_voter *v = to_clk_voter(clk); + return v->parent; +} + +static bool voter_clk_is_local(struct clk *clk) +{ + return true; +} + +static enum handoff voter_clk_handoff(struct clk *clk) +{ + /* Apply default rate vote */ + if (clk->rate) + return HANDOFF_ENABLED_CLK; + + return HANDOFF_DISABLED_CLK; +} + +struct clk_ops clk_ops_voter = { + .enable = voter_clk_enable, + .disable = voter_clk_disable, + .set_rate = voter_clk_set_rate, + .is_enabled = voter_clk_is_enabled, + .round_rate = voter_clk_round_rate, + .get_parent = voter_clk_get_parent, + .is_local = voter_clk_is_local, + .handoff = voter_clk_handoff, +}; diff --git a/arch/arm/mach-msm/clock-voter.h b/arch/arm/mach-msm/clock-voter.h new file mode 100644 index 00000000000..c9aebbab2dc --- /dev/null +++ b/arch/arm/mach-msm/clock-voter.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H +#define __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H + +struct clk_ops; +extern struct clk_ops clk_ops_voter; + +struct clk_voter { + bool enabled; + struct clk *parent; + struct clk c; +}; + +static inline struct clk_voter *to_clk_voter(struct clk *clk) +{ + return container_of(clk, struct clk_voter, c); +} + +#define DEFINE_CLK_VOTER(clk_name, _parent, _default_rate) \ + struct clk_voter clk_name = { \ + .parent = _parent, \ + .c = { \ + .dbg_name = #clk_name, \ + .ops = &clk_ops_voter, \ + .flags = CLKFLAG_SKIP_AUTO_OFF, \ + .rate = _default_rate, \ + CLK_INIT(clk_name.c), \ + }, \ + } + +#endif diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index d9145dfc2a3..8a1c6eb59fb 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/clock.c * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,170 +15,493 @@ */ #include -#include #include #include -#include -#include -#include #include #include +#include #include +#include #include "clock.h" -static DEFINE_MUTEX(clocks_mutex); -static DEFINE_SPINLOCK(clocks_lock); -static LIST_HEAD(clocks); +/* Find the voltage level required for a given rate. */ +static int find_vdd_level(struct clk *clk, unsigned long rate) +{ + int level; + + for (level = 0; level < ARRAY_SIZE(clk->fmax); level++) + if (rate <= clk->fmax[level]) + break; + + if (level == ARRAY_SIZE(clk->fmax)) { + pr_err("Rate %lu for %s is greater than highest Fmax\n", rate, + clk->dbg_name); + return -EINVAL; + } + + return level; +} + +/* Update voltage level given the current votes. */ +static int update_vdd(struct clk_vdd_class *vdd_class) +{ + int level, rc; + + for (level = ARRAY_SIZE(vdd_class->level_votes)-1; level > 0; level--) + if (vdd_class->level_votes[level]) + break; + + if (level == vdd_class->cur_level) + return 0; + + rc = vdd_class->set_vdd(vdd_class, level); + if (!rc) + vdd_class->cur_level = level; + + return rc; +} + +/* Vote for a voltage level. */ +int vote_vdd_level(struct clk_vdd_class *vdd_class, int level) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&vdd_class->lock, flags); + vdd_class->level_votes[level]++; + rc = update_vdd(vdd_class); + if (rc) + vdd_class->level_votes[level]--; + spin_unlock_irqrestore(&vdd_class->lock, flags); + + return rc; +} + +/* Remove vote for a voltage level. */ +int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&vdd_class->lock, flags); + if (WARN(!vdd_class->level_votes[level], + "Reference counts are incorrect for %s level %d\n", + vdd_class->class_name, level)) + goto out; + vdd_class->level_votes[level]--; + rc = update_vdd(vdd_class); + if (rc) + vdd_class->level_votes[level]++; +out: + spin_unlock_irqrestore(&vdd_class->lock, flags); + return rc; +} + +/* Vote for a voltage level corresponding to a clock's rate. */ +static int vote_rate_vdd(struct clk *clk, unsigned long rate) +{ + int level; + + if (!clk->vdd_class) + return 0; + + level = find_vdd_level(clk, rate); + if (level < 0) + return level; + + return vote_vdd_level(clk->vdd_class, level); +} + +/* Remove vote for a voltage level corresponding to a clock's rate. */ +static void unvote_rate_vdd(struct clk *clk, unsigned long rate) +{ + int level; + + if (!clk->vdd_class) + return; + + level = find_vdd_level(clk, rate); + if (level < 0) + return; + + unvote_vdd_level(clk->vdd_class, level); +} + +int clk_prepare(struct clk *clk) +{ + int ret = 0; + struct clk *parent; + + if (!clk) + return 0; + if (IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clk->prepare_lock); + if (clk->prepare_count == 0) { + parent = clk_get_parent(clk); + + ret = clk_prepare(parent); + if (ret) + goto out; + ret = clk_prepare(clk->depends); + if (ret) + goto err_prepare_depends; + + if (clk->ops->prepare) + ret = clk->ops->prepare(clk); + if (ret) + goto err_prepare_clock; + } + clk->prepare_count++; +out: + mutex_unlock(&clk->prepare_lock); + return ret; +err_prepare_clock: + clk_unprepare(clk->depends); +err_prepare_depends: + clk_unprepare(parent); + goto out; +} +EXPORT_SYMBOL(clk_prepare); /* * Standard clock functions defined in include/linux/clk.h */ int clk_enable(struct clk *clk) { + int ret = 0; unsigned long flags; - spin_lock_irqsave(&clocks_lock, flags); + struct clk *parent; + + if (!clk) + return 0; + if (IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clk->lock, flags); + if (WARN(!clk->warned && !clk->prepare_count, + "%s: Don't call enable on unprepared clocks\n", + clk->dbg_name)) + clk->warned = true; + if (clk->count == 0) { + parent = clk_get_parent(clk); + + ret = clk_enable(parent); + if (ret) + goto err_enable_parent; + ret = clk_enable(clk->depends); + if (ret) + goto err_enable_depends; + + ret = vote_rate_vdd(clk, clk->rate); + if (ret) + goto err_vote_vdd; + trace_clock_enable(clk->dbg_name, 1, smp_processor_id()); + if (clk->ops->enable) + ret = clk->ops->enable(clk); + if (ret) + goto err_enable_clock; + } else if (clk->flags & CLKFLAG_HANDOFF_RATE) { + /* + * The clock was already enabled by handoff code so there is no + * need to enable it again here. Clearing the handoff flag will + * prevent the lateinit handoff code from disabling the clock if + * a client driver still has it enabled. + */ + clk->flags &= ~CLKFLAG_HANDOFF_RATE; + goto out; + } clk->count++; - if (clk->count == 1) - clk->ops->enable(clk->id); - spin_unlock_irqrestore(&clocks_lock, flags); +out: + spin_unlock_irqrestore(&clk->lock, flags); + return 0; + +err_enable_clock: + unvote_rate_vdd(clk, clk->rate); +err_vote_vdd: + clk_disable(clk->depends); +err_enable_depends: + clk_disable(parent); +err_enable_parent: + spin_unlock_irqrestore(&clk->lock, flags); + return ret; } EXPORT_SYMBOL(clk_enable); void clk_disable(struct clk *clk) { unsigned long flags; - spin_lock_irqsave(&clocks_lock, flags); - BUG_ON(clk->count == 0); + + if (IS_ERR_OR_NULL(clk)) + return; + + spin_lock_irqsave(&clk->lock, flags); + if (WARN(!clk->warned && !clk->prepare_count, + "%s: Never called prepare or calling disable " + "after unprepare\n", + clk->dbg_name)) + clk->warned = true; + if (WARN(clk->count == 0, "%s is unbalanced", clk->dbg_name)) + goto out; + if (clk->count == 1) { + struct clk *parent = clk_get_parent(clk); + + trace_clock_disable(clk->dbg_name, 0, smp_processor_id()); + if (clk->ops->disable) + clk->ops->disable(clk); + unvote_rate_vdd(clk, clk->rate); + clk_disable(clk->depends); + clk_disable(parent); + } clk->count--; - if (clk->count == 0) - clk->ops->disable(clk->id); - spin_unlock_irqrestore(&clocks_lock, flags); +out: + spin_unlock_irqrestore(&clk->lock, flags); } EXPORT_SYMBOL(clk_disable); +void clk_unprepare(struct clk *clk) +{ + if (IS_ERR_OR_NULL(clk)) + return; + + mutex_lock(&clk->prepare_lock); + if (!clk->prepare_count) { + if (WARN(!clk->warned, "%s is unbalanced (prepare)", + clk->dbg_name)) + clk->warned = true; + goto out; + } + if (clk->prepare_count == 1) { + struct clk *parent = clk_get_parent(clk); + + if (WARN(!clk->warned && clk->count, + "%s: Don't call unprepare when the clock is enabled\n", + clk->dbg_name)) + clk->warned = true; + + if (clk->ops->unprepare) + clk->ops->unprepare(clk); + clk_unprepare(clk->depends); + clk_unprepare(parent); + } + clk->prepare_count--; +out: + mutex_unlock(&clk->prepare_lock); +} +EXPORT_SYMBOL(clk_unprepare); + int clk_reset(struct clk *clk, enum clk_reset_action action) { - return clk->ops->reset(clk->remote_id, action); + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->reset) + return -ENOSYS; + + return clk->ops->reset(clk, action); } EXPORT_SYMBOL(clk_reset); unsigned long clk_get_rate(struct clk *clk) { - return clk->ops->get_rate(clk->id); + if (IS_ERR_OR_NULL(clk)) + return 0; + + if (!clk->ops->get_rate) + return clk->rate; + + return clk->ops->get_rate(clk); } EXPORT_SYMBOL(clk_get_rate); int clk_set_rate(struct clk *clk, unsigned long rate) { - int ret; - if (clk->flags & CLKFLAG_MAX) { - ret = clk->ops->set_max_rate(clk->id, rate); - if (ret) - return ret; - } - if (clk->flags & CLKFLAG_MIN) { - ret = clk->ops->set_min_rate(clk->id, rate); - if (ret) - return ret; + unsigned long start_rate, flags; + int rc = 0; + + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->set_rate) + return -ENOSYS; + + spin_lock_irqsave(&clk->lock, flags); + + /* Return early if the rate isn't going to change */ + if (clk->rate == rate) + goto out; + + trace_clock_set_rate(clk->dbg_name, rate, smp_processor_id()); + if (clk->count) { + start_rate = clk->rate; + /* Enforce vdd requirements for target frequency. */ + rc = vote_rate_vdd(clk, rate); + if (rc) + goto err_vote_vdd; + rc = clk->ops->set_rate(clk, rate); + if (rc) + goto err_set_rate; + /* Release vdd requirements for starting frequency. */ + unvote_rate_vdd(clk, start_rate); + } else { + rc = clk->ops->set_rate(clk, rate); } - if (clk->flags & CLKFLAG_MAX || clk->flags & CLKFLAG_MIN) - return ret; + if (!rc) + clk->rate = rate; +out: + spin_unlock_irqrestore(&clk->lock, flags); + return rc; - return clk->ops->set_rate(clk->id, rate); +err_set_rate: + unvote_rate_vdd(clk, rate); +err_vote_vdd: + spin_unlock_irqrestore(&clk->lock, flags); + return rc; } EXPORT_SYMBOL(clk_set_rate); long clk_round_rate(struct clk *clk, unsigned long rate) { - return clk->ops->round_rate(clk->id, rate); + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->round_rate) + return -ENOSYS; + + return clk->ops->round_rate(clk, rate); } EXPORT_SYMBOL(clk_round_rate); -int clk_set_min_rate(struct clk *clk, unsigned long rate) -{ - return clk->ops->set_min_rate(clk->id, rate); -} -EXPORT_SYMBOL(clk_set_min_rate); - int clk_set_max_rate(struct clk *clk, unsigned long rate) { - return clk->ops->set_max_rate(clk->id, rate); + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->set_max_rate) + return -ENOSYS; + + return clk->ops->set_max_rate(clk, rate); } EXPORT_SYMBOL(clk_set_max_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { - return -ENOSYS; + if (!clk->ops->set_parent) + return 0; + + return clk->ops->set_parent(clk, parent); } EXPORT_SYMBOL(clk_set_parent); struct clk *clk_get_parent(struct clk *clk) { - return ERR_PTR(-ENOSYS); + if (IS_ERR_OR_NULL(clk)) + return NULL; + + if (!clk->ops->get_parent) + return NULL; + + return clk->ops->get_parent(clk); } EXPORT_SYMBOL(clk_get_parent); int clk_set_flags(struct clk *clk, unsigned long flags) { - if (clk == NULL || IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return -EINVAL; - return clk->ops->set_flags(clk->id, flags); + if (!clk->ops->set_flags) + return -ENOSYS; + + return clk->ops->set_flags(clk, flags); } EXPORT_SYMBOL(clk_set_flags); -/* EBI1 is the only shared clock that several clients want to vote on as of - * this commit. If this changes in the future, then it might be better to - * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more - * generic to support different clocks. - */ -static struct clk *ebi1_clk; +static struct clock_init_data __initdata *clk_init_data; -void __init msm_clock_init(struct clk_lookup *clock_tbl, unsigned num_clocks) +void __init msm_clock_init(struct clock_init_data *data) { unsigned n; + struct clk_lookup *clock_tbl; + size_t num_clocks; + struct clk *clk; + + clk_init_data = data; + if (clk_init_data->pre_init) + clk_init_data->pre_init(); + + clock_tbl = data->table; + num_clocks = data->size; - mutex_lock(&clocks_mutex); for (n = 0; n < num_clocks; n++) { - clkdev_add(&clock_tbl[n]); - list_add_tail(&clock_tbl[n].clk->list, &clocks); + struct clk *parent; + clk = clock_tbl[n].clk; + parent = clk_get_parent(clk); + if (parent && list_empty(&clk->siblings)) + list_add(&clk->siblings, &parent->children); } - mutex_unlock(&clocks_mutex); - ebi1_clk = clk_get(NULL, "ebi1_clk"); - BUG_ON(ebi1_clk == NULL); + /* + * Detect and preserve initial clock state until clock_late_init() or + * a driver explicitly changes it, whichever is first. + */ + for (n = 0; n < num_clocks; n++) { + clk = clock_tbl[n].clk; + if (clk->ops->handoff && !(clk->flags & CLKFLAG_HANDOFF_RATE) && + (clk->ops->handoff(clk) == HANDOFF_ENABLED_CLK)) { + clk->flags |= CLKFLAG_HANDOFF_RATE; + clk_prepare_enable(clk); + } + } + clkdev_add_table(clock_tbl, num_clocks); + + if (clk_init_data->post_init) + clk_init_data->post_init(); } -/* The bootloader and/or AMSS may have left various clocks enabled. - * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have - * not been explicitly enabled by a clk_enable() call. +/* + * The bootloader and/or AMSS may have left various clocks enabled. + * Disable any clocks that have not been explicitly enabled by a + * clk_enable() call and don't have the CLKFLAG_SKIP_AUTO_OFF flag. */ static int __init clock_late_init(void) { + unsigned n, count = 0; unsigned long flags; - struct clk *clk; - unsigned count = 0; + int ret = 0; + + clock_debug_init(clk_init_data); + for (n = 0; n < clk_init_data->size; n++) { + struct clk *clk = clk_init_data->table[n].clk; + bool handoff = false; - clock_debug_init(); - mutex_lock(&clocks_mutex); - list_for_each_entry(clk, &clocks, list) { clock_debug_add(clk); - if (clk->flags & CLKFLAG_AUTO_OFF) { - spin_lock_irqsave(&clocks_lock, flags); - if (!clk->count) { + spin_lock_irqsave(&clk->lock, flags); + if (!(clk->flags & CLKFLAG_SKIP_AUTO_OFF)) { + if (!clk->count && clk->ops->auto_off) { count++; - clk->ops->auto_off(clk->id); + clk->ops->auto_off(clk); } - spin_unlock_irqrestore(&clocks_lock, flags); } + if (clk->flags & CLKFLAG_HANDOFF_RATE) { + clk->flags &= ~CLKFLAG_HANDOFF_RATE; + handoff = true; + } + spin_unlock_irqrestore(&clk->lock, flags); + /* + * Calling this outside the lock is safe since + * it doesn't need to be atomic with the flag change. + */ + if (handoff) + clk_disable_unprepare(clk); } - mutex_unlock(&clocks_mutex); pr_info("clock_late_init() disabled %d unused clocks\n", count); - return 0; + if (clk_init_data->late_init) + ret = clk_init_data->late_init(); + return ret; } - late_initcall(clock_late_init); - diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index 2c007f606d2..1be05ad7a36 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/clock.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -17,56 +17,186 @@ #ifndef __ARCH_ARM_MACH_MSM_CLOCK_H #define __ARCH_ARM_MACH_MSM_CLOCK_H -#include +#include #include +#include +#include +#include + #include #define CLKFLAG_INVERT 0x00000001 #define CLKFLAG_NOINVERT 0x00000002 #define CLKFLAG_NONEST 0x00000004 #define CLKFLAG_NORESET 0x00000008 - -#define CLK_FIRST_AVAILABLE_FLAG 0x00000100 -#define CLKFLAG_AUTO_OFF 0x00000200 +#define CLKFLAG_HANDOFF_RATE 0x00000010 +#define CLKFLAG_HWCG 0x00000020 +#define CLKFLAG_RETAIN 0x00000040 +#define CLKFLAG_NORETAIN 0x00000080 +#define CLKFLAG_SKIP_AUTO_OFF 0x00000200 #define CLKFLAG_MIN 0x00000400 #define CLKFLAG_MAX 0x00000800 -struct clk_ops { - int (*enable)(unsigned id); - void (*disable)(unsigned id); - void (*auto_off)(unsigned id); - int (*reset)(unsigned id, enum clk_reset_action action); - int (*set_rate)(unsigned id, unsigned rate); - int (*set_min_rate)(unsigned id, unsigned rate); - int (*set_max_rate)(unsigned id, unsigned rate); - int (*set_flags)(unsigned id, unsigned flags); - unsigned (*get_rate)(unsigned id); - unsigned (*is_enabled)(unsigned id); - long (*round_rate)(unsigned id, unsigned rate); - bool (*is_local)(unsigned id); +/* + * Bit manipulation macros + */ +#define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) +#define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) + +/* + * Halt/Status Checking Mode Macros + */ +#define HALT 0 /* Bit pol: 1 = halted */ +#define NOCHECK 1 /* No bit to check, do nothing */ +#define HALT_VOTED 2 /* Bit pol: 1 = halted; delay on disable */ +#define ENABLE 3 /* Bit pol: 1 = running */ +#define ENABLE_VOTED 4 /* Bit pol: 1 = running; delay on disable */ +#define DELAY 5 /* No bit to check, just delay */ + +#define MAX_VDD_LEVELS 4 + +/** + * struct clk_vdd_class - Voltage scaling class + * @class_name: name of the class + * @set_vdd: function to call when applying a new voltage setting + * @level_votes: array of votes for each level + * @cur_level: the currently set voltage level + * @lock: lock to protect this struct + */ +struct clk_vdd_class { + const char *class_name; + int (*set_vdd)(struct clk_vdd_class *v_class, int level); + int level_votes[MAX_VDD_LEVELS]; + unsigned long cur_level; + spinlock_t lock; }; +#define DEFINE_VDD_CLASS(_name, _set_vdd) \ + struct clk_vdd_class _name = { \ + .class_name = #_name, \ + .set_vdd = _set_vdd, \ + .cur_level = ARRAY_SIZE(_name.level_votes), \ + .lock = __SPIN_LOCK_UNLOCKED(lock) \ + } + +enum handoff { + HANDOFF_ENABLED_CLK, + HANDOFF_DISABLED_CLK, + HANDOFF_UNKNOWN_RATE, +}; + +struct clk_ops { + int (*prepare)(struct clk *clk); + int (*enable)(struct clk *clk); + void (*disable)(struct clk *clk); + void (*unprepare)(struct clk *clk); + void (*auto_off)(struct clk *clk); + void (*enable_hwcg)(struct clk *clk); + void (*disable_hwcg)(struct clk *clk); + int (*in_hwcg_mode)(struct clk *clk); + enum handoff (*handoff)(struct clk *clk); + int (*reset)(struct clk *clk, enum clk_reset_action action); + int (*set_rate)(struct clk *clk, unsigned long rate); + int (*set_max_rate)(struct clk *clk, unsigned long rate); + int (*set_flags)(struct clk *clk, unsigned flags); + unsigned long (*get_rate)(struct clk *clk); + int (*list_rate)(struct clk *clk, unsigned n); + int (*is_enabled)(struct clk *clk); + long (*round_rate)(struct clk *clk, unsigned long rate); + int (*set_parent)(struct clk *clk, struct clk *parent); + struct clk *(*get_parent)(struct clk *clk); + bool (*is_local)(struct clk *clk); +}; + +/** + * struct clk + * @prepare_count: prepare refcount + * @prepare_lock: protects clk_prepare()/clk_unprepare() path and @prepare_count + * @count: enable refcount + * @lock: protects clk_enable()/clk_disable() path and @count + * @depends: non-direct parent of clock to enable when this clock is enabled + * @vdd_class: voltage scaling requirement class + * @fmax: maximum frequency in Hz supported at each voltage level + * @warned: true if the clock has warned of incorrect usage, false otherwise + */ struct clk { - uint32_t id; - uint32_t remote_id; - uint32_t count; uint32_t flags; struct clk_ops *ops; const char *dbg_name; - struct list_head list; + struct clk *depends; + struct clk_vdd_class *vdd_class; + unsigned long fmax[MAX_VDD_LEVELS]; + unsigned long rate; + + struct list_head children; + struct list_head siblings; + + bool warned; + unsigned count; + spinlock_t lock; + unsigned prepare_count; + struct mutex prepare_lock; }; -#define OFF CLKFLAG_AUTO_OFF -#define CLK_MIN CLKFLAG_MIN -#define CLK_MAX CLKFLAG_MAX -#define CLK_MINMAX (CLK_MIN | CLK_MAX) +#define CLK_INIT(name) \ + .lock = __SPIN_LOCK_UNLOCKED((name).lock), \ + .prepare_lock = __MUTEX_INITIALIZER((name).prepare_lock), \ + .children = LIST_HEAD_INIT((name).children), \ + .siblings = LIST_HEAD_INIT((name).siblings) + +/** + * struct clock_init_data - SoC specific clock initialization data + * @table: table of lookups to add + * @size: size of @table + * @pre_init: called before initializing the clock driver. + * @post_init: called after registering @table. clock APIs can be called inside. + * @late_init: called during late init + */ +struct clock_init_data { + struct clk_lookup *table; + size_t size; + void (*pre_init)(void); + void (*post_init)(void); + int (*late_init)(void); +}; + +extern struct clock_init_data msm9615_clock_init_data; +extern struct clock_init_data apq8064_clock_init_data; +extern struct clock_init_data fsm9xxx_clock_init_data; +extern struct clock_init_data msm7x01a_clock_init_data; +extern struct clock_init_data msm7x27_clock_init_data; +extern struct clock_init_data msm7x27a_clock_init_data; +extern struct clock_init_data msm7x30_clock_init_data; +extern struct clock_init_data msm8960_clock_init_data; +extern struct clock_init_data msm8x60_clock_init_data; +extern struct clock_init_data qds8x50_clock_init_data; +extern struct clock_init_data msm8625_dummy_clock_init_data; +extern struct clock_init_data msm8930_clock_init_data; +extern struct clock_init_data msmcopper_clock_init_data; + +void msm_clock_init(struct clock_init_data *data); +int vote_vdd_level(struct clk_vdd_class *vdd_class, int level); +int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level); #ifdef CONFIG_DEBUG_FS -int __init clock_debug_init(void); -int __init clock_debug_add(struct clk *clock); +int clock_debug_init(struct clock_init_data *data); +int clock_debug_add(struct clk *clock); +void clock_debug_print_enabled(void); #else -static inline int __init clock_debug_init(void) { return 0; } -static inline int __init clock_debug_add(struct clk *clock) { return 0; } +static inline int clock_debug_init(struct clk_init_data *data) { return 0; } +static inline int clock_debug_add(struct clk *clock) { return 0; } +static inline void clock_debug_print_enabled(void) { return; } #endif +extern struct clk dummy_clk; + +#define CLK_DUMMY(clk_name, clk_id, clk_dev, flags) { \ + .con_id = clk_name, \ + .dev_id = clk_dev, \ + .clk = &dummy_clk, \ + } + +#define CLK_LOOKUP(con, c, dev) { .con_id = con, .clk = &c, .dev_id = dev } + #endif + diff --git a/arch/arm/mach-msm/cp14.h b/arch/arm/mach-msm/cp14.h new file mode 100644 index 00000000000..d6404120aaf --- /dev/null +++ b/arch/arm/mach-msm/cp14.h @@ -0,0 +1,540 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_CP14_H_ +#define _ARCH_ARM_MACH_MSM_CP14_H_ + +#include + +/* Accessors for CP14 registers */ +#define dbg_read(reg) RCP14_##reg() +#define dbg_write(val, reg) WCP14_##reg(val) +#define etm_read(reg) RCP14_##reg() +#define etm_write(val, reg) WCP14_##reg(val) + +/* MRC14 and MCR14 */ +#define MRC14(op1, crn, crm, op2) \ +({ \ +uint32_t val; \ +asm volatile("mrc p14, "#op1", %0, "#crn", "#crm", "#op2 : "=r" (val)); \ +val; \ +}) + +#define MCR14(val, op1, crn, crm, op2) \ +({ \ +asm volatile("mcr p14, "#op1", %0, "#crn", "#crm", "#op2 : : "r" (val));\ +}) + +/* Debug Registers + * + * Available only in DBGv7 + * DBGECR, DBGDSCCR, DBGDSMCR, DBGDRCR + * + * Available only in DBGv7.1 + * DBGBXVRm, DBGOSDLR, DBGDEVID2, DBGDEVID1 + * + * Read only + * DBGDIDR, DBGDSCRint, DBGDTRRXint, DBGDRAR, DBGOSLSR, DBGOSSRR, DBGPRSR, + * DBGPRSR, DBGDSAR, DBGAUTHSTATUS, DBGDEVID2, DBGDEVID1, DBGDEVID + * + * Write only + * DBGDTRTXint, DBGOSLAR + */ +#define RCP14_DBGDIDR() MRC14(0, c0, c0, 0) +#define RCP14_DBGDSCRint() MRC14(0, c0, c1, 0) +#define RCP14_DBGDTRRXint() MRC14(0, c0, c5, 0) +#define RCP14_DBGWFAR() MRC14(0, c0, c6, 0) +#define RCP14_DBGVCR() MRC14(0, c0, c7, 0) +#define RCP14_DBGECR() MRC14(0, c0, c9, 0) +#define RCP14_DBGDSCCR() MRC14(0, c0, c10, 0) +#define RCP14_DBGDSMCR() MRC14(0, c0, c11, 0) +#define RCP14_DBGDTRRXext() MRC14(0, c0, c0, 2) +#define RCP14_DBGDSCRext() MRC14(0, c0, c2, 2) +#define RCP14_DBGDTRTXext() MRC14(0, c0, c3, 2) +#define RCP14_DBGDRCR() MRC14(0, c0, c4, 2) +#define RCP14_DBGBVR0() MRC14(0, c0, c0, 4) +#define RCP14_DBGBVR1() MRC14(0, c0, c1, 4) +#define RCP14_DBGBVR2() MRC14(0, c0, c2, 4) +#define RCP14_DBGBVR3() MRC14(0, c0, c3, 4) +#define RCP14_DBGBVR4() MRC14(0, c0, c4, 4) +#define RCP14_DBGBVR5() MRC14(0, c0, c5, 4) +#define RCP14_DBGBVR6() MRC14(0, c0, c6, 4) +#define RCP14_DBGBVR7() MRC14(0, c0, c7, 4) +#define RCP14_DBGBVR8() MRC14(0, c0, c8, 4) +#define RCP14_DBGBVR9() MRC14(0, c0, c9, 4) +#define RCP14_DBGBVR10() MRC14(0, c0, c10, 4) +#define RCP14_DBGBVR11() MRC14(0, c0, c11, 4) +#define RCP14_DBGBVR12() MRC14(0, c0, c12, 4) +#define RCP14_DBGBVR13() MRC14(0, c0, c13, 4) +#define RCP14_DBGBVR14() MRC14(0, c0, c14, 4) +#define RCP14_DBGBVR15() MRC14(0, c0, c15, 4) +#define RCP14_DBGBCR0() MRC14(0, c0, c0, 5) +#define RCP14_DBGBCR1() MRC14(0, c0, c1, 5) +#define RCP14_DBGBCR2() MRC14(0, c0, c2, 5) +#define RCP14_DBGBCR3() MRC14(0, c0, c3, 5) +#define RCP14_DBGBCR4() MRC14(0, c0, c4, 5) +#define RCP14_DBGBCR5() MRC14(0, c0, c5, 5) +#define RCP14_DBGBCR6() MRC14(0, c0, c6, 5) +#define RCP14_DBGBCR7() MRC14(0, c0, c7, 5) +#define RCP14_DBGBCR8() MRC14(0, c0, c8, 5) +#define RCP14_DBGBCR9() MRC14(0, c0, c9, 5) +#define RCP14_DBGBCR10() MRC14(0, c0, c10, 5) +#define RCP14_DBGBCR11() MRC14(0, c0, c11, 5) +#define RCP14_DBGBCR12() MRC14(0, c0, c12, 5) +#define RCP14_DBGBCR13() MRC14(0, c0, c13, 5) +#define RCP14_DBGBCR14() MRC14(0, c0, c14, 5) +#define RCP14_DBGBCR15() MRC14(0, c0, c15, 5) +#define RCP14_DBGWVR0() MRC14(0, c0, c0, 6) +#define RCP14_DBGWVR1() MRC14(0, c0, c1, 6) +#define RCP14_DBGWVR2() MRC14(0, c0, c2, 6) +#define RCP14_DBGWVR3() MRC14(0, c0, c3, 6) +#define RCP14_DBGWVR4() MRC14(0, c0, c4, 6) +#define RCP14_DBGWVR5() MRC14(0, c0, c5, 6) +#define RCP14_DBGWVR6() MRC14(0, c0, c6, 6) +#define RCP14_DBGWVR7() MRC14(0, c0, c7, 6) +#define RCP14_DBGWVR8() MRC14(0, c0, c8, 6) +#define RCP14_DBGWVR9() MRC14(0, c0, c9, 6) +#define RCP14_DBGWVR10() MRC14(0, c0, c10, 6) +#define RCP14_DBGWVR11() MRC14(0, c0, c11, 6) +#define RCP14_DBGWVR12() MRC14(0, c0, c12, 6) +#define RCP14_DBGWVR13() MRC14(0, c0, c13, 6) +#define RCP14_DBGWVR14() MRC14(0, c0, c14, 6) +#define RCP14_DBGWVR15() MRC14(0, c0, c15, 6) +#define RCP14_DBGWCR0() MRC14(0, c0, c0, 7) +#define RCP14_DBGWCR1() MRC14(0, c0, c1, 7) +#define RCP14_DBGWCR2() MRC14(0, c0, c2, 7) +#define RCP14_DBGWCR3() MRC14(0, c0, c3, 7) +#define RCP14_DBGWCR4() MRC14(0, c0, c4, 7) +#define RCP14_DBGWCR5() MRC14(0, c0, c5, 7) +#define RCP14_DBGWCR6() MRC14(0, c0, c6, 7) +#define RCP14_DBGWCR7() MRC14(0, c0, c7, 7) +#define RCP14_DBGWCR8() MRC14(0, c0, c8, 7) +#define RCP14_DBGWCR9() MRC14(0, c0, c9, 7) +#define RCP14_DBGWCR10() MRC14(0, c0, c10, 7) +#define RCP14_DBGWCR11() MRC14(0, c0, c11, 7) +#define RCP14_DBGWCR12() MRC14(0, c0, c12, 7) +#define RCP14_DBGWCR13() MRC14(0, c0, c13, 7) +#define RCP14_DBGWCR14() MRC14(0, c0, c14, 7) +#define RCP14_DBGWCR15() MRC14(0, c0, c15, 7) +#define RCP14_DBGDRAR() MRC14(0, c1, c0, 0) +#define RCP14_DBGBXVR0() MRC14(0, c1, c0, 1) +#define RCP14_DBGBXVR1() MRC14(0, c1, c1, 1) +#define RCP14_DBGBXVR2() MRC14(0, c1, c2, 1) +#define RCP14_DBGBXVR3() MRC14(0, c1, c3, 1) +#define RCP14_DBGBXVR4() MRC14(0, c1, c4, 1) +#define RCP14_DBGBXVR5() MRC14(0, c1, c5, 1) +#define RCP14_DBGBXVR6() MRC14(0, c1, c6, 1) +#define RCP14_DBGBXVR7() MRC14(0, c1, c7, 1) +#define RCP14_DBGBXVR8() MRC14(0, c1, c8, 1) +#define RCP14_DBGBXVR9() MRC14(0, c1, c9, 1) +#define RCP14_DBGBXVR10() MRC14(0, c1, c10, 1) +#define RCP14_DBGBXVR11() MRC14(0, c1, c11, 1) +#define RCP14_DBGBXVR12() MRC14(0, c1, c12, 1) +#define RCP14_DBGBXVR13() MRC14(0, c1, c13, 1) +#define RCP14_DBGBXVR14() MRC14(0, c1, c14, 1) +#define RCP14_DBGBXVR15() MRC14(0, c1, c15, 1) +#define RCP14_DBGOSLSR() MRC14(0, c1, c1, 4) +#define RCP14_DBGOSSRR() MRC14(0, c1, c2, 4) +#define RCP14_DBGOSDLR() MRC14(0, c1, c3, 4) +#define RCP14_DBGPRCR() MRC14(0, c1, c4, 4) +#define RCP14_DBGPRSR() MRC14(0, c1, c5, 4) +#define RCP14_DBGDSAR() MRC14(0, c2, c0, 0) +#define RCP14_DBGITCTRL() MRC14(0, c7, c0, 4) +#define RCP14_DBGCLAIMSET() MRC14(0, c7, c8, 6) +#define RCP14_DBGCLAIMCLR() MRC14(0, c7, c9, 6) +#define RCP14_DBGAUTHSTATUS() MRC14(0, c7, c14, 6) +#define RCP14_DBGDEVID2() MRC14(0, c7, c0, 7) +#define RCP14_DBGDEVID1() MRC14(0, c7, c1, 7) +#define RCP14_DBGDEVID() MRC14(0, c7, c2, 7) + +#define WCP14_DBGDTRTXint(val) MCR14(val, 0, c0, c5, 0) +#define WCP14_DBGWFAR(val) MCR14(val, 0, c0, c6, 0) +#define WCP14_DBGVCR(val) MCR14(val, 0, c0, c7, 0) +#define WCP14_DBGECR(val) MCR14(val, 0, c0, c9, 0) +#define WCP14_DBGDSCCR(val) MCR14(val, 0, c0, c10, 0) +#define WCP14_DBGDSMCR(val) MCR14(val, 0, c0, c11, 0) +#define WCP14_DBGDTRRXext(val) MCR14(val, 0, c0, c0, 2) +#define WCP14_DBGDSCRext(val) MCR14(val, 0, c0, c2, 2) +#define WCP14_DBGDTRTXext(val) MCR14(val, 0, c0, c3, 2) +#define WCP14_DBGDRCR(val) MCR14(val, 0, c0, c4, 2) +#define WCP14_DBGBVR0(val) MCR14(val, 0, c0, c0, 4) +#define WCP14_DBGBVR1(val) MCR14(val, 0, c0, c1, 4) +#define WCP14_DBGBVR2(val) MCR14(val, 0, c0, c2, 4) +#define WCP14_DBGBVR3(val) MCR14(val, 0, c0, c3, 4) +#define WCP14_DBGBVR4(val) MCR14(val, 0, c0, c4, 4) +#define WCP14_DBGBVR5(val) MCR14(val, 0, c0, c5, 4) +#define WCP14_DBGBVR6(val) MCR14(val, 0, c0, c6, 4) +#define WCP14_DBGBVR7(val) MCR14(val, 0, c0, c7, 4) +#define WCP14_DBGBVR8(val) MCR14(val, 0, c0, c8, 4) +#define WCP14_DBGBVR9(val) MCR14(val, 0, c0, c9, 4) +#define WCP14_DBGBVR10(val) MCR14(val, 0, c0, c10, 4) +#define WCP14_DBGBVR11(val) MCR14(val, 0, c0, c11, 4) +#define WCP14_DBGBVR12(val) MCR14(val, 0, c0, c12, 4) +#define WCP14_DBGBVR13(val) MCR14(val, 0, c0, c13, 4) +#define WCP14_DBGBVR14(val) MCR14(val, 0, c0, c14, 4) +#define WCP14_DBGBVR15(val) MCR14(val, 0, c0, c15, 4) +#define WCP14_DBGBCR0(val) MCR14(val, 0, c0, c0, 5) +#define WCP14_DBGBCR1(val) MCR14(val, 0, c0, c1, 5) +#define WCP14_DBGBCR2(val) MCR14(val, 0, c0, c2, 5) +#define WCP14_DBGBCR3(val) MCR14(val, 0, c0, c3, 5) +#define WCP14_DBGBCR4(val) MCR14(val, 0, c0, c4, 5) +#define WCP14_DBGBCR5(val) MCR14(val, 0, c0, c5, 5) +#define WCP14_DBGBCR6(val) MCR14(val, 0, c0, c6, 5) +#define WCP14_DBGBCR7(val) MCR14(val, 0, c0, c7, 5) +#define WCP14_DBGBCR8(val) MCR14(val, 0, c0, c8, 5) +#define WCP14_DBGBCR9(val) MCR14(val, 0, c0, c9, 5) +#define WCP14_DBGBCR10(val) MCR14(val, 0, c0, c10, 5) +#define WCP14_DBGBCR11(val) MCR14(val, 0, c0, c11, 5) +#define WCP14_DBGBCR12(val) MCR14(val, 0, c0, c12, 5) +#define WCP14_DBGBCR13(val) MCR14(val, 0, c0, c13, 5) +#define WCP14_DBGBCR14(val) MCR14(val, 0, c0, c14, 5) +#define WCP14_DBGBCR15(val) MCR14(val, 0, c0, c15, 5) +#define WCP14_DBGWVR0(val) MCR14(val, 0, c0, c0, 6) +#define WCP14_DBGWVR1(val) MCR14(val, 0, c0, c1, 6) +#define WCP14_DBGWVR2(val) MCR14(val, 0, c0, c2, 6) +#define WCP14_DBGWVR3(val) MCR14(val, 0, c0, c3, 6) +#define WCP14_DBGWVR4(val) MCR14(val, 0, c0, c4, 6) +#define WCP14_DBGWVR5(val) MCR14(val, 0, c0, c5, 6) +#define WCP14_DBGWVR6(val) MCR14(val, 0, c0, c6, 6) +#define WCP14_DBGWVR7(val) MCR14(val, 0, c0, c7, 6) +#define WCP14_DBGWVR8(val) MCR14(val, 0, c0, c8, 6) +#define WCP14_DBGWVR9(val) MCR14(val, 0, c0, c9, 6) +#define WCP14_DBGWVR10(val) MCR14(val, 0, c0, c10, 6) +#define WCP14_DBGWVR11(val) MCR14(val, 0, c0, c11, 6) +#define WCP14_DBGWVR12(val) MCR14(val, 0, c0, c12, 6) +#define WCP14_DBGWVR13(val) MCR14(val, 0, c0, c13, 6) +#define WCP14_DBGWVR14(val) MCR14(val, 0, c0, c14, 6) +#define WCP14_DBGWVR15(val) MCR14(val, 0, c0, c15, 6) +#define WCP14_DBGWCR0(val) MCR14(val, 0, c0, c0, 7) +#define WCP14_DBGWCR1(val) MCR14(val, 0, c0, c1, 7) +#define WCP14_DBGWCR2(val) MCR14(val, 0, c0, c2, 7) +#define WCP14_DBGWCR3(val) MCR14(val, 0, c0, c3, 7) +#define WCP14_DBGWCR4(val) MCR14(val, 0, c0, c4, 7) +#define WCP14_DBGWCR5(val) MCR14(val, 0, c0, c5, 7) +#define WCP14_DBGWCR6(val) MCR14(val, 0, c0, c6, 7) +#define WCP14_DBGWCR7(val) MCR14(val, 0, c0, c7, 7) +#define WCP14_DBGWCR8(val) MCR14(val, 0, c0, c8, 7) +#define WCP14_DBGWCR9(val) MCR14(val, 0, c0, c9, 7) +#define WCP14_DBGWCR10(val) MCR14(val, 0, c0, c10, 7) +#define WCP14_DBGWCR11(val) MCR14(val, 0, c0, c11, 7) +#define WCP14_DBGWCR12(val) MCR14(val, 0, c0, c12, 7) +#define WCP14_DBGWCR13(val) MCR14(val, 0, c0, c13, 7) +#define WCP14_DBGWCR14(val) MCR14(val, 0, c0, c14, 7) +#define WCP14_DBGWCR15(val) MCR14(val, 0, c0, c15, 7) +#define WCP14_DBGBXVR0(val) MCR14(val, 0, c1, c0, 1) +#define WCP14_DBGBXVR1(val) MCR14(val, 0, c1, c1, 1) +#define WCP14_DBGBXVR2(val) MCR14(val, 0, c1, c2, 1) +#define WCP14_DBGBXVR3(val) MCR14(val, 0, c1, c3, 1) +#define WCP14_DBGBXVR4(val) MCR14(val, 0, c1, c4, 1) +#define WCP14_DBGBXVR5(val) MCR14(val, 0, c1, c5, 1) +#define WCP14_DBGBXVR6(val) MCR14(val, 0, c1, c6, 1) +#define WCP14_DBGBXVR7(val) MCR14(val, 0, c1, c7, 1) +#define WCP14_DBGBXVR8(val) MCR14(val, 0, c1, c8, 1) +#define WCP14_DBGBXVR9(val) MCR14(val, 0, c1, c9, 1) +#define WCP14_DBGBXVR10(val) MCR14(val, 0, c1, c10, 1) +#define WCP14_DBGBXVR11(val) MCR14(val, 0, c1, c11, 1) +#define WCP14_DBGBXVR12(val) MCR14(val, 0, c1, c12, 1) +#define WCP14_DBGBXVR13(val) MCR14(val, 0, c1, c13, 1) +#define WCP14_DBGBXVR14(val) MCR14(val, 0, c1, c14, 1) +#define WCP14_DBGBXVR15(val) MCR14(val, 0, c1, c15, 1) +#define WCP14_DBGOSLAR(val) MCR14(val, 0, c1, c0, 4) +#define WCP14_DBGOSSRR(val) MCR14(val, 0, c1, c2, 4) +#define WCP14_DBGOSDLR(val) MCR14(val, 0, c1, c3, 4) +#define WCP14_DBGPRCR(val) MCR14(val, 0, c1, c4, 4) +#define WCP14_DBGITCTRL(val) MCR14(val, 0, c7, c0, 4) +#define WCP14_DBGCLAIMSET(val) MCR14(val, 0, c7, c8, 6) +#define WCP14_DBGCLAIMCLR(val) MCR14(val, 0, c7, c9, 6) + +/* ETM Registers + * + * Available only in ETMv3.3, 3.4, 3.5 + * ETMASICCR, ETMTECR2, ETMFFRR, ETMVDEVR, ETMVDCR1, ETMVDCR2, ETMVDCR3, + * ETMDCVRn, ETMDCMRn + * + * Available only in ETMv3.5 as read only + * ETMIDR2 + * + * Available only in ETMv3.5, PFTv1.0, 1.1 + * ETMTSEVR, ETMVMIDCVR, ETMPDCR + * + * Read only + * ETMCCR, ETMSCR, ETMIDR, ETMCCER, ETMOSLSR + * ETMLSR, ETMAUTHSTATUS, ETMDEVID, ETMDEVTYPE, ETMPIDR4, ETMPIDR5, ETMPIDR6, + * ETMPIDR7, ETMPIDR0, ETMPIDR1, ETMPIDR2, ETMPIDR2, ETMPIDR3, ETMCIDR0, + * ETMCIDR1, ETMCIDR2, ETMCIDR3 + * + * Write only + * ETMOSLAR, ETMLAR + * Note: ETMCCER[11] controls WO nature of certain regs. Refer ETM arch spec. + */ +#define RCP14_ETMCR() MRC14(1, c0, c0, 0) +#define RCP14_ETMCCR() MRC14(1, c0, c1, 0) +#define RCP14_ETMTRIGGER() MRC14(1, c0, c2, 0) +#define RCP14_ETMASICCR() MRC14(1, c0, c3, 0) +#define RCP14_ETMSR() MRC14(1, c0, c4, 0) +#define RCP14_ETMSCR() MRC14(1, c0, c5, 0) +#define RCP14_ETMTSSCR() MRC14(1, c0, c6, 0) +#define RCP14_ETMTECR2() MRC14(1, c0, c7, 0) +#define RCP14_ETMTEEVR() MRC14(1, c0, c8, 0) +#define RCP14_ETMTECR1() MRC14(1, c0, c9, 0) +#define RCP14_ETMFFRR() MRC14(1, c0, c10, 0) +#define RCP14_ETMFFLR() MRC14(1, c0, c11, 0) +#define RCP14_ETMVDEVR() MRC14(1, c0, c12, 0) +#define RCP14_ETMVDCR1() MRC14(1, c0, c13, 0) +#define RCP14_ETMVDCR2() MRC14(1, c0, c14, 0) +#define RCP14_ETMVDCR3() MRC14(1, c0, c15, 0) +#define RCP14_ETMACVR0() MRC14(1, c0, c0, 1) +#define RCP14_ETMACVR1() MRC14(1, c0, c1, 1) +#define RCP14_ETMACVR2() MRC14(1, c0, c2, 1) +#define RCP14_ETMACVR3() MRC14(1, c0, c3, 1) +#define RCP14_ETMACVR4() MRC14(1, c0, c4, 1) +#define RCP14_ETMACVR5() MRC14(1, c0, c5, 1) +#define RCP14_ETMACVR6() MRC14(1, c0, c6, 1) +#define RCP14_ETMACVR7() MRC14(1, c0, c7, 1) +#define RCP14_ETMACVR8() MRC14(1, c0, c8, 1) +#define RCP14_ETMACVR9() MRC14(1, c0, c9, 1) +#define RCP14_ETMACVR10() MRC14(1, c0, c10, 1) +#define RCP14_ETMACVR11() MRC14(1, c0, c11, 1) +#define RCP14_ETMACVR12() MRC14(1, c0, c12, 1) +#define RCP14_ETMACVR13() MRC14(1, c0, c13, 1) +#define RCP14_ETMACVR14() MRC14(1, c0, c14, 1) +#define RCP14_ETMACVR15() MRC14(1, c0, c15, 1) +#define RCP14_ETMACTR0() MRC14(1, c0, c0, 2) +#define RCP14_ETMACTR1() MRC14(1, c0, c1, 2) +#define RCP14_ETMACTR2() MRC14(1, c0, c2, 2) +#define RCP14_ETMACTR3() MRC14(1, c0, c3, 2) +#define RCP14_ETMACTR4() MRC14(1, c0, c4, 2) +#define RCP14_ETMACTR5() MRC14(1, c0, c5, 2) +#define RCP14_ETMACTR6() MRC14(1, c0, c6, 2) +#define RCP14_ETMACTR7() MRC14(1, c0, c7, 2) +#define RCP14_ETMACTR8() MRC14(1, c0, c8, 2) +#define RCP14_ETMACTR9() MRC14(1, c0, c9, 2) +#define RCP14_ETMACTR10() MRC14(1, c0, c10, 2) +#define RCP14_ETMACTR11() MRC14(1, c0, c11, 2) +#define RCP14_ETMACTR12() MRC14(1, c0, c12, 2) +#define RCP14_ETMACTR13() MRC14(1, c0, c13, 2) +#define RCP14_ETMACTR14() MRC14(1, c0, c14, 2) +#define RCP14_ETMACTR15() MRC14(1, c0, c15, 2) +#define RCP14_ETMDCVR0() MRC14(1, c0, c0, 3) +#define RCP14_ETMDCVR2() MRC14(1, c0, c2, 3) +#define RCP14_ETMDCVR4() MRC14(1, c0, c4, 3) +#define RCP14_ETMDCVR6() MRC14(1, c0, c6, 3) +#define RCP14_ETMDCVR8() MRC14(1, c0, c8, 3) +#define RCP14_ETMDCVR10() MRC14(1, c0, c10, 3) +#define RCP14_ETMDCVR12() MRC14(1, c0, c12, 3) +#define RCP14_ETMDCVR14() MRC14(1, c0, c14, 3) +#define RCP14_ETMDCMR0() MRC14(1, c0, c0, 4) +#define RCP14_ETMDCMR2() MRC14(1, c0, c2, 4) +#define RCP14_ETMDCMR4() MRC14(1, c0, c4, 4) +#define RCP14_ETMDCMR6() MRC14(1, c0, c6, 4) +#define RCP14_ETMDCMR8() MRC14(1, c0, c8, 4) +#define RCP14_ETMDCMR10() MRC14(1, c0, c10, 4) +#define RCP14_ETMDCMR12() MRC14(1, c0, c12, 4) +#define RCP14_ETMDCMR14() MRC14(1, c0, c14, 4) +#define RCP14_ETMCNTRLDVR0() MRC14(1, c0, c0, 5) +#define RCP14_ETMCNTRLDVR1() MRC14(1, c0, c1, 5) +#define RCP14_ETMCNTRLDVR2() MRC14(1, c0, c2, 5) +#define RCP14_ETMCNTRLDVR3() MRC14(1, c0, c3, 5) +#define RCP14_ETMCNTENR0() MRC14(1, c0, c4, 5) +#define RCP14_ETMCNTENR1() MRC14(1, c0, c5, 5) +#define RCP14_ETMCNTENR2() MRC14(1, c0, c6, 5) +#define RCP14_ETMCNTENR3() MRC14(1, c0, c7, 5) +#define RCP14_ETMCNTRLDEVR0() MRC14(1, c0, c8, 5) +#define RCP14_ETMCNTRLDEVR1() MRC14(1, c0, c9, 5) +#define RCP14_ETMCNTRLDEVR2() MRC14(1, c0, c10, 5) +#define RCP14_ETMCNTRLDEVR3() MRC14(1, c0, c11, 5) +#define RCP14_ETMCNTVR0() MRC14(1, c0, c12, 5) +#define RCP14_ETMCNTVR1() MRC14(1, c0, c13, 5) +#define RCP14_ETMCNTVR2() MRC14(1, c0, c14, 5) +#define RCP14_ETMCNTVR3() MRC14(1, c0, c15, 5) +#define RCP14_ETMSQ12EVR() MRC14(1, c0, c0, 6) +#define RCP14_ETMSQ21EVR() MRC14(1, c0, c1, 6) +#define RCP14_ETMSQ23EVR() MRC14(1, c0, c2, 6) +#define RCP14_ETMSQ31EVR() MRC14(1, c0, c3, 6) +#define RCP14_ETMSQ32EVR() MRC14(1, c0, c4, 6) +#define RCP14_ETMSQ13EVR() MRC14(1, c0, c5, 6) +#define RCP14_ETMSQR() MRC14(1, c0, c7, 6) +#define RCP14_ETMEXTOUTEVR0() MRC14(1, c0, c8, 6) +#define RCP14_ETMEXTOUTEVR1() MRC14(1, c0, c9, 6) +#define RCP14_ETMEXTOUTEVR2() MRC14(1, c0, c10, 6) +#define RCP14_ETMEXTOUTEVR3() MRC14(1, c0, c11, 6) +#define RCP14_ETMCIDCVR0() MRC14(1, c0, c12, 6) +#define RCP14_ETMCIDCVR1() MRC14(1, c0, c13, 6) +#define RCP14_ETMCIDCVR2() MRC14(1, c0, c14, 6) +#define RCP14_ETMCIDCMR() MRC14(1, c0, c15, 6) +#define RCP14_ETMIMPSPEC0() MRC14(1, c0, c0, 7) +#define RCP14_ETMIMPSPEC1() MRC14(1, c0, c1, 7) +#define RCP14_ETMIMPSPEC2() MRC14(1, c0, c2, 7) +#define RCP14_ETMIMPSPEC3() MRC14(1, c0, c3, 7) +#define RCP14_ETMIMPSPEC4() MRC14(1, c0, c4, 7) +#define RCP14_ETMIMPSPEC5() MRC14(1, c0, c5, 7) +#define RCP14_ETMIMPSPEC6() MRC14(1, c0, c6, 7) +#define RCP14_ETMIMPSPEC7() MRC14(1, c0, c7, 7) +#define RCP14_ETMSYNCFR() MRC14(1, c0, c8, 7) +#define RCP14_ETMIDR() MRC14(1, c0, c9, 7) +#define RCP14_ETMCCER() MRC14(1, c0, c10, 7) +#define RCP14_ETMEXTINSELR() MRC14(1, c0, c11, 7) +#define RCP14_ETMTESSEICR() MRC14(1, c0, c12, 7) +#define RCP14_ETMEIBCR() MRC14(1, c0, c13, 7) +#define RCP14_ETMTSEVR() MRC14(1, c0, c14, 7) +#define RCP14_ETMAUXCR() MRC14(1, c0, c15, 7) +#define RCP14_ETMTRACEIDR() MRC14(1, c1, c0, 0) +#define RCP14_ETMIDR2() MRC14(1, c1, c2, 0) +#define RCP14_ETMVMIDCVR() MRC14(1, c1, c0, 1) +#define RCP14_ETMOSLSR() MRC14(1, c1, c1, 4) +/* not available in PFTv1.1 */ +#define RCP14_ETMOSSRR() MRC14(1, c1, c2, 4) +#define RCP14_ETMPDCR() MRC14(1, c1, c4, 4) +#define RCP14_ETMPDSR() MRC14(1, c1, c5, 4) +#define RCP14_ETMITCTRL() MRC14(1, c7, c0, 4) +#define RCP14_ETMCLAIMSET() MRC14(1, c7, c8, 6) +#define RCP14_ETMCLAIMCLR() MRC14(1, c7, c9, 6) +#define RCP14_ETMLSR() MRC14(1, c7, c13, 6) +#define RCP14_ETMAUTHSTATUS() MRC14(1, c7, c14, 6) +#define RCP14_ETMDEVID() MRC14(1, c7, c2, 7) +#define RCP14_ETMDEVTYPE() MRC14(1, c7, c3, 7) +#define RCP14_ETMPIDR4() MRC14(1, c7, c4, 7) +#define RCP14_ETMPIDR5() MRC14(1, c7, c5, 7) +#define RCP14_ETMPIDR6() MRC14(1, c7, c6, 7) +#define RCP14_ETMPIDR7() MRC14(1, c7, c7, 7) +#define RCP14_ETMPIDR0() MRC14(1, c7, c8, 7) +#define RCP14_ETMPIDR1() MRC14(1, c7, c9, 7) +#define RCP14_ETMPIDR2() MRC14(1, c7, c10, 7) +#define RCP14_ETMPIDR3() MRC14(1, c7, c11, 7) +#define RCP14_ETMCIDR0() MRC14(1, c7, c12, 7) +#define RCP14_ETMCIDR1() MRC14(1, c7, c13, 7) +#define RCP14_ETMCIDR2() MRC14(1, c7, c14, 7) +#define RCP14_ETMCIDR3() MRC14(1, c7, c15, 7) + +#define WCP14_ETMCR(val) MCR14(val, 1, c0, c0, 0) +#define WCP14_ETMTRIGGER(val) MCR14(val, 1, c0, c2, 0) +#define WCP14_ETMASICCR(val) MCR14(val, 1, c0, c3, 0) +#define WCP14_ETMSR(val) MCR14(val, 1, c0, c4, 0) +#define WCP14_ETMTSSCR(val) MCR14(val, 1, c0, c6, 0) +#define WCP14_ETMTECR2(val) MCR14(val, 1, c0, c7, 0) +#define WCP14_ETMTEEVR(val) MCR14(val, 1, c0, c8, 0) +#define WCP14_ETMTECR1(val) MCR14(val, 1, c0, c9, 0) +#define WCP14_ETMFFRR(val) MCR14(val, 1, c0, c10, 0) +#define WCP14_ETMFFLR(val) MCR14(val, 1, c0, c11, 0) +#define WCP14_ETMVDEVR(val) MCR14(val, 1, c0, c12, 0) +#define WCP14_ETMVDCR1(val) MCR14(val, 1, c0, c13, 0) +#define WCP14_ETMVDCR2(val) MCR14(val, 1, c0, c14, 0) +#define WCP14_ETMVDCR3(val) MCR14(val, 1, c0, c15, 0) +#define WCP14_ETMACVR0(val) MCR14(val, 1, c0, c0, 1) +#define WCP14_ETMACVR1(val) MCR14(val, 1, c0, c1, 1) +#define WCP14_ETMACVR2(val) MCR14(val, 1, c0, c2, 1) +#define WCP14_ETMACVR3(val) MCR14(val, 1, c0, c3, 1) +#define WCP14_ETMACVR4(val) MCR14(val, 1, c0, c4, 1) +#define WCP14_ETMACVR5(val) MCR14(val, 1, c0, c5, 1) +#define WCP14_ETMACVR6(val) MCR14(val, 1, c0, c6, 1) +#define WCP14_ETMACVR7(val) MCR14(val, 1, c0, c7, 1) +#define WCP14_ETMACVR8(val) MCR14(val, 1, c0, c8, 1) +#define WCP14_ETMACVR9(val) MCR14(val, 1, c0, c9, 1) +#define WCP14_ETMACVR10(val) MCR14(val, 1, c0, c10, 1) +#define WCP14_ETMACVR11(val) MCR14(val, 1, c0, c11, 1) +#define WCP14_ETMACVR12(val) MCR14(val, 1, c0, c12, 1) +#define WCP14_ETMACVR13(val) MCR14(val, 1, c0, c13, 1) +#define WCP14_ETMACVR14(val) MCR14(val, 1, c0, c14, 1) +#define WCP14_ETMACVR15(val) MCR14(val, 1, c0, c15, 1) +#define WCP14_ETMACTR0(val) MCR14(val, 1, c0, c0, 2) +#define WCP14_ETMACTR1(val) MCR14(val, 1, c0, c1, 2) +#define WCP14_ETMACTR2(val) MCR14(val, 1, c0, c2, 2) +#define WCP14_ETMACTR3(val) MCR14(val, 1, c0, c3, 2) +#define WCP14_ETMACTR4(val) MCR14(val, 1, c0, c4, 2) +#define WCP14_ETMACTR5(val) MCR14(val, 1, c0, c5, 2) +#define WCP14_ETMACTR6(val) MCR14(val, 1, c0, c6, 2) +#define WCP14_ETMACTR7(val) MCR14(val, 1, c0, c7, 2) +#define WCP14_ETMACTR8(val) MCR14(val, 1, c0, c8, 2) +#define WCP14_ETMACTR9(val) MCR14(val, 1, c0, c9, 2) +#define WCP14_ETMACTR10(val) MCR14(val, 1, c0, c10, 2) +#define WCP14_ETMACTR11(val) MCR14(val, 1, c0, c11, 2) +#define WCP14_ETMACTR12(val) MCR14(val, 1, c0, c12, 2) +#define WCP14_ETMACTR13(val) MCR14(val, 1, c0, c13, 2) +#define WCP14_ETMACTR14(val) MCR14(val, 1, c0, c14, 2) +#define WCP14_ETMACTR15(val) MCR14(val, 1, c0, c15, 2) +#define WCP14_ETMDCVR0(val) MCR14(val, 1, c0, c0, 3) +#define WCP14_ETMDCVR2(val) MCR14(val, 1, c0, c2, 3) +#define WCP14_ETMDCVR4(val) MCR14(val, 1, c0, c4, 3) +#define WCP14_ETMDCVR6(val) MCR14(val, 1, c0, c6, 3) +#define WCP14_ETMDCVR8(val) MCR14(val, 1, c0, c8, 3) +#define WCP14_ETMDCVR10(val) MCR14(val, 1, c0, c10, 3) +#define WCP14_ETMDCVR12(val) MCR14(val, 1, c0, c12, 3) +#define WCP14_ETMDCVR14(val) MCR14(val, 1, c0, c14, 3) +#define WCP14_ETMDCMR0(val) MCR14(val, 1, c0, c0, 4) +#define WCP14_ETMDCMR2(val) MCR14(val, 1, c0, c2, 4) +#define WCP14_ETMDCMR4(val) MCR14(val, 1, c0, c4, 4) +#define WCP14_ETMDCMR6(val) MCR14(val, 1, c0, c6, 4) +#define WCP14_ETMDCMR8(val) MCR14(val, 1, c0, c8, 4) +#define WCP14_ETMDCMR10(val) MCR14(val, 1, c0, c10, 4) +#define WCP14_ETMDCMR12(val) MCR14(val, 1, c0, c12, 4) +#define WCP14_ETMDCMR14(val) MCR14(val, 1, c0, c14, 4) +#define WCP14_ETMCNTRLDVR0(val) MCR14(val, 1, c0, c0, 5) +#define WCP14_ETMCNTRLDVR1(val) MCR14(val, 1, c0, c1, 5) +#define WCP14_ETMCNTRLDVR2(val) MCR14(val, 1, c0, c2, 5) +#define WCP14_ETMCNTRLDVR3(val) MCR14(val, 1, c0, c3, 5) +#define WCP14_ETMCNTENR0(val) MCR14(val, 1, c0, c4, 5) +#define WCP14_ETMCNTENR1(val) MCR14(val, 1, c0, c5, 5) +#define WCP14_ETMCNTENR2(val) MCR14(val, 1, c0, c6, 5) +#define WCP14_ETMCNTENR3(val) MCR14(val, 1, c0, c7, 5) +#define WCP14_ETMCNTRLDEVR0(val) MCR14(val, 1, c0, c8, 5) +#define WCP14_ETMCNTRLDEVR1(val) MCR14(val, 1, c0, c9, 5) +#define WCP14_ETMCNTRLDEVR2(val) MCR14(val, 1, c0, c10, 5) +#define WCP14_ETMCNTRLDEVR3(val) MCR14(val, 1, c0, c11, 5) +#define WCP14_ETMCNTVR0(val) MCR14(val, 1, c0, c12, 5) +#define WCP14_ETMCNTVR1(val) MCR14(val, 1, c0, c13, 5) +#define WCP14_ETMCNTVR2(val) MCR14(val, 1, c0, c14, 5) +#define WCP14_ETMCNTVR3(val) MCR14(val, 1, c0, c15, 5) +#define WCP14_ETMSQ12EVR(val) MCR14(val, 1, c0, c0, 6) +#define WCP14_ETMSQ21EVR(val) MCR14(val, 1, c0, c1, 6) +#define WCP14_ETMSQ23EVR(val) MCR14(val, 1, c0, c2, 6) +#define WCP14_ETMSQ31EVR(val) MCR14(val, 1, c0, c3, 6) +#define WCP14_ETMSQ32EVR(val) MCR14(val, 1, c0, c4, 6) +#define WCP14_ETMSQ13EVR(val) MCR14(val, 1, c0, c5, 6) +#define WCP14_ETMSQR(val) MCR14(val, 1, c0, c7, 6) +#define WCP14_ETMEXTOUTEVR0(val) MCR14(val, 1, c0, c8, 6) +#define WCP14_ETMEXTOUTEVR1(val) MCR14(val, 1, c0, c9, 6) +#define WCP14_ETMEXTOUTEVR2(val) MCR14(val, 1, c0, c10, 6) +#define WCP14_ETMEXTOUTEVR3(val) MCR14(val, 1, c0, c11, 6) +#define WCP14_ETMCIDCVR0(val) MCR14(val, 1, c0, c12, 6) +#define WCP14_ETMCIDCVR1(val) MCR14(val, 1, c0, c13, 6) +#define WCP14_ETMCIDCVR2(val) MCR14(val, 1, c0, c14, 6) +#define WCP14_ETMCIDCMR(val) MCR14(val, 1, c0, c15, 6) +#define WCP14_ETMIMPSPEC0(val) MCR14(val, 1, c0, c0, 7) +#define WCP14_ETMIMPSPEC1(val) MCR14(val, 1, c0, c1, 7) +#define WCP14_ETMIMPSPEC2(val) MCR14(val, 1, c0, c2, 7) +#define WCP14_ETMIMPSPEC3(val) MCR14(val, 1, c0, c3, 7) +#define WCP14_ETMIMPSPEC4(val) MCR14(val, 1, c0, c4, 7) +#define WCP14_ETMIMPSPEC5(val) MCR14(val, 1, c0, c5, 7) +#define WCP14_ETMIMPSPEC6(val) MCR14(val, 1, c0, c6, 7) +#define WCP14_ETMIMPSPEC7(val) MCR14(val, 1, c0, c7, 7) +/* can be read only in ETMv3.4, ETMv3.5 */ +#define WCP14_ETMSYNCFR(val) MCR14(val, 1, c0, c8, 7) +#define WCP14_ETMEXTINSELR(val) MCR14(val, 1, c0, c11, 7) +#define WCP14_ETMTESSEICR(val) MCR14(val, 1, c0, c12, 7) +#define WCP14_ETMEIBCR(val) MCR14(val, 1, c0, c13, 7) +#define WCP14_ETMTSEVR(val) MCR14(val, 1, c0, c14, 7) +#define WCP14_ETMAUXCR(val) MCR14(val, 1, c0, c15, 7) +#define WCP14_ETMTRACEIDR(val) MCR14(val, 1, c1, c0, 0) +#define WCP14_ETMIDR2(val) MCR14(val, 1, c1, c2, 0) +#define WCP14_ETMVMIDCVR(val) MCR14(val, 1, c1, c0, 1) +#define WCP14_ETMOSLAR(val) MCR14(val, 1, c1, c0, 4) +/* not available in PFTv1.1 */ +#define WCP14_ETMOSSRR(val) MCR14(val, 1, c1, c2, 4) +#define WCP14_ETMPDCR(val) MCR14(val, 1, c1, c4, 4) +#define WCP14_ETMPDSR(val) MCR14(val, 1, c1, c5, 4) +#define WCP14_ETMITCTRL(val) MCR14(val, 1, c7, c0, 4) +#define WCP14_ETMCLAIMSET(val) MCR14(val, 1, c7, c8, 6) +#define WCP14_ETMCLAIMCLR(val) MCR14(val, 1, c7, c9, 6) +/* writes to this from CP14 interface are ignored */ +#define WCP14_ETMLAR(val) MCR14(val, 1, c7, c12, 6) + +#endif diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c new file mode 100644 index 00000000000..63534a4da08 --- /dev/null +++ b/arch/arm/mach-msm/cpufreq.c @@ -0,0 +1,303 @@ +/* arch/arm/mach-msm/cpufreq.c + * + * MSM architecture cpufreq driver + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. + * Author: Mike A. Chan + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acpuclock.h" + +#ifdef CONFIG_SMP +struct cpufreq_work_struct { + struct work_struct work; + struct cpufreq_policy *policy; + struct completion complete; + int frequency; + int status; +}; + +static DEFINE_PER_CPU(struct cpufreq_work_struct, cpufreq_work); +static struct workqueue_struct *msm_cpufreq_wq; +#endif + +struct cpufreq_suspend_t { + struct mutex suspend_mutex; + int device_suspended; +}; + +static DEFINE_PER_CPU(struct cpufreq_suspend_t, cpufreq_suspend); + +static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq) +{ + int ret = 0; + struct cpufreq_freqs freqs; + + freqs.old = policy->cur; + freqs.new = new_freq; + freqs.cpu = policy->cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + ret = acpuclk_set_rate(policy->cpu, new_freq, SETRATE_CPUFREQ); + if (!ret) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +#ifdef CONFIG_SMP +static void set_cpu_work(struct work_struct *work) +{ + struct cpufreq_work_struct *cpu_work = + container_of(work, struct cpufreq_work_struct, work); + + cpu_work->status = set_cpu_freq(cpu_work->policy, cpu_work->frequency); + complete(&cpu_work->complete); +} +#endif + +static int msm_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int ret = -EFAULT; + int index; + struct cpufreq_frequency_table *table; +#ifdef CONFIG_SMP + struct cpufreq_work_struct *cpu_work = NULL; + cpumask_var_t mask; + + if (!cpu_active(policy->cpu)) { + pr_info("cpufreq: cpu %d is not active.\n", policy->cpu); + return -ENODEV; + } + + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; +#endif + + mutex_lock(&per_cpu(cpufreq_suspend, policy->cpu).suspend_mutex); + + if (per_cpu(cpufreq_suspend, policy->cpu).device_suspended) { + pr_debug("cpufreq: cpu%d scheduling frequency change " + "in suspend.\n", policy->cpu); + ret = -EFAULT; + goto done; + } + + table = cpufreq_frequency_get_table(policy->cpu); + if (cpufreq_frequency_table_target(policy, table, target_freq, relation, + &index)) { + pr_err("cpufreq: invalid target_freq: %d\n", target_freq); + ret = -EINVAL; + goto done; + } + +#ifdef CONFIG_CPU_FREQ_DEBUG + pr_debug("CPU[%d] target %d relation %d (%d-%d) selected %d\n", + policy->cpu, target_freq, relation, + policy->min, policy->max, table[index].frequency); +#endif + +#ifdef CONFIG_SMP + cpu_work = &per_cpu(cpufreq_work, policy->cpu); + cpu_work->policy = policy; + cpu_work->frequency = table[index].frequency; + cpu_work->status = -ENODEV; + + cpumask_clear(mask); + cpumask_set_cpu(policy->cpu, mask); + if (cpumask_equal(mask, ¤t->cpus_allowed)) { + ret = set_cpu_freq(cpu_work->policy, cpu_work->frequency); + goto done; + } else { + cancel_work_sync(&cpu_work->work); + INIT_COMPLETION(cpu_work->complete); + queue_work_on(policy->cpu, msm_cpufreq_wq, &cpu_work->work); + wait_for_completion(&cpu_work->complete); + } + + ret = cpu_work->status; +#else + ret = set_cpu_freq(policy, table[index].frequency); +#endif + +done: +#ifdef CONFIG_SMP + free_cpumask_var(mask); +#endif + mutex_unlock(&per_cpu(cpufreq_suspend, policy->cpu).suspend_mutex); + return ret; +} + +static int msm_cpufreq_verify(struct cpufreq_policy *policy) +{ + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + return 0; +} + +static int __cpuinit msm_cpufreq_init(struct cpufreq_policy *policy) +{ + int cur_freq; + int index; + struct cpufreq_frequency_table *table; +#ifdef CONFIG_SMP + struct cpufreq_work_struct *cpu_work = NULL; +#endif + + + table = cpufreq_frequency_get_table(policy->cpu); + if (table == NULL) + return -ENODEV; + /* + * In 8625 both cpu core's frequency can not + * be changed independently. Each cpu is bound to + * same frequency. Hence set the cpumask to all cpu. + */ + if (cpu_is_msm8625()) + cpumask_setall(policy->cpus); + + if (cpufreq_frequency_table_cpuinfo(policy, table)) { +#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX + policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_MIN; + policy->cpuinfo.max_freq = CONFIG_MSM_CPU_FREQ_MAX; +#endif + } +#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX + policy->min = CONFIG_MSM_CPU_FREQ_MIN; + policy->max = CONFIG_MSM_CPU_FREQ_MAX; +#endif + + cur_freq = acpuclk_get_rate(policy->cpu); + if (cpufreq_frequency_table_target(policy, table, cur_freq, + CPUFREQ_RELATION_H, &index) && + cpufreq_frequency_table_target(policy, table, cur_freq, + CPUFREQ_RELATION_L, &index)) { + pr_info("cpufreq: cpu%d at invalid freq: %d\n", + policy->cpu, cur_freq); + return -EINVAL; + } + + if (cur_freq != table[index].frequency) { + int ret = 0; + ret = acpuclk_set_rate(policy->cpu, table[index].frequency, + SETRATE_CPUFREQ); + if (ret) + return ret; + pr_info("cpufreq: cpu%d init at %d switching to %d\n", + policy->cpu, cur_freq, table[index].frequency); + cur_freq = table[index].frequency; + } + + policy->cur = cur_freq; + + policy->cpuinfo.transition_latency = + acpuclk_get_switch_time() * NSEC_PER_USEC; +#ifdef CONFIG_SMP + cpu_work = &per_cpu(cpufreq_work, policy->cpu); + INIT_WORK(&cpu_work->work, set_cpu_work); + init_completion(&cpu_work->complete); +#endif + + return 0; +} + +static int msm_cpufreq_suspend(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + mutex_lock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex); + per_cpu(cpufreq_suspend, cpu).device_suspended = 1; + mutex_unlock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex); + } + + return NOTIFY_DONE; +} + +static int msm_cpufreq_resume(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + per_cpu(cpufreq_suspend, cpu).device_suspended = 0; + } + + return NOTIFY_DONE; +} + +static int msm_cpufreq_pm_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + switch (event) { + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + return msm_cpufreq_resume(); + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + return msm_cpufreq_suspend(); + default: + return NOTIFY_DONE; + } +} + +static struct freq_attr *msm_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver msm_cpufreq_driver = { + /* lps calculations are handled here. */ + .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS, + .init = msm_cpufreq_init, + .verify = msm_cpufreq_verify, + .target = msm_cpufreq_target, + .name = "msm", + .attr = msm_freq_attr, +}; + +static struct notifier_block msm_cpufreq_pm_notifier = { + .notifier_call = msm_cpufreq_pm_event, +}; + +static int __init msm_cpufreq_register(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + mutex_init(&(per_cpu(cpufreq_suspend, cpu).suspend_mutex)); + per_cpu(cpufreq_suspend, cpu).device_suspended = 0; + } + +#ifdef CONFIG_SMP + msm_cpufreq_wq = create_workqueue("msm-cpufreq"); +#endif + + register_pm_notifier(&msm_cpufreq_pm_notifier); + return cpufreq_register_driver(&msm_cpufreq_driver); +} + +late_initcall(msm_cpufreq_register); + diff --git a/arch/arm/mach-msm/cpuidle.c b/arch/arm/mach-msm/cpuidle.c new file mode 100644 index 00000000000..e4ec4d48c2b --- /dev/null +++ b/arch/arm/mach-msm/cpuidle.c @@ -0,0 +1,234 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include + +#include "pm.h" + +static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuidle_device, msm_cpuidle_devs); + +static struct cpuidle_driver msm_cpuidle_driver = { + .name = "msm_idle", + .owner = THIS_MODULE, +}; + +static struct msm_cpuidle_state msm_cstates[] = { + {0, 0, "C0", "WFI", + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT}, + + {0, 1, "C1", "RETENTION", + MSM_PM_SLEEP_MODE_RETENTION}, + + {0, 2, "C2", "STANDALONE_POWER_COLLAPSE", + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE}, + + {0, 3, "C3", "POWER_COLLAPSE", + MSM_PM_SLEEP_MODE_POWER_COLLAPSE}, + + {1, 0, "C0", "WFI", + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT}, + + {1, 1, "C1", "RETENTION", + MSM_PM_SLEEP_MODE_RETENTION}, + + {1, 2, "C2", "STANDALONE_POWER_COLLAPSE", + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE}, + + {2, 0, "C0", "WFI", + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT}, + + {2, 1, "C1", "RETENTION", + MSM_PM_SLEEP_MODE_RETENTION}, + + {2, 2, "C2", "STANDALONE_POWER_COLLAPSE", + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE}, + + {3, 0, "C0", "WFI", + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT}, + + {3, 1, "C1", "RETENTION", + MSM_PM_SLEEP_MODE_RETENTION}, + + {3, 2, "C2", "STANDALONE_POWER_COLLAPSE", + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE}, +}; + + +#ifdef CONFIG_MSM_SLEEP_STATS +static DEFINE_PER_CPU(struct atomic_notifier_head, msm_cpuidle_notifiers); + +int msm_cpuidle_register_notifier(unsigned int cpu, struct notifier_block *nb) +{ + struct atomic_notifier_head *head = + &per_cpu(msm_cpuidle_notifiers, cpu); + + return atomic_notifier_chain_register(head, nb); +} +EXPORT_SYMBOL(msm_cpuidle_register_notifier); + +int msm_cpuidle_unregister_notifier(unsigned int cpu, struct notifier_block *nb) +{ + struct atomic_notifier_head *head = + &per_cpu(msm_cpuidle_notifiers, cpu); + + return atomic_notifier_chain_unregister(head, nb); +} +EXPORT_SYMBOL(msm_cpuidle_unregister_notifier); +#endif + +static int msm_cpuidle_enter( + struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) +{ + int ret = 0; + int i = 0; + enum msm_pm_sleep_mode pm_mode; + struct cpuidle_state_usage *st_usage = NULL; +#ifdef CONFIG_MSM_SLEEP_STATS + struct atomic_notifier_head *head = + &__get_cpu_var(msm_cpuidle_notifiers); +#endif + + local_irq_disable(); + +#ifdef CONFIG_MSM_SLEEP_STATS + atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_ENTER, NULL); +#endif + +#ifdef CONFIG_CPU_PM + cpu_pm_enter(); +#endif + + pm_mode = msm_pm_idle_prepare(dev, drv, index); + msm_pm_idle_enter(pm_mode); + for (i = 0; i < dev->state_count; i++) { + st_usage = &dev->states_usage[i]; + if ((enum msm_pm_sleep_mode) cpuidle_get_statedata(st_usage) + == pm_mode) { + ret = i; + break; + } + } + +#ifdef CONFIG_CPU_PM + cpu_pm_exit(); +#endif + +#ifdef CONFIG_MSM_SLEEP_STATS + atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_EXIT, NULL); +#endif + + local_irq_enable(); + + return ret; +} + +static void __init msm_cpuidle_set_states(void) +{ + int i = 0; + int state_count = 0; + struct msm_cpuidle_state *cstate = NULL; + struct cpuidle_state *state = NULL; + + for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) { + cstate = &msm_cstates[i]; + /* We have an asymmetric CPU C-State in MSMs. + * The primary CPU can do PC while all secondary cpus + * can only do standalone PC as part of their idle LPM. + * However, the secondary cpus can do PC when hotplugged + * We do not care about the hotplug here. + * Register the C-States available for Core0. + */ + if (cstate->cpu) + continue; + + state = &msm_cpuidle_driver.states[state_count]; + snprintf(state->name, CPUIDLE_NAME_LEN, cstate->name); + snprintf(state->desc, CPUIDLE_DESC_LEN, cstate->desc); + state->flags = 0; + state->exit_latency = 0; + state->power_usage = 0; + state->target_residency = 0; + state->enter = msm_cpuidle_enter; + + state_count++; + BUG_ON(state_count >= CPUIDLE_STATE_MAX); + } + msm_cpuidle_driver.state_count = state_count; + msm_cpuidle_driver.safe_state_index = 0; +} + +static void __init msm_cpuidle_set_cpu_statedata(struct cpuidle_device *dev) +{ + int i = 0; + int state_count = 0; + struct cpuidle_state_usage *st_usage = NULL; + struct msm_cpuidle_state *cstate = NULL; + + for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) { + cstate = &msm_cstates[i]; + if (cstate->cpu != dev->cpu) + continue; + + st_usage = &dev->states_usage[state_count]; + cpuidle_set_statedata(st_usage, (void *)cstate->mode_nr); + state_count++; + BUG_ON(state_count > msm_cpuidle_driver.state_count); + } + + dev->state_count = state_count; /* Per cpu state count */ +} + +int __init msm_cpuidle_init(void) +{ + unsigned int cpu = 0; + int ret = 0; + + msm_cpuidle_set_states(); + ret = cpuidle_register_driver(&msm_cpuidle_driver); + if (ret) + pr_err("%s: failed to register cpuidle driver: %d\n", + __func__, ret); + + for_each_possible_cpu(cpu) { + struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu); + + dev->cpu = cpu; + msm_cpuidle_set_cpu_statedata(dev); + ret = cpuidle_register_device(dev); + if (ret) { + pr_err("%s: failed to register cpuidle device for " + "cpu %u: %d\n", __func__, cpu, ret); + return ret; + } + } + + return 0; +} + +static int __init msm_cpuidle_early_init(void) +{ +#ifdef CONFIG_MSM_SLEEP_STATS + unsigned int cpu; + + for_each_possible_cpu(cpu) + ATOMIC_INIT_NOTIFIER_HEAD(&per_cpu(msm_cpuidle_notifiers, cpu)); +#endif + return 0; +} + +early_initcall(msm_cpuidle_early_init); diff --git a/arch/arm/mach-msm/dal.c b/arch/arm/mach-msm/dal.c new file mode 100644 index 00000000000..94c02f0e5f5 --- /dev/null +++ b/arch/arm/mach-msm/dal.c @@ -0,0 +1,1326 @@ +/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Device access library (DAL) implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DALRPC_PROTOCOL_VERSION 0x11 +#define DALRPC_SUCCESS 0 +#define DALRPC_MAX_PORTNAME_LEN 64 +#define DALRPC_MAX_ATTACH_PARAM_LEN 64 +#define DALRPC_MAX_SERVICE_NAME_LEN 32 +#define DALRPC_MAX_PARAMS 128 +#define DALRPC_MAX_PARAMS_SIZE (DALRPC_MAX_PARAMS * 4) +#define DALRPC_MAX_MSG_SIZE (sizeof(struct dalrpc_msg_hdr) + \ + DALRPC_MAX_PARAMS_SIZE) +#define DALRPC_MSGID_DDI 0x0 +#define DALRPC_MSGID_DDI_REPLY 0x80 +#define DALRPC_MSGID_ATTACH_REPLY 0x81 +#define DALRPC_MSGID_DETACH_REPLY 0x82 +#define DALRPC_MSGID_ASYNCH 0xC0 +#define ROUND_BUFLEN(x) (((x + 3) & ~0x3)) + +struct dalrpc_msg_hdr { + uint32_t len:16; + uint32_t proto_ver:8; + uint32_t prio:7; + uint32_t async:1; + uint32_t ddi_idx:16; + uint32_t proto_id:8; + uint32_t msgid:8; + void *from; + void *to; +}; + +struct dalrpc_msg { + struct dalrpc_msg_hdr hdr; + uint32_t param[DALRPC_MAX_PARAMS]; +}; + +struct dalrpc_event_handle { + struct list_head list; + + int flag; + spinlock_t lock; +}; + +struct dalrpc_cb_handle { + struct list_head list; + + void (*fn)(void *, uint32_t, void *, uint32_t); + void *context; +}; + +struct daldevice_handle {; + struct list_head list; + + void *remote_handle; + struct completion read_completion; + struct dalrpc_port *port; + struct dalrpc_msg msg; + struct mutex client_lock; +}; + +struct dalrpc_port { + struct list_head list; + + char port[DALRPC_MAX_PORTNAME_LEN+1]; + int refcount; + + struct workqueue_struct *wq; + struct work_struct port_work; + struct mutex write_lock; + + smd_channel_t *ch; + + struct dalrpc_msg msg_in; + struct daldevice_handle *msg_owner; + unsigned msg_bytes_read; + + struct list_head event_list; + struct mutex event_list_lock; + + struct list_head cb_list; + struct mutex cb_list_lock; +}; + +static LIST_HEAD(port_list); +static LIST_HEAD(client_list); +static DEFINE_MUTEX(pc_lists_lock); + +static DECLARE_WAIT_QUEUE_HEAD(event_wq); + +static int client_exists(void *handle) +{ + struct daldevice_handle *h; + + if (!handle) + return 0; + + mutex_lock(&pc_lists_lock); + + list_for_each_entry(h, &client_list, list) + if (h == handle) { + mutex_unlock(&pc_lists_lock); + return 1; + } + + mutex_unlock(&pc_lists_lock); + + return 0; +} + +static int client_exists_locked(void *handle) +{ + struct daldevice_handle *h; + + /* this function must be called with pc_lists_lock acquired */ + + if (!handle) + return 0; + + list_for_each_entry(h, &client_list, list) + if (h == handle) + return 1; + + return 0; +} + +static int port_exists(struct dalrpc_port *p) +{ + struct dalrpc_port *p_iter; + + /* this function must be called with pc_lists_lock acquired */ + + if (!p) + return 0; + + list_for_each_entry(p_iter, &port_list, list) + if (p_iter == p) + return 1; + + return 0; +} + +static struct dalrpc_port *port_name_exists(char *port) +{ + struct dalrpc_port *p; + + /* this function must be called with pc_lists_lock acquired */ + + list_for_each_entry(p, &port_list, list) + if (!strcmp(p->port, port)) + return p; + + return NULL; +} + +static void port_close(struct dalrpc_port *p) +{ + mutex_lock(&pc_lists_lock); + + p->refcount--; + if (p->refcount == 0) + list_del(&p->list); + + mutex_unlock(&pc_lists_lock); + + if (p->refcount == 0) { + destroy_workqueue(p->wq); + smd_close(p->ch); + kfree(p); + } +} + +static int event_exists(struct dalrpc_port *p, + struct dalrpc_event_handle *ev) +{ + struct dalrpc_event_handle *ev_iter; + + /* this function must be called with event_list_lock acquired */ + + list_for_each_entry(ev_iter, &p->event_list, list) + if (ev_iter == ev) + return 1; + + return 0; +} + +static int cb_exists(struct dalrpc_port *p, + struct dalrpc_cb_handle *cb) +{ + struct dalrpc_cb_handle *cb_iter; + + /* this function must be called with the cb_list_lock acquired */ + + list_for_each_entry(cb_iter, &p->cb_list, list) + if (cb_iter == cb) + return 1; + + return 0; +} + +static int check_version(struct dalrpc_msg_hdr *msg_hdr) +{ + static int version_msg = 1; + + /* disabled because asynch events currently have no version */ + return 0; + + if (msg_hdr->proto_ver != DALRPC_PROTOCOL_VERSION) { + if (version_msg) { + printk(KERN_ERR "dalrpc: incompatible verison\n"); + version_msg = 0; + } + return -1; + } + return 0; +} + +static void process_asynch(struct dalrpc_port *p) +{ + struct dalrpc_event_handle *ev; + struct dalrpc_cb_handle *cb; + + ev = (struct dalrpc_event_handle *)p->msg_in.param[0]; + cb = (struct dalrpc_cb_handle *)p->msg_in.param[0]; + + mutex_lock(&p->event_list_lock); + if (event_exists(p, ev)) { + spin_lock(&ev->lock); + ev->flag = 1; + spin_unlock(&ev->lock); + smp_mb(); + wake_up_all(&event_wq); + mutex_unlock(&p->event_list_lock); + return; + } + mutex_unlock(&p->event_list_lock); + + mutex_lock(&p->cb_list_lock); + if (cb_exists(p, cb)) { + cb->fn(cb->context, p->msg_in.param[1], + &p->msg_in.param[3], p->msg_in.param[2]); + mutex_unlock(&p->cb_list_lock); + return; + } + mutex_unlock(&p->cb_list_lock); +} + +static void process_msg(struct dalrpc_port *p) +{ + switch (p->msg_in.hdr.msgid) { + + case DALRPC_MSGID_DDI_REPLY: + case DALRPC_MSGID_ATTACH_REPLY: + case DALRPC_MSGID_DETACH_REPLY: + complete(&p->msg_owner->read_completion); + break; + + case DALRPC_MSGID_ASYNCH: + process_asynch(p); + break; + + default: + printk(KERN_ERR "process_msg: bad msgid %#x\n", + p->msg_in.hdr.msgid); + } +} + +static void flush_msg(struct dalrpc_port *p) +{ + int bytes_read, len; + + len = p->msg_in.hdr.len - sizeof(struct dalrpc_msg_hdr); + while (len > 0) { + bytes_read = smd_read(p->ch, NULL, len); + if (bytes_read <= 0) + break; + len -= bytes_read; + } + p->msg_bytes_read = 0; +} + +static int check_header(struct dalrpc_port *p) +{ + if (check_version(&p->msg_in.hdr) || + p->msg_in.hdr.len > DALRPC_MAX_MSG_SIZE || + (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH && + !client_exists_locked(p->msg_in.hdr.to))) { + printk(KERN_ERR "dalrpc_read_msg: bad msg\n"); + flush_msg(p); + return 1; + } + p->msg_owner = (struct daldevice_handle *)p->msg_in.hdr.to; + + if (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH) + memcpy(&p->msg_owner->msg.hdr, &p->msg_in.hdr, + sizeof(p->msg_in.hdr)); + + return 0; +} + +static int dalrpc_read_msg(struct dalrpc_port *p) +{ + uint8_t *read_ptr; + int bytes_read; + + /* read msg header */ + while (p->msg_bytes_read < sizeof(p->msg_in.hdr)) { + read_ptr = (uint8_t *)&p->msg_in.hdr + p->msg_bytes_read; + + bytes_read = smd_read(p->ch, read_ptr, + sizeof(p->msg_in.hdr) - + p->msg_bytes_read); + if (bytes_read <= 0) + return 0; + p->msg_bytes_read += bytes_read; + + if (p->msg_bytes_read == sizeof(p->msg_in.hdr) && + check_header(p)) + return 1; + } + + /* read remainder of msg */ + if (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH) + read_ptr = (uint8_t *)&p->msg_owner->msg; + else + read_ptr = (uint8_t *)&p->msg_in; + read_ptr += p->msg_bytes_read; + + while (p->msg_bytes_read < p->msg_in.hdr.len) { + bytes_read = smd_read(p->ch, read_ptr, + p->msg_in.hdr.len - p->msg_bytes_read); + if (bytes_read <= 0) + return 0; + p->msg_bytes_read += bytes_read; + read_ptr += bytes_read; + } + + process_msg(p); + p->msg_bytes_read = 0; + p->msg_owner = NULL; + + return 1; +} + +static void dalrpc_work(struct work_struct *work) +{ + struct dalrpc_port *p = container_of(work, + struct dalrpc_port, + port_work); + + /* must lock port/client lists to ensure port doesn't disappear + under an asynch event */ + mutex_lock(&pc_lists_lock); + if (port_exists(p)) + while (dalrpc_read_msg(p)) + ; + mutex_unlock(&pc_lists_lock); +} + +static void dalrpc_smd_cb(void *priv, unsigned smd_flags) +{ + struct dalrpc_port *p = priv; + + if (smd_flags != SMD_EVENT_DATA) + return; + + queue_work(p->wq, &p->port_work); +} + +static struct dalrpc_port *dalrpc_port_open(char *port, int cpu) +{ + struct dalrpc_port *p; + char wq_name[32]; + + p = port_name_exists(port); + if (p) { + p->refcount++; + return p; + } + + p = kzalloc(sizeof(struct dalrpc_port), GFP_KERNEL); + if (!p) + return NULL; + + strlcpy(p->port, port, sizeof(p->port)); + p->refcount = 1; + + snprintf(wq_name, sizeof(wq_name), "dalrpc_rcv_%s", port); + p->wq = create_singlethread_workqueue(wq_name); + if (!p->wq) { + printk(KERN_ERR "dalrpc_init: unable to create workqueue\n"); + goto no_wq; + } + INIT_WORK(&p->port_work, dalrpc_work); + + mutex_init(&p->write_lock); + mutex_init(&p->event_list_lock); + mutex_init(&p->cb_list_lock); + + INIT_LIST_HEAD(&p->event_list); + INIT_LIST_HEAD(&p->cb_list); + + p->msg_owner = NULL; + p->msg_bytes_read = 0; + + if (smd_named_open_on_edge(port, cpu, &p->ch, p, + dalrpc_smd_cb)) { + printk(KERN_ERR "dalrpc_port_init() failed to open port\n"); + goto no_smd; + } + + list_add(&p->list, &port_list); + + return p; + +no_smd: + destroy_workqueue(p->wq); +no_wq: + kfree(p); + return NULL; +} + +static void dalrpc_sendwait(struct daldevice_handle *h) +{ + u8 *buf = (u8 *)&h->msg; + int len = h->msg.hdr.len; + int written; + + mutex_lock(&h->port->write_lock); + do { + written = smd_write(h->port->ch, buf + (h->msg.hdr.len - len), + len); + if (written < 0) + break; + len -= written; + } while (len); + mutex_unlock(&h->port->write_lock); + + if (!h->msg.hdr.async) + wait_for_completion(&h->read_completion); +} + +int daldevice_attach(uint32_t device_id, char *port, int cpu, + void **handle_ptr) +{ + struct daldevice_handle *h; + char dyn_port[DALRPC_MAX_PORTNAME_LEN + 1] = "DAL00"; + int ret; + int tries = 0; + + if (!port) + port = dyn_port; + + if (strlen(port) > DALRPC_MAX_PORTNAME_LEN) + return -EINVAL; + + h = kzalloc(sizeof(struct daldevice_handle), GFP_KERNEL); + if (!h) { + *handle_ptr = NULL; + return -ENOMEM; + } + + init_completion(&h->read_completion); + mutex_init(&h->client_lock); + + mutex_lock(&pc_lists_lock); + list_add(&h->list, &client_list); + mutex_unlock(&pc_lists_lock); + + /* 3 attempts, enough for one each on the user specified port, the + * dynamic discovery port, and the port recommended by the dynamic + * discovery port */ + while (tries < 3) { + tries++; + + mutex_lock(&pc_lists_lock); + h->port = dalrpc_port_open(port, cpu); + if (!h->port) { + list_del(&h->list); + mutex_unlock(&pc_lists_lock); + printk(KERN_ERR "daldevice_attach: could not " + "open port\n"); + kfree(h); + *handle_ptr = NULL; + return -EIO; + } + mutex_unlock(&pc_lists_lock); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4 + + DALRPC_MAX_ATTACH_PARAM_LEN + + DALRPC_MAX_SERVICE_NAME_LEN; + h->msg.hdr.proto_ver = DALRPC_PROTOCOL_VERSION; + h->msg.hdr.ddi_idx = 0; + h->msg.hdr.msgid = 0x1; + h->msg.hdr.prio = 0; + h->msg.hdr.async = 0; + h->msg.hdr.from = h; + h->msg.hdr.to = 0; + h->msg.param[0] = device_id; + + memset(&h->msg.param[1], 0, + DALRPC_MAX_ATTACH_PARAM_LEN + + DALRPC_MAX_SERVICE_NAME_LEN); + + dalrpc_sendwait(h); + ret = h->msg.param[0]; + + if (ret == DALRPC_SUCCESS) { + h->remote_handle = h->msg.hdr.from; + *handle_ptr = h; + break; + } else if (strnlen((char *)&h->msg.param[1], + DALRPC_MAX_PORTNAME_LEN)) { + /* another port was recommended in the response. */ + strlcpy(dyn_port, (char *)&h->msg.param[1], + sizeof(dyn_port)); + dyn_port[DALRPC_MAX_PORTNAME_LEN] = 0; + port = dyn_port; + } else if (port == dyn_port) { + /* the dynamic discovery port (or port that + * was recommended by it) did not recognize + * the device id, give up */ + daldevice_detach(h); + break; + } else + /* the user specified port did not work, try + * the dynamic discovery port */ + port = dyn_port; + + port_close(h->port); + } + + return ret; +} +EXPORT_SYMBOL(daldevice_attach); + +static void dalrpc_ddi_prologue(uint32_t ddi_idx, struct daldevice_handle *h, + uint32_t idx_async) +{ + h->msg.hdr.proto_ver = DALRPC_PROTOCOL_VERSION; + h->msg.hdr.prio = 0; + h->msg.hdr.async = idx_async; + h->msg.hdr.msgid = DALRPC_MSGID_DDI; + h->msg.hdr.from = h; + h->msg.hdr.to = h->remote_handle; + h->msg.hdr.ddi_idx = ddi_idx; +} + +int daldevice_detach(void *handle) +{ + struct daldevice_handle *h = handle; + + if (!client_exists(h)) + return -EINVAL; + + dalrpc_ddi_prologue(0, h, 0); + + if (!h->remote_handle) + goto norpc; + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.msgid = 0x2; + h->msg.param[0] = 0; + + dalrpc_sendwait(h); + +norpc: + mutex_lock(&pc_lists_lock); + list_del(&h->list); + mutex_unlock(&pc_lists_lock); + + port_close(h->port); + + kfree(h); + + return 0; +} +EXPORT_SYMBOL(daldevice_detach); + +uint32_t dalrpc_fcn_0(uint32_t ddi_idx, void *handle, uint32_t s1) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.proto_id = 0; + h->msg.param[0] = s1; + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_0); + +uint32_t dalrpc_fcn_1(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 1; + h->msg.param[0] = s1; + h->msg.param[1] = s2; + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_1); + +uint32_t dalrpc_fcn_2(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t *p_s2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.proto_id = 2; + h->msg.param[0] = s1; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) + *p_s2 = h->msg.param[1]; + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_2); + +uint32_t dalrpc_fcn_3(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t s3) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12; + h->msg.hdr.proto_id = 3; + h->msg.param[0] = s1; + h->msg.param[1] = s2; + h->msg.param[2] = s3; + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_3); + +uint32_t dalrpc_fcn_4(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t *p_s3) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 4; + h->msg.param[0] = s1; + h->msg.param[1] = s2; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) + *p_s3 = h->msg.param[1]; + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_4); + +uint32_t dalrpc_fcn_5(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen) +{ + struct daldevice_handle *h = handle; + uint32_t ret, idx_async; + + if ((ilen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + idx_async = (ddi_idx & 0x80000000) >> 31; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, idx_async); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 5; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + + dalrpc_sendwait(h); + + if (h->msg.hdr.async) + ret = DALRPC_SUCCESS; + else + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_5); + +uint32_t dalrpc_fcn_6(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 6; + h->msg.param[0] = s1; + h->msg.param[1] = ilen; + memcpy(&h->msg.param[2], ibuf, ilen); + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_6); + +uint32_t dalrpc_fcn_7(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + uint32_t *oalen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 7; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + *oalen = h->msg.param[1]; + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_7); + +uint32_t dalrpc_fcn_8(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 8; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_8); + +uint32_t dalrpc_fcn_9(uint32_t ddi_idx, void *handle, void *obuf, + uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.proto_id = 9; + h->msg.param[0] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_9); + +uint32_t dalrpc_fcn_10(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen, void *obuf, + uint32_t olen, uint32_t *oalen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 12) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 10; + h->msg.param[0] = s1; + h->msg.param[1] = ilen; + memcpy(&h->msg.param[2], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 2; + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + *oalen = h->msg.param[1]; + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_10); + +uint32_t dalrpc_fcn_11(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 11; + h->msg.param[0] = s1; + h->msg.param[1] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_11); + +uint32_t dalrpc_fcn_12(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen, uint32_t *oalen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 12; + h->msg.param[0] = s1; + h->msg.param[1] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + *oalen = h->msg.param[1]; + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_12); + +uint32_t dalrpc_fcn_13(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + ilen2 + 12) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + + ROUND_BUFLEN(ilen) + ROUND_BUFLEN(ilen2); + h->msg.hdr.proto_id = 13; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx++] = ilen2; + memcpy(&h->msg.param[param_idx], ibuf2, ilen2); + param_idx += (ROUND_BUFLEN(ilen2) / 4); + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_13); + +uint32_t dalrpc_fcn_14(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + void *obuf2, uint32_t olen2, uint32_t *oalen2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 12) > DALRPC_MAX_PARAMS_SIZE || + (olen + olen2 + 8) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 14; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx++] = olen; + h->msg.param[param_idx] = olen2; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + param_idx = (ROUND_BUFLEN(h->msg.param[1]) / 4) + 2; + if (h->msg.param[param_idx] > olen2) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + memcpy(obuf2, &h->msg.param[param_idx + 1], + h->msg.param[param_idx]); + *oalen2 = h->msg.param[param_idx]; + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_14); + +uint32_t dalrpc_fcn_15(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen, uint32_t *oalen, + void *obuf2, uint32_t olen2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + ilen2 + 16) > DALRPC_MAX_PARAMS_SIZE || + (olen + olen2 + 8) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h, 0); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 16 + + ROUND_BUFLEN(ilen) + ROUND_BUFLEN(ilen2); + h->msg.hdr.proto_id = 15; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx++] = ilen2; + memcpy(&h->msg.param[param_idx], ibuf2, ilen2); + param_idx += (ROUND_BUFLEN(ilen2) / 4); + h->msg.param[param_idx++] = olen; + h->msg.param[param_idx] = olen2; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + param_idx = (ROUND_BUFLEN(h->msg.param[1]) / 4) + 2; + if (h->msg.param[param_idx] > olen2) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + memcpy(obuf2, &h->msg.param[param_idx + 1], + h->msg.param[param_idx]); + *oalen = h->msg.param[1]; + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_15); + +void *dalrpc_alloc_event(void *handle) +{ + struct daldevice_handle *h; + struct dalrpc_event_handle *ev; + + h = (struct daldevice_handle *)handle; + + if (!client_exists(h)) + return NULL; + + ev = kmalloc(sizeof(struct dalrpc_event_handle), GFP_KERNEL); + if (!ev) + return NULL; + + ev->flag = 0; + spin_lock_init(&ev->lock); + + mutex_lock(&h->port->event_list_lock); + list_add(&ev->list, &h->port->event_list); + mutex_unlock(&h->port->event_list_lock); + + return ev; +} +EXPORT_SYMBOL(dalrpc_alloc_event); + +void *dalrpc_alloc_cb(void *handle, + void (*fn)(void *, uint32_t, void *, uint32_t), + void *context) +{ + struct daldevice_handle *h; + struct dalrpc_cb_handle *cb; + + h = (struct daldevice_handle *)handle; + + if (!client_exists(h)) + return NULL; + + cb = kmalloc(sizeof(struct dalrpc_cb_handle), GFP_KERNEL); + if (!cb) + return NULL; + + cb->fn = fn; + cb->context = context; + + mutex_lock(&h->port->cb_list_lock); + list_add(&cb->list, &h->port->cb_list); + mutex_unlock(&h->port->cb_list_lock); + + return cb; +} +EXPORT_SYMBOL(dalrpc_alloc_cb); + +void dalrpc_dealloc_event(void *handle, + void *ev_h) +{ + struct daldevice_handle *h; + struct dalrpc_event_handle *ev; + + h = (struct daldevice_handle *)handle; + ev = (struct dalrpc_event_handle *)ev_h; + + mutex_lock(&h->port->event_list_lock); + list_del(&ev->list); + mutex_unlock(&h->port->event_list_lock); + kfree(ev); +} +EXPORT_SYMBOL(dalrpc_dealloc_event); + +void dalrpc_dealloc_cb(void *handle, + void *cb_h) +{ + struct daldevice_handle *h; + struct dalrpc_cb_handle *cb; + + h = (struct daldevice_handle *)handle; + cb = (struct dalrpc_cb_handle *)cb_h; + + mutex_lock(&h->port->cb_list_lock); + list_del(&cb->list); + mutex_unlock(&h->port->cb_list_lock); + kfree(cb); +} +EXPORT_SYMBOL(dalrpc_dealloc_cb); + +static int event_occurred(int num_events, struct dalrpc_event_handle **events, + int *occurred) +{ + int i; + + for (i = 0; i < num_events; i++) { + spin_lock(&events[i]->lock); + if (events[i]->flag) { + events[i]->flag = 0; + spin_unlock(&events[i]->lock); + *occurred = i; + return 1; + } + spin_unlock(&events[i]->lock); + } + + return 0; +} + +int dalrpc_event_wait_multiple(int num, void **ev_h, int timeout) +{ + struct dalrpc_event_handle **events; + int ret, occurred; + + events = (struct dalrpc_event_handle **)ev_h; + + if (timeout == DALRPC_TIMEOUT_INFINITE) { + wait_event(event_wq, + event_occurred(num, events, &occurred)); + return occurred; + } + + ret = wait_event_timeout(event_wq, + event_occurred(num, events, &occurred), + timeout); + if (ret > 0) + return occurred; + else + return -ETIMEDOUT; +} +EXPORT_SYMBOL(dalrpc_event_wait_multiple); diff --git a/arch/arm/mach-msm/dal_axi.c b/arch/arm/mach-msm/dal_axi.c new file mode 100644 index 00000000000..739b7dcd1b2 --- /dev/null +++ b/arch/arm/mach-msm/dal_axi.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/* The AXI device ID */ +#define DALDEVICEID_AXI 0x02000053 +#define DALRPC_PORT_NAME "DAL00" + +enum { + DALRPC_AXI_CONFIGURE_BRIDGE = DALDEVICE_FIRST_DEVICE_API_IDX + 11 +}; + +enum { + DAL_AXI_BRIDGE_CFG_CGR_SS_2DGRP_SYNC_MODE = 14, + DAL_AXI_BRIDGE_CFG_CGR_SS_2DGRP_ASYNC_MODE, + DAL_AXI_BRIDGE_CFG_CGR_SS_2DGRP_ISOSYNC_MODE, + DAL_AXI_BRIDGE_CFG_CGR_SS_2DGRP_DEBUG_EN, + DAL_AXI_BRIDGE_CFG_CGR_SS_2DGRP_DEBUG_DIS, + DAL_AXI_BRIDGE_CFG_CGR_SS_3DGRP_SYNC_MODE, + DAL_AXI_BRIDGE_CFG_CGR_SS_3DGRP_ASYNC_MODE, + DAL_AXI_BRIDGE_CFG_CGR_SS_3DGRP_ISOSYNC_MODE, + DAL_AXI_BRIDGE_CFG_CGR_SS_3DGRP_DEBUG_EN, + DAL_AXI_BRIDGE_CFG_CGR_SS_3DGRP_DEBUG_DIS, + /* 7x27(A) Graphics Subsystem Bridge Configuration */ + DAL_AXI_BRIDGE_CFG_GRPSS_XBAR_SYNC_MODE = 58, + DAL_AXI_BRIDGE_CFG_GRPSS_XBAR_ASYNC_MODE = 59, + DAL_AXI_BRIDGE_CFG_GRPSS_XBAR_ISOSYNC_MODE = 60 + +}; + +static int axi_configure_bridge_grfx_sync_mode(int bridge_mode) +{ + int rc; + void *dev_handle; + + /* get device handle */ + rc = daldevice_attach( + DALDEVICEID_AXI, DALRPC_PORT_NAME, + DALRPC_DEST_MODEM, &dev_handle + ); + if (rc) { + printk(KERN_ERR "%s: failed to attach AXI bus device (%d)\n", + __func__, rc); + goto fail_dal_attach_detach; + } + + /* call ConfigureBridge */ + rc = dalrpc_fcn_0( + DALRPC_AXI_CONFIGURE_BRIDGE, dev_handle, + bridge_mode + ); + if (rc) { + printk(KERN_ERR "%s: AXI bus device (%d) failed to be configured\n", + __func__, rc); + goto fail_dal_fcn_0; + } + + /* close device handle */ + rc = daldevice_detach(dev_handle); + if (rc) { + printk(KERN_ERR "%s: failed to detach AXI bus device (%d)\n", + __func__, rc); + goto fail_dal_attach_detach; + } + + return 0; + +fail_dal_fcn_0: + (void)daldevice_detach(dev_handle); +fail_dal_attach_detach: + + return rc; +} + + + +int set_grp2d_async(void) +{ + return axi_configure_bridge_grfx_sync_mode( + DAL_AXI_BRIDGE_CFG_CGR_SS_2DGRP_ASYNC_MODE); +} + +int set_grp3d_async(void) +{ + return axi_configure_bridge_grfx_sync_mode( + DAL_AXI_BRIDGE_CFG_CGR_SS_3DGRP_ASYNC_MODE); +} + +int set_grp_xbar_async(void) +{ return axi_configure_bridge_grfx_sync_mode( + DAL_AXI_BRIDGE_CFG_GRPSS_XBAR_ASYNC_MODE); +} diff --git a/arch/arm/mach-msm/dal_remotetest.c b/arch/arm/mach-msm/dal_remotetest.c new file mode 100644 index 00000000000..d7a3f34c945 --- /dev/null +++ b/arch/arm/mach-msm/dal_remotetest.c @@ -0,0 +1,410 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * DAL remote test device test suite. + */ + +#include +#include +#include +#include +#include + +#include "dal_remotetest.h" + +#define BYTEBUF_LEN 64 + +#define rpc_error(num) \ + do { \ + errmask |= (1 << num); \ + printk(KERN_INFO "%s: remote_unittest_%d failed (%d)\n", \ + __func__, num, ret); \ + } while (0) + +#define verify_error(num, field) \ + do { \ + errmask |= (1 << num); \ + printk(KERN_INFO "%s: remote_unittest_%d failed (%s)\n", \ + __func__, num, field); \ + } while (0) + + +static struct dentry *debugfs_dir_entry; +static struct dentry *debugfs_modem_entry; +static struct dentry *debugfs_dsp_entry; + +static uint8_t in_bytebuf[BYTEBUF_LEN]; +static uint8_t out_bytebuf[BYTEBUF_LEN]; +static uint8_t out_bytebuf2[BYTEBUF_LEN]; +static struct remote_test_data in_data; +static struct remote_test_data out_data; +static int block_until_cb = 1; + +static void init_data(struct remote_test_data *data) +{ + int i; + data->regular_event = REMOTE_UNITTEST_INPUT_HANDLE; + data->payload_event = REMOTE_UNITTEST_INPUT_HANDLE; + for (i = 0; i < 32; i++) + data->test[i] = i; +} + +static int verify_data(struct remote_test_data *data) +{ + int i; + if (data->regular_event != REMOTE_UNITTEST_INPUT_HANDLE || + data->payload_event != REMOTE_UNITTEST_INPUT_HANDLE) + return -1; + for (i = 0; i < 32; i++) + if (data->test[i] != i) + return -1; + + return 0; +} + +static int verify_uint32_buffer(uint32_t *buf) +{ + int i; + for (i = 0; i < 32; i++) + if (buf[i] != i) + return -1; + + return 0; +} + +static void init_bytebuf(uint8_t *bytebuf) +{ + int i; + for (i = 0; i < BYTEBUF_LEN; i++) + bytebuf[i] = i & 0xff; +} + +static int verify_bytebuf(uint8_t *bytebuf) +{ + int i; + for (i = 0; i < BYTEBUF_LEN; i++) + if (bytebuf[i] != (i & 0xff)) + return -1; + + return 0; +} + +static void test_cb(void *context, uint32_t param, void *data, uint32_t len) +{ + block_until_cb = 0; +} + +static int remotetest_exec(int dest, u64 *val) +{ + void *dev_handle; + void *event_handles[3]; + void *cb_handle; + int ret; + u64 errmask = 0; + uint32_t ouint; + uint32_t oalen; + + /* test daldevice_attach */ + ret = daldevice_attach(REMOTE_UNITTEST_DEVICEID, NULL, + dest, &dev_handle); + if (ret) { + printk(KERN_INFO "%s: failed to attach (%d)\n", __func__, ret); + *val = 0xffffffff; + return 0; + } + + /* test remote_unittest_0 */ + ret = remote_unittest_0(dev_handle, REMOTE_UNITTEST_INARG_1); + if (ret) + rpc_error(0); + + /* test remote_unittest_1 */ + ret = remote_unittest_1(dev_handle, REMOTE_UNITTEST_INARG_1, + REMOTE_UNITTEST_INARG_2); + if (ret) + rpc_error(1); + + /* test remote_unittest_2 */ + ouint = 0; + ret = remote_unittest_2(dev_handle, REMOTE_UNITTEST_INARG_1, &ouint); + if (ret) + rpc_error(2); + else if (ouint != REMOTE_UNITTEST_OUTARG_1) + verify_error(2, "ouint"); + + /* test remote_unittest_3 */ + ret = remote_unittest_3(dev_handle, REMOTE_UNITTEST_INARG_1, + REMOTE_UNITTEST_INARG_2, + REMOTE_UNITTEST_INARG_3); + if (ret) + rpc_error(3); + + /* test remote_unittest_4 */ + ouint = 0; + ret = remote_unittest_4(dev_handle, REMOTE_UNITTEST_INARG_1, + REMOTE_UNITTEST_INARG_2, &ouint); + if (ret) + rpc_error(4); + else if (ouint != REMOTE_UNITTEST_OUTARG_1) + verify_error(4, "ouint"); + + /* test remote_unittest_5 */ + init_data(&in_data); + ret = remote_unittest_5(dev_handle, &in_data, sizeof(in_data)); + if (ret) + rpc_error(5); + + /* test remote_unittest_6 */ + init_data(&in_data); + ret = remote_unittest_6(dev_handle, REMOTE_UNITTEST_INARG_1, + &in_data.test, sizeof(in_data.test)); + if (ret) + rpc_error(6); + + /* test remote_unittest_7 */ + init_data(&in_data); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_7(dev_handle, &in_data, sizeof(in_data), + &out_data.test, sizeof(out_data.test), + &oalen); + if (ret) + rpc_error(7); + else if (oalen != sizeof(out_data.test)) + verify_error(7, "oalen"); + else if (verify_uint32_buffer(out_data.test)) + verify_error(7, "obuf"); + + /* test remote_unittest_8 */ + init_bytebuf(in_bytebuf); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_8(dev_handle, in_bytebuf, sizeof(in_bytebuf), + &out_data, sizeof(out_data)); + if (ret) + rpc_error(8); + else if (verify_data(&out_data)) + verify_error(8, "obuf"); + + /* test remote_unittest_9 */ + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_9(dev_handle, out_bytebuf, sizeof(out_bytebuf)); + if (ret) + rpc_error(9); + else if (verify_bytebuf(out_bytebuf)) + verify_error(9, "obuf"); + + /* test remote_unittest_10 */ + init_bytebuf(in_bytebuf); + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_10(dev_handle, REMOTE_UNITTEST_INARG_1, + in_bytebuf, sizeof(in_bytebuf), + out_bytebuf, sizeof(out_bytebuf), &oalen); + if (ret) + rpc_error(10); + else if (oalen != sizeof(out_bytebuf)) + verify_error(10, "oalen"); + else if (verify_bytebuf(out_bytebuf)) + verify_error(10, "obuf"); + + /* test remote_unittest_11 */ + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_11(dev_handle, REMOTE_UNITTEST_INARG_1, + out_bytebuf, sizeof(out_bytebuf)); + if (ret) + rpc_error(11); + else if (verify_bytebuf(out_bytebuf)) + verify_error(11, "obuf"); + + /* test remote_unittest_12 */ + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_12(dev_handle, REMOTE_UNITTEST_INARG_1, + out_bytebuf, sizeof(out_bytebuf), &oalen); + if (ret) + rpc_error(12); + else if (oalen != sizeof(out_bytebuf)) + verify_error(12, "oalen"); + else if (verify_bytebuf(out_bytebuf)) + verify_error(12, "obuf"); + + /* test remote_unittest_13 */ + init_data(&in_data); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_13(dev_handle, in_data.test, sizeof(in_data.test), + &in_data, sizeof(in_data), + &out_data, sizeof(out_data)); + if (ret) + rpc_error(13); + else if (verify_data(&out_data)) + verify_error(13, "obuf"); + + /* test remote_unittest_14 */ + init_data(&in_data); + memset(out_bytebuf, 0, sizeof(out_bytebuf)); + memset(out_bytebuf2, 0, sizeof(out_bytebuf2)); + ret = remote_unittest_14(dev_handle, + in_data.test, sizeof(in_data.test), + out_bytebuf, sizeof(out_bytebuf), + out_bytebuf2, sizeof(out_bytebuf2), &oalen); + if (ret) + rpc_error(14); + else if (verify_bytebuf(out_bytebuf)) + verify_error(14, "obuf"); + else if (oalen != sizeof(out_bytebuf2)) + verify_error(14, "oalen"); + else if (verify_bytebuf(out_bytebuf2)) + verify_error(14, "obuf2"); + + /* test remote_unittest_15 */ + init_data(&in_data); + memset(out_bytebuf, 0, sizeof(out_bytebuf)); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_15(dev_handle, + in_data.test, sizeof(in_data.test), + &in_data, sizeof(in_data), + &out_data, sizeof(out_data), &oalen, + out_bytebuf, sizeof(out_bytebuf)); + if (ret) + rpc_error(15); + else if (oalen != sizeof(out_data)) + verify_error(15, "oalen"); + else if (verify_bytebuf(out_bytebuf)) + verify_error(15, "obuf"); + else if (verify_data(&out_data)) + verify_error(15, "obuf2"); + + /* test setting up asynch events */ + event_handles[0] = dalrpc_alloc_event(dev_handle); + event_handles[1] = dalrpc_alloc_event(dev_handle); + event_handles[2] = dalrpc_alloc_event(dev_handle); + cb_handle = dalrpc_alloc_cb(dev_handle, test_cb, &out_data); + in_data.regular_event = (uint32_t)event_handles[2]; + in_data.payload_event = (uint32_t)cb_handle; + ret = remote_unittest_eventcfg(dev_handle, &in_data, sizeof(in_data)); + if (ret) { + errmask |= (1 << 16); + printk(KERN_INFO "%s: failed to configure asynch (%d)\n", + __func__, ret); + } + + /* test event */ + ret = remote_unittest_eventtrig(dev_handle, + REMOTE_UNITTEST_REGULAR_EVENT); + if (ret) { + errmask |= (1 << 17); + printk(KERN_INFO "%s: failed to trigger event (%d)\n", + __func__, ret); + } + ret = dalrpc_event_wait(event_handles[2], 1000); + if (ret) { + errmask |= (1 << 18); + printk(KERN_INFO "%s: failed to receive event (%d)\n", + __func__, ret); + } + + /* test event again */ + ret = remote_unittest_eventtrig(dev_handle, + REMOTE_UNITTEST_REGULAR_EVENT); + if (ret) { + errmask |= (1 << 19); + printk(KERN_INFO "%s: failed to trigger event (%d)\n", + __func__, ret); + } + ret = dalrpc_event_wait_multiple(3, event_handles, 1000); + if (ret != 2) { + errmask |= (1 << 20); + printk(KERN_INFO "%s: failed to receive event (%d)\n", + __func__, ret); + } + + /* test callback */ + ret = remote_unittest_eventtrig(dev_handle, + REMOTE_UNITTEST_CALLBACK_EVENT); + if (ret) { + errmask |= (1 << 21); + printk(KERN_INFO "%s: failed to trigger callback (%d)\n", + __func__, ret); + } else + while (block_until_cb) + ; + + dalrpc_dealloc_cb(dev_handle, cb_handle); + dalrpc_dealloc_event(dev_handle, event_handles[0]); + dalrpc_dealloc_event(dev_handle, event_handles[1]); + dalrpc_dealloc_event(dev_handle, event_handles[2]); + + /* test daldevice_detach */ + ret = daldevice_detach(dev_handle); + if (ret) { + errmask |= (1 << 22); + printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret); + } + + printk(KERN_INFO "%s: remote_unittest complete\n", __func__); + + *val = errmask; + return 0; +} + +static int remotetest_modem_exec(void *data, u64 *val) +{ + return remotetest_exec(DALRPC_DEST_MODEM, val); +} + +static int remotetest_dsp_exec(void *data, u64 *val) +{ + return remotetest_exec(DALRPC_DEST_QDSP, val); +} + +DEFINE_SIMPLE_ATTRIBUTE(dal_modemtest_fops, remotetest_modem_exec, + NULL, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(dal_dsptest_fops, remotetest_dsp_exec, + NULL, "%llu\n"); + +static int __init remotetest_init(void) +{ + debugfs_dir_entry = debugfs_create_dir("dal", 0); + if (IS_ERR(debugfs_dir_entry)) + return PTR_ERR(debugfs_dir_entry); + + debugfs_modem_entry = debugfs_create_file("modem_test", 0444, + debugfs_dir_entry, + NULL, &dal_modemtest_fops); + if (IS_ERR(debugfs_modem_entry)) { + debugfs_remove(debugfs_dir_entry); + return PTR_ERR(debugfs_modem_entry); + } + + debugfs_dsp_entry = debugfs_create_file("dsp_test", 0444, + debugfs_dir_entry, + NULL, &dal_dsptest_fops); + if (IS_ERR(debugfs_dsp_entry)) { + debugfs_remove(debugfs_modem_entry); + debugfs_remove(debugfs_dir_entry); + return PTR_ERR(debugfs_dsp_entry); + } + + return 0; +} + +static void __exit remotetest_exit(void) +{ + debugfs_remove(debugfs_modem_entry); + debugfs_remove(debugfs_dsp_entry); + debugfs_remove(debugfs_dir_entry); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Test for DAL RPC"); +MODULE_VERSION("1.0"); + +module_init(remotetest_init); +module_exit(remotetest_exit); diff --git a/arch/arm/mach-msm/dal_remotetest.h b/arch/arm/mach-msm/dal_remotetest.h new file mode 100644 index 00000000000..cb998c904a8 --- /dev/null +++ b/arch/arm/mach-msm/dal_remotetest.h @@ -0,0 +1,172 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * DAL remote test device API. + */ + +#include + +#include + +#define REMOTE_UNITTEST_DEVICEID 0xDA1DA1DA + +enum { + DALRPC_TEST_API_0 = DALDEVICE_FIRST_DEVICE_API_IDX, + DALRPC_TEST_API_1, + DALRPC_TEST_API_2, + DALRPC_TEST_API_3, + DALRPC_TEST_API_4, + DALRPC_TEST_API_5, + DALRPC_TEST_API_6, + DALRPC_TEST_API_7, + DALRPC_TEST_API_8, + DALRPC_TEST_API_9, + DALRPC_TEST_API_10, + DALRPC_TEST_API_11, + DALRPC_TEST_API_12, + DALRPC_TEST_API_13, + DALRPC_TEST_API_14, + DALRPC_TEST_API_15, + DALRPC_TEST_API_16, + DALRPC_TEST_API_17 +}; + +#define REMOTE_UNITTEST_INARG_1 0x01010101 +#define REMOTE_UNITTEST_INARG_2 0x20202020 +#define REMOTE_UNITTEST_INARG_3 0x12121212 +#define REMOTE_UNITTEST_INPUT_HANDLE 0xDA1FDA1F +#define REMOTE_UNITTEST_OUTARG_1 0xBEEFDEAD + +#define REMOTE_UNITTEST_REGULAR_EVENT 0 +#define REMOTE_UNITTEST_CALLBACK_EVENT 1 + +#define REMOTE_UNITTEST_BAD_PARAM 0x10 + +struct remote_test_data { + uint32_t regular_event; + uint32_t test[32]; + uint32_t payload_event; +}; + +static int remote_unittest_0(void *handle, uint32_t s1) +{ + return dalrpc_fcn_0(DALRPC_TEST_API_0, handle, s1); +} + +static int remote_unittest_1(void *handle, uint32_t s1, uint32_t s2) +{ + return dalrpc_fcn_1(DALRPC_TEST_API_1, handle, s1, s2); +} + +static int remote_unittest_2(void *handle, uint32_t s1, uint32_t *p_s2) +{ + return dalrpc_fcn_2(DALRPC_TEST_API_2, handle, s1, p_s2); +} + +static int remote_unittest_3(void *handle, uint32_t s1, uint32_t s2, + uint32_t s3) +{ + return dalrpc_fcn_3(DALRPC_TEST_API_3, handle, s1, s2, s3); +} + +static int remote_unittest_4(void *handle, uint32_t s1, uint32_t s2, + uint32_t *p_s3) +{ + return dalrpc_fcn_4(DALRPC_TEST_API_4, handle, s1, s2, p_s3); +} + +static int remote_unittest_5(void *handle, const void *ibuf, uint32_t ilen) +{ + return dalrpc_fcn_5(DALRPC_TEST_API_5, handle, ibuf, ilen); +} + +static int remote_unittest_6(void *handle, uint32_t s1, const void *ibuf, + uint32_t ilen) +{ + return dalrpc_fcn_6(DALRPC_TEST_API_6, handle, s1, ibuf, ilen); +} + +static int remote_unittest_7(void *handle, const void *ibuf, uint32_t ilen, + void *obuf, uint32_t olen, uint32_t *oalen) +{ + return dalrpc_fcn_7(DALRPC_TEST_API_7, handle, ibuf, ilen, obuf, + olen, oalen); +} + +static int remote_unittest_8(void *handle, const void *ibuf, uint32_t ilen, + void *obuf, uint32_t olen) +{ + return dalrpc_fcn_8(DALRPC_TEST_API_8, handle, ibuf, ilen, obuf, olen); +} + +static int remote_unittest_9(void *handle, void *obuf, uint32_t olen) +{ + return dalrpc_fcn_9(DALRPC_TEST_API_9, handle, obuf, olen); +} + +static int remote_unittest_10(void *handle, uint32_t s1, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + uint32_t *oalen) +{ + return dalrpc_fcn_10(DALRPC_TEST_API_10, handle, s1, ibuf, ilen, obuf, + olen, oalen); +} + +static int remote_unittest_11(void *handle, uint32_t s1, void *obuf, + uint32_t olen) +{ + return dalrpc_fcn_11(DALRPC_TEST_API_11, handle, s1, obuf, olen); +} + +static int remote_unittest_12(void *handle, uint32_t s1, void *obuf, + uint32_t olen, uint32_t *oalen) +{ + return dalrpc_fcn_12(DALRPC_TEST_API_12, handle, s1, obuf, olen, + oalen); +} + +static int remote_unittest_13(void *handle, const void *ibuf, uint32_t ilen, + const void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen) +{ + return dalrpc_fcn_13(DALRPC_TEST_API_13, handle, ibuf, ilen, ibuf2, + ilen2, obuf, olen); +} + +static int remote_unittest_14(void *handle, const void *ibuf, uint32_t ilen, + void *obuf, uint32_t olen, void *obuf2, + uint32_t olen2, uint32_t *oalen2) +{ + return dalrpc_fcn_14(DALRPC_TEST_API_14, handle, ibuf, ilen, obuf, + olen, obuf2, olen2, oalen2); +} + +static int remote_unittest_15(void *handle, const void *ibuf, uint32_t ilen, + const void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen, uint32_t *oalen, void *obuf2, + uint32_t olen2) +{ + return dalrpc_fcn_15(DALRPC_TEST_API_15, handle, ibuf, ilen, ibuf2, + ilen2, obuf, olen, oalen, obuf2, olen2); +} + +static int remote_unittest_eventcfg(void *handle, const void *ibuf, + uint32_t ilen) +{ + return dalrpc_fcn_5(DALRPC_TEST_API_16, handle, ibuf, ilen); +} + +static int remote_unittest_eventtrig(void *handle, uint32_t event_idx) +{ + return dalrpc_fcn_0(DALRPC_TEST_API_17, handle, event_idx); +} diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c new file mode 100644 index 00000000000..b9617f317ca --- /dev/null +++ b/arch/arm/mach-msm/devices-8064.c @@ -0,0 +1,2673 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "devices.h" +#include "footswitch.h" +#include "msm_watchdog.h" +#include "rpm_stats.h" +#include "rpm_log.h" +#include +#include +#include + +/* Address of GSBI blocks */ +#define MSM_GSBI1_PHYS 0x12440000 +#define MSM_GSBI3_PHYS 0x16200000 +#define MSM_GSBI4_PHYS 0x16300000 +#define MSM_GSBI5_PHYS 0x1A200000 +#define MSM_GSBI6_PHYS 0x16500000 +#define MSM_GSBI7_PHYS 0x16600000 + +/* GSBI UART devices */ +#define MSM_UART1DM_PHYS (MSM_GSBI1_PHYS + 0x10000) +#define MSM_UART3DM_PHYS (MSM_GSBI3_PHYS + 0x40000) +#define MSM_UART7DM_PHYS (MSM_GSBI7_PHYS + 0x40000) + +/* GSBI QUP devices */ +#define MSM_GSBI1_QUP_PHYS (MSM_GSBI1_PHYS + 0x20000) +#define MSM_GSBI3_QUP_PHYS (MSM_GSBI3_PHYS + 0x80000) +#define MSM_GSBI4_QUP_PHYS (MSM_GSBI4_PHYS + 0x80000) +#define MSM_GSBI5_QUP_PHYS (MSM_GSBI5_PHYS + 0x80000) +#define MSM_GSBI6_QUP_PHYS (MSM_GSBI6_PHYS + 0x80000) +#define MSM_GSBI7_QUP_PHYS (MSM_GSBI7_PHYS + 0x80000) +#define MSM_QUP_SIZE SZ_4K + +/* Address of SSBI CMD */ +#define MSM_PMIC1_SSBI_CMD_PHYS 0x00500000 +#define MSM_PMIC2_SSBI_CMD_PHYS 0x00C00000 +#define MSM_PMIC_SSBI_SIZE SZ_4K + +/* Address of HS USBOTG1 */ +#define MSM_HSUSB1_PHYS 0x12500000 +#define MSM_HSUSB1_SIZE SZ_4K + +/* Address of HS USB3 */ +#define MSM_HSUSB3_PHYS 0x12520000 +#define MSM_HSUSB3_SIZE SZ_4K + +/* Address of HS USB4 */ +#define MSM_HSUSB4_PHYS 0x12530000 +#define MSM_HSUSB4_SIZE SZ_4K + +/* Address of PCIE20 PARF */ +#define PCIE20_PARF_PHYS 0x1b600000 +#define PCIE20_PARF_SIZE SZ_128 + +/* Address of PCIE20 ELBI */ +#define PCIE20_ELBI_PHYS 0x1b502000 +#define PCIE20_ELBI_SIZE SZ_256 + +/* Address of PCIE20 */ +#define PCIE20_PHYS 0x1b500000 +#define PCIE20_SIZE SZ_4K + +/* AXI address for PCIE device BAR resources */ +#define PCIE_AXI_BAR_PHYS 0x08000000 +#define PCIE_AXI_BAR_SIZE SZ_8M + +/* AXI address for PCIE device config space */ +#define PCIE_AXI_CONF_PHYS 0x08c00000 +#define PCIE_AXI_CONF_SIZE SZ_4K + +static struct msm_watchdog_pdata msm_watchdog_pdata = { + .pet_time = 10000, + .bark_time = 11000, + .has_secure = true, + .needs_expired_enable = true, +}; + +struct platform_device msm8064_device_watchdog = { + .name = "msm_watchdog", + .id = -1, + .dev = { + .platform_data = &msm_watchdog_pdata, + }, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = ADM_0_SCSS_1_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x18320000, + .end = 0x18320000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 1, + .sd_size = 0x800, +}; + +struct platform_device apq8064_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +static struct resource resources_uart_gsbi1[] = { + { + .start = APQ8064_GSBI1_UARTDM_IRQ, + .end = APQ8064_GSBI1_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device apq8064_device_uart_gsbi1 = { + .name = "msm_serial_hsl", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart_gsbi1), + .resource = resources_uart_gsbi1, +}; + +static struct resource resources_uart_gsbi3[] = { + { + .start = GSBI3_UARTDM_IRQ, + .end = GSBI3_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART3DM_PHYS, + .end = MSM_UART3DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI3_PHYS, + .end = MSM_GSBI3_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device apq8064_device_uart_gsbi3 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart_gsbi3), + .resource = resources_uart_gsbi3, +}; + +static struct resource resources_qup_i2c_gsbi3[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI3_PHYS, + .end = MSM_GSBI3_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI3_QUP_PHYS, + .end = MSM_GSBI3_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI3_QUP_IRQ, + .end = GSBI3_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = 9, + .end = 9, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = 8, + .end = 8, + .flags = IORESOURCE_IO, + }, +}; + +static struct resource resources_qup_i2c_gsbi1[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI1_QUP_PHYS, + .end = MSM_GSBI1_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = APQ8064_GSBI1_QUP_IRQ, + .end = APQ8064_GSBI1_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = 21, + .end = 21, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = 20, + .end = 20, + .flags = IORESOURCE_IO, + }, +}; + +struct platform_device apq8064_device_qup_i2c_gsbi1 = { + .name = "qup_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi1), + .resource = resources_qup_i2c_gsbi1, +}; + +struct platform_device apq8064_device_qup_i2c_gsbi3 = { + .name = "qup_i2c", + .id = 3, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi3), + .resource = resources_qup_i2c_gsbi3, +}; + +static struct resource resources_qup_i2c_gsbi4[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI4_PHYS, + .end = MSM_GSBI4_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI4_QUP_PHYS, + .end = MSM_GSBI4_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI4_QUP_IRQ, + .end = GSBI4_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = 11, + .end = 11, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = 10, + .end = 10, + .flags = IORESOURCE_IO, + }, +}; + +struct platform_device apq8064_device_qup_i2c_gsbi4 = { + .name = "qup_i2c", + .id = 4, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi4), + .resource = resources_qup_i2c_gsbi4, +}; + +static struct resource resources_qup_spi_gsbi5[] = { + { + .name = "spi_base", + .start = MSM_GSBI5_QUP_PHYS, + .end = MSM_GSBI5_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_base", + .start = MSM_GSBI5_PHYS, + .end = MSM_GSBI5_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spi_irq_in", + .start = GSBI5_QUP_IRQ, + .end = GSBI5_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device apq8064_device_qup_spi_gsbi5 = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(resources_qup_spi_gsbi5), + .resource = resources_qup_spi_gsbi5, +}; + +static struct resource resources_qup_i2c_gsbi5[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI5_PHYS, + .end = MSM_GSBI5_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI5_QUP_PHYS, + .end = MSM_GSBI5_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI5_QUP_IRQ, + .end = GSBI5_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = 54, + .end = 54, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = 53, + .end = 53, + .flags = IORESOURCE_IO, + }, +}; + +struct platform_device mpq8064_device_qup_i2c_gsbi5 = { + .name = "qup_i2c", + .id = 5, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi5), + .resource = resources_qup_i2c_gsbi5, +}; + +static struct resource resources_uart_gsbi7[] = { + { + .start = GSBI7_UARTDM_IRQ, + .end = GSBI7_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART7DM_PHYS, + .end = MSM_UART7DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI7_PHYS, + .end = MSM_GSBI7_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device apq8064_device_uart_gsbi7 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart_gsbi7), + .resource = resources_uart_gsbi7, +}; + +struct platform_device apq_pcm = { + .name = "msm-pcm-dsp", + .id = -1, +}; + +struct platform_device apq_pcm_routing = { + .name = "msm-pcm-routing", + .id = -1, +}; + +struct platform_device apq_cpudai0 = { + .name = "msm-dai-q6", + .id = 0x4000, +}; + +struct platform_device apq_cpudai1 = { + .name = "msm-dai-q6", + .id = 0x4001, +}; +struct platform_device mpq_cpudai_sec_i2s_rx = { + .name = "msm-dai-q6", + .id = 4, +}; +struct platform_device apq_cpudai_hdmi_rx = { + .name = "msm-dai-q6-hdmi", + .id = 8, +}; + +struct platform_device apq_cpudai_bt_rx = { + .name = "msm-dai-q6", + .id = 0x3000, +}; + +struct platform_device apq_cpudai_bt_tx = { + .name = "msm-dai-q6", + .id = 0x3001, +}; + +struct platform_device apq_cpudai_fm_rx = { + .name = "msm-dai-q6", + .id = 0x3004, +}; + +struct platform_device apq_cpudai_fm_tx = { + .name = "msm-dai-q6", + .id = 0x3005, +}; + +struct platform_device apq_cpudai_slim_4_rx = { + .name = "msm-dai-q6", + .id = 0x4008, +}; + +struct platform_device apq_cpudai_slim_4_tx = { + .name = "msm-dai-q6", + .id = 0x4009, +}; + +/* + * Machine specific data for AUX PCM Interface + * which the driver will be unware of. + */ +struct msm_dai_auxpcm_pdata apq_auxpcm_pdata = { + .clk = "pcm_clk", + .mode_8k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 2048000, + }, + .mode_16k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 4096000, + } +}; + +struct platform_device apq_cpudai_auxpcm_rx = { + .name = "msm-dai-q6", + .id = 2, + .dev = { + .platform_data = &apq_auxpcm_pdata, + }, +}; + +struct platform_device apq_cpudai_auxpcm_tx = { + .name = "msm-dai-q6", + .id = 3, + .dev = { + .platform_data = &apq_auxpcm_pdata, + }, +}; + +struct msm_mi2s_pdata mpq_mi2s_tx_data = { + .rx_sd_lines = 0, + .tx_sd_lines = MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | + MSM_MI2S_SD3, +}; + +struct platform_device mpq_cpudai_mi2s_tx = { + .name = "msm-dai-q6-mi2s", + .id = -1, /*MI2S_TX */ + .dev = { + .platform_data = &mpq_mi2s_tx_data, + }, +}; + +struct platform_device apq_cpu_fe = { + .name = "msm-dai-fe", + .id = -1, +}; + +struct platform_device apq_stub_codec = { + .name = "msm-stub-codec", + .id = 1, +}; + +struct platform_device apq_voice = { + .name = "msm-pcm-voice", + .id = -1, +}; + +struct platform_device apq_voip = { + .name = "msm-voip-dsp", + .id = -1, +}; + +struct platform_device apq_lpa_pcm = { + .name = "msm-pcm-lpa", + .id = -1, +}; + +struct platform_device apq_compr_dsp = { + .name = "msm-compr-dsp", + .id = -1, +}; + +struct platform_device apq_multi_ch_pcm = { + .name = "msm-multi-ch-pcm-dsp", + .id = -1, +}; + +struct platform_device apq_pcm_hostless = { + .name = "msm-pcm-hostless", + .id = -1, +}; + +struct platform_device apq_cpudai_afe_01_rx = { + .name = "msm-dai-q6", + .id = 0xE0, +}; + +struct platform_device apq_cpudai_afe_01_tx = { + .name = "msm-dai-q6", + .id = 0xF0, +}; + +struct platform_device apq_cpudai_afe_02_rx = { + .name = "msm-dai-q6", + .id = 0xF1, +}; + +struct platform_device apq_cpudai_afe_02_tx = { + .name = "msm-dai-q6", + .id = 0xE1, +}; + +struct platform_device apq_pcm_afe = { + .name = "msm-pcm-afe", + .id = -1, +}; + +struct platform_device apq_cpudai_stub = { + .name = "msm-dai-stub", + .id = -1, +}; + +struct platform_device apq_cpudai_slimbus_1_rx = { + .name = "msm-dai-q6", + .id = 0x4002, +}; + +struct platform_device apq_cpudai_slimbus_1_tx = { + .name = "msm-dai-q6", + .id = 0x4003, +}; + +struct platform_device apq_cpudai_slimbus_2_rx = { + .name = "msm-dai-q6", + .id = 0x4004, +}; + +struct platform_device apq_cpudai_slimbus_2_tx = { + .name = "msm-dai-q6", + .id = 0x4005, +}; + +struct platform_device apq_cpudai_slimbus_3_rx = { + .name = "msm-dai-q6", + .id = 0x4006, +}; + +struct platform_device apq_cpudai_slimbus_3_tx = { + .name = "msm-dai-q6", + .id = 0x4007, +}; + +static struct resource resources_ssbi_pmic1[] = { + { + .start = MSM_PMIC1_SSBI_CMD_PHYS, + .end = MSM_PMIC1_SSBI_CMD_PHYS + MSM_PMIC_SSBI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +#define LPASS_SLIMBUS_PHYS 0x28080000 +#define LPASS_SLIMBUS_BAM_PHYS 0x28084000 +#define LPASS_SLIMBUS_SLEW (MSM8960_TLMM_PHYS + 0x207C) +/* Board info for the slimbus slave device */ +static struct resource slimbus_res[] = { + { + .start = LPASS_SLIMBUS_PHYS, + .end = LPASS_SLIMBUS_PHYS + 8191, + .flags = IORESOURCE_MEM, + .name = "slimbus_physical", + }, + { + .start = LPASS_SLIMBUS_BAM_PHYS, + .end = LPASS_SLIMBUS_BAM_PHYS + 8191, + .flags = IORESOURCE_MEM, + .name = "slimbus_bam_physical", + }, + { + .start = LPASS_SLIMBUS_SLEW, + .end = LPASS_SLIMBUS_SLEW + 4 - 1, + .flags = IORESOURCE_MEM, + .name = "slimbus_slew_reg", + }, + { + .start = SLIMBUS0_CORE_EE1_IRQ, + .end = SLIMBUS0_CORE_EE1_IRQ, + .flags = IORESOURCE_IRQ, + .name = "slimbus_irq", + }, + { + .start = SLIMBUS0_BAM_EE1_IRQ, + .end = SLIMBUS0_BAM_EE1_IRQ, + .flags = IORESOURCE_IRQ, + .name = "slimbus_bam_irq", + }, +}; + +struct platform_device apq8064_slim_ctrl = { + .name = "msm_slim_ctrl", + .id = 1, + .num_resources = ARRAY_SIZE(slimbus_res), + .resource = slimbus_res, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device apq8064_device_ssbi_pmic1 = { + .name = "msm_ssbi", + .id = 0, + .resource = resources_ssbi_pmic1, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic1), +}; + +static struct resource resources_ssbi_pmic2[] = { + { + .start = MSM_PMIC2_SSBI_CMD_PHYS, + .end = MSM_PMIC2_SSBI_CMD_PHYS + MSM_PMIC_SSBI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device apq8064_device_ssbi_pmic2 = { + .name = "msm_ssbi", + .id = 1, + .resource = resources_ssbi_pmic2, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic2), +}; + +static struct resource resources_otg[] = { + { + .start = MSM_HSUSB1_PHYS, + .end = MSM_HSUSB1_PHYS + MSM_HSUSB1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device apq8064_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_hsusb[] = { + { + .start = MSM_HSUSB1_PHYS, + .end = MSM_HSUSB1_PHYS + MSM_HSUSB1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device apq8064_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb), + .resource = resources_hsusb, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM_HSUSB1_PHYS, + .end = MSM_HSUSB1_PHYS + MSM_HSUSB1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_hsic_host[] = { + { + .start = 0x12510000, + .end = 0x12510000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB2_HSIC_IRQ, + .end = USB2_HSIC_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_GPIO_TO_INT(49), + .end = MSM_GPIO_TO_INT(49), + .name = "peripheral_status_irq", + .flags = IORESOURCE_IRQ, + }, + { + .start = 47, + .end = 47, + .name = "wakeup", + .flags = IORESOURCE_IO, + }, +}; + +static u64 dma_mask = DMA_BIT_MASK(32); +struct platform_device apq8064_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device apq8064_device_hsic_host = { + .name = "msm_hsic_host", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsic_host), + .resource = resources_hsic_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource resources_ehci_host3[] = { +{ + .start = MSM_HSUSB3_PHYS, + .end = MSM_HSUSB3_PHYS + MSM_HSUSB3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB3_HS_IRQ, + .end = USB3_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device apq8064_device_ehci_host3 = { + .name = "msm_ehci_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_ehci_host3), + .resource = resources_ehci_host3, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_ehci_host4[] = { +{ + .start = MSM_HSUSB4_PHYS, + .end = MSM_HSUSB4_PHYS + MSM_HSUSB4_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB4_HS_IRQ, + .end = USB4_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device apq8064_device_ehci_host4 = { + .name = "msm_ehci_host", + .id = 1, + .num_resources = ARRAY_SIZE(resources_ehci_host4), + .resource = resources_ehci_host4, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffff, + }, +}; + +#define SHARED_IMEM_TZ_BASE 0x2a03f720 +static struct resource tzlog_resources[] = { + { + .start = SHARED_IMEM_TZ_BASE, + .end = SHARED_IMEM_TZ_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device apq_device_tz_log = { + .name = "tz_log", + .id = 0, + .num_resources = ARRAY_SIZE(tzlog_resources), + .resource = tzlog_resources, +}; + +/* MSM Video core device */ +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors vidc_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; +static struct msm_bus_vectors vidc_venc_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 54525952, + .ib = 436207616, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 72351744, + .ib = 289406976, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 40894464, + .ib = 327155712, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 48234496, + .ib = 192937984, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, +}; +static struct msm_bus_vectors vidc_venc_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 163577856, + .ib = 1308622848, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 219152384, + .ib = 876609536, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, +}; +static struct msm_bus_vectors vidc_vdec_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 121634816, + .ib = 973078528, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 155189248, + .ib = 620756992, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, +}; +static struct msm_bus_vectors vidc_venc_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 372244480, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 501219328, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_ENC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 222298112, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_VIDEO_DEC, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 330301440, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 700000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 10000000, + }, +}; + +static struct msm_bus_paths vidc_bus_client_config[] = { + { + ARRAY_SIZE(vidc_init_vectors), + vidc_init_vectors, + }, + { + ARRAY_SIZE(vidc_venc_vga_vectors), + vidc_venc_vga_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_vga_vectors), + vidc_vdec_vga_vectors, + }, + { + ARRAY_SIZE(vidc_venc_720p_vectors), + vidc_venc_720p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_720p_vectors), + vidc_vdec_720p_vectors, + }, + { + ARRAY_SIZE(vidc_venc_1080p_vectors), + vidc_venc_1080p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_1080p_vectors), + vidc_vdec_1080p_vectors, + }, +}; + +static struct msm_bus_scale_pdata vidc_bus_client_data = { + vidc_bus_client_config, + ARRAY_SIZE(vidc_bus_client_config), + .name = "vidc", +}; +#endif + + +#define APQ8064_VIDC_BASE_PHYS 0x04400000 +#define APQ8064_VIDC_BASE_SIZE 0x00100000 + +static struct resource apq8064_device_vidc_resources[] = { + { + .start = APQ8064_VIDC_BASE_PHYS, + .end = APQ8064_VIDC_BASE_PHYS + APQ8064_VIDC_BASE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = VCODEC_IRQ, + .end = VCODEC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_vidc_platform_data apq8064_vidc_platform_data = { +#ifdef CONFIG_MSM_BUS_SCALING + .vidc_bus_client_pdata = &vidc_bus_client_data, +#endif +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .memtype = ION_CP_MM_HEAP_ID, + .enable_ion = 1, + .cp_enabled = 1, +#else + .memtype = MEMTYPE_EBI1, + .enable_ion = 0, +#endif + .disable_dmx = 0, + .disable_fullhd = 0, + .cont_mode_dpb_count = 18, +}; + +struct platform_device apq8064_msm_device_vidc = { + .name = "msm_vidc", + .id = 0, + .num_resources = ARRAY_SIZE(apq8064_device_vidc_resources), + .resource = apq8064_device_vidc_resources, + .dev = { + .platform_data = &apq8064_vidc_platform_data, + }, +}; +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC1_DML_BASE (MSM_SDC1_BASE + 0x800) +#define MSM_SDC1_BAM_BASE (MSM_SDC1_BASE + 0x2000) +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC2_DML_BASE (MSM_SDC2_BASE + 0x800) +#define MSM_SDC2_BAM_BASE (MSM_SDC2_BASE + 0x2000) +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC3_DML_BASE (MSM_SDC3_BASE + 0x800) +#define MSM_SDC3_BAM_BASE (MSM_SDC3_BASE + 0x2000) +#define MSM_SDC4_BASE 0x121C0000 +#define MSM_SDC4_DML_BASE (MSM_SDC4_BASE + 0x800) +#define MSM_SDC4_BAM_BASE (MSM_SDC4_BASE + 0x2000) + +static struct resource resources_sdc1[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC1_IRQ_0, + .end = SDC1_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC1_DML_BASE, + .end = MSM_SDC1_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC1_BAM_BASE, + .end = MSM_SDC1_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC1_BAM_IRQ, + .end = SDC1_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc2[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC2_IRQ_0, + .end = SDC2_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC2_DML_BASE, + .end = MSM_SDC2_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC2_BAM_BASE, + .end = MSM_SDC2_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC2_BAM_IRQ, + .end = SDC2_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc3[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC3_IRQ_0, + .end = SDC3_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC3_DML_BASE, + .end = MSM_SDC3_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC3_BAM_BASE, + .end = MSM_SDC3_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC3_BAM_IRQ, + .end = SDC3_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc4[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC4_IRQ_0, + .end = SDC4_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC4_DML_BASE, + .end = MSM_SDC4_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC4_BAM_BASE, + .end = MSM_SDC4_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC4_BAM_IRQ, + .end = SDC4_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +struct platform_device apq8064_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device apq8064_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device apq8064_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device apq8064_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *apq8064_sdcc_devices[] __initdata = { + &apq8064_device_sdc1, + &apq8064_device_sdc2, + &apq8064_device_sdc3, + &apq8064_device_sdc4, +}; + +int __init apq8064_add_sdcc(unsigned int controller, + struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (!plat) + return 0; + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = apq8064_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +static struct resource resources_sps[] = { + { + .name = "pipe_mem", + .start = 0x12800000, + .end = 0x12800000 + 0x4000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_dma", + .start = 0x12240000, + .end = 0x12240000 + 0x1000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_bam", + .start = 0x12244000, + .end = 0x12244000 + 0x4000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_irq", + .start = SPS_BAM_DMA_IRQ, + .end = SPS_BAM_DMA_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_bus_8064_sys_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM, +}; +struct platform_device msm_bus_8064_apps_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_APPSS, +}; +struct platform_device msm_bus_8064_mm_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_MMSS, +}; +struct platform_device msm_bus_8064_sys_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM_FPB, +}; +struct platform_device msm_bus_8064_cpss_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_CPSS_FPB, +}; + +static struct msm_sps_platform_data msm_sps_pdata = { + .bamdma_restricted_pipes = 0x06, +}; + +struct platform_device msm_device_sps_apq8064 = { + .name = "msm_sps", + .id = -1, + .num_resources = ARRAY_SIZE(resources_sps), + .resource = resources_sps, + .dev.platform_data = &msm_sps_pdata, +}; + +static struct resource smd_resource[] = { + { + .name = "a9_m2a_0", + .start = INT_A9_M2A_0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "a9_m2a_5", + .start = INT_A9_M2A_5, + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_a11", + .start = INT_ADSP_A11, + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_a11_smsm", + .start = INT_ADSP_A11_SMSM, + .flags = IORESOURCE_IRQ, + }, + { + .name = "dsps_a11", + .start = INT_DSPS_A11, + .flags = IORESOURCE_IRQ, + }, + { + .name = "dsps_a11_smsm", + .start = INT_DSPS_A11_SMSM, + .flags = IORESOURCE_IRQ, + }, + { + .name = "wcnss_a11", + .start = INT_WCNSS_A11, + .flags = IORESOURCE_IRQ, + }, + { + .name = "wcnss_a11_smsm", + .start = INT_WCNSS_A11_SMSM, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smd_subsystem_config smd_config_list[] = { + { + .irq_config_id = SMD_MODEM, + .subsys_name = "gss", + .edge = SMD_APPS_MODEM, + + .smd_int.irq_name = "a9_m2a_0", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 3, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "a9_m2a_5", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 4, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_Q6, + .subsys_name = "q6", + .edge = SMD_APPS_QDSP, + + .smd_int.irq_name = "adsp_a11", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 15, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "adsp_a11_smsm", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 14, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_DSPS, + .subsys_name = "dsps", + .edge = SMD_APPS_DSPS, + + .smd_int.irq_name = "dsps_a11", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1, + .smd_int.out_base = (void __iomem *)MSM_SIC_NON_SECURE_BASE, + .smd_int.out_offset = 0x4080, + + .smsm_int.irq_name = "dsps_a11_smsm", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1, + .smsm_int.out_base = (void __iomem *)MSM_SIC_NON_SECURE_BASE, + .smsm_int.out_offset = 0x4094, + }, + { + .irq_config_id = SMD_WCNSS, + .subsys_name = "wcnss", + .edge = SMD_APPS_WCNSS, + + .smd_int.irq_name = "wcnss_a11", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 25, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "wcnss_a11_smsm", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 23, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, +}; + +static struct smd_subsystem_restart_config smd_ssr_config = { + .disable_smsm_reset_handshake = 1, +}; + +static struct smd_platform smd_platform_data = { + .num_ss_configs = ARRAY_SIZE(smd_config_list), + .smd_ss_configs = smd_config_list, + .smd_ssr_config = &smd_ssr_config, +}; + +struct platform_device msm_device_smd_apq8064 = { + .name = "msm_smd", + .id = -1, + .resource = smd_resource, + .num_resources = ARRAY_SIZE(smd_resource), + .dev = { + .platform_data = &smd_platform_data, + }, +}; + +static struct resource resources_msm_pcie[] = { + { + .name = "parf", + .start = PCIE20_PARF_PHYS, + .end = PCIE20_PARF_PHYS + PCIE20_PARF_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "elbi", + .start = PCIE20_ELBI_PHYS, + .end = PCIE20_ELBI_PHYS + PCIE20_ELBI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "pcie20", + .start = PCIE20_PHYS, + .end = PCIE20_PHYS + PCIE20_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "axi_bar", + .start = PCIE_AXI_BAR_PHYS, + .end = PCIE_AXI_BAR_PHYS + PCIE_AXI_BAR_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "axi_conf", + .start = PCIE_AXI_CONF_PHYS, + .end = PCIE_AXI_CONF_PHYS + PCIE_AXI_CONF_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_pcie = { + .name = "msm_pcie", + .id = -1, + .num_resources = ARRAY_SIZE(resources_msm_pcie), + .resource = resources_msm_pcie, +}; + +#ifdef CONFIG_HW_RANDOM_MSM +/* PRNG device */ +#define MSM_PRNG_PHYS 0x1A500000 +static struct resource rng_resources = { + .flags = IORESOURCE_MEM, + .start = MSM_PRNG_PHYS, + .end = MSM_PRNG_PHYS + SZ_512 - 1, +}; + +struct platform_device apq8064_device_rng = { + .name = "msm_rng", + .id = 0, + .num_resources = 1, + .resource = &rng_resources, +}; +#endif + +static struct resource msm_gss_resources[] = { + { + .start = 0x10000000, + .end = 0x10000000 + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x10008000, + .end = 0x10008000 + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_gss = { + .name = "pil_gss", + .id = -1, + .num_resources = ARRAY_SIZE(msm_gss_resources), + .resource = msm_gss_resources, +}; + +static struct fs_driver_data gfx3d_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk", .reset_rate = 27000000 }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D, + .bus_port1 = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, +}; + +static struct fs_driver_data ijpeg_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_JPEG_ENC, +}; + +static struct fs_driver_data mdp_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { .name = "vsync_clk" }, + { .name = "lut_clk" }, + { .name = "tv_src_clk" }, + { .name = "tv_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_MDP_PORT0, + .bus_port1 = MSM_BUS_MASTER_MDP_PORT1, +}; + +static struct fs_driver_data rot_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_ROTATOR, +}; + +static struct fs_driver_data ved_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VIDEO_ENC, + .bus_port1 = MSM_BUS_MASTER_VIDEO_DEC, +}; + +static struct fs_driver_data vfe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VFE, +}; + +static struct fs_driver_data vpe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VPE, +}; + +static struct fs_driver_data vcap_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 }, + }, + .bus_port0 = MSM_BUS_MASTER_VIDEO_CAP, +}; + +struct platform_device *apq8064_footswitch[] __initdata = { + FS_8X60(FS_MDP, "vdd", "mdp.0", &mdp_fs_data), + FS_8X60(FS_ROT, "vdd", "msm_rotator.0", &rot_fs_data), + FS_8X60(FS_IJPEG, "vdd", "msm_gemini.0", &ijpeg_fs_data), + FS_8X60(FS_VFE, "fs_vfe", NULL, &vfe_fs_data), + FS_8X60(FS_VPE, "fs_vpe", NULL, &vpe_fs_data), + FS_8X60(FS_GFX3D, "vdd", "kgsl-3d0.0", &gfx3d_fs_data), + FS_8X60(FS_VED, "vdd", "msm_vidc.0", &ved_fs_data), + FS_8X60(FS_VCAP, "vdd", "msm_vcap.0", &vcap_fs_data), +}; +unsigned apq8064_num_footswitch __initdata = ARRAY_SIZE(apq8064_footswitch); + +struct msm_rpm_platform_data apq8064_rpm_data __initdata = { + .reg_base_addrs = { + [MSM_RPM_PAGE_STATUS] = MSM_RPM_BASE, + [MSM_RPM_PAGE_CTRL] = MSM_RPM_BASE + 0x400, + [MSM_RPM_PAGE_REQ] = MSM_RPM_BASE + 0x600, + [MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00, + }, + .irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ, + .irq_err = RPM_APCC_CPU0_GP_LOW_IRQ, + .irq_wakeup = RPM_APCC_CPU0_WAKE_UP_IRQ, + .ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008, + .ipc_rpm_val = 4, + .target_id = { + MSM_RPM_MAP(8064, NOTIFICATION_CONFIGURED_0, NOTIFICATION, 4), + MSM_RPM_MAP(8064, NOTIFICATION_REGISTERED_0, NOTIFICATION, 4), + MSM_RPM_MAP(8064, INVALIDATE_0, INVALIDATE, 8), + MSM_RPM_MAP(8064, TRIGGER_TIMED_TO, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8064, TRIGGER_TIMED_SCLK_COUNT, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8064, RPM_CTL, RPM_CTL, 1), + MSM_RPM_MAP(8064, CXO_CLK, CXO_CLK, 1), + MSM_RPM_MAP(8064, PXO_CLK, PXO_CLK, 1), + MSM_RPM_MAP(8064, APPS_FABRIC_CLK, APPS_FABRIC_CLK, 1), + MSM_RPM_MAP(8064, SYSTEM_FABRIC_CLK, SYSTEM_FABRIC_CLK, 1), + MSM_RPM_MAP(8064, MM_FABRIC_CLK, MM_FABRIC_CLK, 1), + MSM_RPM_MAP(8064, DAYTONA_FABRIC_CLK, DAYTONA_FABRIC_CLK, 1), + MSM_RPM_MAP(8064, SFPB_CLK, SFPB_CLK, 1), + MSM_RPM_MAP(8064, CFPB_CLK, CFPB_CLK, 1), + MSM_RPM_MAP(8064, MMFPB_CLK, MMFPB_CLK, 1), + MSM_RPM_MAP(8064, EBI1_CLK, EBI1_CLK, 1), + MSM_RPM_MAP(8064, APPS_FABRIC_CFG_HALT_0, + APPS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8064, APPS_FABRIC_CFG_CLKMOD_0, + APPS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8064, APPS_FABRIC_CFG_IOCTL, + APPS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8064, APPS_FABRIC_ARB_0, APPS_FABRIC_ARB, 12), + MSM_RPM_MAP(8064, SYS_FABRIC_CFG_HALT_0, + SYS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8064, SYS_FABRIC_CFG_CLKMOD_0, + SYS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8064, SYS_FABRIC_CFG_IOCTL, + SYS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8064, SYSTEM_FABRIC_ARB_0, SYSTEM_FABRIC_ARB, 30), + MSM_RPM_MAP(8064, MMSS_FABRIC_CFG_HALT_0, + MMSS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8064, MMSS_FABRIC_CFG_CLKMOD_0, + MMSS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8064, MMSS_FABRIC_CFG_IOCTL, + MMSS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8064, MM_FABRIC_ARB_0, MM_FABRIC_ARB, 21), + MSM_RPM_MAP(8064, PM8921_S1_0, PM8921_S1, 2), + MSM_RPM_MAP(8064, PM8921_S2_0, PM8921_S2, 2), + MSM_RPM_MAP(8064, PM8921_S3_0, PM8921_S3, 2), + MSM_RPM_MAP(8064, PM8921_S4_0, PM8921_S4, 2), + MSM_RPM_MAP(8064, PM8921_S5_0, PM8921_S5, 2), + MSM_RPM_MAP(8064, PM8921_S6_0, PM8921_S6, 2), + MSM_RPM_MAP(8064, PM8921_S7_0, PM8921_S7, 2), + MSM_RPM_MAP(8064, PM8921_S8_0, PM8921_S8, 2), + MSM_RPM_MAP(8064, PM8921_L1_0, PM8921_L1, 2), + MSM_RPM_MAP(8064, PM8921_L2_0, PM8921_L2, 2), + MSM_RPM_MAP(8064, PM8921_L3_0, PM8921_L3, 2), + MSM_RPM_MAP(8064, PM8921_L4_0, PM8921_L4, 2), + MSM_RPM_MAP(8064, PM8921_L5_0, PM8921_L5, 2), + MSM_RPM_MAP(8064, PM8921_L6_0, PM8921_L6, 2), + MSM_RPM_MAP(8064, PM8921_L7_0, PM8921_L7, 2), + MSM_RPM_MAP(8064, PM8921_L8_0, PM8921_L8, 2), + MSM_RPM_MAP(8064, PM8921_L9_0, PM8921_L9, 2), + MSM_RPM_MAP(8064, PM8921_L10_0, PM8921_L10, 2), + MSM_RPM_MAP(8064, PM8921_L11_0, PM8921_L11, 2), + MSM_RPM_MAP(8064, PM8921_L12_0, PM8921_L12, 2), + MSM_RPM_MAP(8064, PM8921_L13_0, PM8921_L13, 2), + MSM_RPM_MAP(8064, PM8921_L14_0, PM8921_L14, 2), + MSM_RPM_MAP(8064, PM8921_L15_0, PM8921_L15, 2), + MSM_RPM_MAP(8064, PM8921_L16_0, PM8921_L16, 2), + MSM_RPM_MAP(8064, PM8921_L17_0, PM8921_L17, 2), + MSM_RPM_MAP(8064, PM8921_L18_0, PM8921_L18, 2), + MSM_RPM_MAP(8064, PM8921_L19_0, PM8921_L19, 2), + MSM_RPM_MAP(8064, PM8921_L20_0, PM8921_L20, 2), + MSM_RPM_MAP(8064, PM8921_L21_0, PM8921_L21, 2), + MSM_RPM_MAP(8064, PM8921_L22_0, PM8921_L22, 2), + MSM_RPM_MAP(8064, PM8921_L23_0, PM8921_L23, 2), + MSM_RPM_MAP(8064, PM8921_L24_0, PM8921_L24, 2), + MSM_RPM_MAP(8064, PM8921_L25_0, PM8921_L25, 2), + MSM_RPM_MAP(8064, PM8921_L26_0, PM8921_L26, 2), + MSM_RPM_MAP(8064, PM8921_L27_0, PM8921_L27, 2), + MSM_RPM_MAP(8064, PM8921_L28_0, PM8921_L28, 2), + MSM_RPM_MAP(8064, PM8921_L29_0, PM8921_L29, 2), + MSM_RPM_MAP(8064, PM8921_CLK1_0, PM8921_CLK1, 2), + MSM_RPM_MAP(8064, PM8921_CLK2_0, PM8921_CLK2, 2), + MSM_RPM_MAP(8064, PM8921_LVS1, PM8921_LVS1, 1), + MSM_RPM_MAP(8064, PM8921_LVS2, PM8921_LVS2, 1), + MSM_RPM_MAP(8064, PM8921_LVS3, PM8921_LVS3, 1), + MSM_RPM_MAP(8064, PM8921_LVS4, PM8921_LVS4, 1), + MSM_RPM_MAP(8064, PM8921_LVS5, PM8921_LVS5, 1), + MSM_RPM_MAP(8064, PM8921_LVS6, PM8921_LVS6, 1), + MSM_RPM_MAP(8064, PM8921_LVS7, PM8921_LVS7, 1), + MSM_RPM_MAP(8064, PM8821_S1_0, PM8821_S1, 2), + MSM_RPM_MAP(8064, PM8821_S2_0, PM8821_S2, 2), + MSM_RPM_MAP(8064, PM8821_L1_0, PM8821_L1, 2), + MSM_RPM_MAP(8064, NCP_0, NCP, 2), + MSM_RPM_MAP(8064, CXO_BUFFERS, CXO_BUFFERS, 1), + MSM_RPM_MAP(8064, USB_OTG_SWITCH, USB_OTG_SWITCH, 1), + MSM_RPM_MAP(8064, HDMI_SWITCH, HDMI_SWITCH, 1), + MSM_RPM_MAP(8064, DDR_DMM_0, DDR_DMM, 2), + MSM_RPM_MAP(8064, QDSS_CLK, QDSS_CLK, 1), + }, + .target_status = { + MSM_RPM_STATUS_ID_MAP(8064, VERSION_MAJOR), + MSM_RPM_STATUS_ID_MAP(8064, VERSION_MINOR), + MSM_RPM_STATUS_ID_MAP(8064, VERSION_BUILD), + MSM_RPM_STATUS_ID_MAP(8064, SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8064, SUPPORTED_RESOURCES_1), + MSM_RPM_STATUS_ID_MAP(8064, SUPPORTED_RESOURCES_2), + MSM_RPM_STATUS_ID_MAP(8064, RESERVED_SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8064, SEQUENCE), + MSM_RPM_STATUS_ID_MAP(8064, RPM_CTL), + MSM_RPM_STATUS_ID_MAP(8064, CXO_CLK), + MSM_RPM_STATUS_ID_MAP(8064, PXO_CLK), + MSM_RPM_STATUS_ID_MAP(8064, APPS_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8064, SYSTEM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8064, MM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8064, DAYTONA_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8064, SFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8064, CFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8064, MMFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8064, EBI1_CLK), + MSM_RPM_STATUS_ID_MAP(8064, APPS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8064, APPS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8064, APPS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8064, APPS_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8064, SYS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8064, SYS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8064, SYS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8064, SYSTEM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8064, MMSS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8064, MMSS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8064, MMSS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8064, MM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S1_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S1_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S2_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S2_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S3_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S3_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S4_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S4_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S5_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S5_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S6_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S6_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S7_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S7_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S8_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_S8_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L1_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L1_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L2_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L2_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L3_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L3_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L4_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L4_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L5_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L5_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L6_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L6_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L7_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L7_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L8_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L8_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L9_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L9_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L10_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L10_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L11_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L11_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L12_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L12_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L13_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L13_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L14_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L14_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L15_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L15_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L16_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L16_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L17_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L17_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L18_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L18_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L19_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L19_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L20_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L20_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L21_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L21_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L22_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L22_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L23_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L23_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L24_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L24_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L25_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L25_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L26_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L26_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L27_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L27_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L28_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L28_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L29_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_L29_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_CLK1_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_CLK1_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_CLK2_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_CLK2_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS1), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS2), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS3), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS4), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS5), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS6), + MSM_RPM_STATUS_ID_MAP(8064, PM8921_LVS7), + MSM_RPM_STATUS_ID_MAP(8064, NCP_0), + MSM_RPM_STATUS_ID_MAP(8064, NCP_1), + MSM_RPM_STATUS_ID_MAP(8064, CXO_BUFFERS), + MSM_RPM_STATUS_ID_MAP(8064, USB_OTG_SWITCH), + MSM_RPM_STATUS_ID_MAP(8064, HDMI_SWITCH), + MSM_RPM_STATUS_ID_MAP(8064, DDR_DMM_0), + MSM_RPM_STATUS_ID_MAP(8064, DDR_DMM_1), + MSM_RPM_STATUS_ID_MAP(8064, EBI1_CH0_RANGE), + MSM_RPM_STATUS_ID_MAP(8064, EBI1_CH1_RANGE), + MSM_RPM_STATUS_ID_MAP(8064, PM8821_S1_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8821_S1_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8821_S2_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8821_S2_1), + MSM_RPM_STATUS_ID_MAP(8064, PM8821_L1_0), + MSM_RPM_STATUS_ID_MAP(8064, PM8821_L1_1), + }, + .target_ctrl_id = { + MSM_RPM_CTRL_MAP(8064, VERSION_MAJOR), + MSM_RPM_CTRL_MAP(8064, VERSION_MINOR), + MSM_RPM_CTRL_MAP(8064, VERSION_BUILD), + MSM_RPM_CTRL_MAP(8064, REQ_CTX_0), + MSM_RPM_CTRL_MAP(8064, REQ_SEL_0), + MSM_RPM_CTRL_MAP(8064, ACK_CTX_0), + MSM_RPM_CTRL_MAP(8064, ACK_SEL_0), + }, + .sel_invalidate = MSM_RPM_8064_SEL_INVALIDATE, + .sel_notification = MSM_RPM_8064_SEL_NOTIFICATION, + .sel_last = MSM_RPM_8064_SEL_LAST, + .ver = {3, 0, 0}, +}; + +struct platform_device apq8064_rpm_device = { + .name = "msm_rpm", + .id = -1, +}; + +static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = { + .phys_addr_base = 0x0010D204, + .phys_size = SZ_8K, +}; + +struct platform_device apq8064_rpm_stat_device = { + .name = "msm_rpm_stat", + .id = -1, + .dev = { + .platform_data = &msm_rpm_stat_pdata, + }, +}; + +static struct msm_rpm_log_platform_data msm_rpm_log_pdata = { + .phys_addr_base = 0x0010C000, + .reg_offsets = { + [MSM_RPM_LOG_PAGE_INDICES] = 0x00000080, + [MSM_RPM_LOG_PAGE_BUFFER] = 0x000000A0, + }, + .phys_size = SZ_8K, + .log_len = 4096, /* log's buffer length in bytes */ + .log_len_mask = (4096 >> 2) - 1, /* length mask in units of u32 */ +}; + +struct platform_device apq8064_rpm_log_device = { + .name = "msm_rpm_log", + .id = -1, + .dev = { + .platform_data = &msm_rpm_log_pdata, + }, +}; + +/* Sensors DSPS platform data */ + +#define PPSS_DSPS_TCM_CODE_BASE 0x12000000 +#define PPSS_DSPS_TCM_CODE_SIZE 0x28000 +#define PPSS_DSPS_TCM_BUF_BASE 0x12040000 +#define PPSS_DSPS_TCM_BUF_SIZE 0x4000 +#define PPSS_DSPS_PIPE_BASE 0x12800000 +#define PPSS_DSPS_PIPE_SIZE 0x4000 +#define PPSS_DSPS_DDR_BASE 0x8fe00000 +#define PPSS_DSPS_DDR_SIZE 0x100000 +#define PPSS_SMEM_BASE 0x80000000 +#define PPSS_SMEM_SIZE 0x200000 +#define PPSS_REG_PHYS_BASE 0x12080000 + +static struct dsps_clk_info dsps_clks[] = {}; +static struct dsps_regulator_info dsps_regs[] = {}; + +/* + * Note: GPIOs field is intialized in run-time at the function + * apq8064_init_dsps(). + */ + +struct msm_dsps_platform_data msm_dsps_pdata_8064 = { + .clks = dsps_clks, + .clks_num = ARRAY_SIZE(dsps_clks), + .gpios = NULL, + .gpios_num = 0, + .regs = dsps_regs, + .regs_num = ARRAY_SIZE(dsps_regs), + .dsps_pwr_ctl_en = 1, + .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE, + .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE, + .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE, + .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE, + .pipe_start = PPSS_DSPS_PIPE_BASE, + .pipe_size = PPSS_DSPS_PIPE_SIZE, + .ddr_start = PPSS_DSPS_DDR_BASE, + .ddr_size = PPSS_DSPS_DDR_SIZE, + .smem_start = PPSS_SMEM_BASE, + .smem_size = PPSS_SMEM_SIZE, + .signature = DSPS_SIGNATURE, +}; + +static struct resource msm_dsps_resources[] = { + { + .start = PPSS_REG_PHYS_BASE, + .end = PPSS_REG_PHYS_BASE + SZ_8K - 1, + .name = "ppss_reg", + .flags = IORESOURCE_MEM, + }, + + { + .start = PPSS_WDOG_TIMER_IRQ, + .end = PPSS_WDOG_TIMER_IRQ, + .name = "ppss_wdog", + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_dsps_device_8064 = { + .name = "msm_dsps", + .id = 0, + .num_resources = ARRAY_SIZE(msm_dsps_resources), + .resource = msm_dsps_resources, + .dev.platform_data = &msm_dsps_pdata_8064, +}; + +#ifdef CONFIG_MSM_MPM +static uint16_t msm_mpm_irqs_m2a[MSM_MPM_NR_MPM_IRQS] __initdata = { + [1] = MSM_GPIO_TO_INT(26), + [2] = MSM_GPIO_TO_INT(88), + [4] = MSM_GPIO_TO_INT(73), + [5] = MSM_GPIO_TO_INT(74), + [6] = MSM_GPIO_TO_INT(75), + [7] = MSM_GPIO_TO_INT(76), + [8] = MSM_GPIO_TO_INT(77), + [9] = MSM_GPIO_TO_INT(36), + [10] = MSM_GPIO_TO_INT(84), + [11] = MSM_GPIO_TO_INT(7), + [12] = MSM_GPIO_TO_INT(11), + [13] = MSM_GPIO_TO_INT(52), + [14] = MSM_GPIO_TO_INT(15), + [15] = MSM_GPIO_TO_INT(83), + [16] = USB3_HS_IRQ, + [19] = MSM_GPIO_TO_INT(61), + [20] = MSM_GPIO_TO_INT(58), + [23] = MSM_GPIO_TO_INT(65), + [24] = MSM_GPIO_TO_INT(63), + [25] = USB1_HS_IRQ, + [27] = HDMI_IRQ, + [29] = MSM_GPIO_TO_INT(22), + [30] = MSM_GPIO_TO_INT(72), + [31] = USB4_HS_IRQ, + [33] = MSM_GPIO_TO_INT(44), + [34] = MSM_GPIO_TO_INT(39), + [35] = MSM_GPIO_TO_INT(19), + [36] = MSM_GPIO_TO_INT(23), + [37] = MSM_GPIO_TO_INT(41), + [38] = MSM_GPIO_TO_INT(30), + [41] = MSM_GPIO_TO_INT(42), + [42] = MSM_GPIO_TO_INT(56), + [43] = MSM_GPIO_TO_INT(55), + [44] = MSM_GPIO_TO_INT(50), + [45] = MSM_GPIO_TO_INT(49), + [46] = MSM_GPIO_TO_INT(47), + [47] = MSM_GPIO_TO_INT(45), + [48] = MSM_GPIO_TO_INT(38), + [49] = MSM_GPIO_TO_INT(34), + [50] = MSM_GPIO_TO_INT(32), + [51] = MSM_GPIO_TO_INT(29), + [52] = MSM_GPIO_TO_INT(18), + [53] = MSM_GPIO_TO_INT(10), + [54] = MSM_GPIO_TO_INT(81), + [55] = MSM_GPIO_TO_INT(6), +}; + +static uint16_t msm_mpm_bypassed_apps_irqs[] __initdata = { + TLMM_MSM_SUMMARY_IRQ, + RPM_APCC_CPU0_GP_HIGH_IRQ, + RPM_APCC_CPU0_GP_MEDIUM_IRQ, + RPM_APCC_CPU0_GP_LOW_IRQ, + RPM_APCC_CPU0_WAKE_UP_IRQ, + RPM_APCC_CPU1_GP_HIGH_IRQ, + RPM_APCC_CPU1_GP_MEDIUM_IRQ, + RPM_APCC_CPU1_GP_LOW_IRQ, + RPM_APCC_CPU1_WAKE_UP_IRQ, + MSS_TO_APPS_IRQ_0, + MSS_TO_APPS_IRQ_1, + MSS_TO_APPS_IRQ_2, + MSS_TO_APPS_IRQ_3, + MSS_TO_APPS_IRQ_4, + MSS_TO_APPS_IRQ_5, + MSS_TO_APPS_IRQ_6, + MSS_TO_APPS_IRQ_7, + MSS_TO_APPS_IRQ_8, + MSS_TO_APPS_IRQ_9, + LPASS_SCSS_GP_LOW_IRQ, + LPASS_SCSS_GP_MEDIUM_IRQ, + LPASS_SCSS_GP_HIGH_IRQ, + SPS_MTI_30, + SPS_MTI_31, + RIVA_APSS_SPARE_IRQ, + RIVA_APPS_WLAN_SMSM_IRQ, + RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, +}; + +struct msm_mpm_device_data apq8064_mpm_dev_data __initdata = { + .irqs_m2a = msm_mpm_irqs_m2a, + .irqs_m2a_size = ARRAY_SIZE(msm_mpm_irqs_m2a), + .bypassed_apps_irqs = msm_mpm_bypassed_apps_irqs, + .bypassed_apps_irqs_size = ARRAY_SIZE(msm_mpm_bypassed_apps_irqs), + .mpm_request_reg_base = MSM_RPM_BASE + 0x9d8, + .mpm_status_reg_base = MSM_RPM_BASE + 0xdf8, + .mpm_apps_ipc_reg = MSM_APCS_GCC_BASE + 0x008, + .mpm_apps_ipc_val = BIT(1), + .mpm_ipc_irq = RPM_APCC_CPU0_GP_MEDIUM_IRQ, + +}; +#endif + +/* AP2MDM_SOFT_RESET is implemented by the PON_RESET_N gpio */ +#define MDM2AP_ERRFATAL 19 +#define AP2MDM_ERRFATAL 18 +#define MDM2AP_STATUS 49 +#define AP2MDM_STATUS 48 +#define AP2MDM_SOFT_RESET 27 +#define AP2MDM_WAKEUP 35 + +static struct resource mdm_resources[] = { + { + .start = MDM2AP_ERRFATAL, + .end = MDM2AP_ERRFATAL, + .name = "MDM2AP_ERRFATAL", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_ERRFATAL, + .end = AP2MDM_ERRFATAL, + .name = "AP2MDM_ERRFATAL", + .flags = IORESOURCE_IO, + }, + { + .start = MDM2AP_STATUS, + .end = MDM2AP_STATUS, + .name = "MDM2AP_STATUS", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_STATUS, + .end = AP2MDM_STATUS, + .name = "AP2MDM_STATUS", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_SOFT_RESET, + .end = AP2MDM_SOFT_RESET, + .name = "AP2MDM_SOFT_RESET", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_WAKEUP, + .end = AP2MDM_WAKEUP, + .name = "AP2MDM_WAKEUP", + .flags = IORESOURCE_IO, + }, +}; + +struct platform_device mdm_8064_device = { + .name = "mdm2_modem", + .id = -1, + .num_resources = ARRAY_SIZE(mdm_resources), + .resource = mdm_resources, +}; + +static int apq8064_LPM_latency = 1000; /* >100 usec for WFI */ + +struct platform_device apq8064_cpu_idle_device = { + .name = "msm_cpu_idle", + .id = -1, + .dev = { + .platform_data = &apq8064_LPM_latency, + }, +}; + +static struct msm_dcvs_freq_entry apq8064_freq[] = { + { 384000, 166981, 345600}, + { 702000, 213049, 632502}, + {1026000, 285712, 925613}, + {1242000, 383945, 1176550}, + {1458000, 419729, 1465478}, + {1512000, 434116, 1546674}, + +}; + +static struct msm_dcvs_core_info apq8064_core_info = { + .freq_tbl = &apq8064_freq[0], + .core_param = { + .max_time_us = 100000, + .num_freq = ARRAY_SIZE(apq8064_freq), + }, + .algo_param = { + .slack_time_us = 58000, + .scale_slack_time = 0, + .scale_slack_time_pct = 0, + .disable_pc_threshold = 1458000, + .em_window_size = 100000, + .em_max_util_pct = 97, + .ss_window_size = 1000000, + .ss_util_pct = 95, + .ss_iobusy_conv = 100, + }, +}; + +struct platform_device apq8064_msm_gov_device = { + .name = "msm_dcvs_gov", + .id = -1, + .dev = { + .platform_data = &apq8064_core_info, + }, +}; + +#ifdef CONFIG_MSM_VCAP +#define VCAP_HW_BASE 0x05900000 + +static struct msm_bus_vectors vcap_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_CAP, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + + +static struct msm_bus_vectors vcap_480_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_CAP, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1280 * 720 * 3 * 60, + .ib = 1280 * 720 * 3 * 60 * 1.5, + }, +}; + +static struct msm_bus_vectors vcap_720_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_CAP, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1280 * 720 * 3 * 60, + .ib = 1280 * 720 * 3 * 60 * 1.5, + }, +}; + +static struct msm_bus_vectors vcap_1080_vectors[] = { + { + .src = MSM_BUS_MASTER_VIDEO_CAP, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1920 * 1080 * 3 * 60, + .ib = 1920 * 1080 * 3 * 60 * 1.5, + }, +}; + +static struct msm_bus_paths vcap_bus_usecases[] = { + { + ARRAY_SIZE(vcap_init_vectors), + vcap_init_vectors, + }, + { + ARRAY_SIZE(vcap_480_vectors), + vcap_480_vectors, + }, + { + ARRAY_SIZE(vcap_720_vectors), + vcap_720_vectors, + }, + { + ARRAY_SIZE(vcap_1080_vectors), + vcap_1080_vectors, + }, +}; + +static struct msm_bus_scale_pdata vcap_axi_client_pdata = { + vcap_bus_usecases, + ARRAY_SIZE(vcap_bus_usecases), +}; + +static struct resource msm_vcap_resources[] = { + { + .name = "vcap", + .start = VCAP_HW_BASE, + .end = VCAP_HW_BASE + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "vc_irq", + .start = VCAP_VC, + .end = VCAP_VC, + .flags = IORESOURCE_IRQ, + }, + { + .name = "vp_irq", + .start = VCAP_VP, + .end = VCAP_VP, + .flags = IORESOURCE_IRQ, + }, +}; + +static unsigned vcap_gpios[] = { + 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 80, 82, + 83, 84, 85, 86, 87, +}; + +static struct vcap_platform_data vcap_pdata = { + .gpios = vcap_gpios, + .num_gpios = ARRAY_SIZE(vcap_gpios), + .bus_client_pdata = &vcap_axi_client_pdata +}; + +struct platform_device msm8064_device_vcap = { + .name = "msm_vcap", + .id = 0, + .resource = msm_vcap_resources, + .num_resources = ARRAY_SIZE(msm_vcap_resources), + .dev = { + .platform_data = &vcap_pdata, + }, +}; +#endif + +static struct resource msm_cache_erp_resources[] = { + { + .name = "l1_irq", + .start = SC_SICCPUXEXTFAULTIRPTREQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "l2_irq", + .start = APCC_QGICL2IRPTREQ, + .flags = IORESOURCE_IRQ, + } +}; + +struct platform_device apq8064_device_cache_erp = { + .name = "msm_cache_erp", + .id = -1, + .num_resources = ARRAY_SIZE(msm_cache_erp_resources), + .resource = msm_cache_erp_resources, +}; + +#define MSM_QDSS_PHYS_BASE 0x01A00000 +#define MSM_ETM_PHYS_BASE (MSM_QDSS_PHYS_BASE + 0x1C000) + +#define QDSS_SOURCE(src_name, fpm) { .name = src_name, .fport_mask = fpm, } + +static struct qdss_source msm_qdss_sources[] = { + QDSS_SOURCE("msm_etm", 0x33), + QDSS_SOURCE("msm_oxili", 0x80), +}; + +static struct msm_qdss_platform_data qdss_pdata = { + .src_table = msm_qdss_sources, + .size = ARRAY_SIZE(msm_qdss_sources), + .afamily = 1, +}; + +struct platform_device apq8064_qdss_device = { + .name = "msm_qdss", + .id = -1, + .dev = { + .platform_data = &qdss_pdata, + }, +}; + +static struct resource msm_etm_resources[] = { + { + .start = MSM_ETM_PHYS_BASE, + .end = MSM_ETM_PHYS_BASE + (SZ_4K * 4) - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device apq8064_etm_device = { + .name = "msm_etm", + .id = 0, + .num_resources = ARRAY_SIZE(msm_etm_resources), + .resource = msm_etm_resources, +}; + +struct msm_iommu_domain_name apq8064_iommu_ctx_names[] = { + /* Camera */ + { + .name = "vpe_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vpe_dst", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_imgwr", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_misc", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_dst", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_dst", + .domain = CAMERA_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_src", + .domain = ROTATOR_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_dst", + .domain = ROTATOR_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_mm1", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_b_mm2", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_stream", + .domain = VIDEO_DOMAIN, + }, +}; + +static struct mem_pool apq8064_video_pools[] = { + /* + * Video hardware has the following requirements: + * 1. All video addresses used by the video hardware must be at a higher + * address than video firmware address. + * 2. Video hardware can only access a range of 256MB from the base of + * the video firmware. + */ + [VIDEO_FIRMWARE_POOL] = + /* Low addresses, intended for video firmware */ + { + .paddr = SZ_128K, + .size = SZ_16M - SZ_128K, + }, + [VIDEO_MAIN_POOL] = + /* Main video pool */ + { + .paddr = SZ_16M, + .size = SZ_256M - SZ_16M, + }, + [GEN_POOL] = + /* Remaining address space up to 2G */ + { + .paddr = SZ_256M, + .size = SZ_2G - SZ_256M, + }, +}; + +static struct mem_pool apq8064_camera_pools[] = { + [GEN_POOL] = + /* One address space for camera */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct mem_pool apq8064_display_pools[] = { + [GEN_POOL] = + /* One address space for display */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct mem_pool apq8064_rotator_pools[] = { + [GEN_POOL] = + /* One address space for rotator */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct msm_iommu_domain apq8064_iommu_domains[] = { + [VIDEO_DOMAIN] = { + .iova_pools = apq8064_video_pools, + .npools = ARRAY_SIZE(apq8064_video_pools), + }, + [CAMERA_DOMAIN] = { + .iova_pools = apq8064_camera_pools, + .npools = ARRAY_SIZE(apq8064_camera_pools), + }, + [DISPLAY_DOMAIN] = { + .iova_pools = apq8064_display_pools, + .npools = ARRAY_SIZE(apq8064_display_pools), + }, + [ROTATOR_DOMAIN] = { + .iova_pools = apq8064_rotator_pools, + .npools = ARRAY_SIZE(apq8064_rotator_pools), + }, +}; + +struct iommu_domains_pdata apq8064_iommu_domain_pdata = { + .domains = apq8064_iommu_domains, + .ndomains = ARRAY_SIZE(apq8064_iommu_domains), + .domain_names = apq8064_iommu_ctx_names, + .nnames = ARRAY_SIZE(apq8064_iommu_ctx_names), + .domain_alloc_flags = 0, +}; + +struct platform_device apq8064_iommu_domain_device = { + .name = "iommu_domains", + .id = -1, + .dev = { + .platform_data = &apq8064_iommu_domain_pdata, + } +}; + +struct msm_rtb_platform_data apq8064_rtb_pdata = { + .size = SZ_1M, +}; + +static int __init msm_rtb_set_buffer_size(char *p) +{ + int s; + + s = memparse(p, NULL); + apq8064_rtb_pdata.size = ALIGN(s, SZ_4K); + return 0; +} +early_param("msm_rtb_size", msm_rtb_set_buffer_size); + +struct platform_device apq8064_rtb_device = { + .name = "msm_rtb", + .id = -1, + .dev = { + .platform_data = &apq8064_rtb_pdata, + }, +}; + +#define APQ8064_L1_SIZE SZ_1M +/* + * The actual L2 size is smaller but we need a larger buffer + * size to store other dump information + */ +#define APQ8064_L2_SIZE SZ_8M + +struct msm_cache_dump_platform_data apq8064_cache_dump_pdata = { + .l2_size = APQ8064_L2_SIZE, + .l1_size = APQ8064_L1_SIZE, +}; + +struct platform_device apq8064_cache_dump_device = { + .name = "msm_cache_dump", + .id = -1, + .dev = { + .platform_data = &apq8064_cache_dump_pdata, + }, +}; diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c new file mode 100644 index 00000000000..c480bba50b1 --- /dev/null +++ b/arch/arm/mach-msm/devices-8930.c @@ -0,0 +1,903 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "rpm_log.h" +#include "rpm_stats.h" +#include "footswitch.h" + +#ifdef CONFIG_MSM_MPM +#include +#endif + +struct msm_rpm_platform_data msm8930_rpm_data __initdata = { + .reg_base_addrs = { + [MSM_RPM_PAGE_STATUS] = MSM_RPM_BASE, + [MSM_RPM_PAGE_CTRL] = MSM_RPM_BASE + 0x400, + [MSM_RPM_PAGE_REQ] = MSM_RPM_BASE + 0x600, + [MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00, + }, + .irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ, + .irq_err = RPM_APCC_CPU0_GP_LOW_IRQ, + .irq_wakeup = RPM_APCC_CPU0_WAKE_UP_IRQ, + .ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008, + .ipc_rpm_val = 4, + .target_id = { + MSM_RPM_MAP(8930, NOTIFICATION_CONFIGURED_0, NOTIFICATION, 4), + MSM_RPM_MAP(8930, NOTIFICATION_REGISTERED_0, NOTIFICATION, 4), + MSM_RPM_MAP(8930, INVALIDATE_0, INVALIDATE, 8), + MSM_RPM_MAP(8960, TRIGGER_TIMED_TO, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8960, TRIGGER_TIMED_SCLK_COUNT, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8930, RPM_CTL, RPM_CTL, 1), + MSM_RPM_MAP(8930, CXO_CLK, CXO_CLK, 1), + MSM_RPM_MAP(8930, PXO_CLK, PXO_CLK, 1), + MSM_RPM_MAP(8930, APPS_FABRIC_CLK, APPS_FABRIC_CLK, 1), + MSM_RPM_MAP(8930, SYSTEM_FABRIC_CLK, SYSTEM_FABRIC_CLK, 1), + MSM_RPM_MAP(8930, MM_FABRIC_CLK, MM_FABRIC_CLK, 1), + MSM_RPM_MAP(8930, DAYTONA_FABRIC_CLK, DAYTONA_FABRIC_CLK, 1), + MSM_RPM_MAP(8930, SFPB_CLK, SFPB_CLK, 1), + MSM_RPM_MAP(8930, CFPB_CLK, CFPB_CLK, 1), + MSM_RPM_MAP(8930, MMFPB_CLK, MMFPB_CLK, 1), + MSM_RPM_MAP(8930, EBI1_CLK, EBI1_CLK, 1), + MSM_RPM_MAP(8930, APPS_FABRIC_CFG_HALT_0, + APPS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8930, APPS_FABRIC_CFG_CLKMOD_0, + APPS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8930, APPS_FABRIC_CFG_IOCTL, + APPS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8930, APPS_FABRIC_ARB_0, APPS_FABRIC_ARB, 6), + MSM_RPM_MAP(8930, SYS_FABRIC_CFG_HALT_0, + SYS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8930, SYS_FABRIC_CFG_CLKMOD_0, + SYS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8930, SYS_FABRIC_CFG_IOCTL, + SYS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8930, SYSTEM_FABRIC_ARB_0, + SYSTEM_FABRIC_ARB, 20), + MSM_RPM_MAP(8930, MMSS_FABRIC_CFG_HALT_0, + MMSS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8930, MMSS_FABRIC_CFG_CLKMOD_0, + MMSS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8930, MMSS_FABRIC_CFG_IOCTL, + MMSS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8930, MM_FABRIC_ARB_0, MM_FABRIC_ARB, 11), + MSM_RPM_MAP(8930, PM8038_S1_0, PM8038_S1, 2), + MSM_RPM_MAP(8930, PM8038_S2_0, PM8038_S2, 2), + MSM_RPM_MAP(8930, PM8038_S3_0, PM8038_S3, 2), + MSM_RPM_MAP(8930, PM8038_S4_0, PM8038_S4, 2), + MSM_RPM_MAP(8930, PM8038_S5_0, PM8038_S5, 2), + MSM_RPM_MAP(8930, PM8038_S6_0, PM8038_S6, 2), + MSM_RPM_MAP(8930, PM8038_L1_0, PM8038_L1, 2), + MSM_RPM_MAP(8930, PM8038_L2_0, PM8038_L2, 2), + MSM_RPM_MAP(8930, PM8038_L3_0, PM8038_L3, 2), + MSM_RPM_MAP(8930, PM8038_L4_0, PM8038_L4, 2), + MSM_RPM_MAP(8930, PM8038_L5_0, PM8038_L5, 2), + MSM_RPM_MAP(8930, PM8038_L6_0, PM8038_L6, 2), + MSM_RPM_MAP(8930, PM8038_L7_0, PM8038_L7, 2), + MSM_RPM_MAP(8930, PM8038_L8_0, PM8038_L8, 2), + MSM_RPM_MAP(8930, PM8038_L9_0, PM8038_L9, 2), + MSM_RPM_MAP(8930, PM8038_L10_0, PM8038_L10, 2), + MSM_RPM_MAP(8930, PM8038_L11_0, PM8038_L11, 2), + MSM_RPM_MAP(8930, PM8038_L12_0, PM8038_L12, 2), + MSM_RPM_MAP(8930, PM8038_L13_0, PM8038_L13, 2), + MSM_RPM_MAP(8930, PM8038_L14_0, PM8038_L14, 2), + MSM_RPM_MAP(8930, PM8038_L15_0, PM8038_L15, 2), + MSM_RPM_MAP(8930, PM8038_L16_0, PM8038_L16, 2), + MSM_RPM_MAP(8930, PM8038_L17_0, PM8038_L17, 2), + MSM_RPM_MAP(8930, PM8038_L18_0, PM8038_L18, 2), + MSM_RPM_MAP(8930, PM8038_L19_0, PM8038_L19, 2), + MSM_RPM_MAP(8930, PM8038_L20_0, PM8038_L20, 2), + MSM_RPM_MAP(8930, PM8038_L21_0, PM8038_L21, 2), + MSM_RPM_MAP(8930, PM8038_L22_0, PM8038_L22, 2), + MSM_RPM_MAP(8930, PM8038_L23_0, PM8038_L23, 2), + MSM_RPM_MAP(8930, PM8038_L24_0, PM8038_L24, 2), + MSM_RPM_MAP(8930, PM8038_L25_0, PM8038_L25, 2), + MSM_RPM_MAP(8930, PM8038_L26_0, PM8038_L26, 2), + MSM_RPM_MAP(8930, PM8038_L27_0, PM8038_L27, 2), + MSM_RPM_MAP(8930, PM8038_CLK1_0, PM8038_CLK1, 2), + MSM_RPM_MAP(8930, PM8038_CLK2_0, PM8038_CLK2, 2), + MSM_RPM_MAP(8930, PM8038_LVS1, PM8038_LVS1, 1), + MSM_RPM_MAP(8930, PM8038_LVS2, PM8038_LVS2, 1), + MSM_RPM_MAP(8930, NCP_0, NCP, 2), + MSM_RPM_MAP(8930, CXO_BUFFERS, CXO_BUFFERS, 1), + MSM_RPM_MAP(8930, USB_OTG_SWITCH, USB_OTG_SWITCH, 1), + MSM_RPM_MAP(8930, HDMI_SWITCH, HDMI_SWITCH, 1), + MSM_RPM_MAP(8930, QDSS_CLK, QDSS_CLK, 1), + MSM_RPM_MAP(8930, VOLTAGE_CORNER, VOLTAGE_CORNER, 1), + }, + .target_status = { + MSM_RPM_STATUS_ID_MAP(8930, VERSION_MAJOR), + MSM_RPM_STATUS_ID_MAP(8930, VERSION_MINOR), + MSM_RPM_STATUS_ID_MAP(8930, VERSION_BUILD), + MSM_RPM_STATUS_ID_MAP(8930, SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8930, SUPPORTED_RESOURCES_1), + MSM_RPM_STATUS_ID_MAP(8930, SUPPORTED_RESOURCES_2), + MSM_RPM_STATUS_ID_MAP(8930, RESERVED_SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8930, SEQUENCE), + MSM_RPM_STATUS_ID_MAP(8930, RPM_CTL), + MSM_RPM_STATUS_ID_MAP(8930, CXO_CLK), + MSM_RPM_STATUS_ID_MAP(8930, PXO_CLK), + MSM_RPM_STATUS_ID_MAP(8930, APPS_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8930, SYSTEM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8930, MM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8930, DAYTONA_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8930, SFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8930, CFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8930, MMFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8930, EBI1_CLK), + MSM_RPM_STATUS_ID_MAP(8930, APPS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8930, APPS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8930, APPS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8930, APPS_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8930, SYS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8930, SYS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8930, SYS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8930, SYSTEM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8930, MMSS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8930, MMSS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8930, MMSS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8930, MM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S1_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S1_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S2_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S2_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S3_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S3_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S4_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_S4_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L1_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L1_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L2_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L2_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L3_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L3_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L4_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L4_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L5_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L5_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L6_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L6_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L7_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L7_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L8_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L8_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L9_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L9_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L10_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L10_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L11_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L11_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L12_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L12_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L13_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L13_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L14_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L14_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L15_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L15_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L16_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L16_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L17_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L17_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L18_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L18_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L19_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L19_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L20_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L20_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L21_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L21_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L22_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L22_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L23_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L23_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L24_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L24_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L25_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_L25_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_CLK1_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_CLK1_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_CLK2_0), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_CLK2_1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_LVS1), + MSM_RPM_STATUS_ID_MAP(8930, PM8038_LVS2), + MSM_RPM_STATUS_ID_MAP(8930, NCP_0), + MSM_RPM_STATUS_ID_MAP(8930, NCP_1), + MSM_RPM_STATUS_ID_MAP(8930, CXO_BUFFERS), + MSM_RPM_STATUS_ID_MAP(8930, USB_OTG_SWITCH), + MSM_RPM_STATUS_ID_MAP(8930, HDMI_SWITCH), + MSM_RPM_STATUS_ID_MAP(8930, QDSS_CLK), + MSM_RPM_STATUS_ID_MAP(8930, VOLTAGE_CORNER), + }, + .target_ctrl_id = { + MSM_RPM_CTRL_MAP(8930, VERSION_MAJOR), + MSM_RPM_CTRL_MAP(8930, VERSION_MINOR), + MSM_RPM_CTRL_MAP(8930, VERSION_BUILD), + MSM_RPM_CTRL_MAP(8930, REQ_CTX_0), + MSM_RPM_CTRL_MAP(8930, REQ_SEL_0), + MSM_RPM_CTRL_MAP(8930, ACK_CTX_0), + MSM_RPM_CTRL_MAP(8930, ACK_SEL_0), + }, + .sel_invalidate = MSM_RPM_8930_SEL_INVALIDATE, + .sel_notification = MSM_RPM_8930_SEL_NOTIFICATION, + .sel_last = MSM_RPM_8930_SEL_LAST, + .ver = {3, 0, 0}, +}; + +struct platform_device msm8930_rpm_device = { + .name = "msm_rpm", + .id = -1, +}; + +static struct msm_rpm_log_platform_data msm_rpm_log_pdata = { + .phys_addr_base = 0x0010C000, + .reg_offsets = { + [MSM_RPM_LOG_PAGE_INDICES] = 0x00000080, + [MSM_RPM_LOG_PAGE_BUFFER] = 0x000000A0, + }, + .phys_size = SZ_8K, + .log_len = 4096, /* log's buffer length in bytes */ + .log_len_mask = (4096 >> 2) - 1, /* length mask in units of u32 */ +}; + +struct platform_device msm8930_rpm_log_device = { + .name = "msm_rpm_log", + .id = -1, + .dev = { + .platform_data = &msm_rpm_log_pdata, + }, +}; + +static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = { + .phys_addr_base = 0x0010D204, + .phys_size = SZ_8K, +}; + +struct platform_device msm8930_rpm_stat_device = { + .name = "msm_rpm_stat", + .id = -1, + .dev = { + .platform_data = &msm_rpm_stat_pdata, + }, +}; + +static int msm8930_LPM_latency = 1000; /* >100 usec for WFI */ + +struct platform_device msm8930_cpu_idle_device = { + .name = "msm_cpu_idle", + .id = -1, + .dev = { + .platform_data = &msm8930_LPM_latency, + }, +}; + +static struct msm_dcvs_freq_entry msm8930_freq[] = { + { 384000, 166981, 345600}, + { 702000, 213049, 632502}, + {1026000, 285712, 925613}, + {1242000, 383945, 1176550}, + {1458000, 419729, 1465478}, + {1512000, 434116, 1546674}, + +}; + +static struct msm_dcvs_core_info msm8930_core_info = { + .freq_tbl = &msm8930_freq[0], + .core_param = { + .max_time_us = 100000, + .num_freq = ARRAY_SIZE(msm8930_freq), + }, + .algo_param = { + .slack_time_us = 58000, + .scale_slack_time = 0, + .scale_slack_time_pct = 0, + .disable_pc_threshold = 1458000, + .em_window_size = 100000, + .em_max_util_pct = 97, + .ss_window_size = 1000000, + .ss_util_pct = 95, + .ss_iobusy_conv = 100, + }, +}; + +struct platform_device msm8930_msm_gov_device = { + .name = "msm_dcvs_gov", + .id = -1, + .dev = { + .platform_data = &msm8930_core_info, + }, +}; + +struct platform_device msm_bus_8930_sys_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM, +}; +struct platform_device msm_bus_8930_apps_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_APPSS, +}; +struct platform_device msm_bus_8930_mm_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_MMSS, +}; +struct platform_device msm_bus_8930_sys_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM_FPB, +}; +struct platform_device msm_bus_8930_cpss_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_CPSS_FPB, +}; + +static struct fs_driver_data gfx3d_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk", .reset_rate = 27000000 }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D, +}; + +static struct fs_driver_data ijpeg_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_JPEG_ENC, +}; + +static struct fs_driver_data mdp_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { .name = "vsync_clk" }, + { .name = "lut_clk" }, + { .name = "tv_src_clk" }, + { .name = "tv_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_MDP_PORT0, + .bus_port1 = MSM_BUS_MASTER_MDP_PORT1, +}; + +static struct fs_driver_data rot_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_ROTATOR, +}; + +static struct fs_driver_data ved_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_HD_CODEC_PORT0, + .bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1, +}; + +static struct fs_driver_data vfe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VFE, +}; + +static struct fs_driver_data vpe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VPE, +}; + +struct platform_device *msm8930_footswitch[] __initdata = { + FS_8X60(FS_MDP, "vdd", "mdp.0", &mdp_fs_data), + FS_8X60(FS_ROT, "vdd", "msm_rotator.0", &rot_fs_data), + FS_8X60(FS_IJPEG, "vdd", "msm_gemini.0", &ijpeg_fs_data), + FS_8X60(FS_VFE, "fs_vfe", NULL, &vfe_fs_data), + FS_8X60(FS_VPE, "fs_vpe", NULL, &vpe_fs_data), + FS_8X60(FS_GFX3D, "vdd", "kgsl-3d0.0", &gfx3d_fs_data), + FS_8X60(FS_VED, "vdd", "msm_vidc.0", &ved_fs_data), +}; +unsigned msm8930_num_footswitch __initdata = ARRAY_SIZE(msm8930_footswitch); + +/* MSM Video core device */ +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors vidc_init_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; +static struct msm_bus_vectors vidc_venc_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 54525952, + .ib = 436207616, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 72351744, + .ib = 289406976, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 40894464, + .ib = 327155712, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 48234496, + .ib = 192937984, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, +}; +static struct msm_bus_vectors vidc_venc_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 163577856, + .ib = 1308622848, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 219152384, + .ib = 876609536, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, +}; +static struct msm_bus_vectors vidc_vdec_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 121634816, + .ib = 973078528, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 155189248, + .ib = 620756992, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, +}; +static struct msm_bus_vectors vidc_venc_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 372244480, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 501219328, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 222298112, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 330301440, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 700000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 10000000, + }, +}; + +static struct msm_bus_paths vidc_bus_client_config[] = { + { + ARRAY_SIZE(vidc_init_vectors), + vidc_init_vectors, + }, + { + ARRAY_SIZE(vidc_venc_vga_vectors), + vidc_venc_vga_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_vga_vectors), + vidc_vdec_vga_vectors, + }, + { + ARRAY_SIZE(vidc_venc_720p_vectors), + vidc_venc_720p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_720p_vectors), + vidc_vdec_720p_vectors, + }, + { + ARRAY_SIZE(vidc_venc_1080p_vectors), + vidc_venc_1080p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_1080p_vectors), + vidc_vdec_1080p_vectors, + }, +}; + +static struct msm_bus_scale_pdata vidc_bus_client_data = { + vidc_bus_client_config, + ARRAY_SIZE(vidc_bus_client_config), + .name = "vidc", +}; +#endif + +#define MSM_VIDC_BASE_PHYS 0x04400000 +#define MSM_VIDC_BASE_SIZE 0x00100000 + +static struct resource apq8930_device_vidc_resources[] = { + { + .start = MSM_VIDC_BASE_PHYS, + .end = MSM_VIDC_BASE_PHYS + MSM_VIDC_BASE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = VCODEC_IRQ, + .end = VCODEC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_vidc_platform_data apq8930_vidc_platform_data = { +#ifdef CONFIG_MSM_BUS_SCALING + .vidc_bus_client_pdata = &vidc_bus_client_data, +#endif +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .memtype = ION_CP_MM_HEAP_ID, + .enable_ion = 1, + .cp_enabled = 1, +#else + .memtype = MEMTYPE_EBI1, + .enable_ion = 0, +#endif + .disable_dmx = 1, + .disable_fullhd = 0, +}; + +struct platform_device apq8930_msm_device_vidc = { + .name = "msm_vidc", + .id = 0, + .num_resources = ARRAY_SIZE(apq8930_device_vidc_resources), + .resource = apq8930_device_vidc_resources, + .dev = { + .platform_data = &apq8930_vidc_platform_data, + }, +}; + +struct platform_device *vidc_device[] __initdata = { + &apq8930_msm_device_vidc +}; + +void __init msm8930_add_vidc_device(void) +{ + if (cpu_is_msm8627()) { + struct msm_vidc_platform_data *pdata; + pdata = (struct msm_vidc_platform_data *) + apq8930_msm_device_vidc.dev.platform_data; + pdata->disable_fullhd = 1; + } + platform_add_devices(vidc_device, ARRAY_SIZE(vidc_device)); +} + +struct msm_iommu_domain_name msm8930_iommu_ctx_names[] = { + /* Camera */ + { + .name = "vpe_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vpe_dst", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_imgwr", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_misc", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_dst", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_dst", + .domain = CAMERA_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_src", + .domain = ROTATOR_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_dst", + .domain = ROTATOR_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_mm1", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_b_mm2", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_stream", + .domain = VIDEO_DOMAIN, + }, +}; + +static struct mem_pool msm8930_video_pools[] = { + /* + * Video hardware has the following requirements: + * 1. All video addresses used by the video hardware must be at a higher + * address than video firmware address. + * 2. Video hardware can only access a range of 256MB from the base of + * the video firmware. + */ + [VIDEO_FIRMWARE_POOL] = + /* Low addresses, intended for video firmware */ + { + .paddr = SZ_128K, + .size = SZ_16M - SZ_128K, + }, + [VIDEO_MAIN_POOL] = + /* Main video pool */ + { + .paddr = SZ_16M, + .size = SZ_256M - SZ_16M, + }, + [GEN_POOL] = + /* Remaining address space up to 2G */ + { + .paddr = SZ_256M, + .size = SZ_2G - SZ_256M, + }, +}; + +static struct mem_pool msm8930_camera_pools[] = { + [GEN_POOL] = + /* One address space for camera */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct mem_pool msm8930_display_pools[] = { + [GEN_POOL] = + /* One address space for display */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct mem_pool msm8930_rotator_pools[] = { + [GEN_POOL] = + /* One address space for rotator */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct msm_iommu_domain msm8930_iommu_domains[] = { + [VIDEO_DOMAIN] = { + .iova_pools = msm8930_video_pools, + .npools = ARRAY_SIZE(msm8930_video_pools), + }, + [CAMERA_DOMAIN] = { + .iova_pools = msm8930_camera_pools, + .npools = ARRAY_SIZE(msm8930_camera_pools), + }, + [DISPLAY_DOMAIN] = { + .iova_pools = msm8930_display_pools, + .npools = ARRAY_SIZE(msm8930_display_pools), + }, + [ROTATOR_DOMAIN] = { + .iova_pools = msm8930_rotator_pools, + .npools = ARRAY_SIZE(msm8930_rotator_pools), + }, +}; + +struct iommu_domains_pdata msm8930_iommu_domain_pdata = { + .domains = msm8930_iommu_domains, + .ndomains = ARRAY_SIZE(msm8930_iommu_domains), + .domain_names = msm8930_iommu_ctx_names, + .nnames = ARRAY_SIZE(msm8930_iommu_ctx_names), + .domain_alloc_flags = 0, +}; + +struct platform_device msm8930_iommu_domain_device = { + .name = "iommu_domains", + .id = -1, + .dev = { + .platform_data = &msm8930_iommu_domain_pdata, + } +}; + +struct msm_rtb_platform_data msm8930_rtb_pdata = { + .size = SZ_1M, +}; + +static int __init msm_rtb_set_buffer_size(char *p) +{ + int s; + + s = memparse(p, NULL); + msm8930_rtb_pdata.size = ALIGN(s, SZ_4K); + return 0; +} +early_param("msm_rtb_size", msm_rtb_set_buffer_size); + + +struct platform_device msm8930_rtb_device = { + .name = "msm_rtb", + .id = -1, + .dev = { + .platform_data = &msm8930_rtb_pdata, + }, +}; diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c new file mode 100644 index 00000000000..db55d143434 --- /dev/null +++ b/arch/arm/mach-msm/devices-8960.c @@ -0,0 +1,3817 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "devices.h" +#include "devices-msm8x60.h" +#include "footswitch.h" +#include "msm_watchdog.h" +#include "rpm_log.h" +#include "rpm_stats.h" +#include "pil-q6v4.h" +#include "scm-pas.h" +#include +#include + +#ifdef CONFIG_MSM_MPM +#include +#endif +#ifdef CONFIG_MSM_DSPS +#include +#endif + + +/* Address of GSBI blocks */ +#define MSM_GSBI1_PHYS 0x16000000 +#define MSM_GSBI2_PHYS 0x16100000 +#define MSM_GSBI3_PHYS 0x16200000 +#define MSM_GSBI4_PHYS 0x16300000 +#define MSM_GSBI5_PHYS 0x16400000 +#define MSM_GSBI6_PHYS 0x16500000 +#define MSM_GSBI7_PHYS 0x16600000 +#define MSM_GSBI8_PHYS 0x1A000000 +#define MSM_GSBI9_PHYS 0x1A100000 +#define MSM_GSBI10_PHYS 0x1A200000 +#define MSM_GSBI11_PHYS 0x12440000 +#define MSM_GSBI12_PHYS 0x12480000 + +#define MSM_UART2DM_PHYS (MSM_GSBI2_PHYS + 0x40000) +#define MSM_UART5DM_PHYS (MSM_GSBI5_PHYS + 0x40000) +#define MSM_UART6DM_PHYS (MSM_GSBI6_PHYS + 0x40000) +#define MSM_UART8DM_PHYS (MSM_GSBI8_PHYS + 0x40000) +#define MSM_UART9DM_PHYS (MSM_GSBI9_PHYS + 0x40000) + +/* GSBI QUP devices */ +#define MSM_GSBI1_QUP_PHYS (MSM_GSBI1_PHYS + 0x80000) +#define MSM_GSBI2_QUP_PHYS (MSM_GSBI2_PHYS + 0x80000) +#define MSM_GSBI3_QUP_PHYS (MSM_GSBI3_PHYS + 0x80000) +#define MSM_GSBI4_QUP_PHYS (MSM_GSBI4_PHYS + 0x80000) +#define MSM_GSBI5_QUP_PHYS (MSM_GSBI5_PHYS + 0x80000) +#define MSM_GSBI6_QUP_PHYS (MSM_GSBI6_PHYS + 0x80000) +#define MSM_GSBI7_QUP_PHYS (MSM_GSBI7_PHYS + 0x80000) +#define MSM_GSBI8_QUP_PHYS (MSM_GSBI8_PHYS + 0x80000) +#define MSM_GSBI9_QUP_PHYS (MSM_GSBI9_PHYS + 0x80000) +#define MSM_GSBI10_QUP_PHYS (MSM_GSBI10_PHYS + 0x80000) +#define MSM_GSBI11_QUP_PHYS (MSM_GSBI11_PHYS + 0x20000) +#define MSM_GSBI12_QUP_PHYS (MSM_GSBI12_PHYS + 0x20000) +#define MSM_QUP_SIZE SZ_4K + +#define MSM_PMIC1_SSBI_CMD_PHYS 0x00500000 +#define MSM_PMIC2_SSBI_CMD_PHYS 0x00C00000 +#define MSM_PMIC_SSBI_SIZE SZ_4K + +#define MSM8960_HSUSB_PHYS 0x12500000 +#define MSM8960_HSUSB_SIZE SZ_4K + +static struct resource resources_otg[] = { + { + .start = MSM8960_HSUSB_PHYS, + .end = MSM8960_HSUSB_PHYS + MSM8960_HSUSB_SIZE, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_hsusb[] = { + { + .start = MSM8960_HSUSB_PHYS, + .end = MSM8960_HSUSB_PHYS + MSM8960_HSUSB_SIZE, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb), + .resource = resources_hsusb, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM8960_HSUSB_PHYS, + .end = MSM8960_HSUSB_PHYS + MSM8960_HSUSB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = DMA_BIT_MASK(32); +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_hsic_host[] = { + { + .start = 0x12520000, + .end = 0x12520000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB_HSIC_IRQ, + .end = USB_HSIC_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_GPIO_TO_INT(69), + .end = MSM_GPIO_TO_INT(69), + .name = "peripheral_status_irq", + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsic_host = { + .name = "msm_hsic_host", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsic_host), + .resource = resources_hsic_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define SHARED_IMEM_TZ_BASE 0x2a03f720 +static struct resource tzlog_resources[] = { + { + .start = SHARED_IMEM_TZ_BASE, + .end = SHARED_IMEM_TZ_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_tz_log = { + .name = "tz_log", + .id = 0, + .num_resources = ARRAY_SIZE(tzlog_resources), + .resource = tzlog_resources, +}; + +static struct resource resources_uart_gsbi2[] = { + { + .start = MSM8960_GSBI2_UARTDM_IRQ, + .end = MSM8960_GSBI2_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI2_PHYS, + .end = MSM_GSBI2_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8960_device_uart_gsbi2 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart_gsbi2), + .resource = resources_uart_gsbi2, +}; +/* GSBI 6 used into UARTDM Mode */ +static struct resource msm_uart_dm6_resources[] = { + { + .start = MSM_UART6DM_PHYS, + .end = MSM_UART6DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = GSBI6_UARTDM_IRQ, + .end = GSBI6_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_GSBI6_PHYS, + .end = MSM_GSBI6_PHYS + 4 - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = DMOV_HSUART_GSBI6_TX_CHAN, + .end = DMOV_HSUART_GSBI6_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART_GSBI6_TX_CRCI, + .end = DMOV_HSUART_GSBI6_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; +static u64 msm_uart_dm6_dma_mask = DMA_BIT_MASK(32); +struct platform_device msm_device_uart_dm6 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart_dm6_resources), + .resource = msm_uart_dm6_resources, + .dev = { + .dma_mask = &msm_uart_dm6_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +/* + * GSBI 9 used into UARTDM Mode + * For 8960 Fusion 2.2 Primary IPC + */ +static struct resource msm_uart_dm9_resources[] = { + { + .start = MSM_UART9DM_PHYS, + .end = MSM_UART9DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = GSBI9_UARTDM_IRQ, + .end = GSBI9_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_GSBI9_PHYS, + .end = MSM_GSBI9_PHYS + 4 - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = DMOV_HSUART_GSBI9_TX_CHAN, + .end = DMOV_HSUART_GSBI9_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART_GSBI9_TX_CRCI, + .end = DMOV_HSUART_GSBI9_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; +static u64 msm_uart_dm9_dma_mask = DMA_BIT_MASK(32); +struct platform_device msm_device_uart_dm9 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart_dm9_resources), + .resource = msm_uart_dm9_resources, + .dev = { + .dma_mask = &msm_uart_dm9_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource resources_uart_gsbi5[] = { + { + .start = GSBI5_UARTDM_IRQ, + .end = GSBI5_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART5DM_PHYS, + .end = MSM_UART5DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI5_PHYS, + .end = MSM_GSBI5_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8960_device_uart_gsbi5 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart_gsbi5), + .resource = resources_uart_gsbi5, +}; + +static struct msm_serial_hslite_platform_data uart_gsbi8_pdata = { + .line = 0, +}; + +static struct resource resources_uart_gsbi8[] = { + { + .start = GSBI8_UARTDM_IRQ, + .end = GSBI8_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART8DM_PHYS, + .end = MSM_UART8DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI8_PHYS, + .end = MSM_GSBI8_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8960_device_uart_gsbi8 = { + .name = "msm_serial_hsl", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart_gsbi8), + .resource = resources_uart_gsbi8, + .dev.platform_data = &uart_gsbi8_pdata, +}; + +/* MSM Video core device */ +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors vidc_init_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; +static struct msm_bus_vectors vidc_venc_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 54525952, + .ib = 436207616, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 72351744, + .ib = 289406976, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 40894464, + .ib = 327155712, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 48234496, + .ib = 192937984, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, +}; +static struct msm_bus_vectors vidc_venc_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 163577856, + .ib = 1308622848, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 219152384, + .ib = 876609536, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, +}; +static struct msm_bus_vectors vidc_vdec_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 121634816, + .ib = 973078528, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 155189248, + .ib = 620756992, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, +}; +static struct msm_bus_vectors vidc_venc_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 372244480, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 501219328, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 222298112, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 330301440, + .ib = 2560000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 700000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 10000000, + }, +}; +static struct msm_bus_vectors vidc_venc_1080p_turbo_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 222298112, + .ib = 3522000000U, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 330301440, + .ib = 3522000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 700000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 10000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_1080p_turbo_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 222298112, + .ib = 3522000000U, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 330301440, + .ib = 3522000000U, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 700000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 10000000, + }, +}; + +static struct msm_bus_paths vidc_bus_client_config[] = { + { + ARRAY_SIZE(vidc_init_vectors), + vidc_init_vectors, + }, + { + ARRAY_SIZE(vidc_venc_vga_vectors), + vidc_venc_vga_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_vga_vectors), + vidc_vdec_vga_vectors, + }, + { + ARRAY_SIZE(vidc_venc_720p_vectors), + vidc_venc_720p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_720p_vectors), + vidc_vdec_720p_vectors, + }, + { + ARRAY_SIZE(vidc_venc_1080p_vectors), + vidc_venc_1080p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_1080p_vectors), + vidc_vdec_1080p_vectors, + }, + { + ARRAY_SIZE(vidc_venc_1080p_turbo_vectors), + vidc_vdec_1080p_turbo_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_1080p_turbo_vectors), + vidc_vdec_1080p_turbo_vectors, + }, +}; + +static struct msm_bus_scale_pdata vidc_bus_client_data = { + vidc_bus_client_config, + ARRAY_SIZE(vidc_bus_client_config), + .name = "vidc", +}; +#endif + +#ifdef CONFIG_HW_RANDOM_MSM +/* PRNG device */ +#define MSM_PRNG_PHYS 0x1A500000 +static struct resource rng_resources = { + .flags = IORESOURCE_MEM, + .start = MSM_PRNG_PHYS, + .end = MSM_PRNG_PHYS + SZ_512 - 1, +}; + +struct platform_device msm_device_rng = { + .name = "msm_rng", + .id = 0, + .num_resources = 1, + .resource = &rng_resources, +}; +#endif + +#define MSM_VIDC_BASE_PHYS 0x04400000 +#define MSM_VIDC_BASE_SIZE 0x00100000 + +static struct resource msm_device_vidc_resources[] = { + { + .start = MSM_VIDC_BASE_PHYS, + .end = MSM_VIDC_BASE_PHYS + MSM_VIDC_BASE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = VCODEC_IRQ, + .end = VCODEC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_vidc_platform_data vidc_platform_data = { +#ifdef CONFIG_MSM_BUS_SCALING + .vidc_bus_client_pdata = &vidc_bus_client_data, +#endif +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .memtype = ION_CP_MM_HEAP_ID, + .enable_ion = 1, + .cp_enabled = 1, +#else + .memtype = MEMTYPE_EBI1, + .enable_ion = 0, +#endif + .disable_dmx = 0, + .disable_fullhd = 0, + .cont_mode_dpb_count = 18, +}; + +struct platform_device msm_device_vidc = { + .name = "msm_vidc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_device_vidc_resources), + .resource = msm_device_vidc_resources, + .dev = { + .platform_data = &vidc_platform_data, + }, +}; + +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC1_DML_BASE (MSM_SDC1_BASE + 0x800) +#define MSM_SDC1_BAM_BASE (MSM_SDC1_BASE + 0x2000) +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC2_DML_BASE (MSM_SDC2_BASE + 0x800) +#define MSM_SDC2_BAM_BASE (MSM_SDC2_BASE + 0x2000) +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC3_DML_BASE (MSM_SDC3_BASE + 0x800) +#define MSM_SDC3_BAM_BASE (MSM_SDC3_BASE + 0x2000) +#define MSM_SDC4_BASE 0x121C0000 +#define MSM_SDC4_DML_BASE (MSM_SDC4_BASE + 0x800) +#define MSM_SDC4_BAM_BASE (MSM_SDC4_BASE + 0x2000) +#define MSM_SDC5_BASE 0x12200000 +#define MSM_SDC5_DML_BASE (MSM_SDC5_BASE + 0x800) +#define MSM_SDC5_BAM_BASE (MSM_SDC5_BASE + 0x2000) + +static struct resource resources_sdc1[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC1_IRQ_0, + .end = SDC1_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC1_DML_BASE, + .end = MSM_SDC1_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC1_BAM_BASE, + .end = MSM_SDC1_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC1_BAM_IRQ, + .end = SDC1_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc2[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC2_IRQ_0, + .end = SDC2_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC2_DML_BASE, + .end = MSM_SDC2_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC2_BAM_BASE, + .end = MSM_SDC2_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC2_BAM_IRQ, + .end = SDC2_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc3[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC3_IRQ_0, + .end = SDC3_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC3_DML_BASE, + .end = MSM_SDC3_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC3_BAM_BASE, + .end = MSM_SDC3_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC3_BAM_IRQ, + .end = SDC3_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc4[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC4_IRQ_0, + .end = SDC4_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC4_DML_BASE, + .end = MSM_SDC4_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC4_BAM_BASE, + .end = MSM_SDC4_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC4_BAM_IRQ, + .end = SDC4_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc5[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC5_BASE, + .end = MSM_SDC5_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC5_IRQ_0, + .end = SDC5_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC5_DML_BASE, + .end = MSM_SDC5_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC5_BAM_BASE, + .end = MSM_SDC5_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC5_BAM_IRQ, + .end = SDC5_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc5 = { + .name = "msm_sdcc", + .id = 5, + .num_resources = ARRAY_SIZE(resources_sdc5), + .resource = resources_sdc5, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +#define MSM_LPASS_QDSP6SS_PHYS 0x28800000 +#define SFAB_LPASS_Q6_ACLK_CTL (MSM_CLK_CTL_BASE + 0x23A0) + +static struct resource msm_8960_q6_lpass_resources[] = { + { + .start = MSM_LPASS_QDSP6SS_PHYS, + .end = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct pil_q6v4_pdata msm_8960_q6_lpass_data = { + .strap_tcm_base = 0x01460000, + .strap_ahb_upper = 0x00290000, + .strap_ahb_lower = 0x00000280, + .aclk_reg = SFAB_LPASS_Q6_ACLK_CTL, + .name = "q6", + .pas_id = PAS_Q6, + .bus_port = MSM_BUS_MASTER_LPASS_PROC, +}; + +struct platform_device msm_8960_q6_lpass = { + .name = "pil_qdsp6v4", + .id = 0, + .num_resources = ARRAY_SIZE(msm_8960_q6_lpass_resources), + .resource = msm_8960_q6_lpass_resources, + .dev.platform_data = &msm_8960_q6_lpass_data, +}; + +#define MSM_MSS_ENABLE_PHYS 0x08B00000 +#define MSM_FW_QDSP6SS_PHYS 0x08800000 +#define MSS_Q6FW_JTAG_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C6C) +#define SFAB_MSS_Q6_FW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2044) + +static struct resource msm_8960_q6_mss_fw_resources[] = { + { + .start = MSM_FW_QDSP6SS_PHYS, + .end = MSM_FW_QDSP6SS_PHYS + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_MSS_ENABLE_PHYS, + .end = MSM_MSS_ENABLE_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct pil_q6v4_pdata msm_8960_q6_mss_fw_data = { + .strap_tcm_base = 0x00400000, + .strap_ahb_upper = 0x00090000, + .strap_ahb_lower = 0x00000080, + .aclk_reg = SFAB_MSS_Q6_FW_ACLK_CTL, + .jtag_clk_reg = MSS_Q6FW_JTAG_CLK_CTL, + .name = "modem_fw", + .depends = "q6", + .pas_id = PAS_MODEM_FW, + .bus_port = MSM_BUS_MASTER_MSS_FW_PROC, +}; + +struct platform_device msm_8960_q6_mss_fw = { + .name = "pil_qdsp6v4", + .id = 1, + .num_resources = ARRAY_SIZE(msm_8960_q6_mss_fw_resources), + .resource = msm_8960_q6_mss_fw_resources, + .dev.platform_data = &msm_8960_q6_mss_fw_data, +}; + +#define MSM_SW_QDSP6SS_PHYS 0x08900000 +#define SFAB_MSS_Q6_SW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2040) +#define MSS_Q6SW_JTAG_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C68) + +static struct resource msm_8960_q6_mss_sw_resources[] = { + { + .start = MSM_SW_QDSP6SS_PHYS, + .end = MSM_SW_QDSP6SS_PHYS + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_MSS_ENABLE_PHYS, + .end = MSM_MSS_ENABLE_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct pil_q6v4_pdata msm_8960_q6_mss_sw_data = { + .strap_tcm_base = 0x00420000, + .strap_ahb_upper = 0x00090000, + .strap_ahb_lower = 0x00000080, + .aclk_reg = SFAB_MSS_Q6_SW_ACLK_CTL, + .jtag_clk_reg = MSS_Q6SW_JTAG_CLK_CTL, + .name = "modem", + .depends = "modem_fw", + .pas_id = PAS_MODEM_SW, + .bus_port = MSM_BUS_MASTER_MSS_SW_PROC, +}; + +struct platform_device msm_8960_q6_mss_sw = { + .name = "pil_qdsp6v4", + .id = 2, + .num_resources = ARRAY_SIZE(msm_8960_q6_mss_sw_resources), + .resource = msm_8960_q6_mss_sw_resources, + .dev.platform_data = &msm_8960_q6_mss_sw_data, +}; + +static struct resource msm_8960_riva_resources[] = { + { + .start = 0x03204000, + .end = 0x03204000 + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_8960_riva = { + .name = "pil_riva", + .id = -1, + .num_resources = ARRAY_SIZE(msm_8960_riva_resources), + .resource = msm_8960_riva_resources, +}; + +struct platform_device msm_pil_tzapps = { + .name = "pil_tzapps", + .id = -1, +}; + +struct platform_device msm_pil_dsps = { + .name = "pil_dsps", + .id = -1, + .dev.platform_data = "dsps", +}; + +struct platform_device msm_pil_vidc = { + .name = "pil_vidc", + .id = -1, +}; + +static struct resource smd_resource[] = { + { + .name = "a9_m2a_0", + .start = INT_A9_M2A_0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "a9_m2a_5", + .start = INT_A9_M2A_5, + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_a11", + .start = INT_ADSP_A11, + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_a11_smsm", + .start = INT_ADSP_A11_SMSM, + .flags = IORESOURCE_IRQ, + }, + { + .name = "dsps_a11", + .start = INT_DSPS_A11, + .flags = IORESOURCE_IRQ, + }, + { + .name = "dsps_a11_smsm", + .start = INT_DSPS_A11_SMSM, + .flags = IORESOURCE_IRQ, + }, + { + .name = "wcnss_a11", + .start = INT_WCNSS_A11, + .flags = IORESOURCE_IRQ, + }, + { + .name = "wcnss_a11_smsm", + .start = INT_WCNSS_A11_SMSM, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smd_subsystem_config smd_config_list[] = { + { + .irq_config_id = SMD_MODEM, + .subsys_name = "modem", + .edge = SMD_APPS_MODEM, + + .smd_int.irq_name = "a9_m2a_0", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 3, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "a9_m2a_5", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 4, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_Q6, + .subsys_name = "q6", + .edge = SMD_APPS_QDSP, + + .smd_int.irq_name = "adsp_a11", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 15, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "adsp_a11_smsm", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 14, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, + { + .irq_config_id = SMD_DSPS, + .subsys_name = "dsps", + .edge = SMD_APPS_DSPS, + + .smd_int.irq_name = "dsps_a11", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1, + .smd_int.out_base = (void __iomem *)MSM_SIC_NON_SECURE_BASE, + .smd_int.out_offset = 0x4080, + + .smsm_int.irq_name = "dsps_a11_smsm", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1, + .smsm_int.out_base = (void __iomem *)MSM_SIC_NON_SECURE_BASE, + .smsm_int.out_offset = 0x4094, + }, + { + .irq_config_id = SMD_WCNSS, + .subsys_name = "wcnss", + .edge = SMD_APPS_WCNSS, + + .smd_int.irq_name = "wcnss_a11", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + .smd_int.out_bit_pos = 1 << 25, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "wcnss_a11_smsm", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_smsm", + .smsm_int.dev_id = 0, + .smsm_int.out_bit_pos = 1 << 23, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + }, +}; + +static struct smd_subsystem_restart_config smd_ssr_config = { + .disable_smsm_reset_handshake = 1, +}; + +static struct smd_platform smd_platform_data = { + .num_ss_configs = ARRAY_SIZE(smd_config_list), + .smd_ss_configs = smd_config_list, + .smd_ssr_config = &smd_ssr_config, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, + .resource = smd_resource, + .num_resources = ARRAY_SIZE(smd_resource), + .dev = { + .platform_data = &smd_platform_data, + }, +}; + +struct platform_device msm_device_bam_dmux = { + .name = "BAM_RMNT", + .id = -1, +}; + +static struct msm_watchdog_pdata msm_watchdog_pdata = { + .pet_time = 10000, + .bark_time = 11000, + .has_secure = true, +}; + +struct platform_device msm8960_device_watchdog = { + .name = "msm_watchdog", + .id = -1, + .dev = { + .platform_data = &msm_watchdog_pdata, + }, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = ADM_0_SCSS_1_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x18320000, + .end = 0x18320000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 1, + .sd_size = 0x800, +}; + +struct platform_device msm8960_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, + &msm_device_sdc5, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 5) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +static struct resource resources_qup_i2c_gsbi4[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI4_PHYS, + .end = MSM_GSBI4_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI4_QUP_PHYS, + .end = MSM_GSBI4_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI4_QUP_IRQ, + .end = GSBI4_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_qup_i2c_gsbi4 = { + .name = "qup_i2c", + .id = 4, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi4), + .resource = resources_qup_i2c_gsbi4, +}; + +static struct resource resources_qup_i2c_gsbi3[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI3_PHYS, + .end = MSM_GSBI3_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI3_QUP_PHYS, + .end = MSM_GSBI3_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI3_QUP_IRQ, + .end = GSBI3_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_qup_i2c_gsbi3 = { + .name = "qup_i2c", + .id = 3, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi3), + .resource = resources_qup_i2c_gsbi3, +}; + +static struct resource resources_qup_i2c_gsbi9[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI9_PHYS, + .end = MSM_GSBI9_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI9_QUP_PHYS, + .end = MSM_GSBI9_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI9_QUP_IRQ, + .end = GSBI9_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_qup_i2c_gsbi9 = { + .name = "qup_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi9), + .resource = resources_qup_i2c_gsbi9, +}; + +static struct resource resources_qup_i2c_gsbi10[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI10_PHYS, + .end = MSM_GSBI10_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI10_QUP_PHYS, + .end = MSM_GSBI10_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI10_QUP_IRQ, + .end = GSBI10_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_qup_i2c_gsbi10 = { + .name = "qup_i2c", + .id = 10, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi10), + .resource = resources_qup_i2c_gsbi10, +}; + +static struct resource resources_qup_i2c_gsbi12[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI12_PHYS, + .end = MSM_GSBI12_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI12_QUP_PHYS, + .end = MSM_GSBI12_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI12_QUP_IRQ, + .end = GSBI12_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_qup_i2c_gsbi12 = { + .name = "qup_i2c", + .id = 12, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi12), + .resource = resources_qup_i2c_gsbi12, +}; + +#ifdef CONFIG_MSM_CAMERA +static struct resource msm_cam_gsbi4_i2c_mux_resources[] = { + { + .name = "i2c_mux_rw", + .start = 0x008003E0, + .end = 0x008003E0 + SZ_8 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "i2c_mux_ctl", + .start = 0x008020B8, + .end = 0x008020B8 + SZ_4 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8960_device_i2c_mux_gsbi4 = { + .name = "msm_cam_i2c_mux", + .id = 0, + .resource = msm_cam_gsbi4_i2c_mux_resources, + .num_resources = ARRAY_SIZE(msm_cam_gsbi4_i2c_mux_resources), +}; + +static struct resource msm_csiphy0_resources[] = { + { + .name = "csiphy", + .start = 0x04800C00, + .end = 0x04800C00 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csiphy", + .start = CSIPHY_4LN_IRQ, + .end = CSIPHY_4LN_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_csiphy1_resources[] = { + { + .name = "csiphy", + .start = 0x04801000, + .end = 0x04801000 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csiphy", + .start = MSM8960_CSIPHY_2LN_IRQ, + .end = MSM8960_CSIPHY_2LN_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_csiphy2_resources[] = { + { + .name = "csiphy", + .start = 0x04801400, + .end = 0x04801400 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csiphy", + .start = MSM8960_CSIPHY_2_2LN_IRQ, + .end = MSM8960_CSIPHY_2_2LN_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_csiphy0 = { + .name = "msm_csiphy", + .id = 0, + .resource = msm_csiphy0_resources, + .num_resources = ARRAY_SIZE(msm_csiphy0_resources), +}; + +struct platform_device msm8960_device_csiphy1 = { + .name = "msm_csiphy", + .id = 1, + .resource = msm_csiphy1_resources, + .num_resources = ARRAY_SIZE(msm_csiphy1_resources), +}; + +struct platform_device msm8960_device_csiphy2 = { + .name = "msm_csiphy", + .id = 2, + .resource = msm_csiphy2_resources, + .num_resources = ARRAY_SIZE(msm_csiphy2_resources), +}; + +static struct resource msm_csid0_resources[] = { + { + .name = "csid", + .start = 0x04800000, + .end = 0x04800000 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csid", + .start = CSI_0_IRQ, + .end = CSI_0_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_csid1_resources[] = { + { + .name = "csid", + .start = 0x04800400, + .end = 0x04800400 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csid", + .start = CSI_1_IRQ, + .end = CSI_1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_csid2_resources[] = { + { + .name = "csid", + .start = 0x04801800, + .end = 0x04801800 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csid", + .start = CSI_2_IRQ, + .end = CSI_2_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_csid0 = { + .name = "msm_csid", + .id = 0, + .resource = msm_csid0_resources, + .num_resources = ARRAY_SIZE(msm_csid0_resources), +}; + +struct platform_device msm8960_device_csid1 = { + .name = "msm_csid", + .id = 1, + .resource = msm_csid1_resources, + .num_resources = ARRAY_SIZE(msm_csid1_resources), +}; + +struct platform_device msm8960_device_csid2 = { + .name = "msm_csid", + .id = 2, + .resource = msm_csid2_resources, + .num_resources = ARRAY_SIZE(msm_csid2_resources), +}; + +struct resource msm_ispif_resources[] = { + { + .name = "ispif", + .start = 0x04800800, + .end = 0x04800800 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "ispif", + .start = ISPIF_IRQ, + .end = ISPIF_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_ispif = { + .name = "msm_ispif", + .id = 0, + .resource = msm_ispif_resources, + .num_resources = ARRAY_SIZE(msm_ispif_resources), +}; + +static struct resource msm_vfe_resources[] = { + { + .name = "vfe32", + .start = 0x04500000, + .end = 0x04500000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "vfe32", + .start = VFE_IRQ, + .end = VFE_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_vfe = { + .name = "msm_vfe", + .id = 0, + .resource = msm_vfe_resources, + .num_resources = ARRAY_SIZE(msm_vfe_resources), +}; + +static struct resource msm_vpe_resources[] = { + { + .name = "vpe", + .start = 0x05300000, + .end = 0x05300000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "vpe", + .start = VPE_IRQ, + .end = VPE_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_device_vpe = { + .name = "msm_vpe", + .id = 0, + .resource = msm_vpe_resources, + .num_resources = ARRAY_SIZE(msm_vpe_resources), +}; +#endif + +#define MSM_TSIF0_PHYS (0x18200000) +#define MSM_TSIF1_PHYS (0x18201000) +#define MSM_TSIF_SIZE (0x200) + +#define TSIF_0_CLK GPIO_CFG(75, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_EN GPIO_CFG(76, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_DATA GPIO_CFG(77, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_SYNC GPIO_CFG(82, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_CLK GPIO_CFG(79, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_EN GPIO_CFG(80, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_DATA GPIO_CFG(81, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_SYNC GPIO_CFG(78, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) + +static const struct msm_gpio tsif0_gpios[] = { + { .gpio_cfg = TSIF_0_CLK, .label = "tsif_clk", }, + { .gpio_cfg = TSIF_0_EN, .label = "tsif_en", }, + { .gpio_cfg = TSIF_0_DATA, .label = "tsif_data", }, + { .gpio_cfg = TSIF_0_SYNC, .label = "tsif_sync", }, +}; + +static const struct msm_gpio tsif1_gpios[] = { + { .gpio_cfg = TSIF_1_CLK, .label = "tsif_clk", }, + { .gpio_cfg = TSIF_1_EN, .label = "tsif_en", }, + { .gpio_cfg = TSIF_1_DATA, .label = "tsif_data", }, + { .gpio_cfg = TSIF_1_SYNC, .label = "tsif_sync", }, +}; + +struct msm_tsif_platform_data tsif1_platform_data = { + .num_gpios = ARRAY_SIZE(tsif1_gpios), + .gpios = tsif1_gpios, + .tsif_pclk = "tsif_pclk", + .tsif_ref_clk = "tsif_ref_clk", +}; + +struct resource tsif1_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = TSIF2_IRQ, + .end = TSIF2_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF1_PHYS, + .end = MSM_TSIF1_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +struct msm_tsif_platform_data tsif0_platform_data = { + .num_gpios = ARRAY_SIZE(tsif0_gpios), + .gpios = tsif0_gpios, + .tsif_pclk = "tsif_pclk", + .tsif_ref_clk = "tsif_ref_clk", +}; +struct resource tsif0_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = TSIF1_IRQ, + .end = TSIF1_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF0_PHYS, + .end = MSM_TSIF0_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +struct platform_device msm_device_tsif[2] = { + { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif0_resources), + .resource = tsif0_resources, + .dev = { + .platform_data = &tsif0_platform_data + }, + }, + { + .name = "msm_tsif", + .id = 1, + .num_resources = ARRAY_SIZE(tsif1_resources), + .resource = tsif1_resources, + .dev = { + .platform_data = &tsif1_platform_data + }, + } +}; + +static struct resource resources_ssbi_pmic[] = { + { + .start = MSM_PMIC1_SSBI_CMD_PHYS, + .end = MSM_PMIC1_SSBI_CMD_PHYS + MSM_PMIC_SSBI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8960_device_ssbi_pmic = { + .name = "msm_ssbi", + .id = 0, + .resource = resources_ssbi_pmic, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic), +}; + +static struct resource resources_qup_spi_gsbi1[] = { + { + .name = "spi_base", + .start = MSM_GSBI1_QUP_PHYS, + .end = MSM_GSBI1_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_base", + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spi_irq_in", + .start = MSM8960_GSBI1_QUP_IRQ, + .end = MSM8960_GSBI1_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_clk", + .start = 9, + .end = 9, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_miso", + .start = 7, + .end = 7, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_mosi", + .start = 6, + .end = 6, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_cs", + .start = 8, + .end = 8, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_cs1", + .start = 14, + .end = 14, + .flags = IORESOURCE_IO, + }, +}; + +struct platform_device msm8960_device_qup_spi_gsbi1 = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(resources_qup_spi_gsbi1), + .resource = resources_qup_spi_gsbi1, +}; + +struct platform_device msm_pcm = { + .name = "msm-pcm-dsp", + .id = -1, +}; + +struct platform_device msm_multi_ch_pcm = { + .name = "msm-multi-ch-pcm-dsp", + .id = -1, +}; + +struct platform_device msm_pcm_routing = { + .name = "msm-pcm-routing", + .id = -1, +}; + +struct platform_device msm_cpudai0 = { + .name = "msm-dai-q6", + .id = 0x4000, +}; + +struct platform_device msm_cpudai1 = { + .name = "msm-dai-q6", + .id = 0x4001, +}; + +struct platform_device msm8960_cpudai_slimbus_2_rx = { + .name = "msm-dai-q6", + .id = 0x4004, +}; + +struct platform_device msm8960_cpudai_slimbus_2_tx = { + .name = "msm-dai-q6", + .id = 0x4005, +}; + +struct platform_device msm_cpudai_hdmi_rx = { + .name = "msm-dai-q6-hdmi", + .id = 8, +}; + +struct platform_device msm_cpudai_bt_rx = { + .name = "msm-dai-q6", + .id = 0x3000, +}; + +struct platform_device msm_cpudai_bt_tx = { + .name = "msm-dai-q6", + .id = 0x3001, +}; + +struct platform_device msm_cpudai_fm_rx = { + .name = "msm-dai-q6", + .id = 0x3004, +}; + +struct platform_device msm_cpudai_fm_tx = { + .name = "msm-dai-q6", + .id = 0x3005, +}; + +struct platform_device msm_cpudai_incall_music_rx = { + .name = "msm-dai-q6", + .id = 0x8005, +}; + +struct platform_device msm_cpudai_incall_record_rx = { + .name = "msm-dai-q6", + .id = 0x8004, +}; + +struct platform_device msm_cpudai_incall_record_tx = { + .name = "msm-dai-q6", + .id = 0x8003, +}; + +/* + * Machine specific data for AUX PCM Interface + * which the driver will be unware of. + */ +struct msm_dai_auxpcm_pdata auxpcm_pdata = { + .clk = "pcm_clk", + .mode_8k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 2048000, + }, + .mode_16k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 4096000, + } +}; + +struct platform_device msm_cpudai_auxpcm_rx = { + .name = "msm-dai-q6", + .id = 2, + .dev = { + .platform_data = &auxpcm_pdata, + }, +}; + +struct platform_device msm_cpudai_auxpcm_tx = { + .name = "msm-dai-q6", + .id = 3, + .dev = { + .platform_data = &auxpcm_pdata, + }, +}; + +struct platform_device msm_cpu_fe = { + .name = "msm-dai-fe", + .id = -1, +}; + +struct platform_device msm_stub_codec = { + .name = "msm-stub-codec", + .id = 1, +}; + +struct platform_device msm_voice = { + .name = "msm-pcm-voice", + .id = -1, +}; + +struct platform_device msm_voip = { + .name = "msm-voip-dsp", + .id = -1, +}; + +struct platform_device msm_lpa_pcm = { + .name = "msm-pcm-lpa", + .id = -1, +}; + +struct platform_device msm_compr_dsp = { + .name = "msm-compr-dsp", + .id = -1, +}; + +struct platform_device msm_pcm_hostless = { + .name = "msm-pcm-hostless", + .id = -1, +}; + +struct platform_device msm_cpudai_afe_01_rx = { + .name = "msm-dai-q6", + .id = 0xE0, +}; + +struct platform_device msm_cpudai_afe_01_tx = { + .name = "msm-dai-q6", + .id = 0xF0, +}; + +struct platform_device msm_cpudai_afe_02_rx = { + .name = "msm-dai-q6", + .id = 0xF1, +}; + +struct platform_device msm_cpudai_afe_02_tx = { + .name = "msm-dai-q6", + .id = 0xE1, +}; + +struct platform_device msm_pcm_afe = { + .name = "msm-pcm-afe", + .id = -1, +}; + +static struct fs_driver_data gfx2d0_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, +}; + +static struct fs_driver_data gfx2d1_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, +}; + +static struct fs_driver_data gfx3d_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk", .reset_rate = 27000000 }, + { .name = "iface_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D, +}; + +static struct fs_driver_data ijpeg_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_JPEG_ENC, +}; + +static struct fs_driver_data mdp_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { .name = "vsync_clk" }, + { .name = "lut_clk" }, + { .name = "tv_src_clk" }, + { .name = "tv_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_MDP_PORT0, + .bus_port1 = MSM_BUS_MASTER_MDP_PORT1, +}; + +static struct fs_driver_data rot_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_ROTATOR, +}; + +static struct fs_driver_data ved_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_HD_CODEC_PORT0, + .bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1, +}; + +static struct fs_driver_data vfe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VFE, +}; + +static struct fs_driver_data vpe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VPE, +}; + +struct platform_device *msm8960_footswitch[] __initdata = { + FS_8X60(FS_MDP, "vdd", "mdp.0", &mdp_fs_data), + FS_8X60(FS_ROT, "vdd", "msm_rotator.0", &rot_fs_data), + FS_8X60(FS_IJPEG, "vdd", "msm_gemini.0", &ijpeg_fs_data), + FS_8X60(FS_VFE, "fs_vfe", NULL, &vfe_fs_data), + FS_8X60(FS_VPE, "fs_vpe", NULL, &vpe_fs_data), + FS_8X60(FS_GFX3D, "vdd", "kgsl-3d0.0", &gfx3d_fs_data), + FS_8X60(FS_GFX2D0, "vdd", "kgsl-2d0.0", &gfx2d0_fs_data), + FS_8X60(FS_GFX2D1, "vdd", "kgsl-2d1.1", &gfx2d1_fs_data), + FS_8X60(FS_VED, "vdd", "msm_vidc.0", &ved_fs_data), +}; +unsigned msm8960_num_footswitch __initdata = ARRAY_SIZE(msm8960_footswitch); + +#ifdef CONFIG_MSM_ROTATOR +static struct msm_bus_vectors rotator_init_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors rotator_ui_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (1024 * 600 * 4 * 2 * 60), + .ib = (1024 * 600 * 4 * 2 * 60 * 1.5), + }, +}; + +static struct msm_bus_vectors rotator_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (640 * 480 * 2 * 2 * 30), + .ib = (640 * 480 * 2 * 2 * 30 * 1.5), + }, +}; +static struct msm_bus_vectors rotator_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (1280 * 736 * 2 * 2 * 30), + .ib = (1280 * 736 * 2 * 2 * 30 * 1.5), + }, +}; + +static struct msm_bus_vectors rotator_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_ROTATOR, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = (1920 * 1088 * 2 * 2 * 30), + .ib = (1920 * 1088 * 2 * 2 * 30 * 1.5), + }, +}; + +static struct msm_bus_paths rotator_bus_scale_usecases[] = { + { + ARRAY_SIZE(rotator_init_vectors), + rotator_init_vectors, + }, + { + ARRAY_SIZE(rotator_ui_vectors), + rotator_ui_vectors, + }, + { + ARRAY_SIZE(rotator_vga_vectors), + rotator_vga_vectors, + }, + { + ARRAY_SIZE(rotator_720p_vectors), + rotator_720p_vectors, + }, + { + ARRAY_SIZE(rotator_1080p_vectors), + rotator_1080p_vectors, + }, +}; + +struct msm_bus_scale_pdata rotator_bus_scale_pdata = { + rotator_bus_scale_usecases, + ARRAY_SIZE(rotator_bus_scale_usecases), + .name = "rotator", +}; + +void __init msm_rotator_update_bus_vectors(unsigned int xres, + unsigned int yres) +{ + rotator_ui_vectors[0].ab = xres * yres * 4 * 2 * 60; + rotator_ui_vectors[0].ib = xres * yres * 4 * 2 * 60 * 3 / 2; +} + +#define ROTATOR_HW_BASE 0x04E00000 +static struct resource resources_msm_rotator[] = { + { + .start = ROTATOR_HW_BASE, + .end = ROTATOR_HW_BASE + 0x100000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = ROT_IRQ, + .end = ROT_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_rot_clocks rotator_clocks[] = { + { + .clk_name = "core_clk", + .clk_type = ROTATOR_CORE_CLK, + .clk_rate = 200 * 1000 * 1000, + }, + { + .clk_name = "iface_clk", + .clk_type = ROTATOR_PCLK, + .clk_rate = 0, + }, +}; + +static struct msm_rotator_platform_data rotator_pdata = { + .number_of_clocks = ARRAY_SIZE(rotator_clocks), + .hardware_version_number = 0x01020309, + .rotator_clks = rotator_clocks, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &rotator_bus_scale_pdata, +#endif +}; + +struct platform_device msm_rotator_device = { + .name = "msm_rotator", + .id = 0, + .num_resources = ARRAY_SIZE(resources_msm_rotator), + .resource = resources_msm_rotator, + .dev = { + .platform_data = &rotator_pdata, + }, +}; +#endif + +#define MIPI_DSI_HW_BASE 0x04700000 +#define MDP_HW_BASE 0x05100000 + +static struct resource msm_mipi_dsi1_resources[] = { + { + .name = "mipi_dsi", + .start = MIPI_DSI_HW_BASE, + .end = MIPI_DSI_HW_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = DSI1_IRQ, + .end = DSI1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_mipi_dsi1_device = { + .name = "mipi_dsi", + .id = 1, + .num_resources = ARRAY_SIZE(msm_mipi_dsi1_resources), + .resource = msm_mipi_dsi1_resources, +}; + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_HW_BASE, + .end = MDP_HW_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MDP_IRQ, + .end = MDP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +#ifdef CONFIG_MSM_BUS_SCALING +static struct platform_device msm_dtv_device = { + .name = "dtv", + .id = 0, +}; +#endif + +struct platform_device msm_lvds_device = { + .name = "lvds", + .id = 0, +}; + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "mipi_dsi", 8)) + msm_register_device(&msm_mipi_dsi1_device, data); + else if (!strncmp(name, "lvds", 4)) + msm_register_device(&msm_lvds_device, data); +#ifdef CONFIG_MSM_BUS_SCALING + else if (!strncmp(name, "dtv", 3)) + msm_register_device(&msm_dtv_device, data); +#endif + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct resource resources_sps[] = { + { + .name = "pipe_mem", + .start = 0x12800000, + .end = 0x12800000 + 0x4000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_dma", + .start = 0x12240000, + .end = 0x12240000 + 0x1000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_bam", + .start = 0x12244000, + .end = 0x12244000 + 0x4000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_irq", + .start = SPS_BAM_DMA_IRQ, + .end = SPS_BAM_DMA_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_sps_platform_data msm_sps_pdata = { + .bamdma_restricted_pipes = 0x06, +}; + +struct platform_device msm_device_sps = { + .name = "msm_sps", + .id = -1, + .num_resources = ARRAY_SIZE(resources_sps), + .resource = resources_sps, + .dev.platform_data = &msm_sps_pdata, +}; + +#ifdef CONFIG_MSM_MPM +static uint16_t msm_mpm_irqs_m2a[MSM_MPM_NR_MPM_IRQS] __initdata = { + [1] = MSM_GPIO_TO_INT(46), + [2] = MSM_GPIO_TO_INT(150), + [4] = MSM_GPIO_TO_INT(103), + [5] = MSM_GPIO_TO_INT(104), + [6] = MSM_GPIO_TO_INT(105), + [7] = MSM_GPIO_TO_INT(106), + [8] = MSM_GPIO_TO_INT(107), + [9] = MSM_GPIO_TO_INT(7), + [10] = MSM_GPIO_TO_INT(11), + [11] = MSM_GPIO_TO_INT(15), + [12] = MSM_GPIO_TO_INT(19), + [13] = MSM_GPIO_TO_INT(23), + [14] = MSM_GPIO_TO_INT(27), + [15] = MSM_GPIO_TO_INT(31), + [16] = MSM_GPIO_TO_INT(35), + [19] = MSM_GPIO_TO_INT(90), + [20] = MSM_GPIO_TO_INT(92), + [23] = MSM_GPIO_TO_INT(85), + [24] = MSM_GPIO_TO_INT(83), + [25] = USB1_HS_IRQ, + [27] = HDMI_IRQ, + [29] = MSM_GPIO_TO_INT(10), + [30] = MSM_GPIO_TO_INT(102), + [31] = MSM_GPIO_TO_INT(81), + [32] = MSM_GPIO_TO_INT(78), + [33] = MSM_GPIO_TO_INT(94), + [34] = MSM_GPIO_TO_INT(72), + [35] = MSM_GPIO_TO_INT(39), + [36] = MSM_GPIO_TO_INT(43), + [37] = MSM_GPIO_TO_INT(61), + [38] = MSM_GPIO_TO_INT(50), + [39] = MSM_GPIO_TO_INT(42), + [41] = MSM_GPIO_TO_INT(62), + [42] = MSM_GPIO_TO_INT(76), + [43] = MSM_GPIO_TO_INT(75), + [44] = MSM_GPIO_TO_INT(70), + [45] = MSM_GPIO_TO_INT(69), + [46] = MSM_GPIO_TO_INT(67), + [47] = MSM_GPIO_TO_INT(65), + [48] = MSM_GPIO_TO_INT(58), + [49] = MSM_GPIO_TO_INT(54), + [50] = MSM_GPIO_TO_INT(52), + [51] = MSM_GPIO_TO_INT(49), + [52] = MSM_GPIO_TO_INT(40), + [53] = MSM_GPIO_TO_INT(37), + [54] = MSM_GPIO_TO_INT(24), + [55] = MSM_GPIO_TO_INT(14), +}; + +static uint16_t msm_mpm_bypassed_apps_irqs[] __initdata = { + TLMM_MSM_SUMMARY_IRQ, + RPM_APCC_CPU0_GP_HIGH_IRQ, + RPM_APCC_CPU0_GP_MEDIUM_IRQ, + RPM_APCC_CPU0_GP_LOW_IRQ, + RPM_APCC_CPU0_WAKE_UP_IRQ, + RPM_APCC_CPU1_GP_HIGH_IRQ, + RPM_APCC_CPU1_GP_MEDIUM_IRQ, + RPM_APCC_CPU1_GP_LOW_IRQ, + RPM_APCC_CPU1_WAKE_UP_IRQ, + MSS_TO_APPS_IRQ_0, + MSS_TO_APPS_IRQ_1, + MSS_TO_APPS_IRQ_2, + MSS_TO_APPS_IRQ_3, + MSS_TO_APPS_IRQ_4, + MSS_TO_APPS_IRQ_5, + MSS_TO_APPS_IRQ_6, + MSS_TO_APPS_IRQ_7, + MSS_TO_APPS_IRQ_8, + MSS_TO_APPS_IRQ_9, + LPASS_SCSS_GP_LOW_IRQ, + LPASS_SCSS_GP_MEDIUM_IRQ, + LPASS_SCSS_GP_HIGH_IRQ, + SPS_MTI_30, + SPS_MTI_31, + RIVA_APSS_SPARE_IRQ, + RIVA_APPS_WLAN_SMSM_IRQ, + RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ, + RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ, +}; + +struct msm_mpm_device_data msm8960_mpm_dev_data __initdata = { + .irqs_m2a = msm_mpm_irqs_m2a, + .irqs_m2a_size = ARRAY_SIZE(msm_mpm_irqs_m2a), + .bypassed_apps_irqs = msm_mpm_bypassed_apps_irqs, + .bypassed_apps_irqs_size = ARRAY_SIZE(msm_mpm_bypassed_apps_irqs), + .mpm_request_reg_base = MSM_RPM_BASE + 0x9d8, + .mpm_status_reg_base = MSM_RPM_BASE + 0xdf8, + .mpm_apps_ipc_reg = MSM_APCS_GCC_BASE + 0x008, + .mpm_apps_ipc_val = BIT(1), + .mpm_ipc_irq = RPM_APCC_CPU0_GP_MEDIUM_IRQ, + +}; +#endif + +#define LPASS_SLIMBUS_PHYS 0x28080000 +#define LPASS_SLIMBUS_BAM_PHYS 0x28084000 +#define LPASS_SLIMBUS_SLEW (MSM8960_TLMM_PHYS + 0x207C) +/* Board info for the slimbus slave device */ +static struct resource slimbus_res[] = { + { + .start = LPASS_SLIMBUS_PHYS, + .end = LPASS_SLIMBUS_PHYS + 8191, + .flags = IORESOURCE_MEM, + .name = "slimbus_physical", + }, + { + .start = LPASS_SLIMBUS_BAM_PHYS, + .end = LPASS_SLIMBUS_BAM_PHYS + 8191, + .flags = IORESOURCE_MEM, + .name = "slimbus_bam_physical", + }, + { + .start = LPASS_SLIMBUS_SLEW, + .end = LPASS_SLIMBUS_SLEW + 4 - 1, + .flags = IORESOURCE_MEM, + .name = "slimbus_slew_reg", + }, + { + .start = SLIMBUS0_CORE_EE1_IRQ, + .end = SLIMBUS0_CORE_EE1_IRQ, + .flags = IORESOURCE_IRQ, + .name = "slimbus_irq", + }, + { + .start = SLIMBUS0_BAM_EE1_IRQ, + .end = SLIMBUS0_BAM_EE1_IRQ, + .flags = IORESOURCE_IRQ, + .name = "slimbus_bam_irq", + }, +}; + +struct platform_device msm_slim_ctrl = { + .name = "msm_slim_ctrl", + .id = 1, + .num_resources = ARRAY_SIZE(slimbus_res), + .resource = slimbus_res, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct msm_dcvs_freq_entry grp3d_freq[] = { + {0, 0, 333932}, + {0, 0, 497532}, + {0, 0, 707610}, + {0, 0, 844545}, +}; + +static struct msm_dcvs_freq_entry grp2d_freq[] = { + {0, 0, 86000}, + {0, 0, 200000}, +}; + +static struct msm_dcvs_core_info grp3d_core_info = { + .freq_tbl = &grp3d_freq[0], + .core_param = { + .max_time_us = 100000, + .num_freq = ARRAY_SIZE(grp3d_freq), + }, + .algo_param = { + .slack_time_us = 39000, + .disable_pc_threshold = 86000, + .ss_window_size = 1000000, + .ss_util_pct = 95, + .em_max_util_pct = 97, + .ss_iobusy_conv = 100, + }, +}; + +static struct msm_dcvs_core_info grp2d_core_info = { + .freq_tbl = &grp2d_freq[0], + .core_param = { + .max_time_us = 100000, + .num_freq = ARRAY_SIZE(grp2d_freq), + }, + .algo_param = { + .slack_time_us = 39000, + .disable_pc_threshold = 90000, + .ss_window_size = 1000000, + .ss_util_pct = 90, + .em_max_util_pct = 95, + }, +}; + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors grp3d_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp3d_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(1000), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2048), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_high_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2656), + }, +}; + +static struct msm_bus_vectors grp3d_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(3968), + }, +}; + +static struct msm_bus_paths grp3d_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp3d_init_vectors), + grp3d_init_vectors, + }, + { + ARRAY_SIZE(grp3d_low_vectors), + grp3d_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_low_vectors), + grp3d_nominal_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_high_vectors), + grp3d_nominal_high_vectors, + }, + { + ARRAY_SIZE(grp3d_max_vectors), + grp3d_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata grp3d_bus_scale_pdata = { + grp3d_bus_scale_usecases, + ARRAY_SIZE(grp3d_bus_scale_usecases), + .name = "grp3d", +}; + +static struct msm_bus_vectors grp2d0_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp2d0_nominal_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(1000), + }, +}; + +static struct msm_bus_vectors grp2d0_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2048), + }, +}; + +static struct msm_bus_paths grp2d0_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp2d0_init_vectors), + grp2d0_init_vectors, + }, + { + ARRAY_SIZE(grp2d0_nominal_vectors), + grp2d0_nominal_vectors, + }, + { + ARRAY_SIZE(grp2d0_max_vectors), + grp2d0_max_vectors, + }, +}; + +struct msm_bus_scale_pdata grp2d0_bus_scale_pdata = { + grp2d0_bus_scale_usecases, + ARRAY_SIZE(grp2d0_bus_scale_usecases), + .name = "grp2d0", +}; + +static struct msm_bus_vectors grp2d1_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp2d1_nominal_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(1000), + }, +}; + +static struct msm_bus_vectors grp2d1_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2048), + }, +}; + +static struct msm_bus_paths grp2d1_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp2d1_init_vectors), + grp2d1_init_vectors, + }, + { + ARRAY_SIZE(grp2d1_nominal_vectors), + grp2d1_nominal_vectors, + }, + { + ARRAY_SIZE(grp2d1_max_vectors), + grp2d1_max_vectors, + }, +}; + +struct msm_bus_scale_pdata grp2d1_bus_scale_pdata = { + grp2d1_bus_scale_usecases, + ARRAY_SIZE(grp2d1_bus_scale_usecases), + .name = "grp2d1", +}; +#endif + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0x04300000, /* GFX3D address */ + .end = 0x0431ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = GFX3D_IRQ, + .end = GFX3D_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct kgsl_iommu_ctx kgsl_3d0_iommu_ctxs[] = { + { "gfx3d_user", 0 }, + { "gfx3d_priv", 1 }, +}; + +static struct kgsl_device_iommu_data kgsl_3d0_iommu_data[] = { + { + .iommu_ctxs = kgsl_3d0_iommu_ctxs, + .iommu_ctx_count = ARRAY_SIZE(kgsl_3d0_iommu_ctxs), + .physstart = 0x07C00000, + .physend = 0x07C00000 + SZ_1M - 1, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 400000000, + .bus_freq = 4, + .io_fraction = 0, + }, + { + .gpu_freq = 300000000, + .bus_freq = 3, + .io_fraction = 33, + }, + { + .gpu_freq = 200000000, + .bus_freq = 2, + .io_fraction = 100, + }, + { + .gpu_freq = 128000000, + .bus_freq = 1, + .io_fraction = 100, + }, + { + .gpu_freq = 27000000, + .bus_freq = 0, + }, + }, + .init_level = 1, + .num_levels = ARRAY_SIZE(grp3d_freq) + 1, + .set_grp_async = NULL, + .idle_timeout = HZ/12, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp3d_bus_scale_pdata, +#endif + .iommu_data = kgsl_3d0_iommu_data, + .iommu_count = ARRAY_SIZE(kgsl_3d0_iommu_data), + .core_info = &grp3d_core_info, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +static struct resource kgsl_2d0_resources[] = { + { + .name = KGSL_2D0_REG_MEMORY, + .start = 0x04100000, /* Z180 base address */ + .end = 0x04100FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_2D0_IRQ, + .start = GFX2D0_IRQ, + .end = GFX2D0_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct kgsl_iommu_ctx kgsl_2d0_iommu_ctxs[] = { + { "gfx2d0_2d0", 0 }, +}; + +static struct kgsl_device_iommu_data kgsl_2d0_iommu_data[] = { + { + .iommu_ctxs = kgsl_2d0_iommu_ctxs, + .iommu_ctx_count = ARRAY_SIZE(kgsl_2d0_iommu_ctxs), + .physstart = 0x07D00000, + .physend = 0x07D00000 + SZ_1M - 1, + }, +}; + +static struct kgsl_device_platform_data kgsl_2d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 200000000, + .bus_freq = 2, + }, + { + .gpu_freq = 96000000, + .bus_freq = 1, + }, + { + .gpu_freq = 27000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = ARRAY_SIZE(grp2d_freq) + 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp2d0_bus_scale_pdata, +#endif + .iommu_data = kgsl_2d0_iommu_data, + .iommu_count = ARRAY_SIZE(kgsl_2d0_iommu_data), + .core_info = &grp2d_core_info, +}; + +struct platform_device msm_kgsl_2d0 = { + .name = "kgsl-2d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_2d0_resources), + .resource = kgsl_2d0_resources, + .dev = { + .platform_data = &kgsl_2d0_pdata, + }, +}; + +static const struct kgsl_iommu_ctx kgsl_2d1_iommu_ctxs[] = { + { "gfx2d1_2d1", 0 }, +}; + +static struct kgsl_device_iommu_data kgsl_2d1_iommu_data[] = { + { + .iommu_ctxs = kgsl_2d1_iommu_ctxs, + .iommu_ctx_count = ARRAY_SIZE(kgsl_2d1_iommu_ctxs), + .physstart = 0x07E00000, + .physend = 0x07E00000 + SZ_1M - 1, + }, +}; + +static struct resource kgsl_2d1_resources[] = { + { + .name = KGSL_2D1_REG_MEMORY, + .start = 0x04200000, /* Z180 device 1 base address */ + .end = 0x04200FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_2D1_IRQ, + .start = GFX2D1_IRQ, + .end = GFX2D1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_2d1_pdata = { + .pwrlevel = { + { + .gpu_freq = 200000000, + .bus_freq = 2, + }, + { + .gpu_freq = 96000000, + .bus_freq = 1, + }, + { + .gpu_freq = 27000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = ARRAY_SIZE(grp2d_freq) + 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp2d1_bus_scale_pdata, +#endif + .iommu_data = kgsl_2d1_iommu_data, + .iommu_count = ARRAY_SIZE(kgsl_2d1_iommu_data), + .core_info = &grp2d_core_info, +}; + +struct platform_device msm_kgsl_2d1 = { + .name = "kgsl-2d1", + .id = 1, + .num_resources = ARRAY_SIZE(kgsl_2d1_resources), + .resource = kgsl_2d1_resources, + .dev = { + .platform_data = &kgsl_2d1_pdata, + }, +}; + +#ifdef CONFIG_MSM_GEMINI +static struct resource msm_gemini_resources[] = { + { + .start = 0x04600000, + .end = 0x04600000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = JPEG_IRQ, + .end = JPEG_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8960_gemini_device = { + .name = "msm_gemini", + .resource = msm_gemini_resources, + .num_resources = ARRAY_SIZE(msm_gemini_resources), +}; +#endif + +#ifdef CONFIG_MSM_MERCURY +static struct resource msm_mercury_resources[] = { + { + .start = 0x05000000, + .end = 0x05000000 + SZ_1M - 1, + .name = "mercury_resource_base", + .flags = IORESOURCE_MEM, + }, + { + .start = JPEGD_IRQ, + .end = JPEGD_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; +struct platform_device msm8960_mercury_device = { + .name = "msm_mercury", + .resource = msm_mercury_resources, + .num_resources = ARRAY_SIZE(msm_mercury_resources), +}; +#endif + +struct msm_rpm_platform_data msm8960_rpm_data __initdata = { + .reg_base_addrs = { + [MSM_RPM_PAGE_STATUS] = MSM_RPM_BASE, + [MSM_RPM_PAGE_CTRL] = MSM_RPM_BASE + 0x400, + [MSM_RPM_PAGE_REQ] = MSM_RPM_BASE + 0x600, + [MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00, + }, + .irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ, + .irq_err = RPM_APCC_CPU0_GP_LOW_IRQ, + .irq_wakeup = RPM_APCC_CPU0_WAKE_UP_IRQ, + .ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008, + .ipc_rpm_val = 4, + .target_id = { + MSM_RPM_MAP(8960, NOTIFICATION_CONFIGURED_0, NOTIFICATION, 4), + MSM_RPM_MAP(8960, NOTIFICATION_REGISTERED_0, NOTIFICATION, 4), + MSM_RPM_MAP(8960, INVALIDATE_0, INVALIDATE, 8), + MSM_RPM_MAP(8960, TRIGGER_TIMED_TO, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8960, TRIGGER_TIMED_SCLK_COUNT, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8960, RPM_CTL, RPM_CTL, 1), + MSM_RPM_MAP(8960, CXO_CLK, CXO_CLK, 1), + MSM_RPM_MAP(8960, PXO_CLK, PXO_CLK, 1), + MSM_RPM_MAP(8960, APPS_FABRIC_CLK, APPS_FABRIC_CLK, 1), + MSM_RPM_MAP(8960, SYSTEM_FABRIC_CLK, SYSTEM_FABRIC_CLK, 1), + MSM_RPM_MAP(8960, MM_FABRIC_CLK, MM_FABRIC_CLK, 1), + MSM_RPM_MAP(8960, DAYTONA_FABRIC_CLK, DAYTONA_FABRIC_CLK, 1), + MSM_RPM_MAP(8960, SFPB_CLK, SFPB_CLK, 1), + MSM_RPM_MAP(8960, CFPB_CLK, CFPB_CLK, 1), + MSM_RPM_MAP(8960, MMFPB_CLK, MMFPB_CLK, 1), + MSM_RPM_MAP(8960, EBI1_CLK, EBI1_CLK, 1), + MSM_RPM_MAP(8960, APPS_FABRIC_CFG_HALT_0, + APPS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8960, APPS_FABRIC_CFG_CLKMOD_0, + APPS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8960, APPS_FABRIC_CFG_IOCTL, + APPS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8960, APPS_FABRIC_ARB_0, APPS_FABRIC_ARB, 12), + MSM_RPM_MAP(8960, SYS_FABRIC_CFG_HALT_0, + SYS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8960, SYS_FABRIC_CFG_CLKMOD_0, + SYS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8960, SYS_FABRIC_CFG_IOCTL, + SYS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8960, SYSTEM_FABRIC_ARB_0, + SYSTEM_FABRIC_ARB, 29), + MSM_RPM_MAP(8960, MMSS_FABRIC_CFG_HALT_0, + MMSS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(8960, MMSS_FABRIC_CFG_CLKMOD_0, + MMSS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(8960, MMSS_FABRIC_CFG_IOCTL, + MMSS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(8960, MM_FABRIC_ARB_0, MM_FABRIC_ARB, 23), + MSM_RPM_MAP(8960, PM8921_S1_0, PM8921_S1, 2), + MSM_RPM_MAP(8960, PM8921_S2_0, PM8921_S2, 2), + MSM_RPM_MAP(8960, PM8921_S3_0, PM8921_S3, 2), + MSM_RPM_MAP(8960, PM8921_S4_0, PM8921_S4, 2), + MSM_RPM_MAP(8960, PM8921_S5_0, PM8921_S5, 2), + MSM_RPM_MAP(8960, PM8921_S6_0, PM8921_S6, 2), + MSM_RPM_MAP(8960, PM8921_S7_0, PM8921_S7, 2), + MSM_RPM_MAP(8960, PM8921_S8_0, PM8921_S8, 2), + MSM_RPM_MAP(8960, PM8921_L1_0, PM8921_L1, 2), + MSM_RPM_MAP(8960, PM8921_L2_0, PM8921_L2, 2), + MSM_RPM_MAP(8960, PM8921_L3_0, PM8921_L3, 2), + MSM_RPM_MAP(8960, PM8921_L4_0, PM8921_L4, 2), + MSM_RPM_MAP(8960, PM8921_L5_0, PM8921_L5, 2), + MSM_RPM_MAP(8960, PM8921_L6_0, PM8921_L6, 2), + MSM_RPM_MAP(8960, PM8921_L7_0, PM8921_L7, 2), + MSM_RPM_MAP(8960, PM8921_L8_0, PM8921_L8, 2), + MSM_RPM_MAP(8960, PM8921_L9_0, PM8921_L9, 2), + MSM_RPM_MAP(8960, PM8921_L10_0, PM8921_L10, 2), + MSM_RPM_MAP(8960, PM8921_L11_0, PM8921_L11, 2), + MSM_RPM_MAP(8960, PM8921_L12_0, PM8921_L12, 2), + MSM_RPM_MAP(8960, PM8921_L13_0, PM8921_L13, 2), + MSM_RPM_MAP(8960, PM8921_L14_0, PM8921_L14, 2), + MSM_RPM_MAP(8960, PM8921_L15_0, PM8921_L15, 2), + MSM_RPM_MAP(8960, PM8921_L16_0, PM8921_L16, 2), + MSM_RPM_MAP(8960, PM8921_L17_0, PM8921_L17, 2), + MSM_RPM_MAP(8960, PM8921_L18_0, PM8921_L18, 2), + MSM_RPM_MAP(8960, PM8921_L19_0, PM8921_L19, 2), + MSM_RPM_MAP(8960, PM8921_L20_0, PM8921_L20, 2), + MSM_RPM_MAP(8960, PM8921_L21_0, PM8921_L21, 2), + MSM_RPM_MAP(8960, PM8921_L22_0, PM8921_L22, 2), + MSM_RPM_MAP(8960, PM8921_L23_0, PM8921_L23, 2), + MSM_RPM_MAP(8960, PM8921_L24_0, PM8921_L24, 2), + MSM_RPM_MAP(8960, PM8921_L25_0, PM8921_L25, 2), + MSM_RPM_MAP(8960, PM8921_L26_0, PM8921_L26, 2), + MSM_RPM_MAP(8960, PM8921_L27_0, PM8921_L27, 2), + MSM_RPM_MAP(8960, PM8921_L28_0, PM8921_L28, 2), + MSM_RPM_MAP(8960, PM8921_L29_0, PM8921_L29, 2), + MSM_RPM_MAP(8960, PM8921_CLK1_0, PM8921_CLK1, 2), + MSM_RPM_MAP(8960, PM8921_CLK2_0, PM8921_CLK2, 2), + MSM_RPM_MAP(8960, PM8921_LVS1, PM8921_LVS1, 1), + MSM_RPM_MAP(8960, PM8921_LVS2, PM8921_LVS2, 1), + MSM_RPM_MAP(8960, PM8921_LVS3, PM8921_LVS3, 1), + MSM_RPM_MAP(8960, PM8921_LVS4, PM8921_LVS4, 1), + MSM_RPM_MAP(8960, PM8921_LVS5, PM8921_LVS5, 1), + MSM_RPM_MAP(8960, PM8921_LVS6, PM8921_LVS6, 1), + MSM_RPM_MAP(8960, PM8921_LVS7, PM8921_LVS7, 1), + MSM_RPM_MAP(8960, NCP_0, NCP, 2), + MSM_RPM_MAP(8960, CXO_BUFFERS, CXO_BUFFERS, 1), + MSM_RPM_MAP(8960, USB_OTG_SWITCH, USB_OTG_SWITCH, 1), + MSM_RPM_MAP(8960, HDMI_SWITCH, HDMI_SWITCH, 1), + MSM_RPM_MAP(8960, DDR_DMM_0, DDR_DMM, 2), + MSM_RPM_MAP(8960, QDSS_CLK, QDSS_CLK, 1), + }, + .target_status = { + MSM_RPM_STATUS_ID_MAP(8960, VERSION_MAJOR), + MSM_RPM_STATUS_ID_MAP(8960, VERSION_MINOR), + MSM_RPM_STATUS_ID_MAP(8960, VERSION_BUILD), + MSM_RPM_STATUS_ID_MAP(8960, SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8960, SUPPORTED_RESOURCES_1), + MSM_RPM_STATUS_ID_MAP(8960, SUPPORTED_RESOURCES_2), + MSM_RPM_STATUS_ID_MAP(8960, RESERVED_SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8960, SEQUENCE), + MSM_RPM_STATUS_ID_MAP(8960, RPM_CTL), + MSM_RPM_STATUS_ID_MAP(8960, CXO_CLK), + MSM_RPM_STATUS_ID_MAP(8960, PXO_CLK), + MSM_RPM_STATUS_ID_MAP(8960, APPS_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8960, SYSTEM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8960, MM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8960, DAYTONA_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8960, SFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8960, CFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8960, MMFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8960, EBI1_CLK), + MSM_RPM_STATUS_ID_MAP(8960, APPS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8960, APPS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8960, APPS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8960, APPS_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8960, SYS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8960, SYS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8960, SYS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8960, SYSTEM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8960, MMSS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(8960, MMSS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(8960, MMSS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(8960, MM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S1_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S1_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S2_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S2_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S3_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S3_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S4_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S4_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S5_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S5_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S6_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S6_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S7_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S7_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S8_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_S8_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L1_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L1_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L2_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L2_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L3_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L3_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L4_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L4_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L5_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L5_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L6_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L6_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L7_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L7_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L8_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L8_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L9_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L9_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L10_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L10_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L11_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L11_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L12_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L12_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L13_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L13_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L14_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L14_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L15_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L15_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L16_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L16_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L17_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L17_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L18_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L18_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L19_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L19_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L20_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L20_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L21_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L21_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L22_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L22_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L23_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L23_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L24_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L24_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L25_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L25_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L26_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L26_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L27_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L27_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L28_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L28_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L29_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_L29_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_CLK1_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_CLK1_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_CLK2_0), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_CLK2_1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS1), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS2), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS3), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS4), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS5), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS6), + MSM_RPM_STATUS_ID_MAP(8960, PM8921_LVS7), + MSM_RPM_STATUS_ID_MAP(8960, NCP_0), + MSM_RPM_STATUS_ID_MAP(8960, NCP_1), + MSM_RPM_STATUS_ID_MAP(8960, CXO_BUFFERS), + MSM_RPM_STATUS_ID_MAP(8960, USB_OTG_SWITCH), + MSM_RPM_STATUS_ID_MAP(8960, HDMI_SWITCH), + MSM_RPM_STATUS_ID_MAP(8960, DDR_DMM_0), + MSM_RPM_STATUS_ID_MAP(8960, DDR_DMM_1), + MSM_RPM_STATUS_ID_MAP(8960, EBI1_CH0_RANGE), + MSM_RPM_STATUS_ID_MAP(8960, EBI1_CH1_RANGE), + }, + .target_ctrl_id = { + MSM_RPM_CTRL_MAP(8960, VERSION_MAJOR), + MSM_RPM_CTRL_MAP(8960, VERSION_MINOR), + MSM_RPM_CTRL_MAP(8960, VERSION_BUILD), + MSM_RPM_CTRL_MAP(8960, REQ_CTX_0), + MSM_RPM_CTRL_MAP(8960, REQ_SEL_0), + MSM_RPM_CTRL_MAP(8960, ACK_CTX_0), + MSM_RPM_CTRL_MAP(8960, ACK_SEL_0), + }, + .sel_invalidate = MSM_RPM_8960_SEL_INVALIDATE, + .sel_notification = MSM_RPM_8960_SEL_NOTIFICATION, + .sel_last = MSM_RPM_8960_SEL_LAST, + .ver = {3, 0, 0}, +}; + +struct platform_device msm8960_rpm_device = { + .name = "msm_rpm", + .id = -1, +}; + +static struct msm_rpm_log_platform_data msm_rpm_log_pdata = { + .phys_addr_base = 0x0010C000, + .reg_offsets = { + [MSM_RPM_LOG_PAGE_INDICES] = 0x00000080, + [MSM_RPM_LOG_PAGE_BUFFER] = 0x000000A0, + }, + .phys_size = SZ_8K, + .log_len = 4096, /* log's buffer length in bytes */ + .log_len_mask = (4096 >> 2) - 1, /* length mask in units of u32 */ +}; + +struct platform_device msm8960_rpm_log_device = { + .name = "msm_rpm_log", + .id = -1, + .dev = { + .platform_data = &msm_rpm_log_pdata, + }, +}; + +static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = { + .phys_addr_base = 0x0010D204, + .phys_size = SZ_8K, +}; + +struct platform_device msm8960_rpm_stat_device = { + .name = "msm_rpm_stat", + .id = -1, + .dev = { + .platform_data = &msm_rpm_stat_pdata, + }, +}; + +struct platform_device msm_bus_sys_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM, +}; +struct platform_device msm_bus_apps_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_APPSS, +}; +struct platform_device msm_bus_mm_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_MMSS, +}; +struct platform_device msm_bus_sys_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM_FPB, +}; +struct platform_device msm_bus_cpss_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_CPSS_FPB, +}; + +/* Sensors DSPS platform data */ +#ifdef CONFIG_MSM_DSPS + +#define PPSS_DSPS_TCM_CODE_BASE 0x12000000 +#define PPSS_DSPS_TCM_CODE_SIZE 0x28000 +#define PPSS_DSPS_TCM_BUF_BASE 0x12040000 +#define PPSS_DSPS_TCM_BUF_SIZE 0x4000 +#define PPSS_DSPS_PIPE_BASE 0x12800000 +#define PPSS_DSPS_PIPE_SIZE 0x4000 +#define PPSS_DSPS_DDR_BASE 0x8fe00000 +#define PPSS_DSPS_DDR_SIZE 0x100000 +#define PPSS_SMEM_BASE 0x80000000 +#define PPSS_SMEM_SIZE 0x200000 +#define PPSS_REG_PHYS_BASE 0x12080000 + +static struct dsps_clk_info dsps_clks[] = {}; +static struct dsps_regulator_info dsps_regs[] = {}; + +/* + * Note: GPIOs field is intialized in run-time at the function + * msm8960_init_dsps(). + */ + +struct msm_dsps_platform_data msm_dsps_pdata = { + .clks = dsps_clks, + .clks_num = ARRAY_SIZE(dsps_clks), + .gpios = NULL, + .gpios_num = 0, + .regs = dsps_regs, + .regs_num = ARRAY_SIZE(dsps_regs), + .dsps_pwr_ctl_en = 1, + .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE, + .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE, + .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE, + .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE, + .pipe_start = PPSS_DSPS_PIPE_BASE, + .pipe_size = PPSS_DSPS_PIPE_SIZE, + .ddr_start = PPSS_DSPS_DDR_BASE, + .ddr_size = PPSS_DSPS_DDR_SIZE, + .smem_start = PPSS_SMEM_BASE, + .smem_size = PPSS_SMEM_SIZE, + .signature = DSPS_SIGNATURE, +}; + +static struct resource msm_dsps_resources[] = { + { + .start = PPSS_REG_PHYS_BASE, + .end = PPSS_REG_PHYS_BASE + SZ_8K - 1, + .name = "ppss_reg", + .flags = IORESOURCE_MEM, + }, + { + .start = PPSS_WDOG_TIMER_IRQ, + .end = PPSS_WDOG_TIMER_IRQ, + .name = "ppss_wdog", + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_dsps_device = { + .name = "msm_dsps", + .id = 0, + .num_resources = ARRAY_SIZE(msm_dsps_resources), + .resource = msm_dsps_resources, + .dev.platform_data = &msm_dsps_pdata, +}; + +#endif /* CONFIG_MSM_DSPS */ + +#ifdef CONFIG_MSM_QDSS + +#define MSM_QDSS_PHYS_BASE 0x01A00000 +#define MSM_ETB_PHYS_BASE (MSM_QDSS_PHYS_BASE + 0x1000) +#define MSM_TPIU_PHYS_BASE (MSM_QDSS_PHYS_BASE + 0x3000) +#define MSM_FUNNEL_PHYS_BASE (MSM_QDSS_PHYS_BASE + 0x4000) +#define MSM_ETM_PHYS_BASE (MSM_QDSS_PHYS_BASE + 0x1C000) + +#define QDSS_SOURCE(src_name, fpm) { .name = src_name, .fport_mask = fpm, } + +static struct qdss_source msm_qdss_sources[] = { + QDSS_SOURCE("msm_etm", 0x3), +}; + +static struct msm_qdss_platform_data qdss_pdata = { + .src_table = msm_qdss_sources, + .size = ARRAY_SIZE(msm_qdss_sources), + .afamily = 1, +}; + +struct platform_device msm_qdss_device = { + .name = "msm_qdss", + .id = -1, + .dev = { + .platform_data = &qdss_pdata, + }, +}; + +static struct resource msm_etb_resources[] = { + { + .start = MSM_ETB_PHYS_BASE, + .end = MSM_ETB_PHYS_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_etb_device = { + .name = "msm_etb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_etb_resources), + .resource = msm_etb_resources, +}; + +static struct resource msm_tpiu_resources[] = { + { + .start = MSM_TPIU_PHYS_BASE, + .end = MSM_TPIU_PHYS_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_tpiu_device = { + .name = "msm_tpiu", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tpiu_resources), + .resource = msm_tpiu_resources, +}; + +static struct resource msm_funnel_resources[] = { + { + .start = MSM_FUNNEL_PHYS_BASE, + .end = MSM_FUNNEL_PHYS_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_funnel_device = { + .name = "msm_funnel", + .id = 0, + .num_resources = ARRAY_SIZE(msm_funnel_resources), + .resource = msm_funnel_resources, +}; + +static struct resource msm_etm_resources[] = { + { + .start = MSM_ETM_PHYS_BASE, + .end = MSM_ETM_PHYS_BASE + (SZ_4K * 2) - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_etm_device = { + .name = "msm_etm", + .id = 0, + .num_resources = ARRAY_SIZE(msm_etm_resources), + .resource = msm_etm_resources, +}; + +#endif + +static int msm8960_LPM_latency = 1000; /* >100 usec for WFI */ + +struct platform_device msm8960_cpu_idle_device = { + .name = "msm_cpu_idle", + .id = -1, + .dev = { + .platform_data = &msm8960_LPM_latency, + }, +}; + +static struct msm_dcvs_freq_entry msm8960_freq[] = { + { 384000, 166981, 345600}, + { 702000, 213049, 632502}, + {1026000, 285712, 925613}, + {1242000, 383945, 1176550}, + {1458000, 419729, 1465478}, + {1512000, 434116, 1546674}, + +}; + +static struct msm_dcvs_core_info msm8960_core_info = { + .freq_tbl = &msm8960_freq[0], + .core_param = { + .max_time_us = 100000, + .num_freq = ARRAY_SIZE(msm8960_freq), + }, + .algo_param = { + .slack_time_us = 58000, + .scale_slack_time = 0, + .scale_slack_time_pct = 0, + .disable_pc_threshold = 1458000, + .em_window_size = 100000, + .em_max_util_pct = 97, + .ss_window_size = 1000000, + .ss_util_pct = 95, + .ss_iobusy_conv = 100, + }, +}; + +struct platform_device msm8960_msm_gov_device = { + .name = "msm_dcvs_gov", + .id = -1, + .dev = { + .platform_data = &msm8960_core_info, + }, +}; + +static struct resource msm_cache_erp_resources[] = { + { + .name = "l1_irq", + .start = SC_SICCPUXEXTFAULTIRPTREQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "l2_irq", + .start = APCC_QGICL2IRPTREQ, + .flags = IORESOURCE_IRQ, + } +}; + +struct platform_device msm8960_device_cache_erp = { + .name = "msm_cache_erp", + .id = -1, + .num_resources = ARRAY_SIZE(msm_cache_erp_resources), + .resource = msm_cache_erp_resources, +}; + +struct msm_iommu_domain_name msm8960_iommu_ctx_names[] = { + /* Camera */ + { + .name = "vpe_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vpe_dst", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_imgwr", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_misc", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_dst", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_src", + .domain = CAMERA_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_dst", + .domain = CAMERA_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_src", + .domain = ROTATOR_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_dst", + .domain = ROTATOR_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_mm1", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_b_mm2", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_stream", + .domain = VIDEO_DOMAIN, + }, +}; + +static struct mem_pool msm8960_video_pools[] = { + /* + * Video hardware has the following requirements: + * 1. All video addresses used by the video hardware must be at a higher + * address than video firmware address. + * 2. Video hardware can only access a range of 256MB from the base of + * the video firmware. + */ + [VIDEO_FIRMWARE_POOL] = + /* Low addresses, intended for video firmware */ + { + .paddr = SZ_128K, + .size = SZ_16M - SZ_128K, + }, + [VIDEO_MAIN_POOL] = + /* Main video pool */ + { + .paddr = SZ_16M, + .size = SZ_256M - SZ_16M, + }, + [GEN_POOL] = + /* Remaining address space up to 2G */ + { + .paddr = SZ_256M, + .size = SZ_2G - SZ_256M, + }, +}; + +static struct mem_pool msm8960_camera_pools[] = { + [GEN_POOL] = + /* One address space for camera */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct mem_pool msm8960_display_pools[] = { + [GEN_POOL] = + /* One address space for display */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct mem_pool msm8960_rotator_pools[] = { + [GEN_POOL] = + /* One address space for rotator */ + { + .paddr = SZ_128K, + .size = SZ_2G - SZ_128K, + }, +}; + +static struct msm_iommu_domain msm8960_iommu_domains[] = { + [VIDEO_DOMAIN] = { + .iova_pools = msm8960_video_pools, + .npools = ARRAY_SIZE(msm8960_video_pools), + }, + [CAMERA_DOMAIN] = { + .iova_pools = msm8960_camera_pools, + .npools = ARRAY_SIZE(msm8960_camera_pools), + }, + [DISPLAY_DOMAIN] = { + .iova_pools = msm8960_display_pools, + .npools = ARRAY_SIZE(msm8960_display_pools), + }, + [ROTATOR_DOMAIN] = { + .iova_pools = msm8960_rotator_pools, + .npools = ARRAY_SIZE(msm8960_rotator_pools), + }, +}; + +struct iommu_domains_pdata msm8960_iommu_domain_pdata = { + .domains = msm8960_iommu_domains, + .ndomains = ARRAY_SIZE(msm8960_iommu_domains), + .domain_names = msm8960_iommu_ctx_names, + .nnames = ARRAY_SIZE(msm8960_iommu_ctx_names), + .domain_alloc_flags = 0, +}; + +struct platform_device msm8960_iommu_domain_device = { + .name = "iommu_domains", + .id = -1, + .dev = { + .platform_data = &msm8960_iommu_domain_pdata, + } +}; + +struct msm_rtb_platform_data msm8960_rtb_pdata = { + .size = SZ_1M, +}; + +static int __init msm_rtb_set_buffer_size(char *p) +{ + int s; + + s = memparse(p, NULL); + msm8960_rtb_pdata.size = ALIGN(s, SZ_4K); + return 0; +} +early_param("msm_rtb_size", msm_rtb_set_buffer_size); + + +struct platform_device msm8960_rtb_device = { + .name = "msm_rtb", + .id = -1, + .dev = { + .platform_data = &msm8960_rtb_pdata, + }, +}; + +#define MSM_8960_L1_SIZE SZ_1M +/* + * The actual L2 size is smaller but we need a larger buffer + * size to store other dump information + */ +#define MSM_8960_L2_SIZE SZ_4M + +struct msm_cache_dump_platform_data msm8960_cache_dump_pdata = { + .l2_size = MSM_8960_L2_SIZE, + .l1_size = MSM_8960_L1_SIZE, +}; + +struct platform_device msm8960_cache_dump_device = { + .name = "msm_cache_dump", + .id = -1, + .dev = { + .platform_data = &msm8960_cache_dump_pdata, + }, +}; + +#define MDM2AP_ERRFATAL 40 +#define AP2MDM_ERRFATAL 80 +#define MDM2AP_STATUS 24 +#define AP2MDM_STATUS 77 +#define AP2MDM_PMIC_PWR_EN 22 +#define AP2MDM_KPDPWR_N 79 +#define AP2MDM_SOFT_RESET 78 + +static struct resource sglte_resources[] = { + { + .start = MDM2AP_ERRFATAL, + .end = MDM2AP_ERRFATAL, + .name = "MDM2AP_ERRFATAL", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_ERRFATAL, + .end = AP2MDM_ERRFATAL, + .name = "AP2MDM_ERRFATAL", + .flags = IORESOURCE_IO, + }, + { + .start = MDM2AP_STATUS, + .end = MDM2AP_STATUS, + .name = "MDM2AP_STATUS", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_STATUS, + .end = AP2MDM_STATUS, + .name = "AP2MDM_STATUS", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_PMIC_PWR_EN, + .end = AP2MDM_PMIC_PWR_EN, + .name = "AP2MDM_PMIC_PWR_EN", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_KPDPWR_N, + .end = AP2MDM_KPDPWR_N, + .name = "AP2MDM_KPDPWR_N", + .flags = IORESOURCE_IO, + }, + { + .start = AP2MDM_SOFT_RESET, + .end = AP2MDM_SOFT_RESET, + .name = "AP2MDM_SOFT_RESET", + .flags = IORESOURCE_IO, + }, +}; + +struct platform_device mdm_sglte_device = { + .name = "mdm2_modem", + .id = -1, + .num_resources = ARRAY_SIZE(sglte_resources), + .resource = sglte_resources, +}; diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c new file mode 100644 index 00000000000..4677a1cd92f --- /dev/null +++ b/arch/arm/mach-msm/devices-9615.c @@ -0,0 +1,1418 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pm.h" +#include "devices.h" +#include +#include "spm.h" +#include "rpm_resources.h" +#include "msm_watchdog.h" +#include "rpm_stats.h" +#include "rpm_log.h" + +/* Address of GSBI blocks */ +#define MSM_GSBI1_PHYS 0x16000000 +#define MSM_GSBI2_PHYS 0x16100000 +#define MSM_GSBI3_PHYS 0x16200000 +#define MSM_GSBI4_PHYS 0x16300000 +#define MSM_GSBI5_PHYS 0x16400000 + +#define MSM_UART4DM_PHYS (MSM_GSBI4_PHYS + 0x40000) + +/* GSBI QUP devices */ +#define MSM_GSBI1_QUP_PHYS (MSM_GSBI1_PHYS + 0x80000) +#define MSM_GSBI2_QUP_PHYS (MSM_GSBI2_PHYS + 0x80000) +#define MSM_GSBI3_QUP_PHYS (MSM_GSBI3_PHYS + 0x80000) +#define MSM_GSBI4_QUP_PHYS (MSM_GSBI4_PHYS + 0x80000) +#define MSM_GSBI5_QUP_PHYS (MSM_GSBI5_PHYS + 0x80000) +#define MSM_QUP_SIZE SZ_4K + +/* Address of SSBI CMD */ +#define MSM_PMIC1_SSBI_CMD_PHYS 0x00500000 +#define MSM_PMIC_SSBI_SIZE SZ_4K + +#define MSM_GPIO_I2C_CLK 16 +#define MSM_GPIO_I2C_SDA 17 + +static struct msm_watchdog_pdata msm_watchdog_pdata = { + .pet_time = 10000, + .bark_time = 11000, + .has_secure = false, + .use_kernel_fiq = true, +}; + +struct platform_device msm9615_device_watchdog = { + .name = "msm_watchdog", + .id = -1, + .dev = { + .platform_data = &msm_watchdog_pdata, + }, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = ADM_0_SCSS_1_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x18320000, + .end = 0x18320000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 1, + .sd_size = 0x800, +}; + +struct platform_device msm9615_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +#define MSM_USB_BAM_BASE 0x12502000 +#define MSM_USB_BAM_SIZE SZ_16K +#define MSM_HSIC_BAM_BASE 0x12542000 +#define MSM_HSIC_BAM_SIZE SZ_16K + +static struct resource resources_otg[] = { + { + .start = MSM9615_HSUSB_PHYS, + .end = MSM9615_HSUSB_PHYS + MSM9615_HSUSB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_HSUSB_RESUME_GPIO 79 + +static struct resource resources_hsusb[] = { + { + .start = MSM9615_HSUSB_PHYS, + .end = MSM9615_HSUSB_PHYS + MSM9615_HSUSB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_HSUSB_RESUME_GPIO, + .end = MSM_HSUSB_RESUME_GPIO, + .name = "USB_RESUME", + .flags = IORESOURCE_IO, + }, +}; + +static struct resource resources_usb_bam[] = { + { + .name = "usb_bam_addr", + .start = MSM_USB_BAM_BASE, + .end = MSM_USB_BAM_BASE + MSM_USB_BAM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "usb_bam_irq", + .start = USB1_HS_BAM_IRQ, + .end = USB1_HS_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "hsic_bam_addr", + .start = MSM_HSIC_BAM_BASE, + .end = MSM_HSIC_BAM_BASE + MSM_HSIC_BAM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "hsic_bam_irq", + .start = USB_HSIC_BAM_IRQ, + .end = USB_HSIC_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_usb_bam = { + .name = "usb_bam", + .id = -1, + .num_resources = ARRAY_SIZE(resources_usb_bam), + .resource = resources_usb_bam, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb), + .resource = resources_hsusb, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource resources_hsic_peripheral[] = { + { + .start = MSM9615_HSIC_PHYS, + .end = MSM9615_HSIC_PHYS + MSM9615_HSIC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB_HSIC_IRQ, + .end = USB_HSIC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsic_peripheral = { + .name = "msm_hsic_peripheral", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsic_peripheral), + .resource = resources_hsic_peripheral, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM9615_HSUSB_PHYS, + .end = MSM9615_HSUSB_PHYS + MSM9615_HSUSB_PHYS - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = DMA_BIT_MASK(32); +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_hsic_host[] = { + { + .start = MSM9615_HSIC_PHYS, + .end = MSM9615_HSIC_PHYS + MSM9615_HSIC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB_HSIC_IRQ, + .end = USB_HSIC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsic_host = { + .name = "msm_hsic_host", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsic_host), + .resource = resources_hsic_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_uart_gsbi4[] = { + { + .start = GSBI4_UARTDM_IRQ, + .end = GSBI4_UARTDM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART4DM_PHYS, + .end = MSM_UART4DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_GSBI4_PHYS, + .end = MSM_GSBI4_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm9615_device_uart_gsbi4 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart_gsbi4), + .resource = resources_uart_gsbi4, +}; + +static struct resource resources_qup_i2c_gsbi5[] = { + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI5_PHYS, + .end = MSM_GSBI5_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_phys_addr", + .start = MSM_GSBI5_QUP_PHYS, + .end = MSM_GSBI5_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI5_QUP_IRQ, + .end = GSBI5_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = MSM_GPIO_I2C_CLK, + .end = MSM_GPIO_I2C_CLK, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = MSM_GPIO_I2C_SDA, + .end = MSM_GPIO_I2C_SDA, + .flags = IORESOURCE_IO, + + }, +}; + +struct platform_device msm9615_device_qup_i2c_gsbi5 = { + .name = "qup_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_qup_i2c_gsbi5), + .resource = resources_qup_i2c_gsbi5, +}; + +static struct resource resources_qup_spi_gsbi3[] = { + { + .name = "spi_base", + .start = MSM_GSBI3_QUP_PHYS, + .end = MSM_GSBI3_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_base", + .start = MSM_GSBI3_PHYS, + .end = MSM_GSBI3_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spi_irq_in", + .start = GSBI3_QUP_IRQ, + .end = GSBI3_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm9615_device_qup_spi_gsbi3 = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(resources_qup_spi_gsbi3), + .resource = resources_qup_spi_gsbi3, +}; + +#define LPASS_SLIMBUS_PHYS 0x28080000 +#define LPASS_SLIMBUS_BAM_PHYS 0x28084000 +#define LPASS_SLIMBUS_SLEW (MSM9615_TLMM_PHYS + 0x207C) +/* Board info for the slimbus slave device */ +static struct resource slimbus_res[] = { + { + .start = LPASS_SLIMBUS_PHYS, + .end = LPASS_SLIMBUS_PHYS + 8191, + .flags = IORESOURCE_MEM, + .name = "slimbus_physical", + }, + { + .start = LPASS_SLIMBUS_BAM_PHYS, + .end = LPASS_SLIMBUS_BAM_PHYS + 8191, + .flags = IORESOURCE_MEM, + .name = "slimbus_bam_physical", + }, + { + .start = LPASS_SLIMBUS_SLEW, + .end = LPASS_SLIMBUS_SLEW + 4 - 1, + .flags = IORESOURCE_MEM, + .name = "slimbus_slew_reg", + }, + { + .start = SLIMBUS0_CORE_EE1_IRQ, + .end = SLIMBUS0_CORE_EE1_IRQ, + .flags = IORESOURCE_IRQ, + .name = "slimbus_irq", + }, + { + .start = SLIMBUS0_BAM_EE1_IRQ, + .end = SLIMBUS0_BAM_EE1_IRQ, + .flags = IORESOURCE_IRQ, + .name = "slimbus_bam_irq", + }, +}; + +struct platform_device msm9615_slim_ctrl = { + .name = "msm_slim_ctrl", + .id = 1, + .num_resources = ARRAY_SIZE(slimbus_res), + .resource = slimbus_res, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device msm_pcm = { + .name = "msm-pcm-dsp", + .id = -1, +}; + +struct platform_device msm_multi_ch_pcm = { + .name = "msm-multi-ch-pcm-dsp", + .id = -1, +}; + +struct platform_device msm_pcm_routing = { + .name = "msm-pcm-routing", + .id = -1, +}; + +struct platform_device msm_cpudai0 = { + .name = "msm-dai-q6", + .id = 0x4000, +}; + +struct platform_device msm_cpudai1 = { + .name = "msm-dai-q6", + .id = 0x4001, +}; + +struct platform_device msm_cpudai_bt_rx = { + .name = "msm-dai-q6", + .id = 0x3000, +}; + +struct platform_device msm_cpudai_bt_tx = { + .name = "msm-dai-q6", + .id = 0x3001, +}; + +/* + * Machine specific data for AUX PCM Interface + * which the driver will be unware of. + */ +struct msm_dai_auxpcm_pdata auxpcm_pdata = { + .clk = "pcm_clk", + .mode_8k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 2048000, + }, + .mode_16k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 4096000, + } +}; + +struct platform_device msm_cpudai_auxpcm_rx = { + .name = "msm-dai-q6", + .id = 2, + .dev = { + .platform_data = &auxpcm_pdata, + }, +}; + +struct platform_device msm_cpudai_auxpcm_tx = { + .name = "msm-dai-q6", + .id = 3, + .dev = { + .platform_data = &auxpcm_pdata, + }, +}; + +struct msm_dai_auxpcm_pdata sec_auxpcm_pdata = { + .clk = "sec_pcm_clk", + .mode_8k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 2048000, + }, + .mode_16k = { + .mode = AFE_PCM_CFG_MODE_PCM, + .sync = AFE_PCM_CFG_SYNC_INT, + .frame = AFE_PCM_CFG_FRM_256BPF, + .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD, + .slot = 0, + .data = AFE_PCM_CFG_CDATAOE_MASTER, + .pcm_clk_rate = 4096000, + } +}; + +struct platform_device msm_cpudai_sec_auxpcm_rx = { + .name = "msm-dai-q6", + .id = 12, + .dev = { + .platform_data = &sec_auxpcm_pdata, + }, +}; + +struct platform_device msm_cpudai_sec_auxpcm_tx = { + .name = "msm-dai-q6", + .id = 13, + .dev = { + .platform_data = &sec_auxpcm_pdata, + }, +}; + +struct platform_device msm_cpu_fe = { + .name = "msm-dai-fe", + .id = -1, +}; + +struct platform_device msm_stub_codec = { + .name = "msm-stub-codec", + .id = 1, +}; + +struct platform_device msm_voice = { + .name = "msm-pcm-voice", + .id = -1, +}; + +struct platform_device msm_i2s_cpudai0 = { + .name = "msm-dai-q6", + .id = PRIMARY_I2S_RX, +}; + +struct platform_device msm_i2s_cpudai1 = { + .name = "msm-dai-q6", + .id = PRIMARY_I2S_TX, +}; +struct platform_device msm_voip = { + .name = "msm-voip-dsp", + .id = -1, +}; + +struct platform_device msm_compr_dsp = { + .name = "msm-compr-dsp", + .id = -1, +}; + +struct platform_device msm_pcm_hostless = { + .name = "msm-pcm-hostless", + .id = -1, +}; + +struct platform_device msm_cpudai_afe_01_rx = { + .name = "msm-dai-q6", + .id = 0xE0, +}; + +struct platform_device msm_cpudai_afe_01_tx = { + .name = "msm-dai-q6", + .id = 0xF0, +}; + +struct platform_device msm_cpudai_afe_02_rx = { + .name = "msm-dai-q6", + .id = 0xF1, +}; + +struct platform_device msm_cpudai_afe_02_tx = { + .name = "msm-dai-q6", + .id = 0xE1, +}; + +struct platform_device msm_pcm_afe = { + .name = "msm-pcm-afe", + .id = -1, +}; + +static struct resource resources_ssbi_pmic1[] = { + { + .start = MSM_PMIC1_SSBI_CMD_PHYS, + .end = MSM_PMIC1_SSBI_CMD_PHYS + MSM_PMIC_SSBI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm9615_device_ssbi_pmic1 = { + .name = "msm_ssbi", + .id = 0, + .resource = resources_ssbi_pmic1, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic1), +}; + +static struct resource resources_sps[] = { + { + .name = "pipe_mem", + .start = 0x12800000, + .end = 0x12800000 + 0x4000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_dma", + .start = 0x12240000, + .end = 0x12240000 + 0x1000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_bam", + .start = 0x12244000, + .end = 0x12244000 + 0x4000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "bamdma_irq", + .start = SPS_BAM_DMA_IRQ, + .end = SPS_BAM_DMA_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_sps_platform_data msm_sps_pdata = { + .bamdma_restricted_pipes = 0x06, +}; + +struct platform_device msm_device_sps = { + .name = "msm_sps", + .id = -1, + .num_resources = ARRAY_SIZE(resources_sps), + .resource = resources_sps, + .dev.platform_data = &msm_sps_pdata, +}; + +#define MSM_NAND_PHYS 0x1B400000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, +}; + +struct flash_platform_data msm_nand_data = { + .version = VERSION_2, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +struct platform_device msm_device_bam_dmux = { + .name = "BAM_RMNT", + .id = -1, +}; + +#ifdef CONFIG_HW_RANDOM_MSM +/* PRNG device */ +#define MSM_PRNG_PHYS 0x1A500000 +static struct resource rng_resources = { + .flags = IORESOURCE_MEM, + .start = MSM_PRNG_PHYS, + .end = MSM_PRNG_PHYS + SZ_512 - 1, +}; + +struct platform_device msm_device_rng = { + .name = "msm_rng", + .id = 0, + .num_resources = 1, + .resource = &rng_resources, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +#define QCE_SIZE 0x10000 +#define QCE_0_BASE 0x18500000 + +#define QCE_HW_KEY_SUPPORT 0 +#define QCE_SHA_HMAC_SUPPORT 1 +#define QCE_SHARE_CE_RESOURCE 1 +#define QCE_CE_SHARED 0 + +static struct resource qcrypto_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource qcedev_resources[] = { + [0] = { + .start = QCE_0_BASE, + .end = QCE_0_BASE + QCE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "crypto_channels", + .start = DMOV_CE_IN_CHAN, + .end = DMOV_CE_OUT_CHAN, + .flags = IORESOURCE_DMA, + }, + [2] = { + .name = "crypto_crci_in", + .start = DMOV_CE_IN_CRCI, + .end = DMOV_CE_IN_CRCI, + .flags = IORESOURCE_DMA, + }, + [3] = { + .name = "crypto_crci_out", + .start = DMOV_CE_OUT_CRCI, + .end = DMOV_CE_OUT_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) + +static struct msm_ce_hw_support qcrypto_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +struct platform_device msm9615_qcrypto_device = { + .name = "qcrypto", + .id = 0, + .num_resources = ARRAY_SIZE(qcrypto_resources), + .resource = qcrypto_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcrypto_ce_hw_suppport, + }, +}; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) + +static struct msm_ce_hw_support qcedev_ce_hw_suppport = { + .ce_shared = QCE_CE_SHARED, + .shared_ce_resource = QCE_SHARE_CE_RESOURCE, + .hw_key_support = QCE_HW_KEY_SUPPORT, + .sha_hmac = QCE_SHA_HMAC_SUPPORT, + .bus_scale_table = NULL, +}; + +struct platform_device msm9615_qcedev_device = { + .name = "qce", + .id = 0, + .num_resources = ARRAY_SIZE(qcedev_resources), + .resource = qcedev_resources, + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &qcedev_ce_hw_suppport, + }, +}; +#endif + +#define MSM_SDC1_BASE 0x12180000 +#define MSM_SDC1_DML_BASE (MSM_SDC1_BASE + 0x800) +#define MSM_SDC1_BAM_BASE (MSM_SDC1_BASE + 0x2000) +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC2_DML_BASE (MSM_SDC2_BASE + 0x800) +#define MSM_SDC2_BAM_BASE (MSM_SDC2_BASE + 0x2000) + +static struct resource resources_sdc1[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC1_IRQ_0, + .end = SDC1_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC1_DML_BASE, + .end = MSM_SDC1_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC1_BAM_BASE, + .end = MSM_SDC1_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC1_BAM_IRQ, + .end = SDC1_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +static struct resource resources_sdc2[] = { + { + .name = "core_mem", + .flags = IORESOURCE_MEM, + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_DML_BASE - 1, + }, + { + .name = "core_irq", + .flags = IORESOURCE_IRQ, + .start = SDC2_IRQ_0, + .end = SDC2_IRQ_0 + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC2_DML_BASE, + .end = MSM_SDC2_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC2_BAM_BASE, + .end = MSM_SDC2_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC2_BAM_IRQ, + .end = SDC2_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 2) + return -EINVAL; + + pdev = msm_sdcc_devices[controller - 1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#ifdef CONFIG_FB_MSM_EBI2 +static struct resource msm_ebi2_lcdc_resources[] = { + { + .name = "base", + .start = 0x1B300000, + .end = 0x1B300000 + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd01", + .start = 0x1FC00000, + .end = 0x1FC00000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_ebi2_lcdc_device = { + .name = "ebi2_lcd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_ebi2_lcdc_resources), + .resource = msm_ebi2_lcdc_resources, +}; +#endif + +#ifdef CONFIG_CACHE_L2X0 +static int __init l2x0_cache_init(void) +{ + int aux_ctrl = 0; + + /* Way Size 010(0x2) 32KB */ + aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \ + (0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \ + (0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT); + + /* L2 Latency setting required by hardware. Default is 0x20 + which is no good. + */ + writel_relaxed(0x220, MSM_L2CC_BASE + L2X0_DATA_LATENCY_CTRL); + l2x0_init(MSM_L2CC_BASE, aux_ctrl, L2X0_AUX_CTRL_MASK); + + return 0; +} +#else +static int __init l2x0_cache_init(void){ return 0; } +#endif + +struct msm_rpm_platform_data msm9615_rpm_data __initdata = { + .reg_base_addrs = { + [MSM_RPM_PAGE_STATUS] = MSM_RPM_BASE, + [MSM_RPM_PAGE_CTRL] = MSM_RPM_BASE + 0x400, + [MSM_RPM_PAGE_REQ] = MSM_RPM_BASE + 0x600, + [MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00, + }, + .irq_ack = RPM_APCC_CPU0_GP_HIGH_IRQ, + .irq_err = RPM_APCC_CPU0_GP_LOW_IRQ, + .irq_wakeup = RPM_APCC_CPU0_WAKE_UP_IRQ, + .ipc_rpm_reg = MSM_APCS_GCC_BASE + 0x008, + .ipc_rpm_val = 4, + .target_id = { + MSM_RPM_MAP(9615, NOTIFICATION_CONFIGURED_0, NOTIFICATION, 4), + MSM_RPM_MAP(9615, NOTIFICATION_REGISTERED_0, NOTIFICATION, 4), + MSM_RPM_MAP(9615, INVALIDATE_0, INVALIDATE, 8), + MSM_RPM_MAP(9615, TRIGGER_TIMED_TO, TRIGGER_TIMED, 1), + MSM_RPM_MAP(9615, TRIGGER_TIMED_SCLK_COUNT, TRIGGER_TIMED, 1), + MSM_RPM_MAP(9615, RPM_CTL, RPM_CTL, 1), + MSM_RPM_MAP(9615, CXO_CLK, CXO_CLK, 1), + MSM_RPM_MAP(9615, SYSTEM_FABRIC_CLK, SYSTEM_FABRIC_CLK, 1), + MSM_RPM_MAP(9615, DAYTONA_FABRIC_CLK, DAYTONA_FABRIC_CLK, 1), + MSM_RPM_MAP(9615, SFPB_CLK, SFPB_CLK, 1), + MSM_RPM_MAP(9615, CFPB_CLK, CFPB_CLK, 1), + MSM_RPM_MAP(9615, EBI1_CLK, EBI1_CLK, 1), + MSM_RPM_MAP(9615, SYS_FABRIC_CFG_HALT_0, + SYS_FABRIC_CFG_HALT, 2), + MSM_RPM_MAP(9615, SYS_FABRIC_CFG_CLKMOD_0, + SYS_FABRIC_CFG_CLKMOD, 3), + MSM_RPM_MAP(9615, SYS_FABRIC_CFG_IOCTL, + SYS_FABRIC_CFG_IOCTL, 1), + MSM_RPM_MAP(9615, SYSTEM_FABRIC_ARB_0, + SYSTEM_FABRIC_ARB, 27), + MSM_RPM_MAP(9615, PM8018_S1_0, PM8018_S1, 2), + MSM_RPM_MAP(9615, PM8018_S2_0, PM8018_S2, 2), + MSM_RPM_MAP(9615, PM8018_S3_0, PM8018_S3, 2), + MSM_RPM_MAP(9615, PM8018_S4_0, PM8018_S4, 2), + MSM_RPM_MAP(9615, PM8018_S5_0, PM8018_S5, 2), + MSM_RPM_MAP(9615, PM8018_L1_0, PM8018_L1, 2), + MSM_RPM_MAP(9615, PM8018_L2_0, PM8018_L2, 2), + MSM_RPM_MAP(9615, PM8018_L3_0, PM8018_L3, 2), + MSM_RPM_MAP(9615, PM8018_L4_0, PM8018_L4, 2), + MSM_RPM_MAP(9615, PM8018_L5_0, PM8018_L5, 2), + MSM_RPM_MAP(9615, PM8018_L6_0, PM8018_L6, 2), + MSM_RPM_MAP(9615, PM8018_L7_0, PM8018_L7, 2), + MSM_RPM_MAP(9615, PM8018_L8_0, PM8018_L8, 2), + MSM_RPM_MAP(9615, PM8018_L9_0, PM8018_L9, 2), + MSM_RPM_MAP(9615, PM8018_L10_0, PM8018_L10, 2), + MSM_RPM_MAP(9615, PM8018_L11_0, PM8018_L11, 2), + MSM_RPM_MAP(9615, PM8018_L12_0, PM8018_L12, 2), + MSM_RPM_MAP(9615, PM8018_L13_0, PM8018_L13, 2), + MSM_RPM_MAP(9615, PM8018_L14_0, PM8018_L14, 2), + MSM_RPM_MAP(9615, PM8018_LVS1, PM8018_LVS1, 1), + MSM_RPM_MAP(9615, NCP_0, NCP, 2), + MSM_RPM_MAP(9615, CXO_BUFFERS, CXO_BUFFERS, 1), + MSM_RPM_MAP(9615, USB_OTG_SWITCH, USB_OTG_SWITCH, 1), + MSM_RPM_MAP(9615, HDMI_SWITCH, HDMI_SWITCH, 1), + }, + .target_status = { + MSM_RPM_STATUS_ID_MAP(9615, VERSION_MAJOR), + MSM_RPM_STATUS_ID_MAP(9615, VERSION_MINOR), + MSM_RPM_STATUS_ID_MAP(9615, VERSION_BUILD), + MSM_RPM_STATUS_ID_MAP(9615, SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(9615, SUPPORTED_RESOURCES_1), + MSM_RPM_STATUS_ID_MAP(9615, SUPPORTED_RESOURCES_2), + MSM_RPM_STATUS_ID_MAP(9615, RESERVED_SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(9615, SEQUENCE), + MSM_RPM_STATUS_ID_MAP(9615, RPM_CTL), + MSM_RPM_STATUS_ID_MAP(9615, CXO_CLK), + MSM_RPM_STATUS_ID_MAP(9615, SYSTEM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(9615, DAYTONA_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(9615, SFPB_CLK), + MSM_RPM_STATUS_ID_MAP(9615, CFPB_CLK), + MSM_RPM_STATUS_ID_MAP(9615, EBI1_CLK), + MSM_RPM_STATUS_ID_MAP(9615, SYS_FABRIC_CFG_HALT), + MSM_RPM_STATUS_ID_MAP(9615, SYS_FABRIC_CFG_CLKMOD), + MSM_RPM_STATUS_ID_MAP(9615, SYS_FABRIC_CFG_IOCTL), + MSM_RPM_STATUS_ID_MAP(9615, SYSTEM_FABRIC_ARB), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S1_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S1_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S2_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S2_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S3_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S3_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S4_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S4_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S5_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_S5_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L1_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L1_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L2_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L2_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L3_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L3_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L4_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L4_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L5_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L5_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L6_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L6_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L7_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L7_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L8_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L8_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L9_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L9_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L10_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L10_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L11_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L11_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L12_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L12_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L13_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L13_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L14_0), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_L14_1), + MSM_RPM_STATUS_ID_MAP(9615, PM8018_LVS1), + MSM_RPM_STATUS_ID_MAP(9615, NCP_0), + MSM_RPM_STATUS_ID_MAP(9615, NCP_1), + MSM_RPM_STATUS_ID_MAP(9615, CXO_BUFFERS), + MSM_RPM_STATUS_ID_MAP(9615, USB_OTG_SWITCH), + MSM_RPM_STATUS_ID_MAP(9615, HDMI_SWITCH), + }, + .target_ctrl_id = { + MSM_RPM_CTRL_MAP(9615, VERSION_MAJOR), + MSM_RPM_CTRL_MAP(9615, VERSION_MINOR), + MSM_RPM_CTRL_MAP(9615, VERSION_BUILD), + MSM_RPM_CTRL_MAP(9615, REQ_CTX_0), + MSM_RPM_CTRL_MAP(9615, REQ_SEL_0), + MSM_RPM_CTRL_MAP(9615, ACK_CTX_0), + MSM_RPM_CTRL_MAP(9615, ACK_SEL_0), + }, + .sel_invalidate = MSM_RPM_9615_SEL_INVALIDATE, + .sel_notification = MSM_RPM_9615_SEL_NOTIFICATION, + .sel_last = MSM_RPM_9615_SEL_LAST, + .ver = {3, 0, 0}, +}; + +struct platform_device msm9615_rpm_device = { + .name = "msm_rpm", + .id = -1, +}; + +static uint16_t msm_mpm_irqs_m2a[MSM_MPM_NR_MPM_IRQS] __initdata = { + [4] = MSM_GPIO_TO_INT(30), + [5] = MSM_GPIO_TO_INT(59), + [6] = MSM_GPIO_TO_INT(81), + [7] = MSM_GPIO_TO_INT(87), + [8] = MSM_GPIO_TO_INT(86), + [9] = MSM_GPIO_TO_INT(2), + [10] = MSM_GPIO_TO_INT(6), + [11] = MSM_GPIO_TO_INT(10), + [12] = MSM_GPIO_TO_INT(14), + [13] = MSM_GPIO_TO_INT(18), + [14] = MSM_GPIO_TO_INT(7), + [15] = MSM_GPIO_TO_INT(11), + [16] = MSM_GPIO_TO_INT(15), + [19] = MSM_GPIO_TO_INT(26), + [20] = MSM_GPIO_TO_INT(28), + [22] = USB_HSIC_IRQ, + [23] = MSM_GPIO_TO_INT(19), + [24] = MSM_GPIO_TO_INT(23), + [26] = MSM_GPIO_TO_INT(3), + [27] = MSM_GPIO_TO_INT(68), + [29] = MSM_GPIO_TO_INT(78), + [31] = MSM_GPIO_TO_INT(0), + [32] = MSM_GPIO_TO_INT(4), + [33] = MSM_GPIO_TO_INT(22), + [34] = MSM_GPIO_TO_INT(17), + [37] = MSM_GPIO_TO_INT(20), + [39] = MSM_GPIO_TO_INT(84), + [40] = USB1_HS_IRQ, + [42] = MSM_GPIO_TO_INT(24), + [43] = MSM_GPIO_TO_INT(79), + [44] = MSM_GPIO_TO_INT(80), + [45] = MSM_GPIO_TO_INT(82), + [46] = MSM_GPIO_TO_INT(85), + [47] = MSM_GPIO_TO_INT(45), + [48] = MSM_GPIO_TO_INT(50), + [49] = MSM_GPIO_TO_INT(51), + [50] = MSM_GPIO_TO_INT(69), + [51] = MSM_GPIO_TO_INT(77), + [52] = MSM_GPIO_TO_INT(1), + [53] = MSM_GPIO_TO_INT(5), + [54] = MSM_GPIO_TO_INT(40), + [55] = MSM_GPIO_TO_INT(27), +}; + +static uint16_t msm_mpm_bypassed_apps_irqs[] __initdata = { + TLMM_MSM_SUMMARY_IRQ, + RPM_APCC_CPU0_GP_HIGH_IRQ, + RPM_APCC_CPU0_GP_MEDIUM_IRQ, + RPM_APCC_CPU0_GP_LOW_IRQ, + RPM_APCC_CPU0_WAKE_UP_IRQ, + MSS_TO_APPS_IRQ_0, + MSS_TO_APPS_IRQ_1, + LPASS_SCSS_GP_LOW_IRQ, + LPASS_SCSS_GP_MEDIUM_IRQ, + LPASS_SCSS_GP_HIGH_IRQ, + SPS_MTI_31, + A2_BAM_IRQ, +}; + +struct msm_mpm_device_data msm9615_mpm_dev_data __initdata = { + .irqs_m2a = msm_mpm_irqs_m2a, + .irqs_m2a_size = ARRAY_SIZE(msm_mpm_irqs_m2a), + .bypassed_apps_irqs = msm_mpm_bypassed_apps_irqs, + .bypassed_apps_irqs_size = ARRAY_SIZE(msm_mpm_bypassed_apps_irqs), + .mpm_request_reg_base = MSM_RPM_BASE + 0x9d8, + .mpm_status_reg_base = MSM_RPM_BASE + 0xdf8, + .mpm_apps_ipc_reg = MSM_APCS_GCC_BASE + 0x008, + .mpm_apps_ipc_val = BIT(1), + .mpm_ipc_irq = RPM_APCC_CPU0_GP_MEDIUM_IRQ, +}; + +static uint8_t spm_wfi_cmd_sequence[] __initdata = { + 0x00, 0x03, 0x00, 0x0f, +}; + +static uint8_t spm_power_collapse_without_rpm[] __initdata = { + 0x34, 0x24, 0x14, 0x04, + 0x54, 0x03, 0x54, 0x04, + 0x14, 0x24, 0x3e, 0x0f, +}; + +static uint8_t spm_power_collapse_with_rpm[] __initdata = { + 0x34, 0x24, 0x14, 0x04, + 0x54, 0x07, 0x54, 0x04, + 0x14, 0x24, 0x3e, 0x0f, +}; + +static struct msm_spm_seq_entry msm_spm_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_MODE_CLOCK_GATING, + .notify_rpm = false, + .cmd = spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = false, + .cmd = spm_power_collapse_without_rpm, + }, + [2] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = true, + .cmd = spm_power_collapse_with_rpm, + }, +}; + +static struct msm_spm_platform_data msm_spm_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1001, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, +}; + +static struct msm_rpmrs_level msm_rpmrs_levels[] __initdata = { + { + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 100, 8000, 100000, 1, + }, + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + true, + 2000, 5000, 60100000, 3000, + }, + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(ON, ACTIVE, MAX, ACTIVE), + false, + 6300, 5000, 60350000, 3500, + }, + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, MAX, ACTIVE), + false, + 13300, 2000, 71850000, 6800, + }, + { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_RPMRS_LIMITS(OFF, HSFS_OPEN, RET_HIGH, RET_LOW), + false, + 28300, 0, 76350000, 9800, + }, +}; + +static struct msm_rpmrs_platform_data msm_rpmrs_data __initdata = { + .levels = &msm_rpmrs_levels[0], + .num_levels = ARRAY_SIZE(msm_rpmrs_levels), + .vdd_mem_levels = { + [MSM_RPMRS_VDD_MEM_RET_LOW] = 750000, + [MSM_RPMRS_VDD_MEM_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_MEM_ACTIVE] = 1050000, + [MSM_RPMRS_VDD_MEM_MAX] = 1150000, + }, + .vdd_dig_levels = { + [MSM_RPMRS_VDD_DIG_RET_LOW] = 500000, + [MSM_RPMRS_VDD_DIG_RET_HIGH] = 750000, + [MSM_RPMRS_VDD_DIG_ACTIVE] = 950000, + [MSM_RPMRS_VDD_DIG_MAX] = 1150000, + }, + .vdd_mask = 0x7FFFFF, + .rpmrs_target_id = { + [MSM_RPMRS_ID_PXO_CLK] = MSM_RPM_ID_CXO_CLK, + [MSM_RPMRS_ID_L2_CACHE_CTL] = MSM_RPM_ID_LAST, + [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_PM8018_S1_0, + [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_PM8018_S1_1, + [MSM_RPMRS_ID_VDD_MEM_0] = MSM_RPM_ID_PM8018_L9_0, + [MSM_RPMRS_ID_VDD_MEM_1] = MSM_RPM_ID_PM8018_L9_1, + [MSM_RPMRS_ID_RPM_CTL] = MSM_RPM_ID_RPM_CTL, + }, +}; + +static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = { + .phys_addr_base = 0x0010D204, + .phys_size = SZ_8K, +}; + +struct platform_device msm9615_rpm_stat_device = { + .name = "msm_rpm_stat", + .id = -1, + .dev = { + .platform_data = &msm_rpm_stat_pdata, + }, +}; + +static struct msm_rpm_log_platform_data msm_rpm_log_pdata = { + .phys_addr_base = 0x0010AC00, + .reg_offsets = { + [MSM_RPM_LOG_PAGE_INDICES] = 0x00000080, + [MSM_RPM_LOG_PAGE_BUFFER] = 0x000000A0, + }, + .phys_size = SZ_8K, + .log_len = 4096, /* log's buffer length in bytes */ + .log_len_mask = (4096 >> 2) - 1, /* length mask in units of u32 */ +}; + +struct platform_device msm9615_rpm_log_device = { + .name = "msm_rpm_log", + .id = -1, + .dev = { + .platform_data = &msm_rpm_log_pdata, + }, +}; + +uint32_t __init msm9615_rpm_get_swfi_latency(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msm_rpmrs_levels); i++) { + if (msm_rpmrs_levels[i].sleep_mode == + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT) + return msm_rpmrs_levels[i].latency_us; + } + return 0; +} + +struct android_usb_platform_data msm_android_usb_pdata; + +struct platform_device msm_android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &msm_android_usb_pdata, + }, +}; + +void __init msm9615_device_init(void) +{ + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); + BUG_ON(msm_rpm_init(&msm9615_rpm_data)); + BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data)); + msm_android_usb_pdata.swfi_latency = + msm_rpmrs_levels[0].latency_us; + +} + +#define MSM_SHARED_RAM_PHYS 0x40000000 +void __init msm9615_map_io(void) +{ + msm_shared_ram_phys = MSM_SHARED_RAM_PHYS; + msm_map_msm9615_io(); + l2x0_cache_init(); + if (socinfo_init() < 0) + pr_err("socinfo_init() failed!\n"); +} + +void __init msm9615_init_irq(void) +{ + struct msm_mpm_device_data *data = NULL; + +#ifdef CONFIG_MSM_MPM + data = &msm9615_mpm_dev_data; +#endif + + msm_mpm_irq_extn_init(data); + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, + (void *)MSM_QGIC_CPU_BASE); +} + +struct platform_device msm_bus_9615_sys_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM, +}; + +struct platform_device msm_bus_def_fab = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_DEFAULT, +}; + +#ifdef CONFIG_FB_MSM_EBI2 +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "ebi2", 4)) + msm_register_device(&msm_ebi2_lcdc_device, data); + else + pr_err("%s: unknown device! %s\n", __func__, name); +} +#endif diff --git a/arch/arm/mach-msm/devices-fsm9xxx.c b/arch/arm/mach-msm/devices-fsm9xxx.c new file mode 100644 index 00000000000..777b6d6e5e9 --- /dev/null +++ b/arch/arm/mach-msm/devices-fsm9xxx.c @@ -0,0 +1,411 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "devices.h" +#include "smd_private.h" +#include "clock-local.h" +#include "msm_watchdog.h" + +#include +#include + +/* + * UARTs + */ + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1_PHYS, + .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +static struct resource resources_uart2[] = { + { + .start = INT_UART2, + .end = INT_UART2, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART2_PHYS, + .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart2 = { + .name = "msm_serial", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart2), + .resource = resources_uart2, +}; + +/* + * SSBIs + */ + +#ifdef CONFIG_MSM_SSBI +#define MSM_SSBI1_PHYS 0x94080000 +#define MSM_SSBI_PMIC1_PHYS MSM_SSBI1_PHYS +static struct resource msm_ssbi_pmic1_resources[] = { + { + .start = MSM_SSBI_PMIC1_PHYS, + .end = MSM_SSBI_PMIC1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi_pmic1 = { + .name = "msm_ssbi", + .id = 0, + .resource = msm_ssbi_pmic1_resources, + .num_resources = ARRAY_SIZE(msm_ssbi_pmic1_resources), +}; +#endif + +#ifdef CONFIG_I2C_SSBI +#define MSM_SSBI2_PHYS 0x94090000 +#define MSM_SSBI2_SIZE SZ_4K + +static struct resource msm_ssbi2_resources[] = { + { + .name = "ssbi_base", + .start = MSM_SSBI2_PHYS, + .end = MSM_SSBI2_PHYS + MSM_SSBI2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi2 = { + .name = "i2c_ssbi", + .id = 1, + .num_resources = ARRAY_SIZE(msm_ssbi2_resources), + .resource = msm_ssbi2_resources, +}; + +#define MSM_SSBI3_PHYS 0x940c0000 +#define MSM_SSBI3_SIZE SZ_4K + +static struct resource msm_ssbi3_resources[] = { + { + .name = "ssbi_base", + .start = MSM_SSBI3_PHYS, + .end = MSM_SSBI3_PHYS + MSM_SSBI3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi3 = { + .name = "i2c_ssbi", + .id = 2, + .num_resources = ARRAY_SIZE(msm_ssbi3_resources), + .resource = msm_ssbi3_resources, +}; + +#endif /* CONFIG_I2C_SSBI */ + +/* + * GSBI + */ + +#ifdef CONFIG_I2C_QUP + +#define MSM_GSBI1_PHYS 0x81200000 +#define MSM_GSBI1_QUP_PHYS 0x81a00000 + +static struct resource gsbi1_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI1_QUP_PHYS, + .end = MSM_GSBI1_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = INT_GSBI_QUP_ERROR, + .end = INT_GSBI_QUP_ERROR, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_gsbi1_qup_i2c_device = { + .name = "qup_i2c", + .id = 3, + .num_resources = ARRAY_SIZE(gsbi1_qup_i2c_resources), + .resource = gsbi1_qup_i2c_resources, +}; + +#endif /* CONFIG_I2C_QUP */ + +/* + * NAND + */ + +#define MSM_NAND_PHYS 0x81600000 +#define MSM_NAND_SIZE SZ_4K +#define MSM_EBI2_CTRL_PHYS 0x81400000 +#define MSM_EBI2_CTRL_SIZE SZ_4K + +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + MSM_NAND_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [3] = { + .name = "ebi2_reg_base", + .start = MSM_EBI2_CTRL_PHYS, + .end = MSM_EBI2_CTRL_PHYS + MSM_EBI2_CTRL_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, + .interleave = 0, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +/* + * SMD + */ + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +/* + * ADM + */ + +static struct resource msm_dmov_resource[] = { + { + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x94610000, + .end = 0x94610000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 3, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +/* + * SDC + */ + +#define MSM_SDC1_PHYS 0x80A00000 +#define MSM_SDC1_SIZE SZ_4K + +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_PHYS, + .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller != 1) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +/* + * QFEC + */ + +# define QFEC_MAC_IRQ INT_SBD_IRQ +# define QFEC_MAC_BASE 0x40000000 +# define QFEC_CLK_BASE 0x94020000 + +# define QFEC_MAC_SIZE 0x2000 +# define QFEC_CLK_SIZE 0x18100 + +# define QFEC_MAC_FUSE_BASE 0x80004210 +# define QFEC_MAC_FUSE_SIZE 16 + +static struct resource qfec_resources[] = { + [0] = { + .start = QFEC_MAC_BASE, + .end = QFEC_MAC_BASE + QFEC_MAC_SIZE, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = QFEC_MAC_IRQ, + .end = QFEC_MAC_IRQ, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = QFEC_CLK_BASE, + .end = QFEC_CLK_BASE + QFEC_CLK_SIZE, + .flags = IORESOURCE_IO, + }, + [3] = { + .start = QFEC_MAC_FUSE_BASE, + .end = QFEC_MAC_FUSE_BASE + QFEC_MAC_FUSE_SIZE, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device qfec_device = { + .name = "qfec", + .id = 0, + .num_resources = ARRAY_SIZE(qfec_resources), + .resource = qfec_resources, +}; + +/* + * FUSE + */ + +#if defined(CONFIG_QFP_FUSE) + +char fuse_regulator_name[] = "8058_lvs0"; + +struct resource qfp_fuse_resources[] = { + { + .start = (uint32_t) MSM_QFP_FUSE_BASE, + .end = (uint32_t) MSM_QFP_FUSE_BASE + MSM_QFP_FUSE_SIZE, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device fsm_qfp_fuse_device = { + .name = "qfp_fuse_driver", + .id = 0, + .dev = {.platform_data = fuse_regulator_name}, + .num_resources = ARRAY_SIZE(qfp_fuse_resources), + .resource = qfp_fuse_resources, +}; + +#endif + +/* + * XO + */ + +struct platform_device fsm_xo_device = { + .name = "fsm_xo_driver", + .id = -1, +}; + +/* + * Watchdog + */ + +static struct msm_watchdog_pdata fsm_watchdog_pdata = { + .pet_time = 10000, + .bark_time = 11000, + .has_secure = false, + .has_vic = true, +}; + +struct platform_device fsm9xxx_device_watchdog = { + .name = "msm_watchdog", + .id = -1, + .dev = { + .platform_data = &fsm_watchdog_pdata, + }, +}; + diff --git a/arch/arm/mach-msm/devices-msm7x00.c b/arch/arm/mach-msm/devices-msm7x00.c index 993780f490a..54ed40133c5 100644 --- a/arch/arm/mach-msm/devices-msm7x00.c +++ b/arch/arm/mach-msm/devices-msm7x00.c @@ -15,18 +15,20 @@ #include #include -#include +#include +#include #include #include +#include +#include +#include #include "devices.h" #include #include #include -#include "clock.h" -#include "clock-pcom.h" #include static struct resource resources_uart1[] = { @@ -92,6 +94,92 @@ struct platform_device msm_device_uart3 = { .resource = resources_uart3, }; +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + static struct resource resources_i2c[] = { { .start = MSM_I2C_PHYS, @@ -112,6 +200,30 @@ struct platform_device msm_device_i2c = { .resource = resources_i2c, }; +#define GPIO_I2C_CLK 60 +#define GPIO_I2C_DAT 61 +void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat) +{ + unsigned id; + if (gpio) { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + *gpio_clk = GPIO_I2C_CLK; + *gpio_dat = GPIO_I2C_DAT; + } else { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + static struct resource resources_hsusb[] = { { .start = MSM_HSUSB_PHYS, @@ -302,8 +414,7 @@ static struct platform_device *msm_sdcc_devices[] __initdata = { &msm_device_sdc4, }; -int __init msm_add_sdcc(unsigned int controller, - struct msm_mmc_platform_data *plat, +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags) { struct platform_device *pdev; @@ -394,48 +505,30 @@ struct platform_device msm_device_mdp = { .resource = resources_mdp, }; -struct clk_lookup msm_clocks_7x01a[] = { - CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), - CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), - CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, 0), - CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), - CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), - CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF), - CLK_PCOM("gp_clk", GP_CLK, NULL, 0), - CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, OFF), - CLK_PCOM("i2c_clk", I2C_CLK, "msm_i2c.0", 0), - CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), - CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), - CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), - CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), - CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), - CLK_PCOM("pbus_clk", PBUS_CLK, NULL, 0), - CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), - CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), - CLK_PCOM("sdc_clk", SDC1_CLK, "msm_sdcc.1", OFF), - CLK_PCOM("sdc_pclk", SDC1_P_CLK, "msm_sdcc.1", OFF), - CLK_PCOM("sdc_clk", SDC2_CLK, "msm_sdcc.2", OFF), - CLK_PCOM("sdc_pclk", SDC2_P_CLK, "msm_sdcc.2", OFF), - CLK_PCOM("sdc_clk", SDC3_CLK, "msm_sdcc.3", OFF), - CLK_PCOM("sdc_pclk", SDC3_P_CLK, "msm_sdcc.3", OFF), - CLK_PCOM("sdc_clk", SDC4_CLK, "msm_sdcc.4", OFF), - CLK_PCOM("sdc_pclk", SDC4_P_CLK, "msm_sdcc.4", OFF), - CLK_PCOM("tsif_clk", TSIF_CLK, NULL, 0), - CLK_PCOM("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), - CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), - CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), - CLK_PCOM("uart_clk", UART1_CLK, "msm_serial.0", OFF), - CLK_PCOM("uart_clk", UART2_CLK, "msm_serial.1", 0), - CLK_PCOM("uart_clk", UART3_CLK, "msm_serial.2", OFF), - CLK_PCOM("uart1dm_clk", UART1DM_CLK, NULL, OFF), - CLK_PCOM("uart2dm_clk", UART2DM_CLK, NULL, 0), - CLK_PCOM("usb_hs_clk", USB_HS_CLK, "msm_hsusb", OFF), - CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, "msm_hsusb", OFF), - CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), - CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF ), - CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), - CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + MSM_TSSC_SIZE - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, }; -unsigned msm_num_clocks_7x01a = ARRAY_SIZE(msm_clocks_7x01a); +struct platform_device msm_device_touchscreen = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; diff --git a/arch/arm/mach-msm/devices-msm7x01a.c b/arch/arm/mach-msm/devices-msm7x01a.c new file mode 100644 index 00000000000..1b9eb8614fe --- /dev/null +++ b/arch/arm/mach-msm/devices-msm7x01a.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +#include + +#include +#include + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1_PHYS, + .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart2[] = { + { + .start = INT_UART2, + .end = INT_UART2, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART2_PHYS, + .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart3[] = { + { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART3_PHYS, + .end = MSM_UART3_PHYS + MSM_UART3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +struct platform_device msm_device_uart2 = { + .name = "msm_serial", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart2), + .resource = resources_uart2, +}; + +struct platform_device msm_device_uart3 = { + .name = "msm_serial", + .id = 2, + .num_resources = ARRAY_SIZE(resources_uart3), + .resource = resources_uart3, +}; + +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_I2C_SIZE SZ_4K +#define MSM_I2C_PHYS 0xA9900000 +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +#define MSM_HSUSB_PHYS 0xA0800000 +static struct resource resources_hsusb_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_hsusb_otg = { + .name = "msm_hsusb_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_otg), + .resource = resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, + { + .name = "vbus_interrupt", + .start = MSM_GPIO_TO_INT(112), + .end = MSM_GPIO_TO_INT(112), + .flags = IORESOURCE_IRQ, + }, + { + .name = "id_interrupt", + .start = MSM_GPIO_TO_INT(114), + .end = MSM_GPIO_TO_INT(114), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_peripheral = { + .name = "msm_hsusb_peripheral", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_peripheral), + .resource = resources_hsusb_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_gadget_peripheral), + .resource = resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, +}; + +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#define MSM_NAND_PHYS 0xA0A00000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0xA9700000, + .end = 0xA9700000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 3, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +#define MSM_SDC1_BASE 0xA0400000 +#define MSM_SDC2_BASE 0xA0500000 +#define MSM_SDC3_BASE 0xA0600000 +#define MSM_SDC4_BASE 0xA0700000 +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#if defined(CONFIG_FB_MSM_MDP40) +#define MDP_BASE 0xA3F00000 +#define PMDH_BASE 0xAD600000 +#define EMDH_BASE 0xAD700000 +#define TVENC_BASE 0xAD400000 +#else +#define MDP_BASE 0xAA200000 +#define PMDH_BASE 0xAA600000 +#define EMDH_BASE 0xAA700000 +#define TVENC_BASE 0xAA400000 +#endif + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_mddi_resources[] = { + { + .name = "pmdh", + .start = PMDH_BASE, + .end = PMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_mddi_ext_resources[] = { + { + .name = "emdh", + .start = EMDH_BASE, + .end = EMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_ebi2_lcd_resources[] = { + { + .name = "base", + .start = 0xa0d00000, + .end = 0xa0d00000 + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd01", + .start = 0x98000000, + .end = 0x98000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd02", + .start = 0x9c000000, + .end = 0x9c000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_tvenc_resources[] = { + { + .name = "tvenc", + .start = TVENC_BASE, + .end = TVENC_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static struct platform_device msm_mddi_device = { + .name = "mddi", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_resources), + .resource = msm_mddi_resources, +}; + +static struct platform_device msm_mddi_ext_device = { + .name = "mddi_ext", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_ext_resources), + .resource = msm_mddi_ext_resources, +}; + +static struct platform_device msm_ebi2_lcd_device = { + .name = "ebi2_lcd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_ebi2_lcd_resources), + .resource = msm_ebi2_lcd_resources, +}; + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +static struct platform_device msm_tvenc_device = { + .name = "tvenc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, +}; + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define MSM_TSIF_PHYS (0xa0100000) +#define MSM_TSIF_SIZE (0x200) + +static struct resource tsif_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = INT_TSIF_IRQ, + .end = INT_TSIF_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF_PHYS, + .end = MSM_TSIF_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +static void tsif_release(struct device *dev) +{ + dev_info(dev, "release\n"); +} + +struct platform_device msm_device_tsif = { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif_resources), + .resource = tsif_resources, + .dev = { + .release = tsif_release, + }, +}; +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + +#define MSM_TSSC_PHYS 0xAA300000 +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + SZ_4K - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, +}; + +struct platform_device msm_device_tssc = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "pmdh", 4)) + msm_register_device(&msm_mddi_device, data); + else if (!strncmp(name, "emdh", 4)) + msm_register_device(&msm_mddi_ext_device, data); + else if (!strncmp(name, "ebi2", 4)) + msm_register_device(&msm_ebi2_lcd_device, data); + else if (!strncmp(name, "tvenc", 5)) + msm_register_device(&msm_tvenc_device, data); + else if (!strncmp(name, "lcdc", 4)) + msm_register_device(&msm_lcdc_device, data); + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct platform_device msm_camera_device = { + .name = "msm_camera", + .id = 0, +}; + +void __init msm_camera_register_device(void *res, uint32_t num, + void *data) +{ + msm_camera_device.num_resources = num; + msm_camera_device.resource = res; + + msm_register_device(&msm_camera_device, data); +} diff --git a/arch/arm/mach-msm/devices-msm7x25.c b/arch/arm/mach-msm/devices-msm7x25.c new file mode 100644 index 00000000000..2be7d5ec671 --- /dev/null +++ b/arch/arm/mach-msm/devices-msm7x25.c @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "smd_private.h" + +#include + +#include +#include +#include +#include + +#include "clock-pcom.h" + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1_PHYS, + .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart2[] = { + { + .start = INT_UART2, + .end = INT_UART2, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART2_PHYS, + .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart3[] = { + { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART3_PHYS, + .end = MSM_UART3_PHYS + MSM_UART3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +struct platform_device msm_device_uart2 = { + .name = "msm_serial", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart2), + .resource = resources_uart2, +}; + +struct platform_device msm_device_uart3 = { + .name = "msm_serial", + .id = 2, + .num_resources = ARRAY_SIZE(resources_uart3), + .resource = resources_uart3, +}; + +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_I2C_SIZE SZ_4K +#define MSM_I2C_PHYS 0xA9900000 +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +#define MSM_HSUSB_PHYS 0xA0800000 +static struct resource resources_hsusb_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_hsusb_otg = { + .name = "msm_hsusb_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_otg), + .resource = resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_peripheral = { + .name = "msm_hsusb_peripheral", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_peripheral), + .resource = resources_hsusb_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_gadget_peripheral), + .resource = resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, +}; + +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#ifdef CONFIG_USB_ANDROID_DIAG +struct usb_diag_platform_data usb_diag_pdata = { + .ch_name = DIAG_LEGACY, + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +struct platform_device usb_diag_device = { + .name = "usb_diag", + .id = -1, + .dev = { + .platform_data = &usb_diag_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_F_SERIAL +static struct usb_gadget_fserial_platform_data fserial_pdata = { + .no_ports = 2, +}; + +struct platform_device usb_gadget_fserial_device = { + .name = "usb_fserial", + .id = -1, + .dev = { + .platform_data = &fserial_pdata, + }, +}; +#endif + +#define MSM_NAND_PHYS 0xA0A00000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0xA9700000, + .end = 0xA9700000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 3, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +#define MSM_SDC1_BASE 0xA0400000 +#define MSM_SDC2_BASE 0xA0500000 +#define MSM_SDC3_BASE 0xA0600000 +#define MSM_SDC4_BASE 0xA0700000 +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#define RAMFS_INFO_MAGICNUMBER 0x654D4D43 +#define RAMFS_INFO_VERSION 0x00000001 +#define RAMFS_MODEMSTORAGE_ID 0x4D454653 + +static struct resource rmt_storage_resources[] = { + { + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rmt_storage_device = { + .name = "rmt_storage", + .id = -1, + .num_resources = ARRAY_SIZE(rmt_storage_resources), + .resource = rmt_storage_resources, +}; + +struct shared_ramfs_entry { + uint32_t client_id; /* Client id to uniquely identify a client */ + uint32_t base_addr; /* Base address of shared RAMFS memory */ + uint32_t size; /* Size of the shared RAMFS memory */ + uint32_t reserved; /* Reserved attribute for future use */ +}; +struct shared_ramfs_table { + uint32_t magic_id; /* Identify RAMFS details in SMEM */ + uint32_t version; /* Version of shared_ramfs_table */ + uint32_t entries; /* Total number of valid entries */ + struct shared_ramfs_entry ramfs_entry[3]; /* List all entries */ +}; + +int __init rmt_storage_add_ramfs(void) +{ + struct shared_ramfs_table *ramfs_table; + struct shared_ramfs_entry *ramfs_entry; + int index; + + ramfs_table = smem_alloc(SMEM_SEFS_INFO, + sizeof(struct shared_ramfs_table)); + + if (!ramfs_table) { + printk(KERN_WARNING "%s: No RAMFS table in SMEM\n", __func__); + return -ENOENT; + } + + if ((ramfs_table->magic_id != (u32) RAMFS_INFO_MAGICNUMBER) || + (ramfs_table->version != (u32) RAMFS_INFO_VERSION)) { + printk(KERN_WARNING "%s: Magic / Version mismatch:, " + "magic_id=%#x, format_version=%#x\n", __func__, + ramfs_table->magic_id, ramfs_table->version); + return -ENOENT; + } + + for (index = 0; index < ramfs_table->entries; index++) { + ramfs_entry = &ramfs_table->ramfs_entry[index]; + + /* Find a match for the Modem Storage RAMFS area */ + if (ramfs_entry->client_id == (u32) RAMFS_MODEMSTORAGE_ID) { + printk(KERN_INFO "%s: RAMFS Info (from SMEM): " + "Baseaddr = 0x%08x, Size = 0x%08x\n", __func__, + ramfs_entry->base_addr, ramfs_entry->size); + + rmt_storage_resources[0].start = ramfs_entry->base_addr; + rmt_storage_resources[0].end = ramfs_entry->base_addr + + ramfs_entry->size - 1; + platform_device_register(&rmt_storage_device); + return 0; + } + } + return -ENOENT; +} + +#if defined(CONFIG_FB_MSM_MDP40) +#define MDP_BASE 0xA3F00000 +#define PMDH_BASE 0xAD600000 +#define EMDH_BASE 0xAD700000 +#define TVENC_BASE 0xAD400000 +#else +#define MDP_BASE 0xAA200000 +#define PMDH_BASE 0xAA600000 +#define EMDH_BASE 0xAA700000 +#define TVENC_BASE 0xAA400000 +#endif + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_mddi_resources[] = { + { + .name = "pmdh", + .start = PMDH_BASE, + .end = PMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_mddi_ext_resources[] = { + { + .name = "emdh", + .start = EMDH_BASE, + .end = EMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_ebi2_lcd_resources[] = { + { + .name = "base", + .start = 0xa0d00000, + .end = 0xa0d00000 + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd01", + .start = 0x98000000, + .end = 0x98000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd02", + .start = 0x9c000000, + .end = 0x9c000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_tvenc_resources[] = { + { + .name = "tvenc", + .start = TVENC_BASE, + .end = TVENC_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static struct platform_device msm_mddi_device = { + .name = "mddi", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_resources), + .resource = msm_mddi_resources, +}; + +static struct platform_device msm_mddi_ext_device = { + .name = "mddi_ext", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_ext_resources), + .resource = msm_mddi_ext_resources, +}; + +static struct platform_device msm_ebi2_lcd_device = { + .name = "ebi2_lcd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_ebi2_lcd_resources), + .resource = msm_ebi2_lcd_resources, +}; + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +static struct platform_device msm_tvenc_device = { + .name = "tvenc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, +}; + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define MSM_TSIF_PHYS (0xa0100000) +#define MSM_TSIF_SIZE (0x200) + +static struct resource tsif_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = INT_TSIF_IRQ, + .end = INT_TSIF_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF_PHYS, + .end = MSM_TSIF_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +static void tsif_release(struct device *dev) +{ + dev_info(dev, "release\n"); +} + +struct platform_device msm_device_tsif = { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif_resources), + .resource = tsif_resources, + .dev = { + .release = tsif_release, + }, +}; +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + +#define MSM_TSSC_PHYS 0xAA300000 +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + SZ_4K - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, +}; + +struct platform_device msm_device_tssc = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "pmdh", 4)) + msm_register_device(&msm_mddi_device, data); + else if (!strncmp(name, "emdh", 4)) + msm_register_device(&msm_mddi_ext_device, data); + else if (!strncmp(name, "ebi2", 4)) + msm_register_device(&msm_ebi2_lcd_device, data); + else if (!strncmp(name, "tvenc", 5)) + msm_register_device(&msm_tvenc_device, data); + else if (!strncmp(name, "lcdc", 4)) + msm_register_device(&msm_lcdc_device, data); + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct platform_device msm_camera_device = { + .name = "msm_camera", + .id = 0, +}; + +void __init msm_camera_register_device(void *res, uint32_t num, + void *data) +{ + msm_camera_device.num_resources = num; + msm_camera_device.resource = res; + + msm_register_device(&msm_camera_device, data); +} + +static DEFINE_CLK_PCOM(adm_clk, ADM_CLK, 0); +static DEFINE_CLK_PCOM(adsp_clk, ADSP_CLK, 0); +static DEFINE_CLK_PCOM(ebi1_clk, EBI1_CLK, CLK_MIN); +static DEFINE_CLK_PCOM(ebi2_clk, EBI2_CLK, 0); +static DEFINE_CLK_PCOM(ecodec_clk, ECODEC_CLK, 0); +static DEFINE_CLK_PCOM(gp_clk, GP_CLK, 0); +static DEFINE_CLK_PCOM(i2c_clk, I2C_CLK, 0); +static DEFINE_CLK_PCOM(icodec_rx_clk, ICODEC_RX_CLK, 0); +static DEFINE_CLK_PCOM(icodec_tx_clk, ICODEC_TX_CLK, 0); +static DEFINE_CLK_PCOM(imem_clk, IMEM_CLK, OFF); +static DEFINE_CLK_PCOM(mdc_clk, MDC_CLK, 0); +static DEFINE_CLK_PCOM(pmdh_clk, PMDH_CLK, OFF | CLK_MINMAX); +static DEFINE_CLK_PCOM(mdp_clk, MDP_CLK, OFF); +static DEFINE_CLK_PCOM(mdp_lcdc_pclk_clk, MDP_LCDC_PCLK_CLK, 0); +static DEFINE_CLK_PCOM(mdp_lcdc_pad_pclk_clk, MDP_LCDC_PAD_PCLK_CLK, 0); +static DEFINE_CLK_PCOM(mdp_vsync_clk, MDP_VSYNC_CLK, OFF); +static DEFINE_CLK_PCOM(pbus_clk, PBUS_CLK, CLK_MIN); +static DEFINE_CLK_PCOM(pcm_clk, PCM_CLK, 0); +static DEFINE_CLK_PCOM(sdac_clk, SDAC_CLK, OFF); +static DEFINE_CLK_PCOM(sdc1_clk, SDC1_CLK, OFF); +static DEFINE_CLK_PCOM(sdc1_p_clk, SDC1_P_CLK, OFF); +static DEFINE_CLK_PCOM(sdc2_clk, SDC2_CLK, OFF); +static DEFINE_CLK_PCOM(sdc2_p_clk, SDC2_P_CLK, OFF); +static DEFINE_CLK_PCOM(sdc3_clk, SDC3_CLK, OFF); +static DEFINE_CLK_PCOM(sdc3_p_clk, SDC3_P_CLK, OFF); +static DEFINE_CLK_PCOM(sdc4_clk, SDC4_CLK, OFF); +static DEFINE_CLK_PCOM(sdc4_p_clk, SDC4_P_CLK, OFF); +static DEFINE_CLK_PCOM(uart1_clk, UART1_CLK, OFF); +static DEFINE_CLK_PCOM(uart2_clk, UART2_CLK, 0); +static DEFINE_CLK_PCOM(uart3_clk, UART3_CLK, OFF); +static DEFINE_CLK_PCOM(uart1dm_clk, UART1DM_CLK, OFF); +static DEFINE_CLK_PCOM(uart2dm_clk, UART2DM_CLK, 0); +static DEFINE_CLK_PCOM(usb_hs_clk, USB_HS_CLK, OFF); +static DEFINE_CLK_PCOM(usb_hs_p_clk, USB_HS_P_CLK, OFF); +static DEFINE_CLK_PCOM(usb_otg_clk, USB_OTG_CLK, 0); +static DEFINE_CLK_PCOM(vdc_clk, VDC_CLK, OFF | CLK_MIN); +static DEFINE_CLK_PCOM(vfe_clk, VFE_CLK, OFF); +static DEFINE_CLK_PCOM(vfe_mdc_clk, VFE_MDC_CLK, OFF); + +struct clk_lookup msm_clocks_7x25[] = { + CLK_LOOKUP("core_clk", adm_clk.c, "msm_dmov"), + CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL), + CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL), + CLK_LOOKUP("ebi2_clk", ebi2_clk.c, NULL), + CLK_LOOKUP("ecodec_clk", ecodec_clk.c, NULL), + CLK_LOOKUP("core_clk", gp_clk.c, NULL), + CLK_LOOKUP("core_clk", i2c_clk.c, "msm_i2c.0"), + CLK_LOOKUP("icodec_rx_clk", icodec_rx_clk.c, NULL), + CLK_LOOKUP("icodec_tx_clk", icodec_tx_clk.c, NULL), + CLK_LOOKUP("mem_clk", imem_clk.c, NULL), + CLK_LOOKUP("mdc_clk", mdc_clk.c, NULL), + CLK_LOOKUP("mddi_clk", pmdh_clk.c, NULL), + CLK_LOOKUP("mdp_clk", mdp_clk.c, NULL), + CLK_LOOKUP("mdp_lcdc_pclk_clk", mdp_lcdc_pclk_clk.c, NULL), + CLK_LOOKUP("mdp_lcdc_pad_pclk_clk", mdp_lcdc_pad_pclk_clk.c, NULL), + CLK_LOOKUP("mdp_vsync_clk", mdp_vsync_clk.c, NULL), + CLK_LOOKUP("pbus_clk", pbus_clk.c, NULL), + CLK_LOOKUP("pcm_clk", pcm_clk.c, NULL), + CLK_LOOKUP("sdac_clk", sdac_clk.c, NULL), + CLK_LOOKUP("core_clk", sdc1_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("iface_clk", sdc1_p_clk.c, "msm_sdcc.1"), + CLK_LOOKUP("core_clk", sdc2_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("iface_clk", sdc2_p_clk.c, "msm_sdcc.2"), + CLK_LOOKUP("core_clk", sdc3_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("iface_clk", sdc3_p_clk.c, "msm_sdcc.3"), + CLK_LOOKUP("core_clk", sdc4_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("iface_clk", sdc4_p_clk.c, "msm_sdcc.4"), + CLK_LOOKUP("core_clk", uart1_clk.c, "msm_serial.0"), + CLK_LOOKUP("core_clk", uart2_clk.c, "msm_serial.1"), + CLK_LOOKUP("core_clk", uart3_clk.c, "msm_serial.2"), + CLK_LOOKUP("core_clk", uart1dm_clk.c, "msm_serial_hs.0"), + CLK_LOOKUP("core_clk", uart2dm_clk.c, "msm_serial_hs.1"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_otg"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_otg"), + CLK_LOOKUP("alt_core_clk", usb_hs_clk.c, "msm_hsusb_peripheral"), + CLK_LOOKUP("iface_clk", usb_hs_p_clk.c, "msm_hsusb_peripheral"), + CLK_LOOKUP("alt_core_clk", usb_otg_clk.c, NULL), + CLK_LOOKUP("vdc_clk", vdc_clk.c, NULL), + CLK_LOOKUP("vfe_clk", vfe_clk.c, NULL), + CLK_LOOKUP("vfe_mdc_clk", vfe_mdc_clk.c, NULL), +}; + +unsigned msm_num_clocks_7x25 = ARRAY_SIZE(msm_clocks_7x25); + diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c new file mode 100644 index 00000000000..ffd10fad83f --- /dev/null +++ b/arch/arm/mach-msm/devices-msm7x27.c @@ -0,0 +1,900 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "footswitch.h" + +#include + +#include +#include +#include +#include +#include "irq.h" +#include "pm.h" + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM7XXX_UART1_PHYS, + .end = MSM7XXX_UART1_PHYS + MSM7XXX_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart2[] = { + { + .start = INT_UART2, + .end = INT_UART2, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM7XXX_UART2_PHYS, + .end = MSM7XXX_UART2_PHYS + MSM7XXX_UART2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +struct platform_device msm_device_uart2 = { + .name = "msm_serial", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart2), + .resource = resources_uart2, +}; + +static struct resource resources_adsp[] = { + { + .start = INT_ADSP_A9_A11, + .end = INT_ADSP_A9_A11, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_adsp_device = { + .name = "msm_adsp", + .id = -1, + .num_resources = ARRAY_SIZE(resources_adsp), + .resource = resources_adsp, +}; + +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_I2C_SIZE SZ_4K +#define MSM_I2C_PHYS 0xA9900000 +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +#define MSM_HSUSB_PHYS 0xA0800000 +static struct resource resources_hsusb_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_hsusb_otg = { + .name = "msm_hsusb_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_otg), + .resource = resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_peripheral = { + .name = "msm_hsusb_peripheral", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_peripheral), + .resource = resources_hsusb_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_gadget_peripheral), + .resource = resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, +}; + +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +struct platform_device asoc_msm_pcm = { + .name = "msm-dsp-audio", + .id = 0, +}; + +struct platform_device asoc_msm_dai0 = { + .name = "msm-codec-dai", + .id = 0, +}; + +struct platform_device asoc_msm_dai1 = { + .name = "msm-cpu-dai", + .id = 0, +}; + +#define MSM_NAND_PHYS 0xA0A00000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0xA9700000, + .end = 0xA9700000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 3, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +static struct msm_pm_irq_calls msm7x27_pm_irq_calls = { + .irq_pending = msm_irq_pending, + .idle_sleep_allowed = msm_irq_idle_sleep_allowed, + .enter_sleep1 = msm_irq_enter_sleep1, + .enter_sleep2 = msm_irq_enter_sleep2, + .exit_sleep1 = msm_irq_exit_sleep1, + .exit_sleep2 = msm_irq_exit_sleep2, + .exit_sleep3 = msm_irq_exit_sleep3, +}; + +void __init msm_pm_register_irqs(void) +{ + msm_pm_set_irq_extns(&msm7x27_pm_irq_calls); +} + +#define MSM_SDC1_BASE 0xA0400000 +#define MSM_SDC2_BASE 0xA0500000 +#define MSM_SDC3_BASE 0xA0600000 +#define MSM_SDC4_BASE 0xA0700000 +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#if defined(CONFIG_FB_MSM_MDP40) +#define MDP_BASE 0xA3F00000 +#define PMDH_BASE 0xAD600000 +#define EMDH_BASE 0xAD700000 +#define TVENC_BASE 0xAD400000 +#else +#define MDP_BASE 0xAA200000 +#define PMDH_BASE 0xAA600000 +#define EMDH_BASE 0xAA700000 +#define TVENC_BASE 0xAA400000 +#endif + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_mddi_resources[] = { + { + .name = "pmdh", + .start = PMDH_BASE, + .end = PMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_mddi_ext_resources[] = { + { + .name = "emdh", + .start = EMDH_BASE, + .end = EMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_ebi2_lcd_resources[] = { + { + .name = "base", + .start = 0xa0d00000, + .end = 0xa0d00000 + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd01", + .start = 0x98000000, + .end = 0x98000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd02", + .start = 0x9c000000, + .end = 0x9c000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_tvenc_resources[] = { + { + .name = "tvenc", + .start = TVENC_BASE, + .end = TVENC_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static struct platform_device msm_mddi_device = { + .name = "mddi", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_resources), + .resource = msm_mddi_resources, +}; + +static struct platform_device msm_mddi_ext_device = { + .name = "mddi_ext", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_ext_resources), + .resource = msm_mddi_ext_resources, +}; + +static struct platform_device msm_ebi2_lcd_device = { + .name = "ebi2_lcd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_ebi2_lcd_resources), + .resource = msm_ebi2_lcd_resources, +}; + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +static struct platform_device msm_tvenc_device = { + .name = "tvenc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, +}; + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define MSM_TSIF_PHYS (0xa0100000) +#define MSM_TSIF_SIZE (0x200) + +static struct resource tsif_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = INT_TSIF_IRQ, + .end = INT_TSIF_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF_PHYS, + .end = MSM_TSIF_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +static void tsif_release(struct device *dev) +{ + dev_info(dev, "release\n"); +} + +struct platform_device msm_device_tsif = { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif_resources), + .resource = tsif_resources, + .dev = { + .release = tsif_release, + }, +}; +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + +#define MSM_TSSC_PHYS 0xAA300000 +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + SZ_4K - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, +}; + +struct platform_device msm_device_tssc = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "pmdh", 4)) + msm_register_device(&msm_mddi_device, data); + else if (!strncmp(name, "emdh", 4)) + msm_register_device(&msm_mddi_ext_device, data); + else if (!strncmp(name, "ebi2", 4)) + msm_register_device(&msm_ebi2_lcd_device, data); + else if (!strncmp(name, "tvenc", 5)) + msm_register_device(&msm_tvenc_device, data); + else if (!strncmp(name, "lcdc", 4)) + msm_register_device(&msm_lcdc_device, data); + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct platform_device msm_camera_device = { + .name = "msm_camera", + .id = 0, +}; + +void __init msm_camera_register_device(void *res, uint32_t num, + void *data) +{ + msm_camera_device.num_resources = num; + msm_camera_device.resource = res; + + msm_register_device(&msm_camera_device, data); +} + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + /* bus_freq has been set to 160000 for power savings. + * OEMs may modify the value at their discretion for performance + * The appropriate maximum replacement for 160000 is: + * msm7x2x_clock_data.max_axi_khz + */ + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 160000000, + }, + }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ, + .strtstp_sleepwake = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX3D, "vdd", "kgsl-3d0.0"), +}; +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); + +static struct resource gpio_resources[] = { + { + .start = INT_GPIO_GROUP1, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_GPIO_GROUP2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_device_gpio = { + .name = "msmgpio", + .id = -1, + .resource = gpio_resources, + .num_resources = ARRAY_SIZE(gpio_resources), +}; + +static int __init msm7627_init_gpio(void) +{ + platform_device_register(&msm_device_gpio); + return 0; +} + +postcore_initcall(msm7627_init_gpio); diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c new file mode 100644 index 00000000000..4654606e6b0 --- /dev/null +++ b/arch/arm/mach-msm/devices-msm7x27a.c @@ -0,0 +1,1730 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "devices-msm7x2xa.h" +#include "footswitch.h" +#include "acpuclock.h" +#include "spm.h" +#include "mpm-8625.h" +#include "irq.h" +#include "pm.h" + +/* Address of GSBI blocks */ +#define MSM_GSBI0_PHYS 0xA1200000 +#define MSM_GSBI1_PHYS 0xA1300000 + +/* GSBI QUPe devices */ +#define MSM_GSBI0_QUP_PHYS (MSM_GSBI0_PHYS + 0x80000) +#define MSM_GSBI1_QUP_PHYS (MSM_GSBI1_PHYS + 0x80000) + +static struct resource gsbi0_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI0_QUP_PHYS, + .end = MSM_GSBI0_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI0_PHYS, + .end = MSM_GSBI0_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +/* Use GSBI0 QUP for /dev/i2c-0 */ +struct platform_device msm_gsbi0_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI0_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi0_qup_i2c_resources), + .resource = gsbi0_qup_i2c_resources, +}; + +static struct resource gsbi1_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI1_QUP_PHYS, + .end = MSM_GSBI1_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = INT_ARM11_DMA, + .end = INT_ARM11_DMA, + .flags = IORESOURCE_IRQ, + }, +}; + +/* Use GSBI1 QUP for /dev/i2c-1 */ +struct platform_device msm_gsbi1_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI1_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi1_qup_i2c_resources), + .resource = gsbi1_qup_i2c_resources, +}; + +#define MSM_HSUSB_PHYS 0xA0800000 +static struct resource resources_hsusb_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_otg), + .resource = resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_gadget_peripheral), + .resource = resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_host[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, +}; + +static struct resource msm_dmov_resource[] = { + { + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0xA9700000, + .end = 0xA9700000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 3, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct resource smd_8625_resource[] = { + { + .name = "a9_m2a_0", + .start = MSM8625_INT_A9_M2A_0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "a9_m2a_5", + .start = MSM8625_INT_A9_M2A_5, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smd_subsystem_config smd_8625_config_list[] = { + { + .irq_config_id = SMD_MODEM, + .subsys_name = "modem", + .edge = SMD_APPS_MODEM, + + .smd_int.irq_name = "a9_m2a_0", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + + .smd_int.out_bit_pos = 1, + .smd_int.out_base = (void __iomem *)MSM_CSR_BASE, + .smd_int.out_offset = 0x400 + (0) * 4, + + .smsm_int.irq_name = "a9_m2a_5", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smsm_dev", + .smsm_int.dev_id = 0, + + .smsm_int.out_bit_pos = 1, + .smsm_int.out_base = (void __iomem *)MSM_CSR_BASE, + .smsm_int.out_offset = 0x400 + (5) * 4, + + } +}; + +static struct smd_platform smd_8625_platform_data = { + .num_ss_configs = ARRAY_SIZE(smd_8625_config_list), + .smd_ss_configs = smd_8625_config_list, +}; + +struct platform_device msm8625_device_smd = { + .name = "msm_smd", + .id = -1, + .resource = smd_8625_resource, + .num_resources = ARRAY_SIZE(smd_8625_resource), + .dev = { + .platform_data = &smd_8625_platform_data, + } +}; + +static struct resource resources_adsp[] = { + { + .start = INT_ADSP_A9_A11, + .end = INT_ADSP_A9_A11, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_adsp_device = { + .name = "msm_adsp", + .id = -1, + .num_resources = ARRAY_SIZE(resources_adsp), + .resource = resources_adsp, +}; + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM7XXX_UART1_PHYS, + .end = MSM7XXX_UART1_PHYS + MSM7XXX_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +#define MSM_UART1DM_PHYS 0xA0200000 +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_UART2DM_PHYS 0xA0300000 +static struct resource msm_uart2dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart2dm_resources), + .resource = msm_uart2dm_resources, +}; + +#define MSM_NAND_PHYS 0xA0A00000 +#define MSM_NANDC01_PHYS 0xA0A40000 +#define MSM_NANDC10_PHYS 0xA0A80000 +#define MSM_NANDC11_PHYS 0xA0AC0000 +#define EBI2_REG_BASE 0xA0D00000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [2] = { + .name = "msm_nandc01_phys", + .start = MSM_NANDC01_PHYS, + .end = MSM_NANDC01_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [3] = { + .name = "msm_nandc10_phys", + .start = MSM_NANDC10_PHYS, + .end = MSM_NANDC10_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [4] = { + .name = "msm_nandc11_phys", + .start = MSM_NANDC11_PHYS, + .end = MSM_NANDC11_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [5] = { + .name = "ebi2_reg_base", + .start = EBI2_REG_BASE, + .end = EBI2_REG_BASE + 0x60, + .flags = IORESOURCE_MEM, + }, +}; + +struct flash_platform_data msm_nand_data = { + .version = VERSION_2, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +static struct msm_pm_irq_calls msm7x27a_pm_irq_calls = { + .irq_pending = msm_irq_pending, + .idle_sleep_allowed = msm_irq_idle_sleep_allowed, + .enter_sleep1 = msm_irq_enter_sleep1, + .enter_sleep2 = msm_irq_enter_sleep2, + .exit_sleep1 = msm_irq_exit_sleep1, + .exit_sleep2 = msm_irq_exit_sleep2, + .exit_sleep3 = msm_irq_exit_sleep3, +}; + +static struct msm_pm_irq_calls msm8625_pm_irq_calls = { + .irq_pending = msm_gic_spi_ppi_pending, + .idle_sleep_allowed = msm_gic_irq_idle_sleep_allowed, + .enter_sleep1 = msm_gic_irq_enter_sleep1, + .enter_sleep2 = msm_gic_irq_enter_sleep2, + .exit_sleep1 = msm_gic_irq_exit_sleep1, + .exit_sleep2 = msm_gic_irq_exit_sleep2, + .exit_sleep3 = msm_gic_irq_exit_sleep3, +}; + +void __init msm_pm_register_irqs(void) +{ + if (cpu_is_msm8625()) + msm_pm_set_irq_extns(&msm8625_pm_irq_calls); + else + msm_pm_set_irq_extns(&msm7x27a_pm_irq_calls); + +} + +#define MSM_SDC1_BASE 0xA0400000 +#define MSM_SDC2_BASE 0xA0500000 +#define MSM_SDC3_BASE 0xA0600000 +#define MSM_SDC4_BASE 0xA0700000 +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +#ifdef CONFIG_MSM_CAMERA_V4L2 +static int apps_reset; +static struct resource msm_csic0_resources[] = { + { + .name = "csic", + .start = 0xA0F00000, + .end = 0xA0F00000 + 0x00100000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = INT_CSI_IRQ_0, + .end = INT_CSI_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_csic1_resources[] = { + { + .name = "csic", + .start = 0xA1000000, + .end = 0xA1000000 + 0x00100000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = INT_CSI_IRQ_1, + .end = INT_CSI_IRQ_1, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm7x27a_device_csic0 = { + .name = "msm_csic", + .id = 0, + .resource = msm_csic0_resources, + .num_resources = ARRAY_SIZE(msm_csic0_resources), +}; + +struct platform_device msm7x27a_device_csic1 = { + .name = "msm_csic", + .id = 1, + .resource = msm_csic1_resources, + .num_resources = ARRAY_SIZE(msm_csic1_resources), +}; + +static struct resource msm_clkctl_resources[] = { + { + .name = "clk_ctl", + .start = MSM7XXX_CLK_CTL_PHYS, + .end = MSM7XXX_CLK_CTL_PHYS + MSM7XXX_CLK_CTL_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; +struct platform_device msm7x27a_device_clkctl = { + .name = "msm_clk_ctl", + .id = 0, + .resource = msm_clkctl_resources, + .num_resources = ARRAY_SIZE(msm_clkctl_resources), + .dev = { + .platform_data = &apps_reset, + }, +}; + +struct platform_device msm7x27a_device_vfe = { + .name = "msm_vfe", + .id = 0, +}; + +#endif + +/* Command sequence for simple WFI */ +static uint8_t spm_wfi_cmd_sequence[] __initdata = { + 0x04, 0x03, 0x04, 0x0f, +}; + +/* Command sequence for GDFS, this won't send any interrupt to the modem */ +static uint8_t spm_pc_without_modem[] __initdata = { + 0x20, 0x00, 0x30, 0x10, + 0x03, 0x1e, 0x0e, 0x3e, + 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, + 0x2E, 0x0f, +}; + +static struct msm_spm_seq_entry msm_spm_seq_list[] __initdata = { + [0] = { + .mode = MSM_SPM_MODE_CLOCK_GATING, + .notify_rpm = false, + .cmd = spm_wfi_cmd_sequence, + }, + [1] = { + .mode = MSM_SPM_MODE_POWER_COLLAPSE, + .notify_rpm = false, + .cmd = spm_pc_without_modem, + }, +}; + +static struct msm_spm_platform_data msm_spm_data[] __initdata = { + [0] = { + .reg_base_addr = MSM_SAW0_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x0, + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, + [1] = { + .reg_base_addr = MSM_SAW1_BASE, + .reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x0, + .reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01, + .num_modes = ARRAY_SIZE(msm_spm_seq_list), + .modes = msm_spm_seq_list, + }, +}; + +void __init msm8x25_spm_device_init(void) +{ + msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data)); +} + +#define MDP_BASE 0xAA200000 +#define MIPI_DSI_HW_BASE 0xA1100000 + +static struct resource msm_mipi_dsi_resources[] = { + { + .name = "mipi_dsi", + .start = MIPI_DSI_HW_BASE, + .end = MIPI_DSI_HW_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_DSI_IRQ, + .end = INT_DSI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_mipi_dsi_device = { + .name = "mipi_dsi", + .id = 1, + .num_resources = ARRAY_SIZE(msm_mipi_dsi_resources), + .resource = msm_mipi_dsi_resources, +}; + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F1008 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 245760000, + .bus_freq = 200000000, + }, + { + .gpu_freq = 192000000, + .bus_freq = 160000000, + }, + { + .gpu_freq = 133330000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = 3, + .set_grp_async = set_grp_xbar_async, + .idle_timeout = HZ, + .strtstp_sleepwake = true, + .nap_allowed = false, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +void __init msm7x25a_kgsl_3d0_init(void) +{ + if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) { + kgsl_3d0_pdata.num_levels = 2; + kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 133330000; + kgsl_3d0_pdata.pwrlevel[0].bus_freq = 160000000; + kgsl_3d0_pdata.pwrlevel[1].gpu_freq = 96000000; + kgsl_3d0_pdata.pwrlevel[1].bus_freq = 0; + } +} + +void __init msm8x25_kgsl_3d0_init(void) +{ + if (cpu_is_msm8625()) { + kgsl_3d0_pdata.idle_timeout = HZ/5; + kgsl_3d0_pdata.strtstp_sleepwake = false; + /* 8x25 supports a higher GPU frequency */ + kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 300000000; + kgsl_3d0_pdata.pwrlevel[0].bus_freq = 200000000; + } +} + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + + +#define PERPH_WEB_BLOCK_ADDR (0xA9D00040) +#define PDM0_CTL_OFFSET (0x04) +#define SIZE_8B (0x08) + +static struct resource resources_led[] = { + { + .start = PERPH_WEB_BLOCK_ADDR, + .end = PERPH_WEB_BLOCK_ADDR + (SIZE_8B) - 1, + .name = "led-gpio-pdm", + .flags = IORESOURCE_MEM, + }, +}; + +static struct led_info msm_kpbl_pdm_led_pdata = { + .name = "keyboard-backlight", +}; + +struct platform_device led_pdev = { + .name = "leds-msm-pdm", + /* use pdev id to represent pdm id */ + .id = 0, + .num_resources = ARRAY_SIZE(resources_led), + .resource = resources_led, + .dev = { + .platform_data = &msm_kpbl_pdm_led_pdata, + }, +}; + +struct platform_device asoc_msm_pcm = { + .name = "msm-dsp-audio", + .id = 0, +}; + +struct platform_device asoc_msm_dai0 = { + .name = "msm-codec-dai", + .id = 0, +}; + +struct platform_device asoc_msm_dai1 = { + .name = "msm-cpu-dai", + .id = 0, +}; + +static struct resource gpio_resources[] = { + { + .start = INT_GPIO_GROUP1, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_GPIO_GROUP2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_device_gpio = { + .name = "msmgpio", + .id = -1, + .resource = gpio_resources, + .num_resources = ARRAY_SIZE(gpio_resources), +}; + +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX3D, "vdd", "kgsl-3d0.0"), +}; +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); + +/* MSM8625 Devices */ + +static struct resource msm8625_resources_uart1[] = { + { + .start = MSM8625_INT_UART1, + .end = MSM8625_INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM7XXX_UART1_PHYS, + .end = MSM7XXX_UART1_PHYS + MSM7XXX_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8625_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(msm8625_resources_uart1), + .resource = msm8625_resources_uart1, +}; + +static struct resource msm8625_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_UART1DM_IRQ, + .end = MSM8625_INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM8625_INT_UART1DM_RX, + .end = MSM8625_INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm8625_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm8625_uart1_dm_resources), + .resource = msm8625_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm8625_uart2dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_UART2DM_IRQ, + .end = MSM8625_INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_device_uart_dm2 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(msm8625_uart2dm_resources), + .resource = msm8625_uart2dm_resources, +}; + +static struct resource msm8625_resources_adsp[] = { + { + .start = MSM8625_INT_ADSP_A9_A11, + .end = MSM8625_INT_ADSP_A9_A11, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_device_adsp = { + .name = "msm_adsp", + .id = -1, + .num_resources = ARRAY_SIZE(msm8625_resources_adsp), + .resource = msm8625_resources_adsp, +}; + +static struct resource msm8625_dmov_resource[] = { + { + .start = MSM8625_INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0xA9700000, + .end = 0xA9700000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm8625_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm8625_dmov_resource, + .num_resources = ARRAY_SIZE(msm8625_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +static struct resource gsbi0_msm8625_qup_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI0_QUP_PHYS, + .end = MSM_GSBI0_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI0_PHYS, + .end = MSM_GSBI0_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = MSM8625_INT_PWB_I2C, + .end = MSM8625_INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +/* Use GSBI0 QUP for /dev/i2c-0 */ +struct platform_device msm8625_gsbi0_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI0_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi0_msm8625_qup_resources), + .resource = gsbi0_msm8625_qup_resources, +}; + +static struct resource gsbi1_msm8625_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI1_QUP_PHYS, + .end = MSM_GSBI1_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = MSM8625_INT_ARM11_DMA, + .end = MSM8625_INT_ARM11_DMA, + .flags = IORESOURCE_IRQ, + }, +}; + +/* Use GSBI1 QUP for /dev/i2c-1 */ +struct platform_device msm8625_gsbi1_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI1_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi1_qup_i2c_resources), + .resource = gsbi1_msm8625_qup_i2c_resources, +}; + +static struct resource msm8625_gpio_resources[] = { + { + .start = MSM8625_INT_GPIO_GROUP1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM8625_INT_GPIO_GROUP2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm8625_device_gpio = { + .name = "msmgpio", + .id = -1, + .resource = msm8625_gpio_resources, + .num_resources = ARRAY_SIZE(msm8625_gpio_resources), +}; + +static struct resource msm8625_resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_SDC1_0, + .end = MSM8625_INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource msm8625_resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_SDC2_0, + .end = MSM8625_INT_SDC2_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource msm8625_resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_SDC3_0, + .end = MSM8625_INT_SDC3_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource msm8625_resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_SDC4_0, + .end = MSM8625_INT_SDC4_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm8625_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(msm8625_resources_sdc1), + .resource = msm8625_resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm8625_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(msm8625_resources_sdc2), + .resource = msm8625_resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm8625_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(msm8625_resources_sdc3), + .resource = msm8625_resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm8625_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(msm8625_resources_sdc4), + .resource = msm8625_resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm8625_sdcc_devices[] __initdata = { + &msm8625_device_sdc1, + &msm8625_device_sdc2, + &msm8625_device_sdc3, + &msm8625_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 4) + return -EINVAL; + + if (cpu_is_msm8625()) + pdev = msm8625_sdcc_devices[controller-1]; + else + pdev = msm_sdcc_devices[controller-1]; + + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +static struct resource msm8625_resources_hsusb_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_USB_HS, + .end = MSM8625_INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(msm8625_resources_hsusb_otg), + .resource = msm8625_resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource msm8625_resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_USB_HS, + .end = MSM8625_INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(msm8625_resources_gadget_peripheral), + .resource = msm8625_resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource msm8625_resources_hsusb_host[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_USB_HS, + .end = MSM8625_INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(msm8625_resources_hsusb_host), + .resource = msm8625_resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm8625_host_devices[] = { + &msm8625_device_hsusb_host, +}; + +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + + if (cpu_is_msm8625()) + pdev = msm8625_host_devices[host]; + else + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#ifdef CONFIG_MSM_CAMERA_V4L2 +static struct resource msm8625_csic0_resources[] = { + { + .name = "csic", + .start = 0xA0F00000, + .end = 0xA0F00000 + 0x00100000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = MSM8625_INT_CSI_IRQ_0, + .end = MSM8625_INT_CSI_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm8625_csic1_resources[] = { + { + .name = "csic", + .start = 0xA1000000, + .end = 0xA1000000 + 0x00100000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = MSM8625_INT_CSI_IRQ_1, + .end = MSM8625_INT_CSI_IRQ_1, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_device_csic0 = { + .name = "msm_csic", + .id = 0, + .resource = msm8625_csic0_resources, + .num_resources = ARRAY_SIZE(msm8625_csic0_resources), +}; + +struct platform_device msm8625_device_csic1 = { + .name = "msm_csic", + .id = 1, + .resource = msm8625_csic1_resources, + .num_resources = ARRAY_SIZE(msm8625_csic1_resources), +}; +#endif + +static struct resource msm8625_mipi_dsi_resources[] = { + { + .name = "mipi_dsi", + .start = MIPI_DSI_HW_BASE, + .end = MIPI_DSI_HW_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_DSI_IRQ, + .end = MSM8625_INT_DSI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm8625_mipi_dsi_device = { + .name = "mipi_dsi", + .id = 1, + .num_resources = ARRAY_SIZE(msm8625_mipi_dsi_resources), + .resource = msm8625_mipi_dsi_resources, +}; + +static struct resource msm8625_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F1008 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM8625_INT_MDP, + .end = MSM8625_INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm8625_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm8625_mdp_resources), + .resource = msm8625_mdp_resources, +}; + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) { + if (cpu_is_msm8625()) + msm_register_device(&msm8625_mdp_device, data); + else + msm_register_device(&msm_mdp_device, data); + } else if (!strncmp(name, "mipi_dsi", 8)) { + if (cpu_is_msm8625()) + msm_register_device(&msm8625_mipi_dsi_device, data); + else + msm_register_device(&msm_mipi_dsi_device, data); + } else if (!strncmp(name, "lcdc", 4)) { + msm_register_device(&msm_lcdc_device, data); + } else { + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); + } +} + +static struct resource msm8625_kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = MSM8625_INT_GRAPHICS, + .end = MSM8625_INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm8625_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(msm8625_kgsl_3d0_resources), + .resource = msm8625_kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +static struct clk_lookup msm_clock_8625_dummy[] = { + CLK_DUMMY("core_clk", adm_clk.c, "msm_dmov", 0), + CLK_DUMMY("adsp_clk", adsp_clk.c, NULL, 0), + CLK_DUMMY("ahb_m_clk", ahb_m_clk.c, NULL, 0), + CLK_DUMMY("ahb_s_clk", ahb_s_clk.c, NULL, 0), + CLK_DUMMY("cam_m_clk", cam_m_clk.c, NULL, 0), + CLK_DUMMY("csi_clk", csi1_clk.c, NULL, 0), + CLK_DUMMY("csi_pclk", csi1_p_clk.c, NULL, 0), + CLK_DUMMY("csi_vfe_clk", csi1_vfe_clk.c, NULL, 0), + CLK_DUMMY("dsi_byte_clk", dsi_byte_clk.c, NULL, 0), + CLK_DUMMY("dsi_clk", dsi_clk.c, NULL, 0), + CLK_DUMMY("dsi_esc_clk", dsi_esc_clk.c, NULL, 0), + CLK_DUMMY("dsi_pixel_clk", dsi_pixel_clk.c, NULL, 0), + CLK_DUMMY("dsi_ref_clk", dsi_ref_clk.c, NULL, 0), + CLK_DUMMY("ebi1_clk", ebi1_clk.c, NULL, 0), + CLK_DUMMY("ebi2_clk", ebi2_clk.c, NULL, 0), + CLK_DUMMY("ecodec_clk", ecodec_clk.c, NULL, 0), + CLK_DUMMY("gp_clk", gp_clk.c, NULL, 0), + CLK_DUMMY("core_clk", gsbi1_qup_clk.c, "qup_i2c.0", 0), + CLK_DUMMY("core_clk", gsbi2_qup_clk.c, "qup_i2c.1", 0), + CLK_DUMMY("iface_clk", gsbi1_qup_p_clk.c, "qup_i2c.0", 0), + CLK_DUMMY("iface_clk", gsbi2_qup_p_clk.c, "qup_i2c.1", 0), + CLK_DUMMY("icodec_rx_clk", icodec_rx_clk.c, NULL, 0), + CLK_DUMMY("icodec_tx_clk", icodec_tx_clk.c, NULL, 0), + CLK_DUMMY("mem_clk", imem_clk.c, NULL, 0), + CLK_DUMMY("mddi_clk", pmdh_clk.c, NULL, 0), + CLK_DUMMY("mdp_clk", mdp_clk.c, NULL, 0), + CLK_DUMMY("mdp_lcdc_pclk_clk", mdp_lcdc_pclk_clk.c, NULL, 0), + CLK_DUMMY("mdp_lcdc_pad_pclk_clk", mdp_lcdc_pad_pclk_clk.c, NULL, 0), + CLK_DUMMY("mdp_vsync_clk", mdp_vsync_clk.c, NULL, 0), + CLK_DUMMY("mdp_dsi_pclk", mdp_dsi_p_clk.c, NULL, 0), + CLK_DUMMY("pbus_clk", pbus_clk.c, NULL, 0), + CLK_DUMMY("pcm_clk", pcm_clk.c, NULL, 0), + CLK_DUMMY("sdac_clk", sdac_clk.c, NULL, 0), + CLK_DUMMY("core_clk", sdc1_clk.c, "msm_sdcc.1", 0), + CLK_DUMMY("iface_clk", sdc1_p_clk.c, "msm_sdcc.1", 0), + CLK_DUMMY("core_clk", sdc2_clk.c, "msm_sdcc.2", 0), + CLK_DUMMY("iface_clk", sdc2_p_clk.c, "msm_sdcc.2", 0), + CLK_DUMMY("core_clk", sdc3_clk.c, "msm_sdcc.3", 0), + CLK_DUMMY("iface_clk", sdc3_p_clk.c, "msm_sdcc.3", 0), + CLK_DUMMY("core_clk", sdc4_clk.c, "msm_sdcc.4", 0), + CLK_DUMMY("iface_clk", sdc4_p_clk.c, "msm_sdcc.4", 0), + CLK_DUMMY("ref_clk", tsif_ref_clk.c, "msm_tsif.0", 0), + CLK_DUMMY("iface_clk", tsif_p_clk.c, "msm_tsif.0", 0), + CLK_DUMMY("core_clk", uart1_clk.c, "msm_serial.0", 0), + CLK_DUMMY("core_clk", uart2_clk.c, "msm_serial.1", 0), + CLK_DUMMY("core_clk", uart1dm_clk.c, "msm_serial_hs.0", 0), + CLK_DUMMY("core_clk", uart2dm_clk.c, "msm_serial_hsl.0", 0), + CLK_DUMMY("usb_hs_core_clk", usb_hs_core_clk.c, NULL, 0), + CLK_DUMMY("usb_hs2_clk", usb_hs2_clk.c, NULL, 0), + CLK_DUMMY("usb_hs_clk", usb_hs_clk.c, NULL, 0), + CLK_DUMMY("usb_hs_pclk", usb_hs_p_clk.c, NULL, 0), + CLK_DUMMY("usb_phy_clk", usb_phy_clk.c, NULL, 0), + CLK_DUMMY("vdc_clk", vdc_clk.c, NULL, 0), + CLK_DUMMY("ebi1_acpu_clk", ebi_acpu_clk.c, NULL, 0), + CLK_DUMMY("ebi1_lcdc_clk", ebi_lcdc_clk.c, NULL, 0), + CLK_DUMMY("ebi1_mddi_clk", ebi_mddi_clk.c, NULL, 0), + CLK_DUMMY("ebi1_usb_clk", ebi_usb_clk.c, NULL, 0), + CLK_DUMMY("ebi1_vfe_clk", ebi_vfe_clk.c, NULL, 0), + CLK_DUMMY("mem_clk", ebi_adm_clk.c, "msm_dmov", 0), +}; + +struct clock_init_data msm8625_dummy_clock_init_data __initdata = { + .table = msm_clock_8625_dummy, + .size = ARRAY_SIZE(msm_clock_8625_dummy), +}; + +enum { + MSM8625, + MSM8625A, +}; + +static int __init msm8625_cpu_id(void) +{ + int raw_id, cpu; + + raw_id = socinfo_get_raw_id(); + switch (raw_id) { + /* Part number for 1GHz part */ + case 0x770: + case 0x771: + case 0x780: + cpu = MSM8625; + break; + /* Part number for 1.2GHz part */ + case 0x773: + case 0x774: + case 0x781: + cpu = MSM8625A; + break; + default: + pr_err("Invalid Raw ID\n"); + return -ENODEV; + } + return cpu; +} + +int __init msm7x2x_misc_init(void) +{ + if (machine_is_msm8625_rumi3()) { + msm_clock_init(&msm8625_dummy_clock_init_data); + return 0; + } + + msm_clock_init(&msm7x27a_clock_init_data); + if (cpu_is_msm7x27aa() || cpu_is_msm7x25ab()) + acpuclk_init(&acpuclk_7x27aa_soc_data); + else if (cpu_is_msm8625()) { + if (msm8625_cpu_id() == MSM8625) + acpuclk_init(&acpuclk_7x27aa_soc_data); + else if (msm8625_cpu_id() == MSM8625A) + acpuclk_init(&acpuclk_8625_soc_data); + } else { + acpuclk_init(&acpuclk_7x27a_soc_data); + } + + + return 0; +} + +#ifdef CONFIG_CACHE_L2X0 +static int __init msm7x27x_cache_init(void) +{ + int aux_ctrl = 0; + int pctrl = 0; + + /* Way Size 010(0x2) 32KB */ + aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \ + (0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \ + (0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT); + + if (cpu_is_msm8625()) { + /* Way Size 011(0x3) 64KB */ + aux_ctrl |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \ + (0x1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) | \ + (0X1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | \ + (0x1 << L2X0_AUX_CTRL_L2_FORCE_NWA_SHIFT); + + /* Write Prefetch Control settings */ + pctrl = readl_relaxed(MSM_L2CC_BASE + L2X0_PREFETCH_CTRL); + pctrl |= (0x3 << L2X0_PREFETCH_CTRL_OFFSET_SHIFT) | \ + (0x1 << L2X0_PREFETCH_CTRL_WRAP8_INC_SHIFT) | \ + (0x1 << L2X0_PREFETCH_CTRL_WRAP8_SHIFT); + writel_relaxed(pctrl , MSM_L2CC_BASE + L2X0_PREFETCH_CTRL); + } + + l2x0_init(MSM_L2CC_BASE, aux_ctrl, L2X0_AUX_CTRL_MASK); + if (cpu_is_msm8625()) { + pctrl = readl_relaxed(MSM_L2CC_BASE + L2X0_PREFETCH_CTRL); + pr_info("Prfetch Ctrl: 0x%08x\n", pctrl); + } + + return 0; +} +#else +static int __init msm7x27x_cache_init(void){ return 0; } +#endif + +void __init msm_common_io_init(void) +{ + msm_map_common_io(); + if (socinfo_init() < 0) + pr_err("%s: socinfo_init() failed!\n", __func__); + msm7x27x_cache_init(); +} + +void __init msm8625_init_irq(void) +{ + msm_gic_irq_extn_init(MSM_QGIC_DIST_BASE, MSM_QGIC_CPU_BASE); + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, + (void *)MSM_QGIC_CPU_BASE); +} + +void __init msm8625_map_io(void) +{ + msm_map_msm8625_io(); + + if (socinfo_init() < 0) + pr_err("%s: socinfo_init() failed!\n", __func__); + msm7x27x_cache_init(); +} + +static int msm7627a_init_gpio(void) +{ + if (cpu_is_msm8625()) + platform_device_register(&msm8625_device_gpio); + else + platform_device_register(&msm_device_gpio); + return 0; +} +postcore_initcall(msm7627a_init_gpio); + +static int msm7627a_panic_handler(struct notifier_block *this, + unsigned long event, void *ptr) +{ + flush_cache_all(); + outer_flush_all(); + return NOTIFY_DONE; +} + +static struct notifier_block panic_handler = { + .notifier_call = msm7627a_panic_handler, +}; + +static int __init panic_register(void) +{ + atomic_notifier_chain_register(&panic_notifier_list, + &panic_handler); + return 0; +} +module_init(panic_register); diff --git a/arch/arm/mach-msm/devices-msm7x2xa.h b/arch/arm/mach-msm/devices-msm7x2xa.h new file mode 100644 index 00000000000..4184a86e08c --- /dev/null +++ b/arch/arm/mach-msm/devices-msm7x2xa.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ARCH_ARM_MACH_MSM_DEVICES_MSM7X2XA_H +#define __ARCH_ARM_MACH_MSM_DEVICES_MSM7X2XA_H + +#define MSM_GSBI0_QUP_I2C_BUS_ID 0 +#define MSM_GSBI1_QUP_I2C_BUS_ID 1 + +void __init msm_common_io_init(void); +void __init msm_init_pmic_vibrator(void); +void __init msm7x25a_kgsl_3d0_init(void); +int __init msm7x2x_misc_init(void); +extern struct platform_device msm7x27a_device_vfe; +extern struct platform_device msm7x27a_device_csic0; +extern struct platform_device msm7x27a_device_csic1; +extern struct platform_device msm7x27a_device_clkctl; + +extern struct platform_device msm8625_device_csic0; +extern struct platform_device msm8625_device_csic1; + +void __init msm8625_init_irq(void); +void __init msm8625_map_io(void); +int ar600x_wlan_power(bool on); +void __init msm8x25_spm_device_init(void); +void __init msm8x25_kgsl_3d0_init(void); +void __iomem *core1_reset_base(void); +#endif diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index 09b4f140382..de704298d01 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Google, Inc. - * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,23 +15,93 @@ #include #include - +#include #include -#include +#include +#include +#include +#include #include #include #include #include +#include #include "devices.h" -#include "smd_private.h" +#include "footswitch.h" #include -#include "clock-pcom.h" -#include "clock-7x30.h" +#include +#include +#ifdef CONFIG_PMIC8058 +#include +#endif +#include +#include +#include "pm.h" +#include "irq.h" -#include +/* EBI THERMAL DRIVER */ +static struct resource msm_ebi0_thermal_resources[] = { + { + .start = 0xA8600000, + .end = 0xA86005FF, + .name = "physbase", + .flags = IORESOURCE_MEM + } +}; + +struct platform_device msm_ebi0_thermal = { + .name = "msm_popmem-tm", + .id = 0, + .num_resources = 1, + .resource = msm_ebi0_thermal_resources +}; + +static struct resource msm_ebi1_thermal_resources[] = { + { + .start = 0xA8700000, + .end = 0xA87005FF, + .name = "physbase", + .flags = IORESOURCE_MEM + } +}; + +struct platform_device msm_ebi1_thermal = { + .name = "msm_popmem-tm", + .id = 1, + .num_resources = 1, + .resource = msm_ebi1_thermal_resources +}; + +static struct resource resources_adsp[] = { +{ + .start = INT_ADSP_A9_A11, + .end = INT_ADSP_A9_A11, + .flags = IORESOURCE_IRQ, +}, +}; + +struct platform_device msm_adsp_device = { + .name = "msm_adsp", + .id = -1, + .num_resources = ARRAY_SIZE(resources_adsp), + .resource = resources_adsp, +}; + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM7X30_UART1_PHYS, + .end = MSM7X30_UART1_PHYS + MSM7X30_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; static struct resource resources_uart2[] = { { @@ -40,13 +110,33 @@ static struct resource resources_uart2[] = { .flags = IORESOURCE_IRQ, }, { - .start = MSM_UART2_PHYS, - .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1, + .start = MSM7X30_UART2_PHYS, + .end = MSM7X30_UART2_PHYS + MSM7X30_UART2_SIZE - 1, .flags = IORESOURCE_MEM, .name = "uart_resource" }, }; +static struct resource resources_uart3[] = { + { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM7X30_UART3_PHYS, + .end = MSM7X30_UART3_PHYS + MSM7X30_UART3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + struct platform_device msm_device_uart2 = { .name = "msm_serial", .id = 1, @@ -54,15 +144,303 @@ struct platform_device msm_device_uart2 = { .resource = resources_uart2, }; -struct platform_device msm_device_smd = { - .name = "msm_smd", - .id = -1, +struct platform_device msm_device_uart3 = { + .name = "msm_serial", + .id = 2, + .num_resources = ARRAY_SIZE(resources_uart3), + .resource = resources_uart3, }; -static struct resource resources_otg[] = { +#define MSM_UART1DM_PHYS 0xA3300000 +#define MSM_UART2DM_PHYS 0xA3200000 +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_I2C_SIZE SZ_4K +#define MSM_I2C_PHYS 0xACD00000 +#define MSM_I2C_2_PHYS 0xACF00000 +static struct resource resources_i2c_2[] = { + { + .start = MSM_I2C_2_PHYS, + .end = MSM_I2C_2_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C_2, + .end = INT_PWB_I2C_2, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c_2 = { + .name = "msm_i2c", + .id = 2, + .num_resources = ARRAY_SIZE(resources_i2c_2), + .resource = resources_i2c_2, +}; + +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +#ifdef CONFIG_MSM_CAMERA_V4L2 +static struct resource msm_csic_resources[] = { + { + .name = "csic", + .start = 0xA6100000, + .end = 0xA6100000 + 0x00000400 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = INT_CSI, + .end = INT_CSI, + .flags = IORESOURCE_IRQ, + }, +}; + +struct resource msm_vfe_resources[] = { + { + .name = "msm_vfe", + .start = 0xA6000000, + .end = 0xA6000000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "msm_vfe", + .start = INT_VFE, + .end = INT_VFE, + .flags = IORESOURCE_IRQ, + }, + { + .name = "msm_camif", + .start = 0xAB000000, + .end = 0xAB000000 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_vpe_resources[] = { + { + .name = "vpe", + .start = 0xAD200000, + .end = 0xAD200000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "vpe", + .start = INT_VPE, + .end = INT_VPE, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_csic0 = { + .name = "msm_csic", + .id = 0, + .resource = msm_csic_resources, + .num_resources = ARRAY_SIZE(msm_csic_resources), +}; + +struct platform_device msm_device_vfe = { + .name = "msm_vfe", + .id = 0, + .resource = msm_vfe_resources, + .num_resources = ARRAY_SIZE(msm_vfe_resources), +}; + +struct platform_device msm_device_vpe = { + .name = "msm_vpe", + .id = 0, + .resource = msm_vpe_resources, + .num_resources = ARRAY_SIZE(msm_vpe_resources), +}; +#endif + +#define MSM_QUP_PHYS 0xA8301000 +#define MSM_GSBI_QUP_I2C_PHYS 0xA8300000 +#define MSM_QUP_SIZE SZ_4K +static struct resource resources_qup[] = { + { + .name = "qup_phys_addr", + .start = MSM_QUP_PHYS, + .end = MSM_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI_QUP_I2C_PHYS, + .end = MSM_GSBI_QUP_I2C_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_in_intr", + .start = INT_PWB_QUP_IN, + .end = INT_PWB_QUP_IN, + .flags = IORESOURCE_IRQ, + }, + { + .name = "qup_out_intr", + .start = INT_PWB_QUP_OUT, + .end = INT_PWB_QUP_OUT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "qup_err_intr", + .start = INT_PWB_QUP_ERR, + .end = INT_PWB_QUP_ERR, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device qup_device_i2c = { + .name = "qup_i2c", + .id = 4, + .num_resources = ARRAY_SIZE(resources_qup), + .resource = resources_qup, +}; + +#ifdef CONFIG_MSM_SSBI +#define MSM_SSBI_PMIC1_PHYS 0xAD900000 +static struct resource msm_ssbi_pmic1_resources[] = { + { + .start = MSM_SSBI_PMIC1_PHYS, + .end = MSM_SSBI_PMIC1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi_pmic1 = { + .name = "msm_ssbi", + .id = 0, + .resource = msm_ssbi_pmic1_resources, + .num_resources = ARRAY_SIZE(msm_ssbi_pmic1_resources), +}; +#endif + +#ifdef CONFIG_I2C_SSBI +#define MSM_SSBI7_PHYS 0xAC800000 +static struct resource msm_ssbi7_resources[] = { + { + .name = "ssbi_base", + .start = MSM_SSBI7_PHYS, + .end = MSM_SSBI7_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi7 = { + .name = "i2c_ssbi", + .id = 7, + .num_resources = ARRAY_SIZE(msm_ssbi7_resources), + .resource = msm_ssbi7_resources, +}; +#endif /* CONFIG_I2C_SSBI */ + +#define MSM_HSUSB_PHYS 0xA3600000 +static struct resource resources_hsusb_otg[] = { { .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, .flags = IORESOURCE_MEM, }, { @@ -72,44 +450,70 @@ static struct resource resources_otg[] = { }, }; -struct platform_device msm_device_otg = { - .name = "msm_otg", - .id = -1, - .num_resources = ARRAY_SIZE(resources_otg), - .resource = resources_otg, - .dev = { - .coherent_dma_mask = 0xffffffff, - }, -}; - -static struct resource resources_hsusb[] = { - { - .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_USB_HS, - .end = INT_USB_HS, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device msm_device_hsusb = { - .name = "msm_hsusb", - .id = -1, - .num_resources = ARRAY_SIZE(resources_hsusb), - .resource = resources_hsusb, - .dev = { - .coherent_dma_mask = 0xffffffff, - }, -}; - static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_hsusb_otg = { + .name = "msm_hsusb_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_otg), + .resource = resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_peripheral = { + .name = "msm_hsusb_peripheral", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_peripheral), + .resource = resources_hsusb_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_gadget_peripheral), + .resource = resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + static struct resource resources_hsusb_host[] = { { .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, .flags = IORESOURCE_MEM, }, { @@ -121,90 +525,854 @@ static struct resource resources_hsusb_host[] = { struct platform_device msm_device_hsusb_host = { .name = "msm_hsusb_host", - .id = -1, + .id = 0, .num_resources = ARRAY_SIZE(resources_hsusb_host), .resource = resources_hsusb_host, .dev = { - .dma_mask = &dma_mask, - .coherent_dma_mask = 0xffffffffULL, + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, }, }; -struct clk_lookup msm_clocks_7x30[] = { - CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), - CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), - CLK_PCOM("cam_m_clk", CAM_M_CLK, NULL, 0), - CLK_PCOM("camif_pad_pclk", CAMIF_PAD_P_CLK, NULL, OFF), - CLK_PCOM("ce_clk", CE_CLK, NULL, 0), - CLK_PCOM("codec_ssbi_clk", CODEC_SSBI_CLK, NULL, 0), - CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), - CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), - CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("emdh_pclk", EMDH_P_CLK, NULL, OFF), - CLK_PCOM("gp_clk", GP_CLK, NULL, 0), - CLK_PCOM("grp_2d_clk", GRP_2D_CLK, NULL, 0), - CLK_PCOM("grp_2d_pclk", GRP_2D_P_CLK, NULL, 0), - CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, 0), - CLK_PCOM("grp_pclk", GRP_3D_P_CLK, NULL, 0), - CLK_7X30S("grp_src_clk", GRP_3D_SRC_CLK, GRP_3D_CLK, NULL, 0), - CLK_PCOM("hdmi_clk", HDMI_CLK, NULL, 0), - CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), - CLK_PCOM("jpeg_clk", JPEG_CLK, NULL, OFF), - CLK_PCOM("jpeg_pclk", JPEG_P_CLK, NULL, OFF), - CLK_PCOM("lpa_codec_clk", LPA_CODEC_CLK, NULL, 0), - CLK_PCOM("lpa_core_clk", LPA_CORE_CLK, NULL, 0), - CLK_PCOM("lpa_pclk", LPA_P_CLK, NULL, 0), - CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), - CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("mddi_pclk", PMDH_P_CLK, NULL, 0), - CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), - CLK_PCOM("mdp_pclk", MDP_P_CLK, NULL, 0), - CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), - CLK_PCOM("mfc_clk", MFC_CLK, NULL, 0), - CLK_PCOM("mfc_div2_clk", MFC_DIV2_CLK, NULL, 0), - CLK_PCOM("mfc_pclk", MFC_P_CLK, NULL, 0), - CLK_PCOM("mi2s_m_clk", MI2S_M_CLK, NULL, 0), - CLK_PCOM("mi2s_s_clk", MI2S_S_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_rx_m_clk", MI2S_CODEC_RX_M_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_rx_s_clk", MI2S_CODEC_RX_S_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_tx_m_clk", MI2S_CODEC_TX_M_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_tx_s_clk", MI2S_CODEC_TX_S_CLK, NULL, 0), - CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), - CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), - CLK_PCOM("rotator_clk", AXI_ROTATOR_CLK, NULL, 0), - CLK_PCOM("rotator_imem_clk", ROTATOR_IMEM_CLK, NULL, OFF), - CLK_PCOM("rotator_pclk", ROTATOR_P_CLK, NULL, OFF), - CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), - CLK_PCOM("spi_clk", SPI_CLK, NULL, 0), - CLK_PCOM("spi_pclk", SPI_P_CLK, NULL, 0), - CLK_7X30S("tv_src_clk", TV_CLK, TV_ENC_CLK, NULL, 0), - CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), - CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), - CLK_PCOM("uart_clk", UART2_CLK, "msm_serial.1", 0), - CLK_PCOM("usb_phy_clk", USB_PHY_CLK, NULL, 0), - CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), - CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, NULL, OFF), - CLK_PCOM("usb_hs_core_clk", USB_HS_CORE_CLK, NULL, OFF), - CLK_PCOM("usb_hs2_clk", USB_HS2_CLK, NULL, OFF), - CLK_PCOM("usb_hs2_pclk", USB_HS2_P_CLK, NULL, OFF), - CLK_PCOM("usb_hs2_core_clk", USB_HS2_CORE_CLK, NULL, OFF), - CLK_PCOM("usb_hs3_clk", USB_HS3_CLK, NULL, OFF), - CLK_PCOM("usb_hs3_pclk", USB_HS3_P_CLK, NULL, OFF), - CLK_PCOM("usb_hs3_core_clk", USB_HS3_CORE_CLK, NULL, OFF), - CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), - CLK_PCOM("vfe_camif_clk", VFE_CAMIF_CLK, NULL, 0), - CLK_PCOM("vfe_clk", VFE_CLK, NULL, 0), - CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, 0), - CLK_PCOM("vfe_pclk", VFE_P_CLK, NULL, OFF), - CLK_PCOM("vpe_clk", VPE_CLK, NULL, 0), - - /* 7x30 v2 hardware only. */ - CLK_PCOM("csi_clk", CSI0_CLK, NULL, 0), - CLK_PCOM("csi_pclk", CSI0_P_CLK, NULL, 0), - CLK_PCOM("csi_vfe_clk", CSI0_VFE_CLK, NULL, 0), +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, }; -unsigned msm_num_clocks_7x30 = ARRAY_SIZE(msm_clocks_7x30); +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +struct platform_device asoc_msm_pcm = { + .name = "msm-dsp-audio", + .id = 0, +}; + +struct platform_device asoc_msm_dai0 = { + .name = "msm-codec-dai", + .id = 0, +}; + +struct platform_device asoc_msm_dai1 = { + .name = "msm-cpu-dai", + .id = 0, +}; + +#if defined (CONFIG_SND_MSM_MVS_DAI_SOC) +struct platform_device asoc_msm_mvs = { + .name = "msm-mvs-audio", + .id = 0, +}; + +struct platform_device asoc_mvs_dai0 = { + .name = "mvs-codec-dai", + .id = 0, +}; + +struct platform_device asoc_mvs_dai1 = { + .name = "mvs-cpu-dai", + .id = 0, +}; +#endif + +#define MSM_NAND_PHYS 0xA0200000 +#define MSM_NANDC01_PHYS 0xA0240000 +#define MSM_NANDC10_PHYS 0xA0280000 +#define MSM_NANDC11_PHYS 0xA02C0000 +#define EBI2_REG_BASE 0xA0000000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [2] = { + .name = "msm_nandc01_phys", + .start = MSM_NANDC01_PHYS, + .end = MSM_NANDC01_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [3] = { + .name = "msm_nandc10_phys", + .start = MSM_NANDC10_PHYS, + .end = MSM_NANDC10_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [4] = { + .name = "msm_nandc11_phys", + .start = MSM_NANDC11_PHYS, + .end = MSM_NANDC11_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, + [5] = { + .name = "ebi2_reg_base", + .start = EBI2_REG_BASE, + .end = EBI2_REG_BASE + 0x60, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, + { + .name = "vbus_on", + .start = PMIC8058_IRQ_BASE + PM8058_CHGVAL_IRQ, + .end = PMIC8058_IRQ_BASE + PM8058_CHGVAL_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, + .dev = { + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct flash_platform_data msm_nand_data = { + .version = VERSION_2, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +static struct msm_pm_irq_calls msm7x30_pm_irq_calls = { + .irq_pending = msm_irq_pending, + .idle_sleep_allowed = msm_irq_idle_sleep_allowed, + .enter_sleep1 = msm_irq_enter_sleep1, + .enter_sleep2 = msm_irq_enter_sleep2, + .exit_sleep1 = msm_irq_exit_sleep1, + .exit_sleep2 = msm_irq_exit_sleep2, + .exit_sleep3 = msm_irq_exit_sleep3, +}; + +void __init msm_pm_register_irqs(void) +{ + msm_pm_set_irq_extns(&msm7x30_pm_irq_calls); +} + +static struct resource smd_resource[] = { + { + .name = "a9_m2a_0", + .start = INT_A9_M2A_0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "a9_m2a_5", + .start = INT_A9_M2A_5, + .flags = IORESOURCE_IRQ, + }, + { + .name = "adsp_a11_smsm", + .start = INT_ADSP_A11, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smd_subsystem_config smd_config_list[] = { + { + .irq_config_id = SMD_MODEM, + .subsys_name = "modem", + .edge = SMD_APPS_MODEM, + + .smd_int.irq_name = "a9_m2a_0", + .smd_int.flags = IRQF_TRIGGER_RISING, + .smd_int.irq_id = -1, + .smd_int.device_name = "smd_dev", + .smd_int.dev_id = 0, + + .smd_int.out_bit_pos = 1 << 0, + .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smd_int.out_offset = 0x8, + + .smsm_int.irq_name = "a9_m2a_5", + .smsm_int.flags = IRQF_TRIGGER_RISING, + .smsm_int.irq_id = -1, + .smsm_int.device_name = "smd_dev", + .smsm_int.dev_id = 0, + + .smsm_int.out_bit_pos = 1 << 5, + .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE, + .smsm_int.out_offset = 0x8, + + } +}; + +static struct smd_platform smd_platform_data = { + .num_ss_configs = ARRAY_SIZE(smd_config_list), + .smd_ss_configs = smd_config_list, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, + .resource = smd_resource, + .num_resources = ARRAY_SIZE(smd_resource), + .dev = { + .platform_data = &smd_platform_data, + } + +}; + +static struct resource msm_dmov_resource[] = { + { + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0xAC400000, + .end = 0xAC400000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 2, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, + }, +}; + +#define MSM_SDC1_BASE 0xA0400000 +#define MSM_SDC2_BASE 0xA0500000 +#define MSM_SDC3_BASE 0xA3000000 +#define MSM_SDC4_BASE 0xA3100000 +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +static struct resource msm_vidc_720p_resources[] = { + { + .start = 0xA3B00000, + .end = 0xA3B00000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MFC720, + .end = INT_MFC720, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_vidc_platform_data vidc_platform_data = { + .memtype = MEMTYPE_EBI0, + .enable_ion = 0, + .disable_dmx = 0, + .cont_mode_dpb_count = 8 +}; + +struct platform_device msm_device_vidc_720p = { + .name = "msm_vidc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_vidc_720p_resources), + .resource = msm_vidc_720p_resources, + .dev = { + .platform_data = &vidc_platform_data, + }, +}; + +#if defined(CONFIG_FB_MSM_MDP40) +#define MDP_BASE 0xA3F00000 +#define PMDH_BASE 0xAD600000 +#define EMDH_BASE 0xAD700000 +#define TVENC_BASE 0xAD400000 +#else +#define MDP_BASE 0xAA200000 +#define PMDH_BASE 0xAA600000 +#define EMDH_BASE 0xAA700000 +#define TVENC_BASE 0xAA400000 +#endif + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_mddi_resources[] = { + { + .name = "pmdh", + .start = PMDH_BASE, + .end = PMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_mddi_ext_resources[] = { + { + .name = "emdh", + .start = EMDH_BASE, + .end = EMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_ebi2_lcd_resources[] = { + { + .name = "base", + .start = 0xa0d00000, + .end = 0xa0d00000 + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd01", + .start = 0x98000000, + .end = 0x98000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd02", + .start = 0x9c000000, + .end = 0x9c000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_tvenc_resources[] = { + { + .name = "tvenc", + .start = TVENC_BASE, + .end = TVENC_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +#ifdef CONFIG_FB_MSM_TVOUT +static struct resource tvout_device_resources[] = { + { + .name = "tvout_device_irq", + .start = INT_TV_ENC, + .end = INT_TV_ENC, + .flags = IORESOURCE_IRQ, + }, +}; +#endif + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static struct platform_device msm_mddi_device = { + .name = "mddi", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_resources), + .resource = msm_mddi_resources, +}; + +static struct platform_device msm_mddi_ext_device = { + .name = "mddi_ext", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_ext_resources), + .resource = msm_mddi_ext_resources, +}; + +static struct platform_device msm_ebi2_lcd_device = { + .name = "ebi2_lcd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_ebi2_lcd_resources), + .resource = msm_ebi2_lcd_resources, +}; + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +static struct platform_device msm_dtv_device = { + .name = "dtv", + .id = 0, +}; + +static struct platform_device msm_tvenc_device = { + .name = "tvenc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, +}; + +#ifdef CONFIG_FB_MSM_TVOUT +static struct platform_device tvout_msm_device = { + .name = "tvout_device", + .id = 0, + .num_resources = ARRAY_SIZE(tvout_device_resources), + .resource = tvout_device_resources, +}; +#endif + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define MSM_TSIF_PHYS (0xa3400000) +#define MSM_TSIF_SIZE (0x200) + +static struct resource tsif_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = INT_TSIF, + .end = INT_TSIF, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF_PHYS, + .end = MSM_TSIF_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +static void tsif_release(struct device *dev) +{ + dev_info(dev, "release\n"); +} + +struct platform_device msm_device_tsif = { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif_resources), + .resource = tsif_resources, + .dev = { + .release = tsif_release, + }, +}; +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + + + +#ifdef CONFIG_MSM_ROTATOR +static struct resource resources_msm_rotator[] = { + { + .start = 0xA3E00000, + .end = 0xA3F00000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_ROTATOR, + .end = INT_ROTATOR, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_rot_clocks rotator_clocks[] = { + { + .clk_name = "core_clk", + .clk_type = ROTATOR_CORE_CLK, + .clk_rate = 0, + }, + { + .clk_name = "iface_clk", + .clk_type = ROTATOR_PCLK, + .clk_rate = 0, + }, + { + .clk_name = "mem_clk", + .clk_type = ROTATOR_IMEM_CLK, + .clk_rate = 0, + }, +}; + +static struct msm_rotator_platform_data rotator_pdata = { + .number_of_clocks = ARRAY_SIZE(rotator_clocks), + .hardware_version_number = 0x1000303, + .rotator_clks = rotator_clocks, +}; + +struct platform_device msm_rotator_device = { + .name = "msm_rotator", + .id = 0, + .num_resources = ARRAY_SIZE(resources_msm_rotator), + .resource = resources_msm_rotator, + .dev = { + .platform_data = &rotator_pdata, + }, +}; +#endif + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "pmdh", 4)) + msm_register_device(&msm_mddi_device, data); + else if (!strncmp(name, "emdh", 4)) + msm_register_device(&msm_mddi_ext_device, data); + else if (!strncmp(name, "ebi2", 4)) + msm_register_device(&msm_ebi2_lcd_device, data); + else if (!strncmp(name, "tvenc", 5)) + msm_register_device(&msm_tvenc_device, data); + else if (!strncmp(name, "lcdc", 4)) + msm_register_device(&msm_lcdc_device, data); + else if (!strncmp(name, "dtv", 3)) + msm_register_device(&msm_dtv_device, data); +#ifdef CONFIG_FB_MSM_TVOUT + else if (!strncmp(name, "tvout_device", 12)) + msm_register_device(&tvout_msm_device, data); +#endif + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct platform_device msm_camera_device = { + .name = "msm_camera", + .id = 0, +}; + +void __init msm_camera_register_device(void *res, uint32_t num, + void *data) +{ + msm_camera_device.num_resources = num; + msm_camera_device.resource = res; + + msm_register_device(&msm_camera_device, data); +} + +struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA3500000, /* 3D GRP address */ + .end = 0xA351ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRP_3D, + .end = INT_GRP_3D, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 245760000, + .bus_freq = 192000000, + }, + { + .gpu_freq = 192000000, + .bus_freq = 152000000, + }, + { + .gpu_freq = 192000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = 3, + .set_grp_async = set_grp3d_async, + .idle_timeout = HZ/20, + .nap_allowed = true, + .idle_needed = true, + .clk_map = KGSL_CLK_SRC | KGSL_CLK_CORE | + KGSL_CLK_IFACE | KGSL_CLK_MEM, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +static struct resource kgsl_2d0_resources[] = { + { + .name = KGSL_2D0_REG_MEMORY, + .start = 0xA3900000, /* Z180 base address */ + .end = 0xA3900FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_2D0_IRQ, + .start = INT_GRP_2D, + .end = INT_GRP_2D, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_2d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 192000000, + }, + }, + .init_level = 0, + .num_levels = 1, + /* HW workaround, run Z180 SYNC @ 192 MHZ */ + .set_grp_async = NULL, + .idle_timeout = HZ/10, + .nap_allowed = true, + .idle_needed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE, +}; + +struct platform_device msm_kgsl_2d0 = { + .name = "kgsl-2d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_2d0_resources), + .resource = kgsl_2d0_resources, + .dev = { + .platform_data = &kgsl_2d0_pdata, + }, +}; + +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX2D0, "vdd", "kgsl-2d0.0"), + FS_PCOM(FS_GFX3D, "vdd", "kgsl-3d0.0"), + FS_PCOM(FS_MDP, "vdd", "mdp.0"), + FS_PCOM(FS_MFC, "fs_mfc", NULL), + FS_PCOM(FS_ROT, "vdd", "msm_rotator.0"), + FS_PCOM(FS_VFE, "fs_vfe", NULL), + FS_PCOM(FS_VPE, "fs_vpe", NULL), +}; +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); + +static struct resource gpio_resources[] = { + { + .start = INT_GPIO_GROUP1, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_GPIO_GROUP2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_device_gpio = { + .name = "msmgpio", + .id = -1, + .resource = gpio_resources, + .num_resources = ARRAY_SIZE(gpio_resources), +}; + +static int __init msm7630_init_gpio(void) +{ + platform_device_register(&msm_device_gpio); + return 0; +} + +postcore_initcall(msm7630_init_gpio); diff --git a/arch/arm/mach-msm/devices-msm8960.c b/arch/arm/mach-msm/devices-msm8960.c deleted file mode 100644 index d9e1f26475d..00000000000 --- a/arch/arm/mach-msm/devices-msm8960.c +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include -#include - -#include -#include -#include - -#include "devices.h" - -#define MSM_GSBI2_PHYS 0x16100000 -#define MSM_UART2DM_PHYS (MSM_GSBI2_PHYS + 0x40000) - -#define MSM_GSBI5_PHYS 0x16400000 -#define MSM_UART5DM_PHYS (MSM_GSBI5_PHYS + 0x40000) - -static struct resource resources_uart_gsbi2[] = { - { - .start = GSBI2_UARTDM_IRQ, - .end = GSBI2_UARTDM_IRQ, - .flags = IORESOURCE_IRQ, - }, - { - .start = MSM_UART2DM_PHYS, - .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, - .name = "uart_resource", - .flags = IORESOURCE_MEM, - }, - { - .start = MSM_GSBI2_PHYS, - .end = MSM_GSBI2_PHYS + PAGE_SIZE - 1, - .name = "gsbi_resource", - .flags = IORESOURCE_MEM, - }, -}; - -struct platform_device msm8960_device_uart_gsbi2 = { - .name = "msm_serial", - .id = 0, - .num_resources = ARRAY_SIZE(resources_uart_gsbi2), - .resource = resources_uart_gsbi2, -}; - -static struct resource resources_uart_gsbi5[] = { - { - .start = GSBI5_UARTDM_IRQ, - .end = GSBI5_UARTDM_IRQ, - .flags = IORESOURCE_IRQ, - }, - { - .start = MSM_UART5DM_PHYS, - .end = MSM_UART5DM_PHYS + PAGE_SIZE - 1, - .name = "uart_resource", - .flags = IORESOURCE_MEM, - }, - { - .start = MSM_GSBI5_PHYS, - .end = MSM_GSBI5_PHYS + PAGE_SIZE - 1, - .name = "gsbi_resource", - .flags = IORESOURCE_MEM, - }, -}; - -struct platform_device msm8960_device_uart_gsbi5 = { - .name = "msm_serial", - .id = 0, - .num_resources = ARRAY_SIZE(resources_uart_gsbi5), - .resource = resources_uart_gsbi5, -}; diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c new file mode 100644 index 00000000000..37844c1cd8a --- /dev/null +++ b/arch/arm/mach-msm/devices-msm8x60.c @@ -0,0 +1,2955 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "footswitch.h" +#include "clock.h" +#include "clock-rpm.h" +#include "clock-voter.h" +#include "devices.h" +#include "devices-msm8x60.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MSM_DSPS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "rpm_log.h" +#include "rpm_stats.h" +#include +#include "msm_watchdog.h" + +/* Address of GSBI blocks */ +#define MSM_GSBI1_PHYS 0x16000000 +#define MSM_GSBI2_PHYS 0x16100000 +#define MSM_GSBI3_PHYS 0x16200000 +#define MSM_GSBI4_PHYS 0x16300000 +#define MSM_GSBI5_PHYS 0x16400000 +#define MSM_GSBI6_PHYS 0x16500000 +#define MSM_GSBI7_PHYS 0x16600000 +#define MSM_GSBI8_PHYS 0x19800000 +#define MSM_GSBI9_PHYS 0x19900000 +#define MSM_GSBI10_PHYS 0x19A00000 +#define MSM_GSBI11_PHYS 0x19B00000 +#define MSM_GSBI12_PHYS 0x19C00000 + +/* GSBI QUPe devices */ +#define MSM_GSBI1_QUP_PHYS 0x16080000 +#define MSM_GSBI2_QUP_PHYS 0x16180000 +#define MSM_GSBI3_QUP_PHYS 0x16280000 +#define MSM_GSBI4_QUP_PHYS 0x16380000 +#define MSM_GSBI5_QUP_PHYS 0x16480000 +#define MSM_GSBI6_QUP_PHYS 0x16580000 +#define MSM_GSBI7_QUP_PHYS 0x16680000 +#define MSM_GSBI8_QUP_PHYS 0x19880000 +#define MSM_GSBI9_QUP_PHYS 0x19980000 +#define MSM_GSBI10_QUP_PHYS 0x19A80000 +#define MSM_GSBI11_QUP_PHYS 0x19B80000 +#define MSM_GSBI12_QUP_PHYS 0x19C80000 + +/* GSBI UART devices */ +#define MSM_UART1DM_PHYS (MSM_GSBI6_PHYS + 0x40000) +#define INT_UART1DM_IRQ GSBI6_UARTDM_IRQ +#define INT_UART2DM_IRQ GSBI12_UARTDM_IRQ +#define MSM_UART2DM_PHYS 0x19C40000 +#define MSM_UART3DM_PHYS (MSM_GSBI3_PHYS + 0x40000) +#define INT_UART3DM_IRQ GSBI3_UARTDM_IRQ +#define TCSR_BASE_PHYS 0x16b00000 + +/* PRNG device */ +#define MSM_PRNG_PHYS 0x16C00000 +#define MSM_UART9DM_PHYS (MSM_GSBI9_PHYS + 0x40000) +#define INT_UART9DM_IRQ GSBI9_UARTDM_IRQ + +static void charm_ap2mdm_kpdpwr_on(void) +{ + gpio_direction_output(AP2MDM_PMIC_RESET_N, 0); + gpio_direction_output(AP2MDM_KPDPWR_N, 1); +} + +static void charm_ap2mdm_kpdpwr_off(void) +{ + int i; + + gpio_direction_output(AP2MDM_ERRFATAL, 1); + + for (i = 20; i > 0; i--) { + if (gpio_get_value(MDM2AP_STATUS) == 0) + break; + msleep(100); + } + gpio_direction_output(AP2MDM_ERRFATAL, 0); + + if (i == 0) { + pr_err("%s: MDM2AP_STATUS never went low. Doing a hard reset \ + of the charm modem.\n", __func__); + gpio_direction_output(AP2MDM_PMIC_RESET_N, 1); + /* + * Currently, there is a debounce timer on the charm PMIC. It is + * necessary to hold the AP2MDM_PMIC_RESET low for ~3.5 seconds + * for the reset to fully take place. Sleep here to ensure the + * reset has occured before the function exits. + */ + msleep(4000); + gpio_direction_output(AP2MDM_PMIC_RESET_N, 0); + } +} + +static struct resource charm_resources[] = { + /* MDM2AP_ERRFATAL */ + { + .start = MSM_GPIO_TO_INT(MDM2AP_ERRFATAL), + .end = MSM_GPIO_TO_INT(MDM2AP_ERRFATAL), + .flags = IORESOURCE_IRQ, + }, + /* MDM2AP_STATUS */ + { + .start = MSM_GPIO_TO_INT(MDM2AP_STATUS), + .end = MSM_GPIO_TO_INT(MDM2AP_STATUS), + .flags = IORESOURCE_IRQ, + } +}; + +static struct charm_platform_data mdm_platform_data = { + .charm_modem_on = charm_ap2mdm_kpdpwr_on, + .charm_modem_off = charm_ap2mdm_kpdpwr_off, +}; + +struct platform_device msm_charm_modem = { + .name = "charm_modem", + .id = -1, + .num_resources = ARRAY_SIZE(charm_resources), + .resource = charm_resources, + .dev = { + .platform_data = &mdm_platform_data, + }, +}; + +#ifdef CONFIG_MSM_DSPS +#define GSBI12_DEV (&msm_dsps_device.dev) +#else +#define GSBI12_DEV (&msm_gsbi12_qup_i2c_device.dev) +#endif + +void __init msm8x60_init_irq(void) +{ + struct msm_mpm_device_data *data = NULL; + +#ifdef CONFIG_MSM_MPM + data = &msm8660_mpm_dev_data; +#endif + + msm_mpm_irq_extn_init(data); + gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE, (void *)MSM_QGIC_CPU_BASE); +} + +#define MSM_LPASS_QDSP6SS_PHYS 0x28800000 + +static struct resource msm_8660_q6_resources[] = { + { + .start = MSM_LPASS_QDSP6SS_PHYS, + .end = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_pil_q6v3 = { + .name = "pil_qdsp6v3", + .id = -1, + .num_resources = ARRAY_SIZE(msm_8660_q6_resources), + .resource = msm_8660_q6_resources, +}; + +#define MSM_MSS_REGS_PHYS 0x10200000 + +static struct resource msm_8660_modem_resources[] = { + { + .start = MSM_MSS_REGS_PHYS, + .end = MSM_MSS_REGS_PHYS + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_pil_modem = { + .name = "pil_modem", + .id = -1, + .num_resources = ARRAY_SIZE(msm_8660_modem_resources), + .resource = msm_8660_modem_resources, +}; + +struct platform_device msm_pil_tzapps = { + .name = "pil_tzapps", + .id = -1, +}; + +struct platform_device msm_pil_dsps = { + .name = "pil_dsps", + .id = -1, + .dev.platform_data = "dsps", +}; + +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + /* GSBI6 is UARTDM1 */ + .start = MSM_GSBI6_PHYS, + .end = MSM_GSBI6_PHYS + 4 - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart3_dm_resources[] = { + { + .start = MSM_UART3DM_PHYS, + .end = MSM_UART3DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART3DM_IRQ, + .end = INT_UART3DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_GSBI3_PHYS, + .end = MSM_GSBI3_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart_dm3 = { + .name = "msm_serial_hsl", + .id = 2, + .num_resources = ARRAY_SIZE(msm_uart3_dm_resources), + .resource = msm_uart3_dm_resources, +}; + +static struct resource msm_uart12_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + /* GSBI 12 is UARTDM2 */ + .start = MSM_GSBI12_PHYS, + .end = MSM_GSBI12_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart_dm12 = { + .name = "msm_serial_hsl", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart12_dm_resources), + .resource = msm_uart12_dm_resources, +}; + +#ifdef CONFIG_MSM_GSBI9_UART +static struct msm_serial_hslite_platform_data uart_gsbi9_pdata = { + .config_gpio = 1, + .uart_tx_gpio = 67, + .uart_rx_gpio = 66, + .line = 1, +}; + +static struct resource msm_uart_gsbi9_resources[] = { + { + .start = MSM_UART9DM_PHYS, + .end = MSM_UART9DM_PHYS + PAGE_SIZE - 1, + .name = "uartdm_resource", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART9DM_IRQ, + .end = INT_UART9DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + /* GSBI 9 is UART_GSBI9 */ + .start = MSM_GSBI9_PHYS, + .end = MSM_GSBI9_PHYS + PAGE_SIZE - 1, + .name = "gsbi_resource", + .flags = IORESOURCE_MEM, + }, +}; +struct platform_device *msm_device_uart_gsbi9; +struct platform_device *msm_add_gsbi9_uart(void) +{ + return platform_device_register_resndata(NULL, "msm_serial_hsl", + 1, msm_uart_gsbi9_resources, + ARRAY_SIZE(msm_uart_gsbi9_resources), + &uart_gsbi9_pdata, + sizeof(uart_gsbi9_pdata)); +} +#endif + +static struct resource gsbi3_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI3_QUP_PHYS, + .end = MSM_GSBI3_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI3_PHYS, + .end = MSM_GSBI3_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI3_QUP_IRQ, + .end = GSBI3_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = 44, + .end = 44, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = 43, + .end = 43, + .flags = IORESOURCE_IO, + }, +}; + +static struct resource gsbi4_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI4_QUP_PHYS, + .end = MSM_GSBI4_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI4_PHYS, + .end = MSM_GSBI4_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI4_QUP_IRQ, + .end = GSBI4_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource gsbi7_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI7_QUP_PHYS, + .end = MSM_GSBI7_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI7_PHYS, + .end = MSM_GSBI7_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI7_QUP_IRQ, + .end = GSBI7_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "i2c_clk", + .start = 60, + .end = 60, + .flags = IORESOURCE_IO, + }, + { + .name = "i2c_sda", + .start = 59, + .end = 59, + .flags = IORESOURCE_IO, + }, +}; + +static struct resource gsbi8_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI8_QUP_PHYS, + .end = MSM_GSBI8_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI8_PHYS, + .end = MSM_GSBI8_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI8_QUP_IRQ, + .end = GSBI8_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource gsbi9_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI9_QUP_PHYS, + .end = MSM_GSBI9_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI9_PHYS, + .end = MSM_GSBI9_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI9_QUP_IRQ, + .end = GSBI9_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource gsbi12_qup_i2c_resources[] = { + { + .name = "qup_phys_addr", + .start = MSM_GSBI12_QUP_PHYS, + .end = MSM_GSBI12_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI12_PHYS, + .end = MSM_GSBI12_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = GSBI12_QUP_IRQ, + .end = GSBI12_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors grp3d_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp3d_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(990), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_low_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(1300), + }, +}; + +static struct msm_bus_vectors grp3d_nominal_high_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2008), + }, +}; + +static struct msm_bus_vectors grp3d_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_3D, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(2484), + }, +}; + +static struct msm_bus_paths grp3d_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp3d_init_vectors), + grp3d_init_vectors, + }, + { + ARRAY_SIZE(grp3d_low_vectors), + grp3d_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_low_vectors), + grp3d_nominal_low_vectors, + }, + { + ARRAY_SIZE(grp3d_nominal_high_vectors), + grp3d_nominal_high_vectors, + }, + { + ARRAY_SIZE(grp3d_max_vectors), + grp3d_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata grp3d_bus_scale_pdata = { + grp3d_bus_scale_usecases, + ARRAY_SIZE(grp3d_bus_scale_usecases), + .name = "grp3d", +}; + +static struct msm_bus_vectors grp2d0_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp2d0_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(990), + }, +}; + +static struct msm_bus_paths grp2d0_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp2d0_init_vectors), + grp2d0_init_vectors, + }, + { + ARRAY_SIZE(grp2d0_max_vectors), + grp2d0_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata grp2d0_bus_scale_pdata = { + grp2d0_bus_scale_usecases, + ARRAY_SIZE(grp2d0_bus_scale_usecases), + .name = "grp2d0", +}; + +static struct msm_bus_vectors grp2d1_init_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors grp2d1_max_vectors[] = { + { + .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = KGSL_CONVERT_TO_MBPS(990), + }, +}; + +static struct msm_bus_paths grp2d1_bus_scale_usecases[] = { + { + ARRAY_SIZE(grp2d1_init_vectors), + grp2d1_init_vectors, + }, + { + ARRAY_SIZE(grp2d1_max_vectors), + grp2d1_max_vectors, + }, +}; + +static struct msm_bus_scale_pdata grp2d1_bus_scale_pdata = { + grp2d1_bus_scale_usecases, + ARRAY_SIZE(grp2d1_bus_scale_usecases), + .name = "grp2d1", +}; +#endif + +#ifdef CONFIG_HW_RANDOM_MSM +static struct resource rng_resources = { + .flags = IORESOURCE_MEM, + .start = MSM_PRNG_PHYS, + .end = MSM_PRNG_PHYS + SZ_512 - 1, +}; + +struct platform_device msm_device_rng = { + .name = "msm_rng", + .id = 0, + .num_resources = 1, + .resource = &rng_resources, +}; +#endif + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0x04300000, /* GFX3D address */ + .end = 0x0431ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = GFX3D_IRQ, + .end = GFX3D_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 266667000, + .bus_freq = 4, + .io_fraction = 0, + }, + { + .gpu_freq = 228571000, + .bus_freq = 3, + .io_fraction = 33, + }, + { + .gpu_freq = 200000000, + .bus_freq = 2, + .io_fraction = 100, + }, + { + .gpu_freq = 177778000, + .bus_freq = 1, + .io_fraction = 100, + }, + { + .gpu_freq = 27000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = 5, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp3d_bus_scale_pdata, +#endif +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; + +static struct resource kgsl_2d0_resources[] = { + { + .name = KGSL_2D0_REG_MEMORY, + .start = 0x04100000, /* Z180 base address */ + .end = 0x04100FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_2D0_IRQ, + .start = GFX2D0_IRQ, + .end = GFX2D0_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_2d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 200000000, + .bus_freq = 1, + }, + { + .gpu_freq = 200000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = 2, + .set_grp_async = NULL, + .idle_timeout = HZ/10, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp2d0_bus_scale_pdata, +#endif +}; + +struct platform_device msm_kgsl_2d0 = { + .name = "kgsl-2d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_2d0_resources), + .resource = kgsl_2d0_resources, + .dev = { + .platform_data = &kgsl_2d0_pdata, + }, +}; + +static struct resource kgsl_2d1_resources[] = { + { + .name = KGSL_2D1_REG_MEMORY, + .start = 0x04200000, /* Z180 device 1 base address */ + .end = 0x04200FFF, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_2D1_IRQ, + .start = GFX2D1_IRQ, + .end = GFX2D1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_2d1_pdata = { + .pwrlevel = { + { + .gpu_freq = 200000000, + .bus_freq = 1, + }, + { + .gpu_freq = 200000000, + .bus_freq = 0, + }, + }, + .init_level = 0, + .num_levels = 2, + .set_grp_async = NULL, + .idle_timeout = HZ/10, + .nap_allowed = true, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &grp2d1_bus_scale_pdata, +#endif +}; + +struct platform_device msm_kgsl_2d1 = { + .name = "kgsl-2d1", + .id = 1, + .num_resources = ARRAY_SIZE(kgsl_2d1_resources), + .resource = kgsl_2d1_resources, + .dev = { + .platform_data = &kgsl_2d1_pdata, + }, +}; + +/* + * this a software workaround for not having two distinct board + * files for 8660v1 and 8660v2. 8660v1 has a faulty 2d clock, and + * this workaround detects the cpu version to tell if the kernel is on a + * 8660v1, and should disable the 2d core. it is called from the board file + */ +void __init msm8x60_check_2d_hardware(void) +{ + if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) && + (SOCINFO_VERSION_MINOR(socinfo_get_version()) == 0)) { + printk(KERN_WARNING "kgsl: 2D cores disabled on 8660v1\n"); + kgsl_2d0_pdata.clk_map = 0; + } +} + +/* Use GSBI3 QUP for /dev/i2c-0 */ +struct platform_device msm_gsbi3_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI3_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi3_qup_i2c_resources), + .resource = gsbi3_qup_i2c_resources, +}; + +/* Use GSBI4 QUP for /dev/i2c-1 */ +struct platform_device msm_gsbi4_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI4_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi4_qup_i2c_resources), + .resource = gsbi4_qup_i2c_resources, +}; + +/* Use GSBI8 QUP for /dev/i2c-3 */ +struct platform_device msm_gsbi8_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI8_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi8_qup_i2c_resources), + .resource = gsbi8_qup_i2c_resources, +}; + +/* Use GSBI9 QUP for /dev/i2c-2 */ +struct platform_device msm_gsbi9_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI9_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi9_qup_i2c_resources), + .resource = gsbi9_qup_i2c_resources, +}; + +/* Use GSBI7 QUP for /dev/i2c-4 (Marimba) */ +struct platform_device msm_gsbi7_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI7_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi7_qup_i2c_resources), + .resource = gsbi7_qup_i2c_resources, +}; + +/* Use GSBI12 QUP for /dev/i2c-5 (Sensors) */ +struct platform_device msm_gsbi12_qup_i2c_device = { + .name = "qup_i2c", + .id = MSM_GSBI12_QUP_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(gsbi12_qup_i2c_resources), + .resource = gsbi12_qup_i2c_resources, +}; + +#ifdef CONFIG_MSM_SSBI +#define MSM_SSBI_PMIC1_PHYS 0x00500000 +static struct resource resources_ssbi_pmic1_resource[] = { + { + .start = MSM_SSBI_PMIC1_PHYS, + .end = MSM_SSBI_PMIC1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi_pmic1 = { + .name = "msm_ssbi", + .id = 0, + .resource = resources_ssbi_pmic1_resource, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic1_resource), +}; + +#define MSM_SSBI2_PMIC2B_PHYS 0x00C00000 +static struct resource resources_ssbi_pmic2_resource[] = { + { + .start = MSM_SSBI2_PMIC2B_PHYS, + .end = MSM_SSBI2_PMIC2B_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi_pmic2 = { + .name = "msm_ssbi", + .id = 1, + .resource = resources_ssbi_pmic2_resource, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic2_resource), +}; +#endif + +#ifdef CONFIG_I2C_SSBI +/* CODEC SSBI on /dev/i2c-8 */ +#define MSM_SSBI3_PHYS 0x18700000 +static struct resource msm_ssbi3_resources[] = { + { + .name = "ssbi_base", + .start = MSM_SSBI3_PHYS, + .end = MSM_SSBI3_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi3 = { + .name = "i2c_ssbi", + .id = MSM_SSBI3_I2C_BUS_ID, + .num_resources = ARRAY_SIZE(msm_ssbi3_resources), + .resource = msm_ssbi3_resources, +}; +#endif /* CONFIG_I2C_SSBI */ + +static struct resource gsbi1_qup_spi_resources[] = { + { + .name = "spi_base", + .start = MSM_GSBI1_QUP_PHYS, + .end = MSM_GSBI1_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_base", + .start = MSM_GSBI1_PHYS, + .end = MSM_GSBI1_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spi_irq_in", + .start = GSBI1_QUP_IRQ, + .end = GSBI1_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spidm_channels", + .start = 5, + .end = 6, + .flags = IORESOURCE_DMA, + }, + { + .name = "spidm_crci", + .start = 8, + .end = 7, + .flags = IORESOURCE_DMA, + }, + { + .name = "spi_clk", + .start = 36, + .end = 36, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_miso", + .start = 34, + .end = 34, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_mosi", + .start = 33, + .end = 33, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_cs", + .start = 35, + .end = 35, + .flags = IORESOURCE_IO, + }, +}; + +/* Use GSBI1 QUP for SPI-0 */ +struct platform_device msm_gsbi1_qup_spi_device = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(gsbi1_qup_spi_resources), + .resource = gsbi1_qup_spi_resources, +}; + + +static struct resource gsbi10_qup_spi_resources[] = { + { + .name = "spi_base", + .start = MSM_GSBI10_QUP_PHYS, + .end = MSM_GSBI10_QUP_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_base", + .start = MSM_GSBI10_PHYS, + .end = MSM_GSBI10_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spi_irq_in", + .start = GSBI10_QUP_IRQ, + .end = GSBI10_QUP_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_clk", + .start = 73, + .end = 73, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_cs", + .start = 72, + .end = 72, + .flags = IORESOURCE_IO, + }, + { + .name = "spi_mosi", + .start = 70, + .end = 70, + .flags = IORESOURCE_IO, + }, +}; + +/* Use GSBI10 QUP for SPI-1 */ +struct platform_device msm_gsbi10_qup_spi_device = { + .name = "spi_qsd", + .id = 1, + .num_resources = ARRAY_SIZE(gsbi10_qup_spi_resources), + .resource = gsbi10_qup_spi_resources, +}; +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC1_DML_BASE (MSM_SDC1_BASE + 0x800) +#define MSM_SDC1_BAM_BASE (MSM_SDC1_BASE + 0x2000) +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC2_DML_BASE (MSM_SDC2_BASE + 0x800) +#define MSM_SDC2_BAM_BASE (MSM_SDC2_BASE + 0x2000) +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC3_DML_BASE (MSM_SDC3_BASE + 0x800) +#define MSM_SDC3_BAM_BASE (MSM_SDC3_BASE + 0x2000) +#define MSM_SDC4_BASE 0x121C0000 +#define MSM_SDC4_DML_BASE (MSM_SDC4_BASE + 0x800) +#define MSM_SDC4_BAM_BASE (MSM_SDC4_BASE + 0x2000) +#define MSM_SDC5_BASE 0x12200000 +#define MSM_SDC5_DML_BASE (MSM_SDC5_BASE + 0x800) +#define MSM_SDC5_BAM_BASE (MSM_SDC5_BASE + 0x2000) + +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_DML_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = SDC1_IRQ_0, + .end = SDC1_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC1_DML_BASE, + .end = MSM_SDC1_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC1_BAM_BASE, + .end = MSM_SDC1_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC1_BAM_IRQ, + .end = SDC1_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#else + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } +#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */ +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_DML_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = SDC2_IRQ_0, + .end = SDC2_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC2_DML_BASE, + .end = MSM_SDC2_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC2_BAM_BASE, + .end = MSM_SDC2_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC2_BAM_IRQ, + .end = SDC2_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#else + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } +#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */ +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_DML_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = SDC3_IRQ_0, + .end = SDC3_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC3_DML_BASE, + .end = MSM_SDC3_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC3_BAM_BASE, + .end = MSM_SDC3_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC3_BAM_IRQ, + .end = SDC3_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#else + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, + .flags = IORESOURCE_DMA, + }, +#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */ +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_DML_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = SDC4_IRQ_0, + .end = SDC4_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC4_DML_BASE, + .end = MSM_SDC4_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC4_BAM_BASE, + .end = MSM_SDC4_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC4_BAM_IRQ, + .end = SDC4_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#else + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, + .flags = IORESOURCE_DMA, + }, +#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */ +}; + +static struct resource resources_sdc5[] = { + { + .start = MSM_SDC5_BASE, + .end = MSM_SDC5_DML_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = SDC5_IRQ_0, + .end = SDC5_IRQ_0, + .flags = IORESOURCE_IRQ, + }, +#ifdef CONFIG_MMC_MSM_SPS_SUPPORT + { + .name = "sdcc_dml_addr", + .start = MSM_SDC5_DML_BASE, + .end = MSM_SDC5_BAM_BASE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_addr", + .start = MSM_SDC5_BAM_BASE, + .end = MSM_SDC5_BAM_BASE + (2 * SZ_4K) - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "sdcc_bam_irq", + .start = SDC5_BAM_IRQ, + .end = SDC5_BAM_IRQ, + .flags = IORESOURCE_IRQ, + }, +#else + { + .name = "sdcc_dma_chnl", + .start = DMOV_SDC5_CHAN, + .end = DMOV_SDC5_CHAN, + .flags = IORESOURCE_DMA, + }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC5_CRCI, + .end = DMOV_SDC5_CRCI, + .flags = IORESOURCE_DMA, + }, +#endif /* CONFIG_MMC_MSM_SPS_SUPPORT */ +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc5 = { + .name = "msm_sdcc", + .id = 5, + .num_resources = ARRAY_SIZE(resources_sdc5), + .resource = resources_sdc5, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, + &msm_device_sdc5, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 5) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#ifdef CONFIG_MSM_CAMERA_V4L2 +static struct resource msm_csic0_resources[] = { + { + .name = "csic", + .start = 0x04800000, + .end = 0x04800000 + 0x00000400 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = CSI_0_IRQ, + .end = CSI_0_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_csic1_resources[] = { + { + .name = "csic", + .start = 0x04900000, + .end = 0x04900000 + 0x00000400 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "csic", + .start = CSI_1_IRQ, + .end = CSI_1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct resource msm_vfe_resources[] = { + { + .name = "msm_vfe", + .start = 0x04500000, + .end = 0x04500000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "msm_vfe", + .start = VFE_IRQ, + .end = VFE_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msm_vpe_resources[] = { + { + .name = "vpe", + .start = 0x05300000, + .end = 0x05300000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "vpe", + .start = INT_VPE, + .end = INT_VPE, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_csic0 = { + .name = "msm_csic", + .id = 0, + .resource = msm_csic0_resources, + .num_resources = ARRAY_SIZE(msm_csic0_resources), +}; + +struct platform_device msm_device_csic1 = { + .name = "msm_csic", + .id = 1, + .resource = msm_csic1_resources, + .num_resources = ARRAY_SIZE(msm_csic1_resources), +}; + +struct platform_device msm_device_vfe = { + .name = "msm_vfe", + .id = 0, + .resource = msm_vfe_resources, + .num_resources = ARRAY_SIZE(msm_vfe_resources), +}; + +struct platform_device msm_device_vpe = { + .name = "msm_vpe", + .id = 0, + .resource = msm_vpe_resources, + .num_resources = ARRAY_SIZE(msm_vpe_resources), +}; + +#endif + + +#define MIPI_DSI_HW_BASE 0x04700000 +#define ROTATOR_HW_BASE 0x04E00000 +#define TVENC_HW_BASE 0x04F00000 +#define MDP_HW_BASE 0x05100000 + +static struct resource msm_mipi_dsi_resources[] = { + { + .name = "mipi_dsi", + .start = MIPI_DSI_HW_BASE, + .end = MIPI_DSI_HW_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = DSI_IRQ, + .end = DSI_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_mipi_dsi_device = { + .name = "mipi_dsi", + .id = 1, + .num_resources = ARRAY_SIZE(msm_mipi_dsi_resources), + .resource = msm_mipi_dsi_resources, +}; + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_HW_BASE, + .end = MDP_HW_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; +#ifdef CONFIG_MSM_ROTATOR +static struct resource resources_msm_rotator[] = { + { + .start = 0x04E00000, + .end = 0x04F00000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = ROT_IRQ, + .end = ROT_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_rot_clocks rotator_clocks[] = { + { + .clk_name = "core_clk", + .clk_type = ROTATOR_CORE_CLK, + .clk_rate = 160 * 1000 * 1000, + }, + { + .clk_name = "iface_clk", + .clk_type = ROTATOR_PCLK, + .clk_rate = 0, + }, +}; + +static struct msm_rotator_platform_data rotator_pdata = { + .number_of_clocks = ARRAY_SIZE(rotator_clocks), + .hardware_version_number = 0x01010307, + .rotator_clks = rotator_clocks, +#ifdef CONFIG_MSM_BUS_SCALING + .bus_scale_table = &rotator_bus_scale_pdata, +#endif + +}; + +struct platform_device msm_rotator_device = { + .name = "msm_rotator", + .id = 0, + .num_resources = ARRAY_SIZE(resources_msm_rotator), + .resource = resources_msm_rotator, + .dev = { + .platform_data = &rotator_pdata, + }, +}; +#endif + + +/* Sensors DSPS platform data */ +#ifdef CONFIG_MSM_DSPS + +#define PPSS_DSPS_TCM_CODE_BASE 0x12000000 +#define PPSS_DSPS_TCM_CODE_SIZE 0x28000 +#define PPSS_DSPS_TCM_BUF_BASE 0x12040000 +#define PPSS_DSPS_TCM_BUF_SIZE 0x4000 +#define PPSS_DSPS_PIPE_BASE 0x12800000 +#define PPSS_DSPS_PIPE_SIZE 0x0 /* 8660 V2 does not use PIPE memory */ +#define PPSS_DSPS_DDR_BASE 0x8fe00000 +#define PPSS_DSPS_DDR_SIZE 0x0 /* 8660 V2 does not use DDR memory */ +#define PPSS_SMEM_BASE 0x40000000 +#define PPSS_SMEM_SIZE 0x4000 +#define PPSS_REG_PHYS_BASE 0x12080000 + +#define MHZ (1000*1000) + +#define TCSR_GSBI_IRQ_MUX_SEL 0x0044 + +#define GSBI_IRQ_MUX_SEL_MASK 0xF +#define GSBI_IRQ_MUX_SEL_DSPS 0xB + +static void dsps_init1(struct msm_dsps_platform_data *data) +{ + int val; + + /* route GSBI12 interrutps to DSPS */ + val = secure_readl(MSM_TCSR_BASE + TCSR_GSBI_IRQ_MUX_SEL); + val &= ~GSBI_IRQ_MUX_SEL_MASK; + val |= GSBI_IRQ_MUX_SEL_DSPS; + secure_writel(val, MSM_TCSR_BASE + TCSR_GSBI_IRQ_MUX_SEL); +} + +static struct dsps_clk_info dsps_clks[] = { + { + .name = "iface_clk", + .rate = 0, /* no rate just on/off */ + }, + { + .name = "mem_clk", + .rate = 0, /* no rate just on/off */ + }, + { + .name = "gsbi_qup_clk", + .rate = 24 * MHZ, /* See clk_tbl_gsbi_qup[] */ + }, + { + .name = "dfab_dsps_clk", + .rate = 64 * MHZ, /* Same rate as USB. */ + } +}; + +static struct dsps_regulator_info dsps_regs[] = { + { + .name = "8058_l5", + .volt = 2850000, /* in uV */ + }, + { + .name = "8058_s3", + .volt = 1800000, /* in uV */ + } +}; + +/* + * Note: GPIOs field is intialized in run-time at the function + * msm8x60_init_dsps(). + */ + +struct msm_dsps_platform_data msm_dsps_pdata = { + .clks = dsps_clks, + .clks_num = ARRAY_SIZE(dsps_clks), + .gpios = NULL, + .gpios_num = 0, + .regs = dsps_regs, + .regs_num = ARRAY_SIZE(dsps_regs), + .init = dsps_init1, + .tcm_code_start = PPSS_DSPS_TCM_CODE_BASE, + .tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE, + .tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE, + .tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE, + .pipe_start = PPSS_DSPS_PIPE_BASE, + .pipe_size = PPSS_DSPS_PIPE_SIZE, + .ddr_start = PPSS_DSPS_DDR_BASE, + .ddr_size = PPSS_DSPS_DDR_SIZE, + .smem_start = PPSS_SMEM_BASE, + .smem_size = PPSS_SMEM_SIZE, + .signature = DSPS_SIGNATURE, +}; + +static struct resource msm_dsps_resources[] = { + { + .start = PPSS_REG_PHYS_BASE, + .end = PPSS_REG_PHYS_BASE + SZ_8K - 1, + .name = "ppss_reg", + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_dsps_device = { + .name = "msm_dsps", + .id = 0, + .num_resources = ARRAY_SIZE(msm_dsps_resources), + .resource = msm_dsps_resources, + .dev.platform_data = &msm_dsps_pdata, +}; + +#endif /* CONFIG_MSM_DSPS */ + +#ifdef CONFIG_FB_MSM_TVOUT +static struct resource msm_tvenc_resources[] = { + { + .name = "tvenc", + .start = TVENC_HW_BASE, + .end = TVENC_HW_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource tvout_device_resources[] = { + { + .name = "tvout_device_irq", + .start = TV_ENC_IRQ, + .end = TV_ENC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; +#endif +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +#ifdef CONFIG_FB_MSM_TVOUT +static struct platform_device msm_tvenc_device = { + .name = "tvenc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, +}; + +static struct platform_device msm_tvout_device = { + .name = "tvout_device", + .id = 0, + .num_resources = ARRAY_SIZE(tvout_device_resources), + .resource = tvout_device_resources, +}; +#endif + +#ifdef CONFIG_MSM_BUS_SCALING +static struct platform_device msm_dtv_device = { + .name = "dtv", + .id = 0, +}; +#endif + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "lcdc", 4)) + msm_register_device(&msm_lcdc_device, data); + else if (!strncmp(name, "mipi_dsi", 8)) + msm_register_device(&msm_mipi_dsi_device, data); +#ifdef CONFIG_FB_MSM_TVOUT + else if (!strncmp(name, "tvenc", 5)) + msm_register_device(&msm_tvenc_device, data); + else if (!strncmp(name, "tvout_device", 12)) + msm_register_device(&msm_tvout_device, data); +#endif +#ifdef CONFIG_MSM_BUS_SCALING + else if (!strncmp(name, "dtv", 3)) + msm_register_device(&msm_dtv_device, data); +#endif + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct resource resources_otg[] = { + { + .start = 0x12500000, + .end = 0x12500000 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_otg = { + .name = "msm_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_otg), + .resource = resources_otg, +}; + +static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; +#ifdef CONFIG_USB_EHCI_MSM_72K +static struct resource resources_hsusb_host[] = { + { + .start = 0x12500000, + .end = 0x12500000 + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = USB1_HS_IRQ, + .end = USB1_HS_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, +}; + +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} +#endif + +#define MSM_TSIF0_PHYS (0x18200000) +#define MSM_TSIF1_PHYS (0x18201000) +#define MSM_TSIF_SIZE (0x200) +#define TCSR_ADM_0_A_CRCI_MUX_SEL 0x0070 + +#define TSIF_0_CLK GPIO_CFG(93, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_EN GPIO_CFG(94, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_DATA GPIO_CFG(95, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_0_SYNC GPIO_CFG(96, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_CLK GPIO_CFG(97, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_EN GPIO_CFG(98, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_DATA GPIO_CFG(99, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) +#define TSIF_1_SYNC GPIO_CFG(100, 1, GPIO_CFG_INPUT, \ + GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA) + +static const struct msm_gpio tsif0_gpios[] = { + { .gpio_cfg = TSIF_0_CLK, .label = "tsif_clk", }, + { .gpio_cfg = TSIF_0_EN, .label = "tsif_en", }, + { .gpio_cfg = TSIF_0_DATA, .label = "tsif_data", }, + { .gpio_cfg = TSIF_0_SYNC, .label = "tsif_sync", }, +}; + +static const struct msm_gpio tsif1_gpios[] = { + { .gpio_cfg = TSIF_1_CLK, .label = "tsif_clk", }, + { .gpio_cfg = TSIF_1_EN, .label = "tsif_en", }, + { .gpio_cfg = TSIF_1_DATA, .label = "tsif_data", }, + { .gpio_cfg = TSIF_1_SYNC, .label = "tsif_sync", }, +}; + +static void tsif_release(struct device *dev) +{ +} + +static void tsif_init1(struct msm_tsif_platform_data *data) +{ + int val; + + /* configure mux to use correct tsif instance */ + val = secure_readl(MSM_TCSR_BASE + TCSR_ADM_0_A_CRCI_MUX_SEL); + val |= 0x80000000; + secure_writel(val, MSM_TCSR_BASE + TCSR_ADM_0_A_CRCI_MUX_SEL); +} + +struct msm_tsif_platform_data tsif1_platform_data = { + .num_gpios = ARRAY_SIZE(tsif1_gpios), + .gpios = tsif1_gpios, + .tsif_pclk = "iface_clk", + .tsif_ref_clk = "ref_clk", + .init = tsif_init1 +}; + +struct resource tsif1_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = TSIF2_IRQ, + .end = TSIF2_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF1_PHYS, + .end = MSM_TSIF1_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +static void tsif_init0(struct msm_tsif_platform_data *data) +{ + int val; + + /* configure mux to use correct tsif instance */ + val = secure_readl(MSM_TCSR_BASE + TCSR_ADM_0_A_CRCI_MUX_SEL); + val &= 0x7FFFFFFF; + secure_writel(val, MSM_TCSR_BASE + TCSR_ADM_0_A_CRCI_MUX_SEL); +} + +struct msm_tsif_platform_data tsif0_platform_data = { + .num_gpios = ARRAY_SIZE(tsif0_gpios), + .gpios = tsif0_gpios, + .tsif_pclk = "iface_clk", + .tsif_ref_clk = "ref_clk", + .init = tsif_init0 +}; +struct resource tsif0_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = TSIF1_IRQ, + .end = TSIF1_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF0_PHYS, + .end = MSM_TSIF0_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +struct platform_device msm_device_tsif[2] = { + { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif0_resources), + .resource = tsif0_resources, + .dev = { + .release = tsif_release, + .platform_data = &tsif0_platform_data + }, + }, + { + .name = "msm_tsif", + .id = 1, + .num_resources = ARRAY_SIZE(tsif1_resources), + .resource = tsif1_resources, + .dev = { + .release = tsif_release, + .platform_data = &tsif1_platform_data + }, + } +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct msm_watchdog_pdata msm_watchdog_pdata = { + .pet_time = 10000, + .bark_time = 11000, + .has_secure = true, +}; + +struct platform_device msm8660_device_watchdog = { + .name = "msm_watchdog", + .id = -1, + .dev = { + .platform_data = &msm_watchdog_pdata, + }, +}; + +static struct resource msm_dmov_resource_adm0[] = { + { + .start = INT_ADM0_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x18320000, + .end = 0x18320000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_dmov_resource_adm1[] = { + { + .start = INT_ADM1_AARM, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x18420000, + .end = 0x18420000 + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_dmov_pdata msm_dmov_pdata_adm0 = { + .sd = 1, + .sd_size = 0x800, +}; + +static struct msm_dmov_pdata msm_dmov_pdata_adm1 = { + .sd = 1, + .sd_size = 0x800, +}; + +struct platform_device msm_device_dmov_adm0 = { + .name = "msm_dmov", + .id = 0, + .resource = msm_dmov_resource_adm0, + .num_resources = ARRAY_SIZE(msm_dmov_resource_adm0), + .dev = { + .platform_data = &msm_dmov_pdata_adm0, + }, +}; + +struct platform_device msm_device_dmov_adm1 = { + .name = "msm_dmov", + .id = 1, + .resource = msm_dmov_resource_adm1, + .num_resources = ARRAY_SIZE(msm_dmov_resource_adm1), + .dev = { + .platform_data = &msm_dmov_pdata_adm1, + }, +}; + +/* MSM Video core device */ +#ifdef CONFIG_MSM_BUS_SCALING +static struct msm_bus_vectors vidc_init_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 0, + .ib = 0, + }, +}; +static struct msm_bus_vectors vidc_venc_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 54525952, + .ib = 436207616, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 72351744, + .ib = 289406976, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 1000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 500000, + .ib = 1000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_vga_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 40894464, + .ib = 327155712, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 48234496, + .ib = 192937984, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 500000, + .ib = 2000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 500000, + .ib = 2000000, + }, +}; +static struct msm_bus_vectors vidc_venc_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 163577856, + .ib = 1308622848, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 219152384, + .ib = 876609536, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 3500000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 1750000, + .ib = 3500000, + }, +}; +static struct msm_bus_vectors vidc_vdec_720p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 121634816, + .ib = 973078528, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 155189248, + .ib = 620756992, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1750000, + .ib = 7000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 1750000, + .ib = 7000000, + }, +}; +static struct msm_bus_vectors vidc_venc_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 372244480, + .ib = 1861222400, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 501219328, + .ib = 2004877312, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 5000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 2500000, + .ib = 5000000, + }, +}; +static struct msm_bus_vectors vidc_vdec_1080p_vectors[] = { + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 222298112, + .ib = 1778384896, + }, + { + .src = MSM_BUS_MASTER_HD_CODEC_PORT1, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 330301440, + .ib = 1321205760, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 2500000, + .ib = 700000000, + }, + { + .src = MSM_BUS_MASTER_AMPSS_M0, + .dst = MSM_BUS_SLAVE_SMI, + .ab = 2500000, + .ib = 10000000, + }, +}; + +static struct msm_bus_paths vidc_bus_client_config[] = { + { + ARRAY_SIZE(vidc_init_vectors), + vidc_init_vectors, + }, + { + ARRAY_SIZE(vidc_venc_vga_vectors), + vidc_venc_vga_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_vga_vectors), + vidc_vdec_vga_vectors, + }, + { + ARRAY_SIZE(vidc_venc_720p_vectors), + vidc_venc_720p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_720p_vectors), + vidc_vdec_720p_vectors, + }, + { + ARRAY_SIZE(vidc_venc_1080p_vectors), + vidc_venc_1080p_vectors, + }, + { + ARRAY_SIZE(vidc_vdec_1080p_vectors), + vidc_vdec_1080p_vectors, + }, +}; + +static struct msm_bus_scale_pdata vidc_bus_client_data = { + vidc_bus_client_config, + ARRAY_SIZE(vidc_bus_client_config), + .name = "vidc", +}; + +#endif + +#define MSM_VIDC_BASE_PHYS 0x04400000 +#define MSM_VIDC_BASE_SIZE 0x00100000 + +static struct resource msm_device_vidc_resources[] = { + { + .start = MSM_VIDC_BASE_PHYS, + .end = MSM_VIDC_BASE_PHYS + MSM_VIDC_BASE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = VCODEC_IRQ, + .end = VCODEC_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +struct msm_vidc_platform_data vidc_platform_data = { +#ifdef CONFIG_MSM_BUS_SCALING + .vidc_bus_client_pdata = &vidc_bus_client_data, +#endif +#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION + .memtype = ION_CP_MM_HEAP_ID, + .enable_ion = 1, + .cp_enabled = 0, +#else + .memtype = MEMTYPE_SMI_KERNEL, + .enable_ion = 0, +#endif + .disable_dmx = 0, + .disable_fullhd = 0, + .cont_mode_dpb_count = 8, + .disable_turbo = 1, +}; + +struct platform_device msm_device_vidc = { + .name = "msm_vidc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_device_vidc_resources), + .resource = msm_device_vidc_resources, + .dev = { + .platform_data = &vidc_platform_data, + }, +}; + +#if defined(CONFIG_MSM_RPM_LOG) || defined(CONFIG_MSM_RPM_LOG_MODULE) +static struct msm_rpm_log_platform_data msm_rpm_log_pdata = { + .phys_addr_base = 0x00106000, + .reg_offsets = { + [MSM_RPM_LOG_PAGE_INDICES] = 0x00000C80, + [MSM_RPM_LOG_PAGE_BUFFER] = 0x00000CA0, + }, + .phys_size = SZ_8K, + .log_len = 4096, /* log's buffer length in bytes */ + .log_len_mask = (4096 >> 2) - 1, /* length mask in units of u32 */ +}; + +struct platform_device msm8660_rpm_log_device = { + .name = "msm_rpm_log", + .id = -1, + .dev = { + .platform_data = &msm_rpm_log_pdata, + }, +}; +#endif + +#if defined(CONFIG_MSM_RPM_STATS_LOG) +static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = { + .phys_addr_base = 0x00107E04, + .phys_size = SZ_8K, +}; + +struct platform_device msm8660_rpm_stat_device = { + .name = "msm_rpm_stat", + .id = -1, + .dev = { + .platform_data = &msm_rpm_stat_pdata, + }, +}; +#endif + +#define SHARED_IMEM_TZ_BASE 0x2a05f720 +static struct resource tzlog_resources[] = { + { + .start = SHARED_IMEM_TZ_BASE, + .end = SHARED_IMEM_TZ_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_tz_log = { + .name = "tz_log", + .id = 0, + .num_resources = ARRAY_SIZE(tzlog_resources), + .resource = tzlog_resources, +}; + +#ifdef CONFIG_MSM_MPM +static uint16_t msm_mpm_irqs_m2a[MSM_MPM_NR_MPM_IRQS] __initdata = { + [1] = MSM_GPIO_TO_INT(61), + [4] = MSM_GPIO_TO_INT(87), + [5] = MSM_GPIO_TO_INT(88), + [6] = MSM_GPIO_TO_INT(89), + [7] = MSM_GPIO_TO_INT(90), + [8] = MSM_GPIO_TO_INT(91), + [9] = MSM_GPIO_TO_INT(34), + [10] = MSM_GPIO_TO_INT(38), + [11] = MSM_GPIO_TO_INT(42), + [12] = MSM_GPIO_TO_INT(46), + [13] = MSM_GPIO_TO_INT(50), + [14] = MSM_GPIO_TO_INT(54), + [15] = MSM_GPIO_TO_INT(58), + [16] = MSM_GPIO_TO_INT(63), + [17] = MSM_GPIO_TO_INT(160), + [18] = MSM_GPIO_TO_INT(162), + [19] = MSM_GPIO_TO_INT(144), + [20] = MSM_GPIO_TO_INT(146), + [25] = USB1_HS_IRQ, + [26] = TV_ENC_IRQ, + [27] = HDMI_IRQ, + [29] = MSM_GPIO_TO_INT(123), + [30] = MSM_GPIO_TO_INT(172), + [31] = MSM_GPIO_TO_INT(99), + [32] = MSM_GPIO_TO_INT(96), + [33] = MSM_GPIO_TO_INT(67), + [34] = MSM_GPIO_TO_INT(71), + [35] = MSM_GPIO_TO_INT(105), + [36] = MSM_GPIO_TO_INT(117), + [37] = MSM_GPIO_TO_INT(29), + [38] = MSM_GPIO_TO_INT(30), + [39] = MSM_GPIO_TO_INT(31), + [40] = MSM_GPIO_TO_INT(37), + [41] = MSM_GPIO_TO_INT(40), + [42] = MSM_GPIO_TO_INT(41), + [43] = MSM_GPIO_TO_INT(45), + [44] = MSM_GPIO_TO_INT(51), + [45] = MSM_GPIO_TO_INT(52), + [46] = MSM_GPIO_TO_INT(57), + [47] = MSM_GPIO_TO_INT(73), + [48] = MSM_GPIO_TO_INT(93), + [49] = MSM_GPIO_TO_INT(94), + [50] = MSM_GPIO_TO_INT(103), + [51] = MSM_GPIO_TO_INT(104), + [52] = MSM_GPIO_TO_INT(106), + [53] = MSM_GPIO_TO_INT(115), + [54] = MSM_GPIO_TO_INT(124), + [55] = MSM_GPIO_TO_INT(125), + [56] = MSM_GPIO_TO_INT(126), + [57] = MSM_GPIO_TO_INT(127), + [58] = MSM_GPIO_TO_INT(128), + [59] = MSM_GPIO_TO_INT(129), +}; + +static uint16_t msm_mpm_bypassed_apps_irqs[] __initdata = { + TLMM_MSM_SUMMARY_IRQ, + RPM_SCSS_CPU0_GP_HIGH_IRQ, + RPM_SCSS_CPU0_GP_MEDIUM_IRQ, + RPM_SCSS_CPU0_GP_LOW_IRQ, + RPM_SCSS_CPU0_WAKE_UP_IRQ, + RPM_SCSS_CPU1_GP_HIGH_IRQ, + RPM_SCSS_CPU1_GP_MEDIUM_IRQ, + RPM_SCSS_CPU1_GP_LOW_IRQ, + RPM_SCSS_CPU1_WAKE_UP_IRQ, + MARM_SCSS_GP_IRQ_0, + MARM_SCSS_GP_IRQ_1, + MARM_SCSS_GP_IRQ_2, + MARM_SCSS_GP_IRQ_3, + MARM_SCSS_GP_IRQ_4, + MARM_SCSS_GP_IRQ_5, + MARM_SCSS_GP_IRQ_6, + MARM_SCSS_GP_IRQ_7, + MARM_SCSS_GP_IRQ_8, + MARM_SCSS_GP_IRQ_9, + LPASS_SCSS_GP_LOW_IRQ, + LPASS_SCSS_GP_MEDIUM_IRQ, + LPASS_SCSS_GP_HIGH_IRQ, + SDC4_IRQ_0, + SPS_MTI_31, +}; + +struct msm_mpm_device_data msm8660_mpm_dev_data __initdata = { + .irqs_m2a = msm_mpm_irqs_m2a, + .irqs_m2a_size = ARRAY_SIZE(msm_mpm_irqs_m2a), + .bypassed_apps_irqs = msm_mpm_bypassed_apps_irqs, + .bypassed_apps_irqs_size = ARRAY_SIZE(msm_mpm_bypassed_apps_irqs), + .mpm_request_reg_base = MSM_RPM_BASE + 0x9d8, + .mpm_status_reg_base = MSM_RPM_BASE + 0xdf8, + .mpm_apps_ipc_reg = MSM_GCC_BASE + 0x008, + .mpm_apps_ipc_val = BIT(1), + .mpm_ipc_irq = RPM_SCSS_CPU0_GP_MEDIUM_IRQ, + +}; +#endif + + +#ifdef CONFIG_MSM_BUS_SCALING +struct platform_device msm_bus_sys_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM, +}; +struct platform_device msm_bus_apps_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_APPSS, +}; +struct platform_device msm_bus_mm_fabric = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_MMSS, +}; +struct platform_device msm_bus_sys_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_SYSTEM_FPB, +}; +struct platform_device msm_bus_cpss_fpb = { + .name = "msm_bus_fabric", + .id = MSM_BUS_FAB_CPSS_FPB, +}; +#endif + +#ifdef CONFIG_SND_SOC_MSM8660_APQ +struct platform_device msm_pcm = { + .name = "msm-pcm-dsp", + .id = -1, +}; + +struct platform_device msm_pcm_routing = { + .name = "msm-pcm-routing", + .id = -1, +}; + +struct platform_device msm_cpudai0 = { + .name = "msm-dai-q6", + .id = PRIMARY_I2S_RX, +}; + +struct platform_device msm_cpudai1 = { + .name = "msm-dai-q6", + .id = PRIMARY_I2S_TX, +}; + +struct platform_device msm_cpudai_hdmi_rx = { + .name = "msm-dai-q6", + .id = HDMI_RX, +}; + +struct platform_device msm_cpudai_bt_rx = { + .name = "msm-dai-q6", + .id = INT_BT_SCO_RX, +}; + +struct platform_device msm_cpudai_bt_tx = { + .name = "msm-dai-q6", + .id = INT_BT_SCO_TX, +}; + +struct platform_device msm_cpudai_fm_rx = { + .name = "msm-dai-q6", + .id = INT_FM_RX, +}; + +struct platform_device msm_cpudai_fm_tx = { + .name = "msm-dai-q6", + .id = INT_FM_TX, +}; + +struct platform_device msm_cpu_fe = { + .name = "msm-dai-fe", + .id = -1, +}; + +struct platform_device msm_stub_codec = { + .name = "msm-stub-codec", + .id = 1, +}; + +struct platform_device msm_voice = { + .name = "msm-pcm-voice", + .id = -1, +}; + +struct platform_device msm_voip = { + .name = "msm-voip-dsp", + .id = -1, +}; + +struct platform_device msm_lpa_pcm = { + .name = "msm-pcm-lpa", + .id = -1, +}; + +struct platform_device msm_pcm_hostless = { + .name = "msm-pcm-hostless", + .id = -1, +}; +#endif + +struct platform_device asoc_msm_pcm = { + .name = "msm-dsp-audio", + .id = 0, +}; + +struct platform_device asoc_msm_dai0 = { + .name = "msm-codec-dai", + .id = 0, +}; + +struct platform_device asoc_msm_dai1 = { + .name = "msm-cpu-dai", + .id = 0, +}; + +#if defined (CONFIG_MSM_8x60_VOIP) +struct platform_device asoc_msm_mvs = { + .name = "msm-mvs-audio", + .id = 0, +}; + +struct platform_device asoc_mvs_dai0 = { + .name = "mvs-codec-dai", + .id = 0, +}; + +struct platform_device asoc_mvs_dai1 = { + .name = "mvs-cpu-dai", + .id = 0, +}; +#endif + +static struct fs_driver_data gfx2d0_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, +}; + +static struct fs_driver_data gfx2d1_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, +}; + +static struct fs_driver_data gfx3d_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk", .reset_rate = 27000000 }, + { .name = "iface_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D, +}; + +static struct fs_driver_data ijpeg_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_JPEG_ENC, +}; + +static struct fs_driver_data mdp_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { .name = "vsync_clk" }, + { .name = "tv_src_clk" }, + { .name = "tv_clk" }, + { .name = "pixel_mdp_clk" }, + { .name = "pixel_lcdc_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_MDP_PORT0, + .bus_port1 = MSM_BUS_MASTER_MDP_PORT1, +}; + +static struct fs_driver_data rot_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_ROTATOR, +}; + +static struct fs_driver_data ved_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_HD_CODEC_PORT0, + .bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1, +}; + +static struct fs_driver_data vfe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VFE, +}; + +static struct fs_driver_data vpe_fs_data = { + .clks = (struct fs_clk_data[]){ + { .name = "core_clk" }, + { .name = "iface_clk" }, + { .name = "bus_clk" }, + { 0 } + }, + .bus_port0 = MSM_BUS_MASTER_VPE, +}; + +struct platform_device *msm8660_footswitch[] __initdata = { + FS_8X60(FS_IJPEG, "vdd", "msm_gemini.0", &ijpeg_fs_data), + FS_8X60(FS_MDP, "vdd", "mdp.0", &mdp_fs_data), + FS_8X60(FS_ROT, "vdd", "msm_rotator.0", &rot_fs_data), + FS_8X60(FS_VED, "vdd", "msm_vidc.0", &ved_fs_data), + FS_8X60(FS_VFE, "fs_vfe", NULL, &vfe_fs_data), + FS_8X60(FS_VPE, "fs_vpe", NULL, &vpe_fs_data), + FS_8X60(FS_GFX3D, "vdd", "kgsl-3d0.0", &gfx3d_fs_data), + FS_8X60(FS_GFX2D0, "vdd", "kgsl-2d0.0", &gfx2d0_fs_data), + FS_8X60(FS_GFX2D1, "vdd", "kgsl-2d1.1", &gfx2d1_fs_data), +}; +unsigned msm8660_num_footswitch __initdata = ARRAY_SIZE(msm8660_footswitch); + +struct msm_rpm_platform_data msm8660_rpm_data __initdata = { + .reg_base_addrs = { + [MSM_RPM_PAGE_STATUS] = MSM_RPM_BASE, + [MSM_RPM_PAGE_CTRL] = MSM_RPM_BASE + 0x400, + [MSM_RPM_PAGE_REQ] = MSM_RPM_BASE + 0x600, + [MSM_RPM_PAGE_ACK] = MSM_RPM_BASE + 0xa00, + }, + .irq_ack = RPM_SCSS_CPU0_GP_HIGH_IRQ, + .irq_err = RPM_SCSS_CPU0_GP_LOW_IRQ, + .irq_wakeup = RPM_SCSS_CPU0_WAKE_UP_IRQ, + .ipc_rpm_reg = MSM_GCC_BASE + 0x008, + .ipc_rpm_val = 4, + .target_id = { + MSM_RPM_MAP(8660, NOTIFICATION_CONFIGURED_0, NOTIFICATION, 8), + MSM_RPM_MAP(8660, NOTIFICATION_REGISTERED_0, NOTIFICATION, 8), + MSM_RPM_MAP(8660, INVALIDATE_0, INVALIDATE, 8), + MSM_RPM_MAP(8660, TRIGGER_TIMED_TO, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8660, TRIGGER_TIMED_SCLK_COUNT, TRIGGER_TIMED, 1), + MSM_RPM_MAP(8660, TRIGGER_SET_FROM, TRIGGER_SET, 1), + MSM_RPM_MAP(8660, TRIGGER_SET_TO, TRIGGER_SET, 1), + MSM_RPM_MAP(8660, TRIGGER_SET_TRIGGER, TRIGGER_SET, 1), + MSM_RPM_MAP(8660, TRIGGER_CLEAR_FROM, TRIGGER_CLEAR, 1), + MSM_RPM_MAP(8660, TRIGGER_CLEAR_TO, TRIGGER_CLEAR, 1), + MSM_RPM_MAP(8660, TRIGGER_CLEAR_TRIGGER, TRIGGER_CLEAR, 1), + + MSM_RPM_MAP(8660, CXO_CLK, CXO_CLK, 1), + MSM_RPM_MAP(8660, PXO_CLK, PXO_CLK, 1), + MSM_RPM_MAP(8660, PLL_4, PLL_4, 1), + MSM_RPM_MAP(8660, APPS_FABRIC_CLK, APPS_FABRIC_CLK, 1), + MSM_RPM_MAP(8660, SYSTEM_FABRIC_CLK, SYSTEM_FABRIC_CLK, 1), + MSM_RPM_MAP(8660, MM_FABRIC_CLK, MM_FABRIC_CLK, 1), + MSM_RPM_MAP(8660, DAYTONA_FABRIC_CLK, DAYTONA_FABRIC_CLK, 1), + MSM_RPM_MAP(8660, SFPB_CLK, SFPB_CLK, 1), + MSM_RPM_MAP(8660, CFPB_CLK, CFPB_CLK, 1), + MSM_RPM_MAP(8660, MMFPB_CLK, MMFPB_CLK, 1), + MSM_RPM_MAP(8660, SMI_CLK, SMI_CLK, 1), + MSM_RPM_MAP(8660, EBI1_CLK, EBI1_CLK, 1), + + MSM_RPM_MAP(8660, APPS_L2_CACHE_CTL, APPS_L2_CACHE_CTL, 1), + + MSM_RPM_MAP(8660, APPS_FABRIC_HALT_0, APPS_FABRIC_HALT, 2), + MSM_RPM_MAP(8660, APPS_FABRIC_CLOCK_MODE_0, + APPS_FABRIC_CLOCK_MODE, 3), + MSM_RPM_MAP(8660, APPS_FABRIC_ARB_0, APPS_FABRIC_ARB, 6), + + MSM_RPM_MAP(8660, SYSTEM_FABRIC_HALT_0, SYSTEM_FABRIC_HALT, 2), + MSM_RPM_MAP(8660, SYSTEM_FABRIC_CLOCK_MODE_0, + SYSTEM_FABRIC_CLOCK_MODE, 3), + MSM_RPM_MAP(8660, SYSTEM_FABRIC_ARB_0, SYSTEM_FABRIC_ARB, 22), + + MSM_RPM_MAP(8660, MM_FABRIC_HALT_0, MM_FABRIC_HALT, 2), + MSM_RPM_MAP(8660, MM_FABRIC_CLOCK_MODE_0, + MM_FABRIC_CLOCK_MODE, 3), + MSM_RPM_MAP(8660, MM_FABRIC_ARB_0, MM_FABRIC_ARB, 23), + + MSM_RPM_MAP(8660, SMPS0B_0, SMPS0B, 2), + MSM_RPM_MAP(8660, SMPS1B_0, SMPS1B, 2), + MSM_RPM_MAP(8660, SMPS2B_0, SMPS2B, 2), + MSM_RPM_MAP(8660, SMPS3B_0, SMPS3B, 2), + MSM_RPM_MAP(8660, SMPS4B_0, SMPS4B, 2), + MSM_RPM_MAP(8660, LDO0B_0, LDO0B, 2), + MSM_RPM_MAP(8660, LDO1B_0, LDO1B, 2), + MSM_RPM_MAP(8660, LDO2B_0, LDO2B, 2), + MSM_RPM_MAP(8660, LDO3B_0, LDO3B, 2), + MSM_RPM_MAP(8660, LDO4B_0, LDO4B, 2), + MSM_RPM_MAP(8660, LDO5B_0, LDO5B, 2), + MSM_RPM_MAP(8660, LDO6B_0, LDO6B, 2), + MSM_RPM_MAP(8660, LVS0B, LVS0B, 1), + MSM_RPM_MAP(8660, LVS1B, LVS1B, 1), + MSM_RPM_MAP(8660, LVS2B, LVS2B, 1), + MSM_RPM_MAP(8660, LVS3B, LVS3B, 1), + MSM_RPM_MAP(8660, MVS, MVS, 1), + + MSM_RPM_MAP(8660, SMPS0_0, SMPS0, 2), + MSM_RPM_MAP(8660, SMPS1_0, SMPS1, 2), + MSM_RPM_MAP(8660, SMPS2_0, SMPS2, 2), + MSM_RPM_MAP(8660, SMPS3_0, SMPS3, 2), + MSM_RPM_MAP(8660, SMPS4_0, SMPS4, 2), + MSM_RPM_MAP(8660, LDO0_0, LDO0, 2), + MSM_RPM_MAP(8660, LDO1_0, LDO1, 2), + MSM_RPM_MAP(8660, LDO2_0, LDO2, 2), + MSM_RPM_MAP(8660, LDO3_0, LDO3, 2), + MSM_RPM_MAP(8660, LDO4_0, LDO4, 2), + MSM_RPM_MAP(8660, LDO5_0, LDO5, 2), + MSM_RPM_MAP(8660, LDO6_0, LDO6, 2), + MSM_RPM_MAP(8660, LDO7_0, LDO7, 2), + MSM_RPM_MAP(8660, LDO8_0, LDO8, 2), + MSM_RPM_MAP(8660, LDO9_0, LDO9, 2), + MSM_RPM_MAP(8660, LDO10_0, LDO10, 2), + MSM_RPM_MAP(8660, LDO11_0, LDO11, 2), + MSM_RPM_MAP(8660, LDO12_0, LDO12, 2), + MSM_RPM_MAP(8660, LDO13_0, LDO13, 2), + MSM_RPM_MAP(8660, LDO14_0, LDO14, 2), + MSM_RPM_MAP(8660, LDO15_0, LDO15, 2), + MSM_RPM_MAP(8660, LDO16_0, LDO16, 2), + MSM_RPM_MAP(8660, LDO17_0, LDO17, 2), + MSM_RPM_MAP(8660, LDO18_0, LDO18, 2), + MSM_RPM_MAP(8660, LDO19_0, LDO19, 2), + MSM_RPM_MAP(8660, LDO20_0, LDO20, 2), + MSM_RPM_MAP(8660, LDO21_0, LDO21, 2), + MSM_RPM_MAP(8660, LDO22_0, LDO22, 2), + MSM_RPM_MAP(8660, LDO23_0, LDO23, 2), + MSM_RPM_MAP(8660, LDO24_0, LDO24, 2), + MSM_RPM_MAP(8660, LDO25_0, LDO25, 2), + MSM_RPM_MAP(8660, LVS0, LVS0, 1), + MSM_RPM_MAP(8660, LVS1, LVS1, 1), + MSM_RPM_MAP(8660, NCP_0, NCP, 2), + MSM_RPM_MAP(8660, CXO_BUFFERS, CXO_BUFFERS, 1), + }, + .target_status = { + MSM_RPM_STATUS_ID_MAP(8660, VERSION_MAJOR), + MSM_RPM_STATUS_ID_MAP(8660, VERSION_MINOR), + MSM_RPM_STATUS_ID_MAP(8660, VERSION_BUILD), + MSM_RPM_STATUS_ID_MAP(8660, SUPPORTED_RESOURCES_0), + MSM_RPM_STATUS_ID_MAP(8660, SUPPORTED_RESOURCES_1), + MSM_RPM_STATUS_ID_MAP(8660, SUPPORTED_RESOURCES_2), + MSM_RPM_STATUS_ID_MAP(8660, SEQUENCE), + + MSM_RPM_STATUS_ID_MAP(8660, CXO_CLK), + MSM_RPM_STATUS_ID_MAP(8660, PXO_CLK), + MSM_RPM_STATUS_ID_MAP(8660, PLL_4), + MSM_RPM_STATUS_ID_MAP(8660, APPS_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8660, SYSTEM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8660, MM_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8660, DAYTONA_FABRIC_CLK), + MSM_RPM_STATUS_ID_MAP(8660, SFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8660, CFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8660, MMFPB_CLK), + MSM_RPM_STATUS_ID_MAP(8660, SMI_CLK), + MSM_RPM_STATUS_ID_MAP(8660, EBI1_CLK), + + MSM_RPM_STATUS_ID_MAP(8660, APPS_L2_CACHE_CTL), + + MSM_RPM_STATUS_ID_MAP(8660, APPS_FABRIC_HALT), + MSM_RPM_STATUS_ID_MAP(8660, APPS_FABRIC_CLOCK_MODE), + MSM_RPM_STATUS_ID_MAP(8660, APPS_FABRIC_ARB), + + MSM_RPM_STATUS_ID_MAP(8660, SYSTEM_FABRIC_HALT), + MSM_RPM_STATUS_ID_MAP(8660, SYSTEM_FABRIC_CLOCK_MODE), + MSM_RPM_STATUS_ID_MAP(8660, SYSTEM_FABRIC_ARB), + + MSM_RPM_STATUS_ID_MAP(8660, MM_FABRIC_HALT), + MSM_RPM_STATUS_ID_MAP(8660, MM_FABRIC_CLOCK_MODE), + MSM_RPM_STATUS_ID_MAP(8660, MM_FABRIC_ARB), + + + MSM_RPM_STATUS_ID_MAP(8660, SMPS0B_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS0B_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS1B_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS1B_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS2B_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS2B_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS3B_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS3B_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS4B_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS4B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO0B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO0B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO1B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO1B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO2B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO2B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO3B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO3B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO4B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO4B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO5B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO5B_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO6B_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO6B_1), + MSM_RPM_STATUS_ID_MAP(8660, LVS0B), + MSM_RPM_STATUS_ID_MAP(8660, LVS1B), + MSM_RPM_STATUS_ID_MAP(8660, LVS2B), + MSM_RPM_STATUS_ID_MAP(8660, LVS3B), + MSM_RPM_STATUS_ID_MAP(8660, MVS), + + + MSM_RPM_STATUS_ID_MAP(8660, SMPS0_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS0_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS1_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS1_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS2_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS2_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS3_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS3_1), + MSM_RPM_STATUS_ID_MAP(8660, SMPS4_0), + MSM_RPM_STATUS_ID_MAP(8660, SMPS4_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO0_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO0_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO1_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO1_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO2_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO2_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO3_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO3_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO4_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO4_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO5_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO5_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO6_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO6_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO7_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO7_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO8_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO8_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO9_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO9_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO10_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO10_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO11_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO11_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO12_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO12_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO13_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO13_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO14_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO14_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO15_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO15_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO16_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO16_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO17_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO17_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO18_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO18_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO19_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO19_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO20_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO20_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO21_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO21_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO22_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO22_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO23_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO23_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO24_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO24_1), + MSM_RPM_STATUS_ID_MAP(8660, LDO25_0), + MSM_RPM_STATUS_ID_MAP(8660, LDO25_1), + MSM_RPM_STATUS_ID_MAP(8660, LVS0), + MSM_RPM_STATUS_ID_MAP(8660, LVS1), + MSM_RPM_STATUS_ID_MAP(8660, NCP_0), + MSM_RPM_STATUS_ID_MAP(8660, NCP_1), + MSM_RPM_STATUS_ID_MAP(8660, CXO_BUFFERS), + }, + .target_ctrl_id = { + MSM_RPM_CTRL_MAP(8660, VERSION_MAJOR), + MSM_RPM_CTRL_MAP(8660, VERSION_MINOR), + MSM_RPM_CTRL_MAP(8660, VERSION_BUILD), + MSM_RPM_CTRL_MAP(8660, REQ_CTX_0), + MSM_RPM_CTRL_MAP(8660, REQ_SEL_0), + MSM_RPM_CTRL_MAP(8660, ACK_CTX_0), + MSM_RPM_CTRL_MAP(8660, ACK_SEL_0), + }, + .sel_invalidate = MSM_RPM_8660_SEL_INVALIDATE, + .sel_notification = MSM_RPM_8660_SEL_NOTIFICATION, + .sel_last = MSM_RPM_8660_SEL_LAST, + .ver = {2, 0, 0}, +}; + +struct platform_device msm8660_rpm_device = { + .name = "msm_rpm", + .id = -1, +}; diff --git a/arch/arm/mach-msm/devices-msm8x60.h b/arch/arm/mach-msm/devices-msm8x60.h new file mode 100644 index 00000000000..9bfaeeed401 --- /dev/null +++ b/arch/arm/mach-msm/devices-msm8x60.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ARCH_ARM_MACH_MSM_DEVICES_MSM8X60_H +#define __ARCH_ARM_MACH_MSM_DEVICES_MSM8X60_H + +#define MSM_GSBI3_QUP_I2C_BUS_ID 0 +#define MSM_GSBI4_QUP_I2C_BUS_ID 1 +#define MSM_GSBI9_QUP_I2C_BUS_ID 2 +#define MSM_GSBI8_QUP_I2C_BUS_ID 3 +#define MSM_GSBI7_QUP_I2C_BUS_ID 4 +#define MSM_GSBI12_QUP_I2C_BUS_ID 5 +#define MSM_SSBI1_I2C_BUS_ID 6 +#define MSM_SSBI2_I2C_BUS_ID 7 +#define MSM_SSBI3_I2C_BUS_ID 8 + +#ifdef CONFIG_SND_SOC_MSM8660_APQ +extern struct platform_device msm_pcm; +extern struct platform_device msm_pcm_routing; +extern struct platform_device msm_cpudai0; +extern struct platform_device msm_cpudai1; +extern struct platform_device msm_cpudai_hdmi_rx; +extern struct platform_device msm_cpudai_bt_rx; +extern struct platform_device msm_cpudai_bt_tx; +extern struct platform_device msm_cpudai_fm_rx; +extern struct platform_device msm_cpudai_fm_tx; +extern struct platform_device msm_cpu_fe; +extern struct platform_device msm_stub_codec; +extern struct platform_device msm_voice; +extern struct platform_device msm_voip; +extern struct platform_device msm_lpa_pcm; +extern struct platform_device msm_pcm_hostless; +#endif + +#ifdef CONFIG_SPI_QUP +extern struct platform_device msm_gsbi1_qup_spi_device; +extern struct platform_device msm_gsbi10_qup_spi_device; +#endif + +extern struct platform_device msm_bus_apps_fabric; +extern struct platform_device msm_bus_sys_fabric; +extern struct platform_device msm_bus_mm_fabric; +extern struct platform_device msm_bus_sys_fpb; +extern struct platform_device msm_bus_cpss_fpb; +extern struct platform_device msm_bus_def_fab; + +extern struct platform_device msm_device_smd; +extern struct platform_device msm_device_gpio; +extern struct platform_device msm_device_vidc; +extern struct platform_device apq8064_msm_device_vidc; + +extern struct platform_device msm_charm_modem; +extern struct platform_device msm_device_tz_log; +#ifdef CONFIG_HW_RANDOM_MSM +extern struct platform_device msm_device_rng; +#endif + +void __init msm8x60_init_irq(void); +void __init msm8x60_check_2d_hardware(void); + +#ifdef CONFIG_MSM_DSPS +extern struct platform_device msm_dsps_device; +#endif + +#if defined(CONFIG_MSM_RPM_STATS_LOG) +extern struct platform_device msm_rpm_stat_device; +#endif +#endif diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index 131633b12a3..ee8a2cfa844 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Google, Inc. - * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,9 +15,10 @@ #include #include -#include -#include +#include +#include +#include #include #include #include @@ -27,8 +28,37 @@ #include -#include -#include "clock-pcom.h" +#include +#include +#include +#include +#include "pm.h" + +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1_PHYS, + .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart2[] = { + { + .start = INT_UART2, + .end = INT_UART2, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART2_PHYS, + .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; static struct resource resources_uart3[] = { { @@ -44,6 +74,20 @@ static struct resource resources_uart3[] = { }, }; +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +struct platform_device msm_device_uart2 = { + .name = "msm_serial", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart2), + .resource = resources_uart2, +}; + struct platform_device msm_device_uart3 = { .name = "msm_serial", .id = 2, @@ -51,15 +95,309 @@ struct platform_device msm_device_uart3 = { .resource = resources_uart3, }; -struct platform_device msm_device_smd = { - .name = "msm_smd", - .id = -1, +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0900000 +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +#define MSM_I2C_SIZE SZ_4K +#define MSM_I2C_PHYS 0xA9900000 + +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +#define MSM_HSUSB_PHYS 0xA0800000 +static struct resource resources_hsusb_otg[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dma_mask = 0xffffffffULL; +struct platform_device msm_device_hsusb_otg = { + .name = "msm_hsusb_otg", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_otg), + .resource = resources_hsusb_otg, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct resource resources_hsusb_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_gadget_peripheral[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_peripheral = { + .name = "msm_hsusb_peripheral", + .id = -1, + .num_resources = ARRAY_SIZE(resources_hsusb_peripheral), + .resource = resources_hsusb_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +struct platform_device msm_device_gadget_peripheral = { + .name = "msm_hsusb", + .id = -1, + .num_resources = ARRAY_SIZE(resources_gadget_peripheral), + .resource = resources_gadget_peripheral, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +#ifdef CONFIG_USB_FS_HOST +#define MSM_HS2USB_PHYS 0xA0800400 +static struct resource resources_hsusb_host2[] = { + { + .start = MSM_HS2USB_PHYS, + .end = MSM_HS2USB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_OTG, + .end = INT_USB_OTG, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host2 = { + .name = "msm_hsusb_host", + .id = 1, + .num_resources = ARRAY_SIZE(resources_hsusb_host2), + .resource = resources_hsusb_host2, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; +#endif + +static struct resource resources_hsusb_host[] = { + { + .start = MSM_HSUSB_PHYS, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HS, + .end = INT_USB_HS, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_hsusb_host = { + .name = "msm_hsusb_host", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hsusb_host), + .resource = resources_hsusb_host, + .dev = { + .dma_mask = &dma_mask, + .coherent_dma_mask = 0xffffffffULL, + }, +}; + +static struct platform_device *msm_host_devices[] = { + &msm_device_hsusb_host, +#ifdef CONFIG_USB_FS_HOST + &msm_device_hsusb_host2, +#endif +}; + +int msm_add_host(unsigned int host, struct msm_usb_host_platform_data *plat) +{ + struct platform_device *pdev; + + pdev = msm_host_devices[host]; + if (!pdev) + return -ENODEV; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +#ifdef CONFIG_USB_ANDROID +struct usb_diag_platform_data usb_diag_pdata = { + .ch_name = DIAG_LEGACY, + .update_pid_and_serial_num = usb_diag_update_pid_and_serial_num, +}; + +struct platform_device usb_diag_device = { + .name = "usb_diag", + .id = -1, + .dev = { + .platform_data = &usb_diag_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_F_SERIAL +static struct usb_gadget_fserial_platform_data fserial_pdata = { + .no_ports = 2, +}; + +struct platform_device usb_gadget_fserial_device = { + .name = "usb_fserial", + .id = -1, + .dev = { + .platform_data = &fserial_pdata, + }, +}; +#endif + +#define MSM_NAND_PHYS 0xA0A00000 +static struct resource resources_nand[] = { + [0] = { + .name = "msm_nand_dmac", + .start = DMOV_NAND_CHAN, + .end = DMOV_NAND_CHAN, + .flags = IORESOURCE_DMA, + }, + [1] = { + .name = "msm_nand_phys", + .start = MSM_NAND_PHYS, + .end = MSM_NAND_PHYS + 0x7FF, + .flags = IORESOURCE_MEM, + }, }; static struct resource resources_otg[] = { { .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, + .end = MSM_HSUSB_PHYS + SZ_1K - 1, .flags = IORESOURCE_MEM, }, { @@ -75,146 +413,172 @@ struct platform_device msm_device_otg = { .num_resources = ARRAY_SIZE(resources_otg), .resource = resources_otg, .dev = { - .coherent_dma_mask = 0xffffffff, + .coherent_dma_mask = 0xffffffffULL, }, }; -static struct resource resources_hsusb[] = { - { - .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_USB_HS, - .end = INT_USB_HS, - .flags = IORESOURCE_IRQ, - }, +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, }; -struct platform_device msm_device_hsusb = { - .name = "msm_hsusb", +struct platform_device msm_device_nand = { + .name = "msm_nand", .id = -1, - .num_resources = ARRAY_SIZE(resources_hsusb), - .resource = resources_hsusb, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, .dev = { - .coherent_dma_mask = 0xffffffff, + .platform_data = &msm_nand_data, }, }; -static u64 dma_mask = 0xffffffffULL; -static struct resource resources_hsusb_host[] = { +static struct msm_pm_irq_calls qsd8x50_pm_irq_calls = { + .irq_pending = msm_irq_pending, + .idle_sleep_allowed = msm_irq_idle_sleep_allowed, + .enter_sleep1 = msm_irq_enter_sleep1, + .enter_sleep2 = msm_irq_enter_sleep2, + .exit_sleep1 = msm_irq_exit_sleep1, + .exit_sleep2 = msm_irq_exit_sleep2, + .exit_sleep3 = msm_irq_exit_sleep3, +}; + +void __init msm_pm_register_irqs(void) +{ + msm_pm_set_irq_extns(&qsd8x50_pm_irq_calls); +} + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct resource msm_dmov_resource[] = { { - .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, - .flags = IORESOURCE_MEM, + .start = INT_ADM_AARM, + .flags = IORESOURCE_IRQ, }, { - .start = INT_USB_HS, - .end = INT_USB_HS, - .flags = IORESOURCE_IRQ, + .start = 0xA9700000, + .end = 0xA9700000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, }, }; -struct platform_device msm_device_hsusb_host = { - .name = "msm_hsusb_host", - .id = -1, - .num_resources = ARRAY_SIZE(resources_hsusb_host), - .resource = resources_hsusb_host, - .dev = { - .dma_mask = &dma_mask, - .coherent_dma_mask = 0xffffffffULL, +static struct msm_dmov_pdata msm_dmov_pdata = { + .sd = 3, + .sd_size = 0x400, +}; + +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, + .resource = msm_dmov_resource, + .num_resources = ARRAY_SIZE(msm_dmov_resource), + .dev = { + .platform_data = &msm_dmov_pdata, }, }; +#define MSM_SDC1_BASE 0xA0300000 +#define MSM_SDC2_BASE 0xA0400000 +#define MSM_SDC3_BASE 0xA0500000 +#define MSM_SDC4_BASE 0xA0600000 static struct resource resources_sdc1[] = { { - .start = MSM_SDC1_PHYS, - .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1, + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { .start = INT_SDC1_0, - .end = INT_SDC1_0, + .end = INT_SDC1_1, .flags = IORESOURCE_IRQ, - .name = "cmd_irq", }, { - .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, - .name = "status_irq" - }, - { - .start = 8, - .end = 8, + .name = "sdcc_dma_chnl", + .start = DMOV_SDC1_CHAN, + .end = DMOV_SDC1_CHAN, .flags = IORESOURCE_DMA, }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC1_CRCI, + .end = DMOV_SDC1_CRCI, + .flags = IORESOURCE_DMA, + } }; static struct resource resources_sdc2[] = { { - .start = MSM_SDC2_PHYS, - .end = MSM_SDC2_PHYS + MSM_SDC2_SIZE - 1, + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { .start = INT_SDC2_0, - .end = INT_SDC2_0, + .end = INT_SDC2_1, .flags = IORESOURCE_IRQ, - .name = "cmd_irq", }, { - .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, - .name = "status_irq" - }, - { - .start = 8, - .end = 8, + .name = "sdcc_dma_chnl", + .start = DMOV_SDC2_CHAN, + .end = DMOV_SDC2_CHAN, .flags = IORESOURCE_DMA, }, + { + .name = "sdcc_dma_crci", + .start = DMOV_SDC2_CRCI, + .end = DMOV_SDC2_CRCI, + .flags = IORESOURCE_DMA, + } }; static struct resource resources_sdc3[] = { { - .start = MSM_SDC3_PHYS, - .end = MSM_SDC3_PHYS + MSM_SDC3_SIZE - 1, + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { .start = INT_SDC3_0, - .end = INT_SDC3_0, + .end = INT_SDC3_1, .flags = IORESOURCE_IRQ, - .name = "cmd_irq", }, { - .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, - .name = "status_irq" + .name = "sdcc_dma_chnl", + .start = DMOV_SDC3_CHAN, + .end = DMOV_SDC3_CHAN, + .flags = IORESOURCE_DMA, }, { - .start = 8, - .end = 8, + .name = "sdcc_dma_crci", + .start = DMOV_SDC3_CRCI, + .end = DMOV_SDC3_CRCI, .flags = IORESOURCE_DMA, }, }; static struct resource resources_sdc4[] = { { - .start = MSM_SDC4_PHYS, - .end = MSM_SDC4_PHYS + MSM_SDC4_SIZE - 1, + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { .start = INT_SDC4_0, - .end = INT_SDC4_0, + .end = INT_SDC4_1, .flags = IORESOURCE_IRQ, - .name = "cmd_irq", }, { - .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, - .name = "status_irq" + .name = "sdcc_dma_chnl", + .start = DMOV_SDC4_CHAN, + .end = DMOV_SDC4_CHAN, + .flags = IORESOURCE_DMA, }, { - .start = 8, - .end = 8, + .name = "sdcc_dma_crci", + .start = DMOV_SDC4_CRCI, + .end = DMOV_SDC4_CRCI, .flags = IORESOURCE_DMA, }, }; @@ -266,84 +630,321 @@ static struct platform_device *msm_sdcc_devices[] __initdata = { &msm_device_sdc4, }; -int __init msm_add_sdcc(unsigned int controller, - struct msm_mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags) +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) { struct platform_device *pdev; - struct resource *res; if (controller < 1 || controller > 4) return -EINVAL; pdev = msm_sdcc_devices[controller-1]; pdev->dev.platform_data = plat; - - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "status_irq"); - if (!res) - return -EINVAL; - else if (stat_irq) { - res->start = res->end = stat_irq; - res->flags &= ~IORESOURCE_DISABLED; - res->flags |= stat_irq_flags; - } - return platform_device_register(pdev); } -struct clk_lookup msm_clocks_8x50[] = { - CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), - CLK_PCOM("ce_clk", CE_CLK, NULL, 0), - CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), - CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), - CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), - CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("gp_clk", GP_CLK, NULL, 0), - CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, 0), - CLK_PCOM("i2c_clk", I2C_CLK, NULL, 0), - CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), - CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), - CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), - CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), - CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), - CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), - CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), - CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), - CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), - CLK_PCOM("sdc_clk", SDC1_CLK, "msm_sdcc.1", OFF), - CLK_PCOM("sdc_pclk", SDC1_P_CLK, "msm_sdcc.1", OFF), - CLK_PCOM("sdc_clk", SDC2_CLK, "msm_sdcc.2", OFF), - CLK_PCOM("sdc_pclk", SDC2_P_CLK, "msm_sdcc.2", OFF), - CLK_PCOM("sdc_clk", SDC3_CLK, "msm_sdcc.3", OFF), - CLK_PCOM("sdc_pclk", SDC3_P_CLK, "msm_sdcc.3", OFF), - CLK_PCOM("sdc_clk", SDC4_CLK, "msm_sdcc.4", OFF), - CLK_PCOM("sdc_pclk", SDC4_P_CLK, "msm_sdcc.4", OFF), - CLK_PCOM("spi_clk", SPI_CLK, NULL, 0), - CLK_PCOM("tsif_clk", TSIF_CLK, NULL, 0), - CLK_PCOM("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), - CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), - CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), - CLK_PCOM("uart_clk", UART1_CLK, NULL, OFF), - CLK_PCOM("uart_clk", UART2_CLK, NULL, 0), - CLK_PCOM("uart_clk", UART3_CLK, "msm_serial.2", OFF), - CLK_PCOM("uartdm_clk", UART1DM_CLK, NULL, OFF), - CLK_PCOM("uartdm_clk", UART2DM_CLK, NULL, 0), - CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), - CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, NULL, OFF), - CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), - CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), - CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), - CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), - CLK_PCOM("vfe_axi_clk", VFE_AXI_CLK, NULL, OFF), - CLK_PCOM("usb_hs2_clk", USB_HS2_CLK, NULL, OFF), - CLK_PCOM("usb_hs2_pclk", USB_HS2_P_CLK, NULL, OFF), - CLK_PCOM("usb_hs3_clk", USB_HS3_CLK, NULL, OFF), - CLK_PCOM("usb_hs3_pclk", USB_HS3_P_CLK, NULL, OFF), - CLK_PCOM("usb_phy_clk", USB_PHY_CLK, NULL, 0), +#if defined(CONFIG_FB_MSM_MDP40) +#define MDP_BASE 0xA3F00000 +#define PMDH_BASE 0xAD600000 +#define EMDH_BASE 0xAD700000 +#define TVENC_BASE 0xAD400000 +#else +#define MDP_BASE 0xAA200000 +#define PMDH_BASE 0xAA600000 +#define EMDH_BASE 0xAA700000 +#define TVENC_BASE 0xAA400000 +#endif + +static struct resource msm_mdp_resources[] = { + { + .name = "mdp", + .start = MDP_BASE, + .end = MDP_BASE + 0x000F0000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, }; -unsigned msm_num_clocks_8x50 = ARRAY_SIZE(msm_clocks_8x50); +static struct resource msm_mddi_resources[] = { + { + .name = "pmdh", + .start = PMDH_BASE, + .end = PMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_mddi_ext_resources[] = { + { + .name = "emdh", + .start = EMDH_BASE, + .end = EMDH_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct resource msm_ebi2_lcd_resources[] = { + { + .name = "base", + .start = 0xa0d00000, + .end = 0xa0d00000 + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd01", + .start = 0x98000000, + .end = 0x98000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "lcd02", + .start = 0x9c000000, + .end = 0x9c000000 + 0x80000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource msm_tvenc_resources[] = { + { + .name = "tvenc", + .start = TVENC_BASE, + .end = TVENC_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device msm_mdp_device = { + .name = "mdp", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mdp_resources), + .resource = msm_mdp_resources, +}; + +static struct platform_device msm_mddi_device = { + .name = "mddi", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_resources), + .resource = msm_mddi_resources, +}; + +static struct platform_device msm_mddi_ext_device = { + .name = "mddi_ext", + .id = 0, + .num_resources = ARRAY_SIZE(msm_mddi_ext_resources), + .resource = msm_mddi_ext_resources, +}; + +static struct platform_device msm_ebi2_lcd_device = { + .name = "ebi2_lcd", + .id = 0, + .num_resources = ARRAY_SIZE(msm_ebi2_lcd_resources), + .resource = msm_ebi2_lcd_resources, +}; + +static struct platform_device msm_lcdc_device = { + .name = "lcdc", + .id = 0, +}; + +static struct platform_device msm_tvenc_device = { + .name = "tvenc", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, +}; + +#if defined(CONFIG_MSM_SOC_REV_A) +#define MSM_QUP_PHYS 0xA1680000 +#define MSM_GSBI_QUP_I2C_PHYS 0xA1600000 +#define INT_PWB_QUP_ERR INT_GSBI_QUP +#else +#define MSM_QUP_PHYS 0xA9900000 +#define MSM_GSBI_QUP_I2C_PHYS 0xA9900000 +#define INT_PWB_QUP_ERR INT_PWB_I2C +#endif +#define MSM_QUP_SIZE SZ_4K +static struct resource resources_qup[] = { + { + .name = "qup_phys_addr", + .start = MSM_QUP_PHYS, + .end = MSM_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI_QUP_I2C_PHYS, + .end = MSM_GSBI_QUP_I2C_PHYS + 4 - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_err_intr", + .start = INT_PWB_QUP_ERR, + .end = INT_PWB_QUP_ERR, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device qup_device_i2c = { + .name = "qup_i2c", + .id = 4, + .num_resources = ARRAY_SIZE(resources_qup), + .resource = resources_qup, +}; + +/* TSIF begin */ +#if defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) + +#define MSM_TSIF_PHYS (0xa0100000) +#define MSM_TSIF_SIZE (0x200) + +static struct resource tsif_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + .start = INT_TSIF_IRQ, + .end = INT_TSIF_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + .start = MSM_TSIF_PHYS, + .end = MSM_TSIF_PHYS + MSM_TSIF_SIZE - 1, + }, + [2] = { + .flags = IORESOURCE_DMA, + .start = DMOV_TSIF_CHAN, + .end = DMOV_TSIF_CRCI, + }, +}; + +static void tsif_release(struct device *dev) +{ + dev_info(dev, "release\n"); +} + +struct platform_device msm_device_tsif = { + .name = "msm_tsif", + .id = 0, + .num_resources = ARRAY_SIZE(tsif_resources), + .resource = tsif_resources, + .dev = { + .release = tsif_release, + }, +}; +#endif /* defined(CONFIG_TSIF) || defined(CONFIG_TSIF_MODULE) */ +/* TSIF end */ + +#define MSM_TSSC_PHYS 0xAA300000 +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + SZ_4K - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, +}; + +struct platform_device msm_device_tssc = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; + +static void __init msm_register_device(struct platform_device *pdev, void *data) +{ + int ret; + + pdev->dev.platform_data = data; + + ret = platform_device_register(pdev); + if (ret) + dev_err(&pdev->dev, + "%s: platform_device_register() failed = %d\n", + __func__, ret); +} + +void __init msm_fb_register_device(char *name, void *data) +{ + if (!strncmp(name, "mdp", 3)) + msm_register_device(&msm_mdp_device, data); + else if (!strncmp(name, "pmdh", 4)) + msm_register_device(&msm_mddi_device, data); + else if (!strncmp(name, "emdh", 4)) + msm_register_device(&msm_mddi_ext_device, data); + else if (!strncmp(name, "ebi2", 4)) + msm_register_device(&msm_ebi2_lcd_device, data); + else if (!strncmp(name, "tvenc", 5)) + msm_register_device(&msm_tvenc_device, data); + else if (!strncmp(name, "lcdc", 4)) + msm_register_device(&msm_lcdc_device, data); + else + printk(KERN_ERR "%s: unknown device! %s\n", __func__, name); +} + +static struct platform_device msm_camera_device = { + .name = "msm_camera", + .id = 0, +}; + +void __init msm_camera_register_device(void *res, uint32_t num, + void *data) +{ + msm_camera_device.num_resources = num; + msm_camera_device.resource = res; + + msm_register_device(&msm_camera_device, data); +} + +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, + }, + }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_MEM, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 9545c196c6e..f1d7aa0678e 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/devices.h * * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -17,42 +18,394 @@ #define __ARCH_ARM_MACH_MSM_DEVICES_H #include - +#include #include "clock.h" +void __init msm9615_device_init(void); +void __init msm9615_map_io(void); +void __init msm_map_msm9615_io(void); +void __init msm9615_init_irq(void); +void __init msm_rotator_update_bus_vectors(unsigned int xres, + unsigned int yres); + +extern struct platform_device asoc_msm_pcm; +extern struct platform_device asoc_msm_dai0; +extern struct platform_device asoc_msm_dai1; +#if defined (CONFIG_SND_MSM_MVS_DAI_SOC) +extern struct platform_device asoc_msm_mvs; +extern struct platform_device asoc_mvs_dai0; +extern struct platform_device asoc_mvs_dai1; +#endif + +extern struct platform_device msm_ebi0_thermal; +extern struct platform_device msm_ebi1_thermal; + +extern struct platform_device msm_adsp_device; extern struct platform_device msm_device_uart1; extern struct platform_device msm_device_uart2; extern struct platform_device msm_device_uart3; +extern struct platform_device msm8625_device_uart1; + +extern struct platform_device msm_device_uart_dm1; +extern struct platform_device msm_device_uart_dm2; +extern struct platform_device msm_device_uart_dm3; +extern struct platform_device msm_device_uart_dm12; +extern struct platform_device *msm_device_uart_gsbi9; +extern struct platform_device msm_device_uart_dm6; +extern struct platform_device msm_device_uart_dm9; extern struct platform_device msm8960_device_uart_gsbi2; extern struct platform_device msm8960_device_uart_gsbi5; +extern struct platform_device msm8960_device_uart_gsbi8; +extern struct platform_device msm8960_device_ssbi_pmic; +extern struct platform_device msm8960_device_qup_i2c_gsbi3; +extern struct platform_device msm8960_device_qup_i2c_gsbi4; +extern struct platform_device msm8960_device_qup_i2c_gsbi9; +extern struct platform_device msm8960_device_qup_i2c_gsbi10; +extern struct platform_device msm8960_device_qup_i2c_gsbi12; +extern struct platform_device msm8960_device_qup_spi_gsbi1; +extern struct platform_device msm8960_gemini_device; +extern struct platform_device msm8960_mercury_device; +extern struct platform_device msm8960_device_i2c_mux_gsbi4; +extern struct platform_device msm8960_device_csiphy0; +extern struct platform_device msm8960_device_csiphy1; +extern struct platform_device msm8960_device_csiphy2; +extern struct platform_device msm8960_device_csid0; +extern struct platform_device msm8960_device_csid1; +extern struct platform_device msm8960_device_csid2; +extern struct platform_device msm8960_device_ispif; +extern struct platform_device msm8960_device_vfe; +extern struct platform_device msm8960_device_vpe; +extern struct platform_device msm8960_device_cache_erp; + +extern struct platform_device apq8064_device_uart_gsbi1; +extern struct platform_device apq8064_device_uart_gsbi3; +extern struct platform_device apq8064_device_uart_gsbi7; +extern struct platform_device apq8064_device_qup_i2c_gsbi1; +extern struct platform_device apq8064_device_qup_i2c_gsbi3; +extern struct platform_device apq8064_device_qup_i2c_gsbi4; +extern struct platform_device apq8064_device_qup_spi_gsbi5; +extern struct platform_device apq8064_slim_ctrl; +extern struct platform_device apq8064_device_ssbi_pmic1; +extern struct platform_device apq8064_device_ssbi_pmic2; +extern struct platform_device apq8064_device_cache_erp; + +extern struct platform_device msm9615_device_uart_gsbi4; +extern struct platform_device msm9615_device_qup_i2c_gsbi5; +extern struct platform_device msm9615_device_qup_spi_gsbi3; +extern struct platform_device msm9615_slim_ctrl; +extern struct platform_device msm9615_device_ssbi_pmic1; +extern struct platform_device msm9615_device_tsens; +extern struct platform_device msm_bus_9615_sys_fabric; +extern struct platform_device msm_bus_def_fab; extern struct platform_device msm_device_sdc1; extern struct platform_device msm_device_sdc2; extern struct platform_device msm_device_sdc3; extern struct platform_device msm_device_sdc4; -extern struct platform_device msm_device_hsusb; -extern struct platform_device msm_device_otg; +extern struct platform_device msm_device_gadget_peripheral; extern struct platform_device msm_device_hsusb_host; +extern struct platform_device msm_device_hsusb_host2; +extern struct platform_device msm_device_hsic_host; + +extern struct platform_device msm_device_otg; +extern struct platform_device msm_android_usb_device; +extern struct platform_device msm_device_hsic_peripheral; +extern struct platform_device msm8960_device_otg; +extern struct platform_device msm8960_device_gadget_peripheral; + +extern struct platform_device apq8064_device_otg; +extern struct platform_device apq8064_usb_diag_device; +extern struct platform_device apq8064_device_gadget_peripheral; +extern struct platform_device apq8064_device_hsusb_host; +extern struct platform_device apq8064_device_hsic_host; +extern struct platform_device apq8064_device_ehci_host3; +extern struct platform_device apq8064_device_ehci_host4; extern struct platform_device msm_device_i2c; +extern struct platform_device msm_device_i2c_2; + +extern struct platform_device qup_device_i2c; + +extern struct platform_device msm_gsbi0_qup_i2c_device; +extern struct platform_device msm_gsbi1_qup_i2c_device; +extern struct platform_device msm_gsbi3_qup_i2c_device; +extern struct platform_device msm_gsbi4_qup_i2c_device; +extern struct platform_device msm_gsbi7_qup_i2c_device; +extern struct platform_device msm_gsbi8_qup_i2c_device; +extern struct platform_device msm_gsbi9_qup_i2c_device; +extern struct platform_device msm_gsbi12_qup_i2c_device; + +extern struct platform_device msm8625_gsbi0_qup_i2c_device; +extern struct platform_device msm8625_gsbi1_qup_i2c_device; +extern struct platform_device msm8625_device_uart_dm1; +extern struct platform_device msm8625_device_uart_dm2; +extern struct platform_device msm8625_device_sdc1; +extern struct platform_device msm8625_device_sdc2; +extern struct platform_device msm8625_device_sdc3; +extern struct platform_device msm8625_device_sdc4; +extern struct platform_device msm8625_device_gadget_peripheral; +extern struct platform_device msm8625_device_hsusb_host; +extern struct platform_device msm8625_device_otg; +extern struct platform_device msm8625_kgsl_3d0; +extern struct platform_device msm8625_device_adsp; + +extern struct platform_device msm_slim_ctrl; +extern struct platform_device msm_device_sps; +extern struct platform_device msm_device_usb_bam; +extern struct platform_device msm_device_sps_apq8064; +extern struct platform_device msm_device_bam_dmux; extern struct platform_device msm_device_smd; +extern struct platform_device msm_device_smd_apq8064; +extern struct platform_device msm8625_device_smd; +extern struct platform_device msm_device_dmov; +extern struct platform_device msm8960_device_dmov; +extern struct platform_device apq8064_device_dmov; +extern struct platform_device msm9615_device_dmov; +extern struct platform_device msm8625_device_dmov; +extern struct platform_device msm_device_dmov_adm0; +extern struct platform_device msm_device_dmov_adm1; + +extern struct platform_device msm_device_pcie; extern struct platform_device msm_device_nand; -extern struct platform_device msm_device_mddi0; -extern struct platform_device msm_device_mddi1; -extern struct platform_device msm_device_mdp; - -extern struct clk_lookup msm_clocks_7x01a[]; -extern unsigned msm_num_clocks_7x01a; - -extern struct clk_lookup msm_clocks_7x30[]; -extern unsigned msm_num_clocks_7x30; - -extern struct clk_lookup msm_clocks_8x50[]; -extern unsigned msm_num_clocks_8x50; +extern struct platform_device msm_device_tssc; +extern struct platform_device msm_rotator_device; +#ifdef CONFIG_MSM_VCAP +extern struct platform_device msm8064_device_vcap; #endif + +#ifdef CONFIG_MSM_BUS_SCALING +extern struct msm_bus_scale_pdata rotator_bus_scale_pdata; +#endif + +extern struct platform_device msm_device_tsif[2]; + +extern struct platform_device msm_device_ssbi_pmic1; +extern struct platform_device msm_device_ssbi_pmic2; +extern struct platform_device msm_device_ssbi1; +extern struct platform_device msm_device_ssbi2; +extern struct platform_device msm_device_ssbi3; +extern struct platform_device msm_device_ssbi6; +extern struct platform_device msm_device_ssbi7; + +extern struct platform_device msm_gsbi1_qup_spi_device; + +extern struct platform_device msm_device_vidc_720p; + +extern struct platform_device msm_pcm; +extern struct platform_device msm_multi_ch_pcm; +extern struct platform_device msm_pcm_routing; +extern struct platform_device msm_cpudai0; +extern struct platform_device msm_cpudai1; +extern struct platform_device mpq_cpudai_sec_i2s_rx; +extern struct platform_device msm8960_cpudai_slimbus_2_rx; +extern struct platform_device msm8960_cpudai_slimbus_2_tx; +extern struct platform_device msm_cpudai_hdmi_rx; +extern struct platform_device msm_cpudai_bt_rx; +extern struct platform_device msm_cpudai_bt_tx; +extern struct platform_device msm_cpudai_fm_rx; +extern struct platform_device msm_cpudai_fm_tx; +extern struct platform_device msm_cpudai_auxpcm_rx; +extern struct platform_device msm_cpudai_auxpcm_tx; +extern struct platform_device msm_cpudai_sec_auxpcm_rx; +extern struct platform_device msm_cpudai_sec_auxpcm_tx; +extern struct platform_device msm_cpu_fe; +extern struct platform_device msm_stub_codec; +extern struct platform_device msm_voice; +extern struct platform_device msm_voip; +extern struct platform_device msm_lpa_pcm; +extern struct platform_device msm_pcm_hostless; +extern struct platform_device msm_cpudai_afe_01_rx; +extern struct platform_device msm_cpudai_afe_01_tx; +extern struct platform_device msm_cpudai_afe_02_rx; +extern struct platform_device msm_cpudai_afe_02_tx; +extern struct platform_device msm_pcm_afe; +extern struct platform_device msm_compr_dsp; +extern struct platform_device msm_cpudai_incall_music_rx; +extern struct platform_device msm_cpudai_incall_record_rx; +extern struct platform_device msm_cpudai_incall_record_tx; +extern struct platform_device msm_i2s_cpudai0; +extern struct platform_device msm_i2s_cpudai1; + +extern struct platform_device msm_pil_q6v3; +extern struct platform_device msm_pil_modem; +extern struct platform_device msm_pil_tzapps; +extern struct platform_device msm_pil_dsps; +extern struct platform_device msm_pil_vidc; +extern struct platform_device msm_8960_q6_lpass; +extern struct platform_device msm_8960_q6_mss_fw; +extern struct platform_device msm_8960_q6_mss_sw; +extern struct platform_device msm_8960_riva; +extern struct platform_device msm_gss; + +extern struct platform_device apq_pcm; +extern struct platform_device apq_pcm_routing; +extern struct platform_device apq_cpudai0; +extern struct platform_device apq_cpudai1; +extern struct platform_device mpq_cpudai_mi2s_tx; +extern struct platform_device apq_cpudai_hdmi_rx; +extern struct platform_device apq_cpudai_bt_rx; +extern struct platform_device apq_cpudai_bt_tx; +extern struct platform_device apq_cpudai_fm_rx; +extern struct platform_device apq_cpudai_fm_tx; +extern struct platform_device apq_cpudai_auxpcm_rx; +extern struct platform_device apq_cpudai_auxpcm_tx; +extern struct platform_device apq_cpu_fe; +extern struct platform_device apq_stub_codec; +extern struct platform_device apq_voice; +extern struct platform_device apq_voip; +extern struct platform_device apq_lpa_pcm; +extern struct platform_device apq_compr_dsp; +extern struct platform_device apq_multi_ch_pcm; +extern struct platform_device apq_pcm_hostless; +extern struct platform_device apq_cpudai_afe_01_rx; +extern struct platform_device apq_cpudai_afe_01_tx; +extern struct platform_device apq_cpudai_afe_02_rx; +extern struct platform_device apq_cpudai_afe_02_tx; +extern struct platform_device apq_pcm_afe; +extern struct platform_device apq_cpudai_stub; +extern struct platform_device apq_cpudai_slimbus_1_rx; +extern struct platform_device apq_cpudai_slimbus_1_tx; +extern struct platform_device apq_cpudai_slimbus_2_rx; +extern struct platform_device apq_cpudai_slimbus_2_tx; +extern struct platform_device apq_cpudai_slimbus_3_rx; +extern struct platform_device apq_cpudai_slimbus_3_tx; +extern struct platform_device apq_cpudai_slim_4_rx; +extern struct platform_device apq_cpudai_slim_4_tx; + +extern struct platform_device *msm_footswitch_devices[]; +extern unsigned msm_num_footswitch_devices; +extern struct platform_device *msm8660_footswitch[]; +extern unsigned msm8660_num_footswitch; +extern struct platform_device *msm8960_footswitch[]; +extern unsigned msm8960_num_footswitch; +extern struct platform_device *apq8064_footswitch[]; +extern unsigned apq8064_num_footswitch; +extern struct platform_device *msm8930_footswitch[]; +extern unsigned msm8930_num_footswitch; + +extern struct platform_device fsm_qfp_fuse_device; + +extern struct platform_device fsm_xo_device; + +extern struct platform_device qfec_device; + +extern struct platform_device msm_kgsl_3d0; +extern struct platform_device msm_kgsl_2d0; +extern struct platform_device msm_kgsl_2d1; + +extern struct platform_device msm_mipi_dsi1_device; +extern struct platform_device msm_lvds_device; +extern struct platform_device msm_ebi2_lcdc_device; + +extern struct clk_lookup msm_clocks_fsm9xxx[]; +extern unsigned msm_num_clocks_fsm9xxx; + +extern struct platform_device msm_footswitch; + +void __init msm_fb_register_device(char *name, void *data); +void __init msm_camera_register_device(void *, uint32_t, void *); +struct platform_device *msm_add_gsbi9_uart(void); +extern struct platform_device msm_device_touchscreen; + +extern struct platform_device led_pdev; + +extern struct platform_device msm8960_rpm_device; +extern struct platform_device msm8960_rpm_stat_device; +extern struct platform_device msm8960_rpm_log_device; + +extern struct platform_device msm8930_rpm_device; +extern struct platform_device msm8930_rpm_stat_device; +extern struct platform_device msm8930_rpm_log_device; + +extern struct platform_device msm8660_rpm_device; +extern struct platform_device msm8660_rpm_stat_device; +extern struct platform_device msm8660_rpm_log_device; + +extern struct platform_device msm9615_rpm_device; +extern struct platform_device msm9615_rpm_stat_device; +extern struct platform_device msm9615_rpm_log_device; + +extern struct platform_device apq8064_rpm_device; +extern struct platform_device apq8064_rpm_stat_device; +extern struct platform_device apq8064_rpm_log_device; + +extern struct platform_device msm_device_rng; +extern struct platform_device apq8064_device_rng; + +#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \ + defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE) +extern struct platform_device msm9615_qcrypto_device; +#endif + +#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \ + defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE) +extern struct platform_device msm9615_qcedev_device; +#endif +extern struct platform_device msm8960_device_watchdog; +extern struct platform_device msm8660_device_watchdog; +extern struct platform_device msm8064_device_watchdog; +extern struct platform_device msm9615_device_watchdog; +extern struct platform_device fsm9xxx_device_watchdog; + +extern struct platform_device apq8064_qdss_device; +extern struct platform_device msm_qdss_device; +extern struct platform_device msm_etb_device; +extern struct platform_device msm_tpiu_device; +extern struct platform_device msm_funnel_device; +extern struct platform_device msm_etm_device; +extern struct platform_device apq8064_etm_device; +#endif + +extern struct platform_device msm_bus_8064_apps_fabric; +extern struct platform_device msm_bus_8064_sys_fabric; +extern struct platform_device msm_bus_8064_mm_fabric; +extern struct platform_device msm_bus_8064_sys_fpb; +extern struct platform_device msm_bus_8064_cpss_fpb; + +extern struct platform_device mdm_8064_device; +extern struct platform_device msm_dsps_device_8064; +extern struct platform_device *msm_copper_stub_regulator_devices[]; +extern int msm_copper_stub_regulator_devices_len; + +extern struct platform_device msm8960_cpu_idle_device; +extern struct platform_device msm8930_cpu_idle_device; +extern struct platform_device apq8064_cpu_idle_device; + +extern struct platform_device msm8960_msm_gov_device; +extern struct platform_device msm8930_msm_gov_device; +extern struct platform_device apq8064_msm_gov_device; + +extern struct platform_device msm_bus_8930_apps_fabric; +extern struct platform_device msm_bus_8930_sys_fabric; +extern struct platform_device msm_bus_8930_mm_fabric; +extern struct platform_device msm_bus_8930_sys_fpb; +extern struct platform_device msm_bus_8930_cpss_fpb; + +extern struct platform_device msm_device_csic0; +extern struct platform_device msm_device_csic1; +extern struct platform_device msm_device_vfe; +extern struct platform_device msm_device_vpe; +extern struct platform_device mpq8064_device_qup_i2c_gsbi5; + +extern struct platform_device msm8960_iommu_domain_device; +extern struct platform_device msm8930_iommu_domain_device; +extern struct platform_device apq8064_iommu_domain_device; + +extern struct platform_device msm8960_rtb_device; +extern struct platform_device msm8930_rtb_device; +extern struct platform_device apq8064_rtb_device; + +extern struct platform_device msm8960_cache_dump_device; +extern struct platform_device apq8064_cache_dump_device; + +extern struct platform_device copper_device_tz_log; + +extern struct platform_device mdm_sglte_device; + +extern struct platform_device apq_device_tz_log; diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c new file mode 100644 index 00000000000..b2b45723e2c --- /dev/null +++ b/arch/arm/mach-msm/devices_htc.c @@ -0,0 +1,450 @@ +/* linux/arch/arm/mach-msm/devices.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include +#include +#include "gpio_chip.h" +#include "devices.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *df_serialno = "000000000000"; + +#if 0 +struct platform_device *devices[] __initdata = { + &msm_device_nand, + &msm_device_smd, + &msm_device_i2c, +}; + +void __init msm_add_devices(void) +{ + platform_add_devices(devices, ARRAY_SIZE(devices)); +} +#endif + +#define HSUSB_API_INIT_PHY_PROC 2 +#define HSUSB_API_PROG 0x30000064 +#define HSUSB_API_VERS 0x10001 +static void internal_phy_reset(void) +{ + struct msm_rpc_endpoint *usb_ep; + int rc; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + printk(KERN_INFO "msm_hsusb_phy_reset\n"); + + usb_ep = msm_rpc_connect(HSUSB_API_PROG, HSUSB_API_VERS, 0); + if (IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: init rpc failed! error: %ld\n", + __func__, PTR_ERR(usb_ep)); + goto close; + } + rc = msm_rpc_call(usb_ep, HSUSB_API_INIT_PHY_PROC, + &req, sizeof(req), 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: rpc call failed! (%d)\n", __func__, rc); + +close: + msm_rpc_close(usb_ep); +} + +/* adjust eye diagram, disable vbusvalid interrupts */ +static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +#ifdef CONFIG_USB_FUNCTION +static char *usb_functions[] = { +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS) + "usb_mass_storage", +#endif +#ifdef CONFIG_USB_FUNCTION_ADB + "adb", +#endif +}; + +static struct msm_hsusb_product usb_products[] = { + { + .product_id = 0x0c01, + .functions = 0x00000041, /* usb_mass_storage */ + }, + { + .product_id = 0x0c02, + .functions = 0x00000043, /* usb_mass_storage + adb */ + }, +}; +#endif + +struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_reset = internal_phy_reset, + .phy_init_seq = hsusb_phy_init_seq, +#ifdef CONFIG_USB_FUNCTION + .vendor_id = 0x0bb4, + .product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + + .functions = usb_functions, + .num_functions = ARRAY_SIZE(usb_functions), + .products = usb_products, + .num_products = ARRAY_SIZE(usb_products), +#endif +}; + +#ifdef CONFIG_USB_FUNCTION +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "HTC ", + .product = "Android Phone ", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c01, + .adb_product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +void __init msm_add_usb_devices(void (*phy_reset) (void)) +{ + /* setup */ + if (phy_reset) + msm_hsusb_pdata.phy_reset = phy_reset; + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + platform_device_register(&msm_device_hsusb); +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE + platform_device_register(&usb_mass_storage_device); +#endif +#ifdef CONFIG_USB_ANDROID + platform_device_register(&android_usb_device); +#endif +} + +static struct android_pmem_platform_data pmem_pdata = { + .name = "pmem", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 1, +}; + +static struct android_pmem_platform_data pmem_adsp_pdata = { + .name = "pmem_adsp", + .allocator_type = PMEM_ALLOCATORTYPE_BUDDYBESTFIT, + .cached = 0, +}; + +static struct android_pmem_platform_data pmem_camera_pdata = { + .name = "pmem_camera", + .allocator_type = PMEM_ALLOCATORTYPE_BUDDYBESTFIT, + .cached = 0, +}; + +static struct android_pmem_platform_data pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 0, + .buffered = 1, +}; + +static struct android_pmem_platform_data pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 0, + .buffered = 1, +}; + +static struct platform_device pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &pmem_pdata }, +}; + +static struct platform_device pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &pmem_adsp_pdata }, +}; + +static struct platform_device pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &pmem_gpu0_pdata }, +}; + +static struct platform_device pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &pmem_gpu1_pdata }, +}; + +static struct platform_device pmem_camera_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &pmem_camera_pdata }, +}; + +static struct resource ram_console_resource[] = { + { + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resource), + .resource = ram_console_resource, +}; + +void __init msm_add_mem_devices(struct msm_pmem_setting *setting) +{ + if (setting->pmem_size) { + pmem_pdata.start = setting->pmem_start; + pmem_pdata.size = setting->pmem_size; + platform_device_register(&pmem_device); + } + + if (setting->pmem_adsp_size) { + pmem_adsp_pdata.start = setting->pmem_adsp_start; + pmem_adsp_pdata.size = setting->pmem_adsp_size; + platform_device_register(&pmem_adsp_device); + } + + if (setting->pmem_gpu0_size) { + pmem_gpu0_pdata.start = setting->pmem_gpu0_start; + pmem_gpu0_pdata.size = setting->pmem_gpu0_size; + platform_device_register(&pmem_gpu0_device); + } + + if (setting->pmem_gpu1_size) { + pmem_gpu1_pdata.start = setting->pmem_gpu1_start; + pmem_gpu1_pdata.size = setting->pmem_gpu1_size; + platform_device_register(&pmem_gpu1_device); + } + + if (setting->pmem_camera_size) { + pmem_camera_pdata.start = setting->pmem_camera_start; + pmem_camera_pdata.size = setting->pmem_camera_size; + platform_device_register(&pmem_camera_device); + } + + if (setting->ram_console_size) { + ram_console_resource[0].start = setting->ram_console_start; + ram_console_resource[0].end = setting->ram_console_start + + setting->ram_console_size - 1; + platform_device_register(&ram_console_device); + } +} + +#define PM_LIBPROG 0x30000061 +#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define PM_LIBVERS 0xfb837d0b +#else +#define PM_LIBVERS 0x10001 +#endif + +#if 0 +static struct platform_device *msm_serial_devices[] __initdata = { + &msm_device_uart1, + &msm_device_uart2, + &msm_device_uart3, + #ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, + &msm_device_uart_dm2, + #endif +}; + +int __init msm_add_serial_devices(unsigned num) +{ + if (num > MSM_SERIAL_NUM) + return -EINVAL; + + return platform_device_register(msm_serial_devices[num]); +} +#endif + +#define ATAG_SMI 0x4d534D71 +/* setup calls mach->fixup, then parse_tags, parse_cmdline + * We need to setup meminfo in mach->fixup, so this function + * will need to traverse each tag to find smi tag. + */ +int __init parse_tag_smi(const struct tag *tags) +{ + int smi_sz = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SMI) { + printk(KERN_DEBUG "find the smi tag\n"); + find = 1; + break; + } + } + if (!find) + return -1; + + printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size); + smi_sz = t->u.mem.size; + return smi_sz; +} +__tagtable(ATAG_SMI, parse_tag_smi); + + +#define ATAG_HWID 0x4d534D72 +int __init parse_tag_hwid(const struct tag *tags) +{ + int hwid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_HWID) { + printk(KERN_DEBUG "find the hwid tag\n"); + find = 1; + break; + } + } + + if (find) + hwid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid); + return hwid; +} +__tagtable(ATAG_HWID, parse_tag_hwid); + +#define ATAG_SKUID 0x4d534D73 +int __init parse_tag_skuid(const struct tag *tags) +{ + int skuid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SKUID) { + printk(KERN_DEBUG "find the skuid tag\n"); + find = 1; + break; + } + } + + if (find) + skuid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid); + return skuid; +} +__tagtable(ATAG_SKUID, parse_tag_skuid); + +#define ATAG_ENGINEERID 0x4d534D75 +int __init parse_tag_engineerid(const struct tag *tags) +{ + int engineerid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ENGINEERID) { + printk(KERN_DEBUG "find the engineer tag\n"); + find = 1; + break; + } + } + + if (find) + engineerid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_engineerid: hwid = 0x%x\n", engineerid); + return engineerid; +} +__tagtable(ATAG_ENGINEERID, parse_tag_engineerid); + +static int mfg_mode; +int __init board_mfg_mode_init(char *s) +{ + if (!strcmp(s, "normal")) + mfg_mode = 0; + else if (!strcmp(s, "factory2")) + mfg_mode = 1; + else if (!strcmp(s, "recovery")) + mfg_mode = 2; + else if (!strcmp(s, "charge")) + mfg_mode = 3; + + return 1; +} +__setup("androidboot.mode=", board_mfg_mode_init); + + +int board_mfg_mode(void) +{ + return mfg_mode; +} + +static int __init board_serialno_setup(char *serialno) +{ + char *str; + + if (board_mfg_mode() || !strlen(serialno)) + str = df_serialno; + else + str = serialno; +#ifdef CONFIG_USB_FUNCTION + msm_hsusb_pdata.serial_number = str; +#endif +#ifdef CONFIG_USB_ANDROID + android_usb_pdata.serial_number = str; +#endif + return 1; +} + +__setup("androidboot.serialno=", board_serialno_setup); diff --git a/arch/arm/mach-msm/dfe-fsm9xxx.c b/arch/arm/mach-msm/dfe-fsm9xxx.c new file mode 100644 index 00000000000..1a956e3d9e3 --- /dev/null +++ b/arch/arm/mach-msm/dfe-fsm9xxx.c @@ -0,0 +1,433 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * DFE of FSM9XXX + */ + +#define HH_ADDR_MASK 0x000ffffc +#define HH_OFFSET_VALID(offset) (((offset) & ~HH_ADDR_MASK) == 0) +#define HH_REG_IOADDR(offset) ((uint8_t *) MSM_HH_BASE + (offset)) +#define HH_MAKE_OFFSET(blk, adr) (((blk)&0x1F)<<15|((adr)&0x1FFF)<<2) + +#define HH_REG_SCPN_IREQ_MASK HH_REG_IOADDR(HH_MAKE_OFFSET(5, 0x12)) +#define HH_REG_SCPN_IREQ_FLAG HH_REG_IOADDR(HH_MAKE_OFFSET(5, 0x13)) + +/* + * Device private information per device node + */ + +#define HH_IRQ_FIFO_SIZE 64 +#define HH_IRQ_FIFO_EMPTY(pdev) ((pdev)->irq_fifo_head == \ + (pdev)->irq_fifo_tail) +#define HH_IRQ_FIFO_FULL(pdev) ((((pdev)->irq_fifo_tail + 1) % \ + HH_IRQ_FIFO_SIZE) == \ + (pdev)->irq_fifo_head) + +static struct hh_dev_node_info { + spinlock_t hh_lock; + char irq_fifo[HH_IRQ_FIFO_SIZE]; + unsigned int irq_fifo_head, irq_fifo_tail; + wait_queue_head_t wq; +} hh_dev_info; + +/* + * Device private information per file + */ + +struct hh_dev_file_info { + /* Buffer */ + unsigned int *parray; + unsigned int array_num; + + struct dfe_command_entry *pcmd; + unsigned int cmd_num; +}; + +/* + * File interface + */ + +static int hh_open(struct inode *inode, struct file *file) +{ + struct hh_dev_file_info *pdfi; + + /* private data allocation */ + pdfi = kmalloc(sizeof(*pdfi), GFP_KERNEL); + if (pdfi == NULL) + return -ENOMEM; + file->private_data = pdfi; + + /* buffer initialization */ + pdfi->parray = NULL; + pdfi->array_num = 0; + pdfi->pcmd = NULL; + pdfi->cmd_num = 0; + + return 0; +} + +static int hh_release(struct inode *inode, struct file *file) +{ + struct hh_dev_file_info *pdfi; + + pdfi = (struct hh_dev_file_info *) file->private_data; + + kfree(pdfi->parray); + pdfi->parray = NULL; + pdfi->array_num = 0; + + kfree(pdfi->pcmd); + pdfi->pcmd = NULL; + pdfi->cmd_num = 0; + + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static ssize_t hh_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + signed char irq = -1; + unsigned long irq_flags; + + do { + spin_lock_irqsave(&hh_dev_info.hh_lock, irq_flags); + if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info)) { + irq = hh_dev_info.irq_fifo[hh_dev_info.irq_fifo_head]; + if (++hh_dev_info.irq_fifo_head == HH_IRQ_FIFO_SIZE) + hh_dev_info.irq_fifo_head = 0; + } + spin_unlock_irqrestore(&hh_dev_info.hh_lock, irq_flags); + + if (irq < 0) + if (wait_event_interruptible(hh_dev_info.wq, + !HH_IRQ_FIFO_EMPTY(&hh_dev_info)) < 0) + break; + } while (irq < 0); + + if (irq < 0) { + /* No pending interrupt */ + return 0; + } else { + put_user(irq, buf); + return 1; + } + + return 0; +} + +static ssize_t hh_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static long hh_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int __user *argp = (unsigned int __user *) arg; + struct hh_dev_file_info *pdfi = + (struct hh_dev_file_info *) file->private_data; + + switch (cmd) { + case DFE_IOCTL_READ_REGISTER: + { + unsigned int offset, value; + + if (get_user(offset, argp)) + return -EFAULT; + if (!HH_OFFSET_VALID(offset)) + return -EINVAL; + value = __raw_readl(HH_REG_IOADDR(offset)); + if (put_user(value, argp)) + return -EFAULT; + } + break; + + case DFE_IOCTL_WRITE_REGISTER: + { + struct dfe_write_register_param param; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + if (!HH_OFFSET_VALID(param.offset)) + return -EINVAL; + __raw_writel(param.value, + HH_REG_IOADDR(param.offset)); + } + break; + + case DFE_IOCTL_WRITE_REGISTER_WITH_MASK: + { + struct dfe_write_register_mask_param param; + unsigned int value; + unsigned long irq_flags; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + if (!HH_OFFSET_VALID(param.offset)) + return -EINVAL; + spin_lock_irqsave(&hh_dev_info.hh_lock, + irq_flags); + value = __raw_readl(HH_REG_IOADDR(param.offset)); + value &= ~param.mask; + value |= param.value & param.mask; + __raw_writel(value, HH_REG_IOADDR(param.offset)); + spin_unlock_irqrestore(&hh_dev_info.hh_lock, + irq_flags); + } + break; + + case DFE_IOCTL_READ_REGISTER_ARRAY: + case DFE_IOCTL_WRITE_REGISTER_ARRAY: + { + struct dfe_read_write_array_param param; + unsigned int req_sz; + unsigned long irq_flags; + unsigned int i; + void *addr; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + if (!HH_OFFSET_VALID(param.offset)) + return -EINVAL; + if (param.num == 0) + break; + req_sz = sizeof(unsigned int) * param.num; + + if (pdfi->array_num < param.num) { + void *pmem; + + pmem = kmalloc(req_sz, GFP_KERNEL); + if (pmem == NULL) + return -ENOMEM; + pdfi->parray = (unsigned int *) pmem; + pdfi->array_num = param.num; + } + + if (cmd == DFE_IOCTL_WRITE_REGISTER_ARRAY) + if (copy_from_user(pdfi->parray, + param.pArray, req_sz)) + return -EFAULT; + + addr = HH_REG_IOADDR(param.offset); + + spin_lock_irqsave(&hh_dev_info.hh_lock, + irq_flags); + for (i = 0; i < param.num; ++i, addr += 4) { + if (cmd == DFE_IOCTL_READ_REGISTER_ARRAY) + pdfi->parray[i] = __raw_readl(addr); + else + __raw_writel(pdfi->parray[i], addr); + } + spin_unlock_irqrestore(&hh_dev_info.hh_lock, + irq_flags); + + if (cmd == DFE_IOCTL_READ_REGISTER_ARRAY) + if (copy_to_user(pdfi->parray, + param.pArray, req_sz)) + return -EFAULT; + } + break; + + case DFE_IOCTL_COMMAND: + { + struct dfe_command_param param; + unsigned int req_sz; + unsigned long irq_flags; + unsigned int i, value; + struct dfe_command_entry *pcmd; + void *addr; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + if (param.num == 0) + break; + req_sz = sizeof(struct dfe_command_entry) * param.num; + + if (pdfi->cmd_num < param.num) { + void *pmem; + + pmem = kmalloc(req_sz, GFP_KERNEL); + if (pmem == NULL) + return -ENOMEM; + pdfi->pcmd = (struct dfe_command_entry *) pmem; + pdfi->cmd_num = param.num; + } + + if (copy_from_user(pdfi->pcmd, param.pEntry, req_sz)) + return -EFAULT; + + pcmd = pdfi->pcmd; + + spin_lock_irqsave(&hh_dev_info.hh_lock, + irq_flags); + for (i = 0; i < param.num; ++i, ++pcmd) { + if (!HH_OFFSET_VALID(pcmd->offset)) + return -EINVAL; + addr = HH_REG_IOADDR(pcmd->offset); + + switch (pcmd->code) { + case DFE_IOCTL_COMMAND_CODE_WRITE: + __raw_writel(pcmd->value, addr); + break; + case DFE_IOCTL_COMMAND_CODE_WRITE_WITH_MASK: + value = __raw_readl(addr); + value &= ~pcmd->mask; + value |= pcmd->value & pcmd->mask; + __raw_writel(value, addr); + break; + } + } + spin_unlock_irqrestore(&hh_dev_info.hh_lock, + irq_flags); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int hh_poll(struct file *filp, + struct poll_table_struct *wait) +{ + unsigned mask = 0; + + if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info)) + mask |= POLLIN; + + if (mask == 0) { + poll_wait(filp, &hh_dev_info.wq, wait); + if (!HH_IRQ_FIFO_EMPTY(&hh_dev_info)) + mask |= POLLIN; + } + + return mask; +} + +static const struct file_operations hh_fops = { + .owner = THIS_MODULE, + .open = hh_open, + .release = hh_release, + .read = hh_read, + .write = hh_write, + .unlocked_ioctl = hh_ioctl, + .poll = hh_poll, +}; + +/* + * Interrupt handling + */ + +static irqreturn_t hh_irq_handler(int irq, void *data) +{ + unsigned int irq_enable, irq_flag, irq_mask; + int i; + + irq_enable = __raw_readl(HH_REG_SCPN_IREQ_MASK); + irq_flag = __raw_readl(HH_REG_SCPN_IREQ_FLAG); + irq_flag &= irq_enable; + + /* Disables interrupts */ + irq_enable &= ~irq_flag; + __raw_writel(irq_enable, HH_REG_SCPN_IREQ_MASK); + + /* Adds the pending interrupts to irq_fifo */ + spin_lock(&hh_dev_info.hh_lock); + for (i = 0, irq_mask = 1; i < 32; ++i, irq_mask <<= 1) { + if (HH_IRQ_FIFO_FULL(&hh_dev_info)) + break; + if (irq_flag & irq_mask) { + hh_dev_info.irq_fifo[hh_dev_info.irq_fifo_tail] = \ + (char) i; + if (++hh_dev_info.irq_fifo_tail == HH_IRQ_FIFO_SIZE) + hh_dev_info.irq_fifo_tail = 0; + } + } + spin_unlock(&hh_dev_info.hh_lock); + + /* Wakes up pending processes */ + wake_up_interruptible(&hh_dev_info.wq); + + return IRQ_HANDLED; +} + +/* + * Driver initialization & cleanup + */ + +static struct miscdevice hh_misc_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = DFE_HH_DEVICE_NAME, + .fops = &hh_fops, +}; + +static int __init hh_init(void) +{ + int ret; + + /* lock initialization */ + spin_lock_init(&hh_dev_info.hh_lock); + + /* interrupt handler */ + hh_dev_info.irq_fifo_head = 0; + hh_dev_info.irq_fifo_tail = 0; + ret = request_irq(INT_HH_SUPSS_IRQ, hh_irq_handler, + IRQF_TRIGGER_RISING, "hh_dev", 0); + if (ret < 0) { + pr_err("Cannot register HH interrupt handler.\n"); + return ret; + } + + /* wait queue */ + init_waitqueue_head(&hh_dev_info.wq); + + return misc_register(&hh_misc_dev); +} + +static void __exit hh_exit(void) +{ + misc_deregister(&hh_misc_dev); + free_irq(INT_HH_SUPSS_IRQ, 0); +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Rohit Vaswani "); +MODULE_DESCRIPTION("Qualcomm Hammerhead Digital Front End driver"); +MODULE_VERSION("1.0"); + +module_init(hh_init); +module_exit(hh_exit); + diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index 02cae5e2951..d3b227468ee 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/dma.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2010, 2012 Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -17,10 +18,190 @@ #include #include #include -#include +#include +#include +#include +#include #include +#define MODULE_NAME "msm_dmov" + #define MSM_DMOV_CHANNEL_COUNT 16 +#define MSM_DMOV_CRCI_COUNT 16 + +enum { + CLK_DIS, + CLK_TO_BE_DIS, + CLK_EN +}; + +struct msm_dmov_ci_conf { + int start; + int end; + int burst; +}; + +struct msm_dmov_crci_conf { + int sd; + int blk_size; +}; + +struct msm_dmov_chan_conf { + int sd; + int block; + int priority; +}; + +struct msm_dmov_conf { + void *base; + struct msm_dmov_crci_conf *crci_conf; + struct msm_dmov_chan_conf *chan_conf; + int channel_active; + int sd; + size_t sd_size; + struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT]; + struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT]; + spinlock_t lock; + unsigned int irq; + struct clk *clk; + struct clk *pclk; + struct clk *ebiclk; + unsigned int clk_ctl; + struct timer_list timer; +}; + +static void msm_dmov_clock_timer(unsigned long); +static int msm_dmov_clk_toggle(int, int); + +#ifdef CONFIG_ARCH_MSM8X60 + +#define DMOV_CHANNEL_DEFAULT_CONF { .sd = 1, .block = 0, .priority = 0 } +#define DMOV_CHANNEL_MODEM_CONF { .sd = 3, .block = 0, .priority = 0 } +#define DMOV_CHANNEL_CONF(secd, blk, pri) \ + { .sd = secd, .block = blk, .priority = pri } + +static struct msm_dmov_chan_conf adm0_chan_conf[] = { + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_DEFAULT_CONF, +}; + +static struct msm_dmov_chan_conf adm1_chan_conf[] = { + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_DEFAULT_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, + DMOV_CHANNEL_MODEM_CONF, +}; + +#define DMOV_CRCI_DEFAULT_CONF { .sd = 1, .blk_size = 0 } +#define DMOV_CRCI_CONF(secd, blk) { .sd = secd, .blk_size = blk } + +static struct msm_dmov_crci_conf adm0_crci_conf[] = { + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_CONF(1, 4), + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, +}; + +static struct msm_dmov_crci_conf adm1_crci_conf[] = { + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_CONF(1, 1), + DMOV_CRCI_CONF(1, 1), + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_CONF(1, 1), + DMOV_CRCI_CONF(1, 1), + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_DEFAULT_CONF, + DMOV_CRCI_CONF(1, 1), + DMOV_CRCI_DEFAULT_CONF, +}; + +static struct msm_dmov_conf dmov_conf[] = { + { + .crci_conf = adm0_crci_conf, + .chan_conf = adm0_chan_conf, + .lock = __SPIN_LOCK_UNLOCKED(dmov_lock), + .clk_ctl = CLK_DIS, + .timer = TIMER_INITIALIZER(msm_dmov_clock_timer, 0, 0), + }, { + .crci_conf = adm1_crci_conf, + .chan_conf = adm1_chan_conf, + .lock = __SPIN_LOCK_UNLOCKED(dmov_lock), + .clk_ctl = CLK_DIS, + .timer = TIMER_INITIALIZER(msm_dmov_clock_timer, 0, 1), + } +}; +#else +static struct msm_dmov_conf dmov_conf[] = { + { + .crci_conf = NULL, + .chan_conf = NULL, + .lock = __SPIN_LOCK_UNLOCKED(dmov_lock), + .clk_ctl = CLK_DIS, + .timer = TIMER_INITIALIZER(msm_dmov_clock_timer, 0, 0), + } +}; +#endif + +#define MSM_DMOV_ID_COUNT (MSM_DMOV_CHANNEL_COUNT * ARRAY_SIZE(dmov_conf)) +#define DMOV_REG(name, adm) ((name) + (dmov_conf[adm].base) +\ + (dmov_conf[adm].sd * dmov_conf[adm].sd_size)) +#define DMOV_ID_TO_ADM(id) ((id) / MSM_DMOV_CHANNEL_COUNT) +#define DMOV_ID_TO_CHAN(id) ((id) % MSM_DMOV_CHANNEL_COUNT) +#define DMOV_CHAN_ADM_TO_ID(ch, adm) ((ch) + (adm) * MSM_DMOV_CHANNEL_COUNT) + +#ifdef CONFIG_MSM_ADM3 +#define DMOV_IRQ_TO_ADM(irq) \ +({ \ + typeof(irq) _irq = irq; \ + ((_irq == INT_ADM1_MASTER) || (_irq == INT_ADM1_AARM)); \ +}) +#else +#define DMOV_IRQ_TO_ADM(irq) 0 +#endif enum { MSM_DMOV_PRINT_ERRORS = 1, @@ -28,11 +209,6 @@ enum { MSM_DMOV_PRINT_FLOW = 4 }; -static DEFINE_SPINLOCK(msm_dmov_lock); -static struct clk *msm_dmov_clk; -static unsigned int channel_active; -static struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT]; -static struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT]; unsigned int msm_dmov_print_mask = MSM_DMOV_PRINT_ERRORS; #define MSM_DMOV_DPRINTF(mask, format, args...) \ @@ -47,48 +223,122 @@ unsigned int msm_dmov_print_mask = MSM_DMOV_PRINT_ERRORS; #define PRINT_FLOW(format, args...) \ MSM_DMOV_DPRINTF(MSM_DMOV_PRINT_FLOW, format, args); -void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful) +static int msm_dmov_clk_toggle(int adm, int on) { - writel((graceful << 31), DMOV_FLUSH0(id)); + int ret = 0; + + if (on) { + ret = clk_enable(dmov_conf[adm].clk); + if (ret) + goto err; + if (dmov_conf[adm].pclk) { + ret = clk_enable(dmov_conf[adm].pclk); + if (ret) { + clk_disable(dmov_conf[adm].clk); + goto err; + } + } + if (dmov_conf[adm].ebiclk) { + ret = clk_enable(dmov_conf[adm].ebiclk); + if (ret) { + if (dmov_conf[adm].pclk) + clk_disable(dmov_conf[adm].pclk); + clk_disable(dmov_conf[adm].clk); + } + } + } else { + clk_disable(dmov_conf[adm].clk); + if (dmov_conf[adm].pclk) + clk_disable(dmov_conf[adm].pclk); + if (dmov_conf[adm].ebiclk) + clk_disable(dmov_conf[adm].ebiclk); + } +err: + return ret; } -void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) +static void msm_dmov_clock_timer(unsigned long adm) +{ + unsigned long irq_flags; + spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) { + BUG_ON(dmov_conf[adm].channel_active); + msm_dmov_clk_toggle(adm, 0); + dmov_conf[adm].clk_ctl = CLK_DIS; + } + spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); +} + +void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) { unsigned long irq_flags; unsigned int status; + int adm = DMOV_ID_TO_ADM(id); + int ch = DMOV_ID_TO_CHAN(id); - spin_lock_irqsave(&msm_dmov_lock, irq_flags); - if (!channel_active) - clk_enable(msm_dmov_clk); - dsb(); - status = readl(DMOV_STATUS(id)); - if (list_empty(&ready_commands[id]) && - (status & DMOV_STATUS_CMD_PTR_RDY)) { -#if 0 - if (list_empty(&active_commands[id])) { - PRINT_FLOW("msm_dmov_enqueue_cmd(%d), enable interrupt\n", id); - writel(DMOV_CONFIG_IRQ_EN, DMOV_CONFIG(id)); - } -#endif - if (cmd->execute_func) - cmd->execute_func(cmd); - PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", id, status); - list_add_tail(&cmd->list, &active_commands[id]); - if (!channel_active) - enable_irq(INT_ADM_AARM); - channel_active |= 1U << id; - writel(cmd->cmdptr, DMOV_CMD_PTR(id)); + spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + if (dmov_conf[adm].clk_ctl == CLK_DIS) { + status = msm_dmov_clk_toggle(adm, 1); + if (status != 0) + goto error; + } else if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) + del_timer(&dmov_conf[adm].timer); + dmov_conf[adm].clk_ctl = CLK_EN; + + status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); + if (status & DMOV_STATUS_CMD_PTR_RDY) { + PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", + id, status); + if (cmd->exec_func) + cmd->exec_func(cmd); + list_add_tail(&cmd->list, &dmov_conf[adm].active_commands[ch]); + if (!dmov_conf[adm].channel_active) + enable_irq(dmov_conf[adm].irq); + dmov_conf[adm].channel_active |= 1U << ch; + PRINT_IO("Writing %x exactly to register", cmd->cmdptr); + writel_relaxed(cmd->cmdptr, DMOV_REG(DMOV_CMD_PTR(ch), adm)); } else { - if (!channel_active) - clk_disable(msm_dmov_clk); - if (list_empty(&active_commands[id])) - PRINT_ERROR("msm_dmov_enqueue_cmd(%d), error datamover stalled, status %x\n", id, status); - - PRINT_IO("msm_dmov_enqueue_cmd(%d), enqueue command, status %x\n", id, status); - list_add_tail(&cmd->list, &ready_commands[id]); + if (!dmov_conf[adm].channel_active) { + dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; + mod_timer(&dmov_conf[adm].timer, jiffies + HZ); + } + if (list_empty(&dmov_conf[adm].active_commands[ch])) + PRINT_ERROR("msm_dmov_enqueue_cmd_ext(%d), stalled, " + "status %x\n", id, status); + PRINT_IO("msm_dmov_enqueue_cmd(%d), enqueue command, status " + "%x\n", id, status); + list_add_tail(&cmd->list, &dmov_conf[adm].ready_commands[ch]); } - spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); +error: + spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); } +EXPORT_SYMBOL(msm_dmov_enqueue_cmd_ext); + +void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) +{ + /* Disable callback function (for backwards compatibility) */ + cmd->exec_func = NULL; + + msm_dmov_enqueue_cmd_ext(id, cmd); +} +EXPORT_SYMBOL(msm_dmov_enqueue_cmd); + +void msm_dmov_flush(unsigned int id, int graceful) +{ + unsigned long irq_flags; + int ch = DMOV_ID_TO_CHAN(id); + int adm = DMOV_ID_TO_ADM(id); + int flush = graceful ? DMOV_FLUSH_TYPE : 0; + spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + /* XXX not checking if flush cmd sent already */ + if (!list_empty(&dmov_conf[adm].active_commands[ch])) { + PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id); + writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm)); + } + /* spin_unlock_irqrestore has the necessary barrier */ + spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); +} +EXPORT_SYMBOL(msm_dmov_flush); struct msm_dmov_exec_cmdptr_cmd { struct msm_dmov_cmd dmov_cmd; @@ -119,12 +369,13 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) cmd.dmov_cmd.cmdptr = cmdptr; cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func; - cmd.dmov_cmd.execute_func = NULL; + cmd.dmov_cmd.exec_func = NULL; cmd.id = id; + cmd.result = 0; init_completion(&cmd.complete); msm_dmov_enqueue_cmd(id, &cmd.dmov_cmd); - wait_for_completion(&cmd.complete); + wait_for_completion_io(&cmd.complete); if (cmd.result != 0x80000002) { PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result); @@ -135,40 +386,61 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) PRINT_FLOW("dmov_exec_cmdptr(%d, %x) done\n", id, cmdptr); return 0; } +EXPORT_SYMBOL(msm_dmov_exec_cmd); +static void fill_errdata(struct msm_dmov_errdata *errdata, int ch, int adm) +{ + errdata->flush[0] = readl_relaxed(DMOV_REG(DMOV_FLUSH0(ch), adm)); + errdata->flush[1] = readl_relaxed(DMOV_REG(DMOV_FLUSH1(ch), adm)); + errdata->flush[2] = 0; + errdata->flush[3] = readl_relaxed(DMOV_REG(DMOV_FLUSH3(ch), adm)); + errdata->flush[4] = readl_relaxed(DMOV_REG(DMOV_FLUSH4(ch), adm)); + errdata->flush[5] = readl_relaxed(DMOV_REG(DMOV_FLUSH5(ch), adm)); +} static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id) { - unsigned int int_status, mask, id; + unsigned int int_status; + unsigned int mask; + unsigned int id; + unsigned int ch; unsigned long irq_flags; unsigned int ch_status; unsigned int ch_result; + unsigned int valid = 0; struct msm_dmov_cmd *cmd; + int adm = DMOV_IRQ_TO_ADM(irq); - spin_lock_irqsave(&msm_dmov_lock, irq_flags); - - int_status = readl(DMOV_ISR); /* read and clear interrupt */ + spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + /* read and clear isr */ + int_status = readl_relaxed(DMOV_REG(DMOV_ISR, adm)); PRINT_FLOW("msm_datamover_irq_handler: DMOV_ISR %x\n", int_status); while (int_status) { mask = int_status & -int_status; - id = fls(mask) - 1; + ch = fls(mask) - 1; + id = DMOV_CHAN_ADM_TO_ID(ch, adm); PRINT_FLOW("msm_datamover_irq_handler %08x %08x id %d\n", int_status, mask, id); int_status &= ~mask; - ch_status = readl(DMOV_STATUS(id)); + ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), adm)); if (!(ch_status & DMOV_STATUS_RSLT_VALID)) { - PRINT_FLOW("msm_datamover_irq_handler id %d, result not valid %x\n", id, ch_status); + PRINT_FLOW("msm_datamover_irq_handler id %d, " + "result not valid %x\n", id, ch_status); continue; } do { - ch_result = readl(DMOV_RSLT(id)); - if (list_empty(&active_commands[id])) { + valid = 1; + ch_result = readl_relaxed(DMOV_REG(DMOV_RSLT(ch), adm)); + if (list_empty(&dmov_conf[adm].active_commands[ch])) { PRINT_ERROR("msm_datamover_irq_handler id %d, got result " "with no active command, status %x, result %x\n", id, ch_status, ch_result); cmd = NULL; - } else - cmd = list_entry(active_commands[id].next, typeof(*cmd), list); + } else { + cmd = list_entry(dmov_conf[adm]. + active_commands[ch].next, typeof(*cmd), + list); + } PRINT_FLOW("msm_datamover_irq_handler id %d, status %x, result %x\n", id, ch_status, ch_result); if (ch_result & DMOV_RSLT_DONE) { PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", @@ -177,95 +449,253 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id) "for %p, result %x\n", id, cmd, ch_result); if (cmd) { list_del(&cmd->list); - dsb(); cmd->complete_func(cmd, ch_result, NULL); } } if (ch_result & DMOV_RSLT_FLUSH) { struct msm_dmov_errdata errdata; - errdata.flush[0] = readl(DMOV_FLUSH0(id)); - errdata.flush[1] = readl(DMOV_FLUSH1(id)); - errdata.flush[2] = readl(DMOV_FLUSH2(id)); - errdata.flush[3] = readl(DMOV_FLUSH3(id)); - errdata.flush[4] = readl(DMOV_FLUSH4(id)); - errdata.flush[5] = readl(DMOV_FLUSH5(id)); + fill_errdata(&errdata, ch, adm); PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); PRINT_FLOW("msm_datamover_irq_handler id %d, flush, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); if (cmd) { list_del(&cmd->list); - dsb(); cmd->complete_func(cmd, ch_result, &errdata); } } if (ch_result & DMOV_RSLT_ERROR) { struct msm_dmov_errdata errdata; - errdata.flush[0] = readl(DMOV_FLUSH0(id)); - errdata.flush[1] = readl(DMOV_FLUSH1(id)); - errdata.flush[2] = readl(DMOV_FLUSH2(id)); - errdata.flush[3] = readl(DMOV_FLUSH3(id)); - errdata.flush[4] = readl(DMOV_FLUSH4(id)); - errdata.flush[5] = readl(DMOV_FLUSH5(id)); + fill_errdata(&errdata, ch, adm); PRINT_ERROR("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); PRINT_ERROR("msm_datamover_irq_handler id %d, error, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]); if (cmd) { list_del(&cmd->list); - dsb(); cmd->complete_func(cmd, ch_result, &errdata); } /* this does not seem to work, once we get an error */ /* the datamover will no longer accept commands */ - writel(0, DMOV_FLUSH0(id)); + writel_relaxed(0, DMOV_REG(DMOV_FLUSH0(ch), + adm)); } - ch_status = readl(DMOV_STATUS(id)); + rmb(); + ch_status = readl_relaxed(DMOV_REG(DMOV_STATUS(ch), + adm)); PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); - if ((ch_status & DMOV_STATUS_CMD_PTR_RDY) && !list_empty(&ready_commands[id])) { - cmd = list_entry(ready_commands[id].next, typeof(*cmd), list); + if ((ch_status & DMOV_STATUS_CMD_PTR_RDY) && + !list_empty(&dmov_conf[adm].ready_commands[ch])) { + cmd = list_entry(dmov_conf[adm]. + ready_commands[ch].next, typeof(*cmd), + list); list_del(&cmd->list); - list_add_tail(&cmd->list, &active_commands[id]); - if (cmd->execute_func) - cmd->execute_func(cmd); - PRINT_FLOW("msm_datamover_irq_handler id %d, start command\n", id); - writel(cmd->cmdptr, DMOV_CMD_PTR(id)); + if (cmd->exec_func) + cmd->exec_func(cmd); + list_add_tail(&cmd->list, + &dmov_conf[adm].active_commands[ch]); + PRINT_FLOW("msm_datamover_irq_handler id %d," + "start command\n", id); + writel_relaxed(cmd->cmdptr, + DMOV_REG(DMOV_CMD_PTR(ch), adm)); } } while (ch_status & DMOV_STATUS_RSLT_VALID); - if (list_empty(&active_commands[id]) && list_empty(&ready_commands[id])) - channel_active &= ~(1U << id); + if (list_empty(&dmov_conf[adm].active_commands[ch]) && + list_empty(&dmov_conf[adm].ready_commands[ch])) + dmov_conf[adm].channel_active &= ~(1U << ch); PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); } - if (!channel_active) { - disable_irq_nosync(INT_ADM_AARM); - clk_disable(msm_dmov_clk); + if (!dmov_conf[adm].channel_active && valid) { + disable_irq_nosync(dmov_conf[adm].irq); + dmov_conf[adm].clk_ctl = CLK_TO_BE_DIS; + mod_timer(&dmov_conf[adm].timer, jiffies + HZ); } - spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); - return IRQ_HANDLED; + spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); + return valid ? IRQ_HANDLED : IRQ_NONE; } -static int __init msm_init_datamover(void) +static int msm_dmov_suspend_late(struct device *dev) { - int i; - int ret; - struct clk *clk; - - for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { - INIT_LIST_HEAD(&ready_commands[i]); - INIT_LIST_HEAD(&active_commands[i]); - writel(DMOV_CONFIG_IRQ_EN | DMOV_CONFIG_FORCE_TOP_PTR_RSLT | DMOV_CONFIG_FORCE_FLUSH_RSLT, DMOV_CONFIG(i)); + unsigned long irq_flags; + struct platform_device *pdev = to_platform_device(dev); + int adm = (pdev->id >= 0) ? pdev->id : 0; + spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags); + if (dmov_conf[adm].clk_ctl == CLK_TO_BE_DIS) { + BUG_ON(dmov_conf[adm].channel_active); + del_timer(&dmov_conf[adm].timer); + msm_dmov_clk_toggle(adm, 0); + dmov_conf[adm].clk_ctl = CLK_DIS; } - clk = clk_get(NULL, "adm_clk"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - msm_dmov_clk = clk; - ret = request_irq(INT_ADM_AARM, msm_datamover_irq_handler, 0, "msmdatamover", NULL); - if (ret) - return ret; - disable_irq(INT_ADM_AARM); + spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags); return 0; } -arch_initcall(msm_init_datamover); +static int msm_dmov_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} +static int msm_dmov_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static int msm_dmov_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: idling...\n"); + return 0; +} + +static struct dev_pm_ops msm_dmov_dev_pm_ops = { + .runtime_suspend = msm_dmov_runtime_suspend, + .runtime_resume = msm_dmov_runtime_resume, + .runtime_idle = msm_dmov_runtime_idle, + .suspend = msm_dmov_suspend_late, +}; + +static int msm_dmov_init_clocks(struct platform_device *pdev) +{ + int adm = (pdev->id >= 0) ? pdev->id : 0; + int ret; + + dmov_conf[adm].clk = clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(dmov_conf[adm].clk)) { + printk(KERN_ERR "%s: Error getting adm_clk\n", __func__); + dmov_conf[adm].clk = NULL; + return -ENOENT; + } + + dmov_conf[adm].pclk = clk_get(&pdev->dev, "iface_clk"); + if (IS_ERR(dmov_conf[adm].pclk)) { + dmov_conf[adm].pclk = NULL; + /* pclk not present on all SoCs, don't bail on failure */ + } + + dmov_conf[adm].ebiclk = clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(dmov_conf[adm].ebiclk)) { + dmov_conf[adm].ebiclk = NULL; + /* ebiclk not present on all SoCs, don't bail on failure */ + } else { + ret = clk_set_rate(dmov_conf[adm].ebiclk, 27000000); + if (ret) + return -ENOENT; + } + + return 0; +} + +static void config_datamover(int adm) +{ +#ifdef CONFIG_MSM_ADM3 + int i; + for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { + struct msm_dmov_chan_conf *chan_conf = + dmov_conf[adm].chan_conf; + unsigned conf; + /* Only configure scorpion channels */ + if (chan_conf[i].sd <= 1) { + conf = readl_relaxed(DMOV_REG(DMOV_CONF(i), adm)); + conf &= ~DMOV_CONF_SD(7); + conf |= DMOV_CONF_SD(chan_conf[i].sd); + writel_relaxed(conf | DMOV_CONF_SHADOW_EN, + DMOV_REG(DMOV_CONF(i), adm)); + } + } + for (i = 0; i < MSM_DMOV_CRCI_COUNT; i++) { + struct msm_dmov_crci_conf *crci_conf = + dmov_conf[adm].crci_conf; + + writel_relaxed(DMOV_CRCI_CTL_BLK_SZ(crci_conf[i].blk_size), + DMOV_REG(DMOV_CRCI_CTL(i), adm)); + } +#endif +} + +static int msm_dmov_probe(struct platform_device *pdev) +{ + int adm = (pdev->id >= 0) ? pdev->id : 0; + int i; + int ret; + struct msm_dmov_pdata *pdata = pdev->dev.platform_data; + struct resource *irqres = + platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct resource *mres = + platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (pdata) { + dmov_conf[adm].sd = pdata->sd; + dmov_conf[adm].sd_size = pdata->sd_size; + } + if (!dmov_conf[adm].sd_size) + return -ENXIO; + + if (!irqres || !irqres->start) + return -ENXIO; + dmov_conf[adm].irq = irqres->start; + + if (!mres || !mres->start) + return -ENXIO; + dmov_conf[adm].base = ioremap_nocache(mres->start, resource_size(mres)); + if (!dmov_conf[adm].base) + return -ENOMEM; + + ret = request_irq(dmov_conf[adm].irq, msm_datamover_irq_handler, + 0, "msmdatamover", NULL); + if (ret) { + PRINT_ERROR("Requesting ADM%d irq %d failed\n", adm, + dmov_conf[adm].irq); + goto out_map; + } + disable_irq(dmov_conf[adm].irq); + ret = msm_dmov_init_clocks(pdev); + if (ret) { + PRINT_ERROR("Requesting ADM%d clocks failed\n", adm); + goto out_irq; + } + ret = msm_dmov_clk_toggle(adm, 1); + if (ret) { + PRINT_ERROR("Enabling ADM%d clocks failed\n", adm); + goto out_irq; + } + + config_datamover(adm); + for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { + INIT_LIST_HEAD(&dmov_conf[adm].ready_commands[i]); + INIT_LIST_HEAD(&dmov_conf[adm].active_commands[i]); + + writel_relaxed(DMOV_RSLT_CONF_IRQ_EN + | DMOV_RSLT_CONF_FORCE_FLUSH_RSLT, + DMOV_REG(DMOV_RSLT_CONF(i), adm)); + } + wmb(); + msm_dmov_clk_toggle(adm, 0); + return ret; +out_irq: + free_irq(dmov_conf[adm].irq, NULL); +out_map: + iounmap(dmov_conf[adm].base); + return ret; +} + +static struct platform_driver msm_dmov_driver = { + .probe = msm_dmov_probe, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .pm = &msm_dmov_dev_pm_ops, + }, +}; + +/* static int __init */ +static int __init msm_init_datamover(void) +{ + int ret; + ret = platform_driver_register(&msm_dmov_driver); + if (ret) + return ret; + return 0; +} +arch_initcall(msm_init_datamover); diff --git a/arch/arm/mach-msm/dma_test.c b/arch/arm/mach-msm/dma_test.c new file mode 100644 index 00000000000..de1ee0abf77 --- /dev/null +++ b/arch/arm/mach-msm/dma_test.c @@ -0,0 +1,360 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/********************************************************************** + * User-space testing of the DMA driver. + * Intended to be loaded as a module. We have a bunch of static + * buffers that the user-side can refer to. The main DMA is simply + * used memory-to-memory. Device DMA is best tested with the specific + * device driver in question. + */ +#define MAX_TEST_BUFFERS 40 +#define MAX_TEST_BUFFER_SIZE 65536 +static void *(buffers[MAX_TEST_BUFFERS]); +static int sizes[MAX_TEST_BUFFERS]; + +/* Anything that allocates or deallocates buffers must lock with this + * mutex. */ +static DEFINE_SEMAPHORE(buffer_lock); + +/* Each buffer has a semaphore associated with it that will be held + * for the duration of any operations on that buffer. It also must be + * available to free the given buffer. */ +static struct semaphore buffer_sems[MAX_TEST_BUFFERS]; + +#define buffer_up(num) up(&buffer_sems[num]) +#define buffer_down(num) down(&buffer_sems[num]) + +/* Use the General Purpose DMA channel as our test channel. This channel + * should be available on any target. */ +#define TEST_CHANNEL DMOV_GP_CHAN + +struct private { + /* Each open instance is allowed a single pending + * operation. */ + struct semaphore sem; + + /* Simple command buffer. Allocated and freed by driver. */ + /* TODO: Allocate these together. */ + dmov_s *command_ptr; + + /* Indirect. */ + u32 *command_ptr_ptr; + + /* Indicates completion with pending request. */ + struct completion complete; +}; + +static void free_buffers(void) +{ + int i; + + for (i = 0; i < MAX_TEST_BUFFERS; i++) { + if (sizes[i] > 0) { + kfree(buffers[i]); + sizes[i] = 0; + } + } +} + +/* Copy between two buffers, using the DMA. */ + +/* Allocate a buffer of a requested size. */ +static int buffer_req(struct msm_dma_alloc_req *req) +{ + int i; + + if (req->size <= 0 || req->size > MAX_TEST_BUFFER_SIZE) + return -EINVAL; + + down(&buffer_lock); + + /* Find a free buffer. */ + for (i = 0; i < MAX_TEST_BUFFERS; i++) + if (sizes[i] == 0) + break; + + if (i >= MAX_TEST_BUFFERS) + goto error; + + buffers[i] = kmalloc(req->size, GFP_KERNEL | __GFP_DMA); + if (buffers[i] == 0) + goto error; + sizes[i] = req->size; + + req->bufnum = i; + + up(&buffer_lock); + return 0; + +error: + up(&buffer_lock); + return -ENOSPC; +} + +static int dma_scopy(struct msm_dma_scopy *scopy, struct private *priv) +{ + int err = 0; + dma_addr_t mapped_cmd; + dma_addr_t mapped_cmd_ptr; + + buffer_down(scopy->srcbuf); + if (scopy->srcbuf != scopy->destbuf) + buffer_down(scopy->destbuf); + + priv->command_ptr->cmd = CMD_PTR_LP | CMD_MODE_SINGLE; + priv->command_ptr->src = dma_map_single(NULL, buffers[scopy->srcbuf], + scopy->size, DMA_TO_DEVICE); + priv->command_ptr->dst = dma_map_single(NULL, buffers[scopy->destbuf], + scopy->size, DMA_FROM_DEVICE); + priv->command_ptr->len = scopy->size; + + mapped_cmd = + dma_map_single(NULL, priv->command_ptr, sizeof(*priv->command_ptr), + DMA_TO_DEVICE); + *(priv->command_ptr_ptr) = CMD_PTR_ADDR(mapped_cmd) | CMD_PTR_LP; + + mapped_cmd_ptr = dma_map_single(NULL, priv->command_ptr_ptr, + sizeof(*priv->command_ptr_ptr), + DMA_TO_DEVICE); + + msm_dmov_exec_cmd(TEST_CHANNEL, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(mapped_cmd_ptr)); + + dma_unmap_single(NULL, (dma_addr_t) mapped_cmd_ptr, + sizeof(*priv->command_ptr_ptr), DMA_TO_DEVICE); + dma_unmap_single(NULL, (dma_addr_t) mapped_cmd, + sizeof(*priv->command_ptr), DMA_TO_DEVICE); + dma_unmap_single(NULL, (dma_addr_t) priv->command_ptr->dst, + scopy->size, DMA_FROM_DEVICE); + dma_unmap_single(NULL, (dma_addr_t) priv->command_ptr->src, + scopy->size, DMA_TO_DEVICE); + + if (scopy->srcbuf != scopy->destbuf) + buffer_up(scopy->destbuf); + buffer_up(scopy->srcbuf); + + return err; +} + +static int dma_test_open(struct inode *inode, struct file *file) +{ + struct private *priv; + + printk(KERN_ALERT "%s\n", __func__); + + priv = kmalloc(sizeof(struct private), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + file->private_data = priv; + + sema_init(&priv->sem, 1); + + /* Note, that these should be allocated together so we don't + * waste 32 bytes for each. */ + + /* Allocate the command pointer. */ + priv->command_ptr = kmalloc(sizeof(&priv->command_ptr), + GFP_KERNEL | __GFP_DMA); + if (priv->command_ptr == NULL) { + kfree(priv); + return -ENOSPC; + } + + /* And the indirect pointer. */ + priv->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); + if (priv->command_ptr_ptr == NULL) { + kfree(priv->command_ptr); + kfree(priv); + return -ENOSPC; + } + + return 0; +} + +static int dma_test_release(struct inode *inode, struct file *file) +{ + struct private *priv; + + printk(KERN_ALERT "%s\n", __func__); + + if (file->private_data != NULL) { + priv = file->private_data; + kfree(priv->command_ptr_ptr); + kfree(priv->command_ptr); + } + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static long dma_test_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + int err = 0; + int tmp; + struct msm_dma_alloc_req alloc_req; + struct msm_dma_bufxfer xfer; + struct msm_dma_scopy scopy; + struct private *priv = file->private_data; + + /* Verify user arguments. */ + if (_IOC_TYPE(cmd) != MSM_DMA_IOC_MAGIC) + return -ENOTTY; + + switch (cmd) { + case MSM_DMA_IOALLOC: + if (!access_ok(VERIFY_WRITE, (void __user *)arg, + sizeof(alloc_req))) + return -EFAULT; + if (__copy_from_user(&alloc_req, (void __user *)arg, + sizeof(alloc_req))) + return -EFAULT; + err = buffer_req(&alloc_req); + if (err < 0) + return err; + if (__copy_to_user((void __user *)arg, &alloc_req, + sizeof(alloc_req))) + return -EFAULT; + break; + + case MSM_DMA_IOFREEALL: + down(&buffer_lock); + for (tmp = 0; tmp < MAX_TEST_BUFFERS; tmp++) { + buffer_down(tmp); + if (sizes[tmp] > 0) { + kfree(buffers[tmp]); + sizes[tmp] = 0; + } + buffer_up(tmp); + } + up(&buffer_lock); + break; + + case MSM_DMA_IOWBUF: + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) + return -EFAULT; + if (xfer.bufnum < 0 || xfer.bufnum >= MAX_TEST_BUFFERS) + return -EINVAL; + buffer_down(xfer.bufnum); + if (sizes[xfer.bufnum] == 0 || + xfer.size <= 0 || xfer.size > sizes[xfer.bufnum]) { + buffer_up(xfer.bufnum); + return -EINVAL; + } + if (copy_from_user(buffers[xfer.bufnum], + (void __user *)xfer.data, xfer.size)) + err = -EFAULT; + buffer_up(xfer.bufnum); + break; + + case MSM_DMA_IORBUF: + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) + return -EFAULT; + if (xfer.bufnum < 0 || xfer.bufnum >= MAX_TEST_BUFFERS) + return -EINVAL; + buffer_down(xfer.bufnum); + if (sizes[xfer.bufnum] == 0 || + xfer.size <= 0 || xfer.size > sizes[xfer.bufnum]) { + buffer_up(xfer.bufnum); + return -EINVAL; + } + if (copy_to_user((void __user *)xfer.data, buffers[xfer.bufnum], + xfer.size)) + err = -EFAULT; + buffer_up(xfer.bufnum); + break; + + case MSM_DMA_IOSCOPY: + if (copy_from_user(&scopy, (void __user *)arg, sizeof(scopy))) + return -EFAULT; + if (scopy.srcbuf < 0 || scopy.srcbuf >= MAX_TEST_BUFFERS || + sizes[scopy.srcbuf] == 0 || + scopy.destbuf < 0 || scopy.destbuf >= MAX_TEST_BUFFERS || + sizes[scopy.destbuf] == 0 || + scopy.size > sizes[scopy.destbuf] || + scopy.size > sizes[scopy.srcbuf]) + return -EINVAL; +#if 0 + /* Test interface using memcpy. */ + memcpy(buffers[scopy.destbuf], + buffers[scopy.srcbuf], scopy.size); +#else + err = dma_scopy(&scopy, priv); +#endif + break; + + default: + return -ENOTTY; + } + + return err; +} + +/********************************************************************** + * Register ourselves as a misc device to be able to test the DMA code + * from userspace. */ + +static const struct file_operations dma_test_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dma_test_ioctl, + .open = dma_test_open, + .release = dma_test_release, +}; + +static struct miscdevice dma_test_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msmdma", + .fops = &dma_test_fops, +}; +static int dma_test_init(void) +{ + int ret, i; + + ret = misc_register(&dma_test_dev); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_TEST_BUFFERS; i++) + sema_init(&buffer_sems[i], 1); + + printk(KERN_ALERT "%s, minor number %d\n", __func__, dma_test_dev.minor); + return 0; +} + +static void dma_test_exit(void) +{ + free_buffers(); + misc_deregister(&dma_test_dev); + printk(KERN_ALERT "%s\n", __func__); +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("David Brown, Qualcomm, Incorporated"); +MODULE_DESCRIPTION("Test for MSM DMA driver"); +MODULE_VERSION("1.01"); + +module_init(dma_test_init); +module_exit(dma_test_exit); diff --git a/arch/arm/mach-msm/etm.c b/arch/arm/mach-msm/etm.c new file mode 100644 index 00000000000..6cceff23a21 --- /dev/null +++ b/arch/arm/mach-msm/etm.c @@ -0,0 +1,1037 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cp14.h" + +#define LOG_BUF_LEN 32768 +/* each slot is 4 bytes, 8kb total */ +#define ETB_RAM_SLOTS 2048 + +#define DATALOG_SYNC 0xB5C7 +#define ETM_DUMP_MSG_ID 0x000A6960 +#define ETB_DUMP_MSG_ID 0x000A6961 + +/* ETB Registers */ +#define ETB_REG_CONTROL ETMIMPSPEC1 +#define ETB_REG_STATUS ETMIMPSPEC2 +#define ETB_REG_COUNT ETMIMPSPEC3 +#define ETB_REG_ADDRESS ETMIMPSPEC4 +#define ETB_REG_DATA ETMIMPSPEC5 + +/* Having etb macro accessors allows macro expansion for ETB reg defines */ +#define etb_read(reg) etm_read(reg) +#define etb_write(val, reg) etm_write(val, reg) + +/* Bitmasks for the ETM control register */ +#define ETM_CONTROL_POWERDOWN 0x00000001 +#define ETM_CONTROL_PROGRAM 0x00000400 + +/* Bitmasks for the ETM status register */ +#define ETM_STATUS_PROGRAMMING 0x00000002 + +/* ETB Status Register bit definitions */ +#define OV 0x00200000 + +/* ETB Control Register bit definitions */ +#define AIR 0x00000008 +#define AIW 0x00000004 +#define CPTM 0x00000002 +#define CPTEN 0x00000001 + +/* Bitmasks for the swconfig field of ETM_CONFIG + * ETM trigger propagated to ETM instances on all cores + */ +#define TRIGGER_ALL 0x00000002 + +#define PROG_TIMEOUT_MS 500 + +static int trace_enabled; +static int cpu_to_dump; +static int next_cpu_to_dump; +static struct wake_lock etm_wake_lock; +static struct pm_qos_request etm_qos_req; +static int trace_on_boot; +module_param_named( + trace_on_boot, trace_on_boot, int, S_IRUGO +); + +struct b { + uint8_t etm_log_buf[LOG_BUF_LEN]; + uint32_t log_end; +}; + +static struct b buf[NR_CPUS]; +static struct b __percpu * *alloc_b; +static atomic_t etm_dev_in_use; + +/* These default settings will be used to configure the ETM/ETB + * when the driver loads. */ +struct etm_config_struct { + uint32_t etm_00_control; + uint32_t etm_02_trigger_event; + uint32_t etm_06_te_start_stop; + uint32_t etm_07_te_single_addr_comp; + uint32_t etm_08_te_event; + uint32_t etm_09_te_control; + uint32_t etm_0a_fifofull_region; + uint32_t etm_0b_fifofull_level; + uint32_t etm_0c_vd_event; + uint32_t etm_0d_vd_single_addr_comp; + uint32_t etm_0e_vd_mmd; + uint32_t etm_0f_vd_control; + uint32_t etm_addr_comp_value[8]; /* 10 to 17 */ + uint32_t etm_addr_access_type[8]; /* 20 to 27 */ + uint32_t etm_data_comp_value[2]; /* 30 and 32 */ + uint32_t etm_data_comp_mask[2]; /* 40 and 42 */ + uint32_t etm_counter_reload_value[2]; /* 50 to 51 */ + uint32_t etm_counter_enable[2]; /* 54 to 55 */ + uint32_t etm_counter_reload_event[2]; /* 58 to 59 */ + uint32_t etm_60_seq_event_1_to_2; + uint32_t etm_61_seq_event_2_to_1; + uint32_t etm_62_seq_event_2_to_3; + uint32_t etm_63_seq_event_3_to_1; + uint32_t etm_64_seq_event_3_to_2; + uint32_t etm_65_seq_event_1_to_3; + uint32_t etm_6c_cid_comp_value_1; + uint32_t etm_6f_cid_comp_mask; + uint32_t etm_78_sync_freq; + uint32_t swconfig; + uint32_t etb_trig_cnt; + uint32_t etb_init_ptr; +}; + +static struct etm_config_struct etm_config = { + /* etm_00_control 0x0000D84E: 32-bit CID, cycle-accurate, + * monitorCPRT */ + .etm_00_control = 0x0000D84E, + /* etm_02_trigger_event 0x00000000: address comparator 0 matches */ + .etm_02_trigger_event = 0x00000000, + .etm_06_te_start_stop = 0x00000000, + .etm_07_te_single_addr_comp = 0x00000000, + /* etm_08_te_event 0x0000006F: always true */ + .etm_08_te_event = 0x0000006F, + /* etm_09_te_control 0x01000000: exclude none */ + .etm_09_te_control = 0x01000000, + .etm_0a_fifofull_region = 0x00000000, + .etm_0b_fifofull_level = 0x00000000, + /* etm_0c_vd_event 0x0000006F: always true */ + .etm_0c_vd_event = 0x0000006F, + .etm_0d_vd_single_addr_comp = 0x00000000, + .etm_0e_vd_mmd = 0x00000000, + /* etm_0f_vd_control 0x00010000: exclude none */ + .etm_0f_vd_control = 0x00010000, + .etm_addr_comp_value[0] = 0x00000000, + .etm_addr_comp_value[1] = 0x00000000, + .etm_addr_comp_value[2] = 0x00000000, + .etm_addr_comp_value[3] = 0x00000000, + .etm_addr_comp_value[4] = 0x00000000, + .etm_addr_comp_value[5] = 0x00000000, + .etm_addr_comp_value[6] = 0x00000000, + .etm_addr_comp_value[7] = 0x00000000, + .etm_addr_access_type[0] = 0x00000000, + .etm_addr_access_type[1] = 0x00000000, + .etm_addr_access_type[2] = 0x00000000, + .etm_addr_access_type[3] = 0x00000000, + .etm_addr_access_type[4] = 0x00000000, + .etm_addr_access_type[5] = 0x00000000, + .etm_addr_access_type[6] = 0x00000000, + .etm_addr_access_type[7] = 0x00000000, + .etm_data_comp_value[0] = 0x00000000, + .etm_data_comp_value[1] = 0x00000000, + .etm_data_comp_mask[0] = 0x00000000, + .etm_data_comp_mask[1] = 0x00000000, + .etm_counter_reload_value[0] = 0x00000000, + .etm_counter_reload_value[1] = 0x00000000, + .etm_counter_enable[0] = 0x0002406F, + .etm_counter_enable[1] = 0x0002406F, + .etm_counter_reload_event[0] = 0x0000406F, + .etm_counter_reload_event[1] = 0x0000406F, + .etm_60_seq_event_1_to_2 = 0x0000406F, + .etm_61_seq_event_2_to_1 = 0x0000406F, + .etm_62_seq_event_2_to_3 = 0x0000406F, + .etm_63_seq_event_3_to_1 = 0x0000406F, + .etm_64_seq_event_3_to_2 = 0x0000406F, + .etm_65_seq_event_1_to_3 = 0x0000406F, + .etm_6c_cid_comp_value_1 = 0x00000000, + .etm_6f_cid_comp_mask = 0x00000000, + .etm_78_sync_freq = 0x00000400, + .swconfig = 0x00000002, + /* etb_trig_cnt 0x00000000: ignore trigger */ + .etb_trig_cnt = 0x00000000, + /* etb_init_ptr 0x00000010: 16 marker bytes */ + .etb_init_ptr = 0x00000010, +}; + +/* ETM clock is derived from the processor clock and gets enabled on a + * logical OR of below items on Scorpion: + * 1.CPMR[ETMCLKEN] is set + * 2.ETM is not idle. Also means ETMCR[PD] is 0 + * 3.Reset is asserted (core or debug) + * 4.MRC/MCR to ETM reg (CP14 access) + * 5.Debugger access to a ETM register in the core power domain + * + * 1. and 2. above are permanent enables whereas 3., 4. and 5. are + * temporary enables + * + * We rely on 4. to be able to access ETMCR and then use 2. above for ETM + * clock vote in the driver and the save-restore code uses 1. above + * for its vote. + */ +static inline void __cpu_set_etm_pwrdwn(void) +{ + uint32_t etm_control; + + isb(); + etm_control = etm_read(ETMCR); + etm_control |= ETM_CONTROL_POWERDOWN; + etm_write(etm_control, ETMCR); +} + +static inline void __cpu_clear_etm_pwrdwn(void) +{ + uint32_t etm_control; + + etm_control = etm_read(ETMCR); + etm_control &= ~ETM_CONTROL_POWERDOWN; + etm_write(etm_control, ETMCR); + isb(); +} + +static void emit_log_char(uint8_t c) +{ + int this_cpu = get_cpu(); + struct b *mybuf = *per_cpu_ptr(alloc_b, this_cpu); + char *log_buf = mybuf->etm_log_buf; + int index = (mybuf->log_end)++ & (LOG_BUF_LEN - 1); + log_buf[index] = c; + put_cpu(); +} + +static void emit_log_word(uint32_t word) +{ + emit_log_char(word >> 24); + emit_log_char(word >> 16); + emit_log_char(word >> 8); + emit_log_char(word >> 0); +} + +static void __cpu_enable_etb(void) +{ + uint32_t etb_control; + uint32_t i; + + /* enable auto-increment on reads and writes */ + etb_control = AIR | AIW; + etb_write(etb_control, ETB_REG_CONTROL); + + /* write tags to the slots before the write pointer so we can + * detect overflow */ + etb_write(0x00000000, ETB_REG_ADDRESS); + for (i = 0; i < (etm_config.etb_init_ptr >> 2); i++) + etb_write(0xDEADBEEF, ETB_REG_DATA); + + etb_write(0x00000000, ETB_REG_STATUS); + + /* initialize write pointer */ + etb_write(etm_config.etb_init_ptr, ETB_REG_ADDRESS); + + /* multiple of 16 */ + etb_write(etm_config.etb_trig_cnt & 0xFFFFFFF0, ETB_REG_COUNT); + + /* Enable ETB and enable the trigger counter as appropriate. A + * trigger count of 0 will be used to signify that the user wants to + * ignore the trigger (just keep writing to the ETB and overwriting + * the oldest data). For "trace before trigger" captures the user + * should set the trigger count to a small number. */ + + etb_control |= CPTEN; + if (etm_config.etb_trig_cnt) + etb_control |= CPTM; + etb_write(etb_control, ETB_REG_CONTROL); +} + +static void __cpu_disable_etb(void) +{ + uint32_t etb_control; + etb_control = etb_read(ETB_REG_CONTROL); + etb_control &= ~CPTEN; + etb_write(etb_control, ETB_REG_CONTROL); +} + +static void __cpu_enable_etm(void) +{ + uint32_t etm_control; + unsigned long timeout = jiffies + msecs_to_jiffies(PROG_TIMEOUT_MS); + + etm_control = etm_read(ETMCR); + etm_control &= ~ETM_CONTROL_PROGRAM; + etm_write(etm_control, ETMCR); + + while ((etm_read(ETMSR) & ETM_STATUS_PROGRAMMING) == 1) { + cpu_relax(); + if (time_after(jiffies, timeout)) { + pr_err("etm: timeout while clearing prog bit\n"); + break; + } + } +} + +static void __cpu_disable_etm(void) +{ + uint32_t etm_control; + unsigned long timeout = jiffies + msecs_to_jiffies(PROG_TIMEOUT_MS); + + etm_control = etm_read(ETMCR); + etm_control |= ETM_CONTROL_PROGRAM; + etm_write(etm_control, ETMCR); + + while ((etm_read(ETMSR) & ETM_STATUS_PROGRAMMING) == 0) { + cpu_relax(); + if (time_after(jiffies, timeout)) { + pr_err("etm: timeout while setting prog bit\n"); + break; + } + } +} + +static void __cpu_enable_trace(void *unused) +{ + uint32_t etm_control; + uint32_t etm_trigger; + uint32_t etm_external_output; + + get_cpu(); + + __cpu_disable_etb(); + /* vote for ETM power/clock enable */ + __cpu_clear_etm_pwrdwn(); + __cpu_disable_etm(); + + etm_control = (etm_config.etm_00_control & ~ETM_CONTROL_POWERDOWN) + | ETM_CONTROL_PROGRAM; + etm_write(etm_control, ETMCR); + + etm_trigger = etm_config.etm_02_trigger_event; + etm_external_output = 0x406F; /* always FALSE */ + + if (etm_config.swconfig & TRIGGER_ALL) { + uint32_t function = 0x5; /* A OR B */ + uint32_t resource_b = 0x60; /* external input 1 */ + + etm_trigger &= 0x7F; /* keep resource A, clear function and + * resource B */ + etm_trigger |= (function << 14); + etm_trigger |= (resource_b << 7); + etm_external_output = etm_trigger; + } + + etm_write(etm_trigger, ETMTRIGGER); + etm_write(etm_config.etm_06_te_start_stop, ETMTSSCR); + etm_write(etm_config.etm_07_te_single_addr_comp, ETMTECR2); + etm_write(etm_config.etm_08_te_event, ETMTEEVR); + etm_write(etm_config.etm_09_te_control, ETMTECR1); + etm_write(etm_config.etm_0a_fifofull_region, ETMFFRR); + etm_write(etm_config.etm_0b_fifofull_level, ETMFFLR); + etm_write(etm_config.etm_0c_vd_event, ETMVDEVR); + etm_write(etm_config.etm_0d_vd_single_addr_comp, ETMVDCR1); + etm_write(etm_config.etm_0e_vd_mmd, ETMVDCR2); + etm_write(etm_config.etm_0f_vd_control, ETMVDCR3); + etm_write(etm_config.etm_addr_comp_value[0], ETMACVR0); + etm_write(etm_config.etm_addr_comp_value[1], ETMACVR1); + etm_write(etm_config.etm_addr_comp_value[2], ETMACVR2); + etm_write(etm_config.etm_addr_comp_value[3], ETMACVR3); + etm_write(etm_config.etm_addr_comp_value[4], ETMACVR4); + etm_write(etm_config.etm_addr_comp_value[5], ETMACVR5); + etm_write(etm_config.etm_addr_comp_value[6], ETMACVR6); + etm_write(etm_config.etm_addr_comp_value[7], ETMACVR7); + etm_write(etm_config.etm_addr_access_type[0], ETMACTR0); + etm_write(etm_config.etm_addr_access_type[1], ETMACTR1); + etm_write(etm_config.etm_addr_access_type[2], ETMACTR2); + etm_write(etm_config.etm_addr_access_type[3], ETMACTR3); + etm_write(etm_config.etm_addr_access_type[4], ETMACTR4); + etm_write(etm_config.etm_addr_access_type[5], ETMACTR5); + etm_write(etm_config.etm_addr_access_type[6], ETMACTR6); + etm_write(etm_config.etm_addr_access_type[7], ETMACTR7); + etm_write(etm_config.etm_data_comp_value[0], ETMDCVR0); + etm_write(etm_config.etm_data_comp_value[1], ETMDCVR2); + etm_write(etm_config.etm_data_comp_mask[0], ETMDCMR0); + etm_write(etm_config.etm_data_comp_mask[1], ETMDCMR2); + etm_write(etm_config.etm_counter_reload_value[0], ETMCNTRLDVR0); + etm_write(etm_config.etm_counter_reload_value[1], ETMCNTRLDVR1); + etm_write(etm_config.etm_counter_enable[0], ETMCNTENR0); + etm_write(etm_config.etm_counter_enable[1], ETMCNTENR1); + etm_write(etm_config.etm_counter_reload_event[0], ETMCNTRLDEVR0); + etm_write(etm_config.etm_counter_reload_event[1], ETMCNTRLDEVR1); + etm_write(etm_config.etm_60_seq_event_1_to_2, ETMSQ12EVR); + etm_write(etm_config.etm_61_seq_event_2_to_1, ETMSQ21EVR); + etm_write(etm_config.etm_62_seq_event_2_to_3, ETMSQ23EVR); + etm_write(etm_config.etm_63_seq_event_3_to_1, ETMSQ31EVR); + etm_write(etm_config.etm_64_seq_event_3_to_2, ETMSQ32EVR); + etm_write(etm_config.etm_65_seq_event_1_to_3, ETMSQ13EVR); + etm_write(etm_external_output, ETMEXTOUTEVR0); + etm_write(etm_config.etm_6c_cid_comp_value_1, ETMCIDCVR0); + etm_write(etm_config.etm_6f_cid_comp_mask, ETMCIDCMR); + etm_write(etm_config.etm_78_sync_freq, ETMSYNCFR); + + /* Note that we must enable the ETB before we enable the ETM if we + * want to capture the "always true" trigger event. */ + + __cpu_enable_etb(); + __cpu_enable_etm(); + + put_cpu(); +} + +static void __cpu_disable_trace(void *unused) +{ + get_cpu(); + + __cpu_disable_etm(); + + /* program trace enable to be low by using always false event */ + etm_write(0x6F | BIT(14), ETMTEEVR); + /* vote for ETM power/clock disable */ + __cpu_set_etm_pwrdwn(); + + __cpu_disable_etb(); + + put_cpu(); +} + +static void enable_trace(void) +{ + wake_lock(&etm_wake_lock); + pm_qos_update_request(&etm_qos_req, 0); + + if (etm_config.swconfig & TRIGGER_ALL) { + /* This register is accessible from either core. + * CPU1_extout[0] -> CPU0_extin[0] + * CPU_extout[0] -> CPU1_extin[0] */ + asm volatile("mcr p15, 3, %0, c15, c5, 2" : : "r" (0x1)); + asm volatile("isb"); + } + + get_cpu(); + __cpu_enable_trace(NULL); + smp_call_function(__cpu_enable_trace, NULL, 1); + put_cpu(); + + /* 1. causes all online cpus to come out of idle PC + * 2. prevents idle PC until save restore flag is enabled atomically + * + * we rely on the user to prevent hotplug on/off racing with this + * operation and to ensure cores where trace is expected to be turned + * on are already hotplugged on + */ + trace_enabled = 1; + + pm_qos_update_request(&etm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&etm_wake_lock); +} + +static void disable_trace(void) +{ + wake_lock(&etm_wake_lock); + pm_qos_update_request(&etm_qos_req, 0); + + get_cpu(); + __cpu_disable_trace(NULL); + smp_call_function(__cpu_disable_trace, NULL, 1); + put_cpu(); + + /* 1. causes all online cpus to come out of idle PC + * 2. prevents idle PC until save restore flag is disabled atomically + * + * we rely on the user to prevent hotplug on/off racing with this + * operation and to ensure cores where trace is expected to be turned + * off are already hotplugged on + */ + trace_enabled = 0; + + cpu_to_dump = next_cpu_to_dump = 0; + + pm_qos_update_request(&etm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&etm_wake_lock); +} + +static void generate_etb_dump(void) +{ + uint32_t i; + uint32_t full_slots; + uint32_t etb_control; + uint32_t prim_len; + uint32_t uptime = 0; + + etb_control = etb_read(ETB_REG_CONTROL); + etb_control |= AIR; + etb_write(etb_control, ETB_REG_CONTROL); + + if (etb_read(ETB_REG_STATUS) & OV) + full_slots = ETB_RAM_SLOTS; + else + full_slots = etb_read(ETB_REG_ADDRESS) >> 2; + + prim_len = 28 + (full_slots * 4); + + emit_log_char((DATALOG_SYNC >> 8) & 0xFF); + emit_log_char((DATALOG_SYNC >> 0) & 0xFF); + emit_log_char((prim_len >> 8) & 0xFF); + emit_log_char((prim_len >> 0) & 0xFF); + emit_log_word(uptime); + emit_log_word(ETB_DUMP_MSG_ID); + emit_log_word(etm_read(ETMCR)); + emit_log_word(etm_config.etb_init_ptr >> 2); + emit_log_word(etb_read(ETB_REG_ADDRESS) >> 2); + emit_log_word((etb_read(ETB_REG_STATUS) & OV) >> 21); + + etb_write(0x00000000, ETB_REG_ADDRESS); + for (i = 0; i < full_slots; i++) + emit_log_word(etb_read(ETB_REG_DATA)); +} + +/* This should match the number of ETM registers being dumped below */ +#define ETM_NUM_REGS_TO_DUMP 54 +static void generate_etm_dump(void) +{ + uint32_t prim_len; + uint32_t uptime = 0; + + prim_len = 12 + (4 * ETM_NUM_REGS_TO_DUMP); + + emit_log_char((DATALOG_SYNC >> 8) & 0xFF); + emit_log_char((DATALOG_SYNC >> 0) & 0xFF); + emit_log_char((prim_len >> 8) & 0xFF); + emit_log_char((prim_len >> 0) & 0xFF); + emit_log_word(uptime); + emit_log_word(ETM_DUMP_MSG_ID); + + emit_log_word(etm_read(ETMCR)); + emit_log_word(etm_read(ETMSR)); + emit_log_word(etb_read(ETB_REG_CONTROL)); + emit_log_word(etb_read(ETB_REG_STATUS)); + emit_log_word(etb_read(ETB_REG_COUNT)); + emit_log_word(etb_read(ETB_REG_ADDRESS)); + emit_log_word(0); /* don't read ETB_REG_DATA, changes ETB_REG_ADDRESS */ + emit_log_word(etm_read(ETMTRIGGER)); + emit_log_word(etm_read(ETMTSSCR)); + emit_log_word(etm_read(ETMTECR2)); + emit_log_word(etm_read(ETMTEEVR)); + emit_log_word(etm_read(ETMTECR1)); + emit_log_word(etm_read(ETMFFRR)); + emit_log_word(etm_read(ETMFFLR)); + emit_log_word(etm_read(ETMVDEVR)); + emit_log_word(etm_read(ETMVDCR1)); + emit_log_word(etm_read(ETMVDCR2)); + emit_log_word(etm_read(ETMVDCR3)); + emit_log_word(etm_read(ETMACVR0)); + emit_log_word(etm_read(ETMACVR1)); + emit_log_word(etm_read(ETMACVR2)); + emit_log_word(etm_read(ETMACVR3)); + emit_log_word(etm_read(ETMACVR4)); + emit_log_word(etm_read(ETMACVR5)); + emit_log_word(etm_read(ETMACVR6)); + emit_log_word(etm_read(ETMACVR7)); + emit_log_word(etm_read(ETMACTR0)); + emit_log_word(etm_read(ETMACTR1)); + emit_log_word(etm_read(ETMACTR2)); + emit_log_word(etm_read(ETMACTR3)); + emit_log_word(etm_read(ETMACTR4)); + emit_log_word(etm_read(ETMACTR5)); + emit_log_word(etm_read(ETMACTR6)); + emit_log_word(etm_read(ETMACTR7)); + emit_log_word(etm_read(ETMDCVR0)); + emit_log_word(etm_read(ETMDCVR2)); + emit_log_word(etm_read(ETMDCMR0)); + emit_log_word(etm_read(ETMDCMR2)); + emit_log_word(etm_read(ETMCNTRLDVR0)); + emit_log_word(etm_read(ETMCNTRLDVR1)); + emit_log_word(etm_read(ETMCNTENR0)); + emit_log_word(etm_read(ETMCNTENR1)); + emit_log_word(etm_read(ETMCNTRLDEVR0)); + emit_log_word(etm_read(ETMCNTRLDEVR1)); + emit_log_word(etm_read(ETMSQ12EVR)); + emit_log_word(etm_read(ETMSQ21EVR)); + emit_log_word(etm_read(ETMSQ23EVR)); + emit_log_word(etm_read(ETMSQ31EVR)); + emit_log_word(etm_read(ETMSQ32EVR)); + emit_log_word(etm_read(ETMSQ13EVR)); + emit_log_word(etm_read(ETMEXTOUTEVR0)); + emit_log_word(etm_read(ETMCIDCVR0)); + emit_log_word(etm_read(ETMCIDCMR)); + emit_log_word(etm_read(ETMSYNCFR)); +} + +static void dump_all(void *unused) +{ + get_cpu(); + __cpu_disable_etb(); + generate_etm_dump(); + generate_etb_dump(); + if (trace_enabled) + __cpu_enable_etb(); + put_cpu(); +} + +static void dump_trace(void) +{ + get_cpu(); + dump_all(NULL); + smp_call_function(dump_all, NULL, 1); + put_cpu(); +} + +static int bytes_to_dump; +static uint8_t *etm_buf_ptr; + +static int etm_dev_open(struct inode *inode, struct file *file) +{ + if (atomic_cmpxchg(&etm_dev_in_use, 0, 1)) + return -EBUSY; + + pr_debug("%s: successfully opened\n", __func__); + return 0; +} + +static ssize_t etm_dev_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + if (cpu_to_dump == next_cpu_to_dump) { + if (cpu_to_dump == 0) + dump_trace(); + bytes_to_dump = buf[cpu_to_dump].log_end; + buf[cpu_to_dump].log_end = 0; + etm_buf_ptr = buf[cpu_to_dump].etm_log_buf; + next_cpu_to_dump++; + if (next_cpu_to_dump >= num_possible_cpus()) + next_cpu_to_dump = 0; + } + + if (len > bytes_to_dump) + len = bytes_to_dump; + + if (copy_to_user(data, etm_buf_ptr, len)) { + pr_debug("%s: copy_to_user failed\n", __func__); + return -EFAULT; + } + + bytes_to_dump -= len; + etm_buf_ptr += len; + + pr_debug("%s: %d bytes copied, %d bytes left (cpu %d)\n", + __func__, len, bytes_to_dump, next_cpu_to_dump); + return len; +} + +static void setup_range_filter(char addr_type, char range, uint32_t reg1, + uint32_t addr1, uint32_t reg2, uint32_t addr2) +{ + etm_config.etm_addr_comp_value[reg1] = addr1; + etm_config.etm_addr_comp_value[reg2] = addr2; + + etm_config.etm_07_te_single_addr_comp |= (1 << reg1); + etm_config.etm_07_te_single_addr_comp |= (1 << reg2); + + etm_config.etm_09_te_control |= (1 << (reg1/2)); + if (range == 'i') + etm_config.etm_09_te_control &= ~(1 << 24); + else if (range == 'e') + etm_config.etm_09_te_control |= (1 << 24); + + if (addr_type == 'i') { + etm_config.etm_addr_access_type[reg1] = 0x99; + etm_config.etm_addr_access_type[reg2] = 0x99; + } else if (addr_type == 'd') { + etm_config.etm_addr_access_type[reg1] = 0x9C; + etm_config.etm_addr_access_type[reg2] = 0x9C; + } +} + +static void setup_start_stop_filter(char addr_type, char start_stop, + uint32_t reg, uint32_t addr) +{ + etm_config.etm_addr_comp_value[reg] = addr; + + if (start_stop == 's') + etm_config.etm_06_te_start_stop |= (1 << reg); + else if (start_stop == 't') + etm_config.etm_06_te_start_stop |= (1 << (reg + 16)); + + etm_config.etm_09_te_control |= (1 << 25); + + if (addr_type == 'i') + etm_config.etm_addr_access_type[reg] = 0x99; + else if (addr_type == 'd') + etm_config.etm_addr_access_type[reg] = 0x9C; +} + +static void setup_viewdata_range_filter(char range, uint32_t reg1, + uint32_t addr1, uint32_t reg2, uint32_t addr2) +{ + etm_config.etm_addr_comp_value[reg1] = addr1; + etm_config.etm_addr_comp_value[reg2] = addr2; + + if (range == 'i') { + etm_config.etm_0d_vd_single_addr_comp |= (1 << reg1); + etm_config.etm_0d_vd_single_addr_comp |= (1 << reg2); + etm_config.etm_0f_vd_control |= (1 << (reg1/2)); + } else if (range == 'e') { + etm_config.etm_0d_vd_single_addr_comp |= (1 << (reg1 + 16)); + etm_config.etm_0d_vd_single_addr_comp |= (1 << (reg2 + 16)); + etm_config.etm_0f_vd_control |= (1 << ((reg1/2) + 8)); + } + etm_config.etm_0f_vd_control &= ~(1 << 16); + + etm_config.etm_addr_access_type[reg1] = 0x9C; + etm_config.etm_addr_access_type[reg2] = 0x9C; +} + +static void setup_viewdata_start_stop_filter(char start_stop, uint32_t reg, + uint32_t addr) +{ + etm_config.etm_addr_comp_value[reg] = addr; + + if (start_stop == 's') + etm_config.etm_06_te_start_stop |= (1 << reg); + else if (start_stop == 't') + etm_config.etm_06_te_start_stop |= (1 << (reg + 16)); + + etm_config.etm_addr_access_type[reg] = 0x9C; +} + +static void setup_access_type(uint32_t reg, uint32_t value) +{ + etm_config.etm_addr_access_type[reg] &= 0xFFFFFFF8; + value &= 0x7; + etm_config.etm_addr_access_type[reg] |= value; +} + +static void reset_filter(void) +{ + etm_config.etm_00_control = 0x0000D84E; + /* etm_02_trigger_event 0x00000000: address comparator 0 matches */ + etm_config.etm_02_trigger_event = 0x00000000; + etm_config.etm_06_te_start_stop = 0x00000000; + etm_config.etm_07_te_single_addr_comp = 0x00000000; + /* etm_08_te_event 0x0000006F: always true */ + etm_config.etm_08_te_event = 0x0000006F; + /* etm_09_te_control 0x01000000: exclude none */ + etm_config.etm_09_te_control = 0x01000000; + etm_config.etm_0a_fifofull_region = 0x00000000; + etm_config.etm_0b_fifofull_level = 0x00000000; + /* etm_0c_vd_event 0x0000006F: always true */ + etm_config.etm_0c_vd_event = 0x0000006F; + etm_config.etm_0d_vd_single_addr_comp = 0x00000000; + etm_config.etm_0e_vd_mmd = 0x00000000; + /* etm_0f_vd_control 0x00010000: exclude none */ + etm_config.etm_0f_vd_control = 0x00010000; + etm_config.etm_addr_comp_value[0] = 0x00000000; + etm_config.etm_addr_comp_value[1] = 0x00000000; + etm_config.etm_addr_comp_value[2] = 0x00000000; + etm_config.etm_addr_comp_value[3] = 0x00000000; + etm_config.etm_addr_comp_value[4] = 0x00000000; + etm_config.etm_addr_comp_value[5] = 0x00000000; + etm_config.etm_addr_comp_value[6] = 0x00000000; + etm_config.etm_addr_comp_value[7] = 0x00000000; + etm_config.etm_addr_access_type[0] = 0x00000000; + etm_config.etm_addr_access_type[1] = 0x00000000; + etm_config.etm_addr_access_type[2] = 0x00000000; + etm_config.etm_addr_access_type[3] = 0x00000000; + etm_config.etm_addr_access_type[4] = 0x00000000; + etm_config.etm_addr_access_type[5] = 0x00000000; + etm_config.etm_addr_access_type[6] = 0x00000000; + etm_config.etm_addr_access_type[7] = 0x00000000; + etm_config.etm_data_comp_value[0] = 0x00000000; + etm_config.etm_data_comp_value[1] = 0x00000000; + etm_config.etm_data_comp_mask[0] = 0x00000000; + etm_config.etm_data_comp_mask[1] = 0x00000000; + etm_config.etm_counter_reload_value[0] = 0x00000000; + etm_config.etm_counter_reload_value[1] = 0x00000000; + etm_config.etm_counter_enable[0] = 0x0002406F; + etm_config.etm_counter_enable[1] = 0x0002406F; + etm_config.etm_counter_reload_event[0] = 0x0000406F; + etm_config.etm_counter_reload_event[1] = 0x0000406F; + etm_config.etm_60_seq_event_1_to_2 = 0x0000406F; + etm_config.etm_61_seq_event_2_to_1 = 0x0000406F; + etm_config.etm_62_seq_event_2_to_3 = 0x0000406F; + etm_config.etm_63_seq_event_3_to_1 = 0x0000406F; + etm_config.etm_64_seq_event_3_to_2 = 0x0000406F; + etm_config.etm_65_seq_event_1_to_3 = 0x0000406F; + etm_config.etm_6c_cid_comp_value_1 = 0x00000000; + etm_config.etm_6f_cid_comp_mask = 0x00000000; + etm_config.etm_78_sync_freq = 0x00000400; + etm_config.swconfig = 0x00000002; + /* etb_trig_cnt 0x00000020: ignore trigger */ + etm_config.etb_trig_cnt = 0x00000000; + /* etb_init_ptr 0x00000010: 16 marker bytes */ + etm_config.etb_init_ptr = 0x00000010; +} + +#define MAX_COMMAND_STRLEN 40 +static ssize_t etm_dev_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + char command[MAX_COMMAND_STRLEN]; + int strlen; + unsigned long value; + unsigned long reg1, reg2; + unsigned long addr1, addr2; + + strlen = strnlen_user(data, MAX_COMMAND_STRLEN); + pr_debug("etm: string length: %d", strlen); + if (strlen == 0 || strlen == (MAX_COMMAND_STRLEN+1)) { + pr_err("etm: error in strlen: %d", strlen); + return -EFAULT; + } + /* includes the null character */ + if (copy_from_user(command, data, strlen)) { + pr_err("etm: error in copy_from_user: %d", strlen); + return -EFAULT; + } + + pr_debug("etm: input = %s", command); + + switch (command[0]) { + case '0': + if (trace_enabled) { + disable_trace(); + pr_info("etm: tracing disabled\n"); + } + break; + case '1': + if (!trace_enabled) { + enable_trace(); + pr_info("etm: tracing enabled\n"); + } + break; + case 'f': + switch (command[2]) { + case 'i': + case 'd': + switch (command[4]) { + case 'i': + if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0", + ®1, &addr1, ®2, &addr2) != 4) + goto err_out; + if (reg1 > 7 || reg2 > 7 || (reg1 % 2)) + goto err_out; + setup_range_filter(command[2], 'i', + reg1, addr1, reg2, addr2); + break; + case 'e': + if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0", + ®1, &addr1, ®2, &addr2) != 4) + goto err_out; + if (reg1 > 7 || reg2 > 7 || (reg1 % 2) + || command[2] == 'd') + goto err_out; + setup_range_filter(command[2], 'e', + reg1, addr1, reg2, addr2); + break; + case 's': + if (sscanf(&command[6], "%lx:%lx\\0", + ®1, &addr1) != 2) + goto err_out; + if (reg1 > 7) + goto err_out; + setup_start_stop_filter(command[2], 's', + reg1, addr1); + break; + case 't': + if (sscanf(&command[6], "%lx:%lx\\0", + ®1, &addr1) != 2) + goto err_out; + if (reg1 > 7) + goto err_out; + setup_start_stop_filter(command[2], 't', + reg1, addr1); + break; + default: + goto err_out; + } + break; + case 'r': + reset_filter(); + break; + default: + goto err_out; + } + break; + case 'v': + switch (command[2]) { + case 'd': + switch (command[4]) { + case 'i': + if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0", + ®1, &addr1, ®2, &addr2) != 4) + goto err_out; + if (reg1 > 7 || reg2 > 7 || (reg1 % 2)) + goto err_out; + setup_viewdata_range_filter('i', + reg1, addr1, reg2, addr2); + break; + case 'e': + if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0", + ®1, &addr1, ®2, &addr2) != 4) + goto err_out; + if (reg1 > 7 || reg2 > 7 || (reg1 % 2)) + goto err_out; + setup_viewdata_range_filter('e', + reg1, addr1, reg2, addr2); + break; + case 's': + if (sscanf(&command[6], "%lx:%lx\\0", + ®1, &addr1) != 2) + goto err_out; + if (reg1 > 7) + goto err_out; + setup_viewdata_start_stop_filter('s', + reg1, addr1); + break; + case 't': + if (sscanf(&command[6], "%lx:%lx\\0", + ®1, &addr1) != 2) + goto err_out; + if (reg1 > 7) + goto err_out; + setup_viewdata_start_stop_filter('t', + reg1, addr1); + break; + default: + goto err_out; + } + break; + default: + goto err_out; + } + break; + case 'a': + switch (command[2]) { + case 't': + if (sscanf(&command[4], "%lx:%lx\\0", + ®1, &value) != 2) + goto err_out; + if (reg1 > 7 || value > 6) + goto err_out; + setup_access_type(reg1, value); + break; + default: + goto err_out; + } + break; + default: + goto err_out; + } + + return len; + +err_out: + return -EFAULT; +} + +static int etm_dev_release(struct inode *inode, struct file *file) +{ + if (cpu_to_dump == next_cpu_to_dump) + next_cpu_to_dump = 0; + cpu_to_dump = next_cpu_to_dump; + + atomic_set(&etm_dev_in_use, 0); + pr_debug("%s: released\n", __func__); + return 0; +} + +static const struct file_operations etm_dev_fops = { + .owner = THIS_MODULE, + .open = etm_dev_open, + .read = etm_dev_read, + .write = etm_dev_write, + .release = etm_dev_release, +}; + +static struct miscdevice etm_dev = { + .name = "msm_etm", + .minor = MISC_DYNAMIC_MINOR, + .fops = &etm_dev_fops, +}; + +static void __cpu_clear_sticky(void *unused) +{ + etm_read(ETMPDSR); /* clear sticky bit in PDSR */ + isb(); +} + +static int __init etm_init(void) +{ + int ret, cpu; + + ret = misc_register(&etm_dev); + if (ret) + return -ENODEV; + + alloc_b = alloc_percpu(typeof(*alloc_b)); + if (!alloc_b) + goto err1; + + for_each_possible_cpu(cpu) + *per_cpu_ptr(alloc_b, cpu) = &buf[cpu]; + + wake_lock_init(&etm_wake_lock, WAKE_LOCK_SUSPEND, "msm_etm"); + pm_qos_add_request(&etm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + /* No need to explicity turn on ETM clock since CP14 access go + * through via the autoclock turn on/off + */ + __cpu_clear_sticky(NULL); + smp_call_function(__cpu_clear_sticky, NULL, 1); + + cpu_to_dump = next_cpu_to_dump = 0; + + pr_info("ETM/ETB intialized.\n"); + + if (trace_on_boot) + enable_trace(); + + return 0; + +err1: + misc_deregister(&etm_dev); + return -ENOMEM; +} +module_init(etm_init); + +static void __exit etm_exit(void) +{ + disable_trace(); + pm_qos_remove_request(&etm_qos_req); + wake_lock_destroy(&etm_wake_lock); + free_percpu(alloc_b); + misc_deregister(&etm_dev); +} +module_exit(etm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("embedded trace driver"); diff --git a/arch/arm/mach-msm/fiq.h b/arch/arm/mach-msm/fiq.h new file mode 100644 index 00000000000..cd903908c08 --- /dev/null +++ b/arch/arm/mach-msm/fiq.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_FIQ_H +#define _ARCH_ARM_MACH_MSM_FIQ_H + +extern unsigned char fiq_glue, fiq_glue_end; +void fiq_glue_setup(void *func, void *data, void *sp); + +#endif diff --git a/arch/arm/mach-msm/fiq_glue.S b/arch/arm/mach-msm/fiq_glue.S new file mode 100644 index 00000000000..df1c7084fe1 --- /dev/null +++ b/arch/arm/mach-msm/fiq_glue.S @@ -0,0 +1,112 @@ +/* arch/arm/mach-msm/fiq_glue.S + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + + .text + + .global fiq_glue_end + + /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ + +ENTRY(fiq_glue) + /* store pc, cpsr from previous mode */ + mrs r12, spsr + sub r11, lr, #4 + subs r10, #1 + bne nested_fiq + + stmfd sp!, {r11-r12, lr} + + /* store r8-r14 from previous mode */ + sub sp, sp, #(7 * 4) + stmia sp, {r8-r14}^ + nop + + /* store r0-r7 from previous mode */ + stmfd sp!, {r0-r7} + + /* setup func(data,regs) arguments */ + mov r0, r9 + mov r1, sp + mov r3, r8 + + mov r7, sp + + /* Get sp and lr from non-user modes */ + and r4, r12, #MODE_MASK + cmp r4, #USR_MODE + beq fiq_from_usr_mode + + mov r7, sp + orr r4, r4, #(PSR_I_BIT | PSR_F_BIT) + msr cpsr_c, r4 + str sp, [r7, #(4 * 13)] + str lr, [r7, #(4 * 14)] + mrs r5, spsr + str r5, [r7, #(4 * 17)] + + cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + /* use fiq stack if we reenter this mode */ + subne sp, r7, #(4 * 3) + +fiq_from_usr_mode: + msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + mov r2, sp + sub sp, r7, #12 + stmfd sp!, {r2, ip, lr} + /* call func(data,regs) */ + blx r3 + ldmfd sp, {r2, ip, lr} + mov sp, r2 + + /* restore/discard saved state */ + cmp r4, #USR_MODE + beq fiq_from_usr_mode_exit + + msr cpsr_c, r4 + ldr sp, [r7, #(4 * 13)] + ldr lr, [r7, #(4 * 14)] + msr spsr_cxsf, r5 + +fiq_from_usr_mode_exit: + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + + ldmfd sp!, {r0-r7} + add sp, sp, #(7 * 4) + ldmfd sp!, {r11-r12, lr} +exit_fiq: + msr spsr_cxsf, r12 + add r10, #1 + movs pc, r11 + +nested_fiq: + orr r12, r12, #(PSR_F_BIT) + b exit_fiq + +fiq_glue_end: + +ENTRY(fiq_glue_setup) /* func, data, sp */ + mrs r3, cpsr + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + movs r8, r0 + mov r9, r1 + mov sp, r2 + moveq r10, #0 + movne r10, #1 + msr cpsr_c, r3 + bx lr + diff --git a/arch/arm/mach-msm/fish_battery.c b/arch/arm/mach-msm/fish_battery.c new file mode 100644 index 00000000000..19fbb91fe83 --- /dev/null +++ b/arch/arm/mach-msm/fish_battery.c @@ -0,0 +1,145 @@ +/* arch/arm/mach-msm/fish_battery.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * based on: arch/arm/mach-msm/htc_battery.c + */ + +#include +#include +#include +#include +#include +#include + +static enum power_supply_property fish_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property fish_power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +static int fish_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static int fish_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static struct power_supply fish_power_supplies[] = { + { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = fish_battery_properties, + .num_properties = ARRAY_SIZE(fish_battery_properties), + .get_property = fish_battery_get_property, + }, + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = fish_power_properties, + .num_properties = ARRAY_SIZE(fish_power_properties), + .get_property = fish_power_get_property, + }, +}; + +static int fish_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = 1; + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fish_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = 100; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fish_battery_probe(struct platform_device *pdev) +{ + int i; + int rc; + + /* init power supplier framework */ + for (i = 0; i < ARRAY_SIZE(fish_power_supplies); i++) { + rc = power_supply_register(&pdev->dev, &fish_power_supplies[i]); + if (rc) + pr_err("%s: Failed to register power supply (%d)\n", + __func__, rc); + } + + return 0; +} + +static struct platform_driver fish_battery_driver = { + .probe = fish_battery_probe, + .driver = { + .name = "fish_battery", + .owner = THIS_MODULE, + }, +}; + +static int __init fish_battery_init(void) +{ + platform_driver_register(&fish_battery_driver); + return 0; +} + +module_init(fish_battery_init); +MODULE_DESCRIPTION("Qualcomm fish battery driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/footswitch-8x60.c b/arch/arm/mach-msm/footswitch-8x60.c new file mode 100644 index 00000000000..84735aabbbf --- /dev/null +++ b/arch/arm/mach-msm/footswitch-8x60.c @@ -0,0 +1,592 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "footswitch.h" + +#ifdef CONFIG_MSM_SECURE_IO +#undef readl_relaxed +#undef writel_relaxed +#define readl_relaxed secure_readl +#define writel_relaxed secure_writel +#endif + +#define REG(off) (MSM_MMSS_CLK_CTL_BASE + (off)) +#define GEMINI_GFS_CTL_REG REG(0x01A0) +#define GFX2D0_GFS_CTL_REG REG(0x0180) +#define GFX2D1_GFS_CTL_REG REG(0x0184) +#define GFX3D_GFS_CTL_REG REG(0x0188) +#define MDP_GFS_CTL_REG REG(0x0190) +#define ROT_GFS_CTL_REG REG(0x018C) +#define VED_GFS_CTL_REG REG(0x0194) +#define VFE_GFS_CTL_REG REG(0x0198) +#define VPE_GFS_CTL_REG REG(0x019C) +#define VCAP_GFS_CTL_REG REG(0x0254) + +#define CLAMP_BIT BIT(5) +#define ENABLE_BIT BIT(8) +#define RETENTION_BIT BIT(9) + +#define GFS_DELAY_CNT 31 + +#define RESET_DELAY_US 1 +/* Clock rate to use if one has not previously been set. */ +#define DEFAULT_RATE 27000000 +#define MAX_CLKS 10 + +/* + * Lock is only needed to protect against the first footswitch_enable() + * call occuring concurrently with late_footswitch_init(). + */ +static DEFINE_MUTEX(claim_lock); + +struct footswitch { + struct regulator_dev *rdev; + struct regulator_desc desc; + void *gfs_ctl_reg; + int bus_port0, bus_port1; + bool is_enabled; + bool is_claimed; + struct fs_clk_data *clk_data; + struct clk *core_clk; +}; + +static int setup_clocks(struct footswitch *fs) +{ + int rc = 0; + struct fs_clk_data *clock; + long rate; + + /* + * Enable all clocks in the power domain. If a specific clock rate is + * required for reset timing, set that rate before enabling the clocks. + */ + for (clock = fs->clk_data; clock->clk; clock++) { + clock->rate = clk_get_rate(clock->clk); + if (!clock->rate || clock->reset_rate) { + rate = clock->reset_rate ? + clock->reset_rate : DEFAULT_RATE; + rc = clk_set_rate(clock->clk, rate); + if (rc && rc != -ENOSYS) { + pr_err("Failed to set %s %s rate to %lu Hz.\n", + fs->desc.name, clock->name, clock->rate); + for (clock--; clock >= fs->clk_data; clock--) { + if (clock->enabled) + clk_disable_unprepare( + clock->clk); + clk_set_rate(clock->clk, clock->rate); + } + return rc; + } + } + /* + * Some clocks are for reset purposes only. These clocks will + * fail to enable. Ignore the failures but keep track of them so + * we don't try to disable them later and crash due to + * unbalanced calls. + */ + clock->enabled = !clk_prepare_enable(clock->clk); + } + + return 0; +} + +static void restore_clocks(struct footswitch *fs) +{ + struct fs_clk_data *clock; + + /* Restore clocks to their orignal states before setup_clocks(). */ + for (clock = fs->clk_data; clock->clk; clock++) { + if (clock->enabled) + clk_disable_unprepare(clock->clk); + if (clock->rate && clk_set_rate(clock->clk, clock->rate)) + pr_err("Failed to restore %s %s rate to %lu Hz.\n", + fs->desc.name, clock->name, clock->rate); + } +} + +static int footswitch_is_enabled(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + + return fs->is_enabled; +} + +static int footswitch_enable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + struct fs_clk_data *clock; + uint32_t regval, rc = 0; + + mutex_lock(&claim_lock); + fs->is_claimed = true; + mutex_unlock(&claim_lock); + + /* Return early if already enabled. */ + regval = readl_relaxed(fs->gfs_ctl_reg); + if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT) + return 0; + + /* Make sure required clocks are on at the correct rates. */ + rc = setup_clocks(fs); + if (rc) + return rc; + + /* Un-halt all bus ports in the power domain. */ + if (fs->bus_port0) { + rc = msm_bus_axi_portunhalt(fs->bus_port0); + if (rc) { + pr_err("%s port 0 unhalt failed.\n", fs->desc.name); + goto err; + } + } + if (fs->bus_port1) { + rc = msm_bus_axi_portunhalt(fs->bus_port1); + if (rc) { + pr_err("%s port 1 unhalt failed.\n", fs->desc.name); + goto err_port2_halt; + } + } + + /* + * (Re-)Assert resets for all clocks in the clock domain, since + * footswitch_enable() is first called before footswitch_disable() + * and resets should be asserted before power is restored. + */ + for (clock = fs->clk_data; clock->clk; clock++) + ; /* Do nothing */ + for (clock--; clock >= fs->clk_data; clock--) + clk_reset(clock->clk, CLK_RESET_ASSERT); + /* Wait for synchronous resets to propagate. */ + udelay(RESET_DELAY_US); + + /* Enable the power rail at the footswitch. */ + regval |= ENABLE_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + /* Wait for the rail to fully charge. */ + mb(); + udelay(1); + + /* Un-clamp the I/O ports. */ + regval &= ~CLAMP_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + /* Deassert resets for all clocks in the power domain. */ + for (clock = fs->clk_data; clock->clk; clock++) + clk_reset(clock->clk, CLK_RESET_DEASSERT); + /* Toggle core reset again after first power-on (required for GFX3D). */ + if (fs->desc.id == FS_GFX3D) { + clk_reset(fs->core_clk, CLK_RESET_ASSERT); + udelay(RESET_DELAY_US); + clk_reset(fs->core_clk, CLK_RESET_DEASSERT); + udelay(RESET_DELAY_US); + } + + /* Prevent core memory from collapsing when its clock is gated. */ + clk_set_flags(fs->core_clk, CLKFLAG_RETAIN); + + /* Return clocks to their state before this function. */ + restore_clocks(fs); + + fs->is_enabled = true; + return 0; + +err_port2_halt: + msm_bus_axi_porthalt(fs->bus_port0); +err: + restore_clocks(fs); + return rc; +} + +static int footswitch_disable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + struct fs_clk_data *clock; + uint32_t regval, rc = 0; + + /* Return early if already disabled. */ + regval = readl_relaxed(fs->gfs_ctl_reg); + if ((regval & ENABLE_BIT) == 0) + return 0; + + /* Make sure required clocks are on at the correct rates. */ + rc = setup_clocks(fs); + if (rc) + return rc; + + /* Allow core memory to collapse when its clock is gated. */ + clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN); + + /* Halt all bus ports in the power domain. */ + if (fs->bus_port0) { + rc = msm_bus_axi_porthalt(fs->bus_port0); + if (rc) { + pr_err("%s port 0 halt failed.\n", fs->desc.name); + goto err; + } + } + if (fs->bus_port1) { + rc = msm_bus_axi_porthalt(fs->bus_port1); + if (rc) { + pr_err("%s port 1 halt failed.\n", fs->desc.name); + goto err_port2_halt; + } + } + + /* + * Assert resets for all clocks in the clock domain so that + * outputs settle prior to clamping. + */ + for (clock = fs->clk_data; clock->clk; clock++) + ; /* Do nothing */ + for (clock--; clock >= fs->clk_data; clock--) + clk_reset(clock->clk, CLK_RESET_ASSERT); + /* Wait for synchronous resets to propagate. */ + udelay(RESET_DELAY_US); + + /* + * Return clocks to their state before this function. For robustness + * if memory-retention across collapses is required, clocks should + * be disabled before asserting the clamps. Assuming clocks were off + * before entering footswitch_disable(), this will be true. + */ + restore_clocks(fs); + + /* + * Clamp the I/O ports of the core to ensure the values + * remain fixed while the core is collapsed. + */ + regval |= CLAMP_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + /* Collapse the power rail at the footswitch. */ + regval &= ~ENABLE_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + fs->is_enabled = false; + return 0; + +err_port2_halt: + msm_bus_axi_portunhalt(fs->bus_port0); +err: + clk_set_flags(fs->core_clk, CLKFLAG_RETAIN); + restore_clocks(fs); + return rc; +} + +static int gfx2d_footswitch_enable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + struct fs_clk_data *clock; + uint32_t regval, rc = 0; + + mutex_lock(&claim_lock); + fs->is_claimed = true; + mutex_unlock(&claim_lock); + + /* Return early if already enabled. */ + regval = readl_relaxed(fs->gfs_ctl_reg); + if ((regval & (ENABLE_BIT | CLAMP_BIT)) == ENABLE_BIT) + return 0; + + /* Make sure required clocks are on at the correct rates. */ + rc = setup_clocks(fs); + if (rc) + return rc; + + /* Un-halt all bus ports in the power domain. */ + if (fs->bus_port0) { + rc = msm_bus_axi_portunhalt(fs->bus_port0); + if (rc) { + pr_err("%s port 0 unhalt failed.\n", fs->desc.name); + goto err; + } + } + + /* Disable core clock. */ + clk_disable_unprepare(fs->core_clk); + + /* + * (Re-)Assert resets for all clocks in the clock domain, since + * footswitch_enable() is first called before footswitch_disable() + * and resets should be asserted before power is restored. + */ + for (clock = fs->clk_data; clock->clk; clock++) + ; /* Do nothing */ + for (clock--; clock >= fs->clk_data; clock--) + clk_reset(clock->clk, CLK_RESET_ASSERT); + /* Wait for synchronous resets to propagate. */ + udelay(RESET_DELAY_US); + + /* Enable the power rail at the footswitch. */ + regval |= ENABLE_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + mb(); + udelay(1); + + /* Un-clamp the I/O ports. */ + regval &= ~CLAMP_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + /* Deassert resets for all clocks in the power domain. */ + for (clock = fs->clk_data; clock->clk; clock++) + clk_reset(clock->clk, CLK_RESET_DEASSERT); + udelay(RESET_DELAY_US); + + /* Re-enable core clock. */ + clk_prepare_enable(fs->core_clk); + + /* Prevent core memory from collapsing when its clock is gated. */ + clk_set_flags(fs->core_clk, CLKFLAG_RETAIN); + + /* Return clocks to their state before this function. */ + restore_clocks(fs); + + fs->is_enabled = true; + return 0; + +err: + restore_clocks(fs); + return rc; +} + +static int gfx2d_footswitch_disable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + struct fs_clk_data *clock; + uint32_t regval, rc = 0; + + /* Return early if already disabled. */ + regval = readl_relaxed(fs->gfs_ctl_reg); + if ((regval & ENABLE_BIT) == 0) + return 0; + + /* Make sure required clocks are on at the correct rates. */ + rc = setup_clocks(fs); + if (rc) + return rc; + + /* Allow core memory to collapse when its clock is gated. */ + clk_set_flags(fs->core_clk, CLKFLAG_NORETAIN); + + /* Halt all bus ports in the power domain. */ + if (fs->bus_port0) { + rc = msm_bus_axi_porthalt(fs->bus_port0); + if (rc) { + pr_err("%s port 0 halt failed.\n", fs->desc.name); + goto err; + } + } + + /* Disable core clock. */ + clk_disable_unprepare(fs->core_clk); + + /* + * Assert resets for all clocks in the clock domain so that + * outputs settle prior to clamping. + */ + for (clock = fs->clk_data; clock->clk; clock++) + ; /* Do nothing */ + for (clock--; clock >= fs->clk_data; clock--) + clk_reset(clock->clk, CLK_RESET_ASSERT); + /* Wait for synchronous resets to propagate. */ + udelay(5); + + /* + * Clamp the I/O ports of the core to ensure the values + * remain fixed while the core is collapsed. + */ + regval |= CLAMP_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + /* Collapse the power rail at the footswitch. */ + regval &= ~ENABLE_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + /* Re-enable core clock. */ + clk_prepare_enable(fs->core_clk); + + /* Return clocks to their state before this function. */ + restore_clocks(fs); + + fs->is_enabled = false; + return 0; + +err: + clk_set_flags(fs->core_clk, CLKFLAG_RETAIN); + restore_clocks(fs); + return rc; +} + +static struct regulator_ops standard_fs_ops = { + .is_enabled = footswitch_is_enabled, + .enable = footswitch_enable, + .disable = footswitch_disable, +}; + +static struct regulator_ops gfx2d_fs_ops = { + .is_enabled = footswitch_is_enabled, + .enable = gfx2d_footswitch_enable, + .disable = gfx2d_footswitch_disable, +}; + +#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg) \ + [(_id)] = { \ + .desc = { \ + .id = (_id), \ + .name = (_name), \ + .ops = (_ops), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + .gfs_ctl_reg = (_gfs_ctl_reg), \ + } +static struct footswitch footswitches[] = { + FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops, GFX2D0_GFS_CTL_REG), + FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops, GFX2D1_GFS_CTL_REG), + FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops, GFX3D_GFS_CTL_REG), + FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops, GEMINI_GFS_CTL_REG), + FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops, MDP_GFS_CTL_REG), + FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops, ROT_GFS_CTL_REG), + FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops, VED_GFS_CTL_REG), + FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops, VFE_GFS_CTL_REG), + FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops, VPE_GFS_CTL_REG), + FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops, VCAP_GFS_CTL_REG), +}; + +static int footswitch_probe(struct platform_device *pdev) +{ + struct footswitch *fs; + struct regulator_init_data *init_data; + struct fs_driver_data *driver_data; + struct fs_clk_data *clock; + uint32_t regval, rc = 0; + + if (pdev == NULL) + return -EINVAL; + + if (pdev->id >= MAX_FS) + return -ENODEV; + + init_data = pdev->dev.platform_data; + driver_data = init_data->driver_data; + fs = &footswitches[pdev->id]; + fs->clk_data = driver_data->clks; + fs->bus_port0 = driver_data->bus_port0; + fs->bus_port1 = driver_data->bus_port1; + + for (clock = fs->clk_data; clock->name; clock++) { + clock->clk = clk_get(&pdev->dev, clock->name); + if (IS_ERR(clock->clk)) { + rc = PTR_ERR(clock->clk); + pr_err("%s clk_get(%s) failed\n", fs->desc.name, + clock->name); + goto err; + } + if (!strncmp(clock->name, "core_clk", 8)) + fs->core_clk = clock->clk; + } + + /* + * Set number of AHB_CLK cycles to delay the assertion of gfs_en_all + * after enabling the footswitch. Also ensure the retention bit is + * clear so disabling the footswitch will power-collapse the core. + */ + regval = readl_relaxed(fs->gfs_ctl_reg); + regval |= GFS_DELAY_CNT; + regval &= ~RETENTION_BIT; + writel_relaxed(regval, fs->gfs_ctl_reg); + + fs->rdev = regulator_register(&fs->desc, &pdev->dev, + init_data, fs, NULL); + if (IS_ERR(footswitches[pdev->id].rdev)) { + pr_err("regulator_register(\"%s\") failed\n", + fs->desc.name); + rc = PTR_ERR(footswitches[pdev->id].rdev); + goto err; + } + + return 0; + +err: + for (clock = fs->clk_data; clock->clk; clock++) + clk_put(clock->clk); + + return rc; +} + +static int __devexit footswitch_remove(struct platform_device *pdev) +{ + struct footswitch *fs = &footswitches[pdev->id]; + struct fs_clk_data *clock; + + for (clock = fs->clk_data; clock->clk; clock++) + clk_put(clock->clk); + regulator_unregister(fs->rdev); + + return 0; +} + +static struct platform_driver footswitch_driver = { + .probe = footswitch_probe, + .remove = __devexit_p(footswitch_remove), + .driver = { + .name = "footswitch-8x60", + .owner = THIS_MODULE, + }, +}; + +static int __init late_footswitch_init(void) +{ + int i; + + mutex_lock(&claim_lock); + /* Turn off all registered but unused footswitches. */ + for (i = 0; i < ARRAY_SIZE(footswitches); i++) + if (footswitches[i].rdev && !footswitches[i].is_claimed) + footswitches[i].rdev->desc->ops-> + disable(footswitches[i].rdev); + mutex_unlock(&claim_lock); + + return 0; +} +late_initcall(late_footswitch_init); + +static int __init footswitch_init(void) +{ + return platform_driver_register(&footswitch_driver); +} +subsys_initcall(footswitch_init); + +static void __exit footswitch_exit(void) +{ + platform_driver_unregister(&footswitch_driver); +} +module_exit(footswitch_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM8x60 rail footswitch"); +MODULE_ALIAS("platform:footswitch-msm8x60"); diff --git a/arch/arm/mach-msm/footswitch-pcom.c b/arch/arm/mach-msm/footswitch-pcom.c new file mode 100644 index 00000000000..07d71182863 --- /dev/null +++ b/arch/arm/mach-msm/footswitch-pcom.c @@ -0,0 +1,335 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "footswitch.h" + +/* PCOM power rail IDs */ +#define PCOM_FS_GRP 8 +#define PCOM_FS_GRP_2D 58 +#define PCOM_FS_MDP 14 +#define PCOM_FS_MFC 68 +#define PCOM_FS_ROTATOR 90 +#define PCOM_FS_VFE 41 +#define PCOM_FS_VPE 76 + +#define PCOM_RAIL_MODE_AUTO 0 +#define PCOM_RAIL_MODE_MANUAL 1 + +/** + * struct footswitch - Per-footswitch data and state + * @rdev: Regulator framework device + * @desc: Regulator descriptor + * @init_data: Regulator platform data + * @pcom_id: Proc-comm ID of the footswitch + * @is_enabled: Flag set when footswitch is enabled + * @is_manual: Flag set when footswitch is in manual proc-comm mode + * @has_ahb_clk: Flag set if footswitched core has an ahb_clk + * @has_src_clk: Flag set if footswitched core has a src_clk + * @src_clk: Controls the core clock's rate + * @core_clk: Clocks the core + * @ahb_clk: Clocks the core's register interface + * @src_clk_init_rate: Rate to use for src_clk if it has not been set yet + * @is_rate_set: Flag set if core_clk's rate has been set + */ +struct footswitch { + struct regulator_dev *rdev; + struct regulator_desc desc; + struct regulator_init_data init_data; + unsigned pcom_id; + bool is_enabled; + bool is_manual; + struct clk *src_clk; + struct clk *core_clk; + struct clk *ahb_clk; + const bool has_ahb_clk; + const bool has_src_clk; + const int src_clk_init_rate; + bool is_rate_set; +}; + +static inline int set_rail_mode(int pcom_id, int mode) +{ + int rc; + + rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &pcom_id, &mode); + if (!rc && pcom_id) + rc = -EINVAL; + + return rc; +} + +static inline int set_rail_state(int pcom_id, int state) +{ + int rc; + + rc = msm_proc_comm(state, &pcom_id, NULL); + if (!rc && pcom_id) + rc = -EINVAL; + + return rc; +} + +static int enable_clocks(struct footswitch *fs) +{ + fs->is_rate_set = !!(clk_get_rate(fs->src_clk)); + if (!fs->is_rate_set) + clk_set_rate(fs->src_clk, fs->src_clk_init_rate); + clk_prepare_enable(fs->core_clk); + + if (fs->ahb_clk) + clk_prepare_enable(fs->ahb_clk); + + return 0; +} + +static void disable_clocks(struct footswitch *fs) +{ + if (fs->ahb_clk) + clk_disable_unprepare(fs->ahb_clk); + clk_disable_unprepare(fs->core_clk); +} + +static int footswitch_is_enabled(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + + return fs->is_enabled; +} + +static int footswitch_enable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + int rc; + + rc = enable_clocks(fs); + if (rc) + return rc; + + rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE); + if (!rc) + fs->is_enabled = true; + + disable_clocks(fs); + + return rc; +} + +static int footswitch_disable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + int rc; + + rc = enable_clocks(fs); + if (rc) + return rc; + + rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_DISABLE); + if (!rc) + fs->is_enabled = false; + + disable_clocks(fs); + + return rc; +} + +static struct regulator_ops footswitch_ops = { + .is_enabled = footswitch_is_enabled, + .enable = footswitch_enable, + .disable = footswitch_disable, +}; + +#define FOOTSWITCH(_id, _pcom_id, _name, _src_clk, _rate, _ahb_clk) \ + [_id] = { \ + .desc = { \ + .id = _id, \ + .name = _name, \ + .ops = &footswitch_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + .pcom_id = _pcom_id, \ + .has_src_clk = _src_clk, \ + .src_clk_init_rate = _rate, \ + .has_ahb_clk = _ahb_clk, \ + } +static struct footswitch footswitches[] = { + FOOTSWITCH(FS_GFX3D, PCOM_FS_GRP, + "fs_gfx3d", true, 24576000, true), + FOOTSWITCH(FS_GFX2D0, PCOM_FS_GRP_2D, + "fs_gfx2d0", false, 24576000, true), + FOOTSWITCH(FS_MDP, PCOM_FS_MDP, + "fs_mdp", false, 24576000, true), + FOOTSWITCH(FS_MFC, PCOM_FS_MFC, + "fs_mfc", false, 24576000, true), + FOOTSWITCH(FS_ROT, PCOM_FS_ROTATOR, + "fs_rot", false, 0, true), + FOOTSWITCH(FS_VFE, PCOM_FS_VFE, + "fs_vfe", false, 24576000, true), + FOOTSWITCH(FS_VPE, PCOM_FS_VPE, + "fs_vpe", false, 24576000, false), +}; + +static int get_clocks(struct device *dev, struct footswitch *fs) +{ + int rc; + + /* + * Some SoCs may not have a separate rate-settable clock. + * If one can't be found, try to use the core clock for + * rate-setting instead. + */ + if (fs->has_src_clk) { + fs->src_clk = clk_get(dev, "src_clk"); + if (IS_ERR(fs->src_clk)) + fs->src_clk = clk_get(dev, "core_clk"); + } else { + fs->src_clk = clk_get(dev, "core_clk"); + } + if (IS_ERR(fs->src_clk)) { + pr_err("%s clk_get(src_clk) failed\n", fs->desc.name); + rc = PTR_ERR(fs->src_clk); + goto err_src_clk; + } + + fs->core_clk = clk_get(dev, "core_clk"); + if (IS_ERR(fs->core_clk)) { + pr_err("%s clk_get(core_clk) failed\n", fs->desc.name); + rc = PTR_ERR(fs->core_clk); + goto err_core_clk; + } + + if (fs->has_ahb_clk) { + fs->ahb_clk = clk_get(dev, "iface_clk"); + if (IS_ERR(fs->ahb_clk)) { + pr_err("%s clk_get(iface_clk) failed\n", fs->desc.name); + rc = PTR_ERR(fs->ahb_clk); + goto err_ahb_clk; + } + } + + return 0; + +err_ahb_clk: + clk_put(fs->core_clk); +err_core_clk: + clk_put(fs->src_clk); +err_src_clk: + return rc; +} + +static void put_clocks(struct footswitch *fs) +{ + clk_put(fs->src_clk); + clk_put(fs->core_clk); + clk_put(fs->ahb_clk); +} + +static int footswitch_probe(struct platform_device *pdev) +{ + struct footswitch *fs; + struct regulator_init_data *init_data; + int rc; + + if (pdev == NULL) + return -EINVAL; + + if (pdev->id >= MAX_FS) + return -ENODEV; + + fs = &footswitches[pdev->id]; + if (!fs->is_manual) { + pr_err("%s is not in manual mode\n", fs->desc.name); + return -EINVAL; + } + init_data = pdev->dev.platform_data; + + rc = get_clocks(&pdev->dev, fs); + if (rc) + return rc; + + fs->rdev = regulator_register(&fs->desc, &pdev->dev, + init_data, fs, NULL); + if (IS_ERR(fs->rdev)) { + pr_err("regulator_register(%s) failed\n", fs->desc.name); + rc = PTR_ERR(fs->rdev); + goto err_register; + } + + return 0; + +err_register: + put_clocks(fs); + + return rc; +} + +static int __devexit footswitch_remove(struct platform_device *pdev) +{ + struct footswitch *fs = &footswitches[pdev->id]; + + regulator_unregister(fs->rdev); + set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_AUTO); + put_clocks(fs); + + return 0; +} + +static struct platform_driver footswitch_driver = { + .probe = footswitch_probe, + .remove = __devexit_p(footswitch_remove), + .driver = { + .name = "footswitch-pcom", + .owner = THIS_MODULE, + }, +}; + +static int __init footswitch_init(void) +{ + struct footswitch *fs; + int ret; + + /* + * Enable all footswitches in manual mode (ie. not controlled along + * with pcom clocks). + */ + for (fs = footswitches; fs < footswitches + ARRAY_SIZE(footswitches); + fs++) { + set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE); + ret = set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_MANUAL); + if (!ret) + fs->is_manual = 1; + } + + return platform_driver_register(&footswitch_driver); +} +subsys_initcall(footswitch_init); + +static void __exit footswitch_exit(void) +{ + platform_driver_unregister(&footswitch_driver); +} +module_exit(footswitch_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("proc_comm rail footswitch"); +MODULE_ALIAS("platform:footswitch-pcom"); diff --git a/arch/arm/mach-msm/footswitch.h b/arch/arm/mach-msm/footswitch.h new file mode 100644 index 00000000000..1809b2e50eb --- /dev/null +++ b/arch/arm/mach-msm/footswitch.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2010-2012 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MSM_FOOTSWITCH__ +#define __MSM_FOOTSWITCH__ + +#include + +/* Device IDs */ +#define FS_GFX2D0 0 +#define FS_GFX2D1 1 +#define FS_GFX3D 2 +#define FS_IJPEG 3 +#define FS_MDP 4 +#define FS_MFC 5 +#define FS_ROT 6 +#define FS_VED 7 +#define FS_VFE 8 +#define FS_VPE 9 +#define FS_VCAP 10 +#define MAX_FS 11 + +struct fs_clk_data { + const char *name; + struct clk *clk; + unsigned long rate; + unsigned long reset_rate; + bool enabled; +}; + +struct fs_driver_data { + int bus_port0, bus_port1; + struct fs_clk_data *clks; +}; + +#define FS_GENERIC(_drv_name, _id, _name, _dev_id, _data) \ +(&(struct platform_device){ \ + .name = (_drv_name), \ + .id = (_id), \ + .dev = { \ + .platform_data = &(struct regulator_init_data){ \ + .constraints = { \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = 1, \ + .consumer_supplies = \ + &(struct regulator_consumer_supply) \ + REGULATOR_SUPPLY((_name), (_dev_id)), \ + .driver_data = (_data), \ + } \ + }, \ +}) +#define FS_PCOM(_id, _name, _dev_id) \ + FS_GENERIC("footswitch-pcom", _id, _name, _dev_id, NULL) +#define FS_8X60(_id, _name, _dev_id, _data) \ + FS_GENERIC("footswitch-8x60", _id, _name, _dev_id, _data) + +#endif diff --git a/arch/arm/mach-msm/gdsc.c b/arch/arm/mach-msm/gdsc.c new file mode 100644 index 00000000000..df3a92d2651 --- /dev/null +++ b/arch/arm/mach-msm/gdsc.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PWR_ON_MASK BIT(31) +#define EN_REST_WAIT_MASK (0xF << 20) +#define EN_FEW_WAIT_MASK (0xF << 16) +#define CLK_DIS_WAIT_MASK (0xF << 12) +#define SW_OVERRIDE_MASK BIT(2) +#define HW_CONTROL_MASK BIT(1) +#define SW_COLLAPSE_MASK BIT(0) + +/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */ +#define EN_REST_WAIT_VAL (0x2 << 20) +#define EN_FEW_WAIT_VAL (0x2 << 16) +#define CLK_DIS_WAIT_VAL (0x2 << 12) + +#define TIMEOUT_US 10 + +struct gdsc { + struct regulator_dev *rdev; + struct regulator_desc rdesc; + void __iomem *gdscr; +}; + +static int gdsc_is_enabled(struct regulator_dev *rdev) +{ + struct gdsc *sc = rdev_get_drvdata(rdev); + + return !!(readl_relaxed(sc->gdscr) & PWR_ON_MASK); +} + +static int gdsc_enable(struct regulator_dev *rdev) +{ + struct gdsc *sc = rdev_get_drvdata(rdev); + uint32_t regval; + int ret; + + regval = readl_relaxed(sc->gdscr); + regval &= ~SW_COLLAPSE_MASK; + writel_relaxed(regval, sc->gdscr); + + ret = readl_tight_poll_timeout(sc->gdscr, regval, regval & PWR_ON_MASK, + TIMEOUT_US); + if (ret) + dev_err(&rdev->dev, "%s enable timed out\n", sc->rdesc.name); + + return ret; +} + +static int gdsc_disable(struct regulator_dev *rdev) +{ + struct gdsc *sc = rdev_get_drvdata(rdev); + uint32_t regval; + int ret; + + regval = readl_relaxed(sc->gdscr); + regval |= SW_COLLAPSE_MASK; + writel_relaxed(regval, sc->gdscr); + + ret = readl_tight_poll_timeout(sc->gdscr, regval, + !(regval & PWR_ON_MASK), TIMEOUT_US); + if (ret) + dev_err(&rdev->dev, "%s disable timed out\n", sc->rdesc.name); + + return ret; +} + +static struct regulator_ops gdsc_ops = { + .is_enabled = gdsc_is_enabled, + .enable = gdsc_enable, + .disable = gdsc_disable, +}; + +static int __devinit gdsc_probe(struct platform_device *pdev) +{ + static atomic_t gdsc_count = ATOMIC_INIT(-1); + struct regulator_init_data *init_data; + struct resource *res; + struct gdsc *sc; + uint32_t regval; + int ret; + + sc = devm_kzalloc(&pdev->dev, sizeof(struct gdsc), GFP_KERNEL); + if (sc == NULL) + return -ENOMEM; + + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + if (init_data == NULL) + return -ENOMEM; + + if (of_get_property(pdev->dev.of_node, "parent-supply", NULL)) + init_data->supply_regulator = "parent"; + + ret = of_property_read_string(pdev->dev.of_node, "regulator-name", + &sc->rdesc.name); + if (ret) + return ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -EINVAL; + sc->gdscr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (sc->gdscr == NULL) + return -ENOMEM; + + sc->rdesc.id = atomic_inc_return(&gdsc_count); + sc->rdesc.ops = &gdsc_ops; + sc->rdesc.type = REGULATOR_VOLTAGE; + sc->rdesc.owner = THIS_MODULE; + platform_set_drvdata(pdev, sc); + + /* + * Disable HW trigger: collapse/restore occur based on registers writes. + * Disable SW override: Use hardware state-machine for sequencing. + */ + regval = readl_relaxed(sc->gdscr); + regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK); + + /* Configure wait time between states. */ + regval &= ~(EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK); + regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL; + writel_relaxed(regval, sc->gdscr); + + sc->rdev = regulator_register(&sc->rdesc, &pdev->dev, init_data, sc, + pdev->dev.of_node); + if (IS_ERR(sc->rdev)) { + dev_err(&pdev->dev, "regulator_register(\"%s\") failed.\n", + sc->rdesc.name); + return PTR_ERR(sc->rdev); + } + + return 0; +} + +static int __devexit gdsc_remove(struct platform_device *pdev) +{ + struct gdsc *sc = platform_get_drvdata(pdev); + regulator_unregister(sc->rdev); + return 0; +} + +static struct of_device_id gdsc_match_table[] = { + { .compatible = "qcom,gdsc" }, + {} +}; + +static struct platform_driver gdsc_driver = { + .probe = gdsc_probe, + .remove = __devexit_p(gdsc_remove), + .driver = { + .name = "gdsc", + .of_match_table = gdsc_match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init gdsc_init(void) +{ + return platform_driver_register(&gdsc_driver); +} +subsys_initcall(gdsc_init); + +static void __exit gdsc_exit(void) +{ + platform_driver_unregister(&gdsc_driver); +} +module_exit(gdsc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Copper GDSC power rail regulator driver"); diff --git a/arch/arm/mach-msm/gpio.h b/arch/arm/mach-msm/gpio.h new file mode 100644 index 00000000000..59ee8f83270 --- /dev/null +++ b/arch/arm/mach-msm/gpio.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_GPIO_H_ +#define _ARCH_ARM_MACH_MSM_GPIO_H_ + +void msm_gpio_enter_sleep(int from_idle); +void msm_gpio_exit_sleep(void); + +/* Locate the GPIO_OUT register for the given GPIO and return its address + * and the bit position of the gpio's bit within the register. + * + * This function is used by gpiomux-v1 in order to support output transitions. + */ +void msm_gpio_find_out(const unsigned gpio, void __iomem **out, + unsigned *offset); + +#endif diff --git a/arch/arm/mach-msm/gpiomux-7x27.c b/arch/arm/mach-msm/gpiomux-7x27.c new file mode 100644 index 00000000000..822cd0419a3 --- /dev/null +++ b/arch/arm/mach-msm/gpiomux-7x27.c @@ -0,0 +1,20 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +static int __init gpiomux_init(void) +{ + return msm_gpiomux_init(NR_GPIO_IRQS); +} +postcore_initcall(gpiomux_init); diff --git a/arch/arm/mach-msm/gpiomux-7x30.c b/arch/arm/mach-msm/gpiomux-7x30.c new file mode 100644 index 00000000000..822cd0419a3 --- /dev/null +++ b/arch/arm/mach-msm/gpiomux-7x30.c @@ -0,0 +1,20 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +static int __init gpiomux_init(void) +{ + return msm_gpiomux_init(NR_GPIO_IRQS); +} +postcore_initcall(gpiomux_init); diff --git a/arch/arm/mach-msm/gpiomux-8x50.c b/arch/arm/mach-msm/gpiomux-8x50.c index f7a4ea593c9..822cd0419a3 100644 --- a/arch/arm/mach-msm/gpiomux-8x50.c +++ b/arch/arm/mach-msm/gpiomux-8x50.c @@ -8,44 +8,13 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ -#include "gpiomux.h" +#include +#include +#include -#if defined(CONFIG_MMC_MSM) || defined(CONFIG_MMC_MSM_MODULE) - #define SDCC_DAT_0_3_CMD_ACTV_CFG (GPIOMUX_VALID | GPIOMUX_PULL_UP\ - | GPIOMUX_FUNC_1 | GPIOMUX_DRV_8MA) - #define SDCC_CLK_ACTV_CFG (GPIOMUX_VALID | GPIOMUX_PULL_NONE\ - | GPIOMUX_FUNC_1 | GPIOMUX_DRV_8MA) -#else - #define SDCC_DAT_0_3_CMD_ACTV_CFG 0 - #define SDCC_CLK_ACTV_CFG 0 -#endif - -#define SDC1_SUSPEND_CONFIG (GPIOMUX_VALID | GPIOMUX_PULL_DOWN\ - | GPIOMUX_FUNC_GPIO | GPIOMUX_DRV_2MA) - -struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = { - [86] = { /* UART3 RX */ - .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN | - GPIOMUX_FUNC_1 | GPIOMUX_VALID, - }, - [87] = { /* UART3 TX */ - .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN | - GPIOMUX_FUNC_1 | GPIOMUX_VALID, - }, - /* SDC1 data[3:0] & CMD */ - [51 ... 55] = { - .active = SDCC_DAT_0_3_CMD_ACTV_CFG, - .suspended = SDC1_SUSPEND_CONFIG - }, - /* SDC1 CLK */ - [56] = { - .active = SDCC_CLK_ACTV_CFG, - .suspended = SDC1_SUSPEND_CONFIG - }, -}; +static int __init gpiomux_init(void) +{ + return msm_gpiomux_init(NR_GPIO_IRQS); +} +postcore_initcall(gpiomux_init); diff --git a/arch/arm/mach-msm/gpiomux-8x60.c b/arch/arm/mach-msm/gpiomux-8x60.c index 7b380b31bd0..c23a41c7655 100644 --- a/arch/arm/mach-msm/gpiomux-8x60.c +++ b/arch/arm/mach-msm/gpiomux-8x60.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,12 +8,1724 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ -#include "gpiomux.h" +#include +#include +#include +#include +#include "gpiomux-8x60.h" -struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {}; +static struct gpiomux_setting console_uart = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +/* The SPI configurations apply to GSBI1 and GSBI10 */ +static struct gpiomux_setting spi_active = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting spi_suspended_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting spi_suspended_cs_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +/* This I2C active configuration applies to GSBI3 and GSBI4 */ +static struct gpiomux_setting i2c_active = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting i2c_active_gsbi7 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +/* This I2C suspended configuration applies to GSBI3, GSBI4 and GSBI7 */ +static struct gpiomux_setting i2c_suspended_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting gsbi8 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting ps_hold = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_12MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting msm_snddev_active_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting msm_snddev_suspend_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ebi2_a_d = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ebi2_oe = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ebi2_we = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ebi2_cs2 = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ebi2_cs3 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) +static struct gpiomux_setting ebi2_cs4 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; +#endif + +static struct gpiomux_setting ebi2_adv = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) +static struct gpiomux_setting usb_isp1763_actv_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting usb_isp1763_susp_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; +#endif + +static struct gpiomux_setting sdcc1_dat_0_3_cmd_actv_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_10MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc1_dat_4_7_cmd_actv_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_10MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc1_clk_actv_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdcc1_suspend_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc2_dat_0_3_cmd_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_10MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc2_dat_4_7_cmd_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_10MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc2_clk_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdcc2_suspend_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc5_dat_0_3_cmd_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_10MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting sdcc5_clk_actv_cfg = { + .func = GPIOMUX_FUNC_2, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting sdcc5_suspend_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting aux_pcm_active_config = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting aux_pcm_suspend_config = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting uart1dm_active = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting uart1dm_suspended = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mi2s_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mi2s_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting lcdc_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting lcdc_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdp_vsync_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting hdmi_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_status_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_status_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cam_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_sync_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_sync_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting tm_active = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting tm_suspended = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting tma_active = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_6MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting ts_suspended = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdp_vsync_active_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting hdmi_active_1_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting hdmi_active_2_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting hdmi_active_3_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting pmic_suspended_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cam_active_1_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cam_active_2_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting cam_active_3_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting cam_active_4_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting cam_active_5_cfg = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_4MA, + .pull = GPIOMUX_PULL_NONE, +}; + +#ifdef CONFIG_MSM_GSBI9_UART +static struct gpiomux_setting uart9dm_active = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA , + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting gsbi9 = { + .func = GPIOMUX_FUNC_1, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; +#endif + +static struct gpiomux_setting ap2mdm_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_status_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_vfr_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_UP, +}; + +static struct gpiomux_setting mdm2ap_vfr_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting mdm2ap_errfatal_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_16MA, + .pull = GPIOMUX_PULL_DOWN, +}; + +static struct gpiomux_setting ap2mdm_kpdpwr_n_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_8MA, + .pull = GPIOMUX_PULL_NONE, +}; + + +static struct gpiomux_setting mdm2ap_vddmin_active_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct gpiomux_setting mdm2ap_vddmin_suspend_cfg = { + .func = GPIOMUX_FUNC_GPIO, + .drv = GPIOMUX_DRV_2MA, + .pull = GPIOMUX_PULL_NONE, +}; + +static struct msm_gpiomux_config msm8x60_gsbi_configs[] __initdata = { + { + .gpio = 33, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 34, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 35, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_cs_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 36, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 43, + .settings = { + [GPIOMUX_SUSPENDED] = &i2c_suspended_config, + [GPIOMUX_ACTIVE] = &i2c_active, + }, + }, + { + .gpio = 44, + .settings = { + [GPIOMUX_SUSPENDED] = &i2c_suspended_config, + [GPIOMUX_ACTIVE] = &i2c_active, + }, + }, + { + .gpio = 47, + .settings = { + [GPIOMUX_SUSPENDED] = &i2c_suspended_config, + [GPIOMUX_ACTIVE] = &i2c_active, + }, + }, + { + .gpio = 48, + .settings = { + [GPIOMUX_SUSPENDED] = &i2c_suspended_config, + [GPIOMUX_ACTIVE] = &i2c_active, + }, + }, + { + .gpio = 59, + .settings = { + [GPIOMUX_SUSPENDED] = &i2c_suspended_config, + [GPIOMUX_ACTIVE] = &i2c_active_gsbi7, + }, + }, + { + .gpio = 60, + .settings = { + [GPIOMUX_SUSPENDED] = &i2c_suspended_config, + [GPIOMUX_ACTIVE] = &i2c_active_gsbi7, + }, + }, + { + .gpio = 64, + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi8, + }, + }, + { + .gpio = 65, + .settings = { + [GPIOMUX_SUSPENDED] = &gsbi8, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_fluid_gsbi_configs[] __initdata = { + { + .gpio = 70, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 72, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_cs_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, + { + .gpio = 73, + .settings = { + [GPIOMUX_SUSPENDED] = &spi_suspended_config, + [GPIOMUX_ACTIVE] = &spi_active, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_ebi2_configs[] __initdata = { + { + .gpio = 40, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_cs2, + }, + }, + { + .gpio = 123, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 124, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 125, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 126, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 127, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 128, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 129, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 130, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + /* ISP VDD_3V3_EN */ + { + .gpio = 132, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_cs4, + }, + }, +#endif + { + .gpio = 133, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_cs3, + }, + }, + { + .gpio = 135, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 136, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 137, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 138, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 139, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 140, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 141, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 142, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 143, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 144, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 145, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 146, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 147, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 148, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 149, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 150, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_a_d, + }, + }, + { + .gpio = 151, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_oe, + }, + }, + { + .gpio = 153, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_adv, + }, + }, + { + .gpio = 157, + .settings = { + [GPIOMUX_SUSPENDED] = &ebi2_we, + }, + }, +}; + +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) +static struct msm_gpiomux_config msm8x60_isp_usb_configs[] __initdata = { + { + .gpio = 117, + .settings = { + [GPIOMUX_ACTIVE] = &usb_isp1763_actv_cfg, + [GPIOMUX_SUSPENDED] = &usb_isp1763_susp_cfg, + }, + }, + { + .gpio = 152, + .settings = { + [GPIOMUX_ACTIVE] = &usb_isp1763_actv_cfg, + [GPIOMUX_SUSPENDED] = &usb_isp1763_susp_cfg, + }, + }, + +}; +#endif + +static struct msm_gpiomux_config msm8x60_uart_configs[] __initdata = { + { /* UARTDM_TX */ + .gpio = 53, + .settings = { + [GPIOMUX_ACTIVE] = &uart1dm_active, + [GPIOMUX_SUSPENDED] = &uart1dm_suspended, + }, + }, + { /* UARTDM_RX */ + .gpio = 54, + .settings = { + [GPIOMUX_ACTIVE] = &uart1dm_active, + [GPIOMUX_SUSPENDED] = &uart1dm_suspended, + }, + }, + { /* UARTDM_CTS */ + .gpio = 55, + .settings = { + [GPIOMUX_ACTIVE] = &uart1dm_active, + [GPIOMUX_SUSPENDED] = &uart1dm_suspended, + }, + }, + { /* UARTDM_RFR */ + .gpio = 56, + .settings = { + [GPIOMUX_ACTIVE] = &uart1dm_active, + [GPIOMUX_SUSPENDED] = &uart1dm_suspended, + }, + }, + { + .gpio = 115, + .settings = { + [GPIOMUX_SUSPENDED] = &console_uart, + }, + }, + { + .gpio = 116, + .settings = { + [GPIOMUX_SUSPENDED] = &console_uart, + }, + }, +#if !defined(CONFIG_USB_PEHCI_HCD) && !defined(CONFIG_USB_PEHCI_HCD_MODULE) + /* USB ISP1763 may also use 117 GPIO */ + { + .gpio = 117, + .settings = { + [GPIOMUX_SUSPENDED] = &console_uart, + }, + }, +#endif + { + .gpio = 118, + .settings = { + [GPIOMUX_SUSPENDED] = &console_uart, + }, + }, +}; + +#ifdef CONFIG_MSM_GSBI9_UART +static struct msm_gpiomux_config msm8x60_charm_uart_configs[] __initdata = { + { /* UART9DM RX */ + .gpio = 66, + .settings = { + [GPIOMUX_ACTIVE] = &uart9dm_active, + [GPIOMUX_SUSPENDED] = &gsbi9, + }, + }, + { /* UART9DM TX */ + .gpio = 67, + .settings = { + [GPIOMUX_ACTIVE] = &uart9dm_active, + [GPIOMUX_SUSPENDED] = &gsbi9, + }, + }, +}; +#endif + +static struct msm_gpiomux_config msm8x60_ts_configs[] __initdata = { + { + /* TS_ATTN */ + .gpio = 58, + .settings = { + [GPIOMUX_SUSPENDED] = &ts_suspended, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_tmg200_configs[] __initdata = { + { + .gpio = 61, + .settings = { + [GPIOMUX_ACTIVE] = &tm_active, + [GPIOMUX_SUSPENDED] = &tm_suspended, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_tma300_configs[] __initdata = { + { + .gpio = 61, + .settings = { + [GPIOMUX_ACTIVE] = &tma_active, + [GPIOMUX_SUSPENDED] = &tm_suspended, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_aux_pcm_configs[] __initdata = { + { + .gpio = 111, + .settings = { + [GPIOMUX_ACTIVE] = &aux_pcm_active_config, + [GPIOMUX_SUSPENDED] = &aux_pcm_suspend_config, + }, + }, + { + .gpio = 112, + .settings = { + [GPIOMUX_ACTIVE] = &aux_pcm_active_config, + [GPIOMUX_SUSPENDED] = &aux_pcm_suspend_config, + }, + }, + { + .gpio = 113, + .settings = { + [GPIOMUX_ACTIVE] = &aux_pcm_active_config, + [GPIOMUX_SUSPENDED] = &aux_pcm_suspend_config, + }, + }, + { + .gpio = 114, + .settings = { + [GPIOMUX_ACTIVE] = &aux_pcm_active_config, + [GPIOMUX_SUSPENDED] = &aux_pcm_suspend_config, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_sdc_configs[] __initdata = { + /* SDCC1 data[0] */ + { + .gpio = 159, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[1] */ + { + .gpio = 160, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[2] */ + { + .gpio = 161, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[3] */ + { + .gpio = 162, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[4] */ + { + .gpio = 163, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[5] */ + { + .gpio = 164, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[6] */ + { + .gpio = 165, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 data[7] */ + { + .gpio = 166, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 CLK */ + { + .gpio = 167, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_clk_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, + /* SDCC1 CMD */ + { + .gpio = 168, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc1_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc1_suspend_config, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_charm_sdc_configs[] __initdata = { + /* SDCC5 cmd */ + { + .gpio = 95, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc5_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc5_suspend_config, + }, + }, + /* SDCC5 data[3]*/ + { + .gpio = 96, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc5_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc5_suspend_config, + }, + }, + /* SDCC5 clk */ + { + .gpio = 97, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc5_clk_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc5_suspend_config, + }, + }, + /* SDCC5 data[2]*/ + { + .gpio = 98, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc5_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc5_suspend_config, + }, + }, + /* SDCC5 data[1]*/ + { + .gpio = 99, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc5_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc5_suspend_config, + }, + }, + /* SDCC5 data[0]*/ + { + .gpio = 100, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc5_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc5_suspend_config, + }, + }, + /* MDM2AP_SYNC */ + { + .gpio = 129, + .settings = { + [GPIOMUX_ACTIVE] = &mdm2ap_sync_active_cfg, + [GPIOMUX_SUSPENDED] = &mdm2ap_sync_suspend_cfg, + }, + }, + + /* MDM2AP_VDDMIN */ + { + .gpio = 140, + .settings = { + [GPIOMUX_ACTIVE] = &mdm2ap_vddmin_active_cfg, + [GPIOMUX_SUSPENDED] = &mdm2ap_vddmin_suspend_cfg, + }, + }, + /* SDCC2 data[0] */ + { + .gpio = 143, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[1] */ + { + .gpio = 144, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[2] */ + { + .gpio = 145, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[3] */ + { + .gpio = 146, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[4] */ + { + .gpio = 147, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[5] */ + { + .gpio = 148, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[6] */ + { + .gpio = 149, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 data[7] */ + { + .gpio = 150, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_4_7_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + /* SDCC2 CMD */ + { + .gpio = 151, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_dat_0_3_cmd_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, + + /* SDCC2 CLK */ + { + .gpio = 152, + .settings = { + [GPIOMUX_ACTIVE] = &sdcc2_clk_actv_cfg, + [GPIOMUX_SUSPENDED] = &sdcc2_suspend_config, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_snd_configs[] __initdata = { + { + .gpio = 108, + .settings = { + [GPIOMUX_ACTIVE] = &msm_snddev_active_config, + [GPIOMUX_SUSPENDED] = &msm_snddev_suspend_config, + }, + }, + { + .gpio = 109, + .settings = { + [GPIOMUX_ACTIVE] = &msm_snddev_active_config, + [GPIOMUX_SUSPENDED] = &msm_snddev_suspend_config, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_mi2s_configs[] __initdata = { + /* MI2S WS */ + { + .gpio = 101, + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_active_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg, + }, + }, + /* MI2S SCLK */ + { + .gpio = 102, + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_active_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg, + }, + }, + /* MI2S MCLK */ + { + .gpio = 103, + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_active_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg, + }, + }, + /* MI2S SD3 */ + { + .gpio = 107, + .settings = { + [GPIOMUX_ACTIVE] = &mi2s_active_cfg, + [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_lcdc_configs[] __initdata = { + /* lcdc_pclk */ + { + .gpio = 0, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_hsync */ + { + .gpio = 1, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_vsync */ + { + .gpio = 2, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_den */ + { + .gpio = 3, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red7 */ + { + .gpio = 4, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red6 */ + { + .gpio = 5, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red5 */ + { + .gpio = 6, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red4 */ + { + .gpio = 7, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red3 */ + { + .gpio = 8, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red2 */ + { + .gpio = 9, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red1 */ + { + .gpio = 10, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_red0 */ + { + .gpio = 11, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn7 */ + { + .gpio = 12, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn6 */ + { + .gpio = 13, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn5 */ + { + .gpio = 14, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn4 */ + { + .gpio = 15, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn3 */ + { + .gpio = 16, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn2 */ + { + .gpio = 17, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn1 */ + { + .gpio = 18, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_grn0 */ + { + .gpio = 19, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu7 */ + { + .gpio = 20, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu6 */ + { + .gpio = 21, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu5 */ + { + .gpio = 22, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu4 */ + { + .gpio = 23, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu3 */ + { + .gpio = 24, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu2 */ + { + .gpio = 25, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu1 */ + { + .gpio = 26, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, + /* lcdc_blu0 */ + { + .gpio = 27, + .settings = { + [GPIOMUX_ACTIVE] = &lcdc_active_cfg, + [GPIOMUX_SUSPENDED] = &lcdc_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_mdp_vsync_configs[] __initdata = { + { + .gpio = 28, + .settings = { + [GPIOMUX_ACTIVE] = &mdp_vsync_active_cfg, + [GPIOMUX_SUSPENDED] = &mdp_vsync_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_hdmi_configs[] __initdata = { + { + .gpio = 169, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_1_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 170, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_2_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 171, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_2_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, + { + .gpio = 172, + .settings = { + [GPIOMUX_ACTIVE] = &hdmi_active_3_cfg, + [GPIOMUX_SUSPENDED] = &hdmi_suspend_cfg, + }, + }, +}; + +/* Because PMIC drivers do not use gpio-management routines and PMIC + * gpios must never sleep, a "good enough" config is obtained by placing + * the active config in the 'suspended' slot and leaving the active + * config invalid: the suspended config will be installed at boot + * and never replaced. + */ + +static struct msm_gpiomux_config msm8x60_pmic_configs[] __initdata = { + { + .gpio = 88, + .settings = { + [GPIOMUX_SUSPENDED] = &pmic_suspended_cfg, + }, + }, + { + .gpio = 91, + .settings = { + [GPIOMUX_SUSPENDED] = &pmic_suspended_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_common_configs[] __initdata = { + /* MDM2AP_STATUS */ + { + .gpio = 77, + .settings = { + [GPIOMUX_ACTIVE] = &mdm2ap_status_active_cfg, + [GPIOMUX_SUSPENDED] = &mdm2ap_status_suspend_cfg, + }, + }, + /* PS_HOLD */ + { + .gpio = 92, + .settings = { + [GPIOMUX_SUSPENDED] = &ps_hold, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_cam_configs[] __initdata = { + { + .gpio = 29, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_2_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 30, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_1_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 31, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_2_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 32, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_5_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 42, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_2_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 47, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_3_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 48, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_3_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 105, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_4_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, + { + .gpio = 106, + .settings = { + [GPIOMUX_ACTIVE] = &cam_active_4_cfg, + [GPIOMUX_SUSPENDED] = &cam_suspend_cfg, + }, + }, +}; + +static struct msm_gpiomux_config msm8x60_charm_configs[] __initdata = { + /* AP2MDM_WAKEUP */ + { + .gpio = 135, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* MDM2AP_VFR */ + { + .gpio = 94, + .settings = { + [GPIOMUX_ACTIVE] = &mdm2ap_vfr_active_cfg, + [GPIOMUX_SUSPENDED] = &mdm2ap_vfr_suspend_cfg, + } + }, + /* AP2MDM_STATUS */ + { + .gpio = 136, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* MDM2AP_STATUS */ + { + .gpio = 134, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_status_cfg, + } + }, + /* MDM2AP_WAKEUP */ + { + .gpio = 40, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* MDM2AP_ERRFATAL */ + { + .gpio = 133, + .settings = { + [GPIOMUX_SUSPENDED] = &mdm2ap_errfatal_cfg, + } + }, + /* AP2MDM_ERRFATAL */ + { + .gpio = 93, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_cfg, + } + }, + /* AP2MDM_KPDPWR_N */ + { + .gpio = 132, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_kpdpwr_n_cfg, + } + }, + /* AP2MDM_PMIC_RESET_N */ + { + .gpio = 131, + .settings = { + [GPIOMUX_SUSPENDED] = &ap2mdm_kpdpwr_n_cfg, + } + } +}; + +struct msm_gpiomux_configs +msm8x60_surf_ffa_gpiomux_cfgs[] __initdata = { + {msm8x60_gsbi_configs, ARRAY_SIZE(msm8x60_gsbi_configs)}, + {msm8x60_ebi2_configs, ARRAY_SIZE(msm8x60_ebi2_configs)}, + {msm8x60_uart_configs, ARRAY_SIZE(msm8x60_uart_configs)}, +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + {msm8x60_isp_usb_configs, ARRAY_SIZE(msm8x60_isp_usb_configs)}, +#endif + {msm8x60_ts_configs, ARRAY_SIZE(msm8x60_ts_configs)}, + {msm8x60_aux_pcm_configs, ARRAY_SIZE(msm8x60_aux_pcm_configs)}, + {msm8x60_sdc_configs, ARRAY_SIZE(msm8x60_sdc_configs)}, + {msm8x60_snd_configs, ARRAY_SIZE(msm8x60_snd_configs)}, + {msm8x60_mi2s_configs, ARRAY_SIZE(msm8x60_mi2s_configs)}, + {msm8x60_lcdc_configs, ARRAY_SIZE(msm8x60_lcdc_configs)}, + {msm8x60_mdp_vsync_configs, ARRAY_SIZE(msm8x60_mdp_vsync_configs)}, + {msm8x60_hdmi_configs, ARRAY_SIZE(msm8x60_hdmi_configs)}, + {msm8x60_pmic_configs, ARRAY_SIZE(msm8x60_pmic_configs)}, + {msm8x60_common_configs, ARRAY_SIZE(msm8x60_common_configs)}, + {msm8x60_cam_configs, ARRAY_SIZE(msm8x60_cam_configs)}, + {msm8x60_tmg200_configs, ARRAY_SIZE(msm8x60_tmg200_configs)}, + {NULL, 0}, +}; + +struct msm_gpiomux_configs +msm8x60_fluid_gpiomux_cfgs[] __initdata = { + {msm8x60_gsbi_configs, ARRAY_SIZE(msm8x60_gsbi_configs)}, + {msm8x60_fluid_gsbi_configs, ARRAY_SIZE(msm8x60_fluid_gsbi_configs)}, + {msm8x60_ebi2_configs, ARRAY_SIZE(msm8x60_ebi2_configs)}, + {msm8x60_uart_configs, ARRAY_SIZE(msm8x60_uart_configs)}, + {msm8x60_ts_configs, ARRAY_SIZE(msm8x60_ts_configs)}, + {msm8x60_aux_pcm_configs, ARRAY_SIZE(msm8x60_aux_pcm_configs)}, + {msm8x60_sdc_configs, ARRAY_SIZE(msm8x60_sdc_configs)}, + {msm8x60_snd_configs, ARRAY_SIZE(msm8x60_snd_configs)}, + {msm8x60_mi2s_configs, ARRAY_SIZE(msm8x60_mi2s_configs)}, + {msm8x60_lcdc_configs, ARRAY_SIZE(msm8x60_lcdc_configs)}, + {msm8x60_mdp_vsync_configs, ARRAY_SIZE(msm8x60_mdp_vsync_configs)}, + {msm8x60_hdmi_configs, ARRAY_SIZE(msm8x60_hdmi_configs)}, + {msm8x60_pmic_configs, ARRAY_SIZE(msm8x60_pmic_configs)}, + {msm8x60_common_configs, ARRAY_SIZE(msm8x60_common_configs)}, + {msm8x60_cam_configs, ARRAY_SIZE(msm8x60_cam_configs)}, + {msm8x60_tma300_configs, ARRAY_SIZE(msm8x60_tma300_configs)}, + {NULL, 0}, +}; + +struct msm_gpiomux_configs +msm8x60_charm_gpiomux_cfgs[] __initdata = { + {msm8x60_gsbi_configs, ARRAY_SIZE(msm8x60_gsbi_configs)}, + {msm8x60_uart_configs, ARRAY_SIZE(msm8x60_uart_configs)}, +#ifdef CONFIG_MSM_GSBI9_UART + {msm8x60_charm_uart_configs, ARRAY_SIZE(msm8x60_charm_uart_configs)}, +#endif + {msm8x60_ts_configs, ARRAY_SIZE(msm8x60_ts_configs)}, + {msm8x60_aux_pcm_configs, ARRAY_SIZE(msm8x60_aux_pcm_configs)}, + {msm8x60_sdc_configs, ARRAY_SIZE(msm8x60_sdc_configs)}, + {msm8x60_snd_configs, ARRAY_SIZE(msm8x60_snd_configs)}, + {msm8x60_mi2s_configs, ARRAY_SIZE(msm8x60_mi2s_configs)}, + {msm8x60_lcdc_configs, ARRAY_SIZE(msm8x60_lcdc_configs)}, + {msm8x60_mdp_vsync_configs, ARRAY_SIZE(msm8x60_mdp_vsync_configs)}, + {msm8x60_hdmi_configs, ARRAY_SIZE(msm8x60_hdmi_configs)}, + {msm8x60_pmic_configs, ARRAY_SIZE(msm8x60_pmic_configs)}, + {msm8x60_common_configs, ARRAY_SIZE(msm8x60_common_configs)}, + {msm8x60_cam_configs, ARRAY_SIZE(msm8x60_cam_configs)}, + {msm8x60_tmg200_configs, ARRAY_SIZE(msm8x60_tmg200_configs)}, + {msm8x60_charm_sdc_configs, ARRAY_SIZE(msm8x60_charm_sdc_configs)}, + {msm8x60_charm_configs, ARRAY_SIZE(msm8x60_charm_configs)}, + {NULL, 0}, +}; + +struct msm_gpiomux_configs +msm8x60_dragon_gpiomux_cfgs[] __initdata = { + {msm8x60_gsbi_configs, ARRAY_SIZE(msm8x60_gsbi_configs)}, + {msm8x60_ebi2_configs, ARRAY_SIZE(msm8x60_ebi2_configs)}, + {msm8x60_uart_configs, ARRAY_SIZE(msm8x60_uart_configs)}, +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) + {msm8x60_isp_usb_configs, ARRAY_SIZE(msm8x60_isp_usb_configs)}, +#endif + {msm8x60_ts_configs, ARRAY_SIZE(msm8x60_ts_configs)}, + {msm8x60_aux_pcm_configs, ARRAY_SIZE(msm8x60_aux_pcm_configs)}, + {msm8x60_sdc_configs, ARRAY_SIZE(msm8x60_sdc_configs)}, + {msm8x60_snd_configs, ARRAY_SIZE(msm8x60_snd_configs)}, + {msm8x60_mi2s_configs, ARRAY_SIZE(msm8x60_mi2s_configs)}, + {msm8x60_lcdc_configs, ARRAY_SIZE(msm8x60_lcdc_configs)}, + {msm8x60_mdp_vsync_configs, ARRAY_SIZE(msm8x60_mdp_vsync_configs)}, + {msm8x60_hdmi_configs, ARRAY_SIZE(msm8x60_hdmi_configs)}, + {msm8x60_pmic_configs, ARRAY_SIZE(msm8x60_pmic_configs)}, + {msm8x60_common_configs, ARRAY_SIZE(msm8x60_common_configs)}, + {msm8x60_cam_configs, ARRAY_SIZE(msm8x60_cam_configs)}, + {msm8x60_tmg200_configs, ARRAY_SIZE(msm8x60_tmg200_configs)}, + {NULL, 0}, +}; + +void __init msm8x60_init_gpiomux(struct msm_gpiomux_configs *cfgs) +{ + int rc; + + rc = msm_gpiomux_init(NR_GPIO_IRQS); + if (rc) { + pr_err("%s failure: %d\n", __func__, rc); + return; + } + + while (cfgs->cfg) { + msm_gpiomux_install(cfgs->cfg, cfgs->ncfg); + ++cfgs; + } +} diff --git a/arch/arm/mach-msm/gpiomux-8x60.h b/arch/arm/mach-msm/gpiomux-8x60.h new file mode 100644 index 00000000000..cacd1ba4820 --- /dev/null +++ b/arch/arm/mach-msm/gpiomux-8x60.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_8X60_H +#define __ARCH_ARM_MACH_MSM_GPIOMUX_8X60_H + +void __init msm8x60_init_gpiomux(struct msm_gpiomux_configs *cfgs); + +extern struct msm_gpiomux_configs msm8x60_surf_ffa_gpiomux_cfgs[] __initdata; +extern struct msm_gpiomux_configs msm8x60_fluid_gpiomux_cfgs[] __initdata; +extern struct msm_gpiomux_configs msm8x60_charm_gpiomux_cfgs[] __initdata; +extern struct msm_gpiomux_configs msm8x60_dragon_gpiomux_cfgs[] __initdata; + +#endif diff --git a/arch/arm/mach-msm/gpiomux-v1.c b/arch/arm/mach-msm/gpiomux-v1.c index 27de2abd714..1163669f3fb 100644 --- a/arch/arm/mach-msm/gpiomux-v1.c +++ b/arch/arm/mach-msm/gpiomux-v1.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,23 +8,37 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ +#include #include -#include "gpiomux.h" -#include "proc_comm.h" +#include +#include +#include +#include "gpio.h" -void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val) +void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val) { - unsigned tlmm_config = (val & ~GPIOMUX_CTL_MASK) | - ((gpio & 0x3ff) << 4); + unsigned tlmm_config; unsigned tlmm_disable = 0; + void __iomem *out_reg; + unsigned offset; + uint32_t bits; int rc; + tlmm_config = (val.drv << 17) | + (val.pull << 15) | + ((gpio & 0x3ff) << 4) | + val.func; + if (val.func == GPIOMUX_FUNC_GPIO) { + tlmm_config |= (val.dir > GPIOMUX_IN ? BIT(14) : 0); + msm_gpio_find_out(gpio, &out_reg, &offset); + bits = __raw_readl(out_reg); + if (val.dir == GPIOMUX_OUT_HIGH) + __raw_writel(bits | BIT(offset), out_reg); + else + __raw_writel(bits & ~BIT(offset), out_reg); + } + mb(); rc = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &tlmm_config, &tlmm_disable); if (rc) diff --git a/arch/arm/mach-msm/gpiomux-v1.h b/arch/arm/mach-msm/gpiomux-v1.h index 71d86feba45..7cf4582311d 100644 --- a/arch/arm/mach-msm/gpiomux-v1.h +++ b/arch/arm/mach-msm/gpiomux-v1.h @@ -8,11 +8,6 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_V1_H #define __ARCH_ARM_MACH_MSM_GPIOMUX_V1_H diff --git a/arch/arm/mach-msm/gpiomux-v2.c b/arch/arm/mach-msm/gpiomux-v2.c index 273396d2b12..ee1e17a916f 100644 --- a/arch/arm/mach-msm/gpiomux-v2.c +++ b/arch/arm/mach-msm/gpiomux-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,18 +8,25 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ +#include #include #include -#include "gpiomux.h" +#include -void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val) +#define GPIO_CFG(n) (MSM_TLMM_BASE + 0x1000 + (0x10 * n)) +#define GPIO_IN_OUT(n) (MSM_TLMM_BASE + 0x1004 + (0x10 * n)) + +void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val) { - writel(val & ~GPIOMUX_CTL_MASK, - MSM_TLMM_BASE + 0x1000 + (0x10 * gpio)); + uint32_t bits; + + bits = (val.drv << 6) | (val.func << 2) | val.pull; + if (val.func == GPIOMUX_FUNC_GPIO) { + bits |= val.dir > GPIOMUX_IN ? BIT(9) : 0; + __raw_writel(val.dir == GPIOMUX_OUT_HIGH ? BIT(1) : 0, + GPIO_IN_OUT(gpio)); + } + __raw_writel(bits, GPIO_CFG(gpio)); + mb(); } diff --git a/arch/arm/mach-msm/gpiomux-v2.h b/arch/arm/mach-msm/gpiomux-v2.h index 3bf10e7f038..b200501788b 100644 --- a/arch/arm/mach-msm/gpiomux-v2.h +++ b/arch/arm/mach-msm/gpiomux-v2.h @@ -8,11 +8,6 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_V2_H #define __ARCH_ARM_MACH_MSM_GPIOMUX_V2_H diff --git a/arch/arm/mach-msm/gpiomux.c b/arch/arm/mach-msm/gpiomux.c index 53af21abd15..85936ba672d 100644 --- a/arch/arm/mach-msm/gpiomux.c +++ b/arch/arm/mach-msm/gpiomux.c @@ -8,57 +8,76 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include +#include #include -#include "gpiomux.h" +#include +struct msm_gpiomux_rec { + struct gpiomux_setting *sets[GPIOMUX_NSETTINGS]; + int ref; +}; static DEFINE_SPINLOCK(gpiomux_lock); +static struct msm_gpiomux_rec *msm_gpiomux_recs; +static struct gpiomux_setting *msm_gpiomux_sets; +static unsigned msm_gpiomux_ngpio; -int msm_gpiomux_write(unsigned gpio, - gpiomux_config_t active, - gpiomux_config_t suspended) +int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which, + struct gpiomux_setting *setting, struct gpiomux_setting *old_setting) { - struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio; + struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio; + unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which; unsigned long irq_flags; - gpiomux_config_t setting; + struct gpiomux_setting *new_set; + int status = 0; - if (gpio >= GPIOMUX_NGPIOS) + if (!msm_gpiomux_recs) + return -EFAULT; + + if (gpio >= msm_gpiomux_ngpio) return -EINVAL; spin_lock_irqsave(&gpiomux_lock, irq_flags); - if (active & GPIOMUX_VALID) - cfg->active = active; + if (old_setting) { + if (rec->sets[which] == NULL) + status = 1; + else + *old_setting = *(rec->sets[which]); + } - if (suspended & GPIOMUX_VALID) - cfg->suspended = suspended; + if (setting) { + msm_gpiomux_sets[set_slot] = *setting; + rec->sets[which] = &msm_gpiomux_sets[set_slot]; + } else { + rec->sets[which] = NULL; + } - setting = cfg->ref ? active : suspended; - if (setting & GPIOMUX_VALID) - __msm_gpiomux_write(gpio, setting); + new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] : + rec->sets[GPIOMUX_SUSPENDED]; + if (new_set) + __msm_gpiomux_write(gpio, *new_set); spin_unlock_irqrestore(&gpiomux_lock, irq_flags); - return 0; + return status; } EXPORT_SYMBOL(msm_gpiomux_write); int msm_gpiomux_get(unsigned gpio) { - struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio; + struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio; unsigned long irq_flags; - if (gpio >= GPIOMUX_NGPIOS) + if (!msm_gpiomux_recs) + return -EFAULT; + + if (gpio >= msm_gpiomux_ngpio) return -EINVAL; spin_lock_irqsave(&gpiomux_lock, irq_flags); - if (cfg->ref++ == 0 && cfg->active & GPIOMUX_VALID) - __msm_gpiomux_write(gpio, cfg->active); + if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE]) + __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]); spin_unlock_irqrestore(&gpiomux_lock, irq_flags); return 0; } @@ -66,31 +85,66 @@ EXPORT_SYMBOL(msm_gpiomux_get); int msm_gpiomux_put(unsigned gpio) { - struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio; + struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio; unsigned long irq_flags; - if (gpio >= GPIOMUX_NGPIOS) + if (!msm_gpiomux_recs) + return -EFAULT; + + if (gpio >= msm_gpiomux_ngpio) return -EINVAL; spin_lock_irqsave(&gpiomux_lock, irq_flags); - BUG_ON(cfg->ref == 0); - if (--cfg->ref == 0 && cfg->suspended & GPIOMUX_VALID) - __msm_gpiomux_write(gpio, cfg->suspended); + BUG_ON(rec->ref == 0); + if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED]) + __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]); spin_unlock_irqrestore(&gpiomux_lock, irq_flags); return 0; } EXPORT_SYMBOL(msm_gpiomux_put); -static int __init gpiomux_init(void) +int msm_gpiomux_init(size_t ngpio) { - unsigned n; + if (!ngpio) + return -EINVAL; - for (n = 0; n < GPIOMUX_NGPIOS; ++n) { - msm_gpiomux_configs[n].ref = 0; - if (!(msm_gpiomux_configs[n].suspended & GPIOMUX_VALID)) - continue; - __msm_gpiomux_write(n, msm_gpiomux_configs[n].suspended); + if (msm_gpiomux_recs) + return -EPERM; + + msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio, + GFP_KERNEL); + if (!msm_gpiomux_recs) + return -ENOMEM; + + /* There is no need to zero this memory, as clients will be blindly + * installing settings on top of it. + */ + msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio * + GPIOMUX_NSETTINGS, GFP_KERNEL); + if (!msm_gpiomux_sets) { + kfree(msm_gpiomux_recs); + msm_gpiomux_recs = NULL; + return -ENOMEM; } + + msm_gpiomux_ngpio = ngpio; + return 0; } -postcore_initcall(gpiomux_init); +EXPORT_SYMBOL(msm_gpiomux_init); + +void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs) +{ + unsigned c, s; + int rc; + + for (c = 0; c < nconfigs; ++c) { + for (s = 0; s < GPIOMUX_NSETTINGS; ++s) { + rc = msm_gpiomux_write(configs[c].gpio, s, + configs[c].settings[s], NULL); + if (rc) + pr_err("%s: write failure: %d\n", __func__, rc); + } + } +} +EXPORT_SYMBOL(msm_gpiomux_install); diff --git a/arch/arm/mach-msm/gpiomux.h b/arch/arm/mach-msm/gpiomux.h deleted file mode 100644 index 00459f6ee13..00000000000 --- a/arch/arm/mach-msm/gpiomux.h +++ /dev/null @@ -1,99 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ -#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_H -#define __ARCH_ARM_MACH_MSM_GPIOMUX_H - -#include -#include -#include - -#if defined(CONFIG_MSM_V2_TLMM) -#include "gpiomux-v2.h" -#else -#include "gpiomux-v1.h" -#endif - -/** - * struct msm_gpiomux_config: gpiomux settings for one gpio line. - * - * A complete gpiomux config is the bitwise-or of a drive-strength, - * function, and pull. For functions other than GPIO, the OE - * is hard-wired according to the function. For GPIO mode, - * OE is controlled by gpiolib. - * - * Available settings differ by target; see the gpiomux header - * specific to your target arch for available configurations. - * - * @active: The configuration to be installed when the line is - * active, or its reference count is > 0. - * @suspended: The configuration to be installed when the line - * is suspended, or its reference count is 0. - * @ref: The reference count of the line. For internal use of - * the gpiomux framework only. - */ -struct msm_gpiomux_config { - gpiomux_config_t active; - gpiomux_config_t suspended; - unsigned ref; -}; - -/** - * @GPIOMUX_VALID: If set, the config field contains 'good data'. - * The absence of this bit will prevent the gpiomux - * system from applying the configuration under all - * circumstances. - */ -enum { - GPIOMUX_VALID = BIT(sizeof(gpiomux_config_t) * BITS_PER_BYTE - 1), - GPIOMUX_CTL_MASK = GPIOMUX_VALID, -}; - -#ifdef CONFIG_MSM_GPIOMUX - -/* Each architecture must provide its own instance of this table. - * To avoid having gpiomux manage any given gpio, one or both of - * the entries can avoid setting GPIOMUX_VALID - the absence - * of that flag will prevent the configuration from being applied - * during state transitions. - */ -extern struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS]; - -/* Install a new configuration to the gpio line. To avoid overwriting - * a configuration, leave the VALID bit out. - */ -int msm_gpiomux_write(unsigned gpio, - gpiomux_config_t active, - gpiomux_config_t suspended); - -/* Architecture-internal function for use by the framework only. - * This function can assume the following: - * - the gpio value has passed a bounds-check - * - the gpiomux spinlock has been obtained - * - * This function is not for public consumption. External users - * should use msm_gpiomux_write. - */ -void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val); -#else -static inline int msm_gpiomux_write(unsigned gpio, - gpiomux_config_t active, - gpiomux_config_t suspended) -{ - return -ENOSYS; -} -#endif -#endif diff --git a/arch/arm/mach-msm/gss-8064.c b/arch/arm/mach-msm/gss-8064.c new file mode 100644 index 00000000000..126f8e0aefc --- /dev/null +++ b/arch/arm/mach-msm/gss-8064.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "modem_notifier.h" +#include "ramdump.h" + +static struct gss_8064_data { + struct miscdevice gss_dev; + void *pil_handle; + void *gss_ramdump_dev; + void *smem_ramdump_dev; +} gss_data; + +static int crash_shutdown; + +#define MAX_SSR_REASON_LEN 81U + +static void log_gss_sfr(void) +{ + u32 size; + char *smem_reason, reason[MAX_SSR_REASON_LEN]; + + smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size); + if (!smem_reason || !size) { + pr_err("GSS subsystem failure reason: (unknown, smem_get_entry failed).\n"); + return; + } + if (!smem_reason[0]) { + pr_err("GSS subsystem failure reason: (unknown, init string found).\n"); + return; + } + + size = min(size, MAX_SSR_REASON_LEN-1); + memcpy(reason, smem_reason, size); + reason[size] = '\0'; + pr_err("GSS subsystem failure reason: %s.\n", reason); + + smem_reason[0] = '\0'; + wmb(); +} + +static void gss_fatal_fn(struct work_struct *work) +{ + uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD; + uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR | + SMSM_SYSTEM_PWRDWN_USR; + uint32_t gss_state; + + pr_err("Watchdog bite received from GSS!\n"); + + gss_state = smsm_get_state(SMSM_MODEM_STATE); + + if (gss_state & panic_smsm_states) { + + pr_err("GSS SMSM state changed to SMSM_RESET.\n" + "Probable err_fatal on the GSS. " + "Calling subsystem restart...\n"); + log_gss_sfr(); + subsystem_restart("gss"); + + } else if (gss_state & reset_smsm_states) { + + pr_err("%s: User-invoked system reset/powerdown. " + "Resetting the SoC now.\n", + __func__); + kernel_restart(NULL); + } else { + /* TODO: Bus unlock code/sequence goes _here_ */ + log_gss_sfr(); + subsystem_restart("gss"); + } +} + +static DECLARE_WORK(gss_fatal_work, gss_fatal_fn); + +static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state) +{ + /* Ignore if we're the one that set SMSM_RESET */ + if (crash_shutdown) + return; + + if (new_state & SMSM_RESET) { + pr_err("GSS SMSM state changed to SMSM_RESET.\n" + "Probable err_fatal on the GSS. " + "Calling subsystem restart...\n"); + log_gss_sfr(); + subsystem_restart("gss"); + } +} + +#define Q6_FW_WDOG_ENABLE 0x08882024 +#define Q6_SW_WDOG_ENABLE 0x08982024 +static int gss_shutdown(const struct subsys_data *subsys) +{ + pil_force_shutdown("gss"); + disable_irq_nosync(GSS_A5_WDOG_EXPIRED); + + return 0; +} + +static int gss_powerup(const struct subsys_data *subsys) +{ + pil_force_boot("gss"); + enable_irq(GSS_A5_WDOG_EXPIRED); + return 0; +} + +void gss_crash_shutdown(const struct subsys_data *subsys) +{ + crash_shutdown = 1; + smsm_reset_modem(SMSM_RESET); +} + +/* FIXME: Get address, size from PIL */ +static struct ramdump_segment gss_segments[] = { + {0x89000000, 0x00D00000} +}; + +static struct ramdump_segment smem_segments[] = { + {0x80000000, 0x00200000}, +}; + +static int gss_ramdump(int enable, + const struct subsys_data *crashed_subsys) +{ + int ret = 0; + + if (enable) { + ret = do_ramdump(gss_data.gss_ramdump_dev, gss_segments, + ARRAY_SIZE(gss_segments)); + + if (ret < 0) { + pr_err("Unable to dump gss memory (rc = %d).\n", + ret); + goto out; + } + + ret = do_ramdump(gss_data.smem_ramdump_dev, smem_segments, + ARRAY_SIZE(smem_segments)); + + if (ret < 0) { + pr_err("Unable to dump smem memory (rc = %d).\n", ret); + goto out; + } + } + +out: + return ret; +} + +static irqreturn_t gss_wdog_bite_irq(int irq, void *dev_id) +{ + schedule_work(&gss_fatal_work); + disable_irq_nosync(GSS_A5_WDOG_EXPIRED); + + return IRQ_HANDLED; +} + +static struct subsys_data gss_8064 = { + .name = "gss", + .shutdown = gss_shutdown, + .powerup = gss_powerup, + .ramdump = gss_ramdump, + .crash_shutdown = gss_crash_shutdown +}; + +static int gss_subsystem_restart_init(void) +{ + return ssr_register_subsystem(&gss_8064); +} + +static int gss_open(struct inode *inode, struct file *filep) +{ + void *ret; + gss_data.pil_handle = ret = pil_get("gss"); + if (!ret) + pr_debug("%s - pil_get returned NULL\n", __func__); + return 0; +} + +static int gss_release(struct inode *inode, struct file *filep) +{ + pil_put(gss_data.pil_handle); + pr_debug("%s pil_put called on GSS\n", __func__); + return 0; +} + +const struct file_operations gss_file_ops = { + .open = gss_open, + .release = gss_release, +}; + +static int __init gss_8064_init(void) +{ + int ret; + + if (!cpu_is_apq8064()) + return -ENODEV; + + ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET, + smsm_state_cb, 0); + + if (ret < 0) + pr_err("%s: Unable to register SMSM callback! (%d)\n", + __func__, ret); + + ret = request_irq(GSS_A5_WDOG_EXPIRED, gss_wdog_bite_irq, + IRQF_TRIGGER_RISING, "gss_a5_wdog", NULL); + + if (ret < 0) { + pr_err("%s: Unable to request gss watchdog IRQ. (%d)\n", + __func__, ret); + disable_irq_nosync(GSS_A5_WDOG_EXPIRED); + goto out; + } + + ret = gss_subsystem_restart_init(); + + if (ret < 0) { + pr_err("%s: Unable to reg with subsystem restart. (%d)\n", + __func__, ret); + goto out; + } + + gss_data.gss_dev.minor = MISC_DYNAMIC_MINOR; + gss_data.gss_dev.name = "gss"; + gss_data.gss_dev.fops = &gss_file_ops; + ret = misc_register(&gss_data.gss_dev); + + if (ret) { + pr_err("%s: misc_registers failed for %s (%d)", __func__, + gss_data.gss_dev.name, ret); + goto out; + } + + gss_data.gss_ramdump_dev = create_ramdump_device("gss"); + + if (!gss_data.gss_ramdump_dev) { + pr_err("%s: Unable to create gss ramdump device. (%d)\n", + __func__, -ENOMEM); + ret = -ENOMEM; + goto out; + } + + gss_data.smem_ramdump_dev = create_ramdump_device("smem"); + + if (!gss_data.smem_ramdump_dev) { + pr_err("%s: Unable to create smem ramdump device. (%d)\n", + __func__, -ENOMEM); + ret = -ENOMEM; + goto out; + } + + pr_info("%s: gss fatal driver init'ed.\n", __func__); +out: + return ret; +} + +module_init(gss_8064_init); diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S index bcd5af223de..50d20607330 100644 --- a/arch/arm/mach-msm/headsmp.S +++ b/arch/arm/mach-msm/headsmp.S @@ -1,8 +1,7 @@ /* - * linux/arch/arm/mach-realview/headsmp.S - * * Copyright (c) 2003 ARM Limited * All Rights Reserved + * Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -11,31 +10,39 @@ #include #include - __CPUINIT +__CPUINIT /* * MSM specific entry point for secondary CPUs. This provides * a "holding pen" into which all secondary cores are held until we're * ready for them to initialise. + * + * This is executing in physical space with cache's off. */ ENTRY(msm_secondary_startup) - mrc p15, 0, r0, c0, c0, 5 - and r0, r0, #15 - adr r4, 1f - ldmia r4, {r5, r6} - sub r4, r4, r5 - add r6, r6, r4 -pen: ldr r7, [r6] - cmp r7, r0 + mrc p15, 0, r0, c0, c0, 5 @ MPIDR + and r0, r0, #15 @ What CPU am I + adr r4, 1f @ address of + ldmia r4, {r5, r6} @ load curr addr and pen_rel addr + sub r4, r4, r5 @ determine virtual/phys offsets + add r6, r6, r4 @ apply +pen: + wfe + dsb @ ensure subsequent access is + @ after event + + ldr r7, [r6] @ pen_rel has cpu to remove from reset + cmp r7, r0 @ are we lucky? bne pen /* * we've been released from the holding pen: secondary_stack * should now contain the SVC stack for this core */ + mvn r7, #0 @ -1 to registers + str r7,[r6] @ back to the pen for ack b secondary_startup ENDPROC(msm_secondary_startup) - .align 1: .long . .long pen_release diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c index a446fc14221..46e835ff7b0 100644 --- a/arch/arm/mach-msm/hotplug.c +++ b/arch/arm/mach-msm/hotplug.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2002 ARM Ltd. * All Rights Reserved + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,12 +10,28 @@ #include #include #include +#include #include #include +#include + +#include +#include + +#include "pm.h" +#include "spm.h" extern volatile int pen_release; +struct msm_hotplug_device { + struct completion cpu_killed; + unsigned int warm_boot; +}; + +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_hotplug_device, + msm_hotplug_devices); + static inline void cpu_enter_lowpower(void) { /* Just flush the cache. Changing the coherency is not yet @@ -30,18 +47,15 @@ static inline void platform_do_lowpower(unsigned int cpu) { /* Just enter wfi for now. TODO: Properly shut off the cpu. */ for (;;) { - /* - * here's the WFI - */ - asm("wfi" - : - : - : "memory", "cc"); + msm_pm_cpu_enter_lowpower(cpu); if (pen_release == cpu_logical_map(cpu)) { /* * OK, proper wakeup, we're done */ + pen_release = -1; + dmac_flush_range((void *)&pen_release, + (void *)(&pen_release + sizeof(pen_release))); break; } @@ -53,6 +67,8 @@ static inline void platform_do_lowpower(unsigned int cpu) * possible, since we are currently running incoherently, and * therefore cannot safely call printk() or anything else */ + dmac_inv_range((void *)&pen_release, + (void *)(&pen_release + sizeof(pen_release))); pr_debug("CPU%u: spurious wakeup call\n", cpu); } } @@ -69,16 +85,19 @@ int platform_cpu_kill(unsigned int cpu) */ void platform_cpu_die(unsigned int cpu) { + if (unlikely(cpu != smp_processor_id())) { + pr_crit("%s: running on %u, should be %u\n", + __func__, smp_processor_id(), cpu); + BUG(); + } + complete(&__get_cpu_var(msm_hotplug_devices).cpu_killed); /* * we're ready for shutdown now, so do it */ cpu_enter_lowpower(); platform_do_lowpower(cpu); - /* - * bring this CPU back into the world of cache - * coherency, and then restore interrupts - */ + pr_debug("CPU%u: %s: normal wakeup\n", cpu, __func__); cpu_leave_lowpower(); } @@ -90,3 +109,70 @@ int platform_cpu_disable(unsigned int cpu) */ return cpu == 0 ? -EPERM : 0; } + +#define CPU_SHIFT 0 +#define CPU_MASK 0xF +#define CPU_OF(n) (((n) & CPU_MASK) << CPU_SHIFT) +#define CPUSET_SHIFT 4 +#define CPUSET_MASK 0xFFFF +#define CPUSET_OF(n) (((n) & CPUSET_MASK) << CPUSET_SHIFT) + +static int hotplug_rtb_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + /* + * Bits [19:4] of the data are the online mask, lower 4 bits are the + * cpu number that is being changed. Additionally, changes to the + * online_mask that will be done by the current hotplug will be made + * even though they aren't necessarily in the online mask yet. + * + * XXX: This design is limited to supporting at most 16 cpus + */ + int this_cpumask = CPUSET_OF(1 << (int)hcpu); + int cpumask = CPUSET_OF(cpumask_bits(cpu_online_mask)[0]); + int cpudata = CPU_OF((int)hcpu) | cpumask; + + switch (action & (~CPU_TASKS_FROZEN)) { + case CPU_STARTING: + uncached_logk(LOGK_HOTPLUG, (void *)(cpudata | this_cpumask)); + break; + case CPU_DYING: + uncached_logk(LOGK_HOTPLUG, (void *)(cpudata & ~this_cpumask)); + break; + default: + break; + } + + return NOTIFY_OK; +} +static struct notifier_block hotplug_rtb_notifier = { + .notifier_call = hotplug_rtb_callback, +}; + +int msm_platform_secondary_init(unsigned int cpu) +{ + int ret; + struct msm_hotplug_device *dev = &__get_cpu_var(msm_hotplug_devices); + + if (!dev->warm_boot) { + dev->warm_boot = 1; + init_completion(&dev->cpu_killed); + return 0; + } + msm_jtag_restore_state(); +#if defined(CONFIG_VFP) && defined (CONFIG_CPU_PM) + vfp_pm_resume(); +#endif + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false); + + return ret; +} + +static int __init init_hotplug(void) +{ + + struct msm_hotplug_device *dev = &__get_cpu_var(msm_hotplug_devices); + init_completion(&dev->cpu_killed); + return register_hotcpu_notifier(&hotplug_rtb_notifier); +} +early_initcall(init_hotplug); diff --git a/arch/arm/mach-msm/hsic_sysmon.c b/arch/arm/mach-msm/hsic_sysmon.c new file mode 100644 index 00000000000..2dedbacc8c5 --- /dev/null +++ b/arch/arm/mach-msm/hsic_sysmon.c @@ -0,0 +1,449 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* add additional information to our printk's */ +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hsic_sysmon.h" +#include "sysmon.h" + +#define DRIVER_DESC "HSIC System monitor driver" + +enum hsic_sysmon_op { + HSIC_SYSMON_OP_READ = 0, + HSIC_SYSMON_OP_WRITE, + NUM_OPS +}; + +struct hsic_sysmon { + struct usb_device *udev; + struct usb_interface *ifc; + __u8 in_epaddr; + __u8 out_epaddr; + unsigned int pipe[NUM_OPS]; + struct kref kref; + struct platform_device pdev; + int id; + + /* debugging counters */ + atomic_t dbg_bytecnt[NUM_OPS]; + atomic_t dbg_pending[NUM_OPS]; +}; +static struct hsic_sysmon *hsic_sysmon_devices[NUM_HSIC_SYSMON_DEVS]; + +static void hsic_sysmon_delete(struct kref *kref) +{ + struct hsic_sysmon *hs = container_of(kref, struct hsic_sysmon, kref); + + usb_put_dev(hs->udev); + hsic_sysmon_devices[hs->id] = NULL; + kfree(hs); +} + +/** + * hsic_sysmon_open() - Opens the system monitor bridge. + * @id: the HSIC system monitor device to open + * + * This should only be called after the platform_device "sys_mon" with id + * SYSMON_SS_EXT_MODEM has been added. The simplest way to do that is to + * register a platform_driver and its probe will be called when the HSIC + * device is ready. + */ +int hsic_sysmon_open(enum hsic_sysmon_device_id id) +{ + struct hsic_sysmon *hs; + + if (id >= NUM_HSIC_SYSMON_DEVS) { + pr_err("invalid dev id(%d)", id); + return -ENODEV; + } + + hs = hsic_sysmon_devices[id]; + if (!hs) { + pr_err("dev is null"); + return -ENODEV; + } + + kref_get(&hs->kref); + + return 0; +} +EXPORT_SYMBOL(hsic_sysmon_open); + +/** + * hsic_sysmon_close() - Closes the system monitor bridge. + * @id: the HSIC system monitor device to close + */ +void hsic_sysmon_close(enum hsic_sysmon_device_id id) +{ + struct hsic_sysmon *hs; + + if (id >= NUM_HSIC_SYSMON_DEVS) { + pr_err("invalid dev id(%d)", id); + return; + } + + hs = hsic_sysmon_devices[id]; + kref_put(&hs->kref, hsic_sysmon_delete); +} +EXPORT_SYMBOL(hsic_sysmon_close); + +/** + * hsic_sysmon_readwrite() - Common function to send read/write over HSIC + */ +static int hsic_sysmon_readwrite(enum hsic_sysmon_device_id id, void *data, + size_t len, size_t *actual_len, int timeout, + enum hsic_sysmon_op op) +{ + struct hsic_sysmon *hs; + int ret; + const char *opstr = (op == HSIC_SYSMON_OP_READ) ? + "read" : "write"; + + pr_debug("%s: id:%d, data len:%d, timeout:%d", opstr, id, len, timeout); + + if (id >= NUM_HSIC_SYSMON_DEVS) { + pr_err("invalid dev id(%d)", id); + return -ENODEV; + } + + if (!len) { + pr_err("length(%d) must be greater than 0", len); + return -EINVAL; + } + + hs = hsic_sysmon_devices[id]; + if (!hs) { + pr_err("device was not opened"); + return -ENODEV; + } + + if (!hs->ifc) { + dev_err(&hs->udev->dev, "can't %s, device disconnected\n", + opstr); + return -ENODEV; + } + + ret = usb_autopm_get_interface(hs->ifc); + if (ret < 0) { + dev_err(&hs->udev->dev, "can't %s, autopm_get failed:%d\n", + opstr, ret); + return ret; + } + + atomic_inc(&hs->dbg_pending[op]); + + ret = usb_bulk_msg(hs->udev, hs->pipe[op], data, len, actual_len, + timeout); + + atomic_dec(&hs->dbg_pending[op]); + + if (ret) + dev_err(&hs->udev->dev, + "can't %s, usb_bulk_msg failed, err:%d\n", opstr, ret); + else + atomic_add(*actual_len, &hs->dbg_bytecnt[op]); + + usb_autopm_put_interface(hs->ifc); + return ret; +} + +/** + * hsic_sysmon_read() - Read data from the HSIC sysmon interface. + * @id: the HSIC system monitor device to open + * @data: pointer to caller-allocated buffer to fill in + * @len: length in bytes of the buffer + * @actual_len: pointer to a location to put the actual length read + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * Synchronously reads data from the HSIC interface. The call will return + * after the read has completed, encountered an error, or timed out. Upon + * successful return actual_len will reflect the number of bytes read. + * + * If successful, it returns 0, otherwise a negative error number. The number + * of actual bytes transferred will be stored in the actual_len paramater. + */ +int hsic_sysmon_read(enum hsic_sysmon_device_id id, char *data, size_t len, + size_t *actual_len, int timeout) +{ + return hsic_sysmon_readwrite(id, data, len, actual_len, + timeout, HSIC_SYSMON_OP_READ); +} +EXPORT_SYMBOL(hsic_sysmon_read); + +/** + * hsic_sysmon_write() - Write data to the HSIC sysmon interface. + * @id: the HSIC system monitor device to open + * @data: pointer to caller-allocated buffer to write + * @len: length in bytes of the data in buffer to write + * @actual_len: pointer to a location to put the actual length written + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * Synchronously writes data to the HSIC interface. The call will return + * after the write has completed, encountered an error, or timed out. Upon + * successful return actual_len will reflect the number of bytes written. + * + * If successful, it returns 0, otherwise a negative error number. The number + * of actual bytes transferred will be stored in the actual_len paramater. + */ +int hsic_sysmon_write(enum hsic_sysmon_device_id id, const char *data, + size_t len, int timeout) +{ + size_t actual_len; + return hsic_sysmon_readwrite(id, (void *)data, len, &actual_len, + timeout, HSIC_SYSMON_OP_WRITE); +} +EXPORT_SYMBOL(hsic_sysmon_write); + +#if defined(CONFIG_DEBUG_FS) +#define DEBUG_BUF_SIZE 512 +static ssize_t sysmon_debug_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char *buf; + int i, ret = 0; + + buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < NUM_HSIC_SYSMON_DEVS; i++) { + struct hsic_sysmon *hs = hsic_sysmon_devices[i]; + if (!hs) + continue; + + ret += scnprintf(buf, DEBUG_BUF_SIZE, + "---HSIC Sysmon #%d---\n" + "epin:%d, epout:%d\n" + "bytes to host: %d\n" + "bytes to mdm: %d\n" + "pending reads: %d\n" + "pending writes: %d\n", + i, hs->in_epaddr & ~0x80, hs->out_epaddr, + atomic_read( + &hs->dbg_bytecnt[HSIC_SYSMON_OP_READ]), + atomic_read( + &hs->dbg_bytecnt[HSIC_SYSMON_OP_WRITE]), + atomic_read( + &hs->dbg_pending[HSIC_SYSMON_OP_READ]), + atomic_read( + &hs->dbg_pending[HSIC_SYSMON_OP_WRITE]) + ); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +static ssize_t sysmon_debug_reset_stats(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + int i; + + for (i = 0; i < NUM_HSIC_SYSMON_DEVS; i++) { + struct hsic_sysmon *hs = hsic_sysmon_devices[i]; + if (hs) { + atomic_set(&hs->dbg_bytecnt[HSIC_SYSMON_OP_READ], 0); + atomic_set(&hs->dbg_bytecnt[HSIC_SYSMON_OP_WRITE], 0); + atomic_set(&hs->dbg_pending[HSIC_SYSMON_OP_READ], 0); + atomic_set(&hs->dbg_pending[HSIC_SYSMON_OP_WRITE], 0); + } + } + + return count; +} + +const struct file_operations sysmon_stats_ops = { + .read = sysmon_debug_read_stats, + .write = sysmon_debug_reset_stats, +}; + +static struct dentry *dent; + +static void hsic_sysmon_debugfs_init(void) +{ + struct dentry *dfile; + + dent = debugfs_create_dir("hsic_sysmon", 0); + if (IS_ERR(dent)) + return; + + dfile = debugfs_create_file("status", 0444, dent, 0, &sysmon_stats_ops); + if (!dfile || IS_ERR(dfile)) + debugfs_remove(dent); +} + +static void hsic_sysmon_debugfs_cleanup(void) +{ + if (dent) { + debugfs_remove_recursive(dent); + dent = NULL; + } +} +#else +static inline void hsic_sysmon_debugfs_init(void) { } +static inline void hsic_sysmon_debugfs_cleanup(void) { } +#endif + +static int +hsic_sysmon_probe(struct usb_interface *ifc, const struct usb_device_id *id) +{ + struct hsic_sysmon *hs; + struct usb_host_interface *ifc_desc; + struct usb_endpoint_descriptor *ep_desc; + int i; + int ret = -ENOMEM; + __u8 ifc_num; + + pr_debug("id:%lu", id->driver_info); + + ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber; + + /* is this the interface we're looking for? */ + if (ifc_num != id->driver_info) + return -ENODEV; + + hs = kzalloc(sizeof(*hs), GFP_KERNEL); + if (!hs) { + pr_err("unable to allocate hsic_sysmon"); + return -ENOMEM; + } + + hs->udev = usb_get_dev(interface_to_usbdev(ifc)); + hs->ifc = ifc; + kref_init(&hs->kref); + + ifc_desc = ifc->cur_altsetting; + for (i = 0; i < ifc_desc->desc.bNumEndpoints; i++) { + ep_desc = &ifc_desc->endpoint[i].desc; + + if (!hs->in_epaddr && usb_endpoint_is_bulk_in(ep_desc)) { + hs->in_epaddr = ep_desc->bEndpointAddress; + hs->pipe[HSIC_SYSMON_OP_READ] = + usb_rcvbulkpipe(hs->udev, hs->in_epaddr); + } + + if (!hs->out_epaddr && usb_endpoint_is_bulk_out(ep_desc)) { + hs->out_epaddr = ep_desc->bEndpointAddress; + hs->pipe[HSIC_SYSMON_OP_WRITE] = + usb_sndbulkpipe(hs->udev, hs->out_epaddr); + } + } + + if (!(hs->in_epaddr && hs->out_epaddr)) { + pr_err("could not find bulk in and bulk out endpoints"); + ret = -ENODEV; + goto error; + } + + hs->id = HSIC_SYSMON_DEV_EXT_MODEM; + hsic_sysmon_devices[HSIC_SYSMON_DEV_EXT_MODEM] = hs; + usb_set_intfdata(ifc, hs); + + hs->pdev.name = "sys_mon"; + hs->pdev.id = SYSMON_SS_EXT_MODEM; + platform_device_register(&hs->pdev); + + pr_debug("complete"); + + return 0; + +error: + if (hs) + kref_put(&hs->kref, hsic_sysmon_delete); + + return ret; +} + +static void hsic_sysmon_disconnect(struct usb_interface *ifc) +{ + struct hsic_sysmon *hs = usb_get_intfdata(ifc); + + platform_device_unregister(&hs->pdev); + kref_put(&hs->kref, hsic_sysmon_delete); + usb_set_intfdata(ifc, NULL); +} + +static int hsic_sysmon_suspend(struct usb_interface *ifc, pm_message_t message) +{ + return 0; +} + +static int hsic_sysmon_resume(struct usb_interface *ifc) +{ + return 0; +} + +/* driver_info maps to the interface number corresponding to sysmon */ +static const struct usb_device_id hsic_sysmon_ids[] = { + { USB_DEVICE(0x5c6, 0x9048), .driver_info = 1, }, + { USB_DEVICE(0x5c6, 0x904C), .driver_info = 1, }, + {} /* terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, hsic_sysmon_ids); + +static struct usb_driver hsic_sysmon_driver = { + .name = "hsic_sysmon", + .probe = hsic_sysmon_probe, + .disconnect = hsic_sysmon_disconnect, + .suspend = hsic_sysmon_suspend, + .resume = hsic_sysmon_resume, + .id_table = hsic_sysmon_ids, + .supports_autosuspend = 1, +}; + +static int __init hsic_sysmon_init(void) +{ + int ret; + + ret = usb_register(&hsic_sysmon_driver); + if (ret) { + pr_err("unable to register " DRIVER_DESC); + return ret; + } + + hsic_sysmon_debugfs_init(); + return 0; +} + +static void __exit hsic_sysmon_exit(void) +{ + hsic_sysmon_debugfs_cleanup(); + usb_deregister(&hsic_sysmon_driver); +} + +module_init(hsic_sysmon_init); +module_exit(hsic_sysmon_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/hsic_sysmon.h b/arch/arm/mach-msm/hsic_sysmon.h new file mode 100644 index 00000000000..aa57b93281a --- /dev/null +++ b/arch/arm/mach-msm/hsic_sysmon.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __HSIC_SYSMON_H__ +#define __HSIC_SYSMON_H__ + +/** + * enum hsic_sysmon_device_id - Supported HSIC subsystem devices + */ +enum hsic_sysmon_device_id { + HSIC_SYSMON_DEV_EXT_MODEM, + NUM_HSIC_SYSMON_DEVS +}; + +#if defined(CONFIG_MSM_HSIC_SYSMON) || defined(CONFIG_MSM_HSIC_SYSMON_MODULE) + +extern int hsic_sysmon_open(enum hsic_sysmon_device_id id); +extern void hsic_sysmon_close(enum hsic_sysmon_device_id id); +extern int hsic_sysmon_read(enum hsic_sysmon_device_id id, char *data, + size_t len, size_t *actual_len, int timeout); +extern int hsic_sysmon_write(enum hsic_sysmon_device_id id, const char *data, + size_t len, int timeout); + +#else /* CONFIG_MSM_HSIC_SYSMON || CONFIG_MSM_HSIC_SYSMON_MODULE */ + +static inline int hsic_sysmon_open(enum hsic_sysmon_device_id id) +{ + return -ENODEV; +} + +static inline void hsic_sysmon_close(enum hsic_sysmon_device_id id) { } + +static inline int hsic_sysmon_read(enum hsic_sysmon_device_id id, char *data, + size_t len, size_t *actual_len, int timeout) +{ + return -ENODEV; +} + +static inline int hsic_sysmon_write(enum hsic_sysmon_device_id id, + const char *data, size_t len, int timeout) +{ + return -ENODEV; +} + +#endif /* CONFIG_MSM_HSIC_SYSMON || CONFIG_MSM_HSIC_SYSMON_MODULE */ + +#endif /* __HSIC_SYSMON_H__ */ diff --git a/arch/arm/mach-msm/hsic_sysmon_test.c b/arch/arm/mach-msm/hsic_sysmon_test.c new file mode 100644 index 00000000000..9929cb736ec --- /dev/null +++ b/arch/arm/mach-msm/hsic_sysmon_test.c @@ -0,0 +1,118 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* add additional information to our printk's */ +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ + +#include +#include +#include +#include +#include + +#include "hsic_sysmon.h" +#include "sysmon.h" + +#define DRIVER_DESC "HSIC System monitor driver test" + +#define RD_BUF_SIZE 4096 + +struct sysmon_test_dev { + int buflen; + char buf[RD_BUF_SIZE]; +}; +static struct sysmon_test_dev *sysmon_dev; + +static ssize_t sysmon_test_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct sysmon_test_dev *dev = sysmon_dev; + int ret; + + if (!dev) + return -ENODEV; + + ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, dev->buf, RD_BUF_SIZE, + &dev->buflen, 3000); + if (!ret) + return simple_read_from_buffer(ubuf, count, ppos, + dev->buf, dev->buflen); + + return 0; +} + +static ssize_t sysmon_test_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct sysmon_test_dev *dev = sysmon_dev; + int ret; + + if (!dev) + return -ENODEV; + + if (copy_from_user(dev->buf, ubuf, count)) { + pr_err("error copying for writing"); + return 0; + } + + ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM, + dev->buf, count, 1000); + if (ret < 0) { + pr_err("error writing to hsic_sysmon"); + return ret; + } + + return count; +} + +static int sysmon_test_open(struct inode *inode, struct file *file) +{ + return hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM); +} + +static int sysmon_test_release(struct inode *inode, struct file *file) +{ + hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM); + return 0; +} + +static struct dentry *dfile; +const struct file_operations sysmon_test_ops = { + .read = sysmon_test_read, + .write = sysmon_test_write, + .open = sysmon_test_open, + .release = sysmon_test_release +}; + +static int __init sysmon_test_init(void) +{ + sysmon_dev = kzalloc(sizeof(*sysmon_dev), GFP_KERNEL); + if (!sysmon_dev) + return -ENOMEM; + + dfile = debugfs_create_file("hsic_sysmon_test", 0666, NULL, + 0, &sysmon_test_ops); + return 0; +} + +static void __exit sysmon_test_exit(void) +{ + if (dfile) + debugfs_remove(dfile); + kfree(sysmon_dev); +} + +module_init(sysmon_test_init); +module_exit(sysmon_test_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/htc_35mm_jack.c b/arch/arm/mach-msm/htc_35mm_jack.c new file mode 100644 index 00000000000..3f95ff2bb6d --- /dev/null +++ b/arch/arm/mach-msm/htc_35mm_jack.c @@ -0,0 +1,397 @@ +/* arch/arm/mach-msm/htc_35mm_jack.c + * + * Copyright (C) 2009 HTC, Inc. + * Author: Arec Kao + * Copyright (C) 2009 Google, Inc. + * Author: Eric Olsen + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HTC_AUDIOJACK +#include +#endif + +/* #define CONFIG_DEBUG_H2W */ + +#define H2WI(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#define H2WE(fmt, arg...) \ + printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) + +#ifdef CONFIG_DEBUG_H2W +#define H2W_DBG(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +void detect_h2w_do_work(struct work_struct *w); + +static struct workqueue_struct *detect_wq; +static struct workqueue_struct *button_wq; + +static DECLARE_DELAYED_WORK(detect_h2w_work, detect_h2w_do_work); + +static void insert_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(insert_35mm_work, insert_35mm_do_work); +static void remove_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(remove_35mm_work, remove_35mm_do_work); +static void button_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(button_35mm_work, button_35mm_do_work); + +struct h35_info { + struct mutex mutex_lock; + struct switch_dev hs_change; + unsigned long insert_jiffies; + int ext_35mm_status; + int is_ext_insert; + int key_code; + int mic_bias_state; + int *is_hpin_stable; + struct input_dev *input; + + struct wake_lock headset_wake_lock; +}; + +static struct h35mm_platform_data *pd; +static struct h35_info *hi; + +static ssize_t h35mm_print_name(struct switch_dev *sdev, char *buf) +{ + return sprintf(buf, "Headset\n"); +} + +static void button_35mm_do_work(struct work_struct *work) +{ + int key = 0; + int pressed = 0; + + if (!hi->is_ext_insert) { + /* no headset ignor key event */ + H2WI("3.5mm headset is plugged out, skip report key event"); + return; + } + + switch (hi->key_code) { + case 0x1: /* Play/Pause */ + H2WI("3.5mm RC: Play Pressed"); + key = KEY_MEDIA; + pressed = 1; + break; + case 0x2: + H2WI("3.5mm RC: BACKWARD Pressed"); + key = KEY_PREVIOUSSONG; + pressed = 1; + break; + case 0x3: + H2WI("3.5mm RC: FORWARD Pressed"); + key = KEY_NEXTSONG; + pressed = 1; + break; + case 0x81: /* Play/Pause */ + H2WI("3.5mm RC: Play Released"); + key = KEY_MEDIA; + pressed = 0; + break; + case 0x82: + H2WI("3.5mm RC: BACKWARD Released"); + key = KEY_PREVIOUSSONG; + pressed = 0; + break; + case 0x83: + H2WI("3.5mm RC: FORWARD Released"); + key = KEY_NEXTSONG; + pressed = 0; + break; + default: + H2WI("3.5mm RC: Unknown Button (0x%x) Pressed", hi->key_code); + return; + } + input_report_key(hi->input, key, pressed); + input_sync(hi->input); + + wake_lock_timeout(&hi->headset_wake_lock, 1.5*HZ); +} + +static void remove_35mm_do_work(struct work_struct *work) +{ + wake_lock_timeout(&hi->headset_wake_lock, 2.5*HZ); + + H2W_DBG(""); + /*To solve the insert, remove, insert headset problem*/ + if (time_before_eq(jiffies, hi->insert_jiffies)) + msleep(800); + + if (hi->is_ext_insert) { + H2WI("Skip 3.5mm headset plug out!!!"); + if (hi->is_hpin_stable) + *(hi->is_hpin_stable) = 1; + return; + } + + pr_info("3.5mm_headset plug out\n"); + + if (pd->key_event_disable != NULL) + pd->key_event_disable(); + + if (hi->mic_bias_state) { + turn_mic_bias_on(0); + hi->mic_bias_state = 0; + } + hi->ext_35mm_status = 0; + if (hi->is_hpin_stable) + *(hi->is_hpin_stable) = 0; + + /* Notify framework via switch class */ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->hs_change, hi->ext_35mm_status); + mutex_unlock(&hi->mutex_lock); +} + +static void insert_35mm_do_work(struct work_struct *work) +{ + H2W_DBG(""); + hi->insert_jiffies = jiffies + 1*HZ; + + wake_lock_timeout(&hi->headset_wake_lock, 1.5*HZ); + + if (hi->is_ext_insert) { + pr_info("3.5mm_headset plug in\n"); + + if (pd->key_event_enable != NULL) + pd->key_event_enable(); + + /* Turn On Mic Bias */ + if (!hi->mic_bias_state) { + turn_mic_bias_on(1); + hi->mic_bias_state = 1; + /* Wait for pin stable */ + msleep(300); + } + + /* Detect headset with or without microphone */ + if(pd->headset_has_mic) { + if (pd->headset_has_mic() == 0) { + /* without microphone */ + pr_info("3.5mm without microphone\n"); + hi->ext_35mm_status = BIT_HEADSET_NO_MIC; + } else { /* with microphone */ + pr_info("3.5mm with microphone\n"); + hi->ext_35mm_status = BIT_HEADSET; + } + } else { + /* Assume no mic */ + pr_info("3.5mm without microphone\n"); + hi->ext_35mm_status = BIT_HEADSET_NO_MIC; + } + hi->ext_35mm_status |= BIT_35MM_HEADSET; + + /* Notify framework via switch class */ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->hs_change, hi->ext_35mm_status); + mutex_unlock(&hi->mutex_lock); + + if (hi->is_hpin_stable) + *(hi->is_hpin_stable) = 1; + } +} + +int htc_35mm_key_event(int keycode, int *hpin_stable) +{ + hi->key_code = keycode; + hi->is_hpin_stable = hpin_stable; + + if ((hi->ext_35mm_status & BIT_HEADSET) == 0) { + *(hi->is_hpin_stable) = 0; + + pr_info("Key press with no mic. Retrying detection\n"); + queue_work(detect_wq, &insert_35mm_work); + } else + queue_work(button_wq, &button_35mm_work); + + return 0; +} + +int htc_35mm_jack_plug_event(int insert, int *hpin_stable) +{ + if (!hi) { + pr_err("Plug event before driver init\n"); + return -1; + } + + mutex_lock(&hi->mutex_lock); + hi->is_ext_insert = insert; + hi->is_hpin_stable = hpin_stable; + mutex_unlock(&hi->mutex_lock); + + H2WI(" %d", hi->is_ext_insert); + if (!hi->is_ext_insert) + queue_work(detect_wq, &remove_35mm_work); + else + queue_work(detect_wq, &insert_35mm_work); + return 1; +} + +static int htc_35mm_probe(struct platform_device *pdev) +{ + int ret; + + pd = pdev->dev.platform_data; + + pr_info("H2W: htc_35mm_jack driver register\n"); + + hi = kzalloc(sizeof(struct h35_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + hi->ext_35mm_status = 0; + hi->is_ext_insert = 0; + hi->mic_bias_state = 0; + + mutex_init(&hi->mutex_lock); + + wake_lock_init(&hi->headset_wake_lock, WAKE_LOCK_SUSPEND, "headset"); + + hi->hs_change.name = "h2w"; + hi->hs_change.print_name = h35mm_print_name; + ret = switch_dev_register(&hi->hs_change); + if (ret < 0) + goto err_switch_dev_register; + + detect_wq = create_workqueue("detection"); + if (detect_wq == NULL) { + ret = -ENOMEM; + goto err_create_detect_work_queue; + } + + button_wq = create_workqueue("button"); + if (button_wq == NULL) { + ret = -ENOMEM; + goto err_create_button_work_queue; + } + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + set_bit(EV_SYN, hi->input->evbit); + set_bit(EV_KEY, hi->input->evbit); + set_bit(KEY_MEDIA, hi->input->keybit); + set_bit(KEY_NEXTSONG, hi->input->keybit); + set_bit(KEY_PLAYPAUSE, hi->input->keybit); + set_bit(KEY_PREVIOUSSONG, hi->input->keybit); + set_bit(KEY_MUTE, hi->input->keybit); + set_bit(KEY_VOLUMEUP, hi->input->keybit); + set_bit(KEY_VOLUMEDOWN, hi->input->keybit); + set_bit(KEY_END, hi->input->keybit); + set_bit(KEY_SEND, hi->input->keybit); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + /* Enable plug events*/ + if (pd->plug_event_enable == NULL) { + ret = -ENOMEM; + goto err_enable_plug_event; + } + if (pd->plug_event_enable() != 1) { + ret = -ENOMEM; + goto err_enable_plug_event; + } + + return 0; + +err_enable_plug_event: +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + destroy_workqueue(button_wq); +err_create_button_work_queue: + destroy_workqueue(detect_wq); +err_create_detect_work_queue: + switch_dev_unregister(&hi->hs_change); +err_switch_dev_register: + kzfree(hi); + pr_err("H2W: Failed to register driver\n"); + + return ret; +} + +static int htc_35mm_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + switch_dev_unregister(&hi->hs_change); + kzfree(hi); + +#if 0 /* Add keys later */ + input_unregister_device(hi->input); +#endif + return 0; +} + +static struct platform_driver htc_35mm_driver = { + .probe = htc_35mm_probe, + .remove = htc_35mm_remove, + .driver = { + .name = "htc_headset", + .owner = THIS_MODULE, + }, +}; + +static int __init htc_35mm_init(void) +{ + H2W_DBG(""); + return platform_driver_register(&htc_35mm_driver); +} + +static void __exit htc_35mm_exit(void) +{ + platform_driver_unregister(&htc_35mm_driver); +} + +module_init(htc_35mm_init); +module_exit(htc_35mm_exit); + +MODULE_AUTHOR("Eric Olsen "); +MODULE_DESCRIPTION("HTC 3.5MM Driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c new file mode 100644 index 00000000000..3de71dddb58 --- /dev/null +++ b/arch/arm/mach-msm/htc_acoustic.c @@ -0,0 +1,239 @@ +/* arch/arm/mach-msm/htc_acoustic.c + * + * Copyright (C) 2007-2008 HTC Corporation + * Author: Laurence Chen + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smd_private.h" + +#define ACOUSTIC_IOCTL_MAGIC 'p' +#define ACOUSTIC_ARM11_DONE _IOW(ACOUSTIC_IOCTL_MAGIC, 22, unsigned int) + +#define HTCRPOG 0x30100002 +#define HTCVERS 0 +#define ONCRPC_SET_MIC_BIAS_PROC (1) +#define ONCRPC_ACOUSTIC_INIT_PROC (5) +#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (6) + +#define HTC_ACOUSTIC_TABLE_SIZE (0x10000) + +#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args) +#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args) + +struct set_smem_req { + struct rpc_request_hdr hdr; + uint32_t size; +}; + +struct set_smem_rep { + struct rpc_reply_hdr hdr; + int n; +}; + +struct set_acoustic_req { + struct rpc_request_hdr hdr; +}; + +struct set_acoustic_rep { + struct rpc_reply_hdr hdr; + int n; +}; + +static uint32_t htc_acoustic_vir_addr; +static struct msm_rpc_endpoint *endpoint; +static struct mutex api_lock; + +static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pgoff, delta; + int rc = -EINVAL; + size_t size; + + D("mmap\n"); + + mutex_lock(&api_lock); + + size = vma->vm_end - vma->vm_start; + + if (vma->vm_pgoff != 0) { + E("mmap failed: page offset %lx\n", vma->vm_pgoff); + goto done; + } + + if (!htc_acoustic_vir_addr) { + E("mmap failed: smem region not allocated\n"); + rc = -EIO; + goto done; + } + + pgoff = MSM_SHARED_RAM_PHYS + + (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE); + delta = PAGE_ALIGN(pgoff) - pgoff; + + if (size + delta > HTC_ACOUSTIC_TABLE_SIZE) { + E("mmap failed: size %d\n", size); + goto done; + } + + pgoff += delta; + vma->vm_flags |= VM_IO | VM_RESERVED; + + rc = io_remap_pfn_range(vma, vma->vm_start, pgoff >> PAGE_SHIFT, + size, vma->vm_page_prot); + + if (rc < 0) + E("mmap failed: remap error %d\n", rc); + +done: mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_open(struct inode *inode, struct file *file) +{ + int rc = -EIO; + struct set_smem_req req_smem; + struct set_smem_rep rep_smem; + + D("open\n"); + + mutex_lock(&api_lock); + + if (!htc_acoustic_vir_addr) { + if (endpoint == NULL) { + endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0); + if (IS_ERR(endpoint)) { + E("init rpc failed! rc = %ld\n", + PTR_ERR(endpoint)); + endpoint = NULL; + goto done; + } + } + + req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ALLOC_ACOUSTIC_MEM_PROC, + &req_smem, sizeof(req_smem), + &rep_smem, sizeof(rep_smem), + 5 * HZ); + + if (rep_smem.n != 0 || rc < 0) { + E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n", + rc); + goto done; + } + htc_acoustic_vir_addr = + (uint32_t)smem_alloc(SMEM_ID_VENDOR1, + HTC_ACOUSTIC_TABLE_SIZE); + if (!htc_acoustic_vir_addr) { + E("open failed: smem_alloc error\n"); + goto done; + } + } + + rc = 0; +done: + mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_release(struct inode *inode, struct file *file) +{ + D("release\n"); + return 0; +} + +static long acoustic_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, reply_value; + struct set_acoustic_req req; + struct set_acoustic_rep rep; + + D("ioctl\n"); + + mutex_lock(&api_lock); + + switch (cmd) { + case ACOUSTIC_ARM11_DONE: + D("ioctl: ACOUSTIC_ARM11_DONE called %d.\n", current->pid); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ACOUSTIC_INIT_PROC, &req, + sizeof(req), &rep, sizeof(rep), + 5 * HZ); + + reply_value = be32_to_cpu(rep.n); + if (reply_value != 0 || rc < 0) { + E("ioctl failed: ONCRPC_ACOUSTIC_INIT_PROC "\ + "error %d.\n", rc); + if (rc >= 0) + rc = -EIO; + break; + } + D("ioctl: ONCRPC_ACOUSTIC_INIT_PROC success.\n"); + break; + default: + E("ioctl: invalid command\n"); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); + return 0; +} + + +static struct file_operations acoustic_fops = { + .owner = THIS_MODULE, + .open = acoustic_open, + .release = acoustic_release, + .mmap = acoustic_mmap, + .unlocked_ioctl = acoustic_ioctl, +}; + +static struct miscdevice acoustic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "htc-acoustic", + .fops = &acoustic_fops, +}; + +static int __init acoustic_init(void) +{ + mutex_init(&api_lock); + return misc_register(&acoustic_misc); +} + +static void __exit acoustic_exit(void) +{ + misc_deregister(&acoustic_misc); +} + +module_init(acoustic_init); +module_exit(acoustic_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC acoustic driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_acoustic_qsd.c b/arch/arm/mach-msm/htc_acoustic_qsd.c new file mode 100644 index 00000000000..ce3c3a0bbfe --- /dev/null +++ b/arch/arm/mach-msm/htc_acoustic_qsd.c @@ -0,0 +1,315 @@ +/* arch/arm/mach-msm/htc_acoustic_qsd.c + * + * Copyright (C) 2009 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "smd_private.h" + +#define ACOUSTIC_IOCTL_MAGIC 'p' +#define ACOUSTIC_UPDATE_ADIE \ + _IOW(ACOUSTIC_IOCTL_MAGIC, 24, unsigned int) + +#define HTCACOUSTICPROG 0x30100003 +#define HTCACOUSTICVERS 0 +#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (1) +#define ONCRPC_UPDATE_ADIE_PROC (2) +#define ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC (3) +#define ONCRPC_FORCE_HEADSET_SPEAKER_PROC (4) + +#define HTC_ACOUSTIC_TABLE_SIZE (0x20000) + +#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args) +#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args) + +static uint32_t htc_acoustic_vir_addr; +static struct msm_rpc_endpoint *endpoint; +static struct mutex api_lock; +static struct mutex rpc_connect_lock; +static struct qsd_acoustic_ops *the_ops; + +void acoustic_register_ops(struct qsd_acoustic_ops *ops) +{ + the_ops = ops; +} + +static int is_rpc_connect(void) +{ + mutex_lock(&rpc_connect_lock); + if (endpoint == NULL) { + endpoint = msm_rpc_connect(HTCACOUSTICPROG, + HTCACOUSTICVERS, 0); + if (IS_ERR(endpoint)) { + pr_err("%s: init rpc failed! rc = %ld\n", + __func__, PTR_ERR(endpoint)); + mutex_unlock(&rpc_connect_lock); + return -1; + } + } + mutex_unlock(&rpc_connect_lock); + return 0; +} + +int turn_mic_bias_on(int on) +{ + D("%s called %d\n", __func__, on); + if (the_ops->enable_mic_bias) + the_ops->enable_mic_bias(on); + + return 0; +} +EXPORT_SYMBOL(turn_mic_bias_on); + +int force_headset_speaker_on(int enable) +{ + struct speaker_headset_req { + struct rpc_request_hdr hdr; + uint32_t enable; + } spkr_req; + + D("%s called %d\n", __func__, enable); + + if (is_rpc_connect() == -1) + return -1; + + spkr_req.enable = cpu_to_be32(enable); + return msm_rpc_call(endpoint, + ONCRPC_FORCE_HEADSET_SPEAKER_PROC, + &spkr_req, sizeof(spkr_req), 5 * HZ); +} +EXPORT_SYMBOL(force_headset_speaker_on); + +int enable_aux_loopback(uint32_t enable) +{ + struct aux_loopback_req { + struct rpc_request_hdr hdr; + uint32_t enable; + } aux_req; + + D("%s called %d\n", __func__, enable); + + if (is_rpc_connect() == -1) + return -1; + + aux_req.enable = cpu_to_be32(enable); + return msm_rpc_call(endpoint, + ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC, + &aux_req, sizeof(aux_req), 5 * HZ); +} +EXPORT_SYMBOL(enable_aux_loopback); + +static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pgoff; + int rc = -EINVAL; + size_t size; + + D("mmap\n"); + + mutex_lock(&api_lock); + + size = vma->vm_end - vma->vm_start; + + if (vma->vm_pgoff != 0) { + E("mmap failed: page offset %lx\n", vma->vm_pgoff); + goto done; + } + + if (!htc_acoustic_vir_addr) { + E("mmap failed: smem region not allocated\n"); + rc = -EIO; + goto done; + } + + pgoff = MSM_SHARED_RAM_PHYS + + (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE); + pgoff = ((pgoff + 4095) & ~4095); + htc_acoustic_vir_addr = ((htc_acoustic_vir_addr + 4095) & ~4095); + + if (pgoff <= 0) { + E("pgoff wrong. %ld\n", pgoff); + goto done; + } + + if (size <= HTC_ACOUSTIC_TABLE_SIZE) { + pgoff = pgoff >> PAGE_SHIFT; + } else { + E("size > HTC_ACOUSTIC_TABLE_SIZE %d\n", size); + goto done; + } + + vma->vm_flags |= VM_IO | VM_RESERVED; + rc = io_remap_pfn_range(vma, vma->vm_start, pgoff, + size, vma->vm_page_prot); + + if (rc < 0) + E("mmap failed: remap error %d\n", rc); + +done: mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_open(struct inode *inode, struct file *file) +{ + int reply_value; + int rc = -EIO; + struct set_smem_req { + struct rpc_request_hdr hdr; + uint32_t size; + } req_smem; + + struct set_smem_rep { + struct rpc_reply_hdr hdr; + int n; + } rep_smem; + + D("open\n"); + + mutex_lock(&api_lock); + + if (!htc_acoustic_vir_addr) { + if (is_rpc_connect() == -1) + goto done; + + req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ALLOC_ACOUSTIC_MEM_PROC, + &req_smem, sizeof(req_smem), + &rep_smem, sizeof(rep_smem), + 5 * HZ); + + reply_value = be32_to_cpu(rep_smem.n); + if (reply_value != 0 || rc < 0) { + E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n", + rc); + goto done; + } + htc_acoustic_vir_addr = + (uint32_t)smem_alloc(SMEM_ID_VENDOR1, + HTC_ACOUSTIC_TABLE_SIZE); + if (!htc_acoustic_vir_addr) { + E("open failed: smem_alloc error\n"); + goto done; + } + } + + rc = 0; +done: + mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_release(struct inode *inode, struct file *file) +{ + D("release\n"); + return 0; +} + +static long acoustic_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, reply_value; + + D("ioctl\n"); + + mutex_lock(&api_lock); + + switch (cmd) { + case ACOUSTIC_UPDATE_ADIE: { + struct update_adie_req { + struct rpc_request_hdr hdr; + int id; + } adie_req; + + struct update_adie_rep { + struct rpc_reply_hdr hdr; + int ret; + } adie_rep; + + D("ioctl: ACOUSTIC_UPDATE_ADIE called %d.\n", current->pid); + + adie_req.id = cpu_to_be32(-1); /* update all codecs */ + rc = msm_rpc_call_reply(endpoint, + ONCRPC_UPDATE_ADIE_PROC, &adie_req, + sizeof(adie_req), &adie_rep, + sizeof(adie_rep), 5 * HZ); + + reply_value = be32_to_cpu(adie_rep.ret); + if (reply_value != 0 || rc < 0) { + E("ioctl failed: ONCRPC_UPDATE_ADIE_PROC "\ + "error %d.\n", rc); + if (rc >= 0) + rc = -EIO; + break; + } + D("ioctl: ONCRPC_UPDATE_ADIE_PROC success.\n"); + break; + } + default: + E("ioctl: invalid command\n"); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); + return rc; +} + +struct rpc_set_uplink_mute_args { + int mute; +}; + +static struct file_operations acoustic_fops = { + .owner = THIS_MODULE, + .open = acoustic_open, + .release = acoustic_release, + .mmap = acoustic_mmap, + .unlocked_ioctl = acoustic_ioctl, +}; + +static struct miscdevice acoustic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "htc-acoustic", + .fops = &acoustic_fops, +}; + +static int __init acoustic_init(void) +{ + mutex_init(&api_lock); + mutex_init(&rpc_connect_lock); + return misc_register(&acoustic_misc); +} + +static void __exit acoustic_exit(void) +{ + misc_deregister(&acoustic_misc); +} + +module_init(acoustic_init); +module_exit(acoustic_exit); + diff --git a/arch/arm/mach-msm/htc_akm_cal.c b/arch/arm/mach-msm/htc_akm_cal.c new file mode 100644 index 00000000000..943083fe0fb --- /dev/null +++ b/arch/arm/mach-msm/htc_akm_cal.c @@ -0,0 +1,64 @@ +/* arch/arm/mach-msm/htc_akm_cal.c + * + * Code to extract compass calibration information from ATAG set up + * by the bootloader. + * + * Copyright (C) 2007-2008 HTC Corporation + * Author: Farmer Tseng + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include + +/* configuration tags specific to AKM8976 */ +#define ATAG_AKM8976 0x89768976 /* AKM8976 */ + +#define MAX_CALI_SIZE 0x1000U + +static char akm_cal_ram[MAX_CALI_SIZE]; + +char *get_akm_cal_ram(void) +{ + return(akm_cal_ram); +} +EXPORT_SYMBOL(get_akm_cal_ram); + +static int __init parse_tag_akm(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_CALI_SIZE); + + printk(KERN_INFO "AKM Data size = %d , 0x%x, size = %d\n", + tag->hdr.size, tag->hdr.tag, size); + +#ifdef ATAG_COMPASS_DEBUG + unsigned i; + unsigned char *ptr; + + ptr = dptr; + printk(KERN_INFO + "AKM Data size = %d , 0x%x\n", + tag->hdr.size, tag->hdr.tag); + for (i = 0; i < size; i++) + printk(KERN_INFO "%02x ", *ptr++); +#endif + memcpy((void *)akm_cal_ram, (void *)dptr, size); + return 0; +} + +__tagtable(ATAG_AKM8976, parse_tag_akm); diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c new file mode 100644 index 00000000000..d49c23e864c --- /dev/null +++ b/arch/arm/mach-msm/htc_battery.c @@ -0,0 +1,771 @@ +/* arch/arm/mach-msm/htc_battery.c + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct wake_lock vbus_wake_lock; + +#define TRACE_BATT 0 + +#if TRACE_BATT +#define BATT(x...) printk(KERN_INFO "[BATT] " x) +#else +#define BATT(x...) do {} while (0) +#endif + +/* rpc related */ +#define APP_BATT_PDEV_NAME "rs30100001" +#define APP_BATT_PROG 0x30100001 +#define APP_BATT_VER 0 +#define HTC_PROCEDURE_BATTERY_NULL 0 +#define HTC_PROCEDURE_GET_BATT_LEVEL 1 +#define HTC_PROCEDURE_GET_BATT_INFO 2 +#define HTC_PROCEDURE_GET_CABLE_STATUS 3 +#define HTC_PROCEDURE_SET_BATT_DELTA 4 + +/* module debugger */ +#define HTC_BATTERY_DEBUG 1 +#define BATTERY_PREVENTION 1 + +/* Enable this will shut down if no battery */ +#define ENABLE_BATTERY_DETECTION 0 + +#define GPIO_BATTERY_DETECTION 21 +#define GPIO_BATTERY_CHARGER_EN 128 + +/* Charge current selection */ +#define GPIO_BATTERY_CHARGER_CURRENT 129 + +typedef enum { + DISABLE = 0, + ENABLE_SLOW_CHG, + ENABLE_FAST_CHG +} batt_ctl_t; + +/* This order is the same as htc_power_supplies[] + * And it's also the same as htc_cable_status_update() + */ +typedef enum { + CHARGER_BATTERY = 0, + CHARGER_USB, + CHARGER_AC +} charger_type_t; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + u32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ +}; + +struct htc_battery_info { + int present; + unsigned long update_time; + + /* lock to protect the battery info */ + struct mutex lock; + + /* lock held while calling the arm9 to query the battery info */ + struct mutex rpc_lock; + struct battery_info_reply rep; +}; + +static struct msm_rpc_endpoint *endpoint; + +static struct htc_battery_info htc_batt_info; + +static unsigned int cache_time = 1000; + +static int htc_battery_initial = 0; + +static enum power_supply_property htc_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property htc_power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +/* HTC dedicated attributes */ +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf); + +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static struct power_supply htc_power_supplies[] = { + { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = htc_battery_properties, + .num_properties = ARRAY_SIZE(htc_battery_properties), + .get_property = htc_battery_get_property, + }, + { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, +}; + + +/* -------------------------------------------------------------------------- */ + +#if defined(CONFIG_DEBUG_FS) +int htc_battery_set_charging(batt_ctl_t ctl); +static int batt_debug_set(void *data, u64 val) +{ + return htc_battery_set_charging((batt_ctl_t) val); +} + +static int batt_debug_get(void *data, u64 *val) +{ + return -ENOSYS; +} + +DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n"); +static int __init batt_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("htc_battery", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops); + + return 0; +} + +device_initcall(batt_debug_init); +#endif + +static int init_batt_gpio(void) +{ + if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0) + goto gpio_failed; + + return 0; + +gpio_failed: + return -EINVAL; + +} + +/* + * battery_charging_ctrl - battery charing control. + * @ctl: battery control command + * + */ +static int battery_charging_ctrl(batt_ctl_t ctl) +{ + int result = 0; + + switch (ctl) { + case DISABLE: + BATT("charger OFF\n"); + /* 0 for enable; 1 disable */ + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1); + break; + case ENABLE_SLOW_CHG: + BATT("charger ON (SLOW)\n"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + case ENABLE_FAST_CHG: + BATT("charger ON (FAST)\n"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + default: + printk(KERN_ERR "Not supported battery ctr called.!\n"); + result = -EINVAL; + break; + } + + return result; +} + +int htc_battery_set_charging(batt_ctl_t ctl) +{ + int rc; + + if ((rc = battery_charging_ctrl(ctl)) < 0) + goto result; + + if (!htc_battery_initial) { + htc_batt_info.rep.charging_enabled = ctl & 0x3; + } else { + mutex_lock(&htc_batt_info.lock); + htc_batt_info.rep.charging_enabled = ctl & 0x3; + mutex_unlock(&htc_batt_info.lock); + } +result: + return rc; +} + +int htc_battery_status_update(u32 curr_level) +{ + int notify; + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + notify = (htc_batt_info.rep.level != curr_level); + htc_batt_info.rep.level = curr_level; + mutex_unlock(&htc_batt_info.lock); + + if (notify) + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + return 0; +} + +int htc_cable_status_update(int status) +{ + int rc = 0; + unsigned source; + + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + switch(status) { + case CHARGER_BATTERY: + BATT("cable NOT PRESENT\n"); + htc_batt_info.rep.charging_source = CHARGER_BATTERY; + break; + case CHARGER_USB: + BATT("cable USB\n"); + htc_batt_info.rep.charging_source = CHARGER_USB; + break; + case CHARGER_AC: + BATT("cable AC\n"); + htc_batt_info.rep.charging_source = CHARGER_AC; + break; + default: + printk(KERN_ERR "%s: Not supported cable status received!\n", + __FUNCTION__); + rc = -EINVAL; + } + source = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + msm_hsusb_set_vbus_state(source == CHARGER_USB); + if (source == CHARGER_USB) { + wake_lock(&vbus_wake_lock); + } else { + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ / 2); + } + + /* if the power source changes, all power supplies may change state */ + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + + return rc; +} + +static int htc_get_batt_info(struct battery_info_reply *buffer) +{ + struct rpc_request_hdr req; + + struct htc_get_batt_info_rep { + struct rpc_reply_hdr hdr; + struct battery_info_reply info; + } rep; + + int rc; + + if (buffer == NULL) + return -EINVAL; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if ( rc < 0 ) + return rc; + + mutex_lock(&htc_batt_info.lock); + buffer->batt_id = be32_to_cpu(rep.info.batt_id); + buffer->batt_vol = be32_to_cpu(rep.info.batt_vol); + buffer->batt_temp = be32_to_cpu(rep.info.batt_temp); + buffer->batt_current = be32_to_cpu(rep.info.batt_current); + buffer->level = be32_to_cpu(rep.info.level); + buffer->charging_source = be32_to_cpu(rep.info.charging_source); + buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled); + buffer->full_bat = be32_to_cpu(rep.info.full_bat); + mutex_unlock(&htc_batt_info.lock); + + return 0; +} + +#if 0 +static int htc_get_cable_status(void) +{ + + struct rpc_request_hdr req; + + struct htc_get_cable_status_rep { + struct rpc_reply_hdr hdr; + int status; + } rep; + + int rc; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_CABLE_STATUS, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) + return rc; + + return be32_to_cpu(rep.status); +} +#endif + +/* -------------------------------------------------------------------------- */ +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + charger_type_t charger; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (charger == CHARGER_AC ? 1 : 0); + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (charger == CHARGER_USB ? 1 : 0); + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int htc_battery_get_charging_status(void) +{ + u32 level; + charger_type_t charger; + int ret; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + + switch (charger) { + case CHARGER_BATTERY: + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHARGER_USB: + case CHARGER_AC: + level = htc_batt_info.rep.level; + if (level == 100) + ret = POWER_SUPPLY_STATUS_FULL; + else + ret = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + ret = POWER_SUPPLY_STATUS_UNKNOWN; + } + mutex_unlock(&htc_batt_info.lock); + return ret; +} + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = htc_battery_get_charging_status(); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = htc_batt_info.present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + mutex_lock(&htc_batt_info.lock); + val->intval = htc_batt_info.rep.level; + mutex_unlock(&htc_batt_info.lock); + break; + default: + return -EINVAL; + } + + return 0; +} + +#define HTC_BATTERY_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \ + .show = htc_battery_show_property, \ + .store = NULL, \ +} + +static struct device_attribute htc_battery_attrs[] = { + HTC_BATTERY_ATTR(batt_id), + HTC_BATTERY_ATTR(batt_vol), + HTC_BATTERY_ATTR(batt_temp), + HTC_BATTERY_ATTR(batt_current), + HTC_BATTERY_ATTR(charging_source), + HTC_BATTERY_ATTR(charging_enabled), + HTC_BATTERY_ATTR(full_bat), +}; + +enum { + BATT_ID = 0, + BATT_VOL, + BATT_TEMP, + BATT_CURRENT, + CHARGING_SOURCE, + CHARGING_ENABLED, + FULL_BAT, +}; + +static int htc_rpc_set_delta(unsigned delta) +{ + struct set_batt_delta_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + req.data = cpu_to_be32(delta); + return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA, + &req, sizeof(req), 5 * HZ); +} + + +static ssize_t htc_battery_set_delta(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long delta = 0; + + delta = simple_strtoul(buf, NULL, 10); + + if (delta > 100) + return -EINVAL; + + mutex_lock(&htc_batt_info.rpc_lock); + rc = htc_rpc_set_delta(delta); + mutex_unlock(&htc_batt_info.rpc_lock); + if (rc < 0) + return rc; + return count; +} + +static struct device_attribute htc_set_delta_attrs[] = { + __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta), +}; + +static int htc_battery_create_attrs(struct device * dev) +{ + int i, j, rc; + + for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) { + rc = device_create_file(dev, &htc_battery_attrs[i]); + if (rc) + goto htc_attrs_failed; + } + + for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) { + rc = device_create_file(dev, &htc_set_delta_attrs[j]); + if (rc) + goto htc_delta_attrs_failed; + } + + goto succeed; + +htc_attrs_failed: + while (i--) + device_remove_file(dev, &htc_battery_attrs[i]); +htc_delta_attrs_failed: + while (j--) + device_remove_file(dev, &htc_set_delta_attrs[i]); +succeed: + return rc; +} + +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i = 0; + const ptrdiff_t off = attr - htc_battery_attrs; + + /* rpc lock is used to prevent two threads from calling + * into the get info rpc at the same time + */ + + mutex_lock(&htc_batt_info.rpc_lock); + /* check cache time to decide if we need to update */ + if (htc_batt_info.update_time && + time_before(jiffies, htc_batt_info.update_time + + msecs_to_jiffies(cache_time))) + goto dont_need_update; + + if (htc_get_batt_info(&htc_batt_info.rep) < 0) + printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__); + else + htc_batt_info.update_time = jiffies; +dont_need_update: + mutex_unlock(&htc_batt_info.rpc_lock); + + mutex_lock(&htc_batt_info.lock); + switch (off) { + case BATT_ID: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_id); + break; + case BATT_VOL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_vol); + break; + case BATT_TEMP: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_temp); + break; + case BATT_CURRENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_current); + break; + case CHARGING_SOURCE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_source); + break; + case CHARGING_ENABLED: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_enabled); + break; + case FULL_BAT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.full_bat); + break; + default: + i = -EINVAL; + } + mutex_unlock(&htc_batt_info.lock); + + return i; +} + +static int htc_battery_probe(struct platform_device *pdev) +{ + int i, rc; + + if (pdev->id != (APP_BATT_VER & RPC_VERSION_MAJOR_MASK)) + return -EINVAL; + + /* init battery gpio */ + if ((rc = init_batt_gpio()) < 0) { + printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__); + return rc; + } + + /* init structure data member */ + htc_batt_info.update_time = jiffies; + htc_batt_info.present = gpio_get_value(GPIO_BATTERY_DETECTION); + + /* init rpc */ + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __FUNCTION__, PTR_ERR(endpoint)); + return rc; + } + + /* init power supplier framework */ + for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) { + rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]); + if (rc) + printk(KERN_ERR "Failed to register power supply (%d)\n", rc); + } + + /* create htc detail attributes */ + htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev); + + /* After battery driver gets initialized, send rpc request to inquiry + * the battery status in case of we lost some info + */ + htc_battery_initial = 1; + + mutex_lock(&htc_batt_info.rpc_lock); + if (htc_get_batt_info(&htc_batt_info.rep) < 0) + printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + + htc_cable_status_update(htc_batt_info.rep.charging_source); + battery_charging_ctrl(htc_batt_info.rep.charging_enabled ? + ENABLE_SLOW_CHG : DISABLE); + + if (htc_rpc_set_delta(1) < 0) + printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); + htc_batt_info.update_time = jiffies; + mutex_unlock(&htc_batt_info.rpc_lock); + + if (htc_batt_info.rep.charging_enabled == 0) + battery_charging_ctrl(DISABLE); + + return 0; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = APP_BATT_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +/* batt_mtoa server definitions */ +#define BATT_MTOA_PROG 0x30100000 +#define BATT_MTOA_VERS 0 +#define RPC_BATT_MTOA_NULL 0 +#define RPC_BATT_MTOA_SET_CHARGING_PROC 1 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 + +struct rpc_batt_mtoa_set_charging_args { + int enable; +}; + +struct rpc_batt_mtoa_cable_status_update_args { + int status; +}; + +struct rpc_dem_battery_update_args { + uint32_t level; +}; + +static int handle_battery_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_BATT_MTOA_NULL: + return 0; + + case RPC_BATT_MTOA_SET_CHARGING_PROC: { + struct rpc_batt_mtoa_set_charging_args *args; + args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1); + args->enable = be32_to_cpu(args->enable); + BATT("set_charging: enable=%d\n",args->enable); + htc_battery_set_charging(args->enable); + return 0; + } + case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: { + struct rpc_batt_mtoa_cable_status_update_args *args; + args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); + args->status = be32_to_cpu(args->status); + BATT("cable_status_update: status=%d\n",args->status); + htc_cable_status_update(args->status); + return 0; + } + case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: { + struct rpc_dem_battery_update_args *args; + args = (struct rpc_dem_battery_update_args *)(req + 1); + args->level = be32_to_cpu(args->level); + BATT("dem_battery_update: level=%d\n",args->level); + htc_battery_status_update(args->level); + return 0; + } + default: + printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n", + __FUNCTION__, req->prog, req->vers, req->procedure); + return -ENODEV; + } +} + +static struct msm_rpc_server battery_server = { + .prog = BATT_MTOA_PROG, + .vers = BATT_MTOA_VERS, + .rpc_call = handle_battery_call, +}; + +static int __init htc_battery_init(void) +{ + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); + mutex_init(&htc_batt_info.lock); + mutex_init(&htc_batt_info.rpc_lock); + msm_rpc_create_server(&battery_server); + platform_driver_register(&htc_battery_driver); + return 0; +} + +module_init(htc_battery_init); +MODULE_DESCRIPTION("HTC Battery Driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/htc_headset.c b/arch/arm/mach-msm/htc_headset.c new file mode 100644 index 00000000000..a69a2e1ca5f --- /dev/null +++ b/arch/arm/mach-msm/htc_headset.c @@ -0,0 +1,1246 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC, Inc. + * + * Authors: + * Laurence Chen + * Nick Pelly + * Thomas Tsai + * Farmer Tseng + * + * 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. + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on trout. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define H2WI(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#define H2WE(fmt, arg...) \ + printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) + +#ifdef CONFIG_DEBUG_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + struct mutex mutex_lock; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + + void (*config_cpld) (int); + void (*init_cpld) (void); + /* for h2w */ + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); + + int htc_headset_flag; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; + + H2W_INFO h2w_info; + H2W_SPEED speed; + struct vreg *vreg_h2w; +}; +static struct h2w_info *hi; + +static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case H2W_NO_DEVICE: + return sprintf(buf, "No Device\n"); + case H2W_HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void button_pressed(void) +{ + H2W_DBG("button_pressed \n"); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG("button_released \n"); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +/***************** + * H2W proctocol * + *****************/ +static inline void h2w_begin_command(void) +{ + /* Disable H2W interrupt */ + set_irq_type(hi->irq_btn, IRQF_TRIGGER_HIGH); + disable_irq(hi->irq); + disable_irq(hi->irq_btn); + + /* Set H2W_CLK as output low */ + hi->set_clk(0); + hi->set_clk_dir(1); +} + +static inline void h2w_end_command(void) +{ + /* Set H2W_CLK as input */ + hi->set_clk_dir(0); + + /* Enable H2W interrupt */ + enable_irq(hi->irq); + enable_irq(hi->irq_btn); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); +} + +/* + * One bit write data + * ________ + * SCLK O ______| |______O(L) + * + * + * SDAT I + */ +static inline void one_clock_write(unsigned short flag) +{ + if (flag) + hi->set_dat(1); + else + hi->set_dat(0); + + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + hi->set_clk(0); +} + +/* + * One bit write data R/W bit + * ________ + * SCLK ______| |______O(L) + * 1----> 1-----> + * 2-------> ______ + * SDAT I + * O(H/L) + */ +static inline void one_clock_write_RWbit(unsigned short flag) +{ + if (flag) + hi->set_dat(1); + else + hi->set_dat(0); + + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + hi->set_clk(0); + hi->set_dat_dir(0); + udelay(hi->speed); +} + +/* + * H2W Reset + * ___________ + * SCLK O(L)______| |___O(L) + * 1----> + * 4-->1-->1-->1us--> + * ____ + * SDAT O(L)________ | |_______O(L) + * + * H2w reset command needs to be issued before every access + */ +static inline void h2w_reset(void) +{ + /* Set H2W_DAT as output low */ + hi->set_dat(0); + hi->set_dat_dir(1); + + udelay(hi->speed); + hi->set_clk(1); + udelay(4 * hi->speed); + hi->set_dat(1); + udelay(hi->speed); + hi->set_dat(0); + udelay(hi->speed); + hi->set_clk(0); + udelay(hi->speed); +} + +/* + * H2W Start + * ___________ + * SCLK O(L)______| |___O(L) + * 1----> + * 2----------->1--> + * + * SDAT O(L)______________________O(L) + */ +static inline void h2w_start(void) +{ + udelay(hi->speed); + hi->set_clk(1); + udelay(2 * hi->speed); + hi->set_clk(0); + udelay(hi->speed); +} + +/* + * H2W Ack + * __________ + * SCLK _____| |_______O(L) + * 1----> 1------> + * 2---------> + * ________________________ + * SDAT become Input mode here I + */ +static inline int h2w_ack(void) +{ + int retry_times = 0; + +ack_resend: + if (retry_times == MAX_ACK_RESEND_TIMES) + return -1; + + udelay(hi->speed); + hi->set_clk(1); + udelay(2 * hi->speed); + + if (!hi->get_dat()) { + retry_times++; + hi->set_clk(0); + udelay(hi->speed); + goto ack_resend; + } + + hi->set_clk(0); + udelay(hi->speed); + return 0; +} + +/* + * One bit read data + * ________ + * SCLK ______| |______O(L) + * 2----> 2-----> + * 2-------> + * SDAT I + */ +static unsigned char h2w_readc(void) +{ + unsigned char h2w_read_data = 0x0; + int index; + + for (index = 0; index < 8; index++) { + hi->set_clk(0); + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + if (hi->get_dat()) + h2w_read_data |= (1 << (7 - index)); + } + hi->set_clk(0); + udelay(hi->speed); + + return h2w_read_data; +} + +static int h2w_readc_cmd(H2W_ADDR address) +{ + int ret = -1, retry_times = 0; + unsigned char read_data; + +read_resend: + if (retry_times == MAX_HOST_RESEND_TIMES) + goto err_read; + + h2w_reset(); + h2w_start(); + /* Write address */ + one_clock_write(address & 0x1000); + one_clock_write(address & 0x0800); + one_clock_write(address & 0x0400); + one_clock_write(address & 0x0200); + one_clock_write(address & 0x0100); + one_clock_write(address & 0x0080); + one_clock_write(address & 0x0040); + one_clock_write(address & 0x0020); + one_clock_write(address & 0x0010); + one_clock_write(address & 0x0008); + one_clock_write(address & 0x0004); + one_clock_write(address & 0x0002); + one_clock_write(address & 0x0001); + one_clock_write_RWbit(1); + if (h2w_ack() < 0) { + H2W_DBG("Addr NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto read_resend; + } + + read_data = h2w_readc(); + + if (h2w_ack() < 0) { + H2W_DBG("Data NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto read_resend; + } + ret = (int)read_data; + +err_read: + if (ret < 0) + H2WE("NO ACK.\n"); + + return ret; +} + +static int h2w_writec_cmd(H2W_ADDR address, unsigned char data) +{ + int ret = -1; + int retry_times = 0; + +write_resend: + if (retry_times == MAX_HOST_RESEND_TIMES) + goto err_write; + + h2w_reset(); + h2w_start(); + + /* Write address */ + one_clock_write(address & 0x1000); + one_clock_write(address & 0x0800); + one_clock_write(address & 0x0400); + one_clock_write(address & 0x0200); + one_clock_write(address & 0x0100); + one_clock_write(address & 0x0080); + one_clock_write(address & 0x0040); + one_clock_write(address & 0x0020); + one_clock_write(address & 0x0010); + one_clock_write(address & 0x0008); + one_clock_write(address & 0x0004); + one_clock_write(address & 0x0002); + one_clock_write(address & 0x0001); + one_clock_write_RWbit(0); + if (h2w_ack() < 0) { + H2W_DBG("Addr NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto write_resend; + } + + /* Write data */ + hi->set_dat_dir(1); + one_clock_write(data & 0x0080); + one_clock_write(data & 0x0040); + one_clock_write(data & 0x0020); + one_clock_write(data & 0x0010); + one_clock_write(data & 0x0008); + one_clock_write(data & 0x0004); + one_clock_write(data & 0x0002); + one_clock_write_RWbit(data & 0x0001); + if (h2w_ack() < 0) { + H2W_DBG("Data NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto write_resend; + } + ret = 0; + +err_write: + if (ret < 0) + H2WE("NO ACK.\n"); + + return ret; +} + +static int h2w_get_fnkey(void) +{ + int ret; + h2w_begin_command(); + ret = h2w_readc_cmd(H2W_FNKEY_UPDOWN); + h2w_end_command(); + return ret; +} + +static int h2w_dev_init(H2W_INFO *ph2w_info) +{ + int ret = -1; + unsigned char ascr0 = 0; + int h2w_sys = 0, maxgpadd = 0, maxadd = 0, key = 0; + + hi->speed = H2W_50KHz; + h2w_begin_command(); + + /* read H2W_SYSTEM */ + h2w_sys = h2w_readc_cmd(H2W_SYSTEM); + if (h2w_sys == -1) { + H2WE("read H2W_SYSTEM(0x0000) failed.\n"); + goto err_plugin; + } + ph2w_info->ACC_CLASS = (h2w_sys & 0x03); + ph2w_info->AUDIO_DEVICE = (h2w_sys & 0x04) > 0 ? 1 : 0; + ph2w_info->HW_REV = (h2w_sys & 0x18) >> 3; + ph2w_info->SLEEP_PR = (h2w_sys & 0x20) >> 5; + ph2w_info->CLK_SP = (h2w_sys & 0xC0) >> 6; + + /* enter init mode */ + if (h2w_writec_cmd(H2W_ASCR0, H2W_ASCR_DEVICE_INI) < 0) { + H2WE("write H2W_ASCR0(0x0002) failed.\n"); + goto err_plugin; + } + udelay(10); + + /* read H2W_MAX_GP_ADD */ + maxgpadd = h2w_readc_cmd(H2W_MAX_GP_ADD); + if (maxgpadd == -1) { + H2WE("write H2W_MAX_GP_ADD(0x0001) failed.\n"); + goto err_plugin; + } + ph2w_info->CLK_SP += (maxgpadd & 0x60) >> 3; + ph2w_info->MAX_GP_ADD = (maxgpadd & 0x1F); + + /* read key group */ + if (ph2w_info->MAX_GP_ADD >= 1) { + ph2w_info->KEY_MAXADD = h2w_readc_cmd(H2W_KEY_MAXADD); + if (ph2w_info->KEY_MAXADD == -1) + goto err_plugin; + if (ph2w_info->KEY_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_ASCII_DOWN); + if (key < 0) + goto err_plugin; + ph2w_info->ASCII_DOWN = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 2) { + key = h2w_readc_cmd(H2W_ASCII_UP); + if (key == -1) + goto err_plugin; + ph2w_info->ASCII_UP = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 3) { + key = h2w_readc_cmd(H2W_FNKEY_UPDOWN); + if (key == -1) + goto err_plugin; + ph2w_info->FNKEY_UPDOWN = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 4) { + key = h2w_readc_cmd(H2W_KD_STATUS); + if (key == -1) + goto err_plugin; + ph2w_info->KD_STATUS = (key == 0x01) ? 1 : 0; + } + } + + /* read led group */ + if (ph2w_info->MAX_GP_ADD >= 2) { + ph2w_info->LED_MAXADD = h2w_readc_cmd(H2W_LED_MAXADD); + if (ph2w_info->LED_MAXADD == -1) + goto err_plugin; + if (ph2w_info->LED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_LEDCT0); + if (key == -1) + goto err_plugin; + ph2w_info->LEDCT0 = (key == 0x02) ? 1 : 0; + } + } + + /* read group 3, 4, 5 */ + if (ph2w_info->MAX_GP_ADD >= 3) { + maxadd = h2w_readc_cmd(H2W_CRDL_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + if (ph2w_info->MAX_GP_ADD >= 4) { + maxadd = h2w_readc_cmd(H2W_CARKIT_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + if (ph2w_info->MAX_GP_ADD >= 5) { + maxadd = h2w_readc_cmd(H2W_USBHOST_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + + /* read medical group */ + if (ph2w_info->MAX_GP_ADD >= 6) { + ph2w_info->MED_MAXADD = h2w_readc_cmd(H2W_MED_MAXADD); + if (ph2w_info->MED_MAXADD == -1) + goto err_plugin; + if (ph2w_info->MED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_MED_CONTROL); + if (key == -1) + goto err_plugin; + ph2w_info->DATA_EN = (key & 0x01); + ph2w_info->AP_EN = (key & 0x02) >> 1; + ph2w_info->AP_ID = (key & 0x1c) >> 2; + } + if (ph2w_info->MED_MAXADD >= 2) { + key = h2w_readc_cmd(H2W_MED_IN_DATA); + if (key == -1) + goto err_plugin; + } + } + + if (ph2w_info->AUDIO_DEVICE) + ascr0 = H2W_ASCR_AUDIO_IN | H2W_ASCR_ACT_EN; + else + ascr0 = H2W_ASCR_ACT_EN; + + if (h2w_writec_cmd(H2W_ASCR0, ascr0) < 0) + goto err_plugin; + udelay(10); + + ret = 0; + + /* adjust speed */ + if (ph2w_info->MAX_GP_ADD == 2) { + /* Remote control */ + hi->speed = H2W_250KHz; + } else if (ph2w_info->MAX_GP_ADD == 6) { + if (ph2w_info->MED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_MED_CONTROL); + if (key == -1) + goto err_plugin; + ph2w_info->DATA_EN = (key & 0x01); + ph2w_info->AP_EN = (key & 0x02) >> 1; + ph2w_info->AP_ID = (key & 0x1c) >> 2; + } + } + +err_plugin: + h2w_end_command(); + + return ret; +} + +static inline void h2w_dev_power_on(int on) +{ + if (!hi->vreg_h2w) + return; + + if (on) + vreg_enable(hi->vreg_h2w); + else + vreg_disable(hi->vreg_h2w); +} + +static int h2w_dev_detect(void) +{ + int ret = -1; + int retry_times; + + for (retry_times = 5; retry_times; retry_times--) { + /* Enable H2W Power */ + h2w_dev_power_on(1); + msleep(100); + memset(&hi->h2w_info, 0, sizeof(H2W_INFO)); + if (h2w_dev_init(&hi->h2w_info) < 0) { + h2w_dev_power_on(0); + msleep(100); + } else if (hi->h2w_info.MAX_GP_ADD == 2) { + ret = 0; + break; + } else { + printk(KERN_INFO "h2w_detect: detect error(%d)\n" + , hi->h2w_info.MAX_GP_ADD); + h2w_dev_power_on(0); + msleep(100); + } + printk(KERN_INFO "h2w_detect(%d)\n" + , hi->h2w_info.MAX_GP_ADD); + } + H2W_DBG("h2w_detect:(%d)\n", retry_times); + return ret; +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, switch_get_state(&hi->sdev) & + ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)); + mutex_unlock(&hi->mutex_lock); + hi->init_cpld(); + + /* Disable button */ + switch (hi->htc_headset_flag) { + case H2W_HTC_HEADSET: + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + break; + case H2W_DEVICE: + h2w_dev_power_on(0); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW); + disable_irq(hi->irq_btn); + /* 10ms (5-15 with 10ms tick) */ + hi->btn_debounce_time = ktime_set(0, 10000000); + hi->set_clk_dir(0); + hi->set_dat_dir(0); + break; + } + + hi->htc_headset_flag = 0; + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(int type) +{ + unsigned long irq_flags; + int state; + + H2W_DBG(""); + + hi->htc_headset_flag = type; + state = BIT_HEADSET | BIT_HEADSET_NO_MIC; + + state = switch_get_state(&hi->sdev); + state &= ~(BIT_HEADSET_NO_MIC | BIT_HEADSET); + switch (type) { + case H2W_HTC_HEADSET: + printk(KERN_INFO "insert_headset H2W_HTC_HEADSET\n"); + state |= BIT_HEADSET; + hi->ignore_btn = !gpio_get_value(hi->cable_in2); + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + hi->debounce_time = ktime_set(0, 200000000); /* 20 ms */ + break; + case H2W_DEVICE: + if (h2w_dev_detect() < 0) { + printk(KERN_INFO "H2W_DEVICE -- Non detect\n"); + remove_headset(); + } else { + printk(KERN_INFO "H2W_DEVICE -- detect\n"); + hi->btn_debounce_time = ktime_set(0, 0); + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); + local_irq_restore(irq_flags); + state |= BIT_HEADSET; + } + break; + case H2W_USB_CRADLE: + state |= BIT_HEADSET_NO_MIC; + break; + case H2W_UART_DEBUG: + hi->config_cpld(hi->debug_uart); + printk(KERN_INFO "switch to H2W_UART_DEBUG\n"); + default: + return; + } + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + +} +#if 0 +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, H2W_NO_DEVICE); + + hi->init_cpld(); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} +#endif +static int is_accessary_pluged_in(void) +{ + int type = 0; + int clk1 = 0, dat1 = 0, clk2 = 0, dat2 = 0, clk3 = 0, dat3 = 0; + + /* Step1: save H2W_CLK and H2W_DAT */ + /* Delay 10ms for pin stable. */ + msleep(10); + clk1 = gpio_get_value(hi->h2w_clk); + dat1 = gpio_get_value(hi->h2w_data); + + /* + * Step2: set GPIO_CABLE_IN1 as output high and GPIO_CABLE_IN2 as + * input + */ + gpio_direction_output(hi->cable_in1, 1); + gpio_direction_input(hi->cable_in2); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Step 3: save H2W_CLK and H2W_DAT */ + clk2 = gpio_get_value(hi->h2w_clk); + dat2 = gpio_get_value(hi->h2w_data); + + /* + * Step 4: set GPIO_CABLE_IN1 as input and GPIO_CABLE_IN2 as output + * high + */ + gpio_direction_input(hi->cable_in1); + gpio_direction_output(hi->cable_in2, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Step 5: save H2W_CLK and H2W_DAT */ + clk3 = gpio_get_value(hi->h2w_clk); + dat3 = gpio_get_value(hi->h2w_data); + + /* Step 6: set both GPIO_CABLE_IN1 and GPIO_CABLE_IN2 as input */ + gpio_direction_input(hi->cable_in1); + gpio_direction_input(hi->cable_in2); + + H2W_DBG("(%d,%d) (%d,%d) (%d,%d)\n", + clk1, dat1, clk2, dat2, clk3, dat3); + + if ((clk1 == 0) && (dat1 == 1) && + (clk2 == 0) && (dat2 == 1) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_HTC_HEADSET; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 0) && (dat2 == 0) && + (clk3 == 0) && (dat3 == 0)) + type = NORMAL_HEARPHONE; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 1) && (dat2 == 0) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_DEVICE; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 1) && (dat2 == 1) && + (clk3 == 1) && (dat3 == 1)) + type = H2W_USB_CRADLE; + else if ((clk1 == 0) && (dat1 == 1) && + (clk2 == 1) && (dat2 == 1) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_UART_DEBUG; + else + type = H2W_NO_DEVICE; + + return type; +} + + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int type; + + H2W_DBG(""); + + if (gpio_get_value(hi->cable_in1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) != H2W_NO_DEVICE) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + hi->config_cpld(H2W_GPIO); + + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Something plugged in, lets make sure its a headset */ + type = is_accessary_pluged_in(); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + insert_headset(type); +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + int key, press, keyname, h2w_key = 1; + + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) { + switch (hi->htc_headset_flag) { + case H2W_HTC_HEADSET: + if (gpio_get_value(hi->cable_in2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && + !atomic_read(&hi->btn_state)) + button_pressed(); + } + break; + case H2W_DEVICE: + if ((hi->get_dat() == 1) && (hi->get_clk() == 1)) { + /* Don't do anything because H2W pull out. */ + H2WE("Remote Control pull out.\n"); + } else { + key = h2w_get_fnkey(); + press = (key > 0x7F) ? 0 : 1; + keyname = key & 0x7F; + /* H2WI("key = %d, press = %d, + keyname = %d \n", + key, press, keyname); */ + switch (keyname) { + case H2W_KEY_PLAY: + H2WI("H2W_KEY_PLAY"); + key = KEY_PLAYPAUSE; + break; + case H2W_KEY_FORWARD: + H2WI("H2W_KEY_FORWARD"); + key = KEY_NEXTSONG; + break; + case H2W_KEY_BACKWARD: + H2WI("H2W_KEY_BACKWARD"); + key = KEY_PREVIOUSSONG; + break; + case H2W_KEY_VOLUP: + H2WI("H2W_KEY_VOLUP"); + key = KEY_VOLUMEUP; + break; + case H2W_KEY_VOLDOWN: + H2WI("H2W_KEY_VOLDOWN"); + key = KEY_VOLUMEDOWN; + break; + case H2W_KEY_PICKUP: + H2WI("H2W_KEY_PICKUP"); + key = KEY_SEND; + break; + case H2W_KEY_HANGUP: + H2WI("H2W_KEY_HANGUP"); + key = KEY_END; + break; + case H2W_KEY_MUTE: + H2WI("H2W_KEY_MUTE"); + key = KEY_MUTE; + break; + case H2W_KEY_HOLD: + H2WI("H2W_KEY_HOLD"); + break; + default: + H2WI("default"); + h2w_key = 0; + } + if (h2w_key) { + if (press) + H2WI("Press\n"); + else + H2WI("Release\n"); + input_report_key(hi->input, key, press); + } + } + break; + } /* end switch */ + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW); + do { + value1 = gpio_get_value(hi->cable_in1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->cable_in1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries), device=%d", + value2, (10-retry_limit), switch_get_state(&hi->sdev)); + + if ((switch_get_state(&hi->sdev) == H2W_NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(hi->cable_in2); + if (hi->htc_headset_flag != H2W_DEVICE) + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->cable_in2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static int h2w_debug_set(void *data, u64 val) +{ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, (int)val); + mutex_unlock(&hi->mutex_lock); + return 0; +} + +static int h2w_debug_get(void *data, u64 *val) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int h2w_probe(struct platform_device *pdev) +{ + int ret; + struct h2w_platform_data *pdata = pdev->dev.platform_data; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + + hi->htc_headset_flag = 0; + hi->cable_in1 = pdata->cable_in1; + hi->cable_in2 = pdata->cable_in2; + hi->h2w_clk = pdata->h2w_clk; + hi->h2w_data = pdata->h2w_data; + hi->debug_uart = pdata->debug_uart; + hi->config_cpld = pdata->config_cpld; + hi->init_cpld = pdata->init_cpld; + hi->set_dat = pdata->set_dat; + hi->set_clk = pdata->set_clk; + hi->set_dat_dir = pdata->set_dat_dir; + hi->set_clk_dir = pdata->set_clk_dir; + hi->get_dat = pdata->get_dat; + hi->get_clk = pdata->get_clk; + hi->speed = H2W_50KHz; + /* obtain needed VREGs */ + if (pdata->power_name) + hi->vreg_h2w = vreg_get(0, pdata->power_name); + + mutex_init(&hi->mutex_lock); + + hi->sdev.name = "h2w"; + hi->sdev.print_name = h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(hi->cable_in1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(hi->cable_in2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(hi->cable_in1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(hi->cable_in2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(hi->cable_in1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(hi->cable_in2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + hi->init_cpld(); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + set_bit(EV_SYN, hi->input->evbit); + set_bit(EV_KEY, hi->input->evbit); + set_bit(KEY_MEDIA, hi->input->keybit); + set_bit(KEY_NEXTSONG, hi->input->keybit); + set_bit(KEY_PLAYPAUSE, hi->input->keybit); + set_bit(KEY_PREVIOUSSONG, hi->input->keybit); + set_bit(KEY_MUTE, hi->input->keybit); + set_bit(KEY_VOLUMEUP, hi->input->keybit); + set_bit(KEY_VOLUMEDOWN, hi->input->keybit); + set_bit(KEY_END, hi->input->keybit); + set_bit(KEY_SEND, hi->input->keybit); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(hi->cable_in2); +err_request_button_gpio: + gpio_free(hi->cable_in1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(hi->cable_in2); + gpio_free(hi->cable_in1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + + +static struct platform_driver h2w_driver = { + .probe = h2w_probe, + .remove = h2w_remove, + .driver = { + .name = "h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init h2w_init(void) +{ + H2W_DBG(""); + return platform_driver_register(&h2w_driver); +} + +static void __exit h2w_exit(void) +{ + platform_driver_unregister(&h2w_driver); +} + +module_init(h2w_init); +module_exit(h2w_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC 2 Wire detection driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c new file mode 100644 index 00000000000..bd286c9f3a8 --- /dev/null +++ b/arch/arm/mach-msm/htc_power_supply.c @@ -0,0 +1,616 @@ +/* arch/arm/mach-msm/htc_battery.c + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "board-mahimahi.h" + +extern void notify_usb_connected(int); + +static char *supply_list[] = { + "battery", +}; + +static struct switch_dev dock_switch = { + .name = "dock", +}; + +static int vbus_present; +static int usb_status; +static bool dock_mains; + +struct dock_state { + struct mutex lock; + u32 t; + u32 last_edge_t[2]; + u32 last_edge_i[2]; + bool level; + bool dock_connected_unknown; +}; + +static struct workqueue_struct *dock_wq; +static struct work_struct dock_work; +static struct wake_lock dock_work_wake_lock; +static struct dock_state ds = { + .lock = __MUTEX_INITIALIZER(ds.lock), +}; + +#define _GPIO_DOCK MAHIMAHI_GPIO_DOCK + +#define dock_out(n) gpio_direction_output(_GPIO_DOCK, n) +#define dock_out2(n) gpio_set_value(_GPIO_DOCK, n) +#define dock_in() gpio_direction_input(_GPIO_DOCK) +#define dock_read() gpio_get_value(_GPIO_DOCK) + +#define MFM_DELAY_NS 10000 + +static int dock_get_edge(struct dock_state *s, u32 timeout, u32 tmin, u32 tmax) +{ + bool lin; + bool in = s->level; + u32 t; + do { + lin = in; + in = dock_read(); + t = msm_read_fast_timer(); + if (in != lin) { + s->last_edge_t[in] = t; + s->last_edge_i[in] = 0; + s->level = in; + if ((s32)(t - tmin) < 0 || (s32)(t - tmax) > 0) + return -1; + return 1; + } + } while((s32)(t - timeout) < 0); + return 0; +} + +static bool dock_sync(struct dock_state *s, u32 timeout) +{ + u32 t; + + s->level = dock_read(); + t = msm_read_fast_timer(); + + if (!dock_get_edge(s, t + timeout, 0, 0)) + return false; + s->last_edge_i[s->level] = 2; + return !!dock_get_edge(s, + s->last_edge_t[s->level] + MFM_DELAY_NS * 4, 0, 0); +} + +static int dock_get_next_bit(struct dock_state *s) +{ + u32 i = s->last_edge_i[!s->level] + ++s->last_edge_i[s->level]; + u32 target = s->last_edge_t[!s->level] + MFM_DELAY_NS * i; + u32 timeout = target + MFM_DELAY_NS / 2; + u32 tmin = target - MFM_DELAY_NS / 4; + u32 tmax = target + MFM_DELAY_NS / 4; + return dock_get_edge(s, timeout, tmin, tmax); +} + +static u32 dock_get_bits(struct dock_state *s, int count, int *errp) +{ + u32 data = 0; + u32 m = 1; + int ret; + int err = 0; + while (count--) { + ret = dock_get_next_bit(s); + if (ret) + data |= m; + if (ret < 0) + err++; + m <<= 1; + } + if (errp) + *errp = err; + return data; +} + +static void dock_delay(u32 timeout) +{ + timeout += msm_read_fast_timer(); + while (((s32)(msm_read_fast_timer() - timeout)) < 0) + ; +} + +static int dock_send_bits(struct dock_state *s, u32 data, int count, int period) +{ + u32 t, t0, to; + + dock_out2(s->level); + t = to = 0; + t0 = msm_read_fast_timer(); + + while (count--) { + if (data & 1) + dock_out2((s->level = !s->level)); + + t = msm_read_fast_timer() - t0; + if (t - to > period / 2) { + pr_info("dock: to = %d, t = %d\n", to, t); + return -EIO; + } + + to += MFM_DELAY_NS; + do { + t = msm_read_fast_timer() - t0; + } while (t < to); + if (t - to > period / 4) { + pr_info("dock: to = %d, t = %d\n", to, t); + return -EIO; + } + data >>= 1; + } + return 0; +} + +static u32 mfm_encode(u16 data, int count, bool p) +{ + u32 mask; + u32 mfm = 0; + u32 clock = ~data & ~(data << 1 | !!p); + for (mask = 1UL << (count - 1); mask; mask >>= 1) { + mfm |= (data & mask); + mfm <<= 1; + mfm |= (clock & mask); + } + return mfm; +} + +static u32 mfm_decode(u32 mfm) +{ + u32 data = 0; + u32 clock = 0; + u32 mask = 1; + while (mfm) { + if (mfm & 1) + clock |= mask; + mfm >>= 1; + if (mfm & 1) + data |= mask; + mfm >>= 1; + mask <<= 1; + } + return data; +} + +static int dock_command(struct dock_state *s, u16 cmd, int len, int retlen) +{ + u32 mfm; + int count; + u32 data = cmd; + int ret; + int err = -1; + unsigned long flags; + + data = data << 2 | 3; /* add 0101 mfm data*/ + mfm = mfm_encode(data, len, false); + count = len * 2 + 2; + + msm_enable_fast_timer(); + local_irq_save(flags); + ret = dock_send_bits(s, mfm, count, MFM_DELAY_NS); + if (!ret) { + dock_in(); + if (dock_sync(s, MFM_DELAY_NS * 5)) + ret = dock_get_bits(s, retlen * 2, &err); + else + ret = -1; + dock_out(s->level); + } + local_irq_restore(flags); + + dock_delay((ret < 0) ? MFM_DELAY_NS * 6 : MFM_DELAY_NS * 2); + msm_disable_fast_timer(); + if (ret < 0) { + pr_warning("dock_command: %x: no response\n", cmd); + return ret; + } + data = mfm_decode(ret); + mfm = mfm_encode(data, retlen, true); + if (mfm != ret || err) { + pr_warning("dock_command: %x: bad response, " + "data %x, mfm %x %x, err %d\n", + cmd, data, mfm, ret, err); + return -EIO; + } + return data; +} + +static int dock_command_retry(struct dock_state *s, u16 cmd, size_t len, size_t retlen) +{ + int retry = 20; + int ret; + while (retry--) { + ret = dock_command(s, cmd, len, retlen); + if (ret >= 0) + return ret; + if (retry != 19) + msleep(10); + } + s->dock_connected_unknown = true; + return -EIO; +} + +static int dock_read_single(struct dock_state *s, int addr) +{ + int ret = -1, last; + int retry = 20; + while (retry--) { + last = ret; + ret = dock_command_retry(s, addr << 1, 6, 8); + if (ret < 0 || ret == last) + return ret; + } + return -EIO; +} + +static int dock_read_multi(struct dock_state *s, int addr, u8 *data, size_t len) +{ + int ret; + int i; + u8 suml, sumr = -1; + int retry = 20; + while (retry--) { + suml = 0; + for (i = 0; i <= len; i++) { + ret = dock_command_retry(s, (addr + i) << 1, 6, 8); + if (ret < 0) + return ret; + if (i < len) { + data[i] = ret; + suml += ret; + } else + sumr = ret; + } + if (sumr == suml) + return 0; + + pr_warning("dock_read_multi(%x): bad checksum, %x != %x\n", + addr, sumr, suml); + } + return -EIO; +} + +static int dock_write_byte(struct dock_state *s, int addr, u8 data) +{ + return dock_command_retry(s, 1 | addr << 1 | data << 4, 6 + 8, 1); +} + +static int dock_write_multi(struct dock_state *s, int addr, u8 *data, size_t len) +{ + int ret; + int i; + u8 sum; + int retry = 2; + while (retry--) { + sum = 0; + for (i = 0; i < len; i++) { + sum += data[i]; + ret = dock_write_byte(s, addr + i, data[i]); + if (ret < 0) + return ret; + } + ret = dock_write_byte(s, addr + len, sum); + if (ret <= 0) + return ret; + } + return -EIO; +} + +static int dock_acquire(struct dock_state *s) +{ + mutex_lock(&s->lock); + dock_in(); + if (dock_read()) { + /* Allow some time for the dock pull-down resistor to discharge + * the capasitor. + */ + msleep(20); + if (dock_read()) { + mutex_unlock(&s->lock); + return -ENOENT; + } + } + dock_out(0); + s->level = false; + return 0; +} + +static void dock_release(struct dock_state *s) +{ + dock_in(); + mutex_unlock(&s->lock); +} + +enum { + DOCK_TYPE = 0x0, + DOCK_BT_ADDR = 0x1, /* - 0x7 */ + + DOCK_PIN_CODE = 0x0, +}; + +static ssize_t bt_addr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + u8 bt_addr[6]; + + ret = dock_acquire(&ds); + if (ret < 0) + return ret; + ret = dock_read_multi(&ds, DOCK_BT_ADDR, bt_addr, 6); + dock_release(&ds); + if (ret < 0) + return ret; + + return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + bt_addr[0], bt_addr[1], bt_addr[2], + bt_addr[3], bt_addr[4], bt_addr[5]); +} +static DEVICE_ATTR(bt_addr, S_IRUGO | S_IWUSR, bt_addr_show, NULL); + +static ssize_t bt_pin_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret, i; + u8 pin[4]; + + if (size < 4) + return -EINVAL; + + for (i = 0; i < sizeof(pin); i++) { + if ((pin[i] = buf[i] - '0') > 10) + return -EINVAL; + } + + ret = dock_acquire(&ds); + if (ret < 0) + return ret; + ret = dock_write_multi(&ds, DOCK_PIN_CODE, pin, 4); + dock_release(&ds); + if (ret < 0) + return ret; + + return size; +} +static DEVICE_ATTR(bt_pin, S_IRUGO | S_IWUSR, NULL, bt_pin_store); + + +static int power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (vbus_present && (usb_status == 2 || dock_mains)); + else + val->intval = vbus_present; + return 0; +} + +static enum power_supply_property power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply ac_supply = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = power_properties, + .num_properties = ARRAY_SIZE(power_properties), + .get_property = power_get_property, +}; + +static struct power_supply usb_supply = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = power_properties, + .num_properties = ARRAY_SIZE(power_properties), + .get_property = power_get_property, +}; + +/* rpc related */ +#define APP_BATT_PDEV_NAME "rs30100001:00000000" +#define APP_BATT_PROG 0x30100001 +#define APP_BATT_VER MSM_RPC_VERS(0,0) +#define HTC_PROCEDURE_BATTERY_NULL 0 +#define HTC_PROCEDURE_GET_BATT_LEVEL 1 +#define HTC_PROCEDURE_GET_BATT_INFO 2 +#define HTC_PROCEDURE_GET_CABLE_STATUS 3 +#define HTC_PROCEDURE_SET_BATT_DELTA 4 + +static struct msm_rpc_endpoint *endpoint; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + u32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ +}; + +static void dock_work_proc(struct work_struct *work) +{ + int dockid; + + if (!vbus_present || dock_acquire(&ds)) + goto no_dock; + + if (ds.dock_connected_unknown) { + /* force a new dock notification if a command failed */ + switch_set_state(&dock_switch, 0); + ds.dock_connected_unknown = false; + } + + dockid = dock_read_single(&ds, DOCK_TYPE); + dock_release(&ds); + + pr_info("Detected dock with ID %02x\n", dockid); + if (dockid >= 0) { + msm_hsusb_set_vbus_state(0); + dock_mains = !!(dockid & 0x80); + switch_set_state(&dock_switch, (dockid & 1) ? 2 : 1); + goto done; + } +no_dock: + dock_mains = false; + switch_set_state(&dock_switch, 0); + msm_hsusb_set_vbus_state(vbus_present); +done: + power_supply_changed(&ac_supply); + power_supply_changed(&usb_supply); + wake_unlock(&dock_work_wake_lock); +} + +static int htc_battery_probe(struct platform_device *pdev) +{ + struct rpc_request_hdr req; + struct htc_get_batt_info_rep { + struct rpc_reply_hdr hdr; + struct battery_info_reply info; + } rep; + + int rc; + + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __FUNCTION__, PTR_ERR(endpoint)); + return PTR_ERR(endpoint); + } + + /* must do this or we won't get cable status updates */ + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + + power_supply_register(&pdev->dev, &ac_supply); + power_supply_register(&pdev->dev, &usb_supply); + + INIT_WORK(&dock_work, dock_work_proc); + dock_wq = create_singlethread_workqueue("dock"); + + return 0; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = APP_BATT_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +/* batt_mtoa server definitions */ +#define BATT_MTOA_PROG 0x30100000 +#define BATT_MTOA_VERS 0 +#define RPC_BATT_MTOA_NULL 0 +#define RPC_BATT_MTOA_SET_CHARGING_PROC 1 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 + +struct rpc_batt_mtoa_cable_status_update_args { + int status; +}; + +static int handle_battery_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct rpc_batt_mtoa_cable_status_update_args *args; + + if (req->procedure != RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC) + return 0; + + args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); + args->status = be32_to_cpu(args->status); + pr_info("cable_status_update: status=%d\n",args->status); + + args->status = !!args->status; + + vbus_present = args->status; + wake_lock(&dock_work_wake_lock); + queue_work(dock_wq, &dock_work); + return 0; +} + +void notify_usb_connected(int status) +{ + printk("### notify_usb_connected(%d) ###\n", status); + usb_status = status; + power_supply_changed(&ac_supply); + power_supply_changed(&usb_supply); +} + +int is_ac_power_supplied(void) +{ + return vbus_present && (usb_status == 2 || dock_mains); +} + +static struct msm_rpc_server battery_server = { + .prog = BATT_MTOA_PROG, + .vers = BATT_MTOA_VERS, + .rpc_call = handle_battery_call, +}; + +static int __init htc_battery_init(void) +{ + int ret; + gpio_request(_GPIO_DOCK, "dock"); + dock_in(); + wake_lock_init(&dock_work_wake_lock, WAKE_LOCK_SUSPEND, "dock"); + platform_driver_register(&htc_battery_driver); + msm_rpc_create_server(&battery_server); + if (switch_dev_register(&dock_switch) == 0) { + ret = device_create_file(dock_switch.dev, &dev_attr_bt_addr); + WARN_ON(ret); + ret = device_create_file(dock_switch.dev, &dev_attr_bt_pin); + WARN_ON(ret); + } + + return 0; +} + +module_init(htc_battery_init); +MODULE_DESCRIPTION("HTC Battery Driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c new file mode 100644 index 00000000000..2ec2c7f4bb1 --- /dev/null +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -0,0 +1,281 @@ +/* arch/arm/mach-msm/htc_pwrsink.c + * + * Copyright (C) 2008 HTC Corporation + * Copyright (C) 2008 Google, Inc. + * Author: San Mehat + * Kant Kang + * Eiven Peng + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" + +enum { + PWRSINK_DEBUG_CURR_CHANGE = 1U << 0, + PWRSINK_DEBUG_CURR_CHANGE_AUDIO = 1U << 1, +}; +static int pwrsink_debug_mask; +module_param_named(debug_mask, pwrsink_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +static int initialized; +static unsigned audio_path = 1; /* HTC_SND_DEVICE_SPEAKER = 1 */ +static struct pwr_sink_audio audio_sink_array[PWRSINK_AUDIO_LAST + 1]; +static struct pwr_sink *sink_array[PWRSINK_LAST + 1]; +static DEFINE_SPINLOCK(sink_lock); +static DEFINE_SPINLOCK(audio_sink_lock); +static unsigned long total_sink; +static uint32_t *smem_total_sink; + +int htc_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) +{ + unsigned long flags; + + if (!smem_total_sink) + smem_total_sink = smem_alloc(SMEM_ID_VENDOR0, sizeof(uint32_t)); + + if (!initialized) + return -EAGAIN; + + if (id < 0 || id > PWRSINK_LAST) + return -EINVAL; + + spin_lock_irqsave(&sink_lock, flags); + + if (!sink_array[id]) { + spin_unlock_irqrestore(&sink_lock, flags); + return -ENOENT; + } + + if (sink_array[id]->percent_util == percent_utilized) { + spin_unlock_irqrestore(&sink_lock, flags); + return 0; + } + + total_sink -= (sink_array[id]->ua_max * + sink_array[id]->percent_util / 100); + sink_array[id]->percent_util = percent_utilized; + total_sink += (sink_array[id]->ua_max * + sink_array[id]->percent_util / 100); + + if (smem_total_sink) + *smem_total_sink = total_sink / 1000; + + pr_debug("htc_pwrsink: ID %d, Util %d%%, Total %lu uA %s\n", + id, percent_utilized, total_sink, + smem_total_sink ? "SET" : ""); + + spin_unlock_irqrestore(&sink_lock, flags); + + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_set); + +static void compute_audio_current(void) +{ + /* unsigned long flags; */ + unsigned max_percent = 0; + int i, active_audio_sinks = 0; + pwrsink_audio_id_type last_active_audio_sink = 0; + + /* Make sure this segment will be spinlocked + before computing by calling function. */ + /* spin_lock_irqsave(&audio_sink_lock, flags); */ + for (i = 0; i <= PWRSINK_AUDIO_LAST; ++i) { + max_percent = (audio_sink_array[i].percent > max_percent) ? + audio_sink_array[i].percent : max_percent; + if (audio_sink_array[i].percent > 0) { + active_audio_sinks++; + last_active_audio_sink = i; + } + } + if (active_audio_sinks == 0) + htc_pwrsink_set(PWRSINK_AUDIO, 0); + else if (active_audio_sinks == 1) { + pwrsink_audio_id_type laas = last_active_audio_sink; + /* TODO: add volume and routing path current. */ + if (audio_path == 1) /* Speaker */ + htc_pwrsink_set(PWRSINK_AUDIO, + audio_sink_array[laas].percent); + else + htc_pwrsink_set(PWRSINK_AUDIO, + audio_sink_array[laas].percent * 9 / 10); + } else if (active_audio_sinks > 1) { + /* TODO: add volume and routing path current. */ + if (audio_path == 1) /* Speaker */ + htc_pwrsink_set(PWRSINK_AUDIO, max_percent); + else + htc_pwrsink_set(PWRSINK_AUDIO, max_percent * 9 / 10); + } + /* spin_unlock_irqrestore(&audio_sink_lock, flags); */ + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: active_audio_sinks=%d, audio_path=%d\n", __func__, + active_audio_sinks, audio_path); +} + +int htc_pwrsink_audio_set(pwrsink_audio_id_type id, unsigned percent_utilized) +{ + unsigned long flags; + + if (id < 0 || id > PWRSINK_AUDIO_LAST) + return -EINVAL; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: id=%d, percent=%d, percent_old=%d\n", __func__, + id, percent_utilized, audio_sink_array[id].percent); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_sink_array[id].percent == percent_utilized) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_sink_array[id].percent = percent_utilized; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_set); + +int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, unsigned volume) +{ + unsigned long flags; + + if (id < 0 || id > PWRSINK_AUDIO_LAST) + return -EINVAL; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: id=%d, volume=%d, volume_old=%d\n", __func__, + id, volume, audio_sink_array[id].volume); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_sink_array[id].volume == volume) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_sink_array[id].volume = volume; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_volume_set); + +int htc_pwrsink_audio_path_set(unsigned path) +{ + unsigned long flags; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: path=%d, path_old=%d\n", + __func__, path, audio_path); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_path == path) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_path = path; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_path_set); + +void htc_pwrsink_suspend_early(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); +} + +int htc_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + + if (pdata && pdata->suspend_late) + pdata->suspend_late(pdev, state); + else + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 13); + return 0; +} + +int htc_pwrsink_resume_early(struct platform_device *pdev) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + + if (pdata && pdata->resume_early) + pdata->resume_early(pdev); + else + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + return 0; +} + +void htc_pwrsink_resume_late(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 100); +} + +struct early_suspend htc_pwrsink_early_suspend = { + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, + .suspend = htc_pwrsink_suspend_early, + .resume = htc_pwrsink_resume_late, +}; + +static int __init htc_pwrsink_probe(struct platform_device *pdev) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (!pdata) + return -EINVAL; + + total_sink = 0; + for (i = 0; i < pdata->num_sinks; i++) { + sink_array[pdata->sinks[i].id] = &pdata->sinks[i]; + total_sink += (pdata->sinks[i].ua_max * + pdata->sinks[i].percent_util / 100); + } + + initialized = 1; + + if (pdata->suspend_early) + htc_pwrsink_early_suspend.suspend = pdata->suspend_early; + if (pdata->resume_late) + htc_pwrsink_early_suspend.resume = pdata->resume_late; + register_early_suspend(&htc_pwrsink_early_suspend); + + return 0; +} + +static struct platform_driver htc_pwrsink_driver = { + .probe = htc_pwrsink_probe, + .suspend_late = htc_pwrsink_suspend_late, + .resume_early = htc_pwrsink_resume_early, + .driver = { + .name = "htc_pwrsink", + .owner = THIS_MODULE, + }, +}; + +static int __init htc_pwrsink_init(void) +{ + initialized = 0; + memset(sink_array, 0, sizeof(sink_array)); + return platform_driver_register(&htc_pwrsink_driver); +} + +module_init(htc_pwrsink_init); diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c new file mode 100644 index 00000000000..2d381a97c07 --- /dev/null +++ b/arch/arm/mach-msm/htc_wifi_nvs.c @@ -0,0 +1,55 @@ +/* arch/arm/mach-msm/htc_wifi_nvs.c + * + * Code to extract WiFi calibration information from ATAG set up + * by the bootloader. + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include + +/* configuration tags specific to msm */ +#define ATAG_MSM_WIFI 0x57494649 /* MSM WiFi */ + +#define MAX_NVS_SIZE 0x800U +static unsigned char wifi_nvs_ram[MAX_NVS_SIZE]; + +unsigned char *get_wifi_nvs_ram( void ) +{ + return( wifi_nvs_ram ); +} +EXPORT_SYMBOL(get_wifi_nvs_ram); + +static int __init parse_tag_msm_wifi(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_NVS_SIZE); +#ifdef ATAG_MSM_WIFI_DEBUG + unsigned i; + + printk("WiFi Data size = %d , 0x%x\n", tag->hdr.size, tag->hdr.tag); + for (i = 0; i < size; i++) + printk("%02x ", *dptr++); +#endif + memcpy( (void *)wifi_nvs_ram, (void *)dptr, size ); + return 0; +} + +__tagtable(ATAG_MSM_WIFI, parse_tag_msm_wifi); diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c new file mode 100644 index 00000000000..c2592ec9efb --- /dev/null +++ b/arch/arm/mach-msm/hw3d.c @@ -0,0 +1,407 @@ +/* arch/arm/mach-msm/hw3d.c + * + * Register/Interrupt access for userspace 3D library. + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(hw3d_lock); +static DECLARE_WAIT_QUEUE_HEAD(hw3d_queue); +static int hw3d_pending; +static int hw3d_disabled; + +static struct clk *grp_clk; +static struct clk *imem_clk; +DECLARE_MUTEX(hw3d_sem); +static unsigned int hw3d_granted; +static struct file *hw3d_granted_file; + +static irqreturn_t hw3d_irq_handler(int irq, void *data) +{ + unsigned long flags; + + spin_lock_irqsave(&hw3d_lock, flags); + if (!hw3d_disabled) { + disable_irq(INT_GRAPHICS); + hw3d_disabled = 1; + } + hw3d_pending = 1; + spin_unlock_irqrestore(&hw3d_lock, flags); + + wake_up(&hw3d_queue); + + return IRQ_HANDLED; +} + +static void hw3d_disable_interrupt(void) +{ + unsigned long flags; + spin_lock_irqsave(&hw3d_lock, flags); + if (!hw3d_disabled) { + disable_irq(INT_GRAPHICS); + hw3d_disabled = 1; + } + spin_unlock_irqrestore(&hw3d_lock, flags); +} + +static long hw3d_wait_for_interrupt(void) +{ + unsigned long flags; + int ret; + + for (;;) { + spin_lock_irqsave(&hw3d_lock, flags); + if (hw3d_pending) { + hw3d_pending = 0; + spin_unlock_irqrestore(&hw3d_lock, flags); + return 0; + } + if (hw3d_disabled) { + hw3d_disabled = 0; + enable_irq(INT_GRAPHICS); + } + spin_unlock_irqrestore(&hw3d_lock, flags); + + ret = wait_event_interruptible(hw3d_queue, hw3d_pending); + if (ret < 0) { + hw3d_disable_interrupt(); + return ret; + } + } + + return 0; +} + +#define HW3D_REGS_LEN 0x100000 +static long hw3d_wait_for_revoke(struct hw3d_info *info, struct file *filp) +{ + struct hw3d_data *data = filp->private_data; + int ret; + + if (is_master(info, filp)) { + pr_err("%s: cannot revoke on master node\n", __func__); + return -EPERM; + } + + ret = wait_event_interruptible(info->revoke_wq, + info->revoking || + data->closing); + if (ret == 0 && data->closing) + ret = -EPIPE; + if (ret < 0) + return ret; + return 0; +} + +static void locked_hw3d_client_done(struct hw3d_info *info, int had_timer) +{ + if (info->enabled) { + pr_debug("hw3d: was enabled\n"); + info->enabled = 0; + clk_disable(info->grp_clk); + clk_disable(info->imem_clk); + } + info->revoking = 0; + + /* double check that the irqs are disabled */ + locked_hw3d_irq_disable(info); + + if (had_timer) + wake_unlock(&info->wake_lock); + wake_up(&info->revoke_done_wq); +} + +static void do_force_revoke(struct hw3d_info *info) +{ + unsigned long flags; + + /* at this point, the task had a chance to relinquish the gpu, but + * it hasn't. So, we kill it */ + spin_lock_irqsave(&info->lock, flags); + pr_debug("hw3d: forcing revoke\n"); + locked_hw3d_irq_disable(info); + if (info->client_task) { + pr_info("hw3d: force revoke from pid=%d\n", + info->client_task->pid); + force_sig(SIGKILL, info->client_task); + put_task_struct(info->client_task); + info->client_task = NULL; + } + locked_hw3d_client_done(info, 1); + pr_debug("hw3d: done forcing revoke\n"); + spin_unlock_irqrestore(&info->lock, flags); +} + +#define REVOKE_TIMEOUT (2 * HZ) +static void locked_hw3d_revoke(struct hw3d_info *info) +{ + /* force us to wait to suspend until the revoke is done. If the + * user doesn't release the gpu, the timer will turn off the gpu, + * and force kill the process. */ + wake_lock(&info->wake_lock); + info->revoking = 1; + wake_up(&info->revoke_wq); + mod_timer(&info->revoke_timer, jiffies + REVOKE_TIMEOUT); +} + +bool is_msm_hw3d_file(struct file *file) +{ + struct hw3d_info *info = hw3d_info; + if (MAJOR(file->f_dentry->d_inode->i_rdev) == MAJOR(info->devno) && + (is_master(info, file) || is_client(info, file))) + return 1; + return 0; +} + +void put_msm_hw3d_file(struct file *file) +{ + if (!is_msm_hw3d_file(file)) + return; + fput(file); +} + +static long hw3d_revoke_gpu(struct file *file) +{ + int ret = 0; + unsigned long user_start, user_len; + struct pmem_region region = {.offset = 0x0, .len = HW3D_REGS_LEN}; + + down(&hw3d_sem); + if (!hw3d_granted) + goto end; + /* revoke the pmem region completely */ + if ((ret = pmem_remap(®ion, file, PMEM_UNMAP))) + goto end; + get_pmem_user_addr(file, &user_start, &user_len); + /* reset the gpu */ + clk_disable(grp_clk); + clk_disable(imem_clk); + hw3d_granted = 0; +end: + up(&hw3d_sem); + return ret; +} + +static long hw3d_grant_gpu(struct file *file) +{ + int ret = 0; + struct pmem_region region = {.offset = 0x0, .len = HW3D_REGS_LEN}; + + down(&hw3d_sem); + if (hw3d_granted) { + ret = -1; + goto end; + } + /* map the registers */ + if ((ret = pmem_remap(®ion, file, PMEM_MAP))) + goto end; + clk_enable(grp_clk); + clk_enable(imem_clk); + hw3d_granted = 1; + hw3d_granted_file = file; +end: + up(&hw3d_sem); + return ret; +} + +static int hw3d_release(struct inode *inode, struct file *file) +{ + down(&hw3d_sem); + /* if the gpu is in use, and its inuse by the file that was released */ + if (hw3d_granted && (file == hw3d_granted_file)) { + clk_disable(grp_clk); + clk_disable(imem_clk); + hw3d_granted = 0; + hw3d_granted_file = NULL; + } + up(&hw3d_sem); + return 0; +} + +static void hw3d_vma_open(struct vm_area_struct *vma) +{ + /* XXX: should the master be allowed to fork and keep the mappings? */ + + /* TODO: remap garbage page into here. + * + * For now, just pull the mapping. The user shouldn't be forking + * and using it anyway. */ + zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, NULL); +} + +static void hw3d_vma_close(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct hw3d_data *data = file->private_data; + int i; + + pr_debug("hw3d: current %u ppid %u file %p count %ld\n", + current->pid, current->parent->pid, file, file_count(file)); + + BUG_ON(!data); + + mutex_lock(&data->mutex); + for (i = 0; i < HW3D_NUM_REGIONS; ++i) { + if (data->vmas[i] == vma) { + data->vmas[i] = NULL; + goto done; + } + } + pr_warning("%s: vma %p not of ours during vma_close\n", __func__, vma); +done: + mutex_unlock(&data->mutex); +} + +static int hw3d_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct hw3d_info *info = hw3d_info; + struct hw3d_data *data = file->private_data; + unsigned long vma_size = vma->vm_end - vma->vm_start; + int ret = 0; + int region = REGION_PAGE_ID(vma->vm_pgoff); + + if (region >= HW3D_NUM_REGIONS) { + pr_err("%s: Trying to mmap unknown region %d\n", __func__, + region); + return -EINVAL; + } else if (vma_size > info->regions[region].size) { + pr_err("%s: VMA size %ld exceeds region %d size %ld\n", + __func__, vma_size, region, + info->regions[region].size); + return -EINVAL; + } else if (REGION_PAGE_OFFS(vma->vm_pgoff) != 0 || + (vma_size & ~PAGE_MASK)) { + pr_err("%s: Can't remap part of the region %d\n", __func__, + region); + return -EINVAL; + } else if (!is_master(info, file) && + current->group_leader != info->client_task) { + pr_err("%s: current(%d) != client_task(%d)\n", __func__, + current->group_leader->pid, info->client_task->pid); + return -EPERM; + } else if (!is_master(info, file) && + (info->revoking || info->suspending)) { + pr_err("%s: cannot mmap while revoking(%d) or suspending(%d)\n", + __func__, info->revoking, info->suspending); + return -EPERM; + } + + mutex_lock(&data->mutex); + if (data->vmas[region] != NULL) { + pr_err("%s: Region %d already mapped (pid=%d tid=%d)\n", + __func__, region, current->group_leader->pid, + current->pid); + ret = -EBUSY; + goto done; + } + + /* our mappings are always noncached */ +#ifdef pgprot_noncached + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); +#endif + + ret = io_remap_pfn_range(vma, vma->vm_start, + info->regions[region].pbase >> PAGE_SHIFT, + vma_size, vma->vm_page_prot); + if (ret) { + pr_err("%s: Cannot remap page range for region %d!\n", __func__, + region); + ret = -EAGAIN; + goto done; + } + + /* Prevent a malicious client from stealing another client's data + * by forcing a revoke on it and then mmapping the GPU buffers. + */ + if (region != HW3D_REGS) + memset(info->regions[region].vbase, 0, + info->regions[region].size); + + vma->vm_ops = &hw3d_vm_ops; + + /* mark this region as mapped */ + data->vmas[region] = vma; + +done: + mutex_unlock(&data->mutex); + return ret; +} + +static long hw3d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case HW3D_REVOKE_GPU: + return hw3d_revoke_gpu(file); + break; + case HW3D_GRANT_GPU: + return hw3d_grant_gpu(file); + break; + case HW3D_WAIT_FOR_INTERRUPT: + return hw3d_wait_for_interrupt(); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct android_pmem_platform_data pmem_data = { + .name = "hw3d", + .start = 0xA0000000, + .size = 0x100000, + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, + .cached = 0, +}; + +static int __init hw3d_init(void) +{ + int ret; + + grp_clk = clk_get(NULL, "grp_clk"); + if (IS_ERR(grp_clk)) + return PTR_ERR(grp_clk); + + imem_clk = clk_get(NULL, "imem_clk"); + if (IS_ERR(imem_clk)) { + clk_put(grp_clk); + return PTR_ERR(imem_clk); + } + ret = request_irq(INT_GRAPHICS, hw3d_irq_handler, + IRQF_TRIGGER_HIGH, "hw3d", 0); + if (ret) { + clk_put(grp_clk); + clk_put(imem_clk); + return ret; + } + hw3d_disable_interrupt(); + hw3d_granted = 0; + + return pmem_setup(&pmem_data, hw3d_ioctl, hw3d_release); +} + +device_initcall(hw3d_init); diff --git a/arch/arm/mach-msm/idle-macros.S b/arch/arm/mach-msm/idle-macros.S new file mode 100644 index 00000000000..1622e13d344 --- /dev/null +++ b/arch/arm/mach-msm/idle-macros.S @@ -0,0 +1,153 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/* Add 300 NOPs after 'wfi' for 8x25 target */ +.macro DELAY_8x25, rept +#ifdef CONFIG_ARCH_MSM8625 + .rept \rept + nop + .endr +#endif +.endm + +/* Switch between smp_to_amp/amp_to_smp configuration */ +.macro SET_SMP_COHERENCY, on = 0 + ldr r0, =target_type + ldr r0, [r0] + mov r1, #TARGET_IS_8625 + cmp r0, r1 + bne skip\@ + mrc p15, 0, r0, c1, c0, 1 /* read ACTLR register */ + .if \on + orr r0, r0, #(1 << 6) /* Set the SMP bit in ACTLR */ + .else + bic r0, r0, #(1 << 6) /* Clear the SMP bit */ + .endif + mcr p15, 0, r0, c1, c0, 1 /* write ACTLR register */ + isb +skip\@: +.endm + +/* + * Enable the "L2" cache, not require to restore the controller registers + */ +.macro ENABLE_8x25_L2 + ldr r0, =target_type + ldr r0, [r0] + mov r1, #TARGET_IS_8625 + cmp r0, r1 + bne skip_enable\@ + ldr r0, =apps_power_collapse + ldr r0, [r0] + cmp r0, #POWER_COLLAPSED + bne skip_enable\@ + ldr r0, =l2x0_base_addr + ldr r0, [r0] + mov r1, #0x1 + str r1, [r0, #L2X0_CTRL] + dmb +skip_enable\@: +.endm + +/* + * Perform the required operation + * operation: type of operation on l2 cache (e.g: clean&inv or inv) + * l2_enable: enable or disable + */ +.macro DO_CACHE_OPERATION, operation, l2_enable + ldr r2, =l2x0_base_addr + ldr r2, [r2] + ldr r0, =0xffff + str r0, [r2, #\operation] +wait\@: + ldr r0, [r2, #\operation] + ldr r1, =0xffff + ands r0, r0, r1 + bne wait\@ +l2x_sync\@: + mov r0, #0x0 + str r0, [r2, #L2X0_CACHE_SYNC] +sync\@: + ldr r0, [r2, #L2X0_CACHE_SYNC] + ands r0, r0, #0x1 + bne sync\@ + mov r1, #\l2_enable + str r1, [r2, #L2X0_CTRL] +.endm + +/* + * Clean and invalidate the L2 cache. + * 1. Check the target type + * 2. Check whether we are coming from PC are not + * 3. Save 'aux', 'data latency', & 'prefetch ctlr' registers + * 4. Start L2 clean & invalidation operation + * 5. Disable the L2 cache + */ +.macro SUSPEND_8x25_L2 + ldr r0, =target_type + ldr r0, [r0] + mov r1, #TARGET_IS_8625 + cmp r0, r1 + bne skip_suspend\@ + ldr r0, =apps_power_collapse + ldr r0, [r0] + cmp r0, #POWER_COLLAPSED + bne skip_suspend\@ + ldr r0, =l2x0_saved_ctrl_reg_val + ldr r1, =l2x0_base_addr + ldr r1, [r1] + ldr r2, [r1, #L2X0_AUX_CTRL] + str r2, [r0, #0x0] /* store aux_ctlr reg value */ + ldr r2, [r1, #L2X0_DATA_LATENCY_CTRL] + str r2, [r0, #0x4] /* store data latency reg value */ + ldr r2, [r1, #L2X0_PREFETCH_CTRL] + str r2, [r0, #0x8] /* store prefetch_ctlr reg value */ + DO_CACHE_OPERATION L2X0_CLEAN_INV_WAY OFF + dmb +skip_suspend\@: +.endm + +/* + * Coming back from a successful PC + * 1. Check the target type + * 2. Check whether we are going to PC are not + * 3. Disable the L2 cache + * 4. Restore 'aux', 'data latency', & 'prefetch ctlr' reg + * 5. Invalidate the cache + * 6. Enable the L2 cache + */ +.macro RESUME_8x25_L2 + ldr r0, =target_type + ldr r0, [r0] + mov r1, #TARGET_IS_8625 + cmp r0, r1 + bne skip_resume\@ + ldr r0, =apps_power_collapse + ldr r0, [r0] + cmp r0, #POWER_COLLAPSED + bne skip_resume\@ + ldr r1, =l2x0_base_addr + ldr r1, [r1] + mov r0, #0x0 + str r0, [r1, #L2X0_CTRL] + ldr r0, =l2x0_saved_ctrl_reg_val + ldr r2, [r0, #0x0] + str r2, [r1, #L2X0_AUX_CTRL] /* restore aux_ctlr reg value */ + ldr r2, [r0, #0x4] + str r2, [r1, #L2X0_DATA_LATENCY_CTRL] + ldr r2, [r0, #0x8] + str r2, [r1, #L2X0_PREFETCH_CTRL] + DO_CACHE_OPERATION L2X0_INV_WAY ON +skip_resume\@: +.endm diff --git a/arch/arm/mach-msm/idle-v6.S b/arch/arm/mach-msm/idle-v6.S new file mode 100644 index 00000000000..81608775812 --- /dev/null +++ b/arch/arm/mach-msm/idle-v6.S @@ -0,0 +1,194 @@ +/* + * Idle processing for ARMv6-based Qualcomm SoCs. + * Work around bugs with SWFI. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +.extern write_to_strongly_ordered_memory + +ENTRY(msm_arch_idle) + mrs r2, cpsr /* save the CPSR state */ + cpsid iaf /* explictly disable I,A and F */ + +#if defined(CONFIG_ARCH_MSM7X27) + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 /* flush entire data cache */ + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + stmfd sp!, {r2, lr} /* preserve r2, thus CPSR and LR */ + bl write_to_strongly_ordered_memory /* flush AXI bus buffer */ + ldmfd sp!, {r2, lr} + mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ +#else + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ + bic r0, r1, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 /* invalidate icache and flush */ + /* branch target cache */ + mcr p15, 0, r0, c7, c14, 0 /* clean and invalidate dcache */ + + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ + + mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ +#endif + + msr cpsr_c, r2 /* restore the CPSR state */ + mov pc, lr + +ENTRY(msm_pm_collapse) + ldr r0, =saved_state + stmia r0!, {r4-r14} + + cpsid f + + mrc p15, 0, r1, c1, c0, 0 /* MMU control */ + mrc p15, 0, r2, c2, c0, 0 /* ttb */ + mrc p15, 0, r3, c3, c0, 0 /* dacr */ + mrc p15, 0, ip, c13, c0, 1 /* context ID */ + stmia r0!, {r1-r3, ip} +#if defined(CONFIG_OPROFILE) + mrc p15, 0, r1, c15, c12, 0 /* pmnc */ + mrc p15, 0, r2, c15, c12, 1 /* ccnt */ + mrc p15, 0, r3, c15, c12, 2 /* pmn0 */ + mrc p15, 0, ip, c15, c12, 3 /* pmn1 */ + stmia r0!, {r1-r3, ip} +#endif + mrc p15, 0, r1, c1, c0, 2 /* read CACR */ + stmia r0!, {r1} + + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ + bic r0, r1, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 /* invalidate icache and flush */ + /* branch target cache */ + mcr p15, 0, r0, c7, c14, 0 /* clean and invalidate dcache */ + + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ + + mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + + cpsie f + + ldr r0, =saved_state /* restore registers */ + ldmfd r0, {r4-r14} + mov r0, #0 /* return power collapse failed */ + mov pc, lr + +ENTRY(msm_pm_collapse_exit) +#if 0 /* serial debug */ + mov r0, #0x80000016 + mcr p15, 0, r0, c15, c2, 4 + mov r0, #0xA9000000 + add r0, r0, #0x00A00000 /* UART1 */ + /*add r0, r0, #0x00C00000*/ /* UART3 */ + mov r1, #'A' + str r1, [r0, #0x00C] +#endif + ldr r1, =saved_state_end + ldr r2, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r1, r1, r3 + sub r1, r1, r2 + + ldmdb r1!, {r2} + mcr p15, 0, r2, c1, c0, 2 /* restore CACR */ +#if defined(CONFIG_OPROFILE) + ldmdb r1!, {r2-r5} + mcr p15, 0, r3, c15, c12, 1 /* ccnt */ + mcr p15, 0, r4, c15, c12, 2 /* pmn0 */ + mcr p15, 0, r5, c15, c12, 3 /* pmn1 */ + mcr p15, 0, r2, c15, c12, 0 /* pmnc */ +#endif + ldmdb r1!, {r2-r5} + mcr p15, 0, r4, c3, c0, 0 /* dacr */ + mcr p15, 0, r3, c2, c0, 0 /* ttb */ + mcr p15, 0, r5, c13, c0, 1 /* context ID */ + mov r0, #0 + mcr p15, 0, r0, c7, c5, 4 /* isb */ + ldmdb r1!, {r4-r14} + + /* Add 1:1 map in the PMD to allow smooth switch when turning on MMU */ + and r3, r3, #~0x7F /* mask off lower 7 bits of TTB */ + adr r0, msm_pm_mapped_pa /* get address of the mapped instr */ + lsr r1, r0, #20 /* get the addr range of addr in MB */ + lsl r1, r1, #2 /* multiply by 4 to get to the pg index */ + add r3, r3, r1 /* pgd + pgd_index(addr) */ + ldr r1, [r3] /* save current entry to r1 */ + lsr r0, #20 /* align current addr to 1MB boundary */ + lsl r0, #20 + /* Create new entry for this 1MB page */ + orr r0, r0, #0x400 /* PMD_SECT_AP_WRITE */ + orr r0, r0, #0x2 /* PMD_TYPE_SECT|PMD_DOMAIN(DOMAIN_KERNEL) */ + str r0, [r3] /* put new entry into the MMU table */ + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r2, c1, c0, 0 /* MMU control */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ +msm_pm_mapped_pa: + /* Switch to virtual */ + adr r2, msm_pm_pa_to_va + ldr r0, =msm_pm_pa_to_va + mov pc, r0 +msm_pm_pa_to_va: + sub r0, r0, r2 + /* Restore r1 in MMU table */ + add r3, r3, r0 + str r1, [r3] + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 /* flush entire data cache */ + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + mcr p15, 0, r0, c8, c7, 0 /* invalidate entire unified TLB */ + mcr p15, 0, r0, c7, c5, 6 /* invalidate entire branch target + * cache */ + mcr p15, 0, r0, c7, c7, 0 /* invalidate both data and instruction + * cache */ + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + + mov r0, #1 + mov pc, lr + nop + nop + nop + nop + nop +1: b 1b + + + .data + +saved_state: + .space 4 * 11 /* r4-14 */ + .space 4 * 4 /* cp15 - MMU control, ttb, dacr, context ID */ +#if defined(CONFIG_OPROFILE) + .space 4 * 4 /* more cp15 - pmnc, ccnt, pmn0, pmn1 */ +#endif + .space 4 /* cacr */ +saved_state_end: + diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S new file mode 100644 index 00000000000..b75f76fb5bc --- /dev/null +++ b/arch/arm/mach-msm/idle-v7.S @@ -0,0 +1,309 @@ +/* + * Idle processing for ARMv7-based Qualcomm SoCs. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include "idle.h" +#include "idle-macros.S" + +#ifdef CONFIG_ARCH_MSM_KRAIT +#define SCM_SVC_BOOT 0x1 +#define SCM_CMD_TERMINATE_PC 0x2 +#endif + +ENTRY(msm_arch_idle) + wfi +#ifdef CONFIG_ARCH_MSM8X60 + mrc p14, 1, r1, c1, c5, 4 /* read ETM PDSR to clear sticky bit */ + mrc p14, 0, r1, c1, c5, 4 /* read DBG PRSR to clear sticky bit */ + isb +#endif + bx lr + +ENTRY(msm_pm_collapse) +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsid f +#endif + + ldr r0, =msm_saved_state /* address of msm_saved_state ptr */ + ldr r0, [r0] /* load ptr */ +#if (NR_CPUS >= 2) + mrc p15, 0, r1, c0, c0, 5 /* MPIDR */ + ands r1, r1, #15 /* What CPU am I */ + mov r2, #CPU_SAVED_STATE_SIZE + mul r1, r1, r2 + add r0, r0, r1 +#endif + + stmia r0!, {r4-r14} + mrc p15, 0, r1, c1, c0, 0 /* MMU control */ + mrc p15, 0, r2, c2, c0, 0 /* TTBR0 */ + mrc p15, 0, r3, c3, c0, 0 /* dacr */ +#ifdef CONFIG_ARCH_MSM_SCORPION + /* This instruction is not valid for non scorpion processors */ + mrc p15, 3, r4, c15, c0, 3 /* L2CR1 is the L2 cache control reg 1 */ +#endif + mrc p15, 0, r5, c10, c2, 0 /* PRRR */ + mrc p15, 0, r6, c10, c2, 1 /* NMRR */ + mrc p15, 0, r7, c1, c0, 1 /* ACTLR */ + mrc p15, 0, r8, c2, c0, 1 /* TTBR1 */ + mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */ + mrc p15, 0, ip, c13, c0, 1 /* context ID */ + stmia r0!, {r1-r9, ip} +#ifdef CONFIG_MSM_CPU_AVS + mrc p15, 7, r1, c15, c1, 7 /* AVSCSR is the Adaptive Voltage Scaling + * Control and Status Register */ + mrc p15, 7, r2, c15, c0, 6 /* AVSDSCR is the Adaptive Voltage + * Scaling Delay Synthesizer Control + * Register */ +#ifndef CONFIG_ARCH_MSM_KRAIT + mrc p15, 7, r3, c15, c1, 0 /* TSCSR is the Temperature Status and + * Control Register + */ +#endif + + stmia r0!, {r1-r3} +#endif + +#ifdef CONFIG_MSM_JTAG + bl msm_jtag_save_state +#endif + + ldr r0, =msm_pm_flush_l2_flag + ldr r0, [r0] + mov r1, #0 + mcr p15, 2, r1, c0, c0, 0 /*CCSELR*/ + isb + mrc p15, 1, r1, c0, c0, 0 /*CCSIDR*/ + mov r2, #1 + and r1, r2, r1, ASR #30 /* Check if the cache is write back */ + orr r1, r0, r1 + cmp r1, #1 + bne skip + bl v7_flush_dcache_all +skip: +#ifdef CONFIG_ARCH_MSM_KRAIT + ldr r0, =SCM_SVC_BOOT + ldr r1, =SCM_CMD_TERMINATE_PC + ldr r2, =msm_pm_flush_l2_flag + ldr r2, [r2] + bl scm_call_atomic1 +#else + mrc p15, 0, r4, c1, c0, 0 /* read current CR */ + bic r0, r4, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + isb + + SUSPEND_8x25_L2 + SET_SMP_COHERENCY OFF + wfi + DELAY_8x25 300 + + mcr p15, 0, r4, c1, c0, 0 /* restore d/i cache */ + isb + ENABLE_8x25_L2 /* enable only l2, no need to restore the reg back */ + SET_SMP_COHERENCY ON +#endif + +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsie f +#endif +#ifdef CONFIG_MSM_JTAG + bl msm_jtag_restore_state +#endif + ldr r0, =msm_saved_state /* address of msm_saved_state ptr */ + ldr r0, [r0] /* load ptr */ +#if (NR_CPUS >= 2) + mrc p15, 0, r1, c0, c0, 5 /* MPIDR */ + ands r1, r1, #15 /* What CPU am I */ + mov r2, #CPU_SAVED_STATE_SIZE + mul r2, r2, r1 + add r0, r0, r2 +#endif + ldmfd r0, {r4-r14} /* restore registers */ + mov r0, #0 /* return power collapse failed */ + bx lr + +ENTRY(msm_pm_collapse_exit) +#if 0 /* serial debug */ + mov r0, #0x80000016 + mcr p15, 0, r0, c15, c2, 4 + mov r0, #0xA9000000 + add r0, r0, #0x00A00000 /* UART1 */ + /*add r0, r0, #0x00C00000*/ /* UART3 */ + mov r1, #'A' + str r1, [r0, #0x00C] +#endif + ldr r1, =msm_saved_state_phys + ldr r2, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r1, r1, r3 + sub r1, r1, r2 + ldr r1, [r1] + add r1, r1, #CPU_SAVED_STATE_SIZE +#if (NR_CPUS >= 2) + mrc p15, 0, r2, c0, c0, 5 /* MPIDR */ + ands r2, r2, #15 /* What CPU am I */ + mov r3, #CPU_SAVED_STATE_SIZE + mul r2, r2, r3 + add r1, r1, r2 +#endif + +#ifdef CONFIG_MSM_CPU_AVS + ldmdb r1!, {r2-r4} +#ifndef CONFIG_ARCH_MSM_KRAIT + mcr p15, 7, r4, c15, c1, 0 /* TSCSR */ +#endif + mcr p15, 7, r3, c15, c0, 6 /* AVSDSCR */ + mcr p15, 7, r2, c15, c1, 7 /* AVSCSR */ +#endif + ldmdb r1!, {r2-r11} + mcr p15, 0, r4, c3, c0, 0 /* dacr */ + mcr p15, 0, r3, c2, c0, 0 /* TTBR0 */ +#ifdef CONFIG_ARCH_MSM_SCORPION + /* This instruction is not valid for non scorpion processors */ + mcr p15, 3, r5, c15, c0, 3 /* L2CR1 */ +#endif + mcr p15, 0, r6, c10, c2, 0 /* PRRR */ + mcr p15, 0, r7, c10, c2, 1 /* NMRR */ + mcr p15, 0, r8, c1, c0, 1 /* ACTLR */ + mcr p15, 0, r9, c2, c0, 1 /* TTBR1 */ + mcr p15, 0, r10, c13, c0, 3 /* TPIDRURO */ + mcr p15, 0, r11, c13, c0, 1 /* context ID */ + isb + ldmdb r1!, {r4-r14} + ldr r0, =msm_pm_pc_pgd + ldr r1, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r0, r0, r3 + sub r0, r0, r1 + ldr r0, [r0] + mrc p15, 0, r1, c2, c0, 0 /* save current TTBR0 */ + and r3, r1, #0x7f /* mask to get TTB flags */ + orr r0, r0, r3 /* add TTB flags to switch TTBR value */ + mcr p15, 0, r0, c2, c0, 0 /* temporary switch TTBR0 */ + isb + mcr p15, 0, r2, c1, c0, 0 /* MMU control */ + isb +msm_pm_mapped_pa: + /* Switch to virtual */ + ldr r0, =msm_pm_pa_to_va + mov pc, r0 +msm_pm_pa_to_va: + mcr p15, 0, r1, c2, c0, 0 /* restore TTBR0 */ + isb + mcr p15, 0, r3, c8, c7, 0 /* UTLBIALL */ + mcr p15, 0, r3, c7, c5, 6 /* BPIALL */ + dsb + isb + +#ifdef CONFIG_ARCH_MSM_KRAIT + mrc p15, 0, r1, c0, c0, 0 + ldr r3, =0xff00fc00 + and r3, r1, r3 + ldr r1, =0x51000400 + cmp r3, r1 + mrceq p15, 7, r3, c15, c0, 2 + biceq r3, r3, #0x400 + mcreq p15, 7, r3, c15, c0, 2 +#else + RESUME_8x25_L2 + SET_SMP_COHERENCY ON +#endif + +#ifdef CONFIG_MSM_JTAG + stmfd sp!, {lr} + bl msm_jtag_restore_state + ldmfd sp!, {lr} +#endif + mov r0, #1 + bx lr + nop + nop + nop + nop + nop +1: b 1b + +ENTRY(msm_pm_boot_entry) + mrc p15, 0, r0, c0, c0, 5 /* MPIDR */ + and r0, r0, #15 /* what CPU am I */ + + ldr r1, =msm_pm_boot_vector + ldr r2, =msm_pm_boot_entry + adr r3, msm_pm_boot_entry + add r1, r1, r3 /* translate virt to phys addr */ + sub r1, r1, r2 + + add r1, r1, r0, LSL #2 /* locate boot vector for our cpu */ + ldr pc, [r1] /* jump */ + +ENTRY(msm_pm_set_l2_flush_flag) + ldr r1, =msm_pm_flush_l2_flag + str r0, [r1] + bx lr + + .data + + .globl msm_pm_pc_pgd +msm_pm_pc_pgd: + .long 0x0 + + .globl msm_saved_state +msm_saved_state: + .long 0x0 + + .globl msm_saved_state_phys +msm_saved_state_phys: + .long 0x0 + + .globl msm_pm_boot_vector +msm_pm_boot_vector: + .space 4 * NR_CPUS + + .globl target_type +target_type: + .long 0x0 + + .globl apps_power_collapse +apps_power_collapse: + .long 0x0 + + .globl l2x0_base_addr +l2x0_base_addr: + .long 0x0 + +/* + * Default the l2 flush flag to 1 so that caches are flushed during power + * collapse unless the L2 driver decides to flush them only during L2 + * Power collapse. + */ +msm_pm_flush_l2_flag: + .long 0x1 + +/* + * Save & restore l2x0 registers while system is entering and resuming + * from Power Collapse. + * 1. aux_ctrl_save (0x0) + * 2. data_latency_ctrl (0x4) + * 3. prefetch control (0x8) + */ +l2x0_saved_ctrl_reg_val: + .space 4 * 3 diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h new file mode 100644 index 00000000000..4abdd047c57 --- /dev/null +++ b/arch/arm/mach-msm/idle.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2007-2009,2012 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_IDLE_H_ +#define _ARCH_ARM_MACH_MSM_IDLE_H_ + +#ifdef CONFIG_MSM_CPU_AVS +/* 11 general purpose registers (r4-r14), 10 cp15 registers, 3 AVS registers */ +#define CPU_SAVED_STATE_SIZE (4 * 11 + 4 * 10 + 4 * 3) +#else +/* 11 general purpose registers (r4-r14), 10 cp15 registers */ +#define CPU_SAVED_STATE_SIZE (4 * 11 + 4 * 10) +#endif + +#define ON 1 +#define OFF 0 +#define TARGET_IS_8625 1 +#define POWER_COLLAPSED 1 + +#ifndef __ASSEMBLY__ + +int msm_arch_idle(void); +int msm_pm_collapse(void); +void msm_pm_collapse_exit(void); +extern void *msm_saved_state; +extern unsigned long msm_saved_state_phys; + +#ifdef CONFIG_CPU_V7 +void msm_pm_boot_entry(void); +void msm_pm_set_l2_flush_flag(unsigned int flag); +extern unsigned long msm_pm_pc_pgd; +extern unsigned long msm_pm_boot_vector[NR_CPUS]; +extern uint32_t target_type; +extern uint32_t apps_power_collapse; +extern uint32_t *l2x0_base_addr; +#else +static inline void msm_pm_set_l2_flush_flag(unsigned int flag) +{ + /* empty */ +} +static inline void msm_pm_boot_entry(void) +{ + /* empty */ +} +static inline void msm_pm_write_boot_vector(unsigned int cpu, + unsigned long address) +{ + /* empty */ +} +#endif +#endif +#endif diff --git a/arch/arm/mach-msm/idle_stats.c b/arch/arm/mach-msm/idle_stats.c new file mode 100644 index 00000000000..f4d3a272329 --- /dev/null +++ b/arch/arm/mach-msm/idle_stats.c @@ -0,0 +1,545 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idle_stats.h" +#include + +/****************************************************************************** + * Debug Definitions + *****************************************************************************/ + +enum { + MSM_IDLE_STATS_DEBUG_API = BIT(0), + MSM_IDLE_STATS_DEBUG_SIGNAL = BIT(1), + MSM_IDLE_STATS_DEBUG_MIGRATION = BIT(2), +}; + +static int msm_idle_stats_debug_mask; +module_param_named( + debug_mask, msm_idle_stats_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + +/****************************************************************************** + * Driver Definitions + *****************************************************************************/ + +#define MSM_IDLE_STATS_DRIVER_NAME "msm_idle_stats" + +static dev_t msm_idle_stats_dev_nr; +static struct cdev msm_idle_stats_cdev; +static struct class *msm_idle_stats_class; + +/****************************************************************************** + * Device Definitions + *****************************************************************************/ + +struct msm_idle_stats_device { + unsigned int cpu; + struct mutex mutex; + struct notifier_block notifier; + + int64_t collection_expiration; + struct msm_idle_stats stats; + struct hrtimer timer; + + wait_queue_head_t wait_q; + atomic_t collecting; +}; + +static DEFINE_SPINLOCK(msm_idle_stats_devs_lock); +static DEFINE_PER_CPU(struct msm_idle_stats_device *, msm_idle_stats_devs); + +/****************************************************************************** + * + *****************************************************************************/ + +static inline int64_t msm_idle_stats_bound_interval(int64_t interval) +{ + if (interval <= 0) + return 1; + + if (interval > UINT_MAX) + return UINT_MAX; + + return interval; +} + +static enum hrtimer_restart msm_idle_stats_timer(struct hrtimer *timer) +{ + struct msm_idle_stats_device *stats_dev; + unsigned int cpu; + int64_t now; + int64_t interval; + + stats_dev = container_of(timer, struct msm_idle_stats_device, timer); + cpu = get_cpu(); + + if (cpu != stats_dev->cpu) { + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_MIGRATION) + pr_info("%s: timer migrated from cpu%u to cpu%u\n", + __func__, stats_dev->cpu, cpu); + + stats_dev->stats.event = MSM_IDLE_STATS_EVENT_TIMER_MIGRATED; + goto timer_exit; + } + + now = ktime_to_us(ktime_get()); + interval = now - stats_dev->stats.last_busy_start; + + if (stats_dev->stats.busy_timer > 0 && + interval >= stats_dev->stats.busy_timer - 1) + stats_dev->stats.event = + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED; + else + stats_dev->stats.event = + MSM_IDLE_STATS_EVENT_COLLECTION_TIMER_EXPIRED; + +timer_exit: + atomic_set(&stats_dev->collecting, 0); + wake_up_interruptible(&stats_dev->wait_q); + + put_cpu(); + return HRTIMER_NORESTART; +} + +static void msm_idle_stats_pre_idle(struct msm_idle_stats_device *stats_dev) +{ + int64_t now; + int64_t interval; + + if (smp_processor_id() != stats_dev->cpu) { + WARN_ON(1); + return; + } + + if (!atomic_read(&stats_dev->collecting)) + return; + + hrtimer_cancel(&stats_dev->timer); + + now = ktime_to_us(ktime_get()); + interval = now - stats_dev->stats.last_busy_start; + interval = msm_idle_stats_bound_interval(interval); + + stats_dev->stats.busy_intervals[stats_dev->stats.nr_collected] + = (__u32) interval; + stats_dev->stats.last_idle_start = now; +} + +static void msm_idle_stats_post_idle(struct msm_idle_stats_device *stats_dev) +{ + int64_t now; + int64_t interval; + int64_t timer_interval; + int rc; + + if (smp_processor_id() != stats_dev->cpu) { + WARN_ON(1); + return; + } + + if (!atomic_read(&stats_dev->collecting)) + return; + + now = ktime_to_us(ktime_get()); + interval = now - stats_dev->stats.last_idle_start; + interval = msm_idle_stats_bound_interval(interval); + + stats_dev->stats.idle_intervals[stats_dev->stats.nr_collected] + = (__u32) interval; + stats_dev->stats.nr_collected++; + stats_dev->stats.last_busy_start = now; + + if (stats_dev->stats.nr_collected >= MSM_IDLE_STATS_NR_MAX_INTERVALS) { + stats_dev->stats.event = MSM_IDLE_STATS_EVENT_COLLECTION_FULL; + goto post_idle_collection_done; + } + + timer_interval = stats_dev->collection_expiration - now; + if (timer_interval <= 0) { + stats_dev->stats.event = + MSM_IDLE_STATS_EVENT_COLLECTION_TIMER_EXPIRED; + goto post_idle_collection_done; + } + + if (stats_dev->stats.busy_timer > 0 && + timer_interval > stats_dev->stats.busy_timer) + timer_interval = stats_dev->stats.busy_timer; + + rc = hrtimer_start(&stats_dev->timer, + ktime_set(0, timer_interval * 1000), HRTIMER_MODE_REL_PINNED); + WARN_ON(rc); + + return; + +post_idle_collection_done: + atomic_set(&stats_dev->collecting, 0); + wake_up_interruptible(&stats_dev->wait_q); +} + +static int msm_idle_stats_notified(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct msm_idle_stats_device *stats_dev = container_of( + nb, struct msm_idle_stats_device, notifier); + + if (val == MSM_CPUIDLE_STATE_EXIT) + msm_idle_stats_post_idle(stats_dev); + else + msm_idle_stats_pre_idle(stats_dev); + + return 0; +} + +static int msm_idle_stats_collect(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct msm_idle_stats_device *stats_dev; + struct msm_idle_stats *stats; + int rc; + + stats_dev = (struct msm_idle_stats_device *) filp->private_data; + stats = &stats_dev->stats; + + rc = mutex_lock_interruptible(&stats_dev->mutex); + if (rc) { + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_SIGNAL) + pr_info("%s: interrupted while waiting on device " + "mutex\n", __func__); + + rc = -EINTR; + goto collect_exit; + } + + if (atomic_read(&stats_dev->collecting)) { + pr_err("%s: inconsistent state\n", __func__); + rc = -EBUSY; + goto collect_unlock_exit; + } + + rc = copy_from_user(stats, (void *)arg, sizeof(*stats)); + if (rc) { + rc = -EFAULT; + goto collect_unlock_exit; + } + + if (stats->nr_collected >= MSM_IDLE_STATS_NR_MAX_INTERVALS || + stats->busy_timer > MSM_IDLE_STATS_MAX_TIMER || + stats->collection_timer > MSM_IDLE_STATS_MAX_TIMER) { + rc = -EINVAL; + goto collect_unlock_exit; + } + + if (get_cpu() != stats_dev->cpu) { + put_cpu(); + rc = -EACCES; + goto collect_unlock_exit; + } + + /* + * When collection_timer == 0, stop collecting at the next + * post idle. + */ + stats_dev->collection_expiration = + ktime_to_us(ktime_get()) + stats->collection_timer; + + /* + * Enable collection before starting any timer. + */ + atomic_set(&stats_dev->collecting, 1); + + /* + * When busy_timer == 0, do not set any busy timer. + */ + if (stats->busy_timer > 0) { + rc = hrtimer_start(&stats_dev->timer, + ktime_set(0, stats->busy_timer * 1000), + HRTIMER_MODE_REL_PINNED); + WARN_ON(rc); + } + + put_cpu(); + if (wait_event_interruptible(stats_dev->wait_q, + !atomic_read(&stats_dev->collecting))) { + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_SIGNAL) + pr_info("%s: interrupted while waiting on " + "collection\n", __func__); + + hrtimer_cancel(&stats_dev->timer); + atomic_set(&stats_dev->collecting, 0); + + rc = -EINTR; + goto collect_unlock_exit; + } + + stats->return_timestamp = ktime_to_us(ktime_get()); + + rc = copy_to_user((void *)arg, stats, sizeof(*stats)); + if (rc) { + rc = -EFAULT; + goto collect_unlock_exit; + } + +collect_unlock_exit: + mutex_unlock(&stats_dev->mutex); + +collect_exit: + return rc; +} + +static int msm_idle_stats_open(struct inode *inode, struct file *filp) +{ + struct msm_idle_stats_device *stats_dev; + int rc; + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: enter\n", __func__); + + rc = nonseekable_open(inode, filp); + if (rc) { + pr_err("%s: failed to set nonseekable\n", __func__); + goto open_bail; + } + + stats_dev = (struct msm_idle_stats_device *) + kzalloc(sizeof(*stats_dev), GFP_KERNEL); + if (!stats_dev) { + pr_err("%s: failed to allocate device struct\n", __func__); + rc = -ENOMEM; + goto open_bail; + } + + stats_dev->cpu = MINOR(inode->i_rdev); + mutex_init(&stats_dev->mutex); + stats_dev->notifier.notifier_call = msm_idle_stats_notified; + hrtimer_init(&stats_dev->timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + stats_dev->timer.function = msm_idle_stats_timer; + init_waitqueue_head(&stats_dev->wait_q); + atomic_set(&stats_dev->collecting, 0); + + filp->private_data = stats_dev; + + /* + * Make sure only one device exists per cpu. + */ + spin_lock(&msm_idle_stats_devs_lock); + if (per_cpu(msm_idle_stats_devs, stats_dev->cpu)) { + spin_unlock(&msm_idle_stats_devs_lock); + rc = -EBUSY; + goto open_free_bail; + } + + per_cpu(msm_idle_stats_devs, stats_dev->cpu) = stats_dev; + spin_unlock(&msm_idle_stats_devs_lock); + + rc = msm_cpuidle_register_notifier(stats_dev->cpu, + &stats_dev->notifier); + if (rc) { + pr_err("%s: failed to register idle notification\n", __func__); + goto open_null_bail; + } + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: done\n", __func__); + return 0; + +open_null_bail: + spin_lock(&msm_idle_stats_devs_lock); + per_cpu(msm_idle_stats_devs, stats_dev->cpu) = NULL; + spin_unlock(&msm_idle_stats_devs_lock); + +open_free_bail: + kfree(stats_dev); + +open_bail: + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: exit, %d\n", __func__, rc); + return rc; +} + +static int msm_idle_stats_release(struct inode *inode, struct file *filp) +{ + struct msm_idle_stats_device *stats_dev; + int rc; + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: enter\n", __func__); + + stats_dev = (struct msm_idle_stats_device *) filp->private_data; + rc = msm_cpuidle_unregister_notifier(stats_dev->cpu, + &stats_dev->notifier); + WARN_ON(rc); + + spin_lock(&msm_idle_stats_devs_lock); + per_cpu(msm_idle_stats_devs, stats_dev->cpu) = NULL; + spin_unlock(&msm_idle_stats_devs_lock); + filp->private_data = NULL; + + hrtimer_cancel(&stats_dev->timer); + kfree(stats_dev); + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: done\n", __func__); + return 0; +} + +static long msm_idle_stats_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int rc; + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: enter\n", __func__); + + switch (cmd) { + case MSM_IDLE_STATS_IOC_COLLECT: + rc = msm_idle_stats_collect(filp, cmd, arg); + break; + + default: + rc = -ENOTTY; + break; + } + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: exit, %d\n", __func__, rc); + return rc; +} + +/****************************************************************************** + * + *****************************************************************************/ + +static const struct file_operations msm_idle_stats_fops = { + .owner = THIS_MODULE, + .open = msm_idle_stats_open, + .release = msm_idle_stats_release, + .unlocked_ioctl = msm_idle_stats_ioctl, +}; + +static int __init msm_idle_stats_init(void) +{ + unsigned int nr_cpus = num_possible_cpus(); + struct device *dev; + int rc; + int i; + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: enter\n", __func__); + + rc = alloc_chrdev_region(&msm_idle_stats_dev_nr, + 0, nr_cpus, MSM_IDLE_STATS_DRIVER_NAME); + if (rc) { + pr_err("%s: failed to allocate device number, rc %d\n", + __func__, rc); + goto init_bail; + } + + msm_idle_stats_class = class_create(THIS_MODULE, + MSM_IDLE_STATS_DRIVER_NAME); + if (IS_ERR(msm_idle_stats_class)) { + pr_err("%s: failed to create device class\n", __func__); + rc = -ENOMEM; + goto init_unreg_bail; + } + + for (i = 0; i < nr_cpus; i++) { + dev = device_create(msm_idle_stats_class, NULL, + msm_idle_stats_dev_nr + i, NULL, + MSM_IDLE_STATS_DRIVER_NAME "%d", i); + + if (!dev) { + pr_err("%s: failed to create device %d\n", + __func__, i); + rc = -ENOMEM; + goto init_remove_bail; + } + } + + cdev_init(&msm_idle_stats_cdev, &msm_idle_stats_fops); + msm_idle_stats_cdev.owner = THIS_MODULE; + + /* + * Call cdev_add() last, after everything else is initialized and + * the driver is ready to accept system calls. + */ + rc = cdev_add(&msm_idle_stats_cdev, msm_idle_stats_dev_nr, nr_cpus); + if (rc) { + pr_err("%s: failed to register char device, rc %d\n", + __func__, rc); + goto init_remove_bail; + } + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: done\n", __func__); + return 0; + +init_remove_bail: + for (i = i - 1; i >= 0; i--) + device_destroy( + msm_idle_stats_class, msm_idle_stats_dev_nr + i); + + class_destroy(msm_idle_stats_class); + +init_unreg_bail: + unregister_chrdev_region(msm_idle_stats_dev_nr, nr_cpus); + +init_bail: + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: exit, %d\n", __func__, rc); + return rc; +} + +static void __exit msm_idle_stats_exit(void) +{ + unsigned int nr_cpus = num_possible_cpus(); + int i; + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: enter\n", __func__); + + cdev_del(&msm_idle_stats_cdev); + + for (i = nr_cpus - 1; i >= 0; i--) + device_destroy( + msm_idle_stats_class, msm_idle_stats_dev_nr + i); + + class_destroy(msm_idle_stats_class); + unregister_chrdev_region(msm_idle_stats_dev_nr, nr_cpus); + + if (msm_idle_stats_debug_mask & MSM_IDLE_STATS_DEBUG_API) + pr_info("%s: done\n", __func__); +} + +module_init(msm_idle_stats_init); +module_exit(msm_idle_stats_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("idle stats driver"); +MODULE_VERSION("1.0"); diff --git a/arch/arm/mach-msm/idle_stats.h b/arch/arm/mach-msm/idle_stats.h new file mode 100644 index 00000000000..6c8db1eb306 --- /dev/null +++ b/arch/arm/mach-msm/idle_stats.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_IDLE_STATS_H +#define __ARCH_ARM_MACH_MSM_IDLE_STATS_H + +#include +#include + +enum msm_idle_stats_event { + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED = 1, + MSM_IDLE_STATS_EVENT_COLLECTION_TIMER_EXPIRED = 2, + MSM_IDLE_STATS_EVENT_COLLECTION_FULL = 3, + MSM_IDLE_STATS_EVENT_TIMER_MIGRATED = 4, +}; + +/* + * All time, timer, and time interval values are in units of + * microseconds unless stated otherwise. + */ +#define MSM_IDLE_STATS_NR_MAX_INTERVALS 100 +#define MSM_IDLE_STATS_MAX_TIMER 1000000 + +struct msm_idle_stats { + __u32 busy_timer; + __u32 collection_timer; + + __u32 busy_intervals[MSM_IDLE_STATS_NR_MAX_INTERVALS]; + __u32 idle_intervals[MSM_IDLE_STATS_NR_MAX_INTERVALS]; + __u32 nr_collected; + __s64 last_busy_start; + __s64 last_idle_start; + + enum msm_idle_stats_event event; + __s64 return_timestamp; +}; + +#define MSM_IDLE_STATS_IOC_MAGIC 0xD8 +#define MSM_IDLE_STATS_IOC_COLLECT \ + _IOWR(MSM_IDLE_STATS_IOC_MAGIC, 1, struct msm_idle_stats) + +#endif /* __ARCH_ARM_MACH_MSM_IDLE_STATS_H */ diff --git a/arch/arm/mach-msm/idle_stats_device.c b/arch/arm/mach-msm/idle_stats_device.c new file mode 100644 index 00000000000..01b464ab303 --- /dev/null +++ b/arch/arm/mach-msm/idle_stats_device.c @@ -0,0 +1,378 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_MUTEX(device_list_lock); +LIST_HEAD(device_list); + +static ktime_t us_to_ktime(__u32 us) +{ + return ns_to_ktime((u64)us * NSEC_PER_USEC); +} + +static struct msm_idle_stats_device *_device_from_minor(unsigned int minor) +{ + struct msm_idle_stats_device *device, *ret = NULL; + + + mutex_lock(&device_list_lock); + list_for_each_entry(device, &device_list, list) { + if (minor == device->miscdev.minor) { + ret = device; + break; + } + } + mutex_unlock(&device_list_lock); + return ret; +} + +void msm_idle_stats_update_event(struct msm_idle_stats_device *device, + __u32 event) +{ + __u32 wake_up = !device->stats->event; + + device->stats->event |= event; + if (wake_up) + wake_up_interruptible(&device->wait); +} +EXPORT_SYMBOL(msm_idle_stats_update_event); + +static enum hrtimer_restart msm_idle_stats_busy_timer(struct hrtimer *timer) +{ + struct msm_idle_stats_device *device = + container_of(timer, struct msm_idle_stats_device, busy_timer); + + + /* This is the only case that the event is modified without a device + * lock. However, since the timer is cancelled in the other cases we are + * assured that we have exclusive access to the event at this time. + */ + hrtimer_set_expires(&device->busy_timer, us_to_ktime(0)); + msm_idle_stats_update_event(device, + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED); + return HRTIMER_NORESTART; +} + +static void start_busy_timer(struct msm_idle_stats_device *device, + ktime_t relative_time) +{ + hrtimer_cancel(&device->busy_timer); + hrtimer_set_expires(&device->busy_timer, us_to_ktime(0)); + if (!((device->stats->event & + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED) || + (device->stats->event & MSM_IDLE_STATS_EVENT_COLLECTION_FULL))) { + if (ktime_to_us(relative_time) > 0) { + hrtimer_start(&device->busy_timer, + relative_time, + HRTIMER_MODE_REL); + } + } +} + +static unsigned int msm_idle_stats_device_poll(struct file *file, + poll_table *wait) +{ + struct msm_idle_stats_device *device = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &device->wait, wait); + if (device->stats->event) + mask = POLLIN | POLLRDNORM; + return mask; +} + +static void msm_idle_stats_add_sample(struct msm_idle_stats_device *device, + struct msm_idle_pulse *pulse) +{ + hrtimer_cancel(&device->busy_timer); + hrtimer_set_expires(&device->busy_timer, us_to_ktime(0)); + if (device->stats->nr_collected >= MSM_IDLE_STATS_NR_MAX_INTERVALS) { + pr_warning("idle_stats_device: Overwriting samples\n"); + device->stats->nr_collected = 0; + } + device->stats->pulse_chain[device->stats->nr_collected] = *pulse; + device->stats->nr_collected++; + + if (device->stats->nr_collected == device->max_samples) { + msm_idle_stats_update_event(device, + MSM_IDLE_STATS_EVENT_COLLECTION_FULL); + } else if (device->stats->nr_collected == + ((device->max_samples * 3) / 4)) { + msm_idle_stats_update_event(device, + MSM_IDLE_STATS_EVENT_COLLECTION_NEARLY_FULL); + } +} + +static long ioctl_read_stats(struct msm_idle_stats_device *device, + unsigned long arg) +{ + int remaining; + int requested; + struct msm_idle_pulse pulse; + struct msm_idle_read_stats *stats; + __s64 remaining_time = + ktime_to_us(hrtimer_get_remaining(&device->busy_timer)); + + device->get_sample(device, &pulse); + spin_lock(&device->lock); + hrtimer_cancel(&device->busy_timer); + stats = device->stats; + if (stats == &device->stats_vector[0]) + device->stats = &device->stats_vector[1]; + else + device->stats = &device->stats_vector[0]; + device->stats->event = 0; + device->stats->nr_collected = 0; + spin_unlock(&device->lock); + if (stats->nr_collected >= device->max_samples) { + stats->nr_collected = device->max_samples; + } else { + stats->pulse_chain[stats->nr_collected] = pulse; + stats->nr_collected++; + if (stats->nr_collected == device->max_samples) + stats->event |= MSM_IDLE_STATS_EVENT_COLLECTION_FULL; + else if (stats->nr_collected == + ((device->max_samples * 3) / 4)) + stats->event |= + MSM_IDLE_STATS_EVENT_COLLECTION_NEARLY_FULL; + } + if (remaining_time < 0) { + stats->busy_timer_remaining = 0; + } else { + stats->busy_timer_remaining = remaining_time; + if ((__s64)stats->busy_timer_remaining != remaining_time) + stats->busy_timer_remaining = -1; + } + stats->return_timestamp = ktime_to_us(ktime_get()); + requested = + ((sizeof(*stats) - sizeof(stats->pulse_chain)) + + (sizeof(stats->pulse_chain[0]) * stats->nr_collected)); + remaining = copy_to_user((void __user *)arg, stats, requested); + if (remaining > 0) + return -EFAULT; + + return 0; +} + +static long ioctl_write_stats(struct msm_idle_stats_device *device, + unsigned long arg) +{ + struct msm_idle_write_stats stats; + int remaining; + int ret = 0; + + remaining = copy_from_user(&stats, (void __user *) arg, sizeof(stats)); + if (remaining > 0) { + ret = -EFAULT; + } else { + spin_lock(&device->lock); + device->busy_timer_interval = us_to_ktime(stats.next_busy_timer); + if (ktime_to_us(device->idle_start) == 0) + start_busy_timer(device, us_to_ktime(stats.busy_timer)); + if ((stats.max_samples > 0) && + (stats.max_samples <= MSM_IDLE_STATS_NR_MAX_INTERVALS)) + device->max_samples = stats.max_samples; + spin_unlock(&device->lock); + } + return ret; +} + +void msm_idle_stats_prepare_idle_start(struct msm_idle_stats_device *device) +{ + spin_lock(&device->lock); + hrtimer_cancel(&device->busy_timer); + spin_unlock(&device->lock); +} +EXPORT_SYMBOL(msm_idle_stats_prepare_idle_start); + +void msm_idle_stats_abort_idle_start(struct msm_idle_stats_device *device) +{ + spin_lock(&device->lock); + if (ktime_to_us(hrtimer_get_expires(&device->busy_timer)) > 0) + hrtimer_restart(&device->busy_timer); + spin_unlock(&device->lock); +} +EXPORT_SYMBOL(msm_idle_stats_abort_idle_start); + +void msm_idle_stats_idle_start(struct msm_idle_stats_device *device) +{ + spin_lock(&device->lock); + hrtimer_cancel(&device->busy_timer); + device->idle_start = ktime_get(); + if (ktime_to_us(hrtimer_get_expires(&device->busy_timer)) > 0) { + device->remaining_time = + hrtimer_get_remaining(&device->busy_timer); + if (ktime_to_us(device->remaining_time) <= 0) + device->remaining_time = us_to_ktime(0); + } else { + device->remaining_time = us_to_ktime(0); + } + spin_unlock(&device->lock); +} +EXPORT_SYMBOL(msm_idle_stats_idle_start); + +void msm_idle_stats_idle_end(struct msm_idle_stats_device *device, + struct msm_idle_pulse *pulse) +{ + int tmp; + u32 idle_time = 0; + spin_lock(&device->lock); + if (ktime_to_us(device->idle_start) != 0) { + idle_time = ktime_to_us(ktime_get()) + - ktime_to_us(device->idle_start); + device->idle_start = us_to_ktime(0); + msm_idle_stats_add_sample(device, pulse); + if (device->stats->event & + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED) { + device->stats->event &= + ~MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED; + msm_idle_stats_update_event(device, + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED_RESET); + } else if (ktime_to_us(device->busy_timer_interval) > 0) { + ktime_t busy_timer = device->busy_timer_interval; + /* if it is serialized, it would be full busy, + * checking 80% + */ + if ((pulse->wait_interval*5 >= idle_time*4) && + (ktime_to_us(device->remaining_time) > 0) && + (ktime_to_us(device->remaining_time) < + ktime_to_us(busy_timer))) + busy_timer = device->remaining_time; + start_busy_timer(device, busy_timer); + /* If previous busy interval exceeds the current submit, + * raise a busy timer expired event intentionally. + */ + tmp = device->stats->nr_collected - 1; + if (tmp > 0) { + if ((device->stats->pulse_chain[tmp - 1].busy_start_time + + device->stats->pulse_chain[tmp - 1].busy_interval) > + device->stats->pulse_chain[tmp].busy_start_time) + msm_idle_stats_update_event(device, + MSM_IDLE_STATS_EVENT_BUSY_TIMER_EXPIRED); + } + } + } + spin_unlock(&device->lock); +} +EXPORT_SYMBOL(msm_idle_stats_idle_end); + +static long msm_idle_stats_device_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct msm_idle_stats_device *device = file->private_data; + int ret; + + switch (cmd) { + case MSM_IDLE_STATS_IOC_READ_STATS: + ret = ioctl_read_stats(device, arg); + break; + case MSM_IDLE_STATS_IOC_WRITE_STATS: + ret = ioctl_write_stats(device, arg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int msm_idle_stats_device_release + (struct inode *inode, struct file *filep) +{ + return 0; +} + +static int msm_idle_stats_device_open(struct inode *inode, struct file *filep) +{ + struct msm_idle_stats_device *device; + + + device = _device_from_minor(iminor(inode)); + + if (device == NULL) + return -EPERM; + + filep->private_data = device; + return 0; +} + +static const struct file_operations msm_idle_stats_fops = { + .open = msm_idle_stats_device_open, + .release = msm_idle_stats_device_release, + .unlocked_ioctl = msm_idle_stats_device_ioctl, + .poll = msm_idle_stats_device_poll, +}; + +int msm_idle_stats_register_device(struct msm_idle_stats_device *device) +{ + int ret = -ENOMEM; + + spin_lock_init(&device->lock); + init_waitqueue_head(&device->wait); + hrtimer_init(&device->busy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + device->busy_timer.function = msm_idle_stats_busy_timer; + + device->stats_vector[0].event = 0; + device->stats_vector[0].nr_collected = 0; + device->stats_vector[1].event = 0; + device->stats_vector[1].nr_collected = 0; + device->stats = &device->stats_vector[0]; + device->busy_timer_interval = us_to_ktime(0); + device->max_samples = MSM_IDLE_STATS_NR_MAX_INTERVALS; + + mutex_lock(&device_list_lock); + list_add(&device->list, &device_list); + mutex_unlock(&device_list_lock); + + device->miscdev.minor = MISC_DYNAMIC_MINOR; + device->miscdev.name = device->name; + device->miscdev.fops = &msm_idle_stats_fops; + + ret = misc_register(&device->miscdev); + + if (ret) + goto err_list; + + return ret; + +err_list: + mutex_lock(&device_list_lock); + list_del(&device->list); + mutex_unlock(&device_list_lock); + return ret; +} +EXPORT_SYMBOL(msm_idle_stats_register_device); + +int msm_idle_stats_deregister_device(struct msm_idle_stats_device *device) +{ + if (device == NULL) + return 0; + + mutex_lock(&device_list_lock); + spin_lock(&device->lock); + hrtimer_cancel(&device->busy_timer); + list_del(&device->list); + spin_unlock(&device->lock); + mutex_unlock(&device_list_lock); + + return misc_deregister(&device->miscdev); +} +EXPORT_SYMBOL(msm_idle_stats_deregister_device); diff --git a/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h b/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h new file mode 100644 index 00000000000..1970d0b8d8f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_AUDIO_DMA_H + + +#define BANK_OFFSET 0x1000 + +#define LPAIF_PCM_CTL_OFFSET 0x0000 + #define CTRL_DATA_OE (1 << 18) + #define RATE_8KHZ (0 << 15) + #define RATE_16KHZ (1 << 15) + #define RATE_32KHZ (2 << 15) + #define RATE_64KHZ (4 << 15) + #define RATE_128KHZ (8 << 15) + #define RATE_256KHZ (9 << 15) + #define PCM_LOOPBACK (1 << 14) + #define SYNC_SRC_INT (0 << 13) + #define SYNC_SRC_EXT (1 << 13) + #define PCM_MODE (0 << 12) + #define AUX_MODE (1 << 12) + #define RPCM_WIDTH_8 (0 << 11) + #define RPCM_WIDTH_16 (1 << 11) + #define TPCM_WIDTH_8 (0 << 10) + #define TPCM_WIDTH_16 (1 << 10) + #define RPCM_SLOT(x) (x << 5) + #define TPCM_SLOT(x) x + +#define LPAIF_I2S_CTL_OFFSET(x) (0x0004 + (0x4 * x)) + #define I2S_LOOPBACK (1 << 15) + #define SPK_EN_DISABLE (0 << 14) + #define SPK_EN_ENABLE (1 << 14) + #define SPK_MODE_NONE (0 << 10) + #define SPK_MODE_SD0 (1 << 10) + #define SPK_MODE_SD1 (2 << 10) + #define SPK_MODE_SD2 (3 << 10) + #define SPK_MODE_SD3 (4 << 10) + #define SPK_MODE_QUAD01 (5 << 10) + #define SPK_MODE_QUAD23 (6 << 10) + #define SPK_MODE_6CH (7 << 10) + #define SPK_MODE_8CH (8 << 10) + #define SPK_MONO_STEREO (0 << 9) + #define SPK_MONO_MONO (1 << 9) + #define MIC_EN_DISABLE (0 << 8) + #define MIC_EN_ENABLE (1 << 8) + #define MIC_MODE_NONE (0 << 4) + #define MIC_MODE_SD0 (1 << 4) + #define MIC_MODE_SD1 (2 << 4) + #define MIC_MODE_SD2 (3 << 4) + #define MIC_MODE_SD3 (4 << 4) + #define MIC_MODE_QUAD01 (5 << 4) + #define MIC_MODE_QUAD23 (6 << 4) + #define MIC_MODE_6CH (7 << 4) + #define MIC_MODE_8CH (8 << 4) + #define MIC_MONO_STEREO (0 << 3) + #define MIC_MONO_MONO (1 << 3) + #define WS_SRC_INT (0 << 2) + #define WS_SRC_EXT (1 << 2) + #define BIT_WIDTH_16 (0 << 0) + #define BIT_WIDTH_24 (1 << 0) + #define BIT_WIDTH_32 (2 << 0) + +#define LPAIF_DMIC_CTL 0x0018 + #define DMIC_EN_DISABLE (0 << 4) + #define DMIC_EN_ENABLE (1 << 4) + #define DMIC_MODE_NONE (0 << 1) + #define DMIC_MODE_LEFT0 (1 << 1) + #define DMIC_MODE_RIGHT0 (2 << 1) + #define DMIC_MODE_LEFT1 (3 << 1) + #define DMIC_MODE_RIGHT1 (4 << 1) + #define DMIC_MODE_STEREO0 (5 << 1) + #define DMIC_MODE_STEREO1 (6 << 1) + #define DMIC_MODE_QUAD (7 << 1) + #define BIT_WIDTH_DMIC_16 (0 << 0) + #define BIT_WIDTH_DMIC_20 (1 << 0) + +#define LPAIF_DMIC_VOL_CTL(x) (0x001c + (0x4 * x)) + #define UPDATE_STATUS_COMP (0 << 20) + #define UPDATE_STATUS_PEND (1 << 20) /* Timeout or Zero Crossing */ + #define UPDATE_GAIN_NO (0 << 19) + #define UPDATE_GAIN_YES (1 << 19) + #define TX_HPF_BP_DC_BLOCK (0 << 18) + #define TX_HPF_BP_BYPASS_DC_BLOCK (1 << 18) + #define DMIC_GAIN_BP_GAIN (0 << 17) + #define DMIC_GAIN_BP_BYPASS_GAIN (1 << 17) + #define MUTE_EN_NORMAL (0 << 16) + #define MUTE_EN_MUTE (1 << 16) + #define TIMEOUT_VAL(x) (x << 8) + #define DMIC_GAIN_MUL(x) (x << 0) + +#define LPAIF_SPARE 0x0030 + +#define LPAIF_WRDMA_LPBK_MIX 0x1000 + #define WRDMA_LPBK_MIX_BLOCK(x) (0 << (x - 5)) + #define WRDMA_LPBK_MIX_ALLOW(x) (1 << (x - 5)) + +#define LPAIF_DEBUG_CTL 0x1004 + #define TESTMODE_OFF (0 << 4) + #define TESTMODE_ON (1 << 4) + #define TESTSEL_CH0 (0 << 0) + #define TESTSEL_CH1 (1 << 0) + #define TESTSEL_CH2 (2 << 0) + #define TESTSEL_CH3 (3 << 0) + #define TESTSEL_CH4 (4 << 0) + #define TESTSEL_CH5 (5 << 0) + #define TESTSEL_CH6 (6 << 0) + #define TESTSEL_CH7 (7 << 0) + #define TESTSEL_CH8 (8 << 0) + #define TESTSEL_MIXER (9 << 0) + #define TESTSEL_CODEC_SPKR (10 << 0) + #define TESTSEL_CODEC_MIC (11 << 0) + #define TESTSEL_MI2S (12 << 0) + #define TESTSEL_SEC_SPKR (13 << 0) + #define TESTSEL_SEC_MIC (14 << 0) + #define TESTSEL_DMIC (15 << 0) + +#define LPAIF_MIXER_CTL 0x2000 + #define OVR_DETECTED_NO (0 << 10) + #define OVR_DETECTED_YES (1 << 10) + #define OVR_CLR_NO (0 << 9) + #define OVR_CLR_YES (1 << 9) + #define SAT_EN_DISABLE (0 << 8) + #define SAT_EN_ENABLE (1 << 8) + #define MIXER_BIT_WIDTH_8 (0 << 6) + #define MIXER_BIT_WIDTH_16 (1 << 6) + #define MIXER_BIT_WIDTH_24 (2 << 6) + #define MIXER_BIT_WIDTH_32 (3 << 6) + #define PORT1_CH_NONE (0 << 3) + #define PORT1_CH_0 (1 << 3) + #define PORT1_CH_1 (2 << 3) + #define PORT1_CH_2 (3 << 3) + #define PORT1_CH_3 (4 << 3) + #define PORT1_CH_4 (5 << 3) + #define PORT0_CH_NONE (0 << 0) + #define PORT0_CH_0 (1 << 0) + #define PORT0_CH_1 (2 << 0) + #define PORT0_CH_2 (3 << 0) + #define PORT0_CH_3 (4 << 0) + #define PORT0_CH_4 (5 << 0) + +#define DMA_IRQ_BASE 0x3000 +#define DMA_IRQ_INDEX(x) (BANK_OFFSET * x) +#define DMA_IRQ_ADDR(irq, addr) (DMA_IRQ_BASE \ + + DMA_IRQ_INDEX(irq) + addr) + +/* Audio Interrupt registers for DMA channel confuguration */ +#define LPAIF_IRQ_EN(x) DMA_IRQ_ADDR(x, 0x00) +#define LPAIF_IRQ_STAT(x) DMA_IRQ_ADDR(x, 0x04) +#define LPAIF_IRQ_RAW_STAT(x) DMA_IRQ_ADDR(x, 0x08) +#define LPAIF_IRQ_CLEAR(x) DMA_IRQ_ADDR(x, 0x0c) +#define LPAIF_IRQ_FORCE(x) DMA_IRQ_ADDR(x, 0x10) + #define PER_CH(x) (1 << (3 * x)) + #define UNDER_CH(x) (2 << (3 * x)) + #define ERR_CH(x) (4 << (3 * x)) + +/* Audio DMA registers for DMA channel confuguration */ +#define DMA_CH_CTL_BASE 0x6000 +#define DMA_CH_INDEX(ch) (BANK_OFFSET * ch) + +#define DMA_CTRL_ADDR(ch, addr) (DMA_CH_CTL_BASE \ + + (DMA_CH_INDEX(ch) + addr)) + +#define LPAIF_DMA_CTL(x) DMA_CTRL_ADDR(x, 0x00) + #define BURST_EN (1 << 11) + #define WPSCNT_ONE (0 << 8) + #define WPSCNT_TWO (1 << 8) + #define WPSCNT_THREE (2 << 8) + #define WPSCNT_FOUR (3 << 8) + #define WPSCNT_SIX (5 << 8) + #define WPSCNT_EIGHT (7 << 8) + #define AUDIO_INTF_NONE (0 << 4) + #define AUDIO_INTF_CODEC (1 << 4) + #define AUDIO_INTF_PCM (2 << 4) + #define AUDIO_INTF_SEC_I2S (3 << 4) + #define AUDIO_INTF_MI2S (4 << 4) + #define AUDIO_INTF_HDMI (5 << 4) + #define AUDIO_INTF_MIXOUT (6 << 4) + #define AUDIO_INTF_LOOPBACK1 (7 << 4) + #define AUDIO_INTF_LOOPBACK2 (8 << 4) + #define FIFO_WATERMRK(x) ((x & 0x7) << 1) + #define ENABLE (1 << 0) + +#define LPAIF_DMA_BASE(x) DMA_CTRL_ADDR(x, 0x04) + #define BASE_ADDR (0xFFFFFFFF << 4) + +#define LPAIF_DMA_BUFF_LEN(x) DMA_CTRL_ADDR(x, 0x08) +#define LPAIF_DMA_CURR_ADDR(x) DMA_CTRL_ADDR(x, 0x0c) +#define LPAIF_DMA_PER_LEN(x) DMA_CTRL_ADDR(x, 0x10) +#define LPAIF_DMA_PER_CNT(x) DMA_CTRL_ADDR(x, 0x14) +#define LPAIF_DMA_FRM(x) DMA_CTRL_ADDR(x, 0x18) +#define LPAIF_DMA_FRMCLR(x) DMA_CTRL_ADDR(x, 0x1c) +#define LPAIF_DMA_SET_BUFF_CNT(x) DMA_CTRL_ADDR(x, 0x20) +#define LPAIF_DMA_SET_PER_CNT(x) DMA_CTRL_ADDR(x, 0x24) + +#define LPAIF_DMA_PER_CNT_PER_CNT_MASK 0x000FFFFF +#define LPAIF_DMA_PER_CNT_PER_CNT_SHIFT 0 +#define LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK 0x00F00000 +#define LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT 20 + +/* channel assignments */ + +#define DMA_CH_0 0 +#define DMA_CH_1 1 +#define DMA_CH_2 2 +#define DMA_CH_3 3 +#define DMA_CH_4 4 +#define DMA_CH_5 5 +#define DMA_CH_6 6 +#define DMA_CH_7 7 + +#endif diff --git a/arch/arm/mach-msm/include/mach/bam_dmux.h b/arch/arm/mach-msm/include/mach/bam_dmux.h new file mode 100644 index 00000000000..f02a882d991 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/bam_dmux.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#ifndef _BAM_DMUX_H +#define _BAM_DMUX_H + +#define BAM_DMUX_CH_NAME_MAX_LEN 20 + +enum { + BAM_DMUX_DATA_RMNET_0, + BAM_DMUX_DATA_RMNET_1, + BAM_DMUX_DATA_RMNET_2, + BAM_DMUX_DATA_RMNET_3, + BAM_DMUX_DATA_RMNET_4, + BAM_DMUX_DATA_RMNET_5, + BAM_DMUX_DATA_RMNET_6, + BAM_DMUX_DATA_RMNET_7, + BAM_DMUX_USB_RMNET_0, + BAM_DMUX_NUM_CHANNELS +}; + +/* event type enum */ +enum { + BAM_DMUX_RECEIVE, /* data is struct sk_buff */ + BAM_DMUX_WRITE_DONE, /* data is struct sk_buff */ + BAM_DMUX_UL_CONNECTED, /* data is null */ + BAM_DMUX_UL_DISCONNECTED, /*data is null */ +}; + +/* + * Open a bam_dmux logical channel + * id - the logical channel to open + * priv - private data pointer to be passed to the notify callback + * notify - event callback function + * priv - private data pointer passed to msm_bam_dmux_open() + * event_type - type of event + * data - data relevant to event. May not be valid. See event_type + * enum for valid cases. + */ +#ifdef CONFIG_MSM_BAM_DMUX +int msm_bam_dmux_open(uint32_t id, void *priv, + void (*notify)(void *priv, int event_type, + unsigned long data)); + +int msm_bam_dmux_close(uint32_t id); + +int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb); + +int msm_bam_dmux_kickoff_ul_wakeup(void); + +int msm_bam_dmux_ul_power_vote(void); + +int msm_bam_dmux_ul_power_unvote(void); + +int msm_bam_dmux_is_ch_full(uint32_t id); + +int msm_bam_dmux_is_ch_low(uint32_t id); + +int msm_bam_dmux_reg_notify(void *priv, + void (*notify)(void *priv, int event_type, + unsigned long data)); +#else +static inline int msm_bam_dmux_open(uint32_t id, void *priv, + void (*notify)(void *priv, int event_type, + unsigned long data)) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_close(uint32_t id) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_write(uint32_t id, struct sk_buff *skb) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_kickoff_ul_wakeup(void) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_ul_power_vote(void) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_ul_power_unvote(void) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_is_ch_full(uint32_t id) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_is_ch_low(uint32_t id) +{ + return -ENODEV; +} + +static inline int msm_bam_dmux_reg_notify(void *priv, + void (*notify)(void *priv, int event_type, + unsigned long data)) +{ + return -ENODEV; +} +#endif +#endif /* _BAM_DMUX_H */ diff --git a/arch/arm/mach-msm/include/mach/barriers.h b/arch/arm/mach-msm/include/mach/barriers.h new file mode 100644 index 00000000000..2d4792c5f75 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/barriers.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include + +#define mb() do \ + { \ + dsb();\ + outer_sync(); \ + write_to_strongly_ordered_memory(); \ + } while (0) +#define rmb() do { dmb(); write_to_strongly_ordered_memory(); } while (0) +#define wmb() mb() diff --git a/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h b/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h new file mode 100644 index 00000000000..c2242971880 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_BCM_BT_LPM_H +#define __ASM_ARCH_BCM_BT_LPM_H + +#include + +/* Uart driver must call this every time it beings TX, to ensure + * this driver keeps WAKE asserted during TX. Called with uart + * spinlock held. */ +extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport); + +struct bcm_bt_lpm_platform_data { + unsigned int gpio_wake; /* CPU -> BCM wakeup gpio */ + unsigned int gpio_host_wake; /* BCM -> CPU wakeup gpio */ + + /* Callback to request the uart driver to clock off. + * Called with uart spinlock held. */ + void (*request_clock_off_locked)(struct uart_port *uport); + /* Callback to request the uart driver to clock on. + * Called with uart spinlock held. */ + void (*request_clock_on_locked)(struct uart_port *uport); +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/board-msm8660.h b/arch/arm/mach-msm/include/mach/board-msm8660.h new file mode 100644 index 00000000000..22e378cc568 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/board-msm8660.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_BOARD_MSM8660_H +#define __ARCH_ARM_MACH_MSM_BOARD_MSM8660_H + +#include +#include +#include + +/* Macros assume PMIC GPIOs start at 0 */ +#define PM8058_GPIO_BASE NR_MSM_GPIOS +#define PM8058_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + PM8058_GPIO_BASE) +#define PM8058_GPIO_SYS_TO_PM(sys_gpio) (sys_gpio - PM8058_GPIO_BASE) +#define PM8058_MPP_BASE (PM8058_GPIO_BASE + PM8058_GPIOS) +#define PM8058_MPP_PM_TO_SYS(pm_gpio) (pm_gpio + PM8058_MPP_BASE) +#define PM8058_MPP_SYS_TO_PM(sys_gpio) (sys_gpio - PM8058_MPP_BASE) +#define PM8058_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) + +#define PM8901_MPP_BASE (PM8058_GPIO_BASE + \ + PM8058_GPIOS + PM8058_MPPS) +#define PM8901_MPP_PM_TO_SYS(pm_gpio) (pm_gpio + PM8901_MPP_BASE) +#define PM8901_MPP_SYS_TO_PM(sys_gpio) (sys_gpio - PM901_MPP_BASE) +#define PM8901_IRQ_BASE (PM8058_IRQ_BASE + \ + NR_PMIC8058_IRQS) + +#ifdef CONFIG_MSM_CAMERA_V4L2 +extern struct msm_camera_board_info msm8x60_camera_board_info; +void msm8x60_init_cam(void); +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 2ce8f1f2fc4..bae5cbc7163 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/board.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -18,33 +19,577 @@ #define __ASM_ARCH_MSM_BOARD_H #include -#include +#include +#include +#include +#include +#include +#include +#include -/* platform device data structures */ - -struct msm_acpu_clock_platform_data -{ - uint32_t acpu_switch_time_us; - uint32_t max_speed_delta_khz; - uint32_t vdd_switch_time_us; - unsigned long power_collapse_khz; - unsigned long wait_for_irq_khz; +struct msm_camera_io_ext { + uint32_t mdcphy; + uint32_t mdcsz; + uint32_t appphy; + uint32_t appsz; + uint32_t camifpadphy; + uint32_t camifpadsz; + uint32_t csiphy; + uint32_t csisz; + uint32_t csiirq; + uint32_t csiphyphy; + uint32_t csiphysz; + uint32_t csiphyirq; + uint32_t ispifphy; + uint32_t ispifsz; + uint32_t ispifirq; }; +struct msm_camera_io_clk { + uint32_t mclk_clk_rate; + uint32_t vfe_clk_rate; +}; + +struct msm_cam_expander_info { + struct i2c_board_info const *board_info; + int bus_id; +}; + +struct msm_camera_device_platform_data { + int (*camera_gpio_on) (void); + void (*camera_gpio_off)(void); + struct msm_camera_io_ext ioext; + struct msm_camera_io_clk ioclk; + uint8_t csid_core; + uint8_t is_csiphy; + uint8_t is_csic; + uint8_t is_csid; + uint8_t is_ispif; + uint8_t is_vpe; + struct msm_bus_scale_pdata *cam_bus_scale_table; +}; +enum msm_camera_csi_data_format { + CSI_8BIT, + CSI_10BIT, + CSI_12BIT, +}; +struct msm_camera_csi_params { + enum msm_camera_csi_data_format data_format; + uint8_t lane_cnt; + uint8_t lane_assign; + uint8_t settle_cnt; + uint8_t dpcm_scheme; +}; + +#ifdef CONFIG_SENSORS_MT9T013 +struct msm_camera_legacy_device_platform_data { + int sensor_reset; + int sensor_pwd; + int vcm_pwd; + void (*config_gpio_on) (void); + void (*config_gpio_off)(void); +}; +#endif + +#define MSM_CAMERA_FLASH_NONE 0 +#define MSM_CAMERA_FLASH_LED 1 + +#define MSM_CAMERA_FLASH_SRC_PMIC (0x00000001<<0) +#define MSM_CAMERA_FLASH_SRC_PWM (0x00000001<<1) +#define MSM_CAMERA_FLASH_SRC_CURRENT_DRIVER (0x00000001<<2) +#define MSM_CAMERA_FLASH_SRC_EXT (0x00000001<<3) +#define MSM_CAMERA_FLASH_SRC_LED (0x00000001<<3) +#define MSM_CAMERA_FLASH_SRC_LED1 (0x00000001<<4) + +struct msm_camera_sensor_flash_pmic { + uint8_t num_of_src; + uint32_t low_current; + uint32_t high_current; + enum pmic8058_leds led_src_1; + enum pmic8058_leds led_src_2; + int (*pmic_set_current)(enum pmic8058_leds id, unsigned mA); +}; + +struct msm_camera_sensor_flash_pwm { + uint32_t freq; + uint32_t max_load; + uint32_t low_load; + uint32_t high_load; + uint32_t channel; +}; + +struct pmic8058_leds_platform_data; +struct msm_camera_sensor_flash_current_driver { + uint32_t low_current; + uint32_t high_current; + const struct pmic8058_leds_platform_data *driver_channel; +}; + +enum msm_camera_ext_led_flash_id { + MAM_CAMERA_EXT_LED_FLASH_SC628A, + MAM_CAMERA_EXT_LED_FLASH_TPS61310, +}; + +struct msm_camera_sensor_flash_external { + uint32_t led_en; + uint32_t led_flash_en; + enum msm_camera_ext_led_flash_id flash_id; + struct msm_cam_expander_info *expander_info; +}; + +struct msm_camera_sensor_flash_led { + const char *led_name; + const int led_name_len; +}; + +struct msm_camera_sensor_flash_src { + int flash_sr_type; + + union { + struct msm_camera_sensor_flash_pmic pmic_src; + struct msm_camera_sensor_flash_pwm pwm_src; + struct msm_camera_sensor_flash_current_driver + current_driver_src; + struct msm_camera_sensor_flash_external + ext_driver_src; + struct msm_camera_sensor_flash_led led_src; + } _fsrc; +}; + +struct msm_camera_sensor_flash_data { + int flash_type; + struct msm_camera_sensor_flash_src *flash_src; +}; + +struct msm_camera_sensor_strobe_flash_data { + uint8_t flash_trigger; + uint8_t flash_charge; /* pin for charge */ + uint8_t flash_charge_done; + uint32_t flash_recharge_duration; + uint32_t irq; + spinlock_t spin_lock; + spinlock_t timer_lock; + int state; +}; + +enum msm_camera_type { + BACK_CAMERA_2D, + FRONT_CAMERA_2D, + BACK_CAMERA_3D, + BACK_CAMERA_INT_3D, +}; + +enum msm_sensor_type { + BAYER_SENSOR, + YUV_SENSOR, +}; + +enum camera_vreg_type { + REG_LDO, + REG_VS, +}; + +struct camera_vreg_t { + char *reg_name; + enum camera_vreg_type type; + int min_voltage; + int max_voltage; + int op_mode; +}; + +struct msm_gpio_set_tbl { + unsigned gpio; + unsigned long flags; + uint32_t delay; +}; + +struct msm_camera_csi_lane_params { + uint8_t csi_lane_assign; + uint8_t csi_lane_mask; +}; + +struct msm_camera_gpio_conf { + void *cam_gpiomux_conf_tbl; + uint8_t cam_gpiomux_conf_tbl_size; + struct gpio *cam_gpio_common_tbl; + uint8_t cam_gpio_common_tbl_size; + struct gpio *cam_gpio_req_tbl; + uint8_t cam_gpio_req_tbl_size; + struct msm_gpio_set_tbl *cam_gpio_set_tbl; + uint8_t cam_gpio_set_tbl_size; + uint32_t gpio_no_mux; + uint32_t *camera_off_table; + uint8_t camera_off_table_size; + uint32_t *camera_on_table; + uint8_t camera_on_table_size; +}; + +enum msm_camera_i2c_mux_mode { + MODE_R, + MODE_L, + MODE_DUAL +}; + +struct msm_camera_i2c_conf { + uint8_t use_i2c_mux; + struct platform_device *mux_dev; + enum msm_camera_i2c_mux_mode i2c_mux_mode; +}; + +struct msm_camera_sensor_platform_info { + int mount_angle; + int sensor_reset; + struct camera_vreg_t *cam_vreg; + int num_vreg; + int32_t (*ext_power_ctrl) (int enable); + struct msm_camera_gpio_conf *gpio_conf; + struct msm_camera_i2c_conf *i2c_conf; + struct msm_camera_csi_lane_params *csi_lane_params; +}; + +enum msm_camera_actuator_name { + MSM_ACTUATOR_MAIN_CAM_0, + MSM_ACTUATOR_MAIN_CAM_1, + MSM_ACTUATOR_MAIN_CAM_2, + MSM_ACTUATOR_MAIN_CAM_3, + MSM_ACTUATOR_MAIN_CAM_4, + MSM_ACTUATOR_MAIN_CAM_5, + MSM_ACTUATOR_WEB_CAM_0, + MSM_ACTUATOR_WEB_CAM_1, + MSM_ACTUATOR_WEB_CAM_2, +}; + +struct msm_actuator_info { + struct i2c_board_info const *board_info; + enum msm_camera_actuator_name cam_name; + int bus_id; + int vcm_pwd; + int vcm_enable; +}; + +struct msm_eeprom_info { + struct i2c_board_info const *board_info; + int bus_id; +}; + +struct msm_camera_sensor_info { + const char *sensor_name; + int sensor_reset_enable; + int sensor_reset; + int sensor_pwd; + int vcm_pwd; + int vcm_enable; + int mclk; + int flash_type; + struct msm_camera_sensor_platform_info *sensor_platform_info; + struct msm_camera_device_platform_data *pdata; + struct resource *resource; + uint8_t num_resources; + struct msm_camera_sensor_flash_data *flash_data; + int csi_if; + struct msm_camera_csi_params csi_params; + struct msm_camera_sensor_strobe_flash_data *strobe_flash_data; + char *eeprom_data; + enum msm_camera_type camera_type; + enum msm_sensor_type sensor_type; + struct msm_actuator_info *actuator_info; + int pmic_gpio_enable; + int (*sensor_lcd_gpio_onoff)(int on); + struct msm_eeprom_info *eeprom_info; +}; + +struct msm_camera_board_info { + struct i2c_board_info *board_info; + uint8_t num_i2c_board_info; +}; + +int msm_get_cam_resources(struct msm_camera_sensor_info *); + struct clk_lookup; -extern struct sys_timer msm_timer; +struct snd_endpoint { + int id; + const char *name; +}; +struct msm_snd_endpoints { + struct snd_endpoint *endpoints; + unsigned num; +}; + +#define MSM_MAX_DEC_CNT 14 +/* 7k target ADSP information */ +/* Bit 23:0, for codec identification like mp3, wav etc * + * Bit 27:24, for mode identification like tunnel, non tunnel* + * bit 31:28, for operation support like DM, DMA */ +enum msm_adspdec_concurrency { + MSM_ADSP_CODEC_WAV = 0, + MSM_ADSP_CODEC_ADPCM = 1, + MSM_ADSP_CODEC_MP3 = 2, + MSM_ADSP_CODEC_REALAUDIO = 3, + MSM_ADSP_CODEC_WMA = 4, + MSM_ADSP_CODEC_AAC = 5, + MSM_ADSP_CODEC_RESERVED = 6, + MSM_ADSP_CODEC_MIDI = 7, + MSM_ADSP_CODEC_YADPCM = 8, + MSM_ADSP_CODEC_QCELP = 9, + MSM_ADSP_CODEC_AMRNB = 10, + MSM_ADSP_CODEC_AMRWB = 11, + MSM_ADSP_CODEC_EVRC = 12, + MSM_ADSP_CODEC_WMAPRO = 13, + MSM_ADSP_MODE_TUNNEL = 24, + MSM_ADSP_MODE_NONTUNNEL = 25, + MSM_ADSP_MODE_LP = 26, + MSM_ADSP_OP_DMA = 28, + MSM_ADSP_OP_DM = 29, +}; + +struct msm_adspdec_info { + const char *module_name; + unsigned module_queueid; + int module_decid; /* objid */ + unsigned nr_codec_support; +}; + +/* Carries information about number codec + * supported if same codec or different codecs + */ +struct dec_instance_table { + uint8_t max_instances_same_dec; + uint8_t max_instances_diff_dec; +}; + +struct msm_adspdec_database { + unsigned num_dec; + unsigned num_concurrency_support; + unsigned int *dec_concurrency_table; /* Bit masked entry to * + * represents codec, mode etc */ + struct msm_adspdec_info *dec_info_list; + struct dec_instance_table *dec_instance_list; +}; + +enum msm_mdp_hw_revision { + MDP_REV_20 = 1, + MDP_REV_22, + MDP_REV_30, + MDP_REV_303, + MDP_REV_31, + MDP_REV_40, + MDP_REV_41, + MDP_REV_42, + MDP_REV_43, + MDP_REV_44, +}; + +struct msm_panel_common_pdata { + uintptr_t hw_revision_addr; + int gpio; + bool bl_lock; + spinlock_t bl_spinlock; + int (*backlight_level)(int level, int max, int min); + int (*pmic_backlight)(int level); + int (*rotate_panel)(void); + int (*panel_num)(void); + void (*panel_config_gpio)(int); + int (*vga_switch)(int select_vga); + int *gpio_num; + int mdp_core_clk_rate; + unsigned num_mdp_clk; + int *mdp_core_clk_table; +#ifdef CONFIG_MSM_BUS_SCALING + struct msm_bus_scale_pdata *mdp_bus_scale_table; +#endif + int mdp_rev; + u32 ov0_wb_size; /* overlay0 writeback size */ + u32 ov1_wb_size; /* overlay1 writeback size */ + u32 mem_hid; + char cont_splash_enabled; +}; + + + +struct lcdc_platform_data { + int (*lcdc_gpio_config)(int on); + int (*lcdc_power_save)(int); + unsigned int (*lcdc_get_clk)(void); +#ifdef CONFIG_MSM_BUS_SCALING + struct msm_bus_scale_pdata *bus_scale_table; +#endif + int (*lvds_pixel_remap)(void); +}; + +struct tvenc_platform_data { + int poll; + int (*pm_vid_en)(int on); +#ifdef CONFIG_MSM_BUS_SCALING + struct msm_bus_scale_pdata *bus_scale_table; +#endif +}; + +struct mddi_platform_data { + int (*mddi_power_save)(int on); + int (*mddi_sel_clk)(u32 *clk_rate); + int (*mddi_client_power)(u32 client_id); +}; + +struct mipi_dsi_platform_data { + int vsync_gpio; + int (*dsi_power_save)(int on); + int (*dsi_client_reset)(void); + int (*get_lane_config)(void); + char (*splash_is_enabled)(void); + int target_type; +}; + +enum mipi_dsi_3d_ctrl { + FPGA_EBI2_INTF, + FPGA_SPI_INTF, +}; + +/* DSI PHY configuration */ +struct mipi_dsi_phy_ctrl { + uint32_t regulator[5]; + uint32_t timing[12]; + uint32_t ctrl[4]; + uint32_t strength[4]; + uint32_t pll[21]; +}; + +struct mipi_dsi_panel_platform_data { + int fpga_ctrl_mode; + int fpga_3d_config_addr; + int *gpio; + struct mipi_dsi_phy_ctrl *phy_ctrl_settings; + char dlane_swap; + void (*dsi_pwm_cfg)(void); + char enable_wled_bl_ctrl; +}; + +struct lvds_panel_platform_data { + int *gpio; +}; + +#define PANEL_NAME_MAX_LEN 50 +struct msm_fb_platform_data { + int (*detect_client)(const char *name); + int mddi_prescan; + int (*allow_set_offset)(void); + char prim_panel_name[PANEL_NAME_MAX_LEN]; + char ext_panel_name[PANEL_NAME_MAX_LEN]; +}; + +struct msm_hdmi_platform_data { + int irq; + int (*cable_detect)(int insert); + int (*comm_power)(int on, int show); + int (*enable_5v)(int on); + int (*core_power)(int on, int show); + int (*cec_power)(int on); + int (*init_irq)(void); + bool (*check_hdcp_hw_support)(void); +}; + +struct msm_mhl_platform_data { + int irq; + int (*gpio_setup)(int on); + void (*reset_pin)(int on); +}; + +struct msm_i2c_platform_data { + int clk_freq; + uint32_t rmutex; + const char *rsl_id; + uint32_t pm_lat; + int pri_clk; + int pri_dat; + int aux_clk; + int aux_dat; + int src_clk_rate; + int use_gsbi_shared_mode; + void (*msm_i2c_config_gpio)(int iface, int config_type); +}; + +struct msm_i2c_ssbi_platform_data { + const char *rsl_id; + enum msm_ssbi_controller_type controller_type; +}; + +struct msm_vidc_platform_data { + int memtype; + u32 enable_ion; + int disable_dmx; + int disable_fullhd; + u32 cp_enabled; +#ifdef CONFIG_MSM_BUS_SCALING + struct msm_bus_scale_pdata *vidc_bus_client_pdata; +#endif + int cont_mode_dpb_count; + int disable_turbo; +}; + +struct vcap_platform_data { + unsigned *gpios; + int num_gpios; + struct msm_bus_scale_pdata *bus_client_pdata; +}; + +#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE) +struct isp1763_platform_data { + unsigned reset_gpio; + int (*setup_gpio)(int enable); +}; +#endif /* common init routines for use by arch/arm/mach-msm/board-*.c */ -void __init msm_add_devices(void); -void __init msm_map_common_io(void); -void __init msm_init_irq(void); -void __init msm_init_gpio(void); -void __init msm_clock_init(struct clk_lookup *clock_tbl, unsigned num_clocks); -void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *); -int __init msm_add_sdcc(unsigned int controller, - struct msm_mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags); +#ifdef CONFIG_OF_DEVICE +void msm_copper_init(struct of_dev_auxdata **); +#endif +void msm_add_devices(void); +void msm_copper_add_devices(void); +void msm_copper_add_drivers(void); +void msm_map_common_io(void); +void msm_map_qsd8x50_io(void); +void msm_map_msm8x60_io(void); +void msm_map_msm8960_io(void); +void msm_map_msm8930_io(void); +void msm_map_apq8064_io(void); +void msm_map_msm7x30_io(void); +void msm_map_fsm9xxx_io(void); +void msm_map_copper_io(void); +void msm_map_msm8625_io(void); +void msm_map_msm9625_io(void); +void msm_init_irq(void); +void msm_copper_init_irq(void); +void vic_handle_irq(struct pt_regs *regs); +void msm_copper_reserve(void); +void msm_copper_very_early(void); +void msm_copper_init_gpiomux(void); + +struct mmc_platform_data; +int msm_add_sdcc(unsigned int controller, + struct mmc_platform_data *plat); + +void msm_pm_register_irqs(void); +struct msm_usb_host_platform_data; +int msm_add_host(unsigned int host, + struct msm_usb_host_platform_data *plat); +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) \ + || defined(CONFIG_USB_MSM_72K) || defined(CONFIG_USB_MSM_72K_MODULE) +void msm_hsusb_set_vbus_state(int online); +#else +static inline void msm_hsusb_set_vbus_state(int online) {} +#endif + +void msm_snddev_init(void); +void msm_snddev_init_timpani(void); +void msm_snddev_poweramp_on(void); +void msm_snddev_poweramp_off(void); +void msm_snddev_hsed_voltage_on(void); +void msm_snddev_hsed_voltage_off(void); +void msm_snddev_tx_route_config(void); +void msm_snddev_tx_route_deconfig(void); + +extern unsigned int msm_shared_ram_phys; /* defined in arch/arm/mach-msm/io.c */ + #endif diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h new file mode 100644 index 00000000000..b537c91b957 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/board_htc.h @@ -0,0 +1,78 @@ +/* arch/arm/mach-msm/include/mach/BOARD_HTC.h + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ASM_ARCH_MSM_BOARD_HTC_H +#define __ASM_ARCH_MSM_BOARD_HTC_H + +#include +#include +#include + +struct msm_pmem_setting{ + resource_size_t pmem_start; + resource_size_t pmem_size; + resource_size_t pmem_adsp_start; + resource_size_t pmem_adsp_size; + resource_size_t pmem_gpu0_start; + resource_size_t pmem_gpu0_size; + resource_size_t pmem_gpu1_start; + resource_size_t pmem_gpu1_size; + resource_size_t pmem_camera_start; + resource_size_t pmem_camera_size; + resource_size_t ram_console_start; + resource_size_t ram_console_size; +}; + +enum { + MSM_SERIAL_UART1 = 0, + MSM_SERIAL_UART2, + MSM_SERIAL_UART3, +#ifdef CONFIG_SERIAL_MSM_HS + MSM_SERIAL_UART1DM, + MSM_SERIAL_UART2DM, +#endif + MSM_SERIAL_NUM, +}; + + +/* common init routines for use by arch/arm/mach-msm/board-*.c */ + +void __init msm_add_usb_devices(void (*phy_reset) (void)); +void __init msm_add_mem_devices(struct msm_pmem_setting *setting); +void __init msm_init_pmic_vibrator(void); + +struct mmc_platform_data; +int __init msm_add_sdcc_devices(unsigned int controller, struct mmc_platform_data *plat); +int __init msm_add_serial_devices(unsigned uart); + +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +/* START: add USB connected notify function */ +struct t_usb_status_notifier{ + struct list_head notifier_link; + const char *name; + void (*func)(int online); +}; + int usb_register_notifier(struct t_usb_status_notifier *); + static LIST_HEAD(g_lh_usb_notifier_list); +/* END: add USB connected notify function */ +#endif + +int __init board_mfg_mode(void); +int __init parse_tag_smi(const struct tag *tags); +int __init parse_tag_hwid(const struct tag * tags); +int __init parse_tag_skuid(const struct tag * tags); +int parse_tag_engineerid(const struct tag * tags); + +char *board_serialno(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h new file mode 100644 index 00000000000..87e7de1a76a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -0,0 +1,701 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM__ARCH_CAMERA_H +#define __ASM__ARCH_CAMERA_H + +#include +#include +#include +#include +#include +#include +#include "linux/types.h" + +#include +#include +#include +#include + +#define CONFIG_MSM_CAMERA_DEBUG +#ifdef CONFIG_MSM_CAMERA_DEBUG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#else +#define CDBG(fmt, args...) do { } while (0) +#endif + +#define PAD_TO_2K(a, b) ((!b) ? a : (((a)+2047) & ~2047)) + +#define MSM_CAMERA_MSG 0 +#define MSM_CAMERA_EVT 1 +#define NUM_WB_EXP_NEUTRAL_REGION_LINES 4 +#define NUM_WB_EXP_STAT_OUTPUT_BUFFERS 3 +#define NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS 16 +#define NUM_STAT_OUTPUT_BUFFERS 3 +#define NUM_AF_STAT_OUTPUT_BUFFERS 3 +#define max_control_command_size 512 +#define CROP_LEN 36 + +enum vfe_mode_of_operation{ + VFE_MODE_OF_OPERATION_CONTINUOUS, + VFE_MODE_OF_OPERATION_SNAPSHOT, + VFE_MODE_OF_OPERATION_VIDEO, + VFE_MODE_OF_OPERATION_RAW_SNAPSHOT, + VFE_MODE_OF_OPERATION_ZSL, + VFE_MODE_OF_OPERATION_JPEG_SNAPSHOT, + VFE_LAST_MODE_OF_OPERATION_ENUM +}; + +enum msm_queue { + MSM_CAM_Q_CTRL, /* control command or control command status */ + MSM_CAM_Q_VFE_EVT, /* adsp event */ + MSM_CAM_Q_VFE_MSG, /* adsp message */ + MSM_CAM_Q_V4L2_REQ, /* v4l2 request */ + MSM_CAM_Q_VPE_MSG, /* vpe message */ + MSM_CAM_Q_PP_MSG, /* pp message */ +}; + +enum vfe_resp_msg { + VFE_EVENT, + VFE_MSG_GENERAL, + VFE_MSG_SNAPSHOT, + VFE_MSG_OUTPUT_P, /* preview (continuous mode ) */ + VFE_MSG_OUTPUT_T, /* thumbnail (snapshot mode )*/ + VFE_MSG_OUTPUT_S, /* main image (snapshot mode )*/ + VFE_MSG_OUTPUT_V, /* video (continuous mode ) */ + VFE_MSG_STATS_AEC, + VFE_MSG_STATS_AF, + VFE_MSG_STATS_AWB, + VFE_MSG_STATS_RS, /* 10 */ + VFE_MSG_STATS_CS, + VFE_MSG_STATS_IHIST, + VFE_MSG_STATS_SKIN, + VFE_MSG_STATS_WE, /* AEC + AWB */ + VFE_MSG_SYNC_TIMER0, + VFE_MSG_SYNC_TIMER1, + VFE_MSG_SYNC_TIMER2, + VFE_MSG_COMMON, + VFE_MSG_V32_START, + VFE_MSG_V32_START_RECORDING, /* 20 */ + VFE_MSG_V32_CAPTURE, + VFE_MSG_V32_JPEG_CAPTURE, + VFE_MSG_OUTPUT_IRQ, + VFE_MSG_V2X_PREVIEW, + VFE_MSG_V2X_CAPTURE, + VFE_MSG_OUTPUT_PRIMARY, + VFE_MSG_OUTPUT_SECONDARY, +}; + +enum vpe_resp_msg { + VPE_MSG_GENERAL, + VPE_MSG_OUTPUT_V, /* video (continuous mode ) */ + VPE_MSG_OUTPUT_ST_L, + VPE_MSG_OUTPUT_ST_R, +}; + +enum msm_stereo_state { + STEREO_VIDEO_IDLE, + STEREO_VIDEO_ACTIVE, + STEREO_SNAP_IDLE, + STEREO_SNAP_STARTED, + STEREO_SNAP_BUFFER1_PROCESSING, + STEREO_SNAP_BUFFER2_PROCESSING, + STEREO_RAW_SNAP_IDLE, + STEREO_RAW_SNAP_STARTED, +}; + +enum msm_ispif_intftype { + PIX0, + RDI0, + PIX1, + RDI1, + PIX2, + RDI2, +}; + +enum msm_ispif_vc { + VC0, + VC1, + VC2, + VC3, +}; + +enum msm_ispif_cid { + CID0, + CID1, + CID2, + CID3, + CID4, + CID5, + CID6, + CID7, + CID8, + CID9, + CID10, + CID11, + CID12, + CID13, + CID14, + CID15, +}; + +struct msm_ispif_params { + uint8_t intftype; + uint16_t cid_mask; + uint8_t csid; +}; + +struct msm_ispif_params_list { + uint32_t len; + struct msm_ispif_params params[3]; +}; + +struct msm_vpe_phy_info { + uint32_t sbuf_phy; + uint32_t planar0_off; + uint32_t planar1_off; + uint32_t planar2_off; + uint32_t p0_phy; + uint32_t p1_phy; + uint32_t p2_phy; + uint8_t output_id; /* VFE31_OUTPUT_MODE_PT/S/V */ + uint32_t frame_id; +}; + +struct msm_camera_csid_vc_cfg { + uint8_t cid; + uint8_t dt; + uint8_t decode_format; +}; + +struct msm_camera_csid_lut_params { + uint8_t num_cid; + struct msm_camera_csid_vc_cfg *vc_cfg; +}; + +struct msm_camera_csid_params { + uint8_t lane_cnt; + uint8_t lane_assign; + struct msm_camera_csid_lut_params lut_params; +}; + +struct msm_camera_csiphy_params { + uint8_t lane_cnt; + uint8_t settle_cnt; + uint8_t lane_mask; +}; + +struct msm_camera_csi2_params { + struct msm_camera_csid_params csid_params; + struct msm_camera_csiphy_params csiphy_params; +}; + +#ifndef CONFIG_MSM_CAMERA_V4L2 +#define VFE31_OUTPUT_MODE_PT (0x1 << 0) +#define VFE31_OUTPUT_MODE_S (0x1 << 1) +#define VFE31_OUTPUT_MODE_V (0x1 << 2) +#define VFE31_OUTPUT_MODE_P (0x1 << 3) +#define VFE31_OUTPUT_MODE_T (0x1 << 4) +#define VFE31_OUTPUT_MODE_P_ALL_CHNLS (0x1 << 5) +#endif + +#define CSI_EMBED_DATA 0x12 +#define CSI_RESERVED_DATA_0 0x13 +#define CSI_YUV422_8 0x1E +#define CSI_RAW8 0x2A +#define CSI_RAW10 0x2B +#define CSI_RAW12 0x2C + +#define CSI_DECODE_6BIT 0 +#define CSI_DECODE_8BIT 1 +#define CSI_DECODE_10BIT 2 +#define CSI_DECODE_DPCM_10_8_10 5 + +struct msm_vfe_phy_info { + uint32_t sbuf_phy; + uint32_t planar0_off; + uint32_t planar1_off; + uint32_t planar2_off; + uint32_t p0_phy; + uint32_t p1_phy; + uint32_t p2_phy; + uint8_t output_id; /* VFE31_OUTPUT_MODE_PT/S/V */ + uint32_t frame_id; +}; + +struct msm_vfe_stats_msg { + uint8_t awb_ymin; + uint32_t aec_buff; + uint32_t awb_buff; + uint32_t af_buff; + uint32_t ihist_buff; + uint32_t rs_buff; + uint32_t cs_buff; + uint32_t skin_buff; + uint32_t status_bits; + uint32_t frame_id; +}; + +struct video_crop_t{ + uint32_t in1_w; + uint32_t out1_w; + uint32_t in1_h; + uint32_t out1_h; + uint32_t in2_w; + uint32_t out2_w; + uint32_t in2_h; + uint32_t out2_h; + uint8_t update_flag; +}; + +struct msm_vpe_buf_info { + uint32_t p0_phy; + uint32_t p1_phy; + struct timespec ts; + uint32_t frame_id; + struct video_crop_t vpe_crop; +}; + +struct msm_vfe_resp { + enum vfe_resp_msg type; + struct msm_cam_evt_msg evt_msg; + struct msm_vfe_phy_info phy; + struct msm_vfe_stats_msg stats_msg; + struct msm_vpe_buf_info vpe_bf; + void *extdata; + int32_t extlen; +}; + +struct msm_vpe_resp { + enum vpe_resp_msg type; + struct msm_cam_evt_msg evt_msg; + struct msm_vpe_phy_info phy; + void *extdata; + int32_t extlen; +}; + +struct msm_vpe_callback { + void (*vpe_resp)(struct msm_vpe_resp *, + enum msm_queue, void *syncdata, + void *time_stamp, gfp_t gfp); + void* (*vpe_alloc)(int, void *syncdata, gfp_t gfp); + void (*vpe_free)(void *ptr); +}; + +struct msm_vfe_callback { + void (*vfe_resp)(struct msm_vfe_resp *, + enum msm_queue, void *syncdata, + gfp_t gfp); + void* (*vfe_alloc)(int, void *syncdata, gfp_t gfp); + void (*vfe_free)(void *ptr); +}; + +struct msm_camvfe_fn { + int (*vfe_init)(struct msm_vfe_callback *, + struct platform_device *); + int (*vfe_enable)(struct camera_enable_cmd *); + int (*vfe_config)(struct msm_vfe_cfg_cmd *, void *); + int (*vfe_disable)(struct camera_enable_cmd *, + struct platform_device *dev); + void (*vfe_release)(struct platform_device *); + void (*vfe_stop)(void); +}; + +struct msm_camvfe_params { + struct msm_vfe_cfg_cmd *vfe_cfg; + void *data; +}; + +struct msm_mctl_pp_params { + struct msm_mctl_pp_cmd *cmd; + void *data; +}; + +struct msm_camvpe_fn { + int (*vpe_reg)(struct msm_vpe_callback *); + int (*vpe_cfg_update) (void *); + void (*send_frame_to_vpe) (uint32_t planar0_off, uint32_t planar1_off, + struct timespec *ts, int output_id); + int (*vpe_config)(struct msm_vpe_cfg_cmd *, void *); + void (*vpe_cfg_offset)(int frame_pack, uint32_t pyaddr, + uint32_t pcbcraddr, struct timespec *ts, int output_id, + struct msm_st_half st_half, int frameid); + int *dis; +}; + +struct msm_sensor_ctrl { + int (*s_init)(const struct msm_camera_sensor_info *); + int (*s_release)(void); + int (*s_config)(void __user *); + enum msm_camera_type s_camera_type; + uint32_t s_mount_angle; + enum msm_st_frame_packing s_video_packing; + enum msm_st_frame_packing s_snap_packing; +}; + +struct msm_strobe_flash_ctrl { + int (*strobe_flash_init) + (struct msm_camera_sensor_strobe_flash_data *); + int (*strobe_flash_release) + (struct msm_camera_sensor_strobe_flash_data *, int32_t); + int (*strobe_flash_charge)(int32_t, int32_t, uint32_t); +}; + +/* this structure is used in kernel */ +struct msm_queue_cmd { + struct list_head list_config; + struct list_head list_control; + struct list_head list_frame; + struct list_head list_pict; + struct list_head list_vpe_frame; + struct list_head list_eventdata; + enum msm_queue type; + void *command; + atomic_t on_heap; + struct timespec ts; + uint32_t error_code; +}; + +struct msm_device_queue { + struct list_head list; + spinlock_t lock; + wait_queue_head_t wait; + int max; + int len; + const char *name; +}; + +struct msm_mctl_stats_t { + struct hlist_head pmem_stats_list; + spinlock_t pmem_stats_spinlock; +}; + +struct msm_sync { + /* These two queues are accessed from a process context only + * They contain pmem descriptors for the preview frames and the stats + * coming from the camera sensor. + */ + struct hlist_head pmem_frames; + struct hlist_head pmem_stats; + + /* The message queue is used by the control thread to send commands + * to the config thread, and also by the DSP to send messages to the + * config thread. Thus it is the only queue that is accessed from + * both interrupt and process context. + */ + struct msm_device_queue event_q; + + /* This queue contains preview frames. It is accessed by the DSP (in + * in interrupt context, and by the frame thread. + */ + struct msm_device_queue frame_q; + int unblock_poll_frame; + int unblock_poll_pic_frame; + + /* This queue contains snapshot frames. It is accessed by the DSP (in + * interrupt context, and by the control thread. + */ + struct msm_device_queue pict_q; + int get_pic_abort; + struct msm_device_queue vpe_q; + + struct msm_camera_sensor_info *sdata; + struct msm_camvfe_fn vfefn; + struct msm_camvpe_fn vpefn; + struct msm_sensor_ctrl sctrl; + struct msm_strobe_flash_ctrl sfctrl; + struct pm_qos_request idle_pm_qos; + struct platform_device *pdev; + int16_t ignore_qcmd_type; + uint8_t ignore_qcmd; + uint8_t opencnt; + void *cropinfo; + int croplen; + int core_powered_on; + + struct fd_roi_info fdroiinfo; + + atomic_t vpe_enable; + uint32_t pp_mask; + uint8_t pp_frame_avail; + struct msm_queue_cmd *pp_prev; + struct msm_queue_cmd *pp_snap; + struct msm_queue_cmd *pp_thumb; + int video_fd; + + const char *apps_id; + + struct mutex lock; + struct list_head list; + uint8_t liveshot_enabled; + struct msm_cam_v4l2_device *pcam_sync; + + uint8_t stereocam_enabled; + struct msm_queue_cmd *pp_stereocam; + struct msm_queue_cmd *pp_stereocam2; + struct msm_queue_cmd *pp_stereosnap; + enum msm_stereo_state stereo_state; + int stcam_quality_ind; + uint32_t stcam_conv_value; + + spinlock_t pmem_frame_spinlock; + spinlock_t pmem_stats_spinlock; + spinlock_t abort_pict_lock; + int snap_count; + int thumb_count; +}; + +#define MSM_APPS_ID_V4L2 "msm_v4l2" +#define MSM_APPS_ID_PROP "msm_qct" + +struct msm_cam_device { + struct msm_sync *sync; /* most-frequently accessed */ + struct device *device; + struct cdev cdev; + /* opened is meaningful only for the config and frame nodes, + * which may be opened only once. + */ + atomic_t opened; +}; + +struct msm_control_device { + struct msm_cam_device *pmsm; + + /* Used for MSM_CAM_IOCTL_CTRL_CMD_DONE responses */ + uint8_t ctrl_data[max_control_command_size]; + struct msm_ctrl_cmd ctrl; + struct msm_queue_cmd qcmd; + + /* This queue used by the config thread to send responses back to the + * control thread. It is accessed only from a process context. + */ + struct msm_device_queue ctrl_q; +}; + +struct register_address_value_pair { + uint16_t register_address; + uint16_t register_value; +}; + +struct msm_pmem_region { + struct hlist_node list; + unsigned long paddr; + unsigned long len; + struct file *file; + struct msm_pmem_info info; + struct ion_handle *handle; +}; + +struct axidata { + uint32_t bufnum1; + uint32_t bufnum2; + uint32_t bufnum3; + struct msm_pmem_region *region; +}; + +#ifdef CONFIG_MSM_CAMERA_FLASH +int msm_camera_flash_set_led_state( + struct msm_camera_sensor_flash_data *fdata, + unsigned led_state); +int msm_strobe_flash_init(struct msm_sync *sync, uint32_t sftype); +int msm_flash_ctrl(struct msm_camera_sensor_info *sdata, + struct flash_ctrl_data *flash_info); +#else +static inline int msm_camera_flash_set_led_state( + struct msm_camera_sensor_flash_data *fdata, + unsigned led_state) +{ + return -ENOTSUPP; +} +static inline int msm_strobe_flash_init( + struct msm_sync *sync, uint32_t sftype) +{ + return -ENOTSUPP; +} +static inline int msm_flash_ctrl( + struct msm_camera_sensor_info *sdata, + struct flash_ctrl_data *flash_info) +{ + return -ENOTSUPP; +} +#endif + + + +void msm_camvfe_init(void); +int msm_camvfe_check(void *); +void msm_camvfe_fn_init(struct msm_camvfe_fn *, void *); +void msm_camvpe_fn_init(struct msm_camvpe_fn *, void *); +int msm_camera_drv_start(struct platform_device *dev, + int (*sensor_probe)(const struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *)); + +enum msm_camio_clk_type { + CAMIO_VFE_MDC_CLK, + CAMIO_MDC_CLK, + CAMIO_VFE_CLK, + CAMIO_VFE_AXI_CLK, + + CAMIO_VFE_CAMIF_CLK, + CAMIO_VFE_PBDG_CLK, + CAMIO_CAM_MCLK_CLK, + CAMIO_CAMIF_PAD_PBDG_CLK, + + CAMIO_CSI0_VFE_CLK, + CAMIO_CSI1_VFE_CLK, + CAMIO_VFE_PCLK, + + CAMIO_CSI_SRC_CLK, + CAMIO_CSI0_CLK, + CAMIO_CSI1_CLK, + CAMIO_CSI0_PCLK, + CAMIO_CSI1_PCLK, + + CAMIO_CSI1_SRC_CLK, + CAMIO_CSI_PIX_CLK, + CAMIO_CSI_PIX1_CLK, + CAMIO_CSI_RDI_CLK, + CAMIO_CSI_RDI1_CLK, + CAMIO_CSI_RDI2_CLK, + CAMIO_CSIPHY0_TIMER_CLK, + CAMIO_CSIPHY1_TIMER_CLK, + + CAMIO_JPEG_CLK, + CAMIO_JPEG_PCLK, + CAMIO_VPE_CLK, + CAMIO_VPE_PCLK, + + CAMIO_CSI0_PHY_CLK, + CAMIO_CSI1_PHY_CLK, + CAMIO_CSIPHY_TIMER_SRC_CLK, + CAMIO_IMEM_CLK, + + CAMIO_MAX_CLK +}; + +enum msm_camio_clk_src_type { + MSM_CAMIO_CLK_SRC_INTERNAL, + MSM_CAMIO_CLK_SRC_EXTERNAL, + MSM_CAMIO_CLK_SRC_MAX +}; + +enum msm_s_test_mode { + S_TEST_OFF, + S_TEST_1, + S_TEST_2, + S_TEST_3 +}; + +enum msm_s_resolution { + S_QTR_SIZE, + S_FULL_SIZE, + S_INVALID_SIZE +}; + +enum msm_s_reg_update { + /* Sensor egisters that need to be updated during initialization */ + S_REG_INIT, + /* Sensor egisters that needs periodic I2C writes */ + S_UPDATE_PERIODIC, + /* All the sensor Registers will be updated */ + S_UPDATE_ALL, + /* Not valid update */ + S_UPDATE_INVALID +}; + +enum msm_s_setting { + S_RES_PREVIEW, + S_RES_CAPTURE +}; + +enum msm_bus_perf_setting { + S_INIT, + S_PREVIEW, + S_VIDEO, + S_CAPTURE, + S_ZSL, + S_STEREO_VIDEO, + S_STEREO_CAPTURE, + S_DEFAULT, + S_EXIT +}; + +struct msm_cam_clk_info { + const char *clk_name; + long clk_rate; +}; + +int msm_camio_enable(struct platform_device *dev); +int msm_camio_vpe_clk_enable(uint32_t); +int msm_camio_vpe_clk_disable(void); + +void msm_camio_mode_config(enum msm_camera_i2c_mux_mode mode); +int msm_camio_clk_enable(enum msm_camio_clk_type clk); +int msm_camio_clk_disable(enum msm_camio_clk_type clk); +int msm_camio_clk_config(uint32_t freq); +void msm_camio_clk_rate_set(int rate); +int msm_camio_vfe_clk_rate_set(int rate); +void msm_camio_clk_rate_set_2(struct clk *clk, int rate); +void msm_camio_clk_axi_rate_set(int rate); +void msm_disable_io_gpio_clk(struct platform_device *); + +void msm_camio_camif_pad_reg_reset(void); +void msm_camio_camif_pad_reg_reset_2(void); + +void msm_camio_vfe_blk_reset(void); +void msm_camio_vfe_blk_reset_2(void); +void msm_camio_vfe_blk_reset_3(void); + +int32_t msm_camio_3d_enable(const struct msm_camera_sensor_info *sinfo); +void msm_camio_3d_disable(void); +void msm_camio_clk_sel(enum msm_camio_clk_src_type); +void msm_camio_disable(struct platform_device *); +int msm_camio_probe_on(struct platform_device *); +int msm_camio_probe_off(struct platform_device *); +int msm_camio_sensor_clk_off(struct platform_device *); +int msm_camio_sensor_clk_on(struct platform_device *); +int msm_camio_csi_config(struct msm_camera_csi_params *csi_params); +int msm_camio_csiphy_config(struct msm_camera_csiphy_params *csiphy_params); +int msm_camio_csid_config(struct msm_camera_csid_params *csid_params); +int add_axi_qos(void); +int update_axi_qos(uint32_t freq); +void release_axi_qos(void); +void msm_camera_io_w(u32 data, void __iomem *addr); +void msm_camera_io_w_mb(u32 data, void __iomem *addr); +u32 msm_camera_io_r(void __iomem *addr); +u32 msm_camera_io_r_mb(void __iomem *addr); +void msm_camera_io_dump(void __iomem *addr, int size); +void msm_camera_io_memcpy(void __iomem *dest_addr, + void __iomem *src_addr, u32 len); +void msm_camio_set_perf_lvl(enum msm_bus_perf_setting); +void msm_camio_bus_scale_cfg( + struct msm_bus_scale_pdata *, enum msm_bus_perf_setting); + +void *msm_isp_sync_alloc(int size, gfp_t gfp); + +void msm_isp_sync_free(void *ptr); + +int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info, + struct clk **clk_ptr, int num_clk, int enable); +int msm_cam_core_reset(void); + +int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg, + int num_vreg, struct regulator **reg_ptr, int config); +int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg, + int num_vreg, struct regulator **reg_ptr, int enable); + +int msm_camera_config_gpio_table + (struct msm_camera_sensor_info *sinfo, int gpio_en); +int msm_camera_request_gpio_table + (struct msm_camera_sensor_info *sinfo, int gpio_en); +#endif diff --git a/arch/arm/mach-msm/include/mach/clk.h b/arch/arm/mach-msm/include/mach/clk.h index e8d38428d81..8c0ebfa6ca8 100644 --- a/arch/arm/mach-msm/include/mach/clk.h +++ b/arch/arm/mach-msm/include/mach/clk.h @@ -25,9 +25,6 @@ enum clk_reset_action { struct clk; -/* Rate is minimum clock rate in Hz */ -int clk_set_min_rate(struct clk *clk, unsigned long rate); - /* Rate is maximum clock rate in Hz */ int clk_set_max_rate(struct clk *clk, unsigned long rate); diff --git a/arch/arm/mach-msm/include/mach/cpu.h b/arch/arm/mach-msm/include/mach/cpu.h deleted file mode 100644 index a9481b08d5c..00000000000 --- a/arch/arm/mach-msm/include/mach/cpu.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#ifndef __ARCH_ARM_MACH_MSM_CPU_H__ -#define __ARCH_ARM_MACH_MSM_CPU_H__ - -/* TODO: For now, only one CPU can be compiled at a time. */ - -#define cpu_is_msm7x01() 0 -#define cpu_is_msm7x30() 0 -#define cpu_is_qsd8x50() 0 -#define cpu_is_msm8x60() 0 -#define cpu_is_msm8960() 0 - -#ifdef CONFIG_ARCH_MSM7X00A -# undef cpu_is_msm7x01 -# define cpu_is_msm7x01() 1 -#endif - -#ifdef CONFIG_ARCH_MSM7X30 -# undef cpu_is_msm7x30 -# define cpu_is_msm7x30() 1 -#endif - -#ifdef CONFIG_ARCH_QSD8X50 -# undef cpu_is_qsd8x50 -# define cpu_is_qsd8x50() 1 -#endif - -#ifdef CONFIG_ARCH_MSM8X60 -# undef cpu_is_msm8x60 -# define cpu_is_msm8x60() 1 -#endif - -#ifdef CONFIG_ARCH_MSM8960 -# undef cpu_is_msm8960 -# define cpu_is_msm8960() 1 -#endif - -#endif diff --git a/arch/arm/mach-msm/include/mach/cpuidle.h b/arch/arm/mach-msm/include/mach/cpuidle.h new file mode 100644 index 00000000000..2a5aa975e65 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/cpuidle.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CPUIDLE_H +#define __ARCH_ARM_MACH_MSM_CPUIDLE_H + +#include +#include "../../pm.h" + +struct msm_cpuidle_state { + unsigned int cpu; + int state_nr; + char *name; + char *desc; + enum msm_pm_sleep_mode mode_nr; +}; + +#ifdef CONFIG_CPU_IDLE +s32 msm_cpuidle_get_deep_idle_latency(void); +int msm_cpuidle_init(void); +#else +static inline int msm_cpuidle_init(void) { return -ENOSYS; } +static inline s32 msm_cpuidle_get_deep_idle_latency(void) { return 0; } +#endif + +#ifdef CONFIG_MSM_SLEEP_STATS +enum { + MSM_CPUIDLE_STATE_ENTER, + MSM_CPUIDLE_STATE_EXIT +}; + +int msm_cpuidle_register_notifier(unsigned int cpu, + struct notifier_block *nb); +int msm_cpuidle_unregister_notifier(unsigned int cpu, + struct notifier_block *nb); +#else +static inline int msm_cpuidle_register_notifier(unsigned int cpu, + struct notifier_block *nb) +{ return -ENODEV; } +static inline int msm_cpuidle_unregister_notifier(unsigned int cpu, + struct notifier_block *nb) +{ return -ENODEV; } +#endif + +#endif /* __ARCH_ARM_MACH_MSM_CPUIDLE_H */ diff --git a/arch/arm/mach-msm/include/mach/dal.h b/arch/arm/mach-msm/include/mach/dal.h new file mode 100644 index 00000000000..d0c754d380f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/dal.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DAL_H__ +#define __DAL_H__ + +#include +#include + +#define DALRPC_DEST_MODEM SMD_APPS_MODEM +#define DALRPC_DEST_QDSP SMD_APPS_QDSP + +#define DALRPC_TIMEOUT_INFINITE -1 + +enum { + DALDEVICE_ATTACH_IDX = 0, + DALDEVICE_DETACH_IDX, + DALDEVICE_INIT_IDX, + DALDEVICE_DEINIT_IDX, + DALDEVICE_OPEN_IDX, + DALDEVICE_CLOSE_IDX, + DALDEVICE_INFO_IDX, + DALDEVICE_POWEREVENT_IDX, + DALDEVICE_SYSREQUEST_IDX, + DALDEVICE_FIRST_DEVICE_API_IDX +}; + +struct daldevice_info_t { + uint32_t size; + uint32_t version; + char name[32]; +}; + +#define DAL_CHUNK_NAME_LENGTH 12 +struct dal_chunk_header { + uint32_t size; + char name[DAL_CHUNK_NAME_LENGTH]; + uint32_t lock; + uint32_t reserved; + uint32_t type; + uint32_t version; +}; + +int daldevice_attach(uint32_t device_id, char *port, int cpu, + void **handle_ptr); + +/* The caller must ensure there are no outstanding dalrpc calls on + * the client before (and while) calling daldevice_detach. */ +int daldevice_detach(void *handle); + +uint32_t dalrpc_fcn_0(uint32_t ddi_idx, void *handle, uint32_t s1); +uint32_t dalrpc_fcn_1(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2); +uint32_t dalrpc_fcn_2(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t *p_s2); +uint32_t dalrpc_fcn_3(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t s3); +uint32_t dalrpc_fcn_4(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t *p_s3); +uint32_t dalrpc_fcn_5(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen); +uint32_t dalrpc_fcn_6(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen); +uint32_t dalrpc_fcn_7(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + uint32_t *oalen); +uint32_t dalrpc_fcn_8(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen); +uint32_t dalrpc_fcn_9(uint32_t ddi_idx, void *handle, void *obuf, + uint32_t olen); +uint32_t dalrpc_fcn_10(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen, void *obuf, + uint32_t olen, uint32_t *oalen); +uint32_t dalrpc_fcn_11(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen); +uint32_t dalrpc_fcn_12(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen, uint32_t *oalen); +uint32_t dalrpc_fcn_13(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen); +uint32_t dalrpc_fcn_14(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf1, uint32_t olen1, + void *obuf2, uint32_t olen2, uint32_t *oalen2); +uint32_t dalrpc_fcn_15(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen, uint32_t *oalen, + void *obuf2, uint32_t olen2); + +static inline uint32_t daldevice_info(void *handle, + struct daldevice_info_t *info, + uint32_t info_size) +{ + return dalrpc_fcn_9(DALDEVICE_INFO_IDX, handle, info, info_size); +} + +static inline uint32_t daldevice_sysrequest(void *handle, uint32_t req_id, + const void *src_ptr, + uint32_t src_len, void *dest_ptr, + uint32_t dest_len, + uint32_t *dest_alen) +{ + return dalrpc_fcn_10(DALDEVICE_SYSREQUEST_IDX, handle, req_id, + src_ptr, src_len, dest_ptr, dest_len, dest_alen); +} + +static inline uint32_t daldevice_init(void *handle) +{ + return dalrpc_fcn_0(DALDEVICE_INIT_IDX, handle, 0); +} + +static inline uint32_t daldevice_deinit(void *handle) +{ + return dalrpc_fcn_0(DALDEVICE_DEINIT_IDX, handle, 0); +} + +static inline uint32_t daldevice_open(void *handle, uint32_t mode) +{ + return dalrpc_fcn_0(DALDEVICE_OPEN_IDX, handle, mode); +} + +static inline uint32_t daldevice_close(void *handle) +{ + return dalrpc_fcn_0(DALDEVICE_CLOSE_IDX, handle, 0); +} + +void *dalrpc_alloc_event(void *handle); +void *dalrpc_alloc_cb(void *handle, + void (*fn)(void *, uint32_t, void *, uint32_t), + void *context); +void dalrpc_dealloc_event(void *handle, + void *ev_h); +void dalrpc_dealloc_cb(void *handle, + void *cb_h); + +#define dalrpc_event_wait(ev_h, timeout) \ + dalrpc_event_wait_multiple(1, &ev_h, timeout) + +int dalrpc_event_wait_multiple(int num, void **ev_h, int timeout); + +#endif /* __DAL_H__ */ diff --git a/arch/arm/mach-msm/include/mach/dal_axi.h b/arch/arm/mach-msm/include/mach/dal_axi.h new file mode 100644 index 00000000000..4e32aa3dabf --- /dev/null +++ b/arch/arm/mach-msm/include/mach/dal_axi.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _DAL_AXI_H +#define _DAL_AXI_H + +#include + +int set_grp2d_async(void); +int set_grp3d_async(void); +int set_grp_xbar_async(void); + +#endif /* _DAL_AXI_H */ diff --git a/arch/arm/mach-msm/include/mach/debug-macro.S b/arch/arm/mach-msm/include/mach/debug-macro.S index 3ffd8668c9a..c7609539720 100644 --- a/arch/arm/mach-msm/include/mach/debug-macro.S +++ b/arch/arm/mach-msm/include/mach/debug-macro.S @@ -1,7 +1,6 @@ /* * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -15,52 +14,67 @@ * */ + + #include #include +#include - .macro addruart, rp, rv, tmp #ifdef MSM_DEBUG_UART_PHYS - ldr \rp, =MSM_DEBUG_UART_PHYS - ldr \rv, =MSM_DEBUG_UART_BASE -#endif + .macro addruart, rp, rv, tmp + ldr \rp, =MSM_DEBUG_UART_PHYS + ldr \rv, =MSM_DEBUG_UART_BASE .endm - .macro senduart, rd, rx + .macro senduart,rd,rx #ifdef CONFIG_MSM_HAS_DEBUG_UART_HS + @ Clear TX_READY by writing to the UARTDM_CR register + mov r12, #0x300 + str r12, [\rx, #UARTDM_CR_OFFSET] + @ Write 0x1 to NCF register + mov r12, #0x1 + str r12, [\rx, #UARTDM_NCF_TX_OFFSET] + @ UARTDM reg. Read to induce delay + ldr r12, [\rx, #UARTDM_SR_OFFSET] @ Write the 1 character to UARTDM_TF - str \rd, [\rx, #0x70] + str \rd, [\rx, #UARTDM_TF_OFFSET] #else - teq \rx, #0 - strne \rd, [\rx, #0x0C] + teq \rx, #0 + strne \rd, [\rx, #0x0C] #endif .endm - .macro waituart, rd, rx + .macro waituart,rd,rx #ifdef CONFIG_MSM_HAS_DEBUG_UART_HS @ check for TX_EMT in UARTDM_SR - ldr \rd, [\rx, #0x08] + ldr \rd, [\rx, #UARTDM_SR_OFFSET] tst \rd, #0x08 bne 1002f @ wait for TXREADY in UARTDM_ISR -1001: ldr \rd, [\rx, #0x14] +1001: ldreq \rd, [\rx, #UARTDM_ISR_OFFSET] tst \rd, #0x80 + dsb beq 1001b -1002: - @ Clear TX_READY by writing to the UARTDM_CR register - mov \rd, #0x300 - str \rd, [\rx, #0x10] - @ Write 0x1 to NCF register - mov \rd, #0x1 - str \rd, [\rx, #0x40] - @ UARTDM reg. Read to induce delay - ldr \rd, [\rx, #0x08] #else @ wait for TX_READY -1001: ldr \rd, [\rx, #0x08] - tst \rd, #0x04 - beq 1001b +1001: ldr \rd, [\rx, #0x08] + tst \rd, #0x04 + beq 1001b #endif +1002: .endm - .macro busyuart, rd, rx +#else + + .macro addruart, rp, rv + .endm + + .macro senduart,rd,rx + .endm + + .macro waituart,rd,rx + .endm +#endif + + .macro busyuart,rd,rx .endm diff --git a/arch/arm/mach-msm/include/mach/debug_mm.h b/arch/arm/mach-msm/include/mach/debug_mm.h new file mode 100644 index 00000000000..091798c3312 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/debug_mm.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ARCH_ARM_MACH_MSM_DEBUG_MM_H_ +#define __ARCH_ARM_MACH_MSM_DEBUG_MM_H_ + +#include + +/* The below macro removes the directory path name and retains only the + * file name to avoid long path names in log messages that comes as + * part of __FILE__ to compiler. + */ +#define __MM_FILE__ strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/')+1) : \ + __FILE__ + +#define MM_DBG(fmt, args...) pr_debug("[%s] " fmt,\ + __func__, ##args) + +#define MM_INFO(fmt, args...) pr_info("[%s:%s] " fmt,\ + __MM_FILE__, __func__, ##args) + +#define MM_ERR(fmt, args...) pr_err("[%s:%s] " fmt,\ + __MM_FILE__, __func__, ##args) +#endif /* __ARCH_ARM_MACH_MSM_DEBUG_MM_H_ */ diff --git a/arch/arm/mach-msm/include/mach/dma-fsm9xxx.h b/arch/arm/mach-msm/include/mach/dma-fsm9xxx.h new file mode 100644 index 00000000000..e28426727eb --- /dev/null +++ b/arch/arm/mach-msm/include/mach/dma-fsm9xxx.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_DMA_FSM9XXX_H +#define __ASM_ARCH_MSM_DMA_FSM9XXX_H + +/* DMA channels allocated to Scorpion */ +#define DMOV_GP_CHAN 4 +#define DMOV_CE1_IN_CHAN 5 +#define DMOV_CE1_OUT_CHAN 6 +#define DMOV_NAND_CHAN 7 +#define DMOV_SDC1_CHAN 8 +#define DMOV_GP2_CHAN 10 +#define DMOV_CE2_IN_CHAN 12 +#define DMOV_CE2_OUT_CHAN 13 +#define DMOV_CE3_IN_CHAN 14 +#define DMOV_CE3_OUT_CHAN 15 + +/* CRCIs */ +#define DMOV_CE1_IN_CRCI 1 +#define DMOV_CE1_OUT_CRCI 2 +#define DMOV_CE1_HASH_CRCI 3 + +#define DMOV_NAND_CRCI_DATA 4 +#define DMOV_NAND_CRCI_CMD 5 + +#define DMOV_SDC1_CRCI 6 + +#define DMOV_HSUART_TX_CRCI 7 +#define DMOV_HSUART_RX_CRCI 8 + +#define DMOV_CE2_IN_CRCI 9 +#define DMOV_CE2_OUT_CRCI 10 +#define DMOV_CE2_HASH_CRCI 11 + +#define DMOV_CE3_IN_CRCI 12 +#define DMOV_CE3_OUT_CRCI 13 +#define DMOV_CE3_HASH_DONE_CRCI 14 + +/* Following CRCIs are not defined in FSM9XXX, but these are added to keep + * the existing SDCC host controller driver compatible with FSM9XXX. + */ +#define DMOV_SDC2_CRCI DMOV_SDC1_CRCI +#define DMOV_SDC3_CRCI DMOV_SDC1_CRCI +#define DMOV_SDC4_CRCI DMOV_SDC1_CRCI + +#endif /* __ASM_ARCH_MSM_DMA_FSM9XXX_H */ diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h index 05583f56952..70519ff8898 100644 --- a/arch/arm/mach-msm/include/mach/dma.h +++ b/arch/arm/mach-msm/include/mach/dma.h @@ -1,6 +1,7 @@ /* linux/include/asm-arm/arch-msm/dma.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -14,10 +15,15 @@ */ #ifndef __ASM_ARCH_MSM_DMA_H +#define __ASM_ARCH_MSM_DMA_H #include #include +#if defined(CONFIG_ARCH_FSM9XXX) +#include +#endif + struct msm_dmov_errdata { uint32_t flush[6]; }; @@ -28,71 +34,191 @@ struct msm_dmov_cmd { void (*complete_func)(struct msm_dmov_cmd *cmd, unsigned int result, struct msm_dmov_errdata *err); - void (*execute_func)(struct msm_dmov_cmd *cmd); - void *data; + void (*exec_func)(struct msm_dmov_cmd *cmd); + void *user; /* Pointer for caller's reference */ +}; + +struct msm_dmov_pdata { + int sd; + size_t sd_size; }; -#ifndef CONFIG_ARCH_MSM8X60 void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd); -void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful); +void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd); +void msm_dmov_flush(unsigned int id, int graceful); int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr); -#else -static inline -void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) { } -static inline -void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful) { } -static inline -int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) { return -EIO; } -#endif +#define DMOV_CRCIS_PER_CONF 10 -#define DMOV_SD0(off, ch) (MSM_DMOV_BASE + 0x0000 + (off) + ((ch) << 2)) -#define DMOV_SD1(off, ch) (MSM_DMOV_BASE + 0x0400 + (off) + ((ch) << 2)) -#define DMOV_SD2(off, ch) (MSM_DMOV_BASE + 0x0800 + (off) + ((ch) << 2)) -#define DMOV_SD3(off, ch) (MSM_DMOV_BASE + 0x0C00 + (off) + ((ch) << 2)) +#define DMOV_ADDR(off, ch) ((off) + ((ch) << 2)) -#if defined(CONFIG_ARCH_MSM7X30) -#define DMOV_SD_AARM DMOV_SD2 -#else -#define DMOV_SD_AARM DMOV_SD3 -#endif - -#define DMOV_CMD_PTR(ch) DMOV_SD_AARM(0x000, ch) +#define DMOV_CMD_PTR(ch) DMOV_ADDR(0x000, ch) #define DMOV_CMD_LIST (0 << 29) /* does not work */ #define DMOV_CMD_PTR_LIST (1 << 29) /* works */ #define DMOV_CMD_INPUT_CFG (2 << 29) /* untested */ #define DMOV_CMD_OUTPUT_CFG (3 << 29) /* untested */ #define DMOV_CMD_ADDR(addr) ((addr) >> 3) -#define DMOV_RSLT(ch) DMOV_SD_AARM(0x040, ch) +#define DMOV_RSLT(ch) DMOV_ADDR(0x040, ch) #define DMOV_RSLT_VALID (1 << 31) /* 0 == host has empties result fifo */ #define DMOV_RSLT_ERROR (1 << 3) #define DMOV_RSLT_FLUSH (1 << 2) #define DMOV_RSLT_DONE (1 << 1) /* top pointer done */ #define DMOV_RSLT_USER (1 << 0) /* command with FR force result */ -#define DMOV_FLUSH0(ch) DMOV_SD_AARM(0x080, ch) -#define DMOV_FLUSH1(ch) DMOV_SD_AARM(0x0C0, ch) -#define DMOV_FLUSH2(ch) DMOV_SD_AARM(0x100, ch) -#define DMOV_FLUSH3(ch) DMOV_SD_AARM(0x140, ch) -#define DMOV_FLUSH4(ch) DMOV_SD_AARM(0x180, ch) -#define DMOV_FLUSH5(ch) DMOV_SD_AARM(0x1C0, ch) +#define DMOV_FLUSH0(ch) DMOV_ADDR(0x080, ch) +#define DMOV_FLUSH1(ch) DMOV_ADDR(0x0C0, ch) +#define DMOV_FLUSH2(ch) DMOV_ADDR(0x100, ch) +#define DMOV_FLUSH3(ch) DMOV_ADDR(0x140, ch) +#define DMOV_FLUSH4(ch) DMOV_ADDR(0x180, ch) +#define DMOV_FLUSH5(ch) DMOV_ADDR(0x1C0, ch) +#define DMOV_FLUSH_TYPE (1 << 31) -#define DMOV_STATUS(ch) DMOV_SD_AARM(0x200, ch) +#define DMOV_STATUS(ch) DMOV_ADDR(0x200, ch) #define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29)) #define DMOV_STATUS_CMD_COUNT(n) (((n) >> 27) & 3) #define DMOV_STATUS_RSLT_VALID (1 << 1) #define DMOV_STATUS_CMD_PTR_RDY (1 << 0) -#define DMOV_ISR DMOV_SD_AARM(0x380, 0) - -#define DMOV_CONFIG(ch) DMOV_SD_AARM(0x300, ch) -#define DMOV_CONFIG_FORCE_TOP_PTR_RSLT (1 << 2) -#define DMOV_CONFIG_FORCE_FLUSH_RSLT (1 << 1) -#define DMOV_CONFIG_IRQ_EN (1 << 0) +#define DMOV_CONF(ch) DMOV_ADDR(0x240, ch) +#define DMOV_CONF_SD(sd) (((sd & 4) << 11) | ((sd & 3) << 4)) +#define DMOV_CONF_IRQ_EN (1 << 6) +#define DMOV_CONF_FORCE_RSLT_EN (1 << 7) +#define DMOV_CONF_SHADOW_EN (1 << 12) +#define DMOV_CONF_MPU_DISABLE (1 << 11) +#define DMOV_CONF_PRIORITY(n) (n << 0) + +#define DMOV_DBG_ERR(ci) DMOV_ADDR(0x280, ci) + +#define DMOV_RSLT_CONF(ch) DMOV_ADDR(0x300, ch) +#define DMOV_RSLT_CONF_FORCE_TOP_PTR_RSLT (1 << 2) +#define DMOV_RSLT_CONF_FORCE_FLUSH_RSLT (1 << 1) +#define DMOV_RSLT_CONF_IRQ_EN (1 << 0) + +#define DMOV_ISR DMOV_ADDR(0x380, 0) + +#define DMOV_CI_CONF(ci) DMOV_ADDR(0x390, ci) +#define DMOV_CI_CONF_RANGE_END(n) ((n) << 24) +#define DMOV_CI_CONF_RANGE_START(n) ((n) << 16) +#define DMOV_CI_CONF_MAX_BURST(n) ((n) << 0) + +#define DMOV_CI_DBG_ERR(ci) DMOV_ADDR(0x3B0, ci) + +#define DMOV_CRCI_CONF0 DMOV_ADDR(0x3D0, 0) +#define DMOV_CRCI_CONF1 DMOV_ADDR(0x3D4, 0) +#define DMOV_CRCI_CONF0_SD(crci, sd) (sd << (crci*3)) +#define DMOV_CRCI_CONF1_SD(crci, sd) (sd << ((crci-DMOV_CRCIS_PER_CONF)*3)) + +#define DMOV_CRCI_CTL(crci) DMOV_ADDR(0x400, crci) +#define DMOV_CRCI_CTL_BLK_SZ(n) ((n) << 0) +#define DMOV_CRCI_CTL_RST (1 << 17) +#define DMOV_CRCI_MUX (1 << 18) /* channel assignments */ +/* + * Format of CRCI numbers: crci number + (muxsel << 4) + */ + +#if defined(CONFIG_ARCH_MSM8X60) +#define DMOV_GP_CHAN 15 + +#define DMOV_NAND_CHAN 17 +#define DMOV_NAND_CHAN_MODEM 26 +#define DMOV_NAND_CHAN_Q6 27 +#define DMOV_NAND_CRCI_CMD 15 +#define DMOV_NAND_CRCI_DATA 3 + +#define DMOV_CE_IN_CHAN 2 +#define DMOV_CE_IN_CRCI 4 + +#define DMOV_CE_OUT_CHAN 3 +#define DMOV_CE_OUT_CRCI 5 + +#define DMOV_CE_HASH_CRCI 15 + +#define DMOV_SDC1_CHAN 18 +#define DMOV_SDC1_CRCI 1 + +#define DMOV_SDC2_CHAN 19 +#define DMOV_SDC2_CRCI 4 + +#define DMOV_SDC3_CHAN 20 +#define DMOV_SDC3_CRCI 2 + +#define DMOV_SDC4_CHAN 21 +#define DMOV_SDC4_CRCI 5 + +#define DMOV_SDC5_CHAN 21 +#define DMOV_SDC5_CRCI 14 + +#define DMOV_TSIF_CHAN 4 +#define DMOV_TSIF_CRCI 6 + +#define DMOV_HSUART1_TX_CHAN 22 +#define DMOV_HSUART1_TX_CRCI 8 + +#define DMOV_HSUART1_RX_CHAN 23 +#define DMOV_HSUART1_RX_CRCI 9 + +#define DMOV_HSUART2_TX_CHAN 8 +#define DMOV_HSUART2_TX_CRCI 13 + +#define DMOV_HSUART2_RX_CHAN 8 +#define DMOV_HSUART2_RX_CRCI 14 + +#elif defined(CONFIG_ARCH_MSM8960) +#define DMOV_GP_CHAN 9 + +#define DMOV_CE_IN_CHAN 0 +#define DMOV_CE_IN_CRCI 2 + +#define DMOV_CE_OUT_CHAN 1 +#define DMOV_CE_OUT_CRCI 3 + +#define DMOV_TSIF_CHAN 2 +#define DMOV_TSIF_CRCI 11 + +#define DMOV_HSUART_GSBI6_TX_CHAN 7 +#define DMOV_HSUART_GSBI6_TX_CRCI 6 + +#define DMOV_HSUART_GSBI6_RX_CHAN 8 +#define DMOV_HSUART_GSBI6_RX_CRCI 11 + +#define DMOV_HSUART_GSBI9_TX_CHAN 4 +#define DMOV_HSUART_GSBI9_TX_CRCI 13 + +#define DMOV_HSUART_GSBI9_RX_CHAN 3 +#define DMOV_HSUART_GSBI9_RX_CRCI 12 + +#elif defined(CONFIG_ARCH_MSM9615) + +#define DMOV_GP_CHAN 4 + +#define DMOV_CE_IN_CHAN 0 +#define DMOV_CE_IN_CRCI 12 + +#define DMOV_CE_OUT_CHAN 1 +#define DMOV_CE_OUT_CRCI 13 + +#define DMOV_NAND_CHAN 3 +#define DMOV_NAND_CRCI_CMD 15 +#define DMOV_NAND_CRCI_DATA 3 + +#elif defined(CONFIG_ARCH_FSM9XXX) +/* defined in dma-fsm9xxx.h */ + +#else +#define DMOV_GP_CHAN 4 + +#define DMOV_CE_IN_CHAN 5 +#define DMOV_CE_IN_CRCI 1 + +#define DMOV_CE_OUT_CHAN 6 +#define DMOV_CE_OUT_CRCI 2 + +#define DMOV_CE_HASH_CRCI 3 + #define DMOV_NAND_CHAN 7 #define DMOV_NAND_CRCI_CMD 5 #define DMOV_NAND_CRCI_DATA 4 @@ -103,11 +229,38 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) { return -EIO; } #define DMOV_SDC2_CHAN 8 #define DMOV_SDC2_CRCI 7 +#define DMOV_SDC3_CHAN 8 +#define DMOV_SDC3_CRCI 12 + +#define DMOV_SDC4_CHAN 8 +#define DMOV_SDC4_CRCI 13 + #define DMOV_TSIF_CHAN 10 #define DMOV_TSIF_CRCI 10 #define DMOV_USB_CHAN 11 +#define DMOV_HSUART1_TX_CHAN 4 +#define DMOV_HSUART1_TX_CRCI 8 + +#define DMOV_HSUART1_RX_CHAN 9 +#define DMOV_HSUART1_RX_CRCI 9 + +#define DMOV_HSUART2_TX_CHAN 4 +#define DMOV_HSUART2_TX_CRCI 14 + +#define DMOV_HSUART2_RX_CHAN 11 +#define DMOV_HSUART2_RX_CRCI 15 +#endif + +/* channels for APQ8064 */ +#define DMOV8064_CE_IN_CHAN 0 +#define DMOV8064_CE_IN_CRCI 14 + +#define DMOV8064_CE_OUT_CHAN 1 +#define DMOV8064_CE_OUT_CRCI 15 + + /* no client rate control ifc (eg, ram) */ #define DMOV_NONE_CRCI 0 diff --git a/arch/arm/mach-msm/include/mach/dma_test.h b/arch/arm/mach-msm/include/mach/dma_test.h new file mode 100644 index 00000000000..c0464fa7ded --- /dev/null +++ b/arch/arm/mach-msm/include/mach/dma_test.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MSM_DMA_TEST__ +#define __MSM_DMA_TEST__ + +#include + +#define MSM_DMA_IOC_MAGIC 0x83 + +/* The testing driver can manage a series of buffers. These are + * allocated and freed using these calls. */ +struct msm_dma_alloc_req { + int size; /* Size of this request, in bytes. */ + int bufnum; /* OUT: Number of buffer allocated. */ +}; +#define MSM_DMA_IOALLOC _IOWR(MSM_DMA_IOC_MAGIC, 2, struct msm_dma_alloc_req) + +/* Free the specified buffer. */ +#define MSM_DMA_IOFREE _IOW(MSM_DMA_IOC_MAGIC, 3, int) + +/* Free all used buffers. */ +#define MSM_DMA_IOFREEALL _IO(MSM_DMA_IOC_MAGIC, 7) + +/* Read/write data into kernel buffer. */ +struct msm_dma_bufxfer { + void *data; + int size; + int bufnum; +}; +#define MSM_DMA_IOWBUF _IOW(MSM_DMA_IOC_MAGIC, 4, struct msm_dma_bufxfer) +#define MSM_DMA_IORBUF _IOW(MSM_DMA_IOC_MAGIC, 5, struct msm_dma_bufxfer) + +/* Use the data mover to copy from one buffer to another. */ +struct msm_dma_scopy { + int srcbuf; + int destbuf; + int size; +}; +#define MSM_DMA_IOSCOPY _IOW(MSM_DMA_IOC_MAGIC, 6, struct msm_dma_scopy) + +#endif /* __MSM_DMA_TEST__ */ diff --git a/arch/arm/mach-msm/include/mach/entry-macro.S b/arch/arm/mach-msm/include/mach/entry-macro.S index f2ae9087f65..dd1b54d901f 100644 --- a/arch/arm/mach-msm/include/mach/entry-macro.S +++ b/arch/arm/mach-msm/include/mach/entry-macro.S @@ -1,4 +1,5 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,12 +9,6 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * */ #if !defined(CONFIG_ARM_GIC) diff --git a/arch/arm/mach-msm/include/mach/fiq.h b/arch/arm/mach-msm/include/mach/fiq.h new file mode 100644 index 00000000000..29a3ba1f33f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/fiq.h @@ -0,0 +1,33 @@ +/* linux/include/asm-arm/arch-msm/irqs.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_FIQ_H +#define __ASM_ARCH_MSM_FIQ_H + +/* cause an interrupt to be an FIQ instead of a regular IRQ */ +void msm_fiq_select(int number); +void msm_fiq_unselect(int number); + +/* enable/disable an interrupt that is an FIQ (not safe from FIQ context) */ +void msm_fiq_enable(int number); +void msm_fiq_disable(int number); + +/* install an FIQ handler */ +int msm_fiq_set_handler(void (*func)(void *data, void *regs), void *data); + +/* cause an edge triggered interrupt to fire (safe from FIQ context */ +void msm_trigger_irq(int number); + +#endif diff --git a/arch/arm/mach-msm/include/mach/gpio-tlmm-v1.h b/arch/arm/mach-msm/include/mach/gpio-tlmm-v1.h new file mode 100644 index 00000000000..e41fe72d248 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/gpio-tlmm-v1.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ASM_ARCH_MSM_GPIO_TLMM_V1_H +#define __ASM_ARCH_MSM_GPIO_TLMM_V1_H + +/* GPIO TLMM (Top Level Multiplexing) Definitions */ + +/* GPIO TLMM: Function -- GPIO specific */ + +/* GPIO TLMM: Direction */ +enum { + GPIO_CFG_INPUT, + GPIO_CFG_OUTPUT, +}; + +/* GPIO TLMM: Pullup/Pulldown */ +enum { + GPIO_CFG_NO_PULL, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_KEEPER, + GPIO_CFG_PULL_UP, +}; + +/* GPIO TLMM: Drive Strength */ +enum { + GPIO_CFG_2MA, + GPIO_CFG_4MA, + GPIO_CFG_6MA, + GPIO_CFG_8MA, + GPIO_CFG_10MA, + GPIO_CFG_12MA, + GPIO_CFG_14MA, + GPIO_CFG_16MA, +}; + +enum { + GPIO_CFG_ENABLE, + GPIO_CFG_DISABLE, +}; + +#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ + ((((gpio) & 0x3FF) << 4) | \ + ((func) & 0xf) | \ + (((dir) & 0x1) << 14) | \ + (((pull) & 0x3) << 15) | \ + (((drvstr) & 0xF) << 17)) + +/** + * extract GPIO pin from bit-field used for gpio_tlmm_config + */ +#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) +#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) +#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) +#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) +#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) + +int gpio_tlmm_config(unsigned config, unsigned disable); + +#endif diff --git a/arch/arm/mach-msm/include/mach/gpio-v1.h b/arch/arm/mach-msm/include/mach/gpio-v1.h new file mode 100644 index 00000000000..eea4c88f127 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/gpio-v1.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ASM_ARCH_MSM_GPIO_V1_H +#define __ASM_ARCH_MSM_GPIO_V1_H + +#include +#include +#include + +#define FIRST_BOARD_GPIO NR_GPIO_IRQS + +static inline int gpio_get_value(unsigned gpio) +{ + return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + __gpio_set_value(gpio, value); +} + +static inline int gpio_cansleep(unsigned gpio) +{ + return __gpio_cansleep(gpio); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + return __gpio_to_irq(gpio); +} + +void msm_gpio_enter_sleep(int from_idle); +void msm_gpio_exit_sleep(void); + +/** + * struct msm_gpio - GPIO pin description + * @gpio_cfg - configuration bitmap, as per gpio_tlmm_config() + * @label - textual label + * + * Usually, GPIO's are operated by sets. + * This struct accumulate all GPIO information in single source + * and facilitete group operations provided by msm_gpios_xxx() + */ +struct msm_gpio { + u32 gpio_cfg; + const char *label; +}; + +/** + * msm_gpios_request_enable() - request and enable set of GPIOs + * + * Request and configure set of GPIO's + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_request_enable(const struct msm_gpio *table, int size); + +/** + * msm_gpios_disable_free() - disable and free set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_disable_free(const struct msm_gpio *table, int size); + +/** + * msm_gpios_request() - request set of GPIOs + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_request(const struct msm_gpio *table, int size); + +/** + * msm_gpios_free() - free set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_free(const struct msm_gpio *table, int size); + +/** + * msm_gpios_enable() - enable set of GPIOs + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_enable(const struct msm_gpio *table, int size); + +/** + * msm_gpios_disable() - disable set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_disable(const struct msm_gpio *table, int size); + +/* GPIO TLMM (Top Level Multiplexing) Definitions */ + +/* GPIO TLMM: Function -- GPIO specific */ + +/* GPIO TLMM: Direction */ +enum { + GPIO_CFG_INPUT, + GPIO_CFG_OUTPUT, +}; + +/* GPIO TLMM: Pullup/Pulldown */ +enum { + GPIO_CFG_NO_PULL, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_KEEPER, + GPIO_CFG_PULL_UP, +}; + +/* GPIO TLMM: Drive Strength */ +enum { + GPIO_CFG_2MA, + GPIO_CFG_4MA, + GPIO_CFG_6MA, + GPIO_CFG_8MA, + GPIO_CFG_10MA, + GPIO_CFG_12MA, + GPIO_CFG_14MA, + GPIO_CFG_16MA, +}; + +enum { + GPIO_CFG_ENABLE, + GPIO_CFG_DISABLE, +}; + +#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ + ((((gpio) & 0x3FF) << 4) | \ + ((func) & 0xf) | \ + (((dir) & 0x1) << 14) | \ + (((pull) & 0x3) << 15) | \ + (((drvstr) & 0xF) << 17)) + +/** + * extract GPIO pin from bit-field used for gpio_tlmm_config + */ +#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) +#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) +#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) +#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) +#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) + +int gpio_tlmm_config(unsigned config, unsigned disable); + +#endif /* __ASM_ARCH_MSM_GPIO_V1_H */ diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 40a8c178f10..1d320035349 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -1 +1,224 @@ -/* empty */ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ASM_ARCH_MSM_GPIO_H +#define __ASM_ARCH_MSM_GPIO_H + +#define ARCH_NR_GPIOS 512 + +#include +#include +#include + +#define FIRST_BOARD_GPIO NR_GPIO_IRQS + +extern struct irq_chip msm_gpio_irq_extn; + +/** + * struct msm_gpio - GPIO pin description + * @gpio_cfg - configuration bitmap, as per gpio_tlmm_config() + * @label - textual label + * + * Usually, GPIO's are operated by sets. + * This struct accumulate all GPIO information in single source + * and facilitete group operations provided by msm_gpios_xxx() + */ +struct msm_gpio { + u32 gpio_cfg; + const char *label; +}; + +/** + * msm_gpios_request_enable() - request and enable set of GPIOs + * + * Request and configure set of GPIO's + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_request_enable(const struct msm_gpio *table, int size); + +/** + * msm_gpios_disable_free() - disable and free set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_disable_free(const struct msm_gpio *table, int size); + +/** + * msm_gpios_request() - request set of GPIOs + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_request(const struct msm_gpio *table, int size); + +/** + * msm_gpios_free() - free set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_free(const struct msm_gpio *table, int size); + +/** + * msm_gpios_enable() - enable set of GPIOs + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_enable(const struct msm_gpio *table, int size); + +/** + * msm_gpios_disable() - disable set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_disable(const struct msm_gpio *table, int size); + +/** + * msm_gpios_show_resume_irq() - show the interrupts that could have triggered + * resume + */ +void msm_gpio_show_resume_irq(void); + +/* GPIO TLMM (Top Level Multiplexing) Definitions */ + +/* GPIO TLMM: Function -- GPIO specific */ + +/* GPIO TLMM: Direction */ +enum { + GPIO_CFG_INPUT, + GPIO_CFG_OUTPUT, +}; + +/* GPIO TLMM: Pullup/Pulldown */ +enum { + GPIO_CFG_NO_PULL, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_KEEPER, + GPIO_CFG_PULL_UP, +}; + +/* GPIO TLMM: Drive Strength */ +enum { + GPIO_CFG_2MA, + GPIO_CFG_4MA, + GPIO_CFG_6MA, + GPIO_CFG_8MA, + GPIO_CFG_10MA, + GPIO_CFG_12MA, + GPIO_CFG_14MA, + GPIO_CFG_16MA, +}; + +enum { + GPIO_CFG_ENABLE, + GPIO_CFG_DISABLE, +}; + +#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ + ((((gpio) & 0x3FF) << 4) | \ + ((func) & 0xf) | \ + (((dir) & 0x1) << 14) | \ + (((pull) & 0x3) << 15) | \ + (((drvstr) & 0xF) << 17)) + +/** + * extract GPIO pin from bit-field used for gpio_tlmm_config + */ +#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) +#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) +#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) +#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) +#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) + +int gpio_tlmm_config(unsigned config, unsigned disable); + +enum msm_tlmm_hdrive_tgt { + TLMM_HDRV_SDC4_CLK = 0, + TLMM_HDRV_SDC4_CMD, + TLMM_HDRV_SDC4_DATA, + TLMM_HDRV_SDC3_CLK, + TLMM_HDRV_SDC3_CMD, + TLMM_HDRV_SDC3_DATA, + TLMM_HDRV_SDC1_CLK, + TLMM_HDRV_SDC1_CMD, + TLMM_HDRV_SDC1_DATA, +}; + +enum msm_tlmm_pull_tgt { + TLMM_PULL_SDC4_CMD = 0, + TLMM_PULL_SDC4_DATA, + TLMM_PULL_SDC3_CLK, + TLMM_PULL_SDC3_CMD, + TLMM_PULL_SDC3_DATA, + TLMM_PULL_SDC1_CLK, + TLMM_PULL_SDC1_CMD, + TLMM_PULL_SDC1_DATA, +}; + +#if defined(CONFIG_GPIO_MSM_V2) || defined(CONFIG_GPIO_MSM_V3) +void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str); +void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull); + +/* + * A GPIO can be set as a direct-connect IRQ. This can be used to bypass + * the normal summary-interrupt mechanism for those GPIO lines deemed to be + * higher priority or otherwise worthy of special treatment, but resources + * are limited: only a few DC interrupt lines are available. + * Care must be taken when usurping a GPIO in this manner, as the summary + * interrupt controller has no idea that the GPIO has been taken away from it. + * Clients can still register to receive the summary interrupt assigned + * to that GPIO, which will uninstall it as a direct connect IRQ with + * no warning. + * + * The irq passed to this function is the DC IRQ number, not the + * irq number seen by the scorpion when the interrupt triggers. For example, + * if 0 is specified, then when DC IRQ 0 triggers, the scorpion will see + * interrupt TLMM_MSM_DIR_CONN_IRQ_0. + * + * input_polarity parameter specifies when the gpio should raise the direct + * interrupt. A value of 0 means that it is active low, anything else means + * active high + * + */ +int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq, + unsigned int input_polarity); +#else +static inline void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, + int drv_str) {} +static inline void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull) {} +static inline int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq, + unsigned int input_polarity) +{ + return -ENOSYS; +} +#endif + +#ifdef CONFIG_OF +int __init msm_gpio_of_init(struct device_node *node, + struct device_node *parent); +#endif + +#endif /* __ASM_ARCH_MSM_GPIO_H */ diff --git a/arch/arm/mach-msm/include/mach/gpiomux.h b/arch/arm/mach-msm/include/mach/gpiomux.h new file mode 100644 index 00000000000..f75b0e0c847 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/gpiomux.h @@ -0,0 +1,177 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_H +#define __ARCH_ARM_MACH_MSM_GPIOMUX_H + +#include +#include + +enum msm_gpiomux_setting { + GPIOMUX_ACTIVE = 0, + GPIOMUX_SUSPENDED, + GPIOMUX_NSETTINGS +}; + +enum gpiomux_drv { + GPIOMUX_DRV_2MA = 0, + GPIOMUX_DRV_4MA, + GPIOMUX_DRV_6MA, + GPIOMUX_DRV_8MA, + GPIOMUX_DRV_10MA, + GPIOMUX_DRV_12MA, + GPIOMUX_DRV_14MA, + GPIOMUX_DRV_16MA, +}; + +enum gpiomux_func { + GPIOMUX_FUNC_GPIO = 0, + GPIOMUX_FUNC_1, + GPIOMUX_FUNC_2, + GPIOMUX_FUNC_3, + GPIOMUX_FUNC_4, + GPIOMUX_FUNC_5, + GPIOMUX_FUNC_6, + GPIOMUX_FUNC_7, + GPIOMUX_FUNC_8, + GPIOMUX_FUNC_9, + GPIOMUX_FUNC_A, + GPIOMUX_FUNC_B, + GPIOMUX_FUNC_C, + GPIOMUX_FUNC_D, + GPIOMUX_FUNC_E, + GPIOMUX_FUNC_F, +}; + +enum gpiomux_pull { + GPIOMUX_PULL_NONE = 0, + GPIOMUX_PULL_DOWN, + GPIOMUX_PULL_KEEPER, + GPIOMUX_PULL_UP, +}; + +/* Direction settings are only meaningful when GPIOMUX_FUNC_GPIO is selected. + * This element is ignored for all other FUNC selections, as the output- + * enable pin is not under software control in those cases. See the SWI + * for your target for more details. + */ +enum gpiomux_dir { + GPIOMUX_IN = 0, + GPIOMUX_OUT_HIGH, + GPIOMUX_OUT_LOW, +}; + +struct gpiomux_setting { + enum gpiomux_func func; + enum gpiomux_drv drv; + enum gpiomux_pull pull; + enum gpiomux_dir dir; +}; + +/** + * struct msm_gpiomux_config: gpiomux settings for one gpio line. + * + * A complete gpiomux config is the combination of a drive-strength, + * function, pull, and (sometimes) direction. For functions other than GPIO, + * the input/output setting is hard-wired according to the function. + * + * @gpio: The index number of the gpio being described. + * @settings: The settings to be installed, specifically: + * GPIOMUX_ACTIVE: The setting to be installed when the + * line is active, or its reference count is > 0. + * GPIOMUX_SUSPENDED: The setting to be installed when + * the line is suspended, or its reference count is 0. + */ +struct msm_gpiomux_config { + unsigned gpio; + struct gpiomux_setting *settings[GPIOMUX_NSETTINGS]; +}; + +/** + * struct msm_gpiomux_configs: a collection of gpiomux configs. + * + * It is so common to manage blocks of gpiomux configs that the data structure + * for doing so has been standardized here as a convenience. + * + * @cfg: A pointer to the first config in an array of configs. + * @ncfg: The number of configs in the array. + */ +struct msm_gpiomux_configs { + struct msm_gpiomux_config *cfg; + size_t ncfg; +}; + +#ifdef CONFIG_MSM_GPIOMUX + +/* Before using gpiomux, initialize the subsystem by telling it how many + * gpios are going to be managed. Calling any other gpiomux functions before + * msm_gpiomux_init is unsupported. + */ +int msm_gpiomux_init(size_t ngpio); + +/* Install a block of gpiomux configurations in gpiomux. This is functionally + * identical to calling msm_gpiomux_write many times. + */ +void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs); + +/* Increment a gpio's reference count, possibly activating the line. */ +int __must_check msm_gpiomux_get(unsigned gpio); + +/* Decrement a gpio's reference count, possibly suspending the line. */ +int msm_gpiomux_put(unsigned gpio); + +/* Install a new setting in a gpio. To erase a slot, use NULL. + * The old setting that was overwritten can be passed back to the caller + * old_setting can be NULL if the caller is not interested in the previous + * setting + * If a previous setting was not available to return (NULL configuration) + * - the function returns 1 + * else function returns 0 + */ +int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which, + struct gpiomux_setting *setting, struct gpiomux_setting *old_setting); + +/* Architecture-internal function for use by the framework only. + * This function can assume the following: + * - the gpio value has passed a bounds-check + * - the gpiomux spinlock has been obtained + * + * This function is not for public consumption. External users + * should use msm_gpiomux_write. + */ +void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val); +#else +static inline int msm_gpiomux_init(size_t ngpio) +{ + return -ENOSYS; +} + +static inline void +msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs) {} + +static inline int __must_check msm_gpiomux_get(unsigned gpio) +{ + return -ENOSYS; +} + +static inline int msm_gpiomux_put(unsigned gpio) +{ + return -ENOSYS; +} + +static inline int msm_gpiomux_write(unsigned gpio, + enum msm_gpiomux_setting which, struct gpiomux_setting *setting, + struct gpiomux_setting *old_setting) +{ + return -ENOSYS; +} +#endif +#endif diff --git a/arch/arm/mach-msm/include/mach/hardware.h b/arch/arm/mach-msm/include/mach/hardware.h index 2d126091ae4..7b7cbaa6566 100644 --- a/arch/arm/mach-msm/include/mach/hardware.h +++ b/arch/arm/mach-msm/include/mach/hardware.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/hardware.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -14,5 +15,8 @@ */ #ifndef __ASM_ARCH_MSM_HARDWARE_H +#define __ASM_ARCH_MSM_HARDWARE_H + +#define pcibios_assign_all_busses() 1 #endif diff --git a/arch/arm/mach-msm/include/mach/htc_35mm_jack.h b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h new file mode 100644 index 00000000000..5ce1e2a1e47 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h @@ -0,0 +1,31 @@ +/* arch/arm/mach-msm/include/mach/htc_35mm_jack.h + * + * Copyright (C) 2009 HTC, Inc. + * Author: Arec Kao + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef HTC_35MM_REMOTE_H +#define HTC_35MM_REMOTE_H + +/* Driver interfaces */ +int htc_35mm_jack_plug_event(int insert, int *hpin_stable); +int htc_35mm_key_event(int key, int *hpin_stable); + +/* Platform Specific Callbacks */ +struct h35mm_platform_data { + int (*plug_event_enable)(void); + int (*headset_has_mic)(void); + int (*key_event_enable)(void); + int (*key_event_disable)(void); +}; +#endif diff --git a/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h new file mode 100644 index 00000000000..2139bf9f903 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h @@ -0,0 +1,29 @@ +/* include/asm/mach-msm/htc_acoustic_qsd.h + * + * Copyright (C) 2009 HTC Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _ARCH_ARM_MACH_MSM_HTC_ACOUSTIC_QSD_H_ +#define _ARCH_ARM_MACH_MSM_HTC_ACOUSTIC_QSD_H_ + +struct qsd_acoustic_ops { + void (*enable_mic_bias)(int en); +}; + +void acoustic_register_ops(struct qsd_acoustic_ops *ops); + +int turn_mic_bias_on(int on); +int force_headset_speaker_on(int enable); +int enable_aux_loopback(uint32_t enable); + +#endif + diff --git a/arch/arm/mach-msm/include/mach/htc_headset.h b/arch/arm/mach-msm/include/mach/htc_headset.h new file mode 100644 index 00000000000..2f4c18db262 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_headset.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008 HTC, Inc. + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_HTC_HEADSET_H +#define __ASM_ARCH_HTC_HEADSET_H + +struct h2w_platform_data { + char *power_name; + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + void (*config_cpld)(int); + void (*init_cpld)(void); + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); +}; + +#define BIT_HEADSET (1 << 0) +#define BIT_HEADSET_NO_MIC (1 << 1) +#define BIT_TTY (1 << 2) +#define BIT_FM_HEADSET (1 << 3) +#define BIT_FM_SPEAKER (1 << 4) + +enum { + H2W_NO_DEVICE = 0, + H2W_HTC_HEADSET = 1, +/* H2W_TTY_DEVICE = 2,*/ + NORMAL_HEARPHONE= 2, + H2W_DEVICE = 3, + H2W_USB_CRADLE = 4, + H2W_UART_DEBUG = 5, +}; + +enum { + H2W_GPIO = 0, + H2W_UART1 = 1, + H2W_UART3 = 2, + H2W_BT = 3 +}; + +#define RESEND_DELAY (3) /* ms */ +#define MAX_ACK_RESEND_TIMES (6) /* follow spec */ +#define MAX_HOST_RESEND_TIMES (3) /* follow spec */ +#define MAX_HYGEIA_RESEND_TIMES (5) + +#define H2W_ASCR_DEVICE_INI (0x01) +#define H2W_ASCR_ACT_EN (0x02) +#define H2W_ASCR_PHONE_IN (0x04) +#define H2W_ASCR_RESET (0x08) +#define H2W_ASCR_AUDIO_IN (0x10) + +#define H2W_LED_OFF (0x0) +#define H2W_LED_BKL (0x1) +#define H2W_LED_MTL (0x2) + +typedef enum { + /* === system group 0x0000~0x00FF === */ + /* (R) Accessory type register */ + H2W_SYSTEM = 0x0000, + /* (R) Maximum group address */ + H2W_MAX_GP_ADD = 0x0001, + /* (R/W) Accessory system control register0 */ + H2W_ASCR0 = 0x0002, + + /* === key group 0x0100~0x01FF === */ + /* (R) Key group maximum sub address */ + H2W_KEY_MAXADD = 0x0100, + /* (R) ASCII key press down flag */ + H2W_ASCII_DOWN = 0x0101, + /* (R) ASCII key release up flag */ + H2W_ASCII_UP = 0x0102, + /* (R) Function key status flag */ + H2W_FNKEY_UPDOWN = 0x0103, + /* (R/W) Key device status */ + H2W_KD_STATUS = 0x0104, + + /* === led group 0x0200~0x02FF === */ + /* (R) LED group maximum sub address */ + H2W_LED_MAXADD = 0x0200, + /* (R/W) LED control register0 */ + H2W_LEDCT0 = 0x0201, + + /* === crdl group 0x0300~0x03FF === */ + /* (R) Cardle group maximum sub address */ + H2W_CRDL_MAXADD = 0x0300, + /* (R/W) Cardle group function control register0 */ + H2W_CRDLCT0 = 0x0301, + + /* === car kit group 0x0400~0x04FF === */ + H2W_CARKIT_MAXADD = 0x0400, + + /* === usb host group 0x0500~0x05FF === */ + H2W_USBHOST_MAXADD = 0x0500, + + /* === medical group 0x0600~0x06FF === */ + H2W_MED_MAXADD = 0x0600, + H2W_MED_CONTROL = 0x0601, + H2W_MED_IN_DATA = 0x0602, +} H2W_ADDR; + + +typedef struct H2W_INFO { + /* system group */ + unsigned char CLK_SP; + int SLEEP_PR; + unsigned char HW_REV; + int AUDIO_DEVICE; + unsigned char ACC_CLASS; + unsigned char MAX_GP_ADD; + + /* key group */ + int KEY_MAXADD; + int ASCII_DOWN; + int ASCII_UP; + int FNKEY_UPDOWN; + int KD_STATUS; + + /* led group */ + int LED_MAXADD; + int LEDCT0; + + /* medical group */ + int MED_MAXADD; + unsigned char AP_ID; + unsigned char AP_EN; + unsigned char DATA_EN; +} H2W_INFO; + +typedef enum { + H2W_500KHz = 1, + H2W_250KHz = 2, + H2W_166KHz = 3, + H2W_125KHz = 4, + H2W_100KHz = 5, + H2W_83KHz = 6, + H2W_71KHz = 7, + H2W_62KHz = 8, + H2W_55KHz = 9, + H2W_50KHz = 10, +} H2W_SPEED; + +typedef enum { + H2W_KEY_INVALID = -1, + H2W_KEY_PLAY = 0, + H2W_KEY_FORWARD = 1, + H2W_KEY_BACKWARD = 2, + H2W_KEY_VOLUP = 3, + H2W_KEY_VOLDOWN = 4, + H2W_KEY_PICKUP = 5, + H2W_KEY_HANGUP = 6, + H2W_KEY_MUTE = 7, + H2W_KEY_HOLD = 8, + H2W_NUM_KEYFUNC = 9, +} KEYFUNC; +#endif diff --git a/arch/arm/mach-msm/include/mach/htc_pwrsink.h b/arch/arm/mach-msm/include/mach/htc_pwrsink.h new file mode 100644 index 00000000000..c7a91f1d906 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_pwrsink.h @@ -0,0 +1,87 @@ +/* include/asm/mach-msm/htc_pwrsink.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2008 HTC Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_ +#define _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_ + +#include +#include + +typedef enum { + PWRSINK_AUDIO_PCM = 0, + PWRSINK_AUDIO_MP3, + PWRSINK_AUDIO_AAC, + + PWRSINK_AUDIO_LAST = PWRSINK_AUDIO_AAC, + PWRSINK_AUDIO_INVALID +} pwrsink_audio_id_type; + +struct pwr_sink_audio { + unsigned volume; + unsigned percent; +}; + +typedef enum { + PWRSINK_SYSTEM_LOAD = 0, + PWRSINK_AUDIO, + PWRSINK_BACKLIGHT, + PWRSINK_LED_BUTTON, + PWRSINK_LED_KEYBOARD, + PWRSINK_GP_CLK, + PWRSINK_BLUETOOTH, + PWRSINK_CAMERA, + PWRSINK_SDCARD, + PWRSINK_VIDEO, + PWRSINK_WIFI, + + PWRSINK_LAST = PWRSINK_WIFI, + PWRSINK_INVALID +} pwrsink_id_type; + +struct pwr_sink { + pwrsink_id_type id; + unsigned ua_max; + unsigned percent_util; +}; + +struct pwr_sink_platform_data { + unsigned num_sinks; + struct pwr_sink *sinks; + int (*suspend_late)(struct platform_device *, pm_message_t state); + int (*resume_early)(struct platform_device *); + void (*suspend_early)(struct early_suspend *); + void (*resume_late)(struct early_suspend *); +}; + +#ifndef CONFIG_HTC_PWRSINK +static inline int htc_pwrsink_set(pwrsink_id_type id, unsigned percent) +{ + return 0; +} +static inline int htc_pwrsink_audio_set(pwrsink_audio_id_type id, + unsigned percent_utilized) { return 0; } +static inline int htc_pwrsink_audio_volume_set( + pwrsink_audio_id_type id, unsigned volume) { return 0; } +static inline int htc_pwrsink_audio_path_set(unsigned path) { return 0; } +#else +extern int htc_pwrsink_set(pwrsink_id_type id, unsigned percent); +extern int htc_pwrsink_audio_set(pwrsink_audio_id_type id, + unsigned percent_utilized); +extern int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, + unsigned volume); +extern int htc_pwrsink_audio_path_set(unsigned path); +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/io.h b/arch/arm/mach-msm/include/mach/io.h new file mode 100644 index 00000000000..445e17567b1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/io.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +#define __io(a) __typesafe_io(a) +#define __mem_pci(a) (a) + +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs-7x00.h b/arch/arm/mach-msm/include/mach/irqs-7x00.h index f1fe70612fe..a8e1da2d62d 100644 --- a/arch/arm/mach-msm/include/mach/irqs-7x00.h +++ b/arch/arm/mach-msm/include/mach/irqs-7x00.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. * Author: Brian Swetland */ @@ -71,5 +71,7 @@ #define NR_MSM_IRQS 64 #define NR_GPIO_IRQS 122 #define NR_BOARD_IRQS 64 +#define NR_SIRC_IRQS 0 +#define INT_ADSP_A11_SMSM INT_ADSP_A11 #endif diff --git a/arch/arm/mach-msm/include/mach/irqs-7x30.h b/arch/arm/mach-msm/include/mach/irqs-7x30.h index 1f15902655f..a624bbf6051 100644 --- a/arch/arm/mach-msm/include/mach/irqs-7x30.h +++ b/arch/arm/mach-msm/include/mach/irqs-7x30.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,7 +24,7 @@ #define INT_AVS_SVIC_SW_DONE 6 #define INT_SC_DBG_RX_FULL 7 #define INT_SC_DBG_TX_EMPTY 8 -#define INT_ARM11_PM 9 +#define INT_ARMQC_PERFMON 9 #define INT_AVS_REQ_DOWN 10 #define INT_AVS_REQ_UP 11 #define INT_SC_ACG 12 @@ -131,8 +131,8 @@ #define INT_TCHSCRN1 INT_TSSC_SAMPLE #define INT_TCHSCRN2 INT_TSSC_PENUP #define INT_GP_TIMER_EXP INT_GPT0_TIMER_EXP -#define INT_ADSP_A11 INT_AD5A_MPROC_APPS_0 -#define INT_ADSP_A9_A11 INT_AD5A_MPROC_APPS_1 +#define INT_ADSP_A9_A11 INT_AD5A_MPROC_APPS_0 +#define INT_ADSP_A11 INT_AD5A_MPROC_APPS_1 #define INT_MDDI_EXT INT_EMDH #define INT_MDDI_PRI INT_PMDH #define INT_MDDI_CLIENT INT_MDC @@ -142,12 +142,9 @@ #define NR_MSM_IRQS 128 #define NR_GPIO_IRQS 182 #define PMIC8058_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) -#define NR_PMIC8058_GPIO_IRQS 40 -#define NR_PMIC8058_MPP_IRQS 12 -#define NR_PMIC8058_MISC_IRQS 8 -#define NR_PMIC8058_IRQS (NR_PMIC8058_GPIO_IRQS +\ - NR_PMIC8058_MPP_IRQS +\ - NR_PMIC8058_MISC_IRQS) +#define NR_PMIC8058_IRQS 256 #define NR_BOARD_IRQS NR_PMIC8058_IRQS +#define INT_ADSP_A11_SMSM INT_ADSP_A11 + #endif /* __ASM_ARCH_MSM_IRQS_7X30_H */ diff --git a/arch/arm/mach-msm/include/mach/irqs-7xxx.h b/arch/arm/mach-msm/include/mach/irqs-7xxx.h new file mode 100644 index 00000000000..c90b4ee0fd1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-7xxx.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + */ + +#ifndef __ASM_ARCH_MSM_IRQS_7XXX_H +#define __ASM_ARCH_MSM_IRQS_7XXX_H + +/* MSM ARM11 Interrupt Numbers */ +/* See 80-VE113-1 A, pp219-221 */ + +#define INT_A9_M2A_0 0 +#define INT_A9_M2A_1 1 +#define INT_A9_M2A_2 2 +#define INT_A9_M2A_3 3 +#define INT_A9_M2A_4 4 +#define INT_A9_M2A_5 5 +#define INT_A9_M2A_6 6 +#define INT_GP_TIMER_EXP 7 +#define INT_DEBUG_TIMER_EXP 8 +#define INT_UART1 9 +#define INT_UART2 10 +#define INT_UART3 11 +#define INT_UART1_RX 12 +#define INT_UART2_RX 13 +#define INT_UART3_RX 14 +#define INT_USB_OTG 15 +#if defined(CONFIG_ARCH_MSM7X27A) +#define INT_DSI_IRQ 16 +#define INT_CSI_IRQ_1 17 +#define INT_CSI_IRQ_0 18 +#else +#define INT_MDDI_PRI 16 +#define INT_MDDI_EXT 17 +#define INT_MDDI_CLIENT 18 +#endif +#define INT_MDP 19 +#define INT_GRAPHICS 20 +#define INT_ADM_AARM 21 +#define INT_ADSP_A11 22 +#define INT_ADSP_A9_A11 23 +#define INT_SDC1_0 24 +#define INT_SDC1_1 25 +#define INT_SDC2_0 26 +#define INT_SDC2_1 27 +#define INT_KEYSENSE 28 +#define INT_TCHSCRN_SSBI 29 +#define INT_TCHSCRN1 30 +#define INT_TCHSCRN2 31 + +#define INT_GPIO_GROUP1 (32 + 0) +#define INT_GPIO_GROUP2 (32 + 1) +#define INT_PWB_I2C (32 + 2) +#define INT_SOFTRESET (32 + 3) +#define INT_NAND_WR_ER_DONE (32 + 4) +#define INT_NAND_OP_DONE (32 + 5) +#define INT_PBUS_ARM11 (32 + 6) +#define INT_AXI_MPU_SMI (32 + 7) +#define INT_AXI_MPU_EBI1 (32 + 8) +#define INT_AD_HSSD (32 + 9) +#define INT_ARMQC_PERFMON (32 + 10) +#define INT_ARM11_DMA (32 + 11) +#define INT_TSIF_IRQ (32 + 12) +#define INT_UART1DM_IRQ (32 + 13) +#define INT_UART1DM_RX (32 + 14) +#define INT_USB_HS (32 + 15) +#define INT_SDC3_0 (32 + 16) +#define INT_SDC3_1 (32 + 17) +#define INT_SDC4_0 (32 + 18) +#define INT_SDC4_1 (32 + 19) +#define INT_UART2DM_IRQ (32 + 20) +#define INT_UART2DM_RX (32 + 21) + +/* 22-31 are reserved except 7x27a*/ +#if defined(CONFIG_ARCH_MSM7X27A) +#define INT_L2CC_EM (32 + 22) +#define INT_L2CC_INTR (32 + 23) +#define INT_CE_IRQ (32 + 24) +#endif + +#define INT_ADSP_A11_SMSM INT_ADSP_A11 +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs-8064.h b/arch/arm/mach-msm/include/mach/irqs-8064.h new file mode 100644 index 00000000000..a5f78f5ccaa --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-8064.h @@ -0,0 +1,312 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IRQS_8064_H +#define __ASM_ARCH_MSM_IRQS_8064_H + +/* MSM ACPU Interrupt Numbers */ + +/* + * 0-15: STI/SGI (software triggered/generated interrupts) + * 16-31: PPI (private peripheral interrupts) + * 32+: SPI (shared peripheral interrupts) + */ + +#define GIC_PPI_START 16 +#define GIC_SPI_START 32 + +#define INT_VGIC (GIC_PPI_START + 0) +#define INT_DEBUG_TIMER_EXP (GIC_PPI_START + 1) +#define INT_GP_TIMER_EXP (GIC_PPI_START + 2) +#define INT_GP_TIMER2_EXP (GIC_PPI_START + 3) +#define WDT0_ACCSCSSNBARK_INT (GIC_PPI_START + 4) +#define WDT1_ACCSCSSNBARK_INT (GIC_PPI_START + 5) +#define AVS_SVICINT (GIC_PPI_START + 6) +#define AVS_SVICINTSWDONE (GIC_PPI_START + 7) +#define CPU_DBGCPUXCOMMRXFULL (GIC_PPI_START + 8) +#define CPU_DBGCPUXCOMMTXEMPTY (GIC_PPI_START + 9) +#define INT_ARMQC_PERFMON (GIC_PPI_START + 10) +#define SC_AVSCPUXDOWN (GIC_PPI_START + 11) +#define SC_AVSCPUXUP (GIC_PPI_START + 12) +#define SC_SICCPUXACGIRPTREQ (GIC_PPI_START + 13) +#define SC_SICCPUXEXTFAULTIRPTREQ (GIC_PPI_START + 14) +/* PPI 15 is unused */ + +#define APCC_QGICACGIRPTREQ (GIC_SPI_START + 0) +#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1) +#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ +#define APCC_QGICL2IRPTREQ (GIC_SPI_START + 2) +#define APCC_QGICMPUIRPTREQ (GIC_SPI_START + 3) +#define TLMM_MSM_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) +#define TLMM_MSM_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) +#define TLMM_MSM_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) +#define TLMM_MSM_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) +#define TLMM_MSM_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) +#define TLMM_MSM_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) +#define TLMM_MSM_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) +#define TLMM_MSM_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) +#define TLMM_MSM_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) +#define TLMM_MSM_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) +#define PM8921_SEC_IRQ_N (GIC_SPI_START + 14) +#define PM8821_SEC_IRQ_N (GIC_SPI_START + 15) +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 16) +#define SPDM_RT_1_IRQ (GIC_SPI_START + 17) +#define SPDM_DIAG_IRQ (GIC_SPI_START + 18) +#define RPM_APCC_CPU0_GP_HIGH_IRQ (GIC_SPI_START + 19) +#define RPM_APCC_CPU0_GP_MEDIUM_IRQ (GIC_SPI_START + 20) +#define RPM_APCC_CPU0_GP_LOW_IRQ (GIC_SPI_START + 21) +#define RPM_APCC_CPU0_WAKE_UP_IRQ (GIC_SPI_START + 22) +#define RPM_APCC_CPU1_GP_HIGH_IRQ (GIC_SPI_START + 23) +#define RPM_APCC_CPU1_GP_MEDIUM_IRQ (GIC_SPI_START + 24) +#define RPM_APCC_CPU1_GP_LOW_IRQ (GIC_SPI_START + 25) +#define RPM_APCC_CPU1_WAKE_UP_IRQ (GIC_SPI_START + 26) +#define SSBI2_2_SC_CPU0_SECURE_IRQ (GIC_SPI_START + 27) +#define SSBI2_2_SC_CPU0_NON_SECURE_IRQ (GIC_SPI_START + 28) +#define SSBI2_1_SC_CPU0_SECURE_IRQ (GIC_SPI_START + 29) +#define SSBI2_1_SC_CPU0_NON_SECURE_IRQ (GIC_SPI_START + 30) +#define MSMC_SC_SEC_CE_IRQ (GIC_SPI_START + 31) +#define MSMC_SC_PRI_CE_IRQ (GIC_SPI_START + 32) +#define SLIMBUS0_CORE_EE1_IRQ (GIC_SPI_START + 33) +#define SLIMBUS0_BAM_EE1_IRQ (GIC_SPI_START + 34) +#define KPSS_SPARE_0 (GIC_SPI_START + 35) +#define GSS_A5_WDOG_EXPIRED (GIC_SPI_START + 36) +#define GSS_TO_APPS_IRQ_0 (GIC_SPI_START + 37) +#define GSS_TO_APPS_IRQ_1 (GIC_SPI_START + 38) +#define GSS_TO_APPS_IRQ_2 (GIC_SPI_START + 39) +#define GSS_TO_APPS_IRQ_3 (GIC_SPI_START + 40) +#define GSS_TO_APPS_IRQ_4 (GIC_SPI_START + 41) +#define GSS_TO_APPS_IRQ_5 (GIC_SPI_START + 42) +#define GSS_TO_APPS_IRQ_6 (GIC_SPI_START + 43) +#define GSS_TO_APPS_IRQ_7 (GIC_SPI_START + 44) +#define GSS_TO_APPS_IRQ_8 (GIC_SPI_START + 45) +#define GSS_TO_APPS_IRQ_9 (GIC_SPI_START + 46) +#define VPE_IRQ (GIC_SPI_START + 47) +#define VFE_IRQ (GIC_SPI_START + 48) +#define VCODEC_IRQ (GIC_SPI_START + 49) +#define KPSS_SPARE_1 (GIC_SPI_START + 50) +#define SMMU_VPE_CB_SC_SECURE_IRQ (GIC_SPI_START + 51) +#define SMMU_VPE_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 52) +#define SMMU_VFE_CB_SC_SECURE_IRQ (GIC_SPI_START + 53) +#define SMMU_VFE_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 54) +#define SMMU_VCODEC_B_CB_SC_SECURE_IRQ (GIC_SPI_START + 55) +#define SMMU_VCODEC_B_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 56) +#define SMMU_VCODEC_A_CB_SC_SECURE_IRQ (GIC_SPI_START + 57) +#define SMMU_VCODEC_A_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 58) +#define SMMU_ROT_CB_SC_SECURE_IRQ (GIC_SPI_START + 59) +#define SMMU_ROT_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 60) +#define SMMU_MDP1_CB_SC_SECURE_IRQ (GIC_SPI_START + 61) +#define SMMU_MDP1_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 62) +#define SMMU_MDP0_CB_SC_SECURE_IRQ (GIC_SPI_START + 63) +#define SMMU_MDP0_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 64) +#define SMMU_JPEGD_CB_SC_SECURE_IRQ (GIC_SPI_START + 65) +#define SMMU_JPEGD_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 66) +#define SMMU_IJPEG_CB_SC_SECURE_IRQ (GIC_SPI_START + 67) +#define SMMU_IJPEG_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 68) +#define SMMU_GFX3D_CB_SC_SECURE_IRQ (GIC_SPI_START + 69) +#define SMMU_GFX3D_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 70) +#define VCAP_VP (GIC_SPI_START + 71) +#define VCAP_VC (GIC_SPI_START + 72) +#define ROT_IRQ (GIC_SPI_START + 73) +#define MMSS_FABRIC_IRQ (GIC_SPI_START + 74) +#define MDP_IRQ (GIC_SPI_START + 75) +#define JPEGD_IRQ (GIC_SPI_START + 76) +#define JPEG_IRQ (GIC_SPI_START + 77) +#define MMSS_IMEM_IRQ (GIC_SPI_START + 78) +#define HDMI_IRQ (GIC_SPI_START + 79) +#define GFX3D_IRQ (GIC_SPI_START + 80) +#define GFX3d_VBIF_IRQ (GIC_SPI_START + 81) +#define DSI1_IRQ (GIC_SPI_START + 82) +#define CSI_1_IRQ (GIC_SPI_START + 83) +#define CSI_0_IRQ (GIC_SPI_START + 84) +#define LPASS_SCSS_AUDIO_IF_OUT0_IRQ (GIC_SPI_START + 85) +#define LPASS_SCSS_MIDI_IRQ (GIC_SPI_START + 86) +#define LPASS_Q6SS_WDOG_EXPIRED (GIC_SPI_START + 87) +#define LPASS_SCSS_GP_LOW_IRQ (GIC_SPI_START + 88) +#define LPASS_SCSS_GP_MEDIUM_IRQ (GIC_SPI_START + 89) +#define LPASS_SCSS_GP_HIGH_IRQ (GIC_SPI_START + 90) +#define TOP_IMEM_IRQ (GIC_SPI_START + 91) +#define FABRIC_SYS_IRQ (GIC_SPI_START + 92) +#define FABRIC_APPS_IRQ (GIC_SPI_START + 93) +#define USB1_HS_BAM_IRQ (GIC_SPI_START + 94) +#define SDC4_BAM_IRQ (GIC_SPI_START + 95) +#define SDC3_BAM_IRQ (GIC_SPI_START + 96) +#define SDC2_BAM_IRQ (GIC_SPI_START + 97) +#define SDC1_BAM_IRQ (GIC_SPI_START + 98) +#define FABRIC_SPS_IRQ (GIC_SPI_START + 99) +#define USB1_HS_IRQ (GIC_SPI_START + 100) +#define SDC4_IRQ_0 (GIC_SPI_START + 101) +#define SDC3_IRQ_0 (GIC_SPI_START + 102) +#define SDC2_IRQ_0 (GIC_SPI_START + 103) +#define SDC1_IRQ_0 (GIC_SPI_START + 104) +#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105) +#define SPS_SEC_VIOL_IRQ (GIC_SPI_START + 106) +#define SPS_MTI_0 (GIC_SPI_START + 107) +#define SPS_MTI_1 (GIC_SPI_START + 108) +#define SPS_MTI_2 (GIC_SPI_START + 109) +#define SPS_MTI_3 (GIC_SPI_START + 110) +#define SPS_MTI_4 (GIC_SPI_START + 111) +#define SPS_MTI_5 (GIC_SPI_START + 112) +#define SPS_MTI_6 (GIC_SPI_START + 113) +#define SPS_MTI_7 (GIC_SPI_START + 114) +#define SPS_MTI_8 (GIC_SPI_START + 115) +#define SPS_MTI_9 (GIC_SPI_START + 116) +#define SPS_MTI_10 (GIC_SPI_START + 117) +#define SPS_MTI_11 (GIC_SPI_START + 118) +#define SPS_MTI_12 (GIC_SPI_START + 119) +#define SPS_MTI_13 (GIC_SPI_START + 120) +#define SPS_MTI_14 (GIC_SPI_START + 121) +#define SPS_MTI_15 (GIC_SPI_START + 122) +#define SPS_MTI_16 (GIC_SPI_START + 123) +#define SPS_MTI_17 (GIC_SPI_START + 124) +#define SPS_MTI_18 (GIC_SPI_START + 125) +#define SPS_MTI_19 (GIC_SPI_START + 126) +#define SPS_MTI_20 (GIC_SPI_START + 127) +#define SPS_MTI_21 (GIC_SPI_START + 128) +#define SPS_MTI_22 (GIC_SPI_START + 129) +#define SPS_MTI_23 (GIC_SPI_START + 130) +#define SPS_MTI_24 (GIC_SPI_START + 131) +#define SPS_MTI_25 (GIC_SPI_START + 132) +#define SPS_MTI_26 (GIC_SPI_START + 133) +#define SPS_MTI_27 (GIC_SPI_START + 134) +#define SPS_MTI_28 (GIC_SPI_START + 135) +#define SPS_MTI_29 (GIC_SPI_START + 136) +#define SPS_MTI_30 (GIC_SPI_START + 137) +#define SPS_MTI_31 (GIC_SPI_START + 138) +#define CSIPHY_0_4LN_IRQ (GIC_SPI_START + 139) +#define CSIPHY_1_2LN_IRQ (GIC_SPI_START + 140) +#define KPSS_SPARE_2 (GIC_SPI_START + 141) +#define USB1_IRQ (GIC_SPI_START + 142) +#define TSSC_SSBI_IRQ (GIC_SPI_START + 143) +#define TSSC_SAMPLE_IRQ (GIC_SPI_START + 144) +#define TSSC_PENUP_IRQ (GIC_SPI_START + 145) +#define KPSS_SPARE_3 (GIC_SPI_START + 146) +#define KPSS_SPARE_4 (GIC_SPI_START + 147) +#define KPSS_SPARE_5 (GIC_SPI_START + 148) +#define KPSS_SPARE_6 (GIC_SPI_START + 149) +#define GSBI3_UARTDM_IRQ (GIC_SPI_START + 150) +#define GSBI3_QUP_IRQ (GIC_SPI_START + 151) +#define GSBI4_UARTDM_IRQ (GIC_SPI_START + 152) +#define GSBI4_QUP_IRQ (GIC_SPI_START + 153) +#define GSBI5_UARTDM_IRQ (GIC_SPI_START + 154) +#define GSBI5_QUP_IRQ (GIC_SPI_START + 155) +#define GSBI6_UARTDM_IRQ (GIC_SPI_START + 156) +#define GSBI6_QUP_IRQ (GIC_SPI_START + 157) +#define GSBI7_UARTDM_IRQ (GIC_SPI_START + 158) +#define GSBI7_QUP_IRQ (GIC_SPI_START + 159) +#define KPSS_SPARE_7 (GIC_SPI_START + 160) +#define KPSS_SPARE_8 (GIC_SPI_START + 161) +#define TSIF_TSPP_IRQ (GIC_SPI_START + 162) +#define TSIF_BAM_IRQ (GIC_SPI_START + 163) +#define TSIF2_IRQ (GIC_SPI_START + 164) +#define TSIF1_IRQ (GIC_SPI_START + 165) +#define DSI2_IRQ (GIC_SPI_START + 166) +#define ISPIF_IRQ (GIC_SPI_START + 167) +#define MSMC_SC_SEC_TMR_IRQ (GIC_SPI_START + 168) +#define MSMC_SC_SEC_WDOG_BARK_IRQ (GIC_SPI_START + 169) +#define ADM_0_SCSS_0_IRQ (GIC_SPI_START + 170) +#define ADM_0_SCSS_1_IRQ (GIC_SPI_START + 171) +#define ADM_0_SCSS_2_IRQ (GIC_SPI_START + 172) +#define ADM_0_SCSS_3_IRQ (GIC_SPI_START + 173) +#define CC_SCSS_WDT1CPU1BITEEXPIRED (GIC_SPI_START + 174) +#define CC_SCSS_WDT1CPU0BITEEXPIRED (GIC_SPI_START + 175) +#define CC_SCSS_WDT0CPU1BITEEXPIRED (GIC_SPI_START + 176) +#define CC_SCSS_WDT0CPU0BITEEXPIRED (GIC_SPI_START + 177) +#define TSENS_UPPER_LOWER_INT (GIC_SPI_START + 178) +#define SSBI2_2_SC_CPU1_SECURE_INT (GIC_SPI_START + 179) +#define SSBI2_2_SC_CPU1_NON_SECURE_INT (GIC_SPI_START + 180) +#define SSBI2_1_SC_CPU1_SECURE_INT (GIC_SPI_START + 181) +#define SSBI2_1_SC_CPU1_NON_SECURE_INT (GIC_SPI_START + 182) +#define XPU_SUMMARY_IRQ (GIC_SPI_START + 183) +#define BUS_EXCEPTION_SUMMARY_IRQ (GIC_SPI_START + 184) +#define HSDDRX_EBI1CH0_IRQ (GIC_SPI_START + 185) +#define HSDDRX_EBI1CH1_IRQ (GIC_SPI_START + 186) +#define USB3_HS_BAM_IRQ (GIC_SPI_START + 187) +#define USB3_HS_IRQ (GIC_SPI_START + 188) +#define CC_SCSS_WDT1CPU3BITEEXPIRED (GIC_SPI_START + 189) +#define CC_SCSS_WDT1CPU2BITEEXPIRED (GIC_SPI_START + 190) +#define CC_SCSS_WDT0CPU3BITEEXPIRED (GIC_SPI_START + 191) +#define CC_SCSS_WDT0CPU2BITEEXPIRED (GIC_SPI_START + 192) +#define APQ8064_GSBI1_UARTDM_IRQ (GIC_SPI_START + 193) +#define APQ8064_GSBI1_QUP_IRQ (GIC_SPI_START + 194) +#define APQ8064_GSBI2_UARTDM_IRQ (GIC_SPI_START + 195) +#define APQ8064_GSBI2_QUP_IRQ (GIC_SPI_START + 196) +#define RIVA_APSS_LTECOEX_IRQ (GIC_SPI_START + 197) +#define RIVA_APSS_SPARE_IRQ (GIC_SPI_START + 198) +#define RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ (GIC_SPI_START + 199) +#define RIVA_APSS_RESET_DONE_IRQ (GIC_SPI_START + 200) +#define RIVA_APSS_ASIC_IRQ (GIC_SPI_START + 201) +#define RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ (GIC_SPI_START + 202) +#define RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ (GIC_SPI_START + 203) +#define RIVA_APPS_WLAN_SMSM_IRQ (GIC_SPI_START + 204) +#define RIVA_APPS_LOG_CTRL_IRQ (GIC_SPI_START + 205) +#define RIVA_APPS_FM_CTRL_IRQ (GIC_SPI_START + 206) +#define RIVA_APPS_HCI_IRQ (GIC_SPI_START + 207) +#define RIVA_APPS_WLAN_CTRL_IRQ (GIC_SPI_START + 208) +#define SATA_CONTROLLER_IRQ (GIC_SPI_START + 209) +#define SMMU_GFX3D1_CB_SC_SECURE_IRQ (GIC_SPI_START + 210) +#define SMMU_GFX3D1_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 211) +#define KPSS_SPARE_9 (GIC_SPI_START + 212) +#define PPSS_WDOG_TIMER_IRQ (GIC_SPI_START + 213) +#define USB4_HS_BAM_IRQ (GIC_SPI_START + 214) +#define USB4_HS_IRQ (GIC_SPI_START + 215) +#define QDSS_ETB_IRQ (GIC_SPI_START + 216) +#define QDSS_CTI2KPSS_CPU1_IRQ (GIC_SPI_START + 217) +#define QDSS_CTI2KPSS_CPU0_IRQ (GIC_SPI_START + 218) +#define TLMM_MSM_DIR_CONN_IRQ_16 (GIC_SPI_START + 219) +#define TLMM_MSM_DIR_CONN_IRQ_17 (GIC_SPI_START + 220) +#define TLMM_MSM_DIR_CONN_IRQ_18 (GIC_SPI_START + 221) +#define TLMM_MSM_DIR_CONN_IRQ_19 (GIC_SPI_START + 222) +#define TLMM_MSM_DIR_CONN_IRQ_20 (GIC_SPI_START + 223) +#define TLMM_MSM_DIR_CONN_IRQ_21 (GIC_SPI_START + 224) +#define PM8921_USR_IRQ_N (GIC_SPI_START + 225) +#define PM8821_USR_IRQ_N (GIC_SPI_START + 226) + +#define CSI_2_IRQ (GIC_SPI_START + 227) +#define APQ8064_CSIPHY_2LN_IRQ (GIC_SPI_START + 228) +#define USB2_HSIC_IRQ (GIC_SPI_START + 229) +#define CE2_BAM_XPU_IRQ (GIC_SPI_START + 230) +#define CE1_BAM_XPU_IRQ (GIC_SPI_START + 231) +#define RPM_SCSS_CPU2_WAKE_UP_IRQ (GIC_SPI_START + 232) +#define RPM_SCSS_CPU3_WAKE_UP_IRQ (GIC_SPI_START + 233) +#define CS3_BAM_XPU_IRQ (GIC_SPI_START + 234) +#define CE3_IRQ (GIC_SPI_START + 235) +#define SMMU_VCAP_CB_SC_SECURE_IRQ (GIC_SPI_START + 236) +#define SMMU_VCAP_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 237) +#define PCIE20_INT_MSI (GIC_SPI_START + 238) +#define PCIE20_INTA (GIC_SPI_START + 239) +#define PCIE20_INTB (GIC_SPI_START + 240) +#define PCIE20_INTC (GIC_SPI_START + 241) +#define PCIE20_INTD (GIC_SPI_START + 242) +#define PCIE20_INT_PLS_HP (GIC_SPI_START + 243) +#define PCIE20_INT_PLS_PME (GIC_SPI_START + 244) +#define PCIE20_INT_LINK_UP (GIC_SPI_START + 245) +#define PCIE20_INT_LINK_DOWN (GIC_SPI_START + 246) +#define PCIE20_INT_HP_LEGACY (GIC_SPI_START + 247) +#define PCIE20_AER_LEGACY (GIC_SPI_START + 248) +#define PCIE20_INT_PME_LEGACY (GIC_SPI_START + 249) +#define PCIE20_INT_BRIDGE_FLUSH_N (GIC_SPI_START + 250) + +/* Backwards compatible IRQ macros. */ +#define INT_ADM_AARM ADM_0_SCSS_0_IRQ + +/* smd/smsm interrupts */ +#define INT_A9_M2A_0 (GIC_SPI_START + 37) /*GSS_TO_APPS_IRQ_0*/ +#define INT_A9_M2A_5 (GIC_SPI_START + 38) /*GSS_TO_APPS_IRQ_1*/ +#define INT_ADSP_A11 LPASS_SCSS_GP_HIGH_IRQ +#define INT_ADSP_A11_SMSM LPASS_SCSS_GP_MEDIUM_IRQ +#define INT_DSPS_A11 SPS_MTI_31 +#define INT_DSPS_A11_SMSM SPS_MTI_30 +#define INT_WCNSS_A11 RIVA_APSS_SPARE_IRQ +#define INT_WCNSS_A11_SMSM RIVA_APPS_WLAN_SMSM_IRQ + +#endif + diff --git a/arch/arm/mach-msm/include/mach/irqs-8625.h b/arch/arm/mach-msm/include/mach/irqs-8625.h new file mode 100644 index 00000000000..3ff73eb8f05 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-8625.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IRQS_8625_H +#define __ASM_ARCH_MSM_IRQS_8625_H + +#define GIC_PPI_START 16 +#define GIC_SPI_START 32 + +/* As per QGIC2 PPI 16 aka 0 is reserved */ +#define MSM8625_INT_A5_PMU_IRQ (GIC_PPI_START + 1) +#define MSM8625_INT_DEBUG_TIMER_EXP (GIC_PPI_START + 2) +#define MSM8625_INT_GP_TIMER_EXP (GIC_PPI_START + 3) +#define MSM8625_INT_COMMRX (GIC_PPI_START + 4) +#define MSM8625_INT_COMMTX (GIC_PPI_START + 5) + +/* rest of the PPI's not used + */ + +#define MSM8625_INT_A9_M2A_0 (GIC_SPI_START + 0) +#define MSM8625_INT_A9_M2A_1 (GIC_SPI_START + 1) +#define MSM8625_INT_A9_M2A_2 (GIC_SPI_START + 2) +#define MSM8625_INT_A9_M2A_3 (GIC_SPI_START + 3) +#define MSM8625_INT_A9_M2A_4 (GIC_SPI_START + 4) +#define MSM8625_INT_A9_M2A_5 (GIC_SPI_START + 5) +#define MSM8625_INT_A9_M2A_6 (GIC_SPI_START + 6) +#define MSM8625_INT_ACSR_MP_CORE_IPC0 (GIC_SPI_START + 7) +#define MSM8625_INT_ACSR_MP_CORE_IPC1 (GIC_SPI_START + 8) +#define MSM8625_INT_UART1 (GIC_SPI_START + 9) +#define MSM8625_INT_UART2 (GIC_SPI_START + 10) +#define MSM8625_INT_UART3 (GIC_SPI_START + 11) +#define MSM8625_INT_UART1_RX (GIC_SPI_START + 12) +#define MSM8625_INT_UART2_RX (GIC_SPI_START + 13) +#define MSM8625_INT_UART3_RX (GIC_SPI_START + 14) +#define MSM8625_INT_USB_OTG (GIC_SPI_START + 15) +#define MSM8625_INT_DSI_IRQ (GIC_SPI_START + 16) +#define MSM8625_INT_CSI_IRQ_1 (GIC_SPI_START + 17) +#define MSM8625_INT_CSI_IRQ_0 (GIC_SPI_START + 18) +#define MSM8625_INT_MDP (GIC_SPI_START + 19) +#define MSM8625_INT_GRAPHICS (GIC_SPI_START + 20) +#define MSM8625_INT_ADM_AARM (GIC_SPI_START + 21) +#define MSM8625_INT_ADSP_A11 (GIC_SPI_START + 22) +#define MSM8625_INT_ADSP_A9_A11 (GIC_SPI_START + 23) +#define MSM8625_INT_SDC1_0 (GIC_SPI_START + 24) +#define MSM8625_INT_SDC1_1 (GIC_SPI_START + 25) +#define MSM8625_INT_SDC2_0 (GIC_SPI_START + 26) +#define MSM8625_INT_SDC2_1 (GIC_SPI_START + 27) +#define MSM8625_INT_KEYSENSE (GIC_SPI_START + 28) +#define MSM8625_INT_TCHSCRN_SSBI (GIC_SPI_START + 29) +#define MSM8625_INT_TCHSCRN1 (GIC_SPI_START + 30) +#define MSM8625_INT_TCHSCRN2 (GIC_SPI_START + 31) + +#define MSM8625_INT_GPIO_GROUP1 (GIC_SPI_START + 32 + 0) +#define MSM8625_INT_GPIO_GROUP2 (GIC_SPI_START + 32 + 1) +#define MSM8625_INT_PWB_I2C (GIC_SPI_START + 32 + 2) +#define MSM8625_INT_SOFTRESET (GIC_SPI_START + 32 + 3) +#define MSM8625_INT_NAND_WR_ER_DONE (GIC_SPI_START + 32 + 4) +#define MSM8625_INT_NAND_OP_DONE (GIC_SPI_START + 32 + 5) +#define MSM8625_INT_PBUS_ARM11 (GIC_SPI_START + 32 + 6) +#define MSM8625_INT_AXI_MPU_SMI (GIC_SPI_START + 32 + 7) +#define MSM8625_INT_AXI_MPU_EBI1 (GIC_SPI_START + 32 + 8) +#define MSM8625_INT_AD_HSSD (GIC_SPI_START + 32 + 9) +#define MSM8625_INT_NOTUSED (GIC_SPI_START + 32 + 10) +#define MSM8625_INT_ARM11_DMA (GIC_SPI_START + 32 + 11) +#define MSM8625_INT_TSIF_IRQ (GIC_SPI_START + 32 + 12) +#define MSM8625_INT_UART1DM_IRQ (GIC_SPI_START + 32 + 13) +#define MSM8625_INT_UART1DM_RX (GIC_SPI_START + 32 + 14) +#define MSM8625_INT_USB_HS (GIC_SPI_START + 32 + 15) +#define MSM8625_INT_SDC3_0 (GIC_SPI_START + 32 + 16) +#define MSM8625_INT_SDC3_1 (GIC_SPI_START + 32 + 17) +#define MSM8625_INT_SDC4_0 (GIC_SPI_START + 32 + 18) +#define MSM8625_INT_SDC4_1 (GIC_SPI_START + 32 + 19) +#define MSM8625_INT_UART2DM_IRQ (GIC_SPI_START + 32 + 20) +#define MSM8625_INT_UART2DM_RX (GIC_SPI_START + 32 + 21) +#define MSM8625_INT_L2CC_EM (GIC_SPI_START + 32 + 22) +#define MSM8625_INT_L2CC_INTR (GIC_SPI_START + 32 + 23) +#define MSM8625_INT_CE_IRQ (GIC_SPI_START + 32 + 24) + +#define MSM8625_INT_ADSP_A11_SMSM MSM8625_INT_ADSP_A11 +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs-8930.h b/arch/arm/mach-msm/include/mach/irqs-8930.h new file mode 100644 index 00000000000..bfc32f6a1ea --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-8930.h @@ -0,0 +1,292 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IRQS_8930_H +#define __ASM_ARCH_MSM_IRQS_8930_H + +/* MSM ACPU Interrupt Numbers */ + +/* 0-15: STI/SGI (software triggered/generated interrupts) + 16-31: PPI (private peripheral interrupts) + 32+: SPI (shared peripheral interrupts) */ + +#define GIC_PPI_START 16 +#define GIC_SPI_START 32 + +#define INT_VGIC (GIC_PPI_START + 0) +#define INT_DEBUG_TIMER_EXP (GIC_PPI_START + 1) +#define INT_GP_TIMER_EXP (GIC_PPI_START + 2) +#define INT_GP_TIMER2_EXP (GIC_PPI_START + 3) +#define WDT0_ACCSCSSNBARK_INT (GIC_PPI_START + 4) +#define WDT1_ACCSCSSNBARK_INT (GIC_PPI_START + 5) +#define AVS_SVICINT (GIC_PPI_START + 6) +#define AVS_SVICINTSWDONE (GIC_PPI_START + 7) +#define CPU_DBGCPUXCOMMRXFULL (GIC_PPI_START + 8) +#define CPU_DBGCPUXCOMMTXEMPTY (GIC_PPI_START + 9) +#define INT_ARMQC_PERFMON (GIC_PPI_START + 10) +#define SC_AVSCPUXDOWN (GIC_PPI_START + 11) +#define SC_AVSCPUXUP (GIC_PPI_START + 12) +#define SC_SICCPUXACGIRPTREQ (GIC_PPI_START + 13) +#define SC_SICCPUXEXTFAULTIRPTREQ (GIC_PPI_START + 14) +/* PPI 15 is unused */ + +#define APCC_QGICACGIRPTREQ (GIC_SPI_START + 0) +#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1) +#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ +#define APCC_QGICL2IRPTREQ (GIC_SPI_START + 2) +#define APCC_QGICMPUIRPTREQ (GIC_SPI_START + 3) +#define TLMM_MSM_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) +#define TLMM_MSM_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) +#define TLMM_MSM_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) +#define TLMM_MSM_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) +#define TLMM_MSM_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) +#define TLMM_MSM_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) +#define TLMM_MSM_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) +#define TLMM_MSM_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) +#define TLMM_MSM_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) +#define TLMM_MSM_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) +#define PM8921_SEC_IRQ_103 (GIC_SPI_START + 14) +#define PM8018_SEC_IRQ_106 (GIC_SPI_START + 15) +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 16) +#define SPDM_RT_1_IRQ (GIC_SPI_START + 17) +#define SPDM_DIAG_IRQ (GIC_SPI_START + 18) +#define RPM_APCC_CPU0_GP_HIGH_IRQ (GIC_SPI_START + 19) +#define RPM_APCC_CPU0_GP_MEDIUM_IRQ (GIC_SPI_START + 20) +#define RPM_APCC_CPU0_GP_LOW_IRQ (GIC_SPI_START + 21) +#define RPM_APCC_CPU0_WAKE_UP_IRQ (GIC_SPI_START + 22) +#define RPM_APCC_CPU1_GP_HIGH_IRQ (GIC_SPI_START + 23) +#define RPM_APCC_CPU1_GP_MEDIUM_IRQ (GIC_SPI_START + 24) +#define RPM_APCC_CPU1_GP_LOW_IRQ (GIC_SPI_START + 25) +#define RPM_APCC_CPU1_WAKE_UP_IRQ (GIC_SPI_START + 26) +#define SSBI2_2_SC_CPU0_SECURE_IRQ (GIC_SPI_START + 27) +#define SSBI2_2_SC_CPU0_NON_SECURE_IRQ (GIC_SPI_START + 28) +#define SSBI2_1_SC_CPU0_SECURE_IRQ (GIC_SPI_START + 29) +#define SSBI2_1_SC_CPU0_NON_SECURE_IRQ (GIC_SPI_START + 30) +#define MSMC_SC_SEC_CE_IRQ (GIC_SPI_START + 31) +#define MSMC_SC_PRI_CE_IRQ (GIC_SPI_START + 32) +#define SLIMBUS0_CORE_EE1_IRQ (GIC_SPI_START + 33) +#define SLIMBUS0_BAM_EE1_IRQ (GIC_SPI_START + 34) +#define Q6FW_WDOG_EXPIRED_IRQ (GIC_SPI_START + 35) +#define Q6SW_WDOG_EXPIRED_IRQ (GIC_SPI_START + 36) +#define MSS_TO_APPS_IRQ_0 (GIC_SPI_START + 37) +#define MSS_TO_APPS_IRQ_1 (GIC_SPI_START + 38) +#define MSS_TO_APPS_IRQ_2 (GIC_SPI_START + 39) +#define MSS_TO_APPS_IRQ_3 (GIC_SPI_START + 40) +#define MSS_TO_APPS_IRQ_4 (GIC_SPI_START + 41) +#define MSS_TO_APPS_IRQ_5 (GIC_SPI_START + 42) +#define MSS_TO_APPS_IRQ_6 (GIC_SPI_START + 43) +#define MSS_TO_APPS_IRQ_7 (GIC_SPI_START + 44) +#define MSS_TO_APPS_IRQ_8 (GIC_SPI_START + 45) +#define MSS_TO_APPS_IRQ_9 (GIC_SPI_START + 46) +#define VPE_IRQ (GIC_SPI_START + 47) +#define VFE_IRQ (GIC_SPI_START + 48) +#define VCODEC_IRQ (GIC_SPI_START + 49) +/* SPI IRQ 50 is unused */ +#define SMMU_VPE_CB_SC_SECURE_IRQ (GIC_SPI_START + 51) +#define SMMU_VPE_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 52) +#define SMMU_VFE_CB_SC_SECURE_IRQ (GIC_SPI_START + 53) +#define SMMU_VFE_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 54) +#define SMMU_VCODEC_B_CB_SC_SECURE_IRQ (GIC_SPI_START + 55) +#define SMMU_VCODEC_B_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 56) +#define SMMU_VCODEC_A_CB_SC_SECURE_IRQ (GIC_SPI_START + 57) +#define SMMU_VCODEC_A_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 58) +#define SMMU_ROT_CB_SC_SECURE_IRQ (GIC_SPI_START + 59) +#define SMMU_ROT_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 60) +#define SMMU_MDP1_CB_SC_SECURE_IRQ (GIC_SPI_START + 61) +#define SMMU_MDP1_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 62) +#define SMMU_MDP0_CB_SC_SECURE_IRQ (GIC_SPI_START + 63) +#define SMMU_MDP0_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 64) +/* SPI IRQ 65 is unused */ +/* SPI IRQ 66 is unused */ +#define SMMU_IJPEG_CB_SC_SECURE_IRQ (GIC_SPI_START + 67) +#define SMMU_IJPEG_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 68) +#define SMMU_GFX3D_CB_SC_SECURE_IRQ (GIC_SPI_START + 69) +#define SMMU_GFX3D_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 70) +/* SPI IRQ 71 is unused */ +/* SPI IRQ 72 is unused */ +#define ROT_IRQ (GIC_SPI_START + 73) +#define MMSS_FABRIC_IRQ (GIC_SPI_START + 74) +#define MDP_IRQ (GIC_SPI_START + 75) +/* SPI IRQ 76 is unused */ +#define JPEG_IRQ (GIC_SPI_START + 77) +#define MMSS_IMEM_IRQ (GIC_SPI_START + 78) +#define HDMI_IRQ (GIC_SPI_START + 79) +#define GFX3D_IRQ (GIC_SPI_START + 80) +/* SPI IRQ 81 is unused */ +#define DSI1_IRQ (GIC_SPI_START + 82) +#define CSI_1_IRQ (GIC_SPI_START + 83) +#define CSI_0_IRQ (GIC_SPI_START + 84) +#define LPASS_SCSS_AUDIO_IF_OUT0_IRQ (GIC_SPI_START + 85) +#define LPASS_SCSS_MIDI_IRQ (GIC_SPI_START + 86) +#define LPASS_Q6SS_WDOG_EXPIRED (GIC_SPI_START + 87) +#define LPASS_SCSS_GP_LOW_IRQ (GIC_SPI_START + 88) +#define LPASS_SCSS_GP_MEDIUM_IRQ (GIC_SPI_START + 89) +#define LPASS_SCSS_GP_HIGH_IRQ (GIC_SPI_START + 90) +#define TOP_IMEM_IRQ (GIC_SPI_START + 91) +#define FABRIC_SYS_IRQ (GIC_SPI_START + 92) +#define FABRIC_APPS_IRQ (GIC_SPI_START + 93) +#define USB1_HS_BAM_IRQ (GIC_SPI_START + 94) +#define SDC4_BAM_IRQ (GIC_SPI_START + 95) +#define SDC3_BAM_IRQ (GIC_SPI_START + 96) +#define SDC2_BAM_IRQ (GIC_SPI_START + 97) +#define SDC1_BAM_IRQ (GIC_SPI_START + 98) +#define FABRIC_SPS_IRQ (GIC_SPI_START + 99) +#define USB1_HS_IRQ (GIC_SPI_START + 100) +#define SDC4_IRQ_0 (GIC_SPI_START + 101) +#define SDC3_IRQ_0 (GIC_SPI_START + 102) +#define SDC2_IRQ_0 (GIC_SPI_START + 103) +#define SDC1_IRQ_0 (GIC_SPI_START + 104) +#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105) +#define SPS_SEC_VIOL_IRQ (GIC_SPI_START + 106) +#define SPS_MTI_0 (GIC_SPI_START + 107) +#define SPS_MTI_1 (GIC_SPI_START + 108) +#define SPS_MTI_2 (GIC_SPI_START + 109) +#define SPS_MTI_3 (GIC_SPI_START + 110) +#define GPS_PPS_OUT (GIC_SPI_START + 111) +/* SPI IRQ 112 is unused */ +/* SPI IRQ 113 is unused */ +/* SPI IRQ 114 is unused */ +/* SPI IRQ 115 is unused */ +#define TLMM_MSM_DIR_CONN_IRQ_11 (GIC_SPI_START + 116) +#define TLMM_MSM_DIR_CONN_IRQ_10 (GIC_SPI_START + 117) +#define BAM_DMA1 (GIC_SPI_START + 118) +#define BAM_DMA2 (GIC_SPI_START + 119) +#define SDC1_IRQ (GIC_SPI_START + 120) +#define SDC2_IRQ (GIC_SPI_START + 121) +#define SDC3_IRQ (GIC_SPI_START + 122) +#define SPS_MTI_16 (GIC_SPI_START + 123) +#define SPS_MTI_17 (GIC_SPI_START + 124) +#define SPS_MTI_18 (GIC_SPI_START + 125) +#define SPS_MTI_19 (GIC_SPI_START + 126) +#define SPS_MTI_20 (GIC_SPI_START + 127) +#define SPS_MTI_21 (GIC_SPI_START + 128) +#define SPS_MTI_22 (GIC_SPI_START + 129) +#define SPS_MTI_23 (GIC_SPI_START + 130) +#define SPS_MTI_24 (GIC_SPI_START + 131) +#define SPS_MTI_25 (GIC_SPI_START + 132) +#define SPS_MTI_26 (GIC_SPI_START + 133) +#define SPS_MTI_27 (GIC_SPI_START + 134) +#define SPS_MTI_28 (GIC_SPI_START + 135) +#define SPS_MTI_29 (GIC_SPI_START + 136) +#define SPS_MTI_30 (GIC_SPI_START + 137) +#define SPS_MTI_31 (GIC_SPI_START + 138) +#define CSIPHY_4LN_IRQ (GIC_SPI_START + 139) +#define MSM8930_CSIPHY_2LN_IRQ (GIC_SPI_START + 140) +#define USB2_IRQ (GIC_SPI_START + 141) +#define USB1_IRQ (GIC_SPI_START + 142) +#define TSSC_SSBI_IRQ (GIC_SPI_START + 143) +#define TSSC_SAMPLE_IRQ (GIC_SPI_START + 144) +#define TSSC_PENUP_IRQ (GIC_SPI_START + 145) +#define MSM8930_GSBI1_UARTDM_IRQ (GIC_SPI_START + 146) +#define MSM8930_GSBI1_QUP_IRQ (GIC_SPI_START + 147) +#define MSM8930_GSBI2_UARTDM_IRQ (GIC_SPI_START + 148) +#define MSM8930_GSBI2_QUP_IRQ (GIC_SPI_START + 149) +#define GSBI3_UARTDM_IRQ (GIC_SPI_START + 150) +#define GSBI3_QUP_IRQ (GIC_SPI_START + 151) +#define GSBI4_UARTDM_IRQ (GIC_SPI_START + 152) +#define GSBI4_QUP_IRQ (GIC_SPI_START + 153) +#define GSBI5_UARTDM_IRQ (GIC_SPI_START + 154) +#define GSBI5_QUP_IRQ (GIC_SPI_START + 155) +#define GSBI6_UARTDM_IRQ (GIC_SPI_START + 156) +#define GSBI6_QUP_IRQ (GIC_SPI_START + 157) +#define GSBI7_UARTDM_IRQ (GIC_SPI_START + 158) +#define GSBI7_QUP_IRQ (GIC_SPI_START + 159) +#define GSBI8_UARTDM_IRQ (GIC_SPI_START + 160) +#define GSBI8_QUP_IRQ (GIC_SPI_START + 161) +#define TSIF_TSPP_IRQ (GIC_SPI_START + 162) +#define TSIF_BAM_IRQ (GIC_SPI_START + 163) +#define TSIF2_IRQ (GIC_SPI_START + 164) +#define TSIF1_IRQ (GIC_SPI_START + 165) +/* SPI IRQ 166 is unused */ +#define ISPIF_IRQ (GIC_SPI_START + 167) +#define MSMC_SC_SEC_TMR_IRQ (GIC_SPI_START + 168) +#define MSMC_SC_SEC_WDOG_BARK_IRQ (GIC_SPI_START + 169) +#define ADM_0_SCSS_0_IRQ (GIC_SPI_START + 170) +#define ADM_0_SCSS_1_IRQ (GIC_SPI_START + 171) +#define ADM_0_SCSS_2_IRQ (GIC_SPI_START + 172) +#define ADM_0_SCSS_3_IRQ (GIC_SPI_START + 173) +#define CC_SCSS_WDT1CPU1BITEEXPIRED (GIC_SPI_START + 174) +#define CC_SCSS_WDT1CPU0BITEEXPIRED (GIC_SPI_START + 175) +#define CC_SCSS_WDT0CPU1BITEEXPIRED (GIC_SPI_START + 176) +#define CC_SCSS_WDT0CPU0BITEEXPIRED (GIC_SPI_START + 177) +#define TSENS_UPPER_LOWER_INT (GIC_SPI_START + 178) +#define SSBI2_2_SC_CPU1_SECURE_INT (GIC_SPI_START + 179) +#define SSBI2_2_SC_CPU1_NON_SECURE_INT (GIC_SPI_START + 180) +#define SSBI2_1_SC_CPU1_SECURE_INT (GIC_SPI_START + 181) +#define SSBI2_1_SC_CPU1_NON_SECURE_INT (GIC_SPI_START + 182) +#define XPU_SUMMARY_IRQ (GIC_SPI_START + 183) +#define BUS_EXCEPTION_SUMMARY_IRQ (GIC_SPI_START + 184) +#define HSDDRX_EBI1CH0_IRQ (GIC_SPI_START + 185) +/* SPI IRQ 186 is unused */ +#define SDC5_BAM_IRQ (GIC_SPI_START + 187) +#define SDC5_IRQ_0 (GIC_SPI_START + 188) +#define GSBI9_UARTDM_IRQ (GIC_SPI_START + 189) +#define GSBI9_QUP_IRQ (GIC_SPI_START + 190) +#define GSBI10_UARTDM_IRQ (GIC_SPI_START + 191) +#define GSBI10_QUP_IRQ (GIC_SPI_START + 192) +#define GSBI11_UARTDM_IRQ (GIC_SPI_START + 193) +#define GSBI11_QUP_IRQ (GIC_SPI_START + 194) +#define GSBI12_UARTDM_IRQ (GIC_SPI_START + 195) +#define GSBI12_QUP_IRQ (GIC_SPI_START + 196) +#define RIVA_APSS_LTECOEX_IRQ (GIC_SPI_START + 197) +#define RIVA_APSS_SPARE_IRQ (GIC_SPI_START + 198) +#define RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ (GIC_SPI_START + 199) +#define RIVA_APSS_RESET_DONE_IRQ (GIC_SPI_START + 200) +#define RIVA_APSS_ASIC_IRQ (GIC_SPI_START + 201) +#define RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ (GIC_SPI_START + 202) +#define RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ (GIC_SPI_START + 203) +#define RIVA_APPS_WLAN_SMSM_IRQ (GIC_SPI_START + 204) +#define RIVA_APPS_LOG_CTRL_IRQ (GIC_SPI_START + 205) +#define RIVA_APPS_FM_CTRL_IRQ (GIC_SPI_START + 206) +#define RIVA_APPS_HCI_IRQ (GIC_SPI_START + 207) +#define RIVA_APPS_WLAN_CTRL_IRQ (GIC_SPI_START + 208) +#define A2_BAM_IRQ (GIC_SPI_START + 209) +/* SPI IRQ 210 is unused */ +/* SPI IRQ 211 is unused */ +/* SPI IRQ 212 is unused */ +#define PPSS_WDOG_TIMER_IRQ (GIC_SPI_START + 213) +#define SPS_SLIMBUS_CORE_EE0_IRQ (GIC_SPI_START + 214) +#define SPS_SLIMBUS_BAM_EE0_IRQ (GIC_SPI_START + 215) +#define QDSS_ETB_IRQ (GIC_SPI_START + 216) +#define QDSS_CTI2KPSS_CPU1_IRQ (GIC_SPI_START + 217) +#define QDSS_CTI2KPSS_CPU0_IRQ (GIC_SPI_START + 218) +#define TLMM_MSM_DIR_CONN_IRQ_16 (GIC_SPI_START + 219) +#define TLMM_MSM_DIR_CONN_IRQ_17 (GIC_SPI_START + 220) +#define TLMM_MSM_DIR_CONN_IRQ_18 (GIC_SPI_START + 221) +#define TLMM_MSM_DIR_CONN_IRQ_19 (GIC_SPI_START + 222) +#define TLMM_MSM_DIR_CONN_IRQ_20 (GIC_SPI_START + 223) +#define TLMM_MSM_DIR_CONN_IRQ_21 (GIC_SPI_START + 224) +#define PM8921_SEC_IRQ_104 (GIC_SPI_START + 225) +#define PM8018_SEC_IRQ_107 (GIC_SPI_START + 226) +#define USB_HSIC_IRQ (GIC_SPI_START + 229) +#define CE2_BAM_XPU_IRQ (GIC_SPI_START + 230) +#define CE1_BAM_XPU_IRQ (GIC_SPI_START + 231) +#define GFX3D_VBIF_IRPT (GIC_SPI_START + 232) +#define RBIF_IRQ_0 (GIC_SPI_START + 233) +#define RBIF_IRQ_1 (GIC_SPI_START + 234) +#define RBIF_IRQ_2 (GIC_SPI_START + 235) + +/* Backwards compatible IRQ macros. */ +#define INT_ADM_AARM ADM_0_SCSS_0_IRQ + +/* smd/smsm interrupts */ +#define INT_A9_M2A_0 (GIC_SPI_START + 37) /*MSS_TO_APPS_IRQ_0*/ +#define INT_A9_M2A_5 (GIC_SPI_START + 38) /*MSS_TO_APPS_IRQ_1*/ +#define INT_ADSP_A11 LPASS_SCSS_GP_HIGH_IRQ +#define INT_ADSP_A11_SMSM LPASS_SCSS_GP_MEDIUM_IRQ +#define INT_DSPS_A11 SPS_MTI_31 +#define INT_DSPS_A11_SMSM SPS_MTI_30 +#define INT_WCNSS_A11 RIVA_APSS_SPARE_IRQ +#define INT_WCNSS_A11_SMSM RIVA_APPS_WLAN_SMSM_IRQ + +#endif + diff --git a/arch/arm/mach-msm/include/mach/irqs-8960.h b/arch/arm/mach-msm/include/mach/irqs-8960.h index 81ab2a6792b..012dd74efeb 100644 --- a/arch/arm/mach-msm/include/mach/irqs-8960.h +++ b/arch/arm/mach-msm/include/mach/irqs-8960.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -32,30 +32,31 @@ #define AVS_SVICINTSWDONE (GIC_PPI_START + 7) #define CPU_DBGCPUXCOMMRXFULL (GIC_PPI_START + 8) #define CPU_DBGCPUXCOMMTXEMPTY (GIC_PPI_START + 9) -#define CPU_SICCPUXPERFMONIRPTREQ (GIC_PPI_START + 10) +#define INT_ARMQC_PERFMON (GIC_PPI_START + 10) #define SC_AVSCPUXDOWN (GIC_PPI_START + 11) #define SC_AVSCPUXUP (GIC_PPI_START + 12) #define SC_SICCPUXACGIRPTREQ (GIC_PPI_START + 13) #define SC_SICCPUXEXTFAULTIRPTREQ (GIC_PPI_START + 14) /* PPI 15 is unused */ -#define SC_SICMPUIRPTREQ (GIC_SPI_START + 0) -#define SC_SICL2IRPTREQ (GIC_SPI_START + 1) -#define SC_SICL2PERFMONIRPTREQ (GIC_SPI_START + 2) -#define SC_SICAGCIRPTREQ (GIC_SPI_START + 3) -#define TLMM_APCC_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) -#define TLMM_APCC_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) -#define TLMM_APCC_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) -#define TLMM_APCC_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) -#define TLMM_APCC_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) -#define TLMM_APCC_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) -#define TLMM_APCC_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) -#define TLMM_APCC_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) -#define TLMM_APCC_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) -#define TLMM_APCC_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) +#define APCC_QGICACGIRPTREQ (GIC_SPI_START + 0) +#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1) +#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ +#define APCC_QGICL2IRPTREQ (GIC_SPI_START + 2) +#define APCC_QGICMPUIRPTREQ (GIC_SPI_START + 3) +#define TLMM_MSM_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) +#define TLMM_MSM_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) +#define TLMM_MSM_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) +#define TLMM_MSM_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) +#define TLMM_MSM_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) +#define TLMM_MSM_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) +#define TLMM_MSM_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) +#define TLMM_MSM_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) +#define TLMM_MSM_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) +#define TLMM_MSM_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) #define PM8921_SEC_IRQ_103 (GIC_SPI_START + 14) #define PM8018_SEC_IRQ_106 (GIC_SPI_START + 15) -#define TLMM_APCC_SUMMARY_IRQ (GIC_SPI_START + 16) +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 16) #define SPDM_RT_1_IRQ (GIC_SPI_START + 17) #define SPDM_DIAG_IRQ (GIC_SPI_START + 18) #define RPM_APCC_CPU0_GP_HIGH_IRQ (GIC_SPI_START + 19) @@ -179,16 +180,16 @@ #define SPS_MTI_30 (GIC_SPI_START + 137) #define SPS_MTI_31 (GIC_SPI_START + 138) #define CSIPHY_4LN_IRQ (GIC_SPI_START + 139) -#define CSIPHY_2LN_IRQ (GIC_SPI_START + 140) +#define MSM8960_CSIPHY_2LN_IRQ (GIC_SPI_START + 140) #define USB2_IRQ (GIC_SPI_START + 141) #define USB1_IRQ (GIC_SPI_START + 142) #define TSSC_SSBI_IRQ (GIC_SPI_START + 143) #define TSSC_SAMPLE_IRQ (GIC_SPI_START + 144) #define TSSC_PENUP_IRQ (GIC_SPI_START + 145) -#define GSBI1_UARTDM_IRQ (GIC_SPI_START + 146) -#define GSBI1_QUP_IRQ (GIC_SPI_START + 147) -#define GSBI2_UARTDM_IRQ (GIC_SPI_START + 148) -#define GSBI2_QUP_IRQ (GIC_SPI_START + 149) +#define MSM8960_GSBI1_UARTDM_IRQ (GIC_SPI_START + 146) +#define MSM8960_GSBI1_QUP_IRQ (GIC_SPI_START + 147) +#define MSM8960_GSBI2_UARTDM_IRQ (GIC_SPI_START + 148) +#define MSM8960_GSBI2_QUP_IRQ (GIC_SPI_START + 149) #define GSBI3_UARTDM_IRQ (GIC_SPI_START + 150) #define GSBI3_QUP_IRQ (GIC_SPI_START + 151) #define GSBI4_UARTDM_IRQ (GIC_SPI_START + 152) @@ -209,10 +210,10 @@ #define ISPIF_IRQ (GIC_SPI_START + 167) #define MSMC_SC_SEC_TMR_IRQ (GIC_SPI_START + 168) #define MSMC_SC_SEC_WDOG_BARK_IRQ (GIC_SPI_START + 169) -#define INT_ADM0_SCSS_0_IRQ (GIC_SPI_START + 170) -#define INT_ADM0_SCSS_1_IRQ (GIC_SPI_START + 171) -#define INT_ADM0_SCSS_2_IRQ (GIC_SPI_START + 172) -#define INT_ADM0_SCSS_3_IRQ (GIC_SPI_START + 173) +#define ADM_0_SCSS_0_IRQ (GIC_SPI_START + 170) +#define ADM_0_SCSS_1_IRQ (GIC_SPI_START + 171) +#define ADM_0_SCSS_2_IRQ (GIC_SPI_START + 172) +#define ADM_0_SCSS_3_IRQ (GIC_SPI_START + 173) #define CC_SCSS_WDT1CPU1BITEEXPIRED (GIC_SPI_START + 174) #define CC_SCSS_WDT1CPU0BITEEXPIRED (GIC_SPI_START + 175) #define CC_SCSS_WDT0CPU1BITEEXPIRED (GIC_SPI_START + 176) @@ -239,11 +240,11 @@ #define RIVA_APSS_LTECOEX_IRQ (GIC_SPI_START + 197) #define RIVA_APSS_SPARE_IRQ (GIC_SPI_START + 198) #define RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ (GIC_SPI_START + 199) -#define RIVA_ASS_RESET_DONE_IRQ (GIC_SPI_START + 200) +#define RIVA_APSS_RESET_DONE_IRQ (GIC_SPI_START + 200) #define RIVA_APSS_ASIC_IRQ (GIC_SPI_START + 201) #define RIVA_APPS_WLAN_RX_DATA_AVAIL_IRQ (GIC_SPI_START + 202) #define RIVA_APPS_WLAN_DATA_XFER_DONE_IRQ (GIC_SPI_START + 203) -#define RIVA_APPS_WLAM_SMSM_IRQ (GIC_SPI_START + 204) +#define RIVA_APPS_WLAN_SMSM_IRQ (GIC_SPI_START + 204) #define RIVA_APPS_LOG_CTRL_IRQ (GIC_SPI_START + 205) #define RIVA_APPS_FM_CTRL_IRQ (GIC_SPI_START + 206) #define RIVA_APPS_HCI_IRQ (GIC_SPI_START + 207) @@ -258,20 +259,30 @@ #define QDSS_ETB_IRQ (GIC_SPI_START + 216) #define QDSS_CTI2KPSS_CPU1_IRQ (GIC_SPI_START + 217) #define QDSS_CTI2KPSS_CPU0_IRQ (GIC_SPI_START + 218) -#define TLMM_APCC_DIR_CONN_IRQ_16 (GIC_SPI_START + 219) -#define TLMM_APCC_DIR_CONN_IRQ_17 (GIC_SPI_START + 220) -#define TLMM_APCC_DIR_CONN_IRQ_18 (GIC_SPI_START + 221) -#define TLMM_APCC_DIR_CONN_IRQ_19 (GIC_SPI_START + 222) -#define TLMM_APCC_DIR_CONN_IRQ_20 (GIC_SPI_START + 223) -#define TLMM_APCC_DIR_CONN_IRQ_21 (GIC_SPI_START + 224) +#define TLMM_MSM_DIR_CONN_IRQ_16 (GIC_SPI_START + 219) +#define TLMM_MSM_DIR_CONN_IRQ_17 (GIC_SPI_START + 220) +#define TLMM_MSM_DIR_CONN_IRQ_18 (GIC_SPI_START + 221) +#define TLMM_MSM_DIR_CONN_IRQ_19 (GIC_SPI_START + 222) +#define TLMM_MSM_DIR_CONN_IRQ_20 (GIC_SPI_START + 223) +#define TLMM_MSM_DIR_CONN_IRQ_21 (GIC_SPI_START + 224) #define PM8921_SEC_IRQ_104 (GIC_SPI_START + 225) #define PM8018_SEC_IRQ_107 (GIC_SPI_START + 226) +#define USB_HSIC_IRQ (GIC_SPI_START + 229) +#define MSM8960_CSIPHY_2_2LN_IRQ (GIC_SPI_START + 228) +#define CSI_2_IRQ (GIC_SPI_START + 227) -/* For now, use the maximum number of interrupts until a pending GIC issue - * is sorted out */ -#define NR_MSM_IRQS 1020 -#define NR_BOARD_IRQS 0 -#define NR_GPIO_IRQS 0 +/* Backwards compatible IRQ macros. */ +#define INT_ADM_AARM ADM_0_SCSS_0_IRQ + +/* smd/smsm interrupts */ +#define INT_A9_M2A_0 (GIC_SPI_START + 37) /*MSS_TO_APPS_IRQ_0*/ +#define INT_A9_M2A_5 (GIC_SPI_START + 38) /*MSS_TO_APPS_IRQ_1*/ +#define INT_ADSP_A11 LPASS_SCSS_GP_HIGH_IRQ +#define INT_ADSP_A11_SMSM LPASS_SCSS_GP_MEDIUM_IRQ +#define INT_DSPS_A11 SPS_MTI_31 +#define INT_DSPS_A11_SMSM SPS_MTI_30 +#define INT_WCNSS_A11 RIVA_APSS_SPARE_IRQ +#define INT_WCNSS_A11_SMSM RIVA_APPS_WLAN_SMSM_IRQ #endif diff --git a/arch/arm/mach-msm/include/mach/irqs-8x50.h b/arch/arm/mach-msm/include/mach/irqs-8x50.h index 26adbe0e940..f0d70f94a09 100644 --- a/arch/arm/mach-msm/include/mach/irqs-8x50.h +++ b/arch/arm/mach-msm/include/mach/irqs-8x50.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -57,7 +57,7 @@ #define INT_TCSR_MPRPH_SC2 (32 + 6) #define INT_OP_PEN (32 + 7) #define INT_AD_HSSD (32 + 8) -#define INT_ARM11_PM (32 + 9) +#define INT_ARMQC_PERFMON (32 + 9) #define INT_SDMA_NON_SECURE (32 + 10) #define INT_TSIF_IRQ (32 + 11) #define INT_UART1DM_IRQ (32 + 12) @@ -85,4 +85,5 @@ #define NR_MSM_IRQS 64 #define NR_BOARD_IRQS 64 +#define INT_ADSP_A11_SMSM INT_ADSP_A11 #endif diff --git a/arch/arm/mach-msm/include/mach/irqs-8x60.h b/arch/arm/mach-msm/include/mach/irqs-8x60.h index a2494d1925d..c9729f4bbbd 100644 --- a/arch/arm/mach-msm/include/mach/irqs-8x60.h +++ b/arch/arm/mach-msm/include/mach/irqs-8x60.h @@ -1,8 +1,8 @@ -/* Copyright (c) 2010 Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved. * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -17,9 +17,8 @@ /* MSM ACPU Interrupt Numbers */ /* 0-15: STI/SGI (software triggered/generated interrupts) - * 16-31: PPI (private peripheral interrupts) - * 32+: SPI (shared peripheral interrupts) - */ + 16-31: PPI (private peripheral interrupts) + 32+: SPI (shared peripheral interrupts) */ #define GIC_PPI_START 16 #define GIC_SPI_START 32 @@ -33,7 +32,7 @@ #define AVS_SVICINTSWDONE (GIC_PPI_START + 6) #define CPU_DBGCPUXCOMMRXFULL (GIC_PPI_START + 7) #define CPU_DBGCPUXCOMMTXEMPTY (GIC_PPI_START + 8) -#define CPU_SICCPUXPERFMONIRPTREQ (GIC_PPI_START + 9) +#define INT_ARMQC_PERFMON (GIC_PPI_START + 9) #define SC_AVSCPUXDOWN (GIC_PPI_START + 10) #define SC_AVSCPUXUP (GIC_PPI_START + 11) #define SC_SICCPUXACGIRPTREQ (GIC_PPI_START + 12) @@ -44,19 +43,19 @@ #define SC_SICL2IRPTREQ (GIC_SPI_START + 1) #define SC_SICL2PERFMONIRPTREQ (GIC_SPI_START + 2) #define NC (GIC_SPI_START + 3) -#define TLMM_SCSS_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) -#define TLMM_SCSS_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) -#define TLMM_SCSS_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) -#define TLMM_SCSS_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) -#define TLMM_SCSS_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) -#define TLMM_SCSS_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) -#define TLMM_SCSS_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) -#define TLMM_SCSS_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) -#define TLMM_SCSS_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) -#define TLMM_SCSS_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) +#define TLMM_MSM_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) +#define TLMM_MSM_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) +#define TLMM_MSM_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) +#define TLMM_MSM_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) +#define TLMM_MSM_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) +#define TLMM_MSM_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) +#define TLMM_MSM_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) +#define TLMM_MSM_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) +#define TLMM_MSM_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) +#define TLMM_MSM_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) #define PM8058_SEC_IRQ_N (GIC_SPI_START + 14) #define PM8901_SEC_IRQ_N (GIC_SPI_START + 15) -#define TLMM_SCSS_SUMMARY_IRQ (GIC_SPI_START + 16) +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 16) #define SPDM_RT_1_IRQ (GIC_SPI_START + 17) #define SPDM_DIAG_IRQ (GIC_SPI_START + 18) #define RPM_SCSS_CPU0_GP_HIGH_IRQ (GIC_SPI_START + 19) @@ -87,7 +86,7 @@ #define MARM_SCSS_GP_IRQ_7 (GIC_SPI_START + 44) #define MARM_SCSS_GP_IRQ_8 (GIC_SPI_START + 45) #define MARM_SCSS_GP_IRQ_9 (GIC_SPI_START + 46) -#define VPE_IRQ (GIC_SPI_START + 47) +#define INT_VPE (GIC_SPI_START + 47) #define VFE_IRQ (GIC_SPI_START + 48) #define VCODEC_IRQ (GIC_SPI_START + 49) #define TV_ENC_IRQ (GIC_SPI_START + 50) @@ -115,9 +114,9 @@ #define SMMU_GFX2D0_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 72) #define ROT_IRQ (GIC_SPI_START + 73) #define MMSS_FABRIC_IRQ (GIC_SPI_START + 74) -#define MDP_IRQ (GIC_SPI_START + 75) +#define INT_MDP (GIC_SPI_START + 75) #define JPEGD_IRQ (GIC_SPI_START + 76) -#define JPEG_IRQ (GIC_SPI_START + 77) +#define INT_JPEG (GIC_SPI_START + 77) #define MMSS_IMEM_IRQ (GIC_SPI_START + 78) #define HDMI_IRQ (GIC_SPI_START + 79) #define GFX3D_IRQ (GIC_SPI_START + 80) @@ -186,21 +185,21 @@ #define TSSC_SSBI_IRQ (GIC_SPI_START + 143) #define TSSC_SAMPLE_IRQ (GIC_SPI_START + 144) #define TSSC_PENUP_IRQ (GIC_SPI_START + 145) -#define INT_UART1DM_IRQ (GIC_SPI_START + 146) -#define GSBI1_QUP_IRQ (GIC_SPI_START + 147) -#define INT_UART2DM_IRQ (GIC_SPI_START + 148) -#define GSBI2_QUP_IRQ (GIC_SPI_START + 149) -#define INT_UART3DM_IRQ (GIC_SPI_START + 150) +#define GSBI1_UARTDM_IRQ (GIC_SPI_START + 146) +#define GSBI1_QUP_IRQ (GIC_SPI_START + 147) +#define GSBI2_UARTDM_IRQ (GIC_SPI_START + 148) +#define GSBI2_QUP_IRQ (GIC_SPI_START + 149) +#define GSBI3_UARTDM_IRQ (GIC_SPI_START + 150) #define GSBI3_QUP_IRQ (GIC_SPI_START + 151) -#define INT_UART4DM_IRQ (GIC_SPI_START + 152) +#define GSBI4_UARTDM_IRQ (GIC_SPI_START + 152) #define GSBI4_QUP_IRQ (GIC_SPI_START + 153) -#define INT_UART5DM_IRQ (GIC_SPI_START + 154) +#define GSBI5_UARTDM_IRQ (GIC_SPI_START + 154) #define GSBI5_QUP_IRQ (GIC_SPI_START + 155) -#define INT_UART6DM_IRQ (GIC_SPI_START + 156) +#define GSBI6_UARTDM_IRQ (GIC_SPI_START + 156) #define GSBI6_QUP_IRQ (GIC_SPI_START + 157) -#define INT_UART7DM_IRQ (GIC_SPI_START + 158) +#define GSBI7_UARTDM_IRQ (GIC_SPI_START + 158) #define GSBI7_QUP_IRQ (GIC_SPI_START + 159) -#define INT_UART8DM_IRQ (GIC_SPI_START + 160) +#define GSBI8_UARTDM_IRQ (GIC_SPI_START + 160) #define GSBI8_QUP_IRQ (GIC_SPI_START + 161) #define TSIF_TSPP_IRQ (GIC_SPI_START + 162) #define TSIF_BAM_IRQ (GIC_SPI_START + 163) @@ -229,20 +228,19 @@ #define HSDDRX_EBI1_IRQ (GIC_SPI_START + 186) #define SDC5_BAM_IRQ (GIC_SPI_START + 187) #define SDC5_IRQ_0 (GIC_SPI_START + 188) -#define INT_UART9DM_IRQ (GIC_SPI_START + 189) +#define GSBI9_UARTDM_IRQ (GIC_SPI_START + 189) #define GSBI9_QUP_IRQ (GIC_SPI_START + 190) -#define INT_UART10DM_IRQ (GIC_SPI_START + 191) +#define GSBI10_UARTDM_IRQ (GIC_SPI_START + 191) #define GSBI10_QUP_IRQ (GIC_SPI_START + 192) -#define INT_UART11DM_IRQ (GIC_SPI_START + 193) +#define GSBI11_UARTDM_IRQ (GIC_SPI_START + 193) #define GSBI11_QUP_IRQ (GIC_SPI_START + 194) -#define INT_UART12DM_IRQ (GIC_SPI_START + 195) +#define GSBI12_UARTDM_IRQ (GIC_SPI_START + 195) #define GSBI12_QUP_IRQ (GIC_SPI_START + 196) -/*SPI 197 to 209 arent used in 8x60*/ -#define SMMU_GFX2D1_CB_SC_SECURE_IRQ (GIC_SPI_START + 210) -#define SMMU_GFX2D1_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 211) +#define SMMU_GFX2D1_CB_SC_SECURE_IRQ (GIC_SPI_START + 210) +#define SMMU_GFX2D1_CB_SC_NON_SECURE_IRQ (GIC_SPI_START + 211) +#define GFX2D1_IRQ (GIC_SPI_START + 212) -/*SPI 212 to 216 arent used in 8x60*/ #define SMPSS_SPARE_1 (GIC_SPI_START + 217) #define SMPSS_SPARE_2 (GIC_SPI_START + 218) #define SMPSS_SPARE_3 (GIC_SPI_START + 219) @@ -251,8 +249,21 @@ #define SMPSS_SPARE_6 (GIC_SPI_START + 222) #define SMPSS_SPARE_7 (GIC_SPI_START + 223) +#define NR_TLMM_MSM_DIR_CONN_IRQ 10 #define NR_GPIO_IRQS 173 +#define NR_MSM_GPIOS NR_GPIO_IRQS #define NR_MSM_IRQS 256 -#define NR_BOARD_IRQS 0 +#define NR_PMIC8058_IRQS 256 +#define NR_PMIC8901_IRQS 72 +#define NR_GPIO_EXPANDER_IRQS 98 +#define NR_BOARD_IRQS (NR_PMIC8058_IRQS + NR_PMIC8901_IRQS +\ + NR_GPIO_EXPANDER_IRQS) + +/* smd/smsm interrupts */ +#define INT_A9_M2A_0 MARM_SCSS_GP_IRQ_0 +#define INT_A9_M2A_5 MARM_SCSS_GP_IRQ_1 +#define INT_ADSP_A11 LPASS_SCSS_GP_HIGH_IRQ +#define INT_ADSP_A11_SMSM LPASS_SCSS_GP_MEDIUM_IRQ +#define INT_DSPS_A11 SPS_MTI_31 #endif diff --git a/arch/arm/mach-msm/include/mach/irqs-9615.h b/arch/arm/mach-msm/include/mach/irqs-9615.h new file mode 100644 index 00000000000..39058a65191 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-9615.h @@ -0,0 +1,204 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IRQS_9615_H +#define __ASM_ARCH_MSM_IRQS_9615_H + +/* MSM ACPU Interrupt Numbers */ + +/* + * 0-15: STI/SGI (software triggered/generated interrupts) + * 16-31: PPI (private peripheral interrupts) + * 32+: SPI (shared peripheral interrupts) + */ + +#define FIQ_START 16 +#define GIC_PPI_START 16 +#define GIC_SPI_START 32 + +#define INT_DEBUG_TIMER_EXP (GIC_PPI_START + 1) +#define INT_GP_TIMER_EXP (GIC_PPI_START + 2) +#define INT_GP_TIMER2_EXP (GIC_PPI_START + 3) +#define WDT0_ACCSCSSNBARK_INT (GIC_PPI_START + 4) +#define WDT1_ACCSCSSNBARK_INT (GIC_PPI_START + 5) +#define AVS_SVICINT (GIC_PPI_START + 6) +#define AVS_SVICINTSWDONE (GIC_PPI_START + 7) +#define CPU_DBGCPUXCOMMRXFULL (GIC_PPI_START + 8) +#define CPU_DBGCPUXCOMMTXEMPTY (GIC_PPI_START + 9) +#define INT_ARMQC_PERFMON (GIC_PPI_START + 10) +#define SC_AVSCPUXDOWN (GIC_PPI_START + 11) +#define SC_AVSCPUXUP (GIC_PPI_START + 12) +#define SC_SICCPUXACGIRPTREQ (GIC_PPI_START + 13) +#define SC_SICCPUXEXTFAULTIRPTREQ (GIC_PPI_START + 14) +/* PPI 15 is unused */ + +#define APCC_QGICACGIRPTREQ (GIC_SPI_START + 0) +#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1) +#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ +#define APCC_QGICL2IRPTREQ (GIC_SPI_START + 2) +#define APCC_QGICMPUIRPTREQ (GIC_SPI_START + 3) +#define TLMM_MSM_DIR_CONN_IRQ_0 (GIC_SPI_START + 4) +#define TLMM_MSM_DIR_CONN_IRQ_1 (GIC_SPI_START + 5) +#define TLMM_MSM_DIR_CONN_IRQ_2 (GIC_SPI_START + 6) +#define TLMM_MSM_DIR_CONN_IRQ_3 (GIC_SPI_START + 7) +#define TLMM_MSM_DIR_CONN_IRQ_4 (GIC_SPI_START + 8) +#define TLMM_MSM_DIR_CONN_IRQ_5 (GIC_SPI_START + 9) +#define TLMM_MSM_DIR_CONN_IRQ_6 (GIC_SPI_START + 10) +#define TLMM_MSM_DIR_CONN_IRQ_7 (GIC_SPI_START + 11) +#define TLMM_MSM_DIR_CONN_IRQ_8 (GIC_SPI_START + 12) +#define TLMM_MSM_DIR_CONN_IRQ_9 (GIC_SPI_START + 13) +/* 14 Reserved */ +#define PM8018_SEC_IRQ_N (GIC_SPI_START + 15) +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 16) +#define SPDM_RT_1_IRQ (GIC_SPI_START + 17) +#define SPDM_DIAG_IRQ (GIC_SPI_START + 18) +#define RPM_APCC_CPU0_GP_HIGH_IRQ (GIC_SPI_START + 19) +#define RPM_APCC_CPU0_GP_MEDIUM_IRQ (GIC_SPI_START + 20) +#define RPM_APCC_CPU0_GP_LOW_IRQ (GIC_SPI_START + 21) +#define RPM_APCC_CPU0_WAKE_UP_IRQ (GIC_SPI_START + 22) +/* 23-28 Reserved */ +#define SSBI2_1_SC_CPU0_SECURE_IRQ (GIC_SPI_START + 29) +#define SSBI2_1_SC_CPU0_NON_SECURE_IRQ (GIC_SPI_START + 30) +/* 31 Reserved */ +#define MSMC_SC_PRI_CE_IRQ (GIC_SPI_START + 32) +#define SLIMBUS0_CORE_EE1_IRQ (GIC_SPI_START + 33) +#define SLIMBUS0_BAM_EE1_IRQ (GIC_SPI_START + 34) +#define Q6FW_WDOG_EXPIRED_IRQ (GIC_SPI_START + 35) +#define Q6SW_WDOG_EXPIRED_IRQ (GIC_SPI_START + 36) +#define MSS_TO_APPS_IRQ_0 (GIC_SPI_START + 37) +#define MSS_TO_APPS_IRQ_1 (GIC_SPI_START + 38) +#define MSS_TO_APPS_IRQ_2 (GIC_SPI_START + 39) +#define MSS_TO_APPS_IRQ_3 (GIC_SPI_START + 40) +#define MSS_TO_APPS_IRQ_4 (GIC_SPI_START + 41) +#define MSS_TO_APPS_IRQ_5 (GIC_SPI_START + 42) +#define MSS_TO_APPS_IRQ_6 (GIC_SPI_START + 43) +#define MSS_TO_APPS_IRQ_7 (GIC_SPI_START + 44) +#define MSS_TO_APPS_IRQ_8 (GIC_SPI_START + 45) +#define MSS_TO_APPS_IRQ_9 (GIC_SPI_START + 46) +/* 47-84 Reserved */ +#define LPASS_SCSS_AUDIO_IF_OUT0_IRQ (GIC_SPI_START + 85) +#define LPASS_SCSS_MIDI_IRQ (GIC_SPI_START + 86) +#define LPASS_Q6SS_WDOG_EXPIRED (GIC_SPI_START + 87) +#define LPASS_SCSS_GP_LOW_IRQ (GIC_SPI_START + 88) +#define LPASS_SCSS_GP_MEDIUM_IRQ (GIC_SPI_START + 89) +#define LPASS_SCSS_GP_HIGH_IRQ (GIC_SPI_START + 90) +#define TOP_IMEM_IRQ (GIC_SPI_START + 91) +#define FABRIC_SYS_IRQ (GIC_SPI_START + 92) +/* 93 Reserved */ +#define USB1_HS_BAM_IRQ (GIC_SPI_START + 94) +/* 95,96 unnamed */ +#define SDC2_BAM_IRQ (GIC_SPI_START + 97) +#define SDC1_BAM_IRQ (GIC_SPI_START + 98) +#define FABRIC_SPS_IRQ (GIC_SPI_START + 99) +#define USB1_HS_IRQ (GIC_SPI_START + 100) +/* 101,102 unnamed */ +#define SDC2_IRQ_0 (GIC_SPI_START + 103) +#define SDC1_IRQ_0 (GIC_SPI_START + 104) +#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105) +#define SPS_SEC_VIOL_IRQ (GIC_SPI_START + 106) +#define SPS_MTI_0 (GIC_SPI_START + 107) +#define SPS_MTI_1 (GIC_SPI_START + 108) +#define SPS_MTI_2 (GIC_SPI_START + 109) +#define SPS_MTI_3 (GIC_SPI_START + 110) +#define SPS_MTI_4 (GIC_SPI_START + 111) +#define SPS_MTI_5 (GIC_SPI_START + 112) +#define SPS_MTI_6 (GIC_SPI_START + 113) +#define SPS_MTI_7 (GIC_SPI_START + 114) +#define SPS_MTI_8 (GIC_SPI_START + 115) +#define SPS_MTI_9 (GIC_SPI_START + 116) +#define SPS_MTI_10 (GIC_SPI_START + 117) +#define SPS_MTI_11 (GIC_SPI_START + 118) +#define SPS_MTI_12 (GIC_SPI_START + 119) +#define SPS_MTI_13 (GIC_SPI_START + 120) +#define SPS_MTI_14 (GIC_SPI_START + 121) +#define SPS_MTI_15 (GIC_SPI_START + 122) +#define SPS_MTI_16 (GIC_SPI_START + 123) +#define SPS_MTI_17 (GIC_SPI_START + 124) +#define SPS_MTI_18 (GIC_SPI_START + 125) +#define SPS_MTI_19 (GIC_SPI_START + 126) +#define SPS_MTI_20 (GIC_SPI_START + 127) +#define SPS_MTI_21 (GIC_SPI_START + 128) +#define SPS_MTI_22 (GIC_SPI_START + 129) +#define SPS_MTI_23 (GIC_SPI_START + 130) +#define SPS_MTI_24 (GIC_SPI_START + 131) +#define SPS_MTI_25 (GIC_SPI_START + 132) +#define SPS_MTI_26 (GIC_SPI_START + 133) +#define SPS_MTI_27 (GIC_SPI_START + 134) +#define SPS_MTI_28 (GIC_SPI_START + 135) +#define SPS_MTI_29 (GIC_SPI_START + 136) +#define SPS_MTI_30 (GIC_SPI_START + 137) +#define SPS_MTI_31 (GIC_SPI_START + 138) +#define CSIPHY_0_4LN_IRQ (GIC_SPI_START + 139) +#define CSIPHY_1_2LN_IRQ (GIC_SPI_START + 140) +/* 141-145 Reserved */ +#define GSBI1_UARTDM_IRQ (GIC_SPI_START + 146) +#define GSBI1_QUP_IRQ (GIC_SPI_START + 147) +#define GSBI2_UARTDM_IRQ (GIC_SPI_START + 148) +#define GSBI2_QUP_IRQ (GIC_SPI_START + 149) +#define GSBI3_UARTDM_IRQ (GIC_SPI_START + 150) +#define GSBI3_QUP_IRQ (GIC_SPI_START + 151) +#define GSBI4_UARTDM_IRQ (GIC_SPI_START + 152) +#define GSBI4_QUP_IRQ (GIC_SPI_START + 153) +#define GSBI5_UARTDM_IRQ (GIC_SPI_START + 154) +#define GSBI5_QUP_IRQ (GIC_SPI_START + 155) +/* 156-167 Reserved */ +#define MSMC_SC_SEC_TMR_IRQ (GIC_SPI_START + 168) +#define MSMC_SC_SEC_WDOG_BARK_IRQ (GIC_SPI_START + 169) +#define ADM_0_SCSS_0_IRQ (GIC_SPI_START + 170) +#define ADM_0_SCSS_1_IRQ (GIC_SPI_START + 171) +#define ADM_0_SCSS_2_IRQ (GIC_SPI_START + 172) +#define ADM_0_SCSS_3_IRQ (GIC_SPI_START + 173) +/* 174 Reserved */ +#define CC_SCSS_WDT1CPU0BITEEXPIRED (GIC_SPI_START + 175) +/* 176 Reserved */ +#define CC_SCSS_WDT0CPU0BITEEXPIRED (GIC_SPI_START + 177) +#define TSENS_UPPER_LOWER_INT (GIC_SPI_START + 178) +/* 179-182 Reserved */ +#define XPU_SUMMARY_IRQ (GIC_SPI_START + 183) +#define BUS_EXCEPTION_SUMMARY_IRQ (GIC_SPI_START + 184) +#define HSDDRX_EBI1CH0_IRQ (GIC_SPI_START + 185) +/* 186-208 Reserved */ +#define A2_BAM_IRQ (GIC_SPI_START + 209) +/* 210-215 Reserved */ +#define QDSS_ETB_IRQ (GIC_SPI_START + 216) +/* 216 Reserved */ +#define QDSS_CTI2KPSS_CPU0_IRQ (GIC_SPI_START + 218) +#define TLMM_MSM_DIR_CONN_IRQ_16 (GIC_SPI_START + 219) +#define TLMM_MSM_DIR_CONN_IRQ_17 (GIC_SPI_START + 220) +#define TLMM_MSM_DIR_CONN_IRQ_18 (GIC_SPI_START + 221) +#define TLMM_MSM_DIR_CONN_IRQ_19 (GIC_SPI_START + 222) +#define TLMM_MSM_DIR_CONN_IRQ_20 (GIC_SPI_START + 223) +#define TLMM_MSM_DIR_CONN_IRQ_21 (GIC_SPI_START + 224) +#define MSM_SPARE0_IRQ (GIC_SPI_START + 225) +#define PMIC_SEC_IRQ_N (GIC_SPI_START + 226) +#define USB_HSIC_BAM_IRQ (GIC_SPI_START + 231) +#define USB_HSIC_IRQ (GIC_SPI_START + 232) + +#define NR_MSM_IRQS 288 +#define NR_GPIO_IRQS 88 +#define NR_PM8018_IRQS 256 +#define NR_WCD9XXX_IRQS 49 +#define NR_TABLA_IRQS NR_WCD9XXX_IRQS +#define NR_BOARD_IRQS (NR_PM8018_IRQS + NR_WCD9XXX_IRQS) +#define NR_TLMM_MSM_DIR_CONN_IRQ 8 /*Need to Verify this Count*/ +#define NR_MSM_GPIOS NR_GPIO_IRQS + +/* Backwards compatible IRQ macros. */ +#define INT_ADM_AARM ADM_0_SCSS_0_IRQ + +/* smd/smsm interrupts */ +#define INT_A9_M2A_0 MSS_TO_APPS_IRQ_0 +#define INT_A9_M2A_5 MSS_TO_APPS_IRQ_1 +#define INT_ADSP_A11 LPASS_SCSS_GP_HIGH_IRQ +#define INT_ADSP_A11_SMSM LPASS_SCSS_GP_MEDIUM_IRQ + +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs-9625.h b/arch/arm/mach-msm/include/mach/irqs-9625.h new file mode 100644 index 00000000000..91b4d070f41 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-9625.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IRQS_9625_H +#define __ASM_ARCH_MSM_IRQS_9625_H + +/* MSM ACPU Interrupt Numbers */ + +/* + * 0-15: STI/SGI (software triggered/generated interrupts) + * 16-31: PPI (private peripheral interrupts) + * 32+: SPI (shared peripheral interrupts) + */ + + +#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1) +#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 16) +#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 208) + +#define NR_MSM_IRQS 288 +#define NR_GPIO_IRQS 88 +#define NR_BOARD_IRQS 0 +#define NR_TLMM_MSM_DIR_CONN_IRQ 8 /*Need to Verify this Count*/ +#define NR_MSM_GPIOS NR_GPIO_IRQS + +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs-copper.h b/arch/arm/mach-msm/include/mach/irqs-copper.h new file mode 100644 index 00000000000..8cd8620be57 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-copper.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IRQS_COPPER_H +#define __ASM_ARCH_MSM_IRQS_COPPER_H + +/* MSM ACPU Interrupt Numbers */ + +/* + * 0-15: STI/SGI (software triggered/generated interrupts) + * 16-31: PPI (private peripheral interrupts) + * 32+: SPI (shared peripheral interrupts) + */ + +#define GIC_PPI_START 16 +#define GIC_SPI_START 32 + +#define AVS_SVICINT (GIC_PPI_START + 6) +#define AVS_SVICINTSWDONE (GIC_PPI_START + 7) +#define INT_ARMQC_PERFMON (GIC_PPI_START + 10) +/* PPI 15 is unused */ + +#define APCC_QGICL2PERFMONIRPTREQ (GIC_SPI_START + 1) +#define SC_SICL2PERFMONIRPTREQ APCC_QGICL2PERFMONIRPTREQ +#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208) +#define SPS_BAM_DMA_IRQ (GIC_SPI_START + 105) + +#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */ +#define NR_GPIO_IRQS 146 +#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */ +#define NR_BOARD_IRQS NR_QPNP_IRQS +#define NR_TLMM_MSM_DIR_CONN_IRQ 8 +#define NR_MSM_GPIOS NR_GPIO_IRQS + +#endif + diff --git a/arch/arm/mach-msm/include/mach/irqs-fsm9xxx.h b/arch/arm/mach-msm/include/mach/irqs-fsm9xxx.h new file mode 100644 index 00000000000..721db1db50d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-fsm9xxx.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ASM_ARCH_MSM_IRQS_FSM9XXX_H +#define __ASM_ARCH_MSM_IRQS_FSM9XXX_H + +/* MSM ACPU Interrupt Numbers */ + +#define INT_DEBUG_TIMER_EXP 0 +#define INT_GPT0_TIMER_EXP 1 +#define INT_GPT1_TIMER_EXP 2 +#define INT_WDT0_ACCSCSSBARK 3 +#define INT_WDT1_ACCSCSSBARK 4 +#define INT_AVS_SVIC 5 +#define INT_AVS_SVIC_SW_DONE 6 +#define INT_SC_DBG_RX_FULL 7 +#define INT_SC_DBG_TX_EMPTY 8 +#define INT_ARMQC_PERFMON 9 +#define INT_AVS_REQ_DOWN 10 +#define INT_AVS_REQ_UP 11 +#define INT_SC_ACG 12 +/* SCSS_VICFIQSTS0[13:15] are RESERVED */ +#define INT_BPU_CPU 16 +#define INT_L2_SVICDMANSIRPTREQ 17 +#define INT_L2_SVICDMASIRPTREQ 18 +#define INT_L2_SVICSLVIRPTREQ 19 +#define INT_SEAWOLF_IRQ0 20 +#define INT_SEAWOLF_IRQ1 21 +#define INT_SEAWOLF_IRQ2 22 +#define INT_SEAWOLF_IRQ3 23 +#define INT_CARIBE_SUPSS_IRQ 24 +#define INT_ADM_SEC0_IRQ 25 +/* SCSS_VICFIQSTS0[26] is RESERVED */ +#define INT_GMII_PHY 27 +#define INT_SBD_IRQ 28 +#define INT_HH_SUPSS_IRQ 29 +#define INT_EMAC_SBD_IRQ 30 +#define INT_PERPH_SUPSS_IRQ 31 + +#define INT_Q6_SW_IRQ_0 (32 + 0) +#define INT_Q6_SW_IRQ_1 (32 + 1) +#define INT_Q6_SW_IRQ_2 (32 + 2) +#define INT_Q6_SW_IRQ_3 (32 + 3) +#define INT_Q6_SW_IRQ_4 (32 + 4) +#define INT_Q6_SW_IRQ_5 (32 + 5) +#define INT_Q6_SW_IRQ_6 (32 + 6) +#define INT_Q6_SW_IRQ_7 (32 + 7) +#define INT_IMEM_IRQ (32 + 8) +#define INT_IMEM_ECC_IRQ (32 + 9) +#define INT_HSDDRX_IRQ (32 + 10) +#define INT_BUFMEM_XPU_IRQ (32 + 11) +#define INT_A9_M2A_0 (32 + 12) +#define INT_A9_M2A_1 (32 + 13) +#define INT_A9_M2A_2 (32 + 14) +#define INT_A9_M2A_3 (32 + 15) +#define INT_A9_M2A_4 (32 + 16) +#define INT_A9_M2A_5 (32 + 17) +#define INT_A9_M2A_6 (32 + 18) +#define INT_A9_M2A_7 (32 + 19) +#define INT_SC_PRI_IRQ (32 + 20) +#define INT_SC_SEC_IRQ (32 + 21) +#define INT_Q6_WDOG_IRQ (32 + 22) +#define INT_ADM_SEC3_IRQ (32 + 23) +#define INT_ARM_WAKE_IRQ (32 + 24) +#define INT_ARM_WDOG_IRQ (32 + 25) +#define INT_SUPSS_CFG_XPU_IRQ (32 + 26) +#define INT_SPB_XPU_IRQ (32 + 27) +#define INT_FPB_XPU_IRQ (32 + 28) +#define INT_Q6_XPU_IRQ (32 + 29) +/* SCSS_VICFIQSTS1[30:31] are RESERVED */ +/* SCSS_VICFIQSTS2[0:31] are RESERVED */ +/* SCSS_VICFIQSTS3[0:31] are RESERVED */ + +/* Retrofit universal macro names */ +#define INT_ADM_AARM INT_ADM_SEC3_IRQ +#define INT_GP_TIMER_EXP INT_GPT0_TIMER_EXP +#define INT_ADSP_A11 INT_Q6_SW_IRQ_0 +#define INT_ADSP_A11_SMSM INT_ADSP_A11 +#define INT_SIRC_0 INT_PERPH_SUPSS_IRQ +#define WDT0_ACCSCSSNBARK_INT INT_WDT0_ACCSCSSBARK + +#define NR_MSM_IRQS 128 +#define NR_GPIO_IRQS 0 +#define PMIC8058_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS + NR_SIRC_IRQS) +#define NR_PMIC8058_IRQS 256 +#define NR_BOARD_IRQS (NR_SIRC_IRQS + NR_PMIC8058_IRQS) + +#define NR_MSM_GPIOS 168 + +#endif /* __ASM_ARCH_MSM_IRQS_FSM9XXX_H */ diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h index 3cd78b165ab..d630799afe5 100644 --- a/arch/arm/mach-msm/include/mach/irqs.h +++ b/arch/arm/mach-msm/include/mach/irqs.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -19,24 +19,85 @@ #define MSM_IRQ_BIT(irq) (1 << ((irq) & 31)) -#if defined(CONFIG_ARCH_MSM7X30) +#include "irqs-8625.h" + +#if defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \ + defined(CONFIG_ARCH_MSM8930) + +#ifdef CONFIG_ARCH_MSM8960 +#include "irqs-8960.h" +#endif + +#ifdef CONFIG_ARCH_MSM8930 +#include "irqs-8930.h" +#endif + +#ifdef CONFIG_ARCH_APQ8064 +#include "irqs-8064.h" +#endif + +/* For now, use the maximum number of interrupts until a pending GIC issue + * is sorted out */ +#define NR_MSM_IRQS 288 +#define NR_GPIO_IRQS 152 +#define NR_PM8921_IRQS 256 +#define NR_PM8821_IRQS 112 +#define NR_WCD9XXX_IRQS 49 +#define NR_TABLA_IRQS NR_WCD9XXX_IRQS +#define NR_GPIO_EXPANDER_IRQS 64 +#ifdef CONFIG_PCI_MSI +#define NR_PCIE_MSI_IRQS 256 +#define NR_BOARD_IRQS (NR_PM8921_IRQS + NR_PM8821_IRQS + \ + NR_WCD9XXX_IRQS + NR_GPIO_EXPANDER_IRQS + NR_PCIE_MSI_IRQS) +#else +#define NR_BOARD_IRQS (NR_PM8921_IRQS + NR_PM8821_IRQS + \ + NR_WCD9XXX_IRQS + NR_GPIO_EXPANDER_IRQS) +#endif +#define NR_TLMM_MSM_DIR_CONN_IRQ 8 /*Need to Verify this Count*/ +#define NR_MSM_GPIOS NR_GPIO_IRQS + +#else + +#if defined(CONFIG_ARCH_MSMCOPPER) +#include "irqs-copper.h" +#elif defined(CONFIG_ARCH_MSM9615) +#include "irqs-9615.h" +#elif defined(CONFIG_ARCH_MSM9625) +#include "irqs-9625.h" +#elif defined(CONFIG_ARCH_MSM7X30) #include "irqs-7x30.h" #elif defined(CONFIG_ARCH_QSD8X50) #include "irqs-8x50.h" #include "sirc.h" #elif defined(CONFIG_ARCH_MSM8X60) #include "irqs-8x60.h" -#elif defined(CONFIG_ARCH_MSM8960) -/* TODO: Make these not generic. */ -#include "irqs-8960.h" -#elif defined(CONFIG_ARCH_MSM_ARM11) -#include "irqs-7x00.h" +#elif defined(CONFIG_ARCH_MSM7X01A) || defined(CONFIG_ARCH_MSM7X25) \ + || defined(CONFIG_ARCH_MSM7X27) +#include "irqs-7xxx.h" + +#define NR_GPIO_IRQS 133 +#define NR_MSM_IRQS 256 +#define NR_BOARD_IRQS 256 +#define NR_MSM_GPIOS NR_GPIO_IRQS +#elif defined(CONFIG_ARCH_FSM9XXX) +#include "irqs-fsm9xxx.h" +#include "sirc.h" #else #error "Unknown architecture specification" #endif +#endif + +#if !defined(CONFIG_SPARSE_IRQ) #define NR_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) #define MSM_GPIO_TO_INT(n) (NR_MSM_IRQS + (n)) +#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0) #define MSM_INT_TO_REG(base, irq) (base + irq / 32) +#endif + +#if defined(CONFIG_PCI_MSI) && defined(CONFIG_MSM_PCIE) +#define MSM_PCIE_MSI_INT(n) (NR_MSM_IRQS + NR_GPIO_IRQS + NR_PM8921_IRQS + \ + NR_PM8821_IRQS + NR_TABLA_IRQS + NR_GPIO_EXPANDER_IRQS + (n)) +#endif #endif diff --git a/arch/arm/mach-msm/include/mach/mdm-peripheral.h b/arch/arm/mach-msm/include/mach/mdm-peripheral.h new file mode 100644 index 00000000000..0f3bd33b528 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/mdm-peripheral.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_MDM_PERIPHERAL_H +#define _ARCH_ARM_MACH_MSM_MDM_PERIPHERAL_H_ + +extern void peripheral_connect(void); +extern void peripheral_disconnect(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/mdm.h b/arch/arm/mach-msm/include/mach/mdm.h new file mode 100644 index 00000000000..f0100feb205 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/mdm.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_MDM_H +#define _ARXH_ARM_MACH_MSM_MDM_H + + +struct charm_platform_data { + void (*charm_modem_on)(void); + void (*charm_modem_off)(void); +}; + +#define AP2MDM_STATUS 136 +#define MDM2AP_STATUS 134 +#define MDM2AP_WAKEUP 40 +#define MDM2AP_ERRFATAL 133 +#define AP2MDM_ERRFATAL 93 + +#define AP2MDM_PMIC_RESET_N 131 +#define AP2MDM_KPDPWR_N 132 +#define AP2PMIC_TMPNI_CKEN 141 +#define AP2MDM_WAKEUP 135 + +extern void (*charm_intentional_reset)(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/mdm2.h b/arch/arm/mach-msm/include/mach/mdm2.h new file mode 100644 index 00000000000..997b3be4e6a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/mdm2.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_MDM2_H +#define _ARCH_ARM_MACH_MSM_MDM2_H + +struct mdm_platform_data { + char *mdm_version; + int ramdump_delay_ms; + int soft_reset_inverted; + int early_power_on; + int sfr_query; + struct platform_device *peripheral_platform_device; +}; + +#endif + diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h new file mode 100644 index 00000000000..259636478b9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -0,0 +1,138 @@ +/* arch/arm/mach-msm/include/mach/memory.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H +#include + +/* physical offset of RAM */ +#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET) + +#define MAX_PHYSMEM_BITS 32 +#define SECTION_SIZE_BITS 28 + +/* Maximum number of Memory Regions +* The largest system can have 4 memory banks, each divided into 8 regions +*/ +#define MAX_NR_REGIONS 32 + +/* The number of regions each memory bank is divided into */ +#define NR_REGIONS_PER_BANK 8 + +/* Certain configurations of MSM7x30 have multiple memory banks. +* One or more of these banks can contain holes in the memory map as well. +* These macros define appropriate conversion routines between the physical +* and virtual address domains for supporting these configurations using +* SPARSEMEM and a 3G/1G VM split. +*/ + +#if defined(CONFIG_ARCH_MSM7X30) + +#define EBI0_PHYS_OFFSET PHYS_OFFSET +#define EBI0_PAGE_OFFSET PAGE_OFFSET +#define EBI0_SIZE 0x10000000 + +#ifndef __ASSEMBLY__ + +extern unsigned long ebi1_phys_offset; + +#define EBI1_PHYS_OFFSET (ebi1_phys_offset) +#define EBI1_PAGE_OFFSET (EBI0_PAGE_OFFSET + EBI0_SIZE) + +#if (defined(CONFIG_SPARSEMEM) && defined(CONFIG_VMSPLIT_3G)) + +#define __phys_to_virt(phys) \ + ((phys) >= EBI1_PHYS_OFFSET ? \ + (phys) - EBI1_PHYS_OFFSET + EBI1_PAGE_OFFSET : \ + (phys) - EBI0_PHYS_OFFSET + EBI0_PAGE_OFFSET) + +#define __virt_to_phys(virt) \ + ((virt) >= EBI1_PAGE_OFFSET ? \ + (virt) - EBI1_PAGE_OFFSET + EBI1_PHYS_OFFSET : \ + (virt) - EBI0_PAGE_OFFSET + EBI0_PHYS_OFFSET) + +#endif +#endif + +#endif + +#ifndef __ASSEMBLY__ +void *alloc_bootmem_aligned(unsigned long size, unsigned long alignment); +void *allocate_contiguous_ebi(unsigned long, unsigned long, int); +unsigned long allocate_contiguous_ebi_nomap(unsigned long, unsigned long); +void clean_and_invalidate_caches(unsigned long, unsigned long, unsigned long); +void clean_caches(unsigned long, unsigned long, unsigned long); +void invalidate_caches(unsigned long, unsigned long, unsigned long); +int platform_physical_remove_pages(u64, u64); +int platform_physical_active_pages(u64, u64); +int platform_physical_low_power_pages(u64, u64); + +extern int (*change_memory_power)(u64, u64, int); + +#if defined(CONFIG_ARCH_MSM_ARM11) || defined(CONFIG_ARCH_MSM_CORTEX_A5) +void write_to_strongly_ordered_memory(void); +void map_page_strongly_ordered(void); +#endif + +#ifdef CONFIG_CACHE_L2X0 +extern void l2x0_cache_sync(void); +#define finish_arch_switch(prev) do { l2x0_cache_sync(); } while (0) +#endif + +#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) +extern void store_ttbr0(void); +#define finish_arch_switch(prev) do { store_ttbr0(); } while (0) +#endif + +#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0 +extern unsigned long membank0_size; +extern unsigned long membank1_start; +void find_membank0_hole(void); + +#define MEMBANK0_PHYS_OFFSET PHYS_OFFSET +#define MEMBANK0_PAGE_OFFSET PAGE_OFFSET + +#define MEMBANK1_PHYS_OFFSET (membank1_start) +#define MEMBANK1_PAGE_OFFSET (MEMBANK0_PAGE_OFFSET + (membank0_size)) + +#define __phys_to_virt(phys) \ + ((MEMBANK1_PHYS_OFFSET && ((phys) >= MEMBANK1_PHYS_OFFSET)) ? \ + (phys) - MEMBANK1_PHYS_OFFSET + MEMBANK1_PAGE_OFFSET : \ + (phys) - MEMBANK0_PHYS_OFFSET + MEMBANK0_PAGE_OFFSET) + +#define __virt_to_phys(virt) \ + ((MEMBANK1_PHYS_OFFSET && ((virt) >= MEMBANK1_PAGE_OFFSET)) ? \ + (virt) - MEMBANK1_PAGE_OFFSET + MEMBANK1_PHYS_OFFSET : \ + (virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET) +#endif + +#endif + +#if defined CONFIG_ARCH_MSM_SCORPION || defined CONFIG_ARCH_MSM_KRAIT +#define arch_has_speculative_dfetch() 1 +#endif + +#endif + +/* these correspond to values known by the modem */ +#define MEMORY_DEEP_POWERDOWN 0 +#define MEMORY_SELF_REFRESH 1 +#define MEMORY_ACTIVE 2 + +#define NPA_MEMORY_NODE_NAME "/mem/apps/ddr_dpd" + +#ifndef CONFIG_ARCH_MSM7X27 +#define CONSISTENT_DMA_SIZE (SZ_1M * 14) +#endif diff --git a/arch/arm/mach-msm/include/mach/mpm.h b/arch/arm/mach-msm/include/mach/mpm.h new file mode 100644 index 00000000000..10a6fb0c93c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/mpm.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_MPM_H +#define __ARCH_ARM_MACH_MSM_MPM_H + +#include +#include + +#define MSM_MPM_NR_MPM_IRQS 64 + +struct msm_mpm_device_data { + uint16_t *irqs_m2a; + unsigned int irqs_m2a_size; + uint16_t *bypassed_apps_irqs; + unsigned int bypassed_apps_irqs_size; + void __iomem *mpm_request_reg_base; + void __iomem *mpm_status_reg_base; + void __iomem *mpm_apps_ipc_reg; + unsigned int mpm_apps_ipc_val; + unsigned int mpm_ipc_irq; +}; + +extern struct msm_mpm_device_data msm8660_mpm_dev_data; +extern struct msm_mpm_device_data msm8960_mpm_dev_data; +extern struct msm_mpm_device_data msm9615_mpm_dev_data; +extern struct msm_mpm_device_data apq8064_mpm_dev_data; + +void msm_mpm_irq_extn_init(struct msm_mpm_device_data *mpm_data); + +#ifdef CONFIG_MSM_MPM +int msm_mpm_enable_pin(unsigned int pin, unsigned int enable); +int msm_mpm_set_pin_wake(unsigned int pin, unsigned int on); +int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type); +bool msm_mpm_irqs_detectable(bool from_idle); +bool msm_mpm_gpio_irqs_detectable(bool from_idle); +void msm_mpm_enter_sleep(bool from_idle); +void msm_mpm_exit_sleep(bool from_idle); +#else +static inline int msm_mpm_enable_irq(unsigned int irq, unsigned int enable) +{ return -ENODEV; } +static inline int msm_mpm_set_irq_wake(unsigned int irq, unsigned int on) +{ return -ENODEV; } +static inline int msm_mpm_set_irq_type(unsigned int irq, unsigned int flow_type) +{ return -ENODEV; } +static inline int msm_mpm_enable_pin(unsigned int pin, unsigned int enable) +{ return -ENODEV; } +static inline int msm_mpm_set_pin_wake(unsigned int pin, unsigned int on) +{ return -ENODEV; } +static inline int msm_mpm_set_pin_type(unsigned int pin, + unsigned int flow_type) +{ return -ENODEV; } +static inline bool msm_mpm_irqs_detectable(bool from_idle) +{ return false; } +static inline bool msm_mpm_gpio_irqs_detectable(bool from_idle) +{ return false; } +static inline void msm_mpm_enter_sleep(bool from_idle) {} +static inline void msm_mpm_exit_sleep(bool from_idle) {} +#endif + + + +#endif /* __ARCH_ARM_MACH_MSM_MPM_H */ diff --git a/arch/arm/mach-msm/include/mach/mpp.h b/arch/arm/mach-msm/include/mach/mpp.h new file mode 100644 index 00000000000..8ac1f543875 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/mpp.h @@ -0,0 +1,276 @@ +/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_MPP_H +#define __ARCH_ARM_MACH_MSM_MPP_H + +#ifdef CONFIG_PMIC8058 +#define MPPS 12 +#else +#define MPPS 22 +#endif + +/* Digital Logical Output Level */ +enum { + MPP_DLOGIC_LVL_MSME, + MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_LVL_RUIM, + MPP_DLOGIC_LVL_MMC, + MPP_DLOGIC_LVL_VDD, +}; + +/* Digital Logical Output Control Value */ +enum { + MPP_DLOGIC_OUT_CTRL_LOW, + MPP_DLOGIC_OUT_CTRL_HIGH, + MPP_DLOGIC_OUT_CTRL_MPP, /* MPP Output = MPP Input */ + MPP_DLOGIC_OUT_CTRL_NOT_MPP, /* MPP Output = Inverted MPP Input */ +}; + +/* Digital Logical Input Value */ +enum { + MPP_DLOGIC_IN_DBUS_NONE, + MPP_DLOGIC_IN_DBUS_1, + MPP_DLOGIC_IN_DBUS_2, + MPP_DLOGIC_IN_DBUS_3, +}; + +#define MPP_CFG(level, control) ((((level) & 0x0FFFF) << 16) | \ + ((control) & 0x0FFFF)) +#define MPP_CFG_INPUT(level, dbus) ((((level) & 0x0FFFF) << 16) | \ + ((dbus) & 0x0FFFF)) + +/* Use mpp number starting from 0 */ +int mpp_config_digital_out(unsigned mpp, unsigned config); +int mpp_config_digital_in(unsigned mpp, unsigned config); + +/* PM8058/PM8901 definitions */ + +/* APIs */ +#ifdef CONFIG_PMIC8058 +int pm8058_mpp_config(unsigned mpp, unsigned type, unsigned level, + unsigned control); +#else +static inline int pm8058_mpp_config(unsigned mpp, unsigned type, + unsigned level, unsigned control) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_PMIC8901 +int pm8901_mpp_config(unsigned mpp, unsigned type, unsigned level, + unsigned control); +#else +static inline int pm8901_mpp_config(unsigned mpp, unsigned type, + unsigned level, unsigned control) +{ + return -EINVAL; +} +#endif + +/* MPP Type: type */ +#define PM_MPP_TYPE_D_INPUT 0 +#define PM_MPP_TYPE_D_OUTPUT 1 +#define PM_MPP_TYPE_D_BI_DIR 2 +#define PM_MPP_TYPE_A_INPUT 3 +#define PM_MPP_TYPE_A_OUTPUT 4 +#define PM_MPP_TYPE_SINK 5 +#define PM_MPP_TYPE_DTEST_SINK 6 +#define PM_MPP_TYPE_DTEST_OUTPUT 7 + + +/* Digital Input/Output: level [8058] */ +#define PM8058_MPP_DIG_LEVEL_VPH 0 +#define PM8058_MPP_DIG_LEVEL_S3 1 +#define PM8058_MPP_DIG_LEVEL_L2 2 +#define PM8058_MPP_DIG_LEVEL_L3 3 + +/* Digital Input/Output: level [8901] */ +#define PM8901_MPP_DIG_LEVEL_MSMIO 0 +#define PM8901_MPP_DIG_LEVEL_DIG 1 +#define PM8901_MPP_DIG_LEVEL_L5 2 +#define PM8901_MPP_DIG_LEVEL_S4 3 +#define PM8901_MPP_DIG_LEVEL_VPH 4 + +/* Digital Input: control */ +#define PM_MPP_DIN_TO_INT 0 +#define PM_MPP_DIN_TO_DBUS1 1 +#define PM_MPP_DIN_TO_DBUS2 2 +#define PM_MPP_DIN_TO_DBUS3 3 + +/* Digital Output: control */ +#define PM_MPP_DOUT_CTL_LOW 0 +#define PM_MPP_DOUT_CTL_HIGH 1 +#define PM_MPP_DOUT_CTL_MPP 2 +#define PM_MPP_DOUT_CTL_INV_MPP 3 + +/* Bidirectional: control */ +#define PM_MPP_BI_PULLUP_1KOHM 0 +#define PM_MPP_BI_PULLUP_OPEN 1 +#define PM_MPP_BI_PULLUP_10KOHM 2 +#define PM_MPP_BI_PULLUP_30KOHM 3 + +/* Analog Input: level */ +#define PM_MPP_AIN_AMUX_CH5 0 +#define PM_MPP_AIN_AMUX_CH6 1 +#define PM_MPP_AIN_AMUX_CH7 2 +#define PM_MPP_AIN_AMUX_CH8 3 +#define PM_MPP_AIN_AMUX_CH9 4 +#define PM_MPP_AIN_AMUX_ABUS1 5 +#define PM_MPP_AIN_AMUX_ABUS2 6 +#define PM_MPP_AIN_AMUX_ABUS3 7 + +/* Analog Output: level */ +#define PM_MPP_AOUT_LVL_1V25 0 +#define PM_MPP_AOUT_LVL_1V25_2 1 +#define PM_MPP_AOUT_LVL_0V625 2 +#define PM_MPP_AOUT_LVL_0V3125 3 +#define PM_MPP_AOUT_LVL_MPP 4 +#define PM_MPP_AOUT_LVL_ABUS1 5 +#define PM_MPP_AOUT_LVL_ABUS2 6 +#define PM_MPP_AOUT_LVL_ABUS3 7 + +/* Analog Output: control */ +#define PM_MPP_AOUT_CTL_DISABLE 0 +#define PM_MPP_AOUT_CTL_ENABLE 1 +#define PM_MPP_AOUT_CTL_MPP_HIGH_EN 2 +#define PM_MPP_AOUT_CTL_MPP_LOW_EN 3 + +/* Current Sink: level */ +#define PM_MPP_CS_OUT_5MA 0 +#define PM_MPP_CS_OUT_10MA 1 +#define PM_MPP_CS_OUT_15MA 2 +#define PM_MPP_CS_OUT_20MA 3 +#define PM_MPP_CS_OUT_25MA 4 +#define PM_MPP_CS_OUT_30MA 5 +#define PM_MPP_CS_OUT_35MA 6 +#define PM_MPP_CS_OUT_40MA 7 + +/* Current Sink: control */ +#define PM_MPP_CS_CTL_DISABLE 0 +#define PM_MPP_CS_CTL_ENABLE 1 +#define PM_MPP_CS_CTL_MPP_HIGH_EN 2 +#define PM_MPP_CS_CTL_MPP_LOW_EN 3 + +/* DTEST Current Sink: control */ +#define PM_MPP_DTEST_CS_CTL_EN1 0 +#define PM_MPP_DTEST_CS_CTL_EN2 1 +#define PM_MPP_DTEST_CS_CTL_EN3 2 +#define PM_MPP_DTEST_CS_CTL_EN4 3 + +/* DTEST Digital Output: control */ +#define PM_MPP_DTEST_DBUS1 0 +#define PM_MPP_DTEST_DBUS2 1 +#define PM_MPP_DTEST_DBUS3 2 +#define PM_MPP_DTEST_DBUS4 3 + +/* Helper APIs */ +static inline int pm8058_mpp_config_digital_in(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_D_INPUT, level, control); +} + +static inline int pm8058_mpp_config_digital_out(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_D_OUTPUT, level, control); +} + +static inline int pm8058_mpp_config_bi_dir(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_D_BI_DIR, level, control); +} + +static inline int pm8058_mpp_config_analog_input(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_A_INPUT, level, control); +} + +static inline int pm8058_mpp_config_analog_output(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_A_OUTPUT, level, control); +} + +static inline int pm8058_mpp_config_current_sink(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_SINK, level, control); +} + +static inline int pm8058_mpp_config_dtest_sink(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_DTEST_SINK, level, control); +} + +static inline int pm8058_mpp_config_dtest_output(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8058_mpp_config(mpp, PM_MPP_TYPE_DTEST_OUTPUT, + level, control); +} + +static inline int pm8901_mpp_config_digital_in(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_D_INPUT, level, control); +} + +static inline int pm8901_mpp_config_digital_out(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_D_OUTPUT, level, control); +} + +static inline int pm8901_mpp_config_bi_dir(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_D_BI_DIR, level, control); +} + +static inline int pm8901_mpp_config_analog_input(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_A_INPUT, level, control); +} + +static inline int pm8901_mpp_config_analog_output(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_A_OUTPUT, level, control); +} + +static inline int pm8901_mpp_config_current_sink(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_SINK, level, control); +} + +static inline int pm8901_mpp_config_dtest_sink(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_DTEST_SINK, level, control); +} + +static inline int pm8901_mpp_config_dtest_output(unsigned mpp, unsigned level, + unsigned control) +{ + return pm8901_mpp_config(mpp, PM_MPP_TYPE_DTEST_OUTPUT, + level, control); +} +#endif diff --git a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h new file mode 100644 index 00000000000..f835e828ed1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h @@ -0,0 +1,20 @@ +#ifndef __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H +#define __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H + +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +extern void set_l2_indirect_reg(u32 reg_addr, u32 val); +extern u32 get_l2_indirect_reg(u32 reg_addr); +extern u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm72k_otg.h b/arch/arm/mach-msm/include/mach/msm72k_otg.h new file mode 100644 index 00000000000..623de2ae15c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm72k_otg.h @@ -0,0 +1,171 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_GADGET_MSM72K_OTG_H__ +#define __LINUX_USB_GADGET_MSM72K_OTG_H__ + +#include +#include +#include +#include +#include + +#include +#include + +#define OTGSC_BSVIE (1 << 27) +#define OTGSC_IDIE (1 << 24) +#define OTGSC_IDPU (1 << 5) +#define OTGSC_BSVIS (1 << 19) +#define OTGSC_ID (1 << 8) +#define OTGSC_IDIS (1 << 16) +#define OTGSC_BSV (1 << 11) +#define OTGSC_DPIE (1 << 30) +#define OTGSC_DPIS (1 << 22) +#define OTGSC_HADP (1 << 6) +#define OTGSC_IDPU (1 << 5) + +#define ULPI_STP_CTRL (1 << 30) +#define ASYNC_INTR_CTRL (1 << 29) +#define ULPI_SYNC_STATE (1 << 27) + +#define PORTSC_PHCD (1 << 23) +#define PORTSC_CSC (1 << 1) +#define disable_phy_clk() (writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC)) +#define enable_phy_clk() (writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC)) +#define is_phy_clk_disabled() (readl(USB_PORTSC) & PORTSC_PHCD) +#define is_phy_active() (readl_relaxed(USB_ULPI_VIEWPORT) &\ + ULPI_SYNC_STATE) +#define is_usb_active() (!(readl(USB_PORTSC) & PORTSC_SUSP)) + +/* Timeout (in msec) values (min - max) associated with OTG timers */ + +#define TA_WAIT_VRISE 100 /* ( - 100) */ +#define TA_WAIT_VFALL 500 /* ( - 1000) */ + +/* + * This option is set for embedded hosts or OTG devices in which leakage + * currents are very minimal. + */ +#ifdef CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT +#define TA_WAIT_BCON 30000 /* (1100 - 30000) */ +#else +#define TA_WAIT_BCON -1 +#endif + +/* AIDL_BDIS should be 500 */ +#define TA_AIDL_BDIS 200 /* (200 - ) */ +#define TA_BIDL_ADIS 155 /* (155 - 200) */ +#define TB_SRP_FAIL 6000 /* (5000 - 6000) */ +#define TB_ASE0_BRST 155 /* (155 - ) */ + +/* TB_SSEND_SRP and TB_SE0_SRP are combined */ +#define TB_SRP_INIT 2000 /* (1500 - ) */ + +/* Timeout variables */ + +#define A_WAIT_VRISE 0 +#define A_WAIT_VFALL 1 +#define A_WAIT_BCON 2 +#define A_AIDL_BDIS 3 +#define A_BIDL_ADIS 4 +#define B_SRP_FAIL 5 +#define B_ASE0_BRST 6 + +/* Internal flags like a_set_b_hnp_en, b_hnp_en are maintained + * in usb_bus and usb_gadget + */ + +#define A_BUS_DROP 0 +#define A_BUS_REQ 1 +#define A_SRP_DET 2 +#define A_VBUS_VLD 3 +#define B_CONN 4 +#define ID 5 +#define ADP_CHANGE 6 +#define POWER_UP 7 +#define A_CLR_ERR 8 +#define A_BUS_RESUME 9 +#define A_BUS_SUSPEND 10 +#define A_CONN 11 +#define B_BUS_REQ 12 +#define B_SESS_VLD 13 +#define ID_A 14 +#define ID_B 15 +#define ID_C 16 + +#define USB_IDCHG_MIN 500 +#define USB_IDCHG_MAX 1500 +#define USB_IB_UNCFG 2 +#define OTG_ID_POLL_MS 1000 + +struct msm_otg { + struct usb_phy phy; + + /* usb clocks */ + struct clk *alt_core_clk; + struct clk *iface_clk; + struct clk *core_clk; + + /* clk regime has created dummy clock id for phy so + * that generic clk_reset api can be used to reset phy + */ + struct clk *phy_reset_clk; + + int irq; + int vbus_on_irq; + int id_irq; + void __iomem *regs; + atomic_t in_lpm; + /* charger-type is modified by gadget for legacy chargers + * and OTG modifies it for ACA + */ + atomic_t chg_type; + + void (*start_host) (struct usb_bus *bus, int suspend); + /* Enable/disable the clocks */ + int (*set_clk) (struct usb_phy *phy, int on); + /* Reset phy and link */ + void (*reset) (struct usb_phy *phy, int phy_reset); + /* pmic notfications apis */ + u8 pmic_vbus_notif_supp; + u8 pmic_id_notif_supp; + struct msm_otg_platform_data *pdata; + + spinlock_t lock; /* protects OTG state */ + struct wake_lock wlock; + unsigned long b_last_se0_sess; /* SRP initial condition check */ + unsigned long inputs; + unsigned long tmouts; + u8 active_tmout; + struct hrtimer timer; + struct workqueue_struct *wq; + struct work_struct sm_work; /* state machine work */ + struct work_struct otg_resume_work; + struct notifier_block usbdev_nb; + struct msm_xo_voter *xo_handle; /*handle to vote for TCXO D1 buffer*/ +#ifdef CONFIG_USB_MSM_ACA + struct timer_list id_timer; /* drives id_status polling */ + unsigned b_max_power; /* ACA: max power of accessory*/ +#endif +}; + +static inline int can_phy_power_collapse(struct msm_otg *dev) +{ + if (!dev || !dev->pdata) + return -ENODEV; + + return dev->pdata->phy_can_powercollapse; +} + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_adsp.h b/arch/arm/mach-msm/include/mach/msm_adsp.h new file mode 100644 index 00000000000..e40c07d82ae --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_adsp.h @@ -0,0 +1,111 @@ +/* include/asm-arm/arch-msm/msm_adsp.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009-2010, 2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM__ARCH_MSM_ADSP_H +#define __ASM__ARCH_MSM_ADSP_H + +struct msm_adsp_module; + +struct msm_adsp_ops { + /* event is called from interrupt context when a message + * arrives from the DSP. Use the provided function pointer + * to copy the message into a local buffer. Do NOT call + * it multiple times. + */ + void (*event)(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)); +}; + +/* Get, Put, Enable, and Disable are synchronous and must only + * be called from thread context. Enable and Disable will block + * up to one second in the event of a fatal DSP error but are + * much faster otherwise. + */ +int msm_adsp_get(const char *name, struct msm_adsp_module **module, + struct msm_adsp_ops *ops, void *driver_data); +void msm_adsp_put(struct msm_adsp_module *module); +int msm_adsp_enable(struct msm_adsp_module *module); +int msm_adsp_disable(struct msm_adsp_module *module); +int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate); +int msm_adsp_disable_event_rsp(struct msm_adsp_module *module); +int32_t get_adsp_resource(unsigned short client_idx, + void *cmd_buf, size_t cmd_size); +int32_t put_adsp_resource(unsigned short client_idx, + void *cmd_buf, size_t cmd_size); + +/* Write is safe to call from interrupt context. + */ +int msm_adsp_write(struct msm_adsp_module *module, + unsigned queue_id, + void *data, size_t len); + +/*Explicitly gererate adsp event */ +int msm_adsp_generate_event(void *data, + struct msm_adsp_module *mod, + unsigned event_id, + unsigned event_length, + unsigned event_size, + void *msg); + +#define ADSP_MESSAGE_ID 0xFFFF + +/* Command Queue Indexes */ +#define QDSP_lpmCommandQueue 0 +#define QDSP_mpuAfeQueue 1 +#define QDSP_mpuGraphicsCmdQueue 2 +#define QDSP_mpuModmathCmdQueue 3 +#define QDSP_mpuVDecCmdQueue 4 +#define QDSP_mpuVDecPktQueue 5 +#define QDSP_mpuVEncCmdQueue 6 +#define QDSP_rxMpuDecCmdQueue 7 +#define QDSP_rxMpuDecPktQueue 8 +#define QDSP_txMpuEncQueue 9 +#define QDSP_uPAudPPCmd1Queue 10 +#define QDSP_uPAudPPCmd2Queue 11 +#define QDSP_uPAudPPCmd3Queue 12 +#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 +#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 +#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 +#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 +#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 +#define QDSP_uPAudPreProcCmdQueue 18 +#define QDSP_uPAudRecBitStreamQueue 19 +#define QDSP_uPAudRecCmdQueue 20 +#define QDSP_uPDiagQueue 21 +#define QDSP_uPJpegActionCmdQueue 22 +#define QDSP_uPJpegCfgCmdQueue 23 +#define QDSP_uPVocProcQueue 24 +#define QDSP_vfeCommandQueue 25 +#define QDSP_vfeCommandScaleQueue 26 +#define QDSP_vfeCommandTableQueue 27 +#define QDSP_vfeFtmCmdQueue 28 +#define QDSP_vfeFtmCmdScaleQueue 29 +#define QDSP_vfeFtmCmdTableQueue 30 +#define QDSP_uPJpegFtmCfgCmdQueue 31 +#define QDSP_uPJpegFtmActionCmdQueue 32 +#define QDSP_apuAfeQueue 33 +#define QDSP_mpuRmtQueue 34 +#define QDSP_uPAudPreProcAudRecCmdQueue 35 +#define QDSP_uPAudRec0BitStreamQueue 36 +#define QDSP_uPAudRec0CmdQueue 37 +#define QDSP_uPAudRec1BitStreamQueue 38 +#define QDSP_uPAudRec1CmdQueue 39 +#define QDSP_apuRmtQueue 40 +#define QDSP_uPAudRec2BitStreamQueue 41 +#define QDSP_uPAudRec2CmdQueue 42 +#define QDSP_MAX_NUM_QUEUES 43 + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_audio_aac.h b/arch/arm/mach-msm/include/mach/msm_audio_aac.h new file mode 100644 index 00000000000..8c4d91bc46c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_audio_aac.h @@ -0,0 +1,71 @@ +/* arch/arm/mach-msm/include/mach/msm_audio_aac.h + * + * Copyright (c) 2009 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_AUDIO_AAC_H +#define __MSM_AUDIO_AAC_H + +#include + +#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) + +#define AUDIO_AAC_FORMAT_ADTS -1 +#define AUDIO_AAC_FORMAT_RAW 0x0000 +#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDIO_AAC_FORMAT_LOAS 0x0002 + +#define AUDIO_AAC_OBJECT_LC 0x0002 +#define AUDIO_AAC_OBJECT_LTP 0x0004 +#define AUDIO_AAC_OBJECT_ERLC 0x0011 + +#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +/* Primary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_PL_PR 0 +/* Secondary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_SL_SR 1 +/* Primary channel on right channel and 2nd on left channel */ +#define AUDIO_AAC_DUAL_MONO_SL_PR 2 +/* 2nd channel on right channel and primary on left channel */ +#define AUDIO_AAC_DUAL_MONO_PL_SR 3 + +struct msm_audio_aac_config { + signed short format; + unsigned short audio_object; + unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */ + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; +}; + +#endif /* __MSM_AUDIO_AAC_H */ diff --git a/arch/arm/mach-msm/include/mach/msm_bus.h b/arch/arm/mach-msm/include/mach/msm_bus.h new file mode 100644 index 00000000000..6d7a5339f1c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_bus.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_BUS_H +#define _ARCH_ARM_MACH_MSM_BUS_H + +#include +#include + +/* + * Macros for clients to convert their data to ib and ab + * Ws : Time window over which to transfer the data in SECONDS + * Bs : Size of the data block in bytes + * Per : Recurrence period + * Tb : Throughput bandwidth to prevent stalling + * R : Ratio of actual bandwidth used to Tb + * Ib : Instantaneous bandwidth + * Ab : Arbitrated bandwidth + * + * IB_RECURRBLOCK and AB_RECURRBLOCK: + * These are used if the requirement is to transfer a + * recurring block of data over a known time window. + * + * IB_THROUGHPUTBW and AB_THROUGHPUTBW: + * These are used for CPU style masters. Here the requirement + * is to have minimum throughput bandwidth available to avoid + * stalling. + */ +#define IB_RECURRBLOCK(Ws, Bs) ((Ws) == 0 ? 0 : ((Bs)/(Ws))) +#define AB_RECURRBLOCK(Ws, Per) ((Ws) == 0 ? 0 : ((Bs)/(Per))) +#define IB_THROUGHPUTBW(Tb) (Tb) +#define AB_THROUGHPUTBW(Tb, R) ((Tb) * (R)) + +struct msm_bus_vectors { + int src; /* Master */ + int dst; /* Slave */ + unsigned int ab; /* Arbitrated bandwidth */ + unsigned int ib; /* Instantaneous bandwidth */ +}; + +struct msm_bus_paths { + int num_paths; + struct msm_bus_vectors *vectors; +}; + +struct msm_bus_scale_pdata { + struct msm_bus_paths *usecase; + int num_usecases; + const char *name; + /* + * If the active_only flag is set to 1, the BW request is applied + * only when at least one CPU is active (powered on). If the flag + * is set to 0, then the BW request is always applied irrespective + * of the CPU state. + */ + unsigned int active_only; +}; + +/* Scaling APIs */ + +/* + * This function returns a handle to the client. This should be used to + * call msm_bus_scale_client_update_request. + * The function returns 0 if bus driver is unable to register a client + */ + +#ifdef CONFIG_MSM_BUS_SCALING +uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata); +int msm_bus_scale_client_update_request(uint32_t cl, unsigned int index); +void msm_bus_scale_unregister_client(uint32_t cl); +/* AXI Port configuration APIs */ +int msm_bus_axi_porthalt(int master_port); +int msm_bus_axi_portunhalt(int master_port); + +#else +static inline uint32_t +msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata) +{ + return 1; +} + +static inline int +msm_bus_scale_client_update_request(uint32_t cl, unsigned int index) +{ + return 0; +} + +static inline void +msm_bus_scale_unregister_client(uint32_t cl) +{ +} + +static inline int msm_bus_axi_porthalt(int master_port) +{ + return 0; +} + +static inline int msm_bus_axi_portunhalt(int master_port) +{ + return 0; +} +#endif + +#endif /*_ARCH_ARM_MACH_MSM_BUS_H*/ diff --git a/arch/arm/mach-msm/include/mach/msm_bus_board.h b/arch/arm/mach-msm/include/mach/msm_bus_board.h new file mode 100644 index 00000000000..956d44e6a06 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_bus_board.h @@ -0,0 +1,433 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_BUS_BOARD_H +#define __ASM_ARCH_MSM_BUS_BOARD_H + +#include +#include + +enum context { + DUAL_CTX, + ACTIVE_CTX, + NUM_CTX +}; + +struct msm_bus_fabric_registration { + unsigned int id; + char *name; + struct msm_bus_node_info *info; + unsigned int len; + int ahb; + const char *fabclk[NUM_CTX]; + unsigned int offset; + unsigned int haltid; + unsigned int rpm_enabled; + const unsigned int nmasters; + const unsigned int nslaves; + const unsigned int ntieredslaves; + bool il_flag; + const struct msm_bus_board_algorithm *board_algo; + int hw_sel; + void *hw_data; +}; + +enum msm_bus_bw_tier_type { + MSM_BUS_BW_TIER1 = 1, + MSM_BUS_BW_TIER2, + MSM_BUS_BW_COUNT, + MSM_BUS_BW_SIZE = 0x7FFFFFFF, +}; + +struct msm_bus_halt_vector { + uint32_t haltval; + uint32_t haltmask; +}; + +extern struct msm_bus_fabric_registration msm_bus_apps_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_sys_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_mm_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_sys_fpb_pdata; +extern struct msm_bus_fabric_registration msm_bus_cpss_fpb_pdata; +extern struct msm_bus_fabric_registration msm_bus_def_fab_pdata; + +extern struct msm_bus_fabric_registration msm_bus_8960_apps_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8960_sys_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8960_mm_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata; +extern struct msm_bus_fabric_registration msm_bus_8960_cpss_fpb_pdata; + +extern struct msm_bus_fabric_registration msm_bus_8064_apps_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8064_sys_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8064_mm_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8064_sys_fpb_pdata; +extern struct msm_bus_fabric_registration msm_bus_8064_cpss_fpb_pdata; + +extern struct msm_bus_fabric_registration msm_bus_9615_sys_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_9615_def_fab_pdata; + +extern struct msm_bus_fabric_registration msm_bus_8930_apps_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8930_sys_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8930_mm_fabric_pdata; +extern struct msm_bus_fabric_registration msm_bus_8930_sys_fpb_pdata; +extern struct msm_bus_fabric_registration msm_bus_8930_cpss_fpb_pdata; + +void msm_bus_rpm_set_mt_mask(void); +int msm_bus_board_rpm_get_il_ids(uint16_t *id); +int msm_bus_board_get_iid(int id); + +/* + * These macros specify the convention followed for allocating + * ids to fabrics, masters and slaves for 8x60. + * + * A node can be identified as a master/slave/fabric by using + * these ids. + */ +#define FABRIC_ID_KEY 1024 +#define SLAVE_ID_KEY ((FABRIC_ID_KEY) >> 1) +#define NUM_FAB 5 +#define MAX_FAB_KEY 7168 /* OR(All fabric ids) */ + +#define GET_FABID(id) ((id) & MAX_FAB_KEY) + +#define NODE_ID(id) ((id) & (FABRIC_ID_KEY - 1)) +#define IS_SLAVE(id) ((NODE_ID(id)) >= SLAVE_ID_KEY ? 1 : 0) +#define CHECK_ID(iid, id) (((iid & id) != id) ? -ENXIO : iid) + +/* + * The following macros are used to format the data for port halt + * and unhalt requests. + */ +#define MSM_BUS_CLK_HALT 0x1 +#define MSM_BUS_CLK_HALT_MASK 0x1 +#define MSM_BUS_CLK_HALT_FIELDSIZE 0x1 +#define MSM_BUS_CLK_UNHALT 0x0 + +#define MSM_BUS_MASTER_SHIFT(master, fieldsize) \ + ((master) * (fieldsize)) + +#define MSM_BUS_SET_BITFIELD(word, fieldmask, fieldvalue) \ + { \ + (word) &= ~(fieldmask); \ + (word) |= (fieldvalue); \ + } + + +#define MSM_BUS_MASTER_HALT(u32haltmask, u32haltval, master) \ + MSM_BUS_SET_BITFIELD(u32haltmask, \ + MSM_BUS_CLK_HALT_MASK< + + +struct l2_cache_line_dump { + unsigned int l2dcrtr0_val; + unsigned int l2dcrtr1_val; + unsigned int cache_line_data[32]; + unsigned int ddr_data[32]; +} __packed; + +struct l2_cache_dump { + unsigned int magic_number; + unsigned int version; + unsigned int tag_size; + unsigned int line_size; + unsigned int total_lines; + struct l2_cache_line_dump cache[8*1024]; + unsigned int l2esr; +} __packed; + + +struct l1_cache_dump { + unsigned int magic; + unsigned int version; + unsigned int flags; + unsigned int cpu_count; + unsigned int i_tag_size; + unsigned int i_line_size; + unsigned int i_num_sets; + unsigned int i_num_ways; + unsigned int d_tag_size; + unsigned int d_line_size; + unsigned int d_num_sets; + unsigned int d_num_ways; + unsigned int spare[32]; + unsigned int lines[]; +} __packed; + + +struct msm_cache_dump_platform_data { + unsigned int l1_size; + unsigned int l2_size; +}; + +#define CACHE_BUFFER_DUMP_SIZE (L1_BUFFER_SIZE + L2_BUFFER_SIZE) + +#define L1C_SERVICE_ID 3 +#define L1C_BUFFER_SET_COMMAND_ID 4 +#define CACHE_BUFFER_DUMP_COMMAND_ID 5 +#define L1C_BUFFER_GET_SIZE_COMMAND_ID 6 +#define L2C_BUFFER_SET_COMMAND_ID 7 +#define L2C_BUFFER_GET_SIZE_COMMAND_ID 8 + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_dsps.h b/arch/arm/mach-msm/include/mach/msm_dsps.h new file mode 100644 index 00000000000..32a4f15722f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_dsps.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MSM_DSPS_H_ +#define _MSM_DSPS_H_ + +#include +#include +#include + +#define DSPS_SIGNATURE 0x12345678 + +/** + * DSPS Clocks Platform data. + * + * @name - clock name. + * @rate - rate to set. zero if not relevant. + * @clock - clock handle, reserved for the driver. + */ +struct dsps_clk_info { + const char *name; + u32 rate; + struct clk *clock; +}; + +/** + * DSPS GPIOs Platform data. + * + * @name - clock name. + * @num - GPIO number. + * @on_val - value to ouptput for ON (depends on polarity). + * @off_val - value to ouptput for OFF (depends on polarity). + * @is_owner - reserved for the driver. + */ +struct dsps_gpio_info { + const char *name; + int num; + int on_val; + int off_val; + int is_owner; +}; + +/** + * DSPS Power regulators Platform data. + * + * @name - regulator name. + * @volt - required voltage (in uV). + * @reg - reserved for the driver. + */ +struct dsps_regulator_info { + const char *name; + int volt; + struct regulator *reg; +}; + +/** + * DSPS Platform data. + * + * @pil_name - peripheral image name + * @clks - array of clocks. + * @clks_num - number of clocks in array. + * @gpios - array of gpios. + * @gpios_num - number of gpios. + * @regs - array of regulators. + * @regs_num - number of regulators. + * @dsps_pwr_ctl_en - to enable DSPS to do power control if set 1 + * otherwise the apps will do power control + * @tcm_code_start - start of the TCM code region as physical address + * @tcm_code_size - size of the TCM code region in bytes + * @tcm_buf_start - start of the TCM buf region as physical address + * @tcm_buf_size - size of the TCM buf region in bytes + * @pipe_start - start of the PIPE region as physical address + * @pipe_size - size of the PIPE region in bytes + * @ddr_start - start of the DDR region as physical address + * @ddr_size - size of the DDR region in bytes + * @smem_start - start of the smem region as physical address + * @smem_size - size of the smem region in bytes + * @signature - signature for validity check. + */ +struct msm_dsps_platform_data { + const char *pil_name; + struct dsps_clk_info *clks; + int clks_num; + struct dsps_gpio_info *gpios; + int gpios_num; + struct dsps_regulator_info *regs; + int regs_num; + int dsps_pwr_ctl_en; + void (*init)(struct msm_dsps_platform_data *data); + int tcm_code_start; + int tcm_code_size; + int tcm_buf_start; + int tcm_buf_size; + int pipe_start; + int pipe_size; + int ddr_start; + int ddr_size; + int smem_start; + int smem_size; + u32 signature; +}; + +#endif /* _MSM_DSPS_H_ */ diff --git a/arch/arm/mach-msm/include/mach/msm_fast_timer.h b/arch/arm/mach-msm/include/mach/msm_fast_timer.h new file mode 100644 index 00000000000..e1660c192a3 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_fast_timer.h @@ -0,0 +1,19 @@ +/* arch/arm/mach-msm/include/mach/msm_fast_timer.h + * + * Copyright (C) 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +void msm_enable_fast_timer(void); +void msm_disable_fast_timer(void); +u32 msm_read_fast_timer(void); + diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 1f4fc81b3d8..3bbaa25da47 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -21,6 +21,10 @@ struct mddi_info; +/* output interface format */ +#define MSM_MDP_OUT_IF_FMT_RGB565 0 +#define MSM_MDP_OUT_IF_FMT_RGB666 1 + struct msm_fb_data { int xres; /* x resolution in pixels */ int yres; /* y resolution in pixels */ @@ -34,9 +38,12 @@ struct msmfb_callback { }; enum { - MSM_MDDI_PMDH_INTERFACE, + MSM_MDDI_PMDH_INTERFACE = 0, MSM_MDDI_EMDH_INTERFACE, MSM_EBI2_INTERFACE, + MSM_LCDC_INTERFACE, + + MSM_MDP_NUM_INTERFACES = MSM_LCDC_INTERFACE + 1, }; #define MSMFB_CAP_PARTIAL_UPDATES (1 << 0) @@ -85,6 +92,8 @@ struct msm_mddi_platform_data { /* fixup the mfr name, product id */ void (*fixup)(uint16_t *mfr_name, uint16_t *product_id); + int vsync_irq; + struct resource *fb_resource; /*optional*/ /* number of clients in the list that follows */ int num_clients; @@ -110,17 +119,50 @@ struct msm_mddi_platform_data { } client_platform_data[]; }; +struct msm_lcdc_timing { + unsigned int clk_rate; /* dclk freq */ + unsigned int hsync_pulse_width; /* in dclks */ + unsigned int hsync_back_porch; /* in dclks */ + unsigned int hsync_front_porch; /* in dclks */ + unsigned int hsync_skew; /* in dclks */ + unsigned int vsync_pulse_width; /* in lines */ + unsigned int vsync_back_porch; /* in lines */ + unsigned int vsync_front_porch; /* in lines */ + + /* control signal polarity */ + unsigned int vsync_act_low:1; + unsigned int hsync_act_low:1; + unsigned int den_act_low:1; +}; + +struct msm_lcdc_panel_ops { + int (*init)(struct msm_lcdc_panel_ops *); + int (*uninit)(struct msm_lcdc_panel_ops *); + int (*blank)(struct msm_lcdc_panel_ops *); + int (*unblank)(struct msm_lcdc_panel_ops *); +}; + +struct msm_lcdc_platform_data { + struct msm_lcdc_panel_ops *panel_ops; + struct msm_lcdc_timing *timing; + int fb_id; + struct msm_fb_data *fb_data; + struct resource *fb_resource; +}; + struct mdp_blit_req; struct fb_info; struct mdp_device { struct device dev; - void (*dma)(struct mdp_device *mpd, uint32_t addr, + void (*dma)(struct mdp_device *mdp, uint32_t addr, uint32_t stride, uint32_t w, uint32_t h, uint32_t x, uint32_t y, struct msmfb_callback *callback, int interface); - void (*dma_wait)(struct mdp_device *mdp); + void (*dma_wait)(struct mdp_device *mdp, int interface); int (*blit)(struct mdp_device *mdp, struct fb_info *fb, struct mdp_blit_req *req); void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id); + int (*check_output_format)(struct mdp_device *mdp, int bpp); + int (*set_output_format)(struct mdp_device *mdp, int bpp); }; struct class_interface; @@ -140,8 +182,17 @@ struct msm_mddi_bridge_platform_data { int (*unblank)(struct msm_mddi_bridge_platform_data *, struct msm_mddi_client_data *); struct msm_fb_data fb_data; + + /* board file will identify what capabilities the panel supports */ + uint32_t panel_caps; }; +struct mdp_v4l2_req; +int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par); +int msm_fb_v4l2_update(void *par, + unsigned long srcp0_addr, unsigned long srcp0_size, + unsigned long srcp1_addr, unsigned long srcp1_size, + unsigned long srcp2_addr, unsigned long srcp2_size); #endif diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h new file mode 100644 index 00000000000..57e794f33bb --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_HDMI_AUDIO_H +#define __MSM_HDMI_AUDIO_H + +/* Supported HDMI Audio channels */ +#define MSM_HDMI_AUDIO_CHANNEL_2 0 +#define MSM_HDMI_AUDIO_CHANNEL_4 1 +#define MSM_HDMI_AUDIO_CHANNEL_6 2 +#define MSM_HDMI_AUDIO_CHANNEL_8 3 + +#define TRUE 1 +#define FALSE 0 + +enum hdmi_supported_sample_rates { + HDMI_SAMPLE_RATE_32KHZ, + HDMI_SAMPLE_RATE_44_1KHZ, + HDMI_SAMPLE_RATE_48KHZ, + HDMI_SAMPLE_RATE_88_2KHZ, + HDMI_SAMPLE_RATE_96KHZ, + HDMI_SAMPLE_RATE_176_4KHZ, + HDMI_SAMPLE_RATE_192KHZ +}; + +int hdmi_audio_enable(bool on , u32 fifo_water_mark); +int hdmi_audio_packet_enable(bool on); +void hdmi_msm_audio_sample_rate_reset(int rate); +int hdmi_msm_audio_get_sample_rate(void); +int hdmi_msm_audio_info_setup(bool enabled, u32 num_of_channels, + u32 channel_allocation, u32 level_shift, bool down_mix); + +#endif /* __MSM_HDMI_AUDIO_H*/ diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h new file mode 100644 index 00000000000..4f140cc8949 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h @@ -0,0 +1,209 @@ +/* linux/include/mach/hsusb.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_HSUSB_H +#define __ASM_ARCH_MSM_HSUSB_H + +#include +#include +#include +#include + +#define PHY_TYPE_MASK 0x0F +#define PHY_TYPE_MODE 0xF0 +#define PHY_MODEL_MASK 0xFF00 +#define PHY_TYPE(x) ((x) & PHY_TYPE_MASK) +#define PHY_MODEL(x) ((x) & PHY_MODEL_MASK) + +#define USB_PHY_MODEL_65NM 0x100 +#define USB_PHY_MODEL_180NM 0x200 +#define USB_PHY_MODEL_45NM 0x400 +#define USB_PHY_UNDEFINED 0x00 +#define USB_PHY_INTEGRATED 0x01 +#define USB_PHY_EXTERNAL 0x02 +#define USB_PHY_SERIAL_PMIC 0x04 + +#define REQUEST_STOP 0 +#define REQUEST_START 1 +#define REQUEST_RESUME 2 +#define REQUEST_HNP_SUSPEND 3 +#define REQUEST_HNP_RESUME 4 + +/* Flags required to read ID state of PHY for ACA */ +#define PHY_ID_MASK 0xB0 +#define PHY_ID_GND 0 +#define PHY_ID_C 0x10 +#define PHY_ID_B 0x30 +#define PHY_ID_A 0x90 + +#define phy_id_state(ints) ((ints) & PHY_ID_MASK) +#define phy_id_state_gnd(ints) (phy_id_state((ints)) == PHY_ID_GND) +#define phy_id_state_a(ints) (phy_id_state((ints)) == PHY_ID_A) +/* RID_B and RID_C states does not exist with standard ACA */ +#ifdef CONFIG_USB_MSM_STANDARD_ACA +#define phy_id_state_b(ints) 0 +#define phy_id_state_c(ints) 0 +#else +#define phy_id_state_b(ints) (phy_id_state((ints)) == PHY_ID_B) +#define phy_id_state_c(ints) (phy_id_state((ints)) == PHY_ID_C) +#endif + +/* + * The following are bit fields describing the usb_request.udc_priv word. + * These bit fields are set by function drivers that wish to queue + * usb_requests with sps/bam parameters. + */ +#define MSM_PIPE_ID_MASK (0x1F) +#define MSM_TX_PIPE_ID_OFS (16) +#define MSM_SPS_MODE BIT(5) +#define MSM_IS_FINITE_TRANSFER BIT(6) +#define MSM_PRODUCER BIT(7) +#define MSM_DISABLE_WB BIT(8) +#define MSM_ETD_IOC BIT(9) +#define MSM_INTERNAL_MEM BIT(10) +#define MSM_VENDOR_ID BIT(16) + +/* used to detect the OTG Mode */ +enum otg_mode { + OTG_ID = 0, /* ID pin detection */ + OTG_USER_CONTROL, /* User configurable */ + OTG_VCHG, /* Based on VCHG interrupt */ +}; + +/* used to configure the default mode,if otg_mode is USER_CONTROL */ +enum usb_mode { + USB_HOST_MODE, + USB_PERIPHERAL_MODE, +}; + +enum chg_type { + USB_CHG_TYPE__SDP, + USB_CHG_TYPE__CARKIT, + USB_CHG_TYPE__WALLCHARGER, + USB_CHG_TYPE__INVALID +}; + +enum pre_emphasis_level { + PRE_EMPHASIS_DEFAULT, + PRE_EMPHASIS_DISABLE, + PRE_EMPHASIS_WITH_10_PERCENT = (1 << 5), + PRE_EMPHASIS_WITH_20_PERCENT = (3 << 4), +}; +enum cdr_auto_reset { + CDR_AUTO_RESET_DEFAULT, + CDR_AUTO_RESET_ENABLE, + CDR_AUTO_RESET_DISABLE, +}; + +enum se1_gate_state { + SE1_GATING_DEFAULT, + SE1_GATING_ENABLE, + SE1_GATING_DISABLE, +}; + +enum hs_drv_amplitude { + HS_DRV_AMPLITUDE_DEFAULT, + HS_DRV_AMPLITUDE_ZERO_PERCENT, + HS_DRV_AMPLITUDE_25_PERCENTI = (1 << 2), + HS_DRV_AMPLITUDE_5_PERCENT = (1 << 3), + HS_DRV_AMPLITUDE_75_PERCENT = (3 << 2), +}; + +#define HS_DRV_SLOPE_DEFAULT (-1) + +/* used to configure the analog switch to select b/w host and peripheral */ +enum usb_switch_control { + USB_SWITCH_PERIPHERAL = 0, /* Configure switch in peripheral mode*/ + USB_SWITCH_HOST, /* Host mode */ + USB_SWITCH_DISABLE, /* No mode selected, shutdown power */ +}; + +struct msm_hsusb_gadget_platform_data { + int *phy_init_seq; + void (*phy_reset)(void); + + int self_powered; + int is_phy_status_timer_on; +}; + +struct msm_otg_platform_data { + int (*rpc_connect)(int); + int (*phy_reset)(void __iomem *); + int pmic_vbus_irq; + int pmic_id_irq; + /* if usb link is in sps there is no need for + * usb pclk as dayatona fabric clock will be + * used instead + */ + int usb_in_sps; + enum pre_emphasis_level pemp_level; + enum cdr_auto_reset cdr_autoreset; + enum hs_drv_amplitude drv_ampl; + enum se1_gate_state se1_gating; + int hsdrvslope; + int phy_reset_sig_inverted; + int phy_can_powercollapse; + int pclk_required_during_lpm; + int bam_disable; + /* HSUSB core in 8660 has the capability to gate the + * pclk when not being used. Though this feature is + * now being disabled because of H/w issues + */ + int pclk_is_hw_gated; + + int (*ldo_init) (int init); + int (*ldo_enable) (int enable); + int (*ldo_set_voltage) (int mV); + + u32 swfi_latency; + /* pmic notfications apis */ + int (*pmic_vbus_notif_init) (void (*callback)(int online), int init); + int (*pmic_id_notif_init) (void (*callback)(int online), int init); + int (*phy_id_setup_init) (int init); + int (*pmic_register_vbus_sn) (void (*callback)(int online)); + void (*pmic_unregister_vbus_sn) (void (*callback)(int online)); + int (*pmic_enable_ldo) (int); + int (*init_gpio)(int on); + void (*setup_gpio)(enum usb_switch_control mode); + u8 otg_mode; + u8 usb_mode; + void (*vbus_power) (unsigned phy_info, int on); + + /* charger notification apis */ + void (*chg_connected)(enum chg_type chg_type); + void (*chg_vbus_draw)(unsigned ma); + int (*chg_init)(int init); + int (*config_vddcx)(int high); + int (*init_vddcx)(int init); + + struct pm_qos_request pm_qos_req_dma; +}; + +struct msm_usb_host_platform_data { + unsigned phy_info; + unsigned int power_budget; + void (*config_gpio)(unsigned int config); + void (*vbus_power) (unsigned phy_info, int on); + int (*vbus_init)(int init); + struct clk *ebi1_clk; +}; + +int msm_ep_config(struct usb_ep *ep); +int msm_ep_unconfig(struct usb_ep *ep); +int msm_data_fifo_config(struct usb_ep *ep, u32 addr, u32 size); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h new file mode 100644 index 00000000000..82542b2a9e6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h @@ -0,0 +1,286 @@ + /* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_GADGET_MSM72K_UDC_H__ +#define __LINUX_USB_GADGET_MSM72K_UDC_H__ + +#define USB_ID (MSM_USB_BASE + 0x0000) +#define USB_HWGENERAL (MSM_USB_BASE + 0x0004) +#define USB_HWHOST (MSM_USB_BASE + 0x0008) +#define USB_HWDEVICE (MSM_USB_BASE + 0x000C) +#define USB_HWTXBUF (MSM_USB_BASE + 0x0010) +#define USB_HWRXBUF (MSM_USB_BASE + 0x0014) +#define USB_AHB_BURST (MSM_USB_BASE + 0x0090) +#define USB_AHB_MODE (MSM_USB_BASE + 0x0098) +#define USB_GEN_CONFIG (MSM_USB_BASE + 0x009C) +#define USB_BAM_DISABLE (1 << 13) +#define USB_ROC_AHB_MODE (MSM_USB_BASE + 0x0090) +#define USB_SBUSCFG (MSM_USB_BASE + 0x0090) + +#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ +#define USB_HCIVERSION (MSM_USB_BASE + 0x0102) /* 16 bit */ +#define USB_HCSPARAMS (MSM_USB_BASE + 0x0104) +#define USB_HCCPARAMS (MSM_USB_BASE + 0x0108) +#define USB_DCIVERSION (MSM_USB_BASE + 0x0120) /* 16 bit */ +#define USB_USBCMD (MSM_USB_BASE + 0x0140) +#define USB_USBSTS (MSM_USB_BASE + 0x0144) +#define USB_USBINTR (MSM_USB_BASE + 0x0148) +#define USB_FRINDEX (MSM_USB_BASE + 0x014C) +#define USB_DEVICEADDR (MSM_USB_BASE + 0x0154) +#define USB_ENDPOINTLISTADDR (MSM_USB_BASE + 0x0158) +#define USB_BURSTSIZE (MSM_USB_BASE + 0x0160) +#define USB_TXFILLTUNING (MSM_USB_BASE + 0x0164) +#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) +#define USB_ENDPTNAK (MSM_USB_BASE + 0x0178) +#define USB_ENDPTNAKEN (MSM_USB_BASE + 0x017C) +#define USB_PORTSC (MSM_USB_BASE + 0x0184) +#define USB_OTGSC (MSM_USB_BASE + 0x01A4) +#define USB_USBMODE (MSM_USB_BASE + 0x01A8) +#define USB_ENDPTSETUPSTAT (MSM_USB_BASE + 0x01AC) +#define USB_ENDPTPRIME (MSM_USB_BASE + 0x01B0) +#define USB_ENDPTFLUSH (MSM_USB_BASE + 0x01B4) +#define USB_ENDPTSTAT (MSM_USB_BASE + 0x01B8) +#define USB_ENDPTCOMPLETE (MSM_USB_BASE + 0x01BC) +#define USB_ENDPTCTRL(n) (MSM_USB_BASE + 0x01C0 + (4 * (n))) + + +#define USBCMD_RESET 2 +#define USBCMD_ATTACH 1 +#define USBCMD_RS (1 << 0) /* run/stop bit */ +#define USBCMD_ATDTW (1 << 14) +#define USBCMD_ITC(n) (n << 16) +#define USBCMD_ITC_MASK (0xFF << 16) +#define ASYNC_INTR_CTRL (1 << 29) +#define ULPI_STP_CTRL (1 << 30) + +#define USBMODE_DEVICE 2 +#define USBMODE_HOST 3 +#define USBMODE_VBUS (1 << 5) /* vbus power select */ + +/* Redefining SDIS bit as it defined incorrectly in ehci.h. */ +#ifdef USBMODE_SDIS +#undef USBMODE_SDIS +#endif +#define USBMODE_SDIS (1 << 4) /* stream disable */ + +struct ept_queue_head { + unsigned config; + unsigned active; /* read-only */ + + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved_0; + + unsigned char setup_data[8]; + + unsigned reserved_1; + unsigned reserved_2; + unsigned reserved_3; + unsigned reserved_4; +}; + +#define CONFIG_MAX_PKT(n) ((n) << 16) +#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ +#define CONFIG_IOS (1 << 15) /* IRQ on setup */ + +struct ept_queue_item { + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved; +}; + +#define TERMINATE 1 + +#define INFO_BYTES(n) ((n) << 16) +#define INFO_IOC (1 << 15) +#define INFO_ACTIVE (1 << 7) +#define INFO_HALTED (1 << 6) +#define INFO_BUFFER_ERROR (1 << 5) +#define INFO_TXN_ERROR (1 << 3) + + +#define STS_NAKI (1 << 16) /* */ +#define STS_SLI (1 << 8) /* R/WC - suspend state entered */ +#define STS_SRI (1 << 7) /* R/WC - SOF recv'd */ +#define STS_URI (1 << 6) /* R/WC - RESET recv'd */ +#define STS_FRI (1 << 3) /* R/WC - Frame List Rollover */ +#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */ +#define STS_UEI (1 << 1) /* R/WC - USB Error */ +#define STS_UI (1 << 0) /* R/WC - USB Transaction Complete */ + + +/* bits used in all the endpoint status registers */ +#define EPT_TX(n) (1 << ((n) + 16)) +#define EPT_RX(n) (1 << (n)) + + +#define CTRL_TXE (1 << 23) +#define CTRL_TXR (1 << 22) +#define CTRL_TXI (1 << 21) +#define CTRL_TXD (1 << 17) +#define CTRL_TXS (1 << 16) +#define CTRL_RXE (1 << 7) +#define CTRL_RXR (1 << 6) +#define CTRL_RXI (1 << 5) +#define CTRL_RXD (1 << 1) +#define CTRL_RXS (1 << 0) + +#define CTRL_TXT_MASK (3 << 18) +#define CTRL_TXT_CTRL (0 << 18) +#define CTRL_TXT_ISOCH (1 << 18) +#define CTRL_TXT_BULK (2 << 18) +#define CTRL_TXT_INT (3 << 18) +#define CTRL_TXT_EP_TYPE_SHIFT 18 + +#define CTRL_RXT_MASK (3 << 2) +#define CTRL_RXT_CTRL (0 << 2) +#define CTRL_RXT_ISOCH (1 << 2) +#define CTRL_RXT_BULK (2 << 2) +#define CTRL_RXT_INT (3 << 2) +#define CTRL_RXT_EP_TYPE_SHIFT 2 + +#define ULPI_CONFIG_REG 0x31 +#if (defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)) \ + || defined(CONFIG_ARCH_QSD8X50) +#define ULPI_DIGOUT_CTRL 0X31 +#define ULPI_CDR_AUTORESET (1 << 5) +#else +#define ULPI_DIGOUT_CTRL 0X36 +#define ULPI_CDR_AUTORESET (1 << 1) +#endif +#define ULPI_SE1_GATE (1 << 2) +#define ULPI_CONFIG_REG1 0x30 +#define ULPI_CONFIG_REG2 0X31 +#define ULPI_CONFIG_REG3 0X32 +#define ULPI_IFC_CTRL_CLR 0x09 +#define ULPI_AMPLITUDE_MAX 0x0C +#define ULPI_OTG_CTRL 0x0B +#define ULPI_OTG_CTRL_CLR 0x0C +#define ULPI_INT_RISE_CLR 0x0F +#define ULPI_INT_FALL_CLR 0x12 +#define ULPI_PRE_EMPHASIS_MASK (3 << 4) +#define ULPI_HSDRVSLOPE_MASK (0x0F) +#define ULPI_DRV_AMPL_MASK (3 << 2) +#define ULPI_ONCLOCK (1 << 6) +#define ULPI_IDPU (1 << 0) +#define ULPI_HOST_DISCONNECT (1 << 0) +#define ULPI_VBUS_VALID (1 << 1) +#define ULPI_SESS_END (1 << 3) +#define ULPI_ID_GND (1 << 4) +#define ULPI_WAKEUP (1 << 31) +#define ULPI_RUN (1 << 30) +#define ULPI_WRITE (1 << 29) +#define ULPI_READ (0 << 29) +#define ULPI_STATE_NORMAL (1 << 27) +#define ULPI_ADDR(n) (((n) & 255) << 16) +#define ULPI_DATA(n) ((n) & 255) +#define ULPI_DATA_READ(n) (((n) >> 8) & 255) + +/* USB_PORTSC bits for determining port speed */ +#define PORTSC_PSPD_FS (0 << 26) +#define PORTSC_PSPD_LS (1 << 26) +#define PORTSC_PSPD_HS (2 << 26) +#define PORTSC_PSPD_MASK (3 << 26) + + +#define OTGSC_BSVIE (1 << 27) /* R/W - BSV Interrupt Enable */ +#define OTGSC_DPIE (1 << 30) /* R/W - DataPulse Interrupt Enable */ +#define OTGSC_1MSE (1 << 29) /* R/W - 1ms Interrupt Enable */ +#define OTGSC_BSEIE (1 << 28) /* R/W - BSE Interrupt Enable */ +#define OTGSC_ASVIE (1 << 26) /* R/W - ASV Interrupt Enable */ +#define OTGSC_ASEIE (1 << 25) /* R/W - ASE Interrupt Enable */ +#define OTGSC_IDIE (1 << 24) /* R/W - ID Interrupt Enable */ +#define OTGSC_BSVIS (1 << 19) /* R/W - BSV Interrupt Status */ +#define OTGSC_IDPU (1 << 5) +#define OTGSC_ID (1 << 8) +#define OTGSC_IDIS (1 << 16) +#define B_SESSION_VALID (1 << 11) +#define OTGSC_INTR_MASK (OTGSC_BSVIE | OTGSC_DPIE | OTGSC_1MSE | \ + OTGSC_BSEIE | OTGSC_ASVIE | OTGSC_ASEIE | \ + OTGSC_IDIE) +#define OTGSC_INTR_STS_MASK (0x7f << 16) +#define CURRENT_CONNECT_STATUS (1 << 0) + +#define PORTSC_FPR (1 << 6) /* R/W - State normal => suspend */ +#define PORTSC_SUSP (1 << 7) /* Read - Port in suspend state */ +#define PORTSC_LS (3 << 10) /* Read - Port's Line status */ +#define PORTSC_PHCD (1 << 23) /* phy suspend mode */ +#define PORTSC_CCS (1 << 0) /* current connect status */ +#define PORTSC_PORT_RESET 0x00000100 +#define PORTSC_PTS (3 << 30) +#define PORTSC_PTS_ULPI (2 << 30) +#define PORTSC_PTS_SERIAL (3 << 30) + +#define PORTSC_PORT_SPEED_FULL 0x00000000 +#define PORTSC_PORT_SPEED_LOW 0x04000000 +#define PORTSC_PORT_SPEED_HIGH 0x08000000 +#define PORTSC_PORT_SPEED_MASK 0x0c000000 + +#define SBUSCFG_AHBBRST_INCR4 0x01 +#define ULPI_USBINTR_ENABLE_RASING_C 0x0F +#define ULPI_USBINTR_ENABLE_FALLING_C 0x12 +#define ULPI_USBINTR_STATUS 0x13 +#define ULPI_USBINTR_ENABLE_RASING_S 0x0E +#define ULPI_USBINTR_ENABLE_FALLING_S 0x11 +#define ULPI_SESSION_END_RAISE (1 << 3) +#define ULPI_SESSION_END_FALL (1 << 3) +#define ULPI_SESSION_VALID_RAISE (1 << 2) +#define ULPI_SESSION_VALID_FALL (1 << 2) +#define ULPI_VBUS_VALID_RAISE (1 << 1) +#define ULPI_VBUS_VALID_FALL (1 << 1) + +#define ULPI_CHG_DETECT_REG 0x34 +/* control charger detection by ULPI or externally */ +#define ULPI_EXTCHGCTRL_65NM (1 << 2) +#define ULPI_EXTCHGCTRL_180NM (1 << 3) +/* charger detection power on control */ +#define ULPI_CHGDETON (1 << 1) + /* enable charger detection */ +#define ULPI_CHGDETEN (1 << 0) +#define ULPI_CHGTYPE_65NM (1 << 3) +#define ULPI_CHGTYPE_180NM (1 << 4) + +/* test mode support */ +#define J_TEST (0x0100) +#define K_TEST (0x0200) +#define SE0_NAK_TEST (0x0300) +#define TST_PKT_TEST (0x0400) +#define PORTSC_PTC (0xf << 16) +#define PORTSC_PTC_J_STATE (0x01 << 16) +#define PORTSC_PTC_K_STATE (0x02 << 16) +#define PORTSC_PTC_SE0_NAK (0x03 << 16) +#define PORTSC_PTC_TST_PKT (0x04 << 16) + +#define USBH (1 << 15) +#define USB_PHY (1 << 18) + +#define ULPI_DEBUG 0x15 +#define ULPI_FUNC_CTRL_CLR 0x06 +#define ULPI_SUSPENDM (1 << 6) +#define ULPI_CLOCK_SUSPENDM (1 << 3) +#define ULPI_CALIB_STS (1 << 7) +#define ULPI_CALIB_VAL(x) (x & 0x7C) +#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ diff --git a/arch/arm/mach-msm/include/mach/msm_i2ckbd.h b/arch/arm/mach-msm/include/mach/msm_i2ckbd.h new file mode 100644 index 00000000000..dc33c757bda --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_i2ckbd.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MSM_I2CKBD_H_ +#define _MSM_I2CKBD_H_ + +struct msm_i2ckbd_platform_data { + uint8_t hwrepeat; + uint8_t scanset1; + int gpioreset; + int gpioirq; + int (*gpio_setup) (void); + void (*gpio_shutdown)(void); + void (*hw_reset) (int); +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h index 6c4046c2129..44f0a8b57d8 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h @@ -1,7 +1,6 @@ /* arch/arm/mach-msm/include/mach/msm_iomap.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -38,28 +37,34 @@ * */ -#define MSM_VIC_BASE IOMEM(0xE0000000) +#define MSM_VIC_BASE IOMEM(0xF8000000) #define MSM_VIC_PHYS 0xC0000000 #define MSM_VIC_SIZE SZ_4K -#define MSM7X00_CSR_PHYS 0xC0100000 -#define MSM7X00_CSR_SIZE SZ_4K +#define MSM_CSR_BASE IOMEM(0xF8001000) +#define MSM_CSR_PHYS 0xC0100000 +#define MSM_CSR_SIZE SZ_4K -#define MSM_DMOV_BASE IOMEM(0xE0002000) -#define MSM_DMOV_PHYS 0xA9700000 -#define MSM_DMOV_SIZE SZ_4K +#define MSM_TMR_PHYS MSM_CSR_PHYS +#define MSM_TMR_BASE MSM_CSR_BASE +#define MSM_TMR_SIZE SZ_4K -#define MSM7X00_GPIO1_PHYS 0xA9200000 -#define MSM7X00_GPIO1_SIZE SZ_4K +#define MSM_GPT_BASE MSM_TMR_BASE +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x10) -#define MSM7X00_GPIO2_PHYS 0xA9300000 -#define MSM7X00_GPIO2_SIZE SZ_4K +#define MSM_GPIO1_BASE IOMEM(0xF8003000) +#define MSM_GPIO1_PHYS 0xA9200000 +#define MSM_GPIO1_SIZE SZ_4K -#define MSM_CLK_CTL_BASE IOMEM(0xE0005000) +#define MSM_GPIO2_BASE IOMEM(0xF8004000) +#define MSM_GPIO2_PHYS 0xA9300000 +#define MSM_GPIO2_SIZE SZ_4K + +#define MSM_CLK_CTL_BASE IOMEM(0xF8005000) #define MSM_CLK_CTL_PHYS 0xA8600000 #define MSM_CLK_CTL_SIZE SZ_4K -#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) +#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) #define MSM_SHARED_RAM_PHYS 0x01F00000 #define MSM_SHARED_RAM_SIZE SZ_1M @@ -84,6 +89,9 @@ #define MSM_SDC4_PHYS 0xA0700000 #define MSM_SDC4_SIZE SZ_4K +#define MSM_NAND_PHYS 0xA0A00000 +#define MSM_NAND_SIZE SZ_4K + #define MSM_I2C_PHYS 0xA9900000 #define MSM_I2C_SIZE SZ_4K @@ -99,17 +107,30 @@ #define MSM_MDP_PHYS 0xAA200000 #define MSM_MDP_SIZE 0x000F0000 +#define MSM_MDC_BASE IOMEM(0xF8200000) #define MSM_MDC_PHYS 0xAA500000 #define MSM_MDC_SIZE SZ_1M +#define MSM_AD5_BASE IOMEM(0xF8300000) #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) -#ifndef __ASSEMBLY__ +#define MSM_VFE_PHYS 0xA0F00000 +#define MSM_VFE_SIZE SZ_1M -extern void __iomem *__msm_ioremap_caller(unsigned long phys_addr, size_t size, - unsigned int mtype, void *caller); +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 +#define MSM_SSBI_PHYS 0xA8100000 +#define MSM_SSBI_SIZE SZ_4K + +#define MSM_TSSC_PHYS 0xAA300000 +#define MSM_TSSC_SIZE SZ_4K + +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_GCC_BASE IOMEM(0xF8009000) +#define MSM_GCC_PHYS 0xC0182000 +#define MSM_GCC_SIZE SZ_4K #endif #endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h index f944fe65a65..dfc6f231a8e 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2011 Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -35,70 +35,53 @@ * */ -#define MSM_VIC_BASE IOMEM(0xE0000000) -#define MSM_VIC_PHYS 0xC0080000 -#define MSM_VIC_SIZE SZ_4K +#define MSM7X30_VIC_PHYS 0xC0080000 +#define MSM7X30_VIC_SIZE SZ_4K -#define MSM7X30_CSR_PHYS 0xC0100000 -#define MSM7X30_CSR_SIZE SZ_4K +#define MSM7X30_CSR_PHYS 0xC0100000 +#define MSM7X30_CSR_SIZE SZ_4K -#define MSM_DMOV_BASE IOMEM(0xE0002000) -#define MSM_DMOV_PHYS 0xAC400000 -#define MSM_DMOV_SIZE SZ_4K +#define MSM7X30_TMR_PHYS MSM7X30_CSR_PHYS +#define MSM7X30_TMR_SIZE SZ_4K -#define MSM7X30_GPIO1_PHYS 0xAC001000 -#define MSM7X30_GPIO1_SIZE SZ_4K +#define MSM7X30_GPIO1_PHYS 0xAC001000 +#define MSM7X30_GPIO1_SIZE SZ_4K -#define MSM7X30_GPIO2_PHYS 0xAC101000 -#define MSM7X30_GPIO2_SIZE SZ_4K +#define MSM7X30_GPIO2_PHYS 0xAC101000 +#define MSM7X30_GPIO2_SIZE SZ_4K -#define MSM_CLK_CTL_BASE IOMEM(0xE0005000) -#define MSM_CLK_CTL_PHYS 0xAB800000 -#define MSM_CLK_CTL_SIZE SZ_4K +#define MSM7X30_CLK_CTL_PHYS 0xAB800000 +#define MSM7X30_CLK_CTL_SIZE SZ_4K -#define MSM_CLK_CTL_SH2_BASE IOMEM(0xE0006000) -#define MSM_CLK_CTL_SH2_PHYS 0xABA01000 -#define MSM_CLK_CTL_SH2_SIZE SZ_4K +#define MSM7X30_CLK_CTL_SH2_PHYS 0xABA01000 +#define MSM7X30_CLK_CTL_SH2_SIZE SZ_4K -#define MSM_ACC_BASE IOMEM(0xE0007000) -#define MSM_ACC_PHYS 0xC0101000 -#define MSM_ACC_SIZE SZ_4K +#define MSM7X30_ACC0_PHYS 0xC0101000 +#define MSM7X30_ACC0_SIZE SZ_4K -#define MSM_SAW_BASE IOMEM(0xE0008000) -#define MSM_SAW_PHYS 0xC0102000 -#define MSM_SAW_SIZE SZ_4K +#define MSM7X30_SAW0_PHYS 0xC0102000 +#define MSM7X30_SAW0_SIZE SZ_4K -#define MSM_GCC_BASE IOMEM(0xE0009000) -#define MSM_GCC_PHYS 0xC0182000 -#define MSM_GCC_SIZE SZ_4K +#define MSM7X30_APCS_GCC_PHYS 0xC0182000 +#define MSM7X30_APCS_GCC_SIZE SZ_4K -#define MSM_TCSR_BASE IOMEM(0xE000A000) -#define MSM_TCSR_PHYS 0xAB600000 -#define MSM_TCSR_SIZE SZ_4K +#define MSM7X30_TCSR_PHYS 0xAB600000 +#define MSM7X30_TCSR_SIZE SZ_4K -#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) -#define MSM_SHARED_RAM_PHYS 0x00100000 -#define MSM_SHARED_RAM_SIZE SZ_1M +#define MSM7X30_UART1_PHYS 0xACA00000 +#define MSM7X30_UART1_SIZE SZ_4K -#define MSM_UART1_PHYS 0xACA00000 -#define MSM_UART1_SIZE SZ_4K +#define MSM7X30_UART2_PHYS 0xACB00000 +#define MSM7X30_UART2_SIZE SZ_4K -#define MSM_UART2_PHYS 0xACB00000 -#define MSM_UART2_SIZE SZ_4K +#define MSM7X30_UART3_PHYS 0xACC00000 +#define MSM7X30_UART3_SIZE SZ_4K -#define MSM_UART3_PHYS 0xACC00000 -#define MSM_UART3_SIZE SZ_4K +#define MSM7X30_MDC_PHYS 0xAA500000 +#define MSM7X30_MDC_SIZE SZ_1M -#define MSM_MDC_BASE IOMEM(0xE0200000) -#define MSM_MDC_PHYS 0xAA500000 -#define MSM_MDC_SIZE SZ_1M - -#define MSM_AD5_BASE IOMEM(0xE0300000) -#define MSM_AD5_PHYS 0xA7000000 -#define MSM_AD5_SIZE (SZ_1M*13) - -#define MSM_HSUSB_PHYS 0xA3600000 -#define MSM_HSUSB_SIZE SZ_1K +#define MSM7X30_AD5_PHYS 0xA7000000 +#define MSM7X30_AD5_SIZE (SZ_1M*13) #ifndef __ASSEMBLY__ extern void msm_map_msm7x30_io(void); diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7xxx.h b/arch/arm/mach-msm/include/mach/msm_iomap-7xxx.h new file mode 100644 index 00000000000..4c26d08b3fa --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7xxx.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * + * The MSM peripherals are spread all over across 768MB of physical + * space, which makes just having a simple IO_ADDRESS macro to slide + * them into the right virtual location rough. Instead, we will + * provide a master phys->virt mapping for peripherals here. + * + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_7XXX_H +#define __ASM_ARCH_MSM_IOMAP_7XXX_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * MSM_VIC_BASE must be an value that can be loaded via a "mov" + * instruction, otherwise entry-macro.S will not compile. + * + * If you add or remove entries here, you'll want to edit the + * msm_io_desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define MSM7XXX_VIC_PHYS 0xC0000000 +#define MSM7XXX_VIC_SIZE SZ_4K + +#define MSM7XXX_CSR_PHYS 0xC0100000 +#define MSM7XXX_CSR_SIZE SZ_4K + +#define MSM7XXX_TMR_PHYS MSM7XXX_CSR_PHYS +#define MSM7XXX_TMR_SIZE SZ_4K + +#define MSM7XXX_GPIO1_PHYS 0xA9200000 +#define MSM7XXX_GPIO1_SIZE SZ_4K + +#define MSM7XXX_GPIO2_PHYS 0xA9300000 +#define MSM7XXX_GPIO2_SIZE SZ_4K + +#define MSM7XXX_CLK_CTL_PHYS 0xA8600000 +#define MSM7XXX_CLK_CTL_SIZE SZ_4K + +#define MSM7XXX_L2CC_PHYS 0xC0400000 +#define MSM7XXX_L2CC_SIZE SZ_4K + +#define MSM7XXX_UART1_PHYS 0xA9A00000 +#define MSM7XXX_UART1_SIZE SZ_4K + +#define MSM7XXX_UART2_PHYS 0xA9B00000 +#define MSM7XXX_UART2_SIZE SZ_4K + +#define MSM7XXX_UART3_PHYS 0xA9C00000 +#define MSM7XXX_UART3_SIZE SZ_4K + +#define MSM7XXX_MDC_PHYS 0xAA500000 +#define MSM7XXX_MDC_SIZE SZ_1M + +#define MSM7XXX_AD5_PHYS 0xAC000000 +#define MSM7XXX_AD5_SIZE (SZ_1M*13) + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8064.h b/arch/arm/mach-msm/include/mach/msm_iomap-8064.h new file mode 100644 index 00000000000..10e2b74f3dc --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8064.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * + * The MSM peripherals are spread all over across 768MB of physical + * space, which makes just having a simple IO_ADDRESS macro to slide + * them into the right virtual location rough. Instead, we will + * provide a master phys->virt mapping for peripherals here. + * + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_8064_H +#define __ASM_ARCH_MSM_IOMAP_8064_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * msm_io_desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define APQ8064_TMR_PHYS 0x0200A000 +#define APQ8064_TMR_SIZE SZ_4K + +#define APQ8064_TMR0_PHYS 0x0208A000 +#define APQ8064_TMR0_SIZE SZ_4K + +#define APQ8064_QGIC_DIST_PHYS 0x02000000 +#define APQ8064_QGIC_DIST_SIZE SZ_4K + +#define APQ8064_QGIC_CPU_PHYS 0x02002000 +#define APQ8064_QGIC_CPU_SIZE SZ_4K + +#define APQ8064_TLMM_PHYS 0x00800000 +#define APQ8064_TLMM_SIZE SZ_16K + +#define APQ8064_ACC0_PHYS 0x02088000 +#define APQ8064_ACC0_SIZE SZ_4K + +#define APQ8064_ACC1_PHYS 0x02098000 +#define APQ8064_ACC1_SIZE SZ_4K + +#define APQ8064_ACC2_PHYS 0x020A8000 +#define APQ8064_ACC2_SIZE SZ_4K + +#define APQ8064_ACC3_PHYS 0x020B8000 +#define APQ8064_ACC3_SIZE SZ_4K + +#define APQ8064_APCS_GCC_PHYS 0x02011000 +#define APQ8064_APCS_GCC_SIZE SZ_4K + +#define APQ8064_CLK_CTL_PHYS 0x00900000 +#define APQ8064_CLK_CTL_SIZE SZ_16K + +#define APQ8064_MMSS_CLK_CTL_PHYS 0x04000000 +#define APQ8064_MMSS_CLK_CTL_SIZE SZ_4K + +#define APQ8064_LPASS_CLK_CTL_PHYS 0x28000000 +#define APQ8064_LPASS_CLK_CTL_SIZE SZ_4K + +#define APQ8064_HFPLL_PHYS 0x00903000 +#define APQ8064_HFPLL_SIZE SZ_4K + +#define APQ8064_IMEM_PHYS 0x2A03F000 +#define APQ8064_IMEM_SIZE SZ_4K + +#define APQ8064_RPM_PHYS 0x00108000 +#define APQ8064_RPM_SIZE SZ_4K + +#define APQ8064_RPM_MPM_PHYS 0x00200000 +#define APQ8064_RPM_MPM_SIZE SZ_4K + +#define APQ8064_SAW0_PHYS 0x02089000 +#define APQ8064_SAW0_SIZE SZ_4K + +#define APQ8064_SAW1_PHYS 0x02099000 +#define APQ8064_SAW1_SIZE SZ_4K + +#define APQ8064_SAW2_PHYS 0x020A9000 +#define APQ8064_SAW2_SIZE SZ_4K + +#define APQ8064_SAW3_PHYS 0x020B9000 +#define APQ8064_SAW3_SIZE SZ_4K + +#define APQ8064_SAW_L2_PHYS 0x02012000 +#define APQ8064_SAW_L2_SIZE SZ_4K +#define APQ8064_QFPROM_PHYS 0x00700000 +#define APQ8064_QFPROM_SIZE SZ_4K + +#define APQ8064_SIC_NON_SECURE_PHYS 0x12100000 +#define APQ8064_SIC_NON_SECURE_SIZE SZ_64K + +#define APQ8064_HDMI_PHYS 0x04A00000 +#define APQ8064_HDMI_SIZE SZ_4K + +#ifdef CONFIG_DEBUG_APQ8064_UART +#define MSM_DEBUG_UART_BASE IOMEM(0xFA740000) +#define MSM_DEBUG_UART_PHYS 0x16640000 +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8625.h b/arch/arm/mach-msm/include/mach/msm_iomap-8625.h new file mode 100644 index 00000000000..3435c2add90 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8625.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * + * The MSM peripherals are spread all over across 768MB of physical + * space, which makes just having a simple IO_ADDRESS macro to slide + * them into the right virtual location rough. Instead, we will + * provide a master phys->virt mapping for peripherals here. + * + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_8625_H +#define __ASM_ARCH_MSM_IOMAP_8625_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * msm_io_desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define MSM8625_TMR_PHYS 0xC0800000 +#define MSM8625_TMR_SIZE SZ_4K + +#define MSM8625_TMR0_PHYS 0xC0100000 +#define MSM8625_TMR0_SIZE SZ_4K + +#define MSM8625_CLK_CTL_PHYS 0xA8600000 +#define MSM8625_CLK_CTL_SIZE SZ_4K + +#define MSM8625_QGIC_DIST_PHYS 0xC0000000 +#define MSM8625_QGIC_DIST_SIZE SZ_4K + +#define MSM8625_QGIC_CPU_PHYS 0xC0002000 +#define MSM8625_QGIC_CPU_SIZE SZ_4K + +#define MSM8625_SCU_PHYS 0xC0600000 +#define MSM8625_SCU_SIZE SZ_256 + +#define MSM8625_SAW0_PHYS 0xC0200000 +#define MSM8625_SAW0_SIZE SZ_4K + +#define MSM8625_SAW1_PHYS 0xC0700000 +#define MSM8625_SAW1_SIZE SZ_4K + +#define MSM8625_CFG_CTL_PHYS 0xA9800000 +#define MSM8625_CFG_CTL_SIZE SZ_4K + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8930.h b/arch/arm/mach-msm/include/mach/msm_iomap-8930.h new file mode 100644 index 00000000000..f3f8b8fcca3 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8930.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * + * The MSM peripherals are spread all over across 768MB of physical + * space, which makes just having a simple IO_ADDRESS macro to slide + * them into the right virtual location rough. Instead, we will + * provide a master phys->virt mapping for peripherals here. + * + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_8930_H +#define __ASM_ARCH_MSM_IOMAP_8930_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * msm_io_desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define MSM8930_TMR_PHYS 0x0200A000 +#define MSM8930_TMR_SIZE SZ_4K + +#define MSM8930_TMR0_PHYS 0x0208A000 +#define MSM8930_TMR0_SIZE SZ_4K + +#define MSM8930_RPM_PHYS 0x00108000 +#define MSM8930_RPM_SIZE SZ_4K + +#define MSM8930_RPM_MPM_PHYS 0x00200000 +#define MSM8930_RPM_MPM_SIZE SZ_4K + +#define MSM8930_TCSR_PHYS 0x1A400000 +#define MSM8930_TCSR_SIZE SZ_4K + +#define MSM8930_APCS_GCC_PHYS 0x02011000 +#define MSM8930_APCS_GCC_SIZE SZ_4K + +#define MSM8930_SAW_L2_PHYS 0x02012000 +#define MSM8930_SAW_L2_SIZE SZ_4K + +#define MSM8930_SAW0_PHYS 0x02089000 +#define MSM8930_SAW0_SIZE SZ_4K + +#define MSM8930_SAW1_PHYS 0x02099000 +#define MSM8930_SAW1_SIZE SZ_4K + +#define MSM8930_IMEM_PHYS 0x2A03F000 +#define MSM8930_IMEM_SIZE SZ_4K + +#define MSM8930_ACC0_PHYS 0x02088000 +#define MSM8930_ACC0_SIZE SZ_4K + +#define MSM8930_ACC1_PHYS 0x02098000 +#define MSM8930_ACC1_SIZE SZ_4K + +#define MSM8930_QGIC_DIST_PHYS 0x02000000 +#define MSM8930_QGIC_DIST_SIZE SZ_4K + +#define MSM8930_QGIC_CPU_PHYS 0x02002000 +#define MSM8930_QGIC_CPU_SIZE SZ_4K + +#define MSM8930_CLK_CTL_PHYS 0x00900000 +#define MSM8930_CLK_CTL_SIZE SZ_16K + +#define MSM8930_MMSS_CLK_CTL_PHYS 0x04000000 +#define MSM8930_MMSS_CLK_CTL_SIZE SZ_4K + +#define MSM8930_LPASS_CLK_CTL_PHYS 0x28000000 +#define MSM8930_LPASS_CLK_CTL_SIZE SZ_4K + +#define MSM8930_HFPLL_PHYS 0x00903000 +#define MSM8930_HFPLL_SIZE SZ_4K + +#define MSM8930_TLMM_PHYS 0x00800000 +#define MSM8930_TLMM_SIZE SZ_16K + +#define MSM8930_DMOV_PHYS 0x18320000 +#define MSM8930_DMOV_SIZE SZ_1M + +#define MSM8930_SIC_NON_SECURE_PHYS 0x12100000 +#define MSM8930_SIC_NON_SECURE_SIZE SZ_64K + +#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4) +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24) + +#define MSM8930_HDMI_PHYS 0x04A00000 +#define MSM8930_HDMI_SIZE SZ_4K + +#ifdef CONFIG_DEBUG_MSM8930_UART +#define MSM_DEBUG_UART_BASE IOMEM(0xFA740000) +#define MSM_DEBUG_UART_PHYS 0x16440000 +#endif + +#define MSM8930_QFPROM_PHYS 0x00700000 +#define MSM8930_QFPROM_SIZE SZ_4K + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8960.h b/arch/arm/mach-msm/include/mach/msm_iomap-8960.h index a1752c0284f..54c901f4a84 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-8960.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8960.h @@ -32,18 +32,79 @@ * */ +#define MSM8960_TMR_PHYS 0x0200A000 +#define MSM8960_TMR_SIZE SZ_4K -#define MSM8960_QGIC_DIST_PHYS 0x02000000 -#define MSM8960_QGIC_DIST_SIZE SZ_4K +#define MSM8960_TMR0_PHYS 0x0208A000 +#define MSM8960_TMR0_SIZE SZ_4K -#define MSM8960_QGIC_CPU_PHYS 0x02002000 -#define MSM8960_QGIC_CPU_SIZE SZ_4K +#define MSM8960_RPM_PHYS 0x00108000 +#define MSM8960_RPM_SIZE SZ_4K -#define MSM8960_TMR_PHYS 0x0200A000 -#define MSM8960_TMR_SIZE SZ_4K +#define MSM8960_RPM_MPM_PHYS 0x00200000 +#define MSM8960_RPM_MPM_SIZE SZ_4K -#define MSM8960_TMR0_PHYS 0x0208A000 -#define MSM8960_TMR0_SIZE SZ_4K +#define MSM8960_TCSR_PHYS 0x1A400000 +#define MSM8960_TCSR_SIZE SZ_4K + +#define MSM8960_APCS_GCC_PHYS 0x02011000 +#define MSM8960_APCS_GCC_SIZE SZ_4K + +#define MSM8960_SAW_L2_PHYS 0x02012000 +#define MSM8960_SAW_L2_SIZE SZ_4K + +#define MSM8960_SAW0_PHYS 0x02089000 +#define MSM8960_SAW0_SIZE SZ_4K + +#define MSM8960_SAW1_PHYS 0x02099000 +#define MSM8960_SAW1_SIZE SZ_4K + +#define MSM8960_IMEM_PHYS 0x2A03F000 +#define MSM8960_IMEM_SIZE SZ_4K + +#define MSM8960_ACC0_PHYS 0x02088000 +#define MSM8960_ACC0_SIZE SZ_4K + +#define MSM8960_ACC1_PHYS 0x02098000 +#define MSM8960_ACC1_SIZE SZ_4K + +#define MSM8960_QGIC_DIST_PHYS 0x02000000 +#define MSM8960_QGIC_DIST_SIZE SZ_4K + +#define MSM8960_QGIC_CPU_PHYS 0x02002000 +#define MSM8960_QGIC_CPU_SIZE SZ_4K + +#define MSM8960_CLK_CTL_PHYS 0x00900000 +#define MSM8960_CLK_CTL_SIZE SZ_16K + +#define MSM8960_MMSS_CLK_CTL_PHYS 0x04000000 +#define MSM8960_MMSS_CLK_CTL_SIZE SZ_4K + +#define MSM8960_LPASS_CLK_CTL_PHYS 0x28000000 +#define MSM8960_LPASS_CLK_CTL_SIZE SZ_4K + +#define MSM8960_HFPLL_PHYS 0x00903000 +#define MSM8960_HFPLL_SIZE SZ_4K + +#define MSM8960_TLMM_PHYS 0x00800000 +#define MSM8960_TLMM_SIZE SZ_16K + +#define MSM8960_SIC_NON_SECURE_PHYS 0x12100000 +#define MSM8960_SIC_NON_SECURE_SIZE SZ_64K + +#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4) +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24) + +#define MSM8960_HDMI_PHYS 0x04A00000 +#define MSM8960_HDMI_SIZE SZ_4K + +#ifdef CONFIG_DEBUG_MSM8960_UART +#define MSM_DEBUG_UART_BASE IOMEM(0xFA740000) +#define MSM_DEBUG_UART_PHYS 0x16440000 +#endif + +#define MSM8960_QFPROM_PHYS 0x00700000 +#define MSM8960_QFPROM_SIZE SZ_4K #ifdef CONFIG_DEBUG_MSM8960_UART #define MSM_DEBUG_UART_BASE 0xE1040000 diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h index da77cc1d545..a1b32ec6775 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2011 Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -35,43 +35,43 @@ * */ -#define MSM_VIC_BASE IOMEM(0xE0000000) +#define MSM_VIC_BASE IOMEM(0xFA000000) #define MSM_VIC_PHYS 0xAC000000 #define MSM_VIC_SIZE SZ_4K -#define QSD8X50_CSR_PHYS 0xAC100000 -#define QSD8X50_CSR_SIZE SZ_4K +#define MSM_CSR_BASE IOMEM(0xFA001000) +#define MSM_CSR_PHYS 0xAC100000 +#define MSM_CSR_SIZE SZ_4K -#define MSM_DMOV_BASE IOMEM(0xE0002000) -#define MSM_DMOV_PHYS 0xA9700000 -#define MSM_DMOV_SIZE SZ_4K +#define MSM_TMR_PHYS MSM_CSR_PHYS +#define MSM_TMR_BASE MSM_CSR_BASE +#define MSM_TMR_SIZE SZ_4K -#define QSD8X50_GPIO1_PHYS 0xA9000000 -#define QSD8X50_GPIO1_SIZE SZ_4K +#define MSM_GPIO1_BASE IOMEM(0xFA003000) +#define MSM_GPIO1_PHYS 0xA9000000 +#define MSM_GPIO1_SIZE SZ_4K -#define QSD8X50_GPIO2_PHYS 0xA9100000 -#define QSD8X50_GPIO2_SIZE SZ_4K +#define MSM_GPIO2_BASE IOMEM(0xFA004000) +#define MSM_GPIO2_PHYS 0xA9100000 +#define MSM_GPIO2_SIZE SZ_4K -#define MSM_CLK_CTL_BASE IOMEM(0xE0005000) +#define MSM_CLK_CTL_BASE IOMEM(0xFA005000) #define MSM_CLK_CTL_PHYS 0xA8600000 #define MSM_CLK_CTL_SIZE SZ_4K -#define MSM_SIRC_BASE IOMEM(0xE1006000) +#define MSM_SIRC_BASE IOMEM(0xFB006000) #define MSM_SIRC_PHYS 0xAC200000 #define MSM_SIRC_SIZE SZ_4K -#define MSM_SCPLL_BASE IOMEM(0xE1007000) +#define MSM_SCPLL_BASE IOMEM(0xFB007000) #define MSM_SCPLL_PHYS 0xA8800000 #define MSM_SCPLL_SIZE SZ_4K -#ifdef CONFIG_MSM_SOC_REV_A -#define MSM_SMI_BASE 0xE0000000 -#else -#define MSM_SMI_BASE 0x00000000 -#endif +#define MSM_TCSR_BASE IOMEM(0xFB008000) +#define MSM_TCSR_PHYS 0xA8700000 +#define MSM_TCSR_SIZE SZ_4K -#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) -#define MSM_SHARED_RAM_PHYS (MSM_SMI_BASE + 0x00100000) +#define MSM_SHARED_RAM_BASE IOMEM(0xFA100000) #define MSM_SHARED_RAM_SIZE SZ_1M #define MSM_UART1_PHYS 0xA9A00000 @@ -83,47 +83,12 @@ #define MSM_UART3_PHYS 0xA9C00000 #define MSM_UART3_SIZE SZ_4K -#define MSM_MDC_BASE IOMEM(0xE0200000) +#define MSM_MDC_BASE IOMEM(0xFA200000) #define MSM_MDC_PHYS 0xAA500000 #define MSM_MDC_SIZE SZ_1M -#define MSM_AD5_BASE IOMEM(0xE0300000) +#define MSM_AD5_BASE IOMEM(0xFA300000) #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) - -#define MSM_I2C_SIZE SZ_4K -#define MSM_I2C_PHYS 0xA9900000 - -#define MSM_HSUSB_PHYS 0xA0800000 -#define MSM_HSUSB_SIZE SZ_1K - -#define MSM_NAND_PHYS 0xA0A00000 - - -#define MSM_TSIF_PHYS (0xa0100000) -#define MSM_TSIF_SIZE (0x200) - -#define MSM_TSSC_PHYS 0xAA300000 - -#define MSM_UART1DM_PHYS 0xA0200000 -#define MSM_UART2DM_PHYS 0xA0900000 - - -#define MSM_SDC1_PHYS 0xA0300000 -#define MSM_SDC1_SIZE SZ_4K - -#define MSM_SDC2_PHYS 0xA0400000 -#define MSM_SDC2_SIZE SZ_4K - -#define MSM_SDC3_PHYS 0xA0500000 -#define MSM_SDC3_SIZE SZ_4K - -#define MSM_SDC4_PHYS 0xA0600000 -#define MSM_SDC4_SIZE SZ_4K - -#ifndef __ASSEMBLY__ -extern void msm_map_qsd8x50_io(void); -#endif - #endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h index 5aed57dc808..4f90ea57a61 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h @@ -35,35 +35,99 @@ * */ -#define MSM8X60_QGIC_DIST_PHYS 0x02080000 -#define MSM8X60_QGIC_DIST_SIZE SZ_4K +#define MSM_QGIC_DIST_BASE IOMEM(0xFA000000) +#define MSM_QGIC_DIST_PHYS 0x02080000 +#define MSM_QGIC_DIST_SIZE SZ_4K -#define MSM8X60_QGIC_CPU_PHYS 0x02081000 -#define MSM8X60_QGIC_CPU_SIZE SZ_4K +#define MSM_QGIC_CPU_BASE IOMEM(0xFA001000) +#define MSM_QGIC_CPU_PHYS 0x02081000 +#define MSM_QGIC_CPU_SIZE SZ_4K -#define MSM_ACC_BASE IOMEM(0xF0002000) +#define MSM_ACC_BASE IOMEM(0xFA002000) #define MSM_ACC_PHYS 0x02001000 #define MSM_ACC_SIZE SZ_4K -#define MSM_GCC_BASE IOMEM(0xF0003000) +#define MSM_GCC_BASE IOMEM(0xFA003000) #define MSM_GCC_PHYS 0x02082000 #define MSM_GCC_SIZE SZ_4K -#define MSM_TLMM_BASE IOMEM(0xF0004000) +#define MSM_TLMM_BASE IOMEM(0xFA004000) #define MSM_TLMM_PHYS 0x00800000 #define MSM_TLMM_SIZE SZ_16K -#define MSM_SHARED_RAM_BASE IOMEM(0xF0100000) +#define MSM_RPM_BASE IOMEM(0xFA008000) +#define MSM_RPM_PHYS 0x00104000 +#define MSM_RPM_SIZE SZ_4K + +#define MSM_CLK_CTL_BASE IOMEM(0xFA010000) +#define MSM_CLK_CTL_PHYS 0x00900000 +#define MSM_CLK_CTL_SIZE SZ_16K + +#define MSM_MMSS_CLK_CTL_BASE IOMEM(0xFA014000) +#define MSM_MMSS_CLK_CTL_PHYS 0x04000000 +#define MSM_MMSS_CLK_CTL_SIZE SZ_4K + +#define MSM_LPASS_CLK_CTL_BASE IOMEM(0xFA015000) +#define MSM_LPASS_CLK_CTL_PHYS 0x28000000 +#define MSM_LPASS_CLK_CTL_SIZE SZ_4K + +#define MSM_TMR_BASE IOMEM(0xFA016000) +#define MSM_TMR_PHYS 0x02000000 +#define MSM_TMR_SIZE SZ_4K + +#define MSM_TMR0_BASE IOMEM(0xFA017000) +#define MSM_TMR0_PHYS 0x02040000 +#define MSM_TMR0_SIZE SZ_4K + +#define MSM_SCPLL_BASE IOMEM(0xFA018000) +#define MSM_SCPLL_PHYS 0x00903000 +#define MSM_SCPLL_SIZE SZ_1K + +#define MSM_SHARED_RAM_BASE IOMEM(0xFA200000) #define MSM_SHARED_RAM_SIZE SZ_1M -#define MSM8X60_TMR_PHYS 0x02000000 -#define MSM8X60_TMR_SIZE SZ_4K +#define MSM_ACC0_BASE IOMEM(0xFA300000) +#define MSM_ACC0_PHYS 0x02041000 +#define MSM_ACC0_SIZE SZ_4K -#define MSM8X60_TMR0_PHYS 0x02040000 -#define MSM8X60_TMR0_SIZE SZ_4K +#define MSM_ACC1_BASE IOMEM(0xFA301000) +#define MSM_ACC1_PHYS 0x02051000 +#define MSM_ACC1_SIZE SZ_4K + +#define MSM_RPM_MPM_BASE IOMEM(0xFA302000) +#define MSM_RPM_MPM_PHYS 0x00200000 +#define MSM_RPM_MPM_SIZE SZ_4K + +#define MSM_SAW0_BASE IOMEM(0xFA303000) +#define MSM_SAW0_PHYS 0x02042000 +#define MSM_SAW0_SIZE SZ_4K + +#define MSM_SAW1_BASE IOMEM(0xFA304000) +#define MSM_SAW1_PHYS 0x02052000 +#define MSM_SAW1_SIZE SZ_4K + +#define MSM_SIC_NON_SECURE_BASE IOMEM(0xFA600000) +#define MSM_SIC_NON_SECURE_PHYS 0x12100000 +#define MSM_SIC_NON_SECURE_SIZE SZ_64K + +#define MSM_QFPROM_BASE IOMEM(0xFA700000) +#define MSM_QFPROM_PHYS 0x00700000 +#define MSM_QFPROM_SIZE SZ_4K + +#define MSM_TCSR_BASE IOMEM(0xFA701000) +#define MSM_TCSR_PHYS 0x16B00000 +#define MSM_TCSR_SIZE SZ_4K + +#define MSM_IMEM_BASE IOMEM(0xFA702000) +#define MSM_IMEM_PHYS 0x2A05F000 +#define MSM_IMEM_SIZE SZ_4K + +#define MSM_HDMI_BASE IOMEM(0xFA800000) +#define MSM_HDMI_PHYS 0x04A00000 +#define MSM_HDMI_SIZE SZ_4K #ifdef CONFIG_DEBUG_MSM8660_UART -#define MSM_DEBUG_UART_BASE 0xE1040000 +#define MSM_DEBUG_UART_BASE 0xFBC40000 #define MSM_DEBUG_UART_PHYS 0x19C40000 #endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9615.h b/arch/arm/mach-msm/include/mach/msm_iomap-9615.h new file mode 100644 index 00000000000..fc9b198a308 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-9615.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * + * The MSM peripherals are spread all over across 768MB of physical + * space, which makes just having a simple IO_ADDRESS macro to slide + * them into the right virtual location rough. Instead, we will + * provide a master phys->virt mapping for peripherals here. + * + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_9615_H +#define __ASM_ARCH_MSM_IOMAP_9615_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * msm_io_desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define MSM9615_TMR_PHYS 0x0200A000 +#define MSM9615_TMR_SIZE SZ_4K + +#define MSM9615_QGIC_DIST_PHYS 0x02000000 +#define MSM9615_QGIC_DIST_SIZE SZ_4K + +#define MSM9615_QGIC_CPU_PHYS 0x02002000 +#define MSM9615_QGIC_CPU_SIZE SZ_4K + +#define MSM9615_TLMM_PHYS 0x00800000 +#define MSM9615_TLMM_SIZE SZ_1M + +#define MSM9615_ACC0_PHYS 0x02008000 +#define MSM9615_ACC0_SIZE SZ_4K + +#define MSM9615_APCS_GCC_PHYS 0x02011000 +#define MSM9615_APCS_GCC_SIZE SZ_4K + +#define MSM9615_SAW0_PHYS 0x02009000 +#define MSM9615_SAW0_SIZE SZ_4K + +#define MSM9615_TCSR_PHYS 0x1A400000 +#define MSM9615_TCSR_SIZE SZ_4K + +#define MSM9615_L2CC_PHYS 0x02040000 +#define MSM9615_L2CC_SIZE SZ_4K + +#define MSM9615_CLK_CTL_PHYS 0x00900000 +#define MSM9615_CLK_CTL_SIZE SZ_16K + +#define MSM9615_LPASS_CLK_CTL_PHYS 0x28000000 +#define MSM9615_LPASS_CLK_CTL_SIZE SZ_4K + +#define MSM9615_RPM_PHYS 0x00108000 +#define MSM9615_RPM_SIZE SZ_4K + +#define MSM9615_RPM_MPM_PHYS 0x00200000 +#define MSM9615_RPM_MPM_SIZE SZ_4K + +#define MSM9615_APCS_GLB_PHYS 0x02010000 +#define MSM9615_APCS_GLB_SIZE SZ_4K + +#define MSM9615_HSUSB_PHYS 0x12500000 +#define MSM9615_HSUSB_SIZE SZ_4K + +#define MSM9615_HSIC_PHYS 0x12540000 +#define MSM9615_HSIC_SIZE SZ_4K + +#define MSM9615_QFPROM_PHYS 0x00700000 +#define MSM9615_QFPROM_SIZE SZ_4K + +#define MSM9615_IMEM_PHYS 0x2B000000 +#define MSM9615_IMEM_SIZE SZ_4K + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h new file mode 100644 index 00000000000..493cf360dda --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_MSM9625_H +#define __ASM_ARCH_MSM_IOMAP_MSM9625_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * io desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define MSM9625_SHARED_RAM_PHYS 0x18D00000 + +#define MSM9625_APCS_GCC_PHYS 0xF9011000 +#define MSM9625_APCS_GCC_SIZE SZ_4K + +#define MSM9625_TMR_PHYS 0xF9021000 +#define MSM9625_TMR_SIZE SZ_4K + +#define MSM9625_TLMM_PHYS 0xFD510000 +#define MSM9625_TLMM_SIZE SZ_16K + +#ifdef CONFIG_DEBUG_MSM9625_UART +#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000) +#define MSM_DEBUG_UART_PHYS 0xF991E000 +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-copper.h b/arch/arm/mach-msm/include/mach/msm_iomap-copper.h new file mode 100644 index 00000000000..441f82af294 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-copper.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_IOMAP_COPPER_H +#define __ASM_ARCH_MSM_IOMAP_COPPER_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * io desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define COPPER_MSM_SHARED_RAM_PHYS 0x0FA00000 + +#define COPPER_QGIC_DIST_PHYS 0xF9000000 +#define COPPER_QGIC_DIST_SIZE SZ_4K + +#define COPPER_QGIC_CPU_PHYS 0xF9002000 +#define COPPER_QGIC_CPU_SIZE SZ_4K + +#define COPPER_APCS_GCC_PHYS 0xF9011000 +#define COPPER_APCS_GCC_SIZE SZ_4K + +#define COPPER_TLMM_PHYS 0xFD510000 +#define COPPER_TLMM_SIZE SZ_16K + +#ifdef CONFIG_DEBUG_MSMCOPPER_UART +#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000) +#define MSM_DEBUG_UART_PHYS 0xF991E000 +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-fsm9xxx.h b/arch/arm/mach-msm/include/mach/msm_iomap-fsm9xxx.h new file mode 100644 index 00000000000..c30c9e4c949 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_iomap-fsm9xxx.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ASM_ARCH_MSM_IOMAP_FSM9XXX_H +#define __ASM_ARCH_MSM_IOMAP_FSM9XXX_H + +/* Physical base address and size of peripherals. + * Ordered by the virtual base addresses they will be mapped at. + * + * If you add or remove entries here, you'll want to edit the + * msm_io_desc array in arch/arm/mach-msm/io.c to reflect your + * changes. + * + */ + +#define MSM_VIC_BASE IOMEM(0xFA000000) +#define MSM_VIC_PHYS 0x9C080000 +#define MSM_VIC_SIZE SZ_4K + +#define MSM_SIRC_BASE IOMEM(0xFA001000) +#define MSM_SIRC_PHYS 0x94190000 +#define MSM_SIRC_SIZE SZ_4K + +#define MSM_CSR_BASE IOMEM(0xFA002000) +#define MSM_CSR_PHYS 0x9C000000 +#define MSM_CSR_SIZE SZ_4K + +#define MSM_TMR_BASE MSM_CSR_BASE + +#define MSM_TLMM_BASE IOMEM(0xFA003000) +#define MSM_TLMM_PHYS 0x94040000 +#define MSM_TLMM_SIZE SZ_4K + +#define MSM_TCSR_BASE IOMEM(0xFA004000) +#define MSM_TCSR_PHYS 0x94030000 +#define MSM_TCSR_SIZE SZ_4K + +#define MSM_CLK_CTL_BASE IOMEM(0xFA005000) +#define MSM_CLK_CTL_PHYS 0x94020000 +#define MSM_CLK_CTL_SIZE SZ_4K + +#define MSM_ACC_BASE IOMEM(0xFA006000) +#define MSM_ACC_PHYS 0x9C001000 +#define MSM_ACC_SIZE SZ_4K + +#define MSM_SAW_BASE IOMEM(0xFA007000) +#define MSM_SAW_PHYS 0x9C002000 +#define MSM_SAW_SIZE SZ_4K + +#define MSM_GCC_BASE IOMEM(0xFA008000) +#define MSM_GCC_PHYS 0x9C082000 +#define MSM_GCC_SIZE SZ_4K + +#define MSM_GRFC_BASE IOMEM(0xFA009000) +#define MSM_GRFC_PHYS 0x94038000 +#define MSM_GRFC_SIZE SZ_4K + +#define MSM_QFP_FUSE_BASE IOMEM(0xFA010000) +#define MSM_QFP_FUSE_PHYS 0x80000000 +#define MSM_QFP_FUSE_SIZE SZ_32K + +#define MSM_HH_BASE IOMEM(0xFA100000) +#define MSM_HH_PHYS 0x94200000 +#define MSM_HH_SIZE SZ_1M + +#define MSM_SHARED_RAM_BASE IOMEM(0xFA200000) +#define MSM_SHARED_RAM_SIZE SZ_1M + +#define MSM_UART1_PHYS 0x94000000 +#define MSM_UART1_SIZE SZ_4K + +#define MSM_UART2_PHYS 0x94100000 +#define MSM_UART2_SIZE SZ_4K + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h index 00afdfb8c38..75cc43ac070 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -37,37 +37,99 @@ * */ -#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_DEBUG_UART_SIZE SZ_4K + +#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) \ + || defined(CONFIG_DEBUG_MSM_UART3) +#define MSM_DEBUG_UART_BASE 0xFC000000 +#define MSM_DEBUG_UART_PHYS CONFIG_MSM_DEBUG_UART_PHYS +#endif + +#define MSM8625_WARM_BOOT_PHYS 0x0FD00000 + + +#if defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \ + defined(CONFIG_ARCH_MSM8930) || defined(CONFIG_ARCH_MSM9615) || \ + defined(CONFIG_ARCH_MSMCOPPER) || defined(CONFIG_ARCH_MSM7X27) || \ + defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X01A) || \ + defined(CONFIG_ARCH_MSM8625) || defined(CONFIG_ARCH_MSM7X30) || \ + defined(CONFIG_ARCH_MSM9625) + +/* Unified iomap */ + +#define MSM_TMR_BASE IOMEM(0xFA000000) /* 4K */ +#define MSM_TMR0_BASE IOMEM(0xFA001000) /* 4K */ +#define MSM_QGIC_DIST_BASE IOMEM(0xFA002000) /* 4K */ +#define MSM_QGIC_CPU_BASE IOMEM(0xFA003000) /* 4K */ +#define MSM_TCSR_BASE IOMEM(0xFA004000) /* 4K */ +#define MSM_APCS_GCC_BASE IOMEM(0xFA006000) /* 4K */ +#define MSM_SAW_L2_BASE IOMEM(0xFA007000) /* 4K */ +#define MSM_SAW0_BASE IOMEM(0xFA008000) /* 4K */ +#define MSM_SAW1_BASE IOMEM(0xFA009000) /* 4K */ +#define MSM_IMEM_BASE IOMEM(0xFA00A000) /* 4K */ +#define MSM_ACC0_BASE IOMEM(0xFA00B000) /* 4K */ +#define MSM_ACC1_BASE IOMEM(0xFA00C000) /* 4K */ +#define MSM_ACC2_BASE IOMEM(0xFA00D000) /* 4K */ +#define MSM_ACC3_BASE IOMEM(0xFA00E000) /* 4K */ +#define MSM_CLK_CTL_BASE IOMEM(0xFA010000) /* 16K */ +#define MSM_MMSS_CLK_CTL_BASE IOMEM(0xFA014000) /* 4K */ +#define MSM_LPASS_CLK_CTL_BASE IOMEM(0xFA015000) /* 4K */ +#define MSM_HFPLL_BASE IOMEM(0xFA016000) /* 4K */ +#define MSM_TLMM_BASE IOMEM(0xFA017000) /* 16K */ +#define MSM_SHARED_RAM_BASE IOMEM(0xFA300000) /* 2M */ +#define MSM_SIC_NON_SECURE_BASE IOMEM(0xFA600000) /* 64K */ +#define MSM_HDMI_BASE IOMEM(0xFA800000) /* 4K */ +#define MSM_RPM_BASE IOMEM(0xFA801000) /* 4K */ +#define MSM_RPM_MPM_BASE IOMEM(0xFA802000) /* 4K */ +#define MSM_QFPROM_BASE IOMEM(0xFA700000) /* 4K */ +#define MSM_L2CC_BASE IOMEM(0xFA701000) /* 4K */ +#define MSM_APCS_GLB_BASE IOMEM(0xFA702000) /* 4K */ +#define MSM_SAW2_BASE IOMEM(0xFA703000) /* 4k */ +#define MSM_SAW3_BASE IOMEM(0xFA704000) /* 4k */ +#define MSM_VIC_BASE IOMEM(0xFA100000) /* 4K */ +#define MSM_CSR_BASE IOMEM(0xFA101000) /* 4K */ +#define MSM_GPIO1_BASE IOMEM(0xFA102000) /* 4K */ +#define MSM_GPIO2_BASE IOMEM(0xFA103000) /* 4K */ +#define MSM_SCU_BASE IOMEM(0xFA104000) /* 4K */ +#define MSM_CFG_CTL_BASE IOMEM(0xFA105000) /* 4K */ +#define MSM_CLK_CTL_SH2_BASE IOMEM(0xFA106000) /* 4K */ +#define MSM_MDC_BASE IOMEM(0xFA400000) /* 1M */ +#define MSM_AD5_BASE IOMEM(0xFA900000) /* 13M (D00000) + 0xFB600000 */ + +#define MSM_STRONGLY_ORDERED_PAGE 0xFA0F0000 +#define MSM8625_SECONDARY_PHYS 0x0FE00000 + + +#if defined(CONFIG_ARCH_MSM9615) || defined(CONFIG_ARCH_MSM7X27) \ + || defined(CONFIG_ARCH_MSM7X30) +#define MSM_SHARED_RAM_SIZE SZ_1M +#else +#define MSM_SHARED_RAM_SIZE SZ_2M +#endif + +#include "msm_iomap-7xxx.h" #include "msm_iomap-7x30.h" -#elif defined(CONFIG_ARCH_QSD8X50) +#include "msm_iomap-8625.h" +#include "msm_iomap-8960.h" +#include "msm_iomap-8930.h" +#include "msm_iomap-8064.h" +#include "msm_iomap-9615.h" +#include "msm_iomap-copper.h" +#include "msm_iomap-9625.h" + +#else +/* Legacy single-target iomap */ +#if defined(CONFIG_ARCH_QSD8X50) #include "msm_iomap-8x50.h" #elif defined(CONFIG_ARCH_MSM8X60) #include "msm_iomap-8x60.h" +#elif defined(CONFIG_ARCH_FSM9XXX) +#include "msm_iomap-fsm9xxx.h" #else -#include "msm_iomap-7x00.h" +#error "Target compiled without IO map\n" #endif -#include "msm_iomap-8960.h" - -#define MSM_DEBUG_UART_SIZE SZ_4K -#if defined(CONFIG_DEBUG_MSM_UART1) -#define MSM_DEBUG_UART_BASE 0xE1000000 -#define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS -#elif defined(CONFIG_DEBUG_MSM_UART2) -#define MSM_DEBUG_UART_BASE 0xE1000000 -#define MSM_DEBUG_UART_PHYS MSM_UART2_PHYS -#elif defined(CONFIG_DEBUG_MSM_UART3) -#define MSM_DEBUG_UART_BASE 0xE1000000 -#define MSM_DEBUG_UART_PHYS MSM_UART3_PHYS #endif -/* Virtual addresses shared across all MSM targets. */ -#define MSM_CSR_BASE IOMEM(0xE0001000) -#define MSM_QGIC_DIST_BASE IOMEM(0xF0000000) -#define MSM_QGIC_CPU_BASE IOMEM(0xF0001000) -#define MSM_TMR_BASE IOMEM(0xF0200000) -#define MSM_TMR0_BASE IOMEM(0xF0201000) -#define MSM_GPIO1_BASE IOMEM(0xE0003000) -#define MSM_GPIO2_BASE IOMEM(0xE0004000) - #endif diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h new file mode 100644 index 00000000000..0a203a55523 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h @@ -0,0 +1,235 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_IPC_LOGGING_H +#define _MSM_IPC_LOGGING_H + +#include + +#define MAX_MSG_SIZE 255 + +enum { + TSV_TYPE_MSG_START = 1, + TSV_TYPE_SKB = TSV_TYPE_MSG_START, + TSV_TYPE_STRING, + TSV_TYPE_MSG_END = TSV_TYPE_STRING, +}; + +struct tsv_header { + unsigned char type; + unsigned char size; /* size of data field */ +}; + +struct encode_context { + struct tsv_header hdr; + char buff[MAX_MSG_SIZE]; + int offset; +}; + +struct decode_context { + int output_format; /* 0 = debugfs */ + char *buff; /* output buffer */ + int size; /* size of output buffer */ +}; + +#if defined(CONFIG_MSM_IPC_LOGGING) +/* + * ipc_log_context_create: Create a debug log context + * Should not be called from atomic context + * + * @max_num_pages: Number of pages of logging space required (max. 10) + * @mod_name : Name of the directory entry under DEBUGFS + * + * returns context id on success, NULL on failure + */ +void *ipc_log_context_create(int max_num_pages, const char *modname); + +/* + * msg_encode_start: Start encoding a log message + * + * @ectxt: Temporary storage to hold the encoded message + * @type: Root event type defined by the module which is logging + */ +void msg_encode_start(struct encode_context *ectxt, uint32_t type); + +/* + * tsv_timestamp_write: Writes the current timestamp count + * + * @ectxt: Context initialized by calling msg_encode_start() + */ +int tsv_timestamp_write(struct encode_context *ectxt); + +/* + * tsv_pointer_write: Writes a data pointer + * + * @ectxt: Context initialized by calling msg_encode_start() + * @pointer: Pointer value to write + */ +int tsv_pointer_write(struct encode_context *ectxt, void *pointer); + +/* + * tsv_int32_write: Writes a 32-bit integer value + * + * @ectxt: Context initialized by calling msg_encode_start() + * @n: Integer to write + */ +int tsv_int32_write(struct encode_context *ectxt, int32_t n); + +/* + * tsv_int32_write: Writes a 32-bit integer value + * + * @ectxt: Context initialized by calling msg_encode_start() + * @n: Integer to write + */ +int tsv_byte_array_write(struct encode_context *ectxt, + void *data, int data_size); + +/* + * msg_encode_end: Complete the message encode process + * + * @ectxt: Temporary storage which holds the encoded message + */ +void msg_encode_end(struct encode_context *ectxt); + +/* + * msg_encode_end: Complete the message encode process + * + * @ectxt: Temporary storage which holds the encoded message + */ +void ipc_log_write(void *ctxt, struct encode_context *ectxt); + +/* + * ipc_log_string: Helper function to log a string + * + * @ilctxt: Debug Log Context created using ipc_log_context_create() + * @fmt: Data specified using format specifiers + */ +int ipc_log_string(void *ilctxt, const char *fmt, ...); + +/* + * Print a string to decode context. + * @dctxt Decode context + * @args printf args + */ +#define IPC_SPRINTF_DECODE(dctxt, args...) \ +do { \ + int i; \ + i = scnprintf(dctxt->buff, dctxt->size, args); \ + dctxt->buff += i; \ + dctxt->size -= i; \ +} while (0) + +/* + * tsv_timestamp_read: Reads a timestamp + * + * @ectxt: Context retrieved by reading from log space + * @dctxt: Temporary storage to hold the decoded message + * @format: Output format while dumping through DEBUGFS + */ +void tsv_timestamp_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format); + +/* + * tsv_pointer_read: Reads a data pointer + * + * @ectxt: Context retrieved by reading from log space + * @dctxt: Temporary storage to hold the decoded message + * @format: Output format while dumping through DEBUGFS + */ +void tsv_pointer_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format); + +/* + * tsv_int32_read: Reads a 32-bit integer value + * + * @ectxt: Context retrieved by reading from log space + * @dctxt: Temporary storage to hold the decoded message + * @format: Output format while dumping through DEBUGFS + */ +int32_t tsv_int32_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format); + +/* + * tsv_int32_read: Reads a 32-bit integer value + * + * @ectxt: Context retrieved by reading from log space + * @dctxt: Temporary storage to hold the decoded message + * @format: Output format while dumping through DEBUGFS + */ +void tsv_byte_array_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format); + +/* + * add_deserialization_func: Register a deserialization function to + * to unpack the subevents of a main event + * + * @ctxt: Debug log context to which the deserialization function has + * to be registered + * @type: Main/Root event, defined by the module which is logging, to + * which this deserialization function has to be registered. + * @dfune: Deserialization function to be registered + * + * return 0 on success, -ve value on FAILURE + */ +int add_deserialization_func(void *ctxt, int type, + void (*dfunc)(struct encode_context *, + struct decode_context *)); +#else + +void *ipc_log_context_create(int max_num_pages, const char *modname) +{ return NULL; } + +void msg_encode_start(struct encode_context *ectxt, uint32_t type) { } + +int tsv_timestamp_write(struct encode_context *ectxt) +{ return -EINVAL; } + +int tsv_pointer_write(struct encode_context *ectxt, void *pointer) +{ return -EINVAL; } + +int tsv_int32_write(struct encode_context *ectxt, int32_t n) +{ return -EINVAL; } + +int tsv_byte_array_write(struct encode_context *ectxt, + void *data, int data_size) +{ return -EINVAL; } + +void msg_encode_end(struct encode_context *ectxt) { } + +void ipc_log_write(void *ctxt, struct encode_context *ectxt) { } + +int ipc_log_string(void *ilctxt, const char *fmt, ...) +{ return -EINVAL; } + +#define IPC_SPRINTF_DECODE(dctxt, args...) do { } while (0) + +void tsv_timestamp_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) { } + +void tsv_pointer_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) { } + +int32_t tsv_int32_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) +{ return 0; } + +void tsv_byte_array_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) { } + +int add_deserialization_func(void *ctxt, int type, + void (*dfunc)(struct encode_context *, + struct decode_context *)) +{ return 0; } + +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h new file mode 100644 index 00000000000..7afb38d9ed5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +/* The MSM Hardware supports multiple flavors of physical memory. + * This file captures hardware specific information of these types. +*/ + +#ifndef __ASM_ARCH_MSM_MEMTYPES_H +#define __ASM_ARCH_MSM_MEMTYPES_H + +#include +#include + +int __init meminfo_init(unsigned int, unsigned int); +/* Redundant check to prevent this from being included outside of 7x30 */ +#if defined(CONFIG_ARCH_MSM7X30) +unsigned int get_num_populated_chipselects(void); +#endif + +unsigned int get_num_memory_banks(void); +unsigned int get_memory_bank_size(unsigned int); +unsigned int get_memory_bank_start(unsigned int); +int soc_change_memory_power(u64, u64, int); + +enum { + MEMTYPE_NONE = -1, + MEMTYPE_SMI_KERNEL = 0, + MEMTYPE_SMI, + MEMTYPE_EBI0, + MEMTYPE_EBI1, + MEMTYPE_MAX, +}; + +void msm_reserve(void); + +#define MEMTYPE_FLAGS_FIXED 0x1 +#define MEMTYPE_FLAGS_1M_ALIGN 0x2 + +struct memtype_reserve { + unsigned long start; + unsigned long size; + unsigned long limit; + int flags; +}; + +struct reserve_info { + struct memtype_reserve *memtype_reserve_table; + void (*calculate_reserve_sizes)(void); + void (*reserve_fixed_area)(unsigned long); + int (*paddr_to_memtype)(unsigned int); + unsigned long low_unstable_address; + unsigned long max_unstable_size; + unsigned long bank_size; + unsigned long fixed_area_start; + unsigned long fixed_area_size; +}; + +extern struct reserve_info *reserve_info; + +unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long); +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_migrate_pages.h b/arch/arm/mach-msm/include/mach/msm_migrate_pages.h new file mode 100644 index 00000000000..5812a64fa04 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_migrate_pages.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MACH_MSM_MIGRATE_PAGES_H_ +#define _MACH_MSM_MIGRATE_PAGES_H_ + +unsigned long get_msm_migrate_pages_status(void); +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_otg.h b/arch/arm/mach-msm/include/mach/msm_otg.h new file mode 100644 index 00000000000..178b65a2afc --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_otg.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_OTG_H +#define __ARCH_ARM_MACH_MSM_OTG_H + +#include +#include + +/* + * The otg driver needs to interact with both device side and host side + * usb controllers. it decides which controller is active at a given + * moment, using the transceiver, ID signal. + */ + +struct msm_otg_transceiver { + struct device *dev; + struct clk *clk; + struct clk *pclk; + int in_lpm; + struct msm_otg_ops *dcd_ops; + struct msm_otg_ops *hcd_ops; + int irq; + int flags; + int state; + int active; + void __iomem *regs; /* device memory/io */ + struct work_struct work; + spinlock_t lock; + struct wake_lock wlock; + + /* bind/unbind the host controller */ + int (*set_host)(struct msm_otg_transceiver *otg, + struct msm_otg_ops *hcd_ops); + + /* bind/unbind the peripheral controller */ + int (*set_peripheral)(struct msm_otg_transceiver *otg, + struct msm_otg_ops *dcd_ops); + int (*set_suspend)(struct msm_otg_transceiver *otg, + int suspend); + +}; + +struct msm_otg_ops { + void (*request)(void *, int); + void *handle; +}; + +/* for usb host and peripheral controller drivers */ +#ifdef CONFIG_USB_MSM_OTG + +extern struct msm_otg_transceiver *msm_otg_get_transceiver(void); +extern void msm_otg_put_transceiver(struct msm_otg_transceiver *xceiv); + +#else + +static inline struct msm_otg_transceiver *msm_otg_get_transceiver(void) +{ + return NULL; +} + +static inline void msm_otg_put_transceiver(struct msm_otg_transceiver *xceiv) +{ +} + +#endif /*CONFIG_USB_MSM_OTG*/ + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_pcie.h b/arch/arm/mach-msm/include/mach/msm_pcie.h new file mode 100644 index 00000000000..008c984ba7e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_pcie.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_PCIE_H +#define __ASM_ARCH_MSM_PCIE_H + +#include + +/* gpios */ +enum msm_pcie_gpio { + MSM_PCIE_GPIO_RST_N, + MSM_PCIE_GPIO_PWR_EN, + MSM_PCIE_MAX_GPIO +}; + +/* gpio info structrue */ +struct msm_pcie_gpio_info_t { + char *name; + uint32_t num; + uint32_t on; +}; + +/* msm pcie platfrom data */ +struct msm_pcie_platform { + struct msm_pcie_gpio_info_t *gpio; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h new file mode 100644 index 00000000000..da2abe2413a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -0,0 +1,120 @@ +/* arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_MSM_QDSP6_Q6AUDIO_ +#define _MACH_MSM_QDSP6_Q6AUDIO_ + +#define AUDIO_FLAG_READ 0 +#define AUDIO_FLAG_WRITE 1 +#define AUDIO_FLAG_INCALL_MIXED 2 + +#include + +enum { + DEVICE_UNMUTE = 0, + DEVICE_MUTE, + STREAM_UNMUTE, + STREAM_MUTE, +}; + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t size; + uint32_t used; /* 1 = CPU is waiting for DSP to consume this buf */ + uint32_t actual_size; /* actual number of bytes read by DSP */ +}; + +struct audio_client { + struct audio_buffer buf[2]; + int cpu_buf; /* next buffer the CPU will touch */ + int dsp_buf; /* next buffer the DSP will touch */ + int running; + int session; + + wait_queue_head_t wait; + struct dal_client *client; + + int cb_status; + uint32_t flags; +}; + +/* Obtain a 16bit signed, interleaved audio channel of the specified + * rate (Hz) and channels (1 or 2), with two buffers of bufsz bytes. + */ +struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t flags, + uint32_t acdb_id); + +struct audio_client *q6audio_open_auxpcm(uint32_t rate, uint32_t channels, + uint32_t flags, uint32_t acdb_id); + +struct audio_client *q6voice_open(uint32_t flags); + +struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t acdb_id); + +struct audio_client *q6audio_open_dtmf(uint32_t rate, uint32_t channels, + uint32_t acdb_id); +int q6audio_play_dtmf(struct audio_client *ac, uint16_t dtmf_hi, + uint16_t dtmf_low, uint16_t duration, uint16_t rx_gain); + +struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t samplerate, + uint32_t channels, uint32_t bitrate, + uint32_t stream_format, uint32_t flags, + uint32_t acdb_id); + +struct audio_client *q6audio_open_qcp(uint32_t bufsz, uint32_t min_rate, + uint32_t max_rate, uint32_t flags, + uint32_t format, uint32_t acdb_id); + +struct audio_client *q6audio_open_amrnb(uint32_t bufsz, uint32_t enc_mode, + uint32_t dtx_enable, uint32_t flags, + uint32_t acdb_id); + +int q6audio_close(struct audio_client *ac); +int q6audio_auxpcm_close(struct audio_client *ac); +int q6voice_close(struct audio_client *ac); +int q6audio_mp3_close(struct audio_client *ac); + +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab); +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab); +int q6audio_async(struct audio_client *ac); + +int q6audio_do_routing(uint32_t route, uint32_t acdb_id); +int q6audio_set_tx_mute(int mute); +int q6audio_reinit_acdb(char* filename); +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst); +int q6audio_set_rx_volume(int level); +int q6audio_set_stream_volume(struct audio_client *ac, int vol); +int q6audio_set_stream_eq_pcm(struct audio_client *ac, void *eq_config); + +struct q6audio_analog_ops { + void (*init)(void); + void (*speaker_enable)(int en); + void (*headset_enable)(int en); + void (*receiver_enable)(int en); + void (*bt_sco_enable)(int en); + void (*int_mic_enable)(int en); + void (*ext_mic_enable)(int en); +}; + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops); + +/* signal non-recoverable DSP error so we can log and/or panic */ +void q6audio_dsp_not_responding(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audiov2.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audiov2.h new file mode 100644 index 00000000000..90a6b568f31 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audiov2.h @@ -0,0 +1,87 @@ +/* arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h + * + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_MSM_QDSP6_Q6AUDIO_ +#define _MACH_MSM_QDSP6_Q6AUDIO_ + +#define AUDIO_FLAG_READ 0 +#define AUDIO_FLAG_WRITE 1 + +extern char *audio_data; +extern int32_t audio_phys; +extern uint32_t tx_clk_freq; + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t size; + uint32_t used; /* 1 = CPU is waiting for DSP to consume this buf */ + uint32_t actual_size; /* actual number of bytes read by DSP */ +}; + +struct audio_client { + struct audio_buffer buf[2]; + int cpu_buf; /* next buffer the CPU will touch */ + int dsp_buf; /* next buffer the DSP will touch */ + int running; + int session; + + wait_queue_head_t wait; + struct dal_client *client; + + int cb_status; + uint32_t flags; +}; + +/* Obtain a 16bit signed, interleaved audio channel of the specified + * rate (Hz) and channels (1 or 2), with two buffers of bufsz bytes. + */ + +struct audio_client *q6voice_open(void); +int q6voice_setup(void); +int q6voice_teardown(void); +int q6voice_close(struct audio_client *ac); + + +struct audio_client *q6audio_open(uint32_t bufsz, uint32_t flags); +int q6audio_start(struct audio_client *ac, void *rpc, uint32_t len); + +int q6audio_close(struct audio_client *ac); +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab); +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab); +int q6audio_async(struct audio_client *ac); + +int q6audio_do_routing(uint32_t route); +int q6audio_set_tx_mute(int mute); +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst); +int q6audio_set_rx_volume(int level); +int q6audio_set_route(const char *name); + +struct q6audio_analog_ops { + void (*init)(void); + void (*speaker_enable)(int en); + void (*headset_enable)(int en); + void (*receiver_enable)(int en); + void (*bt_sco_enable)(int en); + void (*int_mic_enable)(int en); + void (*ext_mic_enable)(int en); +}; + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h new file mode 100644 index 00000000000..28841a9198f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -0,0 +1,376 @@ +/** include/asm-arm/arch-msm/msm_rpcrouter.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM__ARCH_MSM_RPCROUTER_H +#define __ASM__ARCH_MSM_RPCROUTER_H + +#include +#include +#include + +/* RPC API version structure + * Version bit 31 : 1->hashkey versioning, + * 0->major-minor (backward compatible) versioning + * hashkey versioning: + * Version bits 31-0 hashkey + * major-minor (backward compatible) versioning + * Version bits 30-28 reserved (no match) + * Version bits 27-16 major (must match) + * Version bits 15-0 minor (greater or equal) + */ +#define RPC_VERSION_MODE_MASK 0x80000000 +#define RPC_VERSION_MAJOR_MASK 0x0fff0000 +#define RPC_VERSION_MINOR_MASK 0x0000ffff + +/* callback ID for NULL callback function is -1 */ +#define MSM_RPC_CLIENT_NULL_CB_ID 0xffffffff + +struct msm_rpc_endpoint; + +struct rpcsvr_platform_device +{ + struct platform_device base; + uint32_t prog; + uint32_t vers; +}; + +#define RPC_DATA_IN 0 +/* + * Structures for sending / receiving direct RPC requests + * XXX: Any cred/verif lengths > 0 not supported + */ + +struct rpc_request_hdr +{ + uint32_t xid; + uint32_t type; /* 0 */ + uint32_t rpc_vers; /* 2 */ + uint32_t prog; + uint32_t vers; + uint32_t procedure; + uint32_t cred_flavor; + uint32_t cred_length; + uint32_t verf_flavor; + uint32_t verf_length; +}; + +typedef struct +{ + uint32_t low; + uint32_t high; +} rpc_reply_progmismatch_data; + +typedef struct +{ +} rpc_denied_reply_hdr; + +typedef struct +{ + uint32_t verf_flavor; + uint32_t verf_length; + uint32_t accept_stat; +#define RPC_ACCEPTSTAT_SUCCESS 0 +#define RPC_ACCEPTSTAT_PROG_UNAVAIL 1 +#define RPC_ACCEPTSTAT_PROG_MISMATCH 2 +#define RPC_ACCEPTSTAT_PROC_UNAVAIL 3 +#define RPC_ACCEPTSTAT_GARBAGE_ARGS 4 +#define RPC_ACCEPTSTAT_SYSTEM_ERR 5 +#define RPC_ACCEPTSTAT_PROG_LOCKED 6 + /* + * Following data is dependant on accept_stat + * If ACCEPTSTAT == PROG_MISMATCH then there is a + * 'rpc_reply_progmismatch_data' structure following the header. + * Otherwise the data is procedure specific + */ +} rpc_accepted_reply_hdr; + +struct rpc_reply_hdr +{ + uint32_t xid; + uint32_t type; + uint32_t reply_stat; +#define RPCMSG_REPLYSTAT_ACCEPTED 0 +#define RPCMSG_REPLYSTAT_DENIED 1 + union { + rpc_accepted_reply_hdr acc_hdr; + rpc_denied_reply_hdr dny_hdr; + } data; +}; + +struct rpc_board_dev { + uint32_t prog; + struct platform_device pdev; +}; + +/* flags for msm_rpc_connect() */ +#define MSM_RPC_UNINTERRUPTIBLE 0x0001 + +/* use IS_ERR() to check for failure */ +struct msm_rpc_endpoint *msm_rpc_open(void); +/* Connect with the specified server version */ +struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags); +/* Connect with a compatible server version */ +struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog, + uint32_t vers, unsigned flags); +/* check if server version can handle client requested version */ +int msm_rpc_is_compatible_version(uint32_t server_version, + uint32_t client_version); + +int msm_rpc_close(struct msm_rpc_endpoint *ept); +int msm_rpc_write(struct msm_rpc_endpoint *ept, + void *data, int len); +int msm_rpc_read(struct msm_rpc_endpoint *ept, + void **data, unsigned len, long timeout); +void msm_rpc_read_wakeup(struct msm_rpc_endpoint *ept); +void msm_rpc_setup_req(struct rpc_request_hdr *hdr, + uint32_t prog, uint32_t vers, uint32_t proc); +int msm_rpc_register_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers); +int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers); + +int msm_rpc_add_board_dev(struct rpc_board_dev *board_dev, int num); + +int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept); + +int msm_rpc_get_curr_pkt_size(struct msm_rpc_endpoint *ept); +/* simple blocking rpc call + * + * request is mandatory and must have a rpc_request_hdr + * at the start. The header will be filled out for you. + * + * reply provides a buffer for replies of reply_max_size + */ +int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, + void *request, int request_size, + void *reply, int reply_max_size, + long timeout); +int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, + void *request, int request_size, + long timeout); + +struct msm_rpc_xdr { + void *in_buf; + uint32_t in_size; + uint32_t in_index; + wait_queue_head_t in_buf_wait_q; + + void *out_buf; + uint32_t out_size; + uint32_t out_index; + struct mutex out_lock; + + struct msm_rpc_endpoint *ept; +}; + +int xdr_send_int8(struct msm_rpc_xdr *xdr, const int8_t *value); +int xdr_send_uint8(struct msm_rpc_xdr *xdr, const uint8_t *value); +int xdr_send_int16(struct msm_rpc_xdr *xdr, const int16_t *value); +int xdr_send_uint16(struct msm_rpc_xdr *xdr, const uint16_t *value); +int xdr_send_int32(struct msm_rpc_xdr *xdr, const int32_t *value); +int xdr_send_uint32(struct msm_rpc_xdr *xdr, const uint32_t *value); +int xdr_send_bytes(struct msm_rpc_xdr *xdr, const void **data, uint32_t *size); + +int xdr_recv_int8(struct msm_rpc_xdr *xdr, int8_t *value); +int xdr_recv_uint8(struct msm_rpc_xdr *xdr, uint8_t *value); +int xdr_recv_int16(struct msm_rpc_xdr *xdr, int16_t *value); +int xdr_recv_uint16(struct msm_rpc_xdr *xdr, uint16_t *value); +int xdr_recv_int32(struct msm_rpc_xdr *xdr, int32_t *value); +int xdr_recv_uint32(struct msm_rpc_xdr *xdr, uint32_t *value); +int xdr_recv_bytes(struct msm_rpc_xdr *xdr, void **data, uint32_t *size); + +struct msm_rpc_server +{ + struct list_head list; + uint32_t flags; + + uint32_t prog; + uint32_t vers; + + struct mutex cb_req_lock; + + struct msm_rpc_endpoint *cb_ept; + + struct msm_rpc_xdr cb_xdr; + + uint32_t version; + + int (*rpc_call)(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len); + + int (*rpc_call2)(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr); +}; + +int msm_rpc_create_server(struct msm_rpc_server *server); +int msm_rpc_create_server2(struct msm_rpc_server *server); + +#define MSM_RPC_MSGSIZE_MAX 8192 + +struct msm_rpc_client; + +struct msm_rpc_client { + struct task_struct *read_thread; + struct task_struct *cb_thread; + + struct msm_rpc_endpoint *ept; + wait_queue_head_t reply_wait; + + uint32_t prog, ver; + + void *buf; + + struct msm_rpc_xdr xdr; + struct msm_rpc_xdr cb_xdr; + + uint32_t version; + + int (*cb_func)(struct msm_rpc_client *, void *, int); + int (*cb_func2)(struct msm_rpc_client *, struct rpc_request_hdr *req, + struct msm_rpc_xdr *); + void *cb_buf; + int cb_size; + + struct list_head cb_item_list; + struct mutex cb_item_list_lock; + + wait_queue_head_t cb_wait; + int cb_avail; + + atomic_t next_cb_id; + spinlock_t cb_list_lock; + struct list_head cb_list; + + uint32_t exit_flag; + struct completion complete; + struct completion cb_complete; + + struct mutex req_lock; + + void (*cb_restart_teardown)(struct msm_rpc_client *client); + void (*cb_restart_setup)(struct msm_rpc_client *client); + int in_reset; +}; + +struct msm_rpc_client_info { + uint32_t pid; + uint32_t cid; + uint32_t prog; + uint32_t vers; +}; + + +int msm_rpc_client_in_reset(struct msm_rpc_client *client); + +struct msm_rpc_client *msm_rpc_register_client( + const char *name, + uint32_t prog, uint32_t ver, + uint32_t create_cb_thread, + int (*cb_func)(struct msm_rpc_client *, void *, int)); + +struct msm_rpc_client *msm_rpc_register_client2( + const char *name, + uint32_t prog, uint32_t ver, + uint32_t create_cb_thread, + int (*cb_func)(struct msm_rpc_client *, struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr)); + +int msm_rpc_unregister_client(struct msm_rpc_client *client); + +int msm_rpc_client_req(struct msm_rpc_client *client, uint32_t proc, + int (*arg_func)(struct msm_rpc_client *, + void *, void *), void *arg_data, + int (*result_func)(struct msm_rpc_client *, + void *, void *), void *result_data, + long timeout); + +int msm_rpc_client_req2(struct msm_rpc_client *client, uint32_t proc, + int (*arg_func)(struct msm_rpc_client *, + struct msm_rpc_xdr *, void *), + void *arg_data, + int (*result_func)(struct msm_rpc_client *, + struct msm_rpc_xdr *, void *), + void *result_data, + long timeout); + +int msm_rpc_register_reset_callbacks( + struct msm_rpc_client *client, + void (*teardown)(struct msm_rpc_client *client), + void (*setup)(struct msm_rpc_client *client) + ); + +void *msm_rpc_start_accepted_reply(struct msm_rpc_client *client, + uint32_t xid, uint32_t accept_status); + +int msm_rpc_send_accepted_reply(struct msm_rpc_client *client, uint32_t size); + +void *msm_rpc_server_start_accepted_reply(struct msm_rpc_server *server, + uint32_t xid, uint32_t accept_status); + +int msm_rpc_server_send_accepted_reply(struct msm_rpc_server *server, + uint32_t size); + +int msm_rpc_add_cb_func(struct msm_rpc_client *client, void *cb_func); + +void *msm_rpc_get_cb_func(struct msm_rpc_client *client, uint32_t cb_id); + +void msm_rpc_remove_cb_func(struct msm_rpc_client *client, void *cb_func); + +int msm_rpc_server_cb_req(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + uint32_t cb_proc, + int (*arg_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *ret_data, long timeout); + +int msm_rpc_server_cb_req2(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + uint32_t cb_proc, + int (*arg_func)(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data), + void *ret_data, long timeout); + +void msm_rpc_server_get_requesting_client( + struct msm_rpc_client_info *clnt_info); + +int xdr_send_pointer(struct msm_rpc_xdr *xdr, void **obj, + uint32_t obj_size, void *xdr_op); + +int xdr_recv_pointer(struct msm_rpc_xdr *xdr, void **obj, + uint32_t obj_size, void *xdr_op); + +int xdr_send_array(struct msm_rpc_xdr *xdr, void **addr, uint32_t *size, + uint32_t maxsize, uint32_t elm_size, void *xdr_op); + +int xdr_recv_array(struct msm_rpc_xdr *xdr, void **addr, uint32_t *size, + uint32_t maxsize, uint32_t elm_size, void *xdr_op); + +int xdr_recv_req(struct msm_rpc_xdr *xdr, struct rpc_request_hdr *req); +int xdr_recv_reply(struct msm_rpc_xdr *xdr, struct rpc_reply_hdr *reply); +int xdr_start_request(struct msm_rpc_xdr *xdr, uint32_t prog, + uint32_t ver, uint32_t proc); +int xdr_start_accepted_reply(struct msm_rpc_xdr *xdr, uint32_t accept_status); +int xdr_send_msg(struct msm_rpc_xdr *xdr); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_rtb.h b/arch/arm/mach-msm/include/mach/msm_rtb.h new file mode 100644 index 00000000000..74ddfbdeeca --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_rtb.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_RTB_H__ +#define __MSM_RTB_H__ + +/* + * These numbers are used from the kernel command line and sysfs + * to control filtering. Remove items from here with extreme caution. + */ +enum logk_event_type { + LOGK_NONE = 0, + LOGK_READL = 1, + LOGK_WRITEL = 2, + LOGK_LOGBUF = 3, + LOGK_HOTPLUG = 4, + LOGK_CTXID = 5, + LOGK_TIMESTAMP = 6, +}; + +#define LOGTYPE_NOPC 0x80 + +struct msm_rtb_platform_data { + unsigned int size; +}; + +#if defined(CONFIG_MSM_RTB) +/* + * returns 1 if data was logged, 0 otherwise + */ +int uncached_logk_pc(enum logk_event_type log_type, void *caller, + void *data); + +/* + * returns 1 if data was logged, 0 otherwise + */ +int uncached_logk(enum logk_event_type log_type, void *data); + +#define ETB_WAYPOINT do { \ + BRANCH_TO_NEXT_ISTR; \ + nop(); \ + BRANCH_TO_NEXT_ISTR; \ + nop(); \ + } while (0) + +#define BRANCH_TO_NEXT_ISTR asm volatile("b .+4\n" : : : "memory") +/* + * both the mb and the isb are needed to ensure enough waypoints for + * etb tracing + */ +#define LOG_BARRIER do { \ + mb(); \ + isb();\ + } while (0) +#else + +static inline int uncached_logk_pc(enum logk_event_type log_type, + void *caller, + void *data) { return 0; } + +static inline int uncached_logk(enum logk_event_type log_type, + void *data) { return 0; } + +#define ETB_WAYPOINT +#define BRANCH_TO_NEXT_ISTR +/* + * Due to a GCC bug, we need to have a nop here in order to prevent an extra + * read from being generated after the write. + */ +#define LOG_BARRIER nop() +#endif +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_serial_debugger.h b/arch/arm/mach-msm/include/mach/msm_serial_debugger.h new file mode 100644 index 00000000000..f490b1be4f2 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_debugger.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_SERIAL_DEBUGGER_H +#define __ASM_ARCH_MSM_SERIAL_DEBUGGER_H + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq, int wakeup_irq); +#else +static inline void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq, int wakeup_irq) {} +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h new file mode 100644 index 00000000000..b96640d1003 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Author: Nick Pelly + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_SERIAL_HS_H +#define __ASM_ARCH_MSM_SERIAL_HS_H + +#include + +/* Optional platform device data for msm_serial_hs driver. + * Used to configure low power wakeup */ +struct msm_serial_hs_platform_data { + int wakeup_irq; /* wakeup irq */ + /* bool: inject char into rx tty on wakeup */ + unsigned char inject_rx_on_wakeup; + char rx_to_inject; + int (*gpio_config)(int); +}; + +unsigned int msm_hs_tx_empty(struct uart_port *uport); +void msm_hs_request_clock_off(struct uart_port *uport); +void msm_hs_request_clock_on(struct uart_port *uport); +void msm_hs_set_mctrl(struct uart_port *uport, + unsigned int mctrl); +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h b/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h new file mode 100644 index 00000000000..577a09789d8 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_hs_lite.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_SERIAL_HS_LITE_H +#define __ASM_ARCH_MSM_SERIAL_HS_LITE_H + +struct msm_serial_hslite_platform_data { + unsigned config_gpio; + unsigned uart_tx_gpio; + unsigned uart_rx_gpio; + int line; +}; + +#endif + diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hsl_regs.h b/arch/arm/mach-msm/include/mach/msm_serial_hsl_regs.h new file mode 100644 index 00000000000..b465b562a66 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_hsl_regs.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_MSM_SERIAL_HSL_REGS_H +#define __ASM_ARCH_MSM_SERIAL_HSL_REGS_H + +#ifdef CONFIG_MSM_HAS_DEBUG_UART_HS_V14 +#define UARTDM_MR2_OFFSET 0x4 +#define UARTDM_CSR_OFFSET 0xa0 +#define UARTDM_SR_OFFSET 0xa4 +#define UARTDM_CR_OFFSET 0xa8 +#define UARTDM_ISR_OFFSET 0xb4 +#define UARTDM_NCF_TX_OFFSET 0x40 +#define UARTDM_TF_OFFSET 0x100 +#else +#define UARTDM_MR2_OFFSET 0x4 +#define UARTDM_CSR_OFFSET 0x8 +#define UARTDM_SR_OFFSET 0x8 +#define UARTDM_CR_OFFSET 0x10 +#define UARTDM_ISR_OFFSET 0x14 +#define UARTDM_NCF_TX_OFFSET 0x40 +#define UARTDM_TF_OFFSET 0x70 +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_serial_pdata.h b/arch/arm/mach-msm/include/mach/msm_serial_pdata.h new file mode 100644 index 00000000000..4153cb2080e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_pdata.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#ifndef __ASM_ARCH_MSM_SERIAL_HS_H +#define __ASM_ARCH_MSM_SERIAL_HS_H + +#include + +/* Optional platform device data for msm_serial driver. + * Used to configure low power wakeup */ +struct msm_serial_platform_data { + int wakeup_irq; /* wakeup irq */ + /* bool: inject char into rx tty on wakeup */ + unsigned char inject_rx_on_wakeup; + char rx_to_inject; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h index 029463ec875..dc633fbbf4a 100644 --- a/arch/arm/mach-msm/include/mach/msm_smd.h +++ b/arch/arm/mach-msm/include/mach/msm_smd.h @@ -1,6 +1,7 @@ /* linux/include/asm-arm/arch-msm/msm_smd.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -17,22 +18,155 @@ #ifndef __ASM_ARCH_MSM_SMD_H #define __ASM_ARCH_MSM_SMD_H +#include +#include + typedef struct smd_channel smd_channel_t; -extern int (*msm_check_for_modem_crash)(void); - -/* warning: notify() may be called before open returns */ -int smd_open(const char *name, smd_channel_t **ch, void *priv, - void (*notify)(void *priv, unsigned event)); +#define SMD_MAX_CH_NAME_LEN 20 /* includes null char at end */ #define SMD_EVENT_DATA 1 #define SMD_EVENT_OPEN 2 #define SMD_EVENT_CLOSE 3 +#define SMD_EVENT_STATUS 4 +#define SMD_EVENT_REOPEN_READY 5 + +/* + * SMD Processor ID's. + * + * For all processors that have both SMSM and SMD clients, + * the SMSM Processor ID and the SMD Processor ID will + * be the same. In cases where a processor only supports + * SMD, the entry will only exist in this enum. + */ +enum { + SMD_APPS = SMSM_APPS, + SMD_MODEM = SMSM_MODEM, + SMD_Q6 = SMSM_Q6, + SMD_WCNSS = SMSM_WCNSS, + SMD_DSPS = SMSM_DSPS, + SMD_MODEM_Q6_FW, + SMD_RPM, + NUM_SMD_SUBSYSTEMS, +}; + +enum { + SMD_APPS_MODEM = 0, + SMD_APPS_QDSP, + SMD_MODEM_QDSP, + SMD_APPS_DSPS, + SMD_MODEM_DSPS, + SMD_QDSP_DSPS, + SMD_APPS_WCNSS, + SMD_MODEM_WCNSS, + SMD_QDSP_WCNSS, + SMD_DSPS_WCNSS, + SMD_APPS_Q6FW, + SMD_MODEM_Q6FW, + SMD_QDSP_Q6FW, + SMD_DSPS_Q6FW, + SMD_WCNSS_Q6FW, + SMD_APPS_RPM, + SMD_MODEM_RPM, + SMD_QDSP_RPM, + SMD_WCNSS_RPM, + SMD_NUM_TYPE, + SMD_LOOPBACK_TYPE = 100, + +}; + +/* + * SMD IRQ Configuration + * + * Used to initialize IRQ configurations from platform data + * + * @irq_name: irq_name to query platform data + * @irq_id: initialized to -1 in platform data, stores actual irq id on + * successful registration + * @out_base: if not null then settings used for outgoing interrupt + * initialied from platform data + */ + +struct smd_irq_config { + /* incoming interrupt config */ + const char *irq_name; + unsigned long flags; + int irq_id; + const char *device_name; + const void *dev_id; + + /* outgoing interrupt config */ + uint32_t out_bit_pos; + void __iomem *out_base; + uint32_t out_offset; +}; + +/* + * SMD subsystem configurations + * + * SMD subsystems configurations for platform data. This contains the + * M2A and A2M interrupt configurations for both SMD and SMSM per + * subsystem. + * + * @subsys_name: name of subsystem passed to PIL + * @irq_config_id: unique id for each subsystem + * @edge: maps to actual remote subsystem edge + * + */ +struct smd_subsystem_config { + unsigned irq_config_id; + const char *subsys_name; + int edge; + + struct smd_irq_config smd_int; + struct smd_irq_config smsm_int; + +}; + +/* + * Subsystem Restart Configuration + * + * @disable_smsm_reset_handshake + */ +struct smd_subsystem_restart_config { + int disable_smsm_reset_handshake; +}; + +/* + * Shared Memory Regions + * + * the array of these regions is expected to be in ascending order by phys_addr + * + * @phys_addr: physical base address of the region + * @size: size of the region in bytes + */ +struct smd_smem_regions { + void *phys_addr; + unsigned size; +}; + +struct smd_platform { + uint32_t num_ss_configs; + struct smd_subsystem_config *smd_ss_configs; + struct smd_subsystem_restart_config *smd_ssr_config; + uint32_t num_smem_areas; + struct smd_smem_regions *smd_smem_areas; +}; + +#ifdef CONFIG_MSM_SMD +/* warning: notify() may be called before open returns */ +int smd_open(const char *name, smd_channel_t **ch, void *priv, + void (*notify)(void *priv, unsigned event)); int smd_close(smd_channel_t *ch); /* passing a null pointer for data reads and discards */ int smd_read(smd_channel_t *ch, void *data, int len); +int smd_read_from_cb(smd_channel_t *ch, void *data, int len); +/* Same as smd_read() but takes a data buffer from userspace + * The function might sleep. Only safe to call from user context + */ +int smd_read_user_buffer(smd_channel_t *ch, void *data, int len); /* Write to stream channels may do a partial write and return ** the length actually written. @@ -40,7 +174,10 @@ int smd_read(smd_channel_t *ch, void *data, int len); ** it will return the requested length written or an error. */ int smd_write(smd_channel_t *ch, const void *data, int len); -int smd_write_atomic(smd_channel_t *ch, const void *data, int len); +/* Same as smd_write() but takes a data buffer from userspace + * The function might sleep. Only safe to call from user context + */ +int smd_write_user_buffer(smd_channel_t *ch, const void *data, int len); int smd_write_avail(smd_channel_t *ch); int smd_read_avail(smd_channel_t *ch); @@ -50,12 +187,6 @@ int smd_read_avail(smd_channel_t *ch); */ int smd_cur_packet_size(smd_channel_t *ch); -/* used for tty unthrottling and the like -- causes the notify() -** callback to be called from the same lock context as is used -** when it is called from channel updates -*/ -void smd_kick(smd_channel_t *ch); - #if 0 /* these are interruptable waits which will block you until the specified @@ -65,45 +196,234 @@ int smd_wait_until_readable(smd_channel_t *ch, int bytes); int smd_wait_until_writable(smd_channel_t *ch, int bytes); #endif -typedef enum { - SMD_PORT_DS = 0, - SMD_PORT_DIAG, - SMD_PORT_RPC_CALL, - SMD_PORT_RPC_REPLY, - SMD_PORT_BT, - SMD_PORT_CONTROL, - SMD_PORT_MEMCPY_SPARE1, - SMD_PORT_DATA1, - SMD_PORT_DATA2, - SMD_PORT_DATA3, - SMD_PORT_DATA4, - SMD_PORT_DATA5, - SMD_PORT_DATA6, - SMD_PORT_DATA7, - SMD_PORT_DATA8, - SMD_PORT_DATA9, - SMD_PORT_DATA10, - SMD_PORT_DATA11, - SMD_PORT_DATA12, - SMD_PORT_DATA13, - SMD_PORT_DATA14, - SMD_PORT_DATA15, - SMD_PORT_DATA16, - SMD_PORT_DATA17, - SMD_PORT_DATA18, - SMD_PORT_DATA19, - SMD_PORT_DATA20, - SMD_PORT_GPS_NMEA, - SMD_PORT_BRIDGE_1, - SMD_PORT_BRIDGE_2, - SMD_PORT_BRIDGE_3, - SMD_PORT_BRIDGE_4, - SMD_PORT_BRIDGE_5, - SMD_PORT_LOOPBACK, - SMD_PORT_CS_APPS_MODEM, - SMD_PORT_CS_APPS_DSP, - SMD_PORT_CS_MODEM_DSP, - SMD_NUM_PORTS, -} smd_port_id_type; +/* these are used to get and set the IF sigs of a channel. + * DTR and RTS can be set; DSR, CTS, CD and RI can be read. + */ +int smd_tiocmget(smd_channel_t *ch); +int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear); +int +smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear); +int smd_named_open_on_edge(const char *name, uint32_t edge, smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)); + +/* Tells the other end of the smd channel that this end wants to recieve + * interrupts when the written data is read. Read interrupts should only + * enabled when there is no space left in the buffer to write to, thus the + * interrupt acts as notification that space may be avaliable. If the + * other side does not support enabling/disabling interrupts on demand, + * then this function has no effect if called. + */ +void smd_enable_read_intr(smd_channel_t *ch); + +/* Tells the other end of the smd channel that this end does not want + * interrupts when written data is read. The interrupts should be + * disabled by default. If the other side does not support enabling/ + * disabling interrupts on demand, then this function has no effect if + * called. + */ +void smd_disable_read_intr(smd_channel_t *ch); + +/* Starts a packet transaction. The size of the packet may exceed the total + * size of the smd ring buffer. + * + * @ch: channel to write the packet to + * @len: total length of the packet + * + * Returns: + * 0 - success + * -ENODEV - invalid smd channel + * -EACCES - non-packet channel specified + * -EINVAL - invalid length + * -EBUSY - transaction already in progress + * -EAGAIN - no enough memory in ring buffer to start transaction + * -EPERM - unable to sucessfully start transaction due to write error + */ +int smd_write_start(smd_channel_t *ch, int len); + +/* Writes a segment of the packet for a packet transaction. + * + * @ch: channel to write packet to + * @data: buffer of data to write + * @len: length of data buffer + * @user_buf: (0) - buffer from kernelspace (1) - buffer from userspace + * + * Returns: + * number of bytes written + * -ENODEV - invalid smd channel + * -EINVAL - invalid length + * -ENOEXEC - transaction not started + */ +int smd_write_segment(smd_channel_t *ch, void *data, int len, int user_buf); + +/* Completes a packet transaction. Do not call from interrupt context. + * + * @ch: channel to complete transaction on + * + * Returns: + * 0 - success + * -ENODEV - invalid smd channel + * -E2BIG - some ammount of packet is not yet written + */ +int smd_write_end(smd_channel_t *ch); + +/* + * Returns a pointer to the subsystem name or NULL if no + * subsystem name is available. + * + * @type - Edge definition + */ +const char *smd_edge_to_subsystem(uint32_t type); + +/* + * Returns a pointer to the subsystem name given the + * remote processor ID. + * + * @pid Remote processor ID + * @returns Pointer to subsystem name or NULL if not found + */ +const char *smd_pid_to_subsystem(uint32_t pid); + +/* + * Checks to see if a new packet has arrived on the channel. Only to be + * called with interrupts disabled. + * + * @ch: channel to check if a packet has arrived + * + * Returns: + * 0 - packet not available + * 1 - packet available + * -EINVAL - NULL parameter or non-packet based channel provided + */ +int smd_is_pkt_avail(smd_channel_t *ch); + +/* + * SMD initialization function that registers for a SMD platform driver. + * + * returns success on successful driver registration. + */ +int __init msm_smd_init(void); + +#else + +static inline int smd_open(const char *name, smd_channel_t **ch, void *priv, + void (*notify)(void *priv, unsigned event)) +{ + return -ENODEV; +} + +static inline int smd_close(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline int smd_read(smd_channel_t *ch, void *data, int len) +{ + return -ENODEV; +} + +static inline int smd_read_from_cb(smd_channel_t *ch, void *data, int len) +{ + return -ENODEV; +} + +static inline int smd_read_user_buffer(smd_channel_t *ch, void *data, int len) +{ + return -ENODEV; +} + +static inline int smd_write(smd_channel_t *ch, const void *data, int len) +{ + return -ENODEV; +} + +static inline int +smd_write_user_buffer(smd_channel_t *ch, const void *data, int len) +{ + return -ENODEV; +} + +static inline int smd_write_avail(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline int smd_read_avail(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline int smd_cur_packet_size(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline int smd_tiocmget(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline int +smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear) +{ + return -ENODEV; +} + +static inline int +smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear) +{ + return -ENODEV; +} + +static inline int +smd_named_open_on_edge(const char *name, uint32_t edge, smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)) +{ + return -ENODEV; +} + +static inline void smd_enable_read_intr(smd_channel_t *ch) +{ +} + +static inline void smd_disable_read_intr(smd_channel_t *ch) +{ +} + +static inline int smd_write_start(smd_channel_t *ch, int len) +{ + return -ENODEV; +} + +static inline int +smd_write_segment(smd_channel_t *ch, void *data, int len, int user_buf) +{ + return -ENODEV; +} + +static inline int smd_write_end(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline const char *smd_edge_to_subsystem(uint32_t type) +{ + return NULL; +} + +static inline const char *smd_pid_to_subsystem(uint32_t pid) +{ + return NULL; +} + +static inline int smd_is_pkt_avail(smd_channel_t *ch) +{ + return -ENODEV; +} + +static inline int __init msm_smd_init(void) +{ + return 0; +} +#endif #endif diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h new file mode 100644 index 00000000000..fbb8502e673 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_smsm.h @@ -0,0 +1,259 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_SMSM_H_ +#define _ARCH_ARM_MACH_MSM_SMSM_H_ + +#include +#if defined(CONFIG_MSM_N_WAY_SMSM) +enum { + SMSM_APPS_STATE, + SMSM_MODEM_STATE, + SMSM_Q6_STATE, + SMSM_APPS_DEM, + SMSM_WCNSS_STATE = SMSM_APPS_DEM, + SMSM_MODEM_DEM, + SMSM_DSPS_STATE = SMSM_MODEM_DEM, + SMSM_Q6_DEM, + SMSM_POWER_MASTER_DEM, + SMSM_TIME_MASTER_DEM, +}; +extern uint32_t SMSM_NUM_ENTRIES; +#else +enum { + SMSM_APPS_STATE = 1, + SMSM_MODEM_STATE = 3, + SMSM_NUM_ENTRIES, +}; +#endif + +enum { + SMSM_APPS, + SMSM_MODEM, + SMSM_Q6, + SMSM_WCNSS, + SMSM_DSPS, +}; +extern uint32_t SMSM_NUM_HOSTS; + +#define SMSM_INIT 0x00000001 +#define SMSM_OSENTERED 0x00000002 +#define SMSM_SMDWAIT 0x00000004 +#define SMSM_SMDINIT 0x00000008 +#define SMSM_RPCWAIT 0x00000010 +#define SMSM_RPCINIT 0x00000020 +#define SMSM_RESET 0x00000040 +#define SMSM_RSA 0x00000080 +#define SMSM_RUN 0x00000100 +#define SMSM_PWRC 0x00000200 +#define SMSM_TIMEWAIT 0x00000400 +#define SMSM_TIMEINIT 0x00000800 +#define SMSM_PWRC_EARLY_EXIT 0x00001000 +#define SMSM_LTE_COEX_AWAKE 0x00001000 +#define SMSM_WFPI 0x00002000 +#define SMSM_SLEEP 0x00004000 +#define SMSM_SLEEPEXIT 0x00008000 +#define SMSM_OEMSBL_RELEASE 0x00010000 +#define SMSM_APPS_REBOOT 0x00020000 +#define SMSM_SYSTEM_POWER_DOWN 0x00040000 +#define SMSM_SYSTEM_REBOOT 0x00080000 +#define SMSM_SYSTEM_DOWNLOAD 0x00100000 +#define SMSM_PWRC_SUSPEND 0x00200000 +#define SMSM_APPS_SHUTDOWN 0x00400000 +#define SMSM_SMD_LOOPBACK 0x00800000 +#define SMSM_RUN_QUIET 0x01000000 +#define SMSM_MODEM_WAIT 0x02000000 +#define SMSM_MODEM_BREAK 0x04000000 +#define SMSM_MODEM_CONTINUE 0x08000000 +#define SMSM_SYSTEM_REBOOT_USR 0x20000000 +#define SMSM_SYSTEM_PWRDWN_USR 0x40000000 +#define SMSM_UNKNOWN 0x80000000 + +#define SMSM_WKUP_REASON_RPC 0x00000001 +#define SMSM_WKUP_REASON_INT 0x00000002 +#define SMSM_WKUP_REASON_GPIO 0x00000004 +#define SMSM_WKUP_REASON_TIMER 0x00000008 +#define SMSM_WKUP_REASON_ALARM 0x00000010 +#define SMSM_WKUP_REASON_RESET 0x00000020 +#define SMSM_A2_FORCE_SHUTDOWN 0x00002000 +#define SMSM_A2_RESET_BAM 0x00004000 + +#define SMSM_VENDOR 0x00020000 + +#define SMSM_A2_POWER_CONTROL 0x00000002 +#define SMSM_A2_POWER_CONTROL_ACK 0x00000800 + +#define SMSM_WLAN_TX_RINGS_EMPTY 0x00000200 +#define SMSM_WLAN_TX_ENABLE 0x00000400 + +#define SMSM_ERR_SRV_READY 0x00008000 + +#ifdef CONFIG_MSM_SMD +void *smem_alloc(unsigned id, unsigned size); +#else +void *smem_alloc(unsigned id, unsigned size) +{ + return NULL; +} +#endif +void *smem_alloc2(unsigned id, unsigned size_in); +void *smem_get_entry(unsigned id, unsigned *size); +int smsm_change_state(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask); + +/* + * Changes the global interrupt mask. The set and clear masks are re-applied + * every time the global interrupt mask is updated for callback registration + * and de-registration. + * + * The clear mask is applied first, so if a bit is set to 1 in both the clear + * mask and the set mask, the result will be that the interrupt is set. + * + * @smsm_entry SMSM entry to change + * @clear_mask 1 = clear bit, 0 = no-op + * @set_mask 1 = set bit, 0 = no-op + * + * @returns 0 for success, < 0 for error + */ +int smsm_change_intr_mask(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask); +int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask); +uint32_t smsm_get_state(uint32_t smsm_entry); +int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask, + void (*notify)(void *, uint32_t old_state, uint32_t new_state), + void *data); +int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask, + void (*notify)(void *, uint32_t, uint32_t), void *data); +int smsm_driver_state_notifier_register(struct notifier_block *nb); +int smsm_driver_state_notifier_unregister(struct notifier_block *nb); +void smsm_print_sleep_info(uint32_t sleep_delay, uint32_t sleep_limit, + uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs); +void smsm_reset_modem(unsigned mode); +void smsm_reset_modem_cont(void); +void smd_sleep_exit(void); + +#define SMEM_NUM_SMD_STREAM_CHANNELS 64 +#define SMEM_NUM_SMD_BLOCK_CHANNELS 64 + +enum { + /* fixed items */ + SMEM_PROC_COMM = 0, + SMEM_HEAP_INFO, + SMEM_ALLOCATION_TABLE, + SMEM_VERSION_INFO, + SMEM_HW_RESET_DETECT, + SMEM_AARM_WARM_BOOT, + SMEM_DIAG_ERR_MESSAGE, + SMEM_SPINLOCK_ARRAY, + SMEM_MEMORY_BARRIER_LOCATION, + SMEM_FIXED_ITEM_LAST = SMEM_MEMORY_BARRIER_LOCATION, + + /* dynamic items */ + SMEM_AARM_PARTITION_TABLE, + SMEM_AARM_BAD_BLOCK_TABLE, + SMEM_RESERVE_BAD_BLOCKS, + SMEM_WM_UUID, + SMEM_CHANNEL_ALLOC_TBL, + SMEM_SMD_BASE_ID, + SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_STREAM_CHANNELS, + SMEM_SMEM_LOG_EVENTS, + SMEM_SMEM_STATIC_LOG_IDX, + SMEM_SMEM_STATIC_LOG_EVENTS, + SMEM_SMEM_SLOW_CLOCK_SYNC, + SMEM_SMEM_SLOW_CLOCK_VALUE, + SMEM_BIO_LED_BUF, + SMEM_SMSM_SHARED_STATE, + SMEM_SMSM_INT_INFO, + SMEM_SMSM_SLEEP_DELAY, + SMEM_SMSM_LIMIT_SLEEP, + SMEM_SLEEP_POWER_COLLAPSE_DISABLED, + SMEM_KEYPAD_KEYS_PRESSED, + SMEM_KEYPAD_STATE_UPDATED, + SMEM_KEYPAD_STATE_IDX, + SMEM_GPIO_INT, + SMEM_MDDI_LCD_IDX, + SMEM_MDDI_HOST_DRIVER_STATE, + SMEM_MDDI_LCD_DISP_STATE, + SMEM_LCD_CUR_PANEL, + SMEM_MARM_BOOT_SEGMENT_INFO, + SMEM_AARM_BOOT_SEGMENT_INFO, + SMEM_SLEEP_STATIC, + SMEM_SCORPION_FREQUENCY, + SMEM_SMD_PROFILES, + SMEM_TSSC_BUSY, + SMEM_HS_SUSPEND_FILTER_INFO, + SMEM_BATT_INFO, + SMEM_APPS_BOOT_MODE, + SMEM_VERSION_FIRST, + SMEM_VERSION_SMD = SMEM_VERSION_FIRST, + SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24, + SMEM_OSS_RRCASN1_BUF1, + SMEM_OSS_RRCASN1_BUF2, + SMEM_ID_VENDOR0, + SMEM_ID_VENDOR1, + SMEM_ID_VENDOR2, + SMEM_HW_SW_BUILD_ID, + SMEM_SMD_BLOCK_PORT_BASE_ID, + SMEM_SMD_BLOCK_PORT_PROC0_HEAP = SMEM_SMD_BLOCK_PORT_BASE_ID + + SMEM_NUM_SMD_BLOCK_CHANNELS, + SMEM_SMD_BLOCK_PORT_PROC1_HEAP = SMEM_SMD_BLOCK_PORT_PROC0_HEAP + + SMEM_NUM_SMD_BLOCK_CHANNELS, + SMEM_I2C_MUTEX = SMEM_SMD_BLOCK_PORT_PROC1_HEAP + + SMEM_NUM_SMD_BLOCK_CHANNELS, + SMEM_SCLK_CONVERSION, + SMEM_SMD_SMSM_INTR_MUX, + SMEM_SMSM_CPU_INTR_MASK, + SMEM_APPS_DEM_SLAVE_DATA, + SMEM_QDSP6_DEM_SLAVE_DATA, + SMEM_CLKREGIM_BSP, + SMEM_CLKREGIM_SOURCES, + SMEM_SMD_FIFO_BASE_ID, + SMEM_USABLE_RAM_PARTITION_TABLE = SMEM_SMD_FIFO_BASE_ID + + SMEM_NUM_SMD_STREAM_CHANNELS, + SMEM_POWER_ON_STATUS_INFO, + SMEM_DAL_AREA, + SMEM_SMEM_LOG_POWER_IDX, + SMEM_SMEM_LOG_POWER_WRAP, + SMEM_SMEM_LOG_POWER_EVENTS, + SMEM_ERR_CRASH_LOG, + SMEM_ERR_F3_TRACE_LOG, + SMEM_SMD_BRIDGE_ALLOC_TABLE, + SMEM_SMDLITE_TABLE, + SMEM_SD_IMG_UPGRADE_STATUS, + SMEM_SEFS_INFO, + SMEM_RESET_LOG, + SMEM_RESET_LOG_SYMBOLS, + SMEM_MODEM_SW_BUILD_ID, + SMEM_SMEM_LOG_MPROC_WRAP, + SMEM_BOOT_INFO_FOR_APPS, + SMEM_SMSM_SIZE_INFO, + SMEM_SMD_LOOPBACK_REGISTER, + SMEM_SSR_REASON_MSS0, + SMEM_SSR_REASON_WCNSS0, + SMEM_SSR_REASON_LPASS0, + SMEM_SSR_REASON_DSPS0, + SMEM_SSR_REASON_VCODEC0, + SMEM_MEM_LAST = SMEM_SSR_REASON_VCODEC0, + SMEM_NUM_ITEMS, +}; + +enum { + SMEM_APPS_Q6_SMSM = 3, + SMEM_Q6_APPS_SMSM = 5, + SMSM_NUM_INTR_MUX = 8, +}; + +int smsm_check_for_modem_crash(void); +void *smem_find(unsigned id, unsigned size); +void *smem_get_entry(unsigned id, unsigned *size); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h new file mode 100644 index 00000000000..51081b67610 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_spi.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * SPI driver for Qualcomm MSM platforms. + */ + +struct msm_spi_platform_data { + u32 max_clock_speed; + int (*gpio_config)(void); + void (*gpio_release)(void); + int (*dma_config)(void); + const char *rsl_id; + uint32_t pm_lat; +}; diff --git a/arch/arm/mach-msm/include/mach/msm_touchpad.h b/arch/arm/mach-msm/include/mach/msm_touchpad.h new file mode 100644 index 00000000000..4b2d537abd6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_touchpad.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Touchpad driver for QSD platform. + */ + +struct msm_touchpad_platform_data { + int gpioirq; + int gpiosuspend; + int (*gpio_setup) (void); + void (*gpio_shutdown)(void); +}; diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h new file mode 100644 index 00000000000..6912f0caf9e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_tspp.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_TSPP_H_ +#define _MSM_TSPP_H_ + +struct msm_tspp_platform_data { + int num_gpios; + const struct msm_gpio *gpios; + const char *tsif_pclk; + const char *tsif_ref_clk; +}; + +#endif /* _MSM_TSPP_H_ */ + diff --git a/arch/arm/mach-msm/include/mach/msm_xo.h b/arch/arm/mach-msm/include/mach/msm_xo.h new file mode 100644 index 00000000000..f9795b440f4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_xo.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MACH_MSM_XO_H +#define __MACH_MSM_XO_H + +enum msm_xo_ids { + MSM_XO_TCXO_D0, + MSM_XO_TCXO_D1, + MSM_XO_TCXO_A0, + MSM_XO_TCXO_A1, + MSM_XO_TCXO_A2, + MSM_XO_CORE, + NUM_MSM_XO_IDS +}; + +enum msm_xo_modes { + MSM_XO_MODE_OFF, + MSM_XO_MODE_PIN_CTRL, + MSM_XO_MODE_ON, + NUM_MSM_XO_MODES +}; + +struct msm_xo_voter; + +#ifdef CONFIG_MSM_XO +struct msm_xo_voter *msm_xo_get(enum msm_xo_ids xo_id, const char *voter); +void msm_xo_put(struct msm_xo_voter *xo_voter); +int msm_xo_mode_vote(struct msm_xo_voter *xo_voter, enum msm_xo_modes xo_mode); +int __init msm_xo_init(void); +#else +static inline struct msm_xo_voter *msm_xo_get(enum msm_xo_ids xo_id, + const char *voter) +{ + return NULL; +} + +static inline void msm_xo_put(struct msm_xo_voter *xo_voter) { } + +static inline int msm_xo_mode_vote(struct msm_xo_voter *xo_voter, + enum msm_xo_modes xo_mode) +{ + return 0; +} +static inline int msm_xo_init(void) { return 0; } +#endif /* CONFIG_MSM_XO */ + +#endif diff --git a/arch/arm/mach-msm/include/mach/ocmem.h b/arch/arm/mach-msm/include/mach/ocmem.h new file mode 100644 index 00000000000..bf7c3386e47 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/ocmem.h @@ -0,0 +1,109 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_OCMEM_H +#define _ARCH_ARM_MACH_MSM_OCMEM_H + +#include +#include +#include + +#define OCMEM_MIN_ALLOC SZ_64K +#define OCMEM_MIN_ALIGN SZ_64K + +/* Maximum number of slots in DM */ +#define OCMEM_MAX_CHUNKS 32 +#define MIN_CHUNK_SIZE (SZ_1K/8) + +struct ocmem_buf { + unsigned long addr; + unsigned long len; +}; + +struct ocmem_buf_attr { + unsigned long paddr; + unsigned long len; +}; + +struct ocmem_chunk { + bool ro; + unsigned long ddr_paddr; + unsigned long size; +}; + +struct ocmem_map_list { + int num_chunks; + struct ocmem_chunk chunks[OCMEM_MAX_CHUNKS]; +}; + +/* List of clients that allocate/interact with OCMEM */ +/* Must be in sync with client_names */ +enum ocmem_client { + /* GMEM clients */ + OCMEM_GRAPHICS = 0x0, + /* TCMEM clients */ + OCMEM_VIDEO, + OCMEM_CAMERA, + /* Dummy Clients */ + OCMEM_HP_AUDIO, + OCMEM_VOICE, + /* IMEM Clients */ + OCMEM_LP_AUDIO, + OCMEM_SENSORS, + OCMEM_BLAST, + OCMEM_CLIENT_MAX, +}; + +/** + * List of OCMEM notification events which will be broadcasted + * to clients that optionally register for these notifications + * on a per allocation basis. + **/ +enum ocmem_notif_type { + OCMEM_MAP_DONE = 1, + OCMEM_MAP_FAIL, + OCMEM_UNMAP_DONE, + OCMEM_UNMAP_FAIL, + OCMEM_ALLOC_GROW, + OCMEM_ALLOC_SHRINK, + OCMEM_NOTIF_TYPE_COUNT, +}; + +/* APIS */ +/* Notification APIs */ +void *ocmem_notifier_register(int client_id, struct notifier_block *nb); + +int ocmem_notifier_unregister(void *notif_hndl, struct notifier_block *nb); + +/* Allocation APIs */ +struct ocmem_buf *ocmem_allocate(int client_id, unsigned long size); + +struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size); + +struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min, + unsigned long goal, unsigned long step); + +/* Free APIs */ +int ocmem_free(int client_id, struct ocmem_buf *buf); + +/* Dynamic Resize APIs */ +int ocmem_shrink(int client_id, struct ocmem_buf *buf, + unsigned long new_size); + +int ocmem_expand(int client_id, struct ocmem_buf *buf, + unsigned long new_size); + +/* Priority Enforcement APIs */ +int ocmem_evict(int client_id); + +int ocmem_restore(int client_id); +#endif diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h new file mode 100644 index 00000000000..dd3a318ac78 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_OCMEM_CORE_H +#define _ARCH_ARM_MACH_MSM_OCMEM_CORE_H + +/** All interfaces in this header should only be used by OCMEM driver + * Client drivers should use wrappers available in ocmem.h + **/ + +#include "ocmem.h" +#include +#include + +#define OCMEM_PHYS_BASE 0xFEC00000 +#define OCMEM_PHYS_SIZE 0x180000 + +struct ocmem_zone; + +struct ocmem_zone_ops { + unsigned long (*allocate) (struct ocmem_zone *, unsigned long); + int (*free) (struct ocmem_zone *, unsigned long, unsigned long); +}; + +struct ocmem_zone { + int owner; + int active_regions; + int max_regions; + struct list_head region_list; + unsigned long z_start; + unsigned long z_end; + unsigned long z_head; + unsigned long z_tail; + unsigned long z_free; + struct gen_pool *z_pool; + struct ocmem_zone_ops *z_ops; +}; + +struct ocmem_req { + struct rw_semaphore rw_sem; + /* Chain in sched queue */ + struct list_head sched_list; + /* Chain in zone list */ + struct list_head zone_list; + int owner; + int prio; + uint32_t req_id; + unsigned long req_min; + unsigned long req_max; + unsigned long req_step; + /* reverse pointers */ + struct ocmem_zone *zone; + struct ocmem_buf *buffer; + unsigned long state; + /* Request assignments */ + unsigned long req_start; + unsigned long req_end; + unsigned long req_sz; +}; + +struct ocmem_handle { + struct ocmem_buf buffer; + struct mutex handle_mutex; + struct ocmem_req *req; +}; + +struct ocmem_zone *get_zone(unsigned); +unsigned long allocate_head(struct ocmem_zone *, unsigned long); +int free_head(struct ocmem_zone *, unsigned long, unsigned long); +unsigned long allocate_tail(struct ocmem_zone *, unsigned long); +int free_tail(struct ocmem_zone *, unsigned long, unsigned long); + +int ocmem_notifier_init(void); +int check_notifier(int); +int dispatch_notification(int, enum ocmem_notif_type, struct ocmem_buf *); +#endif diff --git a/arch/arm/mach-msm/include/mach/oem_rapi_client.h b/arch/arm/mach-msm/include/mach/oem_rapi_client.h new file mode 100644 index 00000000000..d7a2416fff8 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/oem_rapi_client.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM__ARCH_OEM_RAPI_CLIENT_H +#define __ASM__ARCH_OEM_RAPI_CLIENT_H + +/* + * OEM RAPI CLIENT Driver header file + */ + +#include +#include + +enum { + OEM_RAPI_CLIENT_EVENT_NONE = 0, + + /* + * list of oem rapi client events + */ + + OEM_RAPI_CLIENT_EVENT_MAX + +}; + +struct oem_rapi_client_streaming_func_cb_arg { + uint32_t event; + void *handle; + uint32_t in_len; + char *input; + uint32_t out_len_valid; + uint32_t output_valid; + uint32_t output_size; +}; + +struct oem_rapi_client_streaming_func_cb_ret { + uint32_t *out_len; + char *output; +}; + +struct oem_rapi_client_streaming_func_arg { + uint32_t event; + int (*cb_func)(struct oem_rapi_client_streaming_func_cb_arg *, + struct oem_rapi_client_streaming_func_cb_ret *); + void *handle; + uint32_t in_len; + char *input; + uint32_t out_len_valid; + uint32_t output_valid; + uint32_t output_size; +}; + +struct oem_rapi_client_streaming_func_ret { + uint32_t *out_len; + char *output; +}; + +int oem_rapi_client_streaming_function( + struct msm_rpc_client *client, + struct oem_rapi_client_streaming_func_arg *arg, + struct oem_rapi_client_streaming_func_ret *ret); + +int oem_rapi_client_close(void); + +struct msm_rpc_client *oem_rapi_client_init(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/peripheral-loader.h b/arch/arm/mach-msm/include/mach/peripheral-loader.h new file mode 100644 index 00000000000..327c82f8fd5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/peripheral-loader.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MACH_PERIPHERAL_LOADER_H +#define __MACH_PERIPHERAL_LOADER_H + +#ifdef CONFIG_MSM_PIL +extern void *pil_get(const char *name); +extern void pil_put(void *peripheral_handle); +extern void pil_force_shutdown(const char *name); +extern int pil_force_boot(const char *name); +#else +static inline void *pil_get(const char *name) { return NULL; } +static inline void pil_put(void *peripheral_handle) { } +static inline void pil_force_shutdown(const char *name) { } +static inline int pil_force_boot(const char *name) { return -ENOSYS; } +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/pmic.h b/arch/arm/mach-msm/include/mach/pmic.h new file mode 100644 index 00000000000..b143a59b2af --- /dev/null +++ b/arch/arm/mach-msm/include/mach/pmic.h @@ -0,0 +1,748 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_PMIC_H +#define __ARCH_ARM_MACH_PMIC_H + +#include + +enum spkr_ldo_v_sel { + VOLT_LEVEL_1_1V, + VOLT_LEVEL_1_2V, + VOLT_LEVEL_2_0V, +}; + +enum hp_spkr_left_right { + LEFT_HP_SPKR, + RIGHT_HP_SPKR, +}; + +enum spkr_left_right { + LEFT_SPKR, + RIGHT_SPKR, +}; + +enum spkr_gain { + SPKR_GAIN_MINUS16DB, /* -16 db */ + SPKR_GAIN_MINUS12DB, /* -12 db */ + SPKR_GAIN_MINUS08DB, /* -08 db */ + SPKR_GAIN_MINUS04DB, /* -04 db */ + SPKR_GAIN_00DB, /* 00 db */ + SPKR_GAIN_PLUS04DB, /* +04 db */ + SPKR_GAIN_PLUS08DB, /* +08 db */ + SPKR_GAIN_PLUS12DB, /* +12 db */ +}; + +enum spkr_dly { + SPKR_DLY_10MS, /* ~10 ms delay */ + SPKR_DLY_100MS, /* ~100 ms delay */ +}; + +enum spkr_hpf_corner_freq { + SPKR_FREQ_1_39KHZ, /* 1.39 kHz */ + SPKR_FREQ_0_64KHZ, /* 0.64 kHz */ + SPKR_FREQ_0_86KHZ, /* 0.86 kHz */ + SPKR_FREQ_0_51KHZ, /* 0.51 kHz */ + SPKR_FREQ_1_06KHZ, /* 1.06 kHz */ + SPKR_FREQ_0_57KHZ, /* 0.57 kHz */ + SPKR_FREQ_0_73KHZ, /* 0.73 kHz */ + SPKR_FREQ_0_47KHZ, /* 0.47 kHz */ + SPKR_FREQ_1_20KHZ, /* 1.20 kHz */ + SPKR_FREQ_0_60KHZ, /* 0.60 kHz */ + SPKR_FREQ_0_76KHZ, /* 0.76 kHz */ + SPKR_FREQ_0_49KHZ, /* 0.49 kHz */ + SPKR_FREQ_0_95KHZ, /* 0.95 kHz */ + SPKR_FREQ_0_54KHZ, /* 0.54 kHz */ + SPKR_FREQ_0_68KHZ, /* 0.68 kHz */ + SPKR_FREQ_0_45KHZ, /* 0.45 kHz */ +}; + +/* Turn the speaker on or off and enables or disables mute.*/ +enum spkr_cmd { + SPKR_DISABLE, /* Enable Speaker */ + SPKR_ENABLE, /* Disable Speaker */ + SPKR_MUTE_OFF, /* turn speaker mute off, SOUND ON */ + SPKR_MUTE_ON, /* turn speaker mute on, SOUND OFF */ + SPKR_OFF, /* turn speaker OFF (speaker disable and mute on) */ + SPKR_ON, /* turn speaker ON (speaker enable and mute off) */ + SPKR_SET_FREQ_CMD, /* set speaker frequency */ + SPKR_GET_FREQ_CMD, /* get speaker frequency */ + SPKR_SET_GAIN_CMD, /* set speaker gain */ + SPKR_GET_GAIN_CMD, /* get speaker gain */ + SPKR_SET_DELAY_CMD, /* set speaker delay */ + SPKR_GET_DELAY_CMD, /* get speaker delay */ + SPKR_SET_PDM_MODE, + SPKR_SET_PWM_MODE, +}; + +struct spkr_config_mode { + uint32_t is_right_chan_en; + uint32_t is_left_chan_en; + uint32_t is_right_left_chan_added; + uint32_t is_stereo_en; + uint32_t is_usb_with_hpf_20hz; + uint32_t is_mux_bypassed; + uint32_t is_hpf_en; + uint32_t is_sink_curr_from_ref_volt_cir_en; +}; + +enum mic_volt { + MIC_VOLT_2_00V, /* 2.00 V */ + MIC_VOLT_1_93V, /* 1.93 V */ + MIC_VOLT_1_80V, /* 1.80 V */ + MIC_VOLT_1_73V, /* 1.73 V */ +}; + +enum ledtype { + LED_LCD, + LED_KEYPAD, +}; + +enum flash_led_mode { + FLASH_LED_MODE__MANUAL, + FLASH_LED_MODE__DBUS1, + FLASH_LED_MODE__DBUS2, + FLASH_LED_MODE__DBUS3, +}; + +enum flash_led_pol { + FLASH_LED_POL__ACTIVE_HIGH, + FLASH_LED_POL__ACTIVE_LOW, +}; + +enum switch_cmd { + OFF_CMD, + ON_CMD +}; + +enum vreg_lp_id { + PM_VREG_LP_MSMA_ID, + PM_VREG_LP_MSMP_ID, + PM_VREG_LP_MSME1_ID, + PM_VREG_LP_GP3_ID, + PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME2_ID, + PM_VREG_LP_GP4_ID, + PM_VREG_LP_GP1_ID, + PM_VREG_LP_RFTX_ID, + PM_VREG_LP_RFRX1_ID, + PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_WLAN_ID, + PM_VREG_LP_MMC_ID, + PM_VREG_LP_RUIM_ID, + PM_VREG_LP_MSMC0_ID, + PM_VREG_LP_GP2_ID, + PM_VREG_LP_GP5_ID, + PM_VREG_LP_GP6_ID, + PM_VREG_LP_MPLL_ID, + PM_VREG_LP_RFUBM_ID, + PM_VREG_LP_RFA_ID, + PM_VREG_LP_CDC2_ID, + PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_USIM_ID, + PM_VREG_LP_USB2P6_ID, + PM_VREG_LP_TCXO_ID, + PM_VREG_LP_USB3P3_ID, + + PM_VREG_LP_MSME_ID = PM_VREG_LP_MSME1_ID, + /* backward compatible enums only */ + PM_VREG_LP_CAM_ID = PM_VREG_LP_GP1_ID, + PM_VREG_LP_MDDI_ID = PM_VREG_LP_GP2_ID, + PM_VREG_LP_RUIM2_ID = PM_VREG_LP_GP3_ID, + PM_VREG_LP_AUX_ID = PM_VREG_LP_GP4_ID, + PM_VREG_LP_AUX2_ID = PM_VREG_LP_GP5_ID, + PM_VREG_LP_BT_ID = PM_VREG_LP_GP6_ID, + PM_VREG_LP_MSMC_LDO_ID = PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME1_LDO_ID = PM_VREG_LP_MSME1_ID, + PM_VREG_LP_MSME2_LDO_ID = PM_VREG_LP_MSME2_ID, + PM_VREG_LP_RFA1_ID = PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_RFA2_ID = PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_XO_ID = PM_VREG_LP_TCXO_ID +}; + +enum vreg_id { + PM_VREG_MSMA_ID = 0, + PM_VREG_MSMP_ID, + PM_VREG_MSME1_ID, + PM_VREG_MSMC1_ID, + PM_VREG_MSMC2_ID, + PM_VREG_GP3_ID, + PM_VREG_MSME2_ID, + PM_VREG_GP4_ID, + PM_VREG_GP1_ID, + PM_VREG_TCXO_ID, + PM_VREG_PA_ID, + PM_VREG_RFTX_ID, + PM_VREG_RFRX1_ID, + PM_VREG_RFRX2_ID, + PM_VREG_SYNT_ID, + PM_VREG_WLAN_ID, + PM_VREG_USB_ID, + PM_VREG_BOOST_ID, + PM_VREG_MMC_ID, + PM_VREG_RUIM_ID, + PM_VREG_MSMC0_ID, + PM_VREG_GP2_ID, + PM_VREG_GP5_ID, + PM_VREG_GP6_ID, + PM_VREG_RF_ID, + PM_VREG_RF_VCO_ID, + PM_VREG_MPLL_ID, + PM_VREG_S2_ID, + PM_VREG_S3_ID, + PM_VREG_RFUBM_ID, + PM_VREG_NCP_ID, + PM_VREG_RF2_ID, + PM_VREG_RFA_ID, + PM_VREG_CDC2_ID, + PM_VREG_RFTX2_ID, + PM_VREG_USIM_ID, + PM_VREG_USB2P6_ID, + PM_VREG_USB3P3_ID, + PM_VREG_EXTCDC1_ID, + PM_VREG_EXTCDC2_ID, + + /* backward compatible enums only */ + PM_VREG_MSME_ID = PM_VREG_MSME1_ID, + PM_VREG_MSME_BUCK_SMPS_ID = PM_VREG_MSME1_ID, + PM_VREG_MSME1_LDO_ID = PM_VREG_MSME1_ID, + PM_VREG_MSMC_ID = PM_VREG_MSMC1_ID, + PM_VREG_MSMC_LDO_ID = PM_VREG_MSMC1_ID, + PM_VREG_MSMC1_BUCK_SMPS_ID = PM_VREG_MSMC1_ID, + PM_VREG_MSME2_LDO_ID = PM_VREG_MSME2_ID, + PM_VREG_CAM_ID = PM_VREG_GP1_ID, + PM_VREG_MDDI_ID = PM_VREG_GP2_ID, + PM_VREG_RUIM2_ID = PM_VREG_GP3_ID, + PM_VREG_AUX_ID = PM_VREG_GP4_ID, + PM_VREG_AUX2_ID = PM_VREG_GP5_ID, + PM_VREG_BT_ID = PM_VREG_GP6_ID, + PM_VREG_RF1_ID = PM_VREG_RF_ID, + PM_VREG_S1_ID = PM_VREG_RF1_ID, + PM_VREG_5V_ID = PM_VREG_BOOST_ID, + PM_VREG_RFA1_ID = PM_VREG_RFRX2_ID, + PM_VREG_RFA2_ID = PM_VREG_RFTX2_ID, + PM_VREG_XO_ID = PM_VREG_TCXO_ID +}; + +enum vreg_pdown_id { + PM_VREG_PDOWN_MSMA_ID, + PM_VREG_PDOWN_MSMP_ID, + PM_VREG_PDOWN_MSME1_ID, + PM_VREG_PDOWN_MSMC1_ID, + PM_VREG_PDOWN_MSMC2_ID, + PM_VREG_PDOWN_GP3_ID, + PM_VREG_PDOWN_MSME2_ID, + PM_VREG_PDOWN_GP4_ID, + PM_VREG_PDOWN_GP1_ID, + PM_VREG_PDOWN_TCXO_ID, + PM_VREG_PDOWN_PA_ID, + PM_VREG_PDOWN_RFTX_ID, + PM_VREG_PDOWN_RFRX1_ID, + PM_VREG_PDOWN_RFRX2_ID, + PM_VREG_PDOWN_SYNT_ID, + PM_VREG_PDOWN_WLAN_ID, + PM_VREG_PDOWN_USB_ID, + PM_VREG_PDOWN_MMC_ID, + PM_VREG_PDOWN_RUIM_ID, + PM_VREG_PDOWN_MSMC0_ID, + PM_VREG_PDOWN_GP2_ID, + PM_VREG_PDOWN_GP5_ID, + PM_VREG_PDOWN_GP6_ID, + PM_VREG_PDOWN_RF_ID, + PM_VREG_PDOWN_RF_VCO_ID, + PM_VREG_PDOWN_MPLL_ID, + PM_VREG_PDOWN_S2_ID, + PM_VREG_PDOWN_S3_ID, + PM_VREG_PDOWN_RFUBM_ID, + /* new for HAN */ + PM_VREG_PDOWN_RF1_ID, + PM_VREG_PDOWN_RF2_ID, + PM_VREG_PDOWN_RFA_ID, + PM_VREG_PDOWN_CDC2_ID, + PM_VREG_PDOWN_RFTX2_ID, + PM_VREG_PDOWN_USIM_ID, + PM_VREG_PDOWN_USB2P6_ID, + PM_VREG_PDOWN_USB3P3_ID, + + /* backward compatible enums only */ + PM_VREG_PDOWN_CAM_ID = PM_VREG_PDOWN_GP1_ID, + PM_VREG_PDOWN_MDDI_ID = PM_VREG_PDOWN_GP2_ID, + PM_VREG_PDOWN_RUIM2_ID = PM_VREG_PDOWN_GP3_ID, + PM_VREG_PDOWN_AUX_ID = PM_VREG_PDOWN_GP4_ID, + PM_VREG_PDOWN_AUX2_ID = PM_VREG_PDOWN_GP5_ID, + PM_VREG_PDOWN_BT_ID = PM_VREG_PDOWN_GP6_ID, + PM_VREG_PDOWN_MSME_ID = PM_VREG_PDOWN_MSME1_ID, + PM_VREG_PDOWN_MSMC_ID = PM_VREG_PDOWN_MSMC1_ID, + PM_VREG_PDOWN_RFA1_ID = PM_VREG_PDOWN_RFRX2_ID, + PM_VREG_PDOWN_RFA2_ID = PM_VREG_PDOWN_RFTX2_ID, + PM_VREG_PDOWN_XO_ID = PM_VREG_PDOWN_TCXO_ID +}; + +enum mpp_which { + PM_MPP_1, + PM_MPP_2, + PM_MPP_3, + PM_MPP_4, + PM_MPP_5, + PM_MPP_6, + PM_MPP_7, + PM_MPP_8, + PM_MPP_9, + PM_MPP_10, + PM_MPP_11, + PM_MPP_12, + PM_MPP_13, + PM_MPP_14, + PM_MPP_15, + PM_MPP_16, + PM_MPP_17, + PM_MPP_18, + PM_MPP_19, + PM_MPP_20, + PM_MPP_21, + PM_MPP_22, + + PM_NUM_MPP_HAN = PM_MPP_4 + 1, + PM_NUM_MPP_KIP = PM_MPP_4 + 1, + PM_NUM_MPP_EPIC = PM_MPP_4 + 1, + PM_NUM_MPP_PM7500 = PM_MPP_22 + 1, + PM_NUM_MPP_PM6650 = PM_MPP_12 + 1, + PM_NUM_MPP_PM6658 = PM_MPP_12 + 1, + PM_NUM_MPP_PANORAMIX = PM_MPP_2 + 1, + PM_NUM_MPP_PM6640 = PM_NUM_MPP_PANORAMIX, + PM_NUM_MPP_PM6620 = PM_NUM_MPP_PANORAMIX +}; + +enum mpp_dlogic_level { + PM_MPP__DLOGIC__LVL_MSME, + PM_MPP__DLOGIC__LVL_MSMP, + PM_MPP__DLOGIC__LVL_RUIM, + PM_MPP__DLOGIC__LVL_MMC, + PM_MPP__DLOGIC__LVL_VDD, +}; + +enum mpp_dlogic_in_dbus { + PM_MPP__DLOGIC_IN__DBUS_NONE, + PM_MPP__DLOGIC_IN__DBUS1, + PM_MPP__DLOGIC_IN__DBUS2, + PM_MPP__DLOGIC_IN__DBUS3, +}; + +enum mpp_dlogic_out_ctrl { + PM_MPP__DLOGIC_OUT__CTRL_LOW, + PM_MPP__DLOGIC_OUT__CTRL_HIGH, + PM_MPP__DLOGIC_OUT__CTRL_MPP, + PM_MPP__DLOGIC_OUT__CTRL_NOT_MPP, +}; + +enum mpp_i_sink_level { + PM_MPP__I_SINK__LEVEL_5mA, + PM_MPP__I_SINK__LEVEL_10mA, + PM_MPP__I_SINK__LEVEL_15mA, + PM_MPP__I_SINK__LEVEL_20mA, + PM_MPP__I_SINK__LEVEL_25mA, + PM_MPP__I_SINK__LEVEL_30mA, + PM_MPP__I_SINK__LEVEL_35mA, + PM_MPP__I_SINK__LEVEL_40mA, +}; + +enum mpp_i_sink_switch { + PM_MPP__I_SINK__SWITCH_DIS, + PM_MPP__I_SINK__SWITCH_ENA, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_HIGH, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_LOW, +}; + +enum pm_vib_mot_mode { + PM_VIB_MOT_MODE__MANUAL, + PM_VIB_MOT_MODE__DBUS1, + PM_VIB_MOT_MODE__DBUS2, + PM_VIB_MOT_MODE__DBUS3, +}; + +enum pm_vib_mot_pol { + PM_VIB_MOT_POL__ACTIVE_HIGH, + PM_VIB_MOT_POL__ACTIVE_LOW, +}; + +struct rtc_time { + uint sec; +}; + +enum rtc_alarm { + PM_RTC_ALARM_1, +}; + +enum hsed_controller { + PM_HSED_CONTROLLER_0, + PM_HSED_CONTROLLER_1, + PM_HSED_CONTROLLER_2, +}; + +enum hsed_switch { + PM_HSED_SC_SWITCH_TYPE, + PM_HSED_OC_SWITCH_TYPE, +}; + +enum hsed_enable { + PM_HSED_ENABLE_OFF, + PM_HSED_ENABLE_TCXO, + PM_HSED_ENABLE_PWM_TCXO, + PM_HSED_ENABLE_ALWAYS, +}; + +enum hsed_hyst_pre_div { + PM_HSED_HYST_PRE_DIV_1, + PM_HSED_HYST_PRE_DIV_2, + PM_HSED_HYST_PRE_DIV_4, + PM_HSED_HYST_PRE_DIV_8, + PM_HSED_HYST_PRE_DIV_16, + PM_HSED_HYST_PRE_DIV_32, + PM_HSED_HYST_PRE_DIV_64, + PM_HSED_HYST_PRE_DIV_128, +}; + +enum hsed_hyst_time { + PM_HSED_HYST_TIME_1_CLK_CYCLES, + PM_HSED_HYST_TIME_2_CLK_CYCLES, + PM_HSED_HYST_TIME_3_CLK_CYCLES, + PM_HSED_HYST_TIME_4_CLK_CYCLES, + PM_HSED_HYST_TIME_5_CLK_CYCLES, + PM_HSED_HYST_TIME_6_CLK_CYCLES, + PM_HSED_HYST_TIME_7_CLK_CYCLES, + PM_HSED_HYST_TIME_8_CLK_CYCLES, + PM_HSED_HYST_TIME_9_CLK_CYCLES, + PM_HSED_HYST_TIME_10_CLK_CYCLES, + PM_HSED_HYST_TIME_11_CLK_CYCLES, + PM_HSED_HYST_TIME_12_CLK_CYCLES, + PM_HSED_HYST_TIME_13_CLK_CYCLES, + PM_HSED_HYST_TIME_14_CLK_CYCLES, + PM_HSED_HYST_TIME_15_CLK_CYCLES, + PM_HSED_HYST_TIME_16_CLK_CYCLES, +}; + +enum hsed_period_pre_div { + PM_HSED_PERIOD_PRE_DIV_2, + PM_HSED_PERIOD_PRE_DIV_4, + PM_HSED_PERIOD_PRE_DIV_8, + PM_HSED_PERIOD_PRE_DIV_16, + PM_HSED_PERIOD_PRE_DIV_32, + PM_HSED_PERIOD_PRE_DIV_64, + PM_HSED_PERIOD_PRE_DIV_128, + PM_HSED_PERIOD_PRE_DIV_256, +}; + +enum hsed_period_time { + PM_HSED_PERIOD_TIME_1_CLK_CYCLES, + PM_HSED_PERIOD_TIME_2_CLK_CYCLES, + PM_HSED_PERIOD_TIME_3_CLK_CYCLES, + PM_HSED_PERIOD_TIME_4_CLK_CYCLES, + PM_HSED_PERIOD_TIME_5_CLK_CYCLES, + PM_HSED_PERIOD_TIME_6_CLK_CYCLES, + PM_HSED_PERIOD_TIME_7_CLK_CYCLES, + PM_HSED_PERIOD_TIME_8_CLK_CYCLES, + PM_HSED_PERIOD_TIME_9_CLK_CYCLES, + PM_HSED_PERIOD_TIME_10_CLK_CYCLES, + PM_HSED_PERIOD_TIME_11_CLK_CYCLES, + PM_HSED_PERIOD_TIME_12_CLK_CYCLES, + PM_HSED_PERIOD_TIME_13_CLK_CYCLES, + PM_HSED_PERIOD_TIME_14_CLK_CYCLES, + PM_HSED_PERIOD_TIME_15_CLK_CYCLES, + PM_HSED_PERIOD_TIME_16_CLK_CYCLES, +}; + +enum vreg_lpm_id { + VREG_GP1_ID, + VREG_GP2_ID, + VREG_GP3_ID, + VREG_GP4_ID, + VREG_GP5_ID, + VREG_GP6_ID, + VREG_GP7_ID, + VREG_GP8_ID, + VREG_GP9_ID, + VREG_GP10_ID, + VREG_GP11_ID, + VREG_GP12_ID, + VREG_GP13_ID, + VREG_GP14_ID, + VREG_GP15_ID, + VREG_GP16_ID, + VREG_GP17_ID, + VREG_MDDI_ID, + VREG_MPLL_ID, + VREG_MSMC1_ID, + VREG_MSMC2_ID, + VREG_MSME_ID, + VREG_RF_ID, + VREG_RF1_ID, + VREG_RF2_ID, + VREG_RFA_ID, + VREG_SDCC1_ID, + VREG_TCXO_ID, + VREG_USB1P8_ID, + VREG_USB3P3_ID, + VREG_USIM_ID, + VREG_WLAN1_ID, + VREG_WLAN2_ID, + VREG_XO_OUT_D0_ID, + VREG_NCP_ID, + VREG_LVSW0_ID, + VREG_LVSW1_ID, +}; + +enum low_current_led { + LOW_CURRENT_LED_DRV0, + LOW_CURRENT_LED_DRV1, + LOW_CURRENT_LED_DRV2, +}; + +enum ext_signal { + EXT_SIGNAL_CURRENT_SINK_MANUAL_MODE, + EXT_SIGNAL_CURRENT_SINK_PWM1, + EXT_SIGNAL_CURRENT_SINK_PWM2, + EXT_SIGNAL_CURRENT_SINK_PWM3, + EXT_SIGNAL_CURRENT_SINK_DTEST1, + EXT_SIGNAL_CURRENT_SINK_DTEST2, + EXT_SIGNAL_CURRENT_SINK_DTEST3, + EXT_SIGNAL_CURRENT_SINK_DTEST4, +}; + +enum high_current_led { + HIGH_CURRENT_LED_FLASH_DRV0, + HIGH_CURRENT_LED_FLASH_DRV1, + HIGH_CURRENT_LED_KBD_DRV, +}; + +/* PMIC GPIO */ +enum pmic_gpio { + PMIC_GPIO_1, + PMIC_GPIO_2, + PMIC_GPIO_3, + PMIC_GPIO_4, + PMIC_GPIO_5, + PMIC_GPIO_6, + PMIC_GPIO_7, + PMIC_GPIO_8, + PMIC_GPIO_9, + PMIC_GPIO_10, + PMIC_GPIO_11, +}; + +enum pmic_voltage_src { + PMIC_GPIO_VIN0, + PMIC_GPIO_VIN1, + PMIC_GPIO_VIN2, + PMIC_GPIO_VIN3, + PMIC_GPIO_VIN4, + PMIC_GPIO_VIN5, + PMIC_GPIO_VIN6, + PMIC_GPIO_VIN7, +}; + +enum pmic_io_mode { + INPUT_ON, + INPUT_OUTPUT_ON, + OUTPUT_ON, + INPUT_OUTPUT_OFF, +}; + +enum pmic_current_pull_up { + PULL_UP_30uA, + PULL_UP_1_5uA, + PULL_UP_31_5uA, + PULL_UP_1_5uA_PLUS_30uA_BOOST, + PULL_DOWN_10uA, + PULL_NO_PULL, +}; + +enum pmic_op_buf_drv_strength { + BUFFER_OFF, + BUFFER_HIGH, + BUFFER_MEDIUM, + BUFFER_LOW, +}; + +enum pmic_output_buffer_config { + CONFIG_CMOS, + CONFIG_OPEN_DRAIN, +}; + +enum pmic_dtest_buf_onoff { + DTEST_DISABLE, + DTEST_ENABLE, +}; + +enum pmic_ext_pin_config { + EXT_PIN_ENABLE, + /*! Puts EXT_PIN at high Z state & disables the block */ + EXT_PIN_DISABLE, +}; + +enum pmic_source_config { + SOURCE_GND, + SOURCE_PAIRED_GPIO, + SOURCE_SPECIAL_FUNCTION1, + SOURCE_SPECIAL_FUNCTION2, + SOURCE_DTEST1, + SOURCE_DTEST2, + SOURCE_DTEST3, + SOURCE_DTEST4, +}; + +enum pmic_direction_mode { + MODE_INPUT, + MODE_OTPUT_AND_INPUT_ON, + MODE_OUTPUT, + MODE_INPUT_AND_OUTPUT_OFF, +}; + +struct pm8xxx_gpio_rpc_cfg { + enum pmic_gpio gpio; + bool config_gpio; + enum pmic_voltage_src volt_src; + bool mode_on; + enum pmic_io_mode mode; + enum pmic_output_buffer_config buf_config; + bool invert_ext_pin; + enum pmic_current_pull_up src_pull; + enum pmic_op_buf_drv_strength drv_strength; + enum pmic_dtest_buf_onoff dtest_on; + enum pmic_ext_pin_config ext_config; + enum pmic_source_config src_config; + bool int_polarity; +}; + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id); +int pmic_vreg_set_level(enum vreg_id vreg, int level); +int pmic_vreg_pull_down_switch(enum switch_cmd cmd, enum vreg_pdown_id id); +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_out_ctrl out); +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, enum mpp_i_sink_switch onoff); +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_in_dbus dbus); +int pmic_rtc_start(struct rtc_time *time); +int pmic_rtc_stop(void); +int pmic_rtc_get_time(struct rtc_time *time); +int pmic_rtc_enable_alarm(enum rtc_alarm alarm, + struct rtc_time *time); +int pmic_rtc_disable_alarm(enum rtc_alarm alarm); +int pmic_rtc_get_alarm_time(enum rtc_alarm alarm, + struct rtc_time *time); +int pmic_rtc_get_alarm_status(uint *status); +int pmic_rtc_set_time_adjust(uint adjust); +int pmic_rtc_get_time_adjust(uint *adjust); +int pmic_speaker_cmd(const enum spkr_cmd cmd); +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg); +int pmic_get_spkr_configuration(struct spkr_config_mode *cfg); +int pmic_spkr_en_right_chan(uint enable); +int pmic_spkr_is_right_chan_en(uint *enabled); +int pmic_spkr_en_left_chan(uint enable); +int pmic_spkr_is_left_chan_en(uint *enabled); +int pmic_spkr_en(enum spkr_left_right left_right, uint enabled); +int pmic_spkr_is_en(enum spkr_left_right left_right, uint *enabled); +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain); +int pmic_spkr_get_gain(enum spkr_left_right left_right, enum spkr_gain *gain); +int pmic_set_speaker_gain(enum spkr_gain gain); +int pmic_set_speaker_delay(enum spkr_dly delay); +int pmic_speaker_1k6_zin_enable(uint enable); +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq); +int pmic_spkr_get_mux_hpf_corner_freq(enum spkr_hpf_corner_freq *freq); +int pmic_spkr_select_usb_with_hpf_20hz(uint enable); +int pmic_spkr_is_usb_with_hpf_20hz(uint *enabled); +int pmic_spkr_bypass_mux(uint enable); +int pmic_spkr_is_mux_bypassed(uint *enabled); +int pmic_spkr_en_hpf(uint enable); +int pmic_spkr_is_hpf_en(uint *enabled); +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable); +int pmic_spkr_is_sink_curr_from_ref_volt_cir_en(uint *enabled); +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay); +int pmic_spkr_get_delay(enum spkr_left_right left_right, enum spkr_dly *delay); +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled); +int pmic_spkr_is_mute_en(enum spkr_left_right left_right, uint *enabled); +int pmic_mic_en(uint enable); +int pmic_mic_is_en(uint *enabled); +int pmic_mic_set_volt(enum mic_volt vol); +int pmic_mic_get_volt(enum mic_volt *voltage); +int pmic_set_led_intensity(enum ledtype type, int level); +int pmic_flash_led_set_current(uint16_t milliamps); +int pmic_flash_led_set_mode(enum flash_led_mode mode); +int pmic_flash_led_set_polarity(enum flash_led_pol pol); +int pmic_spkr_add_right_left_chan(uint enable); +int pmic_spkr_is_right_left_chan_added(uint *enabled); +int pmic_spkr_en_stereo(uint enable); +int pmic_spkr_is_stereo_en(uint *enabled); +int pmic_vib_mot_set_volt(uint vol); +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode); +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol); +int pmic_vid_en(uint enable); +int pmic_vid_is_en(uint *enabled); +int pmic_vid_load_detect_en(uint enable); + +int pmic_hsed_set_period( + enum hsed_controller controller, + enum hsed_period_pre_div period_pre_div, + enum hsed_period_time period_time +); + +int pmic_hsed_set_hysteresis( + enum hsed_controller controller, + enum hsed_hyst_pre_div hyst_pre_div, + enum hsed_hyst_time hyst_time +); + +int pmic_hsed_set_current_threshold( + enum hsed_controller controller, + enum hsed_switch switch_hsed, + uint32_t current_threshold +); + +int pmic_hsed_enable( + enum hsed_controller controller, + enum hsed_enable enable +); + +int pmic_high_current_led_set_current(enum high_current_led led, + uint16_t milliamps); +int pmic_high_current_led_set_polarity(enum high_current_led led, + enum flash_led_pol polarity); +int pmic_high_current_led_set_mode(enum high_current_led led, + enum flash_led_mode mode); +int pmic_lp_force_lpm_control(enum switch_cmd cmd, + enum vreg_lpm_id vreg); +int pmic_low_current_led_set_ext_signal(enum low_current_led led, + enum ext_signal sig); +int pmic_low_current_led_set_current(enum low_current_led led, + uint16_t milliamps); + +int pmic_spkr_set_vsel_ldo(enum spkr_left_right left_right, + enum spkr_ldo_v_sel vlt_cntrl); +int pmic_spkr_set_boost(enum spkr_left_right left_right, uint enable); +int pmic_spkr_bypass_en(enum spkr_left_right left_right, uint enable); +int pmic_hp_spkr_mstr_en(enum hp_spkr_left_right left_right, uint enable); +int pmic_hp_spkr_mute_en(enum hp_spkr_left_right left_right, uint enable); +int pmic_hp_spkr_prm_in_en(enum hp_spkr_left_right left_right, uint enable); +int pmic_hp_spkr_aux_in_en(enum hp_spkr_left_right left_right, uint enable); +int pmic_hp_spkr_ctrl_prm_gain_input(enum hp_spkr_left_right left_right, + uint prm_gain_ctl); +int pmic_hp_spkr_ctrl_aux_gain_input(enum hp_spkr_left_right left_right, + uint aux_gain_ctl); +int pmic_xo_core_force_enable(uint enable); +int pmic_gpio_direction_input(unsigned gpio); +int pmic_gpio_direction_output(unsigned gpio); +int pmic_gpio_set_value(unsigned gpio, int value); +int pmic_gpio_get_value(unsigned gpio); +int pmic_gpio_get_direction(unsigned gpio); +int pmic_gpio_config(struct pm8xxx_gpio_rpc_cfg *); +#endif diff --git a/arch/arm/mach-msm/proc_comm.h b/arch/arm/mach-msm/include/mach/proc_comm.h similarity index 61% rename from arch/arm/mach-msm/proc_comm.h rename to arch/arm/mach-msm/include/mach/proc_comm.h index 12da4cacd4a..8a0a2181802 100644 --- a/arch/arm/mach-msm/proc_comm.h +++ b/arch/arm/mach-msm/include/mach/proc_comm.h @@ -1,6 +1,6 @@ -/* arch/arm/mach-msm/proc_comm.h +/* arch/arm/mach-msm/include/mach/proc_comm.h * - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009,2011 Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -13,10 +13,8 @@ * */ -#ifndef _ARCH_ARM_MACH_MSM_PROC_COMM_H_ -#define _ARCH_ARM_MACH_MSM_PROC_COMM_H_ - -#include +#ifndef _ARCH_ARM_MACH_MSM_MSM_PROC_COMM_H_ +#define _ARCH_ARM_MACH_MSM_MSM_PROC_COMM_H_ enum { PCOM_CMD_IDLE = 0x0, @@ -137,7 +135,19 @@ enum { PCOM_CLKCTL_RPC_RAIL_DISABLE, PCOM_CLKCTL_RPC_RAIL_CONTROL, PCOM_CLKCTL_RPC_MIN_MSMC1, - PCOM_NUM_CMDS, + PCOM_CLKCTL_RPC_SRC_REQUEST, + PCOM_NPA_INIT, + PCOM_NPA_ISSUE_REQUIRED_REQUEST, + PCOM_CLKCTL_RPC_SET_EXT_CONFIG, +}; + +enum { + PCOM_OEM_FIRST_CMD = 0x10000000, + PCOM_OEM_TEST_CMD = PCOM_OEM_FIRST_CMD, + + /* add OEM PROC COMM commands here */ + + PCOM_OEM_LAST = PCOM_OEM_TEST_CMD, }; enum { @@ -157,102 +167,15 @@ enum { PCOM_CMD_FAIL_SMSM_NOT_INIT, PCOM_CMD_FAIL_PROC_COMM_BUSY, PCOM_CMD_FAIL_PROC_COMM_NOT_INIT, - }; -/* List of VREGs that support the Pull Down Resistor setting. */ -enum vreg_pdown_id { - PM_VREG_PDOWN_MSMA_ID, - PM_VREG_PDOWN_MSMP_ID, - PM_VREG_PDOWN_MSME1_ID, /* Not supported in Panoramix */ - PM_VREG_PDOWN_MSMC1_ID, /* Not supported in PM6620 */ - PM_VREG_PDOWN_MSMC2_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP3_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_MSME2_ID, /* Supported in PM7500 and Panoramix only */ - PM_VREG_PDOWN_GP4_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP1_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_TCXO_ID, - PM_VREG_PDOWN_PA_ID, - PM_VREG_PDOWN_RFTX_ID, - PM_VREG_PDOWN_RFRX1_ID, - PM_VREG_PDOWN_RFRX2_ID, - PM_VREG_PDOWN_SYNT_ID, - PM_VREG_PDOWN_WLAN_ID, - PM_VREG_PDOWN_USB_ID, - PM_VREG_PDOWN_MMC_ID, - PM_VREG_PDOWN_RUIM_ID, - PM_VREG_PDOWN_MSMC0_ID, /* Supported in PM6610 only */ - PM_VREG_PDOWN_GP2_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP5_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP6_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_RF_ID, - PM_VREG_PDOWN_RF_VCO_ID, - PM_VREG_PDOWN_MPLL_ID, - PM_VREG_PDOWN_S2_ID, - PM_VREG_PDOWN_S3_ID, - PM_VREG_PDOWN_RFUBM_ID, - - /* new for HAN */ - PM_VREG_PDOWN_RF1_ID, - PM_VREG_PDOWN_RF2_ID, - PM_VREG_PDOWN_RFA_ID, - PM_VREG_PDOWN_CDC2_ID, - PM_VREG_PDOWN_RFTX2_ID, - PM_VREG_PDOWN_USIM_ID, - PM_VREG_PDOWN_USB2P6_ID, - PM_VREG_PDOWN_USB3P3_ID, - PM_VREG_PDOWN_INVALID_ID, - - /* backward compatible enums only */ - PM_VREG_PDOWN_CAM_ID = PM_VREG_PDOWN_GP1_ID, - PM_VREG_PDOWN_MDDI_ID = PM_VREG_PDOWN_GP2_ID, - PM_VREG_PDOWN_RUIM2_ID = PM_VREG_PDOWN_GP3_ID, - PM_VREG_PDOWN_AUX_ID = PM_VREG_PDOWN_GP4_ID, - PM_VREG_PDOWN_AUX2_ID = PM_VREG_PDOWN_GP5_ID, - PM_VREG_PDOWN_BT_ID = PM_VREG_PDOWN_GP6_ID, - - PM_VREG_PDOWN_MSME_ID = PM_VREG_PDOWN_MSME1_ID, - PM_VREG_PDOWN_MSMC_ID = PM_VREG_PDOWN_MSMC1_ID, - PM_VREG_PDOWN_RFA1_ID = PM_VREG_PDOWN_RFRX2_ID, - PM_VREG_PDOWN_RFA2_ID = PM_VREG_PDOWN_RFTX2_ID, - PM_VREG_PDOWN_XO_ID = PM_VREG_PDOWN_TCXO_ID -}; - -enum { - PCOM_CLKRGM_APPS_RESET_USB_PHY = 34, - PCOM_CLKRGM_APPS_RESET_USBH = 37, -}; - -/* gpio info for PCOM_RPC_GPIO_TLMM_CONFIG_EX */ - -#define GPIO_ENABLE 0 -#define GPIO_DISABLE 1 - -#define GPIO_INPUT 0 -#define GPIO_OUTPUT 1 - -#define GPIO_NO_PULL 0 -#define GPIO_PULL_DOWN 1 -#define GPIO_KEEPER 2 -#define GPIO_PULL_UP 3 - -#define GPIO_2MA 0 -#define GPIO_4MA 1 -#define GPIO_6MA 2 -#define GPIO_8MA 3 -#define GPIO_10MA 4 -#define GPIO_12MA 5 -#define GPIO_14MA 6 -#define GPIO_16MA 7 - -#define PCOM_GPIO_CFG(gpio, func, dir, pull, drvstr) \ - ((((gpio) & 0x3FF) << 4) | \ - ((func) & 0xf) | \ - (((dir) & 0x1) << 14) | \ - (((pull) & 0x3) << 15) | \ - (((drvstr) & 0xF) << 17)) - +#ifdef CONFIG_MSM_PROC_COMM +void msm_proc_comm_reset_modem_now(void); int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2); -void __init proc_comm_boot_wait(void); +#else +static inline void msm_proc_comm_reset_modem_now(void) { } +static inline int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2) +{ return 0; } +#endif #endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h new file mode 100644 index 00000000000..575a286288a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h @@ -0,0 +1,129 @@ +#ifndef QDSP5AUDPLAYCMDI_H +#define QDSP5AUDPLAYCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P L A Y T A S K C O M M A N D S + +GENERAL DESCRIPTION + Command Interface for AUDPLAYTASK on QDSP5 + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + + audplay_cmd_dec_data_avail + Send buffer to AUDPLAY task + + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaycmdi.h#2 $ + +===========================================================================*/ + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL 0x0000 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_LEN \ + sizeof(audplay_cmd_bitstream_data_avail) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK +*/ +typedef struct { + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously available at the + * above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + +} __attribute__((packed)) audplay_cmd_bitstream_data_avail; + +#define AUDPLAY_CMD_HPCM_BUF_CFG 0x0003 +#define AUDPLAY_CMD_HPCM_BUF_CFG_LEN \ + sizeof(struct audplay_cmd_hpcm_buf_cfg) + +struct audplay_cmd_hpcm_buf_cfg { + unsigned int cmd_id; + unsigned int hostpcm_config; + unsigned int feedback_frequency; + unsigned int byte_swap; + unsigned int max_buffers; + unsigned int partition_number; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BUFFER_REFRESH 0x0004 +#define AUDPLAY_CMD_BUFFER_REFRESH_LEN \ + sizeof(struct audplay_cmd_buffer_update) + +struct audplay_cmd_buffer_refresh { + unsigned int cmd_id; + unsigned int num_buffers; + unsigned int buf_read_count; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2 0x0005 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2_LEN \ + sizeof(audplay_cmd_bitstream_data_avail_nt2) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK + * for NT2 */ +struct audplay_cmd_bitstream_data_avail_nt2 { + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously available at the + * above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + + /* bitstream write pointer */ + unsigned int dspBitstreamWritePtr; + +} __attribute__((packed)); + +#endif /* QDSP5AUDPLAYCMD_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h new file mode 100644 index 00000000000..0bf2468f480 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h @@ -0,0 +1,84 @@ +#ifndef QDSP5AUDPLAYMSG_H +#define QDSP5AUDPLAYMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P L A Y T A S K M S G + +GENERAL DESCRIPTION + Message sent by AUDPLAY task + +REFERENCES + None + + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaymsg.h#3 $ + +===========================================================================*/ +#define AUDPLAY_MSG_DEC_NEEDS_DATA 0x0001 +#define AUDPLAY_MSG_DEC_NEEDS_DATA_MSG_LEN \ + sizeof(audplay_msg_dec_needs_data) + +typedef struct{ + /* reserved*/ + unsigned int dec_id; + + /*The read pointer offset of external memory till which bitstream + has been dme’d in*/ + unsigned int adecDataReadPtrOffset; + + /* The buffer size of external memory. */ + unsigned int adecDataBufSize; + + unsigned int bitstream_free_len; + unsigned int bitstream_write_ptr; + unsigned int bitstarem_buf_start; + unsigned int bitstream_buf_len; +} __attribute__((packed)) audplay_msg_dec_needs_data; + +#define AUDPLAY_UP_STREAM_INFO 0x0003 +#define AUDPLAY_UP_STREAM_INFO_LEN \ + sizeof(struct audplay_msg_stream_info) + +struct audplay_msg_stream_info { + unsigned int decoder_id; + unsigned int channel_info; + unsigned int sample_freq; + unsigned int bitstream_info; + unsigned int bit_rate; +} __attribute__((packed)); + +#define AUDPLAY_MSG_BUFFER_UPDATE 0x0004 +#define AUDPLAY_MSG_BUFFER_UPDATE_LEN \ + sizeof(struct audplay_msg_buffer_update) + +struct audplay_msg_buffer_update { + unsigned int buffer_write_count; + unsigned int num_of_buffer; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); + +#define ADSP_MESSAGE_ID 0xFFFF +#endif /* QDSP5AUDPLAYMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h new file mode 100644 index 00000000000..86216d4c282 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h @@ -0,0 +1,1037 @@ +#ifndef QDSP5AUDPPCMDI_H +#define QDSP5AUDPPCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P O S T P R O C E S S I N G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by AUDPP Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992-2009, 2012 Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppcmdi.h#2 $ + +===========================================================================*/ + +/* + * ARM to AUDPPTASK Commands + * + * ARM uses three command queues to communicate with AUDPPTASK + * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands + * Location : MEMA + * Buffer Size : 6 words + * No of buffers in a queue : 20 for gaming audio and 5 for other images + * 2)uPAudPPCmd2Queue : Used for commands which are not much lengthier + * Location : MEMA + * Buffer Size : 23 + * No of buffers in a queue : 2 + * 3)uPAudOOCmd3Queue : Used for lengthier and more frequent commands + * Location : MEMA + * Buffer Size : 145 + * No of buffers in a queue : 3 + */ + +/* + * Commands Related to uPAudPPCmd1Queue + */ + +/* + * Command Structure to enable or disable the active decoders + */ + +#define AUDPP_CMD_CFG_DEC_TYPE 0x0001 +#define AUDPP_CMD_CFG_DEC_TYPE_LEN sizeof(audpp_cmd_cfg_dec_type) + +/* Enable the decoder */ +#define AUDPP_CMD_DEC_TYPE_M 0x000F + +#define AUDPP_CMD_ENA_DEC_V 0x4000 +#define AUDPP_CMD_DIS_DEC_V 0x0000 +#define AUDPP_CMD_DEC_STATE_M 0x4000 + +#define AUDPP_CMD_UPDATDE_CFG_DEC 0x8000 +#define AUDPP_CMD_DONT_UPDATE_CFG_DEC 0x0000 + + +/* Type specification of cmd_cfg_dec */ + +typedef struct { + unsigned short cmd_id; + unsigned short dec0_cfg; + unsigned short dec1_cfg; + unsigned short dec2_cfg; + unsigned short dec3_cfg; + unsigned short dec4_cfg; +} __attribute__((packed)) audpp_cmd_cfg_dec_type; + +/* + * Command Structure to Pause , Resume and flushes the selected audio decoders + */ + +#define AUDPP_CMD_DEC_CTRL 0x0002 +#define AUDPP_CMD_DEC_CTRL_LEN sizeof(audpp_cmd_dec_ctrl) + +/* Decoder control commands for pause, resume and flush */ +#define AUDPP_CMD_FLUSH_V 0x2000 + +#define AUDPP_CMD_PAUSE_V 0x4000 +#define AUDPP_CMD_RESUME_V 0x0000 + +#define AUDPP_CMD_UPDATE_V 0x8000 +#define AUDPP_CMD_IGNORE_V 0x0000 + + +/* Type Spec for decoder control command*/ + +typedef struct { + unsigned short cmd_id; + unsigned short dec0_ctrl; + unsigned short dec1_ctrl; + unsigned short dec2_ctrl; + unsigned short dec3_ctrl; + unsigned short dec4_ctrl; +} __attribute__((packed)) audpp_cmd_dec_ctrl; + +/* + * Command Structure to Configure the AVSync FeedBack Mechanism + */ + +#define AUDPP_CMD_AVSYNC 0x0003 +#define AUDPP_CMD_AVSYNC_LEN sizeof(audpp_cmd_avsync) + +typedef struct { + unsigned short cmd_id; + unsigned short object_number; + unsigned short interrupt_interval_lsw; + unsigned short interrupt_interval_msw; +} __attribute__((packed)) audpp_cmd_avsync; + +/* + * Command Structure to enable or disable(sleep) the AUDPPTASK + */ + +#define AUDPP_CMD_CFG 0x0004 +#define AUDPP_CMD_CFG_LEN sizeof(audpp_cmd_cfg) + +#define AUDPP_CMD_CFG_SLEEP 0x0000 +#define AUDPP_CMD_CFG_ENABLE 0xFFFF + +typedef struct { + unsigned short cmd_id; + unsigned short cfg; +} __attribute__((packed)) audpp_cmd_cfg; + +/* + * Command Structure to Inject or drop the specified no of samples + */ + +#define AUDPP_CMD_ADJUST_SAMP 0x0005 +#define AUDPP_CMD_ADJUST_SAMP_LEN sizeof(audpp_cmd_adjust_samp) + +#define AUDPP_CMD_SAMP_DROP -1 +#define AUDPP_CMD_SAMP_INSERT 0x0001 + +#define AUDPP_CMD_NUM_SAMPLES 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short object_no; + signed short sample_insert_or_drop; + unsigned short num_samples; +} __attribute__((packed)) audpp_cmd_adjust_samp; + +/* + * Command Structure to Configure AVSync Feedback Mechanism + */ + +#define AUDPP_CMD_AVSYNC_CMD_2 0x0006 +#define AUDPP_CMD_AVSYNC_CMD_2_LEN sizeof(audpp_cmd_avsync_cmd_2) + +typedef struct { + unsigned short cmd_id; + unsigned short object_number; + unsigned short interrupt_interval_lsw; + unsigned short interrupt_interval_msw; + unsigned short sample_counter_dlsw; + unsigned short sample_counter_dmsw; + unsigned short sample_counter_msw; + unsigned short byte_counter_dlsw; + unsigned short byte_counter_dmsw; + unsigned short byte_counter_msw; +} __attribute__((packed)) audpp_cmd_avsync_cmd_2; + +/* + * Command Structure to Configure AVSync Feedback Mechanism + */ + +#define AUDPP_CMD_AVSYNC_CMD_3 0x0007 +#define AUDPP_CMD_AVSYNC_CMD_3_LEN sizeof(audpp_cmd_avsync_cmd_3) + +typedef struct { + unsigned short cmd_id; + unsigned short object_number; + unsigned short interrupt_interval_lsw; + unsigned short interrupt_interval_msw; + unsigned short sample_counter_dlsw; + unsigned short sample_counter_dmsw; + unsigned short sample_counter_msw; + unsigned short byte_counter_dlsw; + unsigned short byte_counter_dmsw; + unsigned short byte_counter_msw; +} __attribute__((packed)) audpp_cmd_avsync_cmd_3; + +#define AUDPP_CMD_ROUTING_MODE 0x0008 +#define AUDPP_CMD_ROUTING_MODE_LEN \ +sizeof(struct audpp_cmd_routing_mode) + +struct audpp_cmd_routing_mode { + unsigned short cmd_id; + unsigned short object_number; + unsigned short routing_mode; +} __attribute__((packed)); + +/* + * Commands Related to uPAudPPCmd2Queue + */ + +/* + * Command Structure to configure Per decoder Parameters (Common) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS 0x0000 +#define AUDPP_CMD_CFG_ADEC_PARAMS_COMMON_LEN \ + sizeof(audpp_cmd_cfg_adec_params_common) + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_FCM 0x4000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_FCM 0x0000 + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_DCM 0x8000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_DCM 0x0000 + +/* Sampling frequency*/ +#define AUDPP_CMD_SAMP_RATE_96000 0x0000 +#define AUDPP_CMD_SAMP_RATE_88200 0x0001 +#define AUDPP_CMD_SAMP_RATE_64000 0x0002 +#define AUDPP_CMD_SAMP_RATE_48000 0x0003 +#define AUDPP_CMD_SAMP_RATE_44100 0x0004 +#define AUDPP_CMD_SAMP_RATE_32000 0x0005 +#define AUDPP_CMD_SAMP_RATE_24000 0x0006 +#define AUDPP_CMD_SAMP_RATE_22050 0x0007 +#define AUDPP_CMD_SAMP_RATE_16000 0x0008 +#define AUDPP_CMD_SAMP_RATE_12000 0x0009 +#define AUDPP_CMD_SAMP_RATE_11025 0x000A +#define AUDPP_CMD_SAMP_RATE_8000 0x000B + + +/* + * Type specification of cmd_adec_cfg sent to all decoder + */ + +typedef struct { + unsigned short cmd_id; + unsigned short length; + unsigned short dec_id; + unsigned short status_msg_flag; + unsigned short decoder_frame_counter_msg_period; + unsigned short input_sampling_frequency; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_common; + +/* + * Command Structure to configure Per decoder Parameters (Wav) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN \ + sizeof(audpp_cmd_cfg_adec_params_wav) + + +#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002 + +#define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000 +#define AUDPP_CMD_WAV_PCM_WIDTH_16 0x0001 +#define AUDPP_CMD_WAV_PCM_WIDTH_24 0x0002 + +typedef struct { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short pcm_width; + unsigned short sign; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_wav; + +/* + * Command Structure to configure Per decoder Parameters (ADPCM) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN \ + sizeof(audpp_cmd_cfg_adec_params_adpcm) + + +#define AUDPP_CMD_ADPCM_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_ADPCM_STEREO_CFG_STEREO 0x0002 + +typedef struct { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short block_size; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_adpcm; + +/* + * Command Structure to configure Per decoder Parameters (WMA) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wma) + +struct audpp_cmd_cfg_adec_params_wma { + audpp_cmd_cfg_adec_params_common common; + unsigned short armdatareqthr; + unsigned short channelsdecoded; + unsigned short wmabytespersec; + unsigned short wmasamplingfreq; + unsigned short wmaencoderopts; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (WMAPRO) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wmapro) + +struct audpp_cmd_cfg_adec_params_wmapro { + audpp_cmd_cfg_adec_params_common common; + unsigned short armdatareqthr; + uint8_t validbitspersample; + uint8_t numchannels; + unsigned short formattag; + unsigned short samplingrate; + unsigned short avgbytespersecond; + unsigned short asfpacketlength; + unsigned short channelmask; + unsigned short encodeopt; + unsigned short advancedencodeopt; + uint32_t advancedencodeopt2; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (MP3) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN \ + sizeof(audpp_cmd_cfg_adec_params_mp3) + +typedef struct { + audpp_cmd_cfg_adec_params_common common; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_mp3; + + +/* + * Command Structure to configure Per decoder Parameters (AAC) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN \ + sizeof(audpp_cmd_cfg_adec_params_aac) + + +#define AUDPP_CMD_AAC_FORMAT_ADTS -1 +#define AUDPP_CMD_AAC_FORMAT_RAW 0x0000 +#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDPP_CMD_AAC_FORMAT_LOAS 0x0002 + +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_ERLC 0x0011 + +#define AUDPP_CMD_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +typedef struct { + audpp_cmd_cfg_adec_params_common common; + signed short format; + unsigned short audio_object; + unsigned short ep_config; + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short channel_configuration; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_aac; + +/* + * Command Structure to configure Per decoder Parameters (V13K) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_v13k) + + +#define AUDPP_CMD_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_STEREO_CFG_STEREO 0x0002 + +struct audpp_cmd_cfg_adec_params_v13k { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)); + +#define AUDPP_CMD_CFG_ADEC_PARAMS_EVRC_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_evrc) + +struct audpp_cmd_cfg_adec_params_evrc { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__ ((packed)); + +/* + * Command Structure to configure Per decoder Parameters (AMRWB) + */ + +struct audpp_cmd_cfg_adec_params_amrwb { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)) ; + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_amrwb) + +/* + * Command Structure to configure the HOST PCM interface + */ + +#define AUDPP_CMD_PCM_INTF 0x0001 +#define AUDPP_CMD_PCM_INTF_2 0x0002 +#define AUDPP_CMD_PCM_INTF_LEN sizeof(audpp_cmd_pcm_intf) + +#define AUDPP_CMD_PCM_INTF_MONO_V 0x0001 +#define AUDPP_CMD_PCM_INTF_STEREO_V 0x0002 + +/* These two values differentiate the two types of commands that could be issued + * Interface configuration command and Buffer update command */ + +#define AUDPP_CMD_PCM_INTF_CONFIG_CMD_V 0x0000 +#define AUDPP_CMD_PCM_INTF_BUFFER_CMD_V -1 + +#define AUDPP_CMD_PCM_INTF_RX_ENA_M 0x000F +#define AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V 0x0008 +#define AUDPP_CMD_PCM_INTF_RX_ENA_DSPTOARM_V 0x0004 + +/* These flags control the enabling and disabling of the interface together + * with host interface bit mask. */ + +#define AUDPP_CMD_PCM_INTF_ENA_V -1 +#define AUDPP_CMD_PCM_INTF_DIS_V 0x0000 + + +#define AUDPP_CMD_PCM_INTF_FULL_DUPLEX 0x0 +#define AUDPP_CMD_PCM_INTF_HALF_DUPLEX_TODSP 0x1 + + +#define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5 +#define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6 + + +typedef struct { + unsigned short cmd_id; + unsigned short object_num; + signed short config; + unsigned short intf_type; + + /* DSP -> ARM Configuration */ + unsigned short read_buf1LSW; + unsigned short read_buf1MSW; + unsigned short read_buf1_len; + + unsigned short read_buf2LSW; + unsigned short read_buf2MSW; + unsigned short read_buf2_len; + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short dsp_to_arm_flag; + unsigned short partition_number; + + /* ARM -> DSP Configuration */ + unsigned short write_buf1LSW; + unsigned short write_buf1MSW; + unsigned short write_buf1_len; + + unsigned short write_buf2LSW; + unsigned short write_buf2MSW; + unsigned short write_buf2_len; + + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short arm_to_rx_flag; + unsigned short weight_decoder_to_rx; + unsigned short weight_arm_to_rx; + + unsigned short partition_number_arm_to_dsp; + unsigned short sample_rate; + unsigned short channel_mode; +} __attribute__((packed)) audpp_cmd_pcm_intf; + +/* + ** BUFFER UPDATE COMMAND + */ +#define AUDPP_CMD_PCM_INTF_SEND_BUF_PARAMS_LEN \ + sizeof(audpp_cmd_pcm_intf_send_buffer) + +typedef struct { + unsigned short cmd_id; + unsigned short host_pcm_object; + /* set config = 0xFFFF for configuration*/ + signed short config; + unsigned short intf_type; + unsigned short dsp_to_arm_buf_id; + unsigned short arm_to_dsp_buf_id; + unsigned short arm_to_dsp_buf_len; +} __attribute__((packed)) audpp_cmd_pcm_intf_send_buffer; + + +/* + * Commands Related to uPAudPPCmd3Queue + */ + +/* + * Command Structure to configure post processing params (Commmon) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS 0x0000 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \ + sizeof(audpp_cmd_cfg_object_params_common) + +#define AUDPP_CMD_OBJ0_UPDATE 0x8000 +#define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ1_UPDATE 0x8000 +#define AUDPP_CMD_OBJ1_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ2_UPDATE 0x8000 +#define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ3_UPDATE 0x8000 +#define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ4_UPDATE 0x8000 +#define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_HPCM_UPDATE 0x8000 +#define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_COMMON_CFG_UPDATE 0x8000 +#define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000 + +typedef struct { + unsigned short cmd_id; + unsigned short obj0_cfg; + unsigned short obj1_cfg; + unsigned short obj2_cfg; + unsigned short obj3_cfg; + unsigned short obj4_cfg; + unsigned short host_pcm_obj_cfg; + unsigned short comman_cfg; + unsigned short command_type; +} __attribute__((packed)) audpp_cmd_cfg_object_params_common; + +/* + * Command Structure to configure post processing params (Volume) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_VOLUME_LEN \ + sizeof(audpp_cmd_cfg_object_params_volume) + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short volume; + unsigned short pan; +} __attribute__((packed)) audpp_cmd_cfg_object_params_volume; + +/* + * Command Structure to configure post processing params (PCM Filter) --DOUBT + */ + +typedef struct { + unsigned short numerator_b0_filter_lsw; + unsigned short numerator_b0_filter_msw; + unsigned short numerator_b1_filter_lsw; + unsigned short numerator_b1_filter_msw; + unsigned short numerator_b2_filter_lsw; + unsigned short numerator_b2_filter_msw; +} __attribute__((packed)) numerator; + +typedef struct { + unsigned short denominator_a0_filter_lsw; + unsigned short denominator_a0_filter_msw; + unsigned short denominator_a1_filter_lsw; + unsigned short denominator_a1_filter_msw; +} __attribute__((packed)) denominator; + +typedef struct { + unsigned short shift_factor_0; +} __attribute__((packed)) shift_factor; + +typedef struct { + unsigned short pan_filter_0; +} __attribute__((packed)) pan; + +typedef struct { + numerator numerator_filter; + denominator denominator_filter; + shift_factor shift_factor_filter; + pan pan_filter; +} __attribute__((packed)) filter_1; + +typedef struct { + numerator numerator_filter[2]; + denominator denominator_filter[2]; + shift_factor shift_factor_filter[2]; + pan pan_filter[2]; +} __attribute__((packed)) filter_2; + +typedef struct { + numerator numerator_filter[3]; + denominator denominator_filter[3]; + shift_factor shift_factor_filter[3]; + pan pan_filter[3]; +} __attribute__((packed)) filter_3; + +typedef struct { + numerator numerator_filter[4]; + denominator denominator_filter[4]; + shift_factor shift_factor_filter[4]; + pan pan_filter[4]; +} __attribute__((packed)) filter_4; + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_PCM_LEN \ + sizeof(audpp_cmd_cfg_object_params_pcm) + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short active_flag; + unsigned short num_bands; + union { + filter_1 filter_1_params; + filter_2 filter_2_params; + filter_3 filter_3_params; + filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)) audpp_cmd_cfg_object_params_pcm; + + +/* + * Command Structure to configure post processing parameters (equalizer) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \ + sizeof(audpp_cmd_cfg_object_params_eqalizer) + +typedef struct { + unsigned short numerator_coeff_0_lsw; + unsigned short numerator_coeff_0_msw; + unsigned short numerator_coeff_1_lsw; + unsigned short numerator_coeff_1_msw; + unsigned short numerator_coeff_2_lsw; + unsigned short numerator_coeff_2_msw; +} __attribute__((packed)) eq_numerator; + +typedef struct { + unsigned short denominator_coeff_0_lsw; + unsigned short denominator_coeff_0_msw; + unsigned short denominator_coeff_1_lsw; + unsigned short denominator_coeff_1_msw; +} __attribute__((packed)) eq_denominator; + +typedef struct { + unsigned short shift_factor; +} __attribute__((packed)) eq_shiftfactor; + +typedef struct { + eq_numerator numerator; + eq_denominator denominator; + eq_shiftfactor shiftfactor; +} __attribute__((packed)) eq_coeff_1; + +typedef struct { + eq_numerator numerator[2]; + eq_denominator denominator[2]; + eq_shiftfactor shiftfactor[2]; +} __attribute__((packed)) eq_coeff_2; + +typedef struct { + eq_numerator numerator[3]; + eq_denominator denominator[3]; + eq_shiftfactor shiftfactor[3]; +} __attribute__((packed)) eq_coeff_3; + +typedef struct { + eq_numerator numerator[4]; + eq_denominator denominator[4]; + eq_shiftfactor shiftfactor[4]; +} __attribute__((packed)) eq_coeff_4; + +typedef struct { + eq_numerator numerator[5]; + eq_denominator denominator[5]; + eq_shiftfactor shiftfactor[5]; +} __attribute__((packed)) eq_coeff_5; + +typedef struct { + eq_numerator numerator[6]; + eq_denominator denominator[6]; + eq_shiftfactor shiftfactor[6]; +} __attribute__((packed)) eq_coeff_6; + +typedef struct { + eq_numerator numerator[7]; + eq_denominator denominator[7]; + eq_shiftfactor shiftfactor[7]; +} __attribute__((packed)) eq_coeff_7; + +typedef struct { + eq_numerator numerator[8]; + eq_denominator denominator[8]; + eq_shiftfactor shiftfactor[8]; +} __attribute__((packed)) eq_coeff_8; + +typedef struct { + eq_numerator numerator[9]; + eq_denominator denominator[9]; + eq_shiftfactor shiftfactor[9]; +} __attribute__((packed)) eq_coeff_9; + +typedef struct { + eq_numerator numerator[10]; + eq_denominator denominator[10]; + eq_shiftfactor shiftfactor[10]; +} __attribute__((packed)) eq_coeff_10; + +typedef struct { + eq_numerator numerator[11]; + eq_denominator denominator[11]; + eq_shiftfactor shiftfactor[11]; +} __attribute__((packed)) eq_coeff_11; + +typedef struct { + eq_numerator numerator[12]; + eq_denominator denominator[12]; + eq_shiftfactor shiftfactor[12]; +} __attribute__((packed)) eq_coeff_12; + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short eq_flag; + unsigned short num_bands; + union { + eq_coeff_1 eq_coeffs_1; + eq_coeff_2 eq_coeffs_2; + eq_coeff_3 eq_coeffs_3; + eq_coeff_4 eq_coeffs_4; + eq_coeff_5 eq_coeffs_5; + eq_coeff_6 eq_coeffs_6; + eq_coeff_7 eq_coeffs_7; + eq_coeff_8 eq_coeffs_8; + eq_coeff_9 eq_coeffs_9; + eq_coeff_10 eq_coeffs_10; + eq_coeff_11 eq_coeffs_11; + eq_coeff_12 eq_coeffs_12; + } __attribute__((packed)) eq_coeff; +} __attribute__((packed)) audpp_cmd_cfg_object_params_eqalizer; + + +/* + * Command Structure to configure post processing parameters (ADRC) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \ + sizeof(audpp_cmd_cfg_object_params_adrc) + + +#define AUDPP_CMD_ADRC_FLAG_DIS 0x0000 +#define AUDPP_CMD_ADRC_FLAG_ENA -1 + +#define AUDPP_MAX_MBADRC_BANDS 5 +#define AUDPP_MBADRC_EXTERNAL_BUF_SIZE 196 + +struct adrc_config { + uint16_t subband_enable; + uint16_t adrc_sub_mute; + uint16_t rms_time; + uint16_t compression_th; + uint16_t compression_slope; + uint16_t attack_const_lsw; + uint16_t attack_const_msw; + uint16_t release_const_lsw; + uint16_t release_const_msw; + uint16_t makeup_gain; +}; + +typedef struct { + audpp_cmd_cfg_object_params_common common; + uint16_t enable; + uint16_t num_bands; + uint16_t down_samp_level; + uint16_t adrc_delay; + uint16_t ext_buf_size; + uint16_t ext_partition; + uint16_t ext_buf_msw; + uint16_t ext_buf_lsw; + struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS]; +} __attribute__((packed)) audpp_cmd_cfg_object_params_mbadrc; + +struct audpp_cmd_cfg_object_params_adrc { + unsigned short adrc_flag; + unsigned short compression_th; + unsigned short compression_slope; + unsigned short rms_time; + unsigned short attack_const_lsw; + unsigned short attack_const_msw; + unsigned short release_const_lsw; + unsigned short release_const_msw; + unsigned short adrc_delay; +}; + +/* + * Command Structure to configure post processing parameters(Spectrum Analizer) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SPECTRAM_LEN \ + sizeof(audpp_cmd_cfg_object_params_spectram) + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short sample_interval; + unsigned short num_coeff; +} __attribute__((packed)) audpp_cmd_cfg_object_params_spectram; + +/* + * Command Structure to configure post processing parameters (QConcert) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \ + sizeof(audpp_cmd_cfg_object_params_qconcert) + + +#define AUDPP_CMD_QCON_ENA_FLAG_ENA -1 +#define AUDPP_CMD_QCON_ENA_FLAG_DIS 0x0000 + +#define AUDPP_CMD_QCON_OP_MODE_HEADPHONE -1 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_FRONT 0x0000 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_SIDE 0x0001 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_DESKTOP 0x0002 + +#define AUDPP_CMD_QCON_GAIN_UNIT 0x7FFF +#define AUDPP_CMD_QCON_GAIN_SIX_DB 0x4027 + + +#define AUDPP_CMD_QCON_EXPANSION_MAX 0x7FFF + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short enable_flag; + signed short op_mode; + signed short gain; + signed short expansion; + signed short delay; + unsigned short stages_per_mode; + unsigned short reverb_enable; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_time_ratio_msw; + unsigned short decay_time_ratio_lsw; + unsigned short reflection_delay_time; + unsigned short late_reverb_gain; + unsigned short late_reverb_delay; + unsigned short delay_buff_size_msw; + unsigned short delay_buff_size_lsw; + unsigned short partition_num; + unsigned short delay_buff_start_msw; + unsigned short delay_buff_start_lsw; +} __attribute__((packed)) audpp_cmd_cfg_object_params_qconcert; + +/* + * Command Structure to configure post processing parameters (Side Chain) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \ + sizeof(audpp_cmd_cfg_object_params_sidechain) + + +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_DIS 0x0000 +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_ENA -1 + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short active_flag; + unsigned short num_bands; + union { + filter_1 filter_1_params; + filter_2 filter_2_params; + filter_3 filter_3_params; + filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)) audpp_cmd_cfg_object_params_sidechain; + + +/* + * Command Structure to configure post processing parameters (QAFX) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QAFX_LEN \ + sizeof(audpp_cmd_cfg_object_params_qafx) + +#define AUDPP_CMD_QAFX_ENA_DISA 0x0000 +#define AUDPP_CMD_QAFX_ENA_ENA_CFG -1 +#define AUDPP_CMD_QAFX_ENA_DIS_CFG 0x0001 + +#define AUDPP_CMD_QAFX_CMD_TYPE_ENV 0x0100 +#define AUDPP_CMD_QAFX_CMD_TYPE_OBJ 0x0010 +#define AUDPP_CMD_QAFX_CMD_TYPE_QUERY 0x1000 + +#define AUDPP_CMD_QAFX_CMDS_ENV_OP_MODE 0x0100 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_POS 0x0101 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_ORI 0x0102 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_VEL 0X0103 +#define AUDPP_CMD_QAFX_CMDS_ENV_ENV_RES 0x0107 + +#define AUDPP_CMD_QAFX_CMDS_OBJ_SAMP_FREQ 0x0010 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VOL 0x0011 +#define AUDPP_CMD_QAFX_CMDS_OBJ_DIST 0x0012 +#define AUDPP_CMD_QAFX_CMDS_OBJ_POS 0x0013 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VEL 0x0014 + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short enable; + unsigned short command_type; + unsigned short num_commands; + unsigned short commands; +} __attribute__((packed)) audpp_cmd_cfg_object_params_qafx; + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (Common) + */ + +#define AUDPP_CMD_REVERB_CONFIG 0x0001 +#define AUDPP_CMD_REVERB_CONFIG_COMMON_LEN \ + sizeof(audpp_cmd_reverb_config_common) + +#define AUDPP_CMD_ENA_ENA 0xFFFF +#define AUDPP_CMD_ENA_DIS 0x0000 +#define AUDPP_CMD_ENA_CFG 0x0001 + +#define AUDPP_CMD_CMD_TYPE_ENV 0x0104 +#define AUDPP_CMD_CMD_TYPE_OBJ 0x0015 +#define AUDPP_CMD_CMD_TYPE_QUERY 0x1000 + +#define SRS_PARAMS_MAX_G 8 +#define SRS_PARAMS_MAX_W 55 +#define SRS_PARAMS_MAX_C 51 +#define SRS_PARAMS_MAX_H 53 +#define SRS_PARAMS_MAX_P 116 +#define SRS_PARAMS_MAX_L 8 + +typedef struct { + unsigned short cmd_id; + unsigned short enable; + unsigned short cmd_type; +} __attribute__((packed)) audpp_cmd_reverb_config_common; + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0104) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_104_LEN \ + sizeof(audpp_cmd_reverb_config_env_104) + +typedef struct { + audpp_cmd_reverb_config_common common; + unsigned short env_gain; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_timeratio_msw; + unsigned short decay_timeratio_lsw; + unsigned short delay_time; + unsigned short reverb_gain; + unsigned short reverb_delay; +} __attribute__((packed)) audpp_cmd_reverb_config_env_104; + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0015) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_15_LEN \ + sizeof(audpp_cmd_reverb_config_env_15) + +typedef struct { + audpp_cmd_reverb_config_common common; + unsigned short object_num; + unsigned short absolute_gain; +} __attribute__((packed)) audpp_cmd_reverb_config_env_15; + +/* + * Command Structure to configure post processing params (SRS TruMedia) + */ +struct audpp_cmd_cfg_object_params_srstm_g { + audpp_cmd_cfg_object_params_common common; + unsigned short v[SRS_PARAMS_MAX_G]; +} __packed; +struct audpp_cmd_cfg_object_params_srstm_w { + audpp_cmd_cfg_object_params_common common; + unsigned short v[SRS_PARAMS_MAX_W]; +} __packed; +struct audpp_cmd_cfg_object_params_srstm_c { + audpp_cmd_cfg_object_params_common common; + unsigned short v[SRS_PARAMS_MAX_C]; +} __packed; +struct audpp_cmd_cfg_object_params_srstm_h { + audpp_cmd_cfg_object_params_common common; + unsigned short v[SRS_PARAMS_MAX_H]; +} __packed; +struct audpp_cmd_cfg_object_params_srstm_p { + audpp_cmd_cfg_object_params_common common; + unsigned short v[SRS_PARAMS_MAX_P]; +} __packed; +struct audpp_cmd_cfg_object_params_srstm_l { + audpp_cmd_cfg_object_params_common common; + unsigned short v[SRS_PARAMS_MAX_L]; +} __packed; + +#endif /* QDSP5AUDPPCMDI_H */ + diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h new file mode 100644 index 00000000000..0ba8261582e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h @@ -0,0 +1,321 @@ +#ifndef QDSP5AUDPPMSG_H +#define QDSP5AUDPPMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P O S T P R O C E S S I N G M S G + +GENERAL DESCRIPTION + Messages sent by AUDPPTASK to ARM + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppmsg.h#4 $ + +===========================================================================*/ + +/* + * AUDPPTASK uses audPPuPRlist to send messages to the ARM + * Location : MEMA + * Buffer Size : 45 + * No of Buffers in a queue : 5 for gaming audio and 1 for other images + */ + +/* + * MSG to Informs the ARM os Success/Failure of bringing up the decoder + */ + +#define AUDPP_MSG_STATUS_MSG 0x0001 +#define AUDPP_MSG_STATUS_MSG_LEN \ + sizeof(audpp_msg_status_msg) + +#define AUDPP_MSG_STATUS_SLEEP 0x0000 +#define AUDPP_MSG_STATUS_INIT 0x0001 +#define AUDPP_MSG_STATUS_CFG 0x0002 +#define AUDPP_MSG_STATUS_PLAY 0x0003 + +#define AUDPP_MSG_REASON_NONE 0x0000 +#define AUDPP_MSG_REASON_MEM 0x0001 +#define AUDPP_MSG_REASON_NODECODER 0x0002 + +typedef struct{ + unsigned short dec_id; + unsigned short status; + unsigned short reason; +} __attribute__((packed)) audpp_msg_status_msg; + +/* + * MSG to communicate the spectrum analyzer output bands to the ARM + */ +#define AUDPP_MSG_SPA_BANDS 0x0002 +#define AUDPP_MSG_SPA_BANDS_LEN \ + sizeof(audpp_msg_spa_bands) + +typedef struct { + unsigned short current_object; + unsigned short spa_band_1; + unsigned short spa_band_2; + unsigned short spa_band_3; + unsigned short spa_band_4; + unsigned short spa_band_5; + unsigned short spa_band_6; + unsigned short spa_band_7; + unsigned short spa_band_8; + unsigned short spa_band_9; + unsigned short spa_band_10; + unsigned short spa_band_11; + unsigned short spa_band_12; + unsigned short spa_band_13; + unsigned short spa_band_14; + unsigned short spa_band_15; + unsigned short spa_band_16; + unsigned short spa_band_17; + unsigned short spa_band_18; + unsigned short spa_band_19; + unsigned short spa_band_20; + unsigned short spa_band_21; + unsigned short spa_band_22; + unsigned short spa_band_23; + unsigned short spa_band_24; + unsigned short spa_band_25; + unsigned short spa_band_26; + unsigned short spa_band_27; + unsigned short spa_band_28; + unsigned short spa_band_29; + unsigned short spa_band_30; + unsigned short spa_band_31; + unsigned short spa_band_32; +} __attribute__((packed)) audpp_msg_spa_bands; + +/* + * MSG to communicate the PCM I/O buffer status to ARM + */ +#define AUDPP_MSG_HOST_PCM_INTF_MSG 0x0003 +#define AUDPP_MSG_HOST_PCM_INTF_MSG_LEN \ + sizeof(audpp_msg_host_pcm_intf_msg) + +#define AUDPP_MSG_HOSTPCM_ID_TX_ARM 0x0000 +#define AUDPP_MSG_HOSTPCM_ID_ARM_TX 0x0001 +#define AUDPP_MSG_HOSTPCM_ID_RX_ARM 0x0002 +#define AUDPP_MSG_HOSTPCM_ID_ARM_RX 0x0003 + +#define AUDPP_MSG_SAMP_FREQ_INDX_96000 0x0000 +#define AUDPP_MSG_SAMP_FREQ_INDX_88200 0x0001 +#define AUDPP_MSG_SAMP_FREQ_INDX_64000 0x0002 +#define AUDPP_MSG_SAMP_FREQ_INDX_48000 0x0003 +#define AUDPP_MSG_SAMP_FREQ_INDX_44100 0x0004 +#define AUDPP_MSG_SAMP_FREQ_INDX_32000 0x0005 +#define AUDPP_MSG_SAMP_FREQ_INDX_24000 0x0006 +#define AUDPP_MSG_SAMP_FREQ_INDX_22050 0x0007 +#define AUDPP_MSG_SAMP_FREQ_INDX_16000 0x0008 +#define AUDPP_MSG_SAMP_FREQ_INDX_12000 0x0009 +#define AUDPP_MSG_SAMP_FREQ_INDX_11025 0x000A +#define AUDPP_MSG_SAMP_FREQ_INDX_8000 0x000B + +#define AUDPP_MSG_CHANNEL_MODE_MONO 0x0001 +#define AUDPP_MSG_CHANNEL_MODE_STEREO 0x0002 + +typedef struct{ + unsigned short obj_num; + unsigned short numbers_of_samples; + unsigned short host_pcm_id; + unsigned short buf_indx; + unsigned short samp_freq_indx; + unsigned short channel_mode; +} __attribute__((packed)) audpp_msg_host_pcm_intf_msg; + + +/* + * MSG to communicate 3D position of the source and listener , source volume + * source rolloff, source orientation + */ + +#define AUDPP_MSG_QAFX_POS 0x0004 +#define AUDPP_MSG_QAFX_POS_LEN \ + sizeof(audpp_msg_qafx_pos) + +typedef struct { + unsigned short current_object; + unsigned short x_pos_lis_msw; + unsigned short x_pos_lis_lsw; + unsigned short y_pos_lis_msw; + unsigned short y_pos_lis_lsw; + unsigned short z_pos_lis_msw; + unsigned short z_pos_lis_lsw; + unsigned short x_fwd_msw; + unsigned short x_fwd_lsw; + unsigned short y_fwd_msw; + unsigned short y_fwd_lsw; + unsigned short z_fwd_msw; + unsigned short z_fwd_lsw; + unsigned short x_up_msw; + unsigned short x_up_lsw; + unsigned short y_up_msw; + unsigned short y_up_lsw; + unsigned short z_up_msw; + unsigned short z_up_lsw; + unsigned short x_vel_lis_msw; + unsigned short x_vel_lis_lsw; + unsigned short y_vel_lis_msw; + unsigned short y_vel_lis_lsw; + unsigned short z_vel_lis_msw; + unsigned short z_vel_lis_lsw; + unsigned short threed_enable_flag; + unsigned short volume; + unsigned short x_pos_source_msw; + unsigned short x_pos_source_lsw; + unsigned short y_pos_source_msw; + unsigned short y_pos_source_lsw; + unsigned short z_pos_source_msw; + unsigned short z_pos_source_lsw; + unsigned short max_dist_0_msw; + unsigned short max_dist_0_lsw; + unsigned short min_dist_0_msw; + unsigned short min_dist_0_lsw; + unsigned short roll_off_factor; + unsigned short mute_after_max_flag; + unsigned short x_vel_source_msw; + unsigned short x_vel_source_lsw; + unsigned short y_vel_source_msw; + unsigned short y_vel_source_lsw; + unsigned short z_vel_source_msw; + unsigned short z_vel_source_lsw; +} __attribute__((packed)) audpp_msg_qafx_pos; + +/* + * MSG to provide AVSYNC feedback from DSP to ARM + */ + +#define AUDPP_MSG_AVSYNC_MSG 0x0005 +#define AUDPP_MSG_AVSYNC_MSG_LEN \ + sizeof(audpp_msg_avsync_msg) + +typedef struct { + unsigned short active_flag; + unsigned short num_samples_counter0_HSW; + unsigned short num_samples_counter0_MSW; + unsigned short num_samples_counter0_LSW; + unsigned short num_bytes_counter0_HSW; + unsigned short num_bytes_counter0_MSW; + unsigned short num_bytes_counter0_LSW; + unsigned short samp_freq_obj_0; + unsigned short samp_freq_obj_1; + unsigned short samp_freq_obj_2; + unsigned short samp_freq_obj_3; + unsigned short samp_freq_obj_4; + unsigned short samp_freq_obj_5; + unsigned short samp_freq_obj_6; + unsigned short samp_freq_obj_7; + unsigned short samp_freq_obj_8; + unsigned short samp_freq_obj_9; + unsigned short samp_freq_obj_10; + unsigned short samp_freq_obj_11; + unsigned short samp_freq_obj_12; + unsigned short samp_freq_obj_13; + unsigned short samp_freq_obj_14; + unsigned short samp_freq_obj_15; + unsigned short num_samples_counter4_HSW; + unsigned short num_samples_counter4_MSW; + unsigned short num_samples_counter4_LSW; + unsigned short num_bytes_counter4_HSW; + unsigned short num_bytes_counter4_MSW; + unsigned short num_bytes_counter4_LSW; +} __attribute__((packed)) audpp_msg_avsync_msg; + +/* + * MSG to provide PCM DMA Missed feedback from the DSP to ARM + */ + +#define AUDPP_MSG_PCMDMAMISSED 0x0006 +#define AUDPP_MSG_PCMDMAMISSED_LEN \ + sizeof(audpp_msg_pcmdmamissed); + +typedef struct{ + /* + ** Bit 0 0 = PCM DMA not missed for object 0 + ** 1 = PCM DMA missed for object0 + ** Bit 1 0 = PCM DMA not missed for object 1 + ** 1 = PCM DMA missed for object1 + ** Bit 2 0 = PCM DMA not missed for object 2 + ** 1 = PCM DMA missed for object2 + ** Bit 3 0 = PCM DMA not missed for object 3 + ** 1 = PCM DMA missed for object3 + ** Bit 4 0 = PCM DMA not missed for object 4 + ** 1 = PCM DMA missed for object4 + */ + unsigned short pcmdmamissed; +} __attribute__((packed)) audpp_msg_pcmdmamissed; + +/* + * MSG to AUDPP enable or disable feedback form DSP to ARM + */ + +#define AUDPP_MSG_CFG_MSG 0x0007 +#define AUDPP_MSG_CFG_MSG_LEN \ + sizeof(audpp_msg_cfg_msg) + +#define AUDPP_MSG_ENA_ENA 0xFFFF +#define AUDPP_MSG_ENA_DIS 0x0000 + +typedef struct{ + /* Enabled - 0xffff + ** Disabled - 0 + */ + unsigned short enabled; +} __attribute__((packed)) audpp_msg_cfg_msg; + +/* + * MSG to communicate the reverb per object volume + */ + +#define AUDPP_MSG_QREVERB_VOLUME 0x0008 +#define AUDPP_MSG_QREVERB_VOLUME_LEN \ + sizeof(audpp_msg_qreverb_volume) + + +typedef struct { + unsigned short obj_0_gain; + unsigned short obj_1_gain; + unsigned short obj_2_gain; + unsigned short obj_3_gain; + unsigned short obj_4_gain; + unsigned short hpcm_obj_volume; +} __attribute__((packed)) audpp_msg_qreverb_volume; + +#define AUDPP_MSG_ROUTING_ACK 0x0009 +#define AUDPP_MSG_ROUTING_ACK_LEN \ + sizeof(struct audpp_msg_routing_ack) + +struct audpp_msg_routing_ack { + unsigned short dec_id; + unsigned short routing_mode; +} __attribute__((packed)); + +#define AUDPP_MSG_FLUSH_ACK 0x000A + +#define ADSP_MESSAGE_ID 0xFFFF + +#endif /* QDSP5AUDPPMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproc.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproc.h new file mode 100644 index 00000000000..234c4ac869a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproc.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef QDSP5AUDPREPROC_H +#define _QDSP5AUDPREPROC_H + +#include +#include + +#define MSM_AUD_ENC_MODE_TUNNEL 0x00000100 +#define MSM_AUD_ENC_MODE_NONTUNNEL 0x00000200 + +#define AUDPREPROC_CODEC_MASK 0x00FF +#define AUDPREPROC_MODE_MASK 0xFF00 + +#define MSM_ADSP_ENC_MODE_TUNNEL 24 +#define MSM_ADSP_ENC_MODE_NON_TUNNEL 25 + +/* Exported common api's from audpreproc layer */ +int audpreproc_aenc_alloc(unsigned enc_type, const char **module_name, + unsigned *queue_id); +void audpreproc_aenc_free(int enc_id); + +#endif /* QDSP5AUDPREPROC_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h new file mode 100644 index 00000000000..8efc916c333 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h @@ -0,0 +1,256 @@ +#ifndef QDSP5AUDPREPROCCMDI_H +#define QDSP5AUDPREPROCCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P R E P R O C E S S I N G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by AUDPREPROC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreproccmdi.h#2 $ + +===========================================================================*/ + +/* + * AUDIOPREPROC COMMANDS: + * ARM uses uPAudPreProcCmdQueue to communicate with AUDPREPROCTASK + * Location : MEMB + * Buffer size : 51 + * Number of buffers in a queue : 3 + */ + +/* + * Command to configure the parameters of AGC + */ + +#define AUDPREPROC_CMD_CFG_AGC_PARAMS 0x0000 +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_LEN \ + sizeof(audpreproc_cmd_cfg_agc_params) + +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE 0x0009 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH 0x000A +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE 0x000B +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH 0x000C +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG 0x000D +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN 0x000E +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG 0x000F + +#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA -1 +#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS 0x0000 + +#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_ADP_GAIN -1 +#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_STATIC_GAIN 0x0000 + +#define AUDPREPROC_CMD_PARAM_MASK_RMS_TAY 0x0004 +#define AUDPREPROC_CMD_PARAM_MASK_RELEASEK 0x0005 +#define AUDPREPROC_CMD_PARAM_MASK_DELAY 0x0006 +#define AUDPREPROC_CMD_PARAM_MASK_ATTACKK 0x0007 +#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW 0x0008 +#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST 0x0009 +#define AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK 0x000A +#define AUDPREPROC_CMD_PARAM_MASK_AIG_MIN 0x000B +#define AUDPREPROC_CMD_PARAM_MASK_AIG_MAX 0x000C +#define AUDPREPROC_CMD_PARAM_MASK_LEAK_UP 0x000D +#define AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN 0x000E +#define AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK 0x000F + +typedef struct { + unsigned short cmd_id; + unsigned short tx_agc_param_mask; + unsigned short tx_agc_enable_flag; + unsigned short static_gain; + signed short adaptive_gain_flag; + unsigned short expander_th; + unsigned short expander_slope; + unsigned short compressor_th; + unsigned short compressor_slope; + unsigned short param_mask; + unsigned short aig_attackk; + unsigned short aig_leak_down; + unsigned short aig_leak_up; + unsigned short aig_max; + unsigned short aig_min; + unsigned short aig_releasek; + unsigned short aig_leakrate_fast; + unsigned short aig_leakrate_slow; + unsigned short attackk_msw; + unsigned short attackk_lsw; + unsigned short delay; + unsigned short releasek_msw; + unsigned short releasek_lsw; + unsigned short rms_tav; +} __attribute__((packed)) audpreproc_cmd_cfg_agc_params; + + +/* + * Command to configure the params of Advanved AGC + */ + +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2 0x0001 +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2_LEN \ + sizeof(audpreproc_cmd_cfg_agc_params_2) + +#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_ENA -1; +#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_DIS 0x0000; + +typedef struct { + unsigned short cmd_id; + unsigned short agc_param_mask; + signed short tx_agc_enable_flag; + unsigned short comp_static_gain; + unsigned short exp_th; + unsigned short exp_slope; + unsigned short comp_th; + unsigned short comp_slope; + unsigned short comp_rms_tav; + unsigned short comp_samp_mask; + unsigned short comp_attackk_msw; + unsigned short comp_attackk_lsw; + unsigned short comp_releasek_msw; + unsigned short comp_releasek_lsw; + unsigned short comp_delay; + unsigned short comp_makeup_gain; +} __attribute__((packed)) audpreproc_cmd_cfg_agc_params_2; + +/* + * Command to configure params for ns + */ + +#define AUDPREPROC_CMD_CFG_NS_PARAMS 0x0002 +#define AUDPREPROC_CMD_CFG_NS_PARAMS_LEN \ + sizeof(audpreproc_cmd_cfg_ns_params) + +#define AUDPREPROC_CMD_EC_MODE_NEW_NLMS_ENA 0x0001 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_DES_ENA 0x0002 +#define AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA 0x0004 +#define AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_CNI_ENA 0x0008 +#define AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS 0x0000 + +#define AUDPREPROC_CMD_EC_MODE_NEW_NLES_ENA 0x0010 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA 0x0020 +#define AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA 0x0040 +#define AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_PCD_ENA 0x0080 +#define AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_FEHI_ENA 0x0100 +#define AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_NEHI_ENA 0x0200 +#define AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLPP_ENA 0x0400 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_FNE_ENA 0x0800 +#define AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_ENA 0x1000 +#define AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS 0x0000 + +typedef struct { + unsigned short cmd_id; + unsigned short ec_mode_new; + unsigned short dens_gamma_n; + unsigned short dens_nfe_block_size; + unsigned short dens_limit_ns; + unsigned short dens_limit_ns_d; + unsigned short wb_gamma_e; + unsigned short wb_gamma_n; +} __attribute__((packed)) audpreproc_cmd_cfg_ns_params; + +/* + * Command to configure parameters for IIR tuning filter + */ + +#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS 0x0003 +#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS_LEN \ + sizeof(audpreproc_cmd_cfg_iir_tuning_filter_params) + +#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS 0x0000 +#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short active_flag; + unsigned short num_bands; + unsigned short numerator_coeff_b0_filter0_lsw; + unsigned short numerator_coeff_b0_filter0_msw; + unsigned short numerator_coeff_b1_filter0_lsw; + unsigned short numerator_coeff_b1_filter0_msw; + unsigned short numerator_coeff_b2_filter0_lsw; + unsigned short numerator_coeff_b2_filter0_msw; + unsigned short numerator_coeff_b0_filter1_lsw; + unsigned short numerator_coeff_b0_filter1_msw; + unsigned short numerator_coeff_b1_filter1_lsw; + unsigned short numerator_coeff_b1_filter1_msw; + unsigned short numerator_coeff_b2_filter1_lsw; + unsigned short numerator_coeff_b2_filter1_msw; + unsigned short numerator_coeff_b0_filter2_lsw; + unsigned short numerator_coeff_b0_filter2_msw; + unsigned short numerator_coeff_b1_filter2_lsw; + unsigned short numerator_coeff_b1_filter2_msw; + unsigned short numerator_coeff_b2_filter2_lsw; + unsigned short numerator_coeff_b2_filter2_msw; + unsigned short numerator_coeff_b0_filter3_lsw; + unsigned short numerator_coeff_b0_filter3_msw; + unsigned short numerator_coeff_b1_filter3_lsw; + unsigned short numerator_coeff_b1_filter3_msw; + unsigned short numerator_coeff_b2_filter3_lsw; + unsigned short numerator_coeff_b2_filter3_msw; + unsigned short denominator_coeff_a0_filter0_lsw; + unsigned short denominator_coeff_a0_filter0_msw; + unsigned short denominator_coeff_a1_filter0_lsw; + unsigned short denominator_coeff_a1_filter0_msw; + unsigned short denominator_coeff_a0_filter1_lsw; + unsigned short denominator_coeff_a0_filter1_msw; + unsigned short denominator_coeff_a1_filter1_lsw; + unsigned short denominator_coeff_a1_filter1_msw; + unsigned short denominator_coeff_a0_filter2_lsw; + unsigned short denominator_coeff_a0_filter2_msw; + unsigned short denominator_coeff_a1_filter2_lsw; + unsigned short denominator_coeff_a1_filter2_msw; + unsigned short denominator_coeff_a0_filter3_lsw; + unsigned short denominator_coeff_a0_filter3_msw; + unsigned short denominator_coeff_a1_filter3_lsw; + unsigned short denominator_coeff_a1_filter3_msw; + + unsigned short shift_factor_filter0; + unsigned short shift_factor_filter1; + unsigned short shift_factor_filter2; + unsigned short shift_factor_filter3; + + unsigned short channel_selected0; + unsigned short channel_selected1; + unsigned short channel_selected2; + unsigned short channel_selected3; +} __attribute__((packed))audpreproc_cmd_cfg_iir_tuning_filter_params; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h new file mode 100644 index 00000000000..0696066f91b --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h @@ -0,0 +1,85 @@ +#ifndef QDSP5AUDPREPROCMSG_H +#define QDSP5AUDPREPROCMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P R E P R O C E S S I N G M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are rcvd by AUDPREPROC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreprocmsg.h#3 $ + +===========================================================================*/ + +/* + * ADSPREPROCTASK Messages + * AUDPREPROCTASK uses audPreProcUpRlist to communicate with ARM + * Location : MEMA + * Message Length : 2 + */ + +/* + * Message to indicate particular feature has been enabled or disabled + */ + + +#define AUDPREPROC_MSG_CMD_CFG_DONE_MSG 0x0001 +#define AUDPREPROC_MSG_CMD_CFG_DONE_MSG_LEN \ + sizeof(audpreproc_msg_cmd_cfg_done_msg) + +#define AUDPREPROC_MSG_TYPE_AGC 0x0000 +#define AUDPREPROC_MSG_TYPE_NOISE_REDUCTION 0x0001 +#define AUDPREPROC_MSG_TYPE_IIR_FILTER 0x0002 + + +#define AUDPREPROC_MSG_STATUS_FLAG_ENA -1 +#define AUDPREPROC_MSG_STATUS_FLAG_DIS 0x0000 + +typedef struct { + unsigned short type; + signed short status_flag; +} __attribute__((packed)) audpreproc_msg_cmd_cfg_done_msg; + + +/* + * Message to indicate particular feature has selected for wrong samp freq + */ + +#define AUDPREPROC_MSG_ERROR_MSG_ID 0x0002 +#define AUDPREPROC_MSG_ERROR_MSG_ID_LEN \ + sizeof(audpreproc_msg_error_msg_id) + +#define AUDPREPROC_MSG_ERR_INDEX_NS 0x0000 + +typedef struct { + unsigned short err_index; +} __attribute__((packed)) audpreproc_msg_error_msg_id; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h new file mode 100644 index 00000000000..5045de05aea --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h @@ -0,0 +1,401 @@ +#ifndef QDSP5AUDRECCMDI_H +#define QDSP5AUDRECCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + * + * A U D I O R E C O R D I N T E R N A L C O M M A N D S + * + * GENERAL DESCRIPTION + * This file contains defintions of format blocks of commands + * that are accepted by AUDREC Task + * + * REFERENCES + * None + * + * EXTERNALIZED FUNCTIONS + * None + * + * Copyright (c) 1992-2009, 2011 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + *====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audreccmdi.h#3 $ + +============================================================================*/ + +/* + * AUDRECTASK COMMANDS + * ARM uses 2 queues to communicate with the AUDRECTASK + * 1.uPAudRecCmdQueue + * Location :MEMC + * Buffer Size : 8 + * No of Buffers in a queue : 3 + * 2.audRecUpBitStreamQueue + * Location : MEMC + * Buffer Size : 4 + * No of buffers in a queue : 2 + */ + +/* + * Commands on uPAudRecCmdQueue + */ + +/* + * Command to initiate and terminate the audio recording section + */ + +#define AUDREC_CMD_CFG 0x0000 +#define AUDREC_CMD_CFG_LEN sizeof(audrec_cmd_cfg) + +#define AUDREC_CMD_TYPE_0_INDEX_WAV 0x0000 +#define AUDREC_CMD_TYPE_0_INDEX_AAC 0x0001 +#define AUDREC_CMD_TYPE_0_INDEX_AMRNB 0x000A +#define AUDREC_CMD_TYPE_0_INDEX_EVRC 0x000B +#define AUDREC_CMD_TYPE_0_INDEX_QCELP 0x000C + +#define AUDREC_CMD_TYPE_0_ENA 0x4000 +#define AUDREC_CMD_TYPE_0_DIS 0x0000 + +#define AUDREC_CMD_TYPE_0_NOUPDATE 0x0000 +#define AUDREC_CMD_TYPE_0_UPDATE 0x8000 + +#define AUDREC_CMD_TYPE_1_INDEX_SBC 0x0002 + +#define AUDREC_CMD_TYPE_1_ENA 0x4000 +#define AUDREC_CMD_TYPE_1_DIS 0x0000 + +#define AUDREC_CMD_TYPE_1_NOUPDATE 0x0000 +#define AUDREC_CMD_TYPE_1_UPDATE 0x8000 + +typedef struct { + unsigned short cmd_id; + unsigned short type_0; + unsigned short type_1; +} __attribute__((packed)) audrec_cmd_cfg; + + +/* + * Command to configure the recording parameters for RecType0(AAC/WAV) encoder + */ + +#define AUDREC_CMD_AREC0PARAM_CFG 0x0001 +#define AUDREC_CMD_AREC0PARAM_CFG_LEN \ + sizeof(audrec_cmd_arec0param_cfg) + +#define AUDREC_CMD_SAMP_RATE_INDX_8000 0x000B +#define AUDREC_CMD_SAMP_RATE_INDX_11025 0x000A +#define AUDREC_CMD_SAMP_RATE_INDX_12000 0x0009 +#define AUDREC_CMD_SAMP_RATE_INDX_16000 0x0008 +#define AUDREC_CMD_SAMP_RATE_INDX_22050 0x0007 +#define AUDREC_CMD_SAMP_RATE_INDX_24000 0x0006 +#define AUDREC_CMD_SAMP_RATE_INDX_32000 0x0005 +#define AUDREC_CMD_SAMP_RATE_INDX_44100 0x0004 +#define AUDREC_CMD_SAMP_RATE_INDX_48000 0x0003 + +#define AUDREC_CMD_STEREO_MODE_MONO 0x0000 +#define AUDREC_CMD_STEREO_MODE_STEREO 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short ptr_to_extpkt_buffer_msw; + unsigned short ptr_to_extpkt_buffer_lsw; + unsigned short buf_len; + unsigned short samp_rate_index; + unsigned short stereo_mode; + unsigned short rec_quality; +} __attribute__((packed)) audrec_cmd_arec0param_cfg; + +/* + * Command to configure the recording parameters for RecType1(SBC) encoder + */ + +#define AUDREC_CMD_AREC1PARAM_CFG 0x0002 +#define AUDREC_CMD_AREC1PARAM_CFG_LEN \ + sizeof(audrec_cmd_arec1param_cfg) + +#define AUDREC_CMD_PARAM_BUF_BLOCKS_4 0x0000 +#define AUDREC_CMD_PARAM_BUF_BLOCKS_8 0x0001 +#define AUDREC_CMD_PARAM_BUF_BLOCKS_12 0x0002 +#define AUDREC_CMD_PARAM_BUF_BLOCKS_16 0x0003 + +#define AUDREC_CMD_PARAM_BUF_SUB_BANDS_8 0x0010 +#define AUDREC_CMD_PARAM_BUF_MODE_MONO 0x0000 +#define AUDREC_CMD_PARAM_BUF_MODE_DUAL 0x0040 +#define AUDREC_CMD_PARAM_BUF_MODE_STEREO 0x0050 +#define AUDREC_CMD_PARAM_BUF_MODE_JSTEREO 0x0060 +#define AUDREC_CMD_PARAM_BUF_LOUDNESS 0x0000 +#define AUDREC_CMD_PARAM_BUF_SNR 0x0100 +#define AUDREC_CMD_PARAM_BUF_BASIC_VER 0x0000 + +typedef struct { + unsigned short cmd_id; + unsigned short ptr_to_extpkt_buffer_msw; + unsigned short ptr_to_extpkt_buffer_lsw; + unsigned short buf_len; + unsigned short param_buf; + unsigned short bit_rate_0; + unsigned short bit_rate_1; +} __attribute__((packed)) audrec_cmd_arec1param_cfg; + +/* + * Command to enable encoder for the recording + */ + +#define AUDREC_CMD_ENC_CFG 0x0003 +#define AUDREC_CMD_ENC_CFG_LEN \ + sizeof(struct audrec_cmd_enc_cfg) + + +#define AUDREC_CMD_ENC_ENA 0x8000 +#define AUDREC_CMD_ENC_DIS 0x0000 + +#define AUDREC_CMD_ENC_TYPE_MASK 0x001F + +struct audrec_cmd_enc_cfg { + unsigned short cmd_id; + unsigned short audrec_enc_type; + unsigned short audrec_obj_idx; +} __attribute__((packed)); + +/* + * Command to set external memory config for the selected encoder + */ + +#define AUDREC_CMD_ARECMEM_CFG 0x0004 +#define AUDREC_CMD_ARECMEM_CFG_LEN \ + sizeof(struct audrec_cmd_arecmem_cfg) + + +struct audrec_cmd_arecmem_cfg { + unsigned short cmd_id; + unsigned short audrec_obj_idx; + unsigned short audrec_up_pkt_intm_cnt; + unsigned short audrec_extpkt_buffer_msw; + unsigned short audrec_extpkt_buffer_lsw; + unsigned short audrec_extpkt_buffer_num; +} __attribute__((packed)); + +/* + * Command to configure the recording parameters for selected encoder + */ + +#define AUDREC_CMD_ARECPARAM_CFG 0x0005 +#define AUDREC_CMD_ARECPARAM_COMMON_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_common_cfg) + + +struct audrec_cmd_arecparam_common_cfg { + unsigned short cmd_id; + unsigned short audrec_obj_idx; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_WAV_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_wav_cfg) + + +struct audrec_cmd_arecparam_wav_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short samp_rate_idx; + unsigned short stereo_mode; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_AAC_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_aac_cfg) + + +struct audrec_cmd_arecparam_aac_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short samp_rate_idx; + unsigned short stereo_mode; + unsigned short rec_quality; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_SBC_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_sbc_cfg) + + +struct audrec_cmd_arecparam_sbc_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short param_buf; + unsigned short bit_rate_0; + unsigned short bit_rate_1; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_AMRNB_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_amrnb_cfg) + + +struct audrec_cmd_arecparam_amrnb_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short samp_rate_idx; + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short update_mode; + unsigned short dtx_mode; + unsigned short test_mode; + unsigned short used_mode; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_EVRC_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_evrc_cfg) + + +struct audrec_cmd_arecparam_evrc_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short samp_rate_idx; + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short update_mode; + unsigned short enc_min_rate; + unsigned short enc_max_rate; + unsigned short rate_modulation_cmd; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_QCELP_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_qcelp_cfg) + + +struct audrec_cmd_arecparam_qcelp_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short samp_rate_idx; + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short update_mode; + unsigned short enc_min_rate; + unsigned short enc_max_rate; + unsigned short rate_modulation_cmd; + unsigned short reduced_rate_level; +} __attribute__((packed)); + +#define AUDREC_CMD_ARECPARAM_FGVNB_CFG_LEN \ + sizeof(struct audrec_cmd_arecparam_fgvnb_cfg) + + +struct audrec_cmd_arecparam_fgvnb_cfg { + struct audrec_cmd_arecparam_common_cfg common; + unsigned short samp_rate_idx; + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short update_mode; + unsigned short fgv_min_rate; + unsigned short fgv_max_rate; + unsigned short reduced_rate_level; +} __attribute__((packed)); + +/* + * Command to configure Tunnel(RT) or Non-Tunnel(FTRT) mode + */ + +#define AUDREC_CMD_ROUTING_MODE 0x0006 +#define AUDREC_CMD_ROUTING_MODE_LEN \ + sizeof(struct audpreproc_audrec_cmd_routing_mode) + +#define AUDIO_ROUTING_MODE_FTRT 0x0001 +#define AUDIO_ROUTING_MODE_RT 0x0002 + +struct audrec_cmd_routing_mode { + unsigned short cmd_id; + unsigned short routing_mode; +} __packed; + +/* + * Command to configure pcm input memory + */ + +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC 0x0007 +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC_LEN \ + sizeof(struct audrec_cmd_pcm_cfg_arm_to_enc) + +struct audrec_cmd_pcm_cfg_arm_to_enc { + unsigned short cmd_id; + unsigned short config_update_flag; + unsigned short enable_flag; + unsigned short sampling_freq; + unsigned short channels; + unsigned short frequency_of_intimation; + unsigned short max_number_of_buffers; +} __packed; + +#define AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE -1 +#define AUDREC_PCM_CONFIG_UPDATE_FLAG_DISABLE 0 + +#define AUDREC_ENABLE_FLAG_VALUE -1 +#define AUDREC_DISABLE_FLAG_VALUE 0 + +/* + * Command to intimate available pcm buffer + */ + +#define AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC 0x0008 +#define AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC_LEN \ + sizeof(struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc) + +struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc { + unsigned short cmd_id; + unsigned short num_buffers; + unsigned short buffer_write_cnt_msw; + unsigned short buffer_write_cnt_lsw; + unsigned short buf_address_length[8];/*this array holds address + and length details of + two buffers*/ +} __packed; + +/* + * Command to flush + */ + +#define AUDREC_CMD_FLUSH 0x009 +#define AUDREC_CMD_FLUSH_LEN \ + sizeof(struct audrec_cmd_flush) + +struct audrec_cmd_flush { + unsigned short cmd_id; +} __packed; + +/* + * Commands on audRecUpBitStreamQueue + */ + +/* + * Command to indicate the current packet read count + */ + +#define AUDREC_CMD_PACKET_EXT_PTR 0x0000 +#define AUDREC_CMD_PACKET_EXT_PTR_LEN \ + sizeof(audrec_cmd_packet_ext_ptr) + +#define AUDREC_CMD_TYPE_0 0x0000 +#define AUDREC_CMD_TYPE_1 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short type; /* audrec_obj_idx */ + unsigned short curr_rec_count_msw; + unsigned short curr_rec_count_lsw; +} __attribute__((packed)) audrec_cmd_packet_ext_ptr; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h new file mode 100644 index 00000000000..339e4f7dafa --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h @@ -0,0 +1,223 @@ +#ifndef QDSP5AUDRECMSGI_H +#define QDSP5AUDRECMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + * + * A U D I O R E C O R D M E S S A G E S + * + * GENERAL DESCRIPTION + * This file contains defintions of format blocks of messages + * that are sent by AUDREC Task + * + * REFERENCES + * None + * + * EXTERNALIZED FUNCTIONS + * None + * + * Copyright (c) 1992-2009, 2011 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + *====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audrecmsg.h#3 $ + +============================================================================*/ + +/* + * AUDRECTASK MESSAGES + * AUDRECTASK uses audRecUpRlist to communicate with ARM + * Location : MEMC + * Buffer size : 4 + * No of buffers in a queue : 2 + */ + +/* + * Message to notify that config command is done + */ + +#define AUDREC_MSG_CMD_CFG_DONE_MSG 0x0002 +#define AUDREC_MSG_CMD_CFG_DONE_MSG_LEN \ + sizeof(struct audrec_msg_cmd_cfg_done_msg) + + +#define AUDREC_MSG_CFG_DONE_TYPE_0_ENA 0x4000 +#define AUDREC_MSG_CFG_DONE_TYPE_0_DIS 0x0000 + +#define AUDREC_MSG_CFG_DONE_TYPE_0_NO_UPDATE 0x0000 +#define AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE 0x8000 + +#define AUDREC_MSG_CFG_DONE_TYPE_1_ENA 0x4000 +#define AUDREC_MSG_CFG_DONE_TYPE_1_DIS 0x0000 + +#define AUDREC_MSG_CFG_DONE_TYPE_1_NO_UPDATE 0x0000 +#define AUDREC_MSG_CFG_DONE_TYPE_1_UPDATE 0x8000 + +#define AUDREC_MSG_CFG_DONE_ENC_ENA 0x8000 +#define AUDREC_MSG_CFG_DONE_ENC_DIS 0x0000 + +struct audrec_msg_cmd_cfg_done_msg { + unsigned short audrec_enc_type; + unsigned short audrec_obj_idx; +} __attribute__((packed)); + +/* + * Message to notify arec0/1 or concurrent encoder cfg done + * and recording params recieved by task + */ + +#define AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG 0x0003 +#define AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG_LEN \ + sizeof(struct audrec_msg_cmd_arec_param_cfg_done_msg) + + +#define AUDREC_MSG_AREC_PARAM_TYPE_0 0x0000 +#define AUDREC_MSG_AREC_PARAM_TYPE_1 0x0001 + +struct audrec_msg_cmd_arec_param_cfg_done_msg { + unsigned short audrec_obj_idx; +} __attribute__((packed)); + +/* + * Message to notify no more buffers are available in ext mem to DME + * Or no concurrent encoder supported + */ +/* for 7x27 */ +#define AUDREC_MSG_FATAL_ERR_MSG 0x0004 +#define AUDREC_MSG_FATAL_ERR_MSG_LEN \ + sizeof(struct audrec_msg_fatal_err_msg) + + +#define AUDREC_MSG_FATAL_ERR_TYPE_0 0x0000 +#define AUDREC_MSG_FATAL_ERR_TYPE_1 0x0001 + +struct audrec_msg_fatal_err_msg { + unsigned short audrec_obj_idx; + unsigned short audrec_err_id; +} __attribute__((packed)); + +/* for 7x27A */ +#define AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG 0x0004 +#define AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN \ + sizeof(struct audrec_msg_no_ext_pkt_avail_msg) + +#define AUDREC_MSG_NO_EXT_PKT_AVAILABLE_TYPE_0 0x0000 +#define AUDREC_MSG_NO_EXT_PKT_AVAILABLE_TYPE_1 0x0001 + +struct audrec_msg_no_ext_pkt_avail_msg { + unsigned short audrec_obj_idx; + unsigned short audrec_err_id; +} __packed; + +/* + * Message to notify DME deliverd the encoded pkt to ext pkt buffer + */ + +#define AUDREC_MSG_PACKET_READY_MSG 0x0005 +#define AUDREC_MSG_PACKET_READY_MSG_LEN \ + sizeof(struct audrec_msg_packet_ready_msg) + + +#define AUDREC_MSG_PACKET_READY_TYPE_0 0x0000 +#define AUDREC_MSG_PACKET_READY_TYPE_1 0x0001 + +struct audrec_msg_packet_ready_msg { + unsigned short audrec_obj_idx; + unsigned short pkt_counter_msw; + unsigned short pkt_counter_lsw; + unsigned short pkt_read_cnt_msw; + unsigned short pkt_read_cnt_lsw; +} __attribute__((packed)); + +/* + * Message to notify external memory cfg done and recieved by task + */ + +#define AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG 0x0006 +#define AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG_LEN \ + sizeof(struct audrec_msg_cmd_arec_mem_cfg_done_msg) + + +struct audrec_msg_cmd_arec_mem_cfg_done_msg { + unsigned short audrec_obj_idx; +} __attribute__((packed)); + +/* + * Message to indicate Routing mode + * configuration success or failure + */ + +#define AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG 0x0007 +#define AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG_LEN \ + sizeof(struct audrec_msg_cmd_routing_mode_done_msg) + +struct audrec_msg_cmd_routing_mode_done_msg { + unsigned short configuration; +} __packed; + +/* + * Message to indicate pcm buffer configured + */ + +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG 0x0008 +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG_LEN \ + sizeof(struct audrec_cmd_pcm_cfg_arm_to_enc_msg) + +struct audrec_cmd_pcm_cfg_arm_to_enc_msg { + unsigned short configuration; +} __packed; + +/* + * Message to indicate encoded packet is delivered to external buffer in FTRT + */ + +#define AUDREC_UP_NT_PACKET_READY_MSG 0x0009 +#define AUDREC_UP_NT_PACKET_READY_MSG_LEN \ + sizeof(struct audrec_up_nt_packet_ready_msg) + +struct audrec_up_nt_packet_ready_msg { + unsigned short audrec_packetwrite_cnt_lsw; + unsigned short audrec_packetwrite_cnt_msw; + unsigned short audrec_upprev_readcount_lsw; + unsigned short audrec_upprev_readcount_msw; +} __packed; + +/* + * Message to indicate pcm buffer is consumed + */ + +#define AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG 0x000A +#define AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG_LEN \ + sizeof(struct audrec_cmd_pcm_buffer_ptr_update_arm_to_enc_msg) + +struct audrec_cmd_pcm_buffer_ptr_update_arm_to_enc_msg { + unsigned short buffer_readcnt_msw; + unsigned short buffer_readcnt_lsw; + unsigned short number_of_buffers; + unsigned short buffer_address_length[]; +} __packed; + +/* + * Message to indicate flush acknowledgement + */ + +#define AUDREC_CMD_FLUSH_DONE_MSG 0x000B + +#define ADSP_MESSAGE_ID 0xFFFF + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h new file mode 100644 index 00000000000..7f25f4703c4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h @@ -0,0 +1,377 @@ +#ifndef QDSP5VIDJPEGCMDI_H +#define QDSP5VIDJPEGCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + J P E G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by JPEG Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: +when who what, where, why +-------- --- ---------------------------------------------------------- +06/09/08 sv initial version +===========================================================================*/ + +/* + * ARM to JPEG configuration commands are passed through the + * uPJpegCfgCmdQueue + */ + +/* + * Command to configure JPEG Encoder + */ + +#define JPEG_CMD_ENC_CFG 0x0000 +#define JPEG_CMD_ENC_CFG_LEN sizeof(jpeg_cmd_enc_cfg) + +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_0 0x0000 +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_90 0x0100 +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_180 0x0200 +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_270 0x0300 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M 0x0003 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2 0x0000 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V1 0x0001 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H1V2 0x0002 + +#define JPEG_CMD_IP_SIZE_CFG_LUMA_HEIGHT_M 0x0000FFFF +#define JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M 0xFFFF0000 +#define JPEG_CMD_ENC_UPSAMP_IP_SIZE_CFG_ENA 0x0001 +#define JPEG_CMD_ENC_UPSAMP_IP_SIZE_CFG_DIS 0x0000 + +#define JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M 0xFFFF + +typedef struct { + unsigned int cmd_id; + unsigned int process_cfg; + unsigned int ip_size_cfg; + unsigned int op_size_cfg; + unsigned int frag_cfg; + unsigned int frag_cfg_part[16]; + + unsigned int part_num; + + unsigned int op_buf_0_cfg_part1; + unsigned int op_buf_0_cfg_part2; + unsigned int op_buf_1_cfg_part1; + unsigned int op_buf_1_cfg_part2; + + unsigned int luma_qunt_table[32]; + unsigned int chroma_qunt_table[32]; + + unsigned int upsamp_ip_size_cfg; + unsigned int upsamp_ip_frame_off; + unsigned int upsamp_pp_filter_coeff[64]; +} __attribute__((packed)) jpeg_cmd_enc_cfg; + +/* + * Command to configure JPEG Decoder + */ + +#define JPEG_CMD_DEC_CFG 0x0001 +#define JPEG_CMD_DEC_CFG_LEN sizeof(jpeg_cmd_dec_cfg) + +#define JPEG_CMD_DEC_OP_DATA_FORMAT_M 0x0001 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2 0x0000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_H2V1 0x0001 + +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_8 0x000000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_4 0x010000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_2 0x020000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_1 0x030000 + +#define JPEG_CMD_DEC_IP_STREAM_BUF_CFG_PART3_NOT_FINAL 0x0000 +#define JPEG_CMD_DEC_IP_STREAM_BUF_CFG_PART3_FINAL 0x0001 + + +typedef struct { + unsigned int cmd_id; + unsigned int img_dimension_cfg; + unsigned int op_data_format; + unsigned int restart_interval; + unsigned int ip_buf_partition_num; + unsigned int ip_stream_buf_cfg_part1; + unsigned int ip_stream_buf_cfg_part2; + unsigned int ip_stream_buf_cfg_part3; + unsigned int op_stream_buf_0_cfg_part1; + unsigned int op_stream_buf_0_cfg_part2; + unsigned int op_stream_buf_0_cfg_part3; + unsigned int op_stream_buf_1_cfg_part1; + unsigned int op_stream_buf_1_cfg_part2; + unsigned int op_stream_buf_1_cfg_part3; + unsigned int luma_qunt_table_0_3; + unsigned int luma_qunt_table_4_7; + unsigned int luma_qunt_table_8_11; + unsigned int luma_qunt_table_12_15; + unsigned int luma_qunt_table_16_19; + unsigned int luma_qunt_table_20_23; + unsigned int luma_qunt_table_24_27; + unsigned int luma_qunt_table_28_31; + unsigned int luma_qunt_table_32_35; + unsigned int luma_qunt_table_36_39; + unsigned int luma_qunt_table_40_43; + unsigned int luma_qunt_table_44_47; + unsigned int luma_qunt_table_48_51; + unsigned int luma_qunt_table_52_55; + unsigned int luma_qunt_table_56_59; + unsigned int luma_qunt_table_60_63; + unsigned int chroma_qunt_table_0_3; + unsigned int chroma_qunt_table_4_7; + unsigned int chroma_qunt_table_8_11; + unsigned int chroma_qunt_table_12_15; + unsigned int chroma_qunt_table_16_19; + unsigned int chroma_qunt_table_20_23; + unsigned int chroma_qunt_table_24_27; + unsigned int chroma_qunt_table_28_31; + unsigned int chroma_qunt_table_32_35; + unsigned int chroma_qunt_table_36_39; + unsigned int chroma_qunt_table_40_43; + unsigned int chroma_qunt_table_44_47; + unsigned int chroma_qunt_table_48_51; + unsigned int chroma_qunt_table_52_55; + unsigned int chroma_qunt_table_56_59; + unsigned int chroma_qunt_table_60_63; + unsigned int luma_dc_hm_code_cnt_table_0_3; + unsigned int luma_dc_hm_code_cnt_table_4_7; + unsigned int luma_dc_hm_code_cnt_table_8_11; + unsigned int luma_dc_hm_code_cnt_table_12_15; + unsigned int luma_dc_hm_code_val_table_0_3; + unsigned int luma_dc_hm_code_val_table_4_7; + unsigned int luma_dc_hm_code_val_table_8_11; + unsigned int chroma_dc_hm_code_cnt_table_0_3; + unsigned int chroma_dc_hm_code_cnt_table_4_7; + unsigned int chroma_dc_hm_code_cnt_table_8_11; + unsigned int chroma_dc_hm_code_cnt_table_12_15; + unsigned int chroma_dc_hm_code_val_table_0_3; + unsigned int chroma_dc_hm_code_val_table_4_7; + unsigned int chroma_dc_hm_code_val_table_8_11; + unsigned int luma_ac_hm_code_cnt_table_0_3; + unsigned int luma_ac_hm_code_cnt_table_4_7; + unsigned int luma_ac_hm_code_cnt_table_8_11; + unsigned int luma_ac_hm_code_cnt_table_12_15; + unsigned int luma_ac_hm_code_val_table_0_3; + unsigned int luma_ac_hm_code_val_table_4_7; + unsigned int luma_ac_hm_code_val_table_8_11; + unsigned int luma_ac_hm_code_val_table_12_15; + unsigned int luma_ac_hm_code_val_table_16_19; + unsigned int luma_ac_hm_code_val_table_20_23; + unsigned int luma_ac_hm_code_val_table_24_27; + unsigned int luma_ac_hm_code_val_table_28_31; + unsigned int luma_ac_hm_code_val_table_32_35; + unsigned int luma_ac_hm_code_val_table_36_39; + unsigned int luma_ac_hm_code_val_table_40_43; + unsigned int luma_ac_hm_code_val_table_44_47; + unsigned int luma_ac_hm_code_val_table_48_51; + unsigned int luma_ac_hm_code_val_table_52_55; + unsigned int luma_ac_hm_code_val_table_56_59; + unsigned int luma_ac_hm_code_val_table_60_63; + unsigned int luma_ac_hm_code_val_table_64_67; + unsigned int luma_ac_hm_code_val_table_68_71; + unsigned int luma_ac_hm_code_val_table_72_75; + unsigned int luma_ac_hm_code_val_table_76_79; + unsigned int luma_ac_hm_code_val_table_80_83; + unsigned int luma_ac_hm_code_val_table_84_87; + unsigned int luma_ac_hm_code_val_table_88_91; + unsigned int luma_ac_hm_code_val_table_92_95; + unsigned int luma_ac_hm_code_val_table_96_99; + unsigned int luma_ac_hm_code_val_table_100_103; + unsigned int luma_ac_hm_code_val_table_104_107; + unsigned int luma_ac_hm_code_val_table_108_111; + unsigned int luma_ac_hm_code_val_table_112_115; + unsigned int luma_ac_hm_code_val_table_116_119; + unsigned int luma_ac_hm_code_val_table_120_123; + unsigned int luma_ac_hm_code_val_table_124_127; + unsigned int luma_ac_hm_code_val_table_128_131; + unsigned int luma_ac_hm_code_val_table_132_135; + unsigned int luma_ac_hm_code_val_table_136_139; + unsigned int luma_ac_hm_code_val_table_140_143; + unsigned int luma_ac_hm_code_val_table_144_147; + unsigned int luma_ac_hm_code_val_table_148_151; + unsigned int luma_ac_hm_code_val_table_152_155; + unsigned int luma_ac_hm_code_val_table_156_159; + unsigned int luma_ac_hm_code_val_table_160_161; + unsigned int chroma_ac_hm_code_cnt_table_0_3; + unsigned int chroma_ac_hm_code_cnt_table_4_7; + unsigned int chroma_ac_hm_code_cnt_table_8_11; + unsigned int chroma_ac_hm_code_cnt_table_12_15; + unsigned int chroma_ac_hm_code_val_table_0_3; + unsigned int chroma_ac_hm_code_val_table_4_7; + unsigned int chroma_ac_hm_code_val_table_8_11; + unsigned int chroma_ac_hm_code_val_table_12_15; + unsigned int chroma_ac_hm_code_val_table_16_19; + unsigned int chroma_ac_hm_code_val_table_20_23; + unsigned int chroma_ac_hm_code_val_table_24_27; + unsigned int chroma_ac_hm_code_val_table_28_31; + unsigned int chroma_ac_hm_code_val_table_32_35; + unsigned int chroma_ac_hm_code_val_table_36_39; + unsigned int chroma_ac_hm_code_val_table_40_43; + unsigned int chroma_ac_hm_code_val_table_44_47; + unsigned int chroma_ac_hm_code_val_table_48_51; + unsigned int chroma_ac_hm_code_val_table_52_55; + unsigned int chroma_ac_hm_code_val_table_56_59; + unsigned int chroma_ac_hm_code_val_table_60_63; + unsigned int chroma_ac_hm_code_val_table_64_67; + unsigned int chroma_ac_hm_code_val_table_68_71; + unsigned int chroma_ac_hm_code_val_table_72_75; + unsigned int chroma_ac_hm_code_val_table_76_79; + unsigned int chroma_ac_hm_code_val_table_80_83; + unsigned int chroma_ac_hm_code_val_table_84_87; + unsigned int chroma_ac_hm_code_val_table_88_91; + unsigned int chroma_ac_hm_code_val_table_92_95; + unsigned int chroma_ac_hm_code_val_table_96_99; + unsigned int chroma_ac_hm_code_val_table_100_103; + unsigned int chroma_ac_hm_code_val_table_104_107; + unsigned int chroma_ac_hm_code_val_table_108_111; + unsigned int chroma_ac_hm_code_val_table_112_115; + unsigned int chroma_ac_hm_code_val_table_116_119; + unsigned int chroma_ac_hm_code_val_table_120_123; + unsigned int chroma_ac_hm_code_val_table_124_127; + unsigned int chroma_ac_hm_code_val_table_128_131; + unsigned int chroma_ac_hm_code_val_table_132_135; + unsigned int chroma_ac_hm_code_val_table_136_139; + unsigned int chroma_ac_hm_code_val_table_140_143; + unsigned int chroma_ac_hm_code_val_table_144_147; + unsigned int chroma_ac_hm_code_val_table_148_151; + unsigned int chroma_ac_hm_code_val_table_152_155; + unsigned int chroma_ac_hm_code_val_table_156_159; + unsigned int chroma_ac_hm_code_val_table_160_161; +} __attribute__((packed)) jpeg_cmd_dec_cfg; + + +/* + * ARM to JPEG configuration commands are passed through the + * uPJpegActionCmdQueue + */ + +/* + * Command to start the encode process + */ + +#define JPEG_CMD_ENC_ENCODE 0x0001 +#define JPEG_CMD_ENC_ENCODE_LEN sizeof(jpeg_cmd_enc_encode) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_enc_encode; + + +/* + * Command to transition from current state of encoder to IDLE state + */ + +#define JPEG_CMD_ENC_IDLE 0x0006 +#define JPEG_CMD_ENC_IDLE_LEN sizeof(jpeg_cmd_enc_idle) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_enc_idle; + + +/* + * Command to inform the encoder that another buffer is ready + */ + +#define JPEG_CMD_ENC_OP_CONSUMED 0x0002 +#define JPEG_CMD_ENC_OP_CONSUMED_LEN sizeof(jpeg_cmd_enc_op_consumed) + + +typedef struct { + unsigned int cmd_id; + unsigned int op_buf_addr; + unsigned int op_buf_size; +} __attribute__((packed)) jpeg_cmd_enc_op_consumed; + + +/* + * Command to start the decoding process + */ + +#define JPEG_CMD_DEC_DECODE 0x0003 +#define JPEG_CMD_DEC_DECODE_LEN sizeof(jpeg_cmd_dec_decode) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_dec_decode; + + +/* + * Command to transition from the current state of decoder to IDLE + */ + +#define JPEG_CMD_DEC_IDLE 0x0007 +#define JPEG_CMD_DEC_IDLE_LEN sizeof(jpeg_cmd_dec_idle) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_dec_idle; + + +/* + * Command to inform that an op buffer is ready for use + */ + +#define JPEG_CMD_DEC_OP_CONSUMED 0x0004 +#define JPEG_CMD_DEC_OP_CONSUMED_LEN sizeof(jpeg_cmd_dec_op_consumed) + + +typedef struct { + unsigned int cmd_id; + unsigned int luma_op_buf_addr; + unsigned int luma_op_buf_size; + unsigned int chroma_op_buf_addr; +} __attribute__((packed)) jpeg_cmd_dec_op_consumed; + + +/* + * Command to pass a new ip buffer to the jpeg decoder + */ + +#define JPEG_CMD_DEC_IP 0x0005 +#define JPEG_CMD_DEC_IP_LEN sizeof(jpeg_cmd_dec_ip_len) + +#define JPEG_CMD_EOI_INDICATOR_NOT_END 0x0000 +#define JPEG_CMD_EOI_INDICATOR_END 0x0001 + +typedef struct { + unsigned int cmd_id; + unsigned int ip_buf_addr; + unsigned int ip_buf_size; + unsigned int eoi_indicator; +} __attribute__((packed)) jpeg_cmd_dec_ip; + + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h new file mode 100644 index 00000000000..993af420a86 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h @@ -0,0 +1,177 @@ +#ifndef QDSP5VIDJPEGMSGI_H +#define QDSP5VIDJPEGMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + J P E G I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are sent by JPEG Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +05/10/08 sv initial version +===========================================================================*/ + +/* + * Messages from JPEG task to ARM through jpeguPMsgQueue + */ + +/* + * Message is ACK for CMD_JPEGE_ENCODE cmd + */ + +#define JPEG_MSG_ENC_ENCODE_ACK 0x0000 +#define JPEG_MSG_ENC_ENCODE_ACK_LEN \ + sizeof(jpeg_msg_enc_encode_ack) + +typedef struct { +} __attribute__((packed)) jpeg_msg_enc_encode_ack; + + +/* + * Message informs the up when op buffer is ready for consumption and + * when encoding is complete or errors + */ + +#define JPEG_MSG_ENC_OP_PRODUCED 0x0001 +#define JPEG_MSG_ENC_OP_PRODUCED_LEN \ + sizeof(jpeg_msg_enc_op_produced) + +#define JPEG_MSGOP_OP_BUF_STATUS_ENC_DONE_PROGRESS 0x0000 +#define JPEG_MSGOP_OP_BUF_STATUS_ENC_DONE_COMPLETE 0x0001 +#define JPEG_MSGOP_OP_BUF_STATUS_ENC_ERR 0x10000 + +typedef struct { + unsigned int op_buf_addr; + unsigned int op_buf_size; + unsigned int op_buf_status; +} __attribute__((packed)) jpeg_msg_enc_op_produced; + + +/* + * Message to ack CMD_JPEGE_IDLE + */ + +#define JPEG_MSG_ENC_IDLE_ACK 0x0002 +#define JPEG_MSG_ENC_IDLE_ACK_LEN sizeof(jpeg_msg_enc_idle_ack) + + +typedef struct { +} __attribute__ ((packed)) jpeg_msg_enc_idle_ack; + + +/* + * Message to indicate the illegal command + */ + +#define JPEG_MSG_ENC_ILLEGAL_COMMAND 0x0003 +#define JPEG_MSG_ENC_ILLEGAL_COMMAND_LEN \ + sizeof(jpeg_msg_enc_illegal_command) + +typedef struct { + unsigned int status; +} __attribute__((packed)) jpeg_msg_enc_illegal_command; + + +/* + * Message to ACK CMD_JPEGD_DECODE + */ + +#define JPEG_MSG_DEC_DECODE_ACK 0x0004 +#define JPEG_MSG_DEC_DECODE_ACK_LEN \ + sizeof(jpeg_msg_dec_decode_ack) + + +typedef struct { +} __attribute__((packed)) jpeg_msg_dec_decode_ack; + + +/* + * Message to inform up that an op buffer is ready for consumption and when + * decoding is complete or an error occurs + */ + +#define JPEG_MSG_DEC_OP_PRODUCED 0x0005 +#define JPEG_MSG_DEC_OP_PRODUCED_LEN \ + sizeof(jpeg_msg_dec_op_produced) + +#define JPEG_MSG_DEC_OP_BUF_STATUS_PROGRESS 0x0000 +#define JPEG_MSG_DEC_OP_BUF_STATUS_DONE 0x0001 + +typedef struct { + unsigned int luma_op_buf_addr; + unsigned int chroma_op_buf_addr; + unsigned int num_mcus; + unsigned int op_buf_status; +} __attribute__((packed)) jpeg_msg_dec_op_produced; + +/* + * Message to ack CMD_JPEGD_IDLE cmd + */ + +#define JPEG_MSG_DEC_IDLE_ACK 0x0006 +#define JPEG_MSG_DEC_IDLE_ACK_LEN sizeof(jpeg_msg_dec_idle_ack) + + +typedef struct { +} __attribute__((packed)) jpeg_msg_dec_idle_ack; + + +/* + * Message to indicate illegal cmd was received + */ + +#define JPEG_MSG_DEC_ILLEGAL_COMMAND 0x0007 +#define JPEG_MSG_DEC_ILLEGAL_COMMAND_LEN \ + sizeof(jpeg_msg_dec_illegal_command) + + +typedef struct { + unsigned int status; +} __attribute__((packed)) jpeg_msg_dec_illegal_command; + +/* + * Message to request up for the next segment of ip bit stream + */ + +#define JPEG_MSG_DEC_IP_REQUEST 0x0008 +#define JPEG_MSG_DEC_IP_REQUEST_LEN \ + sizeof(jpeg_msg_dec_ip_request) + + +typedef struct { +} __attribute__((packed)) jpeg_msg_dec_ip_request; + + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h new file mode 100644 index 00000000000..4ab6cbf44a7 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h @@ -0,0 +1,82 @@ +#ifndef QDSP5LPMCMDI_H +#define QDSP5LPMCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + L P M I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by LPM Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + + +/* + * Command to start LPM processing based on the config params + */ + +#define LPM_CMD_START 0x0000 +#define LPM_CMD_START_LEN sizeof(lpm_cmd_start) + +#define LPM_CMD_SPATIAL_FILTER_PART_OPMODE_0 0x00000000 +#define LPM_CMD_SPATIAL_FILTER_PART_OPMODE_1 0x00010000 +typedef struct { + unsigned int cmd_id; + unsigned int ip_data_cfg_part1; + unsigned int ip_data_cfg_part2; + unsigned int ip_data_cfg_part3; + unsigned int ip_data_cfg_part4; + unsigned int op_data_cfg_part1; + unsigned int op_data_cfg_part2; + unsigned int op_data_cfg_part3; + unsigned int spatial_filter_part[32]; +} __attribute__((packed)) lpm_cmd_start; + + + +/* + * Command to stop LPM processing + */ + +#define LPM_CMD_IDLE 0x0001 +#define LPM_CMD_IDLE_LEN sizeof(lpm_cmd_idle) + +typedef struct { + unsigned int cmd_id; +} __attribute__((packed)) lpm_cmd_idle; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h new file mode 100644 index 00000000000..68f8874ab93 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h @@ -0,0 +1,80 @@ +#ifndef QDSP5LPMMSGI_H +#define QDSP5LPMMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + L P M I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by LPM Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + +/* + * Message to acknowledge CMD_LPM_IDLE command + */ + +#define LPM_MSG_IDLE_ACK 0x0000 +#define LPM_MSG_IDLE_ACK_LEN sizeof(lpm_msg_idle_ack) + +typedef struct { +} __attribute__((packed)) lpm_msg_idle_ack; + + +/* + * Message to acknowledge CMD_LPM_START command + */ + + +#define LPM_MSG_START_ACK 0x0001 +#define LPM_MSG_START_ACK_LEN sizeof(lpm_msg_start_ack) + + +typedef struct { +} __attribute__((packed)) lpm_msg_start_ack; + + +/* + * Message to notify the ARM that LPM processing is complete + */ + +#define LPM_MSG_DONE 0x0002 +#define LPM_MSG_DONE_LEN sizeof(lpm_msg_done) + +typedef struct { +} __attribute__((packed)) lpm_msg_done; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5rmtcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5rmtcmdi.h new file mode 100644 index 00000000000..7a66b687d3c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5rmtcmdi.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef QDSP5RMTCMDI_H +#define QDSP5RMTCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + R M T A S K I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by RM Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/* + * ARM to RMTASK Commands + * + * ARM uses one command queue to communicate with AUDPPTASK + * 1) apuRmtQueue: Used to send commands to RMTASK from APPS processor + * Location : MEMA + * Buffer Size : 3 words + */ + +#define RM_CMD_AUD_CODEC_CFG 0x0 + +#define RM_AUD_CLIENT_ID 0x0 +#define RMT_ENABLE 0x1 +#define RMT_DISABLE 0x0 + +struct aud_codec_config_cmd { + unsigned short cmd_id; + unsigned char task_id; + unsigned char client_id; + unsigned short enable; + unsigned short dec_type; +} __attribute__((packed)); + +#endif /* QDSP5RMTCMDI_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5rmtmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5rmtmsg.h new file mode 100644 index 00000000000..a890e76f5f5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5rmtmsg.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef QDSP5RMTMSG_H +#define QDSP5RMTMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + R M T A S K M S G + +GENERAL DESCRIPTION + Messages sent by RMTASK to APPS PROCESSOR + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/* + * RMTASK uses RmtApuRlist to send messages to the APPS PROCESSOR + * Location : MEMA + * Buffer Size : 3 + */ + +#define RMT_CODEC_CONFIG_ACK 0x1 + +struct aud_codec_config_ack { + unsigned char task_id; + unsigned char client_id; + unsigned char reason; + unsigned char enable; + unsigned short dec_type; +} __attribute__((packed)); + +#define RMT_DSP_OUT_OF_MIPS 0x2 + +struct rmt_dsp_out_of_mips { + unsigned short dec_info; + unsigned short rvd_0; + unsigned short rvd_1; +} __attribute__((packed)); + +#endif /* QDSP5RMTMSG_H */ + diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h new file mode 100644 index 00000000000..1064b176265 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h @@ -0,0 +1,189 @@ +#ifndef QDSP5VIDDECCMDI_H +#define QDSP5VIDDECCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V I D E O D E C O D E R I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by VIDDEC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdeccmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +05/10/08 ac initial version +===========================================================================*/ + + +/* + * Command to inform VIDDEC that new subframe packet is ready + */ + +#define VIDDEC_CMD_SUBFRAME_PKT 0x0000 +#define VIDDEC_CMD_SUBFRAME_PKT_LEN \ + sizeof(viddec_cmd_subframe_pkt) + +#define VIDDEC_CMD_SF_INFO_1_DM_DMA_STATS_EXCHANGE_FLAG_DM 0x0000 +#define VIDDEC_CMD_SF_INFO_1_DM_DMA_STATS_EXCHANGE_FLAG_DMA 0x0001 + +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_CONTI 0x0000 +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_FIRST 0x0001 +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_LAST 0x0002 +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_FIRST_AND_LAST 0x0003 + +#define VIDDEC_CMD_CODEC_SELECTION_WORD_MPEG_4 0x0000 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_263_P0 0x0001 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_264 0x0002 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_263_p3 0x0003 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_RV9 0x0004 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_WMV9 0x0005 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_SMCDB 0x0006 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_QFRE 0x0007 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_VLD 0x0008 + +typedef struct { + unsigned short cmd_id; + unsigned short packet_seq_number; + unsigned short codec_instance_id; + unsigned short subframe_packet_size_high; + unsigned short subframe_packet_size_low; + unsigned short subframe_packet_high; + unsigned short subframe_packet_low; + unsigned short subframe_packet_partition; + unsigned short statistics_packet_size_high; + unsigned short statistics_packet_size_low; + unsigned short statistics_packet_high; + unsigned short statistics_packet_low; + unsigned short statistics_partition; + unsigned short subframe_info_1; + unsigned short subframe_info_0; + unsigned short codec_selection_word; + unsigned short num_mbs; +} __attribute__((packed)) viddec_cmd_subframe_pkt; + + +/* + * Command to inform VIDDEC task that post processing is required for the frame + */ + +#define VIDDEC_CMD_PP_ENABLE 0x0001 +#define VIDDEC_CMD_PP_ENABLE_LEN \ + sizeof(viddec_cmd_pp_enable) + +#define VIDDEC_CMD_PP_INFO_0_DM_DMA_LS_EXCHANGE_FLAG_DM 0x0000 +#define VIDDEC_CMD_PP_INFO_0_DM_DMA_LS_EXCHANGE_FLAG_DMA 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short packet_seq_num; + unsigned short codec_instance_id; + unsigned short postproc_info_0; + unsigned short codec_selection_word; + unsigned short pp_output_addr_high; + unsigned short pp_output_addr_low; + unsigned short postproc_info_1; + unsigned short load_sharing_packet_size_high; + unsigned short load_sharing_packet_size_low; + unsigned short load_sharing_packet_high; + unsigned short load_sharing_packet_low; + unsigned short load_sharing_partition; + unsigned short pp_param_0; + unsigned short pp_param_1; + unsigned short pp_param_2; + unsigned short pp_param_3; +} __attribute__((packed)) viddec_cmd_pp_enable; + + +/* + * FRAME Header Packet : It is at the start of new frame + */ + +#define VIDDEC_CMD_FRAME_HEADER_PACKET 0x0002 + +#define VIDDEC_CMD_FRAME_INFO_0_ERROR_SKIP 0x0000 +#define VIDDEC_CMD_FRAME_INFO_0_ERROR_BLACK 0x0800 + +/* + * SLICE HEADER PACKET + * I-Slice and P-Slice + */ + +#define VIDDEC_CMD_SLICE_HEADER_PKT_ISLICE 0x0003 +#define VIDDEC_CMD_SLICE_HEADER_PKT_ISLICE_LEN \ + sizeof(viddec_cmd_slice_header_pkt_islice) + +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_PSLICE 0x0000 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_BSLICE 0x0100 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_ISLICE 0x0200 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_SPSLICE 0x0300 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_SISLICE 0x0400 +#define VIDDEC_CMD_ISLICE_INFO_1_NOPADDING 0x0000 +#define VIDDEC_CMD_ISLICE_INFO_1_PADDING 0x0800 + +#define VIDDEC_CMD_ISLICE_EOP_MARKER 0x7FFF + +typedef struct { + unsigned short cmd_id; + unsigned short packet_id; + unsigned short slice_info_0; + unsigned short slice_info_1; + unsigned short slice_info_2; + unsigned short num_bytes_in_rbsp_high; + unsigned short num_bytes_in_rbsp_low; + unsigned short num_bytes_in_rbsp_consumed; + unsigned short end_of_packet_marker; +} __attribute__((packed)) viddec_cmd_slice_header_pkt_islice; + + +#define VIDDEC_CMD_SLICE_HEADER_PKT_PSLICE 0x0003 +#define VIDDEC_CMD_SLICE_HEADER_PKT_PSLICE_LEN \ + sizeof(viddec_cmd_slice_header_pkt_pslice) + + +typedef struct { + unsigned short cmd_id; + unsigned short packet_id; + unsigned short slice_info_0; + unsigned short slice_info_1; + unsigned short slice_info_2; + unsigned short slice_info_3; + unsigned short refidx_l0_map_tab_info_0; + unsigned short refidx_l0_map_tab_info_1; + unsigned short refidx_l0_map_tab_info_2; + unsigned short refidx_l0_map_tab_info_3; + unsigned short num_bytes_in_rbsp_high; + unsigned short num_bytes_in_rbsp_low; + unsigned short num_bytes_in_rbsp_consumed; + unsigned short end_of_packet_marker; +} __attribute__((packed)) viddec_cmd_slice_header_pkt_pslice; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h new file mode 100644 index 00000000000..2d3ab89e986 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h @@ -0,0 +1,107 @@ +#ifndef QDSP5VIDDECMSGI_H +#define QDSP5VIDDECMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V I D E O D E C O D E R I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are sent by VIDDEC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdecmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +05/10/08 ac initial version +===========================================================================*/ + +/* + * Message to inform ARM which VDEC_SUBFRAME_PKT_CMD processed by VIDDEC TASK + */ + +#define VIDDEC_MSG_SUBF_DONE 0x0000 +#define VIDDEC_MSG_SUBF_DONE_LEN \ + sizeof(viddec_msg_subf_done) + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; +} __attribute__((packed)) viddec_msg_subf_done; + + +/* + * Message to inform ARM one frame has been decoded + */ + +#define VIDDEC_MSG_FRAME_DONE 0x0001 +#define VIDDEC_MSG_FRAME_DONE_LEN \ + sizeof(viddec_msg_frame_done) + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; +} __attribute__((packed)) viddec_msg_frame_done; + + +/* + * Message to inform ARM that post processing frame has been decoded + */ + +#define VIDDEC_MSG_PP_ENABLE_CMD_DONE 0x0002 +#define VIDDEC_MSG_PP_ENABLE_CMD_DONE_LEN \ + sizeof(viddec_msg_pp_enable_cmd_done) + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; +} __attribute__((packed)) viddec_msg_pp_enable_cmd_done; + + +/* + * Message to inform ARM that one post processing frame has been decoded + */ + + +#define VIDDEC_MSG_PP_FRAME_DONE 0x0003 +#define VIDDEC_MSG_PP_FRAME_DONE_LEN \ + sizeof(viddec_msg_pp_frame_done) + +#define VIDDEC_MSG_DISP_WORTHY_DISP 0x0000 +#define VIDDEC_MSG_DISP_WORTHY_DISP_NONE 0xFFFF + + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; + unsigned short display_worthy; +} __attribute__((packed)) viddec_msg_pp_frame_done; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h new file mode 100644 index 00000000000..b3c018fc31a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h @@ -0,0 +1,231 @@ +#ifndef QDSP5VIDENCCMDI_H +#define QDSP5VIDENCCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V I D E O E N C O D E R I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by VIDENC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +09/25/08 umeshp initial version +===========================================================================*/ + + #define VIDENC_CMD_CFG 0x0000 + #define VIDENC_CMD_ACTIVE 0x0001 + #define VIDENC_CMD_IDLE 0x0002 + #define VIDENC_CMD_FRAME_START 0x0003 + #define VIDENC_CMD_STATUS_QUERY 0x0004 + #define VIDENC_CMD_RC_CFG 0x0005 + #define VIDENC_CMD_INTRA_REFRESH 0x0006 + #define VIDENC_CMD_CODEC_CONFIG 0x0007 + #define VIDENC_CMD_VIDEO_CONFIG 0x0008 + #define VIDENC_CMD_PARAMETER_UPDATE 0x0009 + #define VIDENC_CMD_VENC_CLOCK 0x000A + #define VIDENC_CMD_DIS_CFG 0x000B + #define VIDENC_CMD_DIS 0x000C + #define VIDENC_CMD_DIGITAL_ZOOM 0x000D + + + + +/* + * Command to pass the frame message information to VIDENC + */ + + +#define VIDENC_CMD_FRAME_START_LEN \ + sizeof(videnc_cmd_frame_start) + +typedef struct { + unsigned short cmd_id; + unsigned short frame_info; + unsigned short frame_rho_budget_word_high; + unsigned short frame_rho_budget_word_low; + unsigned short input_luma_addr_high; + unsigned short input_luma_addr_low; + unsigned short input_chroma_addr_high; + unsigned short input_chroma_addr_low; + unsigned short ref_vop_buf_ptr_high; + unsigned short ref_vop_buf_ptr_low; + unsigned short enc_pkt_buf_ptr_high; + unsigned short enc_pkt_buf_ptr_low; + unsigned short enc_pkt_buf_size_high; + unsigned short enc_pkt_buf_size_low; + unsigned short unfilt_recon_vop_buf_ptr_high; + unsigned short unfilt_recon_vop_buf_ptr_low; + unsigned short filt_recon_vop_buf_ptr_high; + unsigned short filt_recon_vop_buf_ptr_low; +} __attribute__((packed)) videnc_cmd_frame_start; + +/* + * Command to pass the frame-level digital stabilization parameters to VIDENC + */ + + +#define VIDENC_CMD_DIS_LEN \ + sizeof(videnc_cmd_dis) + +typedef struct { + unsigned short cmd_id; + unsigned short vfe_out_prev_luma_addr_high; + unsigned short vfe_out_prev_luma_addr_low; + unsigned short stabilization_info; +} __attribute__((packed)) videnc_cmd_dis; + +/* + * Command to pass the codec related parameters to VIDENC + */ + + +#define VIDENC_CMD_CFG_LEN \ + sizeof(videnc_cmd_cfg) + +typedef struct { + unsigned short cmd_id; + unsigned short cfg_info_0; + unsigned short cfg_info_1; + unsigned short four_mv_threshold; + unsigned short ise_fse_mv_cost_fac; + unsigned short venc_frame_dim; + unsigned short venc_DM_partition; +} __attribute__((packed)) videnc_cmd_cfg; + +/* + * Command to start the video encoding + */ + + +#define VIDENC_CMD_ACTIVE_LEN \ + sizeof(videnc_cmd_active) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) videnc_cmd_active; + +/* + * Command to stop the video encoding + */ + + +#define VIDENC_CMD_IDLE_LEN \ + sizeof(videnc_cmd_idle) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) videnc_cmd_idle; + +/* + * Command to query staus of VIDENC + */ + + +#define VIDENC_CMD_STATUS_QUERY_LEN \ + sizeof(videnc_cmd_status_query) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) videnc_cmd_status_query; + +/* + * Command to set rate control for a frame + */ + + +#define VIDENC_CMD_RC_CFG_LEN \ + sizeof(videnc_cmd_rc_cfg) + +typedef struct { + unsigned short cmd_id; + unsigned short max_frame_qp_delta; + unsigned short max_min_frame_qp; +} __attribute__((packed)) videnc_cmd_rc_cfg; + +/* + * Command to set intra-refreshing + */ + + +#define VIDENC_CMD_INTRA_REFRESH_LEN \ + sizeof(videnc_cmd_intra_refresh) + +typedef struct { + unsigned short cmd_id; + unsigned short num_mb_refresh; + unsigned short mb_index[15]; +} __attribute__((packed)) videnc_cmd_intra_refresh; + +/* + * Command to pass digital zoom information to the VIDENC + */ +#define VIDENC_CMD_DIGITAL_ZOOM_LEN \ + sizeof(videnc_cmd_digital_zoom) + +typedef struct { + unsigned short cmd_id; + unsigned short digital_zoom_en; + unsigned short luma_frame_shift_X; + unsigned short luma_frame_shift_Y; + unsigned short up_ip_luma_rows; + unsigned short up_ip_luma_cols; + unsigned short up_ip_chroma_rows; + unsigned short up_ip_chroma_cols; + unsigned short luma_ph_incr_V_low; + unsigned short luma_ph_incr_V_high; + unsigned short luma_ph_incr_H_low; + unsigned short luma_ph_incr_H_high; + unsigned short chroma_ph_incr_V_low; + unsigned short chroma_ph_incr_V_high; + unsigned short chroma_ph_incr_H_low; + unsigned short chroma_ph_incr_H_high; +} __attribute__((packed)) videnc_cmd_digital_zoom; + +/* + * Command to configure digital stabilization parameters + */ + +#define VIDENC_CMD_DIS_CFG_LEN \ + sizeof(videnc_cmd_dis_cfg) + +typedef struct { + unsigned short cmd_id; + unsigned short image_stab_subf_start_row_col; + unsigned short image_stab_subf_dim; + unsigned short image_stab_info_0; +} __attribute__((packed)) videnc_cmd_dis_cfg; + + +/* + * Command to set VIDENC_CMD_VENC_CLOCK + */ + + +#define VIDENC_CMD_VENC_CLOCK_LEN \ + sizeof(struct videnc_cmd_venc_clock) + +struct videnc_cmd_venc_clock { + unsigned short cmd_id; + unsigned short payload; +} __attribute__((packed)) ; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h new file mode 100644 index 00000000000..4c5d752ab42 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h @@ -0,0 +1,910 @@ +#ifndef QDSP5VFECMDI_H +#define QDSP5VFECMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V F E I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by VFE Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfecmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + +/****************************************************************************** + * Commands through vfeCommandScaleQueue + *****************************************************************************/ + +/* + * Command to program scaler for op1 . max op of scaler is VGA + */ + + +#define VFE_CMD_SCALE_OP1_CFG 0x0000 +#define VFE_CMD_SCALE_OP1_CFG_LEN \ + sizeof(vfe_cmd_scale_op1_cfg) + +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_Y_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_Y_CASCADED 0x0001 +#define VFE_CMD_SCALE_OP1_SEL_H_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_H_Y_SCALER_ENA 0x0002 +#define VFE_CMD_SCALE_OP1_SEL_H_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_H_PP_Y_SCALER_ENA 0x0004 +#define VFE_CMD_SCALE_OP1_SEL_V_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_V_Y_SCALER_ENA 0x0008 +#define VFE_CMD_SCALE_OP1_SEL_V_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_V_PP_Y_SCALER_ENA 0x0010 +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_CBCR_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_CBCR_CASCADED 0x0020 +#define VFE_CMD_SCALE_OP1_SEL_H_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_H_CBCR_SCALER_ENA 0x0040 +#define VFE_CMD_SCALE_OP1_SEL_V_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_V_CBCR_SCALER_ENA 0x0080 + +#define VFE_CMD_OP1_PP_Y_SCALER_CFG_PART1_DONT_LOAD_COEFFS 0x80000000 +#define VFE_CMD_OP1_PP_Y_SCALER_CFG_PART1_LOAD_COEFFS 0x80000000 + +typedef struct { + unsigned int cmd_id; + unsigned int scale_op1_sel; + unsigned int y_scaler_cfg_part1; + unsigned int y_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part1; + unsigned int cbcr_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part3; + unsigned int pp_y_scaler_cfg_part1; + unsigned int pp_y_scaler_cfg_part2; + unsigned int y_scaler_v_coeff_bank_part1[16]; + unsigned int y_scaler_v_coeff_bank_part2[16]; + unsigned int y_scaler_h_coeff_bank_part1[16]; + unsigned int y_scaler_h_coeff_bank_part2[16]; +} __attribute__((packed)) vfe_cmd_scale_op1_cfg; + + +/* + * Command to program scaler for op2 + */ + +#define VFE_CMD_SCALE_OP2_CFG 0x0001 +#define VFE_CMD_SCALE_OP2_CFG_LEN \ + sizeof(vfe_cmd_scale_op2_cfg) + +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_Y_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_Y_CASCADED 0x0001 +#define VFE_CMD_SCALE_OP2_SEL_H_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_H_Y_SCALER_ENA 0x0002 +#define VFE_CMD_SCALE_OP2_SEL_H_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_H_PP_Y_SCALER_ENA 0x0004 +#define VFE_CMD_SCALE_OP2_SEL_V_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_V_Y_SCALER_ENA 0x0008 +#define VFE_CMD_SCALE_OP2_SEL_V_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_V_PP_Y_SCALER_ENA 0x0010 +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_CBCR_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_CBCR_CASCADED 0x0020 +#define VFE_CMD_SCALE_OP2_SEL_H_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_H_CBCR_SCALER_ENA 0x0040 +#define VFE_CMD_SCALE_OP2_SEL_V_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_V_CBCR_SCALER_ENA 0x0080 + +#define VFE_CMD_OP2_PP_Y_SCALER_CFG_PART1_DONT_LOAD_COEFFS 0x80000000 +#define VFE_CMD_OP2_PP_Y_SCALER_CFG_PART1_LOAD_COEFFS 0x80000000 + +typedef struct { + unsigned int cmd_id; + unsigned int scale_op2_sel; + unsigned int y_scaler_cfg_part1; + unsigned int y_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part1; + unsigned int cbcr_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part3; + unsigned int pp_y_scaler_cfg_part1; + unsigned int pp_y_scaler_cfg_part2; + unsigned int y_scaler_v_coeff_bank_part1[16]; + unsigned int y_scaler_v_coeff_bank_part2[16]; + unsigned int y_scaler_h_coeff_bank_part1[16]; + unsigned int y_scaler_h_coeff_bank_part2[16]; +} __attribute__((packed)) vfe_cmd_scale_op2_cfg; + + +/****************************************************************************** + * Commands through vfeCommandTableQueue + *****************************************************************************/ + +/* + * Command to program the AXI ip paths + */ + +#define VFE_CMD_AXI_IP_CFG 0x0000 +#define VFE_CMD_AXI_IP_CFG_LEN sizeof(vfe_cmd_axi_ip_cfg) + +#define VFE_CMD_IP_SEL_IP_FORMAT_8 0x0000 +#define VFE_CMD_IP_SEL_IP_FORMAT_10 0x0001 +#define VFE_CMD_IP_SEL_IP_FORMAT_12 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int ip_sel; + unsigned int ip_cfg_part1; + unsigned int ip_cfg_part2; + unsigned int ip_unpack_cfg_part[6]; + unsigned int ip_buf_addr[8]; +} __attribute__ ((packed)) vfe_cmd_axi_ip_cfg; + + +/* + * Command to program axi op paths + */ + +#define VFE_CMD_AXI_OP_CFG 0x0001 +#define VFE_CMD_AXI_OP_CFG_LEN sizeof(vfe_cmd_axi_op_cfg) + +#define VFE_CMD_OP_SEL_OP1 0x0000 +#define VFE_CMD_OP_SEL_OP2 0x0001 +#define VFE_CMD_OP_SEL_OP1_OP2 0x0002 +#define VFE_CMD_OP_SEL_CTOA 0x0003 +#define VFE_CMD_OP_SEL_CTOA_OP1 0x0004 +#define VFE_CMD_OP_SEL_CTOA_OP2 0x0005 +#define VFE_CMD_OP_SEL_OP_FORMAT_8 0x0000 +#define VFE_CMD_OP_SEL_OP_FORMAT_10 0x0008 +#define VFE_CMD_OP_SEL_OP_FORMAT_12 0x0010 + + +typedef struct { + unsigned int cmd_id; + unsigned int op_sel; + unsigned int op1_y_cfg_part1; + unsigned int op1_y_cfg_part2; + unsigned int op1_cbcr_cfg_part1; + unsigned int op1_cbcr_cfg_part2; + unsigned int op2_y_cfg_part1; + unsigned int op2_y_cfg_part2; + unsigned int op2_cbcr_cfg_part1; + unsigned int op2_cbcr_cfg_part2; + unsigned int op1_buf1_addr[16]; + unsigned int op2_buf1_addr[16]; +} __attribute__((packed)) vfe_cmd_axi_op_cfg; + + + + +/* + * Command to program the roll off correction module + */ + +#define VFE_CMD_ROLLOFF_CFG 0x0002 +#define VFE_CMD_ROLLOFF_CFG_LEN \ + sizeof(vfe_cmd_rolloff_cfg) + + +typedef struct { + unsigned int cmd_id; + unsigned int correction_opt_center_pos; + unsigned int radius_square_entry[32]; + unsigned int red_table_entry[32]; + unsigned int green_table_entry[32]; + unsigned int blue_table_entry[32]; +} __attribute__((packed)) vfe_cmd_rolloff_cfg; + +/* + * Command to program RGB gamma table + */ + +#define VFE_CMD_RGB_GAMMA_CFG 0x0003 +#define VFE_CMD_RGB_GAMMA_CFG_LEN \ + sizeof(vfe_cmd_rgb_gamma_cfg) + +#define VFE_CMD_RGB_GAMMA_SEL_LINEAR 0x0000 +#define VFE_CMD_RGB_GAMMA_SEL_PW_LINEAR 0x0001 +typedef struct { + unsigned int cmd_id; + unsigned int rgb_gamma_sel; + unsigned int rgb_gamma_entry[256]; +} __attribute__((packed)) vfe_cmd_rgb_gamma_cfg; + + +/* + * Command to program luma gamma table for the noise reduction path + */ + +#define VFE_CMD_Y_GAMMA_CFG 0x0004 +#define VFE_CMD_Y_GAMMA_CFG_LEN \ + sizeof(vfe_cmd_y_gamma_cfg) + +#define VFE_CMD_Y_GAMMA_SEL_LINEAR 0x0000 +#define VFE_CMD_Y_GAMMA_SEL_PW_LINEAR 0x0001 + +typedef struct { + unsigned int cmd_id; + unsigned int y_gamma_sel; + unsigned int y_gamma_entry[256]; +} __attribute__((packed)) vfe_cmd_y_gamma_cfg; + + + +/****************************************************************************** + * Commands through vfeCommandQueue + *****************************************************************************/ + +/* + * Command to reset the VFE to a known good state.All previously programmed + * Params will be lost + */ + + +#define VFE_CMD_RESET 0x0000 +#define VFE_CMD_RESET_LEN sizeof(vfe_cmd_reset) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_reset; + + +/* + * Command to start VFE processing based on the config params + */ + + +#define VFE_CMD_START 0x0001 +#define VFE_CMD_START_LEN sizeof(vfe_cmd_start) + +#define VFE_CMD_STARTUP_PARAMS_SRC_CAMIF 0x0000 +#define VFE_CMD_STARTUP_PARAMS_SRC_AXI 0x0001 +#define VFE_CMD_STARTUP_PARAMS_MODE_CONTINUOUS 0x0000 +#define VFE_CMD_STARTUP_PARAMS_MODE_SNAPSHOT 0x0002 + +#define VFE_CMD_IMAGE_PL_BLACK_LVL_CORR_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_BLACK_LVL_CORR_ENA 0x0001 +#define VFE_CMD_IMAGE_PL_ROLLOFF_CORR_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_ROLLOFF_CORR_ENA 0x0002 +#define VFE_CMD_IMAGE_PL_WHITE_BAL_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_WHITE_BAL_ENA 0x0004 +#define VFE_CMD_IMAGE_PL_RGB_GAMMA_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_RGB_GAMMA_ENA 0x0008 +#define VFE_CMD_IMAGE_PL_LUMA_NOISE_RED_PATH_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_LUMA_NOISE_RED_PATH_ENA 0x0010 +#define VFE_CMD_IMAGE_PL_ADP_FILTER_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_ADP_FILTER_ENA 0x0020 +#define VFE_CMD_IMAGE_PL_CHROMA_SAMP_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_CHROMA_SAMP_ENA 0x0040 + + +typedef struct { + unsigned int cmd_id; + unsigned int startup_params; + unsigned int image_pipeline; + unsigned int frame_dimension; +} __attribute__((packed)) vfe_cmd_start; + + +/* + * Command to halt all processing + */ + +#define VFE_CMD_STOP 0x0002 +#define VFE_CMD_STOP_LEN sizeof(vfe_cmd_stop) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_stop; + + +/* + * Command to commit the params that have been programmed to take + * effect on the next frame + */ + +#define VFE_CMD_UPDATE 0x0003 +#define VFE_CMD_UPDATE_LEN sizeof(vfe_cmd_update) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_update; + + +/* + * Command to program CAMIF module + */ + +#define VFE_CMD_CAMIF_CFG 0x0004 +#define VFE_CMD_CAMIF_CFG_LEN sizeof(vfe_cmd_camif_cfg) + +#define VFE_CMD_CFG_VSYNC_SYNC_EDGE_HIGH 0x0000 +#define VFE_CMD_CFG_VSYNC_SYNC_EDGE_LOW 0x0002 +#define VFE_CMD_CFG_HSYNC_SYNC_EDGE_HIGH 0x0000 +#define VFE_CMD_CFG_HSYNC_SYNC_EDGE_LOW 0x0004 +#define VFE_CMD_CFG_SYNC_MODE_APS 0x0000 +#define VFE_CMD_CFG_SYNC_MODE_EFS 0X0008 +#define VFE_CMD_CFG_SYNC_MODE_ELS 0x0010 +#define VFE_CMD_CFG_SYNC_MODE_RVD 0x0018 +#define VFE_CMD_CFG_VFE_SUBSAMP_EN_DIS 0x0000 +#define VFE_CMD_CFG_VFE_SUBSAMP_EN_ENA 0x0020 +#define VFE_CMD_CFG_BUS_SUBSAMP_EN_DIS 0x0000 +#define VFE_CMD_CFG_BUS_SUBSAMP_EN_ENA 0x0080 +#define VFE_CMD_CFG_IRQ_SUBSAMP_EN_DIS 0x0000 +#define VFE_CMD_CFG_IRQ_SUBSAMP_EN_ENA 0x0800 + +#define VFE_CMD_SUBSAMP2_CFG_PIXEL_SKIP_16 0x0000 +#define VFE_CMD_SUBSAMP2_CFG_PIXEL_SKIP_12 0x0010 + +#define VFE_CMD_EPOCH_IRQ_1_DIS 0x0000 +#define VFE_CMD_EPOCH_IRQ_1_ENA 0x4000 +#define VFE_CMD_EPOCH_IRQ_2_DIS 0x0000 +#define VFE_CMD_EPOCH_IRQ_2_ENA 0x8000 + +typedef struct { + unsigned int cmd_id; + unsigned int cfg; + unsigned int efs_cfg; + unsigned int frame_cfg; + unsigned int window_width_cfg; + unsigned int window_height_cfg; + unsigned int subsamp1_cfg; + unsigned int subsamp2_cfg; + unsigned int epoch_irq; +} __attribute__((packed)) vfe_cmd_camif_cfg; + + + +/* + * Command to program the black level module + */ + +#define VFE_CMD_BLACK_LVL_CFG 0x0005 +#define VFE_CMD_BLACK_LVL_CFG_LEN sizeof(vfe_cmd_black_lvl_cfg) + +#define VFE_CMD_BL_SEL_MANUAL 0x0000 +#define VFE_CMD_BL_SEL_AUTO 0x0001 + +typedef struct { + unsigned int cmd_id; + unsigned int black_lvl_sel; + unsigned int cfg_part[3]; +} __attribute__((packed)) vfe_cmd_black_lvl_cfg; + + +/* + * Command to program the active region by cropping the region of interest + */ + +#define VFE_CMD_ACTIVE_REGION_CFG 0x0006 +#define VFE_CMD_ACTIVE_REGION_CFG_LEN \ + sizeof(vfe_cmd_active_region_cfg) + + +typedef struct { + unsigned int cmd_id; + unsigned int cfg_part1; + unsigned int cfg_part2; +} __attribute__((packed)) vfe_cmd_active_region_cfg; + + + +/* + * Command to program the defective pixel correction(DPC) , + * adaptive bayer filter (ABF) and demosaic modules + */ + +#define VFE_CMD_DEMOSAIC_CFG 0x0007 +#define VFE_CMD_DEMOSAIC_CFG_LEN sizeof(vfe_cmd_demosaic_cfg) + +#define VFE_CMD_DEMOSAIC_PART1_ABF_EN_DIS 0x0000 +#define VFE_CMD_DEMOSAIC_PART1_ABF_EN_ENA 0x0001 +#define VFE_CMD_DEMOSAIC_PART1_DPC_EN_DIS 0x0000 +#define VFE_CMD_DEMOSAIC_PART1_DPC_EN_ENA 0x0002 +#define VFE_CMD_DEMOSAIC_PART1_FORCE_ABF_OFF 0x0000 +#define VFE_CMD_DEMOSAIC_PART1_FORCE_ABF_ON 0x0004 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1 0x00000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_2 0x10000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_4 0x20000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_8 0x30000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_2 0x50000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_4 0x60000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_8 0x70000000 + +typedef struct { + unsigned int cmd_id; + unsigned int demosaic_part1; + unsigned int demosaic_part2; + unsigned int demosaic_part3; + unsigned int demosaic_part4; + unsigned int demosaic_part5; +} __attribute__((packed)) vfe_cmd_demosaic_cfg; + + +/* + * Command to program the ip format + */ + +#define VFE_CMD_IP_FORMAT_CFG 0x0008 +#define VFE_CMD_IP_FORMAT_CFG_LEN \ + sizeof(vfe_cmd_ip_format_cfg) + +#define VFE_CMD_IP_FORMAT_SEL_RGRG 0x0000 +#define VFE_CMD_IP_FORMAT_SEL_GRGR 0x0001 +#define VFE_CMD_IP_FORMAT_SEL_BGBG 0x0002 +#define VFE_CMD_IP_FORMAT_SEL_GBGB 0x0003 +#define VFE_CMD_IP_FORMAT_SEL_YCBYCR 0x0004 +#define VFE_CMD_IP_FORMAT_SEL_YCRYCB 0x0005 +#define VFE_CMD_IP_FORMAT_SEL_CBYCRY 0x0006 +#define VFE_CMD_IP_FORMAT_SEL_CRYCBY 0x0007 +#define VFE_CMD_IP_FORMAT_SEL_NO_CHROMA 0x0000 +#define VFE_CMD_IP_FORMAT_SEL_CHROMA 0x0008 + + +typedef struct { + unsigned int cmd_id; + unsigned int ip_format_sel; + unsigned int balance_gains_part1; + unsigned int balance_gains_part2; +} __attribute__((packed)) vfe_cmd_ip_format_cfg; + + + +/* + * Command to program max and min allowed op values + */ + +#define VFE_CMD_OP_CLAMP_CFG 0x0009 +#define VFE_CMD_OP_CLAMP_CFG_LEN \ + sizeof(vfe_cmd_op_clamp_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int op_clamp_max; + unsigned int op_clamp_min; +} __attribute__((packed)) vfe_cmd_op_clamp_cfg; + + +/* + * Command to program chroma sub sample module + */ + +#define VFE_CMD_CHROMA_SUBSAMPLE_CFG 0x000A +#define VFE_CMD_CHROMA_SUBSAMPLE_CFG_LEN \ + sizeof(vfe_cmd_chroma_subsample_cfg) + +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_INTERESTIAL_SAMPS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_COSITED_SAMPS 0x0001 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_INTERESTIAL_SAMPS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_COSITED_SAMPS 0x0002 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_SUBSAMP_DIS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_SUBSAMP_ENA 0x0004 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_SUBSAMP_DIS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_SUBSAMP_ENA 0x0008 + +typedef struct { + unsigned int cmd_id; + unsigned int chroma_subsamp_sel; +} __attribute__((packed)) vfe_cmd_chroma_subsample_cfg; + + +/* + * Command to program the white balance module + */ + +#define VFE_CMD_WHITE_BALANCE_CFG 0x000B +#define VFE_CMD_WHITE_BALANCE_CFG_LEN \ + sizeof(vfe_cmd_white_balance_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int white_balance_gains; +} __attribute__((packed)) vfe_cmd_white_balance_cfg; + + +/* + * Command to program the color processing module + */ + +#define VFE_CMD_COLOR_PROCESS_CFG 0x000C +#define VFE_CMD_COLOR_PROCESS_CFG_LEN \ + sizeof(vfe_cmd_color_process_cfg) + +#define VFE_CMD_COLOR_CORRE_PART7_Q7_FACTORS 0x0000 +#define VFE_CMD_COLOR_CORRE_PART7_Q8_FACTORS 0x0001 +#define VFE_CMD_COLOR_CORRE_PART7_Q9_FACTORS 0x0002 +#define VFE_CMD_COLOR_CORRE_PART7_Q10_FACTORS 0x0003 + +typedef struct { + unsigned int cmd_id; + unsigned int color_correction_part1; + unsigned int color_correction_part2; + unsigned int color_correction_part3; + unsigned int color_correction_part4; + unsigned int color_correction_part5; + unsigned int color_correction_part6; + unsigned int color_correction_part7; + unsigned int chroma_enhance_part1; + unsigned int chroma_enhance_part2; + unsigned int chroma_enhance_part3; + unsigned int chroma_enhance_part4; + unsigned int chroma_enhance_part5; + unsigned int luma_calc_part1; + unsigned int luma_calc_part2; +} __attribute__((packed)) vfe_cmd_color_process_cfg; + + +/* + * Command to program adaptive filter module + */ + +#define VFE_CMD_ADP_FILTER_CFG 0x000D +#define VFE_CMD_ADP_FILTER_CFG_LEN \ + sizeof(vfe_cmd_adp_filter_cfg) + +#define VFE_CMD_ASF_CFG_PART_SMOOTH_FILTER_DIS 0x0000 +#define VFE_CMD_ASF_CFG_PART_SMOOTH_FILTER_ENA 0x0001 +#define VFE_CMD_ASF_CFG_PART_NO_SHARP_MODE 0x0000 +#define VFE_CMD_ASF_CFG_PART_SINGLE_FILTER 0x0002 +#define VFE_CMD_ASF_CFG_PART_DUAL_FILTER 0x0004 +#define VFE_CMD_ASF_CFG_PART_SHARP_MODE 0x0007 + +typedef struct { + unsigned int cmd_id; + unsigned int asf_cfg_part[7]; +} __attribute__((packed)) vfe_cmd_adp_filter_cfg; + + +/* + * Command to program for frame skip pattern for op1 and op2 + */ + +#define VFE_CMD_FRAME_SKIP_CFG 0x000E +#define VFE_CMD_FRAME_SKIP_CFG_LEN \ + sizeof(vfe_cmd_frame_skip_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int frame_skip_pattern_op1; + unsigned int frame_skip_pattern_op2; +} __attribute__((packed)) vfe_cmd_frame_skip_cfg; + + +/* + * Command to program field-of-view crop for digital zoom + */ + +#define VFE_CMD_FOV_CROP 0x000F +#define VFE_CMD_FOV_CROP_LEN sizeof(vfe_cmd_fov_crop) + +typedef struct { + unsigned int cmd_id; + unsigned int fov_crop_part1; + unsigned int fov_crop_part2; +} __attribute__((packed)) vfe_cmd_fov_crop; + + + +/* + * Command to program auto focus(AF) statistics module + */ + +#define VFE_CMD_STATS_AUTOFOCUS_CFG 0x0010 +#define VFE_CMD_STATS_AUTOFOCUS_CFG_LEN \ + sizeof(vfe_cmd_stats_autofocus_cfg) + +#define VFE_CMD_AF_STATS_SEL_STATS_DIS 0x0000 +#define VFE_CMD_AF_STATS_SEL_STATS_ENA 0x0001 +#define VFE_CMD_AF_STATS_SEL_PRI_FIXED 0x0000 +#define VFE_CMD_AF_STATS_SEL_PRI_VAR 0x0002 +#define VFE_CMD_AF_STATS_CFG_PART_METRIC_SUM 0x00000000 +#define VFE_CMD_AF_STATS_CFG_PART_METRIC_MAX 0x00200000 + +typedef struct { + unsigned int cmd_id; + unsigned int af_stats_sel; + unsigned int af_stats_cfg_part[8]; + unsigned int af_stats_op_buf_hdr; + unsigned int af_stats_op_buf[3]; +} __attribute__((packed)) vfe_cmd_stats_autofocus_cfg; + + +/* + * Command to program White balance(wb) and exposure (exp) + * statistics module + */ + +#define VFE_CMD_STATS_WB_EXP_CFG 0x0011 +#define VFE_CMD_STATS_WB_EXP_CFG_LEN \ + sizeof(vfe_cmd_stats_wb_exp_cfg) + +#define VFE_CMD_WB_EXP_STATS_SEL_STATS_DIS 0x0000 +#define VFE_CMD_WB_EXP_STATS_SEL_STATS_ENA 0x0001 +#define VFE_CMD_WB_EXP_STATS_SEL_PRI_FIXED 0x0000 +#define VFE_CMD_WB_EXP_STATS_SEL_PRI_VAR 0x0002 + +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_REG_8_8 0x0000 +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_REG_16_16 0x0001 +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_SREG_8_8 0x0000 +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_SREG_4_4 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int wb_exp_stats_sel; + unsigned int wb_exp_stats_cfg_part1; + unsigned int wb_exp_stats_cfg_part2; + unsigned int wb_exp_stats_cfg_part3; + unsigned int wb_exp_stats_cfg_part4; + unsigned int wb_exp_stats_op_buf_hdr; + unsigned int wb_exp_stats_op_buf[3]; +} __attribute__((packed)) vfe_cmd_stats_wb_exp_cfg; + + +/* + * Command to program histogram(hg) stats module + */ + +#define VFE_CMD_STATS_HG_CFG 0x0012 +#define VFE_CMD_STATS_HG_CFG_LEN \ + sizeof(vfe_cmd_stats_hg_cfg) + +#define VFE_CMD_HG_STATS_SEL_PRI_FIXED 0x0000 +#define VFE_CMD_HG_STATS_SEL_PRI_VAR 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int hg_stats_sel; + unsigned int hg_stats_cfg_part1; + unsigned int hg_stats_cfg_part2; + unsigned int hg_stats_op_buf_hdr; + unsigned int hg_stats_op_buf; +} __attribute__((packed)) vfe_cmd_stats_hg_cfg; + + +/* + * Command to acknowledge last MSG_VFE_OP1 message + */ + +#define VFE_CMD_OP1_ACK 0x0013 +#define VFE_CMD_OP1_ACK_LEN sizeof(vfe_cmd_op1_ack) + +typedef struct { + unsigned int cmd_id; + unsigned int op1_buf_y_addr; + unsigned int op1_buf_cbcr_addr; +} __attribute__((packed)) vfe_cmd_op1_ack; + + + +/* + * Command to acknowledge last MSG_VFE_OP2 message + */ + +#define VFE_CMD_OP2_ACK 0x0014 +#define VFE_CMD_OP2_ACK_LEN sizeof(vfe_cmd_op2_ack) + +typedef struct { + unsigned int cmd_id; + unsigned int op2_buf_y_addr; + unsigned int op2_buf_cbcr_addr; +} __attribute__((packed)) vfe_cmd_op2_ack; + + + +/* + * Command to acknowledge MSG_VFE_STATS_AUTOFOCUS msg + */ + +#define VFE_CMD_STATS_AF_ACK 0x0015 +#define VFE_CMD_STATS_AF_ACK_LEN sizeof(vfe_cmd_stats_af_ack) + + +typedef struct { + unsigned int cmd_id; + unsigned int af_stats_op_buf; +} __attribute__((packed)) vfe_cmd_stats_af_ack; + + +/* + * Command to acknowledge MSG_VFE_STATS_WB_EXP msg + */ + +#define VFE_CMD_STATS_WB_EXP_ACK 0x0016 +#define VFE_CMD_STATS_WB_EXP_ACK_LEN sizeof(vfe_cmd_stats_wb_exp_ack) + +typedef struct { + unsigned int cmd_id; + unsigned int wb_exp_stats_op_buf; +} __attribute__((packed)) vfe_cmd_stats_wb_exp_ack; + + +/* + * Command to acknowledge MSG_VFE_EPOCH1 message + */ + +#define VFE_CMD_EPOCH1_ACK 0x0017 +#define VFE_CMD_EPOCH1_ACK_LEN sizeof(vfe_cmd_epoch1_ack) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_epoch1_ack; + + +/* + * Command to acknowledge MSG_VFE_EPOCH2 message + */ + +#define VFE_CMD_EPOCH2_ACK 0x0018 +#define VFE_CMD_EPOCH2_ACK_LEN sizeof(vfe_cmd_epoch2_ack) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_epoch2_ack; + + + +/* + * Command to configure, enable or disable synchronous timer1 + */ + +#define VFE_CMD_SYNC_TIMER1_CFG 0x0019 +#define VFE_CMD_SYNC_TIMER1_CFG_LEN \ + sizeof(vfe_cmd_sync_timer1_cfg) + +#define VFE_CMD_SYNC_T1_CFG_PART1_TIMER_DIS 0x0000 +#define VFE_CMD_SYNC_T1_CFG_PART1_TIMER_ENA 0x0001 +#define VFE_CMD_SYNC_T1_CFG_PART1_POL_HIGH 0x0000 +#define VFE_CMD_SYNC_T1_CFG_PART1_POL_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int sync_t1_cfg_part1; + unsigned int sync_t1_h_sync_countdown; + unsigned int sync_t1_pclk_countdown; + unsigned int sync_t1_duration; +} __attribute__((packed)) vfe_cmd_sync_timer1_cfg; + + +/* + * Command to configure, enable or disable synchronous timer1 + */ + +#define VFE_CMD_SYNC_TIMER2_CFG 0x001A +#define VFE_CMD_SYNC_TIMER2_CFG_LEN \ + sizeof(vfe_cmd_sync_timer2_cfg) + +#define VFE_CMD_SYNC_T2_CFG_PART1_TIMER_DIS 0x0000 +#define VFE_CMD_SYNC_T2_CFG_PART1_TIMER_ENA 0x0001 +#define VFE_CMD_SYNC_T2_CFG_PART1_POL_HIGH 0x0000 +#define VFE_CMD_SYNC_T2_CFG_PART1_POL_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int sync_t2_cfg_part1; + unsigned int sync_t2_h_sync_countdown; + unsigned int sync_t2_pclk_countdown; + unsigned int sync_t2_duration; +} __attribute__((packed)) vfe_cmd_sync_timer2_cfg; + + +/* + * Command to configure and start asynchronous timer1 + */ + +#define VFE_CMD_ASYNC_TIMER1_START 0x001B +#define VFE_CMD_ASYNC_TIMER1_START_LEN \ + sizeof(vfe_cmd_async_timer1_start) + +#define VFE_CMD_ASYNC_T1_POLARITY_A_HIGH 0x0000 +#define VFE_CMD_ASYNC_T1_POLARITY_A_LOW 0x0001 +#define VFE_CMD_ASYNC_T1_POLARITY_B_HIGH 0x0000 +#define VFE_CMD_ASYNC_T1_POLARITY_B_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int async_t1a_cfg; + unsigned int async_t1b_cfg; + unsigned int async_t1_polarity; +} __attribute__((packed)) vfe_cmd_async_timer1_start; + + +/* + * Command to configure and start asynchronous timer2 + */ + +#define VFE_CMD_ASYNC_TIMER2_START 0x001C +#define VFE_CMD_ASYNC_TIMER2_START_LEN \ + sizeof(vfe_cmd_async_timer2_start) + +#define VFE_CMD_ASYNC_T2_POLARITY_A_HIGH 0x0000 +#define VFE_CMD_ASYNC_T2_POLARITY_A_LOW 0x0001 +#define VFE_CMD_ASYNC_T2_POLARITY_B_HIGH 0x0000 +#define VFE_CMD_ASYNC_T2_POLARITY_B_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int async_t2a_cfg; + unsigned int async_t2b_cfg; + unsigned int async_t2_polarity; +} __attribute__((packed)) vfe_cmd_async_timer2_start; + + +/* + * Command to program partial configurations of auto focus(af) + */ + +#define VFE_CMD_STATS_AF_UPDATE 0x001D +#define VFE_CMD_STATS_AF_UPDATE_LEN \ + sizeof(vfe_cmd_stats_af_update) + +#define VFE_CMD_AF_UPDATE_PART1_WINDOW_ONE 0x00000000 +#define VFE_CMD_AF_UPDATE_PART1_WINDOW_MULTI 0x80000000 + +typedef struct { + unsigned int cmd_id; + unsigned int af_update_part1; + unsigned int af_update_part2; +} __attribute__((packed)) vfe_cmd_stats_af_update; + + +/* + * Command to program partial cfg of wb and exp + */ + +#define VFE_CMD_STATS_WB_EXP_UPDATE 0x001E +#define VFE_CMD_STATS_WB_EXP_UPDATE_LEN \ + sizeof(vfe_cmd_stats_wb_exp_update) + +#define VFE_CMD_WB_EXP_UPDATE_PART1_REGIONS_8_8 0x0000 +#define VFE_CMD_WB_EXP_UPDATE_PART1_REGIONS_16_16 0x0001 +#define VFE_CMD_WB_EXP_UPDATE_PART1_SREGIONS_8_8 0x0000 +#define VFE_CMD_WB_EXP_UPDATE_PART1_SREGIONS_4_4 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int wb_exp_update_part1; + unsigned int wb_exp_update_part2; + unsigned int wb_exp_update_part3; + unsigned int wb_exp_update_part4; +} __attribute__((packed)) vfe_cmd_stats_wb_exp_update; + + + +/* + * Command to re program the CAMIF FRAME CONFIG settings + */ + +#define VFE_CMD_UPDATE_CAMIF_FRAME_CFG 0x001F +#define VFE_CMD_UPDATE_CAMIF_FRAME_CFG_LEN \ + sizeof(vfe_cmd_update_camif_frame_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int camif_frame_cfg; +} __attribute__((packed)) vfe_cmd_update_camif_frame_cfg; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h new file mode 100644 index 00000000000..a628f922b2c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h @@ -0,0 +1,290 @@ +#ifndef QDSP5VFEMSGI_H +#define QDSP5VFEMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V F E I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are sent by VFE Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfemsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + + +/* + * Message to acknowledge CMD_VFE_REST command + */ + +#define VFE_MSG_RESET_ACK 0x0000 +#define VFE_MSG_RESET_ACK_LEN sizeof(vfe_msg_reset_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_reset_ack; + + +/* + * Message to acknowledge CMD_VFE_START command + */ + +#define VFE_MSG_START_ACK 0x0001 +#define VFE_MSG_START_ACK_LEN sizeof(vfe_msg_start_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_start_ack; + +/* + * Message to acknowledge CMD_VFE_STOP command + */ + +#define VFE_MSG_STOP_ACK 0x0002 +#define VFE_MSG_STOP_ACK_LEN sizeof(vfe_msg_stop_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_stop_ack; + + +/* + * Message to acknowledge CMD_VFE_UPDATE command + */ + +#define VFE_MSG_UPDATE_ACK 0x0003 +#define VFE_MSG_UPDATE_ACK_LEN sizeof(vfe_msg_update_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_update_ack; + + +/* + * Message to notify the ARM that snapshot processing is complete + * and that the VFE is now STATE_VFE_IDLE + */ + +#define VFE_MSG_SNAPSHOT_DONE 0x0004 +#define VFE_MSG_SNAPSHOT_DONE_LEN \ + sizeof(vfe_msg_snapshot_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_snapshot_done; + + + +/* + * Message to notify ARM that illegal cmd was received and + * system is in the IDLE state + */ + +#define VFE_MSG_ILLEGAL_CMD 0x0005 +#define VFE_MSG_ILLEGAL_CMD_LEN \ + sizeof(vfe_msg_illegal_cmd) + +typedef struct { + unsigned int status; +} __attribute__((packed)) vfe_msg_illegal_cmd; + + +/* + * Message to notify ARM that op1 buf is full and ready + */ + +#define VFE_MSG_OP1 0x0006 +#define VFE_MSG_OP1_LEN sizeof(vfe_msg_op1) + +typedef struct { + unsigned int op1_buf_y_addr; + unsigned int op1_buf_cbcr_addr; + unsigned int black_level_even_col; + unsigned int black_level_odd_col; + unsigned int defect_pixels_detected; + unsigned int asf_max_edge; +} __attribute__((packed)) vfe_msg_op1; + + +/* + * Message to notify ARM that op2 buf is full and ready + */ + +#define VFE_MSG_OP2 0x0007 +#define VFE_MSG_OP2_LEN sizeof(vfe_msg_op2) + +typedef struct { + unsigned int op2_buf_y_addr; + unsigned int op2_buf_cbcr_addr; + unsigned int black_level_even_col; + unsigned int black_level_odd_col; + unsigned int defect_pixels_detected; + unsigned int asf_max_edge; +} __attribute__((packed)) vfe_msg_op2; + + +/* + * Message to notify ARM that autofocus(af) stats are ready + */ + +#define VFE_MSG_STATS_AF 0x0008 +#define VFE_MSG_STATS_AF_LEN sizeof(vfe_msg_stats_af) + +typedef struct { + unsigned int af_stats_op_buffer; +} __attribute__((packed)) vfe_msg_stats_af; + + +/* + * Message to notify ARM that white balance(wb) and exposure (exp) + * stats are ready + */ + +#define VFE_MSG_STATS_WB_EXP 0x0009 +#define VFE_MSG_STATS_WB_EXP_LEN \ + sizeof(vfe_msg_stats_wb_exp) + +typedef struct { + unsigned int wb_exp_stats_op_buf; +} __attribute__((packed)) vfe_msg_stats_wb_exp; + + +/* + * Message to notify the ARM that histogram(hg) stats are ready + */ + +#define VFE_MSG_STATS_HG 0x000A +#define VFE_MSG_STATS_HG_LEN sizeof(vfe_msg_stats_hg) + +typedef struct { + unsigned int hg_stats_op_buf; +} __attribute__((packed)) vfe_msg_stats_hg; + + +/* + * Message to notify the ARM that epoch1 event occurred in the CAMIF + */ + +#define VFE_MSG_EPOCH1 0x000B +#define VFE_MSG_EPOCH1_LEN sizeof(vfe_msg_epoch1) + +typedef struct { +} __attribute__((packed)) vfe_msg_epoch1; + + +/* + * Message to notify the ARM that epoch2 event occurred in the CAMIF + */ + +#define VFE_MSG_EPOCH2 0x000C +#define VFE_MSG_EPOCH2_LEN sizeof(vfe_msg_epoch2) + +typedef struct { +} __attribute__((packed)) vfe_msg_epoch2; + + +/* + * Message to notify the ARM that sync timer1 op is completed + */ + +#define VFE_MSG_SYNC_T1_DONE 0x000D +#define VFE_MSG_SYNC_T1_DONE_LEN sizeof(vfe_msg_sync_t1_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_sync_t1_done; + + +/* + * Message to notify the ARM that sync timer2 op is completed + */ + +#define VFE_MSG_SYNC_T2_DONE 0x000E +#define VFE_MSG_SYNC_T2_DONE_LEN sizeof(vfe_msg_sync_t2_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_sync_t2_done; + + +/* + * Message to notify the ARM that async t1 operation completed + */ + +#define VFE_MSG_ASYNC_T1_DONE 0x000F +#define VFE_MSG_ASYNC_T1_DONE_LEN sizeof(vfe_msg_async_t1_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_async_t1_done; + + + +/* + * Message to notify the ARM that async t2 operation completed + */ + +#define VFE_MSG_ASYNC_T2_DONE 0x0010 +#define VFE_MSG_ASYNC_T2_DONE_LEN sizeof(vfe_msg_async_t2_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_async_t2_done; + + + +/* + * Message to notify the ARM that an error has occurred + */ + +#define VFE_MSG_ERROR 0x0011 +#define VFE_MSG_ERROR_LEN sizeof(vfe_msg_error) + +#define VFE_MSG_ERR_COND_NO_CAMIF_ERR 0x0000 +#define VFE_MSG_ERR_COND_CAMIF_ERR 0x0001 +#define VFE_MSG_ERR_COND_OP1_Y_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP1_Y_BUS_OF 0x0002 +#define VFE_MSG_ERR_COND_OP1_CBCR_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP1_CBCR_BUS_OF 0x0004 +#define VFE_MSG_ERR_COND_OP2_Y_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP2_Y_BUS_OF 0x0008 +#define VFE_MSG_ERR_COND_OP2_CBCR_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP2_CBCR_BUS_OF 0x0010 +#define VFE_MSG_ERR_COND_AF_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_AF_BUS_OF 0x0020 +#define VFE_MSG_ERR_COND_WB_EXP_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_WB_EXP_BUS_OF 0x0040 +#define VFE_MSG_ERR_COND_NO_AXI_ERR 0x0000 +#define VFE_MSG_ERR_COND_AXI_ERR 0x0080 + +#define VFE_MSG_CAMIF_STS_IDLE 0x0000 +#define VFE_MSG_CAMIF_STS_CAPTURE_DATA 0x0001 + +typedef struct { + unsigned int err_cond; + unsigned int camif_sts; +} __attribute__((packed)) vfe_msg_error; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/snd_adie.h b/arch/arm/mach-msm/include/mach/qdsp5/snd_adie.h new file mode 100644 index 00000000000..bf1714e0086 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/snd_adie.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SND_ADIE_SVC_H_ +#define __SND_ADIE_SVC_H_ + +#define ADIE_SVC_PROG 0x30000002 +#define ADIE_SVC_VERS 0x00020003 + +#define ADIE_SVC_CLIENT_STATUS_FUNC_PTR_TYPE_PROC 0xFFFFFF01 +#define SND_ADIE_SVC_CLIENT_REGISTER_PROC 34 +#define SND_ADIE_SVC_CONFIG_ADIE_BLOCK_PROC 35 +#define SND_ADIE_SVC_CLIENT_DEREGISTER_PROC 36 + +#define ADIE_SVC_MAX_CLIENTS 5 + +enum adie_svc_client_operation{ + ADIE_SVC_REGISTER_CLIENT, + ADIE_SVC_DEREGISTER_CLIENT, + ADIE_SVC_CONFIG_ADIE_BLOCK, +}; + +enum adie_svc_status_type{ + ADIE_SVC_STATUS_SUCCESS, + ADIE_SVC_STATUS_FAILURE, + ADIE_SVC_STATUS_INUSE +}; + +enum adie_block_enum_type{ + MIC_BIAS, + HSSD, + HPH_PA +}; + +enum adie_config_enum_type{ + DISABLE, + ENABLE +}; + +struct adie_svc_client{ + int client_id; + int cb_id; + enum adie_svc_status_type status; + bool adie_svc_cb_done; + struct mutex lock; + wait_queue_head_t wq; + struct msm_rpc_client *rpc_client; +}; + +struct adie_svc_client_register_cb_cb_args { + int cb_id; + uint32_t size; + int client_id; + enum adie_block_enum_type adie_block; + enum adie_svc_status_type status; + enum adie_svc_client_operation client_operation; +}; + +struct adie_svc_client_register_cb_args { + int cb_id; +}; + +struct adie_svc_client_deregister_cb_args { + int client_id; +}; + +struct adie_svc_config_adie_block_cb_args { + int client_id; + enum adie_block_enum_type adie_block; + enum adie_config_enum_type config; +}; + +int adie_svc_get(void); +int adie_svc_put(int id); +int adie_svc_config_adie_block(int id, + enum adie_block_enum_type adie_block_type, bool enable); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/acdb_commands.h b/arch/arm/mach-msm/include/mach/qdsp5v2/acdb_commands.h new file mode 100644 index 00000000000..2e6fcdb0aee --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/acdb_commands.h @@ -0,0 +1,303 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_ACDB_COMMANDS_H +#define _MACH_QDSP5_V2_ACDB_COMMANDS_H + +#define ACDB_VOICE_NETWORK_ID_DEFAULT 0x00010037 +#define ACDB_INITIALISING 0 +#define ACDB_READY 1 + + +/* 4KB */ +#define ACDB_PAGE_SIZE 0x1000 + +#define ACDB_CDMA_NB 0x0108b153 +#define ACDB_CDMA_WB 0x0108b154 +#define ACDB_GSM_NB 0x0108b155 +#define ACDB_GSM_WB 0x0108b156 +#define ACDB_WCDMA_NB 0x0108b157 +#define ACDB_WCDMA_WB 0x0108b158 + + +/* ACDB commands */ + + +/* struct acdb_cmd_install_device */ +#define ACDB_INSTALL_DEVICE 0x0108d245 + +/* struct acdb_cmd_install_device */ +#define ACDB_UNINSTALL_DEVICE 0x0108d246 + +/* struct acdb_cmd_device */ +#define ACDB_GET_DEVICE 0x0108bb92 + +/* struct acdb_cmd_device */ +#define ACDB_SET_DEVICE 0x0108bb93 + +/* struct acdb_cmd_get_device_table */ +#define ACDB_GET_DEVICE_TABLE 0x0108bb97 + +/* struct acdb_cmd_get_device_capabilities */ +#define ACDB_GET_DEVICE_CAPABILITIES 0x0108f5ca + +/* struct acdb_cmd_get_device_info */ +#define ACDB_GET_DEVICE_INFO 0x0108f5cb + +/*command to intitialize ACDB based on codec type*/ +#define ACDB_CMD_INITIALIZE_FOR_ADIE 0x00011283 + + +/* ACDB Error codes */ + +#define ACDB_RES_SUCCESS 0 +#define ACDB_RES_FAILURE -1 +#define ACDB_RES_BADPARM -2 +#define ACDB_RES_BADSTATE -3 + +#define TGTVERS_MSM7x30_BRING_UP 0x00010064 + + + +/* Algorithm Aspect IDs */ + +#define IID_ENABLE_FLAG 0x0108b6b9 + + +#define IID_ENABLE_FLAG_SIZE 1 +#define IID_ECHO_CANCELLER_VERSION_SIZE 2 +#define IID_ECHO_CANCELLER_MODE_SIZE 2 +#define IID_ECHO_CANCELLER_NOISE_SUPPRESSOR_ENABLE_SIZE 1 +#define IID_ECHO_CANCELLER_PARAMETERS_SIZE 32 +#define IID_ECHO_CANCELLER_NEXTGEN_NB_PARAMETERS_SIZE (38 * 2) +#define IID_ECHO_CANCELLER_NEXTGEN_WB_PARAMETERS_SIZE (38 * 2) +#define IID_FLUENCE_PARAMETERS_SIZE 486 +#define IID_AFE_VOLUME_CONTROL_SIZE 6 +#define IID_GAIN_SIZE 2 +#define IID_VOICE_FIR_FILTER_SIZE 14 +#define IID_VOICE_IIR_FILTER_SIZE 114 +#define IID_RX_DBM_OFFSET_SIZE 2 +#define IID_AGC_SIZE 36 +#define IID_AVC_SIZE 80 + +#define IID_AUDIO_IIR_COEFF_SIZE 100 +#define IID_MBADRC_PARAMETERS_SIZE 8 +#define IID_MBADRC_EXT_BUFF_SIZE 392 +#define IID_MBADRC_BAND_CONFIG_SIZE 100 +#define IID_QAFX_PARAMETERS_SIZE 2 +#define IID_QCONCERT_PARAMETERS_SIZE 2 +#define IID_AUDIO_AGC_PARAMETERS_SIZE 42 +#define IID_NS_PARAMETERS_SIZE 14 + +#define IID_ECHO_CANCELLER_VERSION 0x00010042 +#define IID_ECHO_CANCELLER_MODE 0x00010043 +#define IID_ECHO_CANCELLER_NOISE_SUPPRESSOR_ENABLE 0x00010044 +#define IID_ECHO_CANCELLER_PARAMETERS 0x00010045 +#define IID_ECHO_CANCELLER_NEXTGEN_NB_PARAMETERS 0x00010046 +#define IID_ECHO_CANCELLER_NEXTGEN_WB_PARAMETERS 0x00010047 +#define IID_FLUENCE_PARAMETERS 0x00010048 +#define IID_AFE_VOLUME_CONTROL 0x00010049 +#define IID_GAIN 0x0001004A +#define IID_VOICE_FIR_FILTER 0x0001004B +#define IID_VOICE_IIR_FILTER 0x0001004C +#define IID_AGC 0x0001004E +#define IID_AVC 0x0001004F +#define ABID_SIDETONE_GAIN 0x00010050 +#define ABID_TX_VOICE_GAIN 0x00010051 +#define ABID_TX_DTMF_GAIN 0x00010052 +#define ABID_CODEC_TX_GAIN 0x00010053 +#define ABID_HSSD 0x00010054 +#define ABID_TX_AGC 0x00010055 +#define ABID_TX_VOICE_FIR 0x00010056 +#define ABID_TX_VOICE_IIR 0x00010057 +#define ABID_ECHO_CANCELLER 0x00010058 +#define ABID_ECHO_CANCELLER_NB_LVHF 0x00010059 +#define ABID_ECHO_CANCELLER_WB_LVHF 0x0001005A +#define ABID_FLUENCE 0x0001005B +#define ABID_CODEC_RX_GAIN 0x0001005C +#define ABID_RX_DBM_OFFSET 0x0001005D +#define ABID_RX_AGC 0x0001005E +#define ABID_AVC 0x0001005F +#define ABID_RX_VOICE_FIR 0x00010060 +#define ABID_RX_VOICE_IIR 0x00010061 +#define ABID_AFE_VOL_CTRL 0x00010067 + + +/* AUDIO IDs */ +#define ABID_AUDIO_AGC_TX 0x00010068 +#define ABID_AUDIO_NS_TX 0x00010069 +#define ABID_VOICE_NS 0x0001006A +#define ABID_AUDIO_IIR_TX 0x0001006B +#define ABID_AUDIO_IIR_RX 0x0001006C +#define ABID_AUDIO_MBADRC_RX 0x0001006E +#define ABID_AUDIO_QAFX_RX 0x0001006F +#define ABID_AUDIO_QCONCERT_RX 0x00010070 +#define ABID_AUDIO_STF_RX 0x00010071 +#define ABID_AUDIO_CALIBRATION_GAIN_RX 0x00011162 +#define ABID_AUDIO_CALIBRATION_GAIN_TX 0x00011149 +#define ABID_AUDIO_PBE_RX 0x00011197 +#define ABID_AUDIO_RMC_TX 0x00011226 +#define ABID_AUDIO_FLUENCE_TX 0x00011244 + + +#define IID_AUDIO_AGC_PARAMETERS 0x0001007E +#define IID_NS_PARAMETERS 0x00010072 +#define IID_AUDIO_IIR_COEFF 0x00010073 +#define IID_MBADRC_EXT_BUFF 0x00010075 +#define IID_MBADRC_BAND_CONFIG 0x00010076 +#define IID_MBADRC_PARAMETERS 0x00010077 +#define IID_QAFX_PARAMETERS 0x00010079 +#define IID_QCONCERT_PARAMETERS 0x0001007A +#define IID_STF_COEFF 0x0001007B +#define IID_AUDIO_CALIBRATION_GAIN_RX 0x00011163 +#define IID_AUDIO_CALIBRATION_GAIN_TX 0x00011171 +#define IID_PBE_CONFIG_PARAMETERS 0x00011198 +#define IID_AUDIO_PBE_RX_ENABLE_FLAG 0x00011199 +#define IID_AUDIO_RMC_PARAM 0x00011227 +#define IID_AUDIO_FLUENCE_TX 0x00011245 + + +#define TOPID_RX_TOPOLOGY_1 0x00010062 +#define TOPID_TX_TOPOLOGY_1 0x00010063 +#define AFERID_INT_SINK 0x00010065 +#define AFERID_INT_SOURCE 0x00010066 +#define AFERID_NO_SINK 0x00000000 +#define AFERID_NULL_SINK 0x0108ea92 + + +struct acdb_cmd_install_device { + u32 command_id; + u32 device_id; + u32 topology_id; + u32 afe_routing_id; + u32 cad_routing_id; /* see "Sample Rate Bit Mask" below */ + u32 sample_rate_mask; + + /* represents device direction: Tx, Rx (aux pga - loopback) */ + u8 device_type; + u8 channel_config; /* Mono or Stereo */ + u32 adie_codec_path_id; +}; + + +struct acdb_cmd_get_device_capabilities { + u32 command_id; + u32 total_bytes; /* Length in bytes allocated for buffer */ + u32 *phys_buf; /* Physical Address of data */ +}; + + +struct acdb_cmd_get_device_info { + u32 command_id; + u32 device_id; + u32 total_bytes; /* Length in bytes allocated for buffer */ + u32 *phys_buf; /* Physical Address of data */ +}; + +struct acdb_cmd_device { + u32 command_id; + u32 device_id; + u32 network_id; + u32 sample_rate_id; /* Actual sample rate value */ + u32 interface_id; /* See interface id's above */ + u32 algorithm_block_id; /* See enumerations above */ + u32 total_bytes; /* Length in bytes used by buffer */ + u32 *phys_buf; /* Physical Address of data */ +}; + +struct acdb_cmd_get_device_table { + u32 command_id; + u32 device_id; + u32 network_id; + u32 sample_rate_id; /* Actual sample rate value */ + u32 total_bytes; /* Length in bytes used by buffer */ + u32 *phys_buf; /* Physical Address of data */ +}; + +struct acdb_result { + /* This field is populated in response to the */ + /* ACDB_GET_DEVICE_CAPABILITIES command and indicates the total */ + /* devices whose capabilities are copied to the physical memory. */ + u32 total_devices; + u32 *buf; /* Physical Address of data */ + u32 used_bytes; /* The size in bytes of the data */ + u32 result; /* See ACDB Error codes above */ +}; + +struct acdb_device_capability { + u32 device_id; + u32 sample_rate_mask; /* See "Sample Rate Bit Mask" below */ +}; + +struct acdb_dev_info { + u32 cad_routing_id; + u32 sample_rate_mask; /* See "Sample Rate Bit Mask" below */ + u32 adsp_device_id; /* QDSP6 device ID */ + u32 device_type; /* Tx, Rx (aux pga - loopback) */ + u32 channel_config; /* Mono or Stereo */ + s32 min_volume; /* Min volume (mB) */ + s32 max_volume; /* Max volume (mB) */ +}; + +/*structure is used to intialize ACDB software on modem +based on adie type detected*/ +struct acdb_cmd_init_adie { + u32 command_id; + u32 adie_type; +}; + +#define ACDB_CURRENT_ADIE_MODE_UNKNOWN 0 +#define ACDB_CURRENT_ADIE_MODE_TIMPANI 1 +#define ACDB_CURRENT_ADIE_MODE_MARIMBA 2 + +/* Sample Rate Bit Mask */ + +/* AUX PGA devices will have a sample rate mask of 0xFFFFFFFF */ +/* 8kHz 0x00000001 */ +/* 11.025kHz 0x00000002 */ +/* 12kHz 0x00000004 */ +/* 16kHz 0x00000008 */ +/* 22.5kHz 0x00000010 */ +/* 24kHz 0x00000020 */ +/* 32kHz 0x00000040 */ +/* 44.1kHz 0x00000080 */ +/* 48kHz 0x00000100 */ + + +/* Device type enumeration */ +enum { + RX_DEVICE = 1, + TX_DEVICE, + AUXPGA_DEVICE, + DEVICE_TYPE_MAX +}; + +#ifdef CONFIG_DEBUG_FS +/*These are ABID used for RTC*/ +#define ABID_AUDIO_RTC_MBADRC_RX 0x0001118A +#define ABID_AUDIO_RTC_VOLUME_PAN_RX 0x0001118C +#define ABID_AUDIO_RTC_SPA 0x0001118E +#define ABID_AUDIO_RTC_EQUALIZER_PARAMETERS 0x0001119F + +/*These are IID used for RTC*/ +#define IID_AUDIO_RTC_MBADRC_PARAMETERS 0x0001118B +#define IID_AUDIO_RTC_VOLUME_PAN_PARAMETERS 0x0001118D +#define IID_AUDIO_RTC_SPA_PARAMETERS 0x0001118F +#define IID_AUDIO_RTC_EQUALIZER_PARAMETERS 0x0001119E +#define IID_AUDIO_RTC_AGC_PARAMETERS 0x000111A7 +#define IID_AUDIO_RTC_TX_IIR_COEFF 0x000111A8 + +#endif + + +#endif + diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/adie_marimba.h b/arch/arm/mach-msm/include/mach/qdsp5v2/adie_marimba.h new file mode 100644 index 00000000000..919da65d23e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/adie_marimba.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_ADIE_MARIMBA_H +#define __MACH_QDSP5_V2_ADIE_MARIMBA_H + +#include + +/* Value Represents a entry */ +#define ADIE_CODEC_ACTION_ENTRY 0x1 +/* Value representing a delay wait */ +#define ADIE_CODEC_ACTION_DELAY_WAIT 0x2 +/* Value representing a stage reached */ +#define ADIE_CODEC_ACTION_STAGE_REACHED 0x3 + +/* This value is the state after the client sets the path */ +#define ADIE_CODEC_PATH_OFF 0x0050 + +/* State to which client asks the drv to proceed to where it can + * set up the clocks and 0-fill PCM buffers + */ +#define ADIE_CODEC_DIGITAL_READY 0x0100 + +/* State to which client asks the drv to proceed to where it can + * start sending data after internal steady state delay + */ +#define ADIE_CODEC_DIGITAL_ANALOG_READY 0x1000 + + +/* Client Asks adie to switch off the Analog portion of the + * the internal codec. After the use of this path + */ +#define ADIE_CODEC_ANALOG_OFF 0x0750 + + +/* Client Asks adie to switch off the digital portion of the + * the internal codec. After switching off the analog portion. + * + * 0-fill PCM may or maynot be sent at this point + * + */ +#define ADIE_CODEC_DIGITAL_OFF 0x0600 + +/* State to which client asks the drv to write the default values + * to the registers */ +#define ADIE_CODEC_FLASH_IMAGE 0x0001 + +/* Path type */ +#define ADIE_CODEC_RX 0 +#define ADIE_CODEC_TX 1 +#define ADIE_CODEC_LB 3 +#define ADIE_CODEC_MAX 4 + +#define ADIE_CODEC_PACK_ENTRY(reg, mask, val) ((val)|(mask << 8)|(reg << 16)) + +#define ADIE_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0); + +struct adie_codec_action_unit { + u32 type; + u32 action; +}; + +struct adie_codec_hwsetting_entry{ + struct adie_codec_action_unit *actions; + u32 action_sz; + u32 freq_plan; + u32 osr; + /* u32 VolMask; + * u32 SidetoneMask; + */ +}; + +struct adie_codec_dev_profile { + u32 path_type; /* RX or TX */ + u32 setting_sz; + struct adie_codec_hwsetting_entry *settings; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/afe.h b/arch/arm/mach-msm/include/mach/qdsp5v2/afe.h new file mode 100644 index 00000000000..c15faccf571 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/afe.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_AFE_H +#define _MACH_QDSP5_V2_AFE_H + +#include +#include + +#define AFE_HW_PATH_CODEC_RX 1 +#define AFE_HW_PATH_CODEC_TX 2 +#define AFE_HW_PATH_AUXPCM_RX 3 +#define AFE_HW_PATH_AUXPCM_TX 4 +#define AFE_HW_PATH_MI2S_RX 5 +#define AFE_HW_PATH_MI2S_TX 6 + +#define AFE_VOLUME_UNITY 0x4000 /* Based on Q14 */ + +struct msm_afe_config { + u16 sample_rate; + u16 channel_mode; + u16 volume; + /* To be expaned for AUX CODEC */ +}; + +int afe_enable(u8 path_id, struct msm_afe_config *config); + +int afe_disable(u8 path_id); + +int afe_config_aux_codec(int pcm_ctl_value, int aux_codec_intf_value, + int data_format_pad); +int afe_config_fm_codec(int fm_enable, uint16_t source); + +int afe_config_fm_volume(uint16_t volume); +int afe_config_fm_calibration_gain(uint16_t device_id, + uint16_t calibration_gain); +void afe_loopback(int enable); +void afe_ext_loopback(int enable, int rx_copp_id, int tx_copp_id); + +void afe_device_volume_ctrl(u16 device_id, u16 device_volume); + +int afe_config_rmc_block(struct acdb_rmc_block *acdb_rmc); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audio_acdb_def.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_acdb_def.h new file mode 100644 index 00000000000..a2a15dcadec --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_acdb_def.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2010 - 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_AUDIO_ACDB_DEF_H +#define _MACH_QDSP5_V2_AUDIO_ACDB_DEF_H + +/* Define ACDB device ID */ +#define ACDB_ID_HANDSET_SPKR 1 +#define ACDB_ID_HANDSET_MIC 2 +#define ACDB_ID_HEADSET_MIC 3 +#define ACDB_ID_HEADSET_SPKR_MONO 4 +#define ACDB_ID_HEADSET_SPKR_STEREO 5 +#define ACDB_ID_SPKR_PHONE_MIC 6 +#define ACDB_ID_SPKR_PHONE_MONO 7 +#define ACDB_ID_SPKR_PHONE_STEREO 8 +#define ACDB_ID_BT_SCO_MIC 9 +#define ACDB_ID_BT_SCO_SPKR 0x0A +#define ACDB_ID_BT_A2DP_SPKR 0x0B +#define ACDB_ID_BT_A2DP_TX 0x10 +#define ACDB_ID_TTY_HEADSET_MIC 0x0C +#define ACDB_ID_TTY_HEADSET_SPKR 0x0D +#define ACDB_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 +#define ACDB_ID_FM_TX_LOOPBACK 0x17 +#define ACDB_ID_FM_TX 0x18 +#define ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX 0x19 +#define ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX 0x1A +#define ACDB_ID_I2S_RX 0x20 +#define ACDB_ID_SPKR_PHONE_MIC_BROADSIDE 0x2B +#define ACDB_ID_HANDSET_MIC_BROADSIDE 0x2C +#define ACDB_ID_SPKR_PHONE_MIC_ENDFIRE 0x2D +#define ACDB_ID_HANDSET_MIC_ENDFIRE 0x2E +#define ACDB_ID_I2S_TX 0x30 +#define ACDB_ID_HDMI 0x40 +#define ACDB_ID_FM_RX 0x4F +/*Replace the max device ID,if any new device is added Specific to RTC only*/ +#define ACDB_ID_MAX ACDB_ID_FM_RX + +/* ID used for virtual devices */ +#define PSEUDO_ACDB_ID 0xFFFF + +#endif /* _MACH_QDSP5_V2_AUDIO_ACDB_DEF_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audio_acdbi.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_acdbi.h new file mode 100644 index 00000000000..559073ce747 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_acdbi.h @@ -0,0 +1,303 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_AUDIO_ACDBI_H +#define _MACH_QDSP5_V2_AUDIO_ACDBI_H + +#define DBOR_SIGNATURE 0x524F4244 + +#ifdef CONFIG_DEBUG_FS +void acdb_rtc_set_err(u32 ErrCode); +#endif + + +struct header { + u32 dbor_signature; + u32 abid; + u32 iid; + u32 data_len; +}; + +enum { + ACDB_AGC_BLOCK = 197, + ACDB_IIR_BLOCK = 245, + ACDB_MBADRC_BLOCK = 343 +}; + +/* Structure to query for acdb parameter */ +struct acdb_get_block { + u32 acdb_id; + u32 sample_rate_id; /* Actual sample rate value */ + u32 interface_id; /* Interface id's */ + u32 algorithm_block_id; /* Algorithm block id */ + u32 total_bytes; /* Length in bytes used by buffer for + configuration */ + u32 *buf_ptr; /* Address for storing configuration + data */ +}; + +struct acdb_agc_block { + u16 enable_status; + u16 comp_rlink_static_gain; + u16 comp_rlink_aig_flag; + u16 exp_rlink_threshold; + u16 exp_rlink_slope; + u16 comp_rlink_threshold; + u16 comp_rlink_slope; + u16 comp_rlink_aig_attack_k; + u16 comp_rlink_aig_leak_down; + u16 comp_rlink_aig_leak_up; + u16 comp_rlink_aig_max; + u16 comp_rlink_aig_min; + u16 comp_rlink_aig_release_k; + u16 comp_rlink_aig_sm_leak_rate_fast; + u16 comp_rlink_aig_sm_leak_rate_slow; + u16 comp_rlink_attack_k_msw; + u16 comp_rlink_attack_k_lsw; + u16 comp_rlink_delay; + u16 comp_rlink_release_k_msw; + u16 comp_rlink_release_k_lsw; + u16 comp_rlink_rms_trav; +}; + + +struct iir_coeff_type { + u16 b0_lo; + u16 b0_hi; + u16 b1_lo; + u16 b1_hi; + u16 b2_lo; + u16 b2_hi; +}; + +struct iir_coeff_stage_a { + u16 a1_lo; + u16 a1_hi; + u16 a2_lo; + u16 a2_hi; +}; + +struct acdb_iir_block { + u16 enable_flag; + u16 stage_count; + struct iir_coeff_type stages[4]; + struct iir_coeff_stage_a stages_a[4]; + u16 shift_factor[4]; + u16 pan[4]; +}; + + + +struct mbadrc_band_config_type { + u16 mbadrc_sub_band_enable; + u16 mbadrc_sub_mute; + u16 mbadrc_comp_rms_tav; + u16 mbadrc_comp_threshold; + u16 mbadrc_comp_slop; + u16 mbadrc_comp_attack_msw; + u16 mbadrc_comp_attack_lsw; + u16 mbadrc_comp_release_msw; + u16 mbadrc_comp_release_lsw; + u16 mbadrc_make_up_gain; +}; + +struct mbadrc_parameter { + u16 mbadrc_enable; + u16 mbadrc_num_bands; + u16 mbadrc_down_sample_level; + u16 mbadrc_delay; +}; + +struct acdb_mbadrc_block { + u16 ext_buf[196]; + struct mbadrc_band_config_type band_config[5]; + struct mbadrc_parameter parameters; +}; + +struct acdb_calib_gain_rx { + u16 audppcalgain; + u16 reserved; +}; + +struct acdb_calib_gain_tx { + u16 audprecalgain; + u16 reserved; +}; + +struct acdb_pbe_block { + s16 realbassmix; + s16 basscolorcontrol; + u16 mainchaindelay; + u16 xoverfltorder; + u16 bandpassfltorder; + s16 adrcdelay; + u16 downsamplelevel; + u16 comprmstav; + s16 expthreshold; + u16 expslope; + u16 compthreshold; + u16 compslope; + u16 cpmpattack_lsw; + u16 compattack_msw; + u16 comprelease_lsw; + u16 comprelease_msw; + u16 compmakeupgain; + s16 baselimthreshold; + s16 highlimthreshold; + s16 basslimmakeupgain; + s16 highlimmakeupgain; + s16 limbassgrc; + s16 limhighgrc; + s16 limdelay; + u16 filter_coeffs[90]; +}; + +struct acdb_rmc_block { + s16 rmc_enable; + u16 rmc_ipw_length_ms; + u16 rmc_detect_start_threshdb; + u16 rmc_peak_length_ms; + s16 rmc_init_pulse_threshdb; + u16 rmc_init_pulse_length_ms; + u16 rmc_total_int_length_ms; + u16 rmc_rampupdn_length_ms; + u16 rmc_delay_length_ms; + u16 reserved00; + u16 reserved01; + s16 reserved02; + s16 reserved03; + s16 reserved04; +}; + +struct acdb_fluence_block { + u16 csmode; + u16 cs_tuningMode; + u16 cs_echo_path_delay_by_80; + u16 cs_echo_path_delay; + u16 af1_twoalpha; + u16 af1_erl; + u16 af1_taps; + u16 af1_preset_coefs; + u16 af1_offset; + u16 af2_twoalpha; + u16 af2_erl; + u16 af2_taps; + u16 af2_preset_coefs; + u16 af2_offset; + u16 pcd_twoalpha; + u16 pcd_offset; + u16 cspcd_threshold; + u16 wgthreshold; + u16 mpthreshold; + u16 sf_init_table_0[8]; + u16 sf_init_table_1[8]; + u16 sf_taps; + u16 sf_twoalpha; + u16 dnns_echoalpharev; + u16 dnns_echoycomp; + u16 dnns_wbthreshold; + u16 dnns_echogammahi; + u16 dnns_echogammalo; + u16 dnns_noisegammas; + u16 dnns_noisegamman; + u16 dnns_noisegainmins; + u16 dnns_noisegainminn; + u16 dnns_noisebiascomp; + u16 dnns_acthreshold; + u16 wb_echo_ratio_2mic; + u16 wb_gamma_e; + u16 wb_gamma_nn; + u16 wb_gamma_sn; + u16 vcodec_delay0; + u16 vcodec_delay1; + u16 vcodec_len0; + u16 vcodec_len1; + u16 vcodec_thr0; + u16 vcodec_thr1; + u16 fixcalfactorleft; + u16 fixcalfactorright; + u16 csoutputgain; + u16 enh_meu_1; + u16 enh_meu_2; + u16 fixed_over_est; + u16 rx_nlpp_limit; + u16 rx_nlpp_gain; + u16 wnd_threshold; + u16 wnd_ns_hover; + u16 wnd_pwr_smalpha; + u16 wnd_det_esmalpha; + u16 wnd_ns_egoffset; + u16 wnd_sm_ratio; + u16 wnd_det_coefs[5]; + u16 wnd_th1; + u16 wnd_th2; + u16 wnd_fq; + u16 wnd_dfc; + u16 wnd_sm_alphainc; + u16 wnd_sm_alphsdec; + u16 lvnv_spdet_far; + u16 lvnv_spdet_mic; + u16 lvnv_spdet_xclip; + u16 dnns_nl_atten; + u16 dnns_cni_level; + u16 dnns_echogammaalpha; + u16 dnns_echogammarescue; + u16 dnns_echogammadt; + u16 mf_noisegammafac; + u16 e_noisegammafac; + u16 dnns_noisegammainit; + u16 sm_noisegammas; + u16 wnd_noisegamman; + u16 af_taps_bg_spkr; + u16 af_erl_bg_spkr; + u16 minimum_erl_bg; + u16 erl_step_bg; + u16 upprisecalpha; + u16 upprisecthresh; + u16 uppriwindbias; + u16 e_pcd_threshold; + u16 nv_maxvadcount; + u16 crystalspeechreserved[38]; + u16 cs_speaker[7]; + u16 ns_fac; + u16 ns_blocksize; + u16 is_bias; + u16 is_bias_inp; + u16 sc_initb; + u16 ac_resetb; + u16 sc_avar; + u16 is_hover[5]; + u16 is_cf_level; + u16 is_cf_ina; + u16 is_cf_inb; + u16 is_cf_a; + u16 is_cf_b; + u16 sc_th; + u16 sc_pscale; + u16 sc_nc; + u16 sc_hover; + u16 sc_alphas; + u16 sc_cfac; + u16 sc_sdmax; + u16 sc_sdmin; + u16 sc_initl; + u16 sc_maxval; + u16 sc_spmin; + u16 is_ec_th; + u16 is_fx_dl; + u16 coeffs_iva_filt_0[32]; + u16 coeffs_iva_filt_1[32]; +}; + +s32 acdb_get_calibration_data(struct acdb_get_block *get_block); +void fluence_feature_update(int enable, int stream_id); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audio_def.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_def.h new file mode 100644 index 00000000000..236c6f68bdb --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_def.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2009,2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_AUDIO_DEF_H +#define _MACH_QDSP5_V2_AUDIO_DEF_H + +/* Define sound device capability */ +#define SNDDEV_CAP_RX 0x1 /* RX direction */ +#define SNDDEV_CAP_TX 0x2 /* TX direction */ +#define SNDDEV_CAP_VOICE 0x4 /* Support voice call */ +#define SNDDEV_CAP_PLAYBACK 0x8 /* Support playback */ +#define SNDDEV_CAP_FM 0x10 /* Support FM radio */ +#define SNDDEV_CAP_TTY 0x20 /* Support TTY */ +#define SNDDEV_CAP_ANC 0x40 /* Support ANC */ +#define SNDDEV_CAP_LB 0x80 /* Loopback */ +#define VOC_NB_INDEX 0 +#define VOC_WB_INDEX 1 +#define VOC_RX_VOL_ARRAY_NUM 2 + +/* Device volume types . In Current deisgn only one of these are supported. */ +#define SNDDEV_DEV_VOL_DIGITAL 0x1 /* Codec Digital volume control */ +#define SNDDEV_DEV_VOL_ANALOG 0x2 /* Codec Analog volume control */ + +#define SIDE_TONE_MASK 0x01 + +#endif /* _MACH_QDSP5_V2_AUDIO_DEF_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audio_dev_ctl.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_dev_ctl.h new file mode 100644 index 00000000000..7c0abcc97a9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_dev_ctl.h @@ -0,0 +1,206 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_SNDDEV_H +#define __MACH_QDSP5_V2_SNDDEV_H +#include + +#define AUDIO_DEV_CTL_MAX_DEV 64 +#define DIR_TX 2 +#define DIR_RX 1 + +#define DEVICE_IGNORE 0xff +#define SESSION_IGNORE 0x00000000 + +#define VOICE_STATE_INVALID 0x0 +#define VOICE_STATE_INCALL 0x1 +#define VOICE_STATE_OFFCALL 0x2 +#define MAX_COPP_NODE_SUPPORTED 6 +#define MAX_AUDREC_SESSIONS 3 + +#define REAL_STEREO_CHANNEL_MODE 9 + +struct msm_snddev_info { + const char *name; + u32 capability; + u32 copp_id; + u32 acdb_id; + u32 dev_volume; + struct msm_snddev_ops { + int (*open)(struct msm_snddev_info *); + int (*close)(struct msm_snddev_info *); + int (*set_freq)(struct msm_snddev_info *, u32); + int (*enable_sidetone)(struct msm_snddev_info *, u32); + int (*set_device_volume)(struct msm_snddev_info *, u32); + } dev_ops; + u8 opened; + void *private_data; + bool state; + u32 sample_rate; + u32 set_sample_rate; + u32 sessions; + int usage_count; + s32 max_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB,[1] for WB */ + s32 min_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; +}; + +struct msm_volume { + int volume; /* Volume parameter, in % Scale */ + int pan; +}; + +extern struct msm_volume msm_vol_ctl; + +int msm_get_dual_mic_config(int enc_session_id); +int msm_set_dual_mic_config(int enc_session_id, int config); +int msm_reset_all_device(void); +void msm_snddev_register(struct msm_snddev_info *); +void msm_snddev_unregister(struct msm_snddev_info *); +int msm_snddev_devcount(void); +int msm_snddev_query(int dev_id); +unsigned short msm_snddev_route_dec(int popp_id); +unsigned short msm_snddev_route_enc(int enc_id); +int msm_snddev_set_dec(int popp_id, int copp_id, int set); +int msm_snddev_set_enc(int popp_id, int copp_id, int set); +int msm_snddev_is_set(int popp_id, int copp_id); +int msm_get_voc_route(u32 *rx_id, u32 *tx_id); +int msm_set_voc_route(struct msm_snddev_info *dev_info, int stream_type, + int dev_id); +int msm_snddev_enable_sidetone(u32 dev_id, u32 enable); + +struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id); + +void msm_release_voc_thread(void); + +int snddev_voice_set_volume(int vol, int path); + +struct auddev_evt_voc_devinfo { + u32 dev_type; /* Rx or Tx */ + u32 acdb_dev_id; /* acdb id of device */ + u32 dev_sample; /* Sample rate of device */ + s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb (milibel), + [0] is for NB, other for WB */ + s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb */ + u32 dev_id; /* registered device id */ +}; + +struct auddev_evt_audcal_info { + u32 dev_id; + u32 acdb_id; + u32 sample_rate; + u32 dev_type; + u32 sessions; +}; + +struct auddev_evt_devinfo { + u32 dev_id; + u32 acdb_id; + u32 sample_rate; + u32 dev_type; + u32 sessions; +}; + +union msm_vol_mute { + int vol; + bool mute; +}; + +struct auddev_evt_voc_mute_info { + u32 dev_type; + u32 acdb_dev_id; + union msm_vol_mute dev_vm_val; +}; + +struct auddev_evt_freq_info { + u32 dev_type; + u32 acdb_dev_id; + u32 sample_rate; +}; + +union auddev_evt_data { + struct auddev_evt_voc_devinfo voc_devinfo; + struct auddev_evt_voc_mute_info voc_vm_info; + struct auddev_evt_freq_info freq_info; + u32 routing_id; + s32 session_vol; + s32 voice_state; + struct auddev_evt_audcal_info audcal_info; + struct auddev_evt_devinfo devinfo; +}; + +struct message_header { + uint32_t id; + uint32_t data_len; +}; + +#define AUDDEV_EVT_DEV_CHG_VOICE 0x01 /* device change event */ +#define AUDDEV_EVT_DEV_RDY 0x02 /* device ready event */ +#define AUDDEV_EVT_DEV_RLS 0x04 /* device released event */ +#define AUDDEV_EVT_REL_PENDING 0x08 /* device release pending */ +#define AUDDEV_EVT_DEVICE_VOL_MUTE_CHG 0x10 /* device volume changed */ +#define AUDDEV_EVT_START_VOICE 0x20 /* voice call start */ +#define AUDDEV_EVT_END_VOICE 0x40 /* voice call end */ +#define AUDDEV_EVT_STREAM_VOL_CHG 0x80 /* device volume changed */ +#define AUDDEV_EVT_FREQ_CHG 0x100 /* Change in freq */ +#define AUDDEV_EVT_VOICE_STATE_CHG 0x200 /* Change in voice state */ +#define AUDDEV_EVT_DEVICE_INFO 0x400 /* routed device information + event */ + +#define AUDDEV_CLNT_VOC 0x1 /* Vocoder clients */ +#define AUDDEV_CLNT_DEC 0x2 /* Decoder clients */ +#define AUDDEV_CLNT_ENC 0x3 /* Encoder clients */ +#define AUDDEV_CLNT_AUDIOCAL 0x4 /* AudioCalibration client */ + +#define AUDIO_DEV_CTL_MAX_LISTNER 20 /* Max Listeners Supported */ + +struct msm_snd_evt_listner { + uint32_t evt_id; + uint32_t clnt_type; + uint32_t clnt_id; + void *private_data; + void (*auddev_evt_listener)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data); + struct msm_snd_evt_listner *cb_next; + struct msm_snd_evt_listner *cb_prev; +}; + +struct event_listner { + struct msm_snd_evt_listner *cb; + u32 num_listner; + int state; /* Call state */ /* TODO remove this if not req*/ +}; + +extern struct event_listner event; +int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, + void (*listner)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data), + void *private_data); +int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id); +void mixer_post_event(u32 evt_id, u32 dev_id); +void broadcast_event(u32 evt_id, u32 dev_id, u32 session_id); +int msm_snddev_request_freq(int *freq, u32 session_id, + u32 capability, u32 clnt_type); +int msm_snddev_withdraw_freq(u32 session_id, + u32 capability, u32 clnt_type); +int msm_device_is_voice(int dev_id); +int msm_get_voc_freq(int *tx_freq, int *rx_freq); +int msm_snddev_get_enc_freq(int session_id); +int msm_set_voice_vol(int dir, s32 volume); +int msm_set_voice_mute(int dir, int mute); +int msm_get_voice_state(void); +#ifdef CONFIG_DEBUG_FS +bool is_dev_opened(u32 acdb_id); +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audio_interct.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_interct.h new file mode 100644 index 00000000000..2a7b89ec221 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audio_interct.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_AUDIO_INTERCT_H +#define __MACH_QDSP5_V2_AUDIO_INTERCT_H + +#define AUDIO_INTERCT_ADSP 0 +#define AUDIO_INTERCT_LPA 1 +#define AUDIO_ADSP_A 1 +#define AUDIO_ADSP_V 0 + +void audio_interct_lpa(u32 source); +void audio_interct_aux_regsel(u32 source); +void audio_interct_rpcm_source(u32 source); +void audio_interct_tpcm_source(u32 source); +void audio_interct_pcmmi2s(u32 source); +void audio_interct_codec(u32 source); +void audio_interct_multichannel(u32 source); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audpp.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audpp.h new file mode 100644 index 00000000000..bdec256e2b1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audpp.h @@ -0,0 +1,129 @@ +/*arch/arm/mach-msm/qdsp5iv2/audpp.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#ifndef _MACH_QDSP5_V2_AUDPP_H +#define _MACH_QDSP5_V2_AUDPP_H + +#include + +typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg); + +/* worst case delay of 1sec for response */ +#define MSM_AUD_DECODER_WAIT_MS 1000 +#define MSM_AUD_MODE_TUNNEL 0x00000100 +#define MSM_AUD_MODE_NONTUNNEL 0x00000200 +#define MSM_AUD_MODE_LP 0x00000400 +#define MSM_AUD_DECODER_MASK 0x0000FFFF +#define MSM_AUD_OP_MASK 0xFFFF0000 + +/* read call timeout for error cases */ +#define MSM_AUD_BUFFER_UPDATE_WAIT_MS 2000 + +/* stream info error message mask */ +#define AUDPLAY_STREAM_INFO_MSG_MASK 0xFFFF0000 +#define AUDPLAY_ERROR_THRESHOLD_ENABLE 0xFFFFFFFF + +#define NON_TUNNEL_MODE_PLAYBACK 1 +#define TUNNEL_MODE_PLAYBACK 0 + +#define AUDPP_MIXER_ICODEC AUDPP_CMD_CFG_DEV_MIXER_DEV_0 +#define AUDPP_MIXER_1 AUDPP_CMD_CFG_DEV_MIXER_DEV_1 +#define AUDPP_MIXER_2 AUDPP_CMD_CFG_DEV_MIXER_DEV_2 +#define AUDPP_MIXER_3 AUDPP_CMD_CFG_DEV_MIXER_DEV_3 +#define AUDPP_MIXER_HLB AUDPP_CMD_CFG_DEV_MIXER_DEV_4 +#define AUDPP_MIXER_NONHLB (AUDPP_CMD_CFG_DEV_MIXER_DEV_0 | \ + AUDPP_CMD_CFG_DEV_MIXER_DEV_1 | \ + AUDPP_CMD_CFG_DEV_MIXER_DEV_2 | \ + AUDPP_CMD_CFG_DEV_MIXER_DEV_3) +#define AUDPP_MIXER_UPLINK_RX AUDPP_CMD_CFG_DEV_MIXER_DEV_5 +#define AUDPP_MAX_COPP_DEVICES 6 + +enum obj_type { + COPP, + POPP +}; + +enum msm_aud_decoder_state { + MSM_AUD_DECODER_STATE_NONE = 0, + MSM_AUD_DECODER_STATE_FAILURE = 1, + MSM_AUD_DECODER_STATE_SUCCESS = 2, + MSM_AUD_DECODER_STATE_CLOSE = 3, +}; + +int audpp_adec_alloc(unsigned dec_attrb, const char **module_name, + unsigned *queueid); +void audpp_adec_free(int decid); + +struct audpp_event_callback { + audpp_event_func fn; + void *private; +}; + +int audpp_register_event_callback(struct audpp_event_callback *eh); +int audpp_unregister_event_callback(struct audpp_event_callback *eh); +int is_audpp_enable(void); + +int audpp_enable(int id, audpp_event_func func, void *private); +void audpp_disable(int id, void *private); + +int audpp_send_queue1(void *cmd, unsigned len); +int audpp_send_queue2(void *cmd, unsigned len); +int audpp_send_queue3(void *cmd, unsigned len); + +void audpp_route_stream(unsigned short dec_id, unsigned short mixer_mask); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan, + enum obj_type objtype); +int audpp_pause(unsigned id, int pause); +int audpp_flush(unsigned id); +int audpp_query_avsync(int id); +int audpp_restore_avsync(int id, uint16_t *avsync); + +int audpp_dsp_set_eq(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_eqalizer *eq, + enum obj_type objtype); + +int audpp_dsp_set_spa(unsigned id, + struct audpp_cmd_cfg_object_params_spectram *spa, + enum obj_type objtype); + +int audpp_dsp_set_stf(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_sidechain *stf, + enum obj_type objtype); + +int audpp_dsp_set_vol_pan(unsigned id, + struct audpp_cmd_cfg_object_params_volume *vol_pan, + enum obj_type objtype); + +int audpp_dsp_set_mbadrc(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_mbadrc *mbadrc, + enum obj_type objtype); + +int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_qconcert *qconcert_plus, + enum obj_type objtype); + +int audpp_dsp_set_rx_iir(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_pcm *iir, + enum obj_type objtype); + +int audpp_dsp_set_gain_rx(unsigned id, + struct audpp_cmd_cfg_cal_gain *calib_gain_rx, + enum obj_type objtype); +int audpp_dsp_set_pbe(unsigned id, unsigned enable, + struct audpp_cmd_cfg_pbe *pbe_block, + enum obj_type objtype); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/audpreproc.h b/arch/arm/mach-msm/include/mach/qdsp5v2/audpreproc.h new file mode 100644 index 00000000000..6abeae120cf --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/audpreproc.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_QDSP5_V2_AUDPREPROC_H +#define _MACH_QDSP5_V2_AUDPREPROC_H + +#include +#include + +#define MAX_ENC_COUNT 3 + +#define MSM_ADSP_ENC_CODEC_WAV 0 +#define MSM_ADSP_ENC_CODEC_AAC 1 +#define MSM_ADSP_ENC_CODEC_SBC 2 +#define MSM_ADSP_ENC_CODEC_AMRNB 3 +#define MSM_ADSP_ENC_CODEC_EVRC 4 +#define MSM_ADSP_ENC_CODEC_QCELP 5 +#define MSM_ADSP_ENC_CODEC_EXT_WAV (15) + +#define MSM_ADSP_ENC_MODE_TUNNEL 24 +#define MSM_ADSP_ENC_MODE_NON_TUNNEL 25 + +#define AUDPREPROC_CODEC_MASK 0x00FF +#define AUDPREPROC_MODE_MASK 0xFF00 + +#define MSM_AUD_ENC_MODE_TUNNEL 0x00000100 +#define MSM_AUD_ENC_MODE_NONTUNNEL 0x00000200 + +#define SOURCE_PIPE_1 0x0001 +#define SOURCE_PIPE_0 0x0000 + +/* event callback routine prototype*/ +typedef void (*audpreproc_event_func)(void *private, unsigned id, void *msg); + +struct audpreproc_event_callback { + audpreproc_event_func fn; + void *private; +}; + +/*holds audrec information*/ +struct audrec_session_info { + int session_id; + int sampling_freq; +}; + +/* Exported common api's from audpreproc layer */ +int audpreproc_aenc_alloc(unsigned enc_type, const char **module_name, + unsigned *queue_id); +void audpreproc_aenc_free(int enc_id); + +int audpreproc_enable(int enc_id, audpreproc_event_func func, void *private); +void audpreproc_disable(int enc_id, void *private); + +int audpreproc_send_audreccmdqueue(void *cmd, unsigned len); + +int audpreproc_send_preproccmdqueue(void *cmd, unsigned len); + +int audpreproc_dsp_set_agc(struct audpreproc_cmd_cfg_agc_params *agc, + unsigned len); +int audpreproc_dsp_set_agc2(struct audpreproc_cmd_cfg_agc_params_2 *agc2, + unsigned len); +int audpreproc_dsp_set_ns(struct audpreproc_cmd_cfg_ns_params *ns, + unsigned len); +int audpreproc_dsp_set_iir( +struct audpreproc_cmd_cfg_iir_tuning_filter_params *iir, unsigned len); + +int audpreproc_dsp_set_agc(struct audpreproc_cmd_cfg_agc_params *agc, + unsigned int len); + +int audpreproc_dsp_set_iir( +struct audpreproc_cmd_cfg_iir_tuning_filter_params *iir, unsigned int len); + +int audpreproc_update_audrec_info(struct audrec_session_info + *audrec_session_info); +int audpreproc_unregister_event_callback(struct audpreproc_event_callback *ecb); + +int audpreproc_register_event_callback(struct audpreproc_event_callback *ecb); + +int audpreproc_dsp_set_gain_tx( + struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx, unsigned len); + +void get_audrec_session_info(int id, struct audrec_session_info *info); + +int audpreproc_dsp_set_lvnv( + struct audpreproc_cmd_cfg_lvnv_param *preproc_lvnv, unsigned len); +#endif /* _MACH_QDSP5_V2_AUDPREPROC_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/aux_pcm.h b/arch/arm/mach-msm/include/mach/qdsp5v2/aux_pcm.h new file mode 100644 index 00000000000..100ddea35ab --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/aux_pcm.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_AUX_PCM_H +#define __MACH_QDSP5_V2_AUX_PCM_H +#include + +/* define some values in AUX_CODEC_CTL register */ +#define AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__MSM_V 0 /* default */ +#define AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__ADSP_V 0x800 +#define AUX_CODEC_CTL__PCM_SYNC_LONG_OFFSET_V 0x400 +#define AUX_CODEC_CTL__PCM_SYNC_SHORT_OFFSET_V 0x200 +#define AUX_CODEC_CTL__I2S_SAMPLE_CLK_SRC__SDAC_V 0 +#define AUX_CODEC_CTL__I2S_SAMPLE_CLK_SRC__ICODEC_V 0x80 +#define AUX_CODEC_CTL__I2S_SAMPLE_CLK_MODE__MASTER_V 0 +#define AUX_CODEC_CTL__I2S_SAMPLE_CLK_MODE__SLAVE_V 0x40 +#define AUX_CODEC_CTL__I2S_RX_MODE__REV_V 0 +#define AUX_CODEC_CTL__I2S_RX_MODE__TRAN_V 0x20 +#define AUX_CODEC_CTL__I2S_CLK_MODE__MASTER_V 0 +#define AUX_CODEC_CTL__I2S_CLK_MODE__SLAVE_V 0x10 +#define AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_MASTER_V 0 +#define AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V 0x4 +#define AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V 0x8 +#define AUX_CODEC_CTL__AUX_CODEC_MDOE__PCM_V 0 +#define AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V 0x2 + +/* define some values in PCM_PATH_CTL register */ +#define PCM_PATH_CTL__ADSP_CTL_EN__MSM_V 0 +#define PCM_PATH_CTL__ADSP_CTL_EN__ADSP_V 0x8 + +/* define some values for aux codec config of AFE*/ +/* PCM CTL */ +#define PCM_CTL__RPCM_WIDTH__LINEAR_V 0x1 +#define PCM_CTL__TPCM_WIDTH__LINEAR_V 0x2 +/* AUX_CODEC_INTF_CTL */ +#define AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V 0x800 +/* DATA_FORMAT_PADDING_INFO */ +#define DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V 0x400 +#define DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V 0x2000 + +void aux_codec_adsp_codec_ctl_en(bool msm_adsp_en); +void aux_codec_pcm_path_ctl_en(bool msm_adsp_en); +int aux_pcm_gpios_request(void); +void aux_pcm_gpios_free(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h new file mode 100644 index 00000000000..92dfe123304 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef CODEC_UTILS_H +#define CODEC_UTILS_H + +#include + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 + +#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 +#define AUDPP_DEC_STATUS_EOS 5 + +/* worst case delay of 3secs(3000ms) for AV Sync Query response */ +#define AVSYNC_EVENT_TIMEOUT 3000 + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; +struct audio; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audio_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct codec_operations { + long (*ioctl)(struct file *, unsigned int, unsigned long); + void (*adec_params)(struct audio *); +}; + +struct audio { + spinlock_t dsp_lock; + + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + struct list_head out_queue; /* queue to retain output buffers */ + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_bits; /* bits per sample (used by PCM decoder) */ + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + + uint32_t drv_status; + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audio_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + uint32_t device_switch; /* Flag to indicate device switch */ + uint64_t bytecount_consumed; + uint64_t bytecount_head; + uint64_t bytecount_given; + uint64_t bytecount_query; + + struct list_head pmem_region_queue; /* protected by lock */ + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; + + unsigned int minor_no; + struct codec_operations codec_ops; + uint32_t buffer_size; + uint32_t buffer_count; +}; + +#endif /* !CODEC_UTILS_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/lpa.h b/arch/arm/mach-msm/include/mach/qdsp5v2/lpa.h new file mode 100644 index 00000000000..d71cf728080 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/lpa.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_LPA_H__ +#define __MACH_QDSP5_V2_LPA_H__ + +#define LPA_OUTPUT_INTF_WB_CODEC 3 +#define LPA_OUTPUT_INTF_SDAC 1 +#define LPA_OUTPUT_INTF_MI2S 2 + +struct lpa_codec_config { + uint32_t sample_rate; + uint32_t sample_width; + uint32_t output_interface; + uint32_t num_channels; +}; + +struct lpa_drv; + +struct lpa_drv *lpa_get(void); +void lpa_put(struct lpa_drv *lpa); +int lpa_cmd_codec_config(struct lpa_drv *lpa, + struct lpa_codec_config *config_ptr); +int lpa_cmd_enable_codec(struct lpa_drv *lpa, bool enable); + +#endif + diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/lpa_hw.h b/arch/arm/mach-msm/include/mach/qdsp5v2/lpa_hw.h new file mode 100644 index 00000000000..bfff3847350 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/lpa_hw.h @@ -0,0 +1,236 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_LPA_HW_H__ +#define __MACH_QDSP5_V2_LPA_HW_H__ + +#define LPA_MAX_BUF_SIZE 0x30000 + +/* LPA Output config registers */ +enum { + LPA_OBUF_CONTROL = 0x00000000, + LPA_OBUF_CODEC = 0x00000004, + LPA_OBUF_HLB_MIN_ADDR = 0x00000008, + LPA_OBUF_HLB_MAX_ADDR = 0x0000000C, + LPA_OBUF_HLB_WPTR = 0x00000010, + LPA_OBUF_HLB_VOLUME_CONTROL = 0x00000014, + LPA_OBUF_LLB_MIN_ADDR = 0x00000018, + LPA_OBUF_LLB_MAX_ADDR = 0x0000001C, + LPA_OBUF_SB_MIN_ADDR = 0x00000020, + LPA_OBUF_SB_MAX_ADDR = 0x00000024, + LPA_OBUF_INTR_ENABLE = 0x00000028, + LPA_OBUF_INTR_STATUS = 0x0000002C, + LPA_OBUF_WMARK_ASSIGN = 0x00000030, + LPA_OBUF_WMARK_0_LLB = 0x00000034, + LPA_OBUF_WMARK_1_LLB = 0x00000038, + LPA_OBUF_WMARK_2_LLB = 0x0000003C, + LPA_OBUF_WMARK_3_LLB = 0x00000040, + LPA_OBUF_WMARK_HLB = 0x00000044, + LPA_OBUF_WMARK_SB = 0x00000048, + LPA_OBUF_RDPTR_LLB = 0x0000004C, + LPA_OBUF_RDPTR_HLB = 0x00000050, + LPA_OBUF_WRPTR_SB = 0x00000054, + LPA_OBUF_UTC_CONFIG = 0x00000058, + LPA_OBUF_UTC_INTR_LOW = 0x0000005C, + LPA_OBUF_UTC_INTR_HIGH = 0x00000060, + LPA_OBUF_UTC_LOW = 0x00000064, + LPA_OBUF_UTC_HIGH = 0x00000068, + LPA_OBUF_MISR = 0x0000006C, + LPA_OBUF_STATUS = 0x00000070, + LPA_OBUF_ACK = 0x00000074, + LPA_OBUF_MEMORY_CONTROL = 0x00000078, + LPA_OBUF_MEMORY_STATUS = 0x0000007C, + LPA_OBUF_MEMORY_TIME_CONTROL = 0x00000080, + LPA_OBUF_ACC_LV = 0x00000084, + LPA_OBUF_ACC_HV = 0x0000008c, + LPA_OBUF_RESETS = 0x00000090, + LPA_OBUF_TESTBUS = 0x00000094, +}; + +/* OBUF_CODEC definition */ +#define LPA_OBUF_CODEC_RESERVED31_22_BMSK 0xffc00000 +#define LPA_OBUF_CODEC_RESERVED31_22_SHFT 0x16 +#define LPA_OBUF_CODEC_LOAD_BMSK 0x200000 +#define LPA_OBUF_CODEC_LOAD_SHFT 0x15 +#define LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK 0x100000 +#define LPA_OBUF_CODEC_CODEC_INTF_EN_SHFT 0x14 +#define LPA_OBUF_CODEC_SAMP_BMSK 0xf0000 +#define LPA_OBUF_CODEC_SAMP_SHFT 0x10 +#define LPA_OBUF_CODEC_BITS_PER_CHAN_BMSK 0xc000 +#define LPA_OBUF_CODEC_BITS_PER_CHAN_SHFT 0xe +#define LPA_OBUF_CODEC_RESERVED_13_7_BMSK 0x3f80 +#define LPA_OBUF_CODEC_RESERVED_13_7_SHFT 0x7 +#define LPA_OBUF_CODEC_INTF_BMSK 0x70 +#define LPA_OBUF_CODEC_INTF_SHFT 0x4 +#define LPA_OBUF_CODEC_NUM_CHAN_BMSK 0xf +#define LPA_OBUF_CODEC_NUM_CHAN_SHFT 0 + +/* OBUF_CONTROL definition */ +#define LPA_OBUF_CONTROL_RESERVED31_9_BMSK 0xfffffe00 +#define LPA_OBUF_CONTROL_RESERVED31_9_SHFT 0x9 +#define LPA_OBUF_CONTROL_TEST_EN_BMSK 0x100 +#define LPA_OBUF_CONTROL_TEST_EN_SHFT 0x8 +#define LPA_OBUF_CONTROL_LLB_CLR_CMD_BMSK 0x80 +#define LPA_OBUF_CONTROL_LLB_CLR_CMD_SHFT 0x7 +#define LPA_OBUF_CONTROL_SB_SAT_EN_BMSK 0x40 +#define LPA_OBUF_CONTROL_SB_SAT_EN_SHFT 0x6 +#define LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK 0x20 +#define LPA_OBUF_CONTROL_LLB_SAT_EN_SHFT 0x5 +#define LPA_OBUF_CONTROL_RESERVED4_BMSK 0x10 +#define LPA_OBUF_CONTROL_RESERVED4_SHFT 0x4 +#define LPA_OBUF_CONTROL_LLB_ACC_EN_BMSK 0x8 +#define LPA_OBUF_CONTROL_LLB_ACC_EN_SHFT 0x3 +#define LPA_OBUF_CONTROL_HLB_EN_BMSK 0x4 +#define LPA_OBUF_CONTROL_HLB_EN_SHFT 0x2 +#define LPA_OBUF_CONTROL_LLB_EN_BMSK 0x2 +#define LPA_OBUF_CONTROL_LLB_EN_SHFT 0x1 +#define LPA_OBUF_CONTROL_SB_EN_BMSK 0x1 +#define LPA_OBUF_CONTROL_SB_EN_SHFT 0 + +/* OBUF_RESET definition */ +#define LPA_OBUF_RESETS_MISR_RESET 0x1 +#define LPA_OBUF_RESETS_OVERALL_RESET 0x2 + +/* OBUF_STATUS definition */ +#define LPA_OBUF_STATUS_RESET_DONE 0x80000 +#define LPA_OBUF_STATUS_LLB_CLR_BMSK 0x40000 +#define LPA_OBUF_STATUS_LLB_CLR_SHFT 0x12 + +/* OBUF_HLB_MIN_ADDR definition */ +#define LPA_OBUF_HLB_MIN_ADDR_LOAD_BMSK 0x40000 +#define LPA_OBUF_HLB_MIN_ADDR_SEG_BMSK 0x3e000 + +/* OBUF_HLB_MAX_ADDR definition */ +#define LPA_OBUF_HLB_MAX_ADDR_SEG_BMSK 0x3fff8 + +/* OBUF_LLB_MIN_ADDR definition */ +#define LPA_OBUF_LLB_MIN_ADDR_LOAD_BMSK 0x40000 +#define LPA_OBUF_LLB_MIN_ADDR_SEG_BMSK 0x3e000 + +/* OBUF_LLB_MAX_ADDR definition */ +#define LPA_OBUF_LLB_MAX_ADDR_SEG_BMSK 0x3ff8 +#define LPA_OBUF_LLB_MAX_ADDR_SEG_SHFT 0x3 + +/* OBUF_SB_MIN_ADDR definition */ +#define LPA_OBUF_SB_MIN_ADDR_LOAD_BMSK 0x4000 +#define LPA_OBUF_SB_MIN_ADDR_SEG_BMSK 0x3e00 + +/* OBUF_SB_MAX_ADDR definition */ +#define LPA_OBUF_SB_MAX_ADDR_SEG_BMSK 0x3ff8 + +/* OBUF_MEMORY_CONTROL definition */ +#define LPA_OBUF_MEM_CTL_PWRUP_BMSK 0xfff +#define LPA_OBUF_MEM_CTL_PWRUP_SHFT 0x0 + +/* OBUF_INTR_ENABLE definition */ +#define LPA_OBUF_INTR_EN_BMSK 0x3 + +/* OBUF_WMARK_ASSIGN definition */ +#define LPA_OBUF_WMARK_ASSIGN_BMSK 0xF +#define LPA_OBUF_WMARK_ASSIGN_DONE 0xF + +/* OBUF_WMARK_n_LLB definition */ +#define LPA_OBUF_WMARK_n_LLB_ADDR(n) (0x00000034 + 0x4 * (n)) +#define LPA_OBUF_LLB_WMARK_CTRL_BMSK 0xc0000 +#define LPA_OBUF_LLB_WMARK_CTRL_SHFT 0x12 +#define LPA_OBUF_LLB_WMARK_MAP_BMSK 0xf00000 +#define LPA_OBUF_LLB_WMARK_MAP_SHFT 0x14 + +/* OBUF_WMARK_SB definition */ +#define LPA_OBUF_SB_WMARK_CTRL_BMSK 0xc0000 +#define LPA_OBUF_SB_WMARK_CTRL_SHFT 0x12 +#define LPA_OBUF_SB_WMARK_MAP_BMSK 0xf00000 +#define LPA_OBUF_SB_WMARK_MAP_SHFT 0x14 + +/* OBUF_WMARK_HLB definition */ +#define LPA_OBUF_HLB_WMARK_CTRL_BMSK 0xc0000 +#define LPA_OBUF_HLB_WMARK_CTRL_SHFT 0x12 +#define LPA_OBUF_HLB_WMARK_MAP_BMSK 0xf00000 +#define LPA_OBUF_HLB_WMARK_MAP_SHFT 0x14 + +/* OBUF_UTC_CONFIG definition */ +#define LPA_OBUF_UTC_CONFIG_MAP_BMSK 0xf0 +#define LPA_OBUF_UTC_CONFIG_MAP_SHFT 0x4 +#define LPA_OBUF_UTC_CONFIG_EN_BMSK 0x1 +#define LPA_OBUF_UTC_CONFIG_EN_SHFT 0 +#define LPA_OBUF_UTC_CONFIG_NO_INTR 0xF + +/* OBUF_ACK definition */ +#define LPA_OBUF_ACK_RESET_DONE_BMSK 0x80000 +#define LPA_OBUF_ACK_RESET_DONE_SHFT 0x13 +enum { + LPA_SAMPLE_RATE_8KHZ = 0x0000, + LPA_SAMPLE_RATE_11P025KHZ = 0x0001, + LPA_SAMPLE_RATE_16KHZ = 0x0002, + LPA_SAMPLE_RATE_22P05KHZ = 0x0003, + LPA_SAMPLE_RATE_32KHZ = 0x0004, + LPA_SAMPLE_RATE_44P1KHZ = 0x0005, + LPA_SAMPLE_RATE_48KHZ = 0x0006, + LPA_SAMPLE_RATE_64KHZ = 0x0007, + LPA_SAMPLE_RATE_96KHZ = 0x0008, +}; + +enum { + LPA_BITS_PER_CHAN_16BITS = 0x0000, + LPA_BITS_PER_CHAN_24BITS = 0x0001, + LPA_BITS_PER_CHAN_32BITS = 0x0002, + LPA_BITS_PER_CHAN_RESERVED = 0x0003, +}; + +enum { + LPA_INTF_WB_CODEC = 0x0000, + LPA_INTF_SDAC = 0x0001, + LPA_INTF_MI2S = 0x0002, + LPA_INTF_RESERVED = 0x0003, +}; + +enum { + LPA_BUF_ID_HLB, /* HLB buffer */ + LPA_BUF_ID_LLB, /* LLB buffer */ + LPA_BUF_ID_SB, /* SB buffer */ + LPA_BUF_ID_UTC, +}; + +/* WB_CODEC & SDAC can only support 16bit mono/stereo. + * MI2S can bit format and number of channel + */ +enum { + LPA_NUM_CHAN_MONO = 0x0000, + LPA_NUM_CHAN_STEREO = 0x0001, + LPA_NUM_CHAN_5P1 = 0x0002, + LPA_NUM_CHAN_7P1 = 0x0003, + LPA_NUM_CHAN_4_CHANNEL = 0x0004, +}; + +enum { + LPA_WMARK_CTL_DISABLED = 0x0, + LPA_WMARK_CTL_NON_BLOCK = 0x1, + LPA_WMARK_CTL_ZERO_INSERT = 0x2, + LPA_WMARK_CTL_RESERVED = 0x3 +}; + +struct lpa_mem_bank_select { + u32 b0:1; /*RAM bank 0 16KB=2Kx64(0) */ + u32 b1:1; /*RAM bank 1 16KB=2Kx64(0) */ + u32 b2:1; /*RAM bank 2 16KB=2Kx64(0) */ + u32 b3:1; /*RAM bank 3 16KB=2Kx64(0) */ + u32 b4:1; /*RAM bank 4 16KB=2Kx64(1) */ + u32 b5:1; /*RAM bank 5 16KB=2Kx64(1) */ + u32 b6:1; /*RAM bank 6 16KB=2Kx64(1) */ + u32 b7:1; /*RAM bank 7 16KB=2Kx64(1) */ + u32 b8:1; /*RAM bank 8 16KB=4Kx32(0) */ + u32 b9:1; /*RAM bank 9 16KB=4Kx32(1) */ + u32 b10:1; /*RAM bank 10 16KB=4Kx32(2) */ + u32 llb:1; /*RAM bank 11 16KB=4Kx32(3) */ +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/marimba_profile.h b/arch/arm/mach-msm/include/mach/qdsp5v2/marimba_profile.h new file mode 100644 index 00000000000..c1cb3fe35fd --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/marimba_profile.h @@ -0,0 +1,3201 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_MARIMBA_PROFILE_H__ +#define __MACH_QDSP5_V2_MARIMBA_PROFILE_H__ + +/***************************************************************************\ + Handset +\***************************************************************************/ + + +#define HANDSET_RX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x21, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_RX_16000_OSR_256 HANDSET_RX_8000_OSR_256 + +#define HANDSET_RX_48000_OSR_64\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x47)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xfF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x21, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_RX_48000_OSR_256\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xfF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x21, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_TX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_TX_16000_OSR_256 HANDSET_TX_8000_OSR_256 + +#define HANDSET_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/***************************************************************************\ + Headset +\***************************************************************************/ + + + +#define HEADSET_STEREO_TX_8000_OSR_256\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE8, 0xE8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_TX_16000_OSR_256 HEADSET_STEREO_TX_8000_OSR_256 + +#define HEADSET_STEREO_TX_48000_OSR_64\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xfc, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x46)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE8, 0xE8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_TX_48000_OSR_256\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FALSH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xfc, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE8, 0xE8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_MONO_TX_16000_OSR_256\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xfc, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFf, 0xc8)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_MONO_TX_8000_OSR_256 HEADSET_MONO_TX_16000_OSR_256 + +#define HEADSET_MONO_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CAPLESS_8000_OSR_256\ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xeb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CAPLESS_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CAPLESS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_RX_LEGACY_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_LEGACY_16000_OSR_256 HEADSET_RX_LEGACY_8000_OSR_256 + +#define HEADSET_RX_LEGACY_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_RX_LEGACY_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CLASS_D_LEGACY_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_RX_CLASS_D_LEGACY_11025_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xbb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_RX_CLASS_D_LEGACY_16000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xd4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xd4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_RX_CLASS_D_LEGACY_22050_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xbb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xd2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xd2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_CLASS_D_LEGACY_32000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xf4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xf4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CLASS_D_LEGACY_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CLASS_D_LEGACY_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_CAPLESS_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x82)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xeb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_CAPLESS_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x82)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + + +#define HEADSET_STEREO_RX_CAPLESS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x82)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x82)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_LEGACY_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xaC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xaC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_LEGACY_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_LEGACY_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_11025_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xbb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_16000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xd4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xd4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_22050_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xbb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xd2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xd2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_32000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xf4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xf4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_RX_CLASS_D_LEGACY_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_RX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe2, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define SPEAKER_RX_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe2, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_RX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe2, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_STEREO_RX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe6, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x0f, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define SPEAKER_STEREO_RX_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe6, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x0f, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_STEREO_RX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe6, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x0f, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define SPEAKER_TX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_TX_48000_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x46)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define SPEAKER_TX_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FM_HANDSET_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x47)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FM_HEADSET_STEREO_CLASS_D_LEGACY_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FM_HEADSET_CLASS_AB_STEREO_LEGACY_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FM_HEADSET_CLASS_AB_STEREO_CAPLESS_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x82)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xeb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FM_SPEAKER_OSR_64 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x67)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe2, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8a, 0x8a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define AUXPGA_HEADSET_STEREO_RX_LEGACY \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0x18, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXPGA_HEADSET_MONO_RX_LEGACY \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0x18, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXPGA_HEADSET_STEREO_RX_CAPLESS \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xeb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0x18, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXPGA_HEADSET_MONO_RX_CAPLESS \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xeb)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0x18, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXPGA_HEADSET_STEREO_RX_CLASS_D \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFC, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define AUXPGA_HEADSET_MONO_RX_CLASS_D \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xDD)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFC, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXPGA_EAR \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2B, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2C, 0xff, 0x89)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0x20, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/***************************************************************************\ + DigitalMicprofile +\***************************************************************************/ +#define DIGITAL_MIC \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x1A, 0xff, 0xc0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x66)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/***************************************************************************\ + DualMicprofile +\***************************************************************************/ +#define SPEAKER_MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE2, 0xE2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xc0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE2, 0xE1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xc0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE2, 0xE2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xc0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE1, 0xE1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xc0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/***************************************************************************\ + AnalogDualMicProfile +\***************************************************************************/ +#define ANALOG_DUAL_MIC \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xfd, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xE2, 0xE2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xD0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x1c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0e, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0d, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x1c, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } +/***************************************************************************\ + TTY +\***************************************************************************/ +#define TTY_HEADSET_MONO_TX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xfc, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x5E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFf, 0xA8)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x0A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define TTY_HEADSET_MONO_TX_16000_OSR_256 TTY_HEADSET_MONO_TX_8000_OSR_256 + +#define TTY_HEADSET_MONO_RX_CLASS_D_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xc4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xc4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0xF4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define TTY_HEADSET_MONO_RX_CLASS_D_16000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xd4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xd4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0xF4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define TTY_HEADSET_MONO_RX_CLASS_D_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xf8, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xfF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0f)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xFF, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xff)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4a, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0a, 0x0a)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3E8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0xF4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0x0f, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/***************************************************************************\ + FFA +\***************************************************************************/ +#define HANDSET_RX_8000_OSR_256_FFA \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x21, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_RX_16000_OSR_256_FFA HANDSET_RX_8000_OSR_256_FFA + +#define HANDSET_RX_48000_OSR_64_FFA \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x47)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xfF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x42)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0xD5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x21, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_RX_48000_OSR_256_FFA \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xfF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3d, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x21, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x2710}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x05, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x36, 0xc0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_TX_8000_OSR_256_FFA \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_TX_16000_OSR_256_FFA HANDSET_TX_8000_OSR_256_FFA + +#define HANDSET_TX_48000_OSR_256_FFA \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x10, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xac)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0x82)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x37, 0xe6, 0xa0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0x2b)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x23, 0x23)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x8A, 0x8A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xa0)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x98)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x88)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x78)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x68)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x58)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x38)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x28)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xaC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0xaC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x7530}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } +#endif /* __MARIMBA_PROFILE_H__ */ + + + + diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/mi2s.h b/arch/arm/mach-msm/include/mach/qdsp5v2/mi2s.h new file mode 100644 index 00000000000..e304e254133 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/mi2s.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_MI2S_H +#define _MACH_QDSP5_V2_MI2S_H + +#define WT_16_BIT 0 +#define WT_24_BIT 1 +#define WT_32_BIT 2 +#define WT_MAX 4 + +enum mi2s_ret_enum_type { + MI2S_FALSE = 0, + MI2S_TRUE +}; + +#define MI2S_CHAN_MONO_RAW 0 +#define MI2S_CHAN_MONO_PACKED 1 +#define MI2S_CHAN_STEREO 2 +#define MI2S_CHAN_4CHANNELS 3 +#define MI2S_CHAN_6CHANNELS 4 +#define MI2S_CHAN_8CHANNELS 5 +#define MI2S_CHAN_MAX_OUTBOUND_CHANNELS MI2S__CHAN_8CHANNELS + +#define MI2S_SD_0 0x01 +#define MI2S_SD_1 0x02 +#define MI2S_SD_2 0x04 +#define MI2S_SD_3 0x08 + +#define MI2S_SD_LINE_MASK (MI2S_SD_0 | MI2S_SD_1 | MI2S_SD_2 | MI2S_SD_3) + +bool mi2s_set_hdmi_output_path(uint8_t channels, uint8_t size, + uint8_t sd_line); + +bool mi2s_set_hdmi_input_path(uint8_t channels, uint8_t size, uint8_t sd_line); + +bool mi2s_set_codec_output_path(uint8_t channels, uint8_t size); + +bool mi2s_set_codec_input_path(uint8_t channels, uint8_t size); + +#endif /* #ifndef MI2S_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/mp3_funcs.h b/arch/arm/mach-msm/include/mach/qdsp5v2/mp3_funcs.h new file mode 100644 index 00000000000..ac06d3b2839 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/mp3_funcs.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef MP3_FUNCS_H +#define MP3_FUNCS_H + +/* Function Prototypes */ +long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +void audpp_cmd_cfg_mp3_params(struct audio *audio); + +#endif /* !MP3_FUNCS_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/msm_lpa.h b/arch/arm/mach-msm/include/mach/qdsp5v2/msm_lpa.h new file mode 100644 index 00000000000..0dced94bc54 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/msm_lpa.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_MSM_LPA_H +#define _MACH_QDSP5_V2_MSM_LPA_H + +struct lpa_mem_config { + u32 llb_min_addr; + u32 llb_max_addr; + u32 sb_min_addr; + u32 sb_max_addr; +}; + +struct msm_lpa_platform_data { + u32 obuf_hlb_size; + u32 dsp_proc_id; + u32 app_proc_id; + struct lpa_mem_config nosb_config; /* no summing */ + struct lpa_mem_config sb_config; /* summing required */ +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/pcm_funcs.h b/arch/arm/mach-msm/include/mach/qdsp5v2/pcm_funcs.h new file mode 100644 index 00000000000..fa650005d1d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/pcm_funcs.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef PCM_FUNCS_H +#define PCM_FUNCS_H + +long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +void audpp_cmd_cfg_pcm_params(struct audio *audio); + +#endif /* !PCM_FUNCS_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5afecmdi.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5afecmdi.h new file mode 100644 index 00000000000..25fe3a01c07 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5afecmdi.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_QDSP5AFECMDI_H +#define __MACH_QDSP5_V2_QDSP5AFECMDI_H + +#define QDSP5_DEVICE_mI2S_CODEC_RX 1 /* internal codec rx path */ +#define QDSP5_DEVICE_mI2S_CODEC_TX 2 /* internal codec tx path */ +#define QDSP5_DEVICE_AUX_CODEC_RX 3 /* external codec rx path */ +#define QDSP5_DEVICE_AUX_CODEC_TX 4 /* external codec tx path */ +#define QDSP5_DEVICE_mI2S_HDMI_RX 5 /* HDMI/FM block rx path */ +#define QDSP5_DEVICE_mI2S_HDMI_TX 6 /* HDMI/FM block tx path */ +#define QDSP5_DEVICE_ID_MAX 7 + +#define AFE_CMD_CODEC_CONFIG_CMD 0x1 +#define AFE_CMD_CODEC_CONFIG_LEN sizeof(struct afe_cmd_codec_config) + +struct afe_cmd_codec_config{ + uint16_t cmd_id; + uint16_t device_id; + uint16_t activity; + uint16_t sample_rate; + uint16_t channel_mode; + uint16_t volume; + uint16_t reserved; +} __attribute__ ((packed)); + +#define AFE_CMD_DEVICE_VOLUME_CTRL 0x2 +#define AFE_CMD_DEVICE_VOLUME_CTRL_LEN \ + sizeof(struct afe_cmd_device_volume_ctrl) + +struct afe_cmd_device_volume_ctrl { + uint16_t cmd_id; + uint16_t device_id; + uint16_t device_volume; + uint16_t reserved; +} __attribute__ ((packed)); + +#define AFE_CMD_AUX_CODEC_CONFIG_CMD 0x3 +#define AFE_CMD_AUX_CODEC_CONFIG_LEN sizeof(struct afe_cmd_aux_codec_config) + +struct afe_cmd_aux_codec_config{ + uint16_t cmd_id; + uint16_t dma_path_ctl; + uint16_t pcm_ctl; + uint16_t eight_khz_int_mode; + uint16_t aux_codec_intf_ctl; + uint16_t data_format_padding_info; +} __attribute__ ((packed)); + +#define AFE_CMD_FM_RX_ROUTING_CMD 0x6 +#define AFE_CMD_FM_RX_ROUTING_LEN sizeof(struct afe_cmd_fm_codec_config) + +struct afe_cmd_fm_codec_config{ + uint16_t cmd_id; + uint16_t enable; + uint16_t device_id; +} __attribute__ ((packed)); + +#define AFE_CMD_FM_PLAYBACK_VOLUME_CMD 0x8 +#define AFE_CMD_FM_PLAYBACK_VOLUME_LEN sizeof(struct afe_cmd_fm_volume_config) + +struct afe_cmd_fm_volume_config{ + uint16_t cmd_id; + uint16_t volume; + uint16_t reserved; +} __attribute__ ((packed)); + +#define AFE_CMD_FM_CALIBRATION_GAIN_CMD 0x11 +#define AFE_CMD_FM_CALIBRATION_GAIN_LEN \ + sizeof(struct afe_cmd_fm_calibgain_config) + +struct afe_cmd_fm_calibgain_config{ + uint16_t cmd_id; + uint16_t device_id; + uint16_t calibration_gain; +} __attribute__ ((packed)); + +#define AFE_CMD_LOOPBACK 0xD +#define AFE_CMD_EXT_LOOPBACK 0xE +#define AFE_CMD_LOOPBACK_LEN sizeof(struct afe_cmd_loopback) +#define AFE_LOOPBACK_ENABLE_COMMAND 0xFFFF +#define AFE_LOOPBACK_DISABLE_COMMAND 0x0000 + +struct afe_cmd_loopback { + uint16_t cmd_id; + uint16_t enable_flag; + uint16_t reserved[2]; +} __attribute__ ((packed)); + +struct afe_cmd_ext_loopback { + uint16_t cmd_id; + uint16_t enable_flag; + uint16_t source_id; + uint16_t dst_id; + uint16_t reserved[2]; +} __packed; + +#define AFE_CMD_CFG_RMC_PARAMS 0x12 +#define AFE_CMD_CFG_RMC_LEN \ + sizeof(struct afe_cmd_cfg_rmc) + +struct afe_cmd_cfg_rmc { + unsigned short cmd_id; + signed short rmc_mode; + unsigned short rmc_ipw_length_ms; + unsigned short rmc_peak_length_ms; + unsigned short rmc_init_pulse_length_ms; + unsigned short rmc_total_int_length_ms; + unsigned short rmc_rampupdn_length_ms; + unsigned short rmc_delay_length_ms; + unsigned short rmc_detect_start_threshdb; + signed short rmc_init_pulse_threshdb; +} __attribute__((packed)); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5afemsg.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5afemsg.h new file mode 100644 index 00000000000..16134e3f60e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5afemsg.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_QDSP5AFEMSG_H +#define __MACH_QDSP5_V2_QDSP5AFEMSG_H + +#define AFE_APU_MSG_CODEC_CONFIG_ACK 0x0001 +#define AFE_APU_MSG_CODEC_CONFIG_ACK_LEN \ + sizeof(struct afe_msg_codec_config_ack) + +#define AFE_APU_MSG_VOC_TIMING_SUCCESS 0x0002 + +#define AFE_MSG_CODEC_CONFIG_ENABLED 0x1 +#define AFE_MSG_CODEC_CONFIG_DISABLED 0xFFFF + +struct afe_msg_codec_config_ack { + uint16_t device_id; + uint16_t device_activity; + uint16_t reserved; +} __attribute__((packed)); + +#endif /* QDSP5AFEMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audplaycmdi.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audplaycmdi.h new file mode 100644 index 00000000000..53128d37154 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audplaycmdi.h @@ -0,0 +1,145 @@ +#ifndef QDSP5AUDPLAYCMDI_H +#define QDSP5AUDPLAYCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + Q D S P 5 A U D I O P L A Y T A S K C O M M A N D S + +GENERAL DESCRIPTION + Command Interface for AUDPLAYTASK on QDSP5 + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + + audplay_cmd_dec_data_avail + Send buffer to AUDPLAY task + + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL 0x0000 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_LEN \ + sizeof(struct audplay_cmd_bitstream_data_avail) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK +*/ +struct audplay_cmd_bitstream_data_avail{ + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously + * available at the above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + +} __attribute__((packed)); + +#define AUDPLAY_CMD_CHANNEL_INFO 0x0001 +#define AUDPLAY_CMD_CHANNEL_INFO_LEN \ + sizeof(struct audplay_cmd_channel_info) + +struct audplay_cmd_channel_select { + unsigned int cmd_id; + unsigned int stream_id; + unsigned int channel_select; +} __attribute__((packed)); + +struct audplay_cmd_threshold_update { + unsigned int cmd_id; + unsigned int threshold_update; + unsigned int threshold_value; +} __attribute__((packed)); + +union audplay_cmd_channel_info { + struct audplay_cmd_channel_select ch_select; + struct audplay_cmd_threshold_update thr_update; +}; + +#define AUDPLAY_CMD_HPCM_BUF_CFG 0x0003 +#define AUDPLAY_CMD_HPCM_BUF_CFG_LEN \ + sizeof(struct audplay_cmd_hpcm_buf_cfg) + +struct audplay_cmd_hpcm_buf_cfg { + unsigned int cmd_id; + unsigned int hostpcm_config; + unsigned int feedback_frequency; + unsigned int byte_swap; + unsigned int max_buffers; + unsigned int partition_number; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BUFFER_REFRESH 0x0004 +#define AUDPLAY_CMD_BUFFER_REFRESH_LEN \ + sizeof(struct audplay_cmd_buffer_update) + +struct audplay_cmd_buffer_refresh { + unsigned int cmd_id; + unsigned int num_buffers; + unsigned int buf_read_count; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2 0x0005 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2_LEN \ + sizeof(struct audplay_cmd_bitstream_data_avail_nt2) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK + * for NT2 */ +struct audplay_cmd_bitstream_data_avail_nt2 { + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously + * available at the above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + + /* bitstream write pointer */ + unsigned int dspBitstreamWritePtr; + +} __attribute__((packed)); + +#define AUDPLAY_CMD_OUTPORT_FLUSH 0x0006 + +struct audplay_cmd_outport_flush { + unsigned int cmd_id; +} __attribute__((packed)); + +#endif /* QDSP5AUDPLAYCMD_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audplaymsg.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audplaymsg.h new file mode 100644 index 00000000000..2eeb5576ab9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audplaymsg.h @@ -0,0 +1,74 @@ +#ifndef QDSP5AUDPLAYMSG_H +#define QDSP5AUDPLAYMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + Q D S P 5 A U D I O P L A Y T A S K M S G + +GENERAL DESCRIPTION + Message sent by AUDPLAY task + +REFERENCES + None + + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +#define AUDPLAY_MSG_DEC_NEEDS_DATA 0x0001 +#define AUDPLAY_MSG_DEC_NEEDS_DATA_MSG_LEN \ + sizeof(audplay_msg_dec_needs_data) + +struct audplay_msg_dec_needs_data { + /* reserved*/ + unsigned int dec_id; + + /*The read pointer offset of external memory till which bitstream + has been dmed in*/ + unsigned int adecDataReadPtrOffset; + + /*The buffer size of external memory. */ + unsigned int adecDataBufSize; + + unsigned int bitstream_free_len; + unsigned int bitstream_write_ptr; + unsigned int bitstarem_buf_start; + unsigned int bitstream_buf_len; +} __attribute__((packed)); + +#define AUDPLAY_UP_STREAM_INFO 0x0003 +#define AUDPLAY_UP_STREAM_INFO_LEN \ + sizeof(struct audplay_msg_stream_info) + +struct audplay_msg_stream_info { + unsigned int decoder_id; + unsigned int channel_info; + unsigned int sample_freq; + unsigned int bitstream_info; + unsigned int bit_rate; +} __attribute__((packed)); + +#define AUDPLAY_MSG_BUFFER_UPDATE 0x0004 +#define AUDPLAY_MSG_BUFFER_UPDATE_LEN \ + sizeof(struct audplay_msg_buffer_update) + +struct audplay_msg_buffer_update { + unsigned int buffer_write_count; + unsigned int num_of_buffer; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); + +#define AUDPLAY_UP_OUTPORT_FLUSH_ACK 0x0005 + +#endif /* QDSP5AUDPLAYMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audppcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audppcmdi.h new file mode 100644 index 00000000000..0416f528633 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audppcmdi.h @@ -0,0 +1,1088 @@ +#ifndef __MACH_QDSP5_V2_QDSP5AUDPPCMDI_H +#define __MACH_QDSP5_V2_QDSP5AUDPPCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P O S T P R O C E S S I N G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by AUDPP Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992-2011, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/* + * ARM to AUDPPTASK Commands + * + * ARM uses three command queues to communicate with AUDPPTASK + * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands + * Location : MEMA + * Buffer Size : 6 words + * No of buffers in a queue : 20 for gaming audio and 5 for other images + * 2)uPAudPPCmd2Queue : Used for commands which are not much lengthier + * Location : MEMA + * Buffer Size : 23 + * No of buffers in a queue : 2 + * 3)uPAudOOCmd3Queue : Used for lengthier and more frequent commands + * Location : MEMA + * Buffer Size : 145 + * No of buffers in a queue : 3 + */ + +/* + * Commands Related to uPAudPPCmd1Queue + */ + +/* + * Command Structure to enable or disable the active decoders + */ + +#define AUDPP_CMD_CFG_DEC_TYPE 0x0001 +#define AUDPP_CMD_CFG_DEC_TYPE_LEN sizeof(struct audpp_cmd_cfg_dec_type) + +/* Enable the decoder */ +#define AUDPP_CMD_DEC_TYPE_M 0x000F + +#define AUDPP_CMD_ENA_DEC_V 0x4000 +#define AUDPP_CMD_DIS_DEC_V 0x0000 +#define AUDPP_CMD_DEC_STATE_M 0x4000 + +#define AUDPP_CMD_UPDATDE_CFG_DEC 0x8000 +#define AUDPP_CMD_DONT_UPDATE_CFG_DEC 0x0000 + + +/* Type specification of cmd_cfg_dec */ + +struct audpp_cmd_cfg_dec_type { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short dec_cfg; + unsigned short dm_mode; +} __attribute__((packed)); + +/* + * Command Structure to Pause , Resume and flushes the selected audio decoders + */ + +#define AUDPP_CMD_DEC_CTRL 0x0002 +#define AUDPP_CMD_DEC_CTRL_LEN sizeof(struct audpp_cmd_dec_ctrl) + +/* Decoder control commands for pause, resume and flush */ +#define AUDPP_CMD_FLUSH_V 0x2000 + +#define AUDPP_CMD_PAUSE_V 0x4000 +#define AUDPP_CMD_RESUME_V 0x0000 + +#define AUDPP_CMD_UPDATE_V 0x8000 +#define AUDPP_CMD_IGNORE_V 0x0000 + + +/* Type Spec for decoder control command*/ + +struct audpp_cmd_dec_ctrl{ + unsigned short cmd_id; + unsigned short stream_id; + unsigned short dec_ctrl; +} __attribute__((packed)); + +/* + * Command Structure to Configure the AVSync FeedBack Mechanism + */ + +#define AUDPP_CMD_AVSYNC 0x0003 +#define AUDPP_CMD_AVSYNC_LEN sizeof(struct audpp_cmd_avsync) + +struct audpp_cmd_avsync{ + unsigned short cmd_id; + unsigned short stream_id; + unsigned short interrupt_interval; + unsigned short sample_counter_dlsw; + unsigned short sample_counter_dmsw; + unsigned short sample_counter_msw; + unsigned short byte_counter_dlsw; + unsigned short byte_counter_dmsw; + unsigned short byte_counter_msw; +} __attribute__((packed)); + +/* + * Macros used to store the AV Sync Info from DSP + */ + +#define AUDPP_AVSYNC_CH_COUNT 1 +#define AUDPP_AVSYNC_NUM_WORDS 6 +/* Timeout of 3000ms for AV Sync Query response */ +#define AUDPP_AVSYNC_EVENT_TIMEOUT 3000 + +/* + * Command Structure to Query AVSync Info from DSP + */ + +#define AUDPP_CMD_QUERY_AVSYNC 0x0006 + +struct audpp_cmd_query_avsync{ + unsigned short cmd_id; + unsigned short stream_id; +} __attribute__((packed)); + +/* + * Command Structure to enable or disable(sleep) the AUDPPTASK + */ + +#define AUDPP_CMD_CFG 0x0004 +#define AUDPP_CMD_CFG_LEN sizeof(struct audpp_cmd_cfg) + +#define AUDPP_CMD_CFG_SLEEP 0x0000 +#define AUDPP_CMD_CFG_ENABLE 0xFFFF + +struct audpp_cmd_cfg { + unsigned short cmd_id; + unsigned short cfg; +} __attribute__((packed)); + +/* + * Command Structure to Inject or drop the specified no of samples + */ + +#define AUDPP_CMD_ADJUST_SAMP 0x0005 +#define AUDPP_CMD_ADJUST_SAMP_LEN sizeof(struct audpp_cmd_adjust_samp) + +#define AUDPP_CMD_SAMP_DROP -1 +#define AUDPP_CMD_SAMP_INSERT 0x0001 + +#define AUDPP_CMD_NUM_SAMPLES 0x0001 + +struct audpp_cmd_adjust_samp { + unsigned short cmd_id; + unsigned short object_no; + signed short sample_insert_or_drop; + unsigned short num_samples; +} __attribute__((packed)); + +/* + * Command Structure to Configure AVSync Feedback Mechanism + */ + +#define AUDPP_CMD_ROUTING_MODE 0x0007 +#define AUDPP_CMD_ROUTING_MODE_LEN \ +sizeof(struct audpp_cmd_routing_mode) + +struct audpp_cmd_routing_mode { + unsigned short cmd_id; + unsigned short object_number; + unsigned short routing_mode; +} __attribute__((packed)); + +/* + * Commands Related to uPAudPPCmd2Queue + */ + +/* + * Command Structure to configure Per decoder Parameters (Common) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS 0x0000 +#define AUDPP_CMD_CFG_ADEC_PARAMS_COMMON_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_common) + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_FCM 0x4000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_FCM 0x0000 + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_DCM 0x8000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_DCM 0x0000 + +/* Sampling frequency*/ +#define AUDPP_CMD_SAMP_RATE_96000 0x0000 +#define AUDPP_CMD_SAMP_RATE_88200 0x0001 +#define AUDPP_CMD_SAMP_RATE_64000 0x0002 +#define AUDPP_CMD_SAMP_RATE_48000 0x0003 +#define AUDPP_CMD_SAMP_RATE_44100 0x0004 +#define AUDPP_CMD_SAMP_RATE_32000 0x0005 +#define AUDPP_CMD_SAMP_RATE_24000 0x0006 +#define AUDPP_CMD_SAMP_RATE_22050 0x0007 +#define AUDPP_CMD_SAMP_RATE_16000 0x0008 +#define AUDPP_CMD_SAMP_RATE_12000 0x0009 +#define AUDPP_CMD_SAMP_RATE_11025 0x000A +#define AUDPP_CMD_SAMP_RATE_8000 0x000B + + +/* + * Type specification of cmd_adec_cfg sent to all decoder + */ + +struct audpp_cmd_cfg_adec_params_common { + unsigned short cmd_id; + unsigned short dec_id; + unsigned short length; + unsigned short reserved; + unsigned short input_sampling_frequency; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (Wav) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wav) + + +#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002 + +#define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000 +#define AUDPP_CMD_WAV_PCM_WIDTH_16 0x0001 +#define AUDPP_CMD_WAV_PCM_WIDTH_24 0x0002 + +struct audpp_cmd_cfg_adec_params_wav { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short pcm_width; + unsigned short sign; +} __attribute__((packed)); + +/* + * Command Structure for CMD_CFG_DEV_MIXER + */ + +#define AUDPP_CMD_CFG_DEV_MIXER_PARAMS_LEN \ + sizeof(struct audpp_cmd_cfg_dev_mixer_params) + +#define AUDPP_CMD_CFG_DEV_MIXER 0x0008 + +#define AUDPP_CMD_CFG_DEV_MIXER_ID_0 0 +#define AUDPP_CMD_CFG_DEV_MIXER_ID_1 1 +#define AUDPP_CMD_CFG_DEV_MIXER_ID_2 2 +#define AUDPP_CMD_CFG_DEV_MIXER_ID_3 3 +#define AUDPP_CMD_CFG_DEV_MIXER_ID_4 4 +#define AUDPP_CMD_CFG_DEV_MIXER_ID_5 5 + +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_NONE 0x0000 +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_0 \ + (0x1 << AUDPP_CMD_CFG_DEV_MIXER_ID_0) +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_1 \ + (0x1 << AUDPP_CMD_CFG_DEV_MIXER_ID_1) +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_2 \ + (0x1 << AUDPP_CMD_CFG_DEV_MIXER_ID_2) +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_3 \ + (0x1 << AUDPP_CMD_CFG_DEV_MIXER_ID_3) +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_4 \ + (0x1 << AUDPP_CMD_CFG_DEV_MIXER_ID_4) +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_5 \ + (0x1 << AUDPP_CMD_CFG_DEV_MIXER_ID_5) + +struct audpp_cmd_cfg_dev_mixer_params { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short mixer_cmd; +} __attribute__((packed)); + + +/* + * Command Structure to configure Per decoder Parameters (ADPCM) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_adpcm) + + +#define AUDPP_CMD_ADPCM_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_ADPCM_STEREO_CFG_STEREO 0x0002 + +struct audpp_cmd_cfg_adec_params_adpcm { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short block_size; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (WMA) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wma) + +struct audpp_cmd_cfg_adec_params_wma { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short armdatareqthr; + unsigned short channelsdecoded; + unsigned short wmabytespersec; + unsigned short wmasamplingfreq; + unsigned short wmaencoderopts; +} __attribute__((packed)); + + +/* + * Command Structure to configure Per decoder Parameters (MP3) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_mp3) + +struct audpp_cmd_cfg_adec_params_mp3 { + struct audpp_cmd_cfg_adec_params_common common; +} __attribute__((packed)); + + +/* + * Command Structure to configure Per decoder Parameters (AAC) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_aac) + + +#define AUDPP_CMD_AAC_FORMAT_ADTS -1 +#define AUDPP_CMD_AAC_FORMAT_RAW 0x0000 +#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDPP_CMD_AAC_FORMAT_LOAS 0x0002 + +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_ERLC 0x0011 + +#define AUDPP_CMD_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +struct audpp_cmd_cfg_adec_params_aac { + struct audpp_cmd_cfg_adec_params_common common; + signed short format; + unsigned short audio_object; + unsigned short ep_config; + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short channel_configuration; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (V13K) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_v13k) + + +#define AUDPP_CMD_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_STEREO_CFG_STEREO 0x0002 + +struct audpp_cmd_cfg_adec_params_v13k { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)); + +#define AUDPP_CMD_CFG_ADEC_PARAMS_EVRC_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_evrc) + +struct audpp_cmd_cfg_adec_params_evrc { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__ ((packed)); + +/* + * Command Structure to configure Per decoder Parameters (AMRWB) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_amrwb) + +struct audpp_cmd_cfg_adec_params_amrwb { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (WMAPRO) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wmapro) + +struct audpp_cmd_cfg_adec_params_wmapro { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short armdatareqthr; + uint8_t validbitspersample; + uint8_t numchannels; + unsigned short formattag; + unsigned short samplingrate; + unsigned short avgbytespersecond; + unsigned short asfpacketlength; + unsigned short channelmask; + unsigned short encodeopt; + unsigned short advancedencodeopt; + uint32_t advancedencodeopt2; +} __attribute__((packed)); + +/* + * Command Structure to configure the HOST PCM interface + */ + +#define AUDPP_CMD_PCM_INTF 0x0001 +#define AUDPP_CMD_PCM_INTF_2 0x0002 +#define AUDPP_CMD_PCM_INTF_LEN sizeof(struct audpp_cmd_pcm_intf) + +#define AUDPP_CMD_PCM_INTF_MONO_V 0x0001 +#define AUDPP_CMD_PCM_INTF_STEREO_V 0x0002 + +/* These two values differentiate the two types of commands that could be issued + * Interface configuration command and Buffer update command */ + +#define AUDPP_CMD_PCM_INTF_CONFIG_CMD_V 0x0000 +#define AUDPP_CMD_PCM_INTF_BUFFER_CMD_V -1 + +#define AUDPP_CMD_PCM_INTF_RX_ENA_M 0x000F +#define AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V 0x0008 +#define AUDPP_CMD_PCM_INTF_RX_ENA_DSPTOARM_V 0x0004 + +/* These flags control the enabling and disabling of the interface together + * with host interface bit mask. */ + +#define AUDPP_CMD_PCM_INTF_ENA_V -1 +#define AUDPP_CMD_PCM_INTF_DIS_V 0x0000 + + +#define AUDPP_CMD_PCM_INTF_FULL_DUPLEX 0x0 +#define AUDPP_CMD_PCM_INTF_HALF_DUPLEX_TODSP 0x1 + + +#define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5 +#define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6 + +struct audpp_cmd_pcm_intf { + unsigned short cmd_id; + unsigned short stream; + unsigned short stream_id; + signed short config; + unsigned short intf_type; + + /* DSP -> ARM Configuration */ + unsigned short read_buf1LSW; + unsigned short read_buf1MSW; + unsigned short read_buf1_len; + + unsigned short read_buf2LSW; + unsigned short read_buf2MSW; + unsigned short read_buf2_len; + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short dsp_to_arm_flag; + unsigned short partition_number; + + /* ARM -> DSP Configuration */ + unsigned short write_buf1LSW; + unsigned short write_buf1MSW; + unsigned short write_buf1_len; + + unsigned short write_buf2LSW; + unsigned short write_buf2MSW; + unsigned short write_buf2_len; + + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short arm_to_rx_flag; + unsigned short weight_decoder_to_rx; + unsigned short weight_arm_to_rx; + + unsigned short partition_number_arm_to_dsp; + unsigned short sample_rate; + unsigned short channel_mode; +} __attribute__((packed)); + +/* + ** BUFFER UPDATE COMMAND + */ +#define AUDPP_CMD_PCM_INTF_SEND_BUF_PARAMS_LEN \ + sizeof(struct audpp_cmd_pcm_intf_send_buffer) + +struct audpp_cmd_pcm_intf_send_buffer { + unsigned short cmd_id; + unsigned short stream; + unsigned short stream_id; + /* set config = 0xFFFF for configuration*/ + signed short config; + unsigned short intf_type; + unsigned short dsp_to_arm_buf_id; + unsigned short arm_to_dsp_buf_id; + unsigned short arm_to_dsp_buf_len; +} __attribute__((packed)); + + +/* + * Commands Related to uPAudPPCmd3Queue + */ + +/* + * Command Structure to configure post processing params (Commmon) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS 0x0000 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_common) + +#define AUDPP_CMD_OBJ0_UPDATE 0x8000 +#define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000 + + +#define AUDPP_CMD_OBJ2_UPDATE 0x8000 +#define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ3_UPDATE 0x8000 +#define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ4_UPDATE 0x8000 +#define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_HPCM_UPDATE 0x8000 +#define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_COMMON_CFG_UPDATE 0x8000 +#define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_POPP_STREAM 0xFFFF +#define AUDPP_CMD_COPP_STREAM 0x0000 + +struct audpp_cmd_cfg_object_params_common{ + unsigned short cmd_id; + unsigned short stream; + unsigned short stream_id; + unsigned short obj_cfg; + unsigned short command_type; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing params (Volume) + */ +#define AUDPP_CMD_VOLUME_PAN 0 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_VOLUME_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_volume) + +struct audpp_cmd_cfg_object_params_volume { + struct audpp_cmd_cfg_object_params_common common; + unsigned short volume; + unsigned short pan; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing params (PCM Filter) + */ + +struct numerator { + unsigned short numerator_b0_filter_lsw; + unsigned short numerator_b0_filter_msw; + unsigned short numerator_b1_filter_lsw; + unsigned short numerator_b1_filter_msw; + unsigned short numerator_b2_filter_lsw; + unsigned short numerator_b2_filter_msw; +} __attribute__((packed)); + +struct denominator { + unsigned short denominator_a0_filter_lsw; + unsigned short denominator_a0_filter_msw; + unsigned short denominator_a1_filter_lsw; + unsigned short denominator_a1_filter_msw; +} __attribute__((packed)); + +struct shift_factor { + unsigned short shift_factor_0; +} __attribute__((packed)); + +struct pan { + unsigned short pan_filter_0; +} __attribute__((packed)); + +struct filter_1 { + struct numerator numerator_filter; + struct denominator denominator_filter; + struct shift_factor shift_factor_filter; + struct pan pan_filter; +} __attribute__((packed)); + +struct filter_2 { + struct numerator numerator_filter[2]; + struct denominator denominator_filter[2]; + struct shift_factor shift_factor_filter[2]; + struct pan pan_filter[2]; +} __attribute__((packed)); + +struct filter_3 { + struct numerator numerator_filter[3]; + struct denominator denominator_filter[3]; + struct shift_factor shift_factor_filter[3]; + struct pan pan_filter[3]; +} __attribute__((packed)); + +struct filter_4 { + struct numerator numerator_filter[4]; + struct denominator denominator_filter[4]; + struct shift_factor shift_factor_filter[4]; + struct pan pan_filter[4]; +} __attribute__((packed)); + +#define AUDPP_CMD_IIR_TUNING_FILTER 1 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_PCM_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_pcm) + + +struct audpp_cmd_cfg_object_params_pcm { + struct audpp_cmd_cfg_object_params_common common; + signed short active_flag; + unsigned short num_bands; + union { + struct filter_1 filter_1_params; + struct filter_2 filter_2_params; + struct filter_3 filter_3_params; + struct filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)); + +#define AUDPP_CMD_CALIB_GAIN_RX 15 +#define AUDPP_CMD_CFG_CAL_GAIN_LEN sizeof(struct audpp_cmd_cfg_cal_gain) + + +struct audpp_cmd_cfg_cal_gain { + struct audpp_cmd_cfg_object_params_common common; + unsigned short audppcalgain; + unsigned short reserved; +} __attribute__((packed)); + + +/* + * Command Structure to configure post processing parameters (equalizer) + */ +#define AUDPP_CMD_EQUALIZER 2 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_eqalizer) + +struct eq_numerator { + unsigned short numerator_coeff_0_lsw; + unsigned short numerator_coeff_0_msw; + unsigned short numerator_coeff_1_lsw; + unsigned short numerator_coeff_1_msw; + unsigned short numerator_coeff_2_lsw; + unsigned short numerator_coeff_2_msw; +} __attribute__((packed)); + +struct eq_denominator { + unsigned short denominator_coeff_0_lsw; + unsigned short denominator_coeff_0_msw; + unsigned short denominator_coeff_1_lsw; + unsigned short denominator_coeff_1_msw; +} __attribute__((packed)); + +struct eq_shiftfactor { + unsigned short shift_factor; +} __attribute__((packed)); + +struct eq_coeff_1 { + struct eq_numerator numerator; + struct eq_denominator denominator; + struct eq_shiftfactor shiftfactor; +} __attribute__((packed)); + +struct eq_coeff_2 { + struct eq_numerator numerator[2]; + struct eq_denominator denominator[2]; + struct eq_shiftfactor shiftfactor[2]; +} __attribute__((packed)); + +struct eq_coeff_3 { + struct eq_numerator numerator[3]; + struct eq_denominator denominator[3]; + struct eq_shiftfactor shiftfactor[3]; +} __attribute__((packed)); + +struct eq_coeff_4 { + struct eq_numerator numerator[4]; + struct eq_denominator denominator[4]; + struct eq_shiftfactor shiftfactor[4]; +} __attribute__((packed)); + +struct eq_coeff_5 { + struct eq_numerator numerator[5]; + struct eq_denominator denominator[5]; + struct eq_shiftfactor shiftfactor[5]; +} __attribute__((packed)); + +struct eq_coeff_6 { + struct eq_numerator numerator[6]; + struct eq_denominator denominator[6]; + struct eq_shiftfactor shiftfactor[6]; +} __attribute__((packed)); + +struct eq_coeff_7 { + struct eq_numerator numerator[7]; + struct eq_denominator denominator[7]; + struct eq_shiftfactor shiftfactor[7]; +} __attribute__((packed)); + +struct eq_coeff_8 { + struct eq_numerator numerator[8]; + struct eq_denominator denominator[8]; + struct eq_shiftfactor shiftfactor[8]; +} __attribute__((packed)); + +struct eq_coeff_9 { + struct eq_numerator numerator[9]; + struct eq_denominator denominator[9]; + struct eq_shiftfactor shiftfactor[9]; +} __attribute__((packed)); + +struct eq_coeff_10 { + struct eq_numerator numerator[10]; + struct eq_denominator denominator[10]; + struct eq_shiftfactor shiftfactor[10]; +} __attribute__((packed)); + +struct eq_coeff_11 { + struct eq_numerator numerator[11]; + struct eq_denominator denominator[11]; + struct eq_shiftfactor shiftfactor[11]; +} __attribute__((packed)); + +struct eq_coeff_12 { + struct eq_numerator numerator[12]; + struct eq_denominator denominator[12]; + struct eq_shiftfactor shiftfactor[12]; +} __attribute__((packed)); + + +struct audpp_cmd_cfg_object_params_eqalizer { + struct audpp_cmd_cfg_object_params_common common; + signed short eq_flag; + unsigned short num_bands; + union { + struct eq_coeff_1 eq_coeffs_1; + struct eq_coeff_2 eq_coeffs_2; + struct eq_coeff_3 eq_coeffs_3; + struct eq_coeff_4 eq_coeffs_4; + struct eq_coeff_5 eq_coeffs_5; + struct eq_coeff_6 eq_coeffs_6; + struct eq_coeff_7 eq_coeffs_7; + struct eq_coeff_8 eq_coeffs_8; + struct eq_coeff_9 eq_coeffs_9; + struct eq_coeff_10 eq_coeffs_10; + struct eq_coeff_11 eq_coeffs_11; + struct eq_coeff_12 eq_coeffs_12; + } __attribute__((packed)) eq_coeff; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters (ADRC) + */ +#define AUDPP_CMD_ADRC 3 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_adrc) + + +#define AUDPP_CMD_ADRC_FLAG_DIS 0x0000 +#define AUDPP_CMD_ADRC_FLAG_ENA -1 +#define AUDPP_CMD_PBE_FLAG_DIS 0x0000 +#define AUDPP_CMD_PBE_FLAG_ENA -1 + +struct audpp_cmd_cfg_object_params_adrc { + struct audpp_cmd_cfg_object_params_common common; + signed short adrc_flag; + unsigned short compression_th; + unsigned short compression_slope; + unsigned short rms_time; + unsigned short attack_const_lsw; + unsigned short attack_const_msw; + unsigned short release_const_lsw; + unsigned short release_const_msw; + unsigned short adrc_delay; +}; + +/* + * Command Structure to configure post processing parameters (MB - ADRC) + */ +#define AUDPP_CMD_MBADRC 10 +#define AUDPP_MAX_MBADRC_BANDS 5 + +struct adrc_config { + uint16_t subband_enable; + uint16_t adrc_sub_mute; + uint16_t rms_time; + uint16_t compression_th; + uint16_t compression_slope; + uint16_t attack_const_lsw; + uint16_t attack_const_msw; + uint16_t release_const_lsw; + uint16_t release_const_msw; + uint16_t makeup_gain; +}; + +struct audpp_cmd_cfg_object_params_mbadrc { + struct audpp_cmd_cfg_object_params_common common; + uint16_t enable; + uint16_t num_bands; + uint16_t down_samp_level; + uint16_t adrc_delay; + uint16_t ext_buf_size; + uint16_t ext_partition; + uint16_t ext_buf_msw; + uint16_t ext_buf_lsw; + struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS]; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters(Spectrum Analizer) + */ +#define AUDPP_CMD_SPECTROGRAM 4 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SPECTRAM_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_spectram) + + +struct audpp_cmd_cfg_object_params_spectram { + struct audpp_cmd_cfg_object_params_common common; + unsigned short sample_interval; + unsigned short num_coeff; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters (QConcert) + */ +#define AUDPP_CMD_QCONCERT 5 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_qconcert) + + +#define AUDPP_CMD_QCON_ENA_FLAG_ENA -1 +#define AUDPP_CMD_QCON_ENA_FLAG_DIS 0x0000 + +#define AUDPP_CMD_QCON_OP_MODE_HEADPHONE -1 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_FRONT 0x0000 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_SIDE 0x0001 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_DESKTOP 0x0002 + +#define AUDPP_CMD_QCON_GAIN_UNIT 0x7FFF +#define AUDPP_CMD_QCON_GAIN_SIX_DB 0x4027 + + +#define AUDPP_CMD_QCON_EXPANSION_MAX 0x7FFF + + +struct audpp_cmd_cfg_object_params_qconcert { + struct audpp_cmd_cfg_object_params_common common; + signed short enable_flag; + signed short op_mode; + signed short gain; + signed short expansion; + signed short delay; + unsigned short stages_per_mode; + unsigned short reverb_enable; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_time_ratio_msw; + unsigned short decay_time_ratio_lsw; + unsigned short reflection_delay_time; + unsigned short late_reverb_gain; + unsigned short late_reverb_delay; + unsigned short delay_buff_size_msw; + unsigned short delay_buff_size_lsw; + unsigned short partition_num; + unsigned short delay_buff_start_msw; + unsigned short delay_buff_start_lsw; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters (Side Chain) + */ +#define AUDPP_CMD_SIDECHAIN_TUNING_FILTER 6 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_sidechain) + + +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_DIS 0x0000 +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_ENA -1 + +struct audpp_cmd_cfg_object_params_sidechain { + struct audpp_cmd_cfg_object_params_common common; + signed short active_flag; + unsigned short num_bands; + union { + struct filter_1 filter_1_params; + struct filter_2 filter_2_params; + struct filter_3 filter_3_params; + struct filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)); + + +/* + * Command Structure to configure post processing parameters (QAFX) + */ +#define AUDPP_CMD_QAFX 8 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QAFX_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_qafx) + +#define AUDPP_CMD_QAFX_ENA_DISA 0x0000 +#define AUDPP_CMD_QAFX_ENA_ENA_CFG -1 +#define AUDPP_CMD_QAFX_ENA_DIS_CFG 0x0001 + +#define AUDPP_CMD_QAFX_CMD_TYPE_ENV 0x0100 +#define AUDPP_CMD_QAFX_CMD_TYPE_OBJ 0x0010 +#define AUDPP_CMD_QAFX_CMD_TYPE_QUERY 0x1000 + +#define AUDPP_CMD_QAFX_CMDS_ENV_OP_MODE 0x0100 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_POS 0x0101 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_ORI 0x0102 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_VEL 0X0103 +#define AUDPP_CMD_QAFX_CMDS_ENV_ENV_RES 0x0107 + +#define AUDPP_CMD_QAFX_CMDS_OBJ_SAMP_FREQ 0x0010 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VOL 0x0011 +#define AUDPP_CMD_QAFX_CMDS_OBJ_DIST 0x0012 +#define AUDPP_CMD_QAFX_CMDS_OBJ_POS 0x0013 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VEL 0x0014 + + +struct audpp_cmd_cfg_object_params_qafx { + struct audpp_cmd_cfg_object_params_common common; + signed short enable; + unsigned short command_type; + unsigned short num_commands; + unsigned short commands; +} __attribute__((packed)); + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (REVERB) (Common) + */ + +#define AUDPP_CMD_REVERB_CONFIG 0x0001 +#define AUDPP_CMD_REVERB_CONFIG_COMMON_LEN \ + sizeof(struct audpp_cmd_reverb_config_common) + +#define AUDPP_CMD_ENA_ENA 0xFFFF +#define AUDPP_CMD_ENA_DIS 0x0000 +#define AUDPP_CMD_ENA_CFG 0x0001 + +#define AUDPP_CMD_CMD_TYPE_ENV 0x0104 +#define AUDPP_CMD_CMD_TYPE_OBJ 0x0015 +#define AUDPP_CMD_CMD_TYPE_QUERY 0x1000 + + +struct audpp_cmd_reverb_config_common { + unsigned short cmd_id; + unsigned short enable; + unsigned short cmd_type; +} __attribute__((packed)); + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0104) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_104_LEN \ + sizeof(struct audpp_cmd_reverb_config_env_104) + +struct audpp_cmd_reverb_config_env_104 { + struct audpp_cmd_reverb_config_common common; + unsigned short env_gain; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_timeratio_msw; + unsigned short decay_timeratio_lsw; + unsigned short delay_time; + unsigned short reverb_gain; + unsigned short reverb_delay; +} __attribute__((packed)); + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0015) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_15_LEN \ + sizeof(struct audpp_cmd_reverb_config_env_15) + +struct audpp_cmd_reverb_config_env_15 { + struct audpp_cmd_reverb_config_common common; + unsigned short object_num; + unsigned short absolute_gain; +} __attribute__((packed)); + +#define AUDPP_CMD_PBE 16 +#define AUDPP_CMD_CFG_PBE_LEN sizeof(struct audpp_cmd_cfg_pbe) + +struct audpp_cmd_cfg_pbe { + struct audpp_cmd_cfg_object_params_common common; + unsigned short pbe_enable; + signed short realbassmix; + signed short basscolorcontrol; + unsigned short mainchaindelay; + unsigned short xoverfltorder; + unsigned short bandpassfltorder; + signed short adrcdelay; + unsigned short downsamplelevel; + unsigned short comprmstav; + signed short expthreshold; + unsigned short expslope; + unsigned short compthreshold; + unsigned short compslope; + unsigned short cpmpattack_lsw; + unsigned short compattack_msw; + unsigned short comprelease_lsw; + unsigned short comprelease_msw; + unsigned short compmakeupgain; + signed short baselimthreshold; + signed short highlimthreshold; + signed short basslimmakeupgain; + signed short highlimmakeupgain; + signed short limbassgrc; + signed short limhighgrc; + signed short limdelay; + unsigned short filter_coeffs[90]; + unsigned short extbuffsize_lsw; + unsigned short extbuffsize_msw; + unsigned short extpartition; + unsigned short extbuffstart_lsw; + unsigned short extbuffstart_msw; +} __attribute__((packed)); + +#define AUDPP_CMD_PP_FEAT_QUERY_PARAMS 0x0002 + +struct audpp_cmd_cfg_object_params_volpan { + struct audpp_cmd_cfg_object_params_common common; + u16 volume ; + u16 pan; +}; + +struct rtc_audpp_read_data { + unsigned short cmd_id; + unsigned short obj_id; + unsigned short route_id; + unsigned short feature_id; + unsigned short extbufsizemsw; + unsigned short extbufsizelsw; + unsigned short extpart; + unsigned short extbufstartmsw; + unsigned short extbufstartlsw; +} __attribute__((packed)) ; + +#define AUDPP_CMD_SAMPLING_FREQUENCY 7 +#define AUDPP_CMD_QRUMBLE 9 + +#endif /* __MACH_QDSP5_V2_QDSP5AUDPPCMDI_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audppmsg.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audppmsg.h new file mode 100644 index 00000000000..b27bd8351eb --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audppmsg.h @@ -0,0 +1,311 @@ +#ifndef QDSP5AUDPPMSG_H +#define QDSP5AUDPPMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P O S T P R O C E S S I N G M S G + +GENERAL DESCRIPTION + Messages sent by AUDPPTASK to ARM + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/* + * AUDPPTASK uses audPPuPRlist to send messages to the ARM + * Location : MEMA + * Buffer Size : 45 + * No of Buffers in a queue : 5 for gaming audio and 1 for other images + */ + +/* + * MSG to Informs the ARM os Success/Failure of bringing up the decoder + */ + +#define AUDPP_MSG_FEAT_QUERY_DM_DONE 0x000b + +#define AUDPP_MSG_STATUS_MSG 0x0001 +#define AUDPP_MSG_STATUS_MSG_LEN \ + sizeof(struct audpp_msg_status_msg) + +#define AUDPP_MSG_STATUS_SLEEP 0x0000 +#define AUDPP_MSG_STATUS_INIT 0x0001 +#define AUDPP_MSG_STATUS_CFG 0x0002 +#define AUDPP_MSG_STATUS_PLAY 0x0003 + +#define AUDPP_MSG_REASON_NONE 0x0000 +#define AUDPP_MSG_REASON_MEM 0x0001 +#define AUDPP_MSG_REASON_NODECODER 0x0002 + +struct audpp_msg_status_msg { + unsigned short dec_id; + unsigned short status; + unsigned short reason; +} __attribute__((packed)); + +/* + * MSG to communicate the spectrum analyzer output bands to the ARM + */ +#define AUDPP_MSG_SPA_BANDS 0x0002 +#define AUDPP_MSG_SPA_BANDS_LEN \ + sizeof(struct audpp_msg_spa_bands) + +struct audpp_msg_spa_bands { + unsigned short current_object; + unsigned short spa_band_1; + unsigned short spa_band_2; + unsigned short spa_band_3; + unsigned short spa_band_4; + unsigned short spa_band_5; + unsigned short spa_band_6; + unsigned short spa_band_7; + unsigned short spa_band_8; + unsigned short spa_band_9; + unsigned short spa_band_10; + unsigned short spa_band_11; + unsigned short spa_band_12; + unsigned short spa_band_13; + unsigned short spa_band_14; + unsigned short spa_band_15; + unsigned short spa_band_16; + unsigned short spa_band_17; + unsigned short spa_band_18; + unsigned short spa_band_19; + unsigned short spa_band_20; + unsigned short spa_band_21; + unsigned short spa_band_22; + unsigned short spa_band_23; + unsigned short spa_band_24; + unsigned short spa_band_25; + unsigned short spa_band_26; + unsigned short spa_band_27; + unsigned short spa_band_28; + unsigned short spa_band_29; + unsigned short spa_band_30; + unsigned short spa_band_31; + unsigned short spa_band_32; +} __attribute__((packed)); + +/* + * MSG to communicate the PCM I/O buffer status to ARM + */ +#define AUDPP_MSG_HOST_PCM_INTF_MSG 0x0003 +#define AUDPP_MSG_HOST_PCM_INTF_MSG_LEN \ + sizeof(struct audpp_msg_host_pcm_intf_msg) + +#define AUDPP_MSG_HOSTPCM_ID_TX_ARM 0x0000 +#define AUDPP_MSG_HOSTPCM_ID_ARM_TX 0x0001 +#define AUDPP_MSG_HOSTPCM_ID_RX_ARM 0x0002 +#define AUDPP_MSG_HOSTPCM_ID_ARM_RX 0x0003 + +#define AUDPP_MSG_SAMP_FREQ_INDX_96000 0x0000 +#define AUDPP_MSG_SAMP_FREQ_INDX_88200 0x0001 +#define AUDPP_MSG_SAMP_FREQ_INDX_64000 0x0002 +#define AUDPP_MSG_SAMP_FREQ_INDX_48000 0x0003 +#define AUDPP_MSG_SAMP_FREQ_INDX_44100 0x0004 +#define AUDPP_MSG_SAMP_FREQ_INDX_32000 0x0005 +#define AUDPP_MSG_SAMP_FREQ_INDX_24000 0x0006 +#define AUDPP_MSG_SAMP_FREQ_INDX_22050 0x0007 +#define AUDPP_MSG_SAMP_FREQ_INDX_16000 0x0008 +#define AUDPP_MSG_SAMP_FREQ_INDX_12000 0x0009 +#define AUDPP_MSG_SAMP_FREQ_INDX_11025 0x000A +#define AUDPP_MSG_SAMP_FREQ_INDX_8000 0x000B + +#define AUDPP_MSG_CHANNEL_MODE_MONO 0x0001 +#define AUDPP_MSG_CHANNEL_MODE_STEREO 0x0002 + +struct audpp_msg_host_pcm_intf_msg { + unsigned short obj_num; + unsigned short numbers_of_samples; + unsigned short host_pcm_id; + unsigned short buf_indx; + unsigned short samp_freq_indx; + unsigned short channel_mode; +} __attribute__((packed)); + + +/* + * MSG to communicate 3D position of the source and listener , source volume + * source rolloff, source orientation + */ + +#define AUDPP_MSG_QAFX_POS 0x0004 +#define AUDPP_MSG_QAFX_POS_LEN \ + sizeof(struct audpp_msg_qafx_pos) + +struct audpp_msg_qafx_pos { + unsigned short current_object; + unsigned short x_pos_lis_msw; + unsigned short x_pos_lis_lsw; + unsigned short y_pos_lis_msw; + unsigned short y_pos_lis_lsw; + unsigned short z_pos_lis_msw; + unsigned short z_pos_lis_lsw; + unsigned short x_fwd_msw; + unsigned short x_fwd_lsw; + unsigned short y_fwd_msw; + unsigned short y_fwd_lsw; + unsigned short z_fwd_msw; + unsigned short z_fwd_lsw; + unsigned short x_up_msw; + unsigned short x_up_lsw; + unsigned short y_up_msw; + unsigned short y_up_lsw; + unsigned short z_up_msw; + unsigned short z_up_lsw; + unsigned short x_vel_lis_msw; + unsigned short x_vel_lis_lsw; + unsigned short y_vel_lis_msw; + unsigned short y_vel_lis_lsw; + unsigned short z_vel_lis_msw; + unsigned short z_vel_lis_lsw; + unsigned short threed_enable_flag; + unsigned short volume; + unsigned short x_pos_source_msw; + unsigned short x_pos_source_lsw; + unsigned short y_pos_source_msw; + unsigned short y_pos_source_lsw; + unsigned short z_pos_source_msw; + unsigned short z_pos_source_lsw; + unsigned short max_dist_0_msw; + unsigned short max_dist_0_lsw; + unsigned short min_dist_0_msw; + unsigned short min_dist_0_lsw; + unsigned short roll_off_factor; + unsigned short mute_after_max_flag; + unsigned short x_vel_source_msw; + unsigned short x_vel_source_lsw; + unsigned short y_vel_source_msw; + unsigned short y_vel_source_lsw; + unsigned short z_vel_source_msw; + unsigned short z_vel_source_lsw; +} __attribute__((packed)); + +/* + * MSG to provide AVSYNC feedback from DSP to ARM + */ + +#define AUDPP_MSG_AVSYNC_MSG 0x0005 +#define AUDPP_MSG_AVSYNC_MSG_LEN \ + sizeof(struct audpp_msg_avsync_msg) + +struct audpp_msg_avsync_msg { + unsigned short active_flag; + unsigned short num_samples_counter0_HSW; + unsigned short num_samples_counter0_MSW; + unsigned short num_samples_counter0_LSW; + unsigned short num_bytes_counter0_HSW; + unsigned short num_bytes_counter0_MSW; + unsigned short num_bytes_counter0_LSW; + unsigned short samp_freq_obj_0; + unsigned short samp_freq_obj_1; + unsigned short samp_freq_obj_2; + unsigned short samp_freq_obj_3; + unsigned short samp_freq_obj_4; + unsigned short samp_freq_obj_5; + unsigned short samp_freq_obj_6; + unsigned short samp_freq_obj_7; + unsigned short samp_freq_obj_8; + unsigned short samp_freq_obj_9; + unsigned short samp_freq_obj_10; + unsigned short samp_freq_obj_11; + unsigned short samp_freq_obj_12; + unsigned short samp_freq_obj_13; + unsigned short samp_freq_obj_14; + unsigned short samp_freq_obj_15; + unsigned short num_samples_counter4_HSW; + unsigned short num_samples_counter4_MSW; + unsigned short num_samples_counter4_LSW; + unsigned short num_bytes_counter4_HSW; + unsigned short num_bytes_counter4_MSW; + unsigned short num_bytes_counter4_LSW; +} __attribute__((packed)); + +/* + * MSG to provide PCM DMA Missed feedback from the DSP to ARM + */ + +#define AUDPP_MSG_PCMDMAMISSED 0x0006 +#define AUDPP_MSG_PCMDMAMISSED_LEN \ + sizeof(struct audpp_msg_pcmdmamissed); + +struct audpp_msg_pcmdmamissed { + /* + ** Bit 0 0 = PCM DMA not missed for object 0 + ** 1 = PCM DMA missed for object0 + ** Bit 1 0 = PCM DMA not missed for object 1 + ** 1 = PCM DMA missed for object1 + ** Bit 2 0 = PCM DMA not missed for object 2 + ** 1 = PCM DMA missed for object2 + ** Bit 3 0 = PCM DMA not missed for object 3 + ** 1 = PCM DMA missed for object3 + ** Bit 4 0 = PCM DMA not missed for object 4 + ** 1 = PCM DMA missed for object4 + */ + unsigned short pcmdmamissed; +} __attribute__((packed)); + +/* + * MSG to AUDPP enable or disable feedback form DSP to ARM + */ + +#define AUDPP_MSG_CFG_MSG 0x0007 +#define AUDPP_MSG_CFG_MSG_LEN \ + sizeof(struct audpp_msg_cfg_msg) + +#define AUDPP_MSG_ENA_ENA 0xFFFF +#define AUDPP_MSG_ENA_DIS 0x0000 + +struct audpp_msg_cfg_msg { + /* Enabled - 0xffff + ** Disabled - 0 + */ + unsigned short enabled; +} __attribute__((packed)); + +/* + * MSG to communicate the reverb per object volume + */ + +#define AUDPP_MSG_QREVERB_VOLUME 0x0008 +#define AUDPP_MSG_QREVERB_VOLUME_LEN \ + sizeof(struct audpp_msg_qreverb_volume) + + +struct audpp_msg_qreverb_volume { + unsigned short obj_0_gain; + unsigned short obj_1_gain; + unsigned short obj_2_gain; + unsigned short obj_3_gain; + unsigned short obj_4_gain; + unsigned short hpcm_obj_volume; +} __attribute__((packed)); + +#define AUDPP_MSG_ROUTING_ACK 0x0009 +#define AUDPP_MSG_ROUTING_ACK_LEN \ + sizeof(struct audpp_msg_routing_ack) + +struct audpp_msg_routing_ack { + unsigned short dec_id; + unsigned short routing_mode; +} __attribute__((packed)); + +#define AUDPP_MSG_FLUSH_ACK 0x000A + +#endif /* QDSP5AUDPPMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audpreproccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audpreproccmdi.h new file mode 100644 index 00000000000..f579e1a3d31 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audpreproccmdi.h @@ -0,0 +1,519 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef QDSP5AUDPREPROCCMDI_H +#define QDSP5AUDPREPROCCMDI_H + +/* + * AUDIOPREPROC COMMANDS: + * ARM uses uPAudPreProcAudRecCmdQueue to communicate with AUDPREPROCTASK + * Location : MEMB + * Buffer size : 7 + * Number of buffers in a queue : 4 + */ + +/* + * Command to enable or disable particular encoder for new interface + */ + +#define AUDPREPROC_AUDREC_CMD_ENC_CFG 0x0000 +#define AUDPREPROC_AUDREC_CMD_ENC_CFG_LEN \ + sizeof(struct audpreproc_audrec_cmd_enc_cfg) +#define AUDREC_TASK_0 0x00 /* SBC / PCM */ +#define AUDREC_TASK_1 0x01 /* AAC / PCM / VOICE ENC */ + +#define ENCODE_ENABLE 0x8000 + +/* encoder type supported */ +#define ENC_TYPE_WAV 0x00 +#define ENC_TYPE_AAC 0x01 +#define ENC_TYPE_SBC 0x02 +#define ENC_TYPE_AMRNB 0x03 +#define ENC_TYPE_EVRC 0x04 +#define ENC_TYPE_V13K 0x05 +#define ENC_TYPE_EXT_WAV 0x0F /* to dynamically configure frame size */ + +/* structure definitions according to + * command description of ARM-DSP interface specifications + */ +struct audpreproc_audrec_cmd_enc_cfg { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short audrec_enc_type; +} __attribute__((packed)); + +/* + * Command to configure parameters of selected Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG 0x0001 + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_COMMON_LEN \ + sizeof(struct audpreproc_audrec_cmd_param_cfg_common) + +#define DUAL_MIC_STEREO_RECORDING 2 + +struct audpreproc_audrec_cmd_param_cfg_common { + unsigned short cmd_id; + unsigned short stream_id; +} __attribute__((packed)); + +/* + * Command Structure to configure WAV Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_WAV_LEN \ + sizeof(struct audpreproc_audrec_cmd_parm_cfg_wav) + +#define AUDREC_CMD_MODE_MONO 0 +#define AUDREC_CMD_MODE_STEREO 1 + +struct audpreproc_audrec_cmd_parm_cfg_wav { + struct audpreproc_audrec_cmd_param_cfg_common common; + unsigned short aud_rec_samplerate_idx; + unsigned short aud_rec_stereo_mode; + unsigned short aud_rec_frame_size; +} __attribute__((packed)); + +/* + * Command Structure to configure AAC Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_AAC_LEN \ + sizeof(struct audpreproc_audrec_cmd_parm_cfg_aac) + +struct audpreproc_audrec_cmd_parm_cfg_aac { + struct audpreproc_audrec_cmd_param_cfg_common common; + unsigned short aud_rec_samplerate_idx; + unsigned short aud_rec_stereo_mode; + signed short recording_quality; +} __attribute__((packed)); + +/* + * Command Structure to configure SBC Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_SBC_LEN \ + sizeof(struct audpreproc_audrec_cmd_parm_cfg_sbc) + +/* encoder parameters mask definitions*/ + +#define AUDREC_SBC_ENC_PARAM_VER_MASK 0x000A +#define AUDREC_SBC_ENC_PARAM_ENAHANCED_SBC_BASELINE_VERSION 0x0000 +#define AUDREC_SBC_ENC_PARAM_ENAHANCED_SBC_NA_MASK 0x0400 +#define AUDREC_SBC_ENC_PARAM_BIT_ALLOC_MASK 0x0008 +#define AUDREC_SBC_ENC_PARAM_SNR_MASK 0x0100 +#define AUDREC_SBC_ENC_PARAM_MODE_MASK 0x0006 +#define AUDREC_SBC_ENC_PARAM_MODE_DUAL_MASK 0x0040 +#define AUDREC_SBC_ENC_PARAM_MODE_STEREO_MASK 0x0080 +#define AUDREC_SBC_ENC_PARAM_MODE_JOINT_STEREO_MASK 0x00C0 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BANDS_MASK 0x0004 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BANDS_8_MASK 0x0001 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_MASK 0x0000 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_4_MASK 0x0000 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_8_MASK 0x0001 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_12_MASK 0x0002 +#define AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_16_MASK 0x0003 + +struct audpreproc_audrec_cmd_parm_cfg_sbc { + struct audpreproc_audrec_cmd_param_cfg_common common; + unsigned short aud_rec_sbc_enc_param; + unsigned short aud_rec_sbc_bit_rate_msw; + unsigned short aud_rec_sbc_bit_rate_lsw; +} __attribute__((packed)); + +/* + * Command Structure to configure AMRNB Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_AMRNB_LEN \ + sizeof(struct audpreproc_audrec_cmd_parm_cfg_amrnb) + +#define AMRNB_DTX_MODE_ENABLE -1 +#define AMRNB_DTX_MODE_DISABLE 0 + +#define AMRNB_TEST_MODE_ENABLE -1 +#define AMRNB_TEST_MODE_DISABLE 0 + +#define AMRNB_USED_MODE_MR475 0x0 +#define AMRNB_USED_MODE_MR515 0x1 +#define AMRNB_USED_MODE_MR59 0x2 +#define AMRNB_USED_MODE_MR67 0x3 +#define AMRNB_USED_MODE_MR74 0x4 +#define AMRNB_USED_MODE_MR795 0x5 +#define AMRNB_USED_MODE_MR102 0x6 +#define AMRNB_USED_MODE_MR122 0x7 + +struct audpreproc_audrec_cmd_parm_cfg_amrnb { + struct audpreproc_audrec_cmd_param_cfg_common common; + signed short dtx_mode; + signed short test_mode; + unsigned short used_mode; +} __attribute__((packed)) ; + +/* + * Command Structure to configure EVRC Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_EVRC_LEN \ + sizeof(struct audpreproc_audrec_cmd_parm_cfg_evrc) + +struct audpreproc_audrec_cmd_parm_cfg_evrc { + struct audpreproc_audrec_cmd_param_cfg_common common; + unsigned short enc_min_rate; + unsigned short enc_max_rate; + unsigned short rate_modulation_cmd; +} __attribute__((packed)); + +/* + * Command Structure to configure QCELP_13K Encoder + */ + +#define AUDPREPROC_AUDREC_CMD_PARAM_CFG_QCELP13K_LEN \ + sizeof(struct audpreproc_audrec_cmd_parm_cfg_qcelp13k) + +struct audpreproc_audrec_cmd_parm_cfg_qcelp13k { + struct audpreproc_audrec_cmd_param_cfg_common common; + unsigned short enc_min_rate; + unsigned short enc_max_rate; + unsigned short rate_modulation_cmd; + unsigned short reduced_rate_level; +} __attribute__((packed)); + +/* + * Command to configure AFE for recording paths + */ +#define AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG 0x0002 + +#define AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_LEN \ + sizeof(struct audpreproc_afe_cmd_audio_record_cfg) + +#define AUDIO_RECORDING_TURN_ON 0xFFFF +#define AUDIO_RECORDING_TURN_OFF 0x0000 + +#define AUDPP_A2DP_PIPE_SOURCE_MIX_MASK 0x0020 +#define VOICE_DL_SOURCE_MIX_MASK 0x0010 +#define VOICE_UL_SOURCE_MIX_MASK 0x0008 +#define FM_SOURCE_MIX_MASK 0x0004 +#define AUX_CODEC_TX_SOURCE_MIX_MASK 0x0002 +#define INTERNAL_CODEC_TX_SOURCE_MIX_MASK 0x0001 + +struct audpreproc_afe_cmd_audio_record_cfg { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short destination_activity; + unsigned short source_mix_mask; + unsigned short pipe_id; + unsigned short reserved; +} __attribute__((packed)); + +/* + * Command to configure Tunnel(RT) or Non-Tunnel(FTRT) mode + */ +#define AUDPREPROC_AUDREC_CMD_ROUTING_MODE 0x0003 +#define AUDPREPROC_AUDREC_CMD_ROUTING_MODE_LEN \ + sizeof(struct audpreproc_audrec_cmd_routing_mode) + +#define AUDIO_ROUTING_MODE_FTRT 0x0001 +#define AUDIO_ROUTING_MODE_RT 0x0002 + +struct audpreproc_audrec_cmd_routing_mode { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short routing_mode; +} __attribute__((packed)); + +/* + * Command to configure DSP for topology where resampler moved + * in front of pre processing chain + */ +#define AUDPREPROC_AUDREC_CMD_ENC_CFG_2 0x0004 +#define AUDPREPROC_AUDREC_CMD_ENC_CFG_2_LEN \ + sizeof(struct audpreproc_audrec_cmd_enc_cfg_2) + + +struct audpreproc_audrec_cmd_enc_cfg_2 { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short audrec_enc_type; +} __attribute__((packed)); + +/* + * AUDIOPREPROC COMMANDS: + * ARM uses uPAudPreProcCmdQueue to communicate with AUDPREPROCTASK + * Location : MEMB + * Buffer size : 52 + * Number of buffers in a queue : 3 + */ + +/* + * Command to configure the parameters of AGC + */ + +#define AUDPREPROC_CMD_CFG_AGC_PARAMS 0x0000 +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_LEN \ + sizeof(struct audpreproc_cmd_cfg_agc_params) + +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE 0x0200 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH 0x0400 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE 0x0800 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH 0x1000 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG 0x2000 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN 0x4000 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG 0x8000 + +#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA -1 +#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS 0x0000 + +#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_ADP_GAIN -1 +#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_STATIC_GAIN 0x0000 + +#define AUDPREPROC_CMD_PARAM_MASK_RMS_TAY 0x0010 +#define AUDPREPROC_CMD_PARAM_MASK_RELEASEK 0x0020 +#define AUDPREPROC_CMD_PARAM_MASK_DELAY 0x0040 +#define AUDPREPROC_CMD_PARAM_MASK_ATTACKK 0x0080 +#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW 0x0100 +#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST 0x0200 +#define AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK 0x0400 +#define AUDPREPROC_CMD_PARAM_MASK_AIG_MIN 0x0800 +#define AUDPREPROC_CMD_PARAM_MASK_AIG_MAX 0x1000 +#define AUDPREPROC_CMD_PARAM_MASK_LEAK_UP 0x2000 +#define AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN 0x4000 +#define AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK 0x8000 + +struct audpreproc_cmd_cfg_agc_params { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short tx_agc_param_mask; + signed short tx_agc_enable_flag; + unsigned short comp_rlink_static_gain; + signed short comp_rlink_aig_flag; + unsigned short expander_rlink_th; + unsigned short expander_rlink_slope; + unsigned short compressor_rlink_th; + unsigned short compressor_rlink_slope; + unsigned short tx_adc_agc_param_mask; + unsigned short comp_rlink_aig_attackk; + unsigned short comp_rlink_aig_leak_down; + unsigned short comp_rlink_aig_leak_up; + unsigned short comp_rlink_aig_max; + unsigned short comp_rlink_aig_min; + unsigned short comp_rlink_aig_releasek; + unsigned short comp_rlink_aig_leakrate_fast; + unsigned short comp_rlink_aig_leakrate_slow; + unsigned short comp_rlink_attackk_msw; + unsigned short comp_rlink_attackk_lsw; + unsigned short comp_rlink_delay; + unsigned short comp_rlink_releasek_msw; + unsigned short comp_rlink_releasek_lsw; + unsigned short comp_rlink_rms_tav; +} __attribute__((packed)); + +/* + * Command to configure the params of Advanved AGC + */ + +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2 0x0001 +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2_LEN \ + sizeof(struct audpreproc_cmd_cfg_agc_params_2) + +#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_ENA -1; +#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_DIS 0x0000; + +struct audpreproc_cmd_cfg_agc_params_2 { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short agc_param_mask; + signed short tx_agc_enable_flag; + unsigned short comp_rlink_static_gain; + unsigned short exp_rlink_th; + unsigned short exp_rlink_slope; + unsigned short comp_rlink_th; + unsigned short comp_rlink_slope; + unsigned short comp_rlink_rms_tav; + unsigned short comp_rlink_down_samp_mask; + unsigned short comp_rlink_attackk_msw; + unsigned short comp_rlink_attackk_lsw; + unsigned short comp_rlink_releasek_msw; + unsigned short comp_rlink_releasek_lsw; + unsigned short comp_rlink_delay; + unsigned short comp_rlink_makeup_gain; +} __attribute__((packed)); + +/* + * Command to configure params for ns + */ + +#define AUDPREPROC_CMD_CFG_NS_PARAMS 0x0002 +#define AUDPREPROC_CMD_CFG_NS_PARAMS_LEN \ + sizeof(struct audpreproc_cmd_cfg_ns_params) + +#define AUDPREPROC_CMD_EC_MODE_NLMS_ENA 0x0001 +#define AUDPREPROC_CMD_EC_MODE_NLMS_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_DES_ENA 0x0002 +#define AUDPREPROC_CMD_EC_MODE_DES_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NS_ENA 0x0004 +#define AUDPREPROC_CMD_EC_MODE_NS_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_CNI_ENA 0x0008 +#define AUDPREPROC_CMD_EC_MODE_CNI_DIS 0x0000 + +#define AUDPREPROC_CMD_EC_MODE_NLES_ENA 0x0010 +#define AUDPREPROC_CMD_EC_MODE_NLES_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_HB_ENA 0x0020 +#define AUDPREPROC_CMD_EC_MODE_HB_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_VA_ENA 0x0040 +#define AUDPREPROC_CMD_EC_MODE_VA_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_PCD_ENA 0x0080 +#define AUDPREPROC_CMD_EC_MODE_PCD_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_FEHI_ENA 0x0100 +#define AUDPREPROC_CMD_EC_MODE_FEHI_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEHI_ENA 0x0200 +#define AUDPREPROC_CMD_EC_MODE_NEHI_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NLPP_ENA 0x0400 +#define AUDPREPROC_CMD_EC_MODE_NLPP_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_FNE_ENA 0x0800 +#define AUDPREPROC_CMD_EC_MODE_FNE_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_PRENLMS_ENA 0x1000 +#define AUDPREPROC_CMD_EC_MODE_PRENLMS_DIS 0x0000 + +struct audpreproc_cmd_cfg_ns_params { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short ec_mode_new; + unsigned short dens_gamma_n; + unsigned short dens_nfe_block_size; + unsigned short dens_limit_ns; + unsigned short dens_limit_ns_d; + unsigned short wb_gamma_e; + unsigned short wb_gamma_n; +} __attribute__((packed)); + +/* + * Command to configure parameters for IIR tuning filter + */ + +#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS 0x0003 +#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS_LEN \ + sizeof(struct audpreproc_cmd_cfg_iir_tuning_filter_params) + +#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS 0x0000 +#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA 0x0001 + +struct audpreproc_cmd_cfg_iir_tuning_filter_params { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short active_flag; + unsigned short num_bands; + + unsigned short numerator_coeff_b0_filter0_lsw; + unsigned short numerator_coeff_b0_filter0_msw; + unsigned short numerator_coeff_b1_filter0_lsw; + unsigned short numerator_coeff_b1_filter0_msw; + unsigned short numerator_coeff_b2_filter0_lsw; + unsigned short numerator_coeff_b2_filter0_msw; + + unsigned short numerator_coeff_b0_filter1_lsw; + unsigned short numerator_coeff_b0_filter1_msw; + unsigned short numerator_coeff_b1_filter1_lsw; + unsigned short numerator_coeff_b1_filter1_msw; + unsigned short numerator_coeff_b2_filter1_lsw; + unsigned short numerator_coeff_b2_filter1_msw; + + unsigned short numerator_coeff_b0_filter2_lsw; + unsigned short numerator_coeff_b0_filter2_msw; + unsigned short numerator_coeff_b1_filter2_lsw; + unsigned short numerator_coeff_b1_filter2_msw; + unsigned short numerator_coeff_b2_filter2_lsw; + unsigned short numerator_coeff_b2_filter2_msw; + + unsigned short numerator_coeff_b0_filter3_lsw; + unsigned short numerator_coeff_b0_filter3_msw; + unsigned short numerator_coeff_b1_filter3_lsw; + unsigned short numerator_coeff_b1_filter3_msw; + unsigned short numerator_coeff_b2_filter3_lsw; + unsigned short numerator_coeff_b2_filter3_msw; + + unsigned short denominator_coeff_a0_filter0_lsw; + unsigned short denominator_coeff_a0_filter0_msw; + unsigned short denominator_coeff_a1_filter0_lsw; + unsigned short denominator_coeff_a1_filter0_msw; + + unsigned short denominator_coeff_a0_filter1_lsw; + unsigned short denominator_coeff_a0_filter1_msw; + unsigned short denominator_coeff_a1_filter1_lsw; + unsigned short denominator_coeff_a1_filter1_msw; + + unsigned short denominator_coeff_a0_filter2_lsw; + unsigned short denominator_coeff_a0_filter2_msw; + unsigned short denominator_coeff_a1_filter2_lsw; + unsigned short denominator_coeff_a1_filter2_msw; + + unsigned short denominator_coeff_a0_filter3_lsw; + unsigned short denominator_coeff_a0_filter3_msw; + unsigned short denominator_coeff_a1_filter3_lsw; + unsigned short denominator_coeff_a1_filter3_msw; + + unsigned short shift_factor_filter0; + unsigned short shift_factor_filter1; + unsigned short shift_factor_filter2; + unsigned short shift_factor_filter3; + + unsigned short pan_of_filter0; + unsigned short pan_of_filter1; + unsigned short pan_of_filter2; + unsigned short pan_of_filter3; +} __attribute__((packed)); + +/* + * Command to configure parameters for calibration gain rx + */ + +#define AUDPREPROC_CMD_CFG_CAL_GAIN_PARAMS 0x0004 +#define AUDPREPROC_CMD_CFG_CAL_GAIN_LEN \ + sizeof(struct audpreproc_cmd_cfg_cal_gain) + +struct audpreproc_cmd_cfg_cal_gain { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short audprecalgain; + unsigned short reserved; +} __attribute__((packed)); + +#define AUDPREPROC_CMD_CFG_LVNV_PARMS 0x0006 +#define AUDPREPROC_CMD_CFG_LVNV_PARMS_LEN \ + sizeof(struct audpreproc_cmd_cfg_lvnv_param) + +struct audpreproc_cmd_cfg_lvnv_param { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short cs_mode; + unsigned short lvnv_ext_buf_size; + unsigned short lvnv_ext_partition; + unsigned short lvnv_ext_buf_start_lsw; + unsigned short lvnv_ext_buf_start_msw; +}; + +#define AUDPREPROC_CMD_FEAT_QUERY_PARAMS 0x0005 + +struct rtc_audpreproc_read_data { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short feature_id; + unsigned short extbufsizemsw; + unsigned short extbufsizelsw; + unsigned short extpart; + unsigned short extbufstartmsw; + unsigned short extbufstartlsw; +} __attribute__((packed)) ; + +#endif /* QDSP5AUDPREPROCCMDI_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audpreprocmsg.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audpreprocmsg.h new file mode 100644 index 00000000000..29da664544c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audpreprocmsg.h @@ -0,0 +1,127 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef QDSP5AUDPREPROCMSG_H +#define QDSP5AUDPREPROCMSG_H + +#define AUDPREPROC_MSG_FEAT_QUERY_DM_DONE 0x0006 + +/* + * ADSPREPROCTASK Messages + * AUDPREPROCTASK uses audPreProcUpRlist to communicate with ARM + * Location : MEMB + * Buffer size : 6 + * No of buffers in queue : 4 + */ + +/* + * Message to indicate Pre processing config command is done + */ + +#define AUDPREPROC_CMD_CFG_DONE_MSG 0x0001 +#define AUDPREPROC_CMD_CFG_DONE_MSG_LEN \ + sizeof(struct audpreproc_cmd_cfg_done_msg) + +#define AUD_PREPROC_TYPE_AGC 0x0 +#define AUD_PREPROC_NOISE_REDUCTION 0x1 +#define AUD_PREPROC_IIR_TUNNING_FILTER 0x2 + +#define AUD_PREPROC_CONFIG_ENABLED -1 +#define AUD_PREPROC_CONFIG_DISABLED 0 + +struct audpreproc_cmd_cfg_done_msg { + unsigned short stream_id; + unsigned short aud_preproc_type; + signed short aud_preproc_status_flag; +} __attribute__((packed)); + +/* + * Message to indicate Pre processing error messages + */ + +#define AUDPREPROC_ERROR_MSG 0x0002 +#define AUDPREPROC_ERROR_MSG_LEN \ + sizeof(struct audpreproc_err_msg) + +#define AUD_PREPROC_ERR_IDX_WRONG_SAMPLING_FREQUENCY 0x00 +#define AUD_PREPROC_ERR_IDX_ENC_NOT_SUPPORTED 0x01 + +struct audpreproc_err_msg { + unsigned short stream_id; + signed short aud_preproc_err_idx; +} __attribute__((packed)); + +/* + * Message to indicate encoder config command + */ + +#define AUDPREPROC_CMD_ENC_CFG_DONE_MSG 0x0003 +#define AUDPREPROC_CMD_ENC_CFG_DONE_MSG_LEN \ + sizeof(struct audpreproc_cmd_enc_cfg_done_msg) + +struct audpreproc_cmd_enc_cfg_done_msg { + unsigned short stream_id; + unsigned short rec_enc_type; +} __attribute__((packed)); + +/* + * Message to indicate encoder param config command + */ + +#define AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG 0x0004 +#define AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG_LEN \ + sizeof(struct audpreproc_cmd_enc_param_cfg_done_msg) + +struct audpreproc_cmd_enc_param_cfg_done_msg { + unsigned short stream_id; +} __attribute__((packed)); + + +/* + * Message to indicate AFE config cmd for + * audio recording is successfully recieved + */ + +#define AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG 0x0005 +#define AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG_LEN \ + sizeof(struct audpreproc_afe_cmd_audio_record_cfg_done) + +struct audpreproc_afe_cmd_audio_record_cfg_done { + unsigned short stream_id; +} __attribute__((packed)); + +/* + * Message to indicate Routing mode + * configuration success or failure + */ + +#define AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG 0x0007 +#define AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG_LEN \ + sizeof(struct audpreproc_cmd_routing_mode_done) + +struct audpreproc_cmd_routing_mode_done { + unsigned short stream_id; + unsigned short configuration; +} __attribute__((packed)); + + +#define AUDPREPROC_CMD_PCM_CFG_ARM_TO_PREPROC_DONE_MSG 0x0008 +#define AUDPREPROC_CMD_PCM_CFG_ARM_TO_PREPROC_DONE_MSG_LEN \ + sizeof(struct audreproc_cmd_pcm_cfg_arm_to_preproc_done) + +struct audreproc_cmd_pcm_cfg_arm_to_preproc_done { + unsigned short stream_id; + unsigned short configuration; +} __attribute__((packed)); + +#endif /* QDSP5AUDPREPROCMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audreccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audreccmdi.h new file mode 100644 index 00000000000..9ba8645ae06 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audreccmdi.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef QDSP5AUDRECCMDI_H +#define QDSP5AUDRECCMDI_H + +/* + * AUDRECTASK COMMANDS + * ARM uses 2 queues to communicate with the AUDRECTASK + * 1.uPAudRec[i]CmdQueue, where i=0,1,2 + * Location :MEMC + * Buffer Size : 5 + * No of Buffers in a queue : 2 + * 2.uPAudRec[i]BitstreamQueue, where i=0,1,2 + * Location : MEMC + * Buffer Size : 5 + * No of buffers in a queue : 3 + */ + +/* + * Commands on uPAudRec[i]CmdQueue, where i=0,1,2 + */ + +/* + * Command to configure memory for enabled encoder + */ + +#define AUDREC_CMD_MEM_CFG_CMD 0x0000 +#define AUDREC_CMD_ARECMEM_CFG_LEN \ + sizeof(struct audrec_cmd_arecmem_cfg) + +struct audrec_cmd_arecmem_cfg { + unsigned short cmd_id; + unsigned short audrec_up_pkt_intm_count; + unsigned short audrec_ext_pkt_start_addr_msw; + unsigned short audrec_ext_pkt_start_addr_lsw; + unsigned short audrec_ext_pkt_buf_number; +} __attribute__((packed)); + +/* + * Command to configure pcm input memory + */ + +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC 0x0001 +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC_LEN \ + sizeof(struct audrec_cmd_pcm_cfg_arm_to_enc) + +struct audrec_cmd_pcm_cfg_arm_to_enc { + unsigned short cmd_id; + unsigned short config_update_flag; + unsigned short enable_flag; + unsigned short sampling_freq; + unsigned short channels; + unsigned short frequency_of_intimation; + unsigned short max_number_of_buffers; +} __attribute__((packed)); + +#define AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE -1 +#define AUDREC_PCM_CONFIG_UPDATE_FLAG_DISABLE 0 + +#define AUDREC_ENABLE_FLAG_VALUE -1 +#define AUDREC_DISABLE_FLAG_VALUE 0 + +/* + * Command to intimate available pcm buffer + */ + +#define AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC 0x0002 +#define AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC_LEN \ + sizeof(struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc) + +struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc { + unsigned short cmd_id; + unsigned short num_buffers; + unsigned short buffer_write_cnt_msw; + unsigned short buffer_write_cnt_lsw; + unsigned short buf_address_length[8];/*this array holds address + and length details of + two buffers*/ +} __attribute__((packed)); + +/* + * Command to flush + */ + +#define AUDREC_CMD_FLUSH 0x0003 +#define AUDREC_CMD_FLUSH_LEN \ + sizeof(struct audrec_cmd_flush) + +struct audrec_cmd_flush { + unsigned short cmd_id; +} __attribute__((packed)); + +/* + * Commands on uPAudRec[i]BitstreamQueue, where i=0,1,2 + */ + +/* + * Command to indicate current packet read count + */ + +#define UP_AUDREC_PACKET_EXT_PTR 0x0000 +#define UP_AUDREC_PACKET_EXT_PTR_LEN \ + sizeof(up_audrec_packet_ext_ptr) + +struct up_audrec_packet_ext_ptr { + unsigned short cmd_id; + unsigned short audrec_up_curr_read_count_lsw; + unsigned short audrec_up_curr_read_count_msw; +} __attribute__((packed)); + +#endif /* QDSP5AUDRECCMDI_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audrecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audrecmsg.h new file mode 100644 index 00000000000..32ccbbc42d4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/qdsp5audrecmsg.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef QDSP5AUDRECMSG_H +#define QDSP5AUDRECMSG_H + +/* + * AUDRECTASK MESSAGES + * AUDRECTASK uses audRec[i]UpRlist, where i=0,1,2 to communicate with ARM + * Location : MEMC + * Buffer size : 5 + * No of buffers in a queue : 10 + */ + +/* + * Message to notify 2 error conditions + */ + +#define AUDREC_FATAL_ERR_MSG 0x0001 +#define AUDREC_FATAL_ERR_MSG_LEN \ + sizeof(struct audrec_fatal_err_msg) + +#define AUDREC_FATAL_ERR_MSG_NO_PKT 0x00 + +struct audrec_fatal_err_msg { + unsigned short audrec_err_id; +} __attribute__((packed)); + +/* + * Message to indicate encoded packet is delivered to external buffer + */ + +#define AUDREC_UP_PACKET_READY_MSG 0x0002 +#define AUDREC_UP_PACKET_READY_MSG_LEN \ + sizeof(struct audrec_up_pkt_ready_msg) + +struct audrec_up_pkt_ready_msg { + unsigned short audrec_packet_write_cnt_lsw; + unsigned short audrec_packet_write_cnt_msw; + unsigned short audrec_up_prev_read_cnt_lsw; + unsigned short audrec_up_prev_read_cnt_msw; +} __attribute__((packed)); + +/* + * Message indicates arecmem cfg done + */ +#define AUDREC_CMD_MEM_CFG_DONE_MSG 0x0003 + +/* buffer conntents are nill only message id is required */ + +/* + * Message to indicate pcm buffer configured + */ + +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG 0x0004 +#define AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG_LEN \ + sizeof(struct audrec_cmd_pcm_cfg_arm_to_enc_msg) + +struct audrec_cmd_pcm_cfg_arm_to_enc_msg { + unsigned short configuration; +} __attribute__((packed)); + +/* + * Message to indicate encoded packet is delivered to external buffer in FTRT + */ + +#define AUDREC_UP_NT_PACKET_READY_MSG 0x0005 +#define AUDREC_UP_NT_PACKET_READY_MSG_LEN \ + sizeof(struct audrec_up_nt_packet_ready_msg) + +struct audrec_up_nt_packet_ready_msg { + unsigned short audrec_packetwrite_cnt_lsw; + unsigned short audrec_packetwrite_cnt_msw; + unsigned short audrec_upprev_readcount_lsw; + unsigned short audrec_upprev_readcount_msw; +} __attribute__((packed)); + +/* + * Message to indicate pcm buffer is consumed + */ + +#define AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG 0x0006 +#define AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG_LEN \ + sizeof(struct audrec_cmd_pcm_buffer_ptr_update_arm_to_enc_msg) + +struct audrec_cmd_pcm_buffer_ptr_update_arm_to_enc_msg { + unsigned short buffer_readcnt_msw; + unsigned short buffer_readcnt_lsw; + unsigned short number_of_buffers; + unsigned short buffer_address_length[]; +} __attribute__((packed)); + +/* + * Message to indicate flush acknowledgement + */ + +#define AUDREC_CMD_FLUSH_DONE_MSG 0x0007 + +/* + * Message to indicate End of Stream acknowledgement + */ + +#define AUDREC_CMD_EOS_ACK_MSG 0x0008 + +#endif /* QDSP5AUDRECMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_ecodec.h b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_ecodec.h new file mode 100644 index 00000000000..35c1edbe10e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_ecodec.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_SNDDEV_ECODEC_H +#define __MACH_QDSP5_V2_SNDDEV_ECODEC_H +#include + +struct snddev_ecodec_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u32 acdb_id; /* Audio Cal purpose */ + u8 channel_mode; + u32 conf_pcm_ctl_val; + u32 conf_aux_codec_intf; + u32 conf_data_format_padding_val; + s32 max_voice_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0]:NB, [1]:WB */ + s32 min_voice_rx_vol[VOC_RX_VOL_ARRAY_NUM]; +}; +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_icodec.h b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_icodec.h new file mode 100644 index 00000000000..7f9938e3481 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_icodec.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_SNDDEV_ICODEC_H +#define __MACH_QDSP5_V2_SNDDEV_ICODEC_H +#include +#include +#include + +struct snddev_icodec_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u32 acdb_id; /* Audio Cal purpose */ + /* Adie profile */ + struct adie_codec_dev_profile *profile; + /* Afe setting */ + u8 channel_mode; + enum hsed_controller *pmctl_id; /* tx only enable mic bias */ + u32 pmctl_id_sz; + u32 default_sample_rate; + void (*pamp_on) (void); + void (*pamp_off) (void); + void (*voltage_on) (void); + void (*voltage_off) (void); + s32 max_voice_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0]: NB,[1]: WB */ + s32 min_voice_rx_vol[VOC_RX_VOL_ARRAY_NUM]; + u32 dev_vol_type; + u32 property; /*variable used to hold the properties + internal to the device*/ +}; +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_mi2s.h b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_mi2s.h new file mode 100644 index 00000000000..cd834a58884 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_mi2s.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_SNDDEV_MI2S_H +#define __MACH_QDSP5_V2_SNDDEV_MI2S_H + +struct snddev_mi2s_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u32 acdb_id; /* Audio Cal purpose */ + u8 channel_mode; + u8 sd_lines; + void (*route) (void); + void (*deroute) (void); + u32 default_sample_rate; +}; + +int mi2s_config_clk_gpio(void); + +int mi2s_config_data_gpio(u32 direction, u8 sd_line_mask); + +int mi2s_unconfig_clk_gpio(void); + +int mi2s_unconfig_data_gpio(u32 direction, u8 sd_line_mask); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_virtual.h b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_virtual.h new file mode 100644 index 00000000000..695b19d05b4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/snddev_virtual.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP5_V2_SNDDEV_VIRTUAL_H +#define __MACH_QDSP5_V2_SNDDEV_VIRTUAL_H +#include + +struct snddev_virtual_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u32 acdb_id; /* Audio Cal purpose */ +}; +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/voice.h b/arch/arm/mach-msm/include/mach/qdsp5v2/voice.h new file mode 100644 index 00000000000..5ca2d6d11e4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5v2/voice.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _MACH_QDSP5_V2_VOICE_H +#define _MACH_QDSP5_V2_VOICE_H + +#define VOICE_DALRPC_DEVICEID 0x02000075 +#define VOICE_DALRPC_PORT_NAME "DAL00" +#define VOICE_DALRPC_CPU 0 + + +/* Commands sent to Modem */ +#define CMD_VOICE_INIT 0x1 +#define CMD_ACQUIRE_DONE 0x2 +#define CMD_RELEASE_DONE 0x3 +#define CMD_DEVICE_INFO 0x4 +#define CMD_DEVICE_CHANGE 0x6 + +/* EVENTS received from MODEM */ +#define EVENT_ACQUIRE_START 0x51 +#define EVENT_RELEASE_START 0x52 +#define EVENT_CHANGE_START 0x54 +#define EVENT_NETWORK_RECONFIG 0x53 + +/* voice state */ +enum { + VOICE_INIT = 0, + VOICE_ACQUIRE, + VOICE_CHANGE, + VOICE_RELEASE, +}; + +enum { + NETWORK_CDMA = 0, + NETWORK_GSM, + NETWORK_WCDMA, + NETWORK_WCDMA_WB, +}; + +enum { + VOICE_DALRPC_CMD = DALDEVICE_FIRST_DEVICE_API_IDX +}; + +/* device state */ +enum { + DEV_INIT = 0, + DEV_READY, + DEV_CHANGE, + DEV_CONCUR, + DEV_REL_DONE, +}; + +/* Voice Event */ +enum{ + VOICE_RELEASE_START = 1, + VOICE_CHANGE_START, + VOICE_ACQUIRE_START, + VOICE_NETWORK_RECONFIG, +}; + +/* Device Event */ +#define DEV_CHANGE_READY 0x1 + +#define VOICE_CALL_START 0x1 +#define VOICE_CALL_END 0 + +#define VOICE_DEV_ENABLED 0x1 +#define VOICE_DEV_DISABLED 0 + +struct voice_header { + uint32_t id; + uint32_t data_len; +}; + +struct voice_init { + struct voice_header hdr; + void *cb_handle; +}; + + +/* Device information payload structure */ +struct voice_device { + struct voice_header hdr; + uint32_t rx_device; + uint32_t tx_device; + uint32_t rx_volume; + uint32_t rx_mute; + uint32_t tx_mute; + uint32_t rx_sample; + uint32_t tx_sample; +}; + +/*Voice command structure*/ +struct voice_network { + struct voice_header hdr; + uint32_t network_info; +}; + +struct device_data { + uint32_t dev_acdb_id; + uint32_t volume; /* in percentage */ + uint32_t mute; + uint32_t sample; + uint32_t enabled; + uint32_t dev_id; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h new file mode 100644 index 00000000000..62d7a337e88 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_H_ +#define __APR_H_ + +#define APR_Q6_NOIMG 0 +#define APR_Q6_LOADING 1 +#define APR_Q6_LOADED 2 + +struct apr_q6 { + void *pil; + uint32_t state; + struct mutex lock; +}; + +struct apr_hdr { + uint16_t hdr_field; + uint16_t pkt_size; + uint8_t src_svc; + uint8_t src_domain; + uint16_t src_port; + uint8_t dest_svc; + uint8_t dest_domain; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; +}; + +#define APR_HDR_LEN(hdr_len) ((hdr_len)/4) +#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len)) +#define APR_HDR_FIELD(msg_type, hdr_len, ver)\ + (((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF)) + +#define APR_HDR_SIZE sizeof(struct apr_hdr) + +/* Version */ +#define APR_PKT_VER 0x0 + +/* Command and Response Types */ +#define APR_MSG_TYPE_EVENT 0x0 +#define APR_MSG_TYPE_CMD_RSP 0x1 +#define APR_MSG_TYPE_SEQ_CMD 0x2 +#define APR_MSG_TYPE_NSEQ_CMD 0x3 +#define APR_MSG_TYPE_MAX 0x04 + +/* APR Basic Response Message */ +#define APR_BASIC_RSP_RESULT 0x000110E8 +#define APR_RSP_ACCEPTED 0x000100BE + +/* Domain IDs */ +#define APR_DOMAIN_SIM 0x1 +#define APR_DOMAIN_PC 0x2 +#define APR_DOMAIN_MODEM 0x3 +#define APR_DOMAIN_ADSP 0x4 +#define APR_DOMAIN_APPS 0x5 +#define APR_DOMAIN_MAX 0x6 + +/* ADSP service IDs */ +#define APR_SVC_TEST_CLIENT 0x2 +#define APR_SVC_ADSP_CORE 0x3 +#define APR_SVC_AFE 0x4 +#define APR_SVC_VSM 0x5 +#define APR_SVC_VPM 0x6 +#define APR_SVC_ASM 0x7 +#define APR_SVC_ADM 0x8 +#define APR_SVC_ADSP_MVM 0x09 +#define APR_SVC_ADSP_CVS 0x0A +#define APR_SVC_ADSP_CVP 0x0B +#define APR_SVC_USM 0x0C +#define APR_SVC_MAX 0x0D + +/* Modem Service IDs */ +#define APR_SVC_MVS 0x3 +#define APR_SVC_MVM 0x4 +#define APR_SVC_CVS 0x5 +#define APR_SVC_CVP 0x6 +#define APR_SVC_SRD 0x7 + +/* APR Port IDs */ +#define APR_MAX_PORTS 0x40 + +#define APR_NAME_MAX 0x40 + +#define RESET_EVENTS 0xFFFFFFFF + +#define LPASS_RESTART_EVENT 0x1000 +#define LPASS_RESTART_READY 0x1001 + +struct apr_client_data { + uint16_t reset_event; + uint16_t reset_proc; + uint16_t payload_size; + uint16_t hdr_len; + uint16_t msg_type; + uint16_t src; + uint16_t dest_svc; + uint16_t src_port; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; + void *payload; +}; + +typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv); + +struct apr_svc { + uint16_t id; + uint16_t dest_id; + uint16_t client_id; + uint8_t rvd; + uint8_t port_cnt; + uint8_t svc_cnt; + uint8_t need_reset; + apr_fn port_fn[APR_MAX_PORTS]; + void *port_priv[APR_MAX_PORTS]; + apr_fn fn; + void *priv; + struct mutex m_lock; + spinlock_t w_lock; +}; + +struct apr_client { + uint8_t id; + uint8_t svc_cnt; + uint8_t rvd; + struct mutex m_lock; + struct apr_svc_ch_dev *handle; + struct apr_svc svc[APR_SVC_MAX]; +}; + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv); +inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port, + uint16_t msg_type, uint16_t dest_port, + uint32_t token, uint32_t opcode, uint16_t len); + +int apr_send_pkt(void *handle, uint32_t *buf); +int apr_deregister(void *handle); +void change_q6_state(int state); +void q6audio_dsp_not_responding(void); +void apr_reset(void *handle); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_tal.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_tal.h new file mode 100644 index 00000000000..163ba7b7463 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_tal.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_TAL_H_ +#define __APR_TAL_H_ + +#include +#include +#include + +/* APR Client IDs */ +#define APR_CLIENT_AUDIO 0x0 +#define APR_CLIENT_VOICE 0x1 +#define APR_CLIENT_MAX 0x2 + +#define APR_DL_SMD 0 +#define APR_DL_MAX 1 + +#define APR_DEST_MODEM 0 +#define APR_DEST_QDSP6 1 +#define APR_DEST_MAX 2 + +#define APR_MAX_BUF 8192 + +#define APR_OPEN_TIMEOUT_MS 5000 + +typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv); +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv); +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len); +int apr_tal_close(struct apr_svc_ch_dev *apr_ch); +struct apr_svc_ch_dev { + struct smd_channel *ch; + spinlock_t lock; + spinlock_t w_lock; + struct mutex m_lock; + apr_svc_cb_fn func; + char data[APR_MAX_BUF]; + wait_queue_head_t wait; + void *priv; + uint32_t smd_state; + wait_queue_head_t dest; + uint32_t dest_state; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h new file mode 100644 index 00000000000..487e81477f9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h @@ -0,0 +1,154 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_US_H__ +#define __APR_US_H__ + +#include + +/* ======================================================================= */ +/* Session Level commands */ +#define USM_SESSION_CMD_MEMORY_MAP 0x00012304 +struct usm_stream_cmd_memory_map { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u16 mempool_id; + u16 reserved; +} __packed; + +#define USM_SESSION_CMD_MEMORY_UNMAP 0x00012305 +struct usm_stream_cmd_memory_unmap { + struct apr_hdr hdr; + u32 buf_add; +} __packed; + +#define USM_SESSION_CMD_RUN 0x00012306 +struct usm_stream_cmd_run { + struct apr_hdr hdr; + u32 flags; + u32 msw_ts; + u32 lsw_ts; +} __packed; + +/* Stream level commands */ +#define USM_STREAM_CMD_OPEN_READ 0x00012309 +struct usm_stream_cmd_open_read { + struct apr_hdr hdr; + u32 uMode; + u32 src_endpoint; + u32 pre_proc_top; + u32 format; +} __packed; + +#define USM_STREAM_CMD_OPEN_WRITE 0x00011271 +struct usm_stream_cmd_open_write { + struct apr_hdr hdr; + u32 format; +} __packed; + + +#define USM_STREAM_CMD_CLOSE 0x0001230A + +/* Encoder configuration definitions */ +#define USM_STREAM_CMD_SET_ENC_PARAM 0x0001230B +/* Decoder configuration definitions */ +#define USM_DATA_CMD_MEDIA_FORMAT_UPDATE 0x00011272 + +/* Encoder/decoder configuration block */ +#define USM_PARAM_ID_ENCDEC_ENC_CFG_BLK 0x0001230D + +/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */ +/* common declarations */ +struct usm_cfg_common { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u32 dev_id; + u32 data_map; +} __packed; + +/* Max number of static located transparent data (bytes) */ +#define USM_MAX_CFG_DATA_SIZE 20 +struct usm_encode_cfg_blk { + u32 frames_per_buf; + u32 format_id; + /* = sizeof(usm_cfg_common)+|tarnsp_data| */ + u32 cfg_size; + struct usm_cfg_common cfg_common; + /* Transparent configuration data for specific encoder */ + u8 transp_data[USM_MAX_CFG_DATA_SIZE]; +} __packed; + +struct usm_stream_cmd_encdec_cfg_blk { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct usm_encode_cfg_blk enc_blk; +} __packed; + +struct us_encdec_cfg { + u32 format_id; + struct usm_cfg_common cfg_common; + u16 params_size; + u8 *params; +} __packed; + +struct usm_stream_media_format_update { + struct apr_hdr hdr; + u32 format_id; + /* = sizeof(usm_cfg_common)+|tarnsp_data| */ + u32 cfg_size; + struct usm_cfg_common cfg_common; + /* Transparent configuration data for specific encoder */ + u8 transp_data[USM_MAX_CFG_DATA_SIZE]; +} __packed; + + +#define USM_DATA_CMD_READ 0x0001230E +struct usm_stream_cmd_read { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u32 uid; + u32 counter; +} __packed; + +#define USM_DATA_EVENT_READ_DONE 0x0001230F + +#define USM_DATA_CMD_WRITE 0x00011273 +struct usm_stream_cmd_write { + struct apr_hdr hdr; + u32 buf_add; + u32 buf_size; + u32 uid; + u32 msw_ts; + u32 lsw_ts; + u32 flags; +} __packed; + +#define USM_DATA_EVENT_WRITE_DONE 0x00011274 + +/* Start/stop US signal detection */ +#define USM_SESSION_CMD_SIGNAL_DETECT_MODE 0x00012719 + +struct usm_session_cmd_detect_info { + struct apr_hdr hdr; + u32 detect_mode; + u32 skip_interval; + u32 algorithm_cfg_size; +} __packed; + +/* US signal detection result */ +#define USM_SESSION_EVENT_SIGNAL_DETECT_RESULT 0x00012720 + +#endif /* __APR_US_H__ */ diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h new file mode 100644 index 00000000000..a55dee6c431 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _AUDIO_ACDB_H +#define _AUDIO_ACDB_H + +#include +#include + +enum { + RX_CAL, + TX_CAL, + MAX_AUDPROC_TYPES +}; + +struct acdb_cal_block { + uint32_t cal_size; + uint32_t cal_kvaddr; + uint32_t cal_paddr; +}; + +struct acdb_atomic_cal_block { + atomic_t cal_size; + atomic_t cal_kvaddr; + atomic_t cal_paddr; +}; + +struct acdb_cal_data { + uint32_t num_cal_blocks; + struct acdb_atomic_cal_block *cal_blocks; +}; + +uint32_t get_voice_rx_topology(void); +uint32_t get_voice_tx_topology(void); +uint32_t get_adm_rx_topology(void); +uint32_t get_adm_tx_topology(void); +uint32_t get_asm_topology(void); +void get_all_voice_cal(struct acdb_cal_block *cal_block); +void get_all_cvp_cal(struct acdb_cal_block *cal_block); +void get_all_vocproc_cal(struct acdb_cal_block *cal_block); +void get_all_vocstrm_cal(struct acdb_cal_block *cal_block); +void get_all_vocvol_cal(struct acdb_cal_block *cal_block); +void get_anc_cal(struct acdb_cal_block *cal_block); +void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block); +void get_vocproc_cal(struct acdb_cal_data *cal_data); +void get_vocstrm_cal(struct acdb_cal_data *cal_data); +void get_vocvol_cal(struct acdb_cal_data *cal_data); +void get_sidetone_cal(struct sidetone_cal *cal_data); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_dev_ctl.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_dev_ctl.h new file mode 100644 index 00000000000..20c6fc4a756 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_dev_ctl.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP6_V2_SNDDEV_H +#define __MACH_QDSP6_V2_SNDDEV_H +#include +#include + +#define AUDIO_DEV_CTL_MAX_DEV 64 +#define DIR_TX 2 +#define DIR_RX 1 + +#define DEVICE_IGNORE 0xffff +#define COPP_IGNORE 0xffffffff +#define SESSION_IGNORE 0x0UL + +/* 8 concurrent sessions with Q6 possible, session:0 + reserved in DSP */ +#define MAX_SESSIONS 0x09 + +/* This represents Maximum bit needed for representing sessions + per clients, MAX_BIT_PER_CLIENT >= MAX_SESSIONS */ +#define MAX_BIT_PER_CLIENT 16 + +#define VOICE_STATE_INVALID 0x0 +#define VOICE_STATE_INCALL 0x1 +#define VOICE_STATE_OFFCALL 0x2 +#define ONE_TO_MANY 1 +#define MANY_TO_ONE 2 + +struct msm_snddev_info { + const char *name; + u32 capability; + u32 copp_id; + u32 acdb_id; + u32 dev_volume; + struct msm_snddev_ops { + int (*open)(struct msm_snddev_info *); + int (*close)(struct msm_snddev_info *); + int (*set_freq)(struct msm_snddev_info *, u32); + int (*enable_sidetone)(struct msm_snddev_info *, u32, uint16_t); + int (*set_device_volume)(struct msm_snddev_info *, u32); + int (*enable_anc)(struct msm_snddev_info *, u32); + } dev_ops; + u8 opened; + void *private_data; + bool state; + u32 sample_rate; + u32 channel_mode; + u32 set_sample_rate; + u64 sessions; + int usage_count; + s32 max_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB,[1] for WB */ + s32 min_voc_rx_vol[VOC_RX_VOL_ARRAY_NUM]; +}; + +struct msm_volume { + int volume; /* Volume parameter, in % Scale */ + int pan; +}; + +extern struct msm_volume msm_vol_ctl; + +void msm_snddev_register(struct msm_snddev_info *); +void msm_snddev_unregister(struct msm_snddev_info *); +int msm_snddev_devcount(void); +int msm_snddev_query(int dev_id); +unsigned short msm_snddev_route_dec(int popp_id); +unsigned short msm_snddev_route_enc(int enc_id); + +int msm_snddev_set_dec(int popp_id, int copp_id, int set, + int rate, int channel_mode); +int msm_snddev_set_enc(int popp_id, int copp_id, int set, + int rate, int channel_mode); + +int msm_snddev_is_set(int popp_id, int copp_id); +int msm_get_voc_route(u32 *rx_id, u32 *tx_id); +int msm_set_voc_route(struct msm_snddev_info *dev_info, int stream_type, + int dev_id); +int msm_snddev_enable_sidetone(u32 dev_id, u32 enable, uint16_t gain); + +int msm_set_copp_id(int session_id, int copp_id); + +int msm_clear_copp_id(int session_id, int copp_id); + +int msm_clear_session_id(int session_id); + +int msm_reset_all_device(void); + +int reset_device(void); + +int msm_clear_all_session(void); + +struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id); + +void msm_release_voc_thread(void); + +int snddev_voice_set_volume(int vol, int path); + +struct auddev_evt_voc_devinfo { + u32 dev_type; /* Rx or Tx */ + u32 acdb_dev_id; /* acdb id of device */ + u32 dev_sample; /* Sample rate of device */ + s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb (milibel), + [0] is for NB, other for WB */ + s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* unit is mb */ + u32 dev_id; /* registered device id */ + u32 dev_port_id; +}; + +struct auddev_evt_audcal_info { + u32 dev_id; + u32 acdb_id; + u32 sample_rate; + u32 dev_type; + u32 sessions; +}; + +union msm_vol_mute { + int vol; + bool mute; +}; + +struct auddev_evt_voc_mute_info { + u32 dev_type; + u32 acdb_dev_id; + u32 voice_session_id; + union msm_vol_mute dev_vm_val; +}; + +struct auddev_evt_freq_info { + u32 dev_type; + u32 acdb_dev_id; + u32 sample_rate; +}; + +union auddev_evt_data { + struct auddev_evt_voc_devinfo voc_devinfo; + struct auddev_evt_voc_mute_info voc_vm_info; + struct auddev_evt_freq_info freq_info; + u32 routing_id; + s32 session_vol; + s32 voice_state; + struct auddev_evt_audcal_info audcal_info; + u32 voice_session_id; +}; + +struct message_header { + uint32_t id; + uint32_t data_len; +}; + +#define AUDDEV_EVT_DEV_CHG_VOICE 0x01 /* device change event */ +#define AUDDEV_EVT_DEV_RDY 0x02 /* device ready event */ +#define AUDDEV_EVT_DEV_RLS 0x04 /* device released event */ +#define AUDDEV_EVT_REL_PENDING 0x08 /* device release pending */ +#define AUDDEV_EVT_DEVICE_VOL_MUTE_CHG 0x10 /* device volume changed */ +#define AUDDEV_EVT_START_VOICE 0x20 /* voice call start */ +#define AUDDEV_EVT_END_VOICE 0x40 /* voice call end */ +#define AUDDEV_EVT_STREAM_VOL_CHG 0x80 /* device volume changed */ +#define AUDDEV_EVT_FREQ_CHG 0x100 /* Change in freq */ +#define AUDDEV_EVT_VOICE_STATE_CHG 0x200 /* Change in voice state */ + +#define AUDDEV_CLNT_VOC 0x1 /*Vocoder clients*/ +#define AUDDEV_CLNT_DEC 0x2 /*Decoder clients*/ +#define AUDDEV_CLNT_ENC 0x3 /* Encoder clients */ +#define AUDDEV_CLNT_AUDIOCAL 0x4 /* AudioCalibration client */ + +#define AUDIO_DEV_CTL_MAX_LISTNER 20 /* Max Listeners Supported */ + +struct msm_snd_evt_listner { + uint32_t evt_id; + uint32_t clnt_type; + uint32_t clnt_id; + void *private_data; + void (*auddev_evt_listener)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data); + struct msm_snd_evt_listner *cb_next; + struct msm_snd_evt_listner *cb_prev; +}; + +struct event_listner { + struct msm_snd_evt_listner *cb; + u32 num_listner; + int state; /* Call state */ /* TODO remove this if not req*/ +}; + +extern struct event_listner event; +int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, + void (*listner)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data), + void *private_data); +int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id); +void mixer_post_event(u32 evt_id, u32 dev_id); +void broadcast_event(u32 evt_id, u32 dev_id, u64 session_id); +int auddev_cfg_tx_copp_topology(int session_id, int cfg); +int msm_snddev_request_freq(int *freq, u32 session_id, + u32 capability, u32 clnt_type); +int msm_snddev_withdraw_freq(u32 session_id, + u32 capability, u32 clnt_type); +int msm_device_is_voice(int dev_id); +int msm_get_voc_freq(int *tx_freq, int *rx_freq); +int msm_snddev_get_enc_freq(int session_id); +int msm_set_voice_vol(int dir, s32 volume, u32 session_id); +int msm_set_voice_mute(int dir, int mute, u32 session_id); +int msm_get_voice_state(void); +int msm_enable_incall_recording(int popp_id, int rec_mode, int rate, + int channel_mode); +int msm_disable_incall_recording(uint32_t popp_id, uint32_t rec_mode); +#endif diff --git a/arch/arm/mach-msm/scm.h b/arch/arm/mach-msm/include/mach/qdsp6v2/dsp_debug.h similarity index 62% rename from arch/arm/mach-msm/scm.h rename to arch/arm/mach-msm/include/mach/qdsp6v2/dsp_debug.h index 00b31ea58f2..94f4ab46936 100644 --- a/arch/arm/mach-msm/scm.h +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/dsp_debug.h @@ -8,18 +8,15 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. + * */ -#ifndef __MACH_SCM_H -#define __MACH_SCM_H +#ifndef __DSP_DEBUG_H_ +#define __DSP_DEBUG_H_ -#define SCM_SVC_BOOT 0x1 -#define SCM_SVC_PIL 0x2 +typedef int (*dsp_state_cb)(int state); +int dsp_debug_register(dsp_state_cb ptr); -extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, - void *resp_buf, size_t resp_len); - -#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) - -extern u32 scm_get_version(void); +#define DSP_STATE_CRASHED 0x0 +#define DSP_STATE_CRASH_DUMP_DONE 0x1 #endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/q6voice.h b/arch/arm/mach-msm/include/mach/qdsp6v2/q6voice.h new file mode 100644 index 00000000000..674cfe835b1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/q6voice.h @@ -0,0 +1,778 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __QDSP6VOICE_H__ +#define __QDSP6VOICE_H__ + +#include + +/* Device Event */ +#define DEV_CHANGE_READY 0x1 + +#define VOICE_CALL_START 0x1 +#define VOICE_CALL_END 0 + +#define VOICE_DEV_ENABLED 0x1 +#define VOICE_DEV_DISABLED 0 + +#define MAX_VOC_PKT_SIZE 642 + +#define SESSION_NAME_LEN 20 + +struct voice_header { + uint32_t id; + uint32_t data_len; +}; + +struct voice_init { + struct voice_header hdr; + void *cb_handle; +}; + + +/* Device information payload structure */ + +struct device_data { + uint32_t dev_acdb_id; + uint32_t volume; /* in percentage */ + uint32_t mute; + uint32_t sample; + uint32_t enabled; + uint32_t dev_id; + uint32_t dev_port_id; +}; + +enum { + VOC_INIT = 0, + VOC_RUN, + VOC_CHANGE, + VOC_RELEASE, +}; + +/* TO MVM commands */ +#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE +/* Create a new full control MVM session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C +/* Attach a stream to the MVM. */ + +#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D +/* Detach a stream from the MVM. */ + +#define VSS_IMVM_CMD_ATTACH_VOCPROC 0x0001123E +/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc + * to all the streams currently attached to it. + */ + +#define VSS_IMVM_CMD_DETACH_VOCPROC 0x0001123F +/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this + * vocproc from all the streams to which it is currently attached. + */ + +#define VSS_IMVM_CMD_START_VOICE 0x00011190 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_STOP_VOICE 0x00011192 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + + +#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C +/* Set the network type. */ + +#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0 +/* Set the voice timing parameters. */ + +struct vss_imvm_cmd_create_control_session_t { + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + +struct vss_istream_cmd_set_tty_mode_t { + uint32_t mode; + /**< + * TTY mode. + * + * 0 : TTY disabled + * 1 : HCO + * 2 : VCO + * 3 : FULL + */ +} __attribute__((packed)); + +struct vss_istream_cmd_attach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being attached. */ +} __attribute__((packed)); + +struct vss_istream_cmd_detach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being detached. */ +} __attribute__((packed)); + +struct vss_imvm_cmd_attach_stream_t { + uint16_t handle; + /* The stream handle to attach. */ +} __attribute__((packed)); + +struct vss_imvm_cmd_detach_stream_t { + uint16_t handle; + /* The stream handle to detach. */ +} __attribute__((packed)); + +struct vss_icommon_cmd_set_network_t { + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ +} __attribute__((packed)); + +struct vss_icommon_cmd_set_voice_timing_t { + uint16_t mode; + /* + * The vocoder frame synchronization mode. + * + * 0 : No frame sync. + * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt). + */ + uint16_t enc_offset; + /* + * The offset in microseconds from the VFR to deliver a Tx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_req_offset; + /* + * The offset in microseconds from the VFR to request for an Rx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_offset; + /* + * The offset in microseconds from the VFR to indicate the deadline to + * receive an Rx vocoder packet. The offset should be less than 20000us. + * Rx vocoder packets received after this deadline are not guaranteed to + * be processed. + */ +} __attribute__((packed)); + +struct mvm_attach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle; +} __attribute__((packed)); + +struct mvm_detach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle; +} __attribute__((packed)); + +struct mvm_create_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_create_control_session_t mvm_session; +} __packed; + +struct mvm_set_tty_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_tty_mode_t tty_mode; +} __attribute__((packed)); + +struct mvm_attach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_attach_stream_t attach_stream; +} __attribute__((packed)); + +struct mvm_detach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_detach_stream_t detach_stream; +} __attribute__((packed)); + +struct mvm_set_network_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_network_t network; +} __attribute__((packed)); + +struct mvm_set_voice_timing_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_voice_timing_t timing; +} __attribute__((packed)); + +/* TO CVS commands */ +#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7 +/* Create a new full control stream session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +#define VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA 0x000110FB + +#define VSS_ISTREAM_CMD_SET_MUTE 0x00011022 + +#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186 +/* Set media type on the stream. */ + +#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015 +/* Event sent by the stream to its client to provide an encoded packet. */ + +#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017 +/* Event sent by the stream to its client requesting for a decoder packet. + * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event. + */ + +#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016 +/* Event sent by the client to the stream in response to a + * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet. + */ + +#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E +/* Set AMR encoder rate. */ + +#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F +/* Set AMR-WB encoder rate. */ + +#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019 +/* Set encoder minimum and maximum rate. */ + +#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D +/* Set encoder DTX mode. */ + +#define VSS_ISTREAM_CMD_START_RECORD 0x00011236 +/* Start in-call conversation recording. */ + +#define VSS_ISTREAM_CMD_STOP_RECORD 0x00011237 +/* Stop in-call conversation recording. */ + +#define VSS_ISTREAM_CMD_START_PLAYBACK 0x00011238 +/* Start in-call music delivery on the Tx voice path. */ + +#define VSS_ISTREAM_CMD_STOP_PLAYBACK 0x00011239 +/* Stop the in-call music delivery on the Tx voice path. */ + +struct vss_istream_cmd_create_passive_control_session_t { + char name[SESSION_NAME_LEN]; + /**< + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __attribute__((packed)); + +struct vss_istream_cmd_set_mute_t { + uint16_t direction; + /**< + * 0 : TX only + * 1 : RX only + * 2 : TX and Rx + */ + uint16_t mute_flag; + /**< + * Mute, un-mute. + * + * 0 : Silence disable + * 1 : Silence enable + * 2 : CNG enable. Applicable to TX only. If set on RX behavior + * will be the same as 1 + */ +} __attribute__((packed)); + +struct vss_istream_cmd_create_full_control_session_t { + uint16_t direction; + /* + * Stream direction. + * + * 0 : TX only + * 1 : RX only + * 2 : TX and RX + * 3 : TX and RX loopback + */ + uint32_t enc_media_type; + /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t dec_media_type; + /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __attribute__((packed)); + +struct vss_istream_cmd_set_media_type_t { + uint32_t rx_media_id; + /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t tx_media_id; + /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ +} __attribute__((packed)); + +struct vss_istream_evt_send_enc_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data buffer. */ +} __attribute__((packed)); + +struct vss_istream_evt_send_dec_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data. */ +} __attribute__((packed)); + +struct vss_istream_cmd_voc_amr_set_enc_rate_t { + uint32_t mode; + /* Set the AMR encoder rate. + * + * 0x00000000 : 4.75 kbps + * 0x00000001 : 5.15 kbps + * 0x00000002 : 5.90 kbps + * 0x00000003 : 6.70 kbps + * 0x00000004 : 7.40 kbps + * 0x00000005 : 7.95 kbps + * 0x00000006 : 10.2 kbps + * 0x00000007 : 12.2 kbps + */ +} __attribute__((packed)); + +struct vss_istream_cmd_voc_amrwb_set_enc_rate_t { + uint32_t mode; + /* Set the AMR-WB encoder rate. + * + * 0x00000000 : 6.60 kbps + * 0x00000001 : 8.85 kbps + * 0x00000002 : 12.65 kbps + * 0x00000003 : 14.25 kbps + * 0x00000004 : 15.85 kbps + * 0x00000005 : 18.25 kbps + * 0x00000006 : 19.85 kbps + * 0x00000007 : 23.05 kbps + * 0x00000008 : 23.85 kbps + */ +} __attribute__((packed)); + +struct vss_istream_cmd_cdma_set_enc_minmax_rate_t { + uint16_t min_rate; + /* Set the lower bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ + uint16_t max_rate; + /* Set the upper bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ +} __attribute__((packed)); + +struct vss_istream_cmd_set_enc_dtx_mode_t { + uint32_t enable; + /* Toggle DTX on or off. + * + * 0 : Disables DTX + * 1 : Enables DTX + */ +} __attribute__((packed)); + +#define VSS_TAP_POINT_NONE 0x00010F78 +/* Indicates no tapping for specified path. */ + +#define VSS_TAP_POINT_STREAM_END 0x00010F79 +/* Indicates that specified path should be tapped at the end of the stream. */ + +struct vss_istream_cmd_start_record_t { + uint32_t rx_tap_point; + /* Tap point to use on the Rx path. Supported values are: + * VSS_TAP_POINT_NONE : Do not record Rx path. + * VSS_TAP_POINT_STREAM_END : Rx tap point is at the end of the stream. + */ + uint32_t tx_tap_point; + /* Tap point to use on the Tx path. Supported values are: + * VSS_TAP_POINT_NONE : Do not record tx path. + * VSS_TAP_POINT_STREAM_END : Tx tap point is at the end of the stream. + */ +} __attribute__((packed)); + +struct cvs_create_passive_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_passive_control_session_t cvs_session; +} __attribute__((packed)); + +struct cvs_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_full_control_session_t cvs_session; +} __attribute__((packed)); + +struct cvs_destroy_session_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvs_cache_calibration_data_cmd { + struct apr_hdr hdr; +} __attribute__ ((packed)); + +struct cvs_set_mute_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_mute_t cvs_set_mute; +} __attribute__((packed)); + +struct cvs_set_media_type_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_media_type_t media_type; +} __attribute__((packed)); + +struct cvs_send_dec_buf_cmd { + struct apr_hdr hdr; + struct vss_istream_evt_send_dec_buffer_t dec_buf; +} __attribute__((packed)); + +struct cvs_set_amr_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate; +} __attribute__((packed)); + +struct cvs_set_amrwb_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate; +} __attribute__((packed)); + +struct cvs_set_cdma_enc_minmax_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate; +} __attribute__((packed)); + +struct cvs_set_enc_dtx_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode; +} __attribute__((packed)); + +struct cvs_start_record_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_start_record_t rec_mode; +} __attribute__((packed)); + +/* TO CVP commands */ + +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +#define VSS_IVOCPROC_CMD_SET_DEVICE 0x000100C4 + +#define VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA 0x000110E3 + +#define VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE 0x000110E4 + +#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB + +#define VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX 0x000110EE + +#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72 + +#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77 + +/* Newtwork IDs */ +#define VSS_NETWORK_ID_DEFAULT 0x00010037 +#define VSS_NETWORK_ID_VOIP_NB 0x00011240 +#define VSS_NETWORK_ID_VOIP_WB 0x00011241 +#define VSS_NETWORK_ID_VOIP_WV 0x00011242 + +/* Media types */ +#define VSS_MEDIA_ID_13K_MODEM 0x00010FC1 +/* Qcelp vocoder modem format */ +#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2 +/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */ +#define VSS_MEDIA_ID_4GV_NB_MODEM 0x00010FC3 +/* 4GV Narrowband modem format */ +#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4 +/* 4GV Wideband modem format */ +#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6 +/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7 +/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */ +#define VSS_MEDIA_ID_EFR_MODEM 0x00010FC8 +/*EFR modem format */ +#define VSS_MEDIA_ID_FR_MODEM 0x00010FC9 +/*FR modem format */ +#define VSS_MEDIA_ID_HR_MODEM 0x00010FCA +/*HR modem format */ +#define VSS_MEDIA_ID_PCM_NB 0x00010FCB +/* Linear PCM (16-bit, little-endian). */ +#define VSS_MEDIA_ID_PCM_WB 0x00010FCC +/* Linear wideband PCM vocoder modem format (16 bits, little endian). */ +#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD +/* G.711 a-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE +/* G.711 mu-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G729 0x00010FD0 +/* G.729AB (contains two 10ms vocoder frames. */ + +#define VOICE_CMD_SET_PARAM 0x00011006 +#define VOICE_CMD_GET_PARAM 0x00011007 +#define VOICE_EVT_GET_PARAM_ACK 0x00011008 + +struct vss_ivocproc_cmd_create_full_control_session_t { + uint16_t direction; + /* + * stream direction. + * 0 : TX only + * 1 : RX only + * 2 : TX and RX + */ + uint32_t tx_port_id; + /* + * TX device port ID which vocproc will connect to. If not supplying a + * port ID set to VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t tx_topology_id; + /* + * Tx leg topology ID. If not supplying a topology ID set to + * VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + uint32_t rx_port_id; + /* + * RX device port ID which vocproc will connect to. If not supplying a + * port ID set to VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t rx_topology_id; + /* + * Rx leg topology ID. If not supplying a topology ID set to + * VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + int32_t network_id; + /* + * Network ID. (Refer to VSS_NETWORK_ID_XXX). If not supplying a network + * ID set to VSS_NETWORK_ID_DEFAULT. + */ +} __attribute__((packed)); + +struct vss_ivocproc_cmd_set_device_t { + uint32_t tx_port_id; + /**< + * TX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t tx_topology_id; + /**< + * TX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ + int32_t rx_port_id; + /**< + * RX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t rx_topology_id; + /**< + * RX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ +} __attribute__((packed)); + +struct vss_ivocproc_cmd_set_volume_index_t { + uint16_t vol_index; + /**< + * Volume index utilized by the vocproc to index into the volume table + * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set + * volume on the VDSP. + */ +} __attribute__((packed)); + +struct cvp_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_create_full_control_session_t cvp_session; +} __attribute__ ((packed)); + +struct cvp_command { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_set_device_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_device_t cvp_set_device; +} __attribute__ ((packed)); + +struct cvp_cache_calibration_data_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_cache_volume_calibration_table_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_set_vp3_data_cmd { + struct apr_hdr hdr; +} __attribute__((packed)); + +struct cvp_set_rx_volume_index_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx; +} __attribute__((packed)); + +/* CB for up-link packets. */ +typedef void (*ul_cb_fn)(uint8_t *voc_pkt, + uint32_t pkt_len, + void *private_data); + +/* CB for down-link packets. */ +typedef void (*dl_cb_fn)(uint8_t *voc_pkt, + uint32_t *pkt_len, + void *private_data); + +struct q_min_max_rate { + uint32_t min_rate; + uint32_t max_rate; +}; + +struct mvs_driver_info { + uint32_t media_type; + uint32_t rate; + uint32_t network_type; + uint32_t dtx_mode; + struct q_min_max_rate q_min_max_rate; + ul_cb_fn ul_cb; + dl_cb_fn dl_cb; + void *private_data; +}; + +struct incall_rec_info { + uint32_t pending; + uint32_t rec_mode; +}; + +struct incall_music_info { + uint32_t pending; + uint32_t playing; +}; + +struct voice_data { + int voc_state;/*INIT, CHANGE, RELEASE, RUN */ + + wait_queue_head_t mvm_wait; + wait_queue_head_t cvs_wait; + wait_queue_head_t cvp_wait; + + /* cache the values related to Rx and Tx */ + struct device_data dev_rx; + struct device_data dev_tx; + + /* call status */ + int v_call_status; /* Start or End */ + + u32 mvm_state; + u32 cvs_state; + u32 cvp_state; + + /* Handle to MVM */ + u16 mvm_handle; + /* Handle to CVS */ + u16 cvs_handle; + /* Handle to CVP */ + u16 cvp_handle; + + struct mutex lock; + + struct incall_rec_info rec_info; + + struct incall_music_info music_info; + + u16 session_id; +}; + +#define MAX_VOC_SESSIONS 2 +#define SESSION_ID_BASE 0xFFF0 + +struct common_data { + uint32_t voc_path; + uint32_t adsp_version; + uint32_t device_events; + + /* These default values are for all devices */ + uint32_t default_mute_val; + uint32_t default_vol_val; + uint32_t default_sample_val; + + /* APR to MVM in the modem */ + void *apr_mvm; + /* APR to CVS in the modem */ + void *apr_cvs; + /* APR to CVP in the modem */ + void *apr_cvp; + + /* APR to MVM in the Q6 */ + void *apr_q6_mvm; + /* APR to CVS in the Q6 */ + void *apr_q6_cvs; + /* APR to CVP in the Q6 */ + void *apr_q6_cvp; + + struct mutex common_lock; + + struct mvs_driver_info mvs_info; + + struct voice_data voice[MAX_VOC_SESSIONS]; +}; + +int voice_set_voc_path_full(uint32_t set); + +void voice_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + void *private_data); + +void voice_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode, + struct q_min_max_rate q_min_max_rate); + +int voice_start_record(uint32_t rec_mode, uint32_t set); + +int voice_start_playback(uint32_t set); + +u16 voice_get_session_id(const char *name); +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/rtac.h b/arch/arm/mach-msm/include/mach/qdsp6v2/rtac.h new file mode 100644 index 00000000000..f5bea3150fd --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/rtac.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __RTAC_H__ +#define __RTAC_H__ + +/* Voice Modes */ +#define RTAC_CVP 0 +#define RTAC_CVS 1 +#define RTAC_VOICE_MODES 2 + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id); +void rtac_remove_adm_device(u32 port_id); +void rtac_remove_popp_from_adm_devices(u32 popp_id); +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 session_id); +void rtac_remove_voice(u32 cvs_handle); +void rtac_set_adm_handle(void *handle); +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size); +void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_asm_handle(u32 session_id, void *handle); +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size); +void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_voice_handle(u32 mode, void *handle); +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size); +void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h b/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h new file mode 100644 index 00000000000..f747a806450 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp6v2/usf.h @@ -0,0 +1,274 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __USF_H__ +#define __USF_H__ + +#include +#include + +#define USF_IOCTL_MAGIC 'U' + +#define US_SET_TX_INFO _IOW(USF_IOCTL_MAGIC, 0, \ + struct us_tx_info_type) +#define US_START_TX _IO(USF_IOCTL_MAGIC, 1) +#define US_GET_TX_UPDATE _IOWR(USF_IOCTL_MAGIC, 2, \ + struct us_tx_update_info_type) +#define US_SET_RX_INFO _IOW(USF_IOCTL_MAGIC, 3, \ + struct us_rx_info_type) +#define US_SET_RX_UPDATE _IOWR(USF_IOCTL_MAGIC, 4, \ + struct us_rx_update_info_type) +#define US_START_RX _IO(USF_IOCTL_MAGIC, 5) + +#define US_STOP_TX _IO(USF_IOCTL_MAGIC, 6) +#define US_STOP_RX _IO(USF_IOCTL_MAGIC, 7) + +#define US_SET_DETECTION _IOWR(USF_IOCTL_MAGIC, 8, \ + struct us_detect_info_type) + +#define US_GET_VERSION _IOWR(USF_IOCTL_MAGIC, 9, \ + struct us_version_info_type) + +/* Special timeout values */ +#define USF_NO_WAIT_TIMEOUT 0x00000000 +/* Infinitive */ +#define USF_INFINITIVE_TIMEOUT 0xffffffff +/* Default value, used by the driver */ +#define USF_DEFAULT_TIMEOUT 0xfffffffe + +/* US detection place (HW|FW) */ +enum us_detect_place_enum { +/* US is detected in HW */ + US_DETECT_HW, +/* US is detected in FW */ + US_DETECT_FW +}; + +/* US detection mode */ +enum us_detect_mode_enum { +/* US detection is disabled */ + US_DETECT_DISABLED_MODE, +/* US detection is enabled in continue mode */ + US_DETECT_CONTINUE_MODE, +/* US detection is enabled in one shot mode */ + US_DETECT_SHOT_MODE +}; + +/* Encoder (TX), decoder (RX) supported US data formats */ +#define USF_POINT_EPOS_FORMAT 0 +#define USF_RAW_FORMAT 1 + +/* Indexes of event types, produced by the calculators */ +#define USF_TSC_EVENT_IND 0 +#define USF_TSC_PTR_EVENT_IND 1 +#define USF_MOUSE_EVENT_IND 2 +#define USF_KEYBOARD_EVENT_IND 3 +#define USF_MAX_EVENT_IND 4 + +/* Types of events, produced by the calculators */ +#define USF_NO_EVENT 0 +#define USF_TSC_EVENT (1 << USF_TSC_EVENT_IND) +#define USF_TSC_PTR_EVENT (1 << USF_TSC_PTR_EVENT_IND) +#define USF_MOUSE_EVENT (1 << USF_MOUSE_EVENT_IND) +#define USF_KEYBOARD_EVENT (1 << USF_KEYBOARD_EVENT_IND) +#define USF_ALL_EVENTS (USF_TSC_EVENT |\ + USF_TSC_PTR_EVENT |\ + USF_MOUSE_EVENT |\ + USF_KEYBOARD_EVENT) + +/* min, max array dimension */ +#define MIN_MAX_DIM 2 + +/* coordinates (x,y,z) array dimension */ +#define COORDINATES_DIM 3 + +/* tilts (x,y) array dimension */ +#define TILTS_DIM 2 + +/* Max size of the client name */ +#define USF_MAX_CLIENT_NAME_SIZE 20 +/* Info structure common for TX and RX */ +struct us_xx_info_type { +/* Input: general info */ +/* Name of the client - event calculator */ + const char *client_name; +/* Selected device identification, accepted in the kernel's CAD */ + uint32_t dev_id; +/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */ + uint32_t stream_format; +/* Required sample rate in Hz */ + uint32_t sample_rate; +/* Size of a buffer (bytes) for US data transfer between the module and USF */ + uint32_t buf_size; +/* Number of the buffers for the US data transfer */ + uint16_t buf_num; +/* Number of the microphones (TX) or speakers(RX) */ + uint16_t port_cnt; +/* Microphones(TX) or speakers(RX) indexes in their enumeration */ + uint8_t port_id[4]; +/* Bits per sample 16 or 32 */ + uint16_t bits_per_sample; +/* Input: Transparent info for encoder in the LPASS */ +/* Parameters data size in bytes */ + uint16_t params_data_size; +/* Pointer to the parameters */ + uint8_t *params_data; +}; + +/* Input events sources */ +enum us_input_event_src_type { + US_INPUT_SRC_PEN, + US_INPUT_SRC_FINGER, + US_INPUT_SRC_UNDEF +}; + +struct us_input_info_type { + /* Touch screen dimensions: min & max;for input module */ + int tsc_x_dim[MIN_MAX_DIM]; + int tsc_y_dim[MIN_MAX_DIM]; + int tsc_z_dim[MIN_MAX_DIM]; + /* Touch screen tilt dimensions: min & max;for input module */ + int tsc_x_tilt[MIN_MAX_DIM]; + int tsc_y_tilt[MIN_MAX_DIM]; + /* Touch screen pressure limits: min & max; for input module */ + int tsc_pressure[MIN_MAX_DIM]; + /* Bitmap of types of events (USF_X_EVENT), produced by calculator */ + uint16_t event_types; + /* Input event source */ + enum us_input_event_src_type event_src; + /* Bitmap of types of events from devs, conflicting with USF */ + uint16_t conflicting_event_types; +}; + +struct us_tx_info_type { + /* Common info */ + struct us_xx_info_type us_xx_info; + /* Info specific for TX*/ + struct us_input_info_type input_info; +}; + +struct us_rx_info_type { + /* Common info */ + struct us_xx_info_type us_xx_info; + /* Info specific for RX*/ +}; + +struct point_event_type { +/* Pen coordinates (x, y, z) in units, defined by */ + int coordinates[COORDINATES_DIM]; + /* {x;y} in transparent units */ + int inclinations[TILTS_DIM]; +/* [0-1023] (10bits); 0 - pen up */ + uint32_t pressure; +}; + +/* Mouse buttons, supported by USF */ +#define USF_BUTTON_LEFT_MASK 1 +#define USF_BUTTON_MIDDLE_MASK 2 +#define USF_BUTTON_RIGHT_MASK 4 +struct mouse_event_type { +/* The mouse relative movement (dX, dY, dZ) */ + int rels[COORDINATES_DIM]; +/* Bitmap of mouse buttons states: 1 - down, 0 - up; */ + uint16_t buttons_states; +}; + +struct key_event_type { +/* Calculated MS key- see input.h. */ + uint32_t key; +/* Keyboard's key state: 1 - down, 0 - up; */ + uint8_t key_state; +}; + +struct usf_event_type { +/* Event sequence number */ + uint32_t seq_num; +/* Event generation system time */ + uint32_t timestamp; +/* Destination input event type index (e.g. touch screen, mouse, key) */ + uint16_t event_type_ind; + union { + struct point_event_type point_event; + struct mouse_event_type mouse_event; + struct key_event_type key_event; + } event_data; +}; + +struct us_tx_update_info_type { +/* Input general: */ +/* Number of calculated events */ + uint16_t event_counter; +/* Calculated events or NULL */ + struct usf_event_type *event; +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +/* Time (sec) to wait for data or special values: */ +/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */ + uint32_t timeout; +/* Events (from conflicting devs) to be disabled/enabled */ + uint16_t event_filters; + +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* Pointer to the parameters */ + uint8_t *params_data; +/* Output parameters: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +}; + +struct us_rx_update_info_type { +/* Input general: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* pPointer to the parameters */ + uint8_t *params_data; +/* Output parameters: */ +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +}; + +struct us_detect_info_type { +/* US detection place (HW|FW) */ +/* NA in the Active and OFF states */ + enum us_detect_place_enum us_detector; +/* US detection mode */ + enum us_detect_mode_enum us_detect_mode; +/* US data dropped during this time (msec) */ + uint32_t skip_time; +/* Transparent data size */ + uint16_t params_data_size; +/* Pointer to the transparent data */ + uint8_t *params_data; +/* Time (sec) to wait for US presence event */ + uint32_t detect_timeout; +/* Out parameter: US presence */ + bool is_us; +}; + +struct us_version_info_type { +/* Size of memory for the version string */ + uint16_t buf_size; +/* Pointer to the memory for the version string */ + char *pbuf; +}; + +#endif /* __USF_H__ */ diff --git a/arch/arm/mach-msm/include/mach/qdss.h b/arch/arm/mach-msm/include/mach/qdss.h new file mode 100644 index 00000000000..05d85770fe9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdss.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MACH_QDSS_H +#define __MACH_QDSS_H + +struct qdss_source { + struct list_head link; + const char *name; + uint32_t fport_mask; +}; + +struct msm_qdss_platform_data { + struct qdss_source *src_table; + size_t size; + uint8_t afamily; +}; + +#ifdef CONFIG_MSM_QDSS +extern struct qdss_source *qdss_get(const char *name); +extern void qdss_put(struct qdss_source *src); +extern int qdss_enable(struct qdss_source *src); +extern void qdss_disable(struct qdss_source *src); +extern void qdss_disable_sink(void); +extern int qdss_clk_enable(void); +extern void qdss_clk_disable(void); +#else +static inline struct qdss_source *qdss_get(const char *name) { return NULL; } +static inline void qdss_put(struct qdss_source *src) {} +static inline int qdss_enable(struct qdss_source *src) { return -ENOSYS; } +static inline void qdss_disable(struct qdss_source *src) {} +static inline void qdss_disable_sink(void) {} +static inline int qdss_clk_enable(void) { return -ENOSYS; } +static inline void qdss_clk_disable(void) {} +#endif + +#ifdef CONFIG_MSM_JTAG +extern void msm_jtag_save_state(void); +extern void msm_jtag_restore_state(void); +#else +static inline void msm_jtag_save_state(void) {} +static inline void msm_jtag_restore_state(void) {} +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/qpnp-int.h b/arch/arm/mach-msm/include/mach/qpnp-int.h new file mode 100644 index 00000000000..a79d2fc2b86 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qpnp-int.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef QPNPINT_H +#define QPNPINT_H + +#include + +struct qpnp_irq_spec { + uint8_t slave; /* 0-15 */ + uint8_t per; /* 0-255 */ + uint8_t irq; /* 0-7 */ +}; + +struct qpnp_local_int { + /* mask - Invoke PMIC Arbiter local mask handler */ + int (*mask)(struct spmi_controller *spmi_ctrl, + struct qpnp_irq_spec *spec, + uint32_t priv_d); + /* unmask - Invoke PMIC Arbiter local unmask handler */ + int (*unmask)(struct spmi_controller *spmi_ctrl, + struct qpnp_irq_spec *spec, + uint32_t priv_d); + /* register_priv_data - Return per irq priv data */ + int (*register_priv_data)(struct spmi_controller *spmi_ctrl, + struct qpnp_irq_spec *spec, + uint32_t *priv_d); +}; + +#ifdef CONFIG_MSM_QPNP_INT +/** + * qpnpint_of_init() - Device Tree irq initialization + * + * Standard Device Tree init routine to be called from + * of_irq_init(). + */ +int __init qpnpint_of_init(struct device_node *node, + struct device_node *parent); + +/** + * qpnpint_register_controller() - Register local interrupt callbacks + * + * Used by the PMIC Arbiter driver or equivalent to register + * callbacks for interrupt events. + */ +int qpnpint_register_controller(unsigned int busno, + struct qpnp_local_int *li_cb); + +/** + * qpnpint_handle_irq - Main interrupt handling routine + * + * Pass a PMIC Arbiter interrupt to Linux. + */ +int qpnpint_handle_irq(struct spmi_controller *spmi_ctrl, + struct qpnp_irq_spec *spec); +#else +static inline int __init qpnpint_of_init(struct device_node *node, + struct device_node *parent) +{ + return -ENXIO; +} +static inline int qpnpint_register_controller(unsigned int busno, + struct qpnp_local_int *li_cb) +{ + return -ENXIO; +} + +static inline int qpnpint_handle_irq(struct spmi_controller *spmi_ctrl, + struct qpnp_irq_spec *spec) +{ + return -ENXIO; +} +#endif /* CONFIG_MSM_QPNP_INT */ +#endif /* QPNPINT_H */ diff --git a/arch/arm/mach-msm/include/mach/remote_spinlock.h b/arch/arm/mach-msm/include/mach/remote_spinlock.h new file mode 100644 index 00000000000..75b70f3b591 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h @@ -0,0 +1,299 @@ +/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Part of this this code is based on the standard ARM spinlock + * implementation (asm/spinlock.h) found in the 2.6.29 kernel. + */ + +#ifndef __ASM__ARCH_QC_REMOTE_SPINLOCK_H +#define __ASM__ARCH_QC_REMOTE_SPINLOCK_H + +#include +#include + +/* Remote spinlock definitions. */ + +struct dek_spinlock { + volatile uint8_t self_lock; + volatile uint8_t other_lock; + volatile uint8_t next_yield; + uint8_t pad; +}; + +typedef union { + volatile uint32_t lock; + struct dek_spinlock dek; +} raw_remote_spinlock_t; + +typedef raw_remote_spinlock_t *_remote_spinlock_t; + +#define remote_spinlock_id_t const char * +#define SMEM_SPINLOCK_PID_APPS 1 + +static inline void __raw_remote_ex_spin_lock(raw_remote_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( +"1: ldrex %0, [%1]\n" +" teq %0, #0\n" +" strexeq %0, %2, [%1]\n" +" teqeq %0, #0\n" +" bne 1b" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + smp_mb(); +} + +static inline int __raw_remote_ex_spin_trylock(raw_remote_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( +" ldrex %0, [%1]\n" +" teq %0, #0\n" +" strexeq %0, %2, [%1]\n" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + if (tmp == 0) { + smp_mb(); + return 1; + } + return 0; +} + +static inline void __raw_remote_ex_spin_unlock(raw_remote_spinlock_t *lock) +{ + smp_mb(); + + __asm__ __volatile__( +" str %1, [%0]\n" + : + : "r" (&lock->lock), "r" (0) + : "cc"); +} + +static inline void __raw_remote_swp_spin_lock(raw_remote_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( +"1: swp %0, %2, [%1]\n" +" teq %0, #0\n" +" bne 1b" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + smp_mb(); +} + +static inline int __raw_remote_swp_spin_trylock(raw_remote_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( +" swp %0, %2, [%1]\n" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + if (tmp == 0) { + smp_mb(); + return 1; + } + return 0; +} + +static inline void __raw_remote_swp_spin_unlock(raw_remote_spinlock_t *lock) +{ + smp_mb(); + + __asm__ __volatile__( +" str %1, [%0]" + : + : "r" (&lock->lock), "r" (0) + : "cc"); +} + +#define DEK_LOCK_REQUEST 1 +#define DEK_LOCK_YIELD (!DEK_LOCK_REQUEST) +#define DEK_YIELD_TURN_SELF 0 +static inline void __raw_remote_dek_spin_lock(raw_remote_spinlock_t *lock) +{ + lock->dek.self_lock = DEK_LOCK_REQUEST; + + while (lock->dek.other_lock) { + + if (lock->dek.next_yield == DEK_YIELD_TURN_SELF) + lock->dek.self_lock = DEK_LOCK_YIELD; + + while (lock->dek.other_lock) + ; + + lock->dek.self_lock = DEK_LOCK_REQUEST; + } + lock->dek.next_yield = DEK_YIELD_TURN_SELF; + + smp_mb(); +} + +static inline int __raw_remote_dek_spin_trylock(raw_remote_spinlock_t *lock) +{ + lock->dek.self_lock = DEK_LOCK_REQUEST; + + if (lock->dek.other_lock) { + lock->dek.self_lock = DEK_LOCK_YIELD; + return 0; + } + + lock->dek.next_yield = DEK_YIELD_TURN_SELF; + + smp_mb(); + return 1; +} + +static inline void __raw_remote_dek_spin_unlock(raw_remote_spinlock_t *lock) +{ + smp_mb(); + + lock->dek.self_lock = DEK_LOCK_YIELD; +} + +static inline int __raw_remote_dek_spin_release(raw_remote_spinlock_t *lock, + uint32_t pid) +{ + return -EINVAL; +} + +static inline void __raw_remote_sfpb_spin_lock(raw_remote_spinlock_t *lock) +{ + do { + writel_relaxed(SMEM_SPINLOCK_PID_APPS, lock); + smp_mb(); + } while (readl_relaxed(lock) != SMEM_SPINLOCK_PID_APPS); +} + +static inline int __raw_remote_sfpb_spin_trylock(raw_remote_spinlock_t *lock) +{ + return 1; +} + +static inline void __raw_remote_sfpb_spin_unlock(raw_remote_spinlock_t *lock) +{ + writel_relaxed(0, lock); + smp_mb(); +} + +/** + * Release spinlock if it is owned by @pid. + * + * This is only to be used for situations where the processor owning + * the spinlock has crashed and the spinlock must be released. + * + * @lock - lock structure + * @pid - processor ID of processor to release + */ +static inline int __raw_remote_gen_spin_release(raw_remote_spinlock_t *lock, + uint32_t pid) +{ + int ret = 1; + + if (readl_relaxed(&lock->lock) == pid) { + writel_relaxed(0, &lock->lock); + wmb(); + ret = 0; + } + return ret; +} + +#if defined(CONFIG_MSM_SMD) || defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB) +int _remote_spin_lock_init(remote_spinlock_id_t, _remote_spinlock_t *lock); +void _remote_spin_release_all(uint32_t pid); +#else +static inline +int _remote_spin_lock_init(remote_spinlock_id_t id, _remote_spinlock_t *lock) +{ + return -EINVAL; +} +static inline void _remote_spin_release_all(uint32_t pid) {} +#endif + +#if defined(CONFIG_MSM_REMOTE_SPINLOCK_DEKKERS) +/* Use Dekker's algorithm when LDREX/STREX and SWP are unavailable for + * shared memory */ +#define _remote_spin_lock(lock) __raw_remote_dek_spin_lock(*lock) +#define _remote_spin_unlock(lock) __raw_remote_dek_spin_unlock(*lock) +#define _remote_spin_trylock(lock) __raw_remote_dek_spin_trylock(*lock) +#define _remote_spin_release(lock, pid) __raw_remote_dek_spin_release(*lock,\ + pid) +#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SWP) +/* Use SWP-based locks when LDREX/STREX are unavailable for shared memory. */ +#define _remote_spin_lock(lock) __raw_remote_swp_spin_lock(*lock) +#define _remote_spin_unlock(lock) __raw_remote_swp_spin_unlock(*lock) +#define _remote_spin_trylock(lock) __raw_remote_swp_spin_trylock(*lock) +#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock,\ + pid) +#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB) +/* Use SFPB Hardware Mutex Registers */ +#define _remote_spin_lock(lock) __raw_remote_sfpb_spin_lock(*lock) +#define _remote_spin_unlock(lock) __raw_remote_sfpb_spin_unlock(*lock) +#define _remote_spin_trylock(lock) __raw_remote_sfpb_spin_trylock(*lock) +#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock,\ + pid) +#else +/* Use LDREX/STREX for shared memory locking, when available */ +#define _remote_spin_lock(lock) __raw_remote_ex_spin_lock(*lock) +#define _remote_spin_unlock(lock) __raw_remote_ex_spin_unlock(*lock) +#define _remote_spin_trylock(lock) __raw_remote_ex_spin_trylock(*lock) +#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock, \ + pid) +#endif + +/* Remote mutex definitions. */ + +typedef struct { + _remote_spinlock_t r_spinlock; + uint32_t delay_us; +} _remote_mutex_t; + +struct remote_mutex_id { + remote_spinlock_id_t r_spinlock_id; + uint32_t delay_us; +}; + +#ifdef CONFIG_MSM_SMD +int _remote_mutex_init(struct remote_mutex_id *id, _remote_mutex_t *lock); +void _remote_mutex_lock(_remote_mutex_t *lock); +void _remote_mutex_unlock(_remote_mutex_t *lock); +int _remote_mutex_trylock(_remote_mutex_t *lock); +#else +static inline +int _remote_mutex_init(struct remote_mutex_id *id, _remote_mutex_t *lock) +{ + return -EINVAL; +} +static inline void _remote_mutex_lock(_remote_mutex_t *lock) {} +static inline void _remote_mutex_unlock(_remote_mutex_t *lock) {} +static inline int _remote_mutex_trylock(_remote_mutex_t *lock) +{ + return 0; +} +#endif + +#endif /* __ASM__ARCH_QC_REMOTE_SPINLOCK_H */ diff --git a/arch/arm/mach-msm/include/mach/restart.h b/arch/arm/mach-msm/include/mach/restart.h new file mode 100644 index 00000000000..b913e3f1dc6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/restart.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ASM_ARCH_MSM_RESTART_H_ +#define _ASM_ARCH_MSM_RESTART_H_ + +#define RESTART_NORMAL 0x0 +#define RESTART_DLOAD 0x1 + +#if defined(CONFIG_MSM_NATIVE_RESTART) +void msm_set_restart_mode(int mode); +void msm_restart(char mode, const char *cmd); +#elif defined(CONFIG_ARCH_FSM9XXX) +void fsm_restart(char mode, const char *cmd); +#else +#define msm_set_restart_mode(mode) +#endif + +extern int pmic_reset_irq; + +#endif + diff --git a/arch/arm/mach-msm/include/mach/rpc_hsusb.h b/arch/arm/mach-msm/include/mach/rpc_hsusb.h new file mode 100644 index 00000000000..88d76502a2b --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpc_hsusb.h @@ -0,0 +1,99 @@ +/* linux/include/mach/rpc_hsusb.h + * + * Copyright (c) 2008-2010, 2012 Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#ifndef __ASM_ARCH_MSM_RPC_HSUSB_H +#define __ASM_ARCH_MSM_RPC_HSUSB_H + +#include +#include +#include + +#if defined(CONFIG_MSM_ONCRPCROUTER) && !defined(CONFIG_ARCH_MSM8X60) +int msm_hsusb_rpc_connect(void); +int msm_hsusb_phy_reset(void); +int msm_hsusb_vbus_powerup(void); +int msm_hsusb_vbus_shutdown(void); +int msm_hsusb_reset_rework_installed(void); +int msm_hsusb_enable_pmic_ulpidata0(void); +int msm_hsusb_disable_pmic_ulpidata0(void); +int msm_hsusb_rpc_close(void); + +int msm_chg_rpc_connect(void); +int msm_chg_usb_charger_connected(uint32_t type); +int msm_chg_usb_i_is_available(uint32_t sample); +int msm_chg_usb_i_is_not_available(void); +int msm_chg_usb_charger_disconnected(void); +int msm_chg_rpc_close(void); + +#ifdef CONFIG_USB_MSM_72K +int hsusb_chg_init(int connect); +void hsusb_chg_vbus_draw(unsigned mA); +void hsusb_chg_connected(enum chg_type chgtype); +#endif + + +int msm_fsusb_rpc_init(struct msm_otg_ops *ops); +int msm_fsusb_init_phy(void); +int msm_fsusb_reset_phy(void); +int msm_fsusb_suspend_phy(void); +int msm_fsusb_resume_phy(void); +int msm_fsusb_rpc_close(void); +int msm_fsusb_remote_dev_disconnected(void); +int msm_fsusb_set_remote_wakeup(void); +void msm_fsusb_rpc_deinit(void); + +/* wrapper to send pid and serial# info to bootloader */ +int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum); +#else +static inline int msm_hsusb_rpc_connect(void) { return 0; } +static inline int msm_hsusb_phy_reset(void) { return 0; } +static inline int msm_hsusb_vbus_powerup(void) { return 0; } +static inline int msm_hsusb_vbus_shutdown(void) { return 0; } +static inline int msm_hsusb_reset_rework_installed(void) { return 0; } +static inline int msm_hsusb_enable_pmic_ulpidata0(void) { return 0; } +static inline int msm_hsusb_disable_pmic_ulpidata0(void) { return 0; } +static inline int msm_hsusb_rpc_close(void) { return 0; } + +static inline int msm_chg_rpc_connect(void) { return 0; } +static inline int msm_chg_usb_charger_connected(uint32_t type) { return 0; } +static inline int msm_chg_usb_i_is_available(uint32_t sample) { return 0; } +static inline int msm_chg_usb_i_is_not_available(void) { return 0; } +static inline int msm_chg_usb_charger_disconnected(void) { return 0; } +static inline int msm_chg_rpc_close(void) { return 0; } + +#ifdef CONFIG_USB_MSM_72K +static inline int hsusb_chg_init(int connect) { return 0; } +static inline void hsusb_chg_vbus_draw(unsigned mA) { } +static inline void hsusb_chg_connected(enum chg_type chgtype) { } +#endif + +static inline int msm_fsusb_rpc_init(struct msm_otg_ops *ops) { return 0; } +static inline int msm_fsusb_init_phy(void) { return 0; } +static inline int msm_fsusb_reset_phy(void) { return 0; } +static inline int msm_fsusb_suspend_phy(void) { return 0; } +static inline int msm_fsusb_resume_phy(void) { return 0; } +static inline int msm_fsusb_rpc_close(void) { return 0; } +static inline int msm_fsusb_remote_dev_disconnected(void) { return 0; } +static inline int msm_fsusb_set_remote_wakeup(void) { return 0; } +static inline void msm_fsusb_rpc_deinit(void) { } +static inline int +usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) { return 0; } +#endif +#endif diff --git a/arch/arm/mach-msm/include/mach/rpc_pmapp.h b/arch/arm/mach-msm/include/mach/rpc_pmapp.h new file mode 100644 index 00000000000..86f04bf2a07 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpc_pmapp.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_RPC_PMAPP_H +#define __ASM_ARCH_MSM_RPC_PMAPP_H + +#include + +/* Clock voting ids */ +enum { + PMAPP_CLOCK_ID_DO = 0, + PMAPP_CLOCK_ID_D1, + PMAPP_CLOCK_ID_A0, + PMAPP_CLOCK_ID_A1, +}; + +/* Clock voting types */ +enum { + PMAPP_CLOCK_VOTE_OFF = 0, + PMAPP_CLOCK_VOTE_ON, + PMAPP_CLOCK_VOTE_PIN_CTRL, +}; + +/* vreg ids */ +enum { + PMAPP_VREG_LDO22 = 14, + PMAPP_VREG_S3 = 21, + PMAPP_VREG_S2 = 23, + PMAPP_VREG_S4 = 24, +}; + +/* SMPS clock voting types */ +enum { + PMAPP_SMPS_CLK_VOTE_DONTCARE = 0, + PMAPP_SMPS_CLK_VOTE_2P74, /* 2.74 MHz */ + PMAPP_SMPS_CLK_VOTE_1P6, /* 1.6 MHz */ +}; + +/* SMPS mode voting types */ +enum { + PMAPP_SMPS_MODE_VOTE_DONTCARE = 0, + PMAPP_SMPS_MODE_VOTE_PWM, + PMAPP_SMPS_MODE_VOTE_PFM, + PMAPP_SMPS_MODE_VOTE_AUTO +}; + +int msm_pm_app_rpc_init(void(*callback)(int online)); +void msm_pm_app_rpc_deinit(void(*callback)(int online)); +int msm_pm_app_register_vbus_sn(void (*callback)(int online)); +void msm_pm_app_unregister_vbus_sn(void (*callback)(int online)); +int msm_pm_app_enable_usb_ldo(int); +int pmic_vote_3p3_pwr_sel_switch(int boost); + +int pmapp_display_clock_config(uint enable); + +int pmapp_clock_vote(const char *voter_id, uint clock_id, uint vote); +int pmapp_smps_clock_vote(const char *voter_id, uint vreg_id, uint vote); +int pmapp_vreg_level_vote(const char *voter_id, uint vreg_id, uint level); +int pmapp_smps_mode_vote(const char *voter_id, uint vreg_id, uint mode); +int pmapp_vreg_pincntrl_vote(const char *voter_id, uint vreg_id, + uint clock_id, uint vote); +int pmapp_disp_backlight_set_brightness(int value); +void pmapp_disp_backlight_init(void); +int pmapp_vreg_lpm_pincntrl_vote(const char *voter_id, uint vreg_id, + uint clock_id, uint vote); +#endif diff --git a/arch/arm/mach-msm/include/mach/rpc_server_handset.h b/arch/arm/mach-msm/include/mach/rpc_server_handset.h new file mode 100644 index 00000000000..e1dc841809b --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpc_server_handset.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_RPC_SERVER_HANDSET_H +#define __ASM_ARCH_MSM_RPC_SERVER_HANDSET_H + +struct msm_handset_platform_data { + const char *hs_name; + uint32_t pwr_key_delay_ms; /* default 500ms */ +}; + +void report_headset_status(bool connected); + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-8064.h b/arch/arm/mach-msm/include/mach/rpm-8064.h new file mode 100644 index 00000000000..c4c6b0a50b1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-8064.h @@ -0,0 +1,432 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_8064_H +#define __ARCH_ARM_MACH_MSM_RPM_8064_H + +/* RPM control message RAM enums */ +enum { + MSM_RPM_8064_CTRL_VERSION_MAJOR, + MSM_RPM_8064_CTRL_VERSION_MINOR, + MSM_RPM_8064_CTRL_VERSION_BUILD, + + MSM_RPM_8064_CTRL_REQ_CTX_0, + MSM_RPM_8064_CTRL_REQ_CTX_7 = MSM_RPM_8064_CTRL_REQ_CTX_0 + 7, + MSM_RPM_8064_CTRL_REQ_SEL_0, + MSM_RPM_8064_CTRL_REQ_SEL_3 = MSM_RPM_8064_CTRL_REQ_SEL_0 + 3, + MSM_RPM_8064_CTRL_ACK_CTX_0, + MSM_RPM_8064_CTRL_ACK_CTX_7 = MSM_RPM_8064_CTRL_ACK_CTX_0 + 7, + MSM_RPM_8064_CTRL_ACK_SEL_0, + MSM_RPM_8064_CTRL_ACK_SEL_7 = MSM_RPM_8064_CTRL_ACK_SEL_0 + 7, +}; + +/* RPM resource select enums defined for RPM core + NOT IN SEQUENTIAL ORDER */ +enum { + MSM_RPM_8064_SEL_NOTIFICATION = 0, + MSM_RPM_8064_SEL_INVALIDATE = 1, + MSM_RPM_8064_SEL_TRIGGER_TIMED = 2, + MSM_RPM_8064_SEL_RPM_CTL = 3, + + MSM_RPM_8064_SEL_CXO_CLK = 5, + MSM_RPM_8064_SEL_PXO_CLK = 6, + MSM_RPM_8064_SEL_QDSS_CLK = 7, + MSM_RPM_8064_SEL_APPS_FABRIC_CLK = 8, + MSM_RPM_8064_SEL_SYSTEM_FABRIC_CLK = 9, + MSM_RPM_8064_SEL_MM_FABRIC_CLK = 10, + MSM_RPM_8064_SEL_DAYTONA_FABRIC_CLK = 11, + MSM_RPM_8064_SEL_SFPB_CLK = 12, + MSM_RPM_8064_SEL_CFPB_CLK = 13, + MSM_RPM_8064_SEL_MMFPB_CLK = 14, + MSM_RPM_8064_SEL_EBI1_CLK = 16, + + MSM_RPM_8064_SEL_APPS_FABRIC_CFG_HALT = 18, + MSM_RPM_8064_SEL_APPS_FABRIC_CFG_CLKMOD = 19, + MSM_RPM_8064_SEL_APPS_FABRIC_CFG_IOCTL = 20, + MSM_RPM_8064_SEL_APPS_FABRIC_ARB = 21, + + MSM_RPM_8064_SEL_SYS_FABRIC_CFG_HALT = 22, + MSM_RPM_8064_SEL_SYS_FABRIC_CFG_CLKMOD = 23, + MSM_RPM_8064_SEL_SYS_FABRIC_CFG_IOCTL = 24, + MSM_RPM_8064_SEL_SYSTEM_FABRIC_ARB = 25, + + MSM_RPM_8064_SEL_MMSS_FABRIC_CFG_HALT = 26, + MSM_RPM_8064_SEL_MMSS_FABRIC_CFG_CLKMOD = 27, + MSM_RPM_8064_SEL_MMSS_FABRIC_CFG_IOCTL = 28, + MSM_RPM_8064_SEL_MM_FABRIC_ARB = 29, + + MSM_RPM_8064_SEL_PM8921_S1 = 30, + MSM_RPM_8064_SEL_PM8921_S2 = 31, + MSM_RPM_8064_SEL_PM8921_S3 = 32, + MSM_RPM_8064_SEL_PM8921_S4 = 33, + MSM_RPM_8064_SEL_PM8921_S5 = 34, + MSM_RPM_8064_SEL_PM8921_S6 = 35, + MSM_RPM_8064_SEL_PM8921_S7 = 36, + MSM_RPM_8064_SEL_PM8921_S8 = 37, + MSM_RPM_8064_SEL_PM8921_L1 = 38, + MSM_RPM_8064_SEL_PM8921_L2 = 39, + MSM_RPM_8064_SEL_PM8921_L3 = 40, + MSM_RPM_8064_SEL_PM8921_L4 = 41, + MSM_RPM_8064_SEL_PM8921_L5 = 42, + MSM_RPM_8064_SEL_PM8921_L6 = 43, + MSM_RPM_8064_SEL_PM8921_L7 = 44, + MSM_RPM_8064_SEL_PM8921_L8 = 45, + MSM_RPM_8064_SEL_PM8921_L9 = 46, + MSM_RPM_8064_SEL_PM8921_L10 = 47, + MSM_RPM_8064_SEL_PM8921_L11 = 48, + MSM_RPM_8064_SEL_PM8921_L12 = 49, + MSM_RPM_8064_SEL_PM8921_L13 = 50, + MSM_RPM_8064_SEL_PM8921_L14 = 51, + MSM_RPM_8064_SEL_PM8921_L15 = 52, + MSM_RPM_8064_SEL_PM8921_L16 = 53, + MSM_RPM_8064_SEL_PM8921_L17 = 54, + MSM_RPM_8064_SEL_PM8921_L18 = 55, + MSM_RPM_8064_SEL_PM8921_L19 = 56, + MSM_RPM_8064_SEL_PM8921_L20 = 57, + MSM_RPM_8064_SEL_PM8921_L21 = 58, + MSM_RPM_8064_SEL_PM8921_L22 = 59, + MSM_RPM_8064_SEL_PM8921_L23 = 60, + MSM_RPM_8064_SEL_PM8921_L24 = 61, + MSM_RPM_8064_SEL_PM8921_L25 = 62, + MSM_RPM_8064_SEL_PM8921_L26 = 63, + MSM_RPM_8064_SEL_PM8921_L27 = 64, + MSM_RPM_8064_SEL_PM8921_L28 = 65, + MSM_RPM_8064_SEL_PM8921_L29 = 66, + MSM_RPM_8064_SEL_PM8921_CLK1 = 67, + MSM_RPM_8064_SEL_PM8921_CLK2 = 68, + MSM_RPM_8064_SEL_PM8921_LVS1 = 69, + MSM_RPM_8064_SEL_PM8921_LVS2 = 70, + MSM_RPM_8064_SEL_PM8921_LVS3 = 71, + MSM_RPM_8064_SEL_PM8921_LVS4 = 72, + MSM_RPM_8064_SEL_PM8921_LVS5 = 73, + MSM_RPM_8064_SEL_PM8921_LVS6 = 74, + MSM_RPM_8064_SEL_PM8921_LVS7 = 75, + MSM_RPM_8064_SEL_PM8821_S1 = 76, + MSM_RPM_8064_SEL_PM8821_S2 = 77, + MSM_RPM_8064_SEL_PM8821_L1 = 78, + + MSM_RPM_8064_SEL_NCP = 80, + MSM_RPM_8064_SEL_CXO_BUFFERS = 81, + MSM_RPM_8064_SEL_USB_OTG_SWITCH = 82, + MSM_RPM_8064_SEL_HDMI_SWITCH = 83, + MSM_RPM_8064_SEL_DDR_DMM = 84, + + MSM_RPM_8064_SEL_LAST = MSM_RPM_8064_SEL_DDR_DMM, +}; + +/* RPM resource (4 byte) word ID enum */ +enum { + MSM_RPM_8064_ID_NOTIFICATION_CONFIGURED_0 = 0, + MSM_RPM_8064_ID_NOTIFICATION_CONFIGURED_3 = + MSM_RPM_8064_ID_NOTIFICATION_CONFIGURED_0 + 3, + + MSM_RPM_8064_ID_NOTIFICATION_REGISTERED_0 = 4, + MSM_RPM_8064_ID_NOTIFICATION_REGISTERED_3 = + MSM_RPM_8064_ID_NOTIFICATION_REGISTERED_0 + 3, + + MSM_RPM_8064_ID_INVALIDATE_0 = 8, + MSM_RPM_8064_ID_INVALIDATE_7 = + MSM_RPM_8064_ID_INVALIDATE_0 + 7, + + MSM_RPM_8064_ID_TRIGGER_TIMED_TO = 16, + MSM_RPM_8064_ID_TRIGGER_TIMED_SCLK_COUNT = 17, + + MSM_RPM_8064_ID_RPM_CTL = 18, + + /* TRIGGER_CLEAR/SET deprecated in these 24 RESERVED bytes */ + MSM_RPM_8064_ID_RESERVED_0 = 19, + MSM_RPM_8064_ID_RESERVED_5 = + MSM_RPM_8064_ID_RESERVED_0 + 5, + + MSM_RPM_8064_ID_CXO_CLK = 25, + MSM_RPM_8064_ID_PXO_CLK = 26, + MSM_RPM_8064_ID_APPS_FABRIC_CLK = 27, + MSM_RPM_8064_ID_SYSTEM_FABRIC_CLK = 28, + MSM_RPM_8064_ID_MM_FABRIC_CLK = 29, + MSM_RPM_8064_ID_DAYTONA_FABRIC_CLK = 30, + MSM_RPM_8064_ID_SFPB_CLK = 31, + MSM_RPM_8064_ID_CFPB_CLK = 32, + MSM_RPM_8064_ID_MMFPB_CLK = 33, + MSM_RPM_8064_ID_EBI1_CLK = 34, + + MSM_RPM_8064_ID_APPS_FABRIC_CFG_HALT_0 = 35, + MSM_RPM_8064_ID_APPS_FABRIC_CFG_HALT_1 = 36, + MSM_RPM_8064_ID_APPS_FABRIC_CFG_CLKMOD_0 = 37, + MSM_RPM_8064_ID_APPS_FABRIC_CFG_CLKMOD_1 = 38, + MSM_RPM_8064_ID_APPS_FABRIC_CFG_CLKMOD_2 = 39, + MSM_RPM_8064_ID_APPS_FABRIC_CFG_IOCTL = 40, + MSM_RPM_8064_ID_APPS_FABRIC_ARB_0 = 41, + MSM_RPM_8064_ID_APPS_FABRIC_ARB_11 = + MSM_RPM_8064_ID_APPS_FABRIC_ARB_0 + 11, + + MSM_RPM_8064_ID_SYS_FABRIC_CFG_HALT_0 = 53, + MSM_RPM_8064_ID_SYS_FABRIC_CFG_HALT_1 = 54, + MSM_RPM_8064_ID_SYS_FABRIC_CFG_CLKMOD_0 = 55, + MSM_RPM_8064_ID_SYS_FABRIC_CFG_CLKMOD_1 = 56, + MSM_RPM_8064_ID_SYS_FABRIC_CFG_CLKMOD_2 = 57, + MSM_RPM_8064_ID_SYS_FABRIC_CFG_IOCTL = 58, + MSM_RPM_8064_ID_SYSTEM_FABRIC_ARB_0 = 59, + MSM_RPM_8064_ID_SYSTEM_FABRIC_ARB_29 = + MSM_RPM_8064_ID_SYSTEM_FABRIC_ARB_0 + 29, + + MSM_RPM_8064_ID_MMSS_FABRIC_CFG_HALT_0 = 89, + MSM_RPM_8064_ID_MMSS_FABRIC_CFG_HALT_1 = 90, + MSM_RPM_8064_ID_MMSS_FABRIC_CFG_CLKMOD_0 = 91, + MSM_RPM_8064_ID_MMSS_FABRIC_CFG_CLKMOD_1 = 92, + MSM_RPM_8064_ID_MMSS_FABRIC_CFG_CLKMOD_2 = 93, + MSM_RPM_8064_ID_MMSS_FABRIC_CFG_IOCTL = 94, + MSM_RPM_8064_ID_MM_FABRIC_ARB_0 = 95, + MSM_RPM_8064_ID_MM_FABRIC_ARB_20 = + MSM_RPM_8064_ID_MM_FABRIC_ARB_0 + 20, + + MSM_RPM_8064_ID_PM8921_S1_0 = 116, + MSM_RPM_8064_ID_PM8921_S1_1 = 117, + MSM_RPM_8064_ID_PM8921_S2_0 = 118, + MSM_RPM_8064_ID_PM8921_S2_1 = 119, + MSM_RPM_8064_ID_PM8921_S3_0 = 120, + MSM_RPM_8064_ID_PM8921_S3_1 = 121, + MSM_RPM_8064_ID_PM8921_S4_0 = 122, + MSM_RPM_8064_ID_PM8921_S4_1 = 123, + MSM_RPM_8064_ID_PM8921_S5_0 = 124, + MSM_RPM_8064_ID_PM8921_S5_1 = 125, + MSM_RPM_8064_ID_PM8921_S6_0 = 126, + MSM_RPM_8064_ID_PM8921_S6_1 = 127, + MSM_RPM_8064_ID_PM8921_S7_0 = 128, + MSM_RPM_8064_ID_PM8921_S7_1 = 129, + MSM_RPM_8064_ID_PM8921_S8_0 = 130, + MSM_RPM_8064_ID_PM8921_S8_1 = 131, + MSM_RPM_8064_ID_PM8921_L1_0 = 132, + MSM_RPM_8064_ID_PM8921_L1_1 = 133, + MSM_RPM_8064_ID_PM8921_L2_0 = 134, + MSM_RPM_8064_ID_PM8921_L2_1 = 135, + MSM_RPM_8064_ID_PM8921_L3_0 = 136, + MSM_RPM_8064_ID_PM8921_L3_1 = 137, + MSM_RPM_8064_ID_PM8921_L4_0 = 138, + MSM_RPM_8064_ID_PM8921_L4_1 = 139, + MSM_RPM_8064_ID_PM8921_L5_0 = 140, + MSM_RPM_8064_ID_PM8921_L5_1 = 141, + MSM_RPM_8064_ID_PM8921_L6_0 = 142, + MSM_RPM_8064_ID_PM8921_L6_1 = 143, + MSM_RPM_8064_ID_PM8921_L7_0 = 144, + MSM_RPM_8064_ID_PM8921_L7_1 = 145, + MSM_RPM_8064_ID_PM8921_L8_0 = 146, + MSM_RPM_8064_ID_PM8921_L8_1 = 147, + MSM_RPM_8064_ID_PM8921_L9_0 = 148, + MSM_RPM_8064_ID_PM8921_L9_1 = 149, + MSM_RPM_8064_ID_PM8921_L10_0 = 150, + MSM_RPM_8064_ID_PM8921_L10_1 = 151, + MSM_RPM_8064_ID_PM8921_L11_0 = 152, + MSM_RPM_8064_ID_PM8921_L11_1 = 153, + MSM_RPM_8064_ID_PM8921_L12_0 = 154, + MSM_RPM_8064_ID_PM8921_L12_1 = 155, + MSM_RPM_8064_ID_PM8921_L13_0 = 156, + MSM_RPM_8064_ID_PM8921_L13_1 = 157, + MSM_RPM_8064_ID_PM8921_L14_0 = 158, + MSM_RPM_8064_ID_PM8921_L14_1 = 159, + MSM_RPM_8064_ID_PM8921_L15_0 = 160, + MSM_RPM_8064_ID_PM8921_L15_1 = 161, + MSM_RPM_8064_ID_PM8921_L16_0 = 162, + MSM_RPM_8064_ID_PM8921_L16_1 = 163, + MSM_RPM_8064_ID_PM8921_L17_0 = 164, + MSM_RPM_8064_ID_PM8921_L17_1 = 165, + MSM_RPM_8064_ID_PM8921_L18_0 = 166, + MSM_RPM_8064_ID_PM8921_L18_1 = 167, + MSM_RPM_8064_ID_PM8921_L19_0 = 168, + MSM_RPM_8064_ID_PM8921_L19_1 = 169, + MSM_RPM_8064_ID_PM8921_L20_0 = 170, + MSM_RPM_8064_ID_PM8921_L20_1 = 171, + MSM_RPM_8064_ID_PM8921_L21_0 = 172, + MSM_RPM_8064_ID_PM8921_L21_1 = 173, + MSM_RPM_8064_ID_PM8921_L22_0 = 174, + MSM_RPM_8064_ID_PM8921_L22_1 = 175, + MSM_RPM_8064_ID_PM8921_L23_0 = 176, + MSM_RPM_8064_ID_PM8921_L23_1 = 177, + MSM_RPM_8064_ID_PM8921_L24_0 = 178, + MSM_RPM_8064_ID_PM8921_L24_1 = 179, + MSM_RPM_8064_ID_PM8921_L25_0 = 180, + MSM_RPM_8064_ID_PM8921_L25_1 = 181, + MSM_RPM_8064_ID_PM8921_L26_0 = 182, + MSM_RPM_8064_ID_PM8921_L26_1 = 183, + MSM_RPM_8064_ID_PM8921_L27_0 = 184, + MSM_RPM_8064_ID_PM8921_L27_1 = 185, + MSM_RPM_8064_ID_PM8921_L28_0 = 186, + MSM_RPM_8064_ID_PM8921_L28_1 = 187, + MSM_RPM_8064_ID_PM8921_L29_0 = 188, + MSM_RPM_8064_ID_PM8921_L29_1 = 189, + MSM_RPM_8064_ID_PM8921_CLK1_0 = 190, + MSM_RPM_8064_ID_PM8921_CLK1_1 = 191, + MSM_RPM_8064_ID_PM8921_CLK2_0 = 192, + MSM_RPM_8064_ID_PM8921_CLK2_1 = 193, + MSM_RPM_8064_ID_PM8921_LVS1 = 194, + MSM_RPM_8064_ID_PM8921_LVS2 = 195, + MSM_RPM_8064_ID_PM8921_LVS3 = 196, + MSM_RPM_8064_ID_PM8921_LVS4 = 197, + MSM_RPM_8064_ID_PM8921_LVS5 = 198, + MSM_RPM_8064_ID_PM8921_LVS6 = 199, + MSM_RPM_8064_ID_PM8921_LVS7 = 200, + MSM_RPM_8064_ID_PM8821_S1_0 = 201, + MSM_RPM_8064_ID_PM8821_S1_1 = 202, + MSM_RPM_8064_ID_PM8821_S2_0 = 203, + MSM_RPM_8064_ID_PM8821_S2_1 = 204, + MSM_RPM_8064_ID_PM8821_L1_0 = 205, + MSM_RPM_8064_ID_PM8821_L1_1 = 206, + MSM_RPM_8064_ID_NCP_0 = 207, + MSM_RPM_8064_ID_NCP_1 = 208, + MSM_RPM_8064_ID_CXO_BUFFERS = 209, + MSM_RPM_8064_ID_USB_OTG_SWITCH = 210, + MSM_RPM_8064_ID_HDMI_SWITCH = 211, + MSM_RPM_8064_ID_DDR_DMM_0 = 212, + MSM_RPM_8064_ID_DDR_DMM_1 = 213, + MSM_RPM_8064_ID_QDSS_CLK = 214, + + MSM_RPM_8064_ID_LAST = MSM_RPM_8064_ID_QDSS_CLK, +}; + + +/* RPM status ID enum */ +enum { + MSM_RPM_8064_STATUS_ID_VERSION_MAJOR = 0, + MSM_RPM_8064_STATUS_ID_VERSION_MINOR = 1, + MSM_RPM_8064_STATUS_ID_VERSION_BUILD = 2, + MSM_RPM_8064_STATUS_ID_SUPPORTED_RESOURCES_0 = 3, + MSM_RPM_8064_STATUS_ID_SUPPORTED_RESOURCES_1 = 4, + MSM_RPM_8064_STATUS_ID_SUPPORTED_RESOURCES_2 = 5, + MSM_RPM_8064_STATUS_ID_RESERVED_SUPPORTED_RESOURCES_0 = 6, + MSM_RPM_8064_STATUS_ID_SEQUENCE = 7, + MSM_RPM_8064_STATUS_ID_RPM_CTL = 8, + MSM_RPM_8064_STATUS_ID_CXO_CLK = 9, + MSM_RPM_8064_STATUS_ID_PXO_CLK = 10, + MSM_RPM_8064_STATUS_ID_APPS_FABRIC_CLK = 11, + MSM_RPM_8064_STATUS_ID_SYSTEM_FABRIC_CLK = 12, + MSM_RPM_8064_STATUS_ID_MM_FABRIC_CLK = 13, + MSM_RPM_8064_STATUS_ID_DAYTONA_FABRIC_CLK = 14, + MSM_RPM_8064_STATUS_ID_SFPB_CLK = 15, + MSM_RPM_8064_STATUS_ID_CFPB_CLK = 16, + MSM_RPM_8064_STATUS_ID_MMFPB_CLK = 17, + MSM_RPM_8064_STATUS_ID_EBI1_CLK = 18, + MSM_RPM_8064_STATUS_ID_APPS_FABRIC_CFG_HALT = 19, + MSM_RPM_8064_STATUS_ID_APPS_FABRIC_CFG_CLKMOD = 20, + MSM_RPM_8064_STATUS_ID_APPS_FABRIC_CFG_IOCTL = 21, + MSM_RPM_8064_STATUS_ID_APPS_FABRIC_ARB = 22, + MSM_RPM_8064_STATUS_ID_SYS_FABRIC_CFG_HALT = 23, + MSM_RPM_8064_STATUS_ID_SYS_FABRIC_CFG_CLKMOD = 24, + MSM_RPM_8064_STATUS_ID_SYS_FABRIC_CFG_IOCTL = 25, + MSM_RPM_8064_STATUS_ID_SYSTEM_FABRIC_ARB = 26, + MSM_RPM_8064_STATUS_ID_MMSS_FABRIC_CFG_HALT = 27, + MSM_RPM_8064_STATUS_ID_MMSS_FABRIC_CFG_CLKMOD = 28, + MSM_RPM_8064_STATUS_ID_MMSS_FABRIC_CFG_IOCTL = 29, + MSM_RPM_8064_STATUS_ID_MM_FABRIC_ARB = 30, + MSM_RPM_8064_STATUS_ID_PM8921_S1_0 = 31, + MSM_RPM_8064_STATUS_ID_PM8921_S1_1 = 32, + MSM_RPM_8064_STATUS_ID_PM8921_S2_0 = 33, + MSM_RPM_8064_STATUS_ID_PM8921_S2_1 = 34, + MSM_RPM_8064_STATUS_ID_PM8921_S3_0 = 35, + MSM_RPM_8064_STATUS_ID_PM8921_S3_1 = 36, + MSM_RPM_8064_STATUS_ID_PM8921_S4_0 = 37, + MSM_RPM_8064_STATUS_ID_PM8921_S4_1 = 38, + MSM_RPM_8064_STATUS_ID_PM8921_S5_0 = 39, + MSM_RPM_8064_STATUS_ID_PM8921_S5_1 = 40, + MSM_RPM_8064_STATUS_ID_PM8921_S6_0 = 41, + MSM_RPM_8064_STATUS_ID_PM8921_S6_1 = 42, + MSM_RPM_8064_STATUS_ID_PM8921_S7_0 = 43, + MSM_RPM_8064_STATUS_ID_PM8921_S7_1 = 44, + MSM_RPM_8064_STATUS_ID_PM8921_S8_0 = 45, + MSM_RPM_8064_STATUS_ID_PM8921_S8_1 = 46, + MSM_RPM_8064_STATUS_ID_PM8921_L1_0 = 47, + MSM_RPM_8064_STATUS_ID_PM8921_L1_1 = 48, + MSM_RPM_8064_STATUS_ID_PM8921_L2_0 = 49, + MSM_RPM_8064_STATUS_ID_PM8921_L2_1 = 50, + MSM_RPM_8064_STATUS_ID_PM8921_L3_0 = 51, + MSM_RPM_8064_STATUS_ID_PM8921_L3_1 = 52, + MSM_RPM_8064_STATUS_ID_PM8921_L4_0 = 53, + MSM_RPM_8064_STATUS_ID_PM8921_L4_1 = 54, + MSM_RPM_8064_STATUS_ID_PM8921_L5_0 = 55, + MSM_RPM_8064_STATUS_ID_PM8921_L5_1 = 56, + MSM_RPM_8064_STATUS_ID_PM8921_L6_0 = 57, + MSM_RPM_8064_STATUS_ID_PM8921_L6_1 = 58, + MSM_RPM_8064_STATUS_ID_PM8921_L7_0 = 59, + MSM_RPM_8064_STATUS_ID_PM8921_L7_1 = 60, + MSM_RPM_8064_STATUS_ID_PM8921_L8_0 = 61, + MSM_RPM_8064_STATUS_ID_PM8921_L8_1 = 62, + MSM_RPM_8064_STATUS_ID_PM8921_L9_0 = 63, + MSM_RPM_8064_STATUS_ID_PM8921_L9_1 = 64, + MSM_RPM_8064_STATUS_ID_PM8921_L10_0 = 65, + MSM_RPM_8064_STATUS_ID_PM8921_L10_1 = 66, + MSM_RPM_8064_STATUS_ID_PM8921_L11_0 = 67, + MSM_RPM_8064_STATUS_ID_PM8921_L11_1 = 68, + MSM_RPM_8064_STATUS_ID_PM8921_L12_0 = 69, + MSM_RPM_8064_STATUS_ID_PM8921_L12_1 = 70, + MSM_RPM_8064_STATUS_ID_PM8921_L13_0 = 71, + MSM_RPM_8064_STATUS_ID_PM8921_L13_1 = 72, + MSM_RPM_8064_STATUS_ID_PM8921_L14_0 = 73, + MSM_RPM_8064_STATUS_ID_PM8921_L14_1 = 74, + MSM_RPM_8064_STATUS_ID_PM8921_L15_0 = 75, + MSM_RPM_8064_STATUS_ID_PM8921_L15_1 = 76, + MSM_RPM_8064_STATUS_ID_PM8921_L16_0 = 77, + MSM_RPM_8064_STATUS_ID_PM8921_L16_1 = 78, + MSM_RPM_8064_STATUS_ID_PM8921_L17_0 = 79, + MSM_RPM_8064_STATUS_ID_PM8921_L17_1 = 80, + MSM_RPM_8064_STATUS_ID_PM8921_L18_0 = 81, + MSM_RPM_8064_STATUS_ID_PM8921_L18_1 = 82, + MSM_RPM_8064_STATUS_ID_PM8921_L19_0 = 83, + MSM_RPM_8064_STATUS_ID_PM8921_L19_1 = 84, + MSM_RPM_8064_STATUS_ID_PM8921_L20_0 = 85, + MSM_RPM_8064_STATUS_ID_PM8921_L20_1 = 86, + MSM_RPM_8064_STATUS_ID_PM8921_L21_0 = 87, + MSM_RPM_8064_STATUS_ID_PM8921_L21_1 = 88, + MSM_RPM_8064_STATUS_ID_PM8921_L22_0 = 89, + MSM_RPM_8064_STATUS_ID_PM8921_L22_1 = 90, + MSM_RPM_8064_STATUS_ID_PM8921_L23_0 = 91, + MSM_RPM_8064_STATUS_ID_PM8921_L23_1 = 92, + MSM_RPM_8064_STATUS_ID_PM8921_L24_0 = 93, + MSM_RPM_8064_STATUS_ID_PM8921_L24_1 = 94, + MSM_RPM_8064_STATUS_ID_PM8921_L25_0 = 95, + MSM_RPM_8064_STATUS_ID_PM8921_L25_1 = 96, + MSM_RPM_8064_STATUS_ID_PM8921_L26_0 = 97, + MSM_RPM_8064_STATUS_ID_PM8921_L26_1 = 98, + MSM_RPM_8064_STATUS_ID_PM8921_L27_0 = 99, + MSM_RPM_8064_STATUS_ID_PM8921_L27_1 = 100, + MSM_RPM_8064_STATUS_ID_PM8921_L28_0 = 101, + MSM_RPM_8064_STATUS_ID_PM8921_L28_1 = 102, + MSM_RPM_8064_STATUS_ID_PM8921_L29_0 = 103, + MSM_RPM_8064_STATUS_ID_PM8921_L29_1 = 104, + MSM_RPM_8064_STATUS_ID_PM8921_CLK1_0 = 105, + MSM_RPM_8064_STATUS_ID_PM8921_CLK1_1 = 106, + MSM_RPM_8064_STATUS_ID_PM8921_CLK2_0 = 107, + MSM_RPM_8064_STATUS_ID_PM8921_CLK2_1 = 108, + MSM_RPM_8064_STATUS_ID_PM8921_LVS1 = 109, + MSM_RPM_8064_STATUS_ID_PM8921_LVS2 = 110, + MSM_RPM_8064_STATUS_ID_PM8921_LVS3 = 111, + MSM_RPM_8064_STATUS_ID_PM8921_LVS4 = 112, + MSM_RPM_8064_STATUS_ID_PM8921_LVS5 = 113, + MSM_RPM_8064_STATUS_ID_PM8921_LVS6 = 114, + MSM_RPM_8064_STATUS_ID_PM8921_LVS7 = 115, + MSM_RPM_8064_STATUS_ID_PM8821_S1_0 = 116, + MSM_RPM_8064_STATUS_ID_PM8821_S1_1 = 117, + MSM_RPM_8064_STATUS_ID_PM8821_S2_0 = 118, + MSM_RPM_8064_STATUS_ID_PM8821_S2_1 = 119, + MSM_RPM_8064_STATUS_ID_PM8821_L1_0 = 120, + MSM_RPM_8064_STATUS_ID_PM8821_L1_1 = 121, + MSM_RPM_8064_STATUS_ID_NCP_0 = 122, + MSM_RPM_8064_STATUS_ID_NCP_1 = 123, + MSM_RPM_8064_STATUS_ID_CXO_BUFFERS = 124, + MSM_RPM_8064_STATUS_ID_USB_OTG_SWITCH = 125, + MSM_RPM_8064_STATUS_ID_HDMI_SWITCH = 126, + MSM_RPM_8064_STATUS_ID_DDR_DMM_0 = 127, + MSM_RPM_8064_STATUS_ID_DDR_DMM_1 = 128, + MSM_RPM_8064_STATUS_ID_EBI1_CH0_RANGE = 129, + MSM_RPM_8064_STATUS_ID_EBI1_CH1_RANGE = 130, + + MSM_RPM_8064_STATUS_ID_LAST = MSM_RPM_8064_STATUS_ID_EBI1_CH1_RANGE, +}; + +#endif /* __ARCH_ARM_MACH_MSM_RPM_8064_H */ diff --git a/arch/arm/mach-msm/include/mach/rpm-8660.h b/arch/arm/mach-msm/include/mach/rpm-8660.h new file mode 100644 index 00000000000..5e3b40444cd --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-8660.h @@ -0,0 +1,455 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_8660_H +#define __ARCH_ARM_MACH_MSM_RPM_8660_H + +/* RPM control message RAM enums */ +enum { + MSM_RPM_8660_CTRL_VERSION_MAJOR, + MSM_RPM_8660_CTRL_VERSION_MINOR, + MSM_RPM_8660_CTRL_VERSION_BUILD, + + MSM_RPM_8660_CTRL_REQ_CTX_0, + MSM_RPM_8660_CTRL_REQ_CTX_7 = MSM_RPM_8660_CTRL_REQ_CTX_0 + 7, + MSM_RPM_8660_CTRL_REQ_SEL_0, + MSM_RPM_8660_CTRL_REQ_SEL_7 = MSM_RPM_8660_CTRL_REQ_SEL_0 + 7, + MSM_RPM_8660_CTRL_ACK_CTX_0, + MSM_RPM_8660_CTRL_ACK_CTX_7 = MSM_RPM_8660_CTRL_ACK_CTX_0 + 7, + MSM_RPM_8660_CTRL_ACK_SEL_0, + MSM_RPM_8660_CTRL_ACK_SEL_7 = MSM_RPM_8660_CTRL_ACK_SEL_0 + 7, +}; + +enum { + MSM_RPM_8660_SEL_NOTIFICATION, + MSM_RPM_8660_SEL_INVALIDATE, + MSM_RPM_8660_SEL_TRIGGER_TIMED, + MSM_RPM_8660_SEL_TRIGGER_SET, + MSM_RPM_8660_SEL_TRIGGER_CLEAR, + + MSM_RPM_8660_SEL_CXO_CLK, + MSM_RPM_8660_SEL_PXO_CLK, + MSM_RPM_8660_SEL_PLL_4, + MSM_RPM_8660_SEL_APPS_FABRIC_CLK, + MSM_RPM_8660_SEL_SYSTEM_FABRIC_CLK, + MSM_RPM_8660_SEL_MM_FABRIC_CLK, + MSM_RPM_8660_SEL_DAYTONA_FABRIC_CLK, + MSM_RPM_8660_SEL_SFPB_CLK, + MSM_RPM_8660_SEL_CFPB_CLK, + MSM_RPM_8660_SEL_MMFPB_CLK, + MSM_RPM_8660_SEL_SMI_CLK, + MSM_RPM_8660_SEL_EBI1_CLK, + + MSM_RPM_8660_SEL_APPS_L2_CACHE_CTL, + + MSM_RPM_8660_SEL_APPS_FABRIC_HALT, + MSM_RPM_8660_SEL_APPS_FABRIC_CLOCK_MODE, + MSM_RPM_8660_SEL_APPS_FABRIC_IOCTL, + MSM_RPM_8660_SEL_APPS_FABRIC_ARB, + + MSM_RPM_8660_SEL_SYSTEM_FABRIC_HALT, + MSM_RPM_8660_SEL_SYSTEM_FABRIC_CLOCK_MODE, + MSM_RPM_8660_SEL_SYSTEM_FABRIC_IOCTL, + MSM_RPM_8660_SEL_SYSTEM_FABRIC_ARB, + + MSM_RPM_8660_SEL_MM_FABRIC_HALT, + MSM_RPM_8660_SEL_MM_FABRIC_CLOCK_MODE, + MSM_RPM_8660_SEL_MM_FABRIC_IOCTL, + MSM_RPM_8660_SEL_MM_FABRIC_ARB, + + MSM_RPM_8660_SEL_SMPS0B, + MSM_RPM_8660_SEL_SMPS1B, + MSM_RPM_8660_SEL_SMPS2B, + MSM_RPM_8660_SEL_SMPS3B, + MSM_RPM_8660_SEL_SMPS4B, + MSM_RPM_8660_SEL_LDO0B, + MSM_RPM_8660_SEL_LDO1B, + MSM_RPM_8660_SEL_LDO2B, + MSM_RPM_8660_SEL_LDO3B, + MSM_RPM_8660_SEL_LDO4B, + MSM_RPM_8660_SEL_LDO5B, + MSM_RPM_8660_SEL_LDO6B, + MSM_RPM_8660_SEL_LVS0B, + MSM_RPM_8660_SEL_LVS1B, + MSM_RPM_8660_SEL_LVS2B, + MSM_RPM_8660_SEL_LVS3B, + MSM_RPM_8660_SEL_MVS, + + MSM_RPM_8660_SEL_SMPS0, + MSM_RPM_8660_SEL_SMPS1, + MSM_RPM_8660_SEL_SMPS2, + MSM_RPM_8660_SEL_SMPS3, + MSM_RPM_8660_SEL_SMPS4, + + MSM_RPM_8660_SEL_LDO0, + MSM_RPM_8660_SEL_LDO1, + MSM_RPM_8660_SEL_LDO2, + MSM_RPM_8660_SEL_LDO3, + MSM_RPM_8660_SEL_LDO4, + MSM_RPM_8660_SEL_LDO5, + MSM_RPM_8660_SEL_LDO6, + MSM_RPM_8660_SEL_LDO7, + MSM_RPM_8660_SEL_LDO8, + MSM_RPM_8660_SEL_LDO9, + MSM_RPM_8660_SEL_LDO10, + MSM_RPM_8660_SEL_LDO11, + MSM_RPM_8660_SEL_LDO12, + MSM_RPM_8660_SEL_LDO13, + MSM_RPM_8660_SEL_LDO14, + MSM_RPM_8660_SEL_LDO15, + MSM_RPM_8660_SEL_LDO16, + MSM_RPM_8660_SEL_LDO17, + MSM_RPM_8660_SEL_LDO18, + MSM_RPM_8660_SEL_LDO19, + MSM_RPM_8660_SEL_LDO20, + MSM_RPM_8660_SEL_LDO21, + MSM_RPM_8660_SEL_LDO22, + MSM_RPM_8660_SEL_LDO23, + MSM_RPM_8660_SEL_LDO24, + MSM_RPM_8660_SEL_LDO25, + MSM_RPM_8660_SEL_LVS0, + MSM_RPM_8660_SEL_LVS1, + MSM_RPM_8660_SEL_NCP, + + MSM_RPM_8660_SEL_CXO_BUFFERS, + + MSM_RPM_8660_SEL_LAST = MSM_RPM_8660_SEL_CXO_BUFFERS, +}; + + +enum { + MSM_RPM_8660_ID_NOTIFICATION_CONFIGURED_0, + MSM_RPM_8660_ID_NOTIFICATION_CONFIGURED_7 = + MSM_RPM_8660_ID_NOTIFICATION_CONFIGURED_0 + 7, + + MSM_RPM_8660_ID_NOTIFICATION_REGISTERED_0, + MSM_RPM_8660_ID_NOTIFICATION_REGISTERED_7 = + MSM_RPM_8660_ID_NOTIFICATION_REGISTERED_0 + 7, + + MSM_RPM_8660_ID_INVALIDATE_0, + MSM_RPM_8660_ID_INVALIDATE_7 = + MSM_RPM_8660_ID_INVALIDATE_0 + 7, + + MSM_RPM_8660_ID_TRIGGER_TIMED_TO, + MSM_RPM_8660_ID_TRIGGER_TIMED_SCLK_COUNT, + + MSM_RPM_8660_ID_TRIGGER_SET_FROM, + MSM_RPM_8660_ID_TRIGGER_SET_TO, + MSM_RPM_8660_ID_TRIGGER_SET_TRIGGER, + + MSM_RPM_8660_ID_TRIGGER_CLEAR_FROM, + MSM_RPM_8660_ID_TRIGGER_CLEAR_TO, + MSM_RPM_8660_ID_TRIGGER_CLEAR_TRIGGER, + + MSM_RPM_8660_ID_CXO_CLK, + MSM_RPM_8660_ID_PXO_CLK, + MSM_RPM_8660_ID_PLL_4, + MSM_RPM_8660_ID_APPS_FABRIC_CLK, + MSM_RPM_8660_ID_SYSTEM_FABRIC_CLK, + MSM_RPM_8660_ID_MM_FABRIC_CLK, + MSM_RPM_8660_ID_DAYTONA_FABRIC_CLK, + MSM_RPM_8660_ID_SFPB_CLK, + MSM_RPM_8660_ID_CFPB_CLK, + MSM_RPM_8660_ID_MMFPB_CLK, + MSM_RPM_8660_ID_SMI_CLK, + MSM_RPM_8660_ID_EBI1_CLK, + + MSM_RPM_8660_ID_APPS_L2_CACHE_CTL, + + MSM_RPM_8660_ID_APPS_FABRIC_HALT_0, + MSM_RPM_8660_ID_APPS_FABRIC_HALT_1, + MSM_RPM_8660_ID_APPS_FABRIC_CLOCK_MODE_0, + MSM_RPM_8660_ID_APPS_FABRIC_CLOCK_MODE_1, + MSM_RPM_8660_ID_APPS_FABRIC_CLOCK_MODE_2, + MSM_RPM_8660_ID_APPS_FABRIC_RESERVED_A, + MSM_RPM_8660_ID_APPS_FABRIC_ARB_0, + MSM_RPM_8660_ID_APPS_FABRIC_ARB_5 = + MSM_RPM_8660_ID_APPS_FABRIC_ARB_0 + 5, + MSM_RPM_8660_ID_APPS_FABRIC_RESERVED_B_0, + MSM_RPM_8660_ID_APPS_FABRIC_RESERVED_B_5 = + MSM_RPM_8660_ID_APPS_FABRIC_RESERVED_B_0 + 5, + + MSM_RPM_8660_ID_SYSTEM_FABRIC_HALT_0, + MSM_RPM_8660_ID_SYSTEM_FABRIC_HALT_1, + MSM_RPM_8660_ID_SYSTEM_FABRIC_CLOCK_MODE_0, + MSM_RPM_8660_ID_SYSTEM_FABRIC_CLOCK_MODE_1, + MSM_RPM_8660_ID_SYSTEM_FABRIC_CLOCK_MODE_2, + MSM_RPM_8660_ID_SYSTEM_FABRIC_RESERVED_A, + MSM_RPM_8660_ID_SYSTEM_FABRIC_ARB_0, + MSM_RPM_8660_ID_SYSTEM_FABRIC_ARB_21 = + MSM_RPM_8660_ID_SYSTEM_FABRIC_ARB_0 + 21, + MSM_RPM_8660_ID_SYSTEM_FABRIC_RESERVED_B_0, + MSM_RPM_8660_ID_SYSTEM_FABRIC_RESERVED_B_13 = + MSM_RPM_8660_ID_SYSTEM_FABRIC_RESERVED_B_0 + 13, + + MSM_RPM_8660_ID_MM_FABRIC_HALT_0, + MSM_RPM_8660_ID_MM_FABRIC_HALT_1, + MSM_RPM_8660_ID_MM_FABRIC_CLOCK_MODE_0, + MSM_RPM_8660_ID_MM_FABRIC_CLOCK_MODE_1, + MSM_RPM_8660_ID_MM_FABRIC_CLOCK_MODE_2, + MSM_RPM_8660_ID_MM_FABRIC_RESERVED_A, + MSM_RPM_8660_ID_MM_FABRIC_ARB_0, + MSM_RPM_8660_ID_MM_FABRIC_ARB_22 = + MSM_RPM_8660_ID_MM_FABRIC_ARB_0 + 22, + + /* pmic 8901 */ + MSM_RPM_8660_ID_SMPS0B_0, + MSM_RPM_8660_ID_SMPS0B_1, + MSM_RPM_8660_ID_SMPS1B_0, + MSM_RPM_8660_ID_SMPS1B_1, + MSM_RPM_8660_ID_SMPS2B_0, + MSM_RPM_8660_ID_SMPS2B_1, + MSM_RPM_8660_ID_SMPS3B_0, + MSM_RPM_8660_ID_SMPS3B_1, + MSM_RPM_8660_ID_SMPS4B_0, + MSM_RPM_8660_ID_SMPS4B_1, + MSM_RPM_8660_ID_LDO0B_0, + MSM_RPM_8660_ID_LDO0B_1, + MSM_RPM_8660_ID_LDO1B_0, + MSM_RPM_8660_ID_LDO1B_1, + MSM_RPM_8660_ID_LDO2B_0, + MSM_RPM_8660_ID_LDO2B_1, + MSM_RPM_8660_ID_LDO3B_0, + MSM_RPM_8660_ID_LDO3B_1, + MSM_RPM_8660_ID_LDO4B_0, + MSM_RPM_8660_ID_LDO4B_1, + MSM_RPM_8660_ID_LDO5B_0, + MSM_RPM_8660_ID_LDO5B_1, + MSM_RPM_8660_ID_LDO6B_0, + MSM_RPM_8660_ID_LDO6B_1, + MSM_RPM_8660_ID_LVS0B, + MSM_RPM_8660_ID_LVS1B, + MSM_RPM_8660_ID_LVS2B, + MSM_RPM_8660_ID_LVS3B, + MSM_RPM_8660_ID_MVS, + + /* pmic 8058 */ + MSM_RPM_8660_ID_SMPS0_0, + MSM_RPM_8660_ID_SMPS0_1, + MSM_RPM_8660_ID_SMPS1_0, + MSM_RPM_8660_ID_SMPS1_1, + MSM_RPM_8660_ID_SMPS2_0, + MSM_RPM_8660_ID_SMPS2_1, + MSM_RPM_8660_ID_SMPS3_0, + MSM_RPM_8660_ID_SMPS3_1, + MSM_RPM_8660_ID_SMPS4_0, + MSM_RPM_8660_ID_SMPS4_1, + MSM_RPM_8660_ID_LDO0_0, + MSM_RPM_8660_ID_LDO0_1, + MSM_RPM_8660_ID_LDO1_0, + MSM_RPM_8660_ID_LDO1_1, + MSM_RPM_8660_ID_LDO2_0, + MSM_RPM_8660_ID_LDO2_1, + MSM_RPM_8660_ID_LDO3_0, + MSM_RPM_8660_ID_LDO3_1, + MSM_RPM_8660_ID_LDO4_0, + MSM_RPM_8660_ID_LDO4_1, + MSM_RPM_8660_ID_LDO5_0, + MSM_RPM_8660_ID_LDO5_1, + MSM_RPM_8660_ID_LDO6_0, + MSM_RPM_8660_ID_LDO6_1, + MSM_RPM_8660_ID_LDO7_0, + MSM_RPM_8660_ID_LDO7_1, + MSM_RPM_8660_ID_LDO8_0, + MSM_RPM_8660_ID_LDO8_1, + MSM_RPM_8660_ID_LDO9_0, + MSM_RPM_8660_ID_LDO9_1, + MSM_RPM_8660_ID_LDO10_0, + MSM_RPM_8660_ID_LDO10_1, + MSM_RPM_8660_ID_LDO11_0, + MSM_RPM_8660_ID_LDO11_1, + MSM_RPM_8660_ID_LDO12_0, + MSM_RPM_8660_ID_LDO12_1, + MSM_RPM_8660_ID_LDO13_0, + MSM_RPM_8660_ID_LDO13_1, + MSM_RPM_8660_ID_LDO14_0, + MSM_RPM_8660_ID_LDO14_1, + MSM_RPM_8660_ID_LDO15_0, + MSM_RPM_8660_ID_LDO15_1, + MSM_RPM_8660_ID_LDO16_0, + MSM_RPM_8660_ID_LDO16_1, + MSM_RPM_8660_ID_LDO17_0, + MSM_RPM_8660_ID_LDO17_1, + MSM_RPM_8660_ID_LDO18_0, + MSM_RPM_8660_ID_LDO18_1, + MSM_RPM_8660_ID_LDO19_0, + MSM_RPM_8660_ID_LDO19_1, + MSM_RPM_8660_ID_LDO20_0, + MSM_RPM_8660_ID_LDO20_1, + MSM_RPM_8660_ID_LDO21_0, + MSM_RPM_8660_ID_LDO21_1, + MSM_RPM_8660_ID_LDO22_0, + MSM_RPM_8660_ID_LDO22_1, + MSM_RPM_8660_ID_LDO23_0, + MSM_RPM_8660_ID_LDO23_1, + MSM_RPM_8660_ID_LDO24_0, + MSM_RPM_8660_ID_LDO24_1, + MSM_RPM_8660_ID_LDO25_0, + MSM_RPM_8660_ID_LDO25_1, + MSM_RPM_8660_ID_LVS0, + MSM_RPM_8660_ID_LVS1, + MSM_RPM_8660_ID_NCP_0, + MSM_RPM_8660_ID_NCP_1, + + MSM_RPM_8660_ID_CXO_BUFFERS, + + MSM_RPM_8660_ID_LAST = MSM_RPM_8660_ID_CXO_BUFFERS +}; + +enum { + MSM_RPM_8660_STATUS_ID_VERSION_MAJOR, + MSM_RPM_8660_STATUS_ID_VERSION_MINOR, + MSM_RPM_8660_STATUS_ID_VERSION_BUILD, + MSM_RPM_8660_STATUS_ID_SUPPORTED_RESOURCES_0, + MSM_RPM_8660_STATUS_ID_SUPPORTED_RESOURCES_1, + MSM_RPM_8660_STATUS_ID_SUPPORTED_RESOURCES_2, + MSM_RPM_8660_STATUS_ID_RESERVED_0, + MSM_RPM_8660_STATUS_ID_RESERVED_4 = + MSM_RPM_8660_STATUS_ID_RESERVED_0 + 4, + MSM_RPM_8660_STATUS_ID_SEQUENCE, + + MSM_RPM_8660_STATUS_ID_CXO_CLK, + MSM_RPM_8660_STATUS_ID_PXO_CLK, + MSM_RPM_8660_STATUS_ID_PLL_4, + MSM_RPM_8660_STATUS_ID_APPS_FABRIC_CLK, + MSM_RPM_8660_STATUS_ID_SYSTEM_FABRIC_CLK, + MSM_RPM_8660_STATUS_ID_MM_FABRIC_CLK, + MSM_RPM_8660_STATUS_ID_DAYTONA_FABRIC_CLK, + MSM_RPM_8660_STATUS_ID_SFPB_CLK, + MSM_RPM_8660_STATUS_ID_CFPB_CLK, + MSM_RPM_8660_STATUS_ID_MMFPB_CLK, + MSM_RPM_8660_STATUS_ID_SMI_CLK, + MSM_RPM_8660_STATUS_ID_EBI1_CLK, + + MSM_RPM_8660_STATUS_ID_APPS_L2_CACHE_CTL, + + MSM_RPM_8660_STATUS_ID_APPS_FABRIC_HALT, + MSM_RPM_8660_STATUS_ID_APPS_FABRIC_CLOCK_MODE, + MSM_RPM_8660_STATUS_ID_APPS_FABRIC_RESERVED, + MSM_RPM_8660_STATUS_ID_APPS_FABRIC_ARB, + + MSM_RPM_8660_STATUS_ID_SYSTEM_FABRIC_HALT, + MSM_RPM_8660_STATUS_ID_SYSTEM_FABRIC_CLOCK_MODE, + MSM_RPM_8660_STATUS_ID_SYSTEM_FABRIC_RESERVED, + MSM_RPM_8660_STATUS_ID_SYSTEM_FABRIC_ARB, + + MSM_RPM_8660_STATUS_ID_MM_FABRIC_HALT, + MSM_RPM_8660_STATUS_ID_MM_FABRIC_CLOCK_MODE, + MSM_RPM_8660_STATUS_ID_MM_FABRIC_RESERVED, + MSM_RPM_8660_STATUS_ID_MM_FABRIC_ARB, + + /* pmic 8901 */ + MSM_RPM_8660_STATUS_ID_SMPS0B_0, + MSM_RPM_8660_STATUS_ID_SMPS0B_1, + MSM_RPM_8660_STATUS_ID_SMPS1B_0, + MSM_RPM_8660_STATUS_ID_SMPS1B_1, + MSM_RPM_8660_STATUS_ID_SMPS2B_0, + MSM_RPM_8660_STATUS_ID_SMPS2B_1, + MSM_RPM_8660_STATUS_ID_SMPS3B_0, + MSM_RPM_8660_STATUS_ID_SMPS3B_1, + MSM_RPM_8660_STATUS_ID_SMPS4B_0, + MSM_RPM_8660_STATUS_ID_SMPS4B_1, + MSM_RPM_8660_STATUS_ID_LDO0B_0, + MSM_RPM_8660_STATUS_ID_LDO0B_1, + MSM_RPM_8660_STATUS_ID_LDO1B_0, + MSM_RPM_8660_STATUS_ID_LDO1B_1, + MSM_RPM_8660_STATUS_ID_LDO2B_0, + MSM_RPM_8660_STATUS_ID_LDO2B_1, + MSM_RPM_8660_STATUS_ID_LDO3B_0, + MSM_RPM_8660_STATUS_ID_LDO3B_1, + MSM_RPM_8660_STATUS_ID_LDO4B_0, + MSM_RPM_8660_STATUS_ID_LDO4B_1, + MSM_RPM_8660_STATUS_ID_LDO5B_0, + MSM_RPM_8660_STATUS_ID_LDO5B_1, + MSM_RPM_8660_STATUS_ID_LDO6B_0, + MSM_RPM_8660_STATUS_ID_LDO6B_1, + MSM_RPM_8660_STATUS_ID_LVS0B, + MSM_RPM_8660_STATUS_ID_LVS1B, + MSM_RPM_8660_STATUS_ID_LVS2B, + MSM_RPM_8660_STATUS_ID_LVS3B, + MSM_RPM_8660_STATUS_ID_MVS, + + /* pmic 8058 */ + MSM_RPM_8660_STATUS_ID_SMPS0_0, + MSM_RPM_8660_STATUS_ID_SMPS0_1, + MSM_RPM_8660_STATUS_ID_SMPS1_0, + MSM_RPM_8660_STATUS_ID_SMPS1_1, + MSM_RPM_8660_STATUS_ID_SMPS2_0, + MSM_RPM_8660_STATUS_ID_SMPS2_1, + MSM_RPM_8660_STATUS_ID_SMPS3_0, + MSM_RPM_8660_STATUS_ID_SMPS3_1, + MSM_RPM_8660_STATUS_ID_SMPS4_0, + MSM_RPM_8660_STATUS_ID_SMPS4_1, + MSM_RPM_8660_STATUS_ID_LDO0_0, + MSM_RPM_8660_STATUS_ID_LDO0_1, + MSM_RPM_8660_STATUS_ID_LDO1_0, + MSM_RPM_8660_STATUS_ID_LDO1_1, + MSM_RPM_8660_STATUS_ID_LDO2_0, + MSM_RPM_8660_STATUS_ID_LDO2_1, + MSM_RPM_8660_STATUS_ID_LDO3_0, + MSM_RPM_8660_STATUS_ID_LDO3_1, + MSM_RPM_8660_STATUS_ID_LDO4_0, + MSM_RPM_8660_STATUS_ID_LDO4_1, + MSM_RPM_8660_STATUS_ID_LDO5_0, + MSM_RPM_8660_STATUS_ID_LDO5_1, + MSM_RPM_8660_STATUS_ID_LDO6_0, + MSM_RPM_8660_STATUS_ID_LDO6_1, + MSM_RPM_8660_STATUS_ID_LDO7_0, + MSM_RPM_8660_STATUS_ID_LDO7_1, + MSM_RPM_8660_STATUS_ID_LDO8_0, + MSM_RPM_8660_STATUS_ID_LDO8_1, + MSM_RPM_8660_STATUS_ID_LDO9_0, + MSM_RPM_8660_STATUS_ID_LDO9_1, + MSM_RPM_8660_STATUS_ID_LDO10_0, + MSM_RPM_8660_STATUS_ID_LDO10_1, + MSM_RPM_8660_STATUS_ID_LDO11_0, + MSM_RPM_8660_STATUS_ID_LDO11_1, + MSM_RPM_8660_STATUS_ID_LDO12_0, + MSM_RPM_8660_STATUS_ID_LDO12_1, + MSM_RPM_8660_STATUS_ID_LDO13_0, + MSM_RPM_8660_STATUS_ID_LDO13_1, + MSM_RPM_8660_STATUS_ID_LDO14_0, + MSM_RPM_8660_STATUS_ID_LDO14_1, + MSM_RPM_8660_STATUS_ID_LDO15_0, + MSM_RPM_8660_STATUS_ID_LDO15_1, + MSM_RPM_8660_STATUS_ID_LDO16_0, + MSM_RPM_8660_STATUS_ID_LDO16_1, + MSM_RPM_8660_STATUS_ID_LDO17_0, + MSM_RPM_8660_STATUS_ID_LDO17_1, + MSM_RPM_8660_STATUS_ID_LDO18_0, + MSM_RPM_8660_STATUS_ID_LDO18_1, + MSM_RPM_8660_STATUS_ID_LDO19_0, + MSM_RPM_8660_STATUS_ID_LDO19_1, + MSM_RPM_8660_STATUS_ID_LDO20_0, + MSM_RPM_8660_STATUS_ID_LDO20_1, + MSM_RPM_8660_STATUS_ID_LDO21_0, + MSM_RPM_8660_STATUS_ID_LDO21_1, + MSM_RPM_8660_STATUS_ID_LDO22_0, + MSM_RPM_8660_STATUS_ID_LDO22_1, + MSM_RPM_8660_STATUS_ID_LDO23_0, + MSM_RPM_8660_STATUS_ID_LDO23_1, + MSM_RPM_8660_STATUS_ID_LDO24_0, + MSM_RPM_8660_STATUS_ID_LDO24_1, + MSM_RPM_8660_STATUS_ID_LDO25_0, + MSM_RPM_8660_STATUS_ID_LDO25_1, + MSM_RPM_8660_STATUS_ID_LVS0, + MSM_RPM_8660_STATUS_ID_LVS1, + MSM_RPM_8660_STATUS_ID_NCP_0, + MSM_RPM_8660_STATUS_ID_NCP_1, + + MSM_RPM_8660_STATUS_ID_CXO_BUFFERS, + + MSM_RPM_8660_STATUS_ID_LAST = + MSM_RPM_8660_STATUS_ID_CXO_BUFFERS +}; + +#endif /* __ARCH_ARM_MACH_MSM_RPM_8660_H */ diff --git a/arch/arm/mach-msm/include/mach/rpm-8930.h b/arch/arm/mach-msm/include/mach/rpm-8930.h new file mode 100644 index 00000000000..6fd9cf43dec --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-8930.h @@ -0,0 +1,359 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_8930_H +#define __ARCH_ARM_MACH_MSM_RPM_8930_H + +/* RPM control message RAM enums */ +enum { + MSM_RPM_8930_CTRL_VERSION_MAJOR, + MSM_RPM_8930_CTRL_VERSION_MINOR, + MSM_RPM_8930_CTRL_VERSION_BUILD, + + MSM_RPM_8930_CTRL_REQ_CTX_0, + MSM_RPM_8930_CTRL_REQ_CTX_7 = MSM_RPM_8930_CTRL_REQ_CTX_0 + 7, + MSM_RPM_8930_CTRL_REQ_SEL_0, + MSM_RPM_8930_CTRL_REQ_SEL_3 = MSM_RPM_8930_CTRL_REQ_SEL_0 + 3, + MSM_RPM_8930_CTRL_ACK_CTX_0, + MSM_RPM_8930_CTRL_ACK_CTX_7 = MSM_RPM_8930_CTRL_ACK_CTX_0 + 7, + MSM_RPM_8930_CTRL_ACK_SEL_0, + MSM_RPM_8930_CTRL_ACK_SEL_7 = MSM_RPM_8930_CTRL_ACK_SEL_0 + 7, +}; + +/* RPM resource select enums defined for RPM core + NOT IN SEQUENTIAL ORDER */ +enum { + MSM_RPM_8930_SEL_NOTIFICATION = 0, + MSM_RPM_8930_SEL_INVALIDATE = 1, + MSM_RPM_8930_SEL_TRIGGER_TIMED_0 = 2, + MSM_RPM_8930_SEL_RPM_CTL = 3, + MSM_RPM_8930_SEL_CXO_CLK = 5, + MSM_RPM_8930_SEL_PXO_CLK = 6, + MSM_RPM_8930_SEL_QDSS_CLK = 7, + MSM_RPM_8930_SEL_APPS_FABRIC_CLK = 8, + MSM_RPM_8930_SEL_SYSTEM_FABRIC_CLK = 9, + MSM_RPM_8930_SEL_MM_FABRIC_CLK = 10, + MSM_RPM_8930_SEL_DAYTONA_FABRIC_CLK = 11, + MSM_RPM_8930_SEL_SFPB_CLK = 12, + MSM_RPM_8930_SEL_CFPB_CLK = 13, + MSM_RPM_8930_SEL_MMFPB_CLK = 14, + MSM_RPM_8930_SEL_EBI1_CLK = 16, + MSM_RPM_8930_SEL_APPS_FABRIC_CFG_HALT = 18, + MSM_RPM_8930_SEL_APPS_FABRIC_CFG_CLKMOD = 19, + MSM_RPM_8930_SEL_APPS_FABRIC_CFG_IOCTL = 20, + MSM_RPM_8930_SEL_APPS_FABRIC_ARB = 21, + MSM_RPM_8930_SEL_SYS_FABRIC_CFG_HALT = 22, + MSM_RPM_8930_SEL_SYS_FABRIC_CFG_CLKMOD = 23, + MSM_RPM_8930_SEL_SYS_FABRIC_CFG_IOCTL = 24, + MSM_RPM_8930_SEL_SYSTEM_FABRIC_ARB = 25, + MSM_RPM_8930_SEL_MMSS_FABRIC_CFG_HALT = 26, + MSM_RPM_8930_SEL_MMSS_FABRIC_CFG_CLKMOD = 27, + MSM_RPM_8930_SEL_MMSS_FABRIC_CFG_IOCTL = 28, + MSM_RPM_8930_SEL_MM_FABRIC_ARB = 29, + MSM_RPM_8930_SEL_PM8038_S1 = 30, + MSM_RPM_8930_SEL_PM8038_S2 = 31, + MSM_RPM_8930_SEL_PM8038_S3 = 32, + MSM_RPM_8930_SEL_PM8038_S4 = 33, + MSM_RPM_8930_SEL_PM8038_S5 = 34, + MSM_RPM_8930_SEL_PM8038_S6 = 35, + MSM_RPM_8930_SEL_PM8038_L1 = 36, + MSM_RPM_8930_SEL_PM8038_L2 = 37, + MSM_RPM_8930_SEL_PM8038_L3 = 38, + MSM_RPM_8930_SEL_PM8038_L4 = 39, + MSM_RPM_8930_SEL_PM8038_L5 = 40, + MSM_RPM_8930_SEL_PM8038_L6 = 41, + MSM_RPM_8930_SEL_PM8038_L7 = 42, + MSM_RPM_8930_SEL_PM8038_L8 = 43, + MSM_RPM_8930_SEL_PM8038_L9 = 44, + MSM_RPM_8930_SEL_PM8038_L10 = 45, + MSM_RPM_8930_SEL_PM8038_L11 = 46, + MSM_RPM_8930_SEL_PM8038_L12 = 47, + MSM_RPM_8930_SEL_PM8038_L13 = 48, + MSM_RPM_8930_SEL_PM8038_L14 = 49, + MSM_RPM_8930_SEL_PM8038_L15 = 50, + MSM_RPM_8930_SEL_PM8038_L16 = 51, + MSM_RPM_8930_SEL_PM8038_L17 = 52, + MSM_RPM_8930_SEL_PM8038_L18 = 53, + MSM_RPM_8930_SEL_PM8038_L19 = 54, + MSM_RPM_8930_SEL_PM8038_L20 = 55, + MSM_RPM_8930_SEL_PM8038_L21 = 56, + MSM_RPM_8930_SEL_PM8038_L22 = 57, + MSM_RPM_8930_SEL_PM8038_L23 = 58, + MSM_RPM_8930_SEL_PM8038_L24 = 59, + MSM_RPM_8930_SEL_PM8038_L25 = 60, + MSM_RPM_8930_SEL_PM8038_L26 = 61, + MSM_RPM_8930_SEL_PM8038_L27 = 62, + MSM_RPM_8930_SEL_PM8038_CLK1 = 63, + MSM_RPM_8930_SEL_PM8038_CLK2 = 64, + MSM_RPM_8930_SEL_PM8038_LVS1 = 65, + MSM_RPM_8930_SEL_PM8038_LVS2 = 66, + MSM_RPM_8930_SEL_NCP = 80, + MSM_RPM_8930_SEL_CXO_BUFFERS = 81, + MSM_RPM_8930_SEL_USB_OTG_SWITCH = 82, + MSM_RPM_8930_SEL_HDMI_SWITCH = 83, + MSM_RPM_8930_SEL_DDR_DMM = 84, + MSM_RPM_8930_SEL_VOLTAGE_CORNER = 87, + MSM_RPM_8930_SEL_LAST = MSM_RPM_8930_SEL_VOLTAGE_CORNER, +}; + +/* RPM resource (4 byte) word ID enum */ +enum { + MSM_RPM_8930_ID_NOTIFICATION_CONFIGURED_0 = 0, + MSM_RPM_8930_ID_NOTIFICATION_CONFIGURED_3 = + MSM_RPM_8930_ID_NOTIFICATION_CONFIGURED_0 + 3, + + MSM_RPM_8930_ID_NOTIFICATION_REGISTERED_0 = 4, + MSM_RPM_8930_ID_NOTIFICATION_REGISTERED_3 = + MSM_RPM_8930_ID_NOTIFICATION_REGISTERED_0 + 3, + + MSM_RPM_8930_ID_INVALIDATE_0 = 8, + MSM_RPM_8930_ID_INVALIDATE_7 = + MSM_RPM_8930_ID_INVALIDATE_0 + 7, + + MSM_RPM_8930_ID_TRIGGER_TIMED_TO_ = 16, + MSM_RPM_8930_ID_TRIGGER_TIMED_SCLK_COUNT = 17, + MSM_RPM_8930_ID_RPM_CTL = 18, + MSM_RPM_8930_ID_RESERVED_0 = 19, + MSM_RPM_8930_ID_RESERVED_5 = + MSM_RPM_8930_ID_RESERVED_0 + 5, + MSM_RPM_8930_ID_CXO_CLK = 25, + MSM_RPM_8930_ID_PXO_CLK = 26, + MSM_RPM_8930_ID_APPS_FABRIC_CLK = 27, + MSM_RPM_8930_ID_SYSTEM_FABRIC_CLK = 28, + MSM_RPM_8930_ID_MM_FABRIC_CLK = 29, + MSM_RPM_8930_ID_DAYTONA_FABRIC_CLK = 30, + MSM_RPM_8930_ID_SFPB_CLK = 31, + MSM_RPM_8930_ID_CFPB_CLK = 32, + MSM_RPM_8930_ID_MMFPB_CLK = 33, + MSM_RPM_8930_ID_EBI1_CLK = 34, + MSM_RPM_8930_ID_APPS_FABRIC_CFG_HALT_0 = 35, + MSM_RPM_8930_ID_APPS_FABRIC_CFG_HALT_1 = 36, + MSM_RPM_8930_ID_APPS_FABRIC_CFG_CLKMOD_0 = 37, + MSM_RPM_8930_ID_APPS_FABRIC_CFG_CLKMOD_1 = 38, + MSM_RPM_8930_ID_APPS_FABRIC_CFG_CLKMOD_2 = 39, + MSM_RPM_8930_ID_APPS_FABRIC_CFG_IOCTL = 40, + MSM_RPM_8930_ID_APPS_FABRIC_ARB_0 = 41, + MSM_RPM_8930_ID_APPS_FABRIC_ARB_5 = + MSM_RPM_8930_ID_APPS_FABRIC_ARB_0 + 5, + MSM_RPM_8930_ID_SYS_FABRIC_CFG_HALT_0 = 47, + MSM_RPM_8930_ID_SYS_FABRIC_CFG_HALT_1 = 48, + MSM_RPM_8930_ID_SYS_FABRIC_CFG_CLKMOD_0 = 49, + MSM_RPM_8930_ID_SYS_FABRIC_CFG_CLKMOD_1 = 50, + MSM_RPM_8930_ID_SYS_FABRIC_CFG_CLKMOD_2 = 51, + MSM_RPM_8930_ID_SYS_FABRIC_CFG_IOCTL = 52, + MSM_RPM_8930_ID_SYSTEM_FABRIC_ARB_0 = 53, + MSM_RPM_8930_ID_SYSTEM_FABRIC_ARB_19 = + MSM_RPM_8930_ID_SYSTEM_FABRIC_ARB_0 + 19, + MSM_RPM_8930_ID_MMSS_FABRIC_CFG_HALT_0 = 73, + MSM_RPM_8930_ID_MMSS_FABRIC_CFG_HALT_1 = 74, + MSM_RPM_8930_ID_MMSS_FABRIC_CFG_CLKMOD_0 = 75, + MSM_RPM_8930_ID_MMSS_FABRIC_CFG_CLKMOD_1 = 76, + MSM_RPM_8930_ID_MMSS_FABRIC_CFG_CLKMOD_2 = 77, + MSM_RPM_8930_ID_MMSS_FABRIC_CFG_IOCTL = 78, + MSM_RPM_8930_ID_MM_FABRIC_ARB_0 = 79, + MSM_RPM_8930_ID_MM_FABRIC_ARB_10 = + MSM_RPM_8930_ID_MM_FABRIC_ARB_0 + 10, + + MSM_RPM_8930_ID_PM8038_S1_0 = 90, + MSM_RPM_8930_ID_PM8038_S1_1 = 91, + MSM_RPM_8930_ID_PM8038_S2_0 = 92, + MSM_RPM_8930_ID_PM8038_S2_1 = 93, + MSM_RPM_8930_ID_PM8038_S3_0 = 94, + MSM_RPM_8930_ID_PM8038_S3_1 = 95, + MSM_RPM_8930_ID_PM8038_S4_0 = 96, + MSM_RPM_8930_ID_PM8038_S4_1 = 97, + MSM_RPM_8930_ID_PM8038_S5_0 = 98, + MSM_RPM_8930_ID_PM8038_S5_1 = 99, + MSM_RPM_8930_ID_PM8038_S6_0 = 100, + MSM_RPM_8930_ID_PM8038_S6_1 = 101, + MSM_RPM_8930_ID_PM8038_L1_0 = 102, + MSM_RPM_8930_ID_PM8038_L1_1 = 103, + MSM_RPM_8930_ID_PM8038_L2_0 = 104, + MSM_RPM_8930_ID_PM8038_L2_1 = 105, + MSM_RPM_8930_ID_PM8038_L3_0 = 106, + MSM_RPM_8930_ID_PM8038_L3_1 = 107, + MSM_RPM_8930_ID_PM8038_L4_0 = 108, + MSM_RPM_8930_ID_PM8038_L4_1 = 109, + MSM_RPM_8930_ID_PM8038_L5_0 = 110, + MSM_RPM_8930_ID_PM8038_L5_1 = 111, + MSM_RPM_8930_ID_PM8038_L6_0 = 112, + MSM_RPM_8930_ID_PM8038_L6_1 = 113, + MSM_RPM_8930_ID_PM8038_L7_0 = 114, + MSM_RPM_8930_ID_PM8038_L7_1 = 115, + MSM_RPM_8930_ID_PM8038_L8_0 = 116, + MSM_RPM_8930_ID_PM8038_L8_1 = 117, + MSM_RPM_8930_ID_PM8038_L9_0 = 118, + MSM_RPM_8930_ID_PM8038_L9_1 = 119, + MSM_RPM_8930_ID_PM8038_L10_0 = 120, + MSM_RPM_8930_ID_PM8038_L10_1 = 121, + MSM_RPM_8930_ID_PM8038_L11_0 = 122, + MSM_RPM_8930_ID_PM8038_L11_1 = 123, + MSM_RPM_8930_ID_PM8038_L12_0 = 124, + MSM_RPM_8930_ID_PM8038_L12_1 = 125, + MSM_RPM_8930_ID_PM8038_L13_0 = 126, + MSM_RPM_8930_ID_PM8038_L13_1 = 127, + MSM_RPM_8930_ID_PM8038_L14_0 = 128, + MSM_RPM_8930_ID_PM8038_L14_1 = 129, + MSM_RPM_8930_ID_PM8038_L15_0 = 130, + MSM_RPM_8930_ID_PM8038_L15_1 = 131, + MSM_RPM_8930_ID_PM8038_L16_0 = 132, + MSM_RPM_8930_ID_PM8038_L16_1 = 133, + MSM_RPM_8930_ID_PM8038_L17_0 = 134, + MSM_RPM_8930_ID_PM8038_L17_1 = 135, + MSM_RPM_8930_ID_PM8038_L18_0 = 136, + MSM_RPM_8930_ID_PM8038_L18_1 = 137, + MSM_RPM_8930_ID_PM8038_L19_0 = 138, + MSM_RPM_8930_ID_PM8038_L19_1 = 139, + MSM_RPM_8930_ID_PM8038_L20_0 = 140, + MSM_RPM_8930_ID_PM8038_L20_1 = 141, + MSM_RPM_8930_ID_PM8038_L21_0 = 142, + MSM_RPM_8930_ID_PM8038_L21_1 = 143, + MSM_RPM_8930_ID_PM8038_L22_0 = 144, + MSM_RPM_8930_ID_PM8038_L22_1 = 145, + MSM_RPM_8930_ID_PM8038_L23_0 = 146, + MSM_RPM_8930_ID_PM8038_L23_1 = 147, + MSM_RPM_8930_ID_PM8038_L24_0 = 148, + MSM_RPM_8930_ID_PM8038_L24_1 = 149, + MSM_RPM_8930_ID_PM8038_L25_0 = 150, + MSM_RPM_8930_ID_PM8038_L25_1 = 151, + MSM_RPM_8930_ID_PM8038_L26_0 = 152, + MSM_RPM_8930_ID_PM8038_L26_1 = 153, + MSM_RPM_8930_ID_PM8038_L27_0 = 154, + MSM_RPM_8930_ID_PM8038_L27_1 = 155, + MSM_RPM_8930_ID_PM8038_CLK1_0 = 156, + MSM_RPM_8930_ID_PM8038_CLK1_1 = 157, + MSM_RPM_8930_ID_PM8038_CLK2_0 = 158, + MSM_RPM_8930_ID_PM8038_CLK2_1 = 159, + MSM_RPM_8930_ID_PM8038_LVS1 = 160, + MSM_RPM_8930_ID_PM8038_LVS2 = 161, + MSM_RPM_8930_ID_NCP_0 = 162, + MSM_RPM_8930_ID_NCP_1 = 163, + MSM_RPM_8930_ID_CXO_BUFFERS = 164, + MSM_RPM_8930_ID_USB_OTG_SWITCH = 165, + MSM_RPM_8930_ID_HDMI_SWITCH = 166, + MSM_RPM_8930_ID_QDSS_CLK = 167, + MSM_RPM_8930_ID_VOLTAGE_CORNER = 168, + MSM_RPM_8930_ID_LAST = MSM_RPM_8930_ID_VOLTAGE_CORNER, +}; + +/* RPM status ID enum */ +enum { + MSM_RPM_8930_STATUS_ID_VERSION_MAJOR = 0, + MSM_RPM_8930_STATUS_ID_VERSION_MINOR = 1, + MSM_RPM_8930_STATUS_ID_VERSION_BUILD = 2, + MSM_RPM_8930_STATUS_ID_SUPPORTED_RESOURCES_0 = 3, + MSM_RPM_8930_STATUS_ID_SUPPORTED_RESOURCES_1 = 4, + MSM_RPM_8930_STATUS_ID_SUPPORTED_RESOURCES_2 = 5, + MSM_RPM_8930_STATUS_ID_RESERVED_SUPPORTED_RESOURCES_0 = 6, + MSM_RPM_8930_STATUS_ID_SEQUENCE = 7, + MSM_RPM_8930_STATUS_ID_RPM_CTL = 8, + MSM_RPM_8930_STATUS_ID_CXO_CLK = 9, + MSM_RPM_8930_STATUS_ID_PXO_CLK = 10, + MSM_RPM_8930_STATUS_ID_APPS_FABRIC_CLK = 11, + MSM_RPM_8930_STATUS_ID_SYSTEM_FABRIC_CLK = 12, + MSM_RPM_8930_STATUS_ID_MM_FABRIC_CLK = 13, + MSM_RPM_8930_STATUS_ID_DAYTONA_FABRIC_CLK = 14, + MSM_RPM_8930_STATUS_ID_SFPB_CLK = 15, + MSM_RPM_8930_STATUS_ID_CFPB_CLK = 16, + MSM_RPM_8930_STATUS_ID_MMFPB_CLK = 17, + MSM_RPM_8930_STATUS_ID_EBI1_CLK = 18, + MSM_RPM_8930_STATUS_ID_APPS_FABRIC_CFG_HALT = 19, + MSM_RPM_8930_STATUS_ID_APPS_FABRIC_CFG_CLKMOD = 20, + MSM_RPM_8930_STATUS_ID_APPS_FABRIC_CFG_IOCTL = 21, + MSM_RPM_8930_STATUS_ID_APPS_FABRIC_ARB = 22, + MSM_RPM_8930_STATUS_ID_SYS_FABRIC_CFG_HALT = 23, + MSM_RPM_8930_STATUS_ID_SYS_FABRIC_CFG_CLKMOD = 24, + MSM_RPM_8930_STATUS_ID_SYS_FABRIC_CFG_IOCTL = 25, + MSM_RPM_8930_STATUS_ID_SYSTEM_FABRIC_ARB = 26, + MSM_RPM_8930_STATUS_ID_MMSS_FABRIC_CFG_HALT = 27, + MSM_RPM_8930_STATUS_ID_MMSS_FABRIC_CFG_CLKMOD = 28, + MSM_RPM_8930_STATUS_ID_MMSS_FABRIC_CFG_IOCTL = 29, + MSM_RPM_8930_STATUS_ID_MM_FABRIC_ARB = 30, + MSM_RPM_8930_STATUS_ID_PM8038_S1_0 = 31, + MSM_RPM_8930_STATUS_ID_PM8038_S1_1 = 32, + MSM_RPM_8930_STATUS_ID_PM8038_S2_0 = 33, + MSM_RPM_8930_STATUS_ID_PM8038_S2_1 = 34, + MSM_RPM_8930_STATUS_ID_PM8038_S3_0 = 35, + MSM_RPM_8930_STATUS_ID_PM8038_S3_1 = 36, + MSM_RPM_8930_STATUS_ID_PM8038_S4_0 = 37, + MSM_RPM_8930_STATUS_ID_PM8038_S4_1 = 38, + MSM_RPM_8930_STATUS_ID_PM8038_L1_0 = 43, + MSM_RPM_8930_STATUS_ID_PM8038_L1_1 = 44, + MSM_RPM_8930_STATUS_ID_PM8038_L2_0 = 45, + MSM_RPM_8930_STATUS_ID_PM8038_L2_1 = 46, + MSM_RPM_8930_STATUS_ID_PM8038_L3_0 = 47, + MSM_RPM_8930_STATUS_ID_PM8038_L3_1 = 48, + MSM_RPM_8930_STATUS_ID_PM8038_L4_0 = 49, + MSM_RPM_8930_STATUS_ID_PM8038_L4_1 = 50, + MSM_RPM_8930_STATUS_ID_PM8038_L5_0 = 51, + MSM_RPM_8930_STATUS_ID_PM8038_L5_1 = 52, + MSM_RPM_8930_STATUS_ID_PM8038_L6_0 = 53, + MSM_RPM_8930_STATUS_ID_PM8038_L6_1 = 54, + MSM_RPM_8930_STATUS_ID_PM8038_L7_0 = 55, + MSM_RPM_8930_STATUS_ID_PM8038_L7_1 = 56, + MSM_RPM_8930_STATUS_ID_PM8038_L8_0 = 57, + MSM_RPM_8930_STATUS_ID_PM8038_L8_1 = 58, + MSM_RPM_8930_STATUS_ID_PM8038_L9_0 = 59, + MSM_RPM_8930_STATUS_ID_PM8038_L9_1 = 60, + MSM_RPM_8930_STATUS_ID_PM8038_L10_0 = 61, + MSM_RPM_8930_STATUS_ID_PM8038_L10_1 = 62, + MSM_RPM_8930_STATUS_ID_PM8038_L11_0 = 63, + MSM_RPM_8930_STATUS_ID_PM8038_L11_1 = 64, + MSM_RPM_8930_STATUS_ID_PM8038_L12_0 = 65, + MSM_RPM_8930_STATUS_ID_PM8038_L12_1 = 66, + MSM_RPM_8930_STATUS_ID_PM8038_L13_0 = 67, + MSM_RPM_8930_STATUS_ID_PM8038_L13_1 = 68, + MSM_RPM_8930_STATUS_ID_PM8038_L14_0 = 69, + MSM_RPM_8930_STATUS_ID_PM8038_L14_1 = 70, + MSM_RPM_8930_STATUS_ID_PM8038_L15_0 = 71, + MSM_RPM_8930_STATUS_ID_PM8038_L15_1 = 72, + MSM_RPM_8930_STATUS_ID_PM8038_L16_0 = 73, + MSM_RPM_8930_STATUS_ID_PM8038_L16_1 = 74, + MSM_RPM_8930_STATUS_ID_PM8038_L17_0 = 75, + MSM_RPM_8930_STATUS_ID_PM8038_L17_1 = 76, + MSM_RPM_8930_STATUS_ID_PM8038_L18_0 = 77, + MSM_RPM_8930_STATUS_ID_PM8038_L18_1 = 78, + MSM_RPM_8930_STATUS_ID_PM8038_L19_0 = 79, + MSM_RPM_8930_STATUS_ID_PM8038_L19_1 = 80, + MSM_RPM_8930_STATUS_ID_PM8038_L20_0 = 81, + MSM_RPM_8930_STATUS_ID_PM8038_L20_1 = 82, + MSM_RPM_8930_STATUS_ID_PM8038_L21_0 = 83, + MSM_RPM_8930_STATUS_ID_PM8038_L21_1 = 84, + MSM_RPM_8930_STATUS_ID_PM8038_L22_0 = 85, + MSM_RPM_8930_STATUS_ID_PM8038_L22_1 = 86, + MSM_RPM_8930_STATUS_ID_PM8038_L23_0 = 87, + MSM_RPM_8930_STATUS_ID_PM8038_L23_1 = 88, + MSM_RPM_8930_STATUS_ID_PM8038_L24_0 = 89, + MSM_RPM_8930_STATUS_ID_PM8038_L24_1 = 90, + MSM_RPM_8930_STATUS_ID_PM8038_L25_0 = 91, + MSM_RPM_8930_STATUS_ID_PM8038_L25_1 = 92, + MSM_RPM_8930_STATUS_ID_PM8038_L26_0 = 93, + MSM_RPM_8930_STATUS_ID_PM8038_L26_1 = 94, + MSM_RPM_8930_STATUS_ID_PM8038_L27_0 = 95, + MSM_RPM_8930_STATUS_ID_PM8038_L27_1 = 96, + MSM_RPM_8930_STATUS_ID_PM8038_CLK1_0 = 97, + MSM_RPM_8930_STATUS_ID_PM8038_CLK1_1 = 98, + MSM_RPM_8930_STATUS_ID_PM8038_CLK2_0 = 99, + MSM_RPM_8930_STATUS_ID_PM8038_CLK2_1 = 100, + MSM_RPM_8930_STATUS_ID_PM8038_LVS1 = 101, + MSM_RPM_8930_STATUS_ID_PM8038_LVS2 = 102, + MSM_RPM_8930_STATUS_ID_NCP_0 = 103, + MSM_RPM_8930_STATUS_ID_NCP_1 = 104, + MSM_RPM_8930_STATUS_ID_CXO_BUFFERS = 105, + MSM_RPM_8930_STATUS_ID_USB_OTG_SWITCH = 106, + MSM_RPM_8930_STATUS_ID_HDMI_SWITCH = 107, + MSM_RPM_8930_STATUS_ID_QDSS_CLK = 108, + MSM_RPM_8930_STATUS_ID_VOLTAGE_CORNER = 109, + MSM_RPM_8930_STATUS_ID_LAST = MSM_RPM_8930_STATUS_ID_VOLTAGE_CORNER, +}; + +#endif /* __ARCH_ARM_MACH_MSM_RPM_8930_H */ diff --git a/arch/arm/mach-msm/include/mach/rpm-8960.h b/arch/arm/mach-msm/include/mach/rpm-8960.h new file mode 100644 index 00000000000..6fe8832e4d8 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-8960.h @@ -0,0 +1,416 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_8960_H +#define __ARCH_ARM_MACH_MSM_RPM_8960_H + +/* RPM control message RAM enums */ +enum { + MSM_RPM_8960_CTRL_VERSION_MAJOR, + MSM_RPM_8960_CTRL_VERSION_MINOR, + MSM_RPM_8960_CTRL_VERSION_BUILD, + + MSM_RPM_8960_CTRL_REQ_CTX_0, + MSM_RPM_8960_CTRL_REQ_CTX_7 = MSM_RPM_8960_CTRL_REQ_CTX_0 + 7, + MSM_RPM_8960_CTRL_REQ_SEL_0, + MSM_RPM_8960_CTRL_REQ_SEL_3 = MSM_RPM_8960_CTRL_REQ_SEL_0 + 3, + MSM_RPM_8960_CTRL_ACK_CTX_0, + MSM_RPM_8960_CTRL_ACK_CTX_7 = MSM_RPM_8960_CTRL_ACK_CTX_0 + 7, + MSM_RPM_8960_CTRL_ACK_SEL_0, + MSM_RPM_8960_CTRL_ACK_SEL_7 = MSM_RPM_8960_CTRL_ACK_SEL_0 + 7, +}; + +/* RPM resource select enums defined for RPM core + NOT IN SEQUENTIAL ORDER */ +enum { + MSM_RPM_8960_SEL_NOTIFICATION = 0, + MSM_RPM_8960_SEL_INVALIDATE = 1, + MSM_RPM_8960_SEL_TRIGGER_TIMED = 2, + MSM_RPM_8960_SEL_RPM_CTL = 3, + + MSM_RPM_8960_SEL_CXO_CLK = 5, + MSM_RPM_8960_SEL_PXO_CLK = 6, + MSM_RPM_8960_SEL_QDSS_CLK = 7, + MSM_RPM_8960_SEL_APPS_FABRIC_CLK = 8, + MSM_RPM_8960_SEL_SYSTEM_FABRIC_CLK = 9, + MSM_RPM_8960_SEL_MM_FABRIC_CLK = 10, + MSM_RPM_8960_SEL_DAYTONA_FABRIC_CLK = 11, + MSM_RPM_8960_SEL_SFPB_CLK = 12, + MSM_RPM_8960_SEL_CFPB_CLK = 13, + MSM_RPM_8960_SEL_MMFPB_CLK = 14, + MSM_RPM_8960_SEL_EBI1_CLK = 16, + + MSM_RPM_8960_SEL_APPS_FABRIC_CFG_HALT = 18, + MSM_RPM_8960_SEL_APPS_FABRIC_CFG_CLKMOD = 19, + MSM_RPM_8960_SEL_APPS_FABRIC_CFG_IOCTL = 20, + MSM_RPM_8960_SEL_APPS_FABRIC_ARB = 21, + + MSM_RPM_8960_SEL_SYS_FABRIC_CFG_HALT = 22, + MSM_RPM_8960_SEL_SYS_FABRIC_CFG_CLKMOD = 23, + MSM_RPM_8960_SEL_SYS_FABRIC_CFG_IOCTL = 24, + MSM_RPM_8960_SEL_SYSTEM_FABRIC_ARB = 25, + + MSM_RPM_8960_SEL_MMSS_FABRIC_CFG_HALT = 26, + MSM_RPM_8960_SEL_MMSS_FABRIC_CFG_CLKMOD = 27, + MSM_RPM_8960_SEL_MMSS_FABRIC_CFG_IOCTL = 28, + MSM_RPM_8960_SEL_MM_FABRIC_ARB = 29, + + MSM_RPM_8960_SEL_PM8921_S1 = 30, + MSM_RPM_8960_SEL_PM8921_S2 = 31, + MSM_RPM_8960_SEL_PM8921_S3 = 32, + MSM_RPM_8960_SEL_PM8921_S4 = 33, + MSM_RPM_8960_SEL_PM8921_S5 = 34, + MSM_RPM_8960_SEL_PM8921_S6 = 35, + MSM_RPM_8960_SEL_PM8921_S7 = 36, + MSM_RPM_8960_SEL_PM8921_S8 = 37, + MSM_RPM_8960_SEL_PM8921_L1 = 38, + MSM_RPM_8960_SEL_PM8921_L2 = 39, + MSM_RPM_8960_SEL_PM8921_L3 = 40, + MSM_RPM_8960_SEL_PM8921_L4 = 41, + MSM_RPM_8960_SEL_PM8921_L5 = 42, + MSM_RPM_8960_SEL_PM8921_L6 = 43, + MSM_RPM_8960_SEL_PM8921_L7 = 44, + MSM_RPM_8960_SEL_PM8921_L8 = 45, + MSM_RPM_8960_SEL_PM8921_L9 = 46, + MSM_RPM_8960_SEL_PM8921_L10 = 47, + MSM_RPM_8960_SEL_PM8921_L11 = 48, + MSM_RPM_8960_SEL_PM8921_L12 = 49, + MSM_RPM_8960_SEL_PM8921_L13 = 50, + MSM_RPM_8960_SEL_PM8921_L14 = 51, + MSM_RPM_8960_SEL_PM8921_L15 = 52, + MSM_RPM_8960_SEL_PM8921_L16 = 53, + MSM_RPM_8960_SEL_PM8921_L17 = 54, + MSM_RPM_8960_SEL_PM8921_L18 = 55, + MSM_RPM_8960_SEL_PM8921_L19 = 56, + MSM_RPM_8960_SEL_PM8921_L20 = 57, + MSM_RPM_8960_SEL_PM8921_L21 = 58, + MSM_RPM_8960_SEL_PM8921_L22 = 59, + MSM_RPM_8960_SEL_PM8921_L23 = 60, + MSM_RPM_8960_SEL_PM8921_L24 = 61, + MSM_RPM_8960_SEL_PM8921_L25 = 62, + MSM_RPM_8960_SEL_PM8921_L26 = 63, + MSM_RPM_8960_SEL_PM8921_L27 = 64, + MSM_RPM_8960_SEL_PM8921_L28 = 65, + MSM_RPM_8960_SEL_PM8921_L29 = 66, + MSM_RPM_8960_SEL_PM8921_CLK1 = 67, + MSM_RPM_8960_SEL_PM8921_CLK2 = 68, + MSM_RPM_8960_SEL_PM8921_LVS1 = 69, + MSM_RPM_8960_SEL_PM8921_LVS2 = 70, + MSM_RPM_8960_SEL_PM8921_LVS3 = 71, + MSM_RPM_8960_SEL_PM8921_LVS4 = 72, + MSM_RPM_8960_SEL_PM8921_LVS5 = 73, + MSM_RPM_8960_SEL_PM8921_LVS6 = 74, + MSM_RPM_8960_SEL_PM8921_LVS7 = 75, + + MSM_RPM_8960_SEL_NCP = 80, + MSM_RPM_8960_SEL_CXO_BUFFERS = 81, + MSM_RPM_8960_SEL_USB_OTG_SWITCH = 82, + MSM_RPM_8960_SEL_HDMI_SWITCH = 83, + MSM_RPM_8960_SEL_DDR_DMM = 84, + + MSM_RPM_8960_SEL_LAST = MSM_RPM_8960_SEL_DDR_DMM, +}; + +/* RPM resource (4 byte) word ID enum */ +enum { + MSM_RPM_8960_ID_NOTIFICATION_CONFIGURED_0 = 0, + MSM_RPM_8960_ID_NOTIFICATION_CONFIGURED_3 = + MSM_RPM_8960_ID_NOTIFICATION_CONFIGURED_0 + 3, + + MSM_RPM_8960_ID_NOTIFICATION_REGISTERED_0 = 4, + MSM_RPM_8960_ID_NOTIFICATION_REGISTERED_3 = + MSM_RPM_8960_ID_NOTIFICATION_REGISTERED_0 + 3, + + MSM_RPM_8960_ID_INVALIDATE_0 = 8, + MSM_RPM_8960_ID_INVALIDATE_7 = + MSM_RPM_8960_ID_INVALIDATE_0 + 7, + + MSM_RPM_8960_ID_TRIGGER_TIMED_TO = 16, + MSM_RPM_8960_ID_TRIGGER_TIMED_SCLK_COUNT = 17, + + MSM_RPM_8960_ID_RPM_CTL = 18, + + /* TRIGGER_CLEAR/SET deprecated in these 24 RESERVED bytes */ + MSM_RPM_8960_ID_RESERVED_0 = 19, + MSM_RPM_8960_ID_RESERVED_5 = + MSM_RPM_8960_ID_RESERVED_0 + 5, + + MSM_RPM_8960_ID_CXO_CLK = 25, + MSM_RPM_8960_ID_PXO_CLK = 26, + MSM_RPM_8960_ID_APPS_FABRIC_CLK = 27, + MSM_RPM_8960_ID_SYSTEM_FABRIC_CLK = 28, + MSM_RPM_8960_ID_MM_FABRIC_CLK = 29, + MSM_RPM_8960_ID_DAYTONA_FABRIC_CLK = 30, + MSM_RPM_8960_ID_SFPB_CLK = 31, + MSM_RPM_8960_ID_CFPB_CLK = 32, + MSM_RPM_8960_ID_MMFPB_CLK = 33, + MSM_RPM_8960_ID_EBI1_CLK = 34, + + MSM_RPM_8960_ID_APPS_FABRIC_CFG_HALT_0 = 35, + MSM_RPM_8960_ID_APPS_FABRIC_CFG_HALT_1 = 36, + MSM_RPM_8960_ID_APPS_FABRIC_CFG_CLKMOD_0 = 37, + MSM_RPM_8960_ID_APPS_FABRIC_CFG_CLKMOD_1 = 38, + MSM_RPM_8960_ID_APPS_FABRIC_CFG_CLKMOD_2 = 39, + MSM_RPM_8960_ID_APPS_FABRIC_CFG_IOCTL = 40, + MSM_RPM_8960_ID_APPS_FABRIC_ARB_0 = 41, + MSM_RPM_8960_ID_APPS_FABRIC_ARB_11 = + MSM_RPM_8960_ID_APPS_FABRIC_ARB_0 + 11, + + MSM_RPM_8960_ID_SYS_FABRIC_CFG_HALT_0 = 53, + MSM_RPM_8960_ID_SYS_FABRIC_CFG_HALT_1 = 54, + MSM_RPM_8960_ID_SYS_FABRIC_CFG_CLKMOD_0 = 55, + MSM_RPM_8960_ID_SYS_FABRIC_CFG_CLKMOD_1 = 56, + MSM_RPM_8960_ID_SYS_FABRIC_CFG_CLKMOD_2 = 57, + MSM_RPM_8960_ID_SYS_FABRIC_CFG_IOCTL = 58, + MSM_RPM_8960_ID_SYSTEM_FABRIC_ARB_0 = 59, + MSM_RPM_8960_ID_SYSTEM_FABRIC_ARB_28 = + MSM_RPM_8960_ID_SYSTEM_FABRIC_ARB_0 + 28, + + MSM_RPM_8960_ID_MMSS_FABRIC_CFG_HALT_0 = 88, + MSM_RPM_8960_ID_MMSS_FABRIC_CFG_HALT_1 = 89, + MSM_RPM_8960_ID_MMSS_FABRIC_CFG_CLKMOD_0 = 90, + MSM_RPM_8960_ID_MMSS_FABRIC_CFG_CLKMOD_1 = 91, + MSM_RPM_8960_ID_MMSS_FABRIC_CFG_CLKMOD_2 = 92, + MSM_RPM_8960_ID_MMSS_FABRIC_CFG_IOCTL = 93, + MSM_RPM_8960_ID_MM_FABRIC_ARB_0 = 94, + MSM_RPM_8960_ID_MM_FABRIC_ARB_22 = + MSM_RPM_8960_ID_MM_FABRIC_ARB_0 + 22, + + MSM_RPM_8960_ID_PM8921_S1_0 = 117, + MSM_RPM_8960_ID_PM8921_S1_1 = 118, + MSM_RPM_8960_ID_PM8921_S2_0 = 119, + MSM_RPM_8960_ID_PM8921_S2_1 = 120, + MSM_RPM_8960_ID_PM8921_S3_0 = 121, + MSM_RPM_8960_ID_PM8921_S3_1 = 122, + MSM_RPM_8960_ID_PM8921_S4_0 = 123, + MSM_RPM_8960_ID_PM8921_S4_1 = 124, + MSM_RPM_8960_ID_PM8921_S5_0 = 125, + MSM_RPM_8960_ID_PM8921_S5_1 = 126, + MSM_RPM_8960_ID_PM8921_S6_0 = 127, + MSM_RPM_8960_ID_PM8921_S6_1 = 128, + MSM_RPM_8960_ID_PM8921_S7_0 = 129, + MSM_RPM_8960_ID_PM8921_S7_1 = 130, + MSM_RPM_8960_ID_PM8921_S8_0 = 131, + MSM_RPM_8960_ID_PM8921_S8_1 = 132, + MSM_RPM_8960_ID_PM8921_L1_0 = 133, + MSM_RPM_8960_ID_PM8921_L1_1 = 134, + MSM_RPM_8960_ID_PM8921_L2_0 = 135, + MSM_RPM_8960_ID_PM8921_L2_1 = 136, + MSM_RPM_8960_ID_PM8921_L3_0 = 137, + MSM_RPM_8960_ID_PM8921_L3_1 = 138, + MSM_RPM_8960_ID_PM8921_L4_0 = 139, + MSM_RPM_8960_ID_PM8921_L4_1 = 140, + MSM_RPM_8960_ID_PM8921_L5_0 = 141, + MSM_RPM_8960_ID_PM8921_L5_1 = 142, + MSM_RPM_8960_ID_PM8921_L6_0 = 143, + MSM_RPM_8960_ID_PM8921_L6_1 = 144, + MSM_RPM_8960_ID_PM8921_L7_0 = 145, + MSM_RPM_8960_ID_PM8921_L7_1 = 146, + MSM_RPM_8960_ID_PM8921_L8_0 = 147, + MSM_RPM_8960_ID_PM8921_L8_1 = 148, + MSM_RPM_8960_ID_PM8921_L9_0 = 149, + MSM_RPM_8960_ID_PM8921_L9_1 = 150, + MSM_RPM_8960_ID_PM8921_L10_0 = 151, + MSM_RPM_8960_ID_PM8921_L10_1 = 152, + MSM_RPM_8960_ID_PM8921_L11_0 = 153, + MSM_RPM_8960_ID_PM8921_L11_1 = 154, + MSM_RPM_8960_ID_PM8921_L12_0 = 155, + MSM_RPM_8960_ID_PM8921_L12_1 = 156, + MSM_RPM_8960_ID_PM8921_L13_0 = 157, + MSM_RPM_8960_ID_PM8921_L13_1 = 158, + MSM_RPM_8960_ID_PM8921_L14_0 = 159, + MSM_RPM_8960_ID_PM8921_L14_1 = 160, + MSM_RPM_8960_ID_PM8921_L15_0 = 161, + MSM_RPM_8960_ID_PM8921_L15_1 = 162, + MSM_RPM_8960_ID_PM8921_L16_0 = 163, + MSM_RPM_8960_ID_PM8921_L16_1 = 164, + MSM_RPM_8960_ID_PM8921_L17_0 = 165, + MSM_RPM_8960_ID_PM8921_L17_1 = 166, + MSM_RPM_8960_ID_PM8921_L18_0 = 167, + MSM_RPM_8960_ID_PM8921_L18_1 = 168, + MSM_RPM_8960_ID_PM8921_L19_0 = 169, + MSM_RPM_8960_ID_PM8921_L19_1 = 170, + MSM_RPM_8960_ID_PM8921_L20_0 = 171, + MSM_RPM_8960_ID_PM8921_L20_1 = 172, + MSM_RPM_8960_ID_PM8921_L21_0 = 173, + MSM_RPM_8960_ID_PM8921_L21_1 = 174, + MSM_RPM_8960_ID_PM8921_L22_0 = 175, + MSM_RPM_8960_ID_PM8921_L22_1 = 176, + MSM_RPM_8960_ID_PM8921_L23_0 = 177, + MSM_RPM_8960_ID_PM8921_L23_1 = 178, + MSM_RPM_8960_ID_PM8921_L24_0 = 179, + MSM_RPM_8960_ID_PM8921_L24_1 = 180, + MSM_RPM_8960_ID_PM8921_L25_0 = 181, + MSM_RPM_8960_ID_PM8921_L25_1 = 182, + MSM_RPM_8960_ID_PM8921_L26_0 = 183, + MSM_RPM_8960_ID_PM8921_L26_1 = 184, + MSM_RPM_8960_ID_PM8921_L27_0 = 185, + MSM_RPM_8960_ID_PM8921_L27_1 = 186, + MSM_RPM_8960_ID_PM8921_L28_0 = 187, + MSM_RPM_8960_ID_PM8921_L28_1 = 188, + MSM_RPM_8960_ID_PM8921_L29_0 = 189, + MSM_RPM_8960_ID_PM8921_L29_1 = 190, + MSM_RPM_8960_ID_PM8921_CLK1_0 = 191, + MSM_RPM_8960_ID_PM8921_CLK1_1 = 192, + MSM_RPM_8960_ID_PM8921_CLK2_0 = 193, + MSM_RPM_8960_ID_PM8921_CLK2_1 = 194, + MSM_RPM_8960_ID_PM8921_LVS1 = 195, + MSM_RPM_8960_ID_PM8921_LVS2 = 196, + MSM_RPM_8960_ID_PM8921_LVS3 = 197, + MSM_RPM_8960_ID_PM8921_LVS4 = 198, + MSM_RPM_8960_ID_PM8921_LVS5 = 199, + MSM_RPM_8960_ID_PM8921_LVS6 = 200, + MSM_RPM_8960_ID_PM8921_LVS7 = 201, + MSM_RPM_8960_ID_NCP_0 = 202, + MSM_RPM_8960_ID_NCP_1 = 203, + MSM_RPM_8960_ID_CXO_BUFFERS = 204, + MSM_RPM_8960_ID_USB_OTG_SWITCH = 205, + MSM_RPM_8960_ID_HDMI_SWITCH = 206, + MSM_RPM_8960_ID_DDR_DMM_0 = 207, + MSM_RPM_8960_ID_DDR_DMM_1 = 208, + MSM_RPM_8960_ID_QDSS_CLK = 209, + + MSM_RPM_8960_ID_LAST = MSM_RPM_8960_ID_QDSS_CLK, +}; + +/* RPM status ID enum */ +enum { + MSM_RPM_8960_STATUS_ID_VERSION_MAJOR = 0, + MSM_RPM_8960_STATUS_ID_VERSION_MINOR = 1, + MSM_RPM_8960_STATUS_ID_VERSION_BUILD = 2, + MSM_RPM_8960_STATUS_ID_SUPPORTED_RESOURCES_0 = 3, + MSM_RPM_8960_STATUS_ID_SUPPORTED_RESOURCES_1 = 4, + MSM_RPM_8960_STATUS_ID_SUPPORTED_RESOURCES_2 = 5, + MSM_RPM_8960_STATUS_ID_RESERVED_SUPPORTED_RESOURCES_0 = 6, + MSM_RPM_8960_STATUS_ID_SEQUENCE = 7, + MSM_RPM_8960_STATUS_ID_RPM_CTL = 8, + MSM_RPM_8960_STATUS_ID_CXO_CLK = 9, + MSM_RPM_8960_STATUS_ID_PXO_CLK = 10, + MSM_RPM_8960_STATUS_ID_APPS_FABRIC_CLK = 11, + MSM_RPM_8960_STATUS_ID_SYSTEM_FABRIC_CLK = 12, + MSM_RPM_8960_STATUS_ID_MM_FABRIC_CLK = 13, + MSM_RPM_8960_STATUS_ID_DAYTONA_FABRIC_CLK = 14, + MSM_RPM_8960_STATUS_ID_SFPB_CLK = 15, + MSM_RPM_8960_STATUS_ID_CFPB_CLK = 16, + MSM_RPM_8960_STATUS_ID_MMFPB_CLK = 17, + MSM_RPM_8960_STATUS_ID_EBI1_CLK = 18, + MSM_RPM_8960_STATUS_ID_APPS_FABRIC_CFG_HALT = 19, + MSM_RPM_8960_STATUS_ID_APPS_FABRIC_CFG_CLKMOD = 20, + MSM_RPM_8960_STATUS_ID_APPS_FABRIC_CFG_IOCTL = 21, + MSM_RPM_8960_STATUS_ID_APPS_FABRIC_ARB = 22, + MSM_RPM_8960_STATUS_ID_SYS_FABRIC_CFG_HALT = 23, + MSM_RPM_8960_STATUS_ID_SYS_FABRIC_CFG_CLKMOD = 24, + MSM_RPM_8960_STATUS_ID_SYS_FABRIC_CFG_IOCTL = 25, + MSM_RPM_8960_STATUS_ID_SYSTEM_FABRIC_ARB = 26, + MSM_RPM_8960_STATUS_ID_MMSS_FABRIC_CFG_HALT = 27, + MSM_RPM_8960_STATUS_ID_MMSS_FABRIC_CFG_CLKMOD = 28, + MSM_RPM_8960_STATUS_ID_MMSS_FABRIC_CFG_IOCTL = 29, + MSM_RPM_8960_STATUS_ID_MM_FABRIC_ARB = 30, + MSM_RPM_8960_STATUS_ID_PM8921_S1_0 = 31, + MSM_RPM_8960_STATUS_ID_PM8921_S1_1 = 32, + MSM_RPM_8960_STATUS_ID_PM8921_S2_0 = 33, + MSM_RPM_8960_STATUS_ID_PM8921_S2_1 = 34, + MSM_RPM_8960_STATUS_ID_PM8921_S3_0 = 35, + MSM_RPM_8960_STATUS_ID_PM8921_S3_1 = 36, + MSM_RPM_8960_STATUS_ID_PM8921_S4_0 = 37, + MSM_RPM_8960_STATUS_ID_PM8921_S4_1 = 38, + MSM_RPM_8960_STATUS_ID_PM8921_S5_0 = 39, + MSM_RPM_8960_STATUS_ID_PM8921_S5_1 = 40, + MSM_RPM_8960_STATUS_ID_PM8921_S6_0 = 41, + MSM_RPM_8960_STATUS_ID_PM8921_S6_1 = 42, + MSM_RPM_8960_STATUS_ID_PM8921_S7_0 = 43, + MSM_RPM_8960_STATUS_ID_PM8921_S7_1 = 44, + MSM_RPM_8960_STATUS_ID_PM8921_S8_0 = 45, + MSM_RPM_8960_STATUS_ID_PM8921_S8_1 = 46, + MSM_RPM_8960_STATUS_ID_PM8921_L1_0 = 47, + MSM_RPM_8960_STATUS_ID_PM8921_L1_1 = 48, + MSM_RPM_8960_STATUS_ID_PM8921_L2_0 = 49, + MSM_RPM_8960_STATUS_ID_PM8921_L2_1 = 50, + MSM_RPM_8960_STATUS_ID_PM8921_L3_0 = 51, + MSM_RPM_8960_STATUS_ID_PM8921_L3_1 = 52, + MSM_RPM_8960_STATUS_ID_PM8921_L4_0 = 53, + MSM_RPM_8960_STATUS_ID_PM8921_L4_1 = 54, + MSM_RPM_8960_STATUS_ID_PM8921_L5_0 = 55, + MSM_RPM_8960_STATUS_ID_PM8921_L5_1 = 56, + MSM_RPM_8960_STATUS_ID_PM8921_L6_0 = 57, + MSM_RPM_8960_STATUS_ID_PM8921_L6_1 = 58, + MSM_RPM_8960_STATUS_ID_PM8921_L7_0 = 59, + MSM_RPM_8960_STATUS_ID_PM8921_L7_1 = 60, + MSM_RPM_8960_STATUS_ID_PM8921_L8_0 = 61, + MSM_RPM_8960_STATUS_ID_PM8921_L8_1 = 62, + MSM_RPM_8960_STATUS_ID_PM8921_L9_0 = 63, + MSM_RPM_8960_STATUS_ID_PM8921_L9_1 = 64, + MSM_RPM_8960_STATUS_ID_PM8921_L10_0 = 65, + MSM_RPM_8960_STATUS_ID_PM8921_L10_1 = 66, + MSM_RPM_8960_STATUS_ID_PM8921_L11_0 = 67, + MSM_RPM_8960_STATUS_ID_PM8921_L11_1 = 68, + MSM_RPM_8960_STATUS_ID_PM8921_L12_0 = 69, + MSM_RPM_8960_STATUS_ID_PM8921_L12_1 = 70, + MSM_RPM_8960_STATUS_ID_PM8921_L13_0 = 71, + MSM_RPM_8960_STATUS_ID_PM8921_L13_1 = 72, + MSM_RPM_8960_STATUS_ID_PM8921_L14_0 = 73, + MSM_RPM_8960_STATUS_ID_PM8921_L14_1 = 74, + MSM_RPM_8960_STATUS_ID_PM8921_L15_0 = 75, + MSM_RPM_8960_STATUS_ID_PM8921_L15_1 = 76, + MSM_RPM_8960_STATUS_ID_PM8921_L16_0 = 77, + MSM_RPM_8960_STATUS_ID_PM8921_L16_1 = 78, + MSM_RPM_8960_STATUS_ID_PM8921_L17_0 = 79, + MSM_RPM_8960_STATUS_ID_PM8921_L17_1 = 80, + MSM_RPM_8960_STATUS_ID_PM8921_L18_0 = 81, + MSM_RPM_8960_STATUS_ID_PM8921_L18_1 = 82, + MSM_RPM_8960_STATUS_ID_PM8921_L19_0 = 83, + MSM_RPM_8960_STATUS_ID_PM8921_L19_1 = 84, + MSM_RPM_8960_STATUS_ID_PM8921_L20_0 = 85, + MSM_RPM_8960_STATUS_ID_PM8921_L20_1 = 86, + MSM_RPM_8960_STATUS_ID_PM8921_L21_0 = 87, + MSM_RPM_8960_STATUS_ID_PM8921_L21_1 = 88, + MSM_RPM_8960_STATUS_ID_PM8921_L22_0 = 89, + MSM_RPM_8960_STATUS_ID_PM8921_L22_1 = 90, + MSM_RPM_8960_STATUS_ID_PM8921_L23_0 = 91, + MSM_RPM_8960_STATUS_ID_PM8921_L23_1 = 92, + MSM_RPM_8960_STATUS_ID_PM8921_L24_0 = 93, + MSM_RPM_8960_STATUS_ID_PM8921_L24_1 = 94, + MSM_RPM_8960_STATUS_ID_PM8921_L25_0 = 95, + MSM_RPM_8960_STATUS_ID_PM8921_L25_1 = 96, + MSM_RPM_8960_STATUS_ID_PM8921_L26_0 = 97, + MSM_RPM_8960_STATUS_ID_PM8921_L26_1 = 98, + MSM_RPM_8960_STATUS_ID_PM8921_L27_0 = 99, + MSM_RPM_8960_STATUS_ID_PM8921_L27_1 = 100, + MSM_RPM_8960_STATUS_ID_PM8921_L28_0 = 101, + MSM_RPM_8960_STATUS_ID_PM8921_L28_1 = 102, + MSM_RPM_8960_STATUS_ID_PM8921_L29_0 = 103, + MSM_RPM_8960_STATUS_ID_PM8921_L29_1 = 104, + MSM_RPM_8960_STATUS_ID_PM8921_CLK1_0 = 105, + MSM_RPM_8960_STATUS_ID_PM8921_CLK1_1 = 106, + MSM_RPM_8960_STATUS_ID_PM8921_CLK2_0 = 107, + MSM_RPM_8960_STATUS_ID_PM8921_CLK2_1 = 108, + MSM_RPM_8960_STATUS_ID_PM8921_LVS1 = 109, + MSM_RPM_8960_STATUS_ID_PM8921_LVS2 = 110, + MSM_RPM_8960_STATUS_ID_PM8921_LVS3 = 111, + MSM_RPM_8960_STATUS_ID_PM8921_LVS4 = 112, + MSM_RPM_8960_STATUS_ID_PM8921_LVS5 = 113, + MSM_RPM_8960_STATUS_ID_PM8921_LVS6 = 114, + MSM_RPM_8960_STATUS_ID_PM8921_LVS7 = 115, + MSM_RPM_8960_STATUS_ID_NCP_0 = 116, + MSM_RPM_8960_STATUS_ID_NCP_1 = 117, + MSM_RPM_8960_STATUS_ID_CXO_BUFFERS = 118, + MSM_RPM_8960_STATUS_ID_USB_OTG_SWITCH = 119, + MSM_RPM_8960_STATUS_ID_HDMI_SWITCH = 120, + MSM_RPM_8960_STATUS_ID_DDR_DMM_0 = 121, + MSM_RPM_8960_STATUS_ID_DDR_DMM_1 = 122, + MSM_RPM_8960_STATUS_ID_EBI1_CH0_RANGE = 123, + MSM_RPM_8960_STATUS_ID_EBI1_CH1_RANGE = 124, + + MSM_RPM_8960_STATUS_ID_LAST = MSM_RPM_8960_STATUS_ID_EBI1_CH1_RANGE, +}; + +#endif /* __ARCH_ARM_MACH_MSM_RPM_8960_H */ diff --git a/arch/arm/mach-msm/include/mach/rpm-9615.h b/arch/arm/mach-msm/include/mach/rpm-9615.h new file mode 100644 index 00000000000..6ae7bae5e7c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-9615.h @@ -0,0 +1,238 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_9615_H +#define __ARCH_ARM_MACH_MSM_RPM_9615_H + +/* RPM control message RAM enums */ +enum { + MSM_RPM_9615_CTRL_VERSION_MAJOR, + MSM_RPM_9615_CTRL_VERSION_MINOR, + MSM_RPM_9615_CTRL_VERSION_BUILD, + + MSM_RPM_9615_CTRL_REQ_CTX_0, + MSM_RPM_9615_CTRL_REQ_CTX_7 = MSM_RPM_9615_CTRL_REQ_CTX_0 + 7, + MSM_RPM_9615_CTRL_REQ_SEL_0, + MSM_RPM_9615_CTRL_REQ_SEL_3 = MSM_RPM_9615_CTRL_REQ_SEL_0 + 3, + MSM_RPM_9615_CTRL_ACK_CTX_0, + MSM_RPM_9615_CTRL_ACK_CTX_7 = MSM_RPM_9615_CTRL_ACK_CTX_0 + 7, + MSM_RPM_9615_CTRL_ACK_SEL_0, + MSM_RPM_9615_CTRL_ACK_SEL_7 = MSM_RPM_9615_CTRL_ACK_SEL_0 + 7, +}; + +enum { + MSM_RPM_9615_SEL_NOTIFICATION = 0, + MSM_RPM_9615_SEL_INVALIDATE = 1, + MSM_RPM_9615_SEL_TRIGGER_TIMED = 2, + MSM_RPM_9615_SEL_RPM_CTL = 3, + + MSM_RPM_9615_SEL_CXO_CLK = 5, + MSM_RPM_9615_SEL_SYSTEM_FABRIC_CLK = 9, + MSM_RPM_9615_SEL_DAYTONA_FABRIC_CLK = 11, + MSM_RPM_9615_SEL_SFPB_CLK = 12, + MSM_RPM_9615_SEL_CFPB_CLK = 13, + MSM_RPM_9615_SEL_EBI1_CLK = 16, + + MSM_RPM_9615_SEL_SYS_FABRIC_CFG_HALT = 22, + MSM_RPM_9615_SEL_SYS_FABRIC_CFG_CLKMOD = 23, + MSM_RPM_9615_SEL_SYS_FABRIC_CFG_IOCTL = 24, + MSM_RPM_9615_SEL_SYSTEM_FABRIC_ARB = 25, + + MSM_RPM_9615_SEL_PM8018_S1 = 30, + MSM_RPM_9615_SEL_PM8018_S2 = 31, + MSM_RPM_9615_SEL_PM8018_S3 = 32, + MSM_RPM_9615_SEL_PM8018_S4 = 33, + MSM_RPM_9615_SEL_PM8018_S5 = 34, + MSM_RPM_9615_SEL_PM8018_L1 = 35, + MSM_RPM_9615_SEL_PM8018_L2 = 36, + MSM_RPM_9615_SEL_PM8018_L3 = 37, + MSM_RPM_9615_SEL_PM8018_L4 = 38, + MSM_RPM_9615_SEL_PM8018_L5 = 39, + MSM_RPM_9615_SEL_PM8018_L6 = 40, + MSM_RPM_9615_SEL_PM8018_L7 = 41, + MSM_RPM_9615_SEL_PM8018_L8 = 42, + MSM_RPM_9615_SEL_PM8018_L9 = 43, + MSM_RPM_9615_SEL_PM8018_L10 = 44, + MSM_RPM_9615_SEL_PM8018_L11 = 45, + MSM_RPM_9615_SEL_PM8018_L12 = 46, + MSM_RPM_9615_SEL_PM8018_L13 = 47, + MSM_RPM_9615_SEL_PM8018_L14 = 48, + MSM_RPM_9615_SEL_PM8018_LVS1 = 49, + + MSM_RPM_9615_SEL_NCP = 80, + MSM_RPM_9615_SEL_CXO_BUFFERS = 81, + MSM_RPM_9615_SEL_USB_OTG_SWITCH = 82, + MSM_RPM_9615_SEL_HDMI_SWITCH = 83, + + MSM_RPM_9615_SEL_LAST = MSM_RPM_9615_SEL_HDMI_SWITCH, +}; + +/* RPM resource (4 byte) word ID enum */ +enum { + MSM_RPM_9615_ID_NOTIFICATION_CONFIGURED_0 = 0, + MSM_RPM_9615_ID_NOTIFICATION_CONFIGURED_3 = + MSM_RPM_9615_ID_NOTIFICATION_CONFIGURED_0 + 3, + + MSM_RPM_9615_ID_NOTIFICATION_REGISTERED_0 = 4, + MSM_RPM_9615_ID_NOTIFICATION_REGISTERED_3 = + MSM_RPM_9615_ID_NOTIFICATION_REGISTERED_0 + 3, + + MSM_RPM_9615_ID_INVALIDATE_0 = 8, + MSM_RPM_9615_ID_INVALIDATE_7 = + MSM_RPM_9615_ID_INVALIDATE_0 + 7, + + MSM_RPM_9615_ID_TRIGGER_TIMED_TO = 16, + MSM_RPM_9615_ID_TRIGGER_TIMED_SCLK_COUNT = 17, + + MSM_RPM_9615_ID_RPM_CTL = 18, + + /* TRIGGER_CLEAR/SET deprecated in these 24 RESERVED bytes */ + MSM_RPM_9615_ID_RESERVED_0 = 19, + MSM_RPM_9615_ID_RESERVED_5 = + MSM_RPM_9615_ID_RESERVED_0 + 5, + + MSM_RPM_9615_ID_CXO_CLK = 25, + MSM_RPM_9615_ID_SYSTEM_FABRIC_CLK = 26, + MSM_RPM_9615_ID_DAYTONA_FABRIC_CLK = 27, + MSM_RPM_9615_ID_SFPB_CLK = 28, + MSM_RPM_9615_ID_CFPB_CLK = 29, + MSM_RPM_9615_ID_EBI1_CLK = 30, + + MSM_RPM_9615_ID_SYS_FABRIC_CFG_HALT_0 = 31, + MSM_RPM_9615_ID_SYS_FABRIC_CFG_HALT_1 = 32, + MSM_RPM_9615_ID_SYS_FABRIC_CFG_CLKMOD_0 = 33, + MSM_RPM_9615_ID_SYS_FABRIC_CFG_CLKMOD_1 = 34, + MSM_RPM_9615_ID_SYS_FABRIC_CFG_CLKMOD_2 = 35, + MSM_RPM_9615_ID_SYS_FABRIC_CFG_IOCTL = 36, + MSM_RPM_9615_ID_SYSTEM_FABRIC_ARB_0 = 37, + MSM_RPM_9615_ID_SYSTEM_FABRIC_ARB_26 = + MSM_RPM_9615_ID_SYSTEM_FABRIC_ARB_0 + 26, + + MSM_RPM_9615_ID_PM8018_S1_0 = 64, + MSM_RPM_9615_ID_PM8018_S1_1 = 65, + MSM_RPM_9615_ID_PM8018_S2_0 = 66, + MSM_RPM_9615_ID_PM8018_S2_1 = 67, + MSM_RPM_9615_ID_PM8018_S3_0 = 68, + MSM_RPM_9615_ID_PM8018_S3_1 = 69, + MSM_RPM_9615_ID_PM8018_S4_0 = 70, + MSM_RPM_9615_ID_PM8018_S4_1 = 71, + MSM_RPM_9615_ID_PM8018_S5_0 = 72, + MSM_RPM_9615_ID_PM8018_S5_1 = 73, + MSM_RPM_9615_ID_PM8018_L1_0 = 74, + MSM_RPM_9615_ID_PM8018_L1_1 = 75, + MSM_RPM_9615_ID_PM8018_L2_0 = 76, + MSM_RPM_9615_ID_PM8018_L2_1 = 77, + MSM_RPM_9615_ID_PM8018_L3_0 = 78, + MSM_RPM_9615_ID_PM8018_L3_1 = 79, + MSM_RPM_9615_ID_PM8018_L4_0 = 80, + MSM_RPM_9615_ID_PM8018_L4_1 = 81, + MSM_RPM_9615_ID_PM8018_L5_0 = 82, + MSM_RPM_9615_ID_PM8018_L5_1 = 83, + MSM_RPM_9615_ID_PM8018_L6_0 = 84, + MSM_RPM_9615_ID_PM8018_L6_1 = 85, + MSM_RPM_9615_ID_PM8018_L7_0 = 86, + MSM_RPM_9615_ID_PM8018_L7_1 = 87, + MSM_RPM_9615_ID_PM8018_L8_0 = 88, + MSM_RPM_9615_ID_PM8018_L8_1 = 89, + MSM_RPM_9615_ID_PM8018_L9_0 = 90, + MSM_RPM_9615_ID_PM8018_L9_1 = 91, + MSM_RPM_9615_ID_PM8018_L10_0 = 92, + MSM_RPM_9615_ID_PM8018_L10_1 = 93, + MSM_RPM_9615_ID_PM8018_L11_0 = 94, + MSM_RPM_9615_ID_PM8018_L11_1 = 95, + MSM_RPM_9615_ID_PM8018_L12_0 = 96, + MSM_RPM_9615_ID_PM8018_L12_1 = 97, + MSM_RPM_9615_ID_PM8018_L13_0 = 98, + MSM_RPM_9615_ID_PM8018_L13_1 = 99, + MSM_RPM_9615_ID_PM8018_L14_0 = 100, + MSM_RPM_9615_ID_PM8018_L14_1 = 101, + MSM_RPM_9615_ID_PM8018_LVS1 = 102, + + MSM_RPM_9615_ID_NCP_0 = 103, + MSM_RPM_9615_ID_NCP_1 = 104, + MSM_RPM_9615_ID_CXO_BUFFERS = 105, + MSM_RPM_9615_ID_USB_OTG_SWITCH = 106, + MSM_RPM_9615_ID_HDMI_SWITCH = 107, + + MSM_RPM_9615_ID_LAST = MSM_RPM_9615_ID_HDMI_SWITCH, +}; + +/* RPM status ID enum */ +enum { + MSM_RPM_9615_STATUS_ID_VERSION_MAJOR = 0, + MSM_RPM_9615_STATUS_ID_VERSION_MINOR = 1, + MSM_RPM_9615_STATUS_ID_VERSION_BUILD = 2, + MSM_RPM_9615_STATUS_ID_SUPPORTED_RESOURCES_0 = 3, + MSM_RPM_9615_STATUS_ID_SUPPORTED_RESOURCES_1 = 4, + MSM_RPM_9615_STATUS_ID_SUPPORTED_RESOURCES_2 = 5, + MSM_RPM_9615_STATUS_ID_RESERVED_SUPPORTED_RESOURCES_0 = 6, + MSM_RPM_9615_STATUS_ID_SEQUENCE = 7, + MSM_RPM_9615_STATUS_ID_RPM_CTL = 8, + MSM_RPM_9615_STATUS_ID_CXO_CLK = 9, + MSM_RPM_9615_STATUS_ID_SYSTEM_FABRIC_CLK = 10, + MSM_RPM_9615_STATUS_ID_DAYTONA_FABRIC_CLK = 11, + MSM_RPM_9615_STATUS_ID_SFPB_CLK = 12, + MSM_RPM_9615_STATUS_ID_CFPB_CLK = 13, + MSM_RPM_9615_STATUS_ID_EBI1_CLK = 14, + MSM_RPM_9615_STATUS_ID_SYS_FABRIC_CFG_HALT = 15, + MSM_RPM_9615_STATUS_ID_SYS_FABRIC_CFG_CLKMOD = 16, + MSM_RPM_9615_STATUS_ID_SYS_FABRIC_CFG_IOCTL = 17, + MSM_RPM_9615_STATUS_ID_SYSTEM_FABRIC_ARB = 18, + MSM_RPM_9615_STATUS_ID_PM8018_S1_0 = 19, + MSM_RPM_9615_STATUS_ID_PM8018_S1_1 = 20, + MSM_RPM_9615_STATUS_ID_PM8018_S2_0 = 21, + MSM_RPM_9615_STATUS_ID_PM8018_S2_1 = 22, + MSM_RPM_9615_STATUS_ID_PM8018_S3_0 = 23, + MSM_RPM_9615_STATUS_ID_PM8018_S3_1 = 24, + MSM_RPM_9615_STATUS_ID_PM8018_S4_0 = 25, + MSM_RPM_9615_STATUS_ID_PM8018_S4_1 = 26, + MSM_RPM_9615_STATUS_ID_PM8018_S5_0 = 27, + MSM_RPM_9615_STATUS_ID_PM8018_S5_1 = 28, + MSM_RPM_9615_STATUS_ID_PM8018_L1_0 = 29, + MSM_RPM_9615_STATUS_ID_PM8018_L1_1 = 30, + MSM_RPM_9615_STATUS_ID_PM8018_L2_0 = 31, + MSM_RPM_9615_STATUS_ID_PM8018_L2_1 = 32, + MSM_RPM_9615_STATUS_ID_PM8018_L3_0 = 33, + MSM_RPM_9615_STATUS_ID_PM8018_L3_1 = 34, + MSM_RPM_9615_STATUS_ID_PM8018_L4_0 = 35, + MSM_RPM_9615_STATUS_ID_PM8018_L4_1 = 36, + MSM_RPM_9615_STATUS_ID_PM8018_L5_0 = 37, + MSM_RPM_9615_STATUS_ID_PM8018_L5_1 = 38, + MSM_RPM_9615_STATUS_ID_PM8018_L6_0 = 39, + MSM_RPM_9615_STATUS_ID_PM8018_L6_1 = 40, + MSM_RPM_9615_STATUS_ID_PM8018_L7_0 = 41, + MSM_RPM_9615_STATUS_ID_PM8018_L7_1 = 42, + MSM_RPM_9615_STATUS_ID_PM8018_L8_0 = 43, + MSM_RPM_9615_STATUS_ID_PM8018_L8_1 = 44, + MSM_RPM_9615_STATUS_ID_PM8018_L9_0 = 45, + MSM_RPM_9615_STATUS_ID_PM8018_L9_1 = 46, + MSM_RPM_9615_STATUS_ID_PM8018_L10_0 = 47, + MSM_RPM_9615_STATUS_ID_PM8018_L10_1 = 48, + MSM_RPM_9615_STATUS_ID_PM8018_L11_0 = 49, + MSM_RPM_9615_STATUS_ID_PM8018_L11_1 = 50, + MSM_RPM_9615_STATUS_ID_PM8018_L12_0 = 51, + MSM_RPM_9615_STATUS_ID_PM8018_L12_1 = 52, + MSM_RPM_9615_STATUS_ID_PM8018_L13_0 = 53, + MSM_RPM_9615_STATUS_ID_PM8018_L13_1 = 54, + MSM_RPM_9615_STATUS_ID_PM8018_L14_0 = 55, + MSM_RPM_9615_STATUS_ID_PM8018_L14_1 = 56, + MSM_RPM_9615_STATUS_ID_PM8018_LVS1 = 57, + MSM_RPM_9615_STATUS_ID_NCP_0 = 58, + MSM_RPM_9615_STATUS_ID_NCP_1 = 59, + MSM_RPM_9615_STATUS_ID_CXO_BUFFERS = 60, + MSM_RPM_9615_STATUS_ID_USB_OTG_SWITCH = 61, + MSM_RPM_9615_STATUS_ID_HDMI_SWITCH = 62, + + MSM_RPM_9615_STATUS_ID_LAST = MSM_RPM_9615_STATUS_ID_HDMI_SWITCH, +}; + +#endif /* __ARCH_ARM_MACH_MSM_RPM_9615_H */ diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-8660.h b/arch/arm/mach-msm/include/mach/rpm-regulator-8660.h new file mode 100644 index 00000000000..85dcd897a26 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator-8660.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_8660_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_8660_H + +#define RPM_VREG_PIN_CTRL_PM8058_A0 0x01 +#define RPM_VREG_PIN_CTRL_PM8058_A1 0x02 +#define RPM_VREG_PIN_CTRL_PM8058_D0 0x04 +#define RPM_VREG_PIN_CTRL_PM8058_D1 0x08 + +#define RPM_VREG_PIN_CTRL_PM8901_A0 0x01 +#define RPM_VREG_PIN_CTRL_PM8901_A1 0x02 +#define RPM_VREG_PIN_CTRL_PM8901_D0 0x04 +#define RPM_VREG_PIN_CTRL_PM8901_D1 0x08 + + +/** + * enum rpm_vreg_pin_fn_8660 - RPM regulator pin function choices + * %RPM_VREG_PIN_FN_8660_ENABLE: pin control switches between disable and + * enable + * %RPM_VREG_PIN_FN_8660_MODE: pin control switches between LPM and HPM + * %RPM_VREG_PIN_FN_8660_SLEEP_B: regulator is forced into LPM when + * sleep_b signal is asserted + * %RPM_VREG_PIN_FN_8660_NONE: do not use pin control for the regulator + * and do not allow another master to + * request pin control + * + * The pin function specified in platform data corresponds to the active state + * pin function value. Pin function will be NONE until a consumer requests + * pin control to be enabled. + */ +enum rpm_vreg_pin_fn_8660 { + RPM_VREG_PIN_FN_8660_ENABLE = 0, + RPM_VREG_PIN_FN_8660_MODE, + RPM_VREG_PIN_FN_8660_SLEEP_B, + RPM_VREG_PIN_FN_8660_NONE, +}; + +/** + * enum rpm_vreg_force_mode_8660 - RPM regulator force mode choices + * %RPM_VREG_FORCE_MODE_8660_PIN_CTRL: allow pin control usage + * %RPM_VREG_FORCE_MODE_8660_NONE: do not force any mode + * %RPM_VREG_FORCE_MODE_8660_LPM: force into low power mode + * %RPM_VREG_FORCE_MODE_8660_HPM: force into high power mode + * + * Force mode is used to override aggregation with other masters and to set + * special operating modes. + */ +enum rpm_vreg_force_mode_8660 { + RPM_VREG_FORCE_MODE_8660_PIN_CTRL = 0, + RPM_VREG_FORCE_MODE_8660_NONE = 0, + RPM_VREG_FORCE_MODE_8660_LPM, + RPM_VREG_FORCE_MODE_8660_HPM, +}; + +enum rpm_vreg_id_8660 { + RPM_VREG_ID_PM8058_L0, + RPM_VREG_ID_PM8058_L1, + RPM_VREG_ID_PM8058_L2, + RPM_VREG_ID_PM8058_L3, + RPM_VREG_ID_PM8058_L4, + RPM_VREG_ID_PM8058_L5, + RPM_VREG_ID_PM8058_L6, + RPM_VREG_ID_PM8058_L7, + RPM_VREG_ID_PM8058_L8, + RPM_VREG_ID_PM8058_L9, + RPM_VREG_ID_PM8058_L10, + RPM_VREG_ID_PM8058_L11, + RPM_VREG_ID_PM8058_L12, + RPM_VREG_ID_PM8058_L13, + RPM_VREG_ID_PM8058_L14, + RPM_VREG_ID_PM8058_L15, + RPM_VREG_ID_PM8058_L16, + RPM_VREG_ID_PM8058_L17, + RPM_VREG_ID_PM8058_L18, + RPM_VREG_ID_PM8058_L19, + RPM_VREG_ID_PM8058_L20, + RPM_VREG_ID_PM8058_L21, + RPM_VREG_ID_PM8058_L22, + RPM_VREG_ID_PM8058_L23, + RPM_VREG_ID_PM8058_L24, + RPM_VREG_ID_PM8058_L25, + RPM_VREG_ID_PM8058_S0, + RPM_VREG_ID_PM8058_S1, + RPM_VREG_ID_PM8058_S2, + RPM_VREG_ID_PM8058_S3, + RPM_VREG_ID_PM8058_S4, + RPM_VREG_ID_PM8058_LVS0, + RPM_VREG_ID_PM8058_LVS1, + RPM_VREG_ID_PM8058_NCP, + RPM_VREG_ID_PM8901_L0, + RPM_VREG_ID_PM8901_L1, + RPM_VREG_ID_PM8901_L2, + RPM_VREG_ID_PM8901_L3, + RPM_VREG_ID_PM8901_L4, + RPM_VREG_ID_PM8901_L5, + RPM_VREG_ID_PM8901_L6, + RPM_VREG_ID_PM8901_S0, + RPM_VREG_ID_PM8901_S1, + RPM_VREG_ID_PM8901_S2, + RPM_VREG_ID_PM8901_S3, + RPM_VREG_ID_PM8901_S4, + RPM_VREG_ID_PM8901_LVS0, + RPM_VREG_ID_PM8901_LVS1, + RPM_VREG_ID_PM8901_LVS2, + RPM_VREG_ID_PM8901_LVS3, + RPM_VREG_ID_PM8901_MVS0, + RPM_VREG_ID_8660_MAX_REAL = RPM_VREG_ID_PM8901_MVS0, + + /* The following are IDs for regulator devices to enable pin control. */ + RPM_VREG_ID_PM8058_L0_PC, + RPM_VREG_ID_PM8058_L1_PC, + RPM_VREG_ID_PM8058_L2_PC, + RPM_VREG_ID_PM8058_L3_PC, + RPM_VREG_ID_PM8058_L4_PC, + RPM_VREG_ID_PM8058_L5_PC, + RPM_VREG_ID_PM8058_L6_PC, + RPM_VREG_ID_PM8058_L7_PC, + RPM_VREG_ID_PM8058_L8_PC, + RPM_VREG_ID_PM8058_L9_PC, + RPM_VREG_ID_PM8058_L10_PC, + RPM_VREG_ID_PM8058_L11_PC, + RPM_VREG_ID_PM8058_L12_PC, + RPM_VREG_ID_PM8058_L13_PC, + RPM_VREG_ID_PM8058_L14_PC, + RPM_VREG_ID_PM8058_L15_PC, + RPM_VREG_ID_PM8058_L16_PC, + RPM_VREG_ID_PM8058_L17_PC, + RPM_VREG_ID_PM8058_L18_PC, + RPM_VREG_ID_PM8058_L19_PC, + RPM_VREG_ID_PM8058_L20_PC, + RPM_VREG_ID_PM8058_L21_PC, + RPM_VREG_ID_PM8058_L22_PC, + RPM_VREG_ID_PM8058_L23_PC, + RPM_VREG_ID_PM8058_L24_PC, + RPM_VREG_ID_PM8058_L25_PC, + RPM_VREG_ID_PM8058_S0_PC, + RPM_VREG_ID_PM8058_S1_PC, + RPM_VREG_ID_PM8058_S2_PC, + RPM_VREG_ID_PM8058_S3_PC, + RPM_VREG_ID_PM8058_S4_PC, + RPM_VREG_ID_PM8058_LVS0_PC, + RPM_VREG_ID_PM8058_LVS1_PC, + + RPM_VREG_ID_PM8901_L0_PC, + RPM_VREG_ID_PM8901_L1_PC, + RPM_VREG_ID_PM8901_L2_PC, + RPM_VREG_ID_PM8901_L3_PC, + RPM_VREG_ID_PM8901_L4_PC, + RPM_VREG_ID_PM8901_L5_PC, + RPM_VREG_ID_PM8901_L6_PC, + RPM_VREG_ID_PM8901_S0_PC, + RPM_VREG_ID_PM8901_S1_PC, + RPM_VREG_ID_PM8901_S2_PC, + RPM_VREG_ID_PM8901_S3_PC, + RPM_VREG_ID_PM8901_S4_PC, + RPM_VREG_ID_PM8901_LVS0_PC, + RPM_VREG_ID_PM8901_LVS1_PC, + RPM_VREG_ID_PM8901_LVS2_PC, + RPM_VREG_ID_PM8901_LVS3_PC, + RPM_VREG_ID_PM8901_MVS0_PC, + RPM_VREG_ID_8660_MAX = RPM_VREG_ID_PM8901_MVS0_PC, +}; + +/* Minimum high power mode loads in uA. */ +#define RPM_VREG_8660_LDO_50_HPM_MIN_LOAD 5000 +#define RPM_VREG_8660_LDO_150_HPM_MIN_LOAD 10000 +#define RPM_VREG_8660_LDO_300_HPM_MIN_LOAD 10000 +#define RPM_VREG_8660_SMPS_HPM_MIN_LOAD 50000 +#define RPM_VREG_8660_FTSMPS_HPM_MIN_LOAD 100000 + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-8930.h b/arch/arm/mach-msm/include/mach/rpm-regulator-8930.h new file mode 100644 index 00000000000..684f9d3b135 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator-8930.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_8930_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_8930_H + +/* Pin control input signals. */ +#define RPM_VREG_PIN_CTRL_PM8038_D1 0x01 +#define RPM_VREG_PIN_CTRL_PM8038_A0 0x02 +#define RPM_VREG_PIN_CTRL_PM8038_A1 0x04 +#define RPM_VREG_PIN_CTRL_PM8038_A2 0x08 + +/** + * enum rpm_vreg_pin_fn_8930 - RPM regulator pin function choices + * %RPM_VREG_PIN_FN_8930_DONT_CARE: do not care about pin control state of + * the regulator; allow another master + * processor to specify pin control + * %RPM_VREG_PIN_FN_8930_ENABLE: pin control switches between disable and + * enable + * %RPM_VREG_PIN_FN_8930_MODE: pin control switches between LPM and HPM + * %RPM_VREG_PIN_FN_8930_SLEEP_B: regulator is forced into LPM when + * sleep_b signal is asserted + * %RPM_VREG_PIN_FN_8930_NONE: do not use pin control for the regulator + * and do not allow another master to + * request pin control + * + * The pin function specified in platform data corresponds to the active state + * pin function value. Pin function will be NONE until a consumer requests + * pin control to be enabled. + */ +enum rpm_vreg_pin_fn_8930 { + RPM_VREG_PIN_FN_8930_DONT_CARE, + RPM_VREG_PIN_FN_8930_ENABLE, + RPM_VREG_PIN_FN_8930_MODE, + RPM_VREG_PIN_FN_8930_SLEEP_B, + RPM_VREG_PIN_FN_8930_NONE, +}; + +/** + * enum rpm_vreg_force_mode_8930 - RPM regulator force mode choices + * %RPM_VREG_FORCE_MODE_8930_PIN_CTRL: allow pin control usage + * %RPM_VREG_FORCE_MODE_8930_NONE: do not force any mode + * %RPM_VREG_FORCE_MODE_8930_LPM: force into low power mode + * %RPM_VREG_FORCE_MODE_8930_AUTO: allow regulator to automatically select + * its own mode based on realtime current + * draw (only available for SMPS + * regulators) + * %RPM_VREG_FORCE_MODE_8930_HPM: force into high power mode + * %RPM_VREG_FORCE_MODE_8930_BYPASS: set regulator to use bypass mode, i.e. + * to act as a switch and not regulate + * (only available for LDO regulators) + * + * Force mode is used to override aggregation with other masters and to set + * special operating modes. + */ +enum rpm_vreg_force_mode_8930 { + RPM_VREG_FORCE_MODE_8930_PIN_CTRL = 0, + RPM_VREG_FORCE_MODE_8930_NONE = 0, + RPM_VREG_FORCE_MODE_8930_LPM, + RPM_VREG_FORCE_MODE_8930_AUTO, /* SMPS only */ + RPM_VREG_FORCE_MODE_8930_HPM, + RPM_VREG_FORCE_MODE_8930_BYPASS, /* LDO only */ +}; + +/** + * enum rpm_vreg_power_mode_8930 - power mode for SMPS regulators + * %RPM_VREG_POWER_MODE_8930_HYSTERETIC: Use hysteretic mode for HPM and when + * usage goes high in AUTO + * %RPM_VREG_POWER_MODE_8930_PWM: Use PWM mode for HPM and when usage + * goes high in AUTO + */ +enum rpm_vreg_power_mode_8930 { + RPM_VREG_POWER_MODE_8930_HYSTERETIC, + RPM_VREG_POWER_MODE_8930_PWM, +}; + +/** + * enum rpm_vreg_id - RPM regulator ID numbers (both real and pin control) + */ +enum rpm_vreg_id_8930 { + RPM_VREG_ID_PM8038_L1, + RPM_VREG_ID_PM8038_L2, + RPM_VREG_ID_PM8038_L3, + RPM_VREG_ID_PM8038_L4, + RPM_VREG_ID_PM8038_L5, + RPM_VREG_ID_PM8038_L6, + RPM_VREG_ID_PM8038_L7, + RPM_VREG_ID_PM8038_L8, + RPM_VREG_ID_PM8038_L9, + RPM_VREG_ID_PM8038_L10, + RPM_VREG_ID_PM8038_L11, + RPM_VREG_ID_PM8038_L12, + RPM_VREG_ID_PM8038_L14, + RPM_VREG_ID_PM8038_L15, + RPM_VREG_ID_PM8038_L16, + RPM_VREG_ID_PM8038_L17, + RPM_VREG_ID_PM8038_L18, + RPM_VREG_ID_PM8038_L19, + RPM_VREG_ID_PM8038_L20, + RPM_VREG_ID_PM8038_L21, + RPM_VREG_ID_PM8038_L22, + RPM_VREG_ID_PM8038_L23, + RPM_VREG_ID_PM8038_L24, + RPM_VREG_ID_PM8038_L26, + RPM_VREG_ID_PM8038_L27, + RPM_VREG_ID_PM8038_S1, + RPM_VREG_ID_PM8038_S2, + RPM_VREG_ID_PM8038_S3, + RPM_VREG_ID_PM8038_S4, + RPM_VREG_ID_PM8038_S5, + RPM_VREG_ID_PM8038_S6, + RPM_VREG_ID_PM8038_LVS1, + RPM_VREG_ID_PM8038_LVS2, + RPM_VREG_ID_PM8038_VDD_DIG_CORNER, + RPM_VREG_ID_PM8038_MAX_REAL = RPM_VREG_ID_PM8038_VDD_DIG_CORNER, + + /* The following are IDs for regulator devices to enable pin control. */ + RPM_VREG_ID_PM8038_L2_PC, + RPM_VREG_ID_PM8038_L3_PC, + RPM_VREG_ID_PM8038_L4_PC, + RPM_VREG_ID_PM8038_L5_PC, + RPM_VREG_ID_PM8038_L6_PC, + RPM_VREG_ID_PM8038_L7_PC, + RPM_VREG_ID_PM8038_L8_PC, + RPM_VREG_ID_PM8038_L9_PC, + RPM_VREG_ID_PM8038_L10_PC, + RPM_VREG_ID_PM8038_L11_PC, + RPM_VREG_ID_PM8038_L12_PC, + RPM_VREG_ID_PM8038_L14_PC, + RPM_VREG_ID_PM8038_L15_PC, + RPM_VREG_ID_PM8038_L17_PC, + RPM_VREG_ID_PM8038_L18_PC, + RPM_VREG_ID_PM8038_L21_PC, + RPM_VREG_ID_PM8038_L22_PC, + RPM_VREG_ID_PM8038_L23_PC, + RPM_VREG_ID_PM8038_L26_PC, + RPM_VREG_ID_PM8038_S1_PC, + RPM_VREG_ID_PM8038_S2_PC, + RPM_VREG_ID_PM8038_S3_PC, + RPM_VREG_ID_PM8038_S4_PC, + RPM_VREG_ID_PM8038_LVS1_PC, + RPM_VREG_ID_PM8038_LVS2_PC, + RPM_VREG_ID_PM8038_MAX = RPM_VREG_ID_PM8038_LVS2_PC, +}; + +/* Minimum high power mode loads in uA. */ +#define RPM_VREG_8930_LDO_50_HPM_MIN_LOAD 5000 +#define RPM_VREG_8930_LDO_150_HPM_MIN_LOAD 10000 +#define RPM_VREG_8930_LDO_300_HPM_MIN_LOAD 10000 +#define RPM_VREG_8930_LDO_600_HPM_MIN_LOAD 10000 +#define RPM_VREG_8930_LDO_1200_HPM_MIN_LOAD 10000 +#define RPM_VREG_8930_SMPS_1500_HPM_MIN_LOAD 100000 +#define RPM_VREG_8930_SMPS_2000_HPM_MIN_LOAD 100000 + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-8960.h b/arch/arm/mach-msm/include/mach/rpm-regulator-8960.h new file mode 100644 index 00000000000..6de47bd2fe4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator-8960.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_8960_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_8960_H + +/* Pin control input signals. */ +#define RPM_VREG_PIN_CTRL_PM8921_D1 0x01 +#define RPM_VREG_PIN_CTRL_PM8921_A0 0x02 +#define RPM_VREG_PIN_CTRL_PM8921_A1 0x04 +#define RPM_VREG_PIN_CTRL_PM8921_A2 0x08 + +/** + * enum rpm_vreg_pin_fn_8960 - RPM regulator pin function choices + * %RPM_VREG_PIN_FN_8960_DONT_CARE: do not care about pin control state of + * the regulator; allow another master + * processor to specify pin control + * %RPM_VREG_PIN_FN_8960_ENABLE: pin control switches between disable and + * enable + * %RPM_VREG_PIN_FN_8960_MODE: pin control switches between LPM and HPM + * %RPM_VREG_PIN_FN_8960_SLEEP_B: regulator is forced into LPM when + * sleep_b signal is asserted + * %RPM_VREG_PIN_FN_8960_NONE: do not use pin control for the regulator + * and do not allow another master to + * request pin control + * + * The pin function specified in platform data corresponds to the active state + * pin function value. Pin function will be NONE until a consumer requests + * pin control to be enabled. + */ +enum rpm_vreg_pin_fn_8960 { + RPM_VREG_PIN_FN_8960_DONT_CARE, + RPM_VREG_PIN_FN_8960_ENABLE, + RPM_VREG_PIN_FN_8960_MODE, + RPM_VREG_PIN_FN_8960_SLEEP_B, + RPM_VREG_PIN_FN_8960_NONE, +}; + +/** + * enum rpm_vreg_force_mode_8960 - RPM regulator force mode choices + * %RPM_VREG_FORCE_MODE_8960_PIN_CTRL: allow pin control usage + * %RPM_VREG_FORCE_MODE_8960_NONE: do not force any mode + * %RPM_VREG_FORCE_MODE_8960_LPM: force into low power mode + * %RPM_VREG_FORCE_MODE_8960_AUTO: allow regulator to automatically select + * its own mode based on realtime current + * draw (only available for SMPS + * regulators) + * %RPM_VREG_FORCE_MODE_8960_HPM: force into high power mode + * %RPM_VREG_FORCE_MODE_8960_BYPASS: set regulator to use bypass mode, i.e. + * to act as a switch and not regulate + * (only available for LDO regulators) + * + * Force mode is used to override aggregation with other masters and to set + * special operating modes. + */ +enum rpm_vreg_force_mode_8960 { + RPM_VREG_FORCE_MODE_8960_PIN_CTRL = 0, + RPM_VREG_FORCE_MODE_8960_NONE = 0, + RPM_VREG_FORCE_MODE_8960_LPM, + RPM_VREG_FORCE_MODE_8960_AUTO, /* SMPS only */ + RPM_VREG_FORCE_MODE_8960_HPM, + RPM_VREG_FORCE_MODE_8960_BYPASS, /* LDO only */ +}; + +/** + * enum rpm_vreg_power_mode_8960 - power mode for SMPS regulators + * %RPM_VREG_POWER_MODE_8960_HYSTERETIC: Use hysteretic mode for HPM and when + * usage goes high in AUTO + * %RPM_VREG_POWER_MODE_8960_PWM: Use PWM mode for HPM and when usage + * goes high in AUTO + */ +enum rpm_vreg_power_mode_8960 { + RPM_VREG_POWER_MODE_8960_HYSTERETIC, + RPM_VREG_POWER_MODE_8960_PWM, +}; + +/** + * enum rpm_vreg_id - RPM regulator ID numbers (both real and pin control) + */ +enum rpm_vreg_id_8960 { + RPM_VREG_ID_PM8921_L1, + RPM_VREG_ID_PM8921_L2, + RPM_VREG_ID_PM8921_L3, + RPM_VREG_ID_PM8921_L4, + RPM_VREG_ID_PM8921_L5, + RPM_VREG_ID_PM8921_L6, + RPM_VREG_ID_PM8921_L7, + RPM_VREG_ID_PM8921_L8, + RPM_VREG_ID_PM8921_L9, + RPM_VREG_ID_PM8921_L10, + RPM_VREG_ID_PM8921_L11, + RPM_VREG_ID_PM8921_L12, + RPM_VREG_ID_PM8921_L14, + RPM_VREG_ID_PM8921_L15, + RPM_VREG_ID_PM8921_L16, + RPM_VREG_ID_PM8921_L17, + RPM_VREG_ID_PM8921_L18, + RPM_VREG_ID_PM8921_L21, + RPM_VREG_ID_PM8921_L22, + RPM_VREG_ID_PM8921_L23, + RPM_VREG_ID_PM8921_L24, + RPM_VREG_ID_PM8921_L25, + RPM_VREG_ID_PM8921_L26, + RPM_VREG_ID_PM8921_L27, + RPM_VREG_ID_PM8921_L28, + RPM_VREG_ID_PM8921_L29, + RPM_VREG_ID_PM8921_S1, + RPM_VREG_ID_PM8921_S2, + RPM_VREG_ID_PM8921_S3, + RPM_VREG_ID_PM8921_S4, + RPM_VREG_ID_PM8921_S5, + RPM_VREG_ID_PM8921_S6, + RPM_VREG_ID_PM8921_S7, + RPM_VREG_ID_PM8921_S8, + RPM_VREG_ID_PM8921_LVS1, + RPM_VREG_ID_PM8921_LVS2, + RPM_VREG_ID_PM8921_LVS3, + RPM_VREG_ID_PM8921_LVS4, + RPM_VREG_ID_PM8921_LVS5, + RPM_VREG_ID_PM8921_LVS6, + RPM_VREG_ID_PM8921_LVS7, + RPM_VREG_ID_PM8921_USB_OTG, + RPM_VREG_ID_PM8921_HDMI_MVS, + RPM_VREG_ID_PM8921_NCP, + RPM_VREG_ID_PM8921_MAX_REAL = RPM_VREG_ID_PM8921_NCP, + + /* The following are IDs for regulator devices to enable pin control. */ + RPM_VREG_ID_PM8921_L1_PC, + RPM_VREG_ID_PM8921_L2_PC, + RPM_VREG_ID_PM8921_L3_PC, + RPM_VREG_ID_PM8921_L4_PC, + RPM_VREG_ID_PM8921_L5_PC, + RPM_VREG_ID_PM8921_L6_PC, + RPM_VREG_ID_PM8921_L7_PC, + RPM_VREG_ID_PM8921_L8_PC, + RPM_VREG_ID_PM8921_L9_PC, + RPM_VREG_ID_PM8921_L10_PC, + RPM_VREG_ID_PM8921_L11_PC, + RPM_VREG_ID_PM8921_L12_PC, + RPM_VREG_ID_PM8921_L14_PC, + RPM_VREG_ID_PM8921_L15_PC, + RPM_VREG_ID_PM8921_L16_PC, + RPM_VREG_ID_PM8921_L17_PC, + RPM_VREG_ID_PM8921_L18_PC, + RPM_VREG_ID_PM8921_L21_PC, + RPM_VREG_ID_PM8921_L22_PC, + RPM_VREG_ID_PM8921_L23_PC, + + RPM_VREG_ID_PM8921_L29_PC, + RPM_VREG_ID_PM8921_S1_PC, + RPM_VREG_ID_PM8921_S2_PC, + RPM_VREG_ID_PM8921_S3_PC, + RPM_VREG_ID_PM8921_S4_PC, + + RPM_VREG_ID_PM8921_S7_PC, + RPM_VREG_ID_PM8921_S8_PC, + RPM_VREG_ID_PM8921_LVS1_PC, + + RPM_VREG_ID_PM8921_LVS3_PC, + RPM_VREG_ID_PM8921_LVS4_PC, + RPM_VREG_ID_PM8921_LVS5_PC, + RPM_VREG_ID_PM8921_LVS6_PC, + RPM_VREG_ID_PM8921_LVS7_PC, + + RPM_VREG_ID_PM8921_MAX = RPM_VREG_ID_PM8921_LVS7_PC, +}; + +/* Minimum high power mode loads in uA. */ +#define RPM_VREG_8960_LDO_50_HPM_MIN_LOAD 5000 +#define RPM_VREG_8960_LDO_150_HPM_MIN_LOAD 10000 +#define RPM_VREG_8960_LDO_300_HPM_MIN_LOAD 10000 +#define RPM_VREG_8960_LDO_600_HPM_MIN_LOAD 10000 +#define RPM_VREG_8960_LDO_1200_HPM_MIN_LOAD 10000 +#define RPM_VREG_8960_SMPS_1500_HPM_MIN_LOAD 100000 +#define RPM_VREG_8960_SMPS_2000_HPM_MIN_LOAD 100000 + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h b/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h new file mode 100644 index 00000000000..f5fa8caa70e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_9615_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_9615_H + +/* Pin control input signals. */ +#define RPM_VREG_PIN_CTRL_PM8018_D1 0x01 +#define RPM_VREG_PIN_CTRL_PM8018_A0 0x02 +#define RPM_VREG_PIN_CTRL_PM8018_A1 0x04 +#define RPM_VREG_PIN_CTRL_PM8018_A2 0x08 + +/** + * enum rpm_vreg_pin_fn_9615 - RPM regulator pin function choices + * %RPM_VREG_PIN_FN_9615_DONT_CARE: do not care about pin control state of + * the regulator; allow another master + * processor to specify pin control + * %RPM_VREG_PIN_FN_9615_ENABLE: pin control switches between disable and + * enable + * %RPM_VREG_PIN_FN_9615_MODE: pin control switches between LPM and HPM + * %RPM_VREG_PIN_FN_9615_SLEEP_B: regulator is forced into LPM when + * sleep_b signal is asserted + * %RPM_VREG_PIN_FN_9615_NONE: do not use pin control for the regulator + * and do not allow another master to + * request pin control + * + * The pin function specified in platform data corresponds to the active state + * pin function value. Pin function will be NONE until a consumer requests + * pin control to be enabled. + */ +enum rpm_vreg_pin_fn_9615 { + RPM_VREG_PIN_FN_9615_DONT_CARE, + RPM_VREG_PIN_FN_9615_ENABLE, + RPM_VREG_PIN_FN_9615_MODE, + RPM_VREG_PIN_FN_9615_SLEEP_B, + RPM_VREG_PIN_FN_9615_NONE, +}; + +/** + * enum rpm_vreg_force_mode_9615 - RPM regulator force mode choices + * %RPM_VREG_FORCE_MODE_9615_PIN_CTRL: allow pin control usage + * %RPM_VREG_FORCE_MODE_9615_NONE: do not force any mode + * %RPM_VREG_FORCE_MODE_9615_LPM: force into low power mode + * %RPM_VREG_FORCE_MODE_9615_AUTO: allow regulator to automatically select + * its own mode based on realtime current + * draw (only available for SMPS + * regulators) + * %RPM_VREG_FORCE_MODE_9615_HPM: force into high power mode + * %RPM_VREG_FORCE_MODE_9615_BYPASS: set regulator to use bypass mode, i.e. + * to act as a switch and not regulate + * (only available for LDO regulators) + * + * Force mode is used to override aggregation with other masters and to set + * special operating modes. + */ +enum rpm_vreg_force_mode_9615 { + RPM_VREG_FORCE_MODE_9615_PIN_CTRL = 0, + RPM_VREG_FORCE_MODE_9615_NONE = 0, + RPM_VREG_FORCE_MODE_9615_LPM, + RPM_VREG_FORCE_MODE_9615_AUTO, /* SMPS only */ + RPM_VREG_FORCE_MODE_9615_HPM, + RPM_VREG_FORCE_MODE_9615_BYPASS, /* LDO only */ +}; + +/** + * enum rpm_vreg_power_mode_9615 - power mode for SMPS regulators + * %RPM_VREG_POWER_MODE_9615_HYSTERETIC: Use hysteretic mode for HPM and when + * usage goes high in AUTO + * %RPM_VREG_POWER_MODE_9615_PWM: Use PWM mode for HPM and when usage + * goes high in AUTO + */ +enum rpm_vreg_power_mode_9615 { + RPM_VREG_POWER_MODE_9615_HYSTERETIC, + RPM_VREG_POWER_MODE_9615_PWM, +}; + +/** + * enum rpm_vreg_id - RPM regulator ID numbers (both real and pin control) + */ +enum rpm_vreg_id_9615 { + RPM_VREG_ID_PM8018_L2, + RPM_VREG_ID_PM8018_L3, + RPM_VREG_ID_PM8018_L4, + RPM_VREG_ID_PM8018_L5, + RPM_VREG_ID_PM8018_L6, + RPM_VREG_ID_PM8018_L7, + RPM_VREG_ID_PM8018_L8, + RPM_VREG_ID_PM8018_L9, + RPM_VREG_ID_PM8018_L10, + RPM_VREG_ID_PM8018_L11, + RPM_VREG_ID_PM8018_L12, + RPM_VREG_ID_PM8018_L13, + RPM_VREG_ID_PM8018_L14, + RPM_VREG_ID_PM8018_S1, + RPM_VREG_ID_PM8018_S2, + RPM_VREG_ID_PM8018_S3, + RPM_VREG_ID_PM8018_S4, + RPM_VREG_ID_PM8018_S5, + RPM_VREG_ID_PM8018_LVS1, + RPM_VREG_ID_PM8018_MAX_REAL = RPM_VREG_ID_PM8018_LVS1, + + /* The following are IDs for regulator devices to enable pin control. */ + RPM_VREG_ID_PM8018_L2_PC, + RPM_VREG_ID_PM8018_L3_PC, + RPM_VREG_ID_PM8018_L4_PC, + RPM_VREG_ID_PM8018_L5_PC, + RPM_VREG_ID_PM8018_L6_PC, + RPM_VREG_ID_PM8018_L7_PC, + RPM_VREG_ID_PM8018_L8_PC, + RPM_VREG_ID_PM8018_L13_PC, + RPM_VREG_ID_PM8018_L14_PC, + RPM_VREG_ID_PM8018_S1_PC, + RPM_VREG_ID_PM8018_S2_PC, + RPM_VREG_ID_PM8018_S3_PC, + RPM_VREG_ID_PM8018_S4_PC, + RPM_VREG_ID_PM8018_S5_PC, + RPM_VREG_ID_PM8018_LVS1_PC, + RPM_VREG_ID_PM8018_MAX = RPM_VREG_ID_PM8018_LVS1_PC, +}; + +/* Minimum high power mode loads in uA. */ +#define RPM_VREG_9615_LDO_50_HPM_MIN_LOAD 5000 +#define RPM_VREG_9615_LDO_150_HPM_MIN_LOAD 10000 +#define RPM_VREG_9615_LDO_300_HPM_MIN_LOAD 10000 +#define RPM_VREG_9615_LDO_1200_HPM_MIN_LOAD 10000 +#define RPM_VREG_9615_SMPS_1500_HPM_MIN_LOAD 100000 + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-copper.h b/arch/arm/mach-msm/include/mach/rpm-regulator-copper.h new file mode 100644 index 00000000000..2006ad34f2d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator-copper.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_COPPER_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_COPPER_H + +/** + * enum rpm_vreg_id - RPM regulator ID numbers (both real and pin control) + */ +enum rpm_vreg_id_copper { + RPM_VREG_ID_PM8941_S1, + RPM_VREG_ID_PM8941_S2, + RPM_VREG_ID_PM8941_L12, + RPM_VREG_ID_PM8941_MAX, +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h b/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h new file mode 100644 index 00000000000..2eb59f5905a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator-smd.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_SMD_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_SMD_H + +#include + +struct rpm_regulator; + +#if defined(CONFIG_MSM_RPM_REGULATOR_SMD) || defined(CONFIG_MSM_RPM_REGULATOR) + +struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply); + +void rpm_regulator_put(struct rpm_regulator *regulator); + +int rpm_regulator_enable(struct rpm_regulator *regulator); + +int rpm_regulator_disable(struct rpm_regulator *regulator); + +int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV, + int max_uV); + +int __init rpm_regulator_smd_driver_init(void); + +#else + +static inline struct rpm_regulator *rpm_regulator_get(struct device *dev, + const char *supply) { return NULL; } + +static inline void rpm_regulator_put(struct rpm_regulator *regulator) { } + +static inline int rpm_regulator_enable(struct rpm_regulator *regulator) + { return 0; } + +static inline int rpm_regulator_disable(struct rpm_regulator *regulator) + { return 0; } + +static inline int rpm_regulator_set_voltage(struct rpm_regulator *regulator, + int min_uV, int max_uV) { return 0; } + +static inline int __init rpm_regulator_smd_driver_init(void) { return 0; } + +#endif /* CONFIG_MSM_RPM_REGULATOR_SMD */ + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator.h b/arch/arm/mach-msm/include/mach/rpm-regulator.h new file mode 100644 index 00000000000..a0102578a7f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-regulator.h @@ -0,0 +1,234 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_H +#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_H + +#include + +#define RPM_REGULATOR_DEV_NAME "rpm-regulator" + +#include +#include +#include +#include +#include + +/** + * enum rpm_vreg_version - supported RPM regulator versions + */ +enum rpm_vreg_version { + RPM_VREG_VERSION_8660, + RPM_VREG_VERSION_8960, + RPM_VREG_VERSION_9615, + RPM_VREG_VERSION_8930, + RPM_VREG_VERSION_MAX = RPM_VREG_VERSION_8930, +}; + +#define RPM_VREG_PIN_CTRL_NONE 0x00 + +/** + * enum rpm_vreg_state - enable state for switch or NCP + */ +enum rpm_vreg_state { + RPM_VREG_STATE_OFF, + RPM_VREG_STATE_ON, +}; + +/** + * enum rpm_vreg_freq - switching frequency for SMPS or NCP + */ +enum rpm_vreg_freq { + RPM_VREG_FREQ_NONE, + RPM_VREG_FREQ_19p20, + RPM_VREG_FREQ_9p60, + RPM_VREG_FREQ_6p40, + RPM_VREG_FREQ_4p80, + RPM_VREG_FREQ_3p84, + RPM_VREG_FREQ_3p20, + RPM_VREG_FREQ_2p74, + RPM_VREG_FREQ_2p40, + RPM_VREG_FREQ_2p13, + RPM_VREG_FREQ_1p92, + RPM_VREG_FREQ_1p75, + RPM_VREG_FREQ_1p60, + RPM_VREG_FREQ_1p48, + RPM_VREG_FREQ_1p37, + RPM_VREG_FREQ_1p28, + RPM_VREG_FREQ_1p20, +}; + +/** + * enum rpm_vreg_voltage_corner - possible voltage corner values + * + * These should be used in regulator_set_voltage and rpm_vreg_set_voltage calls + * for corner type regulators as if they had units of uV. + */ +enum rpm_vreg_voltage_corner { + RPM_VREG_CORNER_NONE = 1, + RPM_VREG_CORNER_LOW, + RPM_VREG_CORNER_NOMINAL, + RPM_VREG_CORNER_HIGH, +}; + +/** + * enum rpm_vreg_voter - RPM regulator voter IDs for private APIs + */ +enum rpm_vreg_voter { + RPM_VREG_VOTER_REG_FRAMEWORK, /* for internal use only */ + RPM_VREG_VOTER1, /* for use by the acpu-clock driver */ + RPM_VREG_VOTER2, /* for use by the acpu-clock driver */ + RPM_VREG_VOTER3, /* for use by other drivers */ + RPM_VREG_VOTER4, /* for use by the acpu-clock driver */ + RPM_VREG_VOTER5, /* for use by the acpu-clock driver */ + RPM_VREG_VOTER6, /* for use by the acpu-clock driver */ + RPM_VREG_VOTER_COUNT, +}; + +/** + * struct rpm_regulator_init_data - RPM regulator initialization data + * @init_data: regulator constraints + * @id: regulator id; from enum rpm_vreg_id + * @sleep_selectable: flag which indicates that regulator should be accessable + * by external private API and that spinlocks should be + * used instead of mutex locks + * @system_uA: current drawn from regulator not accounted for by any + * regulator framework consumer + * @enable_time: time in us taken to enable a regulator to the maximum + * allowed voltage for the system. This is dependent upon + * the load and capacitance for a regulator on the board. + * @pull_down_enable: 0 = no pulldown, 1 = pulldown when regulator disabled + * @freq: enum value representing the switching frequency of an + * SMPS or NCP + * @pin_ctrl: pin control inputs to use for the regulator; should be + * a combination of RPM_VREG_PIN_CTRL_* values + * @pin_fn: action to perform when pin control pin(s) is/are active + * @force_mode: used to specify a force mode which overrides the votes + * of other RPM masters. + * @sleep_set_force_mode: force mode to use in sleep-set requests + * @power_mode: mode to use as HPM (typically PWM or hysteretic) when + * utilizing Auto mode selection + * @default_uV: initial voltage to set the regulator to if enable is + * called before set_voltage (e.g. when boot_on or + * always_on is set). + * @peak_uA: initial peak load requirement sent in RPM request; used + * to determine initial mode. + * @avg_uA: average load requirement sent in RPM request + * @state: initial enable state sent in RPM request for switch or + * NCP + */ +struct rpm_regulator_init_data { + struct regulator_init_data init_data; + int id; + int sleep_selectable; + int system_uA; + int enable_time; + unsigned pull_down_enable; + enum rpm_vreg_freq freq; + unsigned pin_ctrl; + int pin_fn; + int force_mode; + int sleep_set_force_mode; + int power_mode; + int default_uV; + unsigned peak_uA; + unsigned avg_uA; + enum rpm_vreg_state state; +}; + +/** + * struct rpm_regulator_consumer_mapping - mapping used by private consumers + */ +struct rpm_regulator_consumer_mapping { + const char *dev_name; + const char *supply; + int vreg_id; + enum rpm_vreg_voter voter; + int sleep_also; +}; + +/** + * struct rpm_regulator_platform_data - RPM regulator platform data + */ +struct rpm_regulator_platform_data { + struct rpm_regulator_init_data *init_data; + int num_regulators; + enum rpm_vreg_version version; + int vreg_id_vdd_mem; + int vreg_id_vdd_dig; + struct rpm_regulator_consumer_mapping *consumer_map; + int consumer_map_len; +}; + +#ifdef CONFIG_MSM_RPM_REGULATOR +/** + * rpm_vreg_set_voltage - vote for a min_uV value of specified regualtor + * @vreg: ID for regulator + * @voter: ID for the voter + * @min_uV: minimum acceptable voltage (in uV) that is voted for + * @max_uV: maximum acceptable voltage (in uV) that is voted for + * @sleep_also: 0 for active set only, non-0 for active set and sleep set + * + * Returns 0 on success or errno. + * + * This function is used to vote for the voltage of a regulator without + * using the regulator framework. It is needed by consumers which hold spin + * locks or have interrupts disabled because the regulator framework can sleep. + * It is also needed by consumers which wish to only vote for active set + * regulator voltage. + * + * If sleep_also == 0, then a sleep-set value of 0V will be voted for. + * + * This function may only be called for regulators which have the sleep flag + * specified in their private data. + * + * Consumers can vote to disable a regulator with this function by passing + * min_uV = 0 and max_uV = 0. + * + * Voltage switch type regulators may be controlled via rpm_vreg_set_voltage + * as well. For this type of regulator, max_uV > 0 is treated as an enable + * request and max_uV == 0 is treated as a disable request. + */ +int rpm_vreg_set_voltage(int vreg_id, enum rpm_vreg_voter voter, int min_uV, + int max_uV, int sleep_also); + +/** + * rpm_vreg_set_frequency - sets the frequency of a switching regulator + * @vreg: ID for regulator + * @freq: enum corresponding to desired frequency + * + * Returns 0 on success or errno. + */ +int rpm_vreg_set_frequency(int vreg_id, enum rpm_vreg_freq freq); + +#else + +/* + * These stubs exist to allow consumers of these APIs to compile and run + * in absence of a real RPM regulator driver. It is assumed that they are + * aware of the state of their regulators and have either set them + * correctly by some other means or don't care about their state at all. + */ +static inline int rpm_vreg_set_voltage(int vreg_id, enum rpm_vreg_voter voter, + int min_uV, int max_uV, int sleep_also) +{ + return 0; +} + +static inline int rpm_vreg_set_frequency(int vreg_id, enum rpm_vreg_freq freq) +{ + return 0; +} + +#endif /* CONFIG_MSM_RPM_REGULATOR */ + +#endif diff --git a/arch/arm/mach-msm/include/mach/rpm-smd.h b/arch/arm/mach-msm/include/mach/rpm-smd.h new file mode 100644 index 00000000000..ff58fed150a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm-smd.h @@ -0,0 +1,254 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_SMD_H +#define __ARCH_ARM_MACH_MSM_RPM_SMD_H + +/** + * enum msm_rpm_set - RPM enumerations for sleep/active set + * %MSM_RPM_CTX_SET_0: Set resource parameters for active mode. + * %MSM_RPM_CTX_SET_SLEEP: Set resource parameters for sleep. + */ +enum msm_rpm_set { + MSM_RPM_CTX_ACTIVE_SET, + MSM_RPM_CTX_SLEEP_SET, +}; + +struct msm_rpm_request; + +struct msm_rpm_kvp { + uint32_t key; + uint32_t length; + uint8_t *data; +}; +#ifdef CONFIG_MSM_RPM_SMD +/** + * msm_rpm_request() - Creates a parent element to identify the + * resource on the RPM, that stores the KVPs for different fields modified + * for a hardware resource + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @num_elements: number of KVPs pairs associated with the resource + * + * returns pointer to a msm_rpm_request on success, NULL on error + */ +struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements); + +/** + * msm_rpm_request_noirq() - Creates a parent element to identify the + * resource on the RPM, that stores the KVPs for different fields modified + * for a hardware resource. This function is similar to msm_rpm_create_request + * except that it has to be called with interrupts masked. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @num_elements: number of KVPs pairs associated with the resource + * + * returns pointer to a msm_rpm_request on success, NULL on error + */ +struct msm_rpm_request *msm_rpm_create_request_noirq( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements); + +/** + * msm_rpm_add_kvp_data() - Adds a Key value pair to a existing RPM resource. + * + * @handle: RPM resource handle to which the data should be appended + * @key: unsigned integer identify the parameter modified + * @data: byte array that contains the value corresponding to key. + * @size: size of data in bytes. + * + * returns 0 on success or errno + */ +int msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size); + +/** + * msm_rpm_add_kvp_data_noirq() - Adds a Key value pair to a existing RPM + * resource. This function is similar to msm_rpm_add_kvp_data except that it + * has to be called with interrupts masked. + * + * @handle: RPM resource handle to which the data should be appended + * @key: unsigned integer identify the parameter modified + * @data: byte array that contains the value corresponding to key. + * @size: size of data in bytes. + * + * returns 0 on success or errno + */ +int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size); + +/** msm_rpm_free_request() - clean up the RPM request handle created with + * msm_rpm_create_request + * + * @handle: RPM resource handle to be cleared. + */ + +void msm_rpm_free_request(struct msm_rpm_request *handle); + +/** + * msm_rpm_send_request() - Send the RPM messages using SMD. The function + * assigns a message id before sending the data out to the RPM. RPM hardware + * uses the message id to acknowledge the messages. + * + * @handle: pointer to the msm_rpm_request for the resource being modified. + * + * returns non-zero message id on success and zero on a failed transaction. + * The drivers use message id to wait for ACK from RPM. + */ +int msm_rpm_send_request(struct msm_rpm_request *handle); + +/** + * msm_rpm_send_request_noirq() - Send the RPM messages using SMD. The + * function assigns a message id before sending the data out to the RPM. + * RPM hardware uses the message id to acknowledge the messages. This function + * is similar to msm_rpm_send_request except that it has to be called with + * interrupts masked. + * + * @handle: pointer to the msm_rpm_request for the resource being modified. + * + * returns non-zero message id on success and zero on a failed transaction. + * The drivers use message id to wait for ACK from RPM. + */ +int msm_rpm_send_request_noirq(struct msm_rpm_request *handle); + +/** + * msm_rpm_wait_for_ack() - A blocking call that waits for acknowledgment of + * a message from RPM. + * + * @msg_id: the return from msm_rpm_send_requests + * + * returns 0 on success or errno + */ +int msm_rpm_wait_for_ack(uint32_t msg_id); + +/** + * msm_rpm_wait_for_ack_noirq() - A blocking call that waits for acknowledgment + * of a message from RPM. This function is similar to msm_rpm_wait_for_ack + * except that it has to be called with interrupts masked. + * + * @msg_id: the return from msm_rpm_send_request + * + * returns 0 on success or errno + */ +int msm_rpm_wait_for_ack_noirq(uint32_t msg_id); + +/** + * msm_rpm_send_message() -Wrapper function for clients to send data given an + * array of key value pairs. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @kvp: array of KVP data. + * @nelem: number of KVPs pairs associated with the message. + * + * returns 0 on success and errno on failure. + */ +int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); + +/** + * msm_rpm_send_message_noirq() -Wrapper function for clients to send data + * given an array of key value pairs. This function is similar to the + * msm_rpm_send_message() except that it has to be called with interrupts + * disabled. Clients should choose the irq version when possible for system + * performance. + * + * @set: if the device is setting the active/sleep set parameter + * for the resource + * @rsc_type: unsigned 32 bit integer that identifies the type of the resource + * @rsc_id: unsigned 32 bit that uniquely identifies a resource within a type + * @kvp: array of KVP data. + * @nelem: number of KVPs pairs associated with the message. + * + * returns 0 on success and errno on failure. + */ +int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems); + +/** + * msm_rpm_driver_init() - Initialization function that registers for a + * rpm platform driver. + * + * returns 0 on success. + */ +int __init msm_rpm_driver_init(void); + +#else + +static inline struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return NULL; +} + +static inline struct msm_rpm_request *msm_rpm_create_request_noirq( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return NULL; + +} +static inline uint32_t msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int count) +{ + return 0; +} +static inline uint32_t msm_rpm_add_kvp_data_noirq( + struct msm_rpm_request *handle, uint32_t key, + const uint8_t *data, int count) +{ + return 0; +} + +static inline void msm_rpm_free_request(struct msm_rpm_request *handle) +{ + return ; +} + +static inline int msm_rpm_send_request(struct msm_rpm_request *handle) +{ + return 0; +} + +static inline int msm_rpm_send_request_noirq(struct msm_rpm_request *handle) +{ + return 0; + +} +static inline int msm_rpm_wait_for_ack(uint32_t msg_id) +{ + return 0; + +} +static inline int msm_rpm_wait_for_ack_noirq(uint32_t msg_id) +{ + return 0; +} + +static inline int __init msm_rpm_driver_init(void) +{ + return 0; +} +#endif +#endif /*__ARCH_ARM_MACH_MSM_RPM_SMD_H*/ diff --git a/arch/arm/mach-msm/include/mach/rpm.h b/arch/arm/mach-msm/include/mach/rpm.h new file mode 100644 index 00000000000..98621be75c6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpm.h @@ -0,0 +1,941 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_H +#define __ARCH_ARM_MACH_MSM_RPM_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SEL_MASK_SIZE (5) + +enum { + MSM_RPM_PAGE_STATUS, + MSM_RPM_PAGE_CTRL, + MSM_RPM_PAGE_REQ, + MSM_RPM_PAGE_ACK, + MSM_RPM_PAGE_COUNT +}; + +enum { + MSM_RPM_CTX_SET_0, + MSM_RPM_CTX_SET_SLEEP, + MSM_RPM_CTX_SET_COUNT, + + MSM_RPM_CTX_NOTIFICATION = 30, + MSM_RPM_CTX_REJECTED = 31, +}; + +/* RPM control message RAM enums */ +enum { + MSM_RPM_CTRL_VERSION_MAJOR, + MSM_RPM_CTRL_VERSION_MINOR, + MSM_RPM_CTRL_VERSION_BUILD, + + MSM_RPM_CTRL_REQ_CTX_0, + MSM_RPM_CTRL_REQ_SEL_0, + MSM_RPM_CTRL_ACK_CTX_0, + MSM_RPM_CTRL_ACK_SEL_0, + + MSM_RPM_CTRL_LAST, +}; + +enum { + MSM_RPM_ID_NOTIFICATION_CONFIGURED_0 = 0, + MSM_RPM_ID_NOTIFICATION_CONFIGURED_7 = + MSM_RPM_ID_NOTIFICATION_CONFIGURED_0 + 7, + + MSM_RPM_ID_NOTIFICATION_REGISTERED_0, + MSM_RPM_ID_NOTIFICATION_REGISTERED_7 = + MSM_RPM_ID_NOTIFICATION_REGISTERED_0 + 7, + + MSM_RPM_ID_INVALIDATE_0, + MSM_RPM_ID_INVALIDATE_7 = + MSM_RPM_ID_INVALIDATE_0 + 7, + + MSM_RPM_ID_TRIGGER_TIMED_TO, + MSM_RPM_ID_TRIGGER_TIMED_0, + MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, + + MSM_RPM_ID_RPM_CTL, + + /* TRIGGER_CLEAR/SET deprecated in these 24 RESERVED bytes */ + MSM_RPM_ID_RESERVED_0, + MSM_RPM_ID_RESERVED_5 = + MSM_RPM_ID_RESERVED_0 + 5, + + MSM_RPM_ID_CXO_CLK, + MSM_RPM_ID_PXO_CLK, + MSM_RPM_ID_APPS_FABRIC_CLK, + MSM_RPM_ID_SYSTEM_FABRIC_CLK, + MSM_RPM_ID_MM_FABRIC_CLK, + MSM_RPM_ID_DAYTONA_FABRIC_CLK, + MSM_RPM_ID_SFPB_CLK, + MSM_RPM_ID_CFPB_CLK, + MSM_RPM_ID_MMFPB_CLK, + MSM_RPM_ID_EBI1_CLK, + + MSM_RPM_ID_APPS_FABRIC_CFG_HALT_0, + MSM_RPM_ID_APPS_FABRIC_HALT_0 = + MSM_RPM_ID_APPS_FABRIC_CFG_HALT_0, + MSM_RPM_ID_APPS_FABRIC_CFG_HALT_1, + MSM_RPM_ID_APPS_FABRIC_CFG_CLKMOD_0, + MSM_RPM_ID_APPS_FABRIC_CLOCK_MODE_0 = + MSM_RPM_ID_APPS_FABRIC_CFG_CLKMOD_0, + MSM_RPM_ID_APPS_FABRIC_CFG_CLKMOD_1, + MSM_RPM_ID_APPS_FABRIC_CFG_CLKMOD_2, + MSM_RPM_ID_APPS_FABRIC_CFG_IOCTL, + MSM_RPM_ID_APPS_FABRIC_ARB_0, + MSM_RPM_ID_APPS_FABRIC_ARB_11 = + MSM_RPM_ID_APPS_FABRIC_ARB_0 + 11, + + MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0, + MSM_RPM_ID_SYSTEM_FABRIC_HALT_0 = + MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0, + MSM_RPM_ID_SYS_FABRIC_CFG_HALT_1, + MSM_RPM_ID_SYS_FABRIC_CFG_CLKMOD_0, + MSM_RPM_ID_SYSTEM_FABRIC_CLOCK_MODE_0 = + MSM_RPM_ID_SYS_FABRIC_CFG_CLKMOD_0, + MSM_RPM_ID_SYS_FABRIC_CFG_CLKMOD_1, + MSM_RPM_ID_SYS_FABRIC_CFG_CLKMOD_2, + MSM_RPM_ID_SYS_FABRIC_CFG_IOCTL, + MSM_RPM_ID_SYSTEM_FABRIC_ARB_0, + MSM_RPM_ID_SYSTEM_FABRIC_ARB_29 = + MSM_RPM_ID_SYSTEM_FABRIC_ARB_0 + 29, + + MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0, + MSM_RPM_ID_MM_FABRIC_HALT_0 = + MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0, + MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_1, + MSM_RPM_ID_MMSS_FABRIC_CFG_CLKMOD_0, + MSM_RPM_ID_MM_FABRIC_CLOCK_MODE_0 = + MSM_RPM_ID_MMSS_FABRIC_CFG_CLKMOD_0, + MSM_RPM_ID_MMSS_FABRIC_CFG_CLKMOD_1, + MSM_RPM_ID_MMSS_FABRIC_CFG_CLKMOD_2, + MSM_RPM_ID_MMSS_FABRIC_CFG_IOCTL, + MSM_RPM_ID_MM_FABRIC_ARB_0, + MSM_RPM_ID_MM_FABRIC_ARB_22 = + MSM_RPM_ID_MM_FABRIC_ARB_0 + 22, + + MSM_RPM_ID_PM8921_S1_0, + MSM_RPM_ID_PM8921_S1_1, + MSM_RPM_ID_PM8921_S2_0, + MSM_RPM_ID_PM8921_S2_1, + MSM_RPM_ID_PM8921_S3_0, + MSM_RPM_ID_PM8921_S3_1, + MSM_RPM_ID_PM8921_S4_0, + MSM_RPM_ID_PM8921_S4_1, + MSM_RPM_ID_PM8921_S5_0, + MSM_RPM_ID_PM8921_S5_1, + MSM_RPM_ID_PM8921_S6_0, + MSM_RPM_ID_PM8921_S6_1, + MSM_RPM_ID_PM8921_S7_0, + MSM_RPM_ID_PM8921_S7_1, + MSM_RPM_ID_PM8921_S8_0, + MSM_RPM_ID_PM8921_S8_1, + MSM_RPM_ID_PM8921_L1_0, + MSM_RPM_ID_PM8921_L1_1, + MSM_RPM_ID_PM8921_L2_0, + MSM_RPM_ID_PM8921_L2_1, + MSM_RPM_ID_PM8921_L3_0, + MSM_RPM_ID_PM8921_L3_1, + MSM_RPM_ID_PM8921_L4_0, + MSM_RPM_ID_PM8921_L4_1, + MSM_RPM_ID_PM8921_L5_0, + MSM_RPM_ID_PM8921_L5_1, + MSM_RPM_ID_PM8921_L6_0, + MSM_RPM_ID_PM8921_L6_1, + MSM_RPM_ID_PM8921_L7_0, + MSM_RPM_ID_PM8921_L7_1, + MSM_RPM_ID_PM8921_L8_0, + MSM_RPM_ID_PM8921_L8_1, + MSM_RPM_ID_PM8921_L9_0, + MSM_RPM_ID_PM8921_L9_1, + MSM_RPM_ID_PM8921_L10_0, + MSM_RPM_ID_PM8921_L10_1, + MSM_RPM_ID_PM8921_L11_0, + MSM_RPM_ID_PM8921_L11_1, + MSM_RPM_ID_PM8921_L12_0, + MSM_RPM_ID_PM8921_L12_1, + MSM_RPM_ID_PM8921_L13_0, + MSM_RPM_ID_PM8921_L13_1, + MSM_RPM_ID_PM8921_L14_0, + MSM_RPM_ID_PM8921_L14_1, + MSM_RPM_ID_PM8921_L15_0, + MSM_RPM_ID_PM8921_L15_1, + MSM_RPM_ID_PM8921_L16_0, + MSM_RPM_ID_PM8921_L16_1, + MSM_RPM_ID_PM8921_L17_0, + MSM_RPM_ID_PM8921_L17_1, + MSM_RPM_ID_PM8921_L18_0, + MSM_RPM_ID_PM8921_L18_1, + MSM_RPM_ID_PM8921_L19_0, + MSM_RPM_ID_PM8921_L19_1, + MSM_RPM_ID_PM8921_L20_0, + MSM_RPM_ID_PM8921_L20_1, + MSM_RPM_ID_PM8921_L21_0, + MSM_RPM_ID_PM8921_L21_1, + MSM_RPM_ID_PM8921_L22_0, + MSM_RPM_ID_PM8921_L22_1, + MSM_RPM_ID_PM8921_L23_0, + MSM_RPM_ID_PM8921_L23_1, + MSM_RPM_ID_PM8921_L24_0, + MSM_RPM_ID_PM8921_L24_1, + MSM_RPM_ID_PM8921_L25_0, + MSM_RPM_ID_PM8921_L25_1, + MSM_RPM_ID_PM8921_L26_0, + MSM_RPM_ID_PM8921_L26_1, + MSM_RPM_ID_PM8921_L27_0, + MSM_RPM_ID_PM8921_L27_1, + MSM_RPM_ID_PM8921_L28_0, + MSM_RPM_ID_PM8921_L28_1, + MSM_RPM_ID_PM8921_L29_0, + MSM_RPM_ID_PM8921_L29_1, + MSM_RPM_ID_PM8921_CLK1_0, + MSM_RPM_ID_PM8921_CLK1_1, + MSM_RPM_ID_PM8921_CLK2_0, + MSM_RPM_ID_PM8921_CLK2_1, + MSM_RPM_ID_PM8921_LVS1, + MSM_RPM_ID_PM8921_LVS2, + MSM_RPM_ID_PM8921_LVS3, + MSM_RPM_ID_PM8921_LVS4, + MSM_RPM_ID_PM8921_LVS5, + MSM_RPM_ID_PM8921_LVS6, + MSM_RPM_ID_PM8921_LVS7, + MSM_RPM_ID_NCP_0, + MSM_RPM_ID_NCP_1, + MSM_RPM_ID_CXO_BUFFERS, + MSM_RPM_ID_USB_OTG_SWITCH, + MSM_RPM_ID_HDMI_SWITCH, + MSM_RPM_ID_DDR_DMM_0, + MSM_RPM_ID_DDR_DMM_1, + MSM_RPM_ID_QDSS_CLK, + + /* 8660 specific ids */ + MSM_RPM_ID_TRIGGER_SET_FROM, + MSM_RPM_ID_TRIGGER_SET_TO, + MSM_RPM_ID_TRIGGER_SET_TRIGGER, + + MSM_RPM_ID_TRIGGER_CLEAR_FROM, + MSM_RPM_ID_TRIGGER_CLEAR_TO, + MSM_RPM_ID_TRIGGER_CLEAR_TRIGGER, + MSM_RPM_ID_PLL_4, + MSM_RPM_ID_SMI_CLK, + MSM_RPM_ID_APPS_L2_CACHE_CTL, + + /* pmic 8901 */ + MSM_RPM_ID_SMPS0B_0, + MSM_RPM_ID_SMPS0B_1, + MSM_RPM_ID_SMPS1B_0, + MSM_RPM_ID_SMPS1B_1, + MSM_RPM_ID_SMPS2B_0, + MSM_RPM_ID_SMPS2B_1, + MSM_RPM_ID_SMPS3B_0, + MSM_RPM_ID_SMPS3B_1, + MSM_RPM_ID_SMPS4B_0, + MSM_RPM_ID_SMPS4B_1, + MSM_RPM_ID_LDO0B_0, + MSM_RPM_ID_LDO0B_1, + MSM_RPM_ID_LDO1B_0, + MSM_RPM_ID_LDO1B_1, + MSM_RPM_ID_LDO2B_0, + MSM_RPM_ID_LDO2B_1, + MSM_RPM_ID_LDO3B_0, + MSM_RPM_ID_LDO3B_1, + MSM_RPM_ID_LDO4B_0, + MSM_RPM_ID_LDO4B_1, + MSM_RPM_ID_LDO5B_0, + MSM_RPM_ID_LDO5B_1, + MSM_RPM_ID_LDO6B_0, + MSM_RPM_ID_LDO6B_1, + MSM_RPM_ID_LVS0B, + MSM_RPM_ID_LVS1B, + MSM_RPM_ID_LVS2B, + MSM_RPM_ID_LVS3B, + MSM_RPM_ID_MVS, + + /* pmic 8058 */ + MSM_RPM_ID_SMPS0_0, + MSM_RPM_ID_SMPS0_1, + MSM_RPM_ID_SMPS1_0, + MSM_RPM_ID_SMPS1_1, + MSM_RPM_ID_SMPS2_0, + MSM_RPM_ID_SMPS2_1, + MSM_RPM_ID_SMPS3_0, + MSM_RPM_ID_SMPS3_1, + MSM_RPM_ID_SMPS4_0, + MSM_RPM_ID_SMPS4_1, + MSM_RPM_ID_LDO0_0, + MSM_RPM_ID_LDO0_1, + MSM_RPM_ID_LDO1_0, + MSM_RPM_ID_LDO1_1, + MSM_RPM_ID_LDO2_0, + MSM_RPM_ID_LDO2_1, + MSM_RPM_ID_LDO3_0, + MSM_RPM_ID_LDO3_1, + MSM_RPM_ID_LDO4_0, + MSM_RPM_ID_LDO4_1, + MSM_RPM_ID_LDO5_0, + MSM_RPM_ID_LDO5_1, + MSM_RPM_ID_LDO6_0, + MSM_RPM_ID_LDO6_1, + MSM_RPM_ID_LDO7_0, + MSM_RPM_ID_LDO7_1, + MSM_RPM_ID_LDO8_0, + MSM_RPM_ID_LDO8_1, + MSM_RPM_ID_LDO9_0, + MSM_RPM_ID_LDO9_1, + MSM_RPM_ID_LDO10_0, + MSM_RPM_ID_LDO10_1, + MSM_RPM_ID_LDO11_0, + MSM_RPM_ID_LDO11_1, + MSM_RPM_ID_LDO12_0, + MSM_RPM_ID_LDO12_1, + MSM_RPM_ID_LDO13_0, + MSM_RPM_ID_LDO13_1, + MSM_RPM_ID_LDO14_0, + MSM_RPM_ID_LDO14_1, + MSM_RPM_ID_LDO15_0, + MSM_RPM_ID_LDO15_1, + MSM_RPM_ID_LDO16_0, + MSM_RPM_ID_LDO16_1, + MSM_RPM_ID_LDO17_0, + MSM_RPM_ID_LDO17_1, + MSM_RPM_ID_LDO18_0, + MSM_RPM_ID_LDO18_1, + MSM_RPM_ID_LDO19_0, + MSM_RPM_ID_LDO19_1, + MSM_RPM_ID_LDO20_0, + MSM_RPM_ID_LDO20_1, + MSM_RPM_ID_LDO21_0, + MSM_RPM_ID_LDO21_1, + MSM_RPM_ID_LDO22_0, + MSM_RPM_ID_LDO22_1, + MSM_RPM_ID_LDO23_0, + MSM_RPM_ID_LDO23_1, + MSM_RPM_ID_LDO24_0, + MSM_RPM_ID_LDO24_1, + MSM_RPM_ID_LDO25_0, + MSM_RPM_ID_LDO25_1, + MSM_RPM_ID_LVS0, + MSM_RPM_ID_LVS1, + + /* 9615 specific */ + MSM_RPM_ID_PM8018_S1_0, + MSM_RPM_ID_PM8018_S1_1, + MSM_RPM_ID_PM8018_S2_0, + MSM_RPM_ID_PM8018_S2_1, + MSM_RPM_ID_PM8018_S3_0, + MSM_RPM_ID_PM8018_S3_1, + MSM_RPM_ID_PM8018_S4_0, + MSM_RPM_ID_PM8018_S4_1, + MSM_RPM_ID_PM8018_S5_0, + MSM_RPM_ID_PM8018_S5_1, + MSM_RPM_ID_PM8018_L1_0, + MSM_RPM_ID_PM8018_L1_1, + MSM_RPM_ID_PM8018_L2_0, + MSM_RPM_ID_PM8018_L2_1, + MSM_RPM_ID_PM8018_L3_0, + MSM_RPM_ID_PM8018_L3_1, + MSM_RPM_ID_PM8018_L4_0, + MSM_RPM_ID_PM8018_L4_1, + MSM_RPM_ID_PM8018_L5_0, + MSM_RPM_ID_PM8018_L5_1, + MSM_RPM_ID_PM8018_L6_0, + MSM_RPM_ID_PM8018_L6_1, + MSM_RPM_ID_PM8018_L7_0, + MSM_RPM_ID_PM8018_L7_1, + MSM_RPM_ID_PM8018_L8_0, + MSM_RPM_ID_PM8018_L8_1, + MSM_RPM_ID_PM8018_L9_0, + MSM_RPM_ID_PM8018_L9_1, + MSM_RPM_ID_PM8018_L10_0, + MSM_RPM_ID_PM8018_L10_1, + MSM_RPM_ID_PM8018_L11_0, + MSM_RPM_ID_PM8018_L11_1, + MSM_RPM_ID_PM8018_L12_0, + MSM_RPM_ID_PM8018_L12_1, + MSM_RPM_ID_PM8018_L13_0, + MSM_RPM_ID_PM8018_L13_1, + MSM_RPM_ID_PM8018_L14_0, + MSM_RPM_ID_PM8018_L14_1, + MSM_RPM_ID_PM8018_LVS1, + + /* 8930 specific */ + MSM_RPM_ID_PM8038_S1_0, + MSM_RPM_ID_PM8038_S1_1, + MSM_RPM_ID_PM8038_S2_0, + MSM_RPM_ID_PM8038_S2_1, + MSM_RPM_ID_PM8038_S3_0, + MSM_RPM_ID_PM8038_S3_1, + MSM_RPM_ID_PM8038_S4_0, + MSM_RPM_ID_PM8038_S4_1, + MSM_RPM_ID_PM8038_S5_0, + MSM_RPM_ID_PM8038_S5_1, + MSM_RPM_ID_PM8038_S6_0, + MSM_RPM_ID_PM8038_S6_1, + MSM_RPM_ID_PM8038_L1_0, + MSM_RPM_ID_PM8038_L1_1, + MSM_RPM_ID_PM8038_L2_0, + MSM_RPM_ID_PM8038_L2_1, + MSM_RPM_ID_PM8038_L3_0, + MSM_RPM_ID_PM8038_L3_1, + MSM_RPM_ID_PM8038_L4_0, + MSM_RPM_ID_PM8038_L4_1, + MSM_RPM_ID_PM8038_L5_0, + MSM_RPM_ID_PM8038_L5_1, + MSM_RPM_ID_PM8038_L6_0, + MSM_RPM_ID_PM8038_L6_1, + MSM_RPM_ID_PM8038_L7_0, + MSM_RPM_ID_PM8038_L7_1, + MSM_RPM_ID_PM8038_L8_0, + MSM_RPM_ID_PM8038_L8_1, + MSM_RPM_ID_PM8038_L9_0, + MSM_RPM_ID_PM8038_L9_1, + MSM_RPM_ID_PM8038_L10_0, + MSM_RPM_ID_PM8038_L10_1, + MSM_RPM_ID_PM8038_L11_0, + MSM_RPM_ID_PM8038_L11_1, + MSM_RPM_ID_PM8038_L12_0, + MSM_RPM_ID_PM8038_L12_1, + MSM_RPM_ID_PM8038_L13_0, + MSM_RPM_ID_PM8038_L13_1, + MSM_RPM_ID_PM8038_L14_0, + MSM_RPM_ID_PM8038_L14_1, + MSM_RPM_ID_PM8038_L15_0, + MSM_RPM_ID_PM8038_L15_1, + MSM_RPM_ID_PM8038_L16_0, + MSM_RPM_ID_PM8038_L16_1, + MSM_RPM_ID_PM8038_L17_0, + MSM_RPM_ID_PM8038_L17_1, + MSM_RPM_ID_PM8038_L18_0, + MSM_RPM_ID_PM8038_L18_1, + MSM_RPM_ID_PM8038_L19_0, + MSM_RPM_ID_PM8038_L19_1, + MSM_RPM_ID_PM8038_L20_0, + MSM_RPM_ID_PM8038_L20_1, + MSM_RPM_ID_PM8038_L21_0, + MSM_RPM_ID_PM8038_L21_1, + MSM_RPM_ID_PM8038_L22_0, + MSM_RPM_ID_PM8038_L22_1, + MSM_RPM_ID_PM8038_L23_0, + MSM_RPM_ID_PM8038_L23_1, + MSM_RPM_ID_PM8038_L24_0, + MSM_RPM_ID_PM8038_L24_1, + MSM_RPM_ID_PM8038_L25_0, + MSM_RPM_ID_PM8038_L25_1, + MSM_RPM_ID_PM8038_L26_0, + MSM_RPM_ID_PM8038_L26_1, + MSM_RPM_ID_PM8038_L27_0, + MSM_RPM_ID_PM8038_L27_1, + MSM_RPM_ID_PM8038_CLK1_0, + MSM_RPM_ID_PM8038_CLK1_1, + MSM_RPM_ID_PM8038_CLK2_0, + MSM_RPM_ID_PM8038_CLK2_1, + MSM_RPM_ID_PM8038_LVS1, + MSM_RPM_ID_PM8038_LVS2, + MSM_RPM_ID_VOLTAGE_CORNER, + + /* 8064 specific */ + MSM_RPM_ID_PM8821_S1_0, + MSM_RPM_ID_PM8821_S1_1, + MSM_RPM_ID_PM8821_S2_0, + MSM_RPM_ID_PM8821_S2_1, + MSM_RPM_ID_PM8821_L1_0, + MSM_RPM_ID_PM8821_L1_1, + + MSM_RPM_ID_LAST, +}; + +enum { + MSM_RPM_STATUS_ID_VERSION_MAJOR, + MSM_RPM_STATUS_ID_VERSION_MINOR, + MSM_RPM_STATUS_ID_VERSION_BUILD, + MSM_RPM_STATUS_ID_SUPPORTED_RESOURCES_0, + MSM_RPM_STATUS_ID_SUPPORTED_RESOURCES_1, + MSM_RPM_STATUS_ID_SUPPORTED_RESOURCES_2, + MSM_RPM_STATUS_ID_RESERVED_SUPPORTED_RESOURCES_0, + MSM_RPM_STATUS_ID_SEQUENCE, + MSM_RPM_STATUS_ID_RPM_CTL, + MSM_RPM_STATUS_ID_CXO_CLK, + MSM_RPM_STATUS_ID_PXO_CLK, + MSM_RPM_STATUS_ID_APPS_FABRIC_CLK, + MSM_RPM_STATUS_ID_SYSTEM_FABRIC_CLK, + MSM_RPM_STATUS_ID_MM_FABRIC_CLK, + MSM_RPM_STATUS_ID_DAYTONA_FABRIC_CLK, + MSM_RPM_STATUS_ID_SFPB_CLK, + MSM_RPM_STATUS_ID_CFPB_CLK, + MSM_RPM_STATUS_ID_MMFPB_CLK, + MSM_RPM_STATUS_ID_EBI1_CLK, + MSM_RPM_STATUS_ID_APPS_FABRIC_CFG_HALT, + MSM_RPM_STATUS_ID_APPS_FABRIC_HALT = + MSM_RPM_STATUS_ID_APPS_FABRIC_CFG_HALT, + MSM_RPM_STATUS_ID_APPS_FABRIC_CFG_CLKMOD, + MSM_RPM_STATUS_ID_APPS_FABRIC_CLOCK_MODE = + MSM_RPM_STATUS_ID_APPS_FABRIC_CFG_CLKMOD, + MSM_RPM_STATUS_ID_APPS_FABRIC_CFG_IOCTL, + MSM_RPM_STATUS_ID_APPS_FABRIC_ARB, + MSM_RPM_STATUS_ID_SYS_FABRIC_CFG_HALT, + MSM_RPM_STATUS_ID_SYSTEM_FABRIC_HALT = + MSM_RPM_STATUS_ID_SYS_FABRIC_CFG_HALT, + MSM_RPM_STATUS_ID_SYS_FABRIC_CFG_CLKMOD, + MSM_RPM_STATUS_ID_SYSTEM_FABRIC_CLOCK_MODE = + MSM_RPM_STATUS_ID_SYS_FABRIC_CFG_CLKMOD, + MSM_RPM_STATUS_ID_SYS_FABRIC_CFG_IOCTL, + MSM_RPM_STATUS_ID_SYSTEM_FABRIC_ARB, + MSM_RPM_STATUS_ID_MMSS_FABRIC_CFG_HALT, + MSM_RPM_STATUS_ID_MM_FABRIC_HALT = + MSM_RPM_STATUS_ID_MMSS_FABRIC_CFG_HALT, + MSM_RPM_STATUS_ID_MMSS_FABRIC_CFG_CLKMOD, + MSM_RPM_STATUS_ID_MM_FABRIC_CLOCK_MODE = + MSM_RPM_STATUS_ID_MMSS_FABRIC_CFG_CLKMOD, + MSM_RPM_STATUS_ID_MMSS_FABRIC_CFG_IOCTL, + MSM_RPM_STATUS_ID_MM_FABRIC_ARB, + MSM_RPM_STATUS_ID_PM8921_S1_0, + MSM_RPM_STATUS_ID_PM8921_S1_1, + MSM_RPM_STATUS_ID_PM8921_S2_0, + MSM_RPM_STATUS_ID_PM8921_S2_1, + MSM_RPM_STATUS_ID_PM8921_S3_0, + MSM_RPM_STATUS_ID_PM8921_S3_1, + MSM_RPM_STATUS_ID_PM8921_S4_0, + MSM_RPM_STATUS_ID_PM8921_S4_1, + MSM_RPM_STATUS_ID_PM8921_S5_0, + MSM_RPM_STATUS_ID_PM8921_S5_1, + MSM_RPM_STATUS_ID_PM8921_S6_0, + MSM_RPM_STATUS_ID_PM8921_S6_1, + MSM_RPM_STATUS_ID_PM8921_S7_0, + MSM_RPM_STATUS_ID_PM8921_S7_1, + MSM_RPM_STATUS_ID_PM8921_S8_0, + MSM_RPM_STATUS_ID_PM8921_S8_1, + MSM_RPM_STATUS_ID_PM8921_L1_0, + MSM_RPM_STATUS_ID_PM8921_L1_1, + MSM_RPM_STATUS_ID_PM8921_L2_0, + MSM_RPM_STATUS_ID_PM8921_L2_1, + MSM_RPM_STATUS_ID_PM8921_L3_0, + MSM_RPM_STATUS_ID_PM8921_L3_1, + MSM_RPM_STATUS_ID_PM8921_L4_0, + MSM_RPM_STATUS_ID_PM8921_L4_1, + MSM_RPM_STATUS_ID_PM8921_L5_0, + MSM_RPM_STATUS_ID_PM8921_L5_1, + MSM_RPM_STATUS_ID_PM8921_L6_0, + MSM_RPM_STATUS_ID_PM8921_L6_1, + MSM_RPM_STATUS_ID_PM8921_L7_0, + MSM_RPM_STATUS_ID_PM8921_L7_1, + MSM_RPM_STATUS_ID_PM8921_L8_0, + MSM_RPM_STATUS_ID_PM8921_L8_1, + MSM_RPM_STATUS_ID_PM8921_L9_0, + MSM_RPM_STATUS_ID_PM8921_L9_1, + MSM_RPM_STATUS_ID_PM8921_L10_0, + MSM_RPM_STATUS_ID_PM8921_L10_1, + MSM_RPM_STATUS_ID_PM8921_L11_0, + MSM_RPM_STATUS_ID_PM8921_L11_1, + MSM_RPM_STATUS_ID_PM8921_L12_0, + MSM_RPM_STATUS_ID_PM8921_L12_1, + MSM_RPM_STATUS_ID_PM8921_L13_0, + MSM_RPM_STATUS_ID_PM8921_L13_1, + MSM_RPM_STATUS_ID_PM8921_L14_0, + MSM_RPM_STATUS_ID_PM8921_L14_1, + MSM_RPM_STATUS_ID_PM8921_L15_0, + MSM_RPM_STATUS_ID_PM8921_L15_1, + MSM_RPM_STATUS_ID_PM8921_L16_0, + MSM_RPM_STATUS_ID_PM8921_L16_1, + MSM_RPM_STATUS_ID_PM8921_L17_0, + MSM_RPM_STATUS_ID_PM8921_L17_1, + MSM_RPM_STATUS_ID_PM8921_L18_0, + MSM_RPM_STATUS_ID_PM8921_L18_1, + MSM_RPM_STATUS_ID_PM8921_L19_0, + MSM_RPM_STATUS_ID_PM8921_L19_1, + MSM_RPM_STATUS_ID_PM8921_L20_0, + MSM_RPM_STATUS_ID_PM8921_L20_1, + MSM_RPM_STATUS_ID_PM8921_L21_0, + MSM_RPM_STATUS_ID_PM8921_L21_1, + MSM_RPM_STATUS_ID_PM8921_L22_0, + MSM_RPM_STATUS_ID_PM8921_L22_1, + MSM_RPM_STATUS_ID_PM8921_L23_0, + MSM_RPM_STATUS_ID_PM8921_L23_1, + MSM_RPM_STATUS_ID_PM8921_L24_0, + MSM_RPM_STATUS_ID_PM8921_L24_1, + MSM_RPM_STATUS_ID_PM8921_L25_0, + MSM_RPM_STATUS_ID_PM8921_L25_1, + MSM_RPM_STATUS_ID_PM8921_L26_0, + MSM_RPM_STATUS_ID_PM8921_L26_1, + MSM_RPM_STATUS_ID_PM8921_L27_0, + MSM_RPM_STATUS_ID_PM8921_L27_1, + MSM_RPM_STATUS_ID_PM8921_L28_0, + MSM_RPM_STATUS_ID_PM8921_L28_1, + MSM_RPM_STATUS_ID_PM8921_L29_0, + MSM_RPM_STATUS_ID_PM8921_L29_1, + MSM_RPM_STATUS_ID_PM8921_CLK1_0, + MSM_RPM_STATUS_ID_PM8921_CLK1_1, + MSM_RPM_STATUS_ID_PM8921_CLK2_0, + MSM_RPM_STATUS_ID_PM8921_CLK2_1, + MSM_RPM_STATUS_ID_PM8921_LVS1, + MSM_RPM_STATUS_ID_PM8921_LVS2, + MSM_RPM_STATUS_ID_PM8921_LVS3, + MSM_RPM_STATUS_ID_PM8921_LVS4, + MSM_RPM_STATUS_ID_PM8921_LVS5, + MSM_RPM_STATUS_ID_PM8921_LVS6, + MSM_RPM_STATUS_ID_PM8921_LVS7, + MSM_RPM_STATUS_ID_NCP_0, + MSM_RPM_STATUS_ID_NCP_1, + MSM_RPM_STATUS_ID_CXO_BUFFERS, + MSM_RPM_STATUS_ID_USB_OTG_SWITCH, + MSM_RPM_STATUS_ID_HDMI_SWITCH, + MSM_RPM_STATUS_ID_DDR_DMM_0, + MSM_RPM_STATUS_ID_DDR_DMM_1, + MSM_RPM_STATUS_ID_EBI1_CH0_RANGE, + MSM_RPM_STATUS_ID_EBI1_CH1_RANGE, + MSM_RPM_STATUS_ID_QDSS_CLK, + + /* 8660 Specific */ + MSM_RPM_STATUS_ID_PLL_4, + MSM_RPM_STATUS_ID_SMI_CLK, + MSM_RPM_STATUS_ID_APPS_L2_CACHE_CTL, + MSM_RPM_STATUS_ID_SMPS0B_0, + MSM_RPM_STATUS_ID_SMPS0B_1, + MSM_RPM_STATUS_ID_SMPS1B_0, + MSM_RPM_STATUS_ID_SMPS1B_1, + MSM_RPM_STATUS_ID_SMPS2B_0, + MSM_RPM_STATUS_ID_SMPS2B_1, + MSM_RPM_STATUS_ID_SMPS3B_0, + MSM_RPM_STATUS_ID_SMPS3B_1, + MSM_RPM_STATUS_ID_SMPS4B_0, + MSM_RPM_STATUS_ID_SMPS4B_1, + MSM_RPM_STATUS_ID_LDO0B_0, + MSM_RPM_STATUS_ID_LDO0B_1, + MSM_RPM_STATUS_ID_LDO1B_0, + MSM_RPM_STATUS_ID_LDO1B_1, + MSM_RPM_STATUS_ID_LDO2B_0, + MSM_RPM_STATUS_ID_LDO2B_1, + MSM_RPM_STATUS_ID_LDO3B_0, + MSM_RPM_STATUS_ID_LDO3B_1, + MSM_RPM_STATUS_ID_LDO4B_0, + MSM_RPM_STATUS_ID_LDO4B_1, + MSM_RPM_STATUS_ID_LDO5B_0, + MSM_RPM_STATUS_ID_LDO5B_1, + MSM_RPM_STATUS_ID_LDO6B_0, + MSM_RPM_STATUS_ID_LDO6B_1, + MSM_RPM_STATUS_ID_LVS0B, + MSM_RPM_STATUS_ID_LVS1B, + MSM_RPM_STATUS_ID_LVS2B, + MSM_RPM_STATUS_ID_LVS3B, + MSM_RPM_STATUS_ID_MVS, + MSM_RPM_STATUS_ID_SMPS0_0, + MSM_RPM_STATUS_ID_SMPS0_1, + MSM_RPM_STATUS_ID_SMPS1_0, + MSM_RPM_STATUS_ID_SMPS1_1, + MSM_RPM_STATUS_ID_SMPS2_0, + MSM_RPM_STATUS_ID_SMPS2_1, + MSM_RPM_STATUS_ID_SMPS3_0, + MSM_RPM_STATUS_ID_SMPS3_1, + MSM_RPM_STATUS_ID_SMPS4_0, + MSM_RPM_STATUS_ID_SMPS4_1, + MSM_RPM_STATUS_ID_LDO0_0, + MSM_RPM_STATUS_ID_LDO0_1, + MSM_RPM_STATUS_ID_LDO1_0, + MSM_RPM_STATUS_ID_LDO1_1, + MSM_RPM_STATUS_ID_LDO2_0, + MSM_RPM_STATUS_ID_LDO2_1, + MSM_RPM_STATUS_ID_LDO3_0, + MSM_RPM_STATUS_ID_LDO3_1, + MSM_RPM_STATUS_ID_LDO4_0, + MSM_RPM_STATUS_ID_LDO4_1, + MSM_RPM_STATUS_ID_LDO5_0, + MSM_RPM_STATUS_ID_LDO5_1, + MSM_RPM_STATUS_ID_LDO6_0, + MSM_RPM_STATUS_ID_LDO6_1, + MSM_RPM_STATUS_ID_LDO7_0, + MSM_RPM_STATUS_ID_LDO7_1, + MSM_RPM_STATUS_ID_LDO8_0, + MSM_RPM_STATUS_ID_LDO8_1, + MSM_RPM_STATUS_ID_LDO9_0, + MSM_RPM_STATUS_ID_LDO9_1, + MSM_RPM_STATUS_ID_LDO10_0, + MSM_RPM_STATUS_ID_LDO10_1, + MSM_RPM_STATUS_ID_LDO11_0, + MSM_RPM_STATUS_ID_LDO11_1, + MSM_RPM_STATUS_ID_LDO12_0, + MSM_RPM_STATUS_ID_LDO12_1, + MSM_RPM_STATUS_ID_LDO13_0, + MSM_RPM_STATUS_ID_LDO13_1, + MSM_RPM_STATUS_ID_LDO14_0, + MSM_RPM_STATUS_ID_LDO14_1, + MSM_RPM_STATUS_ID_LDO15_0, + MSM_RPM_STATUS_ID_LDO15_1, + MSM_RPM_STATUS_ID_LDO16_0, + MSM_RPM_STATUS_ID_LDO16_1, + MSM_RPM_STATUS_ID_LDO17_0, + MSM_RPM_STATUS_ID_LDO17_1, + MSM_RPM_STATUS_ID_LDO18_0, + MSM_RPM_STATUS_ID_LDO18_1, + MSM_RPM_STATUS_ID_LDO19_0, + MSM_RPM_STATUS_ID_LDO19_1, + MSM_RPM_STATUS_ID_LDO20_0, + MSM_RPM_STATUS_ID_LDO20_1, + MSM_RPM_STATUS_ID_LDO21_0, + MSM_RPM_STATUS_ID_LDO21_1, + MSM_RPM_STATUS_ID_LDO22_0, + MSM_RPM_STATUS_ID_LDO22_1, + MSM_RPM_STATUS_ID_LDO23_0, + MSM_RPM_STATUS_ID_LDO23_1, + MSM_RPM_STATUS_ID_LDO24_0, + MSM_RPM_STATUS_ID_LDO24_1, + MSM_RPM_STATUS_ID_LDO25_0, + MSM_RPM_STATUS_ID_LDO25_1, + MSM_RPM_STATUS_ID_LVS0, + MSM_RPM_STATUS_ID_LVS1, + + /* 9615 Specific */ + MSM_RPM_STATUS_ID_PM8018_S1_0, + MSM_RPM_STATUS_ID_PM8018_S1_1, + MSM_RPM_STATUS_ID_PM8018_S2_0, + MSM_RPM_STATUS_ID_PM8018_S2_1, + MSM_RPM_STATUS_ID_PM8018_S3_0, + MSM_RPM_STATUS_ID_PM8018_S3_1, + MSM_RPM_STATUS_ID_PM8018_S4_0, + MSM_RPM_STATUS_ID_PM8018_S4_1, + MSM_RPM_STATUS_ID_PM8018_S5_0, + MSM_RPM_STATUS_ID_PM8018_S5_1, + MSM_RPM_STATUS_ID_PM8018_L1_0, + MSM_RPM_STATUS_ID_PM8018_L1_1, + MSM_RPM_STATUS_ID_PM8018_L2_0, + MSM_RPM_STATUS_ID_PM8018_L2_1, + MSM_RPM_STATUS_ID_PM8018_L3_0, + MSM_RPM_STATUS_ID_PM8018_L3_1, + MSM_RPM_STATUS_ID_PM8018_L4_0, + MSM_RPM_STATUS_ID_PM8018_L4_1, + MSM_RPM_STATUS_ID_PM8018_L5_0, + MSM_RPM_STATUS_ID_PM8018_L5_1, + MSM_RPM_STATUS_ID_PM8018_L6_0, + MSM_RPM_STATUS_ID_PM8018_L6_1, + MSM_RPM_STATUS_ID_PM8018_L7_0, + MSM_RPM_STATUS_ID_PM8018_L7_1, + MSM_RPM_STATUS_ID_PM8018_L8_0, + MSM_RPM_STATUS_ID_PM8018_L8_1, + MSM_RPM_STATUS_ID_PM8018_L9_0, + MSM_RPM_STATUS_ID_PM8018_L9_1, + MSM_RPM_STATUS_ID_PM8018_L10_0, + MSM_RPM_STATUS_ID_PM8018_L10_1, + MSM_RPM_STATUS_ID_PM8018_L11_0, + MSM_RPM_STATUS_ID_PM8018_L11_1, + MSM_RPM_STATUS_ID_PM8018_L12_0, + MSM_RPM_STATUS_ID_PM8018_L12_1, + MSM_RPM_STATUS_ID_PM8018_L13_0, + MSM_RPM_STATUS_ID_PM8018_L13_1, + MSM_RPM_STATUS_ID_PM8018_L14_0, + MSM_RPM_STATUS_ID_PM8018_L14_1, + MSM_RPM_STATUS_ID_PM8018_LVS1, + + /* 8930 specific */ + MSM_RPM_STATUS_ID_PM8038_S1_0, + MSM_RPM_STATUS_ID_PM8038_S1_1, + MSM_RPM_STATUS_ID_PM8038_S2_0, + MSM_RPM_STATUS_ID_PM8038_S2_1, + MSM_RPM_STATUS_ID_PM8038_S3_0, + MSM_RPM_STATUS_ID_PM8038_S3_1, + MSM_RPM_STATUS_ID_PM8038_S4_0, + MSM_RPM_STATUS_ID_PM8038_S4_1, + MSM_RPM_STATUS_ID_PM8038_S5_0, + MSM_RPM_STATUS_ID_PM8038_S5_1, + MSM_RPM_STATUS_ID_PM8038_S6_0, + MSM_RPM_STATUS_ID_PM8038_S6_1, + MSM_RPM_STATUS_ID_PM8038_L1_0, + MSM_RPM_STATUS_ID_PM8038_L1_1, + MSM_RPM_STATUS_ID_PM8038_L2_0, + MSM_RPM_STATUS_ID_PM8038_L2_1, + MSM_RPM_STATUS_ID_PM8038_L3_0, + MSM_RPM_STATUS_ID_PM8038_L3_1, + MSM_RPM_STATUS_ID_PM8038_L4_0, + MSM_RPM_STATUS_ID_PM8038_L4_1, + MSM_RPM_STATUS_ID_PM8038_L5_0, + MSM_RPM_STATUS_ID_PM8038_L5_1, + MSM_RPM_STATUS_ID_PM8038_L6_0, + MSM_RPM_STATUS_ID_PM8038_L6_1, + MSM_RPM_STATUS_ID_PM8038_L7_0, + MSM_RPM_STATUS_ID_PM8038_L7_1, + MSM_RPM_STATUS_ID_PM8038_L8_0, + MSM_RPM_STATUS_ID_PM8038_L8_1, + MSM_RPM_STATUS_ID_PM8038_L9_0, + MSM_RPM_STATUS_ID_PM8038_L9_1, + MSM_RPM_STATUS_ID_PM8038_L10_0, + MSM_RPM_STATUS_ID_PM8038_L10_1, + MSM_RPM_STATUS_ID_PM8038_L11_0, + MSM_RPM_STATUS_ID_PM8038_L11_1, + MSM_RPM_STATUS_ID_PM8038_L12_0, + MSM_RPM_STATUS_ID_PM8038_L12_1, + MSM_RPM_STATUS_ID_PM8038_L13_0, + MSM_RPM_STATUS_ID_PM8038_L13_1, + MSM_RPM_STATUS_ID_PM8038_L14_0, + MSM_RPM_STATUS_ID_PM8038_L14_1, + MSM_RPM_STATUS_ID_PM8038_L15_0, + MSM_RPM_STATUS_ID_PM8038_L15_1, + MSM_RPM_STATUS_ID_PM8038_L16_0, + MSM_RPM_STATUS_ID_PM8038_L16_1, + MSM_RPM_STATUS_ID_PM8038_L17_0, + MSM_RPM_STATUS_ID_PM8038_L17_1, + MSM_RPM_STATUS_ID_PM8038_L18_0, + MSM_RPM_STATUS_ID_PM8038_L18_1, + MSM_RPM_STATUS_ID_PM8038_L19_0, + MSM_RPM_STATUS_ID_PM8038_L19_1, + MSM_RPM_STATUS_ID_PM8038_L20_0, + MSM_RPM_STATUS_ID_PM8038_L20_1, + MSM_RPM_STATUS_ID_PM8038_L21_0, + MSM_RPM_STATUS_ID_PM8038_L21_1, + MSM_RPM_STATUS_ID_PM8038_L22_0, + MSM_RPM_STATUS_ID_PM8038_L22_1, + MSM_RPM_STATUS_ID_PM8038_L23_0, + MSM_RPM_STATUS_ID_PM8038_L23_1, + MSM_RPM_STATUS_ID_PM8038_L24_0, + MSM_RPM_STATUS_ID_PM8038_L24_1, + MSM_RPM_STATUS_ID_PM8038_L25_0, + MSM_RPM_STATUS_ID_PM8038_L25_1, + MSM_RPM_STATUS_ID_PM8038_L26_0, + MSM_RPM_STATUS_ID_PM8038_L26_1, + MSM_RPM_STATUS_ID_PM8038_L27_0, + MSM_RPM_STATUS_ID_PM8038_L27_1, + MSM_RPM_STATUS_ID_PM8038_CLK1_0, + MSM_RPM_STATUS_ID_PM8038_CLK1_1, + MSM_RPM_STATUS_ID_PM8038_CLK2_0, + MSM_RPM_STATUS_ID_PM8038_CLK2_1, + MSM_RPM_STATUS_ID_PM8038_LVS1, + MSM_RPM_STATUS_ID_PM8038_LVS2, + MSM_RPM_STATUS_ID_VOLTAGE_CORNER, + + /* 8064 specific */ + MSM_RPM_STATUS_ID_PM8821_S1_0, + MSM_RPM_STATUS_ID_PM8821_S1_1, + MSM_RPM_STATUS_ID_PM8821_S2_0, + MSM_RPM_STATUS_ID_PM8821_S2_1, + MSM_RPM_STATUS_ID_PM8821_L1_0, + MSM_RPM_STATUS_ID_PM8821_L1_1, + + MSM_RPM_STATUS_ID_LAST, +}; + +static inline uint32_t msm_rpm_get_ctx_mask(unsigned int ctx) +{ + return 1UL << ctx; +} + +static inline unsigned int msm_rpm_get_sel_mask_reg(unsigned int sel) +{ + return sel / 32; +} + +static inline uint32_t msm_rpm_get_sel_mask(unsigned int sel) +{ + return 1UL << (sel % 32); +} + +struct msm_rpm_iv_pair { + uint32_t id; + uint32_t value; +}; + +struct msm_rpm_notification { + struct list_head list; /* reserved for RPM use */ + struct semaphore sem; + uint32_t sel_masks[SEL_MASK_SIZE]; /* reserved for RPM use */ +}; + +struct msm_rpm_map_data { + uint32_t id; + uint32_t sel; + uint32_t count; +}; + +#define MSM_RPM_MAP(t, i, s, c) \ + [MSM_RPM_ID_##i] = \ + {\ + .id = MSM_RPM_##t##_ID_##i, \ + .sel = MSM_RPM_##t##_SEL_##s, \ + .count = c, \ + } + +#define MSM_RPM_STATUS_ID_VALID BIT(31) + +#define MSM_RPM_STATUS_ID_MAP(t, i) \ + [MSM_RPM_STATUS_ID_## i] = (MSM_RPM_##t##_STATUS_ID_##i \ + | MSM_RPM_STATUS_ID_VALID) + +#define MSM_RPM_CTRL_MAP(t, i) \ + [MSM_RPM_CTRL_##i] = MSM_RPM_##t##_CTRL_##i + + +struct msm_rpm_platform_data { + void __iomem *reg_base_addrs[MSM_RPM_PAGE_COUNT]; + unsigned int irq_ack; + unsigned int irq_err; + unsigned int irq_wakeup; + void *ipc_rpm_reg; + unsigned int ipc_rpm_val; + struct msm_rpm_map_data target_id[MSM_RPM_ID_LAST]; + unsigned int target_status[MSM_RPM_STATUS_ID_LAST]; + unsigned int target_ctrl_id[MSM_RPM_CTRL_LAST]; + unsigned int sel_invalidate, sel_notification, sel_last; + unsigned int ver[3]; +}; + +extern struct msm_rpm_platform_data msm8660_rpm_data; +extern struct msm_rpm_platform_data msm8960_rpm_data; +extern struct msm_rpm_platform_data msm9615_rpm_data; +extern struct msm_rpm_platform_data msm8930_rpm_data; +extern struct msm_rpm_platform_data apq8064_rpm_data; + +int msm_rpm_local_request_is_outstanding(void); +int msm_rpm_get_status(struct msm_rpm_iv_pair *status, int count); +int msm_rpm_set(int ctx, struct msm_rpm_iv_pair *req, int count); +int msm_rpm_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count); + +static inline int msm_rpm_set_nosleep( + int ctx, struct msm_rpm_iv_pair *req, int count) +{ + unsigned long flags; + int rc; + + local_irq_save(flags); + rc = msm_rpm_set_noirq(ctx, req, count); + local_irq_restore(flags); + + return rc; +} + +int msm_rpm_clear(int ctx, struct msm_rpm_iv_pair *req, int count); +int msm_rpm_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count); + +static inline int msm_rpm_clear_nosleep( + int ctx, struct msm_rpm_iv_pair *req, int count) +{ + unsigned long flags; + int rc; + + local_irq_save(flags); + rc = msm_rpm_clear_noirq(ctx, req, count); + local_irq_restore(flags); + + return rc; +} + +int msm_rpm_register_notification(struct msm_rpm_notification *n, + struct msm_rpm_iv_pair *req, int count); +int msm_rpm_unregister_notification(struct msm_rpm_notification *n); +int msm_rpm_init(struct msm_rpm_platform_data *data); + +#endif /* __ARCH_ARM_MACH_MSM_RPM_H */ diff --git a/arch/arm/mach-msm/include/mach/scm-io.h b/arch/arm/mach-msm/include/mach/scm-io.h new file mode 100644 index 00000000000..5393da1020d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/scm-io.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MACH_SCM_IO_H +#define __MACH_SCM_IO_H + +#include + +#ifdef CONFIG_MSM_SECURE_IO + +extern u32 secure_readl(void __iomem *c); +extern void secure_writel(u32 v, void __iomem *c); + +#else + +#define secure_readl(c) readl(c) +#define secure_writel(v, c) writel(v, c) + +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/scm.h b/arch/arm/mach-msm/include/mach/scm.h new file mode 100644 index 00000000000..7cc5f7a5acd --- /dev/null +++ b/arch/arm/mach-msm/include/mach/scm.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MACH_SCM_H +#define __MACH_SCM_H + +#define SCM_SVC_BOOT 0x1 +#define SCM_SVC_PIL 0x2 +#define SCM_SVC_UTIL 0x3 +#define SCM_SVC_TZ 0x4 +#define SCM_SVC_IO 0x5 +#define SCM_SVC_INFO 0x6 +#define SCM_SVC_SSD 0x7 +#define SCM_SVC_FUSE 0x8 +#define SCM_SVC_PWR 0x9 +#define SCM_SVC_CP 0xC +#define SCM_SVC_DCVS 0xD +#define SCM_SVC_TZSCHEDULER 0xFC + +#ifdef CONFIG_MSM_SCM +extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, + void *resp_buf, size_t resp_len); + +extern s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1); +extern s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2); +extern s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, u32 arg3, + u32 arg4, u32 *ret1, u32 *ret2); + +#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) + +extern u32 scm_get_version(void); +extern int scm_is_call_available(u32 svc_id, u32 cmd_id); +extern int scm_get_feat_version(u32 feat); + +#else + +static inline int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len) +{ + return 0; +} + +static inline s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + return 0; +} + +static inline s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) +{ + return 0; +} + +static inline s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 *ret1, u32 *ret2) +{ + return 0; +} + +static inline u32 scm_get_version(void) +{ + return 0; +} + +static inline int scm_is_call_available(u32 svc_id, u32 cmd_id) +{ + return 0; +} + +static inline int scm_get_feat_version(u32 feat) +{ + return 0; +} + +#endif +#endif diff --git a/arch/arm/mach-msm/include/mach/sdio_al.h b/arch/arm/mach-msm/include/mach/sdio_al.h new file mode 100644 index 00000000000..8b8ee5a6363 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/sdio_al.h @@ -0,0 +1,153 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SDIO-Abstraction-Layer API. + */ + +#ifndef __SDIO_AL__ +#define __SDIO_AL__ + +#include + +struct sdio_channel; /* Forward Declaration */ + + +/** + * Channel Events. + * Available bytes notification. + */ +#define SDIO_EVENT_DATA_READ_AVAIL 0x01 +#define SDIO_EVENT_DATA_WRITE_AVAIL 0x02 + +#ifdef CONFIG_MSM_SDIO_AL + +struct sdio_al_platform_data { + int (*config_mdm2ap_status)(int); + int (*get_mdm2ap_status)(void); + int allow_sdioc_version_major_2; + int peer_sdioc_version_minor; + int peer_sdioc_version_major; + int peer_sdioc_boot_version_minor; + int peer_sdioc_boot_version_major; +}; + +/** + * sdio_open - open a channel for read/write data. + * + * @name: channel name - identify the channel to open. + * @ch: channel handle returned. + * @priv: caller private context pointer, passed to the notify callback. + * @notify: notification callback for data available. + * @channel_event: SDIO_EVENT_DATA_READ_AVAIL or SDIO_EVENT_DATA_WRITE_AVAIL + * @return 0 on success, negative value on error. + * + * Warning: notify() may be called before open returns. + */ +int sdio_open(const char *name, struct sdio_channel **ch, void *priv, + void (*notify)(void *priv, unsigned channel_event)); + + +/** + * sdio_close - close a channel. + * + * @ch: channel handle. + * @return 0 on success, negative value on error. + */ +int sdio_close(struct sdio_channel *ch); + +/** + * sdio_read - synchronous read. + * + * @ch: channel handle. + * @data: caller buffer pointer. should be non-cacheable. + * @len: byte count. + * @return 0 on success, negative value on error. + * + * May wait if no available bytes. + * May wait if other channel with higher priority has pending + * transfers. + * Client should check available bytes prior to calling this + * api. + */ +int sdio_read(struct sdio_channel *ch, void *data, int len); + +/** + * sdio_write - synchronous write. + * + * @ch: channel handle. + * @data: caller buffer pointer. should be non-cacheable. + * @len: byte count. + * @return 0 on success, negative value on error. + * + * May wait if no available bytes. + * May wait if other channel with higher priority has pending + * transfers. + * Client should check available bytes prior to calling this + * api. + */ +int sdio_write(struct sdio_channel *ch, const void *data, int len); + +/** + * sdio_write_avail - get available bytes to write. + * + * @ch: channel handle. + * @return byte count on success, negative value on error. + */ +int sdio_write_avail(struct sdio_channel *ch); + +/** + * sdio_read_avail - get available bytes to read. + * + * @ch: channel handle. + * @return byte count on success, negative value on error. + */ +int sdio_read_avail(struct sdio_channel *ch); + +#else + +static int __maybe_unused sdio_open(const char *name, struct sdio_channel **ch, + void *priv, void (*notify)(void *priv, unsigned channel_event)) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_close(struct sdio_channel *ch) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_read(struct sdio_channel *ch, void *data, + int len) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_write(struct sdio_channel *ch, const void *data, + int len) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_write_avail(struct sdio_channel *ch) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_read_avail(struct sdio_channel *ch) +{ + return -ENODEV; +} +#endif + +#endif /* __SDIO_AL__ */ diff --git a/arch/arm/mach-msm/include/mach/sdio_cmux.h b/arch/arm/mach-msm/include/mach/sdio_cmux.h new file mode 100644 index 00000000000..4bcd607896c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/sdio_cmux.h @@ -0,0 +1,146 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SDIO CMUX API + */ + +#ifndef __SDIO_CMUX__ +#define __SDIO_CMUX__ + +#ifdef CONFIG_MSM_SDIO_CMUX + +enum { + SDIO_CMUX_DATA_CTL_0, + SDIO_CMUX_DATA_CTL_1, + SDIO_CMUX_DATA_CTL_2, + SDIO_CMUX_DATA_CTL_3, + SDIO_CMUX_DATA_CTL_4, + SDIO_CMUX_DATA_CTL_5, + SDIO_CMUX_DATA_CTL_6, + SDIO_CMUX_DATA_CTL_7, + SDIO_CMUX_USB_CTL_0, + SDIO_CMUX_USB_DUN_CTL_0, + SDIO_CMUX_CSVT_CTL_0, + SDIO_CMUX_NUM_CHANNELS +}; + + +/* + * sdio_cmux_open - Open the mux channel + * + * @id: Mux Channel id to be opened + * @receive_cb: Notification when data arrives. Parameters are data received, + * size of data, private context pointer. + * @write_done: Notification when data is written. Parameters are data written, + * size of data, private context pointer. Please note that the data + * written pointer will always be NULL as the cmux makes an internal copy + * of the data. + * @priv: caller's private context pointer + */ +int sdio_cmux_open(const int id, + void (*receive_cb)(void *, int, void *), + void (*write_done)(void *, int, void *), + void (*status_callback)(int, void *), + void *priv); + +/* + * sdio_cmux_close - Close the mux channel + * + * @id: Channel id to be closed + */ +int sdio_cmux_close(int id); + +/* + * sdio_cmux_write_avail - Write space avaialable for this channel + * + * @id: Channel id to look for the available write space + */ +int sdio_cmux_write_avail(int id); + +/* + * sdio_cmux_write - Write the data onto the CMUX channel + * + * @id: Channel id onto which the data has to be written + * @data: Starting address of the data buffer to be written + * @len: Length of the data to be written + */ +int sdio_cmux_write(int id, void *data, int len); + +/* these are used to get and set the IF sigs of a channel. + * DTR and RTS can be set; DSR, CTS, CD and RI can be read. + */ +int sdio_cmux_tiocmget(int id); +int sdio_cmux_tiocmset(int id, unsigned int set, unsigned int clear); + +/* + * is_remote_open - Check whether the remote channel is open + * + * @id: Channel id to be checked + */ +int is_remote_open(int id); + +/* + * sdio_cmux_is_channel_reset - Check whether the channel is in reset state + * + * @id: Channel id to be checked + */ +int sdio_cmux_is_channel_reset(int id); + +#else + +static int __maybe_unused sdio_cmux_open(const int id, + void (*receive_cb)(void *, int, void *), + void (*write_done)(void *, int, void *), + void (*status_callback)(int, void *), + void *priv) +{ + return -ENODEV; +} +static int __maybe_unused sdio_cmux_close(int id) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_cmux_write_avail(int id) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_cmux_write(int id, void *data, int len) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_cmux_tiocmget(int id) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_cmux_tiocmset(int id, unsigned int set, + unsigned int clear) +{ + return -ENODEV; +} + +static int __maybe_unused is_remote_open(int id) +{ + return -ENODEV; +} + +static int __maybe_unused sdio_cmux_is_channel_reset(int id) +{ + return -ENODEV; +} +#endif +#endif /* __SDIO_CMUX__ */ diff --git a/arch/arm/mach-msm/include/mach/sdio_dmux.h b/arch/arm/mach-msm/include/mach/sdio_dmux.h new file mode 100644 index 00000000000..afc1b11c825 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/sdio_dmux.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#ifndef _SDIO_DMUX_H +#define _SDIO_DMUX_H + +#ifdef CONFIG_MSM_SDIO_DMUX +enum { + SDIO_DMUX_DATA_RMNET_0, + SDIO_DMUX_DATA_RMNET_1, + SDIO_DMUX_DATA_RMNET_2, + SDIO_DMUX_DATA_RMNET_3, + SDIO_DMUX_DATA_RMNET_4, + SDIO_DMUX_DATA_RMNET_5, + SDIO_DMUX_DATA_RMNET_6, + SDIO_DMUX_DATA_RMNET_7, + SDIO_DMUX_USB_RMNET_0, + SDIO_DMUX_NUM_CHANNELS +}; + +int msm_sdio_dmux_open(uint32_t id, void *priv, + void (*receive_cb)(void *, struct sk_buff *), + void (*write_done)(void *, struct sk_buff *)); + +int msm_sdio_is_channel_in_reset(uint32_t id); + +int msm_sdio_dmux_close(uint32_t id); + +int msm_sdio_dmux_write(uint32_t id, struct sk_buff *skb); + +int msm_sdio_dmux_is_ch_full(uint32_t id); + +int msm_sdio_dmux_is_ch_low(uint32_t id); + +#else + +static int __maybe_unused msm_sdio_dmux_open(uint32_t id, void *priv, + void (*receive_cb)(void *, struct sk_buff *), + void (*write_done)(void *, struct sk_buff *)) +{ + return -ENODEV; +} + +static int __maybe_unused msm_sdio_is_channel_in_reset(uint32_t id) +{ + return -ENODEV; +} + +static int __maybe_unused msm_sdio_dmux_close(uint32_t id) +{ + return -ENODEV; +} + +static int __maybe_unused msm_sdio_dmux_write(uint32_t id, struct sk_buff *skb) +{ + return -ENODEV; +} + +static int __maybe_unused msm_sdio_dmux_is_ch_full(uint32_t id) +{ + return -ENODEV; +} + +static int __maybe_unused msm_sdio_dmux_is_ch_low(uint32_t id) +{ + return -ENODEV; +} + +#endif + +#endif /* _SDIO_DMUX_H */ diff --git a/arch/arm/mach-msm/include/mach/sdio_smem.h b/arch/arm/mach-msm/include/mach/sdio_smem.h new file mode 100644 index 00000000000..b47001f09b1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/sdio_smem.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SDIO_SMEM_H +#define __SDIO_SMEM_H + +#include +#include + +#define SDIO_SMEM_EVENT_READ_DONE 0 +#define SDIO_SMEM_EVENT_READ_ERR 1 + +int sdio_smem_register_client(void); +int sdio_smem_unregister_client(void); + +struct sdio_smem_client { + void *buf; + int size; + struct platform_device plat_dev; + int (*cb_func)(int event); +}; + +#endif /* __SDIO_SMEM_H */ diff --git a/arch/arm/mach-msm/include/mach/sirc-fsm9xxx.h b/arch/arm/mach-msm/include/mach/sirc-fsm9xxx.h new file mode 100644 index 00000000000..b86221103da --- /dev/null +++ b/arch/arm/mach-msm/include/mach/sirc-fsm9xxx.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_SIRC_FSM9XXX_H +#define __ASM_ARCH_MSM_SIRC_FSM9XXX_H + +/* Group A */ +#define INT_EBI2_WR_ER_DONE (FIRST_SIRC_IRQ + 0) +#define INT_EBI2_OP_DONE (FIRST_SIRC_IRQ + 1) +#define INT_SDC1_0 (FIRST_SIRC_IRQ + 2) +#define INT_SDC1_1 (FIRST_SIRC_IRQ + 3) +#define INT_UARTDM (FIRST_SIRC_IRQ + 4) +#define INT_UART1 (FIRST_SIRC_IRQ + 5) +/* RESERVED 6 */ +#define INT_CE (FIRST_SIRC_IRQ + 7) +#define INT_SYS_ENZO_IEQ (FIRST_SIRC_IRQ + 8) +#define INT_PERPH_ENZO (FIRST_SIRC_IRQ + 9) +#define INT_MXBAR_ENZO (FIRST_SIRC_IRQ + 10) +#define INT_AXIAGG_ENZO (FIRST_SIRC_IRQ + 11) +#define INT_UART3 (FIRST_SIRC_IRQ + 12) +#define INT_UART2 (FIRST_SIRC_IRQ + 13) +#define INT_PORT0_SSBI2 (FIRST_SIRC_IRQ + 14) +#define INT_PORT1_SSBI2 (FIRST_SIRC_IRQ + 15) +#define INT_PORT2_SSBI2 (FIRST_SIRC_IRQ + 16) +#define INT_PORT3_SSBI2 (FIRST_SIRC_IRQ + 17) +#define INT_GSBI_QUP_INBUF (FIRST_SIRC_IRQ + 18) +#define INT_GSBI_QUP_OUTBUF (FIRST_SIRC_IRQ + 19) +#define INT_GSBI_QUP_ERROR (FIRST_SIRC_IRQ + 20) +#define INT_SPB_DECODER (FIRST_SIRC_IRQ + 21) +#define INT_FPB_DEC (FIRST_SIRC_IRQ + 22) +#define INT_BPM_HW (FIRST_SIRC_IRQ + 23) +#define INT_GPIO_167 (FIRST_SIRC_IRQ + 24) + +/* Group B */ +#define INT_GPIO_166 (FIRST_SIRC_IRQ + 25) +#define INT_GPIO_165 (FIRST_SIRC_IRQ + 26) +#define INT_GPIO_164 (FIRST_SIRC_IRQ + 27) +#define INT_GPIO_163 (FIRST_SIRC_IRQ + 28) +#define INT_GPIO_162 (FIRST_SIRC_IRQ + 29) +#define INT_GPIO_161 (FIRST_SIRC_IRQ + 30) +#define INT_GPIO_160 (FIRST_SIRC_IRQ + 31) +#define INT_GPIO_159 (FIRST_SIRC_IRQ + 32) +#define INT_GPIO_158 (FIRST_SIRC_IRQ + 33) +#define INT_GPIO_157 (FIRST_SIRC_IRQ + 34) +#define INT_GPIO_156 (FIRST_SIRC_IRQ + 35) +#define INT_GPIO_155 (FIRST_SIRC_IRQ + 36) +#define INT_GPIO_154 (FIRST_SIRC_IRQ + 37) +#define INT_GPIO_153 (FIRST_SIRC_IRQ + 38) +#define INT_GPIO_152 (FIRST_SIRC_IRQ + 39) +#define INT_GPIO_151 (FIRST_SIRC_IRQ + 40) +#define INT_GPIO_150 (FIRST_SIRC_IRQ + 41) +#define INT_GPIO_149 (FIRST_SIRC_IRQ + 42) +#define INT_GPIO_148 (FIRST_SIRC_IRQ + 43) +#define INT_GPIO_147 (FIRST_SIRC_IRQ + 44) +#define INT_GPIO_146 (FIRST_SIRC_IRQ + 45) +#define INT_GPIO_145 (FIRST_SIRC_IRQ + 46) +#define INT_GPIO_144 (FIRST_SIRC_IRQ + 47) +/* RESERVED 48 */ + +#define NR_SIRC_IRQS_GROUPA 25 +#define NR_SIRC_IRQS_GROUPB 24 +#define NR_SIRC_IRQS 49 +#define SIRC_MASK_GROUPA 0x01ffffff +#define SIRC_MASK_GROUPB 0x00ffffff + +#define SPSS_SIRC_INT_CLEAR (MSM_SIRC_BASE + 0x00) +#define SPSS_SIRC_INT_POLARITY (MSM_SIRC_BASE + 0x5C) +#define SPSS_SIRC_INT_SET (MSM_SIRC_BASE + 0x18) +#define SPSS_SIRC_INT_ENABLE (MSM_SIRC_BASE + 0x20) +#define SPSS_SIRC_IRQ_STATUS (MSM_SIRC_BASE + 0x38) +#define SPSS_SIRC_INT_TYPE (MSM_SIRC_BASE + 0x30) +#define SPSS_SIRC_VEC_INDEX_RD (MSM_SIRC_BASE + 0x48) + +#endif /* __ASM_ARCH_MSM_SIRC_FSM9XXX_H */ diff --git a/arch/arm/mach-msm/include/mach/sirc.h b/arch/arm/mach-msm/include/mach/sirc.h index ef55868a5b8..607bab5da5b 100644 --- a/arch/arm/mach-msm/include/mach/sirc.h +++ b/arch/arm/mach-msm/include/mach/sirc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,13 +25,12 @@ struct sirc_regs_t { struct sirc_cascade_regs { void *int_status; unsigned int cascade_irq; + unsigned int cascade_fiq; }; void msm_init_sirc(void); -void msm_sirc_enter_sleep(void); -void msm_sirc_exit_sleep(void); -#if defined(CONFIG_ARCH_MSM_SCORPION) +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) #include @@ -41,6 +40,10 @@ void msm_sirc_exit_sleep(void); #define FIRST_SIRC_IRQ (NR_MSM_IRQS + NR_GPIO_IRQS) +#if defined(CONFIG_ARCH_FSM9XXX) +#include +#else /* CONFIG_ARCH_FSM9XXX */ + #define INT_UART1 (FIRST_SIRC_IRQ + 0) #define INT_UART2 (FIRST_SIRC_IRQ + 1) #define INT_UART3 (FIRST_SIRC_IRQ + 2) @@ -78,8 +81,6 @@ void msm_sirc_exit_sleep(void); #define SIRC_MASK 0x007FFFFF #endif -#define LAST_SIRC_IRQ (FIRST_SIRC_IRQ + NR_SIRC_IRQS - 1) - #define SPSS_SIRC_INT_SELECT (MSM_SIRC_BASE + 0x00) #define SPSS_SIRC_INT_ENABLE (MSM_SIRC_BASE + 0x04) #define SPSS_SIRC_INT_ENABLE_CLEAR (MSM_SIRC_BASE + 0x08) @@ -93,6 +94,10 @@ void msm_sirc_exit_sleep(void); #define SPSS_SIRC_INT_CLEAR (MSM_SIRC_BASE + 0x28) #define SPSS_SIRC_SOFT_INT (MSM_SIRC_BASE + 0x2C) -#endif +#endif /* CONFIG_ARCH_FSM9XXX */ + +#define LAST_SIRC_IRQ (FIRST_SIRC_IRQ + NR_SIRC_IRQS - 1) + +#endif /* CONFIG_ARCH_MSM_SCORPION */ #endif diff --git a/arch/arm/mach-msm/include/mach/smem_log.h b/arch/arm/mach-msm/include/mach/smem_log.h new file mode 100644 index 00000000000..a94ae76a5a6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/smem_log.h @@ -0,0 +1,231 @@ +/* Copyright (c) 2008-2009, 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#define SMEM_LOG_BASE 0x30 + +#define SMIOC_SETMODE _IOW(SMEM_LOG_BASE, 1, int) +#define SMIOC_SETLOG _IOW(SMEM_LOG_BASE, 2, int) + +#define SMIOC_TEXT 0x00000001 +#define SMIOC_BINARY 0x00000002 +#define SMIOC_LOG 0x00000003 +#define SMIOC_STATIC_LOG 0x00000004 + +/* Event indentifier format: + * bit 31-28 is processor ID 8 => apps, 4 => Q6, 0 => modem + * bits 27-16 are subsystem id (event base) + * bits 15-0 are event id + */ + +#define PROC 0xF0000000 +#define SUB 0x0FFF0000 +#define ID 0x0000FFFF + +#define SMEM_LOG_PROC_ID_MODEM 0x00000000 +#define SMEM_LOG_PROC_ID_Q6 0x40000000 +#define SMEM_LOG_PROC_ID_APPS 0x80000000 +#define SMEM_LOG_PROC_ID_WCNSS 0xC0000000 + +#define SMEM_LOG_CONT 0x10000000 + +#define SMEM_LOG_DEBUG_EVENT_BASE 0x00000000 +#define SMEM_LOG_ONCRPC_EVENT_BASE 0x00010000 +#define SMEM_LOG_SMEM_EVENT_BASE 0x00020000 +#define SMEM_LOG_TMC_EVENT_BASE 0x00030000 +#define SMEM_LOG_TIMETICK_EVENT_BASE 0x00040000 +#define SMEM_LOG_DEM_EVENT_BASE 0x00050000 +#define SMEM_LOG_ERROR_EVENT_BASE 0x00060000 +#define SMEM_LOG_DCVS_EVENT_BASE 0x00070000 +#define SMEM_LOG_SLEEP_EVENT_BASE 0x00080000 +#define SMEM_LOG_RPC_ROUTER_EVENT_BASE 0x00090000 +#if defined(CONFIG_MSM_N_WAY_SMSM) +#define DEM_SMSM_ISR (SMEM_LOG_DEM_EVENT_BASE + 0x1) +#define DEM_STATE_CHANGE (SMEM_LOG_DEM_EVENT_BASE + 0x2) +#define DEM_STATE_MACHINE_ENTER (SMEM_LOG_DEM_EVENT_BASE + 0x3) +#define DEM_ENTER_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x4) +#define DEM_END_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x5) +#define DEM_SETUP_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x6) +#define DEM_SETUP_POWER_COLLAPSE (SMEM_LOG_DEM_EVENT_BASE + 0x7) +#define DEM_SETUP_SUSPEND (SMEM_LOG_DEM_EVENT_BASE + 0x8) +#define DEM_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 0x9) +#define DEM_WAKEUP_REASON (SMEM_LOG_DEM_EVENT_BASE + 0xA) +#define DEM_DETECT_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0xB) +#define DEM_DETECT_RESET (SMEM_LOG_DEM_EVENT_BASE + 0xC) +#define DEM_DETECT_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 0xD) +#define DEM_DETECT_RUN (SMEM_LOG_DEM_EVENT_BASE + 0xE) +#define DEM_APPS_SWFI (SMEM_LOG_DEM_EVENT_BASE + 0xF) +#define DEM_SEND_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0x10) +#define DEM_ASSERT_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x11) +#define DEM_NEGATE_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x12) +#define DEM_PROC_COMM_CMD (SMEM_LOG_DEM_EVENT_BASE + 0x13) +#define DEM_REMOVE_PROC_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x14) +#define DEM_RESTORE_PROC_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x15) +#define DEM_SMI_CLK_DISABLED (SMEM_LOG_DEM_EVENT_BASE + 0x16) +#define DEM_SMI_CLK_ENABLED (SMEM_LOG_DEM_EVENT_BASE + 0x17) +#define DEM_MAO_INTS (SMEM_LOG_DEM_EVENT_BASE + 0x18) +#define DEM_APPS_WAKEUP_INT (SMEM_LOG_DEM_EVENT_BASE + 0x19) +#define DEM_PROC_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0x1A) +#define DEM_PROC_POWERUP (SMEM_LOG_DEM_EVENT_BASE + 0x1B) +#define DEM_TIMER_EXPIRED (SMEM_LOG_DEM_EVENT_BASE + 0x1C) +#define DEM_SEND_BATTERY_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x1D) +#define DEM_REMOTE_PWR_CB (SMEM_LOG_DEM_EVENT_BASE + 0x24) +#define DEM_TIME_SYNC_START (SMEM_LOG_DEM_EVENT_BASE + 0x1E) +#define DEM_TIME_SYNC_SEND_VALUE (SMEM_LOG_DEM_EVENT_BASE + 0x1F) +#define DEM_TIME_SYNC_DONE (SMEM_LOG_DEM_EVENT_BASE + 0x20) +#define DEM_TIME_SYNC_REQUEST (SMEM_LOG_DEM_EVENT_BASE + 0x21) +#define DEM_TIME_SYNC_POLL (SMEM_LOG_DEM_EVENT_BASE + 0x22) +#define DEM_TIME_SYNC_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x23) +#define DEM_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x25) +#else +#define DEM_NO_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 1) +#define DEM_INSUF_TIME (SMEM_LOG_DEM_EVENT_BASE + 2) +#define DEMAPPS_ENTER_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 3) +#define DEMAPPS_DETECT_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 4) +#define DEMAPPS_END_APPS_TCXO (SMEM_LOG_DEM_EVENT_BASE + 5) +#define DEMAPPS_ENTER_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 6) +#define DEMAPPS_END_APPS_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 7) +#define DEMAPPS_SETUP_APPS_PWRCLPS (SMEM_LOG_DEM_EVENT_BASE + 8) +#define DEMAPPS_PWRCLPS_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 9) +#define DEMMOD_SEND_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0xA) +#define DEMMOD_NO_APPS_VOTE (SMEM_LOG_DEM_EVENT_BASE + 0xB) +#define DEMMOD_NO_TCXO_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0xC) +#define DEMMOD_BT_CLOCK (SMEM_LOG_DEM_EVENT_BASE + 0xD) +#define DEMMOD_UART_CLOCK (SMEM_LOG_DEM_EVENT_BASE + 0xE) +#define DEMMOD_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0xF) +#define DEM_SLEEP_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x10) +#define DEMMOD_TCXO_END (SMEM_LOG_DEM_EVENT_BASE + 0x11) +#define DEMMOD_END_SLEEP_SIG (SMEM_LOG_DEM_EVENT_BASE + 0x12) +#define DEMMOD_SETUP_APPSSLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x13) +#define DEMMOD_ENTER_TCXO (SMEM_LOG_DEM_EVENT_BASE + 0x14) +#define DEMMOD_WAKE_APPS (SMEM_LOG_DEM_EVENT_BASE + 0x15) +#define DEMMOD_POWER_COLLAPSE_APPS (SMEM_LOG_DEM_EVENT_BASE + 0x16) +#define DEMMOD_RESTORE_APPS_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x17) +#define DEMAPPS_ASSERT_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x18) +#define DEMAPPS_RESTART_START_TIMER (SMEM_LOG_DEM_EVENT_BASE + 0x19) +#define DEMAPPS_ENTER_RUN (SMEM_LOG_DEM_EVENT_BASE + 0x1A) +#define DEMMOD_MAO_INTS (SMEM_LOG_DEM_EVENT_BASE + 0x1B) +#define DEMMOD_POWERUP_APPS_CALLED (SMEM_LOG_DEM_EVENT_BASE + 0x1C) +#define DEMMOD_PC_TIMER_EXPIRED (SMEM_LOG_DEM_EVENT_BASE + 0x1D) +#define DEM_DETECT_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 0x1E) +#define DEM_DETECT_RUN (SMEM_LOG_DEM_EVENT_BASE + 0x1F) +#define DEM_SET_APPS_TIMER (SMEM_LOG_DEM_EVENT_BASE + 0x20) +#define DEM_NEGATE_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x21) +#define DEMMOD_APPS_WAKEUP_INT (SMEM_LOG_DEM_EVENT_BASE + 0x22) +#define DEMMOD_APPS_SWFI (SMEM_LOG_DEM_EVENT_BASE + 0x23) +#define DEM_SEND_BATTERY_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x24) +#define DEM_SMI_CLK_DISABLED (SMEM_LOG_DEM_EVENT_BASE + 0x25) +#define DEM_SMI_CLK_ENABLED (SMEM_LOG_DEM_EVENT_BASE + 0x26) +#define DEMAPPS_SETUP_APPS_SUSPEND (SMEM_LOG_DEM_EVENT_BASE + 0x27) +#define DEM_RPC_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 0x28) +#define DEMAPPS_WAKEUP_REASON (SMEM_LOG_DEM_EVENT_BASE + 0x29) +#define DEM_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x30) +#endif +#define DEMMOD_UMTS_BASE (SMEM_LOG_DEM_EVENT_BASE + 0x8000) +#define DEMMOD_GL1_GO_TO_SLEEP (DEMMOD_UMTS_BASE + 0x0000) +#define DEMMOD_GL1_SLEEP_START (DEMMOD_UMTS_BASE + 0x0001) +#define DEMMOD_GL1_AFTER_GSM_CLK_ON (DEMMOD_UMTS_BASE + 0x0002) +#define DEMMOD_GL1_BEFORE_RF_ON (DEMMOD_UMTS_BASE + 0x0003) +#define DEMMOD_GL1_AFTER_RF_ON (DEMMOD_UMTS_BASE + 0x0004) +#define DEMMOD_GL1_FRAME_TICK (DEMMOD_UMTS_BASE + 0x0005) +#define DEMMOD_GL1_WCDMA_START (DEMMOD_UMTS_BASE + 0x0006) +#define DEMMOD_GL1_WCDMA_ENDING (DEMMOD_UMTS_BASE + 0x0007) +#define DEMMOD_UMTS_NOT_OKTS (DEMMOD_UMTS_BASE + 0x0008) +#define DEMMOD_UMTS_START_TCXO_SHUTDOWN (DEMMOD_UMTS_BASE + 0x0009) +#define DEMMOD_UMTS_END_TCXO_SHUTDOWN (DEMMOD_UMTS_BASE + 0x000A) +#define DEMMOD_UMTS_START_ARM_HALT (DEMMOD_UMTS_BASE + 0x000B) +#define DEMMOD_UMTS_END_ARM_HALT (DEMMOD_UMTS_BASE + 0x000C) +#define DEMMOD_UMTS_NEXT_WAKEUP_SCLK (DEMMOD_UMTS_BASE + 0x000D) +#define TIME_REMOTE_LOG_EVENT_START (SMEM_LOG_TIMETICK_EVENT_BASE + 0) +#define TIME_REMOTE_LOG_EVENT_GOTO_WAIT (SMEM_LOG_TIMETICK_EVENT_BASE + 1) +#define TIME_REMOTE_LOG_EVENT_GOTO_INIT (SMEM_LOG_TIMETICK_EVENT_BASE + 2) +#define ERR_ERROR_FATAL (SMEM_LOG_ERROR_EVENT_BASE + 1) +#define ERR_ERROR_FATAL_TASK (SMEM_LOG_ERROR_EVENT_BASE + 2) +#define DCVSAPPS_LOG_IDLE (SMEM_LOG_DCVS_EVENT_BASE + 0x0) +#define DCVSAPPS_LOG_ERR (SMEM_LOG_DCVS_EVENT_BASE + 0x1) +#define DCVSAPPS_LOG_CHG (SMEM_LOG_DCVS_EVENT_BASE + 0x2) +#define DCVSAPPS_LOG_REG (SMEM_LOG_DCVS_EVENT_BASE + 0x3) +#define DCVSAPPS_LOG_DEREG (SMEM_LOG_DCVS_EVENT_BASE + 0x4) +#define SMEM_LOG_EVENT_CB (SMEM_LOG_SMEM_EVENT_BASE + 0) +#define SMEM_LOG_EVENT_START (SMEM_LOG_SMEM_EVENT_BASE + 1) +#define SMEM_LOG_EVENT_INIT (SMEM_LOG_SMEM_EVENT_BASE + 2) +#define SMEM_LOG_EVENT_RUNNING (SMEM_LOG_SMEM_EVENT_BASE + 3) +#define SMEM_LOG_EVENT_STOP (SMEM_LOG_SMEM_EVENT_BASE + 4) +#define SMEM_LOG_EVENT_RESTART (SMEM_LOG_SMEM_EVENT_BASE + 5) +#define SMEM_LOG_EVENT_SS (SMEM_LOG_SMEM_EVENT_BASE + 6) +#define SMEM_LOG_EVENT_READ (SMEM_LOG_SMEM_EVENT_BASE + 7) +#define SMEM_LOG_EVENT_WRITE (SMEM_LOG_SMEM_EVENT_BASE + 8) +#define SMEM_LOG_EVENT_SIGS1 (SMEM_LOG_SMEM_EVENT_BASE + 9) +#define SMEM_LOG_EVENT_SIGS2 (SMEM_LOG_SMEM_EVENT_BASE + 10) +#define SMEM_LOG_EVENT_WRITE_DM (SMEM_LOG_SMEM_EVENT_BASE + 11) +#define SMEM_LOG_EVENT_READ_DM (SMEM_LOG_SMEM_EVENT_BASE + 12) +#define SMEM_LOG_EVENT_SKIP_DM (SMEM_LOG_SMEM_EVENT_BASE + 13) +#define SMEM_LOG_EVENT_STOP_DM (SMEM_LOG_SMEM_EVENT_BASE + 14) +#define SMEM_LOG_EVENT_ISR (SMEM_LOG_SMEM_EVENT_BASE + 15) +#define SMEM_LOG_EVENT_TASK (SMEM_LOG_SMEM_EVENT_BASE + 16) +#define SMEM_LOG_EVENT_RS (SMEM_LOG_SMEM_EVENT_BASE + 17) +#define ONCRPC_LOG_EVENT_SMD_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 0) +#define ONCRPC_LOG_EVENT_RPC_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 1) +#define ONCRPC_LOG_EVENT_RPC_BOTH_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 2) +#define ONCRPC_LOG_EVENT_RPC_INIT (SMEM_LOG_ONCRPC_EVENT_BASE + 3) +#define ONCRPC_LOG_EVENT_RUNNING (SMEM_LOG_ONCRPC_EVENT_BASE + 4) +#define ONCRPC_LOG_EVENT_APIS_INITED (SMEM_LOG_ONCRPC_EVENT_BASE + 5) +#define ONCRPC_LOG_EVENT_AMSS_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 6) +#define ONCRPC_LOG_EVENT_SMD_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 7) +#define ONCRPC_LOG_EVENT_ONCRPC_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 8) +#define ONCRPC_LOG_EVENT_CB (SMEM_LOG_ONCRPC_EVENT_BASE + 9) +#define ONCRPC_LOG_EVENT_STD_CALL (SMEM_LOG_ONCRPC_EVENT_BASE + 10) +#define ONCRPC_LOG_EVENT_STD_REPLY (SMEM_LOG_ONCRPC_EVENT_BASE + 11) +#define ONCRPC_LOG_EVENT_STD_CALL_ASYNC (SMEM_LOG_ONCRPC_EVENT_BASE + 12) +#define NO_SLEEP_OLD (SMEM_LOG_SLEEP_EVENT_BASE + 0x1) +#define INSUF_TIME (SMEM_LOG_SLEEP_EVENT_BASE + 0x2) +#define MOD_UART_CLOCK (SMEM_LOG_SLEEP_EVENT_BASE + 0x3) +#define SLEEP_INFO (SMEM_LOG_SLEEP_EVENT_BASE + 0x4) +#define MOD_TCXO_END (SMEM_LOG_SLEEP_EVENT_BASE + 0x5) +#define MOD_ENTER_TCXO (SMEM_LOG_SLEEP_EVENT_BASE + 0x6) +#define NO_SLEEP_NEW (SMEM_LOG_SLEEP_EVENT_BASE + 0x7) +#define RPC_ROUTER_LOG_EVENT_UNKNOWN (SMEM_LOG_RPC_ROUTER_EVENT_BASE) +#define RPC_ROUTER_LOG_EVENT_MSG_READ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 1) +#define RPC_ROUTER_LOG_EVENT_MSG_WRITTEN (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 2) +#define RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 3) +#define RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 4) +#define RPC_ROUTER_LOG_EVENT_MID_READ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 5) +#define RPC_ROUTER_LOG_EVENT_MID_WRITTEN (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 6) +#define RPC_ROUTER_LOG_EVENT_MID_CFM_REQ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 7) + +#ifdef CONFIG_MSM_SMD_LOGGING +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3); +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6); +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3); +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6); +#else +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) { } +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) { } +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) { } +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) { } +#endif + diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h new file mode 100644 index 00000000000..c0ad65b8b49 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/socinfo.h @@ -0,0 +1,312 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ +#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ + +#include +#include +#include +#include +#include + +#include +#include +/* + * SOC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. For example: + * 1.0 -> 0x00010000 + * 2.3 -> 0x00020003 + */ +#define SOCINFO_VERSION_MAJOR(ver) ((ver & 0xffff0000) >> 16) +#define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff) + +#ifdef CONFIG_OF +#define early_machine_is_copper() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmcopper") +#define machine_is_copper() \ + of_machine_is_compatible("qcom,msmcopper") +#define machine_is_copper_sim() \ + of_machine_is_compatible("qcom,msmcopper-sim") +#define machine_is_copper_rumi() \ + of_machine_is_compatible("qcom,msmcopper-rumi") +#define early_machine_is_msm9625() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm9625") +#define machine_is_msm9625() \ + of_machine_is_compatible("qcom,msm9625") +#else +#define early_machine_is_copper() 0 +#define machine_is_copper() 0 +#define machine_is_copper_sim() 0 +#define machine_is_copper_rumi() 0 +#define early_machine_is_msm9625() 0 +#define machine_is_msm9625() 0 +#endif + +#define PLATFORM_SUBTYPE_SGLTE 6 + +enum msm_cpu { + MSM_CPU_UNKNOWN = 0, + MSM_CPU_7X01, + MSM_CPU_7X25, + MSM_CPU_7X27, + MSM_CPU_8X50, + MSM_CPU_8X50A, + MSM_CPU_7X30, + MSM_CPU_8X55, + MSM_CPU_8X60, + MSM_CPU_8960, + MSM_CPU_7X27A, + FSM_CPU_9XXX, + MSM_CPU_7X25A, + MSM_CPU_7X25AA, + MSM_CPU_7X25AB, + MSM_CPU_8064, + MSM_CPU_8930, + MSM_CPU_7X27AA, + MSM_CPU_9615, + MSM_CPU_COPPER, + MSM_CPU_8627, + MSM_CPU_8625, + MSM_CPU_9625 +}; + +enum msm_cpu socinfo_get_msm_cpu(void); +uint32_t socinfo_get_id(void); +uint32_t socinfo_get_version(void); +uint32_t socinfo_get_raw_id(void); +char *socinfo_get_build_id(void); +uint32_t socinfo_get_platform_type(void); +uint32_t socinfo_get_platform_subtype(void); +uint32_t socinfo_get_platform_version(void); +int __init socinfo_init(void) __must_check; +const int read_msm_cpu_type(void); +const int get_core_count(void); +const int cpu_is_krait_v1(void); + +static inline int cpu_is_msm7x01(void) +{ +#ifdef CONFIG_ARCH_MSM7X01A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X01; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25(void) +{ +#ifdef CONFIG_ARCH_MSM7X25 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x27(void) +{ +#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A) + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x27a(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27A; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x27aa(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27AA; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25a(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25A; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25aa(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25AA; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25ab(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25AB; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x30(void) +{ +#ifdef CONFIG_ARCH_MSM7X30 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X30; +#else + return 0; +#endif +} + +static inline int cpu_is_qsd8x50(void) +{ +#ifdef CONFIG_ARCH_QSD8X50 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X50; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8x55(void) +{ +#ifdef CONFIG_ARCH_MSM7X30 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X55; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8x60(void) +{ +#ifdef CONFIG_ARCH_MSM8X60 + return read_msm_cpu_type() == MSM_CPU_8X60; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8960(void) +{ +#ifdef CONFIG_ARCH_MSM8960 + return read_msm_cpu_type() == MSM_CPU_8960; +#else + return 0; +#endif +} + +static inline int cpu_is_apq8064(void) +{ +#ifdef CONFIG_ARCH_APQ8064 + return read_msm_cpu_type() == MSM_CPU_8064; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8930(void) +{ +#ifdef CONFIG_ARCH_MSM8930 + return (read_msm_cpu_type() == MSM_CPU_8930) || + (read_msm_cpu_type() == MSM_CPU_8627); +#else + return 0; +#endif +} + +static inline int cpu_is_msm8627(void) +{ +/* 8930 and 8627 will share the same CONFIG_ARCH type unless otherwise needed */ +#ifdef CONFIG_ARCH_MSM8930 + return read_msm_cpu_type() == MSM_CPU_8627; +#else + return 0; +#endif +} + +static inline int cpu_is_fsm9xxx(void) +{ +#ifdef CONFIG_ARCH_FSM9XXX + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == FSM_CPU_9XXX; +#else + return 0; +#endif +} + +static inline int cpu_is_msm9615(void) +{ +#ifdef CONFIG_ARCH_MSM9615 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_9615; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8625(void) +{ +#ifdef CONFIG_ARCH_MSM8625 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8625; +#else + return 0; +#endif +} + +#endif diff --git a/arch/arm/mach-msm/include/mach/subsystem_notif.h b/arch/arm/mach-msm/include/mach/subsystem_notif.h new file mode 100644 index 00000000000..37d4eeca4eb --- /dev/null +++ b/arch/arm/mach-msm/include/mach/subsystem_notif.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Subsystem restart notifier API header + * + */ + +#ifndef _SUBSYS_NOTIFIER_H +#define _SUBSYS_NOTIFIER_H + +#include + +enum subsys_notif_type { + SUBSYS_BEFORE_SHUTDOWN, + SUBSYS_AFTER_SHUTDOWN, + SUBSYS_BEFORE_POWERUP, + SUBSYS_AFTER_POWERUP, + SUBSYS_NOTIF_TYPE_COUNT +}; + +#if defined(CONFIG_MSM_SUBSYSTEM_RESTART) +/* Use the subsys_notif_register_notifier API to register for notifications for + * a particular subsystem. This API will return a handle that can be used to + * un-reg for notifications using the subsys_notif_unregister_notifier API by + * passing in that handle as an argument. + * + * On receiving a notification, the second (unsigned long) argument of the + * notifier callback will contain the notification type, and the third (void *) + * argument will contain the handle that was returned by + * subsys_notif_register_notifier. + */ +void *subsys_notif_register_notifier( + const char *subsys_name, struct notifier_block *nb); +int subsys_notif_unregister_notifier(void *subsys_handle, + struct notifier_block *nb); + +/* Use the subsys_notif_init_subsys API to initialize the notifier chains form + * a particular subsystem. This API will return a handle that can be used to + * queue notifications using the subsys_notif_queue_notification API by passing + * in that handle as an argument. + */ +void *subsys_notif_add_subsys(const char *); +int subsys_notif_queue_notification(void *subsys_handle, + enum subsys_notif_type notif_type); +#else + +static inline void *subsys_notif_register_notifier( + const char *subsys_name, struct notifier_block *nb) +{ + return NULL; +} + +static inline int subsys_notif_unregister_notifier(void *subsys_handle, + struct notifier_block *nb) +{ + return 0; +} + +static inline void *subsys_notif_add_subsys(const char *subsys_name) +{ + return NULL; +} + +static inline int subsys_notif_queue_notification(void *subsys_handle, + enum subsys_notif_type notif_type) +{ + return 0; +} +#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ + +#endif diff --git a/arch/arm/mach-msm/include/mach/subsystem_restart.h b/arch/arm/mach-msm/include/mach/subsystem_restart.h new file mode 100644 index 00000000000..51ace969a55 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/subsystem_restart.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SUBSYS_RESTART_H +#define __SUBSYS_RESTART_H + +#include + +#define SUBSYS_NAME_MAX_LENGTH 40 + +enum { + RESET_SOC = 1, + RESET_SUBSYS_COUPLED, + RESET_SUBSYS_INDEPENDENT, + RESET_LEVEL_MAX +}; + +struct subsys_data { + const char *name; + int (*shutdown) (const struct subsys_data *); + int (*powerup) (const struct subsys_data *); + void (*crash_shutdown) (const struct subsys_data *); + int (*ramdump) (int, const struct subsys_data *); + + /* Internal use only */ + struct list_head list; + void *notif_handle; + + struct mutex shutdown_lock; + struct mutex powerup_lock; + + void *restart_order; + struct subsys_data *single_restart_list[1]; +}; + +#if defined(CONFIG_MSM_SUBSYSTEM_RESTART) + +int get_restart_level(void); +int subsystem_restart(const char *subsys_name); +int ssr_register_subsystem(struct subsys_data *subsys); + +#else + +static inline int get_restart_level(void) +{ + return 0; +} + +static inline int subsystem_restart(const char *subsystem_name) +{ + return 0; +} + +static inline int ssr_register_subsystem(struct subsys_data *subsys) +{ + return 0; +} + +#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ + +#endif diff --git a/arch/arm/mach-msm/include/mach/system.h b/arch/arm/mach-msm/include/mach/system.h index f5fb2ec87ff..490c17d525b 100644 --- a/arch/arm/mach-msm/include/mach/system.h +++ b/arch/arm/mach-msm/include/mach/system.h @@ -17,3 +17,6 @@ * PSHOLD line on the PMIC to hard reset the system */ extern void (*msm_hw_reset_hook)(void); + +void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat); + diff --git a/arch/arm/mach-msm/include/mach/timex.h b/arch/arm/mach-msm/include/mach/timex.h index a62e6b215ae..542aba31126 100644 --- a/arch/arm/mach-msm/include/mach/timex.h +++ b/arch/arm/mach-msm/include/mach/timex.h @@ -18,4 +18,8 @@ #define CLOCK_TICK_RATE 1000000 +#ifdef CONFIG_HAVE_ARCH_HAS_CURRENT_TIMER +#define ARCH_HAS_READ_CURRENT_TIMER +#endif + #endif diff --git a/arch/arm/mach-msm/include/mach/tpm_st_i2c.h b/arch/arm/mach-msm/include/mach/tpm_st_i2c.h new file mode 100644 index 00000000000..362acbbe530 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/tpm_st_i2c.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _TPM_ST_I2C_H_ +#define _TPM_ST_I2C_H_ + +struct tpm_st_i2c_platform_data { + int accept_cmd_gpio; + int data_avail_gpio; + int accept_cmd_irq; + int data_avail_irq; + int (*gpio_setup)(void); + void (*gpio_release)(void); +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/uncompress.h b/arch/arm/mach-msm/include/mach/uncompress.h index c14011fe832..dc20df59d7c 100644 --- a/arch/arm/mach-msm/include/mach/uncompress.h +++ b/arch/arm/mach-msm/include/mach/uncompress.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,9 +16,11 @@ #ifndef __ASM_ARCH_MSM_UNCOMPRESS_H #define __ASM_ARCH_MSM_UNCOMPRESS_H -#include +#include +#include #include #include +#include #define UART_CSR (*(volatile uint32_t *)(MSM_DEBUG_UART_PHYS + 0x08)) #define UART_TF (*(volatile uint32_t *)(MSM_DEBUG_UART_PHYS + 0x0c)) @@ -29,28 +31,33 @@ #define UART_DM_NCHAR (*((volatile uint32_t *)(MSM_DEBUG_UART_PHYS + 0x40))) #define UART_DM_TF (*((volatile uint32_t *)(MSM_DEBUG_UART_PHYS + 0x70))) +#ifndef CONFIG_DEBUG_ICEDCC static void putc(int c) { #if defined(MSM_DEBUG_UART_PHYS) + unsigned long base = MSM_DEBUG_UART_PHYS; + #ifdef CONFIG_MSM_HAS_DEBUG_UART_HS /* * Wait for TX_READY to be set; but skip it if we have a * TX underrun. */ - if (UART_DM_SR & 0x08) - while (!(UART_DM_ISR & 0x80)) + if (!(__raw_readl_no_log(base + UARTDM_SR_OFFSET) & 0x08)) + while (!(__raw_readl_no_log(base + UARTDM_ISR_OFFSET) & 0x80)) cpu_relax(); - UART_DM_CR = 0x300; - UART_DM_NCHAR = 0x1; - UART_DM_TF = c; + __raw_writel_no_log(0x300, base + UARTDM_CR_OFFSET); + __raw_writel_no_log(0x1, base + UARTDM_NCF_TX_OFFSET); + __raw_writel_no_log(c, base + UARTDM_TF_OFFSET); #else - while (!(UART_CSR & 0x04)) + /* Wait for TX_READY to be set */ + while (!(__raw_readl_no_log(base + 0x08) & 0x04)) cpu_relax(); - UART_TF = c; + __raw_writel_no_log(c, base + 0x0c); #endif #endif } +#endif static inline void flush(void) { diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h new file mode 100644 index 00000000000..ec135a38ea2 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usb_bam.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _USB_BAM_H_ +#define _USB_BAM_H_ + +/** + * SPS Pipes direction. + * + * USB_TO_PEER_PERIPHERAL USB (as Producer) to other + * peer peripheral. + * PEER_PERIPHERAL_TO_USB Other Peripheral to + * USB (as consumer). + */ +enum usb_bam_pipe_dir { + USB_TO_PEER_PERIPHERAL, + PEER_PERIPHERAL_TO_USB, +}; + +#ifdef CONFIG_USB_BAM +/** + * Connect USB-to-Periperal SPS connection. + * + * This function returns the allocated pipes number. + * + * @idx - Connection index. + * + * @src_pipe_idx - allocated pipe index - USB as a + * source (output) + * + * @dst_pipe_idx - allocated pipe index - USB as a + * destination (output) + * + * @return 0 on success, negative value on error + * + */ +int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx); + +/** + * Register a wakeup callback from peer BAM. + * + * @idx - Connection index. + * + * @callback - the callback function + * + * @return 0 on success, negative value on error + * + */ +int usb_bam_register_wake_cb(u8 idx, + int (*callback)(void *), void* param); +#else +static inline int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx) +{ + return -ENODEV; +} + +static inline int usb_bam_register_wake_cb(u8 idx, + int (*callback)(void *), void* param) +{ + return -ENODEV; +} +#endif +#endif /* _USB_BAM_H_ */ + diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h new file mode 100644 index 00000000000..be119892ccc --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_GADGET_XPORT_H__ +#define __LINUX_USB_GADGET_XPORT_H__ + +enum transport_type { + USB_GADGET_XPORT_UNDEF, + USB_GADGET_XPORT_TTY, + USB_GADGET_XPORT_SDIO, + USB_GADGET_XPORT_SMD, + USB_GADGET_XPORT_BAM, + USB_GADGET_XPORT_BAM2BAM, + USB_GADGET_XPORT_HSIC, + USB_GADGET_XPORT_HSUART, + USB_GADGET_XPORT_NONE, +}; + +#define XPORT_STR_LEN 10 + +static char *xport_to_str(enum transport_type t) +{ + switch (t) { + case USB_GADGET_XPORT_TTY: + return "TTY"; + case USB_GADGET_XPORT_SDIO: + return "SDIO"; + case USB_GADGET_XPORT_SMD: + return "SMD"; + case USB_GADGET_XPORT_BAM: + return "BAM"; + case USB_GADGET_XPORT_BAM2BAM: + return "BAM2BAM"; + case USB_GADGET_XPORT_HSIC: + return "HSIC"; + case USB_GADGET_XPORT_HSUART: + return "HSUART"; + case USB_GADGET_XPORT_NONE: + return "NONE"; + default: + return "UNDEFINED"; + } +} + +static enum transport_type str_to_xport(const char *name) +{ + if (!strncasecmp("TTY", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_TTY; + if (!strncasecmp("SDIO", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_SDIO; + if (!strncasecmp("SMD", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_SMD; + if (!strncasecmp("BAM", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_BAM; + if (!strncasecmp("BAM2BAM", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_BAM2BAM; + if (!strncasecmp("HSIC", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_HSIC; + if (!strncasecmp("HSUART", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_HSUART; + if (!strncasecmp("", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_NONE; + + return USB_GADGET_XPORT_UNDEF; +} + +enum gadget_type { + USB_GADGET_SERIAL, + USB_GADGET_RMNET, +}; + +#define NUM_RMNET_HSIC_PORTS 1 +#define NUM_DUN_HSIC_PORTS 1 +#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \ + + NUM_DUN_HSIC_PORTS) + +#define NUM_RMNET_HSUART_PORTS 1 +#define NUM_DUN_HSUART_PORTS 1 +#define NUM_HSUART_PORTS (NUM_RMNET_HSUART_PORTS \ + + NUM_DUN_HSUART_PORTS) + +int ghsic_ctrl_connect(void *, int); +void ghsic_ctrl_disconnect(void *, int); +int ghsic_ctrl_setup(unsigned int, enum gadget_type); +int ghsic_data_connect(void *, int); +void ghsic_data_disconnect(void *, int); +int ghsic_data_setup(unsigned int, enum gadget_type); + +int ghsuart_ctrl_connect(void *, int); +void ghsuart_ctrl_disconnect(void *, int); +int ghsuart_ctrl_setup(unsigned int, enum gadget_type); +int ghsuart_data_connect(void *, int); +void ghsuart_data_disconnect(void *, int); +int ghsuart_data_setup(unsigned int, enum gadget_type); +#endif diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index a1e7b116885..86e06a43ecc 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -3,7 +3,7 @@ * MSM7K, QSD io support * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -26,10 +26,11 @@ #include #include #include +#include #include -#define MSM_CHIP_DEVICE(name, chip) { \ +#define MSM_CHIP_DEVICE(name, chip) { \ .virtual = (unsigned long) MSM_##name##_BASE, \ .pfn = __phys_to_pfn(chip##_##name##_PHYS), \ .length = chip##_##name##_SIZE, \ @@ -38,25 +39,48 @@ #define MSM_DEVICE(name) MSM_CHIP_DEVICE(name, MSM) -#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_MSM7X27) \ +/* msm_shared_ram_phys default value of 0x00100000 is the most common value + * and should work as-is for any target without stacked memory. + */ +unsigned int msm_shared_ram_phys = 0x00100000; + +static void __init msm_map_io(struct map_desc *io_desc, int size) +{ + int i; + + BUG_ON(!size); + for (i = 0; i < size; i++) + if (io_desc[i].virtual == (unsigned long)MSM_SHARED_RAM_BASE) + io_desc[i].pfn = __phys_to_pfn(msm_shared_ram_phys); + + iotable_init(io_desc, size); +} + +#if defined(CONFIG_ARCH_MSM7X01A) || defined(CONFIG_ARCH_MSM7X27) \ || defined(CONFIG_ARCH_MSM7X25) static struct map_desc msm_io_desc[] __initdata = { - MSM_DEVICE(VIC), - MSM_CHIP_DEVICE(CSR, MSM7X00), - MSM_DEVICE(DMOV), - MSM_CHIP_DEVICE(GPIO1, MSM7X00), - MSM_CHIP_DEVICE(GPIO2, MSM7X00), - MSM_DEVICE(CLK_CTL), + MSM_CHIP_DEVICE(VIC, MSM7XXX), + MSM_CHIP_DEVICE(CSR, MSM7XXX), + MSM_CHIP_DEVICE(TMR, MSM7XXX), + MSM_CHIP_DEVICE(GPIO1, MSM7XXX), + MSM_CHIP_DEVICE(GPIO2, MSM7XXX), + MSM_CHIP_DEVICE(CLK_CTL, MSM7XXX), + MSM_CHIP_DEVICE(AD5, MSM7XXX), + MSM_CHIP_DEVICE(MDC, MSM7XXX), #if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \ defined(CONFIG_DEBUG_MSM_UART3) MSM_DEVICE(DEBUG_UART), #endif -#ifdef CONFIG_ARCH_MSM7X30 - MSM_DEVICE(GCC), +#ifdef CONFIG_CACHE_L2X0 + { + .virtual = (unsigned long) MSM_L2CC_BASE, + .pfn = __phys_to_pfn(MSM7XXX_L2CC_PHYS), + .length = MSM7XXX_L2CC_SIZE, + .type = MT_DEVICE, + }, #endif { .virtual = (unsigned long) MSM_SHARED_RAM_BASE, - .pfn = __phys_to_pfn(MSM_SHARED_RAM_PHYS), .length = MSM_SHARED_RAM_SIZE, .type = MT_DEVICE, }, @@ -64,95 +88,32 @@ static struct map_desc msm_io_desc[] __initdata = { void __init msm_map_common_io(void) { + /*Peripheral port memory remap, nothing looks to be there for + * cortex a5. + */ +#ifndef CONFIG_ARCH_MSM_CORTEX_A5 /* Make sure the peripheral register window is closed, since * we will use PTE flags (TEX[1]=1,B=0,C=1) to determine which * pages are peripheral interface or not. */ asm("mcr p15, 0, %0, c15, c2, 4" : : "r" (0)); - iotable_init(msm_io_desc, ARRAY_SIZE(msm_io_desc)); +#endif + msm_map_io(msm_io_desc, ARRAY_SIZE(msm_io_desc)); } #endif #ifdef CONFIG_ARCH_QSD8X50 static struct map_desc qsd8x50_io_desc[] __initdata = { MSM_DEVICE(VIC), - MSM_CHIP_DEVICE(CSR, QSD8X50), - MSM_DEVICE(DMOV), - MSM_CHIP_DEVICE(GPIO1, QSD8X50), - MSM_CHIP_DEVICE(GPIO2, QSD8X50), + MSM_DEVICE(CSR), + MSM_DEVICE(TMR), + MSM_DEVICE(GPIO1), + MSM_DEVICE(GPIO2), MSM_DEVICE(CLK_CTL), MSM_DEVICE(SIRC), MSM_DEVICE(SCPLL), MSM_DEVICE(AD5), MSM_DEVICE(MDC), -#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \ - defined(CONFIG_DEBUG_MSM_UART3) - MSM_DEVICE(DEBUG_UART), -#endif - { - .virtual = (unsigned long) MSM_SHARED_RAM_BASE, - .pfn = __phys_to_pfn(MSM_SHARED_RAM_PHYS), - .length = MSM_SHARED_RAM_SIZE, - .type = MT_DEVICE, - }, -}; - -void __init msm_map_qsd8x50_io(void) -{ - iotable_init(qsd8x50_io_desc, ARRAY_SIZE(qsd8x50_io_desc)); -} -#endif /* CONFIG_ARCH_QSD8X50 */ - -#ifdef CONFIG_ARCH_MSM8X60 -static struct map_desc msm8x60_io_desc[] __initdata = { - MSM_CHIP_DEVICE(QGIC_DIST, MSM8X60), - MSM_CHIP_DEVICE(QGIC_CPU, MSM8X60), - MSM_CHIP_DEVICE(TMR, MSM8X60), - MSM_CHIP_DEVICE(TMR0, MSM8X60), - MSM_DEVICE(ACC), - MSM_DEVICE(GCC), -#ifdef CONFIG_DEBUG_MSM8660_UART - MSM_DEVICE(DEBUG_UART), -#endif -}; - -void __init msm_map_msm8x60_io(void) -{ - iotable_init(msm8x60_io_desc, ARRAY_SIZE(msm8x60_io_desc)); -} -#endif /* CONFIG_ARCH_MSM8X60 */ - -#ifdef CONFIG_ARCH_MSM8960 -static struct map_desc msm8960_io_desc[] __initdata = { - MSM_CHIP_DEVICE(QGIC_DIST, MSM8960), - MSM_CHIP_DEVICE(QGIC_CPU, MSM8960), - MSM_CHIP_DEVICE(TMR, MSM8960), - MSM_CHIP_DEVICE(TMR0, MSM8960), -#ifdef CONFIG_DEBUG_MSM8960_UART - MSM_DEVICE(DEBUG_UART), -#endif -}; - -void __init msm_map_msm8960_io(void) -{ - iotable_init(msm8960_io_desc, ARRAY_SIZE(msm8960_io_desc)); -} -#endif /* CONFIG_ARCH_MSM8960 */ - -#ifdef CONFIG_ARCH_MSM7X30 -static struct map_desc msm7x30_io_desc[] __initdata = { - MSM_DEVICE(VIC), - MSM_CHIP_DEVICE(CSR, MSM7X30), - MSM_DEVICE(DMOV), - MSM_CHIP_DEVICE(GPIO1, MSM7X30), - MSM_CHIP_DEVICE(GPIO2, MSM7X30), - MSM_DEVICE(CLK_CTL), - MSM_DEVICE(CLK_CTL_SH2), - MSM_DEVICE(AD5), - MSM_DEVICE(MDC), - MSM_DEVICE(ACC), - MSM_DEVICE(SAW), - MSM_DEVICE(GCC), MSM_DEVICE(TCSR), #if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \ defined(CONFIG_DEBUG_MSM_UART3) @@ -160,7 +121,223 @@ static struct map_desc msm7x30_io_desc[] __initdata = { #endif { .virtual = (unsigned long) MSM_SHARED_RAM_BASE, - .pfn = __phys_to_pfn(MSM_SHARED_RAM_PHYS), + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +}; + +void __init msm_map_qsd8x50_io(void) +{ + msm_map_io(qsd8x50_io_desc, ARRAY_SIZE(qsd8x50_io_desc)); +} +#endif /* CONFIG_ARCH_QSD8X50 */ + +#ifdef CONFIG_ARCH_MSM8X60 +static struct map_desc msm8x60_io_desc[] __initdata = { + MSM_DEVICE(QGIC_DIST), + MSM_DEVICE(QGIC_CPU), + MSM_DEVICE(TMR), + MSM_DEVICE(TMR0), + MSM_DEVICE(RPM_MPM), + MSM_DEVICE(ACC), + MSM_DEVICE(ACC0), + MSM_DEVICE(ACC1), + MSM_DEVICE(SAW0), + MSM_DEVICE(SAW1), + MSM_DEVICE(GCC), + MSM_DEVICE(TLMM), + MSM_DEVICE(SCPLL), + MSM_DEVICE(RPM), + MSM_DEVICE(CLK_CTL), + MSM_DEVICE(MMSS_CLK_CTL), + MSM_DEVICE(LPASS_CLK_CTL), + MSM_DEVICE(TCSR), + MSM_DEVICE(IMEM), + MSM_DEVICE(HDMI), +#ifdef CONFIG_DEBUG_MSM8660_UART + MSM_DEVICE(DEBUG_UART), +#endif + MSM_DEVICE(SIC_NON_SECURE), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, + MSM_DEVICE(QFPROM), +}; + +void __init msm_map_msm8x60_io(void) +{ + msm_map_io(msm8x60_io_desc, ARRAY_SIZE(msm8x60_io_desc)); + init_consistent_dma_size(14*SZ_1M); +} +#endif /* CONFIG_ARCH_MSM8X60 */ + +#ifdef CONFIG_ARCH_MSM8960 +static struct map_desc msm8960_io_desc[] __initdata = { + MSM_CHIP_DEVICE(QGIC_DIST, MSM8960), + MSM_CHIP_DEVICE(QGIC_CPU, MSM8960), + MSM_CHIP_DEVICE(ACC0, MSM8960), + MSM_CHIP_DEVICE(ACC1, MSM8960), + MSM_CHIP_DEVICE(TMR, MSM8960), + MSM_CHIP_DEVICE(TMR0, MSM8960), + MSM_CHIP_DEVICE(RPM_MPM, MSM8960), + MSM_CHIP_DEVICE(CLK_CTL, MSM8960), + MSM_CHIP_DEVICE(MMSS_CLK_CTL, MSM8960), + MSM_CHIP_DEVICE(LPASS_CLK_CTL, MSM8960), + MSM_CHIP_DEVICE(RPM, MSM8960), + MSM_CHIP_DEVICE(TLMM, MSM8960), + MSM_CHIP_DEVICE(HFPLL, MSM8960), + MSM_CHIP_DEVICE(SAW0, MSM8960), + MSM_CHIP_DEVICE(SAW1, MSM8960), + MSM_CHIP_DEVICE(SAW_L2, MSM8960), + MSM_CHIP_DEVICE(SIC_NON_SECURE, MSM8960), + MSM_CHIP_DEVICE(APCS_GCC, MSM8960), + MSM_CHIP_DEVICE(IMEM, MSM8960), + MSM_CHIP_DEVICE(HDMI, MSM8960), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +#ifdef CONFIG_DEBUG_MSM8960_UART + MSM_DEVICE(DEBUG_UART), +#endif + MSM_CHIP_DEVICE(QFPROM, MSM8960), +}; + +void __init msm_map_msm8960_io(void) +{ + msm_map_io(msm8960_io_desc, ARRAY_SIZE(msm8960_io_desc)); +} +#endif /* CONFIG_ARCH_MSM8960 */ + +#ifdef CONFIG_ARCH_MSM8930 +static struct map_desc msm8930_io_desc[] __initdata = { + MSM_CHIP_DEVICE(QGIC_DIST, MSM8930), + MSM_CHIP_DEVICE(QGIC_CPU, MSM8930), + MSM_CHIP_DEVICE(ACC0, MSM8930), + MSM_CHIP_DEVICE(ACC1, MSM8930), + MSM_CHIP_DEVICE(TMR, MSM8930), + MSM_CHIP_DEVICE(TMR0, MSM8930), + MSM_CHIP_DEVICE(RPM_MPM, MSM8930), + MSM_CHIP_DEVICE(CLK_CTL, MSM8930), + MSM_CHIP_DEVICE(MMSS_CLK_CTL, MSM8930), + MSM_CHIP_DEVICE(LPASS_CLK_CTL, MSM8930), + MSM_CHIP_DEVICE(RPM, MSM8930), + MSM_CHIP_DEVICE(TLMM, MSM8930), + MSM_CHIP_DEVICE(HFPLL, MSM8930), + MSM_CHIP_DEVICE(SAW0, MSM8930), + MSM_CHIP_DEVICE(SAW1, MSM8930), + MSM_CHIP_DEVICE(SAW_L2, MSM8930), + MSM_CHIP_DEVICE(SIC_NON_SECURE, MSM8930), + MSM_CHIP_DEVICE(APCS_GCC, MSM8930), + MSM_CHIP_DEVICE(IMEM, MSM8930), + MSM_CHIP_DEVICE(HDMI, MSM8930), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +#ifdef CONFIG_DEBUG_MSM8930_UART + MSM_DEVICE(DEBUG_UART), +#endif + MSM_CHIP_DEVICE(QFPROM, MSM8930), +}; + +void __init msm_map_msm8930_io(void) +{ + msm_map_io(msm8930_io_desc, ARRAY_SIZE(msm8930_io_desc)); +} +#endif /* CONFIG_ARCH_MSM8930 */ + +#ifdef CONFIG_ARCH_APQ8064 +static struct map_desc apq8064_io_desc[] __initdata = { + MSM_CHIP_DEVICE(QGIC_DIST, APQ8064), + MSM_CHIP_DEVICE(QGIC_CPU, APQ8064), + MSM_CHIP_DEVICE(TMR, APQ8064), + MSM_CHIP_DEVICE(TMR0, APQ8064), + MSM_CHIP_DEVICE(TLMM, APQ8064), + MSM_CHIP_DEVICE(ACC0, APQ8064), + MSM_CHIP_DEVICE(ACC1, APQ8064), + MSM_CHIP_DEVICE(ACC2, APQ8064), + MSM_CHIP_DEVICE(ACC3, APQ8064), + MSM_CHIP_DEVICE(HFPLL, APQ8064), + MSM_CHIP_DEVICE(CLK_CTL, APQ8064), + MSM_CHIP_DEVICE(MMSS_CLK_CTL, APQ8064), + MSM_CHIP_DEVICE(LPASS_CLK_CTL, APQ8064), + MSM_CHIP_DEVICE(APCS_GCC, APQ8064), + MSM_CHIP_DEVICE(RPM, APQ8064), + MSM_CHIP_DEVICE(RPM_MPM, APQ8064), + MSM_CHIP_DEVICE(SAW0, APQ8064), + MSM_CHIP_DEVICE(SAW1, APQ8064), + MSM_CHIP_DEVICE(SAW2, APQ8064), + MSM_CHIP_DEVICE(SAW3, APQ8064), + MSM_CHIP_DEVICE(SAW_L2, APQ8064), + MSM_CHIP_DEVICE(IMEM, APQ8064), + MSM_CHIP_DEVICE(HDMI, APQ8064), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, + MSM_CHIP_DEVICE(QFPROM, APQ8064), + MSM_CHIP_DEVICE(SIC_NON_SECURE, APQ8064), +#ifdef CONFIG_DEBUG_APQ8064_UART + MSM_DEVICE(DEBUG_UART), +#endif +}; + +void __init msm_map_apq8064_io(void) +{ + msm_map_io(apq8064_io_desc, ARRAY_SIZE(apq8064_io_desc)); +} +#endif /* CONFIG_ARCH_APQ8064 */ + +#ifdef CONFIG_ARCH_MSMCOPPER +static struct map_desc msm_copper_io_desc[] __initdata = { + MSM_CHIP_DEVICE(QGIC_DIST, COPPER), + MSM_CHIP_DEVICE(QGIC_CPU, COPPER), + MSM_CHIP_DEVICE(APCS_GCC, COPPER), + MSM_CHIP_DEVICE(TLMM, COPPER), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +#ifdef CONFIG_DEBUG_MSMCOPPER_UART + MSM_DEVICE(DEBUG_UART), +#endif +}; + +void __init msm_map_copper_io(void) +{ + msm_shared_ram_phys = COPPER_MSM_SHARED_RAM_PHYS; + msm_map_io(msm_copper_io_desc, ARRAY_SIZE(msm_copper_io_desc)); +} +#endif /* CONFIG_ARCH_MSMCOPPER */ + +#ifdef CONFIG_ARCH_MSM7X30 +static struct map_desc msm7x30_io_desc[] __initdata = { + MSM_CHIP_DEVICE(VIC, MSM7X30), + MSM_CHIP_DEVICE(CSR, MSM7X30), + MSM_CHIP_DEVICE(TMR, MSM7X30), + MSM_CHIP_DEVICE(GPIO1, MSM7X30), + MSM_CHIP_DEVICE(GPIO2, MSM7X30), + MSM_CHIP_DEVICE(CLK_CTL, MSM7X30), + MSM_CHIP_DEVICE(CLK_CTL_SH2, MSM7X30), + MSM_CHIP_DEVICE(AD5, MSM7X30), + MSM_CHIP_DEVICE(MDC, MSM7X30), + MSM_CHIP_DEVICE(ACC0, MSM7X30), + MSM_CHIP_DEVICE(SAW0, MSM7X30), + MSM_CHIP_DEVICE(APCS_GCC, MSM7X30), + MSM_CHIP_DEVICE(TCSR, MSM7X30), +#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \ + defined(CONFIG_DEBUG_MSM_UART3) + MSM_DEVICE(DEBUG_UART), +#endif + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, .length = MSM_SHARED_RAM_SIZE, .type = MT_DEVICE, }, @@ -168,21 +345,133 @@ static struct map_desc msm7x30_io_desc[] __initdata = { void __init msm_map_msm7x30_io(void) { - iotable_init(msm7x30_io_desc, ARRAY_SIZE(msm7x30_io_desc)); + msm_map_io(msm7x30_io_desc, ARRAY_SIZE(msm7x30_io_desc)); } #endif /* CONFIG_ARCH_MSM7X30 */ -void __iomem *__msm_ioremap_caller(unsigned long phys_addr, size_t size, - unsigned int mtype, void *caller) -{ - if (mtype == MT_DEVICE) { - /* The peripherals in the 88000000 - D0000000 range - * are only accessible by type MT_DEVICE_NONSHARED. - * Adjust mtype as necessary to make this "just work." - */ - if ((phys_addr >= 0x88000000) && (phys_addr < 0xD0000000)) - mtype = MT_DEVICE_NONSHARED; - } +#ifdef CONFIG_ARCH_FSM9XXX +static struct map_desc fsm9xxx_io_desc[] __initdata = { + MSM_DEVICE(VIC), + MSM_DEVICE(SIRC), + MSM_DEVICE(CSR), + MSM_DEVICE(TLMM), + MSM_DEVICE(TCSR), + MSM_DEVICE(CLK_CTL), + MSM_DEVICE(ACC), + MSM_DEVICE(SAW), + MSM_DEVICE(GCC), + MSM_DEVICE(GRFC), + MSM_DEVICE(QFP_FUSE), + MSM_DEVICE(HH), +#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \ + defined(CONFIG_DEBUG_MSM_UART3) + MSM_DEVICE(DEBUG_UART), +#endif + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +}; - return __arm_ioremap_caller(phys_addr, size, mtype, caller); +void __init msm_map_fsm9xxx_io(void) +{ + msm_map_io(fsm9xxx_io_desc, ARRAY_SIZE(fsm9xxx_io_desc)); } +#endif /* CONFIG_ARCH_FSM9XXX */ + +#ifdef CONFIG_ARCH_MSM9615 +static struct map_desc msm9615_io_desc[] __initdata = { + MSM_CHIP_DEVICE(QGIC_DIST, MSM9615), + MSM_CHIP_DEVICE(QGIC_CPU, MSM9615), + MSM_CHIP_DEVICE(ACC0, MSM9615), + MSM_CHIP_DEVICE(TMR, MSM9615), + MSM_CHIP_DEVICE(TLMM, MSM9615), + MSM_CHIP_DEVICE(SAW0, MSM9615), + MSM_CHIP_DEVICE(APCS_GCC, MSM9615), + MSM_CHIP_DEVICE(TCSR, MSM9615), + MSM_CHIP_DEVICE(L2CC, MSM9615), + MSM_CHIP_DEVICE(CLK_CTL, MSM9615), + MSM_CHIP_DEVICE(LPASS_CLK_CTL, MSM9615), + MSM_CHIP_DEVICE(RPM, MSM9615), + MSM_CHIP_DEVICE(RPM_MPM, MSM9615), + MSM_CHIP_DEVICE(APCS_GLB, MSM9615), + MSM_CHIP_DEVICE(IMEM, MSM9615), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, + MSM_CHIP_DEVICE(QFPROM, MSM9615), +}; + +void __init msm_map_msm9615_io(void) +{ + msm_map_io(msm9615_io_desc, ARRAY_SIZE(msm9615_io_desc)); +} +#endif /* CONFIG_ARCH_MSM9615 */ + +#ifdef CONFIG_ARCH_MSM8625 +static struct map_desc msm8625_io_desc[] __initdata = { + MSM_CHIP_DEVICE(CSR, MSM7XXX), + MSM_CHIP_DEVICE(GPIO1, MSM7XXX), + MSM_CHIP_DEVICE(GPIO2, MSM7XXX), + MSM_CHIP_DEVICE(QGIC_DIST, MSM8625), + MSM_CHIP_DEVICE(QGIC_CPU, MSM8625), + MSM_CHIP_DEVICE(TMR, MSM8625), + MSM_CHIP_DEVICE(TMR0, MSM8625), + MSM_CHIP_DEVICE(SCU, MSM8625), + MSM_CHIP_DEVICE(CFG_CTL, MSM8625), + MSM_CHIP_DEVICE(CLK_CTL, MSM8625), + MSM_CHIP_DEVICE(SAW0, MSM8625), + MSM_CHIP_DEVICE(SAW1, MSM8625), + MSM_CHIP_DEVICE(AD5, MSM7XXX), + MSM_CHIP_DEVICE(MDC, MSM7XXX), +#if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \ + defined(CONFIG_DEBUG_MSM_UART3) + MSM_DEVICE(DEBUG_UART), +#endif +#ifdef CONFIG_CACHE_L2X0 + { + .virtual = (unsigned long) MSM_L2CC_BASE, + .pfn = __phys_to_pfn(MSM7XXX_L2CC_PHYS), + .length = MSM7XXX_L2CC_SIZE, + .type = MT_DEVICE, + }, +#endif + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +}; + +void __init msm_map_msm8625_io(void) +{ + msm_map_io(msm8625_io_desc, ARRAY_SIZE(msm8625_io_desc)); +} +#else +void __init msm_map_msm8625_io(void) { return; } +#endif /* CONFIG_ARCH_MSM8625 */ + +#ifdef CONFIG_ARCH_MSM9625 +static struct map_desc msm9625_io_desc[] __initdata = { + MSM_CHIP_DEVICE(APCS_GCC, MSM9625), + MSM_CHIP_DEVICE(TLMM, MSM9625), + MSM_CHIP_DEVICE(TMR, MSM9625), + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +#ifdef CONFIG_DEBUG_MSM9625_UART + MSM_DEVICE(DEBUG_UART), +#endif +}; + +void __init msm_map_msm9625_io(void) +{ + msm_shared_ram_phys = MSM9625_SHARED_RAM_PHYS; + msm_map_io(msm9625_io_desc, ARRAY_SIZE(msm9625_io_desc)); +} +#endif /* CONFIG_ARCH_MSM9625 */ diff --git a/arch/arm/mach-msm/ipc_logging.c b/arch/arm/mach-msm/ipc_logging.c new file mode 100644 index 00000000000..2cd30ded331 --- /dev/null +++ b/arch/arm/mach-msm/ipc_logging.c @@ -0,0 +1,556 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipc_logging.h" + +static LIST_HEAD(ipc_log_context_list); +DEFINE_SPINLOCK(ipc_log_context_list_lock); +static atomic_t next_log_id = ATOMIC_INIT(0); + +static struct ipc_log_page *get_first_page(struct ipc_log_context *ilctxt) +{ + struct ipc_log_page_header *p_pghdr; + struct ipc_log_page *pg = NULL; + + if (!ilctxt) + return NULL; + p_pghdr = list_first_entry(&ilctxt->page_list, + struct ipc_log_page_header, list); + pg = container_of(p_pghdr, struct ipc_log_page, hdr); + return pg; +} + +static struct ipc_log_page *get_next_page(struct ipc_log_context *ilctxt, + struct ipc_log_page *cur_pg) +{ + struct ipc_log_page_header *p_pghdr; + struct ipc_log_page *pg = NULL; + + if (!ilctxt || !cur_pg) + return NULL; + + if (ilctxt->last_page == cur_pg) + return ilctxt->first_page; + + p_pghdr = list_first_entry(&cur_pg->hdr.list, + struct ipc_log_page_header, list); + pg = container_of(p_pghdr, struct ipc_log_page, hdr); + + return pg; +} + +/* If data == NULL, drop the log of size data_size*/ +static void ipc_log_read(struct ipc_log_context *ilctxt, + void *data, int data_size) +{ + int bytes_to_read; + + bytes_to_read = MIN(((PAGE_SIZE - sizeof(struct ipc_log_page_header)) + - ilctxt->read_page->hdr.read_offset), + data_size); + if (data) + memcpy(data, (ilctxt->read_page->data + + ilctxt->read_page->hdr.read_offset), bytes_to_read); + if (bytes_to_read != data_size) { + ilctxt->read_page->hdr.read_offset = 0xFFFF; + ilctxt->read_page = get_next_page(ilctxt, ilctxt->read_page); + ilctxt->read_page->hdr.read_offset = 0; + if (data) + memcpy((data + bytes_to_read), + (ilctxt->read_page->data + + ilctxt->read_page->hdr.read_offset), + (data_size - bytes_to_read)); + bytes_to_read = (data_size - bytes_to_read); + } + ilctxt->read_page->hdr.read_offset += bytes_to_read; + ilctxt->write_avail += data_size; +} + +/* + * Reads a message. + * + * If a message is read successfully, then the the message context + * will be set to: + * .hdr message header .size and .type values + * .offset beginning of message data + * + * @ectxt Message context and if NULL, drops the message. + * + * @returns 0 - no message available + * 1 - message read + */ +int msg_read(struct ipc_log_context *ilctxt, + struct encode_context *ectxt) +{ + struct tsv_header hdr; + + ipc_log_read(ilctxt, &hdr, sizeof(hdr)); + if (ectxt) { + ectxt->hdr.type = hdr.type; + ectxt->hdr.size = hdr.size; + ectxt->offset = sizeof(hdr); + ipc_log_read(ilctxt, (ectxt->buff + ectxt->offset), + (int)hdr.size); + } else { + ipc_log_read(ilctxt, NULL, (int)hdr.size); + } + return sizeof(hdr) + (int)hdr.size; +} + +/* + * Commits messages to the FIFO. If the FIFO is full, then enough + * messages are dropped to create space for the new message. + */ +void ipc_log_write(void *ctxt, struct encode_context *ectxt) +{ + struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt; + int bytes_to_write; + unsigned long flags; + + if (!ilctxt || !ectxt) { + pr_err("%s: Invalid ipc_log or encode context\n", __func__); + return; + } + + spin_lock_irqsave(&ipc_log_context_list_lock, flags); + spin_lock(&ilctxt->ipc_log_context_lock); + while (ilctxt->write_avail < ectxt->offset) + msg_read(ilctxt, NULL); + + bytes_to_write = MIN(((PAGE_SIZE - sizeof(struct ipc_log_page_header)) + - ilctxt->write_page->hdr.write_offset), + ectxt->offset); + memcpy((ilctxt->write_page->data + + ilctxt->write_page->hdr.write_offset), + ectxt->buff, bytes_to_write); + if (bytes_to_write != ectxt->offset) { + ilctxt->write_page->hdr.write_offset = 0xFFFF; + ilctxt->write_page = get_next_page(ilctxt, ilctxt->write_page); + ilctxt->write_page->hdr.write_offset = 0; + memcpy((ilctxt->write_page->data + + ilctxt->write_page->hdr.write_offset), + (ectxt->buff + bytes_to_write), + (ectxt->offset - bytes_to_write)); + bytes_to_write = (ectxt->offset - bytes_to_write); + } + ilctxt->write_page->hdr.write_offset += bytes_to_write; + ilctxt->write_avail -= ectxt->offset; + complete(&ilctxt->read_avail); + spin_unlock(&ilctxt->ipc_log_context_lock); + spin_unlock_irqrestore(&ipc_log_context_list_lock, flags); +} +EXPORT_SYMBOL(ipc_log_write); + +/* + * Starts a new message after which you can add serialized data and + * then complete the message by calling msg_encode_end(). + */ +void msg_encode_start(struct encode_context *ectxt, uint32_t type) +{ + if (!ectxt) { + pr_err("%s: Invalid encode context\n", __func__); + return; + } + + ectxt->hdr.type = type; + ectxt->hdr.size = 0; + ectxt->offset = sizeof(ectxt->hdr); +} +EXPORT_SYMBOL(msg_encode_start); + +/* + * Completes the message + */ +void msg_encode_end(struct encode_context *ectxt) +{ + if (!ectxt) { + pr_err("%s: Invalid encode context\n", __func__); + return; + } + + /* finalize data size */ + ectxt->hdr.size = ectxt->offset - sizeof(ectxt->hdr); + BUG_ON(ectxt->hdr.size > MAX_MSG_SIZE); + memcpy(ectxt->buff, &ectxt->hdr, sizeof(ectxt->hdr)); +} +EXPORT_SYMBOL(msg_encode_end); + +/* + * Helper funtion used to write data to a message context. + * + * @ectxt context initialized by calling msg_encode_start() + * @data data to write + * @size number of bytes of data to write + */ +static inline int tsv_write_data(struct encode_context *ectxt, + void *data, uint32_t size) +{ + if (!ectxt) { + pr_err("%s: Invalid encode context\n", __func__); + return -EINVAL; + } + if ((ectxt->offset + size) > MAX_MSG_SIZE) { + pr_err("%s: No space to encode further\n", __func__); + return -EINVAL; + } + + memcpy((void *)(ectxt->buff + ectxt->offset), data, size); + ectxt->offset += size; + return 0; +} + +/* + * Helper function that writes a type to the context. + * + * @ectxt context initialized by calling msg_encode_start() + * @type primitive type + * @size size of primitive in bytes + */ +static inline int tsv_write_header(struct encode_context *ectxt, + uint32_t type, uint32_t size) +{ + struct tsv_header hdr; + + hdr.type = (unsigned char)type; + hdr.size = (unsigned char)size; + return tsv_write_data(ectxt, &hdr, sizeof(hdr)); +} + +/* + * Writes the current timestamp count. + * + * @ectxt context initialized by calling msg_encode_start() + */ +int tsv_timestamp_write(struct encode_context *ectxt) +{ + int ret; + unsigned long long t_now = sched_clock(); + + ret = tsv_write_header(ectxt, TSV_TYPE_TIMESTAMP, sizeof(t_now)); + if (ret) + return ret; + return tsv_write_data(ectxt, &t_now, sizeof(t_now)); +} +EXPORT_SYMBOL(tsv_timestamp_write); + +/* + * Writes a data pointer. + * + * @ectxt context initialized by calling msg_encode_start() + * @pointer pointer value to write + */ +int tsv_pointer_write(struct encode_context *ectxt, void *pointer) +{ + int ret; + ret = tsv_write_header(ectxt, TSV_TYPE_POINTER, sizeof(pointer)); + if (ret) + return ret; + return tsv_write_data(ectxt, &pointer, sizeof(pointer)); +} +EXPORT_SYMBOL(tsv_pointer_write); + +/* + * Writes a 32-bit integer value. + * + * @ectxt context initialized by calling msg_encode_start() + * @n integer to write + */ +int tsv_int32_write(struct encode_context *ectxt, int32_t n) +{ + int ret; + ret = tsv_write_header(ectxt, TSV_TYPE_INT32, sizeof(n)); + if (ret) + return ret; + return tsv_write_data(ectxt, &n, sizeof(n)); +} +EXPORT_SYMBOL(tsv_int32_write); + +/* + * Writes a byte array. + * + * @ectxt context initialized by calling msg_write_start() + * @data Beginning address of data + * @data_size Size of data to be written + */ +int tsv_byte_array_write(struct encode_context *ectxt, + void *data, int data_size) +{ + int ret; + ret = tsv_write_header(ectxt, TSV_TYPE_BYTE_ARRAY, data_size); + if (ret) + return ret; + return tsv_write_data(ectxt, data, data_size); +} +EXPORT_SYMBOL(tsv_byte_array_write); + +/* + * Helper function to log a string + * + * @ilctxt ipc_log_context created using ipc_log_context_create() + * @fmt Data specified using format specifiers + */ +int ipc_log_string(void *ilctxt, const char *fmt, ...) +{ + struct encode_context ectxt; + int avail_size, data_size, hdr_size = sizeof(struct tsv_header); + va_list arg_list; + + if (!ilctxt) + return -EINVAL; + + msg_encode_start(&ectxt, TSV_TYPE_STRING); + tsv_timestamp_write(&ectxt); + avail_size = (MAX_MSG_SIZE - (ectxt.offset + hdr_size)); + va_start(arg_list, fmt); + data_size = vsnprintf((ectxt.buff + ectxt.offset + hdr_size), + avail_size, fmt, arg_list); + va_end(arg_list); + tsv_write_header(&ectxt, TSV_TYPE_BYTE_ARRAY, data_size); + ectxt.offset += data_size; + msg_encode_end(&ectxt); + ipc_log_write(ilctxt, &ectxt); + return 0; +} + +/* + * Helper funtion used to read data from a message context. + * + * @ectxt context initialized by calling msg_read() + * @data data to read + * @size number of bytes of data to read + */ +static void tsv_read_data(struct encode_context *ectxt, + void *data, uint32_t size) +{ + BUG_ON((ectxt->offset + size) > MAX_MSG_SIZE); + memcpy(data, (ectxt->buff + ectxt->offset), size); + ectxt->offset += size; +} + +/* + * Helper function that reads a type from the context and updates the + * context pointers. + * + * @ectxt context initialized by calling msg_read() + * @hdr type header + */ +static void tsv_read_header(struct encode_context *ectxt, + struct tsv_header *hdr) +{ + BUG_ON((ectxt->offset + sizeof(*hdr)) > MAX_MSG_SIZE); + memcpy(hdr, (ectxt->buff + ectxt->offset), sizeof(*hdr)); + ectxt->offset += sizeof(*hdr); +} + +/* + * Reads a timestamp. + * + * @ectxt context initialized by calling msg_read() + * @dctxt deserialization context + * @format output format (appended to %6u.%09u timestamp format) + */ +void tsv_timestamp_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) +{ + struct tsv_header hdr; + unsigned long long val; + unsigned long nanosec_rem; + + tsv_read_header(ectxt, &hdr); + BUG_ON(hdr.type != TSV_TYPE_TIMESTAMP); + tsv_read_data(ectxt, &val, sizeof(val)); + nanosec_rem = do_div(val, 1000000000U); + IPC_SPRINTF_DECODE(dctxt, "[%6u.%09lu]%s", + (unsigned)val, nanosec_rem, format); +} +EXPORT_SYMBOL(tsv_timestamp_read); + +/* + * Reads a data pointer. + * + * @ectxt context initialized by calling msg_read() + * @dctxt deserialization context + * @format output format + */ +void tsv_pointer_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) +{ + struct tsv_header hdr; + void *val; + + tsv_read_header(ectxt, &hdr); + BUG_ON(hdr.type != TSV_TYPE_POINTER); + tsv_read_data(ectxt, &val, sizeof(val)); + + IPC_SPRINTF_DECODE(dctxt, format, val); +} +EXPORT_SYMBOL(tsv_pointer_read); + +/* + * Reads a 32-bit integer value. + * + * @ectxt context initialized by calling msg_read() + * @dctxt deserialization context + * @format output format + */ +int32_t tsv_int32_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) +{ + struct tsv_header hdr; + int32_t val; + + tsv_read_header(ectxt, &hdr); + BUG_ON(hdr.type != TSV_TYPE_INT32); + tsv_read_data(ectxt, &val, sizeof(val)); + + IPC_SPRINTF_DECODE(dctxt, format, val); + return val; +} +EXPORT_SYMBOL(tsv_int32_read); + +/* + * Reads a byte array/string. + * + * @ectxt context initialized by calling msg_read() + * @dctxt deserialization context + * @format output format + */ +void tsv_byte_array_read(struct encode_context *ectxt, + struct decode_context *dctxt, const char *format) +{ + struct tsv_header hdr; + + tsv_read_header(ectxt, &hdr); + BUG_ON(hdr.type != TSV_TYPE_BYTE_ARRAY); + tsv_read_data(ectxt, dctxt->buff, hdr.size); + dctxt->buff += hdr.size; + dctxt->size -= hdr.size; +} +EXPORT_SYMBOL(tsv_byte_array_read); + +int add_deserialization_func(void *ctxt, int type, + void (*dfunc)(struct encode_context *, + struct decode_context *)) +{ + struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt; + struct dfunc_info *df_info; + unsigned long flags; + + if (!ilctxt || !dfunc) + return -EINVAL; + + df_info = kmalloc(sizeof(struct dfunc_info), GFP_KERNEL); + if (!df_info) + return -ENOSPC; + + spin_lock_irqsave(&ipc_log_context_list_lock, flags); + spin_lock(&ilctxt->ipc_log_context_lock); + df_info->type = type; + df_info->dfunc = dfunc; + list_add_tail(&df_info->list, &ilctxt->dfunc_info_list); + spin_unlock(&ilctxt->ipc_log_context_lock); + spin_unlock_irqrestore(&ipc_log_context_list_lock, flags); + return 0; +} +EXPORT_SYMBOL(add_deserialization_func); + +void *ipc_log_context_create(int max_num_pages, + const char *mod_name) +{ + struct ipc_log_context *ctxt; + struct ipc_log_page *pg = NULL; + int page_cnt, local_log_id; + unsigned long flags; + + ctxt = kzalloc(sizeof(struct ipc_log_context), GFP_KERNEL); + if (!ctxt) { + pr_err("%s: cannot create ipc_log_context\n", __func__); + return 0; + } + + local_log_id = atomic_add_return(1, &next_log_id); + init_completion(&ctxt->read_avail); + INIT_LIST_HEAD(&ctxt->page_list); + INIT_LIST_HEAD(&ctxt->dfunc_info_list); + spin_lock_init(&ctxt->ipc_log_context_lock); + for (page_cnt = 0; page_cnt < max_num_pages; page_cnt++) { + pg = kzalloc(sizeof(struct ipc_log_page), GFP_KERNEL); + if (!pg) { + pr_err("%s: cannot create ipc_log_page\n", __func__); + goto release_ipc_log_context; + } + pg->hdr.magic = IPC_LOGGING_MAGIC_NUM; + pg->hdr.nmagic = ~(IPC_LOGGING_MAGIC_NUM); + pg->hdr.log_id = (uint32_t)local_log_id; + pg->hdr.page_num = page_cnt; + pg->hdr.read_offset = 0xFFFF; + pg->hdr.write_offset = 0xFFFF; + spin_lock_irqsave(&ctxt->ipc_log_context_lock, flags); + list_add_tail(&pg->hdr.list, &ctxt->page_list); + spin_unlock_irqrestore(&ctxt->ipc_log_context_lock, flags); + } + ctxt->first_page = get_first_page(ctxt); + ctxt->last_page = pg; + ctxt->write_page = ctxt->first_page; + ctxt->read_page = ctxt->first_page; + ctxt->write_page->hdr.write_offset = 0; + ctxt->read_page->hdr.read_offset = 0; + ctxt->write_avail = max_num_pages * (PAGE_SIZE - + sizeof(struct ipc_log_page_header)); + + create_ctx_debugfs(ctxt, mod_name); + + spin_lock_irqsave(&ipc_log_context_list_lock, flags); + list_add_tail(&ctxt->list, &ipc_log_context_list); + spin_unlock_irqrestore(&ipc_log_context_list_lock, flags); + return (void *)ctxt; + +release_ipc_log_context: + while (page_cnt-- > 0) { + pg = get_first_page(ctxt); + list_del(&pg->hdr.list); + kfree(pg); + } + kfree(ctxt); + return 0; +} +EXPORT_SYMBOL(ipc_log_context_create); + +static int __init ipc_logging_init(void) +{ + check_and_create_debugfs(); + return 0; +} + +module_init(ipc_logging_init); + +MODULE_DESCRIPTION("ipc logging"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/ipc_logging.h b/arch/arm/mach-msm/ipc_logging.h new file mode 100644 index 00000000000..5e614abc184 --- /dev/null +++ b/arch/arm/mach-msm/ipc_logging.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _IPC_LOGGING_H +#define _IPC_LOGGING_H + +struct ipc_log_page_header { + uint32_t magic; + uint32_t nmagic; /* inverse of magic number */ + uint32_t log_id; /* owner of log */ + uint32_t page_num; + uint16_t read_offset; + uint16_t write_offset; + struct list_head list; +}; + +struct ipc_log_page { + struct ipc_log_page_header hdr; + char data[PAGE_SIZE - sizeof(struct ipc_log_page_header)]; +}; + +struct ipc_log_context { + struct list_head list; + struct list_head page_list; + struct ipc_log_page *first_page; + struct ipc_log_page *last_page; + struct ipc_log_page *write_page; + struct ipc_log_page *read_page; + uint32_t write_avail; + struct dentry *dent; + struct list_head dfunc_info_list; + spinlock_t ipc_log_context_lock; + struct completion read_avail; +}; + +struct dfunc_info { + struct list_head list; + int type; + void (*dfunc) (struct encode_context *, struct decode_context *); +}; + +enum { + TSV_TYPE_INVALID, + TSV_TYPE_TIMESTAMP, + TSV_TYPE_POINTER, + TSV_TYPE_INT32, + TSV_TYPE_BYTE_ARRAY, +}; + +enum { + OUTPUT_DEBUGFS, +}; + +#define IPC_LOGGING_MAGIC_NUM 0x52784425 +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define IS_MSG_TYPE(x) (((x) > TSV_TYPE_MSG_START) && \ + ((x) < TSV_TYPE_MSG_END)) + +extern spinlock_t ipc_log_context_list_lock; + +extern int msg_read(struct ipc_log_context *ilctxt, + struct encode_context *ectxt); + +static inline int is_ilctxt_empty(struct ipc_log_context *ilctxt) +{ + if (!ilctxt) + return -EINVAL; + + return ((ilctxt->read_page == ilctxt->write_page) && + (ilctxt->read_page->hdr.read_offset == + ilctxt->write_page->hdr.write_offset)); +} + +#if (defined(CONFIG_DEBUG_FS)) +void check_and_create_debugfs(void); + +void create_ctx_debugfs(struct ipc_log_context *ctxt, + const char *mod_name); +#else +void check_and_create_debugfs(void) +{ +} + +void create_ctx_debugfs(struct ipc_log_context *ctxt, const char *mod_name) +{ +} +#endif + +#endif diff --git a/arch/arm/mach-msm/ipc_logging_debug.c b/arch/arm/mach-msm/ipc_logging_debug.c new file mode 100644 index 00000000000..5f7a95e000a --- /dev/null +++ b/arch/arm/mach-msm/ipc_logging_debug.c @@ -0,0 +1,225 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipc_logging.h" + +static DEFINE_MUTEX(ipc_log_debugfs_init_lock); +static struct dentry *root_dent; +#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4) + +static void *get_deserialization_func(struct ipc_log_context *ilctxt, + int type) +{ + struct dfunc_info *df_info = NULL; + + if (!ilctxt) + return NULL; + + list_for_each_entry(df_info, &ilctxt->dfunc_info_list, list) { + if (df_info->type == type) + return df_info->dfunc; + } + return NULL; +} + +static int deserialize_log(struct ipc_log_context *ilctxt, + char *buff, int size) +{ + struct encode_context ectxt; + struct decode_context dctxt; + void (*deserialize_func)(struct encode_context *ectxt, + struct decode_context *dctxt); + unsigned long flags; + + dctxt.output_format = OUTPUT_DEBUGFS; + dctxt.buff = buff; + dctxt.size = size; + spin_lock_irqsave(&ipc_log_context_list_lock, flags); + spin_lock(&ilctxt->ipc_log_context_lock); + while (dctxt.size >= MAX_MSG_DECODED_SIZE && + !is_ilctxt_empty(ilctxt)) { + msg_read(ilctxt, &ectxt); + deserialize_func = get_deserialization_func(ilctxt, + ectxt.hdr.type); + spin_unlock(&ilctxt->ipc_log_context_lock); + spin_unlock_irqrestore(&ipc_log_context_list_lock, flags); + if (deserialize_func) + deserialize_func(&ectxt, &dctxt); + else + pr_err("%s: unknown message 0x%x\n", + __func__, ectxt.hdr.type); + spin_lock_irqsave(&ipc_log_context_list_lock, flags); + spin_lock(&ilctxt->ipc_log_context_lock); + } + if ((size - dctxt.size) == 0) + init_completion(&ilctxt->read_avail); + spin_unlock(&ilctxt->ipc_log_context_lock); + spin_unlock_irqrestore(&ipc_log_context_list_lock, flags); + return size - dctxt.size; +} + +static int debug_log(struct ipc_log_context *ilctxt, + char *buff, int size, int cont) +{ + int i = 0; + + if (size < MAX_MSG_DECODED_SIZE) { + pr_err("%s: buffer size %d < %d\n", __func__, size, + MAX_MSG_DECODED_SIZE); + return -ENOMEM; + } + do { + i = deserialize_log(ilctxt, buff, size - 1); + if (cont && i == 0) { + wait_for_completion_interruptible(&ilctxt->read_avail); + if (signal_pending(current)) + break; + } + } while (cont && i == 0); + + return i; +} + +/* + * VFS Read operation helper which dispatches the call to the debugfs + * read command stored in file->private_data. + * + * @file File structure + * @buff user buffer + * @count size of user buffer + * @ppos file position to read from (only a value of 0 is accepted) + * @cont 1 = continuous mode (don't return 0 to signal end-of-file) + * + * @returns ==0 end of file + * >0 number of bytes read + * <0 error + */ +static ssize_t debug_read_helper(struct file *file, char __user *buff, + size_t count, loff_t *ppos, int cont) +{ + struct ipc_log_context *ilctxt = file->private_data; + char *buffer; + int bsize; + + buffer = kmalloc(count, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + bsize = debug_log(ilctxt, buffer, count, cont); + if (bsize > 0) { + if (copy_to_user(buff, buffer, bsize)) { + kfree(buffer); + return -EFAULT; + } + *ppos += bsize; + } + kfree(buffer); + return bsize; +} + +static ssize_t debug_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + return debug_read_helper(file, buff, count, ppos, 0); +} + +static ssize_t debug_read_cont(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + return debug_read_helper(file, buff, count, ppos, 1); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static const struct file_operations debug_ops_cont = { + .read = debug_read_cont, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + struct ipc_log_context *ilctxt, + const struct file_operations *fops) +{ + debugfs_create_file(name, mode, dent, ilctxt, fops); +} + +static void dfunc_string(struct encode_context *ectxt, + struct decode_context *dctxt) +{ + tsv_timestamp_read(ectxt, dctxt, " "); + tsv_byte_array_read(ectxt, dctxt, ""); +} + +void check_and_create_debugfs(void) +{ + mutex_lock(&ipc_log_debugfs_init_lock); + if (!root_dent) { + root_dent = debugfs_create_dir("ipc_logging", 0); + + if (IS_ERR(root_dent)) { + pr_err("%s: unable to create debugfs %ld\n", + __func__, IS_ERR(root_dent)); + root_dent = NULL; + } + } + mutex_unlock(&ipc_log_debugfs_init_lock); +} +EXPORT_SYMBOL(check_and_create_debugfs); + +void create_ctx_debugfs(struct ipc_log_context *ctxt, + const char *mod_name) +{ + if (!root_dent) + check_and_create_debugfs(); + + if (root_dent) { + ctxt->dent = debugfs_create_dir(mod_name, root_dent); + if (!IS_ERR(ctxt->dent)) { + debug_create("log", 0444, ctxt->dent, + ctxt, &debug_ops); + debug_create("log_cont", 0444, ctxt->dent, + ctxt, &debug_ops_cont); + } + } + add_deserialization_func((void *)ctxt, + TSV_TYPE_STRING, dfunc_string); +} +EXPORT_SYMBOL(create_ctx_debugfs); diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c new file mode 100644 index 00000000000..6fa435a43ea --- /dev/null +++ b/arch/arm/mach-msm/ipc_router.c @@ -0,0 +1,2555 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "ipc_router.h" +#include "modem_notifier.h" + +enum { + SMEM_LOG = 1U << 0, + RTR_DBG = 1U << 1, + R2R_MSG = 1U << 2, + R2R_RAW = 1U << 3, + NTFY_MSG = 1U << 4, + R2R_RAW_HDR = 1U << 5, +}; + +static int msm_ipc_router_debug_mask; +module_param_named(debug_mask, msm_ipc_router_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define DIAG(x...) pr_info("[RR] ERROR " x) + +#if defined(DEBUG) +#define D(x...) do { \ +if (msm_ipc_router_debug_mask & RTR_DBG) \ + pr_info(x); \ +} while (0) + +#define RR(x...) do { \ +if (msm_ipc_router_debug_mask & R2R_MSG) \ + pr_info("[RR] "x); \ +} while (0) + +#define RAW(x...) do { \ +if (msm_ipc_router_debug_mask & R2R_RAW) \ + pr_info("[RAW] "x); \ +} while (0) + +#define NTFY(x...) do { \ +if (msm_ipc_router_debug_mask & NTFY_MSG) \ + pr_info("[NOTIFY] "x); \ +} while (0) + +#define RAW_HDR(x...) do { \ +if (msm_ipc_router_debug_mask & R2R_RAW_HDR) \ + pr_info("[HDR] "x); \ +} while (0) +#else +#define D(x...) do { } while (0) +#define RR(x...) do { } while (0) +#define RAW(x...) do { } while (0) +#define RAW_HDR(x...) do { } while (0) +#define NTFY(x...) do { } while (0) +#endif + +#define IPC_ROUTER_LOG_EVENT_ERROR 0x10 +#define IPC_ROUTER_LOG_EVENT_TX 0x11 +#define IPC_ROUTER_LOG_EVENT_RX 0x12 + +static LIST_HEAD(control_ports); +static DEFINE_MUTEX(control_ports_lock); + +#define LP_HASH_SIZE 32 +static struct list_head local_ports[LP_HASH_SIZE]; +static DEFINE_MUTEX(local_ports_lock); + +#define SRV_HASH_SIZE 32 +static struct list_head server_list[SRV_HASH_SIZE]; +static DEFINE_MUTEX(server_list_lock); +static wait_queue_head_t newserver_wait; + +struct msm_ipc_server { + struct list_head list; + struct msm_ipc_port_name name; + struct list_head server_port_list; +}; + +struct msm_ipc_server_port { + struct list_head list; + struct msm_ipc_port_addr server_addr; + struct msm_ipc_router_xprt_info *xprt_info; +}; + +#define RP_HASH_SIZE 32 +struct msm_ipc_router_remote_port { + struct list_head list; + uint32_t node_id; + uint32_t port_id; + uint32_t restart_state; + wait_queue_head_t quota_wait; + uint32_t tx_quota_cnt; + struct mutex quota_lock; +}; + +struct msm_ipc_router_xprt_info { + struct list_head list; + struct msm_ipc_router_xprt *xprt; + uint32_t remote_node_id; + uint32_t initialized; + struct list_head pkt_list; + wait_queue_head_t read_wait; + struct wake_lock wakelock; + struct mutex rx_lock; + struct mutex tx_lock; + uint32_t need_len; + uint32_t abort_data_read; + struct work_struct read_data; + struct workqueue_struct *workqueue; +}; + +#define RT_HASH_SIZE 4 +struct msm_ipc_routing_table_entry { + struct list_head list; + uint32_t node_id; + uint32_t neighbor_node_id; + struct list_head remote_port_list[RP_HASH_SIZE]; + struct msm_ipc_router_xprt_info *xprt_info; + struct mutex lock; + unsigned long num_tx_bytes; + unsigned long num_rx_bytes; +}; + +static struct list_head routing_table[RT_HASH_SIZE]; +static DEFINE_MUTEX(routing_table_lock); +static int routing_table_inited; + +static LIST_HEAD(msm_ipc_board_dev_list); +static DEFINE_MUTEX(msm_ipc_board_dev_list_lock); + +static void do_read_data(struct work_struct *work); + +#define RR_STATE_IDLE 0 +#define RR_STATE_HEADER 1 +#define RR_STATE_BODY 2 +#define RR_STATE_ERROR 3 + +#define RESTART_NORMAL 0 +#define RESTART_PEND 1 + +/* State for remote ep following restart */ +#define RESTART_QUOTA_ABORT 1 + +static LIST_HEAD(xprt_info_list); +static DEFINE_MUTEX(xprt_info_list_lock); + +DECLARE_COMPLETION(msm_ipc_remote_router_up); +static DECLARE_COMPLETION(msm_ipc_local_router_up); +#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ) + +static uint32_t next_port_id; +static DEFINE_MUTEX(next_port_id_lock); +static atomic_t pending_close_count = ATOMIC_INIT(0); +static wait_queue_head_t subsystem_restart_wait; +static struct workqueue_struct *msm_ipc_router_workqueue; + +enum { + CLIENT_PORT, + SERVER_PORT, + CONTROL_PORT, +}; + +enum { + DOWN, + UP, +}; + +static void init_routing_table(void) +{ + int i; + for (i = 0; i < RT_HASH_SIZE; i++) + INIT_LIST_HEAD(&routing_table[i]); +} + +static struct msm_ipc_routing_table_entry *alloc_routing_table_entry( + uint32_t node_id) +{ + int i; + struct msm_ipc_routing_table_entry *rt_entry; + + rt_entry = kmalloc(sizeof(struct msm_ipc_routing_table_entry), + GFP_KERNEL); + if (!rt_entry) { + pr_err("%s: rt_entry allocation failed for %d\n", + __func__, node_id); + return NULL; + } + + for (i = 0; i < RP_HASH_SIZE; i++) + INIT_LIST_HEAD(&rt_entry->remote_port_list[i]); + + mutex_init(&rt_entry->lock); + rt_entry->node_id = node_id; + rt_entry->xprt_info = NULL; + return rt_entry; +} + +/*Please take routing_table_lock before calling this function*/ +static int add_routing_table_entry( + struct msm_ipc_routing_table_entry *rt_entry) +{ + uint32_t key; + + if (!rt_entry) + return -EINVAL; + + key = (rt_entry->node_id % RT_HASH_SIZE); + list_add_tail(&rt_entry->list, &routing_table[key]); + return 0; +} + +/*Please take routing_table_lock before calling this function*/ +static struct msm_ipc_routing_table_entry *lookup_routing_table( + uint32_t node_id) +{ + uint32_t key = (node_id % RT_HASH_SIZE); + struct msm_ipc_routing_table_entry *rt_entry; + + list_for_each_entry(rt_entry, &routing_table[key], list) { + if (rt_entry->node_id == node_id) + return rt_entry; + } + return NULL; +} + +struct rr_packet *rr_read(struct msm_ipc_router_xprt_info *xprt_info) +{ + struct rr_packet *temp_pkt; + + if (!xprt_info) + return NULL; + + mutex_lock(&xprt_info->rx_lock); + while (!(xprt_info->abort_data_read) && + list_empty(&xprt_info->pkt_list)) { + mutex_unlock(&xprt_info->rx_lock); + wait_event(xprt_info->read_wait, + ((xprt_info->abort_data_read) || + !list_empty(&xprt_info->pkt_list))); + mutex_lock(&xprt_info->rx_lock); + } + if (xprt_info->abort_data_read) { + mutex_unlock(&xprt_info->rx_lock); + return NULL; + } + + temp_pkt = list_first_entry(&xprt_info->pkt_list, + struct rr_packet, list); + list_del(&temp_pkt->list); + if (list_empty(&xprt_info->pkt_list)) + wake_unlock(&xprt_info->wakelock); + mutex_unlock(&xprt_info->rx_lock); + return temp_pkt; +} + +struct rr_packet *clone_pkt(struct rr_packet *pkt) +{ + struct rr_packet *cloned_pkt; + struct sk_buff *temp_skb, *cloned_skb; + struct sk_buff_head *pkt_fragment_q; + + cloned_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL); + if (!cloned_pkt) { + pr_err("%s: failure\n", __func__); + return NULL; + } + + pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL); + if (!pkt_fragment_q) { + pr_err("%s: pkt_frag_q alloc failure\n", __func__); + kfree(cloned_pkt); + return NULL; + } + skb_queue_head_init(pkt_fragment_q); + + skb_queue_walk(pkt->pkt_fragment_q, temp_skb) { + cloned_skb = skb_clone(temp_skb, GFP_KERNEL); + if (!cloned_skb) + goto fail_clone; + skb_queue_tail(pkt_fragment_q, cloned_skb); + } + cloned_pkt->pkt_fragment_q = pkt_fragment_q; + cloned_pkt->length = pkt->length; + return cloned_pkt; + +fail_clone: + while (!skb_queue_empty(pkt_fragment_q)) { + temp_skb = skb_dequeue(pkt_fragment_q); + kfree_skb(temp_skb); + } + kfree(pkt_fragment_q); + kfree(cloned_pkt); + return NULL; +} + +struct rr_packet *create_pkt(struct sk_buff_head *data) +{ + struct rr_packet *pkt; + struct sk_buff *temp_skb; + + pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL); + if (!pkt) { + pr_err("%s: failure\n", __func__); + return NULL; + } + + pkt->pkt_fragment_q = data; + skb_queue_walk(pkt->pkt_fragment_q, temp_skb) + pkt->length += temp_skb->len; + return pkt; +} + +void release_pkt(struct rr_packet *pkt) +{ + struct sk_buff *temp_skb; + + if (!pkt) + return; + + if (!pkt->pkt_fragment_q) { + kfree(pkt); + return; + } + + while (!skb_queue_empty(pkt->pkt_fragment_q)) { + temp_skb = skb_dequeue(pkt->pkt_fragment_q); + kfree_skb(temp_skb); + } + kfree(pkt->pkt_fragment_q); + kfree(pkt); + return; +} + +static int post_control_ports(struct rr_packet *pkt) +{ + struct msm_ipc_port *port_ptr; + struct rr_packet *cloned_pkt; + + if (!pkt) + return -EINVAL; + + mutex_lock(&control_ports_lock); + list_for_each_entry(port_ptr, &control_ports, list) { + mutex_lock(&port_ptr->port_rx_q_lock); + cloned_pkt = clone_pkt(pkt); + wake_lock(&port_ptr->port_rx_wake_lock); + list_add_tail(&cloned_pkt->list, &port_ptr->port_rx_q); + wake_up(&port_ptr->port_rx_wait_q); + mutex_unlock(&port_ptr->port_rx_q_lock); + } + mutex_unlock(&control_ports_lock); + return 0; +} + +static uint32_t allocate_port_id(void) +{ + uint32_t port_id = 0, prev_port_id, key; + struct msm_ipc_port *port_ptr; + + mutex_lock(&next_port_id_lock); + prev_port_id = next_port_id; + mutex_lock(&local_ports_lock); + do { + next_port_id++; + if ((next_port_id & 0xFFFFFFFE) == 0xFFFFFFFE) + next_port_id = 1; + + key = (next_port_id & (LP_HASH_SIZE - 1)); + if (list_empty(&local_ports[key])) { + port_id = next_port_id; + break; + } + list_for_each_entry(port_ptr, &local_ports[key], list) { + if (port_ptr->this_port.port_id == next_port_id) { + port_id = next_port_id; + break; + } + } + if (!port_id) { + port_id = next_port_id; + break; + } + port_id = 0; + } while (next_port_id != prev_port_id); + mutex_unlock(&local_ports_lock); + mutex_unlock(&next_port_id_lock); + + return port_id; +} + +void msm_ipc_router_add_local_port(struct msm_ipc_port *port_ptr) +{ + uint32_t key; + + if (!port_ptr) + return; + + key = (port_ptr->this_port.port_id & (LP_HASH_SIZE - 1)); + mutex_lock(&local_ports_lock); + list_add_tail(&port_ptr->list, &local_ports[key]); + mutex_unlock(&local_ports_lock); +} + +struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint, + void (*notify)(unsigned event, void *data, + void *addr, void *priv), + void *priv) +{ + struct msm_ipc_port *port_ptr; + + port_ptr = kzalloc(sizeof(struct msm_ipc_port), GFP_KERNEL); + if (!port_ptr) + return NULL; + + port_ptr->this_port.node_id = IPC_ROUTER_NID_LOCAL; + port_ptr->this_port.port_id = allocate_port_id(); + if (!port_ptr->this_port.port_id) { + pr_err("%s: All port ids are in use\n", __func__); + kfree(port_ptr); + return NULL; + } + + spin_lock_init(&port_ptr->port_lock); + INIT_LIST_HEAD(&port_ptr->incomplete); + mutex_init(&port_ptr->incomplete_lock); + INIT_LIST_HEAD(&port_ptr->port_rx_q); + mutex_init(&port_ptr->port_rx_q_lock); + init_waitqueue_head(&port_ptr->port_rx_wait_q); + snprintf(port_ptr->rx_wakelock_name, MAX_WAKELOCK_NAME_SZ, + "msm_ipc_read%08x:%08x", + port_ptr->this_port.node_id, + port_ptr->this_port.port_id); + wake_lock_init(&port_ptr->port_rx_wake_lock, + WAKE_LOCK_SUSPEND, port_ptr->rx_wakelock_name); + + port_ptr->endpoint = endpoint; + port_ptr->notify = notify; + port_ptr->priv = priv; + + msm_ipc_router_add_local_port(port_ptr); + return port_ptr; +} + +static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id) +{ + int key = (port_id & (LP_HASH_SIZE - 1)); + struct msm_ipc_port *port_ptr; + + mutex_lock(&local_ports_lock); + list_for_each_entry(port_ptr, &local_ports[key], list) { + if (port_ptr->this_port.port_id == port_id) { + mutex_unlock(&local_ports_lock); + return port_ptr; + } + } + mutex_unlock(&local_ports_lock); + return NULL; +} + +static struct msm_ipc_router_remote_port *msm_ipc_router_lookup_remote_port( + uint32_t node_id, + uint32_t port_id) +{ + struct msm_ipc_router_remote_port *rport_ptr; + struct msm_ipc_routing_table_entry *rt_entry; + int key = (port_id & (RP_HASH_SIZE - 1)); + + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(node_id); + if (!rt_entry) { + mutex_unlock(&routing_table_lock); + pr_err("%s: Node is not up\n", __func__); + return NULL; + } + + mutex_lock(&rt_entry->lock); + list_for_each_entry(rport_ptr, + &rt_entry->remote_port_list[key], list) { + if (rport_ptr->port_id == port_id) { + if (rport_ptr->restart_state != RESTART_NORMAL) + rport_ptr = NULL; + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + return rport_ptr; + } + } + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + return NULL; +} + +static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port( + uint32_t node_id, + uint32_t port_id) +{ + struct msm_ipc_router_remote_port *rport_ptr; + struct msm_ipc_routing_table_entry *rt_entry; + int key = (port_id & (RP_HASH_SIZE - 1)); + + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(node_id); + if (!rt_entry) { + mutex_unlock(&routing_table_lock); + pr_err("%s: Node is not up\n", __func__); + return NULL; + } + + mutex_lock(&rt_entry->lock); + rport_ptr = kmalloc(sizeof(struct msm_ipc_router_remote_port), + GFP_KERNEL); + if (!rport_ptr) { + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + pr_err("%s: Remote port alloc failed\n", __func__); + return NULL; + } + rport_ptr->port_id = port_id; + rport_ptr->node_id = node_id; + rport_ptr->restart_state = RESTART_NORMAL; + rport_ptr->tx_quota_cnt = 0; + init_waitqueue_head(&rport_ptr->quota_wait); + mutex_init(&rport_ptr->quota_lock); + list_add_tail(&rport_ptr->list, + &rt_entry->remote_port_list[key]); + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + return rport_ptr; +} + +static void msm_ipc_router_destroy_remote_port( + struct msm_ipc_router_remote_port *rport_ptr) +{ + uint32_t node_id; + struct msm_ipc_routing_table_entry *rt_entry; + + if (!rport_ptr) + return; + + node_id = rport_ptr->node_id; + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(node_id); + if (!rt_entry) { + mutex_unlock(&routing_table_lock); + pr_err("%s: Node %d is not up\n", __func__, node_id); + return; + } + + mutex_lock(&rt_entry->lock); + list_del(&rport_ptr->list); + kfree(rport_ptr); + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + return; +} + +static struct msm_ipc_server *msm_ipc_router_lookup_server( + uint32_t service, + uint32_t instance, + uint32_t node_id, + uint32_t port_id) +{ + struct msm_ipc_server *server; + struct msm_ipc_server_port *server_port; + int key = (instance & (SRV_HASH_SIZE - 1)); + + mutex_lock(&server_list_lock); + list_for_each_entry(server, &server_list[key], list) { + if ((server->name.service != service) || + (server->name.instance != instance)) + continue; + if ((node_id == 0) && (port_id == 0)) { + mutex_unlock(&server_list_lock); + return server; + } + list_for_each_entry(server_port, &server->server_port_list, + list) { + if ((server_port->server_addr.node_id == node_id) && + (server_port->server_addr.port_id == port_id)) { + mutex_unlock(&server_list_lock); + return server; + } + } + } + mutex_unlock(&server_list_lock); + return NULL; +} + +static struct msm_ipc_server *msm_ipc_router_create_server( + uint32_t service, + uint32_t instance, + uint32_t node_id, + uint32_t port_id, + struct msm_ipc_router_xprt_info *xprt_info) +{ + struct msm_ipc_server *server = NULL; + struct msm_ipc_server_port *server_port; + int key = (instance & (SRV_HASH_SIZE - 1)); + + mutex_lock(&server_list_lock); + list_for_each_entry(server, &server_list[key], list) { + if ((server->name.service == service) && + (server->name.instance == instance)) + goto create_srv_port; + } + + server = kmalloc(sizeof(struct msm_ipc_server), GFP_KERNEL); + if (!server) { + mutex_unlock(&server_list_lock); + pr_err("%s: Server allocation failed\n", __func__); + return NULL; + } + server->name.service = service; + server->name.instance = instance; + INIT_LIST_HEAD(&server->server_port_list); + list_add_tail(&server->list, &server_list[key]); + +create_srv_port: + server_port = kmalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL); + if (!server_port) { + if (list_empty(&server->server_port_list)) { + list_del(&server->list); + kfree(server); + } + mutex_unlock(&server_list_lock); + pr_err("%s: Server Port allocation failed\n", __func__); + return NULL; + } + server_port->server_addr.node_id = node_id; + server_port->server_addr.port_id = port_id; + server_port->xprt_info = xprt_info; + list_add_tail(&server_port->list, &server->server_port_list); + mutex_unlock(&server_list_lock); + + return server; +} + +static void msm_ipc_router_destroy_server(struct msm_ipc_server *server, + uint32_t node_id, uint32_t port_id) +{ + struct msm_ipc_server_port *server_port; + + if (!server) + return; + + mutex_lock(&server_list_lock); + list_for_each_entry(server_port, &server->server_port_list, list) { + if ((server_port->server_addr.node_id == node_id) && + (server_port->server_addr.port_id == port_id)) + break; + } + if (server_port) { + list_del(&server_port->list); + kfree(server_port); + } + if (list_empty(&server->server_port_list)) { + list_del(&server->list); + kfree(server); + } + mutex_unlock(&server_list_lock); + return; +} + +static int msm_ipc_router_send_control_msg( + struct msm_ipc_router_xprt_info *xprt_info, + union rr_control_msg *msg) +{ + struct rr_packet *pkt; + struct sk_buff *ipc_rtr_pkt; + struct rr_header *hdr; + int pkt_size; + void *data; + struct sk_buff_head *pkt_fragment_q; + int ret; + + if (!xprt_info || ((msg->cmd != IPC_ROUTER_CTRL_CMD_HELLO) && + !xprt_info->initialized)) { + pr_err("%s: xprt_info not initialized\n", __func__); + return -EINVAL; + } + + if (xprt_info->remote_node_id == IPC_ROUTER_NID_LOCAL) + return 0; + + pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL); + if (!pkt) { + pr_err("%s: pkt alloc failed\n", __func__); + return -ENOMEM; + } + + pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL); + if (!pkt_fragment_q) { + pr_err("%s: pkt_fragment_q alloc failed\n", __func__); + kfree(pkt); + return -ENOMEM; + } + skb_queue_head_init(pkt_fragment_q); + + pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg); + ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL); + if (!ipc_rtr_pkt) { + pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__); + kfree(pkt_fragment_q); + kfree(pkt); + return -ENOMEM; + } + + skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE); + data = skb_put(ipc_rtr_pkt, sizeof(*msg)); + memcpy(data, msg, sizeof(*msg)); + hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE); + if (!hdr) { + pr_err("%s: skb_push failed\n", __func__); + kfree_skb(ipc_rtr_pkt); + kfree(pkt_fragment_q); + kfree(pkt); + return -ENOMEM; + } + + hdr->version = IPC_ROUTER_VERSION; + hdr->type = msg->cmd; + hdr->src_node_id = IPC_ROUTER_NID_LOCAL; + hdr->src_port_id = IPC_ROUTER_ADDRESS; + hdr->confirm_rx = 0; + hdr->size = sizeof(*msg); + hdr->dst_node_id = xprt_info->remote_node_id; + hdr->dst_port_id = IPC_ROUTER_ADDRESS; + skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt); + pkt->pkt_fragment_q = pkt_fragment_q; + pkt->length = pkt_size; + + mutex_lock(&xprt_info->tx_lock); + ret = xprt_info->xprt->write(pkt, pkt_size, xprt_info->xprt); + mutex_unlock(&xprt_info->tx_lock); + + release_pkt(pkt); + return ret; +} + +static int msm_ipc_router_send_server_list( + struct msm_ipc_router_xprt_info *xprt_info) +{ + union rr_control_msg ctl; + struct msm_ipc_server *server; + struct msm_ipc_server_port *server_port; + int i; + + if (!xprt_info || !xprt_info->initialized) { + pr_err("%s: Xprt info not initialized\n", __func__); + return -EINVAL; + } + + ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER; + + mutex_lock(&server_list_lock); + for (i = 0; i < SRV_HASH_SIZE; i++) { + list_for_each_entry(server, &server_list[i], list) { + ctl.srv.service = server->name.service; + ctl.srv.instance = server->name.instance; + list_for_each_entry(server_port, + &server->server_port_list, list) { + if (server_port->server_addr.node_id == + xprt_info->remote_node_id) + continue; + + ctl.srv.node_id = + server_port->server_addr.node_id; + ctl.srv.port_id = + server_port->server_addr.port_id; + msm_ipc_router_send_control_msg(xprt_info, + &ctl); + } + } + } + mutex_unlock(&server_list_lock); + + return 0; +} + +#if defined(DEBUG) +static char *type_to_str(int i) +{ + switch (i) { + case IPC_ROUTER_CTRL_CMD_DATA: + return "data "; + case IPC_ROUTER_CTRL_CMD_HELLO: + return "hello "; + case IPC_ROUTER_CTRL_CMD_BYE: + return "bye "; + case IPC_ROUTER_CTRL_CMD_NEW_SERVER: + return "new_srvr"; + case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER: + return "rmv_srvr"; + case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT: + return "rmv_clnt"; + case IPC_ROUTER_CTRL_CMD_RESUME_TX: + return "resum_tx"; + case IPC_ROUTER_CTRL_CMD_EXIT: + return "cmd_exit"; + default: + return "invalid"; + } +} +#endif + +static int broadcast_ctl_msg_locally(union rr_control_msg *msg) +{ + struct rr_packet *pkt; + struct sk_buff *ipc_rtr_pkt; + struct rr_header *hdr; + int pkt_size; + void *data; + struct sk_buff_head *pkt_fragment_q; + int ret; + + pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL); + if (!pkt) { + pr_err("%s: pkt alloc failed\n", __func__); + return -ENOMEM; + } + + pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL); + if (!pkt_fragment_q) { + pr_err("%s: pkt_fragment_q alloc failed\n", __func__); + kfree(pkt); + return -ENOMEM; + } + skb_queue_head_init(pkt_fragment_q); + + pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg); + ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL); + if (!ipc_rtr_pkt) { + pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__); + kfree(pkt_fragment_q); + kfree(pkt); + return -ENOMEM; + } + + skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE); + data = skb_put(ipc_rtr_pkt, sizeof(*msg)); + memcpy(data, msg, sizeof(*msg)); + hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE); + if (!hdr) { + pr_err("%s: skb_push failed\n", __func__); + kfree_skb(ipc_rtr_pkt); + kfree(pkt_fragment_q); + kfree(pkt); + return -ENOMEM; + } + hdr->version = IPC_ROUTER_VERSION; + hdr->type = msg->cmd; + hdr->src_node_id = IPC_ROUTER_NID_LOCAL; + hdr->src_port_id = IPC_ROUTER_ADDRESS; + hdr->confirm_rx = 0; + hdr->size = sizeof(*msg); + hdr->dst_node_id = IPC_ROUTER_NID_LOCAL; + hdr->dst_port_id = IPC_ROUTER_ADDRESS; + skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt); + pkt->pkt_fragment_q = pkt_fragment_q; + pkt->length = pkt_size; + + ret = post_control_ports(pkt); + release_pkt(pkt); + return ret; +} + +static int broadcast_ctl_msg(union rr_control_msg *ctl) +{ + struct msm_ipc_router_xprt_info *xprt_info; + + mutex_lock(&xprt_info_list_lock); + list_for_each_entry(xprt_info, &xprt_info_list, list) { + msm_ipc_router_send_control_msg(xprt_info, ctl); + } + mutex_unlock(&xprt_info_list_lock); + + return 0; +} + +static int relay_ctl_msg(struct msm_ipc_router_xprt_info *xprt_info, + union rr_control_msg *ctl) +{ + struct msm_ipc_router_xprt_info *fwd_xprt_info; + + if (!xprt_info || !ctl) + return -EINVAL; + + mutex_lock(&xprt_info_list_lock); + list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) { + if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id) + msm_ipc_router_send_control_msg(fwd_xprt_info, ctl); + } + mutex_unlock(&xprt_info_list_lock); + + return 0; +} + +static int relay_msg(struct msm_ipc_router_xprt_info *xprt_info, + struct rr_packet *pkt) +{ + struct msm_ipc_router_xprt_info *fwd_xprt_info; + + if (!xprt_info || !pkt) + return -EINVAL; + + mutex_lock(&xprt_info_list_lock); + list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) { + mutex_lock(&fwd_xprt_info->tx_lock); + if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id) + fwd_xprt_info->xprt->write(pkt, pkt->length, + fwd_xprt_info->xprt); + mutex_unlock(&fwd_xprt_info->tx_lock); + } + mutex_unlock(&xprt_info_list_lock); + return 0; +} + +static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info, + struct rr_packet *pkt) +{ + uint32_t dst_node_id; + struct sk_buff *head_pkt; + struct rr_header *hdr; + struct msm_ipc_router_xprt_info *fwd_xprt_info; + struct msm_ipc_routing_table_entry *rt_entry; + + if (!xprt_info || !pkt) + return -EINVAL; + + head_pkt = skb_peek(pkt->pkt_fragment_q); + if (!head_pkt) + return -EINVAL; + + hdr = (struct rr_header *)head_pkt->data; + dst_node_id = hdr->dst_node_id; + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(dst_node_id); + if (!(rt_entry) || !(rt_entry->xprt_info)) { + mutex_unlock(&routing_table_lock); + pr_err("%s: Routing table not initialized\n", __func__); + return -ENODEV; + } + + mutex_lock(&rt_entry->lock); + fwd_xprt_info = rt_entry->xprt_info; + mutex_lock(&fwd_xprt_info->tx_lock); + if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) { + mutex_unlock(&fwd_xprt_info->tx_lock); + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + pr_err("%s: Discarding Command to route back\n", __func__); + return -EINVAL; + } + + if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) { + mutex_unlock(&fwd_xprt_info->tx_lock); + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + pr_err("%s: DST in the same cluster\n", __func__); + return 0; + } + fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt); + mutex_unlock(&fwd_xprt_info->tx_lock); + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + + return 0; +} + +static void reset_remote_port_info(uint32_t node_id, uint32_t port_id) +{ + struct msm_ipc_router_remote_port *rport_ptr; + + rport_ptr = msm_ipc_router_lookup_remote_port(node_id, port_id); + if (!rport_ptr) { + pr_err("%s: No such remote port %08x:%08x\n", + __func__, node_id, port_id); + return; + } + mutex_lock(&rport_ptr->quota_lock); + rport_ptr->restart_state = RESTART_PEND; + wake_up(&rport_ptr->quota_wait); + mutex_unlock(&rport_ptr->quota_lock); + return; +} + +static void msm_ipc_cleanup_remote_server_info( + struct msm_ipc_router_xprt_info *xprt_info) +{ + struct msm_ipc_server *svr, *tmp_svr; + struct msm_ipc_server_port *svr_port, *tmp_svr_port; + int i; + union rr_control_msg ctl; + + if (!xprt_info) { + pr_err("%s: Invalid xprt_info\n", __func__); + return; + } + + ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER; + mutex_lock(&server_list_lock); + for (i = 0; i < SRV_HASH_SIZE; i++) { + list_for_each_entry_safe(svr, tmp_svr, &server_list[i], list) { + ctl.srv.service = svr->name.service; + ctl.srv.instance = svr->name.instance; + list_for_each_entry_safe(svr_port, tmp_svr_port, + &svr->server_port_list, list) { + if (svr_port->xprt_info != xprt_info) + continue; + D("Remove server %08x:%08x - %08x:%08x", + ctl.srv.service, ctl.srv.instance, + svr_port->server_addr.node_id, + svr_port->server_addr.port_id); + reset_remote_port_info( + svr_port->server_addr.node_id, + svr_port->server_addr.port_id); + ctl.srv.node_id = svr_port->server_addr.node_id; + ctl.srv.port_id = svr_port->server_addr.port_id; + relay_ctl_msg(xprt_info, &ctl); + broadcast_ctl_msg_locally(&ctl); + list_del(&svr_port->list); + kfree(svr_port); + } + if (list_empty(&svr->server_port_list)) { + list_del(&svr->list); + kfree(svr); + } + } + } + mutex_unlock(&server_list_lock); +} + +static void msm_ipc_cleanup_remote_client_info( + struct msm_ipc_router_xprt_info *xprt_info) +{ + struct msm_ipc_routing_table_entry *rt_entry; + struct msm_ipc_router_remote_port *rport_ptr; + int i, j; + union rr_control_msg ctl; + + if (!xprt_info) { + pr_err("%s: Invalid xprt_info\n", __func__); + return; + } + + ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT; + mutex_lock(&routing_table_lock); + for (i = 0; i < RT_HASH_SIZE; i++) { + list_for_each_entry(rt_entry, &routing_table[i], list) { + mutex_lock(&rt_entry->lock); + if (rt_entry->xprt_info != xprt_info) { + mutex_unlock(&rt_entry->lock); + continue; + } + for (j = 0; j < RP_HASH_SIZE; j++) { + list_for_each_entry(rport_ptr, + &rt_entry->remote_port_list[j], list) { + if (rport_ptr->restart_state == + RESTART_PEND) + continue; + mutex_lock(&rport_ptr->quota_lock); + rport_ptr->restart_state = RESTART_PEND; + wake_up(&rport_ptr->quota_wait); + mutex_unlock(&rport_ptr->quota_lock); + ctl.cli.node_id = rport_ptr->node_id; + ctl.cli.port_id = rport_ptr->port_id; + broadcast_ctl_msg_locally(&ctl); + } + } + mutex_unlock(&rt_entry->lock); + } + } + mutex_unlock(&routing_table_lock); +} + +static void msm_ipc_cleanup_remote_port_info(uint32_t node_id) +{ + struct msm_ipc_routing_table_entry *rt_entry, *tmp_rt_entry; + struct msm_ipc_router_remote_port *rport_ptr, *tmp_rport_ptr; + int i, j; + + mutex_lock(&routing_table_lock); + for (i = 0; i < RT_HASH_SIZE; i++) { + list_for_each_entry_safe(rt_entry, tmp_rt_entry, + &routing_table[i], list) { + mutex_lock(&rt_entry->lock); + if (rt_entry->neighbor_node_id != node_id) { + mutex_unlock(&rt_entry->lock); + continue; + } + for (j = 0; j < RP_HASH_SIZE; j++) { + list_for_each_entry_safe(rport_ptr, + tmp_rport_ptr, + &rt_entry->remote_port_list[j], list) { + list_del(&rport_ptr->list); + kfree(rport_ptr); + } + } + mutex_unlock(&rt_entry->lock); + } + } + mutex_unlock(&routing_table_lock); +} + +static void msm_ipc_cleanup_routing_table( + struct msm_ipc_router_xprt_info *xprt_info) +{ + int i; + struct msm_ipc_routing_table_entry *rt_entry; + + if (!xprt_info) { + pr_err("%s: Invalid xprt_info\n", __func__); + return; + } + + mutex_lock(&routing_table_lock); + for (i = 0; i < RT_HASH_SIZE; i++) { + list_for_each_entry(rt_entry, &routing_table[i], list) { + mutex_lock(&rt_entry->lock); + if (rt_entry->xprt_info == xprt_info) + rt_entry->xprt_info = NULL; + mutex_unlock(&rt_entry->lock); + } + } + mutex_unlock(&routing_table_lock); +} + +static void modem_reset_cleanup(struct msm_ipc_router_xprt_info *xprt_info) +{ + + if (!xprt_info) { + pr_err("%s: Invalid xprt_info\n", __func__); + return; + } + + msm_ipc_cleanup_remote_server_info(xprt_info); + msm_ipc_cleanup_remote_client_info(xprt_info); + msm_ipc_cleanup_routing_table(xprt_info); +} + +static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info, + struct rr_packet *pkt) +{ + union rr_control_msg ctl; + union rr_control_msg *msg; + struct msm_ipc_router_remote_port *rport_ptr; + int rc = 0; + static uint32_t first = 1; + struct sk_buff *temp_ptr; + struct rr_header *hdr; + struct msm_ipc_server *server; + struct msm_ipc_routing_table_entry *rt_entry; + + if (pkt->length != (IPC_ROUTER_HDR_SIZE + sizeof(*msg))) { + pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length, + (IPC_ROUTER_HDR_SIZE + sizeof(*msg))); + return -EINVAL; + } + + temp_ptr = skb_peek(pkt->pkt_fragment_q); + if (!temp_ptr) { + pr_err("%s: pkt_fragment_q is empty\n", __func__); + return -EINVAL; + } + hdr = (struct rr_header *)temp_ptr->data; + if (!hdr) { + pr_err("%s: No data inside the skb\n", __func__); + return -EINVAL; + } + msg = (union rr_control_msg *)((char *)hdr + IPC_ROUTER_HDR_SIZE); + + switch (msg->cmd) { + case IPC_ROUTER_CTRL_CMD_HELLO: + RR("o HELLO NID %d\n", hdr->src_node_id); + xprt_info->remote_node_id = hdr->src_node_id; + + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(hdr->src_node_id); + if (!rt_entry) { + rt_entry = alloc_routing_table_entry(hdr->src_node_id); + if (!rt_entry) { + mutex_unlock(&routing_table_lock); + pr_err("%s: rt_entry allocation failed\n", + __func__); + return -ENOMEM; + } + add_routing_table_entry(rt_entry); + } + mutex_lock(&rt_entry->lock); + rt_entry->neighbor_node_id = xprt_info->remote_node_id; + rt_entry->xprt_info = xprt_info; + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id); + + memset(&ctl, 0, sizeof(ctl)); + ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO; + msm_ipc_router_send_control_msg(xprt_info, &ctl); + + xprt_info->initialized = 1; + + /* Send list of servers one at a time */ + msm_ipc_router_send_server_list(xprt_info); + + if (first) { + first = 0; + complete_all(&msm_ipc_remote_router_up); + } + RR("HELLO message processed\n"); + break; + case IPC_ROUTER_CTRL_CMD_RESUME_TX: + RR("o RESUME_TX id=%d:%08x\n", + msg->cli.node_id, msg->cli.port_id); + + rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id, + msg->cli.port_id); + if (!rport_ptr) { + pr_err("%s: Unable to resume client\n", __func__); + break; + } + mutex_lock(&rport_ptr->quota_lock); + rport_ptr->tx_quota_cnt = 0; + mutex_unlock(&rport_ptr->quota_lock); + wake_up(&rport_ptr->quota_wait); + break; + + case IPC_ROUTER_CTRL_CMD_NEW_SERVER: + if (msg->srv.instance == 0) { + pr_err( + "rpcrouter: Server create rejected, version = 0, " + "service = %08x\n", msg->srv.service); + break; + } + + RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n", + msg->srv.node_id, msg->srv.port_id, + msg->srv.service, msg->srv.instance); + + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(msg->srv.node_id); + if (!rt_entry) { + rt_entry = alloc_routing_table_entry(msg->srv.node_id); + if (!rt_entry) { + mutex_unlock(&routing_table_lock); + pr_err("%s: rt_entry allocation failed\n", + __func__); + return -ENOMEM; + } + mutex_lock(&rt_entry->lock); + rt_entry->neighbor_node_id = xprt_info->remote_node_id; + rt_entry->xprt_info = xprt_info; + mutex_unlock(&rt_entry->lock); + add_routing_table_entry(rt_entry); + } + mutex_unlock(&routing_table_lock); + + server = msm_ipc_router_lookup_server(msg->srv.service, + msg->srv.instance, + msg->srv.node_id, + msg->srv.port_id); + if (!server) { + server = msm_ipc_router_create_server( + msg->srv.service, msg->srv.instance, + msg->srv.node_id, msg->srv.port_id, xprt_info); + if (!server) { + pr_err("%s: Server Create failed\n", __func__); + return -ENOMEM; + } + + if (!msm_ipc_router_lookup_remote_port( + msg->srv.node_id, msg->srv.port_id)) { + rport_ptr = msm_ipc_router_create_remote_port( + msg->srv.node_id, msg->srv.port_id); + if (!rport_ptr) + pr_err("%s: Remote port create " + "failed\n", __func__); + } + wake_up(&newserver_wait); + } + + relay_msg(xprt_info, pkt); + post_control_ports(pkt); + break; + case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER: + RR("o REMOVE_SERVER service=%08x:%d\n", + msg->srv.service, msg->srv.instance); + server = msm_ipc_router_lookup_server(msg->srv.service, + msg->srv.instance, + msg->srv.node_id, + msg->srv.port_id); + if (server) { + msm_ipc_router_destroy_server(server, + msg->srv.node_id, + msg->srv.port_id); + relay_msg(xprt_info, pkt); + post_control_ports(pkt); + } + break; + case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT: + RR("o REMOVE_CLIENT id=%d:%08x\n", + msg->cli.node_id, msg->cli.port_id); + rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id, + msg->cli.port_id); + if (rport_ptr) + msm_ipc_router_destroy_remote_port(rport_ptr); + + relay_msg(xprt_info, pkt); + post_control_ports(pkt); + break; + case IPC_ROUTER_CTRL_CMD_PING: + /* No action needed for ping messages received */ + RR("o PING\n"); + break; + default: + RR("o UNKNOWN(%08x)\n", msg->cmd); + rc = -ENOSYS; + } + + return rc; +} + +static void do_read_data(struct work_struct *work) +{ + struct rr_header *hdr; + struct rr_packet *pkt = NULL; + struct msm_ipc_port *port_ptr; + struct sk_buff *head_skb; + struct msm_ipc_port_addr *src_addr; + struct msm_ipc_router_remote_port *rport_ptr; + uint32_t resume_tx, resume_tx_node_id, resume_tx_port_id; + + struct msm_ipc_router_xprt_info *xprt_info = + container_of(work, + struct msm_ipc_router_xprt_info, + read_data); + + pkt = rr_read(xprt_info); + if (!pkt) { + pr_err("%s: rr_read failed\n", __func__); + goto fail_io; + } + + if (pkt->length < IPC_ROUTER_HDR_SIZE || + pkt->length > MAX_IPC_PKT_SIZE) { + pr_err("%s: Invalid pkt length %d\n", __func__, pkt->length); + goto fail_data; + } + + head_skb = skb_peek(pkt->pkt_fragment_q); + if (!head_skb) { + pr_err("%s: head_skb is invalid\n", __func__); + goto fail_data; + } + + hdr = (struct rr_header *)(head_skb->data); + RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n", + hdr->version, hdr->type, hdr->src_node_id, hdr->src_port_id, + hdr->confirm_rx, hdr->size, hdr->dst_node_id, hdr->dst_port_id); + RAW_HDR("[r rr_h] " + "ver=%i,type=%s,src_node_id=%08x,src_port_id=%08x," + "confirm_rx=%i,size=%3i,dst_node_id=%08x,dst_port_id=%08x\n", + hdr->version, type_to_str(hdr->type), hdr->src_node_id, + hdr->src_port_id, hdr->confirm_rx, hdr->size, hdr->dst_node_id, + hdr->dst_port_id); + + if (hdr->version != IPC_ROUTER_VERSION) { + pr_err("version %d != %d\n", hdr->version, IPC_ROUTER_VERSION); + goto fail_data; + } + + if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) && + ((hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX) || + (hdr->type == IPC_ROUTER_CTRL_CMD_DATA))) { + forward_msg(xprt_info, pkt); + release_pkt(pkt); + goto done; + } + + if ((hdr->dst_port_id == IPC_ROUTER_ADDRESS) || + (hdr->type == IPC_ROUTER_CTRL_CMD_HELLO)) { + process_control_msg(xprt_info, pkt); + release_pkt(pkt); + goto done; + } +#if defined(CONFIG_MSM_SMD_LOGGING) +#if defined(DEBUG) + if (msm_ipc_router_debug_mask & SMEM_LOG) { + smem_log_event((SMEM_LOG_PROC_ID_APPS | + SMEM_LOG_RPC_ROUTER_EVENT_BASE | + IPC_ROUTER_LOG_EVENT_RX), + (hdr->src_node_id << 24) | + (hdr->src_port_id & 0xffffff), + (hdr->dst_node_id << 24) | + (hdr->dst_port_id & 0xffffff), + (hdr->type << 24) | (hdr->confirm_rx << 16) | + (hdr->size & 0xffff)); + } +#endif +#endif + + resume_tx = hdr->confirm_rx; + resume_tx_node_id = hdr->dst_node_id; + resume_tx_port_id = hdr->dst_port_id; + + port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id); + if (!port_ptr) { + pr_err("%s: No local port id %08x\n", __func__, + hdr->dst_port_id); + release_pkt(pkt); + goto process_done; + } + + rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id, + hdr->src_port_id); + if (!rport_ptr) { + rport_ptr = msm_ipc_router_create_remote_port( + hdr->src_node_id, + hdr->src_port_id); + if (!rport_ptr) { + pr_err("%s: Remote port %08x:%08x creation failed\n", + __func__, hdr->src_node_id, hdr->src_port_id); + goto process_done; + } + } + + if (!port_ptr->notify) { + mutex_lock(&port_ptr->port_rx_q_lock); + wake_lock(&port_ptr->port_rx_wake_lock); + list_add_tail(&pkt->list, &port_ptr->port_rx_q); + wake_up(&port_ptr->port_rx_wait_q); + mutex_unlock(&port_ptr->port_rx_q_lock); + } else { + src_addr = kmalloc(sizeof(struct msm_ipc_port_addr), + GFP_KERNEL); + if (src_addr) { + src_addr->node_id = hdr->src_node_id; + src_addr->port_id = hdr->src_port_id; + } + skb_pull(head_skb, IPC_ROUTER_HDR_SIZE); + port_ptr->notify(MSM_IPC_ROUTER_READ_CB, pkt->pkt_fragment_q, + src_addr, port_ptr->priv); + pkt->pkt_fragment_q = NULL; + src_addr = NULL; + release_pkt(pkt); + } + +process_done: + if (resume_tx) { + union rr_control_msg msg; + + msg.cmd = IPC_ROUTER_CTRL_CMD_RESUME_TX; + msg.cli.node_id = resume_tx_node_id; + msg.cli.port_id = resume_tx_port_id; + + RR("x RESUME_TX id=%d:%08x\n", + msg.cli.node_id, msg.cli.port_id); + msm_ipc_router_send_control_msg(xprt_info, &msg); + } + +done: + queue_work(xprt_info->workqueue, &xprt_info->read_data); + return; + +fail_data: + release_pkt(pkt); +fail_io: + pr_err("ipc_router has died\n"); +} + +int msm_ipc_router_register_server(struct msm_ipc_port *port_ptr, + struct msm_ipc_addr *name) +{ + struct msm_ipc_server *server; + unsigned long flags; + union rr_control_msg ctl; + + if (!port_ptr || !name) + return -EINVAL; + + if (name->addrtype != MSM_IPC_ADDR_NAME) + return -EINVAL; + + server = msm_ipc_router_lookup_server(name->addr.port_name.service, + name->addr.port_name.instance, + IPC_ROUTER_NID_LOCAL, + port_ptr->this_port.port_id); + if (server) { + pr_err("%s: Server already present\n", __func__); + return -EINVAL; + } + + server = msm_ipc_router_create_server(name->addr.port_name.service, + name->addr.port_name.instance, + IPC_ROUTER_NID_LOCAL, + port_ptr->this_port.port_id, + NULL); + if (!server) { + pr_err("%s: Server Creation failed\n", __func__); + return -EINVAL; + } + + ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER; + ctl.srv.service = server->name.service; + ctl.srv.instance = server->name.instance; + ctl.srv.node_id = IPC_ROUTER_NID_LOCAL; + ctl.srv.port_id = port_ptr->this_port.port_id; + broadcast_ctl_msg(&ctl); + spin_lock_irqsave(&port_ptr->port_lock, flags); + port_ptr->type = SERVER_PORT; + port_ptr->port_name.service = server->name.service; + port_ptr->port_name.instance = server->name.instance; + spin_unlock_irqrestore(&port_ptr->port_lock, flags); + return 0; +} + +int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr) +{ + struct msm_ipc_server *server; + unsigned long flags; + union rr_control_msg ctl; + + if (!port_ptr) + return -EINVAL; + + if (port_ptr->type != SERVER_PORT) { + pr_err("%s: Trying to unregister a non-server port\n", + __func__); + return -EINVAL; + } + + if (port_ptr->this_port.node_id != IPC_ROUTER_NID_LOCAL) { + pr_err("%s: Trying to unregister a remote server locally\n", + __func__); + return -EINVAL; + } + + server = msm_ipc_router_lookup_server(port_ptr->port_name.service, + port_ptr->port_name.instance, + port_ptr->this_port.node_id, + port_ptr->this_port.port_id); + if (!server) { + pr_err("%s: Server lookup failed\n", __func__); + return -ENODEV; + } + + ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER; + ctl.srv.service = server->name.service; + ctl.srv.instance = server->name.instance; + ctl.srv.node_id = IPC_ROUTER_NID_LOCAL; + ctl.srv.port_id = port_ptr->this_port.port_id; + broadcast_ctl_msg(&ctl); + msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id, + port_ptr->this_port.port_id); + spin_lock_irqsave(&port_ptr->port_lock, flags); + port_ptr->type = CLIENT_PORT; + spin_unlock_irqrestore(&port_ptr->port_lock, flags); + return 0; +} + +static int loopback_data(struct msm_ipc_port *src, + uint32_t port_id, + struct sk_buff_head *data) +{ + struct sk_buff *head_skb; + struct rr_header *hdr; + struct msm_ipc_port *port_ptr; + struct rr_packet *pkt; + + if (!data) { + pr_err("%s: Invalid pkt pointer\n", __func__); + return -EINVAL; + } + + pkt = create_pkt(data); + if (!pkt) { + pr_err("%s: New pkt create failed\n", __func__); + return -ENOMEM; + } + + head_skb = skb_peek(pkt->pkt_fragment_q); + if (!head_skb) { + pr_err("%s: pkt_fragment_q is empty\n", __func__); + return -EINVAL; + } + hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE); + if (!hdr) { + pr_err("%s: Prepend Header failed\n", __func__); + release_pkt(pkt); + return -ENOMEM; + } + hdr->version = IPC_ROUTER_VERSION; + hdr->type = IPC_ROUTER_CTRL_CMD_DATA; + hdr->src_node_id = src->this_port.node_id; + hdr->src_port_id = src->this_port.port_id; + hdr->size = pkt->length; + hdr->confirm_rx = 0; + hdr->dst_node_id = IPC_ROUTER_NID_LOCAL; + hdr->dst_port_id = port_id; + pkt->length += IPC_ROUTER_HDR_SIZE; + + port_ptr = msm_ipc_router_lookup_local_port(port_id); + if (!port_ptr) { + pr_err("%s: Local port %d not present\n", __func__, port_id); + release_pkt(pkt); + return -ENODEV; + } + + mutex_lock(&port_ptr->port_rx_q_lock); + wake_lock(&port_ptr->port_rx_wake_lock); + list_add_tail(&pkt->list, &port_ptr->port_rx_q); + wake_up(&port_ptr->port_rx_wait_q); + mutex_unlock(&port_ptr->port_rx_q_lock); + + return pkt->length; +} + +static int msm_ipc_router_write_pkt(struct msm_ipc_port *src, + struct msm_ipc_router_remote_port *rport_ptr, + struct rr_packet *pkt) +{ + struct sk_buff *head_skb; + struct rr_header *hdr; + struct msm_ipc_router_xprt_info *xprt_info; + struct msm_ipc_routing_table_entry *rt_entry; + int ret; + DEFINE_WAIT(__wait); + + if (!rport_ptr || !src || !pkt) + return -EINVAL; + + head_skb = skb_peek(pkt->pkt_fragment_q); + if (!head_skb) { + pr_err("%s: pkt_fragment_q is empty\n", __func__); + return -EINVAL; + } + hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE); + if (!hdr) { + pr_err("%s: Prepend Header failed\n", __func__); + return -ENOMEM; + } + hdr->version = IPC_ROUTER_VERSION; + hdr->type = IPC_ROUTER_CTRL_CMD_DATA; + hdr->src_node_id = src->this_port.node_id; + hdr->src_port_id = src->this_port.port_id; + hdr->size = pkt->length; + hdr->confirm_rx = 0; + hdr->dst_node_id = rport_ptr->node_id; + hdr->dst_port_id = rport_ptr->port_id; + pkt->length += IPC_ROUTER_HDR_SIZE; + + for (;;) { + prepare_to_wait(&rport_ptr->quota_wait, &__wait, + TASK_INTERRUPTIBLE); + mutex_lock(&rport_ptr->quota_lock); + if (rport_ptr->restart_state != RESTART_NORMAL) + break; + if (rport_ptr->tx_quota_cnt < + IPC_ROUTER_DEFAULT_RX_QUOTA) + break; + if (signal_pending(current)) + break; + mutex_unlock(&rport_ptr->quota_lock); + schedule(); + } + finish_wait(&rport_ptr->quota_wait, &__wait); + + if (rport_ptr->restart_state != RESTART_NORMAL) { + mutex_unlock(&rport_ptr->quota_lock); + return -ENETRESET; + } + if (signal_pending(current)) { + mutex_unlock(&rport_ptr->quota_lock); + return -ERESTARTSYS; + } + rport_ptr->tx_quota_cnt++; + if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) + hdr->confirm_rx = 1; + mutex_unlock(&rport_ptr->quota_lock); + + mutex_lock(&routing_table_lock); + rt_entry = lookup_routing_table(hdr->dst_node_id); + if (!rt_entry || !rt_entry->xprt_info) { + mutex_unlock(&routing_table_lock); + pr_err("%s: Remote node %d not up\n", + __func__, hdr->dst_node_id); + return -ENODEV; + } + mutex_lock(&rt_entry->lock); + xprt_info = rt_entry->xprt_info; + mutex_lock(&xprt_info->tx_lock); + ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt); + mutex_unlock(&xprt_info->tx_lock); + mutex_unlock(&rt_entry->lock); + mutex_unlock(&routing_table_lock); + + if (ret < 0) { + pr_err("%s: Write on XPRT failed\n", __func__); + return ret; + } + + RAW_HDR("[w rr_h] " + "ver=%i,type=%s,src_nid=%08x,src_port_id=%08x," + "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", + hdr->version, type_to_str(hdr->type), + hdr->src_node_id, hdr->src_port_id, + hdr->confirm_rx, hdr->size, + hdr->dst_node_id, hdr->dst_port_id); + +#if defined(CONFIG_MSM_SMD_LOGGING) +#if defined(DEBUG) + if (msm_ipc_router_debug_mask & SMEM_LOG) { + smem_log_event((SMEM_LOG_PROC_ID_APPS | + SMEM_LOG_RPC_ROUTER_EVENT_BASE | + IPC_ROUTER_LOG_EVENT_TX), + (hdr->src_node_id << 24) | + (hdr->src_port_id & 0xffffff), + (hdr->dst_node_id << 24) | + (hdr->dst_port_id & 0xffffff), + (hdr->type << 24) | (hdr->confirm_rx << 16) | + (hdr->size & 0xffff)); + } +#endif +#endif + + return pkt->length; +} + +int msm_ipc_router_send_to(struct msm_ipc_port *src, + struct sk_buff_head *data, + struct msm_ipc_addr *dest) +{ + uint32_t dst_node_id = 0, dst_port_id = 0; + struct msm_ipc_server *server; + struct msm_ipc_server_port *server_port; + struct msm_ipc_router_remote_port *rport_ptr = NULL; + struct rr_packet *pkt; + int ret; + + if (!src || !data || !dest) { + pr_err("%s: Invalid Parameters\n", __func__); + return -EINVAL; + } + + /* Resolve Address*/ + if (dest->addrtype == MSM_IPC_ADDR_ID) { + dst_node_id = dest->addr.port_addr.node_id; + dst_port_id = dest->addr.port_addr.port_id; + } else if (dest->addrtype == MSM_IPC_ADDR_NAME) { + server = msm_ipc_router_lookup_server( + dest->addr.port_name.service, + dest->addr.port_name.instance, + 0, 0); + if (!server) { + pr_err("%s: Destination not reachable\n", __func__); + return -ENODEV; + } + mutex_lock(&server_list_lock); + server_port = list_first_entry(&server->server_port_list, + struct msm_ipc_server_port, + list); + dst_node_id = server_port->server_addr.node_id; + dst_port_id = server_port->server_addr.port_id; + mutex_unlock(&server_list_lock); + } + if (dst_node_id == IPC_ROUTER_NID_LOCAL) { + ret = loopback_data(src, dst_port_id, data); + return ret; + } + + /* Achieve Flow control */ + rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id, + dst_port_id); + if (!rport_ptr) { + pr_err("%s: Could not create remote port\n", __func__); + return -ENOMEM; + } + + pkt = create_pkt(data); + if (!pkt) { + pr_err("%s: Pkt creation failed\n", __func__); + return -ENOMEM; + } + + ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt); + release_pkt(pkt); + + return ret; +} + +int msm_ipc_router_read(struct msm_ipc_port *port_ptr, + struct sk_buff_head **data, + size_t buf_len) +{ + struct rr_packet *pkt; + int ret; + + if (!port_ptr || !data) + return -EINVAL; + + mutex_lock(&port_ptr->port_rx_q_lock); + if (list_empty(&port_ptr->port_rx_q)) { + mutex_unlock(&port_ptr->port_rx_q_lock); + return -EAGAIN; + } + + pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list); + if ((buf_len) && ((pkt->length - IPC_ROUTER_HDR_SIZE) > buf_len)) { + mutex_unlock(&port_ptr->port_rx_q_lock); + return -ETOOSMALL; + } + list_del(&pkt->list); + if (list_empty(&port_ptr->port_rx_q)) + wake_unlock(&port_ptr->port_rx_wake_lock); + *data = pkt->pkt_fragment_q; + ret = pkt->length; + kfree(pkt); + mutex_unlock(&port_ptr->port_rx_q_lock); + + return ret; +} + +int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr, + struct sk_buff_head **data, + struct msm_ipc_addr *src, + unsigned long timeout) +{ + int ret, data_len, align_size; + struct sk_buff *temp_skb; + struct rr_header *hdr = NULL; + + if (!port_ptr || !data) { + pr_err("%s: Invalid pointers being passed\n", __func__); + return -EINVAL; + } + + *data = NULL; + mutex_lock(&port_ptr->port_rx_q_lock); + while (list_empty(&port_ptr->port_rx_q)) { + mutex_unlock(&port_ptr->port_rx_q_lock); + if (timeout < 0) { + ret = wait_event_interruptible( + port_ptr->port_rx_wait_q, + !list_empty(&port_ptr->port_rx_q)); + if (ret) + return ret; + } else if (timeout > 0) { + timeout = wait_event_interruptible_timeout( + port_ptr->port_rx_wait_q, + !list_empty(&port_ptr->port_rx_q), + timeout); + if (timeout < 0) + return -EFAULT; + } + if (timeout == 0) + return -ETIMEDOUT; + mutex_lock(&port_ptr->port_rx_q_lock); + } + mutex_unlock(&port_ptr->port_rx_q_lock); + + ret = msm_ipc_router_read(port_ptr, data, 0); + if (ret <= 0 || !(*data)) + return ret; + + temp_skb = skb_peek(*data); + hdr = (struct rr_header *)(temp_skb->data); + if (src) { + src->addrtype = MSM_IPC_ADDR_ID; + src->addr.port_addr.node_id = hdr->src_node_id; + src->addr.port_addr.port_id = hdr->src_port_id; + } + + data_len = hdr->size; + skb_pull(temp_skb, IPC_ROUTER_HDR_SIZE); + align_size = ALIGN_SIZE(data_len); + if (align_size) { + temp_skb = skb_peek_tail(*data); + skb_trim(temp_skb, (temp_skb->len - align_size)); + } + return data_len; +} + +struct msm_ipc_port *msm_ipc_router_create_port( + void (*notify)(unsigned event, void *data, void *addr, void *priv), + void *priv) +{ + struct msm_ipc_port *port_ptr; + + port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv); + if (!port_ptr) + pr_err("%s: port_ptr alloc failed\n", __func__); + + return port_ptr; +} + +int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr) +{ + union rr_control_msg msg; + struct rr_packet *pkt, *temp_pkt; + struct msm_ipc_server *server; + + if (!port_ptr) + return -EINVAL; + + if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) { + if (port_ptr->type == SERVER_PORT) { + msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER; + msg.srv.service = port_ptr->port_name.service; + msg.srv.instance = port_ptr->port_name.instance; + msg.srv.node_id = port_ptr->this_port.node_id; + msg.srv.port_id = port_ptr->this_port.port_id; + RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n", + msg.srv.service, msg.srv.instance, + msg.srv.node_id, msg.srv.port_id); + } else if (port_ptr->type == CLIENT_PORT) { + msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT; + msg.cli.node_id = port_ptr->this_port.node_id; + msg.cli.port_id = port_ptr->this_port.port_id; + RR("x REMOVE_CLIENT id=%d:%08x\n", + msg.cli.node_id, msg.cli.port_id); + } + broadcast_ctl_msg(&msg); + broadcast_ctl_msg_locally(&msg); + } + + mutex_lock(&port_ptr->port_rx_q_lock); + list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) { + list_del(&pkt->list); + release_pkt(pkt); + } + mutex_unlock(&port_ptr->port_rx_q_lock); + + if (port_ptr->type == SERVER_PORT) { + server = msm_ipc_router_lookup_server( + port_ptr->port_name.service, + port_ptr->port_name.instance, + port_ptr->this_port.node_id, + port_ptr->this_port.port_id); + if (server) + msm_ipc_router_destroy_server(server, + port_ptr->this_port.node_id, + port_ptr->this_port.port_id); + mutex_lock(&local_ports_lock); + list_del(&port_ptr->list); + mutex_unlock(&local_ports_lock); + } else if (port_ptr->type == CLIENT_PORT) { + mutex_lock(&local_ports_lock); + list_del(&port_ptr->list); + mutex_unlock(&local_ports_lock); + } else if (port_ptr->type == CONTROL_PORT) { + mutex_lock(&control_ports_lock); + list_del(&port_ptr->list); + mutex_unlock(&control_ports_lock); + } + + wake_lock_destroy(&port_ptr->port_rx_wake_lock); + kfree(port_ptr); + return 0; +} + +int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr) +{ + struct rr_packet *pkt; + int rc = 0; + + if (!port_ptr) + return -EINVAL; + + mutex_lock(&port_ptr->port_rx_q_lock); + if (!list_empty(&port_ptr->port_rx_q)) { + pkt = list_first_entry(&port_ptr->port_rx_q, + struct rr_packet, list); + rc = pkt->length; + } + mutex_unlock(&port_ptr->port_rx_q_lock); + + return rc; +} + +int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr) +{ + if (!port_ptr) + return -EINVAL; + + mutex_lock(&local_ports_lock); + list_del(&port_ptr->list); + mutex_unlock(&local_ports_lock); + port_ptr->type = CONTROL_PORT; + mutex_lock(&control_ports_lock); + list_add_tail(&port_ptr->list, &control_ports); + mutex_unlock(&control_ports_lock); + + return 0; +} + +int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name, + struct msm_ipc_port_addr *srv_addr, + int num_entries_in_array, + uint32_t lookup_mask) +{ + struct msm_ipc_server *server; + struct msm_ipc_server_port *server_port; + int key, i = 0; /*num_entries_found*/ + + if (!srv_name) { + pr_err("%s: Invalid srv_name\n", __func__); + return -EINVAL; + } + + if (num_entries_in_array && !srv_addr) { + pr_err("%s: srv_addr NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&server_list_lock); + if (!lookup_mask) + lookup_mask = 0xFFFFFFFF; + for (key = 0; key < SRV_HASH_SIZE; key++) { + list_for_each_entry(server, &server_list[key], list) { + if ((server->name.service != srv_name->service) || + ((server->name.instance & lookup_mask) != + srv_name->instance)) + continue; + + list_for_each_entry(server_port, + &server->server_port_list, list) { + if (i < num_entries_in_array) { + srv_addr[i].node_id = + server_port->server_addr.node_id; + srv_addr[i].port_id = + server_port->server_addr.port_id; + } + i++; + } + } + } + mutex_unlock(&server_list_lock); + + return i; +} + +int msm_ipc_router_close(void) +{ + struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info; + + mutex_lock(&xprt_info_list_lock); + list_for_each_entry_safe(xprt_info, tmp_xprt_info, + &xprt_info_list, list) { + xprt_info->xprt->close(xprt_info->xprt); + list_del(&xprt_info->list); + kfree(xprt_info); + } + mutex_unlock(&xprt_info_list_lock); + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int dump_routing_table(char *buf, int max) +{ + int i = 0, j; + struct msm_ipc_routing_table_entry *rt_entry; + + for (j = 0; j < RT_HASH_SIZE; j++) { + mutex_lock(&routing_table_lock); + list_for_each_entry(rt_entry, &routing_table[j], list) { + mutex_lock(&rt_entry->lock); + i += scnprintf(buf + i, max - i, + "Node Id: 0x%08x\n", rt_entry->node_id); + if (j == IPC_ROUTER_NID_LOCAL) { + i += scnprintf(buf + i, max - i, + "XPRT Name: Loopback\n"); + i += scnprintf(buf + i, max - i, + "Next Hop: %d\n", rt_entry->node_id); + } else { + i += scnprintf(buf + i, max - i, + "XPRT Name: %s\n", + rt_entry->xprt_info->xprt->name); + i += scnprintf(buf + i, max - i, + "Next Hop: 0x%08x\n", + rt_entry->xprt_info->remote_node_id); + } + i += scnprintf(buf + i, max - i, "\n"); + mutex_unlock(&rt_entry->lock); + } + mutex_unlock(&routing_table_lock); + } + + return i; +} + +static int dump_xprt_info(char *buf, int max) +{ + int i = 0; + struct msm_ipc_router_xprt_info *xprt_info; + + mutex_lock(&xprt_info_list_lock); + list_for_each_entry(xprt_info, &xprt_info_list, list) { + i += scnprintf(buf + i, max - i, "XPRT Name: %s\n", + xprt_info->xprt->name); + i += scnprintf(buf + i, max - i, "Link Id: %d\n", + xprt_info->xprt->link_id); + i += scnprintf(buf + i, max - i, "Initialized: %s\n", + (xprt_info->initialized ? "Y" : "N")); + i += scnprintf(buf + i, max - i, "Remote Node Id: 0x%08x\n", + xprt_info->remote_node_id); + i += scnprintf(buf + i, max - i, "\n"); + } + mutex_unlock(&xprt_info_list_lock); + + return i; +} + +static int dump_servers(char *buf, int max) +{ + int i = 0, j; + struct msm_ipc_server *server; + struct msm_ipc_server_port *server_port; + + mutex_lock(&server_list_lock); + for (j = 0; j < SRV_HASH_SIZE; j++) { + list_for_each_entry(server, &server_list[j], list) { + list_for_each_entry(server_port, + &server->server_port_list, + list) { + i += scnprintf(buf + i, max - i, "Service: " + "0x%08x\n", server->name.service); + i += scnprintf(buf + i, max - i, "Instance: " + "0x%08x\n", server->name.instance); + i += scnprintf(buf + i, max - i, + "Node_id: 0x%08x\n", + server_port->server_addr.node_id); + i += scnprintf(buf + i, max - i, + "Port_id: 0x%08x\n", + server_port->server_addr.port_id); + i += scnprintf(buf + i, max - i, "\n"); + } + } + } + mutex_unlock(&server_list_lock); + + return i; +} + +static int dump_remote_ports(char *buf, int max) +{ + int i = 0, j, k; + struct msm_ipc_router_remote_port *rport_ptr; + struct msm_ipc_routing_table_entry *rt_entry; + + for (j = 0; j < RT_HASH_SIZE; j++) { + mutex_lock(&routing_table_lock); + list_for_each_entry(rt_entry, &routing_table[j], list) { + mutex_lock(&rt_entry->lock); + for (k = 0; k < RP_HASH_SIZE; k++) { + list_for_each_entry(rport_ptr, + &rt_entry->remote_port_list[k], + list) { + i += scnprintf(buf + i, max - i, + "Node_id: 0x%08x\n", + rport_ptr->node_id); + i += scnprintf(buf + i, max - i, + "Port_id: 0x%08x\n", + rport_ptr->port_id); + i += scnprintf(buf + i, max - i, + "Quota_cnt: %d\n", + rport_ptr->tx_quota_cnt); + i += scnprintf(buf + i, max - i, "\n"); + } + } + mutex_unlock(&rt_entry->lock); + } + mutex_unlock(&routing_table_lock); + } + + return i; +} + +static int dump_control_ports(char *buf, int max) +{ + int i = 0; + struct msm_ipc_port *port_ptr; + + mutex_lock(&control_ports_lock); + list_for_each_entry(port_ptr, &control_ports, list) { + i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n", + port_ptr->this_port.node_id); + i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n", + port_ptr->this_port.port_id); + i += scnprintf(buf + i, max - i, "\n"); + } + mutex_unlock(&control_ports_lock); + + return i; +} + +static int dump_local_ports(char *buf, int max) +{ + int i = 0, j; + unsigned long flags; + struct msm_ipc_port *port_ptr; + + mutex_lock(&local_ports_lock); + for (j = 0; j < LP_HASH_SIZE; j++) { + list_for_each_entry(port_ptr, &local_ports[j], list) { + spin_lock_irqsave(&port_ptr->port_lock, flags); + i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n", + port_ptr->this_port.node_id); + i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n", + port_ptr->this_port.port_id); + i += scnprintf(buf + i, max - i, "# pkts tx'd %d\n", + port_ptr->num_tx); + i += scnprintf(buf + i, max - i, "# pkts rx'd %d\n", + port_ptr->num_rx); + i += scnprintf(buf + i, max - i, "# bytes tx'd %ld\n", + port_ptr->num_tx_bytes); + i += scnprintf(buf + i, max - i, "# bytes rx'd %ld\n", + port_ptr->num_rx_bytes); + spin_unlock_irqrestore(&port_ptr->port_lock, flags); + i += scnprintf(buf + i, max - i, "\n"); + } + } + mutex_unlock(&local_ports_lock); + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +static void debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("msm_ipc_router", 0); + if (IS_ERR(dent)) + return; + + debug_create("dump_local_ports", 0444, dent, + dump_local_ports); + debug_create("dump_remote_ports", 0444, dent, + dump_remote_ports); + debug_create("dump_control_ports", 0444, dent, + dump_control_ports); + debug_create("dump_servers", 0444, dent, + dump_servers); + debug_create("dump_xprt_info", 0444, dent, + dump_xprt_info); + debug_create("dump_routing_table", 0444, dent, + dump_routing_table); +} + +#else +static void debugfs_init(void) {} +#endif + +static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_xprt_info *xprt_info; + struct msm_ipc_routing_table_entry *rt_entry; + + xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info), + GFP_KERNEL); + if (!xprt_info) + return -ENOMEM; + + xprt_info->xprt = xprt; + xprt_info->initialized = 0; + xprt_info->remote_node_id = -1; + INIT_LIST_HEAD(&xprt_info->pkt_list); + init_waitqueue_head(&xprt_info->read_wait); + mutex_init(&xprt_info->rx_lock); + mutex_init(&xprt_info->tx_lock); + wake_lock_init(&xprt_info->wakelock, + WAKE_LOCK_SUSPEND, xprt->name); + xprt_info->need_len = 0; + xprt_info->abort_data_read = 0; + INIT_WORK(&xprt_info->read_data, do_read_data); + INIT_LIST_HEAD(&xprt_info->list); + + xprt_info->workqueue = create_singlethread_workqueue(xprt->name); + if (!xprt_info->workqueue) { + kfree(xprt_info); + return -ENOMEM; + } + + if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) { + xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL; + xprt_info->initialized = 1; + } + + mutex_lock(&xprt_info_list_lock); + list_add_tail(&xprt_info->list, &xprt_info_list); + mutex_unlock(&xprt_info_list_lock); + + mutex_lock(&routing_table_lock); + if (!routing_table_inited) { + init_routing_table(); + rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL); + add_routing_table_entry(rt_entry); + routing_table_inited = 1; + } + mutex_unlock(&routing_table_lock); + + queue_work(xprt_info->workqueue, &xprt_info->read_data); + + xprt->priv = xprt_info; + + return 0; +} + +static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_xprt_info *xprt_info; + + if (xprt && xprt->priv) { + xprt_info = xprt->priv; + + xprt_info->abort_data_read = 1; + wake_up(&xprt_info->read_wait); + + mutex_lock(&xprt_info_list_lock); + list_del(&xprt_info->list); + mutex_unlock(&xprt_info_list_lock); + + flush_workqueue(xprt_info->workqueue); + destroy_workqueue(xprt_info->workqueue); + wake_lock_destroy(&xprt_info->wakelock); + + xprt->priv = 0; + kfree(xprt_info); + } +} + + +struct msm_ipc_router_xprt_work { + struct msm_ipc_router_xprt *xprt; + struct work_struct work; +}; + +static void xprt_open_worker(struct work_struct *work) +{ + struct msm_ipc_router_xprt_work *xprt_work = + container_of(work, struct msm_ipc_router_xprt_work, work); + + msm_ipc_router_add_xprt(xprt_work->xprt); + kfree(xprt_work); +} + +static void xprt_close_worker(struct work_struct *work) +{ + struct msm_ipc_router_xprt_work *xprt_work = + container_of(work, struct msm_ipc_router_xprt_work, work); + + modem_reset_cleanup(xprt_work->xprt->priv); + msm_ipc_router_remove_xprt(xprt_work->xprt); + + if (atomic_dec_return(&pending_close_count) == 0) + wake_up(&subsystem_restart_wait); + + kfree(xprt_work); +} + +void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, + unsigned event, + void *data) +{ + struct msm_ipc_router_xprt_info *xprt_info = xprt->priv; + struct msm_ipc_router_xprt_work *xprt_work; + struct rr_packet *pkt; + unsigned long ret; + + if (!msm_ipc_router_workqueue) { + ret = wait_for_completion_timeout(&msm_ipc_local_router_up, + IPC_ROUTER_INIT_TIMEOUT); + if (!ret || !msm_ipc_router_workqueue) { + pr_err("%s: IPC Router not initialized\n", __func__); + return; + } + } + + switch (event) { + case IPC_ROUTER_XPRT_EVENT_OPEN: + D("open event for '%s'\n", xprt->name); + xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work), + GFP_ATOMIC); + xprt_work->xprt = xprt; + INIT_WORK(&xprt_work->work, xprt_open_worker); + queue_work(msm_ipc_router_workqueue, &xprt_work->work); + break; + + case IPC_ROUTER_XPRT_EVENT_CLOSE: + D("close event for '%s'\n", xprt->name); + atomic_inc(&pending_close_count); + xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work), + GFP_ATOMIC); + xprt_work->xprt = xprt; + INIT_WORK(&xprt_work->work, xprt_close_worker); + queue_work(msm_ipc_router_workqueue, &xprt_work->work); + break; + } + + if (!data) + return; + + while (!xprt_info) { + msleep(100); + xprt_info = xprt->priv; + } + + pkt = clone_pkt((struct rr_packet *)data); + if (!pkt) + return; + + mutex_lock(&xprt_info->rx_lock); + list_add_tail(&pkt->list, &xprt_info->pkt_list); + wake_lock(&xprt_info->wakelock); + wake_up(&xprt_info->read_wait); + mutex_unlock(&xprt_info->rx_lock); +} + +static int modem_restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data); +static struct notifier_block msm_ipc_router_nb = { + .notifier_call = modem_restart_notifier_cb, +}; + +static int modem_restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + D("%s: SUBSYS_BEFORE_SHUTDOWN\n", __func__); + break; + + case SUBSYS_BEFORE_POWERUP: + D("%s: waiting for RPC restart to complete\n", __func__); + wait_event(subsystem_restart_wait, + atomic_read(&pending_close_count) == 0); + D("%s: finished restart wait\n", __func__); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static void *restart_notifier_handle; +static __init int msm_ipc_router_modem_restart_late_init(void) +{ + restart_notifier_handle = subsys_notif_register_notifier("modem", + &msm_ipc_router_nb); + return 0; +} +late_initcall(msm_ipc_router_modem_restart_late_init); + +static int __init msm_ipc_router_init(void) +{ + int i, ret; + struct msm_ipc_routing_table_entry *rt_entry; + + msm_ipc_router_debug_mask |= SMEM_LOG; + msm_ipc_router_workqueue = + create_singlethread_workqueue("msm_ipc_router"); + if (!msm_ipc_router_workqueue) + return -ENOMEM; + + debugfs_init(); + + for (i = 0; i < SRV_HASH_SIZE; i++) + INIT_LIST_HEAD(&server_list[i]); + + for (i = 0; i < LP_HASH_SIZE; i++) + INIT_LIST_HEAD(&local_ports[i]); + + mutex_lock(&routing_table_lock); + if (!routing_table_inited) { + init_routing_table(); + rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL); + add_routing_table_entry(rt_entry); + routing_table_inited = 1; + } + mutex_unlock(&routing_table_lock); + + init_waitqueue_head(&newserver_wait); + init_waitqueue_head(&subsystem_restart_wait); + ret = msm_ipc_router_init_sockets(); + if (ret < 0) + pr_err("%s: Init sockets failed\n", __func__); + + complete_all(&msm_ipc_local_router_up); + return ret; +} + +module_init(msm_ipc_router_init); +MODULE_DESCRIPTION("MSM IPC Router"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h new file mode 100644 index 00000000000..a90be23cb6a --- /dev/null +++ b/arch/arm/mach-msm/ipc_router.h @@ -0,0 +1,220 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_IPC_ROUTER_H +#define _ARCH_ARM_MACH_MSM_IPC_ROUTER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* definitions for the R2R wire protcol */ +#define IPC_ROUTER_VERSION 1 +#define IPC_ROUTER_PROCESSORS_MAX 4 + +#define IPC_ROUTER_CLIENT_BCAST_ID 0xffffffff +#define IPC_ROUTER_ADDRESS 0xfffffffe + +#define IPC_ROUTER_NID_LOCAL 1 +#define IPC_ROUTER_NID_REMOTE 0 + +#define IPC_ROUTER_CTRL_CMD_DATA 1 +#define IPC_ROUTER_CTRL_CMD_HELLO 2 +#define IPC_ROUTER_CTRL_CMD_BYE 3 +#define IPC_ROUTER_CTRL_CMD_NEW_SERVER 4 +#define IPC_ROUTER_CTRL_CMD_REMOVE_SERVER 5 +#define IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT 6 +#define IPC_ROUTER_CTRL_CMD_RESUME_TX 7 +#define IPC_ROUTER_CTRL_CMD_EXIT 8 +#define IPC_ROUTER_CTRL_CMD_PING 9 + +#define IPC_ROUTER_DEFAULT_RX_QUOTA 5 + +#define IPC_ROUTER_XPRT_EVENT_DATA 1 +#define IPC_ROUTER_XPRT_EVENT_OPEN 2 +#define IPC_ROUTER_XPRT_EVENT_CLOSE 3 + +#define NUM_NODES 2 + +#define IPC_ROUTER_INFINITY -1 +#define DEFAULT_RCV_TIMEO IPC_ROUTER_INFINITY + +#define ALIGN_SIZE(x) ((4 - ((x) & 3)) & 3) + +enum { + MSM_IPC_ROUTER_READ_CB = 0, + MSM_IPC_ROUTER_WRITE_DONE, +}; + +union rr_control_msg { + uint32_t cmd; + struct { + uint32_t cmd; + uint32_t service; + uint32_t instance; + uint32_t node_id; + uint32_t port_id; + } srv; + struct { + uint32_t cmd; + uint32_t node_id; + uint32_t port_id; + } cli; +}; + +struct rr_header { + uint32_t version; + uint32_t type; + uint32_t src_node_id; + uint32_t src_port_id; + uint32_t confirm_rx; + uint32_t size; + uint32_t dst_node_id; + uint32_t dst_port_id; +}; + +#define IPC_ROUTER_HDR_SIZE sizeof(struct rr_header) +#define MAX_IPC_PKT_SIZE 66000 +/* internals */ + +#define IPC_ROUTER_MAX_REMOTE_SERVERS 100 +#define MAX_WAKELOCK_NAME_SZ 32 + +struct rr_packet { + struct list_head list; + struct sk_buff_head *pkt_fragment_q; + uint32_t length; +}; + +struct msm_ipc_port { + struct list_head list; + + struct msm_ipc_port_addr this_port; + struct msm_ipc_port_name port_name; + uint32_t type; + unsigned flags; + spinlock_t port_lock; + + struct list_head incomplete; + struct mutex incomplete_lock; + + struct list_head port_rx_q; + struct mutex port_rx_q_lock; + char rx_wakelock_name[MAX_WAKELOCK_NAME_SZ]; + struct wake_lock port_rx_wake_lock; + wait_queue_head_t port_rx_wait_q; + + int restart_state; + spinlock_t restart_lock; + wait_queue_head_t restart_wait; + + void *endpoint; + void (*notify)(unsigned event, void *data, void *addr, void *priv); + + uint32_t num_tx; + uint32_t num_rx; + unsigned long num_tx_bytes; + unsigned long num_rx_bytes; + void *priv; +}; + +struct msm_ipc_sock { + struct sock sk; + struct msm_ipc_port *port; + void *default_pil; +}; + +enum write_data_type { + HEADER = 1, + PACKMARK, + PAYLOAD, +}; + +struct msm_ipc_router_xprt { + char *name; + uint32_t link_id; + void *priv; + + int (*read_avail)(struct msm_ipc_router_xprt *xprt); + int (*read)(void *data, uint32_t len, + struct msm_ipc_router_xprt *xprt); + int (*write_avail)(struct msm_ipc_router_xprt *xprt); + int (*write)(void *data, uint32_t len, + struct msm_ipc_router_xprt *xprt); + int (*close)(struct msm_ipc_router_xprt *xprt); +}; + +extern struct completion msm_ipc_remote_router_up; + +void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, + unsigned event, + void *data); + + +struct rr_packet *clone_pkt(struct rr_packet *pkt); +void release_pkt(struct rr_packet *pkt); + + +struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint, + void (*notify)(unsigned event, void *data, + void *addr, void *priv), + void *priv); +int msm_ipc_router_send_to(struct msm_ipc_port *src, + struct sk_buff_head *data, + struct msm_ipc_addr *dest); +int msm_ipc_router_read(struct msm_ipc_port *port_ptr, + struct sk_buff_head **data, + size_t buf_len); +int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr); +int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr); +int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name, + struct msm_ipc_port_addr *port_addr, + int num_entries_in_array, + uint32_t lookup_mask); +int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr); + +struct msm_ipc_port *msm_ipc_router_create_port( + void (*notify)(unsigned event, void *data, + void *addr, void *priv), + void *priv); +int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr, + struct sk_buff_head **data, + struct msm_ipc_addr *src_addr, + unsigned long timeout); +int msm_ipc_router_register_server(struct msm_ipc_port *server_port, + struct msm_ipc_addr *name); +int msm_ipc_router_unregister_server(struct msm_ipc_port *server_port); + + +int msm_ipc_router_init_sockets(void); +void msm_ipc_router_exit_sockets(void); + +#if defined CONFIG_MSM_IPC_ROUTER_SMD_XPRT +extern void *msm_ipc_load_default_node(void); + +extern void msm_ipc_unload_default_node(void *pil); +#else +static inline void *msm_ipc_load_default_node(void) +{ return NULL; } + +static inline void msm_ipc_unload_default_node(void *pil) { } +#endif + +#endif diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c new file mode 100644 index 00000000000..b2e649042ff --- /dev/null +++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c @@ -0,0 +1,537 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * IPC ROUTER SMD XPRT module. + */ +#define DEBUG + +#include +#include +#include + +#include +#include + +#include "ipc_router.h" +#include "smd_private.h" + +static int msm_ipc_router_smd_xprt_debug_mask; +module_param_named(debug_mask, msm_ipc_router_smd_xprt_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(DEBUG) +#define D(x...) do { \ +if (msm_ipc_router_smd_xprt_debug_mask) \ + pr_info(x); \ +} while (0) +#else +#define D(x...) do { } while (0) +#endif + +#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg)) + +#define NUM_SMD_XPRTS 3 +#define XPRT_NAME_LEN (SMD_MAX_CH_NAME_LEN + 12) + +struct msm_ipc_router_smd_xprt { + struct msm_ipc_router_xprt xprt; + smd_channel_t *channel; + struct workqueue_struct *smd_xprt_wq; + wait_queue_head_t write_avail_wait_q; + struct rr_packet *in_pkt; + int is_partial_in_pkt; + struct delayed_work read_work; + spinlock_t ss_reset_lock; /*Subsystem reset lock*/ + int ss_reset; + void *pil; +}; + +struct msm_ipc_router_smd_xprt_work { + struct msm_ipc_router_xprt *xprt; + struct work_struct work; +}; + +static void smd_xprt_read_data(struct work_struct *work); +static void smd_xprt_open_event(struct work_struct *work); +static void smd_xprt_close_event(struct work_struct *work); + +struct msm_ipc_router_smd_xprt_config { + char ch_name[SMD_MAX_CH_NAME_LEN]; + char xprt_name[XPRT_NAME_LEN]; + uint32_t edge; + uint32_t link_id; +}; + +struct msm_ipc_router_smd_xprt_config smd_xprt_cfg[] = { + {"RPCRPY_CNTL", "ipc_rtr_smd_rpcrpy_cntl", SMD_APPS_MODEM, 1}, + {"IPCRTR", "ipc_rtr_smd_ipcrtr", SMD_APPS_MODEM, 1}, + {"IPCRTR", "ipc_rtr_q6_ipcrtr", SMD_APPS_QDSP, 1}, +}; + +static struct msm_ipc_router_smd_xprt smd_remote_xprt[NUM_SMD_XPRTS]; + +static int find_smd_xprt_cfg(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < NUM_SMD_XPRTS; i++) { + if (!strncmp(pdev->name, smd_xprt_cfg[i].ch_name, 20) && + (pdev->id == smd_xprt_cfg[i].edge)) + return i; + } + + return -ENODEV; +} + +static int msm_ipc_router_smd_remote_write_avail( + struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + return smd_write_avail(smd_xprtp->channel); +} + +static int msm_ipc_router_smd_remote_write(void *data, + uint32_t len, + struct msm_ipc_router_xprt *xprt) +{ + struct rr_packet *pkt = (struct rr_packet *)data; + struct sk_buff *ipc_rtr_pkt; + int align_sz, align_data = 0; + int offset, sz_written = 0; + int ret, num_retries = 0; + unsigned long flags; + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + if (!pkt) + return -EINVAL; + + if (!len || pkt->length != len) + return -EINVAL; + + align_sz = ALIGN_SIZE(pkt->length); + while ((ret = smd_write_start(smd_xprtp->channel, + (len + align_sz))) < 0) { + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, + flags); + pr_err("%s: %s chnl reset\n", __func__, xprt->name); + return -ENETRESET; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + if (num_retries >= 5) { + pr_err("%s: Error %d @smd_write_start for %s\n", + __func__, ret, xprt->name); + return ret; + } + msleep(50); + num_retries++; + } + + D("%s: Ready to write\n", __func__); + skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) { + offset = 0; + while (offset < ipc_rtr_pkt->len) { + if (!smd_write_avail(smd_xprtp->channel)) + smd_enable_read_intr(smd_xprtp->channel); + + wait_event(smd_xprtp->write_avail_wait_q, + (smd_write_avail(smd_xprtp->channel) || + smd_xprtp->ss_reset)); + smd_disable_read_intr(smd_xprtp->channel); + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore( + &smd_xprtp->ss_reset_lock, flags); + pr_err("%s: %s chnl reset\n", + __func__, xprt->name); + return -ENETRESET; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, + flags); + + sz_written = smd_write_segment(smd_xprtp->channel, + ipc_rtr_pkt->data + offset, + (ipc_rtr_pkt->len - offset), 0); + offset += sz_written; + sz_written = 0; + } + D("%s: Wrote %d bytes over %s\n", + __func__, offset, xprt->name); + } + + if (align_sz) { + if (smd_write_avail(smd_xprtp->channel) < align_sz) + smd_enable_read_intr(smd_xprtp->channel); + + wait_event(smd_xprtp->write_avail_wait_q, + ((smd_write_avail(smd_xprtp->channel) >= + align_sz) || smd_xprtp->ss_reset)); + smd_disable_read_intr(smd_xprtp->channel); + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore( + &smd_xprtp->ss_reset_lock, flags); + pr_err("%s: %s chnl reset\n", + __func__, xprt->name); + return -ENETRESET; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, + flags); + + smd_write_segment(smd_xprtp->channel, + &align_data, align_sz, 0); + D("%s: Wrote %d align bytes over %s\n", + __func__, align_sz, xprt->name); + } + if (!smd_write_end(smd_xprtp->channel)) + D("%s: Finished writing\n", __func__); + return len; +} + +static int msm_ipc_router_smd_remote_close(struct msm_ipc_router_xprt *xprt) +{ + int rc; + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + rc = smd_close(smd_xprtp->channel); + if (smd_xprtp->pil) { + pil_put(smd_xprtp->pil); + smd_xprtp->pil = NULL; + } + return rc; +} + +static void smd_xprt_read_data(struct work_struct *work) +{ + int pkt_size, sz_read, sz; + struct sk_buff *ipc_rtr_pkt; + void *data; + unsigned long flags; + struct delayed_work *rwork = to_delayed_work(work); + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(rwork, struct msm_ipc_router_smd_xprt, read_work); + + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->in_pkt) + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->is_partial_in_pkt = 0; + pr_err("%s: %s channel reset\n", + __func__, smd_xprtp->xprt.name); + return; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + + D("%s pkt_size: %d, read_avail: %d\n", __func__, + smd_cur_packet_size(smd_xprtp->channel), + smd_read_avail(smd_xprtp->channel)); + while ((pkt_size = smd_cur_packet_size(smd_xprtp->channel)) && + smd_read_avail(smd_xprtp->channel)) { + if (!smd_xprtp->is_partial_in_pkt) { + smd_xprtp->in_pkt = kzalloc(sizeof(struct rr_packet), + GFP_KERNEL); + if (!smd_xprtp->in_pkt) { + pr_err("%s: Couldn't alloc rr_packet\n", + __func__); + return; + } + + smd_xprtp->in_pkt->pkt_fragment_q = + kmalloc(sizeof(struct sk_buff_head), + GFP_KERNEL); + if (!smd_xprtp->in_pkt->pkt_fragment_q) { + pr_err("%s: Couldn't alloc pkt_fragment_q\n", + __func__); + kfree(smd_xprtp->in_pkt); + return; + } + skb_queue_head_init(smd_xprtp->in_pkt->pkt_fragment_q); + smd_xprtp->is_partial_in_pkt = 1; + D("%s: Allocated rr_packet\n", __func__); + } + + if (((pkt_size >= MIN_FRAG_SZ) && + (smd_read_avail(smd_xprtp->channel) < MIN_FRAG_SZ)) || + ((pkt_size < MIN_FRAG_SZ) && + (smd_read_avail(smd_xprtp->channel) < pkt_size))) + return; + + sz = smd_read_avail(smd_xprtp->channel); + do { + ipc_rtr_pkt = alloc_skb(sz, GFP_KERNEL); + if (!ipc_rtr_pkt) { + if (sz <= (PAGE_SIZE/2)) { + queue_delayed_work( + smd_xprtp->smd_xprt_wq, + &smd_xprtp->read_work, + msecs_to_jiffies(100)); + return; + } + sz = sz / 2; + } + } while (!ipc_rtr_pkt); + + D("%s: Allocated the sk_buff of size %d\n", __func__, sz); + data = skb_put(ipc_rtr_pkt, sz); + sz_read = smd_read(smd_xprtp->channel, data, sz); + if (sz_read != sz) { + pr_err("%s: Couldn't read %s completely\n", + __func__, smd_xprtp->xprt.name); + kfree_skb(ipc_rtr_pkt); + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->is_partial_in_pkt = 0; + return; + } + skb_queue_tail(smd_xprtp->in_pkt->pkt_fragment_q, ipc_rtr_pkt); + smd_xprtp->in_pkt->length += sz_read; + if (sz_read != pkt_size) + smd_xprtp->is_partial_in_pkt = 1; + else + smd_xprtp->is_partial_in_pkt = 0; + + if (!smd_xprtp->is_partial_in_pkt) { + D("%s: Packet size read %d\n", + __func__, smd_xprtp->in_pkt->length); + msm_ipc_router_xprt_notify(&smd_xprtp->xprt, + IPC_ROUTER_XPRT_EVENT_DATA, + (void *)smd_xprtp->in_pkt); + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->in_pkt = NULL; + } + } +} + +static void smd_xprt_open_event(struct work_struct *work) +{ + struct msm_ipc_router_smd_xprt_work *xprt_work = + container_of(work, struct msm_ipc_router_smd_xprt_work, work); + + msm_ipc_router_xprt_notify(xprt_work->xprt, + IPC_ROUTER_XPRT_EVENT_OPEN, NULL); + D("%s: Notified IPC Router of %s OPEN\n", + __func__, xprt_work->xprt->name); + kfree(xprt_work); +} + +static void smd_xprt_close_event(struct work_struct *work) +{ + struct msm_ipc_router_smd_xprt_work *xprt_work = + container_of(work, struct msm_ipc_router_smd_xprt_work, work); + + msm_ipc_router_xprt_notify(xprt_work->xprt, + IPC_ROUTER_XPRT_EVENT_CLOSE, NULL); + D("%s: Notified IPC Router of %s CLOSE\n", + __func__, xprt_work->xprt->name); + kfree(xprt_work); +} + +static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event) +{ + unsigned long flags; + struct msm_ipc_router_smd_xprt *smd_xprtp; + struct msm_ipc_router_smd_xprt_work *xprt_work; + + smd_xprtp = (struct msm_ipc_router_smd_xprt *)_dev; + if (!smd_xprtp) + return; + + switch (event) { + case SMD_EVENT_DATA: + if (smd_read_avail(smd_xprtp->channel)) + queue_delayed_work(smd_xprtp->smd_xprt_wq, + &smd_xprtp->read_work, 0); + if (smd_write_avail(smd_xprtp->channel)) + wake_up(&smd_xprtp->write_avail_wait_q); + break; + + case SMD_EVENT_OPEN: + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + smd_xprtp->ss_reset = 0; + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work), + GFP_ATOMIC); + if (!xprt_work) { + pr_err("%s: Couldn't notify %d event to IPC Router\n", + __func__, event); + return; + } + xprt_work->xprt = &smd_xprtp->xprt; + INIT_WORK(&xprt_work->work, smd_xprt_open_event); + queue_work(smd_xprtp->smd_xprt_wq, &xprt_work->work); + break; + + case SMD_EVENT_CLOSE: + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + smd_xprtp->ss_reset = 1; + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + wake_up(&smd_xprtp->write_avail_wait_q); + xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work), + GFP_ATOMIC); + if (!xprt_work) { + pr_err("%s: Couldn't notify %d event to IPC Router\n", + __func__, event); + return; + } + xprt_work->xprt = &smd_xprtp->xprt; + INIT_WORK(&xprt_work->work, smd_xprt_close_event); + queue_work(smd_xprtp->smd_xprt_wq, &xprt_work->work); + break; + } +} + +static void *msm_ipc_load_subsystem(uint32_t edge) +{ + void *pil = NULL; + const char *peripheral; + + peripheral = smd_edge_to_subsystem(edge); + if (peripheral) { + pil = pil_get(peripheral); + if (IS_ERR(pil)) { + pr_err("%s: Failed to load %s\n", + __func__, peripheral); + pil = NULL; + } + } + return pil; +} + +static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev) +{ + int rc; + int id; /*Index into the smd_xprt_cfg table*/ + + id = find_smd_xprt_cfg(pdev); + if (id < 0) { + pr_err("%s: called for unknown ch %s\n", + __func__, pdev->name); + return id; + } + + smd_remote_xprt[id].smd_xprt_wq = + create_singlethread_workqueue(pdev->name); + if (!smd_remote_xprt[id].smd_xprt_wq) { + pr_err("%s: WQ creation failed for %s\n", + __func__, pdev->name); + return -EFAULT; + } + + smd_remote_xprt[id].xprt.name = smd_xprt_cfg[id].xprt_name; + smd_remote_xprt[id].xprt.link_id = smd_xprt_cfg[id].link_id; + smd_remote_xprt[id].xprt.read_avail = NULL; + smd_remote_xprt[id].xprt.read = NULL; + smd_remote_xprt[id].xprt.write_avail = + msm_ipc_router_smd_remote_write_avail; + smd_remote_xprt[id].xprt.write = msm_ipc_router_smd_remote_write; + smd_remote_xprt[id].xprt.close = msm_ipc_router_smd_remote_close; + smd_remote_xprt[id].xprt.priv = NULL; + + init_waitqueue_head(&smd_remote_xprt[id].write_avail_wait_q); + smd_remote_xprt[id].in_pkt = NULL; + smd_remote_xprt[id].is_partial_in_pkt = 0; + INIT_DELAYED_WORK(&smd_remote_xprt[id].read_work, smd_xprt_read_data); + spin_lock_init(&smd_remote_xprt[id].ss_reset_lock); + smd_remote_xprt[id].ss_reset = 0; + + smd_remote_xprt[id].pil = msm_ipc_load_subsystem( + smd_xprt_cfg[id].edge); + rc = smd_named_open_on_edge(smd_xprt_cfg[id].ch_name, + smd_xprt_cfg[id].edge, + &smd_remote_xprt[id].channel, + &smd_remote_xprt[id], + msm_ipc_router_smd_remote_notify); + if (rc < 0) { + pr_err("%s: Channel open failed for %s\n", + __func__, smd_xprt_cfg[id].ch_name); + if (smd_remote_xprt[id].pil) { + pil_put(smd_remote_xprt[id].pil); + smd_remote_xprt[id].pil = NULL; + } + destroy_workqueue(smd_remote_xprt[id].smd_xprt_wq); + return rc; + } + + smd_disable_read_intr(smd_remote_xprt[id].channel); + + smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT); + + return 0; +} + +void *msm_ipc_load_default_node(void) +{ + void *pil = NULL; + const char *peripheral; + + peripheral = smd_edge_to_subsystem(SMD_APPS_MODEM); + if (peripheral && !strncmp(peripheral, "modem", 6)) { + pil = pil_get(peripheral); + if (IS_ERR(pil)) { + pr_err("%s: Failed to load %s\n", + __func__, peripheral); + pil = NULL; + } + } + return pil; +} +EXPORT_SYMBOL(msm_ipc_load_default_node); + +void msm_ipc_unload_default_node(void *pil) +{ + if (pil) + pil_put(pil); +} +EXPORT_SYMBOL(msm_ipc_unload_default_node); + +static struct platform_driver msm_ipc_router_smd_remote_driver[] = { + { + .probe = msm_ipc_router_smd_remote_probe, + .driver = { + .name = "RPCRPY_CNTL", + .owner = THIS_MODULE, + }, + }, + { + .probe = msm_ipc_router_smd_remote_probe, + .driver = { + .name = "IPCRTR", + .owner = THIS_MODULE, + }, + }, +}; + +static int __init msm_ipc_router_smd_init(void) +{ + int i, ret, rc = 0; + BUG_ON(ARRAY_SIZE(smd_xprt_cfg) != NUM_SMD_XPRTS); + for (i = 0; i < ARRAY_SIZE(msm_ipc_router_smd_remote_driver); i++) { + ret = platform_driver_register( + &msm_ipc_router_smd_remote_driver[i]); + if (ret) { + pr_err("%s: Failed to register platform driver for" + " xprt%d. Continuing...\n", __func__, i); + rc = ret; + } + } + return rc; +} + +module_init(msm_ipc_router_smd_init); +MODULE_DESCRIPTION("IPC Router SMD XPRT"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c new file mode 100644 index 00000000000..d82ffe54cd5 --- /dev/null +++ b/arch/arm/mach-msm/ipc_socket.c @@ -0,0 +1,548 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + +#include +#include + +#include + +#include "ipc_router.h" + +#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk)) +#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port)) + +static int sockets_enabled; +static struct proto msm_ipc_proto; +static const struct proto_ops msm_ipc_proto_ops; + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int check_permissions(void) +{ + int rc = 0; + if (!current_euid() || in_egroup_p(AID_NET_RAW)) + rc = 1; + return rc; +} +# else +static inline int check_permissions(void) +{ + return 1; +} +#endif + +static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect, + struct iovec const *msg_sect, + size_t total_len) +{ + struct sk_buff_head *msg_head; + struct sk_buff *msg; + int i, copied, first = 1; + int data_size = 0, request_size, offset; + void *data; + + for (i = 0; i < num_sect; i++) + data_size += msg_sect[i].iov_len; + + if (!data_size) + return NULL; + + msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL); + if (!msg_head) { + pr_err("%s: cannot allocate skb_head\n", __func__); + return NULL; + } + skb_queue_head_init(msg_head); + + for (copied = 1, i = 0; copied && (i < num_sect); i++) { + data_size = msg_sect[i].iov_len; + offset = 0; + while (offset != msg_sect[i].iov_len) { + request_size = data_size; + if (first) + request_size += IPC_ROUTER_HDR_SIZE; + + msg = alloc_skb(request_size, GFP_KERNEL); + if (!msg) { + if (request_size <= (PAGE_SIZE/2)) { + pr_err("%s: cannot allocated skb\n", + __func__); + goto msg_build_failure; + } + data_size = data_size / 2; + continue; + } + + if (first) { + skb_reserve(msg, IPC_ROUTER_HDR_SIZE); + first = 0; + } + + data = skb_put(msg, data_size); + copied = !copy_from_user(msg->data, + msg_sect[i].iov_base + offset, + data_size); + if (!copied) { + pr_err("%s: copy_from_user failed\n", + __func__); + kfree_skb(msg); + goto msg_build_failure; + } + skb_queue_tail(msg_head, msg); + offset += data_size; + data_size = msg_sect[i].iov_len - offset; + } + } + return msg_head; + +msg_build_failure: + while (!skb_queue_empty(msg_head)) { + msg = skb_dequeue(msg_head); + kfree_skb(msg); + } + kfree(msg_head); + return NULL; +} + +static int msm_ipc_router_extract_msg(struct msghdr *m, + struct sk_buff_head *msg_head) +{ + struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)m->msg_name; + struct rr_header *hdr; + struct sk_buff *temp; + int offset = 0, data_len = 0, copy_len; + + if (!m || !msg_head) { + pr_err("%s: Invalid pointers passed\n", __func__); + return -EINVAL; + } + + temp = skb_peek(msg_head); + hdr = (struct rr_header *)(temp->data); + if (addr || (hdr->src_port_id != IPC_ROUTER_ADDRESS)) { + addr->family = AF_MSM_IPC; + addr->address.addrtype = MSM_IPC_ADDR_ID; + addr->address.addr.port_addr.node_id = hdr->src_node_id; + addr->address.addr.port_addr.port_id = hdr->src_port_id; + m->msg_namelen = sizeof(struct sockaddr_msm_ipc); + } + + data_len = hdr->size; + skb_pull(temp, IPC_ROUTER_HDR_SIZE); + skb_queue_walk(msg_head, temp) { + copy_len = data_len < temp->len ? data_len : temp->len; + if (copy_to_user(m->msg_iov->iov_base + offset, temp->data, + copy_len)) { + pr_err("%s: Copy to user failed\n", __func__); + return -EFAULT; + } + offset += copy_len; + data_len -= copy_len; + } + return offset; +} + +static void msm_ipc_router_release_msg(struct sk_buff_head *msg_head) +{ + struct sk_buff *temp; + + if (!msg_head) { + pr_err("%s: Invalid msg pointer\n", __func__); + return; + } + + while (!skb_queue_empty(msg_head)) { + temp = skb_dequeue(msg_head); + kfree_skb(temp); + } + kfree(msg_head); +} + +static int msm_ipc_router_create(struct net *net, + struct socket *sock, + int protocol, + int kern) +{ + struct sock *sk; + struct msm_ipc_port *port_ptr; + void *pil; + + if (!check_permissions()) { + pr_err("%s: Do not have permissions\n", __func__); + return -EPERM; + } + + if (unlikely(protocol != 0)) { + pr_err("%s: Protocol not supported\n", __func__); + return -EPROTONOSUPPORT; + } + + switch (sock->type) { + case SOCK_DGRAM: + break; + default: + pr_err("%s: Protocol type not supported\n", __func__); + return -EPROTOTYPE; + } + + sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto); + if (!sk) { + pr_err("%s: sk_alloc failed\n", __func__); + return -ENOMEM; + } + + port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL); + if (!port_ptr) { + pr_err("%s: port_ptr alloc failed\n", __func__); + sk_free(sk); + return -ENOMEM; + } + + sock->ops = &msm_ipc_proto_ops; + sock_init_data(sock, sk); + sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO; + + pil = msm_ipc_load_default_node(); + msm_ipc_sk(sk)->port = port_ptr; + msm_ipc_sk(sk)->default_pil = pil; + + return 0; +} + +int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr, + int uaddr_len) +{ + struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr; + struct sock *sk = sock->sk; + struct msm_ipc_port *port_ptr; + int ret; + + if (!sk) + return -EINVAL; + + if (!uaddr_len) { + pr_err("%s: Invalid address length\n", __func__); + return -EINVAL; + } + + if (addr->family != AF_MSM_IPC) { + pr_err("%s: Address family is incorrect\n", __func__); + return -EAFNOSUPPORT; + } + + if (addr->address.addrtype != MSM_IPC_ADDR_NAME) { + pr_err("%s: Address type is incorrect\n", __func__); + return -EINVAL; + } + + port_ptr = msm_ipc_sk_port(sk); + if (!port_ptr) + return -ENODEV; + + lock_sock(sk); + + ret = msm_ipc_router_register_server(port_ptr, &addr->address); + + release_sock(sk); + return ret; +} + +static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t total_len) +{ + struct sock *sk = sock->sk; + struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk); + struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name; + struct sk_buff_head *msg; + int ret; + + if (!dest) + return -EDESTADDRREQ; + + if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC) + return -EINVAL; + + if (total_len > MAX_IPC_PKT_SIZE) + return -EINVAL; + + lock_sock(sk); + msg = msm_ipc_router_build_msg(m->msg_iovlen, m->msg_iov, total_len); + if (!msg) { + pr_err("%s: Msg build failure\n", __func__); + ret = -ENOMEM; + goto out_sendmsg; + } + + ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address); + if (ret == (IPC_ROUTER_HDR_SIZE + total_len)) + ret = total_len; + +out_sendmsg: + release_sock(sk); + return ret; +} + +static int msm_ipc_router_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *m, size_t buf_len, int flags) +{ + struct sock *sk = sock->sk; + struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk); + struct sk_buff_head *msg; + long timeout; + int ret; + + if (m->msg_iovlen != 1) + return -EOPNOTSUPP; + + if (!buf_len) + return -EINVAL; + + lock_sock(sk); + timeout = sk->sk_rcvtimeo; + mutex_lock(&port_ptr->port_rx_q_lock); + while (list_empty(&port_ptr->port_rx_q)) { + mutex_unlock(&port_ptr->port_rx_q_lock); + release_sock(sk); + if (timeout < 0) { + ret = wait_event_interruptible( + port_ptr->port_rx_wait_q, + !list_empty(&port_ptr->port_rx_q)); + if (ret) + return ret; + } else if (timeout > 0) { + timeout = wait_event_interruptible_timeout( + port_ptr->port_rx_wait_q, + !list_empty(&port_ptr->port_rx_q), + timeout); + if (timeout < 0) + return -EFAULT; + } + + if (timeout == 0) + return -ETIMEDOUT; + lock_sock(sk); + mutex_lock(&port_ptr->port_rx_q_lock); + } + mutex_unlock(&port_ptr->port_rx_q_lock); + + ret = msm_ipc_router_read(port_ptr, &msg, buf_len); + if (ret <= 0 || !msg) { + release_sock(sk); + return ret; + } + + ret = msm_ipc_router_extract_msg(m, msg); + msm_ipc_router_release_msg(msg); + msg = NULL; + release_sock(sk); + return ret; +} + +static int msm_ipc_router_ioctl(struct socket *sock, + unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + struct msm_ipc_port *port_ptr; + struct server_lookup_args server_arg; + struct msm_ipc_port_addr *port_addr = NULL; + unsigned int n, port_addr_sz = 0; + int ret; + + if (!sk) + return -EINVAL; + + lock_sock(sk); + port_ptr = msm_ipc_sk_port(sock->sk); + if (!port_ptr) { + release_sock(sk); + return -EINVAL; + } + + switch (cmd) { + case IPC_ROUTER_IOCTL_GET_VERSION: + n = IPC_ROUTER_VERSION; + ret = put_user(n, (unsigned int *)arg); + break; + + case IPC_ROUTER_IOCTL_GET_MTU: + n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE); + ret = put_user(n, (unsigned int *)arg); + break; + + case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE: + ret = msm_ipc_router_get_curr_pkt_size(port_ptr); + break; + + case IPC_ROUTER_IOCTL_LOOKUP_SERVER: + ret = copy_from_user(&server_arg, (void *)arg, + sizeof(server_arg)); + if (ret) { + ret = -EFAULT; + break; + } + + if (server_arg.num_entries_in_array < 0) { + ret = -EINVAL; + break; + } + if (server_arg.num_entries_in_array) { + port_addr_sz = server_arg.num_entries_in_array * + sizeof(*port_addr); + port_addr = kmalloc(port_addr_sz, GFP_KERNEL); + if (!port_addr) { + ret = -ENOMEM; + break; + } + } + ret = msm_ipc_router_lookup_server_name(&server_arg.port_name, + port_addr, server_arg.num_entries_in_array, + server_arg.lookup_mask); + if (ret < 0) { + pr_err("%s: Server not found\n", __func__); + ret = -ENODEV; + kfree(port_addr); + break; + } + server_arg.num_entries_found = ret; + + ret = copy_to_user((void *)arg, &server_arg, + sizeof(server_arg)); + if (port_addr_sz) { + ret = copy_to_user((void *)(arg + sizeof(server_arg)), + port_addr, port_addr_sz); + if (ret) + ret = -EFAULT; + kfree(port_addr); + } + break; + + case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT: + ret = msm_ipc_router_bind_control_port(port_ptr); + break; + + default: + ret = -EINVAL; + } + release_sock(sk); + return ret; +} + +static unsigned int msm_ipc_router_poll(struct file *file, + struct socket *sock, poll_table *wait) +{ + struct sock *sk = sock->sk; + struct msm_ipc_port *port_ptr; + uint32_t mask = 0; + + if (!sk) + return -EINVAL; + + port_ptr = msm_ipc_sk_port(sk); + if (!port_ptr) + return -EINVAL; + + poll_wait(file, &port_ptr->port_rx_wait_q, wait); + + if (!list_empty(&port_ptr->port_rx_q)) + mask |= (POLLRDNORM | POLLIN); + + return mask; +} + +static int msm_ipc_router_close(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk); + void *pil = msm_ipc_sk(sk)->default_pil; + int ret; + + lock_sock(sk); + ret = msm_ipc_router_close_port(port_ptr); + msm_ipc_unload_default_node(pil); + release_sock(sk); + sock_put(sk); + sock->sk = NULL; + + return ret; +} + +static const struct net_proto_family msm_ipc_family_ops = { + .owner = THIS_MODULE, + .family = AF_MSM_IPC, + .create = msm_ipc_router_create +}; + +static const struct proto_ops msm_ipc_proto_ops = { + .owner = THIS_MODULE, + .family = AF_MSM_IPC, + .bind = msm_ipc_router_bind, + .connect = sock_no_connect, + .sendmsg = msm_ipc_router_sendmsg, + .recvmsg = msm_ipc_router_recvmsg, + .ioctl = msm_ipc_router_ioctl, + .poll = msm_ipc_router_poll, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .release = msm_ipc_router_close, +}; + +static struct proto msm_ipc_proto = { + .name = "MSM_IPC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct msm_ipc_sock), +}; + +int msm_ipc_router_init_sockets(void) +{ + int ret; + + ret = proto_register(&msm_ipc_proto, 1); + if (ret) { + pr_err("Failed to register MSM_IPC protocol type\n"); + goto out_init_sockets; + } + + ret = sock_register(&msm_ipc_family_ops); + if (ret) { + pr_err("Failed to register MSM_IPC socket type\n"); + proto_unregister(&msm_ipc_proto); + goto out_init_sockets; + } + + sockets_enabled = 1; +out_init_sockets: + return ret; +} + +void msm_ipc_router_exit_sockets(void) +{ + if (!sockets_enabled) + return; + + sockets_enabled = 0; + sock_unregister(msm_ipc_family_ops.family); + proto_unregister(&msm_ipc_proto); +} diff --git a/arch/arm/mach-msm/irq-vic.c b/arch/arm/mach-msm/irq-vic.c index 1b54f807c2d..489faa33e20 100644 --- a/arch/arm/mach-msm/irq-vic.c +++ b/arch/arm/mach-msm/irq-vic.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -23,11 +23,16 @@ #include #include +#include +#include +#include #include #include +#include +#include "fiq.h" #include "smd_private.h" enum { @@ -71,7 +76,7 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, #define VIC_INT_POLARITY3 VIC_REG(0x005C) /* 1: NEG, 0: POS */ #define VIC_NO_PEND_VAL VIC_REG(0x0060) -#if defined(CONFIG_ARCH_MSM_SCORPION) +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) #define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064) #define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */ #define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */ @@ -105,7 +110,7 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, #define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ #define VIC_IRQ_VEC_WR VIC_REG(0x00D8) -#if defined(CONFIG_ARCH_MSM_SCORPION) +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) #define VIC_FIQ_VEC_RD VIC_REG(0x00DC) #define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0) #define VIC_FIQ_VEC_WR VIC_REG(0x00E4) @@ -124,7 +129,7 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) -#if defined(CONFIG_ARCH_MSM7X30) +#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_FSM9XXX) #define VIC_NUM_REGS 4 #else #define VIC_NUM_REGS 2 @@ -160,10 +165,13 @@ static struct { static uint32_t msm_irq_idle_disable[VIC_NUM_REGS]; #define SMSM_FAKE_IRQ (0xff) +#if !defined(CONFIG_ARCH_FSM9XXX) static uint8_t msm_irq_to_smsm[NR_IRQS] = { +#if !defined(CONFIG_ARCH_MSM7X27A) [INT_MDDI_EXT] = 1, [INT_MDDI_PRI] = 2, [INT_MDDI_CLIENT] = 3, +#endif [INT_USB_OTG] = 4, [INT_PWB_I2C] = 5, @@ -217,6 +225,18 @@ static uint8_t msm_irq_to_smsm[NR_IRQS] = { [INT_SIRC_1] = SMSM_FAKE_IRQ, #endif }; +# else /* CONFIG_ARCH_FSM9XXX */ +static uint8_t msm_irq_to_smsm[NR_IRQS] = { + [INT_UART1] = 11, + [INT_A9_M2A_0] = SMSM_FAKE_IRQ, + [INT_A9_M2A_1] = SMSM_FAKE_IRQ, + [INT_A9_M2A_5] = SMSM_FAKE_IRQ, + [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_SIRC_0] = 10, + [INT_ADSP_A11] = SMSM_FAKE_IRQ, +}; +#endif /* CONFIG_ARCH_FSM9XXX */ static inline void msm_irq_write_all_regs(void __iomem *base, unsigned int val) { @@ -228,8 +248,32 @@ static inline void msm_irq_write_all_regs(void __iomem *base, unsigned int val) static void msm_irq_ack(struct irq_data *d) { + uint32_t mask; + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_CLEAR0, d->irq); - writel(1 << (d->irq & 31), reg); + mask = 1 << (d->irq & 31); + writel(mask, reg); + mb(); +} + +static void msm_irq_disable(struct irq_data *d) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENCLEAR0, d->irq); + unsigned index = VIC_INT_TO_REG_INDEX(d->irq); + uint32_t mask = 1UL << (d->irq & 31); + int smsm_irq = msm_irq_to_smsm[d->irq]; + + if (!(msm_irq_shadow_reg[index].int_en[1] & mask)) { + msm_irq_shadow_reg[index].int_en[0] &= ~mask; + writel(mask, reg); + mb(); + if (smsm_irq == 0) + msm_irq_idle_disable[index] &= ~mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] &= ~mask; + } + } } static void msm_irq_mask(struct irq_data *d) @@ -241,6 +285,7 @@ static void msm_irq_mask(struct irq_data *d) msm_irq_shadow_reg[index].int_en[0] &= ~mask; writel(mask, reg); + mb(); if (smsm_irq == 0) msm_irq_idle_disable[index] &= ~mask; else { @@ -258,6 +303,7 @@ static void msm_irq_unmask(struct irq_data *d) msm_irq_shadow_reg[index].int_en[0] |= mask; writel(mask, reg); + mb(); if (smsm_irq == 0) msm_irq_idle_disable[index] |= mask; @@ -295,7 +341,7 @@ static int msm_irq_set_wake(struct irq_data *d, unsigned int on) static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) { - void __iomem *treg = VIC_INT_TO_REG_ADDR(VIC_INT_TYPE0, d->irq); + void __iomem *treg = VIC_INT_TO_REG_ADDR(VIC_INT_TYPE0, d->irq); void __iomem *preg = VIC_INT_TO_REG_ADDR(VIC_INT_POLARITY0, d->irq); unsigned index = VIC_INT_TO_REG_INDEX(d->irq); int b = 1 << (d->irq & 31); @@ -320,18 +366,220 @@ static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) __irq_set_handler_locked(d->irq, handle_level_irq); } writel(type, treg); + mb(); msm_irq_shadow_reg[index].int_type = type; return 0; } +unsigned int msm_irq_pending(void) +{ + unsigned int i, pending = 0; + + for (i = 0; (i < VIC_NUM_REGS) && !pending; i++) + pending |= readl(VIC_IRQ_STATUS0 + (i * 4)); + + return pending; +} + +int msm_irq_idle_sleep_allowed(void) +{ + uint32_t i, disable = 0; + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) + DPRINT_ARRAY(msm_irq_idle_disable, + "msm_irq_idle_sleep_allowed: disable"); + + for (i = 0; i < VIC_NUM_REGS; i++) + disable |= msm_irq_idle_disable[i]; + + return !disable; +} + +/* + * Prepare interrupt subsystem for entering sleep -- phase 1. + * If modem_wake is true, return currently enabled interrupts in *irq_mask. + */ +void msm_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t *irq_mask) +{ + if (modem_wake) { + *irq_mask = msm_irq_smsm_wake_enable[!from_idle]; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO + "%s irq_mask %x\n", __func__, *irq_mask); + } +} + +/* + * Prepare interrupt subsystem for entering sleep -- phase 2. + * Detect any pending interrupts and configure interrupt hardware. + * + * Return value: + * -EAGAIN: there are pending interrupt(s); interrupt configuration + * is not changed. + * 0: success + */ +int msm_irq_enter_sleep2(bool modem_wake, int from_idle) +{ + int i, limit = 10; + uint32_t pending[VIC_NUM_REGS]; + + if (from_idle && !modem_wake) + return 0; + + /* edge triggered interrupt may get lost if this mode is used */ + WARN_ON_ONCE(!modem_wake && !from_idle); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s change irq, pend", __func__); + + for (i = 0; i < VIC_NUM_REGS; i++) { + pending[i] = readl(VIC_IRQ_STATUS0 + (i * 4)); + pending[i] &= msm_irq_shadow_reg[i].int_en[!from_idle]; + } + + /* + * Clear INT_A9_M2A_5 since requesting sleep triggers it. + * In some arch e.g. FSM9XXX, INT_A9_M2A_5 may not be in the first set. + */ + pending[INT_A9_M2A_5 / 32] &= ~(1U << (INT_A9_M2A_5 % 32)); + + for (i = 0; i < VIC_NUM_REGS; i++) { + if (pending[i]) { + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) + DPRINT_ARRAY(pending, "%s abort", + __func__); + return -EAGAIN; + } + } + + msm_irq_write_all_regs(VIC_INT_EN0, 0); + + while (limit-- > 0) { + int pend_irq; + int irq = readl(VIC_IRQ_VEC_RD); + if (irq == -1) + break; + pend_irq = readl(VIC_IRQ_VEC_PEND_RD); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + printk(KERN_INFO "%s cleared int %d (%d)\n", + __func__, irq, pend_irq); + } + + if (modem_wake) { + struct irq_data d = { .irq = INT_A9_M2A_6 }; + msm_irq_set_type(&d, IRQF_TRIGGER_RISING); + __raw_writel(1U << (INT_A9_M2A_6 % 32), + VIC_INT_TO_REG_ADDR(VIC_INT_ENSET0, INT_A9_M2A_6)); + } else { + for (i = 0; i < VIC_NUM_REGS; i++) + writel(msm_irq_shadow_reg[i].int_en[1], + VIC_INT_ENSET0 + (i * 4)); + } + mb(); + + return 0; +} + +/* + * Restore interrupt subsystem from sleep -- phase 1. + * Configure interrupt hardware. + */ +void msm_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs) +{ + int i; + struct irq_data d = { .irq = INT_A9_M2A_6 }; + + msm_irq_ack(&d); + + for (i = 0; i < VIC_NUM_REGS; i++) { + writel(msm_irq_shadow_reg[i].int_type, + VIC_INT_TYPE0 + i * 4); + writel(msm_irq_shadow_reg[i].int_polarity, + VIC_INT_POLARITY0 + i * 4); + writel(msm_irq_shadow_reg[i].int_en[0], + VIC_INT_EN0 + i * 4); + writel(msm_irq_shadow_reg[i].int_select, + VIC_INT_SELECT0 + i * 4); + } + + writel(3, VIC_INT_MASTEREN); + mb(); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s %x %x %x now", + __func__, irq_mask, pending_irqs, wakeup_reason); +} + +/* + * Restore interrupt subsystem from sleep -- phase 2. + * Poke the specified pending interrupts into interrupt hardware. + */ +void msm_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending) +{ + int i; + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s %x %x %x now", + __func__, irq_mask, pending, wakeup_reason); + + for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { + unsigned reg_offset = VIC_INT_TO_REG_ADDR(0, i); + uint32_t reg_mask = 1UL << (i & 31); + int smsm_irq = msm_irq_to_smsm[i]; + uint32_t smsm_mask; + + if (smsm_irq == 0) + continue; + + smsm_mask = 1U << (smsm_irq - 1); + if (!(pending & smsm_mask)) + continue; + + pending &= ~smsm_mask; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + DPRINT_REGS(VIC_IRQ_STATUS, + "%s: irq %d still pending %x now", + __func__, i, pending); +#ifdef DEBUG_INTERRUPT_TRIGGER + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + writel(reg_mask, VIC_INT_CLEAR0 + reg_offset); +#endif + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + continue; + + writel(reg_mask, VIC_SOFTINT0 + reg_offset); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) + DPRINT_REGS(VIC_IRQ_STATUS, + "%s: irq %d need trigger, now", + __func__, i); + } + mb(); +} + +/* + * Restore interrupt subsystem from sleep -- phase 3. + * Print debug information. + */ +void msm_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs) +{ + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s %x %x %x state %x now", + __func__, irq_mask, pending_irqs, wakeup_reason, + smsm_get_state(SMSM_MODEM_STATE)); +} + static struct irq_chip msm_irq_chip = { - .name = "msm", - .irq_disable = msm_irq_mask, - .irq_ack = msm_irq_ack, - .irq_mask = msm_irq_mask, - .irq_unmask = msm_irq_unmask, - .irq_set_wake = msm_irq_set_wake, - .irq_set_type = msm_irq_set_type, + .name = "msm", + .irq_disable = msm_irq_disable, + .irq_ack = msm_irq_ack, + .irq_mask = msm_irq_mask, + .irq_unmask = msm_irq_unmask, + .irq_set_wake = msm_irq_set_wake, + .irq_set_type = msm_irq_set_type, }; void __init msm_init_irq(void) @@ -353,11 +601,123 @@ void __init msm_init_irq(void) /* don't use vic */ writel(0, VIC_CONFIG); - /* enable interrupt controller */ - writel(3, VIC_INT_MASTEREN); for (n = 0; n < NR_MSM_IRQS; n++) { irq_set_chip_and_handler(n, &msm_irq_chip, handle_level_irq); set_irq_flags(n, IRQF_VALID); } + + /* enable interrupt controller */ + writel(3, VIC_INT_MASTEREN); + mb(); } + +static inline void msm_vic_handle_irq(void __iomem *base_addr, struct pt_regs + *regs) +{ + u32 irqnr; + + do { + /* 0xD0 has irq# or old irq# if the irq has been handled + * 0xD4 has irq# or -1 if none pending *but* if you just + * read 0xD4 you never get the first irq for some reason + */ + irqnr = readl_relaxed(base_addr + 0xD0); + irqnr = readl_relaxed(base_addr + 0xD4); + if (irqnr == -1) + break; + handle_IRQ(irqnr, regs); + } while (1); +} + +/* enable imprecise aborts */ +#define local_cpsie_enable() __asm__ __volatile__("cpsie a @ enable") + +asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) +{ + local_cpsie_enable(); + msm_vic_handle_irq((void __iomem *)MSM_VIC_BASE, regs); +} + +#if defined(CONFIG_MSM_FIQ_SUPPORT) +void msm_trigger_irq(int irq) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_SOFTINT0, irq); + uint32_t mask = 1UL << (irq & 31); + writel(mask, reg); + mb(); +} + +void msm_fiq_enable(int irq) +{ + struct irq_data d = { .irq = irq }; + unsigned long flags; + local_irq_save(flags); + msm_irq_unmask(&d); + local_irq_restore(flags); +} + +void msm_fiq_disable(int irq) +{ + struct irq_data d = { .irq = irq }; + unsigned long flags; + local_irq_save(flags); + msm_irq_mask(&d); + local_irq_restore(flags); +} + +void msm_fiq_select(int irq) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_SELECT0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select |= mask; + writel(msm_irq_shadow_reg[index].int_select, reg); + mb(); + local_irq_restore(flags); +} + +void msm_fiq_unselect(int irq) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_SELECT0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select &= (!mask); + writel(msm_irq_shadow_reg[index].int_select, reg); + mb(); + local_irq_restore(flags); +} +/* set_fiq_handler originally from arch/arm/kernel/fiq.c */ +static void set_fiq_handler(void *start, unsigned int length) +{ + memcpy((void *)0xffff001c, start, length); + flush_icache_range(0xffff001c, 0xffff001c + length); + if (!vectors_high()) + flush_icache_range(0x1c, 0x1c + length); +} + +static void (*fiq_func)(void *data, void *regs); +static unsigned long long fiq_stack[256]; + +int msm_fiq_set_handler(void (*func)(void *data, void *regs), void *data) +{ + unsigned long flags; + int ret = -ENOMEM; + + local_irq_save(flags); + if (fiq_func == 0) { + fiq_func = func; + fiq_glue_setup(func, data, fiq_stack + 255); + set_fiq_handler(&fiq_glue, (&fiq_glue_end - &fiq_glue)); + ret = 0; + } + local_irq_restore(flags); + return ret; +} +#endif diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index ea514be390c..280160aa018 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -22,9 +22,25 @@ #include #include +#include + #include #include +#include + +#include "sirc.h" +#include "smd_private.h" + +enum { + IRQ_DEBUG_SLEEP_INT_TRIGGER = 1U << 0, + IRQ_DEBUG_SLEEP_INT = 1U << 1, + IRQ_DEBUG_SLEEP_ABORT = 1U << 2, + IRQ_DEBUG_SLEEP = 1U << 3, + IRQ_DEBUG_SLEEP_REQUEST = 1U << 4, +}; +static int msm_irq_debug_mask; +module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define VIC_REG(off) (MSM_VIC_BASE + (off)) @@ -41,9 +57,16 @@ #define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ #define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ #define VIC_NO_PEND_VAL VIC_REG(0x0060) + +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) +#define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064) +#define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */ +#define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */ +#else #define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */ -#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ #define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ +#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ +#endif #define VIC_IRQ_STATUS0 VIC_REG(0x0080) #define VIC_IRQ_STATUS1 VIC_REG(0x0084) #define VIC_FIQ_STATUS0 VIC_REG(0x0090) @@ -57,65 +80,370 @@ #define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ #define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ #define VIC_IRQ_VEC_WR VIC_REG(0x00D8) + +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) +#define VIC_FIQ_VEC_RD VIC_REG(0x00DC) +#define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0) +#define VIC_FIQ_VEC_WR VIC_REG(0x00E4) +#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E8) +#define VIC_IRQ_IN_STACK VIC_REG(0x00EC) +#define VIC_FIQ_IN_SERVICE VIC_REG(0x00F0) +#define VIC_FIQ_IN_STACK VIC_REG(0x00F4) +#define VIC_TEST_BUS_SEL VIC_REG(0x00F8) +#define VIC_IRQ_CTRL_CONFIG VIC_REG(0x00FC) +#else #define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0) #define VIC_IRQ_IN_STACK VIC_REG(0x00E4) #define VIC_TEST_BUS_SEL VIC_REG(0x00E8) +#endif #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) -static void msm_irq_ack(struct irq_data *d) +static uint32_t msm_irq_smsm_wake_enable[2]; +static struct { + uint32_t int_en[2]; + uint32_t int_type; + uint32_t int_polarity; + uint32_t int_select; +} msm_irq_shadow_reg[2]; +static uint32_t msm_irq_idle_disable[2]; + +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) +#define INT_INFO_SMSM_ID SMEM_SMSM_INT_INFO +struct smsm_interrupt_info *smsm_int_info; +#else +#define INT_INFO_SMSM_ID SMEM_APPS_DEM_SLAVE_DATA +struct msm_dem_slave_data *smsm_int_info; +#endif + + +#define SMSM_FAKE_IRQ (0xff) +static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { + [INT_MDDI_EXT] = 1, + [INT_MDDI_PRI] = 2, + [INT_MDDI_CLIENT] = 3, + [INT_USB_OTG] = 4, + + /* [INT_PWB_I2C] = 5 -- not usable */ + [INT_SDC1_0] = 6, + [INT_SDC1_1] = 7, + [INT_SDC2_0] = 8, + + [INT_SDC2_1] = 9, + [INT_ADSP_A9_A11] = 10, + [INT_UART1] = 11, + [INT_UART2] = 12, + + [INT_UART3] = 13, + [INT_UART1_RX] = 14, + [INT_UART2_RX] = 15, + [INT_UART3_RX] = 16, + + [INT_UART1DM_IRQ] = 17, + [INT_UART1DM_RX] = 18, + [INT_KEYSENSE] = 19, + [INT_AD_HSSD] = 20, + + [INT_NAND_WR_ER_DONE] = 21, + [INT_NAND_OP_DONE] = 22, + [INT_TCHSCRN1] = 23, + [INT_TCHSCRN2] = 24, + + [INT_TCHSCRN_SSBI] = 25, + [INT_USB_HS] = 26, + [INT_UART2DM_RX] = 27, + [INT_UART2DM_IRQ] = 28, + + [INT_SDC4_1] = 29, + [INT_SDC4_0] = 30, + [INT_SDC3_1] = 31, + [INT_SDC3_0] = 32, + + /* fake wakeup interrupts */ + [INT_GPIO_GROUP1] = SMSM_FAKE_IRQ, + [INT_GPIO_GROUP2] = SMSM_FAKE_IRQ, + [INT_A9_M2A_0] = SMSM_FAKE_IRQ, + [INT_A9_M2A_1] = SMSM_FAKE_IRQ, + [INT_A9_M2A_5] = SMSM_FAKE_IRQ, + [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_ADSP_A11] = SMSM_FAKE_IRQ, +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) + [INT_SIRC_0] = SMSM_FAKE_IRQ, + [INT_SIRC_1] = SMSM_FAKE_IRQ, +#endif +}; + +static void msm_irq_ack(unsigned int irq) { - void __iomem *reg = VIC_INT_CLEAR0 + ((d->irq & 32) ? 4 : 0); - writel(1 << (d->irq & 31), reg); + void __iomem *reg = VIC_INT_CLEAR0 + ((irq & 32) ? 4 : 0); + irq = 1 << (irq & 31); + writel(irq, reg); } -static void msm_irq_mask(struct irq_data *d) +static void msm_irq_mask(unsigned int irq) { - void __iomem *reg = VIC_INT_ENCLEAR0 + ((d->irq & 32) ? 4 : 0); - writel(1 << (d->irq & 31), reg); + void __iomem *reg = VIC_INT_ENCLEAR0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + msm_irq_shadow_reg[index].int_en[0] &= ~mask; + writel(mask, reg); + if (smsm_irq == 0) + msm_irq_idle_disable[index] &= ~mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] &= ~mask; + } } -static void msm_irq_unmask(struct irq_data *d) +static void msm_irq_unmask(unsigned int irq) { - void __iomem *reg = VIC_INT_ENSET0 + ((d->irq & 32) ? 4 : 0); - writel(1 << (d->irq & 31), reg); + void __iomem *reg = VIC_INT_ENSET0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + msm_irq_shadow_reg[index].int_en[0] |= mask; + writel(mask, reg); + + if (smsm_irq == 0) + msm_irq_idle_disable[index] |= mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] |= mask; + } } -static int msm_irq_set_wake(struct irq_data *d, unsigned int on) +static int msm_irq_set_wake(unsigned int irq, unsigned int on) { - return -EINVAL; + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + if (smsm_irq == 0) { + printk(KERN_ERR "msm_irq_set_wake: bad wakeup irq %d\n", irq); + return -EINVAL; + } + if (on) + msm_irq_shadow_reg[index].int_en[1] |= mask; + else + msm_irq_shadow_reg[index].int_en[1] &= ~mask; + + if (smsm_irq == SMSM_FAKE_IRQ) + return 0; + + mask = 1UL << (smsm_irq - 1); + if (on) + msm_irq_smsm_wake_enable[1] |= mask; + else + msm_irq_smsm_wake_enable[1] &= ~mask; + return 0; } -static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) +static int msm_irq_set_type(unsigned int irq, unsigned int flow_type) { - void __iomem *treg = VIC_INT_TYPE0 + ((d->irq & 32) ? 4 : 0); - void __iomem *preg = VIC_INT_POLARITY0 + ((d->irq & 32) ? 4 : 0); - int b = 1 << (d->irq & 31); + void __iomem *treg = VIC_INT_TYPE0 + ((irq & 32) ? 4 : 0); + void __iomem *preg = VIC_INT_POLARITY0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + int b = 1 << (irq & 31); + uint32_t polarity; + uint32_t type; + polarity = msm_irq_shadow_reg[index].int_polarity; if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) - writel(readl(preg) | b, preg); + polarity |= b; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) - writel(readl(preg) & (~b), preg); + polarity &= ~b; + writel(polarity, preg); + msm_irq_shadow_reg[index].int_polarity = polarity; + type = msm_irq_shadow_reg[index].int_type; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - writel(readl(treg) | b, treg); + type |= b; __irq_set_handler_locked(d->irq, handle_edge_irq); } if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { - writel(readl(treg) & (~b), treg); + type &= ~b; __irq_set_handler_locked(d->irq, handle_level_irq); } + writel(type, treg); + msm_irq_shadow_reg[index].int_type = type; + return 0; +} + +int msm_irq_pending(void) +{ + return readl(VIC_IRQ_STATUS0) || readl(VIC_IRQ_STATUS1); +} + +int msm_irq_idle_sleep_allowed(void) +{ + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) + printk(KERN_INFO "msm_irq_idle_sleep_allowed: disable %x %x\n", + msm_irq_idle_disable[0], msm_irq_idle_disable[1]); + return !(msm_irq_idle_disable[0] || msm_irq_idle_disable[1] || + !smsm_int_info); +} + +/* If arm9_wake is set: pass control to the other core. + * If from_idle is not set: disable non-wakeup interrupts. + */ +void msm_irq_enter_sleep1(bool arm9_wake, int from_idle) +{ + if (!arm9_wake || !smsm_int_info) + return; + smsm_int_info->interrupt_mask = msm_irq_smsm_wake_enable[!from_idle]; + smsm_int_info->pending_interrupts = 0; +} + +int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) +{ + int limit = 10; + uint32_t pending0, pending1; + + if (from_idle && !arm9_wake) + return 0; + + /* edge triggered interrupt may get lost if this mode is used */ + WARN_ON_ONCE(!arm9_wake && !from_idle); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_enter_sleep change irq, pend %x %x\n", + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + pending0 = readl(VIC_IRQ_STATUS0); + pending1 = readl(VIC_IRQ_STATUS1); + pending0 &= msm_irq_shadow_reg[0].int_en[!from_idle]; + /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ + pending0 &= ~(1U << INT_A9_M2A_5); + pending1 &= msm_irq_shadow_reg[1].int_en[!from_idle]; + if (pending0 || pending1) { + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) + printk(KERN_INFO "msm_irq_enter_sleep2 abort %x %x\n", + pending0, pending1); + return -EAGAIN; + } + + writel(0, VIC_INT_EN0); + writel(0, VIC_INT_EN1); + + while (limit-- > 0) { + int pend_irq; + int irq = readl(VIC_IRQ_VEC_RD); + if (irq == -1) + break; + pend_irq = readl(VIC_IRQ_VEC_PEND_RD); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + printk(KERN_INFO "msm_irq_enter_sleep cleared " + "int %d (%d)\n", irq, pend_irq); + } + + if (arm9_wake) { + msm_irq_set_type(INT_A9_M2A_6, IRQF_TRIGGER_RISING); + msm_irq_ack(INT_A9_M2A_6); + writel(1U << INT_A9_M2A_6, VIC_INT_ENSET0); + } else { + writel(msm_irq_shadow_reg[0].int_en[1], VIC_INT_ENSET0); + writel(msm_irq_shadow_reg[1].int_en[1], VIC_INT_ENSET1); + } return 0; } +void msm_irq_exit_sleep1(void) +{ + int i; + + msm_irq_ack(INT_A9_M2A_6); + msm_irq_ack(INT_PWB_I2C); + for (i = 0; i < 2; i++) { + writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE0 + i * 4); + writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY0 + i * 4); + writel(msm_irq_shadow_reg[i].int_en[0], VIC_INT_EN0 + i * 4); + writel(msm_irq_shadow_reg[i].int_select, VIC_INT_SELECT0 + i * 4); + } + writel(3, VIC_INT_MASTEREN); + if (!smsm_int_info) { + printk(KERN_ERR "msm_irq_exit_sleep \n"); + return; + } + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_exit_sleep1 %x %x %x now %x %x\n", + smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); +} + +void msm_irq_exit_sleep2(void) +{ + int i; + uint32_t pending; + + if (!smsm_int_info) { + printk(KERN_ERR "msm_irq_exit_sleep \n"); + return; + } + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_exit_sleep2 %x %x %x now %x %x\n", + smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + pending = smsm_int_info->pending_interrupts; + for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { + unsigned reg_offset = (i & 32) ? 4 : 0; + uint32_t reg_mask = 1UL << (i & 31); + int smsm_irq = msm_irq_to_smsm[i]; + uint32_t smsm_mask; + if (smsm_irq == 0) + continue; + smsm_mask = 1U << (smsm_irq - 1); + if (!(pending & smsm_mask)) + continue; + pending &= ~smsm_mask; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + printk(KERN_INFO "msm_irq_exit_sleep2: irq %d " + "still pending %x now %x %x\n", i, pending, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); +#if 0 /* debug intetrrupt trigger */ + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + writel(reg_mask, VIC_INT_CLEAR0 + reg_offset); +#endif + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + continue; + writel(reg_mask, VIC_SOFTINT0 + reg_offset); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) + printk(KERN_INFO "msm_irq_exit_sleep2: irq %d need " + "trigger, now %x %x\n", i, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + } +} + +void msm_irq_exit_sleep3(void) +{ + if (!smsm_int_info) { + printk(KERN_ERR "msm_irq_exit_sleep \n"); + return; + } + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_exit_sleep3 %x %x %x now %x %x " + "state %x\n", smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS0), + readl(VIC_IRQ_STATUS1), + smsm_get_state(SMSM_STATE_MODEM)); +} + static struct irq_chip msm_irq_chip = { - .name = "msm", - .irq_ack = msm_irq_ack, - .irq_mask = msm_irq_mask, - .irq_unmask = msm_irq_unmask, - .irq_set_wake = msm_irq_set_wake, - .irq_set_type = msm_irq_set_type, + .name = "msm", + .disable = msm_irq_mask, + .ack = msm_irq_ack, + .mask = msm_irq_mask, + .unmask = msm_irq_unmask, + .set_wake = msm_irq_set_wake, + .set_type = msm_irq_set_type, }; void __init msm_init_irq(void) @@ -142,10 +470,138 @@ void __init msm_init_irq(void) writel(0, VIC_CONFIG); /* enable interrupt controller */ - writel(1, VIC_INT_MASTEREN); + writel(3, VIC_INT_MASTEREN); for (n = 0; n < NR_MSM_IRQS; n++) { irq_set_chip_and_handler(n, &msm_irq_chip, handle_level_irq); set_irq_flags(n, IRQF_VALID); } + + msm_init_sirc(); } + +static int __init msm_init_irq_late(void) +{ + smsm_int_info = smem_alloc(INT_INFO_SMSM_ID, sizeof(*smsm_int_info)); + if (!smsm_int_info) + pr_err("set_wakeup_mask NO INT_INFO (%d)\n", INT_INFO_SMSM_ID); + return 0; +} +late_initcall(msm_init_irq_late); + +#if defined(CONFIG_MSM_FIQ_SUPPORT) +void msm_trigger_irq(int irq) +{ + void __iomem *reg = VIC_SOFTINT0 + ((irq & 32) ? 4 : 0); + uint32_t mask = 1UL << (irq & 31); + writel(mask, reg); +} + +void msm_fiq_enable(int irq) +{ + unsigned long flags; + local_irq_save(flags); + irq_desc[irq].chip->unmask(irq); + local_irq_restore(flags); +} + +void msm_fiq_disable(int irq) +{ + unsigned long flags; + local_irq_save(flags); + irq_desc[irq].chip->mask(irq); + local_irq_restore(flags); +} + +static void _msm_fiq_select(int irq) +{ + void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select |= mask; + writel(msm_irq_shadow_reg[index].int_select, reg); + local_irq_restore(flags); +} + +static void _msm_fiq_unselect(int irq) +{ + void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select &= (!mask); + writel(msm_irq_shadow_reg[index].int_select, reg); + local_irq_restore(flags); +} + +void msm_fiq_select(int irq) +{ + if (irq < FIRST_SIRC_IRQ) + _msm_fiq_select(irq); + else if (irq < FIRST_GPIO_IRQ) + sirc_fiq_select(irq, true); + else + pr_err("unsupported fiq %d", irq); +} + +void msm_fiq_unselect(int irq) +{ + if (irq < FIRST_SIRC_IRQ) + _msm_fiq_unselect(irq); + else if (irq < FIRST_GPIO_IRQ) + sirc_fiq_select(irq, false); + else + pr_err("unsupported fiq %d", irq); +} + +/* set_fiq_handler originally from arch/arm/kernel/fiq.c */ +static void set_fiq_handler(void *start, unsigned int length) +{ + memcpy((void *)0xffff001c, start, length); + flush_icache_range(0xffff001c, 0xffff001c + length); + if (!vectors_high()) + flush_icache_range(0x1c, 0x1c + length); +} + +extern unsigned char fiq_glue, fiq_glue_end; + +static void (*fiq_func)(void *data, void *regs, void *svc_sp); +static void *fiq_data; +static void *fiq_stack; + +void fiq_glue_setup(void *func, void *data, void *sp); + +int msm_fiq_set_handler(void (*func)(void *data, void *regs, void *svc_sp), + void *data) +{ + unsigned long flags; + int ret = -ENOMEM; + + if (!fiq_stack) + fiq_stack = kzalloc(THREAD_SIZE, GFP_KERNEL); + if (!fiq_stack) + return -ENOMEM; + + local_irq_save(flags); + if (fiq_func == 0) { + fiq_func = func; + fiq_data = data; + fiq_glue_setup(func, data, fiq_stack + THREAD_START_SP); + set_fiq_handler(&fiq_glue, (&fiq_glue_end - &fiq_glue)); + ret = 0; + } + local_irq_restore(flags); + return ret; +} + +void msm_fiq_exit_sleep(void) +{ + if (fiq_stack) + fiq_glue_setup(fiq_func, fiq_data, fiq_stack + THREAD_START_SP); +} +#endif diff --git a/arch/arm/mach-msm/irq.h b/arch/arm/mach-msm/irq.h new file mode 100644 index 00000000000..8b0fbc04794 --- /dev/null +++ b/arch/arm/mach-msm/irq.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_IRQ_H_ +#define _ARCH_ARM_MACH_MSM_IRQ_H_ + +int msm_irq_idle_sleep_allowed(void); +unsigned int msm_irq_pending(void); +void msm_irq_enter_sleep1(bool arm9_wake, int from_idle, uint32_t *irq_mask); +int msm_irq_enter_sleep2(bool arm9_wake, int from_idle); +void msm_irq_exit_sleep1 + (uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs); +void msm_irq_exit_sleep2 + (uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending); +void msm_irq_exit_sleep3 + (uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs); + +#endif diff --git a/arch/arm/mach-msm/jtag.c b/arch/arm/mach-msm/jtag.c new file mode 100644 index 00000000000..8dae9c63a52 --- /dev/null +++ b/arch/arm/mach-msm/jtag.c @@ -0,0 +1,1171 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qdss-priv.h" +#include "cp14.h" + +/* no of dbg regs + 1 (for storing the reg count) */ +#define MAX_DBG_REGS (90) +#define MAX_DBG_STATE_SIZE (MAX_DBG_REGS * num_possible_cpus()) +/* no of etm regs + 1 (for storing the reg count) */ +#define MAX_ETM_REGS (78) +#define MAX_ETM_STATE_SIZE (MAX_ETM_REGS * num_possible_cpus()) + +#define OSLOCK_MAGIC (0xC5ACCE55) +#define DBGDSCR_MASK (0x6C30FC3C) +#define CPMR_ETMCLKEN (0x8) +#define TZ_DBG_ETM_FEAT_ID (0x8) +#define TZ_DBG_ETM_VER (0x400000) + + +uint32_t msm_jtag_save_cntr[NR_CPUS]; +uint32_t msm_jtag_restore_cntr[NR_CPUS]; + +struct dbg_ctx { + uint8_t arch; + bool save_restore_enabled; + uint8_t nr_wp; + uint8_t nr_bp; + uint8_t nr_ctx_cmp; + uint32_t *state; +}; +static struct dbg_ctx dbg; + +struct etm_ctx { + uint8_t arch; + bool save_restore_enabled; + uint8_t nr_addr_cmp; + uint8_t nr_cntr; + uint8_t nr_ext_inp; + uint8_t nr_ext_out; + uint8_t nr_ctxid_cmp; + uint32_t *state; +}; +static struct etm_ctx etm; + +static int dbg_read_bxr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + state[i++] = dbg_read(DBGBVR0); + state[i++] = dbg_read(DBGBCR0); + break; + case 1: + state[i++] = dbg_read(DBGBVR1); + state[i++] = dbg_read(DBGBCR1); + break; + case 2: + state[i++] = dbg_read(DBGBVR2); + state[i++] = dbg_read(DBGBCR2); + break; + case 3: + state[i++] = dbg_read(DBGBVR3); + state[i++] = dbg_read(DBGBCR3); + break; + case 4: + state[i++] = dbg_read(DBGBVR4); + state[i++] = dbg_read(DBGBCR4); + break; + case 5: + state[i++] = dbg_read(DBGBVR5); + state[i++] = dbg_read(DBGBCR5); + break; + case 6: + state[i++] = dbg_read(DBGBVR6); + state[i++] = dbg_read(DBGBCR6); + break; + case 7: + state[i++] = dbg_read(DBGBVR7); + state[i++] = dbg_read(DBGBCR7); + break; + case 8: + state[i++] = dbg_read(DBGBVR8); + state[i++] = dbg_read(DBGBCR8); + break; + case 9: + state[i++] = dbg_read(DBGBVR9); + state[i++] = dbg_read(DBGBCR9); + break; + case 10: + state[i++] = dbg_read(DBGBVR10); + state[i++] = dbg_read(DBGBCR10); + break; + case 11: + state[i++] = dbg_read(DBGBVR11); + state[i++] = dbg_read(DBGBCR11); + break; + case 12: + state[i++] = dbg_read(DBGBVR12); + state[i++] = dbg_read(DBGBCR12); + break; + case 13: + state[i++] = dbg_read(DBGBVR13); + state[i++] = dbg_read(DBGBCR13); + break; + case 14: + state[i++] = dbg_read(DBGBVR14); + state[i++] = dbg_read(DBGBCR14); + break; + case 15: + state[i++] = dbg_read(DBGBVR15); + state[i++] = dbg_read(DBGBCR15); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int dbg_write_bxr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + dbg_write(state[i++], DBGBVR0); + dbg_write(state[i++], DBGBCR0); + break; + case 1: + dbg_write(state[i++], DBGBVR1); + dbg_write(state[i++], DBGBCR1); + break; + case 2: + dbg_write(state[i++], DBGBVR2); + dbg_write(state[i++], DBGBCR2); + break; + case 3: + dbg_write(state[i++], DBGBVR3); + dbg_write(state[i++], DBGBCR3); + break; + case 4: + dbg_write(state[i++], DBGBVR4); + dbg_write(state[i++], DBGBCR4); + break; + case 5: + dbg_write(state[i++], DBGBVR5); + dbg_write(state[i++], DBGBCR5); + break; + case 6: + dbg_write(state[i++], DBGBVR6); + dbg_write(state[i++], DBGBCR6); + break; + case 7: + dbg_write(state[i++], DBGBVR7); + dbg_write(state[i++], DBGBCR7); + break; + case 8: + dbg_write(state[i++], DBGBVR8); + dbg_write(state[i++], DBGBCR8); + break; + case 9: + dbg_write(state[i++], DBGBVR9); + dbg_write(state[i++], DBGBCR9); + break; + case 10: + dbg_write(state[i++], DBGBVR10); + dbg_write(state[i++], DBGBCR10); + break; + case 11: + dbg_write(state[i++], DBGBVR11); + dbg_write(state[i++], DBGBCR11); + break; + case 12: + dbg_write(state[i++], DBGBVR12); + dbg_write(state[i++], DBGBCR12); + break; + case 13: + dbg_write(state[i++], DBGBVR13); + dbg_write(state[i++], DBGBCR13); + break; + case 14: + dbg_write(state[i++], DBGBVR14); + dbg_write(state[i++], DBGBCR14); + break; + case 15: + dbg_write(state[i++], DBGBVR15); + dbg_write(state[i++], DBGBCR15); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int dbg_read_wxr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + state[i++] = dbg_read(DBGWVR0); + state[i++] = dbg_read(DBGWCR0); + break; + case 1: + state[i++] = dbg_read(DBGWVR1); + state[i++] = dbg_read(DBGWCR1); + break; + case 2: + state[i++] = dbg_read(DBGWVR2); + state[i++] = dbg_read(DBGWCR2); + break; + case 3: + state[i++] = dbg_read(DBGWVR3); + state[i++] = dbg_read(DBGWCR3); + break; + case 4: + state[i++] = dbg_read(DBGWVR4); + state[i++] = dbg_read(DBGWCR4); + break; + case 5: + state[i++] = dbg_read(DBGWVR5); + state[i++] = dbg_read(DBGWCR5); + break; + case 6: + state[i++] = dbg_read(DBGWVR6); + state[i++] = dbg_read(DBGWCR6); + break; + case 7: + state[i++] = dbg_read(DBGWVR7); + state[i++] = dbg_read(DBGWCR7); + break; + case 8: + state[i++] = dbg_read(DBGWVR8); + state[i++] = dbg_read(DBGWCR8); + break; + case 9: + state[i++] = dbg_read(DBGWVR9); + state[i++] = dbg_read(DBGWCR9); + break; + case 10: + state[i++] = dbg_read(DBGWVR10); + state[i++] = dbg_read(DBGWCR10); + break; + case 11: + state[i++] = dbg_read(DBGWVR11); + state[i++] = dbg_read(DBGWCR11); + break; + case 12: + state[i++] = dbg_read(DBGWVR12); + state[i++] = dbg_read(DBGWCR12); + break; + case 13: + state[i++] = dbg_read(DBGWVR13); + state[i++] = dbg_read(DBGWCR13); + break; + case 14: + state[i++] = dbg_read(DBGWVR14); + state[i++] = dbg_read(DBGWCR14); + break; + case 15: + state[i++] = dbg_read(DBGWVR15); + state[i++] = dbg_read(DBGWCR15); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int dbg_write_wxr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + dbg_write(state[i++], DBGWVR0); + dbg_write(state[i++], DBGWCR0); + break; + case 1: + dbg_write(state[i++], DBGWVR1); + dbg_write(state[i++], DBGWCR1); + break; + case 2: + dbg_write(state[i++], DBGWVR2); + dbg_write(state[i++], DBGWCR2); + break; + case 3: + dbg_write(state[i++], DBGWVR3); + dbg_write(state[i++], DBGWCR3); + break; + case 4: + dbg_write(state[i++], DBGWVR4); + dbg_write(state[i++], DBGWCR4); + break; + case 5: + dbg_write(state[i++], DBGWVR5); + dbg_write(state[i++], DBGWCR5); + break; + case 6: + dbg_write(state[i++], DBGWVR6); + dbg_write(state[i++], DBGWCR6); + break; + case 7: + dbg_write(state[i++], DBGWVR7); + dbg_write(state[i++], DBGWCR7); + break; + case 8: + dbg_write(state[i++], DBGWVR8); + dbg_write(state[i++], DBGWCR8); + break; + case 9: + dbg_write(state[i++], DBGWVR9); + dbg_write(state[i++], DBGWCR9); + break; + case 10: + dbg_write(state[i++], DBGWVR10); + dbg_write(state[i++], DBGWCR10); + break; + case 11: + dbg_write(state[i++], DBGWVR11); + dbg_write(state[i++], DBGWCR11); + break; + case 12: + dbg_write(state[i++], DBGWVR12); + dbg_write(state[i++], DBGWCR12); + break; + case 13: + dbg_write(state[i++], DBGWVR13); + dbg_write(state[i++], DBGWCR13); + break; + case 14: + dbg_write(state[i++], DBGWVR14); + dbg_write(state[i++], DBGWCR14); + break; + case 15: + dbg_write(state[i++], DBGWVR15); + dbg_write(state[i++], DBGWCR15); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static inline bool dbg_arch_supported(uint8_t arch) +{ + switch (arch) { + case ARM_DEBUG_ARCH_V7_1: + case ARM_DEBUG_ARCH_V7: + case ARM_DEBUG_ARCH_V7B: + break; + default: + return false; + } + return true; +} + + +static inline void dbg_save_state(int cpu) +{ + int i, j, cnt; + + i = cpu * MAX_DBG_REGS; + + switch (dbg.arch) { + case ARM_DEBUG_ARCH_V7_1: + /* Set OS lock to inform the debugger that the OS is in the + * process of saving debug registers. It prevents accidental + * modification of the debug regs by the external debugger. + */ + dbg_write(OSLOCK_MAGIC, DBGOSLAR); + isb(); + + /* We skip saving DBGBXVRn since not supported on Krait */ + + dbg.state[i++] = dbg_read(DBGWFAR); + for (j = 0; j < dbg.nr_bp; j++) + i = dbg_read_bxr(dbg.state, i, j); + for (j = 0; j < dbg.nr_wp; j++) + i = dbg_read_wxr(dbg.state, i, j); + dbg.state[i++] = dbg_read(DBGVCR); + dbg.state[i++] = dbg_read(DBGCLAIMCLR); + dbg.state[i++] = dbg_read(DBGDTRTXext); + dbg.state[i++] = dbg_read(DBGDTRRXext); + dbg.state[i++] = dbg_read(DBGDSCRext); + + /* Set the OS double lock */ + isb(); + dbg_write(0x1, DBGOSDLR); + isb(); + break; + case ARM_DEBUG_ARCH_V7B: + case ARM_DEBUG_ARCH_V7: + /* Set OS lock to inform the debugger that the OS is in the + * process of saving dbg registers. It prevents accidental + * modification of the dbg regs by the external debugger + * and resets the internal counter. + */ + dbg_write(OSLOCK_MAGIC, DBGOSLAR); + isb(); + + cnt = dbg_read(DBGOSSRR); /* save count for restore */ + /* MAX_DBG_REGS = no of dbg regs + 1 (for storing the reg count) + * check for state overflow, if not enough space, don't save + */ + if (cnt >= MAX_DBG_REGS) + cnt = 0; + dbg.state[i++] = cnt; + for (j = 0; j < cnt; j++) + dbg.state[i++] = dbg_read(DBGOSSRR); + break; + default: + pr_err_ratelimited("unsupported dbg arch %d in %s\n", dbg.arch, + __func__); + } +} + +static inline void dbg_restore_state(int cpu) +{ + int i, j, cnt; + + i = cpu * MAX_DBG_REGS; + + switch (dbg.arch) { + case ARM_DEBUG_ARCH_V7_1: + /* Clear the OS double lock */ + isb(); + dbg_write(0x0, DBGOSDLR); + isb(); + + /* Set OS lock. Lock will already be set after power collapse + * but this write is included to ensure it is set. + */ + dbg_write(OSLOCK_MAGIC, DBGOSLAR); + isb(); + + /* We skip restoring DBGBXVRn since not supported on Krait */ + + dbg_write(dbg.state[i++], DBGWFAR); + for (j = 0; j < dbg.nr_bp; j++) + i = dbg_write_bxr(dbg.state, i, j); + for (j = 0; j < dbg.nr_wp; j++) + i = dbg_write_wxr(dbg.state, i, j); + dbg_write(dbg.state[i++], DBGVCR); + dbg_write(dbg.state[i++], DBGCLAIMSET); + dbg_write(dbg.state[i++], DBGDTRTXext); + dbg_write(dbg.state[i++], DBGDTRRXext); + dbg_write(dbg.state[i++] & DBGDSCR_MASK, DBGDSCRext); + + isb(); + dbg_write(0x0, DBGOSLAR); + isb(); + break; + case ARM_DEBUG_ARCH_V7B: + case ARM_DEBUG_ARCH_V7: + /* Clear sticky bit */ + dbg_read(DBGPRSR); + isb(); + + /* Set OS lock. Lock will already be set after power collapse + * but this write is required to reset the internal counter used + * for DBG state restore. + */ + dbg_write(OSLOCK_MAGIC, DBGOSLAR); + isb(); + + dbg_read(DBGOSSRR); /* dummy read of OSSRR */ + cnt = dbg.state[i++]; + for (j = 0; j < cnt; j++) { + /* DBGDSCR special case + * DBGDSCR = DBGDSCR & DBGDSCR_MASK + */ + if (j == 20) + dbg_write(dbg.state[i++] & DBGDSCR_MASK, + DBGOSSRR); + else + dbg_write(dbg.state[i++], DBGOSSRR); + } + + /* Clear the OS lock */ + isb(); + dbg_write(0x0, DBGOSLAR); + isb(); + break; + default: + pr_err_ratelimited("unsupported dbg arch %d in %s\n", dbg.arch, + __func__); + } + +} + +static int etm_read_acxr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + state[i++] = etm_read(ETMACVR0); + state[i++] = etm_read(ETMACTR0); + break; + case 1: + state[i++] = etm_read(ETMACVR1); + state[i++] = etm_read(ETMACTR1); + break; + case 2: + state[i++] = etm_read(ETMACVR2); + state[i++] = etm_read(ETMACTR2); + break; + case 3: + state[i++] = etm_read(ETMACVR3); + state[i++] = etm_read(ETMACTR3); + break; + case 4: + state[i++] = etm_read(ETMACVR4); + state[i++] = etm_read(ETMACTR4); + break; + case 5: + state[i++] = etm_read(ETMACVR5); + state[i++] = etm_read(ETMACTR5); + break; + case 6: + state[i++] = etm_read(ETMACVR6); + state[i++] = etm_read(ETMACTR6); + break; + case 7: + state[i++] = etm_read(ETMACVR7); + state[i++] = etm_read(ETMACTR7); + break; + case 8: + state[i++] = etm_read(ETMACVR8); + state[i++] = etm_read(ETMACTR8); + break; + case 9: + state[i++] = etm_read(ETMACVR9); + state[i++] = etm_read(ETMACTR9); + break; + case 10: + state[i++] = etm_read(ETMACVR10); + state[i++] = etm_read(ETMACTR10); + break; + case 11: + state[i++] = etm_read(ETMACVR11); + state[i++] = etm_read(ETMACTR11); + break; + case 12: + state[i++] = etm_read(ETMACVR12); + state[i++] = etm_read(ETMACTR12); + break; + case 13: + state[i++] = etm_read(ETMACVR13); + state[i++] = etm_read(ETMACTR13); + break; + case 14: + state[i++] = etm_read(ETMACVR14); + state[i++] = etm_read(ETMACTR14); + break; + case 15: + state[i++] = etm_read(ETMACVR15); + state[i++] = etm_read(ETMACTR15); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_write_acxr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + etm_write(state[i++], ETMACVR0); + etm_write(state[i++], ETMACTR0); + break; + case 1: + etm_write(state[i++], ETMACVR1); + etm_write(state[i++], ETMACTR1); + break; + case 2: + etm_write(state[i++], ETMACVR2); + etm_write(state[i++], ETMACTR2); + break; + case 3: + etm_write(state[i++], ETMACVR3); + etm_write(state[i++], ETMACTR3); + break; + case 4: + etm_write(state[i++], ETMACVR4); + etm_write(state[i++], ETMACTR4); + break; + case 5: + etm_write(state[i++], ETMACVR5); + etm_write(state[i++], ETMACTR5); + break; + case 6: + etm_write(state[i++], ETMACVR6); + etm_write(state[i++], ETMACTR6); + break; + case 7: + etm_write(state[i++], ETMACVR7); + etm_write(state[i++], ETMACTR7); + break; + case 8: + etm_write(state[i++], ETMACVR8); + etm_write(state[i++], ETMACTR8); + break; + case 9: + etm_write(state[i++], ETMACVR9); + etm_write(state[i++], ETMACTR9); + break; + case 10: + etm_write(state[i++], ETMACVR10); + etm_write(state[i++], ETMACTR10); + break; + case 11: + etm_write(state[i++], ETMACVR11); + etm_write(state[i++], ETMACTR11); + break; + case 12: + etm_write(state[i++], ETMACVR12); + etm_write(state[i++], ETMACTR12); + break; + case 13: + etm_write(state[i++], ETMACVR13); + etm_write(state[i++], ETMACTR13); + break; + case 14: + etm_write(state[i++], ETMACVR14); + etm_write(state[i++], ETMACTR14); + break; + case 15: + etm_write(state[i++], ETMACVR15); + etm_write(state[i++], ETMACTR15); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_read_cntx(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + state[i++] = etm_read(ETMCNTRLDVR0); + state[i++] = etm_read(ETMCNTENR0); + state[i++] = etm_read(ETMCNTRLDEVR0); + state[i++] = etm_read(ETMCNTVR0); + break; + case 1: + state[i++] = etm_read(ETMCNTRLDVR1); + state[i++] = etm_read(ETMCNTENR1); + state[i++] = etm_read(ETMCNTRLDEVR1); + state[i++] = etm_read(ETMCNTVR1); + break; + case 2: + state[i++] = etm_read(ETMCNTRLDVR2); + state[i++] = etm_read(ETMCNTENR2); + state[i++] = etm_read(ETMCNTRLDEVR2); + state[i++] = etm_read(ETMCNTVR2); + break; + case 3: + state[i++] = etm_read(ETMCNTRLDVR3); + state[i++] = etm_read(ETMCNTENR3); + state[i++] = etm_read(ETMCNTRLDEVR3); + state[i++] = etm_read(ETMCNTVR3); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_write_cntx(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + etm_write(state[i++], ETMCNTRLDVR0); + etm_write(state[i++], ETMCNTENR0); + etm_write(state[i++], ETMCNTRLDEVR0); + etm_write(state[i++], ETMCNTVR0); + break; + case 1: + etm_write(state[i++], ETMCNTRLDVR1); + etm_write(state[i++], ETMCNTENR1); + etm_write(state[i++], ETMCNTRLDEVR1); + etm_write(state[i++], ETMCNTVR1); + break; + case 2: + etm_write(state[i++], ETMCNTRLDVR2); + etm_write(state[i++], ETMCNTENR2); + etm_write(state[i++], ETMCNTRLDEVR2); + etm_write(state[i++], ETMCNTVR2); + break; + case 3: + etm_write(state[i++], ETMCNTRLDVR3); + etm_write(state[i++], ETMCNTENR3); + etm_write(state[i++], ETMCNTRLDEVR3); + etm_write(state[i++], ETMCNTVR3); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_read_extoutevr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + state[i++] = etm_read(ETMEXTOUTEVR0); + break; + case 1: + state[i++] = etm_read(ETMEXTOUTEVR1); + break; + case 2: + state[i++] = etm_read(ETMEXTOUTEVR2); + break; + case 3: + state[i++] = etm_read(ETMEXTOUTEVR3); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_write_extoutevr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + etm_write(state[i++], ETMEXTOUTEVR0); + break; + case 1: + etm_write(state[i++], ETMEXTOUTEVR1); + break; + case 2: + etm_write(state[i++], ETMEXTOUTEVR2); + break; + case 3: + etm_write(state[i++], ETMEXTOUTEVR3); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_read_cidcvr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + state[i++] = etm_read(ETMCIDCVR0); + break; + case 1: + state[i++] = etm_read(ETMCIDCVR1); + break; + case 2: + state[i++] = etm_read(ETMCIDCVR2); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static int etm_write_cidcvr(uint32_t *state, int i, int j) +{ + switch (j) { + case 0: + etm_write(state[i++], ETMCIDCVR0); + break; + case 1: + etm_write(state[i++], ETMCIDCVR1); + break; + case 2: + etm_write(state[i++], ETMCIDCVR2); + break; + default: + pr_err_ratelimited("idx %d out of bounds in %s\n", j, __func__); + } + return i; +} + +static inline void etm_clk_disable(void) +{ + uint32_t cpmr; + + isb(); + asm volatile("mrc p15, 7, %0, c15, c0, 5" : "=r" (cpmr)); + cpmr &= ~CPMR_ETMCLKEN; + asm volatile("mcr p15, 7, %0, c15, c0, 5" : : "r" (cpmr)); +} + +static inline void etm_clk_enable(void) +{ + uint32_t cpmr; + + asm volatile("mrc p15, 7, %0, c15, c0, 5" : "=r" (cpmr)); + cpmr |= CPMR_ETMCLKEN; + asm volatile("mcr p15, 7, %0, c15, c0, 5" : : "r" (cpmr)); + isb(); +} + +static inline bool etm_arch_supported(uint8_t arch) +{ + switch (arch) { + case ETM_ARCH_V3_3: + case PFT_ARCH_V1_1: + break; + default: + return false; + } + return true; +} + +static inline void etm_save_state(int cpu) +{ + int i, j, cnt; + + i = cpu * MAX_ETM_REGS; + + /* Vote for ETM power/clock enable */ + etm_clk_enable(); + + switch (etm.arch) { + case PFT_ARCH_V1_1: + /* Set OS lock to inform the debugger that the OS is in the + * process of saving etm registers. It prevents accidental + * modification of the etm regs by the external debugger. + * + * We don't poll for ETMSR[1] since it doesn't get set + */ + etm_write(OSLOCK_MAGIC, ETMOSLAR); + isb(); + + etm.state[i++] = etm_read(ETMCR); + etm.state[i++] = etm_read(ETMTRIGGER); + etm.state[i++] = etm_read(ETMSR); + etm.state[i++] = etm_read(ETMTSSCR); + etm.state[i++] = etm_read(ETMTEEVR); + etm.state[i++] = etm_read(ETMTECR1); + etm.state[i++] = etm_read(ETMFFLR); + for (j = 0; j < etm.nr_addr_cmp; j++) + i = etm_read_acxr(etm.state, i, j); + for (j = 0; j < etm.nr_cntr; j++) + i = etm_read_cntx(etm.state, i, j); + etm.state[i++] = etm_read(ETMSQ12EVR); + etm.state[i++] = etm_read(ETMSQ21EVR); + etm.state[i++] = etm_read(ETMSQ23EVR); + etm.state[i++] = etm_read(ETMSQ31EVR); + etm.state[i++] = etm_read(ETMSQ32EVR); + etm.state[i++] = etm_read(ETMSQ13EVR); + etm.state[i++] = etm_read(ETMSQR); + for (j = 0; j < etm.nr_ext_out; j++) + i = etm_read_extoutevr(etm.state, i, j); + for (j = 0; j < etm.nr_ctxid_cmp; j++) + i = etm_read_cidcvr(etm.state, i, j); + etm.state[i++] = etm_read(ETMCIDCMR); + etm.state[i++] = etm_read(ETMSYNCFR); + etm.state[i++] = etm_read(ETMEXTINSELR); + etm.state[i++] = etm_read(ETMTSEVR); + etm.state[i++] = etm_read(ETMAUXCR); + etm.state[i++] = etm_read(ETMTRACEIDR); + etm.state[i++] = etm_read(ETMVMIDCVR); + etm.state[i++] = etm_read(ETMCLAIMCLR); + break; + case ETM_ARCH_V3_3: + /* In ETMv3.3, it is possible for the coresight lock to be + * implemented for CP14 interface but we currently assume that + * it is not, so no need to unlock and lock coresight lock + * (ETMLAR). + * + * Also since save and restore is not conditional i.e. always + * occurs when enabled, there is no need to clear the sticky + * PDSR bit while saving. It will be cleared during boot up/init + * and then by the restore procedure. + */ + + /* Set OS lock to inform the debugger that the OS is in the + * process of saving etm registers. It prevents accidental + * modification of the etm regs by the external debugger + * and resets the internal counter. + */ + etm_write(OSLOCK_MAGIC, ETMOSLAR); + isb(); + + cnt = etm_read(ETMOSSRR); /* save count for restore */ + /* MAX_ETM_REGS = no of etm regs + 1 (for storing the reg count) + * check for state overflow, if not enough space, don't save + */ + if (cnt >= MAX_ETM_REGS) + cnt = 0; + etm.state[i++] = cnt; + for (j = 0; j < cnt; j++) + etm.state[i++] = etm_read(ETMOSSRR); + break; + default: + pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch, + __func__); + } + + /* Vote for ETM power/clock disable */ + etm_clk_disable(); +} + +static inline void etm_restore_state(int cpu) +{ + int i, j, cnt; + + i = cpu * MAX_ETM_REGS; + + /* Vote for ETM power/clock enable */ + etm_clk_enable(); + + switch (etm.arch) { + case PFT_ARCH_V1_1: + /* Set OS lock. Lock will already be set after power collapse + * but this write is included to ensure it is set. + * + * We don't poll for ETMSR[1] since it doesn't get set + */ + etm_write(OSLOCK_MAGIC, ETMOSLAR); + isb(); + + etm_write(etm.state[i++], ETMCR); + etm_write(etm.state[i++], ETMTRIGGER); + etm_write(etm.state[i++], ETMSR); + etm_write(etm.state[i++], ETMTSSCR); + etm_write(etm.state[i++], ETMTEEVR); + etm_write(etm.state[i++], ETMTECR1); + etm_write(etm.state[i++], ETMFFLR); + for (j = 0; j < etm.nr_addr_cmp; j++) + i = etm_write_acxr(etm.state, i, j); + for (j = 0; j < etm.nr_cntr; j++) + i = etm_write_cntx(etm.state, i, j); + etm_write(etm.state[i++], ETMSQ12EVR); + etm_write(etm.state[i++], ETMSQ21EVR); + etm_write(etm.state[i++], ETMSQ23EVR); + etm_write(etm.state[i++], ETMSQ31EVR); + etm_write(etm.state[i++], ETMSQ32EVR); + etm_write(etm.state[i++], ETMSQ13EVR); + etm_write(etm.state[i++], ETMSQR); + for (j = 0; j < etm.nr_ext_out; j++) + i = etm_write_extoutevr(etm.state, i, j); + for (j = 0; j < etm.nr_ctxid_cmp; j++) + i = etm_write_cidcvr(etm.state, i, j); + etm_write(etm.state[i++], ETMCIDCMR); + etm_write(etm.state[i++], ETMSYNCFR); + etm_write(etm.state[i++], ETMEXTINSELR); + etm_write(etm.state[i++], ETMTSEVR); + etm_write(etm.state[i++], ETMAUXCR); + etm_write(etm.state[i++], ETMTRACEIDR); + etm_write(etm.state[i++], ETMVMIDCVR); + etm_write(etm.state[i++], ETMCLAIMSET); + + /* Clear the OS lock */ + isb(); + etm_write(0x0, ETMOSLAR); + isb(); + break; + case ETM_ARCH_V3_3: + /* In ETMv3.3, it is possible for the coresight lock to be + * implemented for CP14 interface but we currently assume that + * it is not, so no need to unlock and lock coresight lock + * (ETMLAR). + */ + + /* Clear sticky bit */ + etm_read(ETMPDSR); + isb(); + + /* Set OS lock. Lock will already be set after power collapse + * but this write is required to reset the internal counter used + * for ETM state restore. + */ + etm_write(OSLOCK_MAGIC, ETMOSLAR); + isb(); + + etm_read(ETMOSSRR); /* dummy read of OSSRR */ + cnt = etm.state[i++]; + for (j = 0; j < cnt; j++) + etm_write(etm.state[i++], ETMOSSRR); + + /* Clear the OS lock */ + isb(); + etm_write(0x0, ETMOSLAR); + isb(); + break; + default: + pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch, + __func__); + } + + /* Vote for ETM power/clock disable */ + etm_clk_disable(); +} + +/** + * msm_jtag_save_state - save debug and etm registers + * + * Debug and etm registers are saved before power collapse if debug + * and etm architecture is supported respectively and TZ isn't supporting + * the save and restore of debug and etm registers. + * + * CONTEXT: + * Called with preemption off and interrupts locked from: + * 1. per_cpu idle thread context for idle power collapses + * or + * 2. per_cpu idle thread context for hotplug/suspend power collapse + * for nonboot cpus + * or + * 3. suspend thread context for suspend power collapse for core0 + * + * In all cases we will run on the same cpu for the entire duration. + */ +void msm_jtag_save_state(void) +{ + int cpu; + + cpu = raw_smp_processor_id(); + + msm_jtag_save_cntr[cpu]++; + /* ensure counter is updated before moving forward */ + mb(); + + if (dbg.save_restore_enabled) + dbg_save_state(cpu); + if (etm.save_restore_enabled) + etm_save_state(cpu); +} +EXPORT_SYMBOL(msm_jtag_save_state); + +/** + * msm_jtag_restore_state - restore debug and etm registers + * + * Debug and etm registers are restored after power collapse if debug + * and etm architecture is supported respectively and TZ isn't supporting + * the save and restore of debug and etm registers. + * + * CONTEXT: + * Called with preemption off and interrupts locked from: + * 1. per_cpu idle thread context for idle power collapses + * or + * 2. per_cpu idle thread context for hotplug/suspend power collapse + * for nonboot cpus + * or + * 3. suspend thread context for suspend power collapse for core0 + * + * In all cases we will run on the same cpu for the entire duration. + */ +void msm_jtag_restore_state(void) +{ + int cpu; + + cpu = raw_smp_processor_id(); + + msm_jtag_restore_cntr[cpu]++; + /* ensure counter is updated before moving forward */ + mb(); + + if (dbg.save_restore_enabled) + dbg_restore_state(cpu); + if (etm.save_restore_enabled) + etm_restore_state(cpu); +} +EXPORT_SYMBOL(msm_jtag_restore_state); + +static int __init msm_jtag_dbg_init(void) +{ + int ret; + uint32_t dbgdidr; + + /* This will run on core0 so use it to populate parameters */ + + /* Populate dbg_ctx data */ + + dbgdidr = dbg_read(DBGDIDR); + dbg.arch = BMVAL(dbgdidr, 16, 19); + dbg.nr_ctx_cmp = BMVAL(dbgdidr, 20, 23) + 1; + dbg.nr_bp = BMVAL(dbgdidr, 24, 27) + 1; + dbg.nr_wp = BMVAL(dbgdidr, 28, 31) + 1; + + if (dbg_arch_supported(dbg.arch)) { + if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) { + dbg.save_restore_enabled = true; + } else { + pr_info("dbg save-restore supported by TZ\n"); + goto dbg_out; + } + } else { + pr_info("dbg arch %u not supported\n", dbg.arch); + goto dbg_out; + } + + /* Allocate dbg state save space */ + dbg.state = kzalloc(MAX_DBG_STATE_SIZE * sizeof(uint32_t), GFP_KERNEL); + if (!dbg.state) { + ret = -ENOMEM; + goto dbg_err; + } +dbg_out: + return 0; +dbg_err: + return ret; +} +arch_initcall(msm_jtag_dbg_init); + +static int __init msm_jtag_etm_init(void) +{ + int ret; + uint32_t etmidr; + uint32_t etmccr; + + /* Vote for ETM power/clock enable */ + etm_clk_enable(); + + /* Clear sticky bit in PDSR - required for ETMv3.3 (8660) */ + etm_read(ETMPDSR); + isb(); + + /* Populate etm_ctx data */ + + etmidr = etm_read(ETMIDR); + etm.arch = BMVAL(etmidr, 4, 11); + + etmccr = etm_read(ETMCCR); + etm.nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2; + etm.nr_cntr = BMVAL(etmccr, 13, 15); + etm.nr_ext_inp = BMVAL(etmccr, 17, 19); + etm.nr_ext_out = BMVAL(etmccr, 20, 22); + etm.nr_ctxid_cmp = BMVAL(etmccr, 24, 25); + + if (etm_arch_supported(etm.arch)) { + if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER) { + etm.save_restore_enabled = true; + } else { + pr_info("etm save-restore supported by TZ\n"); + goto etm_out; + } + } else { + pr_info("etm arch %u not supported\n", etm.arch); + goto etm_out; + } + + /* Vote for ETM power/clock disable */ + etm_clk_disable(); + + /* Allocate etm state save space */ + etm.state = kzalloc(MAX_ETM_STATE_SIZE * sizeof(uint32_t), GFP_KERNEL); + if (!etm.state) { + ret = -ENOMEM; + goto etm_err; + } +etm_out: + etm_clk_disable(); + return 0; +etm_err: + return ret; +} +arch_initcall(msm_jtag_etm_init); diff --git a/arch/arm/mach-msm/keypad-surf-ffa.c b/arch/arm/mach-msm/keypad-surf-ffa.c new file mode 100644 index 00000000000..711cdbb8d63 --- /dev/null +++ b/arch/arm/mach-msm/keypad-surf-ffa.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include + +/* don't turn this on without updating the ffa support */ +#define SCAN_FUNCTION_KEYS 0 + +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + +static unsigned int keypad_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int keypad_col_gpios[] = { 36, 37, 38, 39, 40 }; + +static unsigned int keypad_row_gpios_8k_ffa[] = {31, 32, 33, 34, 35, 36}; +static unsigned int keypad_col_gpios_8k_ffa[] = {38, 39, 40, 41, 42}; + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(keypad_col_gpios) + (col)) +#define FFA_8K_KEYMAP_INDEX(row, col) ((row)* \ + ARRAY_SIZE(keypad_col_gpios_8k_ffa) + (col)) + +static const unsigned short keypad_keymap_surf[ARRAY_SIZE(keypad_col_gpios) * + ARRAY_SIZE(keypad_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static const unsigned short keypad_keymap_ffa[ARRAY_SIZE(keypad_col_gpios) * + ARRAY_SIZE(keypad_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +#define QSD8x50_FFA_KEYMAP_SIZE (ARRAY_SIZE(keypad_col_gpios_8k_ffa) * \ + ARRAY_SIZE(keypad_row_gpios_8k_ffa)) + +static const unsigned short keypad_keymap_8k_ffa[QSD8x50_FFA_KEYMAP_SIZE] = { + + [FFA_8K_KEYMAP_INDEX(0, 0)] = KEY_VOLUMEDOWN, + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [FFA_8K_KEYMAP_INDEX(0, 2)] = KEY_DOWN, + [FFA_8K_KEYMAP_INDEX(0, 3)] = KEY_8, + [FFA_8K_KEYMAP_INDEX(0, 4)] = KEY_5, + + [FFA_8K_KEYMAP_INDEX(1, 0)] = KEY_UP, + [FFA_8K_KEYMAP_INDEX(1, 1)] = KEY_CLEAR, + [FFA_8K_KEYMAP_INDEX(1, 2)] = KEY_4, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [FFA_8K_KEYMAP_INDEX(1, 4)] = KEY_2, + + [FFA_8K_KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [FFA_8K_KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [FFA_8K_KEYMAP_INDEX(2, 2)] = KEY_0, + [FFA_8K_KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [FFA_8K_KEYMAP_INDEX(2, 4)] = KEY_9, + + [FFA_8K_KEYMAP_INDEX(3, 0)] = KEY_3, + [FFA_8K_KEYMAP_INDEX(3, 1)] = KEY_RIGHT, + [FFA_8K_KEYMAP_INDEX(3, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [FFA_8K_KEYMAP_INDEX(3, 4)] = KEY_6, + + [FFA_8K_KEYMAP_INDEX(4, 0)] = 232, /* OK */ + [FFA_8K_KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [FFA_8K_KEYMAP_INDEX(4, 2)] = KEY_1, + [FFA_8K_KEYMAP_INDEX(4, 3)] = KEY_SEND, + [FFA_8K_KEYMAP_INDEX(4, 4)] = KEY_LEFT, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [FFA_8K_KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [FFA_8K_KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [FFA_8K_KEYMAP_INDEX(5, 3)] = 229, /* 1 */ + [FFA_8K_KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +/* SURF keypad platform device information */ +static struct gpio_event_matrix_info surf_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = keypad_keymap_surf, + .output_gpios = keypad_row_gpios, + .input_gpios = keypad_col_gpios, + .noutputs = ARRAY_SIZE(keypad_row_gpios), + .ninputs = ARRAY_SIZE(keypad_col_gpios), + .settle_time.tv64 = 40 * NSEC_PER_USEC, + .poll_time.tv64 = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS +}; + +static struct gpio_event_info *surf_keypad_info[] = { + &surf_keypad_matrix_info.info +}; + +static struct gpio_event_platform_data surf_keypad_data = { + .name = "surf_keypad", + .info = surf_keypad_info, + .info_count = ARRAY_SIZE(surf_keypad_info) +}; + +struct platform_device keypad_device_surf = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &surf_keypad_data, + }, +}; + +/* 8k FFA keypad platform device information */ +static struct gpio_event_matrix_info keypad_matrix_info_8k_ffa = { + .info.func = gpio_event_matrix_func, + .keymap = keypad_keymap_8k_ffa, + .output_gpios = keypad_row_gpios_8k_ffa, + .input_gpios = keypad_col_gpios_8k_ffa, + .noutputs = ARRAY_SIZE(keypad_row_gpios_8k_ffa), + .ninputs = ARRAY_SIZE(keypad_col_gpios_8k_ffa), + .settle_time.tv64 = 40 * NSEC_PER_USEC, + .poll_time.tv64 = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS +}; + +static struct gpio_event_info *keypad_info_8k_ffa[] = { + &keypad_matrix_info_8k_ffa.info +}; + +static struct gpio_event_platform_data keypad_data_8k_ffa = { + .name = "8k_ffa_keypad", + .info = keypad_info_8k_ffa, + .info_count = ARRAY_SIZE(keypad_info_8k_ffa) +}; + +struct platform_device keypad_device_8k_ffa = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &keypad_data_8k_ffa, + }, +}; + +/* 7k FFA keypad platform device information */ +static struct gpio_event_matrix_info keypad_matrix_info_7k_ffa = { + .info.func = gpio_event_matrix_func, + .keymap = keypad_keymap_ffa, + .output_gpios = keypad_row_gpios, + .input_gpios = keypad_col_gpios, + .noutputs = ARRAY_SIZE(keypad_row_gpios), + .ninputs = ARRAY_SIZE(keypad_col_gpios), + .settle_time.tv64 = 40 * NSEC_PER_USEC, + .poll_time.tv64 = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS +}; + +static struct gpio_event_info *keypad_info_7k_ffa[] = { + &keypad_matrix_info_7k_ffa.info +}; + +static struct gpio_event_platform_data keypad_data_7k_ffa = { + .name = "7k_ffa_keypad", + .info = keypad_info_7k_ffa, + .info_count = ARRAY_SIZE(keypad_info_7k_ffa) +}; + +struct platform_device keypad_device_7k_ffa = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &keypad_data_7k_ffa, + }, +}; + diff --git a/arch/arm/mach-msm/lpass-8660.c b/arch/arm/mach-msm/lpass-8660.c new file mode 100644 index 00000000000..10183609ff0 --- /dev/null +++ b/arch/arm/mach-msm/lpass-8660.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "modem_notifier.h" +#include "ramdump.h" + +#define Q6SS_WDOG_ENABLE 0x28882024 +#define Q6SS_SOFT_INTR_WAKEUP 0x288A001C +#define MODULE_NAME "lpass_8x60" +#define SCM_Q6_NMI_CMD 0x1 + +/* Subsystem restart: QDSP6 data, functions */ +static void *q6_ramdump_dev; +static void q6_fatal_fn(struct work_struct *); +static DECLARE_WORK(q6_fatal_work, q6_fatal_fn); +static void __iomem *q6_wakeup_intr; + +static void q6_fatal_fn(struct work_struct *work) +{ + pr_err("%s: Watchdog bite received from Q6!\n", MODULE_NAME); + subsystem_restart("lpass"); + enable_irq(LPASS_Q6SS_WDOG_EXPIRED); +} + +static void send_q6_nmi(void) +{ + /* Send NMI to QDSP6 via an SCM call. */ + scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1); + + /* Wakeup the Q6 */ + if (q6_wakeup_intr) + writel_relaxed(0x2000, q6_wakeup_intr); + else + pr_warn("lpass-8660: Unable to send wakeup interrupt to Q6.\n"); + + /* Q6 requires atleast 100ms to dump caches etc.*/ + mdelay(100); + + pr_info("subsystem-fatal-8x60: Q6 NMI was sent.\n"); +} + +int subsys_q6_shutdown(const struct subsys_data *crashed_subsys) +{ + void __iomem *q6_wdog_addr = + ioremap_nocache(Q6SS_WDOG_ENABLE, 8); + + send_q6_nmi(); + writel_relaxed(0x0, q6_wdog_addr); + /* The write needs to go through before the q6 is shutdown. */ + mb(); + iounmap(q6_wdog_addr); + + pil_force_shutdown("q6"); + disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED); + + return 0; +} + +int subsys_q6_powerup(const struct subsys_data *crashed_subsys) +{ + int ret = pil_force_boot("q6"); + enable_irq(LPASS_Q6SS_WDOG_EXPIRED); + return ret; +} + +/* FIXME: Get address, size from PIL */ +static struct ramdump_segment q6_segments[] = { {0x46700000, 0x47F00000 - + 0x46700000}, {0x28400000, 0x12800} }; +static int subsys_q6_ramdump(int enable, + const struct subsys_data *crashed_subsys) +{ + if (enable) + return do_ramdump(q6_ramdump_dev, q6_segments, + ARRAY_SIZE(q6_segments)); + else + return 0; +} + +void subsys_q6_crash_shutdown(const struct subsys_data *crashed_subsys) +{ + send_q6_nmi(); +} + +static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id) +{ + int ret; + + ret = schedule_work(&q6_fatal_work); + disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED); + + return IRQ_HANDLED; +} + +static struct subsys_data subsys_8x60_q6 = { + .name = "lpass", + .shutdown = subsys_q6_shutdown, + .powerup = subsys_q6_powerup, + .ramdump = subsys_q6_ramdump, + .crash_shutdown = subsys_q6_crash_shutdown +}; + +static void __exit lpass_fatal_exit(void) +{ + iounmap(q6_wakeup_intr); + free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL); +} + +static int __init lpass_fatal_init(void) +{ + int ret; + + ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq, + IRQF_TRIGGER_RISING, "q6_wdog", NULL); + + if (ret < 0) { + pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.", + __func__); + goto out; + } + + q6_ramdump_dev = create_ramdump_device("lpass"); + + if (!q6_ramdump_dev) { + ret = -ENOMEM; + goto out; + } + + q6_wakeup_intr = ioremap_nocache(Q6SS_SOFT_INTR_WAKEUP, 8); + + if (!q6_wakeup_intr) + pr_warn("lpass-8660: Unable to ioremap q6 wakeup address."); + + ret = ssr_register_subsystem(&subsys_8x60_q6); +out: + return ret; +} + +module_init(lpass_fatal_init); +module_exit(lpass_fatal_exit); + diff --git a/arch/arm/mach-msm/lpass-8960.c b/arch/arm/mach-msm/lpass-8960.c new file mode 100644 index 00000000000..c58b0e1024d --- /dev/null +++ b/arch/arm/mach-msm/lpass-8960.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "ramdump.h" +#include "sysmon.h" + +#define SCM_Q6_NMI_CMD 0x1 +#define MODULE_NAME "lpass_8960" +#define MAX_BUF_SIZE 0x51 + +/* Subsystem restart: QDSP6 data, functions */ +static void lpass_fatal_fn(struct work_struct *); +static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn); +struct lpass_ssr { + void *lpass_ramdump_dev; +} lpass_ssr; + +static struct lpass_ssr lpass_ssr_8960; +static int q6_crash_shutdown; + +static int riva_notifier_cb(struct notifier_block *this, unsigned long code, + void *ss_handle) +{ + int ret; + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("%s: R-Notify: Shutdown started\n", __func__); + ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss", + SUBSYS_BEFORE_SHUTDOWN); + if (ret < 0) + pr_err("%s: sysmon_send_event error %d", __func__, + ret); + break; + } + return NOTIFY_DONE; +} + +static void *ssr_notif_hdle; +static struct notifier_block rnb = { + .notifier_call = riva_notifier_cb, +}; + +static int modem_notifier_cb(struct notifier_block *this, unsigned long code, + void *ss_handle) +{ + int ret; + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("%s: M-Notify: Shutdown started\n", __func__); + ret = sysmon_send_event(SYSMON_SS_LPASS, "modem", + SUBSYS_BEFORE_SHUTDOWN); + if (ret < 0) + pr_err("%s: sysmon_send_event error %d", __func__, + ret); + break; + } + return NOTIFY_DONE; +} + +static void *ssr_modem_notif_hdle; +static struct notifier_block mnb = { + .notifier_call = modem_notifier_cb, +}; + +static void lpass_log_failure_reason(void) +{ + char *reason; + char buffer[MAX_BUF_SIZE]; + unsigned size; + + reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size); + + if (!reason) { + pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).", + MODULE_NAME); + return; + } + + if (reason[0] == '\0') { + pr_err("%s: subsystem failure reason: (unknown, init value found)", + MODULE_NAME); + return; + } + + size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1); + memcpy(buffer, reason, size); + buffer[size] = '\0'; + pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer); + memset((void *)reason, 0x0, size); + wmb(); +} + +static void lpass_fatal_fn(struct work_struct *work) +{ + pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME, + __func__); + lpass_log_failure_reason(); + panic(MODULE_NAME ": Resetting the SoC"); +} + +static void lpass_smsm_state_cb(void *data, uint32_t old_state, + uint32_t new_state) +{ + /* Ignore if we're the one that set SMSM_RESET */ + if (q6_crash_shutdown) + return; + + if (new_state & SMSM_RESET) { + pr_err("%s: LPASS SMSM state changed to SMSM_RESET," + " new_state = 0x%x, old_state = 0x%x\n", __func__, + new_state, old_state); + lpass_log_failure_reason(); + panic(MODULE_NAME ": Resetting the SoC"); + } +} + +static void send_q6_nmi(void) +{ + /* Send NMI to QDSP6 via an SCM call. */ + uint32_t cmd = 0x1; + + scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, + &cmd, sizeof(cmd), NULL, 0); + + /* Q6 requires worstcase 100ms to dump caches etc.*/ + mdelay(100); + pr_debug("%s: Q6 NMI was sent.\n", __func__); +} + +static int lpass_shutdown(const struct subsys_data *subsys) +{ + send_q6_nmi(); + pil_force_shutdown("q6"); + disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED); + + return 0; +} + +static int lpass_powerup(const struct subsys_data *subsys) +{ + int ret = pil_force_boot("q6"); + enable_irq(LPASS_Q6SS_WDOG_EXPIRED); + return ret; +} +/* RAM segments - address and size for 8960 */ +static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 - + 0x8da00000}, {0x28400000, 0x20000} }; +static int lpass_ramdump(int enable, const struct subsys_data *subsys) +{ + pr_debug("%s: enable[%d]\n", __func__, enable); + if (enable) + return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev, + q6_segments, + ARRAY_SIZE(q6_segments)); + else + return 0; +} + +static void lpass_crash_shutdown(const struct subsys_data *subsys) +{ + q6_crash_shutdown = 1; + send_q6_nmi(); +} + +static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id) +{ + int ret; + + pr_debug("%s: rxed irq[0x%x]", __func__, irq); + disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED); + ret = schedule_work(&lpass_fatal_work); + + return IRQ_HANDLED; +} + +static struct subsys_data lpass_8960 = { + .name = "lpass", + .shutdown = lpass_shutdown, + .powerup = lpass_powerup, + .ramdump = lpass_ramdump, + .crash_shutdown = lpass_crash_shutdown +}; + +static int __init lpass_restart_init(void) +{ + return ssr_register_subsystem(&lpass_8960); +} + +static int __init lpass_fatal_init(void) +{ + int ret; + + ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET, + lpass_smsm_state_cb, 0); + + if (ret < 0) + pr_err("%s: Unable to register SMSM callback! (%d)\n", + __func__, ret); + + ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq, + IRQF_TRIGGER_RISING, "q6_wdog", NULL); + + if (ret < 0) { + pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.", + __func__); + goto out; + } + ret = lpass_restart_init(); + if (ret < 0) { + pr_err("%s: Unable to reg with lpass ssr. (%d)\n", + __func__, ret); + goto out; + } + + lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass"); + + if (!lpass_ssr_8960.lpass_ramdump_dev) { + pr_err("%s: Unable to create ramdump device.\n", + __func__); + ret = -ENOMEM; + goto out; + } + ssr_notif_hdle = subsys_notif_register_notifier("riva", + &rnb); + if (IS_ERR(ssr_notif_hdle) < 0) { + ret = PTR_ERR(ssr_notif_hdle); + pr_err("%s: subsys_register_notifier for Riva: err = %d\n", + __func__, ret); + free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL); + goto out; + } + + ssr_modem_notif_hdle = subsys_notif_register_notifier("modem", + &mnb); + if (IS_ERR(ssr_modem_notif_hdle) < 0) { + ret = PTR_ERR(ssr_modem_notif_hdle); + pr_err("%s: subsys_register_notifier for Modem: err = %d\n", + __func__, ret); + subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb); + free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL); + goto out; + } + + pr_info("%s: lpass SSR driver init'ed.\n", __func__); +out: + return ret; +} + +static void __exit lpass_fatal_exit(void) +{ + subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb); + subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb); + free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL); +} + +module_init(lpass_fatal_init); +module_exit(lpass_fatal_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c new file mode 100644 index 00000000000..a1f5ff5595e --- /dev/null +++ b/arch/arm/mach-msm/lpm_levels.c @@ -0,0 +1,266 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "rpm_resources.h" +#include "pm.h" + +static struct msm_rpmrs_level *msm_lpm_levels; +static int msm_lpm_level_count; + +static int msm_lpm_enter_sleep(uint32_t sclk_count, void *limits, + bool from_idle, bool notify_rpm) +{ + /* TODO */ + return 0; +} + +static void msm_lpm_exit_sleep(void *limits, bool from_idle, + bool notify_rpm, bool collapsed) +{ + /* TODO */ + return; +} + +static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits, + bool irqs_detect, bool gpio_detect) +{ + /* TODO */ + return true; +} + +void msm_rpmrs_show_resources(void) +{ + /* TODO */ + return; +} + +s32 msm_cpuidle_get_deep_idle_latency(void) +{ + int i; + struct msm_rpmrs_level *level = msm_lpm_levels, *best = level; + + if (!level) + return 0; + + for (i = 0; i < msm_lpm_level_count; i++, level++) { + if (!level->available) + continue; + if (level->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE) + continue; + /* Pick the first power collapse mode by default */ + if (best->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE) + best = level; + /* Find the lowest latency for power collapse */ + if (level->latency_us < best->latency_us) + best = level; + } + return best->latency_us - 1; +} + +static void *msm_lpm_lowest_limits(bool from_idle, + enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us, + uint32_t sleep_us, uint32_t *power) +{ + unsigned int cpu = smp_processor_id(); + struct msm_rpmrs_level *best_level = NULL; + bool irqs_detectable = false; + bool gpio_detectable = false; + uint32_t pwr; + int i; + + if (!msm_lpm_levels) + return NULL; + + if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) { + irqs_detectable = msm_mpm_irqs_detectable(from_idle); + gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle); + } + + for (i = 0; i < msm_lpm_level_count; i++) { + struct msm_rpmrs_level *level = &msm_lpm_levels[i]; + + if (!level->available) + continue; + + if (sleep_mode != level->sleep_mode) + continue; + + if (latency_us < level->latency_us) + continue; + + if (!msm_rpmrs_irqs_detectable(&level->rs_limits, + irqs_detectable, gpio_detectable)) + continue; + + if (sleep_us <= 1) { + pwr = level->energy_overhead; + } else if (sleep_us <= level->time_overhead_us) { + pwr = level->energy_overhead / sleep_us; + } else if ((sleep_us >> 10) > level->time_overhead_us) { + pwr = level->steady_state_power; + } else { + pwr = level->steady_state_power; + pwr -= (level->time_overhead_us * + level->steady_state_power)/sleep_us; + pwr += level->energy_overhead / sleep_us; + } + + if (!best_level || best_level->rs_limits.power[cpu] >= pwr) { + + level->rs_limits.latency_us[cpu] = level->latency_us; + level->rs_limits.power[cpu] = pwr; + best_level = level; + + if (power) + *power = pwr; + } + } + + return best_level ? &best_level->rs_limits : NULL; +} +static struct msm_pm_sleep_ops msm_lpm_ops = { + .lowest_limits = msm_lpm_lowest_limits, + .enter_sleep = msm_lpm_enter_sleep, + .exit_sleep = msm_lpm_exit_sleep, +}; + +static int __devinit msm_lpm_levels_probe(struct platform_device *pdev) +{ + struct msm_rpmrs_level *levels = NULL; + struct msm_rpmrs_level *level = NULL; + struct device_node *node = NULL; + char *key = NULL; + uint32_t val = 0; + int ret = 0; + uint32_t num_levels = 0; + int idx = 0; + + for_each_child_of_node(pdev->dev.of_node, node) + num_levels++; + + levels = kzalloc(num_levels * sizeof(struct msm_rpmrs_level), + GFP_KERNEL); + if (!levels) + return -ENOMEM; + + for_each_child_of_node(pdev->dev.of_node, node) { + level = &levels[idx++]; + level->available = false; + + key = "qcom,mode"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->sleep_mode = val; + + key = "qcom,xo"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->rs_limits.pxo = val; + + key = "qcom,l2"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->rs_limits.l2_cache = val; + + key = "qcom,vdd-dig-upper-bound"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->rs_limits.vdd_dig_upper_bound = val; + + key = "qcom,vdd-dig-lower-bound"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->rs_limits.vdd_dig = val; + + key = "qcom,vdd-mem-upper-bound"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->rs_limits.vdd_mem_upper_bound = val; + + key = "qcom,vdd-mem-lower-bound"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->rs_limits.vdd_mem = val; + + key = "qcom,latency-us"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->latency_us = val; + + key = "qcom,ss-power"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->steady_state_power = val; + + key = "qcom,energy-overhead"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->energy_overhead = val; + + key = "qcom,time-overhead"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + level->time_overhead_us = val; + + level->available = true; + } + + msm_lpm_levels = levels; + msm_lpm_level_count = idx; + + msm_pm_set_sleep_ops(&msm_lpm_ops); + + return 0; +fail: + pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key); + kfree(levels); + return -EFAULT; +} + +static struct of_device_id msm_lpm_levels_match_table[] = { + {.compatible = "qcom,lpm-levels"}, + {}, +}; + +static struct platform_driver msm_lpm_levels_driver = { + .probe = msm_lpm_levels_probe, + .driver = { + .name = "lpm-levels", + .owner = THIS_MODULE, + .of_match_table = msm_lpm_levels_match_table, + }, +}; + +static int __init msm_lpm_levels_module_init(void) +{ + return platform_driver_register(&msm_lpm_levels_driver); +} +late_initcall(msm_lpm_levels_module_init); diff --git a/arch/arm/mach-msm/mdm.c b/arch/arm/mach-msm/mdm.c new file mode 100644 index 00000000000..cbdc92ab7ff --- /dev/null +++ b/arch/arm/mach-msm/mdm.c @@ -0,0 +1,483 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_watchdog.h" +#include "devices.h" +#include "clock.h" + +#define CHARM_MODEM_TIMEOUT 6000 +#define CHARM_HOLD_TIME 4000 +#define CHARM_MODEM_DELTA 100 + +static void (*power_on_charm)(void); +static void (*power_down_charm)(void); + +static int charm_debug_on; +static int charm_status_irq; +static int charm_errfatal_irq; +static int charm_ready; +static enum charm_boot_type boot_type = CHARM_NORMAL_BOOT; +static int charm_boot_status; +static int charm_ram_dump_status; +static struct workqueue_struct *charm_queue; + +#define CHARM_DBG(...) do { if (charm_debug_on) \ + pr_info(__VA_ARGS__); \ + } while (0); + + +DECLARE_COMPLETION(charm_needs_reload); +DECLARE_COMPLETION(charm_boot); +DECLARE_COMPLETION(charm_ram_dumps); + +static void charm_disable_irqs(void) +{ + disable_irq_nosync(charm_errfatal_irq); + disable_irq_nosync(charm_status_irq); + +} + +static int charm_subsys_shutdown(const struct subsys_data *crashed_subsys) +{ + charm_ready = 0; + power_down_charm(); + return 0; +} + +static int charm_subsys_powerup(const struct subsys_data *crashed_subsys) +{ + power_on_charm(); + boot_type = CHARM_NORMAL_BOOT; + complete(&charm_needs_reload); + wait_for_completion(&charm_boot); + pr_info("%s: charm modem has been restarted\n", __func__); + INIT_COMPLETION(charm_boot); + return charm_boot_status; +} + +static int charm_subsys_ramdumps(int want_dumps, + const struct subsys_data *crashed_subsys) +{ + charm_ram_dump_status = 0; + if (want_dumps) { + boot_type = CHARM_RAM_DUMPS; + complete(&charm_needs_reload); + wait_for_completion(&charm_ram_dumps); + INIT_COMPLETION(charm_ram_dumps); + power_down_charm(); + } + return charm_ram_dump_status; +} + +static struct subsys_data charm_subsystem = { + .shutdown = charm_subsys_shutdown, + .ramdump = charm_subsys_ramdumps, + .powerup = charm_subsys_powerup, + .name = "external_modem", +}; + +static int charm_panic_prep(struct notifier_block *this, + unsigned long event, void *ptr) +{ + int i; + + CHARM_DBG("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n", + __func__); + if (get_restart_level() == RESET_SOC) + pm8xxx_stay_on(); + + charm_disable_irqs(); + gpio_set_value(AP2MDM_ERRFATAL, 1); + gpio_set_value(AP2MDM_WAKEUP, 1); + for (i = CHARM_MODEM_TIMEOUT; i > 0; i -= CHARM_MODEM_DELTA) { + pet_watchdog(); + mdelay(CHARM_MODEM_DELTA); + if (gpio_get_value(MDM2AP_STATUS) == 0) + break; + } + if (i <= 0) + pr_err("%s: MDM2AP_STATUS never went low\n", __func__); + return NOTIFY_DONE; +} + +static struct notifier_block charm_panic_blk = { + .notifier_call = charm_panic_prep, +}; + +static int first_boot = 1; + +static long charm_modem_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + + int status, ret = 0; + + if (_IOC_TYPE(cmd) != CHARM_CODE) { + pr_err("%s: invalid ioctl code\n", __func__); + return -EINVAL; + } + + CHARM_DBG("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd)); + switch (cmd) { + case WAKE_CHARM: + CHARM_DBG("%s: Powering on\n", __func__); + power_on_charm(); + break; + case CHECK_FOR_BOOT: + if (gpio_get_value(MDM2AP_STATUS) == 0) + put_user(1, (unsigned long __user *) arg); + else + put_user(0, (unsigned long __user *) arg); + break; + case NORMAL_BOOT_DONE: + CHARM_DBG("%s: check if charm is booted up\n", __func__); + get_user(status, (unsigned long __user *) arg); + if (status) + charm_boot_status = -EIO; + else + charm_boot_status = 0; + charm_ready = 1; + + gpio_set_value(AP2MDM_KPDPWR_N, 0); + if (!first_boot) + complete(&charm_boot); + else + first_boot = 0; + break; + case RAM_DUMP_DONE: + CHARM_DBG("%s: charm done collecting RAM dumps\n", __func__); + get_user(status, (unsigned long __user *) arg); + if (status) + charm_ram_dump_status = -EIO; + else + charm_ram_dump_status = 0; + complete(&charm_ram_dumps); + break; + case WAIT_FOR_RESTART: + CHARM_DBG("%s: wait for charm to need images reloaded\n", + __func__); + ret = wait_for_completion_interruptible(&charm_needs_reload); + if (!ret) + put_user(boot_type, (unsigned long __user *) arg); + INIT_COMPLETION(charm_needs_reload); + break; + default: + pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd)); + ret = -EINVAL; + break; + } + + return ret; +} + +static int charm_modem_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations charm_modem_fops = { + .owner = THIS_MODULE, + .open = charm_modem_open, + .unlocked_ioctl = charm_modem_ioctl, +}; + + +struct miscdevice charm_modem_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mdm", + .fops = &charm_modem_fops +}; + + + +static void charm_status_fn(struct work_struct *work) +{ + pr_info("Reseting the charm because status changed\n"); + subsystem_restart("external_modem"); +} + +static DECLARE_WORK(charm_status_work, charm_status_fn); + +static void charm_fatal_fn(struct work_struct *work) +{ + pr_info("Reseting the charm due to an errfatal\n"); + if (get_restart_level() == RESET_SOC) + pm8xxx_stay_on(); + subsystem_restart("external_modem"); +} + +static DECLARE_WORK(charm_fatal_work, charm_fatal_fn); + +static irqreturn_t charm_errfatal(int irq, void *dev_id) +{ + CHARM_DBG("%s: charm got errfatal interrupt\n", __func__); + if (charm_ready && (gpio_get_value(MDM2AP_STATUS) == 1)) { + CHARM_DBG("%s: scheduling work now\n", __func__); + queue_work(charm_queue, &charm_fatal_work); + } + return IRQ_HANDLED; +} + +static irqreturn_t charm_status_change(int irq, void *dev_id) +{ + CHARM_DBG("%s: charm sent status change interrupt\n", __func__); + if ((gpio_get_value(MDM2AP_STATUS) == 0) && charm_ready) { + CHARM_DBG("%s: scheduling work now\n", __func__); + queue_work(charm_queue, &charm_status_work); + } else if (gpio_get_value(MDM2AP_STATUS) == 1) { + CHARM_DBG("%s: charm is now ready\n", __func__); + } + return IRQ_HANDLED; +} + +static int charm_debug_on_set(void *data, u64 val) +{ + charm_debug_on = val; + return 0; +} + +static int charm_debug_on_get(void *data, u64 *val) +{ + *val = charm_debug_on; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(charm_debug_on_fops, + charm_debug_on_get, + charm_debug_on_set, "%llu\n"); + +static int charm_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("charm_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("debug_on", 0644, dent, NULL, + &charm_debug_on_fops); + return 0; +} + +static int gsbi9_uart_notifier_cb(struct notifier_block *this, + unsigned long code, void *_cmd) +{ + switch (code) { + case SUBSYS_AFTER_SHUTDOWN: + platform_device_unregister(msm_device_uart_gsbi9); + msm_device_uart_gsbi9 = msm_add_gsbi9_uart(); + if (IS_ERR(msm_device_uart_gsbi9)) + pr_err("%s(): Failed to create uart gsbi9 device\n", + __func__); + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block gsbi9_nb = { + .notifier_call = gsbi9_uart_notifier_cb, +}; + +static int __init charm_modem_probe(struct platform_device *pdev) +{ + int ret, irq; + struct charm_platform_data *d = pdev->dev.platform_data; + + gpio_request(AP2MDM_STATUS, "AP2MDM_STATUS"); + gpio_request(AP2MDM_ERRFATAL, "AP2MDM_ERRFATAL"); + gpio_request(AP2MDM_KPDPWR_N, "AP2MDM_KPDPWR_N"); + gpio_request(AP2MDM_PMIC_RESET_N, "AP2MDM_PMIC_RESET_N"); + gpio_request(MDM2AP_STATUS, "MDM2AP_STATUS"); + gpio_request(MDM2AP_ERRFATAL, "MDM2AP_ERRFATAL"); + gpio_request(AP2MDM_WAKEUP, "AP2MDM_WAKEUP"); + + gpio_direction_output(AP2MDM_STATUS, 1); + gpio_direction_output(AP2MDM_ERRFATAL, 0); + gpio_direction_output(AP2MDM_WAKEUP, 0); + gpio_direction_input(MDM2AP_STATUS); + gpio_direction_input(MDM2AP_ERRFATAL); + + power_on_charm = d->charm_modem_on; + power_down_charm = d->charm_modem_off; + + charm_queue = create_singlethread_workqueue("charm_queue"); + if (!charm_queue) { + pr_err("%s: could not create workqueue. All charm \ + functionality will be disabled\n", + __func__); + ret = -ENOMEM; + goto fatal_err; + } + + atomic_notifier_chain_register(&panic_notifier_list, &charm_panic_blk); + charm_debugfs_init(); + + ssr_register_subsystem(&charm_subsystem); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. \ + error=%d No IRQ will be generated on errfatal.", + __func__, irq); + goto errfatal_err; + } + + ret = request_irq(irq, charm_errfatal, + IRQF_TRIGGER_RISING , "charm errfatal", NULL); + + if (ret < 0) { + pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d\ + . No IRQ will be generated on errfatal.", + __func__, irq, ret); + goto errfatal_err; + } + charm_errfatal_irq = irq; + +errfatal_err: + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + pr_err("%s: could not get MDM2AP_STATUS IRQ resource. \ + error=%d No IRQ will be generated on status change.", + __func__, irq); + goto status_err; + } + + ret = request_threaded_irq(irq, NULL, charm_status_change, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "charm status", NULL); + + if (ret < 0) { + pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d\ + . No IRQ will be generated on status change.", + __func__, irq, ret); + goto status_err; + } + charm_status_irq = irq; + +status_err: + subsys_notif_register_notifier("external_modem", &gsbi9_nb); + + pr_info("%s: Registering charm modem\n", __func__); + + return misc_register(&charm_modem_misc); + +fatal_err: + gpio_free(AP2MDM_STATUS); + gpio_free(AP2MDM_ERRFATAL); + gpio_free(AP2MDM_KPDPWR_N); + gpio_free(AP2MDM_PMIC_RESET_N); + gpio_free(MDM2AP_STATUS); + gpio_free(MDM2AP_ERRFATAL); + return ret; + +} + + +static int __devexit charm_modem_remove(struct platform_device *pdev) +{ + gpio_free(AP2MDM_STATUS); + gpio_free(AP2MDM_ERRFATAL); + gpio_free(AP2MDM_KPDPWR_N); + gpio_free(AP2MDM_PMIC_RESET_N); + gpio_free(MDM2AP_STATUS); + gpio_free(MDM2AP_ERRFATAL); + + return misc_deregister(&charm_modem_misc); +} + +static void charm_modem_shutdown(struct platform_device *pdev) +{ + int i; + + CHARM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n", + __func__); + + charm_disable_irqs(); + + gpio_set_value(AP2MDM_STATUS, 0); + gpio_set_value(AP2MDM_WAKEUP, 1); + + for (i = CHARM_MODEM_TIMEOUT; i > 0; i -= CHARM_MODEM_DELTA) { + pet_watchdog(); + msleep(CHARM_MODEM_DELTA); + if (gpio_get_value(MDM2AP_STATUS) == 0) + break; + } + + if (i <= 0) { + pr_err("%s: MDM2AP_STATUS never went low.\n", + __func__); + gpio_direction_output(AP2MDM_PMIC_RESET_N, 1); + for (i = CHARM_HOLD_TIME; i > 0; i -= CHARM_MODEM_DELTA) { + pet_watchdog(); + msleep(CHARM_MODEM_DELTA); + } + gpio_direction_output(AP2MDM_PMIC_RESET_N, 0); + } + gpio_set_value(AP2MDM_WAKEUP, 0); +} + +static struct platform_driver charm_modem_driver = { + .remove = charm_modem_remove, + .shutdown = charm_modem_shutdown, + .driver = { + .name = "charm_modem", + .owner = THIS_MODULE + }, +}; + +static int __init charm_modem_init(void) +{ + return platform_driver_probe(&charm_modem_driver, charm_modem_probe); +} + +static void __exit charm_modem_exit(void) +{ + platform_driver_unregister(&charm_modem_driver); +} + +module_init(charm_modem_init); +module_exit(charm_modem_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("msm8660 charm modem driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("charm_modem"); diff --git a/arch/arm/mach-msm/mdm2.c b/arch/arm/mach-msm/mdm2.c new file mode 100644 index 00000000000..bd7bd9e9fc1 --- /dev/null +++ b/arch/arm/mach-msm/mdm2.c @@ -0,0 +1,237 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_watchdog.h" +#include "devices.h" +#include "clock.h" +#include "mdm_private.h" + +#define MDM_MODEM_TIMEOUT 6000 +#define MDM_HOLD_TIME 4000 +#define MDM_MODEM_DELTA 100 + +static int mdm_debug_on; +static int power_on_count; +static int hsic_peripheral_status; +static DEFINE_MUTEX(hsic_status_lock); + +static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv) +{ + if (!mdm_drv->pdata->peripheral_platform_device) + return; + + mutex_lock(&hsic_status_lock); + if (hsic_peripheral_status) + goto out; + platform_device_add(mdm_drv->pdata->peripheral_platform_device); + hsic_peripheral_status = 1; +out: + mutex_unlock(&hsic_status_lock); +} + +static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv) +{ + if (!mdm_drv->pdata->peripheral_platform_device) + return; + + mutex_lock(&hsic_status_lock); + if (!hsic_peripheral_status) + goto out; + platform_device_del(mdm_drv->pdata->peripheral_platform_device); + hsic_peripheral_status = 0; +out: + mutex_unlock(&hsic_status_lock); +} + +static void mdm_power_down_common(struct mdm_modem_drv *mdm_drv) +{ + int soft_reset_direction = + mdm_drv->pdata->soft_reset_inverted ? 1 : 0; + + gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, + soft_reset_direction); + mdm_peripheral_disconnect(mdm_drv); +} + +static void mdm_do_first_power_on(struct mdm_modem_drv *mdm_drv) +{ + int soft_reset_direction = + mdm_drv->pdata->soft_reset_inverted ? 0 : 1; + + if (power_on_count != 1) { + pr_err("%s: Calling fn when power_on_count != 1\n", + __func__); + return; + } + + pr_err("%s: Powering on modem for the first time\n", __func__); + mdm_peripheral_disconnect(mdm_drv); + + /* If the device has a kpd pwr gpio then toggle it. */ + if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) { + /* Pull AP2MDM_KPDPWR gpio high and wait for PS_HOLD to settle, + * then pull it back low. + */ + pr_debug("%s: Pulling AP2MDM_KPDPWR gpio high\n", __func__); + gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1); + msleep(1000); + gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0); + } + + /* De-assert the soft reset line. */ + pr_debug("%s: De-asserting soft reset gpio\n", __func__); + gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, + soft_reset_direction); + + mdm_peripheral_connect(mdm_drv); + msleep(200); +} + +static void mdm_do_soft_power_on(struct mdm_modem_drv *mdm_drv) +{ + int soft_reset_direction = + mdm_drv->pdata->soft_reset_inverted ? 0 : 1; + + /* De-assert the soft reset line. */ + pr_err("%s: soft resetting mdm modem\n", __func__); + + mdm_peripheral_disconnect(mdm_drv); + + gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, + soft_reset_direction == 1 ? 0 : 1); + usleep_range(5000, 10000); + gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, + soft_reset_direction == 1 ? 1 : 0); + + mdm_peripheral_connect(mdm_drv); + msleep(200); +} + +static void mdm_power_on_common(struct mdm_modem_drv *mdm_drv) +{ + power_on_count++; + + /* this gpio will be used to indicate apq readiness, + * de-assert it now so that it can be asserted later. + * May not be used. + */ + if (mdm_drv->ap2mdm_wakeup_gpio > 0) + gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0); + + /* + * If we did an "early power on" then ignore the very next + * power-on request because it would the be first request from + * user space but we're already powered on. Ignore it. + */ + if (mdm_drv->pdata->early_power_on && + (power_on_count == 2)) + return; + + if (power_on_count == 1) + mdm_do_first_power_on(mdm_drv); + else + mdm_do_soft_power_on(mdm_drv); +} + +static void debug_state_changed(int value) +{ + mdm_debug_on = value; +} + +static void mdm_status_changed(struct mdm_modem_drv *mdm_drv, int value) +{ + pr_debug("%s: value:%d\n", __func__, value); + + if (value) { + mdm_peripheral_disconnect(mdm_drv); + mdm_peripheral_connect(mdm_drv); + if (mdm_drv->ap2mdm_wakeup_gpio > 0) + gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1); + } +} + +static struct mdm_ops mdm_cb = { + .power_on_mdm_cb = mdm_power_on_common, + .reset_mdm_cb = mdm_power_on_common, + .power_down_mdm_cb = mdm_power_down_common, + .debug_state_changed_cb = debug_state_changed, + .status_cb = mdm_status_changed, +}; + +static int __init mdm_modem_probe(struct platform_device *pdev) +{ + return mdm_common_create(pdev, &mdm_cb); +} + +static int __devexit mdm_modem_remove(struct platform_device *pdev) +{ + return mdm_common_modem_remove(pdev); +} + +static void mdm_modem_shutdown(struct platform_device *pdev) +{ + mdm_common_modem_shutdown(pdev); +} + +static struct platform_driver mdm_modem_driver = { + .remove = mdm_modem_remove, + .shutdown = mdm_modem_shutdown, + .driver = { + .name = "mdm2_modem", + .owner = THIS_MODULE + }, +}; + +static int __init mdm_modem_init(void) +{ + return platform_driver_probe(&mdm_modem_driver, mdm_modem_probe); +} + +static void __exit mdm_modem_exit(void) +{ + platform_driver_unregister(&mdm_modem_driver); +} + +module_init(mdm_modem_init); +module_exit(mdm_modem_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("mdm modem driver"); +MODULE_VERSION("2.0"); +MODULE_ALIAS("mdm_modem"); diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c new file mode 100644 index 00000000000..ffff78222fd --- /dev/null +++ b/arch/arm/mach-msm/mdm_common.c @@ -0,0 +1,607 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_watchdog.h" +#include "mdm_private.h" +#include "sysmon.h" + +#define MDM_MODEM_TIMEOUT 6000 +#define MDM_MODEM_DELTA 100 +#define MDM_BOOT_TIMEOUT 60000L +#define MDM_RDUMP_TIMEOUT 60000L + +static int mdm_debug_on; +static struct workqueue_struct *mdm_queue; +static struct workqueue_struct *mdm_sfr_queue; + +#define EXTERNAL_MODEM "external_modem" + +static struct mdm_modem_drv *mdm_drv; + +DECLARE_COMPLETION(mdm_needs_reload); +DECLARE_COMPLETION(mdm_boot); +DECLARE_COMPLETION(mdm_ram_dumps); + +static int first_boot = 1; + +#define RD_BUF_SIZE 100 +#define SFR_MAX_RETRIES 10 +#define SFR_RETRY_INTERVAL 1000 + +static void mdm_restart_reason_fn(struct work_struct *work) +{ + int ret, ntries = 0; + char sfr_buf[RD_BUF_SIZE]; + + do { + msleep(SFR_RETRY_INTERVAL); + ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM, + sfr_buf, sizeof(sfr_buf)); + if (ret) { + /* + * The sysmon device may not have been probed as yet + * after the restart. + */ + pr_err("%s: Error retrieving mdm restart reason, ret = %d, " + "%d/%d tries\n", __func__, ret, + ntries + 1, SFR_MAX_RETRIES); + } else { + pr_err("mdm restart reason: %s\n", sfr_buf); + break; + } + } while (++ntries < SFR_MAX_RETRIES); +} + +static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn); + +long mdm_modem_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int status, ret = 0; + + if (_IOC_TYPE(cmd) != CHARM_CODE) { + pr_err("%s: invalid ioctl code\n", __func__); + return -EINVAL; + } + + pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd)); + switch (cmd) { + case WAKE_CHARM: + pr_info("%s: Powering on mdm\n", __func__); + mdm_drv->ops->power_on_mdm_cb(mdm_drv); + break; + case CHECK_FOR_BOOT: + if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) + put_user(1, (unsigned long __user *) arg); + else + put_user(0, (unsigned long __user *) arg); + break; + case NORMAL_BOOT_DONE: + pr_debug("%s: check if mdm is booted up\n", __func__); + get_user(status, (unsigned long __user *) arg); + if (status) { + pr_debug("%s: normal boot failed\n", __func__); + mdm_drv->mdm_boot_status = -EIO; + } else { + pr_info("%s: normal boot done\n", __func__); + mdm_drv->mdm_boot_status = 0; + } + mdm_drv->mdm_ready = 1; + + if (mdm_drv->ops->normal_boot_done_cb != NULL) + mdm_drv->ops->normal_boot_done_cb(mdm_drv); + + if (!first_boot) + complete(&mdm_boot); + else + first_boot = 0; + break; + case RAM_DUMP_DONE: + pr_debug("%s: mdm done collecting RAM dumps\n", __func__); + get_user(status, (unsigned long __user *) arg); + if (status) + mdm_drv->mdm_ram_dump_status = -EIO; + else { + pr_info("%s: ramdump collection completed\n", __func__); + mdm_drv->mdm_ram_dump_status = 0; + } + complete(&mdm_ram_dumps); + break; + case WAIT_FOR_RESTART: + pr_debug("%s: wait for mdm to need images reloaded\n", + __func__); + ret = wait_for_completion_interruptible(&mdm_needs_reload); + if (!ret) + put_user(mdm_drv->boot_type, + (unsigned long __user *) arg); + INIT_COMPLETION(mdm_needs_reload); + break; + case GET_DLOAD_STATUS: + pr_debug("getting status of mdm2ap_errfatal_gpio\n"); + if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 && + !mdm_drv->mdm_ready) + put_user(1, (unsigned long __user *) arg); + else + put_user(0, (unsigned long __user *) arg); + break; + default: + pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd)); + ret = -EINVAL; + break; + } + + return ret; +} + +static void mdm_status_fn(struct work_struct *work) +{ + int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio); + + pr_debug("%s: status:%d\n", __func__, value); + if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb) + mdm_drv->ops->status_cb(mdm_drv, value); +} + +static DECLARE_WORK(mdm_status_work, mdm_status_fn); + +static void mdm_disable_irqs(void) +{ + disable_irq_nosync(mdm_drv->mdm_errfatal_irq); + disable_irq_nosync(mdm_drv->mdm_status_irq); +} + +static irqreturn_t mdm_errfatal(int irq, void *dev_id) +{ + pr_debug("%s: mdm got errfatal interrupt\n", __func__); + if (mdm_drv->mdm_ready && + (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) { + pr_info("%s: Reseting the mdm due to an errfatal\n", __func__); + mdm_drv->mdm_ready = 0; + subsystem_restart(EXTERNAL_MODEM); + } + return IRQ_HANDLED; +} + +static int mdm_modem_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations mdm_modem_fops = { + .owner = THIS_MODULE, + .open = mdm_modem_open, + .unlocked_ioctl = mdm_modem_ioctl, +}; + + +static struct miscdevice mdm_modem_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mdm", + .fops = &mdm_modem_fops +}; + +static int mdm_panic_prep(struct notifier_block *this, + unsigned long event, void *ptr) +{ + int i; + + pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n", + __func__); + mdm_disable_irqs(); + gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1); + + for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) { + pet_watchdog(); + mdelay(MDM_MODEM_DELTA); + if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) + break; + } + if (i <= 0) { + pr_err("%s: MDM2AP_STATUS never went low\n", __func__); + /* Reset the modem so that it will go into download mode. */ + if (mdm_drv && mdm_drv->ops->reset_mdm_cb) + mdm_drv->ops->reset_mdm_cb(mdm_drv); + } + return NOTIFY_DONE; +} + +static struct notifier_block mdm_panic_blk = { + .notifier_call = mdm_panic_prep, +}; + +static irqreturn_t mdm_status_change(int irq, void *dev_id) +{ + int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio); + + pr_debug("%s: mdm sent status change interrupt\n", __func__); + if (value == 0 && mdm_drv->mdm_ready == 1) { + pr_info("%s: unexpected reset external modem\n", __func__); + mdm_drv->mdm_unexpected_reset_occurred = 1; + mdm_drv->mdm_ready = 0; + subsystem_restart(EXTERNAL_MODEM); + } else if (value == 1) { + pr_info("%s: status = 1: mdm is now ready\n", __func__); + queue_work(mdm_queue, &mdm_status_work); + } + return IRQ_HANDLED; +} + +static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys) +{ + gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1); + if (mdm_drv->pdata->ramdump_delay_ms > 0) { + /* Wait for the external modem to complete + * its preparation for ramdumps. + */ + msleep(mdm_drv->pdata->ramdump_delay_ms); + } + if (!mdm_drv->mdm_unexpected_reset_occurred) + mdm_drv->ops->reset_mdm_cb(mdm_drv); + else + mdm_drv->mdm_unexpected_reset_occurred = 0; + + return 0; +} + +static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys) +{ + gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0); + gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1); + mdm_drv->ops->power_on_mdm_cb(mdm_drv); + mdm_drv->boot_type = CHARM_NORMAL_BOOT; + complete(&mdm_needs_reload); + if (!wait_for_completion_timeout(&mdm_boot, + msecs_to_jiffies(MDM_BOOT_TIMEOUT))) { + mdm_drv->mdm_boot_status = -ETIMEDOUT; + pr_info("%s: mdm modem restart timed out.\n", __func__); + } else { + pr_info("%s: mdm modem has been restarted\n", __func__); + + /* Log the reason for the restart */ + if (mdm_drv->pdata->sfr_query) + queue_work(mdm_sfr_queue, &sfr_reason_work); + } + INIT_COMPLETION(mdm_boot); + return mdm_drv->mdm_boot_status; +} + +static int mdm_subsys_ramdumps(int want_dumps, + const struct subsys_data *crashed_subsys) +{ + mdm_drv->mdm_ram_dump_status = 0; + if (want_dumps) { + mdm_drv->boot_type = CHARM_RAM_DUMPS; + complete(&mdm_needs_reload); + if (!wait_for_completion_timeout(&mdm_ram_dumps, + msecs_to_jiffies(MDM_RDUMP_TIMEOUT))) { + mdm_drv->mdm_ram_dump_status = -ETIMEDOUT; + pr_info("%s: mdm modem ramdumps timed out.\n", + __func__); + } else + pr_info("%s: mdm modem ramdumps completed.\n", + __func__); + INIT_COMPLETION(mdm_ram_dumps); + mdm_drv->ops->power_down_mdm_cb(mdm_drv); + } + return mdm_drv->mdm_ram_dump_status; +} + +static struct subsys_data mdm_subsystem = { + .shutdown = mdm_subsys_shutdown, + .ramdump = mdm_subsys_ramdumps, + .powerup = mdm_subsys_powerup, + .name = EXTERNAL_MODEM, +}; + +static int mdm_debug_on_set(void *data, u64 val) +{ + mdm_debug_on = val; + if (mdm_drv->ops->debug_state_changed_cb) + mdm_drv->ops->debug_state_changed_cb(mdm_debug_on); + return 0; +} + +static int mdm_debug_on_get(void *data, u64 *val) +{ + *val = mdm_debug_on; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops, + mdm_debug_on_get, + mdm_debug_on_set, "%llu\n"); + +static int mdm_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("mdm_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("debug_on", 0644, dent, NULL, + &mdm_debug_on_fops); + return 0; +} + +static void mdm_modem_initialize_data(struct platform_device *pdev, + struct mdm_ops *mdm_ops) +{ + struct resource *pres; + + /* MDM2AP_ERRFATAL */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "MDM2AP_ERRFATAL"); + if (pres) + mdm_drv->mdm2ap_errfatal_gpio = pres->start; + + /* AP2MDM_ERRFATAL */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "AP2MDM_ERRFATAL"); + if (pres) + mdm_drv->ap2mdm_errfatal_gpio = pres->start; + + /* MDM2AP_STATUS */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "MDM2AP_STATUS"); + if (pres) + mdm_drv->mdm2ap_status_gpio = pres->start; + + /* AP2MDM_STATUS */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "AP2MDM_STATUS"); + if (pres) + mdm_drv->ap2mdm_status_gpio = pres->start; + + /* MDM2AP_WAKEUP */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "MDM2AP_WAKEUP"); + if (pres) + mdm_drv->mdm2ap_wakeup_gpio = pres->start; + + /* AP2MDM_WAKEUP */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "AP2MDM_WAKEUP"); + if (pres) + mdm_drv->ap2mdm_wakeup_gpio = pres->start; + + /* AP2MDM_SOFT_RESET */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "AP2MDM_SOFT_RESET"); + if (pres) + mdm_drv->ap2mdm_soft_reset_gpio = pres->start; + + /* AP2MDM_KPDPWR_N */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "AP2MDM_KPDPWR_N"); + if (pres) + mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start; + + /* AP2MDM_PMIC_PWR_EN */ + pres = platform_get_resource_byname(pdev, IORESOURCE_IO, + "AP2MDM_PMIC_PWR_EN"); + if (pres) + mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start; + + mdm_drv->boot_type = CHARM_NORMAL_BOOT; + + mdm_drv->ops = mdm_ops; + mdm_drv->pdata = pdev->dev.platform_data; +} + +int mdm_common_create(struct platform_device *pdev, + struct mdm_ops *p_mdm_cb) +{ + int ret = -1, irq; + + mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL); + if (mdm_drv == NULL) { + pr_err("%s: kzalloc fail.\n", __func__); + goto alloc_err; + } + + mdm_modem_initialize_data(pdev, p_mdm_cb); + if (mdm_drv->ops->debug_state_changed_cb) + mdm_drv->ops->debug_state_changed_cb(mdm_debug_on); + + gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS"); + gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL"); + if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) + gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N"); + gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS"); + gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL"); + + if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0) + gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio, + "AP2MDM_PMIC_PWR_EN"); + if (mdm_drv->ap2mdm_soft_reset_gpio > 0) + gpio_request(mdm_drv->ap2mdm_soft_reset_gpio, + "AP2MDM_SOFT_RESET"); + + if (mdm_drv->ap2mdm_wakeup_gpio > 0) + gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP"); + + gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1); + gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0); + + if (mdm_drv->ap2mdm_wakeup_gpio > 0) + gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0); + + gpio_direction_input(mdm_drv->mdm2ap_status_gpio); + gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio); + + mdm_queue = create_singlethread_workqueue("mdm_queue"); + if (!mdm_queue) { + pr_err("%s: could not create workqueue. All mdm " + "functionality will be disabled\n", + __func__); + ret = -ENOMEM; + goto fatal_err; + } + + mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0); + if (!mdm_sfr_queue) { + pr_err("%s: could not create workqueue mdm_sfr_queue." + " All mdm functionality will be disabled\n", + __func__); + ret = -ENOMEM; + destroy_workqueue(mdm_queue); + goto fatal_err; + } + + atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk); + mdm_debugfs_init(); + + /* Register subsystem handlers */ + ssr_register_subsystem(&mdm_subsystem); + + /* ERR_FATAL irq. */ + irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio); + if (irq < 0) { + pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. " + "error=%d No IRQ will be generated on errfatal.", + __func__, irq); + goto errfatal_err; + } + ret = request_irq(irq, mdm_errfatal, + IRQF_TRIGGER_RISING , "mdm errfatal", NULL); + + if (ret < 0) { + pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d" + ". No IRQ will be generated on errfatal.", + __func__, irq, ret); + goto errfatal_err; + } + mdm_drv->mdm_errfatal_irq = irq; + +errfatal_err: + + /* status irq */ + irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio); + if (irq < 0) { + pr_err("%s: could not get MDM2AP_STATUS IRQ resource. " + "error=%d No IRQ will be generated on status change.", + __func__, irq); + goto status_err; + } + + ret = request_threaded_irq(irq, NULL, mdm_status_change, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, + "mdm status", mdm_drv); + + if (ret < 0) { + pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d" + ". No IRQ will be generated on status change.", + __func__, irq, ret); + goto status_err; + } + mdm_drv->mdm_status_irq = irq; + +status_err: + /* + * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains + * high until the whole phone is shut down. + */ + if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0) + gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1); + + /* Perform early powerup of the external modem in order to + * allow tabla devices to be found. + */ + if (mdm_drv->pdata->early_power_on) + mdm_drv->ops->power_on_mdm_cb(mdm_drv); + + pr_info("%s: Registering mdm modem\n", __func__); + return misc_register(&mdm_modem_misc); + +fatal_err: + gpio_free(mdm_drv->ap2mdm_status_gpio); + gpio_free(mdm_drv->ap2mdm_errfatal_gpio); + if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) + gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio); + if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0) + gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio); + gpio_free(mdm_drv->mdm2ap_status_gpio); + gpio_free(mdm_drv->mdm2ap_errfatal_gpio); + if (mdm_drv->ap2mdm_soft_reset_gpio > 0) + gpio_free(mdm_drv->ap2mdm_soft_reset_gpio); + + if (mdm_drv->ap2mdm_wakeup_gpio > 0) + gpio_free(mdm_drv->ap2mdm_wakeup_gpio); + + kfree(mdm_drv); + ret = -ENODEV; + +alloc_err: + return ret; +} + +int mdm_common_modem_remove(struct platform_device *pdev) +{ + int ret; + + gpio_free(mdm_drv->ap2mdm_status_gpio); + gpio_free(mdm_drv->ap2mdm_errfatal_gpio); + if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) + gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio); + if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0) + gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio); + gpio_free(mdm_drv->mdm2ap_status_gpio); + gpio_free(mdm_drv->mdm2ap_errfatal_gpio); + if (mdm_drv->ap2mdm_soft_reset_gpio > 0) + gpio_free(mdm_drv->ap2mdm_soft_reset_gpio); + + if (mdm_drv->ap2mdm_wakeup_gpio > 0) + gpio_free(mdm_drv->ap2mdm_wakeup_gpio); + + kfree(mdm_drv); + + ret = misc_deregister(&mdm_modem_misc); + return ret; +} + +void mdm_common_modem_shutdown(struct platform_device *pdev) +{ + mdm_disable_irqs(); + + mdm_drv->ops->power_down_mdm_cb(mdm_drv); + if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0) + gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0); +} + diff --git a/arch/arm/mach-msm/mdm_private.h b/arch/arm/mach-msm/mdm_private.h new file mode 100644 index 00000000000..f157d88341f --- /dev/null +++ b/arch/arm/mach-msm/mdm_private.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_MDM_PRIVATE_H +#define _ARCH_ARM_MACH_MSM_MDM_PRIVATE_H + +struct mdm_modem_drv; + +struct mdm_ops { + void (*power_on_mdm_cb)(struct mdm_modem_drv *mdm_drv); + void (*reset_mdm_cb)(struct mdm_modem_drv *mdm_drv); + void (*normal_boot_done_cb)(struct mdm_modem_drv *mdm_drv); + void (*power_down_mdm_cb)(struct mdm_modem_drv *mdm_drv); + void (*debug_state_changed_cb)(int value); + void (*status_cb)(struct mdm_modem_drv *mdm_drv, int value); +}; + +/* Private mdm2 data structure */ +struct mdm_modem_drv { + unsigned mdm2ap_errfatal_gpio; + unsigned ap2mdm_errfatal_gpio; + unsigned mdm2ap_status_gpio; + unsigned ap2mdm_status_gpio; + unsigned mdm2ap_wakeup_gpio; + unsigned ap2mdm_wakeup_gpio; + unsigned ap2mdm_kpdpwr_n_gpio; + unsigned ap2mdm_soft_reset_gpio; + unsigned ap2mdm_pmic_pwr_en_gpio; + + int mdm_errfatal_irq; + int mdm_status_irq; + int mdm_ready; + int mdm_boot_status; + int mdm_ram_dump_status; + enum charm_boot_type boot_type; + int mdm_debug_on; + int mdm_unexpected_reset_occurred; + + struct mdm_ops *ops; + struct mdm_platform_data *pdata; +}; + +int mdm_common_create(struct platform_device *pdev, + struct mdm_ops *mdm_cb); +int mdm_common_modem_remove(struct platform_device *pdev); +void mdm_common_modem_shutdown(struct platform_device *pdev); +void mdm_common_set_debug_state(int value); + +#endif + diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c new file mode 100644 index 00000000000..40845d78c2e --- /dev/null +++ b/arch/arm/mach-msm/memory.c @@ -0,0 +1,441 @@ +/* arch/arm/mach-msm/memory.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_MSM_NPA_REMOTE) +#include "npa_remote.h" +#include +#include +#endif +#include +#include +#include +#include + +/* fixme */ +#include +#include <../../mm/mm.h> +#include + +void *strongly_ordered_page; +char strongly_ordered_mem[PAGE_SIZE*2-4]; + +void map_page_strongly_ordered(void) +{ +#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A) + long unsigned int phys; + struct map_desc map; + + if (strongly_ordered_page) + return; + + strongly_ordered_page = (void*)PFN_ALIGN((int)&strongly_ordered_mem); + phys = __pa(strongly_ordered_page); + + map.pfn = __phys_to_pfn(phys); + map.virtual = MSM_STRONGLY_ORDERED_PAGE; + map.length = PAGE_SIZE; + map.type = MT_DEVICE_STRONGLY_ORDERED; + create_mapping(&map); + + printk(KERN_ALERT "Initialized strongly ordered page successfully\n"); +#endif +} +EXPORT_SYMBOL(map_page_strongly_ordered); + +void write_to_strongly_ordered_memory(void) +{ +#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A) + if (!strongly_ordered_page) { + if (!in_interrupt()) + map_page_strongly_ordered(); + else { + printk(KERN_ALERT "Cannot map strongly ordered page in " + "Interrupt Context\n"); + /* capture it here before the allocation fails later */ + BUG(); + } + } + *(int *)MSM_STRONGLY_ORDERED_PAGE = 0; +#endif +} +EXPORT_SYMBOL(write_to_strongly_ordered_memory); + +/* These cache related routines make the assumption (if outer cache is + * available) that the associated physical memory is contiguous. + * They will operate on all (L1 and L2 if present) caches. + */ +void clean_and_invalidate_caches(unsigned long vstart, + unsigned long length, unsigned long pstart) +{ + dmac_flush_range((void *)vstart, (void *) (vstart + length)); + outer_flush_range(pstart, pstart + length); +} + +void clean_caches(unsigned long vstart, + unsigned long length, unsigned long pstart) +{ + dmac_clean_range((void *)vstart, (void *) (vstart + length)); + outer_clean_range(pstart, pstart + length); +} + +void invalidate_caches(unsigned long vstart, + unsigned long length, unsigned long pstart) +{ + dmac_inv_range((void *)vstart, (void *) (vstart + length)); + outer_inv_range(pstart, pstart + length); +} + +void * __init alloc_bootmem_aligned(unsigned long size, unsigned long alignment) +{ + void *unused_addr = NULL; + unsigned long addr, tmp_size, unused_size; + + /* Allocate maximum size needed, see where it ends up. + * Then free it -- in this path there are no other allocators + * so we can depend on getting the same address back + * when we allocate a smaller piece that is aligned + * at the end (if necessary) and the piece we really want, + * then free the unused first piece. + */ + + tmp_size = size + alignment - PAGE_SIZE; + addr = (unsigned long)alloc_bootmem(tmp_size); + free_bootmem(__pa(addr), tmp_size); + + unused_size = alignment - (addr % alignment); + if (unused_size) + unused_addr = alloc_bootmem(unused_size); + + addr = (unsigned long)alloc_bootmem(size); + if (unused_size) + free_bootmem(__pa(unused_addr), unused_size); + + return (void *)addr; +} + +int (*change_memory_power)(u64, u64, int); + +int platform_physical_remove_pages(u64 start, u64 size) +{ + if (!change_memory_power) + return 0; + return change_memory_power(start, size, MEMORY_DEEP_POWERDOWN); +} + +int platform_physical_active_pages(u64 start, u64 size) +{ + if (!change_memory_power) + return 0; + return change_memory_power(start, size, MEMORY_ACTIVE); +} + +int platform_physical_low_power_pages(u64 start, u64 size) +{ + if (!change_memory_power) + return 0; + return change_memory_power(start, size, MEMORY_SELF_REFRESH); +} + +char *memtype_name[] = { + "SMI_KERNEL", + "SMI", + "EBI0", + "EBI1" +}; + +struct reserve_info *reserve_info; + +static unsigned long stable_size(struct membank *mb, + unsigned long unstable_limit) +{ + unsigned long upper_limit = mb->start + mb->size; + + if (!unstable_limit) + return mb->size; + + /* Check for 32 bit roll-over */ + if (upper_limit >= mb->start) { + /* If we didn't roll over we can safely make the check below */ + if (upper_limit <= unstable_limit) + return mb->size; + } + + if (mb->start >= unstable_limit) + return 0; + return unstable_limit - mb->start; +} + +/* stable size of all memory banks contiguous to and below this one */ +static unsigned long total_stable_size(unsigned long bank) +{ + int i; + struct membank *mb = &meminfo.bank[bank]; + int memtype = reserve_info->paddr_to_memtype(mb->start); + unsigned long size; + + size = stable_size(mb, reserve_info->low_unstable_address); + for (i = bank - 1, mb = &meminfo.bank[bank - 1]; i >= 0; i--, mb--) { + if (mb->start + mb->size != (mb + 1)->start) + break; + if (reserve_info->paddr_to_memtype(mb->start) != memtype) + break; + size += stable_size(mb, reserve_info->low_unstable_address); + } + return size; +} + +static void __init calculate_reserve_limits(void) +{ + int i; + struct membank *mb; + int memtype; + struct memtype_reserve *mt; + unsigned long size; + + for (i = 0, mb = &meminfo.bank[0]; i < meminfo.nr_banks; i++, mb++) { + memtype = reserve_info->paddr_to_memtype(mb->start); + if (memtype == MEMTYPE_NONE) { + pr_warning("unknown memory type for bank at %lx\n", + (long unsigned int)mb->start); + continue; + } + mt = &reserve_info->memtype_reserve_table[memtype]; + size = total_stable_size(i); + mt->limit = max(mt->limit, size); + } +} + +static void __init adjust_reserve_sizes(void) +{ + int i; + struct memtype_reserve *mt; + + mt = &reserve_info->memtype_reserve_table[0]; + for (i = 0; i < MEMTYPE_MAX; i++, mt++) { + if (mt->flags & MEMTYPE_FLAGS_1M_ALIGN) + mt->size = (mt->size + SECTION_SIZE - 1) & SECTION_MASK; + if (mt->size > mt->limit) { + pr_warning("%lx size for %s too large, setting to %lx\n", + mt->size, memtype_name[i], mt->limit); + mt->size = mt->limit; + } + } +} + +static void __init reserve_memory_for_mempools(void) +{ + int i, memtype, membank_type; + struct memtype_reserve *mt; + struct membank *mb; + int ret; + unsigned long size; + + mt = &reserve_info->memtype_reserve_table[0]; + for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) { + if (mt->flags & MEMTYPE_FLAGS_FIXED || !mt->size) + continue; + + /* We know we will find memory bank(s) of the proper size + * as we have limited the size of the memory pool for + * each memory type to the largest total size of the memory + * banks which are contiguous and of the correct memory type. + * Choose the memory bank with the highest physical + * address which is large enough, so that we will not + * take memory from the lowest memory bank which the kernel + * is in (and cause boot problems) and so that we might + * be able to steal memory that would otherwise become + * highmem. However, do not use unstable memory. + */ + for (i = meminfo.nr_banks - 1; i >= 0; i--) { + mb = &meminfo.bank[i]; + membank_type = + reserve_info->paddr_to_memtype(mb->start); + if (memtype != membank_type) + continue; + size = total_stable_size(i); + if (size >= mt->size) { + size = stable_size(mb, + reserve_info->low_unstable_address); + if (!size) + continue; + /* mt->size may be larger than size, all this + * means is that we are carving the memory pool + * out of multiple contiguous memory banks. + */ + mt->start = mb->start + (size - mt->size); + ret = memblock_remove(mt->start, mt->size); + BUG_ON(ret); + break; + } + } + } +} + +static void __init initialize_mempools(void) +{ + struct mem_pool *mpool; + int memtype; + struct memtype_reserve *mt; + + mt = &reserve_info->memtype_reserve_table[0]; + for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) { + if (!mt->size) + continue; + mpool = initialize_memory_pool(mt->start, mt->size, memtype); + if (!mpool) + pr_warning("failed to create %s mempool\n", + memtype_name[memtype]); + } +} + +#define MAX_FIXED_AREA_SIZE 0x11000000 + +void __init msm_reserve(void) +{ + unsigned long msm_fixed_area_size; + unsigned long msm_fixed_area_start; + + memory_pool_init(); + reserve_info->calculate_reserve_sizes(); + + msm_fixed_area_size = reserve_info->fixed_area_size; + msm_fixed_area_start = reserve_info->fixed_area_start; + if (msm_fixed_area_size) + if (msm_fixed_area_start > reserve_info->low_unstable_address + - MAX_FIXED_AREA_SIZE) + reserve_info->low_unstable_address = + msm_fixed_area_start; + + calculate_reserve_limits(); + adjust_reserve_sizes(); + reserve_memory_for_mempools(); + initialize_mempools(); +} + +static int get_ebi_memtype(void) +{ + /* on 7x30 and 8x55 "EBI1 kernel PMEM" is really on EBI0 */ + if (cpu_is_msm7x30() || cpu_is_msm8x55()) + return MEMTYPE_EBI0; + return MEMTYPE_EBI1; +} + +void *allocate_contiguous_ebi(unsigned long size, + unsigned long align, int cached) +{ + return allocate_contiguous_memory(size, get_ebi_memtype(), + align, cached); +} +EXPORT_SYMBOL(allocate_contiguous_ebi); + +unsigned long allocate_contiguous_ebi_nomap(unsigned long size, + unsigned long align) +{ + return _allocate_contiguous_memory_nomap(size, get_ebi_memtype(), + align, __builtin_return_address(0)); +} +EXPORT_SYMBOL(allocate_contiguous_ebi_nomap); + +/* emulation of the deprecated pmem_kalloc and pmem_kfree */ +int32_t pmem_kalloc(const size_t size, const uint32_t flags) +{ + int pmem_memtype; + int memtype = MEMTYPE_NONE; + int ebi1_memtype = MEMTYPE_EBI1; + unsigned int align; + int32_t paddr; + + switch (flags & PMEM_ALIGNMENT_MASK) { + case PMEM_ALIGNMENT_4K: + align = SZ_4K; + break; + case PMEM_ALIGNMENT_1M: + align = SZ_1M; + break; + default: + pr_alert("Invalid alignment %x\n", + (flags & PMEM_ALIGNMENT_MASK)); + return -EINVAL; + } + + /* on 7x30 and 8x55 "EBI1 kernel PMEM" is really on EBI0 */ + if (cpu_is_msm7x30() || cpu_is_msm8x55()) + ebi1_memtype = MEMTYPE_EBI0; + + pmem_memtype = flags & PMEM_MEMTYPE_MASK; + if (pmem_memtype == PMEM_MEMTYPE_EBI1) + memtype = ebi1_memtype; + else if (pmem_memtype == PMEM_MEMTYPE_SMI) + memtype = MEMTYPE_SMI_KERNEL; + else { + pr_alert("Invalid memory type %x\n", + flags & PMEM_MEMTYPE_MASK); + return -EINVAL; + } + + paddr = _allocate_contiguous_memory_nomap(size, memtype, align, + __builtin_return_address(0)); + + if (!paddr && pmem_memtype == PMEM_MEMTYPE_SMI) + paddr = _allocate_contiguous_memory_nomap(size, + ebi1_memtype, align, __builtin_return_address(0)); + + if (!paddr) + return -ENOMEM; + return paddr; +} +EXPORT_SYMBOL(pmem_kalloc); + +int pmem_kfree(const int32_t physaddr) +{ + free_contiguous_memory_by_paddr(physaddr); + + return 0; +} +EXPORT_SYMBOL(pmem_kfree); + +unsigned int msm_ttbr0; + +void store_ttbr0(void) +{ + /* Store TTBR0 for post-mortem debugging purposes. */ + asm("mrc p15, 0, %0, c2, c0, 0\n" + : "=r" (msm_ttbr0)); +} + +int request_fmem_c_region(void *unused) +{ + return fmem_set_state(FMEM_C_STATE); +} + +int release_fmem_c_region(void *unused) +{ + return fmem_set_state(FMEM_T_STATE); +} diff --git a/arch/arm/mach-msm/memory_topology.c b/arch/arm/mach-msm/memory_topology.c new file mode 100644 index 00000000000..70aaf4a7ea3 --- /dev/null +++ b/arch/arm/mach-msm/memory_topology.c @@ -0,0 +1,268 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "smd_private.h" + +#if defined(CONFIG_ARCH_MSM8960) +#include "rpm_resources.h" +#endif + +static struct mem_region_t { + u64 start; + u64 size; + /* reserved for future use */ + u64 num_partitions; + int state; +} mem_regions[MAX_NR_REGIONS]; + +static struct mutex mem_regions_mutex; +static unsigned int nr_mem_regions; +static int mem_regions_mask; + +enum { + STATE_POWER_DOWN = 0x0, + STATE_ACTIVE = 0x2, + STATE_DEFAULT = STATE_ACTIVE +}; + +static int default_mask = ~0x0; + +/* Return the number of chipselects populated with a memory bank */ +/* This is 7x30 only and will be re-implemented in the future */ + +#if defined(CONFIG_ARCH_MSM7X30) +unsigned int get_num_populated_chipselects() +{ + /* Currently, Linux cannot determine the memory toplogy of a target */ + /* This is a kludge until all this info is figured out from smem */ + + /* There is atleast one chipselect populated for hosting the 1st bank */ + unsigned int num_chipselects = 1; + int i; + for (i = 0; i < meminfo.nr_banks; i++) { + struct membank *bank = &meminfo.bank[i]; + if (bank->start == EBI1_PHYS_OFFSET) + num_chipselects++; + } + return num_chipselects; +} +#endif + +#if (defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_MSM8930)) \ + && defined(CONFIG_ENABLE_DMM) +static int rpm_change_memory_state(int retention_mask, + int active_mask) +{ + int ret; + struct msm_rpm_iv_pair cmd[2]; + struct msm_rpm_iv_pair status[2]; + + cmd[0].id = MSM_RPM_ID_DDR_DMM_0; + cmd[1].id = MSM_RPM_ID_DDR_DMM_1; + + status[0].id = MSM_RPM_STATUS_ID_DDR_DMM_0; + status[1].id = MSM_RPM_STATUS_ID_DDR_DMM_1; + + cmd[0].value = retention_mask; + cmd[1].value = active_mask; + + ret = msm_rpm_set(MSM_RPM_CTX_SET_0, cmd, 2); + if (ret < 0) { + pr_err("rpm set failed"); + return -EINVAL; + } + + ret = msm_rpm_get_status(status, 2); + if (ret < 0) { + pr_err("rpm status failed"); + return -EINVAL; + } + if (status[0].value == retention_mask && + status[1].value == active_mask) + return 0; + else { + pr_err("rpm failed to change memory state"); + return -EINVAL; + } +} + +static int switch_memory_state(int mask, int new_state, int start_region, + int end_region) +{ + int final_mask = 0; + int i; + + mutex_lock(&mem_regions_mutex); + + for (i = start_region; i <= end_region; i++) { + if (new_state == mem_regions[i].state) + goto no_change; + /* All region states must be the same to change them */ + if (mem_regions[i].state != mem_regions[start_region].state) + goto no_change; + } + + if (new_state == STATE_POWER_DOWN) + final_mask = mem_regions_mask & mask; + else if (new_state == STATE_ACTIVE) + final_mask = mem_regions_mask | ~mask; + else + goto no_change; + + pr_info("request memory %d to %d state switch (%d->%d)\n", + start_region, end_region, mem_regions[start_region].state, + new_state); + if (rpm_change_memory_state(final_mask, final_mask) == 0) { + for (i = start_region; i <= end_region; i++) + mem_regions[i].state = new_state; + mem_regions_mask = final_mask; + + pr_info("completed memory %d to %d state switch to %d\n", + start_region, end_region, new_state); + mutex_unlock(&mem_regions_mutex); + return 0; + } + + pr_err("failed memory %d to %d state switch (%d->%d)\n", + start_region, end_region, mem_regions[start_region].state, + new_state); + +no_change: + mutex_unlock(&mem_regions_mutex); + return -EINVAL; +} +#else + +static int switch_memory_state(int mask, int new_state, int start_region, + int end_region) +{ + return -EINVAL; +} +#endif + +/* The hotplug code expects the number of bytes that switched state successfully + * as the return value, so a return value of zero indicates an error +*/ +int soc_change_memory_power(u64 start, u64 size, int change) +{ + int i = 0; + int mask = default_mask; + u64 end = start + size; + int start_region = 0; + int end_region = 0; + + if (change != STATE_ACTIVE && change != STATE_POWER_DOWN) { + pr_info("requested state transition invalid\n"); + return 0; + } + /* Find the memory regions that fall within the range */ + for (i = 0; i < nr_mem_regions; i++) { + if (mem_regions[i].start <= start && + mem_regions[i].start >= + mem_regions[start_region].start) { + start_region = i; + } + if (end <= mem_regions[i].start + mem_regions[i].size) { + end_region = i; + break; + } + } + + /* Set the bitmask for each region in the range */ + for (i = start_region; i <= end_region; i++) + mask &= ~(0x1 << i); + + if (!switch_memory_state(mask, change, start_region, end_region)) + return size; + else + return 0; +} + +unsigned int get_num_memory_banks(void) +{ + return nr_mem_regions; +} + +unsigned int get_memory_bank_size(unsigned int id) +{ + BUG_ON(id >= nr_mem_regions); + return mem_regions[id].size; +} + +unsigned int get_memory_bank_start(unsigned int id) +{ + BUG_ON(id >= nr_mem_regions); + return mem_regions[id].start; +} + +int __init meminfo_init(unsigned int type, unsigned int min_bank_size) +{ + unsigned int i, j; + unsigned long bank_size; + unsigned long bank_start; + unsigned long region_size; + struct smem_ram_ptable *ram_ptable; + /* physical memory banks */ + unsigned int nr_mem_banks = 0; + /* logical memory regions for dmm */ + nr_mem_regions = 0; + + ram_ptable = smem_alloc(SMEM_USABLE_RAM_PARTITION_TABLE, + sizeof(struct smem_ram_ptable)); + + if (!ram_ptable) { + pr_err("Could not read ram partition table\n"); + return -EINVAL; + } + + pr_info("meminfo_init: smem ram ptable found: ver: %d len: %d\n", + ram_ptable->version, ram_ptable->len); + + for (i = 0; i < ram_ptable->len; i++) { + /* A bank is valid only if is greater than min_bank_size. If + * non-valid memory (e.g. modem memory) became greater than + * min_bank_size, there is currently no way to differentiate. + */ + if (ram_ptable->parts[i].type == type && + ram_ptable->parts[i].size >= min_bank_size) { + bank_start = ram_ptable->parts[i].start; + bank_size = ram_ptable->parts[i].size; + region_size = bank_size / NR_REGIONS_PER_BANK; + + for (j = 0; j < NR_REGIONS_PER_BANK; j++) { + mem_regions[nr_mem_regions].start = + bank_start; + mem_regions[nr_mem_regions].size = + region_size; + mem_regions[nr_mem_regions].state = + STATE_DEFAULT; + bank_start += region_size; + nr_mem_regions++; + } + nr_mem_banks++; + } + } + mutex_init(&mem_regions_mutex); + mem_regions_mask = default_mask; + pr_info("Found %d memory banks grouped into %d memory regions\n", + nr_mem_banks, nr_mem_regions); + return 0; +} diff --git a/arch/arm/mach-msm/mkrpcsym.pl b/arch/arm/mach-msm/mkrpcsym.pl new file mode 100644 index 00000000000..f4abb5fc36c --- /dev/null +++ b/arch/arm/mach-msm/mkrpcsym.pl @@ -0,0 +1,162 @@ +#!/usr/bin/perl +# +# Generate the smd_rpc_sym.c symbol file for ONCRPC SMEM Logging +# +# Copyright (c) 2009, Code Aurora Forum. 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. +# * Neither the name of Code Aurora Forum, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +# 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. + +use strict; +use POSIX; + +my $base_fn = "smd_rpc_sym"; +my %prog_table; +my ($in, $out) = @ARGV; +my $max_table_size = 1024; + +my $header = <<"EOF"; +/* Autogenerated by mkrpcsym.pl. Do not edit */ +EOF + +sub smd_rpc_gen_files() { + my $c_fp; + my $h_fp; + my @table; + my $tbl_index; + my $num_undefined=0; + + # Process the input hash table into an array + # Any duplicate items will be combined, missing items will + # become "UNKNOWN" We end-up with a fully-qualified table + # from 0 to n. + + $prog_table{"UNDEFINED"}{'name'}="UNDEFINED"; + $prog_table{"UNDEFINED"}{'prog'}=-1; + my $hex_num = 0xFFFF; + foreach my $api_prog (sort {$a cmp $b} keys %prog_table ) { + $tbl_index = hex($api_prog) & hex("0000FFFF"); + if($prog_table{$api_prog}{'prog'} >= 0) { + if($tbl_index < $max_table_size) { + $table[$tbl_index]=$prog_table{$api_prog}; + } else { + print "Skipping table item $tbl_index, larger ", + "than max:$max_table_size \n"; + } + } + } + for (my $i=0; $i<=$#table; $i++) { + if (!exists $table[$i]) { + $table[$i]=$prog_table{"UNDEFINED"}; + $num_undefined++; + } + } + + + open($c_fp, ">", $out) or die $!; + print $c_fp $header; + print $c_fp "\n\n\n"; + print $c_fp <<"EOF"; +#include +#include +#include +#include + +struct sym { + const char *str; +}; + +EOF + +# Each API is named starts with "CB " to allow both the forward and +# callback names of the API to be returned from a common database. +# By convention, program names starting with 0x30 are forward APIS, +# API names starting with 0x31 are callback apis. + print $c_fp "const char *smd_rpc_syms[] = {\n"; + + for (my $i=0; $i<= $#table; $i++) { + my $l = length($table[$i]{'name'}); + my $t = floor((45 - $l - 4)/8); + print $c_fp "\t\"CB ".uc($table[$i]{'name'})."\","; + if($table[$i]{'name'} ne "UNDEFINED") { + for (my $i=0;$i<$t;$i++) { + print $c_fp "\t"; + } + print $c_fp "/*".$table[$i]{'prog'}."*/\n"; + } else { + print $c_fp "\n"; + } + } + + print $c_fp "};\n"; + print $c_fp <<"EOF"; + +static struct sym_tbl { + const char **data; + int size; +} tbl = { smd_rpc_syms, ARRAY_SIZE(smd_rpc_syms)}; + +const char *smd_rpc_get_sym(uint32_t val) +{ + int idx = val & 0xFFFF; + if (idx < tbl.size) { + if (val & 0x01000000) + return tbl.data[idx]; + else + return tbl.data[idx] + 3; + } + return 0; +} +EXPORT_SYMBOL(smd_rpc_get_sym); + +EOF + close $c_fp; +} + +sub read_smd_rpc_table() { + my $fp; + my $line; + open($fp, "<", $in) or die "$! File:$in"; + while ($line = <$fp>) { + chomp($line); + if($line =~ /([^\s]+)\s+([\w]+)$/) { + if(defined $prog_table{$1}) { + print "Error entry already defined $1,", + " in $prog_table{$1}{name} \n"; + } else { + $prog_table{$1}{'name'}=$2; + $prog_table{$1}{'prog'}=$1; + } + } else { + if($line =~ /\w/) { + print "Error parsing error >>$line<< \n"; + } + } + } + close $fp; +} + +read_smd_rpc_table(); +smd_rpc_gen_files(); diff --git a/arch/arm/mach-msm/modem-8660.c b/arch/arm/mach-msm/modem-8660.c new file mode 100644 index 00000000000..0b7b768856a --- /dev/null +++ b/arch/arm/mach-msm/modem-8660.c @@ -0,0 +1,288 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "modem_notifier.h" +#include "ramdump.h" + +#define MODEM_HWIO_MSS_RESET_ADDR 0x00902C48 +#define MODULE_NAME "modem_8660" +#define MODEM_WDOG_ENABLE 0x10020008 +#define MODEM_CLEANUP_DELAY_MS 20 + +#define SUBSYS_FATAL_DEBUG + +#if defined(SUBSYS_FATAL_DEBUG) +static void debug_crash_modem_fn(struct work_struct *); +static int reset_modem; +static int ignore_smsm_ack; + +static DECLARE_DELAYED_WORK(debug_crash_modem_work, + debug_crash_modem_fn); + +module_param(reset_modem, int, 0644); +#endif + +/* Subsystem restart: Modem data, functions */ +static void *modem_ramdump_dev; +static void modem_fatal_fn(struct work_struct *); +static void modem_unlock_timeout(struct work_struct *work); +static int modem_notif_handler(struct notifier_block *this, + unsigned long code, + void *_cmd); +static DECLARE_WORK(modem_fatal_work, modem_fatal_fn); +static DECLARE_DELAYED_WORK(modem_unlock_timeout_work, + modem_unlock_timeout); + +static struct notifier_block modem_notif_nb = { + .notifier_call = modem_notif_handler, +}; + +static void modem_unlock_timeout(struct work_struct *work) +{ + void __iomem *hwio_modem_reset_addr = + ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8); + pr_crit("%s: Timeout waiting for modem to unlock.\n", MODULE_NAME); + + /* Set MSS_MODEM_RESET to 0x0 since the unlock didn't work */ + writel_relaxed(0x0, hwio_modem_reset_addr); + /* Write needs to go through before the modem is restarted. */ + mb(); + iounmap(hwio_modem_reset_addr); + + subsystem_restart("modem"); + enable_irq(MARM_WDOG_EXPIRED); +} + +static void modem_fatal_fn(struct work_struct *work) +{ + uint32_t modem_state; + uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD; + uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR | + SMSM_SYSTEM_PWRDWN_USR; + + pr_err("%s: Watchdog bite received from modem!\n", MODULE_NAME); + + modem_state = smsm_get_state(SMSM_MODEM_STATE); + pr_err("%s: Modem SMSM state = 0x%x!", MODULE_NAME, modem_state); + + if (modem_state == 0 || modem_state & panic_smsm_states) { + + subsystem_restart("modem"); + enable_irq(MARM_WDOG_EXPIRED); + + } else if (modem_state & reset_smsm_states) { + + pr_err("%s: User-invoked system reset/powerdown.", + MODULE_NAME); + kernel_restart(NULL); + + } else { + + int ret; + void *hwio_modem_reset_addr = + ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8); + + pr_err("%s: Modem AHB locked up.\n", MODULE_NAME); + pr_err("%s: Trying to free up modem!\n", MODULE_NAME); + + writel_relaxed(0x3, hwio_modem_reset_addr); + + /* If we are still alive after 6 seconds (allowing for + * the 5-second-delayed-panic-reboot), modem is either + * still wedged or SMSM didn't come through. Force panic + * in that case. + */ + ret = schedule_delayed_work(&modem_unlock_timeout_work, + msecs_to_jiffies(6000)); + + iounmap(hwio_modem_reset_addr); + } +} + +static int modem_notif_handler(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + if (code == MODEM_NOTIFIER_START_RESET) { + if (ignore_smsm_ack) { + ignore_smsm_ack = 0; + goto out; + } + pr_err("%s: Modem error fatal'ed.", MODULE_NAME); + subsystem_restart("modem"); + } +out: + return NOTIFY_DONE; +} + +static int modem_shutdown(const struct subsys_data *crashed_subsys) +{ + void __iomem *modem_wdog_addr; + + /* If the modem didn't already crash, setting SMSM_RESET + * here will help flush caches etc. The ignore_smsm_ack + * flag is set to ignore the SMSM_RESET notification + * that is generated due to the modem settings its own + * SMSM_RESET bit in response to the apps setting the + * apps SMSM_RESET bit. + */ + if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) { + ignore_smsm_ack = 1; + smsm_reset_modem(SMSM_RESET); + } + + /* Disable the modem watchdog to allow clean modem bootup */ + modem_wdog_addr = ioremap_nocache(MODEM_WDOG_ENABLE, 8); + writel_relaxed(0x0, modem_wdog_addr); + + /* + * The write above needs to go through before the modem is + * powered up again (subsystem restart). + */ + mb(); + iounmap(modem_wdog_addr); + + /* Wait here to allow the modem to clean up caches etc. */ + msleep(MODEM_CLEANUP_DELAY_MS); + pil_force_shutdown("modem"); + disable_irq_nosync(MARM_WDOG_EXPIRED); + + + + return 0; +} + +static int modem_powerup(const struct subsys_data *crashed_subsys) +{ + int ret; + + ret = pil_force_boot("modem"); + enable_irq(MARM_WDOG_EXPIRED); + + return ret; +} + +/* FIXME: Get address, size from PIL */ +static struct ramdump_segment modem_segments[] = { + {0x42F00000, 0x46000000 - 0x42F00000} }; + +static int modem_ramdump(int enable, + const struct subsys_data *crashed_subsys) +{ + if (enable) + return do_ramdump(modem_ramdump_dev, modem_segments, + ARRAY_SIZE(modem_segments)); + else + return 0; +} + +static void modem_crash_shutdown( + const struct subsys_data *crashed_subsys) +{ + /* If modem hasn't already crashed, send SMSM_RESET. */ + if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) { + modem_unregister_notifier(&modem_notif_nb); + smsm_reset_modem(SMSM_RESET); + } + + /* Wait to allow the modem to clean up caches etc. */ + mdelay(5); +} + +static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id) +{ + int ret; + + ret = schedule_work(&modem_fatal_work); + disable_irq_nosync(MARM_WDOG_EXPIRED); + + return IRQ_HANDLED; +} + +static struct subsys_data subsys_8660_modem = { + .name = "modem", + .shutdown = modem_shutdown, + .powerup = modem_powerup, + .ramdump = modem_ramdump, + .crash_shutdown = modem_crash_shutdown +}; + +static int __init modem_8660_init(void) +{ + int ret; + + /* Need to listen for SMSM_RESET always */ + modem_register_notifier(&modem_notif_nb); + +#if defined(SUBSYS_FATAL_DEBUG) + schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(5000)); +#endif + + ret = request_irq(MARM_WDOG_EXPIRED, modem_wdog_bite_irq, + IRQF_TRIGGER_RISING, "modem_wdog", NULL); + + if (ret < 0) { + pr_err("%s: Unable to request MARM_WDOG_EXPIRED irq.", + __func__); + goto out; + } + + modem_ramdump_dev = create_ramdump_device("modem"); + + if (!modem_ramdump_dev) { + ret = -ENOMEM; + goto out; + } + + ret = ssr_register_subsystem(&subsys_8660_modem); +out: + return ret; +} + +static void __exit modem_8660_exit(void) +{ + free_irq(MARM_WDOG_EXPIRED, NULL); +} + +#ifdef SUBSYS_FATAL_DEBUG +static void debug_crash_modem_fn(struct work_struct *work) +{ + if (reset_modem == 1) + smsm_reset_modem(SMSM_RESET); + else if (reset_modem == 2) + subsystem_restart("lpass"); + + reset_modem = 0; + schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(1000)); +} +#endif + +module_init(modem_8660_init); +module_exit(modem_8660_exit); + diff --git a/arch/arm/mach-msm/modem-8960.c b/arch/arm/mach-msm/modem-8960.c new file mode 100644 index 00000000000..5d02bda3b46 --- /dev/null +++ b/arch/arm/mach-msm/modem-8960.c @@ -0,0 +1,355 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "modem_notifier.h" +#include "ramdump.h" + +static int crash_shutdown; + +#define MAX_SSR_REASON_LEN 81U +#define Q6_FW_WDOG_ENABLE 0x08882024 +#define Q6_SW_WDOG_ENABLE 0x08982024 + +static void log_modem_sfr(void) +{ + u32 size; + char *smem_reason, reason[MAX_SSR_REASON_LEN]; + + smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size); + if (!smem_reason || !size) { + pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n"); + return; + } + if (!smem_reason[0]) { + pr_err("modem subsystem failure reason: (unknown, init string found).\n"); + return; + } + + size = min(size, MAX_SSR_REASON_LEN-1); + memcpy(reason, smem_reason, size); + reason[size] = '\0'; + pr_err("modem subsystem failure reason: %s.\n", reason); + + smem_reason[0] = '\0'; + wmb(); +} + +static void restart_modem(void) +{ + log_modem_sfr(); + subsystem_restart("modem"); +} + +static void modem_wdog_check(struct work_struct *work) +{ + void __iomem *q6_sw_wdog_addr; + u32 regval; + + q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4); + if (!q6_sw_wdog_addr) + panic("Unable to check modem watchdog status.\n"); + + regval = readl_relaxed(q6_sw_wdog_addr); + if (!regval) { + pr_err("modem-8960: Modem watchdog wasn't activated!. Restarting the modem now.\n"); + restart_modem(); + } + + iounmap(q6_sw_wdog_addr); +} + +static DECLARE_DELAYED_WORK(modem_wdog_check_work, modem_wdog_check); + +static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state) +{ + /* Ignore if we're the one that set SMSM_RESET */ + if (crash_shutdown) + return; + + if (new_state & SMSM_RESET) { + pr_err("Probable fatal error on the modem.\n"); + restart_modem(); + } +} + +static int modem_shutdown(const struct subsys_data *subsys) +{ + void __iomem *q6_fw_wdog_addr; + void __iomem *q6_sw_wdog_addr; + + /* + * Cancel any pending wdog_check work items, since we're shutting + * down anyway. + */ + cancel_delayed_work(&modem_wdog_check_work); + + /* + * Disable the modem watchdog since it keeps running even after the + * modem is shutdown. + */ + q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4); + if (!q6_fw_wdog_addr) + return -ENOMEM; + + q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4); + if (!q6_sw_wdog_addr) { + iounmap(q6_fw_wdog_addr); + return -ENOMEM; + } + + writel_relaxed(0x0, q6_fw_wdog_addr); + writel_relaxed(0x0, q6_sw_wdog_addr); + mb(); + iounmap(q6_sw_wdog_addr); + iounmap(q6_fw_wdog_addr); + + pil_force_shutdown("modem"); + pil_force_shutdown("modem_fw"); + disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ); + disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ); + + return 0; +} + +#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000 + +static int modem_powerup(const struct subsys_data *subsys) +{ + pil_force_boot("modem_fw"); + pil_force_boot("modem"); + enable_irq(Q6FW_WDOG_EXPIRED_IRQ); + enable_irq(Q6SW_WDOG_EXPIRED_IRQ); + schedule_delayed_work(&modem_wdog_check_work, + msecs_to_jiffies(MODEM_WDOG_CHECK_TIMEOUT_MS)); + return 0; +} + +void modem_crash_shutdown(const struct subsys_data *subsys) +{ + crash_shutdown = 1; + smsm_reset_modem(SMSM_RESET); +} + +/* FIXME: Get address, size from PIL */ +static struct ramdump_segment modemsw_segments[] = { + {0x89000000, 0x8D400000 - 0x89000000}, +}; + +static struct ramdump_segment modemfw_segments[] = { + {0x8D400000, 0x8DA00000 - 0x8D400000}, +}; + +static struct ramdump_segment smem_segments[] = { + {0x80000000, 0x00200000}, +}; + +static void *modemfw_ramdump_dev; +static void *modemsw_ramdump_dev; +static void *smem_ramdump_dev; + +static int modem_ramdump(int enable, + const struct subsys_data *crashed_subsys) +{ + int ret = 0; + + if (enable) { + ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments, + ARRAY_SIZE(modemsw_segments)); + + if (ret < 0) { + pr_err("Unable to dump modem sw memory (rc = %d).\n", + ret); + goto out; + } + + ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments, + ARRAY_SIZE(modemfw_segments)); + + if (ret < 0) { + pr_err("Unable to dump modem fw memory (rc = %d).\n", + ret); + goto out; + } + + ret = do_ramdump(smem_ramdump_dev, smem_segments, + ARRAY_SIZE(smem_segments)); + + if (ret < 0) { + pr_err("Unable to dump smem memory (rc = %d).\n", ret); + goto out; + } + } + +out: + return ret; +} + +static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id) +{ + switch (irq) { + + case Q6SW_WDOG_EXPIRED_IRQ: + pr_err("Watchdog bite received from modem software!\n"); + restart_modem(); + break; + case Q6FW_WDOG_EXPIRED_IRQ: + pr_err("Watchdog bite received from modem firmware!\n"); + restart_modem(); + break; + break; + + default: + pr_err("%s: Unknown IRQ!\n", __func__); + } + + return IRQ_HANDLED; +} + +static struct subsys_data modem_8960 = { + .name = "modem", + .shutdown = modem_shutdown, + .powerup = modem_powerup, + .ramdump = modem_ramdump, + .crash_shutdown = modem_crash_shutdown +}; + +static int modem_subsystem_restart_init(void) +{ + return ssr_register_subsystem(&modem_8960); +} + +static int modem_debug_set(void *data, u64 val) +{ + if (val == 1) + subsystem_restart("modem"); + + return 0; +} + +static int modem_debug_get(void *data, u64 *val) +{ + *val = 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set, + "%llu\n"); + +static int modem_debugfs_init(void) +{ + struct dentry *dent; + dent = debugfs_create_dir("modem_debug", 0); + + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("reset_modem", 0644, dent, NULL, + &modem_debug_fops); + return 0; +} + +static int __init modem_8960_init(void) +{ + int ret; + + if (!cpu_is_msm8960() && !cpu_is_msm8930() && !cpu_is_msm9615()) + return -ENODEV; + + ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET, + smsm_state_cb, 0); + + if (ret < 0) + pr_err("%s: Unable to register SMSM callback! (%d)\n", + __func__, ret); + + ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq, + IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL); + + if (ret < 0) { + pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n", + __func__, ret); + goto out; + } + + ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq, + IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL); + + if (ret < 0) { + pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n", + __func__, ret); + disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ); + goto out; + } + + ret = modem_subsystem_restart_init(); + + if (ret < 0) { + pr_err("%s: Unable to reg with subsystem restart. (%d)\n", + __func__, ret); + goto out; + } + + modemfw_ramdump_dev = create_ramdump_device("modem_fw"); + + if (!modemfw_ramdump_dev) { + pr_err("%s: Unable to create modem fw ramdump device. (%d)\n", + __func__, -ENOMEM); + ret = -ENOMEM; + goto out; + } + + modemsw_ramdump_dev = create_ramdump_device("modem_sw"); + + if (!modemsw_ramdump_dev) { + pr_err("%s: Unable to create modem sw ramdump device. (%d)\n", + __func__, -ENOMEM); + ret = -ENOMEM; + goto out; + } + + smem_ramdump_dev = create_ramdump_device("smem"); + + if (!smem_ramdump_dev) { + pr_err("%s: Unable to create smem ramdump device. (%d)\n", + __func__, -ENOMEM); + ret = -ENOMEM; + goto out; + } + + ret = modem_debugfs_init(); + + pr_info("%s: modem fatal driver init'ed.\n", __func__); +out: + return ret; +} + +module_init(modem_8960_init); diff --git a/arch/arm/mach-msm/modem_notifier.c b/arch/arm/mach-msm/modem_notifier.c new file mode 100644 index 00000000000..d92098b6948 --- /dev/null +++ b/arch/arm/mach-msm/modem_notifier.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Modem Restart Notifier -- Provides notification + * of modem restart events. + */ + +#include +#include +#include +#include +#include + +#include "modem_notifier.h" + +#define DEBUG + +static struct srcu_notifier_head modem_notifier_list; +static struct workqueue_struct *modem_notifier_wq; + +static void notify_work_smsm_init(struct work_struct *work) +{ + modem_notify(0, MODEM_NOTIFIER_SMSM_INIT); +} +static DECLARE_WORK(modem_notifier_smsm_init_work, ¬ify_work_smsm_init); + +void modem_queue_smsm_init_notify(void) +{ + int ret; + + ret = queue_work(modem_notifier_wq, &modem_notifier_smsm_init_work); + + if (!ret) + printk(KERN_ERR "%s\n", __func__); +} +EXPORT_SYMBOL(modem_queue_smsm_init_notify); + +static void notify_work_start_reset(struct work_struct *work) +{ + modem_notify(0, MODEM_NOTIFIER_START_RESET); +} +static DECLARE_WORK(modem_notifier_start_reset_work, ¬ify_work_start_reset); + +void modem_queue_start_reset_notify(void) +{ + int ret; + + ret = queue_work(modem_notifier_wq, &modem_notifier_start_reset_work); + + if (!ret) + printk(KERN_ERR "%s\n", __func__); +} +EXPORT_SYMBOL(modem_queue_start_reset_notify); + +static void notify_work_end_reset(struct work_struct *work) +{ + modem_notify(0, MODEM_NOTIFIER_END_RESET); +} +static DECLARE_WORK(modem_notifier_end_reset_work, ¬ify_work_end_reset); + +void modem_queue_end_reset_notify(void) +{ + int ret; + + ret = queue_work(modem_notifier_wq, &modem_notifier_end_reset_work); + + if (!ret) + printk(KERN_ERR "%s\n", __func__); +} +EXPORT_SYMBOL(modem_queue_end_reset_notify); + +int modem_register_notifier(struct notifier_block *nb) +{ + int ret; + + ret = srcu_notifier_chain_register( + &modem_notifier_list, nb); + + return ret; +} +EXPORT_SYMBOL(modem_register_notifier); + +int modem_unregister_notifier(struct notifier_block *nb) +{ + int ret; + + ret = srcu_notifier_chain_unregister( + &modem_notifier_list, nb); + + return ret; +} +EXPORT_SYMBOL(modem_unregister_notifier); + +void modem_notify(void *data, unsigned int state) +{ + srcu_notifier_call_chain(&modem_notifier_list, state, data); +} +EXPORT_SYMBOL(modem_notify); + +#if defined(CONFIG_DEBUG_FS) +static int debug_reset_start(const char __user *buf, int count) +{ + modem_queue_start_reset_notify(); + return 0; +} + +static int debug_reset_end(const char __user *buf, int count) +{ + modem_queue_end_reset_notify(); + return 0; +} + +static ssize_t debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fling)(const char __user *buf, int max) = file->private_data; + fling(buf, count); + return count; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .write = debug_write, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fling)(const char __user *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fling, &debug_ops); +} + +static void modem_notifier_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("modem_notifier", 0); + if (IS_ERR(dent)) + return; + + debug_create("reset_start", 0444, dent, debug_reset_start); + debug_create("reset_end", 0444, dent, debug_reset_end); +} +#else +static void modem_notifier_debugfs_init(void) {} +#endif + +#if defined(DEBUG) +static int modem_notifier_test_call(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + switch (code) { + case MODEM_NOTIFIER_START_RESET: + printk(KERN_ERR "Notify: start reset\n"); + break; + case MODEM_NOTIFIER_END_RESET: + printk(KERN_ERR "Notify: end reset\n"); + break; + case MODEM_NOTIFIER_SMSM_INIT: + printk(KERN_ERR "Notify: smsm init\n"); + break; + default: + printk(KERN_ERR "Notify: general\n"); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block nb = { + .notifier_call = modem_notifier_test_call, +}; + +static void register_test_notifier(void) +{ + modem_register_notifier(&nb); +} +#endif + +static int __init init_modem_notifier_list(void) +{ + srcu_init_notifier_head(&modem_notifier_list); + modem_notifier_debugfs_init(); +#if defined(DEBUG) + register_test_notifier(); +#endif + + /* Create the workqueue */ + modem_notifier_wq = create_singlethread_workqueue("modem_notifier"); + if (!modem_notifier_wq) { + srcu_cleanup_notifier_head(&modem_notifier_list); + return -ENOMEM; + } + + return 0; +} +module_init(init_modem_notifier_list); diff --git a/arch/arm/mach-msm/modem_notifier.h b/arch/arm/mach-msm/modem_notifier.h new file mode 100644 index 00000000000..1bd2d6d9580 --- /dev/null +++ b/arch/arm/mach-msm/modem_notifier.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Modem Restart Notifier API + * + */ + +#ifndef _MODEM_NOTIFIER_H +#define _MODEM_NOTIFIER_H + +#include + +#define MODEM_NOTIFIER_START_RESET 0x1 +#define MODEM_NOTIFIER_END_RESET 0x2 +#define MODEM_NOTIFIER_SMSM_INIT 0x3 + +extern int modem_register_notifier(struct notifier_block *nb); +extern int modem_unregister_notifier(struct notifier_block *nb); +extern void modem_notify(void *data, unsigned int state); +extern void modem_queue_start_reset_notify(void); +extern void modem_queue_end_reset_notify(void); +extern void modem_queue_smsm_init_notify(void); + +#endif /* _MODEM_NOTIFIER_H */ diff --git a/arch/arm/mach-msm/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c new file mode 100644 index 00000000000..fa966d2570b --- /dev/null +++ b/arch/arm/mach-msm/mpm-8625.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mpm-8625.h" + +#define NUM_REGS_ENABLE 2 +/* (NR_MSM_IRQS/32) 96 max irqs supported */ +#define NUM_REGS_DISABLE 3 +#define GIC_IRQ_MASK(irq) BIT(irq % 32) +#define GIC_IRQ_INDEX(irq) (irq / 32) + +enum { + IRQ_DEBUG_SLEEP_INT_TRIGGER = BIT(0), + IRQ_DEBUG_SLEEP_INT = BIT(1), + IRQ_DEBUG_SLEEP_ABORT = BIT(2), + IRQ_DEBUG_SLEEP = BIT(3), + IRQ_DEBUG_SLEEP_REQUEST = BIT(4) +}; + +static int msm_gic_irq_debug_mask; +module_param_named(debug_mask, msm_gic_irq_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +static uint32_t msm_gic_irq_smsm_wake_enable[NUM_REGS_ENABLE]; +static uint32_t msm_gic_irq_idle_disable[NUM_REGS_DISABLE]; + + /* + * Some of the interrupts which will not be considered as wake capable + * should be marked as FAKE. + * Interrupts: GPIO, Timers etc.. + */ +#define SMSM_FAKE_IRQ (0xff) + + /* msm_gic_irq_to_smsm: IRQ's those will be monitored by Modem */ +static uint8_t msm_gic_irq_to_smsm[NR_IRQS] = { + [MSM8625_INT_USB_OTG] = 4, + [MSM8625_INT_PWB_I2C] = 5, + [MSM8625_INT_SDC1_0] = 6, + [MSM8625_INT_SDC1_1] = 7, + [MSM8625_INT_SDC2_0] = 8, + [MSM8625_INT_SDC2_1] = 9, + [MSM8625_INT_ADSP_A9_A11] = 10, + [MSM8625_INT_UART1] = 11, + [MSM8625_INT_UART2] = 12, + [MSM8625_INT_UART3] = 13, + [MSM8625_INT_UART1_RX] = 14, + [MSM8625_INT_UART2_RX] = 15, + [MSM8625_INT_UART3_RX] = 16, + [MSM8625_INT_UART1DM_IRQ] = 17, + [MSM8625_INT_UART1DM_RX] = 18, + [MSM8625_INT_KEYSENSE] = 19, + [MSM8625_INT_AD_HSSD] = 20, + [MSM8625_INT_NAND_WR_ER_DONE] = 21, + [MSM8625_INT_NAND_OP_DONE] = 22, + [MSM8625_INT_TCHSCRN1] = 23, + [MSM8625_INT_TCHSCRN2] = 24, + [MSM8625_INT_TCHSCRN_SSBI] = 25, + [MSM8625_INT_USB_HS] = 26, + [MSM8625_INT_UART2DM_RX] = 27, + [MSM8625_INT_UART2DM_IRQ] = 28, + [MSM8625_INT_SDC4_1] = 29, + [MSM8625_INT_SDC4_0] = 30, + [MSM8625_INT_SDC3_1] = 31, + [MSM8625_INT_SDC3_0] = 32, + + /* fake wakeup interrupts */ + [MSM8625_INT_GPIO_GROUP1] = SMSM_FAKE_IRQ, + [MSM8625_INT_GPIO_GROUP2] = SMSM_FAKE_IRQ, + [MSM8625_INT_A9_M2A_0] = SMSM_FAKE_IRQ, + [MSM8625_INT_A9_M2A_1] = SMSM_FAKE_IRQ, + [MSM8625_INT_A9_M2A_5] = SMSM_FAKE_IRQ, + [MSM8625_INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, + [MSM8625_INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, + [MSM8625_INT_ADSP_A11] = SMSM_FAKE_IRQ, +}; + +static void msm_gic_mask_irq(struct irq_data *d) +{ + unsigned int index = GIC_IRQ_INDEX(d->irq); + uint32_t mask; + int smsm_irq = msm_gic_irq_to_smsm[d->irq]; + + mask = GIC_IRQ_MASK(d->irq); + + if (smsm_irq == 0) { + msm_gic_irq_idle_disable[index] &= ~mask; + } else { + mask = GIC_IRQ_MASK(smsm_irq - 1); + msm_gic_irq_smsm_wake_enable[0] &= ~mask; + } +} + +static void msm_gic_unmask_irq(struct irq_data *d) +{ + unsigned int index = GIC_IRQ_INDEX(d->irq); + uint32_t mask; + int smsm_irq = msm_gic_irq_to_smsm[d->irq]; + + mask = GIC_IRQ_MASK(d->irq); + + if (smsm_irq == 0) { + msm_gic_irq_idle_disable[index] |= mask; + } else { + mask = GIC_IRQ_MASK(smsm_irq - 1); + msm_gic_irq_smsm_wake_enable[0] |= mask; + } +} + +static int msm_gic_set_irq_wake(struct irq_data *d, unsigned int on) +{ + uint32_t mask; + int smsm_irq = msm_gic_irq_to_smsm[d->irq]; + + if (smsm_irq == 0) { + pr_err("bad wake up irq %d\n", d->irq); + return -EINVAL; + } + + if (smsm_irq == SMSM_FAKE_IRQ) + return 0; + + mask = GIC_IRQ_MASK(smsm_irq - 1); + if (on) + msm_gic_irq_smsm_wake_enable[1] |= mask; + else + msm_gic_irq_smsm_wake_enable[1] &= ~mask; + + return 0; +} + +void __init msm_gic_irq_extn_init(void __iomem *db, void __iomem *cb) +{ + gic_arch_extn.irq_mask = msm_gic_mask_irq; + gic_arch_extn.irq_unmask = msm_gic_unmask_irq; + gic_arch_extn.irq_disable = msm_gic_mask_irq; + gic_arch_extn.irq_set_wake = msm_gic_set_irq_wake; +} + +/* Power APIs */ + + /* + * Iterate over the disable list + */ + +int msm_gic_irq_idle_sleep_allowed(void) +{ + uint32_t i, disable = 0; + + for (i = 0; i < NUM_REGS_DISABLE; i++) + disable |= msm_gic_irq_idle_disable[i]; + + return !disable; +} + + /* + * Prepare interrupt subsystem for entering sleep -- phase 1 + * If modem_wake is true, return currently enabled interrupt + * mask in *irq_mask + */ +void msm_gic_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t + *irq_mask) +{ + if (modem_wake) { + *irq_mask = msm_gic_irq_smsm_wake_enable[!from_idle]; + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP) + pr_info("%s irq_mask %x\n", __func__, *irq_mask); + } +} + + /* + * Prepare interrupt susbsytem for entering sleep -- phase 2 + * Detect any pending interrupts and configure interrupt hardware. + * Return value: + * -EAGAIN: there are pending interrupt(s); interrupt configuration is not + * changed + * 0: Success + */ +int msm_gic_irq_enter_sleep2(bool modem_wake, int from_idle) +{ + if (from_idle && !modem_wake) + return 0; + + /* edge triggered interrupt may get lost if this mode is used */ + WARN_ON_ONCE(!modem_wake && !from_idle); + + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP) + pr_info("%s interrupts pending\n", __func__); + + /* check the pending interrupts */ + if (msm_gic_spi_ppi_pending()) { + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) + pr_info("%s aborted....\n", __func__); + return -EAGAIN; + } + + if (modem_wake) { + /* save the contents of GIC CPU interface and Distributor + * Disable all the Interrupts, if we enter from idle pc + */ + msm_gic_save(modem_wake, from_idle); + irq_set_irq_type(MSM8625_INT_A9_M2A_6, IRQF_TRIGGER_RISING); + enable_irq(MSM8625_INT_A9_M2A_6); + pr_debug("%s going for sleep now\n", __func__); + } + + return 0; +} + + /* + * Restore interrupt subsystem from sleep -- phase 1 + * Configure the interrupt hardware. + */ +void msm_gic_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs) +{ + /* Restore GIC contents, which were saved */ + msm_gic_restore(); + + /* Disable A9_M2A_6 */ + disable_irq(MSM8625_INT_A9_M2A_6); + + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP) + pr_info("%s %x %x %x now\n", __func__, irq_mask, + pending_irqs, wakeup_reason); +} + + /* + * Restore interrupt subsystem from sleep -- phase 2 + * Poke the specified pending interrupts into interrupt hardware. + */ +void msm_gic_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending) +{ + int i, smsm_irq, smsm_mask; + struct irq_desc *desc; + + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP) + pr_info("%s %x %x %x now\n", __func__, irq_mask, + pending, wakeup_reason); + + for (i = 0; pending && i < ARRAY_SIZE(msm_gic_irq_to_smsm); i++) { + smsm_irq = msm_gic_irq_to_smsm[i]; + + if (smsm_irq == 0) + continue; + + smsm_mask = BIT(smsm_irq - 1); + if (!(pending & smsm_mask)) + continue; + + pending &= ~smsm_mask; + + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + pr_info("%s, irq %d, still pending %x now\n", + __func__, i, pending); + /* Peding IRQ */ + desc = i ? irq_to_desc(i) : NULL; + + /* Check if the pending */ + if (desc && !irqd_is_level_type(&desc->irq_data)) { + /* Mark the IRQ as pending, if not Level */ + irq_set_pending(i); + check_irq_resend(desc, i); + } + } +} + + /* + * Restore interrupt subsystem from sleep -- phase 3 + * Print debug information + */ +void msm_gic_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs) +{ + if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP) + pr_info("%s, irq_mask %x pending_irqs %x, wakeup_reason %x," + "state %x now\n", __func__, irq_mask, + pending_irqs, wakeup_reason, + smsm_get_state(SMSM_MODEM_STATE)); +} diff --git a/arch/arm/mach-msm/mpm-8625.h b/arch/arm/mach-msm/mpm-8625.h new file mode 100644 index 00000000000..4ada9e2560b --- /dev/null +++ b/arch/arm/mach-msm/mpm-8625.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_MPM_8625_H_ +#define _ARCH_ARM_MACH_MSM_MPM_8625_H_ + +void msm_gic_irq_extn_init(void __iomem *, void __iomem *); + +unsigned int msm_gic_spi_ppi_pending(void); +int msm_gic_irq_idle_sleep_allowed(void); +void msm_gic_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t + *irq_mask); +int msm_gic_irq_enter_sleep2(bool modem_wake, int from_idle); +void msm_gic_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs); +void msm_gic_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending); +void msm_gic_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs); +#endif diff --git a/arch/arm/mach-msm/mpm.c b/arch/arm/mach-msm/mpm.c new file mode 100644 index 00000000000..b395b61349c --- /dev/null +++ b/arch/arm/mach-msm/mpm.c @@ -0,0 +1,546 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/****************************************************************************** + * Debug Definitions + *****************************************************************************/ + +enum { + MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0), + MSM_MPM_DEBUG_PENDING_IRQ = BIT(1), + MSM_MPM_DEBUG_WRITE = BIT(2), + MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3), +}; + +static int msm_mpm_debug_mask = 1; +module_param_named( + debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + +/****************************************************************************** + * Request and Status Definitions + *****************************************************************************/ + +enum { + MSM_MPM_REQUEST_REG_ENABLE, + MSM_MPM_REQUEST_REG_DETECT_CTL, + MSM_MPM_REQUEST_REG_POLARITY, + MSM_MPM_REQUEST_REG_CLEAR, +}; + +enum { + MSM_MPM_STATUS_REG_PENDING, +}; + +/****************************************************************************** + * IRQ Mapping Definitions + *****************************************************************************/ + +#define MSM_MPM_NR_APPS_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS) + +#define MSM_MPM_REG_WIDTH DIV_ROUND_UP(MSM_MPM_NR_MPM_IRQS, 32) +#define MSM_MPM_IRQ_INDEX(irq) (irq / 32) +#define MSM_MPM_IRQ_MASK(irq) BIT(irq % 32) + +static struct msm_mpm_device_data msm_mpm_dev_data; +static uint8_t msm_mpm_irqs_a2m[MSM_MPM_NR_APPS_IRQS]; + +static DEFINE_SPINLOCK(msm_mpm_lock); + +/* + * Note: the following two bitmaps only mark irqs that are _not_ + * mappable to MPM. + */ +static DECLARE_BITMAP(msm_mpm_enabled_apps_irqs, MSM_MPM_NR_APPS_IRQS); +static DECLARE_BITMAP(msm_mpm_wake_apps_irqs, MSM_MPM_NR_APPS_IRQS); + +static DECLARE_BITMAP(msm_mpm_gpio_irqs_mask, MSM_MPM_NR_APPS_IRQS); + +static uint32_t msm_mpm_enabled_irq[MSM_MPM_REG_WIDTH]; +static uint32_t msm_mpm_wake_irq[MSM_MPM_REG_WIDTH]; +static uint32_t msm_mpm_detect_ctl[MSM_MPM_REG_WIDTH]; +static uint32_t msm_mpm_polarity[MSM_MPM_REG_WIDTH]; + + +/****************************************************************************** + * Low Level Functions for Accessing MPM + *****************************************************************************/ + +static inline uint32_t msm_mpm_read( + unsigned int reg, unsigned int subreg_index) +{ + unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index; + return __raw_readl(msm_mpm_dev_data.mpm_status_reg_base + offset * 4); +} + +static inline void msm_mpm_write( + unsigned int reg, unsigned int subreg_index, uint32_t value) +{ + unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index; + __raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4); + + if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask) + pr_info("%s: reg %u.%u: 0x%08x\n", + __func__, reg, subreg_index, value); +} + +static inline void msm_mpm_send_interrupt(void) +{ + __raw_writel(msm_mpm_dev_data.mpm_apps_ipc_val, + msm_mpm_dev_data.mpm_apps_ipc_reg); + /* Ensure the write is complete before returning. */ + mb(); +} + +static irqreturn_t msm_mpm_irq(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +/****************************************************************************** + * MPM Access Functions + *****************************************************************************/ + +static void msm_mpm_set(bool wakeset) +{ + uint32_t *irqs; + unsigned int reg; + int i; + + irqs = wakeset ? msm_mpm_wake_irq : msm_mpm_enabled_irq; + for (i = 0; i < MSM_MPM_REG_WIDTH; i++) { + reg = MSM_MPM_REQUEST_REG_ENABLE; + msm_mpm_write(reg, i, irqs[i]); + + reg = MSM_MPM_REQUEST_REG_DETECT_CTL; + msm_mpm_write(reg, i, msm_mpm_detect_ctl[i]); + + reg = MSM_MPM_REQUEST_REG_POLARITY; + msm_mpm_write(reg, i, msm_mpm_polarity[i]); + + reg = MSM_MPM_REQUEST_REG_CLEAR; + msm_mpm_write(reg, i, 0xffffffff); + } + + /* Ensure that the set operation is complete before sending the + * interrupt + */ + mb(); + msm_mpm_send_interrupt(); +} + +static void msm_mpm_clear(void) +{ + int i; + + for (i = 0; i < MSM_MPM_REG_WIDTH; i++) { + msm_mpm_write(MSM_MPM_REQUEST_REG_ENABLE, i, 0); + msm_mpm_write(MSM_MPM_REQUEST_REG_CLEAR, i, 0xffffffff); + } + + /* Ensure the clear is complete before sending the interrupt */ + mb(); + msm_mpm_send_interrupt(); +} + +/****************************************************************************** + * Interrupt Mapping Functions + *****************************************************************************/ + +static inline bool msm_mpm_is_valid_apps_irq(unsigned int irq) +{ + return irq < ARRAY_SIZE(msm_mpm_irqs_a2m); +} + +static inline uint8_t msm_mpm_get_irq_a2m(unsigned int irq) +{ + return msm_mpm_irqs_a2m[irq]; +} + +static inline void msm_mpm_set_irq_a2m(unsigned int apps_irq, + unsigned int mpm_irq) +{ + msm_mpm_irqs_a2m[apps_irq] = (uint8_t) mpm_irq; +} + +static inline bool msm_mpm_is_valid_mpm_irq(unsigned int irq) +{ + return irq < msm_mpm_dev_data.irqs_m2a_size; +} + +static inline uint16_t msm_mpm_get_irq_m2a(unsigned int irq) +{ + return msm_mpm_dev_data.irqs_m2a[irq]; +} + +static bool msm_mpm_bypass_apps_irq(unsigned int irq) +{ + int i; + + for (i = 0; i < msm_mpm_dev_data.bypassed_apps_irqs_size; i++) + if (irq == msm_mpm_dev_data.bypassed_apps_irqs[i]) + return true; + + return false; +} + +static int msm_mpm_enable_irq_exclusive( + unsigned int irq, bool enable, bool wakeset) +{ + uint32_t mpm_irq; + + if (!msm_mpm_is_valid_apps_irq(irq)) + return -EINVAL; + + if (msm_mpm_bypass_apps_irq(irq)) + return 0; + + mpm_irq = msm_mpm_get_irq_a2m(irq); + if (mpm_irq) { + uint32_t *mpm_irq_masks = wakeset ? + msm_mpm_wake_irq : msm_mpm_enabled_irq; + uint32_t index = MSM_MPM_IRQ_INDEX(mpm_irq); + uint32_t mask = MSM_MPM_IRQ_MASK(mpm_irq); + + if (enable) + mpm_irq_masks[index] |= mask; + else + mpm_irq_masks[index] &= ~mask; + } else { + unsigned long *apps_irq_bitmap = wakeset ? + msm_mpm_wake_apps_irqs : msm_mpm_enabled_apps_irqs; + + if (enable) + __set_bit(irq, apps_irq_bitmap); + else + __clear_bit(irq, apps_irq_bitmap); + } + + return 0; +} + +static int msm_mpm_set_irq_type_exclusive( + unsigned int irq, unsigned int flow_type) +{ + uint32_t mpm_irq; + + if (!msm_mpm_is_valid_apps_irq(irq)) + return -EINVAL; + + if (msm_mpm_bypass_apps_irq(irq)) + return 0; + + mpm_irq = msm_mpm_get_irq_a2m(irq); + if (mpm_irq) { + uint32_t index = MSM_MPM_IRQ_INDEX(mpm_irq); + uint32_t mask = MSM_MPM_IRQ_MASK(mpm_irq); + + if (index >= MSM_MPM_REG_WIDTH) + return -EFAULT; + + if (flow_type & IRQ_TYPE_EDGE_BOTH) + msm_mpm_detect_ctl[index] |= mask; + else + msm_mpm_detect_ctl[index] &= ~mask; + + if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) + msm_mpm_polarity[index] |= mask; + else + msm_mpm_polarity[index] &= ~mask; + } + + return 0; +} + +static int __msm_mpm_enable_irq(unsigned int irq, unsigned int enable) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&msm_mpm_lock, flags); + rc = msm_mpm_enable_irq_exclusive(irq, (bool)enable, false); + spin_unlock_irqrestore(&msm_mpm_lock, flags); + + return rc; +} + +static void msm_mpm_enable_irq(struct irq_data *d) +{ + __msm_mpm_enable_irq(d->irq, 1); +} + +static void msm_mpm_disable_irq(struct irq_data *d) +{ + __msm_mpm_enable_irq(d->irq, 0); +} + +static int msm_mpm_set_irq_wake(struct irq_data *d, unsigned int on) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&msm_mpm_lock, flags); + rc = msm_mpm_enable_irq_exclusive(d->irq, (bool)on, true); + spin_unlock_irqrestore(&msm_mpm_lock, flags); + + return rc; +} + +static int msm_mpm_set_irq_type(struct irq_data *d, unsigned int flow_type) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&msm_mpm_lock, flags); + rc = msm_mpm_set_irq_type_exclusive(d->irq, flow_type); + spin_unlock_irqrestore(&msm_mpm_lock, flags); + + return rc; +} + +/****************************************************************************** + * Public functions + *****************************************************************************/ +int msm_mpm_enable_pin(unsigned int pin, unsigned int enable) +{ + uint32_t index = MSM_MPM_IRQ_INDEX(pin); + uint32_t mask = MSM_MPM_IRQ_MASK(pin); + unsigned long flags; + + spin_lock_irqsave(&msm_mpm_lock, flags); + + if (enable) + msm_mpm_enabled_irq[index] |= mask; + else + msm_mpm_enabled_irq[index] &= ~mask; + + spin_unlock_irqrestore(&msm_mpm_lock, flags); + return 0; +} + +int msm_mpm_set_pin_wake(unsigned int pin, unsigned int on) +{ + uint32_t index = MSM_MPM_IRQ_INDEX(pin); + uint32_t mask = MSM_MPM_IRQ_MASK(pin); + unsigned long flags; + + spin_lock_irqsave(&msm_mpm_lock, flags); + + if (on) + msm_mpm_wake_irq[index] |= mask; + else + msm_mpm_wake_irq[index] &= ~mask; + + spin_unlock_irqrestore(&msm_mpm_lock, flags); + return 0; +} + +int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type) +{ + uint32_t index = MSM_MPM_IRQ_INDEX(pin); + uint32_t mask = MSM_MPM_IRQ_MASK(pin); + unsigned long flags; + + spin_lock_irqsave(&msm_mpm_lock, flags); + + if (flow_type & IRQ_TYPE_EDGE_BOTH) + msm_mpm_detect_ctl[index] |= mask; + else + msm_mpm_detect_ctl[index] &= ~mask; + + if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) + msm_mpm_polarity[index] |= mask; + else + msm_mpm_polarity[index] &= ~mask; + + spin_unlock_irqrestore(&msm_mpm_lock, flags); + return 0; +} + +bool msm_mpm_irqs_detectable(bool from_idle) +{ + unsigned long *apps_irq_bitmap; + int debug_mask; + + if (from_idle) { + apps_irq_bitmap = msm_mpm_enabled_apps_irqs; + debug_mask = msm_mpm_debug_mask & + MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE; + } else { + apps_irq_bitmap = msm_mpm_wake_apps_irqs; + debug_mask = msm_mpm_debug_mask & + MSM_MPM_DEBUG_NON_DETECTABLE_IRQ; + } + + if (debug_mask) { + static char buf[DIV_ROUND_UP(MSM_MPM_NR_APPS_IRQS, 32)*9+1]; + + bitmap_scnprintf(buf, sizeof(buf), apps_irq_bitmap, + MSM_MPM_NR_APPS_IRQS); + buf[sizeof(buf) - 1] = '\0'; + + pr_info("%s: cannot monitor %s", __func__, buf); + } + + return (bool)__bitmap_empty(apps_irq_bitmap, MSM_MPM_NR_APPS_IRQS); +} + +bool msm_mpm_gpio_irqs_detectable(bool from_idle) +{ + unsigned long *apps_irq_bitmap = from_idle ? + msm_mpm_enabled_apps_irqs : msm_mpm_wake_apps_irqs; + + return !__bitmap_intersects(msm_mpm_gpio_irqs_mask, apps_irq_bitmap, + MSM_MPM_NR_APPS_IRQS); +} + +void msm_mpm_enter_sleep(bool from_idle) +{ + msm_mpm_set(!from_idle); +} + +void msm_mpm_exit_sleep(bool from_idle) +{ + unsigned long pending; + int i; + int k; + + for (i = 0; i < MSM_MPM_REG_WIDTH; i++) { + pending = msm_mpm_read(MSM_MPM_STATUS_REG_PENDING, i); + + if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask) + pr_info("%s: pending.%d: 0x%08lx", __func__, + i, pending); + + k = find_first_bit(&pending, 32); + while (k < 32) { + unsigned int mpm_irq = 32 * i + k; + unsigned int apps_irq = msm_mpm_get_irq_m2a(mpm_irq); + struct irq_desc *desc = apps_irq ? + irq_to_desc(apps_irq) : NULL; + + if (desc && !irqd_is_level_type(&desc->irq_data)) { + irq_set_pending(apps_irq); + if (from_idle) + check_irq_resend(desc, apps_irq); + } + + k = find_next_bit(&pending, 32, k + 1); + } + } + + msm_mpm_clear(); +} + +static int __init msm_mpm_early_init(void) +{ + uint8_t mpm_irq; + uint16_t apps_irq; + + for (mpm_irq = 0; msm_mpm_is_valid_mpm_irq(mpm_irq); mpm_irq++) { + apps_irq = msm_mpm_get_irq_m2a(mpm_irq); + if (apps_irq && msm_mpm_is_valid_apps_irq(apps_irq)) + msm_mpm_set_irq_a2m(apps_irq, mpm_irq); + } + + return 0; +} +core_initcall(msm_mpm_early_init); + +void __init msm_mpm_irq_extn_init(struct msm_mpm_device_data *mpm_data) +{ + gic_arch_extn.irq_mask = msm_mpm_disable_irq; + gic_arch_extn.irq_unmask = msm_mpm_enable_irq; + gic_arch_extn.irq_disable = msm_mpm_disable_irq; + gic_arch_extn.irq_set_type = msm_mpm_set_irq_type; + gic_arch_extn.irq_set_wake = msm_mpm_set_irq_wake; + + msm_gpio_irq_extn.irq_mask = msm_mpm_disable_irq; + msm_gpio_irq_extn.irq_unmask = msm_mpm_enable_irq; + msm_gpio_irq_extn.irq_disable = msm_mpm_disable_irq; + msm_gpio_irq_extn.irq_set_type = msm_mpm_set_irq_type; + msm_gpio_irq_extn.irq_set_wake = msm_mpm_set_irq_wake; + + bitmap_set(msm_mpm_gpio_irqs_mask, NR_MSM_IRQS, NR_GPIO_IRQS); + + if (!mpm_data) { +#ifdef CONFIG_MSM_MPM + BUG(); +#endif + return; + } + + memcpy(&msm_mpm_dev_data, mpm_data, sizeof(struct msm_mpm_device_data)); + + msm_mpm_dev_data.irqs_m2a = + kzalloc(msm_mpm_dev_data.irqs_m2a_size * sizeof(uint16_t), + GFP_KERNEL); + BUG_ON(!msm_mpm_dev_data.irqs_m2a); + memcpy(msm_mpm_dev_data.irqs_m2a, mpm_data->irqs_m2a, + msm_mpm_dev_data.irqs_m2a_size * sizeof(uint16_t)); + msm_mpm_dev_data.bypassed_apps_irqs = + kzalloc(msm_mpm_dev_data.bypassed_apps_irqs_size * + sizeof(uint16_t), GFP_KERNEL); + BUG_ON(!msm_mpm_dev_data.bypassed_apps_irqs); + memcpy(msm_mpm_dev_data.bypassed_apps_irqs, + mpm_data->bypassed_apps_irqs, + msm_mpm_dev_data.bypassed_apps_irqs_size * sizeof(uint16_t)); +} + +static int __init msm_mpm_init(void) +{ + unsigned int irq = msm_mpm_dev_data.mpm_ipc_irq; + int rc; + + rc = request_irq(irq, msm_mpm_irq, + IRQF_TRIGGER_RISING, "mpm_drv", msm_mpm_irq); + + if (rc) { + pr_err("%s: failed to request irq %u: %d\n", + __func__, irq, rc); + goto init_bail; + } + + rc = irq_set_irq_wake(irq, 1); + if (rc) { + pr_err("%s: failed to set wakeup irq %u: %d\n", + __func__, irq, rc); + goto init_free_bail; + } + + return 0; + +init_free_bail: + free_irq(irq, msm_mpm_irq); + +init_bail: + return rc; +} +device_initcall(msm_mpm_init); diff --git a/arch/arm/mach-msm/mpp.c b/arch/arm/mach-msm/mpp.c new file mode 100644 index 00000000000..bd70e9a02c8 --- /dev/null +++ b/arch/arm/mach-msm/mpp.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* Qualcomm PMIC Multi-Purpose Pin Configurations */ + +#include +#include +#include +#include +#include + +#include +#include + +int mpp_config_digital_out(unsigned mpp, unsigned config) +{ + int err; + err = msm_proc_comm(PCOM_PM_MPP_CONFIG, &mpp, &config); + if (err) + pr_err("%s: msm_proc_comm(PCOM_PM_MPP_CONFIG) failed\n", + __func__); + return err; +} +EXPORT_SYMBOL(mpp_config_digital_out); + +int mpp_config_digital_in(unsigned mpp, unsigned config) +{ + int err; + err = msm_proc_comm(PCOM_PM_MPP_CONFIG_DIGITAL_INPUT, &mpp, &config); + if (err) + pr_err("%s: msm_proc_comm(PCOM_PM_MPP_CONFIG) failed\n", + __func__); + return err; +} +EXPORT_SYMBOL(mpp_config_digital_in); + +#if defined(CONFIG_DEBUG_FS) +static int test_result; + +static int mpp_debug_set(void *data, u64 val) +{ + unsigned mpp = (unsigned) data; + + test_result = mpp_config_digital_out(mpp, (unsigned)val); + if (test_result) { + printk(KERN_ERR + "%s: mpp_config_digital_out \ + [mpp(%d) = 0x%x] failed (err=%d)\n", + __func__, mpp, (unsigned)val, test_result); + } + return 0; +} + +static int mpp_debug_get(void *data, u64 *val) +{ + if (!test_result) + *val = 0; + else + *val = 1; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(mpp_fops, mpp_debug_get, mpp_debug_set, "%llu\n"); + +static int __init mpp_debug_init(void) +{ + struct dentry *dent; + int n; + char file_name[16]; + + dent = debugfs_create_dir("mpp", 0); + if (IS_ERR(dent)) + return 0; + + for (n = 0; n < MPPS; n++) { + snprintf(file_name, sizeof(file_name), "mpp%d", n + 1); + debugfs_create_file(file_name, 0644, dent, + (void *)n, &mpp_fops); + } + + return 0; +} + +device_initcall(mpp_debug_init); +#endif + diff --git a/arch/arm/mach-msm/msm-buspm-dev.c b/arch/arm/mach-msm/msm-buspm-dev.c new file mode 100644 index 00000000000..296418d0426 --- /dev/null +++ b/arch/arm/mach-msm/msm-buspm-dev.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-buspm-dev.h" + +#define MSM_BUSPM_DRV_NAME "msm-buspm-dev" + +/* + * Allocate kernel buffer. + * Currently limited to one buffer per file descriptor. If alloc() is + * called twice for the same descriptor, the original buffer is freed. + * There is also no locking protection so the same descriptor can not be shared. + */ + +static inline void *msm_buspm_dev_get_vaddr(struct file *filp) +{ + struct msm_buspm_map_dev *dev = filp->private_data; + + return (dev) ? dev->vaddr : NULL; +} + +static inline unsigned long msm_buspm_dev_get_paddr(struct file *filp) +{ + struct msm_buspm_map_dev *dev = filp->private_data; + + return (dev) ? dev->paddr : 0L; +} + +static void msm_buspm_dev_free(struct file *filp) +{ + struct msm_buspm_map_dev *dev = filp->private_data; + + if (dev) { + pr_debug("freeing memory at 0x%p\n", dev->vaddr); + free_contiguous_memory(dev->vaddr); + dev->paddr = 0L; + dev->vaddr = NULL; + } +} + +static int msm_buspm_dev_open(struct inode *inode, struct file *filp) +{ + struct msm_buspm_map_dev *dev; + + if (capable(CAP_SYS_ADMIN)) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev) + filp->private_data = dev; + else + return -ENOMEM; + } else { + return -EPERM; + } + + return 0; +} + +static int +msm_buspm_dev_alloc(struct file *filp, struct buspm_alloc_params data) +{ + unsigned long paddr; + void *vaddr; + struct msm_buspm_map_dev *dev = filp->private_data; + + /* If buffer already allocated, then free it */ + if (dev->vaddr) + msm_buspm_dev_free(filp); + + /* Allocate uncached memory */ + vaddr = allocate_contiguous_ebi(data.size, PAGE_SIZE, 0); + paddr = (vaddr) ? memory_pool_node_paddr(vaddr) : 0L; + + if (vaddr == NULL) { + pr_err("allocation of 0x%x bytes failed", data.size); + return -ENOMEM; + } + + dev->vaddr = vaddr; + dev->paddr = paddr; + dev->buflen = data.size; + filp->f_pos = 0; + pr_debug("virt addr = 0x%p\n", dev->vaddr); + pr_debug("phys addr = 0x%lx\n", dev->paddr); + + return 0; +} + +static long +msm_buspm_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct buspm_xfer_req xfer; + struct buspm_alloc_params alloc_data; + unsigned long paddr; + int retval = 0; + void *buf = msm_buspm_dev_get_vaddr(filp); + unsigned char *dbgbuf = buf; + + switch (cmd) { + case MSM_BUSPM_IOC_FREE: + pr_debug("cmd = 0x%x (FREE)\n", cmd); + msm_buspm_dev_free(filp); + break; + + case MSM_BUSPM_IOC_ALLOC: + pr_debug("cmd = 0x%x (ALLOC)\n", cmd); + retval = __get_user(alloc_data.size, (size_t __user *)arg); + + if (retval == 0) + retval = msm_buspm_dev_alloc(filp, alloc_data); + break; + + case MSM_BUSPM_IOC_RD_PHYS_ADDR: + pr_debug("Read Physical Address\n"); + paddr = msm_buspm_dev_get_paddr(filp); + if (paddr == 0L) { + retval = -EINVAL; + } else { + pr_debug("phys addr = 0x%lx\n", paddr); + retval = __put_user(paddr, + (unsigned long __user *)arg); + } + break; + + case MSM_BUSPM_IOC_RDBUF: + pr_debug("Read Buffer: 0x%x%x%x%x\n", + dbgbuf[0], dbgbuf[1], dbgbuf[2], dbgbuf[3]); + + if (!buf) { + retval = -EINVAL; + break; + } + + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) { + retval = -EFAULT; + break; + } + + if ((xfer.size <= sizeof(buf)) && + (copy_to_user((void __user *)xfer.data, buf, + xfer.size))) { + retval = -EFAULT; + break; + } + break; + + case MSM_BUSPM_IOC_WRBUF: + pr_debug("Write Buffer\n"); + + if (!buf) { + retval = -EINVAL; + break; + } + + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) { + retval = -EFAULT; + break; + } + + if ((sizeof(buf) <= xfer.size) && + (copy_from_user(buf, (void __user *)xfer.data, + xfer.size))) { + retval = -EFAULT; + break; + } + break; + + default: + pr_debug("Unknown command 0x%x\n", cmd); + retval = -EINVAL; + break; + } + + return retval; +} + +static int msm_buspm_dev_release(struct inode *inode, struct file *filp) +{ + struct msm_buspm_map_dev *dev = filp->private_data; + + msm_buspm_dev_free(filp); + kfree(dev); + filp->private_data = NULL; + + return 0; +} + +static int msm_buspm_dev_mmap(struct file *filp, struct vm_area_struct *vma) +{ + pr_debug("vma = 0x%p\n", vma); + + /* Mappings are uncached */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EFAULT; + + return 0; +} + +static const struct file_operations msm_buspm_dev_fops = { + .owner = THIS_MODULE, + .mmap = msm_buspm_dev_mmap, + .open = msm_buspm_dev_open, + .unlocked_ioctl = msm_buspm_dev_ioctl, + .llseek = noop_llseek, + .release = msm_buspm_dev_release, +}; + +struct miscdevice msm_buspm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MSM_BUSPM_DRV_NAME, + .fops = &msm_buspm_dev_fops, +}; + +static int __init msm_buspm_dev_init(void) +{ + int ret = 0; + + ret = misc_register(&msm_buspm_misc); + if (ret < 0) + pr_err("%s: Cannot register misc device\n", __func__); + + return ret; +} + +static void __exit msm_buspm_dev_exit(void) +{ + misc_deregister(&msm_buspm_misc); +} +module_init(msm_buspm_dev_init); +module_exit(msm_buspm_dev_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:"MSM_BUSPM_DRV_NAME); diff --git a/arch/arm/mach-msm/msm-buspm-dev.h b/arch/arm/mach-msm/msm-buspm-dev.h new file mode 100644 index 00000000000..583908714b9 --- /dev/null +++ b/arch/arm/mach-msm/msm-buspm-dev.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_BUSPM_DEV_H__ +#define __MSM_BUSPM_DEV_H__ + +#include + +struct msm_buspm_map_dev { + void *vaddr; + unsigned long paddr; + size_t buflen; +}; + +/* Read/write data into kernel buffer */ +struct buspm_xfer_req { + int size; /* Size of this request, in bytes */ + void *data; /* Data buffer to transfer data to/from */ +}; + +struct buspm_alloc_params { + int size; +}; + +#define MSM_BUSPM_IOC_MAGIC 'p' + +#define MSM_BUSPM_IOC_FREE \ + _IOW(MSM_BUSPM_IOC_MAGIC, 0, void *) + +#define MSM_BUSPM_IOC_ALLOC \ + _IOW(MSM_BUSPM_IOC_MAGIC, 1, size_t) + +#define MSM_BUSPM_IOC_RDBUF \ + _IOW(MSM_BUSPM_IOC_MAGIC, 2, struct buspm_xfer_req) + +#define MSM_BUSPM_IOC_WRBUF \ + _IOW(MSM_BUSPM_IOC_MAGIC, 3, struct buspm_xfer_req) + +#define MSM_BUSPM_IOC_RD_PHYS_ADDR \ + _IOR(MSM_BUSPM_IOC_MAGIC, 4, unsigned long) +#endif diff --git a/arch/arm/mach-msm/msm-keypad-devices.h b/arch/arm/mach-msm/msm-keypad-devices.h new file mode 100644 index 00000000000..469564a754a --- /dev/null +++ b/arch/arm/mach-msm/msm-keypad-devices.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MSM_KEYPAD_DEVICES_H +#define _MSM_KEYPAD_DEVICES_H + +extern struct platform_device keypad_device_7k_ffa; +extern struct platform_device keypad_device_8k_ffa; +extern struct platform_device keypad_device_surf; + +#endif diff --git a/arch/arm/mach-msm/msm-krait-l2-accessors.c b/arch/arm/mach-msm/msm-krait-l2-accessors.c new file mode 100644 index 00000000000..3d341e31864 --- /dev/null +++ b/arch/arm/mach-msm/msm-krait-l2-accessors.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +DEFINE_RAW_SPINLOCK(l2_access_lock); + +u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val) +{ + unsigned long flags; + u32 ret_val; + /* CP15 registers are not emulated on RUMI3. */ + if (machine_is_msm8960_rumi3()) + return 0; + + raw_spin_lock_irqsave(&l2_access_lock, flags); + + mb(); + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t" + "isb\n\t" + "mrc p15, 3, %[l2cpdr_read], c15, c0, 7\n\t" + : [l2cpdr_read]"=r" (ret_val) + : [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val) + ); + raw_spin_unlock_irqrestore(&l2_access_lock, flags); + + return ret_val; +} +EXPORT_SYMBOL(set_get_l2_indirect_reg); + +void set_l2_indirect_reg(u32 reg_addr, u32 val) +{ + unsigned long flags; + /* CP15 registers are not emulated on RUMI3. */ + if (machine_is_msm8960_rumi3()) + return; + + raw_spin_lock_irqsave(&l2_access_lock, flags); + mb(); + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t" + "isb\n\t" + : + : [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val) + ); + raw_spin_unlock_irqrestore(&l2_access_lock, flags); +} +EXPORT_SYMBOL(set_l2_indirect_reg); + +u32 get_l2_indirect_reg(u32 reg_addr) +{ + u32 val; + unsigned long flags; + /* CP15 registers are not emulated on RUMI3. */ + if (machine_is_msm8960_rumi3()) + return 0; + + raw_spin_lock_irqsave(&l2_access_lock, flags); + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mrc p15, 3, %[l2cpdr], c15, c0, 7\n\t" + : [l2cpdr]"=r" (val) + : [l2cpselr]"r" (reg_addr) + ); + raw_spin_unlock_irqrestore(&l2_access_lock, flags); + + return val; +} +EXPORT_SYMBOL(get_l2_indirect_reg); diff --git a/arch/arm/mach-msm/msm_bus/Makefile b/arch/arm/mach-msm/msm_bus/Makefile new file mode 100644 index 00000000000..061998cb6ad --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for msm-bus driver specific files +# +obj-y += msm_bus_core.o msm_bus_fabric.o msm_bus_config.o msm_bus_arb.o msm_bus_rpm.o +obj-$(CONFIG_ARCH_MSM8X60) += msm_bus_board_8660.o +obj-$(CONFIG_ARCH_MSM8960) += msm_bus_board_8960.o +obj-$(CONFIG_ARCH_MSM9615) += msm_bus_board_9615.o +obj-$(CONFIG_ARCH_APQ8064) += msm_bus_board_8064.o +obj-$(CONFIG_ARCH_MSM8930) += msm_bus_board_8930.o +obj-$(CONFIG_DEBUG_FS) += msm_bus_dbg.o diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c new file mode 100644 index 00000000000..07082b7e296 --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c @@ -0,0 +1,719 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define INDEX_MASK 0x0000FFFF +#define PNODE_MASK 0xFFFF0000 +#define SHIFT_VAL 16 +#define CREATE_PNODE_ID(n, i) (((n) << SHIFT_VAL) | (i)) +#define GET_INDEX(n) ((n) & INDEX_MASK) +#define GET_NODE(n) ((n) >> SHIFT_VAL) +#define IS_NODE(n) ((n) % FABRIC_ID_KEY) +#define SEL_FAB_CLK 1 +#define SEL_SLAVE_CLK 0 + +#define BW_TO_CLK_FREQ_HZ(width, bw) ((unsigned long)\ + DIV_ROUND_UP((bw), (width))) +#define IS_MASTER_VALID(mas) \ + (((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \ + ? 1 : 0) + +#define IS_SLAVE_VALID(slv) \ + (((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0) + +static DEFINE_MUTEX(msm_bus_lock); + +/** + * add_path_node: Adds the path information to the current node + * @info: Internal node info structure + * @next: Combination of the id and index of the next node + * Function returns: Number of pnodes (path_nodes) on success, + * error on failure. + * + * Every node maintains the list of path nodes. A path node is + * reached by finding the node-id and index stored at the current + * node. This makes updating the paths with requested bw and clock + * values efficient, as it avoids lookup for each update-path request. + */ +static int add_path_node(struct msm_bus_inode_info *info, int next) +{ + struct path_node *pnode; + int i; + if (ZERO_OR_NULL_PTR(info)) { + MSM_BUS_ERR("Cannot find node info!: id :%d\n", + info->node_info->priv_id); + return -ENXIO; + } + + for (i = 0; i <= info->num_pnodes; i++) { + if (info->pnode[i].next == -2) { + MSM_BUS_DBG("Reusing pnode for info: %d at index: %d\n", + info->node_info->priv_id, i); + info->pnode[i].clk[DUAL_CTX] = 0; + info->pnode[i].clk[ACTIVE_CTX] = 0; + info->pnode[i].bw[DUAL_CTX] = 0; + info->pnode[i].bw[ACTIVE_CTX] = 0; + info->pnode[i].next = next; + MSM_BUS_DBG("%d[%d] : (%d, %d)\n", + info->node_info->priv_id, i, GET_NODE(next), + GET_INDEX(next)); + return i; + } + } + + info->num_pnodes++; + pnode = krealloc(info->pnode, + ((info->num_pnodes + 1) * sizeof(struct path_node)) + , GFP_KERNEL); + if (ZERO_OR_NULL_PTR(pnode)) { + MSM_BUS_ERR("Error creating path node!\n"); + info->num_pnodes--; + return -ENOMEM; + } + info->pnode = pnode; + info->pnode[info->num_pnodes].clk[DUAL_CTX] = 0; + info->pnode[info->num_pnodes].clk[ACTIVE_CTX] = 0; + info->pnode[info->num_pnodes].bw[DUAL_CTX] = 0; + info->pnode[info->num_pnodes].bw[ACTIVE_CTX] = 0; + info->pnode[info->num_pnodes].next = next; + MSM_BUS_DBG("%d[%d] : (%d, %d)\n", info->node_info->priv_id, + info->num_pnodes, GET_NODE(next), GET_INDEX(next)); + return info->num_pnodes; +} + +static int clearvisitedflag(struct device *dev, void *data) +{ + struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); + fabdev->visited = false; + return 0; +} + +/** + * getpath() - Finds the path from the topology between src and dest + * @src: Source. This is the master from which the request originates + * @dest: Destination. This is the slave to which we're trying to reach + * + * Function returns: next_pnode_id. The higher 16 bits of the next_pnode_id + * represent the src id of the next node on path. The lower 16 bits of the + * next_pnode_id represent the "index", which is the next entry in the array + * of pnodes for that node to fill in clk and bw values. This is created using + * CREATE_PNODE_ID. The return value is stored in ret_pnode, and this is added + * to the list of path nodes. + * + * This function recursively finds the path by updating the src to the + * closest possible node to dest. + */ +static int getpath(int src, int dest) +{ + int pnode_num = -1, i; + struct msm_bus_fabnodeinfo *fabnodeinfo; + struct msm_bus_fabric_device *fabdev; + int next_pnode_id = -1; + struct msm_bus_inode_info *info = NULL; + int _src = src/FABRIC_ID_KEY; + int _dst = dest/FABRIC_ID_KEY; + int ret_pnode = -1; + int fabid = GET_FABID(src); + + /* Find the location of fabric for the src */ + MSM_BUS_DBG("%d --> %d\n", src, dest); + + fabdev = msm_bus_get_fabric_device(fabid); + if (!fabdev) { + MSM_BUS_WARN("Fabric Not yet registered. Try again\n"); + return -ENXIO; + } + + /* Are we there yet? */ + if (src == dest) { + info = fabdev->algo->find_node(fabdev, src); + if (ZERO_OR_NULL_PTR(info)) { + MSM_BUS_ERR("Node %d not found\n", dest); + return -ENXIO; + } + + for (i = 0; i <= info->num_pnodes; i++) { + if (info->pnode[i].next == -2) { + MSM_BUS_DBG("src = dst Reusing pnode for" + " info: %d at index: %d\n", + info->node_info->priv_id, i); + next_pnode_id = CREATE_PNODE_ID(src, i); + info->pnode[i].clk[DUAL_CTX] = 0; + info->pnode[i].bw[DUAL_CTX] = 0; + info->pnode[i].next = next_pnode_id; + MSM_BUS_DBG("returning: %d, %d\n", GET_NODE + (next_pnode_id), GET_INDEX(next_pnode_id)); + return next_pnode_id; + } + } + next_pnode_id = CREATE_PNODE_ID(src, (info->num_pnodes + 1)); + pnode_num = add_path_node(info, next_pnode_id); + if (pnode_num < 0) { + MSM_BUS_ERR("Error adding path node\n"); + return -ENXIO; + } + MSM_BUS_DBG("returning: %d, %d\n", GET_NODE(next_pnode_id), + GET_INDEX(next_pnode_id)); + return next_pnode_id; + } else if (_src == _dst) { + /* + * src and dest belong to same fabric, find the destination + * from the radix tree + */ + info = fabdev->algo->find_node(fabdev, dest); + if (ZERO_OR_NULL_PTR(info)) { + MSM_BUS_ERR("Node %d not found\n", dest); + return -ENXIO; + } + + ret_pnode = getpath(info->node_info->priv_id, dest); + next_pnode_id = ret_pnode; + } else { + /* find the dest fabric */ + int trynextgw = true; + struct list_head *gateways = fabdev->algo->get_gw_list(fabdev); + list_for_each_entry(fabnodeinfo, gateways, list) { + /* see if the destination is at a connected fabric */ + if (_dst == (fabnodeinfo->info->node_info->priv_id / + FABRIC_ID_KEY)) { + /* Found the fab on which the device exists */ + info = fabnodeinfo->info; + trynextgw = false; + ret_pnode = getpath(info->node_info->priv_id, + dest); + pnode_num = add_path_node(info, ret_pnode); + if (pnode_num < 0) { + MSM_BUS_ERR("Error adding path node\n"); + return -ENXIO; + } + next_pnode_id = CREATE_PNODE_ID( + info->node_info->priv_id, pnode_num); + break; + } + } + + /* find the gateway */ + if (trynextgw) { + gateways = fabdev->algo->get_gw_list(fabdev); + list_for_each_entry(fabnodeinfo, gateways, list) { + struct msm_bus_fabric_device *gwfab = + msm_bus_get_fabric_device(fabnodeinfo-> + info->node_info->priv_id); + if (!gwfab->visited) { + MSM_BUS_DBG("VISITED ID: %d\n", + gwfab->id); + gwfab->visited = true; + info = fabnodeinfo->info; + ret_pnode = getpath(info-> + node_info->priv_id, dest); + pnode_num = add_path_node(info, + ret_pnode); + if (pnode_num < 0) { + MSM_BUS_ERR("Malloc failure in" + " adding path node\n"); + return -ENXIO; + } + next_pnode_id = CREATE_PNODE_ID( + info->node_info->priv_id, pnode_num); + break; + } + } + if (next_pnode_id < 0) + return -ENXIO; + } + } + + if (!IS_NODE(src)) { + MSM_BUS_DBG("Returning next_pnode_id:%d[%d]\n", GET_NODE( + next_pnode_id), GET_INDEX(next_pnode_id)); + return next_pnode_id; + } + info = fabdev->algo->find_node(fabdev, src); + if (!info) { + MSM_BUS_ERR("Node info not found.\n"); + return -ENXIO; + } + + pnode_num = add_path_node(info, next_pnode_id); + MSM_BUS_DBG(" Last: %d[%d] = (%d, %d)\n", + src, info->num_pnodes, GET_NODE(next_pnode_id), + GET_INDEX(next_pnode_id)); + MSM_BUS_DBG("returning: %d, %d\n", src, pnode_num); + return CREATE_PNODE_ID(src, pnode_num); +} + +/** + * update_path() - Update the path with the bandwidth and clock values, as + * requested by the client. + * + * @curr: Current source node, as specified in the client vector (master) + * @pnode: The first-hop node on the path, stored in the internal client struct + * @req_clk: Requested clock value from the vector + * @req_bw: Requested bandwidth value from the vector + * @curr_clk: Current clock frequency + * @curr_bw: Currently allocated bandwidth + * + * This function updates the nodes on the path calculated using getpath(), with + * clock and bandwidth values. The sum of bandwidths, and the max of clock + * frequencies is calculated at each node on the path. Commit data to be sent + * to RPM for each master and slave is also calculated here. + */ +static int update_path(int curr, int pnode, unsigned long req_clk, unsigned + long req_bw, unsigned long curr_clk, unsigned long curr_bw, + unsigned int ctx, unsigned int cl_active_flag) +{ + int index, ret = 0; + struct msm_bus_inode_info *info; + int next_pnode; + long int add_bw = req_bw - curr_bw; + unsigned bwsum = 0; + unsigned req_clk_hz, curr_clk_hz, bwsum_hz; + int *master_tiers; + struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device + (GET_FABID(curr)); + + MSM_BUS_DBG("args: %d %d %d %lu %lu %lu %lu %u\n", + curr, GET_NODE(pnode), GET_INDEX(pnode), req_clk, req_bw, + curr_clk, curr_bw, ctx); + index = GET_INDEX(pnode); + MSM_BUS_DBG("Client passed index :%d\n", index); + info = fabdev->algo->find_node(fabdev, curr); + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + return -ENXIO; + } + + info->link_info.sel_bw = &info->link_info.bw[ctx]; + info->link_info.sel_clk = &info->link_info.clk[ctx]; + *info->link_info.sel_bw += add_bw; + + info->pnode[index].sel_bw = &info->pnode[index].bw[ctx]; + + /** + * To select the right clock, AND the context with + * client active flag. + */ + info->pnode[index].sel_clk = &info->pnode[index].clk[ctx & + cl_active_flag]; + *info->pnode[index].sel_bw += add_bw; + + info->link_info.num_tiers = info->node_info->num_tiers; + info->link_info.tier = info->node_info->tier; + master_tiers = info->node_info->tier; + + do { + struct msm_bus_inode_info *hop; + fabdev = msm_bus_get_fabric_device(GET_FABID(curr)); + if (!fabdev) { + MSM_BUS_ERR("Fabric not found\n"); + return -ENXIO; + } + MSM_BUS_DBG("id: %d\n", info->node_info->priv_id); + + /* find next node and index */ + next_pnode = info->pnode[index].next; + curr = GET_NODE(next_pnode); + index = GET_INDEX(next_pnode); + MSM_BUS_DBG("id:%d, next: %d\n", info-> + node_info->priv_id, curr); + + /* Get hop */ + /* check if we are here as gateway, or does the hop belong to + * this fabric */ + if (IS_NODE(curr)) + hop = fabdev->algo->find_node(fabdev, curr); + else + hop = fabdev->algo->find_gw_node(fabdev, curr); + if (!hop) { + MSM_BUS_ERR("Null Info found for hop\n"); + return -ENXIO; + } + + hop->link_info.sel_bw = &hop->link_info.bw[ctx]; + hop->link_info.sel_clk = &hop->link_info.clk[ctx]; + *hop->link_info.sel_bw += add_bw; + + hop->pnode[index].sel_bw = &hop->pnode[index].bw[ctx]; + hop->pnode[index].sel_clk = &hop->pnode[index].clk[ctx & + cl_active_flag]; + + if (!hop->node_info->buswidth) { + MSM_BUS_WARN("No bus width found. Using default\n"); + hop->node_info->buswidth = 8; + } + *hop->pnode[index].sel_clk = BW_TO_CLK_FREQ_HZ(hop->node_info-> + buswidth, req_clk); + *hop->pnode[index].sel_bw += add_bw; + MSM_BUS_DBG("fabric: %d slave: %d, slave-width: %d info: %d\n", + fabdev->id, hop->node_info->priv_id, hop->node_info-> + buswidth, info->node_info->priv_id); + /* Update Bandwidth */ + fabdev->algo->update_bw(fabdev, hop, info, add_bw, + master_tiers, ctx); + bwsum = (uint16_t)*hop->link_info.sel_bw; + /* Update Fabric clocks */ + curr_clk_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, + curr_clk); + req_clk_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, + req_clk); + bwsum_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth, + bwsum); + MSM_BUS_DBG("Calling update-clks: curr_hz: %lu, req_hz: %lu," + " bw_hz %u\n", curr_clk, req_clk, bwsum_hz); + ret = fabdev->algo->update_clks(fabdev, hop, index, + curr_clk_hz, req_clk_hz, bwsum_hz, SEL_FAB_CLK, + ctx, cl_active_flag); + if (ret) + MSM_BUS_WARN("Failed to update clk\n"); + info = hop; + } while (GET_NODE(info->pnode[index].next) != info->node_info->priv_id); + + /* Update BW, clk after exiting the loop for the last one */ + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + return -ENXIO; + } + /* Update slave clocks */ + ret = fabdev->algo->update_clks(fabdev, info, index, curr_clk_hz, + req_clk_hz, bwsum_hz, SEL_SLAVE_CLK, ctx, cl_active_flag); + if (ret) + MSM_BUS_ERR("Failed to update clk\n"); + return ret; +} + +/** + * msm_bus_commit_fn() - Commits the data for fabric to rpm + * @dev: fabric device + * @data: NULL + */ +static int msm_bus_commit_fn(struct device *dev, void *data) +{ + int ret = 0; + struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); + MSM_BUS_DBG("Committing: fabid: %d\n", fabdev->id); + ret = fabdev->algo->commit(fabdev); + return ret; +} + +/** + * msm_bus_scale_register_client() - Register the clients with the msm bus + * driver + * @pdata: Platform data of the client, containing src, dest, ab, ib + * + * Client data contains the vectors specifying arbitrated bandwidth (ab) + * and instantaneous bandwidth (ib) requested between a particular + * src and dest. + */ +uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata) +{ + struct msm_bus_client *client = NULL; + int i; + int src, dest, nfab; + struct msm_bus_fabric_device *deffab; + + deffab = msm_bus_get_fabric_device(MSM_BUS_FAB_DEFAULT); + if (!deffab) { + MSM_BUS_ERR("Error finding default fabric\n"); + return -ENXIO; + } + + nfab = msm_bus_get_num_fab(); + if (nfab < deffab->board_algo->board_nfab) { + MSM_BUS_ERR("Can't register client!\n" + "Num of fabrics up: %d\n", + nfab); + return 0; + } + + if ((!pdata) || (pdata->usecase->num_paths == 0) || IS_ERR(pdata)) { + MSM_BUS_ERR("Cannot register client with null data\n"); + return 0; + } + + client = kzalloc(sizeof(struct msm_bus_client), GFP_KERNEL); + if (!client) { + MSM_BUS_ERR("Error allocating client\n"); + return 0; + } + + mutex_lock(&msm_bus_lock); + client->pdata = pdata; + client->curr = -1; + for (i = 0; i < pdata->usecase->num_paths; i++) { + int *pnode; + struct msm_bus_fabric_device *srcfab; + pnode = krealloc(client->src_pnode, ((i + 1) * sizeof(int)), + GFP_KERNEL); + if (ZERO_OR_NULL_PTR(pnode)) { + MSM_BUS_ERR("Invalid Pnode ptr!\n"); + continue; + } else + client->src_pnode = pnode; + + if (!IS_MASTER_VALID(pdata->usecase->vectors[i].src)) { + MSM_BUS_ERR("Invalid Master ID %d in request!\n", + pdata->usecase->vectors[i].src); + goto err; + } + + if (!IS_SLAVE_VALID(pdata->usecase->vectors[i].dst)) { + MSM_BUS_ERR("Invalid Slave ID %d in request!\n", + pdata->usecase->vectors[i].dst); + goto err; + } + + src = msm_bus_board_get_iid(pdata->usecase->vectors[i].src); + if (src == -ENXIO) { + MSM_BUS_ERR("Master %d not supported. Client cannot be" + " registered\n", + pdata->usecase->vectors[i].src); + goto err; + } + dest = msm_bus_board_get_iid(pdata->usecase->vectors[i].dst); + if (dest == -ENXIO) { + MSM_BUS_ERR("Slave %d not supported. Client cannot be" + " registered\n", + pdata->usecase->vectors[i].dst); + goto err; + } + srcfab = msm_bus_get_fabric_device(GET_FABID(src)); + srcfab->visited = true; + pnode[i] = getpath(src, dest); + bus_for_each_dev(&msm_bus_type, NULL, NULL, clearvisitedflag); + if (pnode[i] == -ENXIO) { + MSM_BUS_ERR("Cannot register client now! Try again!\n"); + goto err; + } + } + msm_bus_dbg_client_data(client->pdata, MSM_BUS_DBG_REGISTER, + (uint32_t)client); + mutex_unlock(&msm_bus_lock); + MSM_BUS_DBG("ret: %u num_paths: %d\n", (uint32_t)client, + pdata->usecase->num_paths); + return (uint32_t)(client); +err: + kfree(client->src_pnode); + kfree(client); + mutex_unlock(&msm_bus_lock); + return 0; +} +EXPORT_SYMBOL(msm_bus_scale_register_client); + +/** + * msm_bus_scale_client_update_request() - Update the request for bandwidth + * from a particular client + * + * cl: Handle to the client + * index: Index into the vector, to which the bw and clock values need to be + * updated + */ +int msm_bus_scale_client_update_request(uint32_t cl, unsigned index) +{ + int i, ret = 0; + struct msm_bus_scale_pdata *pdata; + int pnode, src, curr, ctx; + unsigned long req_clk, req_bw, curr_clk, curr_bw; + struct msm_bus_client *client = (struct msm_bus_client *)cl; + if (IS_ERR(client)) { + MSM_BUS_ERR("msm_bus_scale_client update req error %d\n", + (uint32_t)client); + return -ENXIO; + } + + mutex_lock(&msm_bus_lock); + if (client->curr == index) + goto err; + + curr = client->curr; + pdata = client->pdata; + + if (index >= pdata->num_usecases) { + MSM_BUS_ERR("Client %u passed invalid index: %d\n", + (uint32_t)client, index); + ret = -ENXIO; + goto err; + } + + MSM_BUS_DBG("cl: %u index: %d curr: %d" + " num_paths: %d\n", cl, index, client->curr, + client->pdata->usecase->num_paths); + + for (i = 0; i < pdata->usecase->num_paths; i++) { + src = msm_bus_board_get_iid(client->pdata->usecase[index]. + vectors[i].src); + if (src == -ENXIO) { + MSM_BUS_ERR("Master %d not supported. Request cannot" + " be updated\n", client->pdata->usecase-> + vectors[i].src); + goto err; + } + + if (msm_bus_board_get_iid(client->pdata->usecase[index]. + vectors[i].dst) == -ENXIO) { + MSM_BUS_ERR("Slave %d not supported. Request cannot" + " be updated\n", client->pdata->usecase-> + vectors[i].dst); + } + + pnode = client->src_pnode[i]; + req_clk = client->pdata->usecase[index].vectors[i].ib; + req_bw = client->pdata->usecase[index].vectors[i].ab; + if (curr < 0) { + curr_clk = 0; + curr_bw = 0; + } else { + curr_clk = client->pdata->usecase[curr].vectors[i].ib; + curr_bw = client->pdata->usecase[curr].vectors[i].ab; + MSM_BUS_DBG("ab: %lu ib: %lu\n", curr_bw, curr_clk); + } + + if (!pdata->active_only) { + ret = update_path(src, pnode, req_clk, req_bw, + curr_clk, curr_bw, 0, pdata->active_only); + if (ret) { + MSM_BUS_ERR("Update path failed! %d\n", ret); + goto err; + } + } + + ret = update_path(src, pnode, req_clk, req_bw, curr_clk, + curr_bw, ACTIVE_CTX, pdata->active_only); + if (ret) { + MSM_BUS_ERR("Update Path failed! %d\n", ret); + goto err; + } + } + + client->curr = index; + ctx = ACTIVE_CTX; + msm_bus_dbg_client_data(client->pdata, index, cl); + bus_for_each_dev(&msm_bus_type, NULL, NULL, msm_bus_commit_fn); + +err: + mutex_unlock(&msm_bus_lock); + return ret; +} +EXPORT_SYMBOL(msm_bus_scale_client_update_request); + +int reset_pnodes(int curr, int pnode) +{ + struct msm_bus_inode_info *info; + struct msm_bus_fabric_device *fabdev; + int index, next_pnode; + fabdev = msm_bus_get_fabric_device(GET_FABID(curr)); + index = GET_INDEX(pnode); + info = fabdev->algo->find_node(fabdev, curr); + if (!info) { + MSM_BUS_ERR("Cannot find node info!\n"); + return -ENXIO; + } + + MSM_BUS_DBG("Starting the loop--remove\n"); + do { + struct msm_bus_inode_info *hop; + fabdev = msm_bus_get_fabric_device(GET_FABID(curr)); + if (!fabdev) { + MSM_BUS_ERR("Fabric not found\n"); + return -ENXIO; + } + + next_pnode = info->pnode[index].next; + info->pnode[index].next = -2; + curr = GET_NODE(next_pnode); + index = GET_INDEX(next_pnode); + if (IS_NODE(curr)) + hop = fabdev->algo->find_node(fabdev, curr); + else + hop = fabdev->algo->find_gw_node(fabdev, curr); + if (!hop) { + MSM_BUS_ERR("Null Info found for hop\n"); + return -ENXIO; + } + + MSM_BUS_DBG("%d[%d] = %d\n", info->node_info->priv_id, index, + info->pnode[index].next); + MSM_BUS_DBG("num_pnodes: %d: %d\n", info->node_info->priv_id, + info->num_pnodes); + info = hop; + } while (GET_NODE(info->pnode[index].next) != info->node_info->priv_id); + + info->pnode[index].next = -2; + MSM_BUS_DBG("%d[%d] = %d\n", info->node_info->priv_id, index, + info->pnode[index].next); + MSM_BUS_DBG("num_pnodes: %d: %d\n", info->node_info->priv_id, + info->num_pnodes); + return 0; +} + +int msm_bus_board_get_iid(int id) +{ + struct msm_bus_fabric_device *deffab; + + deffab = msm_bus_get_fabric_device(MSM_BUS_FAB_DEFAULT); + if (!deffab) { + MSM_BUS_ERR("Error finding default fabric\n"); + return -ENXIO; + } + + return deffab->board_algo->get_iid(id); +} + +void msm_bus_scale_client_reset_pnodes(uint32_t cl) +{ + int i, src, pnode, index; + struct msm_bus_client *client = (struct msm_bus_client *)(cl); + if (IS_ERR(client)) { + MSM_BUS_ERR("msm_bus_scale_reset_pnodes error\n"); + return; + } + index = 0; + for (i = 0; i < client->pdata->usecase->num_paths; i++) { + src = msm_bus_board_get_iid( + client->pdata->usecase[index].vectors[i].src); + pnode = client->src_pnode[i]; + MSM_BUS_DBG("(%d, %d)\n", GET_NODE(pnode), GET_INDEX(pnode)); + reset_pnodes(src, pnode); + } +} + +/** + * msm_bus_scale_unregister_client() - Unregister the client from the bus driver + * @cl: Handle to the client + */ +void msm_bus_scale_unregister_client(uint32_t cl) +{ + struct msm_bus_client *client = (struct msm_bus_client *)(cl); + if (IS_ERR(client) || (!client)) + return; + if (client->curr != 0) + msm_bus_scale_client_update_request(cl, 0); + MSM_BUS_DBG("Unregistering client %d\n", cl); + mutex_lock(&msm_bus_lock); + msm_bus_scale_client_reset_pnodes(cl); + msm_bus_dbg_client_data(client->pdata, MSM_BUS_DBG_UNREGISTER, cl); + mutex_unlock(&msm_bus_lock); + kfree(client->src_pnode); + kfree(client); +} +EXPORT_SYMBOL(msm_bus_scale_unregister_client); + diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c new file mode 100644 index 00000000000..5a3d722c7cc --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8064.c @@ -0,0 +1,975 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define NMASTERS 54 +#define NSLAVES 75 +#define NFAB_8064 5 + +enum msm_bus_fabric_tiered_slave_type { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_0 = 1, + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_1, + MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM, + + MSM_BUS_TIERED_SLAVE_MM_IMEM = 1, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_1, + + MSM_BUS_TIERED_SLAVE_EBI1_CH0 = 1, + MSM_BUS_TIERED_SLAVE_EBI1_CH1, + MSM_BUS_TIERED_SLAVE_KMPSS_L2, +}; + +enum msm_bus_8064_master_ports_type { + MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB = 0, + MSM_BUS_MASTER_PORT_SPS, + MSM_BUS_MASTER_PORT_ADM_PORT0, + MSM_BUS_MASTER_PORT_ADM_PORT1, + MSM_BUS_MASTER_PORT_LPASS_PROC, + MSM_BUS_MASTER_PORT_GSS_NAV, + MSM_BUS_MASTER_PORT_PCIE, + MSM_BUS_MASTER_PORT_RIVA, + MSM_BUS_MASTER_PORT_SATA, + MSM_BUS_MASTER_PORT_LPASS, + MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_MMSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI, + MSM_BUS_SYSTEM_MASTER_PORT_CRYPTO, + + MSM_BUS_MASTER_PORT_MDP_PORT0 = 0, + MSM_BUS_MASTER_PORT_MDP_PORT1, + MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0, + MSM_BUS_MASTER_PORT_ROTATOR, + MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT1, + MSM_BUS_MASTER_PORT_JPEG_DEC, + MSM_BUS_MASTER_PORT_VIDEO_CAP, + MSM_BUS_MASTER_PORT_VFE, + MSM_BUS_MASTER_PORT_VPE, + MSM_BUS_MASTER_PORT_JPEG_ENC, + MSM_BUS_MASTER_PORT_VIDEO_DEC, + MSM_BUS_MMSS_MASTER_PORT_APPS_FAB, + MSM_BUS_MASTER_PORT_VIDEO_ENC, + + MSM_BUS_MASTER_PORT_KMPSS_M0 = 0, + MSM_BUS_MASTER_PORT_KMPSS_M1, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_1, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_1, + +}; + +enum msm_bus_8064_slave_ports_type { + MSM_BUS_SLAVE_PORT_MM_IMEM = 0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1, + + MSM_BUS_SLAVE_PORT_EBI1_CH0 = 0, + MSM_BUS_SLAVE_PORT_EBI1_CH1, + MSM_BUS_SLAVE_PORT_KMPSS_L2, + MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB, + MSM_BUS_SLAVE_PORT_SYSTEM_FAB, + + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_0 = 0, + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_1, + MSM_BUS_SLAVE_PORT_SPS, + MSM_BUS_SLAVE_PORT_SYSTEM_IMEM, + MSM_BUS_SLAVE_PORT_CORESIGHT, + MSM_BUS_SLAVE_PORT_PCIE, + MSM_BUS_SLAVE_PORT_KMPSS, + MSM_BUS_SLAVE_PORT_GSS, + MSM_BUS_SLAVE_PORT_LPASS, + MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_MMSS_FPB, + MSM_BUS_SLAVE_PORT_RIVA, + MSM_BUS_SLAVE_PORT_SATA, + MSM_BUS_SLAVE_PORT_CRYPTO, +}; + +static int tier2[] = {MSM_BUS_BW_TIER2,}; +static uint32_t master_iids[NMASTERS]; +static uint32_t slave_iids[NSLAVES]; + +static int mport_kmpss_m0[] = {MSM_BUS_MASTER_PORT_KMPSS_M0,}; +static int mport_kmpss_m1[] = {MSM_BUS_MASTER_PORT_KMPSS_M1,}; + +static int mmss_mport_apps_fab[] = {MSM_BUS_MMSS_MASTER_PORT_APPS_FAB,}; +static int system_mport_appss_fab[] = {MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB,}; +static int sport_ebi1_ch0[] = { + MSM_BUS_SLAVE_PORT_EBI1_CH0, + MSM_BUS_SLAVE_PORT_EBI1_CH1, +}; +static int sport_ebi1_ch1[] = {MSM_BUS_SLAVE_PORT_EBI1_CH1,}; +static int sport_kmpss_l2[] = {MSM_BUS_SLAVE_PORT_KMPSS_L2,}; +static int appss_sport_mmss_fab[] = {MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB,}; +static int sport_system_fab[] = {MSM_BUS_SLAVE_PORT_SYSTEM_FAB,}; + +static int tiered_slave_ebi1_ch0[] = { + MSM_BUS_TIERED_SLAVE_EBI1_CH0, + MSM_BUS_TIERED_SLAVE_EBI1_CH1, +}; +static int tiered_slave_ebi1_ch1[] = {MSM_BUS_TIERED_SLAVE_EBI1_CH1,}; + +static int tiered_slave_kmpss[] = {MSM_BUS_TIERED_SLAVE_KMPSS_L2,}; + +static struct msm_bus_node_info apps_fabric_info[] = { + { + .id = MSM_BUS_MASTER_AMPSS_M0, + .masterp = mport_kmpss_m0, + .num_mports = ARRAY_SIZE(mport_kmpss_m0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_AMPSS_M1, + .masterp = mport_kmpss_m1, + .num_mports = ARRAY_SIZE(mport_kmpss_m1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_EBI_CH0, + .slavep = sport_ebi1_ch0, + .num_sports = ARRAY_SIZE(sport_ebi1_ch0), + .tier = tiered_slave_ebi1_ch0, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch0), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_EBI_CH1, + .slavep = sport_ebi1_ch1, + .num_sports = ARRAY_SIZE(sport_ebi1_ch1), + .tier = tiered_slave_ebi1_ch1, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch1), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_AMPSS_L2, + .slavep = sport_kmpss_l2, + .num_sports = ARRAY_SIZE(sport_kmpss_l2), + .tier = tiered_slave_kmpss, + .num_tiers = ARRAY_SIZE(tiered_slave_kmpss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_MMSS, + .gateway = 1, + .slavep = appss_sport_mmss_fab, + .num_sports = ARRAY_SIZE(appss_sport_mmss_fab), + .masterp = mmss_mport_apps_fab, + .num_mports = ARRAY_SIZE(mmss_mport_apps_fab), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = sport_system_fab, + .num_sports = ARRAY_SIZE(sport_system_fab), + .masterp = system_mport_appss_fab, + .num_mports = ARRAY_SIZE(system_mport_appss_fab), + .buswidth = 8, + }, +}; + +static int mport_sps[] = {MSM_BUS_MASTER_PORT_SPS,}; +static int mport_adm_port0[] = {MSM_BUS_MASTER_PORT_ADM_PORT0,}; +static int mport_adm_port1[] = {MSM_BUS_MASTER_PORT_ADM_PORT1,}; +static int mport_gss_nav[] = {MSM_BUS_MASTER_PORT_GSS_NAV,}; +static int mport_pcie[] = {MSM_BUS_MASTER_PORT_PCIE,}; +static int mport_lpass_proc[] = {MSM_BUS_MASTER_PORT_LPASS_PROC,}; +static int mport_sata[] = {MSM_BUS_MASTER_PORT_SATA,}; + +static int mport_riva[] = {MSM_BUS_MASTER_PORT_RIVA,}; +static int mport_crypto[] = {MSM_BUS_SYSTEM_MASTER_PORT_CRYPTO,}; +static int mport_lpass[] = {MSM_BUS_MASTER_PORT_LPASS,}; +static int system_mport_mmss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_MMSS_FPB,}; +static int system_mport_adm_ahb_ci[] = {MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI,}; +static int appss_mport_fab_system[] = { + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_1 +}; +static int mport_system_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB,}; +static int system_mport_cpss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB,}; + +static int system_sport_appss_fab[] = { + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_0, + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_1 +}; +static int system_sport_system_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB,}; +static int system_sport_cpss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB,}; +static int sport_sps[] = {MSM_BUS_SLAVE_PORT_SPS,}; +static int sport_system_imem[] = {MSM_BUS_SLAVE_PORT_SYSTEM_IMEM,}; +static int sport_coresight[] = {MSM_BUS_SLAVE_PORT_CORESIGHT,}; +static int sport_crypto[] = {MSM_BUS_SLAVE_PORT_CRYPTO,}; +static int sport_riva[] = {MSM_BUS_SLAVE_PORT_RIVA,}; +static int sport_sata[] = {MSM_BUS_SLAVE_PORT_SATA,}; +static int sport_kmpss[] = {MSM_BUS_SLAVE_PORT_KMPSS,}; +static int sport_gss[] = {MSM_BUS_SLAVE_PORT_GSS,}; +static int sport_lpass[] = {MSM_BUS_SLAVE_PORT_LPASS,}; +static int sport_mmss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_MMSS_FPB,}; + +static int tiered_slave_system_imem[] = {MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM,}; +static int system_tiered_slave_fab_appss[] = { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_0, + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_1, +}; + +static struct msm_bus_node_info system_fabric_info[] = { + { + .id = MSM_BUS_MASTER_SPS, + .masterp = mport_sps, + .num_mports = ARRAY_SIZE(mport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT0, + .masterp = mport_adm_port0, + .num_mports = ARRAY_SIZE(mport_adm_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT1, + .masterp = mport_adm_port1, + .num_mports = ARRAY_SIZE(mport_adm_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS_PROC, + .masterp = mport_lpass_proc, + .num_mports = ARRAY_SIZE(mport_lpass_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GSS_NAV, + .masterp = mport_gss_nav, + .num_mports = ARRAY_SIZE(mport_gss_nav), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_PCIE, + .masterp = mport_pcie, + .num_mports = ARRAY_SIZE(mport_pcie), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RIVA, + .masterp = mport_riva, + .num_mports = ARRAY_SIZE(mport_riva), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_SATA, + .masterp = mport_sata, + .num_mports = ARRAY_SIZE(mport_sata), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_CRYPTO, + .masterp = mport_crypto, + .num_mports = ARRAY_SIZE(mport_crypto), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS, + .masterp = mport_lpass, + .num_mports = ARRAY_SIZE(mport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SYSTEM_MASTER_MMSS_FPB, + .masterp = system_mport_mmss_fpb, + .num_mports = ARRAY_SIZE(system_mport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM0_CI, + .masterp = system_mport_adm_ahb_ci, + .num_mports = ARRAY_SIZE(system_mport_adm_ahb_ci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = system_sport_appss_fab, + .num_sports = ARRAY_SIZE(system_sport_appss_fab), + .masterp = appss_mport_fab_system, + .num_mports = ARRAY_SIZE(appss_mport_fab_system), + .tier = system_tiered_slave_fab_appss, + .num_tiers = ARRAY_SIZE(system_tiered_slave_fab_appss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_FAB_CPSS_FPB, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_SLAVE_SPS, + .slavep = sport_sps, + .num_sports = ARRAY_SIZE(sport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "dfab_clk", + .slaveclk[ACTIVE_CTX] = "dfab_a_clk", + }, + { + .id = MSM_BUS_SLAVE_SYSTEM_IMEM, + .slavep = sport_system_imem, + .num_sports = ARRAY_SIZE(sport_system_imem), + .tier = tiered_slave_system_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_system_imem), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_CORESIGHT, + .slavep = sport_coresight, + .num_sports = ARRAY_SIZE(sport_coresight), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_CRYPTO, + .slavep = sport_crypto, + .num_sports = ARRAY_SIZE(sport_crypto), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_RIVA, + .slavep = sport_riva, + .num_sports = ARRAY_SIZE(sport_riva), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_SATA, + .slavep = sport_sata, + .num_sports = ARRAY_SIZE(sport_sata), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_AMPSS, + .slavep = sport_kmpss, + .num_sports = ARRAY_SIZE(sport_kmpss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_GSS, + .slavep = sport_gss, + .num_sports = ARRAY_SIZE(sport_gss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_LPASS, + .slavep = sport_lpass, + .num_sports = ARRAY_SIZE(sport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SYSTEM_SLAVE_MMSS_FPB, + .slavep = sport_mmss_fpb, + .num_sports = ARRAY_SIZE(sport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, +}; + +static int mport_mdp[] = { + MSM_BUS_MASTER_PORT_MDP_PORT0, + MSM_BUS_MASTER_PORT_MDP_PORT1, +}; +static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,}; +static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,}; +static int mport_graphics_3d_port0[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0,}; +static int mport_graphics_3d_port1[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT1,}; +static int mport_jpeg_dec[] = {MSM_BUS_MASTER_PORT_JPEG_DEC,}; +static int mport_video_cap[] = {MSM_BUS_MASTER_PORT_VIDEO_CAP,}; +static int mport_vfe[] = {MSM_BUS_MASTER_PORT_VFE,}; +static int mport_vpe[] = {MSM_BUS_MASTER_PORT_VPE,}; +static int mport_jpeg_enc[] = {MSM_BUS_MASTER_PORT_JPEG_ENC,}; +static int mport_video_enc[] = {MSM_BUS_MASTER_PORT_VIDEO_ENC,}; +static int mport_video_dec[] = {MSM_BUS_MASTER_PORT_VIDEO_DEC,}; +static int appss_mport_fab_mmss[] = { + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_1 +}; + +static int mmss_sport_apps_fab[] = { + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1 +}; +static int sport_mm_imem[] = {MSM_BUS_SLAVE_PORT_MM_IMEM,}; + +static int mmss_tiered_slave_fab_apps[] = { + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_1, +}; +static int tiered_slave_mm_imem[] = {MSM_BUS_TIERED_SLAVE_MM_IMEM,}; + + +static struct msm_bus_node_info mmss_fabric_info[] = { + { + .id = MSM_BUS_MASTER_MDP_PORT0, + .masterp = mport_mdp, + .num_mports = ARRAY_SIZE(mport_mdp), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MDP_PORT1, + .masterp = mport_mdp1, + .num_mports = ARRAY_SIZE(mport_mdp1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ROTATOR, + .masterp = mport_rotator, + .num_mports = ARRAY_SIZE(mport_rotator), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_3D, + .masterp = mport_graphics_3d_port0, + .num_mports = ARRAY_SIZE(mport_graphics_3d_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_3D_PORT1, + .masterp = mport_graphics_3d_port1, + .num_mports = ARRAY_SIZE(mport_graphics_3d_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_DEC, + .masterp = mport_jpeg_dec, + .num_mports = ARRAY_SIZE(mport_jpeg_dec), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VIDEO_CAP, + .masterp = mport_video_cap, + .num_mports = ARRAY_SIZE(mport_video_cap), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VIDEO_ENC, + .masterp = mport_video_enc, + .num_mports = ARRAY_SIZE(mport_video_enc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VFE, + .masterp = mport_vfe, + .num_mports = ARRAY_SIZE(mport_vfe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VPE, + .masterp = mport_vpe, + .num_mports = ARRAY_SIZE(mport_vpe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_ENC, + .masterp = mport_jpeg_enc, + .num_mports = ARRAY_SIZE(mport_jpeg_enc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + /* This port has been added for V2. It is absent in V1 */ + { + .id = MSM_BUS_MASTER_VIDEO_DEC, + .masterp = mport_video_dec, + .num_mports = ARRAY_SIZE(mport_video_dec), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = mmss_sport_apps_fab, + .num_sports = ARRAY_SIZE(mmss_sport_apps_fab), + .masterp = appss_mport_fab_mmss, + .num_mports = ARRAY_SIZE(appss_mport_fab_mmss), + .tier = mmss_tiered_slave_fab_apps, + .num_tiers = ARRAY_SIZE(mmss_tiered_slave_fab_apps), + .buswidth = 16, + }, + { + .id = MSM_BUS_SLAVE_MM_IMEM, + .slavep = sport_mm_imem, + .num_sports = ARRAY_SIZE(sport_mm_imem), + .tier = tiered_slave_mm_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_mm_imem), + .buswidth = 8, + }, +}; + +static struct msm_bus_node_info sys_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_MASTER_SPDM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RPM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_SPDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM_MSG_RAM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_B, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_C, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_B, + .buswidth = 4, + .ahb = 1, + }, +}; + +static struct msm_bus_node_info cpss_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_NAND, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS0, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS3, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS4, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS5, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_TSIF, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TSSC, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_DIMEM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TCSR, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PRNG, + .buswidth = 4, + .ahb = 1, + }, +}; + +static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration + *fabreg, int fabid) +{ + int i; + for (i = 0; i < fabreg->len; i++) { + if (!fabreg->info[i].gateway) { + fabreg->info[i].priv_id = fabid + fabreg->info[i].id; + if (fabreg->info[i].id < SLAVE_ID_KEY) { + WARN(fabreg->info[i].id >= NMASTERS, + "id %d exceeds array size!\n", + fabreg->info[i].id); + master_iids[fabreg->info[i].id] = + fabreg->info[i].priv_id; + } else { + WARN((fabreg->info[i].id - SLAVE_ID_KEY) >= + NSLAVES, "id %d exceeds array size!\n", + fabreg->info[i].id); + slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)] + = fabreg->info[i].priv_id; + } + } else + fabreg->info[i].priv_id = fabreg->info[i].id; + } +} + +static int msm_bus_board_8064_get_iid(int id) +{ + if ((id < SLAVE_ID_KEY && id >= NMASTERS) || + id >= (SLAVE_ID_KEY + NSLAVES)) { + MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id); + return -EINVAL; + } + + return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] : + slave_iids[id - SLAVE_ID_KEY]), id); +} + +static struct msm_bus_board_algorithm msm_bus_board_algo = { + .board_nfab = NFAB_8064, + .get_iid = msm_bus_board_8064_get_iid, + .assign_iids = msm_bus_board_assign_iids, +}; + +struct msm_bus_fabric_registration msm_bus_8064_apps_fabric_pdata = { + .id = MSM_BUS_FAB_APPSS, + .name = "msm_apps_fab", + .info = apps_fabric_info, + .len = ARRAY_SIZE(apps_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_APPS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_APPS_FABRIC_ARB_0, + .nmasters = 6, + .nslaves = 5, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8064_sys_fabric_pdata = { + .id = MSM_BUS_FAB_SYSTEM, + .name = "msm_sys_fab", + system_fabric_info, + ARRAY_SIZE(system_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0, + .nmasters = 15, + .nslaves = 15, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8064_mm_fabric_pdata = { + .id = MSM_BUS_FAB_MMSS, + .name = "msm_mm_fab", + mmss_fabric_info, + ARRAY_SIZE(mmss_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_MM_FABRIC_ARB_0, + .nmasters = 13, + .nslaves = 3, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8064_sys_fpb_pdata = { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .name = "msm_sys_fpb", + sys_fpb_fabric_info, + ARRAY_SIZE(sys_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8064_cpss_fpb_pdata = { + .id = MSM_BUS_FAB_CPSS_FPB, + .name = "msm_cpss_fpb", + cpss_fpb_fabric_info, + ARRAY_SIZE(cpss_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c new file mode 100644 index 00000000000..296c6dcf9e1 --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c @@ -0,0 +1,924 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define NMASTERS 39 +#define NSLAVES 68 +#define NFAB_8660 5 + +enum msm_bus_fabric_tiered_slave_type { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS = 1, + MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM, + + MSM_BUS_TIERED_SLAVE_SMI = 1, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS, + MSM_BUS_TIERED_SLAVE_MM_IMEM, + + MSM_BUS_TIERED_SLAVE_EBI_CH0 = 1, + MSM_BUS_TIERED_SLAVE_SMPSS_L2, +}; + +enum msm_bus_8660_master_ports_type { + MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB = 0, + MSM_BUS_MASTER_PORT_SPS, + MSM_BUS_MASTER_PORT_ADM0_PORT0, + MSM_BUS_MASTER_PORT_ADM0_PORT1, + MSM_BUS_MASTER_PORT_ADM1_PORT0, + MSM_BUS_MASTER_PORT_ADM1_PORT1, + MSM_BUS_MASTER_PORT_LPASS_PROC, + MSM_BUS_MASTER_PORT_MSS_PROCI, + MSM_BUS_MASTER_PORT_MSM_MSS_PROCD, + MSM_BUS_MASTER_PORT_MSM_MDM_PORT0, + MSM_BUS_MASTER_PORT_LPASS, + MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB, + MSM_BUS_MASTER_PORT_SYSTEM_FPB, + MSM_BUS_MASTER_PORT_MMSS_FPB, + MSM_BUS_MASTER_PORT_ADM1_AHB_CI, + MSM_BUS_MASTER_PORT_ADM0_AHB_CI, + MSM_BUS_MASTER_PORT_MSS_MDM_PORT1, + + MSM_BUS_MASTER_PORT_MDP_PORT0 = 0, + MSM_BUS_MASTER_PORT_MDP_PORT1, + MSM_BUS_MMSS_MASTER_PORT_ADM1_PORT0, + MSM_BUS_MASTER_PORT_ROTATOR, + MSM_BUS_MASTER_PORT_GRAPHICS_3D, + MSM_BUS_MASTER_PORT_JPEG_DEC, + MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0, + MSM_BUS_MASTER_PORT_VFE, + MSM_BUS_MASTER_PORT_VPE, + MSM_BUS_MASTER_PORT_JPEG_ENC, + MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1, + MSM_BUS_MMSS_MASTER_PORT_APPS_FAB, + MSM_BUS_MASTER_PORT_HD_CODEC_PORT0, + MSM_BUS_MASTER_PORT_HD_CODEC_PORT1, + + MSM_BUS_MASTER_PORT_SMPSS_M0 = 0, + MSM_BUS_MASTER_PORT_SMPSS_M1, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM, + +}; + +static int tier2[] = {MSM_BUS_BW_TIER2,}; + +enum msm_bus_8660_slave_ports_type { + MSM_BUS_SLAVE_PORT_SMI = 0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1, + MSM_BUS_SLAVE_PORT_MM_IMEM, + + MSM_BUS_SLAVE_PORT_EBI_CH0 = 0, + MSM_BUS_SLAVE_PORT_SMPSS_L2, + MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB, + MSM_BUS_SLAVE_PORT_SYSTEM_FAB, + + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB = 0, + MSM_BUS_SLAVE_PORT_SPS, + MSM_BUS_SLAVE_PORT_SYSTEM_IMEM, + MSM_BUS_SLAVE_PORT_SMPSS, + MSM_BUS_SLAVE_PORT_MSS, + MSM_BUS_SLAVE_PORT_LPASS, + MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB, + MSM_BUS_SLAVE_PORT_MMSS_FPB, +}; + +static uint32_t master_iids[NMASTERS]; +static uint32_t slave_iids[NSLAVES]; + +static int ampss_m0_mports[] = {MSM_BUS_MASTER_PORT_SMPSS_M0,}; +static int ampss_m1_mports[] = {MSM_BUS_MASTER_PORT_SMPSS_M1,}; +static int mmss_mport_apps_fab[] = {MSM_BUS_MMSS_MASTER_PORT_APPS_FAB,}; +static int system_mport_appss_fab[] = {MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB,}; + +static int sport_ebi_ch0[] = {MSM_BUS_SLAVE_PORT_EBI_CH0,}; +static int sport_smpss_l2[] = {MSM_BUS_SLAVE_PORT_SMPSS_L2,}; +static int appss_sport_mmss_fab[] = {MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB,}; +static int sport_system_fab[] = {MSM_BUS_SLAVE_PORT_SYSTEM_FAB,}; + +static int tiered_slave_ebi[] = {MSM_BUS_TIERED_SLAVE_EBI_CH0,}; +static int tiered_slave_smpss[] = {MSM_BUS_TIERED_SLAVE_SMPSS_L2,}; + +static struct msm_bus_node_info apps_fabric_info[] = { + { + .id = MSM_BUS_MASTER_AMPSS_M0, + .masterp = ampss_m0_mports, + .num_mports = ARRAY_SIZE(ampss_m0_mports), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_AMPSS_M1, + .masterp = ampss_m1_mports, + .num_mports = ARRAY_SIZE(ampss_m1_mports), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_EBI_CH0, + .slavep = sport_ebi_ch0, + .num_sports = ARRAY_SIZE(sport_ebi_ch0), + .tier = tiered_slave_ebi, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_AMPSS_L2, + .slavep = sport_smpss_l2, + .num_sports = ARRAY_SIZE(sport_smpss_l2), + .tier = tiered_slave_smpss, + .num_tiers = ARRAY_SIZE(tiered_slave_smpss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_MMSS, + .gateway = 1, + .slavep = appss_sport_mmss_fab, + .num_sports = ARRAY_SIZE(appss_sport_mmss_fab), + .masterp = mmss_mport_apps_fab, + .num_mports = ARRAY_SIZE(mmss_mport_apps_fab), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = sport_system_fab, + .num_sports = ARRAY_SIZE(sport_system_fab), + .masterp = system_mport_appss_fab, + .num_mports = ARRAY_SIZE(system_mport_appss_fab), + .buswidth = 8, + }, +}; + +static int mport_sps[] = {MSM_BUS_MASTER_PORT_SPS,}; +static int mport_adm0_port0[] = {MSM_BUS_MASTER_PORT_ADM0_PORT0,}; +static int mport_adm0_port1[] = {MSM_BUS_MASTER_PORT_ADM0_PORT1,}; +static int mport_adm1_port0[] = {MSM_BUS_MASTER_PORT_ADM1_PORT0,}; +static int mport_adm1_port1[] = {MSM_BUS_MASTER_PORT_ADM1_PORT1,}; +static int mport_lpass_proc[] = {MSM_BUS_MASTER_PORT_LPASS_PROC,}; +static int mport_mss_proci[] = {MSM_BUS_MASTER_PORT_MSS_PROCI,}; +static int mport_msm_mss_procd[] = {MSM_BUS_MASTER_PORT_MSM_MSS_PROCD,}; +static int mport_mss_mdm_port0[] = {MSM_BUS_MASTER_PORT_MSM_MDM_PORT0,}; +static int mport_lpass[] = {MSM_BUS_MASTER_PORT_LPASS,}; +static int mport_mmss_fpb[] = {MSM_BUS_MASTER_PORT_MMSS_FPB,}; +static int mport_adm1_ahb_ci[] = {MSM_BUS_MASTER_PORT_ADM1_AHB_CI,}; +static int mport_adm0_ahb_ci[] = {MSM_BUS_MASTER_PORT_ADM0_AHB_CI,}; +static int mport_mss_mdm_port1[] = {MSM_BUS_MASTER_PORT_MSS_MDM_PORT1,}; +static int appss_mport_fab_system[] = {MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM,}; +static int mport_system_fpb[] = {MSM_BUS_MASTER_PORT_SYSTEM_FPB,}; +static int system_mport_cpss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB,}; + +static int system_sport_appss_fab[] = {MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB,}; +static int system_sport_system_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB,}; +static int system_sport_cpss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB,}; +static int sport_sps[] = {MSM_BUS_SLAVE_PORT_SPS,}; +static int sport_system_imem[] = {MSM_BUS_SLAVE_PORT_SYSTEM_IMEM,}; +static int sport_smpss[] = {MSM_BUS_SLAVE_PORT_SMPSS,}; +static int sport_mss[] = {MSM_BUS_SLAVE_PORT_MSS,}; +static int sport_lpass[] = {MSM_BUS_SLAVE_PORT_LPASS,}; +static int sport_mmss_fpb[] = {MSM_BUS_SLAVE_PORT_MMSS_FPB,}; + +static int tiered_slave_system_imem[] = {MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM,}; +static int system_tiered_slave_fab_appss[] = { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS,}; + +static struct msm_bus_node_info system_fabric_info[] = { + { + .id = MSM_BUS_MASTER_SPS, + .masterp = mport_sps, + .num_mports = ARRAY_SIZE(mport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT0, + .masterp = mport_adm0_port0, + .num_mports = ARRAY_SIZE(mport_adm0_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT1, + .masterp = mport_adm0_port1, + .num_mports = ARRAY_SIZE(mport_adm0_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SYSTEM_MASTER_ADM1_PORT0, + .masterp = mport_adm1_port0, + .num_mports = ARRAY_SIZE(mport_adm1_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM1_PORT1, + .masterp = mport_adm1_port1, + .num_mports = ARRAY_SIZE(mport_adm1_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS_PROC, + .masterp = mport_lpass_proc, + .num_mports = ARRAY_SIZE(mport_lpass_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_PROCI, + .masterp = mport_mss_proci, + .num_mports = ARRAY_SIZE(mport_mss_proci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_PROCD, + .masterp = mport_msm_mss_procd, + .num_mports = ARRAY_SIZE(mport_msm_mss_procd), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_MDM_PORT0, + .masterp = mport_mss_mdm_port0, + .num_mports = ARRAY_SIZE(mport_mss_mdm_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS, + .masterp = mport_lpass, + .num_mports = ARRAY_SIZE(mport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SYSTEM_MASTER_MMSS_FPB, + .masterp = mport_mmss_fpb, + .num_mports = ARRAY_SIZE(mport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM1_CI, + .masterp = mport_adm1_ahb_ci, + .num_mports = ARRAY_SIZE(mport_adm1_ahb_ci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM0_CI, + .masterp = mport_adm0_ahb_ci, + .num_mports = ARRAY_SIZE(mport_adm0_ahb_ci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_MDM_PORT1, + .masterp = mport_mss_mdm_port1, + .num_mports = ARRAY_SIZE(mport_mss_mdm_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = system_sport_appss_fab, + .num_sports = ARRAY_SIZE(system_sport_appss_fab), + .masterp = appss_mport_fab_system, + .num_mports = ARRAY_SIZE(appss_mport_fab_system), + .tier = system_tiered_slave_fab_appss, + .num_tiers = ARRAY_SIZE(system_tiered_slave_fab_appss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_FAB_CPSS_FPB, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_SLAVE_SPS, + .slavep = sport_sps, + .num_sports = ARRAY_SIZE(sport_sps), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_SYSTEM_IMEM, + .slavep = sport_system_imem, + .num_sports = ARRAY_SIZE(sport_system_imem), + .tier = tiered_slave_system_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_system_imem), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_AMPSS, + .slavep = sport_smpss, + .num_sports = ARRAY_SIZE(sport_smpss), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_MSS, + .slavep = sport_mss, + .num_sports = ARRAY_SIZE(sport_mss), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_LPASS, + .slavep = sport_lpass, + .num_sports = ARRAY_SIZE(sport_lpass), + .buswidth = 8, + }, + { + .id = MSM_BUS_SYSTEM_SLAVE_MMSS_FPB, + .slavep = sport_mmss_fpb, + .num_sports = ARRAY_SIZE(sport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, +}; + +static int mport_mdp_port0[] = {MSM_BUS_MASTER_PORT_MDP_PORT0,}; +static int mport_mdp_port1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,}; +static int mmss_mport_adm1_port0[] = {MSM_BUS_MMSS_MASTER_PORT_ADM1_PORT0,}; +static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,}; +static int mport_graphics_3d[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D,}; +static int mport_jpeg_dec[] = {MSM_BUS_MASTER_PORT_JPEG_DEC,}; +static int mport_graphics_2d_core0[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0,}; +static int mport_vfe[] = {MSM_BUS_MASTER_PORT_VFE,}; +static int mport_vpe[] = {MSM_BUS_MASTER_PORT_VPE,}; +static int mport_jpeg_enc[] = {MSM_BUS_MASTER_PORT_JPEG_ENC,}; +static int mport_graphics_2d_core1[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1,}; +static int mport_hd_codec_port0[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,}; +static int mport_hd_codec_port1[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT1,}; +static int appss_mport_fab_mmss[] = {MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS,}; + +static int sport_smi[] = {MSM_BUS_SLAVE_PORT_SMI,}; +static int mmss_sport_apps_fab_0[] = {MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0,}; +static int sport_mm_imem[] = {MSM_BUS_SLAVE_PORT_MM_IMEM,}; + +static int tiered_slave_smi[] = {MSM_BUS_TIERED_SLAVE_SMI,}; +static int mmss_tiered_slave_fab_apps[] = {MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS,}; +static int tiered_slave_mm_imem[] = {MSM_BUS_TIERED_SLAVE_MM_IMEM,}; + +static struct msm_bus_node_info mmss_fabric_info[] = { + { + .id = MSM_BUS_MASTER_MDP_PORT0, + .masterp = mport_mdp_port0, + .num_mports = ARRAY_SIZE(mport_mdp_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MDP_PORT1, + .masterp = mport_mdp_port1, + .num_mports = ARRAY_SIZE(mport_mdp_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MMSS_MASTER_ADM1_PORT0, + .masterp = mmss_mport_adm1_port0, + .num_mports = ARRAY_SIZE(mmss_mport_adm1_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ROTATOR, + .masterp = mport_rotator, + .num_mports = ARRAY_SIZE(mport_rotator), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_3D, + .masterp = mport_graphics_3d, + .num_mports = ARRAY_SIZE(mport_graphics_3d), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_DEC, + .masterp = mport_jpeg_dec, + .num_mports = ARRAY_SIZE(mport_jpeg_dec), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .masterp = mport_graphics_2d_core0, + .num_mports = ARRAY_SIZE(mport_graphics_2d_core0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VFE, + .masterp = mport_vfe, + .num_mports = ARRAY_SIZE(mport_vfe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VPE, + .masterp = mport_vpe, + .num_mports = ARRAY_SIZE(mport_vpe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_ENC, + .masterp = mport_jpeg_enc, + .num_mports = ARRAY_SIZE(mport_jpeg_enc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + /* This port has been added for V2. It is absent in V1 */ + { + .id = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .masterp = mport_graphics_2d_core1, + .num_mports = ARRAY_SIZE(mport_graphics_2d_core1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_HD_CODEC_PORT0, + .masterp = mport_hd_codec_port0, + .num_mports = ARRAY_SIZE(mport_hd_codec_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_HD_CODEC_PORT1, + .masterp = mport_hd_codec_port1, + .num_mports = ARRAY_SIZE(mport_hd_codec_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_SMI, + .slavep = sport_smi, + .num_sports = ARRAY_SIZE(sport_smi), + .tier = tiered_slave_smi, + .num_tiers = ARRAY_SIZE(tiered_slave_smi), + .buswidth = 16, + .slaveclk[DUAL_CTX] = "smi_clk", + .slaveclk[ACTIVE_CTX] = "smi_a_clk", + }, + { + .id = MSM_BUS_MMSS_SLAVE_FAB_APPS_1, + .slavep = mmss_sport_apps_fab_0, + .num_sports = ARRAY_SIZE(mmss_sport_apps_fab_0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = mmss_sport_apps_fab_0, + .num_sports = ARRAY_SIZE(mmss_sport_apps_fab_0), + .masterp = appss_mport_fab_mmss, + .num_mports = ARRAY_SIZE(appss_mport_fab_mmss), + .tier = mmss_tiered_slave_fab_apps, + .num_tiers = ARRAY_SIZE(mmss_tiered_slave_fab_apps), + .buswidth = 16, + }, + { + .id = MSM_BUS_SLAVE_MM_IMEM, + .slavep = sport_mm_imem, + .num_sports = ARRAY_SIZE(sport_mm_imem), + .tier = tiered_slave_mm_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_mm_imem), + .buswidth = 8, + }, +}; + +static struct msm_bus_node_info sys_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_MASTER_SPDM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RPM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_SPDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM_MSG_RAM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_B, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_C, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_B, + .buswidth = 4, + .ahb = 1, + }, +}; + +static struct msm_bus_node_info cpss_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_NAND, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS0, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS3, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS4, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS5, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_TSIF, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TSSC, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_DIMEM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TCSR, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PRNG, + .buswidth = 4, + .ahb = 1, + }, +}; + +static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration + *fabreg, int fabid) +{ + int i; + for (i = 0; i < fabreg->len; i++) { + if (!fabreg->info[i].gateway) { + fabreg->info[i].priv_id = fabid + fabreg->info[i].id; + if (fabreg->info[i].id < SLAVE_ID_KEY) + master_iids[fabreg->info[i].id] = + fabreg->info[i].priv_id; + else + slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)] + = fabreg->info[i].priv_id; + } else + fabreg->info[i].priv_id = fabreg->info[i].id; + } +} + +static int msm_bus_board_8660_get_iid(int id) +{ + if ((id < SLAVE_ID_KEY && id >= NMASTERS) || + id >= (SLAVE_ID_KEY + NSLAVES)) { + MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id); + return -EINVAL; + } + + return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] : + slave_iids[id - SLAVE_ID_KEY]), id); +} + +static struct msm_bus_board_algorithm msm_bus_board_algo = { + .board_nfab = NFAB_8660, + .get_iid = msm_bus_board_8660_get_iid, + .assign_iids = msm_bus_board_assign_iids, +}; + +struct msm_bus_fabric_registration msm_bus_apps_fabric_pdata = { + .id = MSM_BUS_FAB_APPSS, + .name = "msm_apps_fab", + .info = apps_fabric_info, + .len = ARRAY_SIZE(apps_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_APPS_FABRIC_HALT_0, + .offset = MSM_RPM_ID_APPS_FABRIC_ARB_0, + .nmasters = 4, + .nslaves = 4, + .ntieredslaves = 2, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_sys_fabric_pdata = { + .id = MSM_BUS_FAB_SYSTEM, + .name = "msm_sys_fab", + system_fabric_info, + ARRAY_SIZE(system_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_SYSTEM_FABRIC_HALT_0, + .offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0, + .nmasters = 17, + .nslaves = 9, + .ntieredslaves = 2, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_mm_fabric_pdata = { + .id = MSM_BUS_FAB_MMSS, + .name = "msm_mm_fab", + mmss_fabric_info, + ARRAY_SIZE(mmss_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_MM_FABRIC_HALT_0, + .offset = MSM_RPM_ID_MM_FABRIC_ARB_0, + .nmasters = 14, + .nslaves = 4, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_sys_fpb_pdata = { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .name = "msm_sys_fpb", + sys_fpb_fabric_info, + ARRAY_SIZE(sys_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_cpss_fpb_pdata = { + .id = MSM_BUS_FAB_CPSS_FPB, + .name = "msm_cpss_fpb", + cpss_fpb_fabric_info, + ARRAY_SIZE(cpss_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +int msm_bus_board_rpm_get_il_ids(uint16_t id[]) +{ + return -ENXIO; +} diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c new file mode 100644 index 00000000000..0f37c6de347 --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8930.c @@ -0,0 +1,880 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define NMASTERS 45 +#define NSLAVES 75 +#define NFAB_8930 5 + +enum msm_bus_fabric_tiered_slave_type { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_0 = 1, + MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM, + + MSM_BUS_TIERED_SLAVE_MM_IMEM = 1, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0, + + MSM_BUS_TIERED_SLAVE_EBI1_CH0 = 1, + MSM_BUS_TIERED_SLAVE_KMPSS_L2, +}; + +enum msm_bus_8930_master_ports_type { + MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB = 0, + MSM_BUS_MASTER_PORT_SPS, + MSM_BUS_MASTER_PORT_ADM_PORT0, + MSM_BUS_MASTER_PORT_ADM_PORT1, + MSM_BUS_MASTER_PORT_LPASS_PROC, + MSM_BUS_MASTER_PORT_MSS, + MSM_BUS_MASTER_PORT_RIVA, + MSM_BUS_MASTER_PORT_MSS_SW_PROC, + MSM_BUS_MASTER_PORT_MSS_FW_PROC, + MSM_BUS_MASTER_PORT_LPASS, + MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_MMSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI, + + MSM_BUS_MASTER_PORT_MDP_PORT0 = 0, + MSM_BUS_MASTER_PORT_MDP_PORT1, + MSM_BUS_MASTER_PORT_GRAPHICS_3D, + MSM_BUS_MASTER_PORT_ROTATOR, + MSM_BUS_MASTER_PORT_VFE, + MSM_BUS_MASTER_PORT_VPE, + MSM_BUS_MASTER_PORT_JPEG_ENC, + MSM_BUS_MMSS_MASTER_PORT_APPS_FAB, + MSM_BUS_MASTER_PORT_HD_CODEC_PORT0, + MSM_BUS_MASTER_PORT_HD_CODEC_PORT1, + + MSM_BUS_MASTER_PORT_KMPSS_M0 = 0, + MSM_BUS_MASTER_PORT_KMPSS_M1, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_0, +}; + +enum msm_bus_8930_slave_ports_type { + MSM_BUS_SLAVE_PORT_MM_IMEM = 0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, + + MSM_BUS_SLAVE_PORT_EBI1_CH0 = 0, + MSM_BUS_SLAVE_PORT_KMPSS_L2, + MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB, + MSM_BUS_SLAVE_PORT_SYSTEM_FAB, + + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_0 = 0, + MSM_BUS_SLAVE_PORT_SPS, + MSM_BUS_SLAVE_PORT_SYSTEM_IMEM, + MSM_BUS_SLAVE_PORT_CORESIGHT, + MSM_BUS_SLAVE_PORT_KMPSS, + MSM_BUS_SLAVE_PORT_MSS, + MSM_BUS_SLAVE_PORT_LPASS, + MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_MMSS_FPB, + MSM_BUS_SLAVE_PORT_RIVA, +}; + +static int tier2[] = {MSM_BUS_BW_TIER2,}; +static uint32_t master_iids[NMASTERS]; +static uint32_t slave_iids[NSLAVES]; + +static int mport_kmpss_m0[] = {MSM_BUS_MASTER_PORT_KMPSS_M0,}; +static int mport_kmpss_m1[] = {MSM_BUS_MASTER_PORT_KMPSS_M1,}; + +static int mmss_mport_apps_fab[] = {MSM_BUS_MMSS_MASTER_PORT_APPS_FAB,}; +static int system_mport_appss_fab[] = {MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB,}; +static int sport_ebi1_ch0[] = { + MSM_BUS_SLAVE_PORT_EBI1_CH0, +}; +static int sport_kmpss_l2[] = {MSM_BUS_SLAVE_PORT_KMPSS_L2,}; +static int appss_sport_mmss_fab[] = {MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB,}; +static int sport_system_fab[] = {MSM_BUS_SLAVE_PORT_SYSTEM_FAB,}; + +static int tiered_slave_ebi1_ch0[] = { + MSM_BUS_TIERED_SLAVE_EBI1_CH0, +}; + +static int tiered_slave_kmpss[] = {MSM_BUS_TIERED_SLAVE_KMPSS_L2,}; + +static struct msm_bus_node_info apps_fabric_info[] = { + { + .id = MSM_BUS_MASTER_AMPSS_M0, + .masterp = mport_kmpss_m0, + .num_mports = ARRAY_SIZE(mport_kmpss_m0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_AMPSS_M1, + .masterp = mport_kmpss_m1, + .num_mports = ARRAY_SIZE(mport_kmpss_m1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_EBI_CH0, + .slavep = sport_ebi1_ch0, + .num_sports = ARRAY_SIZE(sport_ebi1_ch0), + .tier = tiered_slave_ebi1_ch0, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch0), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_AMPSS_L2, + .slavep = sport_kmpss_l2, + .num_sports = ARRAY_SIZE(sport_kmpss_l2), + .tier = tiered_slave_kmpss, + .num_tiers = ARRAY_SIZE(tiered_slave_kmpss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_MMSS, + .gateway = 1, + .slavep = appss_sport_mmss_fab, + .num_sports = ARRAY_SIZE(appss_sport_mmss_fab), + .masterp = mmss_mport_apps_fab, + .num_mports = ARRAY_SIZE(mmss_mport_apps_fab), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = sport_system_fab, + .num_sports = ARRAY_SIZE(sport_system_fab), + .masterp = system_mport_appss_fab, + .num_mports = ARRAY_SIZE(system_mport_appss_fab), + .buswidth = 8, + }, +}; + +static int mport_sps[] = {MSM_BUS_MASTER_PORT_SPS,}; +static int mport_adm_port0[] = {MSM_BUS_MASTER_PORT_ADM_PORT0,}; +static int mport_adm_port1[] = {MSM_BUS_MASTER_PORT_ADM_PORT1,}; +static int mport_mss[] = {MSM_BUS_MASTER_PORT_MSS,}; +static int mport_lpass_proc[] = {MSM_BUS_MASTER_PORT_LPASS_PROC,}; +static int mport_riva[] = {MSM_BUS_MASTER_PORT_RIVA,}; +static int mport_mss_sw_proc[] = {MSM_BUS_MASTER_PORT_MSS_SW_PROC,}; +static int mport_mss_fw_proc[] = {MSM_BUS_MASTER_PORT_MSS_FW_PROC,}; +static int mport_lpass[] = {MSM_BUS_MASTER_PORT_LPASS,}; +static int system_mport_mmss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_MMSS_FPB,}; +static int system_mport_adm_ahb_ci[] = {MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI,}; +static int appss_mport_fab_system[] = { + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_0, +}; +static int mport_system_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB,}; +static int system_mport_cpss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB,}; + +static int system_sport_appss_fab[] = { + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_0, +}; +static int system_sport_system_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB,}; +static int system_sport_cpss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB,}; +static int sport_sps[] = {MSM_BUS_SLAVE_PORT_SPS,}; +static int sport_system_imem[] = {MSM_BUS_SLAVE_PORT_SYSTEM_IMEM,}; +static int sport_coresight[] = {MSM_BUS_SLAVE_PORT_CORESIGHT,}; +static int sport_riva[] = {MSM_BUS_SLAVE_PORT_RIVA,}; +static int sport_kmpss[] = {MSM_BUS_SLAVE_PORT_KMPSS,}; +static int sport_mss[] = {MSM_BUS_SLAVE_PORT_MSS,}; +static int sport_lpass[] = {MSM_BUS_SLAVE_PORT_LPASS,}; +static int sport_mmss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_MMSS_FPB,}; + +static int tiered_slave_system_imem[] = {MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM,}; +static int system_tiered_slave_fab_appss[] = { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_0, +}; + +static struct msm_bus_node_info system_fabric_info[] = { + { + .id = MSM_BUS_MASTER_SPS, + .masterp = mport_sps, + .num_mports = ARRAY_SIZE(mport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT0, + .masterp = mport_adm_port0, + .num_mports = ARRAY_SIZE(mport_adm_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT1, + .masterp = mport_adm_port1, + .num_mports = ARRAY_SIZE(mport_adm_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS_PROC, + .masterp = mport_lpass_proc, + .num_mports = ARRAY_SIZE(mport_lpass_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS, + .masterp = mport_mss, + .num_mports = ARRAY_SIZE(mport_mss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RIVA, + .masterp = mport_riva, + .num_mports = ARRAY_SIZE(mport_riva), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_SW_PROC, + .masterp = mport_mss_sw_proc, + .num_mports = ARRAY_SIZE(mport_mss_sw_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_FW_PROC, + .masterp = mport_mss_fw_proc, + .num_mports = ARRAY_SIZE(mport_mss_fw_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS, + .masterp = mport_lpass, + .num_mports = ARRAY_SIZE(mport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SYSTEM_MASTER_MMSS_FPB, + .masterp = system_mport_mmss_fpb, + .num_mports = ARRAY_SIZE(system_mport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM0_CI, + .masterp = system_mport_adm_ahb_ci, + .num_mports = ARRAY_SIZE(system_mport_adm_ahb_ci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = system_sport_appss_fab, + .num_sports = ARRAY_SIZE(system_sport_appss_fab), + .masterp = appss_mport_fab_system, + .num_mports = ARRAY_SIZE(appss_mport_fab_system), + .tier = system_tiered_slave_fab_appss, + .num_tiers = ARRAY_SIZE(system_tiered_slave_fab_appss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_FAB_CPSS_FPB, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_SLAVE_SPS, + .slavep = sport_sps, + .num_sports = ARRAY_SIZE(sport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "dfab_clk", + .slaveclk[ACTIVE_CTX] = "dfab_a_clk", + }, + { + .id = MSM_BUS_SLAVE_SYSTEM_IMEM, + .slavep = sport_system_imem, + .num_sports = ARRAY_SIZE(sport_system_imem), + .tier = tiered_slave_system_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_system_imem), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_CORESIGHT, + .slavep = sport_coresight, + .num_sports = ARRAY_SIZE(sport_coresight), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_RIVA, + .slavep = sport_riva, + .num_sports = ARRAY_SIZE(sport_riva), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_AMPSS, + .slavep = sport_kmpss, + .num_sports = ARRAY_SIZE(sport_kmpss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_MSS, + .slavep = sport_mss, + .num_sports = ARRAY_SIZE(sport_mss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_LPASS, + .slavep = sport_lpass, + .num_sports = ARRAY_SIZE(sport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SYSTEM_SLAVE_MMSS_FPB, + .slavep = sport_mmss_fpb, + .num_sports = ARRAY_SIZE(sport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, +}; + +static int mport_mdp[] = { + MSM_BUS_MASTER_PORT_MDP_PORT0, + MSM_BUS_MASTER_PORT_MDP_PORT1, +}; +static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,}; +static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,}; +static int mport_graphics_3d[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D,}; +static int mport_vfe[] = {MSM_BUS_MASTER_PORT_VFE,}; +static int mport_vpe[] = {MSM_BUS_MASTER_PORT_VPE,}; +static int mport_jpeg_enc[] = {MSM_BUS_MASTER_PORT_JPEG_ENC,}; +static int mport_hd_codec_port0[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,}; +static int mport_hd_codec_port1[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT1,}; +static int appss_mport_fab_mmss[] = { + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0, +}; + +static int mmss_sport_apps_fab[] = { + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, +}; +static int sport_mm_imem[] = {MSM_BUS_SLAVE_PORT_MM_IMEM,}; + +static int mmss_tiered_slave_fab_apps[] = { + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0, +}; +static int tiered_slave_mm_imem[] = {MSM_BUS_TIERED_SLAVE_MM_IMEM,}; + + +static struct msm_bus_node_info mmss_fabric_info[] = { + { + .id = MSM_BUS_MASTER_MDP_PORT0, + .masterp = mport_mdp, + .num_mports = ARRAY_SIZE(mport_mdp), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MDP_PORT1, + .masterp = mport_mdp1, + .num_mports = ARRAY_SIZE(mport_mdp1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ROTATOR, + .masterp = mport_rotator, + .num_mports = ARRAY_SIZE(mport_rotator), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_3D, + .masterp = mport_graphics_3d, + .num_mports = ARRAY_SIZE(mport_graphics_3d), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VFE, + .masterp = mport_vfe, + .num_mports = ARRAY_SIZE(mport_vfe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VPE, + .masterp = mport_vpe, + .num_mports = ARRAY_SIZE(mport_vpe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_ENC, + .masterp = mport_jpeg_enc, + .num_mports = ARRAY_SIZE(mport_jpeg_enc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_HD_CODEC_PORT0, + .masterp = mport_hd_codec_port0, + .num_mports = ARRAY_SIZE(mport_hd_codec_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_HD_CODEC_PORT1, + .masterp = mport_hd_codec_port1, + .num_mports = ARRAY_SIZE(mport_hd_codec_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = mmss_sport_apps_fab, + .num_sports = ARRAY_SIZE(mmss_sport_apps_fab), + .masterp = appss_mport_fab_mmss, + .num_mports = ARRAY_SIZE(appss_mport_fab_mmss), + .tier = mmss_tiered_slave_fab_apps, + .num_tiers = ARRAY_SIZE(mmss_tiered_slave_fab_apps), + .buswidth = 16, + }, + { + .id = MSM_BUS_SLAVE_MM_IMEM, + .slavep = sport_mm_imem, + .num_sports = ARRAY_SIZE(sport_mm_imem), + .tier = tiered_slave_mm_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_mm_imem), + .buswidth = 8, + }, +}; + +static struct msm_bus_node_info sys_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_MASTER_SPDM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RPM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_SPDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM_MSG_RAM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_B, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_C, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_B, + .buswidth = 4, + .ahb = 1, + }, +}; + +static struct msm_bus_node_info cpss_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_NAND, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS0, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS3, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS4, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS5, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_TSIF, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TSSC, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_DIMEM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TCSR, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PRNG, + .buswidth = 4, + .ahb = 1, + }, +}; + +static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration + *fabreg, int fabid) +{ + int i; + for (i = 0; i < fabreg->len; i++) { + if (!fabreg->info[i].gateway) { + fabreg->info[i].priv_id = fabid + fabreg->info[i].id; + if (fabreg->info[i].id < SLAVE_ID_KEY) + master_iids[fabreg->info[i].id] = + fabreg->info[i].priv_id; + else + slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)] + = fabreg->info[i].priv_id; + } else + fabreg->info[i].priv_id = fabreg->info[i].id; + } +} + +static int msm_bus_board_8930_get_iid(int id) +{ + if ((id < SLAVE_ID_KEY && id >= NMASTERS) || + id >= (SLAVE_ID_KEY + NSLAVES)) { + MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id); + return -EINVAL; + } + + return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] : + slave_iids[id - SLAVE_ID_KEY]), id); +} + +static struct msm_bus_board_algorithm msm_bus_board_algo = { + .board_nfab = NFAB_8930, + .get_iid = msm_bus_board_8930_get_iid, + .assign_iids = msm_bus_board_assign_iids, +}; + +struct msm_bus_fabric_registration msm_bus_8930_apps_fabric_pdata = { + .id = MSM_BUS_FAB_APPSS, + .name = "msm_apps_fab", + .info = apps_fabric_info, + .len = ARRAY_SIZE(apps_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_APPS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_APPS_FABRIC_ARB_0, + .nmasters = 4, + .nslaves = 4, + .ntieredslaves = 2, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8930_sys_fabric_pdata = { + .id = MSM_BUS_FAB_SYSTEM, + .name = "msm_sys_fab", + system_fabric_info, + ARRAY_SIZE(system_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0, + .nmasters = 14, + .nslaves = 11, + .ntieredslaves = 2, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8930_mm_fabric_pdata = { + .id = MSM_BUS_FAB_MMSS, + .name = "msm_mm_fab", + mmss_fabric_info, + ARRAY_SIZE(mmss_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_MM_FABRIC_ARB_0, + .nmasters = 10, + .nslaves = 2, + .ntieredslaves = 2, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8930_sys_fpb_pdata = { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .name = "msm_sys_fpb", + sys_fpb_fabric_info, + ARRAY_SIZE(sys_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8930_cpss_fpb_pdata = { + .id = MSM_BUS_FAB_CPSS_FPB, + .name = "msm_cpss_fpb", + cpss_fpb_fabric_info, + ARRAY_SIZE(cpss_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c new file mode 100644 index 00000000000..7ede23d46cd --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c @@ -0,0 +1,955 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define NMASTERS 45 +#define NSLAVES 75 +#define NFAB_8960 5 + +enum msm_bus_fabric_tiered_slave_type { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_0 = 1, + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_1, + MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM, + + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0 = 1, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_1, + MSM_BUS_TIERED_SLAVE_MM_IMEM, + + MSM_BUS_TIERED_SLAVE_EBI1_CH0 = 1, + MSM_BUS_TIERED_SLAVE_EBI1_CH1, + MSM_BUS_TIERED_SLAVE_KMPSS_L2, +}; + +enum msm_bus_8960_master_ports_type { + MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB = 0, + MSM_BUS_MASTER_PORT_SPS, + MSM_BUS_MASTER_PORT_ADM_PORT0, + MSM_BUS_MASTER_PORT_ADM_PORT1, + MSM_BUS_MASTER_PORT_LPASS_PROC, + MSM_BUS_MASTER_PORT_MSS, + MSM_BUS_SYSTEM_MASTER_PORT_UNUSED_6, + MSM_BUS_MASTER_PORT_RIVA, + MSM_BUS_MASTER_PORT_MSS_SW_PROC, + MSM_BUS_MASTER_PORT_MSS_FW_PROC, + MSM_BUS_MASTER_PORT_LPASS, + MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_MMSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI, + + MSM_BUS_MASTER_PORT_MDP_PORT0 = 0, + MSM_BUS_MASTER_PORT_MDP_PORT1, + MSM_BUS_MMSS_MASTER_PORT_UNUSED_2, + MSM_BUS_MASTER_PORT_ROTATOR, + MSM_BUS_MASTER_PORT_GRAPHICS_3D, + MSM_BUS_MASTER_PORT_JPEG_DEC, + MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0, + MSM_BUS_MASTER_PORT_VFE, + MSM_BUS_MASTER_PORT_VPE, + MSM_BUS_MASTER_PORT_JPEG_ENC, + MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1, + MSM_BUS_MMSS_MASTER_PORT_APPS_FAB, + MSM_BUS_MASTER_PORT_HD_CODEC_PORT0, + MSM_BUS_MASTER_PORT_HD_CODEC_PORT1, + + MSM_BUS_MASTER_PORT_KMPSS_M0 = 0, + MSM_BUS_MASTER_PORT_KMPSS_M1, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_1, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_1, + +}; + +enum msm_bus_8660_slave_ports_type { + MSM_BUS_MMSS_SLAVE_PORT_UNUSED_0 = 0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1, + MSM_BUS_SLAVE_PORT_MM_IMEM, + + MSM_BUS_SLAVE_PORT_EBI1_CH0 = 0, + MSM_BUS_SLAVE_PORT_EBI1_CH1, + MSM_BUS_SLAVE_PORT_KMPSS_L2, + MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB, + MSM_BUS_SLAVE_PORT_SYSTEM_FAB, + + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_0 = 0, + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_1, + MSM_BUS_SLAVE_PORT_SPS, + MSM_BUS_SLAVE_PORT_SYSTEM_IMEM, + MSM_BUS_SLAVE_PORT_CORESIGHT, + MSM_BUS_SLAVE_PORT_KMPSS, + MSM_BUS_SLAVE_PORT_MSS, + MSM_BUS_SLAVE_PORT_LPASS, + MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_MMSS_FPB, + MSM_BUS_SLAVE_PORT_RIVA, +}; + +static int tier2[] = {MSM_BUS_BW_TIER2,}; +static uint32_t master_iids[NMASTERS]; +static uint32_t slave_iids[NSLAVES]; + +static int mport_kmpss_m0[] = {MSM_BUS_MASTER_PORT_KMPSS_M0,}; +static int mport_kmpss_m1[] = {MSM_BUS_MASTER_PORT_KMPSS_M1,}; + +static int mmss_mport_apps_fab[] = {MSM_BUS_MMSS_MASTER_PORT_APPS_FAB,}; +static int system_mport_appss_fab[] = {MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB,}; +static int sport_ebi1_ch0[] = { + MSM_BUS_SLAVE_PORT_EBI1_CH0, + MSM_BUS_SLAVE_PORT_EBI1_CH1, +}; +static int sport_ebi1_ch1[] = {MSM_BUS_SLAVE_PORT_EBI1_CH1,}; +static int sport_kmpss_l2[] = {MSM_BUS_SLAVE_PORT_KMPSS_L2,}; +static int appss_sport_mmss_fab[] = {MSM_BUS_APPSS_SLAVE_PORT_MMSS_FAB,}; +static int sport_system_fab[] = {MSM_BUS_SLAVE_PORT_SYSTEM_FAB,}; + +static int tiered_slave_ebi1_ch0[] = { + MSM_BUS_TIERED_SLAVE_EBI1_CH0, + MSM_BUS_TIERED_SLAVE_EBI1_CH1, +}; +static int tiered_slave_ebi1_ch1[] = {MSM_BUS_TIERED_SLAVE_EBI1_CH1,}; + +static int tiered_slave_kmpss[] = {MSM_BUS_TIERED_SLAVE_KMPSS_L2,}; + +static struct msm_bus_node_info apps_fabric_info[] = { + { + .id = MSM_BUS_MASTER_AMPSS_M0, + .masterp = mport_kmpss_m0, + .num_mports = ARRAY_SIZE(mport_kmpss_m0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_AMPSS_M1, + .masterp = mport_kmpss_m1, + .num_mports = ARRAY_SIZE(mport_kmpss_m1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_EBI_CH0, + .slavep = sport_ebi1_ch0, + .num_sports = ARRAY_SIZE(sport_ebi1_ch0), + .tier = tiered_slave_ebi1_ch0, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch0), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_EBI_CH1, + .slavep = sport_ebi1_ch1, + .num_sports = ARRAY_SIZE(sport_ebi1_ch1), + .tier = tiered_slave_ebi1_ch1, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch1), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_AMPSS_L2, + .slavep = sport_kmpss_l2, + .num_sports = ARRAY_SIZE(sport_kmpss_l2), + .tier = tiered_slave_kmpss, + .num_tiers = ARRAY_SIZE(tiered_slave_kmpss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_MMSS, + .gateway = 1, + .slavep = appss_sport_mmss_fab, + .num_sports = ARRAY_SIZE(appss_sport_mmss_fab), + .masterp = mmss_mport_apps_fab, + .num_mports = ARRAY_SIZE(mmss_mport_apps_fab), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = sport_system_fab, + .num_sports = ARRAY_SIZE(sport_system_fab), + .masterp = system_mport_appss_fab, + .num_mports = ARRAY_SIZE(system_mport_appss_fab), + .buswidth = 8, + }, +}; + +static int mport_sps[] = {MSM_BUS_MASTER_PORT_SPS,}; +static int mport_adm_port0[] = {MSM_BUS_MASTER_PORT_ADM_PORT0,}; +static int mport_adm_port1[] = {MSM_BUS_MASTER_PORT_ADM_PORT1,}; +static int mport_mss[] = {MSM_BUS_MASTER_PORT_MSS,}; +static int mport_lpass_proc[] = {MSM_BUS_MASTER_PORT_LPASS_PROC,}; +static int system_mport_unused_6[] = {MSM_BUS_SYSTEM_MASTER_PORT_UNUSED_6,}; +static int mport_riva[] = {MSM_BUS_MASTER_PORT_RIVA,}; +static int mport_mss_sw_proc[] = {MSM_BUS_MASTER_PORT_MSS_SW_PROC,}; +static int mport_mss_fw_proc[] = {MSM_BUS_MASTER_PORT_MSS_FW_PROC,}; +static int mport_lpass[] = {MSM_BUS_MASTER_PORT_LPASS,}; +static int system_mport_mmss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_MMSS_FPB,}; +static int system_mport_adm_ahb_ci[] = {MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI,}; +static int appss_mport_fab_system[] = { + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_SYSTEM_1 +}; +static int mport_system_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB,}; +static int system_mport_cpss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB,}; + +static int system_sport_appss_fab[] = { + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_0, + MSM_BUS_SYSTEM_SLAVE_PORT_APPSS_FAB_1 +}; +static int system_sport_system_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB,}; +static int system_sport_cpss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB,}; +static int sport_sps[] = {MSM_BUS_SLAVE_PORT_SPS,}; +static int sport_system_imem[] = {MSM_BUS_SLAVE_PORT_SYSTEM_IMEM,}; +static int sport_coresight[] = {MSM_BUS_SLAVE_PORT_CORESIGHT,}; +static int sport_riva[] = {MSM_BUS_SLAVE_PORT_RIVA,}; +static int sport_kmpss[] = {MSM_BUS_SLAVE_PORT_KMPSS,}; +static int sport_mss[] = {MSM_BUS_SLAVE_PORT_MSS,}; +static int sport_lpass[] = {MSM_BUS_SLAVE_PORT_LPASS,}; +static int sport_mmss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_MMSS_FPB,}; + +static int tiered_slave_system_imem[] = {MSM_BUS_TIERED_SLAVE_SYSTEM_IMEM,}; +static int system_tiered_slave_fab_appss[] = { + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_0, + MSM_BUS_SYSTEM_TIERED_SLAVE_FAB_APPSS_1, +}; + +static struct msm_bus_node_info system_fabric_info[] = { + { + .id = MSM_BUS_MASTER_SPS, + .masterp = mport_sps, + .num_mports = ARRAY_SIZE(mport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT0, + .masterp = mport_adm_port0, + .num_mports = ARRAY_SIZE(mport_adm_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT1, + .masterp = mport_adm_port1, + .num_mports = ARRAY_SIZE(mport_adm_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS_PROC, + .masterp = mport_lpass_proc, + .num_mports = ARRAY_SIZE(mport_lpass_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS, + .masterp = mport_mss, + .num_mports = ARRAY_SIZE(mport_mss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SYSTEM_MASTER_UNUSED_6, + .masterp = system_mport_unused_6, + .num_mports = ARRAY_SIZE(system_mport_unused_6), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RIVA, + .masterp = mport_riva, + .num_mports = ARRAY_SIZE(mport_riva), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_SW_PROC, + .masterp = mport_mss_sw_proc, + .num_mports = ARRAY_SIZE(mport_mss_sw_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_FW_PROC, + .masterp = mport_mss_fw_proc, + .num_mports = ARRAY_SIZE(mport_mss_fw_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS, + .masterp = mport_lpass, + .num_mports = ARRAY_SIZE(mport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SYSTEM_MASTER_MMSS_FPB, + .masterp = system_mport_mmss_fpb, + .num_mports = ARRAY_SIZE(system_mport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM0_CI, + .masterp = system_mport_adm_ahb_ci, + .num_mports = ARRAY_SIZE(system_mport_adm_ahb_ci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = system_sport_appss_fab, + .num_sports = ARRAY_SIZE(system_sport_appss_fab), + .masterp = appss_mport_fab_system, + .num_mports = ARRAY_SIZE(appss_mport_fab_system), + .tier = system_tiered_slave_fab_appss, + .num_tiers = ARRAY_SIZE(system_tiered_slave_fab_appss), + .buswidth = 8, + }, + { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_FAB_CPSS_FPB, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_SLAVE_SPS, + .slavep = sport_sps, + .num_sports = ARRAY_SIZE(sport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "dfab_clk", + .slaveclk[ACTIVE_CTX] = "dfab_a_clk", + }, + { + .id = MSM_BUS_SLAVE_SYSTEM_IMEM, + .slavep = sport_system_imem, + .num_sports = ARRAY_SIZE(sport_system_imem), + .tier = tiered_slave_system_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_system_imem), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_CORESIGHT, + .slavep = sport_coresight, + .num_sports = ARRAY_SIZE(sport_coresight), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_RIVA, + .slavep = sport_riva, + .num_sports = ARRAY_SIZE(sport_riva), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_AMPSS, + .slavep = sport_kmpss, + .num_sports = ARRAY_SIZE(sport_kmpss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_MSS, + .slavep = sport_mss, + .num_sports = ARRAY_SIZE(sport_mss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_LPASS, + .slavep = sport_lpass, + .num_sports = ARRAY_SIZE(sport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SYSTEM_SLAVE_MMSS_FPB, + .slavep = sport_mmss_fpb, + .num_sports = ARRAY_SIZE(sport_mmss_fpb), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, +}; + +static int mport_mdp[] = { + MSM_BUS_MASTER_PORT_MDP_PORT0, + MSM_BUS_MASTER_PORT_MDP_PORT1, +}; +static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,}; +static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,}; +static int mport_graphics_3d[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D,}; +static int mport_jpeg_dec[] = {MSM_BUS_MASTER_PORT_JPEG_DEC,}; +static int mport_graphics_2d_core0[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0,}; +static int mport_vfe[] = {MSM_BUS_MASTER_PORT_VFE,}; +static int mport_vpe[] = {MSM_BUS_MASTER_PORT_VPE,}; +static int mport_jpeg_enc[] = {MSM_BUS_MASTER_PORT_JPEG_ENC,}; +static int mport_graphics_2d_core1[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1,}; +static int mport_hd_codec_port0[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,}; +static int mport_hd_codec_port1[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT1,}; +static int appss_mport_fab_mmss[] = { + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0, + MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_1 +}; + +static int mmss_sport_apps_fab[] = { + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0, + MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1 +}; +static int sport_mm_imem[] = {MSM_BUS_SLAVE_PORT_MM_IMEM,}; + +static int mmss_tiered_slave_fab_apps[] = { + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0, + MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_1, +}; +static int tiered_slave_mm_imem[] = {MSM_BUS_TIERED_SLAVE_MM_IMEM,}; + + +static struct msm_bus_node_info mmss_fabric_info[] = { + { + .id = MSM_BUS_MASTER_MDP_PORT0, + .masterp = mport_mdp, + .num_mports = ARRAY_SIZE(mport_mdp), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MDP_PORT1, + .masterp = mport_mdp1, + .num_mports = ARRAY_SIZE(mport_mdp1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ROTATOR, + .masterp = mport_rotator, + .num_mports = ARRAY_SIZE(mport_rotator), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_3D, + .masterp = mport_graphics_3d, + .num_mports = ARRAY_SIZE(mport_graphics_3d), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_DEC, + .masterp = mport_jpeg_dec, + .num_mports = ARRAY_SIZE(mport_jpeg_dec), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_GRAPHICS_2D_CORE0, + .masterp = mport_graphics_2d_core0, + .num_mports = ARRAY_SIZE(mport_graphics_2d_core0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VFE, + .masterp = mport_vfe, + .num_mports = ARRAY_SIZE(mport_vfe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_VPE, + .masterp = mport_vpe, + .num_mports = ARRAY_SIZE(mport_vpe), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_JPEG_ENC, + .masterp = mport_jpeg_enc, + .num_mports = ARRAY_SIZE(mport_jpeg_enc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + /* This port has been added for V2. It is absent in V1 */ + { + .id = MSM_BUS_MASTER_GRAPHICS_2D_CORE1, + .masterp = mport_graphics_2d_core1, + .num_mports = ARRAY_SIZE(mport_graphics_2d_core1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_HD_CODEC_PORT0, + .masterp = mport_hd_codec_port0, + .num_mports = ARRAY_SIZE(mport_hd_codec_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_HD_CODEC_PORT1, + .masterp = mport_hd_codec_port1, + .num_mports = ARRAY_SIZE(mport_hd_codec_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_APPSS, + .gateway = 1, + .slavep = mmss_sport_apps_fab, + .num_sports = ARRAY_SIZE(mmss_sport_apps_fab), + .masterp = appss_mport_fab_mmss, + .num_mports = ARRAY_SIZE(appss_mport_fab_mmss), + .tier = mmss_tiered_slave_fab_apps, + .num_tiers = ARRAY_SIZE(mmss_tiered_slave_fab_apps), + .buswidth = 16, + }, + { + .id = MSM_BUS_SLAVE_MM_IMEM, + .slavep = sport_mm_imem, + .num_sports = ARRAY_SIZE(sport_mm_imem), + .tier = tiered_slave_mm_imem, + .num_tiers = ARRAY_SIZE(tiered_slave_mm_imem), + .buswidth = 8, + }, +}; + +static struct msm_bus_node_info sys_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_MASTER_SPDM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_RPM, + .ahb = 1, + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_SLAVE_SPDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_RPM_MSG_RAM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MPM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_B, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC1_SSBI1_C, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_A, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_PMIC2_SSBI2_B, + .buswidth = 4, + .ahb = 1, + }, +}; + +static struct msm_bus_node_info cpss_fpb_fabric_info[] = { + { + .id = MSM_BUS_FAB_SYSTEM, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_UART, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI1_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI2_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI3_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI4_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI5_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI6_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI7_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI8_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI9_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI10_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI11_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_GSBI12_QUP, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_NAND, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS0, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS3, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS4, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_EBI2_CS5, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS1, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_USB_FS2, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_TSIF, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TSSC, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PDM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_DIMEM, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_TCSR, + .buswidth = 8, + .ahb = 1, + }, + { + .id = MSM_BUS_SLAVE_MSM_PRNG, + .buswidth = 4, + .ahb = 1, + }, +}; + +static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration + *fabreg, int fabid) +{ + int i; + for (i = 0; i < fabreg->len; i++) { + if (!fabreg->info[i].gateway) { + fabreg->info[i].priv_id = fabid + fabreg->info[i].id; + if (fabreg->info[i].id < SLAVE_ID_KEY) + master_iids[fabreg->info[i].id] = + fabreg->info[i].priv_id; + else + slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)] + = fabreg->info[i].priv_id; + } else + fabreg->info[i].priv_id = fabreg->info[i].id; + } +} + +static int msm_bus_board_8960_get_iid(int id) +{ + if ((id < SLAVE_ID_KEY && id >= NMASTERS) || + id >= (SLAVE_ID_KEY + NSLAVES)) { + MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id); + return -EINVAL; + } + + return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] : + slave_iids[id - SLAVE_ID_KEY]), id); +} + +static struct msm_bus_board_algorithm msm_bus_board_algo = { + .board_nfab = NFAB_8960, + .get_iid = msm_bus_board_8960_get_iid, + .assign_iids = msm_bus_board_assign_iids, +}; + +struct msm_bus_fabric_registration msm_bus_8960_apps_fabric_pdata = { + .id = MSM_BUS_FAB_APPSS, + .name = "msm_apps_fab", + .info = apps_fabric_info, + .len = ARRAY_SIZE(apps_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_APPS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_APPS_FABRIC_ARB_0, + .nmasters = 6, + .nslaves = 5, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8960_sys_fabric_pdata = { + .id = MSM_BUS_FAB_SYSTEM, + .name = "msm_sys_fab", + system_fabric_info, + ARRAY_SIZE(system_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0, + .nmasters = 15, + .nslaves = 12, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8960_mm_fabric_pdata = { + .id = MSM_BUS_FAB_MMSS, + .name = "msm_mm_fab", + mmss_fabric_info, + ARRAY_SIZE(mmss_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_MM_FABRIC_ARB_0, + .nmasters = 14, + .nslaves = 4, + .ntieredslaves = 3, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata = { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .name = "msm_sys_fpb", + sys_fpb_fabric_info, + ARRAY_SIZE(sys_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_8960_cpss_fpb_pdata = { + .id = MSM_BUS_FAB_CPSS_FPB, + .name = "msm_cpss_fpb", + cpss_fpb_fabric_info, + ARRAY_SIZE(cpss_fpb_fabric_info), + .ahb = 1, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +int msm_bus_board_rpm_get_il_ids(uint16_t id[]) +{ + id[0] = MSM_RPM_STATUS_ID_EBI1_CH0_RANGE; + id[1] = MSM_RPM_STATUS_ID_EBI1_CH1_RANGE; + return 0; +} diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c new file mode 100644 index 00000000000..34cb2db2552 --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c @@ -0,0 +1,313 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define NMASTERS 14 +#define NSLAVES 12 +#define NFAB_9615 2 + +enum msm_bus_fabric_tiered_slave_type { + MSM_BUS_TIERED_SLAVE_EBI1_CH0 = 1, +}; + +enum msm_bus_9615_master_ports_type { + MSM_BUS_MASTER_PORT_SPS = 0, + MSM_BUS_MASTER_PORT_APSS_PROC, + MSM_BUS_MASTER_PORT_ADM_PORT0, + MSM_BUS_MASTER_PORT_ADM_PORT1, + MSM_BUS_MASTER_PORT_LPASS_PROC, + MSM_BUS_MASTER_PORT_MSS, + MSM_BUS_MASTER_PORT_MSS_SW_PROC, + MSM_BUS_MASTER_PORT_MSS_FW_PROC, + MSM_BUS_MASTER_PORT_LPASS, + MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB, + MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI, +}; + +enum msm_bus_9615_slave_ports_type { + MSM_BUS_SLAVE_PORT_SPS = 0, + MSM_BUS_SLAVE_PORT_EBI1_CH0, + MSM_BUS_SLAVE_PORT_APSS_L2, + MSM_BUS_SLAVE_PORT_SYSTEM_IMEM, + MSM_BUS_SLAVE_PORT_CORESIGHT, + MSM_BUS_SLAVE_PORT_APSS, + MSM_BUS_SLAVE_PORT_MSS, + MSM_BUS_SLAVE_PORT_LPASS, + MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB, + MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB, +}; + +static int tier2[] = {MSM_BUS_BW_TIER2,}; +static uint32_t master_iids[NMASTERS]; +static uint32_t slave_iids[NSLAVES]; + +static int sport_ebi1_ch0[] = {MSM_BUS_SLAVE_PORT_EBI1_CH0,}; +static int sport_apss_l2[] = {MSM_BUS_SLAVE_PORT_APSS_L2,}; + +static int tiered_slave_ebi1_ch0[] = {MSM_BUS_TIERED_SLAVE_EBI1_CH0,}; + +static int mport_sps[] = {MSM_BUS_MASTER_PORT_SPS,}; +static int mport_apss_proc[] = {MSM_BUS_MASTER_PORT_APSS_PROC,}; +static int mport_adm_port0[] = {MSM_BUS_MASTER_PORT_ADM_PORT0,}; +static int mport_adm_port1[] = {MSM_BUS_MASTER_PORT_ADM_PORT1,}; +static int mport_mss[] = {MSM_BUS_MASTER_PORT_MSS,}; +static int mport_lpass_proc[] = {MSM_BUS_MASTER_PORT_LPASS_PROC,}; +static int mport_mss_sw_proc[] = {MSM_BUS_MASTER_PORT_MSS_SW_PROC,}; +static int mport_mss_fw_proc[] = {MSM_BUS_MASTER_PORT_MSS_FW_PROC,}; +static int mport_lpass[] = {MSM_BUS_MASTER_PORT_LPASS,}; +static int system_mport_adm_ahb_ci[] = {MSM_BUS_SYSTEM_MASTER_PORT_ADM_AHB_CI,}; +static int mport_system_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_SYSTEM_FPB,}; +static int system_mport_cpss_fpb[] = {MSM_BUS_SYSTEM_MASTER_PORT_CPSS_FPB,}; + +static int system_sport_system_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_SYSTEM_FPB,}; +static int system_sport_cpss_fpb[] = {MSM_BUS_SYSTEM_SLAVE_PORT_CPSS_FPB,}; +static int sport_sps[] = {MSM_BUS_SLAVE_PORT_SPS,}; +static int sport_system_imem[] = {MSM_BUS_SLAVE_PORT_SYSTEM_IMEM,}; +static int sport_coresight[] = {MSM_BUS_SLAVE_PORT_CORESIGHT,}; +static int sport_apss[] = {MSM_BUS_SLAVE_PORT_APSS,}; +static int sport_mss[] = {MSM_BUS_SLAVE_PORT_MSS,}; +static int sport_lpass[] = {MSM_BUS_SLAVE_PORT_LPASS,}; + +static struct msm_bus_node_info system_fabric_info[] = { + { + .id = MSM_BUS_MASTER_SPS, + .masterp = mport_sps, + .num_mports = ARRAY_SIZE(mport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT0, + .masterp = mport_adm_port0, + .num_mports = ARRAY_SIZE(mport_adm_port0), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM_PORT1, + .masterp = mport_adm_port1, + .num_mports = ARRAY_SIZE(mport_adm_port1), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS_PROC, + .masterp = mport_lpass_proc, + .num_mports = ARRAY_SIZE(mport_lpass_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS, + .masterp = mport_mss, + .num_mports = ARRAY_SIZE(mport_mss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_AMPSS_M0, + .masterp = mport_apss_proc, + .num_mports = ARRAY_SIZE(mport_apss_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_SW_PROC, + .masterp = mport_mss_sw_proc, + .num_mports = ARRAY_SIZE(mport_mss_sw_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_MSS_FW_PROC, + .masterp = mport_mss_fw_proc, + .num_mports = ARRAY_SIZE(mport_mss_fw_proc), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_LPASS, + .masterp = mport_lpass, + .num_mports = ARRAY_SIZE(mport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_MASTER_ADM0_CI, + .masterp = system_mport_adm_ahb_ci, + .num_mports = ARRAY_SIZE(system_mport_adm_ahb_ci), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + }, + { + .id = MSM_BUS_FAB_SYSTEM_FPB, + .gateway = 1, + .slavep = system_sport_system_fpb, + .num_sports = ARRAY_SIZE(system_sport_system_fpb), + .masterp = mport_system_fpb, + .num_mports = ARRAY_SIZE(mport_system_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_FAB_CPSS_FPB, + .gateway = 1, + .slavep = system_sport_cpss_fpb, + .num_sports = ARRAY_SIZE(system_sport_cpss_fpb), + .masterp = system_mport_cpss_fpb, + .num_mports = ARRAY_SIZE(system_mport_cpss_fpb), + .buswidth = 4, + }, + { + .id = MSM_BUS_SLAVE_SPS, + .slavep = sport_sps, + .num_sports = ARRAY_SIZE(sport_sps), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "dfab_clk", + .slaveclk[ACTIVE_CTX] = "dfab_a_clk", + }, + { + .id = MSM_BUS_SLAVE_EBI_CH0, + .slavep = sport_ebi1_ch0, + .num_sports = ARRAY_SIZE(sport_ebi1_ch0), + .tier = tiered_slave_ebi1_ch0, + .num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch0), + .buswidth = 8, + .slaveclk[DUAL_CTX] = "mem_clk", + .slaveclk[ACTIVE_CTX] = "mem_a_clk", + }, + { + .id = MSM_BUS_SLAVE_SYSTEM_IMEM, + .slavep = sport_system_imem, + .num_sports = ARRAY_SIZE(sport_system_imem), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_CORESIGHT, + .slavep = sport_coresight, + .num_sports = ARRAY_SIZE(sport_coresight), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_AMPSS, + .slavep = sport_apss, + .num_sports = ARRAY_SIZE(sport_apss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_AMPSS_L2, + .slavep = sport_apss_l2, + .num_sports = ARRAY_SIZE(sport_apss_l2), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_MSS, + .slavep = sport_mss, + .num_sports = ARRAY_SIZE(sport_mss), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, + { + .id = MSM_BUS_SLAVE_LPASS, + .slavep = sport_lpass, + .num_sports = ARRAY_SIZE(sport_lpass), + .tier = tier2, + .num_tiers = ARRAY_SIZE(tier2), + .buswidth = 8, + }, +}; + +static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration + *fabreg, int fabid) +{ + int i; + for (i = 0; i < fabreg->len; i++) { + if (!fabreg->info[i].gateway) { + fabreg->info[i].priv_id = fabid + fabreg->info[i].id; + if (fabreg->info[i].id < SLAVE_ID_KEY) + master_iids[fabreg->info[i].id] = + fabreg->info[i].priv_id; + else + slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)] + = fabreg->info[i].priv_id; + } else + fabreg->info[i].priv_id = fabreg->info[i].id; + } +} + +static int msm_bus_board_9615_get_iid(int id) +{ + if ((id < SLAVE_ID_KEY && id >= NMASTERS) || + id >= (SLAVE_ID_KEY + NSLAVES)) { + MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id); + return -EINVAL; + } + + return ((id < SLAVE_ID_KEY) ? master_iids[id] : slave_iids[id - + SLAVE_ID_KEY]); +} + +static struct msm_bus_board_algorithm msm_bus_board_algo = { + .board_nfab = NFAB_9615, + .get_iid = msm_bus_board_9615_get_iid, + .assign_iids = msm_bus_board_assign_iids, +}; + +struct msm_bus_fabric_registration msm_bus_9615_sys_fabric_pdata = { + .id = MSM_BUS_FAB_SYSTEM, + .name = "msm_sys_fab", + system_fabric_info, + ARRAY_SIZE(system_fabric_info), + .ahb = 0, + .fabclk[DUAL_CTX] = "bus_clk", + .fabclk[ACTIVE_CTX] = "bus_a_clk", + .haltid = MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0, + .offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0, + .nmasters = 12, + .nslaves = 10, + .ntieredslaves = 1, + .board_algo = &msm_bus_board_algo, +}; + +struct msm_bus_fabric_registration msm_bus_9615_def_fab_pdata = { + .id = MSM_BUS_FAB_DEFAULT, + .name = "msm_def_fab", + .ahb = 1, + .nmasters = 0, + .nslaves = 0, + .ntieredslaves = 0, + .board_algo = &msm_bus_board_algo, +}; + +int msm_bus_board_rpm_get_il_ids(uint16_t id[]) +{ + return -ENXIO; +} diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_config.c b/arch/arm/mach-msm/msm_bus/msm_bus_config.c new file mode 100644 index 00000000000..28f30736798 --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_config.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +static DEFINE_MUTEX(msm_bus_config_lock); + +/** + * msm_bus_axi_porthalt() - Halt the given axi master port + * @master_port: AXI Master port to be halted + */ +int msm_bus_axi_porthalt(int master_port) +{ + int ret = 0; + int priv_id; + struct msm_bus_fabric_device *fabdev; + + priv_id = msm_bus_board_get_iid(master_port); + MSM_BUS_DBG("master_port: %d iid: %d fabid%d\n", + master_port, priv_id, GET_FABID(priv_id)); + fabdev = msm_bus_get_fabric_device(GET_FABID(priv_id)); + if (IS_ERR(fabdev)) { + MSM_BUS_ERR("Fabric device not found for mport: %d\n", + master_port); + return -ENODEV; + } + mutex_lock(&msm_bus_config_lock); + ret = fabdev->algo->port_halt(fabdev, priv_id); + mutex_unlock(&msm_bus_config_lock); + return ret; +} +EXPORT_SYMBOL(msm_bus_axi_porthalt); + +/** + * msm_bus_axi_portunhalt() - Unhalt the given axi master port + * @master_port: AXI Master port to be unhalted + */ +int msm_bus_axi_portunhalt(int master_port) +{ + int ret = 0; + int priv_id; + struct msm_bus_fabric_device *fabdev; + + priv_id = msm_bus_board_get_iid(master_port); + MSM_BUS_DBG("master_port: %d iid: %d fabid: %d\n", + master_port, priv_id, GET_FABID(priv_id)); + fabdev = msm_bus_get_fabric_device(GET_FABID(priv_id)); + if (IS_ERR(fabdev)) { + MSM_BUS_ERR("Fabric device not found for mport: %d\n", + master_port); + return -ENODEV; + } + mutex_lock(&msm_bus_config_lock); + ret = fabdev->algo->port_unhalt(fabdev, priv_id); + mutex_unlock(&msm_bus_config_lock); + return ret; +} +EXPORT_SYMBOL(msm_bus_axi_portunhalt); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.c b/arch/arm/mach-msm/msm_bus/msm_bus_core.c new file mode 100644 index 00000000000..4d73b0357f5 --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +static atomic_t num_fab = ATOMIC_INIT(0); + +int msm_bus_get_num_fab(void) +{ + return atomic_read(&num_fab); +} + +int msm_bus_device_match(struct device *dev, void* id) +{ + struct msm_bus_fabric_device *fabdev = to_msm_bus_fabric_device(dev); + + if (!fabdev) { + MSM_BUS_WARN("Fabric %p returning 0\n", fabdev); + return 0; + } + return (fabdev->id == (int)id); +} + +struct bus_type msm_bus_type = { + .name = "msm-bus-type", +}; +EXPORT_SYMBOL(msm_bus_type); + +/** + * msm_bus_get_fabric_device() - This function is used to search for + * the fabric device on the bus + * @fabid: Fabric id + * Function returns: Pointer to the fabric device + */ +struct msm_bus_fabric_device *msm_bus_get_fabric_device(int fabid) +{ + struct device *dev; + struct msm_bus_fabric_device *fabric; + dev = bus_find_device(&msm_bus_type, NULL, (void *)fabid, + msm_bus_device_match); + fabric = to_msm_bus_fabric_device(dev); + return fabric; +} + +/** + * msm_bus_fabric_device_register() - Registers a fabric on msm bus + * @fabdev: Fabric device to be registered + */ +int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabdev) +{ + int ret = 0; + fabdev->dev.bus = &msm_bus_type; + ret = dev_set_name(&fabdev->dev, fabdev->name); + if (ret) { + MSM_BUS_ERR("error setting dev name\n"); + goto err; + } + ret = device_register(&fabdev->dev); + if (ret < 0) { + MSM_BUS_ERR("error registering device%d %s\n", + ret, fabdev->name); + goto err; + } + atomic_inc(&num_fab); +err: + return ret; +} + +/** + * msm_bus_fabric_device_unregister() - Unregisters the fabric + * devices from the msm bus + */ +void msm_bus_fabric_device_unregister(struct msm_bus_fabric_device *fabdev) +{ + device_unregister(&fabdev->dev); + atomic_dec(&num_fab); +} + +static void __exit msm_bus_exit(void) +{ + bus_unregister(&msm_bus_type); +} + +static int __init msm_bus_init(void) +{ + int retval = 0; + retval = bus_register(&msm_bus_type); + if (retval) + MSM_BUS_ERR("bus_register error! %d\n", + retval); + return retval; +} +postcore_initcall(msm_bus_init); +module_exit(msm_bus_exit); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.2"); +MODULE_ALIAS("platform:msm_bus"); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h new file mode 100644 index 00000000000..341fda8ffeb --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h @@ -0,0 +1,217 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_BUS_CORE_H +#define _ARCH_ARM_MACH_MSM_BUS_CORE_H + +#include +#include +#include +#include +#include +#include + +#define MSM_BUS_DBG(msg, ...) \ + pr_debug(msg, ## __VA_ARGS__) +#define MSM_BUS_ERR(msg, ...) \ + pr_err(msg, ## __VA_ARGS__) +#define MSM_BUS_WARN(msg, ...) \ + pr_warn(msg, ## __VA_ARGS__) +#define MSM_FAB_ERR(msg, ...) \ + dev_err(&fabric->fabdev.dev, msg, ## __VA_ARGS__) + +#define IS_MASTER_VALID(mas) \ + (((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \ + ? 1 : 0) +#define IS_SLAVE_VALID(slv) \ + (((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0) + +#define INTERLEAVED_BW(fab_pdata, bw, ports) \ + ((fab_pdata->il_flag) ? DIV_ROUND_UP((bw), (ports)) : (bw)) +#define INTERLEAVED_VAL(fab_pdata, n) \ + ((fab_pdata->il_flag) ? (n) : 1) + +enum msm_bus_dbg_op_type { + MSM_BUS_DBG_UNREGISTER = -2, + MSM_BUS_DBG_REGISTER, + MSM_BUS_DBG_OP = 1, +}; + +extern struct bus_type msm_bus_type; + +struct msm_bus_node_info { + unsigned int id; + unsigned int priv_id; + int gateway; + int *masterp; + int num_mports; + int *slavep; + int num_sports; + int *tier; + int num_tiers; + int ahb; + int hw_sel; + const char *slaveclk[NUM_CTX]; + const char *memclk; + unsigned int buswidth; + unsigned int ws; + unsigned int mode; +}; + +struct path_node { + unsigned long clk[NUM_CTX]; + unsigned long bw[NUM_CTX]; + unsigned long *sel_clk; + unsigned long *sel_bw; + int next; +}; + +struct msm_bus_link_info { + unsigned long clk[NUM_CTX]; + unsigned long *sel_clk; + unsigned long memclk; + long bw[NUM_CTX]; + long *sel_bw; + int *tier; + int num_tiers; +}; + +struct nodeclk { + struct clk *clk; + unsigned long rate; + bool dirty; + bool enable; +}; + +struct msm_bus_inode_info { + struct msm_bus_node_info *node_info; + unsigned long max_bw; + unsigned long max_clk; + struct msm_bus_link_info link_info; + int num_pnodes; + struct path_node *pnode; + int commit_index; + struct nodeclk nodeclk[NUM_CTX]; + struct nodeclk memclk; + void *hw_data; +}; + +struct msm_bus_hw_algorithm { + int (*allocate_commit_data)(struct msm_bus_fabric_registration + *fab_pdata, void **cdata, int ctx); + void *(*allocate_hw_data)(struct platform_device *pdev, + struct msm_bus_fabric_registration *fab_pdata); + void (*node_init)(void *hw_data, struct msm_bus_inode_info *info); + void (*free_commit_data)(void *cdata); + void (*update_bw)(struct msm_bus_inode_info *hop, + struct msm_bus_inode_info *info, + struct msm_bus_fabric_registration *fab_pdata, + void *sel_cdata, int *master_tiers, + long int add_bw); + void (*fill_cdata_buffer)(int *curr, char *buf, const int max_size, + void *cdata, int nmasters, int nslaves, int ntslaves); + int (*commit)(struct msm_bus_fabric_registration + *fab_pdata, void *hw_data, void **cdata); + int (*port_unhalt)(uint32_t haltid, uint8_t mport); + int (*port_halt)(uint32_t haltid, uint8_t mport); +}; + +struct msm_bus_fabric_device { + int id; + const char *name; + struct device dev; + const struct msm_bus_fab_algorithm *algo; + const struct msm_bus_board_algorithm *board_algo; + struct msm_bus_hw_algorithm hw_algo; + int visited; +}; +#define to_msm_bus_fabric_device(d) container_of(d, \ + struct msm_bus_fabric_device, d) + + +struct msm_bus_fab_algorithm { + int (*update_clks)(struct msm_bus_fabric_device *fabdev, + struct msm_bus_inode_info *pme, int index, + unsigned long curr_clk, unsigned long req_clk, + unsigned long bwsum, int flag, int ctx, + unsigned int cl_active_flag); + int (*port_halt)(struct msm_bus_fabric_device *fabdev, int portid); + int (*port_unhalt)(struct msm_bus_fabric_device *fabdev, int portid); + int (*commit)(struct msm_bus_fabric_device *fabdev); + struct msm_bus_inode_info *(*find_node)(struct msm_bus_fabric_device + *fabdev, int id); + struct msm_bus_inode_info *(*find_gw_node)(struct msm_bus_fabric_device + *fabdev, int id); + struct list_head *(*get_gw_list)(struct msm_bus_fabric_device *fabdev); + void (*update_bw)(struct msm_bus_fabric_device *fabdev, struct + msm_bus_inode_info * hop, struct msm_bus_inode_info *info, + long int add_bw, int *master_tiers, int ctx); +}; + +struct msm_bus_board_algorithm { + const int board_nfab; + void (*assign_iids)(struct msm_bus_fabric_registration *fabreg, + int fabid); + int (*get_iid)(int id); +}; + +/** + * Used to store the list of fabrics and other info to be + * maintained outside the fabric structure. + * Used while calculating path, and to find fabric ptrs + */ +struct msm_bus_fabnodeinfo { + struct list_head list; + struct msm_bus_inode_info *info; +}; + +struct msm_bus_client { + int id; + struct msm_bus_scale_pdata *pdata; + int *src_pnode; + int curr; +}; + +int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabric); +void msm_bus_fabric_device_unregister(struct msm_bus_fabric_device *fabric); +struct msm_bus_fabric_device *msm_bus_get_fabric_device(int fabid); +int msm_bus_get_num_fab(void); + +void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, const int max_size, + void *cdata, int nmasters, int nslaves, int ntslaves); + +int msm_bus_hw_fab_init(struct msm_bus_fabric_registration *pdata, + struct msm_bus_hw_algorithm *hw_algo); +int msm_bus_rpm_hw_init(struct msm_bus_fabric_registration *pdata, + struct msm_bus_hw_algorithm *hw_algo); +int msm_bus_noc_hw_init(struct msm_bus_fabric_registration *pdata, + struct msm_bus_hw_algorithm *hw_algo); +int msm_bus_bimc_hw_init(struct msm_bus_fabric_registration *pdata, + struct msm_bus_hw_algorithm *hw_algo); +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MSM_BUS_SCALING) +void msm_bus_dbg_client_data(struct msm_bus_scale_pdata *pdata, int index, + uint32_t cl); +void msm_bus_dbg_commit_data(const char *fabname, void *cdata, + int nmasters, int nslaves, int ntslaves, int op); +#else +static inline void msm_bus_dbg_client_data(struct msm_bus_scale_pdata *pdata, + int index, uint32_t cl) +{ +} +static inline void msm_bus_dbg_commit_data(const char *fabname, + void *cdata, int nmasters, int nslaves, int ntslaves, + int op) +{ +} +#endif + +#endif /*_ARCH_ARM_MACH_MSM_BUS_CORE_H*/ diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c new file mode 100644 index 00000000000..76f85c6385f --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c @@ -0,0 +1,715 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +#define MAX_BUFF_SIZE 4096 +#define FILL_LIMIT 128 + +static struct dentry *clients; +static struct dentry *dir; +static DEFINE_MUTEX(msm_bus_dbg_fablist_lock); +struct msm_bus_dbg_state { + uint32_t cl; + uint8_t enable; + uint8_t current_index; +} clstate; + +struct msm_bus_cldata { + const struct msm_bus_scale_pdata *pdata; + int index; + uint32_t clid; + int size; + struct dentry *file; + struct list_head list; + char buffer[MAX_BUFF_SIZE]; +}; + +struct msm_bus_fab_list { + const char *name; + int size; + struct dentry *file; + struct list_head list; + char buffer[MAX_BUFF_SIZE]; +}; + +LIST_HEAD(fabdata_list); +LIST_HEAD(cl_list); + +/** + * The following structures and funtions are used for + * the test-client which can be created at run-time. + */ + +static struct msm_bus_vectors init_vectors[1]; +static struct msm_bus_vectors current_vectors[1]; +static struct msm_bus_vectors requested_vectors[1]; + +static struct msm_bus_paths shell_client_usecases[] = { + { + .num_paths = ARRAY_SIZE(init_vectors), + .vectors = init_vectors, + }, + { + .num_paths = ARRAY_SIZE(current_vectors), + .vectors = current_vectors, + }, + { + .num_paths = ARRAY_SIZE(requested_vectors), + .vectors = requested_vectors, + }, +}; + +static struct msm_bus_scale_pdata shell_client = { + .usecase = shell_client_usecases, + .num_usecases = ARRAY_SIZE(shell_client_usecases), + .name = "test-client", +}; + +static void msm_bus_dbg_init_vectors(void) +{ + init_vectors[0].src = -1; + init_vectors[0].dst = -1; + init_vectors[0].ab = 0; + init_vectors[0].ib = 0; + current_vectors[0].src = -1; + current_vectors[0].dst = -1; + current_vectors[0].ab = 0; + current_vectors[0].ib = 0; + requested_vectors[0].src = -1; + requested_vectors[0].dst = -1; + requested_vectors[0].ab = 0; + requested_vectors[0].ib = 0; + clstate.enable = 0; + clstate.current_index = 0; +} + +static int msm_bus_dbg_update_cl_request(uint32_t cl) +{ + int ret = 0; + + if (clstate.current_index < 2) + clstate.current_index = 2; + else { + clstate.current_index = 1; + current_vectors[0].ab = requested_vectors[0].ab; + current_vectors[0].ib = requested_vectors[0].ib; + } + + if (clstate.enable) { + MSM_BUS_DBG("Updating request for shell client, index: %d\n", + clstate.current_index); + ret = msm_bus_scale_client_update_request(clstate.cl, + clstate.current_index); + } else + MSM_BUS_DBG("Enable bit not set. Skipping update request\n"); + + return ret; +} + +static void msm_bus_dbg_unregister_client(uint32_t cl) +{ + MSM_BUS_DBG("Unregistering shell client\n"); + msm_bus_scale_unregister_client(clstate.cl); + clstate.cl = 0; +} + +static uint32_t msm_bus_dbg_register_client(void) +{ + int ret = 0; + + if (init_vectors[0].src != requested_vectors[0].src) { + MSM_BUS_DBG("Shell client master changed. Unregistering\n"); + msm_bus_dbg_unregister_client(clstate.cl); + } + if (init_vectors[0].dst != requested_vectors[0].dst) { + MSM_BUS_DBG("Shell client slave changed. Unregistering\n"); + msm_bus_dbg_unregister_client(clstate.cl); + } + + if (!clstate.enable) { + MSM_BUS_DBG("Enable bit not set, skipping registration: cl " + "%d\n", clstate.cl); + return 0; + } + + if (clstate.cl) { + MSM_BUS_DBG("Client registered, skipping registration\n"); + return 0; + } + + current_vectors[0].src = init_vectors[0].src; + requested_vectors[0].src = init_vectors[0].src; + current_vectors[0].dst = init_vectors[0].dst; + requested_vectors[0].dst = init_vectors[0].dst; + MSM_BUS_DBG("Registering shell client\n"); + ret = msm_bus_scale_register_client(&shell_client); + return ret; +} + +static int msm_bus_dbg_mas_get(void *data, u64 *val) +{ + *val = init_vectors[0].src; + MSM_BUS_DBG("Get master: %llu\n", *val); + return 0; +} + +static int msm_bus_dbg_mas_set(void *data, u64 val) +{ + init_vectors[0].src = val; + MSM_BUS_DBG("Set master: %llu\n", val); + clstate.cl = msm_bus_dbg_register_client(); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(shell_client_mas_fops, msm_bus_dbg_mas_get, + msm_bus_dbg_mas_set, "%llu\n"); + +static int msm_bus_dbg_slv_get(void *data, u64 *val) +{ + *val = init_vectors[0].dst; + MSM_BUS_DBG("Get slave: %llu\n", *val); + return 0; +} + +static int msm_bus_dbg_slv_set(void *data, u64 val) +{ + init_vectors[0].dst = val; + MSM_BUS_DBG("Set slave: %llu\n", val); + clstate.cl = msm_bus_dbg_register_client(); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(shell_client_slv_fops, msm_bus_dbg_slv_get, + msm_bus_dbg_slv_set, "%llu\n"); + +static int msm_bus_dbg_ab_get(void *data, u64 *val) +{ + *val = requested_vectors[0].ab; + MSM_BUS_DBG("Get ab: %llu\n", *val); + return 0; +} + +static int msm_bus_dbg_ab_set(void *data, u64 val) +{ + requested_vectors[0].ab = val; + MSM_BUS_DBG("Set ab: %llu\n", val); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(shell_client_ab_fops, msm_bus_dbg_ab_get, + msm_bus_dbg_ab_set, "%llu\n"); + +static int msm_bus_dbg_ib_get(void *data, u64 *val) +{ + *val = requested_vectors[0].ib; + MSM_BUS_DBG("Get ib: %llu\n", *val); + return 0; +} + +static int msm_bus_dbg_ib_set(void *data, u64 val) +{ + requested_vectors[0].ib = val; + MSM_BUS_DBG("Set ib: %llu\n", val); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(shell_client_ib_fops, msm_bus_dbg_ib_get, + msm_bus_dbg_ib_set, "%llu\n"); + +static int msm_bus_dbg_en_get(void *data, u64 *val) +{ + *val = clstate.enable; + MSM_BUS_DBG("Get enable: %llu\n", *val); + return 0; +} + +static int msm_bus_dbg_en_set(void *data, u64 val) +{ + int ret = 0; + + clstate.enable = val; + if (clstate.enable) { + if (!clstate.cl) { + MSM_BUS_DBG("client: %u\n", clstate.cl); + clstate.cl = msm_bus_dbg_register_client(); + if (clstate.cl) + ret = msm_bus_dbg_update_cl_request(clstate.cl); + } else { + MSM_BUS_DBG("update request for cl: %u\n", clstate.cl); + ret = msm_bus_dbg_update_cl_request(clstate.cl); + } + } + + MSM_BUS_DBG("Set enable: %llu\n", val); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(shell_client_en_fops, msm_bus_dbg_en_get, + msm_bus_dbg_en_set, "%llu\n"); + +/** + * The following funtions are used for viewing the client data + * and changing the client request at run-time + */ + +static ssize_t client_data_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int bsize = 0; + uint32_t cl = (uint32_t)file->private_data; + struct msm_bus_cldata *cldata = NULL; + + list_for_each_entry(cldata, &cl_list, list) { + if (cldata->clid == cl) + break; + } + bsize = cldata->size; + return simple_read_from_buffer(buf, count, ppos, + cldata->buffer, bsize); +} + +static int client_data_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations client_data_fops = { + .open = client_data_open, + .read = client_data_read, +}; + +struct dentry *msm_bus_dbg_create(const char *name, mode_t mode, + struct dentry *dent, uint32_t clid) +{ + if (dent == NULL) { + MSM_BUS_DBG("debugfs not ready yet\n"); + return NULL; + } + return debugfs_create_file(name, mode, dent, (void *)clid, + &client_data_fops); +} + +static int msm_bus_dbg_record_client(const struct msm_bus_scale_pdata *pdata, + int index, uint32_t clid, struct dentry *file) +{ + struct msm_bus_cldata *cldata; + + cldata = kmalloc(sizeof(struct msm_bus_cldata), GFP_KERNEL); + if (!cldata) { + MSM_BUS_DBG("Failed to allocate memory for client data\n"); + return -ENOMEM; + } + cldata->pdata = pdata; + cldata->index = index; + cldata->clid = clid; + cldata->file = file; + cldata->size = 0; + list_add_tail(&cldata->list, &cl_list); + return 0; +} + +static void msm_bus_dbg_free_client(uint32_t clid) +{ + struct msm_bus_cldata *cldata = NULL; + + list_for_each_entry(cldata, &cl_list, list) { + if (cldata->clid == clid) { + debugfs_remove(cldata->file); + list_del(&cldata->list); + kfree(cldata); + break; + } + } +} + +static int msm_bus_dbg_fill_cl_buffer(const struct msm_bus_scale_pdata *pdata, + int index, uint32_t clid) +{ + int i = 0, j; + char *buf = NULL; + struct msm_bus_cldata *cldata = NULL; + struct timespec ts; + + list_for_each_entry(cldata, &cl_list, list) { + if (cldata->clid == clid) + break; + } + if (cldata->file == NULL) { + if (pdata->name == NULL) { + MSM_BUS_DBG("Client doesn't have a name\n"); + return -EINVAL; + } + cldata->file = msm_bus_dbg_create(pdata->name, S_IRUGO, + clients, clid); + } + + if (cldata->size < (MAX_BUFF_SIZE - FILL_LIMIT)) + i = cldata->size; + else { + i = 0; + cldata->size = 0; + } + buf = cldata->buffer; + ts = ktime_to_timespec(ktime_get()); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n%d.%d\n", + (int)ts.tv_sec, (int)ts.tv_nsec); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "curr : %d\n", index); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "masters: "); + + for (j = 0; j < pdata->usecase->num_paths; j++) + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%d ", + pdata->usecase[index].vectors[j].src); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nslaves : "); + for (j = 0; j < pdata->usecase->num_paths; j++) + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%d ", + pdata->usecase[index].vectors[j].dst); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nab : "); + for (j = 0; j < pdata->usecase->num_paths; j++) + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u ", + pdata->usecase[index].vectors[j].ab); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nib : "); + for (j = 0; j < pdata->usecase->num_paths; j++) + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u ", + pdata->usecase[index].vectors[j].ib); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n"); + + cldata->size = i; + return i; +} + +static int msm_bus_dbg_update_request(struct msm_bus_cldata *cldata, int index) +{ + int ret = 0; + + if ((index < 0) || (index > cldata->pdata->num_usecases)) { + MSM_BUS_DBG("Invalid index!\n"); + return -EINVAL; + } + ret = msm_bus_scale_client_update_request(cldata->clid, index); + return ret; +} + +static ssize_t msm_bus_dbg_update_request_write(struct file *file, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct msm_bus_cldata *cldata; + unsigned long index = 0; + int ret = 0; + char *chid; + char *buf = kmalloc((sizeof(char) * (cnt + 1)), GFP_KERNEL); + + if (!buf || IS_ERR(buf)) { + MSM_BUS_ERR("Memory allocation for buffer failed\n"); + return -ENOMEM; + } + if (cnt == 0) + return 0; + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = '\0'; + chid = buf; + MSM_BUS_DBG("buffer: %s\n size: %d\n", buf, sizeof(ubuf)); + + list_for_each_entry(cldata, &cl_list, list) { + if (strstr(chid, cldata->pdata->name)) { + cldata = cldata; + strsep(&chid, " "); + if (chid) { + ret = strict_strtoul(chid, 10, &index); + if (ret) { + MSM_BUS_DBG("Index conversion" + " failed\n"); + return -EFAULT; + } + } else + MSM_BUS_DBG("Error parsing input. Index not" + " found\n"); + break; + } + } + + msm_bus_dbg_update_request(cldata, index); + kfree(buf); + return cnt; +} + +/** + * The following funtions are used for viewing the commit data + * for each fabric + */ +static ssize_t fabric_data_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_bus_fab_list *fablist = NULL; + int bsize = 0; + ssize_t ret; + const char *name = file->private_data; + + mutex_lock(&msm_bus_dbg_fablist_lock); + list_for_each_entry(fablist, &fabdata_list, list) { + if (strcmp(fablist->name, name) == 0) + break; + } + bsize = fablist->size; + ret = simple_read_from_buffer(buf, count, ppos, + fablist->buffer, bsize); + mutex_unlock(&msm_bus_dbg_fablist_lock); + return ret; +} + +static const struct file_operations fabric_data_fops = { + .open = client_data_open, + .read = fabric_data_read, +}; + +static int msm_bus_dbg_record_fabric(const char *fabname, struct dentry *file) +{ + struct msm_bus_fab_list *fablist; + int ret = 0; + + mutex_lock(&msm_bus_dbg_fablist_lock); + fablist = kmalloc(sizeof(struct msm_bus_fab_list), GFP_KERNEL); + if (!fablist) { + MSM_BUS_DBG("Failed to allocate memory for commit data\n"); + ret = -ENOMEM; + goto err; + } + + fablist->name = fabname; + fablist->size = 0; + list_add_tail(&fablist->list, &fabdata_list); +err: + mutex_unlock(&msm_bus_dbg_fablist_lock); + return ret; +} + +static void msm_bus_dbg_free_fabric(const char *fabname) +{ + struct msm_bus_fab_list *fablist = NULL; + + mutex_lock(&msm_bus_dbg_fablist_lock); + list_for_each_entry(fablist, &fabdata_list, list) { + if (strcmp(fablist->name, fabname) == 0) { + debugfs_remove(fablist->file); + list_del(&fablist->list); + kfree(fablist); + break; + } + } + mutex_unlock(&msm_bus_dbg_fablist_lock); +} + +static int msm_bus_dbg_fill_fab_buffer(const char *fabname, + void *cdata, int nmasters, int nslaves, + int ntslaves) +{ + int i; + char *buf = NULL; + struct msm_bus_fab_list *fablist = NULL; + struct timespec ts; + + mutex_lock(&msm_bus_dbg_fablist_lock); + list_for_each_entry(fablist, &fabdata_list, list) { + if (strcmp(fablist->name, fabname) == 0) + break; + } + if (fablist->file == NULL) { + MSM_BUS_DBG("Fabric dbg entry does not exist\n"); + mutex_unlock(&msm_bus_dbg_fablist_lock); + return -EFAULT; + } + + if (fablist->size < MAX_BUFF_SIZE - 256) + i = fablist->size; + else { + i = 0; + fablist->size = 0; + } + buf = fablist->buffer; + mutex_unlock(&msm_bus_dbg_fablist_lock); + ts = ktime_to_timespec(ktime_get()); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n%d.%d\n", + (int)ts.tv_sec, (int)ts.tv_nsec); + + msm_bus_rpm_fill_cdata_buffer(&i, buf, MAX_BUFF_SIZE, cdata, + nmasters, nslaves, ntslaves); + i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n"); + mutex_lock(&msm_bus_dbg_fablist_lock); + fablist->size = i; + mutex_unlock(&msm_bus_dbg_fablist_lock); + return 0; +} + +static const struct file_operations msm_bus_dbg_update_request_fops = { + .open = client_data_open, + .write = msm_bus_dbg_update_request_write, +}; + +/** + * msm_bus_dbg_client_data() - Add debug data for clients + * @pdata: Platform data of the client + * @index: The current index or operation to be performed + * @clid: Client handle obtained during registration + */ +void msm_bus_dbg_client_data(struct msm_bus_scale_pdata *pdata, int index, + uint32_t clid) +{ + struct dentry *file = NULL; + + if (index == MSM_BUS_DBG_REGISTER) { + msm_bus_dbg_record_client(pdata, index, clid, file); + if (!pdata->name) { + MSM_BUS_DBG("Cannot create debugfs entry. Null name\n"); + return; + } + } else if (index == MSM_BUS_DBG_UNREGISTER) { + msm_bus_dbg_free_client(clid); + MSM_BUS_DBG("Client %d unregistered\n", clid); + } else + msm_bus_dbg_fill_cl_buffer(pdata, index, clid); +} +EXPORT_SYMBOL(msm_bus_dbg_client_data); + +/** + * msm_bus_dbg_commit_data() - Add commit data from fabrics + * @fabname: Fabric name specified in platform data + * @cdata: Commit Data + * @nmasters: Number of masters attached to fabric + * @nslaves: Number of slaves attached to fabric + * @ntslaves: Number of tiered slaves attached to fabric + * @op: Operation to be performed + */ +void msm_bus_dbg_commit_data(const char *fabname, void *cdata, + int nmasters, int nslaves, int ntslaves, int op) +{ + struct dentry *file = NULL; + + if (op == MSM_BUS_DBG_REGISTER) + msm_bus_dbg_record_fabric(fabname, file); + else if (op == MSM_BUS_DBG_UNREGISTER) + msm_bus_dbg_free_fabric(fabname); + else + msm_bus_dbg_fill_fab_buffer(fabname, cdata, nmasters, + nslaves, ntslaves); +} +EXPORT_SYMBOL(msm_bus_dbg_commit_data); + +static int __init msm_bus_debugfs_init(void) +{ + struct dentry *commit, *shell_client; + struct msm_bus_fab_list *fablist; + struct msm_bus_cldata *cldata = NULL; + uint64_t val = 0; + + dir = debugfs_create_dir("msm-bus-dbg", NULL); + if ((!dir) || IS_ERR(dir)) { + MSM_BUS_ERR("Couldn't create msm-bus-dbg\n"); + goto err; + } + + clients = debugfs_create_dir("client-data", dir); + if ((!dir) || IS_ERR(dir)) { + MSM_BUS_ERR("Couldn't create clients\n"); + goto err; + } + + shell_client = debugfs_create_dir("shell-client", dir); + if ((!dir) || IS_ERR(dir)) { + MSM_BUS_ERR("Couldn't create clients\n"); + goto err; + } + + commit = debugfs_create_dir("commit-data", dir); + if ((!dir) || IS_ERR(dir)) { + MSM_BUS_ERR("Couldn't create commit\n"); + goto err; + } + + if (debugfs_create_file("update_request", S_IRUGO | S_IWUSR, + shell_client, &val, &shell_client_en_fops) == NULL) + goto err; + if (debugfs_create_file("ib", S_IRUGO | S_IWUSR, shell_client, &val, + &shell_client_ib_fops) == NULL) + goto err; + if (debugfs_create_file("ab", S_IRUGO | S_IWUSR, shell_client, &val, + &shell_client_ab_fops) == NULL) + goto err; + if (debugfs_create_file("slv", S_IRUGO | S_IWUSR, shell_client, + &val, &shell_client_slv_fops) == NULL) + goto err; + if (debugfs_create_file("mas", S_IRUGO | S_IWUSR, shell_client, + &val, &shell_client_mas_fops) == NULL) + goto err; + if (debugfs_create_file("update-request", S_IRUGO | S_IWUSR, + clients, NULL, &msm_bus_dbg_update_request_fops) == NULL) + goto err; + + list_for_each_entry(cldata, &cl_list, list) { + if (cldata->pdata->name == NULL) { + MSM_BUS_DBG("Client name not found\n"); + continue; + } + cldata->file = msm_bus_dbg_create(cldata-> + pdata->name, S_IRUGO, clients, cldata->clid); + } + + mutex_lock(&msm_bus_dbg_fablist_lock); + list_for_each_entry(fablist, &fabdata_list, list) { + fablist->file = debugfs_create_file(fablist->name, S_IRUGO, + commit, (void *)fablist->name, &fabric_data_fops); + if (fablist->file == NULL) { + MSM_BUS_DBG("Cannot create files for commit data\n"); + goto err; + } + } + mutex_unlock(&msm_bus_dbg_fablist_lock); + + msm_bus_dbg_init_vectors(); + return 0; +err: + debugfs_remove_recursive(dir); + return -ENODEV; +} +late_initcall(msm_bus_debugfs_init); + +static void __exit msm_bus_dbg_teardown(void) +{ + struct msm_bus_fab_list *fablist = NULL, *fablist_temp; + struct msm_bus_cldata *cldata = NULL, *cldata_temp; + + debugfs_remove_recursive(dir); + list_for_each_entry_safe(cldata, cldata_temp, &cl_list, list) { + list_del(&cldata->list); + kfree(cldata); + } + mutex_lock(&msm_bus_dbg_fablist_lock); + list_for_each_entry_safe(fablist, fablist_temp, &fabdata_list, list) { + list_del(&fablist->list); + kfree(fablist); + } + mutex_unlock(&msm_bus_dbg_fablist_lock); +} +module_exit(msm_bus_dbg_teardown); +MODULE_DESCRIPTION("Debugfs for msm bus scaling client"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Gagan Mac "); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c new file mode 100644 index 00000000000..8c015d11d0d --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c @@ -0,0 +1,752 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" + +enum { + SLAVE_NODE, + MASTER_NODE, +}; + +enum { + DISABLE, + ENABLE, +}; + +struct msm_bus_fabric { + struct msm_bus_fabric_device fabdev; + int ahb; + void *cdata[NUM_CTX]; + bool arb_dirty; + bool clk_dirty; + struct radix_tree_root fab_tree; + int num_nodes; + struct list_head gateways; + struct msm_bus_inode_info info; + struct msm_bus_fabric_registration *pdata; + void *hw_data; +}; +#define to_msm_bus_fabric(d) container_of(d, \ + struct msm_bus_fabric, d) + +/** + * msm_bus_fabric_add_node() - Add a node to the fabric structure + * @fabric: Fabric device to which the node should be added + * @info: The node to be added + */ +static int msm_bus_fabric_add_node(struct msm_bus_fabric *fabric, + struct msm_bus_inode_info *info) +{ + int status = -ENOMEM; + MSM_BUS_DBG("msm_bus_fabric_add_node: ID %d Gw: %d\n", + info->node_info->priv_id, info->node_info->gateway); + status = radix_tree_preload(GFP_ATOMIC); + if (status) + goto out; + + status = radix_tree_insert(&fabric->fab_tree, info->node_info->priv_id, + info); + radix_tree_preload_end(); + if (IS_SLAVE(info->node_info->priv_id)) + radix_tree_tag_set(&fabric->fab_tree, info->node_info->priv_id, + SLAVE_NODE); + + if (info->node_info->slaveclk[DUAL_CTX]) { + info->nodeclk[DUAL_CTX].clk = clk_get_sys("msm_bus", + info->node_info->slaveclk[DUAL_CTX]); + if (IS_ERR(info->nodeclk[DUAL_CTX].clk)) { + MSM_BUS_ERR("Could not get clock for %s\n", + info->node_info->slaveclk[DUAL_CTX]); + status = -EINVAL; + goto out; + } + info->nodeclk[DUAL_CTX].enable = false; + info->nodeclk[DUAL_CTX].dirty = false; + } + +out: + return status; +} + +/** + * msm_bus_add_fab() - Add a fabric (gateway) to the current fabric + * @fabric: Fabric device to which the gateway info should be added + * @info: Gateway node to be added to the fabric + */ +static int msm_bus_fabric_add_fab(struct msm_bus_fabric *fabric, + struct msm_bus_inode_info *info) +{ + struct msm_bus_fabnodeinfo *fabnodeinfo; + MSM_BUS_DBG("msm_bus_fabric_add_fab: ID %d Gw: %d\n", + info->node_info->priv_id, info->node_info->gateway); + fabnodeinfo = kzalloc(sizeof(struct msm_bus_fabnodeinfo), GFP_KERNEL); + if (fabnodeinfo == NULL) { + MSM_FAB_ERR("msm_bus_fabric_add_fab: " + "No Node Info\n"); + MSM_FAB_ERR("axi: Cannot register fabric!\n"); + return -ENOMEM; + } + + fabnodeinfo->info = info; + fabnodeinfo->info->num_pnodes = -1; + list_add_tail(&fabnodeinfo->list, &fabric->gateways); + return 0; +} + +/** + * register_fabric_info() - Create the internal fabric structure and + * build the topology tree from platform specific data + * @pdev: Platform device for getting base addresses + * @fabric: Fabric to which the gateways, nodes should be added + * + * This function is called from probe. Iterates over the platform data, + * and builds the topology + */ +static int register_fabric_info(struct platform_device *pdev, + struct msm_bus_fabric *fabric) +{ + int i = 0, ret = 0, err = 0; + + MSM_BUS_DBG("id:%d pdata-id: %d len: %d\n", fabric->fabdev.id, + fabric->pdata->id, fabric->pdata->len); + fabric->hw_data = fabric->fabdev.hw_algo.allocate_hw_data(pdev, + fabric->pdata); + if (ZERO_OR_NULL_PTR(fabric->hw_data)) { + MSM_BUS_ERR("Couldn't allocate hw_data for fab: %d\n", + fabric->fabdev.id); + goto error; + } + + for (i = 0; i < fabric->pdata->len; i++) { + struct msm_bus_inode_info *info; + int ctx; + + info = kzalloc(sizeof(struct msm_bus_inode_info), GFP_KERNEL); + if (info == NULL) { + MSM_BUS_ERR("Error allocating info\n"); + return -ENOMEM; + } + + info->node_info = fabric->pdata->info + i; + info->commit_index = -1; + info->num_pnodes = -1; + + for (ctx = 0; ctx < NUM_CTX; ctx++) { + if (info->node_info->slaveclk[ctx]) { + info->nodeclk[ctx].clk = clk_get_sys("msm_bus", + info->node_info->slaveclk[ctx]); + if (IS_ERR(info->nodeclk[ctx].clk)) { + MSM_BUS_ERR("Couldn't get clk %s\n", + info->node_info->slaveclk[ctx]); + err = -EINVAL; + } + info->nodeclk[ctx].enable = false; + info->nodeclk[ctx].dirty = false; + } + } + if (info->node_info->memclk) { + info->memclk.clk = clk_get_sys("msm_bus", + info->node_info->memclk); + if (IS_ERR(info->memclk.clk)) { + MSM_BUS_ERR("Couldn't get clk %s\n", + info->node_info->memclk); + err = -EINVAL; + } + info->memclk.enable = false; + info->memclk.dirty = false; + } + + ret = info->node_info->gateway ? + msm_bus_fabric_add_fab(fabric, info) : + msm_bus_fabric_add_node(fabric, info); + if (ret) { + MSM_BUS_ERR("Unable to add node info, ret: %d\n", ret); + kfree(info); + goto error; + } + + if (fabric->fabdev.hw_algo.node_init == NULL) + continue; + + fabric->fabdev.hw_algo.node_init(fabric->hw_data, info); + if (ret) { + MSM_BUS_ERR("Unable to init node info, ret: %d\n", ret); + kfree(info); + } + } + + MSM_BUS_DBG("Fabric: %d nmasters: %d nslaves: %d\n" + " ntieredslaves: %d, rpm_enabled: %d\n", + fabric->fabdev.id, fabric->pdata->nmasters, + fabric->pdata->nslaves, fabric->pdata->ntieredslaves, + fabric->pdata->rpm_enabled); + MSM_BUS_DBG("msm_bus_register_fabric_info i: %d\n", i); + fabric->num_nodes = fabric->pdata->len; +error: + fabric->num_nodes = i; + msm_bus_dbg_commit_data(fabric->fabdev.name, NULL, 0, 0, 0, + MSM_BUS_DBG_REGISTER); + return ret | err; +} + +/** + * msm_bus_fabric_update_clks() - Set the clocks for fabrics and slaves + * @fabric: Fabric for which the clocks need to be updated + * @slave: The node for which the clocks need to be updated + * @index: The index for which the current clocks are set + * @curr_clk_hz:Current clock value + * @req_clk_hz: Requested clock value + * @bwsum: Bandwidth Sum + * @clk_flag: Flag determining whether fabric clock or the slave clock has to + * be set. If clk_flag is set, fabric clock is set, else slave clock is set. + */ +static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev, + struct msm_bus_inode_info *slave, int index, + unsigned long curr_clk_hz, unsigned long req_clk_hz, + unsigned long bwsum_hz, int clk_flag, int ctx, + unsigned int cl_active_flag) +{ + int i, status = 0; + unsigned long max_pclk = 0, rate; + unsigned long *pclk = NULL; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + struct nodeclk *nodeclk; + + /** + * Integration for clock rates is not required if context is not + * same as client's active-only flag + */ + if (ctx != cl_active_flag) + goto skip_set_clks; + + /* Maximum for this gateway */ + for (i = 0; i <= slave->num_pnodes; i++) { + if (i == index && (req_clk_hz < curr_clk_hz)) + continue; + slave->pnode[i].sel_clk = &slave->pnode[i].clk[ctx]; + max_pclk = max(max_pclk, *slave->pnode[i].sel_clk); + } + + *slave->link_info.sel_clk = + max(max_pclk, max(bwsum_hz, req_clk_hz)); + /* Is this gateway or slave? */ + if (clk_flag && (!fabric->ahb)) { + struct msm_bus_fabnodeinfo *fabgw = NULL; + struct msm_bus_inode_info *info = NULL; + /* Maximum of all gateways set at fabric */ + list_for_each_entry(fabgw, &fabric->gateways, list) { + info = fabgw->info; + if (!info) + continue; + info->link_info.sel_clk = &info->link_info.clk[ctx]; + max_pclk = max(max_pclk, *info->link_info.sel_clk); + } + MSM_BUS_DBG("max_pclk from gateways: %lu\n", max_pclk); + + /* Maximum of all slave clocks. */ + + for (i = 0; i < fabric->pdata->len; i++) { + if (fabric->pdata->info[i].gateway || + (fabric->pdata->info[i].id < SLAVE_ID_KEY)) + continue; + info = radix_tree_lookup(&fabric->fab_tree, + fabric->pdata->info[i].priv_id); + if (!info) + continue; + info->link_info.sel_clk = &info->link_info.clk[ctx]; + max_pclk = max(max_pclk, *info->link_info.sel_clk); + } + + + MSM_BUS_DBG("max_pclk from slaves & gws: %lu\n", max_pclk); + fabric->info.link_info.sel_clk = + &fabric->info.link_info.clk[ctx]; + pclk = fabric->info.link_info.sel_clk; + } else { + slave->link_info.sel_clk = &slave->link_info.clk[ctx]; + pclk = slave->link_info.sel_clk; + } + + + *pclk = max(max_pclk, max(bwsum_hz, req_clk_hz)); + + if (!fabric->pdata->rpm_enabled) + goto skip_set_clks; + + if (clk_flag) { + nodeclk = &fabric->info.nodeclk[ctx]; + if (nodeclk->clk) { + MSM_BUS_DBG("clks: id: %d set-clk: %lu bwsum_hz:%lu\n", + fabric->fabdev.id, *pclk, bwsum_hz); + if (nodeclk->rate != *pclk) { + nodeclk->dirty = true; + nodeclk->rate = *pclk; + } + fabric->clk_dirty = true; + } + } else { + nodeclk = &slave->nodeclk[ctx]; + if (nodeclk->clk) { + rate = *pclk; + MSM_BUS_DBG("AXI_clks: id: %d set-clk: %lu " + "bwsum_hz: %lu\n" , slave->node_info->priv_id, rate, + bwsum_hz); + if (nodeclk->rate != rate) { + nodeclk->dirty = true; + nodeclk->rate = rate; + } + } + if (!status && slave->memclk.clk) { + rate = *slave->link_info.sel_clk; + if (slave->memclk.rate != rate) { + slave->memclk.rate = rate; + slave->memclk.dirty = true; + } + slave->memclk.rate = rate; + fabric->clk_dirty = true; + } + } +skip_set_clks: + return status; +} + +void msm_bus_fabric_update_bw(struct msm_bus_fabric_device *fabdev, + struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, + long int add_bw, int *master_tiers, int ctx) +{ + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + void *sel_cdata; + + /* Temporarily stub out arbitration settings for copper */ + if (machine_is_copper()) + return; + + sel_cdata = fabric->cdata[ctx]; + + /* If it's an ahb fabric, don't calculate arb values */ + if (fabric->ahb) { + MSM_BUS_DBG("AHB fabric, skipping bw calculation\n"); + return; + } + if (!add_bw) { + MSM_BUS_DBG("No bandwidth delta. Skipping commit\n"); + return; + } + + fabdev->hw_algo.update_bw(hop, info, fabric->pdata, sel_cdata, + master_tiers, add_bw); + fabric->arb_dirty = true; +} + +static int msm_bus_fabric_clk_set(int enable, struct msm_bus_inode_info *info) +{ + int i, status = 0; + for (i = 0; i < NUM_CTX; i++) + if (info->nodeclk[i].dirty) { + status = clk_set_rate(info->nodeclk[i].clk, info-> + nodeclk[i].rate); + if (enable && !(info->nodeclk[i].enable)) { + clk_prepare_enable(info->nodeclk[i].clk); + info->nodeclk[i].dirty = false; + info->nodeclk[i].enable = true; + } else if ((info->nodeclk[i].rate == 0) && (!enable) + && (info->nodeclk[i].enable)) { + clk_disable_unprepare(info->nodeclk[i].clk); + info->nodeclk[i].dirty = false; + info->nodeclk[i].enable = false; + } + } + + if (info->memclk.dirty) { + status = clk_set_rate(info->memclk.clk, info->memclk.rate); + if (enable && !(info->memclk.enable)) { + clk_prepare_enable(info->memclk.clk); + info->memclk.dirty = false; + info->memclk.enable = true; + } else if (info->memclk.rate == 0 && (!enable) && + (info->memclk.enable)) { + clk_disable_unprepare(info->memclk.clk); + info->memclk.dirty = false; + info->memclk.enable = false; + } + } + + return status; +} + +/** + * msm_bus_fabric_clk_commit() - Call clock enable and update clock + * values. +*/ +static int msm_bus_fabric_clk_commit(int enable, struct msm_bus_fabric *fabric) +{ + unsigned int i, nfound = 0, status = 0; + struct msm_bus_inode_info *info[fabric->pdata->nslaves]; + + if (fabric->clk_dirty == false) { + MSM_BUS_DBG("No clocks have been touched for fabric: %d\n", + fabric->fabdev.id); + goto out; + } else + status = msm_bus_fabric_clk_set(enable, &fabric->info); + + if (status) + MSM_BUS_WARN("Error setting clocks on fabric: %d\n", + fabric->fabdev.id); + + nfound = radix_tree_gang_lookup_tag(&fabric->fab_tree, (void **)&info, + fabric->fabdev.id, fabric->pdata->nslaves, SLAVE_NODE); + if (nfound == 0) { + MSM_BUS_DBG("No slaves found for fabric: %d\n", + fabric->fabdev.id); + goto out; + } + + for (i = 0; i < nfound; i++) { + status = msm_bus_fabric_clk_set(enable, info[i]); + if (status) + MSM_BUS_WARN("Error setting clocks for node: %d\n", + info[i]->node_info->id); + } + +out: + return status; +} + +/** + * msm_bus_fabric_hw_commit() - Commit the arbitration data to Hardware. + * @fabric: Fabric for which the data should be committed + * */ +static int msm_bus_fabric_hw_commit(struct msm_bus_fabric_device *fabdev) +{ + int status = 0; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + + /* + * For a non-zero bandwidth request, clocks should be enabled before + * sending the arbitration data to RPM, but should be disabled only + * after commiting the data. + */ + status = msm_bus_fabric_clk_commit(ENABLE, fabric); + if (status) + MSM_BUS_DBG("Error setting clocks on fabric: %d\n", + fabric->fabdev.id); + + if (!fabric->arb_dirty) { + MSM_BUS_DBG("Not committing as fabric not arb_dirty\n"); + goto skip_arb; + } + + status = fabdev->hw_algo.commit(fabric->pdata, fabric->hw_data, + (void **)fabric->cdata); + if (status) + MSM_BUS_DBG("Error committing arb data for fabric: %d\n", + fabric->fabdev.id); + + fabric->arb_dirty = false; +skip_arb: + /* + * If the bandwidth request is 0 for a fabric, the clocks + * should be disabled after arbitration data is committed. + */ + status = msm_bus_fabric_clk_commit(DISABLE, fabric); + if (status) + MSM_BUS_DBG("Error disabling clocks on fabric: %d\n", + fabric->fabdev.id); + fabric->clk_dirty = false; + return status; +} + +/** + * msm_bus_fabric_port_halt() - Used to halt a master port + * @fabric: Fabric on which the current master node is present + * @portid: Port id of the master + */ +int msm_bus_fabric_port_halt(struct msm_bus_fabric_device *fabdev, int iid) +{ + struct msm_bus_inode_info *info = NULL; + uint8_t mport; + uint32_t haltid = 0; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + + info = fabdev->algo->find_node(fabdev, iid); + if (!info) { + MSM_BUS_ERR("Error: Info not found for id: %u", iid); + return -EINVAL; + } + + haltid = fabric->pdata->haltid; + mport = info->node_info->masterp[0]; + + return fabdev->hw_algo.port_halt(haltid, mport); +} + +/** + * msm_bus_fabric_port_unhalt() - Used to unhalt a master port + * @fabric: Fabric on which the current master node is present + * @portid: Port id of the master + */ +int msm_bus_fabric_port_unhalt(struct msm_bus_fabric_device *fabdev, int iid) +{ + struct msm_bus_inode_info *info = NULL; + uint8_t mport; + uint32_t haltid = 0; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + + info = fabdev->algo->find_node(fabdev, iid); + if (!info) { + MSM_BUS_ERR("Error: Info not found for id: %u", iid); + return -EINVAL; + } + + haltid = fabric->pdata->haltid; + mport = info->node_info->masterp[0]; + return fabdev->hw_algo.port_unhalt(haltid, mport); +} + +/** + * msm_bus_fabric_find_gw_node() - This function finds the gateway node + * attached on a given fabric + * @id: ID of the gateway node + * @fabric: Fabric to find the gateway node on + * Function returns: Pointer to the gateway node + */ +static struct msm_bus_inode_info *msm_bus_fabric_find_gw_node(struct + msm_bus_fabric_device * fabdev, int id) +{ + struct msm_bus_inode_info *info = NULL; + struct msm_bus_fabnodeinfo *fab; + struct msm_bus_fabric *fabric; + if (!fabdev) { + MSM_BUS_ERR("No fabric device found!\n"); + return NULL; + } + + fabric = to_msm_bus_fabric(fabdev); + if (!fabric || IS_ERR(fabric)) { + MSM_BUS_ERR("No fabric type found!\n"); + return NULL; + } + list_for_each_entry(fab, &fabric->gateways, list) { + if (fab->info->node_info->priv_id == id) { + info = fab->info; + break; + } + } + + return info; +} + +static struct msm_bus_inode_info *msm_bus_fabric_find_node(struct + msm_bus_fabric_device * fabdev, int id) +{ + struct msm_bus_inode_info *info = NULL; + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + info = radix_tree_lookup(&fabric->fab_tree, id); + if (!info) + MSM_BUS_ERR("Null info found for id %d\n", id); + return info; +} + +static struct list_head *msm_bus_fabric_get_gw_list(struct msm_bus_fabric_device + *fabdev) +{ + struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev); + if (!fabric || IS_ERR(fabric)) { + MSM_BUS_ERR("No fabric found from fabdev\n"); + return NULL; + } + return &fabric->gateways; + +} +static struct msm_bus_fab_algorithm msm_bus_algo = { + .update_clks = msm_bus_fabric_update_clks, + .update_bw = msm_bus_fabric_update_bw, + .port_halt = msm_bus_fabric_port_halt, + .port_unhalt = msm_bus_fabric_port_unhalt, + .commit = msm_bus_fabric_hw_commit, + .find_node = msm_bus_fabric_find_node, + .find_gw_node = msm_bus_fabric_find_gw_node, + .get_gw_list = msm_bus_fabric_get_gw_list, +}; + +static int msm_bus_fabric_hw_init(struct msm_bus_fabric_registration *pdata, + struct msm_bus_hw_algorithm *hw_algo) +{ + int ret = 0; + ret = msm_bus_rpm_hw_init(pdata, hw_algo); + if (ret) { + MSM_BUS_ERR("RPM initialization failed\n"); + ret = -EINVAL; + } + + return ret; +} + +static int msm_bus_fabric_probe(struct platform_device *pdev) +{ + int ctx, ret = 0; + struct msm_bus_fabric *fabric; + struct msm_bus_fabric_registration *pdata; + + fabric = kzalloc(sizeof(struct msm_bus_fabric), GFP_KERNEL); + if (!fabric) { + MSM_BUS_ERR("Fabric alloc failed\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&fabric->gateways); + INIT_RADIX_TREE(&fabric->fab_tree, GFP_ATOMIC); + fabric->num_nodes = 0; + fabric->fabdev.id = pdev->id; + fabric->fabdev.visited = false; + + fabric->info.node_info = kzalloc(sizeof(struct msm_bus_node_info), + GFP_KERNEL); + if (ZERO_OR_NULL_PTR(fabric->info.node_info)) { + MSM_BUS_ERR("Fabric node info alloc failed\n"); + kfree(fabric); + return -ENOMEM; + } + fabric->info.node_info->priv_id = fabric->fabdev.id; + fabric->info.node_info->id = fabric->fabdev.id; + fabric->info.num_pnodes = -1; + fabric->info.link_info.clk[DUAL_CTX] = 0; + fabric->info.link_info.bw[DUAL_CTX] = 0; + fabric->info.link_info.clk[ACTIVE_CTX] = 0; + fabric->info.link_info.bw[ACTIVE_CTX] = 0; + + fabric->fabdev.id = pdev->id; + pdata = (struct msm_bus_fabric_registration *)pdev->dev.platform_data; + fabric->fabdev.name = pdata->name; + fabric->fabdev.algo = &msm_bus_algo; + ret = msm_bus_fabric_hw_init(pdata, &fabric->fabdev.hw_algo); + if (ret) { + MSM_BUS_ERR("Error initializing hardware for fabric: %d\n", + fabric->fabdev.id); + goto err; + } + + fabric->ahb = pdata->ahb; + fabric->pdata = pdata; + fabric->pdata->board_algo->assign_iids(fabric->pdata, + fabric->fabdev.id); + fabric->fabdev.board_algo = fabric->pdata->board_algo; + + /* + * clk and bw for fabric->info will contain the max bw and clk + * it will allow. This info will come from the boards file. + */ + ret = msm_bus_fabric_device_register(&fabric->fabdev); + if (ret) { + MSM_BUS_ERR("Error registering fabric %d ret %d\n", + fabric->fabdev.id, ret); + goto err; + } + + for (ctx = 0; ctx < NUM_CTX; ctx++) { + if (pdata->fabclk[ctx]) { + fabric->info.nodeclk[ctx].clk = clk_get( + &fabric->fabdev.dev, pdata->fabclk[ctx]); + if (IS_ERR(fabric->info.nodeclk[ctx].clk)) { + MSM_BUS_ERR("Couldn't get clock %s\n", + pdata->fabclk[ctx]); + ret = -EINVAL; + goto err; + } + fabric->info.nodeclk[ctx].enable = false; + fabric->info.nodeclk[ctx].dirty = false; + } + } + + /* Find num. of slaves, masters, populate gateways, radix tree */ + ret = register_fabric_info(pdev, fabric); + if (ret) { + MSM_BUS_ERR("Could not register fabric %d info, ret: %d\n", + fabric->fabdev.id, ret); + goto err; + } + if (!fabric->ahb) { + /* Allocate memory for commit data */ + for (ctx = 0; ctx < NUM_CTX; ctx++) { + ret = fabric->fabdev.hw_algo.allocate_commit_data( + fabric->pdata, &fabric->cdata[ctx], ctx); + if (ret) { + MSM_BUS_ERR("Failed to alloc commit data for " + "fab: %d, ret = %d\n", + fabric->fabdev.id, ret); + goto err; + } + } + } + + return ret; +err: + kfree(fabric->info.node_info); + kfree(fabric); + return ret; +} + +static int msm_bus_fabric_remove(struct platform_device *pdev) +{ + struct msm_bus_fabric_device *fabdev = NULL; + struct msm_bus_fabric *fabric; + int i; + int ret = 0; + + fabdev = platform_get_drvdata(pdev); + msm_bus_fabric_device_unregister(fabdev); + fabric = to_msm_bus_fabric(fabdev); + msm_bus_dbg_commit_data(fabric->fabdev.name, NULL, 0, 0, 0, + MSM_BUS_DBG_UNREGISTER); + for (i = 0; i < fabric->pdata->nmasters; i++) + radix_tree_delete(&fabric->fab_tree, fabric->fabdev.id + i); + for (i = (fabric->fabdev.id + SLAVE_ID_KEY); i < + fabric->pdata->nslaves; i++) + radix_tree_delete(&fabric->fab_tree, i); + if (!fabric->ahb) { + fabdev->hw_algo.free_commit_data(fabric->cdata[DUAL_CTX]); + fabdev->hw_algo.free_commit_data(fabric->cdata[ACTIVE_CTX]); + } + + kfree(fabric->info.node_info); + kfree(fabric->hw_data); + kfree(fabric); + return ret; +} + +static struct platform_driver msm_bus_fabric_driver = { + .probe = msm_bus_fabric_probe, + .remove = msm_bus_fabric_remove, + .driver = { + .name = "msm_bus_fabric", + .owner = THIS_MODULE, + }, +}; + +static int __init msm_bus_fabric_init_driver(void) +{ + MSM_BUS_ERR("msm_bus_fabric_init_driver\n"); + return platform_driver_register(&msm_bus_fabric_driver); +} +postcore_initcall(msm_bus_fabric_init_driver); diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c new file mode 100644 index 00000000000..4653431dd2c --- /dev/null +++ b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c @@ -0,0 +1,964 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_bus_core.h" +#include "../rpm_resources.h" + +void msm_bus_rpm_set_mt_mask() +{ +#ifdef CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED + struct msm_rpm_iv_pair mt[1]; + int mask = MSM_RPMRS_MASK_RPM_CTL_MULTI_TIER; + mt[0].id = MSM_RPM_ID_RPM_CTL; + mt[0].value = 2; + msm_rpmrs_set_bits_noirq(MSM_RPM_CTX_SET_0, mt, 1, + &mask); +#endif +} + +bool msm_bus_rpm_is_mem_interleaved(void) +{ + int status = 0; + struct msm_rpm_iv_pair il[2]; + uint16_t id[2]; + + il[0].value = 0; + il[1].value = 0; + status = msm_bus_board_rpm_get_il_ids(id); + if (status) { + MSM_BUS_DBG("Dynamic check not supported, " + "default: Interleaved memory\n"); + goto inter; + } + + il[0].id = id[0]; + il[1].id = id[1]; + status = msm_rpm_get_status(il, ARRAY_SIZE(il)); + if (status) { + MSM_BUS_ERR("Status read for interleaving returned: %d\n" + "Using interleaved memory by default\n", + status); + goto inter; + } + + /* + * If the start address of EBI1-CH0 is the same as + * the start address of EBI1-CH1, the memory is interleaved. + * The start addresses are stored in the 16 MSBs of the status + * register + */ + if ((il[0].value & 0xFFFF0000) != (il[1].value & 0xFFFF0000)) { + MSM_BUS_DBG("Non-interleaved memory\n"); + return false; + } + +inter: + MSM_BUS_DBG("Interleaved memory\n"); + return true; +} + +#ifndef CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED +struct commit_data { + uint16_t *bwsum; + uint16_t *arb; + unsigned long *actarb; +}; + +/* + * The following macros are used for various operations on commit data. + * Commit data is an array of 32 bit integers. The size of arrays is unique + * to the fabric. Commit arrays are allocated at run-time based on the number + * of masters, slaves and tiered-slaves registered. + */ + +#define MSM_BUS_GET_BW_INFO(val, type, bw) \ + do { \ + (type) = MSM_BUS_GET_BW_TYPE(val); \ + (bw) = MSM_BUS_GET_BW(val); \ + } while (0) + + +#define MSM_BUS_GET_BW_INFO_BYTES (val, type, bw) \ + do { \ + (type) = MSM_BUS_GET_BW_TYPE(val); \ + (bw) = msm_bus_get_bw_bytes(val); \ + } while (0) + +#define ROUNDED_BW_VAL_FROM_BYTES(bw) \ + ((((bw) >> 17) + 1) & 0x8000 ? 0x7FFF : (((bw) >> 17) + 1)) + +#define BW_VAL_FROM_BYTES(bw) \ + ((((bw) >> 17) & 0x8000) ? 0x7FFF : ((bw) >> 17)) + +static uint32_t msm_bus_set_bw_bytes(unsigned long bw) +{ + return ((((bw) & 0x1FFFF) && (((bw) >> 17) == 0)) ? + ROUNDED_BW_VAL_FROM_BYTES(bw) : BW_VAL_FROM_BYTES(bw)); + +} + +uint64_t msm_bus_get_bw_bytes(unsigned long val) +{ + return ((val) & 0x7FFF) << 17; +} + +uint16_t msm_bus_get_bw(unsigned long val) +{ + return (val)&0x7FFF; +} + +static uint16_t msm_bus_create_bw_tier_pair_bytes(uint8_t type, + unsigned long bw) +{ + return ((((type) == MSM_BUS_BW_TIER1 ? 1 : 0) << 15) | + (msm_bus_set_bw_bytes(bw))); +}; + +uint16_t msm_bus_create_bw_tier_pair(uint8_t type, unsigned long bw) +{ + return (((type) == MSM_BUS_BW_TIER1 ? 1 : 0) << 15) | ((bw) & 0x7FFF); +} + +void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, const int max_size, + void *cdata, int nmasters, int nslaves, int ntslaves) +{ + int j, c; + struct commit_data *cd = (struct commit_data *)cdata; + + *curr += scnprintf(buf + *curr, max_size - *curr, "BWSum:\n"); + for (c = 0; c < nslaves; c++) + *curr += scnprintf(buf + *curr, max_size - *curr, + "0x%x\t", cd->bwsum[c]); + *curr += scnprintf(buf + *curr, max_size - *curr, "\nArb:"); + for (c = 0; c < ntslaves; c++) { + *curr += scnprintf(buf + *curr, max_size - *curr, + "\nTSlave %d:\n", c); + for (j = 0; j < nmasters; j++) + *curr += scnprintf(buf + *curr, max_size - *curr, + " 0x%x\t", cd->arb[(c * nmasters) + j]); + } +} + +/** + * allocate_commit_data() - Allocate the data for commit array in the + * format specified by RPM + * @fabric: Fabric device for which commit data is allocated + */ +static int msm_bus_rpm_allocate_commit_data(struct msm_bus_fabric_registration + *fab_pdata, void **cdata, int ctx) +{ + struct commit_data **cd = (struct commit_data **)cdata; + *cd = kzalloc(sizeof(struct commit_data), GFP_KERNEL); + if (!*cd) { + MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); + return -ENOMEM; + } + (*cd)->bwsum = kzalloc((sizeof(uint16_t) * fab_pdata->nslaves), + GFP_KERNEL); + if (!(*cd)->bwsum) { + MSM_BUS_DBG("Couldn't alloc mem for slaves\n"); + kfree(*cd); + return -ENOMEM; + } + (*cd)->arb = kzalloc(((sizeof(uint16_t *)) * + (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), + GFP_KERNEL); + if (!(*cd)->arb) { + MSM_BUS_DBG("Couldn't alloc memory for" + " slaves\n"); + kfree((*cd)->bwsum); + kfree(*cd); + return -ENOMEM; + } + (*cd)->actarb = kzalloc(((sizeof(unsigned long *)) * + (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), + GFP_KERNEL); + if (!(*cd)->actarb) { + MSM_BUS_DBG("Couldn't alloc memory for" + " slaves\n"); + kfree((*cd)->bwsum); + kfree((*cd)->arb); + kfree(*cd); + return -ENOMEM; + } + + return 0; +} + +static void free_commit_data(void *cdata) +{ + struct commit_data *cd = (struct commit_data *)cdata; + + kfree(cd->bwsum); + kfree(cd->arb); + kfree(cd->actarb); + kfree(cd); +} + +/** + * allocate_rpm_data() - Allocate the id-value pairs to be + * sent to RPM + */ +static void *msm_bus_rpm_allocate_rpm_data(struct platform_device *pdev, + struct msm_bus_fabric_registration *fab_pdata) +{ + struct msm_rpm_iv_pair *rpm_data; + uint16_t count = ((fab_pdata->nmasters * fab_pdata->ntieredslaves) + + fab_pdata->nslaves + 1)/2; + + rpm_data = kmalloc((sizeof(struct msm_rpm_iv_pair) * count), + GFP_KERNEL); + return (void *)rpm_data; +} + +#define BWMASK 0x7FFF +#define TIERMASK 0x8000 +#define GET_TIER(n) (((n) & TIERMASK) >> 15) + +static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, + struct msm_bus_inode_info *info, + struct msm_bus_fabric_registration *fab_pdata, + void *sel_cdata, int *master_tiers, + long int add_bw) +{ + int index, i, j, tiers, ports; + struct commit_data *sel_cd = (struct commit_data *)sel_cdata; + + add_bw = INTERLEAVED_BW(fab_pdata, add_bw, info->node_info->num_mports); + ports = INTERLEAVED_VAL(fab_pdata, info->node_info->num_mports); + tiers = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_tiers); + for (i = 0; i < tiers; i++) { + for (j = 0; j < ports; j++) { + uint16_t hop_tier; + /* + * For interleaved gateway ports and slave ports, + * there is one-one mapping between gateway port and + * the slave port + */ + if (info->node_info->gateway && i != j && + (hop->node_info->num_sports > 1)) + continue; + + if (!hop->node_info->tier) + hop_tier = MSM_BUS_BW_TIER2 - 1; + else + hop_tier = hop->node_info->tier[i] - 1; + index = ((hop_tier * fab_pdata->nmasters) + + (info->node_info->masterp[j])); + /* If there is tier, calculate arb for commit */ + if (hop->node_info->tier) { + uint16_t tier; + unsigned long tieredbw = sel_cd->actarb[index]; + if (GET_TIER(sel_cd->arb[index])) + tier = MSM_BUS_BW_TIER1; + else if (master_tiers) + /* + * By default master is only in the + * tier specified by default. + * To change the default tier, client + * needs to explicitly request for a + * different supported tier */ + tier = master_tiers[0]; + else + tier = MSM_BUS_BW_TIER2; + + /* + * Make sure gateway to slave port bandwidth + * is not divided when slave is interleaved + */ + if (info->node_info->gateway + && hop->node_info->num_sports > 1) + tieredbw += add_bw; + else + tieredbw += INTERLEAVED_BW(fab_pdata, + add_bw, hop->node_info-> + num_sports); + + /* If bw is 0, update tier to default */ + if (!tieredbw) + tier = MSM_BUS_BW_TIER2; + /* Update Arb for fab,get HW Mport from enum */ + sel_cd->arb[index] = + msm_bus_create_bw_tier_pair_bytes(tier, + tieredbw); + sel_cd->actarb[index] = tieredbw; + MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%ld " + "bwsum: %ld\n", hop_tier, info->node_info-> + masterp[i], tieredbw, *hop->link_info.sel_bw); + } + } + } + + /* Update bwsum for slaves on fabric */ + ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports); + for (i = 0; i < ports; i++) { + sel_cd->bwsum[hop->node_info->slavep[i]] + = (uint16_t)msm_bus_create_bw_tier_pair_bytes(0, + (*hop->link_info.sel_bw/hop->node_info->num_sports)); + MSM_BUS_DBG("slavep:%d, link_bw: %ld\n", + hop->node_info->slavep[i], (*hop->link_info.sel_bw/ + hop->node_info->num_sports)); + } +} + +#define RPM_SHIFT_VAL 16 +#define RPM_SHIFT(n) ((n) << RPM_SHIFT_VAL) +static int msm_bus_rpm_compare_cdata( + struct msm_bus_fabric_registration *fab_pdata, + struct commit_data *cd1, struct commit_data *cd2) +{ + size_t n; + int ret; + n = sizeof(uint16_t) * fab_pdata->nslaves; + ret = memcmp(cd1->bwsum, cd2->bwsum, n); + if (ret) { + MSM_BUS_DBG("Commit Data bwsum not equal\n"); + return ret; + } + + n = sizeof(uint16_t *) * ((fab_pdata->ntieredslaves * + fab_pdata->nmasters) + 1); + ret = memcmp(cd1->arb, cd2->arb, n); + if (ret) { + MSM_BUS_DBG("Commit Data arb[%d] not equal\n", n); + return ret; + } + + return 0; +} + +static int msm_bus_rpm_commit_arb(struct msm_bus_fabric_registration + *fab_pdata, int ctx, struct msm_rpm_iv_pair *rpm_data, + struct commit_data *cd, bool valid) +{ + int i, j, offset = 0, status = 0, count, index = 0; + /* + * count is the number of 2-byte words required to commit the + * data to rpm. This is calculated by the following formula. + * Commit data is split into two arrays: + * 1. arb[nmasters * ntieredslaves] + * 2. bwsum[nslaves] + */ + count = ((fab_pdata->nmasters * fab_pdata->ntieredslaves) + + (fab_pdata->nslaves) + 1)/2; + + offset = fab_pdata->offset; + + /* + * Copy bwsum to rpm data + * Since bwsum is uint16, the values need to be adjusted to + * be copied to value field of rpm-data, which is 32 bits. + */ + for (i = 0; i < (fab_pdata->nslaves - 1); i += 2) { + rpm_data[index].id = offset + index; + rpm_data[index].value = RPM_SHIFT(*(cd->bwsum + i + 1)) | + *(cd->bwsum + i); + index++; + } + /* Account for odd number of slaves */ + if (fab_pdata->nslaves & 1) { + rpm_data[index].id = offset + index; + rpm_data[index].value = *(cd->arb); + rpm_data[index].value = RPM_SHIFT(rpm_data[index].value) | + *(cd->bwsum + i); + index++; + i = 1; + } else + i = 0; + + /* Copy arb values to rpm data */ + for (; i < (fab_pdata->ntieredslaves * fab_pdata->nmasters); + i += 2) { + rpm_data[index].id = offset + index; + rpm_data[index].value = RPM_SHIFT(*(cd->arb + i + 1)) | + *(cd->arb + i); + index++; + } + + MSM_BUS_DBG("rpm data for fab: %d\n", fab_pdata->id); + for (i = 0; i < count; i++) + MSM_BUS_DBG("%d %x\n", rpm_data[i].id, rpm_data[i].value); + + MSM_BUS_DBG("Commit Data: Fab: %d BWSum:\n", fab_pdata->id); + for (i = 0; i < fab_pdata->nslaves; i++) + MSM_BUS_DBG("fab_slaves:0x%x\n", cd->bwsum[i]); + MSM_BUS_DBG("Commit Data: Fab: %d Arb:\n", fab_pdata->id); + for (i = 0; i < fab_pdata->ntieredslaves; i++) { + MSM_BUS_DBG("tiered-slave: %d\n", i); + for (j = 0; j < fab_pdata->nmasters; j++) + MSM_BUS_DBG(" 0x%x\n", + cd->arb[(i * fab_pdata->nmasters) + j]); + } + + MSM_BUS_DBG("calling msm_rpm_set: %d\n", status); + msm_bus_dbg_commit_data(fab_pdata->name, cd, fab_pdata-> + nmasters, fab_pdata->nslaves, fab_pdata->ntieredslaves, + MSM_BUS_DBG_OP); + if (fab_pdata->rpm_enabled) { + if (valid) { + if (ctx == ACTIVE_CTX) { + status = msm_rpm_set(MSM_RPM_CTX_SET_0, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_set returned: %d\n", + status); + } else if (ctx == DUAL_CTX) { + status = msm_rpm_set(MSM_RPM_CTX_SET_SLEEP, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_set returned: %d\n", + status); + } + } else { + if (ctx == ACTIVE_CTX) { + status = msm_rpm_clear(MSM_RPM_CTX_SET_0, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_clear returned: %d\n", + status); + } else if (ctx == DUAL_CTX) { + status = msm_rpm_clear(MSM_RPM_CTX_SET_SLEEP, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_clear returned: %d\n", + status); + } + } + } + + return status; +} + +#else + +#define NUM_TIERS 2 +#define RPM_SHIFT24(n) ((n) << 24) +#define RPM_SHIFT16(n) ((n) << 16) +#define RPM_SHIFT8(n) ((n) << 8) +struct commit_data { + uint16_t *bwsum; + uint8_t *arb[NUM_TIERS]; + unsigned long *actarb[NUM_TIERS]; +}; + +#define MODE_BIT(val) ((val) & 0x80) +#define MODE0_IMM(val) ((val) & 0xF) +#define MODE0_SHIFT(val) (((val) & 0x70) >> 4) +#define MODE1_STEP 48 /* 48 MB */ +#define MODE1_OFFSET 512 /* 512 MB */ +#define MODE1_IMM(val) ((val) & 0x7F) +#define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x)) + +static uint8_t msm_bus_set_bw_bytes(unsigned long val) +{ + unsigned int shift; + unsigned int intVal; + unsigned char result; + + /* Convert to MB */ + intVal = (unsigned int)((val + ((1 << 20) - 1)) >> 20); + /** + * Divide by 2^20 and round up + * A value graeter than 0x1E0 will round up to 512 and overflow + * Mode 0 so it should be made Mode 1 + */ + if (0x1E0 > intVal) { + /** + * MODE 0 + * Compute the shift value + * Shift value is 32 - the number of leading zeroes - + * 4 to save the most significant 4 bits of the value + */ + shift = 32 - 4 - min((uint8_t)28, (uint8_t)__CLZ(intVal)); + + /* Add min value - 1 to force a round up when shifting right */ + intVal += (1 << shift) - 1; + + /* Recompute the shift value in case there was an overflow */ + shift = 32 - 4 - min((uint8_t)28, (uint8_t)__CLZ(intVal)); + + /* Clear the mode bit (msb) and fill in the fields */ + result = ((0x70 & (shift << 4)) | + (0x0F & (intVal >> shift))); + } else { + /* MODE 1 */ + result = (unsigned char)(0x80 | + ((intVal - MODE1_OFFSET + MODE1_STEP - 1) / + MODE1_STEP)); + } + + return result; +} + +uint64_t msm_bus_get_bw(unsigned long val) +{ + return MODE_BIT(val) ? + /* Mode 1 */ + (MODE1_IMM(val) * MODE1_STEP + MODE1_OFFSET) : + /* Mode 0 */ + (MODE0_IMM(val) << MODE0_SHIFT(val)); +} + +uint64_t msm_bus_get_bw_bytes(unsigned long val) +{ + return msm_bus_get_bw(val) << 20; +} + +static uint8_t msm_bus_create_bw_tier_pair_bytes(uint8_t type, + unsigned long bw) +{ + return msm_bus_set_bw_bytes(bw); +}; + +uint8_t msm_bus_create_bw_tier_pair(uint8_t type, unsigned long bw) +{ + return msm_bus_create_bw_tier_pair_bytes(type, bw); +}; + +static int msm_bus_rpm_allocate_commit_data(struct msm_bus_fabric_registration + *fab_pdata, void **cdata, int ctx) +{ + struct commit_data **cd = (struct commit_data **)cdata; + int i; + + *cd = kzalloc(sizeof(struct commit_data), GFP_KERNEL); + if (!*cd) { + MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); + goto cdata_err; + } + + (*cd)->bwsum = kzalloc((sizeof(uint16_t) * fab_pdata->nslaves), + GFP_KERNEL); + if (!(*cd)->bwsum) { + MSM_BUS_DBG("Couldn't alloc mem for slaves\n"); + goto bwsum_err; + } + + for (i = 0; i < NUM_TIERS; i++) { + (*cd)->arb[i] = kzalloc(((sizeof(uint8_t *)) * + (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), + GFP_KERNEL); + if (!(*cd)->arb[i]) { + MSM_BUS_DBG("Couldn't alloc memory for" + " slaves\n"); + goto arb_err; + } + + (*cd)->actarb[i] = kzalloc(((sizeof(unsigned long *)) * + (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), + GFP_KERNEL); + if (!(*cd)->actarb[i]) { + MSM_BUS_DBG("Couldn't alloc memory for" + " slaves\n"); + kfree((*cd)->arb[i]); + goto arb_err; + } + } + + + return 0; + +arb_err: + for (i = i - 1; i >= 0; i--) { + kfree((*cd)->arb[i]); + kfree((*cd)->actarb[i]); + } +bwsum_err: + kfree((*cd)->bwsum); +cdata_err: + kfree(*cd); + return -ENOMEM; +} + +static void free_commit_data(void *cdata) +{ + int i; + struct commit_data *cd = (struct commit_data *)cdata; + kfree(cd->bwsum); + for (i = 0; i < NUM_TIERS; i++) { + kfree(cd->arb[i]); + kfree(cd->actarb[i]); + } + kfree(cd); +} + +static void *msm_bus_rpm_allocate_rpm_data(struct platform_device *pdev, + struct msm_bus_fabric_registration *fab_pdata) +{ + struct msm_rpm_iv_pair *rpm_data; + uint16_t count = (((fab_pdata->nmasters * fab_pdata->ntieredslaves * + NUM_TIERS)/2) + fab_pdata->nslaves + 1)/2; + + rpm_data = kmalloc((sizeof(struct msm_rpm_iv_pair) * count), + GFP_KERNEL); + return (void *)rpm_data; +} + +static int msm_bus_rpm_compare_cdata( + struct msm_bus_fabric_registration *fab_pdata, + struct commit_data *cd1, struct commit_data *cd2) +{ + size_t n; + int i, ret; + n = sizeof(uint16_t) * fab_pdata->nslaves; + ret = memcmp(cd1->bwsum, cd2->bwsum, n); + if (ret) { + MSM_BUS_DBG("Commit Data bwsum not equal\n"); + return ret; + } + + n = sizeof(uint8_t *) * ((fab_pdata->ntieredslaves * + fab_pdata->nmasters) + 1); + for (i = 0; i < NUM_TIERS; i++) { + ret = memcmp(cd1->arb[i], cd2->arb[i], n); + if (ret) { + MSM_BUS_DBG("Commit Data arb[%d] not equal\n", i); + return ret; + } + } + + return 0; +} + +static int msm_bus_rpm_commit_arb(struct msm_bus_fabric_registration + *fab_pdata, int ctx, struct msm_rpm_iv_pair *rpm_data, + struct commit_data *cd, bool valid) +{ + int i, j, k, offset = 0, status = 0, count, index = 0; + /* + * count is the number of 2-byte words required to commit the + * data to rpm. This is calculated by the following formula. + * Commit data is split into two arrays: + * 1. arb[nmasters * ntieredslaves][num_tiers] + * 2. bwsum[nslaves] + */ + count = (((fab_pdata->nmasters * fab_pdata->ntieredslaves * NUM_TIERS) + /2) + fab_pdata->nslaves + 1)/2; + + offset = fab_pdata->offset; + + /* + * Copy bwsum to rpm data + * Since bwsum is uint16, the values need to be adjusted to + * be copied to value field of rpm-data, which is 32 bits. + */ + for (i = 0; i < (fab_pdata->nslaves - 1); i += 2) { + rpm_data[index].id = offset + index; + rpm_data[index].value = RPM_SHIFT16(*(cd->bwsum + i + 1)) | + *(cd->bwsum + i); + index++; + } + /* Account for odd number of slaves */ + if (fab_pdata->nslaves & 1) { + rpm_data[index].id = offset + index; + rpm_data[index].value = RPM_SHIFT8(*cd->arb[1]) | + *(cd->arb[0]); + rpm_data[index].value = RPM_SHIFT16(rpm_data[index].value) | + *(cd->bwsum + i); + index++; + i = 1; + } else + i = 0; + + /* Copy arb values to rpm data */ + for (; i < (fab_pdata->ntieredslaves * fab_pdata->nmasters); + i += 2) { + uint16_t tv1, tv0; + rpm_data[index].id = offset + index; + tv0 = RPM_SHIFT8(*(cd->arb[1] + i)) | (*(cd->arb[0] + i)); + tv1 = RPM_SHIFT8(*(cd->arb[1] + i + 1)) | (*(cd->arb[0] + i + + 1)); + rpm_data[index].value = RPM_SHIFT16(tv1) | tv0; + index++; + } + + MSM_BUS_DBG("rpm data for fab: %d\n", fab_pdata->id); + for (i = 0; i < count; i++) + MSM_BUS_DBG("%d %x\n", rpm_data[i].id, rpm_data[i].value); + + MSM_BUS_DBG("Commit Data: Fab: %d BWSum:\n", fab_pdata->id); + for (i = 0; i < fab_pdata->nslaves; i++) + MSM_BUS_DBG("fab_slaves:0x%x\n", cd->bwsum[i]); + MSM_BUS_DBG("Commit Data: Fab: %d Arb:\n", fab_pdata->id); + for (k = 0; k < NUM_TIERS; k++) { + MSM_BUS_DBG("Tier: %d\n", k); + for (i = 0; i < fab_pdata->ntieredslaves; i++) { + MSM_BUS_DBG("tiered-slave: %d\n", i); + for (j = 0; j < fab_pdata->nmasters; j++) + MSM_BUS_DBG(" 0x%x\n", + cd->arb[k][(i * fab_pdata->nmasters) + + j]); + } + } + + MSM_BUS_DBG("calling msm_rpm_set: %d\n", status); + msm_bus_dbg_commit_data(fab_pdata->name, (void *)cd, fab_pdata-> + nmasters, fab_pdata->nslaves, fab_pdata->ntieredslaves, + MSM_BUS_DBG_OP); + if (fab_pdata->rpm_enabled) { + if (valid) { + if (ctx == ACTIVE_CTX) { + status = msm_rpm_set(MSM_RPM_CTX_SET_0, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_set returned: %d\n", + status); + } else if (ctx == DUAL_CTX) { + status = msm_rpm_set(MSM_RPM_CTX_SET_SLEEP, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_set returned: %d\n", + status); + } + } else { + if (ctx == ACTIVE_CTX) { + status = msm_rpm_clear(MSM_RPM_CTX_SET_0, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_clear returned: %d\n", + status); + } else if (ctx == DUAL_CTX) { + status = msm_rpm_clear(MSM_RPM_CTX_SET_SLEEP, + rpm_data, count); + MSM_BUS_DBG("msm_rpm_clear returned: %d\n", + status); + } + } + } + + return status; +} + +#define FORMAT_BW(x) \ + ((x < 0) ? \ + -(msm_bus_get_bw_bytes(msm_bus_create_bw_tier_pair_bytes(0, -(x)))) : \ + (msm_bus_get_bw_bytes(msm_bus_create_bw_tier_pair_bytes(0, x)))) + +static uint16_t msm_bus_pack_bwsum_bytes(unsigned long bw) +{ + return (bw + ((1 << 20) - 1)) >> 20; +}; + +static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, + struct msm_bus_inode_info *info, + struct msm_bus_fabric_registration *fab_pdata, + void *sel_cdata, int *master_tiers, + long int add_bw) +{ + int index, i, j, tiers, ports; + struct commit_data *sel_cd = (struct commit_data *)sel_cdata; + + add_bw = INTERLEAVED_BW(fab_pdata, add_bw, info->node_info->num_mports); + ports = INTERLEAVED_VAL(fab_pdata, info->node_info->num_mports); + tiers = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_tiers); + for (i = 0; i < tiers; i++) { + for (j = 0; j < ports; j++) { + uint16_t hop_tier; + /* + * For interleaved gateway ports and slave ports, + * there is one-one mapping between gateway port and + * the slave port + */ + if (info->node_info->gateway && i != j + && hop->node_info->num_sports > 1) + continue; + + if (!hop->node_info->tier) + hop_tier = MSM_BUS_BW_TIER2 - 1; + else + hop_tier = hop->node_info->tier[i] - 1; + index = ((hop_tier * fab_pdata->nmasters) + + (info->node_info->masterp[j])); + /* If there is tier, calculate arb for commit */ + if (hop->node_info->tier) { + uint16_t tier; + unsigned long tieredbw; + if (master_tiers) + tier = master_tiers[0] - 1; + else + tier = MSM_BUS_BW_TIER2 - 1; + + tieredbw = sel_cd->actarb[tier][index]; + /* + * Make sure gateway to slave port bandwidth + * is not divided when slave is interleaved + */ + if (info->node_info->gateway + && hop->node_info->num_sports > 1) + tieredbw += add_bw; + else + tieredbw += INTERLEAVED_BW(fab_pdata, + add_bw, hop->node_info-> + num_sports); + + /* Update Arb for fab,get HW Mport from enum */ + sel_cd->arb[tier][index] = + msm_bus_create_bw_tier_pair_bytes(0, tieredbw); + sel_cd->actarb[tier][index] = tieredbw; + MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%lu " + "bwsum: %ld\n", hop_tier, info->node_info-> + masterp[i], tieredbw, *hop->link_info.sel_bw); + } + } + } + + /* Update bwsum for slaves on fabric */ + + ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports); + for (i = 0; i < ports; i++) { + sel_cd->bwsum[hop->node_info->slavep[i]] + = msm_bus_pack_bwsum_bytes((*hop->link_info. + sel_bw/hop->node_info->num_sports)); + MSM_BUS_DBG("slavep:%d, link_bw: %ld\n", + hop->node_info->slavep[i], (*hop->link_info.sel_bw/ + hop->node_info->num_sports)); + } +} + + +void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, const int max_size, + void *cdata, int nmasters, int nslaves, int ntslaves) +{ + int j, k, c; + struct commit_data *cd = (struct commit_data *)cdata; + + *curr += scnprintf(buf + *curr, max_size - *curr, "BWSum:\n"); + for (c = 0; c < nslaves; c++) + *curr += scnprintf(buf + *curr, max_size - *curr, + "0x%x\t", cd->bwsum[c]); + *curr += scnprintf(buf + *curr, max_size - *curr, "\nArb:"); + for (k = 0; k < NUM_TIERS; k++) { + *curr += scnprintf(buf + *curr, max_size - *curr, + "\nTier %d:\n", k); + for (c = 0; c < ntslaves; c++) { + *curr += scnprintf(buf + *curr, max_size - *curr, + "TSlave %d:\n", c); + for (j = 0; j < nmasters; j++) + *curr += scnprintf(buf + *curr, max_size - + *curr, " 0x%x\t", + cd->arb[k][(c * nmasters) + j]); + } + } +} +#endif + +/** +* msm_bus_rpm_commit() - Commit the arbitration data to RPM +* @fabric: Fabric for which the data should be committed +**/ +static int msm_bus_rpm_commit(struct msm_bus_fabric_registration + *fab_pdata, void *hw_data, void **cdata) +{ + + int ret; + bool valid; + struct commit_data *dual_cd, *act_cd; + struct msm_rpm_iv_pair *rpm_data = (struct msm_rpm_iv_pair *)hw_data; + dual_cd = (struct commit_data *)cdata[DUAL_CTX]; + act_cd = (struct commit_data *)cdata[ACTIVE_CTX]; + + /* + * If the arb data for active set and sleep set is + * different, commit both sets. + * If the arb data for active set and sleep set is + * the same, invalidate the sleep set. + */ + ret = msm_bus_rpm_compare_cdata(fab_pdata, act_cd, dual_cd); + if (!ret) + /* Invalidate sleep set.*/ + valid = false; + else + valid = true; + + ret = msm_bus_rpm_commit_arb(fab_pdata, DUAL_CTX, rpm_data, + dual_cd, valid); + if (ret) + MSM_BUS_ERR("Error comiting fabric:%d in %d ctx\n", + fab_pdata->id, DUAL_CTX); + + valid = true; + ret = msm_bus_rpm_commit_arb(fab_pdata, ACTIVE_CTX, rpm_data, act_cd, + valid); + if (ret) + MSM_BUS_ERR("Error comiting fabric:%d in %d ctx\n", + fab_pdata->id, ACTIVE_CTX); + + return ret; +} + +static int msm_bus_rpm_port_halt(uint32_t haltid, uint8_t mport) +{ + int status = 0; + struct msm_bus_halt_vector hvector = {0, 0}; + struct msm_rpm_iv_pair rpm_data[2]; + + MSM_BUS_MASTER_HALT(hvector.haltmask, hvector.haltval, mport); + rpm_data[0].id = haltid; + rpm_data[0].value = hvector.haltval; + rpm_data[1].id = haltid + 1; + rpm_data[1].value = hvector.haltmask; + + MSM_BUS_DBG("ctx: %d, id: %d, value: %d\n", + MSM_RPM_CTX_SET_0, rpm_data[0].id, rpm_data[0].value); + MSM_BUS_DBG("ctx: %d, id: %d, value: %d\n", + MSM_RPM_CTX_SET_0, rpm_data[1].id, rpm_data[1].value); + + status = msm_rpm_set(MSM_RPM_CTX_SET_0, rpm_data, 2); + if (status) + MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); + return status; +} + +static int msm_bus_rpm_port_unhalt(uint32_t haltid, uint8_t mport) +{ + int status = 0; + struct msm_bus_halt_vector hvector = {0, 0}; + struct msm_rpm_iv_pair rpm_data[2]; + + MSM_BUS_MASTER_UNHALT(hvector.haltmask, hvector.haltval, + mport); + rpm_data[0].id = haltid; + rpm_data[0].value = hvector.haltval; + rpm_data[1].id = haltid + 1; + rpm_data[1].value = hvector.haltmask; + + MSM_BUS_DBG("unalt: ctx: %d, id: %d, value: %d\n", + MSM_RPM_CTX_SET_SLEEP, rpm_data[0].id, rpm_data[0].value); + MSM_BUS_DBG("unhalt: ctx: %d, id: %d, value: %d\n", + MSM_RPM_CTX_SET_SLEEP, rpm_data[1].id, rpm_data[1].value); + + status = msm_rpm_set(MSM_RPM_CTX_SET_0, rpm_data, 2); + if (status) + MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); + return status; +} + +int msm_bus_rpm_hw_init(struct msm_bus_fabric_registration *pdata, + struct msm_bus_hw_algorithm *hw_algo) +{ + pdata->il_flag = msm_bus_rpm_is_mem_interleaved(); + hw_algo->allocate_commit_data = msm_bus_rpm_allocate_commit_data; + hw_algo->allocate_hw_data = msm_bus_rpm_allocate_rpm_data; + hw_algo->node_init = NULL; + hw_algo->free_commit_data = free_commit_data; + hw_algo->update_bw = msm_bus_rpm_update_bw; + hw_algo->commit = msm_bus_rpm_commit; + hw_algo->port_halt = msm_bus_rpm_port_halt; + hw_algo->port_unhalt = msm_bus_rpm_port_unhalt; + if (!pdata->ahb) + pdata->rpm_enabled = 1; + return 0; +} diff --git a/arch/arm/mach-msm/msm_cache_dump.c b/arch/arm/mach-msm/msm_cache_dump.c new file mode 100644 index 00000000000..9759d5acabf --- /dev/null +++ b/arch/arm/mach-msm/msm_cache_dump.c @@ -0,0 +1,148 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define L2_DUMP_OFFSET 0x14 + +static unsigned long msm_cache_dump_addr; + +/* + * These should not actually be dereferenced. There's no + * need for a virtual mapping, but the physical address is + * necessary. + */ +static struct l1_cache_dump *l1_dump; +static struct l2_cache_dump *l2_dump; + +static int msm_cache_dump_panic(struct notifier_block *this, + unsigned long event, void *ptr) +{ +#ifdef CONFIG_MSM_CACHE_DUMP_ON_PANIC + /* + * Clear the bootloader magic so the dumps aren't overwritten + */ + __raw_writel(0, MSM_IMEM_BASE + L2_DUMP_OFFSET); + + scm_call_atomic1(L1C_SERVICE_ID, CACHE_BUFFER_DUMP_COMMAND_ID, 2); + scm_call_atomic1(L1C_SERVICE_ID, CACHE_BUFFER_DUMP_COMMAND_ID, 1); +#endif + return 0; +} + +static struct notifier_block msm_cache_dump_blk = { + .notifier_call = msm_cache_dump_panic, + /* + * higher priority to ensure this runs before another panic handler + * flushes the caches. + */ + .priority = 1, +}; + +static int msm_cache_dump_probe(struct platform_device *pdev) +{ + struct msm_cache_dump_platform_data *d = pdev->dev.platform_data; + int ret; + struct { + unsigned long buf; + unsigned long size; + } l1_cache_data; + void *temp; + unsigned long total_size = d->l1_size + d->l2_size; + + msm_cache_dump_addr = allocate_contiguous_ebi_nomap(total_size, SZ_4K); + + if (!msm_cache_dump_addr) { + pr_err("%s: Could not get memory for cache dumping\n", + __func__); + return -ENOMEM; + } + + temp = ioremap(msm_cache_dump_addr, total_size); + memset(temp, 0xFF, total_size); + iounmap(temp); + + l1_cache_data.buf = msm_cache_dump_addr; + l1_cache_data.size = d->l1_size; + + ret = scm_call(L1C_SERVICE_ID, L1C_BUFFER_SET_COMMAND_ID, + &l1_cache_data, sizeof(l1_cache_data), NULL, 0); + + if (ret) + pr_err("%s: could not register L1 buffer ret = %d.\n", + __func__, ret); + + l1_dump = (struct l1_cache_dump *)msm_cache_dump_addr; + +#if defined(CONFIG_MSM_CACHE_DUMP_ON_PANIC) + l1_cache_data.buf = msm_cache_dump_addr + d->l1_size; + l1_cache_data.size = d->l2_size; + + ret = scm_call(L1C_SERVICE_ID, L2C_BUFFER_SET_COMMAND_ID, + &l1_cache_data, sizeof(l1_cache_data), NULL, 0); + + if (ret) + pr_err("%s: could not register L2 buffer ret = %d.\n", + __func__, ret); +#endif + __raw_writel(msm_cache_dump_addr + d->l1_size, + MSM_IMEM_BASE + L2_DUMP_OFFSET); + + + l2_dump = (struct l2_cache_dump *)(msm_cache_dump_addr + d->l1_size); + + atomic_notifier_chain_register(&panic_notifier_list, + &msm_cache_dump_blk); + return 0; +} + +static int msm_cache_dump_remove(struct platform_device *pdev) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &msm_cache_dump_blk); + return 0; +} + +static struct platform_driver msm_cache_dump_driver = { + .remove = __devexit_p(msm_cache_dump_remove), + .driver = { + .name = "msm_cache_dump", + .owner = THIS_MODULE + }, +}; + +static int __init msm_cache_dump_init(void) +{ + return platform_driver_probe(&msm_cache_dump_driver, + msm_cache_dump_probe); +} + +static void __exit msm_cache_dump_exit(void) +{ + platform_driver_unregister(&msm_cache_dump_driver); +} +late_initcall(msm_cache_dump_init); +module_exit(msm_cache_dump_exit) diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c new file mode 100644 index 00000000000..0c158de57ac --- /dev/null +++ b/arch/arm/mach-msm/msm_dcvs.c @@ -0,0 +1,791 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CORE_HANDLE_OFFSET (0xA0) +#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__) +#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__) +#define MAX_PENDING (5) + +enum { + MSM_DCVS_DEBUG_NOTIFIER = BIT(0), + MSM_DCVS_DEBUG_IDLE_PULSE = BIT(1), + MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2), +}; + +struct core_attribs { + struct kobj_attribute idle_enabled; + struct kobj_attribute freq_change_enabled; + struct kobj_attribute actual_freq; + struct kobj_attribute freq_change_us; + + struct kobj_attribute max_time_us; + + struct kobj_attribute slack_time_us; + struct kobj_attribute scale_slack_time; + struct kobj_attribute scale_slack_time_pct; + struct kobj_attribute disable_pc_threshold; + struct kobj_attribute em_window_size; + struct kobj_attribute em_max_util_pct; + struct kobj_attribute ss_window_size; + struct kobj_attribute ss_util_pct; + struct kobj_attribute ss_iobusy_conv; + + struct attribute_group attrib_group; +}; + +struct dcvs_core { + char core_name[CORE_NAME_MAX]; + uint32_t new_freq[MAX_PENDING]; + uint32_t actual_freq; + uint32_t freq_change_us; + + uint32_t max_time_us; /* core param */ + + struct msm_dcvs_algo_param algo_param; + struct msm_dcvs_idle *idle_driver; + struct msm_dcvs_freq *freq_driver; + + /* private */ + int64_t time_start; + struct mutex lock; + spinlock_t cpu_lock; + struct task_struct *task; + struct core_attribs attrib; + uint32_t handle; + uint32_t group_id; + uint32_t freq_pending; + struct hrtimer timer; + int32_t timer_disabled; + /* track if kthread for change_freq is active */ + int32_t change_freq_activated; +}; + +static int msm_dcvs_debug; +static int msm_dcvs_enabled = 1; +module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP); + +static struct dentry *debugfs_base; + +static struct dcvs_core core_list[CORES_MAX]; +static DEFINE_MUTEX(core_list_lock); + +static struct kobject *cores_kobj; +static struct dcvs_core *core_handles[CORES_MAX]; + +/* Change core frequency, called with core mutex locked */ +static int __msm_dcvs_change_freq(struct dcvs_core *core) +{ + int ret = 0; + unsigned long flags = 0; + unsigned int requested_freq = 0; + unsigned int prev_freq = 0; + int64_t time_start = 0; + int64_t time_end = 0; + uint32_t slack_us = 0; + uint32_t ret1 = 0; + + if (!core->freq_driver || !core->freq_driver->set_frequency) { + /* Core may have unregistered or hotplugged */ + return -ENODEV; + } +repeat: + spin_lock_irqsave(&core->cpu_lock, flags); + if (unlikely(!core->freq_pending)) { + spin_unlock_irqrestore(&core->cpu_lock, flags); + return ret; + } + requested_freq = core->new_freq[core->freq_pending - 1]; + if (unlikely(core->freq_pending > 1) && + (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)) { + int i; + for (i = 0; i < core->freq_pending - 1; i++) { + __info("Core %s missing freq %u\n", + core->core_name, core->new_freq[i]); + } + } + time_start = core->time_start; + core->time_start = 0; + core->freq_pending = 0; + /** + * Cancel the timers, we dont want the timer firing as we are + * changing the clock rate. Dont let idle_exit and others setup + * timers as well. + */ + hrtimer_cancel(&core->timer); + core->timer_disabled = 1; + spin_unlock_irqrestore(&core->cpu_lock, flags); + + if (requested_freq == core->actual_freq) + return ret; + + /** + * Call the frequency sink driver to change the frequency + * We will need to get back the actual frequency in KHz and + * the record the time taken to change it. + */ + ret = core->freq_driver->set_frequency(core->freq_driver, + requested_freq); + if (ret <= 0) { + __err("Core %s failed to set freq %u\n", + core->core_name, requested_freq); + /* continue to call TZ to get updated slack timer */ + } else { + prev_freq = core->actual_freq; + core->actual_freq = ret; + } + + time_end = ktime_to_ns(ktime_get()); + if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE) + __info("Core %s Time end %llu Time start: %llu\n", + core->core_name, time_end, time_start); + time_end -= time_start; + do_div(time_end, NSEC_PER_USEC); + core->freq_change_us = (uint32_t)time_end; + + /** + * Disable low power modes if the actual frequency is > + * disable_pc_threshold. + */ + if (core->actual_freq > + core->algo_param.disable_pc_threshold) { + core->idle_driver->enable(core->idle_driver, + MSM_DCVS_DISABLE_HIGH_LATENCY_MODES); + if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE) + __info("Disabling LPM for %s\n", core->core_name); + } else if (core->actual_freq <= + core->algo_param.disable_pc_threshold) { + core->idle_driver->enable(core->idle_driver, + MSM_DCVS_ENABLE_HIGH_LATENCY_MODES); + if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE) + __info("Enabling LPM for %s\n", core->core_name); + } + + /** + * Update algorithm with new freq and time taken to change + * to this frequency and that will get us the new slack + * timer + */ + ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE, + core->actual_freq, (uint32_t)time_end, &slack_us, &ret1); + if (!ret) { + /* Reset the slack timer */ + if (slack_us) { + core->timer_disabled = 0; + ret = hrtimer_start(&core->timer, + ktime_set(0, slack_us * 1000), + HRTIMER_MODE_REL_PINNED); + if (ret) + __err("Failed to register timer for core %s\n", + core->core_name); + } + } else { + __err("Error sending core (%s) freq change (%u)\n", + core->core_name, core->actual_freq); + } + + if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE) + __info("Freq %u requested for core %s (actual %u prev %u) " + "change time %u us slack time %u us\n", + requested_freq, core->core_name, + core->actual_freq, prev_freq, + core->freq_change_us, slack_us); + + /** + * By the time we are done with freq changes, we could be asked to + * change again. Check before exiting. + */ + if (core->freq_pending) + goto repeat; + + core->change_freq_activated = 0; + return ret; +} + +static int msm_dcvs_do_freq(void *data) +{ + struct dcvs_core *core = (struct dcvs_core *)data; + static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1}; + + sched_setscheduler(current, SCHED_FIFO, ¶m); + set_current_state(TASK_UNINTERRUPTIBLE); + + while (!kthread_should_stop()) { + mutex_lock(&core->lock); + __msm_dcvs_change_freq(core); + mutex_unlock(&core->lock); + + schedule(); + + if (kthread_should_stop()) + break; + + set_current_state(TASK_UNINTERRUPTIBLE); + } + + __set_current_state(TASK_RUNNING); + + return 0; +} + +static int msm_dcvs_update_freq(struct dcvs_core *core, + enum msm_dcvs_scm_event event, uint32_t param0, + uint32_t *ret1, int *freq_changed) +{ + int ret = 0; + unsigned long flags = 0; + uint32_t new_freq = 0; + + spin_lock_irqsave(&core->cpu_lock, flags); + ret = msm_dcvs_scm_event(core->handle, event, param0, + core->actual_freq, &new_freq, ret1); + if (ret) { + __err("Error (%d) sending SCM event %d for core %s\n", + ret, event, core->core_name); + goto freq_done; + } + + if ((core->actual_freq != new_freq) && + (core->new_freq[core->freq_pending] != new_freq)) { + if (core->freq_pending >= MAX_PENDING - 1) + core->freq_pending = MAX_PENDING - 1; + core->new_freq[core->freq_pending++] = new_freq; + core->time_start = ktime_to_ns(ktime_get()); + + /* Schedule the frequency change */ + if (!core->task) + __err("Uninitialized task for core %s\n", + core->core_name); + else { + if (freq_changed) + *freq_changed = 1; + core->change_freq_activated = 1; + wake_up_process(core->task); + } + } else { + if (freq_changed) + *freq_changed = 0; + } +freq_done: + spin_unlock_irqrestore(&core->cpu_lock, flags); + + return ret; +} + +static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer) +{ + int ret = 0; + struct dcvs_core *core = container_of(timer, struct dcvs_core, timer); + uint32_t ret1; + uint32_t ret2; + + if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE) + __info("Slack timer fired for core %s\n", core->core_name); + + /** + * Timer expired, notify TZ + * Dont care about the third arg. + */ + ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0, + &ret1, &ret2); + if (ret) + __err("Timer expired for core %s but failed to notify.\n", + core->core_name); + + return HRTIMER_NORESTART; +} + +/* Helper functions and macros for sysfs nodes for a core */ +#define CORE_FROM_ATTRIBS(attr, name) \ + container_of(container_of(attr, struct core_attribs, name), \ + struct dcvs_core, attrib); + +#define DCVS_PARAM_SHOW(_name, v) \ +static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \ + return snprintf(buf, PAGE_SIZE, "%d\n", v); \ +} + +#define DCVS_ALGO_PARAM(_name) \ +static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\ + struct kobj_attribute *attr, char *buf) \ +{ \ + struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \ + return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \ +} \ +static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, const char *buf, size_t count) \ +{ \ + int ret = 0; \ + uint32_t val = 0; \ + struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \ + mutex_lock(&core->lock); \ + ret = kstrtouint(buf, 10, &val); \ + if (ret) { \ + __err("Invalid input %s for %s\n", buf, __stringify(_name));\ + } else { \ + uint32_t old_val = core->algo_param._name; \ + core->algo_param._name = val; \ + ret = msm_dcvs_scm_set_algo_params(core->handle, \ + &core->algo_param); \ + if (ret) { \ + core->algo_param._name = old_val; \ + __err("Error(%d) in setting %d for algo param %s\n",\ + ret, val, __stringify(_name)); \ + } \ + } \ + mutex_unlock(&core->lock); \ + return count; \ +} + +#define DCVS_RO_ATTRIB(i, _name) \ + core->attrib._name.attr.name = __stringify(_name); \ + core->attrib._name.attr.mode = S_IRUGO; \ + core->attrib._name.show = msm_dcvs_attr_##_name##_show; \ + core->attrib._name.store = NULL; \ + core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr; + +#define DCVS_RW_ATTRIB(i, _name) \ + core->attrib._name.attr.name = __stringify(_name); \ + core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \ + core->attrib._name.show = msm_dcvs_attr_##_name##_show; \ + core->attrib._name.store = msm_dcvs_attr_##_name##_store; \ + core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr; + +/** + * Function declarations for different attributes. + * Gets used when setting the attribute show and store parameters. + */ +DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL)) +DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL)) +DCVS_PARAM_SHOW(actual_freq, (core->actual_freq)) +DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us)) +DCVS_PARAM_SHOW(max_time_us, (core->max_time_us)) + +DCVS_ALGO_PARAM(slack_time_us) +DCVS_ALGO_PARAM(scale_slack_time) +DCVS_ALGO_PARAM(scale_slack_time_pct) +DCVS_ALGO_PARAM(disable_pc_threshold) +DCVS_ALGO_PARAM(em_window_size) +DCVS_ALGO_PARAM(em_max_util_pct) +DCVS_ALGO_PARAM(ss_window_size) +DCVS_ALGO_PARAM(ss_util_pct) +DCVS_ALGO_PARAM(ss_iobusy_conv) + +static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core) +{ + int ret = 0; + struct kobject *core_kobj = NULL; + const int attr_count = 15; + + BUG_ON(!cores_kobj); + + core->attrib.attrib_group.attrs = + kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL); + + if (!core->attrib.attrib_group.attrs) { + ret = -ENOMEM; + goto done; + } + + DCVS_RO_ATTRIB(0, idle_enabled); + DCVS_RO_ATTRIB(1, freq_change_enabled); + DCVS_RO_ATTRIB(2, actual_freq); + DCVS_RO_ATTRIB(3, freq_change_us); + DCVS_RO_ATTRIB(4, max_time_us); + + DCVS_RW_ATTRIB(5, slack_time_us); + DCVS_RW_ATTRIB(6, scale_slack_time); + DCVS_RW_ATTRIB(7, scale_slack_time_pct); + DCVS_RW_ATTRIB(8, disable_pc_threshold); + DCVS_RW_ATTRIB(9, em_window_size); + DCVS_RW_ATTRIB(10, em_max_util_pct); + DCVS_RW_ATTRIB(11, ss_window_size); + DCVS_RW_ATTRIB(12, ss_util_pct); + DCVS_RW_ATTRIB(13, ss_iobusy_conv); + + core->attrib.attrib_group.attrs[14] = NULL; + + core_kobj = kobject_create_and_add(core->core_name, cores_kobj); + if (!core_kobj) { + ret = -ENOMEM; + goto done; + } + + ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group); + if (ret) + __err("Cannot create core %s attr group\n", core->core_name); + else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER) + __info("Setting up attributes for core %s\n", core->core_name); + +done: + if (ret) { + kfree(core->attrib.attrib_group.attrs); + kobject_del(core_kobj); + } + + return ret; +} + +/* Return the core if found or add to list if @add_to_list is true */ +static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list) +{ + struct dcvs_core *core = NULL; + int i; + int empty = -1; + + if (!name[0] || + (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1)) + return core; + + mutex_lock(&core_list_lock); + for (i = 0; i < CORES_MAX; i++) { + core = &core_list[i]; + if ((empty < 0) && !core->core_name[0]) { + empty = i; + continue; + } + if (!strncmp(name, core->core_name, CORE_NAME_MAX)) + break; + } + + /* Check for core_list full */ + if ((i == CORES_MAX) && (empty < 0)) { + mutex_unlock(&core_list_lock); + return NULL; + } + + if (i == CORES_MAX && add_to_list) { + core = &core_list[empty]; + strlcpy(core->core_name, name, CORE_NAME_MAX); + mutex_init(&core->lock); + spin_lock_init(&core->cpu_lock); + core->handle = empty + CORE_HANDLE_OFFSET; + hrtimer_init(&core->timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + core->timer.function = msm_dcvs_core_slack_timer; + } + mutex_unlock(&core_list_lock); + + return core; +} + +int msm_dcvs_register_core(const char *core_name, uint32_t group_id, + struct msm_dcvs_core_info *info) +{ + int ret = -EINVAL; + struct dcvs_core *core = NULL; + + if (!core_name || !core_name[0]) + return ret; + + core = msm_dcvs_get_core(core_name, true); + if (!core) + return ret; + + mutex_lock(&core->lock); + if (group_id) { + /** + * Create a group for cores, if this core is part of a group + * if the group_id is 0, the core is not part of a group. + * If the group_id already exits, it will through an error + * which we will ignore. + */ + ret = msm_dcvs_scm_create_group(group_id); + if (ret == -ENOMEM) + goto bail; + } + core->group_id = group_id; + + core->max_time_us = info->core_param.max_time_us; + memcpy(&core->algo_param, &info->algo_param, + sizeof(struct msm_dcvs_algo_param)); + + ret = msm_dcvs_scm_register_core(core->handle, group_id, + &info->core_param, info->freq_tbl); + if (ret) + goto bail; + + ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param); + if (ret) + goto bail; + + ret = msm_dcvs_setup_core_sysfs(core); + if (ret) { + __err("Unable to setup core %s sysfs\n", core->core_name); + core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL; + goto bail; + } + +bail: + mutex_unlock(&core->lock); + return ret; +} +EXPORT_SYMBOL(msm_dcvs_register_core); + +int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv) +{ + int ret = -EINVAL; + struct dcvs_core *core = NULL; + uint32_t ret1; + uint32_t ret2; + + if (!drv || !drv->core_name) + return ret; + + core = msm_dcvs_get_core(drv->core_name, true); + if (!core) + return ret; + + mutex_lock(&core->lock); + if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)) + __info("Frequency notifier for %s being replaced\n", + core->core_name); + core->freq_driver = drv; + core->task = kthread_create(msm_dcvs_do_freq, (void *)core, + "msm_dcvs/%d", core->handle); + if (IS_ERR(core->task)) { + mutex_unlock(&core->lock); + return -EFAULT; + } + + if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE) + __info("Enabling idle pulse for %s\n", core->core_name); + + if (core->idle_driver) { + core->actual_freq = core->freq_driver->get_frequency(drv); + /* Notify TZ to start receiving idle info for the core */ + ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 1, + &ret1, &ret2); + core->idle_driver->enable(core->idle_driver, + MSM_DCVS_ENABLE_IDLE_PULSE); + } + + mutex_unlock(&core->lock); + + return core->handle; +} +EXPORT_SYMBOL(msm_dcvs_freq_sink_register); + +int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv) +{ + int ret = -EINVAL; + struct dcvs_core *core = NULL; + uint32_t ret1; + uint32_t ret2; + + if (!drv || !drv->core_name) + return ret; + + core = msm_dcvs_get_core(drv->core_name, false); + if (!core) + return ret; + + mutex_lock(&core->lock); + if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE) + __info("Disabling idle pulse for %s\n", core->core_name); + if (core->idle_driver) { + core->idle_driver->enable(core->idle_driver, + MSM_DCVS_DISABLE_IDLE_PULSE); + /* Notify TZ to stop receiving idle info for the core */ + ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 0, + &ret1, &ret2); + hrtimer_cancel(&core->timer); + core->idle_driver->enable(core->idle_driver, + MSM_DCVS_ENABLE_HIGH_LATENCY_MODES); + if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE) + __info("Enabling LPM for %s\n", core->core_name); + } + core->freq_pending = 0; + core->freq_driver = NULL; + mutex_unlock(&core->lock); + kthread_stop(core->task); + + return 0; +} +EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister); + +int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv) +{ + int ret = -EINVAL; + struct dcvs_core *core = NULL; + + if (!drv || !drv->core_name) + return ret; + + core = msm_dcvs_get_core(drv->core_name, true); + if (!core) + return ret; + + mutex_lock(&core->lock); + if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)) + __info("Idle notifier for %s being replaced\n", + core->core_name); + core->idle_driver = drv; + mutex_unlock(&core->lock); + + return core->handle; +} +EXPORT_SYMBOL(msm_dcvs_idle_source_register); + +int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv) +{ + int ret = -EINVAL; + struct dcvs_core *core = NULL; + + if (!drv || !drv->core_name) + return ret; + + core = msm_dcvs_get_core(drv->core_name, false); + if (!core) + return ret; + + mutex_lock(&core->lock); + core->idle_driver = NULL; + mutex_unlock(&core->lock); + + return 0; +} +EXPORT_SYMBOL(msm_dcvs_idle_source_unregister); + +int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited) +{ + int ret = 0; + struct dcvs_core *core = NULL; + uint32_t timer_interval_us = 0; + uint32_t r0, r1; + uint32_t freq_changed = 0; + + if (handle >= CORE_HANDLE_OFFSET && + (handle - CORE_HANDLE_OFFSET) < CORES_MAX) + core = &core_list[handle - CORE_HANDLE_OFFSET]; + + BUG_ON(!core); + + if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE) + __info("Core %s idle state %d\n", core->core_name, state); + + switch (state) { + case MSM_DCVS_IDLE_ENTER: + hrtimer_cancel(&core->timer); + ret = msm_dcvs_scm_event(core->handle, + MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1); + if (ret) + __err("Error (%d) sending idle enter for %s\n", + ret, core->core_name); + break; + + case MSM_DCVS_IDLE_EXIT: + hrtimer_cancel(&core->timer); + ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT, + iowaited, &timer_interval_us, &freq_changed); + if (ret) + __err("Error (%d) sending idle exit for %s\n", + ret, core->core_name); + /* only start slack timer if change_freq won't */ + if (freq_changed || core->change_freq_activated) + break; + if (timer_interval_us && !core->timer_disabled) { + ret = hrtimer_start(&core->timer, + ktime_set(0, timer_interval_us * 1000), + HRTIMER_MODE_REL_PINNED); + + if (ret) + __err("Failed to register timer for core %s\n", + core->core_name); + } + break; + } + + return ret; +} +EXPORT_SYMBOL(msm_dcvs_idle); + +static int __init msm_dcvs_late_init(void) +{ + struct kobject *module_kobj = NULL; + int ret = 0; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + ret = -ENOENT; + goto err; + } + + cores_kobj = kobject_create_and_add("cores", module_kobj); + if (!cores_kobj) { + __err("Cannot create %s kobject\n", "cores"); + ret = -ENOMEM; + goto err; + } + + debugfs_base = debugfs_create_dir("msm_dcvs", NULL); + if (!debugfs_base) { + __err("Cannot create debugfs base %s\n", "msm_dcvs"); + ret = -ENOENT; + goto err; + } + + if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR, + debugfs_base, &msm_dcvs_debug)) { + __err("Cannot create debugfs entry %s\n", "debug_mask"); + ret = -ENOMEM; + goto err; + } + +err: + if (ret) { + kobject_del(cores_kobj); + cores_kobj = NULL; + debugfs_remove(debugfs_base); + } + + return ret; +} +late_initcall(msm_dcvs_late_init); + +static int __init msm_dcvs_early_init(void) +{ + int ret = 0; + + if (!msm_dcvs_enabled) { + __info("Not enabled (%d)\n", msm_dcvs_enabled); + return 0; + } + + ret = msm_dcvs_scm_init(10 * 1024); + if (ret) + __err("Unable to initialize DCVS err=%d\n", ret); + + return ret; +} +postcore_initcall(msm_dcvs_early_init); diff --git a/arch/arm/mach-msm/msm_dcvs_idle.c b/arch/arm/mach-msm/msm_dcvs_idle.c new file mode 100644 index 00000000000..179e17015fd --- /dev/null +++ b/arch/arm/mach-msm/msm_dcvs_idle.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cpu_idle_info { + int cpu; + int enabled; + int handle; + struct msm_dcvs_idle dcvs_notifier; +}; + +static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info); +static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu); +static char core_name[NR_CPUS][10]; +static struct pm_qos_request qos_req; +static uint32_t latency; + +static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self, + enum msm_core_control_event event) +{ + struct cpu_idle_info *info = container_of(self, + struct cpu_idle_info, dcvs_notifier); + + switch (event) { + case MSM_DCVS_ENABLE_IDLE_PULSE: + info->enabled = true; + break; + + case MSM_DCVS_DISABLE_IDLE_PULSE: + info->enabled = false; + break; + + case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES: + pm_qos_update_request(&qos_req, PM_QOS_DEFAULT_VALUE); + break; + + case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES: + pm_qos_update_request(&qos_req, latency); + break; + } + + return 0; +} + +static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd, + void *v) +{ + struct cpu_idle_info *info = + &per_cpu(cpu_idle_info, smp_processor_id()); + u64 io_wait_us = 0; + u64 prev_io_wait_us = 0; + u64 last_update_time = 0; + u64 val = 0; + uint32_t iowaited = 0; + + if (!info->enabled) + return NOTIFY_OK; + + switch (cmd) { + case CPU_PM_ENTER: + val = get_cpu_iowait_time_us(smp_processor_id(), + &last_update_time); + /* val could be -1 when NOHZ is not enabled */ + if (val == (u64)-1) + val = 0; + per_cpu(iowait_on_cpu, smp_processor_id()) = val; + msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0); + break; + + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id()); + val = get_cpu_iowait_time_us(smp_processor_id(), + &last_update_time); + if (val == (u64)-1) + val = 0; + io_wait_us = val; + iowaited = (io_wait_us - prev_io_wait_us); + msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block idle_nb = { + .notifier_call = msm_cpuidle_notifier, +}; + +static int msm_dcvs_idle_probe(struct platform_device *pdev) +{ + int cpu; + struct cpu_idle_info *info = NULL; + struct msm_dcvs_idle *inotify = NULL; + + for_each_possible_cpu(cpu) { + info = &per_cpu(cpu_idle_info, cpu); + info->cpu = cpu; + inotify = &info->dcvs_notifier; + snprintf(core_name[cpu], 10, "cpu%d", cpu); + inotify->core_name = core_name[cpu]; + inotify->enable = msm_dcvs_idle_notifier; + info->handle = msm_dcvs_idle_source_register(inotify); + BUG_ON(info->handle < 0); + } + + latency = *((uint32_t *)pdev->dev.platform_data); + pm_qos_add_request(&qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + return cpu_pm_register_notifier(&idle_nb); +} + +static int msm_dcvs_idle_remove(struct platform_device *pdev) +{ + int ret = 0; + int rc = 0; + int cpu = 0; + struct msm_dcvs_idle *inotify = NULL; + struct cpu_idle_info *info = NULL; + + rc = cpu_pm_unregister_notifier(&idle_nb); + + for_each_possible_cpu(cpu) { + info = &per_cpu(cpu_idle_info, cpu); + inotify = &info->dcvs_notifier; + ret = msm_dcvs_idle_source_unregister(inotify); + if (ret) { + rc = -EFAULT; + pr_err("Error de-registering core %d idle notifier.\n", + cpu); + } + } + + return rc; +} + +static struct platform_driver idle_pdrv = { + .probe = msm_dcvs_idle_probe, + .remove = __devexit_p(msm_dcvs_idle_remove), + .driver = { + .name = "msm_cpu_idle", + .owner = THIS_MODULE, + }, +}; + +static int msm_dcvs_idle_init(void) +{ + return platform_driver_register(&idle_pdrv); +} +late_initcall(msm_dcvs_idle_init); diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c new file mode 100644 index 00000000000..6095e08139b --- /dev/null +++ b/arch/arm/mach-msm/msm_dcvs_scm.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DCVS_CMD_CREATE_GROUP 1 +#define DCVS_CMD_REGISTER_CORE 2 +#define DCVS_CMD_SET_ALGO_PARAM 3 +#define DCVS_CMD_EVENT 4 +#define DCVS_CMD_INIT 5 + +struct scm_register_core { + uint32_t core_id; + uint32_t group_id; + phys_addr_t core_param_phy; + phys_addr_t freq_phy; +}; + +struct scm_algo { + uint32_t core_id; + phys_addr_t algo_phy; +}; + +struct scm_init { + uint32_t phy; + uint32_t size; +}; + +int msm_dcvs_scm_init(size_t size) +{ + int ret = 0; + struct scm_init init; + uint32_t p = 0; + + /* Allocate word aligned non-cacheable memory */ + p = allocate_contiguous_ebi_nomap(size, 4); + if (!p) + return -ENOMEM; + + init.phy = p; + init.size = size; + + ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_INIT, + &init, sizeof(init), NULL, 0); + + /* Not freed if the initialization succeeds */ + if (ret) + free_contiguous_memory_by_paddr(p); + + return ret; +} +EXPORT_SYMBOL(msm_dcvs_scm_init); + +int msm_dcvs_scm_create_group(uint32_t id) +{ + int ret = 0; + + ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_CREATE_GROUP, + &id, sizeof(uint32_t), NULL, 0); + + return ret; +} +EXPORT_SYMBOL(msm_dcvs_scm_create_group); + +int msm_dcvs_scm_register_core(uint32_t core_id, uint32_t group_id, + struct msm_dcvs_core_param *param, + struct msm_dcvs_freq_entry *freq) +{ + int ret = 0; + struct scm_register_core reg_data; + struct msm_dcvs_core_param *p = NULL; + struct msm_dcvs_freq_entry *f = NULL; + + p = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_core_param)), GFP_KERNEL); + if (!p) + return -ENOMEM; + + f = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_freq_entry) * + param->num_freq), GFP_KERNEL); + if (!f) { + kfree(p); + return -ENOMEM; + } + + memcpy(p, param, sizeof(struct msm_dcvs_core_param)); + memcpy(f, freq, sizeof(struct msm_dcvs_freq_entry) * param->num_freq); + + reg_data.core_id = core_id; + reg_data.group_id = group_id; + reg_data.core_param_phy = virt_to_phys(p); + reg_data.freq_phy = virt_to_phys(f); + + ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_REGISTER_CORE, + ®_data, sizeof(reg_data), NULL, 0); + + kfree(f); + kfree(p); + + return ret; +} +EXPORT_SYMBOL(msm_dcvs_scm_register_core); + +int msm_dcvs_scm_set_algo_params(uint32_t core_id, + struct msm_dcvs_algo_param *param) +{ + int ret = 0; + struct scm_algo algo; + struct msm_dcvs_algo_param *p = NULL; + + p = kzalloc(PAGE_ALIGN(sizeof(struct msm_dcvs_algo_param)), GFP_KERNEL); + if (!p) + return -ENOMEM; + + memcpy(p, param, sizeof(struct msm_dcvs_algo_param)); + + algo.core_id = core_id; + algo.algo_phy = virt_to_phys(p); + + ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM, + &algo, sizeof(algo), NULL, 0); + + kfree(p); + + return ret; +} +EXPORT_SYMBOL(msm_dcvs_scm_set_algo_params); + +int msm_dcvs_scm_event(uint32_t core_id, + enum msm_dcvs_scm_event event_id, + uint32_t param0, uint32_t param1, + uint32_t *ret0, uint32_t *ret1) +{ + int ret = -EINVAL; + + if (!ret0 || !ret1) + return ret; + + ret = scm_call_atomic4_3(SCM_SVC_DCVS, DCVS_CMD_EVENT, + core_id, event_id, param0, param1, ret0, ret1); + + return ret; +} +EXPORT_SYMBOL(msm_dcvs_scm_event); diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c new file mode 100644 index 00000000000..eda22e13036 --- /dev/null +++ b/arch/arm/mach-msm/msm_dsps.c @@ -0,0 +1,1006 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * msm_dsps - control DSPS clocks, gpios and vregs. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ramdump.h" +#include "timer.h" + +#define DRV_NAME "msm_dsps" +#define DRV_VERSION "4.01" + +#define PPSS_PAUSE_REG 0x1804 + +#define PPSS_TIMER0_32KHZ_REG 0x1004 +#define PPSS_TIMER0_20MHZ_REG 0x0804 + +/** + * Driver Context + * + * @dev_class - device class. + * @dev_num - device major & minor number. + * @dev - the device. + * @cdev - character device for user interface. + * @pdata - platform data. + * @pil - handle to DSPS Firmware loader. + * @dspsfw_ramdump_dev - handle to ramdump device for DSPS + * @dspsfw_ramdump_segments - Ramdump segment information for DSPS + * @smem_ramdump_dev - handle to ramdump device for smem + * @smem_ramdump_segments - Ramdump segment information for smem + * @is_on - DSPS is on. + * @ref_count - open/close reference count. + * @wdog_irq - DSPS Watchdog IRQ + * @crash_in_progress - 1 if crash recovery is in progress + * @ppss_base - ppss registers virtual base address. + */ +struct dsps_drv { + + struct class *dev_class; + dev_t dev_num; + struct device *dev; + struct cdev *cdev; + + struct msm_dsps_platform_data *pdata; + + void *pil; + + void *dspsfw_ramdump_dev; + struct ramdump_segment dspsfw_ramdump_segments[4]; + + void *smem_ramdump_dev; + struct ramdump_segment smem_ramdump_segments[1]; + + int is_on; + int ref_count; + int wdog_irq; + + atomic_t crash_in_progress; + void __iomem *ppss_base; +}; + +/** + * Driver context. + */ +static struct dsps_drv *drv; + +/** + * self-initiated shutdown flag + */ +static int dsps_crash_shutdown_g; + +static void dsps_restart_handler(void); + +/** + * Load DSPS Firmware. + */ +static int dsps_load(const char *name) +{ + pr_debug("%s.\n", __func__); + + drv->pil = pil_get(name); + + if (IS_ERR(drv->pil)) { + pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name); + return -ENODEV; + } + msleep(20); + return 0; +} + +/** + * Unload DSPS Firmware. + */ +static void dsps_unload(void) +{ + pr_debug("%s.\n", __func__); + + pil_put(drv->pil); +} + +/** + * Suspend DSPS CPU. + */ +static void dsps_suspend(void) +{ + pr_debug("%s.\n", __func__); + + writel_relaxed(1, drv->ppss_base + PPSS_PAUSE_REG); + mb(); /* Make sure write commited before ioctl returns. */ +} + +/** + * Resume DSPS CPU. + */ +static void dsps_resume(void) +{ + pr_debug("%s.\n", __func__); + + writel_relaxed(0, drv->ppss_base + PPSS_PAUSE_REG); + mb(); /* Make sure write commited before ioctl returns. */ +} + +/** + * Read DSPS slow timer. + */ +static u32 dsps_read_slow_timer(void) +{ + u32 val; + + /* Read the timer value from the MSM sclk. The MSM slow clock & DSPS + * timers are in sync, so these are the same value */ + val = msm_timer_get_sclk_ticks(); + pr_debug("%s.count=%d.\n", __func__, val); + + return val; +} + +/** + * Read DSPS fast timer. + */ +static u32 dsps_read_fast_timer(void) +{ + u32 val; + + val = readl_relaxed(drv->ppss_base + PPSS_TIMER0_20MHZ_REG); + rmb(); /* order reads from the user output buffer */ + + pr_debug("%s.count=%d.\n", __func__, val); + + return val; +} + +/** + * Power on request. + * + * Set clocks to ON. + * Set sensors chip-select GPIO to non-reset (on) value. + * + */ +static int dsps_power_on_handler(void) +{ + int ret = 0; + int i, ci, gi, ri; + + pr_debug("%s.\n", __func__); + + if (drv->is_on) { + pr_debug("%s: already ON.\n", __func__); + return 0; + } + + for (ci = 0; ci < drv->pdata->clks_num; ci++) { + const char *name = drv->pdata->clks[ci].name; + u32 rate = drv->pdata->clks[ci].rate; + struct clk *clock = drv->pdata->clks[ci].clock; + + if (clock == NULL) + continue; + + if (rate > 0) { + ret = clk_set_rate(clock, rate); + pr_debug("%s: clk %s set rate %d.", + __func__, name, rate); + if (ret) { + pr_err("%s: clk %s set rate %d. err=%d.", + __func__, name, rate, ret); + goto clk_err; + } + + } + + ret = clk_prepare_enable(clock); + if (ret) { + pr_err("%s: enable clk %s err %d.", + __func__, name, ret); + goto clk_err; + } + } + + for (gi = 0; gi < drv->pdata->gpios_num; gi++) { + const char *name = drv->pdata->gpios[gi].name; + int num = drv->pdata->gpios[gi].num; + int val = drv->pdata->gpios[gi].on_val; + int is_owner = drv->pdata->gpios[gi].is_owner; + + if (!is_owner) + continue; + + ret = gpio_direction_output(num, val); + if (ret) { + pr_err("%s: set GPIO %s num %d to %d err %d.", + __func__, name, num, val, ret); + goto gpio_err; + } + } + + for (ri = 0; ri < drv->pdata->regs_num; ri++) { + const char *name = drv->pdata->regs[ri].name; + struct regulator *reg = drv->pdata->regs[ri].reg; + int volt = drv->pdata->regs[ri].volt; + + if (reg == NULL) + continue; + + pr_debug("%s: set regulator %s.", __func__, name); + + ret = regulator_set_voltage(reg, volt, volt); + + if (ret) { + pr_err("%s: set regulator %s voltage %d err = %d.\n", + __func__, name, volt, ret); + goto reg_err; + } + + ret = regulator_enable(reg); + if (ret) { + pr_err("%s: enable regulator %s err = %d.\n", + __func__, name, ret); + goto reg_err; + } + } + + drv->is_on = true; + + return 0; + + /* + * If failling to set ANY clock/gpio/regulator to ON then we set + * them back to OFF to avoid consuming power for unused + * clocks/gpios/regulators. + */ +reg_err: + for (i = 0; i < ri; i++) { + struct regulator *reg = drv->pdata->regs[ri].reg; + + if (reg == NULL) + continue; + + regulator_disable(reg); + } + +gpio_err: + for (i = 0; i < gi; i++) { + int num = drv->pdata->gpios[i].num; + int val = drv->pdata->gpios[i].off_val; + int is_owner = drv->pdata->gpios[i].is_owner; + + if (!is_owner) + continue; + + ret = gpio_direction_output(num, val); + } + +clk_err: + for (i = 0; i < ci; i++) { + struct clk *clock = drv->pdata->clks[i].clock; + + if (clock == NULL) + continue; + + clk_disable_unprepare(clock); + } + + return -ENODEV; +} + +/** + * Power off request. + * + * Set clocks to OFF. + * Set sensors chip-select GPIO to reset (off) value. + * + */ +static int dsps_power_off_handler(void) +{ + int ret; + int i; + + pr_debug("%s.\n", __func__); + + if (!drv->is_on) { + pr_debug("%s: already OFF.\n", __func__); + return 0; + } + + for (i = 0; i < drv->pdata->clks_num; i++) + if (drv->pdata->clks[i].clock) { + const char *name = drv->pdata->clks[i].name; + + pr_debug("%s: set clk %s off.", __func__, name); + clk_disable_unprepare(drv->pdata->clks[i].clock); + } + + for (i = 0; i < drv->pdata->regs_num; i++) + if (drv->pdata->regs[i].reg) { + const char *name = drv->pdata->regs[i].name; + + pr_debug("%s: set regulator %s off.", __func__, name); + regulator_disable(drv->pdata->regs[i].reg); + } + + /* Clocks on/off has reference count but GPIOs don't. */ + drv->is_on = false; + + for (i = 0; i < drv->pdata->gpios_num; i++) { + const char *name = drv->pdata->gpios[i].name; + int num = drv->pdata->gpios[i].num; + int val = drv->pdata->gpios[i].off_val; + + pr_debug("%s: set gpio %s off.", __func__, name); + + ret = gpio_direction_output(num, val); + if (ret) { + pr_err("%s: set GPIO %s err %d.", __func__, name, ret); + return ret; + } + } + + return 0; +} + +/** + * + * Log subsystem restart failure reason + */ +static void dsps_log_sfr(void) +{ + const char dflt_reason[] = "Died too early due to unknown reason"; + char *smem_reset_reason; + unsigned smem_reset_size; + + smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0, + &smem_reset_size); + if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) { + smem_reset_reason[smem_reset_size-1] = 0; + pr_err("%s: DSPS failure: %s\nResetting DSPS\n", + __func__, smem_reset_reason); + memset(smem_reset_reason, 0, smem_reset_size); + wmb(); + } else + pr_err("%s: DSPS failure: %s\nResetting DSPS\n", + __func__, dflt_reason); +} + +/** + * Watchdog interrupt handler + * + */ +static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id) +{ + pr_err("%s\n", __func__); + dsps_log_sfr(); + dsps_restart_handler(); + return IRQ_HANDLED; +} + +/** + * IO Control - handle commands from client. + * + */ +static long dsps_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + u32 val = 0; + + pr_debug("%s.\n", __func__); + + switch (cmd) { + case DSPS_IOCTL_ON: + ret = dsps_power_on_handler(); + dsps_resume(); + break; + case DSPS_IOCTL_OFF: + if (!drv->pdata->dsps_pwr_ctl_en) { + dsps_suspend(); + ret = dsps_power_off_handler(); + } + break; + case DSPS_IOCTL_READ_SLOW_TIMER: + val = dsps_read_slow_timer(); + ret = put_user(val, (u32 __user *) arg); + break; + case DSPS_IOCTL_READ_FAST_TIMER: + val = dsps_read_fast_timer(); + ret = put_user(val, (u32 __user *) arg); + break; + case DSPS_IOCTL_RESET: + pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n", + __func__); + dsps_restart_handler(); + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * allocate resources. + * @pdev - pointer to platform device. + */ +static int dsps_alloc_resources(struct platform_device *pdev) +{ + int ret = -ENODEV; + struct resource *ppss_res; + struct resource *ppss_wdog; + int i; + + pr_debug("%s.\n", __func__); + + if ((drv->pdata->signature != DSPS_SIGNATURE)) { + pr_err("%s: invalid signature for pdata.", __func__); + return -EINVAL; + } + + ppss_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "ppss_reg"); + if (!ppss_res) { + pr_err("%s: failed to get ppss_reg resource.\n", __func__); + return -EINVAL; + } + + for (i = 0; i < drv->pdata->clks_num; i++) { + const char *name = drv->pdata->clks[i].name; + struct clk *clock; + + drv->pdata->clks[i].clock = NULL; + + pr_debug("%s: get clk %s.", __func__, name); + + clock = clk_get(drv->dev, name); + if (IS_ERR(clock)) { + pr_err("%s: can't get clk %s.", __func__, name); + goto clk_err; + } + drv->pdata->clks[i].clock = clock; + } + + for (i = 0; i < drv->pdata->gpios_num; i++) { + const char *name = drv->pdata->gpios[i].name; + int num = drv->pdata->gpios[i].num; + + drv->pdata->gpios[i].is_owner = false; + + pr_debug("%s: get gpio %s.", __func__, name); + + ret = gpio_request(num, name); + if (ret) { + pr_err("%s: request GPIO %s err %d.", + __func__, name, ret); + goto gpio_err; + } + + drv->pdata->gpios[i].is_owner = true; + + } + + for (i = 0; i < drv->pdata->regs_num; i++) { + const char *name = drv->pdata->regs[i].name; + + drv->pdata->regs[i].reg = NULL; + + pr_debug("%s: get regulator %s.", __func__, name); + + drv->pdata->regs[i].reg = regulator_get(drv->dev, name); + if (IS_ERR(drv->pdata->regs[i].reg)) { + pr_err("%s: get regulator %s failed.", + __func__, name); + goto reg_err; + } + } + + drv->ppss_base = ioremap(ppss_res->start, + resource_size(ppss_res)); + + ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "ppss_wdog"); + if (ppss_wdog) { + drv->wdog_irq = ppss_wdog->start; + ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq, + IRQF_TRIGGER_RISING, "dsps_wdog", NULL); + if (ret) { + pr_err("%s: request_irq fail %d\n", __func__, ret); + goto request_irq_err; + } + } else { + drv->wdog_irq = -1; + pr_debug("%s: ppss_wdog not supported.\n", __func__); + } + + drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start; + drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size; + drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start; + drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size; + drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start; + drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size; + drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start; + drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size; + + drv->dspsfw_ramdump_dev = create_ramdump_device("dsps"); + if (!drv->dspsfw_ramdump_dev) { + pr_err("%s: create_ramdump_device(\"dsps\") fail\n", + __func__); + goto create_ramdump_err; + } + + drv->smem_ramdump_segments[0].address = drv->pdata->smem_start; + drv->smem_ramdump_segments[0].size = drv->pdata->smem_size; + drv->smem_ramdump_dev = create_ramdump_device("smem"); + if (!drv->smem_ramdump_dev) { + pr_err("%s: create_ramdump_device(\"smem\") fail\n", + __func__); + goto create_ramdump_err; + } + + if (drv->pdata->init) + drv->pdata->init(drv->pdata); + + return 0; + +create_ramdump_err: + disable_irq_nosync(drv->wdog_irq); + free_irq(drv->wdog_irq, NULL); + +request_irq_err: + iounmap(drv->ppss_base); + +reg_err: + for (i = 0; i < drv->pdata->regs_num; i++) { + if (drv->pdata->regs[i].reg) { + regulator_put(drv->pdata->regs[i].reg); + drv->pdata->regs[i].reg = NULL; + } + } + +gpio_err: + for (i = 0; i < drv->pdata->gpios_num; i++) + if (drv->pdata->gpios[i].is_owner) { + gpio_free(drv->pdata->gpios[i].num); + drv->pdata->gpios[i].is_owner = false; + } +clk_err: + for (i = 0; i < drv->pdata->clks_num; i++) + if (drv->pdata->clks[i].clock) { + clk_put(drv->pdata->clks[i].clock); + drv->pdata->clks[i].clock = NULL; + } + + return ret; +} + +/** + * Open File. + * + */ +static int dsps_open(struct inode *ip, struct file *fp) +{ + int ret = 0; + + pr_debug("%s.\n", __func__); + + if (drv->ref_count == 0) { + + /* clocks must be ON before loading.*/ + ret = dsps_power_on_handler(); + if (ret) + return ret; + + ret = dsps_load(drv->pdata->pil_name); + + if (ret) { + dsps_power_off_handler(); + return ret; + } + + dsps_resume(); + } + drv->ref_count++; + + return ret; +} + +/** + * free resources. + * + */ +static void dsps_free_resources(void) +{ + int i; + + pr_debug("%s.\n", __func__); + + for (i = 0; i < drv->pdata->clks_num; i++) + if (drv->pdata->clks[i].clock) { + clk_put(drv->pdata->clks[i].clock); + drv->pdata->clks[i].clock = NULL; + } + + for (i = 0; i < drv->pdata->gpios_num; i++) + if (drv->pdata->gpios[i].is_owner) { + gpio_free(drv->pdata->gpios[i].num); + drv->pdata->gpios[i].is_owner = false; + } + + for (i = 0; i < drv->pdata->regs_num; i++) { + if (drv->pdata->regs[i].reg) { + regulator_put(drv->pdata->regs[i].reg); + drv->pdata->regs[i].reg = NULL; + } + } + + free_irq(drv->wdog_irq, NULL); + + iounmap(drv->ppss_base); +} + +/** + * Close File. + * + * The client shall close and re-open the file for re-loading the DSPS + * firmware. + * The file system will close the file if the user space app has crashed. + * + * If the DSPS is running, then we must reset DSPS CPU & HW before + * setting the clocks off. + * The DSPS reset should be done as part of the pil_put(). + * The DSPS reset should be used for error recovery if the DSPS firmware + * has crashed and re-loading the firmware is required. + */ +static int dsps_release(struct inode *inode, struct file *file) +{ + pr_debug("%s.\n", __func__); + + drv->ref_count--; + + if (drv->ref_count == 0) { + if (!drv->pdata->dsps_pwr_ctl_en) { + dsps_suspend(); + + dsps_unload(); + + dsps_power_off_handler(); + } + } + + return 0; +} + +const struct file_operations dsps_fops = { + .owner = THIS_MODULE, + .open = dsps_open, + .release = dsps_release, + .unlocked_ioctl = dsps_ioctl, +}; + +/** + * Fatal error handler + * Resets DSPS. + */ +static void dsps_restart_handler(void) +{ + pr_debug("%s: Restart lvl %d\n", + __func__, get_restart_level()); + + if (atomic_add_return(1, &drv->crash_in_progress) > 1) { + pr_err("%s: DSPS already resetting. Count %d\n", __func__, + atomic_read(&drv->crash_in_progress)); + } else { + subsystem_restart("dsps"); + } +} + + +/** + * SMSM state change callback + * + */ +static void dsps_smsm_state_cb(void *data, uint32_t old_state, + uint32_t new_state) +{ + pr_debug("%s\n", __func__); + if (dsps_crash_shutdown_g == 1) { + pr_debug("%s: SMSM_RESET state change ignored\n", + __func__); + dsps_crash_shutdown_g = 0; + return; + } + if (new_state & SMSM_RESET) { + dsps_log_sfr(); + dsps_restart_handler(); + } +} + +/** + * Shutdown function + * called by the restart notifier + * + */ +static int dsps_shutdown(const struct subsys_data *subsys) +{ + pr_debug("%s\n", __func__); + disable_irq_nosync(drv->wdog_irq); + dsps_suspend(); + pil_force_shutdown(drv->pdata->pil_name); + dsps_power_off_handler(); + return 0; +} + +/** + * Powerup function + * called by the restart notifier + * + */ +static int dsps_powerup(const struct subsys_data *subsys) +{ + pr_debug("%s\n", __func__); + dsps_power_on_handler(); + pil_force_boot(drv->pdata->pil_name); + atomic_set(&drv->crash_in_progress, 0); + enable_irq(drv->wdog_irq); + dsps_resume(); + return 0; +} + +/** + * Crash shutdown function + * called by the restart notifier + * + */ +static void dsps_crash_shutdown(const struct subsys_data *subsys) +{ + pr_debug("%s\n", __func__); + dsps_crash_shutdown_g = 1; + smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET); +} + +/** + * Ramdump function + * called by the restart notifier + * + */ +static int dsps_ramdump(int enable, const struct subsys_data *subsys) +{ + int ret = 0; + pr_debug("%s\n", __func__); + + if (enable) { + if (drv->dspsfw_ramdump_dev != NULL) { + ret = do_ramdump(drv->dspsfw_ramdump_dev, + drv->dspsfw_ramdump_segments, + ARRAY_SIZE(drv->dspsfw_ramdump_segments)); + if (ret < 0) { + pr_err("%s: Unable to dump DSPS memory (rc = %d).\n", + __func__, ret); + goto dsps_ramdump_out; + } + } + if (drv->smem_ramdump_dev != NULL) { + ret = do_ramdump(drv->smem_ramdump_dev, + drv->smem_ramdump_segments, + ARRAY_SIZE(drv->smem_ramdump_segments)); + if (ret < 0) { + pr_err("%s: Unable to dump smem memory (rc = %d).\n", + __func__, ret); + goto dsps_ramdump_out; + } + } + } + +dsps_ramdump_out: + return ret; +} + +static struct subsys_data dsps_ssrops = { + .name = "dsps", + .shutdown = dsps_shutdown, + .powerup = dsps_powerup, + .ramdump = dsps_ramdump, + .crash_shutdown = dsps_crash_shutdown +}; + +/** + * platform driver + * + */ +static int __devinit dsps_probe(struct platform_device *pdev) +{ + int ret; + + pr_debug("%s.\n", __func__); + + if (pdev->dev.platform_data == NULL) { + pr_err("%s: platform data is NULL.\n", __func__); + return -ENODEV; + } + + drv = kzalloc(sizeof(*drv), GFP_KERNEL); + if (drv == NULL) { + pr_err("%s: kzalloc fail.\n", __func__); + goto alloc_err; + } + atomic_set(&drv->crash_in_progress, 0); + + drv->pdata = pdev->dev.platform_data; + + drv->dev_class = class_create(THIS_MODULE, DRV_NAME); + if (drv->dev_class == NULL) { + pr_err("%s: class_create fail.\n", __func__); + goto res_err; + } + + ret = alloc_chrdev_region(&drv->dev_num, 0, 1, DRV_NAME); + if (ret) { + pr_err("%s: alloc_chrdev_region fail.\n", __func__); + goto alloc_chrdev_region_err; + } + + drv->dev = device_create(drv->dev_class, NULL, + drv->dev_num, + drv, DRV_NAME); + if (IS_ERR(drv->dev)) { + pr_err("%s: device_create fail.\n", __func__); + goto device_create_err; + } + + drv->cdev = cdev_alloc(); + if (drv->cdev == NULL) { + pr_err("%s: cdev_alloc fail.\n", __func__); + goto cdev_alloc_err; + } + cdev_init(drv->cdev, &dsps_fops); + drv->cdev->owner = THIS_MODULE; + + ret = cdev_add(drv->cdev, drv->dev_num, 1); + if (ret) { + pr_err("%s: cdev_add fail.\n", __func__); + goto cdev_add_err; + } + + ret = dsps_alloc_resources(pdev); + if (ret) { + pr_err("%s: failed to allocate dsps resources.\n", __func__); + goto cdev_add_err; + } + + ret = + smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET, + dsps_smsm_state_cb, 0); + if (ret) { + pr_err("%s: smsm_state_cb_register fail %d\n", __func__, + ret); + goto smsm_register_err; + } + + ret = ssr_register_subsystem(&dsps_ssrops); + if (ret) { + pr_err("%s: ssr_register_subsystem fail %d\n", __func__, + ret); + goto ssr_register_err; + } + + return 0; + +ssr_register_err: + smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET, + dsps_smsm_state_cb, + 0); +smsm_register_err: + cdev_del(drv->cdev); +cdev_add_err: + kfree(drv->cdev); +cdev_alloc_err: + device_destroy(drv->dev_class, drv->dev_num); +device_create_err: + unregister_chrdev_region(drv->dev_num, 1); +alloc_chrdev_region_err: + class_destroy(drv->dev_class); +res_err: + kfree(drv); + drv = NULL; +alloc_err: + return -ENODEV; +} + +static int __devexit dsps_remove(struct platform_device *pdev) +{ + pr_debug("%s.\n", __func__); + + dsps_power_off_handler(); + dsps_free_resources(); + + cdev_del(drv->cdev); + kfree(drv->cdev); + drv->cdev = NULL; + device_destroy(drv->dev_class, drv->dev_num); + unregister_chrdev_region(drv->dev_num, 1); + class_destroy(drv->dev_class); + kfree(drv); + drv = NULL; + + return 0; +} + +static struct platform_driver dsps_driver = { + .probe = dsps_probe, + .remove = __exit_p(dsps_remove), + .driver = { + .name = "msm_dsps", + }, +}; + +/** + * Module Init. + */ +static int __init dsps_init(void) +{ + int ret; + + pr_info("%s driver version %s.\n", DRV_NAME, DRV_VERSION); + + ret = platform_driver_register(&dsps_driver); + + if (ret) + pr_err("dsps_init.err=%d.\n", ret); + + return ret; +} + +/** + * Module Exit. + */ +static void __exit dsps_exit(void) +{ + pr_debug("%s.\n", __func__); + + platform_driver_unregister(&dsps_driver); +} + +module_init(dsps_init); +module_exit(dsps_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Dedicated Sensors Processor Subsystem (DSPS) driver"); +MODULE_AUTHOR("Amir Samuelov "); + diff --git a/arch/arm/mach-msm/msm_fault_handlers.c b/arch/arm/mach-msm/msm_fault_handlers.c new file mode 100644 index 00000000000..c97585605b4 --- /dev/null +++ b/arch/arm/mach-msm/msm_fault_handlers.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 1995 Linus Torvalds + * Modifications for ARM processor (c) 1995-2004 Russell King + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include "acpuclock.h" + +#define __str(x) #x +#define MRC(x, v1, v2, v4, v5, v6) do { \ + unsigned int __##x; \ + asm("mrc " __str(v1) ", " __str(v2) ", %0, " __str(v4) ", " \ + __str(v5) ", " __str(v6) "\n" \ + : "=r" (__##x)); \ + pr_info("%s: %s = 0x%.8x\n", __func__, #x, __##x); \ +} while (0) + +static int msm_imp_ext_abort(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + int cpu; + unsigned int regval; + static unsigned char flush_toggle; + + asm("mrc p15, 7, %0, c15, c0, 1\n" /* read EFSR for fault status */ + : "=r" (regval)); + if (regval == 0x2) { + /* Fault was caused by icache parity error. Alternate + * simply retrying the access and flushing the icache. */ + flush_toggle ^= 1; + if (flush_toggle) + asm("mcr p15, 0, %0, c7, c5, 0\n" + : + : "r" (regval)); /* input value is ignored */ + /* Clear fault in EFSR. */ + asm("mcr p15, 7, %0, c15, c0, 1\n" + : + : "r" (regval)); + /* Clear fault in ADFSR. */ + regval = 0; + asm("mcr p15, 0, %0, c5, c1, 0\n" + : + : "r" (regval)); + return 0; + } + + MRC(ADFSR, p15, 0, c5, c1, 0); + MRC(DFSR, p15, 0, c5, c0, 0); + MRC(ACTLR, p15, 0, c1, c0, 1); + MRC(EFSR, p15, 7, c15, c0, 1); + MRC(L2SR, p15, 3, c15, c1, 0); + MRC(L2CR0, p15, 3, c15, c0, 1); + MRC(L2CPUESR, p15, 3, c15, c1, 1); + MRC(L2CPUCR, p15, 3, c15, c0, 2); + MRC(SPESR, p15, 1, c9, c7, 0); + MRC(SPCR, p15, 0, c9, c7, 0); + MRC(DMACHSR, p15, 1, c11, c0, 0); + MRC(DMACHESR, p15, 1, c11, c0, 1); + MRC(DMACHCR, p15, 0, c11, c0, 2); + for_each_online_cpu(cpu) + pr_info("cpu %d, acpuclk rate: %lu kHz\n", cpu, + acpuclk_get_rate(cpu)); + + return 1; +} + +static int __init msm_register_fault_handlers(void) +{ + /* hook in our handler for imprecise abort for when we get + i-cache parity errors */ + hook_fault_code(22, msm_imp_ext_abort, SIGBUS, 0, + "imprecise external abort"); + + return 0; +} +arch_initcall(msm_register_fault_handlers); diff --git a/arch/arm/mach-msm/msm_kexec.c b/arch/arm/mach-msm/msm_kexec.c new file mode 100644 index 00000000000..4597cf0da81 --- /dev/null +++ b/arch/arm/mach-msm/msm_kexec.c @@ -0,0 +1,30 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#ifdef CONFIG_MSM_WATCHDOG +#include + +#define WDT0_EN (MSM_TMR_BASE + 0x40) +#endif + +void arch_kexec(void) +{ +#ifdef CONFIG_MSM_WATCHDOG + /* Prevent watchdog from resetting SoC */ + writel(0, WDT0_EN); + pr_crit("KEXEC: MSM Watchdog Exit - Deactivated\n"); +#endif + return; +} diff --git a/arch/arm/mach-msm/msm_rq_stats.c b/arch/arm/mach-msm/msm_rq_stats.c new file mode 100644 index 00000000000..738819f9804 --- /dev/null +++ b/arch/arm/mach-msm/msm_rq_stats.c @@ -0,0 +1,220 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Qualcomm MSM Runqueue Stats Interface for Userspace + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_LONG_SIZE 24 +#define DEFAULT_RQ_POLL_JIFFIES 1 +#define DEFAULT_DEF_TIMER_JIFFIES 5 + +static void def_work_fn(struct work_struct *work) +{ + int64_t diff; + + diff = ktime_to_ns(ktime_get()) - rq_info.def_start_time; + do_div(diff, 1000 * 1000); + rq_info.def_interval = (unsigned int) diff; + + /* Notify polling threads on change of value */ + sysfs_notify(rq_info.kobj, NULL, "def_timer_ms"); +} + +static ssize_t show_run_queue_avg(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + unsigned int val = 0; + unsigned long flags = 0; + + spin_lock_irqsave(&rq_lock, flags); + /* rq avg currently available only on one core */ + val = rq_info.rq_avg; + rq_info.rq_avg = 0; + spin_unlock_irqrestore(&rq_lock, flags); + + return snprintf(buf, PAGE_SIZE, "%d.%d\n", val/10, val%10); +} + +static ssize_t show_run_queue_poll_ms(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret = 0; + unsigned long flags = 0; + + spin_lock_irqsave(&rq_lock, flags); + ret = snprintf(buf, MAX_LONG_SIZE, "%u\n", + jiffies_to_msecs(rq_info.rq_poll_jiffies)); + spin_unlock_irqrestore(&rq_lock, flags); + + return ret; +} + +static ssize_t store_run_queue_poll_ms(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + unsigned long flags = 0; + static DEFINE_MUTEX(lock_poll_ms); + + mutex_lock(&lock_poll_ms); + + spin_lock_irqsave(&rq_lock, flags); + sscanf(buf, "%u", &val); + rq_info.rq_poll_jiffies = msecs_to_jiffies(val); + spin_unlock_irqrestore(&rq_lock, flags); + + mutex_unlock(&lock_poll_ms); + + return count; +} + +static ssize_t show_def_timer_ms(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, MAX_LONG_SIZE, "%u\n", rq_info.def_interval); +} + +static ssize_t store_def_timer_ms(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int val = 0; + + sscanf(buf, "%u", &val); + rq_info.def_timer_jiffies = msecs_to_jiffies(val); + + rq_info.def_start_time = ktime_to_ns(ktime_get()); + return count; +} + +#define MSM_RQ_STATS_RO_ATTRIB(att) ({ \ + struct attribute *attrib = NULL; \ + struct kobj_attribute *ptr = NULL; \ + ptr = kzalloc(sizeof(struct kobj_attribute), GFP_KERNEL); \ + if (ptr) { \ + ptr->attr.name = #att; \ + ptr->attr.mode = S_IRUGO; \ + ptr->show = show_##att; \ + ptr->store = NULL; \ + attrib = &ptr->attr; \ + } \ + attrib; }) + +#define MSM_RQ_STATS_RW_ATTRIB(att) ({ \ + struct attribute *attrib = NULL; \ + struct kobj_attribute *ptr = NULL; \ + ptr = kzalloc(sizeof(struct kobj_attribute), GFP_KERNEL); \ + if (ptr) { \ + ptr->attr.name = #att; \ + ptr->attr.mode = S_IWUSR|S_IRUSR; \ + ptr->show = show_##att; \ + ptr->store = store_##att; \ + attrib = &ptr->attr; \ + } \ + attrib; }) + +static int init_rq_attribs(void) +{ + int i; + int err = 0; + const int attr_count = 4; + + struct attribute **attribs = + kzalloc(sizeof(struct attribute *) * attr_count, GFP_KERNEL); + + if (!attribs) + goto rel; + + rq_info.rq_avg = 0; + + attribs[0] = MSM_RQ_STATS_RW_ATTRIB(def_timer_ms); + attribs[1] = MSM_RQ_STATS_RO_ATTRIB(run_queue_avg); + attribs[2] = MSM_RQ_STATS_RW_ATTRIB(run_queue_poll_ms); + attribs[3] = NULL; + + for (i = 0; i < attr_count - 1 ; i++) { + if (!attribs[i]) + goto rel2; + } + + rq_info.attr_group = kzalloc(sizeof(struct attribute_group), + GFP_KERNEL); + if (!rq_info.attr_group) + goto rel3; + rq_info.attr_group->attrs = attribs; + + /* Create /sys/devices/system/cpu/cpu0/rq-stats/... */ + rq_info.kobj = kobject_create_and_add("rq-stats", + &get_cpu_device(0)->kobj); + if (!rq_info.kobj) + goto rel3; + + err = sysfs_create_group(rq_info.kobj, rq_info.attr_group); + if (err) + kobject_put(rq_info.kobj); + else + kobject_uevent(rq_info.kobj, KOBJ_ADD); + + if (!err) + return err; + +rel3: + kfree(rq_info.attr_group); + kfree(rq_info.kobj); +rel2: + for (i = 0; i < attr_count - 1; i++) + kfree(attribs[i]); +rel: + kfree(attribs); + + return -ENOMEM; +} + +static int __init msm_rq_stats_init(void) +{ + int ret; + + /* Bail out if this is not an SMP Target */ + if (!is_smp()) { + rq_info.init = 0; + return -ENOSYS; + } + + rq_wq = create_singlethread_workqueue("rq_stats"); + BUG_ON(!rq_wq); + INIT_WORK(&rq_info.def_timer_work, def_work_fn); + spin_lock_init(&rq_lock); + rq_info.rq_poll_jiffies = DEFAULT_RQ_POLL_JIFFIES; + rq_info.def_timer_jiffies = DEFAULT_DEF_TIMER_JIFFIES; + rq_info.rq_poll_last_jiffy = 0; + rq_info.def_timer_last_jiffy = 0; + ret = init_rq_attribs(); + + rq_info.init = 1; + return ret; +} +late_initcall(msm_rq_stats_init); diff --git a/arch/arm/mach-msm/msm_rtb.c b/arch/arm/mach-msm/msm_rtb.c new file mode 100644 index 00000000000..9dbf9c1ebed --- /dev/null +++ b/arch/arm/mach-msm/msm_rtb.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SENTINEL_BYTE_1 0xFF +#define SENTINEL_BYTE_2 0xAA +#define SENTINEL_BYTE_3 0xFF + +/* Write + * 1) 3 bytes sentinel + * 2) 1 bytes of log type + * 3) 4 bytes of where the caller came from + * 4) 4 bytes index + * 4) 4 bytes extra data from the caller + * + * Total = 16 bytes. + */ +struct msm_rtb_layout { + unsigned char sentinel[3]; + unsigned char log_type; + void *caller; + unsigned long idx; + void *data; +} __attribute__ ((__packed__)); + + +struct msm_rtb_state { + struct msm_rtb_layout *rtb; + unsigned long phys; + int nentries; + int size; + int enabled; + int initialized; + uint32_t filter; + int step_size; +}; + +#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS) +DEFINE_PER_CPU(atomic_t, msm_rtb_idx_cpu); +#else +static atomic_t msm_rtb_idx; +#endif + +struct msm_rtb_state msm_rtb = { + .filter = 1 << LOGK_LOGBUF, + .enabled = 1, +}; + +module_param_named(filter, msm_rtb.filter, uint, 0644); +module_param_named(enable, msm_rtb.enabled, int, 0644); + +static int msm_rtb_panic_notifier(struct notifier_block *this, + unsigned long event, void *ptr) +{ + msm_rtb.enabled = 0; + return NOTIFY_DONE; +} + +static struct notifier_block msm_rtb_panic_blk = { + .notifier_call = msm_rtb_panic_notifier, +}; + +int msm_rtb_event_should_log(enum logk_event_type log_type) +{ + return msm_rtb.initialized && msm_rtb.enabled && + ((1 << (log_type & ~LOGTYPE_NOPC)) & msm_rtb.filter); +} +EXPORT_SYMBOL(msm_rtb_event_should_log); + +static void msm_rtb_emit_sentinel(struct msm_rtb_layout *start) +{ + start->sentinel[0] = SENTINEL_BYTE_1; + start->sentinel[1] = SENTINEL_BYTE_2; + start->sentinel[2] = SENTINEL_BYTE_3; +} + +static void msm_rtb_write_type(enum logk_event_type log_type, + struct msm_rtb_layout *start) +{ + start->log_type = (char)log_type; +} + +static void msm_rtb_write_caller(void *caller, struct msm_rtb_layout *start) +{ + start->caller = caller; +} + +static void msm_rtb_write_idx(unsigned long idx, + struct msm_rtb_layout *start) +{ + start->idx = idx; +} + +static void msm_rtb_write_data(void *data, struct msm_rtb_layout *start) +{ + start->data = data; +} + +static void uncached_logk_pc_idx(enum logk_event_type log_type, void *caller, + void *data, int idx) +{ + struct msm_rtb_layout *start; + + start = &msm_rtb.rtb[idx & (msm_rtb.nentries - 1)]; + + msm_rtb_emit_sentinel(start); + msm_rtb_write_type(log_type, start); + msm_rtb_write_caller(caller, start); + msm_rtb_write_idx(idx, start); + msm_rtb_write_data(data, start); + mb(); + + return; +} + +static void uncached_logk_timestamp(int idx) +{ + unsigned long long timestamp; + void *timestamp_upper, *timestamp_lower; + timestamp = sched_clock(); + timestamp_lower = (void *)lower_32_bits(timestamp); + timestamp_upper = (void *)upper_32_bits(timestamp); + + uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC, timestamp_lower, + timestamp_upper, idx); +} + +#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS) +static int msm_rtb_get_idx(void) +{ + int cpu, i, offset; + atomic_t *index; + + /* + * ideally we would use get_cpu but this is a close enough + * approximation for our purposes. + */ + cpu = raw_smp_processor_id(); + + index = &per_cpu(msm_rtb_idx_cpu, cpu); + + i = atomic_add_return(msm_rtb.step_size, index); + i -= msm_rtb.step_size; + + /* Check if index has wrapped around */ + offset = (i & (msm_rtb.nentries - 1)) - + ((i - msm_rtb.step_size) & (msm_rtb.nentries - 1)); + if (offset < 0) { + uncached_logk_timestamp(i); + i = atomic_add_return(msm_rtb.step_size, index); + i -= msm_rtb.step_size; + } + + return i; +} +#else +static int msm_rtb_get_idx(void) +{ + int i, offset; + + i = atomic_inc_return(&msm_rtb_idx); + i--; + + /* Check if index has wrapped around */ + offset = (i & (msm_rtb.nentries - 1)) - + ((i - 1) & (msm_rtb.nentries - 1)); + if (offset < 0) { + uncached_logk_timestamp(i); + i = atomic_inc_return(&msm_rtb_idx); + i--; + } + + return i; +} +#endif + +int uncached_logk_pc(enum logk_event_type log_type, void *caller, + void *data) +{ + int i; + + if (!msm_rtb_event_should_log(log_type)) + return 0; + + i = msm_rtb_get_idx(); + + uncached_logk_pc_idx(log_type, caller, data, i); + + return 1; +} +EXPORT_SYMBOL(uncached_logk_pc); + +noinline int uncached_logk(enum logk_event_type log_type, void *data) +{ + return uncached_logk_pc(log_type, __builtin_return_address(0), data); +} +EXPORT_SYMBOL(uncached_logk); + +int msm_rtb_probe(struct platform_device *pdev) +{ + struct msm_rtb_platform_data *d = pdev->dev.platform_data; +#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS) + unsigned int cpu; +#endif + + msm_rtb.size = d->size; + + if (msm_rtb.size <= 0 || msm_rtb.size > SZ_1M) + return -EINVAL; + + /* + * The ioremap call is made separately to store the physical + * address of the buffer. This is necessary for cases where + * the only way to access the buffer is a physical address. + */ + msm_rtb.phys = allocate_contiguous_ebi_nomap(msm_rtb.size, SZ_4K); + + if (!msm_rtb.phys) + return -ENOMEM; + + msm_rtb.rtb = ioremap(msm_rtb.phys, msm_rtb.size); + + if (!msm_rtb.rtb) { + free_contiguous_memory_by_paddr(msm_rtb.phys); + return -ENOMEM; + } + + msm_rtb.nentries = msm_rtb.size / sizeof(struct msm_rtb_layout); + + /* Round this down to a power of 2 */ + msm_rtb.nentries = __rounddown_pow_of_two(msm_rtb.nentries); + + memset(msm_rtb.rtb, 0, msm_rtb.size); + + +#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS) + for_each_possible_cpu(cpu) { + atomic_t *a = &per_cpu(msm_rtb_idx_cpu, cpu); + atomic_set(a, cpu); + } + msm_rtb.step_size = num_possible_cpus(); +#else + atomic_set(&msm_rtb_idx, 0); + msm_rtb.step_size = 1; +#endif + + atomic_notifier_chain_register(&panic_notifier_list, + &msm_rtb_panic_blk); + msm_rtb.initialized = 1; + return 0; +} + +static struct platform_driver msm_rtb_driver = { + .driver = { + .name = "msm_rtb", + .owner = THIS_MODULE + }, +}; + +static int __init msm_rtb_init(void) +{ + return platform_driver_probe(&msm_rtb_driver, msm_rtb_probe); +} + +static void __exit msm_rtb_exit(void) +{ + platform_driver_unregister(&msm_rtb_driver); +} +module_init(msm_rtb_init) +module_exit(msm_rtb_exit) diff --git a/arch/arm/mach-msm/msm_show_resume_irq.c b/arch/arm/mach-msm/msm_show_resume_irq.c new file mode 100644 index 00000000000..82093673c49 --- /dev/null +++ b/arch/arm/mach-msm/msm_show_resume_irq.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +int msm_show_resume_irq_mask; + +module_param_named( + debug_mask, msm_show_resume_irq_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c new file mode 100644 index 00000000000..5595ba07e34 --- /dev/null +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -0,0 +1,162 @@ +/* include/asm/mach-msm/htc_pwrsink.h + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2011 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include "pmic.h" +#include "timed_output.h" + +#include + +#define PM_LIBPROG 0x30000061 +#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define PM_LIBVERS 0xfb837d0b +#else +#define PM_LIBVERS 0x10001 +#endif + +#define HTC_PROCEDURE_SET_VIB_ON_OFF 21 +#define PMIC_VIBRATOR_LEVEL (3000) + +static struct work_struct work_vibrator_on; +static struct work_struct work_vibrator_off; +static struct hrtimer vibe_timer; + +#ifdef CONFIG_PM8XXX_RPC_VIBRATOR +static void set_pmic_vibrator(int on) +{ + int rc; + + rc = pmic_vib_mot_set_mode(PM_VIB_MOT_MODE__MANUAL); + if (rc) { + pr_err("%s: Vibrator set mode failed", __func__); + return; + } + + if (on) + rc = pmic_vib_mot_set_volt(PMIC_VIBRATOR_LEVEL); + else + rc = pmic_vib_mot_set_volt(0); + + if (rc) + pr_err("%s: Vibrator set voltage level failed", __func__); +} +#else +static void set_pmic_vibrator(int on) +{ + static struct msm_rpc_endpoint *vib_endpoint; + struct set_vib_on_off_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + if (!vib_endpoint) { + vib_endpoint = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0); + if (IS_ERR(vib_endpoint)) { + printk(KERN_ERR "init vib rpc failed!\n"); + vib_endpoint = 0; + return; + } + } + + + if (on) + req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL); + else + req.data = cpu_to_be32(0); + + msm_rpc_call(vib_endpoint, HTC_PROCEDURE_SET_VIB_ON_OFF, &req, + sizeof(req), 5 * HZ); +} +#endif + +static void pmic_vibrator_on(struct work_struct *work) +{ + set_pmic_vibrator(1); +} + +static void pmic_vibrator_off(struct work_struct *work) +{ + set_pmic_vibrator(0); +} + +static void timed_vibrator_on(struct timed_output_dev *sdev) +{ + schedule_work(&work_vibrator_on); +} + +static void timed_vibrator_off(struct timed_output_dev *sdev) +{ + schedule_work(&work_vibrator_off); +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + hrtimer_cancel(&vibe_timer); + + if (value == 0) + timed_vibrator_off(dev); + else { + value = (value > 15000 ? 15000 : value); + + timed_vibrator_on(dev); + + hrtimer_start(&vibe_timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } +} + +static int vibrator_get_time(struct timed_output_dev *dev) +{ + if (hrtimer_active(&vibe_timer)) { + ktime_t r = hrtimer_get_remaining(&vibe_timer); + struct timeval t = ktime_to_timeval(r); + return t.tv_sec * 1000 + t.tv_usec / 1000; + } + return 0; +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + timed_vibrator_off(NULL); + return HRTIMER_NORESTART; +} + +static struct timed_output_dev pmic_vibrator = { + .name = "vibrator", + .get_time = vibrator_get_time, + .enable = vibrator_enable, +}; + +void __init msm_init_pmic_vibrator(void) +{ + INIT_WORK(&work_vibrator_on, pmic_vibrator_on); + INIT_WORK(&work_vibrator_off, pmic_vibrator_off); + + hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vibe_timer.function = vibrator_timer_func; + + timed_output_dev_register(&pmic_vibrator); +} + +MODULE_DESCRIPTION("timed output pmic vibrator device"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/msm_watchdog.c b/arch/arm/mach-msm/msm_watchdog.c new file mode 100644 index 00000000000..7ac3f743e13 --- /dev/null +++ b/arch/arm/mach-msm/msm_watchdog.c @@ -0,0 +1,440 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_watchdog.h" +#include "timer.h" + +#define MODULE_NAME "msm_watchdog" + +#define TCSR_WDT_CFG 0x30 + +#define WDT0_RST 0x38 +#define WDT0_EN 0x40 +#define WDT0_STS 0x44 +#define WDT0_BARK_TIME 0x4C +#define WDT0_BITE_TIME 0x5C + +#define WDT_HZ 32768 + +struct msm_watchdog_dump msm_dump_cpu_ctx; + +static void __iomem *msm_tmr0_base; + +static unsigned long delay_time; +static unsigned long bark_time; +static unsigned long long last_pet; +static bool has_vic; + +/* + * On the kernel command line specify + * msm_watchdog.enable=1 to enable the watchdog + * By default watchdog is turned on + */ +static int enable = 1; +module_param(enable, int, 0); + +/* + * If the watchdog is enabled at bootup (enable=1), + * the runtime_disable sysfs node at + * /sys/module/msm_watchdog/runtime_disable + * can be used to deactivate the watchdog. + * This is a one-time setting. The watchdog + * cannot be re-enabled once it is disabled. + */ +static int runtime_disable; +static DEFINE_MUTEX(disable_lock); +static int wdog_enable_set(const char *val, struct kernel_param *kp); +module_param_call(runtime_disable, wdog_enable_set, param_get_int, + &runtime_disable, 0644); + +/* + * On the kernel command line specify msm_watchdog.appsbark=1 to handle + * watchdog barks in Linux. By default barks are processed by the secure side. + */ +static int appsbark; +module_param(appsbark, int, 0); + +static int appsbark_fiq; + +/* + * Use /sys/module/msm_watchdog/parameters/print_all_stacks + * to control whether stacks of all running + * processes are printed when a wdog bark is received. + */ +static int print_all_stacks = 1; +module_param(print_all_stacks, int, S_IRUGO | S_IWUSR); + +/* Area for context dump in secure mode */ +static void *scm_regsave; + +static struct msm_watchdog_pdata __percpu **percpu_pdata; + +static void pet_watchdog_work(struct work_struct *work); +static void init_watchdog_work(struct work_struct *work); +static DECLARE_DELAYED_WORK(dogwork_struct, pet_watchdog_work); +static DECLARE_WORK(init_dogwork_struct, init_watchdog_work); + +/* Called from the FIQ bark handler */ +void msm_wdog_bark_fin(void) +{ + pr_crit("\nApps Watchdog bark received - Calling Panic\n"); + panic("Apps Watchdog Bark received\n"); +} + +static int msm_watchdog_suspend(struct device *dev) +{ + if (!enable) + return 0; + + __raw_writel(1, msm_tmr0_base + WDT0_RST); + __raw_writel(0, msm_tmr0_base + WDT0_EN); + mb(); + return 0; +} + +static int msm_watchdog_resume(struct device *dev) +{ + if (!enable) + return 0; + + __raw_writel(1, msm_tmr0_base + WDT0_EN); + __raw_writel(1, msm_tmr0_base + WDT0_RST); + mb(); + return 0; +} + +static int panic_wdog_handler(struct notifier_block *this, + unsigned long event, void *ptr) +{ + if (panic_timeout == 0) { + __raw_writel(0, msm_tmr0_base + WDT0_EN); + mb(); + } else { + __raw_writel(WDT_HZ * (panic_timeout + 4), + msm_tmr0_base + WDT0_BARK_TIME); + __raw_writel(WDT_HZ * (panic_timeout + 4), + msm_tmr0_base + WDT0_BITE_TIME); + __raw_writel(1, msm_tmr0_base + WDT0_RST); + } + return NOTIFY_DONE; +} + +static struct notifier_block panic_blk = { + .notifier_call = panic_wdog_handler, +}; + +struct wdog_disable_work_data { + struct work_struct work; + struct completion complete; +}; + +static void wdog_disable_work(struct work_struct *work) +{ + struct wdog_disable_work_data *work_data = + container_of(work, struct wdog_disable_work_data, work); + __raw_writel(0, msm_tmr0_base + WDT0_EN); + mb(); + if (has_vic) { + free_irq(WDT0_ACCSCSSNBARK_INT, 0); + } else { + disable_percpu_irq(WDT0_ACCSCSSNBARK_INT); + if (!appsbark_fiq) { + free_percpu_irq(WDT0_ACCSCSSNBARK_INT, + percpu_pdata); + free_percpu(percpu_pdata); + } + } + enable = 0; + atomic_notifier_chain_unregister(&panic_notifier_list, &panic_blk); + cancel_delayed_work(&dogwork_struct); + /* may be suspended after the first write above */ + __raw_writel(0, msm_tmr0_base + WDT0_EN); + complete(&work_data->complete); + pr_info("MSM Watchdog deactivated.\n"); +} + +static int wdog_enable_set(const char *val, struct kernel_param *kp) +{ + int ret = 0; + int old_val = runtime_disable; + struct wdog_disable_work_data work_data; + + mutex_lock(&disable_lock); + if (!enable) { + printk(KERN_INFO "MSM Watchdog is not active.\n"); + ret = -EINVAL; + goto done; + } + + ret = param_set_int(val, kp); + if (ret) + goto done; + + if (runtime_disable == 1) { + if (old_val) + goto done; + init_completion(&work_data.complete); + INIT_WORK_ONSTACK(&work_data.work, wdog_disable_work); + schedule_work_on(0, &work_data.work); + wait_for_completion(&work_data.complete); + } else { + runtime_disable = old_val; + ret = -EINVAL; + } +done: + mutex_unlock(&disable_lock); + return ret; +} + +unsigned min_slack_ticks = UINT_MAX; +unsigned long long min_slack_ns = ULLONG_MAX; + +void pet_watchdog(void) +{ + int slack; + unsigned long long time_ns; + unsigned long long slack_ns; + unsigned long long bark_time_ns = bark_time * 1000000ULL; + + if (!enable) + return; + + slack = __raw_readl(msm_tmr0_base + WDT0_STS) >> 3; + slack = ((bark_time*WDT_HZ)/1000) - slack; + if (slack < min_slack_ticks) + min_slack_ticks = slack; + __raw_writel(1, msm_tmr0_base + WDT0_RST); + time_ns = sched_clock(); + slack_ns = (last_pet + bark_time_ns) - time_ns; + if (slack_ns < min_slack_ns) + min_slack_ns = slack_ns; + last_pet = time_ns; +} + +static void pet_watchdog_work(struct work_struct *work) +{ + pet_watchdog(); + + if (enable) + schedule_delayed_work_on(0, &dogwork_struct, delay_time); +} + +static irqreturn_t wdog_bark_handler(int irq, void *dev_id) +{ + unsigned long nanosec_rem; + unsigned long long t = sched_clock(); + struct task_struct *tsk; + + nanosec_rem = do_div(t, 1000000000); + printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t, + nanosec_rem / 1000); + + nanosec_rem = do_div(last_pet, 1000000000); + printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long) + last_pet, nanosec_rem / 1000); + + if (print_all_stacks) { + + /* Suspend wdog until all stacks are printed */ + msm_watchdog_suspend(NULL); + + printk(KERN_INFO "Stack trace dump:\n"); + + for_each_process(tsk) { + printk(KERN_INFO "\nPID: %d, Name: %s\n", + tsk->pid, tsk->comm); + show_stack(tsk, NULL); + } + + msm_watchdog_resume(NULL); + } + + panic("Apps watchdog bark received!"); + return IRQ_HANDLED; +} + +#define SCM_SET_REGSAVE_CMD 0x2 + +static void configure_bark_dump(void) +{ + int ret; + struct { + unsigned addr; + int len; + } cmd_buf; + + if (!appsbark) { + scm_regsave = (void *)__get_free_page(GFP_KERNEL); + + if (scm_regsave) { + cmd_buf.addr = __pa(scm_regsave); + cmd_buf.len = PAGE_SIZE; + + ret = scm_call(SCM_SVC_UTIL, SCM_SET_REGSAVE_CMD, + &cmd_buf, sizeof(cmd_buf), NULL, 0); + if (ret) + pr_err("Setting register save address failed.\n" + "Registers won't be dumped on a dog " + "bite\n"); + } else { + pr_err("Allocating register save space failed\n" + "Registers won't be dumped on a dog bite\n"); + /* + * No need to bail if allocation fails. Simply don't + * send the command, and the secure side will reset + * without saving registers. + */ + } + } +} + +struct fiq_handler wdog_fh = { + .name = MODULE_NAME, +}; + +static void init_watchdog_work(struct work_struct *work) +{ + u64 timeout = (bark_time * WDT_HZ)/1000; + void *stack; + int ret; + + if (has_vic) { + ret = request_irq(WDT0_ACCSCSSNBARK_INT, wdog_bark_handler, 0, + "apps_wdog_bark", NULL); + if (ret) + return; + } else if (appsbark_fiq) { + claim_fiq(&wdog_fh); + set_fiq_handler(&msm_wdog_fiq_start, msm_wdog_fiq_length); + stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); + if (!stack) { + pr_info("No free pages available - %s fails\n", + __func__); + return; + } + + msm_wdog_fiq_setup(stack); + gic_set_irq_secure(WDT0_ACCSCSSNBARK_INT); + } else { + percpu_pdata = alloc_percpu(struct msm_watchdog_pdata *); + if (!percpu_pdata) { + pr_err("%s: memory allocation failed for percpu data\n", + __func__); + return; + } + + /* Must request irq before sending scm command */ + ret = request_percpu_irq(WDT0_ACCSCSSNBARK_INT, + wdog_bark_handler, "apps_wdog_bark", percpu_pdata); + if (ret) { + free_percpu(percpu_pdata); + return; + } + } + + configure_bark_dump(); + + __raw_writel(timeout, msm_tmr0_base + WDT0_BARK_TIME); + __raw_writel(timeout + 3*WDT_HZ, msm_tmr0_base + WDT0_BITE_TIME); + + schedule_delayed_work_on(0, &dogwork_struct, delay_time); + + atomic_notifier_chain_register(&panic_notifier_list, + &panic_blk); + + __raw_writel(1, msm_tmr0_base + WDT0_EN); + __raw_writel(1, msm_tmr0_base + WDT0_RST); + last_pet = sched_clock(); + + if (!has_vic) + enable_percpu_irq(WDT0_ACCSCSSNBARK_INT, IRQ_TYPE_EDGE_RISING); + + printk(KERN_INFO "MSM Watchdog Initialized\n"); + + return; +} + +static int msm_watchdog_probe(struct platform_device *pdev) +{ + struct msm_watchdog_pdata *pdata = pdev->dev.platform_data; + + if (!enable || !pdata || !pdata->pet_time || !pdata->bark_time) { + printk(KERN_INFO "MSM Watchdog Not Initialized\n"); + return -ENODEV; + } + + bark_time = pdata->bark_time; + has_vic = pdata->has_vic; + if (!pdata->has_secure) { + appsbark = 1; + appsbark_fiq = pdata->use_kernel_fiq; + } + + msm_tmr0_base = msm_timer_get_timer0_base(); + + /* + * This is only temporary till SBLs turn on the XPUs + * This initialization will be done in SBLs on a later releases + */ + if (cpu_is_msm9615()) + __raw_writel(0xF, MSM_TCSR_BASE + TCSR_WDT_CFG); + + if (pdata->needs_expired_enable) + __raw_writel(0x1, MSM_CLK_CTL_BASE + 0x3820); + + delay_time = msecs_to_jiffies(pdata->pet_time); + schedule_work_on(0, &init_dogwork_struct); + return 0; +} + +static const struct dev_pm_ops msm_watchdog_dev_pm_ops = { + .suspend_noirq = msm_watchdog_suspend, + .resume_noirq = msm_watchdog_resume, +}; + +static struct platform_driver msm_watchdog_driver = { + .probe = msm_watchdog_probe, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .pm = &msm_watchdog_dev_pm_ops, + }, +}; + +static int init_watchdog(void) +{ + return platform_driver_register(&msm_watchdog_driver); +} + +late_initcall(init_watchdog); +MODULE_DESCRIPTION("MSM Watchdog Driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/msm_watchdog.h b/arch/arm/mach-msm/msm_watchdog.h new file mode 100644 index 00000000000..00ff0b61d0b --- /dev/null +++ b/arch/arm/mach-msm/msm_watchdog.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_MSM_WATCHDOG_H +#define __ARCH_ARM_MACH_MSM_MSM_WATCHDOG_H + +struct msm_watchdog_pdata { + /* pet interval period in ms */ + unsigned int pet_time; + /* bark timeout in ms */ + unsigned int bark_time; + bool has_secure; + bool needs_expired_enable; + bool has_vic; + /* You have to be running in secure mode to use FIQ */ + bool use_kernel_fiq; +}; + +struct msm_watchdog_dump { + uint32_t magic; + uint32_t curr_cpsr; + uint32_t usr_r0; + uint32_t usr_r1; + uint32_t usr_r2; + uint32_t usr_r3; + uint32_t usr_r4; + uint32_t usr_r5; + uint32_t usr_r6; + uint32_t usr_r7; + uint32_t usr_r8; + uint32_t usr_r9; + uint32_t usr_r10; + uint32_t usr_r11; + uint32_t usr_r12; + uint32_t usr_r13; + uint32_t usr_r14; + uint32_t irq_spsr; + uint32_t irq_r13; + uint32_t irq_r14; + uint32_t svc_spsr; + uint32_t svc_r13; + uint32_t svc_r14; + uint32_t abt_spsr; + uint32_t abt_r13; + uint32_t abt_r14; + uint32_t und_spsr; + uint32_t und_r13; + uint32_t und_r14; + uint32_t fiq_spsr; + uint32_t fiq_r8; + uint32_t fiq_r9; + uint32_t fiq_r10; + uint32_t fiq_r11; + uint32_t fiq_r12; + uint32_t fiq_r13; + uint32_t fiq_r14; +}; + +void msm_wdog_fiq_setup(void *stack); +extern unsigned int msm_wdog_fiq_length, msm_wdog_fiq_start; + +#ifdef CONFIG_MSM_WATCHDOG +void pet_watchdog(void); +#else +static inline void pet_watchdog(void) { } +#endif + +#endif diff --git a/arch/arm/mach-msm/msm_watchdog_asm.S b/arch/arm/mach-msm/msm_watchdog_asm.S new file mode 100644 index 00000000000..c0377d61cdb --- /dev/null +++ b/arch/arm/mach-msm/msm_watchdog_asm.S @@ -0,0 +1,84 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#define VERSION_ID 0x1 +#define MAGIC 0xDEAD0000 | VERSION_ID + + .text + + .align 3 + +ENTRY(msm_wdog_fiq_start) + mov sp, r8 @get stack + ldr r8, Ldump_cpu_ctx + @ store magic to indicate a valid dump + ldr r9, Lmagic + str r9, [r8], #4 + @ get the current cpsr + mrs r9, cpsr + str r9, [r8],#4 + @ get the USR r0-r7 + stmia r8!, {r0-r7} + mov r4, r8 + mov r5, #PSR_I_BIT | PSR_F_BIT | SYSTEM_MODE + msr cpsr_c, r5 @ select SYSTEM mode + stmia r4!, {r8-r14} + mov r5, #PSR_I_BIT | PSR_F_BIT | IRQ_MODE + msr cpsr_c, r5 @ select IRQ mode + mrs r5, spsr + str r5, [r4], #4 + stmia r4!, {r13-r14} + mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE + msr cpsr_c, r5 @ select SVC mode + mrs r5, spsr + str r5, [r4], #4 + stmia r4!, {r13-r14} + mov r5, #PSR_I_BIT | PSR_F_BIT | ABT_MODE + msr cpsr_c, r5 @ select ABT mode + mrs r5, spsr + str r5, [r4], #4 + stmia r4!, {r13-r14} + mov r5, #PSR_I_BIT | PSR_F_BIT | UND_MODE + msr cpsr_c, r5 @ select UND mode + mrs r5, spsr + str r5, [r4], #4 + stmia r4!, {r13-r14} + mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE + msr cpsr_c, r5 @ select FIQ mode + mrs r5, spsr + str r5, [r4], #4 + stmia r4!, {r8-r14} + dsb + mov r5, #PSR_F_BIT | SVC_MODE + msr cpsr_c, r5 @ select SVC mode + ldr r2, Lwatchdog_bark_fin + blx r2 +Ldump_cpu_ctx: + .word msm_dump_cpu_ctx +Lmagic: + .word MAGIC +Lwatchdog_bark_fin: + .word msm_wdog_bark_fin +ENTRY(msm_wdog_fiq_length) + .word . - msm_wdog_fiq_start + +/* setup the stack */ +ENTRY(msm_wdog_fiq_setup) + mrs r3, cpsr + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + mov r8, r0 + msr cpsr_c, r3 + bx lr diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c new file mode 100644 index 00000000000..407231ae0cc --- /dev/null +++ b/arch/arm/mach-msm/msm_xo.c @@ -0,0 +1,386 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rpm_resources.h" + +static DEFINE_MUTEX(msm_xo_lock); + +struct msm_xo { + unsigned votes[NUM_MSM_XO_MODES]; + unsigned mode; + struct list_head voters; +}; + +struct msm_xo_voter { + const char *name; + unsigned mode; + struct msm_xo *xo; + struct list_head list; +}; + +static struct msm_xo msm_xo_sources[NUM_MSM_XO_IDS]; + +#ifdef CONFIG_DEBUG_FS +static const char *msm_xo_to_str[NUM_MSM_XO_IDS] = { + [MSM_XO_TCXO_D0] = "D0", + [MSM_XO_TCXO_D1] = "D1", + [MSM_XO_TCXO_A0] = "A0", + [MSM_XO_TCXO_A1] = "A1", + [MSM_XO_TCXO_A2] = "A2", + [MSM_XO_CORE] = "CORE", +}; + +static const char *msm_xo_mode_to_str[NUM_MSM_XO_MODES] = { + [MSM_XO_MODE_ON] = "ON", + [MSM_XO_MODE_PIN_CTRL] = "PIN", + [MSM_XO_MODE_OFF] = "OFF", +}; + +static int msm_xo_debugfs_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t msm_xo_debugfs_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + int r; + char buf[10]; + struct msm_xo_voter *xo = filp->private_data; + + r = snprintf(buf, sizeof(buf), "%s\n", msm_xo_mode_to_str[xo->mode]); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t msm_xo_debugfs_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct msm_xo_voter *xo = filp->private_data; + char buf[10], *b; + int i, ret; + + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = '\0'; + b = strstrip(buf); + + for (i = 0; i < ARRAY_SIZE(msm_xo_mode_to_str); i++) + if (!strncasecmp(b, msm_xo_mode_to_str[i], sizeof(buf))) { + ret = msm_xo_mode_vote(xo, i); + return ret ? : cnt; + } + + return -EINVAL; +} + +static const struct file_operations msm_xo_debugfs_fops = { + .open = msm_xo_debugfs_open, + .read = msm_xo_debugfs_read, + .write = msm_xo_debugfs_write, +}; + +static struct dentry *xo_debugfs_root; +static struct msm_xo_voter *xo_debugfs_voters[NUM_MSM_XO_IDS]; + +static int __init msm_xo_init_debugfs_voters(void) +{ + int i; + + xo_debugfs_root = debugfs_create_dir("msm_xo", NULL); + if (!xo_debugfs_root) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++) { + xo_debugfs_voters[i] = msm_xo_get(i, "debugfs"); + if (IS_ERR(xo_debugfs_voters[i])) + goto err; + debugfs_create_file(msm_xo_to_str[i], S_IRUGO | S_IWUSR, + xo_debugfs_root, xo_debugfs_voters[i], + &msm_xo_debugfs_fops); + } + return 0; +err: + while (--i >= 0) + msm_xo_put(xo_debugfs_voters[i]); + debugfs_remove_recursive(xo_debugfs_root); + return -ENOMEM; +} + +static void msm_xo_dump_xo(struct seq_file *m, struct msm_xo *xo, + const char *name) +{ + struct msm_xo_voter *voter; + + seq_printf(m, "CXO %-16s%s\n", name, msm_xo_mode_to_str[xo->mode]); + list_for_each_entry(voter, &xo->voters, list) + seq_printf(m, " %s %-16s %s\n", + xo->mode == voter->mode ? "*" : " ", + voter->name, + msm_xo_mode_to_str[voter->mode]); +} + +static int msm_xo_show_voters(struct seq_file *m, void *v) +{ + int i; + + mutex_lock(&msm_xo_lock); + for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++) + msm_xo_dump_xo(m, &msm_xo_sources[i], msm_xo_to_str[i]); + mutex_unlock(&msm_xo_lock); + + return 0; +} + +static int msm_xo_voters_open(struct inode *inode, struct file *file) +{ + return single_open(file, msm_xo_show_voters, inode->i_private); +} + +static const struct file_operations msm_xo_voters_ops = { + .open = msm_xo_voters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init msm_xo_debugfs_init(void) +{ + msm_xo_init_debugfs_voters(); + if (!debugfs_create_file("xo_voters", S_IRUGO, NULL, NULL, + &msm_xo_voters_ops)) + return -ENOMEM; + return 0; +} +#else +static int __init msm_xo_debugfs_init(void) { return 0; } +#endif + +static int msm_xo_update_vote(struct msm_xo *xo) +{ + int ret; + unsigned vote, prev_vote = xo->mode; + struct msm_rpm_iv_pair cmd; + + if (xo->votes[MSM_XO_MODE_ON]) + vote = MSM_XO_MODE_ON; + else if (xo->votes[MSM_XO_MODE_PIN_CTRL]) + vote = MSM_XO_MODE_PIN_CTRL; + else + vote = MSM_XO_MODE_OFF; + + if (vote == prev_vote) + return 0; + + /* + * Change the vote here to simplify the TCXO logic. If the RPM + * command fails we'll rollback. + */ + xo->mode = vote; + cmd.id = MSM_RPM_ID_CXO_BUFFERS; + cmd.value = (msm_xo_sources[MSM_XO_TCXO_D0].mode << 0) | + (msm_xo_sources[MSM_XO_TCXO_D1].mode << 8) | + (msm_xo_sources[MSM_XO_TCXO_A0].mode << 16) | + (msm_xo_sources[MSM_XO_TCXO_A1].mode << 24) | + (msm_xo_sources[MSM_XO_TCXO_A2].mode << 28) | + /* + * 8660 RPM has XO_CORE at bit 18 and 8960 RPM has + * XO_CORE at bit 20. Since the opposite bit is + * reserved in both cases, just set both and be + * done with it. + */ + ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20) | + ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 18); + ret = msm_rpm_set(MSM_RPM_CTX_SET_0, &cmd, 1); + + if (ret) + xo->mode = prev_vote; + + return ret; +} + +static int __msm_xo_mode_vote(struct msm_xo_voter *xo_voter, unsigned mode) +{ + int ret; + struct msm_xo *xo = xo_voter->xo; + int is_d0 = xo == &msm_xo_sources[MSM_XO_TCXO_D0]; + int needs_workaround = cpu_is_msm8960() || cpu_is_apq8064() || + cpu_is_msm8930() || cpu_is_msm9615(); + + if (xo_voter->mode == mode) + return 0; + + xo->votes[mode]++; + xo->votes[xo_voter->mode]--; + ret = msm_xo_update_vote(xo); + if (ret) { + xo->votes[xo_voter->mode]++; + xo->votes[mode]--; + goto out; + } + /* TODO: Remove once RPM separates the concept of D0 and CXO */ + if (is_d0 && needs_workaround) { + static struct clk *xo_clk; + + if (!xo_clk) { + xo_clk = clk_get_sys("msm_xo", "xo"); + BUG_ON(IS_ERR(xo_clk)); + } + /* Ignore transitions from pin to on or vice versa */ + if (mode && xo_voter->mode == MSM_XO_MODE_OFF) + clk_enable(xo_clk); + else if (!mode) + clk_disable(xo_clk); + } + xo_voter->mode = mode; +out: + return ret; +} + +/** + * msm_xo_mode_vote() - Vote for an XO to be ON, OFF, or under PIN_CTRL + * @xo_voter - Valid handle returned from msm_xo_get() + * @mode - Mode to vote for (ON, OFF, PIN_CTRL) + * + * Vote for an XO to be either ON, OFF, or under PIN_CTRL. Votes are + * aggregated with ON taking precedence over PIN_CTRL taking precedence + * over OFF. + * + * This function returns 0 on success or a negative error code on failure. + */ +int msm_xo_mode_vote(struct msm_xo_voter *xo_voter, enum msm_xo_modes mode) +{ + int ret; + + if (!xo_voter) + return 0; + + if (mode >= NUM_MSM_XO_MODES || IS_ERR(xo_voter)) + return -EINVAL; + + mutex_lock(&msm_xo_lock); + ret = __msm_xo_mode_vote(xo_voter, mode); + mutex_unlock(&msm_xo_lock); + + return ret; +} +EXPORT_SYMBOL(msm_xo_mode_vote); + +/** + * msm_xo_get() - Get a voting handle for an XO + * @xo_id - XO identifier + * @voter - Debug string to identify users + * + * XO voters vote for OFF by default. This function returns a pointer + * indicating success. An ERR_PTR is returned on failure. + * + * If XO voting is disabled, %NULL is returned. + */ +struct msm_xo_voter *msm_xo_get(enum msm_xo_ids xo_id, const char *voter) +{ + int ret; + struct msm_xo_voter *xo_voter; + + if (xo_id >= NUM_MSM_XO_IDS) { + ret = -EINVAL; + goto err; + } + + xo_voter = kzalloc(sizeof(*xo_voter), GFP_KERNEL); + if (!xo_voter) { + ret = -ENOMEM; + goto err; + } + + xo_voter->name = kstrdup(voter, GFP_KERNEL); + if (!xo_voter->name) { + ret = -ENOMEM; + goto err_name; + } + + xo_voter->xo = &msm_xo_sources[xo_id]; + + /* Voters vote for OFF by default */ + mutex_lock(&msm_xo_lock); + xo_voter->xo->votes[MSM_XO_MODE_OFF]++; + list_add(&xo_voter->list, &xo_voter->xo->voters); + mutex_unlock(&msm_xo_lock); + + return xo_voter; + +err_name: + kfree(xo_voter); +err: + return ERR_PTR(ret); +} +EXPORT_SYMBOL(msm_xo_get); + +/** + * msm_xo_put() - Release a voting handle + * @xo_voter - Valid handle returned from msm_xo_get() + * + * Release a reference to an XO voting handle. This also removes the voter's + * vote, therefore calling msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF) + * beforehand is unnecessary. + */ +void msm_xo_put(struct msm_xo_voter *xo_voter) +{ + if (!xo_voter || IS_ERR(xo_voter)) + return; + + mutex_lock(&msm_xo_lock); + __msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF); + xo_voter->xo->votes[MSM_XO_MODE_OFF]--; + list_del(&xo_voter->list); + mutex_unlock(&msm_xo_lock); + + kfree(xo_voter->name); + kfree(xo_voter); +} +EXPORT_SYMBOL(msm_xo_put); + +int __init msm_xo_init(void) +{ + int i, ret; + struct msm_rpm_iv_pair cmd[1]; + + for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++) + INIT_LIST_HEAD(&msm_xo_sources[i].voters); + + cmd[0].id = MSM_RPM_ID_CXO_BUFFERS; + cmd[0].value = 0; + ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, cmd, ARRAY_SIZE(cmd)); + if (ret) + return ret; + msm_xo_debugfs_init(); + return 0; +} diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c new file mode 100644 index 00000000000..499ad992ba1 --- /dev/null +++ b/arch/arm/mach-msm/nand_partitions.c @@ -0,0 +1,202 @@ +/* arch/arm/mach-msm/nand_partitions.c + * + * Code to extract partition information from ATAG set up by the + * bootloader. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009,2011 Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#ifdef CONFIG_MSM_SMD +#include "smd_private.h" +#endif + +/* configuration tags specific to msm */ + +#define ATAG_MSM_PARTITION 0x4d534D70 /* MSMp */ + +struct msm_ptbl_entry { + char name[16]; + __u32 offset; + __u32 size; + __u32 flags; +}; + +#define MSM_MAX_PARTITIONS 18 + +static struct mtd_partition msm_nand_partitions[MSM_MAX_PARTITIONS]; +static char msm_nand_names[MSM_MAX_PARTITIONS * 16]; + +extern struct flash_platform_data msm_nand_data; + +static int __init parse_tag_msm_partition(const struct tag *tag) +{ + struct mtd_partition *ptn = msm_nand_partitions; + char *name = msm_nand_names; + struct msm_ptbl_entry *entry = (void *) &tag->u; + unsigned count, n; + + count = (tag->hdr.size - 2) / + (sizeof(struct msm_ptbl_entry) / sizeof(__u32)); + + if (count > MSM_MAX_PARTITIONS) + count = MSM_MAX_PARTITIONS; + + for (n = 0; n < count; n++) { + memcpy(name, entry->name, 15); + name[15] = 0; + + ptn->name = name; + ptn->offset = entry->offset; + ptn->size = entry->size; + + printk(KERN_INFO "Partition (from atag) %s " + "-- Offset:%llx Size:%llx\n", + ptn->name, ptn->offset, ptn->size); + + name += 16; + entry++; + ptn++; + } + + msm_nand_data.nr_parts = count; + msm_nand_data.parts = msm_nand_partitions; + + return 0; +} + +__tagtable(ATAG_MSM_PARTITION, parse_tag_msm_partition); + +#define FLASH_PART_MAGIC1 0x55EE73AA +#define FLASH_PART_MAGIC2 0xE35EBDDB +#define FLASH_PARTITION_VERSION 0x3 + +#define LINUX_FS_PARTITION_NAME "0:EFS2APPS" + +struct flash_partition_entry { + char name[16]; + u32 offset; /* Offset in blocks from beginning of device */ + u32 length; /* Length of the partition in blocks */ + u8 attrib1; + u8 attrib2; + u8 attrib3; + u8 which_flash; /* Numeric ID (first = 0, second = 1) */ +}; +struct flash_partition_table { + u32 magic1; + u32 magic2; + u32 version; + u32 numparts; + struct flash_partition_entry part_entry[16]; +}; + +#ifdef CONFIG_MSM_SMD +static int get_nand_partitions(void) +{ + struct flash_partition_table *partition_table; + struct flash_partition_entry *part_entry; + struct mtd_partition *ptn = msm_nand_partitions; + char *name = msm_nand_names; + int part; + + if (msm_nand_data.nr_parts) + return 0; + + partition_table = (struct flash_partition_table *) + smem_alloc(SMEM_AARM_PARTITION_TABLE, + sizeof(struct flash_partition_table)); + + if (!partition_table) { + printk(KERN_WARNING "%s: no flash partition table in shared " + "memory\n", __func__); + return -ENOENT; + } + + if ((partition_table->magic1 != (u32) FLASH_PART_MAGIC1) || + (partition_table->magic2 != (u32) FLASH_PART_MAGIC2) || + (partition_table->version != (u32) FLASH_PARTITION_VERSION)) { + printk(KERN_WARNING "%s: version mismatch -- magic1=%#x, " + "magic2=%#x, version=%#x\n", __func__, + partition_table->magic1, + partition_table->magic2, + partition_table->version); + return -EFAULT; + } + + msm_nand_data.nr_parts = 0; + + /* Get the LINUX FS partition info */ + for (part = 0; part < partition_table->numparts; part++) { + part_entry = &partition_table->part_entry[part]; + + /* Find a match for the Linux file system partition */ + if (strcmp(part_entry->name, LINUX_FS_PARTITION_NAME) == 0) { + strcpy(name, part_entry->name); + ptn->name = name; + + /*TODO: Get block count and size info */ + ptn->offset = part_entry->offset; + + /* For SMEM, -1 indicates remaining space in flash, + * but for MTD it is 0 + */ + if (part_entry->length == (u32)-1) + ptn->size = 0; + else + ptn->size = part_entry->length; + + msm_nand_data.nr_parts = 1; + msm_nand_data.parts = msm_nand_partitions; + + printk(KERN_INFO "Partition(from smem) %s " + "-- Offset:%llx Size:%llx\n", + ptn->name, ptn->offset, ptn->size); + + return 0; + } + } + + printk(KERN_WARNING "%s: no partition table found!", __func__); + + return -ENODEV; +} +#else +static int get_nand_partitions(void) +{ + + if (msm_nand_data.nr_parts) + return 0; + + printk(KERN_WARNING "%s: no partition table found!", __func__); + + return -ENODEV; +} +#endif + +device_initcall(get_nand_partitions); diff --git a/arch/arm/mach-msm/no-pm.c b/arch/arm/mach-msm/no-pm.c new file mode 100644 index 00000000000..d38b4167ce4 --- /dev/null +++ b/arch/arm/mach-msm/no-pm.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include +#include "idle.h" +#include "pm.h" + +void arch_idle(void) +{ } + +void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count) +{ } + +void msm_pm_cpu_enter_lowpower(unsigned cpu) +{ + asm("wfi" + : + : + : "memory", "cc"); +} + +void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) { } + +void msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls) {} + +int msm_pm_idle_prepare(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + return -ENOSYS; +} + +int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode) +{ + return -ENOSYS; +} + diff --git a/arch/arm/mach-msm/nohlt.c b/arch/arm/mach-msm/nohlt.c new file mode 100644 index 00000000000..532d57d6eb7 --- /dev/null +++ b/arch/arm/mach-msm/nohlt.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * MSM architecture driver to control arm halt behavior + */ + +#include +#include +#include +#include + +static int set_nohalt(void *data, u64 val) +{ + if (val) + disable_hlt(); + else + enable_hlt(); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(nohalt_ops, NULL, set_nohalt, "%llu\n"); + +static int __init init_hlt_debug(void) +{ + debugfs_create_file("nohlt", 0200, NULL, NULL, &nohalt_ops); + + return 0; +} + +late_initcall(init_hlt_debug); diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c new file mode 100644 index 00000000000..ddfc9065684 --- /dev/null +++ b/arch/arm/mach-msm/ocmem.c @@ -0,0 +1,317 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ocmem_partition { + const char *name; + int id; + unsigned long p_start; + unsigned long p_size; + unsigned long p_min; + unsigned int p_tail; +}; + +struct ocmem_plat_data { + void __iomem *vbase; + unsigned long size; + unsigned long base; + struct ocmem_partition *parts; + unsigned nr_parts; +}; + +struct ocmem_zone zones[OCMEM_CLIENT_MAX]; + +struct ocmem_zone *get_zone(unsigned id) +{ + if (id < OCMEM_GRAPHICS || id >= OCMEM_CLIENT_MAX) + return NULL; + else + return &zones[id]; +} + +static struct ocmem_plat_data *ocmem_pdata; + +#define CLIENT_NAME_MAX 10 +/* Must be in sync with enum ocmem_client */ +static const char *client_names[OCMEM_CLIENT_MAX] = { + "graphics", + "video", + "camera", + "hp_audio", + "voice", + "lp_audio", + "sensors", + "blast", +}; + +struct ocmem_quota_table { + const char *name; + int id; + unsigned long start; + unsigned long size; + unsigned long min; + unsigned int tail; +}; + +/* This static table will go away with device tree support */ +static struct ocmem_quota_table qt[OCMEM_CLIENT_MAX] = { + /* name, id, start, size, min, tail */ + { "graphics", OCMEM_GRAPHICS, 0x0, 0x100000, 0x80000, 0}, + { "video", OCMEM_VIDEO, 0x100000, 0x80000, 0x55000, 1}, + { "camera", OCMEM_CAMERA, 0x0, 0x0, 0x0, 0}, + { "voice", OCMEM_VOICE, 0x0, 0x0, 0x0, 0 }, + { "hp_audio", OCMEM_HP_AUDIO, 0x0, 0x0, 0x0, 0}, + { "lp_audio", OCMEM_LP_AUDIO, 0x80000, 0xA0000, 0xA0000, 0}, + { "blast", OCMEM_BLAST, 0x120000, 0x20000, 0x20000, 0}, + { "sensors", OCMEM_SENSORS, 0x140000, 0x40000, 0x40000, 0}, +}; + +static inline int get_id(const char *name) +{ + int i = 0; + for (i = 0 ; i < OCMEM_CLIENT_MAX; i++) { + if (strncmp(name, client_names[i], CLIENT_NAME_MAX) == 0) + return i; + } + return -EINVAL; +} + +static struct ocmem_plat_data *parse_static_config(struct platform_device *pdev) +{ + struct ocmem_plat_data *pdata = NULL; + struct ocmem_partition *parts = NULL; + struct device *dev = &pdev->dev; + unsigned nr_parts = 0; + int i; + int j; + + pdata = devm_kzalloc(dev, sizeof(struct ocmem_plat_data), + GFP_KERNEL); + + if (!pdata) { + dev_err(dev, "Unable to allocate memory for" + " platform data\n"); + return NULL; + } + + for (i = 0 ; i < ARRAY_SIZE(qt); i++) + if (qt[i].size != 0x0) + nr_parts++; + + if (nr_parts == 0x0) { + dev_err(dev, "No valid ocmem partitions\n"); + return NULL; + } else + dev_info(dev, "Total partitions = %d\n", nr_parts); + + parts = devm_kzalloc(dev, sizeof(struct ocmem_partition) * nr_parts, + GFP_KERNEL); + + if (!parts) { + dev_err(dev, "Unable to allocate memory for" + " partition data\n"); + return NULL; + } + + for (i = 0, j = 0; i < ARRAY_SIZE(qt); i++) { + if (qt[i].size == 0x0) { + dev_dbg(dev, "Skipping creation of pool for %s\n", + qt[i].name); + continue; + } + parts[j].id = qt[i].id; + parts[j].p_size = qt[i].size; + parts[j].p_start = qt[i].start; + parts[j].p_min = qt[i].min; + parts[j].p_tail = qt[i].tail; + j++; + } + BUG_ON(j != nr_parts); + pdata->nr_parts = nr_parts; + pdata->parts = parts; + pdata->base = OCMEM_PHYS_BASE; + pdata->size = OCMEM_PHYS_SIZE; + return pdata; +} + +static struct ocmem_plat_data *parse_dt_config(struct platform_device *pdev) +{ + return NULL; +} + +static int ocmem_zone_init(struct platform_device *pdev) +{ + + int ret = -1; + int i = 0; + unsigned active_zones = 0; + + struct ocmem_zone *zone = NULL; + struct ocmem_zone_ops *z_ops = NULL; + struct device *dev = &pdev->dev; + unsigned long start; + struct ocmem_plat_data *pdata = NULL; + + pdata = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->nr_parts; i++) { + struct ocmem_partition *part = &pdata->parts[i]; + zone = get_zone(part->id); + + dev_dbg(dev, "Partition %d, start %lx, size %lx for %s\n", + i, part->p_start, part->p_size, + client_names[part->id]); + + if (part->p_size > pdata->size) { + dev_alert(dev, "Quota > ocmem_size for id:%d\n", + part->id); + continue; + } + + zone->z_pool = gen_pool_create(PAGE_SHIFT, -1); + + if (!zone->z_pool) { + dev_alert(dev, "Creating pool failed for id:%d\n", + part->id); + return -EBUSY; + } + + start = pdata->base + part->p_start; + ret = gen_pool_add(zone->z_pool, start, + part->p_size, -1); + + if (ret < 0) { + gen_pool_destroy(zone->z_pool); + dev_alert(dev, "Unable to back pool %d with " + "buffer:%lx\n", part->id, part->p_size); + return -EBUSY; + } + + /* Initialize zone allocators */ + z_ops = devm_kzalloc(dev, sizeof(struct ocmem_zone_ops), + GFP_KERNEL); + if (!z_ops) { + pr_alert("ocmem: Unable to allocate memory for" + "zone ops:%d\n", i); + return -EBUSY; + } + + /* Initialize zone parameters */ + zone->z_start = start; + zone->z_head = zone->z_start; + zone->z_end = start + part->p_size; + zone->z_tail = zone->z_end; + zone->z_free = part->p_size; + zone->owner = part->id; + zone->active_regions = 0; + zone->max_regions = 0; + INIT_LIST_HEAD(&zone->region_list); + zone->z_ops = z_ops; + if (part->p_tail) { + z_ops->allocate = allocate_tail; + z_ops->free = free_tail; + } else { + z_ops->allocate = allocate_head; + z_ops->free = free_head; + } + active_zones++; + + if (active_zones == 1) + pr_info("Physical OCMEM zone layout:\n"); + + pr_info(" zone %s\t: 0x%08lx - 0x%08lx (%4ld KB)\n", + client_names[part->id], zone->z_start, + zone->z_end, part->p_size/SZ_1K); + } + + dev_info(dev, "Total active zones = %d\n", active_zones); + return 0; +} + +static int __devinit msm_ocmem_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + if (!pdev->dev.of_node->child) { + dev_info(dev, "Missing Configuration in Device Tree\n"); + ocmem_pdata = parse_static_config(pdev); + } else { + ocmem_pdata = parse_dt_config(pdev); + } + + /* Check if we have some configuration data to start */ + if (!ocmem_pdata) + return -ENODEV; + + /* Sanity Checks */ + BUG_ON(!IS_ALIGNED(ocmem_pdata->size, PAGE_SIZE)); + BUG_ON(!IS_ALIGNED(ocmem_pdata->base, PAGE_SIZE)); + + platform_set_drvdata(pdev, ocmem_pdata); + + if (ocmem_zone_init(pdev)) + return -EBUSY; + + if (ocmem_notifier_init()) + return -EBUSY; + + dev_info(dev, "initialized successfully\n"); + return 0; +} + +static int __devexit msm_ocmem_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct of_device_id msm_ocmem_dt_match[] = { + { .compatible = "qcom,msm_ocmem", + }, + {} +}; + +static struct platform_driver msm_ocmem_driver = { + .probe = msm_ocmem_probe, + .remove = __devexit_p(msm_ocmem_remove), + .driver = { + .name = "msm_ocmem", + .owner = THIS_MODULE, + .of_match_table = msm_ocmem_dt_match, + }, +}; + +static int __init ocmem_init(void) +{ + return platform_driver_register(&msm_ocmem_driver); +} +subsys_initcall(ocmem_init); + +static void __exit ocmem_exit(void) +{ + platform_driver_unregister(&msm_ocmem_driver); +} +module_exit(ocmem_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Support for On-Chip Memory on MSM"); diff --git a/arch/arm/mach-msm/ocmem_allocator.c b/arch/arm/mach-msm/ocmem_allocator.c new file mode 100644 index 00000000000..71cacda4dc7 --- /dev/null +++ b/arch/arm/mach-msm/ocmem_allocator.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +/* All allocator operations are serialized by ocmem driver */ + +/* The allocators work as follows: + Constraints: + 1) There is no IOMMU access to OCMEM hence successive allocations + in the zone must be physically contiguous + 2) Allocations must be freed in reverse order within a zone. + + z->z_start: Fixed pointer to the start of a zone + z->z_end: Fixed pointer to the end of a zone + + z->z_head: Movable pointer to the next free area when growing at head + Fixed on zones that grow from tail + + z->z_tail: Movable pointer to the next free area when growing at tail + Fixed on zones that grow from head + + z->z_free: Free space in a zone that is updated on an allocation/free + + reserve: Enable libgenpool to simulate tail allocations +*/ + +unsigned long allocate_head(struct ocmem_zone *z, unsigned long size) +{ + + unsigned long offset; + + offset = gen_pool_alloc(z->z_pool, size); + + if (!offset) + return -ENOMEM; + + z->z_head += size; + z->z_free -= size; + return offset; +} + +unsigned long allocate_tail(struct ocmem_zone *z, unsigned long size) +{ + unsigned long offset; + unsigned long reserve; + unsigned long head; + + if (z->z_tail < (z->z_head + size)) + return -ENOMEM; + + reserve = z->z_tail - z->z_head - size; + if (reserve) { + head = gen_pool_alloc(z->z_pool, reserve); + offset = gen_pool_alloc(z->z_pool, size); + gen_pool_free(z->z_pool, head, reserve); + } else + offset = gen_pool_alloc(z->z_pool, size); + + if (!offset) + return -ENOMEM; + + z->z_tail -= size; + z->z_free -= size; + return offset; +} + +int free_head(struct ocmem_zone *z, unsigned long offset, + unsigned long size) +{ + if (offset > z->z_head) { + pr_err("ocmem: Detected out of order free " + "leading to fragmentation\n"); + return -EINVAL; + } + gen_pool_free(z->z_pool, offset, size); + z->z_head -= size; + z->z_free += size; + return 0; +} + +int free_tail(struct ocmem_zone *z, unsigned long offset, + unsigned long size) +{ + if (offset > z->z_tail) { + pr_err("ocmem: Detected out of order free " + "leading to fragmentation\n"); + return -EINVAL; + } + gen_pool_free(z->z_pool, offset, size); + z->z_tail += size; + z->z_free += size; + return 0; +} diff --git a/arch/arm/mach-msm/ocmem_notifier.c b/arch/arm/mach-msm/ocmem_notifier.c new file mode 100644 index 00000000000..58ad3d95f6f --- /dev/null +++ b/arch/arm/mach-msm/ocmem_notifier.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +static unsigned notifier_threshold; + +/* Protect the notifier structure below */ +DEFINE_MUTEX(nc_lock); + +struct ocmem_notifier { + int owner; + struct atomic_notifier_head nc; + unsigned listeners; +} notifiers[OCMEM_CLIENT_MAX]; + +static int check_id(int id) +{ + return (id < OCMEM_CLIENT_MAX && id >= OCMEM_GRAPHICS); +} + +int check_notifier(int id) +{ + int ret = 0; + + if (!check_id(id)) + return 0; + + mutex_lock(&nc_lock); + ret = notifiers[id].listeners; + mutex_unlock(&nc_lock); + return ret; +} + +int ocmem_notifier_init(void) +{ + int id; + /* Maximum notifiers for each subsystem */ + notifier_threshold = 1; + mutex_lock(&nc_lock); + for (id = 0; id < OCMEM_CLIENT_MAX; id++) { + notifiers[id].owner = id; + ATOMIC_INIT_NOTIFIER_HEAD(¬ifiers[id].nc); + notifiers[id].listeners = 0; + } + mutex_unlock(&nc_lock); + return 0; +} + +/* Broadcast a notification to listeners */ +int dispatch_notification(int id, enum ocmem_notif_type notif, + struct ocmem_buf *buf) +{ + int ret = 0; + struct ocmem_notifier *nc_hndl = NULL; + mutex_lock(&nc_lock); + nc_hndl = ¬ifiers[id]; + if (nc_hndl->listeners == 0) { + /* Send an error so that the scheduler can clean up */ + mutex_unlock(&nc_lock); + return -EINVAL; + } + ret = atomic_notifier_call_chain(¬ifiers[id].nc, notif, buf); + mutex_unlock(&nc_lock); + return ret; +} + +void *ocmem_notifier_register(int client_id, struct notifier_block *nb) +{ + + int ret = 0; + struct ocmem_notifier *nc_hndl = NULL; + + if (!check_id(client_id)) { + pr_err("ocmem: Invalid Client id\n"); + return NULL; + } + + if (!nb) { + pr_err("ocmem: Invalid Notifier Block\n"); + return NULL; + } + + mutex_lock(&nc_lock); + + nc_hndl = ¬ifiers[client_id]; + + if (nc_hndl->listeners >= notifier_threshold) { + pr_err("ocmem: Max notifiers already registered\n"); + mutex_unlock(&nc_lock); + return NULL; + } + + ret = atomic_notifier_chain_register(&nc_hndl->nc, nb); + + if (ret < 0) { + mutex_unlock(&nc_lock); + return NULL; + } + + nc_hndl->listeners++; + pr_info("ocmem: Notifier registered for %d\n", client_id); + mutex_unlock(&nc_lock); + return nc_hndl; +} +EXPORT_SYMBOL(ocmem_notifier_register); + +int ocmem_notifier_unregister(void *hndl, struct notifier_block *nb) +{ + + int ret = 0; + + struct ocmem_notifier *nc_hndl = (struct ocmem_notifier *) hndl; + + if (!nc_hndl) { + pr_err("ocmem: Invalid notification handle\n"); + return -EINVAL; + } + + mutex_lock(&nc_lock); + ret = atomic_notifier_chain_unregister(&nc_hndl->nc, nb); + nc_hndl->listeners--; + mutex_unlock(&nc_lock); + + return ret; +} +EXPORT_SYMBOL(ocmem_notifier_unregister); diff --git a/arch/arm/mach-msm/oem_rapi_client.c b/arch/arm/mach-msm/oem_rapi_client.c new file mode 100644 index 00000000000..bcf6e57d588 --- /dev/null +++ b/arch/arm/mach-msm/oem_rapi_client.c @@ -0,0 +1,357 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * OEM RAPI CLIENT Driver source file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OEM_RAPI_PROG 0x3000006B +#define OEM_RAPI_VERS 0x00010001 + +#define OEM_RAPI_NULL_PROC 0 +#define OEM_RAPI_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define OEM_RAPI_STREAMING_FUNCTION_PROC 2 + +#define OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE 128 + +static struct msm_rpc_client *rpc_client; +static uint32_t open_count; +static DEFINE_MUTEX(oem_rapi_client_lock); + +/* TODO: check where to allocate memory for return */ +static int oem_rapi_client_cb(struct msm_rpc_client *client, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + uint32_t cb_id, accept_status; + int rc; + void *cb_func; + uint32_t temp; + + struct oem_rapi_client_streaming_func_cb_arg arg; + struct oem_rapi_client_streaming_func_cb_ret ret; + + arg.input = NULL; + ret.out_len = NULL; + ret.output = NULL; + + xdr_recv_uint32(xdr, &cb_id); /* cb_id */ + xdr_recv_uint32(xdr, &arg.event); /* enum */ + xdr_recv_uint32(xdr, (uint32_t *)(&arg.handle)); /* handle */ + xdr_recv_uint32(xdr, &arg.in_len); /* in_len */ + xdr_recv_bytes(xdr, (void **)&arg.input, &temp); /* input */ + xdr_recv_uint32(xdr, &arg.out_len_valid); /* out_len */ + if (arg.out_len_valid) { + ret.out_len = kmalloc(sizeof(*ret.out_len), GFP_KERNEL); + if (!ret.out_len) { + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto oem_rapi_send_ack; + } + } + + xdr_recv_uint32(xdr, &arg.output_valid); /* out */ + if (arg.output_valid) { + xdr_recv_uint32(xdr, &arg.output_size); /* ouput_size */ + + ret.output = kmalloc(arg.output_size, GFP_KERNEL); + if (!ret.output) { + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto oem_rapi_send_ack; + } + } + + cb_func = msm_rpc_get_cb_func(client, cb_id); + if (cb_func) { + rc = ((int (*)(struct oem_rapi_client_streaming_func_cb_arg *, + struct oem_rapi_client_streaming_func_cb_ret *)) + cb_func)(&arg, &ret); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + oem_rapi_send_ack: + xdr_start_accepted_reply(xdr, accept_status); + + if (accept_status == RPC_ACCEPTSTAT_SUCCESS) { + uint32_t temp = sizeof(uint32_t); + xdr_send_pointer(xdr, (void **)&(ret.out_len), temp, + xdr_send_uint32); + + /* output */ + if (ret.output && ret.out_len) + xdr_send_bytes(xdr, (const void **)&ret.output, + ret.out_len); + else { + temp = 0; + xdr_send_uint32(xdr, &temp); + } + } + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: sending reply failed: %d\n", __func__, rc); + + kfree(arg.input); + kfree(ret.out_len); + kfree(ret.output); + + return 0; +} + +static int oem_rapi_client_streaming_function_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, + void *data) +{ + int cb_id; + struct oem_rapi_client_streaming_func_arg *arg = data; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + xdr_send_uint32(xdr, &arg->event); /* enum */ + xdr_send_uint32(xdr, &cb_id); /* cb_id */ + xdr_send_uint32(xdr, (uint32_t *)(&arg->handle)); /* handle */ + xdr_send_uint32(xdr, &arg->in_len); /* in_len */ + xdr_send_bytes(xdr, (const void **)&arg->input, + &arg->in_len); /* input */ + xdr_send_uint32(xdr, &arg->out_len_valid); /* out_len */ + xdr_send_uint32(xdr, &arg->output_valid); /* output */ + + /* output_size */ + if (arg->output_valid) + xdr_send_uint32(xdr, &arg->output_size); + + return 0; +} + +static int oem_rapi_client_streaming_function_ret(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, + void *data) +{ + struct oem_rapi_client_streaming_func_ret *ret = data; + uint32_t temp; + + /* out_len */ + xdr_recv_pointer(xdr, (void **)&(ret->out_len), sizeof(uint32_t), + xdr_recv_uint32); + + /* output */ + if (ret->out_len && *ret->out_len) + xdr_recv_bytes(xdr, (void **)&ret->output, &temp); + + return 0; +} + +int oem_rapi_client_streaming_function( + struct msm_rpc_client *client, + struct oem_rapi_client_streaming_func_arg *arg, + struct oem_rapi_client_streaming_func_ret *ret) +{ + return msm_rpc_client_req2(client, + OEM_RAPI_STREAMING_FUNCTION_PROC, + oem_rapi_client_streaming_function_arg, arg, + oem_rapi_client_streaming_function_ret, + ret, -1); +} +EXPORT_SYMBOL(oem_rapi_client_streaming_function); + +int oem_rapi_client_close(void) +{ + mutex_lock(&oem_rapi_client_lock); + if (--open_count == 0) { + msm_rpc_unregister_client(rpc_client); + pr_info("%s: disconnected from remote oem rapi server\n", + __func__); + } + mutex_unlock(&oem_rapi_client_lock); + return 0; +} +EXPORT_SYMBOL(oem_rapi_client_close); + +struct msm_rpc_client *oem_rapi_client_init(void) +{ + mutex_lock(&oem_rapi_client_lock); + if (open_count == 0) { + rpc_client = msm_rpc_register_client2("oemrapiclient", + OEM_RAPI_PROG, + OEM_RAPI_VERS, 0, + oem_rapi_client_cb); + if (!IS_ERR(rpc_client)) + open_count++; + } + mutex_unlock(&oem_rapi_client_lock); + return rpc_client; +} +EXPORT_SYMBOL(oem_rapi_client_init); + +#if defined(CONFIG_DEBUG_FS) + +static struct dentry *dent; +static int oem_rapi_client_test_res; + +static int oem_rapi_client_null(struct msm_rpc_client *client, + void *arg, void *ret) +{ + return msm_rpc_client_req2(client, OEM_RAPI_NULL_PROC, + NULL, NULL, NULL, NULL, -1); +} + +static int oem_rapi_client_test_streaming_cb_func( + struct oem_rapi_client_streaming_func_cb_arg *arg, + struct oem_rapi_client_streaming_func_cb_ret *ret) +{ + uint32_t size; + + size = (arg->in_len < OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE) ? + arg->in_len : OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE; + + if (ret->out_len != 0) + *ret->out_len = size; + + if (ret->output != 0) + memcpy(ret->output, arg->input, size); + + return 0; +} + +static ssize_t debug_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char _buf[16]; + + snprintf(_buf, sizeof(_buf), "%i\n", oem_rapi_client_test_res); + + return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf)); +} + +static ssize_t debug_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + char input[OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE]; + struct oem_rapi_client_streaming_func_arg arg; + struct oem_rapi_client_streaming_func_ret ret; + + unsigned char cmd[64]; + int len; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "null", 64)) { + oem_rapi_client_test_res = oem_rapi_client_null(rpc_client, + NULL, NULL); + } else if (!strncmp(cmd, "streaming_func", 64)) { + memset(input, 5, 16); + arg.event = 0; + arg.cb_func = oem_rapi_client_test_streaming_cb_func; + arg.handle = (void *)20; + arg.in_len = 16; + arg.input = input; + arg.out_len_valid = 1; + arg.output_valid = 1; + arg.output_size = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE; + ret.out_len = NULL; + ret.output = NULL; + + oem_rapi_client_test_res = oem_rapi_client_streaming_function( + rpc_client, &arg, &ret); + + kfree(ret.out_len); + kfree(ret.output); + + } else + oem_rapi_client_test_res = -EINVAL; + + if (oem_rapi_client_test_res) + pr_err("oem rapi client test fail %d\n", + oem_rapi_client_test_res); + else + pr_info("oem rapi client test passed\n"); + + return count; +} + +static int debug_release(struct inode *ip, struct file *fp) +{ + return oem_rapi_client_close(); +} + +static int debug_open(struct inode *ip, struct file *fp) +{ + struct msm_rpc_client *client; + client = oem_rapi_client_init(); + if (IS_ERR(client)) { + pr_err("%s: couldn't open oem rapi client\n", __func__); + return PTR_ERR(client); + } else + pr_info("%s: connected to remote oem rapi server\n", __func__); + + return 0; +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = debug_open, + .release = debug_release, + .read = debug_read, + .write = debug_write, +}; + +static void __exit oem_rapi_client_mod_exit(void) +{ + debugfs_remove(dent); +} + +static int __init oem_rapi_client_mod_init(void) +{ + dent = debugfs_create_file("oem_rapi", 0444, 0, NULL, &debug_ops); + open_count = 0; + oem_rapi_client_test_res = -1; + return 0; +} + +module_init(oem_rapi_client_mod_init); +module_exit(oem_rapi_client_mod_exit); + +#endif + +MODULE_DESCRIPTION("OEM RAPI CLIENT Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pcie.c b/arch/arm/mach-msm/pcie.c new file mode 100644 index 00000000000..f0809d35790 --- /dev/null +++ b/arch/arm/mach-msm/pcie.c @@ -0,0 +1,671 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * MSM PCIe controller driver. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie.h" + +/* Root Complex Port vendor/device IDs */ +#define PCIE_VENDOR_ID_RCP 0x17cb +#define PCIE_DEVICE_ID_RCP 0x0101 + +#define PCIE20_PARF_PCS_DEEMPH 0x34 +#define PCIE20_PARF_PCS_SWING 0x38 +#define PCIE20_PARF_PHY_CTRL 0x40 +#define PCIE20_PARF_PHY_REFCLK 0x4C +#define PCIE20_PARF_CONFIG_BITS 0x50 + +#define PCIE20_ELBI_SYS_CTRL 0x04 + +#define PCIE20_CAP 0x70 +#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) + +#define PCIE20_COMMAND_STATUS 0x04 +#define PCIE20_BUSNUMBERS 0x18 +#define PCIE20_MEMORY_BASE_LIMIT 0x20 + +#define PCIE20_PLR_IATU_VIEWPORT 0x900 +#define PCIE20_PLR_IATU_CTRL1 0x904 +#define PCIE20_PLR_IATU_CTRL2 0x908 +#define PCIE20_PLR_IATU_LBAR 0x90C +#define PCIE20_PLR_IATU_UBAR 0x910 +#define PCIE20_PLR_IATU_LAR 0x914 +#define PCIE20_PLR_IATU_LTAR 0x918 +#define PCIE20_PLR_IATU_UTAR 0x91c + +#define PCIE_RESET (MSM_CLK_CTL_BASE + 0x22dc) +#define PCIE_SFAB_AXI_S5_FCLK_CTL (MSM_CLK_CTL_BASE + 0x2154) + +#define MSM_PCIE_DEV_BAR_ADDR PCIBIOS_MIN_MEM +#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 + +#define RD 0 +#define WR 1 + +/* debug mask sys interface */ +static int msm_pcie_debug_mask; +module_param_named(debug_mask, msm_pcie_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +/* resources from device file */ +enum msm_pcie_res { + MSM_PCIE_RES_PARF, + MSM_PCIE_RES_ELBI, + MSM_PCIE_RES_PCIE20, + MSM_PCIE_RES_AXI_BAR, + MSM_PCIE_RES_AXI_CONF, + MSM_PCIE_MAX_RES +}; + +/* msm pcie device data */ +static struct msm_pcie_dev_t msm_pcie_dev; + +/* regulators */ +static struct msm_pcie_vreg_info_t msm_pcie_vreg_info[MSM_PCIE_MAX_VREG] = { + {NULL, "vp_pcie", 1050000, 1050000, 40900}, + {NULL, "vptx_pcie", 1050000, 1050000, 18200}, + {NULL, "vdd_pcie_vph", 0, 0, 0}, + {NULL, "pcie_ext_3p3v", 0, 0, 0} +}; + +/* clocks */ +static struct msm_pcie_clk_info_t msm_pcie_clk_info[MSM_PCIE_MAX_CLK] = { + {NULL, "bus_clk"}, + {NULL, "iface_clk"}, + {NULL, "ref_clk"} +}; + +/* resources */ +static struct msm_pcie_res_info_t msm_pcie_res_info[MSM_PCIE_MAX_RES] = { + {"parf", 0, 0, 0}, + {"elbi", 0, 0, 0}, + {"pcie20", 0, 0, 0}, + {"axi_bar", 0, 0, 0}, + {"axi_conf", 0, 0, 0}, +}; + +int msm_pcie_get_debug_mask(void) +{ + return msm_pcie_debug_mask; +} + +static void msm_pcie_write_mask(void __iomem *addr, + uint32_t clear_mask, uint32_t set_mask) +{ + uint32_t val; + + val = (readl_relaxed(addr) & ~clear_mask) | set_mask; + writel_relaxed(val, addr); + wmb(); /* ensure data is written to hardware register */ +} + +static int msm_pcie_is_link_up(void) +{ + return readl_relaxed(msm_pcie_dev.pcie20 + PCIE20_CAP_LINKCTRLSTATUS) & + BIT(29); +} + +static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper, + int where, int size, u32 *val) +{ + uint32_t word_offset, byte_offset, mask; + uint32_t rd_val, wr_val; + struct msm_pcie_dev_t *dev = &msm_pcie_dev; + void __iomem *config_base; + + /* + * Only buses 0 and 1 are supported. RC port on bus 0 and EP in bus 1. + * For downstream bus (1), make sure link is up + */ + if ((bus->number > 1) || (devfn != 0)) { + PCIE_DBG("invalid %s - bus %d devfn %d\n", + (oper == RD) ? "rd" : "wr", bus->number, devfn); + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } else if ((bus->number != 0) && !msm_pcie_is_link_up()) { + PCIE_DBG("%s fail, link down - bus %d devfn %d\n", + (oper == RD) ? "rd" : "wr", bus->number, devfn); + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + word_offset = where & ~0x3; + byte_offset = where & 0x3; + mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset); + + config_base = (bus->number == 0) ? dev->pcie20 : dev->axi_conf; + rd_val = readl_relaxed(config_base + word_offset); + + if (oper == RD) { + *val = ((rd_val & mask) >> (8 * byte_offset)); + + PCIE_DBG("%d:0x%02x + 0x%04x[%d] -> 0x%08x; rd 0x%08x\n", + bus->number, devfn, where, size, *val, rd_val); + } else { + wr_val = (rd_val & ~mask) | + ((*val << (8 * byte_offset)) & mask); + writel_relaxed(wr_val, config_base + word_offset); + wmb(); /* ensure config data is written to hardware register */ + + PCIE_DBG("%d:0x%02x + 0x%04x[%d] <- 0x%08x;" + " rd 0x%08x val 0x%08x\n", bus->number, + devfn, where, size, wr_val, rd_val, *val); + } + + return 0; +} + +static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + return msm_pcie_oper_conf(bus, devfn, RD, where, size, val); +} + +static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val); +} + +static struct pci_ops msm_pcie_ops = { + .read = msm_pcie_rd_conf, + .write = msm_pcie_wr_conf, +}; + +static int __init msm_pcie_gpio_init(void) +{ + int rc, i; + struct msm_pcie_gpio_info_t *info; + + for (i = 0; i < MSM_PCIE_MAX_GPIO; i++) { + info = &msm_pcie_dev.gpio[i]; + + rc = gpio_request(info->num, info->name); + if (rc) { + pr_err("can't get gpio %s; %d\n", info->name, rc); + break; + } + + rc = gpio_direction_output(info->num, 0); + if (rc) { + pr_err("can't set gpio direction %s; %d\n", + info->name, rc); + gpio_free(info->num); + break; + } + } + + if (rc) + while (i--) + gpio_free(msm_pcie_dev.gpio[i].num); + + return rc; +} + +static void msm_pcie_gpio_deinit(void) +{ + int i; + + for (i = 0; i < MSM_PCIE_MAX_GPIO; i++) + gpio_free(msm_pcie_dev.gpio[i].num); +} + +static int __init msm_pcie_vreg_init(struct device *dev) +{ + int i, rc = 0; + struct regulator *vreg; + struct msm_pcie_vreg_info_t *info; + + for (i = 0; i < MSM_PCIE_MAX_VREG; i++) { + info = &msm_pcie_dev.vreg[i]; + + vreg = regulator_get(dev, info->name); + if (!vreg || IS_ERR(vreg)) { + rc = (PTR_ERR(vreg)) ? PTR_ERR(vreg) : -ENODEV; + pr_err("can't get %s; %d\n", info->name, rc); + break; + } + + if (info->max_v) { + rc = regulator_set_voltage(vreg, + info->min_v, info->max_v); + if (rc) { + pr_err("can't set voltage %s; %d\n", + info->name, rc); + regulator_put(vreg); + break; + } + } + + if (info->opt_mode) { + rc = regulator_set_optimum_mode(vreg, info->opt_mode); + if (rc < 0) { + pr_err("can't set mode %s; %d\n", + info->name, rc); + regulator_put(vreg); + break; + } + } + + rc = regulator_enable(vreg); + if (rc) { + pr_err("can't enable %s, %d\n", info->name, rc); + regulator_put(vreg); + break; + } + info->hdl = vreg; + } + + if (rc) + while (i--) { + regulator_disable(msm_pcie_dev.vreg[i].hdl); + regulator_put(msm_pcie_dev.vreg[i].hdl); + msm_pcie_dev.vreg[i].hdl = NULL; + } + + return rc; +} + +static void msm_pcie_vreg_deinit(void) +{ + int i; + + for (i = 0; i < MSM_PCIE_MAX_VREG; i++) { + regulator_disable(msm_pcie_dev.vreg[i].hdl); + regulator_put(msm_pcie_dev.vreg[i].hdl); + msm_pcie_dev.vreg[i].hdl = NULL; + } +} + +static int __init msm_pcie_clk_init(struct device *dev) +{ + int i, rc = 0; + struct clk *clk_hdl; + struct msm_pcie_clk_info_t *info; + + for (i = 0; i < MSM_PCIE_MAX_CLK; i++) { + info = &msm_pcie_dev.clk[i]; + + clk_hdl = clk_get(dev, info->name); + if (!clk_hdl || IS_ERR(clk_hdl)) { + rc = (PTR_ERR(clk_hdl)) ? PTR_ERR(clk_hdl) : -ENODEV; + pr_err("can't get clk %s; %d\n", info->name, rc); + break; + } + clk_prepare_enable(clk_hdl); + info->hdl = clk_hdl; + } + + if (rc) + while (i--) { + clk_disable_unprepare(msm_pcie_dev.clk[i].hdl); + clk_put(msm_pcie_dev.clk[i].hdl); + msm_pcie_dev.clk[i].hdl = NULL; + } + + return rc; +} + +static void msm_pcie_clk_deinit(void) +{ + int i; + + for (i = 0; i < MSM_PCIE_MAX_CLK; i++) { + clk_disable_unprepare(msm_pcie_dev.clk[i].hdl); + clk_put(msm_pcie_dev.clk[i].hdl); + msm_pcie_dev.clk[i].hdl = NULL; + } +} + +static void __init msm_pcie_config_controller(void) +{ + struct msm_pcie_dev_t *dev = &msm_pcie_dev; + struct msm_pcie_res_info_t *axi_bar = &dev->res[MSM_PCIE_RES_AXI_BAR]; + struct msm_pcie_res_info_t *axi_conf = &dev->res[MSM_PCIE_RES_AXI_CONF]; + + /* + * program and enable address translation region 0 (device config + * address space); region type config; + * axi config address range to device config address range + */ + writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_VIEWPORT); + /* ensure that hardware locks the region before programming it */ + wmb(); + + writel_relaxed(4, dev->pcie20 + PCIE20_PLR_IATU_CTRL1); + writel_relaxed(BIT(31), dev->pcie20 + PCIE20_PLR_IATU_CTRL2); + writel_relaxed(axi_conf->start, dev->pcie20 + PCIE20_PLR_IATU_LBAR); + writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UBAR); + writel_relaxed(axi_conf->end, dev->pcie20 + PCIE20_PLR_IATU_LAR); + writel_relaxed(MSM_PCIE_DEV_CFG_ADDR, + dev->pcie20 + PCIE20_PLR_IATU_LTAR); + writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UTAR); + /* ensure that hardware registers the configuration */ + wmb(); + + /* + * program and enable address translation region 2 (device resource + * address space); region type memory; + * axi device bar address range to device bar address range + */ + writel_relaxed(2, dev->pcie20 + PCIE20_PLR_IATU_VIEWPORT); + /* ensure that hardware locks the region before programming it */ + wmb(); + + writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_CTRL1); + writel_relaxed(BIT(31), dev->pcie20 + PCIE20_PLR_IATU_CTRL2); + writel_relaxed(axi_bar->start, dev->pcie20 + PCIE20_PLR_IATU_LBAR); + writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UBAR); + writel_relaxed(axi_bar->end, dev->pcie20 + PCIE20_PLR_IATU_LAR); + writel_relaxed(MSM_PCIE_DEV_BAR_ADDR, + dev->pcie20 + PCIE20_PLR_IATU_LTAR); + writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UTAR); + /* ensure that hardware registers the configuration */ + wmb(); +} + +static int __init msm_pcie_get_resources(struct platform_device *pdev) +{ + int i, rc = 0; + struct resource *res; + struct msm_pcie_res_info_t *info; + struct msm_pcie_dev_t *dev = &msm_pcie_dev; + + for (i = 0; i < MSM_PCIE_MAX_RES; i++) { + info = &dev->res[i]; + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, info->name); + if (!res) { + pr_err("can't get %s resource\n", info->name); + rc = -ENOMEM; + break; + } + + info->base = ioremap(res->start, resource_size(res)); + if (!info->base) { + pr_err("can't remap %s\n", info->name); + rc = -ENOMEM; + break; + } + + info->start = res->start; + info->end = res->end; + } + + if (rc) { + while (i--) { + iounmap(dev->res[i].base); + dev->res[i].base = NULL; + } + } else { + dev->parf = dev->res[MSM_PCIE_RES_PARF].base; + dev->elbi = dev->res[MSM_PCIE_RES_ELBI].base; + dev->pcie20 = dev->res[MSM_PCIE_RES_PCIE20].base; + dev->axi_conf = dev->res[MSM_PCIE_RES_AXI_CONF].base; + } + + return rc; +} + +static void msm_pcie_release_resources(void) +{ + int i; + + for (i = 0; i < MSM_PCIE_MAX_RES; i++) { + iounmap(msm_pcie_dev.res[i].base); + msm_pcie_dev.res[i].base = NULL; + } + + msm_pcie_dev.parf = NULL; + msm_pcie_dev.elbi = NULL; + msm_pcie_dev.pcie20 = NULL; + msm_pcie_dev.axi_conf = NULL; +} + +static int __init msm_pcie_setup(int nr, struct pci_sys_data *sys) +{ + int rc; + struct msm_pcie_dev_t *dev = &msm_pcie_dev; + uint32_t val; + + PCIE_DBG("bus %d\n", nr); + if (nr != 0) + return 0; + + /* assert PCIe reset link to keep EP in reset */ + gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_RST_N].num, + dev->gpio[MSM_PCIE_GPIO_RST_N].on); + + /* enable power */ + rc = msm_pcie_vreg_init(&dev->pdev->dev); + if (rc) + goto out; + + /* assert PCIe PARF reset while powering the core */ + msm_pcie_write_mask(PCIE_RESET, 0, BIT(2)); + + /* enable clocks */ + rc = msm_pcie_clk_init(&dev->pdev->dev); + if (rc) + goto clk_fail; + + /* enable pcie power; wait 3ms for clock to stabilize */ + gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_PWR_EN].num, + dev->gpio[MSM_PCIE_GPIO_PWR_EN].on); + usleep(3000); + + /* + * de-assert PCIe PARF reset; + * wait 1us before accessing PARF registers + */ + msm_pcie_write_mask(PCIE_RESET, BIT(2), 0); + udelay(1); + + /* enable PCIe clocks and resets */ + msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); + + /* PARF programming */ + writel_relaxed(0x282828, dev->parf + PCIE20_PARF_PCS_DEEMPH); + writel_relaxed(0x7F7F, dev->parf + PCIE20_PARF_PCS_SWING); + writel_relaxed((4<<24), dev->parf + PCIE20_PARF_CONFIG_BITS); + /* ensure that hardware registers the PARF configuration */ + wmb(); + + /* enable reference clock */ + msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); + + /* enable access to PCIe slave port on system fabric */ + writel_relaxed(BIT(4), PCIE_SFAB_AXI_S5_FCLK_CTL); + /* ensure that access is enabled before proceeding */ + wmb(); + + /* de-assert PICe PHY, Core, POR and AXI clk domain resets */ + msm_pcie_write_mask(PCIE_RESET, BIT(5), 0); + msm_pcie_write_mask(PCIE_RESET, BIT(4), 0); + msm_pcie_write_mask(PCIE_RESET, BIT(3), 0); + msm_pcie_write_mask(PCIE_RESET, BIT(0), 0); + + /* wait 150ms for clock acquisition */ + udelay(150); + + /* de-assert PCIe reset link to bring EP out of reset */ + gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_RST_N].num, + !dev->gpio[MSM_PCIE_GPIO_RST_N].on); + + /* enable link training */ + msm_pcie_write_mask(dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); + + /* poll for link to come up for upto 100ms */ + rc = readl_poll_timeout( + (msm_pcie_dev.pcie20 + PCIE20_CAP_LINKCTRLSTATUS), + val, (val & BIT(29)), 10000, 100000); + if (rc) { + pr_err("link initialization failed\n"); + goto link_fail; + } else + pr_info("link initialized\n"); + + msm_pcie_config_controller(); + rc = msm_pcie_irq_init(dev); + if (!rc) + goto out; + +link_fail: + msm_pcie_clk_deinit(); +clk_fail: + msm_pcie_vreg_deinit(); +out: + return (rc) ? 0 : 1; +} + +static struct pci_bus __init *msm_pcie_scan_bus(int nr, + struct pci_sys_data *sys) +{ + struct pci_bus *bus = NULL; + + PCIE_DBG("bus %d\n", nr); + if (nr == 0) + bus = pci_scan_bus(sys->busnr, &msm_pcie_ops, sys); + + return bus; +} + +static int __init msm_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + PCIE_DBG("slot %d pin %d\n", slot, pin); + return (pin <= 4) ? (PCIE20_INTA + pin - 1) : 0; +} + +static struct hw_pci msm_pci __initdata = { + .nr_controllers = 1, + .swizzle = pci_std_swizzle, + .setup = msm_pcie_setup, + .scan = msm_pcie_scan_bus, + .map_irq = msm_pcie_map_irq, +}; + +static int __init msm_pcie_probe(struct platform_device *pdev) +{ + const struct msm_pcie_platform *pdata; + int rc; + + PCIE_DBG("\n"); + + msm_pcie_dev.pdev = pdev; + pdata = pdev->dev.platform_data; + msm_pcie_dev.gpio = pdata->gpio; + msm_pcie_dev.vreg = msm_pcie_vreg_info; + msm_pcie_dev.clk = msm_pcie_clk_info; + msm_pcie_dev.res = msm_pcie_res_info; + + rc = msm_pcie_get_resources(msm_pcie_dev.pdev); + if (rc) + return rc; + + rc = msm_pcie_gpio_init(); + if (rc) { + msm_pcie_release_resources(); + return rc; + } + + /* kick start ARM PCI configuration framework */ + pci_common_init(&msm_pci); + return 0; +} + +static int __exit msm_pcie_remove(struct platform_device *pdev) +{ + PCIE_DBG("\n"); + + msm_pcie_irq_deinit(&msm_pcie_dev); + msm_pcie_vreg_deinit(); + msm_pcie_clk_deinit(); + msm_pcie_gpio_deinit(); + msm_pcie_release_resources(); + + msm_pcie_dev.pdev = NULL; + msm_pcie_dev.vreg = NULL; + msm_pcie_dev.clk = NULL; + msm_pcie_dev.gpio = NULL; + return 0; +} + +static struct platform_driver msm_pcie_driver = { + .remove = __exit_p(msm_pcie_remove), + .driver = { + .name = "msm_pcie", + .owner = THIS_MODULE, + }, +}; + +static int __init msm_pcie_init(void) +{ + PCIE_DBG("\n"); + pcibios_min_io = 0x10000000; + pcibios_min_mem = 0x10000000; + return platform_driver_probe(&msm_pcie_driver, msm_pcie_probe); +} +subsys_initcall(msm_pcie_init); + +/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */ +static void __devinit msm_pcie_fixup_header(struct pci_dev *dev) +{ + PCIE_DBG("hdr_type %d\n", dev->hdr_type); + if (dev->hdr_type == 1) + dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8); +} +DECLARE_PCI_FIXUP_HEADER(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, + msm_pcie_fixup_header); + +/* + * actual physical (BAR) address of the device resources starts from 0x10xxxxxx; + * the system axi address for the device resources starts from 0x08xxxxxx; + * correct the device resource structure here; address translation unit handles + * the required translations + */ +static void __devinit msm_pcie_fixup_final(struct pci_dev *dev) +{ + int i; + + PCIE_DBG("vendor 0x%x 0x%x\n", dev->vendor, dev->device); + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + if (dev->resource[i].start & 0xFF000000) { + dev->resource[i].start &= 0x00FFFFFF; + dev->resource[i].start |= 0x08000000; + dev->resource[i].end &= 0x00FFFFFF; + dev->resource[i].end |= 0x08000000; + } + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, msm_pcie_fixup_final); diff --git a/arch/arm/mach-msm/pcie.h b/arch/arm/mach-msm/pcie.h new file mode 100644 index 00000000000..4866ec5c0cc --- /dev/null +++ b/arch/arm/mach-msm/pcie.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_PCIE_H +#define __ARCH_ARM_MACH_MSM_PCIE_H + +#include +#include +#include +#include +#include +#include + +#define MSM_PCIE_MAX_VREG 4 +#define MSM_PCIE_MAX_CLK 3 + +#define PCIE_DBG(x...) do { \ + if (msm_pcie_get_debug_mask()) \ + pr_info(x); \ + } while (0) + +/* voltage regulator info structrue */ +struct msm_pcie_vreg_info_t { + struct regulator *hdl; + char *name; + uint32_t max_v; + uint32_t min_v; + uint32_t opt_mode; +}; + +/* clock info structure */ +struct msm_pcie_clk_info_t { + struct clk *hdl; + char *name; +}; + +/* resource info structure */ +struct msm_pcie_res_info_t { + char *name; + uint32_t start; + uint32_t end; + void __iomem *base; +}; + +/* msm pcie device structure */ +struct msm_pcie_dev_t { + struct platform_device *pdev; + + struct msm_pcie_vreg_info_t *vreg; + struct msm_pcie_gpio_info_t *gpio; + struct msm_pcie_clk_info_t *clk; + struct msm_pcie_res_info_t *res; + + void __iomem *parf; + void __iomem *elbi; + void __iomem *pcie20; + void __iomem *axi_conf; +}; + +extern uint32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev); +extern void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev); +extern int msm_pcie_get_debug_mask(void); + +#endif diff --git a/arch/arm/mach-msm/pcie_irq.c b/arch/arm/mach-msm/pcie_irq.c new file mode 100644 index 00000000000..d91556168f9 --- /dev/null +++ b/arch/arm/mach-msm/pcie_irq.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * MSM PCIe controller IRQ driver. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include + +#include "pcie.h" + +/* Any address will do here, as it won't be dereferenced */ +#define MSM_PCIE_MSI_PHY 0xa0000000 + +#define PCIE20_MSI_CTRL_ADDR (0x820) +#define PCIE20_MSI_CTRL_UPPER_ADDR (0x824) +#define PCIE20_MSI_CTRL_INTR_EN (0x828) +#define PCIE20_MSI_CTRL_INTR_MASK (0x82C) +#define PCIE20_MSI_CTRL_INTR_STATUS (0x830) + +#define PCIE20_MSI_CTRL_MAX 8 + +static DECLARE_BITMAP(msi_irq_in_use, NR_PCIE_MSI_IRQS); + +irqreturn_t handle_msi_irq(int irq, void *data) +{ + int i, j; + unsigned long val; + struct msm_pcie_dev_t *dev = data; + void __iomem *ctrl_status; + + /* check for set bits, clear it by setting that bit + and trigger corresponding irq */ + for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) { + ctrl_status = dev->pcie20 + + PCIE20_MSI_CTRL_INTR_STATUS + (i * 12); + + val = readl_relaxed(ctrl_status); + while (val) { + j = find_first_bit(&val, 32); + writel_relaxed(BIT(j), ctrl_status); + /* ensure that interrupt is cleared (acked) */ + wmb(); + + generic_handle_irq(MSM_PCIE_MSI_INT(j + (32 * i))); + val = readl_relaxed(ctrl_status); + } + } + + return IRQ_HANDLED; +} + +uint32_t __init msm_pcie_irq_init(struct msm_pcie_dev_t *dev) +{ + int i, rc; + + PCIE_DBG("\n"); + + /* program MSI controller and enable all interrupts */ + writel_relaxed(MSM_PCIE_MSI_PHY, dev->pcie20 + PCIE20_MSI_CTRL_ADDR); + writel_relaxed(0, dev->pcie20 + PCIE20_MSI_CTRL_UPPER_ADDR); + + for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) + writel_relaxed(~0, dev->pcie20 + + PCIE20_MSI_CTRL_INTR_EN + (i * 12)); + + /* ensure that hardware is configured before proceeding */ + wmb(); + + /* register handler for physical MSI interrupt line */ + rc = request_irq(PCIE20_INT_MSI, handle_msi_irq, IRQF_TRIGGER_RISING, + "msm_pcie_msi", dev); + if (rc) + pr_err("Unable to allocate msi interrupt\n"); + + return rc; +} + +void __exit msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev) +{ + free_irq(PCIE20_INT_MSI, dev); +} + +void msm_pcie_destroy_irq(unsigned int irq) +{ + int pos = irq - MSM_PCIE_MSI_INT(0); + + dynamic_irq_cleanup(irq); + clear_bit(pos, msi_irq_in_use); +} + +/* hookup to linux pci msi framework */ +void arch_teardown_msi_irq(unsigned int irq) +{ + PCIE_DBG("irq %d deallocated\n", irq); + msm_pcie_destroy_irq(irq); +} + +static void msm_pcie_msi_nop(struct irq_data *d) +{ + return; +} + +static struct irq_chip pcie_msi_chip = { + .name = "msm-pcie-msi", + .irq_ack = msm_pcie_msi_nop, + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, +}; + +static int msm_pcie_create_irq(void) +{ + int irq, pos; + +again: + pos = find_first_zero_bit(msi_irq_in_use, NR_PCIE_MSI_IRQS); + irq = MSM_PCIE_MSI_INT(pos); + if (irq >= (MSM_PCIE_MSI_INT(0) + NR_PCIE_MSI_IRQS)) + return -ENOSPC; + + if (test_and_set_bit(pos, msi_irq_in_use)) + goto again; + + dynamic_irq_init(irq); + return irq; +} + +/* hookup to linux pci msi framework */ +int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) +{ + int irq; + struct msi_msg msg; + + irq = msm_pcie_create_irq(); + if (irq < 0) + return irq; + + PCIE_DBG("irq %d allocated\n", irq); + + irq_set_msi_desc(irq, desc); + + /* write msi vector and data */ + msg.address_hi = 0; + msg.address_lo = MSM_PCIE_MSI_PHY; + msg.data = irq - MSM_PCIE_MSI_INT(0); + write_msi_msg(irq, &msg); + + irq_set_chip_and_handler(irq, &pcie_msi_chip, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + return 0; +} diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c new file mode 100644 index 00000000000..bfbf4bc19fa --- /dev/null +++ b/arch/arm/mach-msm/peripheral-loader.c @@ -0,0 +1,687 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "peripheral-loader.h" + +enum pil_state { + PIL_OFFLINE, + PIL_ONLINE, +}; + +static const char *pil_states[] = { + [PIL_OFFLINE] = "OFFLINE", + [PIL_ONLINE] = "ONLINE", +}; + +struct pil_device { + struct pil_desc *desc; + int count; + enum pil_state state; + struct mutex lock; + struct device dev; + struct module *owner; +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct delayed_work proxy; + struct wake_lock wlock; + char wake_name[32]; +}; + +#define to_pil_device(d) container_of(d, struct pil_device, dev) + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", to_pil_device(dev)->desc->name); +} + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + enum pil_state state = to_pil_device(dev)->state; + return snprintf(buf, PAGE_SIZE, "%s\n", pil_states[state]); +} + +static struct device_attribute pil_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(state), + { }, +}; + +struct bus_type pil_bus_type = { + .name = "pil", + .dev_attrs = pil_attrs, +}; + +static int __find_peripheral(struct device *dev, void *data) +{ + struct pil_device *pdev = to_pil_device(dev); + return !strncmp(pdev->desc->name, data, INT_MAX); +} + +static struct pil_device *find_peripheral(const char *str) +{ + struct device *dev; + + if (!str) + return NULL; + + dev = bus_find_device(&pil_bus_type, NULL, (void *)str, + __find_peripheral); + return dev ? to_pil_device(dev) : NULL; +} + +static void pil_proxy_work(struct work_struct *work) +{ + struct pil_device *pil; + + pil = container_of(work, struct pil_device, proxy.work); + pil->desc->ops->proxy_unvote(pil->desc); + wake_unlock(&pil->wlock); +} + +static int pil_proxy_vote(struct pil_device *pil) +{ + if (pil->desc->ops->proxy_vote) { + wake_lock(&pil->wlock); + return pil->desc->ops->proxy_vote(pil->desc); + } + return 0; +} + +static void pil_proxy_unvote(struct pil_device *pil, unsigned long timeout) +{ + if (pil->desc->ops->proxy_unvote) + schedule_delayed_work(&pil->proxy, msecs_to_jiffies(timeout)); +} + +#define IOMAP_SIZE SZ_4M + +static int load_segment(const struct elf32_phdr *phdr, unsigned num, + struct pil_device *pil) +{ + int ret = 0, count, paddr; + char fw_name[30]; + const struct firmware *fw = NULL; + const u8 *data; + + if (memblock_overlaps_memory(phdr->p_paddr, phdr->p_memsz)) { + dev_err(&pil->dev, "%s: kernel memory would be overwritten " + "[%#08lx, %#08lx)\n", pil->desc->name, + (unsigned long)phdr->p_paddr, + (unsigned long)(phdr->p_paddr + phdr->p_memsz)); + return -EPERM; + } + + if (phdr->p_filesz) { + snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", + pil->desc->name, num); + ret = request_firmware(&fw, fw_name, &pil->dev); + if (ret) { + dev_err(&pil->dev, "%s: Failed to locate blob %s\n", + pil->desc->name, fw_name); + return ret; + } + + if (fw->size != phdr->p_filesz) { + dev_err(&pil->dev, "%s: Blob size %u doesn't match " + "%u\n", pil->desc->name, fw->size, + phdr->p_filesz); + ret = -EPERM; + goto release_fw; + } + } + + /* Load the segment into memory */ + count = phdr->p_filesz; + paddr = phdr->p_paddr; + data = fw ? fw->data : NULL; + while (count > 0) { + int size; + u8 __iomem *buf; + + size = min_t(size_t, IOMAP_SIZE, count); + buf = ioremap(paddr, size); + if (!buf) { + dev_err(&pil->dev, "%s: Failed to map memory\n", + pil->desc->name); + ret = -ENOMEM; + goto release_fw; + } + memcpy(buf, data, size); + iounmap(buf); + + count -= size; + paddr += size; + data += size; + } + + /* Zero out trailing memory */ + count = phdr->p_memsz - phdr->p_filesz; + while (count > 0) { + int size; + u8 __iomem *buf; + + size = min_t(size_t, IOMAP_SIZE, count); + buf = ioremap(paddr, size); + if (!buf) { + dev_err(&pil->dev, "%s: Failed to map memory\n", + pil->desc->name); + ret = -ENOMEM; + goto release_fw; + } + memset(buf, 0, size); + iounmap(buf); + + count -= size; + paddr += size; + } + + if (pil->desc->ops->verify_blob) { + ret = pil->desc->ops->verify_blob(pil->desc, phdr->p_paddr, + phdr->p_memsz); + if (ret) + dev_err(&pil->dev, "%s: Blob%u failed verification\n", + pil->desc->name, num); + } + +release_fw: + release_firmware(fw); + return ret; +} + +#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24)) + +static int segment_is_loadable(const struct elf32_phdr *p) +{ + return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags); +} + +/* Sychronize request_firmware() with suspend */ +static DECLARE_RWSEM(pil_pm_rwsem); + +static int load_image(struct pil_device *pil) +{ + int i, ret; + char fw_name[30]; + struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const struct firmware *fw; + unsigned long proxy_timeout = pil->desc->proxy_timeout; + + down_read(&pil_pm_rwsem); + snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name); + ret = request_firmware(&fw, fw_name, &pil->dev); + if (ret) { + dev_err(&pil->dev, "%s: Failed to locate %s\n", + pil->desc->name, fw_name); + goto out; + } + + if (fw->size < sizeof(*ehdr)) { + dev_err(&pil->dev, "%s: Not big enough to be an elf header\n", + pil->desc->name); + ret = -EIO; + goto release_fw; + } + + ehdr = (struct elf32_hdr *)fw->data; + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(&pil->dev, "%s: Not an elf header\n", pil->desc->name); + ret = -EIO; + goto release_fw; + } + + if (ehdr->e_phnum == 0) { + dev_err(&pil->dev, "%s: No loadable segments\n", + pil->desc->name); + ret = -EIO; + goto release_fw; + } + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw->size) { + dev_err(&pil->dev, "%s: Program headers not within mdt\n", + pil->desc->name); + ret = -EIO; + goto release_fw; + } + + ret = pil->desc->ops->init_image(pil->desc, fw->data, fw->size); + if (ret) { + dev_err(&pil->dev, "%s: Invalid firmware metadata\n", + pil->desc->name); + goto release_fw; + } + + phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr)); + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (!segment_is_loadable(phdr)) + continue; + + ret = load_segment(phdr, i, pil); + if (ret) { + dev_err(&pil->dev, "%s: Failed to load segment %d\n", + pil->desc->name, i); + goto release_fw; + } + } + + ret = pil_proxy_vote(pil); + if (ret) { + dev_err(&pil->dev, "%s: Failed to proxy vote\n", + pil->desc->name); + goto release_fw; + } + + ret = pil->desc->ops->auth_and_reset(pil->desc); + if (ret) { + dev_err(&pil->dev, "%s: Failed to bring out of reset\n", + pil->desc->name); + proxy_timeout = 0; /* Remove proxy vote immediately on error */ + goto err_boot; + } + dev_info(&pil->dev, "%s: Brought out of reset\n", pil->desc->name); +err_boot: + pil_proxy_unvote(pil, proxy_timeout); +release_fw: + release_firmware(fw); +out: + up_read(&pil_pm_rwsem); + return ret; +} + +static void pil_set_state(struct pil_device *pil, enum pil_state state) +{ + if (pil->state != state) { + pil->state = state; + sysfs_notify(&pil->dev.kobj, NULL, "state"); + } +} + +/** + * pil_get() - Load a peripheral into memory and take it out of reset + * @name: pointer to a string containing the name of the peripheral to load + * + * This function returns a pointer if it succeeds. If an error occurs an + * ERR_PTR is returned. + * + * If PIL is not enabled in the kernel, the value %NULL will be returned. + */ +void *pil_get(const char *name) +{ + int ret; + struct pil_device *pil; + struct pil_device *pil_d; + void *retval; + + if (!name) + return NULL; + + pil = retval = find_peripheral(name); + if (!pil) + return ERR_PTR(-ENODEV); + if (!try_module_get(pil->owner)) { + put_device(&pil->dev); + return ERR_PTR(-ENODEV); + } + + pil_d = pil_get(pil->desc->depends_on); + if (IS_ERR(pil_d)) { + retval = pil_d; + goto err_depends; + } + + mutex_lock(&pil->lock); + if (!pil->count) { + ret = load_image(pil); + if (ret) { + retval = ERR_PTR(ret); + goto err_load; + } + } + pil->count++; + pil_set_state(pil, PIL_ONLINE); + mutex_unlock(&pil->lock); +out: + return retval; +err_load: + mutex_unlock(&pil->lock); + pil_put(pil_d); +err_depends: + put_device(&pil->dev); + module_put(pil->owner); + goto out; +} +EXPORT_SYMBOL(pil_get); + +static void pil_shutdown(struct pil_device *pil) +{ + pil->desc->ops->shutdown(pil->desc); + flush_delayed_work(&pil->proxy); + pil_set_state(pil, PIL_OFFLINE); +} + +/** + * pil_put() - Inform PIL the peripheral no longer needs to be active + * @peripheral_handle: pointer from a previous call to pil_get() + * + * This doesn't imply that a peripheral is shutdown or in reset since another + * driver could be using the peripheral. + */ +void pil_put(void *peripheral_handle) +{ + struct pil_device *pil_d, *pil = peripheral_handle; + + if (IS_ERR_OR_NULL(pil)) + return; + + mutex_lock(&pil->lock); + if (WARN(!pil->count, "%s: %s: Reference count mismatch\n", + pil->desc->name, __func__)) + goto err_out; + if (!--pil->count) + pil_shutdown(pil); + mutex_unlock(&pil->lock); + + pil_d = find_peripheral(pil->desc->depends_on); + module_put(pil->owner); + if (pil_d) { + pil_put(pil_d); + put_device(&pil_d->dev); + } + put_device(&pil->dev); + return; +err_out: + mutex_unlock(&pil->lock); + return; +} +EXPORT_SYMBOL(pil_put); + +void pil_force_shutdown(const char *name) +{ + struct pil_device *pil; + + pil = find_peripheral(name); + if (!pil) { + pr_err("%s: Couldn't find %s\n", __func__, name); + return; + } + + mutex_lock(&pil->lock); + if (!WARN(!pil->count, "%s: %s: Reference count mismatch\n", + pil->desc->name, __func__)) + pil_shutdown(pil); + mutex_unlock(&pil->lock); + + put_device(&pil->dev); +} +EXPORT_SYMBOL(pil_force_shutdown); + +int pil_force_boot(const char *name) +{ + int ret = -EINVAL; + struct pil_device *pil; + + pil = find_peripheral(name); + if (!pil) { + pr_err("%s: Couldn't find %s\n", __func__, name); + return -EINVAL; + } + + mutex_lock(&pil->lock); + if (!WARN(!pil->count, "%s: %s: Reference count mismatch\n", + pil->desc->name, __func__)) + ret = load_image(pil); + if (!ret) + pil_set_state(pil, PIL_ONLINE); + mutex_unlock(&pil->lock); + put_device(&pil->dev); + + return ret; +} +EXPORT_SYMBOL(pil_force_boot); + +#ifdef CONFIG_DEBUG_FS +static int msm_pil_debugfs_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + int r; + char buf[40]; + struct pil_device *pil = filp->private_data; + + mutex_lock(&pil->lock); + r = snprintf(buf, sizeof(buf), "%d\n", pil->count); + mutex_unlock(&pil->lock); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t msm_pil_debugfs_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct pil_device *pil = filp->private_data; + char buf[4]; + + if (cnt > sizeof(buf)) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + + if (!strncmp(buf, "get", 3)) { + if (IS_ERR(pil_get(pil->desc->name))) + return -EIO; + } else if (!strncmp(buf, "put", 3)) + pil_put(pil); + else + return -EINVAL; + + return cnt; +} + +static const struct file_operations msm_pil_debugfs_fops = { + .open = msm_pil_debugfs_open, + .read = msm_pil_debugfs_read, + .write = msm_pil_debugfs_write, +}; + +static struct dentry *pil_base_dir; + +static int __init msm_pil_debugfs_init(void) +{ + pil_base_dir = debugfs_create_dir("pil", NULL); + if (!pil_base_dir) { + pil_base_dir = NULL; + return -ENOMEM; + } + + return 0; +} + +static void __exit msm_pil_debugfs_exit(void) +{ + debugfs_remove_recursive(pil_base_dir); +} + +static int msm_pil_debugfs_add(struct pil_device *pil) +{ + if (!pil_base_dir) + return -ENOMEM; + + pil->dentry = debugfs_create_file(pil->desc->name, S_IRUGO | S_IWUSR, + pil_base_dir, pil, &msm_pil_debugfs_fops); + return !pil->dentry ? -ENOMEM : 0; +} + +static void msm_pil_debugfs_remove(struct pil_device *pil) +{ + debugfs_remove(pil->dentry); +} +#else +static int __init msm_pil_debugfs_init(void) { return 0; }; +static void __exit msm_pil_debugfs_exit(void) { return 0; }; +static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; } +static void msm_pil_debugfs_remove(struct pil_device *pil) { } +#endif + +static int __msm_pil_shutdown(struct device *dev, void *data) +{ + pil_shutdown(to_pil_device(dev)); + return 0; +} + +static int msm_pil_shutdown_at_boot(void) +{ + return bus_for_each_dev(&pil_bus_type, NULL, NULL, __msm_pil_shutdown); +} +late_initcall(msm_pil_shutdown_at_boot); + +static void pil_device_release(struct device *dev) +{ + struct pil_device *pil = to_pil_device(dev); + wake_lock_destroy(&pil->wlock); + mutex_destroy(&pil->lock); + kfree(pil); +} + +struct pil_device *msm_pil_register(struct pil_desc *desc) +{ + int err; + static atomic_t pil_count = ATOMIC_INIT(-1); + struct pil_device *pil; + + /* Ignore users who don't make any sense */ + if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote, + "invalid proxy voting. ignoring\n")) + ((struct pil_reset_ops *)desc->ops)->proxy_unvote = NULL; + + WARN(desc->ops->proxy_unvote && !desc->proxy_timeout, + "A proxy timeout of 0 ms was specified for %s. Specify one in " + "desc->proxy_timeout.\n", desc->name); + + pil = kzalloc(sizeof(*pil), GFP_KERNEL); + if (!pil) + return ERR_PTR(-ENOMEM); + + mutex_init(&pil->lock); + pil->desc = desc; + pil->owner = desc->owner; + pil->dev.parent = desc->dev; + pil->dev.bus = &pil_bus_type; + pil->dev.release = pil_device_release; + + snprintf(pil->wake_name, sizeof(pil->wake_name), "pil-%s", desc->name); + wake_lock_init(&pil->wlock, WAKE_LOCK_SUSPEND, pil->wake_name); + INIT_DELAYED_WORK(&pil->proxy, pil_proxy_work); + + dev_set_name(&pil->dev, "pil%d", atomic_inc_return(&pil_count)); + err = device_register(&pil->dev); + if (err) { + put_device(&pil->dev); + wake_lock_destroy(&pil->wlock); + mutex_destroy(&pil->lock); + kfree(pil); + return ERR_PTR(err); + } + + err = msm_pil_debugfs_add(pil); + if (err) { + device_unregister(&pil->dev); + return ERR_PTR(err); + } + + return pil; +} +EXPORT_SYMBOL(msm_pil_register); + +void msm_pil_unregister(struct pil_device *pil) +{ + if (IS_ERR_OR_NULL(pil)) + return; + + if (get_device(&pil->dev)) { + mutex_lock(&pil->lock); + WARN_ON(pil->count); + flush_delayed_work_sync(&pil->proxy); + msm_pil_debugfs_remove(pil); + device_unregister(&pil->dev); + mutex_unlock(&pil->lock); + put_device(&pil->dev); + } +} +EXPORT_SYMBOL(msm_pil_unregister); + +static int pil_pm_notify(struct notifier_block *b, unsigned long event, void *p) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + down_write(&pil_pm_rwsem); + break; + case PM_POST_SUSPEND: + up_write(&pil_pm_rwsem); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block pil_pm_notifier = { + .notifier_call = pil_pm_notify, +}; + +static int __init msm_pil_init(void) +{ + int ret = msm_pil_debugfs_init(); + if (ret) + return ret; + register_pm_notifier(&pil_pm_notifier); + return bus_register(&pil_bus_type); +} +subsys_initcall(msm_pil_init); + +static void __exit msm_pil_exit(void) +{ + bus_unregister(&pil_bus_type); + unregister_pm_notifier(&pil_pm_notifier); + msm_pil_debugfs_exit(); +} +module_exit(msm_pil_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset"); diff --git a/arch/arm/mach-msm/peripheral-loader.h b/arch/arm/mach-msm/peripheral-loader.h new file mode 100644 index 00000000000..e3b250b089f --- /dev/null +++ b/arch/arm/mach-msm/peripheral-loader.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_PERIPHERAL_LOADER_H +#define __MSM_PERIPHERAL_LOADER_H + +struct device; +struct module; + +/** + * struct pil_desc - PIL descriptor + * @name: string used for pil_get() + * @depends_on: booted before this peripheral + * @dev: parent device + * @ops: callback functions + * @owner: module the descriptor belongs to + * @proxy_timeout: delay in ms until proxy vote is removed + */ +struct pil_desc { + const char *name; + const char *depends_on; + struct device *dev; + const struct pil_reset_ops *ops; + struct module *owner; + unsigned long proxy_timeout; +}; + +/** + * struct pil_reset_ops - PIL operations + * @init_image: prepare an image for authentication + * @verify_blob: authenticate a program segment, called once for each loadable + * program segment (optional) + * @proxy_vote: make proxy votes before auth_and_reset (optional) + * @auth_and_reset: boot the processor + * @proxy_unvote: remove any proxy votes (optional) + * @shutdown: shutdown the processor + */ +struct pil_reset_ops { + int (*init_image)(struct pil_desc *pil, const u8 *metadata, + size_t size); + int (*verify_blob)(struct pil_desc *pil, u32 phy_addr, size_t size); + int (*proxy_vote)(struct pil_desc *pil); + int (*auth_and_reset)(struct pil_desc *pil); + void (*proxy_unvote)(struct pil_desc *pil); + int (*shutdown)(struct pil_desc *pil); +}; + +struct pil_device; + +extern struct pil_device *msm_pil_register(struct pil_desc *desc); +extern void msm_pil_unregister(struct pil_device *pil); + +#endif diff --git a/arch/arm/mach-msm/pil-dsps.c b/arch/arm/mach-msm/pil-dsps.c new file mode 100644 index 00000000000..81f5330fe81 --- /dev/null +++ b/arch/arm/mach-msm/pil-dsps.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +#define PPSS_RESET (MSM_CLK_CTL_BASE + 0x2594) +#define PPSS_RESET_PROC_RESET 0x2 +#define PPSS_RESET_RESET 0x1 +#define PPSS_PROC_CLK_CTL (MSM_CLK_CTL_BASE + 0x2588) +#define CLK_BRANCH_ENA 0x10 +#define PPSS_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2580) +#define CLK_HALT_DFAB_STATE (MSM_CLK_CTL_BASE + 0x2FC8) + +static int init_image_dsps(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + /* Bring memory and bus interface out of reset */ + writel_relaxed(PPSS_RESET_PROC_RESET, PPSS_RESET); + writel_relaxed(CLK_BRANCH_ENA, PPSS_HCLK_CTL); + mb(); + return 0; +} + +static int reset_dsps(struct pil_desc *pil) +{ + writel_relaxed(CLK_BRANCH_ENA, PPSS_PROC_CLK_CTL); + while (readl_relaxed(CLK_HALT_DFAB_STATE) & BIT(18)) + cpu_relax(); + /* Bring DSPS out of reset */ + writel_relaxed(0x0, PPSS_RESET); + return 0; +} + +static int shutdown_dsps(struct pil_desc *pil) +{ + writel_relaxed(PPSS_RESET_PROC_RESET | PPSS_RESET_RESET, PPSS_RESET); + usleep_range(1000, 2000); + writel_relaxed(PPSS_RESET_PROC_RESET, PPSS_RESET); + writel_relaxed(0x0, PPSS_PROC_CLK_CTL); + return 0; +} + +struct pil_reset_ops pil_dsps_ops = { + .init_image = init_image_dsps, + .auth_and_reset = reset_dsps, + .shutdown = shutdown_dsps, +}; + +static int init_image_dsps_trusted(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + return pas_init_image(PAS_DSPS, metadata, size); +} + +static int reset_dsps_trusted(struct pil_desc *pil) +{ + return pas_auth_and_reset(PAS_DSPS); +} + +static int shutdown_dsps_trusted(struct pil_desc *pil) +{ + return pas_shutdown(PAS_DSPS); +} + +struct pil_reset_ops pil_dsps_ops_trusted = { + .init_image = init_image_dsps_trusted, + .auth_and_reset = reset_dsps_trusted, + .shutdown = shutdown_dsps_trusted, +}; + +static int __devinit pil_dsps_driver_probe(struct platform_device *pdev) +{ + struct pil_desc *desc; + struct pil_device *pil; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + desc->name = pdev->dev.platform_data; + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + if (pas_supported(PAS_DSPS) > 0) { + desc->ops = &pil_dsps_ops_trusted; + dev_info(&pdev->dev, "using secure boot\n"); + } else { + desc->ops = &pil_dsps_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + } + pil = msm_pil_register(desc); + if (IS_ERR(pil)) + return PTR_ERR(pil); + platform_set_drvdata(pdev, pil); + return 0; +} + +static int __devexit pil_dsps_driver_exit(struct platform_device *pdev) +{ + struct pil_device *pil = platform_get_drvdata(pdev); + msm_pil_unregister(pil); + return 0; +} + +static struct platform_driver pil_dsps_driver = { + .probe = pil_dsps_driver_probe, + .remove = __devexit_p(pil_dsps_driver_exit), + .driver = { + .name = "pil_dsps", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_dsps_init(void) +{ + return platform_driver_register(&pil_dsps_driver); +} +module_init(pil_dsps_init); + +static void __exit pil_dsps_exit(void) +{ + platform_driver_unregister(&pil_dsps_driver); +} +module_exit(pil_dsps_exit); + +MODULE_DESCRIPTION("Support for booting sensors (DSPS) images"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c new file mode 100644 index 00000000000..dc7baa1663b --- /dev/null +++ b/arch/arm/mach-msm/pil-gss.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +#define GSS_CSR_AHB_CLK_SEL 0x0 +#define GSS_CSR_RESET 0x4 +#define GSS_CSR_CLK_BLK_CONFIG 0x8 +#define GSS_CSR_CLK_ENABLE 0xC +#define GSS_CSR_BOOT_REMAP 0x14 +#define GSS_CSR_POWER_UP_DOWN 0x18 +#define GSS_CSR_CFG_HID 0x2C + +#define GSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60) +#define GSS_RESET (MSM_CLK_CTL_BASE + 0x2C64) +#define GSS_CLAMP_ENA (MSM_CLK_CTL_BASE + 0x2C68) +#define GSS_CXO_SRC_CTL (MSM_CLK_CTL_BASE + 0x2C74) + +#define PLL5_STATUS (MSM_CLK_CTL_BASE + 0x30F8) +#define PLL_ENA_GSS (MSM_CLK_CTL_BASE + 0x3480) + +#define PLL5_VOTE BIT(5) +#define PLL_STATUS BIT(16) +#define REMAP_ENABLE BIT(16) +#define A5_POWER_STATUS BIT(4) +#define A5_POWER_ENA BIT(0) +#define NAV_POWER_ENA BIT(1) +#define XO_CLK_BRANCH_ENA BIT(0) +#define SLP_CLK_BRANCH_ENA BIT(4) +#define A5_RESET BIT(0) + +struct gss_data { + void __iomem *base; + void __iomem *qgic2_base; + unsigned long start_addr; + struct clk *xo; + struct pil_device *pil; +}; + +static int pil_gss_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct gss_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} + +static int make_gss_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct gss_data *drv = dev_get_drvdata(pil->dev); + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + return ret; + } + return 0; +} + +static void remove_gss_proxy_votes(struct pil_desc *pil) +{ + struct gss_data *drv = dev_get_drvdata(pil->dev); + clk_disable_unprepare(drv->xo); +} + +static void gss_init(struct gss_data *drv) +{ + void __iomem *base = drv->base; + + /* Supply clocks to GSS. */ + writel_relaxed(XO_CLK_BRANCH_ENA, GSS_CXO_SRC_CTL); + writel_relaxed(SLP_CLK_BRANCH_ENA, GSS_SLP_CLK_CTL); + + /* Deassert GSS reset and clamps. */ + writel_relaxed(0x0, GSS_RESET); + writel_relaxed(0x0, GSS_CLAMP_ENA); + mb(); + + /* + * Configure clock source and dividers for 288MHz core, 144MHz AXI and + * 72MHz AHB, all derived from the 288MHz PLL. + */ + writel_relaxed(0x341, base + GSS_CSR_CLK_BLK_CONFIG); + writel_relaxed(0x1, base + GSS_CSR_AHB_CLK_SEL); + + /* Assert all GSS resets. */ + writel_relaxed(0x7F, base + GSS_CSR_RESET); + + /* Enable all bus clocks and wait for resets to propagate. */ + writel_relaxed(0x1F, base + GSS_CSR_CLK_ENABLE); + mb(); + udelay(1); + + /* Release subsystem from reset, but leave A5 in reset. */ + writel_relaxed(A5_RESET, base + GSS_CSR_RESET); +} + +static void cfg_qgic2_bus_access(void *data) +{ + struct gss_data *drv = data; + int i; + + /* + * Apply a 8064 v1.0 workaround to configure QGIC bus access. + * This must be done from Krait 0 to configure the Master ID + * correctly. + */ + writel_relaxed(0x2, drv->base + GSS_CSR_CFG_HID); + for (i = 0; i <= 3; i++) + readl_relaxed(drv->qgic2_base); +} + +static int pil_gss_shutdown(struct pil_desc *pil) +{ + struct gss_data *drv = dev_get_drvdata(pil->dev); + void __iomem *base = drv->base; + u32 regval; + int ret; + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + return ret; + } + + /* Make sure bus port is halted. */ + msm_bus_axi_porthalt(MSM_BUS_MASTER_GSS_NAV); + + /* + * Vote PLL on in GSS's voting register and wait for it to enable. + * The PLL must be enable to switch the GFMUX to a low-power source. + */ + writel_relaxed(PLL5_VOTE, PLL_ENA_GSS); + while ((readl_relaxed(PLL5_STATUS) & PLL_STATUS) == 0) + cpu_relax(); + + /* Perform one-time GSS initialization. */ + gss_init(drv); + + /* Assert A5 reset. */ + regval = readl_relaxed(base + GSS_CSR_RESET); + regval |= A5_RESET; + writel_relaxed(regval, base + GSS_CSR_RESET); + + /* Power down A5 and NAV. */ + regval = readl_relaxed(base + GSS_CSR_POWER_UP_DOWN); + regval &= ~(A5_POWER_ENA|NAV_POWER_ENA); + writel_relaxed(regval, base + GSS_CSR_POWER_UP_DOWN); + + /* Select XO clock source and increase dividers to save power. */ + regval = readl_relaxed(base + GSS_CSR_CLK_BLK_CONFIG); + regval |= 0x3FF; + writel_relaxed(regval, base + GSS_CSR_CLK_BLK_CONFIG); + + /* Disable bus clocks. */ + writel_relaxed(0x1F, base + GSS_CSR_CLK_ENABLE); + + /* Clear GSS PLL votes. */ + writel_relaxed(0, PLL_ENA_GSS); + mb(); + + clk_disable_unprepare(drv->xo); + + return 0; +} + +static int pil_gss_reset(struct pil_desc *pil) +{ + struct gss_data *drv = dev_get_drvdata(pil->dev); + void __iomem *base = drv->base; + unsigned long start_addr = drv->start_addr; + int ret; + + /* Unhalt bus port. */ + ret = msm_bus_axi_portunhalt(MSM_BUS_MASTER_GSS_NAV); + if (ret) { + dev_err(pil->dev, "Failed to unhalt bus port\n"); + return ret; + } + + /* Vote PLL on in GSS's voting register and wait for it to enable. */ + writel_relaxed(PLL5_VOTE, PLL_ENA_GSS); + while ((readl_relaxed(PLL5_STATUS) & PLL_STATUS) == 0) + cpu_relax(); + + /* Perform GSS initialization. */ + gss_init(drv); + + /* Configure boot address and enable remap. */ + writel_relaxed(REMAP_ENABLE | (start_addr >> 16), + base + GSS_CSR_BOOT_REMAP); + + /* Power up A5 core. */ + writel_relaxed(A5_POWER_ENA, base + GSS_CSR_POWER_UP_DOWN); + while (!(readl_relaxed(base + GSS_CSR_POWER_UP_DOWN) & A5_POWER_STATUS)) + cpu_relax(); + + if (cpu_is_apq8064() && + ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) && + (SOCINFO_VERSION_MINOR(socinfo_get_version()) == 0))) { + ret = smp_call_function_single(0, cfg_qgic2_bus_access, drv, 1); + if (ret) { + pr_err("Failed to configure QGIC2 bus access\n"); + pil_gss_shutdown(pil); + return ret; + } + } + + /* Release A5 from reset. */ + writel_relaxed(0x0, base + GSS_CSR_RESET); + + return 0; +} + +static struct pil_reset_ops pil_gss_ops = { + .init_image = pil_gss_init_image, + .auth_and_reset = pil_gss_reset, + .shutdown = pil_gss_shutdown, + .proxy_vote = make_gss_proxy_votes, + .proxy_unvote = remove_gss_proxy_votes, +}; + +static int pil_gss_init_image_trusted(struct pil_desc *pil, + const u8 *metadata, size_t size) +{ + return pas_init_image(PAS_GSS, metadata, size); +} + +static int pil_gss_shutdown_trusted(struct pil_desc *pil) +{ + struct gss_data *drv = dev_get_drvdata(pil->dev); + int ret; + + /* + * CXO is used in the secure shutdown code to configure the processor + * for low power mode. + */ + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + return ret; + } + + msm_bus_axi_porthalt(MSM_BUS_MASTER_GSS_NAV); + ret = pas_shutdown(PAS_GSS); + clk_disable_unprepare(drv->xo); + + return ret; +} + +static int pil_gss_reset_trusted(struct pil_desc *pil) +{ + int err; + + err = msm_bus_axi_portunhalt(MSM_BUS_MASTER_GSS_NAV); + if (err) { + dev_err(pil->dev, "Failed to unhalt bus port\n"); + goto out; + } + + err = pas_auth_and_reset(PAS_GSS); + if (err) + goto halt_port; + + return 0; + +halt_port: + msm_bus_axi_porthalt(MSM_BUS_MASTER_GSS_NAV); +out: + return err; +} + +static struct pil_reset_ops pil_gss_ops_trusted = { + .init_image = pil_gss_init_image_trusted, + .auth_and_reset = pil_gss_reset_trusted, + .shutdown = pil_gss_shutdown_trusted, + .proxy_vote = make_gss_proxy_votes, + .proxy_unvote = remove_gss_proxy_votes, +}; + +static int __devinit pil_gss_probe(struct platform_device *pdev) +{ + struct gss_data *drv; + struct resource *res; + struct pil_desc *desc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!drv->base) + return -ENOMEM; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -EINVAL; + + drv->qgic2_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->qgic2_base) + return -ENOMEM; + + drv->xo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->xo)) + return PTR_ERR(drv->xo); + + desc->name = "gss"; + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + desc->proxy_timeout = 10000; + + if (pas_supported(PAS_GSS) > 0) { + desc->ops = &pil_gss_ops_trusted; + dev_info(&pdev->dev, "using secure boot\n"); + } else { + desc->ops = &pil_gss_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + } + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) { + return PTR_ERR(drv->pil); + } + return 0; +} + +static int __devexit pil_gss_remove(struct platform_device *pdev) +{ + struct gss_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct platform_driver pil_gss_driver = { + .probe = pil_gss_probe, + .remove = __devexit_p(pil_gss_remove), + .driver = { + .name = "pil_gss", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_gss_init(void) +{ + return platform_driver_register(&pil_gss_driver); +} +module_init(pil_gss_init); + +static void __exit pil_gss_exit(void) +{ + platform_driver_unregister(&pil_gss_driver); +} +module_exit(pil_gss_exit); + +MODULE_DESCRIPTION("Support for booting the GSS processor"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-mba.c b/arch/arm/mach-msm/pil-mba.c new file mode 100644 index 00000000000..7405ab96aba --- /dev/null +++ b/arch/arm/mach-msm/pil-mba.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" + +#define RMB_MBA_COMMAND 0x08 +#define RMB_MBA_STATUS 0x0C +#define RMB_PMI_META_DATA 0x10 +#define RMB_PMI_CODE_START 0x14 +#define RMB_PMI_CODE_LENGTH 0x18 + +#define CMD_META_DATA_READY 0x1 +#define CMD_LOAD_READY 0x2 + +#define STATUS_META_DATA_AUTH_SUCCESS 0x3 +#define STATUS_AUTH_COMPLETE 0x4 +#define STATUS_ERROR_MASK BIT(31) + +#define AUTH_TIMEOUT_US 10000000 +#define PROXY_TIMEOUT_MS 10000 +#define POLL_INTERVAL_US 50 + +struct mba_data { + void __iomem *reg_base; + void __iomem *metadata_base; + unsigned long metadata_phys; + struct pil_device *pil; + struct clk *xo; + u32 img_length; +}; + +static int pil_mba_make_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct mba_data *drv = dev_get_drvdata(pil->dev); + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + return ret; + } + return 0; +} + +static void pil_mba_remove_proxy_votes(struct pil_desc *pil) +{ + struct mba_data *drv = dev_get_drvdata(pil->dev); + clk_disable_unprepare(drv->xo); +} + +static int pil_mba_init_image(struct pil_desc *pil, + const u8 *metadata, size_t size) +{ + struct mba_data *drv = dev_get_drvdata(pil->dev); + u32 status; + int ret; + + /* Copy metadata to assigned shared buffer location */ + memcpy(drv->metadata_base, metadata, size); + + /* Initialize length counter to 0 */ + writel_relaxed(0, drv->reg_base + RMB_PMI_CODE_LENGTH); + drv->img_length = 0; + + /* Pass address of meta-data to the MBA and perform authentication */ + writel_relaxed(drv->metadata_phys, drv->reg_base + RMB_PMI_META_DATA); + writel_relaxed(CMD_META_DATA_READY, drv->reg_base + RMB_MBA_COMMAND); + ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status, + status == STATUS_META_DATA_AUTH_SUCCESS, + POLL_INTERVAL_US, AUTH_TIMEOUT_US); + if (ret) + dev_err(pil->dev, "MBA authentication timed out\n"); + + return ret; +} + +static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr, + size_t size) +{ + struct mba_data *drv = dev_get_drvdata(pil->dev); + + /* Begin image authentication */ + if (drv->img_length == 0) { + writel_relaxed(phy_addr, drv->reg_base + RMB_PMI_CODE_START); + writel_relaxed(CMD_LOAD_READY, drv->reg_base + RMB_MBA_COMMAND); + } + /* Increment length counter */ + drv->img_length += size; + writel_relaxed(drv->img_length, drv->reg_base + RMB_PMI_CODE_LENGTH); + + return readl_relaxed(drv->reg_base + RMB_MBA_STATUS) + & STATUS_ERROR_MASK; +} + +static int pil_mba_auth(struct pil_desc *pil) +{ + struct mba_data *drv = dev_get_drvdata(pil->dev); + int ret; + u32 status; + + /* Wait for all segments to be authenticated or an error to occur */ + ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status, + status == STATUS_AUTH_COMPLETE || + status & STATUS_ERROR_MASK, + 50, AUTH_TIMEOUT_US); + if (ret) + return ret; + + if (status & STATUS_ERROR_MASK) + return -EINVAL; + + return 0; +} + +static int pil_mba_shutdown(struct pil_desc *pil) +{ + return 0; +} + +static struct pil_reset_ops pil_mba_ops = { + .init_image = pil_mba_init_image, + .proxy_vote = pil_mba_make_proxy_votes, + .proxy_unvote = pil_mba_remove_proxy_votes, + .verify_blob = pil_mba_verify_blob, + .auth_and_reset = pil_mba_auth, + .shutdown = pil_mba_shutdown, +}; + +static int __devinit pil_mba_driver_probe(struct platform_device *pdev) +{ + struct mba_data *drv; + struct resource *res; + struct pil_desc *desc; + int ret; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + drv->reg_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->reg_base) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + drv->metadata_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->metadata_base) + return -ENOMEM; + drv->metadata_phys = res->start; + } + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name", + &desc->name); + if (ret) + return ret; + + of_property_read_string(pdev->dev.of_node, "qcom,depends-on", + &desc->depends_on); + + drv->xo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->xo)) + return PTR_ERR(drv->xo); + + desc->dev = &pdev->dev; + desc->ops = &pil_mba_ops; + desc->owner = THIS_MODULE; + desc->proxy_timeout = PROXY_TIMEOUT_MS; + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) + return PTR_ERR(drv->pil); + + return 0; +} + +static int __devexit pil_mba_driver_exit(struct platform_device *pdev) +{ + return 0; +} + +static struct of_device_id mba_match_table[] = { + { .compatible = "qcom,pil-mba" }, + {} +}; + +struct platform_driver pil_mba_driver = { + .probe = pil_mba_driver_probe, + .remove = __devexit_p(pil_mba_driver_exit), + .driver = { + .name = "pil-mba", + .of_match_table = mba_match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init pil_mba_init(void) +{ + return platform_driver_register(&pil_mba_driver); +} +module_init(pil_mba_init); + +static void __exit pil_mba_exit(void) +{ + platform_driver_unregister(&pil_mba_driver); +} +module_exit(pil_mba_exit); + +MODULE_DESCRIPTION("Support for modem boot using the Modem Boot Authenticator"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-modem.c b/arch/arm/mach-msm/pil-modem.c new file mode 100644 index 00000000000..83444965285 --- /dev/null +++ b/arch/arm/mach-msm/pil-modem.c @@ -0,0 +1,311 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +#define MARM_BOOT_CONTROL 0x0010 +#define MARM_RESET (MSM_CLK_CTL_BASE + 0x2BD4) +#define MAHB0_SFAB_PORT_RESET (MSM_CLK_CTL_BASE + 0x2304) +#define MARM_CLK_BRANCH_ENA_VOTE (MSM_CLK_CTL_BASE + 0x3000) +#define MARM_CLK_SRC0_NS (MSM_CLK_CTL_BASE + 0x2BC0) +#define MARM_CLK_SRC1_NS (MSM_CLK_CTL_BASE + 0x2BC4) +#define MARM_CLK_SRC_CTL (MSM_CLK_CTL_BASE + 0x2BC8) +#define MARM_CLK_CTL (MSM_CLK_CTL_BASE + 0x2BCC) +#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00) +#define MSS_MODEM_CXO_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C44) +#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60) +#define MSS_MARM_SYS_REF_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C64) +#define MAHB0_CLK_CTL (MSM_CLK_CTL_BASE + 0x2300) +#define MAHB1_CLK_CTL (MSM_CLK_CTL_BASE + 0x2BE4) +#define MAHB2_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C20) +#define MAHB1_NS (MSM_CLK_CTL_BASE + 0x2BE0) +#define MARM_CLK_FS (MSM_CLK_CTL_BASE + 0x2BD0) +#define MAHB2_CLK_FS (MSM_CLK_CTL_BASE + 0x2C24) +#define PLL_ENA_MARM (MSM_CLK_CTL_BASE + 0x3500) +#define PLL8_STATUS (MSM_CLK_CTL_BASE + 0x3158) +#define CLK_HALT_MSS_SMPSS_MISC_STATE (MSM_CLK_CTL_BASE + 0x2FDC) + +struct modem_data { + void __iomem *base; + unsigned long start_addr; + struct pil_device *pil; + struct clk *xo; +}; + +static int make_modem_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct modem_data *drv = dev_get_drvdata(pil->dev); + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + return ret; + } + return 0; +} + +static void remove_modem_proxy_votes(struct pil_desc *pil) +{ + struct modem_data *drv = dev_get_drvdata(pil->dev); + clk_disable_unprepare(drv->xo); +} + +static int modem_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct modem_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} + +static int modem_reset(struct pil_desc *pil) +{ + u32 reg; + const struct modem_data *drv = dev_get_drvdata(pil->dev); + + /* Put modem AHB0,1,2 clocks into reset */ + writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET); + writel_relaxed(BIT(7), MAHB1_CLK_CTL); + writel_relaxed(BIT(7), MAHB2_CLK_CTL); + + /* Vote for pll8 on behalf of the modem */ + reg = readl_relaxed(PLL_ENA_MARM); + reg |= BIT(8); + writel_relaxed(reg, PLL_ENA_MARM); + + /* Wait for PLL8 to enable */ + while (!(readl_relaxed(PLL8_STATUS) & BIT(16))) + cpu_relax(); + + /* Set MAHB1 divider to Div-5 to run MAHB1,2 and sfab at 79.8 Mhz*/ + writel_relaxed(0x4, MAHB1_NS); + + /* Vote for modem AHB1 and 2 clocks to be on on behalf of the modem */ + reg = readl_relaxed(MARM_CLK_BRANCH_ENA_VOTE); + reg |= BIT(0) | BIT(1); + writel_relaxed(reg, MARM_CLK_BRANCH_ENA_VOTE); + + /* Source marm_clk off of PLL8 */ + reg = readl_relaxed(MARM_CLK_SRC_CTL); + if ((reg & 0x1) == 0) { + writel_relaxed(0x3, MARM_CLK_SRC1_NS); + reg |= 0x1; + } else { + writel_relaxed(0x3, MARM_CLK_SRC0_NS); + reg &= ~0x1; + } + writel_relaxed(reg | 0x2, MARM_CLK_SRC_CTL); + + /* + * Force core on and periph on signals to remain active during halt + * for marm_clk and mahb2_clk + */ + writel_relaxed(0x6F, MARM_CLK_FS); + writel_relaxed(0x6F, MAHB2_CLK_FS); + + /* + * Enable all of the marm_clk branches, cxo sourced marm branches, + * and sleep clock branches + */ + writel_relaxed(0x10, MARM_CLK_CTL); + writel_relaxed(0x10, MAHB0_CLK_CTL); + writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL); + writel_relaxed(0x10, MSS_MODEM_CXO_CLK_CTL); + writel_relaxed(0x10, MSS_SLP_CLK_CTL); + writel_relaxed(0x10, MSS_MARM_SYS_REF_CLK_CTL); + + /* Wait for above clocks to be turned on */ + while (readl_relaxed(CLK_HALT_MSS_SMPSS_MISC_STATE) & (BIT(7) | BIT(8) | + BIT(9) | BIT(10) | BIT(4) | BIT(6))) + cpu_relax(); + + /* Take MAHB0,1,2 clocks out of reset */ + writel_relaxed(0x0, MAHB2_CLK_CTL); + writel_relaxed(0x0, MAHB1_CLK_CTL); + writel_relaxed(0x0, MAHB0_SFAB_PORT_RESET); + mb(); + + /* Setup exception vector table base address */ + writel_relaxed(drv->start_addr | 0x1, drv->base + MARM_BOOT_CONTROL); + + /* Wait for vector table to be setup */ + mb(); + + /* Bring modem out of reset */ + writel_relaxed(0x0, MARM_RESET); + + return 0; +} + +static int modem_shutdown(struct pil_desc *pil) +{ + u32 reg; + + /* Put modem into reset */ + writel_relaxed(0x1, MARM_RESET); + mb(); + + /* Put modem AHB0,1,2 clocks into reset */ + writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET); + writel_relaxed(BIT(7), MAHB1_CLK_CTL); + writel_relaxed(BIT(7), MAHB2_CLK_CTL); + mb(); + + /* + * Disable all of the marm_clk branches, cxo sourced marm branches, + * and sleep clock branches + */ + writel_relaxed(0x0, MARM_CLK_CTL); + writel_relaxed(0x0, MAHB0_CLK_CTL); + writel_relaxed(0x0, SFAB_MSS_S_HCLK_CTL); + writel_relaxed(0x0, MSS_MODEM_CXO_CLK_CTL); + writel_relaxed(0x0, MSS_SLP_CLK_CTL); + writel_relaxed(0x0, MSS_MARM_SYS_REF_CLK_CTL); + + /* Disable marm_clk */ + reg = readl_relaxed(MARM_CLK_SRC_CTL); + reg &= ~0x2; + writel_relaxed(reg, MARM_CLK_SRC_CTL); + + /* Clear modem's votes for ahb clocks */ + writel_relaxed(0x0, MARM_CLK_BRANCH_ENA_VOTE); + + /* Clear modem's votes for PLLs */ + writel_relaxed(0x0, PLL_ENA_MARM); + + return 0; +} + +static struct pil_reset_ops pil_modem_ops = { + .init_image = modem_init_image, + .auth_and_reset = modem_reset, + .shutdown = modem_shutdown, + .proxy_vote = make_modem_proxy_votes, + .proxy_unvote = remove_modem_proxy_votes, +}; + +static int modem_init_image_trusted(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + return pas_init_image(PAS_MODEM, metadata, size); +} + +static int modem_reset_trusted(struct pil_desc *pil) +{ + return pas_auth_and_reset(PAS_MODEM); +} + +static int modem_shutdown_trusted(struct pil_desc *pil) +{ + return pas_shutdown(PAS_MODEM); +} + +static struct pil_reset_ops pil_modem_ops_trusted = { + .init_image = modem_init_image_trusted, + .auth_and_reset = modem_reset_trusted, + .shutdown = modem_shutdown_trusted, + .proxy_vote = make_modem_proxy_votes, + .proxy_unvote = remove_modem_proxy_votes, +}; + +static int __devinit pil_modem_driver_probe(struct platform_device *pdev) +{ + struct modem_data *drv; + struct resource *res; + struct pil_desc *desc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!drv->base) + return -ENOMEM; + + drv->xo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->xo)) + return PTR_ERR(drv->xo); + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + desc->name = "modem"; + desc->depends_on = "q6"; + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + desc->proxy_timeout = 10000; + + if (pas_supported(PAS_MODEM) > 0) { + desc->ops = &pil_modem_ops_trusted; + dev_info(&pdev->dev, "using secure boot\n"); + } else { + desc->ops = &pil_modem_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + } + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) { + return PTR_ERR(drv->pil); + } + return 0; +} + +static int __devexit pil_modem_driver_exit(struct platform_device *pdev) +{ + struct modem_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct platform_driver pil_modem_driver = { + .probe = pil_modem_driver_probe, + .remove = __devexit_p(pil_modem_driver_exit), + .driver = { + .name = "pil_modem", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_modem_init(void) +{ + return platform_driver_register(&pil_modem_driver); +} +module_init(pil_modem_init); + +static void __exit pil_modem_exit(void) +{ + platform_driver_unregister(&pil_modem_driver); +} +module_exit(pil_modem_exit); + +MODULE_DESCRIPTION("Support for booting modem processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c new file mode 100644 index 00000000000..58d51762e03 --- /dev/null +++ b/arch/arm/mach-msm/pil-pronto.c @@ -0,0 +1,349 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +#define PRONTO_PMU_COMMON_GDSCR 0x24 +#define PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE BIT(0) +#define CLK_DIS_WAIT 12 +#define EN_FEW_WAIT 16 +#define EN_REST_WAIT 20 + +#define PRONTO_PMU_COMMON_CPU_CBCR 0x30 +#define PRONTO_PMU_COMMON_CPU_CBCR_CLK_EN BIT(0) +#define PRONTO_PMU_COMMON_CPU_CLK_OFF BIT(31) + +#define PRONTO_PMU_COMMON_AHB_CBCR 0x34 +#define PRONTO_PMU_COMMON_AHB_CBCR_CLK_EN BIT(0) +#define PRONTO_PMU_COMMON_AHB_CLK_OFF BIT(31) + +#define PRONTO_PMU_COMMON_CSR 0x1040 +#define PRONTO_PMU_COMMON_CSR_A2XB_CFG_EN BIT(0) + +#define PRONTO_PMU_SOFT_RESET 0x104C +#define PRONTO_PMU_SOFT_RESET_CRCM_CCPU_SOFT_RESET BIT(10) + +#define PRONTO_PMU_CCPU_CTL 0x2000 +#define PRONTO_PMU_CCPU_CTL_REMAP_EN BIT(2) +#define PRONTO_PMU_CCPU_CTL_HIGH_IVT BIT(0) + +#define PRONTO_PMU_CCPU_BOOT_REMAP_ADDR 0x2004 + +#define CLK_CTL_WCNSS_RESTART_BIT BIT(0) + +#define AXI_HALTREQ 0x0 +#define AXI_HALTACK 0x4 +#define AXI_IDLE 0x8 + +#define HALT_ACK_TIMEOUT_US 500000 +#define CLK_UPDATE_TIMEOUT_US 500000 + +struct pronto_data { + void __iomem *base; + void __iomem *reset_base; + void __iomem *axi_halt_base; + unsigned long start_addr; + struct pil_device *pil; + struct clk *cxo; + struct regulator *vreg; +}; + +static int pil_pronto_make_proxy_vote(struct pil_desc *pil) +{ + struct pronto_data *drv = dev_get_drvdata(pil->dev); + int ret; + + ret = regulator_enable(drv->vreg); + if (ret) { + dev_err(pil->dev, "failed to enable pll supply\n"); + goto err; + } + ret = clk_prepare_enable(drv->cxo); + if (ret) { + dev_err(pil->dev, "failed to enable cxo\n"); + goto err_clk; + } + return 0; +err_clk: + regulator_disable(drv->vreg); +err: + return ret; +} + +static void pil_pronto_remove_proxy_vote(struct pil_desc *pil) +{ + struct pronto_data *drv = dev_get_drvdata(pil->dev); + regulator_disable(drv->vreg); + clk_disable_unprepare(drv->cxo); +} + +static int pil_pronto_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct pronto_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} + +static int pil_pronto_reset(struct pil_desc *pil) +{ + u32 reg; + int rc; + struct pronto_data *drv = dev_get_drvdata(pil->dev); + void __iomem *base = drv->base; + unsigned long start_addr = drv->start_addr; + + /* Deassert reset to Pronto */ + reg = readl_relaxed(drv->reset_base); + reg &= ~CLK_CTL_WCNSS_RESTART_BIT; + writel_relaxed(reg, drv->reset_base); + mb(); + + /* Configure boot address */ + writel_relaxed(start_addr >> 16, base + + PRONTO_PMU_CCPU_BOOT_REMAP_ADDR); + + /* Use the high vector table */ + reg = readl_relaxed(base + PRONTO_PMU_CCPU_CTL); + reg |= PRONTO_PMU_CCPU_CTL_REMAP_EN | PRONTO_PMU_CCPU_CTL_HIGH_IVT; + writel_relaxed(reg, base + PRONTO_PMU_CCPU_CTL); + + /* Turn on AHB clock of common_ss */ + reg = readl_relaxed(base + PRONTO_PMU_COMMON_AHB_CBCR); + reg |= PRONTO_PMU_COMMON_AHB_CBCR_CLK_EN; + writel_relaxed(reg, base + PRONTO_PMU_COMMON_AHB_CBCR); + + /* Turn on CPU clock of common_ss */ + reg = readl_relaxed(base + PRONTO_PMU_COMMON_CPU_CBCR); + reg |= PRONTO_PMU_COMMON_CPU_CBCR_CLK_EN; + writel_relaxed(reg, base + PRONTO_PMU_COMMON_CPU_CBCR); + + /* Enable A2XB bridge */ + reg = readl_relaxed(base + PRONTO_PMU_COMMON_CSR); + reg |= PRONTO_PMU_COMMON_CSR_A2XB_CFG_EN; + writel_relaxed(reg, base + PRONTO_PMU_COMMON_CSR); + + /* Enable common_ss power */ + reg = readl_relaxed(base + PRONTO_PMU_COMMON_GDSCR); + reg &= ~PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE; + writel_relaxed(reg, base + PRONTO_PMU_COMMON_GDSCR); + + /* Wait for AHB clock to be on */ + rc = readl_tight_poll_timeout(base + PRONTO_PMU_COMMON_AHB_CBCR, + reg, + !(reg & PRONTO_PMU_COMMON_AHB_CLK_OFF), + CLK_UPDATE_TIMEOUT_US); + if (rc) { + dev_err(pil->dev, "pronto common ahb clk enable timeout\n"); + return rc; + } + + /* Wait for CPU clock to be on */ + rc = readl_tight_poll_timeout(base + PRONTO_PMU_COMMON_CPU_CBCR, + reg, + !(reg & PRONTO_PMU_COMMON_CPU_CLK_OFF), + CLK_UPDATE_TIMEOUT_US); + if (rc) { + dev_err(pil->dev, "pronto common cpu clk enable timeout\n"); + return rc; + } + + /* Deassert ARM9 software reset */ + reg = readl_relaxed(base + PRONTO_PMU_SOFT_RESET); + reg &= ~PRONTO_PMU_SOFT_RESET_CRCM_CCPU_SOFT_RESET; + writel_relaxed(reg, base + PRONTO_PMU_SOFT_RESET); + + return 0; +} + +static int pil_pronto_shutdown(struct pil_desc *pil) +{ + struct pronto_data *drv = dev_get_drvdata(pil->dev); + int ret; + u32 reg, status; + + /* Halt A2XB */ + writel_relaxed(1, drv->axi_halt_base + AXI_HALTREQ); + ret = readl_poll_timeout(drv->axi_halt_base + AXI_HALTACK, + status, status, 50, HALT_ACK_TIMEOUT_US); + if (ret) + dev_err(pil->dev, "Port halt timeout\n"); + else if (!readl_relaxed(drv->axi_halt_base + AXI_IDLE)) + dev_err(pil->dev, "Port halt failed\n"); + + writel_relaxed(0, drv->axi_halt_base + AXI_HALTREQ); + + /* Assert reset to Pronto */ + reg = readl_relaxed(drv->reset_base); + reg |= CLK_CTL_WCNSS_RESTART_BIT; + writel_relaxed(reg, drv->reset_base); + + /* Wait for reset to complete */ + mb(); + usleep_range(1000, 2000); + + /* Deassert reset to Pronto */ + reg = readl_relaxed(drv->reset_base); + reg &= ~CLK_CTL_WCNSS_RESTART_BIT; + writel_relaxed(reg, drv->reset_base); + mb(); + + return 0; +} + +static struct pil_reset_ops pil_pronto_ops = { + .init_image = pil_pronto_init_image, + .auth_and_reset = pil_pronto_reset, + .shutdown = pil_pronto_shutdown, + .proxy_vote = pil_pronto_make_proxy_vote, + .proxy_unvote = pil_pronto_remove_proxy_vote, +}; + +static int __devinit pil_pronto_probe(struct platform_device *pdev) +{ + struct pronto_data *drv; + struct resource *res; + struct pil_desc *desc; + int ret; + uint32_t regval; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!drv->base) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -EINVAL; + + drv->reset_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) + return -EINVAL; + + drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name", + &desc->name); + if (ret) + return ret; + + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + desc->proxy_timeout = 10000; + + /* TODO: need to add secure boot when the support is available */ + desc->ops = &pil_pronto_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + + drv->vreg = devm_regulator_get(&pdev->dev, "vdd_pronto_pll"); + if (IS_ERR(drv->vreg)) { + dev_err(&pdev->dev, "failed to get pronto pll supply"); + return PTR_ERR(drv->vreg); + } + + ret = regulator_set_voltage(drv->vreg, 1800000, 1800000); + if (ret) { + dev_err(&pdev->dev, "failed to set pll supply voltage\n"); + return ret; + } + + ret = regulator_set_optimum_mode(drv->vreg, 18000); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set pll supply mode\n"); + return ret; + } + + drv->cxo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->cxo)) + return PTR_ERR(drv->cxo); + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) + return PTR_ERR(drv->pil); + + /* Initialize common_ss GDSCR to wait 4 cycles between states */ + regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR) + & PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE; + regval |= (2 << EN_REST_WAIT) | (2 << EN_FEW_WAIT) + | (2 << CLK_DIS_WAIT); + writel_relaxed(regval, drv->base + PRONTO_PMU_COMMON_GDSCR); + + return 0; +} + +static int __devexit pil_pronto_remove(struct platform_device *pdev) +{ + struct pronto_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct of_device_id msm_pil_pronto_match[] = { + {.compatible = "qcom,pil-pronto"}, + {} +}; + +static struct platform_driver pil_pronto_driver = { + .probe = pil_pronto_probe, + .remove = __devexit_p(pil_pronto_remove), + .driver = { + .name = "pil_pronto", + .owner = THIS_MODULE, + .of_match_table = msm_pil_pronto_match, + }, +}; + +static int __init pil_pronto_init(void) +{ + return platform_driver_register(&pil_pronto_driver); +} +module_init(pil_pronto_init); + +static void __exit pil_pronto_exit(void) +{ + platform_driver_unregister(&pil_pronto_driver); +} +module_exit(pil_pronto_exit); + +MODULE_DESCRIPTION("Support for booting PRONTO (WCNSS) processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c new file mode 100644 index 00000000000..28b9deed97a --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v3.c @@ -0,0 +1,277 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +#define QDSP6SS_RST_EVB 0x0000 +#define QDSP6SS_STRAP_TCM 0x001C +#define QDSP6SS_STRAP_AHB 0x0020 + +#define LCC_Q6_FUNC (MSM_LPASS_CLK_CTL_BASE + 0x001C) +#define LV_EN BIT(27) +#define STOP_CORE BIT(26) +#define CLAMP_IO BIT(25) +#define Q6SS_PRIV_ARES BIT(24) +#define Q6SS_SS_ARES BIT(23) +#define Q6SS_ISDB_ARES BIT(22) +#define Q6SS_ETM_ARES BIT(21) +#define Q6_JTAG_CRC_EN BIT(20) +#define Q6_JTAG_INV_EN BIT(19) +#define Q6_JTAG_CXC_EN BIT(18) +#define Q6_PXO_CRC_EN BIT(17) +#define Q6_PXO_INV_EN BIT(16) +#define Q6_PXO_CXC_EN BIT(15) +#define Q6_PXO_SLEEP_EN BIT(14) +#define Q6_SLP_CRC_EN BIT(13) +#define Q6_SLP_INV_EN BIT(12) +#define Q6_SLP_CXC_EN BIT(11) +#define CORE_ARES BIT(10) +#define CORE_L1_MEM_CORE_EN BIT(9) +#define CORE_TCM_MEM_CORE_EN BIT(8) +#define CORE_TCM_MEM_PERPH_EN BIT(7) +#define CORE_GFM4_CLK_EN BIT(2) +#define CORE_GFM4_RES BIT(1) +#define RAMP_PLL_SRC_SEL BIT(0) + +#define Q6_STRAP_AHB_UPPER (0x290 << 12) +#define Q6_STRAP_AHB_LOWER 0x280 +#define Q6_STRAP_TCM_BASE (0x28C << 15) +#define Q6_STRAP_TCM_CONFIG 0x28B + +struct q6v3_data { + void __iomem *base; + unsigned long start_addr; + struct pil_device *pil; + struct clk *pll; +}; + +static int pil_q6v3_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct q6v3_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} + +static void pil_q6v3_remove_proxy_votes(struct pil_desc *pil) +{ + struct q6v3_data *drv = dev_get_drvdata(pil->dev); + clk_disable_unprepare(drv->pll); +} + +static int pil_q6v3_make_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct q6v3_data *drv = dev_get_drvdata(pil->dev); + + ret = clk_prepare_enable(drv->pll); + if (ret) { + dev_err(pil->dev, "Failed to enable PLL\n"); + return ret; + } + return 0; +} + +static int pil_q6v3_reset(struct pil_desc *pil) +{ + u32 reg; + struct q6v3_data *drv = dev_get_drvdata(pil->dev); + + /* Put Q6 into reset */ + reg = readl_relaxed(LCC_Q6_FUNC); + reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE | + CORE_ARES; + reg &= ~CORE_GFM4_CLK_EN; + writel_relaxed(reg, LCC_Q6_FUNC); + + /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */ + usleep_range(20, 30); + + /* Turn on Q6 memory */ + reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN | + CORE_TCM_MEM_PERPH_EN; + writel_relaxed(reg, LCC_Q6_FUNC); + + /* Turn on Q6 core clocks and take core out of reset */ + reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | + CORE_ARES); + writel_relaxed(reg, LCC_Q6_FUNC); + + /* Wait for clocks to be enabled */ + mb(); + /* Program boot address */ + writel_relaxed((drv->start_addr >> 12) & 0xFFFFF, + drv->base + QDSP6SS_RST_EVB); + + writel_relaxed(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE, + drv->base + QDSP6SS_STRAP_TCM); + writel_relaxed(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER, + drv->base + QDSP6SS_STRAP_AHB); + + /* Wait for addresses to be programmed before starting Q6 */ + mb(); + + /* Start Q6 instruction execution */ + reg &= ~STOP_CORE; + writel_relaxed(reg, LCC_Q6_FUNC); + + return 0; +} + +static int pil_q6v3_shutdown(struct pil_desc *pil) +{ + u32 reg; + + /* Put Q6 into reset */ + reg = readl_relaxed(LCC_Q6_FUNC); + reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE | + CORE_ARES; + reg &= ~CORE_GFM4_CLK_EN; + writel_relaxed(reg, LCC_Q6_FUNC); + + /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */ + usleep_range(20, 30); + + /* Turn off Q6 memory */ + reg &= ~(CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN | + CORE_TCM_MEM_PERPH_EN); + writel_relaxed(reg, LCC_Q6_FUNC); + + reg |= CLAMP_IO; + writel_relaxed(reg, LCC_Q6_FUNC); + + return 0; +} + +static struct pil_reset_ops pil_q6v3_ops = { + .init_image = pil_q6v3_init_image, + .auth_and_reset = pil_q6v3_reset, + .shutdown = pil_q6v3_shutdown, + .proxy_vote = pil_q6v3_make_proxy_votes, + .proxy_unvote = pil_q6v3_remove_proxy_votes, +}; + +static int pil_q6v3_init_image_trusted(struct pil_desc *pil, + const u8 *metadata, size_t size) +{ + return pas_init_image(PAS_Q6, metadata, size); +} + +static int pil_q6v3_reset_trusted(struct pil_desc *pil) +{ + return pas_auth_and_reset(PAS_Q6); +} + +static int pil_q6v3_shutdown_trusted(struct pil_desc *pil) +{ + return pas_shutdown(PAS_Q6); +} + +static struct pil_reset_ops pil_q6v3_ops_trusted = { + .init_image = pil_q6v3_init_image_trusted, + .auth_and_reset = pil_q6v3_reset_trusted, + .shutdown = pil_q6v3_shutdown_trusted, + .proxy_vote = pil_q6v3_make_proxy_votes, + .proxy_unvote = pil_q6v3_remove_proxy_votes, +}; + +static int __devinit pil_q6v3_driver_probe(struct platform_device *pdev) +{ + struct q6v3_data *drv; + struct resource *res; + struct pil_desc *desc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!drv->base) + return -ENOMEM; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->pll = devm_clk_get(&pdev->dev, "pll4"); + if (IS_ERR(drv->pll)) + return PTR_ERR(drv->pll); + + desc->name = "q6"; + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + desc->proxy_timeout = 10000; + + if (pas_supported(PAS_Q6) > 0) { + desc->ops = &pil_q6v3_ops_trusted; + dev_info(&pdev->dev, "using secure boot\n"); + } else { + desc->ops = &pil_q6v3_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + } + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) { + return PTR_ERR(drv->pil); + } + return 0; +} + +static int __devexit pil_q6v3_driver_exit(struct platform_device *pdev) +{ + struct q6v3_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct platform_driver pil_q6v3_driver = { + .probe = pil_q6v3_driver_probe, + .remove = __devexit_p(pil_q6v3_driver_exit), + .driver = { + .name = "pil_qdsp6v3", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_q6v3_init(void) +{ + return platform_driver_register(&pil_q6v3_driver); +} +module_init(pil_q6v3_init); + +static void __exit pil_q6v3_exit(void) +{ + platform_driver_unregister(&pil_q6v3_driver); +} +module_exit(pil_q6v3_exit); + +MODULE_DESCRIPTION("Support for booting QDSP6v3 (Hexagon) processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c new file mode 100644 index 00000000000..131a74bf2eb --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v4.c @@ -0,0 +1,466 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "peripheral-loader.h" +#include "pil-q6v4.h" +#include "scm-pas.h" + +#define QDSP6SS_RST_EVB 0x0 +#define QDSP6SS_RESET 0x04 +#define QDSP6SS_STRAP_TCM 0x1C +#define QDSP6SS_STRAP_AHB 0x20 +#define QDSP6SS_GFMUX_CTL 0x30 +#define QDSP6SS_PWR_CTL 0x38 + +#define MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C70) +#define MSS_SLP_CLK_CTL (MSM_CLK_CTL_BASE + 0x2C60) +#define SFAB_MSS_M_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2340) +#define SFAB_MSS_S_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2C00) +#define MSS_RESET (MSM_CLK_CTL_BASE + 0x2C64) + +#define Q6SS_SS_ARES BIT(0) +#define Q6SS_CORE_ARES BIT(1) +#define Q6SS_ISDB_ARES BIT(2) +#define Q6SS_ETM_ARES BIT(3) +#define Q6SS_STOP_CORE_ARES BIT(4) +#define Q6SS_PRIV_ARES BIT(5) + +#define Q6SS_L2DATA_SLP_NRET_N BIT(0) +#define Q6SS_SLP_RET_N BIT(1) +#define Q6SS_L1TCM_SLP_NRET_N BIT(2) +#define Q6SS_L2TAG_SLP_NRET_N BIT(3) +#define Q6SS_ETB_SLEEP_NRET_N BIT(4) +#define Q6SS_ARR_STBY_N BIT(5) +#define Q6SS_CLAMP_IO BIT(6) + +#define Q6SS_CLK_ENA BIT(1) +#define Q6SS_SRC_SWITCH_CLK_OVR BIT(8) + +struct q6v4_data { + void __iomem *base; + void __iomem *modem_base; + unsigned long start_addr; + struct regulator *vreg; + struct regulator *pll_supply; + bool vreg_enabled; + struct clk *xo; + struct pil_device *pil; +}; + +static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct q6v4_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} + +static int pil_q6v4_make_proxy_votes(struct pil_desc *pil) +{ + const struct q6v4_data *drv = dev_get_drvdata(pil->dev); + int ret; + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + goto err; + } + if (drv->pll_supply) { + ret = regulator_enable(drv->pll_supply); + if (ret) { + dev_err(pil->dev, "Failed to enable pll supply\n"); + goto err_regulator; + } + } + return 0; +err_regulator: + clk_disable_unprepare(drv->xo); +err: + return ret; +} + +static void pil_q6v4_remove_proxy_votes(struct pil_desc *pil) +{ + const struct q6v4_data *drv = dev_get_drvdata(pil->dev); + if (drv->pll_supply) + regulator_disable(drv->pll_supply); + clk_disable_unprepare(drv->xo); +} + +static int pil_q6v4_power_up(struct device *dev) +{ + int err; + struct q6v4_data *drv = dev_get_drvdata(dev); + + err = regulator_set_voltage(drv->vreg, 375000, 375000); + if (err) { + dev_err(dev, "Failed to set regulator's voltage step.\n"); + return err; + } + err = regulator_enable(drv->vreg); + if (err) { + dev_err(dev, "Failed to enable regulator.\n"); + return err; + } + + /* + * Q6 hardware requires a two step voltage ramp-up. + * Delay between the steps. + */ + udelay(100); + + err = regulator_set_voltage(drv->vreg, 1050000, 1050000); + if (err) { + dev_err(dev, "Failed to set regulator's voltage.\n"); + return err; + } + drv->vreg_enabled = true; + return 0; +} + +static DEFINE_MUTEX(pil_q6v4_modem_lock); +static unsigned pil_q6v4_modem_count; + +/* Bring modem subsystem out of reset */ +static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk) +{ + mutex_lock(&pil_q6v4_modem_lock); + if (!pil_q6v4_modem_count) { + /* Enable MSS clocks */ + writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL); + writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL); + writel_relaxed(0x10, MSS_S_HCLK_CTL); + writel_relaxed(0x10, MSS_SLP_CLK_CTL); + /* Wait for clocks to enable */ + mb(); + udelay(10); + + /* De-assert MSS reset */ + writel_relaxed(0x0, MSS_RESET); + mb(); + udelay(10); + /* Enable MSS */ + writel_relaxed(0x7, base); + } + + /* Enable JTAG clocks */ + /* TODO: Remove if/when Q6 software enables them? */ + writel_relaxed(0x10, jtag_clk); + + pil_q6v4_modem_count++; + mutex_unlock(&pil_q6v4_modem_lock); +} + +/* Put modem subsystem back into reset */ +static void pil_q6v4_shutdown_modem(void) +{ + mutex_lock(&pil_q6v4_modem_lock); + if (pil_q6v4_modem_count) + pil_q6v4_modem_count--; + if (pil_q6v4_modem_count == 0) + writel_relaxed(0x1, MSS_RESET); + mutex_unlock(&pil_q6v4_modem_lock); +} + +static int pil_q6v4_reset(struct pil_desc *pil) +{ + u32 reg, err; + const struct q6v4_data *drv = dev_get_drvdata(pil->dev); + const struct pil_q6v4_pdata *pdata = pil->dev->platform_data; + + err = pil_q6v4_power_up(pil->dev); + if (err) + return err; + /* Enable Q6 ACLK */ + writel_relaxed(0x10, pdata->aclk_reg); + + if (drv->modem_base) + pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg); + + /* Unhalt bus port */ + err = msm_bus_axi_portunhalt(pdata->bus_port); + if (err) + dev_err(pil->dev, "Failed to unhalt bus port\n"); + + /* Deassert Q6SS_SS_ARES */ + reg = readl_relaxed(drv->base + QDSP6SS_RESET); + reg &= ~(Q6SS_SS_ARES); + writel_relaxed(reg, drv->base + QDSP6SS_RESET); + + /* Program boot address */ + writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF, + drv->base + QDSP6SS_RST_EVB); + + /* Program TCM and AHB address ranges */ + writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM); + writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower, + drv->base + QDSP6SS_STRAP_AHB); + + /* Turn off Q6 core clock */ + writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR, + drv->base + QDSP6SS_GFMUX_CTL); + + /* Put memories to sleep */ + writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL); + + /* Assert resets */ + reg = readl_relaxed(drv->base + QDSP6SS_RESET); + reg |= (Q6SS_CORE_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES + | Q6SS_STOP_CORE_ARES); + writel_relaxed(reg, drv->base + QDSP6SS_RESET); + + /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */ + mb(); + usleep_range(20, 30); + + /* Turn on Q6 memories */ + reg = Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | Q6SS_L1TCM_SLP_NRET_N + | Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLEEP_NRET_N | Q6SS_ARR_STBY_N + | Q6SS_CLAMP_IO; + writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL); + + /* Turn on Q6 core clock */ + reg = Q6SS_CLK_ENA | Q6SS_SRC_SWITCH_CLK_OVR; + writel_relaxed(reg, drv->base + QDSP6SS_GFMUX_CTL); + + /* Remove Q6SS_CLAMP_IO */ + reg = readl_relaxed(drv->base + QDSP6SS_PWR_CTL); + reg &= ~Q6SS_CLAMP_IO; + writel_relaxed(reg, drv->base + QDSP6SS_PWR_CTL); + + /* Bring Q6 core out of reset and start execution. */ + writel_relaxed(0x0, drv->base + QDSP6SS_RESET); + + return 0; +} + +static int pil_q6v4_shutdown(struct pil_desc *pil) +{ + u32 reg; + struct q6v4_data *drv = dev_get_drvdata(pil->dev); + const struct pil_q6v4_pdata *pdata = pil->dev->platform_data; + + /* Make sure bus port is halted */ + msm_bus_axi_porthalt(pdata->bus_port); + + /* Turn off Q6 core clock */ + writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR, + drv->base + QDSP6SS_GFMUX_CTL); + + /* Assert resets */ + reg = (Q6SS_SS_ARES | Q6SS_CORE_ARES | Q6SS_ISDB_ARES + | Q6SS_ETM_ARES | Q6SS_STOP_CORE_ARES | Q6SS_PRIV_ARES); + writel_relaxed(reg, drv->base + QDSP6SS_RESET); + + /* Turn off Q6 memories */ + writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL); + + if (drv->modem_base) + pil_q6v4_shutdown_modem(); + + if (drv->vreg_enabled) { + regulator_disable(drv->vreg); + drv->vreg_enabled = false; + } + + return 0; +} + +static struct pil_reset_ops pil_q6v4_ops = { + .init_image = pil_q6v4_init_image, + .auth_and_reset = pil_q6v4_reset, + .shutdown = pil_q6v4_shutdown, + .proxy_vote = pil_q6v4_make_proxy_votes, + .proxy_unvote = pil_q6v4_remove_proxy_votes, +}; + +static int pil_q6v4_init_image_trusted(struct pil_desc *pil, + const u8 *metadata, size_t size) +{ + const struct pil_q6v4_pdata *pdata = pil->dev->platform_data; + return pas_init_image(pdata->pas_id, metadata, size); +} + +static int pil_q6v4_reset_trusted(struct pil_desc *pil) +{ + const struct pil_q6v4_pdata *pdata = pil->dev->platform_data; + int err; + + err = pil_q6v4_power_up(pil->dev); + if (err) + return err; + + /* Unhalt bus port */ + err = msm_bus_axi_portunhalt(pdata->bus_port); + if (err) + dev_err(pil->dev, "Failed to unhalt bus port\n"); + return pas_auth_and_reset(pdata->pas_id); +} + +static int pil_q6v4_shutdown_trusted(struct pil_desc *pil) +{ + int ret; + struct q6v4_data *drv = dev_get_drvdata(pil->dev); + struct pil_q6v4_pdata *pdata = pil->dev->platform_data; + + /* Make sure bus port is halted */ + msm_bus_axi_porthalt(pdata->bus_port); + + ret = pas_shutdown(pdata->pas_id); + if (ret) + return ret; + + if (drv->vreg_enabled) { + regulator_disable(drv->vreg); + drv->vreg_enabled = false; + } + + return ret; +} + +static struct pil_reset_ops pil_q6v4_ops_trusted = { + .init_image = pil_q6v4_init_image_trusted, + .auth_and_reset = pil_q6v4_reset_trusted, + .shutdown = pil_q6v4_shutdown_trusted, + .proxy_vote = pil_q6v4_make_proxy_votes, + .proxy_unvote = pil_q6v4_remove_proxy_votes, +}; + +static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev) +{ + const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data; + struct q6v4_data *drv; + struct resource *res; + struct pil_desc *desc; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!drv->base) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + drv->modem_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->modem_base) + return -ENOMEM; + } + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd"); + if (IS_ERR(drv->pll_supply)) { + drv->pll_supply = NULL; + } else { + ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000); + if (ret) { + dev_err(&pdev->dev, "failed to set pll voltage\n"); + return ret; + } + + ret = regulator_set_optimum_mode(drv->pll_supply, 100000); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set pll optimum mode\n"); + return ret; + } + } + + desc->name = pdata->name; + desc->depends_on = pdata->depends; + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + desc->proxy_timeout = 10000; + + if (pas_supported(pdata->pas_id) > 0) { + desc->ops = &pil_q6v4_ops_trusted; + dev_info(&pdev->dev, "using secure boot\n"); + } else { + desc->ops = &pil_q6v4_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + } + + drv->vreg = devm_regulator_get(&pdev->dev, "core_vdd"); + if (IS_ERR(drv->vreg)) + return PTR_ERR(drv->vreg); + + ret = regulator_set_optimum_mode(drv->vreg, 100000); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to set regulator's mode.\n"); + return ret; + } + + drv->xo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->xo)) + return PTR_ERR(drv->xo); + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) + return PTR_ERR(drv->pil); + return 0; +} + +static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev) +{ + struct q6v4_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct platform_driver pil_q6v4_driver = { + .probe = pil_q6v4_driver_probe, + .remove = __devexit_p(pil_q6v4_driver_exit), + .driver = { + .name = "pil_qdsp6v4", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_q6v4_init(void) +{ + return platform_driver_register(&pil_q6v4_driver); +} +module_init(pil_q6v4_init); + +static void __exit pil_q6v4_exit(void) +{ + platform_driver_unregister(&pil_q6v4_driver); +} +module_exit(pil_q6v4_exit); + +MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-q6v4.h b/arch/arm/mach-msm/pil-q6v4.h new file mode 100644 index 00000000000..b0b97d0fc5a --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v4.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_PIL_Q6V4_H +#define __MSM_PIL_Q6V4_H + +struct pil_q6v4_pdata { + const unsigned long strap_tcm_base; + const unsigned long strap_ahb_upper; + const unsigned long strap_ahb_lower; + void __iomem *aclk_reg; + void __iomem *jtag_clk_reg; + const char *name; + const char *depends; + const unsigned pas_id; + int bus_port; +}; +#endif diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c new file mode 100644 index 00000000000..311f8a78021 --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v5-lpass.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "pil-q6v5.h" + +#define QDSP6SS_RST_EVB 0x010 +#define PROXY_TIMEOUT_MS 10000 + +static int pil_lpass_shutdown(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + + pil_q6v5_halt_axi_port(pil, drv->axi_halt_base); + + /* + * If the shutdown function is called before the reset function, clocks + * will not be enabled yet. Enable them here so that register writes + * performed during the shutdown succeed. + */ + if (drv->is_booted == false) + pil_q6v5_enable_clks(pil); + + pil_q6v5_shutdown(pil); + pil_q6v5_disable_clks(pil); + + drv->is_booted = false; + + return 0; +} + +static int pil_lpass_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + int ret; + + ret = pil_q6v5_enable_clks(pil); + if (ret) + return ret; + + /* Program Image Address */ + writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0), + drv->reg_base + QDSP6SS_RST_EVB); + + ret = pil_q6v5_reset(pil); + if (ret) { + pil_q6v5_disable_clks(pil); + return ret; + } + + drv->is_booted = true; + + return 0; +} + +static struct pil_reset_ops pil_lpass_ops = { + .init_image = pil_q6v5_init_image, + .proxy_vote = pil_q6v5_make_proxy_votes, + .proxy_unvote = pil_q6v5_remove_proxy_votes, + .auth_and_reset = pil_lpass_reset, + .shutdown = pil_lpass_shutdown, +}; + +static int __devinit pil_lpass_driver_probe(struct platform_device *pdev) +{ + struct q6v5_data *drv; + struct pil_desc *desc; + + desc = pil_q6v5_init(pdev); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + drv = platform_get_drvdata(pdev); + if (drv == NULL) + return -ENODEV; + + desc->ops = &pil_lpass_ops; + desc->owner = THIS_MODULE; + desc->proxy_timeout = PROXY_TIMEOUT_MS; + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) + return PTR_ERR(drv->pil); + + return 0; +} + +static int __devexit pil_lpass_driver_exit(struct platform_device *pdev) +{ + struct q6v5_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct of_device_id lpass_match_table[] = { + { .compatible = "qcom,pil-q6v5-lpass" }, + {} +}; + +static struct platform_driver pil_lpass_driver = { + .probe = pil_lpass_driver_probe, + .remove = __devexit_p(pil_lpass_driver_exit), + .driver = { + .name = "pil-q6v5-lpass", + .of_match_table = lpass_match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init pil_lpass_init(void) +{ + return platform_driver_register(&pil_lpass_driver); +} +module_init(pil_lpass_init); + +static void __exit pil_lpass_exit(void) +{ + platform_driver_unregister(&pil_lpass_driver); +} +module_exit(pil_lpass_exit); + +MODULE_DESCRIPTION("Support for booting low-power audio subsystems with QDSP6v5 (Hexagon) processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c new file mode 100644 index 00000000000..e279f993500 --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v5-mss.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "peripheral-loader.h" +#include "pil-q6v5.h" + +/* Q6 Register Offsets */ +#define QDSP6SS_RST_EVB 0x010 + +/* AXI Halting Registers */ +#define MSS_Q6_HALT_BASE 0x180 +#define MSS_MODEM_HALT_BASE 0x200 +#define MSS_NC_HALT_BASE 0x280 + +/* RMB Status Register Values */ +#define STATUS_PBL_SUCCESS 0x1 +#define STATUS_XPU_UNLOCKED 0x1 +#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2 + +/* PBL/MBA interface registers */ +#define RMB_MBA_IMAGE 0x00 +#define RMB_PBL_STATUS 0x04 +#define RMB_MBA_STATUS 0x0C + +#define PBL_MBA_WAIT_TIMEOUT_US 100000 +#define PROXY_TIMEOUT_MS 10000 +#define POLL_INTERVAL_US 50 + +static int pil_mss_power_up(struct device *dev) +{ + int ret; + struct q6v5_data *drv = dev_get_drvdata(dev); + + ret = regulator_enable(drv->vreg); + if (ret) + dev_err(dev, "Failed to enable regulator.\n"); + + return ret; +} + +static int pil_mss_power_down(struct device *dev) +{ + struct q6v5_data *drv = dev_get_drvdata(dev); + + return regulator_disable(drv->vreg); +} + +static int wait_for_mba_ready(struct device *dev) +{ + struct q6v5_data *drv = dev_get_drvdata(dev); + int ret; + u32 status; + + /* Wait for PBL completion. */ + ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status, + status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US); + if (ret) { + dev_err(dev, "PBL boot timed out\n"); + return ret; + } + if (status != STATUS_PBL_SUCCESS) { + dev_err(dev, "PBL returned unexpected status %d\n", status); + return -EINVAL; + } + + /* Wait for MBA completion. */ + ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status, + status != 0, POLL_INTERVAL_US, PBL_MBA_WAIT_TIMEOUT_US); + if (ret) { + dev_err(dev, "MBA boot timed out\n"); + return ret; + } + if (status != STATUS_XPU_UNLOCKED && + status != STATUS_XPU_UNLOCKED_SCRIBBLED) { + dev_err(dev, "MBA returned unexpected status %d\n", status); + return -EINVAL; + } + + return 0; +} + +static int pil_mss_shutdown(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + + pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE); + pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE); + pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_NC_HALT_BASE); + + /* + * If the shutdown function is called before the reset function, clocks + * and power will not be enabled yet. Enable them here so that register + * writes performed during the shutdown succeed. + */ + if (drv->is_booted == false) { + pil_mss_power_up(pil->dev); + pil_q6v5_enable_clks(pil); + } + pil_q6v5_shutdown(pil); + + pil_q6v5_disable_clks(pil); + pil_mss_power_down(pil->dev); + + writel_relaxed(1, drv->restart_reg); + + drv->is_booted = false; + + return 0; +} + +static int pil_mss_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + int ret; + + writel_relaxed(0, drv->restart_reg); + mb(); + + /* + * Bring subsystem out of reset and enable required + * regulators and clocks. + */ + ret = pil_mss_power_up(pil->dev); + if (ret) + goto err_power; + + ret = pil_q6v5_enable_clks(pil); + if (ret) + goto err_clks; + + /* Program Image Address */ + if (drv->self_auth) + writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE); + else + writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0, + drv->reg_base + QDSP6SS_RST_EVB); + + ret = pil_q6v5_reset(pil); + if (ret) + goto err_q6v5_reset; + + /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */ + if (drv->self_auth) { + ret = wait_for_mba_ready(pil->dev); + if (ret) + goto err_auth; + } + + drv->is_booted = true; + + return 0; + +err_auth: + pil_q6v5_shutdown(pil); +err_q6v5_reset: + pil_q6v5_disable_clks(pil); +err_clks: + pil_mss_power_down(pil->dev); +err_power: + return ret; +} + +static struct pil_reset_ops pil_mss_ops = { + .init_image = pil_q6v5_init_image, + .proxy_vote = pil_q6v5_make_proxy_votes, + .proxy_unvote = pil_q6v5_remove_proxy_votes, + .auth_and_reset = pil_mss_reset, + .shutdown = pil_mss_shutdown, +}; + +static int __devinit pil_mss_driver_probe(struct platform_device *pdev) +{ + struct q6v5_data *drv; + struct pil_desc *desc; + struct resource *res; + int ret; + + desc = pil_q6v5_init(pdev); + if (IS_ERR(desc)) + return PTR_ERR(desc); + drv = platform_get_drvdata(pdev); + if (drv == NULL) + return -ENODEV; + + desc->ops = &pil_mss_ops; + desc->owner = THIS_MODULE; + desc->proxy_timeout = PROXY_TIMEOUT_MS; + + of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth", + &drv->self_auth); + if (drv->self_auth) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + drv->rmb_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->rmb_base) + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + drv->restart_reg = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->restart_reg) + return -ENOMEM; + + drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss"); + if (IS_ERR(drv->vreg)) + return PTR_ERR(drv->vreg); + + ret = regulator_set_voltage(drv->vreg, 1150000, 1150000); + if (ret) + dev_err(&pdev->dev, "Failed to set regulator's voltage.\n"); + + ret = regulator_set_optimum_mode(drv->vreg, 100000); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to set regulator's mode.\n"); + return ret; + } + + drv->mem_clk = devm_clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(drv->mem_clk)) + return PTR_ERR(drv->mem_clk); + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) + return PTR_ERR(drv->pil); + + return 0; +} + +static int __devexit pil_mss_driver_exit(struct platform_device *pdev) +{ + struct q6v5_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct of_device_id mss_match_table[] = { + { .compatible = "qcom,pil-q6v5-mss" }, + {} +}; + +static struct platform_driver pil_mss_driver = { + .probe = pil_mss_driver_probe, + .remove = __devexit_p(pil_mss_driver_exit), + .driver = { + .name = "pil-q6v5-mss", + .of_match_table = mss_match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init pil_mss_init(void) +{ + return platform_driver_register(&pil_mss_driver); +} +module_init(pil_mss_init); + +static void __exit pil_mss_exit(void) +{ + platform_driver_unregister(&pil_mss_driver); +} +module_exit(pil_mss_exit); + +MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-q6v5.c b/arch/arm/mach-msm/pil-q6v5.c new file mode 100644 index 00000000000..a362a7e3dc5 --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v5.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "peripheral-loader.h" +#include "pil-q6v5.h" + +/* QDSP6SS Register Offsets */ +#define QDSP6SS_RESET 0x014 +#define QDSP6SS_GFMUX_CTL 0x020 +#define QDSP6SS_PWR_CTL 0x030 + +/* AXI Halt Register Offsets */ +#define AXI_HALTREQ 0x0 +#define AXI_HALTACK 0x4 +#define AXI_IDLE 0x8 + +#define HALT_ACK_TIMEOUT_US 100000 + +/* QDSP6SS_RESET */ +#define Q6SS_CORE_ARES BIT(1) +#define Q6SS_ETM_ISDB_ARES BIT(3) +#define Q6SS_STOP_CORE BIT(4) + +/* QDSP6SS_GFMUX_CTL */ +#define Q6SS_CLK_ENA BIT(1) + +/* QDSP6SS_PWR_CTL */ +#define Q6SS_L2DATA_SLP_NRET_N BIT(0) +#define Q6SS_L2TAG_SLP_NRET_N BIT(16) +#define Q6SS_ETB_SLP_NRET_N BIT(17) +#define Q6SS_L2DATA_STBY_N BIT(18) +#define Q6SS_SLP_RET_N BIT(19) +#define Q6SS_CLAMP_IO BIT(20) +#define QDSS_BHS_ON BIT(21) + +int pil_q6v5_make_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to enable XO\n"); + return ret; + } + return 0; +} +EXPORT_SYMBOL(pil_q6v5_make_proxy_votes); + +void pil_q6v5_remove_proxy_votes(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + clk_disable_unprepare(drv->xo); +} +EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes); + +void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base) +{ + int ret; + u32 status; + + /* Assert halt request */ + writel_relaxed(1, halt_base + AXI_HALTREQ); + + /* Wait for halt */ + ret = readl_poll_timeout(halt_base + AXI_HALTACK, + status, status != 0, 50, HALT_ACK_TIMEOUT_US); + if (ret) + dev_warn(pil->dev, "Port %p halt timeout\n", halt_base); + else if (!readl_relaxed(halt_base + AXI_IDLE)) + dev_warn(pil->dev, "Port %p halt failed\n", halt_base); + + /* Clear halt request (port will remain halted until reset) */ + writel_relaxed(0, halt_base + AXI_HALTREQ); +} +EXPORT_SYMBOL(pil_q6v5_halt_axi_port); + +int pil_q6v5_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} +EXPORT_SYMBOL(pil_q6v5_init_image); + +int pil_q6v5_enable_clks(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + int ret; + + ret = clk_reset(drv->core_clk, CLK_RESET_DEASSERT); + if (ret) + goto err_reset; + ret = clk_prepare_enable(drv->core_clk); + if (ret) + goto err_core_clk; + ret = clk_prepare_enable(drv->bus_clk); + if (ret) + goto err_bus_clk; + if (drv->mem_clk) { + ret = clk_prepare_enable(drv->mem_clk); + if (ret) + goto err_mem_clk; + } + + return 0; + +err_mem_clk: + clk_disable_unprepare(drv->bus_clk); +err_bus_clk: + clk_disable_unprepare(drv->core_clk); +err_core_clk: + clk_reset(drv->core_clk, CLK_RESET_ASSERT); +err_reset: + return ret; +} +EXPORT_SYMBOL(pil_q6v5_enable_clks); + +void pil_q6v5_disable_clks(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + + clk_disable_unprepare(drv->bus_clk); + clk_disable_unprepare(drv->core_clk); + clk_disable_unprepare(drv->mem_clk); + clk_reset(drv->core_clk, CLK_RESET_ASSERT); +} +EXPORT_SYMBOL(pil_q6v5_disable_clks); + +void pil_q6v5_shutdown(struct pil_desc *pil) +{ + u32 val; + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + + /* Turn off core clock */ + val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL); + val &= ~Q6SS_CLK_ENA; + writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL); + + /* Clamp IO */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_CLAMP_IO; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn off Q6 memories */ + val &= ~(Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | + Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N | + Q6SS_L2DATA_STBY_N); + writel_relaxed(Q6SS_CLAMP_IO, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Assert Q6 resets */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val = (Q6SS_CORE_ARES | Q6SS_ETM_ISDB_ARES); + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Kill power at block headswitch (affects LPASS only) */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val &= ~QDSS_BHS_ON; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); +} +EXPORT_SYMBOL(pil_q6v5_shutdown); + +int pil_q6v5_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = dev_get_drvdata(pil->dev); + u32 val; + + /* Assert resets, stop core */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val |= (Q6SS_CORE_ARES | Q6SS_ETM_ISDB_ARES | Q6SS_STOP_CORE); + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Enable power block headswitch (only affects LPASS) */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= QDSS_BHS_ON; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn on memories */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_L2DATA_SLP_NRET_N | Q6SS_SLP_RET_N | + Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N | + Q6SS_L2DATA_STBY_N; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Remove IO clamp */ + val &= ~Q6SS_CLAMP_IO; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Bring core out of reset */ + val = Q6SS_STOP_CORE; + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Turn on core clock */ + val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL); + val |= Q6SS_CLK_ENA; + writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL); + + /* Start core execution */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val &= ~Q6SS_STOP_CORE; + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + return 0; +} +EXPORT_SYMBOL(pil_q6v5_reset); + +struct pil_desc __devinit *pil_q6v5_init(struct platform_device *pdev) +{ + struct q6v5_data *drv; + struct resource *res; + struct pil_desc *desc; + int ret; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return ERR_PTR(-ENOMEM); + platform_set_drvdata(pdev, drv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return ERR_PTR(-EINVAL); + drv->reg_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->reg_base) + return ERR_PTR(-ENOMEM); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->axi_halt_base) + return ERR_PTR(-ENOMEM); + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name", + &desc->name); + if (ret) + return ERR_PTR(ret); + + drv->xo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->xo)) + return ERR_CAST(drv->xo); + + drv->bus_clk = devm_clk_get(&pdev->dev, "bus_clk"); + if (IS_ERR(drv->bus_clk)) + return ERR_CAST(drv->bus_clk); + + drv->core_clk = devm_clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(drv->core_clk)) + return ERR_CAST(drv->core_clk); + + desc->dev = &pdev->dev; + + return desc; +} +EXPORT_SYMBOL(pil_q6v5_init); diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h new file mode 100644 index 00000000000..e0d7a208760 --- /dev/null +++ b/arch/arm/mach-msm/pil-q6v5.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_PIL_Q6V5_H +#define __MSM_PIL_Q6V5_H + +struct regulator; +struct clk; +struct pil_device; +struct pil_desc; +struct platform_device; + +struct q6v5_data { + void __iomem *reg_base; + struct clk *xo; + struct clk *bus_clk; + struct clk *core_clk; + struct clk *mem_clk; + void __iomem *axi_halt_base; + void __iomem *rmb_base; + void __iomem *restart_reg; + unsigned long start_addr; + struct regulator *vreg; + bool is_booted; + int self_auth; + struct pil_device *pil; +}; + +int pil_q6v5_make_proxy_votes(struct pil_desc *pil); +void pil_q6v5_remove_proxy_votes(struct pil_desc *pil); +void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base); +int pil_q6v5_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size); +void pil_q6v5_shutdown(struct pil_desc *pil); +int pil_q6v5_reset(struct pil_desc *pil); +int pil_q6v5_enable_clks(struct pil_desc *pil); +void pil_q6v5_disable_clks(struct pil_desc *pil); +struct pil_desc *pil_q6v5_init(struct platform_device *pdev); + +#endif diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c new file mode 100644 index 00000000000..8a16b43f6ae --- /dev/null +++ b/arch/arm/mach-msm/pil-riva.c @@ -0,0 +1,384 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +#define RIVA_PMU_A2XB_CFG 0xB8 +#define RIVA_PMU_A2XB_CFG_EN BIT(0) + +#define RIVA_PMU_CFG 0x28 +#define RIVA_PMU_CFG_WARM_BOOT BIT(0) +#define RIVA_PMU_CFG_IRIS_XO_MODE 0x6 +#define RIVA_PMU_CFG_IRIS_XO_MODE_48 (3 << 1) + +#define RIVA_PMU_OVRD_EN 0x2C +#define RIVA_PMU_OVRD_EN_CCPU_RESET BIT(0) +#define RIVA_PMU_OVRD_EN_CCPU_CLK BIT(1) + +#define RIVA_PMU_OVRD_VAL 0x30 +#define RIVA_PMU_OVRD_VAL_CCPU_RESET BIT(0) +#define RIVA_PMU_OVRD_VAL_CCPU_CLK BIT(1) + +#define RIVA_PMU_CCPU_CTL 0x9C +#define RIVA_PMU_CCPU_CTL_HIGH_IVT BIT(0) +#define RIVA_PMU_CCPU_CTL_REMAP_EN BIT(2) + +#define RIVA_PMU_CCPU_BOOT_REMAP_ADDR 0xA0 + +#define RIVA_PLL_MODE (MSM_CLK_CTL_BASE + 0x31A0) +#define PLL_MODE_OUTCTRL BIT(0) +#define PLL_MODE_BYPASSNL BIT(1) +#define PLL_MODE_RESET_N BIT(2) +#define PLL_MODE_REF_XO_SEL 0x30 +#define PLL_MODE_REF_XO_SEL_CXO (2 << 4) +#define PLL_MODE_REF_XO_SEL_RF (3 << 4) +#define RIVA_PLL_L_VAL (MSM_CLK_CTL_BASE + 0x31A4) +#define RIVA_PLL_M_VAL (MSM_CLK_CTL_BASE + 0x31A8) +#define RIVA_PLL_N_VAL (MSM_CLK_CTL_BASE + 0x31Ac) +#define RIVA_PLL_CONFIG (MSM_CLK_CTL_BASE + 0x31B4) +#define RIVA_PLL_STATUS (MSM_CLK_CTL_BASE + 0x31B8) +#define RIVA_RESET (MSM_CLK_CTL_BASE + 0x35E0) + +#define RIVA_PMU_ROOT_CLK_SEL 0xC8 +#define RIVA_PMU_ROOT_CLK_SEL_3 BIT(2) + +#define RIVA_PMU_CLK_ROOT3 0x78 +#define RIVA_PMU_CLK_ROOT3_ENA BIT(0) +#define RIVA_PMU_CLK_ROOT3_SRC0_DIV 0x3C +#define RIVA_PMU_CLK_ROOT3_SRC0_DIV_2 (1 << 2) +#define RIVA_PMU_CLK_ROOT3_SRC0_SEL 0x1C0 +#define RIVA_PMU_CLK_ROOT3_SRC0_SEL_RIVA (1 << 6) +#define RIVA_PMU_CLK_ROOT3_SRC1_DIV 0x1E00 +#define RIVA_PMU_CLK_ROOT3_SRC1_DIV_2 (1 << 9) +#define RIVA_PMU_CLK_ROOT3_SRC1_SEL 0xE000 +#define RIVA_PMU_CLK_ROOT3_SRC1_SEL_RIVA (1 << 13) + +struct riva_data { + void __iomem *base; + unsigned long start_addr; + struct clk *xo; + struct regulator *pll_supply; + struct pil_device *pil; +}; + +static bool cxo_is_needed(struct riva_data *drv) +{ + u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG); + return (reg & RIVA_PMU_CFG_IRIS_XO_MODE) + != RIVA_PMU_CFG_IRIS_XO_MODE_48; +} + +static int pil_riva_make_proxy_vote(struct pil_desc *pil) +{ + struct riva_data *drv = dev_get_drvdata(pil->dev); + int ret; + + ret = regulator_enable(drv->pll_supply); + if (ret) { + dev_err(pil->dev, "failed to enable pll supply\n"); + goto err; + } + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "failed to enable xo\n"); + goto err_clk; + } + return 0; +err_clk: + regulator_disable(drv->pll_supply); +err: + return ret; +} + +static void pil_riva_remove_proxy_vote(struct pil_desc *pil) +{ + struct riva_data *drv = dev_get_drvdata(pil->dev); + regulator_disable(drv->pll_supply); + clk_disable_unprepare(drv->xo); +} + +static int pil_riva_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata; + struct riva_data *drv = dev_get_drvdata(pil->dev); + drv->start_addr = ehdr->e_entry; + return 0; +} + +static int pil_riva_reset(struct pil_desc *pil) +{ + u32 reg, sel; + struct riva_data *drv = dev_get_drvdata(pil->dev); + void __iomem *base = drv->base; + unsigned long start_addr = drv->start_addr; + bool use_cxo = cxo_is_needed(drv); + + /* Enable A2XB bridge */ + reg = readl_relaxed(base + RIVA_PMU_A2XB_CFG); + reg |= RIVA_PMU_A2XB_CFG_EN; + writel_relaxed(reg, base + RIVA_PMU_A2XB_CFG); + + /* Program PLL 13 to 960 MHz */ + reg = readl_relaxed(RIVA_PLL_MODE); + reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N); + writel_relaxed(reg, RIVA_PLL_MODE); + + if (use_cxo) + writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL); + else + writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL); + writel_relaxed(0, RIVA_PLL_M_VAL); + writel_relaxed(1, RIVA_PLL_N_VAL); + writel_relaxed(0x01495227, RIVA_PLL_CONFIG); + + reg = readl_relaxed(RIVA_PLL_MODE); + reg &= ~(PLL_MODE_REF_XO_SEL); + reg |= use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF; + writel_relaxed(reg, RIVA_PLL_MODE); + + /* Enable PLL 13 */ + reg |= PLL_MODE_BYPASSNL; + writel_relaxed(reg, RIVA_PLL_MODE); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + mb(); + usleep_range(10, 20); + + reg |= PLL_MODE_RESET_N; + writel_relaxed(reg, RIVA_PLL_MODE); + reg |= PLL_MODE_OUTCTRL; + writel_relaxed(reg, RIVA_PLL_MODE); + + /* Wait for PLL to settle */ + mb(); + usleep_range(50, 100); + + /* Configure cCPU for 240 MHz */ + sel = readl_relaxed(base + RIVA_PMU_ROOT_CLK_SEL); + reg = readl_relaxed(base + RIVA_PMU_CLK_ROOT3); + if (sel & RIVA_PMU_ROOT_CLK_SEL_3) { + reg &= ~(RIVA_PMU_CLK_ROOT3_SRC0_SEL | + RIVA_PMU_CLK_ROOT3_SRC0_DIV); + reg |= RIVA_PMU_CLK_ROOT3_SRC0_SEL_RIVA | + RIVA_PMU_CLK_ROOT3_SRC0_DIV_2; + } else { + reg &= ~(RIVA_PMU_CLK_ROOT3_SRC1_SEL | + RIVA_PMU_CLK_ROOT3_SRC1_DIV); + reg |= RIVA_PMU_CLK_ROOT3_SRC1_SEL_RIVA | + RIVA_PMU_CLK_ROOT3_SRC1_DIV_2; + } + writel_relaxed(reg, base + RIVA_PMU_CLK_ROOT3); + reg |= RIVA_PMU_CLK_ROOT3_ENA; + writel_relaxed(reg, base + RIVA_PMU_CLK_ROOT3); + reg = readl_relaxed(base + RIVA_PMU_ROOT_CLK_SEL); + reg ^= RIVA_PMU_ROOT_CLK_SEL_3; + writel_relaxed(reg, base + RIVA_PMU_ROOT_CLK_SEL); + + /* Use the high vector table */ + reg = readl_relaxed(base + RIVA_PMU_CCPU_CTL); + reg |= RIVA_PMU_CCPU_CTL_HIGH_IVT | RIVA_PMU_CCPU_CTL_REMAP_EN; + writel_relaxed(reg, base + RIVA_PMU_CCPU_CTL); + + /* Set base memory address */ + writel_relaxed(start_addr >> 16, base + RIVA_PMU_CCPU_BOOT_REMAP_ADDR); + + /* Clear warmboot bit indicating this is a cold boot */ + reg = readl_relaxed(base + RIVA_PMU_CFG); + reg &= ~(RIVA_PMU_CFG_WARM_BOOT); + writel_relaxed(reg, base + RIVA_PMU_CFG); + + /* Enable the cCPU clock */ + reg = readl_relaxed(base + RIVA_PMU_OVRD_VAL); + reg |= RIVA_PMU_OVRD_VAL_CCPU_CLK; + writel_relaxed(reg, base + RIVA_PMU_OVRD_VAL); + + /* Take cCPU out of reset */ + reg |= RIVA_PMU_OVRD_VAL_CCPU_RESET; + writel_relaxed(reg, base + RIVA_PMU_OVRD_VAL); + + return 0; +} + +static int pil_riva_shutdown(struct pil_desc *pil) +{ + struct riva_data *drv = dev_get_drvdata(pil->dev); + u32 reg; + + /* Put cCPU and cCPU clock into reset */ + reg = readl_relaxed(drv->base + RIVA_PMU_OVRD_VAL); + reg &= ~(RIVA_PMU_OVRD_VAL_CCPU_RESET | RIVA_PMU_OVRD_VAL_CCPU_CLK); + writel_relaxed(reg, drv->base + RIVA_PMU_OVRD_VAL); + reg = readl_relaxed(drv->base + RIVA_PMU_OVRD_EN); + reg |= RIVA_PMU_OVRD_EN_CCPU_RESET | RIVA_PMU_OVRD_EN_CCPU_CLK; + writel_relaxed(reg, drv->base + RIVA_PMU_OVRD_EN); + mb(); + + /* Assert reset to Riva */ + writel_relaxed(1, RIVA_RESET); + mb(); + usleep_range(1000, 2000); + + /* Deassert reset to Riva */ + writel_relaxed(0, RIVA_RESET); + mb(); + + return 0; +} + +static struct pil_reset_ops pil_riva_ops = { + .init_image = pil_riva_init_image, + .auth_and_reset = pil_riva_reset, + .shutdown = pil_riva_shutdown, + .proxy_vote = pil_riva_make_proxy_vote, + .proxy_unvote = pil_riva_remove_proxy_vote, +}; + +static int pil_riva_init_image_trusted(struct pil_desc *pil, + const u8 *metadata, size_t size) +{ + return pas_init_image(PAS_RIVA, metadata, size); +} + +static int pil_riva_reset_trusted(struct pil_desc *pil) +{ + return pas_auth_and_reset(PAS_RIVA); +} + +static int pil_riva_shutdown_trusted(struct pil_desc *pil) +{ + return pas_shutdown(PAS_RIVA); +} + +static struct pil_reset_ops pil_riva_ops_trusted = { + .init_image = pil_riva_init_image_trusted, + .auth_and_reset = pil_riva_reset_trusted, + .shutdown = pil_riva_shutdown_trusted, + .proxy_vote = pil_riva_make_proxy_vote, + .proxy_unvote = pil_riva_remove_proxy_vote, +}; + +static int __devinit pil_riva_probe(struct platform_device *pdev) +{ + struct riva_data *drv; + struct resource *res; + struct pil_desc *desc; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!drv->base) + return -ENOMEM; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd"); + if (IS_ERR(drv->pll_supply)) { + dev_err(&pdev->dev, "failed to get pll supply\n"); + return PTR_ERR(drv->pll_supply); + } + if (regulator_count_voltages(drv->pll_supply) > 0) { + ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000); + if (ret) { + dev_err(&pdev->dev, + "failed to set pll supply voltage\n"); + return ret; + } + + ret = regulator_set_optimum_mode(drv->pll_supply, 100000); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to set pll supply optimum mode\n"); + return ret; + } + } + + desc->name = "wcnss"; + desc->dev = &pdev->dev; + desc->owner = THIS_MODULE; + desc->proxy_timeout = 10000; + + if (pas_supported(PAS_RIVA) > 0) { + desc->ops = &pil_riva_ops_trusted; + dev_info(&pdev->dev, "using secure boot\n"); + } else { + desc->ops = &pil_riva_ops; + dev_info(&pdev->dev, "using non-secure boot\n"); + } + + drv->xo = devm_clk_get(&pdev->dev, "cxo"); + if (IS_ERR(drv->xo)) + return PTR_ERR(drv->xo); + + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) + return PTR_ERR(drv->pil); + return 0; +} + +static int __devexit pil_riva_remove(struct platform_device *pdev) +{ + struct riva_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + return 0; +} + +static struct platform_driver pil_riva_driver = { + .probe = pil_riva_probe, + .remove = __devexit_p(pil_riva_remove), + .driver = { + .name = "pil_riva", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_riva_init(void) +{ + return platform_driver_register(&pil_riva_driver); +} +module_init(pil_riva_init); + +static void __exit pil_riva_exit(void) +{ + platform_driver_unregister(&pil_riva_driver); +} +module_exit(pil_riva_exit); + +MODULE_DESCRIPTION("Support for booting RIVA (WCNSS) processors"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-tzapps.c b/arch/arm/mach-msm/pil-tzapps.c new file mode 100644 index 00000000000..2345453809d --- /dev/null +++ b/arch/arm/mach-msm/pil-tzapps.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +static int pil_tzapps_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + return pas_init_image(PAS_TZAPPS, metadata, size); +} + +static int pil_tzapps_reset(struct pil_desc *pil) +{ + return pas_auth_and_reset(PAS_TZAPPS); +} + +static int pil_tzapps_shutdown(struct pil_desc *pil) +{ + return pas_shutdown(PAS_TZAPPS); +} + +static struct pil_reset_ops pil_tzapps_ops = { + .init_image = pil_tzapps_init_image, + .auth_and_reset = pil_tzapps_reset, + .shutdown = pil_tzapps_shutdown, +}; + +static int __devinit pil_tzapps_driver_probe(struct platform_device *pdev) +{ + struct pil_desc *desc; + struct pil_device *pil; + + if (pas_supported(PAS_TZAPPS) < 0) + return -ENOSYS; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + desc->name = "tzapps"; + desc->dev = &pdev->dev; + desc->ops = &pil_tzapps_ops; + desc->owner = THIS_MODULE; + pil = msm_pil_register(desc); + if (IS_ERR(pil)) + return PTR_ERR(pil); + platform_set_drvdata(pdev, pil); + return 0; +} + +static int __devexit pil_tzapps_driver_exit(struct platform_device *pdev) +{ + struct pil_device *pil = platform_get_drvdata(pdev); + msm_pil_unregister(pil); + return 0; +} + +static struct platform_driver pil_tzapps_driver = { + .probe = pil_tzapps_driver_probe, + .remove = __devexit_p(pil_tzapps_driver_exit), + .driver = { + .name = "pil_tzapps", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_tzapps_init(void) +{ + return platform_driver_register(&pil_tzapps_driver); +} +module_init(pil_tzapps_init); + +static void __exit pil_tzapps_exit(void) +{ + platform_driver_unregister(&pil_tzapps_driver); +} +module_exit(pil_tzapps_exit); + +MODULE_DESCRIPTION("Support for booting TZApps images"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/pil-vidc.c b/arch/arm/mach-msm/pil-vidc.c new file mode 100644 index 00000000000..ceb9bcd2969 --- /dev/null +++ b/arch/arm/mach-msm/pil-vidc.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "scm-pas.h" + +struct vidc_data { + struct clk *smmu_iface; + struct clk *core; + struct pil_device *pil; +}; + +static int pil_vidc_init_image(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + return pas_init_image(PAS_VIDC, metadata, size); +} + +static int pil_vidc_reset(struct pil_desc *pil) +{ + int ret; + struct vidc_data *drv = dev_get_drvdata(pil->dev); + + ret = clk_prepare_enable(drv->smmu_iface); + if (ret) + goto err_smmu; + ret = clk_prepare_enable(drv->core); + if (ret) + goto err_core; + ret = pas_auth_and_reset(PAS_VIDC); + + clk_disable_unprepare(drv->core); +err_core: + clk_disable_unprepare(drv->smmu_iface); +err_smmu: + return ret; +} + +static int pil_vidc_shutdown(struct pil_desc *pil) +{ + return pas_shutdown(PAS_VIDC); +} + +static struct pil_reset_ops pil_vidc_ops = { + .init_image = pil_vidc_init_image, + .auth_and_reset = pil_vidc_reset, + .shutdown = pil_vidc_shutdown, +}; + +static int __devinit pil_vidc_driver_probe(struct platform_device *pdev) +{ + struct pil_desc *desc; + struct vidc_data *drv; + int ret; + + if (pas_supported(PAS_VIDC) < 0) + return -ENOSYS; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + drv->smmu_iface = clk_get(&pdev->dev, "smmu_iface_clk"); + if (IS_ERR(drv->smmu_iface)) { + dev_err(&pdev->dev, "failed to get smmu interface clock\n"); + ret = PTR_ERR(drv->smmu_iface); + goto err_smmu; + } + drv->core = clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(drv->core)) { + dev_err(&pdev->dev, "failed to get core clock\n"); + ret = PTR_ERR(drv->core); + goto err_core; + } + + desc->name = "vidc"; + desc->dev = &pdev->dev; + desc->ops = &pil_vidc_ops; + desc->owner = THIS_MODULE; + drv->pil = msm_pil_register(desc); + if (IS_ERR(drv->pil)) { + ret = PTR_ERR(drv->pil); + goto err_register; + } + return 0; + +err_register: + clk_put(drv->core); +err_core: + clk_put(drv->smmu_iface); +err_smmu: + return ret; +} + +static int __devexit pil_vidc_driver_exit(struct platform_device *pdev) +{ + struct vidc_data *drv = platform_get_drvdata(pdev); + msm_pil_unregister(drv->pil); + clk_put(drv->smmu_iface); + clk_put(drv->core); + return 0; +} + +static struct platform_driver pil_vidc_driver = { + .probe = pil_vidc_driver_probe, + .remove = __devexit_p(pil_vidc_driver_exit), + .driver = { + .name = "pil_vidc", + .owner = THIS_MODULE, + }, +}; + +static int __init pil_vidc_init(void) +{ + return platform_driver_register(&pil_vidc_driver); +} +module_init(pil_vidc_init); + +static void __exit pil_vidc_exit(void) +{ + platform_driver_unregister(&pil_vidc_driver); +} +module_exit(pil_vidc_exit); + +MODULE_DESCRIPTION("Support for secure booting vidc images"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/ping_apps_server.c b/arch/arm/mach-msm/ping_apps_server.c new file mode 100644 index 00000000000..0a856003f96 --- /dev/null +++ b/arch/arm/mach-msm/ping_apps_server.c @@ -0,0 +1,605 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * PING APPS SERVER Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* ping server definitions */ + +#define PING_APPS_PROG 0x30000082 +#define PING_APPS_VERS 0x00010001 + +#define PING_APPS_NULL 0 +#define PING_APPS_DATA 4 +#define PING_APPS_REG 2 +#define PING_APPS_UNREG 3 +#define PING_APPS_DATA_CB_REG 6 +#define PING_APPS_DATA_CB_UNREG 5 + +#define PING_APPS_REG_CB 2 +#define PING_APPS_DATA_CB 1 + +static LIST_HEAD(cb_entry_list); +static DEFINE_MUTEX(cb_entry_list_lock); + +static struct task_struct *server_thread; + +struct ping_apps_register_arg { + uint32_t cb_id; + int32_t num; +}; + +struct ping_apps_unregister_arg { + uint32_t cb_id; +}; + +struct ping_apps_register_cb_arg { + uint32_t cb_id; + int32_t num; +}; + +struct ping_apps_register_ret { + uint32_t result; +}; + +struct ping_apps_unregister_ret { + uint32_t result; +}; + +struct ping_apps_data_cb_reg_arg { + uint32_t cb_id; + uint32_t num; + uint32_t size; + uint32_t interval_ms; + uint32_t num_tasks; +}; + +struct ping_apps_data_cb_unreg_arg { + uint32_t cb_id; +}; + +struct ping_apps_data_cb_arg { + uint32_t cb_id; + uint32_t *data; + uint32_t size; + uint32_t sum; +}; + +struct ping_apps_data_cb_reg_ret { + uint32_t result; +}; + +struct ping_apps_data_cb_unreg_ret { + uint32_t result; +}; + +struct ping_apps_data_cb_ret { + uint32_t result; +}; + +struct ping_apps_data_arg { + uint32_t *data; + uint32_t size; +}; + +struct ping_apps_data_ret { + uint32_t result; +}; + +struct ping_apps_data_cb_info { + void *cb_func; + uint32_t size; + uint32_t num_tasks; +}; + +struct ping_apps_cb_entry { + struct list_head list; + + struct msm_rpc_client_info clnt_info; + void *cb_info; + uint32_t cb_id; + int32_t num; + uint32_t interval_ms; + uint32_t time_to_next_cb; + void (*cb_func)(struct ping_apps_cb_entry *); +}; + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr); + +static int ping_apps_data_cb(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + struct ping_apps_data_cb_arg *arg, + struct ping_apps_data_cb_ret *ret); + +static int ping_apps_register_cb(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + struct ping_apps_register_cb_arg *arg, + void *ret); + +static struct msm_rpc_server rpc_server = { + .prog = PING_APPS_PROG, + .vers = PING_APPS_VERS, + .rpc_call2 = handle_rpc_call, +}; + +static void handle_ping_apps_data_cb(struct ping_apps_cb_entry *cb_entry) +{ + struct ping_apps_data_cb_arg arg; + struct ping_apps_data_cb_ret ret; + uint32_t my_sum = 0; + uint32_t *my_data; + int i; + + if (cb_entry->num > 0) { + cb_entry->num--; + arg.cb_id = cb_entry->cb_id; + arg.size = ((struct ping_apps_data_cb_info *) + (cb_entry->cb_info))->size; + + my_data = kmalloc((arg.size * sizeof(uint32_t)), GFP_KERNEL); + if (!my_data) + return; + + for (i = 0; i < arg.size; i++) { + my_data[i] = (42 + i); + my_sum ^= (42 + i); + } + arg.data = my_data; + arg.sum = my_sum; + + ((int (*)(struct msm_rpc_server *, + struct msm_rpc_client_info *, + struct ping_apps_data_cb_arg *, + struct ping_apps_data_cb_ret *)) + ((struct ping_apps_data_cb_info *) + (cb_entry->cb_info))->cb_func)(&rpc_server, + &cb_entry->clnt_info, + &arg, &ret); + pr_info("%s: cb_id = %d, ret = %d\n", + __func__, arg.cb_id, ret.result); + kfree(my_data); + } +} + +static void handle_ping_apps_register_cb(struct ping_apps_cb_entry *cb_entry) +{ + struct ping_apps_register_cb_arg arg; + + if (cb_entry->num > 0) { + cb_entry->num--; + arg.cb_id = cb_entry->cb_id; + arg.num = cb_entry->num; + + pr_info("%s: cb_id = %d, num = %d\n", + __func__, arg.cb_id, arg.num); + ((int (*)(struct msm_rpc_server *, + struct msm_rpc_client_info *, + struct ping_apps_register_cb_arg *, + void *))cb_entry->cb_info)(&rpc_server, + &cb_entry->clnt_info, + &arg, NULL); + } + +} + +static int ping_apps_cb_process_thread(void *data) +{ + struct ping_apps_cb_entry *cb_entry; + uint32_t sleep_time; + uint32_t time_slept = 0; + + pr_info("%s: thread started\n", __func__); + for (;;) { + sleep_time = 1000; + mutex_lock(&cb_entry_list_lock); + list_for_each_entry(cb_entry, &cb_entry_list, list) { + if (cb_entry->time_to_next_cb <= time_slept) { + cb_entry->cb_func(cb_entry); + cb_entry->time_to_next_cb = + cb_entry->interval_ms; + } else + cb_entry->time_to_next_cb -= time_slept; + + if (cb_entry->time_to_next_cb < sleep_time) + sleep_time = cb_entry->time_to_next_cb; + } + mutex_unlock(&cb_entry_list_lock); + + msleep(sleep_time); + time_slept = sleep_time; + } + + do_exit(0); +} + +static int ping_apps_data_register(struct ping_apps_data_arg *arg, + struct ping_apps_data_ret *ret) +{ + int i; + + ret->result = 0; + for (i = 0; i < arg->size; i++) + ret->result ^= arg->data[i]; + + return 0; +} + +static int ping_apps_data_cb_reg(struct ping_apps_data_cb_reg_arg *arg, + struct ping_apps_data_cb_reg_ret *ret) +{ + struct ping_apps_cb_entry *cb_entry; + struct ping_apps_data_cb_info *cb_info; + + cb_entry = kmalloc(sizeof(*cb_entry), GFP_KERNEL); + if (!cb_entry) + return -ENOMEM; + + cb_entry->cb_info = kmalloc(sizeof(struct ping_apps_data_cb_info), + GFP_KERNEL); + if (!cb_entry->cb_info) { + kfree(cb_entry); + return -ENOMEM; + } + cb_info = (struct ping_apps_data_cb_info *)cb_entry->cb_info; + + INIT_LIST_HEAD(&cb_entry->list); + cb_entry->cb_func = handle_ping_apps_data_cb; + cb_entry->cb_id = arg->cb_id; + cb_entry->num = arg->num; + cb_entry->interval_ms = arg->interval_ms; + cb_entry->time_to_next_cb = arg->interval_ms; + cb_info->cb_func = ping_apps_data_cb; + cb_info->size = arg->size; + cb_info->num_tasks = arg->num_tasks; + + mutex_lock(&cb_entry_list_lock); + list_add_tail(&cb_entry->list, &cb_entry_list); + mutex_unlock(&cb_entry_list_lock); + + msm_rpc_server_get_requesting_client(&cb_entry->clnt_info); + + if (IS_ERR(server_thread)) + server_thread = kthread_run(ping_apps_cb_process_thread, + NULL, "kpingrpccbprocessd"); + if (IS_ERR(server_thread)) { + kfree(cb_entry); + return PTR_ERR(server_thread); + } + + ret->result = 1; + return 0; +} + +static int ping_apps_data_cb_unreg(struct ping_apps_data_cb_unreg_arg *arg, + struct ping_apps_data_cb_unreg_ret *ret) +{ + struct ping_apps_cb_entry *cb_entry, *tmp_cb_entry; + + mutex_lock(&cb_entry_list_lock); + list_for_each_entry_safe(cb_entry, tmp_cb_entry, + &cb_entry_list, list) { + if (cb_entry->cb_id == arg->cb_id) { + list_del(&cb_entry->list); + kfree(cb_entry->cb_info); + kfree(cb_entry); + break; + } + } + mutex_unlock(&cb_entry_list_lock); + + ret->result = 1; + return 0; +} + +static int ping_apps_register(struct ping_apps_register_arg *arg, + struct ping_apps_register_ret *ret) +{ + struct ping_apps_cb_entry *cb_entry; + + cb_entry = kmalloc(sizeof(*cb_entry), GFP_KERNEL); + if (!cb_entry) + return -ENOMEM; + + INIT_LIST_HEAD(&cb_entry->list); + cb_entry->cb_func = handle_ping_apps_register_cb; + cb_entry->cb_info = ping_apps_register_cb; + cb_entry->cb_id = arg->cb_id; + cb_entry->num = arg->num; + cb_entry->interval_ms = 100; + cb_entry->time_to_next_cb = 100; + + mutex_lock(&cb_entry_list_lock); + list_add_tail(&cb_entry->list, &cb_entry_list); + mutex_unlock(&cb_entry_list_lock); + + msm_rpc_server_get_requesting_client(&cb_entry->clnt_info); + + if (IS_ERR(server_thread)) + server_thread = kthread_run(ping_apps_cb_process_thread, + NULL, "kpingrpccbprocessd"); + if (IS_ERR(server_thread)) { + kfree(cb_entry); + return PTR_ERR(server_thread); + } + + ret->result = 1; + return 0; +} + +static int ping_apps_unregister(struct ping_apps_unregister_arg *arg, + struct ping_apps_unregister_ret *ret) +{ + struct ping_apps_cb_entry *cb_entry, *tmp_cb_entry; + + mutex_lock(&cb_entry_list_lock); + list_for_each_entry_safe(cb_entry, tmp_cb_entry, + &cb_entry_list, list) { + if (cb_entry->cb_id == arg->cb_id) { + list_del(&cb_entry->list); + kfree(cb_entry); + break; + } + } + mutex_unlock(&cb_entry_list_lock); + + ret->result = 1; + return 0; +} + +static int ping_apps_data_cb_arg_func(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_apps_data_cb_arg *arg = data; + + xdr_send_uint32(xdr, &arg->cb_id); + xdr_send_array(xdr, (void **)&arg->data, &arg->size, 64, + sizeof(uint32_t), (void *)xdr_send_uint32); + xdr_send_uint32(xdr, &arg->size); + xdr_send_uint32(xdr, &arg->sum); + + return 0; +} + +static int ping_apps_data_cb_ret_func(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_apps_data_cb_ret *ret = data; + + xdr_recv_uint32(xdr, &ret->result); + + return 0; +} + +static int ping_apps_data_cb(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + struct ping_apps_data_cb_arg *arg, + struct ping_apps_data_cb_ret *ret) +{ + return msm_rpc_server_cb_req2(server, clnt_info, + PING_APPS_DATA_CB, + ping_apps_data_cb_arg_func, arg, + ping_apps_data_cb_ret_func, ret, -1); +} + +static int ping_apps_register_cb_arg(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_apps_register_cb_arg *arg = data; + + xdr_send_uint32(xdr, &arg->cb_id); + xdr_send_int32(xdr, &arg->num); + + return 0; +} + +static int ping_apps_register_cb(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + struct ping_apps_register_cb_arg *arg, + void *ret) +{ + return msm_rpc_server_cb_req2(server, clnt_info, + PING_APPS_REG_CB, + ping_apps_register_cb_arg, + arg, NULL, NULL, -1); +} + +static int handle_ping_apps_data_register(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + uint32_t rc; + struct ping_apps_data_arg arg; + struct ping_apps_data_ret ret; + + pr_info("%s: request received\n", __func__); + + xdr_recv_array(xdr, (void **)&arg.data, &arg.size, 64, + sizeof(uint32_t), (void *)xdr_recv_uint32); + xdr_recv_uint32(xdr, &arg.size); + + rc = ping_apps_data_register(&arg, &ret); + if (rc < 0) + goto free_and_return; + + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); + xdr_send_uint32(xdr, &ret.result); + rc = xdr_send_msg(xdr); + if (rc < 0) + pr_info("%s: sending reply failed\n", __func__); + else + rc = 1; + + free_and_return: + kfree(arg.data); + return rc; +} + +static int handle_ping_apps_data_cb_reg(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + uint32_t rc; + struct ping_apps_data_cb_reg_arg arg; + struct ping_apps_data_cb_reg_ret ret; + + pr_info("%s: request received\n", __func__); + + xdr_recv_uint32(xdr, &arg.cb_id); + xdr_recv_uint32(xdr, &arg.num); + xdr_recv_uint32(xdr, &arg.size); + xdr_recv_uint32(xdr, &arg.interval_ms); + xdr_recv_uint32(xdr, &arg.num_tasks); + + rc = ping_apps_data_cb_reg(&arg, &ret); + if (rc < 0) + return rc; + + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); + xdr_send_uint32(xdr, &ret.result); + rc = xdr_send_msg(xdr); + if (rc < 0) + pr_info("%s: sending reply failed\n", __func__); + else + rc = 1; + + return rc; +} + +static int handle_ping_apps_data_cb_unreg(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + uint32_t rc; + struct ping_apps_data_cb_unreg_arg arg; + struct ping_apps_data_cb_unreg_ret ret; + + pr_info("%s: request received\n", __func__); + + xdr_recv_uint32(xdr, &arg.cb_id); + + rc = ping_apps_data_cb_unreg(&arg, &ret); + if (rc < 0) + return rc; + + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); + xdr_send_uint32(xdr, &ret.result); + rc = xdr_send_msg(xdr); + if (rc < 0) + pr_info("%s: sending reply failed\n", __func__); + else + rc = 1; + + return rc; +} + +static int handle_ping_apps_register(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + uint32_t rc; + struct ping_apps_register_arg arg; + struct ping_apps_register_ret ret; + + pr_info("%s: request received\n", __func__); + + xdr_recv_uint32(xdr, &arg.cb_id); + xdr_recv_int32(xdr, &arg.num); + + rc = ping_apps_register(&arg, &ret); + if (rc < 0) + return rc; + + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); + xdr_send_uint32(xdr, &ret.result); + rc = xdr_send_msg(xdr); + if (rc < 0) + pr_info("%s: sending reply failed\n", __func__); + else + rc = 1; + + return rc; +} + +static int handle_ping_apps_unregister(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + uint32_t rc; + struct ping_apps_unregister_arg arg; + struct ping_apps_unregister_ret ret; + + pr_info("%s: request received\n", __func__); + + xdr_recv_uint32(xdr, &arg.cb_id); + + rc = ping_apps_unregister(&arg, &ret); + if (rc < 0) + return rc; + + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_SUCCESS); + xdr_send_uint32(xdr, &ret.result); + rc = xdr_send_msg(xdr); + if (rc < 0) + pr_info("%s: sending reply failed\n", __func__); + else + rc = 1; + + return rc; +} + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + switch (req->procedure) { + case PING_APPS_NULL: + pr_info("%s: null procedure request received\n", __func__); + return 0; + case PING_APPS_DATA: + return handle_ping_apps_data_register(server, req, xdr); + case PING_APPS_REG: + return handle_ping_apps_register(server, req, xdr); + case PING_APPS_UNREG: + return handle_ping_apps_unregister(server, req, xdr); + case PING_APPS_DATA_CB_REG: + return handle_ping_apps_data_cb_reg(server, req, xdr); + case PING_APPS_DATA_CB_UNREG: + return handle_ping_apps_data_cb_unreg(server, req, xdr); + default: + return -ENODEV; + } +} + +static int __init ping_apps_server_init(void) +{ + INIT_LIST_HEAD(&cb_entry_list); + server_thread = ERR_PTR(-1); + return msm_rpc_create_server2(&rpc_server); +} + +module_init(ping_apps_server_init); + +MODULE_DESCRIPTION("PING APPS SERVER Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/ping_mdm_rpc_client.c b/arch/arm/mach-msm/ping_mdm_rpc_client.c new file mode 100644 index 00000000000..57ac85d50e2 --- /dev/null +++ b/arch/arm/mach-msm/ping_mdm_rpc_client.c @@ -0,0 +1,814 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SMD RPC PING MODEM Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PING_TEST_BASE 0x31 + +#define PTIOC_NULL_TEST _IO(PING_TEST_BASE, 1) +#define PTIOC_REG_TEST _IO(PING_TEST_BASE, 2) +#define PTIOC_DATA_REG_TEST _IO(PING_TEST_BASE, 3) +#define PTIOC_DATA_CB_REG_TEST _IO(PING_TEST_BASE, 4) + +#define PING_MDM_PROG 0x30000081 +#define PING_MDM_VERS 0x00010001 +#define PING_MDM_CB_PROG 0x31000081 +#define PING_MDM_CB_VERS 0x00010001 + +#define PING_MDM_NULL_PROC 0 +#define PING_MDM_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define PING_MDM_REGISTER_PROC 2 +#define PING_MDM_UNREGISTER_PROC 3 +#define PING_MDM_REGISTER_DATA_PROC 4 +#define PING_MDM_UNREGISTER_DATA_CB_PROC 5 +#define PING_MDM_REGISTER_DATA_CB_PROC 6 + +#define PING_MDM_DATA_CB_PROC 1 +#define PING_MDM_CB_PROC 2 + +#define PING_MAX_RETRY 5 + +static struct msm_rpc_client *rpc_client; +static uint32_t open_count; +static DEFINE_MUTEX(ping_mdm_lock); + +struct ping_mdm_register_cb_arg { + uint32_t cb_id; + int val; +}; + +struct ping_mdm_register_data_cb_cb_arg { + uint32_t cb_id; + uint32_t *data; + uint32_t size; + uint32_t sum; +}; + +struct ping_mdm_register_data_cb_cb_ret { + uint32_t result; +}; + +static struct dentry *dent; +static uint32_t test_res; +static int reg_cb_num, reg_cb_num_req; +static int data_cb_num, data_cb_num_req; +static int reg_done_flag, data_cb_done_flag; +static DECLARE_WAIT_QUEUE_HEAD(reg_test_wait); +static DECLARE_WAIT_QUEUE_HEAD(data_cb_test_wait); + +enum { + PING_MODEM_NOT_IN_RESET = 0, + PING_MODEM_IN_RESET, + PING_LEAVING_RESET, + PING_MODEM_REGISTER_CB +}; +static int fifo_event; +static DEFINE_MUTEX(event_fifo_lock); +static DEFINE_KFIFO(event_fifo, int, sizeof(int)*16); + +static int ping_mdm_register_cb(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr) +{ + int rc; + uint32_t accept_status; + struct ping_mdm_register_cb_arg arg; + void *cb_func; + + xdr_recv_uint32(xdr, &arg.cb_id); /* cb_id */ + xdr_recv_int32(xdr, &arg.val); /* val */ + + cb_func = msm_rpc_get_cb_func(client, arg.cb_id); + if (cb_func) { + rc = ((int (*)(struct ping_mdm_register_cb_arg *, void *)) + cb_func)(&arg, NULL); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + xdr_start_accepted_reply(xdr, accept_status); + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int ping_mdm_data_cb(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr) +{ + int rc; + void *cb_func; + uint32_t size, accept_status; + struct ping_mdm_register_data_cb_cb_arg arg; + struct ping_mdm_register_data_cb_cb_ret ret; + + xdr_recv_uint32(xdr, &arg.cb_id); /* cb_id */ + + /* data */ + xdr_recv_array(xdr, (void **)(&(arg.data)), &size, 64, + sizeof(uint32_t), (void *)xdr_recv_uint32); + + xdr_recv_uint32(xdr, &arg.size); /* size */ + xdr_recv_uint32(xdr, &arg.sum); /* sum */ + + cb_func = msm_rpc_get_cb_func(client, arg.cb_id); + if (cb_func) { + rc = ((int (*) + (struct ping_mdm_register_data_cb_cb_arg *, + struct ping_mdm_register_data_cb_cb_ret *)) + cb_func)(&arg, &ret); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + xdr_start_accepted_reply(xdr, accept_status); + + if (accept_status == RPC_ACCEPTSTAT_SUCCESS) + xdr_send_uint32(xdr, &ret.result); /* result */ + + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + kfree(arg.data); + return rc; +} + +static int ping_mdm_cb_func(struct msm_rpc_client *client, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + int rc = 0; + + switch (req->procedure) { + case PING_MDM_CB_PROC: + rc = ping_mdm_register_cb(client, xdr); + break; + case PING_MDM_DATA_CB_PROC: + rc = ping_mdm_data_cb(client, xdr); + break; + default: + pr_err("%s: procedure not supported %d\n", + __func__, req->procedure); + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_PROC_UNAVAIL); + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: sending reply failed: %d\n", __func__, rc); + break; + } + return rc; +} + +struct ping_mdm_unregister_data_cb_arg { + int (*cb_func)( + struct ping_mdm_register_data_cb_cb_arg *arg, + struct ping_mdm_register_data_cb_cb_ret *ret); +}; + +struct ping_mdm_register_data_cb_arg { + int (*cb_func)( + struct ping_mdm_register_data_cb_cb_arg *arg, + struct ping_mdm_register_data_cb_cb_ret *ret); + uint32_t num; + uint32_t size; + uint32_t interval_ms; + uint32_t num_tasks; +}; + +struct ping_mdm_register_data_cb_ret { + uint32_t result; +}; + +struct ping_mdm_unregister_data_cb_ret { + uint32_t result; +}; + +static int ping_mdm_data_cb_register_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_register_data_cb_arg *arg = data; + int cb_id; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + xdr_send_uint32(xdr, &cb_id); /* cb_id */ + xdr_send_uint32(xdr, &arg->num); /* num */ + xdr_send_uint32(xdr, &arg->size); /* size */ + xdr_send_uint32(xdr, &arg->interval_ms); /* interval_ms */ + xdr_send_uint32(xdr, &arg->num_tasks); /* num_tasks */ + + return 0; +} + +static int ping_mdm_data_cb_unregister_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_unregister_data_cb_arg *arg = data; + int cb_id; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + xdr_send_uint32(xdr, &cb_id); /* cb_id */ + + return 0; +} + +static int ping_mdm_data_cb_register_ret(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_register_data_cb_ret *ret = data; + + xdr_recv_uint32(xdr, &ret->result); /* result */ + + return 0; +} + +static int ping_mdm_register_data_cb( + struct msm_rpc_client *client, + struct ping_mdm_register_data_cb_arg *arg, + struct ping_mdm_register_data_cb_ret *ret) +{ + return msm_rpc_client_req2(client, + PING_MDM_REGISTER_DATA_CB_PROC, + ping_mdm_data_cb_register_arg, arg, + ping_mdm_data_cb_register_ret, ret, -1); +} + +static int ping_mdm_unregister_data_cb( + struct msm_rpc_client *client, + struct ping_mdm_unregister_data_cb_arg *arg, + struct ping_mdm_unregister_data_cb_ret *ret) +{ + return msm_rpc_client_req2(client, + PING_MDM_UNREGISTER_DATA_CB_PROC, + ping_mdm_data_cb_unregister_arg, arg, + ping_mdm_data_cb_register_ret, ret, -1); +} + +struct ping_mdm_data_arg { + uint32_t *data; + uint32_t size; +}; + +struct ping_mdm_data_ret { + uint32_t result; +}; + +static int ping_mdm_data_register_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_data_arg *arg = data; + + /* data */ + xdr_send_array(xdr, (void **)&arg->data, &arg->size, 64, + sizeof(uint32_t), (void *)xdr_send_uint32); + + xdr_send_uint32(xdr, &arg->size); /* size */ + + return 0; +} + +static int ping_mdm_data_register_ret(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_data_ret *ret = data; + + xdr_recv_uint32(xdr, &ret->result); /* result */ + + return 0; +} + +static int ping_mdm_data_register( + struct msm_rpc_client *client, + struct ping_mdm_data_arg *arg, + struct ping_mdm_data_ret *ret) +{ + return msm_rpc_client_req2(client, + PING_MDM_REGISTER_DATA_PROC, + ping_mdm_data_register_arg, arg, + ping_mdm_data_register_ret, ret, -1); +} + +struct ping_mdm_register_arg { + int (*cb_func)(struct ping_mdm_register_cb_arg *, void *); + int num; +}; + +struct ping_mdm_unregister_arg { + int (*cb_func)(struct ping_mdm_register_cb_arg *, void *); +}; + +struct ping_mdm_register_ret { + uint32_t result; +}; + +struct ping_mdm_unregister_ret { + uint32_t result; +}; + +static int ping_mdm_register_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_register_arg *arg = data; + int cb_id; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + xdr_send_uint32(xdr, &cb_id); /* cb_id */ + xdr_send_uint32(xdr, &arg->num); /* num */ + + return 0; +} + +static int ping_mdm_unregister_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_unregister_arg *arg = data; + int cb_id; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + xdr_send_uint32(xdr, &cb_id); /* cb_id */ + + return 0; +} + +static int ping_mdm_register_ret(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct ping_mdm_register_ret *ret = data; + + xdr_recv_uint32(xdr, &ret->result); /* result */ + + return 0; +} + +static int ping_mdm_register( + struct msm_rpc_client *client, + struct ping_mdm_register_arg *arg, + struct ping_mdm_register_ret *ret) +{ + return msm_rpc_client_req2(client, + PING_MDM_REGISTER_PROC, + ping_mdm_register_arg, arg, + ping_mdm_register_ret, ret, -1); +} + +static int ping_mdm_unregister( + struct msm_rpc_client *client, + struct ping_mdm_unregister_arg *arg, + struct ping_mdm_unregister_ret *ret) +{ + return msm_rpc_client_req2(client, + PING_MDM_UNREGISTER_PROC, + ping_mdm_unregister_arg, arg, + ping_mdm_register_ret, ret, -1); +} + +static int ping_mdm_null(struct msm_rpc_client *client, + void *arg, void *ret) +{ + return msm_rpc_client_req2(client, PING_MDM_NULL_PROC, + NULL, NULL, NULL, NULL, -1); +} + +static int ping_mdm_close(void) +{ + mutex_lock(&ping_mdm_lock); + if (--open_count == 0) { + msm_rpc_unregister_client(rpc_client); + pr_info("%s: disconnected from remote ping server\n", + __func__); + } + mutex_unlock(&ping_mdm_lock); + return 0; +} + +static void handle_restart_teardown(struct msm_rpc_client *client) +{ + int event = PING_MODEM_IN_RESET; + + pr_info("%s: modem in reset\n", __func__); + + mutex_lock(&event_fifo_lock); + kfifo_in(&event_fifo, &event, sizeof(event)); + fifo_event = 1; + mutex_unlock(&event_fifo_lock); + + wake_up(&data_cb_test_wait); +} + +static void handle_restart_setup(struct msm_rpc_client *client) +{ + int event = PING_LEAVING_RESET; + + pr_info("%s: modem leaving reset\n", __func__); + + mutex_lock(&event_fifo_lock); + kfifo_in(&event_fifo, &event, sizeof(event)); + fifo_event = 1; + mutex_unlock(&event_fifo_lock); + + wake_up(&data_cb_test_wait); +} + +static struct msm_rpc_client *ping_mdm_init(void) +{ + mutex_lock(&ping_mdm_lock); + if (open_count == 0) { + rpc_client = msm_rpc_register_client2("pingdef", + PING_MDM_PROG, + PING_MDM_VERS, 1, + ping_mdm_cb_func); + if (!IS_ERR(rpc_client)) { + open_count++; + msm_rpc_register_reset_callbacks(rpc_client, + handle_restart_teardown, + handle_restart_setup); + } + } + mutex_unlock(&ping_mdm_lock); + return rpc_client; +} + +static int ping_mdm_data_register_test(void) +{ + int i, rc = 0; + uint32_t my_data[64]; + uint32_t my_sum = 0; + struct ping_mdm_data_arg data_arg; + struct ping_mdm_data_ret data_ret; + + for (i = 0; i < 64; i++) { + my_data[i] = (42 + i); + my_sum ^= (42 + i); + } + + data_arg.data = my_data; + data_arg.size = 64; + + rc = ping_mdm_data_register(rpc_client, &data_arg, &data_ret); + if (rc) + return rc; + + if (my_sum != data_ret.result) { + pr_err("%s: sum mismatch %d %d\n", + __func__, my_sum, data_ret.result); + rc = -1; + } + + return rc; +} + +static int ping_mdm_test_register_data_cb( + struct ping_mdm_register_data_cb_cb_arg *arg, + struct ping_mdm_register_data_cb_cb_ret *ret) +{ + uint32_t i, sum = 0; + + data_cb_num++; + + pr_info("%s: received cb_id %d, size = %d, sum = %u, num = %u of %u\n", + __func__, arg->cb_id, arg->size, arg->sum, data_cb_num, + data_cb_num_req); + + if (arg->data) + for (i = 0; i < arg->size; i++) + sum ^= arg->data[i]; + + if (sum != arg->sum) + pr_err("%s: sum mismatch %u %u\n", __func__, sum, arg->sum); + + if (data_cb_num == data_cb_num_req) { + data_cb_done_flag = 1; + wake_up(&data_cb_test_wait); + } + + ret->result = 1; + return 0; +} + +static int ping_mdm_data_cb_register( + struct ping_mdm_register_data_cb_ret *reg_ret) +{ + int rc; + struct ping_mdm_register_data_cb_arg reg_arg; + + reg_arg.cb_func = ping_mdm_test_register_data_cb; + reg_arg.num = data_cb_num_req - data_cb_num; + reg_arg.size = 64; + reg_arg.interval_ms = 10; + reg_arg.num_tasks = 1; + + pr_info("%s: registering callback\n", __func__); + rc = ping_mdm_register_data_cb(rpc_client, ®_arg, reg_ret); + if (rc) + pr_err("%s: failed to register callback %d\n", __func__, rc); + + return rc; +} + + +static void retry_timer_cb(unsigned long data) +{ + int event = (int)data; + + pr_info("%s: retry timer triggered\n", __func__); + + mutex_lock(&event_fifo_lock); + kfifo_in(&event_fifo, &event, sizeof(event)); + fifo_event = 1; + mutex_unlock(&event_fifo_lock); + + wake_up(&data_cb_test_wait); +} + +static int ping_mdm_data_cb_register_test(void) +{ + int rc; + int event; + int retry_count = 0; + struct ping_mdm_register_data_cb_ret reg_ret; + struct ping_mdm_unregister_data_cb_arg unreg_arg; + struct ping_mdm_unregister_data_cb_ret unreg_ret; + struct timer_list retry_timer; + + mutex_init(&event_fifo_lock); + init_timer(&retry_timer); + + data_cb_done_flag = 0; + data_cb_num = 0; + if (!data_cb_num_req) + data_cb_num_req = 10; + + rc = ping_mdm_data_cb_register(®_ret); + if (rc) + return rc; + + pr_info("%s: data_cb_register result: 0x%x\n", + __func__, reg_ret.result); + + while (!data_cb_done_flag) { + wait_event(data_cb_test_wait, data_cb_done_flag || fifo_event); + fifo_event = 0; + + for (;;) { + mutex_lock(&event_fifo_lock); + + if (kfifo_is_empty(&event_fifo)) { + mutex_unlock(&event_fifo_lock); + break; + } + rc = kfifo_out(&event_fifo, &event, sizeof(event)); + mutex_unlock(&event_fifo_lock); + BUG_ON(rc != sizeof(event)); + + pr_info("%s: processing event data_cb_done_flag=%d,event=%d\n", + __func__, data_cb_done_flag, event); + + if (event == PING_MODEM_IN_RESET) { + pr_info("%s: modem entering reset\n", __func__); + retry_count = 0; + } else if (event == PING_LEAVING_RESET) { + pr_info("%s: modem exiting reset - " + "re-registering cb\n", __func__); + + rc = ping_mdm_data_cb_register(®_ret); + if (rc) { + retry_count++; + if (retry_count < PING_MAX_RETRY) { + pr_info("%s: retry %d failed\n", + __func__, retry_count); + + retry_timer.expires = jiffies + + msecs_to_jiffies(1000); + retry_timer.data = + PING_LEAVING_RESET; + retry_timer.function = + retry_timer_cb; + add_timer(&retry_timer); + } else { + pr_err("%s: max retries exceeded, aborting\n", + __func__); + return -ENETRESET; + } + } else + pr_info("%s: data_cb_register result: 0x%x\n", + __func__, reg_ret.result); + } + } + } + + while (del_timer(&retry_timer)) + ; + + unreg_arg.cb_func = ping_mdm_test_register_data_cb; + rc = ping_mdm_unregister_data_cb(rpc_client, &unreg_arg, &unreg_ret); + if (rc) + return rc; + + pr_info("%s: data_cb_unregister result: 0x%x\n", + __func__, unreg_ret.result); + + pr_info("%s: Test completed\n", __func__); + + return 0; +} + +static int ping_mdm_test_register_cb( + struct ping_mdm_register_cb_arg *arg, void *ret) +{ + pr_info("%s: received cb_id %d, val = %d\n", + __func__, arg->cb_id, arg->val); + + reg_cb_num++; + if (reg_cb_num == reg_cb_num_req) { + reg_done_flag = 1; + wake_up(®_test_wait); + } + return 0; +} + +static int ping_mdm_register_test(void) +{ + int rc = 0; + struct ping_mdm_register_arg reg_arg; + struct ping_mdm_unregister_arg unreg_arg; + struct ping_mdm_register_ret reg_ret; + struct ping_mdm_unregister_ret unreg_ret; + + reg_cb_num = 0; + reg_cb_num_req = 10; + reg_done_flag = 0; + + reg_arg.num = 10; + reg_arg.cb_func = ping_mdm_test_register_cb; + + rc = ping_mdm_register(rpc_client, ®_arg, ®_ret); + if (rc) + return rc; + + pr_info("%s: register result: 0x%x\n", + __func__, reg_ret.result); + + wait_event(reg_test_wait, reg_done_flag); + + unreg_arg.cb_func = ping_mdm_test_register_cb; + rc = ping_mdm_unregister(rpc_client, &unreg_arg, &unreg_ret); + if (rc) + return rc; + + pr_info("%s: unregister result: 0x%x\n", + __func__, unreg_ret.result); + + return 0; +} + +static int ping_mdm_null_test(void) +{ + return ping_mdm_null(rpc_client, NULL, NULL); +} + +static int ping_test_release(struct inode *ip, struct file *fp) +{ + return ping_mdm_close(); +} + +static int ping_test_open(struct inode *ip, struct file *fp) +{ + struct msm_rpc_client *client; + + client = ping_mdm_init(); + if (IS_ERR(client)) { + pr_err("%s: couldn't open ping client\n", __func__); + return PTR_ERR(client); + } else + pr_info("%s: connected to remote ping server\n", + __func__); + + return 0; +} + +static ssize_t ping_test_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char _buf[16]; + + snprintf(_buf, sizeof(_buf), "%i\n", test_res); + + return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf)); +} + +static ssize_t ping_test_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + unsigned char cmd[64]; + int len; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "null_test", 64)) + test_res = ping_mdm_null_test(); + else if (!strncmp(cmd, "reg_test", 64)) + test_res = ping_mdm_register_test(); + else if (!strncmp(cmd, "data_reg_test", 64)) + test_res = ping_mdm_data_register_test(); + else if (!strncmp(cmd, "data_cb_reg_test", 64)) + test_res = ping_mdm_data_cb_register_test(); + else if (!strncmp(cmd, "count=", 6)) { + long tmp; + + if (strict_strtol(cmd + 6, 0, &tmp) == 0) { + data_cb_num_req = tmp; + pr_info("Set repetition count to %d\n", + data_cb_num_req); + } else { + data_cb_num_req = 10; + pr_err("invalid number %s, defaulting to %d\n", + cmd + 6, data_cb_num_req); + } + } + else + test_res = -EINVAL; + + return count; +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = ping_test_open, + .read = ping_test_read, + .write = ping_test_write, + .release = ping_test_release, +}; + +static void __exit ping_test_exit(void) +{ + debugfs_remove(dent); +} + +static int __init ping_test_init(void) +{ + dent = debugfs_create_file("ping_mdm", 0444, 0, NULL, &debug_ops); + test_res = 0; + open_count = 0; + return 0; +} + +module_init(ping_test_init); +module_exit(ping_test_exit); + +MODULE_DESCRIPTION("PING TEST Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/platsmp-8625.c b/arch/arm/mach-msm/platsmp-8625.c new file mode 100644 index 00000000000..915047a380c --- /dev/null +++ b/arch/arm/mach-msm/platsmp-8625.c @@ -0,0 +1,295 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "pm.h" + +#define MSM_CORE1_RESET 0xA8600590 +#define MSM_CORE1_STATUS_MSK 0x02800000 + +/* + * control for which core is the next to come out of the secondary + * boot "holding pen" + */ +int pen_release = -1; + +static bool cold_boot_done; + +static uint32_t *msm8625_boot_vector; +static void __iomem *reset_core1_base; + +/* + * Write pen_release in a way that is guaranteed to be visible to all + * observers, irrespective of whether they're taking part in coherency + * or not. This is necessary for the hotplug code to work reliably. + */ +static void __cpuinit write_pen_release(int val) +{ + pen_release = val; + smp_wmb(); + __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); + outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); +} + +static void __iomem *scu_base_addr(void) +{ + return MSM_SCU_BASE; +} + +static DEFINE_SPINLOCK(boot_lock); + +/* + * MP_CORE_IPC will be used to generate interrupt and can be used by either + * of core. + * To bring core1 out of GDFS we need to raise the SPI using the MP_CORE_IPC. + */ +static void raise_clear_spi(unsigned int cpu, bool set) +{ + int value; + + value = __raw_readl(MSM_CSR_BASE + 0x54); + if (set) + __raw_writel(value | BIT(cpu), MSM_CSR_BASE + 0x54); + else + __raw_writel(value & ~BIT(cpu), MSM_CSR_BASE + 0x54); + mb(); +} + +static void clear_pending_spi(unsigned int irq) +{ + struct irq_data *d = irq_get_irq_data(irq); + struct irq_chip *c = irq_data_get_irq_chip(d); + + c->irq_mask(d); + local_irq_disable(); + /* Clear the IRQ from the ENABLE_SET */ + gic_clear_spi_pending(irq); + local_irq_enable(); +} + +void __cpuinit platform_secondary_init(unsigned int cpu) +{ + pr_debug("CPU%u: Booted secondary processor\n", cpu); + + WARN_ON(msm_platform_secondary_init(cpu)); + + /* + * if any interrupts are already enabled for the primary + * core (e.g. timer irq), then they will not have been enabled + * for us: do so + */ + gic_secondary_init(0); + + /* + * let the primary processor know we're out of the + * pen, then head off into the C entry point + */ + write_pen_release(-1); + + /* clear the IPC1(SPI-8) pending SPI */ + if (power_collapsed) { + raise_clear_spi(1, false); + clear_pending_spi(MSM8625_INT_ACSR_MP_CORE_IPC1); + power_collapsed = 0; + } + + /* + * Synchronise with the boot thread. + */ + spin_lock(&boot_lock); + spin_unlock(&boot_lock); +} + +static int __cpuinit msm8625_release_secondary(void) +{ + void __iomem *base_ptr; + int value = 0; + unsigned long timeout; + + /* + * loop to ensure that the GHS_STATUS_CORE1 bit in the + * MPA5_STATUS_REG(0x3c) is set. The timeout for the while + * loop can be set as 20us as of now + */ + timeout = jiffies + usecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + value = __raw_readl(MSM_CFG_CTL_BASE + 0x3c); + if ((value & MSM_CORE1_STATUS_MSK) == + MSM_CORE1_STATUS_MSK) + break; + udelay(1); + } + + if (!value) { + pr_err("Core 1 cannot be brought out of Reset!!!\n"); + return -ENODEV; + } + + base_ptr = ioremap_nocache(MSM_CORE1_RESET, SZ_4); + if (!base_ptr) + return -ENODEV; + /* Reset core 1 out of reset */ + __raw_writel(0x0, base_ptr); + mb(); + + reset_core1_base = base_ptr; + + return 0; +} + +void __iomem *core1_reset_base(void) +{ + return reset_core1_base; +} + +int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + unsigned long timeout; + + preset_lpj = loops_per_jiffy; + + if (cold_boot_done == false) { + if (msm8625_release_secondary()) { + pr_err("Failed to release secondary core\n"); + return -ENODEV; + } + cold_boot_done = true; + } + + /* + * Set synchronisation state between this boot processor + * and the secondary one + */ + spin_lock(&boot_lock); + + /* + * This is really belt and braces; we hold unintended secondary + * CPUs in the holding pen until we're ready for them. However, + * since we haven't sent them a soft interrupt, they shouldn't + * be there. + */ + write_pen_release(cpu); + + /* + * Send the secondary CPU a soft interrupt, thereby causing + * the boot monitor to read the system wide flags register, + * and branch to the address found there. + * + * power_collapsed is the flag which will be updated for Powercollapse. + * Once we are out of PC, as Core1 will be in the state of GDFS which + * needs to be brought out by raising an SPI. + */ + + if (power_collapsed) { + core1_gic_configure_and_raise(); + raise_clear_spi(1, true); + } else { + gic_raise_softirq(cpumask_of(cpu), 1); + } + + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { + smp_rmb(); + if (pen_release == -1) + break; + + udelay(10); + } + + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + spin_unlock(&boot_lock); + + return 0; +} + +/* + * Initialise the CPU possible map early - this describes the CPUs + * which may be present or become present in the system. + */ +void __init smp_init_cpus(void) +{ + void __iomem *scu_base = scu_base_addr(); + + unsigned int i, ncores; + + ncores = scu_base ? scu_get_core_count(scu_base) : 1; + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); + + set_smp_cross_call(gic_raise_softirq); +} + +static void __init msm8625_boot_vector_init(uint32_t *boot_vector, + unsigned long entry) +{ + if (!boot_vector) + return; + msm8625_boot_vector = boot_vector; + + msm8625_boot_vector[0] = 0xE51FF004; /* ldr pc, 4 */ + msm8625_boot_vector[1] = entry; +} + +void __init platform_smp_prepare_cpus(unsigned int max_cpus) +{ + int i, value; + void __iomem *second_ptr; + + /* + * Initialise the present map, which describes the set of CPUs + * actually populated at the present time. + */ + for (i = 0; i < max_cpus; i++) + set_cpu_present(i, true); + + scu_enable(scu_base_addr()); + + /* + * Write the address of secondary startup into the + * boot remapper register. The secondary CPU branches to this address. + */ + __raw_writel(MSM8625_SECONDARY_PHYS, (MSM_CFG_CTL_BASE + 0x34)); + mb(); + + second_ptr = ioremap_nocache(MSM8625_SECONDARY_PHYS, SZ_8); + if (!second_ptr) { + pr_err("failed to ioremap for secondary core\n"); + return; + } + + msm8625_boot_vector_init(second_ptr, + virt_to_phys(msm_secondary_startup)); + iounmap(second_ptr); + + /* Enable boot remapper address: bit 26 for core1 */ + value = __raw_readl(MSM_CFG_CTL_BASE + 0x30); + __raw_writel(value | (0x4 << 24), MSM_CFG_CTL_BASE + 0x30) ; + mb(); +} diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c index db0117ec55f..b40c0c70415 100644 --- a/arch/arm/mach-msm/platsmp.c +++ b/arch/arm/mach-msm/platsmp.c @@ -1,19 +1,18 @@ /* * Copyright (C) 2002 ARM Ltd. * All Rights Reserved - * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include -#include +#include #include -#include -#include -#include +#include #include #include @@ -22,18 +21,20 @@ #include #include +#include +#include #include +#include "pm.h" #include "scm-boot.h" +#include "spm.h" #define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 #define SCSS_CPU1CORE_RESET 0xD80 #define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 -/* Mask for edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */ -#define GIC_PPI_EDGE_MASK 0xFFFFD7FF - extern void msm_secondary_startup(void); + /* * control for which core is the next to come out of the secondary * boot "holding pen". @@ -42,16 +43,9 @@ volatile int pen_release = -1; static DEFINE_SPINLOCK(boot_lock); -static inline int get_core_count(void) -{ - /* 1 + the PART[1:0] field of MIDR */ - return ((read_cpuid_id() >> 4) & 3) + 1; -} - void __cpuinit platform_secondary_init(unsigned int cpu) { - /* Configure edge-triggered PPIs */ - writel(GIC_PPI_EDGE_MASK, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4); + WARN_ON(msm_platform_secondary_init(cpu)); /* * if any interrupts are already enabled for the primary @@ -60,13 +54,6 @@ void __cpuinit platform_secondary_init(unsigned int cpu) */ gic_secondary_init(0); - /* - * let the primary processor know we're out of the - * pen, then head off into the C entry point - */ - pen_release = -1; - smp_wmb(); - /* * Synchronise with the boot thread. */ @@ -74,34 +61,128 @@ void __cpuinit platform_secondary_init(unsigned int cpu) spin_unlock(&boot_lock); } -static __cpuinit void prepare_cold_cpu(unsigned int cpu) +static int __cpuinit scorpion_release_secondary(void) { - int ret; - ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup), - SCM_FLAG_COLDBOOT_CPU1); - if (ret == 0) { - void __iomem *sc1_base_ptr; - sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); - if (sc1_base_ptr) { - writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); - writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); - writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); - iounmap(sc1_base_ptr); - } - } else - printk(KERN_DEBUG "Failed to set secondary core boot " - "address\n"); + void *base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); + if (!base_ptr) + return -EINVAL; + + writel_relaxed(0, base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); + dmb(); + writel_relaxed(0, base_ptr + SCSS_CPU1CORE_RESET); + writel_relaxed(3, base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); + mb(); + iounmap(base_ptr); + + return 0; } +static int __cpuinit krait_release_secondary_sim(unsigned long base, int cpu) +{ + void *base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K); + if (!base_ptr) + return -ENODEV; + + if (machine_is_msm8960_sim() || machine_is_msm8960_rumi3()) { + writel_relaxed(0x10, base_ptr+0x04); + writel_relaxed(0x80, base_ptr+0x04); + } + + if (machine_is_apq8064_sim()) + writel_relaxed(0xf0000, base_ptr+0x04); + + if (machine_is_copper_sim()) { + writel_relaxed(0x800, base_ptr+0x04); + writel_relaxed(0x3FFF, base_ptr+0x14); + } + + mb(); + iounmap(base_ptr); + return 0; +} + +static int __cpuinit krait_release_secondary(unsigned long base, int cpu) +{ + void *base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K); + if (!base_ptr) + return -ENODEV; + + msm_spm_turn_on_cpu_rail(cpu); + + writel_relaxed(0x109, base_ptr+0x04); + writel_relaxed(0x101, base_ptr+0x04); + ndelay(300); + + writel_relaxed(0x121, base_ptr+0x04); + udelay(2); + + writel_relaxed(0x020, base_ptr+0x04); + udelay(2); + + writel_relaxed(0x000, base_ptr+0x04); + udelay(100); + + writel_relaxed(0x080, base_ptr+0x04); + mb(); + iounmap(base_ptr); + return 0; +} + +static int __cpuinit release_secondary(unsigned int cpu) +{ + BUG_ON(cpu >= get_core_count()); + + if (cpu_is_msm8x60()) + return scorpion_release_secondary(); + + if (machine_is_msm8960_sim() || machine_is_msm8960_rumi3() || + machine_is_apq8064_sim()) + return krait_release_secondary_sim(0x02088000, cpu); + + if (machine_is_copper_sim()) + return krait_release_secondary_sim(0xf9088000, cpu); + + if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) + return krait_release_secondary(0x02088000, cpu); + + WARN(1, "unknown CPU case in release_secondary\n"); + return -EINVAL; +} + +DEFINE_PER_CPU(int, cold_boot_done); +static int cold_boot_flags[] = { + 0, + SCM_FLAG_COLDBOOT_CPU1, + SCM_FLAG_COLDBOOT_CPU2, + SCM_FLAG_COLDBOOT_CPU3, +}; + int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { + int ret; + int flag = 0; unsigned long timeout; - static int cold_boot_done; - /* Only need to bring cpu out of reset this way once */ - if (cold_boot_done == false) { - prepare_cold_cpu(cpu); - cold_boot_done = true; + pr_debug("Starting secondary CPU %d\n", cpu); + + /* Set preset_lpj to avoid subsequent lpj recalculations */ + preset_lpj = loops_per_jiffy; + + if (cpu > 0 && cpu < ARRAY_SIZE(cold_boot_flags)) + flag = cold_boot_flags[cpu]; + else + __WARN(); + + if (per_cpu(cold_boot_done, cpu) == false) { + ret = scm_set_boot_addr((void *) + virt_to_phys(msm_secondary_startup), + flag); + if (ret == 0) + release_secondary(cpu); + else + printk(KERN_DEBUG "Failed to set secondary core boot " + "address\n"); + per_cpu(cold_boot_done, cpu) = true; } /* @@ -121,6 +202,8 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) pen_release = cpu_logical_map(cpu); __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); + __asm__("sev"); + mb(); /* * Send the secondary CPU a soft interrupt, thereby causing @@ -135,6 +218,8 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) if (pen_release == -1) break; + dmac_inv_range((void *)&pen_release, + (void *)(&pen_release+sizeof(pen_release))); udelay(10); } @@ -146,12 +231,9 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) return pen_release != -1 ? -ENOSYS : 0; } - /* * Initialise the CPU possible map early - this describes the CPUs - * which may be present or become present in the system. The msm8x60 - * does not support the ARM SCU, so just set the possible cpu mask to - * NR_CPUS. + * which may be present or become present in the system. */ void __init smp_init_cpus(void) { @@ -166,7 +248,7 @@ void __init smp_init_cpus(void) for (i = 0; i < ncores; i++) set_cpu_possible(i, true); - set_smp_cross_call(gic_raise_softirq); + set_smp_cross_call(gic_raise_softirq); } void __init platform_smp_prepare_cpus(unsigned int max_cpus) diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c new file mode 100644 index 00000000000..14e6f675ac7 --- /dev/null +++ b/arch/arm/mach-msm/pm-8x60.c @@ -0,0 +1,1052 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_VFP +#include +#endif + +#include "acpuclock.h" +#include "clock.h" +#include "avs.h" +#include +#include "idle.h" +#include "pm.h" +#include "scm-boot.h" +#include "spm.h" +#include "timer.h" +#include "pm-boot.h" + +/****************************************************************************** + * Debug Definitions + *****************************************************************************/ + +enum { + MSM_PM_DEBUG_SUSPEND = BIT(0), + MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1), + MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2), + MSM_PM_DEBUG_CLOCK = BIT(3), + MSM_PM_DEBUG_RESET_VECTOR = BIT(4), + MSM_PM_DEBUG_IDLE_CLK = BIT(5), + MSM_PM_DEBUG_IDLE = BIT(6), + MSM_PM_DEBUG_IDLE_LIMITS = BIT(7), + MSM_PM_DEBUG_HOTPLUG = BIT(8), +}; + +static int msm_pm_debug_mask = 1; +module_param_named( + debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + + +/****************************************************************************** + * Sleep Modes and Parameters + *****************************************************************************/ +enum { + MSM_PM_MODE_ATTR_SUSPEND, + MSM_PM_MODE_ATTR_IDLE, + MSM_PM_MODE_ATTR_NR, +}; + +static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = { + [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled", + [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled", +}; + +struct msm_pm_kobj_attribute { + unsigned int cpu; + struct kobj_attribute ka; +}; + +#define GET_CPU_OF_ATTR(attr) \ + (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu) + +struct msm_pm_sysfs_sleep_mode { + struct kobject *kobj; + struct attribute_group attr_group; + struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1]; + struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR]; +}; + +static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse", + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi", + [MSM_PM_SLEEP_MODE_RETENTION] = "retention", + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] = + "standalone_power_collapse", +}; + +static struct msm_pm_sleep_ops pm_sleep_ops; +/* + * Write out the attribute. + */ +static ssize_t msm_pm_mode_attr_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct kernel_param kp; + unsigned int cpu; + struct msm_pm_platform_data *mode; + + if (msm_pm_sleep_mode_labels[i] == NULL) + continue; + + if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i])) + continue; + + cpu = GET_CPU_OF_ATTR(attr); + mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)]; + + if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) { + u32 arg = mode->suspend_enabled; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) { + u32 arg = mode->idle_enabled; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } + + break; + } + + if (ret > 0) { + strlcat(buf, "\n", PAGE_SIZE); + ret++; + } + + return ret; +} + +/* + * Read in the new attribute value. + */ +static ssize_t msm_pm_mode_attr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct kernel_param kp; + unsigned int cpu; + struct msm_pm_platform_data *mode; + + if (msm_pm_sleep_mode_labels[i] == NULL) + continue; + + if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i])) + continue; + + cpu = GET_CPU_OF_ATTR(attr); + mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)]; + + if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) { + kp.arg = &mode->suspend_enabled; + ret = param_set_byte(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) { + kp.arg = &mode->idle_enabled; + ret = param_set_byte(buf, &kp); + } + + break; + } + + return ret ? ret : count; +} + +/* + * Add sysfs entries for one cpu. + */ +static int __init msm_pm_mode_sysfs_add_cpu( + unsigned int cpu, struct kobject *modes_kobj) +{ + char cpu_name[8]; + struct kobject *cpu_kobj; + struct msm_pm_sysfs_sleep_mode *mode = NULL; + int i, j, k; + int ret; + + snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu); + cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj); + if (!cpu_kobj) { + pr_err("%s: cannot create %s kobject\n", __func__, cpu_name); + ret = -ENOMEM; + goto mode_sysfs_add_cpu_exit; + } + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + int idx = MSM_PM_MODE(cpu, i); + + if ((!msm_pm_sleep_modes[idx].suspend_supported) + && (!msm_pm_sleep_modes[idx].idle_supported)) + continue; + + if (!msm_pm_sleep_mode_labels[i] || + !msm_pm_sleep_mode_labels[i][0]) + continue; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + pr_err("%s: cannot allocate memory for attributes\n", + __func__); + ret = -ENOMEM; + goto mode_sysfs_add_cpu_exit; + } + + mode->kobj = kobject_create_and_add( + msm_pm_sleep_mode_labels[i], cpu_kobj); + if (!mode->kobj) { + pr_err("%s: cannot create kobject\n", __func__); + ret = -ENOMEM; + goto mode_sysfs_add_cpu_exit; + } + + for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) { + if ((k == MSM_PM_MODE_ATTR_IDLE) && + !msm_pm_sleep_modes[idx].idle_supported) + continue; + if ((k == MSM_PM_MODE_ATTR_SUSPEND) && + !msm_pm_sleep_modes[idx].suspend_supported) + continue; + mode->kas[j].cpu = cpu; + mode->kas[j].ka.attr.mode = 0644; + mode->kas[j].ka.show = msm_pm_mode_attr_show; + mode->kas[j].ka.store = msm_pm_mode_attr_store; + mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k]; + mode->attrs[j] = &mode->kas[j].ka.attr; + j++; + } + mode->attrs[j] = NULL; + + mode->attr_group.attrs = mode->attrs; + ret = sysfs_create_group(mode->kobj, &mode->attr_group); + if (ret) { + pr_err("%s: cannot create kobject attribute group\n", + __func__); + goto mode_sysfs_add_cpu_exit; + } + } + + ret = 0; + +mode_sysfs_add_cpu_exit: + if (ret) { + if (mode && mode->kobj) + kobject_del(mode->kobj); + kfree(mode); + } + + return ret; +} + +/* + * Add sysfs entries for the sleep modes. + */ +static int __init msm_pm_mode_sysfs_add(void) +{ + struct kobject *module_kobj; + struct kobject *modes_kobj; + unsigned int cpu; + int ret; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + ret = -ENOENT; + goto mode_sysfs_add_exit; + } + + modes_kobj = kobject_create_and_add("modes", module_kobj); + if (!modes_kobj) { + pr_err("%s: cannot create modes kobject\n", __func__); + ret = -ENOMEM; + goto mode_sysfs_add_exit; + } + + for_each_possible_cpu(cpu) { + ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj); + if (ret) + goto mode_sysfs_add_exit; + } + + ret = 0; + +mode_sysfs_add_exit: + return ret; +} + +/****************************************************************************** + * Configure Hardware before/after Low Power Mode + *****************************************************************************/ + +/* + * Configure hardware registers in preparation for Apps power down. + */ +static void msm_pm_config_hw_before_power_down(void) +{ + return; +} + +/* + * Clear hardware registers after Apps powers up. + */ +static void msm_pm_config_hw_after_power_up(void) +{ + return; +} + +/* + * Configure hardware registers in preparation for SWFI. + */ +static void msm_pm_config_hw_before_swfi(void) +{ + return; +} + + +/****************************************************************************** + * Suspend Max Sleep Time + *****************************************************************************/ + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE +static int msm_pm_sleep_time_override; +module_param_named(sleep_time_override, + msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP); +#endif + +#define SCLK_HZ (32768) +#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000) + +static uint32_t msm_pm_max_sleep_time; + +/* + * Convert time from nanoseconds to slow clock ticks, then cap it to the + * specified limit + */ +static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit) +{ + do_div(time_ns, NSEC_PER_SEC / SCLK_HZ); + return (time_ns > limit) ? limit : time_ns; +} + +/* + * Set the sleep time for suspend. 0 means infinite sleep time. + */ +void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) +{ + if (max_sleep_time_ns == 0) { + msm_pm_max_sleep_time = 0; + } else { + msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time( + max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT); + + if (msm_pm_max_sleep_time == 0) + msm_pm_max_sleep_time = 1; + } + + if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) + pr_info("%s: Requested %lld ns Giving %u sclk ticks\n", + __func__, max_sleep_time_ns, msm_pm_max_sleep_time); +} +EXPORT_SYMBOL(msm_pm_set_max_sleep_time); + + +/****************************************************************************** + * + *****************************************************************************/ + +static void *msm_pm_idle_rs_limits; +static bool msm_pm_use_qtimer; + +static void msm_pm_swfi(void) +{ + msm_pm_config_hw_before_swfi(); + msm_arch_idle(); +} + + +static void msm_pm_retention(void) +{ + int ret = 0; + + msm_pm_config_hw_before_swfi(); + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_RETENTION, false); + WARN_ON(ret); + msm_arch_idle(); + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false); + WARN_ON(ret); +} + +#ifdef CONFIG_CACHE_L2X0 +static inline bool msm_pm_l2x0_power_collapse(void) +{ + bool collapsed = 0; + + l2cc_suspend(); + collapsed = msm_pm_collapse(); + l2cc_resume(); + + return collapsed; +} +#else +static inline bool msm_pm_l2x0_power_collapse(void) +{ + return msm_pm_collapse(); +} +#endif + +static bool __ref msm_pm_spm_power_collapse( + unsigned int cpu, bool from_idle, bool notify_rpm) +{ + void *entry; + bool collapsed = 0; + int ret; + unsigned int saved_gic_cpu_ctrl; + + saved_gic_cpu_ctrl = readl_relaxed(MSM_QGIC_CPU_BASE + GIC_CPU_CTRL); + mb(); + + if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) + pr_info("CPU%u: %s: notify_rpm %d\n", + cpu, __func__, (int) notify_rpm); + + ret = msm_spm_set_low_power_mode( + MSM_SPM_MODE_POWER_COLLAPSE, notify_rpm); + WARN_ON(ret); + + entry = (!cpu || from_idle) ? + msm_pm_collapse_exit : msm_secondary_startup; + msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry)); + + if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask) + pr_info("CPU%u: %s: program vector to %p\n", + cpu, __func__, entry); + +#ifdef CONFIG_VFP + vfp_pm_suspend(); +#endif + + collapsed = msm_pm_l2x0_power_collapse(); + + msm_pm_boot_config_after_pc(cpu); + + if (collapsed) { +#ifdef CONFIG_VFP + vfp_pm_resume(); +#endif + cpu_init(); + writel(0xF0, MSM_QGIC_CPU_BASE + GIC_CPU_PRIMASK); + writel_relaxed(saved_gic_cpu_ctrl, + MSM_QGIC_CPU_BASE + GIC_CPU_CTRL); + mb(); + local_fiq_enable(); + } + + if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) + pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n", + cpu, __func__, collapsed); + + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false); + WARN_ON(ret); + return collapsed; +} + +static bool msm_pm_power_collapse_standalone(bool from_idle) +{ + unsigned int cpu = smp_processor_id(); + unsigned int avsdscr_setting; + bool collapsed; + + avsdscr_setting = avs_get_avsdscr(); + avs_disable(); + collapsed = msm_pm_spm_power_collapse(cpu, from_idle, false); + avs_reset_delays(avsdscr_setting); + return collapsed; +} + +static bool msm_pm_power_collapse(bool from_idle) +{ + unsigned int cpu = smp_processor_id(); + unsigned long saved_acpuclk_rate; + unsigned int avsdscr_setting; + bool collapsed; + + if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) + pr_info("CPU%u: %s: idle %d\n", + cpu, __func__, (int)from_idle); + + msm_pm_config_hw_before_power_down(); + if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) + pr_info("CPU%u: %s: pre power down\n", cpu, __func__); + + avsdscr_setting = avs_get_avsdscr(); + avs_disable(); + + if (cpu_online(cpu)) + saved_acpuclk_rate = acpuclk_power_collapse(); + else + saved_acpuclk_rate = 0; + + if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask) + pr_info("CPU%u: %s: change clock rate (old rate = %lu)\n", + cpu, __func__, saved_acpuclk_rate); + + collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true); + + if (cpu_online(cpu)) { + if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask) + pr_info("CPU%u: %s: restore clock rate to %lu\n", + cpu, __func__, saved_acpuclk_rate); + if (acpuclk_set_rate(cpu, saved_acpuclk_rate, SETRATE_PC) < 0) + pr_err("CPU%u: %s: failed to restore clock rate(%lu)\n", + cpu, __func__, saved_acpuclk_rate); + } else { + unsigned int gic_dist_enabled; + unsigned int gic_dist_pending; + gic_dist_enabled = readl_relaxed( + MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_CLEAR); + gic_dist_pending = readl_relaxed( + MSM_QGIC_DIST_BASE + GIC_DIST_PENDING_SET); + mb(); + gic_dist_pending &= gic_dist_enabled; + + if (gic_dist_pending) + pr_err("CPU %d interrupted during hotplug.Pending int 0x%x\n", + cpu, gic_dist_pending); + } + + + avs_reset_delays(avsdscr_setting); + msm_pm_config_hw_after_power_up(); + if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) + pr_info("CPU%u: %s: post power up\n", cpu, __func__); + + if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) + pr_info("CPU%u: %s: return\n", cpu, __func__); + return collapsed; +} + +static void msm_pm_qtimer_available(void) +{ + if (machine_is_copper()) + msm_pm_use_qtimer = true; +} + +static int64_t msm_pm_timer_enter_idle(void) +{ + if (msm_pm_use_qtimer) + return ktime_to_ns(tick_nohz_get_sleep_length()); + + return msm_timer_enter_idle(); +} + +static void msm_pm_timer_exit_idle(bool timer_halted) +{ + if (msm_pm_use_qtimer) + return; + + msm_timer_exit_idle((int) timer_halted); +} + +static int64_t msm_pm_timer_enter_suspend(int64_t *period) +{ + int time = 0; + + if (msm_pm_use_qtimer) + return sched_clock(); + + time = msm_timer_get_sclk_time(period); + if (!time) + pr_err("%s: Unable to read sclk.\n", __func__); + + return time; +} + +static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period) +{ + if (msm_pm_use_qtimer) + return sched_clock() - time; + + if (time != 0) { + int64_t end_time = msm_timer_get_sclk_time(NULL); + if (end_time != 0) { + time = end_time - time; + if (time < 0) + time += period; + } else + time = 0; + } + + return time; +} + +/****************************************************************************** + * External Idle/Suspend Functions + *****************************************************************************/ + +void arch_idle(void) +{ + return; +} + +int msm_pm_idle_prepare(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + uint32_t latency_us; + uint32_t sleep_us; + int i; + unsigned int power_usage = -1; + int ret = 0; + + latency_us = (uint32_t) pm_qos_request(PM_QOS_CPU_DMA_LATENCY); + sleep_us = (uint32_t) ktime_to_ns(tick_nohz_get_sleep_length()); + sleep_us = DIV_ROUND_UP(sleep_us, 1000); + + for (i = 0; i < dev->state_count; i++) { + struct cpuidle_state *state = &drv->states[i]; + struct cpuidle_state_usage *st_usage = &dev->states_usage[i]; + enum msm_pm_sleep_mode mode; + bool allow; + void *rs_limits = NULL; + uint32_t power; + int idx; + + mode = (enum msm_pm_sleep_mode) cpuidle_get_statedata(st_usage); + idx = MSM_PM_MODE(dev->cpu, mode); + + allow = msm_pm_sleep_modes[idx].idle_enabled && + msm_pm_sleep_modes[idx].idle_supported; + + switch (mode) { + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + if (!allow) + break; + + if (num_online_cpus() > 1) { + allow = false; + break; + } + /* fall through */ + + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE: + if (!allow) + break; + /* fall through */ + + case MSM_PM_SLEEP_MODE_RETENTION: + if (!allow) + break; + /* fall through */ + + case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT: + if (!allow) + break; + + if (pm_sleep_ops.lowest_limits) + rs_limits = pm_sleep_ops.lowest_limits(true, + mode, latency_us, sleep_us, + &power); + + if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask) + pr_info("CPU%u: %s: %s, latency %uus, " + "sleep %uus, limit %p\n", + dev->cpu, __func__, state->desc, + latency_us, sleep_us, rs_limits); + + if (!rs_limits) + allow = false; + break; + + default: + allow = false; + break; + } + + if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask) + pr_info("CPU%u: %s: allow %s: %d\n", + dev->cpu, __func__, state->desc, (int)allow); + + if (allow) { + if (power < power_usage) { + power_usage = power; + ret = mode; + } + + if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == mode) + msm_pm_idle_rs_limits = rs_limits; + } + } + + return ret; +} + +int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode) +{ + int64_t time; + int exit_stat; + + if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask) + pr_info("CPU%u: %s: mode %d\n", + smp_processor_id(), __func__, sleep_mode); + + time = ktime_to_ns(ktime_get()); + + switch (sleep_mode) { + case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT: + msm_pm_swfi(); + exit_stat = MSM_PM_STAT_IDLE_WFI; + break; + + case MSM_PM_SLEEP_MODE_RETENTION: + msm_pm_retention(); + exit_stat = MSM_PM_STAT_RETENTION; + break; + + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE: + msm_pm_power_collapse_standalone(true); + exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE; + break; + + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: { + int64_t timer_expiration = 0; + bool timer_halted = false; + uint32_t sleep_delay; + int ret = -ENODEV; + int notify_rpm = + (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE); + int collapsed; + + timer_expiration = msm_pm_timer_enter_idle(); + + sleep_delay = (uint32_t) msm_pm_convert_and_cap_time( + timer_expiration, MSM_PM_SLEEP_TICK_LIMIT); + if (sleep_delay == 0) /* 0 would mean infinite time */ + sleep_delay = 1; + + if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask) + clock_debug_print_enabled(); + + if (pm_sleep_ops.enter_sleep) + ret = pm_sleep_ops.enter_sleep(sleep_delay, + msm_pm_idle_rs_limits, + true, notify_rpm); + if (!ret) { + collapsed = msm_pm_power_collapse(true); + timer_halted = true; + + if (pm_sleep_ops.exit_sleep) + pm_sleep_ops.exit_sleep(msm_pm_idle_rs_limits, + true, notify_rpm, collapsed); + } + msm_pm_timer_exit_idle(timer_halted); + exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE; + break; + } + + default: + __WARN(); + goto cpuidle_enter_bail; + } + + time = ktime_to_ns(ktime_get()) - time; + msm_pm_add_stat(exit_stat, time); + + do_div(time, 1000); + return (int) time; + +cpuidle_enter_bail: + return 0; +} + +static struct msm_pm_sleep_status_data *msm_pm_slp_sts; + +static DEFINE_PER_CPU_SHARED_ALIGNED(enum msm_pm_sleep_mode, + msm_pm_last_slp_mode); + +bool msm_pm_verify_cpu_pc(unsigned int cpu) +{ + enum msm_pm_sleep_mode mode = per_cpu(msm_pm_last_slp_mode, cpu); + + if (msm_pm_slp_sts) + if ((mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) || + (mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)) + return true; + + return false; +} + +void msm_pm_cpu_enter_lowpower(unsigned int cpu) +{ + int i; + bool allow[MSM_PM_SLEEP_MODE_NR]; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct msm_pm_platform_data *mode; + + mode = &msm_pm_sleep_modes[MSM_PM_MODE(cpu, i)]; + allow[i] = mode->suspend_supported && mode->suspend_enabled; + } + + if (MSM_PM_DEBUG_HOTPLUG & msm_pm_debug_mask) + pr_notice("CPU%u: %s: shutting down cpu\n", cpu, __func__); + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) { + per_cpu(msm_pm_last_slp_mode, cpu) + = MSM_PM_SLEEP_MODE_POWER_COLLAPSE; + msm_pm_power_collapse(false); + } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { + per_cpu(msm_pm_last_slp_mode, cpu) + = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE; + msm_pm_power_collapse_standalone(false); + } else if (allow[MSM_PM_SLEEP_MODE_RETENTION]) { + per_cpu(msm_pm_last_slp_mode, cpu) + = MSM_PM_SLEEP_MODE_RETENTION; + msm_pm_retention(); + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + per_cpu(msm_pm_last_slp_mode, cpu) + = MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT; + msm_pm_swfi(); + } else + per_cpu(msm_pm_last_slp_mode, cpu) = MSM_PM_SLEEP_MODE_NR; +} + +int msm_pm_wait_cpu_shutdown(unsigned int cpu) +{ + + int timeout = 10; + + if (!msm_pm_slp_sts) + return 0; + + while (timeout--) { + + /* + * Check for the SPM of the core being hotplugged to set + * its sleep state.The SPM sleep state indicates that the + * core has been power collapsed. + */ + + int acc_sts = __raw_readl(msm_pm_slp_sts->base_addr + + cpu * msm_pm_slp_sts->cpu_offset); + mb(); + + if (acc_sts & msm_pm_slp_sts->mask) + return 0; + + usleep(100); + } + pr_warn("%s(): Timed out waiting for CPU %u SPM to enter sleep state", + __func__, cpu); + return -EBUSY; +} + +static int msm_pm_enter(suspend_state_t state) +{ + bool allow[MSM_PM_SLEEP_MODE_NR]; + int i; + int64_t period = 0; + int64_t time = msm_pm_timer_enter_suspend(&period); + + if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) + pr_info("%s\n", __func__); + + if (smp_processor_id()) { + __WARN(); + goto enter_exit; + } + + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct msm_pm_platform_data *mode; + + mode = &msm_pm_sleep_modes[MSM_PM_MODE(0, i)]; + allow[i] = mode->suspend_supported && mode->suspend_enabled; + } + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) { + void *rs_limits = NULL; + int ret = -ENODEV; + uint32_t power; + + if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) + pr_info("%s: power collapse\n", __func__); + + clock_debug_print_enabled(); + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE + if (msm_pm_sleep_time_override > 0) { + int64_t ns = NSEC_PER_SEC * + (int64_t) msm_pm_sleep_time_override; + msm_pm_set_max_sleep_time(ns); + msm_pm_sleep_time_override = 0; + } +#endif /* CONFIG_MSM_SLEEP_TIME_OVERRIDE */ + if (pm_sleep_ops.lowest_limits) + rs_limits = pm_sleep_ops.lowest_limits(false, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, -1, + -1, &power); + + if (rs_limits) { + if (pm_sleep_ops.enter_sleep) + ret = pm_sleep_ops.enter_sleep( + msm_pm_max_sleep_time, + rs_limits, false, true); + if (!ret) { + int collapsed = msm_pm_power_collapse(false); + if (pm_sleep_ops.exit_sleep) { + pm_sleep_ops.exit_sleep(rs_limits, + false, true, collapsed); + } + } + } else { + pr_err("%s: cannot find the lowest power limit\n", + __func__); + } + time = msm_pm_timer_exit_suspend(time, period); + msm_pm_add_stat(MSM_PM_STAT_SUSPEND, time); + } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { + if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) + pr_info("%s: standalone power collapse\n", __func__); + msm_pm_power_collapse_standalone(false); + } else if (allow[MSM_PM_SLEEP_MODE_RETENTION]) { + if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) + pr_info("%s: retention\n", __func__); + msm_pm_retention(); + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) + pr_info("%s: swfi\n", __func__); + msm_pm_swfi(); + } + + +enter_exit: + if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask) + pr_info("%s: return\n", __func__); + + return 0; +} + +static struct platform_suspend_ops msm_pm_ops = { + .enter = msm_pm_enter, + .valid = suspend_valid_only_mem, +}; + +/****************************************************************************** + * Initialization routine + *****************************************************************************/ +void __init msm_pm_init_sleep_status_data( + struct msm_pm_sleep_status_data *data) +{ + msm_pm_slp_sts = data; +} + +void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops) +{ + if (ops) + pm_sleep_ops = *ops; +} + +static int __init msm_pm_init(void) +{ + pgd_t *pc_pgd; + pmd_t *pmd; + unsigned long pmdval; + enum msm_pm_time_stats_id enable_stats[] = { + MSM_PM_STAT_IDLE_WFI, + MSM_PM_STAT_RETENTION, + MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_POWER_COLLAPSE, + MSM_PM_STAT_SUSPEND, + }; + unsigned long exit_phys; + + /* Page table for cores to come back up safely. */ + pc_pgd = pgd_alloc(&init_mm); + if (!pc_pgd) + return -ENOMEM; + + exit_phys = virt_to_phys(msm_pm_collapse_exit); + + pmd = pmd_offset(pud_offset(pc_pgd + pgd_index(exit_phys),exit_phys), + exit_phys); + pmdval = (exit_phys & PGDIR_MASK) | + PMD_TYPE_SECT | PMD_SECT_AP_WRITE; + pmd[0] = __pmd(pmdval); + pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); + + msm_saved_state_phys = + allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE * + num_possible_cpus(), 4); + if (!msm_saved_state_phys) + return -ENOMEM; + msm_saved_state = ioremap_nocache(msm_saved_state_phys, + CPU_SAVED_STATE_SIZE * + num_possible_cpus()); + if (!msm_saved_state) + return -ENOMEM; + + /* It is remotely possible that the code in msm_pm_collapse_exit() + * which turns on the MMU with this mapping is in the + * next even-numbered megabyte beyond the + * start of msm_pm_collapse_exit(). + * Map this megabyte in as well. + */ + pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1))); + flush_pmd_entry(pmd); + msm_pm_pc_pgd = virt_to_phys(pc_pgd); + clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd), + virt_to_phys(&msm_pm_pc_pgd)); + + msm_pm_mode_sysfs_add(); + msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats)); + + msm_spm_allow_x_cpu_set_vdd(false); + + suspend_set_ops(&msm_pm_ops); + msm_pm_qtimer_available(); + msm_cpuidle_init(); + + return 0; +} + +late_initcall(msm_pm_init); diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c new file mode 100644 index 00000000000..079ed9c1923 --- /dev/null +++ b/arch/arm/mach-msm/pm-boot.c @@ -0,0 +1,272 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scm-boot.h" +#include "idle.h" +#include "pm-boot.h" + +static uint32_t *msm_pm_reset_vector; +static uint32_t saved_vector[2]; +static void (*msm_pm_boot_before_pc)(unsigned int cpu, unsigned long entry); +static void (*msm_pm_boot_after_pc)(unsigned int cpu); + +static void msm_pm_write_boot_vector(unsigned int cpu, unsigned long address) +{ + msm_pm_boot_vector[cpu] = address; + clean_caches((unsigned long)&msm_pm_boot_vector[cpu], + sizeof(msm_pm_boot_vector[cpu]), + virt_to_phys(&msm_pm_boot_vector[cpu])); +} + +#ifdef CONFIG_MSM_SCM +static int __devinit msm_pm_tz_boot_init(void) +{ + int flag = 0; + if (num_possible_cpus() == 1) + flag = SCM_FLAG_WARMBOOT_CPU0; + else if (num_possible_cpus() == 2) + flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1; + else if (num_possible_cpus() == 4) + flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1 | + SCM_FLAG_WARMBOOT_CPU2 | SCM_FLAG_WARMBOOT_CPU3; + else + __WARN(); + + return scm_set_boot_addr((void *)virt_to_phys(msm_pm_boot_entry), flag); +} + +static void msm_pm_config_tz_before_pc(unsigned int cpu, + unsigned long entry) +{ + msm_pm_write_boot_vector(cpu, entry); +} +#else +static int __init msm_pm_tz_boot_init(void) +{ + return 0; +}; + +static inline void msm_pm_config_tz_before_pc(unsigned int cpu, + unsigned long entry) {} +#endif + +static int __devinit msm_pm_boot_reset_vector_init(uint32_t *reset_vector) +{ + if (!reset_vector) + return -ENODEV; + msm_pm_reset_vector = reset_vector; + mb(); + + return 0; +} + +static void msm_pm_config_rst_vector_before_pc(unsigned int cpu, + unsigned long entry) +{ + saved_vector[0] = msm_pm_reset_vector[0]; + saved_vector[1] = msm_pm_reset_vector[1]; + msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */ + msm_pm_reset_vector[1] = entry; +} + +static void msm_pm_config_rst_vector_after_pc(unsigned int cpu) +{ + msm_pm_reset_vector[0] = saved_vector[0]; + msm_pm_reset_vector[1] = saved_vector[1]; +} + +void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry) +{ + if (msm_pm_boot_before_pc) + msm_pm_boot_before_pc(cpu, entry); +} + +void msm_pm_boot_config_after_pc(unsigned int cpu) +{ + if (msm_pm_boot_after_pc) + msm_pm_boot_after_pc(cpu); +} +#define BOOT_REMAP_ENABLE BIT(0) + +int __devinit msm_pm_boot_init(struct msm_pm_boot_platform_data *pdata) +{ + int ret = 0; + unsigned long entry; + void __iomem *warm_boot_ptr; + + switch (pdata->mode) { + case MSM_PM_BOOT_CONFIG_TZ: + ret = msm_pm_tz_boot_init(); + msm_pm_boot_before_pc = msm_pm_config_tz_before_pc; + msm_pm_boot_after_pc = NULL; + break; + case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS: + pdata->v_addr = ioremap(pdata->p_addr, PAGE_SIZE); + /* Fall through */ + case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT: + + if (!pdata->v_addr) + return -ENODEV; + + ret = msm_pm_boot_reset_vector_init(pdata->v_addr); + msm_pm_boot_before_pc + = msm_pm_config_rst_vector_before_pc; + msm_pm_boot_after_pc + = msm_pm_config_rst_vector_after_pc; + break; + case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR: + if (!cpu_is_msm8625()) { + void *remapped; + + /* + * Set the boot remap address and enable remapping of + * reset vector + */ + if (!pdata->p_addr || !pdata->v_addr) + return -ENODEV; + + remapped = ioremap_nocache(pdata->p_addr, SZ_8); + ret = msm_pm_boot_reset_vector_init(remapped); + + __raw_writel((pdata->p_addr | BOOT_REMAP_ENABLE), + pdata->v_addr); + + msm_pm_boot_before_pc + = msm_pm_config_rst_vector_before_pc; + msm_pm_boot_after_pc + = msm_pm_config_rst_vector_after_pc; + } else { + warm_boot_ptr = ioremap_nocache( + MSM8625_WARM_BOOT_PHYS, SZ_64); + ret = msm_pm_boot_reset_vector_init(warm_boot_ptr); + + entry = virt_to_phys(msm_pm_boot_entry); + + /* Below sequence is a work around for cores + * to come out of GDFS properly on 8625 target. + * On 8625 while cores coming out of GDFS observed + * the memory corruption at very first memory read. + */ + msm_pm_reset_vector[0] = 0xE59F000C; /* ldr r0, 0x14 */ + msm_pm_reset_vector[1] = 0xE59F1008; /* ldr r1, 0x14 */ + msm_pm_reset_vector[2] = 0xE1500001; /* cmp r0, r1 */ + msm_pm_reset_vector[3] = 0x1AFFFFFB; /* bne 0x0 */ + msm_pm_reset_vector[4] = 0xE12FFF10; /* bx r0 */ + msm_pm_reset_vector[5] = entry; /* 0x14 */ + + /* Here upper 16bits[16:31] used by CORE1 + * lower 16bits[0:15] used by CORE0 + */ + entry = (MSM8625_WARM_BOOT_PHYS | + ((MSM8625_WARM_BOOT_PHYS & 0xFFFF0000) >> 16)); + + /* write 'entry' to boot remapper register */ + __raw_writel(entry, (pdata->v_addr + + MPA5_BOOT_REMAP_ADDR)); + + /* Enable boot remapper for C0 [bit:25th] */ + __raw_writel(readl_relaxed(pdata->v_addr + + MPA5_CFG_CTL_REG) | BIT(25), + pdata->v_addr + MPA5_CFG_CTL_REG); + + /* Enable boot remapper for C1 [bit:26th] */ + __raw_writel(readl_relaxed(pdata->v_addr + + MPA5_CFG_CTL_REG) | BIT(26), + pdata->v_addr + MPA5_CFG_CTL_REG); + msm_pm_boot_before_pc = msm_pm_write_boot_vector; + } + break; + default: + __WARN(); + } + + return ret; +} + +static int __devinit msm_pm_boot_probe(struct platform_device *pdev) +{ + struct msm_pm_boot_platform_data pdata; + char *key = NULL; + uint32_t val = 0; + int ret = 0; + int flag = 0; + + key = "qcom,mode"; + ret = of_property_read_u32(pdev->dev.of_node, key, &val); + if (ret) { + pr_err("Unable to read boot mode Err(%d).\n", ret); + return -ENODEV; + } + pdata.mode = val; + + key = "qcom,phy-addr"; + ret = of_property_read_u32(pdev->dev.of_node, key, &val); + if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS) + goto fail; + if (!ret) { + pdata.p_addr = val; + flag++; + } + + key = "qcom,virt-addr"; + ret = of_property_read_u32(pdev->dev.of_node, key, &val); + if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT) + goto fail; + if (!ret) { + pdata.v_addr = (void *)val; + flag++; + } + + if (pdata.mode == MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR && (flag != 2)) { + key = "addresses for boot remap"; + goto fail; + } + + return msm_pm_boot_init(&pdata); + +fail: + pr_err("Error reading %s\n", key); + return -EFAULT; +} + +static struct of_device_id msm_pm_match_table[] = { + {.compatible = "qcom,pm-boot"}, + {}, +}; + +static struct platform_driver msm_pm_boot_driver = { + .probe = msm_pm_boot_probe, + .driver = { + .name = "pm-boot", + .owner = THIS_MODULE, + .of_match_table = msm_pm_match_table, + }, +}; + +static int __init msm_pm_boot_module_init(void) +{ + return platform_driver_register(&msm_pm_boot_driver); +} +module_init(msm_pm_boot_module_init); diff --git a/arch/arm/mach-msm/pm-boot.h b/arch/arm/mach-msm/pm-boot.h new file mode 100644 index 00000000000..30b67c216ca --- /dev/null +++ b/arch/arm/mach-msm/pm-boot.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _ARCH_ARM_MACH_MSM_PM_BOOT_H +#define _ARCH_ARM_MACH_MSM_PM_BOOT_H + +/* 8x25 specific macros */ +#define MPA5_CFG_CTL_REG 0x30 +#define MPA5_BOOT_REMAP_ADDR 0x34 +/* end */ + +enum { + MSM_PM_BOOT_CONFIG_TZ = 0, + MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS = 1, + MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT = 2, + MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR = 3, +}; + +struct msm_pm_boot_platform_data { + int mode; + phys_addr_t p_addr; + void __iomem *v_addr; +}; + +#ifdef CONFIG_PM +int __init msm_pm_boot_init(struct msm_pm_boot_platform_data *pdata); +#else +static inline int __init msm_pm_boot_init( + struct msm_pm_boot_platform_data *pdata) +{ + return 0; +} +#endif + +void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry); +void msm_pm_boot_config_after_pc(unsigned int cpu); + +#endif diff --git a/arch/arm/mach-msm/pm-data.c b/arch/arm/mach-msm/pm-data.c new file mode 100644 index 00000000000..6f4743f5898 --- /dev/null +++ b/arch/arm/mach-msm/pm-data.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include "pm.h" + +struct msm_pm_platform_data msm_pm_sleep_modes[] = { + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RETENTION)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 1, + .suspend_enabled = 1, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 0, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_RETENTION)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(1, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 0, + .idle_enabled = 1, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 0, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_RETENTION)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 0, + .idle_enabled = 1, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = { + .idle_supported = 0, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_RETENTION)] = { + .idle_supported = 1, + .suspend_supported = 1, + .idle_enabled = 0, + .suspend_enabled = 0, + }, + + [MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = { + .idle_supported = 1, + .suspend_supported = 0, + .idle_enabled = 1, + .suspend_enabled = 0, + }, +}; diff --git a/arch/arm/mach-msm/pm-stats.c b/arch/arm/mach-msm/pm-stats.c new file mode 100644 index 00000000000..936820a9a2c --- /dev/null +++ b/arch/arm/mach-msm/pm-stats.c @@ -0,0 +1,305 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pm.h" + +struct msm_pm_time_stats { + const char *name; + int64_t first_bucket_time; + int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int count; + int64_t total_time; + bool enabled; +}; + +struct msm_pm_cpu_time_stats { + struct msm_pm_time_stats stats[MSM_PM_STAT_COUNT]; +}; + +static DEFINE_SPINLOCK(msm_pm_stats_lock); +static DEFINE_PER_CPU_SHARED_ALIGNED( + struct msm_pm_cpu_time_stats, msm_pm_stats); + +/* + * Add the given time data to the statistics collection. + */ +void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) +{ + unsigned long flags; + struct msm_pm_time_stats *stats; + int64_t bt; + int i; + + spin_lock_irqsave(&msm_pm_stats_lock, flags); + stats = __get_cpu_var(msm_pm_stats).stats; + + if (!stats[id].enabled) + goto add_bail; + + stats[id].total_time += t; + stats[id].count++; + + bt = t; + do_div(bt, stats[id].first_bucket_time); + + if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT * + (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1))) + i = DIV_ROUND_UP(fls((uint32_t)bt), + CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT); + else + i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; + + if (i >= CONFIG_MSM_IDLE_STATS_BUCKET_COUNT) + i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; + + stats[id].bucket[i]++; + + if (t < stats[id].min_time[i] || !stats[id].max_time[i]) + stats[id].min_time[i] = t; + if (t > stats[id].max_time[i]) + stats[id].max_time[i] = t; + +add_bail: + spin_unlock_irqrestore(&msm_pm_stats_lock, flags); +} + +/* + * Helper function of snprintf where buf is auto-incremented, size is auto- + * decremented, and there is no return value. + * + * NOTE: buf and size must be l-values (e.g. variables) + */ +#define SNPRINTF(buf, size, format, ...) \ + do { \ + if (size > 0) { \ + int ret; \ + ret = snprintf(buf, size, format, ## __VA_ARGS__); \ + if (ret > size) { \ + buf += size; \ + size = 0; \ + } else { \ + buf += ret; \ + size -= ret; \ + } \ + } \ + } while (0) + +/* + * Write out the power management statistics. + */ +static int msm_pm_read_proc + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + unsigned int cpu = off / MSM_PM_STAT_COUNT; + int id = off % MSM_PM_STAT_COUNT; + char *p = page; + + if (count < 1024) { + *start = (char *) 0; + *eof = 0; + return 0; + } + + if (cpu < num_possible_cpus()) { + unsigned long flags; + struct msm_pm_time_stats *stats; + int i; + int64_t bucket_time; + int64_t s; + uint32_t ns; + + spin_lock_irqsave(&msm_pm_stats_lock, flags); + stats = per_cpu(msm_pm_stats, cpu).stats; + + /* Skip the disabled ones */ + if (!stats[id].enabled) { + *p = '\0'; + p++; + goto again; + } + + s = stats[id].total_time; + ns = do_div(s, NSEC_PER_SEC); + SNPRINTF(p, count, + "[cpu %u] %s:\n" + " count: %7d\n" + " total_time: %lld.%09u\n", + cpu, stats[id].name, + stats[id].count, + s, ns); + + bucket_time = stats[id].first_bucket_time; + for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) { + s = bucket_time; + ns = do_div(s, NSEC_PER_SEC); + SNPRINTF(p, count, + " <%6lld.%09u: %7d (%lld-%lld)\n", + s, ns, stats[id].bucket[i], + stats[id].min_time[i], + stats[id].max_time[i]); + + bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT; + } + + SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n", + s, ns, stats[id].bucket[i], + stats[id].min_time[i], + stats[id].max_time[i]); + +again: + *start = (char *) 1; + *eof = (off + 1 >= MSM_PM_STAT_COUNT * num_possible_cpus()); + + spin_unlock_irqrestore(&msm_pm_stats_lock, flags); + } + + return p - page; +} +#undef SNPRINTF + +#define MSM_PM_STATS_RESET "reset" + +/* + * Reset the power management statistics values. + */ +static int msm_pm_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char buf[sizeof(MSM_PM_STATS_RESET)]; + int ret; + unsigned long flags; + unsigned int cpu; + + if (count < sizeof(MSM_PM_STATS_RESET)) { + ret = -EINVAL; + goto write_proc_failed; + } + + if (copy_from_user(buf, buffer, sizeof(MSM_PM_STATS_RESET))) { + ret = -EFAULT; + goto write_proc_failed; + } + + if (memcmp(buf, MSM_PM_STATS_RESET, sizeof(MSM_PM_STATS_RESET))) { + ret = -EINVAL; + goto write_proc_failed; + } + + spin_lock_irqsave(&msm_pm_stats_lock, flags); + for_each_possible_cpu(cpu) { + struct msm_pm_time_stats *stats; + int i; + + stats = per_cpu(msm_pm_stats, cpu).stats; + for (i = 0; i < MSM_PM_STAT_COUNT; i++) { + memset(stats[i].bucket, + 0, sizeof(stats[i].bucket)); + memset(stats[i].min_time, + 0, sizeof(stats[i].min_time)); + memset(stats[i].max_time, + 0, sizeof(stats[i].max_time)); + stats[i].count = 0; + stats[i].total_time = 0; + } + } + + spin_unlock_irqrestore(&msm_pm_stats_lock, flags); + return count; + +write_proc_failed: + return ret; +} +#undef MSM_PM_STATS_RESET + +void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, int size) +{ + unsigned int cpu; + struct proc_dir_entry *d_entry; + int i = 0; + + for_each_possible_cpu(cpu) { + struct msm_pm_time_stats *stats = + per_cpu(msm_pm_stats, cpu).stats; + + stats[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request"; + stats[MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin"; + stats[MSM_PM_STAT_IDLE_SPIN].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi"; + stats[MSM_PM_STAT_IDLE_WFI].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_RETENTION].name = "retention"; + stats[MSM_PM_STAT_RETENTION].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE].name = + "idle-standalone-power-collapse"; + stats[MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE]. + first_bucket_time = CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE].name = + "idle-failed-standalone-power-collapse"; + stats[MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE]. + first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].name = + "idle-power-collapse"; + stats[MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name = + "idle-failed-power-collapse"; + stats[MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE]. + first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_SUSPEND].name = "suspend"; + stats[MSM_PM_STAT_SUSPEND].first_bucket_time = + CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend"; + stats[MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + stats[MSM_PM_STAT_NOT_IDLE].name = "not-idle"; + stats[MSM_PM_STAT_NOT_IDLE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + + for (i = 0; i < size; i++) + stats[enable_stats[i]].enabled = true; + + } + + d_entry = create_proc_entry("msm_pm_stats", + S_IRUGO | S_IWUSR | S_IWGRP, NULL); + if (d_entry) { + d_entry->read_proc = msm_pm_read_proc; + d_entry->write_proc = msm_pm_write_proc; + d_entry->data = NULL; + } +} diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h new file mode 100644 index 00000000000..70d54da8cf2 --- /dev/null +++ b/arch/arm/mach-msm/pm.h @@ -0,0 +1,141 @@ +/* arch/arm/mach-msm/pm.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_PM_H +#define __ARCH_ARM_MACH_MSM_PM_H + +#include +#include + +#ifdef CONFIG_SMP +extern void msm_secondary_startup(void); +#else +#define msm_secondary_startup NULL +#endif + +extern int power_collapsed; + +struct msm_pm_irq_calls { + unsigned int (*irq_pending)(void); + int (*idle_sleep_allowed)(void); + void (*enter_sleep1)(bool modem_wake, int from_idle, uint32_t + *irq_mask); + int (*enter_sleep2)(bool modem_wake, int from_idle); + void (*exit_sleep1)(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs); + void (*exit_sleep2)(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs); + void (*exit_sleep3)(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs); +}; + +enum msm_pm_sleep_mode { + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT = 0, + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT = 1, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE = 2, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE = 3, + MSM_PM_SLEEP_MODE_APPS_SLEEP = 4, + MSM_PM_SLEEP_MODE_RETENTION = MSM_PM_SLEEP_MODE_APPS_SLEEP, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND = 5, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN = 6, + MSM_PM_SLEEP_MODE_NR +}; + +#define MSM_PM_MODE(cpu, mode_nr) ((cpu) * MSM_PM_SLEEP_MODE_NR + (mode_nr)) + +struct msm_pm_platform_data { + u8 idle_supported; /* Allow device to enter mode during idle */ + u8 suspend_supported; /* Allow device to enter mode during suspend */ + u8 suspend_enabled; /* enabled for suspend */ + u8 idle_enabled; /* enabled for idle low power */ + u32 latency; /* interrupt latency in microseconds when entering + and exiting the low power mode */ + u32 residency; /* time threshold in microseconds beyond which + staying in the low power mode saves power */ +}; + +extern struct msm_pm_platform_data msm_pm_sleep_modes[]; + +struct msm_pm_sleep_status_data { + void *base_addr; + uint32_t cpu_offset; + uint32_t mask; +}; + +struct msm_pm_sleep_ops { + void *(*lowest_limits)(bool from_idle, + enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us, + uint32_t sleep_us, uint32_t *power); + int (*enter_sleep)(uint32_t sclk_count, void *limits, + bool from_idle, bool notify_rpm); + void (*exit_sleep)(void *limits, bool from_idle, + bool notify_rpm, bool collapsed); +}; + +void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count); +int msm_pm_idle_prepare(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index); +void msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls); +int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode); +void msm_pm_cpu_enter_lowpower(unsigned int cpu); + +void __init msm_pm_init_sleep_status_data( + struct msm_pm_sleep_status_data *sleep_data); + + +#ifdef CONFIG_MSM_PM8X60 +void msm_pm_set_rpm_wakeup_irq(unsigned int irq); +int msm_pm_wait_cpu_shutdown(unsigned int cpu); +bool msm_pm_verify_cpu_pc(unsigned int cpu); +void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops); +#else +static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {} +static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; } +static inline bool msm_pm_verify_cpu_pc(unsigned int cpu) { return true; } +static inline void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops) {} +#endif +#ifdef CONFIG_HOTPLUG_CPU +int msm_platform_secondary_init(unsigned int cpu); +#else +static inline int msm_platform_secondary_init(unsigned int cpu) { return 0; } +#endif + +enum msm_pm_time_stats_id { + MSM_PM_STAT_REQUESTED_IDLE = 0, + MSM_PM_STAT_IDLE_SPIN, + MSM_PM_STAT_IDLE_WFI, + MSM_PM_STAT_RETENTION, + MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE, + MSM_PM_STAT_SUSPEND, + MSM_PM_STAT_FAILED_SUSPEND, + MSM_PM_STAT_NOT_IDLE, + MSM_PM_STAT_COUNT +}; + +#ifdef CONFIG_MSM_IDLE_STATS +void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, int size); +void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t); +#else +static inline void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, + int size) {} +static inline void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) {} +#endif + +#endif /* __ARCH_ARM_MACH_MSM_PM_H */ diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c new file mode 100644 index 00000000000..ae7a3cd2dba --- /dev/null +++ b/arch/arm/mach-msm/pm2.c @@ -0,0 +1,1756 @@ +/* arch/arm/mach-msm/pm2.c + * + * MSM Power Management Routines + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_CPU_V7 +#include +#include +#endif +#include +#ifdef CONFIG_CACHE_L2X0 +#include +#endif +#ifdef CONFIG_VFP +#include +#endif + +#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN +#include +#endif +#include +#include +#include + +#include "smd_private.h" +#include "smd_rpcrouter.h" +#include "acpuclock.h" +#include "clock.h" +#include "idle.h" +#include "irq.h" +#include "gpio.h" +#include "timer.h" +#include "pm.h" +#include "spm.h" +#include "sirc.h" +#include "pm-boot.h" +#include "devices-msm7x2xa.h" + +/****************************************************************************** + * Debug Definitions + *****************************************************************************/ + +enum { + MSM_PM_DEBUG_SUSPEND = BIT(0), + MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1), + MSM_PM_DEBUG_STATE = BIT(2), + MSM_PM_DEBUG_CLOCK = BIT(3), + MSM_PM_DEBUG_RESET_VECTOR = BIT(4), + MSM_PM_DEBUG_SMSM_STATE = BIT(5), + MSM_PM_DEBUG_IDLE = BIT(6), + MSM_PM_DEBUG_HOTPLUG = BIT(7), +}; + +static int msm_pm_debug_mask; +int power_collapsed; +module_param_named( + debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + +#define MSM_PM_DPRINTK(mask, level, message, ...) \ + do { \ + if ((mask) & msm_pm_debug_mask) \ + printk(level message, ## __VA_ARGS__); \ + } while (0) + +#define MSM_PM_DEBUG_PRINT_STATE(tag) \ + do { \ + MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \ + KERN_INFO, "%s: " \ + "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \ + "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \ + "SMSM_APPS_DEM %x\n", \ + tag, \ + __raw_readl(APPS_CLK_SLEEP_EN), \ + __raw_readl(APPS_PWRDOWN), \ + smsm_get_state(SMSM_POWER_MASTER_DEM), \ + smsm_get_state(SMSM_MODEM_STATE), \ + smsm_get_state(SMSM_APPS_DEM)); \ + } while (0) + +#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \ + do { \ + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \ + smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \ + msm_pm_smem_data->resources_used, \ + msm_pm_smem_data->irq_mask, \ + msm_pm_smem_data->wakeup_reason, \ + msm_pm_smem_data->pending_irqs); \ + } while (0) + + +/****************************************************************************** + * Sleep Modes and Parameters + *****************************************************************************/ + +static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME; +module_param_named( + idle_sleep_min_time, msm_pm_idle_sleep_min_time, + int, S_IRUGO | S_IWUSR | S_IWGRP +); + +enum { + MSM_PM_MODE_ATTR_SUSPEND, + MSM_PM_MODE_ATTR_IDLE, + MSM_PM_MODE_ATTR_LATENCY, + MSM_PM_MODE_ATTR_RESIDENCY, + MSM_PM_MODE_ATTR_NR, +}; + +static char *msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_NR] = { + [MSM_PM_MODE_ATTR_SUSPEND] = "suspend_enabled", + [MSM_PM_MODE_ATTR_IDLE] = "idle_enabled", + [MSM_PM_MODE_ATTR_LATENCY] = "latency", + [MSM_PM_MODE_ATTR_RESIDENCY] = "residency", +}; + +static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ", + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse", + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = + "ramp_down_and_wfi", + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi", + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = + "power_collapse_no_xo_shutdown", + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] = + "standalone_power_collapse", +}; + +static struct msm_pm_platform_data *msm_pm_modes; +static struct msm_pm_irq_calls *msm_pm_irq_extns; + +struct msm_pm_kobj_attribute { + unsigned int cpu; + struct kobj_attribute ka; +}; + +#define GET_CPU_OF_ATTR(attr) \ + (container_of(attr, struct msm_pm_kobj_attribute, ka)->cpu) + +struct msm_pm_sysfs_sleep_mode { + struct kobject *kobj; + struct attribute_group attr_group; + struct attribute *attrs[MSM_PM_MODE_ATTR_NR + 1]; + struct msm_pm_kobj_attribute kas[MSM_PM_MODE_ATTR_NR]; +}; + +/* + * Write out the attribute. + */ +static ssize_t msm_pm_mode_attr_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct kernel_param kp; + unsigned int cpu; + struct msm_pm_platform_data *mode; + + if (msm_pm_sleep_mode_labels[i] == NULL) + continue; + + if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i])) + continue; + + cpu = GET_CPU_OF_ATTR(attr); + mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)]; + + if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) { + u32 arg = mode->suspend_enabled; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) { + u32 arg = mode->idle_enabled; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) { + u32 arg = mode->latency; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) { + u32 arg = mode->residency; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } + + break; + } + + if (ret > 0) { + strlcat(buf, "\n", PAGE_SIZE); + ret++; + } + + return ret; +} + +/* + * Read in the new attribute value. + */ +static ssize_t msm_pm_mode_attr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct kernel_param kp; + unsigned int cpu; + struct msm_pm_platform_data *mode; + + if (msm_pm_sleep_mode_labels[i] == NULL) + continue; + + if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i])) + continue; + + cpu = GET_CPU_OF_ATTR(attr); + mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)]; + + if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_SUSPEND])) { + kp.arg = &mode->suspend_enabled; + ret = param_set_byte(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_IDLE])) { + kp.arg = &mode->idle_enabled; + ret = param_set_byte(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_LATENCY])) { + kp.arg = &mode->latency; + ret = param_set_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + msm_pm_mode_attr_labels[MSM_PM_MODE_ATTR_RESIDENCY])) { + kp.arg = &mode->residency; + ret = param_set_ulong(buf, &kp); + } + + break; + } + + return ret ? ret : count; +} + + /* Add sysfs entries for one cpu. */ +static int __init msm_pm_mode_sysfs_add_cpu( + unsigned int cpu, struct kobject *modes_kobj) +{ + char cpu_name[8]; + struct kobject *cpu_kobj; + struct msm_pm_sysfs_sleep_mode *mode = NULL; + int i, j, k; + int ret; + + snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu); + cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj); + if (!cpu_kobj) { + pr_err("%s: cannot create %s kobject\n", __func__, cpu_name); + ret = -ENOMEM; + goto mode_sysfs_add_cpu_exit; + } + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + int idx = MSM_PM_MODE(cpu, i); + + if ((!msm_pm_modes[idx].suspend_supported) && + (!msm_pm_modes[idx].idle_supported)) + continue; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + pr_err("%s: cannot allocate memory for attributes\n", + __func__); + ret = -ENOMEM; + goto mode_sysfs_add_cpu_exit; + } + + mode->kobj = kobject_create_and_add( + msm_pm_sleep_mode_labels[i], cpu_kobj); + if (!mode->kobj) { + pr_err("%s: cannot create kobject\n", __func__); + ret = -ENOMEM; + goto mode_sysfs_add_cpu_exit; + } + + for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) { + if ((k == MSM_PM_MODE_ATTR_IDLE) && + !msm_pm_modes[idx].idle_supported) + continue; + if ((k == MSM_PM_MODE_ATTR_SUSPEND) && + !msm_pm_modes[idx].suspend_supported) + continue; + mode->kas[j].cpu = cpu; + mode->kas[j].ka.attr.mode = 0644; + mode->kas[j].ka.show = msm_pm_mode_attr_show; + mode->kas[j].ka.store = msm_pm_mode_attr_store; + mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k]; + mode->attrs[j] = &mode->kas[j].ka.attr; + j++; + } + mode->attrs[j] = NULL; + + mode->attr_group.attrs = mode->attrs; + ret = sysfs_create_group(mode->kobj, &mode->attr_group); + if (ret) { + printk(KERN_ERR + "%s: cannot create kobject attribute group\n", + __func__); + goto mode_sysfs_add_cpu_exit; + } + } + + ret = 0; + +mode_sysfs_add_cpu_exit: + if (ret) { + if (mode && mode->kobj) + kobject_del(mode->kobj); + kfree(mode); + } + + return ret; +} + +/* + * Add sysfs entries for the sleep modes. + */ +static int __init msm_pm_mode_sysfs_add(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *modes_kobj = NULL; + unsigned int cpu; + int ret; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + printk(KERN_ERR "%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + ret = -ENOENT; + goto mode_sysfs_add_exit; + } + + modes_kobj = kobject_create_and_add("modes", module_kobj); + if (!modes_kobj) { + printk(KERN_ERR "%s: cannot create modes kobject\n", __func__); + ret = -ENOMEM; + goto mode_sysfs_add_exit; + } + + for_each_possible_cpu(cpu) { + ret = msm_pm_mode_sysfs_add_cpu(cpu, modes_kobj); + if (ret) + goto mode_sysfs_add_exit; + } + + ret = 0; + +mode_sysfs_add_exit: + return ret; +} + +s32 msm_cpuidle_get_deep_idle_latency(void) +{ + int i = MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN); + return msm_pm_modes[i].latency - 1; +} + +void __init msm_pm_set_platform_data( + struct msm_pm_platform_data *data, int count) +{ + BUG_ON(MSM_PM_SLEEP_MODE_NR * num_possible_cpus() > count); + msm_pm_modes = data; +} + +void __init msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls) +{ + /* sanity check */ + BUG_ON(irq_calls == NULL || irq_calls->irq_pending == NULL || + irq_calls->idle_sleep_allowed == NULL || + irq_calls->enter_sleep1 == NULL || + irq_calls->enter_sleep2 == NULL || + irq_calls->exit_sleep1 == NULL || + irq_calls->exit_sleep2 == NULL || + irq_calls->exit_sleep3 == NULL); + + msm_pm_irq_extns = irq_calls; +} + +/****************************************************************************** + * Sleep Limitations + *****************************************************************************/ +enum { + SLEEP_LIMIT_NONE = 0, + SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2, + SLEEP_LIMIT_MASK = 0x03, +}; + +static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE; +#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE +enum { + SLEEP_RESOURCE_MEMORY_BIT0 = 0x0200, + SLEEP_RESOURCE_MEMORY_BIT1 = 0x0010, +}; +#endif + + +/****************************************************************************** + * Configure Hardware for Power Down/Up + *****************************************************************************/ + +#if defined(CONFIG_ARCH_MSM7X30) +#define APPS_CLK_SLEEP_EN (MSM_APCS_GCC_BASE + 0x020) +#define APPS_PWRDOWN (MSM_ACC0_BASE + 0x01c) +#define APPS_SECOP (MSM_TCSR_BASE + 0x038) +#define APPS_STANDBY_CTL NULL +#else +#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c) +#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440) +#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108) +#define APPS_SECOP NULL +#endif + +/* + * Configure hardware registers in preparation for Apps power down. + */ +static void msm_pm_config_hw_before_power_down(void) +{ + if (cpu_is_msm7x30() || cpu_is_msm8x55()) { + __raw_writel(4, APPS_SECOP); + } else if (cpu_is_msm7x27()) { + __raw_writel(0x1f, APPS_CLK_SLEEP_EN); + } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() || + cpu_is_msm7x25a() || cpu_is_msm7x25aa() || + cpu_is_msm7x25ab()) { + __raw_writel(0x7, APPS_CLK_SLEEP_EN); + } else if (cpu_is_qsd8x50()) { + __raw_writel(0x1f, APPS_CLK_SLEEP_EN); + mb(); + __raw_writel(0, APPS_STANDBY_CTL); + } + mb(); + __raw_writel(1, APPS_PWRDOWN); + mb(); +} + +/* + * Program the top csr from core0 context to put the + * core1 into GDFS, as core1 is not running yet. + */ +static void configure_top_csr(void) +{ + void __iomem *base_ptr; + unsigned int value = 0; + + base_ptr = core1_reset_base(); + if (!base_ptr) + return; + + /* bring the core1 out of reset */ + __raw_writel(0x3, base_ptr); + mb(); + /* + * override DBGNOPOWERDN and program the GDFS + * count val + */ + + __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38)); + mb(); + + /* Initialize the SPM0 and SPM1 registers */ + msm_spm_reinit(); + + /* enable TCSR for core1 */ + value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG)); + value |= BIT(22); + __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG); + mb(); + + /* set reset bit for SPM1 */ + value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG)); + value |= BIT(20); + __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG); + mb(); + + /* set CLK_OFF bit */ + value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG)); + value |= BIT(18); + __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG); + mb(); + + /* set clamps bit */ + value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG)); + value |= BIT(21); + __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG); + mb(); + + /* set power_up bit */ + value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG)); + value |= BIT(19); + __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG); + mb(); + + /* Disable TSCR for core0 */ + value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG)); + value &= ~BIT(22); + __raw_writel(value, MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG); + mb(); + __raw_writel(0x0, base_ptr); + mb(); +} + +/* + * Clear hardware registers after Apps powers up. + */ +static void msm_pm_config_hw_after_power_up(void) +{ + + if (cpu_is_msm7x30() || cpu_is_msm8x55()) { + __raw_writel(0, APPS_SECOP); + mb(); + __raw_writel(0, APPS_PWRDOWN); + mb(); + msm_spm_reinit(); + } else if (cpu_is_msm8625()) { + __raw_writel(0, APPS_PWRDOWN); + mb(); + + if (power_collapsed) { + /* + * enable the SCU while coming out of power + * collapse. + */ + scu_enable(MSM_SCU_BASE); + /* + * Program the top csr to put the core1 into GDFS. + */ + configure_top_csr(); + } + } else { + __raw_writel(0, APPS_PWRDOWN); + mb(); + __raw_writel(0, APPS_CLK_SLEEP_EN); + mb(); + } +} + +/* + * Configure hardware registers in preparation for SWFI. + */ +static void msm_pm_config_hw_before_swfi(void) +{ + if (cpu_is_qsd8x50()) { + __raw_writel(0x1f, APPS_CLK_SLEEP_EN); + mb(); + } else if (cpu_is_msm7x27()) { + __raw_writel(0x0f, APPS_CLK_SLEEP_EN); + mb(); + } else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() || + cpu_is_msm7x25a() || cpu_is_msm7x25aa() || + cpu_is_msm7x25ab()) { + __raw_writel(0x7, APPS_CLK_SLEEP_EN); + mb(); + } +} + +/* + * Respond to timing out waiting for Modem + * + * NOTE: The function never returns. + */ +static void msm_pm_timeout(void) +{ +#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP) + printk(KERN_EMERG "%s(): resetting chip\n", __func__); + msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL); +#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM) + printk(KERN_EMERG "%s(): resetting modem\n", __func__); + msm_proc_comm_reset_modem_now(); +#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT) + printk(KERN_EMERG "%s(): halting\n", __func__); +#endif + for (;;) + ; +} + + +/****************************************************************************** + * State Polling Definitions + *****************************************************************************/ + +struct msm_pm_polled_group { + uint32_t group_id; + + uint32_t bits_all_set; + uint32_t bits_all_clear; + uint32_t bits_any_set; + uint32_t bits_any_clear; + + uint32_t value_read; +}; + +/* + * Return true if all bits indicated by flag are set in source. + */ +static inline bool msm_pm_all_set(uint32_t source, uint32_t flag) +{ + return (source & flag) == flag; +} + +/* + * Return true if any bit indicated by flag are set in source. + */ +static inline bool msm_pm_any_set(uint32_t source, uint32_t flag) +{ + return !flag || (source & flag); +} + +/* + * Return true if all bits indicated by flag are cleared in source. + */ +static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag) +{ + return (~source & flag) == flag; +} + +/* + * Return true if any bit indicated by flag are cleared in source. + */ +static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag) +{ + return !flag || (~source & flag); +} + +/* + * Poll the shared memory states as indicated by the poll groups. + * + * nr_grps: number of groups in the array + * grps: array of groups + * + * The function returns when conditions specified by any of the poll + * groups become true. The conditions specified by a poll group are + * deemed true when 1) at least one bit from bits_any_set is set OR one + * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set + * are set; and 3) all bits in bits_all_clear are cleared. + * + * Return value: + * >=0: index of the poll group whose conditions have become true + * -ETIMEDOUT: timed out + */ +static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps) +{ + int i, k; + + for (i = 0; i < 50000; i++) { + for (k = 0; k < nr_grps; k++) { + bool all_set, all_clear; + bool any_set, any_clear; + + grps[k].value_read = smsm_get_state(grps[k].group_id); + + all_set = msm_pm_all_set(grps[k].value_read, + grps[k].bits_all_set); + all_clear = msm_pm_all_clear(grps[k].value_read, + grps[k].bits_all_clear); + any_set = msm_pm_any_set(grps[k].value_read, + grps[k].bits_any_set); + any_clear = msm_pm_any_clear(grps[k].value_read, + grps[k].bits_any_clear); + + if (all_set && all_clear && (any_set || any_clear)) + return k; + } + udelay(50); + } + + printk(KERN_ERR "%s failed:\n", __func__); + for (k = 0; k < nr_grps; k++) + printk(KERN_ERR "(%x, %x, %x, %x) %x\n", + grps[k].bits_all_set, grps[k].bits_all_clear, + grps[k].bits_any_set, grps[k].bits_any_clear, + grps[k].value_read); + + return -ETIMEDOUT; +} + + +/****************************************************************************** + * Suspend Max Sleep Time + *****************************************************************************/ + +#define SCLK_HZ (32768) +#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000) + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE +static int msm_pm_sleep_time_override; +module_param_named(sleep_time_override, + msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP); +#endif + +static uint32_t msm_pm_max_sleep_time; + +/* + * Convert time from nanoseconds to slow clock ticks, then cap it to the + * specified limit + */ +static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit) +{ + do_div(time_ns, NSEC_PER_SEC / SCLK_HZ); + return (time_ns > limit) ? limit : time_ns; +} + +/* + * Set the sleep time for suspend. 0 means infinite sleep time. + */ +void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) +{ + unsigned long flags; + + local_irq_save(flags); + if (max_sleep_time_ns == 0) { + msm_pm_max_sleep_time = 0; + } else { + msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time( + max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT); + + if (msm_pm_max_sleep_time == 0) + msm_pm_max_sleep_time = 1; + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, + "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__, + max_sleep_time_ns, msm_pm_max_sleep_time); + local_irq_restore(flags); +} +EXPORT_SYMBOL(msm_pm_set_max_sleep_time); + + +/****************************************************************************** + * Shared Memory Bits + *****************************************************************************/ + +#define DEM_MASTER_BITS_PER_CPU 6 + +/* Power Master State Bits - Per CPU */ +#define DEM_MASTER_SMSM_RUN \ + (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_RSA \ + (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \ + (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_SLEEP_EXIT \ + (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_READY \ + (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_SLEEP \ + (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) + +/* Power Slave State Bits */ +#define DEM_SLAVE_SMSM_RUN (0x0001) +#define DEM_SLAVE_SMSM_PWRC (0x0002) +#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004) +#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008) +#define DEM_SLAVE_SMSM_WFPI (0x0010) +#define DEM_SLAVE_SMSM_SLEEP (0x0020) +#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040) +#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080) +#define DEM_SLAVE_SMSM_RESET (0x0100) +#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200) + + +/****************************************************************************** + * Shared Memory Data + *****************************************************************************/ + +#define DEM_MAX_PORT_NAME_LEN (20) + +struct msm_pm_smem_t { + uint32_t sleep_time; + uint32_t irq_mask; + uint32_t resources_used; + uint32_t reserved1; + + uint32_t wakeup_reason; + uint32_t pending_irqs; + uint32_t rpc_prog; + uint32_t rpc_proc; + char smd_port_name[DEM_MAX_PORT_NAME_LEN]; + uint32_t reserved2; +}; + + +/****************************************************************************** + * + *****************************************************************************/ +static struct msm_pm_smem_t *msm_pm_smem_data; +static atomic_t msm_pm_init_done = ATOMIC_INIT(0); + +static int msm_pm_modem_busy(void) +{ + if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) { + MSM_PM_DPRINTK(MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, "%s(): master not ready\n", __func__); + return -EBUSY; + } + + return 0; +} + +/* + * Power collapse the Apps processor. This function executes the handshake + * protocol with Modem. + * + * Return value: + * -EAGAIN: modem reset occurred or early exit from power collapse + * -EBUSY: modem not ready for our power collapse -- no power loss + * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss + * 0: success + */ +static int msm_pm_power_collapse + (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit) +{ + struct msm_pm_polled_group state_grps[2]; + unsigned long saved_acpuclk_rate; + int collapsed = 0; + int ret; + int val; + int modem_early_exit = 0; + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__, + (int)from_idle, sleep_delay, sleep_limit); + + if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, "%s(): master not ready\n", __func__); + ret = -EBUSY; + goto power_collapse_bail; + } + + memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data)); + + if (cpu_is_msm8625()) { + /* Program the SPM */ + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, + false); + WARN_ON(ret); + } + + msm_pm_irq_extns->enter_sleep1(true, from_idle, + &msm_pm_smem_data->irq_mask); + msm_sirc_enter_sleep(); + msm_gpio_enter_sleep(from_idle); + + msm_pm_smem_data->sleep_time = sleep_delay; + msm_pm_smem_data->resources_used = sleep_limit; + + /* Enter PWRC/PWRC_SUSPEND */ + + if (from_idle) + smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN, + DEM_SLAVE_SMSM_PWRC); + else + smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC"); + MSM_PM_DEBUG_PRINT_SLEEP_INFO(); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse entry " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + goto power_collapse_early_exit; + } + + /* DEM Master in RSA */ + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA"); + + ret = msm_pm_irq_extns->enter_sleep2(true, from_idle); + if (ret < 0) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__, + ret); + goto power_collapse_early_exit; + } + + msm_pm_config_hw_before_power_down(); + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down"); + + saved_acpuclk_rate = acpuclk_power_collapse(); + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): change clock rate (old rate = %lu)\n", __func__, + saved_acpuclk_rate); + + if (saved_acpuclk_rate == 0) { + msm_pm_config_hw_after_power_up(); + goto power_collapse_early_exit; + } + + msm_pm_boot_config_before_pc(smp_processor_id(), + virt_to_phys(msm_pm_collapse_exit)); + +#ifdef CONFIG_VFP + if (from_idle) + vfp_pm_suspend(); +#endif + +#ifdef CONFIG_CACHE_L2X0 + if (!cpu_is_msm8625()) + l2cc_suspend(); + else + apps_power_collapse = 1; +#endif + + collapsed = msm_pm_collapse(); + + /* + * TBD: Currently recognise the MODEM early exit + * path by reading the MPA5_GDFS_CNT_VAL register. + */ + if (cpu_is_msm8625()) { + /* + * on system reset, default value of MPA5_GDFS_CNT_VAL + * is = 0x0, later modem reprogram this value to + * 0x00030004. Once APPS did a power collapse and + * coming out of it expected value of this register + * always be 0x00030004. Incase if APPS sees the value + * as 0x00030002 consider this case as a modem early + * exit. + */ + val = __raw_readl(MSM_CFG_CTL_BASE + 0x38); + if (val != 0x00030002) + power_collapsed = 1; + else + modem_early_exit = 1; + } + +#ifdef CONFIG_CACHE_L2X0 + if (!cpu_is_msm8625()) + l2cc_resume(); + else + apps_power_collapse = 0; +#endif + + msm_pm_boot_config_after_pc(smp_processor_id()); + + if (collapsed) { +#ifdef CONFIG_VFP + if (from_idle) + vfp_pm_resume(); +#endif + cpu_init(); + local_fiq_enable(); + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_collapse returned %d\n", __func__, collapsed); + + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): restore clock rate to %lu\n", __func__, + saved_acpuclk_rate); + if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate, + SETRATE_PC) < 0) + printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n", + __func__, saved_acpuclk_rate); + + msm_pm_irq_extns->exit_sleep1(msm_pm_smem_data->irq_mask, + msm_pm_smem_data->wakeup_reason, + msm_pm_smem_data->pending_irqs); + + msm_pm_config_hw_after_power_up(); + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up"); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_any_set = + DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse exit " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + goto power_collapse_early_exit; + } + + /* Sanity check */ + if (collapsed && !modem_early_exit) { + BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA)); + } else { + BUG_ON(!(state_grps[0].value_read & + DEM_MASTER_SMSM_PWRC_EARLY_EXIT)); + goto power_collapse_early_exit; + } + + /* Enter WFPI */ + + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND, + DEM_SLAVE_SMSM_WFPI); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI"); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse WFPI " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + ret = -EAGAIN; + goto power_collapse_restore_gpio_bail; + } + + /* DEM Master == RUN */ + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN"); + MSM_PM_DEBUG_PRINT_SLEEP_INFO(); + + msm_pm_irq_extns->exit_sleep2(msm_pm_smem_data->irq_mask, + msm_pm_smem_data->wakeup_reason, + msm_pm_smem_data->pending_irqs); + msm_pm_irq_extns->exit_sleep3(msm_pm_smem_data->irq_mask, + msm_pm_smem_data->wakeup_reason, + msm_pm_smem_data->pending_irqs); + msm_gpio_exit_sleep(); + msm_sirc_exit_sleep(); + + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN"); + + smd_sleep_exit(); + + if (cpu_is_msm8625()) { + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, + false); + WARN_ON(ret); + } + + return 0; + +power_collapse_early_exit: + /* Enter PWRC_EARLY_EXIT */ + + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND, + DEM_SLAVE_SMSM_PWRC_EARLY_EXIT); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT"); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE"); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse EARLY_EXIT " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + } + + /* DEM Master == RESET or PWRC_EARLY_EXIT */ + + ret = -EAGAIN; + +power_collapse_restore_gpio_bail: + msm_gpio_exit_sleep(); + msm_sirc_exit_sleep(); + + /* Enter RUN */ + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND | + DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN"); + + if (collapsed) + smd_sleep_exit(); + +power_collapse_bail: + if (cpu_is_msm8625()) { + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, + false); + WARN_ON(ret); + } + + return ret; +} + +/* + * Power collapse the Apps processor without involving Modem. + * + * Return value: + * 0: success + */ +static int __ref msm_pm_power_collapse_standalone(bool from_idle) +{ + int collapsed = 0; + int ret; + void *entry; + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, "%s()\n", __func__); + + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, false); + WARN_ON(ret); + + entry = (!smp_processor_id() || from_idle) ? + msm_pm_collapse_exit : msm_secondary_startup; + + msm_pm_boot_config_before_pc(smp_processor_id(), + virt_to_phys(entry)); + +#ifdef CONFIG_VFP + vfp_pm_suspend(); +#endif + +#ifdef CONFIG_CACHE_L2X0 + if (!cpu_is_msm8625()) + l2cc_suspend(); +#endif + + collapsed = msm_pm_collapse(); + +#ifdef CONFIG_CACHE_L2X0 + if (!cpu_is_msm8625()) + l2cc_resume(); +#endif + + msm_pm_boot_config_after_pc(smp_processor_id()); + + if (collapsed) { +#ifdef CONFIG_VFP + vfp_pm_resume(); +#endif + cpu_init(); + local_fiq_enable(); + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_collapse returned %d\n", __func__, collapsed); + + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false); + WARN_ON(ret); + + return !collapsed; +} + +/* + * Bring the Apps processor to SWFI. + * + * Return value: + * -EIO: could not ramp Apps processor clock + * 0: success + */ +static int msm_pm_swfi(bool ramp_acpu) +{ + unsigned long saved_acpuclk_rate = 0; + + if (ramp_acpu) { + saved_acpuclk_rate = acpuclk_wait_for_irq(); + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): change clock rate (old rate = %lu)\n", __func__, + saved_acpuclk_rate); + + if (!saved_acpuclk_rate) + return -EIO; + } + + if (!cpu_is_msm8625()) + msm_pm_config_hw_before_swfi(); + + msm_arch_idle(); + + if (ramp_acpu) { + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): restore clock rate to %lu\n", __func__, + saved_acpuclk_rate); + if (acpuclk_set_rate(smp_processor_id(), saved_acpuclk_rate, + SETRATE_SWFI) < 0) + printk(KERN_ERR + "%s(): failed to restore clock rate(%lu)\n", + __func__, saved_acpuclk_rate); + } + + return 0; +} + +static int64_t msm_pm_timer_enter_suspend(int64_t *period) +{ + int time = 0; + + time = msm_timer_get_sclk_time(period); + if (!time) + pr_err("%s: Unable to read sclk.\n", __func__); + return time; +} + +static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period) +{ + + if (time != 0) { + int64_t end_time = msm_timer_get_sclk_time(NULL); + if (end_time != 0) { + time = end_time - time; + if (time < 0) + time += period; + } else + time = 0; + } + return time; +} + +/****************************************************************************** + * External Idle/Suspend Functions + *****************************************************************************/ + +/* + * Put CPU in low power mode. + */ +void arch_idle(void) +{ + bool allow[MSM_PM_SLEEP_MODE_NR]; + uint32_t sleep_limit = SLEEP_LIMIT_NONE; + + int64_t timer_expiration; + int latency_qos; + int ret; + int i; + unsigned int cpu; + int64_t t1; + static DEFINE_PER_CPU(int64_t, t2); + int exit_stat; + + if (!atomic_read(&msm_pm_init_done)) + return; + + cpu = smp_processor_id(); + latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); + /* get the next timer expiration */ + timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length()); + + t1 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2)); + msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration); + exit_stat = MSM_PM_STAT_IDLE_SPIN; + + for (i = 0; i < ARRAY_SIZE(allow); i++) + allow[i] = true; + + if (num_online_cpus() > 1 || + (timer_expiration < msm_pm_idle_sleep_min_time) || + !msm_pm_irq_extns->idle_sleep_allowed()) { + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false; + } + + for (i = 0; i < ARRAY_SIZE(allow); i++) { + struct msm_pm_platform_data *mode = + &msm_pm_modes[MSM_PM_MODE(cpu, i)]; + if (!mode->idle_supported || !mode->idle_enabled || + mode->latency >= latency_qos || + mode->residency * 1000ULL >= timer_expiration) + allow[i] = false; + } + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { + uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM; + while (msm_pm_modem_busy() && wait_us) { + if (wait_us > 100) { + udelay(100); + wait_us -= 100; + } else { + udelay(wait_us); + wait_us = 0; + } + } + + if (msm_pm_modem_busy()) { + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] + = false; + } + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO, + "%s(): latency qos %d, next timer %lld, sleep limit %u\n", + __func__, latency_qos, timer_expiration, sleep_limit); + + for (i = 0; i < ARRAY_SIZE(allow); i++) + MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO, + "%s(): allow %s: %d\n", __func__, + msm_pm_sleep_mode_labels[i], (int)allow[i]); + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { + /* Sync the timer with SCLK, it is needed only for modem + * assissted pollapse case. + */ + int64_t next_timer_exp = msm_timer_enter_idle(); + uint32_t sleep_delay; + bool low_power = false; + + sleep_delay = (uint32_t) msm_pm_convert_and_cap_time( + next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT); + + if (sleep_delay == 0) /* 0 would mean infinite time */ + sleep_delay = 1; + + if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; + +#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE) + sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1; +#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION) + sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0; +#endif + + ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit); + low_power = (ret != -EBUSY && ret != -ETIMEDOUT); + msm_timer_exit_idle(low_power); + + if (ret) + exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE; + else { + exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE; + msm_pm_sleep_limit = sleep_limit; + } + } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { + ret = msm_pm_power_collapse_standalone(true); + exit_stat = ret ? + MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE : + MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE; + } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) { + ret = msm_pm_swfi(true); + if (ret) + while (!msm_pm_irq_extns->irq_pending()) + udelay(1); + exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI; + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + msm_pm_swfi(false); + exit_stat = MSM_PM_STAT_IDLE_WFI; + } else { + while (!msm_pm_irq_extns->irq_pending()) + udelay(1); + exit_stat = MSM_PM_STAT_IDLE_SPIN; + } + + __get_cpu_var(t2) = ktime_to_ns(ktime_get()); + msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1); +} + +/* + * Suspend the Apps processor. + * + * Return value: + * -EPERM: Suspend happened by a not permitted core + * -EAGAIN: modem reset occurred or early exit from suspend + * -EBUSY: modem not ready for our suspend + * -EINVAL: invalid sleep mode + * -EIO: could not ramp Apps processor clock + * -ETIMEDOUT: timed out waiting for modem's handshake + * 0: success + */ +static int msm_pm_enter(suspend_state_t state) +{ + bool allow[MSM_PM_SLEEP_MODE_NR]; + uint32_t sleep_limit = SLEEP_LIMIT_NONE; + int ret = -EPERM; + int i; + int64_t period = 0; + int64_t time = 0; + + /* Must executed by CORE0 */ + if (smp_processor_id()) { + __WARN(); + goto suspend_exit; + } + + time = msm_pm_timer_enter_suspend(&period); + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, + "%s(): sleep limit %u\n", __func__, sleep_limit); + + for (i = 0; i < ARRAY_SIZE(allow); i++) + allow[i] = true; + + for (i = 0; i < ARRAY_SIZE(allow); i++) { + struct msm_pm_platform_data *mode; + mode = &msm_pm_modes[MSM_PM_MODE(0, i)]; + if (!mode->suspend_supported || !mode->suspend_enabled) + allow[i] = false; + } + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { + enum msm_pm_time_stats_id id; + + clock_debug_print_enabled(); + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE + if (msm_pm_sleep_time_override > 0) { + int64_t ns; + ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override; + msm_pm_set_max_sleep_time(ns); + msm_pm_sleep_time_override = 0; + } +#endif + if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; + +#if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE) + sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1; +#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION) + sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0; +#elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN) + if (get_msm_migrate_pages_status() != MEM_OFFLINE) + sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0; +#endif + + for (i = 0; i < 30 && msm_pm_modem_busy(); i++) + udelay(500); + + ret = msm_pm_power_collapse( + false, msm_pm_max_sleep_time, sleep_limit); + + if (ret) + id = MSM_PM_STAT_FAILED_SUSPEND; + else { + id = MSM_PM_STAT_SUSPEND; + msm_pm_sleep_limit = sleep_limit; + } + + time = msm_pm_timer_exit_suspend(time, period); + msm_pm_add_stat(id, time); + } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { + ret = msm_pm_power_collapse_standalone(false); + } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) { + ret = msm_pm_swfi(true); + if (ret) + while (!msm_pm_irq_extns->irq_pending()) + udelay(1); + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + msm_pm_swfi(false); + } + +suspend_exit: + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, + "%s(): return %d\n", __func__, ret); + + return ret; +} + +static struct platform_suspend_ops msm_pm_ops = { + .enter = msm_pm_enter, + .valid = suspend_valid_only_mem, +}; + +/* Hotplug the "non boot" CPU's and put + * the cores into low power mode + */ +void msm_pm_cpu_enter_lowpower(unsigned int cpu) +{ + bool allow[MSM_PM_SLEEP_MODE_NR]; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct msm_pm_platform_data *mode; + + mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)]; + allow[i] = mode->suspend_supported && mode->suspend_enabled; + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO, + "CPU%u: %s: shutting down cpu\n", cpu, __func__); + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { + msm_pm_power_collapse_standalone(false); + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + msm_pm_swfi(false); + } else { + MSM_PM_DPRINTK(MSM_PM_DEBUG_HOTPLUG, KERN_INFO, + "CPU%u: %s: shutting down failed!!!\n", cpu, __func__); + } +} + +/****************************************************************************** + * Restart Definitions + *****************************************************************************/ + +static uint32_t restart_reason = 0x776655AA; + +static void msm_pm_power_off(void) +{ + msm_rpcrouter_close(); + msm_proc_comm(PCOM_POWER_DOWN, 0, 0); + for (;;) + ; +} + +static void msm_pm_restart(char str, const char *cmd) +{ + msm_rpcrouter_close(); + msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0); + + for (;;) + ; +} + +static int msm_reboot_call + (struct notifier_block *this, unsigned long code, void *_cmd) +{ + if ((code == SYS_RESTART) && _cmd) { + char *cmd = _cmd; + if (!strcmp(cmd, "bootloader")) { + restart_reason = 0x77665500; + } else if (!strcmp(cmd, "recovery")) { + restart_reason = 0x77665502; + } else if (!strcmp(cmd, "eraseflash")) { + restart_reason = 0x776655EF; + } else if (!strncmp(cmd, "oem-", 4)) { + unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff; + restart_reason = 0x6f656d00 | code; + } else { + restart_reason = 0x77665501; + } + } + return NOTIFY_DONE; +} + +static struct notifier_block msm_reboot_notifier = { + .notifier_call = msm_reboot_call, +}; + + +/* + * Initialize the power management subsystem. + * + * Return value: + * -ENODEV: initialization failed + * 0: success + */ +static int __init msm_pm_init(void) +{ + int ret; + int val; + enum msm_pm_time_stats_id enable_stats[] = { + MSM_PM_STAT_REQUESTED_IDLE, + MSM_PM_STAT_IDLE_SPIN, + MSM_PM_STAT_IDLE_WFI, + MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE, + MSM_PM_STAT_SUSPEND, + MSM_PM_STAT_FAILED_SUSPEND, + MSM_PM_STAT_NOT_IDLE, + }; + +#ifdef CONFIG_CPU_V7 + pgd_t *pc_pgd; + pmd_t *pmd; + unsigned long pmdval; + unsigned long exit_phys; + + exit_phys = virt_to_phys(msm_pm_collapse_exit); + + /* Page table for cores to come back up safely. */ + pc_pgd = pgd_alloc(&init_mm); + if (!pc_pgd) + return -ENOMEM; + pmd = pmd_offset(pud_offset(pc_pgd + pgd_index(exit_phys), exit_phys), + exit_phys); + pmdval = (exit_phys & PGDIR_MASK) | + PMD_TYPE_SECT | PMD_SECT_AP_WRITE; + pmd[0] = __pmd(pmdval); + pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); + + msm_saved_state_phys = + allocate_contiguous_ebi_nomap(CPU_SAVED_STATE_SIZE * + num_possible_cpus(), 4); + if (!msm_saved_state_phys) + return -ENOMEM; + msm_saved_state = ioremap_nocache(msm_saved_state_phys, + CPU_SAVED_STATE_SIZE * + num_possible_cpus()); + if (!msm_saved_state) + return -ENOMEM; + + /* It is remotely possible that the code in msm_pm_collapse_exit() + * which turns on the MMU with this mapping is in the + * next even-numbered megabyte beyond the + * start of msm_pm_collapse_exit(). + * Map this megabyte in as well. + */ + pmd[2] = __pmd(pmdval + (2 << (PGDIR_SHIFT - 1))); + flush_pmd_entry(pmd); + msm_pm_pc_pgd = virt_to_phys(pc_pgd); + clean_caches((unsigned long)&msm_pm_pc_pgd, sizeof(msm_pm_pc_pgd), + virt_to_phys(&msm_pm_pc_pgd)); +#endif + + pm_power_off = msm_pm_power_off; + arm_pm_restart = msm_pm_restart; + register_reboot_notifier(&msm_reboot_notifier); + + msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA, + sizeof(*msm_pm_smem_data)); + if (msm_pm_smem_data == NULL) { + printk(KERN_ERR "%s: failed to get smsm_data\n", __func__); + return -ENODEV; + } + + ret = msm_timer_init_time_sync(msm_pm_timeout); + if (ret) + return ret; + + ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0); + if (ret) { + printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n", + __func__, ret); + return ret; + } + + if (cpu_is_msm8625()) { + target_type = TARGET_IS_8625; + clean_caches((unsigned long)&target_type, sizeof(target_type), + virt_to_phys(&target_type)); + + /* + * Configure the MPA5_GDFS_CNT_VAL register for + * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the + * DBGNOPOWERDN for each cpu. + * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for + * GDFS control. + */ + val = 0x00030002; + __raw_writel(val, (MSM_CFG_CTL_BASE + 0x38)); + + l2x0_base_addr = MSM_L2CC_BASE; + } + +#ifdef CONFIG_MSM_MEMORY_LOW_POWER_MODE + /* The wakeup_reason field is overloaded during initialization time + to signal Modem that Apps will control the low power modes of + the memory. + */ + msm_pm_smem_data->wakeup_reason = 1; + smsm_change_state(SMSM_APPS_DEM, 0, DEM_SLAVE_SMSM_RUN); +#endif + + BUG_ON(msm_pm_modes == NULL); + + suspend_set_ops(&msm_pm_ops); + + msm_pm_mode_sysfs_add(); + msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats)); + + atomic_set(&msm_pm_init_done, 1); + return 0; +} + +late_initcall_sync(msm_pm_init); diff --git a/arch/arm/mach-msm/pmic.c b/arch/arm/mach-msm/pmic.c new file mode 100644 index 00000000000..1e927109ad0 --- /dev/null +++ b/arch/arm/mach-msm/pmic.c @@ -0,0 +1,1286 @@ +/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smd_rpcrouter.h" + +#define TRACE_PMIC 0 + +#if TRACE_PMIC +#define PMIC(x...) printk(KERN_INFO "[PMIC] " x) +#else +#define PMIC(x...) do {} while (0) +#endif + + +#define LIB_NULL_PROC 0 +#define LIB_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define LP_MODE_CONTROL_PROC 2 +#define VREG_SET_LEVEL_PROC 3 +#define VREG_PULL_DOWN_SWITCH_PROC 4 +#define SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC 5 +#define SECURE_MPP_CONFIG_I_SINK_PROC 6 +#define RTC_START_PROC 7 +#define RTC_STOP_PROC 8 +#define RTC_GET_TIME_PROC 9 +#define RTC_ENABLE_ALARM_PROC 10 +#define RTC_DISABLE_ALARM_PROC 11 +#define RTC_GET_ALARM_TIME_PROC 12 +#define RTC_GET_ALARM_STATUS_PROC 13 +#define RTC_SET_TIME_ADJUST_PROC 14 +#define RTC_GET_TIME_ADJUST_PROC 15 +#define SET_LED_INTENSITY_PROC 16 +#define FLASH_LED_SET_CURRENT_PROC 17 +#define FLASH_LED_SET_MODE_PROC 18 +#define FLASH_LED_SET_POLARITY_PROC 19 +#define SPEAKER_CMD_PROC 20 +#define SET_SPEAKER_GAIN_PROC 21 +#define VIB_MOT_SET_VOLT_PROC 22 +#define VIB_MOT_SET_MODE_PROC 23 +#define VIB_MOT_SET_POLARITY_PROC 24 +#define VID_EN_PROC 25 +#define VID_IS_EN_PROC 26 +#define VID_LOAD_DETECT_EN_PROC 27 +#define MIC_EN_PROC 28 +#define MIC_IS_EN_PROC 29 +#define MIC_SET_VOLT_PROC 30 +#define MIC_GET_VOLT_PROC 31 +#define SPKR_EN_RIGHT_CHAN_PROC 32 +#define SPKR_IS_RIGHT_CHAN_EN_PROC 33 +#define SPKR_EN_LEFT_CHAN_PROC 34 +#define SPKR_IS_LEFT_CHAN_EN_PROC 35 +#define SET_SPKR_CONFIGURATION_PROC 36 +#define GET_SPKR_CONFIGURATION_PROC 37 +#define SPKR_GET_GAIN_PROC 38 +#define SPKR_IS_EN_PROC 39 +#define SPKR_EN_MUTE_PROC 40 +#define SPKR_IS_MUTE_EN_PROC 41 +#define SPKR_SET_DELAY_PROC 42 +#define SPKR_GET_DELAY_PROC 43 +#define SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC 44 +#define SET_SPEAKER_DELAY_PROC 45 +#define SPEAKER_1K6_ZIN_ENABLE_PROC 46 +#define SPKR_SET_MUX_HPF_CORNER_FREQ_PROC 47 +#define SPKR_GET_MUX_HPF_CORNER_FREQ_PROC 48 +#define SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC 49 +#define SPKR_EN_STEREO_PROC 50 +#define SPKR_IS_STEREO_EN_PROC 51 +#define SPKR_SELECT_USB_WITH_HPF_20HZ_PROC 52 +#define SPKR_IS_USB_WITH_HPF_20HZ_PROC 53 +#define SPKR_BYPASS_MUX_PROC 54 +#define SPKR_IS_MUX_BYPASSED_PROC 55 +#define SPKR_EN_HPF_PROC 56 +#define SPKR_IS_HPF_EN_PROC 57 +#define SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC 58 +#define SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC 59 +#define SPKR_ADD_RIGHT_LEFT_CHAN_PROC 60 +#define SPKR_SET_GAIN_PROC 61 +#define SPKR_EN_PROC 62 +#define HSED_SET_PERIOD_PROC 63 +#define HSED_SET_HYSTERESIS_PROC 64 +#define HSED_SET_CURRENT_THRESHOLD_PROC 65 +#define HSED_ENABLE_PROC 66 +#define HIGH_CURRENT_LED_SET_CURRENT_PROC 67 +#define HIGH_CURRENT_LED_SET_POLARITY_PROC 68 +#define HIGH_CURRENT_LED_SET_MODE_PROC 69 +#define LP_FORCE_LPM_CONTROL_PROC 70 +#define LOW_CURRENT_LED_SET_EXT_SIGNAL_PROC 71 +#define LOW_CURRENT_LED_SET_CURRENT_PROC 72 +#define SPKR_SET_VSEL_LDO_PROC 86 +#define HP_SPKR_CTRL_AUX_GAIN_INPUT_PROC 87 +#define HP_SPKR_MSTR_EN_PROC 88 +#define SPKR_SET_BOOST_PROC 89 +#define HP_SPKR_PRM_IN_EN_PROC 90 +#define HP_SPKR_CTRL_PRM_GAIN_INPUT_PROC 91 +#define HP_SPKR_MUTE_EN_PROC 92 +#define SPKR_BYPASS_EN_PROC 93 +#define HP_SPKR_AUX_IN_EN_PROC 94 +#define XO_CORE_FORCE_ENABLE 96 +#define GPIO_SET_CURRENT_SOURCE_PULLS_PROC 97 +#define GPIO_SET_GPIO_DIRECTION_INPUT_PROC 98 +#define GPIO_SET_EXT_PIN_CONFIG_PROC 99 +#define GPIO_SET_GPIO_CONFIG_PROC 100 +#define GPIO_CONFIG_DIGITAL_OUTPUT_PROC 101 +#define GPIO_GET_GPIO_DIRECTION_PROC 102 +#define GPIO_SET_SLEEP_CLK_CONFIG_PROC 103 +#define GPIO_CONFIG_DIGITAL_INPUT_PROC 104 +#define GPIO_SET_OUTPUT_BUFFER_CONFIGURATION_PROC 105 +#define GPIO_SET_PROC 106 +#define GPIO_CONFIG_MODE_SELECTION_PROC 107 +#define GPIO_SET_INVERSION_CONFIGURATION_PROC 108 +#define GPIO_SET_GPIO_DIRECTION_OUTPUT_PROC 109 +#define GPIO_SET_SOURCE_CONFIGURATION_PROC 110 +#define GPIO_GET_PROC 111 +#define GPIO_SET_VOLTAGE_SOURCE_PROC 112 +#define GPIO_SET_OUTPUT_BUFFER_DRIVE_STRENGTH_PROC 113 + +/* rpc related */ +#define PMIC_RPC_TIMEOUT (5*HZ) + +#define PMIC_PDEV_NAME "rs00010001:00000000" +#define PMIC_RPC_PROG 0x30000061 +#define PMIC_RPC_VER_1_1 0x00010001 +#define PMIC_RPC_VER_2_1 0x00020001 +#define PMIC_RPC_VER_3_1 0x00030001 +#define PMIC_RPC_VER_5_1 0x00050001 +#define PMIC_RPC_VER_6_1 0x00060001 + +/* error bit flags defined by modem side */ +#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001) +#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002) +#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004) +#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008) +#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010) + +#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F) /* all 5 previous */ + +#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080) +#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100) + +#define PMIC_BUFF_SIZE 256 + +struct pmic_buf { + char *start; /* buffer start addr */ + char *end; /* buffer end addr */ + int size; /* buffer size */ + char *data; /* payload begin addr */ + int len; /* payload len */ +}; + +static DEFINE_MUTEX(pmic_mtx); + +struct pmic_ctrl { + int inited; + struct pmic_buf tbuf; + struct pmic_buf rbuf; + struct msm_rpc_endpoint *endpoint; +}; + +static struct pmic_ctrl pmic_ctrl = { + .inited = -1, +}; + +/* Add newer versions at the top of array */ +static const unsigned int rpc_vers[] = { + PMIC_RPC_VER_6_1, + PMIC_RPC_VER_5_1, + PMIC_RPC_VER_3_1, + PMIC_RPC_VER_2_1, + PMIC_RPC_VER_1_1, +}; + +static int pmic_rpc_req_reply(struct pmic_buf *tbuf, + struct pmic_buf *rbuf, int proc); +static int pmic_rpc_set_only(uint data0, uint data1, uint data2, + uint data3, int num, int proc); +static int pmic_rpc_set_struct(int, uint, uint *data, uint size, int proc); +static int pmic_rpc_set_get(uint setdata, uint *getdata, int size, int proc); +static int pmic_rpc_get_only(uint *getdata, int size, int proc); + +static int pmic_buf_init(void) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + + memset(&pmic_ctrl, 0, sizeof(pmic_ctrl)); + + pm->tbuf.start = kmalloc(PMIC_BUFF_SIZE, GFP_KERNEL); + if (pm->tbuf.start == NULL) { + printk(KERN_ERR "%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + + pm->tbuf.data = pm->tbuf.start; + pm->tbuf.size = PMIC_BUFF_SIZE; + pm->tbuf.end = pm->tbuf.start + PMIC_BUFF_SIZE; + pm->tbuf.len = 0; + + pm->rbuf.start = kmalloc(PMIC_BUFF_SIZE, GFP_KERNEL); + if (pm->rbuf.start == NULL) { + kfree(pm->tbuf.start); + printk(KERN_ERR "%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + pm->rbuf.data = pm->rbuf.start; + pm->rbuf.size = PMIC_BUFF_SIZE; + pm->rbuf.end = pm->rbuf.start + PMIC_BUFF_SIZE; + pm->rbuf.len = 0; + + pm->inited = 1; + + return 0; +} + +static inline void pmic_buf_reserve(struct pmic_buf *bp, int len) +{ + bp->data += len; + bp->len += len; +} + +static inline void pmic_buf_reset(struct pmic_buf *bp) +{ + bp->data = bp->start; + bp->len = 0; +} + +static int modem_to_linux_err(uint err) +{ + if (err == 0) + return 0; + + if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE) + return -EINVAL; /* PM_ERR_FLAG__PAR[1..5]_OUT_OF_RANGE */ + + if (err & PM_ERR_FLAG__SBI_OPT_ERR) + return -EIO; + + if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED) + return -ENOSYS; + + return -EPERM; +} + +static int pmic_put_tx_data(struct pmic_buf *tp, uint datav) +{ + uint *lp; + + if ((tp->size - tp->len) < sizeof(datav)) { + printk(KERN_ERR "%s: OVERFLOW size=%d len=%d\n", + __func__, tp->size, tp->len); + return -1; + } + + lp = (uint *)tp->data; + *lp = cpu_to_be32(datav); + tp->data += sizeof(datav); + tp->len += sizeof(datav); + + return sizeof(datav); +} + +static int pmic_pull_rx_data(struct pmic_buf *rp, uint *datap) +{ + uint *lp; + + if (rp->len < sizeof(*datap)) { + printk(KERN_ERR "%s: UNDERRUN len=%d\n", __func__, rp->len); + return -1; + } + lp = (uint *)rp->data; + *datap = be32_to_cpu(*lp); + rp->data += sizeof(*datap); + rp->len -= sizeof(*datap); + + return sizeof(*datap); +} + + +/* + * + * +-------------------+ + * | PROC cmd layer | + * +-------------------+ + * | RPC layer | + * +-------------------+ + * + * 1) network byte order + * 2) RPC request header(40 bytes) and RPC reply header (24 bytes) + * 3) each transaction consists of a request and reply + * 3) PROC (comamnd) layer has its own sub-protocol defined + * 4) sub-protocol can be grouped to follwoing 7 cases: + * a) set one argument, no get + * b) set two argument, no get + * c) set three argument, no get + * d) set a struct, no get + * e) set a argument followed by a struct, no get + * f) set a argument, get a argument + * g) no set, get either a argument or a struct + */ + +/** + * pmic_rpc_req_reply() - send request and wait for reply + * @tbuf: buffer contains arguments + * @rbuf: buffer to be filled with arguments at reply + * @proc: command/request id + * + * This function send request to modem and wait until reply received + */ +static int pmic_rpc_req_reply(struct pmic_buf *tbuf, struct pmic_buf *rbuf, + int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + int ans, len, i; + + + if ((pm->endpoint == NULL) || IS_ERR(pm->endpoint)) { + for (i = 0; i < ARRAY_SIZE(rpc_vers); i++) { + pm->endpoint = msm_rpc_connect_compatible(PMIC_RPC_PROG, + rpc_vers[i], MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(pm->endpoint)) { + ans = PTR_ERR(pm->endpoint); + printk(KERN_ERR "%s: init rpc failed! ans = %d" + " for 0x%x version, fallback\n", + __func__, ans, rpc_vers[i]); + } else { + printk(KERN_DEBUG "%s: successfully connected" + " to 0x%x rpc version\n", + __func__, rpc_vers[i]); + break; + } + } + } + + if (IS_ERR(pm->endpoint)) { + ans = PTR_ERR(pm->endpoint); + return ans; + } + + /* + * data is point to next available space at this moment, + * move it back to beginning of request header and increase + * the length + */ + tbuf->data = tbuf->start; + + len = msm_rpc_call_reply(pm->endpoint, proc, + tbuf->data, tbuf->len, + rbuf->data, rbuf->size, + PMIC_RPC_TIMEOUT); + + if (len <= 0) { + printk(KERN_ERR "%s: rpc failed! len = %d\n", __func__, len); + pm->endpoint = NULL; /* re-connect later ? */ + return len; + } + + rbuf->len = len; + /* strip off rpc_reply_hdr */ + rbuf->data += sizeof(struct rpc_reply_hdr); + rbuf->len -= sizeof(struct rpc_reply_hdr); + + return rbuf->len; +} + +/** + * pmic_rpc_set_only() - set arguments and no get + * @data0: first argumrnt + * @data1: second argument + * @data2: third argument + * @data3: fourth argument + * @num: number of argument + * @proc: command/request id + * + * This function covers case a, b, and c + */ +static int pmic_rpc_set_only(uint data0, uint data1, uint data2, uint data3, + int num, int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + int stat; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + if (num > 0) + pmic_put_tx_data(tp, data0); + + if (num > 1) + pmic_put_tx_data(tp, data1); + + if (num > 2) + pmic_put_tx_data(tp, data2); + + if (num > 3) + pmic_put_tx_data(tp, data3); + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + +/** + * pmic_rpc_set_struct() - set the whole struct + * @xflag: indicates an extra argument + * @xdata: the extra argument + * @*data: starting address of struct + * @size: size of struct + * @proc: command/request id + * + * This fucntion covers case d and e + */ +static int pmic_rpc_set_struct(int xflag, uint xdata, uint *data, uint size, + int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + int i, stat, more_data; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + if (xflag) + pmic_put_tx_data(tp, xdata); + + more_data = 1; /* tell server there have more data followed */ + pmic_put_tx_data(tp, more_data); + + size >>= 2; + for (i = 0; i < size; i++) { + pmic_put_tx_data(tp, *data); + data++; + } + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + +/** + * pmic_rpc_set_get() - set one argument and get one argument + * @setdata: set argument + * @*getdata: memory to store argumnet + * @size: size of memory + * @proc: command/request id + * + * This function covers case f + */ +static int pmic_rpc_set_get(uint setdata, uint *getdata, int size, int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + unsigned int *lp; + int i, stat, more_data; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + pmic_put_tx_data(tp, setdata); + + /* + * more_data = TRUE to ask server reply with requested datum + * otherwise, server will reply without datum + */ + more_data = (getdata != NULL); + pmic_put_tx_data(tp, more_data); + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + pmic_pull_rx_data(rp, &more_data); + + if (more_data) { /* more data followed */ + size >>= 2; + lp = getdata; + for (i = 0; i < size; i++) { + if (pmic_pull_rx_data(rp, lp++) < 0) + break; /* not supposed to happen */ + } + } + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + +/** + * pmic_rpc_get_only() - get one or more than one arguments + * @*getdata: memory to store arguments + * @size: size of mmory + * @proc: command/request id + * + * This function covers case g + */ +static int pmic_rpc_get_only(uint *getdata, int size, int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + unsigned int *lp; + int i, stat, more_data; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + /* + * more_data = TRUE to ask server reply with requested datum + * otherwise, server will reply without datum + */ + more_data = (getdata != NULL); + pmic_put_tx_data(tp, more_data); + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + pmic_pull_rx_data(rp, &more_data); + + if (more_data) { /* more data followed */ + size >>= 2; + lp = getdata; + for (i = 0; i < size; i++) { + if (pmic_pull_rx_data(rp, lp++) < 0) + break; /* not supposed to happen */ + } + } + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id) +{ + return pmic_rpc_set_only(cmd, id, 0, 0, 2, LP_MODE_CONTROL_PROC); +} +EXPORT_SYMBOL(pmic_lp_mode_control); + +int pmic_vreg_set_level(enum vreg_id vreg, int level) +{ + return pmic_rpc_set_only(vreg, level, 0, 0, 2, VREG_SET_LEVEL_PROC); +} +EXPORT_SYMBOL(pmic_vreg_set_level); + +int pmic_vreg_pull_down_switch(enum switch_cmd cmd, enum vreg_pdown_id id) +{ + return pmic_rpc_set_only(cmd, id, 0, 0, 2, VREG_PULL_DOWN_SWITCH_PROC); +} +EXPORT_SYMBOL(pmic_vreg_pull_down_switch); + +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, + enum mpp_dlogic_out_ctrl out) +{ + return pmic_rpc_set_only(which, level, out, 0, 3, + SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_control_digital_output); + +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, + enum mpp_i_sink_switch onoff) +{ + return pmic_rpc_set_only(which, level, onoff, 0, 3, + SECURE_MPP_CONFIG_I_SINK_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_config_i_sink); + +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, + enum mpp_dlogic_in_dbus dbus) +{ + return pmic_rpc_set_only(which, level, dbus, 0, 3, + SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_config_digital_input); + +int pmic_rtc_start(struct rtc_time *time) +{ + return pmic_rpc_set_struct(0, 0, (uint *)time, sizeof(*time), + RTC_START_PROC); +} +EXPORT_SYMBOL(pmic_rtc_start); + +int pmic_rtc_stop(void) +{ + return pmic_rpc_set_only(0, 0, 0, 0, 0, RTC_STOP_PROC); +} +EXPORT_SYMBOL(pmic_rtc_stop); + +int pmic_rtc_get_time(struct rtc_time *time) +{ + return pmic_rpc_get_only((uint *)time, sizeof(*time), + RTC_GET_TIME_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_time); + +int pmic_rtc_enable_alarm(enum rtc_alarm alarm, + struct rtc_time *time) +{ + return pmic_rpc_set_struct(1, alarm, (uint *)time, sizeof(*time), + RTC_ENABLE_ALARM_PROC); +} +EXPORT_SYMBOL(pmic_rtc_enable_alarm); + +int pmic_rtc_disable_alarm(enum rtc_alarm alarm) +{ + return pmic_rpc_set_only(alarm, 0, 0, 0, 1, RTC_DISABLE_ALARM_PROC); +} +EXPORT_SYMBOL(pmic_rtc_disable_alarm); + +int pmic_rtc_get_alarm_time(enum rtc_alarm alarm, + struct rtc_time *time) +{ + return pmic_rpc_set_get(alarm, (uint *)time, sizeof(*time), + RTC_GET_ALARM_TIME_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_alarm_time); + +int pmic_rtc_get_alarm_status(uint *status) +{ + return pmic_rpc_get_only(status, sizeof(*status), + RTC_GET_ALARM_STATUS_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_alarm_status); + +int pmic_rtc_set_time_adjust(uint adjust) +{ + return pmic_rpc_set_only(adjust, 0, 0, 0, 1, + RTC_SET_TIME_ADJUST_PROC); +} +EXPORT_SYMBOL(pmic_rtc_set_time_adjust); + +int pmic_rtc_get_time_adjust(uint *adjust) +{ + return pmic_rpc_get_only(adjust, sizeof(*adjust), + RTC_GET_TIME_ADJUST_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_time_adjust); + +/* + * generic speaker + */ +int pmic_speaker_cmd(const enum spkr_cmd cmd) +{ + return pmic_rpc_set_only(cmd, 0, 0, 0, 1, SPEAKER_CMD_PROC); +} +EXPORT_SYMBOL(pmic_speaker_cmd); + +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg) +{ + return pmic_rpc_set_struct(0, 0, (uint *)cfg, sizeof(*cfg), + SET_SPKR_CONFIGURATION_PROC); +} +EXPORT_SYMBOL(pmic_set_spkr_configuration); + +int pmic_get_spkr_configuration(struct spkr_config_mode *cfg) +{ + return pmic_rpc_get_only((uint *)cfg, sizeof(*cfg), + GET_SPKR_CONFIGURATION_PROC); +} +EXPORT_SYMBOL(pmic_get_spkr_configuration); + +int pmic_spkr_en_right_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_RIGHT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_right_chan); + +int pmic_spkr_is_right_chan_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_RIGHT_CHAN_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_right_chan_en); + +int pmic_spkr_en_left_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_LEFT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_left_chan); + +int pmic_spkr_is_left_chan_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_LEFT_CHAN_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_left_chan_en); + +int pmic_set_speaker_gain(enum spkr_gain gain) +{ + return pmic_rpc_set_only(gain, 0, 0, 0, 1, SET_SPEAKER_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_set_speaker_gain); + +int pmic_set_speaker_delay(enum spkr_dly delay) +{ + return pmic_rpc_set_only(delay, 0, 0, 0, 1, SET_SPEAKER_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_set_speaker_delay); + +int pmic_speaker_1k6_zin_enable(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPEAKER_1K6_ZIN_ENABLE_PROC); +} +EXPORT_SYMBOL(pmic_speaker_1k6_zin_enable); + +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq) +{ + return pmic_rpc_set_only(freq, 0, 0, 0, 1, + SPKR_SET_MUX_HPF_CORNER_FREQ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_mux_hpf_corner_freq); + +int pmic_spkr_get_mux_hpf_corner_freq(enum spkr_hpf_corner_freq *freq) +{ + return pmic_rpc_get_only(freq, sizeof(*freq), + SPKR_GET_MUX_HPF_CORNER_FREQ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_get_mux_hpf_corner_freq); + +int pmic_spkr_select_usb_with_hpf_20hz(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_SELECT_USB_WITH_HPF_20HZ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_select_usb_with_hpf_20hz); + +int pmic_spkr_is_usb_with_hpf_20hz(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_USB_WITH_HPF_20HZ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_usb_with_hpf_20hz); + +int pmic_spkr_bypass_mux(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_BYPASS_MUX_PROC); +} +EXPORT_SYMBOL(pmic_spkr_bypass_mux); + +int pmic_spkr_is_mux_bypassed(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_MUX_BYPASSED_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_mux_bypassed); + +int pmic_spkr_en_hpf(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_HPF_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_hpf); + +int pmic_spkr_is_hpf_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_HPF_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_hpf_en); + +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_sink_curr_from_ref_volt_cir); + +int pmic_spkr_is_sink_curr_from_ref_volt_cir_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_sink_curr_from_ref_volt_cir_en); + +/* + * speaker indexed by left_right + */ +int pmic_spkr_en(enum spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, SPKR_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en); + +int pmic_spkr_is_en(enum spkr_left_right left_right, uint *enabled) +{ + return pmic_rpc_set_get(left_right, enabled, sizeof(*enabled), + SPKR_IS_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_en); + +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain) +{ + return pmic_rpc_set_only(left_right, gain, 0, 0, 2, SPKR_SET_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_gain); + +int pmic_spkr_get_gain(enum spkr_left_right left_right, enum spkr_gain *gain) +{ + return pmic_rpc_set_get(left_right, gain, sizeof(*gain), + SPKR_GET_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_get_gain); + +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay) +{ + return pmic_rpc_set_only(left_right, delay, 0, 0, 2, + SPKR_SET_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_delay); + +int pmic_spkr_get_delay(enum spkr_left_right left_right, enum spkr_dly *delay) +{ + return pmic_rpc_set_get(left_right, delay, sizeof(*delay), + SPKR_GET_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_spkr_get_delay); + +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled) +{ + return pmic_rpc_set_only(left_right, enabled, 0, 0, 2, + SPKR_EN_MUTE_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_mute); + +int pmic_spkr_is_mute_en(enum spkr_left_right left_right, uint *enabled) +{ + return pmic_rpc_set_get(left_right, enabled, sizeof(*enabled), + SPKR_IS_MUTE_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_mute_en); + +int pmic_spkr_set_vsel_ldo(enum spkr_left_right left_right, + enum spkr_ldo_v_sel vlt_cntrl) +{ + return pmic_rpc_set_only(left_right, vlt_cntrl, 0, 0, 2, + SPKR_SET_VSEL_LDO_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_vsel_ldo); + +int pmic_spkr_set_boost(enum spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, + SPKR_SET_BOOST_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_boost); + +int pmic_spkr_bypass_en(enum spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, + SPKR_BYPASS_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_bypass_en); + +/* + * mic + */ +int pmic_mic_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, MIC_EN_PROC); +} +EXPORT_SYMBOL(pmic_mic_en); + +int pmic_mic_is_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), MIC_IS_EN_PROC); +} +EXPORT_SYMBOL(pmic_mic_is_en); + +int pmic_mic_set_volt(enum mic_volt vol) +{ + return pmic_rpc_set_only(vol, 0, 0, 0, 1, MIC_SET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_mic_set_volt); + +int pmic_mic_get_volt(enum mic_volt *voltage) +{ + return pmic_rpc_get_only(voltage, sizeof(*voltage), MIC_GET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_mic_get_volt); + +int pmic_vib_mot_set_volt(uint vol) +{ + return pmic_rpc_set_only(vol, 0, 0, 0, 1, VIB_MOT_SET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_volt); + +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode) +{ + return pmic_rpc_set_only(mode, 0, 0, 0, 1, VIB_MOT_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_mode); + +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol) +{ + return pmic_rpc_set_only(pol, 0, 0, 0, 1, VIB_MOT_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_polarity); + +int pmic_vid_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_en); + +int pmic_vid_is_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), VID_IS_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_is_en); + +int pmic_vid_load_detect_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_LOAD_DETECT_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_load_detect_en); + +int pmic_set_led_intensity(enum ledtype type, int level) +{ + return pmic_rpc_set_only(type, level, 0, 0, 2, SET_LED_INTENSITY_PROC); +} +EXPORT_SYMBOL(pmic_set_led_intensity); + +int pmic_flash_led_set_current(const uint16_t milliamps) +{ + return pmic_rpc_set_only(milliamps, 0, 0, 0, 1, + FLASH_LED_SET_CURRENT_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_current); + +int pmic_flash_led_set_mode(enum flash_led_mode mode) +{ + return pmic_rpc_set_only((int)mode, 0, 0, 0, 1, + FLASH_LED_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_mode); + +int pmic_flash_led_set_polarity(enum flash_led_pol pol) +{ + return pmic_rpc_set_only((int)pol, 0, 0, 0, 1, + FLASH_LED_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_polarity); + +int pmic_spkr_add_right_left_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_ADD_RIGHT_LEFT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_add_right_left_chan); + +int pmic_spkr_is_right_left_chan_added(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_right_left_chan_added); + +int pmic_spkr_en_stereo(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_STEREO_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_stereo); + +int pmic_spkr_is_stereo_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_STEREO_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_stereo_en); + +int pmic_hsed_set_period( + enum hsed_controller controller, + enum hsed_period_pre_div period_pre_div, + enum hsed_period_time period_time +) +{ + return pmic_rpc_set_only(controller, period_pre_div, period_time, 0, + 3, + HSED_SET_PERIOD_PROC); +} +EXPORT_SYMBOL(pmic_hsed_set_period); + +int pmic_hsed_set_hysteresis( + enum hsed_controller controller, + enum hsed_hyst_pre_div hyst_pre_div, + enum hsed_hyst_time hyst_time +) +{ + return pmic_rpc_set_only(controller, hyst_pre_div, hyst_time, 0, + 3, + HSED_SET_HYSTERESIS_PROC); +} +EXPORT_SYMBOL(pmic_hsed_set_hysteresis); + +int pmic_hsed_set_current_threshold( + enum hsed_controller controller, + enum hsed_switch switch_hsed, + uint32_t current_threshold +) +{ + return pmic_rpc_set_only(controller, switch_hsed, current_threshold, 0, + 3, + HSED_SET_CURRENT_THRESHOLD_PROC); +} +EXPORT_SYMBOL(pmic_hsed_set_current_threshold); + +int pmic_hsed_enable( + enum hsed_controller controller, + enum hsed_enable enable_hsed +) +{ + return pmic_rpc_set_only(controller, enable_hsed, 0, 0, + 2, + HSED_ENABLE_PROC); +} +EXPORT_SYMBOL(pmic_hsed_enable); + +int pmic_high_current_led_set_current(enum high_current_led led, + uint16_t milliamps) +{ + return pmic_rpc_set_only(led, milliamps, 0, 0, + 2, + HIGH_CURRENT_LED_SET_CURRENT_PROC); +} +EXPORT_SYMBOL(pmic_high_current_led_set_current); + +int pmic_high_current_led_set_polarity(enum high_current_led led, + enum flash_led_pol polarity) +{ + return pmic_rpc_set_only(led, polarity, 0, 0, + 2, + HIGH_CURRENT_LED_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_high_current_led_set_polarity); + +int pmic_high_current_led_set_mode(enum high_current_led led, + enum flash_led_mode mode) +{ + return pmic_rpc_set_only(led, mode, 0, 0, + 2, + HIGH_CURRENT_LED_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_high_current_led_set_mode); + +int pmic_lp_force_lpm_control(enum switch_cmd cmd, + enum vreg_lpm_id vreg) +{ + return pmic_rpc_set_only(cmd, vreg, 0, 0, + 2, + LP_FORCE_LPM_CONTROL_PROC); +} +EXPORT_SYMBOL(pmic_lp_force_lpm_control); + +int pmic_low_current_led_set_ext_signal(enum low_current_led led, + enum ext_signal sig) +{ + return pmic_rpc_set_only(led, sig, 0, 0, + 2, + LOW_CURRENT_LED_SET_EXT_SIGNAL_PROC); +} +EXPORT_SYMBOL(pmic_low_current_led_set_ext_signal); + +int pmic_low_current_led_set_current(enum low_current_led led, + uint16_t milliamps) +{ + return pmic_rpc_set_only(led, milliamps, 0, 0, + 2, + LOW_CURRENT_LED_SET_CURRENT_PROC); +} +EXPORT_SYMBOL(pmic_low_current_led_set_current); + +/* + * Head phone speaker + */ +int pmic_hp_spkr_mstr_en(enum hp_spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, + HP_SPKR_MSTR_EN_PROC); +} +EXPORT_SYMBOL(pmic_hp_spkr_mstr_en); + +int pmic_hp_spkr_mute_en(enum hp_spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, + HP_SPKR_MUTE_EN_PROC); +} +EXPORT_SYMBOL(pmic_hp_spkr_mute_en); + +int pmic_hp_spkr_prm_in_en(enum hp_spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, + HP_SPKR_PRM_IN_EN_PROC); +} +EXPORT_SYMBOL(pmic_hp_spkr_prm_in_en); + +int pmic_hp_spkr_aux_in_en(enum hp_spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, + HP_SPKR_AUX_IN_EN_PROC); +} +EXPORT_SYMBOL(pmic_hp_spkr_aux_in_en); + +int pmic_hp_spkr_ctrl_prm_gain_input(enum hp_spkr_left_right left_right, + uint prm_gain_ctl) +{ + return pmic_rpc_set_only(left_right, prm_gain_ctl, 0, 0, 2, + HP_SPKR_CTRL_PRM_GAIN_INPUT_PROC); +} +EXPORT_SYMBOL(pmic_hp_spkr_ctrl_prm_gain_input); + +int pmic_hp_spkr_ctrl_aux_gain_input(enum hp_spkr_left_right left_right, + uint aux_gain_ctl) +{ + return pmic_rpc_set_only(left_right, aux_gain_ctl, 0, 0, 2, + HP_SPKR_CTRL_AUX_GAIN_INPUT_PROC); +} +EXPORT_SYMBOL(pmic_hp_spkr_ctrl_aux_gain_input); + +int pmic_xo_core_force_enable(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, XO_CORE_FORCE_ENABLE); +} +EXPORT_SYMBOL(pmic_xo_core_force_enable); + +int pmic_gpio_direction_input(unsigned gpio) +{ + return pmic_rpc_set_only(gpio, 0, 0, 0, 1, + GPIO_SET_GPIO_DIRECTION_INPUT_PROC); +} +EXPORT_SYMBOL(pmic_gpio_direction_input); + +int pmic_gpio_direction_output(unsigned gpio) +{ + return pmic_rpc_set_only(gpio, 0, 0, 0, 1, + GPIO_SET_GPIO_DIRECTION_OUTPUT_PROC); +} +EXPORT_SYMBOL(pmic_gpio_direction_output); + +int pmic_gpio_set_value(unsigned gpio, int value) +{ + return pmic_rpc_set_only(gpio, value, 0, 0, 2, GPIO_SET_PROC); +} +EXPORT_SYMBOL(pmic_gpio_set_value); + +int pmic_gpio_get_value(unsigned gpio) +{ + uint value; + int ret; + + ret = pmic_rpc_set_get(gpio, &value, sizeof(value), GPIO_GET_PROC); + if (ret < 0) + return ret; + return value ? 1 : 0; +} +EXPORT_SYMBOL(pmic_gpio_get_value); + +int pmic_gpio_get_direction(unsigned gpio) +{ + enum pmic_direction_mode dir; + int ret; + + ret = pmic_rpc_set_get(gpio, &dir, sizeof(dir), + GPIO_GET_GPIO_DIRECTION_PROC); + if (ret < 0) + return ret; + return dir; +} +EXPORT_SYMBOL(pmic_gpio_get_direction); + +int pmic_gpio_config(struct pm8xxx_gpio_rpc_cfg *param) +{ + return pmic_rpc_set_struct(0, 0, (uint *)param, sizeof(*param), + GPIO_SET_GPIO_CONFIG_PROC); +} +EXPORT_SYMBOL(pmic_gpio_config); diff --git a/arch/arm/mach-msm/pmic.h b/arch/arm/mach-msm/pmic.h new file mode 100644 index 00000000000..e6ef960d977 --- /dev/null +++ b/arch/arm/mach-msm/pmic.h @@ -0,0 +1,295 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_PMIC_H +#define __ARCH_ARM_MACH_PMIC_H + +#include + +enum spkr_left_right { + LEFT_SPKR, + RIGHT_SPKR, +}; + +enum spkr_gain { + SPKR_GAIN_MINUS16DB, /* -16 db */ + SPKR_GAIN_MINUS12DB, /* -12 db */ + SPKR_GAIN_MINUS08DB, /* -08 db */ + SPKR_GAIN_MINUS04DB, /* -04 db */ + SPKR_GAIN_00DB, /* 00 db */ + SPKR_GAIN_PLUS04DB, /* +04 db */ + SPKR_GAIN_PLUS08DB, /* +08 db */ + SPKR_GAIN_PLUS12DB, /* +12 db */ +}; + +enum spkr_dly { + SPKR_DLY_10MS, /* ~10 ms delay */ + SPKR_DLY_100MS, /* ~100 ms delay */ +}; + +enum spkr_hpf_corner_freq { + SPKR_FREQ_1_39KHZ, /* 1.39 kHz */ + SPKR_FREQ_0_64KHZ, /* 0.64 kHz */ + SPKR_FREQ_0_86KHZ, /* 0.86 kHz */ + SPKR_FREQ_0_51KHZ, /* 0.51 kHz */ + SPKR_FREQ_1_06KHZ, /* 1.06 kHz */ + SPKR_FREQ_0_57KHZ, /* 0.57 kHz */ + SPKR_FREQ_0_73KHZ, /* 0.73 kHz */ + SPKR_FREQ_0_47KHZ, /* 0.47 kHz */ + SPKR_FREQ_1_20KHZ, /* 1.20 kHz */ + SPKR_FREQ_0_60KHZ, /* 0.60 kHz */ + SPKR_FREQ_0_76KHZ, /* 0.76 kHz */ + SPKR_FREQ_0_49KHZ, /* 0.49 kHz */ + SPKR_FREQ_0_95KHZ, /* 0.95 kHz */ + SPKR_FREQ_0_54KHZ, /* 0.54 kHz */ + SPKR_FREQ_0_68KHZ, /* 0.68 kHz */ + SPKR_FREQ_0_45KHZ, /* 0.45 kHz */ +}; + +/* Turn the speaker on or off and enables or disables mute.*/ +enum spkr_cmd { + SPKR_DISABLE, /* Enable Speaker */ + SPKR_ENABLE, /* Disable Speaker */ + SPKR_MUTE_OFF, /* turn speaker mute off, SOUND ON */ + SPKR_MUTE_ON, /* turn speaker mute on, SOUND OFF */ + SPKR_OFF, /* turn speaker OFF (speaker disable and mute on) */ + SPKR_ON, /* turn speaker ON (speaker enable and mute off) */ + SPKR_SET_FREQ_CMD, /* set speaker frequency */ + SPKR_GET_FREQ_CMD, /* get speaker frequency */ + SPKR_SET_GAIN_CMD, /* set speaker gain */ + SPKR_GET_GAIN_CMD, /* get speaker gain */ + SPKR_SET_DELAY_CMD, /* set speaker delay */ + SPKR_GET_DELAY_CMD, /* get speaker delay */ + SPKR_SET_PDM_MODE, + SPKR_SET_PWM_MODE, +}; + +struct spkr_config_mode { + uint32_t is_right_chan_en; + uint32_t is_left_chan_en; + uint32_t is_right_left_chan_added; + uint32_t is_stereo_en; + uint32_t is_usb_with_hpf_20hz; + uint32_t is_mux_bypassed; + uint32_t is_hpf_en; + uint32_t is_sink_curr_from_ref_volt_cir_en; +}; + +enum mic_volt { + MIC_VOLT_2_00V, /* 2.00 V */ + MIC_VOLT_1_93V, /* 1.93 V */ + MIC_VOLT_1_80V, /* 1.80 V */ + MIC_VOLT_1_73V, /* 1.73 V */ +}; + +enum ledtype { + LED_LCD, + LED_KEYPAD, +}; + +enum flash_led_mode { + FLASH_LED_MODE__MANUAL, + FLASH_LED_MODE__DBUS1, + FLASH_LED_MODE__DBUS2, + FLASH_LED_MODE__DBUS3, +}; + +enum flash_led_pol { + FLASH_LED_POL__ACTIVE_HIGH, + FLASH_LED_POL__ACTIVE_LOW, +}; + +enum switch_cmd { + OFF_CMD, + ON_CMD +}; + +enum vreg_lp_id { + PM_VREG_LP_MSMA_ID, + PM_VREG_LP_MSMP_ID, + PM_VREG_LP_MSME1_ID, + PM_VREG_LP_GP3_ID, + PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME2_ID, + PM_VREG_LP_GP4_ID, + PM_VREG_LP_GP1_ID, + PM_VREG_LP_RFTX_ID, + PM_VREG_LP_RFRX1_ID, + PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_WLAN_ID, + PM_VREG_LP_MMC_ID, + PM_VREG_LP_RUIM_ID, + PM_VREG_LP_MSMC0_ID, + PM_VREG_LP_GP2_ID, + PM_VREG_LP_GP5_ID, + PM_VREG_LP_GP6_ID, + PM_VREG_LP_MPLL_ID, + PM_VREG_LP_RFUBM_ID, + PM_VREG_LP_RFA_ID, + PM_VREG_LP_CDC2_ID, + PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_USIM_ID, + PM_VREG_LP_USB2P6_ID, + PM_VREG_LP_TCXO_ID, + PM_VREG_LP_USB3P3_ID, + + PM_VREG_LP_MSME_ID = PM_VREG_LP_MSME1_ID, + /* backward compatible enums only */ + PM_VREG_LP_CAM_ID = PM_VREG_LP_GP1_ID, + PM_VREG_LP_MDDI_ID = PM_VREG_LP_GP2_ID, + PM_VREG_LP_RUIM2_ID = PM_VREG_LP_GP3_ID, + PM_VREG_LP_AUX_ID = PM_VREG_LP_GP4_ID, + PM_VREG_LP_AUX2_ID = PM_VREG_LP_GP5_ID, + PM_VREG_LP_BT_ID = PM_VREG_LP_GP6_ID, + PM_VREG_LP_MSMC_LDO_ID = PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME1_LDO_ID = PM_VREG_LP_MSME1_ID, + PM_VREG_LP_MSME2_LDO_ID = PM_VREG_LP_MSME2_ID, + PM_VREG_LP_RFA1_ID = PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_RFA2_ID = PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_XO_ID = PM_VREG_LP_TCXO_ID +}; + +enum mpp_which { + PM_MPP_1, + PM_MPP_2, + PM_MPP_3, + PM_MPP_4, + PM_MPP_5, + PM_MPP_6, + PM_MPP_7, + PM_MPP_8, + PM_MPP_9, + PM_MPP_10, + PM_MPP_11, + PM_MPP_12, + PM_MPP_13, + PM_MPP_14, + PM_MPP_15, + PM_MPP_16, + PM_MPP_17, + PM_MPP_18, + PM_MPP_19, + PM_MPP_20, + PM_MPP_21, + PM_MPP_22, + + PM_NUM_MPP_HAN = PM_MPP_4 + 1, + PM_NUM_MPP_KIP = PM_MPP_4 + 1, + PM_NUM_MPP_EPIC = PM_MPP_4 + 1, + PM_NUM_MPP_PM7500 = PM_MPP_22 + 1, + PM_NUM_MPP_PM6650 = PM_MPP_12 + 1, + PM_NUM_MPP_PM6658 = PM_MPP_12 + 1, + PM_NUM_MPP_PANORAMIX = PM_MPP_2 + 1, + PM_NUM_MPP_PM6640 = PM_NUM_MPP_PANORAMIX, + PM_NUM_MPP_PM6620 = PM_NUM_MPP_PANORAMIX +}; + +enum mpp_dlogic_level { + PM_MPP__DLOGIC__LVL_MSME, + PM_MPP__DLOGIC__LVL_MSMP, + PM_MPP__DLOGIC__LVL_RUIM, + PM_MPP__DLOGIC__LVL_MMC, + PM_MPP__DLOGIC__LVL_VDD, +}; + +enum mpp_dlogic_in_dbus { + PM_MPP__DLOGIC_IN__DBUS_NONE, + PM_MPP__DLOGIC_IN__DBUS1, + PM_MPP__DLOGIC_IN__DBUS2, + PM_MPP__DLOGIC_IN__DBUS3, +}; + +enum mpp_dlogic_out_ctrl { + PM_MPP__DLOGIC_OUT__CTRL_LOW, + PM_MPP__DLOGIC_OUT__CTRL_HIGH, + PM_MPP__DLOGIC_OUT__CTRL_MPP, + PM_MPP__DLOGIC_OUT__CTRL_NOT_MPP, +}; + +enum mpp_i_sink_level { + PM_MPP__I_SINK__LEVEL_5mA, + PM_MPP__I_SINK__LEVEL_10mA, + PM_MPP__I_SINK__LEVEL_15mA, + PM_MPP__I_SINK__LEVEL_20mA, + PM_MPP__I_SINK__LEVEL_25mA, + PM_MPP__I_SINK__LEVEL_30mA, + PM_MPP__I_SINK__LEVEL_35mA, + PM_MPP__I_SINK__LEVEL_40mA, +}; + +enum mpp_i_sink_switch { + PM_MPP__I_SINK__SWITCH_DIS, + PM_MPP__I_SINK__SWITCH_ENA, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_HIGH, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_LOW, +}; + +enum pm_vib_mot_mode { + PM_VIB_MOT_MODE__MANUAL, + PM_VIB_MOT_MODE__DBUS1, + PM_VIB_MOT_MODE__DBUS2, + PM_VIB_MOT_MODE__DBUS3, +}; + +enum pm_vib_mot_pol { + PM_VIB_MOT_POL__ACTIVE_HIGH, + PM_VIB_MOT_POL__ACTIVE_LOW, +}; + +struct rtc_time { + uint sec; +}; + +enum rtc_alarm { + PM_RTC_ALARM_1, +}; + + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id); +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_out_ctrl out); +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, enum mpp_i_sink_switch onoff); +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_in_dbus dbus); +int pmic_speaker_cmd(const enum spkr_cmd cmd); +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg); +int pmic_spkr_en_right_chan(uint enable); +int pmic_spkr_en_left_chan(uint enable); +int pmic_spkr_en(enum spkr_left_right left_right, uint enabled); +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain); +int pmic_set_speaker_gain(enum spkr_gain gain); +int pmic_set_speaker_delay(enum spkr_dly delay); +int pmic_speaker_1k6_zin_enable(uint enable); +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq); +int pmic_spkr_select_usb_with_hpf_20hz(uint enable); +int pmic_spkr_bypass_mux(uint enable); +int pmic_spkr_en_hpf(uint enable); +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable); +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay); +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled); +int pmic_mic_en(uint enable); +int pmic_mic_set_volt(enum mic_volt vol); +int pmic_set_led_intensity(enum ledtype type, int level); +int pmic_flash_led_set_current(uint16_t milliamps); +int pmic_flash_led_set_mode(enum flash_led_mode mode); +int pmic_flash_led_set_polarity(enum flash_led_pol pol); +int pmic_spkr_add_right_left_chan(uint enable); +int pmic_spkr_en_stereo(uint enable); +int pmic_vib_mot_set_volt(uint vol); +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode); +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol); +int pmic_vid_en(uint enable); +int pmic_vid_load_detect_en(uint enable); + +#endif diff --git a/arch/arm/mach-msm/pmic_debugfs.c b/arch/arm/mach-msm/pmic_debugfs.c new file mode 100644 index 00000000000..c52cf9b14c2 --- /dev/null +++ b/arch/arm/mach-msm/pmic_debugfs.c @@ -0,0 +1,1156 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +#include + + +static int debug_lp_mode_control(char *buf, int size) +{ + enum switch_cmd cmd; + enum vreg_lp_id id; + int cnt; + + + cnt = sscanf(buf, "%u %u", &cmd, &id); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d", __func__, cnt); + return -EINVAL; + } + + if (pmic_lp_mode_control(cmd, id) < 0) + return -EFAULT; + + return size; +} + +static int debug_vreg_set_level(char *buf, int size) +{ + enum vreg_id vreg; + int level; + int cnt; + + cnt = sscanf(buf, "%u %u", &vreg, &level); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d", __func__, cnt); + return -EINVAL; + } + if (pmic_vreg_set_level(vreg, level) < 0) + return -EFAULT; + + return size; +} + +static int debug_vreg_pull_down_switch(char *buf, int size) +{ + enum switch_cmd cmd; + enum vreg_pdown_id id; + int cnt; + + cnt = sscanf(buf, "%u %u", &cmd, &id); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d", __func__, cnt); + return -EINVAL; + } + if (pmic_vreg_pull_down_switch(cmd, id) < 0) + return -EFAULT; + + return size; +} + +static int debug_secure_mpp_control_digital_output(char *buf, int size) +{ + enum mpp_which which; + enum mpp_dlogic_level level; + enum mpp_dlogic_out_ctrl out; + int cnt; + + cnt = sscanf(buf, "%u %u %u", &which, &level, &out); + if (cnt < 3) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + + if (pmic_secure_mpp_control_digital_output(which, level, out) < 0) + return -EFAULT; + + return size; +} + +static int debug_secure_mpp_config_i_sink(char *buf, int size) +{ + enum mpp_which which; + enum mpp_i_sink_level level; + enum mpp_i_sink_switch onoff; + int cnt; + + cnt = sscanf(buf, "%u %u %u", &which, &level, &onoff); + if (cnt < 3) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + + if (pmic_secure_mpp_config_i_sink(which, level, onoff) < 0) + return -EFAULT; + + return size; +} + +static int debug_secure_mpp_config_digital_input(char *buf, int size) +{ + enum mpp_which which; + enum mpp_dlogic_level level; + enum mpp_dlogic_in_dbus dbus; + int cnt; + + cnt = sscanf(buf, "%u %u %u", &which, &level, &dbus); + if (cnt < 3) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_secure_mpp_config_digital_input(which, level, dbus) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_start(char *buf, int size) +{ + uint time; + struct rtc_time *hal; + int cnt; + + cnt = sscanf(buf, "%d", &time); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + hal = (struct rtc_time *)&time; + if (pmic_rtc_start(hal) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_stop(char *buf, int size) +{ + if (pmic_rtc_stop() < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_get_time(char *buf, int size) +{ + uint time; + struct rtc_time *hal; + + hal = (struct rtc_time *)&time; + if (pmic_rtc_get_time(hal) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", time); +} + +static int debug_rtc_alarm_ndx; + +int debug_rtc_enable_alarm(char *buf, int size) +{ + enum rtc_alarm alarm; + struct rtc_time *hal; + uint time; + int cnt; + + + cnt = sscanf(buf, "%u %u", &alarm, &time); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + hal = (struct rtc_time *)&time; + + if (pmic_rtc_enable_alarm(alarm, hal) < 0) + return -EFAULT; + + debug_rtc_alarm_ndx = alarm; + return size; +} + +static int debug_rtc_disable_alarm(char *buf, int size) +{ + + enum rtc_alarm alarm; + int cnt; + + cnt = sscanf(buf, "%u", &alarm); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_rtc_disable_alarm(alarm) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_get_alarm_time(char *buf, int size) +{ + uint time; + struct rtc_time *hal; + + hal = (struct rtc_time *)&time; + if (pmic_rtc_get_alarm_time(debug_rtc_alarm_ndx, hal) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", time); +} +static int debug_rtc_get_alarm_status(char *buf, int size) +{ + int status;; + + if (pmic_rtc_get_alarm_status(&status) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", status); + +} + +static int debug_rtc_set_time_adjust(char *buf, int size) +{ + uint adjust; + int cnt; + + cnt = sscanf(buf, "%d", &adjust); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_rtc_set_time_adjust(adjust) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_get_time_adjust(char *buf, int size) +{ + int adjust;; + + if (pmic_rtc_get_time_adjust(&adjust) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", adjust); +} + +static int debug_set_led_intensity(char *buf, int size) +{ + enum ledtype type; + int level; + int cnt; + + cnt = sscanf(buf, "%u %d", &type, &level); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_set_led_intensity(type, level) < 0) + return -EFAULT; + + return size; +} + +static int debug_flash_led_set_current(char *buf, int size) +{ + int milliamps; + int cnt; + + cnt = sscanf(buf, "%d", &milliamps); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_flash_led_set_current(milliamps) < 0) + return -EFAULT; + + return size; +} +static int debug_flash_led_set_mode(char *buf, int size) +{ + + uint mode; + int cnt; + + cnt = sscanf(buf, "%d", &mode); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_flash_led_set_mode(mode) < 0) + return -EFAULT; + + return size; +} + +static int debug_flash_led_set_polarity(char *buf, int size) +{ + int pol; + int cnt; + + cnt = sscanf(buf, "%d", &pol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_flash_led_set_polarity(pol) < 0) + return -EFAULT; + + return size; +} + +static int debug_speaker_cmd(char *buf, int size) +{ + int cmd; + int cnt; + + cnt = sscanf(buf, "%d", &cmd); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_speaker_cmd(cmd) < 0) + return -EFAULT; + + return size; +} +static int debug_set_speaker_gain(char *buf, int size) +{ + int gain; + int cnt; + + cnt = sscanf(buf, "%d", &gain); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_set_speaker_gain(gain) < 0) + return -EFAULT; + + return size; +} + +static int debug_mic_en(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_mic_en(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_mic_is_en(char *buf, int size) +{ + int enabled; + + if (pmic_mic_is_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_mic_set_volt(char *buf, int size) +{ + int vol; + int cnt; + + cnt = sscanf(buf, "%d", &vol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_mic_set_volt(vol) < 0) + return -EFAULT; + + return size; +} + +static int debug_mic_get_volt(char *buf, int size) +{ + uint vol; + + if (pmic_mic_get_volt(&vol) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", vol); +} + +static int debug_spkr_en_right_chan(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_right_chan(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_right_chan_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_right_chan_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} +static int debug_spkr_en_left_chan(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_left_chan(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_left_chan_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_left_chan_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_set_spkr_configuration(char *buf, int size) +{ + + struct spkr_config_mode cfg; + int cnt; + + cnt = sscanf(buf, "%d %d %d %d %d %d %d %d", + &cfg.is_right_chan_en, + &cfg.is_left_chan_en, + &cfg.is_right_left_chan_added, + &cfg.is_stereo_en, + &cfg.is_usb_with_hpf_20hz, + &cfg.is_mux_bypassed, + &cfg.is_hpf_en, + &cfg.is_sink_curr_from_ref_volt_cir_en); + + if (cnt < 8) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + + if (pmic_set_spkr_configuration(&cfg) < 0) + return -EFAULT; + + return size; +} + +static int debug_get_spkr_configuration(char *buf, int size) +{ + struct spkr_config_mode cfg; + + if (pmic_get_spkr_configuration(&cfg) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d %d %d %d %d %d %d %d\n", + cfg.is_right_chan_en, + cfg.is_left_chan_en, + cfg.is_right_left_chan_added, + cfg.is_stereo_en, + cfg.is_usb_with_hpf_20hz, + cfg.is_mux_bypassed, + cfg.is_hpf_en, + cfg.is_sink_curr_from_ref_volt_cir_en); + +} + +static int debug_set_speaker_delay(char *buf, int size) +{ + int delay; + int cnt; + + cnt = sscanf(buf, "%d", &delay); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_set_speaker_delay(delay) < 0) + return -EFAULT; + + return size; +} + +static int debug_speaker_1k6_zin_enable(char *buf, int size) +{ + uint enable; + int cnt; + + cnt = sscanf(buf, "%u", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_speaker_1k6_zin_enable(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_set_mux_hpf_corner_freq(char *buf, int size) +{ + int freq; + int cnt; + + cnt = sscanf(buf, "%d", &freq); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_set_mux_hpf_corner_freq(freq) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_get_mux_hpf_corner_freq(char *buf, int size) +{ + uint freq; + + if (pmic_spkr_get_mux_hpf_corner_freq(&freq) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", freq); +} + +static int debug_spkr_add_right_left_chan(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_add_right_left_chan(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_right_left_chan_added(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_right_left_chan_added(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_en_stereo(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_stereo(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_stereo_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_stereo_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_select_usb_with_hpf_20hz(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_select_usb_with_hpf_20hz(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_usb_with_hpf_20hz(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_usb_with_hpf_20hz(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_bypass_mux(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_bypass_mux(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_mux_bypassed(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_mux_bypassed(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_en_hpf(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_hpf(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_hpf_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_hpf_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_en_sink_curr_from_ref_volt_cir(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_sink_curr_from_ref_volt_cir(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_sink_curr_from_ref_volt_cir_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_sink_curr_from_ref_volt_cir_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_vib_mot_set_volt(char *buf, int size) +{ + int vol; + int cnt; + + cnt = sscanf(buf, "%d", &vol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vib_mot_set_volt(vol) < 0) + return -EFAULT; + + return size; +} +static int debug_vib_mot_set_mode(char *buf, int size) +{ + int mode; + int cnt; + + cnt = sscanf(buf, "%d", &mode); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vib_mot_set_mode(mode) < 0) + return -EFAULT; + + return size; +} + +static int debug_vib_mot_set_polarity(char *buf, int size) +{ + int pol; + int cnt; + + cnt = sscanf(buf, "%d", &pol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vib_mot_set_polarity(pol) < 0) + return -EFAULT; + + return size; +} +static int debug_vid_en(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vid_en(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_vid_is_en(char *buf, int size) +{ + int enabled; + + if (pmic_vid_is_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_vid_load_detect_en(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vid_load_detect_en(enable) < 0) + return -EFAULT; + + return size; +} + +/************************************************** + * speaker indexed by left_right +**************************************************/ +static enum spkr_left_right debug_spkr_left_right = LEFT_SPKR; + +static int debug_spkr_en(char *buf, int size) +{ + int left_right; + int enable; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &enable); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en(left_right, enable) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_is_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_en(debug_spkr_left_right, &enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_set_gain(char *buf, int size) +{ + int left_right; + int enable; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &enable); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_set_gain(left_right, enable) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_get_gain(char *buf, int size) +{ + uint gain; + + if (pmic_spkr_get_gain(debug_spkr_left_right, &gain) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", gain); +} +static int debug_spkr_set_delay(char *buf, int size) +{ + int left_right; + int delay; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &delay); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_set_delay(left_right, delay) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_get_delay(char *buf, int size) +{ + uint delay; + + if (pmic_spkr_get_delay(debug_spkr_left_right, &delay) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", delay); +} + +static int debug_spkr_en_mute(char *buf, int size) +{ + int left_right; + int enable; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &enable); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_mute(left_right, enable) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_is_mute_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_mute_en(debug_spkr_left_right, &enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +/******************************************************************* + * debug function table +*******************************************************************/ + +struct pmic_debug_desc { + int (*get) (char *, int); + int (*set) (char *, int); +}; + +struct pmic_debug_desc pmic_debug[] = { + {NULL, NULL}, /*LIB_NULL_PROC */ + {NULL, NULL}, /* LIB_RPC_GLUE_CODE_INFO_REMOTE_PROC */ + {NULL, debug_lp_mode_control}, /* LP_MODE_CONTROL_PROC */ + {NULL, debug_vreg_set_level}, /*VREG_SET_LEVEL_PROC */ + {NULL, debug_vreg_pull_down_switch}, /*VREG_PULL_DOWN_SWITCH_PROC */ + {NULL, debug_secure_mpp_control_digital_output}, + /* SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC */ + /*SECURE_MPP_CONFIG_I_SINK_PROC */ + {NULL, debug_secure_mpp_config_i_sink}, + {NULL, debug_rtc_start}, /*RTC_START_PROC */ + {NULL, debug_rtc_stop}, /* RTC_STOP_PROC */ + {debug_rtc_get_time, NULL}, /* RTC_GET_TIME_PROC */ + {NULL, debug_rtc_enable_alarm}, /* RTC_ENABLE_ALARM_PROC */ + {NULL , debug_rtc_disable_alarm}, /*RTC_DISABLE_ALARM_PROC */ + {debug_rtc_get_alarm_time, NULL}, /* RTC_GET_ALARM_TIME_PROC */ + {debug_rtc_get_alarm_status, NULL}, /* RTC_GET_ALARM_STATUS_PROC */ + {NULL, debug_rtc_set_time_adjust}, /* RTC_SET_TIME_ADJUST_PROC */ + {debug_rtc_get_time_adjust, NULL}, /* RTC_GET_TIME_ADJUST_PROC */ + {NULL, debug_set_led_intensity}, /* SET_LED_INTENSITY_PROC */ + {NULL, debug_flash_led_set_current}, /* FLASH_LED_SET_CURRENT_PROC */ + {NULL, debug_flash_led_set_mode}, /* FLASH_LED_SET_MODE_PROC */ + {NULL, debug_flash_led_set_polarity}, /* FLASH_LED_SET_POLARITY_PROC */ + {NULL, debug_speaker_cmd}, /* SPEAKER_CMD_PROC */ + {NULL, debug_set_speaker_gain}, /* SET_SPEAKER_GAIN_PROC */ + {NULL, debug_vib_mot_set_volt}, /* VIB_MOT_SET_VOLT_PROC */ + {NULL, debug_vib_mot_set_mode}, /* VIB_MOT_SET_MODE_PROC */ + {NULL, debug_vib_mot_set_polarity}, /* VIB_MOT_SET_POLARITY_PROC */ + {NULL, debug_vid_en}, /* VID_EN_PROC */ + {debug_vid_is_en, NULL}, /* VID_IS_EN_PROC */ + {NULL, debug_vid_load_detect_en}, /* VID_LOAD_DETECT_EN_PROC */ + {NULL, debug_mic_en}, /* MIC_EN_PROC */ + {debug_mic_is_en, NULL}, /* MIC_IS_EN_PROC */ + {NULL, debug_mic_set_volt}, /* MIC_SET_VOLT_PROC */ + {debug_mic_get_volt, NULL}, /* MIC_GET_VOLT_PROC */ + {NULL, debug_spkr_en_right_chan}, /* SPKR_EN_RIGHT_CHAN_PROC */ + {debug_spkr_is_right_chan_en, NULL}, /* SPKR_IS_RIGHT_CHAN_EN_PROC */ + {NULL, debug_spkr_en_left_chan}, /* SPKR_EN_LEFT_CHAN_PROC */ + {debug_spkr_is_left_chan_en, NULL}, /* SPKR_IS_LEFT_CHAN_EN_PROC */ + {NULL, debug_set_spkr_configuration}, /* SET_SPKR_CONFIGURATION_PROC */ + {debug_get_spkr_configuration, NULL}, /* GET_SPKR_CONFIGURATION_PROC */ + {debug_spkr_get_gain, NULL}, /* SPKR_GET_GAIN_PROC */ + {debug_spkr_is_en, NULL}, /* SPKR_IS_EN_PROC */ + {NULL, debug_spkr_en_mute}, /* SPKR_EN_MUTE_PROC */ + {debug_spkr_is_mute_en, NULL}, /* SPKR_IS_MUTE_EN_PROC */ + {NULL, debug_spkr_set_delay}, /* SPKR_SET_DELAY_PROC */ + {debug_spkr_get_delay, NULL}, /* SPKR_GET_DELAY_PROC */ + /* SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC */ + {NULL, debug_secure_mpp_config_digital_input}, + {NULL, debug_set_speaker_delay}, /* SET_SPEAKER_DELAY_PROC */ + {NULL, debug_speaker_1k6_zin_enable}, /* SPEAKER_1K6_ZIN_ENABLE_PROC */ + /* SPKR_SET_MUX_HPF_CORNER_FREQ_PROC */ + {NULL, debug_spkr_set_mux_hpf_corner_freq}, + /* SPKR_GET_MUX_HPF_CORNER_FREQ_PROC */ + {debug_spkr_get_mux_hpf_corner_freq, NULL}, + /* SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC */ + {debug_spkr_is_right_left_chan_added, NULL}, + {NULL, debug_spkr_en_stereo}, /* SPKR_EN_STEREO_PROC */ + {debug_spkr_is_stereo_en, NULL}, /* SPKR_IS_STEREO_EN_PROC */ + /* SPKR_SELECT_USB_WITH_HPF_20HZ_PROC */ + {NULL, debug_spkr_select_usb_with_hpf_20hz}, + /* SPKR_IS_USB_WITH_HPF_20HZ_PROC */ + {debug_spkr_is_usb_with_hpf_20hz, NULL}, + {NULL, debug_spkr_bypass_mux}, /* SPKR_BYPASS_MUX_PROC */ + {debug_spkr_is_mux_bypassed, NULL}, /* SPKR_IS_MUX_BYPASSED_PROC */ + {NULL, debug_spkr_en_hpf}, /* SPKR_EN_HPF_PROC */ + { debug_spkr_is_hpf_en, NULL}, /* SPKR_IS_HPF_EN_PROC */ + /* SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC */ + {NULL, debug_spkr_en_sink_curr_from_ref_volt_cir}, + /* SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC */ + {debug_spkr_is_sink_curr_from_ref_volt_cir_en, NULL}, + /* SPKR_ADD_RIGHT_LEFT_CHAN_PROC */ + {NULL, debug_spkr_add_right_left_chan}, + {NULL, debug_spkr_set_gain}, /* SPKR_SET_GAIN_PROC */ + {NULL , debug_spkr_en}, /* SPKR_EN_PROC */ +}; + +/***********************************************************************/ + +#define PROC_END (sizeof(pmic_debug)/sizeof(struct pmic_debug_desc)) + + +#define PMIC_DEBUG_BUF 512 + +static int debug_proc; /* PROC's index */ + +static char debug_buf[PMIC_DEBUG_BUF]; + +static int proc_index_set(void *data, u64 val) +{ + int ndx; + + ndx = (int)val; + + if (ndx >= 0 && ndx <= PROC_END) + debug_proc = ndx; + + return 0; +} + +static int proc_index_get(void *data, u64 *val) +{ + *val = (u64)debug_proc; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE( + proc_index_fops, + proc_index_get, + proc_index_set, + "%llu\n"); + + +static int pmic_debugfs_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int pmic_debugfs_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t pmic_debugfs_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + struct pmic_debug_desc *pd; + int len = 0; + + printk(KERN_INFO "%s: proc=%d count=%d *ppos=%d\n", + __func__, debug_proc, count, (uint)*ppos); + + if (count > sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + + debug_buf[count] = 0; /* end of string */ + + pd = &pmic_debug[debug_proc]; + + if (pd->set) { + len = pd->set(debug_buf, count); + printk(KERN_INFO "%s: len=%d\n", __func__, len); + return len; + } + + return 0; +} + +static ssize_t pmic_debugfs_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct pmic_debug_desc *pd; + int len = 0; + + printk(KERN_INFO "%s: proc=%d count=%d *ppos=%d\n", + __func__, debug_proc, count, (uint)*ppos); + + pd = &pmic_debug[debug_proc]; + + if (*ppos) + return 0; /* the end */ + + if (pd->get) { + len = pd->get(debug_buf, sizeof(debug_buf)); + if (len > 0) { + if (len > count) + len = count; + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + } + } + + printk(KERN_INFO "%s: len=%d\n", __func__, len); + + if (len < 0) + return 0; + + *ppos += len; /* increase offset */ + + return len; +} + +static const struct file_operations pmic_debugfs_fops = { + .open = pmic_debugfs_open, + .release = pmic_debugfs_release, + .read = pmic_debugfs_read, + .write = pmic_debugfs_write, +}; + +static int __init pmic_debugfs_init(void) +{ + struct dentry *dent = debugfs_create_dir("pmic", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return -1; + } + + if (debugfs_create_file("index", 0644, dent, 0, &proc_index_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -1; + } + + if (debugfs_create_file("debug", 0644, dent, 0, &pmic_debugfs_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } + + debug_proc = 0; + debug_rtc_alarm_ndx = 0; + + return 0; +} + +late_initcall(pmic_debugfs_init); diff --git a/arch/arm/mach-msm/pmu.c b/arch/arm/mach-msm/pmu.c new file mode 100644 index 00000000000..1f82468171c --- /dev/null +++ b/arch/arm/mach-msm/pmu.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +static struct resource cpu_pmu_resource[] = { + { + .start = INT_ARMQC_PERFMON, + .end = INT_ARMQC_PERFMON, + .flags = IORESOURCE_IRQ, + }, +}; + +#ifdef CONFIG_CPU_HAS_L2_PMU +static struct resource l2_pmu_resource[] = { + { + .start = SC_SICL2PERFMONIRPTREQ, + .end = SC_SICL2PERFMONIRPTREQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device l2_pmu_device = { + .name = "l2-arm-pmu", + .id = ARM_PMU_DEVICE_L2, + .resource = l2_pmu_resource, + .num_resources = ARRAY_SIZE(l2_pmu_resource), +}; + +#endif + +static struct platform_device cpu_pmu_device = { + .name = "cpu-arm-pmu", + .id = ARM_PMU_DEVICE_CPU, + .resource = cpu_pmu_resource, + .num_resources = ARRAY_SIZE(cpu_pmu_resource), +}; + +static struct platform_device *pmu_devices[] = { + &cpu_pmu_device, +#ifdef CONFIG_CPU_HAS_L2_PMU + &l2_pmu_device, +#endif +}; + +static int __init msm_pmu_init(void) +{ + return platform_add_devices(pmu_devices, ARRAY_SIZE(pmu_devices)); +} + +arch_initcall(msm_pmu_init); diff --git a/arch/arm/mach-msm/proc_comm.c b/arch/arm/mach-msm/proc_comm.c index 9980dc736e7..421e7defe82 100644 --- a/arch/arm/mach-msm/proc_comm.c +++ b/arch/arm/mach-msm/proc_comm.c @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/proc_comm.c * * Copyright (C) 2007-2008 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -18,23 +19,24 @@ #include #include #include +#include #include #include +#include -#include "proc_comm.h" - -static inline void msm_a2m_int(uint32_t irq) -{ -#if defined(CONFIG_ARCH_MSM7X30) - writel(1 << irq, MSM_GCC_BASE + 0x8); -#else - writel(1, MSM_CSR_BASE + 0x400 + (irq * 4)); -#endif -} +#include "smd_private.h" static inline void notify_other_proc_comm(void) { - msm_a2m_int(6); + /* Make sure the write completes before interrupt */ + wmb(); +#if defined(CONFIG_ARCH_MSM7X30) + __raw_writel(1 << 6, MSM_APCS_GCC_BASE + 0x8); +#elif defined(CONFIG_ARCH_MSM8X60) + __raw_writel(1 << 5, MSM_GCC_BASE + 0x8); +#else + __raw_writel(1, MSM_CSR_BASE + 0x400 + (6) * 4); +#endif } #define APP_COMMAND 0x00 @@ -48,83 +50,109 @@ static inline void notify_other_proc_comm(void) #define MDM_DATA2 0x1C static DEFINE_SPINLOCK(proc_comm_lock); - -/* The higher level SMD support will install this to - * provide a way to check for and handle modem restart. - */ -int (*msm_check_for_modem_crash)(void); +static int msm_proc_comm_disable; /* Poll for a state change, checking for possible * modem crashes along the way (so we don't wait - * forever while the ARM9 is blowing up). + * forever while the ARM9 is blowing up. * * Return an error in the event of a modem crash and * restart so the msm_proc_comm() routine can restart * the operation from the beginning. */ -static int proc_comm_wait_for(void __iomem *addr, unsigned value) +static int proc_comm_wait_for(unsigned addr, unsigned value) { - for (;;) { - if (readl(addr) == value) + while (1) { + /* Barrier here prevents excessive spinning */ + mb(); + if (readl_relaxed(addr) == value) return 0; - if (msm_check_for_modem_crash) - if (msm_check_for_modem_crash()) - return -EAGAIN; + if (smsm_check_for_modem_crash()) + return -EAGAIN; + + udelay(5); } } +void msm_proc_comm_reset_modem_now(void) +{ + unsigned base = (unsigned)MSM_SHARED_RAM_BASE; + unsigned long flags; + + spin_lock_irqsave(&proc_comm_lock, flags); + +again: + if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) + goto again; + + writel_relaxed(PCOM_RESET_MODEM, base + APP_COMMAND); + writel_relaxed(0, base + APP_DATA1); + writel_relaxed(0, base + APP_DATA2); + + spin_unlock_irqrestore(&proc_comm_lock, flags); + + /* Make sure the writes complete before notifying the other side */ + wmb(); + notify_other_proc_comm(); + + return; +} +EXPORT_SYMBOL(msm_proc_comm_reset_modem_now); + int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2) { - void __iomem *base = MSM_SHARED_RAM_BASE; + unsigned base = (unsigned)MSM_SHARED_RAM_BASE; unsigned long flags; int ret; spin_lock_irqsave(&proc_comm_lock, flags); - for (;;) { - if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) - continue; - - writel(cmd, base + APP_COMMAND); - writel(data1 ? *data1 : 0, base + APP_DATA1); - writel(data2 ? *data2 : 0, base + APP_DATA2); - - notify_other_proc_comm(); - - if (proc_comm_wait_for(base + APP_COMMAND, PCOM_CMD_DONE)) - continue; - - if (readl(base + APP_STATUS) != PCOM_CMD_FAIL) { - if (data1) - *data1 = readl(base + APP_DATA1); - if (data2) - *data2 = readl(base + APP_DATA2); - ret = 0; - } else { - ret = -EIO; - } - break; + if (msm_proc_comm_disable) { + ret = -EIO; + goto end; } - writel(PCOM_CMD_IDLE, base + APP_COMMAND); +again: + if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) + goto again; + + writel_relaxed(cmd, base + APP_COMMAND); + writel_relaxed(data1 ? *data1 : 0, base + APP_DATA1); + writel_relaxed(data2 ? *data2 : 0, base + APP_DATA2); + + /* Make sure the writes complete before notifying the other side */ + wmb(); + notify_other_proc_comm(); + + if (proc_comm_wait_for(base + APP_COMMAND, PCOM_CMD_DONE)) + goto again; + + if (readl_relaxed(base + APP_STATUS) == PCOM_CMD_SUCCESS) { + if (data1) + *data1 = readl_relaxed(base + APP_DATA1); + if (data2) + *data2 = readl_relaxed(base + APP_DATA2); + ret = 0; + } else { + ret = -EIO; + } + + writel_relaxed(PCOM_CMD_IDLE, base + APP_COMMAND); + + switch (cmd) { + case PCOM_RESET_CHIP: + case PCOM_RESET_CHIP_IMM: + case PCOM_RESET_APPS: + msm_proc_comm_disable = 1; + printk(KERN_ERR "msm: proc_comm: proc comm disabled\n"); + break; + } +end: + /* Make sure the writes complete before returning */ + wmb(); spin_unlock_irqrestore(&proc_comm_lock, flags); - return ret; } - -/* - * We need to wait for the ARM9 to at least partially boot - * up before we can continue. Since the ARM9 does resource - * allocation, if we dont' wait we could end up crashing or in - * and unknown state. This function should be called early to - * wait on the ARM9. - */ -void __devinit proc_comm_boot_wait(void) -{ - void __iomem *base = MSM_SHARED_RAM_BASE; - - proc_comm_wait_for(base + MDM_STATUS, PCOM_READY); - -} +EXPORT_SYMBOL(msm_proc_comm); diff --git a/arch/arm/mach-msm/proc_comm_test.c b/arch/arm/mach-msm/proc_comm_test.c new file mode 100644 index 00000000000..e4eca119e9a --- /dev/null +++ b/arch/arm/mach-msm/proc_comm_test.c @@ -0,0 +1,125 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * PROC COMM TEST Driver source file + */ + +#include +#include +#include +#include +#include + +static struct dentry *dent; +static int proc_comm_test_res; + +static int proc_comm_reverse_test(void) +{ + uint32_t data1, data2; + int rc; + + data1 = 10; + data2 = 20; + + rc = msm_proc_comm(PCOM_OEM_TEST_CMD, &data1, &data2); + if (rc) + return rc; + + if ((data1 != 20) || (data2 != 10)) + return -1; + + return 0; +} + +static ssize_t debug_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char _buf[16]; + + snprintf(_buf, sizeof(_buf), "%i\n", proc_comm_test_res); + + return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf)); +} + +static ssize_t debug_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + + unsigned char cmd[64]; + int len; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "reverse_test", 64)) + proc_comm_test_res = proc_comm_reverse_test(); + else + proc_comm_test_res = -EINVAL; + + if (proc_comm_test_res) + pr_err("proc comm test fail %d\n", + proc_comm_test_res); + else + pr_info("proc comm test passed\n"); + + return count; +} + +static int debug_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static int debug_open(struct inode *ip, struct file *fp) +{ + return 0; +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = debug_open, + .release = debug_release, + .read = debug_read, + .write = debug_write, +}; + +static void __exit proc_comm_test_mod_exit(void) +{ + debugfs_remove(dent); +} + +static int __init proc_comm_test_mod_init(void) +{ + dent = debugfs_create_file("proc_comm", 0444, 0, NULL, &debug_ops); + proc_comm_test_res = -1; + return 0; +} + +module_init(proc_comm_test_mod_init); +module_exit(proc_comm_test_mod_exit); + +MODULE_DESCRIPTION("PROC COMM TEST Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/proccomm-regulator.c b/arch/arm/mach-msm/proccomm-regulator.c new file mode 100644 index 00000000000..21a4f846fe3 --- /dev/null +++ b/arch/arm/mach-msm/proccomm-regulator.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proccomm-regulator.h" + +#define MV_TO_UV(mv) ((mv)*1000) +#define UV_TO_MV(uv) (((uv)+999)/1000) + +/* + * Wrappers for the msm_proc_comm() calls. + * Does basic impedance matching between what the proccomm interface + * expects and how the driver sees the world. + */ + +/* Converts a proccomm error to an errno value. */ +static int _pcom_err_to_linux_errno(unsigned error) +{ + if (!error) /* 0 == no error */ + return 0; + else if (error & 0x1F) /* bits 0..4 => parameter 1..5 out of range */ + return -EDOM; + else if (error & 0x100) /* bit 8 => feature not supported */ + return -ENOSYS; + else /* anything else non-zero: unknown error */ + return -EINVAL; +} + +/* vreg_switch: (vreg ID, on/off) => (return code, ) */ +static int _vreg_switch(int vreg_id, bool enable) +{ + unsigned _id = (unsigned)vreg_id; + unsigned _enable = !!enable; + + return msm_proc_comm(PCOM_VREG_SWITCH, &_id, &_enable); +} + +/* vreg_set_level: (vreg ID, mV) => (return code, ) */ +static int _vreg_set_level(int vreg_id, int level_mV) +{ + unsigned _id = (unsigned)vreg_id; + unsigned _level = (unsigned)level_mV; + int rc; + + rc = msm_proc_comm(PCOM_VREG_SET_LEVEL, &_id, &_level); + + if (rc) + return rc; + + return _pcom_err_to_linux_errno(_id); +} + +/* vreg_pull_down: (pull down, vreg ID) => (, ) */ +/* Returns error code from msm_proc_comm. */ +static int _vreg_pull_down(int vreg_id, bool pull_down) +{ + unsigned _id = (unsigned)vreg_id; + unsigned _enable = !!pull_down; + + return msm_proc_comm(PCOM_VREG_PULLDOWN, &_enable, &_id); +} + +struct proccomm_regulator_drvdata { + struct regulator_desc rdesc; + int rise_time; + int last_voltage; + bool enabled; + bool negative; +}; + +static int proccomm_vreg_enable(struct regulator_dev *rdev) +{ + struct proccomm_regulator_drvdata *ddata; + int rc; + + ddata = rdev_get_drvdata(rdev); + rc = _vreg_switch(rdev_get_id(rdev), VREG_SWITCH_ENABLE); + + if (rc) { + dev_err(rdev_get_dev(rdev), + "could not enable regulator %d (%s): %d\n", + rdev_get_id(rdev), ddata->rdesc.name, rc); + } else { + dev_dbg(rdev_get_dev(rdev), + "enabled regulator %d (%s)\n", + rdev_get_id(rdev), ddata->rdesc.name); + ddata->enabled = 1; + } + + return rc; +} + +static int proccomm_vreg_disable(struct regulator_dev *rdev) +{ + struct proccomm_regulator_drvdata *ddata; + int rc; + + ddata = rdev_get_drvdata(rdev); + rc = _vreg_switch(rdev_get_id(rdev), VREG_SWITCH_DISABLE); + + if (rc) { + dev_err(rdev_get_dev(rdev), + "could not disable regulator %d (%s): %d\n", + rdev_get_id(rdev), ddata->rdesc.name, rc); + } else { + dev_dbg(rdev_get_dev(rdev), + "disabled regulator %d (%s)\n", + rdev_get_id(rdev), ddata->rdesc.name); + ddata->enabled = 0; + } + + return rc; +} + +static int proccomm_vreg_is_enabled(struct regulator_dev *rdev) +{ + struct proccomm_regulator_drvdata *ddata = rdev_get_drvdata(rdev); + + return ddata->enabled; +} + +static int proccomm_vreg_rise_time(struct regulator_dev *rdev) +{ + struct proccomm_regulator_drvdata *ddata = rdev_get_drvdata(rdev); + + return ddata->rise_time; +} + +static int proccomm_vreg_get_voltage(struct regulator_dev *rdev) +{ + + struct proccomm_regulator_drvdata *ddata = rdev_get_drvdata(rdev); + + return MV_TO_UV(ddata->last_voltage); +} + +static int proccomm_vreg_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *sel) +{ + struct proccomm_regulator_drvdata *ddata = rdev_get_drvdata(rdev); + int level_mV = UV_TO_MV(min_uV); + int rc; + + rc = _vreg_set_level(rdev_get_id(rdev), + ddata->negative ? -level_mV : level_mV); + + if (rc) { + dev_err(rdev_get_dev(rdev), + "could not set voltage for regulator %d (%s) " + "to %d mV: %d\n", + rdev_get_id(rdev), ddata->rdesc.name, level_mV, rc); + } else { + dev_dbg(rdev_get_dev(rdev), + "voltage for regulator %d (%s) set to %d mV\n", + rdev_get_id(rdev), ddata->rdesc.name, level_mV); + ddata->last_voltage = level_mV; + } + + return rc; +} + +static struct regulator_ops proccomm_regulator_ops = { + .enable = proccomm_vreg_enable, + .disable = proccomm_vreg_disable, + .is_enabled = proccomm_vreg_is_enabled, + .get_voltage = proccomm_vreg_get_voltage, + .set_voltage = proccomm_vreg_set_voltage, + .enable_time = proccomm_vreg_rise_time, +}; + +/* + * Create and register a struct regulator_dev based on the information in + * a struct proccomm_regulator_info. + * Fills in the rdev field in struct proccomm_regulator_info. + */ +static struct regulator_dev *__devinit create_proccomm_rdev( + struct proccomm_regulator_info *info, struct device *parent) +{ + const char *name; + struct proccomm_regulator_drvdata *d; + struct regulator_dev *rdev; + int rc = 0; + + if (info->id < 0) { + dev_err(parent, "invalid regulator id %d\n", info->id); + rc = -EINVAL; + goto out; + } + + name = info->init_data.constraints.name; + + if (!name) { + dev_err(parent, + "could not register regulator with id %d: " + "no name specified\n", info->id); + rc = -EINVAL; + goto out; + } + + if (info->pulldown > 0) { + rc = _vreg_pull_down(info->id, info->pulldown); + if (rc) { + dev_err(parent, + "probing for regulator %d (%s) failed\n", + info->id, name); + goto out; + } + } + + d = kzalloc(sizeof(*d), GFP_KERNEL); + + if (!d) { + dev_err(parent, + "could not allocate struct proccomm_regulator_drvdata " + "for regulator %d (%s)\n", info->id, name); + rc = -ENOMEM; + goto out; + } + + d->rdesc.name = name; + d->rdesc.id = info->id; + d->rdesc.ops = &proccomm_regulator_ops; + d->rdesc.type = REGULATOR_VOLTAGE; + d->rdesc.owner = THIS_MODULE; + d->rise_time = info->rise_time; + d->enabled = 0; + d->negative = info->negative; + d->rdesc.n_voltages = info->n_voltages; + + rdev = regulator_register(&d->rdesc, parent, &info->init_data, d, NULL); + + if (IS_ERR(rdev)) { + rc = PTR_ERR(rdev); + dev_err(parent, "error registering regulator %d (%s): %d\n", + info->id, name, rc); + goto clean; + } + + dev_dbg(parent, "registered regulator %d (%s)\n", info->id, name); + + return rdev; + +clean: + kfree(d); +out: + return ERR_PTR(rc); +} + +/* + * Unregister and destroy a struct regulator_dev created by + * create_proccomm_rdev. + */ +static void destroy_proccomm_rdev(struct regulator_dev *rdev) +{ + struct proccomm_regulator_drvdata *d; + + if (!rdev) + return; + + d = rdev_get_drvdata(rdev); + + regulator_unregister(rdev); + + dev_dbg(rdev_get_dev(rdev)->parent, + "unregistered regulator %d (%s)\n", + d->rdesc.id, d->rdesc.name); + + kfree(d); +} + + +static int __devinit proccomm_vreg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct proccomm_regulator_platform_data *pdata = dev->platform_data; + struct regulator_dev **rdevs; + int rc = 0; + size_t i = 0; + + if (!pdata) { + dev_err(dev, "invalid platform data\n"); + rc = -EINVAL; + goto check_fail; + } + + if (pdata->nregs == 0) { + dev_err(dev, "registering an empty regulator list; " + "this is probably not what you want\n"); + rc = -EINVAL; + goto check_fail; + } + + rdevs = kcalloc(pdata->nregs, sizeof(*rdevs), GFP_KERNEL); + + if (!rdevs) { + dev_err(dev, "could not allocate storage for " + "struct regulator_dev array\n"); + rc = -ENOMEM; + goto check_fail; + } + + platform_set_drvdata(pdev, rdevs); + + dev_dbg(dev, "registering %d proccomm regulators\n", pdata->nregs); + + for (i = 0; i < pdata->nregs; i++) { + rdevs[i] = create_proccomm_rdev(&pdata->regs[i], dev); + if (IS_ERR(rdevs[i])) { + rc = PTR_ERR(rdevs[i]); + goto backout; + } + } + + dev_dbg(dev, "%d proccomm regulators registered\n", pdata->nregs); + + return rc; + +backout: + while (--i >= 0) + destroy_proccomm_rdev(rdevs[i]); + + kfree(rdevs); + +check_fail: + return rc; +} + +static int __devexit proccomm_vreg_remove(struct platform_device *pdev) +{ + struct proccomm_regulator_platform_data *pdata; + struct regulator_dev **rdevs; + size_t i; + + pdata = pdev->dev.platform_data; + rdevs = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->nregs; i++) + destroy_proccomm_rdev(rdevs[i]); + + kfree(rdevs); + + return 0; +} + +static struct platform_driver proccomm_vreg_driver = { + .probe = proccomm_vreg_probe, + .remove = __devexit_p(proccomm_vreg_remove), + .driver = { + .name = PROCCOMM_REGULATOR_DEV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init proccomm_vreg_init(void) +{ + return platform_driver_register(&proccomm_vreg_driver); +} +postcore_initcall(proccomm_vreg_init); + +static void __exit proccomm_vreg_exit(void) +{ + platform_driver_unregister(&proccomm_vreg_driver); +} +module_exit(proccomm_vreg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ProcComm regulator driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:" PROCCOMM_REGULATOR_DEV_NAME); diff --git a/arch/arm/mach-msm/proccomm-regulator.h b/arch/arm/mach-msm/proccomm-regulator.h new file mode 100644 index 00000000000..46d3b13d4c2 --- /dev/null +++ b/arch/arm/mach-msm/proccomm-regulator.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_PROCCOMM_REGULATOR_H__ +#define __ARCH_ARM_MACH_MSM_PROCCOMM_REGULATOR_H__ + +#include + +#define PROCCOMM_REGULATOR_DEV_NAME "proccomm-regulator" + +/** + * struct proccomm_regulator_info - A description of one proccomm regulator + * @init_data: Initialization data for the regulator. + * Must contain: + * - A list of struct regulator_consumer_supply indicating + * supply names for the regulator + * - A filled out struct regulation_constraints containing: + * - The name of the regulator + * - The minimum and maximum voltages supported + * - The supported modes (REGULATOR_MODE_NORMAL) + * - The supported operations, currently limited to: + * REGULATOR_CHANGE_STATUS + * REGULATOR_CHANGE_VOLTAGE + * - The input voltage, if the regulator is powered by another + * - Properly set always_on, boot_on, and apply_uV flags + * - The name of the supply regulator, if applicable + * @id: The proccomm ID of this regulator. + * @rise_time: The time that the regulator takes to initialize, + * in microseconds. Set to 0 to disable rise-time checking. + * @pulldown: Whether the regulator should be pulled down when off. + * 1 to pull down the regulator. + * 0 to leave the regulator floating. + * -1 to indicate no preference. + */ +struct proccomm_regulator_info { + struct regulator_init_data init_data; + int id; + int rise_time; + int pulldown; + int negative; + int n_voltages; +}; + +/** + * struct proccomm_regulator_platform_data - proccomm driver platform data. + * + * Contains a description of a set of proccomm-controlled regulators. + * Pass this in the platform_data field when instantiating the driver. + * + * @regs: An array of struct proccomm_regulator_info describing + * the regulators to register. + * @nregs: The number of regulators to register. + */ +struct proccomm_regulator_platform_data { + struct proccomm_regulator_info *regs; + size_t nregs; +}; + +#if defined(CONFIG_MSM_VREG_SWITCH_INVERTED) +#define VREG_SWITCH_ENABLE 0 +#define VREG_SWITCH_DISABLE 1 +#else +#define VREG_SWITCH_ENABLE 1 +#define VREG_SWITCH_DISABLE 0 +#endif + +#endif diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile new file mode 100644 index 00000000000..2ce0031afd9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -0,0 +1,20 @@ +obj-y += adsp.o adsp_driver.o adsp_info.o adsp_rm.o dsp_debug.o adsp_debug.o +obj-y += adsp_video_verify_cmd.o +obj-y += adsp_videoenc_verify_cmd.o +obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o +obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o +obj-y += adsp_lpm_verify_cmd.o +ifdef CONFIG_MSM7X27A_AUDIO +obj-y += audio_pcm_in.o +obj-$(CONFIG_DEBUG_FS) += audio_voice_lb.o +else +obj-y += audio_in.o snd_pcm_client.o +endif +obj-$(CONFIG_MSM7X27A_AUDIO) += audio_aac_in.o audio_evrc_in.o audio_qcelp_in.o +obj-y += audio_out.o audio_mp3.o audmgr.o audpp.o audrec.o audpreproc.o +obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o audio_amrnb_in.o +obj-y += audio_wma.o audio_voicememo.o audio_pcm.o audio_amrwb.o audio_wmapro.o +obj-y += snd.o snd_adie.o +obj-$(CONFIG_ARCH_MSM7X27A) += audio_fm.o +obj-$(CONFIG_ARCH_MSM7X27A) += audio_mvs.o +obj-$(CONFIG_ARCH_MSM7X27A) += audio_lpa.o diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c new file mode 100644 index 00000000000..b4850586082 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -0,0 +1,1466 @@ +/* arch/arm/mach-msm/qdsp5/adsp.c + * + * Register/Interrupt access for userspace aDSP library. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* TODO: + * - move shareable rpc code outside of adsp.c + * - general solution for virt->phys patchup + * - queue IDs should be relative to modules + * - disallow access to non-associated queues + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DEBUG_FS +static struct dentry *dentry_adsp; +static struct dentry *dentry_wdata; +static struct dentry *dentry_rdata; +static int wdump, rdump; +#endif /* CONFIG_DEBUG_FS */ +static struct wake_lock adsp_wake_lock; +static inline void prevent_suspend(void) +{ + wake_lock(&adsp_wake_lock); +} +static inline void allow_suspend(void) +{ + wake_unlock(&adsp_wake_lock); +} + +#include +#include +#include +#include "adsp.h" + +static struct adsp_info adsp_info; +static struct msm_rpc_endpoint *rpc_cb_server_client; +static struct msm_adsp_module *adsp_modules; +static int adsp_open_count; + +static uint32_t rpc_adsp_rtos_atom_prog; +static uint32_t rpc_adsp_rtos_atom_vers; +static uint32_t rpc_adsp_rtos_atom_vers_comp; +static uint32_t rpc_adsp_rtos_mtoa_prog; +static uint32_t rpc_adsp_rtos_mtoa_vers; +static uint32_t rpc_adsp_rtos_mtoa_vers_comp; +static DEFINE_MUTEX(adsp_open_lock); + +static struct workqueue_struct *msm_adsp_probe_work_queue; +static void adsp_probe_work(struct work_struct *work); +static DECLARE_WORK(msm_adsp_probe_work, adsp_probe_work); + +/* protect interactions with the ADSP command/message queue */ +static spinlock_t adsp_cmd_lock; +static spinlock_t adsp_write_lock; + +static uint32_t current_image = -1; + +void adsp_set_image(struct adsp_info *info, uint32_t image) +{ + current_image = image; +} + +/* + * Checks whether the module_id is available in the + * module_entries table.If module_id is available returns `0`. + * If module_id is not available returns `-ENXIO`. + */ +static int32_t adsp_validate_module(uint32_t module_id) +{ + uint32_t *ptr; + uint32_t module_index; + uint32_t num_mod_entries; + + ptr = adsp_info.init_info_ptr->module_entries; + num_mod_entries = adsp_info.init_info_ptr->module_table_size; + + for (module_index = 0; module_index < num_mod_entries; module_index++) + if (module_id == ptr[module_index]) + return 0; + + return -ENXIO; +} + +static int32_t adsp_validate_queue(uint32_t mod_id, unsigned q_idx, + uint32_t size) +{ + int32_t i; + struct adsp_rtos_mp_mtoa_init_info_type *sptr; + + sptr = adsp_info.init_info_ptr; + for (i = 0; i < sptr->mod_to_q_entries; i++) + if (mod_id == sptr->mod_to_q_tbl[i].module) + if (q_idx == sptr->mod_to_q_tbl[i].q_type) { + if (size <= sptr->mod_to_q_tbl[i].q_max_len) + return 0; + MM_ERR("q_idx: %d is not a valid queue \ + for module %x\n", q_idx, mod_id); + return -EINVAL; + } + MM_ERR("cmd_buf size is more than allowed size\n"); + return -EINVAL; +} + +uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) +{ + return info->task_to_module[current_image][task]; +} + +uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id) +{ + return info->queue_offset[current_image][queue_id]; +} + +static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, + struct msm_adsp_module *adsp_module) +{ + int rc; + struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; + struct rpc_reply_hdr rpc_rsp; + + rpc_req.gotit = cpu_to_be32(1); + rpc_req.cmd = cpu_to_be32(cmd); + rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); + rpc_req.module = cpu_to_be32(module); + rc = msm_rpc_call_reply(adsp_module->rpc_client, + RPC_ADSP_RTOS_APP_TO_MODEM_PROC, + &rpc_req, sizeof(rpc_req), + &rpc_rsp, sizeof(rpc_rsp), + 5 * HZ); + + if (rc < 0) { + MM_ERR("error receiving RPC reply: %d (%d)\n", + rc, -ERESTARTSYS); + return rc; + } + + if (be32_to_cpu(rpc_rsp.reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + MM_ERR("RPC call was denied!\n"); + return -EPERM; + } + + if (be32_to_cpu(rpc_rsp.data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + MM_ERR("RPC call was not successful (%d)\n", + be32_to_cpu(rpc_rsp.data.acc_hdr.accept_stat)); + return -EINVAL; + } + + return 0; +} + +static int get_module_index(uint32_t id) +{ + int mod_idx; + for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++) + if (adsp_info.module[mod_idx].id == id) + return mod_idx; + + return -ENXIO; +} + +static struct msm_adsp_module *find_adsp_module_by_id( + struct adsp_info *info, uint32_t id) +{ + int mod_idx; + + if (id > info->max_module_id) { + return NULL; + } else { + mod_idx = get_module_index(id); + if (mod_idx < 0) + return NULL; + return info->id_to_module[mod_idx]; + } +} + +static struct msm_adsp_module *find_adsp_module_by_name( + struct adsp_info *info, const char *name) +{ + unsigned n; + for (n = 0; n < info->module_count; n++) + if (!strcmp(name, adsp_modules[n].name)) + return adsp_modules + n; + return NULL; +} + +static int adsp_rpc_init(struct msm_adsp_module *adsp_module) +{ + /* remove the original connect once compatible support is complete */ + adsp_module->rpc_client = msm_rpc_connect( + rpc_adsp_rtos_atom_prog, + rpc_adsp_rtos_atom_vers, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(adsp_module->rpc_client)) + adsp_module->rpc_client = msm_rpc_connect_compatible( + rpc_adsp_rtos_atom_prog, + rpc_adsp_rtos_atom_vers_comp, + MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(adsp_module->rpc_client)) { + int rc = PTR_ERR(adsp_module->rpc_client); + adsp_module->rpc_client = 0; + MM_ERR("could not open rpc client: %d\n", rc); + return rc; + } + + return 0; +} + +/* + * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get + * queue offsets and module entries (init info) as part of the event. + */ +static void msm_get_init_info(void) +{ + int rc; + struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; + struct rpc_reply_hdr rpc_rsp; + + adsp_info.init_info_rpc_client = msm_rpc_connect( + rpc_adsp_rtos_atom_prog, + rpc_adsp_rtos_atom_vers, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(adsp_info.init_info_rpc_client)) { + adsp_info.init_info_rpc_client = msm_rpc_connect_compatible( + rpc_adsp_rtos_atom_prog, + rpc_adsp_rtos_atom_vers_comp, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(adsp_info.init_info_rpc_client)) { + rc = PTR_ERR(adsp_info.init_info_rpc_client); + adsp_info.init_info_rpc_client = 0; + MM_ERR("could not open rpc client: %d\n", rc); + return; + } + } + + rpc_req.gotit = cpu_to_be32(1); + rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO); + rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); + rpc_req.module = 0; + + rc = msm_rpc_call_reply(adsp_info.init_info_rpc_client, + RPC_ADSP_RTOS_APP_TO_MODEM_PROC, + &rpc_req, sizeof(rpc_req), + &rpc_rsp, sizeof(rpc_rsp), + 5 * HZ); + + if (rc < 0) + MM_ERR("could not send RPC request: %d\n", rc); +} + +int msm_adsp_get(const char *name, struct msm_adsp_module **out, + struct msm_adsp_ops *ops, void *driver_data) +{ + struct msm_adsp_module *module; + int rc = 0; + static uint32_t init_info_cmd_sent; + + mutex_lock(&adsp_info.lock); + if (!init_info_cmd_sent) { + init_waitqueue_head(&adsp_info.init_info_wait); + msm_get_init_info(); + rc = wait_event_timeout(adsp_info.init_info_wait, + adsp_info.init_info_state == ADSP_STATE_INIT_INFO, + 5 * HZ); + if (!rc) { + MM_ERR("INIT_INFO failed\n"); + mutex_unlock(&adsp_info.lock); + return -ETIMEDOUT; + + } + init_info_cmd_sent++; + } + mutex_unlock(&adsp_info.lock); + + module = find_adsp_module_by_name(&adsp_info, name); + if (!module) + return -ENODEV; + + mutex_lock(&module->lock); + MM_INFO("opening module %s\n", module->name); + + if (module->ops) { + rc = -EBUSY; + goto done; + } + + rc = adsp_rpc_init(module); + if (rc) + goto done; + + module->ops = ops; + module->driver_data = driver_data; + *out = module; + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP, + module->id, module); + if (rc) { + module->ops = NULL; + module->driver_data = NULL; + *out = NULL; + MM_ERR("REGISTER_APP failed\n"); + goto done; + } + + MM_DBG("module %s has been registered\n", module->name); + +done: + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_get); + +static int msm_adsp_disable_locked(struct msm_adsp_module *module); + +void msm_adsp_put(struct msm_adsp_module *module) +{ + unsigned long flags; + + mutex_lock(&module->lock); + if (module->ops) { + MM_INFO("closing module %s\n", module->name); + + /* lock to ensure a dsp event cannot be delivered + * during or after removal of the ops and driver_data + */ + spin_lock_irqsave(&adsp_cmd_lock, flags); + module->ops = NULL; + module->driver_data = NULL; + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + + if (module->state != ADSP_STATE_DISABLED) { + MM_INFO("disabling module %s\n", module->name); + msm_adsp_disable_locked(module); + } + + msm_rpc_close(module->rpc_client); + module->rpc_client = 0; + } else { + MM_INFO("module %s is already closed\n", module->name); + } + mutex_unlock(&module->lock); +} +EXPORT_SYMBOL(msm_adsp_put); + +/* this should be common code with rpc_servers.c */ +static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, + uint32_t xid, uint32_t accept_status) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf)); + if (rc < 0) + MM_ERR("could not write RPC response: %d\n", rc); + return rc; +} + +int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + uint32_t ctrl_word; + uint32_t dsp_q_addr; + uint32_t dsp_addr; + uint32_t cmd_id = 0; + int cnt = 0; + int ret_status = 0; + unsigned long flags; + struct adsp_info *info; + + if (!module || !cmd_buf) { + MM_ERR("Called with NULL parameters\n"); + return -EINVAL; + } + info = module->info; + spin_lock_irqsave(&adsp_write_lock, flags); + + if (module->state != ADSP_STATE_ENABLED) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + MM_ERR("module %s not enabled before write\n", module->name); + return -ENODEV; + } + if (adsp_validate_module(module->id)) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + MM_ERR("module id validation failed %s %d\n", + module->name, module->id); + return -ENXIO; + } + if (dsp_queue_addr >= QDSP_MAX_NUM_QUEUES) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + MM_ERR("Invalid Queue Index: %d\n", dsp_queue_addr); + return -ENXIO; + } + if (adsp_validate_queue(module->id, dsp_queue_addr, cmd_size)) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + return -EINVAL; + } + dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr); + dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M; + + /* Poll until the ADSP is ready to accept a command. + * Wait for 100us, return error if it's not responding. + * If this returns an error, we need to disable ALL modules and + * then retry. + */ + while (((ctrl_word = readl(info->write_ctrl)) & + ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != + ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { + if (cnt > 50) { + MM_ERR("timeout waiting for DSP write ready\n"); + ret_status = -EIO; + goto fail; + } + MM_DBG("waiting for DSP write ready\n"); + udelay(2); + cnt++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Clear the command bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. This notifies the DSP that + * we are about to send a command on this particular queue. The + * DSP will in response change its state. + */ + writel(1, info->send_irq); + + /* Poll until the adsp responds to the interrupt; this does not + * generate an interrupt from the adsp. This should happen within + * 5ms. + */ + cnt = 0; + while ((readl(info->write_ctrl) & + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { + if (cnt > 2500) { + MM_ERR("timeout waiting for adsp ack\n"); + ret_status = -EIO; + goto fail; + } + udelay(2); + cnt++; + } + + /* Read the ctrl word */ + ctrl_word = readl(info->write_ctrl); + + if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != + ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { + ret_status = -EAGAIN; + goto fail; + } else { + /* No error */ + /* Get the DSP buffer address */ + dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE; + + if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint16_t *buf_ptr = (uint16_t *) cmd_buf; + uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; + cmd_size /= sizeof(uint16_t); + + /* Save the command ID */ + cmd_id = (uint32_t) buf_ptr[0]; + + /* Copy the command to DSP memory */ + cmd_size++; + while (--cmd_size) + *dsp_addr16++ = *buf_ptr++; + } else { + uint32_t *buf_ptr = (uint32_t *) cmd_buf; + uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; + cmd_size /= sizeof(uint32_t); + + /* Save the command ID */ + cmd_id = buf_ptr[0]; + + cmd_size++; + while (--cmd_size) + *dsp_addr32++ = *buf_ptr++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Set the command bits to write done */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. It does not respond with + * an interrupt, and we do not need to wait for it to + * acknowledge, because it will hold the mutex lock until it's + * ready to receive more commands again. + */ + writel(1, info->send_irq); + + module->num_commands++; + } /* Ctrl word status bits were 00, no error in the ctrl word */ + +fail: + spin_unlock_irqrestore(&adsp_write_lock, flags); + return ret_status; +} +EXPORT_SYMBOL(msm_adsp_write); + +int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + int rc, retries = 0; +#ifdef CONFIG_DEBUG_FS + uint16_t *ptr; + int ii; + + if (wdump > 0) { + ptr = cmd_buf; + pr_info("A->D:%x\n", module->id); + pr_info("adsp: %x %d\n", dsp_queue_addr, cmd_size); + for (ii = 0; ii < cmd_size/2; ii++) + pr_info("%x ", ptr[ii]); + pr_info("\n"); + } +#endif /* CONFIG_DEBUG_FS */ + do { + rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, + cmd_size); + if (rc == -EAGAIN) + udelay(10); + } while (rc == -EAGAIN && retries++ < 300); + if (retries > 50) + MM_ERR("adsp: %s command took %d attempts: rc %d\n", + module->name, retries, rc); + return rc; +} + +static void *event_addr; +static void read_event(void *buf, size_t len) +{ + uint32_t dptr[3]; + struct rpc_adsp_rtos_modem_to_app_args_t *sptr; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + + sptr = event_addr; + pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + + dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event); + dptr[1] = be32_to_cpu(pkt_ptr->module); + dptr[2] = be32_to_cpu(pkt_ptr->image); + + if (len > EVENT_LEN) + len = EVENT_LEN; + + memcpy(buf, dptr, len); +} + +static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) +{ + struct rpc_adsp_rtos_modem_to_app_args_t *args = + (struct rpc_adsp_rtos_modem_to_app_args_t *)req; + uint32_t event; + uint32_t proc_id; + uint32_t module_id; + uint32_t image; + struct msm_adsp_module *module; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + struct queue_to_offset_type *qptr; + struct queue_to_offset_type *qtbl; + struct mod_to_queue_offsets *mqptr; + struct mod_to_queue_offsets *mqtbl; + uint32_t *mptr; + uint32_t *mtbl; + uint32_t q_idx; + uint32_t num_entries; + uint32_t entries_per_image; + struct adsp_rtos_mp_mtoa_init_info_type *iptr; + struct adsp_rtos_mp_mtoa_init_info_type *sptr; + int32_t i_no, e_idx; + + event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event); + proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id); + + if (event == RPC_ADSP_RTOS_INIT_INFO) { + MM_INFO("INIT_INFO Event\n"); + sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_init_packet; + + iptr = adsp_info.init_info_ptr; + iptr->image_count = be32_to_cpu(sptr->image_count); + if (iptr->image_count > IMG_MAX) + iptr->image_count = IMG_MAX; + iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets); + num_entries = iptr->num_queue_offsets; + if (num_entries > ENTRIES_MAX) { + num_entries = ENTRIES_MAX; + iptr->num_queue_offsets = ENTRIES_MAX; + } + qptr = &sptr->queue_offsets_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + qtbl = &iptr->queue_offsets_tbl[i_no][0]; + for (e_idx = 0; e_idx < num_entries; e_idx++) { + qtbl[e_idx].offset = be32_to_cpu(qptr->offset); + qtbl[e_idx].queue = be32_to_cpu(qptr->queue); + q_idx = be32_to_cpu(qptr->queue); + iptr->queue_offsets[i_no][q_idx] = qtbl[e_idx].offset; + qptr++; + } + } + + num_entries = be32_to_cpu(sptr->num_task_module_entries); + if (num_entries > ENTRIES_MAX) + num_entries = ENTRIES_MAX; + iptr->num_task_module_entries = num_entries; + entries_per_image = num_entries / iptr->image_count; + mptr = &sptr->task_to_module_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + mtbl = &iptr->task_to_module_tbl[i_no][0]; + for (e_idx = 0; e_idx < entries_per_image; e_idx++) { + mtbl[e_idx] = be32_to_cpu(*mptr); + mptr++; + } + } + + iptr->module_table_size = be32_to_cpu(sptr->module_table_size); +#if CONFIG_ADSP_RPC_VER > 0x30001 + if (iptr->module_table_size > MODULES_MAX) + iptr->module_table_size = MODULES_MAX; +#else + if (iptr->module_table_size > ENTRIES_MAX) + iptr->module_table_size = ENTRIES_MAX; +#endif + mptr = &sptr->module_entries[0]; + for (i_no = 0; i_no < iptr->module_table_size; i_no++) + iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]); + + mqptr = &sptr->mod_to_q_tbl[0]; + mqtbl = &iptr->mod_to_q_tbl[0]; + iptr->mod_to_q_entries = be32_to_cpu(sptr->mod_to_q_entries); + if (iptr->mod_to_q_entries > ENTRIES_MAX) + iptr->mod_to_q_entries = ENTRIES_MAX; + for (e_idx = 0; e_idx < iptr->mod_to_q_entries; e_idx++) { + mqtbl[e_idx].module = be32_to_cpu(mqptr->module); + mqtbl[e_idx].q_type = be32_to_cpu(mqptr->q_type); + mqtbl[e_idx].q_max_len = be32_to_cpu(mqptr->q_max_len); + mqptr++; + } + + adsp_info.init_info_state = ADSP_STATE_INIT_INFO; + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_SUCCESS); + wake_up(&adsp_info.init_info_wait); + + return; + } + + pkt_ptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + module_id = be32_to_cpu(pkt_ptr->module); + image = be32_to_cpu(pkt_ptr->image); + + MM_DBG("rpc event=%d, proc_id=%d, module=%d, image=%d\n", + event, proc_id, module_id, image); + + module = find_adsp_module_by_id(&adsp_info, module_id); + if (!module) { + MM_ERR("module %d is not supported!\n", module_id); + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_GARBAGE_ARGS); + return; + } + + mutex_lock(&module->lock); + switch (event) { + case RPC_ADSP_RTOS_MOD_READY: + MM_INFO("module %s: READY\n", module->name); + module->state = ADSP_STATE_ENABLED; + wake_up(&module->state_wait); + adsp_set_image(module->info, image); + break; + case RPC_ADSP_RTOS_MOD_DISABLE: + MM_INFO("module %s: DISABLED\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_SERVICE_RESET: + MM_INFO("module %s: SERVICE_RESET\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_CMD_SUCCESS: + MM_INFO("module %s: CMD_SUCCESS\n", module->name); + break; + case RPC_ADSP_RTOS_CMD_FAIL: + MM_INFO("module %s: CMD_FAIL\n", module->name); + break; + case RPC_ADSP_RTOS_DISABLE_FAIL: + MM_INFO("module %s: DISABLE_FAIL\n", module->name); + break; + default: + MM_ERR("unknown event %d\n", event); + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_GARBAGE_ARGS); + mutex_unlock(&module->lock); + return; + } + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_SUCCESS); +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS + event_addr = (uint32_t *)req; + module->ops->event(module->driver_data, + EVENT_MSG_ID, + EVENT_LEN, + read_event); +#endif + mutex_unlock(&module->lock); +} + +static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req) +{ + switch (req->procedure) { + case RPC_ADSP_RTOS_MTOA_NULL_PROC: + rpc_send_accepted_void_reply(rpc_cb_server_client, + req->xid, + RPC_ACCEPTSTAT_SUCCESS); + break; +#if CONFIG_ADSP_RPC_VER > 0x30001 + case RPC_ADSP_RTOS_MTOA_INIT_INFO_PROC: + case RPC_ADSP_RTOS_MTOA_EVENT_INFO_PROC: +#else + case RPC_ADSP_RTOS_MODEM_TO_APP_PROC: +#endif + handle_adsp_rtos_mtoa_app(req); + break; + default: + MM_ERR("unknowned proc %d\n", req->procedure); + rpc_send_accepted_void_reply( + rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_PROC_UNAVAIL); + break; + } + return 0; +} + +/* this should be common code with rpc_servers.c */ +static int adsp_rpc_thread(void *data) +{ + void *buffer; + struct rpc_request_hdr *req; + int rc, exit = 0; + + do { + rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1); + if (rc < 0) { + MM_ERR("could not read rpc: %d\n", rc); + break; + } + req = (struct rpc_request_hdr *)buffer; + + req->type = be32_to_cpu(req->type); + req->xid = be32_to_cpu(req->xid); + req->rpc_vers = be32_to_cpu(req->rpc_vers); + req->prog = be32_to_cpu(req->prog); + req->vers = be32_to_cpu(req->vers); + req->procedure = be32_to_cpu(req->procedure); + + if (req->type != 0) + goto bad_rpc; + if (req->rpc_vers != 2) + goto bad_rpc; + if (req->prog != rpc_adsp_rtos_mtoa_prog) + goto bad_rpc; + if (!msm_rpc_is_compatible_version(rpc_adsp_rtos_mtoa_vers, + req->vers)) + goto bad_rpc; + + handle_adsp_rtos_mtoa(req); + kfree(buffer); + continue; + +bad_rpc: + MM_ERR("bogus rpc from modem\n"); + kfree(buffer); + } while (!exit); + do_exit(0); +} + +static size_t read_event_size; +static void *read_event_addr; + +static void read_event_16(void *buf, size_t len) +{ + uint16_t *dst = buf; + uint16_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static void read_event_32(void *buf, size_t len) +{ + uint32_t *dst = buf; + uint32_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v( + struct adsp_info *info, void *dsp_addr) +{ + struct msm_adsp_module *module; + unsigned rtos_task_id; + unsigned msg_id; + unsigned msg_length; +#ifdef CONFIG_DEBUG_FS + uint16_t *ptr16; + uint32_t *ptr32; + int ii; +#endif /* CONFIG_DEBUG_FS */ + void (*func)(void *, size_t); + + if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint32_t *dsp_addr32 = dsp_addr; + uint32_t tmp = *dsp_addr32++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M); + read_event_size = tmp >> 16; + read_event_addr = dsp_addr32; + msg_length = read_event_size * sizeof(uint32_t); + func = read_event_32; + } else { + uint16_t *dsp_addr16 = dsp_addr; + uint16_t tmp = *dsp_addr16++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M; + read_event_size = *dsp_addr16++; + read_event_addr = dsp_addr16; + msg_length = read_event_size * sizeof(uint16_t); + func = read_event_16; + } + + if (rtos_task_id > info->max_task_id) { + MM_ERR("bogus task id %d\n", rtos_task_id); + return 0; + } + module = find_adsp_module_by_id(info, + adsp_get_module(info, rtos_task_id)); + + if (!module) { + MM_ERR("no module for task id %d\n", rtos_task_id); + return 0; + } + + module->num_events++; + + if (!module->ops) { + MM_ERR("module %s is not open\n", module->name); + return 0; + } +#ifdef CONFIG_DEBUG_FS + if (rdump > 0 && + (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET))) { + ptr32 = read_event_addr; + pr_info("D->A\n"); + pr_info("m_id = %x id = %x\n", module->id, msg_id); + for (ii = 0; ii < msg_length/4; ii++) + pr_info("%x ", ptr32[ii]); + pr_info("\n"); + } else if (rdump > 0) { + ptr16 = read_event_addr; + pr_info("D->A\n"); + pr_info("m_id = %x id = %x\n", module->id, msg_id); + for (ii = 0; ii < msg_length/2; ii++) + pr_info("%x ", ptr16[ii]); + pr_info("\n"); + } +#endif /* CONFIG_DEBUG_FS */ + + module->ops->event(module->driver_data, msg_id, msg_length, func); + return 0; +} + +static int adsp_get_event(struct adsp_info *info) +{ + uint32_t ctrl_word; + uint32_t ready; + void *dsp_addr; + uint32_t cmd_type; + int cnt; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + + /* Whenever the DSP has a message, it updates this control word + * and generates an interrupt. When we receive the interrupt, we + * read this register to find out what ADSP task the command is + * comming from. + * + * The ADSP should *always* be ready on the first call, but the + * irq handler calls us in a loop (to handle back-to-back command + * processing), so we give the DSP some time to return to the + * ready state. The DSP will not issue another IRQ for events + * pending between the first IRQ and the event queue being drained, + * unfortunately. + */ + + for (cnt = 0; cnt < 50; cnt++) { + ctrl_word = readl(info->read_ctrl); + + if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) == + ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V) + goto ready; + + udelay(2); + } + MM_ERR("not ready after 100uS\n"); + rc = -EBUSY; + goto done; + +ready: + /* Here we check to see if there are pending messages. If there are + * none, we siply return -EAGAIN to indicate that there are no more + * messages pending. + */ + ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M; + if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) && + (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) { + rc = -EAGAIN; + goto done; + } + + /* DSP says that there are messages waiting for the host to read */ + + /* Get the Command Type */ + cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M; + + /* Get the DSP buffer address */ + dsp_addr = (void *)((ctrl_word & + ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE); + + /* We can only handle Task-to-Host messages */ + if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) { + MM_ERR("unknown dsp cmd_type %d\n", cmd_type); + rc = -EIO; + goto done; + } + + adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr); + + ctrl_word = readl(info->read_ctrl); + ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M; + + /* Write ctrl word to the DSP */ + writel(ctrl_word, info->read_ctrl); + + /* Generate an interrupt to the DSP */ + writel(1, info->send_irq); + +done: + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return rc; +} + +static irqreturn_t adsp_irq_handler(int irq, void *data) +{ + struct adsp_info *info = &adsp_info; + int cnt = 0; + for (cnt = 0; cnt < 15; cnt++) + if (adsp_get_event(info) < 0) + break; + if (cnt > info->event_backlog_max) + info->event_backlog_max = cnt; + info->events_received += cnt; + if (cnt == 15) + MM_ERR("too many (%d) events for single irq!\n", cnt); + return IRQ_HANDLED; +} + +int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate) +{ + if (!module) + return -EINVAL; + + if (module->clk && clk_rate) + return clk_set_rate(module->clk, clk_rate); + + return -EINVAL; +} + +int msm_adsp_generate_event(void *data, + struct msm_adsp_module *mod, + unsigned event_id, + unsigned event_length, + unsigned event_size, + void *msg) +{ + unsigned long flags; + void (*func)(void *, size_t); + + if (!mod) + return -EINVAL; + + if (event_size == sizeof(uint32_t)) + func = read_event_32; + else if (event_size == sizeof(uint16_t)) + func = read_event_16; + else + return -EINVAL; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + read_event_addr = msg; + read_event_size = event_length; + mod->ops->event(data, event_id, event_length, func); + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return 0; +} + +int msm_adsp_enable(struct msm_adsp_module *module) +{ + int rc = 0; + + if (!module) + return -EINVAL; + + MM_INFO("enable '%s'state[%d] id[%d]\n", + module->name, module->state, module->id); + + mutex_lock(&module->lock); + switch (module->state) { + case ADSP_STATE_DISABLED: + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE, + module->id, module); + if (rc) + break; + module->state = ADSP_STATE_ENABLING; + mutex_unlock(&module->lock); + rc = wait_event_timeout(module->state_wait, + module->state != ADSP_STATE_ENABLING, + 1 * HZ); + mutex_lock(&module->lock); + if (module->state == ADSP_STATE_ENABLED) { + rc = 0; + } else { + MM_ERR("module '%s' enable timed out\n", module->name); + rc = -ETIMEDOUT; + } + if (module->open_count++ == 0 && module->clk) + clk_prepare_enable(module->clk); + + mutex_lock(&adsp_open_lock); + if (adsp_open_count++ == 0) { + enable_irq(adsp_info.int_adsp); + prevent_suspend(); + } + mutex_unlock(&adsp_open_lock); + break; + case ADSP_STATE_ENABLING: + MM_DBG("module '%s' enable in progress\n", module->name); + break; + case ADSP_STATE_ENABLED: + MM_DBG("module '%s' already enabled\n", module->name); + break; + case ADSP_STATE_DISABLING: + MM_ERR("module '%s' disable in progress\n", module->name); + rc = -EBUSY; + break; + } + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_enable); + +int msm_adsp_disable_event_rsp(struct msm_adsp_module *module) +{ + int rc = 0; + + if (!module) + return -EINVAL; + + mutex_lock(&module->lock); + + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, + module->id, module); + mutex_unlock(&module->lock); + + return rc; +} +EXPORT_SYMBOL(msm_adsp_disable_event_rsp); + +static int msm_adsp_disable_locked(struct msm_adsp_module *module) +{ + int rc = 0; + + if (!module) + return -EINVAL; + + switch (module->state) { + case ADSP_STATE_DISABLED: + MM_DBG("module '%s' already disabled\n", module->name); + break; + case ADSP_STATE_ENABLING: + case ADSP_STATE_ENABLED: + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE, + module->id, module); + module->state = ADSP_STATE_DISABLED; + if (--module->open_count == 0 && module->clk) + clk_disable_unprepare(module->clk); + mutex_lock(&adsp_open_lock); + if (--adsp_open_count == 0) { + disable_irq(adsp_info.int_adsp); + allow_suspend(); + MM_DBG("disable interrupt\n"); + } + mutex_unlock(&adsp_open_lock); + } + return rc; +} + +int msm_adsp_disable(struct msm_adsp_module *module) +{ + int rc; + + if (!module) + return -EINVAL; + + MM_INFO("disable '%s'\n", module->name); + mutex_lock(&module->lock); + rc = msm_adsp_disable_locked(module); + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_disable); + +static int msm_adsp_probe(struct platform_device *pdev) +{ + unsigned count; + int rc, i; + + adsp_info.int_adsp = platform_get_irq(pdev, 0); + if (adsp_info.int_adsp < 0) { + MM_ERR("no irq resource?\n"); + return -ENODEV; + } + + wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp"); + adsp_info.init_info_ptr = kzalloc( + (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL); + if (!adsp_info.init_info_ptr) + return -ENOMEM; + + rc = adsp_init_info(&adsp_info); + if (rc) + return rc; + adsp_info.send_irq += (uint32_t) MSM_AD5_BASE; + adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE; + adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE; + count = adsp_info.module_count; + + adsp_modules = kzalloc( + (sizeof(struct msm_adsp_module) + sizeof(void *)) * + count, GFP_KERNEL); + if (!adsp_modules) + return -ENOMEM; + + adsp_info.id_to_module = (void *) (adsp_modules + count); + + spin_lock_init(&adsp_cmd_lock); + spin_lock_init(&adsp_write_lock); + mutex_init(&adsp_info.lock); + + rc = request_irq(adsp_info.int_adsp, adsp_irq_handler, + IRQF_TRIGGER_RISING, "adsp", 0); + if (rc < 0) + goto fail_request_irq; + disable_irq(adsp_info.int_adsp); + + rpc_cb_server_client = msm_rpc_open(); + if (IS_ERR(rpc_cb_server_client)) { + rpc_cb_server_client = NULL; + rc = PTR_ERR(rpc_cb_server_client); + MM_ERR("could not create rpc server (%d)\n", rc); + goto fail_rpc_open; + } + + rc = msm_rpc_register_server(rpc_cb_server_client, + rpc_adsp_rtos_mtoa_prog, + rpc_adsp_rtos_mtoa_vers); + if (rc) { + MM_ERR("could not register callback server (%d)\n", rc); + goto fail_rpc_register; + } + + /* schedule start of kernel thread later using work queue */ + queue_work(msm_adsp_probe_work_queue, &msm_adsp_probe_work); + + for (i = 0; i < count; i++) { + struct msm_adsp_module *mod = adsp_modules + i; + mutex_init(&mod->lock); + init_waitqueue_head(&mod->state_wait); + mod->info = &adsp_info; + mod->name = adsp_info.module[i].name; + mod->id = adsp_info.module[i].id; + if (adsp_info.module[i].clk_name) + mod->clk = clk_get(NULL, adsp_info.module[i].clk_name); + else + mod->clk = NULL; + if (mod->clk && adsp_info.module[i].clk_rate) + clk_set_rate(mod->clk, adsp_info.module[i].clk_rate); + mod->verify_cmd = adsp_info.module[i].verify_cmd; + mod->patch_event = adsp_info.module[i].patch_event; + INIT_HLIST_HEAD(&mod->pmem_regions); + mod->pdev.name = adsp_info.module[i].pdev_name; + mod->pdev.id = -1; + adsp_info.id_to_module[i] = mod; + platform_device_register(&mod->pdev); + } + + msm_adsp_publish_cdevs(adsp_modules, count); + rmtask_init(); + + return 0; + +fail_rpc_register: + msm_rpc_close(rpc_cb_server_client); + rpc_cb_server_client = NULL; +fail_rpc_open: + enable_irq(adsp_info.int_adsp); + free_irq(adsp_info.int_adsp, 0); +fail_request_irq: + kfree(adsp_modules); + kfree(adsp_info.init_info_ptr); + return rc; +} + +static void adsp_probe_work(struct work_struct *work) +{ + /* start the kernel thread to process the callbacks */ + kthread_run(adsp_rpc_thread, NULL, "kadspd"); +} + +#ifdef CONFIG_DEBUG_FS +static int get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (strict_strtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } + else + return -EINVAL; + } + return 0; +} + + +static ssize_t adsp_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_debug("adsp debugfs opened\n"); + return 0; +} +static ssize_t adsp_debug_write(struct file *file, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + char *access_str = file->private_data; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + rc = copy_from_user(lbuf, buf, cnt); + if (rc) { + pr_info("Unable to copy data from user space\n"); + return -EFAULT; + } + lbuf[cnt] = '\0'; + + if (!strcmp(access_str, "write_log")) { + if (get_parameters(lbuf, param, 1) == 0) { + switch (param[0]) { + case 1: + if (wdump <= 0) + wdump = 1; + pr_debug("write cmd to DSP(A->D) dump \ + started:%d\n", wdump); + break; + case 0: + if (wdump > 0) + wdump = 0; + pr_debug("Stop write cmd to \ + DSP(A->D):%d\n", wdump); + break; + default: + rc = -EINVAL; + break; + } + } else + rc = -EINVAL; + } else if (!strcmp(access_str, "read_log")) { + if (get_parameters(lbuf, param, 1) == 0) { + switch (param[0]) { + case 1: + if (rdump <= 0) + rdump = 1; + pr_debug("write cmd from DSP(D->A) dump \ + started:%d\n", wdump); + break; + case 0: + if (rdump > 0) + rdump = 0; + pr_debug("Stop write cmd from \ + DSP(D->A):%d\n", wdump); + break; + default: + rc = -EINVAL; + break; + } + } else + rc = -EINVAL; + } else { + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else { + pr_err("%s: rc = %d\n", __func__, rc); + pr_info("\nWrong command: Use =>\n"); + pr_info("-------------------------\n"); + pr_info("To Start A->D:: echo \"1\">/sys/kernel/debug/ \ + adsp_cmd/write_log\n"); + pr_info("To Start D->A:: echo \"1\">/sys/kernel/debug/ \ + adsp_cmd/read_log\n"); + pr_info("To Stop A->D:: echo \"0\">/sys/kernel/debug/ \ + adsp_cmd/write_log\n"); + pr_info("To Stop D->A:: echo \"0\">/sys/kernel/debug/ \ + adsp_cmd/read_log\n"); + pr_info("------------------------\n"); + } + + return rc; +} +#endif + +static struct platform_driver msm_adsp_driver = { + .probe = msm_adsp_probe, + .driver = { + .owner = THIS_MODULE, + }, +}; + +static const char msm_adsp_driver_name[] = "msm_adsp"; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations adsp_debug_fops = { + .write = adsp_debug_write, + .open = adsp_debug_open, +}; +#endif + +static int __init adsp_init(void) +{ + int rc; + +#ifdef CONFIG_DEBUG_FS + dentry_adsp = debugfs_create_dir("adsp_cmd", 0); + if (!IS_ERR(dentry_adsp)) { + dentry_wdata = debugfs_create_file("write_log", \ + S_IFREG | S_IRUGO, dentry_adsp, + (void *) "write_log" , &adsp_debug_fops); + dentry_rdata = debugfs_create_file("read_log", \ + S_IFREG | S_IRUGO, dentry_adsp, + (void *) "read_log", &adsp_debug_fops); + } + rdump = 0; + wdump = 0; +#endif /* CONFIG_DEBUG_FS */ + + rpc_adsp_rtos_atom_prog = 0x3000000a; + rpc_adsp_rtos_atom_vers = 0x10001; + rpc_adsp_rtos_atom_vers_comp = 0x00010001; + rpc_adsp_rtos_mtoa_prog = 0x3000000b; +#if CONFIG_ADSP_RPC_VER > 0x30001 + rpc_adsp_rtos_mtoa_vers = 0x30002; + rpc_adsp_rtos_mtoa_vers_comp = 0x00030002; +#else + rpc_adsp_rtos_mtoa_vers = 0x30001; + rpc_adsp_rtos_mtoa_vers_comp = 0x00030001; +#endif + + msm_adsp_probe_work_queue = create_workqueue("msm_adsp_probe"); + if (msm_adsp_probe_work_queue == NULL) + return -ENOMEM; + msm_adsp_driver.driver.name = msm_adsp_driver_name; + rc = platform_driver_register(&msm_adsp_driver); + MM_INFO("%s -- %d\n", msm_adsp_driver_name, rc); + return rc; +} + +device_initcall(adsp_init); diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h new file mode 100644 index 00000000000..0f16111c9a9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -0,0 +1,356 @@ +/* arch/arm/mach-msm/qdsp5/adsp.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2010, 2012 Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_ADSP_H +#define _ARCH_ARM_MACH_MSM_ADSP_H + +#include +#include +#include +#include + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len); +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len, + struct file **filp, unsigned long *offset); +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr); + +int adsp_vfe_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_lpm_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_video_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +void q5audio_dsp_not_responding(void); + +struct adsp_event; + +int adsp_vfe_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + +int adsp_jpeg_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + + +struct adsp_module_info { + const char *name; + const char *pdev_name; + uint32_t id; + const char *clk_name; + unsigned long clk_rate; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +#define ADSP_EVENT_MAX_SIZE 496 +#define EVENT_LEN 12 +#define EVENT_MSG_ID ((uint16_t)~0) + +struct adsp_event { + struct list_head list; + uint32_t size; /* always in bytes */ + uint16_t msg_id; + uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */ + int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ + union { + uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; + uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; + } data; +}; + +struct adsp_info { + uint32_t send_irq; + uint32_t read_ctrl; + uint32_t write_ctrl; + + uint32_t max_msg16_size; + uint32_t max_msg32_size; + + uint32_t max_task_id; + uint32_t max_module_id; + uint32_t max_queue_id; + uint32_t max_image_id; + + /* for each image id, a map of queue id to offset */ + uint32_t **queue_offset; + + /* for each image id, a map of task id to module id */ + uint32_t **task_to_module; + + /* for each module id, map of module id to module */ + struct msm_adsp_module **id_to_module; + + uint32_t module_count; + struct adsp_module_info *module; + + /* stats */ + uint32_t events_received; + uint32_t event_backlog_max; + + /* rpc_client for init_info */ + struct msm_rpc_endpoint *init_info_rpc_client; + struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr; + wait_queue_head_t init_info_wait; + unsigned init_info_state; + struct mutex lock; + + /* Interrupt value */ + int int_adsp; +}; + +#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0 +#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0 +#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2 +#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2 +#define RPC_ADSP_RTOS_MTOA_EVENT_INFO_PROC 3 +#define RPC_ADSP_RTOS_MTOA_INIT_INFO_PROC 4 + +enum rpc_adsp_rtos_proc_type { + RPC_ADSP_RTOS_PROC_NONE = 0, + RPC_ADSP_RTOS_PROC_MODEM = 1, + RPC_ADSP_RTOS_PROC_APPS = 2, +}; + +enum { + RPC_ADSP_RTOS_CMD_REGISTER_APP, + RPC_ADSP_RTOS_CMD_ENABLE, + RPC_ADSP_RTOS_CMD_DISABLE, + RPC_ADSP_RTOS_CMD_KERNEL_COMMAND, + RPC_ADSP_RTOS_CMD_16_COMMAND, + RPC_ADSP_RTOS_CMD_32_COMMAND, + RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, + RPC_ADSP_RTOS_CMD_REMOTE_EVENT, + RPC_ADSP_RTOS_CMD_SET_STATE, + RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT, + RPC_ADSP_RTOS_CMD_GET_INIT_INFO, +}; + +enum rpc_adsp_rtos_mod_status_type { + RPC_ADSP_RTOS_MOD_READY, + RPC_ADSP_RTOS_MOD_DISABLE, + RPC_ADSP_RTOS_SERVICE_RESET, + RPC_ADSP_RTOS_CMD_FAIL, + RPC_ADSP_RTOS_CMD_SUCCESS, + RPC_ADSP_RTOS_INIT_INFO, + RPC_ADSP_RTOS_DISABLE_FAIL, +}; + +struct rpc_adsp_rtos_app_to_modem_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */ + uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */ + uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ +}; + +enum qdsp_image_type { + QDSP_IMAGE_COMBO, + QDSP_IMAGE_GAUDIO, + QDSP_IMAGE_QTV_LP, + QDSP_IMAGE_MAX, + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ + QDSP_IMAGE_32BIT_DUMMY = 0x10000 +}; + +struct adsp_rtos_mp_mtoa_header_type { + enum rpc_adsp_rtos_mod_status_type event; + enum rpc_adsp_rtos_proc_type proc_id; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Event Info*/ +struct adsp_rtos_mp_mtoa_type { + uint32_t module; + uint32_t image; + uint32_t apps_okts; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Init Info */ +#if CONFIG_ADSP_RPC_VER > 0x30001 +#define IMG_MAX 2 +#define ENTRIES_MAX 36 +#define MODULES_MAX 64 +#else +#define IMG_MAX 6 +#define ENTRIES_MAX 48 +#endif +#define QUEUES_MAX 64 + +struct queue_to_offset_type { + uint32_t queue; + uint32_t offset; +}; + +struct mod_to_queue_offsets { + uint32_t module; + uint32_t q_type; + uint32_t q_max_len; +}; + +struct adsp_rtos_mp_mtoa_init_info_type { + uint32_t image_count; + uint32_t num_queue_offsets; + struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX]; + uint32_t num_task_module_entries; + uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX]; + + uint32_t module_table_size; +#if CONFIG_ADSP_RPC_VER > 0x30001 + uint32_t module_entries[MODULES_MAX]; +#else + uint32_t module_entries[ENTRIES_MAX]; +#endif + uint32_t mod_to_q_entries; + struct mod_to_queue_offsets mod_to_q_tbl[ENTRIES_MAX]; + /* + * queue_offsets[] is to store only queue_offsets + */ + uint32_t queue_offsets[IMG_MAX][QUEUES_MAX]; +}; + +struct adsp_rtos_mp_mtoa_s_type { + struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header; +#if CONFIG_ADSP_RPC_VER == 0x30001 + uint32_t desc_field; +#endif + union { + struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet; + struct adsp_rtos_mp_mtoa_type mp_mtoa_packet; + } adsp_rtos_mp_mtoa_data; +}; + +struct rpc_adsp_rtos_modem_to_app_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + struct adsp_rtos_mp_mtoa_s_type mtoa_pkt; +}; + +#define ADSP_STATE_DISABLED 0 +#define ADSP_STATE_ENABLING 1 +#define ADSP_STATE_ENABLED 2 +#define ADSP_STATE_DISABLING 3 +#define ADSP_STATE_INIT_INFO 4 + +struct msm_adsp_module { + struct mutex lock; + const char *name; + unsigned id; + struct adsp_info *info; + + struct msm_rpc_endpoint *rpc_client; + struct msm_adsp_ops *ops; + void *driver_data; + + /* statistics */ + unsigned num_commands; + unsigned num_events; + + wait_queue_head_t state_wait; + unsigned state; + + struct platform_device pdev; + struct clk *clk; + int open_count; + + struct mutex pmem_regions_lock; + struct hlist_head pmem_regions; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); +extern int adsp_init_info(struct adsp_info *info); +extern void rmtask_init(void); + +/* Value to indicate that a queue is not defined for a particular image */ +#define QDSP_RTOS_NO_QUEUE 0xfffffffe + +/* + * Constants used to communicate with the ADSP RTOS + */ +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU + +/* Combination of MUTEX and CMD bits to check if the DSP is busy */ +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U + +/* RTOS to Host processor command mask values */ +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U + +/* Combination of FLAG and COMMAND bits to check if MSG ready */ +#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U +#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U +#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U +#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U + +#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U + +#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U + +#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU + +#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU +#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U + +/* Base address of DSP and DSP hardware registers */ +#define QDSP_RAMC_OFFSET 0x400000 + +#endif diff --git a/arch/arm/mach-msm/qdsp5/adsp_6210.c b/arch/arm/mach-msm/qdsp5/adsp_6210.c new file mode 100644 index 00000000000..322ba68cb36 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_6210.c @@ -0,0 +1,283 @@ +/* arch/arm/mach-msm/qdsp5/adsp_6210.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adsp.h" + +/* Firmware modules */ +typedef enum { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEO_AAC_VOC, + QDSP_MODULE_PCM_DEC, + QDSP_MODULE_AUDIO_DEC_MP3, + QDSP_MODULE_AUDIO_DEC_AAC, + QDSP_MODULE_AUDIO_DEC_WMA, + QDSP_MODULE_HOSTPCM, + QDSP_MODULE_DTMF, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_SBC_ENC, + QDSP_MODULE_VOC, + QDSP_MODULE_VOC_PCM, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_WAV_ENC, + QDSP_MODULE_AACLC_ENC, + QDSP_MODULE_VIDEO_AMR, + QDSP_MODULE_VOC_AMR, + QDSP_MODULE_VOC_EVRC, + QDSP_MODULE_VOC_13K, + QDSP_MODULE_VOC_FGV, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_QCAMTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MIDI, + QDSP_MODULE_GAUDIO, + QDSP_MODULE_VDEC_LP_MODE, + QDSP_MODULE_MAX, +} qdsp_module_type; + +#define QDSP_RTOS_MAX_TASK_ID 19U + +/* Table of modules indexed by task ID for the GAUDIO image */ +static qdsp_module_type qdsp_gaudio_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the GAUDIO image */ +static uint32_t qdsp_gaudio_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3be, /* QDSP_mpuAfeQueue */ + 0x3ee, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x3c2, /* QDSP_uPAudPPCmd1Queue */ + 0x3c6, /* QDSP_uPAudPPCmd2Queue */ + 0x3ca, /* QDSP_uPAudPPCmd3Queue */ + 0x3da, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x3de, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + 0x3e2, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + 0x3e6, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + 0x3ea, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x3ce, /* QDSP_uPAudPreProcCmdQueue */ + 0x3d6, /* QDSP_uPAudRecBitStreamQueue */ + 0x3d2, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the COMBO image */ +static qdsp_module_type qdsp_combo_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the COMBO image */ +static uint32_t qdsp_combo_queue_offset_table[] = { + 0x585, /* QDSP_lpmCommandQueue */ + 0x52d, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + 0x541, /* QDSP_mpuModmathCmdQueue */ + 0x555, /* QDSP_mpuVDecCmdQueue */ + 0x559, /* QDSP_mpuVDecPktQueue */ + 0x551, /* QDSP_mpuVEncCmdQueue */ + 0x535, /* QDSP_rxMpuDecCmdQueue */ + 0x539, /* QDSP_rxMpuDecPktQueue */ + 0x53d, /* QDSP_txMpuEncQueue */ + 0x55d, /* QDSP_uPAudPPCmd1Queue */ + 0x561, /* QDSP_uPAudPPCmd2Queue */ + 0x565, /* QDSP_uPAudPPCmd3Queue */ + 0x575, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x579, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x569, /* QDSP_uPAudPreProcCmdQueue */ + 0x571, /* QDSP_uPAudRecBitStreamQueue */ + 0x56d, /* QDSP_uPAudRecCmdQueue */ + 0x581, /* QDSP_uPJpegActionCmdQueue */ + 0x57d, /* QDSP_uPJpegCfgCmdQueue */ + 0x531, /* QDSP_uPVocProcQueue */ + 0x545, /* QDSP_vfeCommandQueue */ + 0x54d, /* QDSP_vfeCommandScaleQueue */ + 0x549 /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the QTV_LP image */ +static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the QTV_LP image */ +static uint32_t qdsp_qtv_lp_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x40c, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + 0x410, /* QDSP_mpuVDecCmdQueue */ + 0x414, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x41c, /* QDSP_uPAudPPCmd1Queue */ + 0x420, /* QDSP_uPAudPPCmd2Queue */ + 0x424, /* QDSP_uPAudPPCmd3Queue */ + 0x430, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x418, /* QDSP_uPAudPreProcCmdQueue */ + 0x42c, /* QDSP_uPAudRecBitStreamQueue */ + 0x428, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Tables to convert tasks to modules */ +static uint32_t *qdsp_task_to_module[] = { + qdsp_combo_task_to_module_table, + qdsp_gaudio_task_to_module_table, + qdsp_qtv_lp_task_to_module_table, +}; + +/* Tables to retrieve queue offsets */ +static uint32_t *qdsp_queue_offset_table[] = { + qdsp_combo_queue_offset_table, + qdsp_gaudio_queue_offset_table, + qdsp_qtv_lp_queue_offset_table, +}; + +#define QDSP_MODULE(n) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPPTASK), + QDSP_MODULE(AUDRECTASK), + QDSP_MODULE(AUDPREPROCTASK), + QDSP_MODULE(VFETASK), + QDSP_MODULE(QCAMTASK), + QDSP_MODULE(LPMTASK), + QDSP_MODULE(JPEGTASK), + QDSP_MODULE(VIDEOTASK), + QDSP_MODULE(VDEC_LP_MODE), +}; + +int adsp_init_info(struct adsp_info *info) +{ + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + + info->max_task_id = 16; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_QUEUE_MAX; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_6220.c b/arch/arm/mach-msm/qdsp5/adsp_6220.c new file mode 100644 index 00000000000..f947cd7a86d --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_6220.c @@ -0,0 +1,284 @@ +/* arch/arm/mach-msm/qdsp5/adsp_6220.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adsp.h" + +/* Firmware modules */ +typedef enum { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEO_AAC_VOC, + QDSP_MODULE_PCM_DEC, + QDSP_MODULE_AUDIO_DEC_MP3, + QDSP_MODULE_AUDIO_DEC_AAC, + QDSP_MODULE_AUDIO_DEC_WMA, + QDSP_MODULE_HOSTPCM, + QDSP_MODULE_DTMF, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_SBC_ENC, + QDSP_MODULE_VOC, + QDSP_MODULE_VOC_PCM, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_WAV_ENC, + QDSP_MODULE_AACLC_ENC, + QDSP_MODULE_VIDEO_AMR, + QDSP_MODULE_VOC_AMR, + QDSP_MODULE_VOC_EVRC, + QDSP_MODULE_VOC_13K, + QDSP_MODULE_VOC_FGV, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_QCAMTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MIDI, + QDSP_MODULE_GAUDIO, + QDSP_MODULE_VDEC_LP_MODE, + QDSP_MODULE_MAX, +} qdsp_module_type; + +#define QDSP_RTOS_MAX_TASK_ID 19U + +/* Table of modules indexed by task ID for the GAUDIO image */ +static qdsp_module_type qdsp_gaudio_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the GAUDIO image */ +static uint32_t qdsp_gaudio_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3f0, /* QDSP_mpuAfeQueue */ + 0x420, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x3f4, /* QDSP_uPAudPPCmd1Queue */ + 0x3f8, /* QDSP_uPAudPPCmd2Queue */ + 0x3fc, /* QDSP_uPAudPPCmd3Queue */ + 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x400, /* QDSP_uPAudPreProcCmdQueue */ + 0x408, /* QDSP_uPAudRecBitStreamQueue */ + 0x404, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the COMBO image */ +static qdsp_module_type qdsp_combo_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the COMBO image */ +static uint32_t qdsp_combo_queue_offset_table[] = { + 0x6f2, /* QDSP_lpmCommandQueue */ + 0x69e, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + 0x6b2, /* QDSP_mpuModmathCmdQueue */ + 0x6c6, /* QDSP_mpuVDecCmdQueue */ + 0x6ca, /* QDSP_mpuVDecPktQueue */ + 0x6c2, /* QDSP_mpuVEncCmdQueue */ + 0x6a6, /* QDSP_rxMpuDecCmdQueue */ + 0x6aa, /* QDSP_rxMpuDecPktQueue */ + 0x6ae, /* QDSP_txMpuEncQueue */ + 0x6ce, /* QDSP_uPAudPPCmd1Queue */ + 0x6d2, /* QDSP_uPAudPPCmd2Queue */ + 0x6d6, /* QDSP_uPAudPPCmd3Queue */ + 0x6e6, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x6da, /* QDSP_uPAudPreProcCmdQueue */ + 0x6e2, /* QDSP_uPAudRecBitStreamQueue */ + 0x6de, /* QDSP_uPAudRecCmdQueue */ + 0x6ee, /* QDSP_uPJpegActionCmdQueue */ + 0x6ea, /* QDSP_uPJpegCfgCmdQueue */ + 0x6a2, /* QDSP_uPVocProcQueue */ + 0x6b6, /* QDSP_vfeCommandQueue */ + 0x6be, /* QDSP_vfeCommandScaleQueue */ + 0x6ba /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the QTV_LP image */ +static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the QTV_LP image */ +static uint32_t qdsp_qtv_lp_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x430, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + 0x434, /* QDSP_mpuVDecCmdQueue */ + 0x438, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x440, /* QDSP_uPAudPPCmd1Queue */ + 0x444, /* QDSP_uPAudPPCmd2Queue */ + 0x448, /* QDSP_uPAudPPCmd3Queue */ + 0x454, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x43c, /* QDSP_uPAudPreProcCmdQueue */ + 0x450, /* QDSP_uPAudRecBitStreamQueue */ + 0x44c, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Tables to convert tasks to modules */ +static qdsp_module_type *qdsp_task_to_module[] = { + qdsp_combo_task_to_module_table, + qdsp_gaudio_task_to_module_table, + qdsp_qtv_lp_task_to_module_table, +}; + +/* Tables to retrieve queue offsets */ +static uint32_t *qdsp_queue_offset_table[] = { + qdsp_combo_queue_offset_table, + qdsp_gaudio_queue_offset_table, + qdsp_qtv_lp_queue_offset_table, +}; + +#define QDSP_MODULE(n) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK), + QDSP_MODULE(AUDPPTASK), + QDSP_MODULE(AUDPREPROCTASK), + QDSP_MODULE(AUDRECTASK), + QDSP_MODULE(VFETASK), + QDSP_MODULE(QCAMTASK), + QDSP_MODULE(LPMTASK), + QDSP_MODULE(JPEGTASK), + QDSP_MODULE(VIDEOTASK), + QDSP_MODULE(VDEC_LP_MODE), +}; + +int adsp_init_info(struct adsp_info *info) +{ + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + + info->max_task_id = 16; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_QUEUE_MAX; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_6225.c b/arch/arm/mach-msm/qdsp5/adsp_6225.c new file mode 100644 index 00000000000..6f8d3f43bf2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_6225.c @@ -0,0 +1,328 @@ +/* arch/arm/mach-msm/qdsp5/adsp_6225.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adsp.h" + +/* Firmware modules */ +typedef enum { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEO_AAC_VOC, + QDSP_MODULE_PCM_DEC, + QDSP_MODULE_AUDIO_DEC_MP3, + QDSP_MODULE_AUDIO_DEC_AAC, + QDSP_MODULE_AUDIO_DEC_WMA, + QDSP_MODULE_HOSTPCM, + QDSP_MODULE_DTMF, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_SBC_ENC, + QDSP_MODULE_VOC_UMTS, + QDSP_MODULE_VOC_CDMA, + QDSP_MODULE_VOC_PCM, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_WAV_ENC, + QDSP_MODULE_AACLC_ENC, + QDSP_MODULE_VIDEO_AMR, + QDSP_MODULE_VOC_AMR, + QDSP_MODULE_VOC_EVRC, + QDSP_MODULE_VOC_13K, + QDSP_MODULE_VOC_FGV, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_QCAMTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MIDI, + QDSP_MODULE_GAUDIO, + QDSP_MODULE_VDEC_LP_MODE, + QDSP_MODULE_MAX, +} qdsp_module_type; + +#define QDSP_RTOS_MAX_TASK_ID 30U + +/* Table of modules indexed by task ID for the GAUDIO image */ +static qdsp_module_type qdsp_gaudio_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, +}; + +/* Queue offset table indexed by queue ID for the GAUDIO image */ +static uint32_t qdsp_gaudio_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3f0, /* QDSP_mpuAfeQueue */ + 0x420, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x3f4, /* QDSP_uPAudPPCmd1Queue */ + 0x3f8, /* QDSP_uPAudPPCmd2Queue */ + 0x3fc, /* QDSP_uPAudPPCmd3Queue */ + 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x400, /* QDSP_uPAudPreProcCmdQueue */ + 0x408, /* QDSP_uPAudRecBitStreamQueue */ + 0x404, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */ +}; + +/* Table of modules indexed by task ID for the COMBO image */ +static qdsp_module_type qdsp_combo_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_MAX, +}; + +/* Queue offset table indexed by queue ID for the COMBO image */ +static uint32_t qdsp_combo_queue_offset_table[] = { + 0x714, /* QDSP_lpmCommandQueue */ + 0x6bc, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + 0x6d0, /* QDSP_mpuModmathCmdQueue */ + 0x6e8, /* QDSP_mpuVDecCmdQueue */ + 0x6ec, /* QDSP_mpuVDecPktQueue */ + 0x6e4, /* QDSP_mpuVEncCmdQueue */ + 0x6c4, /* QDSP_rxMpuDecCmdQueue */ + 0x6c8, /* QDSP_rxMpuDecPktQueue */ + 0x6cc, /* QDSP_txMpuEncQueue */ + 0x6f0, /* QDSP_uPAudPPCmd1Queue */ + 0x6f4, /* QDSP_uPAudPPCmd2Queue */ + 0x6f8, /* QDSP_uPAudPPCmd3Queue */ + 0x708, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x6fc, /* QDSP_uPAudPreProcCmdQueue */ + 0x704, /* QDSP_uPAudRecBitStreamQueue */ + 0x700, /* QDSP_uPAudRecCmdQueue */ + 0x710, /* QDSP_uPJpegActionCmdQueue */ + 0x70c, /* QDSP_uPJpegCfgCmdQueue */ + 0x6c0, /* QDSP_uPVocProcQueue */ + 0x6d8, /* QDSP_vfeCommandQueue */ + 0x6e0, /* QDSP_vfeCommandScaleQueue */ + 0x6dc, /* QDSP_vfeCommandTableQueue */ + 0x6d4, /* QDSP_uPDiagQueue */ +}; + +/* Table of modules indexed by task ID for the QTV_LP image */ +static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, +}; + +/* Queue offset table indexed by queue ID for the QTV_LP image */ +static uint32_t qdsp_qtv_lp_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3fe, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + 0x402, /* QDSP_mpuVDecCmdQueue */ + 0x406, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x40e, /* QDSP_uPAudPPCmd1Queue */ + 0x412, /* QDSP_uPAudPPCmd2Queue */ + 0x416, /* QDSP_uPAudPPCmd3Queue */ + 0x422, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x40a, /* QDSP_uPAudPreProcCmdQueue */ + 0x41e, /* QDSP_uPAudRecBitStreamQueue */ + 0x41a, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */ +}; + +/* Tables to convert tasks to modules */ +static qdsp_module_type *qdsp_task_to_module[] = { + qdsp_combo_task_to_module_table, + qdsp_gaudio_task_to_module_table, + qdsp_qtv_lp_task_to_module_table, +}; + +/* Tables to retrieve queue offsets */ +static uint32_t *qdsp_queue_offset_table[] = { + qdsp_combo_queue_offset_table, + qdsp_gaudio_queue_offset_table, + qdsp_qtv_lp_queue_offset_table, +}; + +#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \ + .clk_name = clkname, .clk_rate = clkrate, \ + .verify_cmd = verify_cmd_func, .patch_event = patch_event_func } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd, + adsp_vfe_patch_event), + QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL), + QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd, + adsp_jpeg_patch_event), + QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000, + adsp_video_verify_cmd, NULL), + QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL), + QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000, + adsp_videoenc_verify_cmd, NULL), +}; + +int adsp_init_info(struct adsp_info *info) +{ + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + + info->max_task_id = 16; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_QUEUE_MAX; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_debug.c b/arch/arm/mach-msm/qdsp5/adsp_debug.c new file mode 100644 index 00000000000..03deab9f158 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_debug.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "adsp.h" + +#define MAX_LEN 64 +#ifdef CONFIG_DEBUG_FS +static struct dentry *adsp_dentry; +#endif +static char l_buf[MAX_LEN]; +static unsigned int crash_enable; + +static ssize_t q5_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_DBG("q5 debugfs opened\n"); + return 0; +} + +static ssize_t q5_debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int len; + + if (count < 0) + return 0; + len = count > (MAX_LEN - 1) ? (MAX_LEN - 1) : count; + if (copy_from_user(l_buf, buf, len)) { + MM_INFO("Unable to copy data from user space\n"); + return -EFAULT; + } + l_buf[len] = 0; + if (l_buf[len - 1] == '\n') { + l_buf[len - 1] = 0; + len--; + } + if (!strncmp(l_buf, "boom", MAX_LEN)) { + q5audio_dsp_not_responding(); + } else if (!strncmp(l_buf, "enable", MAX_LEN)) { + crash_enable = 1; + MM_INFO("Crash enabled : %d\n", crash_enable); + } else if (!strncmp(l_buf, "disable", MAX_LEN)) { + crash_enable = 0; + MM_INFO("Crash disabled : %d\n", crash_enable); + } else + MM_INFO("Unknown Command\n"); + + return count; +} + +static const struct file_operations q5_debug_fops = { + .write = q5_debug_write, + .open = q5_debug_open, +}; + +static int __init q5_debug_init(void) +{ +#ifdef CONFIG_DEBUG_FS + adsp_dentry = debugfs_create_file("q5_debug", S_IFREG | S_IRUGO, + NULL, (void *) NULL, &q5_debug_fops); +#endif /* CONFIG_DEBUG_FS */ + return 0; +} +device_initcall(q5_debug_init); + diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c new file mode 100644 index 00000000000..6860d84f446 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c @@ -0,0 +1,668 @@ +/* arch/arm/mach-msm/qdsp5/adsp_driver.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adsp.h" + +#include +#include +#include + +struct adsp_pmem_info { + int fd; + void *vaddr; +}; + +struct adsp_pmem_region { + struct hlist_node list; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + struct file *file; +}; + +struct adsp_device { + struct msm_adsp_module *module; + + spinlock_t event_queue_lock; + wait_queue_head_t event_wait; + struct list_head event_queue; + int abort; + + const char *name; + struct device *device; + struct cdev cdev; +}; + +static struct adsp_device *inode_to_device(struct inode *inode); + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = __v >= __r->vaddr && \ + __e <= __r->vaddr + __r->len; \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +static int adsp_pmem_check(struct msm_adsp_module *module, + void *vaddr, unsigned long len) +{ + struct adsp_pmem_region *region_elt; + struct hlist_node *node; + struct adsp_pmem_region t = { .vaddr = vaddr, .len = len }; + + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("module %s:" + " region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + module->name, + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int adsp_pmem_add(struct msm_adsp_module *module, + struct adsp_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct adsp_pmem_region *region; + int rc = -EINVAL; + + mutex_lock(&module->pmem_regions_lock); + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) { + rc = -ENOMEM; + goto end; + } + INIT_HLIST_NODE(®ion->list); + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = adsp_pmem_check(module, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + + hlist_add_head(®ion->list, &module->pmem_regions); +end: + mutex_unlock(&module->pmem_regions_lock); + return rc; +} + +static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr, + unsigned long len, struct adsp_pmem_region **region) +{ + struct hlist_node *node; + void *vaddr = *addr; + struct adsp_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (vaddr >= region_elt->vaddr && + vaddr < region_elt->vaddr + region_elt->len && + vaddr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("module %s: " + "multiple hits for vaddr %p, len %ld\n", + module->name, vaddr, len); + hlist_for_each_entry(region_elt, node, + &module->pmem_regions, list) { + if (vaddr >= region_elt->vaddr && + vaddr < region_elt->vaddr + region_elt->len && + vaddr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("%p, %ld --> %p\n", + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len, + struct file **filp, unsigned long *offset) +{ + struct adsp_pmem_region *region; + void *vaddr = *addr; + unsigned long *paddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion); + if (ret) { + MM_ERR("not patching %s (paddr & kvaddr)," + " lookup (%p, %ld) failed\n", + module->name, vaddr, len); + return ret; + } + *paddr = region->paddr + (vaddr - region->vaddr); + *kvaddr = region->kvaddr + (vaddr - region->vaddr); + if (filp) + *filp = region->file; + if (offset) + *offset = vaddr - region->vaddr; + return 0; +} + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len) +{ + struct adsp_pmem_region *region; + void *vaddr = *addr; + unsigned long *paddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion); + if (ret) { + MM_ERR("not patching %s, lookup (%p, %ld) failed\n", + module->name, vaddr, len); + return ret; + } + + *paddr = region->paddr + (vaddr - region->vaddr); + return 0; +} + +static int adsp_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + /* call the per module verifier */ + if (module->verify_cmd) + return module->verify_cmd(module, queue_id, cmd_data, + cmd_size); + else + MM_INFO("no packet verifying function " + "for task %s\n", module->name); + return 0; +} + +static long adsp_write_cmd(struct adsp_device *adev, void __user *arg) +{ + struct adsp_command_t cmd; + unsigned char buf[256]; + void *cmd_data; + long rc; + + if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) + return -EFAULT; + + if (cmd.len > 256) { + cmd_data = kmalloc(cmd.len, GFP_USER); + if (!cmd_data) + return -ENOMEM; + } else { + cmd_data = buf; + } + + if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) { + rc = -EFAULT; + goto end; + } + + mutex_lock(&adev->module->pmem_regions_lock); + if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) { + MM_ERR("module %s: verify failed.\n", adev->module->name); + rc = -EINVAL; + goto end; + } + /* complete the writes to the buffer */ + wmb(); + rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len); +end: + mutex_unlock(&adev->module->pmem_regions_lock); + + if (cmd.len > 256) + kfree(cmd_data); + + return rc; +} + +static int adsp_events_pending(struct adsp_device *adev) +{ + unsigned long flags; + int yes; + spin_lock_irqsave(&adev->event_queue_lock, flags); + yes = !list_empty(&adev->event_queue); + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + return yes || adev->abort; +} + +static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr, + struct adsp_pmem_region **region) +{ + struct hlist_node *node; + unsigned long paddr = (unsigned long)(*addr); + struct adsp_pmem_region *region_elt; + + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (paddr >= region_elt->paddr && + paddr < region_elt->paddr + region_elt->len) { + *region = region_elt; + return 0; + } + } + return -1; +} + +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr) +{ + struct adsp_pmem_region *region; + unsigned long paddr = (unsigned long)(*addr); + unsigned long *vaddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_paddr(module, addr, ®ion); + if (ret) { + MM_ERR("not patching %s, paddr %p lookup failed\n", + module->name, vaddr); + return ret; + } + + *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr); + return 0; +} + +static int adsp_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + /* call the per-module msg verifier */ + if (module->patch_event) + return module->patch_event(module, event); + return 0; +} + +static long adsp_get_event(struct adsp_device *adev, void __user *arg) +{ + unsigned long flags; + struct adsp_event *data = NULL; + struct adsp_event_t evt; + int timeout; + long rc = 0; + + if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t))) + return -EFAULT; + + timeout = (int)evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + adev->event_wait, adsp_events_pending(adev), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + adev->event_wait, adsp_events_pending(adev)); + } + if (rc < 0) + return rc; + + if (adev->abort) + return -ENODEV; + + spin_lock_irqsave(&adev->event_queue_lock, flags); + if (!list_empty(&adev->event_queue)) { + data = list_first_entry(&adev->event_queue, + struct adsp_event, list); + list_del(&data->list); + } + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + + if (!data) + return -EAGAIN; + + /* DSP messages are type 0; they may contain physical addresses */ + if (data->type == 0) + adsp_patch_event(adev->module, data); + + /* map adsp_event --> adsp_event_t */ + if (evt.len < data->size) { + rc = -ETOOSMALL; + goto end; + } + /* order the reads to the buffer */ + rmb(); + if (data->msg_id != EVENT_MSG_ID) { + if (copy_to_user((void *)(evt.data), data->data.msg16, + data->size)) { + rc = -EFAULT; + goto end; + } + } else { + if (copy_to_user((void *)(evt.data), data->data.msg32, + data->size)) { + rc = -EFAULT; + goto end; + } + } + + evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */ + evt.msg_id = data->msg_id; + evt.flags = data->is16; + evt.len = data->size; + if (copy_to_user(arg, &evt, sizeof(evt))) + rc = -EFAULT; +end: + kfree(data); + return rc; +} + +static int adsp_pmem_del(struct msm_adsp_module *module) +{ + struct hlist_node *node, *tmp; + struct adsp_pmem_region *region; + + mutex_lock(&module->pmem_regions_lock); + hlist_for_each_safe(node, tmp, &module->pmem_regions) { + region = hlist_entry(node, struct adsp_pmem_region, list); + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + mutex_unlock(&module->pmem_regions_lock); + BUG_ON(!hlist_empty(&module->pmem_regions)); + + return 0; +} + +static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct adsp_device *adev = filp->private_data; + + switch (cmd) { + case ADSP_IOCTL_ENABLE: + return msm_adsp_enable(adev->module); + + case ADSP_IOCTL_DISABLE: + return msm_adsp_disable(adev->module); + + case ADSP_IOCTL_DISABLE_EVENT_RSP: + return msm_adsp_disable_event_rsp(adev->module); + + case ADSP_IOCTL_DISABLE_ACK: + MM_ERR("ADSP_IOCTL_DISABLE_ACK is not implemented\n"); + break; + + case ADSP_IOCTL_WRITE_COMMAND: + return adsp_write_cmd(adev, (void __user *) arg); + + case ADSP_IOCTL_GET_EVENT: + return adsp_get_event(adev, (void __user *) arg); + + case ADSP_IOCTL_SET_CLKRATE: { + unsigned long clk_rate; + if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate))) + return -EFAULT; + return adsp_set_clkrate(adev->module, clk_rate); + } + + case ADSP_IOCTL_REGISTER_PMEM: { + struct adsp_pmem_info info; + if (copy_from_user(&info, (void *) arg, sizeof(info))) + return -EFAULT; + return adsp_pmem_add(adev->module, &info); + } + + case ADSP_IOCTL_ABORT_EVENT_READ: + adev->abort = 1; + wake_up(&adev->event_wait); + break; + + case ADSP_IOCTL_UNREGISTER_PMEM: + return adsp_pmem_del(adev->module); + + default: + break; + } + return -EINVAL; +} + +static int adsp_release(struct inode *inode, struct file *filp) +{ + struct adsp_device *adev = filp->private_data; + struct msm_adsp_module *module = adev->module; + int rc = 0; + + MM_INFO("release '%s'\n", adev->name); + + /* clear module before putting it to avoid race with open() */ + adev->module = NULL; + + rc = adsp_pmem_del(module); + + msm_adsp_put(module); + return rc; +} + +static void adsp_event(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct adsp_device *adev = driver_data; + struct adsp_event *event; + unsigned long flags; + + if (len > ADSP_EVENT_MAX_SIZE) { + MM_ERR("event too large (%d bytes)\n", len); + return; + } + + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) { + MM_ERR("cannot allocate buffer\n"); + return; + } + + if (id != EVENT_MSG_ID) { + event->type = 0; + event->is16 = 0; + event->msg_id = id; + event->size = len; + + getevent(event->data.msg16, len); + } else { + event->type = 1; + event->is16 = 1; + event->msg_id = id; + event->size = len; + getevent(event->data.msg32, len); + } + + spin_lock_irqsave(&adev->event_queue_lock, flags); + list_add_tail(&event->list, &adev->event_queue); + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + wake_up(&adev->event_wait); +} + +static struct msm_adsp_ops adsp_ops = { + .event = adsp_event, +}; + +static int adsp_open(struct inode *inode, struct file *filp) +{ + struct adsp_device *adev; + int rc; + + rc = nonseekable_open(inode, filp); + if (rc < 0) + return rc; + + adev = inode_to_device(inode); + if (!adev) + return -ENODEV; + + MM_INFO("open '%s'\n", adev->name); + + rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev); + if (rc) + return rc; + + MM_INFO("opened module '%s' adev %p\n", adev->name, adev); + filp->private_data = adev; + adev->abort = 0; + INIT_HLIST_HEAD(&adev->module->pmem_regions); + mutex_init(&adev->module->pmem_regions_lock); + + return 0; +} + +static unsigned adsp_device_count; +static struct adsp_device *adsp_devices; + +static struct adsp_device *inode_to_device(struct inode *inode) +{ + unsigned n = MINOR(inode->i_rdev); + if (n < adsp_device_count) { + if (adsp_devices[n].device) + return adsp_devices + n; + } + return NULL; +} + +static dev_t adsp_devno; +static struct class *adsp_class; + +static struct file_operations adsp_fops = { + .owner = THIS_MODULE, + .open = adsp_open, + .unlocked_ioctl = adsp_ioctl, + .release = adsp_release, +}; + +static void adsp_create(struct adsp_device *adev, const char *name, + struct device *parent, dev_t devt) +{ + struct device *dev; + int rc; + + dev = device_create(adsp_class, parent, devt, "%s", name); + if (IS_ERR(dev)) + return; + + init_waitqueue_head(&adev->event_wait); + INIT_LIST_HEAD(&adev->event_queue); + spin_lock_init(&adev->event_queue_lock); + + cdev_init(&adev->cdev, &adsp_fops); + adev->cdev.owner = THIS_MODULE; + + rc = cdev_add(&adev->cdev, devt, 1); + if (rc < 0) { + device_destroy(adsp_class, devt); + } else { + adev->device = dev; + adev->name = name; + } +} + +void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n) +{ + int rc; + + adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL); + if (!adsp_devices) + return; + + adsp_class = class_create(THIS_MODULE, "adsp"); + if (IS_ERR(adsp_class)) + goto fail_create_class; + + rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp"); + if (rc < 0) + goto fail_alloc_region; + + adsp_device_count = n; + for (n = 0; n < adsp_device_count; n++) { + adsp_create(adsp_devices + n, + modules[n].name, &modules[n].pdev.dev, + MKDEV(MAJOR(adsp_devno), n)); + } + + return; + +fail_alloc_region: + class_unregister(adsp_class); +fail_create_class: + kfree(adsp_devices); +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_info.c b/arch/arm/mach-msm/qdsp5/adsp_info.c new file mode 100644 index 00000000000..dea52bbf367 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_info.c @@ -0,0 +1,144 @@ +/* arch/arm/mach-msm/adsp_info.c + * + * Copyright (c) 2008-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adsp.h" + +/* Firmware modules */ +#define QDSP_MODULE_KERNEL 0x0106dd4e +#define QDSP_MODULE_AFETASK 0x0106dd6f +#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70 +#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71 +#define QDSP_MODULE_AUDPPTASK 0x0106dd72 +#define QDSP_MODULE_VIDEOTASK 0x0106dd73 +#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74 +#define QDSP_MODULE_PCM_DEC 0x0106dd75 +#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76 +#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77 +#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78 +#define QDSP_MODULE_HOSTPCM 0x0106dd79 +#define QDSP_MODULE_DTMF 0x0106dd7a +#define QDSP_MODULE_AUDRECTASK 0x0106dd7b +#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c +#define QDSP_MODULE_SBC_ENC 0x0106dd7d +#define QDSP_MODULE_VOC_UMTS 0x0106dd9a +#define QDSP_MODULE_VOC_CDMA 0x0106dd98 +#define QDSP_MODULE_VOC_PCM 0x0106dd7f +#define QDSP_MODULE_VOCENCTASK 0x0106dd80 +#define QDSP_MODULE_VOCDECTASK 0x0106dd81 +#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82 +#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83 +#define QDSP_MODULE_VFETASK 0x0106dd84 +#define QDSP_MODULE_WAV_ENC 0x0106dd85 +#define QDSP_MODULE_AACLC_ENC 0x0106dd86 +#define QDSP_MODULE_VIDEO_AMR 0x0106dd87 +#define QDSP_MODULE_VOC_AMR 0x0106dd88 +#define QDSP_MODULE_VOC_EVRC 0x0106dd89 +#define QDSP_MODULE_VOC_13K 0x0106dd8a +#define QDSP_MODULE_VOC_FGV 0x0106dd8b +#define QDSP_MODULE_DIAGTASK 0x0106dd8c +#define QDSP_MODULE_JPEGTASK 0x0106dd8d +#define QDSP_MODULE_LPMTASK 0x0106dd8e +#define QDSP_MODULE_QCAMTASK 0x0106dd8f +#define QDSP_MODULE_MODMATHTASK 0x0106dd90 +#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91 +#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92 +#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93 +#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94 +#define QDSP_MODULE_MIDI 0x0106dd95 +#define QDSP_MODULE_GAUDIO 0x0106dd96 +#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97 +#define QDSP_MODULE_VIDEO_AAC_VOC_TURBO 0x01089f77 +#define QDSP_MODULE_VIDEO_AMR_TURBO 0x01089f78 +#define QDSP_MODULE_WM_TURBO_MODE 0x01089f79 +#define QDSP_MODULE_VDEC_LP_MODE_TURBO 0x01089f7a +#define QDSP_MODULE_AUDREC0TASK 0x0109696f +#define QDSP_MODULE_AUDREC1TASK 0x01096970 +#define QDSP_MODULE_RMTASK 0x01090f8e +#define QDSP_MODULE_MAX 0x7fffffff + + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ +#define QDSP_MODULE_32BIT_DUMMY 0x10000 + +static uint32_t *qdsp_task_to_module[IMG_MAX]; +static uint32_t *qdsp_queue_offset_table[IMG_MAX]; + +#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \ + .clk_name = clkname, .clk_rate = clkrate, \ + .verify_cmd = verify_cmd_func, .patch_event = patch_event_func } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPLAY1TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPLAY2TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPLAY3TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(RMTASK, NULL, 0, NULL, NULL), +#if !defined(CONFIG_ARCH_MSM7X30) + QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(VFETASK, NULL, 0, adsp_vfe_verify_cmd, + adsp_vfe_patch_event), + QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL), + QDSP_MODULE(JPEGTASK, "vdc_clk", 96000000, adsp_jpeg_verify_cmd, + adsp_jpeg_patch_event), + QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000, + adsp_video_verify_cmd, NULL), + QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL), + QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000, + adsp_videoenc_verify_cmd, NULL), + QDSP_MODULE(VIDEO_AAC_VOC_TURBO, NULL, 0, NULL, NULL), + QDSP_MODULE(VIDEO_AMR_TURBO, NULL, 0, NULL, NULL), + QDSP_MODULE(WM_TURBO_MODE, NULL, 0, NULL, NULL), + QDSP_MODULE(VDEC_LP_MODE_TURBO, NULL, 0, NULL, NULL), +#if defined(CONFIG_MSM7X27A_AUDIO) + QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL), +#endif +#else + QDSP_MODULE(AFETASK , NULL, 0, NULL, NULL), + QDSP_MODULE(AUDREC0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL), +#endif +}; + +int adsp_init_info(struct adsp_info *info) +{ + uint32_t img_num; + + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + for (img_num = 0; img_num < IMG_MAX; img_num++) + qdsp_queue_offset_table[img_num] = + &info->init_info_ptr->queue_offsets[img_num][0]; + + for (img_num = 0; img_num < IMG_MAX; img_num++) + qdsp_task_to_module[img_num] = + &info->init_info_ptr->task_to_module_tbl[img_num][0]; + info->max_task_id = 30; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_MAX_NUM_QUEUES; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c new file mode 100644 index 00000000000..8fb2e06c79c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c @@ -0,0 +1,39 @@ +/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c + * + * Verification code for aDSP JPEG events. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "adsp.h" + +int adsp_jpeg_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + if (event->msg_id == JPEG_MSG_ENC_OP_PRODUCED) { + jpeg_msg_enc_op_produced *op = (jpeg_msg_enc_op_produced *)event->data.msg16; + return adsp_pmem_paddr_fixup(module, (void **)&op->op_buf_addr); + } + if (event->msg_id == JPEG_MSG_DEC_OP_PRODUCED) { + jpeg_msg_dec_op_produced *op = (jpeg_msg_dec_op_produced *) + event->data.msg16; + return adsp_pmem_paddr_fixup(module, + (void **)&op->luma_op_buf_addr) || + adsp_pmem_paddr_fixup(module, + (void **)&op->chroma_op_buf_addr); + } + + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c new file mode 100644 index 00000000000..87d5dc389df --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c @@ -0,0 +1,201 @@ +/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c + * + * Verification code for aDSP JPEG packets from userspace. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "adsp.h" +#include + +static uint32_t dec_fmt; + +static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size, + uint32_t *chroma_size) +{ + uint32_t fmt, luma_width, luma_height; + + fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M; + luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M) + >> 16; + luma_height = cmd->frag_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M; + *luma_size = luma_width * luma_height; + if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2) + *chroma_size = *luma_size/2; + else + *chroma_size = *luma_size; +} + +static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + jpeg_cmd_enc_cfg *cmd = (jpeg_cmd_enc_cfg *)cmd_data; + uint32_t luma_size, chroma_size; + int i, num_frags; + + if (cmd_size != sizeof(jpeg_cmd_enc_cfg)) { + MM_ERR("module %s: JPEG ENC CFG invalid \ + cmd_size %d\n", module->name, cmd_size); + return -1; + } + + get_sizes(cmd, &luma_size, &chroma_size); + num_frags = (cmd->process_cfg >> 10) & 0xf; + num_frags = ((num_frags == 1) ? num_frags : num_frags * 2); + for (i = 0; i < num_frags; i += 2) { + if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) || + adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size)) + return -1; + } + + if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_0_cfg_part1, + cmd->op_buf_0_cfg_part2) || + adsp_pmem_fixup(module, (void **)&cmd->op_buf_1_cfg_part1, + cmd->op_buf_1_cfg_part2)) + return -1; + return 0; +} + +static inline int verify_jpeg_cmd_dec_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + jpeg_cmd_dec_cfg *cmd = (jpeg_cmd_dec_cfg *)cmd_data; + uint32_t div; + + if (cmd_size != sizeof(jpeg_cmd_dec_cfg)) { + MM_ERR("module %s: JPEG DEC CFG invalid \ + cmd_size %d\n", module->name, cmd_size); + return -1; + } + + if (adsp_pmem_fixup(module, (void **)&cmd->ip_stream_buf_cfg_part1, + cmd->ip_stream_buf_cfg_part2) || + adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part1, + cmd->op_stream_buf_0_cfg_part2) || + adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part1, + cmd->op_stream_buf_1_cfg_part2)) + return -1; + dec_fmt = cmd->op_data_format & + JPEG_CMD_DEC_OP_DATA_FORMAT_M; + div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1; + if (adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part3, + cmd->op_stream_buf_0_cfg_part2 / div) || + adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part3, + cmd->op_stream_buf_1_cfg_part2 / div)) + return -1; + return 0; +} + +static int verify_jpeg_cfg_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + switch(cmd_id) { + case JPEG_CMD_ENC_CFG: + return verify_jpeg_cmd_enc_cfg(module, cmd_data, cmd_size); + case JPEG_CMD_DEC_CFG: + return verify_jpeg_cmd_dec_cfg(module, cmd_data, cmd_size); + default: + if (cmd_id > 1) { + MM_ERR("module %s: invalid JPEG CFG cmd_id %d\n", + module->name, cmd_id); + return -1; + } + } + return 0; +} + +static int verify_jpeg_action_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + switch (cmd_id) { + case JPEG_CMD_ENC_OP_CONSUMED: + { + jpeg_cmd_enc_op_consumed *cmd = + (jpeg_cmd_enc_op_consumed *)cmd_data; + + if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) { + MM_ERR("module %s: JPEG_CMD_ENC_OP_CONSUMED \ + invalid size %d\n", module->name, cmd_size); + return -1; + } + + if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_addr, + cmd->op_buf_size)) + return -1; + } + break; + case JPEG_CMD_DEC_OP_CONSUMED: + { + uint32_t div; + jpeg_cmd_dec_op_consumed *cmd = + (jpeg_cmd_dec_op_consumed *)cmd_data; + + if (cmd_size != sizeof(jpeg_cmd_dec_op_consumed)) { + MM_ERR("module %s: JPEG_CMD_DEC_OP_CONSUMED \ + invalid size %d\n", module->name, cmd_size); + return -1; + } + + div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1; + if (adsp_pmem_fixup(module, (void **)&cmd->luma_op_buf_addr, + cmd->luma_op_buf_size) || + adsp_pmem_fixup(module, (void **)&cmd->chroma_op_buf_addr, + cmd->luma_op_buf_size / div)) + return -1; + } + break; + + case JPEG_CMD_DEC_IP: + { + jpeg_cmd_dec_ip *cmd = + (jpeg_cmd_dec_ip *)cmd_data; + + if (cmd_size != sizeof(jpeg_cmd_dec_ip)) { + MM_ERR("module %s: JPEG_CMD_DEC_IP invalid \ + size %d\n", module->name, cmd_size); + return -1; + } + if (adsp_pmem_fixup(module, (void **)&cmd->ip_buf_addr, + cmd->ip_buf_size)) + return -1; + } + break; + + default: + if (cmd_id > 7) { + MM_ERR("module %s: invalid cmd_id %d\n", + module->name, cmd_id); + return -1; + } + } + return 0; +} + +int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch(queue_id) { + case QDSP_uPJpegCfgCmdQueue: + return verify_jpeg_cfg_cmd(module, cmd_data, cmd_size); + case QDSP_uPJpegActionCmdQueue: + return verify_jpeg_action_cmd(module, cmd_data, cmd_size); + default: + return -1; + } +} + diff --git a/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c new file mode 100644 index 00000000000..06b70de38ed --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c @@ -0,0 +1,66 @@ +/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c + * + * Verificion code for aDSP LPM packets from userspace. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "adsp.h" +#include + +int adsp_lpm_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + uint32_t cmd_id, col_height, input_row_incr, output_row_incr, + input_size, output_size; + uint32_t size_mask = 0x0fff; + lpm_cmd_start *cmd; + + if (queue_id != QDSP_lpmCommandQueue) { + MM_ERR("module %s: wrong queue id %d\n", + module->name, queue_id); + return -1; + } + + cmd = (lpm_cmd_start *)cmd_data; + cmd_id = cmd->cmd_id; + + if (cmd_id == LPM_CMD_START) { + if (cmd_size != sizeof(lpm_cmd_start)) { + MM_ERR("module %s: wrong size %d, \ + expect %d\n", module->name, + cmd_size, sizeof(lpm_cmd_start)); + return -1; + } + col_height = cmd->ip_data_cfg_part1 & size_mask; + input_row_incr = cmd->ip_data_cfg_part2 & size_mask; + output_row_incr = cmd->op_data_cfg_part1 & size_mask; + input_size = col_height * input_row_incr; + output_size = col_height * output_row_incr; + if ((cmd->ip_data_cfg_part4 && adsp_pmem_fixup(module, + (void **)(&cmd->ip_data_cfg_part4), + input_size)) || + (cmd->op_data_cfg_part3 && adsp_pmem_fixup(module, + (void **)(&cmd->op_data_cfg_part3), + output_size))) + return -1; + } else if (cmd_id > 1) { + MM_ERR("module %s: invalid cmd_id %d\n", module->name, cmd_id); + return -1; + } + return 0; +} + diff --git a/arch/arm/mach-msm/qdsp5/adsp_rm.c b/arch/arm/mach-msm/qdsp5/adsp_rm.c new file mode 100644 index 00000000000..81147f700d3 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_rm.c @@ -0,0 +1,193 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "adsp.h" + +#define MAX_CLIENTS 5 +#define MAX_AUDIO_CLIENTS 5 +#define MAX_RM_CLIENTS MAX_AUDIO_CLIENTS + +static char *rm_errs[] = { + "", + "PCM Blocks not Sufficient", + "TASK is already occupied", + "Concurrency not supported", + "MIPS not sufficient" + }; +static struct client { + wait_queue_head_t wait; + unsigned int wait_state; + struct aud_codec_config_ack cfg_msg; +} rmclient[MAX_RM_CLIENTS]; + +static struct rm { + struct msm_adsp_module *mod; + int cnt; + int state; + + struct aud_codec_config_ack cfg_msg; + struct mutex lock; +} rmtask; + +static void rm_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)); +static struct msm_adsp_ops rm_ops = { + .event = rm_dsp_event, +}; + +int32_t get_adsp_resource(unsigned short client_id, + void *cmd_buf, size_t cmd_size) +{ + int rc = 0; + int client_idx; + + client_idx = ((client_id >> 8) * MAX_CLIENTS) + (client_id & 0xFF); + if (client_idx >= MAX_RM_CLIENTS) + return -EINVAL; + + mutex_lock(&rmtask.lock); + if (rmtask.state != ADSP_STATE_ENABLED) { + rc = msm_adsp_get("RMTASK", &rmtask.mod, &rm_ops, NULL); + if (rc) { + MM_ERR("Failed to get module RMTASK\n"); + mutex_unlock(&rmtask.lock); + return rc; + } + rc = msm_adsp_enable(rmtask.mod); + if (rc) { + MM_ERR("RMTASK enable Failed\n"); + msm_adsp_put(rmtask.mod); + mutex_unlock(&rmtask.lock); + return rc; + } + rmtask.state = ADSP_STATE_ENABLED; + } + rmclient[client_idx].wait_state = -1; + mutex_unlock(&rmtask.lock); + msm_adsp_write(rmtask.mod, QDSP_apuRmtQueue, cmd_buf, cmd_size); + rc = wait_event_interruptible_timeout(rmclient[client_idx].wait, + rmclient[client_idx].wait_state != -1, 5 * HZ); + mutex_lock(&rmtask.lock); + if (unlikely(rc < 0)) { + if (rc == -ERESTARTSYS) + MM_ERR("wait_event_interruptible " + "returned -ERESTARTSYS\n"); + else + MM_ERR("wait_event_interruptible " + "returned error\n"); + if (!rmtask.cnt) + goto disable_rm; + goto unlock; + } else if (rc == 0) { + MM_ERR("RMTASK Msg not received\n"); + rc = -ETIMEDOUT; + if (!rmtask.cnt) + goto disable_rm; + goto unlock; + } + if (!(rmclient[client_idx].cfg_msg.enable)) { + MM_ERR("Reason for failure: %s\n", + rm_errs[rmclient[client_idx].cfg_msg.reason]); + rc = -EBUSY; + if (!rmtask.cnt) + goto disable_rm; + goto unlock; + } + rmtask.cnt++; + mutex_unlock(&rmtask.lock); + return 0; + +disable_rm: + msm_adsp_disable(rmtask.mod); + msm_adsp_put(rmtask.mod); + rmtask.state = ADSP_STATE_DISABLED; +unlock: + mutex_unlock(&rmtask.lock); + return rc; +} +EXPORT_SYMBOL(get_adsp_resource); + +int32_t put_adsp_resource(unsigned short client_id, void *cmd_buf, + size_t cmd_size) +{ + mutex_lock(&rmtask.lock); + if (rmtask.state != ADSP_STATE_ENABLED) { + mutex_unlock(&rmtask.lock); + return 0; + } + + msm_adsp_write(rmtask.mod, QDSP_apuRmtQueue, cmd_buf, cmd_size); + rmtask.cnt--; + if (!rmtask.cnt) { + msm_adsp_disable(rmtask.mod); + msm_adsp_put(rmtask.mod); + rmtask.state = ADSP_STATE_DISABLED; + } + mutex_unlock(&rmtask.lock); + return 0; +} +EXPORT_SYMBOL(put_adsp_resource); + +static void rm_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + unsigned short client_id; + int client_idx; + + MM_DBG("Msg ID = %d\n", id); + + switch (id) { + case RMT_CODEC_CONFIG_ACK: { + getevent(&rmtask.cfg_msg, sizeof(rmtask.cfg_msg)); + client_id = ((rmtask.cfg_msg.client_id << 8) | + rmtask.cfg_msg.task_id); + client_idx = ((client_id >> 8) * MAX_CLIENTS) + + (client_id & 0xFF); + memcpy(&rmclient[client_idx].cfg_msg, &rmtask.cfg_msg, + sizeof(rmtask.cfg_msg)); + rmclient[client_idx].wait_state = 1; + wake_up(&rmclient[client_idx].wait); + break; + } + case RMT_DSP_OUT_OF_MIPS: { + struct rmt_dsp_out_of_mips msg; + getevent(&msg, sizeof(msg)); + MM_ERR("RMT_DSP_OUT_OF_MIPS: Not enough resorces in ADSP \ + to handle all sessions :%hx\n", msg.dec_info); + break; + } + default: + MM_DBG("Unknown Msg Id\n"); + break; + } +} + +void rmtask_init(void) +{ + int i; + + for (i = 0; i < MAX_RM_CLIENTS; i++) + init_waitqueue_head(&rmclient[i].wait); + mutex_init(&rmtask.lock); +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c new file mode 100644 index 00000000000..68ae3802ecd --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c @@ -0,0 +1,54 @@ +/* arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c + * + * Verification code for aDSP VFE packets from userspace. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "adsp.h" + +static int patch_op_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + vfe_msg_op1 *op = (vfe_msg_op1 *)event->data.msg16; + if (adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_y_addr) || + adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_cbcr_addr)) + return -1; + return 0; +} + +static int patch_af_wb_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + vfe_msg_stats_wb_exp *af = (vfe_msg_stats_wb_exp *)event->data.msg16; + return adsp_pmem_paddr_fixup(module, (void **)&af->wb_exp_stats_op_buf); +} + +int adsp_vfe_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + switch(event->msg_id) { + case VFE_MSG_OP1: + case VFE_MSG_OP2: + return patch_op_event(module, event); + case VFE_MSG_STATS_AF: + case VFE_MSG_STATS_WB_EXP: + return patch_af_wb_event(module, event); + default: + break; + } + + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c new file mode 100644 index 00000000000..dcd3d968af8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c @@ -0,0 +1,244 @@ +/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c + * + * Verification code for aDSP VFE packets from userspace. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "adsp.h" +#include + +static uint32_t size1_y, size2_y, size1_cbcr, size2_cbcr; +static uint32_t af_size = 4228; +static uint32_t awb_size = 8196; + +static inline int verify_cmd_op_ack(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_op1_ack *cmd = (vfe_cmd_op1_ack *)cmd_data; + void **addr_y = (void **)&cmd->op1_buf_y_addr; + void **addr_cbcr = (void **)(&cmd->op1_buf_cbcr_addr); + + if (cmd_size != sizeof(vfe_cmd_op1_ack)) + return -1; + if ((*addr_y && adsp_pmem_fixup(module, addr_y, size1_y)) || + (*addr_cbcr && adsp_pmem_fixup(module, addr_cbcr, size1_cbcr))) + return -1; + return 0; +} + +static inline int verify_cmd_stats_autofocus_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + int i; + vfe_cmd_stats_autofocus_cfg *cmd = + (vfe_cmd_stats_autofocus_cfg *)cmd_data; + + if (cmd_size != sizeof(vfe_cmd_stats_autofocus_cfg)) + return -1; + + for (i = 0; i < 3; i++) { + void **addr = (void **)(&cmd->af_stats_op_buf[i]); + if (*addr && adsp_pmem_fixup(module, addr, af_size)) + return -1; + } + return 0; +} + +static inline int verify_cmd_stats_wb_exp_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_stats_wb_exp_cfg *cmd = + (vfe_cmd_stats_wb_exp_cfg *)cmd_data; + int i; + + if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_cfg)) + return -1; + + for (i = 0; i < 3; i++) { + void **addr = (void **)(&cmd->wb_exp_stats_op_buf[i]); + if (*addr && adsp_pmem_fixup(module, addr, awb_size)) + return -1; + } + return 0; +} + +static inline int verify_cmd_stats_af_ack(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_stats_af_ack *cmd = (vfe_cmd_stats_af_ack *)cmd_data; + void **addr = (void **)&cmd->af_stats_op_buf; + + if (cmd_size != sizeof(vfe_cmd_stats_af_ack)) + return -1; + + if (*addr && adsp_pmem_fixup(module, addr, af_size)) + return -1; + return 0; +} + +static inline int verify_cmd_stats_wb_exp_ack(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_stats_wb_exp_ack *cmd = + (vfe_cmd_stats_wb_exp_ack *)cmd_data; + void **addr = (void **)&cmd->wb_exp_stats_op_buf; + + if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_ack)) + return -1; + + if (*addr && adsp_pmem_fixup(module, addr, awb_size)) + return -1; + return 0; +} + +static int verify_vfe_command(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + switch (cmd_id) { + case VFE_CMD_OP1_ACK: + return verify_cmd_op_ack(module, cmd_data, cmd_size); + case VFE_CMD_OP2_ACK: + return verify_cmd_op_ack(module, cmd_data, cmd_size); + case VFE_CMD_STATS_AUTOFOCUS_CFG: + return verify_cmd_stats_autofocus_cfg(module, cmd_data, + cmd_size); + case VFE_CMD_STATS_WB_EXP_CFG: + return verify_cmd_stats_wb_exp_cfg(module, cmd_data, cmd_size); + case VFE_CMD_STATS_AF_ACK: + return verify_cmd_stats_af_ack(module, cmd_data, cmd_size); + case VFE_CMD_STATS_WB_EXP_ACK: + return verify_cmd_stats_wb_exp_ack(module, cmd_data, cmd_size); + default: + if (cmd_id > 29) { + MM_ERR("module %s: invalid VFE command id %d\n", + module->name, cmd_id); + return -1; + } + } + return 0; +} + +static int verify_vfe_command_scale(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + // FIXME: check the size + if (cmd_id > 1) { + MM_ERR("module %s: invalid VFE SCALE command id %d\n", + module->name, cmd_id); + return -1; + } + return 0; +} + + +static uint32_t get_size(uint32_t hw) +{ + uint32_t height, width; + uint32_t height_mask = 0x3ffc; + uint32_t width_mask = 0x3ffc000; + + height = (hw & height_mask) >> 2; + width = (hw & width_mask) >> 14 ; + return height * width; +} + +static int verify_vfe_command_table(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + int i; + + switch (cmd_id) { + case VFE_CMD_AXI_IP_CFG: + { + vfe_cmd_axi_ip_cfg *cmd = (vfe_cmd_axi_ip_cfg *)cmd_data; + uint32_t size; + if (cmd_size != sizeof(vfe_cmd_axi_ip_cfg)) { + MM_ERR("module %s: invalid VFE TABLE \ + (VFE_CMD_AXI_IP_CFG) command size %d\n", + module->name, cmd_size); + return -1; + } + size = get_size(cmd->ip_cfg_part2); + + for (i = 0; i < 8; i++) { + void **addr = (void **) + &cmd->ip_buf_addr[i]; + if (*addr && adsp_pmem_fixup(module, addr, size)) + return -1; + } + } + case VFE_CMD_AXI_OP_CFG: + { + vfe_cmd_axi_op_cfg *cmd = (vfe_cmd_axi_op_cfg *)cmd_data; + void **addr1_y, **addr2_y, **addr1_cbcr, **addr2_cbcr; + + if (cmd_size != sizeof(vfe_cmd_axi_op_cfg)) { + MM_ERR("module %s: invalid VFE TABLE \ + (VFE_CMD_AXI_OP_CFG) command size %d\n", + module->name, cmd_size); + return -1; + } + size1_y = get_size(cmd->op1_y_cfg_part2); + size1_cbcr = get_size(cmd->op1_cbcr_cfg_part2); + size2_y = get_size(cmd->op2_y_cfg_part2); + size2_cbcr = get_size(cmd->op2_cbcr_cfg_part2); + for (i = 0; i < 8; i++) { + addr1_y = (void **)(&cmd->op1_buf1_addr[2*i]); + addr1_cbcr = (void **)(&cmd->op1_buf1_addr[2*i+1]); + addr2_y = (void **)(&cmd->op2_buf1_addr[2*i]); + addr2_cbcr = (void **)(&cmd->op2_buf1_addr[2*i+1]); +/* + printk("module %s: [%d] %p %p %p %p\n", + module->name, i, + *addr1_y, *addr1_cbcr, *addr2_y, *addr2_cbcr); +*/ + if ((*addr1_y && adsp_pmem_fixup(module, addr1_y, size1_y)) || + (*addr1_cbcr && adsp_pmem_fixup(module, addr1_cbcr, size1_cbcr)) || + (*addr2_y && adsp_pmem_fixup(module, addr2_y, size2_y)) || + (*addr2_cbcr && adsp_pmem_fixup(module, addr2_cbcr, size2_cbcr))) + return -1; + } + } + default: + if (cmd_id > 4) { + MM_ERR("module %s: invalid VFE TABLE command \ + id %d\n", module->name, cmd_id); + return -1; + } + } + return 0; +} + +int adsp_vfe_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch (queue_id) { + case QDSP_vfeCommandQueue: + return verify_vfe_command(module, cmd_data, cmd_size); + case QDSP_vfeCommandScaleQueue: + return verify_vfe_command_scale(module, cmd_data, cmd_size); + case QDSP_vfeCommandTableQueue: + return verify_vfe_command_table(module, cmd_data, cmd_size); + default: + MM_ERR("module %s: unknown queue id %d\n", + module->name, queue_id); + return -1; + } +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c new file mode 100644 index 00000000000..492fa0edd95 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c @@ -0,0 +1,272 @@ +/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c + * + * Verificion code for aDSP VDEC packets from userspace. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include "adsp.h" +#include + +#define MAX_FLUSH_SIZE 160 + +static inline void *high_low_short_to_ptr(unsigned short high, + unsigned short low) +{ + return (void *)((((unsigned long)high) << 16) | ((unsigned long)low)); +} + +static inline void ptr_to_high_low_short(void *ptr, unsigned short *high, + unsigned short *low) +{ + *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff); + *low = (unsigned short)((unsigned long)ptr & 0xffff); +} + +static int pmem_fixup_high_low(unsigned short *high, + unsigned short *low, + unsigned short size_high, + unsigned short size_low, + struct msm_adsp_module *module, + unsigned long *addr, unsigned long *size, + struct file **filp, unsigned long *offset) +{ + void *phys_addr; + unsigned long phys_size; + unsigned long kvaddr; + + phys_addr = high_low_short_to_ptr(*high, *low); + phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low); + MM_DBG("virt %x %x\n", (unsigned int)phys_addr, + (unsigned int)phys_size); + if (phys_addr) { + if (adsp_pmem_fixup_kvaddr(module, &phys_addr, + &kvaddr, phys_size, filp, offset)) { + MM_ERR("ah%x al%x sh%x sl%x addr %x size %x\n", + *high, *low, size_high, + size_low, (unsigned int)phys_addr, + (unsigned int)phys_size); + return -EINVAL; + } + } + ptr_to_high_low_short(phys_addr, high, low); + MM_DBG("phys %x %x\n", (unsigned int)phys_addr, + (unsigned int)phys_size); + if (addr) + *addr = kvaddr; + if (size) + *size = phys_size; + return 0; +} + +static int verify_vdec_pkt_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + unsigned short cmd_id = ((unsigned short *)cmd_data)[0]; + viddec_cmd_subframe_pkt *pkt; + unsigned long subframe_pkt_addr; + unsigned long subframe_pkt_size; + unsigned short *frame_header_pkt; + int i, num_addr, col_addr = 0, skip; + int start_pos = 0, xdim_pos = 1, ydim_pos = 2; + unsigned short *frame_buffer_high, *frame_buffer_low; + unsigned long frame_buffer_size; + unsigned short frame_buffer_size_high, frame_buffer_size_low; + struct file *filp = NULL; + unsigned long offset = 0; + struct pmem_addr pmem_addr; + unsigned long Codec_Id = 0; + + MM_DBG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, + (unsigned int)cmd_data); + if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) { + MM_INFO("adsp_video: unknown video packet %u\n", cmd_id); + return 0; + } + if (cmd_size < sizeof(viddec_cmd_subframe_pkt)) + return -1; + + pkt = (viddec_cmd_subframe_pkt *)cmd_data; + + if (pmem_fixup_high_low(&(pkt->subframe_packet_high), + &(pkt->subframe_packet_low), + pkt->subframe_packet_size_high, + pkt->subframe_packet_size_low, + module, + &subframe_pkt_addr, + &subframe_pkt_size, + &filp, &offset)) + return -1; + Codec_Id = pkt->codec_selection_word; + /*Invalidate cache before accessing the cached pmem buffer*/ + if (filp) { + pmem_addr.vaddr = subframe_pkt_addr; + pmem_addr.length = (((subframe_pkt_size*2) + 31) & (~31)) + 32; + pmem_addr.offset = offset; + if (pmem_cache_maint (filp, PMEM_INV_CACHES, &pmem_addr)) { + MM_ERR("Cache operation failed for phys addr high %x" + " addr low %x\n", pkt->subframe_packet_high, + pkt->subframe_packet_low); + return -EINVAL; + } + } + /* deref those ptrs and check if they are a frame header packet */ + frame_header_pkt = (unsigned short *)subframe_pkt_addr; + switch (frame_header_pkt[0]) { + case 0xB201: /* h.264 vld in dsp */ + if (Codec_Id == 0x8) { + num_addr = 16; + skip = 0; + start_pos = 5; + } else { + num_addr = 16; + skip = 0; + start_pos = 6; + col_addr = 17; + } + break; + case 0x8201: /* h.264 vld in arm */ + num_addr = 16; + skip = 0; + start_pos = 6; + break; + case 0x4D01: /* mpeg-4 and h.263 vld in arm */ + num_addr = 3; + skip = 0; + start_pos = 5; + break; + case 0x9201: /*For Real Decoder*/ + num_addr = 2; + skip = 0; + start_pos = 5; + break; + case 0xBD01: /* mpeg-4 and h.263 vld in dsp */ + num_addr = 3; + skip = 0; + start_pos = 6; + if (((frame_header_pkt[5] & 0x000c) >> 2) == 0x2) /* B-frame */ + start_pos = 8; + break; + case 0x0001: /* wmv */ + num_addr = 2; + skip = 0; + start_pos = 5; + break; + case 0xC201: /*WMV main profile*/ + num_addr = 3; + skip = 0; + start_pos = 6; + break; + case 0xDD01: /* VP6 */ + num_addr = 3; + skip = 0; + start_pos = 10; + break; + case 0xFD01: /* VP8 */ + num_addr = 3; + skip = 0; + start_pos = 24; + break; + default: + return 0; + } + + frame_buffer_high = &frame_header_pkt[start_pos]; + frame_buffer_low = &frame_header_pkt[start_pos + 1]; + frame_buffer_size = (frame_header_pkt[xdim_pos] * + frame_header_pkt[ydim_pos] * 3) / 2; + ptr_to_high_low_short((void *)frame_buffer_size, + &frame_buffer_size_high, + &frame_buffer_size_low); + for (i = 0; i < num_addr; i++) { + if (frame_buffer_high && frame_buffer_low) { + if (pmem_fixup_high_low(frame_buffer_high, + frame_buffer_low, + frame_buffer_size_high, + frame_buffer_size_low, + module, + NULL, NULL, NULL, NULL)) + return -EINVAL; + } + frame_buffer_high += 2; + frame_buffer_low += 2; + } + /* Patch the output buffer. */ + frame_buffer_high += 2*skip; + frame_buffer_low += 2*skip; + if (frame_buffer_high && frame_buffer_low) { + if (pmem_fixup_high_low(frame_buffer_high, + frame_buffer_low, + frame_buffer_size_high, + frame_buffer_size_low, + module, + NULL, NULL, NULL, NULL)) + return -EINVAL; + } + if (col_addr) { + frame_buffer_high += 2; + frame_buffer_low += 2; + /* Patch the Co-located buffers.*/ + frame_buffer_size = (72 * frame_header_pkt[xdim_pos] * + frame_header_pkt[ydim_pos]) >> 16; + ptr_to_high_low_short((void *)frame_buffer_size, + &frame_buffer_size_high, + &frame_buffer_size_low); + for (i = 0; i < col_addr; i++) { + if (frame_buffer_high && frame_buffer_low) { + if (pmem_fixup_high_low(frame_buffer_high, + frame_buffer_low, + frame_buffer_size_high, + frame_buffer_size_low, + module, + NULL, NULL, NULL, NULL)) + return -EINVAL; + } + frame_buffer_high += 2; + frame_buffer_low += 2; + } + } + /*Flush the cached pmem subframe packet before sending to DSP*/ + if (filp) { + pmem_addr.vaddr = subframe_pkt_addr; + pmem_addr.length = MAX_FLUSH_SIZE; + pmem_addr.offset = offset; + if (pmem_cache_maint(filp, PMEM_CLEAN_CACHES, &pmem_addr)) { + MM_ERR("Cache operation failed for phys addr high %x" + " addr low %x\n", pkt->subframe_packet_high, + pkt->subframe_packet_low); + return -1; + } + } + + return 0; +} + +int adsp_video_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch (queue_id) { + case QDSP_mpuVDecPktQueue: + return verify_vdec_pkt_cmd(module, cmd_data, cmd_size); + default: + MM_INFO("unknown video queue %u\n", queue_id); + return 0; + } +} + diff --git a/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c new file mode 100644 index 00000000000..936b7af81bb --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c @@ -0,0 +1,235 @@ +/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c + * + * Verificion code for aDSP VENC packets from userspace. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include +#include "adsp.h" +#include + + +static unsigned short x_dimension, y_dimension; + +static inline void *high_low_short_to_ptr(unsigned short high, + unsigned short low) +{ + return (void *)((((unsigned long)high) << 16) | ((unsigned long)low)); +} + +static inline void ptr_to_high_low_short(void *ptr, unsigned short *high, + unsigned short *low) +{ + *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff); + *low = (unsigned short)((unsigned long)ptr & 0xffff); +} + +static int pmem_fixup_high_low(unsigned short *high, + unsigned short *low, + unsigned short size_high, + unsigned short size_low, + struct msm_adsp_module *module, + unsigned long *addr, unsigned long *size) +{ + void *phys_addr; + unsigned long phys_size; + unsigned long kvaddr; + + phys_addr = high_low_short_to_ptr(*high, *low); + phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low); + MM_DBG("virt %x %x\n", (unsigned int)phys_addr, + (unsigned int)phys_size); + if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size, + NULL, NULL)) { + MM_ERR("ah%x al%x sh%x sl%x addr %x size %x\n", + *high, *low, size_high, + size_low, (unsigned int)phys_addr, + (unsigned int) phys_size); + return -1; + } + ptr_to_high_low_short(phys_addr, high, low); + MM_DBG("phys %x %x\n", (unsigned int)phys_addr, + (unsigned int)phys_size); + if (addr) + *addr = kvaddr; + if (size) + *size = phys_size; + return 0; +} + +static int verify_venc_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + unsigned short cmd_id = ((unsigned short *)cmd_data)[0]; + unsigned long frame_buf_size, luma_buf_size, chroma_buf_size; + unsigned short frame_buf_size_high, frame_buf_size_low; + unsigned short luma_buf_size_high, luma_buf_size_low; + unsigned short chroma_buf_size_high, chroma_buf_size_low; + videnc_cmd_cfg *config_cmd; + videnc_cmd_frame_start *frame_cmd; + videnc_cmd_dis *dis_cmd; + + MM_DBG("cmd_size %d cmd_id %d cmd_data %x\n", + cmd_size, cmd_id, (unsigned int)cmd_data); + switch (cmd_id) { + case VIDENC_CMD_ACTIVE: + if (cmd_size < sizeof(videnc_cmd_active)) + return -1; + break; + case VIDENC_CMD_IDLE: + if (cmd_size < sizeof(videnc_cmd_idle)) + return -1; + x_dimension = y_dimension = 0; + break; + case VIDENC_CMD_STATUS_QUERY: + if (cmd_size < sizeof(videnc_cmd_status_query)) + return -1; + break; + case VIDENC_CMD_RC_CFG: + if (cmd_size < sizeof(videnc_cmd_rc_cfg)) + return -1; + break; + case VIDENC_CMD_INTRA_REFRESH: + if (cmd_size < sizeof(videnc_cmd_intra_refresh)) + return -1; + break; + case VIDENC_CMD_DIGITAL_ZOOM: + if (cmd_size < sizeof(videnc_cmd_digital_zoom)) + return -1; + break; + case VIDENC_CMD_DIS_CFG: + if (cmd_size < sizeof(videnc_cmd_dis_cfg)) + return -1; + break; + case VIDENC_CMD_VENC_CLOCK: + if (cmd_size < sizeof(struct videnc_cmd_venc_clock)) + return -1; + break; + case VIDENC_CMD_CFG: + if (cmd_size < sizeof(videnc_cmd_cfg)) + return -1; + config_cmd = (videnc_cmd_cfg *)cmd_data; + x_dimension = ((config_cmd->venc_frame_dim) & 0xFF00)>>8; + x_dimension = x_dimension*16; + y_dimension = (config_cmd->venc_frame_dim) & 0xFF; + y_dimension = y_dimension * 16; + break; + case VIDENC_CMD_FRAME_START: + if (cmd_size < sizeof(videnc_cmd_frame_start)) + return -1; + frame_cmd = (videnc_cmd_frame_start *)cmd_data; + luma_buf_size = x_dimension * y_dimension; + chroma_buf_size = luma_buf_size>>1; + frame_buf_size = luma_buf_size + chroma_buf_size; + ptr_to_high_low_short((void *)luma_buf_size, + &luma_buf_size_high, + &luma_buf_size_low); + ptr_to_high_low_short((void *)chroma_buf_size, + &chroma_buf_size_high, + &chroma_buf_size_low); + ptr_to_high_low_short((void *)frame_buf_size, + &frame_buf_size_high, + &frame_buf_size_low); + /* Address of raw Y data. */ + if (pmem_fixup_high_low(&frame_cmd->input_luma_addr_high, + &frame_cmd->input_luma_addr_low, + luma_buf_size_high, + luma_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Address of raw CbCr data */ + if (pmem_fixup_high_low(&frame_cmd->input_chroma_addr_high, + &frame_cmd->input_chroma_addr_low, + chroma_buf_size_high, + chroma_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Reference VOP */ + if (pmem_fixup_high_low(&frame_cmd->ref_vop_buf_ptr_high, + &frame_cmd->ref_vop_buf_ptr_low, + frame_buf_size_high, + frame_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Encoded Packet Address */ + if (pmem_fixup_high_low(&frame_cmd->enc_pkt_buf_ptr_high, + &frame_cmd->enc_pkt_buf_ptr_low, + frame_cmd->enc_pkt_buf_size_high, + frame_cmd->enc_pkt_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Unfiltered VOP Buffer Address */ + if (pmem_fixup_high_low( + &frame_cmd->unfilt_recon_vop_buf_ptr_high, + &frame_cmd->unfilt_recon_vop_buf_ptr_low, + frame_buf_size_high, + frame_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Filtered VOP Buffer Address */ + if (pmem_fixup_high_low(&frame_cmd->filt_recon_vop_buf_ptr_high, + &frame_cmd->filt_recon_vop_buf_ptr_low, + frame_buf_size_high, + frame_buf_size_low, + module, + NULL, NULL)) + return -1; + break; + case VIDENC_CMD_DIS: + if (cmd_size < sizeof(videnc_cmd_dis)) + return -1; + dis_cmd = (videnc_cmd_dis *)cmd_data; + luma_buf_size = x_dimension * y_dimension; + ptr_to_high_low_short((void *)luma_buf_size, + &luma_buf_size_high, + &luma_buf_size_low); + /* Prev VFE Luma Output Address */ + if (pmem_fixup_high_low(&dis_cmd->vfe_out_prev_luma_addr_high, + &dis_cmd->vfe_out_prev_luma_addr_low, + luma_buf_size_high, + luma_buf_size_low, + module, + NULL, NULL)) + return -1; + break; + default: + MM_INFO("adsp_video:unknown encoder video cmd %u\n", cmd_id); + return 0; + } + + return 0; +} + + +int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch (queue_id) { + case QDSP_mpuVEncCmdQueue: + return verify_venc_cmd(module, cmd_data, cmd_size); + default: + MM_INFO("unknown video queue %u\n", queue_id); + return 0; + } +} + diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c new file mode 100644 index 00000000000..725819f85d6 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_aac.c @@ -0,0 +1,1915 @@ +/* arch/arm/mach-msm/qdsp5/audio_aac.c + * + * aac audio decoder device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2008-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#define BUFSZ 32768 +#define DMASZ (BUFSZ * 2) +#define BUFSZ_MIN 4096 +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AAC 5 + +#define PCM_BUFSZ_MIN 9600 /* Hold one stereo AAC frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDAAC_METAFIELD_MASK 0xFFFF0000 +#define AUDAAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDAAC_EOS_FLG_MASK 0x01 +#define AUDAAC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDAAC_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDAAC_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audaac_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audaac_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + void *map_v_read; + void *map_v_write; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + struct msm_audio_aac_config aac_config; + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int eos_in_progress; + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int rmt_resource_released; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audaac_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + struct msm_audio_bitstream_info stream_info; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audaac_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_AAC; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_AAC; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for AAC \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_AAC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} +static void audaac_update_stream_info(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload e_payload; + + /* get stream info from DSP msg */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + audio->stream_info.codec_type = AUDIO_CODEC_TYPE_AAC; + audio->stream_info.chan_info = (0x0000FFFF & payload[1]); + audio->stream_info.sample_rate = (0x0000FFFF & payload[2]); + audio->stream_info.bit_stream_info = (0x0000FFFF & payload[3]); + audio->stream_info.bit_rate = payload[4]; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_DBG("chan_info=%d, sample_rate=%d, bit_stream_info=%d\n", + audio->stream_info.chan_info, + audio->stream_info.sample_rate, + audio->stream_info.bit_stream_info); + + /* send event to ARM to notify steam info coming */ + e_payload.stream_info = audio->stream_info; + audaac_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); +} +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case AUDPLAY_UP_STREAM_INFO: + audaac_update_stream_info(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_aac = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id,\ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + audpp_cmd_cfg_adec_params_aac cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.format = audio->aac_config.format; + cmd.audio_object = audio->aac_config.audio_object; + cmd.ep_config = audio->aac_config.ep_config; + cmd.aac_section_data_resilience_flag = + audio->aac_config.aac_section_data_resilience_flag; + cmd.aac_scalefactor_data_resilience_flag = + audio->aac_config.aac_scalefactor_data_resilience_flag; + cmd.aac_spectral_data_resilience_flag = + audio->aac_config.aac_spectral_data_resilience_flag; + cmd.sbr_on_flag = audio->aac_config.sbr_on_flag; + cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag; + cmd.channel_configuration = audio->aac_config.channel_configuration; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDAAC_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + /* complete all the writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + /* AAC frame size */ + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % 1024) + + (audio->mfield ? 24 : 0); + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } else if ((audio->out[0].used == 0) && + (audio->out[1].used == 0) && + (audio->eos_in_progress)) { + wake_up(&audio->write_wait); + } + + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static int audaac_validate_usr_config(struct msm_audio_aac_config *config) +{ + int ret_val = -1; + + if (config->format != AUDIO_AAC_FORMAT_ADTS && + config->format != AUDIO_AAC_FORMAT_RAW && + config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW && + config->format != AUDIO_AAC_FORMAT_LOAS) + goto done; + + if (config->audio_object != AUDIO_AAC_OBJECT_LC && + config->audio_object != AUDIO_AAC_OBJECT_LTP && + config->audio_object != AUDIO_AAC_OBJECT_BSAC && + config->audio_object != AUDIO_AAC_OBJECT_ERLC) + goto done; + + if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) { + if (config->ep_config > 3) + goto done; + if (config->aac_scalefactor_data_resilience_flag != + AUDIO_AAC_SCA_DATA_RES_OFF && + config->aac_scalefactor_data_resilience_flag != + AUDIO_AAC_SCA_DATA_RES_ON) + goto done; + if (config->aac_section_data_resilience_flag != + AUDIO_AAC_SEC_DATA_RES_OFF && + config->aac_section_data_resilience_flag != + AUDIO_AAC_SEC_DATA_RES_ON) + goto done; + if (config->aac_spectral_data_resilience_flag != + AUDIO_AAC_SPEC_DATA_RES_OFF && + config->aac_spectral_data_resilience_flag != + AUDIO_AAC_SPEC_DATA_RES_ON) + goto done; + } else { + config->aac_section_data_resilience_flag = + AUDIO_AAC_SEC_DATA_RES_OFF; + config->aac_scalefactor_data_resilience_flag = + AUDIO_AAC_SCA_DATA_RES_OFF; + config->aac_spectral_data_resilience_flag = + AUDIO_AAC_SPEC_DATA_RES_OFF; + } + +#ifndef CONFIG_AUDIO_AAC_PLUS + if (AUDIO_AAC_SBR_ON_FLAG_OFF != config->sbr_on_flag) + goto done; +#else + if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF && + config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON) + goto done; +#endif + +#ifndef CONFIG_AUDIO_ENHANCED_AAC_PLUS + if (AUDIO_AAC_SBR_PS_ON_FLAG_OFF != config->sbr_ps_on_flag) + goto done; +#else + if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF && + config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON) + goto done; +#endif + + if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR) + goto done; + + if (config->channel_configuration > 2) + goto done; + + ret_val = 0; + done: + return ret_val; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audaac_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audaac_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audaac_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audaac_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audaac_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audaac_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audaac_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audaac_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audaac_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audaac_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audaac_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH running=%d\n", audio->running); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + + if (config.channel_count == 1) { + config.channel_count = + AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = + AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->mfield = config.meta_field; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == + AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_AAC_CONFIG:{ + if (copy_to_user((void *)arg, &audio->aac_config, + sizeof(audio->aac_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_AAC_CONFIG:{ + struct msm_audio_aac_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + if (audaac_validate_usr_config(&usr_config) == 0) { + audio->aac_config = usr_config; + rc = 0; + } else + rc = -EINVAL; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if (config.pcm_feedback) { + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + } + rc = 0; + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_STREAM_INFO:{ + if (audio->stream_info.sample_rate == 0) { + /* haven't received DSP stream event, + the stream info is not updated */ + rc = -EPERM; + break; + } + if (copy_to_user((void *)arg, &audio->stream_info, + sizeof(struct msm_audio_bitstream_info))) + rc = -EFAULT; + else + rc = 0; + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} +/* Only useful in tunnel-mode */ +static int audaac_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("to read %d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped) + || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("no partial frame done reading\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + /* order reads to the output buffer */ + rmb(); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x\n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + /* + * Force to exit while loop + * to prevent output thread + * sleep too long if data is not + * ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audaac_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + char *buf_ptr; + int rc = 0; + unsigned long flags = 0; + + MM_DBG("signal input EOS reserved=%d\n", audio->reserved); + if (audio->reserved) { + MM_DBG("Pass reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + audio->reserved = 0; + frame->used = 2; + audplay_send_data(audio, 0); + } + MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n", + audio->out[0].used, audio->out[1].used, audio->out_needed); + frame = audio->out + audio->out_head; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->eos_in_progress = 1; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->eos_in_progress = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDAAC_EOS_NONE; + unsigned dsize; + + unsigned short mfield_size = 0; + MM_DBG("cnt=%d\n", count); + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDAAC_EOS_FLG_OFFSET] & + AUDAAC_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDAAC_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDAAC_EOS_FLG_OFFSET] &= + ~AUDAAC_EOS_FLG_MASK; + } + /* Check EOS to see if */ + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", + audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition, + (int) buf, (int) start); + if (eos_condition == AUDAAC_EOS_SET) + rc = audaac_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audaac_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audaac_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audaac_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audaac_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audaac_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audaac_suspend(struct early_suspend *h) +{ + struct audaac_suspend_ctl *ctl = + container_of(h, struct audaac_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audaac_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audaac_resume(struct early_suspend *h) +{ + struct audaac_suspend_ctl *ctl = + container_of(h, struct audaac_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audaac_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audaac_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audaac_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].used %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audaac_debug_fops = { + .read = audaac_debug_read, + .open = audaac_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, index, offset = 0; + unsigned pmem_sz = DMASZ; + struct audaac_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_aac_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_AAC; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap(audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + audio->read_phys = allocate_contiguous_ebi_nomap(PCM_BUFSZ_MIN * + PCM_BUF_MAX_COUNT, SZ_4K); + if (!audio->read_phys) { + MM_ERR("could not allocate read buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->map_v_read = ioremap(audio->read_phys, + PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map read buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + free_contiguous_memory_by_paddr(audio->read_phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->read_data = audio->map_v_read; + MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->read_phys, (int)audio->read_data); + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_aac, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for AAC session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + audio->pcm_buf_count = PCM_BUF_MAX_COUNT; + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) { + audio->in[index].data = audio->read_data + offset; + audio->in[index].addr = audio->read_phys + offset; + audio->in[index].size = PCM_BUFSZ_MIN; + audio->in[index].used = 0; + offset += PCM_BUFSZ_MIN; + } + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS; + audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC; + audio->aac_config.ep_config = 0; + audio->aac_config.aac_section_data_resilience_flag = + AUDIO_AAC_SEC_DATA_RES_OFF; + audio->aac_config.aac_scalefactor_data_resilience_flag = + AUDIO_AAC_SCA_DATA_RES_OFF; + audio->aac_config.aac_spectral_data_resilience_flag = + AUDIO_AAC_SPEC_DATA_RES_OFF; +#ifdef CONFIG_AUDIO_AAC_PLUS + audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON; +#else + audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_OFF; +#endif +#ifdef CONFIG_AUDIO_ENHANCED_AAC_PLUS + audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON; +#else + audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_OFF; +#endif + audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR; + audio->aac_config.channel_configuration = 2; + audio->vol_pan.volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_aac_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audaac_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audaac_resume; + audio->suspend_ctl.node.suspend = audaac_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (index = 0; index < AUDAAC_EVENT_NUM; index++) { + e_node = kmalloc(sizeof(struct audaac_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } + memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info)); +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audaac_fsync +}; + +struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_aac_misc); +} + +static void __exit audio_exit(void) +{ + misc_deregister(&audio_aac_misc); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM AAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_aac_in.c b/arch/arm/mach-msm/qdsp5/audio_aac_in.c new file mode 100644 index 00000000000..79a828a97a1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_aac_in.c @@ -0,0 +1,1459 @@ +/* arch/arm/mach-msm/qdsp5/audio_aac_in.c + * + * aac audio input device + * + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_aac_in.c, + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define FRAME_HEADER_SIZE 8 /* 8 bytes frame header */ +#define NT_FRAME_HEADER_SIZE 24 /* 24 bytes frame header */ +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM 8 +#define AAC_FRAME_SIZE 1536 /* 36 bytes data */ +/*Tunnel mode : 1536 bytes data + 8 byte header*/ +#define FRAME_SIZE (AAC_FRAME_SIZE + FRAME_HEADER_SIZE) +/* 1536 bytes data + 24 meta field*/ +#define NT_FRAME_SIZE (AAC_FRAME_SIZE + NT_FRAME_HEADER_SIZE) +#define DMASZ (FRAME_SIZE * FRAME_NUM) +#define NT_DMASZ (NT_FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM 2 +#define OUT_BUFFER_SIZE (32 * 1024 + NT_FRAME_HEADER_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + +#define AUDPREPROC_AAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer*/ +#define AUDPREPROC_AAC_EOS_FLG_MASK 0x01 +#define AUDPREPROC_AAC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_AAC_EOS_SET 0x1 /* EOS set in meta field */ + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_aac_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + int mfield; /* meta field embedded in data */ + int wflush; /*write flush */ + int rflush; /*read flush*/ + int out_frame_cnt; + + struct msm_adsp_module *audrec; + struct msm_adsp_module *audpre; + + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* Frame size (1536 bytes) */ + uint32_t bit_rate; /* bit rate for AAC */ + uint32_t record_quality; /* record quality (bits/sample/channel) */ + uint32_t enc_type; /* 1 for AAC */ + uint32_t mode; /* T or NT Mode*/ + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + unsigned short samp_rate_index; + uint32_t audrec_obj_idx ; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_read; + void *map_v_write; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; +} __packed; + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __packed; + +struct aac_encoded_meta_out { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audio_send_queue_pre(audio, cmd, len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) + +#define audio_send_queue_recbs(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +static int audaac_in_dsp_enable(struct audio_aac_in *audio, int enable); +static int audaac_in_encparam_config(struct audio_aac_in *audio); +static int audaac_in_encmem_config(struct audio_aac_in *audio); +static int audaac_in_dsp_read_buffer(struct audio_aac_in *audio, + uint32_t read_cnt); +static void audaac_in_flush(struct audio_aac_in *audio); + +static void audaac_in_get_dsp_frames(struct audio_aac_in *audio); +static int audpcm_config(struct audio_aac_in *audio); +static void audaac_out_flush(struct audio_aac_in *audio); +static int audaac_in_routing_mode_config(struct audio_aac_in *audio); +static void audrec_pcm_send_data(struct audio_aac_in *audio, unsigned needed); +static void audaac_nt_in_get_dsp_frames(struct audio_aac_in *audio); +static void audaac_in_flush(struct audio_aac_in *audio); + +static unsigned convert_dsp_samp_index(unsigned index) +{ + switch (index) { + case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000; + case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100; + case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000; + case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000; + case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050; + case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000; + case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000; + case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025; + case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000; + default: return AUDREC_CMD_SAMP_RATE_INDX_11025; + } +} + +static unsigned convert_samp_rate(unsigned hz) +{ + switch (hz) { + case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000; + case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100; + case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000; + case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000; + case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050; + case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000; + case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000; + case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025; + case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000; + default: return RPC_AUD_DEF_SAMPLE_RATE_11025; + } +} + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* Convert Bit Rate to Record Quality field of DSP */ +static unsigned int bitrate_to_record_quality(unsigned int sample_rate, + unsigned int channel, unsigned int bit_rate) { + unsigned int temp; + + temp = sample_rate * channel; + MM_DBG(" sample rate * channel = %d\n", temp); + /* To represent in Q12 fixed format */ + temp = (bit_rate * 4096) / temp; + MM_DBG(" Record Quality = 0x%8x\n", temp); + return temp; +} + +/* must be called with audio->lock held */ +static int audaac_in_enable(struct audio_aac_in *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + cfg.codec = RPC_AUD_DEF_CODEC_AAC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + audmgr_disable(&audio->audmgr); + MM_ERR("msm_adsp_enable(audpre) failed\n"); + return -ENODEV; + } + } + if (msm_adsp_enable(audio->audrec)) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audmgr_disable(&audio->audmgr); + msm_adsp_disable(audio->audpre); + } + MM_ERR("msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audaac_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audaac_in_disable(struct audio_aac_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audaac_in_dsp_enable(audio, 0); + + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + audio->stopped = 1; + wake_up(&audio->wait); + msm_adsp_disable(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + MM_DBG("type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + MM_ERR("err_index %d\n", msg[0]); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + + +static void audaac_in_get_dsp_frames(struct audio_aac_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - + sizeof(*frame)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("in_count = %d\n", audio->in_count); + } else + audio->in_count++; + + audaac_in_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audaac_nt_in_get_dsp_frames(struct audio_aac_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + +static int audrec_pcm_buffer_ptr_refresh(struct audio_aac_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == NT_FRAME_HEADER_SIZE) + len = len / 2; + else + len = (len + NT_FRAME_HEADER_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_config(struct audio_aac_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE; + cmd.sampling_freq = convert_samp_index(audio->samp_rate); + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audaac_in_routing_mode_config(struct audio_aac_in *audio) +{ + struct audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ROUTING_MODE; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + cmd.routing_mode = 1; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_aac_in *audio = NULL; + if (data) + audio = data; + else { + MM_ERR("invalid data for event %x\n", id); + return; + } + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: { + struct audrec_msg_cmd_cfg_done_msg cmd_cfg_done_msg; + getevent(&cmd_cfg_done_msg, AUDREC_MSG_CMD_CFG_DONE_MSG_LEN); + if (cmd_cfg_done_msg.audrec_enc_type & \ + AUDREC_MSG_CFG_DONE_ENC_ENA) { + audio->audrec_obj_idx = cmd_cfg_done_msg.audrec_obj_idx; + MM_DBG("CFG ENABLED\n"); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audaac_in_routing_mode_config(audio); + } else { + audaac_in_encmem_config(audio); + } + } else { + MM_DBG("CFG SLEEP\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG: { + struct audrec_msg_cmd_routing_mode_done_msg \ + routing_msg; + getevent(&routing_msg, AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG); + MM_DBG("AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG"); + if (routing_msg.configuration == 0) { + MM_ERR("routing configuration failed\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } else + audaac_in_encmem_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { + MM_DBG("AREC_MEM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audaac_in_encparam_config(audio); + else + audpcm_config(audio); + break; + } + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_DBG("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audaac_in_encparam_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_DBG("AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG\n"); + audio->running = 1; + wake_up(&audio->wait_enable); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: { + struct audrec_msg_no_ext_pkt_avail_msg err_msg; + getevent(&err_msg, AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN); + MM_DBG("NO_EXT_PKT_AVAILABLE_MSG %x\n",\ + err_msg.audrec_err_id); + break; + } + case AUDREC_MSG_PACKET_READY_MSG: { + struct audrec_msg_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt msw %d \ + write cnt lsw %d read cnt msw %d read cnt lsw %d \n",\ + pkt_ready_msg.pkt_counter_msw, \ + pkt_ready_msg.pkt_counter_lsw, \ + pkt_ready_msg.pkt_read_cnt_msw, \ + pkt_ready_msg.pkt_read_cnt_lsw); + + audaac_in_get_dsp_frames(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audaac_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +struct msm_adsp_ops audpre_aac_adsp_ops = { + .event = audpre_dsp_event, +}; + +struct msm_adsp_ops audrec_aac_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audaac_in_dsp_enable(struct audio_aac_in *audio, int enable) +{ + struct audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ENC_CFG; + cmd.audrec_enc_type = (audio->enc_type & 0xFF) | + (enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS); + /* Don't care */ + cmd.audrec_obj_idx = audio->audrec_obj_idx; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audaac_in_encmem_config(struct audio_aac_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + int header_len = 0; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG; + cmd.audrec_obj_idx = audio->audrec_obj_idx; + /* Rate at which packet complete message comes */ + cmd.audrec_up_pkt_intm_cnt = 1; + cmd.audrec_extpkt_buffer_msw = audio->phys >> 16; + cmd.audrec_extpkt_buffer_lsw = audio->phys; + /* Max Buffer no available for frames */ + cmd.audrec_extpkt_buffer_num = FRAME_NUM; + + /* prepare buffer pointers: + * T:1536 bytes aac packet + 4 halfword header + * NT:1536 bytes aac packet + 12 halfword header + */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + header_len = FRAME_HEADER_SIZE/2; + else + header_len = NT_FRAME_HEADER_SIZE/2; + + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + header_len; + data += (AAC_FRAME_SIZE/2) + header_len; + MM_DBG("0x%8x\n", (int)(audio->in[n].data - header_len*2)); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audaac_in_encparam_config(struct audio_aac_in *audio) +{ + struct audrec_cmd_arecparam_aac_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG; + cmd.common.audrec_obj_idx = audio->audrec_obj_idx; + cmd.samp_rate_idx = audio->samp_rate_index; + cmd.stereo_mode = audio->channel_mode; + cmd.rec_quality = audio->record_quality; + + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audaac_flush_command(struct audio_aac_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audaac_in_dsp_read_buffer(struct audio_aac_in *audio, + uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + cmd.type = audio->audrec_obj_idx; + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audaac_ioport_reset(struct audio_aac_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audaac_in_flush(audio); + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audaac_out_flush(audio); + mutex_unlock(&audio->write_lock); +} + +static void audaac_in_flush(struct audio_aac_in *audio) +{ + int i; + unsigned long flags; + + audio->dsp_cnt = 0; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + audio->eos_ack = 0; + for (i = FRAME_NUM-1; i >= 0; i--) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audaac_out_flush(struct audio_aac_in *audio) +{ + int i; + unsigned long flags; + + audio->out_head = 0; + audio->out_count = 0; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out_tail = 0; + for (i = OUT_FRAME_NUM-1; i >= 0; i--) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static long audaac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_aac_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + rc = audaac_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + rc = audaac_in_disable(audio); + break; + } + case AUDIO_FLUSH: { + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audaac_ioport_reset(audio); + if (audio->running) { + audaac_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + cfg.channel_count = 1; + cfg.type = 0; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) + rc = -EINVAL; + break; + } else { + if (cfg.buffer_size != (AAC_FRAME_SIZE + 14)) + rc = -EINVAL; + break; + } + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) + cfg.channels = 1; + else + cfg.channels = 2; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + cfg.bit_rate = audio->bit_rate; + cfg.stream_format = AUDIO_AAC_FORMAT_RAW; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + unsigned int record_quality; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.stream_format != AUDIO_AAC_FORMAT_RAW) { + MM_ERR("unsupported AAC format\n"); + rc = -EINVAL; + break; + } + record_quality = bitrate_to_record_quality(cfg.sample_rate, + cfg.channels, cfg.bit_rate); + /* Range of Record Quality Supported by DSP, Q12 format */ + if ((record_quality < 0x800) || (record_quality > 0x4000)) { + MM_ERR("Unsupported bit rate\n"); + rc = -EINVAL; + break; + } + MM_DBG("channels = %d\n", cfg.channels); + if (cfg.channels == 1) { + cfg.channels = AUDREC_CMD_STEREO_MODE_MONO; + } else if (cfg.channels == 2) { + cfg.channels = AUDREC_CMD_STEREO_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + + audio->samp_rate = convert_samp_rate(cfg.sample_rate); + audio->samp_rate_index = + convert_dsp_samp_index(cfg.sample_rate); + audio->channel_mode = cfg.channels; + audio->bit_rate = cfg.bit_rate; + audio->record_quality = record_quality; + MM_DBG(" Record Quality = 0x%8x\n", audio->record_quality); + break; + } + + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audaac_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_aac_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + struct aac_encoded_meta_out meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG("count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->rflush); + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, + (sizeof(struct aac_encoded_meta_out) - \ + sizeof(uint16_t))); + meta_field.metadata_len = + sizeof(struct aac_encoded_meta_out); + if (copy_to_user((char *)start, (char *)&meta_field, + sizeof(struct aac_encoded_meta_out))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_DBG("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct aac_encoded_meta_out); + count -= sizeof(struct aac_encoded_meta_out); + } + if (count >= size) { + /* order the reads on the buffer */ + dma_coherent_post_ops(); + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) { + if (!audio->eos_ack) { + MM_DBG("sending read ptr command \ + %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audaac_in_dsp_read_buffer(audio, + audio->dsp_cnt++); + } + } + } else { + MM_ERR("short read\n"); + break; + } + break; + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static void audrec_pcm_send_data(struct audio_aac_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audrec_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + + +static int audaac_in_fsync(struct file *file, loff_t a, loff_t b, int datasync) + +{ + struct audio_aac_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + +int audrec_aac_process_eos(struct audio_aac_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audrec_pcm_send_data(audio, 0); +done: + return rc; +} +static ssize_t audaac_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_aac_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int rc = 0, eos_condition = AUDPREPROC_AAC_EOS_NONE; + unsigned short mfield_size = 0; + int write_count = 0; + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + /* if supplied count is more than driver buffer size + * then only copy driver buffer size + */ + if (count > frame->size) + count = frame->size; + + write_count = count; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] & + AUDPREPROC_AAC_EOS_FLG_MASK) { + eos_condition = AUDPREPROC_AAC_EOS_SET; + MM_DBG("EOS SET\n"); + if (mfield_size == count) { + buf += mfield_size; + eos_condition = 0; + goto exit; + } else + cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] &= + ~AUDPREPROC_AAC_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audrec_pcm_send_data(audio, 0); + else { + audrec_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_AAC_EOS_SET) + rc = audrec_aac_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audaac_in_release(struct inode *inode, struct file *file) +{ + struct audio_aac_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audaac_in_disable(audio); + audaac_in_flush(audio); + msm_adsp_put(audio->audrec); + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \ + (audio->out_data)) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_aac_in the_audio_aac_in; + +static int audaac_in_open(struct inode *inode, struct file *file) +{ + struct audio_aac_in *audio = &the_audio_aac_in; + int rc; + int encid; + int dma_size = 0; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + dma_size = NT_DMASZ; + MM_DBG("Opened for non tunnel mode encoding\n"); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + dma_size = DMASZ; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + MM_ERR("Invalid mode\n"); + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025; + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025; + + /* For AAC, bit rate hard coded, default settings is + * sample rate (11025) x channel count (1) x recording quality (1.75) + * = 19293 bps */ + audio->bit_rate = 19293; + audio->record_quality = 0x1c00; + + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (AAC_FRAME_SIZE + 14); + else + audio->buffer_size = (FRAME_SIZE - 8); + audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_AAC | audio->mode; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + } + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_aac_adsp_ops, audio); + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_aac_adsp_ops, audio); + if (rc) { + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + goto done; + } + } + + audio->dsp_cnt = 0; + audio->stopped = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audaac_in_flush(audio); + audaac_out_flush(audio); + + audio->phys = allocate_contiguous_ebi_nomap(dma_size, SZ_4K); + if (audio->phys) { + audio->map_v_read = ioremap( + audio->phys, dma_size); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map DMA buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto evt_error; + } + audio->data = audio->map_v_read; + } else { + MM_ERR("could not allocate read buffers\n"); + rc = -ENOMEM; + goto evt_error; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + + audio->out_data = NULL; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, + SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate write buffers\n"); + rc = -ENOMEM; + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + goto evt_error; + } else { + audio->map_v_write = ioremap( + audio->out_phys, BUFFER_SIZE); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address\n"); + rc = -ENOMEM; + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + free_contiguous_memory_by_paddr(\ + audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, (int)audio->out_data); + } + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (unsigned int)audio->out[0].data, + (unsigned int)audio->out[1].data); + audio->mfield = NT_FRAME_HEADER_SIZE; + audio->out_frame_cnt++; + } + file->private_data = audio; + audio->opened = 1; + +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_aac_in_fops = { + .owner = THIS_MODULE, + .open = audaac_in_open, + .release = audaac_in_release, + .read = audaac_in_read, + .write = audaac_in_write, + .fsync = audaac_in_fsync, + .unlocked_ioctl = audaac_in_ioctl, +}; + +static struct miscdevice audaac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_aac_in_fops, +}; + +static int __init audaac_in_init(void) +{ + mutex_init(&the_audio_aac_in.lock); + mutex_init(&the_audio_aac_in.read_lock); + spin_lock_init(&the_audio_aac_in.dsp_lock); + init_waitqueue_head(&the_audio_aac_in.wait); + init_waitqueue_head(&the_audio_aac_in.wait_enable); + mutex_init(&the_audio_aac_in.write_lock); + init_waitqueue_head(&the_audio_aac_in.write_wait); + return misc_register(&audaac_in_misc); +} +device_initcall(audaac_in_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c new file mode 100644 index 00000000000..1a1002e705f --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c @@ -0,0 +1,1630 @@ +/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c + * + * amrnb audio decoder device + * + * Copyright (c) 2008-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#define BUFSZ 1024 /* Hold minimum 700ms voice data and 14 bytes of meta in*/ +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AMRNB 10 + +#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and 24 bytes of meta out*/ +#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDAMRNB_METAFIELD_MASK 0xFFFF0000 +#define AUDAMRNB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDAMRNB_EOS_FLG_MASK 0x01 +#define AUDAMRNB_EOS_NONE 0x0 /* No EOS detected */ +#define AUDAMRNB_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDAMRNB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audamrnb_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audamrnb_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; + uint8_t buf_refresh:1; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int rmt_resource_released; + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audamrnb_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +struct audpp_cmd_cfg_adec_params_amrnb { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)) ; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audamrnb_send_data(struct audio *audio, unsigned needed); +static void audamrnb_config_hostpcm(struct audio *audio); +static void audamrnb_buffer_refresh(struct audio *audio); +static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrnb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_AMRNB; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_AMRNB; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audamrnb_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for AMRNB \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audamrnb_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audamrnb_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audamrnb_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audamrnb_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audamrnb_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder\n"); + } +} + +static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + if (audio->pcm_feedback) { + audamrnb_config_hostpcm(audio); + audamrnb_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audamrnb_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_amrnb = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_amrnb cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDAMRNB_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audamrnb_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ) + + (audio->mfield ? 24 : 0); + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audamrnb_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audamrnb_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audamrnb_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audamrnb_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audamrnb_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audamrnb_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audamrnb_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audamrnb_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audamrnb_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audamrnb_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrnb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audamrnb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audamrnb_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audamrnb_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audamrnb_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audamrnb_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrnb_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audamrnb_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audamrnb_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audamrnb_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audamrnb_disable(audio); + audamrnb_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audamrnb_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + audio->mfield = config.meta_field; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = 8000; + config.channel_count = 1; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read buf\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr 0x%08x kernel \ + addr 0x%08x\n", audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audamrnb_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + /* order reads from the output buffer */ + rmb(); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audamrnb_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audamrnb_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audamrnb_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audamrnb_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDAMRNB_EOS_NONE; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + + MM_DBG("buffer available\n"); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer + * contains just meta field + */ + if (cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] & + AUDAMRNB_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDAMRNB_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] &= + ~AUDAMRNB_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = (xfer + mfield_size); + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + audamrnb_send_data(audio, 0); + + } + if (eos_condition == AUDAMRNB_EOS_SET) + rc = audamrnb_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audamrnb_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audamrnb_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audamrnb_flush(audio); + audamrnb_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audamrnb_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrnb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audamrnb_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audamrnb_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audamrnb_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audamrnb_suspend(struct early_suspend *h) +{ + struct audamrnb_suspend_ctl *ctl = + container_of(h, struct audamrnb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrnb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audamrnb_resume(struct early_suspend *h) +{ + struct audamrnb_suspend_ctl *ctl = + container_of(h, struct audamrnb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrnb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audamrnb_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audamrnb_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].used %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audamrnb_debug_fops = { + .read = audamrnb_debug_read, + .open = audamrnb_debug_open, +}; +#endif + +static int audamrnb_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audamrnb_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrnb_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_AMRNB; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap( + audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, freeing \ + instance 0x%08x freeing\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + } + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_amrnb, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for AMRNB session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + + audamrnb_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrnb_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audamrnb_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audamrnb_resume; + audio->suspend_ctl.node.suspend = audamrnb_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDAMRNB_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audamrnb_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audamrnb_open, + .release = audamrnb_release, + .read = audamrnb_read, + .write = audamrnb_write, + .unlocked_ioctl = audamrnb_ioctl, + .fsync = audamrnb_fsync, +}; + +struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +static int __init audamrnb_init(void) +{ + return misc_register(&audio_amrnb_misc); +} + +static void __exit audamrnb_exit(void) +{ + misc_deregister(&audio_amrnb_misc); +} + +module_init(audamrnb_init); +module_exit(audamrnb_exit); + +MODULE_DESCRIPTION("MSM AMR-NB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c new file mode 100644 index 00000000000..ac2b5ca9fc8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c @@ -0,0 +1,1484 @@ +/* arch/arm/mach-msm/qdsp5/audio_amrnb_in.c + * + * amrnb encoder device + * + * Copyright (c) 2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This code is based in part on arch/arm/mach-msm/qdsp5/audio_in.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + * + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define FRAME_HEADER_SIZE 8 /* 8 bytes frame header */ +#define NT_FRAME_HEADER_SIZE 24 /* 24 bytes frame header */ +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM 8 +#define AMRNB_FRAME_SIZE 36 /* 36 bytes data */ +/*Tunnel mode : 1536 bytes data + 8 byte header*/ +#define FRAME_SIZE (AMRNB_FRAME_SIZE + FRAME_HEADER_SIZE) +/* 1536 bytes data + 24 meta field*/ +#define NT_FRAME_SIZE (AMRNB_FRAME_SIZE + NT_FRAME_HEADER_SIZE) +#define DMASZ (FRAME_SIZE * FRAME_NUM) +#define NT_DMASZ (NT_FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM 2 +#define OUT_BUFFER_SIZE (4 * 1024 + NT_FRAME_HEADER_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + +/* Offset from beginning of buffer*/ +#define AUDPREPROC_AMRNB_EOS_FLG_OFFSET 0x0A +#define AUDPREPROC_AMRNB_EOS_FLG_MASK 0x01 +#define AUDPREPROC_AMRNB_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_AMRNB_EOS_SET 0x1 /* EOS set in meta field */ + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_amrnb_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + uint8_t mfield; /* meta field embedded in data */ + uint8_t wflush; /*write flush */ + uint8_t rflush; /*read flush*/ + uint32_t out_frame_cnt; + + struct msm_adsp_module *audrec; + struct msm_adsp_module *audpre; + + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; + uint32_t enc_type; /* 0 for WAV ,1 for AAC,10 for AMRNB */ + uint32_t mode; /* T or NT Mode*/ + struct msm_audio_amrnb_enc_config amrnb_enc_cfg; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + unsigned short samp_rate_index; + uint32_t audrec_obj_idx ; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_write; + + uint8_t opened; + uint8_t enabled; + uint8_t running; + uint8_t stopped; /* set when stopped, cleared on flush */ +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; +} __packed; + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __packed; + +struct amrnb_encoded_meta_out { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audio_send_queue_pre(audio, cmd, len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) + +#define audio_send_queue_recbs(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +static int audamrnb_in_dsp_enable(struct audio_amrnb_in *audio, int enable); +static int audamrnb_in_encparam_config(struct audio_amrnb_in *audio); +static int audamrnb_in_encmem_config(struct audio_amrnb_in *audio); +static int audamrnb_in_dsp_read_buffer(struct audio_amrnb_in *audio, + uint32_t read_cnt); +static void audamrnb_in_flush(struct audio_amrnb_in *audio); + +static void audamrnb_in_get_dsp_frames(struct audio_amrnb_in *audio); +static int audpcm_config(struct audio_amrnb_in *audio); +static void audamrnb_out_flush(struct audio_amrnb_in *audio); +static int audamrnb_in_routing_mode_config(struct audio_amrnb_in *audio); +static void audrec_pcm_send_data(struct audio_amrnb_in *audio, unsigned needed); +static void audamrnb_nt_in_get_dsp_frames(struct audio_amrnb_in *audio); +static void audamrnb_in_flush(struct audio_amrnb_in *audio); + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* must be called with audio->lock held */ +static int audamrnb_in_enable(struct audio_amrnb_in *audio) +{ + struct audmgr_config cfg; + int32_t rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + audmgr_disable(&audio->audmgr); + MM_ERR("msm_adsp_enable(audpre) failed\n"); + return -ENODEV; + } + } + if (msm_adsp_enable(audio->audrec)) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audmgr_disable(&audio->audmgr); + msm_adsp_disable(audio->audpre); + } + MM_ERR("msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audamrnb_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audamrnb_in_disable(struct audio_amrnb_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audamrnb_in_dsp_enable(audio, 0); + + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + MM_DBG("type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + MM_ERR("err_index %d\n", msg[0]); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +static void audamrnb_in_get_dsp_frames(struct audio_amrnb_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - + sizeof(*frame)); + spin_lock_irqsave(&audio->dsp_lock, flags); + + /* Send Complete Transcoded Data, not actual frame part */ + audio->in[index].size = FRAME_SIZE - (sizeof(*frame)); + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("in_count = %d\n", audio->in_count); + } else + audio->in_count++; + + audamrnb_in_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audamrnb_nt_in_get_dsp_frames(struct audio_amrnb_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + +static int audrec_pcm_buffer_ptr_refresh(struct audio_amrnb_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == NT_FRAME_HEADER_SIZE) + len = len / 2; + else + len = (len + NT_FRAME_HEADER_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_config(struct audio_amrnb_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE; + cmd.sampling_freq = convert_samp_index(audio->samp_rate); + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + + +static int audamrnb_in_routing_mode_config(struct audio_amrnb_in *audio) +{ + struct audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ROUTING_MODE; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + cmd.routing_mode = 1; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_amrnb_in *audio = data; + if (data) + audio = data; + else { + MM_ERR("invalid data for event %x\n", id); + return; + } + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: { + struct audrec_msg_cmd_cfg_done_msg cmd_cfg_done_msg; + getevent(&cmd_cfg_done_msg, AUDREC_MSG_CMD_CFG_DONE_MSG_LEN); + if (cmd_cfg_done_msg.audrec_enc_type & \ + AUDREC_MSG_CFG_DONE_ENC_ENA) { + audio->audrec_obj_idx = cmd_cfg_done_msg.audrec_obj_idx; + MM_DBG("CFG ENABLED\n"); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audamrnb_in_routing_mode_config(audio); + } else { + audamrnb_in_encmem_config(audio); + } + } else { + MM_DBG("CFG SLEEP\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG: { + struct audrec_msg_cmd_routing_mode_done_msg \ + routing_msg; + getevent(&routing_msg, AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG); + MM_DBG("AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG"); + if (routing_msg.configuration == 0) { + MM_ERR("routing configuration failed\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } else + audamrnb_in_encmem_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { + MM_DBG("AREC_MEM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audamrnb_in_encparam_config(audio); + else + audpcm_config(audio); + break; + } + + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_DBG("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audamrnb_in_encparam_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_DBG("AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG\n"); + audio->running = 1; + wake_up(&audio->wait_enable); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: { + struct audrec_msg_no_ext_pkt_avail_msg err_msg; + getevent(&err_msg, AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN); + MM_DBG("NO_EXT_PKT_AVAILABLE_MSG %x\n",\ + err_msg.audrec_err_id); + break; + } + case AUDREC_MSG_PACKET_READY_MSG: { + struct audrec_msg_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt msw %d \ + write cnt lsw %d read cnt msw %d read cnt lsw %d \n",\ + pkt_ready_msg.pkt_counter_msw, \ + pkt_ready_msg.pkt_counter_lsw, \ + pkt_ready_msg.pkt_read_cnt_msw, \ + pkt_ready_msg.pkt_read_cnt_lsw); + + audamrnb_in_get_dsp_frames(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audamrnb_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +struct msm_adsp_ops audpre_amrnb_adsp_ops = { + .event = audpre_dsp_event, +}; + +struct msm_adsp_ops audrec_amrnb_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audamrnb_in_dsp_enable(struct audio_amrnb_in *audio, int enable) +{ + struct audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ENC_CFG; + cmd.audrec_enc_type = (audio->enc_type & 0xFF) | + (enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS); + /* Don't care on enable, required on disable */ + cmd.audrec_obj_idx = audio->audrec_obj_idx; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audamrnb_in_encmem_config(struct audio_amrnb_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + uint8_t n; + uint16_t header_len = 0; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG; + cmd.audrec_obj_idx = audio->audrec_obj_idx; + /* Rate at which packet complete message comes */ + cmd.audrec_up_pkt_intm_cnt = 1; + cmd.audrec_extpkt_buffer_msw = audio->phys >> 16; + cmd.audrec_extpkt_buffer_lsw = audio->phys; + /* Max Buffer no available for frames */ + cmd.audrec_extpkt_buffer_num = FRAME_NUM; + + /* prepare buffer pointers: + * T:36 bytes amrnb packet + 4 halfword header + * NT:36 bytes amrnb packet + 12 halfword header + */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + header_len = FRAME_HEADER_SIZE/2; + else + header_len = NT_FRAME_HEADER_SIZE/2; + + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + header_len; + data += (AMRNB_FRAME_SIZE/2) + header_len; + MM_DBG("0x%8x\n", (uint32_t)(audio->in[n].data - header_len*2)); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audamrnb_in_encparam_config(struct audio_amrnb_in *audio) +{ + struct audrec_cmd_arecparam_amrnb_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG; + cmd.common.audrec_obj_idx = audio->audrec_obj_idx; + cmd.samp_rate_idx = audio->samp_rate_index; /* 8k Sampling rate */ + cmd.voicememoencweight1 = audio->amrnb_enc_cfg.voicememoencweight1; + cmd.voicememoencweight2 = audio->amrnb_enc_cfg.voicememoencweight2; + cmd.voicememoencweight3 = audio->amrnb_enc_cfg.voicememoencweight3; + cmd.voicememoencweight4 = audio->amrnb_enc_cfg.voicememoencweight4; + cmd.update_mode = 0x8000 | 0x0000; + cmd.dtx_mode = audio->amrnb_enc_cfg.dtx_mode_enable; + cmd.test_mode = audio->amrnb_enc_cfg.test_mode_enable; + cmd.used_mode = audio->amrnb_enc_cfg.enc_mode; + + MM_DBG("cmd.common.cmd_id = 0x%4x\n", cmd.common.cmd_id); + MM_DBG("cmd.common.audrec_obj_idx = 0x%4x\n", + cmd.common.audrec_obj_idx); + MM_DBG("cmd.samp_rate_idx = 0x%4x\n", cmd.samp_rate_idx); + MM_DBG("cmd.voicememoencweight1 = 0x%4x\n", + cmd.voicememoencweight1); + MM_DBG("cmd.voicememoencweight2 = 0x%4x\n", + cmd.voicememoencweight2); + MM_DBG("cmd.voicememoencweight3 = 0x%4x\n", + cmd.voicememoencweight3); + MM_DBG("cmd.voicememoencweight4 = 0x%4x\n", + cmd.voicememoencweight4); + MM_DBG("cmd.update_mode = 0x%4x\n", cmd.update_mode); + MM_DBG("cmd.dtx_mode = 0x%4x\n", cmd.dtx_mode); + MM_DBG("cmd.test_mode = 0x%4x\n", cmd.test_mode); + MM_DBG("cmd.used_mode = 0x%4x\n", cmd.used_mode); + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audamrnb_flush_command(struct audio_amrnb_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} +static int audamrnb_in_dsp_read_buffer(struct audio_amrnb_in *audio, + uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + cmd.type = audio->audrec_obj_idx; + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audamrnb_ioport_reset(struct audio_amrnb_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audamrnb_in_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audamrnb_out_flush(audio); + mutex_unlock(&audio->read_lock); +} + +static void audamrnb_in_flush(struct audio_amrnb_in *audio) +{ + uint8_t i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + audio->eos_ack = 0; + for (i = FRAME_NUM-1; i >= 0; i--) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audamrnb_out_flush(struct audio_amrnb_in *audio) +{ + uint8_t i; + + audio->out_head = 0; + audio->out_tail = 0; + audio->out_count = 0; + for (i = OUT_FRAME_NUM-1; i >= 0; i--) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } +} + +/* ------------------- device --------------------- */ +static long audamrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_amrnb_in *audio = file->private_data; + int32_t rc = 0; + + MM_DBG("\n"); + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + rc = audamrnb_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_INFO("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + rc = audamrnb_in_disable(audio); + audio->stopped = 1; + break; + } + case AUDIO_FLUSH: { + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audamrnb_ioport_reset(audio); + if (audio->running) { + audamrnb_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + } else { + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + } + cfg.sample_rate = convert_samp_index(audio->samp_rate); + cfg.channel_count = 1; + cfg.type = 0; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) { + rc = -EINVAL; + break; + } + } else { + if (cfg.buffer_size != (AMRNB_FRAME_SIZE + 14)) { + rc = -EINVAL; + break; + } + } + audio->buffer_size = cfg.buffer_size; + break; + } + + case AUDIO_GET_AMRNB_ENC_CONFIG: { + if (copy_to_user((void *)arg, &audio->amrnb_enc_cfg, + sizeof(audio->amrnb_enc_cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG: { + struct msm_audio_amrnb_enc_config cfg; + if (copy_from_user + (&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + } else + rc = 0; + audio->amrnb_enc_cfg.voicememoencweight1 = + cfg.voicememoencweight1; + audio->amrnb_enc_cfg.voicememoencweight2 = + cfg.voicememoencweight2; + audio->amrnb_enc_cfg.voicememoencweight3 = + cfg.voicememoencweight3; + audio->amrnb_enc_cfg.voicememoencweight4 = + cfg.voicememoencweight4; + audio->amrnb_enc_cfg.dtx_mode_enable = cfg.dtx_mode_enable; + audio->amrnb_enc_cfg.test_mode_enable = cfg.test_mode_enable; + audio->amrnb_enc_cfg.enc_mode = cfg.enc_mode; + /* Run time change of Param */ + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audamrnb_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_amrnb_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int32_t rc = 0; + struct amrnb_encoded_meta_out meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG("count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->rflush); + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, + (sizeof(struct amrnb_encoded_meta_out) - \ + sizeof(uint16_t))); + meta_field.metadata_len = + sizeof(struct amrnb_encoded_meta_out); + if (copy_to_user((char *)start, (char *)&meta_field, + sizeof(struct amrnb_encoded_meta_out))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_ERR("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct amrnb_encoded_meta_out); + count -= sizeof(struct amrnb_encoded_meta_out); + } + if (count >= size) { + /* order the reads on the buffer */ + dma_coherent_post_ops(); + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) { + if (!audio->eos_ack) { + MM_DBG("sending read ptr command \ + %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audamrnb_in_dsp_read_buffer(audio, + audio->dsp_cnt++); + } + } + } else { + MM_ERR("short read\n"); + break; + } + + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static void audrec_pcm_send_data(struct audio_amrnb_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audrec_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static int audamrnb_in_fsync(struct file *file, loff_t a, loff_t b, int datasync) + +{ + struct audio_amrnb_in *audio = file->private_data; + int32_t rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + +int audrec_amrnb_process_eos(struct audio_amrnb_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int32_t rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audrec_pcm_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audamrnb_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_amrnb_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int32_t rc = 0, eos_condition = AUDPREPROC_AMRNB_EOS_NONE; + unsigned short mfield_size = 0; + int32_t write_count = 0; + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + /* if supplied count is more than driver buffer size + * then only copy driver buffer size + */ + if (count > frame->size) + count = frame->size; + + write_count = count; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_AMRNB_EOS_FLG_OFFSET] & + AUDPREPROC_AMRNB_EOS_FLG_MASK) { + eos_condition = AUDPREPROC_AMRNB_EOS_SET; + MM_DBG("EOS SET\n"); + if (mfield_size == count) { + buf += mfield_size; + eos_condition = 0; + goto exit; + } else + cpy_ptr[AUDPREPROC_AMRNB_EOS_FLG_OFFSET] &= + ~AUDPREPROC_AMRNB_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audrec_pcm_send_data(audio, 0); + else { + audrec_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_AMRNB_EOS_SET) + rc = audrec_amrnb_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audamrnb_in_release(struct inode *inode, struct file *file) +{ + struct audio_amrnb_in *audio = file->private_data; + int32_t dma_size = 0; + mutex_lock(&audio->lock); + audamrnb_in_disable(audio); + audamrnb_in_flush(audio); + msm_adsp_put(audio->audrec); + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \ + (audio->out_data)) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + if (audio->data) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + dma_size = DMASZ; + else + dma_size = NT_DMASZ; + + dma_free_coherent(NULL, + dma_size, audio->data, audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_amrnb_in the_audio_amrnb_in; + +static int audamrnb_in_open(struct inode *inode, struct file *file) +{ + struct audio_amrnb_in *audio = &the_audio_amrnb_in; + int32_t rc; + int encid; + int32_t dma_size = 0; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + dma_size = NT_DMASZ; + MM_DBG("Opened for non tunnel mode encoding\n"); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + dma_size = DMASZ; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + MM_ERR("Invalid mode\n"); + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_8000, + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_8000; + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (AMRNB_FRAME_SIZE + 14); + else + audio->buffer_size = (FRAME_SIZE - 8); + audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_AMRNB | audio->mode; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + } + audio->amrnb_enc_cfg.voicememoencweight1 = 0x0000; + audio->amrnb_enc_cfg.voicememoencweight2 = 0x0000; + audio->amrnb_enc_cfg.voicememoencweight3 = 0x4000; + audio->amrnb_enc_cfg.voicememoencweight4 = 0x0000; + audio->amrnb_enc_cfg.dtx_mode_enable = 0; + audio->amrnb_enc_cfg.test_mode_enable = 0; + audio->amrnb_enc_cfg.enc_mode = 7; + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_amrnb_adsp_ops, audio); + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_amrnb_adsp_ops, audio); + if (rc) { + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + goto done; + } + } + audio->dsp_cnt = 0; + audio->stopped = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audamrnb_in_flush(audio); + audamrnb_out_flush(audio); + /* used dma_allco_coherent for backward compatibility with 7x27 */ + audio->data = dma_alloc_coherent(NULL, dma_size, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + MM_ERR("Unable to allocate DMA buffer\n"); + goto evt_error; + } + + audio->out_data = NULL; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, + SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate write buffers\n"); + rc = -ENOMEM; + dma_free_coherent(NULL, + dma_size, audio->data, audio->phys); + goto evt_error; + } else { + audio->map_v_write = ioremap( + audio->out_phys, BUFFER_SIZE); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address\n"); + rc = -ENOMEM; + dma_free_coherent(NULL, + dma_size, audio->data, audio->phys); + free_contiguous_memory_by_paddr(\ + audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, + (uint32_t)audio->out_data); + } + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (uint32_t)audio->out[0].data, + (uint32_t)audio->out[1].data); + audio->mfield = NT_FRAME_HEADER_SIZE; + audio->out_frame_cnt++; + } + file->private_data = audio; + audio->opened = 1; + +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audamrnb_in_open, + .release = audamrnb_in_release, + .read = audamrnb_in_read, + .write = audamrnb_in_write, + .fsync = audamrnb_in_fsync, + .unlocked_ioctl = audamrnb_in_ioctl, +}; + +struct miscdevice audamrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_fops, +}; + +#ifdef CONFIG_DEBUG_FS +static ssize_t audamrnb_in_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audamrnb_in_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int32_t debug_bufmax = 1024; + static char buffer[1024]; + int32_t n = 0, i; + struct audio_amrnb_in *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "audrec_obj_idx %d\n", audio->audrec_obj_idx); + n += scnprintf(buffer + n, debug_bufmax - n, + "dsp_cnt %d \n", audio->dsp_cnt); + n += scnprintf(buffer + n, debug_bufmax - n, + "in_count %d \n", audio->in_count); + for (i = 0; i < FRAME_NUM; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "audio->in[%d].size %d \n", i, audio->in[i].size); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when record halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_size %d \n", audio->buffer_size); + n += scnprintf(buffer + n, debug_bufmax - n, + "in_head %d \n", audio->in_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "in_tail %d \n", audio->in_tail); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audamrnb_in_debug_fops = { + .read = audamrnb_in_debug_read, + .open = audamrnb_in_debug_open, +}; +#endif + +static int __init audamrnb_in_init(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + mutex_init(&the_audio_amrnb_in.lock); + mutex_init(&the_audio_amrnb_in.read_lock); + spin_lock_init(&the_audio_amrnb_in.dsp_lock); + init_waitqueue_head(&the_audio_amrnb_in.wait); + init_waitqueue_head(&the_audio_amrnb_in.wait_enable); + mutex_init(&the_audio_amrnb_in.write_lock); + init_waitqueue_head(&the_audio_amrnb_in.write_wait); + +#ifdef CONFIG_DEBUG_FS + dentry = debugfs_create_file("msm_amrnb_in", S_IFREG | S_IRUGO, NULL, + (void *) &the_audio_amrnb_in, &audamrnb_in_debug_fops); + + if (IS_ERR(dentry)) + MM_ERR("debugfs_create_file failed\n"); +#endif + return misc_register(&audamrnb_in_misc); +} + +static void __exit audamrnb_in_exit(void) +{ + misc_deregister(&audamrnb_in_misc); +} + +module_init(audamrnb_in_init); +module_exit(audamrnb_in_exit); + +MODULE_DESCRIPTION("MSM AMRNB Encoder driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_amrwb.c b/arch/arm/mach-msm/qdsp5/audio_amrwb.c new file mode 100644 index 00000000000..b85b153a0db --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_amrwb.c @@ -0,0 +1,1698 @@ +/* linux/arch/arm/mach-msm/qdsp5/audio_amrwb.c + * + * amrwb audio decoder device + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#define BUFSZ 4110 /* Hold minimum 700ms voice data and 14 bytes of meta in*/ +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AMRWB 11 + +#define PCM_BUFSZ_MIN 8216 /* 100ms worth of data and 24 bytes of meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDAMRWB_METAFIELD_MASK 0xFFFF0000 +#define AUDAMRWB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDAMRWB_EOS_FLG_MASK 0x01 +#define AUDAMRWB_EOS_NONE 0x0 /* No EOS detected */ +#define AUDAMRWB_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDAMRWB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audamrwb_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audamrwb_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + struct audmgr audmgr; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int rmt_resource_released; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audamrwb_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audamrwb_send_data(struct audio *audio, unsigned needed); +static void audamrwb_config_hostpcm(struct audio *audio); +static void audamrwb_buffer_refresh(struct audio *audio); +static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrwb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_AMRWB; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_AMRWB; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audamrwb_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for AMRWB \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_AMR_WB; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audamrwb_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audamrwb_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audamrwb_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audamrwb_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audamrwb_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audamrwb_update_pcm_buf_entry(audio, msg); + break; + + default: + MM_ERR("unexpected message from decoder\n"); + } +} + +static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason=0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play\n"); + if (audio->pcm_feedback) { + audamrwb_config_hostpcm(audio); + audamrwb_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_DBG("unknown decoder status\n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audamrwb_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + default: + MM_DBG("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_amrwb = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRWB; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_amrwb cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.stereo_cfg = audio->out_channel_mode; + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDAMRWB_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audamrwb_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audamrwb_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audamrwb_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audamrwb_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audamrwb_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audamrwb_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audamrwb_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audamrwb_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audamrwb_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audamrwb_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audamrwb_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrwb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audamrwb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audamrwb_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audamrwb_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audamrwb_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audamrwb_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrwb_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audamrwb_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audamrwb_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audamrwb_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audamrwb_disable(audio); + audamrwb_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG(" AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audamrwb_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) + config.channel_count = + AUDPP_CMD_PCM_INTF_MONO_V; + else if (config.channel_count == 2) + config.channel_count = + AUDPP_CMD_PCM_INTF_STEREO_V; + else + rc = -EINVAL; + audio->out_channel_mode = config.channel_count; + audio->out_sample_rate = config.sample_rate; + audio->mfield = config.meta_field; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == + AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", config.buffer_count * + config.buffer_size); + audio->read_phys = allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map mem for read buf\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = audio->map_v_read; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr 0x%08x \ + kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audamrwb_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audamrwb_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audamrwb_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("count %d\n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", + audio->read_next); + + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_ERR("kick start pcm feedback again\n"); + audamrwb_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audamrwb_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + char *buf_ptr; + int rc = 0; + + MM_DBG("signal input EOS reserved=%d\n", audio->reserved); + if (audio->reserved) { + MM_DBG("Pass reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + audio->reserved = 0; + frame->used = 2; + audamrwb_send_data(audio, 0); + } + + MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n", + audio->out[0].used, audio->out[1].used, audio->out_needed); + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audamrwb_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audamrwb_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDAMRWB_EOS_NONE; + unsigned short mfield_size = 0; + unsigned dsize; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + + MM_DBG("buffer available\n"); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer + * contains just meta field + */ + if (cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] & + AUDAMRWB_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDAMRWB_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] &= + ~AUDAMRWB_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + ((frame->size - mfield_size) - 1) : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audamrwb_send_data(audio, 0); + } + } + MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition, + (int) buf, (int) start); + if (eos_condition == AUDAMRWB_EOS_SET) + rc = audamrwb_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audamrwb_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audamrwb_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audamrwb_flush(audio); + audamrwb_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audamrwb_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrwb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audamrwb_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audamrwb_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audamrwb_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audamrwb_suspend(struct early_suspend *h) +{ + struct audamrwb_suspend_ctl *ctl = + container_of(h, struct audamrwb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrwb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audamrwb_resume(struct early_suspend *h) +{ + struct audamrwb_suspend_ctl *ctl = + container_of(h, struct audamrwb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrwb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audamrwb_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audamrwb_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].used %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audamrwb_debug_fops = { + .read = audamrwb_debug_read, + .open = audamrwb_debug_open, +}; +#endif + +static int audamrwb_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audamrwb_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrwb_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("No memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_AMRWB; + if (file->f_mode & FMODE_READ) + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + else + dec_attrb |= MSM_AUD_MODE_TUNNEL; + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_amrwb, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for AMRWB session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + audio->vol_pan.pan = 0x0; + audio->eq_enable = 0; + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audamrwb_flush(audio); + + file->private_data = audio; + audio->opened = 1; + audio->event_abort = 0; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrwb_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audamrwb_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audamrwb_resume; + audio->suspend_ctl.node.suspend = audamrwb_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDAMRWB_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audamrwb_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwb_fops = { + .owner = THIS_MODULE, + .open = audamrwb_open, + .release = audamrwb_release, + .read = audamrwb_read, + .write = audamrwb_write, + .unlocked_ioctl = audamrwb_ioctl, + .fsync = audamrwb_fsync, +}; + +struct miscdevice audio_amrwb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb", + .fops = &audio_amrwb_fops, +}; + +static int __init audamrwb_init(void) +{ + return misc_register(&audio_amrwb_misc); +} + +static void __exit audamrwb_exit(void) +{ + misc_deregister(&audio_amrwb_misc); +} + +module_init(audamrwb_init); +module_exit(audamrwb_exit); + +MODULE_DESCRIPTION("MSM AMR-WB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c new file mode 100644 index 00000000000..489929ba1ce --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c @@ -0,0 +1,1623 @@ +/* arch/arm/mach-msm/audio_evrc.c + * + * Copyright (c) 2008-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This code also borrows from audio_aac.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +/* Hold 30 packets of 24 bytes each and 14 bytes of meta in */ +#define BUFSZ 734 +#define DMASZ (BUFSZ * 2) + +#define AUDDEC_DEC_EVRC 12 + +#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and + and 24 bytes of meta out */ +#define PCM_BUF_MAX_COUNT 5 +/* DSP only accepts 5 buffers at most + * but support 2 buffers currently + */ +#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */ + +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDEVRC_METAFIELD_MASK 0xFFFF0000 +#define AUDEVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDEVRC_EOS_FLG_MASK 0x01 +#define AUDEVRC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDEVRC_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDEVRC_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audevrc_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audevrc_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; + uint8_t buf_refresh:1; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int rmt_resource_released; + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audevrc_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audevrc_send_data(struct audio *audio, unsigned needed); +static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audevrc_config_hostpcm(struct audio *audio); +static void audevrc_buffer_refresh(struct audio *audio); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audevrc_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_EVRC; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_EVRC; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audevrc_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for EVRC \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_EVRC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audevrc_disable(struct audio *audio) +{ + int rc = 0; + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ + +static void audevrc_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr + == payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audevrc_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audevrc_send_data(audio, 1); + break; + case AUDPLAY_MSG_BUFFER_UPDATE: + MM_DBG("\n"); /* Macro prints the file name and function */ + audevrc_update_pcm_buf_entry(audio, msg); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + default: + MM_ERR("unexpected message from decoder \n"); + } +} + +static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + if (audio->pcm_feedback) { + audevrc_config_hostpcm(audio); + audevrc_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK\n"); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audevrc_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_evrc = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_evrc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = sizeof(cmd); + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDEVRC_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audevrc_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audevrc_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audevrc_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audevrc_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audevrc_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audevrc_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audevrc_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audevrc_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audevrc_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audevrc_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audevrc_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audevrc_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audevrc_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + + +static long audevrc_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audevrc_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audevrc_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audevrc_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audevrc_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audevrc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audevrc_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audevrc_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audevrc_disable(audio); + audevrc_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audevrc_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + audio->mfield = config.meta_field; + rc = 0; + MM_DBG("AUDIO_SET_CONFIG applicable only \ + for meta field configuration\n"); + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = 8000; + config.channel_count = 1; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map mem" + " for read buf\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audevrc_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + if (!audio->pcm_feedback) { + return 0; + /* PCM feedback is not enabled. Nothing to read */ + } + mutex_lock(&audio->read_lock); + MM_DBG("\n"); /* Macro prints the file name and function */ + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + MM_DBG("wait terminated \n"); + if (rc < 0) + break; + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + /* order reads from the output buffer */ + rmb(); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment + */ + + } + } + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audevrc_buffer_refresh(audio); + } + mutex_unlock(&audio->read_lock); + if (buf > start) + rc = buf - start; + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audevrc_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audevrc_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audevrc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + unsigned short mfield_size = 0; + int rc = 0, eos_condition = AUDEVRC_EOS_NONE; + + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, + mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] & + AUDEVRC_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDEVRC_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] &= + ~AUDEVRC_EOS_FLG_MASK; + } + /* Check EOS to see if */ + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer + mfield_size; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + audevrc_send_data(audio, 0); + } + if (eos_condition == AUDEVRC_EOS_SET) + rc = audevrc_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audevrc_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audevrc_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audevrc_flush(audio); + audevrc_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audevrc_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audevrc_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audevrc_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audevrc_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audevrc_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audevrc_suspend(struct early_suspend *h) +{ + struct audevrc_suspend_ctl *ctl = + container_of(h, struct audevrc_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audevrc_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audevrc_resume(struct early_suspend *h) +{ + struct audevrc_suspend_ctl *ctl = + container_of(h, struct audevrc_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audevrc_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audevrc_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audevrc_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audevrc_debug_fops = { + .read = audevrc_debug_read, + .open = audevrc_debug_open, +}; +#endif + +static int audevrc_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audevrc_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_evrc_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_EVRC; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_evrc, audio); + + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for EVRC session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x3FFF; + + audevrc_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_evrc_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audevrc_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audevrc_resume; + audio->suspend_ctl.node.suspend = audevrc_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDEVRC_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audevrc_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audevrc_open, + .release = audevrc_release, + .read = audevrc_read, + .write = audevrc_write, + .unlocked_ioctl = audevrc_ioctl, + .fsync = audevrc_fsync, +}; + +struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +static int __init audevrc_init(void) +{ + return misc_register(&audio_evrc_misc); + +} + +static void __exit audevrc_exit(void) +{ + misc_deregister(&audio_evrc_misc); +} + +module_init(audevrc_init); +module_exit(audevrc_exit); + +MODULE_DESCRIPTION("MSM EVRC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c new file mode 100644 index 00000000000..e13b072fb31 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c @@ -0,0 +1,1396 @@ +/* arch/arm/mach-msm/qdsp5/audio_evrc_in.c + * + * evrc audio input device + * + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c, + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include +#include +#include +#include +#include + +#define FRAME_HEADER_SIZE 8 /* 8 bytes frame header */ +#define NT_FRAME_HEADER_SIZE 24 /* 24 bytes frame header */ +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM 8 +#define EVRC_FRAME_SIZE 36 /* 36 bytes data */ +/*Tunnel mode : 36 bytes data + 8 byte header*/ +#define FRAME_SIZE (EVRC_FRAME_SIZE + FRAME_HEADER_SIZE) + /* 36 bytes data + 24 meta field*/ +#define NT_FRAME_SIZE (EVRC_FRAME_SIZE + NT_FRAME_HEADER_SIZE) +#define DMASZ (FRAME_SIZE * FRAME_NUM) +#define NT_DMASZ (NT_FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM 2 +#define OUT_BUFFER_SIZE (4 * 1024 + NT_FRAME_HEADER_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + +#define AUDPREPROC_EVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer*/ +#define AUDPREPROC_EVRC_EOS_FLG_MASK 0x01 +#define AUDPREPROC_EVRC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_EVRC_EOS_SET 0x1 /* EOS set in meta field */ + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_evrc_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + int mfield; /* meta field embedded in data */ + int wflush; /*write flush */ + int rflush; /*read flush*/ + int out_frame_cnt; + + struct msm_adsp_module *audrec; + struct msm_adsp_module *audpre; + + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* Frame size (36 bytes) */ + uint32_t enc_type; /* 11 for EVRC */ + uint32_t mode; /* T or NT Mode*/ + + struct msm_audio_evrc_enc_config cfg; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + unsigned short samp_rate_index; + uint32_t audrec_obj_idx ; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + void *map_v_read; + void *map_v_write; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; +} __packed; + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __packed; + +struct evrc_encoded_meta_out { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audio_send_queue_pre(audio, cmd, len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) + +#define audio_send_queue_recbs(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +static int audevrc_in_dsp_enable(struct audio_evrc_in *audio, int enable); +static int audevrc_in_encparam_config(struct audio_evrc_in *audio); +static int audevrc_in_encmem_config(struct audio_evrc_in *audio); +static int audevrc_in_dsp_read_buffer(struct audio_evrc_in *audio, + uint32_t read_cnt); +static void audevrc_in_flush(struct audio_evrc_in *audio); + +static void audevrc_in_get_dsp_frames(struct audio_evrc_in *audio); +static int audpcm_config(struct audio_evrc_in *audio); +static void audevrc_out_flush(struct audio_evrc_in *audio); +static int audevrc_in_routing_mode_config(struct audio_evrc_in *audio); +static void audrec_pcm_send_data(struct audio_evrc_in *audio, unsigned needed); +static void audevrc_nt_in_get_dsp_frames(struct audio_evrc_in *audio); +static void audevrc_in_flush(struct audio_evrc_in *audio); + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* must be called with audio->lock held */ +static int audevrc_in_enable(struct audio_evrc_in *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + cfg.codec = RPC_AUD_DEF_CODEC_EVRC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + audmgr_disable(&audio->audmgr); + MM_ERR("msm_adsp_enable(audpre) failed\n"); + return -ENODEV; + } + } + if (msm_adsp_enable(audio->audrec)) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audmgr_disable(&audio->audmgr); + msm_adsp_disable(audio->audpre); + } + MM_ERR("msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audevrc_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audevrc_in_disable(struct audio_evrc_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audevrc_in_dsp_enable(audio, 0); + + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + audio->stopped = 1; + wake_up(&audio->wait); + msm_adsp_disable(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + MM_DBG("type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + MM_ERR("err_index %d\n", msg[0]); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +static void audevrc_in_get_dsp_frames(struct audio_evrc_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - + sizeof(*frame)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("in_count = %d\n", audio->in_count); + } else + audio->in_count++; + + audevrc_in_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audevrc_nt_in_get_dsp_frames(struct audio_evrc_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + +static int audrec_pcm_buffer_ptr_refresh(struct audio_evrc_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == NT_FRAME_HEADER_SIZE) + len = len / 2; + else + len = (len + NT_FRAME_HEADER_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_config(struct audio_evrc_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE; + cmd.sampling_freq = convert_samp_index(audio->samp_rate); + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + + +static int audevrc_in_routing_mode_config(struct audio_evrc_in *audio) +{ + struct audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ROUTING_MODE; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + cmd.routing_mode = 1; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_evrc_in *audio = NULL; + + if (data) + audio = data; + else { + MM_ERR("invalid data for event %x\n", id); + return; + } + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: { + struct audrec_msg_cmd_cfg_done_msg cmd_cfg_done_msg; + getevent(&cmd_cfg_done_msg, AUDREC_MSG_CMD_CFG_DONE_MSG_LEN); + if (cmd_cfg_done_msg.audrec_enc_type & \ + AUDREC_MSG_CFG_DONE_ENC_ENA) { + audio->audrec_obj_idx = cmd_cfg_done_msg.audrec_obj_idx; + MM_DBG("CFG ENABLED\n"); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audevrc_in_routing_mode_config(audio); + } else { + audevrc_in_encmem_config(audio); + } + } else { + MM_DBG("CFG SLEEP\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG: { + struct audrec_msg_cmd_routing_mode_done_msg \ + routing_msg; + getevent(&routing_msg, AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG); + MM_DBG("AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG"); + if (routing_msg.configuration == 0) { + MM_ERR("routing configuration failed\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } else + audevrc_in_encmem_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { + MM_DBG("AREC_MEM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audevrc_in_encparam_config(audio); + else + audpcm_config(audio); + break; + } + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_DBG("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audevrc_in_encparam_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_DBG("AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG\n"); + audio->running = 1; + wake_up(&audio->wait_enable); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: { + struct audrec_msg_no_ext_pkt_avail_msg err_msg; + getevent(&err_msg, AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN); + MM_DBG("NO_EXT_PKT_AVAILABLE_MSG %x\n",\ + err_msg.audrec_err_id); + break; + } + case AUDREC_MSG_PACKET_READY_MSG: { + struct audrec_msg_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt msw %d \ + write cnt lsw %d read cnt msw %d read cnt lsw %d \n",\ + pkt_ready_msg.pkt_counter_msw, \ + pkt_ready_msg.pkt_counter_lsw, \ + pkt_ready_msg.pkt_read_cnt_msw, \ + pkt_ready_msg.pkt_read_cnt_lsw); + + audevrc_in_get_dsp_frames(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audevrc_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +static struct msm_adsp_ops audpre_evrc_adsp_ops = { + .event = audpre_dsp_event, +}; + +static struct msm_adsp_ops audrec_evrc_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audevrc_in_dsp_enable(struct audio_evrc_in *audio, int enable) +{ + struct audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ENC_CFG; + cmd.audrec_enc_type = (audio->enc_type & 0xFF) | + (enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS); + /* Don't care */ + cmd.audrec_obj_idx = audio->audrec_obj_idx; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audevrc_in_encmem_config(struct audio_evrc_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + int header_len = 0; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG; + cmd.audrec_obj_idx = audio->audrec_obj_idx; + /* Rate at which packet complete message comes */ + cmd.audrec_up_pkt_intm_cnt = 1; + cmd.audrec_extpkt_buffer_msw = audio->phys >> 16; + cmd.audrec_extpkt_buffer_lsw = audio->phys; + /* Max Buffer no available for frames */ + cmd.audrec_extpkt_buffer_num = FRAME_NUM; + + /* prepare buffer pointers: + * T:36 bytes evrc packet + 4 halfword header + * NT:36 bytes evrc packet + 12 halfword header + */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + header_len = FRAME_HEADER_SIZE/2; + else + header_len = NT_FRAME_HEADER_SIZE/2; + + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + header_len; + data += (EVRC_FRAME_SIZE/2) + header_len; + MM_DBG("0x%8x\n", (int)(audio->in[n].data - header_len*2)); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audevrc_in_encparam_config(struct audio_evrc_in *audio) +{ + struct audrec_cmd_arecparam_evrc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG; + cmd.common.audrec_obj_idx = audio->audrec_obj_idx; + cmd.enc_min_rate = audio->cfg.min_bit_rate; + cmd.enc_max_rate = audio->cfg.max_bit_rate; + cmd.rate_modulation_cmd = 0; /* Default set to 0 */ + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audevrc_flush_command(struct audio_evrc_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audevrc_in_dsp_read_buffer(struct audio_evrc_in *audio, + uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + cmd.type = audio->audrec_obj_idx; + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audevrc_ioport_reset(struct audio_evrc_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audevrc_in_flush(audio); + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audevrc_out_flush(audio); + mutex_unlock(&audio->write_lock); +} + +static void audevrc_in_flush(struct audio_evrc_in *audio) +{ + int i; + unsigned long flags; + + audio->dsp_cnt = 0; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + audio->eos_ack = 0; + for (i = FRAME_NUM-1; i >= 0; i--) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audevrc_out_flush(struct audio_evrc_in *audio) +{ + int i; + unsigned long flags; + + audio->out_head = 0; + audio->out_count = 0; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out_tail = 0; + for (i = OUT_FRAME_NUM-1; i >= 0; i--) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static long audevrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_evrc_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + rc = audevrc_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + rc = audevrc_in_disable(audio); + break; + } + case AUDIO_FLUSH: { + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audevrc_ioport_reset(audio); + if (audio->running) { + audevrc_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + cfg.channel_count = 1; + cfg.type = 0; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) { + rc = -EINVAL; + break; + } + } else { + if (cfg.buffer_size != (EVRC_FRAME_SIZE + 14)) { + rc = -EINVAL; + break; + } + } + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate, + cfg.max_bit_rate, cfg.cdma_rate); + if (cfg.min_bit_rate > CDMA_RATE_FULL || \ + cfg.min_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid min bitrate\n"); + rc = -EFAULT; + break; + } + if (cfg.max_bit_rate > CDMA_RATE_FULL || \ + cfg.max_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid max bitrate\n"); + rc = -EFAULT; + break; + } + /* Recording Does not support Erase and Blank */ + if (cfg.cdma_rate > CDMA_RATE_FULL || + cfg.cdma_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid qcelp cdma rate\n"); + rc = -EFAULT; + break; + } + memcpy(&audio->cfg, &cfg, sizeof(cfg)); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audevrc_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_evrc_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + struct evrc_encoded_meta_out meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG("count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->rflush); + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, + (sizeof(struct evrc_encoded_meta_out) - \ + sizeof(uint16_t))); + meta_field.metadata_len = + sizeof(struct evrc_encoded_meta_out); + if (copy_to_user((char *)start, (char *)&meta_field, + sizeof(struct evrc_encoded_meta_out))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_ERR("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct evrc_encoded_meta_out); + count -= sizeof(struct evrc_encoded_meta_out); + } + if (count >= size) { + /* order the reads on the buffer */ + dma_coherent_post_ops(); + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) { + if (!audio->eos_ack) { + MM_DBG("sending read ptr command \ + %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audevrc_in_dsp_read_buffer(audio, + audio->dsp_cnt++); + } + } + } else { + MM_ERR("short read\n"); + break; + } + break; + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static void audrec_pcm_send_data(struct audio_evrc_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audrec_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static int audevrc_in_fsync(struct file *file,loff_t a, loff_t b, int datasync) + +{ + struct audio_evrc_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + +int audrec_evrc_process_eos(struct audio_evrc_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audrec_pcm_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audevrc_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_evrc_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int rc = 0, eos_condition = AUDPREPROC_EVRC_EOS_NONE; + unsigned short mfield_size = 0; + int write_count = 0; + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + /* if supplied count is more than driver buffer size + * then only copy driver buffer size + */ + if (count > frame->size) + count = frame->size; + + write_count = count; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] & + AUDPREPROC_EVRC_EOS_FLG_MASK) { + eos_condition = AUDPREPROC_EVRC_EOS_SET; + MM_DBG("EOS SET\n"); + if (mfield_size == count) { + buf += mfield_size; + eos_condition = 0; + goto exit; + } else + cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] &= + ~AUDPREPROC_EVRC_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audrec_pcm_send_data(audio, 0); + else { + audrec_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_EVRC_EOS_SET) + rc = audrec_evrc_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audevrc_in_release(struct inode *inode, struct file *file) +{ + struct audio_evrc_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audevrc_in_disable(audio); + audevrc_in_flush(audio); + msm_adsp_put(audio->audrec); + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \ + (audio->out_data)) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +static struct audio_evrc_in the_audio_evrc_in; + +static int audevrc_in_open(struct inode *inode, struct file *file) +{ + struct audio_evrc_in *audio = &the_audio_evrc_in; + int rc; + int encid; + int dma_size = 0; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + dma_size = NT_DMASZ; + MM_DBG("Opened for non tunnel mode encoding\n"); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + dma_size = DMASZ; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + MM_ERR("Invalid mode\n"); + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_8000, + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_8000; + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (EVRC_FRAME_SIZE + 14); + else + audio->buffer_size = EVRC_FRAME_SIZE; + audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_EVRC | audio->mode; + + audio->cfg.cdma_rate = CDMA_RATE_FULL; + audio->cfg.min_bit_rate = CDMA_RATE_FULL; + audio->cfg.max_bit_rate = CDMA_RATE_FULL; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + } + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_evrc_adsp_ops, audio); + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_evrc_adsp_ops, audio); + if (rc) { + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + goto done; + } + } + + audio->dsp_cnt = 0; + audio->stopped = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audevrc_in_flush(audio); + audevrc_out_flush(audio); + + audio->phys = allocate_contiguous_ebi_nomap(dma_size, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate physical read buffers\n"); + rc = -ENOMEM; + goto evt_error; + } else { + audio->map_v_read = ioremap(audio->phys, dma_size); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map physical address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto evt_error; + } + audio->data = audio->map_v_read; + MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + audio->out_data = NULL; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, + SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate physical write buffers\n"); + rc = -ENOMEM; + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + goto evt_error; + } else { + audio->map_v_write = ioremap( + audio->out_phys, BUFFER_SIZE); + + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address\n"); + rc = -ENOMEM; + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + free_contiguous_memory_by_paddr(\ + audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, (int)audio->out_data); + } + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (unsigned int)audio->out[0].data, + (unsigned int)audio->out[1].data); + audio->mfield = NT_FRAME_HEADER_SIZE; + audio->out_frame_cnt++; + } + file->private_data = audio; + audio->opened = 1; + +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_evrc_in_fops = { + .owner = THIS_MODULE, + .open = audevrc_in_open, + .release = audevrc_in_release, + .read = audevrc_in_read, + .write = audevrc_in_write, + .fsync = audevrc_in_fsync, + .unlocked_ioctl = audevrc_in_ioctl, +}; + +static struct miscdevice audevrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_evrc_in_fops, +}; + +static int __init audevrc_in_init(void) +{ + mutex_init(&the_audio_evrc_in.lock); + mutex_init(&the_audio_evrc_in.read_lock); + spin_lock_init(&the_audio_evrc_in.dsp_lock); + init_waitqueue_head(&the_audio_evrc_in.wait); + init_waitqueue_head(&the_audio_evrc_in.wait_enable); + mutex_init(&the_audio_evrc_in.write_lock); + init_waitqueue_head(&the_audio_evrc_in.write_wait); + return misc_register(&audevrc_in_misc); +} +device_initcall(audevrc_in_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_fm.c b/arch/arm/mach-msm/qdsp5/audio_fm.c new file mode 100644 index 00000000000..2ab7cadfdeb --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_fm.c @@ -0,0 +1,169 @@ +/* arch/arm/mach-msm/qdsp5/audio_fm.c + * + * pcm audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "audmgr.h" + +struct audio { + struct mutex lock; + int opened; + int enabled; + int running; + struct audmgr audmgr; + uint16_t volume; +}; + +static struct audio fm_audio; + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_VOICE; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + audio->enabled = 1; + return rc; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audmgr_disable(&audio->audmgr); + } + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + + MM_DBG("cmd %d", cmd); + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + break; + + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &fm_audio; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + mutex_lock(&audio->lock); + + if (audio->opened) { + MM_ERR("busy\n"); + rc = -EBUSY; + goto done; + } + + rc = audmgr_open(&audio->audmgr); + + if (rc) { + MM_ERR("%s: failed to register listnet\n", __func__); + goto done; + } + + file->private_data = audio; + audio->opened = 1; + +done: + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_fm_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, +}; + +struct miscdevice audio_fm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_fm", + .fops = &audio_fm_fops, +}; + +static int __init audio_init(void) +{ + struct audio *audio = &fm_audio; + + mutex_init(&audio->lock); + return misc_register(&audio_fm_misc); +} + +device_initcall(audio_init); + +MODULE_DESCRIPTION("MSM FM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c new file mode 100644 index 00000000000..6fc5d6bf299 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_in.c @@ -0,0 +1,996 @@ +/* arch/arm/mach-msm/qdsp5/audio_in.c + * + * pcm audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include +#include +#include +#include + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_SIZE (2052 * 2) +#define MONO_DATA_SIZE (2048) +#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) +#define DMASZ (FRAME_SIZE * FRAME_NUM) + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + + struct msm_adsp_module *audpre; + struct msm_adsp_module *audrec; + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */ + uint32_t type; /* 0 for PCM ,1 for AAC */ + uint32_t bit_rate; /* bit rate for AAC */ + uint32_t record_quality; /* record quality (bits/sample/channel) + for AAC*/ + uint32_t buffer_cfg_ioctl; /* to allow any one of buffer set ioctl */ + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + unsigned short samp_rate_index; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + + /* audpre settings */ + int tx_agc_enable; + audpreproc_cmd_cfg_agc_params tx_agc_cfg; + int ns_enable; + audpreproc_cmd_cfg_ns_params ns_cfg; + /* For different sample rate, the coeff might be different. * + * All the coeff should be passed from user space */ + int iir_enable; + audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg; +}; + +static int audio_in_dsp_enable(struct audio_in *audio, int enable); +static int audio_in_encoder_config(struct audio_in *audio); +static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); +static void audio_flush(struct audio_in *audio); +static int audio_dsp_set_tx_agc(struct audio_in *audio); +static int audio_dsp_set_ns(struct audio_in *audio); +static int audio_dsp_set_iir(struct audio_in *audio); + +static unsigned convert_dsp_samp_index(unsigned index) +{ + switch (index) { + case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000; + case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100; + case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000; + case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000; + case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050; + case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000; + case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000; + case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025; + case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000; + default: return AUDREC_CMD_SAMP_RATE_INDX_11025; + } +} + +static unsigned convert_samp_rate(unsigned hz) +{ + switch (hz) { + case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000; + case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100; + case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000; + case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000; + case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050; + case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000; + case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000; + case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025; + case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000; + default: return RPC_AUD_DEF_SAMPLE_RATE_11025; + } +} + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* must be called with audio->lock held */ +static int audio_in_enable(struct audio_in *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + else + cfg.codec = RPC_AUD_DEF_CODEC_AAC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + MM_ERR("msm_adsp_enable(audpre) failed\n"); + return -ENODEV; + } + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audio_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audio_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audio_in_dsp_enable(audio, 0); + + wake_up(&audio->wait); + + msm_adsp_disable(audio->audrec); + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + MM_INFO("type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + MM_INFO("err_index %d\n", msg[0]); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +struct audio_frame { + uint16_t count_low; + uint16_t count_high; + uint16_t bytes; + uint16_t unknown; + unsigned char samples[]; +} __attribute__((packed)); + +static void audio_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + /* XXX check for bogus frame size? */ + + frame = (void *) (((char *)audio->in[index].data) - + sizeof(*frame)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->bytes; + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + else + audio->in_count++; + + audio_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + uint16_t msg[3]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: + if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) { + if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) { + MM_INFO("CFG ENABLED\n"); + audio_in_encoder_config(audio); + } else { + MM_INFO("CFG SLEEP\n"); + audio->running = 0; + audio->tx_agc_enable = 0; + audio->ns_enable = 0; + audio->iir_enable = 0; + } + } else { + MM_INFO("CMD_CFG_DONE %x\n", msg[0]); + } + break; + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_INFO("PARAM CFG DONE\n"); + audio->running = 1; + audio_dsp_set_tx_agc(audio); + audio_dsp_set_ns(audio); + audio_dsp_set_iir(audio); + break; + } + case AUDREC_MSG_FATAL_ERR_MSG: + MM_ERR("ERROR %x\n", msg[0]); + break; + case AUDREC_MSG_PACKET_READY_MSG: +/* REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); */ + audio_in_get_dsp_frames(audio); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +struct msm_adsp_ops audpre_adsp_ops = { + .event = audpre_dsp_event, +}; + +struct msm_adsp_ops audrec_adsp_ops = { + .event = audrec_dsp_event, +}; + + +#define audio_send_queue_pre(audio, cmd, len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) +#define audio_send_queue_recbs(audio, cmd, len) \ + msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, \ + QDSP_uPAudRecCmdQueue, cmd, len) + +/* Convert Bit Rate to Record Quality field of DSP */ +static unsigned int bitrate_to_record_quality(unsigned int sample_rate, + unsigned int channel, unsigned int bit_rate) { + unsigned int temp; + + temp = sample_rate * channel; + MM_DBG(" sample rate * channel = %d \n", temp); + /* To represent in Q12 fixed format */ + temp = (bit_rate * 4096) / temp; + MM_DBG(" Record Quality = 0x%8x \n", temp); + return temp; +} + +static int audio_dsp_set_tx_agc(struct audio_in *audio) +{ + audpreproc_cmd_cfg_agc_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + audio->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS; + if (audio->tx_agc_enable) { + /* cmd.tx_agc_param_mask = 0xFE00 from sample code */ + audio->tx_agc_cfg.tx_agc_param_mask = + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + audio->tx_agc_cfg.tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA; + /* cmd.param_mask = 0xFFF0 from sample code */ + audio->tx_agc_cfg.param_mask = + (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) | + (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) | + (1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK); + } else { + audio->tx_agc_cfg.tx_agc_param_mask = + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + audio->tx_agc_cfg.tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS; + } + cmd = audio->tx_agc_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_tx_agc(struct audio_in *audio, int enable) +{ + if (audio->tx_agc_enable != enable) { + audio->tx_agc_enable = enable; + if (audio->running) + audio_dsp_set_tx_agc(audio); + } + return 0; +} + +static int audio_dsp_set_ns(struct audio_in *audio) +{ + audpreproc_cmd_cfg_ns_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + audio->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS; + + if (audio->ns_enable) { + /* cmd.ec_mode_new is fixed as 0x0064 when enable + * from sample code */ + audio->ns_cfg.ec_mode_new = + AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA | + AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA | + AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA; + } else { + audio->ns_cfg.ec_mode_new = + AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS; + } + cmd = audio->ns_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_ns(struct audio_in *audio, int enable) +{ + if (audio->ns_enable != enable) { + audio->ns_enable = enable; + if (audio->running) + audio_dsp_set_ns(audio); + } + return 0; +} + +static int audio_dsp_set_iir(struct audio_in *audio) +{ + audpreproc_cmd_cfg_iir_tuning_filter_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + audio->iir_cfg.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; + + if (audio->iir_enable) + /* cmd.active_flag is 0xFFFF from sample code but 0x0001 here */ + audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA; + else + audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS; + + cmd = audio->iir_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_iir(struct audio_in *audio, int enable) +{ + if (audio->iir_enable != enable) { + audio->iir_enable = enable; + if (audio->running) + audio_dsp_set_iir(audio); + } + return 0; +} + +static int audio_in_dsp_enable(struct audio_in *audio, int enable) +{ + audrec_cmd_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_CFG; + cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS; + cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | audio->type); + cmd.type_1 = 0; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audio_in_encoder_config(struct audio_in *audio) +{ + audrec_cmd_arec0param_cfg cmd; + uint16_t *data = (void *) audio->data; + unsigned n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG; + cmd.ptr_to_extpkt_buffer_msw = audio->phys >> 16; + cmd.ptr_to_extpkt_buffer_lsw = audio->phys; + cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */ + cmd.samp_rate_index = audio->samp_rate_index; + cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */ + + /* cmd.rec_quality is based on user set bit rate / sample rate / + * channel + */ + cmd.rec_quality = audio->record_quality; + + /* prepare buffer pointers: + * Mono: 1024 samples + 4 halfword header + * Stereo: 2048 samples + 4 halfword header + * AAC + * Mono/Stere: 768 + 4 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + 4; + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) + data += (4 + (audio->channel_mode ? 2048 : 1024)); + else if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC) + data += (4 + 768); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */ + cmd.type = AUDREC_CMD_TYPE_0; + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } +} + +static long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_in_enable(audio); + break; + case AUDIO_STOP: + rc = audio_in_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audio_flush(audio); + mutex_unlock(&audio->read_lock); + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + /* The below code is to make mutual exclusive between + * AUDIO_SET_CONFIG and AUDIO_SET_STREAM_CONFIG. + * Allow any one IOCTL. + */ + if (audio->buffer_cfg_ioctl == AUDIO_SET_STREAM_CONFIG) { + rc = -EINVAL; + break; + } + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.channel_count == 1) { + cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO; + } else if (cfg.channel_count == 2) { + cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + + if (cfg.type == 0) { + cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV; + } else if (cfg.type == 1) { + cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC; + } else { + rc = -EINVAL; + break; + } + audio->samp_rate = convert_samp_rate(cfg.sample_rate); + audio->samp_rate_index = + convert_dsp_samp_index(cfg.sample_rate); + audio->channel_mode = cfg.channel_count; + audio->buffer_size = + audio->channel_mode ? STEREO_DATA_SIZE + : MONO_DATA_SIZE; + audio->type = cfg.type; + audio->buffer_cfg_ioctl = AUDIO_SET_CONFIG; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) + cfg.channel_count = 1; + else + cfg.channel_count = 2; + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) + cfg.type = 0; + else + cfg.type = 1; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + /* The below code is to make mutual exclusive between + * AUDIO_SET_CONFIG and AUDIO_SET_STREAM_CONFIG. + * Allow any one IOCTL. + */ + if (audio->buffer_cfg_ioctl == AUDIO_SET_CONFIG) { + rc = -EINVAL; + break; + } + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } else + rc = 0; + audio->buffer_size = cfg.buffer_size; + /* The IOCTL is only of AAC, set the encoder as AAC */ + audio->type = 1; + audio->buffer_cfg_ioctl = AUDIO_SET_STREAM_CONFIG; + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) + cfg.channels = 1; + else + cfg.channels = 2; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + cfg.bit_rate = audio->bit_rate; + cfg.stream_format = AUDIO_AAC_FORMAT_RAW; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + unsigned int record_quality; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.stream_format != AUDIO_AAC_FORMAT_RAW) { + MM_ERR("unsupported AAC format\n"); + rc = -EINVAL; + break; + } + record_quality = bitrate_to_record_quality(cfg.sample_rate, + cfg.channels, cfg.bit_rate); + /* Range of Record Quality Supported by DSP, Q12 format */ + if ((record_quality < 0x800) || (record_quality > 0x4000)) { + MM_ERR("Unsupported bit rate \n"); + rc = -EINVAL; + break; + } + if (cfg.channels == 1) { + cfg.channels = AUDREC_CMD_STEREO_MODE_MONO; + } else if (cfg.channels == 2) { + cfg.channels = AUDREC_CMD_STEREO_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + audio->samp_rate = convert_samp_rate(cfg.sample_rate); + audio->samp_rate_index = + convert_dsp_samp_index(cfg.sample_rate); + audio->channel_mode = cfg.channels; + audio->bit_rate = cfg.bit_rate; + audio->record_quality = record_quality; + MM_DBG(" Record Quality = 0x%8x \n", audio->record_quality); + rc = 0; + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped); + if (rc < 0) + break; + + if (audio->stopped && !audio->in_count) { + rc = 0;/* End of File */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + if (count >= size) { + /* order the reads on the buffer */ + dma_coherent_post_ops(); + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + } else { + MM_ERR("short read\n"); + break; + } + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC) + break; /* AAC only read one frame */ + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int audio_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audio_in_disable(audio); + audio_flush(audio); + msm_adsp_put(audio->audrec); + msm_adsp_put(audio->audpre); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_in the_audio_in; + +static int audio_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + int rc; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025; + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025; + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + audio->buffer_size = MONO_DATA_SIZE; + audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV; + + /* For AAC, bit rate hard coded, default settings is + * sample rate (11025) x channel count (1) x recording quality (1.75) + * = 19293 bps */ + audio->bit_rate = 19293; + audio->record_quality = 0x1c00; + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_adsp_ops, audio); + if (rc) + goto done; + rc = msm_adsp_get("AUDRECTASK", &audio->audrec, + &audrec_adsp_ops, audio); + if (rc) + goto done; + + audio->dsp_cnt = 0; + audio->stopped = 0; + audio->buffer_cfg_ioctl = 0; /* No valid ioctl set */ + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0, enable; + uint16_t enable_mask; + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_ENABLE_AUDPRE: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + enable = (enable_mask & AGC_ENABLE) ? 1 : 0; + audio_enable_tx_agc(audio, enable); + enable = (enable_mask & NS_ENABLE) ? 1 : 0; + audio_enable_ns(audio, enable); + enable = (enable_mask & TX_IIR_ENABLE) ? 1 : 0; + audio_enable_iir(audio, enable); + break; + + case AUDIO_SET_AGC: + if (copy_from_user(&audio->tx_agc_cfg, (void *) arg, + sizeof(audio->tx_agc_cfg))) + rc = -EFAULT; + break; + + case AUDIO_SET_NS: + if (copy_from_user(&audio->ns_cfg, (void *) arg, + sizeof(audio->ns_cfg))) + rc = -EFAULT; + break; + + case AUDIO_SET_TX_IIR: + if (copy_from_user(&audio->iir_cfg, (void *) arg, + sizeof(audio->iir_cfg))) + rc = -EFAULT; + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&audio->lock); + return rc; +} + +static int audpre_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + + file->private_data = audio; + + return 0; +} + +static struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &audio_fops, +}; + +static const struct file_operations audpre_fops = { + .owner = THIS_MODULE, + .open = audpre_open, + .unlocked_ioctl = audpre_ioctl, +}; + +struct miscdevice audpre_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_preproc_ctl", + .fops = &audpre_fops, +}; + +static int __init audio_in_init(void) +{ + the_audio_in.data = dma_alloc_coherent(NULL, DMASZ, + &the_audio_in.phys, GFP_KERNEL); + if (!the_audio_in.data) { + MM_ERR("Unable to allocate DMA buffer\n"); + return -ENOMEM; + } + + mutex_init(&the_audio_in.lock); + mutex_init(&the_audio_in.read_lock); + spin_lock_init(&the_audio_in.dsp_lock); + init_waitqueue_head(&the_audio_in.wait); + return misc_register(&audio_in_misc) || misc_register(&audpre_misc); +} + +device_initcall(audio_in_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_lpa.c b/arch/arm/mach-msm/qdsp5/audio_lpa.c new file mode 100644 index 00000000000..8754337e909 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_lpa.c @@ -0,0 +1,1487 @@ + +/* audio_lpa.c - low power audio driver + * + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * Based on the PCM decoder driver in arch/arm/mach-msm/qdsp5/audio_pcm.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 + +#define MSM_MAX_VOLUME 0x2000 +/* 17 added to avoid more deviation */ +#define MSM_VOLUME_STEP (MSM_MAX_VOLUME+17) +#define MSM_VOLUME_FACTOR (10000) + +/* Size must be power of 2 */ +#define MAX_BUF 2 +#define BUFSZ (524288) + +#define AUDDEC_DEC_PCM 0 + +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDPCM_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct audio; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audpcm_suspend_ctl { +struct early_suspend node; +struct audio *audio; +}; +#endif + +struct audpcm_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audlpa_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audpcm_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + struct list_head out_queue; /* queue to retain output buffers */ + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_bits; /* bits per sample */ + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; + struct msm_mapped_buffer *map_v_write; + + uint32_t drv_status; + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int teos; /* valid only if tunnel mode & no data left for decoder */ + int rmt_resource_released; + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + + unsigned long volume; + + uint16_t dec_id; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audpcm_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + struct list_head pmem_region_queue; + int buffer_count; + int buffer_size; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); +static void audpcm_async_send_data(struct audio *audio, + unsigned needed); + + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_PCM; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_PCM; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) + MM_ERR("ADSP resources are not available"); + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audpcm_async_send_data(audio, 1); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + default: + MM_ERR("unexpected message from decoder\n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason =0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play\n"); + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_ERR("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + + +struct msm_adsp_ops audlpadec_adsp_ops = { + .event = audplay_dsp_event, +}; + + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + audpp_cmd_cfg_adec_params_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.stereo_cfg = audio->out_channel_mode; + cmd.pcm_width = audio->out_bits; + cmd.sign = 0; + audpp_send_queue2(&cmd, sizeof(cmd)); +} +static void audpcm_async_send_data(struct audio *audio, unsigned needed) +{ + unsigned long flags; + + if (!audio->running) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload payload; + struct audpcm_buffer_node *used_buf; + + MM_DBG("consumed\n"); + + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audpcm_buffer_node, list); + list_del(&used_buf->list); + payload.aio_buf = used_buf->buf; + audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + } + if (audio->out_needed) { + struct audpcm_buffer_node *next_buf; + audplay_cmd_bitstream_data_avail cmd; + if (!list_empty(&audio->out_queue)) { + next_buf = list_first_entry(&audio->out_queue, + struct audpcm_buffer_node, list); + MM_DBG("next_buf %p\n", next_buf); + if (next_buf) { + MM_DBG("next buf phy %lx len %d\n", + next_buf->paddr, next_buf->buf.data_len); + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + if (next_buf->buf.data_len) + cmd.decoder_id = audio->dec_id; + else { + cmd.decoder_id = -1; + MM_DBG("input EOS signaled\n"); + } + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static void audpcm_async_flush(struct audio *audio) +{ + struct audpcm_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audpcm_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} +static void audio_ioport_reset(struct audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + MM_DBG("fsync in progress\n"); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audpcm_async_flush(audio); + mutex_unlock(&audio->write_lock); + } else + audpcm_async_flush(audio); + } else { + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audpcm_async_flush(audio); + mutex_unlock(&audio->write_lock); + } +} + +static int audpcm_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audpcm_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audpcm_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audpcm_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audpcm_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audpcm_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audpcm_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt && drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + mutex_lock(&audio->lock); + audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audlpa_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audlpa_pmem_region *region_elt; + struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audlpa_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audlpa_pmem_region *region; + int rc = -EINVAL; + + MM_DBG("\n"); /* Macro prints the file name and function */ + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audlpa_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr, + region->vaddr, region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); +end: + return rc; +} + +static int audlpa_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + MM_DBG("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + MM_DBG("remove region fd %d vaddr %p\n", + info->fd, info->vaddr); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audlpa_pmem_region **region) +{ + struct audlpa_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audlpa_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audlpa_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + MM_ERR("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audlpa_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audpcm_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len" + "%d\n", buf_node, dir, + buf_node->buf.buf_addr, buf_node->buf.buf_len, + buf_node->buf.data_len); + + buf_node->paddr = audlpa_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + audpcm_async_send_data(audio, 0); + } + MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr); + + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + + audio->volume = MSM_VOLUME_STEP * arg; + audio->volume /= MSM_VOLUME_FACTOR; + + if (audio->volume > MSM_MAX_VOLUME) + audio->volume = MSM_MAX_VOLUME; + + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, + audio->volume, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audpcm_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + if (config.bits == 8) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8; + else if (config.bits == 16) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_bits = config.bits; + audio->buffer_count = config.buffer_count; + audio->buffer_size = config.buffer_size; + MM_DBG("AUDIO_SET_CONFIG\n"); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_count = audio->buffer_count; + config.buffer_size = audio->buffer_size; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8) + config.bits = 8; + else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_16) + config.bits = 16; + else + config.bits = 16; + config.unused[0] = 0; + config.unused[1] = 0; + + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + MM_DBG("AUDIO_GET_CONFIG\n"); + break; + } + + + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_pmem_remove(audio, &info); + break; + } + + case AUDIO_ASYNC_WRITE: + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audlpa_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_ASYNC_READ: + MM_ERR("AUDIO_ASYNC_READ not supported\n"); + rc = -EPERM; + break; + + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audlpa_async_fsync(struct audio *audio) +{ + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + (audio->teos && audio->out_needed && + list_empty(&audio->out_queue)) + || audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audlpa_sync_fsync(struct audio *audio) +{ + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audpcm_async_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +int audlpa_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + + if (!audio->running) + return -EINVAL; + + return audlpa_async_fsync(audio); +} + +static void audpcm_reset_pmem_region(struct audio *audio) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audpcm_async_flush(audio); + audpcm_reset_pmem_region(audio); + + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audpcm_reset_event_queue(audio); + MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data); + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audpcm_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audpcm_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audpcm_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audpcm_suspend(struct early_suspend *h) +{ + struct audpcm_suspend_ctl *ctl = + container_of(h, struct audpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audpcm_resume(struct early_suspend *h) +{ + struct audpcm_suspend_ctl *ctl = + container_of(h, struct audpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audpcm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audpcm_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %lx\n", audio->volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d\n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d\n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d\n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d\n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d\n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d\n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d\n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d\n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d\n", audio->out[1].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audpcm_debug_fops = { + .read = audpcm_debug_read, + .open = audpcm_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, i, dec_attrb, decid; + struct audpcm_event *e_node = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_lpa_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_PCM; + if (file->f_mode & FMODE_READ) { + MM_ERR("Non-Tunneled mode not supported\n"); + rc = -EPERM; + kfree(audio); + goto done; + } else + dec_attrb |= MSM_AUD_MODE_TUNNEL; + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available\n"); + rc = -ENODEV; + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->buffer_size = BUFSZ; + audio->buffer_count = MAX_BUF; + rc = audmgr_open(&audio->audmgr); + if (rc) + goto err; + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audlpadec_adsp_ops, audio); + if (rc) { + MM_ERR("failed to get %s module\n", audio->module_name); + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for PCM session"); + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + audio->volume = 0x2000; + audpcm_async_flush(audio); + + file->private_data = audio; + audio->opened = 1; + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_pcm_lp_dec_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audpcm_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audpcm_resume; + audio->suspend_ctl.node.suspend = audpcm_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDPCM_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audpcm_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + audpp_adec_free(audio->dec_id); + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + kfree(audio); + return rc; +} + +static const struct file_operations audio_pcm_lp_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audlpa_fsync, +}; + +struct miscdevice audio_lpa_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_lp_dec", + .fops = &audio_pcm_lp_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_lpa_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c new file mode 100644 index 00000000000..93461070862 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c @@ -0,0 +1,2360 @@ +/* arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 + +/* Size must be power of 2 */ +#define BUFSZ_MAX 32768 +#define BUFSZ_MIN 4096 +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_MP3 2 + +#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDMP3_METAFIELD_MASK 0xFFFF0000 +#define AUDMP3_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDMP3_EOS_FLG_MASK 0x01 +#define AUDMP3_EOS_NONE 0x0 /* No EOS detected */ +#define AUDMP3_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDMP3_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct audio; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audmp3_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audmp3_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audmp3_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audmp3_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audmp3_drv_operations { + void (*pcm_buf_update)(struct audio *, uint32_t *); + void (*buffer_refresh)(struct audio *); + void (*send_data)(struct audio *, unsigned); + void (*out_flush)(struct audio *); + void (*in_flush)(struct audio *); + int (*fsync)(struct audio *); +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + struct list_head out_queue; /* queue to retain output buffers */ + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + struct list_head in_queue; /* queue to retain input buffers */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + uint32_t drv_status; + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int rmt_resource_released; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audmp3_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + struct list_head pmem_region_queue; /* protected by lock */ + struct audmp3_drv_operations drv_ops; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audmp3_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_MP3; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_MP3; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for MP3 \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_MP3; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audmp3_async_pcm_buf_update(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audmp3_buffer_node *filled_buf; + uint8_t index; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + BUG_ON(list_empty(&audio->in_queue)); + filled_buf = list_first_entry(&audio->in_queue, + struct audmp3_buffer_node, list); + if (filled_buf->paddr == payload[2 + index * 2]) { + list_del(&filled_buf->list); + event_payload.aio_buf = filled_buf->buf; + event_payload.aio_buf.data_len = + payload[3 + index * 2]; + MM_DBG("pcm buf %p data_len %d\n", filled_buf, + event_payload.aio_buf.data_len); + audmp3_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + MM_ERR("expected=%lx ret=%x\n", filled_buf->paddr, + payload[2 + index * 2]); + break; + } + } + + audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN; + audio->drv_ops.buffer_refresh(audio); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} + +static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[2 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audio->drv_ops.buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audio->drv_ops.send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio->drv_ops.pcm_buf_update(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason=0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audio->drv_ops.buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audio->drv_ops.buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + + +struct msm_adsp_ops audplay_adsp_ops = { + .event = audplay_dsp_event, +}; + + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_MP3; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + audpp_cmd_cfg_adec_params_mp3 cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDMP3_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + /* complete all the writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +/* Caller holds irq_lock */ +static void audmp3_async_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + struct audmp3_buffer_node *next_buf; + + if (!audio->running || + audio->drv_status & ADRV_STATUS_IBUF_GIVEN) + return; + + if (!list_empty(&audio->in_queue)) { + next_buf = list_first_entry(&audio->in_queue, + struct audmp3_buffer_node, list); + if (!next_buf) + return; + MM_DBG("next buf %p phy %lx len %d\n", next_buf, + next_buf->paddr, next_buf->buf.buf_len); + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = next_buf->paddr; + refresh_cmd.buf0_length = next_buf->buf.buf_len - + (next_buf->buf.buf_len % 576) + + (audio->mfield ? 24 : 0); /* Mp3 frame size */ + refresh_cmd.buf_read_count = 0; + audio->drv_status |= ADRV_STATUS_IBUF_GIVEN; + (void) audplay_send_queue0(audio, &refresh_cmd, + sizeof(refresh_cmd)); + } + +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % 576) + + (audio->mfield ? 24 : 0); /* Mp3 frame size */ + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audmp3_async_send_data(struct audio *audio, unsigned needed) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload payload; + struct audmp3_buffer_node *used_buf; + + MM_DBG("consumed\n"); + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audmp3_buffer_node, list); + list_del(&used_buf->list); + payload.aio_buf = used_buf->buf; + audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + + } + + if (audio->out_needed) { + struct audmp3_buffer_node *next_buf; + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + if (!list_empty(&audio->out_queue)) { + next_buf = list_first_entry(&audio->out_queue, + struct audmp3_buffer_node, list); + MM_DBG("next_buf %p\n", next_buf); + if (next_buf) { + MM_DBG("next buf phy %lx len %d\n", + next_buf->paddr, + next_buf->buf.data_len); + + cmd.cmd_id = + AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDMP3_METAFIELD_MASK | + (next_buf->buf.mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + /* complete the writes to the input buffer */ + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } + } + +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static void audmp3_async_flush(struct audio *audio) +{ + struct audmp3_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audmp3_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audio_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audmp3_async_flush_pcm_buf(struct audio *audio) +{ + struct audmp3_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audmp3_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = 0; + audmp3_post_event(audio, AUDIO_EVENT_READ_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN; + +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audio_ioport_reset(struct audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + MM_DBG("fsync in progress\n"); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + } else + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + } else { + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio->drv_ops.in_flush(audio); + mutex_unlock(&audio->read_lock); + } +} + +static int audmp3_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audmp3_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audmp3_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audmp3_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audmp3_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audmp3_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audmp3_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audmp3_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audmp3_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audmp3_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE || + drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + mutex_lock(&audio->lock); + audmp3_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + + /* order reads from the output buffer */ + if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) + rmb(); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audmp3_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audmp3_pmem_region *region_elt; + struct audmp3_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audmp3_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audmp3_pmem_region *region; + int rc = -EINVAL; + + MM_DBG("\n"); /* Macro prints the file name and function */ + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audmp3_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr, + region->vaddr, region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); +end: + return rc; +} + +static int audmp3_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audmp3_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audmp3_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + MM_DBG("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + MM_DBG("remove region fd %d vaddr %p \n", + info->fd, info->vaddr); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audmp3_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audmp3_pmem_region **region) +{ + struct audmp3_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audmp3_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audmp3_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + MM_ERR("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audmp3_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audmp3_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len \ + %d\n", buf_node, dir, + buf_node->buf.buf_addr, buf_node->buf.buf_len, + buf_node->buf.data_len); + + buf_node->paddr = audmp3_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1) || + (!audio->pcm_feedback && + !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + audio->drv_ops.send_data(audio, 0); + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->in_queue); + audio->drv_ops.buffer_refresh(audio); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr); + + return 0; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG(" AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audmp3_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) { + rc = -EFAULT; + } else { + rc = 0; + } + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + rc = 0; + break; + } + + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + + if (IS_ERR(audio->map_v_read)) { + MM_ERR("map of read buf failed\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audmp3_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audmp3_pmem_remove(audio, &info); + break; + } + case AUDIO_ASYNC_WRITE: + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audmp3_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_ASYNC_READ: + if (audio->pcm_feedback) + rc = audmp3_aio_buf_add(audio, 0, (void __user *) arg); + else + rc = -EPERM; + break; + + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audmp3_async_fsync(struct audio *audio) +{ + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + (audio->teos && audio->out_needed && + list_empty(&audio->out_queue)) + || audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audmp3_sync_fsync(struct audio *audio) +{ + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audio->drv_ops.send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +int audmp3_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + + if (!audio->running || audio->pcm_feedback) + return -EINVAL; + + return audio->drv_ops.fsync(audio); +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (audio->drv_status & ADRV_STATUS_AIO_INTF) + return -EPERM; + else if (!audio->pcm_feedback) + return 0; /* PCM feedback disabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible( + audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped) + || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since + * driver does not know frame size, read count + * must be greater or equal + * to size of PCM samples + */ + MM_DBG("no partial frame done reading\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + /* order reads from the output buffer */ + rmb(); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audio->drv_ops.buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audmp3_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audio->drv_ops.send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audio->drv_ops.send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDMP3_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + if (audio->drv_status & ADRV_STATUS_AIO_INTF) + return -EPERM; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDMP3_EOS_FLG_OFFSET] & + AUDMP3_EOS_FLG_MASK) { + MM_DBG("EOS SET\n"); + eos_condition = AUDMP3_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDMP3_EOS_FLG_OFFSET] + &= ~AUDMP3_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audio->drv_ops.send_data(audio, 0); + } + } + if (eos_condition == AUDMP3_EOS_SET) + rc = audmp3_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static void audmp3_reset_pmem_region(struct audio *audio) +{ + struct audmp3_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audmp3_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audmp3_reset_pmem_region(audio); + + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audmp3_reset_event_queue(audio); + MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data); + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audmp3_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audmp3_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audmp3_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audmp3_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audmp3_suspend(struct early_suspend *h) +{ + struct audmp3_suspend_ctl *ctl = + container_of(h, struct audmp3_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audmp3_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audmp3_resume(struct early_suspend *h) +{ + struct audmp3_suspend_ctl *ctl = + container_of(h, struct audmp3_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audmp3_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audmp3_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audmp3_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audmp3_debug_fops = { + .read = audmp3_debug_read, + .open = audmp3_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + + struct audio *audio = NULL; + int rc, i, dec_attrb, decid; + struct audmp3_event *e_node = NULL; + unsigned pmem_sz = DMASZ_MAX; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_mp3_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_MP3; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + /* Non AIO interface */ + if (!(file->f_flags & O_NONBLOCK)) { + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, + SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap( + audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write \ + buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr\ + 0x%08x\n", audio->phys,\ + (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + } + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops, audio); + + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for MP3 session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + if (file->f_flags & O_NONBLOCK) { + MM_DBG("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.pcm_buf_update = audmp3_async_pcm_buf_update; + audio->drv_ops.buffer_refresh = audmp3_async_buffer_refresh; + audio->drv_ops.send_data = audmp3_async_send_data; + audio->drv_ops.out_flush = audmp3_async_flush; + audio->drv_ops.in_flush = audmp3_async_flush_pcm_buf; + audio->drv_ops.fsync = audmp3_async_fsync; + } else { + MM_DBG("set to std io interface\n"); + audio->drv_ops.pcm_buf_update = audio_update_pcm_buf_entry; + audio->drv_ops.buffer_refresh = audplay_buffer_refresh; + audio->drv_ops.send_data = audplay_send_data; + audio->drv_ops.out_flush = audio_flush; + audio->drv_ops.in_flush = audio_flush_pcm_buf; + audio->drv_ops.fsync = audmp3_sync_fsync; + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = (audio->out_dma_sz >> 1); + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->vol_pan.volume = 0x2000; + + audio->drv_ops.out_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_mp3_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audmp3_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audmp3_resume; + audio->suspend_ctl.node.suspend = audmp3_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDMP3_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audmp3_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audmp3_fsync, +}; + +struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_mp3_misc); +} + +static void __exit audio_exit(void) +{ + misc_deregister(&audio_mp3_misc); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM MP3 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_mvs.c b/arch/arm/mach-msm/qdsp5/audio_mvs.c new file mode 100644 index 00000000000..9b524b4bc16 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_mvs.c @@ -0,0 +1,1707 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MVS_PROG 0x30000014 +#define MVS_VERS 0x00030001 +#define MVS_VERS_COMP_VER2 0x00060001 +#define MVS_VERS_COMP_VER3 0x00030001 + + +#define MVS_CLIENT_ID_VOIP 0x00000003 + +#define MVS_ACQUIRE_PROC 4 +#define MVS_ENABLE_PROC 5 +#define MVS_RELEASE_PROC 6 +#define MVS_AMR_SET_AMR_MODE_PROC 7 +#define MVS_AMR_SET_AWB_MODE_PROC 8 +#define MVS_VOC_SET_FRAME_RATE_PROC 10 +#define MVS_GSM_SET_DTX_MODE_PROC 11 +#define MVS_G729A_SET_MODE_PROC 12 +#define MVS_G711_GET_MODE_PROC 14 +#define MVS_G711_SET_MODE_PROC 15 +#define MVS_G711A_GET_MODE_PROC 16 +#define MVS_G711A_SET_MODE_PROC 17 +#define MVS_G722_SET_MODE_PROC 20 +#define MVS_G722_GET_MODE_PROC 21 +#define MVS_SET_DTX_MODE_PROC 22 + +#define MVS_EVENT_CB_TYPE_PROC 1 +#define MVS_PACKET_UL_FN_TYPE_PROC 2 +#define MVS_PACKET_DL_FN_TYPE_PROC 3 + +#define MVS_CB_FUNC_ID 0xAAAABBBB +#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC +#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD + +#define MVS_FRAME_MODE_VOC_TX 1 +#define MVS_FRAME_MODE_VOC_RX 2 +#define MVS_FRAME_MODE_AMR_UL 3 +#define MVS_FRAME_MODE_AMR_DL 4 +#define MVS_FRAME_MODE_GSM_UL 5 +#define MVS_FRAME_MODE_GSM_DL 6 +#define MVS_FRAME_MODE_HR_UL 7 +#define MVS_FRAME_MODE_HR_DL 8 +#define MVS_FRAME_MODE_G711_UL 9 +#define MVS_FRAME_MODE_G711_DL 10 +#define MVS_FRAME_MODE_PCM_UL 13 +#define MVS_FRAME_MODE_PCM_DL 14 +#define MVS_FRAME_MODE_PCM_WB_UL 23 +#define MVS_FRAME_MODE_PCM_WB_DL 24 +#define MVS_FRAME_MODE_G729A_UL 17 +#define MVS_FRAME_MODE_G729A_DL 18 +#define MVS_FRAME_MODE_G711A_UL 19 +#define MVS_FRAME_MODE_G711A_DL 20 +#define MVS_FRAME_MODE_G722_UL 21 +#define MVS_FRAME_MODE_G722_DL 22 + + + +#define MVS_PKT_CONTEXT_ISR 0x00000001 + +#define RPC_TYPE_REQUEST 0 +#define RPC_TYPE_REPLY 1 + +#define RPC_STATUS_FAILURE 0 +#define RPC_STATUS_SUCCESS 1 +#define RPC_STATUS_REJECT 1 + +#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) +#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) +#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) + +enum audio_mvs_state_type { + AUDIO_MVS_CLOSED, + AUDIO_MVS_OPENED, + AUDIO_MVS_STARTED, + AUDIO_MVS_STOPPED +}; + +enum audio_mvs_event_type { + AUDIO_MVS_COMMAND, + AUDIO_MVS_MODE, + AUDIO_MVS_NOTIFY +}; + +enum audio_mvs_cmd_status_type { + AUDIO_MVS_CMD_FAILURE, + AUDIO_MVS_CMD_BUSY, + AUDIO_MVS_CMD_SUCCESS +}; + +enum audio_mvs_mode_status_type { + AUDIO_MVS_MODE_NOT_AVAIL, + AUDIO_MVS_MODE_INIT, + AUDIO_MVS_MODE_READY +}; + +enum audio_mvs_pkt_status_type { + AUDIO_MVS_PKT_NORMAL, + AUDIO_MVS_PKT_FAST, + AUDIO_MVS_PKT_SLOW +}; + +/* Parameters required for MVS acquire. */ +struct rpc_audio_mvs_acquire_args { + uint32_t client_id; + uint32_t cb_func_id; +}; + +struct audio_mvs_acquire_msg { + struct rpc_request_hdr rpc_hdr; + struct rpc_audio_mvs_acquire_args acquire_args; +}; + +/* Parameters required for MVS enable. */ +struct rpc_audio_mvs_enable_args { + uint32_t client_id; + uint32_t mode; + uint32_t ul_cb_func_id; + uint32_t dl_cb_func_id; + uint32_t context; +}; + +struct audio_mvs_enable_msg { + struct rpc_request_hdr rpc_hdr; + struct rpc_audio_mvs_enable_args enable_args; +}; + +/* Parameters required for MVS release. */ +struct audio_mvs_release_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t client_id; +}; + +/* Parameters required for setting AMR mode. */ +struct audio_mvs_set_amr_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t amr_mode; +}; + +/* Parameters required for setting DTX. */ +struct audio_mvs_set_dtx_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t dtx_mode; +}; + +/* Parameters required for setting EVRC mode. */ +struct audio_mvs_set_voc_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t max_rate; + uint32_t min_rate; +}; + +/* Parameters for G711 mode */ +struct audio_mvs_set_g711_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g711_mode; +}; + +/* Parameters for G729 mode */ +struct audio_mvs_set_g729_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g729_mode; +}; + +/* Parameters for G722 mode */ +struct audio_mvs_set_g722_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g722_mode; +}; + + +/* Parameters for G711A mode */ +struct audio_mvs_set_g711A_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g711A_mode; +}; + +/* Parameters for EFR FR and HR mode */ +struct audio_mvs_set_efr_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t efr_mode; +}; + +union audio_mvs_event_data { + struct mvs_ev_command_type { + uint32_t event; + uint32_t client_id; + uint32_t cmd_status; + } mvs_ev_command_type; + + struct mvs_ev_mode_type { + uint32_t event; + uint32_t client_id; + uint32_t mode_status; + uint32_t mode; + } mvs_ev_mode_type; + + struct mvs_ev_notify_type { + uint32_t event; + uint32_t client_id; + uint32_t buf_dir; + uint32_t max_frames; + } mvs_ev_notify_type; +}; + +struct audio_mvs_cb_func_args { + uint32_t cb_func_id; + uint32_t valid_ptr; + uint32_t event; + union audio_mvs_event_data event_data; +}; + +struct audio_mvs_frame_info_hdr { + uint32_t frame_mode; + uint32_t mvs_mode; + uint16_t buf_free_cnt; +}; + +struct audio_mvs_ul_reply { + struct rpc_reply_hdr reply_hdr; + uint32_t valid_pkt_status_ptr; + uint32_t pkt_status; +}; + +struct audio_mvs_dl_cb_func_args { + uint32_t cb_func_id; + + uint32_t valid_ptr; + uint32_t frame_mode; + uint32_t frame_mode_ignore; + + struct audio_mvs_frame_info_hdr frame_info_hdr; + + uint32_t amr_frame; + uint32_t amr_mode; +}; +/*general codec parameters includes AMR, G711A, PCM +G729, VOC and HR vocoders +*/ +struct gnr_cdc_param { + uint32_t param1; + uint32_t param2; + uint32_t valid_pkt_status_ptr; + uint32_t pkt_status; +}; +/*G711 codec parameter*/ +struct g711_param { + uint32_t param1; + uint32_t valid_pkt_status_ptr; + uint32_t pkt_status; +}; + +union codec_param { + struct gnr_cdc_param gnr_arg; + struct g711_param g711_arg; +}; + +struct audio_mvs_dl_reply { + struct rpc_reply_hdr reply_hdr; + + uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE/4]; + + uint32_t valid_frame_info_ptr; + uint32_t frame_mode; + uint32_t frame_mode_again; + + struct audio_mvs_frame_info_hdr frame_info_hdr; + union codec_param cdc_param; +}; + +struct audio_mvs_buf_node { + struct list_head list; + struct msm_audio_mvs_frame frame; +}; + +/* Each buffer is 20 ms, queue holds 200 ms of data. */ +#define MVS_MAX_Q_LEN 10 + +struct audio_mvs_info_type { + enum audio_mvs_state_type state; + uint32_t frame_mode; + uint32_t mvs_mode; + uint32_t buf_free_cnt; + uint32_t rate_type; + uint32_t dtx_mode; + + struct msm_rpc_endpoint *rpc_endpt; + uint32_t rpc_prog; + uint32_t rpc_ver; + uint32_t rpc_status; + + uint8_t *mem_chunk; + + struct list_head in_queue; + struct list_head free_in_queue; + + struct list_head out_queue; + struct list_head free_out_queue; + + struct task_struct *task; + + wait_queue_head_t wait; + wait_queue_head_t mode_wait; + wait_queue_head_t out_wait; + + struct mutex lock; + struct mutex in_lock; + struct mutex out_lock; + + struct wake_lock suspend_lock; + struct pm_qos_request pm_qos_req; +}; + +static struct audio_mvs_info_type audio_mvs_info; + +static int audio_mvs_setup_mode(struct audio_mvs_info_type *audio) +{ + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + switch (audio->mvs_mode) { + case MVS_MODE_AMR: + case MVS_MODE_AMR_WB: { + struct audio_mvs_set_amr_mode_msg set_amr_mode_msg; + struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg; + + /* Set AMR mode. */ + memset(&set_amr_mode_msg, 0, sizeof(set_amr_mode_msg)); + set_amr_mode_msg.amr_mode = cpu_to_be32(audio->rate_type); + + if (audio->mvs_mode == MVS_MODE_AMR) { + msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_AMR_SET_AMR_MODE_PROC); + } else { + msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_AMR_SET_AWB_MODE_PROC); + } + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_amr_mode_msg, + sizeof(set_amr_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set amr mode done\n"); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_AMR_DL; + + /* Disable DTX. */ + memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg)); + set_dtx_mode_msg.dtx_mode = cpu_to_be32(0); + + msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_SET_DTX_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_dtx_mode_msg, + sizeof(set_dtx_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set dtx done\n"); + + rc = 0; + } + } else { + MM_ERR("RPC write for set amr mode failed %d\n", rc); + } + break; + } + case MVS_MODE_PCM: + case MVS_MODE_LINEAR_PCM: { + /* PCM does not have any params to be set. + Save the MVS configuration information. */ + audio->rate_type = MVS_AMR_MODE_UNDEF; + audio->frame_mode = MVS_FRAME_MODE_PCM_DL; + break; + } + case MVS_MODE_PCM_WB: { + audio->rate_type = MVS_AMR_MODE_UNDEF; + audio->frame_mode = MVS_FRAME_MODE_PCM_WB_DL; + break; + } + case MVS_MODE_IS127: + case MVS_MODE_IS733: + case MVS_MODE_4GV_NB: + case MVS_MODE_4GV_WB: { + struct audio_mvs_set_voc_mode_msg set_voc_mode_msg; + + /* Set EVRC mode. */ + memset(&set_voc_mode_msg, 0, sizeof(set_voc_mode_msg)); + set_voc_mode_msg.min_rate = cpu_to_be32(audio->rate_type); + set_voc_mode_msg.max_rate = cpu_to_be32(audio->rate_type); + + msm_rpc_setup_req(&set_voc_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_VOC_SET_FRAME_RATE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_voc_mode_msg, + sizeof(set_voc_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set voc mode done\n"); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_VOC_RX; + + rc = 0; + } else { + MM_ERR("RPC write for set voc mode failed %d\n", rc); + } + break; + } + case MVS_MODE_G711: { + struct audio_mvs_set_g711_mode_msg set_g711_mode_msg; + + /* Set G711 mode. */ + memset(&set_g711_mode_msg, 0, sizeof(set_g711_mode_msg)); + set_g711_mode_msg.g711_mode = cpu_to_be32(audio->rate_type); + + MM_DBG("mode of g711:%d\n", set_g711_mode_msg.g711_mode); + + msm_rpc_setup_req(&set_g711_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G711_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g711_mode_msg, + sizeof(set_g711_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set g711 mode done\n"); + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G711_DL; + + rc = 0; + } else { + MM_ERR("RPC write for set g711 mode failed %d\n", rc); + } + break; + } + case MVS_MODE_G729A: { + struct audio_mvs_set_g729_mode_msg set_g729_mode_msg; + + /* Set G729 mode. */ + memset(&set_g729_mode_msg, 0, sizeof(set_g729_mode_msg)); + set_g729_mode_msg.g729_mode = cpu_to_be32(audio->dtx_mode); + + MM_DBG("mode of g729:%d\n", + set_g729_mode_msg.g729_mode); + + msm_rpc_setup_req(&set_g729_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G729A_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g729_mode_msg, + sizeof(set_g729_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set g729 mode done\n"); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G729A_DL; + + rc = 0; + } else { + MM_ERR("RPC write for set g729 mode failed %d\n", rc); + } + break; + } + case MVS_MODE_G722: { + struct audio_mvs_set_g722_mode_msg set_g722_mode_msg; + + /* Set G722 mode. */ + memset(&set_g722_mode_msg, 0, sizeof(set_g722_mode_msg)); + set_g722_mode_msg.g722_mode = cpu_to_be32(audio->rate_type); + + MM_DBG("mode of g722:%d\n", + set_g722_mode_msg.g722_mode); + + msm_rpc_setup_req(&set_g722_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G722_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g722_mode_msg, + sizeof(set_g722_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set g722 mode done\n"); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G722_DL; + + rc = 0; + } + break; + } + case MVS_MODE_G711A: { + struct audio_mvs_set_g711A_mode_msg set_g711A_mode_msg; + struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg; + + /* Set G711A mode. */ + memset(&set_g711A_mode_msg, 0, sizeof(set_g711A_mode_msg)); + set_g711A_mode_msg.g711A_mode = cpu_to_be32(audio->rate_type); + + MM_DBG("mode of g711A:%d\n", + set_g711A_mode_msg.g711A_mode); + + msm_rpc_setup_req(&set_g711A_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G711A_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g711A_mode_msg, + sizeof(set_g711A_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set g711A mode done\n"); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G711A_DL; + /* Set DTX MODE. */ + memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg)); + set_dtx_mode_msg.dtx_mode = + cpu_to_be32((audio->dtx_mode)); + + msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_SET_DTX_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_dtx_mode_msg, + sizeof(set_dtx_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set dtx done\n"); + + rc = 0; + } + rc = 0; + } else { + MM_ERR("RPC write for set g711A mode failed %d\n", rc); + } + break; + } + case MVS_MODE_EFR: + case MVS_MODE_FR: + case MVS_MODE_HR: { + struct audio_mvs_set_efr_mode_msg set_efr_mode_msg; + + /* Set G729 mode. */ + memset(&set_efr_mode_msg, 0, sizeof(set_efr_mode_msg)); + set_efr_mode_msg.efr_mode = cpu_to_be32(audio->dtx_mode); + + MM_DBG("mode of EFR, FR and HR:%d\n", + set_efr_mode_msg.efr_mode); + + msm_rpc_setup_req(&set_efr_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_GSM_SET_DTX_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_efr_mode_msg, + sizeof(set_efr_mode_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for set EFR, FR and HR mode done\n"); + + /* Save the MVS configuration information. */ + if ((audio->mvs_mode == MVS_MODE_EFR) || + (audio->mvs_mode == MVS_MODE_FR)) + audio->frame_mode = MVS_FRAME_MODE_GSM_DL; + if (audio->mvs_mode == MVS_MODE_HR) + audio->frame_mode = MVS_FRAME_MODE_HR_DL; + + rc = 0; + } else { + MM_ERR("RPC write for set EFR, FR" + "and HR mode failed %d\n", rc); + } + break; + } + default: + rc = -EINVAL; + MM_ERR("Default case\n"); + } + return rc; +} + +static int audio_mvs_setup(struct audio_mvs_info_type *audio) +{ + int rc = 0; + struct audio_mvs_enable_msg enable_msg; + + MM_DBG("\n"); + + /* Enable MVS. */ + memset(&enable_msg, 0, sizeof(enable_msg)); + enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP); + enable_msg.enable_args.mode = cpu_to_be32(audio->mvs_mode); + enable_msg.enable_args.ul_cb_func_id = cpu_to_be32(MVS_UL_CB_FUNC_ID); + enable_msg.enable_args.dl_cb_func_id = cpu_to_be32(MVS_DL_CB_FUNC_ID); + enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR); + + msm_rpc_setup_req(&enable_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_ENABLE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, &enable_msg, sizeof(enable_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for enable done\n"); + + rc = wait_event_timeout(audio->mode_wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 10 * HZ); + + if (rc > 0) { + MM_DBG("Wait event for enable succeeded\n"); + rc = audio_mvs_setup_mode(audio); + if (rc < 0) { + MM_ERR("Unknown MVS mode %d\n", + audio->mvs_mode); + } + MM_ERR("rc value after mode setup: %d\n", rc); + } else { + MM_ERR("Wait event for enable failed %d\n", rc); + } + } else { + MM_ERR("RPC write for enable failed %d\n", rc); + } + + return rc; +} + +static int audio_mvs_start(struct audio_mvs_info_type *audio) +{ + int rc = 0; + struct audio_mvs_acquire_msg acquire_msg; + + MM_DBG("\n"); + + /* Prevent sleep. */ + wake_lock(&audio->suspend_lock); + pm_qos_update_request(&audio->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + /* Acquire MVS. */ + memset(&acquire_msg, 0, sizeof(acquire_msg)); + acquire_msg.acquire_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP); + acquire_msg.acquire_args.cb_func_id = cpu_to_be32(MVS_CB_FUNC_ID); + + msm_rpc_setup_req(&acquire_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_ACQUIRE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &acquire_msg, + sizeof(acquire_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for acquire done\n"); + + rc = wait_event_timeout(audio->wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 1 * HZ); + + if (rc > 0) { + + rc = audio_mvs_setup(audio); + + if (rc == 0) + audio->state = AUDIO_MVS_STARTED; + + } else { + MM_ERR("Wait event for acquire failed %d\n", rc); + + rc = -EBUSY; + } + } else { + MM_ERR("RPC write for acquire failed %d\n", rc); + + rc = -EBUSY; + } + + return rc; +} + +static int audio_mvs_stop(struct audio_mvs_info_type *audio) +{ + int rc = 0; + struct audio_mvs_release_msg release_msg; + + MM_DBG("\n"); + + /* Release MVS. */ + memset(&release_msg, 0, sizeof(release_msg)); + release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP); + + msm_rpc_setup_req(&release_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_RELEASE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, &release_msg, sizeof(release_msg)); + + if (rc >= 0) { + MM_DBG("RPC write for release done\n"); + + rc = wait_event_timeout(audio->mode_wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 1 * HZ); + + if (rc > 0) { + MM_DBG("Wait event for release succeeded\n"); + + audio->state = AUDIO_MVS_STOPPED; + + /* Un-block read in case it is waiting for data. */ + wake_up(&audio->out_wait); + rc = 0; + } else { + MM_ERR("Wait event for release failed %d\n", rc); + } + } else { + MM_ERR("RPC write for release failed %d\n", rc); + } + + /* Allow sleep. */ + pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&audio->suspend_lock); + + return rc; +} + +static void audio_mvs_process_rpc_request(uint32_t procedure, + uint32_t xid, + void *data, + uint32_t length, + struct audio_mvs_info_type *audio) +{ + int rc = 0; + + MM_DBG("\n"); + + switch (procedure) { + case MVS_EVENT_CB_TYPE_PROC: { + struct audio_mvs_cb_func_args *args = data; + struct rpc_reply_hdr reply_hdr; + + MM_DBG("MVS CB CB_FUNC_ID 0x%x\n", + be32_to_cpu(args->cb_func_id)); + + if (be32_to_cpu(args->valid_ptr)) { + uint32_t event_type = be32_to_cpu(args->event); + + MM_DBG("MVS CB event type %d\n", + be32_to_cpu(args->event)); + + if (event_type == AUDIO_MVS_COMMAND) { + uint32_t cmd_status = be32_to_cpu( + args->event_data.mvs_ev_command_type.cmd_status); + + MM_DBG("MVS CB command status %d\n", + cmd_status); + + if (cmd_status == AUDIO_MVS_CMD_SUCCESS) { + audio->rpc_status = RPC_STATUS_SUCCESS; + wake_up(&audio->wait); + } + + } else if (event_type == AUDIO_MVS_MODE) { + uint32_t mode_status = be32_to_cpu( + args->event_data.mvs_ev_mode_type.mode_status); + + MM_DBG("MVS CB mode status %d\n", mode_status); + + if (mode_status == AUDIO_MVS_MODE_READY) { + audio->rpc_status = RPC_STATUS_SUCCESS; + wake_up(&audio->mode_wait); + } + } else { + MM_ERR("MVS CB unknown event type %d\n", + event_type); + } + } else { + MM_ERR("MVS CB event pointer not valid\n"); + } + + /* Send ack to modem. */ + memset(&reply_hdr, 0, sizeof(reply_hdr)); + reply_hdr.xid = cpu_to_be32(xid); + reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY); + reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32( + RPC_ACCEPTSTAT_SUCCESS); + reply_hdr.data.acc_hdr.verf_flavor = 0; + reply_hdr.data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(audio->rpc_endpt, + &reply_hdr, + sizeof(reply_hdr)); + + if (rc < 0) + MM_ERR("RPC write for response failed %d\n", rc); + + break; + } + + case MVS_PACKET_UL_FN_TYPE_PROC: { + uint32_t *args = data; + uint32_t pkt_len; + uint32_t frame_mode; + struct audio_mvs_ul_reply ul_reply; + struct audio_mvs_buf_node *buf_node = NULL; + + MM_DBG("MVS UL CB_FUNC_ID 0x%x\n", + be32_to_cpu(*args)); + args++; + + pkt_len = be32_to_cpu(*args); + MM_DBG("UL pkt_len %d\n", pkt_len); + args++; + + /* Copy the vocoder packets. */ + mutex_lock(&audio->out_lock); + + if (!list_empty(&audio->free_out_queue)) { + buf_node = list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + memcpy(&buf_node->frame.voc_pkt[0], args, pkt_len); + buf_node->frame.len = pkt_len; + pkt_len = ALIGN(pkt_len, 4); + args = args + pkt_len/4; + + MM_DBG("UL valid_ptr 0x%x\n", + be32_to_cpu(*args)); + args++; + + frame_mode = be32_to_cpu(*args); + MM_DBG("UL frame_mode %d\n", + frame_mode); + args++; + + MM_DBG("UL frame_mode %d\n", + be32_to_cpu(*args)); + args++; + + MM_DBG("UL frame_mode %d\n", + be32_to_cpu(*args)); + args++; + + MM_DBG("UL mvs_mode %d\n", + be32_to_cpu(*args)); + args++; + + MM_DBG("UL buf_free_cnt %d\n", + be32_to_cpu(*args)); + args++; + + if (frame_mode == MVS_FRAME_MODE_AMR_UL) { + /* Extract AMR frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + MM_DBG("UL AMR frame_type %d\n", + be32_to_cpu(*args)); + } else if ((frame_mode == MVS_FRAME_MODE_PCM_UL) || + (frame_mode == MVS_FRAME_MODE_VOC_TX)) { + /* PCM and EVRC don't have frame_type */ + buf_node->frame.frame_type = 0; + } else if (frame_mode == MVS_FRAME_MODE_G711_UL) { + /* Extract G711 frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + MM_DBG("UL G711 frame_type %d\n", + be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G729A_UL) { + /* Extract G729 frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + MM_DBG("UL G729 frame_type %d\n", + be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G722_UL) { + /* Extract G722 frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + MM_DBG("UL G722 frame_type %d\n", + be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G711A_UL) { + /* Extract G711A frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + MM_DBG("UL G711A frame_type %d\n", + be32_to_cpu(*args)); + } else if ((frame_mode == MVS_FRAME_MODE_GSM_UL) || + (frame_mode == MVS_FRAME_MODE_HR_UL)) { + /* Extract EFR, FR and HR frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + MM_DBG("UL EFR,FR,HR frame_type %d\n", + be32_to_cpu(*args)); + } else { + MM_DBG("UL Unknown frame mode %d\n", + frame_mode); + } + + list_add_tail(&buf_node->list, &audio->out_queue); + } else { + MM_ERR("UL data dropped, read is slow\n"); + } + + mutex_unlock(&audio->out_lock); + + wake_up(&audio->out_wait); + + /* Send UL message accept to modem. */ + memset(&ul_reply, 0, sizeof(ul_reply)); + ul_reply.reply_hdr.xid = cpu_to_be32(xid); + ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY); + ul_reply.reply_hdr.reply_stat = cpu_to_be32( + RPCMSG_REPLYSTAT_ACCEPTED); + + ul_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32( + RPC_ACCEPTSTAT_SUCCESS); + ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0; + ul_reply.reply_hdr.data.acc_hdr.verf_length = 0; + + ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001); + ul_reply.pkt_status = cpu_to_be32(0x00000000); + + rc = msm_rpc_write(audio->rpc_endpt, + &ul_reply, + sizeof(ul_reply)); + + if (rc < 0) + MM_ERR("RPC write for UL response failed %d\n", + rc); + + break; + } + + case MVS_PACKET_DL_FN_TYPE_PROC: { + struct audio_mvs_dl_cb_func_args *args = data; + struct audio_mvs_dl_reply dl_reply; + uint32_t frame_mode; + struct audio_mvs_buf_node *buf_node = NULL; + + MM_DBG("MVS DL CB CB_FUNC_ID 0x%x\n", + be32_to_cpu(args->cb_func_id)); + + frame_mode = be32_to_cpu(args->frame_mode); + MM_DBG("DL frame_mode %d\n", frame_mode); + + /* Prepare and send the DL packets to modem. */ + memset(&dl_reply, 0, sizeof(dl_reply)); + dl_reply.reply_hdr.xid = cpu_to_be32(xid); + dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY); + dl_reply.reply_hdr.reply_stat = cpu_to_be32( + RPCMSG_REPLYSTAT_ACCEPTED); + + dl_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32( + RPC_ACCEPTSTAT_SUCCESS); + dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0; + dl_reply.reply_hdr.data.acc_hdr.verf_length = 0; + + mutex_lock(&audio->in_lock); + + if (!list_empty(&audio->in_queue)) { + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + memcpy(&dl_reply.voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + MM_DBG("frame mode %d buf_node->frame.len %d\n", + frame_mode, buf_node->frame.len); + if (frame_mode == MVS_FRAME_MODE_AMR_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_PCM_DL) { + dl_reply.cdc_param.gnr_arg.param1 = 0; + dl_reply.cdc_param.gnr_arg.param2 = 0; + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_VOC_RX) { + dl_reply.cdc_param.gnr_arg.param1 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.gnr_arg.param2 = 0; + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G711_DL) { + dl_reply.cdc_param.g711_arg.param1 = + cpu_to_be32(buf_node->frame.frame_type); + dl_reply.cdc_param.\ + g711_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.g711_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G729A_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G722_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G711A_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if ((frame_mode == MVS_FRAME_MODE_GSM_DL) || + (frame_mode == MVS_FRAME_MODE_HR_DL)) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else { + MM_ERR("DL Unknown frame mode %d\n", + frame_mode); + } + list_add_tail(&buf_node->list, &audio->free_in_queue); + } else { + MM_DBG("No DL data available to send to MVS\n"); + if (frame_mode == MVS_FRAME_MODE_G711_DL) { + dl_reply.cdc_param.\ + g711_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.g711_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_SLOW); + } else { + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_SLOW); + } + } + + mutex_unlock(&audio->in_lock); + + dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001); + + dl_reply.frame_mode = cpu_to_be32(audio->frame_mode); + dl_reply.frame_mode_again = cpu_to_be32(audio->frame_mode); + + dl_reply.frame_info_hdr.frame_mode = + cpu_to_be32(audio->frame_mode); + dl_reply.frame_info_hdr.mvs_mode = cpu_to_be32(audio->mvs_mode); + dl_reply.frame_info_hdr.buf_free_cnt = 0; + + rc = msm_rpc_write(audio->rpc_endpt, + &dl_reply, + sizeof(dl_reply)); + + if (rc < 0) + MM_ERR("RPC write for DL response failed %d\n", + rc); + + break; + } + + default: + MM_ERR("Unknown CB type %d\n", procedure); + } +} + +static int audio_mvs_thread(void *data) +{ + struct audio_mvs_info_type *audio = data; + struct rpc_request_hdr *rpc_hdr = NULL; + + MM_DBG("\n"); + + while (!kthread_should_stop()) { + + int rpc_hdr_len = msm_rpc_read(audio->rpc_endpt, + (void **) &rpc_hdr, + -1, + -1); + + if (rpc_hdr_len < 0) { + MM_ERR("RPC read failed %d\n", + rpc_hdr_len); + + break; + } else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) { + continue; + } else { + uint32_t rpc_type = be32_to_cpu(rpc_hdr->type); + if (rpc_type == RPC_TYPE_REPLY) { + struct rpc_reply_hdr *rpc_reply = + (void *) rpc_hdr; + uint32_t reply_status; + + if (rpc_hdr_len < RPC_REPLY_HDR_SZ) + continue; + + reply_status = + be32_to_cpu(rpc_reply->reply_stat); + + if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) { + /* If the command is not accepted, there + * will be no response callback. Wake + * the caller and report error. */ + audio->rpc_status = RPC_STATUS_REJECT; + + wake_up(&audio->wait); + + MM_ERR("RPC reply status denied\n"); + } + } else if (rpc_type == RPC_TYPE_REQUEST) { + if (rpc_hdr_len < RPC_REQUEST_HDR_SZ) + continue; + + audio_mvs_process_rpc_request( + be32_to_cpu(rpc_hdr->procedure), + be32_to_cpu(rpc_hdr->xid), + (void *) (rpc_hdr + 1), + (rpc_hdr_len - sizeof(*rpc_hdr)), + audio); + } else { + MM_ERR("Unexpected RPC type %d\n", rpc_type); + } + } + + kfree(rpc_hdr); + rpc_hdr = NULL; + } + + MM_DBG("MVS thread stopped\n"); + + return 0; +} + +static int audio_mvs_alloc_buf(struct audio_mvs_info_type *audio) +{ + int i = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + + MM_DBG("\n"); + + /* Allocate input buffers. */ + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = kmalloc(sizeof(struct audio_mvs_buf_node), + GFP_KERNEL); + + if (buf_node != NULL) { + list_add_tail(&buf_node->list, + &audio->free_in_queue); + } else { + MM_ERR("No memory for IO buffers\n"); + goto err; + } + buf_node = NULL; + } + + /* Allocate output buffers. */ + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = kmalloc(sizeof(struct audio_mvs_buf_node), + GFP_KERNEL); + + if (buf_node != NULL) { + list_add_tail(&buf_node->list, + &audio->free_out_queue); + } else { + MM_ERR("No memory for IO buffers\n"); + goto err; + } + buf_node = NULL; + } + + return 0; + +err: + list_for_each_safe(ptr, next, &audio->free_in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + ptr = next = NULL; + list_for_each_safe(ptr, next, &audio->free_out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + return -ENOMEM; +} + +static void audio_mvs_free_buf(struct audio_mvs_info_type *audio) +{ + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct audio_mvs_buf_node *buf_node = NULL; + + MM_DBG("\n"); + + mutex_lock(&audio->in_lock); + /* Free input buffers. */ + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + ptr = next = NULL; + /* Free free_input buffers. */ + list_for_each_safe(ptr, next, &audio->free_in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + mutex_unlock(&audio->in_lock); + + mutex_lock(&audio->out_lock); + ptr = next = NULL; + /* Free output buffers. */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + /* Free free_ioutput buffers. */ + ptr = next = NULL; + list_for_each_safe(ptr, next, &audio->free_out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + mutex_unlock(&audio->out_lock); +} +static int audio_mvs_release(struct inode *inode, struct file *file) +{ + + struct audio_mvs_info_type *audio = file->private_data; + + MM_DBG("\n"); + + mutex_lock(&audio->lock); + if (audio->state == AUDIO_MVS_STARTED) + audio_mvs_stop(audio); + audio_mvs_free_buf(audio); + audio->state = AUDIO_MVS_CLOSED; + mutex_unlock(&audio->lock); + + MM_DBG("Release done\n"); + return 0; +} + +static ssize_t audio_mvs_read(struct file *file, + char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + MM_DBG("\n"); + + rc = wait_event_interruptible_timeout(audio->out_wait, + (!list_empty(&audio->out_queue) || + audio->state == AUDIO_MVS_STOPPED), + 1 * HZ); + + if (rc > 0) { + mutex_lock(&audio->out_lock); + if ((audio->state == AUDIO_MVS_STARTED) && + (!list_empty(&audio->out_queue))) { + + if (count >= sizeof(struct msm_audio_mvs_frame)) { + buf_node = list_first_entry(&audio->out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_to_user(buf, + &buf_node->frame, + sizeof(struct msm_audio_mvs_frame)); + + if (rc == 0) { + rc = buf_node->frame.len + + sizeof(buf_node->frame.frame_type) + + sizeof(buf_node->frame.len); + } else { + MM_ERR("Copy to user retuned %d", rc); + + rc = -EFAULT; + } + + list_add_tail(&buf_node->list, + &audio->free_out_queue); + } else { + MM_ERR("Read count %d < sizeof(frame) %d", + count, + sizeof(struct msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + MM_ERR("Read performed in state %d\n", + audio->state); + + rc = -EPERM; + } + mutex_unlock(&audio->out_lock); + + } else if (rc == 0) { + MM_ERR("No UL data available\n"); + + rc = -ETIMEDOUT; + } else { + MM_ERR("Read was interrupted\n"); + + rc = -ERESTARTSYS; + } + + return rc; +} + +static ssize_t audio_mvs_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + MM_DBG("\n"); + + mutex_lock(&audio->in_lock); + if (audio->state == AUDIO_MVS_STARTED) { + if (count <= sizeof(struct msm_audio_mvs_frame)) { + if (!list_empty(&audio->free_in_queue)) { + buf_node = + list_first_entry(&audio->free_in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_from_user(&buf_node->frame, + buf, + count); + + list_add_tail(&buf_node->list, + &audio->in_queue); + } else { + MM_ERR("No free DL buffs\n"); + } + } else { + MM_ERR("Write count %d < sizeof(frame) %d", + count, + sizeof(struct msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + MM_ERR("Write performed in invalid state %d\n", + audio->state); + + rc = -EPERM; + } + mutex_unlock(&audio->in_lock); + + return rc; +} + +static long audio_mvs_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + + struct audio_mvs_info_type *audio = file->private_data; + + MM_DBG("\n"); + + switch (cmd) { + case AUDIO_GET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + MM_DBG("IOCTL GET_MVS_CONFIG\n"); + + mutex_lock(&audio->lock); + config.mvs_mode = audio->mvs_mode; + config.rate_type = audio->rate_type; + mutex_unlock(&audio->lock); + + rc = copy_to_user((void *)arg, &config, sizeof(config)); + if (rc == 0) + rc = sizeof(config); + else + MM_ERR("Config copy failed %d\n", rc); + + break; + } + + case AUDIO_SET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + MM_DBG("IOCTL SET_MVS_CONFIG\n"); + + rc = copy_from_user(&config, (void *)arg, sizeof(config)); + if (rc == 0) { + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_OPENED) { + audio->mvs_mode = config.mvs_mode; + audio->rate_type = config.rate_type; + audio->dtx_mode = config.dtx_mode; + } else { + MM_ERR("Set confg called in state %d\n", + audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + } else { + MM_ERR("Config copy failed %d\n", rc); + } + + break; + } + + case AUDIO_START: { + MM_DBG("IOCTL START\n"); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_OPENED || + audio->state == AUDIO_MVS_STOPPED) { + rc = audio_mvs_start(audio); + if (rc != 0) + audio_mvs_stop(audio); + } else { + MM_ERR("Start called in invalid state %d\n", + audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + + break; + } + + case AUDIO_STOP: { + MM_DBG("IOCTL STOP\n"); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STARTED) { + rc = audio_mvs_stop(audio); + } else { + MM_ERR("Stop called in invalid state %d\n", + audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + break; + } + + default: { + MM_ERR("Unknown IOCTL %d\n", cmd); + } + } + + return rc; +} + +static int audio_mvs_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + MM_DBG("\n"); + + memset(&audio_mvs_info, 0, sizeof(audio_mvs_info)); + mutex_init(&audio_mvs_info.lock); + mutex_init(&audio_mvs_info.in_lock); + mutex_init(&audio_mvs_info.out_lock); + + init_waitqueue_head(&audio_mvs_info.wait); + init_waitqueue_head(&audio_mvs_info.mode_wait); + init_waitqueue_head(&audio_mvs_info.out_wait); + + INIT_LIST_HEAD(&audio_mvs_info.in_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_in_queue); + INIT_LIST_HEAD(&audio_mvs_info.out_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_out_queue); + + wake_lock_init(&audio_mvs_info.suspend_lock, + WAKE_LOCK_SUSPEND, + "audio_mvs_suspend"); + pm_qos_add_request(&audio_mvs_info.pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG, + MVS_VERS_COMP_VER2, + MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(audio_mvs_info.rpc_endpt)) { + MM_ERR("MVS RPC connect failed ver 0x%x\n", + MVS_VERS_COMP_VER2); + audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG, + MVS_VERS_COMP_VER3, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(audio_mvs_info.rpc_endpt)) { + MM_ERR("MVS RPC connect failed ver 0x%x\n", + MVS_VERS_COMP_VER3); + } else { + MM_DBG("MVS RPC connect succeeded ver 0x%x\n", + MVS_VERS_COMP_VER3); + audio_mvs_info.rpc_prog = MVS_PROG; + audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER3; + } + } else { + MM_DBG("MVS RPC connect succeeded ver 0x%x\n", + MVS_VERS_COMP_VER2); + audio_mvs_info.rpc_prog = MVS_PROG; + audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER2; + } + audio_mvs_info.task = kthread_run(audio_mvs_thread, + &audio_mvs_info, + "audio_mvs"); + if (IS_ERR(audio_mvs_info.task)) { + MM_ERR("MVS thread create failed\n"); + rc = PTR_ERR(audio_mvs_info.task); + audio_mvs_info.task = NULL; + msm_rpc_close(audio_mvs_info.rpc_endpt); + audio_mvs_info.rpc_endpt = NULL; + goto done; + } + + mutex_lock(&audio_mvs_info.lock); + + if (audio_mvs_info.state == AUDIO_MVS_CLOSED) { + + if (audio_mvs_info.task != NULL || + audio_mvs_info.rpc_endpt != NULL) { + rc = audio_mvs_alloc_buf(&audio_mvs_info); + + if (rc == 0) { + audio_mvs_info.state = AUDIO_MVS_OPENED; + file->private_data = &audio_mvs_info; + } + } else { + MM_ERR("MVS thread and RPC end point do not exist\n"); + + rc = -ENODEV; + } + } else { + MM_ERR("MVS driver exists, state %d\n", + audio_mvs_info.state); + + rc = -EBUSY; + } + + mutex_unlock(&audio_mvs_info.lock); + +done: + return rc; +} + +static const struct file_operations audio_mvs_fops = { + .owner = THIS_MODULE, + .open = audio_mvs_open, + .release = audio_mvs_release, + .read = audio_mvs_read, + .write = audio_mvs_write, + .unlocked_ioctl = audio_mvs_ioctl +}; + +struct miscdevice audio_mvs_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mvs", + .fops = &audio_mvs_fops +}; +static int __init audio_mvs_init(void) +{ + return misc_register(&audio_mvs_misc); +} + +static void __exit audio_mvs_exit(void) +{ + MM_DBG("\n"); + + misc_deregister(&audio_mvs_misc); +} + +module_init(audio_mvs_init); +module_exit(audio_mvs_exit); + +MODULE_DESCRIPTION("MSM MVS driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c new file mode 100644 index 00000000000..8eaf829bf51 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_out.c @@ -0,0 +1,1158 @@ +/* arch/arm/mach-msm/qdsp5/audio_out.c + * + * pcm audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include + +#include +#include + +#include "evlog.h" + +#define LOG_AUDIO_EVENTS 1 +#define LOG_AUDIO_FAULTS 0 + +#define SRS_ID_GLOBAL 0x00000001 +#define SRS_ID_WOWHD 0x00000002 +#define SRS_ID_CSHP 0x00000003 +#define SRS_ID_HPF 0x00000004 +#define SRS_ID_PEQ 0x00000005 +#define SRS_ID_HL 0x00000006 + +#define SRS_MASK_G 1 +#define SRS_MASK_W 2 +#define SRS_MASK_C 4 +#define SRS_MASK_HP 8 +#define SRS_MASK_P 16 +#define SRS_MASK_HL 32 + + +enum { + EV_NULL, + EV_OPEN, + EV_WRITE, + EV_RETURN, + EV_IOCTL, + EV_WRITE_WAIT, + EV_WAIT_EVENT, + EV_FILL_BUFFER, + EV_SEND_BUFFER, + EV_DSP_EVENT, + EV_ENABLE, +}; + +#if (LOG_AUDIO_EVENTS != 1) +static inline void LOG(unsigned id, unsigned arg) {} +#else +static const char *pcm_log_strings[] = { + "NULL", + "OPEN", + "WRITE", + "RETURN", + "IOCTL", + "WRITE_WAIT", + "WAIT_EVENT", + "FILL_BUFFER", + "SEND_BUFFER", + "DSP_EVENT", + "ENABLE", +}; + +DECLARE_LOG(pcm_log, 64, pcm_log_strings); + +static int __init _pcm_log_init(void) +{ + return ev_log_init(&pcm_log); +} +module_init(_pcm_log_init); + +#define LOG(id,arg) ev_log_write(&pcm_log, id, arg) +#endif + + + + + +#define BUFSZ (960 * 5) +#define DMASZ (BUFSZ * 2) + +#define COMMON_OBJ_ID 6 + +struct buffer { + void *data; + unsigned size; + unsigned used; + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t wait; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_weight; + uint32_t out_buffer_size; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int teos; /* valid only if tunnel mode & no data left for decoder */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + + struct wake_lock wakelock; + struct pm_qos_request pm_qos_req; + + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +struct audio_copp { + int mbadrc_enable; + int mbadrc_needs_commit; + char *mbadrc_data; + dma_addr_t mbadrc_phys; + + audpp_cmd_cfg_object_params_mbadrc mbadrc; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + + int rx_iir_enable; + int rx_iir_needs_commit; + audpp_cmd_cfg_object_params_pcm iir; + + audpp_cmd_cfg_object_params_volume vol_pan; + + int qconcert_plus_enable; + int qconcert_plus_needs_commit; + + int srs_enable; + int srs_needs_commit; + int srs_feature_mask; + audpp_cmd_cfg_object_params_qconcert qconcert_plus; + + int status; + int opened; + struct mutex lock; + + struct audpp_event_callback ecb; + + struct audpp_cmd_cfg_object_params_srstm_g g; + struct audpp_cmd_cfg_object_params_srstm_w w; + struct audpp_cmd_cfg_object_params_srstm_c c; + struct audpp_cmd_cfg_object_params_srstm_h h; + struct audpp_cmd_cfg_object_params_srstm_p p; + struct audpp_cmd_cfg_object_params_srstm_l l; +} the_audio_copp; + +static void audio_prevent_sleep(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + wake_lock(&audio->wakelock); + pm_qos_update_request(&audio->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); +} + +static void audio_allow_sleep(struct audio *audio) +{ + pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&audio->wakelock); + MM_DBG("\n"); /* Macro prints the file name and function */ +} + +static int audio_dsp_out_enable(struct audio *audio, int yes); +static int audio_dsp_send_buffer(struct audio *audio, unsigned id, unsigned len); + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static int audio_enable_srs_trumedia(struct audio_copp *audio_copp, int enable); +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + /* refuse to start if we're not ready */ + if (!audio->out[0].used || !audio->out[1].used) + return -EIO; + + /* we start buffers 0 and 1, so buffer 0 will be the + * next one the dsp will want + */ + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + audio_prevent_sleep(audio); + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) { + audio_allow_sleep(audio); + return rc; + } + + if (audpp_enable(-1, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + audmgr_disable(&audio->audmgr); + audio_allow_sleep(audio); + return -ENODEV; + } + + audio->enabled = 1; + htc_pwrsink_set(PWRSINK_AUDIO, 100); + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio_dsp_out_enable(audio, 0); + + audpp_disable(-1, audio); + + audio->stopped = 1; + wake_up(&audio->wait); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + audio_allow_sleep(audio); + } + return 0; +} + +void audio_commit_pending_pp_params(void *priv, unsigned id, uint16_t *msg) +{ + struct audio_copp *audio_copp = priv; + + if (AUDPP_MSG_CFG_MSG == id && msg[0] == AUDPP_MSG_ENA_DIS) + return; + + if (!audio_copp->status) + return; + + audpp_dsp_set_mbadrc(COMMON_OBJ_ID, audio_copp->mbadrc_enable, + &audio_copp->mbadrc); + + audpp_dsp_set_eq(COMMON_OBJ_ID, audio_copp->eq_enable, + &audio_copp->eq); + + audpp_dsp_set_rx_iir(COMMON_OBJ_ID, audio_copp->rx_iir_enable, + &audio_copp->iir); + audpp_dsp_set_vol_pan(COMMON_OBJ_ID, &audio_copp->vol_pan); + + audpp_dsp_set_qconcert_plus(COMMON_OBJ_ID, + audio_copp->qconcert_plus_enable, + &audio_copp->qconcert_plus); + audio_enable_srs_trumedia(audio_copp, true); +} +EXPORT_SYMBOL(audio_commit_pending_pp_params); + +/* ------------------- dsp --------------------- */ +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + struct buffer *frame; + unsigned long flags; + + LOG(EV_DSP_EVENT, id); + switch (id) { + case AUDPP_MSG_HOST_PCM_INTF_MSG: { + unsigned id = msg[2]; + unsigned idx = msg[3] - 1; + + /* MM_INFO("HOST_PCM id %d idx %d\n", id, idx); */ + if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) { + MM_ERR("bogus id\n"); + break; + } + if (idx > 1) { + MM_ERR("bogus buffer idx\n"); + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->running) { + atomic_add(audio->out[idx].used, &audio->out_bytes); + audio->out[idx].used = 0; + + frame = audio->out + audio->out_tail; + if (frame->used) { + audio_dsp_send_buffer( + audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + } else { + audio->out_needed++; + } + wake_up(&audio->wait); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + break; + } + case AUDPP_MSG_PCMDMAMISSED: + MM_INFO("PCMDMAMISSED %d\n", msg[0]); + audio->teos = 1; + wake_up(&audio->wait); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + LOG(EV_ENABLE, 1); + MM_DBG("CFG_MSG ENABLE\n"); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(5, &audio->vol_pan); + audio_dsp_out_enable(audio, 1); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + LOG(EV_ENABLE, 0); + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_ERR("CFG_MSG %d?\n", msg[0]); + } + break; + default: + MM_ERR("UNKNOWN (%d)\n", id); + } +} + +static int audio_dsp_out_enable(struct audio *audio, int yes) +{ + audpp_cmd_pcm_intf cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_PCM_INTF_2; + cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM; + cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + + if (yes) { + cmd.write_buf1LSW = audio->out[0].addr; + cmd.write_buf1MSW = audio->out[0].addr >> 16; + if (audio->out[0].used) + cmd.write_buf1_len = audio->out[0].used; + else + cmd.write_buf1_len = audio->out[0].size; + cmd.write_buf2LSW = audio->out[1].addr; + cmd.write_buf2MSW = audio->out[1].addr >> 16; + if (audio->out[1].used) + cmd.write_buf2_len = audio->out[1].used; + else + cmd.write_buf2_len = audio->out[1].size; + cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V; + cmd.weight_decoder_to_rx = audio->out_weight; + cmd.weight_arm_to_rx = 1; + cmd.partition_number_arm_to_dsp = 0; + cmd.sample_rate = audio->out_sample_rate; + cmd.channel_mode = audio->out_channel_mode; + } + + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, unsigned len) +{ + audpp_cmd_pcm_intf_send_buffer cmd; + + cmd.cmd_id = AUDPP_CMD_PCM_INTF_2; + cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM; + cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + cmd.dsp_to_arm_buf_id = 0; + cmd.arm_to_dsp_buf_id = idx + 1; + cmd.arm_to_dsp_buf_len = len; + + LOG(EV_SEND_BUFFER, idx); + dma_coherent_pre_ops(); + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static int audio_enable_mbadrc(struct audio_copp *audio_copp, int enable) +{ + if (audio_copp->mbadrc_enable == enable && + !audio_copp->mbadrc_needs_commit) + return 0; + + audio_copp->mbadrc_enable = enable; + if (is_audpp_enable()) { + audpp_dsp_set_mbadrc(COMMON_OBJ_ID, enable, + &audio_copp->mbadrc); + audio_copp->mbadrc_needs_commit = 0; + } + + return 0; +} + +static int audio_enable_eq(struct audio_copp *audio_copp, int enable) +{ + if (audio_copp->eq_enable == enable && + !audio_copp->eq_needs_commit) + return 0; + + audio_copp->eq_enable = enable; + + if (is_audpp_enable()) { + audpp_dsp_set_eq(COMMON_OBJ_ID, enable, &audio_copp->eq); + audio_copp->eq_needs_commit = 0; + } + return 0; +} + +static int audio_enable_rx_iir(struct audio_copp *audio_copp, int enable) +{ + if (audio_copp->rx_iir_enable == enable && + !audio_copp->rx_iir_needs_commit) + return 0; + + audio_copp->rx_iir_enable = enable; + + if (is_audpp_enable()) { + audpp_dsp_set_rx_iir(COMMON_OBJ_ID, enable, &audio_copp->iir); + audio_copp->rx_iir_needs_commit = 0; + } + return 0; +} + +static int audio_enable_srs_trumedia(struct audio_copp *audio_copp, int enable) +{ + + if (!audio_copp->srs_needs_commit) + return 0; + + audio_copp->srs_enable = enable; + + MM_DBG("Enable SRS flags 0x%x enable %d\n", + audio_copp->srs_feature_mask, enable); + if (is_audpp_enable()) { + MM_DBG("Updating audpp for srs\n"); + if (audio_copp->srs_feature_mask & SRS_MASK_W) + audpp_dsp_set_rx_srs_trumedia_w(&audio_copp->w); + if (audio_copp->srs_feature_mask & SRS_MASK_C) + audpp_dsp_set_rx_srs_trumedia_c(&audio_copp->c); + if (audio_copp->srs_feature_mask & SRS_MASK_HP) + audpp_dsp_set_rx_srs_trumedia_h(&audio_copp->h); + if (audio_copp->srs_feature_mask & SRS_MASK_P) + audpp_dsp_set_rx_srs_trumedia_p(&audio_copp->p); + if (audio_copp->srs_feature_mask & SRS_MASK_HL) + audpp_dsp_set_rx_srs_trumedia_l(&audio_copp->l); + if (audio_copp->srs_feature_mask & SRS_MASK_G) + audpp_dsp_set_rx_srs_trumedia_g(&audio_copp->g); + + audio_copp->srs_needs_commit = 0; + audio_copp->srs_feature_mask = 0; + } + return 0; +} + +static int audio_enable_vol_pan(struct audio_copp *audio_copp) +{ + if (is_audpp_enable()) + audpp_dsp_set_vol_pan(COMMON_OBJ_ID, &audio_copp->vol_pan); + return 0; +} + +static int audio_enable_qconcert_plus(struct audio_copp *audio_copp, int enable) +{ + if (audio_copp->qconcert_plus_enable == enable && + !audio_copp->qconcert_plus_needs_commit) + return 0; + + audio_copp->qconcert_plus_enable = enable; + + if (is_audpp_enable()) { + audpp_dsp_set_qconcert_plus(COMMON_OBJ_ID, enable, + &audio_copp->qconcert_plus); + audio_copp->qconcert_plus_needs_commit = 0; + } + return 0; +} + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->out_bytes); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(5, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(5, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + + LOG(EV_IOCTL, cmd); + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_enable(audio); + break; + case AUDIO_STOP: + rc = audio_disable(audio); + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count= AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + if (!audio->running) + return -EINVAL; + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->wait, + (!audio->out[0].used && + !audio->out[1].used)); + + if (rc < 0) + goto done; + + /* pcm dmamiss message is sent continously when + * decoder is starved so no race condition concern + */ + + audio->teos = 0; + + rc = wait_event_interruptible(audio->wait, + audio->teos); + +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static inline int rt_policy(int policy) +{ + if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR)) + return 1; + return 0; +} + +static inline int task_has_rt_policy(struct task_struct *p) +{ + return rt_policy(p->policy); +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct sched_param s = { .sched_priority = 1 }; + struct audio *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int old_prio = current->rt_priority; + int old_policy = current->policy; + int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE); + int rc = 0; + + LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24)); + + /* just for this write, set us real-time */ + if (!task_has_rt_policy(current)) { + struct cred *new = prepare_creds(); + cap_raise(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + if ((sched_setscheduler(current, SCHED_RR, &s)) < 0) + MM_ERR("sched_setscheduler failed\n"); + } + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + + LOG(EV_WAIT_EVENT, 0); + rc = wait_event_interruptible(audio->wait, + (frame->used == 0) || (audio->stopped)); + LOG(EV_WAIT_EVENT, 1); + + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = count > frame->size ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + spin_lock_irqsave(&audio->dsp_lock, flags); + LOG(EV_FILL_BUFFER, audio->out_head ^ 1); + frame = audio->out + audio->out_tail; + if (frame->used && audio->out_needed) { + audio_dsp_send_buffer(audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + audio->out_needed--; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + mutex_unlock(&audio->write_lock); + + /* restore scheduling policy and priority */ + if (!rt_policy(old_policy)) { + struct sched_param v = { .sched_priority = old_prio }; + if ((sched_setscheduler(current, old_policy, &v)) < 0) + MM_ERR("sched_setscheduler failed\n"); + if (likely(!cap_nice)) { + struct cred *new = prepare_creds(); + cap_lower(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + } + } + + LOG(EV_RETURN,(buf > start) ? (buf - start) : rc); + if (buf > start) + return buf - start; + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + LOG(EV_OPEN, 0); + mutex_lock(&audio->lock); + audio_disable(audio); + audio_flush(audio); + audio->opened = 0; + mutex_unlock(&audio->lock); + htc_pwrsink_set(PWRSINK_AUDIO, 0); + return 0; +} + +struct audio the_audio; + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + MM_ERR("busy\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + MM_ERR("could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + + audio->out_buffer_size = BUFSZ; + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_weight = 100; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + audio->vol_pan.pan = 0x0; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; + LOG(EV_OPEN, 1); +done: + mutex_unlock(&audio->lock); + return rc; +} + +static long audpp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio_copp *audio_copp = file->private_data; + int rc = 0, enable; + uint16_t enable_mask; + int prev_state; + uint32_t to_set, size = 0; + void *tmpbuf, *srs_params = NULL; + + mutex_lock(&audio_copp->lock); + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + enable = ((enable_mask & ADRC_ENABLE) || + (enable_mask & MBADRC_ENABLE)) ? 1 : 0; + audio_enable_mbadrc(audio_copp, enable); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio_copp, enable); + enable = (enable_mask & IIR_ENABLE) ? 1 : 0; + audio_enable_rx_iir(audio_copp, enable); + enable = (enable_mask & QCONCERT_PLUS_ENABLE) ? 1 : 0; + audio_enable_qconcert_plus(audio_copp, enable); + enable = (enable_mask & SRS_ENABLE) ? 1 : 0; + audio_enable_srs_trumedia(audio_copp, enable); + break; + + case AUDIO_SET_MBADRC: { + uint32_t mbadrc_coeff_buf; + prev_state = audio_copp->mbadrc_enable; + audio_copp->mbadrc_enable = 0; + if (copy_from_user(&audio_copp->mbadrc.num_bands, (void *) arg, + sizeof(audio_copp->mbadrc) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) + rc = -EFAULT; + else if (audio_copp->mbadrc.ext_buf_size) { + mbadrc_coeff_buf = (uint32_t) ((char *) arg + + sizeof(audio_copp->mbadrc) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)); + if ((copy_from_user(audio_copp->mbadrc_data, + (void *) mbadrc_coeff_buf, + AUDPP_MBADRC_EXTERNAL_BUF_SIZE * 2))) { + rc = -EFAULT; + break; + } + audio_copp->mbadrc.ext_buf_lsw = + audio_copp->mbadrc_phys & 0xFFFF; + audio_copp->mbadrc.ext_buf_msw = + ((audio_copp->mbadrc_phys & 0xFFFF0000) >> 16); + } + audio_copp->mbadrc_enable = prev_state; + if (!rc) + audio_copp->mbadrc_needs_commit = 1; + break; + } + + case AUDIO_SET_ADRC: { + struct audpp_cmd_cfg_object_params_adrc adrc; + prev_state = audio_copp->mbadrc_enable; + audio_copp->mbadrc_enable = 0; + if (copy_from_user(&adrc.compression_th, (void *) arg, + sizeof(adrc) - 2)) { + rc = -EFAULT; + audio_copp->mbadrc_enable = prev_state; + break; + } + audio_copp->mbadrc.num_bands = 1; + audio_copp->mbadrc.down_samp_level = 8; + audio_copp->mbadrc.adrc_delay = adrc.adrc_delay; + audio_copp->mbadrc.ext_buf_size = 0; + audio_copp->mbadrc.ext_partition = 0; + audio_copp->mbadrc.adrc_band[0].subband_enable = 1; + audio_copp->mbadrc.adrc_band[0].adrc_sub_mute = 0; + audio_copp->mbadrc.adrc_band[0].rms_time = + adrc.rms_time; + audio_copp->mbadrc.adrc_band[0].compression_th = + adrc.compression_th; + audio_copp->mbadrc.adrc_band[0].compression_slope = + adrc.compression_slope; + audio_copp->mbadrc.adrc_band[0].attack_const_lsw = + adrc.attack_const_lsw; + audio_copp->mbadrc.adrc_band[0].attack_const_msw = + adrc.attack_const_msw; + audio_copp->mbadrc.adrc_band[0].release_const_lsw = + adrc.release_const_lsw; + audio_copp->mbadrc.adrc_band[0].release_const_msw = + adrc.release_const_msw; + audio_copp->mbadrc.adrc_band[0].makeup_gain = 0x2000; + audio_copp->mbadrc_enable = prev_state; + audio_copp->mbadrc_needs_commit = 1; + break; + } + + case AUDIO_SET_EQ: + prev_state = audio_copp->eq_enable; + audio_copp->eq_enable = 0; + if (copy_from_user(&audio_copp->eq.num_bands, (void *) arg, + sizeof(audio_copp->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) + rc = -EFAULT; + audio_copp->eq_enable = prev_state; + audio_copp->eq_needs_commit = 1; + break; + + case AUDIO_SET_RX_IIR: + prev_state = audio_copp->rx_iir_enable; + audio_copp->rx_iir_enable = 0; + if (copy_from_user(&audio_copp->iir.num_bands, (void *) arg, + sizeof(audio_copp->iir) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) + rc = -EFAULT; + audio_copp->rx_iir_enable = prev_state; + audio_copp->rx_iir_needs_commit = 1; + break; + + case AUDIO_SET_VOLUME: + audio_copp->vol_pan.volume = arg; + audio_enable_vol_pan(audio_copp); + break; + + case AUDIO_SET_PAN: + audio_copp->vol_pan.pan = arg; + audio_enable_vol_pan(audio_copp); + break; + + case AUDIO_SET_QCONCERT_PLUS: + prev_state = audio_copp->qconcert_plus_enable; + audio_copp->qconcert_plus_enable = 0; + if (copy_from_user(&audio_copp->qconcert_plus.op_mode, + (void *) arg, + sizeof(audio_copp->qconcert_plus) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) + rc = -EFAULT; + audio_copp->qconcert_plus_enable = prev_state; + audio_copp->qconcert_plus_needs_commit = 1; + break; + + case AUDIO_SET_SRS_TRUMEDIA_PARAM: { + prev_state = audio_copp->srs_enable; + audio_copp->srs_enable = 0; + + if (copy_from_user(&to_set, (void *)arg, sizeof(uint32_t))) { + rc = -EFAULT; + break; + } + switch (to_set) { + case SRS_ID_GLOBAL: + srs_params = (void *)audio_copp->g.v; + size = sizeof(audio_copp->g.v); + audio_copp->srs_feature_mask |= SRS_MASK_G; + break; + case SRS_ID_WOWHD: + srs_params = (void *)audio_copp->w.v; + size = sizeof(audio_copp->w.v); + audio_copp->srs_feature_mask |= SRS_MASK_W; + break; + case SRS_ID_CSHP: + srs_params = (void *)audio_copp->c.v; + size = sizeof(audio_copp->c.v); + audio_copp->srs_feature_mask |= SRS_MASK_C; + break; + case SRS_ID_HPF: + srs_params = (void *)audio_copp->h.v; + size = sizeof(audio_copp->h.v); + audio_copp->srs_feature_mask |= SRS_MASK_HP; + break; + case SRS_ID_PEQ: + srs_params = (void *)audio_copp->p.v; + size = sizeof(audio_copp->p.v); + audio_copp->srs_feature_mask |= SRS_MASK_P; + break; + case SRS_ID_HL: + srs_params = (void *)audio_copp->l.v; + size = sizeof(audio_copp->l.v); + audio_copp->srs_feature_mask |= SRS_MASK_HL; + break; + default: + MM_ERR("SRS TruMedia error: invalid ioctl\n"); + rc = -EINVAL; + } + + if (rc >= 0) { + tmpbuf = kzalloc(sizeof(uint32_t) + size , GFP_KERNEL); + if (!tmpbuf) { + MM_ERR("SRS TruMedia error: no kernel mem\n"); + rc = -ENOMEM; + } else { + if (copy_from_user(tmpbuf, (void *)arg, + sizeof(uint32_t) + size)) + rc = -EFAULT; + memcpy(srs_params, + &(((uint32_t *)tmpbuf)[1]), size); + kfree(tmpbuf); + } + } + + MM_DBG("Ioctl SRS flags=0x%x\n", audio_copp->srs_feature_mask); + if (rc < 0) + MM_ERR("SRS TruMedia error setting params failed.\n"); + else{ + audio_copp->srs_needs_commit = 1; + audio_copp->srs_enable = prev_state; + } + break; + } + + default: + rc = -EINVAL; + } + + mutex_unlock(&audio_copp->lock); + return rc; +} + +static int audpp_open(struct inode *inode, struct file *file) +{ + struct audio_copp *audio_copp = &the_audio_copp; + int rc; + + mutex_lock(&audio_copp->lock); + if (audio_copp->opened) { + mutex_unlock(&audio_copp->lock); + return -EBUSY; + } + + audio_copp->opened = 1; + + if (!audio_copp->status) { + audio_copp->ecb.fn = audio_commit_pending_pp_params; + audio_copp->ecb.private = audio_copp; + rc = audpp_register_event_callback(&audio_copp->ecb); + if (rc) { + audio_copp->opened = 0; + mutex_unlock(&audio_copp->lock); + return rc; + } + audio_copp->mbadrc_data = dma_alloc_coherent(NULL, + AUDPP_MBADRC_EXTERNAL_BUF_SIZE * 2, + &audio_copp->mbadrc_phys, GFP_KERNEL); + if (!audio_copp->mbadrc_data) { + MM_ERR("could not allocate DMA buffers\n"); + audio_copp->opened = 0; + audpp_unregister_event_callback(&audio_copp->ecb); + mutex_unlock(&audio_copp->lock); + return -ENOMEM; + } + audio_copp->vol_pan.volume = 0x2000; + audio_copp->vol_pan.pan = 0x0; + audio_copp->status = 1; + } + + file->private_data = audio_copp; + mutex_unlock(&audio_copp->lock); + + return 0; +} + +static int audpp_release(struct inode *inode, struct file *file) +{ + struct audio_copp *audio_copp = &the_audio_copp; + + audio_copp->opened = 0; + + return 0; +} + +static struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +static struct file_operations audpp_fops = { + .owner = THIS_MODULE, + .open = audpp_open, + .release = audpp_release, + .unlocked_ioctl = audpp_ioctl, +}; + +struct miscdevice audio_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &audio_fops, +}; + +struct miscdevice audpp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_ctl", + .fops = &audpp_fops, +}; + +static int __init audio_init(void) +{ + mutex_init(&the_audio.lock); + mutex_init(&the_audio.write_lock); + mutex_init(&the_audio_copp.lock); + spin_lock_init(&the_audio.dsp_lock); + init_waitqueue_head(&the_audio.wait); + wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm"); + pm_qos_add_request(&the_audio.pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + return (misc_register(&audio_misc) || misc_register(&audpp_misc)); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm.c b/arch/arm/mach-msm/qdsp5/audio_pcm.c new file mode 100644 index 00000000000..49c781f0a09 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_pcm.c @@ -0,0 +1,1707 @@ + +/* audio_pcm.c - pcm audio decoder driver + * + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 decoder driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 + +/* Size must be power of 2 */ +#define BUFSZ_MAX 32768 +#define BUFSZ_MIN 4096 +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDDEC_DEC_PCM 0 + +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDPCM_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct audio; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audpcm_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audpcm_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audpcm_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr_ref; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audpcm_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audpcm_drv_operations { + void (*send_data)(struct audio *, unsigned); + void (*out_flush)(struct audio *); + int (*fsync)(struct audio *); +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + struct list_head out_queue; /* queue to retain output buffers */ + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_bits; /* bits per sample */ + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; + void *map_v_write; + + uint32_t drv_status; + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int teos; /* valid only if tunnel mode & no data left for decoder */ + int rmt_resource_released; + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + + unsigned volume; + + uint16_t dec_id; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audpcm_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + struct list_head pmem_region_queue; + struct audpcm_drv_operations drv_ops; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_PCM; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_PCM; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for PCM \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audio->drv_ops.send_data(audio, 1); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_ERR("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + + +struct msm_adsp_ops audpcmdec_adsp_ops = { + .event = audplay_dsp_event, +}; + + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + audpp_cmd_cfg_adec_params_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.stereo_cfg = audio->out_channel_mode; + cmd.pcm_width = audio->out_bits; + cmd.sign = 0; + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audpcm_async_send_data(struct audio *audio, unsigned needed) +{ + unsigned long flags; + + if (!audio->running) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload payload; + struct audpcm_buffer_node *used_buf; + + MM_DBG("consumed\n"); + + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audpcm_buffer_node, list); + list_del(&used_buf->list); + payload.aio_buf = used_buf->buf; + audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + } + if (audio->out_needed) { + struct audpcm_buffer_node *next_buf; + audplay_cmd_bitstream_data_avail cmd; + if (!list_empty(&audio->out_queue)) { + next_buf = list_first_entry(&audio->out_queue, + struct audpcm_buffer_node, list); + MM_DBG("next_buf %p\n", next_buf); + if (next_buf) { + MM_DBG("next buf phy %lx len %d\n", + next_buf->paddr, next_buf->buf.data_len); + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + if (next_buf->buf.data_len) + cmd.decoder_id = audio->dec_id; + else { + cmd.decoder_id = -1; + MM_DBG("input EOS signaled\n"); + } + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + if (!audio->running) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static void audpcm_async_flush(struct audio *audio) +{ + struct audpcm_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audpcm_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audio_ioport_reset(struct audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + MM_DBG("fsync in progress\n"); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + } else + audio->drv_ops.out_flush(audio); + } else { + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + } +} + +static int audpcm_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audpcm_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audpcm_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audpcm_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audpcm_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audpcm_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audpcm_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt && drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + mutex_lock(&audio->lock); + audpcm_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audpcm_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audpcm_pmem_region *region_elt; + struct audpcm_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audpcm_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audpcm_pmem_region *region; + struct vm_area_struct *vma; + int rc = -EINVAL; + + MM_DBG("\n"); /* Macro prints the file name and function */ + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + return -EINVAL; + } + + vma = find_vma_intersection(current->active_mm, + (unsigned long) info->vaddr, (unsigned long) info->vaddr+1); + + if (vma && ((vma->vm_end - vma->vm_start) == len)) { + rc = audpcm_pmem_check(audio, (void *) vma->vm_start, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + return rc; + } + region->vaddr = (void *) vma->vm_start; + region->vaddr_ref = info->vaddr; + MM_DBG("Valid VMA region vma->vm_start = 0x%8x \ + vma->vm_end = 0x%8x\n", (int) vma->vm_start, + (int) vma->vm_end); + } else { + MM_ERR("No valid VMA region found\n"); + put_pmem_file(file); + kfree(region); + return rc; + } + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr, + region->vaddr, region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); + return rc; +} + +static int audpcm_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audpcm_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audpcm_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr_ref == info->vaddr)) { + if (region->ref_cnt) { + MM_DBG("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + MM_DBG("remove region fd %d vaddr %p \n", info->fd, + info->vaddr); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audpcm_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audpcm_pmem_region **region) +{ + struct audpcm_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audpcm_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audpcm_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + MM_ERR("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audpcm_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audpcm_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len %d\n", + buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + + buf_node->paddr = audpcm_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1) || + (!buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + audio->drv_ops.send_data(audio, 0); + } + + MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr); + + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audpcm_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + if (config.bits == 8) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8; + else if (config.bits == 16) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + else if (config.bits == 24) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_24; + else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_bits = config.bits; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8) + config.bits = 8; + else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_24) + config.bits = 24; + else + config.bits = 16; + config.unused[0] = 0; + config.unused[1] = 0; + + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + + + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audpcm_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audpcm_pmem_remove(audio, &info); + break; + } + + case AUDIO_ASYNC_WRITE: + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audpcm_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_ASYNC_READ: + MM_ERR("AUDIO_ASYNC_READ not supported\n"); + rc = -EPERM; + break; + + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audpcm_async_fsync(struct audio *audio) +{ + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + (audio->teos && audio->out_needed && + list_empty(&audio->out_queue)) + || audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audpcm_sync_fsync(struct audio *audio) +{ + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audio->drv_ops.send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +int audpcm_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + + if (!audio->running) + return -EINVAL; + + return audio->drv_ops.fsync(audio); +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0; + unsigned dsize; + + if (audio->drv_status & ADRV_STATUS_AIO_INTF) + return -EPERM; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > (frame->size - 1)) ? + frame->size - 1 : count; + cpy_ptr++; + dsize = 1; + audio->reserved = 0; + } else + xfer = (count > frame->size) ? frame->size : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audio->drv_ops.send_data(audio, 0); + } + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + + return rc; +} + +static void audpcm_reset_pmem_region(struct audio *audio) +{ + struct audpcm_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audpcm_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audio->drv_ops.out_flush(audio); + audpcm_reset_pmem_region(audio); + + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audpcm_reset_event_queue(audio); + MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data); + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audpcm_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audpcm_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audpcm_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audpcm_suspend(struct early_suspend *h) +{ + struct audpcm_suspend_ctl *ctl = + container_of(h, struct audpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audpcm_resume(struct early_suspend *h) +{ + struct audpcm_suspend_ctl *ctl = + container_of(h, struct audpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audpcm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audpcm_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audpcm_debug_fops = { + .read = audpcm_debug_read, + .open = audpcm_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, i, dec_attrb, decid; + struct audpcm_event *e_node = NULL; + unsigned pmem_sz = DMASZ_MAX; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_pcm_dec_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_PCM; + if (file->f_mode & FMODE_READ) { + MM_ERR("Non-Tunneled mode not supported\n"); + rc = -EPERM; + kfree(audio); + goto done; + } else + dec_attrb |= MSM_AUD_MODE_TUNNEL; + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available\n"); + rc = -ENODEV; + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + /* Non AIO interface */ + if (!(file->f_flags & O_NONBLOCK)) { + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, + SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap( + audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write\ + buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->phys); + audpp_adec_free(audio->dec_id); + MM_DBG("audio instance 0x%08x\ + freeing\n", (int)audio); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr\ + 0x%08x\n", audio->phys,\ + (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers\n"); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + MM_DBG("audio instance 0x%08x freeing\n",\ + (int)audio); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto err; + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audpcmdec_adsp_ops, audio); + if (rc) { + MM_ERR("failed to get %s module\n", audio->module_name); + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for PCM session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + if (file->f_flags & O_NONBLOCK) { + MM_DBG("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.send_data = audpcm_async_send_data; + audio->drv_ops.out_flush = audpcm_async_flush; + audio->drv_ops.fsync = audpcm_async_fsync; + } else { + MM_DBG("set to std io interface\n"); + audio->drv_ops.send_data = audplay_send_data; + audio->drv_ops.out_flush = audio_flush; + audio->drv_ops.fsync = audpcm_sync_fsync; + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = (audio->out_dma_sz >> 1); + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + audio->volume = 0x2000; + audio->drv_ops.out_flush(audio); + + file->private_data = audio; + audio->opened = 1; + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_pcm_dec_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audpcm_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audpcm_resume; + audio->suspend_ctl.node.suspend = audpcm_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDPCM_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audpcm_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + audpp_adec_free(audio->dec_id); + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + kfree(audio); + return rc; +} + +static const struct file_operations audio_pcm_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audpcm_fsync, +}; + +struct miscdevice audio_pcm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_dec", + .fops = &audio_pcm_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_pcm_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5/audio_pcm_in.c new file mode 100644 index 00000000000..851980db607 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_pcm_in.c @@ -0,0 +1,956 @@ +/* arch/arm/mach-msm/qdsp5/audio_pcm_in.c + * + * pcm audio input device + * + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c, + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include +#include +#include +#include +#include + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_SIZE (2052 * 2) +#define MONO_DATA_SIZE (2048) +#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) +#define DMASZ (FRAME_SIZE * FRAME_NUM) +#define MSM_AUD_BUFFER_UPDATE_WAIT_MS 2000 + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + + struct msm_adsp_module *audpre; + struct msm_adsp_module *audrec; + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */ + uint32_t enc_type; /* 0 for PCM */ + uint32_t mode; /* Tunnel for PCM */ + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + unsigned short samp_rate_index; + uint32_t audrec_obj_idx ; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + + /* audpre settings */ + int tx_agc_enable; + audpreproc_cmd_cfg_agc_params tx_agc_cfg; + int ns_enable; + audpreproc_cmd_cfg_ns_params ns_cfg; + /* For different sample rate, the coeff might be different. * + * All the coeff should be passed from user space */ + int iir_enable; + audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg; +}; + +static int audpcm_in_dsp_enable(struct audio_in *audio, int enable); +static int audpcm_in_encmem_config(struct audio_in *audio); +static int audpcm_in_encparam_config(struct audio_in *audio); +static int audpcm_in_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); +static void audpcm_in_flush(struct audio_in *audio); +static int audio_dsp_set_tx_agc(struct audio_in *audio); +static int audio_dsp_set_ns(struct audio_in *audio); +static int audio_dsp_set_iir(struct audio_in *audio); + +static unsigned convert_dsp_samp_index(unsigned index) +{ + switch (index) { + case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000; + case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100; + case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000; + case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000; + case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050; + case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000; + case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000; + case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025; + case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000; + default: return AUDREC_CMD_SAMP_RATE_INDX_11025; + } +} + +static unsigned convert_samp_rate(unsigned hz) +{ + switch (hz) { + case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000; + case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100; + case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000; + case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000; + case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050; + case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000; + case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000; + case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025; + case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000; + default: return RPC_AUD_DEF_SAMPLE_RATE_11025; + } +} + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* must be called with audio->lock held */ +static int audpcm_in_enable(struct audio_in *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + MM_ERR("msm_adsp_enable(audpre) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + if (msm_adsp_enable(audio->audrec)) { + audmgr_disable(&audio->audmgr); + msm_adsp_disable(audio->audpre); + MM_ERR("msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audpcm_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audpcm_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audpcm_in_dsp_enable(audio, 0); + + audio->stopped = 1; + wake_up(&audio->wait); + + msm_adsp_disable(audio->audrec); + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + MM_INFO("type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + MM_ERR("err_index %d\n", msg[0]); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +struct audio_frame { + uint16_t count_low; + uint16_t count_high; + uint16_t bytes; + uint16_t unknown; + unsigned char samples[]; +} __packed; + +static void audpcm_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - + sizeof(*frame)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->bytes; + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("Error! not able to keep up the read\n"); + } else + audio->in_count++; + + audpcm_in_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = NULL; + uint16_t msg[3]; + + if (data) + audio = data; + else { + MM_ERR("invalid data for event %x\n", id); + return; + } + + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: { + if (msg[0] & AUDREC_MSG_CFG_DONE_ENC_ENA) { + audio->audrec_obj_idx = msg[1]; + MM_INFO("CFG ENABLED\n"); + audpcm_in_encmem_config(audio); + } else { + MM_INFO("CFG SLEEP\n"); + audio->running = 0; + audio->tx_agc_enable = 0; + audio->ns_enable = 0; + audio->iir_enable = 0; + } + break; + } + case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { + MM_DBG("AREC_MEM_CFG_DONE_MSG\n"); + audpcm_in_encparam_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_INFO("PARAM CFG DONE\n"); + audio->running = 1; + audio_dsp_set_tx_agc(audio); + audio_dsp_set_ns(audio); + audio_dsp_set_iir(audio); + break; + } + case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: { + MM_DBG("ERROR %x\n", msg[0]); + break; + } + case AUDREC_MSG_PACKET_READY_MSG: { + struct audrec_msg_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt msw %d \ + write cnt lsw %d read cnt msw %d read cnt lsw %d \n",\ + pkt_ready_msg.pkt_counter_msw, \ + pkt_ready_msg.pkt_counter_lsw, \ + pkt_ready_msg.pkt_read_cnt_msw, \ + pkt_ready_msg.pkt_read_cnt_lsw); + + audpcm_in_get_dsp_frames(audio); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + } + default: + MM_ERR("unknown event %d\n", id); + } +} + +static struct msm_adsp_ops audpre_adsp_ops = { + .event = audpre_dsp_event, +}; + +static struct msm_adsp_ops audrec_adsp_ops = { + .event = audrec_dsp_event, +}; + + +#define audio_send_queue_pre(audio, cmd, len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) + +#define audio_send_queue_recbs(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +static int audio_dsp_set_tx_agc(struct audio_in *audio) +{ + audpreproc_cmd_cfg_agc_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + audio->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS; + if (audio->tx_agc_enable) { + /* cmd.tx_agc_param_mask = 0xFE00 from sample code */ + audio->tx_agc_cfg.tx_agc_param_mask = + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + audio->tx_agc_cfg.tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA; + /* cmd.param_mask = 0xFFF0 from sample code */ + audio->tx_agc_cfg.param_mask = + (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) | + (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) | + (1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK); + } else { + audio->tx_agc_cfg.tx_agc_param_mask = + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + audio->tx_agc_cfg.tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS; + } + cmd = audio->tx_agc_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_tx_agc(struct audio_in *audio, int enable) +{ + if (audio->tx_agc_enable != enable) { + audio->tx_agc_enable = enable; + if (audio->running) + audio_dsp_set_tx_agc(audio); + } + return 0; +} + +static int audio_dsp_set_ns(struct audio_in *audio) +{ + audpreproc_cmd_cfg_ns_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + audio->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS; + + if (audio->ns_enable) { + /* cmd.ec_mode_new is fixed as 0x0064 when enable + * from sample code */ + audio->ns_cfg.ec_mode_new = + AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA | + AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA | + AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA; + } else { + audio->ns_cfg.ec_mode_new = + AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS; + } + cmd = audio->ns_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_ns(struct audio_in *audio, int enable) +{ + if (audio->ns_enable != enable) { + audio->ns_enable = enable; + if (audio->running) + audio_dsp_set_ns(audio); + } + return 0; +} + +static int audio_dsp_set_iir(struct audio_in *audio) +{ + audpreproc_cmd_cfg_iir_tuning_filter_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + audio->iir_cfg.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; + + if (audio->iir_enable) + /* cmd.active_flag is 0xFFFF from sample code but 0x0001 here */ + audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA; + else + audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS; + + cmd = audio->iir_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_iir(struct audio_in *audio, int enable) +{ + if (audio->iir_enable != enable) { + audio->iir_enable = enable; + if (audio->running) + audio_dsp_set_iir(audio); + } + return 0; +} + +static int audpcm_in_dsp_enable(struct audio_in *audio, int enable) +{ + struct audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ENC_CFG; + + cmd.audrec_enc_type = (audio->enc_type & 0xFF) | + (enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS); + /* Don't care */ + cmd.audrec_obj_idx = audio->audrec_obj_idx; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_in_encmem_config(struct audio_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t cnt = 0; + uint16_t *data = (void *) audio->data; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG; + cmd.audrec_obj_idx = audio->audrec_obj_idx; + /* Rate at which packet complete message comes */ + cmd.audrec_up_pkt_intm_cnt = 1; + cmd.audrec_extpkt_buffer_msw = audio->phys >> 16; + cmd.audrec_extpkt_buffer_lsw = audio->phys; + /* Max Buffer no available for frames */ + cmd.audrec_extpkt_buffer_num = FRAME_NUM; + + /* prepare buffer pointers: + * Mono: 1024 samples + 4 halfword header + * Stereo: 2048 samples + 4 halfword header + */ + for (cnt = 0; cnt < FRAME_NUM; cnt++) { + audio->in[cnt].data = data + 4; + data += (4 + (audio->channel_mode ? 2048 : 1024)); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_in_encparam_config(struct audio_in *audio) +{ + struct audrec_cmd_arecparam_wav_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG; + cmd.common.audrec_obj_idx = audio->audrec_obj_idx; + cmd.samp_rate_idx = audio->samp_rate_index; + cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */ + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_in_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + cmd.type = audio->audrec_obj_idx; + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audpcm_in_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = FRAME_NUM-1; i >= 0; i--) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } +} + +static long audpcm_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + rc = audpcm_in_enable(audio); + audio->stopped = 0; + break; + } + case AUDIO_STOP: + rc = audpcm_in_disable(audio); + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audpcm_in_flush(audio); + mutex_unlock(&audio->read_lock); + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.channel_count == 1) { + cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO; + } else if (cfg.channel_count == 2) { + cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + + audio->samp_rate = convert_samp_rate(cfg.sample_rate); + audio->samp_rate_index = + convert_dsp_samp_index(cfg.sample_rate); + audio->channel_mode = cfg.channel_count; + audio->buffer_size = + audio->channel_mode ? STEREO_DATA_SIZE + : MONO_DATA_SIZE; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) + cfg.channel_count = 1; + else + cfg.channel_count = 2; + cfg.type = 0; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audpcm_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible_timeout( + audio->wait, (audio->in_count > 0) || audio->stopped, + msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS)); + if (rc == 0) { + rc = -ETIMEDOUT; + break; + } else if (rc < 0) { + break; + } + + if (audio->stopped && !audio->in_count) { + rc = 0;/* End of File */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + if (count >= size) { + /* order the reads on the buffer */ + dma_coherent_post_ops(); + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is invalid and we need to + * retry + */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + } else { + MM_ERR("short read\n"); + break; + } + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static ssize_t audpcm_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int audpcm_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audpcm_in_disable(audio); + audpcm_in_flush(audio); + audpreproc_aenc_free(audio->enc_id); + msm_adsp_put(audio->audrec); + msm_adsp_put(audio->audpre); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + if (audio->data) { + free_contiguous_memory((void *)audio->data); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +static struct audio_in the_audio_in; + +static int audpcm_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + int rc; + + int encid; + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025; + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025; + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + audio->buffer_size = MONO_DATA_SIZE; + audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_WAV | audio->mode; + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_adsp_ops, audio); + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_adsp_ops, audio); + if (rc) { + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->dsp_cnt = 0; + audio->stopped = 0; + + audpcm_in_flush(audio); + + audio->data = allocate_contiguous_memory(DMASZ, MEMTYPE_EBI1, + SZ_4K, 0); + if (!audio->data) { + MM_ERR("could not allocate read buffers\n"); + rc = -ENOMEM; + goto evt_error; + } else { + audio->phys = memory_pool_node_paddr(audio->data); + if (!audio->phys) { + MM_ERR("could not get physical address\n"); + rc = -ENOMEM; + free_contiguous_memory(audio->data); + goto evt_error; + } + MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + msm_adsp_put(audio->audpre); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0, enable; + uint16_t enable_mask; + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_ENABLE_AUDPRE: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + enable = (enable_mask & AGC_ENABLE) ? 1 : 0; + audio_enable_tx_agc(audio, enable); + enable = (enable_mask & NS_ENABLE) ? 1 : 0; + audio_enable_ns(audio, enable); + enable = (enable_mask & TX_IIR_ENABLE) ? 1 : 0; + audio_enable_iir(audio, enable); + break; + + case AUDIO_SET_AGC: + if (copy_from_user(&audio->tx_agc_cfg, (void *) arg, + sizeof(audio->tx_agc_cfg))) + rc = -EFAULT; + break; + + case AUDIO_SET_NS: + if (copy_from_user(&audio->ns_cfg, (void *) arg, + sizeof(audio->ns_cfg))) + rc = -EFAULT; + break; + + case AUDIO_SET_TX_IIR: + if (copy_from_user(&audio->iir_cfg, (void *) arg, + sizeof(audio->iir_cfg))) + rc = -EFAULT; + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&audio->lock); + return rc; +} + +static int audpre_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + + file->private_data = audio; + + return 0; +} + +static const struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audpcm_in_open, + .release = audpcm_in_release, + .read = audpcm_in_read, + .write = audpcm_in_write, + .unlocked_ioctl = audpcm_in_ioctl, +}; + +static struct miscdevice audpcm_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &audio_fops, +}; + +static const struct file_operations audpre_fops = { + .owner = THIS_MODULE, + .open = audpre_open, + .unlocked_ioctl = audpre_ioctl, +}; + +static struct miscdevice audpre_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_preproc_ctl", + .fops = &audpre_fops, +}; + +static int __init audpcm_in_init(void) +{ + + mutex_init(&the_audio_in.lock); + mutex_init(&the_audio_in.read_lock); + spin_lock_init(&the_audio_in.dsp_lock); + init_waitqueue_head(&the_audio_in.wait); + return misc_register(&audpcm_in_misc) || misc_register(&audpre_misc); +} +device_initcall(audpcm_in_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c new file mode 100644 index 00000000000..f4367598b0c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c @@ -0,0 +1,1620 @@ +/* arch/arm/mach-msm/qdsp5/audio_qcelp.c + * + * qcelp 13k audio decoder device + * + * Copyright (c) 2008-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This code is based in part on audio_mp3.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +#define BUFSZ 1094 /* QCELP 13K Hold 600ms packet data = 36 * 30 and + 14 bytes of meta in */ +#define BUF_COUNT 2 +#define DMASZ (BUFSZ * BUF_COUNT) + +#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and + 24 bytes of meta out */ +#define PCM_BUF_MAX_COUNT 5 + +#define AUDDEC_DEC_QCELP 9 + +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDQCELP_METAFIELD_MASK 0xFFFF0000 +#define AUDQCELP_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDQCELP_EOS_FLG_MASK 0x01 +#define AUDQCELP_EOS_NONE 0x0 /* No EOS detected */ +#define AUDQCELP_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDQCELP_EVENT_NUM 10 /* Default number of pre-allocated event pkts */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audqcelp_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audqcelp_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[BUF_COUNT]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section - START */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* Host PCM section - END */ + + struct msm_adsp_module *audplay; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; /* set when non-tunnel mode */ + uint8_t buf_refresh:1; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int rmt_resource_released; + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audqcelp_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audqcelp_send_data(struct audio *audio, unsigned needed); +static void audqcelp_config_hostpcm(struct audio *audio); +static void audqcelp_buffer_refresh(struct audio *audio); +static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audqcelp_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_QCELP; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_QCELP; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audqcelp_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for QCELP \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_13K; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audqcelp_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audqcelp_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audqcelp_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audqcelp_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audqcelp_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + } +} + +static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + if (audio->pcm_feedback) { + audqcelp_config_hostpcm(audio); + audqcelp_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audqcelp_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_qcelp = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_v13k cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDQCELP_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audqcelp_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audqcelp_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + +static void audqcelp_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audqcelp_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->out_needed = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audqcelp_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audqcelp_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audqcelp_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audqcelp_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audqcelp_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audqcelp_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audqcelp_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audqcelp_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audqcelp_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + + +static long audqcelp_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audqcelp_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audqcelp_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audqcelp_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audqcelp_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audqcelp_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audqcelp_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audqcelp_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audqcelp_disable(audio); + audqcelp_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audqcelp_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user(&config, (void *)arg, + sizeof(config))) { + rc = -EFAULT; + break; + } + audio->mfield = config.meta_field; + MM_DBG("AUDIO_SET_CONFIG applicable \ + for metafield configuration\n"); + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = BUF_COUNT; + config.sample_rate = 8000; + config.channel_count = 1; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + + if (copy_from_user(&config, (void *)arg, + sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", + config.buffer_count * config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read buf\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr 0x%08x \ + kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audqcelp_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d\n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + not know frame size, read count must be greater or equal + to size of PCM samples */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + /* order reads from the output buffer */ + rmb(); + if (copy_to_user(buf, + audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x\n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audqcelp_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audqcelp_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audqcelp_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audqcelp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDQCELP_EOS_NONE; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + MM_DBG("buffer available\n"); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] & + AUDQCELP_EOS_FLG_MASK) { + MM_DBG("EOS SET\n"); + eos_condition = AUDQCELP_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] &= + ~AUDQCELP_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer + mfield_size; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + audqcelp_send_data(audio, 0); + } + if (eos_condition == AUDQCELP_EOS_SET) + rc = audqcelp_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audqcelp_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int) audio); + mutex_lock(&audio->lock); + audqcelp_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audqcelp_flush(audio); + audqcelp_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audqcelp_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audqcelp_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audqcelp_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audqcelp_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audqcelp_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audqcelp_suspend(struct early_suspend *h) +{ + struct audqcelp_suspend_ctl *ctl = + container_of(h, struct audqcelp_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audqcelp_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audqcelp_resume(struct early_suspend *h) +{ + struct audqcelp_suspend_ctl *ctl = + container_of(h, struct audqcelp_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audqcelp_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audqcelp_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audqcelp_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audqcelp_debug_fops = { + .read = audqcelp_debug_read, + .open = audqcelp_debug_open, +}; +#endif + +static int audqcelp_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audqcelp_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_qcelp_" + 5]; +#endif + + /* Create audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_QCELP; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_qcelp, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for QCELP session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + /* Initialize buffer */ + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + + audqcelp_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_qcelp_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audqcelp_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audqcelp_resume; + audio->suspend_ctl.node.suspend = audqcelp_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDQCELP_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audqcelp_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audqcelp_open, + .release = audqcelp_release, + .read = audqcelp_read, + .write = audqcelp_write, + .unlocked_ioctl = audqcelp_ioctl, + .fsync = audqcelp_fsync, +}; + +struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +static int __init audqcelp_init(void) +{ + return misc_register(&audio_qcelp_misc); +} + +static void __exit audqcelp_exit(void) +{ + misc_deregister(&audio_qcelp_misc); +} + +module_init(audqcelp_init); +module_exit(audqcelp_exit); + +MODULE_DESCRIPTION("MSM QCELP 13K driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c new file mode 100644 index 00000000000..92036e58a65 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c @@ -0,0 +1,1400 @@ +/* arch/arm/mach-msm/qdsp5/audio_qcelp_in.c + * + * qcelp audio input device + * + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c, + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include "audmgr.h" + +#include +#include +#include +#include +#include +#include + +#define FRAME_HEADER_SIZE 8 /* 8 bytes frame header */ +#define NT_FRAME_HEADER_SIZE 24 /* 24 bytes frame header */ +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM 8 +#define QCELP_FRAME_SIZE 36 /* 36 bytes data */ +/*Tunnel mode : 36 bytes data + 8 byte header*/ +#define FRAME_SIZE (QCELP_FRAME_SIZE + FRAME_HEADER_SIZE) + /* 36 bytes data + 24 meta field*/ +#define NT_FRAME_SIZE (QCELP_FRAME_SIZE + NT_FRAME_HEADER_SIZE) +#define DMASZ (FRAME_SIZE * FRAME_NUM) +#define NT_DMASZ (NT_FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM 2 +#define OUT_BUFFER_SIZE (4 * 1024 + NT_FRAME_HEADER_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + +/* Offset from beginning of buffer*/ +#define AUDPREPROC_QCELP_EOS_FLG_OFFSET 0x0A +#define AUDPREPROC_QCELP_EOS_FLG_MASK 0x01 +#define AUDPREPROC_QCELP_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_QCELP_EOS_SET 0x1 /* EOS set in meta field */ + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_qcelp_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + int mfield; /* meta field embedded in data */ + int wflush; /*write flush */ + int rflush; /*read flush*/ + int out_frame_cnt; + + struct msm_adsp_module *audrec; + struct msm_adsp_module *audpre; + + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* Frame size (36 bytes) */ + uint32_t enc_type; /* 11 for QCELP */ + uint32_t mode; /* T or NT Mode*/ + + struct msm_audio_qcelp_enc_config cfg; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + unsigned short samp_rate_index; + uint32_t audrec_obj_idx ; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + void *map_v_read; + void *map_v_write; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; +} __packed; + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __packed; + +struct qcelp_encoded_meta_out { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audio_send_queue_pre(audio, cmd, len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) + +#define audio_send_queue_recbs(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +static int audqcelp_in_dsp_enable(struct audio_qcelp_in *audio, int enable); +static int audqcelp_in_encparam_config(struct audio_qcelp_in *audio); +static int audqcelp_in_encmem_config(struct audio_qcelp_in *audio); +static int audqcelp_in_dsp_read_buffer(struct audio_qcelp_in *audio, + uint32_t read_cnt); +static void audqcelp_in_flush(struct audio_qcelp_in *audio); + +static void audqcelp_in_get_dsp_frames(struct audio_qcelp_in *audio); +static int audpcm_config(struct audio_qcelp_in *audio); +static void audqcelp_out_flush(struct audio_qcelp_in *audio); +static int audqcelp_in_routing_mode_config(struct audio_qcelp_in *audio); +static void audrec_pcm_send_data(struct audio_qcelp_in *audio, unsigned needed); +static void audqcelp_nt_in_get_dsp_frames(struct audio_qcelp_in *audio); +static void audqcelp_in_flush(struct audio_qcelp_in *audio); + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* must be called with audio->lock held */ +static int audqcelp_in_enable(struct audio_qcelp_in *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + cfg.codec = RPC_AUD_DEF_CODEC_13K; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + audmgr_disable(&audio->audmgr); + MM_ERR("msm_adsp_enable(audpre) failed\n"); + return -ENODEV; + } + } + if (msm_adsp_enable(audio->audrec)) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audmgr_disable(&audio->audmgr); + msm_adsp_disable(audio->audpre); + } + MM_ERR("msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audqcelp_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audqcelp_in_disable(struct audio_qcelp_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audqcelp_in_dsp_enable(audio, 0); + + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + audio->stopped = 1; + wake_up(&audio->wait); + msm_adsp_disable(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + MM_DBG("type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + MM_ERR("err_index %d\n", msg[0]); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audpreproctask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +static void audqcelp_in_get_dsp_frames(struct audio_qcelp_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - + sizeof(*frame)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("in_count = %d\n", audio->in_count); + } else + audio->in_count++; + + audqcelp_in_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audqcelp_nt_in_get_dsp_frames(struct audio_qcelp_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + +static int audrec_pcm_buffer_ptr_refresh(struct audio_qcelp_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == NT_FRAME_HEADER_SIZE) + len = len / 2; + else + len = (len + NT_FRAME_HEADER_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_config(struct audio_qcelp_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE; + cmd.sampling_freq = convert_samp_index(audio->samp_rate); + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + + +static int audqcelp_in_routing_mode_config(struct audio_qcelp_in *audio) +{ + struct audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ROUTING_MODE; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + cmd.routing_mode = 1; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_qcelp_in *audio = NULL; + + if (data) + audio = data; + else { + MM_ERR("invalid data for event %x\n", id); + return; + } + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: { + struct audrec_msg_cmd_cfg_done_msg cmd_cfg_done_msg; + getevent(&cmd_cfg_done_msg, AUDREC_MSG_CMD_CFG_DONE_MSG_LEN); + if (cmd_cfg_done_msg.audrec_enc_type & \ + AUDREC_MSG_CFG_DONE_ENC_ENA) { + audio->audrec_obj_idx = cmd_cfg_done_msg.audrec_obj_idx; + MM_DBG("CFG ENABLED\n"); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audqcelp_in_routing_mode_config(audio); + } else { + audqcelp_in_encmem_config(audio); + } + } else { + MM_DBG("CFG SLEEP\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG: { + struct audrec_msg_cmd_routing_mode_done_msg \ + routing_msg; + getevent(&routing_msg, AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG); + MM_DBG("AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG"); + if (routing_msg.configuration == 0) { + MM_ERR("routing configuration failed\n"); + audio->running = 0; + wake_up(&audio->wait_enable); + } else + audqcelp_in_encmem_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { + MM_DBG("AREC_MEM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audqcelp_in_encparam_config(audio); + else + audpcm_config(audio); + break; + } + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_DBG("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audqcelp_in_encparam_config(audio); + break; + } + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_DBG("AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG\n"); + audio->running = 1; + wake_up(&audio->wait_enable); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audrec_pcm_send_data(audio, 1); + break; + } + case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: { + struct audrec_msg_no_ext_pkt_avail_msg err_msg; + getevent(&err_msg, AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN); + MM_DBG("NO_EXT_PKT_AVAILABLE_MSG %x\n",\ + err_msg.audrec_err_id); + break; + } + case AUDREC_MSG_PACKET_READY_MSG: { + struct audrec_msg_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt msw %d \ + write cnt lsw %d read cnt msw %d read cnt lsw %d \n",\ + pkt_ready_msg.pkt_counter_msw, \ + pkt_ready_msg.pkt_counter_lsw, \ + pkt_ready_msg.pkt_read_cnt_msw, \ + pkt_ready_msg.pkt_read_cnt_lsw); + + audqcelp_in_get_dsp_frames(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audqcelp_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +static struct msm_adsp_ops audpre_qcelp_adsp_ops = { + .event = audpre_dsp_event, +}; + +static struct msm_adsp_ops audrec_qcelp_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audqcelp_in_dsp_enable(struct audio_qcelp_in *audio, int enable) +{ + struct audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_ENC_CFG; + cmd.audrec_enc_type = (audio->enc_type & 0xFF) | + (enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS); + /* Don't care */ + cmd.audrec_obj_idx = audio->audrec_obj_idx; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audqcelp_in_encmem_config(struct audio_qcelp_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + int header_len = 0; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG; + cmd.audrec_obj_idx = audio->audrec_obj_idx; + /* Rate at which packet complete message comes */ + cmd.audrec_up_pkt_intm_cnt = 1; + cmd.audrec_extpkt_buffer_msw = audio->phys >> 16; + cmd.audrec_extpkt_buffer_lsw = audio->phys; + /* Max Buffer no available for frames */ + cmd.audrec_extpkt_buffer_num = FRAME_NUM; + + /* prepare buffer pointers: + * T:36 bytes qcelp packet + 4 halfword header + * NT:36 bytes qcelp packet + 12 halfword header + */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + header_len = FRAME_HEADER_SIZE/2; + else + header_len = NT_FRAME_HEADER_SIZE/2; + + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + header_len; + data += (QCELP_FRAME_SIZE/2) + header_len; + MM_DBG("0x%8x\n", (int)(audio->in[n].data - header_len*2)); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audqcelp_in_encparam_config(struct audio_qcelp_in *audio) +{ + struct audrec_cmd_arecparam_qcelp_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG; + cmd.common.audrec_obj_idx = audio->audrec_obj_idx; + cmd.enc_min_rate = audio->cfg.min_bit_rate; + cmd.enc_max_rate = audio->cfg.max_bit_rate; + cmd.rate_modulation_cmd = 0; /* Default set to 0 */ + cmd.reduced_rate_level = 0; /* Default set to 0 */ + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audqcelp_flush_command(struct audio_qcelp_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audqcelp_in_dsp_read_buffer(struct audio_qcelp_in *audio, + uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + cmd.type = audio->audrec_obj_idx; + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audqcelp_ioport_reset(struct audio_qcelp_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audqcelp_in_flush(audio); + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audqcelp_out_flush(audio); + mutex_unlock(&audio->write_lock); +} + +static void audqcelp_in_flush(struct audio_qcelp_in *audio) +{ + int i; + unsigned long flags; + + audio->eos_ack = 0; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = FRAME_NUM-1; i >= 0; i--) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audqcelp_out_flush(struct audio_qcelp_in *audio) +{ + int i; + unsigned long flags; + + audio->out_head = 0; + audio->out_count = 0; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out_tail = 0; + for (i = OUT_FRAME_NUM-1; i >= 0; i--) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static long audqcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_qcelp_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + rc = audqcelp_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + rc = audqcelp_in_disable(audio); + break; + } + case AUDIO_FLUSH: { + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audqcelp_ioport_reset(audio); + if (audio->running) { + audqcelp_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + cfg.channel_count = 1; + cfg.type = 0; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) { + rc = -EINVAL; + break; + } + } else { + if (cfg.buffer_size != (QCELP_FRAME_SIZE + 14)) { + rc = -EINVAL; + break; + } + } + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate, + cfg.max_bit_rate, cfg.cdma_rate); + if (cfg.min_bit_rate > CDMA_RATE_FULL || \ + cfg.min_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid min bitrate\n"); + rc = -EFAULT; + break; + } + if (cfg.max_bit_rate > CDMA_RATE_FULL || \ + cfg.max_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid max bitrate\n"); + rc = -EFAULT; + break; + } + /* Recording Does not support Erase and Blank */ + if (cfg.cdma_rate > CDMA_RATE_FULL || + cfg.cdma_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid qcelp cdma rate\n"); + rc = -EFAULT; + break; + } + memcpy(&audio->cfg, &cfg, sizeof(cfg)); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audqcelp_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_qcelp_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + struct qcelp_encoded_meta_out meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG("count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->rflush); + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, + (sizeof(struct qcelp_encoded_meta_out) - \ + sizeof(uint16_t))); + meta_field.metadata_len = + sizeof(struct qcelp_encoded_meta_out); + if (copy_to_user((char *)start, (char *)&meta_field, + sizeof(struct qcelp_encoded_meta_out))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_ERR("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct qcelp_encoded_meta_out); + count -= sizeof(struct qcelp_encoded_meta_out); + } + if (count >= size) { + /* order the reads on the buffer */ + dma_coherent_post_ops(); + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) { + if (!audio->eos_ack) { + MM_DBG("sending read ptr command \ + %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audqcelp_in_dsp_read_buffer(audio, + audio->dsp_cnt++); + } + } + } else { + MM_ERR("short read\n"); + break; + } + break; + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static void audrec_pcm_send_data(struct audio_qcelp_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audrec_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static int audqcelp_in_fsync(struct file *file, loff_t a, loff_t b, int datasync) + +{ + struct audio_qcelp_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + +int audrec_qcelp_process_eos(struct audio_qcelp_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audrec_pcm_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audqcelp_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_qcelp_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int rc = 0, eos_condition = AUDPREPROC_QCELP_EOS_NONE; + unsigned short mfield_size = 0; + int write_count = 0; + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + /* if supplied count is more than driver buffer size + * then only copy driver buffer size + */ + if (count > frame->size) + count = frame->size; + + write_count = count; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] & + AUDPREPROC_QCELP_EOS_FLG_MASK) { + eos_condition = AUDPREPROC_QCELP_EOS_SET; + MM_DBG("EOS SET\n"); + if (mfield_size == count) { + buf += mfield_size; + eos_condition = 0; + goto exit; + } else + cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] &= + ~AUDPREPROC_QCELP_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audrec_pcm_send_data(audio, 0); + else { + audrec_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_QCELP_EOS_SET) + rc = audrec_qcelp_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audqcelp_in_release(struct inode *inode, struct file *file) +{ + struct audio_qcelp_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audqcelp_in_disable(audio); + audqcelp_in_flush(audio); + msm_adsp_put(audio->audrec); + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \ + (audio->out_data)) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +static struct audio_qcelp_in the_audio_qcelp_in; + +static int audqcelp_in_open(struct inode *inode, struct file *file) +{ + struct audio_qcelp_in *audio = &the_audio_qcelp_in; + int rc; + int encid; + int dma_size = 0; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + dma_size = NT_DMASZ; + MM_DBG("Opened for non tunnel mode encoding\n"); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + dma_size = DMASZ; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + MM_ERR("Invalid mode\n"); + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_8000, + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_8000; + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (QCELP_FRAME_SIZE + 14); + else + audio->buffer_size = QCELP_FRAME_SIZE; + audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_QCELP | audio->mode; + + audio->cfg.cdma_rate = CDMA_RATE_FULL; + audio->cfg.min_bit_rate = CDMA_RATE_FULL; + audio->cfg.max_bit_rate = CDMA_RATE_FULL; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + } + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_qcelp_adsp_ops, audio); + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_qcelp_adsp_ops, audio); + if (rc) { + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + goto done; + } + } + + audio->dsp_cnt = 0; + audio->stopped = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audqcelp_in_flush(audio); + audqcelp_out_flush(audio); + + audio->phys = allocate_contiguous_ebi_nomap(dma_size, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate physical read buffers\n"); + rc = -ENOMEM; + goto evt_error; + } else { + audio->map_v_read = ioremap(audio->phys, dma_size); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map physical address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto evt_error; + } + audio->data = audio->map_v_read; + MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + audio->out_data = NULL; + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, + SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate physical write buffers\n"); + rc = -ENOMEM; + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + goto evt_error; + } else { + audio->map_v_write = ioremap( + audio->out_phys, BUFFER_SIZE); + + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address\n"); + rc = -ENOMEM; + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + free_contiguous_memory_by_paddr(\ + audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, (int)audio->out_data); + } + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (unsigned int)audio->out[0].data, + (unsigned int)audio->out[1].data); + audio->mfield = NT_FRAME_HEADER_SIZE; + audio->out_frame_cnt++; + } + file->private_data = audio; + audio->opened = 1; + +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + msm_adsp_put(audio->audpre); + + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_qcelp_in_fops = { + .owner = THIS_MODULE, + .open = audqcelp_in_open, + .release = audqcelp_in_release, + .read = audqcelp_in_read, + .write = audqcelp_in_write, + .fsync = audqcelp_in_fsync, + .unlocked_ioctl = audqcelp_in_ioctl, +}; + +static struct miscdevice audqcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_qcelp_in_fops, +}; + +static int __init audqcelp_in_init(void) +{ + mutex_init(&the_audio_qcelp_in.lock); + mutex_init(&the_audio_qcelp_in.read_lock); + spin_lock_init(&the_audio_qcelp_in.dsp_lock); + init_waitqueue_head(&the_audio_qcelp_in.wait); + init_waitqueue_head(&the_audio_qcelp_in.wait_enable); + mutex_init(&the_audio_qcelp_in.write_lock); + init_waitqueue_head(&the_audio_qcelp_in.write_wait); + return misc_register(&audqcelp_in_misc); +} +device_initcall(audqcelp_in_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_voice_lb.c b/arch/arm/mach-msm/qdsp5/audio_voice_lb.c new file mode 100644 index 00000000000..08fa4872db2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_voice_lb.c @@ -0,0 +1,369 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "audmgr_new.h" + +#define VOICELOOPBACK_PROG 0x300000B8 +#define VOICELOOP_VERS 0x00010001 + +#define VOICELOOPBACK_START_PROC 2 +#define VOICELOOPBACK_STOP_PROC 3 + +#define RPC_TYPE_REQUEST 0 +#define RPC_TYPE_REPLY 1 + +#define RPC_STATUS_FAILURE 0 +#define RPC_STATUS_SUCCESS 1 +#define RPC_STATUS_REJECT 1 + +#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) +#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) +#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) + +#define MAX_LEN 32 + +struct audio { + struct msm_rpc_endpoint *rpc_endpt; + uint32_t rpc_prog; + uint32_t rpc_ver; + uint32_t rpc_status; + struct audmgr audmgr; + + struct dentry *dentry; + + struct mutex lock; + + struct task_struct *task; + + wait_queue_head_t wait; + int enabled; + int thread_exit; +}; + +static struct audio the_audio; + +static int audio_voice_loopback_thread(void *data) +{ + struct audio *audio = data; + struct rpc_request_hdr *rpc_hdr = NULL; + int rpc_hdr_len; + + MM_DBG("\n"); + + while (!kthread_should_stop()) { + if (rpc_hdr != NULL) { + kfree(rpc_hdr); + rpc_hdr = NULL; + } + + if (audio->thread_exit) + break; + + rpc_hdr_len = msm_rpc_read(audio->rpc_endpt, + (void **) &rpc_hdr, + -1, + -1); + if (rpc_hdr_len < 0) { + MM_ERR("RPC read failed %d\n", rpc_hdr_len); + break; + } else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) { + continue; + } else { + uint32_t rpc_type = be32_to_cpu(rpc_hdr->type); + if (rpc_type == RPC_TYPE_REPLY) { + struct rpc_reply_hdr *rpc_reply = + (void *) rpc_hdr; + uint32_t reply_status; + + reply_status = + be32_to_cpu(rpc_reply->reply_stat); + + if (reply_status == RPC_ACCEPTSTAT_SUCCESS) + audio->rpc_status = \ + RPC_STATUS_SUCCESS; + else { + audio->rpc_status = \ + RPC_STATUS_REJECT; + MM_ERR("RPC reply status denied\n"); + } + wake_up(&audio->wait); + } else { + MM_ERR("Unexpected RPC type %d\n", rpc_type); + } + } + } + kfree(rpc_hdr); + rpc_hdr = NULL; + + MM_DBG("Audio Voice Looopback thread stopped\n"); + + return 0; +} + +static int audio_voice_loopback_start(struct audio *audio) +{ + int rc = 0; + struct audmgr_config cfg; + struct rpc_request_hdr rpc_hdr; + + MM_DBG("\n"); + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000; + cfg.def_method = RPC_AUD_DEF_METHOD_VOICE; + cfg.codec = RPC_AUD_DEF_CODEC_VOC_CDMA; + cfg.snd_method = RPC_SND_METHOD_VOICE; + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) { + MM_ERR("audmgr open failed, freeing instance\n"); + rc = -EINVAL; + goto done; + } + + memset(&rpc_hdr, 0, sizeof(rpc_hdr)); + + msm_rpc_setup_req(&rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + VOICELOOPBACK_START_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &rpc_hdr, + sizeof(rpc_hdr)); + if (rc >= 0) { + rc = wait_event_timeout(audio->wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 1 * HZ); + if (rc > 0) { + if (audio->rpc_status != RPC_STATUS_SUCCESS) { + MM_ERR("Start loopback failed %d\n", rc); + rc = -EBUSY; + } else { + rc = 0; + } + } else { + MM_ERR("Wait event for acquire failed %d\n", rc); + rc = -EBUSY; + } + } else { + audmgr_disable(&audio->audmgr); + MM_ERR("RPC write for start loopback failed %d\n", rc); + rc = -EBUSY; + } +done: + return rc; +} + +static int audio_voice_loopback_stop(struct audio *audio) +{ + int rc = 0; + struct rpc_request_hdr rpc_hdr; + + MM_DBG("\n"); + + memset(&rpc_hdr, 0, sizeof(rpc_hdr)); + + msm_rpc_setup_req(&rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + VOICELOOPBACK_STOP_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + audio->thread_exit = 1; + rc = msm_rpc_write(audio->rpc_endpt, + &rpc_hdr, + sizeof(rpc_hdr)); + if (rc >= 0) { + + rc = wait_event_timeout(audio->wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 1 * HZ); + if (rc > 0) { + MM_DBG("Wait event for release succeeded\n"); + rc = 0; + } else { + MM_ERR("Wait event for release failed %d\n", rc); + } + } else { + MM_ERR("RPC write for release failed %d\n", rc); + } + + audmgr_disable(&audio->audmgr); + + return rc; +} + +static int audio_voice_loopback_open(struct audio *audio_info) +{ + int rc = 0; + + MM_DBG("\n"); + + rc = audmgr_open(&audio_info->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance\n"); + rc = -EINVAL; + goto done; + } + + audio_info->rpc_endpt = msm_rpc_connect_compatible(VOICELOOPBACK_PROG, + VOICELOOP_VERS, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(audio_info->rpc_endpt)) { + MM_ERR("VOICE LOOPBACK RPC connect\ + failed ver 0x%x\n", + VOICELOOP_VERS); + rc = PTR_ERR(audio_info->rpc_endpt); + audio_info->rpc_endpt = NULL; + rc = -EINVAL; + } else { + MM_DBG("VOICE LOOPBACK connect succeeded ver 0x%x\n", + VOICELOOP_VERS); + audio_info->thread_exit = 0; + audio_info->task = kthread_run(audio_voice_loopback_thread, + audio_info, + "audio_voice_loopback"); + if (IS_ERR(audio_info->task)) { + MM_ERR("voice loopback thread create failed\n"); + rc = PTR_ERR(audio_info->task); + audio_info->task = NULL; + msm_rpc_close(audio_info->rpc_endpt); + audio_info->rpc_endpt = NULL; + rc = -EINVAL; + } + audio_info->rpc_prog = VOICELOOPBACK_PROG; + audio_info->rpc_ver = VOICELOOP_VERS; + } +done: + return rc; +} + +static int audio_voice_loopback_close(struct audio *audio_info) +{ + MM_DBG("\n"); + msm_rpc_close(audio_info->rpc_endpt); + audio_info->rpc_endpt = NULL; + audmgr_close(&audio_info->audmgr); + audio_info->task = NULL; + return 0; +} + +static ssize_t audio_voice_loopback_debug_write(struct file *file, + const char __user *buf, + size_t cnt, loff_t *ppos) +{ + char lbuf[MAX_LEN]; + int rc = 0; + + if (cnt > (MAX_LEN - 1)) + return -EINVAL; + + memset(&lbuf[0], 0, sizeof(lbuf)); + + rc = copy_from_user(lbuf, buf, cnt); + if (rc) { + MM_ERR("Unable to copy data from user space\n"); + return -EFAULT; + } + + lbuf[cnt] = '\0'; + + if (!strncmp(&lbuf[0], "1", cnt-1)) { + mutex_lock(&the_audio.lock); + if (!the_audio.enabled) { + rc = audio_voice_loopback_open(&the_audio); + if (!rc) { + rc = audio_voice_loopback_start(&the_audio); + if (rc < 0) { + the_audio.enabled = 0; + audio_voice_loopback_close(&the_audio); + } else { + the_audio.enabled = 1; + } + } + } + mutex_unlock(&the_audio.lock); + } else if (!strncmp(lbuf, "0", cnt-1)) { + mutex_lock(&the_audio.lock); + if (the_audio.enabled) { + audio_voice_loopback_stop(&the_audio); + audio_voice_loopback_close(&the_audio); + the_audio.enabled = 0; + } + mutex_unlock(&the_audio.lock); + } else { + rc = -EINVAL; + } + + if (rc == 0) { + rc = cnt; + } else { + MM_INFO("rc = %d\n", rc); + MM_INFO("\nWrong command: Use =>\n"); + MM_INFO("-------------------------\n"); + MM_INFO("To Start Loopback:: echo \"1\">/sys/kernel/debug/\ + voice_loopback\n"); + MM_INFO("To Stop Loopback:: echo \"0\">/sys/kernel/debug/\ + voice_loopback\n"); + MM_INFO("------------------------\n"); + } + + return rc; +} + +static ssize_t audio_voice_loopback_debug_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + MM_DBG("Audio Voiceloop debugfs opened\n"); + return 0; +} + +static const struct file_operations voice_loopback_debug_fops = { + .write = audio_voice_loopback_debug_write, + .open = audio_voice_loopback_debug_open, +}; + +static int __init audio_init(void) +{ + int rc = 0; + memset(&the_audio, 0, sizeof(the_audio)); + + mutex_init(&the_audio.lock); + + init_waitqueue_head(&the_audio.wait); + + the_audio.dentry = debugfs_create_file("voice_loopback", + S_IFREG | S_IRUGO, + NULL, + NULL, &voice_loopback_debug_fops); + if (IS_ERR(the_audio.dentry)) + MM_ERR("debugfs_create_file failed\n"); + + return rc; +} +late_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_voicememo.c b/arch/arm/mach-msm/qdsp5/audio_voicememo.c new file mode 100644 index 00000000000..03dd29534da --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_voicememo.c @@ -0,0 +1,981 @@ +/* arch/arm/mach-msm/qdsp5/audio_voicememo.c + * + * Voice Memo device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This code is based in part on arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "audmgr.h" + +#define SND_PROG_VERS "rs30000002:0x00020001" +#define SND_PROG 0x30000002 +#define SND_VERS_COMP 0x00020001 +#define SND_VERS2_COMP 0x00030001 + +#define SND_VOC_REC_START_PROC 19 +#define SND_VOC_REC_STOP_PROC 20 +#define SND_VOC_REC_PAUSE_PROC 21 +#define SND_VOC_REC_RESUME_PROC 22 +#define SND_VOC_REC_PUT_BUF_PROC 23 + +#define SND_VOC_REC_AV_SYNC_CB_PTR_PROC 9 +#define SND_VOC_REC_CB_FUNC_TYPE_PROC 10 + +#define REC_CLIENT_DATA 0x11223344 +#define DATA_CB_FUNC_ID 0x12345678 +#define AV_SYNC_CB_FUNC_ID 0x87654321 +#define CLIENT_DATA 0xaabbccdd + +#define RPC_TYPE_REQUEST 0 +#define RPC_TYPE_REPLY 1 + +#define RPC_STATUS_FAILURE 0 +#define RPC_STATUS_SUCCESS 1 + +#define RPC_VERSION 2 + +#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) +#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) +#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) +#define RPC_REPLY_SZ (sizeof(uint32_t) * 6) + +#define MAX_FRAME_SIZE 36 /* QCELP - 36, AMRNB - 32, EVRC - 24 */ +#define MAX_REC_BUF_COUNT 5 /* Maximum supported voc rec buffers */ +#define MAX_REC_BUF_SIZE (MAX_FRAME_SIZE * 10) +#define MAX_VOICEMEMO_BUF_SIZE \ + ((MAX_REC_BUF_SIZE)*MAX_REC_BUF_COUNT) /* 5 buffers for 200ms frame */ +#define MSM_AUD_BUFFER_UPDATE_WAIT_MS 2000 + +enum rpc_voc_rec_status_type { + RPC_VOC_REC_STAT_SUCCESS = 1, + RPC_VOC_REC_STAT_DONE = 2, + RPC_VOC_REC_STAT_AUTO_STOP = 4, + RPC_VOC_REC_STAT_PAUSED = 8, + RPC_VOC_REC_STAT_RESUMED = 16, + RPC_VOC_REC_STAT_ERROR = 32, + RPC_VOC_REC_STAT_BUFFER_ERROR = 64, + RPC_VOC_REC_STAT_INVALID_PARAM = 128, + RPC_VOC_REC_STAT_INT_TIME = 256, + RPC_VOC_REC_STAT_DATA = 512, + RPC_VOC_REC_STAT_NOT_READY = 1024, + RPC_VOC_REC_STAT_INFORM_EVRC = 2048, + RPC_VOC_REC_STAT_INFORM_13K = 4096, + RPC_VOC_REC_STAT_INFORM_AMR = 8192, + RPC_VOC_REC_STAT_INFORM_MAX = 65535 +}; + +struct rpc_snd_voc_rec_start_args { + uint32_t param_status; /* 1 = valid, 0 = not valid */ + uint32_t rec_type; + uint32_t rec_interval_ms; + uint32_t auto_stop_ms; + uint32_t capability; + uint32_t max_rate; + uint32_t min_rate; + uint32_t frame_format; + uint32_t dtx_enable; + uint32_t data_req_ms; + uint32_t rec_client_data; + + uint32_t cb_func_id; + uint32_t sync_cb_func_id; + uint32_t client_data; +}; + +struct rpc_snd_voc_rec_put_buf_args { + uint32_t buf; + uint32_t num_bytes; +}; + +struct snd_voc_rec_start_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_voc_rec_start_args args; +}; + +struct snd_voc_rec_put_buf_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_voc_rec_put_buf_args args; +}; + +struct snd_voc_rec_av_sync_cb_func_data { + uint32_t sync_cb_func_id; + uint32_t status; /* Pointer status (1 = valid, 0 = invalid) */ + uint32_t num_samples; + uint32_t time_stamp[2]; + uint32_t lost_samples; + uint32_t frame_index; + uint32_t client_data; +}; + +struct snd_voc_rec_cb_func_fw_data { + uint32_t fw_ptr_status; /* FW Pointer status (1=valid,0=invalid) */ + uint32_t rec_buffer_size; + uint32_t data[MAX_REC_BUF_SIZE/4]; + uint32_t rec_buffer_size_copy; + uint32_t rec_num_frames; /* Number of voice frames */ + uint32_t rec_length; /* Valid data in record buffer = + * data_req_ms amount of data */ + uint32_t client_data; /* A11 rec buffer pointer */ + uint32_t rw_ptr_status; /* RW Pointer status (1=valid,0=invalid) */ +}; + +struct snd_voc_rec_cb_func_rw_data { + uint32_t fw_ptr_status; /* FW Pointer status (1=valid,0=invalid) */ + uint32_t rw_ptr_status; /* RW Pointer status (1=valid,0=invalid) */ + uint32_t rec_buffer_size; + uint32_t data[MAX_REC_BUF_SIZE/4]; + uint32_t rec_buffer_size_copy; + uint32_t rec_num_frames; /* Number of voice frames */ + uint32_t rec_length; /* Valid data in record buffer = + * data_req_ms amount of data */ + uint32_t client_data; /* A11 rec buffer pointer */ +}; + +struct snd_voc_rec_data_cb_func_data { + uint32_t cb_func_id; + uint32_t status; /* Pointer status (1 = valid, 0 = invalid) */ + uint32_t rec_status; + + union { + struct snd_voc_rec_cb_func_fw_data fw_data; + struct snd_voc_rec_cb_func_rw_data rw_data; + } pkt; +}; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Usage actual recorded data */ + unsigned addr; + unsigned numframes; +}; + +struct audio_voicememo { + uint32_t byte_count; /* Pass statistics to user space for + * time stamping */ + uint32_t frame_count; + + int opened; + int enabled; + int running; + int stopped; + int pause_resume; + + uint32_t rpc_prog; + uint32_t rpc_ver; + uint32_t rpc_xid; + uint32_t rpc_status; + + struct mutex lock; + struct mutex read_lock; + struct mutex dsp_lock; + wait_queue_head_t read_wait; + wait_queue_head_t wait; + + struct buffer in[MAX_REC_BUF_COUNT]; + char *rec_buf_ptr; + dma_addr_t phys; + uint32_t rec_buf_size; + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that should be filled as + * data comes from A9 */ + + struct audmgr audmgr; + + struct msm_audio_voicememo_config voicememo_cfg; + + struct msm_rpc_endpoint *sndept; + struct task_struct *task; +}; + +static struct audio_voicememo the_audio_voicememo; + +static int audvoicememo_validate_usr_config( + struct msm_audio_voicememo_config *config) +{ + int rc = -1; /* error */ + + if (config->rec_type != RPC_VOC_REC_FORWARD && + config->rec_type != RPC_VOC_REC_REVERSE && + config->rec_type != RPC_VOC_REC_BOTH) + goto done; + + /* QCELP, EVRC, AMR-NB only */ + if (config->capability != RPC_VOC_CAP_IS733 && + config->capability != RPC_VOC_CAP_IS127 && + config->capability != RPC_VOC_CAP_AMR) + goto done; + + /* QCP, AMR format supported */ + if ((config->frame_format != RPC_VOC_PB_NATIVE_QCP) && + (config->frame_format != RPC_VOC_PB_AMR)) + goto done; + + if ((config->frame_format == RPC_VOC_PB_AMR) && + (config->capability != RPC_VOC_CAP_AMR)) + goto done; + + /* To make sure, max kernel buf size matches + * with max data request time */ + if (config->data_req_ms > ((MAX_REC_BUF_SIZE/MAX_FRAME_SIZE)*20)) + goto done; + + rc = 0; +done: + return rc; +} + +static void audvoicememo_flush_buf(struct audio_voicememo *audio) +{ + uint8_t index; + + for (index = 0; index < MAX_REC_BUF_COUNT; index++) + audio->in[index].used = 0; + + audio->read_next = 0; + mutex_lock(&audio->dsp_lock); + audio->fill_next = 0; + mutex_unlock(&audio->dsp_lock); +} + +static void audvoicememo_ioport_reset(struct audio_voicememo *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audvoicememo_flush_buf(audio); + mutex_unlock(&audio->read_lock); +} + +/* must be called with audio->lock held */ +static int audvoicememo_enable(struct audio_voicememo *audio) +{ + struct audmgr_config cfg; + struct snd_voc_rec_put_buf_msg bmsg; + struct snd_voc_rec_start_msg msg; + uint8_t index; + uint32_t offset = 0; + int rc; + + if (audio->enabled) + return 0; + + /* Codec / method configure to audmgr client */ + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + + if (audio->voicememo_cfg.capability == RPC_VOC_CAP_IS733) + cfg.codec = RPC_AUD_DEF_CODEC_VOC_13K; + else if (audio->voicememo_cfg.capability == RPC_VOC_CAP_IS127) + cfg.codec = RPC_AUD_DEF_CODEC_VOC_EVRC; + else + cfg.codec = RPC_AUD_DEF_CODEC_VOC_AMR; /* RPC_VOC_CAP_AMR */ + + cfg.snd_method = RPC_SND_METHOD_VOICE; + rc = audmgr_enable(&audio->audmgr, &cfg); + + if (rc < 0) + return rc; + + /* Configure VOC Rec buffer */ + for (index = 0; index < MAX_REC_BUF_COUNT; index++) { + audio->in[index].data = audio->rec_buf_ptr + offset; + audio->in[index].addr = audio->phys + offset; + audio->in[index].size = audio->rec_buf_size; + audio->in[index].used = 0; + audio->in[index].numframes = 0; + offset += audio->rec_buf_size; + bmsg.args.buf = (uint32_t) audio->in[index].data; + bmsg.args.num_bytes = cpu_to_be32(audio->in[index].size); + MM_DBG("rec_buf_ptr=0x%8x, rec_buf_size = 0x%8x\n", + bmsg.args.buf, bmsg.args.num_bytes); + + msm_rpc_setup_req(&bmsg.hdr, audio->rpc_prog, audio->rpc_ver, + SND_VOC_REC_PUT_BUF_PROC); + audio->rpc_xid = bmsg.hdr.xid; + audio->rpc_status = RPC_STATUS_FAILURE; + msm_rpc_write(audio->sndept, &bmsg, sizeof(bmsg)); + rc = wait_event_timeout(audio->wait, + audio->rpc_status != RPC_STATUS_FAILURE, 1 * HZ); + if (rc == 0) + goto err; + } + + + /* Start Recording */ + msg.args.param_status = cpu_to_be32(0x00000001); + msg.args.rec_type = cpu_to_be32(audio->voicememo_cfg.rec_type); + msg.args.rec_interval_ms = + cpu_to_be32(audio->voicememo_cfg.rec_interval_ms); + msg.args.auto_stop_ms = cpu_to_be32(audio->voicememo_cfg.auto_stop_ms); + msg.args.capability = cpu_to_be32(audio->voicememo_cfg.capability); + msg.args.max_rate = cpu_to_be32(audio->voicememo_cfg.max_rate); + msg.args.min_rate = cpu_to_be32(audio->voicememo_cfg.min_rate); + msg.args.frame_format = cpu_to_be32(audio->voicememo_cfg.frame_format); + msg.args.dtx_enable = cpu_to_be32(audio->voicememo_cfg.dtx_enable); + msg.args.data_req_ms = cpu_to_be32(audio->voicememo_cfg.data_req_ms); + msg.args.rec_client_data = cpu_to_be32(REC_CLIENT_DATA); + msg.args.cb_func_id = cpu_to_be32(DATA_CB_FUNC_ID); + msg.args.sync_cb_func_id = cpu_to_be32(AV_SYNC_CB_FUNC_ID); + msg.args.client_data = cpu_to_be32(CLIENT_DATA); + + msm_rpc_setup_req(&msg.hdr, audio->rpc_prog, audio->rpc_ver, + SND_VOC_REC_START_PROC); + + audio->rpc_xid = msg.hdr.xid; + audio->rpc_status = RPC_STATUS_FAILURE; + msm_rpc_write(audio->sndept, &msg, sizeof(msg)); + rc = wait_event_timeout(audio->wait, + audio->rpc_status != RPC_STATUS_FAILURE, 1 * HZ); + if (rc == 0) + goto err; + + audio->rpc_xid = 0; + audio->enabled = 1; + return 0; + +err: + audio->rpc_xid = 0; + audmgr_disable(&audio->audmgr); + MM_ERR("Fail\n"); + return -1; +} + +/* must be called with audio->lock held */ +static int audvoicememo_disable(struct audio_voicememo *audio) +{ + struct rpc_request_hdr rhdr; + int rc = 0; + if (audio->enabled) { + msm_rpc_setup_req(&rhdr, audio->rpc_prog, audio->rpc_ver, + SND_VOC_REC_STOP_PROC); + rc = msm_rpc_write(audio->sndept, &rhdr, sizeof(rhdr)); + rc = wait_event_timeout(audio->wait, audio->stopped == 1, + 1 * HZ); + if (rc == 0) + audio->stopped = 1; + wake_up(&audio->read_wait); + audmgr_disable(&audio->audmgr); + audio->enabled = 0; + } + return 0; +} + +/* RPC Reply Generator */ +static void rpc_reply(struct msm_rpc_endpoint *ept, uint32_t xid) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + MM_DBG("inside\n"); + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(RPC_TYPE_REPLY); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(ept, reply_buf, sizeof(reply_buf)); + if (rc < 0) + MM_ERR("could not write RPC response: %d\n", rc); +} + +static void process_rpc_request(uint32_t proc, uint32_t xid, + void *data, int len, void *private) +{ + struct audio_voicememo *audio = private; + + MM_DBG("inside\n"); + /* Sending Ack before processing the request + * to make sure A9 get response immediate + * However, if there is validation of request planned + * may be move this reply Ack at the end */ + rpc_reply(audio->sndept, xid); + switch (proc) { + case SND_VOC_REC_AV_SYNC_CB_PTR_PROC: { + MM_DBG("AV Sync CB:func_id=0x%8x,status=0x%x\n", + be32_to_cpu(( \ + (struct snd_voc_rec_av_sync_cb_func_data *)\ + data)->sync_cb_func_id),\ + be32_to_cpu(( \ + (struct snd_voc_rec_av_sync_cb_func_data *)\ + data)->status)); + break; + } + case SND_VOC_REC_CB_FUNC_TYPE_PROC: { + struct snd_voc_rec_data_cb_func_data *datacb_data + = (void *)(data); + struct snd_voc_rec_put_buf_msg bmsg; + uint32_t rec_status = be32_to_cpu(datacb_data->rec_status); + + MM_DBG("Data CB:func_id=0x%8x,status=0x%x,\ + rec_status=0x%x\n", + be32_to_cpu(datacb_data->cb_func_id),\ + be32_to_cpu(datacb_data->status),\ + be32_to_cpu(datacb_data->rec_status)); + + /* Data recorded */ + if ((rec_status == RPC_VOC_REC_STAT_DATA) || + (rec_status == RPC_VOC_REC_STAT_DONE)) { + if (datacb_data->pkt.fw_data.fw_ptr_status && + be32_to_cpu(datacb_data->pkt.fw_data.rec_length) && + be32_to_cpu(datacb_data->pkt.fw_data.rec_length) + <= MAX_FRAME_SIZE) { + + MM_DBG("Copy FW link:rec_buf_size \ + = 0x%08x, rec_length=0x%08x\n", + be32_to_cpu( \ + datacb_data->pkt.fw_data. \ + rec_buffer_size_copy),\ + be32_to_cpu(datacb_data->pkt.fw_data. \ + rec_length)); + + mutex_lock(&audio->dsp_lock); + memcpy(audio->in[audio->fill_next].data, \ + &(datacb_data->pkt.fw_data.data[0]), \ + be32_to_cpu( + datacb_data->pkt.fw_data.rec_length)); + audio->in[audio->fill_next].used = + be32_to_cpu( + datacb_data->pkt.fw_data.rec_length); + audio->in[audio->fill_next].numframes = + be32_to_cpu( + datacb_data->pkt.fw_data.rec_num_frames); + mutex_unlock(&audio->dsp_lock); + } else if (datacb_data->pkt.rw_data.rw_ptr_status && + be32_to_cpu(datacb_data->pkt.rw_data.rec_length) && + be32_to_cpu(datacb_data->pkt.rw_data.rec_length) + <= MAX_FRAME_SIZE) { + + MM_DBG("Copy RW link:rec_buf_size \ + =0x%08x, rec_length=0x%08x\n", + be32_to_cpu( \ + datacb_data->pkt.rw_data. \ + rec_buffer_size_copy),\ + be32_to_cpu(datacb_data->pkt.rw_data. \ + rec_length)); + + mutex_lock(&audio->dsp_lock); + memcpy(audio->in[audio->fill_next].data, \ + &(datacb_data->pkt.rw_data.data[0]), \ + be32_to_cpu( + datacb_data->pkt.rw_data.rec_length)); + audio->in[audio->fill_next].used = + be32_to_cpu( + datacb_data->pkt.rw_data.rec_length); + audio->in[audio->fill_next].numframes = + be32_to_cpu( + datacb_data->pkt.rw_data.rec_num_frames); + mutex_unlock(&audio->dsp_lock); + } else { + MM_ERR("FW: ptr_status %d, rec_length=0x%08x," + "RW: ptr_status %d, rec_length=0x%08x\n", + datacb_data->pkt.rw_data.fw_ptr_status, \ + be32_to_cpu( \ + datacb_data->pkt.fw_data.rec_length), \ + datacb_data->pkt.rw_data.fw_ptr_status, \ + be32_to_cpu( \ + datacb_data->pkt.fw_data.rec_length)); + } + if (rec_status != RPC_VOC_REC_STAT_DONE) { + /* Not end of record */ + bmsg.args.buf = \ + (uint32_t) audio->in[audio->fill_next].data; + bmsg.args.num_bytes = \ + be32_to_cpu(audio->in[audio->fill_next].size); + + if (++audio->fill_next == MAX_REC_BUF_COUNT) + audio->fill_next = 0; + + msm_rpc_setup_req(&bmsg.hdr, audio->rpc_prog, + audio->rpc_ver, SND_VOC_REC_PUT_BUF_PROC); + + msm_rpc_write(audio->sndept, &bmsg, + sizeof(bmsg)); + + wake_up(&audio->read_wait); + } else { + /* Indication record stopped gracefully */ + MM_DBG("End Of Voice Record\n"); + audio->stopped = 1; + wake_up(&audio->wait); + } + } else if (rec_status == RPC_VOC_REC_STAT_PAUSED) { + MM_DBG(" Voice Record PAUSED\n"); + audio->pause_resume = 1; + } else if (rec_status == RPC_VOC_REC_STAT_RESUMED) { + MM_DBG(" Voice Record RESUMED\n"); + audio->pause_resume = 0; + } else if ((rec_status == RPC_VOC_REC_STAT_ERROR) || + (rec_status == RPC_VOC_REC_STAT_INVALID_PARAM) || + (rec_status == RPC_VOC_REC_STAT_BUFFER_ERROR)) + MM_ERR("error recording =0x%8x\n", + rec_status); + else if (rec_status == RPC_VOC_REC_STAT_INT_TIME) + MM_DBG("Frames recorded matches interval \ + callback time\n"); + else if (rec_status == RPC_VOC_REC_STAT_AUTO_STOP) { + MM_DBG(" Voice Record AUTO STOP\n"); + mutex_lock(&audio->lock); + audio->stopped = 1; + wake_up(&audio->read_wait); + audmgr_disable(&audio->audmgr); + audvoicememo_ioport_reset(audio); + audio->stopped = 0; + audio->enabled = 0; + mutex_unlock(&audio->lock); + } + break; + } + default: + MM_ERR("UNKNOWN PROC , proc = 0x%8x \n", proc); + } +} + +static int voicememo_rpc_thread(void *data) +{ + struct audio_voicememo *audio = data; + struct rpc_request_hdr *hdr = NULL; + uint32_t type; + int len; + + MM_DBG("start\n"); + + while (!kthread_should_stop()) { + kfree(hdr); + hdr = NULL; + + len = msm_rpc_read(audio->sndept, (void **) &hdr, -1, -1); + MM_DBG("rpc_read len = 0x%x\n", len); + if (len < 0) { + MM_ERR("rpc read failed (%d)\n", len); + break; + } + if (len < RPC_COMMON_HDR_SZ) + continue; + type = be32_to_cpu(hdr->type); + if (type == RPC_TYPE_REPLY) { + struct rpc_reply_hdr *rep = (void *) hdr; + uint32_t status; + if (len < RPC_REPLY_HDR_SZ) + continue; + status = be32_to_cpu(rep->reply_stat); + if (status == RPCMSG_REPLYSTAT_ACCEPTED) { + status = + be32_to_cpu(rep->data.acc_hdr.accept_stat); + + /* Confirm major RPC success during open*/ + if ((audio->enabled == 0) && + (status == RPC_ACCEPTSTAT_SUCCESS) && + (audio->rpc_xid == rep->xid)) { + audio->rpc_status = \ + RPC_STATUS_SUCCESS; + wake_up(&audio->wait); + } + MM_DBG("rpc_reply status 0x%8x\n", status); + } else { + MM_ERR("rpc_reply denied!\n"); + } + /* process reply */ + continue; + } else if (type == RPC_TYPE_REQUEST) { + if (len < RPC_REQUEST_HDR_SZ) + continue; + process_rpc_request(be32_to_cpu(hdr->procedure), + be32_to_cpu(hdr->xid), + (void *) (hdr + 1), + len - sizeof(*hdr), + audio); + } else + MM_ERR("Unexpected type (%d)\n", type); + } + MM_DBG("stop\n"); + kfree(hdr); + hdr = NULL; + + return 0; +} + +/* ------------------- device --------------------- */ +static long audio_voicememo_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_voicememo *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + mutex_lock(&audio->dsp_lock); + stats.byte_count = audio->byte_count; + stats.sample_count = audio->frame_count; + mutex_unlock(&audio->dsp_lock); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + MM_DBG("AUDIO_START\n"); + audio->byte_count = 0; + audio->frame_count = 0; + if (audio->voicememo_cfg.rec_type != RPC_VOC_REC_NONE) + rc = audvoicememo_enable(audio); + else + rc = -EINVAL; + MM_DBG("AUDIO_START rc %d\n", rc); + break; + } + case AUDIO_STOP: { + MM_DBG("AUDIO_STOP\n"); + rc = audvoicememo_disable(audio); + audvoicememo_ioport_reset(audio); + audio->stopped = 0; + MM_DBG("AUDIO_STOP rc %d\n", rc); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + MM_DBG("AUDIO_GET_CONFIG\n"); + cfg.buffer_size = audio->rec_buf_size; + cfg.buffer_count = MAX_REC_BUF_COUNT; + cfg.sample_rate = 8000; /* Voice Encoder works on 8k, + * Mono */ + cfg.channel_count = 1; + cfg.type = 0; + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + else + rc = 0; + MM_DBG("AUDIO_GET_CONFIG rc %d\n", rc); + break; + } + case AUDIO_GET_VOICEMEMO_CONFIG: { + MM_DBG("AUDIO_GET_VOICEMEMO_CONFIG\n"); + if (copy_to_user((void *)arg, &audio->voicememo_cfg, + sizeof(audio->voicememo_cfg))) + rc = -EFAULT; + else + rc = 0; + MM_DBG("AUDIO_GET_VOICEMEMO_CONFIG rc %d\n", rc); + break; + } + case AUDIO_SET_VOICEMEMO_CONFIG: { + struct msm_audio_voicememo_config usr_config; + MM_DBG("AUDIO_SET_VOICEMEMO_CONFIG\n"); + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + if (audvoicememo_validate_usr_config(&usr_config) + == 0) { + audio->voicememo_cfg = usr_config; + rc = 0; + } else + rc = -EINVAL; + MM_DBG("AUDIO_SET_VOICEMEMO_CONFIG rc %d\n", rc); + break; + } + case AUDIO_PAUSE: { + struct rpc_request_hdr rhdr; + MM_DBG("AUDIO_PAUSE\n"); + if (arg == 1) + msm_rpc_setup_req(&rhdr, audio->rpc_prog, + audio->rpc_ver, SND_VOC_REC_PAUSE_PROC); + else + msm_rpc_setup_req(&rhdr, audio->rpc_prog, + audio->rpc_ver, SND_VOC_REC_RESUME_PROC); + + rc = msm_rpc_write(audio->sndept, &rhdr, sizeof(rhdr)); + MM_DBG("AUDIO_PAUSE exit %d\n", rc); + break; + } + default: + MM_ERR("IOCTL %d not supported\n", cmd); + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_voicememo_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_voicememo *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + mutex_lock(&audio->read_lock); + + MM_DBG("buff read =0x%8x \n", count); + + while (count > 0) { + rc = wait_event_interruptible_timeout(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped), + msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS)); + + if (rc == 0) { + rc = -ETIMEDOUT; + break; + } else if (rc < 0) + break; + + if (audio->stopped) { + rc = -EBUSY; + break; + } + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not split frames, read count must be greater or + * equal to size of existing frames to copy + */ + MM_DBG("read not in frame boundary\n"); + break; + } else { + mutex_lock(&audio->dsp_lock); + dma_coherent_post_ops(); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + mutex_unlock(&audio->dsp_lock); + break; + } + count -= audio->in[audio->read_next].used; + audio->byte_count += audio->in[audio->read_next].used; + audio->frame_count += + audio->in[audio->read_next].numframes; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + mutex_unlock(&audio->dsp_lock); + if ((++audio->read_next) == MAX_REC_BUF_COUNT) + audio->read_next = 0; + if (audio->in[audio->read_next].used == 0) + break; /* No data ready at this moment + * Exit while loop to prevent + * output thread sleep too long + */ + } + } + mutex_unlock(&audio->read_lock); + if (buf > start) + rc = buf - start; + MM_DBG("exit return =0x%8x\n", rc); + return rc; +} + +static ssize_t audio_voicememo_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int audio_voicememo_release(struct inode *inode, struct file *file) +{ + struct audio_voicememo *audio = file->private_data; + + mutex_lock(&audio->lock); + audvoicememo_disable(audio); + audvoicememo_flush_buf(audio); + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static int audio_voicememo_open(struct inode *inode, struct file *file) +{ + struct audio_voicememo *audio = &the_audio_voicememo; + int rc; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + + rc = audmgr_open(&audio->audmgr); + + if (rc) + goto done; + + /*Set default param to None*/ + memset(&audio->voicememo_cfg, 0, sizeof(audio->voicememo_cfg)); + + file->private_data = audio; + audio->opened = 1; + audio->stopped = 0; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_voicememo_open, + .release = audio_voicememo_release, + .read = audio_voicememo_read, + .write = audio_voicememo_write, + .unlocked_ioctl = audio_voicememo_ioctl, +}; + +struct miscdevice audio_voicememo_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_voicememo", + .fops = &audio_fops, +}; + +static int audio_voicememo_probe(struct platform_device *pdev) +{ + int rc; + + if ((pdev->id != (SND_VERS_COMP & RPC_VERSION_MAJOR_MASK)) && + (pdev->id != (SND_VERS2_COMP & RPC_VERSION_MAJOR_MASK))) + return -EINVAL; + + mutex_init(&the_audio_voicememo.lock); + mutex_init(&the_audio_voicememo.read_lock); + mutex_init(&the_audio_voicememo.dsp_lock); + init_waitqueue_head(&the_audio_voicememo.read_wait); + init_waitqueue_head(&the_audio_voicememo.wait); + + the_audio_voicememo.rec_buf_ptr = dma_alloc_coherent(NULL, + MAX_VOICEMEMO_BUF_SIZE, + &the_audio_voicememo.phys, GFP_KERNEL); + if (the_audio_voicememo.rec_buf_ptr == NULL) { + MM_ERR("error allocating memory\n"); + rc = -ENOMEM; + return rc; + } + the_audio_voicememo.rec_buf_size = MAX_REC_BUF_SIZE; + MM_DBG("rec_buf_ptr = 0x%8x, phys = 0x%8x \n", + (uint32_t) the_audio_voicememo.rec_buf_ptr, \ + the_audio_voicememo.phys); + + the_audio_voicememo.sndept = msm_rpc_connect_compatible(SND_PROG, + SND_VERS_COMP, MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(the_audio_voicememo.sndept)) { + MM_DBG("connect failed with VERS \ + = %x, trying again with another API\n", + SND_VERS_COMP); + the_audio_voicememo.sndept = msm_rpc_connect_compatible( + SND_PROG, SND_VERS2_COMP, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(the_audio_voicememo.sndept)) { + rc = PTR_ERR(the_audio_voicememo.sndept); + the_audio_voicememo.sndept = NULL; + MM_ERR("Failed to connect to snd svc\n"); + goto err; + } + the_audio_voicememo.rpc_ver = SND_VERS2_COMP; + } else + the_audio_voicememo.rpc_ver = SND_VERS_COMP; + + the_audio_voicememo.task = kthread_run(voicememo_rpc_thread, + &the_audio_voicememo, "voicememo_rpc"); + if (IS_ERR(the_audio_voicememo.task)) { + rc = PTR_ERR(the_audio_voicememo.task); + the_audio_voicememo.task = NULL; + msm_rpc_close(the_audio_voicememo.sndept); + the_audio_voicememo.sndept = NULL; + MM_ERR("Failed to create voicememo_rpc task\n"); + goto err; + } + the_audio_voicememo.rpc_prog = SND_PROG; + + return misc_register(&audio_voicememo_misc); +err: + dma_free_coherent(NULL, MAX_VOICEMEMO_BUF_SIZE, + the_audio_voicememo.rec_buf_ptr, + the_audio_voicememo.phys); + the_audio_voicememo.rec_buf_ptr = NULL; + return rc; +} + +static void __exit audio_voicememo_exit(void) +{ + /* Close the RPC connection to make thread to comeout */ + msm_rpc_close(the_audio_voicememo.sndept); + the_audio_voicememo.sndept = NULL; + kthread_stop(the_audio_voicememo.task); + the_audio_voicememo.task = NULL; + if (the_audio_voicememo.rec_buf_ptr) + dma_free_coherent(NULL, MAX_VOICEMEMO_BUF_SIZE, + the_audio_voicememo.rec_buf_ptr, + the_audio_voicememo.phys); + the_audio_voicememo.rec_buf_ptr = NULL; + misc_deregister(&audio_voicememo_misc); +} + +static char audio_voicememo_rpc_name[] = "rs00000000"; + +static struct platform_driver audio_voicememo_driver = { + .probe = audio_voicememo_probe, + .driver = { + .owner = THIS_MODULE, + }, + }; + +static int __init audio_voicememo_init(void) +{ + snprintf(audio_voicememo_rpc_name, sizeof(audio_voicememo_rpc_name), + "rs%08x", SND_PROG); + audio_voicememo_driver.driver.name = audio_voicememo_rpc_name; + return platform_driver_register(&audio_voicememo_driver); +} + +module_init(audio_voicememo_init); +module_exit(audio_voicememo_exit); + +MODULE_DESCRIPTION("MSM Voice Memo driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("QUALCOMM"); diff --git a/arch/arm/mach-msm/qdsp5/audio_wma.c b/arch/arm/mach-msm/qdsp5/audio_wma.c new file mode 100644 index 00000000000..162a6f10e48 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_wma.c @@ -0,0 +1,1779 @@ +/* audio_wma.c - wma audio decoder driver + * + * Copyright (c) 2009, 2011-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +/* Size must be power of 2 */ +#define BUFSZ_MAX 2062 /* Includes meta in size */ +#define BUFSZ_MIN 1038 /* Includes meta in size */ +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_WMA 4 + +#define PCM_BUFSZ_MIN 8216 /* Hold one stereo WMA frame and meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDWMA_METAFIELD_MASK 0xFFFF0000 +#define AUDWMA_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDWMA_EOS_FLG_MASK 0x01 +#define AUDWMA_EOS_NONE 0x0 /* No EOS detected */ +#define AUDWMA_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDWMA_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audwma_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audwma_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + struct msm_audio_wma_config wma_config; + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int rmt_resource_released; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audwma_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwma_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_WMA; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_WMA; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for WMA \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_WMA; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + } + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("audio_update_pcm_buf_entry: \ + in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("audio_update_pcm_buf_entry: \ + expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play\n"); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +static struct msm_adsp_ops audplay_adsp_ops_wma = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMA; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_wma cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + /* + * Test done for sample with the following configuration + * armdatareqthr = 1262 + * channelsdecoded = 1(MONO)/2(STEREO) + * wmabytespersec = Tested with 6003 Bytes per sec + * wmasamplingfreq = 44100 + * wmaencoderopts = 31 + */ + + cmd.armdatareqthr = audio->wma_config.armdatareqthr; + cmd.channelsdecoded = audio->wma_config.channelsdecoded; + cmd.wmabytespersec = audio->wma_config.wmabytespersec; + cmd.wmasamplingfreq = audio->wma_config.wmasamplingfreq; + cmd.wmaencoderopts = audio->wma_config.wmaencoderopts; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + + MM_DBG("buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDWMA_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (audio->wflush) { + audio->out_needed = 1; + goto done; + } + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + MM_DBG("\n"); /* Macro prints the file name and function */ + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audwma_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audwma_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audwma_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audwma_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audwma_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audwma_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audwma_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audwma_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_WMA_CONFIG:{ + if (copy_to_user((void *)arg, &audio->wma_config, + sizeof(audio->wma_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_WMA_CONFIG:{ + struct msm_audio_wma_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + audio->wma_config = usr_config; + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("map of read buf failed\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("audio_read: no partial frame done reading\n"); + break; + } else { + MM_DBG("audio_read: read from in[%d]\n", + audio->read_next); + /* order reads from the output buffer */ + rmb(); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audwma_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audplay_send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDWMA_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("audio_write: mf offset_val %x\n", + mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDWMA_EOS_FLG_OFFSET] & + AUDWMA_EOS_FLG_MASK) { + MM_DBG("audio_write: EOS SET\n"); + eos_condition = AUDWMA_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDWMA_EOS_FLG_OFFSET] + &= ~AUDWMA_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("audio_write: continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + if (eos_condition == AUDWMA_EOS_SET) + rc = audwma_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audwma_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwma_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audwma_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audwma_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audwma_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audwma_suspend(struct early_suspend *h) +{ + struct audwma_suspend_ctl *ctl = + container_of(h, struct audwma_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwma_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audwma_resume(struct early_suspend *h) +{ + struct audwma_suspend_ctl *ctl = + container_of(h, struct audwma_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwma_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audwma_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audwma_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audwma_debug_fops = { + .read = audwma_debug_read, + .open = audwma_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + unsigned pmem_sz = DMASZ_MAX; + struct audwma_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_WMA; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap(audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) { + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance \ + 0x%08x\n", (int)audio); + goto err; + } + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_wma, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for WMA session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + audio->wma_config.armdatareqthr = 1262; + audio->wma_config.channelsdecoded = 2; + audio->wma_config.wmabytespersec = 6003; + audio->wma_config.wmasamplingfreq = 44100; + audio->wma_config.wmaencoderopts = 31; + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audio->vol_pan.volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wma_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audwma_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audwma_resume; + audio->suspend_ctl.node.suspend = audwma_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDWMA_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audwma_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +struct miscdevice audio_wma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_wma_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_wmapro.c b/arch/arm/mach-msm/qdsp5/audio_wmapro.c new file mode 100644 index 00000000000..641b1c77743 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_wmapro.c @@ -0,0 +1,1766 @@ +/* audio_wmapro.c - wmapro audio decoder driver + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" + +/* Size must be power of 2 */ +#define BUFSZ_MAX 8206 /* Includes meta in size */ +#define BUFSZ_MIN 2062 /* Includes meta in size */ +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_WMAPRO 13 + +#define PCM_BUFSZ_MIN 8216 /* Hold one stereo WMAPRO frame and meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDWMAPRO_METAFIELD_MASK 0xFFFF0000 +#define AUDWMAPRO_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDWMAPRO_EOS_FLG_MASK 0x01 +#define AUDWMAPRO_EOS_NONE 0x0 /* No EOS detected */ +#define AUDWMAPRO_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDWMAPRO_EVENT_NUM 10 /* Default no. of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audwmapro_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audwmapro_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + struct msm_audio_wmapro_config wmapro_config; + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int rmt_resource_released; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audwmapro_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + int eq_enable; + int eq_needs_commit; + audpp_cmd_cfg_object_params_eqalizer eq; + audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwmapro_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +static int rmt_put_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_DISABLE; + cmd.dec_type = AUDDEC_DEC_WMAPRO; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return put_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +static int rmt_get_resource(struct audio *audio) +{ + struct aud_codec_config_cmd cmd; + unsigned short client_idx; + + cmd.cmd_id = RM_CMD_AUD_CODEC_CFG; + cmd.client_id = RM_AUD_CLIENT_ID; + cmd.task_id = audio->dec_id; + cmd.enable = RMT_ENABLE; + cmd.dec_type = AUDDEC_DEC_WMAPRO; + client_idx = ((cmd.client_id << 8) | cmd.task_id); + + return get_adsp_resource(client_idx, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + if (audio->rmt_resource_released == 1) { + audio->rmt_resource_released = 0; + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for WMAPRO \ + session 0x%08x on decoder: %d\n Ignoring \ + error and going ahead with the playback\n", + (int)audio, audio->dec_id); + } + } + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_WMA; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + rmt_put_resource(audio); + audio->rmt_resource_released = 1; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("audio_update_pcm_buf_entry: \ + in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("audio_update_pcm_buf_entry: \ + expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play\n"); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +static struct msm_adsp_ops audplay_adsp_ops_wmapro = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)]; + + memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMAPRO; + else + cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_wmapro cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + cmd.armdatareqthr = audio->wmapro_config.armdatareqthr; + cmd.numchannels = audio->wmapro_config.numchannels; + cmd.validbitspersample = audio->wmapro_config.validbitspersample; + cmd.formattag = audio->wmapro_config.formattag; + cmd.samplingrate = audio->wmapro_config.samplingrate; + cmd.avgbytespersecond = audio->wmapro_config.avgbytespersecond; + cmd.asfpacketlength = audio->wmapro_config.asfpacketlength; + cmd.channelmask = audio->wmapro_config.channelmask; + cmd.encodeopt = audio->wmapro_config.encodeopt; + cmd.advancedencodeopt = audio->wmapro_config.advancedencodeopt; + cmd.advancedencodeopt2 = audio->wmapro_config.advancedencodeopt2; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + + MM_DBG("buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDWMAPRO_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (audio->wflush) { + audio->out_needed = 1; + goto done; + } + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + MM_DBG("\n"); /* Macro prints the file name and function */ + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static int audwmapro_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audwmapro_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audwmapro_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audwmapro_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audwmapro_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audwmapro_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audwmapro_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq); + audio->eq_needs_commit = 0; + } + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audwmapro_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_WMAPRO_CONFIG:{ + if (copy_to_user((void *)arg, &audio->wmapro_config, + sizeof(audio->wmapro_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_WMAPRO_CONFIG:{ + struct msm_audio_wmapro_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + audio->wmapro_config = usr_config; + + /* Need to swap the first and last words of advancedencodeopt2 + * as DSP cannot read 32-bit variable at a time. Need to be + * split into two 16-bit and swap them as required by DSP */ + + audio->wmapro_config.advancedencodeopt2 = + ((audio->wmapro_config.advancedencodeopt2 & 0xFFFF0000) + >> 16) | ((audio->wmapro_config.advancedencodeopt2 + << 16) & 0xFFFF0000); + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + + if (IS_ERR(audio->map_v_read)) { + MM_ERR("map of read buf failed\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = audio->map_v_read; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t a, loff_t b, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("audio_read: no partial frame done reading\n"); + break; + } else { + MM_DBG("audio_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audwmapro_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audplay_send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDWMAPRO_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("audio_write: mf offset_val %x\n", + mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET] & + AUDWMAPRO_EOS_FLG_MASK) { + MM_DBG("audio_write: EOS SET\n"); + eos_condition = AUDWMAPRO_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET] + &= ~AUDWMAPRO_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("audio_write: continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + if (eos_condition == AUDWMAPRO_EOS_SET) + rc = audwmapro_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + audio_disable(audio); + if (audio->rmt_resource_released == 0) + rmt_put_resource(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audwmapro_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwmapro_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audwmapro_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audwmapro_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audwmapro_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audwmapro_suspend(struct early_suspend *h) +{ + struct audwmapro_suspend_ctl *ctl = + container_of(h, struct audwmapro_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwmapro_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audwmapro_resume(struct early_suspend *h) +{ + struct audwmapro_suspend_ctl *ctl = + container_of(h, struct audwmapro_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwmapro_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audwmapro_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audwmapro_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audwmapro_debug_fops = { + .read = audwmapro_debug_read, + .open = audwmapro_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + unsigned pmem_sz = DMASZ_MAX; + struct audwmapro_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_WMAPRO; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap(audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + rc = audmgr_open(&audio->audmgr); + if (rc) { + MM_ERR("audmgr open failed, freeing instance 0x%08x\n", + (int)audio); + goto err; + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_wmapro, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + audmgr_close(&audio->audmgr); + goto err; + } + + rc = rmt_get_resource(audio); + if (rc) { + MM_ERR("ADSP resources are not available for WMAPRO session \ + 0x%08x on decoder: %d\n", (int)audio, audio->dec_id); + if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) + audmgr_close(&audio->audmgr); + msm_adsp_put(audio->audplay); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audio->vol_pan.volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wmapro_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audwmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audwmapro_resume; + audio->suspend_ctl.node.suspend = audwmapro_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDWMAPRO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audwmapro_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +struct miscdevice audio_wmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_wmapro_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c new file mode 100644 index 00000000000..231a28cdde8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audmgr.c @@ -0,0 +1,365 @@ +/* arch/arm/mach-msm/qdsp5/audmgr.c + * + * interface to "audmgr" service on the baseband cpu + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "audmgr.h" +#include + +#define STATE_CLOSED 0 +#define STATE_DISABLED 1 +#define STATE_ENABLING 2 +#define STATE_ENABLED 3 +#define STATE_DISABLING 4 +#define STATE_ERROR 5 + +/* store information used across complete audmgr sessions */ +struct audmgr_global { + struct mutex *lock; + struct msm_rpc_endpoint *ept; + struct task_struct *task; + uint32_t rpc_version; +}; +static DEFINE_MUTEX(audmgr_lock); + +static struct audmgr_global the_audmgr_state = { + .lock = &audmgr_lock, +}; + +static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid) +{ + uint32_t rep[6]; + + rep[0] = cpu_to_be32(xid); + rep[1] = cpu_to_be32(1); + rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS); + rep[4] = 0; + rep[5] = 0; + + msm_rpc_write(ept, rep, sizeof(rep)); +} + +static void process_audmgr_callback(struct audmgr_global *amg, + struct rpc_audmgr_cb_func_ptr *args, + int len) +{ + struct audmgr *am; + + /* Allow only if complete arguments recevied */ + if (len < (sizeof(struct rpc_audmgr_cb_func_ptr))) + return; + + /* Allow only if valid argument */ + if (be32_to_cpu(args->set_to_one) != 1) + return; + + am = (struct audmgr *) be32_to_cpu(args->client_data); + + if (!am) + return; + + switch (be32_to_cpu(args->status)) { + case RPC_AUDMGR_STATUS_READY: + am->handle = be32_to_cpu(args->u.handle); + MM_INFO("rpc READY handle=0x%08x\n", am->handle); + break; + case RPC_AUDMGR_STATUS_CODEC_CONFIG: { + uint32_t volume; + volume = be32_to_cpu(args->u.volume); + MM_INFO("rpc CODEC_CONFIG volume=0x%08x\n", volume); + am->state = STATE_ENABLED; + wake_up(&am->wait); + break; + } + case RPC_AUDMGR_STATUS_PENDING: + MM_ERR("PENDING?\n"); + break; + case RPC_AUDMGR_STATUS_SUSPEND: + MM_ERR("SUSPEND?\n"); + break; + case RPC_AUDMGR_STATUS_FAILURE: + MM_ERR("FAILURE\n"); + break; + case RPC_AUDMGR_STATUS_VOLUME_CHANGE: + MM_ERR("VOLUME_CHANGE?\n"); + break; + case RPC_AUDMGR_STATUS_DISABLED: + MM_ERR("DISABLED\n"); + am->state = STATE_DISABLED; + wake_up(&am->wait); + break; + case RPC_AUDMGR_STATUS_ERROR: + MM_ERR("ERROR?\n"); + am->state = STATE_ERROR; + wake_up(&am->wait); + break; + default: + break; + } +} + +static void process_rpc_request(uint32_t proc, uint32_t xid, + void *data, int len, void *private) +{ + struct audmgr_global *amg = private; + + if (proc == AUDMGR_CB_FUNC_PTR) + process_audmgr_callback(amg, data, len); + else + MM_ERR("unknown rpc proc %d\n", proc); + rpc_ack(amg->ept, xid); +} + +#define RPC_TYPE_REQUEST 0 +#define RPC_TYPE_REPLY 1 + +#define RPC_VERSION 2 + +#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) +#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) +#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) +#define RPC_REPLY_SZ (sizeof(uint32_t) * 6) + +static int audmgr_rpc_thread(void *data) +{ + struct audmgr_global *amg = data; + struct rpc_request_hdr *hdr = NULL; + uint32_t type; + int len; + + MM_INFO("start\n"); + + while (!kthread_should_stop()) { + if (hdr) { + kfree(hdr); + hdr = NULL; + } + len = msm_rpc_read(amg->ept, (void **) &hdr, -1, -1); + if (len < 0) { + MM_ERR("rpc read failed (%d)\n", len); + break; + } + if (len < RPC_COMMON_HDR_SZ) + continue; + + type = be32_to_cpu(hdr->type); + if (type == RPC_TYPE_REPLY) { + struct rpc_reply_hdr *rep = (void *) hdr; + uint32_t status; + if (len < RPC_REPLY_HDR_SZ) + continue; + status = be32_to_cpu(rep->reply_stat); + if (status == RPCMSG_REPLYSTAT_ACCEPTED) { + status = be32_to_cpu(rep->data.acc_hdr.accept_stat); + MM_INFO("rpc_reply status %d\n", status); + } else { + MM_INFO("rpc_reply denied!\n"); + } + /* process reply */ + continue; + } + + if (len < RPC_REQUEST_HDR_SZ) + continue; + + process_rpc_request(be32_to_cpu(hdr->procedure), + be32_to_cpu(hdr->xid), + (void *) (hdr + 1), + len - sizeof(*hdr), + data); + } + MM_INFO("exit\n"); + if (hdr) { + kfree(hdr); + hdr = NULL; + } + amg->task = NULL; + return 0; +} + +struct audmgr_enable_msg { + struct rpc_request_hdr hdr; + struct rpc_audmgr_enable_client_args args; +}; + +struct audmgr_disable_msg { + struct rpc_request_hdr hdr; + uint32_t handle; +}; + +int audmgr_open(struct audmgr *am) +{ + struct audmgr_global *amg = &the_audmgr_state; + int rc; + + if (am->state != STATE_CLOSED) + return 0; + + mutex_lock(amg->lock); + + /* connect to audmgr end point and polling thread only once */ + if (amg->ept == NULL) { + amg->ept = msm_rpc_connect_compatible(AUDMGR_PROG, + AUDMGR_VERS_COMP_VER3, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(amg->ept)) { + MM_ERR("connect failed with current VERS \ + = %x, trying again with another API\n", + AUDMGR_VERS_COMP_VER3); + amg->ept = msm_rpc_connect_compatible(AUDMGR_PROG, + AUDMGR_VERS_COMP_VER2, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(amg->ept)) { + MM_ERR("connect failed with current VERS \ + = %x, trying again with another API\n", + AUDMGR_VERS_COMP_VER2); + amg->ept = msm_rpc_connect_compatible( + AUDMGR_PROG, + AUDMGR_VERS_COMP, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(amg->ept)) { + MM_ERR("connect failed with current \ + VERS=%x, trying again with another \ + API\n", AUDMGR_VERS_COMP); + amg->ept = msm_rpc_connect(AUDMGR_PROG, + AUDMGR_VERS, + MSM_RPC_UNINTERRUPTIBLE); + amg->rpc_version = AUDMGR_VERS; + } else + amg->rpc_version = AUDMGR_VERS_COMP; + } else + amg->rpc_version = AUDMGR_VERS_COMP_VER2; + } else + amg->rpc_version = AUDMGR_VERS_COMP_VER3; + + if (IS_ERR(amg->ept)) { + rc = PTR_ERR(amg->ept); + amg->ept = NULL; + MM_ERR("failed to connect to audmgr svc\n"); + goto done; + } + + amg->task = kthread_run(audmgr_rpc_thread, amg, "audmgr_rpc"); + if (IS_ERR(amg->task)) { + rc = PTR_ERR(amg->task); + amg->task = NULL; + msm_rpc_close(amg->ept); + amg->ept = NULL; + goto done; + } + } + + /* Initialize session parameters */ + init_waitqueue_head(&am->wait); + am->state = STATE_DISABLED; + rc = 0; +done: + mutex_unlock(amg->lock); + return rc; +} +EXPORT_SYMBOL(audmgr_open); + +int audmgr_close(struct audmgr *am) +{ + return -EBUSY; +} +EXPORT_SYMBOL(audmgr_close); + +int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) +{ + struct audmgr_global *amg = &the_audmgr_state; + struct audmgr_enable_msg msg; + int rc; + + if (am->state == STATE_ENABLED) + return 0; + + if (am->state == STATE_DISABLING) + MM_ERR("state is DISABLING in enable?\n"); + am->state = STATE_ENABLING; + + MM_INFO("session 0x%08x\n", (int) am); + msg.args.set_to_one = cpu_to_be32(1); + msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate); + msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate); + msg.args.def_method = cpu_to_be32(cfg->def_method); + msg.args.codec_type = cpu_to_be32(cfg->codec); + msg.args.snd_method = cpu_to_be32(cfg->snd_method); + msg.args.cb_func = cpu_to_be32(0x11111111); + msg.args.client_data = cpu_to_be32((int)am); + + msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, amg->rpc_version, + AUDMGR_ENABLE_CLIENT); + + rc = msm_rpc_write(amg->ept, &msg, sizeof(msg)); + if (rc < 0) + return rc; + + rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ); + if (rc == 0) { + MM_ERR("ARM9 did not reply to RPC am->state = %d\n", am->state); + } + if (am->state == STATE_ENABLED) + return 0; + + MM_ERR("unexpected state %d while enabling?!\n", am->state); + return -ENODEV; +} +EXPORT_SYMBOL(audmgr_enable); + +int audmgr_disable(struct audmgr *am) +{ + struct audmgr_global *amg = &the_audmgr_state; + struct audmgr_disable_msg msg; + int rc; + + if (am->state == STATE_DISABLED) + return 0; + + MM_INFO("session 0x%08x\n", (int) am); + msg.handle = cpu_to_be32(am->handle); + msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, amg->rpc_version, + AUDMGR_DISABLE_CLIENT); + + am->state = STATE_DISABLING; + + rc = msm_rpc_write(amg->ept, &msg, sizeof(msg)); + if (rc < 0) + return rc; + + rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ); + if (rc == 0) { + MM_ERR("ARM9 did not reply to RPC am->state = %d\n", am->state); + } + + if (am->state == STATE_DISABLED) + return 0; + + MM_ERR("unexpected state %d while disabling?!\n", am->state); + return -ENODEV; +} +EXPORT_SYMBOL(audmgr_disable); diff --git a/arch/arm/mach-msm/qdsp5/audmgr.h b/arch/arm/mach-msm/qdsp5/audmgr.h new file mode 100644 index 00000000000..34c8488cd08 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audmgr.h @@ -0,0 +1,280 @@ +/* arch/arm/mach-msm/qdsp5/audmgr.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _AUDIO_RPC_H_ +#define _AUDIO_RPC_H_ + +#include + +enum rpc_aud_def_sample_rate_type { + RPC_AUD_DEF_SAMPLE_RATE_NONE, + RPC_AUD_DEF_SAMPLE_RATE_8000, + RPC_AUD_DEF_SAMPLE_RATE_11025, + RPC_AUD_DEF_SAMPLE_RATE_12000, + RPC_AUD_DEF_SAMPLE_RATE_16000, + RPC_AUD_DEF_SAMPLE_RATE_22050, + RPC_AUD_DEF_SAMPLE_RATE_24000, + RPC_AUD_DEF_SAMPLE_RATE_32000, + RPC_AUD_DEF_SAMPLE_RATE_44100, + RPC_AUD_DEF_SAMPLE_RATE_48000, + RPC_AUD_DEF_SAMPLE_RATE_MAX, +}; + +enum rpc_aud_def_method_type { + RPC_AUD_DEF_METHOD_NONE, + RPC_AUD_DEF_METHOD_KEY_BEEP, + RPC_AUD_DEF_METHOD_PLAYBACK, + RPC_AUD_DEF_METHOD_VOICE, + RPC_AUD_DEF_METHOD_RECORD, + RPC_AUD_DEF_METHOD_HOST_PCM, + RPC_AUD_DEF_METHOD_MIDI_OUT, + RPC_AUD_DEF_METHOD_RECORD_SBC, + RPC_AUD_DEF_METHOD_DTMF_RINGER, + RPC_AUD_DEF_METHOD_MAX, +}; + +enum rpc_aud_def_codec_type { + RPC_AUD_DEF_CODEC_NONE, + RPC_AUD_DEF_CODEC_DTMF, + RPC_AUD_DEF_CODEC_MIDI, + RPC_AUD_DEF_CODEC_MP3, + RPC_AUD_DEF_CODEC_PCM, + RPC_AUD_DEF_CODEC_AAC, + RPC_AUD_DEF_CODEC_WMA, + RPC_AUD_DEF_CODEC_RA, + RPC_AUD_DEF_CODEC_ADPCM, + RPC_AUD_DEF_CODEC_GAUDIO, + RPC_AUD_DEF_CODEC_VOC_EVRC, + RPC_AUD_DEF_CODEC_VOC_13K, + RPC_AUD_DEF_CODEC_VOC_4GV_NB, + RPC_AUD_DEF_CODEC_VOC_AMR, + RPC_AUD_DEF_CODEC_VOC_EFR, + RPC_AUD_DEF_CODEC_VOC_FR, + RPC_AUD_DEF_CODEC_VOC_HR, + RPC_AUD_DEF_CODEC_VOC_CDMA, + RPC_AUD_DEF_CODEC_VOC_CDMA_WB, + RPC_AUD_DEF_CODEC_VOC_UMTS, + RPC_AUD_DEF_CODEC_VOC_UMTS_WB, + RPC_AUD_DEF_CODEC_SBC, + RPC_AUD_DEF_CODEC_VOC_PCM, + RPC_AUD_DEF_CODEC_AMR_WB, + RPC_AUD_DEF_CODEC_AMR_WB_PLUS, + RPC_AUD_DEF_CODEC_AAC_BSAC, + RPC_AUD_DEF_CODEC_MAX, + RPC_AUD_DEF_CODEC_AMR_NB, + RPC_AUD_DEF_CODEC_13K, + RPC_AUD_DEF_CODEC_EVRC, + RPC_AUD_DEF_CODEC_MAX_002, +}; + +enum rpc_snd_method_type { + RPC_SND_METHOD_VOICE = 0, + RPC_SND_METHOD_KEY_BEEP, + RPC_SND_METHOD_MESSAGE, + RPC_SND_METHOD_RING, + RPC_SND_METHOD_MIDI, + RPC_SND_METHOD_AUX, + RPC_SND_METHOD_MAX, +}; + +enum rpc_voc_codec_type { + RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_1, + RPC_VOC_CODEC_STEREO_HEADSET, + RPC_VOC_CODEC_ON_CHIP_AUX, + RPC_VOC_CODEC_BT_OFF_BOARD, + RPC_VOC_CODEC_BT_A2DP, + RPC_VOC_CODEC_OFF_BOARD, + RPC_VOC_CODEC_SDAC, + RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TTY_ON_CHIP_1, + RPC_VOC_CODEC_TTY_OFF_BOARD, + RPC_VOC_CODEC_TTY_VCO, + RPC_VOC_CODEC_TTY_HCO, + RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC, + RPC_VOC_CODEC_MAX, + RPC_VOC_CODEC_NONE, +}; + +enum rpc_audmgr_status_type { + RPC_AUDMGR_STATUS_READY, + RPC_AUDMGR_STATUS_CODEC_CONFIG, + RPC_AUDMGR_STATUS_PENDING, + RPC_AUDMGR_STATUS_SUSPEND, + RPC_AUDMGR_STATUS_FAILURE, + RPC_AUDMGR_STATUS_VOLUME_CHANGE, + RPC_AUDMGR_STATUS_DISABLED, + RPC_AUDMGR_STATUS_ERROR, +}; + +struct rpc_audmgr_enable_client_args { + uint32_t set_to_one; + uint32_t tx_sample_rate; + uint32_t rx_sample_rate; + uint32_t def_method; + uint32_t codec_type; + uint32_t snd_method; + + uint32_t cb_func; + uint32_t client_data; +}; + +#define AUDMGR_ENABLE_CLIENT 2 +#define AUDMGR_DISABLE_CLIENT 3 +#define AUDMGR_SUSPEND_EVENT_RSP 4 +#define AUDMGR_REGISTER_OPERATION_LISTENER 5 +#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6 +#define AUDMGR_REGISTER_CODEC_LISTENER 7 +#define AUDMGR_GET_RX_SAMPLE_RATE 8 +#define AUDMGR_GET_TX_SAMPLE_RATE 9 +#define AUDMGR_SET_DEVICE_MODE 10 + +#define AUDMGR_PROG_VERS "rs30000013:0x7feccbff" +#define AUDMGR_PROG 0x30000013 +#define AUDMGR_VERS 0x7feccbff +#define AUDMGR_VERS_COMP 0x00010001 +#define AUDMGR_VERS_COMP_VER2 0x00020001 +#define AUDMGR_VERS_COMP_VER3 0x00030001 + +struct rpc_audmgr_cb_func_ptr { + uint32_t cb_id; /* cb_func */ + uint32_t status; /* Audmgr status */ + uint32_t set_to_one; /* Pointer status (1 = valid, 0 = invalid) */ + uint32_t disc; + /* disc = AUDMGR_STATUS_READY => data=handle + disc = AUDMGR_STATUS_CODEC_CONFIG => data = volume + disc = AUDMGR_STATUS_DISABLED => data =status_disabled + disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume_change */ + union { + uint32_t handle; + uint32_t volume; + uint32_t status_disabled; + uint32_t volume_change; + } u; + uint32_t client_data; +}; + +#define AUDMGR_CB_FUNC_PTR 1 +#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2 +#define AUDMGR_CODEC_LSTR_FUNC_PTR 3 + +#define AUDMGR_CB_PROG_VERS "rs31000013:0xf8e3e2d9" +#define AUDMGR_CB_PROG 0x31000013 +#define AUDMGR_CB_VERS 0xf8e3e2d9 + +struct audmgr { + wait_queue_head_t wait; + uint32_t handle; + int state; +}; + +struct audmgr_config { + uint32_t tx_rate; + uint32_t rx_rate; + uint32_t def_method; + uint32_t codec; + uint32_t snd_method; +}; + +int audmgr_open(struct audmgr *am); +int audmgr_close(struct audmgr *am); +int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg); +int audmgr_disable(struct audmgr *am); + +typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg); +typedef void (*audrec_event_func)(void *private, unsigned id, uint16_t *msg); + +/* worst case delay of 1sec for response */ +#define MSM_AUD_DECODER_WAIT_MS 1000 +#define MSM_AUD_MODE_TUNNEL 0x00000100 +#define MSM_AUD_MODE_NONTUNNEL 0x00000200 +#define MSM_AUD_DECODER_MASK 0x0000FFFF +#define MSM_AUD_OP_MASK 0xFFFF0000 + +/*Playback mode*/ +#define NON_TUNNEL_MODE_PLAYBACK 1 +#define TUNNEL_MODE_PLAYBACK 0 + +enum msm_aud_decoder_state { + MSM_AUD_DECODER_STATE_NONE = 0, + MSM_AUD_DECODER_STATE_FAILURE = 1, + MSM_AUD_DECODER_STATE_SUCCESS = 2, + MSM_AUD_DECODER_STATE_CLOSE = 3, +}; + +int audpp_adec_alloc(unsigned dec_attrb, const char **module_name, + unsigned *queueid); +void audpp_adec_free(int decid); + +struct audpp_event_callback { + audpp_event_func fn; + void *private; +}; + +int audpp_register_event_callback(struct audpp_event_callback *eh); +int audpp_unregister_event_callback(struct audpp_event_callback *eh); +int is_audpp_enable(void); + +int audpp_enable(int id, audpp_event_func func, void *private); +void audpp_disable(int id, void *private); + +int audpp_send_queue1(void *cmd, unsigned len); +int audpp_send_queue2(void *cmd, unsigned len); +int audpp_send_queue3(void *cmd, unsigned len); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan); +int audpp_pause(unsigned id, int pause); +int audpp_flush(unsigned id); +void audpp_avsync(int id, unsigned rate); +unsigned audpp_avsync_sample_count(int id); +unsigned audpp_avsync_byte_count(int id); +int audpp_dsp_set_mbadrc(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_mbadrc *mbadrc); +int audpp_dsp_set_eq(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_eqalizer *eq); +int audpp_dsp_set_rx_iir(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_pcm *iir); + +int audpp_dsp_set_rx_srs_trumedia_g + (struct audpp_cmd_cfg_object_params_srstm_g *srstm); +int audpp_dsp_set_rx_srs_trumedia_w + (struct audpp_cmd_cfg_object_params_srstm_w *srstm); +int audpp_dsp_set_rx_srs_trumedia_c + (struct audpp_cmd_cfg_object_params_srstm_c *srstm); +int audpp_dsp_set_rx_srs_trumedia_h + (struct audpp_cmd_cfg_object_params_srstm_h *srstm); +int audpp_dsp_set_rx_srs_trumedia_p + (struct audpp_cmd_cfg_object_params_srstm_p *srstm); +int audpp_dsp_set_rx_srs_trumedia_l + (struct audpp_cmd_cfg_object_params_srstm_l *srstm); + +int audpp_dsp_set_vol_pan(unsigned id, + audpp_cmd_cfg_object_params_volume *vol_pan); +int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_qconcert *qconcert_plus); +int audrectask_enable(unsigned enc_type, audrec_event_func func, void *private); +void audrectask_disable(unsigned enc_type, void *private); + +int audrectask_send_cmdqueue(void *cmd, unsigned len); +int audrectask_send_bitstreamqueue(void *cmd, unsigned len); + +#endif diff --git a/arch/arm/mach-msm/qdsp5/audmgr_new.h b/arch/arm/mach-msm/qdsp5/audmgr_new.h new file mode 100644 index 00000000000..3604405b39c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audmgr_new.h @@ -0,0 +1,213 @@ +/* arch/arm/mach-msm/qdsp5/audmgr.h + * + * Copyright 2008 (c) Code Aurora Forum. All rights reserved. + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H +#define _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H + +enum rpc_aud_def_sample_rate_type { + RPC_AUD_DEF_SAMPLE_RATE_NONE, + RPC_AUD_DEF_SAMPLE_RATE_8000, + RPC_AUD_DEF_SAMPLE_RATE_11025, + RPC_AUD_DEF_SAMPLE_RATE_12000, + RPC_AUD_DEF_SAMPLE_RATE_16000, + RPC_AUD_DEF_SAMPLE_RATE_22050, + RPC_AUD_DEF_SAMPLE_RATE_24000, + RPC_AUD_DEF_SAMPLE_RATE_32000, + RPC_AUD_DEF_SAMPLE_RATE_44100, + RPC_AUD_DEF_SAMPLE_RATE_48000, + RPC_AUD_DEF_SAMPLE_RATE_MAX, +}; + +enum rpc_aud_def_method_type { + RPC_AUD_DEF_METHOD_NONE, + RPC_AUD_DEF_METHOD_KEY_BEEP, + RPC_AUD_DEF_METHOD_PLAYBACK, + RPC_AUD_DEF_METHOD_VOICE, + RPC_AUD_DEF_METHOD_RECORD, + RPC_AUD_DEF_METHOD_HOST_PCM, + RPC_AUD_DEF_METHOD_MIDI_OUT, + RPC_AUD_DEF_METHOD_RECORD_SBC, + RPC_AUD_DEF_METHOD_DTMF_RINGER, + RPC_AUD_DEF_METHOD_MAX, +}; + +enum rpc_aud_def_codec_type { + RPC_AUD_DEF_CODEC_NONE, + RPC_AUD_DEF_CODEC_DTMF, + RPC_AUD_DEF_CODEC_MIDI, + RPC_AUD_DEF_CODEC_MP3, + RPC_AUD_DEF_CODEC_PCM, + RPC_AUD_DEF_CODEC_AAC, + RPC_AUD_DEF_CODEC_WMA, + RPC_AUD_DEF_CODEC_RA, + RPC_AUD_DEF_CODEC_ADPCM, + RPC_AUD_DEF_CODEC_GAUDIO, + RPC_AUD_DEF_CODEC_VOC_EVRC, + RPC_AUD_DEF_CODEC_VOC_13K, + RPC_AUD_DEF_CODEC_VOC_4GV_NB, + RPC_AUD_DEF_CODEC_VOC_AMR, + RPC_AUD_DEF_CODEC_VOC_EFR, + RPC_AUD_DEF_CODEC_VOC_FR, + RPC_AUD_DEF_CODEC_VOC_HR, + RPC_AUD_DEF_CODEC_VOC_CDMA, + RPC_AUD_DEF_CODEC_VOC_CDMA_WB, + RPC_AUD_DEF_CODEC_VOC_UMTS, + RPC_AUD_DEF_CODEC_VOC_UMTS_WB, + RPC_AUD_DEF_CODEC_SBC, + RPC_AUD_DEF_CODEC_VOC_PCM, + RPC_AUD_DEF_CODEC_AMR_WB, + RPC_AUD_DEF_CODEC_AMR_WB_PLUS, + RPC_AUD_DEF_CODEC_AAC_BSAC, + RPC_AUD_DEF_CODEC_MAX, + RPC_AUD_DEF_CODEC_AMR_NB, + RPC_AUD_DEF_CODEC_13K, + RPC_AUD_DEF_CODEC_EVRC, + RPC_AUD_DEF_CODEC_MAX_002, +}; + +enum rpc_snd_method_type { + RPC_SND_METHOD_VOICE = 0, + RPC_SND_METHOD_KEY_BEEP, + RPC_SND_METHOD_MESSAGE, + RPC_SND_METHOD_RING, + RPC_SND_METHOD_MIDI, + RPC_SND_METHOD_AUX, + RPC_SND_METHOD_MAX, +}; + +enum rpc_voc_codec_type { + RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_1, + RPC_VOC_CODEC_STEREO_HEADSET, + RPC_VOC_CODEC_ON_CHIP_AUX, + RPC_VOC_CODEC_BT_OFF_BOARD, + RPC_VOC_CODEC_BT_A2DP, + RPC_VOC_CODEC_OFF_BOARD, + RPC_VOC_CODEC_SDAC, + RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TTY_ON_CHIP_1, + RPC_VOC_CODEC_TTY_OFF_BOARD, + RPC_VOC_CODEC_TTY_VCO, + RPC_VOC_CODEC_TTY_HCO, + RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC, + RPC_VOC_CODEC_MAX, + RPC_VOC_CODEC_NONE, +}; + +enum rpc_audmgr_status_type { + RPC_AUDMGR_STATUS_READY, + RPC_AUDMGR_STATUS_CODEC_CONFIG, + RPC_AUDMGR_STATUS_PENDING, + RPC_AUDMGR_STATUS_SUSPEND, + RPC_AUDMGR_STATUS_FAILURE, + RPC_AUDMGR_STATUS_VOLUME_CHANGE, + RPC_AUDMGR_STATUS_DISABLED, + RPC_AUDMGR_STATUS_ERROR, +}; + +struct rpc_audmgr_enable_client_args { + uint32_t set_to_one; + uint32_t tx_sample_rate; + uint32_t rx_sample_rate; + uint32_t def_method; + uint32_t codec_type; + uint32_t snd_method; + + uint32_t cb_func; + uint32_t client_data; +}; + +#define AUDMGR_ENABLE_CLIENT 2 +#define AUDMGR_DISABLE_CLIENT 3 +#define AUDMGR_SUSPEND_EVENT_RSP 4 +#define AUDMGR_REGISTER_OPERATION_LISTENER 5 +#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6 +#define AUDMGR_REGISTER_CODEC_LISTENER 7 +#define AUDMGR_GET_RX_SAMPLE_RATE 8 +#define AUDMGR_GET_TX_SAMPLE_RATE 9 +#define AUDMGR_SET_DEVICE_MODE 10 + +#define AUDMGR_PROG 0x30000013 +#define AUDMGR_VERS MSM_RPC_VERS(1,0) + +struct rpc_audmgr_cb_func_ptr { + uint32_t cb_id; + uint32_t status; /* Audmgr status */ + uint32_t set_to_one; /* Pointer status (1 = valid, 0 = invalid) */ + uint32_t disc; + /* disc = AUDMGR_STATUS_READY => data=handle + disc = AUDMGR_STATUS_CODEC_CONFIG => data = handle + disc = AUDMGR_STATUS_DISABLED => data =status_disabled + disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume-change */ + union { + uint32_t handle; + uint32_t volume; + uint32_t status_disabled; + uint32_t volume_change; + } u; +}; + +#define AUDMGR_CB_FUNC_PTR 1 +#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2 +#define AUDMGR_CODEC_LSTR_FUNC_PTR 3 + +#define AUDMGR_CB_PROG 0x31000013 +#define AUDMGR_CB_VERS 0xf8e3e2d9 + +struct audmgr { + wait_queue_head_t wait; + uint32_t handle; + struct msm_rpc_endpoint *ept; + struct task_struct *task; + int state; +}; + +struct audmgr_config { + uint32_t tx_rate; + uint32_t rx_rate; + uint32_t def_method; + uint32_t codec; + uint32_t snd_method; +}; + +int audmgr_open(struct audmgr *am); +int audmgr_close(struct audmgr *am); +int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg); +int audmgr_disable(struct audmgr *am); + +typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg); + +int audpp_enable(int id, audpp_event_func func, void *private); +void audpp_disable(int id, void *private); + +int audpp_send_queue1(void *cmd, unsigned len); +int audpp_send_queue2(void *cmd, unsigned len); +int audpp_send_queue3(void *cmd, unsigned len); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan); +int audpp_pause(unsigned id, int pause); +int audpp_flush(unsigned id); +void audpp_avsync(int id, unsigned rate); +unsigned audpp_avsync_sample_count(int id); +unsigned audpp_avsync_byte_count(int id); + +#endif diff --git a/arch/arm/mach-msm/qdsp5/audpp.c b/arch/arm/mach-msm/qdsp5/audpp.c new file mode 100644 index 00000000000..1616ad0b524 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audpp.c @@ -0,0 +1,1014 @@ + +/* arch/arm/mach-msm/qdsp5/audpp.c + * + * common code to deal with the AUDPP dsp task (audio postproc) + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009-2010, 2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "audmgr.h" + +#include +#include +#include + +#include "evlog.h" + +enum { + EV_NULL, + EV_ENABLE, + EV_DISABLE, + EV_EVENT, + EV_DATA, +}; + +static const char *dsp_log_strings[] = { + "NULL", + "ENABLE", + "DISABLE", + "EVENT", + "DATA", +}; + +DECLARE_LOG(dsp_log, 64, dsp_log_strings); + +static int __init _dsp_log_init(void) +{ + return ev_log_init(&dsp_log); +} + +module_init(_dsp_log_init); +#define LOG(id,arg) ev_log_write(&dsp_log, id, arg) + +static DEFINE_MUTEX(audpp_lock); +static DEFINE_MUTEX(audpp_dec_lock); + +#define CH_COUNT 5 +#define AUDPP_CLNT_MAX_COUNT 6 +#define AUDPP_AVSYNC_INFO_SIZE 7 + +#define AUDPP_SRS_PARAMS 2 +#define AUDPP_SRS_PARAMS_G 0 +#define AUDPP_SRS_PARAMS_W 1 +#define AUDPP_SRS_PARAMS_C 2 +#define AUDPP_SRS_PARAMS_H 3 +#define AUDPP_SRS_PARAMS_P 4 +#define AUDPP_SRS_PARAMS_L 5 + +#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 +#define AUDPP_CMD_EQ_FLAG_DIS 0x0000 +#define AUDPP_CMD_EQ_FLAG_ENA -1 +#define AUDPP_CMD_IIR_FLAG_DIS 0x0000 +#define AUDPP_CMD_IIR_FLAG_ENA -1 + +#define AUDPP_CMD_VOLUME_PAN 0 +#define AUDPP_CMD_IIR_TUNING_FILTER 1 +#define AUDPP_CMD_EQUALIZER 2 +#define AUDPP_CMD_ADRC 3 +#define AUDPP_CMD_SPECTROGRAM 4 +#define AUDPP_CMD_QCONCERT 5 +#define AUDPP_CMD_SIDECHAIN_TUNING_FILTER 6 +#define AUDPP_CMD_SAMPLING_FREQUENCY 7 +#define AUDPP_CMD_QAFX 8 +#define AUDPP_CMD_QRUMBLE 9 +#define AUDPP_CMD_MBADRC 10 + +#define MAX_EVENT_CALLBACK_CLIENTS 1 + +#define AUDPP_CONCURRENCY_DEFAULT 6 /* All non tunnel mode */ +#define AUDPP_MAX_DECODER_CNT 5 +#define AUDPP_CODEC_MASK 0x000000FF +#define AUDPP_MODE_MASK 0x00000F00 +#define AUDPP_OP_MASK 0xF0000000 + +struct audpp_decoder_info { + unsigned int codec; + pid_t pid; +}; + +struct audpp_state { + struct msm_adsp_module *mod; + audpp_event_func func[AUDPP_CLNT_MAX_COUNT]; + void *private[AUDPP_CLNT_MAX_COUNT]; + struct mutex *lock; + unsigned open_count; + unsigned enabled; + + /* Related to decoder allocation */ + struct mutex *lock_dec; + struct msm_adspdec_database *dec_database; + struct audpp_decoder_info dec_info_table[AUDPP_MAX_DECODER_CNT]; + unsigned dec_inuse; + unsigned long concurrency; + + /* which channels are actually enabled */ + unsigned avsync_mask; + + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1]; + struct audpp_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS]; + + spinlock_t avsync_lock; + + wait_queue_head_t event_wait; +}; + +struct audpp_state the_audpp_state = { + .lock = &audpp_lock, + .lock_dec = &audpp_dec_lock, +}; + +int audpp_send_queue1(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd1Queue, cmd, len); +} +EXPORT_SYMBOL(audpp_send_queue1); + +int audpp_send_queue2(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd2Queue, cmd, len); +} +EXPORT_SYMBOL(audpp_send_queue2); + +int audpp_send_queue3(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd3Queue, cmd, len); +} +EXPORT_SYMBOL(audpp_send_queue3); + +static int audpp_dsp_config(int enable) +{ + audpp_cmd_cfg cmd; + + cmd.cmd_id = AUDPP_CMD_CFG; + cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +int is_audpp_enable(void) +{ + struct audpp_state *audpp = &the_audpp_state; + + return audpp->enabled; +} +EXPORT_SYMBOL(is_audpp_enable); + +int audpp_register_event_callback(struct audpp_event_callback *ecb) +{ + struct audpp_state *audpp = &the_audpp_state; + int i; + + for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { + if (NULL == audpp->cb_tbl[i]) { + audpp->cb_tbl[i] = ecb; + return 0; + } + } + return -1; +} +EXPORT_SYMBOL(audpp_register_event_callback); + +int audpp_unregister_event_callback(struct audpp_event_callback *ecb) +{ + struct audpp_state *audpp = &the_audpp_state; + int i; + + for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { + if (ecb == audpp->cb_tbl[i]) { + audpp->cb_tbl[i] = NULL; + return 0; + } + } + return -1; +} +EXPORT_SYMBOL(audpp_unregister_event_callback); + +static void audpp_broadcast(struct audpp_state *audpp, unsigned id, + uint16_t *msg) +{ + unsigned n; + for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) { + if (audpp->func[n]) + audpp->func[n] (audpp->private[n], id, msg); + } + + for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) + if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn) + audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, id, + msg); +} + +static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id, + unsigned id, uint16_t *msg) +{ + if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id]) + audpp->func[clnt_id] (audpp->private[clnt_id], id, msg); +} + +static void audpp_handle_pcmdmamiss(struct audpp_state *audpp, + uint16_t bit_mask) +{ + uint8_t b_index; + + for (b_index = 0; b_index < AUDPP_CLNT_MAX_COUNT; b_index++) { + if (bit_mask & (0x1 << b_index)) + if (audpp->func[b_index]) + audpp->func[b_index] (audpp->private[b_index], + AUDPP_MSG_PCMDMAMISSED, + &bit_mask); + } +} + +static void audpp_fake_event(struct audpp_state *audpp, int id, + unsigned event, unsigned arg) +{ + uint16_t msg[1]; + msg[0] = arg; + audpp->func[id] (audpp->private[id], event, msg); +} + +static void audpp_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audpp_state *audpp = data; + unsigned long flags; + uint16_t msg[8]; + int cid = 0; + + if (id == AUDPP_MSG_AVSYNC_MSG) { + spin_lock_irqsave(&audpp->avsync_lock, flags); + getevent(audpp->avsync, sizeof(audpp->avsync)); + + /* mask off any channels we're not watching to avoid + * cases where we might get one last update after + * disabling avsync and end up in an odd state when + * we next read... + */ + audpp->avsync[0] &= audpp->avsync_mask; + spin_unlock_irqrestore(&audpp->avsync_lock, flags); + return; + } + + getevent(msg, sizeof(msg)); + + LOG(EV_EVENT, (id << 16) | msg[0]); + LOG(EV_DATA, (msg[1] << 16) | msg[2]); + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned cid = msg[0]; + MM_DBG("status %d %d %d\n", cid, msg[1], msg[2]); + if ((cid < 5) && audpp->func[cid]) + audpp->func[cid] (audpp->private[cid], id, msg); + break; + } + case AUDPP_MSG_HOST_PCM_INTF_MSG: + if (audpp->func[5]) + audpp->func[5] (audpp->private[5], id, msg); + break; + case AUDPP_MSG_PCMDMAMISSED: + audpp_handle_pcmdmamiss(audpp, msg[0]); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_INFO("ENABLE\n"); + if (!audpp->enabled) { + audpp->enabled = 1; + audpp_broadcast(audpp, id, msg); + } else { + cid = msg[1]; + audpp_fake_event(audpp, cid, + id, AUDPP_MSG_ENA_ENA); + } + + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + if (audpp->open_count == 0) { + MM_INFO("DISABLE\n"); + audpp->enabled = 0; + wake_up(&audpp->event_wait); + audpp_broadcast(audpp, id, msg); + } else { + cid = msg[1]; + audpp_fake_event(audpp, cid, + id, AUDPP_MSG_ENA_DIS); + audpp->func[cid] = NULL; + audpp->private[cid] = NULL; + } + } else { + MM_ERR("invalid config msg %d\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + audpp_notify_clnt(audpp, msg[0], id, msg); + break; + case AUDPP_MSG_FLUSH_ACK: + audpp_notify_clnt(audpp, msg[0], id, msg); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable/disable(audpptask)"); + break; + default: + MM_ERR("unhandled msg id %x\n", id); + } +} + +static struct msm_adsp_ops adsp_ops = { + .event = audpp_dsp_event, +}; + +int audpp_enable(int id, audpp_event_func func, void *private) +{ + struct audpp_state *audpp = &the_audpp_state; + uint16_t msg[8]; + int res = 0; + + if (id < -1 || id > 4) + return -EINVAL; + + if (id == -1) + id = 5; + + mutex_lock(audpp->lock); + if (audpp->func[id]) { + res = -EBUSY; + goto out; + } + + audpp->func[id] = func; + audpp->private[id] = private; + + LOG(EV_ENABLE, 1); + if (audpp->open_count++ == 0) { + MM_DBG("enable\n"); + res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp); + if (res < 0) { + MM_ERR("cannot open AUDPPTASK\n"); + audpp->open_count = 0; + audpp->func[id] = NULL; + audpp->private[id] = NULL; + goto out; + } + LOG(EV_ENABLE, 2); + msm_adsp_enable(audpp->mod); + audpp_dsp_config(1); + } else { + if (audpp->enabled) { + msg[0] = AUDPP_MSG_ENA_ENA; + msg[1] = id; + res = msm_adsp_generate_event(audpp, audpp->mod, + AUDPP_MSG_CFG_MSG, sizeof(msg), + sizeof(uint16_t), (void *)msg); + if (res < 0) + goto out; + } + } + + res = 0; +out: + mutex_unlock(audpp->lock); + return res; +} +EXPORT_SYMBOL(audpp_enable); + +void audpp_disable(int id, void *private) +{ + struct audpp_state *audpp = &the_audpp_state; + uint16_t msg[8]; + int rc; + + if (id < -1 || id > 4) + return; + + if (id == -1) + id = 5; + + mutex_lock(audpp->lock); + LOG(EV_DISABLE, 1); + if (!audpp->func[id]) + goto out; + if (audpp->private[id] != private) + goto out; + + msg[0] = AUDPP_MSG_ENA_DIS; + msg[1] = id; + rc = msm_adsp_generate_event(audpp, audpp->mod, + AUDPP_MSG_CFG_MSG, sizeof(msg), + sizeof(uint16_t), (void *)msg); + if (rc < 0) + goto out; + + if (--audpp->open_count == 0) { + MM_DBG("disable\n"); + LOG(EV_DISABLE, 2); + audpp_dsp_config(0); + rc = wait_event_interruptible(audpp->event_wait, + (audpp->enabled == 0)); + if (audpp->enabled == 0) + MM_INFO("Received CFG_MSG_DISABLE from ADSP\n"); + else + MM_ERR("Didn't receive CFG_MSG DISABLE \ + message from ADSP\n"); + msm_adsp_disable(audpp->mod); + msm_adsp_put(audpp->mod); + audpp->mod = NULL; + } +out: + mutex_unlock(audpp->lock); +} +EXPORT_SYMBOL(audpp_disable); + +#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT)) + +void audpp_avsync(int id, unsigned rate) +{ + unsigned long flags; + audpp_cmd_avsync cmd; + + if (BAD_ID(id)) + return; + + spin_lock_irqsave(&the_audpp_state.avsync_lock, flags); + if (rate) + the_audpp_state.avsync_mask |= (1 << id); + else + the_audpp_state.avsync_mask &= (~(1 << id)); + the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask; + spin_unlock_irqrestore(&the_audpp_state.avsync_lock, flags); + + cmd.cmd_id = AUDPP_CMD_AVSYNC; + cmd.object_number = id; + cmd.interrupt_interval_lsw = rate; + cmd.interrupt_interval_msw = rate >> 16; + audpp_send_queue1(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_avsync); + +unsigned audpp_avsync_sample_count(int id) +{ + struct audpp_state *audpp = &the_audpp_state; + uint16_t *avsync = audpp->avsync; + unsigned val; + unsigned long flags; + unsigned mask; + + if (BAD_ID(id)) + return 0; + + mask = 1 << id; + id = id * AUDPP_AVSYNC_INFO_SIZE + 2; + spin_lock_irqsave(&audpp->avsync_lock, flags); + if (avsync[0] & mask) + val = (avsync[id] << 16) | avsync[id + 1]; + else + val = 0; + spin_unlock_irqrestore(&audpp->avsync_lock, flags); + + return val; +} +EXPORT_SYMBOL(audpp_avsync_sample_count); + +unsigned audpp_avsync_byte_count(int id) +{ + struct audpp_state *audpp = &the_audpp_state; + uint16_t *avsync = audpp->avsync; + unsigned val; + unsigned long flags; + unsigned mask; + + if (BAD_ID(id)) + return 0; + + mask = 1 << id; + id = id * AUDPP_AVSYNC_INFO_SIZE + 5; + spin_lock_irqsave(&audpp->avsync_lock, flags); + if (avsync[0] & mask) + val = (avsync[id] << 16) | avsync[id + 1]; + else + val = 0; + spin_unlock_irqrestore(&audpp->avsync_lock, flags); + + return val; +} +EXPORT_SYMBOL(audpp_avsync_byte_count); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan) +{ + /* cmd, obj_cfg[7], cmd_type, volume, pan */ + uint16_t cmd[11]; + + if (id > 6) + return -EINVAL; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS; + cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd[8] = AUDPP_CMD_VOLUME_PAN; + cmd[9] = volume; + cmd[10] = pan; + + return audpp_send_queue3(cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_set_volume_and_pan); + +/* Implementation of COPP features */ +int audpp_dsp_set_mbadrc(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_mbadrc *mbadrc) +{ + audpp_cmd_cfg_object_params_mbadrc cmd; + + if (id != 6) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_MBADRC; + + if (enable) { + memcpy(&cmd.num_bands, &mbadrc->num_bands, + sizeof(*mbadrc) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)); + cmd.enable = AUDPP_CMD_ADRC_FLAG_ENA; + } else + cmd.enable = AUDPP_CMD_ADRC_FLAG_DIS; + + /*order the writes to mbadrc */ + dma_coherent_pre_ops(); + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_mbadrc); + +int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_qconcert * + qconcert_plus) +{ + audpp_cmd_cfg_object_params_qconcert cmd; + if (id != 6) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_QCONCERT; + + if (enable) { + memcpy(&cmd.op_mode, &qconcert_plus->op_mode, + sizeof(audpp_cmd_cfg_object_params_qconcert) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)); + cmd.enable_flag = AUDPP_CMD_ADRC_FLAG_ENA; + } else + cmd.enable_flag = AUDPP_CMD_ADRC_FLAG_DIS; + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +int audpp_dsp_set_rx_iir(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_pcm *iir) +{ + audpp_cmd_cfg_object_params_pcm cmd; + + if (id != 6) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER; + + if (enable) { + cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA; + cmd.num_bands = iir->num_bands; + memcpy(&cmd.params_filter, &iir->params_filter, + sizeof(iir->params_filter)); + } else + cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS; + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_iir); + +int audpp_dsp_set_rx_srs_trumedia_g( + struct audpp_cmd_cfg_object_params_srstm_g *srstm) +{ + struct audpp_cmd_cfg_object_params_srstm_g cmd; + + MM_DBG("%s\n", __func__); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_SRS_PARAMS; + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_SRS_PARAMS_G; + + memcpy(cmd.v, srstm->v, sizeof(srstm->v)); + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_srs_trumedia_g); + +int audpp_dsp_set_rx_srs_trumedia_w( + struct audpp_cmd_cfg_object_params_srstm_w *srstm) +{ + struct audpp_cmd_cfg_object_params_srstm_w cmd; + + MM_DBG("%s\n", __func__); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_SRS_PARAMS; + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_SRS_PARAMS_W; + + memcpy(cmd.v, srstm->v, sizeof(srstm->v)); + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_srs_trumedia_w); + +int audpp_dsp_set_rx_srs_trumedia_c( + struct audpp_cmd_cfg_object_params_srstm_c *srstm) +{ + struct audpp_cmd_cfg_object_params_srstm_c cmd; + + MM_DBG("%s\n", __func__); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_SRS_PARAMS; + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_SRS_PARAMS_C; + + memcpy(cmd.v, srstm->v, sizeof(srstm->v)); + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_srs_trumedia_c); + +int audpp_dsp_set_rx_srs_trumedia_h( + struct audpp_cmd_cfg_object_params_srstm_h *srstm) +{ + struct audpp_cmd_cfg_object_params_srstm_h cmd; + + MM_DBG("%s\n", __func__); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_SRS_PARAMS; + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_SRS_PARAMS_H; + + memcpy(cmd.v, srstm->v, sizeof(srstm->v)); + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_srs_trumedia_h); + +int audpp_dsp_set_rx_srs_trumedia_p( + struct audpp_cmd_cfg_object_params_srstm_p *srstm) +{ + struct audpp_cmd_cfg_object_params_srstm_p cmd; + + MM_DBG("%s\n", __func__); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_SRS_PARAMS; + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_SRS_PARAMS_P; + + memcpy(cmd.v, srstm->v, sizeof(srstm->v)); + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_srs_trumedia_p); + +int audpp_dsp_set_rx_srs_trumedia_l( + struct audpp_cmd_cfg_object_params_srstm_l *srstm) +{ + struct audpp_cmd_cfg_object_params_srstm_l cmd; + + MM_DBG("%s\n", __func__); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_SRS_PARAMS; + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_SRS_PARAMS_L; + + memcpy(cmd.v, srstm->v, sizeof(srstm->v)); + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_srs_trumedia_l); + +/* Implementation Of COPP + POPP */ +int audpp_dsp_set_eq(unsigned id, unsigned enable, + audpp_cmd_cfg_object_params_eqalizer *eq) +{ + audpp_cmd_cfg_object_params_eqalizer cmd; + unsigned short *id_ptr = (unsigned short *)&cmd; + + if (id > 6 || id == 5) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + id_ptr[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_EQUALIZER; + + if (enable) { + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA; + cmd.num_bands = eq->num_bands; + memcpy(&cmd.eq_coeff, &eq->eq_coeff, sizeof(eq->eq_coeff)); + } else + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS; + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_eq); + +int audpp_dsp_set_vol_pan(unsigned id, + audpp_cmd_cfg_object_params_volume *vol_pan) +{ + audpp_cmd_cfg_object_params_volume cmd; + unsigned short *id_ptr = (unsigned short *)&cmd; + + if (id > 6) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + id_ptr[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_VOLUME_PAN; + + cmd.volume = vol_pan->volume; + cmd.pan = vol_pan->pan; + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_vol_pan); + +int audpp_pause(unsigned id, int pause) +{ + /* pause 1 = pause 0 = resume */ + u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)]; + + if (id >= CH_COUNT) + return -EINVAL; + + memset(pause_cmd, 0, sizeof(pause_cmd)); + + pause_cmd[0] = AUDPP_CMD_DEC_CTRL; + if (pause == 1) + pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V; + else if (pause == 0) + pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V; + else + return -EINVAL; + + return audpp_send_queue1(pause_cmd, sizeof(pause_cmd)); +} +EXPORT_SYMBOL(audpp_pause); + +int audpp_flush(unsigned id) +{ + u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)]; + + if (id >= CH_COUNT) + return -EINVAL; + + memset(flush_cmd, 0, sizeof(flush_cmd)); + + flush_cmd[0] = AUDPP_CMD_DEC_CTRL; + flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V; + + return audpp_send_queue1(flush_cmd, sizeof(flush_cmd)); +} +EXPORT_SYMBOL(audpp_flush); + +/* dec_attrb = 7:0, 0 - No Decoder, else supported decoder * + * like mp3, aac, wma etc ... * + * = 15:8, bit[8] = 1 - Tunnel, bit[9] = 1 - NonTunnel * + * = 31:16, reserved */ +int audpp_adec_alloc(unsigned dec_attrb, const char **module_name, + unsigned *queueid) +{ + struct audpp_state *audpp = &the_audpp_state; + int decid = -1, idx, lidx, mode, codec; + int codecs_supported, min_codecs_supported; + unsigned int *concurrency_entry; + mutex_lock(audpp->lock_dec); + /* Represents in bit mask */ + mode = ((dec_attrb & AUDPP_MODE_MASK) << 16); + codec = (1 << (dec_attrb & AUDPP_CODEC_MASK)); + /* Point to Last entry of the row */ + concurrency_entry = ((audpp->dec_database->dec_concurrency_table + + ((audpp->concurrency + 1) * + (audpp->dec_database->num_dec))) - 1); + + lidx = audpp->dec_database->num_dec; + min_codecs_supported = sizeof(unsigned int) * 8; + + MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec); + + for (idx = lidx; idx > 0; idx--, concurrency_entry--) { + if (!(audpp->dec_inuse & (1 << (idx - 1)))) { + if ((mode & *concurrency_entry) && + (codec & *concurrency_entry)) { + /* Check supports minimum number codecs */ + codecs_supported = + audpp->dec_database->dec_info_list[idx - + 1]. + nr_codec_support; + if (codecs_supported < min_codecs_supported) { + lidx = idx - 1; + min_codecs_supported = codecs_supported; + } + } + } + } + + if (lidx < audpp->dec_database->num_dec) { + audpp->dec_inuse |= (1 << lidx); + *module_name = + audpp->dec_database->dec_info_list[lidx].module_name; + *queueid = + audpp->dec_database->dec_info_list[lidx].module_queueid; + decid = audpp->dec_database->dec_info_list[lidx].module_decid; + audpp->dec_info_table[lidx].codec = + (dec_attrb & AUDPP_CODEC_MASK); + audpp->dec_info_table[lidx].pid = current->pid; + /* point to row to get supported operation */ + concurrency_entry = + ((audpp->dec_database->dec_concurrency_table + + ((audpp->concurrency) * (audpp->dec_database->num_dec))) + + lidx); + decid |= ((*concurrency_entry & AUDPP_OP_MASK) >> 12); + MM_INFO("decid =0x%08x module_name=%s, queueid=%d \n", + decid, *module_name, *queueid); + } + mutex_unlock(audpp->lock_dec); + return decid; + +} +EXPORT_SYMBOL(audpp_adec_alloc); + +void audpp_adec_free(int decid) +{ + struct audpp_state *audpp = &the_audpp_state; + int idx; + mutex_lock(audpp->lock_dec); + for (idx = audpp->dec_database->num_dec; idx > 0; idx--) { + if (audpp->dec_database->dec_info_list[idx - 1].module_decid == + decid) { + audpp->dec_inuse &= ~(1 << (idx - 1)); + audpp->dec_info_table[idx - 1].codec = -1; + audpp->dec_info_table[idx - 1].pid = 0; + MM_INFO("free decid =%d \n", decid); + break; + } + } + mutex_unlock(audpp->lock_dec); + return; + +} +EXPORT_SYMBOL(audpp_adec_free); + +static ssize_t concurrency_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct audpp_state *audpp = &the_audpp_state; + int rc; + mutex_lock(audpp->lock_dec); + rc = sprintf(buf, "%ld\n", audpp->concurrency); + mutex_unlock(audpp->lock_dec); + return rc; +} + +static ssize_t concurrency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct audpp_state *audpp = &the_audpp_state; + unsigned long concurrency; + int rc = -1; + mutex_lock(audpp->lock_dec); + if (audpp->dec_inuse) { + MM_ERR("Can not change profile, while playback in progress\n"); + goto done; + } + rc = strict_strtoul(buf, 10, &concurrency); + if (!rc && + (concurrency < audpp->dec_database->num_concurrency_support)) { + audpp->concurrency = concurrency; + MM_DBG("Concurrency case %ld\n", audpp->concurrency); + rc = count; + } else { + MM_ERR("Not a valid Concurrency case\n"); + rc = -EINVAL; + } +done: + mutex_unlock(audpp->lock_dec); + return rc; +} + +static ssize_t decoder_info_show(struct device *dev, + struct device_attribute *attr, char *buf); +static struct device_attribute dev_attr_decoder[AUDPP_MAX_DECODER_CNT] = { + __ATTR(decoder0, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder1, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder2, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder3, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder4, S_IRUGO, decoder_info_show, NULL), +}; + +static ssize_t decoder_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cpy_sz = 0; + struct audpp_state *audpp = &the_audpp_state; + const ptrdiff_t off = attr - dev_attr_decoder; /* decoder number */ + mutex_lock(audpp->lock_dec); + cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d:", + audpp->dec_info_table[off].codec); + cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d\n", + audpp->dec_info_table[off].pid); + mutex_unlock(audpp->lock_dec); + return cpy_sz; +} + +static DEVICE_ATTR(concurrency, S_IWUSR | S_IRUGO, concurrency_show, + concurrency_store); +static int audpp_probe(struct platform_device *pdev) +{ + int rc, idx; + struct audpp_state *audpp = &the_audpp_state; + audpp->concurrency = AUDPP_CONCURRENCY_DEFAULT; + audpp->dec_database = + (struct msm_adspdec_database *)pdev->dev.platform_data; + + MM_INFO("Number of decoder supported %d\n", + audpp->dec_database->num_dec); + MM_INFO("Number of concurrency supported %d\n", + audpp->dec_database->num_concurrency_support); + + init_waitqueue_head(&audpp->event_wait); + + spin_lock_init(&audpp->avsync_lock); + + for (idx = 0; idx < audpp->dec_database->num_dec; idx++) { + audpp->dec_info_table[idx].codec = -1; + audpp->dec_info_table[idx].pid = 0; + MM_INFO("module_name:%s\n", + audpp->dec_database->dec_info_list[idx].module_name); + MM_INFO("queueid:%d\n", + audpp->dec_database->dec_info_list[idx].module_queueid); + MM_INFO("decid:%d\n", + audpp->dec_database->dec_info_list[idx].module_decid); + MM_INFO("nr_codec_support:%d\n", + audpp->dec_database->dec_info_list[idx]. + nr_codec_support); + } + + for (idx = 0; idx < audpp->dec_database->num_dec; idx++) { + rc = device_create_file(&pdev->dev, &dev_attr_decoder[idx]); + if (rc) + goto err; + } + rc = device_create_file(&pdev->dev, &dev_attr_concurrency); + if (rc) + goto err; + else + goto done; +err: + while (idx--) + device_remove_file(&pdev->dev, &dev_attr_decoder[idx]); +done: + return rc; +} + +static struct platform_driver audpp_plat_driver = { + .probe = audpp_probe, + .driver = { + .name = "msm_adspdec", + .owner = THIS_MODULE, + }, +}; + +static int __init audpp_init(void) +{ + return platform_driver_register(&audpp_plat_driver); +} + +device_initcall(audpp_init); diff --git a/arch/arm/mach-msm/qdsp5/audpreproc.c b/arch/arm/mach-msm/qdsp5/audpreproc.c new file mode 100644 index 00000000000..230429f141d --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audpreproc.c @@ -0,0 +1,169 @@ +/* + * Common code to deal with the AUDPREPROC dsp task (audio preprocessing) + * + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(audpreproc_lock); + +struct msm_adspenc_info { + const char *module_name; + unsigned module_queueids; + int module_encid; /* streamid */ + int enc_formats; /* supported formats */ + int nr_codec_support; /* number of codec suported */ +}; + +#define ENC_MODULE_INFO(name, queueids, encid, formats, nr_codec) \ + {.module_name = name, .module_queueids = queueids, \ + .module_encid = encid, .enc_formats = formats, \ + .nr_codec_support = nr_codec } + +#ifdef CONFIG_MSM7X27A_AUDIO +#define ENC0_FORMAT ((1<lock); + /* Represents in bit mask */ + mode = ((enc_type & AUDPREPROC_MODE_MASK) << 16); + codec = (1 << (enc_type & AUDPREPROC_CODEC_MASK)); + + lidx = msm_enc_database.num_enc; + min_codecs_supported = sizeof(unsigned int) * 8; + MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec); + + for (idx = lidx-1; idx >= 0; idx--) { + /* encoder free and supports the format */ + if (!(audpreproc->enc_inuse & (1 << (idx))) && + ((mode & msm_enc_database.enc_info_list[idx].enc_formats) + == mode) && ((codec & + msm_enc_database.enc_info_list[idx].enc_formats) + == codec)){ + /* Check supports minimum number codecs */ + codecs_supported = + msm_enc_database.enc_info_list[idx].nr_codec_support; + if (codecs_supported < min_codecs_supported) { + lidx = idx; + min_codecs_supported = codecs_supported; + } + } + } + + if (lidx < msm_enc_database.num_enc) { + audpreproc->enc_inuse |= (1 << lidx); + *module_name = + msm_enc_database.enc_info_list[lidx].module_name; + *queue_ids = + msm_enc_database.enc_info_list[lidx].module_queueids; + encid = msm_enc_database.enc_info_list[lidx].module_encid; + } + + mutex_unlock(audpreproc->lock); + return encid; +} +EXPORT_SYMBOL(audpreproc_aenc_alloc); + +void audpreproc_aenc_free(int enc_id) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + int idx; + + mutex_lock(audpreproc->lock); + for (idx = 0; idx < msm_enc_database.num_enc; idx++) { + if (msm_enc_database.enc_info_list[idx].module_encid == + enc_id) { + audpreproc->enc_inuse &= ~(1 << idx); + break; + } + } + mutex_unlock(audpreproc->lock); + return; + +} +EXPORT_SYMBOL(audpreproc_aenc_free); diff --git a/arch/arm/mach-msm/qdsp5/audrec.c b/arch/arm/mach-msm/qdsp5/audrec.c new file mode 100644 index 00000000000..d5cb168b98e --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audrec.c @@ -0,0 +1,272 @@ +/* arch/arm/mach-msm/qdsp5/audrec.c + * + * common code to deal with the AUDREC dsp task (audio recording) + * + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "audmgr.h" +#include + +static DEFINE_MUTEX(audrec_lock); + +#define MAX_ENC_COUNT 8 /* Max encoder supported */ + +#define ENC_SESSION_FREE 0 +#define ENC_SESSION_ACTIVE 1 + +struct enc_session { + unsigned enc_type; /* Param to identify type of encoder */ + unsigned audrec_obj_idx; /* Param to identify REC_OBJ or Session ID */ + audrec_event_func event_func; /* Event Call back + routine for the encoder */ + void *private; /* private data element passed as + part of Event Call back routine */ + unsigned state; /* Current state of the encoder session , + free, active*/ +}; + +struct audrec_state { + struct msm_adsp_module *audrec_mod; + struct enc_session enc_session[MAX_ENC_COUNT]; + struct mutex *lock; + unsigned enc_count; +}; + +struct audrec_state the_audrec_state = { + .lock = &audrec_lock, +}; + +int audrectask_send_cmdqueue(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audrec_state.audrec_mod, + QDSP_uPAudRecCmdQueue, cmd, len); +} +EXPORT_SYMBOL(audrectask_send_cmdqueue); + +int audrectask_send_bitstreamqueue(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audrec_state.audrec_mod, + QDSP_uPAudRecBitStreamQueue, cmd, len); +} +EXPORT_SYMBOL(audrectask_send_bitstreamqueue); + +static void audrectask_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audrec_state *audrec = data; + int cnt; + uint16_t msg[5]; /* Max size of message */ + getevent(msg, len); + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: { + MM_DBG("CMD CFG DONE %x\n", msg[1]); + if (msg[0] & AUDREC_MSG_CFG_DONE_ENC_ENA) { + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].enc_type == + (msg[0] & AUDREC_CMD_ENC_TYPE_MASK)) { + audrec->enc_session[cnt].audrec_obj_idx + = msg[1]; + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, + msg); + break; + } + } + } else { + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].enc_type == + (msg[0] & AUDREC_CMD_ENC_TYPE_MASK)) { + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, + msg); + audrec->enc_session[cnt].audrec_obj_idx + = 0xFFFFFFFF; + audrec->enc_session[cnt].state + = ENC_SESSION_FREE; + audrec->enc_session[cnt].enc_type + = 0xFFFFFFFF; + audrec->enc_session[cnt].event_func + = NULL; + audrec->enc_session[cnt].private + = NULL; + break; + } + } + } + break; + } + case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: { + MM_DBG("CMD AREC MEM CFG DONE %x\n", msg[0]); + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].audrec_obj_idx == + msg[0]) { + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, msg); + break; + } + } + break; + } + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD AREC PARAM CFG DONE %x\n", msg[0]); + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].audrec_obj_idx == + msg[0]) { + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, msg); + break; + } + } + break; + } + case AUDREC_MSG_PACKET_READY_MSG: { + MM_DBG("PCK READY %x\n", msg[0]); + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].audrec_obj_idx == + msg[0]) { + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, msg); + break; + } + } + break; + } + case AUDREC_MSG_FATAL_ERR_MSG: { + MM_ERR("ERROR %x\n", msg[0]); + if (msg[1] & AUDREC_MSG_FATAL_ERR_TYPE_0) { + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].audrec_obj_idx == + msg[0]) { + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, + msg); + break; + } + } + } else if (msg[1] & AUDREC_MSG_FATAL_ERR_TYPE_1) { + cnt = audrec->enc_count-1; + if (audrec->enc_session[cnt].event_func) + audrec->enc_session[cnt].event_func( + audrec->enc_session[cnt].private, id, + msg); + } + break; + } + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module \ + enable/disable(audrectask)\n"); + break; + default: + MM_ERR("unknown event %d\n", id); + } +} + +static struct msm_adsp_ops adsp_ops = { + .event = audrectask_dsp_event, +}; + +int audrectask_enable(unsigned enc_type, audrec_event_func func, void *private) +{ + struct audrec_state *audrec = &the_audrec_state; + int cnt, rc = 0; + + mutex_lock(audrec->lock); + + if (audrec->enc_count++ == 0) { + MM_DBG("enable\n"); + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].state == + ENC_SESSION_FREE) { + audrec->enc_session[cnt].state = + ENC_SESSION_ACTIVE; + audrec->enc_session[cnt].enc_type = enc_type; + audrec->enc_session[cnt].event_func = func; + audrec->enc_session[cnt].private = private; + break; + } + } + rc = msm_adsp_get("AUDRECTASK", &audrec->audrec_mod, &adsp_ops, + audrec); + if (rc < 0) { + MM_ERR("cannot open AUDRECTASK\n"); + audrec->enc_count = 0; + audrec->enc_session[cnt].state = ENC_SESSION_FREE; + audrec->enc_session[cnt].enc_type = 0xFFFFFFFF; + audrec->enc_session[cnt].event_func = NULL; + audrec->enc_session[cnt].private = NULL; + goto out; + } + msm_adsp_enable(audrec->audrec_mod); + } else { + for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) { + if (audrec->enc_session[cnt].state == + ENC_SESSION_FREE) { + audrec->enc_session[cnt].state = + ENC_SESSION_ACTIVE; + audrec->enc_session[cnt].enc_type = enc_type; + audrec->enc_session[cnt].event_func = func; + audrec->enc_session[cnt].private = private; + break; + } + } + } + if (cnt == MAX_ENC_COUNT) + rc = -EBUSY; + else + rc = 0; + +out: + mutex_unlock(audrec->lock); + return rc; +} +EXPORT_SYMBOL(audrectask_enable); + +void audrectask_disable(unsigned enc_type, void *private) +{ + struct audrec_state *audrec = &the_audrec_state; + + mutex_lock(audrec->lock); + + if (--audrec->enc_count == 0) { + MM_DBG("\n"); /* Macro prints the file name and function */ + msm_adsp_disable(audrec->audrec_mod); + msm_adsp_put(audrec->audrec_mod); + audrec->audrec_mod = NULL; + } + + mutex_unlock(audrec->lock); +} +EXPORT_SYMBOL(audrectask_disable); + diff --git a/arch/arm/mach-msm/qdsp5/dsp_debug.c b/arch/arm/mach-msm/qdsp5/dsp_debug.c new file mode 100644 index 00000000000..331ba00cad5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/dsp_debug.c @@ -0,0 +1,235 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dsp_debug.h" + +static wait_queue_head_t dsp_wait; +static int dsp_has_crashed; +static int dsp_wait_count; + +static atomic_t dsp_crash_count = ATOMIC_INIT(0); +static dsp_state_cb cb_ptr; + +#define MAX_LEN 64 +#define HDR_LEN 20 +#define NUM_DSP_RAM_BANKS 3 + +static char l_buf[MAX_LEN]; +#ifdef CONFIG_DEBUG_FS +static struct dentry *dsp_dentry; +#endif + +void q5audio_dsp_not_responding(void) +{ + if (cb_ptr) + cb_ptr(DSP_STATE_CRASHED); + + MM_DBG("entered q5audio_dsp_not_responding\n"); + if (atomic_add_return(1, &dsp_crash_count) != 1) { + MM_ERR("q5audio_dsp_not_responding() \ + - parking additional crasher...\n"); + for (;;) + msleep(1000); + } + if (dsp_wait_count) { + dsp_has_crashed = 1; + wake_up(&dsp_wait); + + while (dsp_has_crashed != 2) + wait_event(dsp_wait, dsp_has_crashed == 2); + } else { + MM_ERR("q5audio_dsp_not_responding() - no waiter?\n"); + } + + if (cb_ptr) + cb_ptr(DSP_STATE_CRASH_DUMP_DONE); +} + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t dsp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + if (!strncmp(cmd, "wait-for-crash", sizeof("wait-for-crash"))) { + while (!dsp_has_crashed) { + int res; + dsp_wait_count++; + res = wait_event_interruptible(dsp_wait, + dsp_has_crashed); + if (res < 0) { + dsp_wait_count--; + return res; + } + } + } else if (!strncmp(cmd, "boom", sizeof("boom"))) { + q5audio_dsp_not_responding(); + } else if (!strncmp(cmd, "continue-crash", sizeof("continue-crash"))) { + dsp_has_crashed = 2; + wake_up(&dsp_wait); + } else { + MM_ERR("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__, + __func__, cmd); + } + + return count; +} + +static ssize_t dsp_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + size_t actual = 0; + static void *dsp_addr; + static unsigned copy_ok_count; + + MM_INFO("pos = %lld\n", *pos); + if (*pos >= DSP_RAM_SIZE * NUM_DSP_RAM_BANKS) + return 0; + + if (*pos == 0) + dsp_addr = (*pos + RAMA_BASE); + else if (*pos == DSP_RAM_SIZE) + dsp_addr = RAMB_BASE; + else if (*pos >= DSP_RAM_SIZE * 2) + dsp_addr = RAMC_BASE; + + MM_INFO("dsp_addr = %p\n", dsp_addr); + while (count >= PAGE_SIZE) { + if (copy_to_user(buf, dsp_addr, PAGE_SIZE)) { + MM_ERR("[%s:%s] copy error @ %p\n", __MM_FILE__, + __func__, buf); + return -EFAULT; + } + copy_ok_count += PAGE_SIZE; + dsp_addr = (char *)dsp_addr + PAGE_SIZE; + buf += PAGE_SIZE; + actual += PAGE_SIZE; + count -= PAGE_SIZE; + } + + *pos += actual; + return actual; +} + +static int dsp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +int dsp_debug_register(dsp_state_cb ptr) +{ + if (ptr == NULL) + return -EINVAL; + + cb_ptr = ptr; + + return 0; +} + +static const struct file_operations dsp_fops = { + .owner = THIS_MODULE, + .open = dsp_open, + .read = dsp_read, + .write = dsp_write, + .release = dsp_release, +}; + +#ifdef CONFIG_DEBUG_FS +static struct miscdevice dsp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dsp_debug", + .fops = &dsp_fops, +}; +#endif + +static ssize_t dsp_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_DBG("adsp debugfs opened\n"); + return 0; +} + +static ssize_t dsp_debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int len; + + if (count < 0) + return 0; + len = count > (MAX_LEN - 1) ? (MAX_LEN - 1) : count; + if (copy_from_user(l_buf + HDR_LEN, buf, len)) { + MM_ERR("Unable to copy data from user space\n"); + return -EFAULT; + } + l_buf[len + HDR_LEN] = 0; + if (l_buf[len + HDR_LEN - 1] == '\n') { + l_buf[len + HDR_LEN - 1] = 0; + len--; + } + if (!strncmp(l_buf + HDR_LEN, "boom", 64)) { + q5audio_dsp_not_responding(); + } else if (!strncmp(l_buf + HDR_LEN, "continue-crash", + sizeof("continue-crash"))) { + dsp_has_crashed = 2; + wake_up(&dsp_wait); + } else + MM_ERR("Unknown command\n"); + + return count; +} +static const struct file_operations dsp_debug_fops = { + .write = dsp_debug_write, + .open = dsp_debug_open, +}; + +static int __init dsp_init(void) +{ + init_waitqueue_head(&dsp_wait); +#ifdef CONFIG_DEBUG_FS + dsp_dentry = debugfs_create_file("dsp_debug", S_IFREG | S_IRUGO, + NULL, (void *) NULL, &dsp_debug_fops); + + return misc_register(&dsp_misc); +#else + return 0; +#endif /* CONFIG_DEBUG_FS */ +} + +device_initcall(dsp_init); diff --git a/arch/arm/mach-msm/qdsp5/dsp_debug.h b/arch/arm/mach-msm/qdsp5/dsp_debug.h new file mode 100644 index 00000000000..bd40682c9ce --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/dsp_debug.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DSP_DEBUG_H_ +#define __DSP_DEBUG_H_ + +typedef int (*dsp_state_cb)(int state); +int dsp_debug_register(dsp_state_cb ptr); + +#define DSP_STATE_CRASHED 0x0 +#define DSP_STATE_CRASH_DUMP_DONE 0x1 + +#define RAMA_BASE MSM_AD5_BASE +#define RAMB_BASE ((RAMA_BASE) + (0x200000)) +#define RAMC_BASE ((RAMB_BASE) + (0x200000)) +#define DSP_RAM_SIZE 0x40000 + +#endif diff --git a/arch/arm/mach-msm/qdsp5/evlog.h b/arch/arm/mach-msm/qdsp5/evlog.h new file mode 100644 index 00000000000..1f0f16bada2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/evlog.h @@ -0,0 +1,125 @@ +/* arch/arm/mach-msm/qdsp5/evlog.h + * + * simple event log debugging facility + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#define EV_LOG_ENTRY_NAME(n) n##_entry + +#define DECLARE_LOG(_name, _size, _str) \ +static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \ +static struct ev_log _name = { \ + .name = #_name, \ + .strings = _str, \ + .num_strings = ARRAY_SIZE(_str), \ + .entry = EV_LOG_ENTRY_NAME(_name), \ + .max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \ +} + +struct ev_entry { + struct timespec when; + uint32_t id; + uint32_t arg; +}; + +struct ev_log { + struct ev_entry *entry; + unsigned max; + unsigned next; + unsigned fault; + const char **strings; + unsigned num_strings; + const char *name; +}; + +static char ev_buf[4096]; + +static ssize_t ev_log_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct ev_log *log = file->private_data; + struct ev_entry *entry; + unsigned long flags; + int size = 0; + unsigned n, id, max; + struct timespec now, t; + + max = log->max; + getnstimeofday(&now); + local_irq_save(flags); + n = (log->next - 1) & (max - 1); + entry = log->entry; + while (n != log->next) { + t = timespec_sub(now, entry[n].when); + id = entry[n].id; + if (id) { + const char *str; + if (id < log->num_strings) + str = log->strings[id]; + else + str = "UNKNOWN"; + size += scnprintf(ev_buf + size, 4096 - size, + "%lu.%03lu %08x %s\n", + t.tv_sec, t.tv_nsec / 1000000, + entry[n].arg, str); + } + n = (n - 1) & (max - 1); + } + log->fault = 0; + local_irq_restore(flags); + return simple_read_from_buffer(buf, count, ppos, ev_buf, size); +} + +static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg) +{ + struct ev_entry *entry; + unsigned long flags; + local_irq_save(flags); + + if (log->fault) { + if (log->fault == 1) + goto done; + log->fault--; + } + + entry = log->entry + log->next; + getnstimeofday(&entry->when); + entry->id = id; + entry->arg = arg; + log->next = (log->next + 1) & (log->max - 1); +done: + local_irq_restore(flags); +} + +static int ev_log_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations ev_log_ops = { + .read = ev_log_read, + .open = ev_log_open, +}; + +static int ev_log_init(struct ev_log *log) +{ + debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops); + return 0; +} + diff --git a/arch/arm/mach-msm/qdsp5/snd.c b/arch/arm/mach-msm/qdsp5/snd.c new file mode 100644 index 00000000000..f1db012b0a0 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/snd.c @@ -0,0 +1,675 @@ +/* arch/arm/mach-msm/qdsp5/snd.c + * + * interface to "snd" service on the baseband cpu + * + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_ctxt { + struct mutex lock; + int opened; + struct msm_rpc_endpoint *ept; + struct msm_snd_endpoints *snd_epts; +}; + +struct snd_sys_ctxt { + struct mutex lock; + struct msm_rpc_endpoint *ept; +}; + +static struct snd_sys_ctxt the_snd_sys; + +static struct snd_ctxt the_snd; + +#define RPC_SND_PROG 0x30000002 +#define RPC_SND_CB_PROG 0x31000002 + +#define RPC_SND_VERS 0x00020001 +#define RPC_SND_VERS2 0x00030001 + +#define SND_SET_DEVICE_PROC 2 +#define SND_SET_VOLUME_PROC 3 +#define SND_AVC_CTL_PROC 29 +#define SND_AGC_CTL_PROC 30 + +struct rpc_snd_set_device_args { + uint32_t device; + uint32_t ear_mute; + uint32_t mic_mute; + + uint32_t cb_func; + uint32_t client_data; +}; + +struct rpc_snd_set_volume_args { + uint32_t device; + uint32_t method; + uint32_t volume; + + uint32_t cb_func; + uint32_t client_data; +}; + +struct rpc_snd_avc_ctl_args { + uint32_t avc_ctl; + uint32_t cb_func; + uint32_t client_data; +}; + +struct rpc_snd_agc_ctl_args { + uint32_t agc_ctl; + uint32_t cb_func; + uint32_t client_data; +}; + +struct snd_set_device_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_set_device_args args; +}; + +struct snd_set_volume_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_set_volume_args args; +}; + +struct snd_avc_ctl_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_avc_ctl_args args; +}; + +struct snd_agc_ctl_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_agc_ctl_args args; +}; + +struct snd_endpoint *get_snd_endpoints(int *size); + +static inline int check_mute(int mute) +{ + return (mute == SND_MUTE_MUTED || + mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL; +} + +static int get_endpoint(struct snd_ctxt *snd, unsigned long arg) +{ + int rc = 0, index; + struct msm_snd_endpoint ept; + + if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) { + MM_ERR("snd_ioctl get endpoint: invalid read pointer\n"); + return -EFAULT; + } + + index = ept.id; + if (index < 0 || index >= snd->snd_epts->num) { + MM_ERR("snd_ioctl get endpoint: invalid index!\n"); + return -EINVAL; + } + + ept.id = snd->snd_epts->endpoints[index].id; + strncpy(ept.name, + snd->snd_epts->endpoints[index].name, + sizeof(ept.name)); + + if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) { + MM_ERR("snd_ioctl get endpoint: invalid write pointer\n"); + rc = -EFAULT; + } + + return rc; +} + +static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct snd_set_device_msg dmsg; + struct snd_set_volume_msg vmsg; + struct snd_avc_ctl_msg avc_msg; + struct snd_agc_ctl_msg agc_msg; + + struct msm_snd_device_config dev; + struct msm_snd_volume_config vol; + struct snd_ctxt *snd = file->private_data; + int rc = 0; + + uint32_t avc, agc; + + mutex_lock(&snd->lock); + switch (cmd) { + case SND_SET_DEVICE: + if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) { + MM_ERR("set device: invalid pointer\n"); + rc = -EFAULT; + break; + } + + dmsg.args.device = cpu_to_be32(dev.device); + dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute); + dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute); + if (check_mute(dev.ear_mute) < 0 || + check_mute(dev.mic_mute) < 0) { + MM_ERR("set device: invalid mute status\n"); + rc = -EINVAL; + break; + } + dmsg.args.cb_func = -1; + dmsg.args.client_data = 0; + + MM_INFO("snd_set_device %d %d %d\n", dev.device, + dev.ear_mute, dev.mic_mute); + + rc = msm_rpc_call(snd->ept, + SND_SET_DEVICE_PROC, + &dmsg, sizeof(dmsg), 5 * HZ); + break; + + case SND_SET_VOLUME: + if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) { + MM_ERR("set volume: invalid pointer\n"); + rc = -EFAULT; + break; + } + + vmsg.args.device = cpu_to_be32(vol.device); + vmsg.args.method = cpu_to_be32(vol.method); + if (vol.method != SND_METHOD_VOICE) { + MM_ERR("set volume: invalid method\n"); + rc = -EINVAL; + break; + } + + vmsg.args.volume = cpu_to_be32(vol.volume); + vmsg.args.cb_func = -1; + vmsg.args.client_data = 0; + + MM_INFO("snd_set_volume %d %d %d\n", vol.device, + vol.method, vol.volume); + + rc = msm_rpc_call(snd->ept, + SND_SET_VOLUME_PROC, + &vmsg, sizeof(vmsg), 5 * HZ); + break; + + case SND_AVC_CTL: + if (get_user(avc, (uint32_t __user *) arg)) { + rc = -EFAULT; + break; + } else if ((avc != 1) && (avc != 0)) { + rc = -EINVAL; + break; + } + + avc_msg.args.avc_ctl = cpu_to_be32(avc); + avc_msg.args.cb_func = -1; + avc_msg.args.client_data = 0; + + MM_INFO("snd_avc_ctl %d\n", avc); + + rc = msm_rpc_call(snd->ept, + SND_AVC_CTL_PROC, + &avc_msg, sizeof(avc_msg), 5 * HZ); + break; + + case SND_AGC_CTL: + if (get_user(agc, (uint32_t __user *) arg)) { + rc = -EFAULT; + break; + } else if ((agc != 1) && (agc != 0)) { + rc = -EINVAL; + break; + } + agc_msg.args.agc_ctl = cpu_to_be32(agc); + agc_msg.args.cb_func = -1; + agc_msg.args.client_data = 0; + + MM_INFO("snd_agc_ctl %d\n", agc); + + rc = msm_rpc_call(snd->ept, + SND_AGC_CTL_PROC, + &agc_msg, sizeof(agc_msg), 5 * HZ); + break; + + case SND_GET_NUM_ENDPOINTS: + if (copy_to_user((void __user *)arg, + &snd->snd_epts->num, sizeof(unsigned))) { + MM_ERR("get endpoint: invalid pointer\n"); + rc = -EFAULT; + } + break; + + case SND_GET_ENDPOINT: + rc = get_endpoint(snd, arg); + break; + + default: + MM_ERR("unknown command\n"); + rc = -EINVAL; + break; + } + mutex_unlock(&snd->lock); + + return rc; +} + +static int snd_release(struct inode *inode, struct file *file) +{ + struct snd_ctxt *snd = file->private_data; + int rc; + + mutex_lock(&snd->lock); + rc = msm_rpc_close(snd->ept); + if (rc < 0) + MM_ERR("msm_rpc_close failed\n"); + snd->ept = NULL; + snd->opened = 0; + mutex_unlock(&snd->lock); + return 0; +} +static int snd_sys_release(void) +{ + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + mutex_lock(&snd_sys->lock); + rc = msm_rpc_close(snd_sys->ept); + if (rc < 0) + MM_ERR("msm_rpc_close failed\n"); + snd_sys->ept = NULL; + mutex_unlock(&snd_sys->lock); + return rc; +} +static int snd_open(struct inode *inode, struct file *file) +{ + struct snd_ctxt *snd = &the_snd; + int rc = 0; + + mutex_lock(&snd->lock); + if (snd->opened == 0) { + if (snd->ept == NULL) { + snd->ept = msm_rpc_connect_compatible(RPC_SND_PROG, + RPC_SND_VERS, 0); + if (IS_ERR(snd->ept)) { + MM_DBG("connect failed with current VERS \ + = %x, trying again with another API\n", + RPC_SND_VERS2); + snd->ept = + msm_rpc_connect_compatible(RPC_SND_PROG, + RPC_SND_VERS2, 0); + } + if (IS_ERR(snd->ept)) { + rc = PTR_ERR(snd->ept); + snd->ept = NULL; + MM_ERR("failed to connect snd svc\n"); + goto err; + } + } + file->private_data = snd; + snd->opened = 1; + } else { + MM_ERR("snd already opened\n"); + rc = -EBUSY; + } + +err: + mutex_unlock(&snd->lock); + return rc; +} +static int snd_sys_open(void) +{ + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + mutex_lock(&snd_sys->lock); + if (snd_sys->ept == NULL) { + snd_sys->ept = msm_rpc_connect_compatible(RPC_SND_PROG, + RPC_SND_VERS, 0); + if (IS_ERR(snd_sys->ept)) { + MM_DBG("connect failed with current VERS \ + = %x, trying again with another API\n", + RPC_SND_VERS2); + snd_sys->ept = msm_rpc_connect_compatible(RPC_SND_PROG, + RPC_SND_VERS2, 0); + } + if (IS_ERR(snd_sys->ept)) { + rc = PTR_ERR(snd_sys->ept); + snd_sys->ept = NULL; + MM_ERR("failed to connect snd svc\n"); + goto err; + } + } else + MM_DBG("snd already opened\n"); + +err: + mutex_unlock(&snd_sys->lock); + return rc; +} + +static struct file_operations snd_fops = { + .owner = THIS_MODULE, + .open = snd_open, + .release = snd_release, + .unlocked_ioctl = snd_ioctl, +}; + +struct miscdevice snd_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_snd", + .fops = &snd_fops, +}; + +static long snd_agc_enable(unsigned long arg) +{ + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + struct snd_agc_ctl_msg agc_msg; + int rc = 0; + + if ((arg != 1) && (arg != 0)) + return -EINVAL; + + agc_msg.args.agc_ctl = cpu_to_be32(arg); + agc_msg.args.cb_func = -1; + agc_msg.args.client_data = 0; + + MM_DBG("snd_agc_ctl %ld,%d\n", arg, agc_msg.args.agc_ctl); + + rc = msm_rpc_call(snd_sys->ept, + SND_AGC_CTL_PROC, + &agc_msg, sizeof(agc_msg), 5 * HZ); + return rc; +} + +static long snd_avc_enable(unsigned long arg) +{ + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + struct snd_avc_ctl_msg avc_msg; + int rc = 0; + + if ((arg != 1) && (arg != 0)) + return -EINVAL; + + avc_msg.args.avc_ctl = cpu_to_be32(arg); + + avc_msg.args.cb_func = -1; + avc_msg.args.client_data = 0; + + MM_DBG("snd_avc_ctl %ld,%d\n", arg, avc_msg.args.avc_ctl); + + rc = msm_rpc_call(snd_sys->ept, + SND_AVC_CTL_PROC, + &avc_msg, sizeof(avc_msg), 5 * HZ); + return rc; +} + +static ssize_t snd_agc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + ssize_t status; + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + rc = snd_sys_open(); + if (rc) + return rc; + + mutex_lock(&snd_sys->lock); + + if (sysfs_streq(buf, "enable")) + status = snd_agc_enable(1); + else if (sysfs_streq(buf, "disable")) + status = snd_agc_enable(0); + else + status = -EINVAL; + + mutex_unlock(&snd_sys->lock); + rc = snd_sys_release(); + if (rc) + return rc; + + return status ? : size; +} + +static ssize_t snd_avc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + ssize_t status; + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + rc = snd_sys_open(); + if (rc) + return rc; + + mutex_lock(&snd_sys->lock); + + if (sysfs_streq(buf, "enable")) + status = snd_avc_enable(1); + else if (sysfs_streq(buf, "disable")) + status = snd_avc_enable(0); + else + status = -EINVAL; + + mutex_unlock(&snd_sys->lock); + rc = snd_sys_release(); + if (rc) + return rc; + + return status ? : size; +} + +static long snd_vol_enable(const char *arg) +{ + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + struct snd_set_volume_msg vmsg; + struct msm_snd_volume_config vol; + int rc = 0; + + rc = sscanf(arg, "%d %d %d", &vol.device, &vol.method, &vol.volume); + if (rc != 3) { + MM_ERR("Invalid arguments. Usage: \ + \n"); + rc = -EINVAL; + return rc; + } + + vmsg.args.device = cpu_to_be32(vol.device); + vmsg.args.method = cpu_to_be32(vol.method); + if (vol.method != SND_METHOD_VOICE) { + MM_ERR("snd_ioctl set volume: invalid method\n"); + rc = -EINVAL; + return rc; + } + + vmsg.args.volume = cpu_to_be32(vol.volume); + vmsg.args.cb_func = -1; + vmsg.args.client_data = 0; + + MM_DBG("snd_set_volume %d %d %d\n", vol.device, vol.method, + vol.volume); + + rc = msm_rpc_call(snd_sys->ept, + SND_SET_VOLUME_PROC, + &vmsg, sizeof(vmsg), 5 * HZ); + return rc; +} + +static long snd_dev_enable(const char *arg) +{ + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + struct snd_set_device_msg dmsg; + struct msm_snd_device_config dev; + int rc = 0; + + rc = sscanf(arg, "%d %d %d", &dev.device, &dev.ear_mute, &dev.mic_mute); + if (rc != 3) { + MM_ERR("Invalid arguments. Usage: \ + \n"); + rc = -EINVAL; + return rc; + } + dmsg.args.device = cpu_to_be32(dev.device); + dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute); + dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute); + if (check_mute(dev.ear_mute) < 0 || + check_mute(dev.mic_mute) < 0) { + MM_ERR("snd_ioctl set device: invalid mute status\n"); + rc = -EINVAL; + return rc; + } + dmsg.args.cb_func = -1; + dmsg.args.client_data = 0; + + MM_INFO("snd_set_device %d %d %d\n", dev.device, dev.ear_mute, + dev.mic_mute); + + rc = msm_rpc_call(snd_sys->ept, + SND_SET_DEVICE_PROC, + &dmsg, sizeof(dmsg), 5 * HZ); + return rc; +} + +static ssize_t snd_dev_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + ssize_t status; + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + rc = snd_sys_open(); + if (rc) + return rc; + + mutex_lock(&snd_sys->lock); + status = snd_dev_enable(buf); + mutex_unlock(&snd_sys->lock); + + rc = snd_sys_release(); + if (rc) + return rc; + + return status ? : size; +} + +static ssize_t snd_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + ssize_t status; + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + rc = snd_sys_open(); + if (rc) + return rc; + + mutex_lock(&snd_sys->lock); + status = snd_vol_enable(buf); + mutex_unlock(&snd_sys->lock); + + rc = snd_sys_release(); + if (rc) + return rc; + + return status ? : size; +} + +static DEVICE_ATTR(agc, S_IWUSR | S_IRUGO, + NULL, snd_agc_store); + +static DEVICE_ATTR(avc, S_IWUSR | S_IRUGO, + NULL, snd_avc_store); + +static DEVICE_ATTR(device, S_IWUSR | S_IRUGO, + NULL, snd_dev_store); + +static DEVICE_ATTR(volume, S_IWUSR | S_IRUGO, + NULL, snd_vol_store); + +static int snd_probe(struct platform_device *pdev) +{ + struct snd_ctxt *snd = &the_snd; + struct snd_sys_ctxt *snd_sys = &the_snd_sys; + int rc = 0; + + mutex_init(&snd->lock); + mutex_init(&snd_sys->lock); + snd_sys->ept = NULL; + snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data; + rc = misc_register(&snd_misc); + if (rc) + return rc; + + rc = device_create_file(snd_misc.this_device, &dev_attr_agc); + if (rc) { + misc_deregister(&snd_misc); + return rc; + } + + rc = device_create_file(snd_misc.this_device, &dev_attr_avc); + if (rc) { + device_remove_file(snd_misc.this_device, + &dev_attr_agc); + misc_deregister(&snd_misc); + return rc; + } + + rc = device_create_file(snd_misc.this_device, &dev_attr_device); + if (rc) { + device_remove_file(snd_misc.this_device, + &dev_attr_agc); + device_remove_file(snd_misc.this_device, + &dev_attr_avc); + misc_deregister(&snd_misc); + return rc; + } + + rc = device_create_file(snd_misc.this_device, &dev_attr_volume); + if (rc) { + device_remove_file(snd_misc.this_device, + &dev_attr_agc); + device_remove_file(snd_misc.this_device, + &dev_attr_avc); + device_remove_file(snd_misc.this_device, + &dev_attr_device); + misc_deregister(&snd_misc); + } + + return rc; +} + +static struct platform_driver snd_plat_driver = { + .probe = snd_probe, + .driver = { + .name = "msm_snd", + .owner = THIS_MODULE, + }, +}; + +static int __init snd_init(void) +{ + return platform_driver_register(&snd_plat_driver); +} + +module_init(snd_init); diff --git a/arch/arm/mach-msm/qdsp5/snd_adie.c b/arch/arm/mach-msm/qdsp5/snd_adie.c new file mode 100644 index 00000000000..ba7efc30f2f --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/snd_adie.c @@ -0,0 +1,480 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct adie_svc_client adie_client[ADIE_SVC_MAX_CLIENTS]; +static DEFINE_MUTEX(adie_client_lock); + +static int adie_svc_process_cb(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + int rc, id; + uint32_t accept_status; + struct rpc_request_hdr *req; + struct adie_svc_client_register_cb_cb_args arg, *buf_ptr; + + req = (struct rpc_request_hdr *)buffer; + for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) { + if (adie_client[id].rpc_client == client) + break; + } + if (id == ADIE_SVC_MAX_CLIENTS) { + MM_ERR("RPC reply with invalid rpc client\n"); + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto err; + } + + buf_ptr = (struct adie_svc_client_register_cb_cb_args *)(req + 1); + arg.cb_id = be32_to_cpu(buf_ptr->cb_id); + arg.size = be32_to_cpu(buf_ptr->size); + arg.client_id = be32_to_cpu(buf_ptr->client_id); + arg.adie_block = be32_to_cpu(buf_ptr->adie_block); + arg.status = be32_to_cpu(buf_ptr->status); + arg.client_operation = be32_to_cpu(buf_ptr->client_operation); + + if (arg.cb_id != adie_client[id].cb_id) { + MM_ERR("RPC reply with invalid invalid cb_id\n"); + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto err; + } + + mutex_lock(&adie_client[id].lock); + switch (arg.client_operation) { + case ADIE_SVC_REGISTER_CLIENT: + MM_DBG("ADIE_SVC_REGISTER_CLIENT callback\n"); + adie_client[id].client_id = arg.client_id; + break; + case ADIE_SVC_DEREGISTER_CLIENT: + MM_DBG("ADIE_SVC_DEREGISTER_CLIENT callback\n"); + break; + case ADIE_SVC_CONFIG_ADIE_BLOCK: + MM_DBG("ADIE_SVC_CONFIG_ADIE_BLOCK callback\n"); + if (adie_client[id].client_id != arg.client_id) { + mutex_unlock(&adie_client[id].lock); + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto err; + } + break; + default: + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto err; + } + + adie_client[id].status = arg.status; + adie_client[id].adie_svc_cb_done = 1; + mutex_unlock(&adie_client[id].lock); + wake_up(&adie_client[id].wq); + accept_status = RPC_ACCEPTSTAT_SUCCESS; + +err: + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + accept_status); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) + MM_ERR("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int adie_svc_rpc_cb_func(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + int rc = 0; + struct rpc_request_hdr *req; + + req = (struct rpc_request_hdr *)buffer; + + MM_DBG("procedure received to rpc cb %d\n", + be32_to_cpu(req->procedure)); + switch (be32_to_cpu(req->procedure)) { + case ADIE_SVC_CLIENT_STATUS_FUNC_PTR_TYPE_PROC: + rc = adie_svc_process_cb(client, buffer, in_size); + break; + default: + MM_ERR("%s: procedure not supported %d\n", __func__, + be32_to_cpu(req->procedure)); + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + RPC_ACCEPTSTAT_PROC_UNAVAIL); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) + MM_ERR("%s: sending reply failed: %d\n", __func__, rc); + break; + } + return rc; +} + +static int adie_svc_client_register_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct adie_svc_client_register_cb_args *arg; + + arg = (struct adie_svc_client_register_cb_args *)data; + + *((int *)buf) = cpu_to_be32((int)arg->cb_id); + return sizeof(int); +} + +static int adie_svc_client_deregister_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct adie_svc_client_deregister_cb_args *arg; + + arg = (struct adie_svc_client_deregister_cb_args *)data; + + *((int *)buf) = cpu_to_be32(arg->client_id); + return sizeof(int); +} + +static int adie_svc_config_adie_block_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct adie_svc_config_adie_block_cb_args *arg; + int size = 0; + + arg = (struct adie_svc_config_adie_block_cb_args *)data; + + *((int *)buf) = cpu_to_be32(arg->client_id); + size += sizeof(int); + buf += sizeof(int); + + *((int *)buf) = cpu_to_be32(arg->adie_block); + size += sizeof(int); + buf += sizeof(int); + + *((int *)buf) = cpu_to_be32(arg->config); + size += sizeof(int); + + return size; +} + +/* Returns : client id on success + * and -1 on failure + */ +int adie_svc_get(void) +{ + int id, rc = 0; + struct adie_svc_client_register_cb_args arg; + + mutex_lock(&adie_client_lock); + for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) { + if (adie_client[id].client_id == -1 && + adie_client[id].rpc_client == NULL) + break; + } + if (id == ADIE_SVC_MAX_CLIENTS) { + mutex_unlock(&adie_client_lock); + return -1; + } + + mutex_lock(&adie_client[id].lock); + adie_client[id].rpc_client = msm_rpc_register_client("adie_client", + ADIE_SVC_PROG, + ADIE_SVC_VERS, 1, + adie_svc_rpc_cb_func); + if (IS_ERR(adie_client[id].rpc_client)) { + MM_ERR("Failed to register RPC client\n"); + adie_client[id].rpc_client = NULL; + mutex_unlock(&adie_client[id].lock); + mutex_unlock(&adie_client_lock); + return -1; + } + mutex_unlock(&adie_client_lock); + + adie_client[id].adie_svc_cb_done = 0; + arg.cb_id = id; + adie_client[id].cb_id = arg.cb_id; + mutex_unlock(&adie_client[id].lock); + rc = msm_rpc_client_req(adie_client[id].rpc_client, + SND_ADIE_SVC_CLIENT_REGISTER_PROC, + adie_svc_client_register_arg, &arg, + NULL, NULL, -1); + if (!rc) { + rc = wait_event_interruptible(adie_client[id].wq, + adie_client[id].adie_svc_cb_done); + mutex_lock(&adie_client[id].lock); + if (unlikely(rc < 0)) { + if (rc == -ERESTARTSYS) + MM_ERR("wait_event_interruptible " + "returned -ERESTARTSYS\n"); + else + MM_ERR("wait_event_interruptible " + "returned error\n"); + rc = -1; + goto err; + } + MM_DBG("Status %d received from CB function, id %d rc %d\n", + adie_client[id].status, adie_client[id].client_id, rc); + rc = id; + if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) { + MM_ERR("Received failed status for register request\n"); + rc = -1; + } else + goto done; + } else { + MM_ERR("Failed to send register client request\n"); + rc = -1; + mutex_lock(&adie_client[id].lock); + } +err: + msm_rpc_unregister_client(adie_client[id].rpc_client); + adie_client[id].rpc_client = NULL; + adie_client[id].client_id = -1; + adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID; + adie_client[id].adie_svc_cb_done = 0; +done: + mutex_unlock(&adie_client[id].lock); + return rc; +} +EXPORT_SYMBOL(adie_svc_get); + +/* Returns: 0 on succes and + * -1 on failure + */ +int adie_svc_put(int id) +{ + int rc = 0; + struct adie_svc_client_deregister_cb_args arg; + + if (id < 0 || id >= ADIE_SVC_MAX_CLIENTS) + return -1; + + mutex_lock(&adie_client[id].lock); + if (adie_client[id].client_id == -1 || + adie_client[id].rpc_client == NULL) { + mutex_unlock(&adie_client[id].lock); + return -1; + } + arg.client_id = adie_client[id].client_id; + adie_client[id].adie_svc_cb_done = 0; + mutex_unlock(&adie_client[id].lock); + rc = msm_rpc_client_req(adie_client[id].rpc_client, + SND_ADIE_SVC_CLIENT_DEREGISTER_PROC, + adie_svc_client_deregister_arg, &arg, + NULL, NULL, -1); + if (!rc) { + rc = wait_event_interruptible(adie_client[id].wq, + adie_client[id].adie_svc_cb_done); + if (unlikely(rc < 0)) { + if (rc == -ERESTARTSYS) + MM_ERR("wait_event_interruptible " + "returned -ERESTARTSYS\n"); + else + MM_ERR("wait_event_interruptible " + "returned error\n"); + rc = -1; + goto err; + } + MM_DBG("Status received from CB function\n"); + mutex_lock(&adie_client[id].lock); + if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) { + rc = -1; + } else { + msm_rpc_unregister_client(adie_client[id].rpc_client); + adie_client[id].rpc_client = NULL; + adie_client[id].client_id = -1; + adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID; + adie_client[id].adie_svc_cb_done = 0; + } + mutex_unlock(&adie_client[id].lock); + } else { + MM_ERR("Failed to send deregister client request\n"); + rc = -1; + } +err: + return rc; +} +EXPORT_SYMBOL(adie_svc_put); + +/* Returns: 0 on success + * 2 already in use + * -1 on failure + */ +int adie_svc_config_adie_block(int id, + enum adie_block_enum_type adie_block_type, bool enable) +{ + int rc = 0; + struct adie_svc_config_adie_block_cb_args arg; + + if (id < 0 || id >= ADIE_SVC_MAX_CLIENTS) + return -1; + + mutex_lock(&adie_client[id].lock); + if (adie_client[id].client_id == -1 || + adie_client[id].rpc_client == NULL) { + mutex_unlock(&adie_client[id].lock); + return -1; + } + arg.client_id = adie_client[id].client_id; + arg.adie_block = adie_block_type; + arg.config = (enum adie_config_enum_type)enable; + adie_client[id].adie_svc_cb_done = 0; + mutex_unlock(&adie_client[id].lock); + rc = msm_rpc_client_req(adie_client[id].rpc_client, + SND_ADIE_SVC_CONFIG_ADIE_BLOCK_PROC, + adie_svc_config_adie_block_arg, &arg, + NULL, NULL, -1); + if (!rc) { + rc = wait_event_interruptible(adie_client[id].wq, + adie_client[id].adie_svc_cb_done); + if (unlikely(rc < 0)) { + if (rc == -ERESTARTSYS) + MM_ERR("wait_event_interruptible " + "returned -ERESTARTSYS\n"); + else + MM_ERR("wait_event_interruptible " + "returned error\n"); + rc = -1; + goto err; + } + MM_DBG("Status received from CB function\n"); + mutex_lock(&adie_client[id].lock); + if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) + rc = -1; + else + rc = adie_client[id].status; + mutex_unlock(&adie_client[id].lock); + } else { + MM_ERR("Failed to send adie block config request\n"); + rc = -1; + } +err: + return rc; +} +EXPORT_SYMBOL(adie_svc_config_adie_block); + +#ifdef CONFIG_DEBUG_FS + +struct dentry *dentry; + +static ssize_t snd_adie_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t snd_adie_debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int rc = 0, op = 0; + int id = 0, adie_block = 0, config = 1; + + sscanf(buf, "%d %d %d %d", &op, &id, &adie_block, &config); + MM_INFO("\nUser input: op %d id %d block %d config %d\n", op, id, + adie_block, config); + switch (op) { + case ADIE_SVC_REGISTER_CLIENT: + MM_INFO("ADIE_SVC_REGISTER_CLIENT\n"); + rc = adie_svc_get(); + if (rc >= 0) + MM_INFO("Client registered: %d\n", rc); + else + MM_ERR("Failed registering client\n"); + break; + case ADIE_SVC_DEREGISTER_CLIENT: + MM_INFO("ADIE_SVC_DEREGISTER_CLIENT: %d\n", id); + rc = adie_svc_put(id); + if (!rc) + MM_INFO("Client %d deregistered\n", id); + else + MM_ERR("Failed unregistering the client: %d\n", id); + break; + case ADIE_SVC_CONFIG_ADIE_BLOCK: + MM_INFO("ADIE_SVC_CONFIG_ADIE_BLOCK: id %d adie_block %d \ + config %d\n", id, adie_block, config); + rc = adie_svc_config_adie_block(id, + (enum adie_block_enum_type)adie_block, (bool)config); + if (!rc) + MM_INFO("ADIE block %d %s", adie_block, + config ? "enabled\n" : "disabled\n"); + else if (rc == 2) + MM_INFO("ADIE block %d already in use\n", adie_block); + else + MM_ERR("ERROR configuring the ADIE block\n"); + break; + default: + MM_INFO("Invalid operation\n"); + } + return count; +} + +static ssize_t snd_adie_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + static char buffer[1024]; + const int debug_bufmax = sizeof(buffer); + int id, n = 0; + + n += scnprintf(buffer + n, debug_bufmax - n, + "LIST OF CLIENTS\n"); + for (id = 0; id < ADIE_SVC_MAX_CLIENTS ; id++) { + if (adie_client[id].client_id != -1 && + adie_client[id].rpc_client != NULL) { + n += scnprintf(buffer + n, debug_bufmax - n, + "id %d rpc client 0x%08x\n", id, + (uint32_t)adie_client[id].rpc_client); + } + } + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations snd_adie_debug_fops = { + .read = snd_adie_debug_read, + .open = snd_adie_debug_open, + .write = snd_adie_debug_write, +}; +#endif + +static void __exit snd_adie_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + if (dentry) + debugfs_remove(dentry); +#endif +} + +static int __init snd_adie_init(void) +{ + int id; +#ifdef CONFIG_DEBUG_FS + char name[sizeof "msm_snd_adie"]; + + snprintf(name, sizeof name, "msm_snd_adie"); + dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO, + NULL, NULL, &snd_adie_debug_fops); + if (IS_ERR(dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif + for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) { + adie_client[id].client_id = -1; + adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID; + adie_client[id].status = 0; + adie_client[id].adie_svc_cb_done = 0; + mutex_init(&adie_client[id].lock); + init_waitqueue_head(&adie_client[id].wq); + adie_client[id].rpc_client = NULL; + } + return 0; +} + +module_init(snd_adie_init); +module_exit(snd_adie_exit); diff --git a/arch/arm/mach-msm/qdsp5/snd_pcm_client.c b/arch/arm/mach-msm/qdsp5/snd_pcm_client.c new file mode 100644 index 00000000000..b58d3a2b6c2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/snd_pcm_client.c @@ -0,0 +1,522 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SND_VOC_PCM_INTERFACE_PROG 0x30000002 +#define SND_VOC_PCM_INTERFACE_VERS 0x00020004 + +/* Supply always 160 words of PCM samples (20ms data) */ +#define MAX_VOC_FRAME_SIZE 160 +#define VOC_FRAME_DURATION 20 +/* Buffering for Maximum 8 frames between userspace and driver */ +#define MAX_VOC_FRAMES 8 +#define BUFSZ ((MAX_VOC_FRAME_SIZE*2)*MAX_VOC_FRAMES) +#define SND_VOC_PCM_CLIENT_INPUT_FN_TYPE_PROC 3 +#define SND_VOC_REGISTER_PCM_INPUT_CLIENT_PROC 24 + +#define START_CALLBACK_ID 0x12345678 +#define STOP_CALLBACK_ID 0xffffffff + +#define MAX_WAIT_CONSUME (MAX_VOC_FRAMES * VOC_FRAME_DURATION) +/* PCM Interfaces */ +enum voice_pcm_interface_type { + VOICE_PCM_INTERFACE_TX_INPUT = 3, /* PCM Inject input to PreProc */ +}; + +enum voice_pcm_interface_reg_status_type { + SUCCESS = 0, /* Success 0, else failure */ +}; + +/* status used by PCM input callbacks to indicate availability of PCM Data */ +enum voice_pcm_data_status_type { + VOICE_PCM_DATA_STATUS_AVAILABLE, /* Data available for PCM input */ + VOICE_PCM_DATA_STATUS_UNAVAILABLE, /* Data not available */ + VOICE_PCM_DATA_STATUS_MAX +}; + +/* Argument needed to register PCM input client */ +struct snd_voice_pcm_interface_ipclnt_reg_args { + /* Interface number specifies the PCM inject point */ + enum voice_pcm_interface_type interface; + /* Non-NULL indicates start,NULL indicates stop */ + uint32_t callback_id; +}; + +struct snd_voice_pcm_interface_ipclnt_reg_status { + enum voice_pcm_interface_reg_status_type status; +}; + +struct snd_voice_pcm_interface_ipclnt_fn_type_args { + uint32_t callback_id; + uint32_t pcm_data_ptr_not_null; + uint32_t pcm_data_max_length; +}; + +struct snd_voice_pcm_interface_ipclnt_fn_type_reply { + enum voice_pcm_data_status_type status; + struct { + uint32_t pcm_data_len; + struct { + uint16_t pcm_data_ignore; + uint16_t pcm_data_valid; + } pcm_data_val[MAX_VOC_FRAME_SIZE]; + } pcm_data; +}; + +struct buffer { + void *data; + unsigned size; + unsigned used; +}; + +struct audio { + struct buffer out[MAX_VOC_FRAMES]; + + uint8_t out_head; + uint8_t out_tail; + + atomic_t out_bytes; + /* data allocated for various buffers */ + char *data; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t wait; + wait_queue_head_t stop_wait; + + int buffer_finished; + int opened; + int enabled; + int running; + int stopped; /* set when stopped */ + + struct msm_rpc_client *client; +}; + +static struct audio the_audio; + +static int snd_voice_pcm_interface_ipclnt_reg_args( + struct msm_rpc_client *client, void *buf, void *data) +{ + struct snd_voice_pcm_interface_ipclnt_reg_args *arg; + int size = 0; + + arg = (struct snd_voice_pcm_interface_ipclnt_reg_args *)data; + *((int *)buf) = cpu_to_be32(arg->interface); + size += sizeof(int); + buf += sizeof(int); + *((int *)buf) = cpu_to_be32(arg->callback_id); + size += sizeof(int); + + return size; +} + +static int snd_voice_pcm_interface_ipclnt_reg_status( + struct msm_rpc_client *client, void *buf, void *data) +{ + struct snd_voice_pcm_interface_ipclnt_reg_status *result = + (struct snd_voice_pcm_interface_ipclnt_reg_status *)buf; + + *((int *)data) = be32_to_cpu(result->status); + return 0; +} + +static void process_callback(struct audio *audio, + void *buffer, int in_size) +{ + uint32_t accept_status = RPC_ACCEPTSTAT_SUCCESS; + struct rpc_request_hdr *req; + struct snd_voice_pcm_interface_ipclnt_fn_type_args arg, *buf_ptr; + struct snd_voice_pcm_interface_ipclnt_fn_type_reply *reply; + struct buffer *frame; + uint32_t status; + uint32_t pcm_data_len; + + req = (struct rpc_request_hdr *)buffer; + buf_ptr = (struct snd_voice_pcm_interface_ipclnt_fn_type_args *)\ + (req + 1); + arg.callback_id = be32_to_cpu(buf_ptr->callback_id); + arg.pcm_data_ptr_not_null = be32_to_cpu(buf_ptr->pcm_data_ptr_not_null); + arg.pcm_data_max_length = be32_to_cpu(buf_ptr->pcm_data_max_length); + + MM_DBG("callback_id = 0x%8x pcm_data_ptr_not_null = 0x%8x"\ + "pcm_data_max_length = 0x%8x\n", arg.callback_id,\ + arg.pcm_data_ptr_not_null, arg.pcm_data_max_length); + /* Flag interface as running */ + if (!audio->running) + audio->running = 1; + if (!arg.pcm_data_ptr_not_null) { + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + msm_rpc_start_accepted_reply(audio->client, + be32_to_cpu(req->xid), accept_status); + msm_rpc_send_accepted_reply(audio->client, 0); + return; + } + reply = (struct snd_voice_pcm_interface_ipclnt_fn_type_reply *) + msm_rpc_start_accepted_reply(audio->client, + be32_to_cpu(req->xid), accept_status); + frame = audio->out + audio->out_tail; + /* If Data available, send data */ + if (frame->used) { + int i; + unsigned short *src = frame->data; + atomic_add(frame->used, &audio->out_bytes); + status = VOICE_PCM_DATA_STATUS_AVAILABLE; + pcm_data_len = MAX_VOC_FRAME_SIZE; + xdr_send_int32(&audio->client->cb_xdr, &status); + xdr_send_int32(&audio->client->cb_xdr, &pcm_data_len); + /* Expected cb_xdr buffer size is more than PCM buffer size */ + for (i = 0; i < MAX_VOC_FRAME_SIZE; i++, ++src) + xdr_send_int16(&audio->client->cb_xdr, src); + frame->used = 0; + audio->out_tail = ((++audio->out_tail) % MAX_VOC_FRAMES); + wake_up(&audio->wait); + } else { + status = VOICE_PCM_DATA_STATUS_UNAVAILABLE; + pcm_data_len = 0; + xdr_send_int32(&audio->client->cb_xdr, &status); + xdr_send_int32(&audio->client->cb_xdr, &pcm_data_len); + wake_up(&audio->wait); + /* Flag all buffer completed */ + if (audio->stopped) { + audio->buffer_finished = 1; + wake_up(&audio->stop_wait); + } + } + MM_DBG("Provided PCM data = 0x%8x\n", reply->status); + msm_rpc_send_accepted_reply(audio->client, 0); + return; +} + +static int pcm_interface_process_callback_routine(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + struct rpc_request_hdr *req; + struct audio *audio = &the_audio; + int rc = 0; + + req = (struct rpc_request_hdr *)buffer; + + MM_DBG("proc id = 0x%8x xid = 0x%8x size = 0x%8x\n", + be32_to_cpu(req->procedure), be32_to_cpu(req->xid), in_size); + switch (be32_to_cpu(req->procedure)) { + /* Procedure which called every 20ms for PCM samples request*/ + case SND_VOC_PCM_CLIENT_INPUT_FN_TYPE_PROC: + process_callback(audio, buffer, in_size); + break; + default: + MM_ERR("Not supported proceudure 0x%8x\n", + be32_to_cpu(req->procedure)); + /* Not supported RPC Procedure, send nagative code */ + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + RPC_ACCEPTSTAT_PROC_UNAVAIL); + msm_rpc_send_accepted_reply(client, 0); + } + return rc; +} + +static void audio_flush(struct audio *audio) +{ + int cnt; + for (cnt = 0; cnt < MAX_VOC_FRAMES; cnt++) + audio->out[cnt].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; + audio->running = 0; + audio->buffer_finished = 0; +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + int rc; + struct snd_voice_pcm_interface_ipclnt_reg_args arg; + struct snd_voice_pcm_interface_ipclnt_reg_status result; + + /* voice_pcm_interface_type */ + arg.interface = VOICE_PCM_INTERFACE_TX_INPUT; + /* Should be non-zero, unique */ + arg.callback_id = START_CALLBACK_ID; + /* Start Voice PCM interface */ + rc = msm_rpc_client_req(audio->client, + SND_VOC_REGISTER_PCM_INPUT_CLIENT_PROC, + snd_voice_pcm_interface_ipclnt_reg_args, &arg, + snd_voice_pcm_interface_ipclnt_reg_status, + &result, -1); + MM_DBG("input client registration status rc 0x%8x result 0x%8x\n", + rc, result.status); + /* If error in server side */ + if (rc == 0) + if (result.status != SUCCESS) + rc = -ENODEV; + return rc; +} +static int audio_disable(struct audio *audio) +{ + int rc; + struct snd_voice_pcm_interface_ipclnt_reg_args arg; + struct snd_voice_pcm_interface_ipclnt_reg_status result; + + /* Wait till all buffers consumed to prevent data loss + Also ensure if client stops due to vocoder disable + do not loop forever */ + rc = wait_event_interruptible_timeout(audio->stop_wait, + !(audio->running) || (audio->buffer_finished == 1), + msecs_to_jiffies(MAX_WAIT_CONSUME)); + if (rc < 0) + return 0; + /* voice_pcm_interface_type */ + arg.interface = VOICE_PCM_INTERFACE_TX_INPUT; + arg.callback_id = STOP_CALLBACK_ID; /* Should be zero */ + /* Stop Voice PCM interface */ + rc = msm_rpc_client_req(audio->client, + SND_VOC_REGISTER_PCM_INPUT_CLIENT_PROC, + snd_voice_pcm_interface_ipclnt_reg_args, &arg, + snd_voice_pcm_interface_ipclnt_reg_status, + &result, -1); + MM_DBG("input client de-registration status rc 0x%8x result 0x%8x\n", + rc, result.status); + /* If error in server side */ + if (rc == 0) + if (result.status != SUCCESS) + rc = -ENODEV; + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->out_bytes); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_enable(audio); + if (rc == 0) + audio->enabled = 1; + break; + case AUDIO_STOP: + if (audio->enabled) { + audio->stopped = 1; + rc = audio_disable(audio); + if (rc == 0) { + audio->enabled = 0; + audio->running = 0; + wake_up(&audio->wait); + } else + audio->stopped = 0; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.type == 0) { + /* Selection for different PCM intect point */ + } else { + rc = -EINVAL; + break; + } + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = MAX_VOC_FRAME_SIZE * 2; + config.buffer_count = MAX_VOC_FRAMES; + config.sample_rate = 8000; + config.channel_count = 1; + config.type = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + default: { + rc = -EINVAL; + MM_ERR(" Unsupported ioctl 0x%8x\n", cmd); + } + } + mutex_unlock(&audio->lock); + return rc; +} +static ssize_t audio_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int rc = 0; + + mutex_lock(&audio->write_lock); + /* Ensure to copy only till frame boundary */ + while (count >= (MAX_VOC_FRAME_SIZE*2)) { + frame = audio->out + audio->out_head; + rc = wait_event_interruptible_timeout(audio->wait,\ + (frame->used == 0) || (audio->stopped), + msecs_to_jiffies(MAX_WAIT_CONSUME)); + + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + if (rc == 0) { + rc = -ETIMEDOUT; + break; + } + + xfer = count > frame->size ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + frame->used = xfer; + audio->out_head = ((++audio->out_head) % MAX_VOC_FRAMES); + count -= xfer; + buf += xfer; + } + mutex_unlock(&audio->write_lock); + MM_DBG("write done 0x%8x\n", (unsigned int)(buf - start)); + if (rc < 0) + return rc; + return buf - start; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + int rc, cnt; + + mutex_lock(&audio->lock); + + if (audio->opened) { + MM_ERR("busy as driver already in open state\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = kmalloc(BUFSZ, GFP_KERNEL); + if (!audio->data) { + MM_ERR("could not allocate buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + audio->client = msm_rpc_register_client("voice_pcm_interface_client", + SND_VOC_PCM_INTERFACE_PROG, + SND_VOC_PCM_INTERFACE_VERS, 1, + pcm_interface_process_callback_routine); + if (IS_ERR(audio->client)) { + MM_ERR("Failed to register voice pcm interface client"\ + "to 0x%8x\n", SND_VOC_PCM_INTERFACE_PROG); + kfree(audio->data); + audio->data = NULL; + rc = -ENODEV; + goto done; + } + MM_INFO("voice pcm client registred %p\n", audio->client); + for (cnt = 0; cnt < MAX_VOC_FRAMES; cnt++) { + audio->out[cnt].data = (audio->data +\ + ((MAX_VOC_FRAME_SIZE * 2) * cnt)); + audio->out[cnt].size = MAX_VOC_FRAME_SIZE * 2; + MM_DBG("data ptr = %p\n", audio->out[cnt].data); + } + file->private_data = audio; + audio_flush(audio); + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + if (audio->enabled) { + audio->stopped = 1; + audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + wake_up(&audio->wait); + } + msm_rpc_unregister_client(audio->client); + kfree(audio->data); + audio->data = NULL; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static const struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, +}; + +static struct miscdevice audio_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "snd_pcm_client", + .fops = &audio_fops, +}; + +static int __init audio_init(void) +{ + mutex_init(&the_audio.lock); + mutex_init(&the_audio.write_lock); + init_waitqueue_head(&the_audio.wait); + init_waitqueue_head(&the_audio.stop_wait); + return misc_register(&audio_misc); +} +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/Makefile b/arch/arm/mach-msm/qdsp5v2/Makefile new file mode 100644 index 00000000000..3ae3c1b1fb4 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/Makefile @@ -0,0 +1,22 @@ +obj-y += afe.o audio_interct.o mi2s.o audio_dev_ctl.o voice.o + +ifeq ($(CONFIG_TIMPANI_CODEC), y) +obj-y += snddev_icodec.o +else ifeq ($(CONFIG_MARIMBA_CODEC), y) +obj-y += snddev_icodec.o +endif + +obj-$(CONFIG_MARIMBA_CODEC) += snddev_data_marimba.o +obj-$(CONFIG_TIMPANI_CODEC) += snddev_data_timpani.o + +obj-y += audio_pcm.o audpp.o audio_mp3.o audio_wma.o audio_aac.o audio_amrnb.o +obj-y += audio_amrwb.o audio_wmapro.o audio_adpcm.o audio_evrc.o audio_qcelp.o +obj-y += aux_pcm.o snddev_ecodec.o audio_out.o +obj-y += audio_lpa.o mp3_funcs.o pcm_funcs.o +obj-y += audpreproc.o audio_pcm_in.o audio_aac_in.o audio_amrnb_in.o audio_a2dp_in.o +obj-y += audio_evrc_in.o audio_qcelp_in.o +obj-y += adsp.o adsp_driver.o adsp_info.o +obj-y += audio_acdb.o snddev_virtual.o +obj-y += audio_fm.o +obj-y += lpa.o snddev_mi2s.o +obj-y += audio_mvs.o \ No newline at end of file diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.c b/arch/arm/mach-msm/qdsp5v2/adsp.c new file mode 100644 index 00000000000..acd9c4c1bcc --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp.c @@ -0,0 +1,1225 @@ +/* + * Register/Interrupt access for userspace aDSP library. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009,2011-2012 Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* TODO: + * - move shareable rpc code outside of adsp.c + * - general solution for virt->phys patchup + * - queue IDs should be relative to modules + * - disallow access to non-associated queues + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp.h" +#include +#include + +#ifdef CONFIG_DEBUG_FS +static struct dentry *dentry_adsp; +static struct dentry *dentry_wdata; +static struct dentry *dentry_rdata; +static int wdump, rdump; +#endif /* CONFIG_DEBUG_FS */ + +static struct adsp_info adsp_info; +static struct msm_adsp_module *adsp_modules; +static int adsp_open_count; + +static DEFINE_MUTEX(adsp_open_lock); + +/* protect interactions with the ADSP command/message queue */ +static spinlock_t adsp_cmd_lock; +static spinlock_t adsp_write_lock; + +static uint32_t current_image = -1; + +void adsp_set_image(struct adsp_info *info, uint32_t image) +{ + current_image = image; +} + +/* + * Checks whether the module_id is available in the + * module_entries table.If module_id is available returns `0`. + * If module_id is not available returns `-ENXIO`. + */ +static int32_t adsp_validate_module(uint32_t module_id) +{ + uint32_t *ptr; + uint32_t module_index; + uint32_t num_mod_entries; + + ptr = adsp_info.init_info_ptr->module_entries; + num_mod_entries = adsp_info.init_info_ptr->module_table_size; + + for (module_index = 0; module_index < num_mod_entries; module_index++) + if (module_id == ptr[module_index]) + return 0; + + return -ENXIO; +} + +static int32_t adsp_validate_queue(uint32_t mod_id, unsigned q_idx, + uint32_t size) +{ + int32_t i; + struct adsp_rtos_mp_mtoa_init_info_type *sptr; + + sptr = adsp_info.init_info_ptr; + for (i = 0; i < sptr->mod_to_q_entries; i++) + if (mod_id == sptr->mod_to_q_tbl[i].module) + if (q_idx == sptr->mod_to_q_tbl[i].q_type) { + if (size <= sptr->mod_to_q_tbl[i].q_max_len) + return 0; + MM_ERR("q_idx: %d is not a valid queue \ + for module %x\n", q_idx, mod_id); + return -EINVAL; + } + MM_ERR("cmd_buf size is more than allowed size\n"); + return -EINVAL; +} + +uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) +{ + return info->task_to_module[current_image][task]; +} + +uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id) +{ + return info->queue_offset[current_image][queue_id]; +} + +static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, + struct msm_adsp_module *adsp_module) +{ + struct adsp_rtos_atom_cmd adspsvc_cmd; + int err; + + adspsvc_cmd.cmd = cmd; + adspsvc_cmd.proc_id = RPC_ADSP_RTOS_PROC_APPS; + adspsvc_cmd.module = module; + adspsvc_cmd.cb_handle = adsp_info.cb_handle; + + err = dalrpc_fcn_5(DALDEVICE_ADSP_CMD_IDX | 0x80000000, + adsp_info.handle, + &adspsvc_cmd, sizeof(adspsvc_cmd)); + if (err < 0) + MM_ERR("ADSP command send Failed\n"); + + return 0; +} + +static int get_module_index(uint32_t id) +{ + int mod_idx; + for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++) + if (adsp_info.module[mod_idx].id == id) + return mod_idx; + + return -ENXIO; +} + +static struct msm_adsp_module *find_adsp_module_by_id( + struct adsp_info *info, uint32_t id) +{ + int mod_idx; + + if (id > info->max_module_id) { + return NULL; + } else { + mod_idx = get_module_index(id); + if (mod_idx < 0) + return NULL; + return info->id_to_module[mod_idx]; + } +} + +static struct msm_adsp_module *find_adsp_module_by_name( + struct adsp_info *info, const char *name) +{ + unsigned n; + for (n = 0; n < info->module_count; n++) + if (!strcmp(name, adsp_modules[n].name)) + return adsp_modules + n; + return NULL; +} + +/* + * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get + * queue offsets and module entries (init info) as part of the event. + */ +static void msm_get_init_info(void) +{ + struct adsp_rtos_atom_cmd cmd; + int err; + + cmd.cmd = RPC_ADSP_RTOS_CMD_GET_INIT_INFO; + cmd.proc_id = RPC_ADSP_RTOS_PROC_APPS; + cmd.module = 0; + cmd.cb_handle = adsp_info.cb_handle; + + err = dalrpc_fcn_5(DALDEVICE_ADSP_CMD_IDX | 0x80000000, + adsp_info.handle, + &cmd, sizeof(cmd)); + if (err < 0) + MM_ERR("INIT_INFO command send Failed\n"); +} + +int msm_adsp_get(const char *name, struct msm_adsp_module **out, + struct msm_adsp_ops *ops, void *driver_data) +{ + struct msm_adsp_module *module; + int rc = 0; + + module = find_adsp_module_by_name(&adsp_info, name); + if (!module) + return -ENODEV; + + mutex_lock(&module->lock); + MM_DBG("opening module %s\n", module->name); + + if (module->ops) { + rc = -EBUSY; + mutex_unlock(&module->lock); + goto done; + } + + module->ops = ops; + module->driver_data = driver_data; + *out = module; + mutex_unlock(&module->lock); + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP, + module->id, module); + if (rc) { + mutex_lock(&module->lock); + module->ops = NULL; + module->driver_data = NULL; + *out = NULL; + MM_ERR("REGISTER_APP failed\n"); + mutex_unlock(&module->lock); + goto done; + } + + MM_INFO("module %s has been registered\n", module->name); + +done: + return rc; +} +EXPORT_SYMBOL(msm_adsp_get); + +void msm_adsp_put(struct msm_adsp_module *module) +{ + unsigned long flags; + + mutex_lock(&module->lock); + if (module->ops) { + MM_INFO("closing module %s\n", module->name); + + /* lock to ensure a dsp event cannot be delivered + * during or after removal of the ops and driver_data + */ + spin_lock_irqsave(&adsp_cmd_lock, flags); + module->ops = NULL; + module->driver_data = NULL; + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + + if (module->state != ADSP_STATE_DISABLED) { + MM_INFO("disabling module %s\n", module->name); + mutex_unlock(&module->lock); + msm_adsp_disable(module); + return; + } + } else { + MM_INFO("module %s is already closed\n", module->name); + } + mutex_unlock(&module->lock); +} +EXPORT_SYMBOL(msm_adsp_put); + +int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + uint32_t ctrl_word; + uint32_t dsp_q_addr; + uint32_t dsp_addr; + uint32_t cmd_id = 0; + int cnt = 0; + int ret_status = 0; + unsigned long flags; + struct adsp_info *info; + + if (!module || !cmd_buf) { + MM_ERR("Called with NULL parameters\n"); + return -EINVAL; + } + info = module->info; + spin_lock_irqsave(&adsp_write_lock, flags); + + if (module->state != ADSP_STATE_ENABLED) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + MM_ERR("module %s not enabled before write\n", module->name); + return -ENODEV; + } + if (adsp_validate_module(module->id)) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + MM_ERR("module id validation failed %s %d\n", + module->name, module->id); + return -ENXIO; + } + if (dsp_queue_addr >= QDSP_MAX_NUM_QUEUES) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + MM_ERR("Invalid Queue Index: %d\n", dsp_queue_addr); + return -ENXIO; + } + if (adsp_validate_queue(module->id, dsp_queue_addr, cmd_size)) { + spin_unlock_irqrestore(&adsp_write_lock, flags); + return -EINVAL; + } + dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr); + dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M; + + /* Poll until the ADSP is ready to accept a command. + * Wait for 100us, return error if it's not responding. + * If this returns an error, we need to disable ALL modules and + * then retry. + */ + while (((ctrl_word = readl(info->write_ctrl)) & + ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != + ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { + if (cnt > 50) { + MM_ERR("timeout waiting for DSP write ready\n"); + ret_status = -EIO; + goto fail; + } + MM_DBG("waiting for DSP write ready\n"); + udelay(2); + cnt++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Clear the command bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. This notifies the DSP that + * we are about to send a command on this particular queue. The + * DSP will in response change its state. + */ + writel(1, info->send_irq); + + /* Poll until the adsp responds to the interrupt; this does not + * generate an interrupt from the adsp. This should happen within + * 5ms. + */ + cnt = 0; + while ((readl(info->write_ctrl) & + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { + if (cnt > 2500) { + MM_ERR("timeout waiting for adsp ack\n"); + ret_status = -EIO; + goto fail; + } + udelay(2); + cnt++; + } + + /* Read the ctrl word */ + ctrl_word = readl(info->write_ctrl); + + if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != + ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { + ret_status = -EAGAIN; + goto fail; + } else { + /* No error */ + /* Get the DSP buffer address */ + dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE; + + if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint16_t *buf_ptr = (uint16_t *) cmd_buf; + uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; + cmd_size /= sizeof(uint16_t); + + /* Save the command ID */ + cmd_id = (uint32_t) buf_ptr[0]; + + /* Copy the command to DSP memory */ + cmd_size++; + while (--cmd_size) + *dsp_addr16++ = *buf_ptr++; + } else { + uint32_t *buf_ptr = (uint32_t *) cmd_buf; + uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; + cmd_size /= sizeof(uint32_t); + + /* Save the command ID */ + cmd_id = buf_ptr[0]; + + cmd_size++; + while (--cmd_size) + *dsp_addr32++ = *buf_ptr++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Set the command bits to write done */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. It does not respond with + * an interrupt, and we do not need to wait for it to + * acknowledge, because it will hold the mutex lock until it's + * ready to receive more commands again. + */ + writel(1, info->send_irq); + + module->num_commands++; + } /* Ctrl word status bits were 00, no error in the ctrl word */ + +fail: + spin_unlock_irqrestore(&adsp_write_lock, flags); + return ret_status; +} +EXPORT_SYMBOL(msm_adsp_write); + +int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + int rc, retries = 0; +#ifdef CONFIG_DEBUG_FS + uint16_t *ptr; + int ii; + + if (wdump > 0) { + ptr = cmd_buf; + pr_info("A->D:%x\n", module->id); + pr_info("adsp: %x %d\n", dsp_queue_addr, cmd_size); + for (ii = 0; ii < cmd_size/2; ii++) + pr_info("%x ", ptr[ii]); + pr_info("\n"); + } +#endif /* CONFIG_DEBUG_FS */ + do { + rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, + cmd_size); + if (rc == -EAGAIN) + udelay(50); + } while (rc == -EAGAIN && retries++ < 300); + if (retries > 20) + MM_INFO("%s command took %d attempts: rc %d\n", + module->name, retries, rc); + return rc; +} + +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS +static void *event_addr; +static void read_event(void *buf, size_t len) +{ + uint32_t dptr[3]; + struct adsp_rtos_mp_mtoa_s_type *sptr; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + + sptr = event_addr; + pkt_ptr = &sptr->adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + + dptr[0] = sptr->mp_mtoa_header.event; + dptr[1] = pkt_ptr->module; + dptr[2] = pkt_ptr->image; + + if (len > EVENT_LEN) + len = EVENT_LEN; + + memcpy(buf, dptr, len); +} +#endif + +static void adsp_rtos_mtoa_cb(void *context, uint32_t param, + void *evt_buf, uint32_t len) +{ + struct adsp_rtos_mp_mtoa_s_type *args = NULL; + uint32_t event = 0; + uint32_t proc_id = 0; + uint32_t module_id; + uint32_t image; + struct msm_adsp_module *module; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + struct queue_to_offset_type *qptr; + struct queue_to_offset_type *qtbl; + struct mod_to_queue_offsets *mqptr; + struct mod_to_queue_offsets *mqtbl; + uint32_t *mptr; + uint32_t *mtbl; + uint32_t q_idx; + uint32_t num_entries; + uint32_t entries_per_image; + struct adsp_rtos_mp_mtoa_init_info_type *iptr; + struct adsp_rtos_mp_mtoa_init_info_type *sptr; + int32_t i_no, e_idx; + static uint32_t init_info_completed; + static uint32_t init_info_len = + sizeof(struct adsp_rtos_mp_mtoa_header_type); + static uint32_t next_init_info_byte; + static uint32_t expected_byte = 1; + uint32_t hdr_len = sizeof(struct adsp_rtos_mp_mtoa_header_type); + + if (len) { + args = (struct adsp_rtos_mp_mtoa_s_type *) evt_buf; + event = args->mp_mtoa_header.event; + proc_id = args->mp_mtoa_header.proc_id; + } + + if (!init_info_completed && event == RPC_ADSP_RTOS_INIT_INFO) { + memcpy(((char *)adsp_info.raw_event) + init_info_len, + (char *)evt_buf + hdr_len + 4, + len - ((hdr_len + 4))); + init_info_len += (len - (hdr_len + 4)); + evt_buf += hdr_len; + next_init_info_byte = *(uint32_t *) evt_buf; + expected_byte += len; + if (next_init_info_byte && + (expected_byte != next_init_info_byte)) { + MM_ERR("INIT_INFO - expecting next byte to be %d\n" + "\tbut ADSPSVC indicated next byte to be %d\n", + expected_byte, next_init_info_byte); + return; + } + if (!next_init_info_byte) { + args = adsp_info.raw_event; + args->mp_mtoa_header.event = event; + args->mp_mtoa_header.proc_id = proc_id; + init_info_completed = 1; + } else + return; + } + + if (event == RPC_ADSP_RTOS_INIT_INFO) { + MM_INFO("INIT_INFO Event\n"); + sptr = &args->adsp_rtos_mp_mtoa_data.mp_mtoa_init_packet; + + iptr = adsp_info.init_info_ptr; + iptr->image_count = sptr->image_count; + if (iptr->image_count > IMG_MAX) + iptr->image_count = IMG_MAX; + iptr->num_queue_offsets = sptr->num_queue_offsets; + num_entries = iptr->num_queue_offsets; + if (num_entries > ENTRIES_MAX) { + num_entries = ENTRIES_MAX; + iptr->num_queue_offsets = ENTRIES_MAX; + } + qptr = &sptr->queue_offsets_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + qtbl = &iptr->queue_offsets_tbl[i_no][0]; + for (e_idx = 0; e_idx < num_entries; e_idx++) { + qtbl[e_idx].offset = qptr->offset; + qtbl[e_idx].queue = qptr->queue; + q_idx = qptr->queue; + iptr->queue_offsets[i_no][q_idx] = + qtbl[e_idx].offset; + qptr++; + } + } + + num_entries = sptr->num_task_module_entries; + if (num_entries > ENTRIES_MAX) + num_entries = ENTRIES_MAX; + iptr->num_task_module_entries = num_entries; + entries_per_image = num_entries / iptr->image_count; + mptr = &sptr->task_to_module_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + mtbl = &iptr->task_to_module_tbl[i_no][0]; + for (e_idx = 0; e_idx < entries_per_image; e_idx++) { + mtbl[e_idx] = *mptr; + mptr++; + } + } + + iptr->module_table_size = sptr->module_table_size; + if (iptr->module_table_size > MODULES_MAX) + iptr->module_table_size = MODULES_MAX; + mptr = &sptr->module_entries[0]; + for (i_no = 0; i_no < iptr->module_table_size; i_no++) + iptr->module_entries[i_no] = mptr[i_no]; + + mqptr = &sptr->mod_to_q_tbl[0]; + mqtbl = &iptr->mod_to_q_tbl[0]; + iptr->mod_to_q_entries = sptr->mod_to_q_entries; + if (iptr->mod_to_q_entries > ENTRIES_MAX) + iptr->mod_to_q_entries = ENTRIES_MAX; + for (e_idx = 0; e_idx < iptr->mod_to_q_entries; e_idx++) { + mqtbl[e_idx].module = mqptr->module; + mqtbl[e_idx].q_type = mqptr->q_type; + mqtbl[e_idx].q_max_len = mqptr->q_max_len; + mqptr++; + } + + adsp_info.init_info_state = ADSP_STATE_INIT_INFO; + kfree(adsp_info.raw_event); + wake_up(&adsp_info.init_info_wait); + return; + } + pkt_ptr = &args->adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + module_id = pkt_ptr->module; + image = pkt_ptr->image; + + MM_INFO("rpc event=%d, proc_id=%d, module=%d, image=%d\n", + event, proc_id, module_id, image); + + module = find_adsp_module_by_id(&adsp_info, module_id); + if (!module) { + MM_ERR("module %d is not supported!\n", module_id); + return; + } + + mutex_lock(&module->lock); + switch (event) { + case RPC_ADSP_RTOS_MOD_READY: + MM_INFO("module %s: READY\n", module->name); + module->state = ADSP_STATE_ENABLED; + wake_up(&module->state_wait); + adsp_set_image(module->info, image); + break; + case RPC_ADSP_RTOS_MOD_DISABLE: + MM_INFO("module %s: DISABLED\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_SERVICE_RESET: + MM_INFO("module %s: SERVICE_RESET\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_CMD_SUCCESS: + MM_INFO("module %s: CMD_SUCCESS\n", module->name); + break; + case RPC_ADSP_RTOS_CMD_FAIL: + MM_INFO("module %s: CMD_FAIL\n", module->name); + break; + case RPC_ADSP_RTOS_DISABLE_FAIL: + MM_INFO("module %s: DISABLE_FAIL\n", module->name); + break; + default: + MM_ERR("unknown event %d\n", event); + mutex_unlock(&module->lock); + return; + } +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS + event_addr = (uint32_t *)evt_buf; + if (module->ops) + module->ops->event(module->driver_data, + EVENT_MSG_ID, + EVENT_LEN, + read_event); +#endif + mutex_unlock(&module->lock); +} + +static size_t read_event_size; +static void *read_event_addr; + +static void read_event_16(void *buf, size_t len) +{ + uint16_t *dst = buf; + uint16_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static void read_event_32(void *buf, size_t len) +{ + uint32_t *dst = buf; + uint32_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v( + struct adsp_info *info, void *dsp_addr) +{ + struct msm_adsp_module *module; + unsigned rtos_task_id; + unsigned msg_id; + unsigned msg_length; +#ifdef CONFIG_DEBUG_FS + uint16_t *ptr; + int ii; +#endif /* CONFIG_DEBUG_FS */ + void (*func)(void *, size_t); + + if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint32_t *dsp_addr32 = dsp_addr; + uint32_t tmp = *dsp_addr32++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M); + read_event_size = tmp >> 16; + read_event_addr = dsp_addr32; + msg_length = read_event_size * sizeof(uint32_t); + func = read_event_32; + } else { + uint16_t *dsp_addr16 = dsp_addr; + uint16_t tmp = *dsp_addr16++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M; + read_event_size = *dsp_addr16++; + read_event_addr = dsp_addr16; + msg_length = read_event_size * sizeof(uint16_t); + func = read_event_16; + } + + if (rtos_task_id > info->max_task_id) { + MM_ERR("bogus task id %d\n", rtos_task_id); + return 0; + } + module = find_adsp_module_by_id(info, + adsp_get_module(info, rtos_task_id)); + + if (!module) { + MM_ERR("no module for task id %d\n", rtos_task_id); + return 0; + } + + module->num_events++; + + if (!module->ops) { + MM_ERR("module %s is not open\n", module->name); + return 0; + } +#ifdef CONFIG_DEBUG_FS + if (rdump > 0) { + ptr = read_event_addr; + pr_info("D->A\n"); + pr_info("m_id = %x id = %x\n", module->id, msg_id); + for (ii = 0; ii < msg_length/2; ii++) + pr_info("%x ", ptr[ii]); + pr_info("\n"); + } +#endif /* CONFIG_DEBUG_FS */ + + module->ops->event(module->driver_data, msg_id, msg_length, func); + return 0; +} + +static int adsp_get_event(struct adsp_info *info) +{ + uint32_t ctrl_word; + uint32_t ready; + void *dsp_addr; + uint32_t cmd_type; + int cnt; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + + /* Whenever the DSP has a message, it updates this control word + * and generates an interrupt. When we receive the interrupt, we + * read this register to find out what ADSP task the command is + * comming from. + * + * The ADSP should *always* be ready on the first call, but the + * irq handler calls us in a loop (to handle back-to-back command + * processing), so we give the DSP some time to return to the + * ready state. The DSP will not issue another IRQ for events + * pending between the first IRQ and the event queue being drained, + * unfortunately. + */ + + for (cnt = 0; cnt < 50; cnt++) { + ctrl_word = readl(info->read_ctrl); + + if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) == + ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V) + goto ready; + + udelay(2); + } + MM_ERR("not ready after 100uS\n"); + rc = -EBUSY; + goto done; + +ready: + /* Here we check to see if there are pending messages. If there are + * none, we siply return -EAGAIN to indicate that there are no more + * messages pending. + */ + ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M; + if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) && + (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) { + rc = -EAGAIN; + goto done; + } + + /* DSP says that there are messages waiting for the host to read */ + + /* Get the Command Type */ + cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M; + + /* Get the DSP buffer address */ + dsp_addr = (void *)((ctrl_word & + ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE); + + /* We can only handle Task-to-Host messages */ + if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) { + MM_ERR("unknown dsp cmd_type %d\n", cmd_type); + rc = -EIO; + goto done; + } + + adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr); + + ctrl_word = readl(info->read_ctrl); + ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M; + + /* Write ctrl word to the DSP */ + writel(ctrl_word, info->read_ctrl); + + /* Generate an interrupt to the DSP */ + writel(1, info->send_irq); + +done: + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return rc; +} + +static irqreturn_t adsp_irq_handler(int irq, void *data) +{ + struct adsp_info *info = &adsp_info; + int cnt = 0; + for (cnt = 0; cnt < 15; cnt++) + if (adsp_get_event(info) < 0) + break; + if (cnt > info->event_backlog_max) + info->event_backlog_max = cnt; + info->events_received += cnt; + if (cnt == 15) + MM_ERR("too many (%d) events for single irq!\n", cnt); + return IRQ_HANDLED; +} + +int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate) +{ + if (module->clk && clk_rate) + return clk_set_rate(module->clk, clk_rate); + + return -EINVAL; +} + +int msm_adsp_enable(struct msm_adsp_module *module) +{ + int rc = 0; + + MM_INFO("enable '%s'state[%d] id[%d]\n", + module->name, module->state, module->id); + + mutex_lock(&module->lock); + switch (module->state) { + case ADSP_STATE_DISABLED: + module->state = ADSP_STATE_ENABLING; + mutex_unlock(&module->lock); + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE, + module->id, module); + if (rc) { + mutex_lock(&module->lock); + module->state = ADSP_STATE_DISABLED; + break; + } + rc = wait_event_timeout(module->state_wait, + module->state != ADSP_STATE_ENABLING, + 1 * HZ); + mutex_lock(&module->lock); + if (module->state == ADSP_STATE_ENABLED) { + rc = 0; + } else { + MM_ERR("module '%s' enable timed out\n", module->name); + rc = -ETIMEDOUT; + } + if (module->open_count++ == 0 && module->clk) + clk_enable(module->clk); + + mutex_lock(&adsp_open_lock); + if (adsp_open_count++ == 0) + enable_irq(adsp_info.int_adsp); + mutex_unlock(&adsp_open_lock); + break; + case ADSP_STATE_ENABLING: + MM_DBG("module '%s' enable in progress\n", module->name); + break; + case ADSP_STATE_ENABLED: + MM_DBG("module '%s' already enabled\n", module->name); + break; + case ADSP_STATE_DISABLING: + MM_ERR("module '%s' disable in progress\n", module->name); + rc = -EBUSY; + break; + } + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_enable); + +int msm_adsp_disable_event_rsp(struct msm_adsp_module *module) +{ + int rc = 0; + + mutex_lock(&module->lock); + + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, + module->id, module); + mutex_unlock(&module->lock); + + return rc; +} +EXPORT_SYMBOL(msm_adsp_disable_event_rsp); + +int msm_adsp_disable(struct msm_adsp_module *module) +{ + int rc = 0; + + mutex_lock(&module->lock); + switch (module->state) { + case ADSP_STATE_DISABLED: + MM_DBG("module '%s' already disabled\n", module->name); + mutex_unlock(&module->lock); + break; + case ADSP_STATE_ENABLING: + case ADSP_STATE_ENABLED: + mutex_unlock(&module->lock); + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE, + module->id, module); + mutex_lock(&module->lock); + module->state = ADSP_STATE_DISABLED; + if (--module->open_count == 0 && module->clk) + clk_disable(module->clk); + mutex_unlock(&module->lock); + mutex_lock(&adsp_open_lock); + if (--adsp_open_count == 0) { + disable_irq(adsp_info.int_adsp); + MM_INFO("disable interrupt\n"); + } + mutex_unlock(&adsp_open_lock); + break; + } + return rc; +} +EXPORT_SYMBOL(msm_adsp_disable); + +static int msm_adsp_probe(struct platform_device *pdev) +{ + unsigned count; + int rc, i; + + adsp_info.int_adsp = platform_get_irq(pdev, 0); + if (adsp_info.int_adsp < 0) { + MM_ERR("no irq resource?\n"); + return -ENODEV; + } + + adsp_info.init_info_ptr = kzalloc( + (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL); + if (!adsp_info.init_info_ptr) + return -ENOMEM; + + adsp_info.raw_event = kzalloc( + (sizeof(struct adsp_rtos_mp_mtoa_s_type)), GFP_KERNEL); + if (!adsp_info.raw_event) { + kfree(adsp_info.init_info_ptr); + return -ENOMEM; + } + + rc = adsp_init_info(&adsp_info); + if (rc) { + kfree(adsp_info.init_info_ptr); + kfree(adsp_info.raw_event); + return rc; + } + adsp_info.send_irq += (uint32_t) MSM_AD5_BASE; + adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE; + adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE; + count = adsp_info.module_count; + + adsp_modules = kzalloc( + (sizeof(struct msm_adsp_module) + sizeof(void *)) * + count, GFP_KERNEL); + if (!adsp_modules) { + kfree(adsp_info.init_info_ptr); + kfree(adsp_info.raw_event); + return -ENOMEM; + } + + adsp_info.id_to_module = (void *) (adsp_modules + count); + + spin_lock_init(&adsp_cmd_lock); + spin_lock_init(&adsp_write_lock); + + rc = request_irq(adsp_info.int_adsp, adsp_irq_handler, + IRQF_TRIGGER_RISING, "adsp", 0); + if (rc < 0) + goto fail_request_irq; + disable_irq(adsp_info.int_adsp); + + for (i = 0; i < count; i++) { + struct msm_adsp_module *mod = adsp_modules + i; + mutex_init(&mod->lock); + init_waitqueue_head(&mod->state_wait); + mod->info = &adsp_info; + mod->name = adsp_info.module[i].name; + mod->id = adsp_info.module[i].id; + if (adsp_info.module[i].clk_name) + mod->clk = clk_get(NULL, adsp_info.module[i].clk_name); + else + mod->clk = NULL; + if (mod->clk && adsp_info.module[i].clk_rate) + clk_set_rate(mod->clk, adsp_info.module[i].clk_rate); + mod->verify_cmd = adsp_info.module[i].verify_cmd; + mod->patch_event = adsp_info.module[i].patch_event; + INIT_HLIST_HEAD(&mod->pmem_regions); + mod->pdev.name = adsp_info.module[i].pdev_name; + mod->pdev.id = -1; + adsp_info.id_to_module[i] = mod; + platform_device_register(&mod->pdev); + } + + msm_adsp_publish_cdevs(adsp_modules, count); + + rc = daldevice_attach(DALRPC_ADSPSVC_DEVICEID, DALRPC_ADSPSVC_PORT, + DALRPC_ADSPSVC_DEST, &adsp_info.handle); + if (rc) { + MM_ERR("adsp attach failed : %d\n", rc); + goto fail_dal_attach; + } + + adsp_info.cb_handle = dalrpc_alloc_cb(adsp_info.handle, + adsp_rtos_mtoa_cb, NULL); + if (adsp_info.cb_handle == NULL) { + MM_ERR("Callback registration failed\n"); + goto fail_allocate_cb; + } + + /* Get INIT_INFO */ + init_waitqueue_head(&adsp_info.init_info_wait); + msm_get_init_info(); + rc = wait_event_timeout(adsp_info.init_info_wait, + adsp_info.init_info_state == ADSP_STATE_INIT_INFO, + 10 * HZ); + if (!rc) { + MM_ERR("INIT_INFO failed\n"); + rc = -ETIMEDOUT; + } else + return 0; + +fail_allocate_cb: + daldevice_detach(adsp_info.handle); + adsp_info.handle = NULL; +fail_dal_attach: + enable_irq(adsp_info.int_adsp); + free_irq(adsp_info.int_adsp, 0); +fail_request_irq: + kfree(adsp_modules); + kfree(adsp_info.init_info_ptr); + kfree(adsp_info.raw_event); + return rc; +} + +#ifdef CONFIG_DEBUG_FS +static int get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (strict_strtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } + else + return -EINVAL; + } + return 0; +} + +static ssize_t adsp_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_debug("adsp debugfs opened\n"); + return 0; +} +static ssize_t adsp_debug_write(struct file *file, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + char *access_str = file->private_data; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + rc = copy_from_user(lbuf, buf, cnt); + if (rc) { + pr_info("Unable to copy data from user space\n"); + return -EFAULT; + } + lbuf[cnt] = '\0'; + + if (!strncmp(access_str, "write_log", 9)) { + if (get_parameters(lbuf, param, 1) == 0) { + switch (param[0]) { + case 1: + if (wdump <= 0) + wdump = 1; + pr_debug("write cmd to DSP(A->D) dump \ + started:%d\n", wdump); + break; + case 0: + if (wdump > 0) + wdump = 0; + pr_debug("Stop write cmd to \ + DSP(A->D):%d\n", wdump); + break; + default: + rc = -EINVAL; + break; + } + } else + rc = -EINVAL; + } else if (!strncmp(access_str, "read_log", 8)) { + if (get_parameters(lbuf, param, 1) == 0) { + switch (param[0]) { + case 1: + if (rdump <= 0) + rdump = 1; + pr_debug("write cmd from DSP(D->A) dump \ + started:%d\n", wdump); + break; + case 0: + if (rdump > 0) + rdump = 0; + pr_debug("Stop write cmd from \ + DSP(D->A):%d\n", wdump); + break; + default: + rc = -EINVAL; + break; + } + } else + rc = -EINVAL; + } else { + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else { + pr_err("%s: rc = %d\n", __func__, rc); + pr_info("\nWrong command: Use =>\n"); + pr_info("-------------------------\n"); + pr_info("To Start A->D:: echo \"1\">/sys/kernel/debug/ \ + adsp_cmd/write_log\n"); + pr_info("To Start D->A:: echo \"1\">/sys/kernel/debug/ \ + adsp_cmd/read_log\n"); + pr_info("To Stop A->D:: echo \"0\">/sys/kernel/debug/ \ + adsp_cmd/write_log\n"); + pr_info("To Stop D->A:: echo \"0\">/sys/kernel/debug/ \ + adsp_cmd/read_log\n"); + pr_info("------------------------\n"); + } + + return rc; +} +#endif + +static struct platform_driver msm_adsp_driver = { + .probe = msm_adsp_probe, + .driver = { + .owner = THIS_MODULE, + }, +}; + +static char msm_adsp_driver_name[] = "msm_adsp"; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations adsp_debug_fops = { + .write = adsp_debug_write, + .open = adsp_debug_open, +}; +#endif + +static int __init adsp_init(void) +{ + int rc; + +#ifdef CONFIG_DEBUG_FS + dentry_adsp = debugfs_create_dir("adsp_cmd", 0); + if (!IS_ERR(dentry_adsp)) { + dentry_wdata = debugfs_create_file("write_log", \ + S_IFREG | S_IRUGO, dentry_adsp, + (void *) "write_log" , &adsp_debug_fops); + dentry_rdata = debugfs_create_file("read_log", \ + S_IFREG | S_IRUGO, dentry_adsp, + (void *) "read_log", &adsp_debug_fops); + } +#endif /* CONFIG_DEBUG_FS */ + + msm_adsp_driver.driver.name = msm_adsp_driver_name; + rc = platform_driver_register(&msm_adsp_driver); + MM_INFO("%s -- %d\n", msm_adsp_driver_name, rc); + return rc; +} + +device_initcall(adsp_init); diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.h b/arch/arm/mach-msm/qdsp5v2/adsp.h new file mode 100644 index 00000000000..5aceff9777c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp.h @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_ADSP_H +#define _ARCH_ARM_MACH_MSM_ADSP_H + +#include +#include +#include +#include +#include + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len); +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len); +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr); + +int adsp_vfe_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_lpm_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_video_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); + + +struct adsp_event; + +int adsp_vfe_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + +int adsp_jpeg_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + + +struct adsp_module_info { + const char *name; + const char *pdev_name; + uint32_t id; + const char *clk_name; + unsigned long clk_rate; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +#define ADSP_EVENT_MAX_SIZE 496 +#define EVENT_LEN 12 +#define EVENT_MSG_ID ((uint16_t)~0) + +struct adsp_event { + struct list_head list; + uint32_t size; /* always in bytes */ + uint16_t msg_id; + uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */ + int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ + union { + uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; + uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; + } data; +}; + +#define DALRPC_ADSPSVC_DEVICEID 0x0200009A +#define DALRPC_ADSPSVC_DEST SMD_APPS_MODEM +#define DALRPC_ADSPSVC_PORT "DAL00" + +enum { + DALDEVICE_ADSP_CMD_IDX = DALDEVICE_FIRST_DEVICE_API_IDX, +}; + +struct adsp_rtos_atom_cmd { + uint32_t cmd; + uint32_t proc_id; + uint32_t module; + void *cb_handle; +}; + +enum rpc_adsp_rtos_proc_type { + RPC_ADSP_RTOS_PROC_NONE = 0, + RPC_ADSP_RTOS_PROC_MODEM = 1, + RPC_ADSP_RTOS_PROC_APPS = 2, +}; + +enum { + RPC_ADSP_RTOS_CMD_REGISTER_APP, + RPC_ADSP_RTOS_CMD_ENABLE, + RPC_ADSP_RTOS_CMD_DISABLE, + RPC_ADSP_RTOS_CMD_KERNEL_COMMAND, + RPC_ADSP_RTOS_CMD_16_COMMAND, + RPC_ADSP_RTOS_CMD_32_COMMAND, + RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, + RPC_ADSP_RTOS_CMD_REMOTE_EVENT, + RPC_ADSP_RTOS_CMD_SET_STATE, + RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT, + RPC_ADSP_RTOS_CMD_GET_INIT_INFO, +}; + +enum rpc_adsp_rtos_mod_status_type { + RPC_ADSP_RTOS_MOD_READY, + RPC_ADSP_RTOS_MOD_DISABLE, + RPC_ADSP_RTOS_SERVICE_RESET, + RPC_ADSP_RTOS_CMD_FAIL, + RPC_ADSP_RTOS_CMD_SUCCESS, + RPC_ADSP_RTOS_INIT_INFO, + RPC_ADSP_RTOS_DISABLE_FAIL, +}; + +enum qdsp_image_type { + QDSP_IMAGE_COMBO, + QDSP_IMAGE_GAUDIO, + QDSP_IMAGE_QTV_LP, + QDSP_IMAGE_MAX, + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ + QDSP_IMAGE_32BIT_DUMMY = 0x10000 +}; + +struct adsp_rtos_mp_mtoa_header_type { + enum rpc_adsp_rtos_mod_status_type event; + uint32_t version; + enum rpc_adsp_rtos_proc_type proc_id; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Event Info*/ +struct adsp_rtos_mp_mtoa_type { + uint32_t module; + uint32_t image; + uint32_t apps_okts; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Init Info */ +#define IMG_MAX 2 +#define ENTRIES_MAX 36 +#define MODULES_MAX 64 +#define QUEUES_MAX 64 + +struct queue_to_offset_type { + uint32_t queue; + uint32_t offset; +}; + +struct mod_to_queue_offsets { + uint32_t module; + uint32_t q_type; + uint32_t q_max_len; +}; + +struct adsp_rtos_mp_mtoa_init_info_type { + uint32_t image_count; + uint32_t num_queue_offsets; + struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX]; + uint32_t num_task_module_entries; + uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX]; + + uint32_t module_table_size; + uint32_t module_entries[MODULES_MAX]; + uint32_t mod_to_q_entries; + struct mod_to_queue_offsets mod_to_q_tbl[ENTRIES_MAX]; + /* + * queue_offsets[] is to store only queue_offsets + */ + uint32_t queue_offsets[IMG_MAX][QUEUES_MAX]; +}; + +struct adsp_rtos_mp_mtoa_s_type { + struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header; + + union { + struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet; + struct adsp_rtos_mp_mtoa_type mp_mtoa_packet; + } adsp_rtos_mp_mtoa_data; +}; + +struct adsp_info { + uint32_t send_irq; + uint32_t read_ctrl; + uint32_t write_ctrl; + + uint32_t max_msg16_size; + uint32_t max_msg32_size; + + uint32_t max_task_id; + uint32_t max_module_id; + uint32_t max_queue_id; + uint32_t max_image_id; + + /* for each image id, a map of queue id to offset */ + uint32_t **queue_offset; + + /* for each image id, a map of task id to module id */ + uint32_t **task_to_module; + + /* for each module id, map of module id to module */ + struct msm_adsp_module **id_to_module; + + uint32_t module_count; + struct adsp_module_info *module; + + /* stats */ + uint32_t events_received; + uint32_t event_backlog_max; + + /* rpc_client for init_info */ + struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr; + struct adsp_rtos_mp_mtoa_s_type *raw_event; + wait_queue_head_t init_info_wait; + unsigned init_info_state; + + void *handle; + void *cb_handle; + + /* Interrupt value */ + int int_adsp; +}; + +#define ADSP_STATE_DISABLED 0 +#define ADSP_STATE_ENABLING 1 +#define ADSP_STATE_ENABLED 2 +#define ADSP_STATE_DISABLING 3 +#define ADSP_STATE_INIT_INFO 4 + +struct msm_adsp_module { + struct mutex lock; + const char *name; + unsigned id; + struct adsp_info *info; + + struct msm_adsp_ops *ops; + void *driver_data; + + /* statistics */ + unsigned num_commands; + unsigned num_events; + + wait_queue_head_t state_wait; + unsigned state; + + struct platform_device pdev; + struct clk *clk; + int open_count; + + struct mutex pmem_regions_lock; + struct hlist_head pmem_regions; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); +extern int adsp_init_info(struct adsp_info *info); + +/* Value to indicate that a queue is not defined for a particular image */ +#define QDSP_RTOS_NO_QUEUE 0xfffffffe + +/* + * Constants used to communicate with the ADSP RTOS + */ +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU + +/* Combination of MUTEX and CMD bits to check if the DSP is busy */ +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U + +/* RTOS to Host processor command mask values */ +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U + +/* Combination of FLAG and COMMAND bits to check if MSG ready */ +#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U +#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U +#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U +#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U + +#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U + +#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U + +#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU + +#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU +#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U + +/* Base address of DSP and DSP hardware registers */ +#define QDSP_RAMC_OFFSET 0x400000 + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_driver.c b/arch/arm/mach-msm/qdsp5v2/adsp_driver.c new file mode 100644 index 00000000000..2a4e4ec6387 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_driver.c @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp.h" +#include +#include + +struct adsp_pmem_info { + int fd; + void *vaddr; +}; + +struct adsp_pmem_region { + struct hlist_node list; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + struct file *file; +}; + +struct adsp_device { + struct msm_adsp_module *module; + + spinlock_t event_queue_lock; + wait_queue_head_t event_wait; + struct list_head event_queue; + int abort; + + const char *name; + struct device *device; + struct cdev cdev; +}; + +static struct adsp_device *inode_to_device(struct inode *inode); + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = __v >= __r->vaddr && \ + __e <= __r->vaddr + __r->len; \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +static int adsp_pmem_check(struct msm_adsp_module *module, + void *vaddr, unsigned long len) +{ + struct adsp_pmem_region *region_elt; + struct hlist_node *node; + struct adsp_pmem_region t = { .vaddr = vaddr, .len = len }; + + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("module %s:" + " region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + module->name, + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int adsp_pmem_add(struct msm_adsp_module *module, + struct adsp_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct adsp_pmem_region *region; + int rc = -EINVAL; + + mutex_lock(&module->pmem_regions_lock); + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) { + rc = -ENOMEM; + goto end; + } + INIT_HLIST_NODE(®ion->list); + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = adsp_pmem_check(module, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + + hlist_add_head(®ion->list, &module->pmem_regions); +end: + mutex_unlock(&module->pmem_regions_lock); + return rc; +} + +static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr, + unsigned long len, struct adsp_pmem_region **region) +{ + struct hlist_node *node; + void *vaddr = *addr; + struct adsp_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (vaddr >= region_elt->vaddr && + vaddr < region_elt->vaddr + region_elt->len && + vaddr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("module %s: " + "multiple hits for vaddr %p, len %ld\n", + module->name, vaddr, len); + hlist_for_each_entry(region_elt, node, + &module->pmem_regions, list) { + if (vaddr >= region_elt->vaddr && + vaddr < region_elt->vaddr + region_elt->len && + vaddr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("%p, %ld --> %p\n", + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len) +{ + struct adsp_pmem_region *region; + void *vaddr = *addr; + unsigned long *paddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion); + if (ret) { + MM_ERR("not patching %s (paddr & kvaddr)," + " lookup (%p, %ld) failed\n", + module->name, vaddr, len); + return ret; + } + *paddr = region->paddr + (vaddr - region->vaddr); + *kvaddr = region->kvaddr + (vaddr - region->vaddr); + return 0; +} + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len) +{ + struct adsp_pmem_region *region; + void *vaddr = *addr; + unsigned long *paddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion); + if (ret) { + MM_ERR("not patching %s, lookup (%p, %ld) failed\n", + module->name, vaddr, len); + return ret; + } + + *paddr = region->paddr + (vaddr - region->vaddr); + return 0; +} + +static int adsp_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + /* call the per module verifier */ + if (module->verify_cmd) + return module->verify_cmd(module, queue_id, cmd_data, + cmd_size); + else + MM_INFO("no packet verifying function " + "for task %s\n", module->name); + return 0; +} + +static long adsp_write_cmd(struct adsp_device *adev, void __user *arg) +{ + struct adsp_command_t cmd; + unsigned char buf[256]; + void *cmd_data; + long rc; + + if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) + return -EFAULT; + + if (cmd.len > 256) { + cmd_data = kmalloc(cmd.len, GFP_USER); + if (!cmd_data) + return -ENOMEM; + } else { + cmd_data = buf; + } + + if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) { + rc = -EFAULT; + goto end; + } + + mutex_lock(&adev->module->pmem_regions_lock); + if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) { + MM_ERR("module %s: verify failed.\n", adev->module->name); + rc = -EINVAL; + goto end; + } + rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len); +end: + mutex_unlock(&adev->module->pmem_regions_lock); + + if (cmd.len > 256) + kfree(cmd_data); + + return rc; +} + +static int adsp_events_pending(struct adsp_device *adev) +{ + unsigned long flags; + int yes; + spin_lock_irqsave(&adev->event_queue_lock, flags); + yes = !list_empty(&adev->event_queue); + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + return yes || adev->abort; +} + +static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr, + struct adsp_pmem_region **region) +{ + struct hlist_node *node; + unsigned long paddr = (unsigned long)(*addr); + struct adsp_pmem_region *region_elt; + + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (paddr >= region_elt->paddr && + paddr < region_elt->paddr + region_elt->len) { + *region = region_elt; + return 0; + } + } + return -1; +} + +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr) +{ + struct adsp_pmem_region *region; + unsigned long paddr = (unsigned long)(*addr); + unsigned long *vaddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_paddr(module, addr, ®ion); + if (ret) { + MM_ERR("not patching %s, paddr %p lookup failed\n", + module->name, vaddr); + return ret; + } + + *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr); + return 0; +} + +static int adsp_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + /* call the per-module msg verifier */ + if (module->patch_event) + return module->patch_event(module, event); + return 0; +} + +static long adsp_get_event(struct adsp_device *adev, void __user *arg) +{ + unsigned long flags; + struct adsp_event *data = NULL; + struct adsp_event_t evt; + int timeout; + long rc = 0; + + if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t))) + return -EFAULT; + + timeout = (int)evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + adev->event_wait, adsp_events_pending(adev), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + adev->event_wait, adsp_events_pending(adev)); + } + if (rc < 0) + return rc; + + if (adev->abort) + return -ENODEV; + + spin_lock_irqsave(&adev->event_queue_lock, flags); + if (!list_empty(&adev->event_queue)) { + data = list_first_entry(&adev->event_queue, + struct adsp_event, list); + list_del(&data->list); + } + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + + if (!data) + return -EAGAIN; + + /* DSP messages are type 0; they may contain physical addresses */ + if (data->type == 0) + adsp_patch_event(adev->module, data); + + /* map adsp_event --> adsp_event_t */ + if (evt.len < data->size) { + rc = -ETOOSMALL; + goto end; + } + if (data->msg_id != EVENT_MSG_ID) { + if (copy_to_user((void *)(evt.data), data->data.msg16, + data->size)) { + rc = -EFAULT; + goto end; + } + } else { + if (copy_to_user((void *)(evt.data), data->data.msg32, + data->size)) { + rc = -EFAULT; + goto end; + } + } + + evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */ + evt.msg_id = data->msg_id; + evt.flags = data->is16; + evt.len = data->size; + if (copy_to_user(arg, &evt, sizeof(evt))) + rc = -EFAULT; +end: + kfree(data); + return rc; +} + +static int adsp_pmem_del(struct msm_adsp_module *module) +{ + struct hlist_node *node, *tmp; + struct adsp_pmem_region *region; + + mutex_lock(&module->pmem_regions_lock); + hlist_for_each_safe(node, tmp, &module->pmem_regions) { + region = hlist_entry(node, struct adsp_pmem_region, list); + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + mutex_unlock(&module->pmem_regions_lock); + BUG_ON(!hlist_empty(&module->pmem_regions)); + + return 0; +} + +static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct adsp_device *adev = filp->private_data; + + switch (cmd) { + case ADSP_IOCTL_ENABLE: + return msm_adsp_enable(adev->module); + + case ADSP_IOCTL_DISABLE: + return msm_adsp_disable(adev->module); + + case ADSP_IOCTL_DISABLE_EVENT_RSP: + return msm_adsp_disable_event_rsp(adev->module); + + case ADSP_IOCTL_DISABLE_ACK: + MM_ERR("ADSP_IOCTL_DISABLE_ACK is not implemented\n"); + break; + + case ADSP_IOCTL_WRITE_COMMAND: + return adsp_write_cmd(adev, (void __user *) arg); + + case ADSP_IOCTL_GET_EVENT: + return adsp_get_event(adev, (void __user *) arg); + + case ADSP_IOCTL_SET_CLKRATE: { + unsigned long clk_rate; + if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate))) + return -EFAULT; + return adsp_set_clkrate(adev->module, clk_rate); + } + + case ADSP_IOCTL_REGISTER_PMEM: { + struct adsp_pmem_info info; + if (copy_from_user(&info, (void *) arg, sizeof(info))) + return -EFAULT; + return adsp_pmem_add(adev->module, &info); + } + + case ADSP_IOCTL_ABORT_EVENT_READ: + adev->abort = 1; + wake_up(&adev->event_wait); + break; + + case ADSP_IOCTL_UNREGISTER_PMEM: + return adsp_pmem_del(adev->module); + + default: + break; + } + return -EINVAL; +} + +static int adsp_release(struct inode *inode, struct file *filp) +{ + struct adsp_device *adev = filp->private_data; + struct msm_adsp_module *module = adev->module; + int rc = 0; + + MM_INFO("release '%s'\n", adev->name); + + /* clear module before putting it to avoid race with open() */ + adev->module = NULL; + + rc = adsp_pmem_del(module); + + msm_adsp_put(module); + return rc; +} + +static void adsp_event(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct adsp_device *adev = driver_data; + struct adsp_event *event; + unsigned long flags; + + if (len > ADSP_EVENT_MAX_SIZE) { + MM_ERR("event too large (%d bytes)\n", len); + return; + } + + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) { + MM_ERR("cannot allocate buffer\n"); + return; + } + + if (id != EVENT_MSG_ID) { + event->type = 0; + event->is16 = 0; + event->msg_id = id; + event->size = len; + + getevent(event->data.msg16, len); + } else { + event->type = 1; + event->is16 = 1; + event->msg_id = id; + event->size = len; + getevent(event->data.msg32, len); + } + + spin_lock_irqsave(&adev->event_queue_lock, flags); + list_add_tail(&event->list, &adev->event_queue); + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + wake_up(&adev->event_wait); +} + +static struct msm_adsp_ops adsp_ops = { + .event = adsp_event, +}; + +static int adsp_open(struct inode *inode, struct file *filp) +{ + struct adsp_device *adev; + int rc; + + rc = nonseekable_open(inode, filp); + if (rc < 0) + return rc; + + adev = inode_to_device(inode); + if (!adev) + return -ENODEV; + + MM_INFO("open '%s'\n", adev->name); + + rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev); + if (rc) + return rc; + + MM_INFO("opened module '%s' adev %p\n", adev->name, adev); + filp->private_data = adev; + adev->abort = 0; + INIT_HLIST_HEAD(&adev->module->pmem_regions); + mutex_init(&adev->module->pmem_regions_lock); + + return 0; +} + +static unsigned adsp_device_count; +static struct adsp_device *adsp_devices; + +static struct adsp_device *inode_to_device(struct inode *inode) +{ + unsigned n = MINOR(inode->i_rdev); + if (n < adsp_device_count) { + if (adsp_devices[n].device) + return adsp_devices + n; + } + return NULL; +} + +static dev_t adsp_devno; +static struct class *adsp_class; + +static const struct file_operations adsp_fops = { + .owner = THIS_MODULE, + .open = adsp_open, + .unlocked_ioctl = adsp_ioctl, + .release = adsp_release, +}; + +static void adsp_create(struct adsp_device *adev, const char *name, + struct device *parent, dev_t devt) +{ + struct device *dev; + int rc; + + dev = device_create(adsp_class, parent, devt, "%s", name); + if (IS_ERR(dev)) + return; + + init_waitqueue_head(&adev->event_wait); + INIT_LIST_HEAD(&adev->event_queue); + spin_lock_init(&adev->event_queue_lock); + + cdev_init(&adev->cdev, &adsp_fops); + adev->cdev.owner = THIS_MODULE; + + rc = cdev_add(&adev->cdev, devt, 1); + if (rc < 0) { + device_destroy(adsp_class, devt); + } else { + adev->device = dev; + adev->name = name; + } +} + +void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n) +{ + int rc; + + adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL); + if (!adsp_devices) + return; + + adsp_class = class_create(THIS_MODULE, "adsp"); + if (IS_ERR(adsp_class)) + goto fail_create_class; + + rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp"); + if (rc < 0) + goto fail_alloc_region; + + adsp_device_count = n; + for (n = 0; n < adsp_device_count; n++) { + adsp_create(adsp_devices + n, + modules[n].name, &modules[n].pdev.dev, + MKDEV(MAJOR(adsp_devno), n)); + } + + return; + +fail_alloc_region: + class_unregister(adsp_class); +fail_create_class: + kfree(adsp_devices); +} diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_info.c b/arch/arm/mach-msm/qdsp5v2/adsp_info.c new file mode 100644 index 00000000000..40263673eb2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_info.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adsp.h" + +/* Firmware modules */ +#define QDSP_MODULE_KERNEL 0x0106dd4e +#define QDSP_MODULE_AFETASK 0x0106dd6f +#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70 +#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71 +#define QDSP_MODULE_AUDPPTASK 0x0106dd72 +#define QDSP_MODULE_VIDEOTASK 0x0106dd73 +#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74 +#define QDSP_MODULE_PCM_DEC 0x0106dd75 +#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76 +#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77 +#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78 +#define QDSP_MODULE_HOSTPCM 0x0106dd79 +#define QDSP_MODULE_DTMF 0x0106dd7a +#define QDSP_MODULE_AUDRECTASK 0x0106dd7b +#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c +#define QDSP_MODULE_SBC_ENC 0x0106dd7d +#define QDSP_MODULE_VOC_UMTS 0x0106dd9a +#define QDSP_MODULE_VOC_CDMA 0x0106dd98 +#define QDSP_MODULE_VOC_PCM 0x0106dd7f +#define QDSP_MODULE_VOCENCTASK 0x0106dd80 +#define QDSP_MODULE_VOCDECTASK 0x0106dd81 +#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82 +#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83 +#define QDSP_MODULE_VFETASK 0x0106dd84 +#define QDSP_MODULE_WAV_ENC 0x0106dd85 +#define QDSP_MODULE_AACLC_ENC 0x0106dd86 +#define QDSP_MODULE_VIDEO_AMR 0x0106dd87 +#define QDSP_MODULE_VOC_AMR 0x0106dd88 +#define QDSP_MODULE_VOC_EVRC 0x0106dd89 +#define QDSP_MODULE_VOC_13K 0x0106dd8a +#define QDSP_MODULE_VOC_FGV 0x0106dd8b +#define QDSP_MODULE_DIAGTASK 0x0106dd8c +#define QDSP_MODULE_JPEGTASK 0x0106dd8d +#define QDSP_MODULE_LPMTASK 0x0106dd8e +#define QDSP_MODULE_QCAMTASK 0x0106dd8f +#define QDSP_MODULE_MODMATHTASK 0x0106dd90 +#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91 +#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92 +#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93 +#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94 +#define QDSP_MODULE_MIDI 0x0106dd95 +#define QDSP_MODULE_GAUDIO 0x0106dd96 +#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97 +#define QDSP_MODULE_VIDEO_AAC_VOC_TURBO 0x01089f77 +#define QDSP_MODULE_VIDEO_AMR_TURBO 0x01089f78 +#define QDSP_MODULE_WM_TURBO_MODE 0x01089f79 +#define QDSP_MODULE_VDEC_LP_MODE_TURBO 0x01089f7a +#define QDSP_MODULE_AUDREC0TASK 0x0109696f +#define QDSP_MODULE_AUDREC1TASK 0x01096970 +#define QDSP_MODULE_AUDREC2TASK 0x010a2f59 +#define QDSP_MODULE_MAX 0x7fffffff + + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ +#define QDSP_MODULE_32BIT_DUMMY 0x10000 + +static uint32_t *qdsp_task_to_module[IMG_MAX]; +static uint32_t *qdsp_queue_offset_table[IMG_MAX]; + +#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \ + .clk_name = clkname, .clk_rate = clkrate, \ + .verify_cmd = verify_cmd_func, .patch_event = patch_event_func } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPLAY1TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPLAY2TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPLAY3TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AFETASK , NULL, 0, NULL, NULL), + QDSP_MODULE(AUDREC0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDREC2TASK, NULL, 0, NULL, NULL), +}; + +int adsp_init_info(struct adsp_info *info) +{ + uint32_t img_num; + + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + for (img_num = 0; img_num < IMG_MAX; img_num++) + qdsp_queue_offset_table[img_num] = + &info->init_info_ptr->queue_offsets[img_num][0]; + + for (img_num = 0; img_num < IMG_MAX; img_num++) + qdsp_task_to_module[img_num] = + &info->init_info_ptr->task_to_module_tbl[img_num][0]; + info->max_task_id = ENTRIES_MAX; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_MAX_NUM_QUEUES; + info->max_image_id = 0; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5v2/afe.c b/arch/arm/mach-msm/qdsp5v2/afe.c new file mode 100644 index 00000000000..20c9898ef8f --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/afe.c @@ -0,0 +1,534 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AFE_MAX_TIMEOUT 500 /* 500 ms */ +#define AFE_MAX_CLNT 6 /* 6 HW path defined so far */ +#define GETDEVICEID(x) ((x) - 1) + +struct msm_afe_state { + struct msm_adsp_module *mod; + struct msm_adsp_ops adsp_ops; + struct mutex lock; + u8 in_use; + u8 codec_config[AFE_MAX_CLNT]; + wait_queue_head_t wait; + u8 aux_conf_flag; +}; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_afelb; +#endif + + +static struct msm_afe_state the_afe_state; + +#define afe_send_queue(afe, cmd, len) \ + msm_adsp_write(afe->mod, QDSP_apuAfeQueue, \ + cmd, len) + +static void afe_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct msm_afe_state *afe = data; + + MM_DBG("msg_id %d \n", id); + + switch (id) { + case AFE_APU_MSG_CODEC_CONFIG_ACK: { + struct afe_msg_codec_config_ack afe_ack; + getevent(&afe_ack, AFE_APU_MSG_CODEC_CONFIG_ACK_LEN); + MM_DBG("%s: device_id: %d device activity: %d\n", __func__, + afe_ack.device_id, afe_ack.device_activity); + if (afe_ack.device_activity == AFE_MSG_CODEC_CONFIG_DISABLED) + afe->codec_config[GETDEVICEID(afe_ack.device_id)] = 0; + else + afe->codec_config[GETDEVICEID(afe_ack.device_id)] = + afe_ack.device_activity; + + wake_up(&afe->wait); + break; + } + case AFE_APU_MSG_VOC_TIMING_SUCCESS: + MM_INFO("Received VOC_TIMING_SUCCESS message from AFETASK\n"); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable/disable(audpptask)"); + break; + default: + MM_ERR("unexpected message from afe \n"); + } + + return; +} + +static void afe_dsp_codec_config(struct msm_afe_state *afe, + u8 path_id, u8 enable, struct msm_afe_config *config) +{ + struct afe_cmd_codec_config cmd; + + MM_DBG("%s() %p\n", __func__, config); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD; + cmd.device_id = path_id; + cmd.activity = enable; + if (config) { + MM_DBG("%s: sample_rate %x ch mode %x vol %x\n", + __func__, config->sample_rate, + config->channel_mode, config->volume); + cmd.sample_rate = config->sample_rate; + cmd.channel_mode = config->channel_mode; + cmd.volume = config->volume; + } + afe_send_queue(afe, &cmd, sizeof(cmd)); +} +/* Function is called after afe module been enabled */ +void afe_loopback(int enable) +{ + struct afe_cmd_loopback cmd; + struct msm_afe_state *afe; + + afe = &the_afe_state; + MM_DBG("enable %d\n", enable); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_LOOPBACK; + if (enable) + cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND; + + afe_send_queue(afe, &cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(afe_loopback); + +void afe_ext_loopback(int enable, int rx_copp_id, int tx_copp_id) +{ + struct afe_cmd_ext_loopback cmd; + struct msm_afe_state *afe; + + afe = &the_afe_state; + MM_DBG("enable %d\n", enable); + if ((rx_copp_id == 0) && (tx_copp_id == 0)) { + afe_loopback(enable); + } else { + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_EXT_LOOPBACK; + cmd.source_id = tx_copp_id; + cmd.dst_id = rx_copp_id; + if (enable) + cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND; + + afe_send_queue(afe, &cmd, sizeof(cmd)); + } +} +EXPORT_SYMBOL(afe_ext_loopback); + +void afe_device_volume_ctrl(u16 device_id, u16 device_volume) +{ + struct afe_cmd_device_volume_ctrl cmd; + struct msm_afe_state *afe; + + afe = &the_afe_state; + MM_DBG("device 0x%4x volume 0x%4x\n", device_id, device_volume); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_DEVICE_VOLUME_CTRL; + cmd.device_id = device_id; + cmd.device_volume = device_volume; + afe_send_queue(afe, &cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(afe_device_volume_ctrl); + +int afe_enable(u8 path_id, struct msm_afe_config *config) +{ + struct msm_afe_state *afe = &the_afe_state; + int rc; + + MM_DBG("%s: path %d\n", __func__, path_id); + if ((GETDEVICEID(path_id) < 0) || (GETDEVICEID(path_id) > 5)) { + MM_ERR("Invalid path_id: %d\n", path_id); + return -EINVAL; + } + mutex_lock(&afe->lock); + if (!afe->in_use && !afe->aux_conf_flag) { + /* enable afe */ + rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe); + if (rc < 0) { + MM_ERR("%s: failed to get AFETASK module\n", __func__); + goto error_adsp_get; + } + rc = msm_adsp_enable(afe->mod); + if (rc < 0) + goto error_adsp_enable; + } + /* Issue codec config command */ + afe_dsp_codec_config(afe, path_id, 1, config); + rc = wait_event_timeout(afe->wait, + afe->codec_config[GETDEVICEID(path_id)], + msecs_to_jiffies(AFE_MAX_TIMEOUT)); + if (!rc) { + MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT); + rc = -ENODEV; + if (!afe->in_use) { + if (!afe->aux_conf_flag || + (afe->aux_conf_flag && + (path_id == AFE_HW_PATH_AUXPCM_RX || + path_id == AFE_HW_PATH_AUXPCM_TX))) { + /* clean up if there is no client */ + msm_adsp_disable(afe->mod); + msm_adsp_put(afe->mod); + afe->aux_conf_flag = 0; + afe->mod = NULL; + } + } + + } else { + rc = 0; + afe->in_use++; + } + + mutex_unlock(&afe->lock); + return rc; + +error_adsp_enable: + msm_adsp_put(afe->mod); + afe->mod = NULL; +error_adsp_get: + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_enable); + +int afe_config_fm_codec(int fm_enable, uint16_t source) +{ + struct afe_cmd_fm_codec_config cmd; + struct msm_afe_state *afe = &the_afe_state; + int rc = 0; + int i = 0; + unsigned short *ptrmem = (unsigned short *)&cmd; + + MM_INFO(" configure fm codec\n"); + mutex_lock(&afe->lock); + if (!afe->in_use) { + /* enable afe */ + rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe); + if (rc < 0) { + MM_ERR("%s: failed to get AFETASK module\n", __func__); + goto error_adsp_get; + } + rc = msm_adsp_enable(afe->mod); + if (rc < 0) + goto error_adsp_enable; + } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_FM_RX_ROUTING_CMD; + cmd.enable = fm_enable; + cmd.device_id = source; + + for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem) + MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem); + afe_send_queue(afe, &cmd, sizeof(cmd)); + + mutex_unlock(&afe->lock); + return rc; +error_adsp_enable: + msm_adsp_put(afe->mod); + afe->mod = NULL; +error_adsp_get: + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_config_fm_codec); + +int afe_config_fm_volume(uint16_t volume) +{ + struct afe_cmd_fm_volume_config cmd; + struct msm_afe_state *afe = &the_afe_state; + int rc = 0; + + MM_INFO(" configure fm volume\n"); + mutex_lock(&afe->lock); + if (!afe->in_use) { + /* enable afe */ + rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe); + if (rc < 0) { + MM_ERR("%s: failed to get AFETASK module\n", __func__); + goto error_adsp_get; + } + rc = msm_adsp_enable(afe->mod); + if (rc < 0) + goto error_adsp_enable; + } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_FM_PLAYBACK_VOLUME_CMD; + cmd.volume = volume; + + afe_send_queue(afe, &cmd, sizeof(cmd)); + + mutex_unlock(&afe->lock); + return rc; +error_adsp_enable: + msm_adsp_put(afe->mod); + afe->mod = NULL; +error_adsp_get: + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_config_fm_volume); + +int afe_config_fm_calibration_gain(uint16_t device_id, + uint16_t calibration_gain) +{ + struct afe_cmd_fm_calibgain_config cmd; + struct msm_afe_state *afe = &the_afe_state; + int rc = 0; + + MM_INFO("Configure for rx device = 0x%4x, gain = 0x%4x\n", device_id, + calibration_gain); + mutex_lock(&afe->lock); + if (!afe->in_use) { + /* enable afe */ + rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe); + if (rc < 0) { + MM_ERR("%s: failed to get AFETASK module\n", __func__); + goto error_adsp_get; + } + rc = msm_adsp_enable(afe->mod); + if (rc < 0) + goto error_adsp_enable; + } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_FM_CALIBRATION_GAIN_CMD; + cmd.device_id = device_id; + cmd.calibration_gain = calibration_gain; + + afe_send_queue(afe, &cmd, sizeof(cmd)); + + mutex_unlock(&afe->lock); + return rc; +error_adsp_enable: + msm_adsp_put(afe->mod); + afe->mod = NULL; +error_adsp_get: + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_config_fm_calibration_gain); + +int afe_config_aux_codec(int pcm_ctl_value, int aux_codec_intf_value, + int data_format_pad) +{ + struct afe_cmd_aux_codec_config cmd; + struct msm_afe_state *afe = &the_afe_state; + int rc = 0; + + MM_DBG(" configure aux codec \n"); + mutex_lock(&afe->lock); + if (!afe->in_use && !afe->aux_conf_flag) { + /* enable afe */ + rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe); + if (rc < 0) { + MM_ERR("%s: failed to get AFETASK module\n", __func__); + goto error_adsp_get; + } + rc = msm_adsp_enable(afe->mod); + if (rc < 0) + goto error_adsp_enable; + } + afe->aux_conf_flag = 1; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_AUX_CODEC_CONFIG_CMD; + cmd.dma_path_ctl = 0; + cmd.pcm_ctl = pcm_ctl_value; + cmd.eight_khz_int_mode = 0; + cmd.aux_codec_intf_ctl = aux_codec_intf_value; + cmd.data_format_padding_info = data_format_pad; + + afe_send_queue(afe, &cmd, sizeof(cmd)); + + mutex_unlock(&afe->lock); + return rc; +error_adsp_enable: + msm_adsp_put(afe->mod); + afe->mod = NULL; +error_adsp_get: + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_config_aux_codec); + +int afe_config_rmc_block(struct acdb_rmc_block *acdb_rmc) +{ + struct afe_cmd_cfg_rmc cmd; + struct msm_afe_state *afe = &the_afe_state; + int rc = 0; + int i = 0; + unsigned short *ptrmem = (unsigned short *)&cmd; + + MM_DBG(" configure rmc block\n"); + mutex_lock(&afe->lock); + if (!afe->in_use && !afe->mod) { + /* enable afe */ + rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe); + if (rc < 0) { + MM_DBG("%s: failed to get AFETASK module\n", __func__); + goto error_adsp_get; + } + rc = msm_adsp_enable(afe->mod); + if (rc < 0) + goto error_adsp_enable; + } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AFE_CMD_CFG_RMC_PARAMS; + + cmd.rmc_mode = acdb_rmc->rmc_enable; + cmd.rmc_ipw_length_ms = acdb_rmc->rmc_ipw_length_ms; + cmd.rmc_peak_length_ms = acdb_rmc->rmc_peak_length_ms; + cmd.rmc_init_pulse_length_ms = acdb_rmc->rmc_init_pulse_length_ms; + cmd.rmc_total_int_length_ms = acdb_rmc->rmc_total_int_length_ms; + cmd.rmc_rampupdn_length_ms = acdb_rmc->rmc_rampupdn_length_ms; + cmd.rmc_delay_length_ms = acdb_rmc->rmc_delay_length_ms; + cmd.rmc_detect_start_threshdb = acdb_rmc->rmc_detect_start_threshdb; + cmd.rmc_init_pulse_threshdb = acdb_rmc->rmc_init_pulse_threshdb; + + for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem) + MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem); + afe_send_queue(afe, &cmd, sizeof(cmd)); + + mutex_unlock(&afe->lock); + return rc; +error_adsp_enable: + msm_adsp_put(afe->mod); + afe->mod = NULL; +error_adsp_get: + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_config_rmc_block); + +int afe_disable(u8 path_id) +{ + struct msm_afe_state *afe = &the_afe_state; + int rc; + + mutex_lock(&afe->lock); + + BUG_ON(!afe->in_use); + MM_DBG("%s() path_id:%d codec state:%d\n", __func__, path_id, + afe->codec_config[GETDEVICEID(path_id)]); + afe_dsp_codec_config(afe, path_id, 0, NULL); + rc = wait_event_timeout(afe->wait, + !afe->codec_config[GETDEVICEID(path_id)], + msecs_to_jiffies(AFE_MAX_TIMEOUT)); + if (!rc) { + MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT); + rc = -1; + } else + rc = 0; + afe->in_use--; + MM_DBG("%s() in_use:%d \n", __func__, afe->in_use); + if (!afe->in_use) { + msm_adsp_disable(afe->mod); + msm_adsp_put(afe->mod); + afe->aux_conf_flag = 0; + afe->mod = NULL; + } + mutex_unlock(&afe->lock); + return rc; +} +EXPORT_SYMBOL(afe_disable); + + +#ifdef CONFIG_DEBUG_FS +static int afe_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_INFO("debug intf %s\n", (char *) file->private_data); + return 0; +} + +static ssize_t afe_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char cmd; + + if (get_user(cmd, ubuf)) + return -EFAULT; + + MM_INFO("%s %c\n", lb_str, cmd); + + if (!strcmp(lb_str, "afe_loopback")) { + switch (cmd) { + case '1': + afe_loopback(1); + break; + case '0': + afe_loopback(0); + break; + } + } + + return cnt; +} + +static const struct file_operations afe_debug_fops = { + .open = afe_debug_open, + .write = afe_debug_write +}; +#endif + +static int __init afe_init(void) +{ + struct msm_afe_state *afe = &the_afe_state; + + MM_INFO("AFE driver init\n"); + + memset(afe, 0, sizeof(struct msm_afe_state)); + afe->adsp_ops.event = afe_dsp_event; + mutex_init(&afe->lock); + init_waitqueue_head(&afe->wait); + +#ifdef CONFIG_DEBUG_FS + debugfs_afelb = debugfs_create_file("afe_loopback", + S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback", + &afe_debug_fops); +#endif + + return 0; +} + +static void __exit afe_exit(void) +{ + MM_INFO("AFE driver exit\n"); +#ifdef CONFIG_DEBUG_FS + if (debugfs_afelb) + debugfs_remove(debugfs_afelb); +#endif + if (the_afe_state.mod) + msm_adsp_put(the_afe_state.mod); + return; +} + +module_init(afe_init); +module_exit(afe_exit); + +MODULE_DESCRIPTION("MSM AFE driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c new file mode 100644 index 00000000000..733b7a178db --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c @@ -0,0 +1,991 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * sbc/pcm audio input driver + * Based on the pcm input driver in arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c + * + * Copyright (C) 2008 HTC Corporation + * Copyright (C) 2008 Google, Inc. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_SIZE (2052 * 2) +#define FRAME_SIZE_SBC (768 * 2) +#define MONO_DATA_SIZE (2048) +#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) +#define DMASZ (FRAME_SIZE * FRAME_NUM) + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t frame_num; + uint32_t frame_len; +}; + +struct audio_a2dp_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + + struct msm_adsp_module *audrec; + + struct audrec_session_info session_info; /*audrec session info*/ + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */ + uint32_t enc_type; + struct msm_audio_sbc_enc_config cfg; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + uint32_t mode; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + uint16_t source; /* Encoding source bit mask */ + uint32_t device_events; /* device events interested in */ + uint32_t dev_cnt; + spinlock_t dev_lock; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *msm_map; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int abort; /* set when error, like sample rate mismatch */ + char *build_id; +}; + +static struct audio_a2dp_in the_audio_a2dp_in; + +struct wav_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_a2dp; + unsigned char raw_bitstream[]; /* samples */ +}; + +struct sbc_frame { + uint16_t bit_rate_msw; + uint16_t bit_rate_lsw; + uint16_t frame_length; + uint16_t frame_num; + unsigned char raw_bitstream[]; /* samples */ +}; + +struct audio_frame { + union { + struct wav_frame wav; + struct sbc_frame sbc; + } a2dp; +} __attribute__((packed)); + +/* Audrec Queue command sent macro's */ +#define audrec_send_bitstreamqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) + +#define audrec_send_audrecqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +/* DSP command send functions */ +static int auda2dp_in_enc_config(struct audio_a2dp_in *audio, int enable); +static int auda2dp_in_param_config(struct audio_a2dp_in *audio); +static int auda2dp_in_mem_config(struct audio_a2dp_in *audio); +static int auda2dp_in_record_config(struct audio_a2dp_in *audio, int enable); +static int auda2dp_dsp_read_buffer(struct audio_a2dp_in *audio, + uint32_t read_cnt); + +static void auda2dp_in_get_dsp_frames(struct audio_a2dp_in *audio); + +static void auda2dp_in_flush(struct audio_a2dp_in *audio); + +static void a2dp_in_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio_a2dp_in *audio = (struct audio_a2dp_in *) private_data; + unsigned long flags; + + MM_DBG("evt_id = 0x%8x\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: { + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt++; + audio->source |= (0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((audio->running == 1) && (audio->enabled == 1)) + auda2dp_in_record_config(audio, 1); + + break; + } + case AUDDEV_EVT_DEV_RLS: { + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt--; + audio->source &= ~(0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if (!audio->running || !audio->enabled) + break; + + /* Turn of as per source */ + if (audio->source) + auda2dp_in_record_config(audio, 1); + else + /* Turn off all */ + auda2dp_in_record_config(audio, 0); + + break; + } + case AUDDEV_EVT_FREQ_CHG: { + MM_DBG("Encoder Driver got sample rate change event\n"); + MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate); + MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type); + MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id); + if (audio->running == 1) { + /* Stop Recording sample rate does not match + with device sample rate */ + if (evt_payload->freq_info.sample_rate != + audio->samp_rate) { + auda2dp_in_record_config(audio, 0); + audio->abort = 1; + wake_up(&audio->wait); + } + } + break; + } + default: + MM_ERR("wrong event %d\n", evt_id); + break; + } +} + +/* ------------------- dsp preproc event handler--------------------- */ +static void audpreproc_dsp_event(void *data, unsigned id, void *msg) +{ + struct audio_a2dp_in *audio = data; + + switch (id) { + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg *err_msg = msg; + + MM_ERR("ERROR_MSG: stream id %d err idx %d\n", + err_msg->stream_id, err_msg->aud_preproc_err_idx); + /* Error case */ + wake_up(&audio->wait_enable); + break; + } + case AUDPREPROC_CMD_CFG_DONE_MSG: { + MM_DBG("CMD_CFG_DONE_MSG \n"); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg; + + MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + 0x%8x\n", enc_cfg_msg->stream_id, + enc_cfg_msg->rec_enc_type); + /* Encoder enable success */ + if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) + auda2dp_in_param_config(audio); + else { /* Encoder disable success */ + audio->running = 0; + auda2dp_in_record_config(audio, 0); + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n"); + auda2dp_in_mem_config(audio); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n"); + wake_up(&audio->wait_enable); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +/* ------------------- dsp audrec event handler--------------------- */ +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_a2dp_in *audio = data; + + switch (id) { + case AUDREC_CMD_MEM_CFG_DONE_MSG: { + MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n"); + audio->running = 1; + if (audio->dev_cnt > 0) + auda2dp_in_record_config(audio, 1); + break; + } + case AUDREC_FATAL_ERR_MSG: { + struct audrec_fatal_err_msg fatal_err_msg; + + getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN); + MM_ERR("FATAL_ERR_MSG: err id %d\n", + fatal_err_msg.audrec_err_id); + /* Error stop the encoder */ + audio->stopped = 1; + wake_up(&audio->wait); + break; + } + case AUDREC_UP_PACKET_READY_MSG: { + struct audrec_up_pkt_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packet_write_cnt_lsw, \ + pkt_ready_msg.audrec_packet_write_cnt_msw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_msw); + + auda2dp_in_get_dsp_frames(audio); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event: module audrectask\n"); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +static void auda2dp_in_get_dsp_frames(struct audio_a2dp_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->enc_type == ENC_TYPE_WAV) + audio->in[index].size = frame->a2dp.wav.frame_length; + else if (audio->enc_type == ENC_TYPE_SBC) { + audio->in[index].size = frame->a2dp.sbc.frame_length * + frame->a2dp.sbc.frame_num; + audio->in[index].frame_num = frame->a2dp.sbc.frame_num; + audio->in[index].frame_len = frame->a2dp.sbc.frame_length; + } + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + else + audio->in_count++; + + auda2dp_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + +static struct msm_adsp_ops audrec_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int auda2dp_in_enc_config(struct audio_a2dp_in *audio, int enable) +{ + struct audpreproc_audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + if (audio->build_id[17] == '1') { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2; + } else { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG; + } + cmd.stream_id = audio->enc_id; + + if (enable) + cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE; + else + cmd.audrec_enc_type &= ~(ENCODE_ENABLE); + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int auda2dp_in_param_config(struct audio_a2dp_in *audio) +{ + if (audio->enc_type == ENC_TYPE_WAV) { + struct audpreproc_audrec_cmd_parm_cfg_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + + cmd.aud_rec_samplerate_idx = audio->samp_rate; + cmd.aud_rec_stereo_mode = audio->channel_mode; + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); + } else if (audio->enc_type == ENC_TYPE_SBC) { + struct audpreproc_audrec_cmd_parm_cfg_sbc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + cmd.aud_rec_sbc_enc_param = + (audio->cfg.number_of_blocks << + AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_MASK) | + (audio->cfg.number_of_subbands << + AUDREC_SBC_ENC_PARAM_NUM_SUB_BANDS_MASK) | + (audio->cfg.mode << + AUDREC_SBC_ENC_PARAM_MODE_MASK) | + (audio->cfg.bit_allocation << + AUDREC_SBC_ENC_PARAM_BIT_ALLOC_MASK); + cmd.aud_rec_sbc_bit_rate_msw = + (audio->cfg.bit_rate & 0xFFFF0000) >> 16; + cmd.aud_rec_sbc_bit_rate_lsw = + (audio->cfg.bit_rate & 0xFFFF); + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); + } + return 0; +} + +/* To Do: msm_snddev_route_enc(audio->enc_id); */ +static int auda2dp_in_record_config(struct audio_a2dp_in *audio, int enable) +{ + struct audpreproc_afe_cmd_audio_record_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG; + cmd.stream_id = audio->enc_id; + if (enable) + cmd.destination_activity = AUDIO_RECORDING_TURN_ON; + else + cmd.destination_activity = AUDIO_RECORDING_TURN_OFF; + + cmd.source_mix_mask = audio->source; + if (audio->enc_id == 2) { + if ((cmd.source_mix_mask & + INTERNAL_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) { + cmd.pipe_id = SOURCE_PIPE_1; + } + if (cmd.source_mix_mask & + AUDPP_A2DP_PIPE_SOURCE_MIX_MASK) + cmd.pipe_id |= SOURCE_PIPE_0; + } + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int auda2dp_in_mem_config(struct audio_a2dp_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD; + cmd.audrec_up_pkt_intm_count = 1; + cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16; + cmd.audrec_ext_pkt_start_addr_lsw = audio->phys; + cmd.audrec_ext_pkt_buf_number = FRAME_NUM; + + /* prepare buffer pointers: + * Wav: + * Mono: 1024 samples + 4 halfword header + * Stereo: 2048 samples + 4 halfword header + * SBC: + * 768 + 4 halfword header + */ + if (audio->enc_type == ENC_TYPE_SBC) { + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + 4; + data += (4 + (FRAME_SIZE_SBC/2)); + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8)); + } + } else if (audio->enc_type == ENC_TYPE_WAV) { + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + 4; + data += (4 + (audio->channel_mode ? 2048 : 1024)); + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8)); + } + } + + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +static int auda2dp_dsp_read_buffer(struct audio_a2dp_in *audio, + uint32_t read_cnt) +{ + struct up_audrec_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR; + cmd.audrec_up_curr_read_count_msw = read_cnt >> 16; + cmd.audrec_up_curr_read_count_lsw = read_cnt; + + return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int auda2dp_in_enable(struct audio_a2dp_in *audio) +{ + if (audio->enabled) + return 0; + + if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) { + MM_ERR("msm_adsp_enable(audpreproc) failed\n"); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + audpreproc_disable(audio->enc_id, audio); + return -ENODEV; + } + audio->enabled = 1; + auda2dp_in_enc_config(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int auda2dp_in_disable(struct audio_a2dp_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + auda2dp_in_enc_config(audio, 0); + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + audpreproc_disable(audio->enc_id, audio); + } + return 0; +} + +static void auda2dp_in_flush(struct audio_a2dp_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +/* ------------------- device --------------------- */ +static long auda2dp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_a2dp_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t freq; + /* Poll at 48KHz always */ + freq = 48000; + MM_DBG("AUDIO_START\n"); + rc = msm_snddev_request_freq(&freq, audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("sample rate configured %d sample rate requested %d\n", + freq, audio->samp_rate); + if (rc < 0) { + MM_DBG("sample rate can not be set, return code %d\n",\ + rc); + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + break; + } + /*update aurec session info in audpreproc layer*/ + audio->session_info.session_id = audio->enc_id; + audio->session_info.sampling_freq = audio->samp_rate; + audpreproc_update_audrec_info(&audio->session_info); + rc = auda2dp_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) { + rc = -ENODEV; + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + } else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + rc = auda2dp_in_disable(audio); + rc = msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + audio->stopped = 1; + audio->abort = 0; + break; + } + case AUDIO_FLUSH: { + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + auda2dp_in_flush(audio); + mutex_unlock(&audio->read_lock); + } + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if ((audio->enc_type == ENC_TYPE_SBC) && + (cfg.buffer_size != FRAME_SIZE_SBC)) + rc = -EINVAL; + else + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + if (audio->enc_type == ENC_TYPE_SBC) + cfg.buffer_size = FRAME_SIZE_SBC; + else + cfg.buffer_size = MONO_DATA_SIZE; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_SBC_ENC_CONFIG: { + if (copy_from_user(&audio->cfg, (void *) arg, + sizeof(audio->cfg))) { + rc = -EFAULT; + break; + } + audio->samp_rate = audio->cfg.sample_rate; + audio->channel_mode = audio->cfg.channels; + audio->enc_type = ENC_TYPE_SBC; + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.channel_count == 1) { + cfg.channel_count = AUDREC_CMD_MODE_MONO; + audio->buffer_size = MONO_DATA_SIZE; + } else if (cfg.channel_count == 2) { + cfg.channel_count = AUDREC_CMD_MODE_STEREO; + audio->buffer_size = STEREO_DATA_SIZE; + } else { + rc = -EINVAL; + break; + } + audio->samp_rate = cfg.sample_rate; + audio->channel_mode = cfg.channel_count; + audio->enc_type = ENC_TYPE_WAV; + break; + } + case AUDIO_GET_SBC_ENC_CONFIG: { + struct msm_audio_sbc_enc_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.bit_allocation = audio->cfg.bit_allocation; + cfg.mode = audio->cfg.mode; + cfg.number_of_subbands = audio->cfg.number_of_subbands; + cfg.number_of_blocks = audio->cfg.number_of_blocks; + cfg.sample_rate = audio->samp_rate; + cfg.channels = audio->channel_mode; + cfg.bit_rate = audio->cfg.bit_rate; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_count = FRAME_NUM; + cfg.sample_rate = audio->samp_rate; + if (audio->channel_mode == AUDREC_CMD_MODE_MONO) { + cfg.channel_count = 1; + cfg.buffer_size = MONO_DATA_SIZE; + } else { + cfg.channel_count = 2; + cfg.buffer_size = STEREO_DATA_SIZE; + } + cfg.type = ENC_TYPE_WAV; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->enc_id, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t auda2dp_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_a2dp_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + uint32_t f_len = 0, f_num = 0; + int i = 0; + + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->abort); + + if (rc < 0) + break; + + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } + + if (audio->abort) { + rc = -EPERM; /* Not permitted due to abort */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + if (count >= size) { + if (audio->enc_type == ENC_TYPE_SBC && + (audio->in[index].frame_len % 2)) { + f_len = audio->in[index].frame_len; + f_num = audio->in[index].frame_num; + for (i = 0; i < f_num; i++) { + if (copy_to_user(&buf[i * f_len], + (uint8_t *) (data + (i * (f_len + 1))), + f_len)) { + rc = -EFAULT; + break; + } + } + } else { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + } else { + MM_ERR("short read\n"); + break; + } + } + mutex_unlock(&audio->read_lock); + if (buf > start) + return buf - start; + + return rc; +} + +static ssize_t auda2dp_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int auda2dp_in_release(struct inode *inode, struct file *file) +{ + struct audio_a2dp_in *audio = file->private_data; + + mutex_lock(&audio->lock); + /* with draw frequency for session + incase not stopped the driver */ + msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX, + AUDDEV_CLNT_ENC); + auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id); + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + auda2dp_in_disable(audio); + auda2dp_in_flush(audio); + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->opened = 0; + if (audio->data) { + iounmap(audio->msm_map); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +static int auda2dp_in_open(struct inode *inode, struct file *file) +{ + struct audio_a2dp_in *audio = &the_audio_a2dp_in; + int rc; + int encid; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (audio->phys) { + audio->msm_map = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->msm_map)) { + MM_ERR("could not map the phys address to kernel" + "space\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto done; + } + audio->data = (u8 *)audio->msm_map; + } else { + MM_ERR("could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + rc = -EACCES; + MM_ERR("Non tunnel encoding is not supported\n"); + goto done; + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + MM_DBG("Opened for Tunnel mode encoding\n"); + } else { + rc = -EACCES; + goto done; + } + /* Settings will be re-config at AUDIO_SET_CONFIG/SBC_ENC_CONFIG, + * but at least we need to have initial config + */ + audio->channel_mode = AUDREC_CMD_MODE_MONO; + audio->buffer_size = FRAME_SIZE_SBC; + audio->samp_rate = 48000; + audio->enc_type = ENC_TYPE_SBC | audio->mode; + audio->cfg.bit_allocation = AUDIO_SBC_BA_SNR; + audio->cfg.mode = AUDIO_SBC_MODE_JSTEREO; + audio->cfg.number_of_subbands = AUDIO_SBC_BANDS_8; + audio->cfg.number_of_blocks = AUDIO_SBC_BLOCKS_16; + audio->cfg.bit_rate = 320000; /* max 512kbps(mono), 320kbs(others) */ + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_adsp_ops, audio); + + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->stopped = 0; + audio->source = 0; + audio->abort = 0; + auda2dp_in_flush(audio); + audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_FREQ_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_ENC, audio->enc_id, + a2dp_in_listener, (void *) audio); + if (rc) { + MM_ERR("failed to register device event listener\n"); + goto evt_error; + } + audio->build_id = socinfo_get_build_id(); + MM_DBG("Modem build id = %s\n", audio->build_id); + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_a2dp_in_fops = { + .owner = THIS_MODULE, + .open = auda2dp_in_open, + .release = auda2dp_in_release, + .read = auda2dp_in_read, + .write = auda2dp_in_write, + .unlocked_ioctl = auda2dp_in_ioctl, +}; + +struct miscdevice audio_a2dp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_a2dp_in", + .fops = &audio_a2dp_in_fops, +}; + +static int __init auda2dp_in_init(void) +{ + mutex_init(&the_audio_a2dp_in.lock); + mutex_init(&the_audio_a2dp_in.read_lock); + spin_lock_init(&the_audio_a2dp_in.dsp_lock); + spin_lock_init(&the_audio_a2dp_in.dev_lock); + init_waitqueue_head(&the_audio_a2dp_in.wait); + init_waitqueue_head(&the_audio_a2dp_in.wait_enable); + return misc_register(&audio_a2dp_in_misc); +} + +device_initcall(auda2dp_in_init); + +MODULE_DESCRIPTION("MSM SBC encode driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_aac.c b/arch/arm/mach-msm/qdsp5v2/audio_aac.c new file mode 100644 index 00000000000..05bca03d6a9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_aac.c @@ -0,0 +1,2037 @@ +/* + * aac audio decoder device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define BUFSZ 32768 +#define DMASZ (BUFSZ * 2) +#define BUFSZ_MIN 4096 +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AAC 5 + +#define PCM_BUFSZ_MIN 9600 /* Hold one stereo AAC frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDAAC_METAFIELD_MASK 0xFFFF0000 +#define AUDAAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDAAC_EOS_FLG_MASK 0x01 +#define AUDAAC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDAAC_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDAAC_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define BITSTREAM_ERROR_THRESHOLD_VALUE 0x1 /* DEFAULT THRESHOLD VALUE */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audaac_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audaac_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + struct msm_audio_aac_config aac_config; + + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audaac_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + uint32_t device_events; + + struct msm_audio_bitstream_info stream_info; + struct msm_audio_bitstream_error_info bitstream_error_info; + uint32_t bitstream_error_threshold_value; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_error_threshold_config(struct audio *audio); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audaac_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +static void aac_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} + +static void audaac_bitstream_error_info(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload e_payload; + + if (payload[0] != AUDDEC_DEC_AAC) { + MM_ERR("Unexpected bitstream error info from DSP:\ + Invalid decoder\n"); + return; + } + + /* get stream info from DSP msg */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + audio->bitstream_error_info.dec_id = payload[0]; + audio->bitstream_error_info.err_msg_indicator = payload[1]; + audio->bitstream_error_info.err_type = payload[2]; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_ERR("bit_stream_error_type=%d error_count=%d\n", + audio->bitstream_error_info.err_type, (0x0000FFFF & + audio->bitstream_error_info.err_msg_indicator)); + + /* send event to ARM to notify error info coming */ + e_payload.error_info = audio->bitstream_error_info; + audaac_post_event(audio, AUDIO_EVENT_BITSTREAM_ERROR_INFO, e_payload); +} + +static void audaac_update_stream_info(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload e_payload; + + /* get stream info from DSP msg */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + audio->stream_info.codec_type = AUDIO_CODEC_TYPE_AAC; + audio->stream_info.chan_info = (0x0000FFFF & payload[1]); + audio->stream_info.sample_rate = (0x0000FFFF & payload[2]); + audio->stream_info.bit_stream_info = (0x0000FFFF & payload[3]); + audio->stream_info.bit_rate = payload[4]; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_DBG("chan_info=%d, sample_rate=%d, bit_stream_info=%d\n", + audio->stream_info.chan_info, + audio->stream_info.sample_rate, + audio->stream_info.bit_stream_info); + + /* send event to ARM to notify steam info coming */ + e_payload.stream_info = audio->stream_info; + audaac_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); +} +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case AUDPLAY_UP_STREAM_INFO: + if ((msg[1] & AUDPLAY_STREAM_INFO_MSG_MASK) == + AUDPLAY_STREAM_INFO_MSG_MASK) { + audaac_bitstream_error_info(audio, msg); + } else { + audaac_update_stream_info(audio, msg); + } + break; + + case AUDPLAY_UP_OUTPORT_FLUSH_ACK: + MM_DBG("OUTPORT_FLUSH_ACK\n"); + audio->rflush = 0; + wake_up(&audio->read_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audplay_error_threshold_config(audio); + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_aac = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id,\ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_aac cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.format = audio->aac_config.format; + cmd.audio_object = audio->aac_config.audio_object; + cmd.ep_config = audio->aac_config.ep_config; + cmd.aac_section_data_resilience_flag = + audio->aac_config.aac_section_data_resilience_flag; + cmd.aac_scalefactor_data_resilience_flag = + audio->aac_config.aac_scalefactor_data_resilience_flag; + cmd.aac_spectral_data_resilience_flag = + audio->aac_config.aac_spectral_data_resilience_flag; + cmd.sbr_on_flag = audio->aac_config.sbr_on_flag; + cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag; + cmd.channel_configuration = audio->aac_config.channel_configuration; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDAAC_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + /* AAC frame size */ + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % 1024) + + (audio->mfield ? 24 : 0); + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_outport_flush(struct audio *audio) +{ + struct audplay_cmd_outport_flush op_flush_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + op_flush_cmd.cmd_id = AUDPLAY_CMD_OUTPORT_FLUSH; + (void)audplay_send_queue0(audio, &op_flush_cmd, sizeof(op_flush_cmd)); +} + +static void audplay_error_threshold_config(struct audio *audio) +{ + union audplay_cmd_channel_info ch_cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + ch_cfg_cmd.thr_update.cmd_id = AUDPLAY_CMD_CHANNEL_INFO; + ch_cfg_cmd.thr_update.threshold_update = AUDPLAY_ERROR_THRESHOLD_ENABLE; + ch_cfg_cmd.thr_update.threshold_value = + audio->bitstream_error_threshold_value; + (void)audplay_send_queue0(audio, &ch_cfg_cmd, sizeof(ch_cfg_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static int audaac_validate_usr_config(struct msm_audio_aac_config *config) +{ + int ret_val = -1; + + if (config->format != AUDIO_AAC_FORMAT_ADTS && + config->format != AUDIO_AAC_FORMAT_RAW && + config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW && + config->format != AUDIO_AAC_FORMAT_LOAS) + goto done; + + if (config->audio_object != AUDIO_AAC_OBJECT_LC && + config->audio_object != AUDIO_AAC_OBJECT_LTP && + config->audio_object != AUDIO_AAC_OBJECT_BSAC && + config->audio_object != AUDIO_AAC_OBJECT_ERLC) + goto done; + + if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) { + if (config->ep_config > 3) + goto done; + if (config->aac_scalefactor_data_resilience_flag != + AUDIO_AAC_SCA_DATA_RES_OFF && + config->aac_scalefactor_data_resilience_flag != + AUDIO_AAC_SCA_DATA_RES_ON) + goto done; + if (config->aac_section_data_resilience_flag != + AUDIO_AAC_SEC_DATA_RES_OFF && + config->aac_section_data_resilience_flag != + AUDIO_AAC_SEC_DATA_RES_ON) + goto done; + if (config->aac_spectral_data_resilience_flag != + AUDIO_AAC_SPEC_DATA_RES_OFF && + config->aac_spectral_data_resilience_flag != + AUDIO_AAC_SPEC_DATA_RES_ON) + goto done; + } else { + config->aac_section_data_resilience_flag = + AUDIO_AAC_SEC_DATA_RES_OFF; + config->aac_scalefactor_data_resilience_flag = + AUDIO_AAC_SCA_DATA_RES_OFF; + config->aac_spectral_data_resilience_flag = + AUDIO_AAC_SPEC_DATA_RES_OFF; + } + +#ifndef CONFIG_AUDIO_AAC_PLUS + if (AUDIO_AAC_SBR_ON_FLAG_OFF != config->sbr_on_flag) + goto done; +#else + if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF && + config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON) + goto done; +#endif + +#ifndef CONFIG_AUDIO_ENHANCED_AAC_PLUS + if (AUDIO_AAC_SBR_PS_ON_FLAG_OFF != config->sbr_ps_on_flag) + goto done; +#else + if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF && + config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON) + goto done; +#endif + + if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR) + goto done; + + if (config->channel_configuration > 2) + goto done; + + ret_val = 0; + done: + return ret_val; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + +} + +static int audaac_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audaac_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audaac_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audaac_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audaac_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audaac_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audaac_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audaac_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audaac_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audaac_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audaac_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH running=%d\n", audio->running); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + + case AUDIO_OUTPORT_FLUSH: + MM_DBG("AUDIO_OUTPORT_FLUSH\n"); + audio->rflush = 1; + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audplay_outport_flush(audio); + rc = wait_event_interruptible(audio->read_wait, + !audio->rflush); + if (rc < 0) { + MM_ERR("AUDPLAY_OUTPORT_FLUSH interrupted\n"); + rc = -EINTR; + } + break; + + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + + if (config.channel_count == 1) { + config.channel_count = + AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = + AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->mfield = config.meta_field; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == + AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_AAC_CONFIG:{ + if (copy_to_user((void *)arg, &audio->aac_config, + sizeof(audio->aac_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_AAC_CONFIG:{ + struct msm_audio_aac_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + if (audaac_validate_usr_config(&usr_config) == 0) { + audio->aac_config = usr_config; + rc = 0; + } else + rc = -EINVAL; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if (config.pcm_feedback) { + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; + } + rc = 0; + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_STREAM_INFO:{ + if (audio->stream_info.sample_rate == 0) { + /* haven't received DSP stream event, + the stream info is not updated */ + rc = -EPERM; + break; + } + if (copy_to_user((void *)arg, &audio->stream_info, + sizeof(struct msm_audio_bitstream_info))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_BITSTREAM_ERROR_INFO:{ + if ((audio->bitstream_error_info.err_msg_indicator & + AUDPLAY_STREAM_INFO_MSG_MASK) == + AUDPLAY_STREAM_INFO_MSG_MASK) { + /* haven't received bitstream error info event, + the bitstream error info is not updated */ + rc = -EPERM; + break; + } + if (copy_to_user((void *)arg, &audio->bitstream_error_info, + sizeof(struct msm_audio_bitstream_error_info))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + case AUDIO_SET_ERR_THRESHOLD_VALUE: + if (copy_from_user(&audio->bitstream_error_threshold_value, + (void *)arg, sizeof(uint32_t))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} +/* Only useful in tunnel-mode */ +static int audaac_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("to read %d \n", count); + while (count > 0) { + rc = wait_event_interruptible_timeout(audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped) + || (audio->rflush), + msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS)); + + if (rc == 0) { + rc = -ETIMEDOUT; + break; + } else if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("no partial frame done reading\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x\n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + /* + * Force to exit while loop + * to prevent output thread + * sleep too long if data is not + * ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audaac_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + char *buf_ptr; + int rc = 0; + + MM_DBG("signal input EOS reserved=%d\n", audio->reserved); + if (audio->reserved) { + MM_DBG("Pass reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + audio->reserved = 0; + frame->used = 2; + audplay_send_data(audio, 0); + } + MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n", + audio->out[0].used, audio->out[1].used, audio->out_needed); + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDAAC_EOS_NONE; + unsigned dsize; + + unsigned short mfield_size = 0; + MM_DBG("cnt=%d\n", count); + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDAAC_EOS_FLG_OFFSET] & + AUDAAC_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDAAC_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDAAC_EOS_FLG_OFFSET] &= + ~AUDAAC_EOS_FLG_MASK; + } + /* Check EOS to see if */ + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", + audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition, + (int) buf, (int) start); + if (eos_condition == AUDAAC_EOS_SET) + rc = audaac_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audaac_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audaac_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audaac_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audaac_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audaac_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audaac_suspend(struct early_suspend *h) +{ + struct audaac_suspend_ctl *ctl = + container_of(h, struct audaac_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audaac_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audaac_resume(struct early_suspend *h) +{ + struct audaac_suspend_ctl *ctl = + container_of(h, struct audaac_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audaac_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audaac_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audaac_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].used %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audaac_debug_fops = { + .read = audaac_debug_read, + .open = audaac_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, index, offset = 0; + unsigned pmem_sz = DMASZ; + struct audaac_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_aac_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_AAC; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K); + if (audio->phys) { + audio->map_v_write = + ioremap(audio->phys, + pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = (u8 *)audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + audio->read_phys = allocate_contiguous_ebi_nomap(PCM_BUFSZ_MIN + * PCM_BUF_MAX_COUNT, SZ_4K); + if (!audio->read_phys) { + MM_ERR("could not allocate read buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->map_v_read = ioremap(audio->read_phys, + PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map read phys address, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + free_contiguous_memory_by_paddr(audio->read_phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->read_data = audio->map_v_read; + MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->read_phys, (int)audio->read_data); + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_aac, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + audio->pcm_buf_count = PCM_BUF_MAX_COUNT; + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) { + audio->in[index].data = audio->read_data + offset; + audio->in[index].addr = audio->read_phys + offset; + audio->in[index].size = PCM_BUFSZ_MIN; + audio->in[index].used = 0; + offset += PCM_BUFSZ_MIN; + } + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS; + audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC; + audio->aac_config.ep_config = 0; + audio->aac_config.aac_section_data_resilience_flag = + AUDIO_AAC_SEC_DATA_RES_OFF; + audio->aac_config.aac_scalefactor_data_resilience_flag = + AUDIO_AAC_SCA_DATA_RES_OFF; + audio->aac_config.aac_spectral_data_resilience_flag = + AUDIO_AAC_SPEC_DATA_RES_OFF; +#ifdef CONFIG_AUDIO_AAC_PLUS + audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON; +#else + audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_OFF; +#endif +#ifdef CONFIG_AUDIO_ENHANCED_AAC_PLUS + audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON; +#else + audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_OFF; +#endif + audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR; + audio->aac_config.channel_configuration = 2; + audio->vol_pan.volume = 0x2000; + audio->bitstream_error_threshold_value = + BITSTREAM_ERROR_THRESHOLD_VALUE; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + aac_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_aac_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audaac_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audaac_resume; + audio->suspend_ctl.node.suspend = audaac_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (index = 0; index < AUDAAC_EVENT_NUM; index++) { + e_node = kmalloc(sizeof(struct audaac_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } + memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info)); + memset(&audio->bitstream_error_info, 0, + sizeof(struct msm_audio_bitstream_info)); +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audaac_fsync +}; + +struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_aac_misc); +} + +static void __exit audio_exit(void) +{ + misc_deregister(&audio_aac_misc); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM AAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c b/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c new file mode 100644 index 00000000000..8aee9466da7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c @@ -0,0 +1,1482 @@ +/* + * aac audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_SIZE (772 * 2) /* 1536 bytes data */ +#define NT_FRAME_SIZE (780 * 2) /* 1536 bytes data + 24 meta field*/ +#define AAC_FRAME_SIZE 1536 +#define DMASZ (FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM (2) +#define META_OUT_SIZE (24) +#define META_IN_SIZE (14) +#define OUT_BUFFER_SIZE (32 * 1024 + META_OUT_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + +#define AUDPREPROC_AAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDPREPROC_AAC_EOS_FLG_MASK 0x01 +#define AUDPREPROC_AAC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_AAC_EOS_SET 0x1 /* EOS set in meta field */ + +#define PCM_CONFIG_UPDATE_FLAG_ENABLE -1 +#define PCM_CONFIG_UPDATE_FLAG_DISABLE 0 + +#define ENABLE_FLAG_VALUE -1 +#define DISABLE_FLAG_VALUE 0 + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int wflush; /*write flush */ + int rflush; /*read flush*/ + int out_frame_cnt; + + struct msm_adsp_module *audrec; + + /* configuration to use on next enable */ + uint32_t buffer_size; /* Frame size (36 bytes) */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t bit_rate; /* bit rate for AAC */ + uint32_t record_quality; /* record quality (bits/sample/channel) */ + uint32_t enc_type; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + uint32_t mode; + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; + + struct audrec_session_info session_info; /*audrec session info*/ + uint16_t source; /* Encoding source bit mask */ + uint32_t device_events; /* device events interested in */ + uint32_t dev_cnt; + spinlock_t dev_lock; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int abort; /* set when error, like sample rate mismatch */ + char *build_id; +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +struct aac_encoded_meta_in { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audrec_send_bitstreamqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) + +#define audrec_send_audrecqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +/* DSP command send functions */ +static int audaac_in_enc_config(struct audio_in *audio, int enable); +static int audaac_in_param_config(struct audio_in *audio); +static int audaac_in_mem_config(struct audio_in *audio); +static int audaac_in_record_config(struct audio_in *audio, int enable); +static int audaac_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); + +static void audaac_in_get_dsp_frames(struct audio_in *audio); +static int audpcm_config(struct audio_in *audio); +static void audaac_out_flush(struct audio_in *audio); +static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio); +static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed); +static void audaac_nt_in_get_dsp_frames(struct audio_in *audio); + +static void audaac_in_flush(struct audio_in *audio); + +static void aac_in_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio_in *audio = (struct audio_in *) private_data; + unsigned long flags; + + MM_DBG("evt_id = 0x%8x\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: { + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt++; + audio->source |= (0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((audio->running == 1) && (audio->enabled == 1) && + (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) + audaac_in_record_config(audio, 1); + + break; + } + case AUDDEV_EVT_DEV_RLS: { + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt--; + audio->source &= ~(0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((!audio->running) || (!audio->enabled)) + break; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + /* Turn of as per source */ + if (audio->source) + audaac_in_record_config(audio, 1); + else + /* Turn off all */ + audaac_in_record_config(audio, 0); + } + break; + } + case AUDDEV_EVT_FREQ_CHG: { + MM_DBG("Encoder Driver got sample rate change event\n"); + MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate); + MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type); + MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id); + if ((audio->running == 1) && (audio->enabled == 1)) { + /* Stop Recording sample rate does not match + with device sample rate */ + if (evt_payload->freq_info.sample_rate != + audio->samp_rate) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audaac_in_record_config(audio, 0); + audio->abort = 1; + wake_up(&audio->wait); + } + } + break; + } + default: + MM_ERR("wrong event %d\n", evt_id); + break; + } +} + +/* Convert Bit Rate to Record Quality field of DSP */ +static unsigned int bitrate_to_record_quality(unsigned int sample_rate, + unsigned int channel, unsigned int bit_rate) { + unsigned int temp; + + temp = sample_rate * channel; + MM_DBG(" sample rate * channel = %d \n", temp); + /* To represent in Q12 fixed format */ + temp = (bit_rate * 4096) / temp; + MM_DBG(" Record Quality = 0x%8x \n", temp); + return temp; +} + +/* ------------------- dsp preproc event handler--------------------- */ +static void audpreproc_dsp_event(void *data, unsigned id, void *msg) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg *err_msg = msg; + + MM_ERR("ERROR_MSG: stream id %d err idx %d\n", + err_msg->stream_id, err_msg->aud_preproc_err_idx); + /* Error case */ + wake_up(&audio->wait_enable); + break; + } + case AUDPREPROC_CMD_CFG_DONE_MSG: { + MM_DBG("CMD_CFG_DONE_MSG \n"); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg; + + MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + 0x%8x\n", enc_cfg_msg->stream_id, + enc_cfg_msg->rec_enc_type); + /* Encoder enable success */ + if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) { + if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audpreproc_cmd_cfg_routing_mode(audio); + } else { + audaac_in_param_config(audio); + } + } else { /* Encoder disable success */ + audio->running = 0; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audaac_in_record_config(audio, 0); + else + wake_up(&audio->wait_enable); + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audaac_in_mem_config(audio); + else + audpcm_config(audio); + break; + } + case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: { + struct audpreproc_cmd_routing_mode_done\ + *routing_cfg_done_msg = msg; + if (routing_cfg_done_msg->configuration == 0) { + MM_INFO("routing configuration failed\n"); + audio->running = 0; + } else + audaac_in_param_config(audio); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG\n"); + wake_up(&audio->wait_enable); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +/* ------------------- dsp audrec event handler--------------------- */ +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDREC_CMD_MEM_CFG_DONE_MSG: { + MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n"); + audio->running = 1; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (audio->dev_cnt > 0) + audaac_in_record_config(audio, 1); + } else { + audpreproc_pcm_send_data(audio, 1); + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_FATAL_ERR_MSG: { + struct audrec_fatal_err_msg fatal_err_msg; + + getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN); + MM_ERR("FATAL_ERR_MSG: err id %d\n", + fatal_err_msg.audrec_err_id); + /* Error stop the encoder */ + audio->stopped = 1; + wake_up(&audio->wait); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + wake_up(&audio->write_wait); + break; + } + case AUDREC_UP_PACKET_READY_MSG: { + struct audrec_up_pkt_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packet_write_cnt_lsw, \ + pkt_ready_msg.audrec_packet_write_cnt_msw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_msw); + + audaac_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audpreproc_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audaac_in_mem_config(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audaac_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_EOS_ACK_MSG: { + MM_DBG("eos ack recieved\n"); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event:module audrectask\n"); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +static void audaac_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + MM_DBG("head = %d\n", audio->in_head); + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + } else + audio->in_count++; + + audaac_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audaac_nt_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + MM_DBG("head = %d\n", audio->in_head); + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + + +struct msm_adsp_ops audrec_aac_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == META_OUT_SIZE) + len = len / 2; + else + len = (len + META_OUT_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audrec_send_audrecqueue(audio, (void *)&cmd, + (unsigned int)sizeof(cmd)); +} + + +static int audpcm_config(struct audio_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = ENABLE_FLAG_VALUE; + cmd.sampling_freq = audio->samp_rate; + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audrec_send_audrecqueue(audio, (void *)&cmd, + (unsigned int)sizeof(cmd)); +} + + +static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE; + cmd.stream_id = audio->enc_id; + if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL) + cmd.routing_mode = 1; + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + + + +static int audaac_in_enc_config(struct audio_in *audio, int enable) +{ + struct audpreproc_audrec_cmd_enc_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + if (audio->build_id[17] == '1') { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG_2 command"); + } else { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG command"); + } + cmd.stream_id = audio->enc_id; + + if (enable) + cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE; + else + cmd.audrec_enc_type &= ~(ENCODE_ENABLE); + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audaac_in_param_config(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_parm_cfg_aac cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + + cmd.aud_rec_samplerate_idx = audio->samp_rate; + cmd.aud_rec_stereo_mode = audio->channel_mode; + cmd.recording_quality = audio->record_quality; + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +/* To Do: msm_snddev_route_enc(audio->enc_id); */ +static int audaac_in_record_config(struct audio_in *audio, int enable) +{ + struct audpreproc_afe_cmd_audio_record_cfg cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG; + cmd.stream_id = audio->enc_id; + if (enable) + cmd.destination_activity = AUDIO_RECORDING_TURN_ON; + else + cmd.destination_activity = AUDIO_RECORDING_TURN_OFF; + + cmd.source_mix_mask = audio->source; + if (audio->enc_id == 2) { + if ((cmd.source_mix_mask & INTERNAL_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) { + cmd.pipe_id = SOURCE_PIPE_1; + } + if (cmd.source_mix_mask & + AUDPP_A2DP_PIPE_SOURCE_MIX_MASK) + cmd.pipe_id |= SOURCE_PIPE_0; + } + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audaac_in_mem_config(struct audio_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD; + cmd.audrec_up_pkt_intm_count = 1; + cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16; + cmd.audrec_ext_pkt_start_addr_lsw = audio->phys; + cmd.audrec_ext_pkt_buf_number = FRAME_NUM; + MM_DBG("audio->phys = %x\n", audio->phys); + /* prepare buffer pointers: + * 1536 bytes aac packet + 4 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audio->in[n].data = data + 4; + data += (FRAME_SIZE/2); + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8)); + } else { + audio->in[n].data = data + 12; + data += ((AAC_FRAME_SIZE) / 2) + 12; + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24)); + } + } + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +static int audaac_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + struct up_audrec_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR; + cmd.audrec_up_curr_read_count_msw = read_cnt >> 16; + cmd.audrec_up_curr_read_count_lsw = read_cnt; + + return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd)); +} +static int audaac_flush_command(struct audio_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audaac_in_enable(struct audio_in *audio) +{ + if (audio->enabled) + return 0; + + if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) { + MM_ERR("msm_adsp_enable(audpreproc) failed\n"); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + audpreproc_disable(audio->enc_id, audio); + return -ENODEV; + } + audio->enabled = 1; + audaac_in_enc_config(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audaac_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + audaac_in_enc_config(audio, 0); + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + audpreproc_disable(audio->enc_id, audio); + } + return 0; +} + +static void audaac_ioport_reset(struct audio_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audaac_in_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audaac_out_flush(audio); + mutex_unlock(&audio->read_lock); +} + +static void audaac_in_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + audio->eos_ack = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audaac_out_flush(struct audio_in *audio) +{ + int i; + + audio->out_head = 0; + audio->out_tail = 0; + audio->out_count = 0; + for (i = 0; i < OUT_FRAME_NUM; i++) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } +} + +/* ------------------- device --------------------- */ +static long audaac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t freq; + /* Poll at 48KHz always */ + freq = 48000; + MM_DBG("AUDIO_START\n"); + rc = msm_snddev_request_freq(&freq, audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("sample rate configured %d sample rate requested %d\n", + freq, audio->samp_rate); + if (rc < 0) { + MM_DBG(" Sample rate can not be set, return code %d\n", + rc); + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + break; + } + /*update aurec session info in audpreproc layer*/ + audio->session_info.session_id = audio->enc_id; + audio->session_info.sampling_freq = audio->samp_rate; + audpreproc_update_audrec_info(&audio->session_info); + rc = audaac_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + rc = audaac_in_disable(audio); + rc = msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + audio->stopped = 1; + audio->abort = 0; + break; + } + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audaac_ioport_reset(audio); + if (audio->running) { + audaac_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) { + rc = -EINVAL; + break; + } + } else { + if (cfg.buffer_size != (NT_FRAME_SIZE - 24)) { + rc = -EINVAL; + break; + } + } + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_pcm_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + if (audio->channel_mode == AUDREC_CMD_MODE_MONO) + cfg.channels = 1; + else + cfg.channels = 2; + cfg.sample_rate = audio->samp_rate; + cfg.bit_rate = audio->bit_rate; + cfg.stream_format = AUDIO_AAC_FORMAT_RAW; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + unsigned int record_quality; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.stream_format != AUDIO_AAC_FORMAT_RAW) { + MM_ERR("unsupported AAC format\n"); + rc = -EINVAL; + break; + } + record_quality = bitrate_to_record_quality(cfg.sample_rate, + cfg.channels, cfg.bit_rate); + /* Range of Record Quality Supported by DSP, Q12 format */ + if ((record_quality < 0x800) || (record_quality > 0x4000)) { + MM_ERR("Unsupported bit rate \n"); + rc = -EINVAL; + break; + } + MM_DBG("channels = %d\n", cfg.channels); + if (cfg.channels == 1) { + cfg.channels = AUDREC_CMD_MODE_MONO; + } else if (cfg.channels == 2) { + cfg.channels = AUDREC_CMD_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + MM_DBG("channels = %d\n", cfg.channels); + audio->samp_rate = cfg.sample_rate; + audio->channel_mode = cfg.channels; + audio->bit_rate = cfg.bit_rate; + audio->record_quality = record_quality; + MM_DBG(" Record Quality = 0x%8x \n", audio->record_quality); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->enc_id, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audaac_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + struct aac_encoded_meta_in meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG(" count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->abort || audio->rflush); + + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } + + if (audio->abort) { + rc = -EPERM; /* Not permitted due to abort */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, 12); + meta_field.metadata_len = + sizeof(struct aac_encoded_meta_in); + if (copy_to_user((char *)start, (char *)&meta_field, + sizeof(struct aac_encoded_meta_in))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_ERR("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct aac_encoded_meta_in); + count -= sizeof(struct aac_encoded_meta_in); + } + if (count >= size) { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && + (!audio->eos_ack)) { + MM_DBG("sending read ptr command %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audaac_dsp_read_buffer(audio, + audio->dsp_cnt++); + break; + } + } else { + MM_ERR("short read\n"); + break; + } + break; + } + mutex_unlock(&audio->read_lock); + if (buf > start) + return buf - start; + + return rc; +} + +static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audpreproc_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + + +static int audaac_in_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) + +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + + int audpreproc_aac_process_eos(struct audio_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audpreproc_pcm_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audaac_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int rc = 0, eos_condition = AUDPREPROC_AAC_EOS_NONE; + unsigned short mfield_size = 0; + int write_count = count; + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] & + AUDPREPROC_AAC_EOS_FLG_MASK) { + MM_DBG("EOS SET\n"); + eos_condition = AUDPREPROC_AAC_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + if (audio->mode == + MSM_AUD_ENC_MODE_NONTUNNEL) { + eos_condition = 0; + goto exit; + } + goto error; + } else + cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] &= + ~AUDPREPROC_AAC_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audpreproc_pcm_send_data(audio, 0); + else { + audpreproc_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_AAC_EOS_SET) + rc = audpreproc_aac_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audaac_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + /* with draw frequency for session + incase not stopped the driver */ + msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX, + AUDDEV_CLNT_ENC); + auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id); + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + audaac_in_disable(audio); + audaac_in_flush(audio); + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->opened = 0; + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + if (audio->out_data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_in the_audio_aac_in; + +static int audaac_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_aac_in; + int rc; + int encid; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (audio->phys) { + audio->map_v_read = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map DMA buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto done; + } + audio->data = audio->map_v_read; + } else { + MM_ERR("could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (NT_FRAME_SIZE - 24); + else + audio->buffer_size = (FRAME_SIZE - 8); + audio->enc_type = ENC_TYPE_AAC | audio->mode; + audio->samp_rate = 8000; + audio->channel_mode = AUDREC_CMD_MODE_MONO; + /* For AAC, bit rate hard coded, default settings is + * sample rate (8000) x channel count (1) x recording quality (1.75) + * = 14000 bps */ + audio->bit_rate = 14000; + audio->record_quality = 0x1c00; + MM_DBG("enc_type = %x\n", audio->enc_type); + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_aac_adsp_ops, audio); + + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->stopped = 0; + audio->source = 0; + audio->abort = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audaac_in_flush(audio); + audaac_out_flush(audio); + + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate write buffers\n"); + rc = -ENOMEM; + goto evt_error; + } else { + audio->map_v_write = ioremap( + audio->out_phys, BUFFER_SIZE); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, (int)audio->out_data); + } + audio->build_id = socinfo_get_build_id(); + MM_DBG("Modem build id = %s\n", audio->build_id); + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (unsigned int)audio->out[0].data, + (unsigned int)audio->out[1].data); + audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_FREQ_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_ENC, audio->enc_id, + aac_in_listener, (void *) audio); + if (rc) { + MM_ERR("failed to register device event listener\n"); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + goto evt_error; + } + audio->mfield = META_OUT_SIZE; + file->private_data = audio; + audio->opened = 1; + audio->out_frame_cnt++; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = audaac_in_open, + .release = audaac_in_release, + .read = audaac_in_read, + .write = audaac_in_write, + .fsync = audaac_in_fsync, + .unlocked_ioctl = audaac_in_ioctl, +}; + +struct miscdevice audio_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_in_fops, +}; + +static int __init audaac_in_init(void) +{ + mutex_init(&the_audio_aac_in.lock); + mutex_init(&the_audio_aac_in.read_lock); + spin_lock_init(&the_audio_aac_in.dsp_lock); + spin_lock_init(&the_audio_aac_in.dev_lock); + init_waitqueue_head(&the_audio_aac_in.wait); + init_waitqueue_head(&the_audio_aac_in.wait_enable); + mutex_init(&the_audio_aac_in.write_lock); + init_waitqueue_head(&the_audio_aac_in.write_wait); + + return misc_register(&audio_aac_in_misc); +} + +device_initcall(audaac_in_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_acdb.c b/arch/arm/mach-msm/qdsp5v2/audio_acdb.c new file mode 100644 index 00000000000..89957a4b22c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_acdb.c @@ -0,0 +1,3448 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* this is the ACDB device ID */ +#define DALDEVICEID_ACDB 0x02000069 +#define ACDB_PORT_NAME "DAL00" +#define ACDB_CPU SMD_APPS_MODEM +#define ACDB_BUF_SIZE 4096 +#define PBE_BUF_SIZE (33*1024) +#define FLUENCE_BUF_SIZE 498 + +#define ACDB_VALUES_NOT_FILLED 0 +#define ACDB_VALUES_FILLED 1 +#define MAX_RETRY 10 + +/*below macro is used to align the session info received from +Devctl driver with the state mentioned as not to alter the +Existing code*/ +#define AUDREC_OFFSET 2 +/* rpc table index */ +enum { + ACDB_DalACDB_ioctl = DALDEVICE_FIRST_DEVICE_API_IDX +}; + +enum { + CAL_DATA_READY = 0x1, + AUDPP_READY = 0x2, + AUDREC0_READY = 0x4, + AUDREC1_READY = 0x8, + AUDREC2_READY = 0x10, +}; + + +struct acdb_data { + void *handle; + + u32 phys_addr; + u8 *virt_addr; + + struct task_struct *cb_thread_task; + struct auddev_evt_audcal_info *device_info; + + u32 acdb_state; + struct audpp_event_callback audpp_cb; + struct audpreproc_event_callback audpreproc_cb; + + struct audpp_cmd_cfg_object_params_pcm *pp_iir; + struct audpp_cmd_cfg_cal_gain *calib_gain_rx; + struct audpp_cmd_cfg_pbe *pbe_block; + struct audpp_cmd_cfg_object_params_mbadrc *pp_mbadrc; + struct audpreproc_cmd_cfg_agc_params *preproc_agc; + struct audpreproc_cmd_cfg_iir_tuning_filter_params *preproc_iir; + struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx; + struct acdb_mbadrc_block mbadrc_block; + struct audpreproc_cmd_cfg_lvnv_param preproc_lvnv; + + wait_queue_head_t wait; + struct mutex acdb_mutex; + u32 device_cb_compl; + u32 audpp_cb_compl; + u32 preproc_cb_compl; + u8 preproc_stream_id; + u8 audrec_applied; + u32 multiple_sessions; + u32 cur_tx_session; + struct acdb_result acdb_result; + u16 *pbe_extbuff; + u16 *pbe_enable_flag; + u32 fluence_extbuff; + u8 *fluence_extbuff_virt; + void *map_v_fluence; + + struct acdb_pbe_block *pbe_blk; + + spinlock_t dsp_lock; + int dec_id; + struct audpp_cmd_cfg_object_params_eqalizer eq; + /*status to enable or disable the fluence*/ + int fleuce_feature_status[MAX_AUDREC_SESSIONS]; + struct audrec_session_info session_info; + /*pmem info*/ + int pmem_fd; + unsigned long paddr; + unsigned long kvaddr; + unsigned long pmem_len; + struct file *file; + /* pmem for get acdb blk */ + unsigned long get_blk_paddr; + u8 *get_blk_kvaddr; + void *map_v_get_blk; + char *build_id; +}; + +static struct acdb_data acdb_data; + +struct acdb_cache_node { + u32 node_status; + s32 stream_id; + u32 phys_addr_acdb_values; + void *map_v_addr; + u8 *virt_addr_acdb_values; + struct auddev_evt_audcal_info device_info; +}; + +/*for RX devices acdb values are applied based on copp ID so +the depth of tx cache is MAX number of COPP supported in the system*/ +struct acdb_cache_node acdb_cache_rx[MAX_COPP_NODE_SUPPORTED]; + +/*for TX devices acdb values are applied based on AUDREC session and +the depth of the tx cache is define by number of AUDREC sessions supported*/ +struct acdb_cache_node acdb_cache_tx[MAX_AUDREC_SESSIONS]; + +/*Audrec session info includes Attributes Sampling frequency and enc_id */ +struct audrec_session_info session_info[MAX_AUDREC_SESSIONS]; +#ifdef CONFIG_DEBUG_FS + +#define RTC_MAX_TIMEOUT 500 /* 500 ms */ +#define PMEM_RTC_ACDB_QUERY_MEM 4096 +#define EXTRACT_HIGH_WORD(x) ((x & 0xFFFF0000)>>16) +#define EXTRACT_LOW_WORD(x) (0x0000FFFF & x) +#define ACDB_RTC_TX 0xF1 +#define ACDB_RTC_RX 0x1F + + +static u32 acdb_audpp_entry[][4] = { + + { ABID_AUDIO_RTC_VOLUME_PAN_RX,\ + IID_AUDIO_RTC_VOLUME_PAN_PARAMETERS,\ + AUDPP_CMD_VOLUME_PAN,\ + ACDB_RTC_RX + }, + { ABID_AUDIO_IIR_RX,\ + IID_AUDIO_IIR_COEFF,\ + AUDPP_CMD_IIR_TUNING_FILTER, + ACDB_RTC_RX + }, + { ABID_AUDIO_RTC_EQUALIZER_PARAMETERS,\ + IID_AUDIO_RTC_EQUALIZER_PARAMETERS,\ + AUDPP_CMD_EQUALIZER,\ + ACDB_RTC_RX + }, + { ABID_AUDIO_RTC_SPA,\ + IID_AUDIO_RTC_SPA_PARAMETERS,\ + AUDPP_CMD_SPECTROGRAM, + ACDB_RTC_RX + }, + { ABID_AUDIO_STF_RX,\ + IID_AUDIO_IIR_COEFF,\ + AUDPP_CMD_SIDECHAIN_TUNING_FILTER,\ + ACDB_RTC_RX + }, + { + ABID_AUDIO_MBADRC_RX,\ + IID_AUDIO_RTC_MBADRC_PARAMETERS,\ + AUDPP_CMD_MBADRC,\ + ACDB_RTC_RX + }, + { + ABID_AUDIO_AGC_TX,\ + IID_AUDIO_AGC_PARAMETERS,\ + AUDPREPROC_CMD_CFG_AGC_PARAMS,\ + ACDB_RTC_TX + }, + { + ABID_AUDIO_AGC_TX,\ + IID_AUDIO_RTC_AGC_PARAMETERS,\ + AUDPREPROC_CMD_CFG_AGC_PARAMS,\ + ACDB_RTC_TX + }, + { + ABID_AUDIO_NS_TX,\ + IID_NS_PARAMETERS,\ + AUDPREPROC_CMD_CFG_NS_PARAMS,\ + ACDB_RTC_TX + }, + { + ABID_AUDIO_IIR_TX,\ + IID_AUDIO_RTC_TX_IIR_COEFF,\ + AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS,\ + ACDB_RTC_TX + }, + { + ABID_AUDIO_IIR_TX,\ + IID_AUDIO_IIR_COEFF,\ + AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS,\ + ACDB_RTC_TX + } + /*Any new entries should be added here*/ +}; + +static struct dentry *get_set_abid_dentry; +static struct dentry *get_set_abid_data_dentry; + +struct rtc_acdb_pmem { + u8 *viraddr; + int32_t phys; + void *map_v_rtc; +}; + +struct rtc_acdb_data { + u32 acdb_id; + u32 cmd_id; + u32 set_abid; + u32 set_iid; + u32 abid; + u32 err; + bool valid_abid; + u32 tx_rx_ctl; + struct rtc_acdb_pmem rtc_read; + struct rtc_acdb_pmem rtc_write; + wait_queue_head_t wait; +}; + +struct get_abid { + u32 cmd_id; + u32 acdb_id; + u32 set_abid; + u32 set_iid; +}; + +struct acdb_block_mbadrc_rtc { + u16 enable; + u16 num_bands; + u16 down_samp_level; + u16 adrc_delay; + u16 ext_buf_size; + u16 ext_partition; + u16 ext_buf_msw; + u16 ext_buf_lsw; + struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS]; + signed int ExtBuff[196]; +} __attribute__((packed)); + +enum { + ACDB_RTC_SUCCESS, + ACDB_RTC_ERR_INVALID_DEVICE, + ACDB_RTC_ERR_DEVICE_INACTIVE, + ACDB_RTC_ERR_INVALID_ABID, + ACDB_RTC_DSP_FAILURE, + ACDB_RTC_DSP_FEATURE_NOT_AVAILABLE, + ACDB_RTC_ERR_INVALID_LEN, + ACDB_RTC_ERR_UNKNOWN_FAILURE, + ACDB_RTC_PENDING_RESPONSE, + ACDB_RTC_INIT_FAILURE, +}; + +static struct rtc_acdb_data rtc_acdb; + +static int rtc_getsetabid_dbg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_INFO("GET-SET ABID Open debug intf %s\n", + (char *) file->private_data); + return 0; +} + +static bool get_feature_id(u32 set_abid, u32 iid, unsigned short *feature_id) +{ + bool ret_value = false; + int i = 0; + + for (; i < (sizeof(acdb_audpp_entry) / sizeof(acdb_audpp_entry[0]));\ + i++) { + if (acdb_audpp_entry[i][0] == set_abid && + acdb_audpp_entry[i][1] == iid) { + *feature_id = acdb_audpp_entry[i][2]; + rtc_acdb.tx_rx_ctl = acdb_audpp_entry[i][3]; + ret_value = true; + break; + } + } + return ret_value; +} +static ssize_t rtc_getsetabid_dbg_write(struct file *filp, + const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct get_abid write_abid; + unsigned short feat_id = 0; + rtc_acdb.valid_abid = false; + + if (copy_from_user(&write_abid, \ + (void *)ubuf, sizeof(struct get_abid))) { + MM_ERR("ACDB DATA WRITE - INVALID READ LEN\n"); + rtc_acdb.err = ACDB_RTC_ERR_INVALID_LEN; + return cnt; + } + MM_INFO("SET ABID : Cmd ID: %d Device:%d ABID:%d IID : %d cnt: %d\n",\ + write_abid.cmd_id, write_abid.acdb_id, + write_abid.set_abid, write_abid.set_iid, cnt); + if (write_abid.acdb_id > ACDB_ID_MAX || + write_abid.acdb_id < ACDB_ID_HANDSET_SPKR){ + rtc_acdb.err = ACDB_RTC_ERR_INVALID_DEVICE; + return cnt; + } + if (!is_dev_opened(write_abid.acdb_id)) { + rtc_acdb.err = ACDB_RTC_ERR_DEVICE_INACTIVE; + return cnt; + } + rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID; + rtc_acdb.abid = write_abid.set_abid; + if (get_feature_id(write_abid.set_abid, \ + write_abid.set_iid, &feat_id)) { + rtc_acdb.err = ACDB_RTC_SUCCESS; + rtc_acdb.cmd_id = write_abid.cmd_id; + rtc_acdb.acdb_id = write_abid.acdb_id; + rtc_acdb.set_abid = feat_id; + rtc_acdb.valid_abid = true; + rtc_acdb.set_iid = write_abid.set_iid; + } + return cnt; +} +static ssize_t rtc_getsetabid_dbg_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + static char buffer[1024]; + int n = 0; + u32 msg = rtc_acdb.err; + memcpy(buffer, &rtc_acdb.cmd_id, sizeof(struct get_abid)); + memcpy(buffer+16, &msg, 4); + n = 20; + MM_INFO("SET ABID : Cmd ID: %x Device:%x ABID:%x IID : %x Err: %d\n",\ + rtc_acdb.cmd_id, rtc_acdb.acdb_id, rtc_acdb.set_abid,\ + rtc_acdb.set_iid, rtc_acdb.err); + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static int rtc_getsetabid_data_dbg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_INFO("GET-SET ABID DATA Open debug intf %s\n", + (char *) file->private_data); + return 0; +} + +void acdb_rtc_set_err(u32 ErrCode) +{ + if (rtc_acdb.err == ACDB_RTC_PENDING_RESPONSE) { + if (ErrCode == 0xFFFF) { + rtc_acdb.err = ACDB_RTC_SUCCESS; + MM_INFO("RTC READ SUCCESS---\n"); + } else if (ErrCode == 0) { + rtc_acdb.err = ACDB_RTC_DSP_FAILURE; + MM_INFO("RTC READ FAIL---\n"); + } else if (ErrCode == 1) { + rtc_acdb.err = ACDB_RTC_DSP_FEATURE_NOT_AVAILABLE; + MM_INFO("RTC READ FEAT UNAVAILABLE---\n"); + } else { + rtc_acdb.err = ACDB_RTC_DSP_FAILURE; + MM_ERR("RTC Err CODE---\n"); + } + } else { + rtc_acdb.err = ACDB_RTC_DSP_FAILURE; + MM_ERR("RTC Err code Invalid State\n"); + } + wake_up(&rtc_acdb.wait); +} +static ssize_t rtc_getsetabid_data_dbg_read(struct file *file, + char __user *buf, size_t count, + loff_t *ppos) +{ + static char buffer[PMEM_RTC_ACDB_QUERY_MEM]; + int rc, n = 0; + int counter = 0; + struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read; + memset(&buffer, 0, PMEM_RTC_ACDB_QUERY_MEM); + + if (rtc_acdb.valid_abid != true) { + MM_ERR("ACDB DATA READ ---INVALID ABID\n"); + n = 0; + rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID; + } else { + if (PMEM_RTC_ACDB_QUERY_MEM < count) { + MM_ERR("ACDB DATA READ ---\ + INVALID READ LEN %x\n", count); + n = 0; + rtc_acdb.err = ACDB_RTC_ERR_INVALID_LEN; + } else { + rtc_acdb.err = ACDB_RTC_PENDING_RESPONSE; + if (rtc_read->viraddr != NULL) { + memset(rtc_read->viraddr, + 0, PMEM_RTC_ACDB_QUERY_MEM); + } + if (rtc_acdb.tx_rx_ctl == ACDB_RTC_RX) { + struct rtc_audpp_read_data rtc_read_cmd; + rtc_read_cmd.cmd_id = + AUDPP_CMD_PP_FEAT_QUERY_PARAMS; + rtc_read_cmd.obj_id = + AUDPP_CMD_COPP_STREAM; + rtc_read_cmd.route_id = + acdb_data.device_info->dev_id; + rtc_read_cmd.feature_id = rtc_acdb.set_abid; + rtc_read_cmd.extbufsizemsw = + EXTRACT_HIGH_WORD(\ + PMEM_RTC_ACDB_QUERY_MEM); + rtc_read_cmd.extbufsizelsw = + EXTRACT_LOW_WORD(\ + PMEM_RTC_ACDB_QUERY_MEM); + rtc_read_cmd.extpart = 0x0000; + rtc_read_cmd.extbufstartmsw = + EXTRACT_HIGH_WORD(rtc_read->phys); + rtc_read_cmd.extbufstartlsw = + EXTRACT_LOW_WORD(rtc_read->phys); + rc = audpp_send_queue2(&rtc_read_cmd, + sizeof(rtc_read_cmd)); + MM_INFO("ACDB READ Command RC --->%x\ + Route ID=%x\n", rc,\ + acdb_data.device_info->dev_id); + } else if (rtc_acdb.tx_rx_ctl == ACDB_RTC_TX) { + struct rtc_audpreproc_read_data rtc_audpreproc; + rtc_audpreproc.cmd_id = + AUDPREPROC_CMD_FEAT_QUERY_PARAMS; + rtc_audpreproc.stream_id = + acdb_data.preproc_stream_id; + rtc_audpreproc.feature_id = rtc_acdb.set_abid; + rtc_audpreproc.extbufsizemsw = + EXTRACT_HIGH_WORD(\ + PMEM_RTC_ACDB_QUERY_MEM); + rtc_audpreproc.extbufsizelsw = + EXTRACT_LOW_WORD(\ + PMEM_RTC_ACDB_QUERY_MEM); + rtc_audpreproc.extpart = 0x0000; + rtc_audpreproc.extbufstartmsw = + EXTRACT_HIGH_WORD(rtc_read->phys); + rtc_audpreproc.extbufstartlsw = + EXTRACT_LOW_WORD(rtc_read->phys); + rc = audpreproc_send_preproccmdqueue( + &rtc_audpreproc,\ + sizeof(rtc_audpreproc)); + MM_INFO("ACDB READ Command RC --->%x,\ + stream_id %x\n", rc,\ + acdb_data.preproc_stream_id); + } + rc = wait_event_timeout(rtc_acdb.wait, + (rtc_acdb.err != + ACDB_RTC_PENDING_RESPONSE), + msecs_to_jiffies(RTC_MAX_TIMEOUT)); + MM_INFO("ACDB READ ACK Count = %x Err = %x\n", + count, rtc_acdb.err); + { + if (rtc_acdb.err == ACDB_RTC_SUCCESS + && rtc_read->viraddr != NULL) { + memcpy(buffer, rtc_read->viraddr, count); + n = count; + while (counter < count) { + MM_DBG("%x", \ + rtc_read->viraddr[counter]); + counter++; + } + } + } + } + } + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static bool acdb_set_tx_rtc(const char *ubuf, size_t writecount) +{ + struct audpreproc_cmd_cfg_iir_tuning_filter_params *preproc_iir; + struct audpreproc_cmd_cfg_agc_params *preproc_agc; + struct audpreproc_cmd_cfg_ns_params *preproc_ns; + s32 result = 0; + bool retval = false; + unsigned short iircmdsize = + sizeof(struct audpreproc_cmd_cfg_iir_tuning_filter_params); + unsigned short iircmdid = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; + + rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE; + + switch (rtc_acdb.set_abid) { + + case AUDPREPROC_CMD_CFG_AGC_PARAMS: + case AUDPREPROC_CMD_CFG_AGC_PARAMS_2: + { + preproc_agc = kmalloc(sizeof(\ + struct audpreproc_cmd_cfg_agc_params),\ + GFP_KERNEL); + if ((sizeof(struct audpreproc_cmd_cfg_agc_params) -\ + (2*sizeof(unsigned short))) + < writecount) { + MM_ERR("ACDB DATA WRITE --\ + AGC TX writecount > DSP struct\n"); + } else { + if (preproc_agc != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)preproc_agc; + offset = offsetof(struct \ + audpreproc_cmd_cfg_agc_params,\ + tx_agc_param_mask); + offset_addr = (unsigned short *)(base + offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + preproc_agc->cmd_id = + AUDPREPROC_CMD_CFG_AGC_PARAMS; + preproc_agc->stream_id = + acdb_data.preproc_stream_id; + result = audpreproc_dsp_set_agc( + preproc_agc, + sizeof(struct \ + audpreproc_cmd_cfg_agc_params)); + if (result) { + MM_ERR("ACDB=> Failed to \ + send AGC data to \ + preproc)\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---\ + GC Tx copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --\ + AGC TX kalloc Failed LEN\n"); + } + } + if (preproc_agc != NULL) + kfree(preproc_agc); + break; + } + case AUDPREPROC_CMD_CFG_NS_PARAMS: + { + + preproc_ns = kmalloc(sizeof(struct \ + audpreproc_cmd_cfg_ns_params),\ + GFP_KERNEL); + if ((sizeof(struct audpreproc_cmd_cfg_ns_params) -\ + (2 * sizeof(unsigned short))) + < writecount) { + MM_ERR("ACDB DATA WRITE --\ + NS TX writecount > DSP struct\n"); + } else { + if (preproc_ns != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)preproc_ns; + offset = offsetof(struct \ + audpreproc_cmd_cfg_ns_params,\ + ec_mode_new); + offset_addr = (unsigned short *)(base + offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + preproc_ns->cmd_id = + AUDPREPROC_CMD_CFG_NS_PARAMS; + preproc_ns->stream_id = + acdb_data.preproc_stream_id; + result = audpreproc_dsp_set_ns( + preproc_ns, + sizeof(struct \ + audpreproc_cmd_cfg_ns_params)); + if (result) { + MM_ERR("ACDB=> Failed to send \ + NS data to preproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---NS Tx \ + copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --NS TX\ + kalloc Failed LEN\n"); + } + } + if (preproc_ns != NULL) + kfree(preproc_ns); + break; + } + case AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS: + { + + preproc_iir = kmalloc(sizeof(struct \ + audpreproc_cmd_cfg_iir_tuning_filter_params),\ + GFP_KERNEL); + if ((sizeof(struct \ + audpreproc_cmd_cfg_iir_tuning_filter_params)-\ + (2 * sizeof(unsigned short))) + < writecount) { + MM_ERR("ACDB DATA WRITE --IIR TX writecount\ + > DSP struct\n"); + } else { + if (preproc_iir != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)preproc_iir; + offset = offsetof(struct \ + audpreproc_cmd_cfg_iir_tuning_filter_params,\ + active_flag); + offset_addr = (unsigned short *)(base + \ + offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + preproc_iir->cmd_id = iircmdid; + preproc_iir->stream_id = + acdb_data.preproc_stream_id; + result = audpreproc_dsp_set_iir(\ + preproc_iir, + iircmdsize); + if (result) { + MM_ERR("ACDB=> Failed to send\ + IIR data to preproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---IIR Tx \ + copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --IIR TX kalloc \ + Failed LEN\n"); + } + } + if (preproc_iir != NULL) + kfree(preproc_iir); + break; + } + } + return retval; +} + +static bool acdb_set_rx_rtc(const char *ubuf, size_t writecount) +{ + + struct audpp_cmd_cfg_object_params_volpan *volpan_config; + struct audpp_cmd_cfg_object_params_mbadrc *mbadrc_config; + struct acdb_block_mbadrc_rtc *acdb_mbadrc_rtc; + struct audpp_cmd_cfg_object_params_sidechain *stf_config; + struct audpp_cmd_cfg_object_params_spectram *spa_config; + struct audpp_cmd_cfg_object_params_eqalizer *eq_config; + struct audpp_cmd_cfg_object_params_pcm *iir_config; + unsigned short temp_spa[34]; + struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write; + s32 result = 0; + bool retval = false; + + switch (rtc_acdb.set_abid) { + case AUDPP_CMD_VOLUME_PAN: + { + volpan_config = kmalloc(sizeof(struct \ + audpp_cmd_cfg_object_params_volpan),\ + GFP_KERNEL); + if ((sizeof(struct audpp_cmd_cfg_object_params_volpan) -\ + sizeof(struct audpp_cmd_cfg_object_params_common)) + < writecount) { + MM_ERR("ACDB DATA WRITE --\ + VolPan writecount > DSP struct\n"); + } else { + if (volpan_config != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)volpan_config; + offset = offsetof(struct \ + audpp_cmd_cfg_object_params_volpan,\ + volume); + offset_addr = (unsigned short *)(base+offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + MM_ERR("ACDB RX WRITE DATA:\ + AUDPP_CMD_VOLUME_PAN\n"); + result = audpp_set_volume_and_pan( + acdb_data.device_info->dev_id,\ + volpan_config->volume, + volpan_config->pan, + COPP); + if (result) { + MM_ERR("ACDB=> Failed to \ + send VOLPAN data to" + " postproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---\ + copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --\ + Vol Pan kalloc Failed LEN\n"); + } + } + if (volpan_config != NULL) + kfree(volpan_config); + break; + } + + case AUDPP_CMD_IIR_TUNING_FILTER: + { + iir_config = kmalloc(sizeof(struct \ + audpp_cmd_cfg_object_params_pcm),\ + GFP_KERNEL); + if ((sizeof(struct audpp_cmd_cfg_object_params_pcm) -\ + sizeof(struct audpp_cmd_cfg_object_params_common)) + < writecount) { + MM_ERR("ACDB DATA WRITE --\ + IIR RX writecount > DSP struct\n"); + } else { + if (iir_config != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)iir_config; + offset = offsetof(struct \ + audpp_cmd_cfg_object_params_pcm,\ + active_flag); + offset_addr = (unsigned short *)(base+offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + + iir_config->common.cmd_id = + AUDPP_CMD_CFG_OBJECT_PARAMS; + iir_config->common.stream = + AUDPP_CMD_COPP_STREAM; + iir_config->common.stream_id = 0; + iir_config->common.obj_cfg = + AUDPP_CMD_OBJ0_UPDATE; + iir_config->common.command_type = 0; + MM_ERR("ACDB RX WRITE DATA:\ + AUDPP_CMD_IIR_TUNING_FILTER\n"); + result = audpp_dsp_set_rx_iir( + acdb_data.device_info->dev_id, + iir_config->active_flag,\ + iir_config, COPP); + if (result) { + MM_ERR("ACDB=> Failed to send\ + IIR data to\ + postproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---\ + IIR Rx copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --\ + acdb_iir_block kalloc Failed LEN\n"); + } + } + if (iir_config != NULL) + kfree(iir_config); + break; + } + case AUDPP_CMD_EQUALIZER: + { + eq_config = kmalloc(sizeof(struct \ + audpp_cmd_cfg_object_params_eqalizer),\ + GFP_KERNEL); + if ((sizeof(struct audpp_cmd_cfg_object_params_eqalizer) -\ + sizeof(struct audpp_cmd_cfg_object_params_common)) + < writecount) { + MM_ERR("ACDB DATA WRITE --\ + EQ RX writecount > DSP struct\n"); + } else { + if (eq_config != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)eq_config; + offset = offsetof(struct \ + audpp_cmd_cfg_object_params_eqalizer,\ + eq_flag); + offset_addr = (unsigned short *)(base+offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + eq_config->common.cmd_id = + AUDPP_CMD_CFG_OBJECT_PARAMS; + eq_config->common.stream = + AUDPP_CMD_COPP_STREAM; + eq_config->common.stream_id = 0; + eq_config->common.obj_cfg = + AUDPP_CMD_OBJ0_UPDATE; + eq_config->common.command_type = 0; + MM_ERR("ACDB RX WRITE\ + DATA:AUDPP_CMD_EQUALIZER\n"); + result = audpp_dsp_set_eq( + acdb_data.device_info->dev_id, + eq_config->eq_flag,\ + eq_config, + COPP); + if (result) { + MM_ERR("ACDB=> Failed to \ + send EQ data to postproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---\ + EQ Rx copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --\ + EQ kalloc Failed LEN\n"); + } + } + if (eq_config != NULL) + kfree(eq_config); + break; + } + + case AUDPP_CMD_SPECTROGRAM: + { + spa_config = kmalloc(sizeof(struct \ + audpp_cmd_cfg_object_params_spectram),\ + GFP_KERNEL); + if ((sizeof(struct audpp_cmd_cfg_object_params_spectram)-\ + sizeof(struct \ + audpp_cmd_cfg_object_params_common)) + < (2 * sizeof(unsigned short))) { + MM_ERR("ACDB DATA WRITE --SPA \ + RX writecount > DSP struct\n"); + } else { + if (spa_config != NULL) { + if ((copy_from_user(&temp_spa[0],\ + (void *)ubuf, + (34 * sizeof(unsigned short)))) + == 0x00) { + spa_config->common.cmd_id = + AUDPP_CMD_CFG_OBJECT_PARAMS; + spa_config->common.stream = + AUDPP_CMD_COPP_STREAM; + spa_config->common.stream_id = 0; + spa_config->common.obj_cfg = + AUDPP_CMD_OBJ0_UPDATE; + spa_config->common.command_type = 0; + spa_config->sample_interval = + temp_spa[0]; + spa_config->num_coeff = temp_spa[1]; + MM_ERR("ACDB RX WRITE DATA:\ + AUDPP_CMD_SPECTROGRAM\n"); + result = audpp_dsp_set_spa( + acdb_data.device_info->dev_id,\ + spa_config, COPP); + if (result) { + MM_ERR("ACDB=> Failed to \ + send SPA data \ + to postproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE \ + ---SPA Rx copy_from_user\ + Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --\ + SPA kalloc Failed LEN\n"); + } + } + if (spa_config != NULL) + kfree(spa_config); + break; + } + case AUDPP_CMD_MBADRC: + { + acdb_mbadrc_rtc = kmalloc(sizeof(struct \ + acdb_block_mbadrc_rtc),\ + GFP_KERNEL); + mbadrc_config = kmalloc(sizeof(struct \ + audpp_cmd_cfg_object_params_mbadrc),\ + GFP_KERNEL); + if (mbadrc_config != NULL && acdb_mbadrc_rtc != NULL) { + if ((copy_from_user(acdb_mbadrc_rtc,\ + (void *)ubuf, + sizeof(struct acdb_block_mbadrc_rtc))) + == 0x00) { + mbadrc_config->common.cmd_id = + AUDPP_CMD_CFG_OBJECT_PARAMS; + mbadrc_config->common.stream = + AUDPP_CMD_COPP_STREAM; + mbadrc_config->common.stream_id = 0; + mbadrc_config->common.obj_cfg = + AUDPP_CMD_OBJ0_UPDATE; + mbadrc_config->common.command_type = 0; + mbadrc_config->enable = + acdb_mbadrc_rtc->enable; + mbadrc_config->num_bands = + acdb_mbadrc_rtc->num_bands; + mbadrc_config->down_samp_level = + acdb_mbadrc_rtc->down_samp_level; + mbadrc_config->adrc_delay = + acdb_mbadrc_rtc->adrc_delay; + memcpy(mbadrc_config->adrc_band,\ + acdb_mbadrc_rtc->adrc_band,\ + AUDPP_MAX_MBADRC_BANDS *\ + sizeof(struct adrc_config)); + if (mbadrc_config->num_bands > 1) { + mbadrc_config->ext_buf_size = + (97 * 2) + (33 * 2 * \ + (mbadrc_config->num_bands - 2)); + } + mbadrc_config->ext_partition = 0; + mbadrc_config->ext_buf_lsw = + (u16) EXTRACT_LOW_WORD(\ + rtc_write->phys); + mbadrc_config->ext_buf_msw = + (u16) EXTRACT_HIGH_WORD(\ + rtc_write->phys); + memcpy(rtc_write->viraddr, + acdb_mbadrc_rtc->ExtBuff, + (196*sizeof(signed int))); + result = audpp_dsp_set_mbadrc( + acdb_data.device_info->dev_id, + mbadrc_config->enable, + mbadrc_config, COPP); + if (result) { + MM_ERR("ACDB=> Failed to \ + Send MBADRC data \ + to postproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---\ + MBADRC Rx copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE --MBADRC kalloc Failed LEN\n"); + } + if (mbadrc_config != NULL) + kfree(mbadrc_config); + if (acdb_mbadrc_rtc != NULL) + kfree(acdb_mbadrc_rtc); + break; + } + case AUDPP_CMD_SIDECHAIN_TUNING_FILTER: + { + stf_config = kmalloc(sizeof(struct \ + audpp_cmd_cfg_object_params_sidechain),\ + GFP_KERNEL); + if ((sizeof(struct audpp_cmd_cfg_object_params_sidechain) -\ + sizeof(struct audpp_cmd_cfg_object_params_common)) + < writecount) { + MM_ERR("ACDB DATA WRITE --\ + STF RX writecount > DSP struct\n"); + } else { + if (stf_config != NULL) { + char *base; unsigned short offset; + unsigned short *offset_addr; + base = (char *)stf_config; + offset = offsetof(struct \ + audpp_cmd_cfg_object_params_sidechain,\ + active_flag); + offset_addr = (unsigned short *)(base+offset); + if ((copy_from_user(offset_addr,\ + (void *)ubuf, writecount)) == 0x00) { + stf_config->common.cmd_id = + AUDPP_CMD_CFG_OBJECT_PARAMS; + stf_config->common.stream = + AUDPP_CMD_COPP_STREAM; + stf_config->common.stream_id = 0; + stf_config->common.obj_cfg = + AUDPP_CMD_OBJ0_UPDATE; + stf_config->common.command_type = 0; + MM_ERR("ACDB RX WRITE DATA:\ + AUDPP_CMD_SIDECHAIN_TUNING_FILTER\n"); + result = audpp_dsp_set_stf( + acdb_data.device_info->dev_id,\ + stf_config->active_flag,\ + stf_config, COPP); + if (result) { + MM_ERR("ACDB=> Failed to send \ + STF data to postproc\n"); + } else { + retval = true; + } + } else { + MM_ERR("ACDB DATA WRITE ---\ + STF Rx copy_from_user Fail\n"); + } + } else { + MM_ERR("ACDB DATA WRITE \ + STF kalloc Failed LEN\n"); + } + } + if (stf_config != NULL) + kfree(stf_config); + break; + } + } + return retval; +} +static ssize_t rtc_getsetabid_data_dbg_write(struct file *filp, + const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + if (rtc_acdb.valid_abid != true) { + MM_INFO("ACDB DATA READ ---INVALID ABID\n"); + rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID; + } else { + if (rtc_acdb.tx_rx_ctl == ACDB_RTC_RX) { + if (acdb_set_rx_rtc(ubuf, cnt)) { + rtc_acdb.err = ACDB_RTC_SUCCESS; + } else { + rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE; + cnt = 0; + } + } else if (rtc_acdb.tx_rx_ctl == ACDB_RTC_TX) { + if (acdb_set_tx_rtc(ubuf, cnt)) { + rtc_acdb.err = ACDB_RTC_SUCCESS; + } else { + rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE; + cnt = 0; + } + } + } + return cnt; +} + + +static const struct file_operations rtc_acdb_data_debug_fops = { + .open = rtc_getsetabid_data_dbg_open, + .write = rtc_getsetabid_data_dbg_write, + .read = rtc_getsetabid_data_dbg_read +}; + +static const struct file_operations rtc_acdb_debug_fops = { + .open = rtc_getsetabid_dbg_open, + .write = rtc_getsetabid_dbg_write, + .read = rtc_getsetabid_dbg_read +}; + +static void rtc_acdb_deinit(void) +{ + struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read; + struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write; + if (get_set_abid_dentry) { + MM_DBG("GetSet ABID remove debugfs\n"); + debugfs_remove(get_set_abid_dentry); + } + + if (get_set_abid_data_dentry) { + MM_DBG("GetSet ABID remove debugfs\n"); + debugfs_remove(get_set_abid_data_dentry); + } + rtc_acdb.abid = 0; + rtc_acdb.acdb_id = 0; + rtc_acdb.cmd_id = 0; + rtc_acdb.err = 1; + rtc_acdb.set_abid = 0; + rtc_acdb.set_iid = 0; + rtc_acdb.tx_rx_ctl = 0; + rtc_acdb.valid_abid = false; + + if (rtc_read->viraddr != NULL || ((void *)rtc_read->phys) != NULL) { + iounmap(rtc_read->map_v_rtc); + free_contiguous_memory_by_paddr(rtc_read->phys); + } + if (rtc_write->viraddr != NULL || ((void *)rtc_write->phys) != NULL) { + iounmap(rtc_write->map_v_rtc); + free_contiguous_memory_by_paddr(rtc_write->phys); + } +} + +static bool rtc_acdb_init(void) +{ + struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read; + struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write; + s32 result = 0; + char name[sizeof "get_set_abid"+1]; + char name1[sizeof "get_set_abid_data"+1]; + rtc_acdb.abid = 0; + rtc_acdb.acdb_id = 0; + rtc_acdb.cmd_id = 0; + rtc_acdb.err = 1; + rtc_acdb.set_abid = 0; + rtc_acdb.set_iid = 0; + rtc_acdb.valid_abid = false; + rtc_acdb.tx_rx_ctl = 0; + if (acdb_data.build_id[17] == '1') { + snprintf(name, sizeof name, "get_set_abid"); + get_set_abid_dentry = debugfs_create_file(name, + S_IFREG | S_IRUGO | S_IWUGO, + NULL, NULL, &rtc_acdb_debug_fops); + if (IS_ERR(get_set_abid_dentry)) { + MM_ERR("SET GET ABID debugfs_create_file failed\n"); + return false; + } + + snprintf(name1, sizeof name1, "get_set_abid_data"); + get_set_abid_data_dentry = debugfs_create_file(name1, + S_IFREG | S_IRUGO | S_IWUGO, + NULL, NULL, + &rtc_acdb_data_debug_fops); + if (IS_ERR(get_set_abid_data_dentry)) { + MM_ERR("SET GET ABID DATA" + " debugfs_create_file failed\n"); + return false; + } + } + + rtc_read->phys = allocate_contiguous_ebi_nomap(PMEM_RTC_ACDB_QUERY_MEM, + SZ_4K); + + if (!rtc_read->phys) { + MM_ERR("ACDB Cannot allocate physical memory\n"); + result = -ENOMEM; + goto error; + } + rtc_read->map_v_rtc = ioremap(rtc_read->phys, + PMEM_RTC_ACDB_QUERY_MEM); + + if (IS_ERR(rtc_read->map_v_rtc)) { + MM_ERR("ACDB Could not map physical address\n"); + result = -ENOMEM; + goto error; + } + rtc_read->viraddr = rtc_read->map_v_rtc; + memset(rtc_read->viraddr, 0, PMEM_RTC_ACDB_QUERY_MEM); + + rtc_write->phys = allocate_contiguous_ebi_nomap(PMEM_RTC_ACDB_QUERY_MEM, + SZ_4K); + + if (!rtc_write->phys) { + MM_ERR("ACDB Cannot allocate physical memory\n"); + result = -ENOMEM; + goto error; + } + rtc_write->map_v_rtc = ioremap(rtc_write->phys, + PMEM_RTC_ACDB_QUERY_MEM); + + if (IS_ERR(rtc_write->map_v_rtc)) { + MM_ERR("ACDB Could not map physical address\n"); + result = -ENOMEM; + goto error; + } + rtc_write->viraddr = rtc_write->map_v_rtc; + memset(rtc_write->viraddr, 0, PMEM_RTC_ACDB_QUERY_MEM); + init_waitqueue_head(&rtc_acdb.wait); + return true; +error: + MM_DBG("INIT RTC FAILED REMOVING RTC DEBUG FS\n"); + if (get_set_abid_dentry) { + MM_DBG("GetSet ABID remove debugfs\n"); + debugfs_remove(get_set_abid_dentry); + } + + if (get_set_abid_data_dentry) { + MM_DBG("GetSet ABID remove debugfs\n"); + debugfs_remove(get_set_abid_data_dentry); + } + if (rtc_read->viraddr != NULL || ((void *)rtc_read->phys) != NULL) { + iounmap(rtc_read->map_v_rtc); + free_contiguous_memory_by_paddr(rtc_read->phys); + } + if (rtc_write->viraddr != NULL || ((void *)rtc_write->phys) != NULL) { + iounmap(rtc_write->map_v_rtc); + free_contiguous_memory_by_paddr(rtc_write->phys); + } + return false; +} +#endif /*CONFIG_DEBUG_FS*/ +static s32 acdb_set_calibration_blk(unsigned long arg) +{ + struct acdb_cmd_device acdb_cmd; + s32 result = 0; + + MM_DBG("acdb_set_calibration_blk\n"); + if (copy_from_user(&acdb_cmd, (struct acdb_cmd_device *)arg, + sizeof(acdb_cmd))) { + MM_ERR("Failed copy command struct from user in" + "acdb_set_calibration_blk\n"); + return -EFAULT; + } + acdb_cmd.phys_buf = (u32 *)acdb_data.paddr; + + MM_DBG("acdb_cmd.phys_buf %x\n", (u32)acdb_cmd.phys_buf); + + result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle, + (const void *)&acdb_cmd, sizeof(acdb_cmd), + &acdb_data.acdb_result, + sizeof(acdb_data.acdb_result)); + + if (result < 0) { + MM_ERR("ACDB=> Device Set RPC failure" + " result = %d\n", result); + return -EINVAL; + } else { + MM_ERR("ACDB=> Device Set RPC success\n"); + if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) + MM_DBG("ACDB_SET_DEVICE Success\n"); + else if (acdb_data.acdb_result.result == ACDB_RES_FAILURE) + MM_ERR("ACDB_SET_DEVICE Failure\n"); + else if (acdb_data.acdb_result.result == ACDB_RES_BADPARM) + MM_ERR("ACDB_SET_DEVICE BadParams\n"); + else + MM_ERR("Unknown error\n"); + } + return result; +} + +static s32 acdb_get_calibration_blk(unsigned long arg) +{ + s32 result = 0; + struct acdb_cmd_device acdb_cmd; + + MM_DBG("acdb_get_calibration_blk\n"); + + if (copy_from_user(&acdb_cmd, (struct acdb_cmd_device *)arg, + sizeof(acdb_cmd))) { + MM_ERR("Failed copy command struct from user in" + "acdb_get_calibration_blk\n"); + return -EFAULT; + } + acdb_cmd.phys_buf = (u32 *)acdb_data.paddr; + MM_ERR("acdb_cmd.phys_buf %x\n", (u32)acdb_cmd.phys_buf); + + result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle, + (const void *)&acdb_cmd, sizeof(acdb_cmd), + &acdb_data.acdb_result, + sizeof(acdb_data.acdb_result)); + + if (result < 0) { + MM_ERR("ACDB=> Device Get RPC failure" + " result = %d\n", result); + return -EINVAL; + } else { + MM_ERR("ACDB=> Device Get RPC Success\n"); + if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) + MM_DBG("ACDB_GET_DEVICE Success\n"); + else if (acdb_data.acdb_result.result == ACDB_RES_FAILURE) + MM_ERR("ACDB_GET_DEVICE Failure\n"); + else if (acdb_data.acdb_result.result == ACDB_RES_BADPARM) + MM_ERR("ACDB_GET_DEVICE BadParams\n"); + else + MM_ERR("Unknown error\n"); + } + return result; +} + +static int audio_acdb_open(struct inode *inode, struct file *file) +{ + MM_DBG("%s\n", __func__); + return 0; +} +static int audio_acdb_release(struct inode *inode, struct file *file) +{ + MM_DBG("%s\n", __func__); + return 0; +} + +static long audio_acdb_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + unsigned long flags = 0; + struct msm_audio_pmem_info info; + + MM_DBG("%s\n", __func__); + + switch (cmd) { + case AUDIO_SET_EQ: + MM_DBG("IOCTL SET_EQ_CONFIG\n"); + if (copy_from_user(&acdb_data.eq.num_bands, (void *) arg, + sizeof(acdb_data.eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&acdb_data.dsp_lock, flags); + acdb_data.dec_id = 0; + rc = audpp_dsp_set_eq(acdb_data.dec_id, 1, + &acdb_data.eq, COPP); + if (rc < 0) + MM_ERR("AUDPP returned err =%d\n", rc); + spin_unlock_irqrestore(&acdb_data.dsp_lock, flags); + break; + case AUDIO_REGISTER_PMEM: + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) { + MM_ERR("Cannot copy from user\n"); + return -EFAULT; + } + rc = get_pmem_file(info.fd, &acdb_data.paddr, + &acdb_data.kvaddr, + &acdb_data.pmem_len, + &acdb_data.file); + if (rc == 0) + acdb_data.pmem_fd = info.fd; + break; + case AUDIO_DEREGISTER_PMEM: + if (acdb_data.pmem_fd) + put_pmem_file(acdb_data.file); + break; + case AUDIO_SET_ACDB_BLK: + MM_DBG("IOCTL AUDIO_SET_ACDB_BLK\n"); + rc = acdb_set_calibration_blk(arg); + break; + case AUDIO_GET_ACDB_BLK: + MM_DBG("IOiCTL AUDIO_GET_ACDB_BLK\n"); + rc = acdb_get_calibration_blk(arg); + break; + default: + MM_DBG("Unknown IOCTL%d\n", cmd); + rc = -EINVAL; + } + return rc; +} + +static const struct file_operations acdb_fops = { + .owner = THIS_MODULE, + .open = audio_acdb_open, + .release = audio_acdb_release, + .llseek = no_llseek, + .unlocked_ioctl = audio_acdb_ioctl +}; + +struct miscdevice acdb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_acdb", + .fops = &acdb_fops, +}; + +static s32 acdb_get_calibration(void) +{ + struct acdb_cmd_get_device_table acdb_cmd; + s32 result = 0; + u32 iterations = 0; + + MM_DBG("acdb state = %d\n", acdb_data.acdb_state); + + acdb_cmd.command_id = ACDB_GET_DEVICE_TABLE; + acdb_cmd.device_id = acdb_data.device_info->acdb_id; + acdb_cmd.network_id = 0x0108B153; + acdb_cmd.sample_rate_id = acdb_data.device_info->sample_rate; + acdb_cmd.total_bytes = ACDB_BUF_SIZE; + acdb_cmd.phys_buf = (u32 *)acdb_data.phys_addr; + MM_DBG("device_id = %d, sampling_freq = %d\n", + acdb_cmd.device_id, acdb_cmd.sample_rate_id); + + do { + result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle, + (const void *)&acdb_cmd, sizeof(acdb_cmd), + &acdb_data.acdb_result, + sizeof(acdb_data.acdb_result)); + + if (result < 0) { + MM_ERR("ACDB=> Device table RPC failure" + " result = %d\n", result); + goto error; + } + /*following check is introduced to handle boot up race + condition between AUDCAL SW peers running on apps + and modem (ACDB_RES_BADSTATE indicates modem AUDCAL SW is + not in initialized sate) we need to retry to get ACDB + values*/ + if (acdb_data.acdb_result.result == ACDB_RES_BADSTATE) { + msleep(500); + iterations++; + } else if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) { + MM_DBG("Modem query for acdb values is successful" + " (iterations = %d)\n", iterations); + acdb_data.acdb_state |= CAL_DATA_READY; + return result; + } else { + MM_ERR("ACDB=> modem failed to fill acdb values," + " reuslt = %d, (iterations = %d)\n", + acdb_data.acdb_result.result, + iterations); + goto error; + } + } while (iterations < MAX_RETRY); + MM_ERR("ACDB=> AUDCAL SW on modem is not in intiailized state (%d)\n", + acdb_data.acdb_result.result); +error: + result = -EINVAL; + return result; +} + +s32 acdb_get_calibration_data(struct acdb_get_block *get_block) +{ + s32 result = -EINVAL; + struct acdb_cmd_device acdb_cmd; + struct acdb_result acdb_result; + + MM_DBG("acdb_get_calibration_data\n"); + + acdb_cmd.command_id = ACDB_GET_DEVICE; + acdb_cmd.network_id = 0x0108B153; + acdb_cmd.device_id = get_block->acdb_id; + acdb_cmd.sample_rate_id = get_block->sample_rate_id; + acdb_cmd.interface_id = get_block->interface_id; + acdb_cmd.algorithm_block_id = get_block->algorithm_block_id; + acdb_cmd.total_bytes = get_block->total_bytes; + acdb_cmd.phys_buf = (u32 *)acdb_data.get_blk_paddr; + + result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle, + (const void *)&acdb_cmd, sizeof(acdb_cmd), + &acdb_result, + sizeof(acdb_result)); + + if (result < 0) { + MM_ERR("ACDB=> Device Get RPC failure" + " result = %d\n", result); + goto err_state; + } else { + MM_DBG("ACDB=> Device Get RPC Success\n"); + if (acdb_result.result == ACDB_RES_SUCCESS) { + MM_DBG("ACDB_GET_DEVICE Success\n"); + result = 0; + memcpy(get_block->buf_ptr, acdb_data.get_blk_kvaddr, + get_block->total_bytes); + } else if (acdb_result.result == ACDB_RES_FAILURE) + MM_ERR("ACDB_GET_DEVICE Failure\n"); + else if (acdb_result.result == ACDB_RES_BADPARM) + MM_ERR("ACDB_GET_DEVICE BadParams\n"); + else + MM_ERR("Unknown error\n"); + } +err_state: + return result; +} +EXPORT_SYMBOL(acdb_get_calibration_data); + +static u8 check_device_info_already_present( + struct auddev_evt_audcal_info audcal_info, + struct acdb_cache_node *acdb_cache_free_node) +{ + if ((audcal_info.dev_id == + acdb_cache_free_node->device_info.dev_id) && + (audcal_info.sample_rate == + acdb_cache_free_node->device_info.\ + sample_rate) && + (audcal_info.acdb_id == + acdb_cache_free_node->device_info.acdb_id)) { + MM_DBG("acdb values are already present\n"); + /*if acdb state is not set for CAL_DATA_READY and node status + is filled, acdb state should be updated with CAL_DATA_READY + state*/ + acdb_data.acdb_state |= CAL_DATA_READY; + /*checking for cache node status if it is not filled then the + acdb values are not cleaned from node so update node status + with acdb value filled*/ + if ((acdb_cache_free_node->node_status != ACDB_VALUES_FILLED) && + ((audcal_info.dev_type & RX_DEVICE) == 1)) { + MM_DBG("device was released earlier\n"); + acdb_cache_free_node->node_status = ACDB_VALUES_FILLED; + return 2; /*node is presnet but status as not filled*/ + } + return 1; /*node is present but status as filled*/ + } + MM_DBG("copying device info into node\n"); + /*as device information is not present in cache copy + the current device information into the node*/ + memcpy(&acdb_cache_free_node->device_info, + &audcal_info, sizeof(audcal_info)); + return 0; /*cant find the node*/ +} + +static struct acdb_iir_block *get_audpp_irr_block(void) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_IIR_RX) { + if (prs_hdr->iid == IID_AUDIO_IIR_COEFF) + return (struct acdb_iir_block *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + + +static s32 acdb_fill_audpp_iir(void) +{ + struct acdb_iir_block *acdb_iir; + s32 i = 0; + + acdb_iir = get_audpp_irr_block(); + if (acdb_iir == NULL) { + MM_ERR("unable to find audpp iir block returning\n"); + return -1; + } + memset(acdb_data.pp_iir, 0, sizeof(*acdb_data.pp_iir)); + + acdb_data.pp_iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + acdb_data.pp_iir->common.stream = AUDPP_CMD_COPP_STREAM; + acdb_data.pp_iir->common.stream_id = 0; + acdb_data.pp_iir->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE; + acdb_data.pp_iir->common.command_type = 0; + + acdb_data.pp_iir->active_flag = acdb_iir->enable_flag; + acdb_data.pp_iir->num_bands = acdb_iir->stage_count; + for (; i < acdb_iir->stage_count; i++) { + acdb_data.pp_iir->params_filter.filter_4_params. + numerator_filter[i].numerator_b0_filter_lsw = + acdb_iir->stages[i].b0_lo; + acdb_data.pp_iir->params_filter.filter_4_params. + numerator_filter[i].numerator_b0_filter_msw = + acdb_iir->stages[i].b0_hi; + acdb_data.pp_iir->params_filter.filter_4_params. + numerator_filter[i].numerator_b1_filter_lsw = + acdb_iir->stages[i].b1_lo; + acdb_data.pp_iir->params_filter.filter_4_params. + numerator_filter[i].numerator_b1_filter_msw = + acdb_iir->stages[i].b1_hi; + acdb_data.pp_iir->params_filter.filter_4_params. + numerator_filter[i].numerator_b2_filter_lsw = + acdb_iir->stages[i].b2_lo; + acdb_data.pp_iir->params_filter.filter_4_params. + numerator_filter[i].numerator_b2_filter_msw = + acdb_iir->stages[i].b2_hi; + acdb_data.pp_iir->params_filter.filter_4_params. + denominator_filter[i].denominator_a0_filter_lsw = + acdb_iir->stages_a[i].a1_lo; + acdb_data.pp_iir->params_filter.filter_4_params. + denominator_filter[i].denominator_a0_filter_msw = + acdb_iir->stages_a[i].a1_hi; + acdb_data.pp_iir->params_filter.filter_4_params. + denominator_filter[i].denominator_a1_filter_lsw = + acdb_iir->stages_a[i].a2_lo; + acdb_data.pp_iir->params_filter.filter_4_params. + denominator_filter[i].denominator_a1_filter_msw = + acdb_iir->stages_a[i].a2_hi; + acdb_data.pp_iir->params_filter.filter_4_params. + shift_factor_filter[i].shift_factor_0 = + acdb_iir->shift_factor[i]; + acdb_data.pp_iir->params_filter.filter_4_params.pan_filter[i]. + pan_filter_0 = acdb_iir->pan[i]; + } + return 0; +} + +static void extract_mbadrc(u32 *phy_addr, struct header *prs_hdr, u32 *index) +{ + if (prs_hdr->iid == IID_MBADRC_EXT_BUFF) { + MM_DBG("Got IID = IID_MBADRC_EXT_BUFF\n"); + *phy_addr = acdb_data.phys_addr + *index + + sizeof(struct header); + memcpy(acdb_data.mbadrc_block.ext_buf, + (acdb_data.virt_addr + *index + + sizeof(struct header)), 196*2); + MM_DBG("phy_addr = %x\n", *phy_addr); + *index += prs_hdr->data_len + sizeof(struct header); + } else if (prs_hdr->iid == IID_MBADRC_BAND_CONFIG) { + MM_DBG("Got IID == IID_MBADRC_BAND_CONFIG\n"); + memcpy(acdb_data.mbadrc_block.band_config, (acdb_data.virt_addr + + *index + sizeof(struct header)), + sizeof(struct mbadrc_band_config_type) * + acdb_data.mbadrc_block.parameters.\ + mbadrc_num_bands); + *index += prs_hdr->data_len + sizeof(struct header); + } else if (prs_hdr->iid == IID_MBADRC_PARAMETERS) { + struct mbadrc_parameter *tmp; + tmp = (struct mbadrc_parameter *)(acdb_data.virt_addr + *index + + sizeof(struct header)); + MM_DBG("Got IID == IID_MBADRC_PARAMETERS\n"); + acdb_data.mbadrc_block.parameters.mbadrc_enable = + tmp->mbadrc_enable; + acdb_data.mbadrc_block.parameters.mbadrc_num_bands = + tmp->mbadrc_num_bands; + acdb_data.mbadrc_block.parameters.mbadrc_down_sample_level = + tmp->mbadrc_down_sample_level; + acdb_data.mbadrc_block.parameters.mbadrc_delay = + tmp->mbadrc_delay; + *index += prs_hdr->data_len + sizeof(struct header); + } +} + +static void get_audpp_mbadrc_block(u32 *phy_addr) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_MBADRC_RX) { + if ((prs_hdr->iid == IID_MBADRC_EXT_BUFF) + || (prs_hdr->iid == + IID_MBADRC_BAND_CONFIG) + || (prs_hdr->iid == + IID_MBADRC_PARAMETERS)) { + extract_mbadrc(phy_addr, prs_hdr, + &index); + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } +} + +static s32 acdb_fill_audpp_mbadrc(void) +{ + u32 mbadrc_phys_addr = -1; + get_audpp_mbadrc_block(&mbadrc_phys_addr); + if (IS_ERR_VALUE(mbadrc_phys_addr)) { + MM_ERR("failed to get mbadrc block\n"); + return -1; + } + + memset(acdb_data.pp_mbadrc, 0, sizeof(*acdb_data.pp_mbadrc)); + + acdb_data.pp_mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + acdb_data.pp_mbadrc->common.stream = AUDPP_CMD_COPP_STREAM; + acdb_data.pp_mbadrc->common.stream_id = 0; + acdb_data.pp_mbadrc->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE; + acdb_data.pp_mbadrc->common.command_type = 0; + + acdb_data.pp_mbadrc->enable = acdb_data.mbadrc_block.\ + parameters.mbadrc_enable; + acdb_data.pp_mbadrc->num_bands = + acdb_data.mbadrc_block.\ + parameters.mbadrc_num_bands; + acdb_data.pp_mbadrc->down_samp_level = + acdb_data.mbadrc_block.parameters.\ + mbadrc_down_sample_level; + acdb_data.pp_mbadrc->adrc_delay = + acdb_data.mbadrc_block.parameters.\ + mbadrc_delay; + + if (acdb_data.mbadrc_block.parameters.mbadrc_num_bands > 1) + acdb_data.pp_mbadrc->ext_buf_size = (97 * 2) + + (33 * 2 * (acdb_data.mbadrc_block.parameters.\ + mbadrc_num_bands - 2)); + + acdb_data.pp_mbadrc->ext_partition = 0; + acdb_data.pp_mbadrc->ext_buf_lsw = (u16)(mbadrc_phys_addr\ + & 0xFFFF); + acdb_data.pp_mbadrc->ext_buf_msw = (u16)((mbadrc_phys_addr\ + & 0xFFFF0000) >> 16); + memcpy(acdb_data.pp_mbadrc->adrc_band, acdb_data.mbadrc_block.\ + band_config, + sizeof(struct mbadrc_band_config_type) * + acdb_data.mbadrc_block.parameters.mbadrc_num_bands); + return 0; +} + +static struct acdb_calib_gain_rx *get_audpp_cal_gain(void) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_CALIBRATION_GAIN_RX) { + if (prs_hdr->iid == + IID_AUDIO_CALIBRATION_GAIN_RX) { + MM_DBG("Got audpp_calib_gain_rx" + " block\n"); + return (struct acdb_calib_gain_rx *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + +static s32 acdb_fill_audpp_cal_gain(void) +{ + struct acdb_calib_gain_rx *acdb_calib_gain_rx = NULL; + + acdb_calib_gain_rx = get_audpp_cal_gain(); + if (acdb_calib_gain_rx == NULL) { + MM_ERR("unable to find audpp" + " calibration gain block returning\n"); + return -1; + } + MM_DBG("Calibration value" + " for calib_gain_rx %d\n", acdb_calib_gain_rx->audppcalgain); + memset(acdb_data.calib_gain_rx, 0, sizeof(*acdb_data.calib_gain_rx)); + + acdb_data.calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + acdb_data.calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM; + acdb_data.calib_gain_rx->common.stream_id = 0; + acdb_data.calib_gain_rx->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE; + acdb_data.calib_gain_rx->common.command_type = 0; + + acdb_data.calib_gain_rx->audppcalgain = + acdb_calib_gain_rx->audppcalgain; + return 0; +} + +static void extract_pbe_block(struct header *prs_hdr, u32 *index) +{ + if (prs_hdr->iid == IID_AUDIO_PBE_RX_ENABLE_FLAG) { + MM_DBG("Got IID = IID_AUDIO_PBE_RX_ENABLE\n"); + acdb_data.pbe_enable_flag = (u16 *)(acdb_data.virt_addr + + *index + + sizeof(struct header)); + *index += prs_hdr->data_len + sizeof(struct header); + } else if (prs_hdr->iid == IID_PBE_CONFIG_PARAMETERS) { + MM_DBG("Got IID == IID_PBE_CONFIG_PARAMETERS\n"); + acdb_data.pbe_blk = (struct acdb_pbe_block *) + (acdb_data.virt_addr + *index + + sizeof(struct header)); + *index += prs_hdr->data_len + sizeof(struct header); + } +} + +static s32 get_audpp_pbe_block(void) +{ + struct header *prs_hdr; + u32 index = 0; + s32 result = -1; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_PBE_RX) { + if ((prs_hdr->iid == IID_PBE_CONFIG_PARAMETERS) + || (prs_hdr->iid == + IID_AUDIO_PBE_RX_ENABLE_FLAG)) { + extract_pbe_block(prs_hdr, &index); + result = 0; + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return result; +} + +static s32 acdb_fill_audpp_pbe(void) +{ + s32 result = -1; + + result = get_audpp_pbe_block(); + if (IS_ERR_VALUE(result)) + return result; + memset(acdb_data.pbe_block, 0, sizeof(*acdb_data.pbe_block)); + + acdb_data.pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + acdb_data.pbe_block->common.stream = AUDPP_CMD_COPP_STREAM; + acdb_data.pbe_block->common.stream_id = 0; + acdb_data.pbe_block->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE; + acdb_data.pbe_block->common.command_type = 0; + acdb_data.pbe_block->pbe_enable = *acdb_data.pbe_enable_flag; + + acdb_data.pbe_block->realbassmix = acdb_data.pbe_blk->realbassmix; + acdb_data.pbe_block->basscolorcontrol = + acdb_data.pbe_blk->basscolorcontrol; + acdb_data.pbe_block->mainchaindelay = acdb_data.pbe_blk->mainchaindelay; + acdb_data.pbe_block->xoverfltorder = acdb_data.pbe_blk->xoverfltorder; + acdb_data.pbe_block->bandpassfltorder = + acdb_data.pbe_blk->bandpassfltorder; + acdb_data.pbe_block->adrcdelay = acdb_data.pbe_blk->adrcdelay; + acdb_data.pbe_block->downsamplelevel = + acdb_data.pbe_blk->downsamplelevel; + acdb_data.pbe_block->comprmstav = acdb_data.pbe_blk->comprmstav; + acdb_data.pbe_block->expthreshold = acdb_data.pbe_blk->expthreshold; + acdb_data.pbe_block->expslope = acdb_data.pbe_blk->expslope; + acdb_data.pbe_block->compthreshold = acdb_data.pbe_blk->compthreshold; + acdb_data.pbe_block->compslope = acdb_data.pbe_blk->compslope; + acdb_data.pbe_block->cpmpattack_lsw = acdb_data.pbe_blk->cpmpattack_lsw; + acdb_data.pbe_block->compattack_msw = acdb_data.pbe_blk->compattack_msw; + acdb_data.pbe_block->comprelease_lsw = + acdb_data.pbe_blk->comprelease_lsw; + acdb_data.pbe_block->comprelease_msw = + acdb_data.pbe_blk->comprelease_msw; + acdb_data.pbe_block->compmakeupgain = acdb_data.pbe_blk->compmakeupgain; + acdb_data.pbe_block->baselimthreshold = + acdb_data.pbe_blk->baselimthreshold; + acdb_data.pbe_block->highlimthreshold = + acdb_data.pbe_blk->highlimthreshold; + acdb_data.pbe_block->basslimmakeupgain = + acdb_data.pbe_blk->basslimmakeupgain; + acdb_data.pbe_block->highlimmakeupgain = + acdb_data.pbe_blk->highlimmakeupgain; + acdb_data.pbe_block->limbassgrc = acdb_data.pbe_blk->limbassgrc; + acdb_data.pbe_block->limhighgrc = acdb_data.pbe_blk->limhighgrc; + acdb_data.pbe_block->limdelay = acdb_data.pbe_blk->limdelay; + memcpy(acdb_data.pbe_block->filter_coeffs, + acdb_data.pbe_blk->filter_coeffs, sizeof(u16)*90); + acdb_data.pbe_block->extpartition = 0; + acdb_data.pbe_block->extbuffsize_lsw = PBE_BUF_SIZE; + acdb_data.pbe_block->extbuffsize_msw = 0; + acdb_data.pbe_block->extbuffstart_lsw = ((u32)acdb_data.pbe_extbuff + & 0xFFFF); + acdb_data.pbe_block->extbuffstart_msw = (((u32)acdb_data.pbe_extbuff + & 0xFFFF0000) >> 16); + return 0; +} + + +static s32 acdb_calibrate_audpp(void) +{ + s32 result = 0; + + result = acdb_fill_audpp_iir(); + if (!IS_ERR_VALUE(result)) { + result = audpp_dsp_set_rx_iir(acdb_data.device_info->dev_id, + acdb_data.pp_iir->active_flag, + acdb_data.pp_iir, COPP); + if (result) { + MM_ERR("ACDB=> Failed to send IIR data to postproc\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AUDPP is calibrated with IIR parameters" + " for COPP ID %d\n", + acdb_data.device_info->dev_id); + } + result = acdb_fill_audpp_mbadrc(); + if (!IS_ERR_VALUE(result)) { + result = audpp_dsp_set_mbadrc(acdb_data.device_info->dev_id, + acdb_data.pp_mbadrc->enable, + acdb_data.pp_mbadrc, COPP); + if (result) { + MM_ERR("ACDB=> Failed to send MBADRC data to" + " postproc\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AUDPP is calibrated with MBADRC parameters" + " for COPP ID %d\n", + acdb_data.device_info->dev_id); + } + result = acdb_fill_audpp_cal_gain(); + if (!(IS_ERR_VALUE(result))) { + result = audpp_dsp_set_gain_rx(acdb_data.device_info->dev_id, + acdb_data.calib_gain_rx, COPP); + if (result) { + MM_ERR("ACDB=> Failed to send gain_rx" + " data to postproc\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AUDPP is calibrated with calib_gain_rx\n"); + } + result = acdb_fill_audpp_pbe(); + if (!(IS_ERR_VALUE(result))) { + result = audpp_dsp_set_pbe(acdb_data.device_info->dev_id, + acdb_data.pbe_block->pbe_enable, + acdb_data.pbe_block, COPP); + if (result) { + MM_ERR("ACDB=> Failed to send pbe block" + "data to postproc\n"); + result = -EINVAL; + goto done; + } + MM_DBG("AUDPP is calibarted with PBE\n"); + } +done: + return result; +} + +static struct acdb_agc_block *get_audpreproc_agc_block(void) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_AGC_TX) { + if (prs_hdr->iid == IID_AUDIO_AGC_PARAMETERS) { + MM_DBG("GOT ABID_AUDIO_AGC_TX\n"); + return (struct acdb_agc_block *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + +static s32 acdb_fill_audpreproc_agc(void) +{ + struct acdb_agc_block *acdb_agc; + + acdb_agc = get_audpreproc_agc_block(); + if (!acdb_agc) { + MM_DBG("unable to find preproc agc parameters winding up\n"); + return -1; + } + memset(acdb_data.preproc_agc, 0, sizeof(*acdb_data.preproc_agc)); + acdb_data.preproc_agc->cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS; + acdb_data.preproc_agc->stream_id = acdb_data.preproc_stream_id; + /* 0xFE00 to configure all parameters */ + acdb_data.preproc_agc->tx_agc_param_mask = 0xFFFF; + + if (acdb_agc->enable_status) + acdb_data.preproc_agc->tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA; + else + acdb_data.preproc_agc->tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS; + + acdb_data.preproc_agc->comp_rlink_static_gain = + acdb_agc->comp_rlink_static_gain; + acdb_data.preproc_agc->comp_rlink_aig_flag = + acdb_agc->comp_rlink_aig_flag; + acdb_data.preproc_agc->expander_rlink_th = + acdb_agc->exp_rlink_threshold; + acdb_data.preproc_agc->expander_rlink_slope = + acdb_agc->exp_rlink_slope; + acdb_data.preproc_agc->compressor_rlink_th = + acdb_agc->comp_rlink_threshold; + acdb_data.preproc_agc->compressor_rlink_slope = + acdb_agc->comp_rlink_slope; + + /* 0xFFF0 to configure all parameters */ + acdb_data.preproc_agc->tx_adc_agc_param_mask = 0xFFFF; + + acdb_data.preproc_agc->comp_rlink_aig_attackk = + acdb_agc->comp_rlink_aig_attack_k; + acdb_data.preproc_agc->comp_rlink_aig_leak_down = + acdb_agc->comp_rlink_aig_leak_down; + acdb_data.preproc_agc->comp_rlink_aig_leak_up = + acdb_agc->comp_rlink_aig_leak_up; + acdb_data.preproc_agc->comp_rlink_aig_max = + acdb_agc->comp_rlink_aig_max; + acdb_data.preproc_agc->comp_rlink_aig_min = + acdb_agc->comp_rlink_aig_min; + acdb_data.preproc_agc->comp_rlink_aig_releasek = + acdb_agc->comp_rlink_aig_release_k; + acdb_data.preproc_agc->comp_rlink_aig_leakrate_fast = + acdb_agc->comp_rlink_aig_sm_leak_rate_fast; + acdb_data.preproc_agc->comp_rlink_aig_leakrate_slow = + acdb_agc->comp_rlink_aig_sm_leak_rate_slow; + acdb_data.preproc_agc->comp_rlink_attackk_msw = + acdb_agc->comp_rlink_attack_k_msw; + acdb_data.preproc_agc->comp_rlink_attackk_lsw = + acdb_agc->comp_rlink_attack_k_lsw; + acdb_data.preproc_agc->comp_rlink_delay = + acdb_agc->comp_rlink_delay; + acdb_data.preproc_agc->comp_rlink_releasek_msw = + acdb_agc->comp_rlink_release_k_msw; + acdb_data.preproc_agc->comp_rlink_releasek_lsw = + acdb_agc->comp_rlink_release_k_lsw; + acdb_data.preproc_agc->comp_rlink_rms_tav = + acdb_agc->comp_rlink_rms_trav; + return 0; +} + +static struct acdb_iir_block *get_audpreproc_irr_block(void) +{ + + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_IIR_TX) { + if (prs_hdr->iid == IID_AUDIO_IIR_COEFF) + return (struct acdb_iir_block *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + + +static s32 acdb_fill_audpreproc_iir(void) +{ + struct acdb_iir_block *acdb_iir; + + + acdb_iir = get_audpreproc_irr_block(); + if (!acdb_iir) { + MM_DBG("unable to find preproc iir parameters winding up\n"); + return -1; + } + memset(acdb_data.preproc_iir, 0, sizeof(*acdb_data.preproc_iir)); + + acdb_data.preproc_iir->cmd_id = + AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; + acdb_data.preproc_iir->stream_id = acdb_data.preproc_stream_id; + acdb_data.preproc_iir->active_flag = acdb_iir->enable_flag; + acdb_data.preproc_iir->num_bands = acdb_iir->stage_count; + + acdb_data.preproc_iir->numerator_coeff_b0_filter0_lsw = + acdb_iir->stages[0].b0_lo; + acdb_data.preproc_iir->numerator_coeff_b0_filter0_msw = + acdb_iir->stages[0].b0_hi; + acdb_data.preproc_iir->numerator_coeff_b1_filter0_lsw = + acdb_iir->stages[0].b1_lo; + acdb_data.preproc_iir->numerator_coeff_b1_filter0_msw = + acdb_iir->stages[0].b1_hi; + acdb_data.preproc_iir->numerator_coeff_b2_filter0_lsw = + acdb_iir->stages[0].b2_lo; + acdb_data.preproc_iir->numerator_coeff_b2_filter0_msw = + acdb_iir->stages[0].b2_hi; + + acdb_data.preproc_iir->numerator_coeff_b0_filter1_lsw = + acdb_iir->stages[1].b0_lo; + acdb_data.preproc_iir->numerator_coeff_b0_filter1_msw = + acdb_iir->stages[1].b0_hi; + acdb_data.preproc_iir->numerator_coeff_b1_filter1_lsw = + acdb_iir->stages[1].b1_lo; + acdb_data.preproc_iir->numerator_coeff_b1_filter1_msw = + acdb_iir->stages[1].b1_hi; + acdb_data.preproc_iir->numerator_coeff_b2_filter1_lsw = + acdb_iir->stages[1].b2_lo; + acdb_data.preproc_iir->numerator_coeff_b2_filter1_msw = + acdb_iir->stages[1].b2_hi; + + acdb_data.preproc_iir->numerator_coeff_b0_filter2_lsw = + acdb_iir->stages[2].b0_lo; + acdb_data.preproc_iir->numerator_coeff_b0_filter2_msw = + acdb_iir->stages[2].b0_hi; + acdb_data.preproc_iir->numerator_coeff_b1_filter2_lsw = + acdb_iir->stages[2].b1_lo; + acdb_data.preproc_iir->numerator_coeff_b1_filter2_msw = + acdb_iir->stages[2].b1_hi; + acdb_data.preproc_iir->numerator_coeff_b2_filter2_lsw = + acdb_iir->stages[2].b2_lo; + acdb_data.preproc_iir->numerator_coeff_b2_filter2_msw = + acdb_iir->stages[2].b2_hi; + + acdb_data.preproc_iir->numerator_coeff_b0_filter3_lsw = + acdb_iir->stages[3].b0_lo; + acdb_data.preproc_iir->numerator_coeff_b0_filter3_msw = + acdb_iir->stages[3].b0_hi; + acdb_data.preproc_iir->numerator_coeff_b1_filter3_lsw = + acdb_iir->stages[3].b1_lo; + acdb_data.preproc_iir->numerator_coeff_b1_filter3_msw = + acdb_iir->stages[3].b1_hi; + acdb_data.preproc_iir->numerator_coeff_b2_filter3_lsw = + acdb_iir->stages[3].b2_lo; + acdb_data.preproc_iir->numerator_coeff_b2_filter3_msw = + acdb_iir->stages[3].b2_hi; + + acdb_data.preproc_iir->denominator_coeff_a0_filter0_lsw = + acdb_iir->stages_a[0].a1_lo; + acdb_data.preproc_iir->denominator_coeff_a0_filter0_msw = + acdb_iir->stages_a[0].a1_hi; + acdb_data.preproc_iir->denominator_coeff_a1_filter0_lsw = + acdb_iir->stages_a[0].a2_lo; + acdb_data.preproc_iir->denominator_coeff_a1_filter0_msw = + acdb_iir->stages_a[0].a2_hi; + + acdb_data.preproc_iir->denominator_coeff_a0_filter1_lsw = + acdb_iir->stages_a[1].a1_lo; + acdb_data.preproc_iir->denominator_coeff_a0_filter1_msw = + acdb_iir->stages_a[1].a1_hi; + acdb_data.preproc_iir->denominator_coeff_a1_filter1_lsw = + acdb_iir->stages_a[1].a2_lo; + acdb_data.preproc_iir->denominator_coeff_a1_filter1_msw = + acdb_iir->stages_a[1].a2_hi; + + acdb_data.preproc_iir->denominator_coeff_a0_filter2_lsw = + acdb_iir->stages_a[2].a1_lo; + acdb_data.preproc_iir->denominator_coeff_a0_filter2_msw = + acdb_iir->stages_a[2].a1_hi; + acdb_data.preproc_iir->denominator_coeff_a1_filter2_lsw = + acdb_iir->stages_a[2].a2_lo; + acdb_data.preproc_iir->denominator_coeff_a1_filter2_msw = + acdb_iir->stages_a[2].a2_hi; + + acdb_data.preproc_iir->denominator_coeff_a0_filter3_lsw = + acdb_iir->stages_a[3].a1_lo; + acdb_data.preproc_iir->denominator_coeff_a0_filter3_msw = + acdb_iir->stages_a[3].a1_hi; + acdb_data.preproc_iir->denominator_coeff_a1_filter3_lsw = + acdb_iir->stages_a[3].a2_lo; + acdb_data.preproc_iir->denominator_coeff_a1_filter3_msw = + acdb_iir->stages_a[3].a2_hi; + + acdb_data.preproc_iir->shift_factor_filter0 = + acdb_iir->shift_factor[0]; + acdb_data.preproc_iir->shift_factor_filter1 = + acdb_iir->shift_factor[1]; + acdb_data.preproc_iir->shift_factor_filter2 = + acdb_iir->shift_factor[2]; + acdb_data.preproc_iir->shift_factor_filter3 = + acdb_iir->shift_factor[3]; + + acdb_data.preproc_iir->pan_of_filter0 = + acdb_iir->pan[0]; + acdb_data.preproc_iir->pan_of_filter1 = + acdb_iir->pan[1]; + acdb_data.preproc_iir->pan_of_filter2 = + acdb_iir->pan[2]; + acdb_data.preproc_iir->pan_of_filter3 = + acdb_iir->pan[3]; + return 0; +} + +static struct acdb_calib_gain_tx *get_audpreproc_cal_gain(void) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_CALIBRATION_GAIN_TX) { + if (prs_hdr->iid == + IID_AUDIO_CALIBRATION_GAIN_TX) { + MM_DBG("Got audpreproc_calib_gain_tx" + " block\n"); + return (struct acdb_calib_gain_tx *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + +static s32 acdb_fill_audpreproc_cal_gain(void) +{ + struct acdb_calib_gain_tx *acdb_calib_gain_tx = NULL; + + acdb_calib_gain_tx = get_audpreproc_cal_gain(); + if (acdb_calib_gain_tx == NULL) { + MM_ERR("unable to find audpreproc" + " calibration block returning\n"); + return -1; + } + MM_DBG("Calibration value" + " for calib_gain_tx %d\n", acdb_calib_gain_tx->audprecalgain); + memset(acdb_data.calib_gain_tx, 0, sizeof(*acdb_data.calib_gain_tx)); + + acdb_data.calib_gain_tx->cmd_id = + AUDPREPROC_CMD_CFG_CAL_GAIN_PARAMS; + acdb_data.calib_gain_tx->stream_id = acdb_data.preproc_stream_id; + acdb_data.calib_gain_tx->audprecalgain = + acdb_calib_gain_tx->audprecalgain; + return 0; +} + +static struct acdb_rmc_block *get_rmc_blk(void) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_RMC_TX) { + if (prs_hdr->iid == + IID_AUDIO_RMC_PARAM) { + MM_DBG("Got afe_rmc block\n"); + return (struct acdb_rmc_block *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + +struct acdb_fluence_block *get_audpp_fluence_block(void) +{ + struct header *prs_hdr; + u32 index = 0; + + while (index < acdb_data.acdb_result.used_bytes) { + prs_hdr = (struct header *)(acdb_data.virt_addr + index); + + if (prs_hdr->dbor_signature == DBOR_SIGNATURE) { + if (prs_hdr->abid == ABID_AUDIO_FLUENCE_TX) { + if (prs_hdr->iid == IID_AUDIO_FLUENCE_TX) { + MM_DBG("got fluence block\n"); + return (struct acdb_fluence_block *) + (acdb_data.virt_addr + index + + sizeof(struct header)); + } + } else { + index += prs_hdr->data_len + + sizeof(struct header); + } + } else { + break; + } + } + return NULL; +} + +static s32 acdb_fill_audpreproc_fluence(void) +{ + struct acdb_fluence_block *fluence_block = NULL; + fluence_block = get_audpp_fluence_block(); + if (!fluence_block) { + MM_ERR("error in finding fluence block\n"); + return -EPERM; + } + memset(&acdb_data.preproc_lvnv, 0, sizeof( + struct audpreproc_cmd_cfg_lvnv_param)); + memcpy(acdb_data.fluence_extbuff_virt, + &fluence_block->cs_tuningMode, + (sizeof(struct acdb_fluence_block) - + sizeof(fluence_block->csmode))); + acdb_data.preproc_lvnv.cmd_id = AUDPREPROC_CMD_CFG_LVNV_PARMS; + acdb_data.preproc_lvnv.stream_id = acdb_data.preproc_stream_id; + acdb_data.preproc_lvnv.cs_mode = fluence_block->csmode; + acdb_data.preproc_lvnv.lvnv_ext_buf_size = FLUENCE_BUF_SIZE; + acdb_data.preproc_lvnv.lvnv_ext_buf_start_lsw =\ + ((u32)(acdb_data.fluence_extbuff)\ + & 0x0000FFFF); + acdb_data.preproc_lvnv.lvnv_ext_buf_start_msw =\ + (((u32)acdb_data.fluence_extbuff\ + & 0xFFFF0000) >> 16); + return 0; +} + +s32 acdb_calibrate_audpreproc(void) +{ + s32 result = 0; + struct acdb_rmc_block *acdb_rmc = NULL; + + result = acdb_fill_audpreproc_agc(); + if (!IS_ERR_VALUE(result)) { + result = audpreproc_dsp_set_agc(acdb_data.preproc_agc, sizeof( + struct audpreproc_cmd_cfg_agc_params)); + if (result) { + MM_ERR("ACDB=> Failed to send AGC data to preproc)\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AUDPREC is calibrated with AGC parameters" + " for COPP ID %d and AUDREC session %d\n", + acdb_data.device_info->dev_id, + acdb_data.preproc_stream_id); + } + result = acdb_fill_audpreproc_iir(); + if (!IS_ERR_VALUE(result)) { + result = audpreproc_dsp_set_iir(acdb_data.preproc_iir, + sizeof(struct\ + audpreproc_cmd_cfg_iir_tuning_filter_params)); + if (result) { + MM_ERR("ACDB=> Failed to send IIR data to preproc\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("audpreproc is calibrated with iir parameters" + " for COPP ID %d and AUREC session %d\n", + acdb_data.device_info->dev_id, + acdb_data.preproc_stream_id); + } + result = acdb_fill_audpreproc_cal_gain(); + if (!(IS_ERR_VALUE(result))) { + result = audpreproc_dsp_set_gain_tx(acdb_data.calib_gain_tx, + sizeof(struct audpreproc_cmd_cfg_cal_gain)); + if (result) { + MM_ERR("ACDB=> Failed to send calib_gain_tx" + " data to preproc\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AUDPREPROC is calibrated" + " with calib_gain_tx\n"); + } + if (acdb_data.build_id[17] != '0') { + acdb_rmc = get_rmc_blk(); + if (acdb_rmc != NULL) { + result = afe_config_rmc_block(acdb_rmc); + if (result) { + MM_ERR("ACDB=> Failed to send rmc" + " data to afe\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AFE is calibrated with rmc params\n"); + } else + MM_DBG("RMC block was not found\n"); + } + if (!acdb_data.fleuce_feature_status[acdb_data.preproc_stream_id]) { + result = acdb_fill_audpreproc_fluence(); + if (!(IS_ERR_VALUE(result))) { + result = audpreproc_dsp_set_lvnv( + &acdb_data.preproc_lvnv, + sizeof(struct\ + audpreproc_cmd_cfg_lvnv_param)); + if (result) { + MM_ERR("ACDB=> Failed to send lvnv " + "data to preproc\n"); + result = -EINVAL; + goto done; + } else + MM_DBG("AUDPREPROC is calibrated" + " with lvnv parameters\n"); + } else + MM_ERR("fluence block is not found\n"); + } else + MM_DBG("fluence block override\n"); +done: + return result; +} + +static s32 acdb_send_calibration(void) +{ + s32 result = 0; + + if ((acdb_data.device_info->dev_type & RX_DEVICE) == 1) { + result = acdb_calibrate_audpp(); + if (result) + goto done; + } else if ((acdb_data.device_info->dev_type & TX_DEVICE) == 2) { + result = acdb_calibrate_audpreproc(); + if (result) + goto done; + if (acdb_data.preproc_stream_id == 0) + acdb_data.audrec_applied |= AUDREC0_READY; + else if (acdb_data.preproc_stream_id == 1) + acdb_data.audrec_applied |= AUDREC1_READY; + else if (acdb_data.preproc_stream_id == 2) + acdb_data.audrec_applied |= AUDREC2_READY; + MM_DBG("acdb_data.audrec_applied = %x\n", + acdb_data.audrec_applied); + } +done: + return result; +} + +static u8 check_tx_acdb_values_cached(void) +{ + u8 stream_id = acdb_data.preproc_stream_id; + + if ((acdb_data.device_info->dev_id == + acdb_cache_tx[stream_id].device_info.dev_id) && + (acdb_data.device_info->sample_rate == + acdb_cache_tx[stream_id].device_info.sample_rate) && + (acdb_data.device_info->acdb_id == + acdb_cache_tx[stream_id].device_info.acdb_id) && + (acdb_cache_tx[stream_id].node_status == + ACDB_VALUES_FILLED)) + return 0; + else + return 1; +} + +static void handle_tx_device_ready_callback(void) +{ + u8 i = 0; + u8 ret = 0; + u8 acdb_value_apply = 0; + u8 result = 0; + u8 stream_id = acdb_data.preproc_stream_id; + + if (acdb_data.multiple_sessions) { + for (i = 0; i < MAX_AUDREC_SESSIONS; i++) { + /*check is to exclude copying acdb values in the + current node pointed by acdb_data structure*/ + if (acdb_cache_tx[i].phys_addr_acdb_values != + acdb_data.phys_addr) { + ret = check_device_info_already_present(\ + *acdb_data.device_info, + &acdb_cache_tx[i]); + if (ret) { + memcpy((char *)acdb_cache_tx[i].\ + virt_addr_acdb_values, + (char *)acdb_data.virt_addr, + ACDB_BUF_SIZE); + acdb_cache_tx[i].node_status = + ACDB_VALUES_FILLED; + } + } + } + acdb_data.multiple_sessions = 0; + } + /*check wheather AUDREC enabled before device call backs*/ + if ((acdb_data.acdb_state & AUDREC0_READY) && + !(acdb_data.audrec_applied & AUDREC0_READY)) { + MM_DBG("AUDREC0 already enabled apply acdb values\n"); + acdb_value_apply |= AUDREC0_READY; + } else if ((acdb_data.acdb_state & AUDREC1_READY) && + !(acdb_data.audrec_applied & AUDREC1_READY)) { + MM_DBG("AUDREC1 already enabled apply acdb values\n"); + acdb_value_apply |= AUDREC1_READY; + } else if ((acdb_data.acdb_state & AUDREC2_READY) && + !(acdb_data.audrec_applied & AUDREC2_READY)) { + MM_DBG("AUDREC2 already enabled apply acdb values\n"); + acdb_value_apply |= AUDREC2_READY; + } + if (acdb_value_apply) { + if (session_info[stream_id].sampling_freq) + acdb_data.device_info->sample_rate = + session_info[stream_id].sampling_freq; + result = check_tx_acdb_values_cached(); + if (result) { + result = acdb_get_calibration(); + if (result < 0) { + MM_ERR("Not able to get calibration" + " data continue\n"); + return; + } + } + acdb_cache_tx[stream_id].node_status = ACDB_VALUES_FILLED; + acdb_send_calibration(); + } +} + +static struct acdb_cache_node *get_acdb_values_from_cache_tx(u32 stream_id) +{ + MM_DBG("searching node with stream_id %d\n", stream_id); + if ((acdb_cache_tx[stream_id].stream_id == stream_id) && + (acdb_cache_tx[stream_id].node_status == + ACDB_VALUES_NOT_FILLED)) { + return &acdb_cache_tx[stream_id]; + } + MM_DBG("Error! in finding node\n"); + return NULL; +} + +static void update_acdb_data_struct(struct acdb_cache_node *cur_node) +{ + if (cur_node) { + acdb_data.device_info = &cur_node->device_info; + acdb_data.virt_addr = cur_node->virt_addr_acdb_values; + acdb_data.phys_addr = cur_node->phys_addr_acdb_values; + } else + MM_ERR("error in curent node\n"); +} + +static void send_acdb_values_for_active_devices(void) +{ + u32 i = 0; + for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) { + if (acdb_cache_rx[i].node_status == + ACDB_VALUES_FILLED) { + update_acdb_data_struct(&acdb_cache_rx[i]); + if (acdb_data.acdb_state & CAL_DATA_READY) + acdb_send_calibration(); + } + } +} + +static s32 initialize_rpc(void) +{ + s32 result = 0; + + result = daldevice_attach(DALDEVICEID_ACDB, ACDB_PORT_NAME, + ACDB_CPU, &acdb_data.handle); + + if (result) { + MM_ERR("ACDB=> Device Attach failed\n"); + result = -ENODEV; + goto done; + } +done: + return result; +} + +static u32 allocate_memory_acdb_cache_tx(void) +{ + u32 result = 0; + u32 i = 0; + u32 err = 0; + /*initialize local cache */ + for (i = 0; i < MAX_AUDREC_SESSIONS; i++) { + acdb_cache_tx[i].phys_addr_acdb_values = + allocate_contiguous_ebi_nomap(ACDB_BUF_SIZE, + SZ_4K); + + if (!acdb_cache_tx[i].phys_addr_acdb_values) { + MM_ERR("ACDB=> Cannot allocate physical memory\n"); + result = -ENOMEM; + goto error; + } + acdb_cache_tx[i].map_v_addr = ioremap( + acdb_cache_tx[i].phys_addr_acdb_values, + ACDB_BUF_SIZE); + if (IS_ERR(acdb_cache_tx[i].map_v_addr)) { + MM_ERR("ACDB=> Could not map physical address\n"); + result = -ENOMEM; + free_contiguous_memory_by_paddr( + acdb_cache_tx[i].phys_addr_acdb_values); + goto error; + } + acdb_cache_tx[i].virt_addr_acdb_values = + acdb_cache_tx[i].map_v_addr; + memset(acdb_cache_tx[i].virt_addr_acdb_values, 0, + ACDB_BUF_SIZE); + } + return result; +error: + for (err = 0; err < i; err++) { + iounmap(acdb_cache_tx[err].map_v_addr); + free_contiguous_memory_by_paddr( + acdb_cache_tx[err].phys_addr_acdb_values); + } + return result; +} + +static u32 allocate_memory_acdb_cache_rx(void) +{ + u32 result = 0; + u32 i = 0; + u32 err = 0; + + /*initialize local cache */ + for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) { + acdb_cache_rx[i].phys_addr_acdb_values = + allocate_contiguous_ebi_nomap( + ACDB_BUF_SIZE, SZ_4K); + + if (!acdb_cache_rx[i].phys_addr_acdb_values) { + MM_ERR("ACDB=> Can not allocate physical memory\n"); + result = -ENOMEM; + goto error; + } + acdb_cache_rx[i].map_v_addr = + ioremap(acdb_cache_rx[i].phys_addr_acdb_values, + ACDB_BUF_SIZE); + if (IS_ERR(acdb_cache_rx[i].map_v_addr)) { + MM_ERR("ACDB=> Could not map physical address\n"); + result = -ENOMEM; + free_contiguous_memory_by_paddr( + acdb_cache_rx[i].phys_addr_acdb_values); + goto error; + } + acdb_cache_rx[i].virt_addr_acdb_values = + acdb_cache_rx[i].map_v_addr; + memset(acdb_cache_rx[i].virt_addr_acdb_values, 0, + ACDB_BUF_SIZE); + } + return result; +error: + for (err = 0; err < i; err++) { + iounmap(acdb_cache_rx[err].map_v_addr); + free_contiguous_memory_by_paddr( + acdb_cache_rx[err].phys_addr_acdb_values); + } + return result; +} + +static u32 allocate_memory_acdb_get_blk(void) +{ + u32 result = 0; + acdb_data.get_blk_paddr = allocate_contiguous_ebi_nomap( + ACDB_BUF_SIZE, SZ_4K); + if (!acdb_data.get_blk_paddr) { + MM_ERR("ACDB=> Cannot allocate physical memory\n"); + result = -ENOMEM; + goto error; + } + acdb_data.map_v_get_blk = ioremap(acdb_data.get_blk_paddr, + ACDB_BUF_SIZE); + if (IS_ERR(acdb_data.map_v_get_blk)) { + MM_ERR("ACDB=> Could not map physical address\n"); + result = -ENOMEM; + free_contiguous_memory_by_paddr( + acdb_data.get_blk_paddr); + goto error; + } + acdb_data.get_blk_kvaddr = acdb_data.map_v_get_blk; + memset(acdb_data.get_blk_kvaddr, 0, ACDB_BUF_SIZE); +error: + return result; +} + +static void free_memory_acdb_cache_rx(void) +{ + u32 i = 0; + + for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) { + iounmap(acdb_cache_rx[i].map_v_addr); + free_contiguous_memory_by_paddr( + acdb_cache_rx[i].phys_addr_acdb_values); + } +} + +static void free_memory_acdb_cache_tx(void) +{ + u32 i = 0; + + for (i = 0; i < MAX_AUDREC_SESSIONS; i++) { + iounmap(acdb_cache_tx[i].map_v_addr); + free_contiguous_memory_by_paddr( + acdb_cache_tx[i].phys_addr_acdb_values); + } +} + +static void free_memory_acdb_get_blk(void) +{ + iounmap(acdb_data.map_v_get_blk); + free_contiguous_memory_by_paddr(acdb_data.get_blk_paddr); +} + +static s32 initialize_memory(void) +{ + s32 result = 0; + + result = allocate_memory_acdb_get_blk(); + if (result < 0) { + MM_ERR("memory allocation for get blk failed\n"); + goto done; + } + + result = allocate_memory_acdb_cache_rx(); + if (result < 0) { + MM_ERR("memory allocation for rx cache is failed\n"); + free_memory_acdb_get_blk(); + goto done; + } + result = allocate_memory_acdb_cache_tx(); + if (result < 0) { + MM_ERR("memory allocation for tx cache is failed\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + goto done; + } + acdb_data.pp_iir = kmalloc(sizeof(*acdb_data.pp_iir), + GFP_KERNEL); + if (acdb_data.pp_iir == NULL) { + MM_ERR("ACDB=> Could not allocate postproc iir memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + result = -ENOMEM; + goto done; + } + + acdb_data.pp_mbadrc = kmalloc(sizeof(*acdb_data.pp_mbadrc), GFP_KERNEL); + if (acdb_data.pp_mbadrc == NULL) { + MM_ERR("ACDB=> Could not allocate postproc mbadrc memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + result = -ENOMEM; + goto done; + } + acdb_data.calib_gain_rx = kmalloc(sizeof(*acdb_data.calib_gain_rx), + GFP_KERNEL); + if (acdb_data.calib_gain_rx == NULL) { + MM_ERR("ACDB=> Could not allocate" + " postproc calib_gain_rx memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + result = -ENOMEM; + goto done; + } + + acdb_data.preproc_agc = kmalloc(sizeof(*acdb_data.preproc_agc), + GFP_KERNEL); + if (acdb_data.preproc_agc == NULL) { + MM_ERR("ACDB=> Could not allocate preproc agc memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + result = -ENOMEM; + goto done; + } + + acdb_data.preproc_iir = kmalloc(sizeof(*acdb_data.preproc_iir), + GFP_KERNEL); + if (acdb_data.preproc_iir == NULL) { + MM_ERR("ACDB=> Could not allocate preproc iir memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + kfree(acdb_data.preproc_agc); + result = -ENOMEM; + goto done; + } + acdb_data.calib_gain_tx = kmalloc(sizeof(*acdb_data.calib_gain_tx), + GFP_KERNEL); + if (acdb_data.calib_gain_tx == NULL) { + MM_ERR("ACDB=> Could not allocate" + " preproc calib_gain_tx memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + kfree(acdb_data.preproc_agc); + kfree(acdb_data.preproc_iir); + result = -ENOMEM; + goto done; + } + acdb_data.pbe_block = kmalloc(sizeof(*acdb_data.pbe_block), + GFP_KERNEL); + if (acdb_data.pbe_block == NULL) { + MM_ERR("ACDB=> Could not allocate pbe_block memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + kfree(acdb_data.preproc_agc); + kfree(acdb_data.preproc_iir); + kfree(acdb_data.calib_gain_tx); + result = -ENOMEM; + goto done; + } + acdb_data.pbe_extbuff = (u16 *) allocate_contiguous_ebi_nomap( + PBE_BUF_SIZE, SZ_4K); + if (!acdb_data.pbe_extbuff) { + MM_ERR("ACDB=> Cannot allocate physical memory\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + kfree(acdb_data.preproc_agc); + kfree(acdb_data.preproc_iir); + kfree(acdb_data.calib_gain_tx); + kfree(acdb_data.pbe_block); + result = -ENOMEM; + goto done; + } + acdb_data.fluence_extbuff = allocate_contiguous_ebi_nomap( + FLUENCE_BUF_SIZE, SZ_4K); + if (!acdb_data.fluence_extbuff) { + MM_ERR("ACDB=> cannot allocate physical memory for " + "fluence block\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + kfree(acdb_data.preproc_agc); + kfree(acdb_data.preproc_iir); + kfree(acdb_data.calib_gain_tx); + kfree(acdb_data.pbe_block); + free_contiguous_memory_by_paddr((int32_t)acdb_data.pbe_extbuff); + result = -ENOMEM; + goto done; + } + acdb_data.map_v_fluence = ioremap( + acdb_data.fluence_extbuff, + FLUENCE_BUF_SIZE); + if (IS_ERR(acdb_data.map_v_fluence)) { + MM_ERR("ACDB=> Could not map physical address\n"); + free_memory_acdb_get_blk(); + free_memory_acdb_cache_rx(); + free_memory_acdb_cache_tx(); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.calib_gain_rx); + kfree(acdb_data.preproc_agc); + kfree(acdb_data.preproc_iir); + kfree(acdb_data.calib_gain_tx); + kfree(acdb_data.pbe_block); + free_contiguous_memory_by_paddr( + (int32_t)acdb_data.pbe_extbuff); + free_contiguous_memory_by_paddr( + (int32_t)acdb_data.fluence_extbuff); + result = -ENOMEM; + goto done; + } else + acdb_data.fluence_extbuff_virt = + acdb_data.map_v_fluence; +done: + return result; +} + +static u32 free_acdb_cache_node(union auddev_evt_data *evt) +{ + u32 session_id; + if ((evt->audcal_info.dev_type & TX_DEVICE) == 2) { + /*Second argument to find_first_bit should be maximum number + of bits interested + */ + session_id = find_first_bit( + (unsigned long *)&(evt->audcal_info.sessions), + sizeof(evt->audcal_info.sessions) * 8); + MM_DBG("freeing node %d for tx device", session_id); + acdb_cache_tx[session_id]. + node_status = ACDB_VALUES_NOT_FILLED; + } else { + MM_DBG("freeing rx cache node %d\n", + evt->audcal_info.dev_id); + acdb_cache_rx[evt->audcal_info.dev_id]. + node_status = ACDB_VALUES_NOT_FILLED; + } + return 0; +} + +static u8 check_device_change(struct auddev_evt_audcal_info audcal_info) +{ + if (!acdb_data.device_info) { + MM_ERR("not pointing to previous valid device detail\n"); + return 1; /*device info will not be pointing to*/ + /* valid device when acdb driver comes up*/ + } + if ((audcal_info.dev_id == acdb_data.device_info->dev_id) && + (audcal_info.sample_rate == + acdb_data.device_info->sample_rate) && + (audcal_info.acdb_id == acdb_data.device_info->acdb_id)) { + return 0; + } + return 1; +} + +static void device_cb(u32 evt_id, union auddev_evt_data *evt, void *private) +{ + struct auddev_evt_audcal_info audcal_info; + struct acdb_cache_node *acdb_cache_free_node = NULL; + u32 stream_id = 0; + u8 ret = 0; + u8 count = 0; + u8 i = 0; + u8 device_change = 0; + + if (!((evt_id == AUDDEV_EVT_DEV_RDY) || + (evt_id == AUDDEV_EVT_DEV_RLS))) { + goto done; + } + /*if session value is zero it indicates that device call back is for + voice call we will drop the request as acdb values for voice call is + not applied from acdb driver*/ + if (!evt->audcal_info.sessions) { + MM_DBG("no active sessions and call back is for" + " voice call\n"); + goto done; + } + if (evt_id == AUDDEV_EVT_DEV_RLS) { + MM_DBG("got release command for dev %d\n", + evt->audcal_info.dev_id); + acdb_data.acdb_state &= ~CAL_DATA_READY; + free_acdb_cache_node(evt); + /*reset the applied flag for the session routed to the device*/ + acdb_data.audrec_applied &= ~(evt->audcal_info.sessions + << AUDREC_OFFSET); + goto done; + } + if (((evt->audcal_info.dev_type & RX_DEVICE) == 1) && + (evt->audcal_info.acdb_id == PSEUDO_ACDB_ID)) { + MM_INFO("device cb is for rx device with pseudo acdb id\n"); + goto done; + } + audcal_info = evt->audcal_info; + MM_DBG("dev_id = %d\n", audcal_info.dev_id); + MM_DBG("sample_rate = %d\n", audcal_info.sample_rate); + MM_DBG("acdb_id = %d\n", audcal_info.acdb_id); + MM_DBG("sessions = %d\n", audcal_info.sessions); + MM_DBG("acdb_state = %x\n", acdb_data.acdb_state); + mutex_lock(&acdb_data.acdb_mutex); + device_change = check_device_change(audcal_info); + if (!device_change) { + if ((audcal_info.dev_type & TX_DEVICE) == 2) { + if (!(acdb_data.acdb_state & AUDREC0_READY)) + acdb_data.audrec_applied &= ~AUDREC0_READY; + if (!(acdb_data.acdb_state & AUDREC1_READY)) + acdb_data.audrec_applied &= ~AUDREC1_READY; + if (!(acdb_data.acdb_state & AUDREC2_READY)) + acdb_data.audrec_applied &= ~AUDREC2_READY; + acdb_data.acdb_state &= ~CAL_DATA_READY; + goto update_cache; + } + } else + /* state is updated to querry the modem for values */ + acdb_data.acdb_state &= ~CAL_DATA_READY; + +update_cache: + if ((audcal_info.dev_type & TX_DEVICE) == 2) { + /*loop is to take care of use case:- multiple Audrec + sessions are routed before enabling the device in this use + case we will get the sessions value as bits set for all the + sessions routed before device enable, so we should take care + of copying device info to all the sessions*/ + for (i = 0; i < MAX_AUDREC_SESSIONS; i++) { + stream_id = ((audcal_info.sessions >> i) & 0x01); + if (stream_id) { + acdb_cache_free_node = &acdb_cache_tx[i]; + ret = check_device_info_already_present( + audcal_info, + acdb_cache_free_node); + acdb_cache_free_node->stream_id = i; + acdb_data.cur_tx_session = i; + count++; + } + } + if (count > 1) + acdb_data.multiple_sessions = 1; + } else { + acdb_cache_free_node = &acdb_cache_rx[audcal_info.dev_id]; + ret = check_device_info_already_present(audcal_info, + acdb_cache_free_node); + if (ret == 1) { + MM_DBG("got device ready call back for another " + "audplay task sessions on same COPP\n"); + /*stream_id is used to keep track of number of active*/ + /*sessions active on this device*/ + acdb_cache_free_node->stream_id++; + mutex_unlock(&acdb_data.acdb_mutex); + goto done; + } + acdb_cache_free_node->stream_id++; + } + update_acdb_data_struct(acdb_cache_free_node); + acdb_data.device_cb_compl = 1; + mutex_unlock(&acdb_data.acdb_mutex); + wake_up(&acdb_data.wait); +done: + return; +} + + +static s32 register_device_cb(void) +{ + s32 result = 0; + + result = auddev_register_evt_listner((AUDDEV_EVT_DEV_RDY + | AUDDEV_EVT_DEV_RLS), + AUDDEV_CLNT_AUDIOCAL, 0, device_cb, (void *)&acdb_data); + + if (result) { + MM_ERR("ACDB=> Could not register device callback\n"); + result = -ENODEV; + goto done; + } +done: + return result; +} + +static void audpp_cb(void *private, u32 id, u16 *msg) +{ + MM_DBG("\n"); + if (id != AUDPP_MSG_CFG_MSG) + goto done; + + if (msg[0] == AUDPP_MSG_ENA_DIS) { + if (--acdb_cache_rx[acdb_data.\ + device_info->dev_id].stream_id <= 0) { + acdb_data.acdb_state &= ~AUDPP_READY; + acdb_cache_rx[acdb_data.device_info->dev_id]\ + .stream_id = 0; + MM_DBG("AUDPP_MSG_ENA_DIS\n"); + } + goto done; + } + + acdb_data.acdb_state |= AUDPP_READY; + acdb_data.audpp_cb_compl = 1; + wake_up(&acdb_data.wait); +done: + return; +} + +static s8 handle_audpreproc_cb(void) +{ + struct acdb_cache_node *acdb_cached_values; + s8 result = 0; + u8 stream_id = acdb_data.preproc_stream_id; + acdb_data.preproc_cb_compl = 0; + acdb_cached_values = get_acdb_values_from_cache_tx(stream_id); + if (acdb_cached_values == NULL) { + MM_DBG("ERROR: to get chached acdb values\n"); + return -EPERM; + } + update_acdb_data_struct(acdb_cached_values); + if (acdb_data.device_info->dev_id == PSEUDO_ACDB_ID) { + MM_INFO("audpreproc is routed to pseudo device\n"); + return result; + } + if (acdb_data.build_id[17] == '1') { + if (session_info[stream_id].sampling_freq) + acdb_data.device_info->sample_rate = + session_info[stream_id].sampling_freq; + } + if (!(acdb_data.acdb_state & CAL_DATA_READY)) { + result = check_tx_acdb_values_cached(); + if (result) { + result = acdb_get_calibration(); + if (result < 0) { + MM_ERR("failed to get calibration data\n"); + return result; + } + } + acdb_cached_values->node_status = ACDB_VALUES_FILLED; + } + return result; +} + +void fluence_feature_update(int enable, int stream_id) +{ + MM_INFO("Fluence feature over ride with = %d\n", enable); + acdb_data.fleuce_feature_status[stream_id] = enable; +} +EXPORT_SYMBOL(fluence_feature_update); + +static void audpreproc_cb(void *private, u32 id, void *msg) +{ + struct audpreproc_cmd_enc_cfg_done_msg *tmp; + u8 result = 0; + int stream_id = 0; + if (id != AUDPREPROC_CMD_ENC_CFG_DONE_MSG) + goto done; + + tmp = (struct audpreproc_cmd_enc_cfg_done_msg *)msg; + acdb_data.preproc_stream_id = tmp->stream_id; + stream_id = acdb_data.preproc_stream_id; + get_audrec_session_info(stream_id, &session_info[stream_id]); + MM_DBG("rec_enc_type = %x\n", tmp->rec_enc_type); + if ((tmp->rec_enc_type & 0x8000) == + AUD_PREPROC_CONFIG_DISABLED) { + if (acdb_data.preproc_stream_id == 0) { + acdb_data.acdb_state &= ~AUDREC0_READY; + acdb_data.audrec_applied &= ~AUDREC0_READY; + } else if (acdb_data.preproc_stream_id == 1) { + acdb_data.acdb_state &= ~AUDREC1_READY; + acdb_data.audrec_applied &= ~AUDREC1_READY; + } else if (acdb_data.preproc_stream_id == 2) { + acdb_data.acdb_state &= ~AUDREC2_READY; + acdb_data.audrec_applied &= ~AUDREC2_READY; + } + acdb_data.fleuce_feature_status[stream_id] = 0; + acdb_cache_tx[tmp->stream_id].node_status =\ + ACDB_VALUES_NOT_FILLED; + acdb_data.acdb_state &= ~CAL_DATA_READY; + goto done; + } + /*Following check is added to make sure that device info + is updated. audpre proc layer enabled without device + callback at this scenario we should not access + device information + */ + if (acdb_data.build_id[17] != '0') { + if (acdb_data.device_info && + session_info[stream_id].sampling_freq) { + acdb_data.device_info->sample_rate = + session_info[stream_id].sampling_freq; + result = check_tx_acdb_values_cached(); + if (!result) { + MM_INFO("acdb values for the stream is" \ + " querried from modem"); + acdb_data.acdb_state |= CAL_DATA_READY; + } else { + acdb_data.acdb_state &= ~CAL_DATA_READY; + } + } + } + if (acdb_data.preproc_stream_id == 0) + acdb_data.acdb_state |= AUDREC0_READY; + else if (acdb_data.preproc_stream_id == 1) + acdb_data.acdb_state |= AUDREC1_READY; + else if (acdb_data.preproc_stream_id == 2) + acdb_data.acdb_state |= AUDREC2_READY; + acdb_data.preproc_cb_compl = 1; + MM_DBG("acdb_data.acdb_state = %x\n", acdb_data.acdb_state); + wake_up(&acdb_data.wait); +done: + return; +} + +static s32 register_audpp_cb(void) +{ + s32 result = 0; + + acdb_data.audpp_cb.fn = audpp_cb; + acdb_data.audpp_cb.private = NULL; + result = audpp_register_event_callback(&acdb_data.audpp_cb); + if (result) { + MM_ERR("ACDB=> Could not register audpp callback\n"); + result = -ENODEV; + goto done; + } +done: + return result; +} + +static s32 register_audpreproc_cb(void) +{ + s32 result = 0; + + acdb_data.audpreproc_cb.fn = audpreproc_cb; + acdb_data.audpreproc_cb.private = NULL; + result = audpreproc_register_event_callback(&acdb_data.audpreproc_cb); + if (result) { + MM_ERR("ACDB=> Could not register audpreproc callback\n"); + result = -ENODEV; + goto done; + } + +done: + return result; +} + +static s32 acdb_initialize_data(void) +{ + s32 result = 0; + + mutex_init(&acdb_data.acdb_mutex); + + result = initialize_rpc(); + if (result) + goto err; + + result = initialize_memory(); + if (result) + goto err1; + + result = register_device_cb(); + if (result) + goto err2; + + result = register_audpp_cb(); + if (result) + goto err3; + + result = register_audpreproc_cb(); + if (result) + goto err4; + + + return result; + +err4: + result = audpreproc_unregister_event_callback(&acdb_data.audpreproc_cb); + if (result) + MM_ERR("ACDB=> Could not unregister audpreproc callback\n"); +err3: + result = audpp_unregister_event_callback(&acdb_data.audpp_cb); + if (result) + MM_ERR("ACDB=> Could not unregister audpp callback\n"); +err2: + result = auddev_unregister_evt_listner(AUDDEV_CLNT_AUDIOCAL, 0); + if (result) + MM_ERR("ACDB=> Could not unregister device callback\n"); +err1: + daldevice_detach(acdb_data.handle); + acdb_data.handle = NULL; +err: + return result; +} + +static s32 initialize_modem_acdb(void) +{ + struct acdb_cmd_init_adie acdb_cmd; + u8 codec_type = -1; + s32 result = 0; + u8 iterations = 0; + + codec_type = adie_get_detected_codec_type(); + if (codec_type == MARIMBA_ID) + acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_MARIMBA; + else if (codec_type == TIMPANI_ID) + acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_TIMPANI; + else + acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_UNKNOWN; + acdb_cmd.command_id = ACDB_CMD_INITIALIZE_FOR_ADIE; + do { + /*Initialize ACDB software on modem based on codec type*/ + result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle, + (const void *)&acdb_cmd, sizeof(acdb_cmd), + &acdb_data.acdb_result, + sizeof(acdb_data.acdb_result)); + if (result < 0) { + MM_ERR("ACDB=> RPC failure result = %d\n", result); + goto error; + } + /*following check is introduced to handle boot up race + condition between AUDCAL SW peers running on apps + and modem (ACDB_RES_BADSTATE indicates modem AUDCAL SW is + not in initialized sate) we need to retry to get ACDB + initialized*/ + if (acdb_data.acdb_result.result == ACDB_RES_BADSTATE) { + msleep(500); + iterations++; + } else if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) { + MM_DBG("Modem ACDB SW initialized ((iterations = %d)\n", + iterations); + return result; + } else { + MM_ERR("ACDB=> Modem ACDB SW failed to initialize" + " reuslt = %d, (iterations = %d)\n", + acdb_data.acdb_result.result, + iterations); + goto error; + } + } while (iterations < MAX_RETRY); + MM_ERR("ACDB=> AUDCAL SW on modem is not in intiailized state (%d)\n", + acdb_data.acdb_result.result); +error: + result = -EINVAL; + return result; +} + +static s32 acdb_calibrate_device(void *data) +{ + s32 result = 0; + + /* initialize driver */ + result = acdb_initialize_data(); + if (result) + goto done; + if (acdb_data.build_id[17] != '0') { + result = initialize_modem_acdb(); + if (result < 0) + MM_ERR("failed to initialize modem ACDB\n"); + } + + while (!kthread_should_stop()) { + MM_DBG("Waiting for call back events\n"); + wait_event_interruptible(acdb_data.wait, + (acdb_data.device_cb_compl + | acdb_data.audpp_cb_compl + | acdb_data.preproc_cb_compl)); + mutex_lock(&acdb_data.acdb_mutex); + if (acdb_data.device_cb_compl) { + acdb_data.device_cb_compl = 0; + if (!(acdb_data.acdb_state & CAL_DATA_READY)) { + if ((acdb_data.device_info->dev_type + & RX_DEVICE) == 1) { + /*we need to get calibration values + only for RX device as resampler + moved to start of the pre - proc chain + tx calibration value will be based on + sampling frequency what audrec is + configured, calibration values for tx + device are fetch in audpreproc + callback*/ + result = acdb_get_calibration(); + if (result < 0) { + mutex_unlock( + &acdb_data.acdb_mutex); + MM_ERR("Not able to get " + "calibration " + "data continue\n"); + continue; + } + } + } + MM_DBG("acdb state = %d\n", + acdb_data.acdb_state); + if ((acdb_data.device_info->dev_type & TX_DEVICE) == 2) + handle_tx_device_ready_callback(); + else { + acdb_cache_rx[acdb_data.device_info->dev_id]\ + .node_status = + ACDB_VALUES_FILLED; + if (acdb_data.acdb_state & + AUDPP_READY) { + MM_DBG("AUDPP already enabled " + "apply acdb values\n"); + goto apply; + } + } + } + + if (!(acdb_data.audpp_cb_compl || + acdb_data.preproc_cb_compl)) { + MM_DBG("need to wait for either AUDPP / AUDPREPROC " + "Event\n"); + mutex_unlock(&acdb_data.acdb_mutex); + continue; + } else { + MM_DBG("got audpp / preproc call back\n"); + if (acdb_data.audpp_cb_compl) { + send_acdb_values_for_active_devices(); + acdb_data.audpp_cb_compl = 0; + mutex_unlock(&acdb_data.acdb_mutex); + continue; + } else { + result = handle_audpreproc_cb(); + if (result < 0) { + mutex_unlock(&acdb_data.acdb_mutex); + continue; + } + } + } +apply: + if (acdb_data.acdb_state & CAL_DATA_READY) + result = acdb_send_calibration(); + + mutex_unlock(&acdb_data.acdb_mutex); + } +done: + return 0; +} + +static int __init acdb_init(void) +{ + + s32 result = 0; + + memset(&acdb_data, 0, sizeof(acdb_data)); + spin_lock_init(&acdb_data.dsp_lock); + acdb_data.cb_thread_task = kthread_run(acdb_calibrate_device, + NULL, "acdb_cb_thread"); + + if (IS_ERR(acdb_data.cb_thread_task)) { + MM_ERR("ACDB=> Could not register cb thread\n"); + result = -ENODEV; + goto err; + } + + acdb_data.build_id = socinfo_get_build_id(); + MM_INFO("build id used is = %s\n", acdb_data.build_id); + +#ifdef CONFIG_DEBUG_FS + /*This is RTC specific INIT used only with debugfs*/ + if (!rtc_acdb_init()) + MM_ERR("RTC ACDB=>INIT Failure\n"); + +#endif + init_waitqueue_head(&acdb_data.wait); + + return misc_register(&acdb_misc); +err: + return result; +} + +static void __exit acdb_exit(void) +{ + s32 result = 0; + u32 i = 0; + + result = auddev_unregister_evt_listner(AUDDEV_CLNT_AUDIOCAL, 0); + if (result) + MM_ERR("ACDB=> Could not unregister device callback\n"); + + result = audpp_unregister_event_callback(&acdb_data.audpp_cb); + if (result) + MM_ERR("ACDB=> Could not unregister audpp callback\n"); + + result = audpreproc_unregister_event_callback(&acdb_data.\ + audpreproc_cb); + if (result) + MM_ERR("ACDB=> Could not unregister audpreproc callback\n"); + + result = kthread_stop(acdb_data.cb_thread_task); + if (result) + MM_ERR("ACDB=> Could not stop kthread\n"); + + free_memory_acdb_get_blk(); + + for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) { + if (i < MAX_AUDREC_SESSIONS) { + iounmap(acdb_cache_tx[i].map_v_addr); + free_contiguous_memory_by_paddr( + acdb_cache_tx[i].phys_addr_acdb_values); + } + iounmap(acdb_cache_rx[i].map_v_addr); + free_contiguous_memory_by_paddr( + acdb_cache_rx[i].phys_addr_acdb_values); + } + kfree(acdb_data.device_info); + kfree(acdb_data.pp_iir); + kfree(acdb_data.pp_mbadrc); + kfree(acdb_data.preproc_agc); + kfree(acdb_data.preproc_iir); + free_contiguous_memory_by_paddr( + (int32_t)acdb_data.pbe_extbuff); + iounmap(acdb_data.map_v_fluence); + free_contiguous_memory_by_paddr( + (int32_t)acdb_data.fluence_extbuff); + mutex_destroy(&acdb_data.acdb_mutex); + memset(&acdb_data, 0, sizeof(acdb_data)); + #ifdef CONFIG_DEBUG_FS + rtc_acdb_deinit(); + #endif +} + +late_initcall(acdb_init); +module_exit(acdb_exit); + +MODULE_DESCRIPTION("MSM 7x30 Audio ACDB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c b/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c new file mode 100644 index 00000000000..95f0547ed01 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c @@ -0,0 +1,1754 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Size must be power of 2 */ +#define BUFSZ_MAX 32768 /* Includes meta in size */ +#define BUFSZ_MIN 4096 /* Includes meta in size */ +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_ADPCM 1 + +#define PCM_BUFSZ_MIN 8216 /* Hold one stereo ADPCM frame and meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDADPCM_METAFIELD_MASK 0xFFFF0000 +#define AUDADPCM_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDADPCM_EOS_FLG_MASK 0x01 +#define AUDADPCM_EOS_NONE 0x0 /* No EOS detected */ +#define AUDADPCM_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDADPCM_EVENT_NUM 10 /* Default no. of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audadpcm_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audadpcm_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_block_size; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audadpcm_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audadpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +static void adpcm_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + if (audio->dec_state == MSM_AUD_DECODER_STATE_SUCCESS && + audio->enabled == 1) + audpp_route_stream(audio->dec_id, + msm_snddev_route_dec(audio->dec_id)); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("audio_update_pcm_buf_entry: \ + in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("audio_update_pcm_buf_entry: \ + expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +static struct msm_adsp_ops audplay_adsp_ops_adpcm = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_ADPCM; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_adpcm cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + cmd.stereo_cfg = audio->out_channel_mode; + cmd.block_size = audio->out_block_size; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + + MM_DBG("buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDADPCM_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (audio->wflush) { + audio->out_needed = 1; + goto done; + } + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + MM_DBG("\n"); /* Macro prints the file name and function */ + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audadpcm_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audadpcm_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audadpcm_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audadpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audadpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audadpcm_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audadpcm_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audadpcm_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audadpcm_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audadpcm_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audadpcm_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_block_size = config.bits; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("read buf map fail\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("audio_read: no partial frame done reading\n"); + break; + } else { + MM_DBG("audio_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audadpcm_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audplay_send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDADPCM_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("audio_write: mf offset_val %x\n", + mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDADPCM_EOS_FLG_OFFSET] & + AUDADPCM_EOS_FLG_MASK) { + MM_DBG("audio_write: EOS SET\n"); + eos_condition = AUDADPCM_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDADPCM_EOS_FLG_OFFSET] + &= ~AUDADPCM_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("audio_write: continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + if (eos_condition == AUDADPCM_EOS_SET) + rc = audadpcm_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audadpcm_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audadpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audadpcm_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audadpcm_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audadpcm_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audadpcm_suspend(struct early_suspend *h) +{ + struct audadpcm_suspend_ctl *ctl = + container_of(h, struct audadpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audadpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audadpcm_resume(struct early_suspend *h) +{ + struct audadpcm_suspend_ctl *ctl = + container_of(h, struct audadpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audadpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audadpcm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audadpcm_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audadpcm_debug_fops = { + .read = audadpcm_debug_read, + .open = audadpcm_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + unsigned pmem_sz = DMASZ_MAX; + struct audadpcm_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_adpcm_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_ADPCM; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, + SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap(audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_adpcm, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audio->vol_pan.volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + adpcm_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_adpcm_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audadpcm_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audadpcm_resume; + audio->suspend_ctl.node.suspend = audadpcm_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDADPCM_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audadpcm_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_adpcm_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +struct miscdevice audio_adpcm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_adpcm", + .fops = &audio_adpcm_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_adpcm_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c b/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c new file mode 100644 index 00000000000..55c49b34ecc --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c @@ -0,0 +1,1645 @@ +/* + * amrnb audio decoder device + * + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ 1024 /* Hold minimum 700ms voice data and 14 bytes of meta in*/ +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AMRNB 10 + +#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and 24 bytes of meta out*/ +#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDAMRNB_METAFIELD_MASK 0xFFFF0000 +#define AUDAMRNB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDAMRNB_EOS_FLG_MASK 0x01 +#define AUDAMRNB_EOS_NONE 0x0 /* No EOS detected */ +#define AUDAMRNB_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDAMRNB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audamrnb_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audamrnb_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; + uint8_t buf_refresh:1; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audamrnb_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +struct audpp_cmd_cfg_adec_params_amrnb { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)) ; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audamrnb_send_data(struct audio *audio, unsigned needed); +static void audamrnb_config_hostpcm(struct audio *audio); +static void audamrnb_buffer_refresh(struct audio *audio); +static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrnb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +/* must be called with audio->lock held */ +static int audamrnb_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +static void amrnb_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audamrnb_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audamrnb_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audamrnb_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audamrnb_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audamrnb_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder\n"); + } +} + +static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audamrnb_config_hostpcm(audio); + audamrnb_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audamrnb_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_amrnb = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_amrnb cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDAMRNB_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audamrnb_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ) + + (audio->mfield ? 24 : 0); + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audamrnb_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audamrnb_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audamrnb_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audamrnb_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audamrnb_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audamrnb_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audamrnb_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audamrnb_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audamrnb_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audamrnb_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrnb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audamrnb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audamrnb_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audamrnb_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audamrnb_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audamrnb_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrnb_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audamrnb_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audamrnb_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audamrnb_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audamrnb_disable(audio); + audio->stopped = 1; + audamrnb_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audamrnb_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + audio->mfield = config.meta_field; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = 8000; + config.channel_count = 1; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read phys address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr 0x%08x kernel \ + addr 0x%08x\n", audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audamrnb_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audamrnb_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audamrnb_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audamrnb_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audamrnb_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDAMRNB_EOS_NONE; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + + MM_DBG("buffer available\n"); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer + * contains just meta field + */ + if (cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] & + AUDAMRNB_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDAMRNB_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] &= + ~AUDAMRNB_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = (xfer + mfield_size); + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + audamrnb_send_data(audio, 0); + + } + if (eos_condition == AUDAMRNB_EOS_SET) + rc = audamrnb_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audamrnb_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audamrnb_disable(audio); + audamrnb_flush(audio); + audamrnb_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audamrnb_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrnb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audamrnb_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audamrnb_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audamrnb_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audamrnb_suspend(struct early_suspend *h) +{ + struct audamrnb_suspend_ctl *ctl = + container_of(h, struct audamrnb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrnb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audamrnb_resume(struct early_suspend *h) +{ + struct audamrnb_suspend_ctl *ctl = + container_of(h, struct audamrnb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrnb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audamrnb_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audamrnb_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].used %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audamrnb_debug_fops = { + .read = audamrnb_debug_read, + .open = audamrnb_debug_open, +}; +#endif + +static int audamrnb_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audamrnb_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrnb_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_AMRNB; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + free_contiguous_memory_by_paddr(audio->phys); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_amrnb, audio); + if (rc) { + MM_ERR("failed to get %s module freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + + audamrnb_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + amrnb_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrnb_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audamrnb_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audamrnb_resume; + audio->suspend_ctl.node.suspend = audamrnb_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDAMRNB_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audamrnb_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audamrnb_open, + .release = audamrnb_release, + .read = audamrnb_read, + .write = audamrnb_write, + .unlocked_ioctl = audamrnb_ioctl, + .fsync = audamrnb_fsync, +}; + +struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +static int __init audamrnb_init(void) +{ + return misc_register(&audio_amrnb_misc); +} + +static void __exit audamrnb_exit(void) +{ + misc_deregister(&audio_amrnb_misc); +} + +module_init(audamrnb_init); +module_exit(audamrnb_exit); + +MODULE_DESCRIPTION("MSM AMR-NB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c new file mode 100644 index 00000000000..790c510163b --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c @@ -0,0 +1,903 @@ +/* + * amrnb audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_SIZE (22 * 2) /* 36 bytes data */ +#define DMASZ (FRAME_SIZE * FRAME_NUM) + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + + struct msm_adsp_module *audrec; + struct audrec_session_info session_info; /*audrec session info*/ + + /* configuration to use on next enable */ + uint32_t buffer_size; /* Frame size (36 bytes) */ + uint32_t enc_type; + + int dtx_mode; + uint32_t frame_format; + uint32_t used_mode; + uint32_t rec_mode; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + uint32_t mode; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; + + uint16_t source; /* Encoding source bit mask */ + uint32_t device_events; + uint32_t in_call; + uint32_t dev_cnt; + int voice_state; + spinlock_t dev_lock; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_read; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + char *build_id; +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +/* Audrec Queue command sent macro's */ +#define audrec_send_bitstreamqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) + +#define audrec_send_audrecqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +struct audio_in the_audio_amrnb_in; + +/* DSP command send functions */ +static int audamrnb_in_enc_config(struct audio_in *audio, int enable); +static int audamrnb_in_param_config(struct audio_in *audio); +static int audamrnb_in_mem_config(struct audio_in *audio); +static int audamrnb_in_record_config(struct audio_in *audio, int enable); +static int audamrnb_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); + +static void audamrnb_in_get_dsp_frames(struct audio_in *audio); + +static void audamrnb_in_flush(struct audio_in *audio); + +static void amrnb_in_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio_in *audio = (struct audio_in *) private_data; + unsigned long flags; + + MM_DBG("evt_id = 0x%8x\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: { + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt++; + if (!audio->in_call) + audio->source |= (0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((audio->running == 1) && (audio->enabled == 1)) + audamrnb_in_record_config(audio, 1); + + break; + } + case AUDDEV_EVT_DEV_RLS: { + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt--; + if (!audio->in_call) + audio->source &= ~(0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((!audio->running) || (!audio->enabled)) + break; + + /* Turn of as per source */ + if (audio->source) + audamrnb_in_record_config(audio, 1); + else + /* Turn off all */ + audamrnb_in_record_config(audio, 0); + + break; + } + case AUDDEV_EVT_VOICE_STATE_CHG: { + MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n", + evt_payload->voice_state); + audio->voice_state = evt_payload->voice_state; + if (audio->in_call && audio->running) { + if (audio->voice_state == VOICE_STATE_INCALL) + audamrnb_in_record_config(audio, 1); + else if (audio->voice_state == VOICE_STATE_OFFCALL) { + audamrnb_in_record_config(audio, 0); + wake_up(&audio->wait); + } + } + + break; + } + default: + MM_ERR("wrong event %d\n", evt_id); + break; + } +} + +/* ------------------- dsp preproc event handler--------------------- */ +static void audpreproc_dsp_event(void *data, unsigned id, void *msg) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg *err_msg = msg; + + MM_ERR("ERROR_MSG: stream id %d err idx %d\n", + err_msg->stream_id, err_msg->aud_preproc_err_idx); + /* Error case */ + wake_up(&audio->wait_enable); + break; + } + case AUDPREPROC_CMD_CFG_DONE_MSG: { + MM_DBG("CMD_CFG_DONE_MSG \n"); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg; + + MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + 0x%8x\n", enc_cfg_msg->stream_id, + enc_cfg_msg->rec_enc_type); + /* Encoder enable success */ + if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) + audamrnb_in_param_config(audio); + else { /* Encoder disable success */ + audio->running = 0; + audamrnb_in_record_config(audio, 0); + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n"); + audamrnb_in_mem_config(audio); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n"); + wake_up(&audio->wait_enable); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +/* ------------------- dsp audrec event handler--------------------- */ +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDREC_CMD_MEM_CFG_DONE_MSG: { + MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n"); + audio->running = 1; + if ((!audio->in_call && (audio->dev_cnt > 0)) || + (audio->in_call && + (audio->voice_state == VOICE_STATE_INCALL))) + audamrnb_in_record_config(audio, 1); + break; + } + case AUDREC_FATAL_ERR_MSG: { + struct audrec_fatal_err_msg fatal_err_msg; + + getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN); + MM_ERR("FATAL_ERR_MSG: err id %d\n", + fatal_err_msg.audrec_err_id); + /* Error stop the encoder */ + audio->stopped = 1; + wake_up(&audio->wait); + break; + } + case AUDREC_UP_PACKET_READY_MSG: { + struct audrec_up_pkt_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packet_write_cnt_lsw, \ + pkt_ready_msg.audrec_packet_write_cnt_msw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_msw); + + audamrnb_in_get_dsp_frames(audio); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event:module audrectask\n"); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +static void audamrnb_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + else + audio->in_count++; + + audamrnb_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} +struct msm_adsp_ops audrec_amrnb_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audamrnb_in_enc_config(struct audio_in *audio, int enable) +{ + struct audpreproc_audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + if (audio->build_id[17] == '1') { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG_2 command"); + } else { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG command"); + } + cmd.stream_id = audio->enc_id; + + if (enable) + cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE; + else + cmd.audrec_enc_type &= ~(ENCODE_ENABLE); + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audamrnb_in_param_config(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_parm_cfg_amrnb cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + + cmd.dtx_mode = audio->dtx_mode; + cmd.test_mode = -1; /* Default set to -1 */ + cmd.used_mode = audio->used_mode; + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +/* To Do: msm_snddev_route_enc(audio->enc_id); */ +static int audamrnb_in_record_config(struct audio_in *audio, int enable) +{ + struct audpreproc_afe_cmd_audio_record_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG; + cmd.stream_id = audio->enc_id; + if (enable) + cmd.destination_activity = AUDIO_RECORDING_TURN_ON; + else + cmd.destination_activity = AUDIO_RECORDING_TURN_OFF; + + cmd.source_mix_mask = audio->source; + if (audio->enc_id == 2) { + if ((cmd.source_mix_mask & + INTERNAL_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) { + cmd.pipe_id = SOURCE_PIPE_1; + } + if (cmd.source_mix_mask & + AUDPP_A2DP_PIPE_SOURCE_MIX_MASK) + cmd.pipe_id |= SOURCE_PIPE_0; + } + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audamrnb_in_mem_config(struct audio_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD; + cmd.audrec_up_pkt_intm_count = 1; + cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16; + cmd.audrec_ext_pkt_start_addr_lsw = audio->phys; + cmd.audrec_ext_pkt_buf_number = FRAME_NUM; + + /* prepare buffer pointers: + * 36 bytes amrnb packet + 4 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + 4; + data += (FRAME_SIZE/2); /* word increment */ + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8)); + } + + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +static int audamrnb_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + struct up_audrec_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR; + cmd.audrec_up_curr_read_count_msw = read_cnt >> 16; + cmd.audrec_up_curr_read_count_lsw = read_cnt; + + return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audamrnb_in_enable(struct audio_in *audio) +{ + if (audio->enabled) + return 0; + + if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) { + MM_ERR("msm_adsp_enable(audpreproc) failed\n"); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + audpreproc_disable(audio->enc_id, audio); + return -ENODEV; + } + audio->enabled = 1; + audamrnb_in_enc_config(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audamrnb_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + audamrnb_in_enc_config(audio, 0); + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + audpreproc_disable(audio->enc_id, audio); + } + return 0; +} + +static void audamrnb_in_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +/* ------------------- device --------------------- */ +static long audamrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t freq; + freq = 48000; + MM_DBG("AUDIO_START\n"); + if (audio->in_call && (audio->voice_state != + VOICE_STATE_INCALL)) { + rc = -EPERM; + break; + } + rc = msm_snddev_request_freq(&freq, audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("sample rate configured %d\n", freq); + if (rc < 0) { + MM_DBG(" Sample rate can not be set, return code %d\n", + rc); + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + break; + } + /*update aurec session info in audpreproc layer*/ + audio->session_info.session_id = audio->enc_id; + /*amrnb works only on 8KHz*/ + audio->session_info.sampling_freq = 8000; + audpreproc_update_audrec_info(&audio->session_info); + rc = audamrnb_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + rc = audamrnb_in_disable(audio); + rc = msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + audio->stopped = 1; + break; + } + case AUDIO_FLUSH: { + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audamrnb_in_flush(audio); + mutex_unlock(&audio->read_lock); + } + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (cfg.buffer_size != (FRAME_SIZE - 8)) + rc = -EINVAL; + else + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.dtx_enable = ((audio->dtx_mode == -1) ? 1 : 0); + cfg.band_mode = audio->used_mode; + cfg.frame_format = audio->frame_format; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* DSP does not support any other than default format */ + if (audio->frame_format != cfg.frame_format) { + rc = -EINVAL; + break; + } + if (cfg.dtx_enable == 0) + audio->dtx_mode = 0; + else if (cfg.dtx_enable == 1) + audio->dtx_mode = -1; + else { + rc = -EINVAL; + break; + } + audio->used_mode = cfg.band_mode; + break; + } + case AUDIO_SET_INCALL: { + struct msm_voicerec_mode cfg; + unsigned long flags; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.rec_mode != VOC_REC_BOTH && + cfg.rec_mode != VOC_REC_UPLINK && + cfg.rec_mode != VOC_REC_DOWNLINK) { + MM_ERR("invalid rec_mode\n"); + rc = -EINVAL; + break; + } else { + spin_lock_irqsave(&audio->dev_lock, flags); + if (cfg.rec_mode == VOC_REC_UPLINK) + audio->source = VOICE_UL_SOURCE_MIX_MASK; + else if (cfg.rec_mode == VOC_REC_DOWNLINK) + audio->source = VOICE_DL_SOURCE_MIX_MASK; + else + audio->source = VOICE_DL_SOURCE_MIX_MASK | + VOICE_UL_SOURCE_MIX_MASK ; + audio->in_call = 1; + spin_unlock_irqrestore(&audio->dev_lock, flags); + } + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->enc_id, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audamrnb_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped + || (audio->in_call && audio->running && + (audio->voice_state == VOICE_STATE_OFFCALL))); + if (rc < 0) + break; + + if (!audio->in_count) { + if (audio->stopped) { + rc = 0;/* End of File */ + break; + } else if (audio->in_call && audio->running && + (audio->voice_state == VOICE_STATE_OFFCALL)) { + MM_DBG("Not Permitted Voice Terminated\n"); + rc = -EPERM; /* Voice Call stopped */ + break; + } + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + if (count >= size) { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + } else { + MM_ERR("short read\n"); + break; + } + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static ssize_t audamrnb_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int audamrnb_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + MM_DBG("\n"); + mutex_lock(&audio->lock); + audio->in_call = 0; + /* with draw frequency for session + incase not stopped the driver */ + msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX, + AUDDEV_CLNT_ENC); + auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id); + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + audamrnb_in_disable(audio); + audamrnb_in_flush(audio); + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->opened = 0; + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +static int audamrnb_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_amrnb_in; + int rc; + int encid; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (audio->phys) { + audio->map_v_read = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map DMA buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto done; + } + audio->data = audio->map_v_read; + } else { + MM_ERR("could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + rc = -EACCES; + MM_ERR("Non tunnel encoding is not supported\n"); + goto done; + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + rc = -EACCES; + goto done; + } + + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->buffer_size = (FRAME_SIZE - 8); + audio->enc_type = ENC_TYPE_AMRNB | audio->mode; + audio->dtx_mode = -1; + audio->frame_format = 0; + audio->used_mode = 7; /* Bit Rate 12.2 kbps MR122 */ + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_amrnb_adsp_ops, audio); + + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->stopped = 0; + audio->source = 0; + + audamrnb_in_flush(audio); + + audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_VOICE_STATE_CHG; + + audio->voice_state = msm_get_voice_state(); + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_ENC, audio->enc_id, + amrnb_in_listener, (void *) audio); + if (rc) { + MM_ERR("failed to register device event listener\n"); + goto evt_error; + } + audio->build_id = socinfo_get_build_id(); + MM_DBG("Modem build id = %s\n", audio->build_id); + + file->private_data = audio; + audio->opened = 1; +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = audamrnb_in_open, + .release = audamrnb_in_release, + .read = audamrnb_in_read, + .write = audamrnb_in_write, + .unlocked_ioctl = audamrnb_in_ioctl, +}; + +struct miscdevice audio_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_in_fops, +}; + +static int __init audamrnb_in_init(void) +{ + mutex_init(&the_audio_amrnb_in.lock); + mutex_init(&the_audio_amrnb_in.read_lock); + spin_lock_init(&the_audio_amrnb_in.dsp_lock); + spin_lock_init(&the_audio_amrnb_in.dev_lock); + init_waitqueue_head(&the_audio_amrnb_in.wait); + init_waitqueue_head(&the_audio_amrnb_in.wait_enable); + return misc_register(&audio_amrnb_in_misc); +} + +device_initcall(audamrnb_in_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c b/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c new file mode 100644 index 00000000000..a653d5bffb3 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c @@ -0,0 +1,1727 @@ +/* amrwb audio decoder device + * + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ 4110 /* Hold minimum 700ms voice data and 14 bytes of meta in*/ +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AMRWB 11 + +#define PCM_BUFSZ_MIN 8216 /* 100ms worth of data and 24 bytes of meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDAMRWB_METAFIELD_MASK 0xFFFF0000 +#define AUDAMRWB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDAMRWB_EOS_FLG_MASK 0x01 +#define AUDAMRWB_EOS_NONE 0x0 /* No EOS detected */ +#define AUDAMRWB_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDAMRWB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audamrwb_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audamrwb_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audamrwb_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audamrwb_send_data(struct audio *audio, unsigned needed); +static void audamrwb_config_hostpcm(struct audio *audio); +static void audamrwb_buffer_refresh(struct audio *audio); +static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrwb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +/* must be called with audio->lock held */ +static int audamrwb_enable(struct audio *audio) +{ + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audamrwb_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +static void amrwb_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR("ERROR:wrong event\n"); + break; + } +} + +/* must be called with audio->lock held */ +static int audamrwb_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audamrwb_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("audamrwb_update_pcm_buf_entry: \ + in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audamrwb_buffer_refresh(audio); + } else { + MM_DBG("audamrwb_update_pcm_buf_entry: \ + read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("audplay_dsp_event: msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audamrwb_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audamrwb_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event:module audplaytask\n"); + break; + + default: + MM_DBG("unexpected message from decoder\n"); + } +} + +static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason=0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play\n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audamrwb_config_hostpcm(audio); + audamrwb_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_DBG("unknown decoder status\n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audamrwb_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_DBG("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_amrwb = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRWB; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_amrwb cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.stereo_cfg = audio->out_channel_mode; + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDAMRWB_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audamrwb_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audamrwb_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audamrwb_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audamrwb_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audamrwb_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audamrwb_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audamrwb_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audamrwb_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audamrwb_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audamrwb_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audamrwb_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrwb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audamrwb_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audamrwb_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audamrwb_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audamrwb_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audamrwb_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audamrwb_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audamrwb_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audamrwb_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audamrwb_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audamrwb_disable(audio); + audio->stopped = 1; + audamrwb_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audamrwb_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) + config.channel_count = + AUDPP_CMD_PCM_INTF_MONO_V; + else if (config.channel_count == 2) + config.channel_count = + AUDPP_CMD_PCM_INTF_STEREO_V; + else + rc = -EINVAL; + audio->out_channel_mode = config.channel_count; + audio->out_sample_rate = config.sample_rate; + audio->mfield = config.meta_field; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == + AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", config.buffer_count * + config.buffer_size); + audio->read_phys = allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("Error could not map read" + " phys address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = audio->map_v_read; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr 0x%08x \ + kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audamrwb_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audamrwb_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audamrwb_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("count %d\n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x\n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audamrwb_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audamrwb_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + char *buf_ptr; + int rc = 0; + + MM_DBG("signal input EOS reserved=%d\n", audio->reserved); + if (audio->reserved) { + MM_DBG("Pass reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + audio->reserved = 0; + frame->used = 2; + audamrwb_send_data(audio, 0); + } + + MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n", + audio->out[0].used, audio->out[1].used, audio->out_needed); + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audamrwb_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audamrwb_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDAMRWB_EOS_NONE; + unsigned short mfield_size = 0; + unsigned dsize; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + + MM_DBG("buffer available\n"); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer + * contains just meta field + */ + if (cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] & + AUDAMRWB_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDAMRWB_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] &= + ~AUDAMRWB_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + ((frame->size - mfield_size) - 1) : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audamrwb_send_data(audio, 0); + } + } + MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition, + (int) buf, (int) start); + if (eos_condition == AUDAMRWB_EOS_SET) + rc = audamrwb_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audamrwb_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audamrwb_disable(audio); + audamrwb_flush(audio); + audamrwb_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audamrwb_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audamrwb_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audamrwb_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audamrwb_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audamrwb_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audamrwb_suspend(struct early_suspend *h) +{ + struct audamrwb_suspend_ctl *ctl = + container_of(h, struct audamrwb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrwb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audamrwb_resume(struct early_suspend *h) +{ + struct audamrwb_suspend_ctl *ctl = + container_of(h, struct audamrwb_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audamrwb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audamrwb_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audamrwb_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].used %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audamrwb_debug_fops = { + .read = audamrwb_debug_read, + .open = audamrwb_debug_open, +}; +#endif + +static int audamrwb_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audamrwb_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrwb_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_AMRWB; + if (file->f_mode & FMODE_READ) + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + else + dec_attrb |= MSM_AUD_MODE_TUNNEL; + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_amrwb, audio); + if (rc) { + MM_ERR("failed to get %s module freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + audio->vol_pan.pan = 0x0; + audio->eq_enable = 0; + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audamrwb_flush(audio); + + file->private_data = audio; + audio->opened = 1; + audio->event_abort = 0; + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + amrwb_listner, + (void *)audio); + if (rc) { + MM_ERR("failed to register listner\n"); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrwb_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audamrwb_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audamrwb_resume; + audio->suspend_ctl.node.suspend = audamrwb_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDAMRWB_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audamrwb_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwb_fops = { + .owner = THIS_MODULE, + .open = audamrwb_open, + .release = audamrwb_release, + .read = audamrwb_read, + .write = audamrwb_write, + .unlocked_ioctl = audamrwb_ioctl, + .fsync = audamrwb_fsync, +}; + +struct miscdevice audio_amrwb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb", + .fops = &audio_amrwb_fops, +}; + +static int __init audamrwb_init(void) +{ + return misc_register(&audio_amrwb_misc); +} + +static void __exit audamrwb_exit(void) +{ + misc_deregister(&audio_amrwb_misc); +} + +module_init(audamrwb_init); +module_exit(audamrwb_exit); + +MODULE_DESCRIPTION("MSM AMR-WB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c b/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c new file mode 100644 index 00000000000..b6d6e5e4346 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c @@ -0,0 +1,1328 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + + +static DEFINE_MUTEX(session_lock); + +struct audio_dev_ctrl_state { + struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV]; + u32 num_dev; + atomic_t opened; + struct msm_snddev_info *voice_rx_dev; + struct msm_snddev_info *voice_tx_dev; + wait_queue_head_t wait; +}; + +static struct audio_dev_ctrl_state audio_dev_ctrl; +struct event_listner event; +#define MAX_DEC_SESSIONS 7 +#define MAX_ENC_SESSIONS 3 + +struct session_freq { + int freq; + int evt; +}; + + +struct audio_routing_info { + unsigned short mixer_mask[MAX_DEC_SESSIONS]; + unsigned short audrec_mixer_mask[MAX_ENC_SESSIONS]; + struct session_freq dec_freq[MAX_DEC_SESSIONS]; + struct session_freq enc_freq[MAX_ENC_SESSIONS]; + int dual_mic_setting[MAX_ENC_SESSIONS]; + int voice_tx_dev_id; + int voice_rx_dev_id; + int voice_tx_sample_rate; + int voice_rx_sample_rate; + signed int voice_tx_vol; + signed int voice_rx_vol; + int tx_mute; + int rx_mute; + int voice_state; +}; + +static struct audio_routing_info routing_info; + +#ifdef CONFIG_DEBUG_FS + +static struct dentry *dentry; +static int rtc_getdevice_dbg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_INFO("debug intf %s\n", (char *) file->private_data); + return 0; +} +bool is_dev_opened(u32 adb_id) +{ + + int dev_id = 0; + struct msm_snddev_info *dev_info = NULL; + + for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + MM_ERR("pass invalid dev_id %d\n", dev_id); + return false; + } + if (dev_info->opened && (dev_info->acdb_id == adb_id)) + return true; + } + + return false; +} +static ssize_t rtc_getdevice_dbg_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + static char buffer[1024]; + static char swap_buf[1024]; + const int debug_bufmax = sizeof(buffer); + int n = 0; + int swap_count = 0; + int rc = 0; + int dev_count = 0; + int dev_id = 0; + struct msm_snddev_info *dev_info = NULL; + + + if (audio_dev_ctrl.num_dev <= 0) { + MM_ERR("Invalid no Device present\n"); + dev_count = 0; + n = scnprintf(buffer, debug_bufmax, "DEV_NO:0x%x\n", dev_count); + } else { + for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + MM_ERR("pass invalid dev_id %d\n", dev_id); + rc = PTR_ERR(dev_info); + return rc; + } + if (dev_info->opened) { + n += scnprintf(swap_buf + n, debug_bufmax - n, + "ACDB_ID:0x%x;CAPB:0x%x\n", + dev_info->acdb_id, + dev_info->capability); + dev_count++; + MM_DBG("RTC Get Device %x COPP %x Session Mask \ + %x Capb %x Dev Count %x\n", + dev_id , dev_info->copp_id, dev_info->sessions, + dev_info->capability, dev_count); + + } + } + + swap_count = scnprintf(buffer, debug_bufmax, \ + "DEV_NO:0x%x\n", dev_count); + + memcpy(buffer+swap_count, swap_buf, n*sizeof(char)); + n = n+swap_count; + + buffer[n] = 0; + } + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations rtc_acdb_debug_fops = { + .open = rtc_getdevice_dbg_open, + .read = rtc_getdevice_dbg_read +}; +#endif +int msm_reset_all_device(void) +{ + int rc = 0; + int dev_id = 0; + struct msm_snddev_info *dev_info = NULL; + + for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + MM_ERR("pass invalid dev_id %d\n", dev_id); + rc = PTR_ERR(dev_info); + return rc; + } + if (!dev_info->opened) + continue; + MM_DBG("Resetting device %d active on COPP %d" + "with 0x%08x as routing\n", + dev_id, dev_info->copp_id, dev_info->sessions); + broadcast_event(AUDDEV_EVT_REL_PENDING, + dev_id, + SESSION_IGNORE); + rc = dev_info->dev_ops.close(dev_info); + if (rc < 0) { + MM_ERR("Snd device %d failed close!\n", dev_id); + return rc; + } else { + dev_info->opened = 0; + broadcast_event(AUDDEV_EVT_DEV_RLS, + dev_id, + SESSION_IGNORE); + } + dev_info->sessions = 0; + } + return 0; +} +EXPORT_SYMBOL(msm_reset_all_device); + +int msm_set_dual_mic_config(int enc_session_id, int config) +{ + int i; + if (enc_session_id >= MAX_ENC_SESSIONS) + return -EINVAL; + /*config is set(1) dual mic recording is selected */ + /*config is reset (0) dual mic recording is not selected*/ + routing_info.dual_mic_setting[enc_session_id] = config; + for (i = 0; i < MAX_ENC_SESSIONS; i++) + MM_DBG("dual_mic_setting[%d] = %d\n", + i, routing_info.dual_mic_setting[i]); + return 0; +} +EXPORT_SYMBOL(msm_set_dual_mic_config); + +int msm_get_dual_mic_config(int enc_session_id) +{ + if (enc_session_id >= MAX_ENC_SESSIONS) + return -EINVAL; + return routing_info.dual_mic_setting[enc_session_id]; +} +EXPORT_SYMBOL(msm_get_dual_mic_config); + +int msm_get_voice_state(void) +{ + MM_DBG("voice state %d\n", routing_info.voice_state); + return routing_info.voice_state; +} +EXPORT_SYMBOL(msm_get_voice_state); + +int msm_set_voice_mute(int dir, int mute) +{ + MM_DBG("dir %x mute %x\n", dir, mute); + if (!audio_dev_ctrl.voice_rx_dev + || !audio_dev_ctrl.voice_tx_dev) + return -EPERM; + if (dir == DIR_TX) { + routing_info.tx_mute = mute; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_tx_dev_id, SESSION_IGNORE); + } else + return -EPERM; + return 0; +} +EXPORT_SYMBOL(msm_set_voice_mute); + +int msm_set_voice_vol(int dir, s32 volume) +{ + if (!audio_dev_ctrl.voice_rx_dev + || !audio_dev_ctrl.voice_tx_dev) + return -EPERM; + if (dir == DIR_TX) { + routing_info.voice_tx_vol = volume; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_tx_dev_id, + SESSION_IGNORE); + } else if (dir == DIR_RX) { + routing_info.voice_rx_vol = volume; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_rx_dev_id, + SESSION_IGNORE); + } else + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(msm_set_voice_vol); + +void msm_snddev_register(struct msm_snddev_info *dev_info) +{ + mutex_lock(&session_lock); + if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) { + audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info; + dev_info->dev_volume = 50; /* 50% */ + dev_info->sessions = 0x0; + dev_info->usage_count = 0; + dev_info->set_sample_rate = 0; + audio_dev_ctrl.num_dev++; + } else + MM_ERR("%s: device registry max out\n", __func__); + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(msm_snddev_register); + +int msm_snddev_devcount(void) +{ + return audio_dev_ctrl.num_dev; +} +EXPORT_SYMBOL(msm_snddev_devcount); + +int msm_snddev_query(int dev_id) +{ + if (dev_id <= audio_dev_ctrl.num_dev) + return 0; + return -ENODEV; +} +EXPORT_SYMBOL(msm_snddev_query); + +int msm_snddev_is_set(int popp_id, int copp_id) +{ + return routing_info.mixer_mask[popp_id] & (0x1 << copp_id); +} +EXPORT_SYMBOL(msm_snddev_is_set); + +unsigned short msm_snddev_route_enc(int enc_id) +{ + if (enc_id >= MAX_ENC_SESSIONS) + return -EINVAL; + return routing_info.audrec_mixer_mask[enc_id]; +} +EXPORT_SYMBOL(msm_snddev_route_enc); + +unsigned short msm_snddev_route_dec(int popp_id) +{ + if (popp_id >= MAX_DEC_SESSIONS) + return -EINVAL; + return routing_info.mixer_mask[popp_id]; +} +EXPORT_SYMBOL(msm_snddev_route_dec); + +int msm_snddev_set_dec(int popp_id, int copp_id, int set) +{ + if (set) + routing_info.mixer_mask[popp_id] |= (0x1 << copp_id); + else + routing_info.mixer_mask[popp_id] &= ~(0x1 << copp_id); + + return 0; +} +EXPORT_SYMBOL(msm_snddev_set_dec); + +int msm_snddev_set_enc(int popp_id, int copp_id, int set) +{ + if (set) + routing_info.audrec_mixer_mask[popp_id] |= (0x1 << copp_id); + else + routing_info.audrec_mixer_mask[popp_id] &= ~(0x1 << copp_id); + return 0; +} +EXPORT_SYMBOL(msm_snddev_set_enc); + +int msm_device_is_voice(int dev_id) +{ + if ((dev_id == routing_info.voice_rx_dev_id) + || (dev_id == routing_info.voice_tx_dev_id)) + return 0; + else + return -EINVAL; +} +EXPORT_SYMBOL(msm_device_is_voice); + +int msm_set_voc_route(struct msm_snddev_info *dev_info, + int stream_type, int dev_id) +{ + int rc = 0; + u32 session_mask = 0; + + mutex_lock(&session_lock); + switch (stream_type) { + case AUDIO_ROUTE_STREAM_VOICE_RX: + if (audio_dev_ctrl.voice_rx_dev) + audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFF; + + if (!(dev_info->capability & SNDDEV_CAP_RX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + audio_dev_ctrl.voice_rx_dev = dev_info; + if (audio_dev_ctrl.voice_rx_dev) { + session_mask = + 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1)); + audio_dev_ctrl.voice_rx_dev->sessions |= + session_mask; + } + routing_info.voice_rx_dev_id = dev_id; + break; + case AUDIO_ROUTE_STREAM_VOICE_TX: + if (audio_dev_ctrl.voice_tx_dev) + audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFF; + + if (!(dev_info->capability & SNDDEV_CAP_TX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + + audio_dev_ctrl.voice_tx_dev = dev_info; + if (audio_dev_ctrl.voice_rx_dev) { + session_mask = + 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1)); + audio_dev_ctrl.voice_tx_dev->sessions |= + session_mask; + } + routing_info.voice_tx_dev_id = dev_id; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&session_lock); + return rc; +} +EXPORT_SYMBOL(msm_set_voc_route); + +void msm_release_voc_thread(void) +{ + wake_up(&audio_dev_ctrl.wait); +} +EXPORT_SYMBOL(msm_release_voc_thread); + +int msm_snddev_get_enc_freq(session_id) +{ + return routing_info.enc_freq[session_id].freq; +} +EXPORT_SYMBOL(msm_snddev_get_enc_freq); + +int msm_get_voc_freq(int *tx_freq, int *rx_freq) +{ + *tx_freq = routing_info.voice_tx_sample_rate; + *rx_freq = routing_info.voice_rx_sample_rate; + return 0; +} +EXPORT_SYMBOL(msm_get_voc_freq); + +int msm_get_voc_route(u32 *rx_id, u32 *tx_id) +{ + int rc = 0; + + if (!rx_id || !tx_id) + return -EINVAL; + + mutex_lock(&session_lock); + if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) { + rc = -ENODEV; + mutex_unlock(&session_lock); + return rc; + } + + *rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id; + *tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id; + + mutex_unlock(&session_lock); + + return rc; +} +EXPORT_SYMBOL(msm_get_voc_route); + +struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id) +{ + struct msm_snddev_info *info; + + if ((audio_dev_ctrl.num_dev - 1) < dev_id) { + info = ERR_PTR(-ENODEV); + goto error; + } + + info = audio_dev_ctrl.devs[dev_id]; +error: + return info; + +} +EXPORT_SYMBOL(audio_dev_ctrl_find_dev); + +int snddev_voice_set_volume(int vol, int path) +{ + if (audio_dev_ctrl.voice_rx_dev + && audio_dev_ctrl.voice_tx_dev) { + if (path) + audio_dev_ctrl.voice_tx_dev->dev_volume = vol; + else + audio_dev_ctrl.voice_rx_dev->dev_volume = vol; + } else + return -ENODEV; + return 0; +} +EXPORT_SYMBOL(snddev_voice_set_volume); + +static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl, + void __user *arg) +{ + int rc = 0; + u32 index; + struct msm_snd_device_list work_list; + struct msm_snd_device_info *work_tbl; + + if (copy_from_user(&work_list, arg, sizeof(work_list))) { + rc = -EFAULT; + goto error; + } + + if (work_list.num_dev > dev_ctrl->num_dev) { + rc = -EINVAL; + goto error; + } + + work_tbl = kmalloc(work_list.num_dev * + sizeof(struct msm_snd_device_info), GFP_KERNEL); + if (!work_tbl) { + rc = -ENOMEM; + goto error; + } + + for (index = 0; index < dev_ctrl->num_dev; index++) { + work_tbl[index].dev_id = index; + work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability; + strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name, + 64); + } + + if (copy_to_user((void *) (work_list.list), work_tbl, + work_list.num_dev * sizeof(struct msm_snd_device_info))) + rc = -EFAULT; + kfree(work_tbl); +error: + return rc; +} + + +int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, + void (*listner)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data), + void *private_data) +{ + int rc; + struct msm_snd_evt_listner *callback = NULL; + struct msm_snd_evt_listner *new_cb; + + new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL); + if (!new_cb) { + MM_ERR("No memory to add new listener node\n"); + return -ENOMEM; + } + + mutex_lock(&session_lock); + new_cb->cb_next = NULL; + new_cb->auddev_evt_listener = listner; + new_cb->evt_id = evt_id; + new_cb->clnt_type = clnt_type; + new_cb->clnt_id = clnt_id; + new_cb->private_data = private_data; + if (event.cb == NULL) { + event.cb = new_cb; + new_cb->cb_prev = NULL; + } else { + callback = event.cb; + for (; ;) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + callback->cb_next = new_cb; + new_cb->cb_prev = callback; + } + event.num_listner++; + mutex_unlock(&session_lock); + rc = 0; + return rc; +} +EXPORT_SYMBOL(auddev_register_evt_listner); + +int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id) +{ + struct msm_snd_evt_listner *callback = event.cb; + struct msm_snddev_info *info; + u32 session_mask = 0; + int i = 0; + + mutex_lock(&session_lock); + while (callback != NULL) { + if ((callback->clnt_type == clnt_type) + && (callback->clnt_id == clnt_id)) + break; + callback = callback->cb_next; + } + if (callback == NULL) { + mutex_unlock(&session_lock); + return -EINVAL; + } + + if ((callback->cb_next == NULL) && (callback->cb_prev == NULL)) + event.cb = NULL; + else if (callback->cb_next == NULL) + callback->cb_prev->cb_next = NULL; + else if (callback->cb_prev == NULL) { + callback->cb_next->cb_prev = NULL; + event.cb = callback->cb_next; + } else { + callback->cb_prev->cb_next = callback->cb_next; + callback->cb_next->cb_prev = callback->cb_prev; + } + kfree(callback); + + session_mask = (0x1 << (clnt_id)) << (8 * ((int)clnt_type-1)); + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + info->sessions &= ~session_mask; + } + if (clnt_type == AUDDEV_CLNT_ENC) + msm_set_dual_mic_config(clnt_id, 0); + mutex_unlock(&session_lock); + return 0; +} +EXPORT_SYMBOL(auddev_unregister_evt_listner); + +int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type) +{ + int i = 0; + struct msm_snddev_info *info; + u32 session_mask = 0; + + if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_DEC) + && (session_id >= MAX_DEC_SESSIONS)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_ENC) + && (session_id >= MAX_ENC_SESSIONS)) + return -EINVAL; + + session_mask = (0x1 << (session_id)) << (8 * ((int)clnt_type-1)); + + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + if ((info->sessions & session_mask) + && (info->capability & capability)) { + if (!(info->sessions & ~(session_mask))) + info->set_sample_rate = 0; + } + } + if (clnt_type == AUDDEV_CLNT_DEC) + routing_info.dec_freq[session_id].freq + = 0; + else if (clnt_type == AUDDEV_CLNT_ENC) + routing_info.enc_freq[session_id].freq + = 0; + else if (capability == SNDDEV_CAP_TX) + routing_info.voice_tx_sample_rate = 0; + else + routing_info.voice_rx_sample_rate = 48000; + return 0; +} + +int msm_snddev_request_freq(int *freq, u32 session_id, + u32 capability, u32 clnt_type) +{ + int i = 0; + int rc = 0; + struct msm_snddev_info *info; + u32 set_freq; + u32 session_mask = 0; + u32 clnt_type_mask = 0; + + MM_DBG(": clnt_type 0x%08x\n", clnt_type); + + if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_DEC) + && (session_id >= MAX_DEC_SESSIONS)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_ENC) + && (session_id >= MAX_ENC_SESSIONS)) + return -EINVAL; + session_mask = ((0x1 << session_id)) << (8 * (clnt_type-1)); + clnt_type_mask = (0xFF << (8 * (clnt_type-1))); + if (!(*freq == 8000) && !(*freq == 11025) && + !(*freq == 12000) && !(*freq == 16000) && + !(*freq == 22050) && !(*freq == 24000) && + !(*freq == 32000) && !(*freq == 44100) && + !(*freq == 48000)) + return -EINVAL; + + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + if ((info->sessions & session_mask) + && (info->capability & capability)) { + rc = 0; + if ((info->sessions & ~clnt_type_mask) + && ((*freq != 8000) && (*freq != 16000) + && (*freq != 48000))) { + if (clnt_type == AUDDEV_CLNT_ENC) { + routing_info.enc_freq[session_id].freq + = 0; + return -EPERM; + } else if (clnt_type == AUDDEV_CLNT_DEC) { + routing_info.dec_freq[session_id].freq + = 0; + return -EPERM; + } + } + if (*freq == info->set_sample_rate) { + rc = info->set_sample_rate; + continue; + } + set_freq = MAX(*freq, info->set_sample_rate); + + + if (clnt_type == AUDDEV_CLNT_DEC) + routing_info.dec_freq[session_id].freq + = set_freq; + else if (clnt_type == AUDDEV_CLNT_ENC) + routing_info.enc_freq[session_id].freq + = set_freq; + else if (capability == SNDDEV_CAP_TX) + routing_info.voice_tx_sample_rate = set_freq; + + rc = set_freq; + *freq = set_freq; + /* There is difference in device sample rate to + * requested sample rate. So update device sample rate + * and propagate sample rate change event to active + * sessions of the device. + */ + if (info->set_sample_rate != set_freq) { + info->set_sample_rate = set_freq; + if (info->opened) { + /* Ignore propagating sample rate + * change event to requested client + * session + */ + if (clnt_type == AUDDEV_CLNT_DEC) + routing_info.\ + dec_freq[session_id].evt = 1; + else if (clnt_type == AUDDEV_CLNT_ENC) + routing_info.\ + enc_freq[session_id].evt = 1; + broadcast_event(AUDDEV_EVT_FREQ_CHG, i, + SESSION_IGNORE); + set_freq = info->dev_ops.set_freq(info, + set_freq); + broadcast_event(AUDDEV_EVT_DEV_RDY, i, + SESSION_IGNORE); + } + } + } + MM_DBG("info->set_sample_rate = %d\n", info->set_sample_rate); + MM_DBG("routing_info.enc_freq.freq = %d\n", + routing_info.enc_freq[session_id].freq); + } + return rc; +} +EXPORT_SYMBOL(msm_snddev_request_freq); + +int msm_snddev_enable_sidetone(u32 dev_id, u32 enable) +{ + int rc; + struct msm_snddev_info *dev_info; + + MM_DBG("dev_id %d enable %d\n", dev_id, enable); + + dev_info = audio_dev_ctrl_find_dev(dev_id); + + if (IS_ERR(dev_info)) { + MM_ERR("bad dev_id %d\n", dev_id); + rc = -EINVAL; + } else if (!dev_info->dev_ops.enable_sidetone) { + MM_DBG("dev %d no sidetone support\n", dev_id); + rc = -EPERM; + } else + rc = dev_info->dev_ops.enable_sidetone(dev_info, enable); + + return rc; +} +EXPORT_SYMBOL(msm_snddev_enable_sidetone); + +static long audio_dev_ctrl_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc = 0; + struct audio_dev_ctrl_state *dev_ctrl = file->private_data; + + mutex_lock(&session_lock); + switch (cmd) { + case AUDIO_GET_NUM_SND_DEVICE: + rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg); + break; + case AUDIO_GET_SND_DEVICES: + rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg); + break; + case AUDIO_ENABLE_SND_DEVICE: { + struct msm_snddev_info *dev_info; + u32 dev_id; + + if (get_user(dev_id, (u32 __user *) arg)) { + rc = -EFAULT; + break; + } + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) + rc = PTR_ERR(dev_info); + else { + rc = dev_info->dev_ops.open(dev_info); + if (!rc) + dev_info->opened = 1; + wake_up(&audio_dev_ctrl.wait); + } + break; + + } + + case AUDIO_DISABLE_SND_DEVICE: { + struct msm_snddev_info *dev_info; + u32 dev_id; + + if (get_user(dev_id, (u32 __user *) arg)) { + rc = -EFAULT; + break; + } + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) + rc = PTR_ERR(dev_info); + else { + rc = dev_info->dev_ops.close(dev_info); + dev_info->opened = 0; + } + break; + } + + case AUDIO_ROUTE_STREAM: { + struct msm_audio_route_config route_cfg; + struct msm_snddev_info *dev_info; + + if (copy_from_user(&route_cfg, (void __user *) arg, + sizeof(struct msm_audio_route_config))) { + rc = -EFAULT; + break; + } + MM_DBG("%s: route cfg %d %d type\n", __func__, + route_cfg.dev_id, route_cfg.stream_type); + dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id); + if (IS_ERR(dev_info)) { + MM_ERR("%s: pass invalid dev_id\n", __func__); + rc = PTR_ERR(dev_info); + break; + } + + switch (route_cfg.stream_type) { + + case AUDIO_ROUTE_STREAM_VOICE_RX: + if (!(dev_info->capability & SNDDEV_CAP_RX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + dev_ctrl->voice_rx_dev = dev_info; + break; + case AUDIO_ROUTE_STREAM_VOICE_TX: + if (!(dev_info->capability & SNDDEV_CAP_TX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + dev_ctrl->voice_tx_dev = dev_info; + break; + } + break; + } + + default: + rc = -EINVAL; + } + mutex_unlock(&session_lock); + return rc; +} + +static int audio_dev_ctrl_open(struct inode *inode, struct file *file) +{ + MM_DBG("open audio_dev_ctrl\n"); + atomic_inc(&audio_dev_ctrl.opened); + file->private_data = &audio_dev_ctrl; + return 0; +} + +static int audio_dev_ctrl_release(struct inode *inode, struct file *file) +{ + MM_DBG("release audio_dev_ctrl\n"); + atomic_dec(&audio_dev_ctrl.opened); + return 0; +} + +static const struct file_operations audio_dev_ctrl_fops = { + .owner = THIS_MODULE, + .open = audio_dev_ctrl_open, + .release = audio_dev_ctrl_release, + .unlocked_ioctl = audio_dev_ctrl_ioctl, +}; + + +struct miscdevice audio_dev_ctrl_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_dev_ctrl", + .fops = &audio_dev_ctrl_fops, +}; + +/* session id is 32 bit routing mask per device + * 0-7 for voice clients + * 8-15 for Decoder clients + * 16-23 for Encoder clients + * 24-31 Do not care + */ +void broadcast_event(u32 evt_id, u32 dev_id, u32 session_id) +{ + int clnt_id = 0, i; + union auddev_evt_data *evt_payload; + struct msm_snd_evt_listner *callback; + struct msm_snddev_info *dev_info = NULL; + u32 session_mask = 0; + static int pending_sent; + + MM_DBG(": evt_id = %d\n", evt_id); + + if ((evt_id != AUDDEV_EVT_START_VOICE) + && (evt_id != AUDDEV_EVT_END_VOICE) + && (evt_id != AUDDEV_EVT_STREAM_VOL_CHG) + && (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + MM_ERR("pass invalid dev_id\n"); + return; + } + } + + if (event.cb != NULL) + callback = event.cb; + else + return; + + evt_payload = kzalloc(sizeof(union auddev_evt_data), + GFP_KERNEL); + if (evt_payload == NULL) { + MM_ERR("Memory allocation for event payload failed\n"); + return; + } + + mutex_lock(&session_lock); + + if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + routing_info.voice_state = dev_id; + + for (; ;) { + if (!(evt_id & callback->evt_id)) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + clnt_id = callback->clnt_id; + memset(evt_payload, 0, sizeof(union auddev_evt_data)); + + if ((evt_id == AUDDEV_EVT_START_VOICE) + || (evt_id == AUDDEV_EVT_END_VOICE)) + goto skip_check; + if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) + goto aud_cal; + + session_mask = (0x1 << (clnt_id)) + << (8 * ((int)callback->clnt_type-1)); + + if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \ + (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) { + MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG or\ + AUDDEV_EVT_VOICE_STATE_CHG\n"); + goto volume_strm; + } + + MM_DBG("dev_info->sessions = %08x\n", dev_info->sessions); + + if ((!session_id && !(dev_info->sessions & session_mask)) || + (session_id && ((dev_info->sessions & session_mask) != + session_id))) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE) + goto voc_events; + +volume_strm: + if (callback->clnt_type == AUDDEV_CLNT_DEC) { + MM_DBG("AUDDEV_CLNT_DEC\n"); + if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) { + MM_DBG("clnt_id = %d, session_id = 0x%8x\n", + clnt_id, session_id); + if (session_mask != session_id) + goto sent_dec; + else + evt_payload->session_vol = + msm_vol_ctl.volume; + } else if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.dec_freq[clnt_id].evt) { + routing_info.dec_freq[clnt_id].evt + = 0; + goto sent_dec; + } else if (routing_info.dec_freq[clnt_id].freq + == dev_info->set_sample_rate) + goto sent_dec; + else { + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } + /* Propogate device information to client */ + } else if (evt_id == AUDDEV_EVT_DEVICE_INFO) { + evt_payload->devinfo.dev_id + = dev_info->copp_id; + evt_payload->devinfo.acdb_id + = dev_info->acdb_id; + evt_payload->devinfo.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->devinfo.sample_rate + = dev_info->sample_rate; + if (session_id == SESSION_IGNORE) + evt_payload->devinfo.sessions + = dev_info->sessions; + else + evt_payload->devinfo.sessions + = session_id; + evt_payload->devinfo.sessions = + (evt_payload->devinfo.sessions >> + ((AUDDEV_CLNT_DEC-1) * 8)); + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else + evt_payload->routing_id = dev_info->copp_id; + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); +sent_dec: + if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) && + (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) + routing_info.dec_freq[clnt_id].freq + = dev_info->set_sample_rate; + + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + if (callback->clnt_type == AUDDEV_CLNT_ENC) { + + MM_DBG("AUDDEV_CLNT_ENC\n"); + if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.enc_freq[clnt_id].evt) { + routing_info.enc_freq[clnt_id].evt + = 0; + goto sent_enc; + } else { + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } + /* Propogate device information to client */ + } else if (evt_id == AUDDEV_EVT_DEVICE_INFO) { + evt_payload->devinfo.dev_id + = dev_info->copp_id; + evt_payload->devinfo.acdb_id + = dev_info->acdb_id; + evt_payload->devinfo.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->devinfo.sample_rate + = dev_info->sample_rate; + if (session_id == SESSION_IGNORE) + evt_payload->devinfo.sessions + = dev_info->sessions; + else + evt_payload->devinfo.sessions + = session_id; + evt_payload->devinfo.sessions = + (evt_payload->devinfo.sessions >> + ((AUDDEV_CLNT_ENC-1) * 8)); + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else + evt_payload->routing_id = dev_info->copp_id; + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); +sent_enc: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } +aud_cal: + if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) { + int temp_sessions; + MM_DBG("AUDDEV_CLNT_AUDIOCAL\n"); + if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else if (!dev_info->sessions) + goto sent_aud_cal; + else { + evt_payload->audcal_info.dev_id = + dev_info->copp_id; + evt_payload->audcal_info.acdb_id = + dev_info->acdb_id; + evt_payload->audcal_info.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->audcal_info.sample_rate = + dev_info->set_sample_rate ? + dev_info->set_sample_rate : + dev_info->sample_rate; + } + if (evt_payload->audcal_info.dev_type == + SNDDEV_CAP_TX) { + if (session_id == SESSION_IGNORE) + temp_sessions = dev_info->sessions; + else + temp_sessions = session_id; + evt_payload->audcal_info.sessions = + (temp_sessions >> + ((AUDDEV_CLNT_ENC-1) * 8)); + } else { + if (session_id == SESSION_IGNORE) + temp_sessions = dev_info->sessions; + else + temp_sessions = session_id; + evt_payload->audcal_info.sessions = + (temp_sessions >> + ((AUDDEV_CLNT_DEC-1) * 8)); + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); + +sent_aud_cal: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } +skip_check: +voc_events: + if (callback->clnt_type == AUDDEV_CLNT_VOC) { + MM_DBG("AUDDEV_CLNT_VOC\n"); + if (evt_id == AUDDEV_EVT_DEV_RLS) { + if (!pending_sent) + goto sent_voc; + else + pending_sent = 0; + } + if (evt_id == AUDDEV_EVT_REL_PENDING) + pending_sent = 1; + + if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) { + if (dev_info->capability & SNDDEV_CAP_TX) { + evt_payload->voc_vm_info.dev_type = + SNDDEV_CAP_TX; + evt_payload->voc_vm_info.acdb_dev_id = + dev_info->acdb_id; + evt_payload-> + voc_vm_info.dev_vm_val.mute = + routing_info.tx_mute; + } else { + evt_payload->voc_vm_info.dev_type = + SNDDEV_CAP_RX; + evt_payload->voc_vm_info.acdb_dev_id = + dev_info->acdb_id; + evt_payload-> + voc_vm_info.dev_vm_val.vol = + routing_info.voice_rx_vol; + } + } else if ((evt_id == AUDDEV_EVT_START_VOICE) + || (evt_id == AUDDEV_EVT_END_VOICE)) + memset(evt_payload, 0, + sizeof(union auddev_evt_data)); + else if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.voice_tx_sample_rate + != dev_info->set_sample_rate) { + routing_info.voice_tx_sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } else + goto sent_voc; + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else { + evt_payload->voc_devinfo.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->voc_devinfo.acdb_dev_id = + dev_info->acdb_id; + evt_payload->voc_devinfo.dev_sample = + dev_info->set_sample_rate ? + dev_info->set_sample_rate : + dev_info->sample_rate; + evt_payload->voc_devinfo.dev_id = dev_id; + if (dev_info->capability & SNDDEV_CAP_RX) { + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; + i++) { + evt_payload-> + voc_devinfo.max_rx_vol[i] = + dev_info->max_voc_rx_vol[i]; + evt_payload + ->voc_devinfo.min_rx_vol[i] = + dev_info->min_voc_rx_vol[i]; + } + } + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); + if (evt_id == AUDDEV_EVT_DEV_RLS) + dev_info->sessions &= ~(0xFF); +sent_voc: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + } + kfree(evt_payload); + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(broadcast_event); + + +void mixer_post_event(u32 evt_id, u32 id) +{ + + MM_DBG("evt_id = %d\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */ + broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEV_RDY: + broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEV_RLS: + broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_REL_PENDING: + broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id, + SESSION_IGNORE); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id, + SESSION_IGNORE); + break; + case AUDDEV_EVT_START_VOICE: + broadcast_event(AUDDEV_EVT_START_VOICE, + id, SESSION_IGNORE); + break; + case AUDDEV_EVT_END_VOICE: + broadcast_event(AUDDEV_EVT_END_VOICE, + id, SESSION_IGNORE); + break; + case AUDDEV_EVT_FREQ_CHG: + broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE); + break; + default: + break; + } +} +EXPORT_SYMBOL(mixer_post_event); + +static int __init audio_dev_ctrl_init(void) +{ +#ifdef CONFIG_DEBUG_FS + char name[sizeof "rtc_get_device"+1]; +#endif + + init_waitqueue_head(&audio_dev_ctrl.wait); + + event.cb = NULL; + + atomic_set(&audio_dev_ctrl.opened, 0); + audio_dev_ctrl.num_dev = 0; + audio_dev_ctrl.voice_tx_dev = NULL; + audio_dev_ctrl.voice_rx_dev = NULL; + routing_info.voice_state = VOICE_STATE_INVALID; +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "rtc_get_device"); + dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO, + NULL, NULL, &rtc_acdb_debug_fops); + if (IS_ERR(dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif + + return misc_register(&audio_dev_ctrl_misc); +} + +static void __exit audio_dev_ctrl_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + if (dentry) + debugfs_remove(dentry); +#endif + +} +module_init(audio_dev_ctrl_init); +module_exit(audio_dev_ctrl_exit); + +MODULE_DESCRIPTION("MSM 7K Audio Device Control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_evrc.c b/arch/arm/mach-msm/qdsp5v2/audio_evrc.c new file mode 100644 index 00000000000..7306e986d18 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_evrc.c @@ -0,0 +1,1640 @@ +/* + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This code also borrows from audio_aac.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hold 30 packets of 24 bytes each and 14 bytes of meta in */ +#define BUFSZ 734 +#define DMASZ (BUFSZ * 2) + +#define AUDDEC_DEC_EVRC 12 + +#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and + and 24 bytes of meta out */ +#define PCM_BUF_MAX_COUNT 5 +/* DSP only accepts 5 buffers at most + * but support 2 buffers currently + */ +#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */ + +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDEVRC_METAFIELD_MASK 0xFFFF0000 +#define AUDEVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDEVRC_EOS_FLG_MASK 0x01 +#define AUDEVRC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDEVRC_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDEVRC_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audevrc_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audevrc_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; + uint8_t buf_refresh:1; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audevrc_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audevrc_send_data(struct audio *audio, unsigned needed); +static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audevrc_config_hostpcm(struct audio *audio); +static void audevrc_buffer_refresh(struct audio *audio); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audevrc_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +/* must be called with audio->lock held */ +static int audevrc_enable(struct audio *audio) +{ + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +static void evrc_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audevrc_disable(struct audio *audio) +{ + int rc = 0; + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ + +static void audevrc_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr + == payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audevrc_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audevrc_send_data(audio, 1); + break; + case AUDPLAY_MSG_BUFFER_UPDATE: + MM_DBG("\n"); /* Macro prints the file name and function */ + audevrc_update_pcm_buf_entry(audio, msg); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + default: + MM_ERR("unexpected message from decoder \n"); + } +} + +static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audevrc_config_hostpcm(audio); + audevrc_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK\n"); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audevrc_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_evrc = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_evrc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = sizeof(cmd); + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDEVRC_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audevrc_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audevrc_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audevrc_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audevrc_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audevrc_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audevrc_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audevrc_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audevrc_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audevrc_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audevrc_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audevrc_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audevrc_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audevrc_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + + +static long audevrc_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audevrc_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audevrc_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audevrc_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audevrc_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audevrc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audevrc_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audevrc_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audevrc_disable(audio); + audio->stopped = 1; + audevrc_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audevrc_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + audio->mfield = config.meta_field; + rc = 0; + MM_DBG("AUDIO_SET_CONFIG applicable only \ + for meta field configuration\n"); + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = 8000; + config.channel_count = 1; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read" + " phy address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audevrc_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + if (!audio->pcm_feedback) { + return 0; + /* PCM feedback is not enabled. Nothing to read */ + } + mutex_lock(&audio->read_lock); + MM_DBG("\n"); /* Macro prints the file name and function */ + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + MM_DBG("wait terminated \n"); + if (rc < 0) + break; + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment + */ + + } + } + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audevrc_buffer_refresh(audio); + } + mutex_unlock(&audio->read_lock); + if (buf > start) + rc = buf - start; + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audevrc_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audevrc_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audevrc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + unsigned short mfield_size = 0; + int rc = 0, eos_condition = AUDEVRC_EOS_NONE; + + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, + mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] & + AUDEVRC_EOS_FLG_MASK) { + MM_DBG("eos set\n"); + eos_condition = AUDEVRC_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] &= + ~AUDEVRC_EOS_FLG_MASK; + } + /* Check EOS to see if */ + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer + mfield_size; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + audevrc_send_data(audio, 0); + } + if (eos_condition == AUDEVRC_EOS_SET) + rc = audevrc_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audevrc_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audevrc_disable(audio); + audevrc_flush(audio); + audevrc_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audevrc_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audevrc_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audevrc_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audevrc_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audevrc_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audevrc_suspend(struct early_suspend *h) +{ + struct audevrc_suspend_ctl *ctl = + container_of(h, struct audevrc_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audevrc_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audevrc_resume(struct early_suspend *h) +{ + struct audevrc_suspend_ctl *ctl = + container_of(h, struct audevrc_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audevrc_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audevrc_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audevrc_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audevrc_debug_fops = { + .read = audevrc_debug_read, + .open = audevrc_debug_open, +}; +#endif + +static int audevrc_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audevrc_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_evrc_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_EVRC; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("failed to map write physical address, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_evrc, audio); + + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x3FFF; + + audevrc_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + evrc_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_evrc_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audevrc_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audevrc_resume; + audio->suspend_ctl.node.suspend = audevrc_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDEVRC_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audevrc_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audevrc_open, + .release = audevrc_release, + .read = audevrc_read, + .write = audevrc_write, + .unlocked_ioctl = audevrc_ioctl, + .fsync = audevrc_fsync, +}; + +struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +static int __init audevrc_init(void) +{ + return misc_register(&audio_evrc_misc); + +} + +static void __exit audevrc_exit(void) +{ + misc_deregister(&audio_evrc_misc); +} + +module_init(audevrc_init); +module_exit(audevrc_exit); + +MODULE_DESCRIPTION("MSM EVRC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c new file mode 100644 index 00000000000..1ee502969a1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c @@ -0,0 +1,1507 @@ +/* + * evrc audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define META_OUT_SIZE 24 +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM 8 +#define EVRC_FRAME_SIZE 36 /* 36 bytes data */ +#define FRAME_SIZE (22 * 2) /* 36 bytes data */ + /* 36 bytes data + 24 meta field*/ +#define NT_FRAME_SIZE (EVRC_FRAME_SIZE + META_OUT_SIZE) +#define DMASZ (NT_FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM 2 +#define OUT_BUFFER_SIZE (4 * 1024 + META_OUT_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + +#define AUDPREPROC_EVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer*/ +#define AUDPREPROC_EVRC_EOS_FLG_MASK 0x01 +#define AUDPREPROC_EVRC_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_EVRC_EOS_SET 0x1 /* EOS set in meta field */ + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + int mfield; /* meta field embedded in data */ + int wflush; /*write flush */ + int rflush; /*read flush*/ + int out_frame_cnt; + + struct msm_adsp_module *audrec; + + struct audrec_session_info session_info; /*audrec session info*/ + + /* configuration to use on next enable */ + uint32_t buffer_size; /* Frame size (36 bytes) */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t enc_type; + + struct msm_audio_evrc_enc_config cfg; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + uint32_t mode; + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; + + uint16_t source; /* Encoding source bit mask */ + uint32_t device_events; + uint32_t in_call; + uint32_t dev_cnt; + int voice_state; + spinlock_t dev_lock; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_read; + void *map_v_write; + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + char *build_id; +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +struct evrc_encoded_meta_out { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audrec_send_bitstreamqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) + +#define audrec_send_audrecqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +/* DSP command send functions */ +static int audevrc_in_enc_config(struct audio_in *audio, int enable); +static int audevrc_in_param_config(struct audio_in *audio); +static int audevrc_in_mem_config(struct audio_in *audio); +static int audevrc_in_record_config(struct audio_in *audio, int enable); +static int audevrc_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); + +static void audevrc_in_get_dsp_frames(struct audio_in *audio); +static int audpcm_config(struct audio_in *audio); +static void audevrc_out_flush(struct audio_in *audio); +static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio); +static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed); +static void audevrc_nt_in_get_dsp_frames(struct audio_in *audio); + +static void audevrc_in_flush(struct audio_in *audio); + +static void evrc_in_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio_in *audio = (struct audio_in *) private_data; + unsigned long flags; + + MM_DBG("evt_id = 0x%8x\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: { + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt++; + if (!audio->in_call) + audio->source |= (0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((audio->running == 1) && (audio->enabled == 1) && + (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) + audevrc_in_record_config(audio, 1); + } + break; + case AUDDEV_EVT_DEV_RLS: { + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt--; + if (!audio->in_call) + audio->source &= ~(0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((!audio->running) || (!audio->enabled)) + break; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + /* Turn of as per source */ + if (audio->source) + audevrc_in_record_config(audio, 1); + else + /* Turn off all */ + audevrc_in_record_config(audio, 0); + } + } + break; + case AUDDEV_EVT_VOICE_STATE_CHG: { + MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n", + evt_payload->voice_state); + audio->voice_state = evt_payload->voice_state; + if (audio->in_call && audio->running && + (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + if (audio->voice_state == VOICE_STATE_INCALL) + audevrc_in_record_config(audio, 1); + else if (audio->voice_state == VOICE_STATE_OFFCALL) { + audevrc_in_record_config(audio, 0); + wake_up(&audio->wait); + } + } + + break; + } + default: + MM_ERR("wrong event %d\n", evt_id); + break; + } +} + +/* ------------------- dsp preproc event handler--------------------- */ +static void audpreproc_dsp_event(void *data, unsigned id, void *msg) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg *err_msg = msg; + + MM_ERR("ERROR_MSG: stream id %d err idx %d\n", + err_msg->stream_id, err_msg->aud_preproc_err_idx); + /* Error case */ + wake_up(&audio->wait_enable); + break; + } + case AUDPREPROC_CMD_CFG_DONE_MSG: { + MM_DBG("CMD_CFG_DONE_MSG \n"); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg; + + MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + 0x%8x\n", enc_cfg_msg->stream_id, + enc_cfg_msg->rec_enc_type); + /* Encoder enable success */ + if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) { + if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audpreproc_cmd_cfg_routing_mode(audio); + } else { + audevrc_in_param_config(audio); + } + } else { /* Encoder disable success */ + audio->running = 0; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audevrc_in_record_config(audio, 0); + else + wake_up(&audio->wait_enable); + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audevrc_in_mem_config(audio); + else + audpcm_config(audio); + break; + } + case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: { + struct audpreproc_cmd_routing_mode_done\ + *routing_cfg_done_msg = msg; + if (routing_cfg_done_msg->configuration == 0) { + MM_INFO("routing configuration failed\n"); + audio->running = 0; + } else + audevrc_in_param_config(audio); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n"); + wake_up(&audio->wait_enable); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +/* ------------------- dsp audrec event handler--------------------- */ +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDREC_CMD_MEM_CFG_DONE_MSG: { + MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n"); + audio->running = 1; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if ((!audio->in_call && (audio->dev_cnt > 0)) || + (audio->in_call && + (audio->voice_state \ + == VOICE_STATE_INCALL))) + audevrc_in_record_config(audio, 1); + } else { + audpreproc_pcm_send_data(audio, 1); + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_FATAL_ERR_MSG: { + struct audrec_fatal_err_msg fatal_err_msg; + + getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN); + MM_ERR("FATAL_ERR_MSG: err id %d\n", + fatal_err_msg.audrec_err_id); + /* Error stop the encoder */ + audio->stopped = 1; + wake_up(&audio->wait); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + wake_up(&audio->write_wait); + break; + } + case AUDREC_UP_PACKET_READY_MSG: { + struct audrec_up_pkt_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packet_write_cnt_lsw, \ + pkt_ready_msg.audrec_packet_write_cnt_msw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_msw); + + audevrc_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audpreproc_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audevrc_in_mem_config(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audevrc_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_EOS_ACK_MSG: { + MM_DBG("eos ack recieved\n"); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event:module audrectask\n"); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +static void audevrc_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("in_count = %d\n", audio->in_count); + } else + audio->in_count++; + + audevrc_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audevrc_nt_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + + +struct msm_adsp_ops audrec_evrc_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == META_OUT_SIZE) + len = len / 2; + else + len = (len + META_OUT_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audrec_send_audrecqueue(audio, (void *)&cmd, + (unsigned int)sizeof(cmd)); +} + + +static int audpcm_config(struct audio_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE; + cmd.sampling_freq = audio->samp_rate; + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audrec_send_audrecqueue(audio, (void *)&cmd, + (unsigned int)sizeof(cmd)); +} + + +static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE; + cmd.stream_id = audio->enc_id; + if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL) + cmd.routing_mode = 1; + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + + + +static int audevrc_in_enc_config(struct audio_in *audio, int enable) +{ + struct audpreproc_audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + if (audio->build_id[17] == '1') { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG_2 command"); + } else { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG command"); + } + cmd.stream_id = audio->enc_id; + + if (enable) + cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE; + else + cmd.audrec_enc_type &= ~(ENCODE_ENABLE); + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audevrc_in_param_config(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_parm_cfg_evrc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + + cmd.enc_min_rate = audio->cfg.min_bit_rate; + cmd.enc_max_rate = audio->cfg.max_bit_rate; + cmd.rate_modulation_cmd = 0; /* Default set to 0 */ + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +/* To Do: msm_snddev_route_enc(audio->enc_id); */ +static int audevrc_in_record_config(struct audio_in *audio, int enable) +{ + struct audpreproc_afe_cmd_audio_record_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG; + cmd.stream_id = audio->enc_id; + if (enable) + cmd.destination_activity = AUDIO_RECORDING_TURN_ON; + else + cmd.destination_activity = AUDIO_RECORDING_TURN_OFF; + + cmd.source_mix_mask = audio->source; + if (audio->enc_id == 2) { + if ((cmd.source_mix_mask & + INTERNAL_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) { + cmd.pipe_id = SOURCE_PIPE_1; + } + if (cmd.source_mix_mask & + AUDPP_A2DP_PIPE_SOURCE_MIX_MASK) + cmd.pipe_id |= SOURCE_PIPE_0; + } + MM_DBG("stream_id %x destination_activity %x \ + source_mix_mask %x pipe_id %x",\ + cmd.stream_id, cmd.destination_activity, + cmd.source_mix_mask, cmd.pipe_id); + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audevrc_in_mem_config(struct audio_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD; + cmd.audrec_up_pkt_intm_count = 1; + cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16; + cmd.audrec_ext_pkt_start_addr_lsw = audio->phys; + cmd.audrec_ext_pkt_buf_number = FRAME_NUM; + MM_DBG("audio->phys = %x\n", audio->phys); + /* prepare buffer pointers: + * T:36 bytes evrc packet + 4 halfword header + * NT:36 bytes evrc packet + 12 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audio->in[n].data = data + 4; + data += (FRAME_SIZE/2); + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8)); + } else { + audio->in[n].data = data + 12; + data += ((EVRC_FRAME_SIZE) / 2) + 12; + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24)); + } + } + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +static int audevrc_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + struct up_audrec_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR; + cmd.audrec_up_curr_read_count_msw = read_cnt >> 16; + cmd.audrec_up_curr_read_count_lsw = read_cnt; + + return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd)); +} +static int audevrc_flush_command(struct audio_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audevrc_in_enable(struct audio_in *audio) +{ + if (audio->enabled) + return 0; + + if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) { + MM_ERR("msm_adsp_enable(audpreproc) failed\n"); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + audpreproc_disable(audio->enc_id, audio); + return -ENODEV; + } + audio->enabled = 1; + audevrc_in_enc_config(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audevrc_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + audevrc_in_enc_config(audio, 0); + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + audpreproc_disable(audio->enc_id, audio); + } + return 0; +} + +static void audevrc_ioport_reset(struct audio_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audevrc_in_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audevrc_out_flush(audio); + mutex_unlock(&audio->read_lock); +} + +static void audevrc_in_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + audio->eos_ack = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audevrc_out_flush(struct audio_in *audio) +{ + int i; + + audio->out_head = 0; + audio->out_tail = 0; + audio->out_count = 0; + for (i = 0; i < OUT_FRAME_NUM; i++) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } +} + +/* ------------------- device --------------------- */ +static long audevrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t freq; + freq = 48000; + MM_DBG("AUDIO_START\n"); + if (audio->in_call && (audio->voice_state != + VOICE_STATE_INCALL)) { + rc = -EPERM; + break; + } + rc = msm_snddev_request_freq(&freq, audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("sample rate configured %d\n", freq); + if (rc < 0) { + MM_DBG(" Sample rate can not be set, return code %d\n", + rc); + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + break; + } + /*update aurec session info in audpreproc layer*/ + audio->session_info.session_id = audio->enc_id; + audio->session_info.sampling_freq = audio->samp_rate; + audpreproc_update_audrec_info(&audio->session_info); + rc = audevrc_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + rc = audevrc_in_disable(audio); + rc = msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + audio->stopped = 1; + break; + } + case AUDIO_FLUSH: { + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audevrc_ioport_reset(audio); + if (audio->running) { + audevrc_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) { + rc = -EINVAL; + break; + } + } else { + if (cfg.buffer_size != (EVRC_FRAME_SIZE + 14)) { + rc = -EINVAL; + break; + } + } + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate, + cfg.max_bit_rate, cfg.cdma_rate); + if (cfg.min_bit_rate > CDMA_RATE_FULL || \ + cfg.min_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid min bitrate\n"); + rc = -EFAULT; + break; + } + if (cfg.max_bit_rate > CDMA_RATE_FULL || \ + cfg.max_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid max bitrate\n"); + rc = -EFAULT; + break; + } + /* Recording Does not support Erase and Blank */ + if (cfg.cdma_rate > CDMA_RATE_FULL || + cfg.cdma_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid qcelp cdma rate\n"); + rc = -EFAULT; + break; + } + memcpy(&audio->cfg, &cfg, sizeof(cfg)); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + cfg.sample_rate = audio->samp_rate; + cfg.channel_count = audio->channel_mode; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_INCALL: { + struct msm_voicerec_mode cfg; + unsigned long flags; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.rec_mode != VOC_REC_BOTH && + cfg.rec_mode != VOC_REC_UPLINK && + cfg.rec_mode != VOC_REC_DOWNLINK) { + MM_ERR("invalid rec_mode\n"); + rc = -EINVAL; + break; + } else { + spin_lock_irqsave(&audio->dev_lock, flags); + if (cfg.rec_mode == VOC_REC_UPLINK) + audio->source = \ + VOICE_UL_SOURCE_MIX_MASK; + else if (cfg.rec_mode == VOC_REC_DOWNLINK) + audio->source = \ + VOICE_DL_SOURCE_MIX_MASK; + else + audio->source = \ + VOICE_DL_SOURCE_MIX_MASK | + VOICE_UL_SOURCE_MIX_MASK ; + audio->in_call = 1; + spin_unlock_irqrestore(&audio->dev_lock, flags); + } + } + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->enc_id, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audevrc_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + struct evrc_encoded_meta_out meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG("count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->rflush || + ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) && + audio->in_call && audio->running && + (audio->voice_state == VOICE_STATE_OFFCALL))); + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } else if ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) && + audio->in_call && audio->running && + (audio->voice_state \ + == VOICE_STATE_OFFCALL)) { + MM_DBG("Not Permitted Voice Terminated\n"); + rc = -EPERM; /* Voice Call stopped */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, + (sizeof(struct evrc_encoded_meta_out) - \ + sizeof(uint16_t))); + meta_field.metadata_len = + sizeof(struct evrc_encoded_meta_out); + if (copy_to_user((char *)start, (char *)&meta_field, + sizeof(struct evrc_encoded_meta_out))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_ERR("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct evrc_encoded_meta_out); + count -= sizeof(struct evrc_encoded_meta_out); + } + if (count >= size) { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) { + if (!audio->eos_ack) { + MM_DBG("sending read ptr command \ + %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audevrc_dsp_read_buffer(audio, + audio->dsp_cnt++); + } + } + } else { + MM_ERR("short read\n"); + break; + } + break; + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audpreproc_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + + +static int audevrc_in_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) + +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + + int audpreproc_evrc_process_eos(struct audio_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audpreproc_pcm_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audevrc_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int rc = 0, eos_condition = AUDPREPROC_EVRC_EOS_NONE; + unsigned short mfield_size = 0; + int write_count = 0; + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + /* if supplied count is more than driver buffer size + * then only copy driver buffer size + */ + if (count > frame->size) + count = frame->size; + + write_count = count; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] & + AUDPREPROC_EVRC_EOS_FLG_MASK) { + eos_condition = AUDPREPROC_EVRC_EOS_SET; + MM_DBG("EOS SET\n"); + if (mfield_size == count) { + buf += mfield_size; + eos_condition = 0; + goto exit; + } else + cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] &= + ~AUDPREPROC_EVRC_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audpreproc_pcm_send_data(audio, 0); + else { + audpreproc_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_EVRC_EOS_SET) + rc = audpreproc_evrc_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audevrc_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audio->in_call = 0; + /* with draw frequency for session + incase not stopped the driver */ + msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX, + AUDDEV_CLNT_ENC); + auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id); + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + audevrc_in_disable(audio); + audevrc_in_flush(audio); + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->opened = 0; + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + if (audio->out_data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_in the_audio_evrc_in; +static int audevrc_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_evrc_in; + int rc; + int encid; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (audio->phys) { + audio->map_v_read = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read physical address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto done; + } + audio->data = audio->map_v_read; + } else { + MM_ERR("could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + MM_DBG("Opened for non tunnel mode encoding\n"); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + MM_ERR("Invalid mode\n"); + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (EVRC_FRAME_SIZE + 14); + else + audio->buffer_size = (FRAME_SIZE - 8); + audio->enc_type = ENC_TYPE_EVRC | audio->mode; + audio->samp_rate = 8000; + audio->channel_mode = AUDREC_CMD_MODE_MONO; + audio->cfg.cdma_rate = CDMA_RATE_FULL; + audio->cfg.min_bit_rate = CDMA_RATE_FULL; + audio->cfg.max_bit_rate = CDMA_RATE_FULL; + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_evrc_adsp_ops, audio); + + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->stopped = 0; + audio->source = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audevrc_in_flush(audio); + audevrc_out_flush(audio); + + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, + SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate write buffers\n"); + rc = -ENOMEM; + goto evt_error; + } else { + audio->map_v_write = ioremap(audio->out_phys, BUFFER_SIZE); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could map write buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, (int)audio->out_data); + } + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (unsigned int)audio->out[0].data, + (unsigned int)audio->out[1].data); + audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_VOICE_STATE_CHG; + + audio->voice_state = msm_get_voice_state(); + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_ENC, audio->enc_id, + evrc_in_listener, (void *) audio); + if (rc) { + MM_ERR("failed to register device event listener\n"); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + goto evt_error; + } + audio->mfield = META_OUT_SIZE; + file->private_data = audio; + audio->opened = 1; + audio->out_frame_cnt++; + audio->build_id = socinfo_get_build_id(); + MM_DBG("Modem build id = %s\n", audio->build_id); + +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = audevrc_in_open, + .release = audevrc_in_release, + .read = audevrc_in_read, + .write = audevrc_in_write, + .fsync = audevrc_in_fsync, + .unlocked_ioctl = audevrc_in_ioctl, +}; + +struct miscdevice audio_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_in_fops, +}; + +static int __init audevrc_in_init(void) +{ + mutex_init(&the_audio_evrc_in.lock); + mutex_init(&the_audio_evrc_in.read_lock); + spin_lock_init(&the_audio_evrc_in.dsp_lock); + spin_lock_init(&the_audio_evrc_in.dev_lock); + init_waitqueue_head(&the_audio_evrc_in.wait); + init_waitqueue_head(&the_audio_evrc_in.wait_enable); + mutex_init(&the_audio_evrc_in.write_lock); + init_waitqueue_head(&the_audio_evrc_in.write_wait); + return misc_register(&audio_evrc_in_misc); +} + +device_initcall(audevrc_in_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_fm.c b/arch/arm/mach-msm/qdsp5v2/audio_fm.c new file mode 100644 index 00000000000..af65c802587 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_fm.c @@ -0,0 +1,358 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SESSION_ID_FM 6 +#define FM_ENABLE 0xFFFF +#define FM_DISABLE 0x0 +#define FM_COPP 0x2 +/* Macro specifies maximum FM routing + possible */ +#define FM_MAX_RX_ROUTE 0x2 + +struct fm_rx_calib_gain { + uint16_t device_id; + struct auddev_evt_devinfo dev_details; + struct acdb_calib_gain_rx calib_rx; +}; + +struct audio { + struct mutex lock; + + int opened; + int enabled; + int running; + + uint16_t dec_id; + uint16_t source; + uint16_t fm_source; + uint16_t fm_mask; + uint32_t device_events; + uint16_t volume; + struct fm_rx_calib_gain fm_calibration_rx[FM_MAX_RX_ROUTE]; +}; + +static struct audio fm_audio; + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + int rc = 0; + if (audio->enabled) + return 0; + + MM_DBG("fm mask= %08x fm_source = %08x\n", + audio->fm_mask, audio->fm_source); + if (audio->fm_mask && audio->fm_source) { + rc = afe_config_fm_codec(FM_ENABLE, audio->fm_mask); + if (!rc) + audio->running = 1; + /* Routed to icodec rx path */ + if ((audio->fm_mask & AFE_HW_PATH_CODEC_RX) == + AFE_HW_PATH_CODEC_RX) { + afe_config_fm_calibration_gain( + audio->fm_calibration_rx[0].device_id, + audio->fm_calibration_rx[0].calib_rx.audppcalgain); + } + /* Routed to aux codec rx path */ + if ((audio->fm_mask & AFE_HW_PATH_AUXPCM_RX) == + AFE_HW_PATH_AUXPCM_RX){ + afe_config_fm_calibration_gain( + audio->fm_calibration_rx[1].device_id, + audio->fm_calibration_rx[1].calib_rx.audppcalgain); + } + } + + audio->enabled = 1; + return rc; +} + +static void fm_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + struct auddev_evt_devinfo *devinfo = + (struct auddev_evt_devinfo *)evt_payload; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + if (evt_payload->routing_id == FM_COPP) + audio->fm_source = 1; + else + audio->source = (0x1 << evt_payload->routing_id); + + if (audio->source & 0x1) + audio->fm_mask = 0x1; + else if (audio->source & 0x2) + audio->fm_mask = 0x3; + else + audio->fm_mask = 0x0; + + if (!audio->enabled + || !audio->fm_mask + || !audio->fm_source) + break; + else { + afe_config_fm_codec(FM_ENABLE, audio->fm_mask); + audio->running = 1; + } + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + if (evt_payload->routing_id == FM_COPP) + audio->fm_source = 0; + else + audio->source &= ~(0x1 << evt_payload->routing_id); + + if (audio->source & 0x1) + audio->fm_mask = 0x1; + else if (audio->source & 0x2) + audio->fm_mask = 0x3; + else + audio->fm_mask = 0x0; + + if (audio->running + && (!audio->fm_mask || !audio->fm_source)) { + afe_config_fm_codec(FM_DISABLE, audio->fm_mask); + audio->running = 0; + } + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol \n"); + audio->volume = evt_payload->session_vol; + afe_config_fm_volume(audio->volume); + break; + case AUDDEV_EVT_DEVICE_INFO:{ + struct acdb_get_block get_block; + int rc = 0; + MM_DBG(":AUDDEV_EVT_DEVICE_INFO\n"); + MM_DBG("sample_rate = %d\n", devinfo->sample_rate); + MM_DBG("acdb_id = %d\n", devinfo->acdb_id); + /* Applucable only for icodec rx and aux codec rx path + and fm stream routed to it */ + if (((devinfo->dev_id == 0x00) || (devinfo->dev_id == 0x01)) && + (devinfo->sessions && (1 << audio->dec_id))) { + /* Query ACDB driver for calib gain, only if difference + in device */ + if ((audio->fm_calibration_rx[devinfo->dev_id]. + dev_details.acdb_id != devinfo->acdb_id) || + (audio->fm_calibration_rx[devinfo->dev_id]. + dev_details.sample_rate != + devinfo->sample_rate)) { + audio->fm_calibration_rx[devinfo->dev_id]. + dev_details.dev_id = devinfo->dev_id; + audio->fm_calibration_rx[devinfo->dev_id]. + dev_details.sample_rate = + devinfo->sample_rate; + audio->fm_calibration_rx[devinfo->dev_id]. + dev_details.dev_type = + devinfo->dev_type; + audio->fm_calibration_rx[devinfo->dev_id]. + dev_details.sessions = + devinfo->sessions; + /* Query ACDB driver for calibration gain */ + get_block.acdb_id = devinfo->acdb_id; + get_block.sample_rate_id = devinfo->sample_rate; + get_block.interface_id = + IID_AUDIO_CALIBRATION_GAIN_RX; + get_block.algorithm_block_id = + ABID_AUDIO_CALIBRATION_GAIN_RX; + get_block.total_bytes = + sizeof(struct acdb_calib_gain_rx); + get_block.buf_ptr = (u32 *) + &audio->fm_calibration_rx[devinfo->dev_id]. + calib_rx; + + rc = acdb_get_calibration_data(&get_block); + if (rc < 0) { + MM_ERR("Unable to get calibration"\ + "gain\n"); + /* Set to unity incase of error */ + audio->\ + fm_calibration_rx[devinfo->dev_id]. + calib_rx.audppcalgain = 0x2000; + } else + MM_DBG("calibration gain = 0x%8x\n", + *(get_block.buf_ptr)); + } + if (audio->running) { + afe_config_fm_calibration_gain( + audio->fm_calibration_rx[devinfo->dev_id]. + device_id, + audio->fm_calibration_rx[devinfo->dev_id]. + calib_rx.audppcalgain); + } + } + break; + } + default: + MM_DBG(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + return afe_config_fm_codec(FM_DISABLE, audio->source); +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + + MM_DBG("cmd = %d\n", cmd); + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_DBG("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &fm_audio; + int rc = 0; + + + if (audio->opened) + return -EPERM; + + /* Allocate the decoder */ + audio->dec_id = SESSION_ID_FM; + + audio->running = 0; + audio->fm_source = 0; + audio->fm_mask = 0; + + /* Initialize the calibration gain structure */ + audio->fm_calibration_rx[0].device_id = AFE_HW_PATH_CODEC_RX; + audio->fm_calibration_rx[1].device_id = AFE_HW_PATH_AUXPCM_RX; + audio->fm_calibration_rx[0].calib_rx.audppcalgain = 0x2000; + audio->fm_calibration_rx[1].calib_rx.audppcalgain = 0x2000; + audio->fm_calibration_rx[0].dev_details.acdb_id = PSEUDO_ACDB_ID; + audio->fm_calibration_rx[1].dev_details.acdb_id = PSEUDO_ACDB_ID; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG| + AUDDEV_EVT_DEVICE_INFO; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + fm_listner, + (void *)audio); + + if (rc) { + MM_ERR("%s: failed to register listnet\n", __func__); + goto event_err; + } + + audio->opened = 1; + file->private_data = audio; + +event_err: + return rc; +} + +static const struct file_operations audio_fm_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, +}; + +struct miscdevice audio_fm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_fm", + .fops = &audio_fm_fops, +}; + +static int __init audio_init(void) +{ + struct audio *audio = &fm_audio; + + mutex_init(&audio->lock); + return misc_register(&audio_fm_misc); +} + +device_initcall(audio_init); + +MODULE_DESCRIPTION("MSM FM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_interct.c b/arch/arm/mach-msm/qdsp5v2/audio_interct.c new file mode 100644 index 00000000000..785ed8ee5d6 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_interct.c @@ -0,0 +1,124 @@ +/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +#define AUDIO_INTERCT_ADSPLPA_WBRX_SEL_BMSK 0x4 +#define AUDIO_INTERCT_ADSPLPA_WBRX_SEL_SHFT 0x2 +#define AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_BMSK 0x10 +#define AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_SHFT 0x4 +#define AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_BMSK 0x40 +#define AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_SHFT 0x6 +#define AUDIO_INTERCT_ADSPAV_AUX_REGSEL_BMSK 0x100 +#define AUDIO_INTERCT_ADSPAV_AUX_REGSEL_SHFT 0x8 + +/* Should look to protect this register */ +void __iomem *aictl_reg; + +void audio_interct_codec(u32 source) +{ + u32 reg_val; + + reg_val = readl(aictl_reg); + reg_val = (reg_val & ~AUDIO_INTERCT_ADSPLPA_WBRX_SEL_BMSK) | + (source << AUDIO_INTERCT_ADSPLPA_WBRX_SEL_SHFT); + writel(reg_val, aictl_reg); + mb(); +} +EXPORT_SYMBOL(audio_interct_codec); + +void audio_interct_aux_regsel(u32 source) +{ + u32 reg_val; + + reg_val = readl(aictl_reg); + reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_AUX_REGSEL_BMSK) | + (source << AUDIO_INTERCT_ADSPAV_AUX_REGSEL_SHFT); + writel(reg_val, aictl_reg); + mb(); +} +EXPORT_SYMBOL(audio_interct_aux_regsel); + +void audio_interct_tpcm_source(u32 source) +{ + u32 reg_val; + + reg_val = readl(aictl_reg); + reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_BMSK) | + (source << AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_SHFT); + writel(reg_val, aictl_reg); + mb(); +} +EXPORT_SYMBOL(audio_interct_tpcm_source); + +void audio_interct_rpcm_source(u32 source) +{ + u32 reg_val; + + reg_val = readl(aictl_reg); + reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_BMSK) | + (source << AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_SHFT); + writel(reg_val, aictl_reg); + mb(); +} +EXPORT_SYMBOL(audio_interct_rpcm_source); + +static int audio_interct_probe(struct platform_device *pdev) +{ + int rc = 0; + struct resource *aictl_mem; + + aictl_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!aictl_mem) { + rc = -ENODEV; + goto error; + } + aictl_reg = ioremap(aictl_mem->start, + (aictl_mem->end - aictl_mem->start) + 1); +error: + return rc; +} + + +static int audio_interct_remove(struct platform_device *pdev) +{ + iounmap(aictl_reg); + return 0; +} + +static struct platform_driver audio_interct_driver = { + .probe = audio_interct_probe, + .remove = audio_interct_remove, + .driver = { + .name = "audio_interct", + .owner = THIS_MODULE, + }, +}; + +static int __init audio_interct_init(void) +{ + return platform_driver_register(&audio_interct_driver); +} + +static void __exit audio_interct_exit(void) +{ + platform_driver_unregister(&audio_interct_driver); +} + +module_init(audio_interct_init); +module_exit(audio_interct_exit); + +MODULE_DESCRIPTION("MSM Audio Interconnect driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c new file mode 100644 index 00000000000..d5fb2e9d239 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c @@ -0,0 +1,1748 @@ +/* low power audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 + +#define DEVICE_SWITCH_STATE_NONE 0 +#define DEVICE_SWITCH_STATE_PENDING 1 +#define DEVICE_SWITCH_STATE_READY 2 +#define DEVICE_SWITCH_STATE_COMPLETE 3 + +#define AUDDEC_DEC_PCM 0 +#define AUDDEC_DEC_MP3 2 + +#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */ + +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDMP3_METAFIELD_MASK 0xFFFF0000 +#define AUDMP3_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDMP3_EOS_FLG_MASK 0x01 +#define AUDMP3_EOS_NONE 0x0 /* No EOS detected */ +#define AUDMP3_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDLPA_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define MASK_32BITS 0xFFFFFFFF + +#define MAX_BUF 4 +#define BUFSZ (524288) + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +/* payload[7]; -1 indicates error, 0 indicates no error */ +#define CHECK_ERROR(v) (!v[7]) + +/* calculates avsync_info from payload */ +#define CALCULATE_AVSYNC_FROM_PAYLOAD(v) ((uint64_t)((((uint64_t)v[10]) \ + << 32) | (v[11] & MASK_32BITS))) + +/* calculates avsync_info from avsync_info stored in audio */ +#define CALCULATE_AVSYNC(v) \ + ((uint64_t)((((uint64_t)v[4]) << 32) | \ + (v[5] << 16) | (v[6]))) + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audlpa_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audlpa_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audlpa_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audlpa_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audlpa_dec { + char *name; + int dec_attrb; + long (*ioctl)(struct file *, unsigned int, unsigned long); + void (*adec_params)(struct audio *); +}; + +struct audlpa_dec audlpa_decs[] = { + {"msm_mp3_lp", AUDDEC_DEC_MP3, &mp3_ioctl, &audpp_cmd_cfg_mp3_params}, + {"msm_pcm_lp_dec", AUDDEC_DEC_PCM, &pcm_ioctl, + &audpp_cmd_cfg_pcm_params}, +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audlpa_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); +static void audlpa_async_send_data(struct audio *audio, unsigned needed, + uint32_t *payload); + +static void lpa_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY routing id = %d\n", + evt_payload->routing_id); + /* Do not select HLB path for icodec, if there is already COPP3 + * routing exists. DSP can not support concurrency of HLB path + * and COPP3 routing as it involves different buffer Path */ + if (((0x1 << evt_payload->routing_id) == AUDPP_MIXER_ICODEC) && + !(audio->source & AUDPP_MIXER_3)) { + audio->source |= AUDPP_MIXER_HLB; + MM_DBG("mixer_mask modified for low-power audio\n"); + } else + audio->source |= (0x1 << evt_payload->routing_id); + + MM_DBG("running = %d, enabled = %d, source = 0x%x\n", + audio->running, audio->enabled, audio->source); + if (audio->running == 1 && audio->enabled == 1) { + audpp_route_stream(audio->dec_id, audio->source); + if (audio->source & AUDPP_MIXER_HLB) { + audpp_dsp_set_vol_pan( + AUDPP_CMD_CFG_DEV_MIXER_ID_4, + &audio->vol_pan, + COPP); + /*restore the POPP gain to 0x2000 + this is needed to avoid use cases + where POPP volume is lowered during + NON HLB playback, when device moved + from NON HLB to HLB POPP is not + disabled but POPP gain will be retained + as the old one which result + in lower volume*/ + audio->vol_pan.volume = 0x2000; + audpp_dsp_set_vol_pan( + audio->dec_id, + &audio->vol_pan, POPP); + } else if (audio->source & AUDPP_MIXER_NONHLB) + audpp_dsp_set_vol_pan( + audio->dec_id, &audio->vol_pan, POPP); + if (audio->device_switch == DEVICE_SWITCH_STATE_READY) { + audio->wflush = 1; + audio->device_switch = + DEVICE_SWITCH_STATE_COMPLETE; + audpp_flush(audio->dec_id); + if (wait_event_interruptible(audio->write_wait, + !audio->wflush) < 0) + MM_DBG("AUDIO_FLUSH interrupted\n"); + + if (audio->wflush == 0) { + if (audio->drv_status & + ADRV_STATUS_PAUSE) { + if (audpp_pause(audio->dec_id, + 1)) + MM_DBG("audpp_pause" + "failed\n"); + } + } + } + } + break; + case AUDDEV_EVT_REL_PENDING: + MM_DBG(":AUDDEV_EVT_REL_PENDING\n"); + /* If route to multiple devices like COPP3, not need to + * handle device switch */ + if ((audio->running == 1) && (audio->enabled == 1) && + !(audio->source & AUDPP_MIXER_3)) { + if (audio->device_switch == DEVICE_SWITCH_STATE_NONE) { + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + if (audpp_pause(audio->dec_id, 1)) + MM_DBG("audpp pause failed\n"); + } + audio->device_switch = + DEVICE_SWITCH_STATE_PENDING; + audio->avsync_flag = 0; + if (audpp_query_avsync(audio->dec_id) < 0) + MM_DBG("query avsync failed\n"); + + if (wait_event_interruptible_timeout + (audio->avsync_wait, audio->avsync_flag, + msecs_to_jiffies(AVSYNC_EVENT_TIMEOUT)) < 0) + MM_DBG("AV sync timeout failed\n"); + if (audio->avsync_flag == 1) { + if (audio->device_switch == + DEVICE_SWITCH_STATE_PENDING) + audio->device_switch = + DEVICE_SWITCH_STATE_READY; + } + } + } + break; + case AUDDEV_EVT_DEV_RLS: + /* If there is already COPP3 routing exists. icodec route + * was not having HLB path. */ + MM_DBG(":AUDDEV_EVT_DEV_RLS routing id = %d\n", + evt_payload->routing_id); + if (((0x1 << evt_payload->routing_id) == AUDPP_MIXER_ICODEC) && + !(audio->source & AUDPP_MIXER_3)) + audio->source &= ~AUDPP_MIXER_HLB; + else + audio->source &= ~(0x1 << evt_payload->routing_id); + MM_DBG("running = %d, enabled = %d, source = 0x%x\n", + audio->running, audio->enabled, audio->source); + + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG("\n:AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n" + "running = %d, enabled = %d, source = 0x%x", + audio->vol_pan.volume, audio->running, + audio->enabled, audio->source); + if (audio->running == 1 && audio->enabled == 1) { + if (audio->source & AUDPP_MIXER_HLB) + audpp_dsp_set_vol_pan( + AUDPP_CMD_CFG_DEV_MIXER_ID_4, + &audio->vol_pan, COPP); + else if (audio->source & AUDPP_MIXER_NONHLB) + audpp_dsp_set_vol_pan( + audio->dec_id, &audio->vol_pan, POPP); + } + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audlpa_async_send_data(audio, 1, msg); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + default: + MM_ERR("unexpected message from decoder\n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason=0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + audio->codec_ops.adec_params(audio); + break; + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play\n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + case AUDPP_DEC_STATUS_EOS: + MM_DBG("decoder status: EOS\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + default: + MM_ERR("unknown decoder status\n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + MM_DBG("source = 0x%x\n", audio->source); + if (audio->source & AUDPP_MIXER_HLB) + audpp_dsp_set_vol_pan( + AUDPP_CMD_CFG_DEV_MIXER_ID_4, + &audio->vol_pan, + COPP); + else if (audio->source & AUDPP_MIXER_NONHLB) + audpp_dsp_set_vol_pan( + audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audio->codec_ops.adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_lpa = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | + audlpa_decs[audio->minor_no].dec_attrb; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audlpa_async_send_buffer(struct audio *audio) +{ + int found = 0; + uint64_t temp = 0; + struct audplay_cmd_bitstream_data_avail cmd; + struct audlpa_buffer_node *next_buf = NULL; + + temp = audio->bytecount_head; + if (audio->device_switch == DEVICE_SWITCH_STATE_NONE) { + list_for_each_entry(next_buf, &audio->out_queue, list) { + if (temp == audio->bytecount_given) { + found = 1; + break; + } else + temp += next_buf->buf.data_len; + } + if (next_buf && found) { + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + audio->bytecount_given += next_buf->buf.data_len; + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } else if (audio->device_switch == DEVICE_SWITCH_STATE_COMPLETE) { + audio->device_switch = DEVICE_SWITCH_STATE_NONE; + next_buf = list_first_entry(&audio->out_queue, + struct audlpa_buffer_node, list); + if (next_buf) { + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + temp = audio->bytecount_head + + next_buf->buf.data_len - + audio->bytecount_consumed; + if (audpp_restore_avsync(audio->dec_id, + &audio->avsync[0])) + MM_DBG("audpp_restore_avsync failed\n"); + + if ((signed)(temp >= 0) && + ((signed)(next_buf->buf.data_len - temp) >= 0)) { + MM_DBG("audlpa_async_send_buffer - sending the" + "rest of the buffer bassedon AV sync"); + cmd.buf_ptr = (unsigned) (next_buf->paddr + + (next_buf->buf.data_len - + temp)); + cmd.buf_size = temp >> 1; + cmd.partition_number = 0; + audio->bytecount_given = + audio->bytecount_consumed + temp; + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } else if ((signed)(temp >= 0) && + ((signed)(next_buf->buf.data_len - + temp) < 0)) { + MM_DBG("audlpa_async_send_buffer - else case:" + "sending the rest of the buffer bassedon" + "AV sync"); + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + audio->bytecount_given = audio->bytecount_head + + next_buf->buf.data_len; + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } + } +} + +static void audlpa_async_send_data(struct audio *audio, unsigned needed, + uint32_t *payload) +{ + unsigned long flags; + uint64_t temp = 0; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + union msm_audio_event_payload evt_payload; + struct audlpa_buffer_node *used_buf = NULL; + + if (CHECK_ERROR(payload)) + audio->bytecount_consumed = + CALCULATE_AVSYNC_FROM_PAYLOAD(payload); + + if ((audio->device_switch == + DEVICE_SWITCH_STATE_COMPLETE) && + (audio->avsync_flag == 1)) { + audio->avsync_flag = 0; + audio->bytecount_consumed = + CALCULATE_AVSYNC(audio->avsync); + } + BUG_ON(list_empty(&audio->out_queue)); + temp = audio->bytecount_head; + used_buf = list_first_entry(&audio->out_queue, + struct audlpa_buffer_node, list); + if (audio->device_switch != + DEVICE_SWITCH_STATE_COMPLETE) { + audio->bytecount_head += + used_buf->buf.data_len; + temp = audio->bytecount_head; + list_del(&used_buf->list); + evt_payload.aio_buf = used_buf->buf; + audlpa_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + evt_payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + } + } + if (audio->out_needed) { + if (!list_empty(&audio->out_queue)) + audlpa_async_send_buffer(audio); + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static void audlpa_async_flush(struct audio *audio) +{ + struct audlpa_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audlpa_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + if ((buf_node->paddr != 0xFFFFFFFF) && + (buf_node->buf.data_len != 0)) + audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + audio->bytecount_consumed = 0; + audio->bytecount_head = 0; + audio->bytecount_given = 0; + audio->device_switch = DEVICE_SWITCH_STATE_NONE; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + MM_DBG("fsync in progress\n"); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audlpa_async_flush(audio); + mutex_unlock(&audio->write_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + } else + audlpa_async_flush(audio); +} + +static int audlpa_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audlpa_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audlpa_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audlpa_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audlpa_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audlpa_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audlpa_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE || + drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + mutex_lock(&audio->lock); + audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audlpa_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audlpa_pmem_region *region_elt; + struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audlpa_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audlpa_pmem_region *region; + int rc = -EINVAL; + + MM_DBG("\n"); /* Macro prints the file name and function */ + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audlpa_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr, + region->vaddr, region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); +end: + return rc; +} + +static int audlpa_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + MM_DBG("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + MM_DBG("remove region fd %d vaddr %p\n", + info->fd, info->vaddr); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audlpa_pmem_region **region) +{ + struct audlpa_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audlpa_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audlpa_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + MM_ERR("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audlpa_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audlpa_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len" + "%d\n", buf_node, dir, + buf_node->buf.buf_addr, buf_node->buf.buf_len, + buf_node->buf.data_len); + + buf_node->paddr = audlpa_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + audlpa_async_send_data(audio, 0, 0); + } else { + /* read */ + } + + MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr); + + return 0; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("audio_ioctl() cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(AUDPP_CMD_CFG_DEV_MIXER_ID_4, + &audio->vol_pan, + COPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(AUDPP_CMD_CFG_DEV_MIXER_ID_4, + &audio->vol_pan, + COPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG(" AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audlpa_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_DBG("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + break; + + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audpp_pause(audio->dec_id, (int) arg); + if (rc < 0) { + MM_ERR("%s: pause cmd failed rc=%d\n", + __func__, rc); + rc = -EINTR; + break; + } + } + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + MM_INFO("AUDIO_SET_CONFIG\n"); + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + MM_INFO("ERROR: copy from user\n"); + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + MM_INFO("ERROR: config.channel_count == %d\n", + config.channel_count); + break; + } + + if (config.bits == 8) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8; + else if (config.bits == 16) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + else if (config.bits == 24) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_24; + else { + rc = -EINVAL; + MM_INFO("ERROR: config.bits == %d\n", config.bits); + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_bits = config.bits; + audio->buffer_count = config.buffer_count; + audio->buffer_size = config.buffer_size; + MM_DBG("AUDIO_SET_CONFIG: config.bits = %d\n", config.bits); + rc = 0; + break; + } + + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_count = audio->buffer_count; + config.buffer_size = audio->buffer_size; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8) + config.bits = 8; + else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_24) + config.bits = 24; + else + config.bits = 16; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + MM_DBG("AUDIO_GET_CONFIG: config.bits = %d\n", config.bits); + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + if (arg == 1) + audio->drv_status |= ADRV_STATUS_PAUSE; + else if (arg == 0) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + break; + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_pmem_remove(audio, &info); + break; + } + case AUDIO_ASYNC_WRITE: + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audlpa_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = audio->codec_ops.ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audlpa_async_fsync(struct audio *audio) +{ + int rc = 0, empty = 0; + struct audlpa_buffer_node *buf_node; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + audio->teos = 0; + empty = list_empty(&audio->out_queue); + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + if (!buf_node) + goto done; + + buf_node->paddr = 0xFFFFFFFF; + buf_node->buf.data_len = 0; + buf_node->buf.buf_addr = NULL; + buf_node->buf.buf_len = 0; + buf_node->buf.private_data = NULL; + list_add_tail(&buf_node->list, &audio->out_queue); + if ((empty != 0) && (audio->out_needed == 1)) + audlpa_async_send_data(audio, 0, 0); + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush || + audio->stopped); + + if (rc < 0) + goto done; + + if (audio->teos == 1) { + /* Releasing all the pending buffers to user */ + audio->teos = 0; + audlpa_async_flush(audio); + } + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audlpa_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + + if (!audio->running) + return -EINVAL; + + return audlpa_async_fsync(audio); +} + +static void audlpa_reset_pmem_region(struct audio *audio) +{ + struct audlpa_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audlpa_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audlpa_async_flush(audio); + audlpa_reset_pmem_region(audio); + + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audlpa_reset_event_queue(audio); + iounmap(audio->data); + pmem_kfree(audio->phys); + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audlpa_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audlpa_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audlpa_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audlpa_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audlpa_suspend(struct early_suspend *h) +{ + struct audlpa_suspend_ctl *ctl = + container_of(h, struct audlpa_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audlpa_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audlpa_resume(struct early_suspend *h) +{ + struct audlpa_suspend_ctl *ctl = + container_of(h, struct audlpa_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audlpa_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audlpa_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audlpa_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x\n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d\n", + audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d\n", + audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d\n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d\n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d\n", audio->out_needed); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audlpa_debug_fops = { + .read = audlpa_debug_read, + .open = audlpa_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, i, dec_attrb = 0, decid; + struct audlpa_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_lpa_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + if ((file->f_mode & FMODE_WRITE) && !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + /* Allocate the decoder based on inode minor number*/ + audio->minor_no = iminor(inode); + dec_attrb |= audlpa_decs[audio->minor_no].dec_attrb; + audio->codec_ops.ioctl = audlpa_decs[audio->minor_no].ioctl; + audio->codec_ops.adec_params = audlpa_decs[audio->minor_no].adec_params; + audio->buffer_size = BUFSZ; + audio->buffer_count = MAX_BUF; + + dec_attrb |= MSM_AUD_MODE_LP; + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available\n"); + rc = -ENODEV; + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + MM_DBG("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_lpa, audio); + + if (rc) { + MM_ERR("failed to get %s module\n", audio->module_name); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + audio->vol_pan.volume = 0x2000; + + audlpa_async_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS | AUDDEV_EVT_REL_PENDING + |AUDDEV_EVT_STREAM_VOL_CHG; + audio->device_switch = DEVICE_SWITCH_STATE_NONE; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->bytecount_consumed = 0; + audio->bytecount_head = 0; + audio->bytecount_given = 0; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + lpa_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listnet\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_lpa_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audlpa_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audlpa_resume; + audio->suspend_ctl.node.suspend = audlpa_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDLPA_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audlpa_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->data); + pmem_kfree(audio->phys); + audpp_adec_free(audio->dec_id); + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + kfree(audio); + return rc; +} + +static const struct file_operations audio_lpa_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audlpa_fsync, +}; + +static dev_t audlpa_devno; +static struct class *audlpa_class; +struct audlpa_device { + const char *name; + struct device *device; + struct cdev cdev; +}; + +static struct audlpa_device *audlpa_devices; + +static void audlpa_create(struct audlpa_device *adev, const char *name, + struct device *parent, dev_t devt) +{ + struct device *dev; + int rc; + + dev = device_create(audlpa_class, parent, devt, "%s", name); + if (IS_ERR(dev)) + return; + + cdev_init(&adev->cdev, &audio_lpa_fops); + adev->cdev.owner = THIS_MODULE; + + rc = cdev_add(&adev->cdev, devt, 1); + if (rc < 0) { + device_destroy(audlpa_class, devt); + } else { + adev->device = dev; + adev->name = name; + } +} + +static int __init audio_init(void) +{ + int rc; + int n = ARRAY_SIZE(audlpa_decs); + + audlpa_devices = kzalloc(sizeof(struct audlpa_device) * n, GFP_KERNEL); + if (!audlpa_devices) + return -ENOMEM; + + audlpa_class = class_create(THIS_MODULE, "audlpa"); + if (IS_ERR(audlpa_class)) + goto fail_create_class; + + rc = alloc_chrdev_region(&audlpa_devno, 0, n, "msm_audio_lpa"); + if (rc < 0) + goto fail_alloc_region; + + for (n = 0; n < ARRAY_SIZE(audlpa_decs); n++) { + audlpa_create(audlpa_devices + n, + audlpa_decs[n].name, NULL, + MKDEV(MAJOR(audlpa_devno), n)); + } + + return 0; + +fail_alloc_region: + class_unregister(audlpa_class); + return rc; +fail_create_class: + kfree(audlpa_devices); + return -ENOMEM; +} + +static void __exit audio_exit(void) +{ + class_unregister(audlpa_class); + kfree(audlpa_devices); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM LPA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_mp3.c b/arch/arm/mach-msm/qdsp5v2/audio_mp3.c new file mode 100644 index 00000000000..53ae0a470d7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_mp3.c @@ -0,0 +1,2521 @@ +/* mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 + +/* Size must be power of 2 */ +#define BUFSZ_MAX 32768 +#define BUFSZ_MIN 4096 +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_MP3 2 + +#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDMP3_METAFIELD_MASK 0xFFFF0000 +#define AUDMP3_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDMP3_EOS_FLG_MASK 0x01 +#define AUDMP3_EOS_NONE 0x0 /* No EOS detected */ +#define AUDMP3_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDMP3_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define BITSTREAM_ERROR_THRESHOLD_VALUE 0x1 /* DEFAULT THRESHOLD VALUE */ + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) +struct audio; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audmp3_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audmp3_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audmp3_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audmp3_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audmp3_drv_operations { + void (*pcm_buf_update)(struct audio *, uint32_t *); + void (*buffer_refresh)(struct audio *); + void (*send_data)(struct audio *, unsigned); + void (*out_flush)(struct audio *); + void (*in_flush)(struct audio *); + int (*fsync)(struct audio *); +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + struct list_head out_queue; /* queue to retain output buffers */ + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + struct list_head in_queue; /* queue to retain input buffers */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + uint32_t drv_status; + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audmp3_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + struct list_head pmem_region_queue; /* protected by lock */ + struct audmp3_drv_operations drv_ops; + + struct msm_audio_bitstream_info stream_info; + struct msm_audio_bitstream_error_info bitstream_error_info; + uint32_t bitstream_error_threshold_value; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_error_threshold_config(struct audio *audio); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audmp3_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); + +static void mp3_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audmp3_async_pcm_buf_update(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audmp3_buffer_node *filled_buf; + uint8_t index; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + BUG_ON(list_empty(&audio->in_queue)); + filled_buf = list_first_entry(&audio->in_queue, + struct audmp3_buffer_node, list); + if (filled_buf->paddr == payload[2 + index * 2]) { + list_del(&filled_buf->list); + event_payload.aio_buf = filled_buf->buf; + event_payload.aio_buf.data_len = + payload[3 + index * 2]; + MM_DBG("pcm buf %p data_len %d\n", filled_buf, + event_payload.aio_buf.data_len); + audmp3_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + MM_ERR("expected=%lx ret=%x\n", filled_buf->paddr, + payload[2 + index * 2]); + break; + } + } + + audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN; + audio->drv_ops.buffer_refresh(audio); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} + +static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[2 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audio->drv_ops.buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} + +static void audmp3_bitstream_error_info(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload e_payload; + + if (payload[0] != AUDDEC_DEC_MP3) { + MM_ERR("Unexpected bitstream error info from DSP:\ + Invalid decoder\n"); + return; + } + + /* get stream info from DSP msg */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + audio->bitstream_error_info.dec_id = payload[0]; + audio->bitstream_error_info.err_msg_indicator = payload[1]; + audio->bitstream_error_info.err_type = payload[2]; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_ERR("bit_stream_error_type=%d error_count=%d\n", + audio->bitstream_error_info.err_type, (0x0000FFFF & + audio->bitstream_error_info.err_msg_indicator)); + + /* send event to ARM to notify error info coming */ + e_payload.error_info = audio->bitstream_error_info; + audmp3_post_event(audio, AUDIO_EVENT_BITSTREAM_ERROR_INFO, e_payload); +} + +static void audmp3_update_stream_info(struct audio *audio, uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload e_payload; + + /* get stream info from DSP msg */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + audio->stream_info.codec_type = AUDIO_CODEC_TYPE_MP3; + audio->stream_info.chan_info = (0x0000FFFF & payload[1]); + audio->stream_info.sample_rate = (0x0000FFFF & payload[2]); + audio->stream_info.bit_stream_info = (0x0000FFFF & payload[3]); + audio->stream_info.bit_rate = payload[4]; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + MM_DBG("chan_info=%d, sample_rate=%d, bit_stream_info=%d\n", + audio->stream_info.chan_info, + audio->stream_info.sample_rate, + audio->stream_info.bit_stream_info); + + /* send event to ARM to notify steam info coming */ + e_payload.stream_info = audio->stream_info; + audmp3_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audio->drv_ops.send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio->drv_ops.pcm_buf_update(audio, msg); + break; + + case AUDPLAY_UP_STREAM_INFO: + if ((msg[1] & AUDPLAY_STREAM_INFO_MSG_MASK) == + AUDPLAY_STREAM_INFO_MSG_MASK) { + audmp3_bitstream_error_info(audio, msg); + } else { + audmp3_update_stream_info(audio, msg); + } + break; + + case AUDPLAY_UP_OUTPORT_FLUSH_ACK: + MM_DBG("OUTPORT_FLUSH_ACK\n"); + audio->rflush = 0; + wake_up(&audio->read_wait); + if (audio->pcm_feedback) + audio->drv_ops.buffer_refresh(audio); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status: sleep reason=0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audplay_error_threshold_config(audio); + audplay_config_hostpcm(audio); + audio->drv_ops.buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audio->drv_ops.buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + + +struct msm_adsp_ops audplay_adsp_ops = { + .event = audplay_dsp_event, +}; + + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_MP3; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_mp3 cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDMP3_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} +/* Caller holds irq_lock */ +static void audmp3_async_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + struct audmp3_buffer_node *next_buf; + + if (!audio->running || + audio->drv_status & ADRV_STATUS_IBUF_GIVEN) + return; + + if (!list_empty(&audio->in_queue)) { + next_buf = list_first_entry(&audio->in_queue, + struct audmp3_buffer_node, list); + if (!next_buf) + return; + MM_DBG("next buf %p phy %lx len %d\n", next_buf, + next_buf->paddr, next_buf->buf.buf_len); + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = next_buf->paddr; + refresh_cmd.buf0_length = next_buf->buf.buf_len - + (next_buf->buf.buf_len % 576) + + (audio->mfield ? 24 : 0); /* Mp3 frame size */ + refresh_cmd.buf_read_count = 0; + audio->drv_status |= ADRV_STATUS_IBUF_GIVEN; + (void) audplay_send_queue0(audio, &refresh_cmd, + sizeof(refresh_cmd)); + } + +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % 576) + + (audio->mfield ? 24 : 0); /* Mp3 frame size */ + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_error_threshold_config(struct audio *audio) +{ + union audplay_cmd_channel_info ch_cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + ch_cfg_cmd.thr_update.cmd_id = AUDPLAY_CMD_CHANNEL_INFO; + ch_cfg_cmd.thr_update.threshold_update = AUDPLAY_ERROR_THRESHOLD_ENABLE; + ch_cfg_cmd.thr_update.threshold_value = + audio->bitstream_error_threshold_value; + (void)audplay_send_queue0(audio, &ch_cfg_cmd, sizeof(ch_cfg_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audplay_outport_flush(struct audio *audio) +{ + struct audplay_cmd_outport_flush op_flush_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + op_flush_cmd.cmd_id = AUDPLAY_CMD_OUTPORT_FLUSH; + (void)audplay_send_queue0(audio, &op_flush_cmd, sizeof(op_flush_cmd)); +} + +static void audmp3_async_send_data(struct audio *audio, unsigned needed) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload payload; + struct audmp3_buffer_node *used_buf; + + MM_DBG("consumed\n"); + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audmp3_buffer_node, list); + list_del(&used_buf->list); + payload.aio_buf = used_buf->buf; + audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + + } + + if (audio->out_needed) { + struct audmp3_buffer_node *next_buf; + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + if (!list_empty(&audio->out_queue)) { + next_buf = list_first_entry(&audio->out_queue, + struct audmp3_buffer_node, list); + MM_DBG("next_buf %p\n", next_buf); + if (next_buf) { + MM_DBG("next buf phy %lx len %d\n", + next_buf->paddr, + next_buf->buf.data_len); + + cmd.cmd_id = + AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDMP3_METAFIELD_MASK | + (next_buf->buf.mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } + } + +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static void audmp3_async_flush(struct audio *audio) +{ + struct audmp3_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audmp3_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audmp3_async_flush_pcm_buf(struct audio *audio) +{ + struct audmp3_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audmp3_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = 0; + audmp3_post_event(audio, AUDIO_EVENT_READ_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN; + +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audio_ioport_reset(struct audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + MM_DBG("fsync in progress\n"); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + } else + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + } else { + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio->drv_ops.in_flush(audio); + mutex_unlock(&audio->read_lock); + } + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audmp3_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audmp3_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audmp3_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audmp3_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audmp3_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audmp3_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audmp3_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audmp3_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audmp3_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audmp3_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE || + drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + mutex_lock(&audio->lock); + audmp3_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audmp3_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audmp3_pmem_region *region_elt; + struct audmp3_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audmp3_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audmp3_pmem_region *region; + int rc = -EINVAL; + + MM_DBG("\n"); /* Macro prints the file name and function */ + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + rc = audmp3_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr, + region->vaddr, region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); +end: + return rc; +} + +static int audmp3_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audmp3_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audmp3_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + MM_DBG("region %p in use ref_cnt %d\n", + region, region->ref_cnt); + break; + } + MM_DBG("remove region fd %d vaddr %p \n", + info->fd, info->vaddr); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audmp3_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audmp3_pmem_region **region) +{ + struct audmp3_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audmp3_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audmp3_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + MM_ERR("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audmp3_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audmp3_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len \ + %d\n", buf_node, dir, + buf_node->buf.buf_addr, buf_node->buf.buf_len, + buf_node->buf.data_len); + + buf_node->paddr = audmp3_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1) || + (!audio->pcm_feedback && + !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + audio->drv_ops.send_data(audio, 0); + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->in_queue); + audio->drv_ops.buffer_refresh(audio); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr); + + return 0; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG(" AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audmp3_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_OUTPORT_FLUSH: + MM_DBG("AUDIO_OUTPORT_FLUSH\n"); + audio->rflush = 1; + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + audio->drv_ops.in_flush(audio); + } else { + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio->drv_ops.in_flush(audio); + mutex_unlock(&audio->read_lock); + } + audplay_outport_flush(audio); + rc = wait_event_interruptible(audio->read_wait, + !audio->rflush); + if (rc < 0) { + MM_ERR("AUDPLAY_OUTPORT_FLUSH interrupted\n"); + rc = -EINTR; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + rc = 0; + break; + } + + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read buffer" + " physical address\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + + case AUDIO_GET_STREAM_INFO:{ + if (audio->stream_info.sample_rate == 0) { + /* haven't received DSP stream event, + the stream info is not updated */ + rc = -EPERM; + break; + } + if (copy_to_user((void *)arg, &audio->stream_info, + sizeof(struct msm_audio_bitstream_info))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_BITSTREAM_ERROR_INFO:{ + if ((audio->bitstream_error_info.err_msg_indicator & + AUDPLAY_STREAM_INFO_MSG_MASK) == + AUDPLAY_STREAM_INFO_MSG_MASK) { + /* haven't received bitstream error info event, + the bitstream error info is not updated */ + rc = -EPERM; + break; + } + if (copy_to_user((void *)arg, &audio->bitstream_error_info, + sizeof(struct msm_audio_bitstream_error_info))) + rc = -EFAULT; + else + rc = 0; + break; + } + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audmp3_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audmp3_pmem_remove(audio, &info); + break; + } + case AUDIO_ASYNC_WRITE: + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audmp3_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_ASYNC_READ: + if (audio->pcm_feedback) + rc = audmp3_aio_buf_add(audio, 0, (void __user *) arg); + else + rc = -EPERM; + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + case AUDIO_SET_ERR_THRESHOLD_VALUE: + if (copy_from_user(&audio->bitstream_error_threshold_value, + (void *)arg, sizeof(uint32_t))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audmp3_async_fsync(struct audio *audio) +{ + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + (audio->teos && audio->out_needed && + list_empty(&audio->out_queue)) + || audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audmp3_sync_fsync(struct audio *audio) +{ + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audio->drv_ops.send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +int audmp3_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + + if (!audio->running || audio->pcm_feedback) + return -EINVAL; + + return audio->drv_ops.fsync(audio); +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (audio->drv_status & ADRV_STATUS_AIO_INTF) + return -EPERM; + else if (!audio->pcm_feedback) + return 0; /* PCM feedback disabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible_timeout( + audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped) + || (audio->rflush), + msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS)); + + if (rc == 0) { + rc = -ETIMEDOUT; + break; + } else if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since + * driver does not know frame size, read count + * must be greater or equal + * to size of PCM samples + */ + MM_DBG("no partial frame done reading\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audio->drv_ops.buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audmp3_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audio->drv_ops.send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audio->drv_ops.send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDMP3_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + if (audio->drv_status & ADRV_STATUS_AIO_INTF) + return -EPERM; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDMP3_EOS_FLG_OFFSET] & + AUDMP3_EOS_FLG_MASK) { + MM_DBG("EOS SET\n"); + eos_condition = AUDMP3_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDMP3_EOS_FLG_OFFSET] + &= ~AUDMP3_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audio->drv_ops.send_data(audio, 0); + } + } + if (eos_condition == AUDMP3_EOS_SET) + rc = audmp3_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static void audmp3_reset_pmem_region(struct audio *audio) +{ + struct audmp3_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audmp3_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audmp3_reset_pmem_region(audio); + + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audmp3_reset_event_queue(audio); + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audmp3_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audmp3_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audmp3_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audmp3_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audmp3_suspend(struct early_suspend *h) +{ + struct audmp3_suspend_ctl *ctl = + container_of(h, struct audmp3_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audmp3_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audmp3_resume(struct early_suspend *h) +{ + struct audmp3_suspend_ctl *ctl = + container_of(h, struct audmp3_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audmp3_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audmp3_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audmp3_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audmp3_debug_fops = { + .read = audmp3_debug_read, + .open = audmp3_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + + struct audio *audio = NULL; + int rc, i, dec_attrb, decid; + struct audmp3_event *e_node = NULL; + unsigned pmem_sz = DMASZ_MAX; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_mp3_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_MP3; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + /* AIO interface */ + if (file->f_flags & O_NONBLOCK) { + MM_DBG("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.pcm_buf_update = audmp3_async_pcm_buf_update; + audio->drv_ops.buffer_refresh = audmp3_async_buffer_refresh; + audio->drv_ops.send_data = audmp3_async_send_data; + audio->drv_ops.out_flush = audmp3_async_flush; + audio->drv_ops.in_flush = audmp3_async_flush_pcm_buf; + audio->drv_ops.fsync = audmp3_async_fsync; + } else { + MM_DBG("set to std io interface\n"); + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, + SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap( + audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("failed to map write physical" + " address , freeing instance" + "0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr\ + 0x%08x\n", audio->phys,\ + (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + audio->drv_ops.pcm_buf_update = audio_update_pcm_buf_entry; + audio->drv_ops.buffer_refresh = audplay_buffer_refresh; + audio->drv_ops.send_data = audplay_send_data; + audio->drv_ops.out_flush = audio_flush; + audio->drv_ops.in_flush = audio_flush_pcm_buf; + audio->drv_ops.fsync = audmp3_sync_fsync; + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = (audio->out_dma_sz >> 1); + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops, audio); + + if (rc) { + MM_ERR("failed to get %s module freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->vol_pan.volume = 0x2000; + audio->bitstream_error_threshold_value = + BITSTREAM_ERROR_THRESHOLD_VALUE; + + audio->drv_ops.out_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + mp3_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_mp3_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audmp3_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audmp3_resume; + audio->suspend_ctl.node.suspend = audmp3_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDMP3_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audmp3_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } + memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info)); + memset(&audio->bitstream_error_info, 0, + sizeof(struct msm_audio_bitstream_info)); +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audmp3_fsync, +}; + +struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_mp3_misc); +} + +static void __exit audio_exit(void) +{ + misc_deregister(&audio_mp3_misc); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM MP3 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_mvs.c b/arch/arm/mach-msm/qdsp5v2/audio_mvs.c new file mode 100644 index 00000000000..99da8362a3a --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_mvs.c @@ -0,0 +1,1757 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MVS_PROG 0x30000014 +#define MVS_VERS 0x00030001 +#define MVS_VERS_COMP_VER4 0x00040001 +#define MVS_VERS_COMP_VER5 0x00050001 + +#define MVS_CLIENT_ID_VOIP 0x00000003 + +#define MVS_ACQUIRE_PROC 4 +#define MVS_ENABLE_PROC 5 +#define MVS_RELEASE_PROC 6 +#define MVS_AMR_SET_AMR_MODE_PROC 7 +#define MVS_AMR_SET_AWB_MODE_PROC 8 +#define MVS_VOC_SET_FRAME_RATE_PROC 10 +#define MVS_GSM_SET_DTX_MODE_PROC 11 +#define MVS_G729A_SET_MODE_PROC 12 +#define MVS_G711_GET_MODE_PROC 14 +#define MVS_G711_SET_MODE_PROC 15 +#define MVS_G711A_GET_MODE_PROC 16 +#define MVS_G711A_SET_MODE_PROC 17 +#define MVS_G722_SET_MODE_PROC 20 +#define MVS_G722_GET_MODE_PROC 21 +#define MVS_SET_DTX_MODE_PROC 22 + +#define MVS_EVENT_CB_TYPE_PROC 1 +#define MVS_PACKET_UL_FN_TYPE_PROC 2 +#define MVS_PACKET_DL_FN_TYPE_PROC 3 + +#define MVS_CB_FUNC_ID 0xAAAABBBB +#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC +#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD + +#define MVS_FRAME_MODE_VOC_TX 1 +#define MVS_FRAME_MODE_VOC_RX 2 +#define MVS_FRAME_MODE_AMR_UL 3 +#define MVS_FRAME_MODE_AMR_DL 4 +#define MVS_FRAME_MODE_GSM_UL 5 +#define MVS_FRAME_MODE_GSM_DL 6 +#define MVS_FRAME_MODE_HR_UL 7 +#define MVS_FRAME_MODE_HR_DL 8 +#define MVS_FRAME_MODE_G711_UL 9 +#define MVS_FRAME_MODE_G711_DL 10 +#define MVS_FRAME_MODE_PCM_UL 13 +#define MVS_FRAME_MODE_PCM_DL 14 +#define MVS_FRAME_MODE_G729A_UL 17 +#define MVS_FRAME_MODE_G729A_DL 18 +#define MVS_FRAME_MODE_G711A_UL 19 +#define MVS_FRAME_MODE_G711A_DL 20 +#define MVS_FRAME_MODE_G722_UL 21 +#define MVS_FRAME_MODE_G722_DL 22 + + + +#define MVS_PKT_CONTEXT_ISR 0x00000001 + +#define RPC_TYPE_REQUEST 0 +#define RPC_TYPE_REPLY 1 + +#define RPC_STATUS_FAILURE 0 +#define RPC_STATUS_SUCCESS 1 +#define RPC_STATUS_REJECT 1 + +#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) +#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) +#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) + +enum audio_mvs_state_type { + AUDIO_MVS_CLOSED, + AUDIO_MVS_OPENED, + AUDIO_MVS_STARTED, + AUDIO_MVS_STOPPED +}; + +enum audio_mvs_event_type { + AUDIO_MVS_COMMAND, + AUDIO_MVS_MODE, + AUDIO_MVS_NOTIFY +}; + +enum audio_mvs_cmd_status_type { + AUDIO_MVS_CMD_FAILURE, + AUDIO_MVS_CMD_BUSY, + AUDIO_MVS_CMD_SUCCESS +}; + +enum audio_mvs_mode_status_type { + AUDIO_MVS_MODE_NOT_AVAIL, + AUDIO_MVS_MODE_INIT, + AUDIO_MVS_MODE_READY +}; + +enum audio_mvs_pkt_status_type { + AUDIO_MVS_PKT_NORMAL, + AUDIO_MVS_PKT_FAST, + AUDIO_MVS_PKT_SLOW +}; + +/* Parameters required for MVS acquire. */ +struct rpc_audio_mvs_acquire_args { + uint32_t client_id; + uint32_t cb_func_id; +}; + +struct audio_mvs_acquire_msg { + struct rpc_request_hdr rpc_hdr; + struct rpc_audio_mvs_acquire_args acquire_args; +}; + +/* Parameters required for MVS enable. */ +struct rpc_audio_mvs_enable_args { + uint32_t client_id; + uint32_t mode; + uint32_t ul_cb_func_id; + uint32_t dl_cb_func_id; + uint32_t context; +}; + +struct audio_mvs_enable_msg { + struct rpc_request_hdr rpc_hdr; + struct rpc_audio_mvs_enable_args enable_args; +}; + +/* Parameters required for MVS release. */ +struct audio_mvs_release_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t client_id; +}; + +/* Parameters required for setting AMR mode. */ +struct audio_mvs_set_amr_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t amr_mode; +}; + +/* Parameters required for setting DTX. */ +struct audio_mvs_set_dtx_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t dtx_mode; +}; + +/* Parameters required for setting EVRC mode. */ +struct audio_mvs_set_voc_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t max_rate; + uint32_t min_rate; +}; + +/* Parameters for G711 mode */ +struct audio_mvs_set_g711_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g711_mode; +}; + +/* Parameters for G729 mode */ +struct audio_mvs_set_g729_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g729_mode; +}; + +/* Parameters for G722 mode */ +struct audio_mvs_set_g722_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g722_mode; +}; + + +/* Parameters for G711A mode */ +struct audio_mvs_set_g711A_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t g711A_mode; +}; + +/* Parameters for EFR FR and HR mode */ +struct audio_mvs_set_efr_mode_msg { + struct rpc_request_hdr rpc_hdr; + uint32_t efr_mode; +}; + +union audio_mvs_event_data { + struct mvs_ev_command_type { + uint32_t event; + uint32_t client_id; + uint32_t cmd_status; + } mvs_ev_command_type; + + struct mvs_ev_mode_type { + uint32_t event; + uint32_t client_id; + uint32_t mode_status; + uint32_t mode; + } mvs_ev_mode_type; + + struct mvs_ev_notify_type { + uint32_t event; + uint32_t client_id; + uint32_t buf_dir; + uint32_t max_frames; + } mvs_ev_notify_type; +}; + +struct audio_mvs_cb_func_args { + uint32_t cb_func_id; + uint32_t valid_ptr; + uint32_t event; + union audio_mvs_event_data event_data; +}; + +struct audio_mvs_frame_info_hdr { + uint32_t frame_mode; + uint32_t mvs_mode; + uint16_t buf_free_cnt; +}; + +struct audio_mvs_ul_reply { + struct rpc_reply_hdr reply_hdr; + uint32_t valid_pkt_status_ptr; + uint32_t pkt_status; +}; + +struct audio_mvs_dl_cb_func_args { + uint32_t cb_func_id; + + uint32_t valid_ptr; + uint32_t frame_mode; + uint32_t frame_mode_ignore; + + struct audio_mvs_frame_info_hdr frame_info_hdr; + + uint32_t amr_frame; + uint32_t amr_mode; +}; +/*general codec parameters includes AMR, G711A, PCM +G729, VOC and HR vocoders +*/ +struct gnr_cdc_param { + uint32_t param1; + uint32_t param2; + uint32_t valid_pkt_status_ptr; + uint32_t pkt_status; +}; +/*G711 codec parameter*/ +struct g711_param { + uint32_t param1; + uint32_t valid_pkt_status_ptr; + uint32_t pkt_status; +}; + +union codec_param { + struct gnr_cdc_param gnr_arg; + struct g711_param g711_arg; +}; + +struct audio_mvs_dl_reply { + struct rpc_reply_hdr reply_hdr; + + uint32_t voc_pkt[Q5V2_MVS_MAX_VOC_PKT_SIZE/4]; + + uint32_t valid_frame_info_ptr; + uint32_t frame_mode; + uint32_t frame_mode_again; + + struct audio_mvs_frame_info_hdr frame_info_hdr; + union codec_param cdc_param; +}; + +struct audio_mvs_buf_node { + struct list_head list; + struct q5v2_msm_audio_mvs_frame frame; +}; + +/* Each buffer is 20 ms, queue holds 200 ms of data. */ +#define MVS_MAX_Q_LEN 10 + +struct audio_mvs_info_type { + enum audio_mvs_state_type state; + uint32_t frame_mode; + uint32_t mvs_mode; + uint32_t buf_free_cnt; + uint32_t rate_type; + uint32_t dtx_mode; + + struct msm_rpc_endpoint *rpc_endpt; + uint32_t rpc_prog; + uint32_t rpc_ver; + uint32_t rpc_status; + + uint8_t *mem_chunk; + + struct list_head in_queue; + struct list_head free_in_queue; + + struct list_head out_queue; + struct list_head free_out_queue; + + struct task_struct *task; + + wait_queue_head_t wait; + wait_queue_head_t mode_wait; + wait_queue_head_t out_wait; + + struct mutex lock; + struct mutex in_lock; + struct mutex out_lock; + + struct wake_lock suspend_lock; + struct pm_qos_request pm_qos_req; +}; + +static struct audio_mvs_info_type audio_mvs_info; + +static int audio_mvs_setup_mode(struct audio_mvs_info_type *audio) +{ + int rc = 0; + + pr_debug("%s:\n", __func__); + + switch (audio->mvs_mode) { + case MVS_MODE_AMR: + case MVS_MODE_AMR_WB: { + struct audio_mvs_set_amr_mode_msg set_amr_mode_msg; + struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg; + + /* Set AMR mode. */ + memset(&set_amr_mode_msg, 0, sizeof(set_amr_mode_msg)); + set_amr_mode_msg.amr_mode = cpu_to_be32(audio->rate_type); + + if (audio->mvs_mode == MVS_MODE_AMR) { + msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_AMR_SET_AMR_MODE_PROC); + } else { + msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_AMR_SET_AWB_MODE_PROC); + } + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_amr_mode_msg, + sizeof(set_amr_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set amr mode done\n", + __func__); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_AMR_DL; + + /* Disable DTX. */ + memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg)); + set_dtx_mode_msg.dtx_mode = cpu_to_be32(0); + + msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_SET_DTX_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_dtx_mode_msg, + sizeof(set_dtx_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set dtx done\n", + __func__); + + rc = 0; + } + } else { + pr_err("%s: RPC write for set amr mode failed %d\n", + __func__, rc); + } + break; + } + case MVS_MODE_PCM: + case MVS_MODE_LINEAR_PCM: { + /* PCM does not have any params to be set. + Save the MVS configuration information. */ + audio->rate_type = MVS_AMR_MODE_UNDEF; + audio->frame_mode = MVS_FRAME_MODE_PCM_DL; + break; + } + case MVS_MODE_IS127: + case MVS_MODE_IS733: + case MVS_MODE_4GV_NB: + case MVS_MODE_4GV_WB: { + struct audio_mvs_set_voc_mode_msg set_voc_mode_msg; + + /* Set EVRC mode. */ + memset(&set_voc_mode_msg, 0, sizeof(set_voc_mode_msg)); + set_voc_mode_msg.min_rate = cpu_to_be32(audio->rate_type); + set_voc_mode_msg.max_rate = cpu_to_be32(audio->rate_type); + + msm_rpc_setup_req(&set_voc_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_VOC_SET_FRAME_RATE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_voc_mode_msg, + sizeof(set_voc_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set voc mode done\n", + __func__); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_VOC_RX; + + rc = 0; + } else { + pr_err("%s: RPC write for set voc mode failed %d\n", + __func__, rc); + } + break; + } + case MVS_MODE_G711: { + struct audio_mvs_set_g711_mode_msg set_g711_mode_msg; + + /* Set G711 mode. */ + memset(&set_g711_mode_msg, 0, sizeof(set_g711_mode_msg)); + set_g711_mode_msg.g711_mode = cpu_to_be32(audio->rate_type); + + pr_debug("%s: mode of g711:%d\n", + __func__, set_g711_mode_msg.g711_mode); + + msm_rpc_setup_req(&set_g711_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G711_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g711_mode_msg, + sizeof(set_g711_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set g711 mode done\n", + __func__); + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G711_DL; + + rc = 0; + } else { + pr_err("%s: RPC write for set g711 mode failed %d\n", + __func__, rc); + } + break; + } + case MVS_MODE_G729A: { + struct audio_mvs_set_g729_mode_msg set_g729_mode_msg; + + /* Set G729 mode. */ + memset(&set_g729_mode_msg, 0, sizeof(set_g729_mode_msg)); + set_g729_mode_msg.g729_mode = cpu_to_be32(audio->dtx_mode); + + pr_debug("%s: mode of g729:%d\n", + __func__, set_g729_mode_msg.g729_mode); + + msm_rpc_setup_req(&set_g729_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G729A_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g729_mode_msg, + sizeof(set_g729_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set g729 mode done\n", + __func__); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G729A_DL; + + rc = 0; + } else { + pr_err("%s: RPC write for set g729 mode failed %d\n", + __func__, rc); + } + break; + } + case MVS_MODE_G722: { + struct audio_mvs_set_g722_mode_msg set_g722_mode_msg; + + /* Set G722 mode. */ + memset(&set_g722_mode_msg, 0, sizeof(set_g722_mode_msg)); + set_g722_mode_msg.g722_mode = cpu_to_be32(audio->rate_type); + + pr_debug("%s: mode of g722:%d\n", + __func__, set_g722_mode_msg.g722_mode); + + msm_rpc_setup_req(&set_g722_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G722_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g722_mode_msg, + sizeof(set_g722_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set g722 mode done\n", + __func__); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G722_DL; + + rc = 0; + } + break; + } + case MVS_MODE_G711A: { + struct audio_mvs_set_g711A_mode_msg set_g711A_mode_msg; + struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg; + + /* Set G711A mode. */ + memset(&set_g711A_mode_msg, 0, sizeof(set_g711A_mode_msg)); + set_g711A_mode_msg.g711A_mode = cpu_to_be32(audio->rate_type); + + pr_debug("%s: mode of g711A:%d\n", + __func__, set_g711A_mode_msg.g711A_mode); + + msm_rpc_setup_req(&set_g711A_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_G711A_SET_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_g711A_mode_msg, + sizeof(set_g711A_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set g711A mode done\n", + __func__); + + /* Save the MVS configuration information. */ + audio->frame_mode = MVS_FRAME_MODE_G711A_DL; + /* Set DTX MODE. */ + memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg)); + set_dtx_mode_msg.dtx_mode = + cpu_to_be32((audio->dtx_mode)); + + msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_SET_DTX_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_dtx_mode_msg, + sizeof(set_dtx_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set dtx done\n", + __func__); + + rc = 0; + } + rc = 0; + } else { + pr_err("%s: RPC write for set g711A mode failed %d\n", + __func__, rc); + } + break; + } + case MVS_MODE_EFR: + case MVS_MODE_FR: + case MVS_MODE_HR: { + struct audio_mvs_set_efr_mode_msg set_efr_mode_msg; + + /* Set G729 mode. */ + memset(&set_efr_mode_msg, 0, sizeof(set_efr_mode_msg)); + set_efr_mode_msg.efr_mode = cpu_to_be32(audio->dtx_mode); + + pr_debug("%s: mode of EFR, FR and HR:%d\n", + __func__, set_efr_mode_msg.efr_mode); + + msm_rpc_setup_req(&set_efr_mode_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_GSM_SET_DTX_MODE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &set_efr_mode_msg, + sizeof(set_efr_mode_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for set EFR, FR and HR mode done\n", + __func__); + + /* Save the MVS configuration information. */ + if ((audio->mvs_mode == MVS_MODE_EFR) || + (audio->mvs_mode == MVS_MODE_FR)) + audio->frame_mode = MVS_FRAME_MODE_GSM_DL; + if (audio->mvs_mode == MVS_MODE_HR) + audio->frame_mode = MVS_FRAME_MODE_HR_DL; + + rc = 0; + } else { + pr_err("%s: RPC write for set EFR, FR and HR mode failed %d\n", + __func__, rc); + } + break; + } + default: + rc = -EINVAL; + pr_err("Default case\n"); + } + return rc; +} + +static int audio_mvs_setup(struct audio_mvs_info_type *audio) +{ + int rc = 0; + struct audio_mvs_enable_msg enable_msg; + + pr_debug("%s:\n", __func__); + + /* Enable MVS. */ + memset(&enable_msg, 0, sizeof(enable_msg)); + enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP); + enable_msg.enable_args.mode = cpu_to_be32(audio->mvs_mode); + enable_msg.enable_args.ul_cb_func_id = cpu_to_be32(MVS_UL_CB_FUNC_ID); + enable_msg.enable_args.dl_cb_func_id = cpu_to_be32(MVS_DL_CB_FUNC_ID); + enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR); + + msm_rpc_setup_req(&enable_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_ENABLE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, &enable_msg, sizeof(enable_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for enable done\n", __func__); + + rc = wait_event_timeout(audio->mode_wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 10 * HZ); + + if (rc > 0) { + pr_debug("%s: Wait event for enable succeeded\n", + __func__); + rc = audio_mvs_setup_mode(audio); + if (rc < 0) { + pr_err("%s: Unknown MVS mode %d\n", + __func__, audio->mvs_mode); + } + pr_err("rc value after mode setup: %d\n", rc); + } else { + pr_err("%s: Wait event for enable failed %d\n", + __func__, rc); + } + } else { + pr_err("%s: RPC write for enable failed %d\n", __func__, rc); + } + + return rc; +} + +static int audio_mvs_start(struct audio_mvs_info_type *audio) +{ + int rc = 0; + struct audio_mvs_acquire_msg acquire_msg; + + pr_info("%s:\n", __func__); + + /* Prevent sleep. */ + wake_lock(&audio->suspend_lock); + pm_qos_update_request(&audio->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + /* Acquire MVS. */ + memset(&acquire_msg, 0, sizeof(acquire_msg)); + acquire_msg.acquire_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP); + acquire_msg.acquire_args.cb_func_id = cpu_to_be32(MVS_CB_FUNC_ID); + + msm_rpc_setup_req(&acquire_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_ACQUIRE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, + &acquire_msg, + sizeof(acquire_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for acquire done\n", __func__); + + rc = wait_event_timeout(audio->wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 1 * HZ); + + if (rc > 0) { + + rc = audio_mvs_setup(audio); + + if (rc == 0) + audio->state = AUDIO_MVS_STARTED; + + } else { + pr_err("%s: Wait event for acquire failed %d\n", + __func__, rc); + + rc = -EBUSY; + } + } else { + pr_err("%s: RPC write for acquire failed %d\n", __func__, rc); + + rc = -EBUSY; + } + + return rc; +} + +static int audio_mvs_stop(struct audio_mvs_info_type *audio) +{ + int rc = 0; + struct audio_mvs_release_msg release_msg; + + pr_info("%s:\n", __func__); + + /* Release MVS. */ + memset(&release_msg, 0, sizeof(release_msg)); + release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP); + + msm_rpc_setup_req(&release_msg.rpc_hdr, + audio->rpc_prog, + audio->rpc_ver, + MVS_RELEASE_PROC); + + audio->rpc_status = RPC_STATUS_FAILURE; + rc = msm_rpc_write(audio->rpc_endpt, &release_msg, sizeof(release_msg)); + + if (rc >= 0) { + pr_debug("%s: RPC write for release done\n", __func__); + + rc = wait_event_timeout(audio->mode_wait, + (audio->rpc_status != RPC_STATUS_FAILURE), + 1 * HZ); + + if (rc > 0) { + pr_debug("%s: Wait event for release succeeded\n", + __func__); + + audio->state = AUDIO_MVS_STOPPED; + + /* Un-block read in case it is waiting for data. */ + wake_up(&audio->out_wait); + rc = 0; + } else { + pr_err("%s: Wait event for release failed %d\n", + __func__, rc); + } + } else { + pr_err("%s: RPC write for release failed %d\n", __func__, rc); + } + + /* Allow sleep. */ + pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&audio->suspend_lock); + + return rc; +} + +static void audio_mvs_process_rpc_request(uint32_t procedure, + uint32_t xid, + void *data, + uint32_t length, + struct audio_mvs_info_type *audio) +{ + int rc = 0; + + pr_debug("%s:\n", __func__); + + switch (procedure) { + case MVS_EVENT_CB_TYPE_PROC: { + struct audio_mvs_cb_func_args *args = data; + struct rpc_reply_hdr reply_hdr; + + pr_debug("%s: MVS CB CB_FUNC_ID 0x%x\n", + __func__, be32_to_cpu(args->cb_func_id)); + + if (be32_to_cpu(args->valid_ptr)) { + uint32_t event_type = be32_to_cpu(args->event); + + pr_debug("%s: MVS CB event type %d\n", + __func__, be32_to_cpu(args->event)); + + if (event_type == AUDIO_MVS_COMMAND) { + uint32_t cmd_status = be32_to_cpu( + args->event_data.mvs_ev_command_type.cmd_status); + + pr_debug("%s: MVS CB command status %d\n", + __func__, cmd_status); + + if (cmd_status == AUDIO_MVS_CMD_SUCCESS) { + audio->rpc_status = RPC_STATUS_SUCCESS; + wake_up(&audio->wait); + } + + } else if (event_type == AUDIO_MVS_MODE) { + uint32_t mode_status = be32_to_cpu( + args->event_data.mvs_ev_mode_type.mode_status); + + pr_debug("%s: MVS CB mode status %d\n", + __func__, mode_status); + + if (mode_status == AUDIO_MVS_MODE_READY) { + audio->rpc_status = RPC_STATUS_SUCCESS; + wake_up(&audio->mode_wait); + } + } else { + pr_err("%s: MVS CB unknown event type %d\n", + __func__, event_type); + } + } else { + pr_err("%s: MVS CB event pointer not valid\n", + __func__); + } + + /* Send ack to modem. */ + memset(&reply_hdr, 0, sizeof(reply_hdr)); + reply_hdr.xid = cpu_to_be32(xid); + reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY); + reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32( + RPC_ACCEPTSTAT_SUCCESS); + reply_hdr.data.acc_hdr.verf_flavor = 0; + reply_hdr.data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(audio->rpc_endpt, + &reply_hdr, + sizeof(reply_hdr)); + + if (rc < 0) + pr_err("%s: RPC write for response failed %d\n", + __func__, rc); + + break; + } + + case MVS_PACKET_UL_FN_TYPE_PROC: { + uint32_t *args = data; + uint32_t pkt_len; + uint32_t frame_mode; + struct audio_mvs_ul_reply ul_reply; + struct audio_mvs_buf_node *buf_node = NULL; + + pr_debug("%s: MVS UL CB_FUNC_ID 0x%x\n", + __func__, be32_to_cpu(*args)); + args++; + + pkt_len = be32_to_cpu(*args); + pr_debug("%s: UL pkt_len %d\n", __func__, pkt_len); + args++; + + /* Copy the vocoder packets. */ + mutex_lock(&audio->out_lock); + + if (!list_empty(&audio->free_out_queue)) { + buf_node = list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + memcpy(&buf_node->frame.voc_pkt[0], args, pkt_len); + buf_node->frame.len = pkt_len; + pkt_len = ALIGN(pkt_len, 4); + args = args + pkt_len/4; + + pr_debug("%s: UL valid_ptr 0x%x\n", + __func__, be32_to_cpu(*args)); + args++; + + frame_mode = be32_to_cpu(*args); + pr_debug("%s: UL frame_mode %d\n", + __func__, frame_mode); + args++; + + pr_debug("%s: UL frame_mode %d\n", + __func__, be32_to_cpu(*args)); + args++; + + pr_debug("%s: UL frame_mode %d\n", + __func__, be32_to_cpu(*args)); + args++; + + pr_debug("%s: UL mvs_mode %d\n", + __func__, be32_to_cpu(*args)); + args++; + + pr_debug("%s: UL buf_free_cnt %d\n", + __func__, be32_to_cpu(*args)); + args++; + + if (frame_mode == MVS_FRAME_MODE_AMR_UL) { + /* Extract AMR frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL AMR frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_PCM_UL) { + /* PCM don't have frame_type */ + buf_node->frame.frame_type = 0; + } else if (frame_mode == MVS_FRAME_MODE_VOC_TX) { + /* Extracting EVRC current buffer frame rate*/ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL EVRC frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G711_UL) { + /* Extract G711 frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL G711 frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G729A_UL) { + /* Extract G729 frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL G729 frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G722_UL) { + /* Extract G722 frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL G722 frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else if (frame_mode == MVS_FRAME_MODE_G711A_UL) { + /* Extract G711A frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL G711A frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else if ((frame_mode == MVS_FRAME_MODE_GSM_UL) || + (frame_mode == MVS_FRAME_MODE_HR_UL)) { + /* Extract EFR, FR and HR frame type. */ + buf_node->frame.frame_type = be32_to_cpu(*args); + + pr_debug("%s: UL EFR,FR,HR frame_type %d\n", + __func__, be32_to_cpu(*args)); + } else { + pr_debug("%s: UL Unknown frame mode %d\n", + __func__, frame_mode); + } + + list_add_tail(&buf_node->list, &audio->out_queue); + } else { + pr_err("%s: UL data dropped, read is slow\n", __func__); + } + + mutex_unlock(&audio->out_lock); + + wake_up(&audio->out_wait); + + /* Send UL message accept to modem. */ + memset(&ul_reply, 0, sizeof(ul_reply)); + ul_reply.reply_hdr.xid = cpu_to_be32(xid); + ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY); + ul_reply.reply_hdr.reply_stat = cpu_to_be32( + RPCMSG_REPLYSTAT_ACCEPTED); + + ul_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32( + RPC_ACCEPTSTAT_SUCCESS); + ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0; + ul_reply.reply_hdr.data.acc_hdr.verf_length = 0; + + ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001); + ul_reply.pkt_status = cpu_to_be32(0x00000000); + + rc = msm_rpc_write(audio->rpc_endpt, + &ul_reply, + sizeof(ul_reply)); + + if (rc < 0) + pr_err("%s: RPC write for UL response failed %d\n", + __func__, rc); + + break; + } + + case MVS_PACKET_DL_FN_TYPE_PROC: { + struct audio_mvs_dl_cb_func_args *args = data; + struct audio_mvs_dl_reply dl_reply; + uint32_t frame_mode; + struct audio_mvs_buf_node *buf_node = NULL; + + pr_debug("%s: MVS DL CB CB_FUNC_ID 0x%x\n", + __func__, be32_to_cpu(args->cb_func_id)); + + frame_mode = be32_to_cpu(args->frame_mode); + pr_debug("%s: DL frame_mode %d\n", __func__, frame_mode); + + /* Prepare and send the DL packets to modem. */ + memset(&dl_reply, 0, sizeof(dl_reply)); + dl_reply.reply_hdr.xid = cpu_to_be32(xid); + dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY); + dl_reply.reply_hdr.reply_stat = cpu_to_be32( + RPCMSG_REPLYSTAT_ACCEPTED); + + dl_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32( + RPC_ACCEPTSTAT_SUCCESS); + dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0; + dl_reply.reply_hdr.data.acc_hdr.verf_length = 0; + + mutex_lock(&audio->in_lock); + + if (!list_empty(&audio->in_queue)) { + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + memcpy(&dl_reply.voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + pr_debug("%s:frame mode %d\n", __func__, frame_mode); + if (frame_mode == MVS_FRAME_MODE_AMR_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_PCM_DL) { + dl_reply.cdc_param.gnr_arg.param1 = 0; + dl_reply.cdc_param.gnr_arg.param2 = 0; + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_VOC_RX) { + dl_reply.cdc_param.gnr_arg.param1 = + cpu_to_be32(buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = 0; + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G711_DL) { + dl_reply.cdc_param.g711_arg.param1 = + cpu_to_be32(buf_node->frame.frame_type); + dl_reply.cdc_param.\ + g711_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.g711_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G729A_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G722_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if (frame_mode == MVS_FRAME_MODE_G711A_DL) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else if ((frame_mode == MVS_FRAME_MODE_GSM_DL) || + (frame_mode == MVS_FRAME_MODE_HR_DL)) { + dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32( + buf_node->frame.frame_type); + dl_reply.cdc_param.gnr_arg.param2 = + cpu_to_be32(audio->rate_type); + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_NORMAL); + } else { + pr_err("%s: DL Unknown frame mode %d\n", + __func__, frame_mode); + } + list_add_tail(&buf_node->list, &audio->free_in_queue); + } else { + pr_debug("%s: No DL data available to send to MVS\n", + __func__); + if (frame_mode == MVS_FRAME_MODE_G711_DL) { + dl_reply.cdc_param.\ + g711_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.g711_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_SLOW); + } else { + dl_reply.cdc_param.\ + gnr_arg.valid_pkt_status_ptr = + cpu_to_be32(0x00000001); + dl_reply.cdc_param.gnr_arg.pkt_status = + cpu_to_be32(AUDIO_MVS_PKT_SLOW); + } + } + + mutex_unlock(&audio->in_lock); + + dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001); + + dl_reply.frame_mode = cpu_to_be32(audio->frame_mode); + dl_reply.frame_mode_again = cpu_to_be32(audio->frame_mode); + + dl_reply.frame_info_hdr.frame_mode = + cpu_to_be32(audio->frame_mode); + dl_reply.frame_info_hdr.mvs_mode = cpu_to_be32(audio->mvs_mode); + dl_reply.frame_info_hdr.buf_free_cnt = 0; + + rc = msm_rpc_write(audio->rpc_endpt, + &dl_reply, + sizeof(dl_reply)); + + if (rc < 0) + pr_err("%s: RPC write for DL response failed %d\n", + __func__, rc); + + break; + } + + default: + pr_err("%s: Unknown CB type %d\n", __func__, procedure); + } +} + +static int audio_mvs_thread(void *data) +{ + struct audio_mvs_info_type *audio = data; + struct rpc_request_hdr *rpc_hdr = NULL; + + pr_info("%s:\n", __func__); + + while (!kthread_should_stop()) { + + int rpc_hdr_len = msm_rpc_read(audio->rpc_endpt, + (void **) &rpc_hdr, + -1, + -1); + + if (rpc_hdr_len < 0) { + pr_err("%s: RPC read failed %d\n", + __func__, rpc_hdr_len); + + break; + } else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) { + continue; + } else { + uint32_t rpc_type = be32_to_cpu(rpc_hdr->type); + if (rpc_type == RPC_TYPE_REPLY) { + struct rpc_reply_hdr *rpc_reply = + (void *) rpc_hdr; + uint32_t reply_status; + + if (rpc_hdr_len < RPC_REPLY_HDR_SZ) + continue; + + reply_status = + be32_to_cpu(rpc_reply->reply_stat); + + if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) { + /* If the command is not accepted, there + * will be no response callback. Wake + * the caller and report error. */ + audio->rpc_status = RPC_STATUS_REJECT; + + wake_up(&audio->wait); + + pr_err("%s: RPC reply status denied\n", + __func__); + } + } else if (rpc_type == RPC_TYPE_REQUEST) { + if (rpc_hdr_len < RPC_REQUEST_HDR_SZ) + continue; + + audio_mvs_process_rpc_request( + be32_to_cpu(rpc_hdr->procedure), + be32_to_cpu(rpc_hdr->xid), + (void *) (rpc_hdr + 1), + (rpc_hdr_len - sizeof(*rpc_hdr)), + audio); + } else { + pr_err("%s: Unexpected RPC type %d\n", + __func__, rpc_type); + } + } + + kfree(rpc_hdr); + rpc_hdr = NULL; + } + + pr_info("%s: MVS thread stopped\n", __func__); + + return 0; +} + +static int audio_mvs_alloc_buf(struct audio_mvs_info_type *audio) +{ + int i = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + + pr_debug("%s:\n", __func__); + + /* Allocate input buffers. */ + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = kmalloc(sizeof(struct audio_mvs_buf_node), + GFP_KERNEL); + + if (buf_node != NULL) { + list_add_tail(&buf_node->list, + &audio->free_in_queue); + } else { + pr_err("%s: No memory for IO buffers\n", + __func__); + goto err; + } + buf_node = NULL; + } + + /* Allocate output buffers. */ + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = kmalloc(sizeof(struct audio_mvs_buf_node), + GFP_KERNEL); + + if (buf_node != NULL) { + list_add_tail(&buf_node->list, + &audio->free_out_queue); + } else { + pr_err("%s: No memory for IO buffers\n", + __func__); + goto err; + } + buf_node = NULL; + } + + return 0; + +err: + list_for_each_safe(ptr, next, &audio->free_in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + ptr = next = NULL; + list_for_each_safe(ptr, next, &audio->free_out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + return -ENOMEM; +} + +static void audio_mvs_free_buf(struct audio_mvs_info_type *audio) +{ + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct audio_mvs_buf_node *buf_node = NULL; + + pr_debug("%s:\n", __func__); + + mutex_lock(&audio->in_lock); + /* Free input buffers. */ + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + ptr = next = NULL; + /* Free free_input buffers. */ + list_for_each_safe(ptr, next, &audio->free_in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + mutex_unlock(&audio->in_lock); + + mutex_lock(&audio->out_lock); + ptr = next = NULL; + /* Free output buffers. */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + + /* Free free_ioutput buffers. */ + ptr = next = NULL; + list_for_each_safe(ptr, next, &audio->free_out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + kfree(buf_node); + buf_node = NULL; + } + mutex_unlock(&audio->out_lock); +} + +static int audio_mvs_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + pr_info("%s:\n", __func__); + + mutex_lock(&audio_mvs_info.lock); + + if (audio_mvs_info.state == AUDIO_MVS_CLOSED) { + + if (audio_mvs_info.task != NULL || + audio_mvs_info.rpc_endpt != NULL) { + rc = audio_mvs_alloc_buf(&audio_mvs_info); + + if (rc == 0) { + audio_mvs_info.state = AUDIO_MVS_OPENED; + file->private_data = &audio_mvs_info; + } + } else { + pr_err("%s: MVS thread and RPC end point do not exist\n", + __func__); + + rc = -ENODEV; + } + } else { + pr_err("%s: MVS driver exists, state %d\n", + __func__, audio_mvs_info.state); + + rc = -EBUSY; + } + + mutex_unlock(&audio_mvs_info.lock); + + return rc; +} + +static int audio_mvs_release(struct inode *inode, struct file *file) +{ + + struct audio_mvs_info_type *audio = file->private_data; + + pr_info("%s:\n", __func__); + + mutex_lock(&audio->lock); + if (audio->state == AUDIO_MVS_STARTED) + audio_mvs_stop(audio); + audio_mvs_free_buf(audio); + audio->state = AUDIO_MVS_CLOSED; + mutex_unlock(&audio->lock); + + pr_debug("%s: Release done\n", __func__); + return 0; +} + +static ssize_t audio_mvs_read(struct file *file, + char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_debug("%s:\n", __func__); + + rc = wait_event_interruptible_timeout(audio->out_wait, + (!list_empty(&audio->out_queue) || + audio->state == AUDIO_MVS_STOPPED), + 1 * HZ); + + if (rc > 0) { + mutex_lock(&audio->out_lock); + if ((audio->state == AUDIO_MVS_STARTED) && + (!list_empty(&audio->out_queue))) { + + if (count >= sizeof(struct q5v2_msm_audio_mvs_frame)) { + buf_node = list_first_entry(&audio->out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_to_user(buf, + &buf_node->frame, + sizeof(struct q5v2_msm_audio_mvs_frame) + ); + + if (rc == 0) { + rc = buf_node->frame.len + + sizeof(buf_node->frame.frame_type) + + sizeof(buf_node->frame.len); + } else { + pr_err("%s: Copy to user retuned %d", + __func__, rc); + + rc = -EFAULT; + } + + list_add_tail(&buf_node->list, + &audio->free_out_queue); + } else { + pr_err("%s: Read count %d < sizeof(frame) %d", + __func__, count, + sizeof(struct q5v2_msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + pr_err("%s: Read performed in state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + mutex_unlock(&audio->out_lock); + + } else if (rc == 0) { + pr_err("%s: No UL data available\n", __func__); + + rc = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + + rc = -ERESTARTSYS; + } + + return rc; +} + +static ssize_t audio_mvs_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_debug("%s:\n", __func__); + + mutex_lock(&audio->in_lock); + if (audio->state == AUDIO_MVS_STARTED) { + if (count <= sizeof(struct q5v2_msm_audio_mvs_frame)) { + if (!list_empty(&audio->free_in_queue)) { + buf_node = + list_first_entry(&audio->free_in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_from_user(&buf_node->frame, + buf, + count); + + list_add_tail(&buf_node->list, + &audio->in_queue); + } else { + pr_err("%s: No free DL buffs\n", __func__); + } + } else { + pr_err("%s: Write count %d < sizeof(frame) %d", + __func__, count, + sizeof(struct q5v2_msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + pr_err("%s: Write performed in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + mutex_unlock(&audio->in_lock); + + return rc; +} + +static long audio_mvs_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + + struct audio_mvs_info_type *audio = file->private_data; + + pr_info("%s:\n", __func__); + + switch (cmd) { + case AUDIO_GET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + pr_debug("%s: IOCTL GET_MVS_CONFIG\n", __func__); + + mutex_lock(&audio->lock); + config.mvs_mode = audio->mvs_mode; + config.rate_type = audio->rate_type; + mutex_unlock(&audio->lock); + + rc = copy_to_user((void *)arg, &config, sizeof(config)); + if (rc == 0) + rc = sizeof(config); + else + pr_err("%s: Config copy failed %d\n", __func__, rc); + + break; + } + + case AUDIO_SET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + pr_debug("%s: IOCTL SET_MVS_CONFIG\n", __func__); + + rc = copy_from_user(&config, (void *)arg, sizeof(config)); + if (rc == 0) { + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_OPENED) { + audio->mvs_mode = config.mvs_mode; + audio->rate_type = config.rate_type; + audio->dtx_mode = config.dtx_mode; + } else { + pr_err("%s: Set confg called in state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + } else { + pr_err("%s: Config copy failed %d\n", __func__, rc); + } + + break; + } + + case AUDIO_START: { + pr_debug("%s: IOCTL START\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_OPENED || + audio->state == AUDIO_MVS_STOPPED) { + rc = audio_mvs_start(audio); + + if (rc != 0) + audio_mvs_stop(audio); + } else { + pr_err("%s: Start called in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + + break; + } + + case AUDIO_STOP: { + pr_debug("%s: IOCTL STOP\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STARTED) { + rc = audio_mvs_stop(audio); + } else { + pr_err("%s: Stop called in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + break; + } + + default: { + pr_err("%s: Unknown IOCTL %d\n", __func__, cmd); + } + } + + return rc; +} + +static const struct file_operations audio_mvs_fops = { + .owner = THIS_MODULE, + .open = audio_mvs_open, + .release = audio_mvs_release, + .read = audio_mvs_read, + .write = audio_mvs_write, + .unlocked_ioctl = audio_mvs_ioctl +}; + +struct miscdevice audio_mvs_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mvs", + .fops = &audio_mvs_fops +}; + +static int __init audio_mvs_init(void) +{ + int rc; + + pr_info("%s:\n", __func__); + + memset(&audio_mvs_info, 0, sizeof(audio_mvs_info)); + mutex_init(&audio_mvs_info.lock); + mutex_init(&audio_mvs_info.in_lock); + mutex_init(&audio_mvs_info.out_lock); + + init_waitqueue_head(&audio_mvs_info.wait); + init_waitqueue_head(&audio_mvs_info.mode_wait); + init_waitqueue_head(&audio_mvs_info.out_wait); + + INIT_LIST_HEAD(&audio_mvs_info.in_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_in_queue); + INIT_LIST_HEAD(&audio_mvs_info.out_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_out_queue); + + wake_lock_init(&audio_mvs_info.suspend_lock, + WAKE_LOCK_SUSPEND, + "audio_mvs_suspend"); + pm_qos_add_request(&audio_mvs_info.pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG, + MVS_VERS_COMP_VER5, + MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(audio_mvs_info.rpc_endpt)) { + pr_err("%s: MVS RPC connect failed ver 0x%x\n", __func__, + MVS_VERS_COMP_VER5); + audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG, + MVS_VERS_COMP_VER4, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(audio_mvs_info.rpc_endpt)) { + pr_err("%s: MVS RPC connect failed ver 0x%x\n", + __func__, MVS_VERS_COMP_VER4); + audio_mvs_info.rpc_endpt = + msm_rpc_connect_compatible(MVS_PROG, + MVS_VERS, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(audio_mvs_info.rpc_endpt)) { + pr_err("%s: MVS RPC connect failed ver 0x%x\n", + __func__, MVS_VERS); + rc = PTR_ERR(audio_mvs_info.rpc_endpt); + audio_mvs_info.rpc_endpt = NULL; + goto done; + } else { + pr_debug("%s: MVS RPC connect succeeded ver\ + 0x%x\n", __func__, MVS_VERS); + audio_mvs_info.rpc_prog = MVS_PROG; + audio_mvs_info.rpc_ver = MVS_VERS; + } + } else { + pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n", + __func__, MVS_VERS_COMP_VER4); + audio_mvs_info.rpc_prog = MVS_PROG; + audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER4; + } + } else { + pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n", __func__, + MVS_VERS_COMP_VER5); + audio_mvs_info.rpc_prog = MVS_PROG; + audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER5; + } + audio_mvs_info.task = kthread_run(audio_mvs_thread, + &audio_mvs_info, + "audio_mvs"); + if (IS_ERR(audio_mvs_info.task)) { + pr_err("%s: MVS thread create failed\n", __func__); + rc = PTR_ERR(audio_mvs_info.task); + audio_mvs_info.task = NULL; + msm_rpc_close(audio_mvs_info.rpc_endpt); + audio_mvs_info.rpc_endpt = NULL; + goto done; + } + + rc = misc_register(&audio_mvs_misc); +done: + return rc; +} + +static void __exit audio_mvs_exit(void) +{ + pr_info("%s:\n", __func__); + + misc_deregister(&audio_mvs_misc); +} + +module_init(audio_mvs_init); +module_exit(audio_mvs_exit); + +MODULE_DESCRIPTION("MSM MVS driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c new file mode 100644 index 00000000000..147ac7761dc --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c @@ -0,0 +1,730 @@ +/* + * pcm audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define BUFSZ (960 * 5) +#define DMASZ (BUFSZ * 2) + +#define HOSTPCM_STREAM_ID 5 + +struct buffer { + void *data; + unsigned size; + unsigned used; + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t wait; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_weight; + uint32_t out_buffer_size; + uint32_t device_events; + int16_t source; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_write; + int teos; /* valid only if tunnel mode & no data left for decoder */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + uint16_t dec_id; + int voice_state; + + struct wake_lock wakelock; + struct pm_qos_request pm_qos_req; + + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static void audio_out_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + case AUDDEV_EVT_VOICE_STATE_CHG: + MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n", + evt_payload->voice_state); + audio->voice_state = evt_payload->voice_state; + /* Voice uplink Rx case */ + if (audio->running && + (audio->source & AUDPP_MIXER_UPLINK_RX) && + (audio->voice_state == VOICE_STATE_OFFCALL)) { + MM_DBG("Voice is terminated, Wake up write: %x %x\n", + audio->voice_state, audio->source); + wake_up(&audio->wait); + } + break; + default: + MM_ERR("ERROR:wrong event\n"); + break; + } +} + +static void audio_prevent_sleep(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + wake_lock(&audio->wakelock); + pm_qos_update_request(&audio->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); +} + +static void audio_allow_sleep(struct audio *audio) +{ + pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&audio->wakelock); + MM_DBG("\n"); /* Macro prints the file name and function */ +} + +static int audio_dsp_out_enable(struct audio *audio, int yes); +static int audio_dsp_send_buffer(struct audio *audio, unsigned id, + unsigned len); + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + /* refuse to start if we're not ready */ + if (!audio->out[0].used || !audio->out[1].used) + return -EIO; + + /* we start buffers 0 and 1, so buffer 0 will be the + * next one the dsp will want + */ + audio->out_tail = 0; + audio->out_needed = 0; + + audio_prevent_sleep(audio); + + if (audpp_enable(-1, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + audio_allow_sleep(audio); + return -ENODEV; + } + + audio->enabled = 1; + htc_pwrsink_set(PWRSINK_AUDIO, 100); + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio_dsp_out_enable(audio, 0); + + audpp_disable(-1, audio); + + wake_up(&audio->wait); + audio->out_needed = 0; + audio_allow_sleep(audio); + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + struct buffer *frame; + unsigned long flags; + static unsigned long pcmdmamsd_time; + + switch (id) { + case AUDPP_MSG_HOST_PCM_INTF_MSG: { + unsigned id = msg[3]; + unsigned idx = msg[4] - 1; + + MM_DBG("HOST_PCM id %d idx %d\n", id, idx); + if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) { + MM_ERR("bogus id\n"); + break; + } + if (idx > 1) { + MM_ERR("bogus buffer idx\n"); + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->running) { + atomic_add(audio->out[idx].used, &audio->out_bytes); + audio->out[idx].used = 0; + frame = audio->out + audio->out_tail; + if (frame->used) { + /* Reset teos flag to avoid stale + * PCMDMAMISS been considered + */ + audio->teos = 0; + audio_dsp_send_buffer( + audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + } else { + audio->out_needed++; + } + wake_up(&audio->wait); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + break; + } + case AUDPP_MSG_PCMDMAMISSED: + /* prints only if 1 second is elapsed since the last time + * this message has been printed */ + if (printk_timed_ratelimit(&pcmdmamsd_time, 1000)) + printk(KERN_INFO "[%s:%s] PCMDMAMISSED %d\n", + __MM_FILE__, __func__, msg[0]); + audio->teos++; + MM_DBG("PCMDMAMISSED Count per Buffer %d\n", audio->teos); + wake_up(&audio->wait); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_route_stream(audio->dec_id, audio->source); + audio_dsp_out_enable(audio, 1); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_ERR("CFG_MSG %d?\n", msg[0]); + } + break; + default: + MM_ERR("UNKNOWN (%d)\n", id); + } +} + +static int audio_dsp_out_enable(struct audio *audio, int yes) +{ + struct audpp_cmd_pcm_intf cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_PCM_INTF; + cmd.stream = AUDPP_CMD_POPP_STREAM; + cmd.stream_id = audio->dec_id; + cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + + if (yes) { + cmd.write_buf1LSW = audio->out[0].addr; + cmd.write_buf1MSW = audio->out[0].addr >> 16; + if (audio->out[0].used) + cmd.write_buf1_len = audio->out[0].used; + else + cmd.write_buf1_len = audio->out[0].size; + cmd.write_buf2LSW = audio->out[1].addr; + cmd.write_buf2MSW = audio->out[1].addr >> 16; + if (audio->out[1].used) + cmd.write_buf2_len = audio->out[1].used; + else + cmd.write_buf2_len = audio->out[1].size; + cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V; + cmd.weight_decoder_to_rx = audio->out_weight; + cmd.weight_arm_to_rx = 1; + cmd.partition_number_arm_to_dsp = 0; + cmd.sample_rate = audio->out_sample_rate; + cmd.channel_mode = audio->out_channel_mode; + } + + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, + unsigned len) +{ + struct audpp_cmd_pcm_intf_send_buffer cmd; + + cmd.cmd_id = AUDPP_CMD_PCM_INTF; + cmd.stream = AUDPP_CMD_POPP_STREAM; + cmd.stream_id = audio->dec_id; + cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + cmd.dsp_to_arm_buf_id = 0; + cmd.arm_to_dsp_buf_id = idx + 1; + cmd.arm_to_dsp_buf_len = len; + + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->out_bytes); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + if ((audio->voice_state != VOICE_STATE_INCALL) + && (audio->source & AUDPP_MIXER_UPLINK_RX)) { + MM_ERR("Unable to Start : state %d source %d\n", + audio->voice_state, audio->source); + rc = -EPERM; + break; + } + rc = audio_enable(audio); + break; + case AUDIO_STOP: + rc = audio_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + else if (config.channel_count == 2) + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + return -EFAULT; + rc = 0; + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + if (!audio->running) + return -EINVAL; + + mutex_lock(&audio->write_lock); + + /* PCM DMAMISS message is sent only once in + * hpcm interface. So, wait for buffer complete + * and teos flag. + */ + rc = wait_event_interruptible(audio->wait, + (!audio->out[0].used && + !audio->out[1].used)); + + if (rc < 0) + goto done; + + rc = wait_event_interruptible(audio->wait, + audio->teos); +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static inline int rt_policy(int policy) +{ + if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR)) + return 1; + return 0; +} + +static inline int task_has_rt_policy(struct task_struct *p) +{ + return rt_policy(p->policy); +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct sched_param s = { .sched_priority = 1 }; + struct audio *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int old_prio = current->rt_priority; + int old_policy = current->policy; + int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE); + int rc = 0; + + + if ((audio->voice_state == VOICE_STATE_OFFCALL) + && (audio->source & AUDPP_MIXER_UPLINK_RX) && + audio->running) { + MM_ERR("Not Permitted Voice Terminated: state %d source %x \ + running %d\n", + audio->voice_state, audio->source, audio->running); + return -EPERM; + } + /* just for this write, set us real-time */ + if (!task_has_rt_policy(current)) { + struct cred *new = prepare_creds(); + cap_raise(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + if ((sched_setscheduler(current, SCHED_RR, &s)) < 0) + MM_ERR("sched_setscheduler failed\n"); + } + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->wait, + (frame->used == 0) || (audio->stopped) || + ((audio->voice_state == VOICE_STATE_OFFCALL) && + (audio->source & AUDPP_MIXER_UPLINK_RX))); + + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } else if ((audio->voice_state == VOICE_STATE_OFFCALL) && + (audio->source & AUDPP_MIXER_UPLINK_RX)) { + MM_ERR("Not Permitted Voice Terminated: %d\n", + audio->voice_state); + rc = -EPERM; + break; + } + + xfer = count > frame->size ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + spin_lock_irqsave(&audio->dsp_lock, flags); + frame = audio->out + audio->out_tail; + if (frame->used && audio->out_needed) { + /* Reset teos flag to avoid stale + * PCMDMAMISS been considered + */ + audio->teos = 0; + audio_dsp_send_buffer(audio, audio->out_tail, + frame->used); + audio->out_tail ^= 1; + audio->out_needed--; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + mutex_unlock(&audio->write_lock); + + /* restore scheduling policy and priority */ + if (!rt_policy(old_policy)) { + struct sched_param v = { .sched_priority = old_prio }; + if ((sched_setscheduler(current, old_policy, &v)) < 0) + MM_ERR("sched_setscheduler failed\n"); + if (likely(!cap_nice)) { + struct cred *new = prepare_creds(); + cap_lower(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + } + } + + if (buf > start) + return buf - start; + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio_flush(audio); + audio->opened = 0; + mutex_unlock(&audio->lock); + htc_pwrsink_set(PWRSINK_AUDIO, 0); + return 0; +} + +static struct audio the_audio; + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + MM_ERR("busy\n"); + rc = -EBUSY; + goto done; + } + + + audio->dec_id = HOSTPCM_STREAM_ID; + + audio->out_buffer_size = BUFSZ; + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_weight = 100; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + audio->vol_pan.pan = 0x0; + audio->source = 0x0; + + audio_flush(audio); + audio->voice_state = msm_get_voice_state(); + MM_DBG("voice_state = %x\n", audio->voice_state); + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG| + AUDDEV_EVT_VOICE_STATE_CHG; + + MM_DBG("register for event callback pdata %p\n", audio); + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + audio_out_listener, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listener\n", __func__); + goto done; + } + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +struct miscdevice audio_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &audio_fops, +}; + +static int __init audio_init(void) +{ + the_audio.phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (the_audio.phys) { + the_audio.map_v_write = ioremap(the_audio.phys, DMASZ); + if (IS_ERR(the_audio.map_v_write)) { + MM_ERR("could not map physical buffers\n"); + free_contiguous_memory_by_paddr(the_audio.phys); + return -ENOMEM; + } + the_audio.data = the_audio.map_v_write; + } else { + MM_ERR("could not allocate physical buffers\n"); + return -ENOMEM; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) the_audio.data, (int) the_audio.phys); + mutex_init(&the_audio.lock); + mutex_init(&the_audio.write_lock); + spin_lock_init(&the_audio.dsp_lock); + init_waitqueue_head(&the_audio.wait); + wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm"); + pm_qos_add_request(&the_audio.pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + return misc_register(&audio_misc); +} + +late_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm.c new file mode 100644 index 00000000000..4b308b05a4b --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm.c @@ -0,0 +1,1707 @@ +/* arch/arm/mach-msm/qdsp5v2/audio_pcm.c + * + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADRV_STATUS_AIO_INTF 0x00000001 +#define ADRV_STATUS_OBUF_GIVEN 0x00000002 +#define ADRV_STATUS_IBUF_GIVEN 0x00000004 +#define ADRV_STATUS_FSYNC 0x00000008 + +/* Size must be power of 2 */ +#define BUFSZ_MAX 32768 +#define BUFSZ_MIN 4096 +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDDEC_DEC_PCM 0 + +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDPCM_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct audio; + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audpcm_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audpcm_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audpcm_pmem_region { + struct list_head list; + struct file *file; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audpcm_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audpcm_drv_operations { + void (*send_data)(struct audio *, unsigned); + void (*out_flush)(struct audio *); + int (*fsync)(struct audio *); +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + struct list_head out_queue; /* queue to retain output buffers */ + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_bits; /* bits per sample */ + + /* data allocated for various buffers */ + char *data; + int32_t phys; + void *map_v_write; + uint32_t drv_status; + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint32_t device_events; + + unsigned volume; + + uint16_t dec_id; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audpcm_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + struct list_head pmem_region_queue; + struct audpcm_drv_operations drv_ops; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); + +static void pcm_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->volume = evt_payload->session_vol; + MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->volume); + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0, POPP); + break; + default: + MM_ERR("ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audio->drv_ops.send_data(audio, 1); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event:module audplaytask\n"); + break; + + default: + MM_ERR("unexpected message from decoder\n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason=0x%04x\n", + reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + audpp_route_stream(audio->dec_id, + audio->source); + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_ERR("audio_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + pr_info("%s: AVSYNC_MSG\n", __func__); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_DBG("audio_dsp_event: UNKNOWN (%d)\n", id); + } + +} + + +struct msm_adsp_ops audpcmdec_adsp_ops = { + .event = audplay_dsp_event, +}; + + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN >> 1; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.stereo_cfg = audio->out_channel_mode; + cmd.pcm_width = audio->out_bits; + cmd.sign = 0; + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audpcm_async_send_data(struct audio *audio, unsigned needed) +{ + unsigned long flags; + + if (!audio->running) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload payload; + struct audpcm_buffer_node *used_buf; + + MM_DBG("consumed\n"); + + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audpcm_buffer_node, list); + list_del(&used_buf->list); + payload.aio_buf = used_buf->buf; + audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + } + if (audio->out_needed) { + struct audpcm_buffer_node *next_buf; + struct audplay_cmd_bitstream_data_avail cmd; + if (!list_empty(&audio->out_queue)) { + next_buf = list_first_entry(&audio->out_queue, + struct audpcm_buffer_node, list); + MM_DBG("next_buf %p\n", next_buf); + if (next_buf) { + MM_DBG("next buf phy %lx len %d\n", + next_buf->paddr, next_buf->buf.data_len); + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + if (next_buf->buf.data_len) + cmd.decoder_id = audio->dec_id; + else { + cmd.decoder_id = -1; + MM_DBG("input EOS signaled\n"); + } + cmd.buf_ptr = (unsigned) next_buf->paddr; + cmd.buf_size = next_buf->buf.data_len >> 1; + cmd.partition_number = 0; + /* complete writes to the input buffer */ + wmb(); + audplay_send_queue0(audio, &cmd, sizeof(cmd)); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + if (!audio->running) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ +static void audpcm_async_flush(struct audio *audio) +{ + struct audpcm_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audpcm_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_ioport_reset(struct audio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + MM_DBG("fsync in progress\n"); + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + } else + audio->drv_ops.out_flush(audio); + } else { + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio->drv_ops.out_flush(audio); + mutex_unlock(&audio->write_lock); + } + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audpcm_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audpcm_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audpcm_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audpcm_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audpcm_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audpcm_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audpcm_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audpcm_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt && drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + mutex_lock(&audio->lock); + audpcm_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audpcm_pmem_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audpcm_pmem_region *region_elt; + struct audpcm_pmem_region t = { .vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->pmem_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + MM_ERR("region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audpcm_pmem_add(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct audpcm_pmem_region *region; + int rc = -EINVAL; + + MM_DBG("\n"); /* Macro prints the file name and function */ + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + return -EINVAL; + } + + rc = audpcm_pmem_check(audio, info->vaddr, len); + if (rc < 0) { + put_pmem_file(file); + kfree(region); + return rc; + } + + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + region->ref_cnt = 0; + MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr, + region->vaddr, region->len); + list_add_tail(®ion->list, &audio->pmem_region_queue); + return rc; +} + +static int audpcm_pmem_remove(struct audio *audio, + struct msm_audio_pmem_info *info) +{ + struct audpcm_pmem_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audpcm_pmem_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + MM_DBG("region %p in use ref_cnt %d\n", region, + region->ref_cnt); + break; + } + MM_DBG("remove region fd %d vaddr %p \n", info->fd, + info->vaddr); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audpcm_pmem_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audpcm_pmem_region **region) +{ + struct audpcm_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->pmem_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len); + list_for_each_entry(region_elt, + &audio->pmem_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + MM_ERR("\t%p, %ld --> %p\n", + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audpcm_pmem_region *region; + unsigned long paddr; + int ret; + + ret = audpcm_pmem_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + MM_ERR("lookup (%p, %ld) failed\n", addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audpcm_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audpcm_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len %d\n", + buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + + buf_node->paddr = audpcm_pmem_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1) || + (!buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + audio->drv_ops.send_data(audio, 0); + } + + MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr); + + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audpcm_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + if (config.bits == 8) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8; + else if (config.bits == 16) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + else if (config.bits == 24) + config.bits = AUDPP_CMD_WAV_PCM_WIDTH_24; + else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_bits = config.bits; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8) + config.bits = 8; + else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_24) + config.bits = 24; + else + config.bits = 16; + config.unused[0] = 0; + config.unused[1] = 0; + + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + + case AUDIO_REGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_REGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audpcm_pmem_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_PMEM: { + struct msm_audio_pmem_info info; + MM_DBG("AUDIO_DEREGISTER_PMEM\n"); + if (copy_from_user(&info, (void *) arg, sizeof(info))) + rc = -EFAULT; + else + rc = audpcm_pmem_remove(audio, &info); + break; + } + + case AUDIO_ASYNC_WRITE: + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audpcm_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_ASYNC_READ: + MM_ERR("AUDIO_ASYNC_READ not supported\n"); + rc = -EPERM; + break; + + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + return -EFAULT; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audpcm_async_fsync(struct audio *audio) +{ + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + (audio->teos && audio->out_needed && + list_empty(&audio->out_queue)) + || audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audpcm_sync_fsync(struct audio *audio) +{ + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audio->drv_ops.send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + return rc; +} + +int audpcm_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + + if (!audio->running) + return -EINVAL; + + return audio->drv_ops.fsync(audio); +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0; + unsigned dsize; + + if (audio->drv_status & ADRV_STATUS_AIO_INTF) + return -EPERM; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > (frame->size - 1)) ? + frame->size - 1 : count; + cpy_ptr++; + dsize = 1; + audio->reserved = 0; + } else + xfer = (count > frame->size) ? frame->size : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audio->drv_ops.send_data(audio, 0); + } + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + + return rc; +} + +static void audpcm_reset_pmem_region(struct audio *audio) +{ + struct audpcm_pmem_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->pmem_region_queue) { + region = list_entry(ptr, struct audpcm_pmem_region, list); + list_del(®ion->list); + put_pmem_file(region->file); + kfree(region); + } + + return; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio->drv_ops.out_flush(audio); + audpcm_reset_pmem_region(audio); + + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audpcm_reset_event_queue(audio); + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audpcm_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audpcm_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audpcm_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audpcm_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audpcm_suspend(struct early_suspend *h) +{ + struct audpcm_suspend_ctl *ctl = + container_of(h, struct audpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audpcm_resume(struct early_suspend *h) +{ + struct audpcm_suspend_ctl *ctl = + container_of(h, struct audpcm_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audpcm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audpcm_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audpcm_debug_fops = { + .read = audpcm_debug_read, + .open = audpcm_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, i, dec_attrb, decid; + struct audpcm_event *e_node = NULL; + unsigned pmem_sz = DMASZ_MAX; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_pcm_dec_" + 5]; +#endif + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_PCM; + if (file->f_mode & FMODE_READ) { + MM_ERR("Non-Tunneled mode not supported\n"); + rc = -EPERM; + kfree(audio); + goto done; + } else + dec_attrb |= MSM_AUD_MODE_TUNNEL; + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + /* AIO interface */ + if (file->f_flags & O_NONBLOCK) { + MM_DBG("set to aio interface\n"); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.send_data = audpcm_async_send_data; + audio->drv_ops.out_flush = audpcm_async_flush; + audio->drv_ops.fsync = audpcm_async_fsync; + } else { + MM_DBG("set to std io interface\n"); + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, + SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap( + audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys\ + address freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x \ + kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers \ + freeing instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + audio->drv_ops.send_data = audplay_send_data; + audio->drv_ops.out_flush = audio_flush; + audio->drv_ops.fsync = audpcm_sync_fsync; + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = (audio->out_dma_sz >> 1); + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audpcmdec_adsp_ops, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->pmem_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16; + audio->volume = 0x7FFF; + audio->drv_ops.out_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + pcm_listner, + (void *)audio); + if (rc) { + MM_ERR("failed to register listnet\n"); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_pcm_dec_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audpcm_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_ERR("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audpcm_resume; + audio->suspend_ctl.node.suspend = audpcm_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDPCM_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audpcm_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + if (audio->data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + } + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_pcm_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audpcm_fsync, +}; + +struct miscdevice audio_pcm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_dec", + .fops = &audio_pcm_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_pcm_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c new file mode 100644 index 00000000000..ce67ebbafdd --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c @@ -0,0 +1,976 @@ +/* + * pcm audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_HEADER_SIZE (8) /*4 half words*/ +/* size of a mono frame with 256 samples */ +#define MONO_DATA_SIZE_256 (512) /* in bytes*/ +/*size of a mono frame with 512 samples */ +#define MONO_DATA_SIZE_512 (1024) /* in bytes*/ +/*size of a mono frame with 1024 samples */ +#define MONO_DATA_SIZE_1024 (2048) /* in bytes */ + +/*size of a stereo frame with 256 samples per channel */ +#define STEREO_DATA_SIZE_256 (1024) /* in bytes*/ +/*size of a stereo frame with 512 samples per channel */ +#define STEREO_DATA_SIZE_512 (2048) /* in bytes*/ +/*size of a stereo frame with 1024 samples per channel */ +#define STEREO_DATA_SIZE_1024 (4096) /* in bytes */ + +#define MAX_FRAME_SIZE ((STEREO_DATA_SIZE_1024) + FRAME_HEADER_SIZE) +#define DMASZ (MAX_FRAME_SIZE * FRAME_NUM) + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + + struct msm_adsp_module *audrec; + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */ + uint32_t enc_type; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + uint32_t mode; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; /* Session Id */ + + uint16_t source; /* Encoding source bit mask */ + uint32_t device_events; /* device events interested in */ + uint32_t in_call; + uint32_t dev_cnt; + int voice_state; + spinlock_t dev_lock; + + struct audrec_session_info session_info; /*audrec session info*/ + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_read; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int abort; /* set when error, like sample rate mismatch */ + int dual_mic_config; + char *build_id; +}; + +static struct audio_in the_audio_in; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +/* Audrec Queue command sent macro's */ +#define audrec_send_bitstreamqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) + +#define audrec_send_audrecqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +/* DSP command send functions */ +static int audpcm_in_enc_config(struct audio_in *audio, int enable); +static int audpcm_in_param_config(struct audio_in *audio); +static int audpcm_in_mem_config(struct audio_in *audio); +static int audpcm_in_record_config(struct audio_in *audio, int enable); +static int audpcm_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); + +static void audpcm_in_get_dsp_frames(struct audio_in *audio); + +static void audpcm_in_flush(struct audio_in *audio); + +static void pcm_in_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio_in *audio = (struct audio_in *) private_data; + unsigned long flags; + + MM_DBG("evt_id = 0x%8x\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: { + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt++; + if (!audio->in_call) + audio->source |= (0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((audio->running == 1) && (audio->enabled == 1)) + audpcm_in_record_config(audio, 1); + + break; + } + case AUDDEV_EVT_DEV_RLS: { + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt--; + if (!audio->in_call) + audio->source &= ~(0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if (!audio->running || !audio->enabled) + break; + + /* Turn of as per source */ + if (audio->source) + audpcm_in_record_config(audio, 1); + else + /* Turn off all */ + audpcm_in_record_config(audio, 0); + + break; + } + case AUDDEV_EVT_VOICE_STATE_CHG: { + MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n", + evt_payload->voice_state); + audio->voice_state = evt_payload->voice_state; + if (audio->in_call && audio->running) { + if (audio->voice_state == VOICE_STATE_INCALL) + audpcm_in_record_config(audio, 1); + else if (audio->voice_state == VOICE_STATE_OFFCALL) { + audpcm_in_record_config(audio, 0); + wake_up(&audio->wait); + } + } + break; + } + case AUDDEV_EVT_FREQ_CHG: { + MM_DBG("Encoder Driver got sample rate change event\n"); + MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate); + MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type); + MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id); + if (audio->running == 1) { + /* Stop Recording sample rate does not match + with device sample rate */ + if (evt_payload->freq_info.sample_rate != + audio->samp_rate) { + audpcm_in_record_config(audio, 0); + audio->abort = 1; + wake_up(&audio->wait); + } + } + break; + } + default: + MM_ERR("wrong event %d\n", evt_id); + break; + } +} + +/* ------------------- dsp preproc event handler--------------------- */ +static void audpreproc_dsp_event(void *data, unsigned id, void *msg) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg *err_msg = msg; + + MM_ERR("ERROR_MSG: stream id %d err idx %d\n", + err_msg->stream_id, err_msg->aud_preproc_err_idx); + /* Error case */ + wake_up(&audio->wait_enable); + break; + } + case AUDPREPROC_CMD_CFG_DONE_MSG: { + MM_DBG("CMD_CFG_DONE_MSG \n"); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg; + + MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + 0x%8x\n", enc_cfg_msg->stream_id, + enc_cfg_msg->rec_enc_type); + /* Encoder enable success */ + if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) + audpcm_in_param_config(audio); + else { /* Encoder disable success */ + audio->running = 0; + audpcm_in_record_config(audio, 0); + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n"); + audpcm_in_mem_config(audio); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n"); + wake_up(&audio->wait_enable); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +/* ------------------- dsp audrec event handler--------------------- */ +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDREC_CMD_MEM_CFG_DONE_MSG: { + MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n"); + audio->running = 1; + if ((!audio->in_call && (audio->dev_cnt > 0)) || + (audio->in_call && + (audio->voice_state == VOICE_STATE_INCALL))) + audpcm_in_record_config(audio, 1); + break; + } + case AUDREC_FATAL_ERR_MSG: { + struct audrec_fatal_err_msg fatal_err_msg; + + getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN); + MM_ERR("FATAL_ERR_MSG: err id %d\n", + fatal_err_msg.audrec_err_id); + /* Error stop the encoder */ + audio->stopped = 1; + wake_up(&audio->wait); + break; + } + case AUDREC_UP_PACKET_READY_MSG: { + struct audrec_up_pkt_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packet_write_cnt_lsw, \ + pkt_ready_msg.audrec_packet_write_cnt_msw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_msw); + + audpcm_in_get_dsp_frames(audio); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event :module audrectask\n"); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +static void audpcm_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + else + audio->in_count++; + + audpcm_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +struct msm_adsp_ops audrec_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audpcm_in_enc_config(struct audio_in *audio, int enable) +{ + struct audpreproc_audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + if (audio->build_id[17] == '1') { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG_2 command"); + } else { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG command"); + } + cmd.stream_id = audio->enc_id; + + if (enable) + cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE; + else + cmd.audrec_enc_type &= ~(ENCODE_ENABLE); + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audpcm_in_param_config(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_parm_cfg_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + + cmd.aud_rec_samplerate_idx = audio->samp_rate; + if (audio->dual_mic_config) + cmd.aud_rec_stereo_mode = DUAL_MIC_STEREO_RECORDING; + else + cmd.aud_rec_stereo_mode = audio->channel_mode; + + if (audio->channel_mode == AUDREC_CMD_MODE_MONO) + cmd.aud_rec_frame_size = audio->buffer_size/2; + else + cmd.aud_rec_frame_size = audio->buffer_size/4; + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +/* To Do: msm_snddev_route_enc(audio->enc_id); */ +static int audpcm_in_record_config(struct audio_in *audio, int enable) +{ + struct audpreproc_afe_cmd_audio_record_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG; + cmd.stream_id = audio->enc_id; + if (enable) + cmd.destination_activity = AUDIO_RECORDING_TURN_ON; + else + cmd.destination_activity = AUDIO_RECORDING_TURN_OFF; + + cmd.source_mix_mask = audio->source; + if (audio->enc_id == 2) { + if ((cmd.source_mix_mask & + INTERNAL_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) { + cmd.pipe_id = SOURCE_PIPE_1; + } + if (cmd.source_mix_mask & + AUDPP_A2DP_PIPE_SOURCE_MIX_MASK) + cmd.pipe_id |= SOURCE_PIPE_0; + } + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audpcm_in_mem_config(struct audio_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD; + cmd.audrec_up_pkt_intm_count = 1; + cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16; + cmd.audrec_ext_pkt_start_addr_lsw = audio->phys; + cmd.audrec_ext_pkt_buf_number = FRAME_NUM; + + /* prepare buffer pointers: + * Mono: 1024 samples + 4 halfword header + * Stereo: 2048 samples + 4 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + /* word increment*/ + audio->in[n].data = data + (FRAME_HEADER_SIZE/2); + data += ((FRAME_HEADER_SIZE/2) + (audio->buffer_size/2)); + MM_DBG("0x%8x\n", (int)(audio->in[n].data - FRAME_HEADER_SIZE)); + } + + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +static int audpcm_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + struct up_audrec_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR; + cmd.audrec_up_curr_read_count_msw = read_cnt >> 16; + cmd.audrec_up_curr_read_count_lsw = read_cnt; + + return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audpcm_in_enable(struct audio_in *audio) +{ + if (audio->enabled) + return 0; + + if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) { + MM_ERR("msm_adsp_enable(audpreproc) failed\n"); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + audpreproc_disable(audio->enc_id, audio); + return -ENODEV; + } + audio->enabled = 1; + audpcm_in_enc_config(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audpcm_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + audpcm_in_enc_config(audio, 0); + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + audpreproc_disable(audio->enc_id, audio); + } + return 0; +} + +static void audpcm_in_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +/* ------------------- device --------------------- */ +static long audpcm_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t freq; + /* Poll at 48KHz always */ + freq = 48000; + MM_DBG("AUDIO_START\n"); + if (audio->in_call && (audio->voice_state != + VOICE_STATE_INCALL)) { + rc = -EPERM; + break; + } + rc = msm_snddev_request_freq(&freq, audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("sample rate configured %d sample rate requested %d\n", + freq, audio->samp_rate); + if (rc < 0) { + MM_DBG("sample rate can not be set, return code %d\n",\ + rc); + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + break; + } + audio->dual_mic_config = msm_get_dual_mic_config(audio->enc_id); + /*DSP supports fluence block and by default ACDB layer will + applies the fluence pre-processing feature, if dual MIC config + is enabled implies client want to record pure dual MIC sample + for this we need to over ride the fluence pre processing + feature at ACDB layer to not to apply if fluence preprocessing + feature supported*/ + if (audio->dual_mic_config) { + MM_INFO("dual MIC config = %d, over ride the fluence " + "feature\n", audio->dual_mic_config); + fluence_feature_update(audio->dual_mic_config, + audio->enc_id); + } + /*update aurec session info in audpreproc layer*/ + audio->session_info.session_id = audio->enc_id; + audio->session_info.sampling_freq = audio->samp_rate; + audpreproc_update_audrec_info(&audio->session_info); + rc = audpcm_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + rc = audpcm_in_disable(audio); + rc = msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + audio->stopped = 1; + audio->abort = 0; + break; + } + case AUDIO_FLUSH: { + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audpcm_in_flush(audio); + mutex_unlock(&audio->read_lock); + } + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (audio->build_id[17] == '1') { + audio->enc_type = ENC_TYPE_EXT_WAV | audio->mode; + if (cfg.channel_count == 1) { + cfg.channel_count = AUDREC_CMD_MODE_MONO; + if ((cfg.buffer_size == MONO_DATA_SIZE_256) || + (cfg.buffer_size == + MONO_DATA_SIZE_512) || + (cfg.buffer_size == + MONO_DATA_SIZE_1024)) { + audio->buffer_size = cfg.buffer_size; + } else { + rc = -EINVAL; + break; + } + } else if (cfg.channel_count == 2) { + cfg.channel_count = AUDREC_CMD_MODE_STEREO; + if ((cfg.buffer_size == + STEREO_DATA_SIZE_256) || + (cfg.buffer_size == + STEREO_DATA_SIZE_512) || + (cfg.buffer_size == + STEREO_DATA_SIZE_1024)) { + audio->buffer_size = cfg.buffer_size; + } else { + rc = -EINVAL; + break; + } + } else { + rc = -EINVAL; + break; + } + } else if (audio->build_id[17] == '0') { + audio->enc_type = ENC_TYPE_WAV | audio->mode; + if (cfg.channel_count == 1) { + cfg.channel_count = AUDREC_CMD_MODE_MONO; + audio->buffer_size = MONO_DATA_SIZE_1024; + } else if (cfg.channel_count == 2) { + cfg.channel_count = AUDREC_CMD_MODE_STEREO; + audio->buffer_size = STEREO_DATA_SIZE_1024; + } + } else { + MM_ERR("wrong build_id = %s\n", audio->build_id); + return -ENODEV; + } + audio->samp_rate = cfg.sample_rate; + audio->channel_mode = cfg.channel_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + cfg.sample_rate = audio->samp_rate; + if (audio->channel_mode == AUDREC_CMD_MODE_MONO) + cfg.channel_count = 1; + else + cfg.channel_count = 2; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_INCALL: { + struct msm_voicerec_mode cfg; + unsigned long flags; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.rec_mode != VOC_REC_BOTH && + cfg.rec_mode != VOC_REC_UPLINK && + cfg.rec_mode != VOC_REC_DOWNLINK) { + MM_ERR("invalid rec_mode\n"); + rc = -EINVAL; + break; + } else { + spin_lock_irqsave(&audio->dev_lock, flags); + if (cfg.rec_mode == VOC_REC_UPLINK) + audio->source = VOICE_UL_SOURCE_MIX_MASK; + else if (cfg.rec_mode == VOC_REC_DOWNLINK) + audio->source = VOICE_DL_SOURCE_MIX_MASK; + else + audio->source = VOICE_DL_SOURCE_MIX_MASK | + VOICE_UL_SOURCE_MIX_MASK ; + audio->in_call = 1; + spin_unlock_irqrestore(&audio->dev_lock, flags); + } + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->enc_id, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audpcm_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->abort || (audio->in_call && audio->running && + (audio->voice_state == VOICE_STATE_OFFCALL))); + if (rc < 0) + break; + + if (!audio->in_count) { + if (audio->stopped) { + MM_DBG("Driver in stop state, No more \ + buffer to read"); + rc = 0;/* End of File */ + break; + } else if (audio->in_call && audio->running && + (audio->voice_state == VOICE_STATE_OFFCALL)) { + MM_DBG("Not Permitted Voice Terminated\n"); + rc = -EPERM; /* Voice Call stopped */ + break; + } + } + + if (audio->abort) { + rc = -EPERM; /* Not permitted due to abort */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + if (count >= size) { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + } else { + MM_ERR("short read count %d\n", count); + break; + } + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static ssize_t audpcm_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int audpcm_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audio->in_call = 0; + /* with draw frequency for session + incase not stopped the driver */ + msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX, + AUDDEV_CLNT_ENC); + auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id); + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + audpcm_in_disable(audio); + audpcm_in_flush(audio); + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->opened = 0; + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +static int audpcm_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + int rc; + int encid; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (audio->phys) { + audio->map_v_read = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map read phys buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto done; + } + audio->data = audio->map_v_read; + } else { + MM_ERR("could not allocate read buffers\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + rc = -EACCES; + MM_ERR("Non tunnel encoding is not supported\n"); + goto done; + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + rc = -EACCES; + goto done; + } + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->channel_mode = AUDREC_CMD_MODE_MONO; + audio->buffer_size = MONO_DATA_SIZE_1024; + audio->samp_rate = 8000; + audio->enc_type = ENC_TYPE_EXT_WAV | audio->mode; + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_adsp_ops, audio); + + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->stopped = 0; + audio->source = 0; + audio->abort = 0; + audpcm_in_flush(audio); + audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_FREQ_CHG | + AUDDEV_EVT_VOICE_STATE_CHG; + + audio->voice_state = msm_get_voice_state(); + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_ENC, audio->enc_id, + pcm_in_listener, (void *) audio); + if (rc) { + MM_ERR("failed to register device event listener\n"); + goto evt_error; + } + file->private_data = audio; + audio->opened = 1; + rc = 0; + audio->build_id = socinfo_get_build_id(); + MM_DBG("Modem build id = %s\n", audio->build_id); +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = audpcm_in_open, + .release = audpcm_in_release, + .read = audpcm_in_read, + .write = audpcm_in_write, + .unlocked_ioctl = audpcm_in_ioctl, +}; + +struct miscdevice audio_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &audio_in_fops, +}; + +static int __init audpcm_in_init(void) +{ + mutex_init(&the_audio_in.lock); + mutex_init(&the_audio_in.read_lock); + spin_lock_init(&the_audio_in.dsp_lock); + spin_lock_init(&the_audio_in.dev_lock); + init_waitqueue_head(&the_audio_in.wait); + init_waitqueue_head(&the_audio_in.wait_enable); + return misc_register(&audio_in_misc); +} + +device_initcall(audpcm_in_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c b/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c new file mode 100644 index 00000000000..c53922bcbd5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c @@ -0,0 +1,1639 @@ +/* + * qcelp 13k audio decoder device + * + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This code is based in part on audio_mp3.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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, you can find it at http://www.fsf.org. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ 1094 /* QCELP 13K Hold 600ms packet data = 36 * 30 and + 14 bytes of meta in */ +#define BUF_COUNT 2 +#define DMASZ (BUFSZ * BUF_COUNT) + +#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and + 24 bytes of meta out */ +#define PCM_BUF_MAX_COUNT 5 + +#define AUDDEC_DEC_QCELP 9 + +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDQCELP_METAFIELD_MASK 0xFFFF0000 +#define AUDQCELP_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDQCELP_EOS_FLG_MASK 0x01 +#define AUDQCELP_EOS_NONE 0x0 /* No EOS detected */ +#define AUDQCELP_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDQCELP_EVENT_NUM 10 /* Default number of pre-allocated event pkts */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audqcelp_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audqcelp_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[BUF_COUNT]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section - START */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* Host PCM section - END */ + + struct msm_adsp_module *audplay; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; /* set when non-tunnel mode */ + uint8_t buf_refresh:1; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audqcelp_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audqcelp_send_data(struct audio *audio, unsigned needed); +static void audqcelp_config_hostpcm(struct audio *audio); +static void audqcelp_buffer_refresh(struct audio *audio); +static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audqcelp_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +/* must be called with audio->lock held */ +static int audqcelp_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +static void qcelp_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audqcelp_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audqcelp_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audqcelp_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audqcelp_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audqcelp_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + } +} + +static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init \n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audqcelp_config_hostpcm(audio); + audqcelp_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audqcelp_buffer_refresh(audio); + break; + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_qcelp = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_v13k cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDQCELP_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audqcelp_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + MM_DBG("buf0_addr=%x buf0_len=%d\n", refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audqcelp_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + +static void audqcelp_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audqcelp_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->out_needed = 0; +} + +static void audqcelp_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audqcelp_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audqcelp_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audqcelp_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audqcelp_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audqcelp_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audqcelp_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audqcelp_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audqcelp_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + + +static long audqcelp_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audqcelp_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audqcelp_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audqcelp_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audqcelp_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audqcelp_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audqcelp_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audqcelp_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audqcelp_disable(audio); + audio->stopped = 1; + audqcelp_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audqcelp_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + if (copy_from_user(&config, (void *)arg, + sizeof(config))) { + rc = -EFAULT; + break; + } + audio->mfield = config.meta_field; + MM_DBG("AUDIO_SET_CONFIG applicable \ + for metafield configuration\n"); + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = BUF_COUNT; + config.sample_rate = 8000; + config.channel_count = 1; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + + if (copy_from_user(&config, (void *)arg, + sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buf %d\n", + config.buffer_count * config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("failed to map read buf\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr 0x%08x \ + kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audqcelp_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d\n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + not know frame size, read count must be greater or equal + to size of PCM samples */ + MM_DBG("read stop - partial frame\n"); + break; + } else { + MM_DBG("read from in[%d]\n", audio->read_next); + + if (copy_to_user(buf, + audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x\n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; + /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audqcelp_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audqcelp_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audqcelp_send_data(audio, 0); + +done: + return rc; +} + +static ssize_t audqcelp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDQCELP_EOS_NONE; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + MM_DBG("buffer available\n"); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] & + AUDQCELP_EOS_FLG_MASK) { + MM_DBG("EOS SET\n"); + eos_condition = AUDQCELP_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] &= + ~AUDQCELP_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer + mfield_size; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + audqcelp_send_data(audio, 0); + } + if (eos_condition == AUDQCELP_EOS_SET) + rc = audqcelp_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audqcelp_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int) audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audqcelp_disable(audio); + audqcelp_flush(audio); + audqcelp_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audqcelp_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audqcelp_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audqcelp_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audqcelp_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audqcelp_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audqcelp_suspend(struct early_suspend *h) +{ + struct audqcelp_suspend_ctl *ctl = + container_of(h, struct audqcelp_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audqcelp_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audqcelp_resume(struct early_suspend *h) +{ + struct audqcelp_suspend_ctl *ctl = + container_of(h, struct audqcelp_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audqcelp_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audqcelp_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audqcelp_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 1024; + static char buffer[1024]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audqcelp_debug_fops = { + .read = audqcelp_debug_read, + .open = audqcelp_debug_open, +}; +#endif + +static int audqcelp_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + struct audqcelp_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_qcelp_" + 5]; +#endif + + /* Create audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance\n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_QCELP; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (!audio->phys) { + MM_ERR("could not allocate write buffers, freeing instance \ + 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else { + audio->map_v_write = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write phys address, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->phys, (int)audio->data); + } + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_qcelp, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + /* Initialize buffer */ + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->vol_pan.volume = 0x2000; + + audqcelp_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + qcelp_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listnet\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_qcelp_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audqcelp_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audqcelp_resume; + audio->suspend_ctl.node.suspend = audqcelp_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDQCELP_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audqcelp_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audqcelp_open, + .release = audqcelp_release, + .read = audqcelp_read, + .write = audqcelp_write, + .unlocked_ioctl = audqcelp_ioctl, + .fsync = audqcelp_fsync, +}; + +struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +static int __init audqcelp_init(void) +{ + return misc_register(&audio_qcelp_misc); +} + +static void __exit audqcelp_exit(void) +{ + misc_deregister(&audio_qcelp_misc); +} + +module_init(audqcelp_init); +module_exit(audqcelp_exit); + +MODULE_DESCRIPTION("MSM QCELP 13K driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c new file mode 100644 index 00000000000..e1af2ad7f73 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c @@ -0,0 +1,1513 @@ +/* + * qcelp audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define META_OUT_SIZE 24 +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM 8 +#define QCELP_FRAME_SIZE 36 /* 36 bytes data */ +#define FRAME_SIZE (22 * 2) /* 36 bytes data */ + /* 36 bytes data + 24 meta field*/ +#define NT_FRAME_SIZE (QCELP_FRAME_SIZE + META_OUT_SIZE) +#define DMASZ (NT_FRAME_SIZE * FRAME_NUM) +#define OUT_FRAME_NUM (2) +#define OUT_BUFFER_SIZE (4 * 1024 + META_OUT_SIZE) +#define BUFFER_SIZE (OUT_BUFFER_SIZE * OUT_FRAME_NUM) + + +#define AUDPREPROC_QCELP_EOS_FLG_OFFSET 0x0A +#define AUDPREPROC_QCELP_EOS_FLG_MASK 0x01 +#define AUDPREPROC_QCELP_EOS_NONE 0x0 /* No EOS detected */ +#define AUDPREPROC_QCELP_EOS_SET 0x1 /* EOS set in meta field */ + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; + uint32_t used; + uint32_t mfield_sz; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + wait_queue_head_t wait_enable; + /*write section*/ + struct buffer out[OUT_FRAME_NUM]; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + uint32_t out_count; + + struct mutex write_lock; + wait_queue_head_t write_wait; + int32_t out_phys; /* physical address of write buffer */ + char *out_data; + int mfield; /* meta field embedded in data */ + int wflush; /*write flush */ + int rflush; /*read flush*/ + int out_frame_cnt; + + struct msm_adsp_module *audrec; + + struct audrec_session_info session_info; /*audrec session info*/ + + /* configuration to use on next enable */ + uint32_t buffer_size; /* Frame size (36 bytes) */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t enc_type; + + struct msm_audio_qcelp_enc_config cfg; + uint32_t rec_mode; + + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + uint32_t mode; + uint32_t eos_ack; + uint32_t flush_ack; + + const char *module_name; + unsigned queue_ids; + uint16_t enc_id; + + uint16_t source; /* Encoding source bit mask */ + uint32_t device_events; + uint32_t in_call; + uint32_t dev_cnt; + int voice_state; + spinlock_t dev_lock; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + void *map_v_read; + void *map_v_write; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + char *build_id; +}; + +struct audio_frame { + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +struct audio_frame_nt { + uint16_t metadata_len; + uint16_t frame_count_lsw; + uint16_t frame_count_msw; + uint16_t frame_length; + uint16_t erased_pcm; + uint16_t reserved; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; + unsigned char raw_bitstream[]; /* samples */ +} __attribute__((packed)); + +struct qcelp_encoded_meta_out { + uint16_t metadata_len; + uint16_t time_stamp_dword_lsw; + uint16_t time_stamp_dword_msw; + uint16_t time_stamp_lsw; + uint16_t time_stamp_msw; + uint16_t nflag_lsw; + uint16_t nflag_msw; +}; + +/* Audrec Queue command sent macro's */ +#define audrec_send_bitstreamqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\ + cmd, len) + +#define audrec_send_audrecqueue(audio, cmd, len) \ + msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\ + cmd, len) + +/* DSP command send functions */ +static int audqcelp_in_enc_config(struct audio_in *audio, int enable); +static int audqcelp_in_param_config(struct audio_in *audio); +static int audqcelp_in_mem_config(struct audio_in *audio); +static int audqcelp_in_record_config(struct audio_in *audio, int enable); +static int audqcelp_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); + +static void audqcelp_in_get_dsp_frames(struct audio_in *audio); +static int audpcm_config(struct audio_in *audio); +static void audqcelp_out_flush(struct audio_in *audio); +static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio); +static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed); +static void audqcelp_nt_in_get_dsp_frames(struct audio_in *audio); + +static void audqcelp_in_flush(struct audio_in *audio); + +static void qcelp_in_listener(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio_in *audio = (struct audio_in *) private_data; + unsigned long flags; + + MM_DBG("evt_id = 0x%8x\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: { + MM_DBG("AUDDEV_EVT_DEV_RDY\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt++; + if (!audio->in_call) + audio->source |= (0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((audio->running == 1) && (audio->enabled == 1) && + (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) + audqcelp_in_record_config(audio, 1); + } + break; + case AUDDEV_EVT_DEV_RLS: { + MM_DBG("AUDDEV_EVT_DEV_RLS\n"); + spin_lock_irqsave(&audio->dev_lock, flags); + audio->dev_cnt--; + if (!audio->in_call) + audio->source &= ~(0x1 << evt_payload->routing_id); + spin_unlock_irqrestore(&audio->dev_lock, flags); + + if ((!audio->running) || (!audio->enabled)) + break; + + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + /* Turn of as per source */ + if (audio->source) + audqcelp_in_record_config(audio, 1); + else + /* Turn off all */ + audqcelp_in_record_config(audio, 0); + } + } + break; + case AUDDEV_EVT_VOICE_STATE_CHG: { + MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n", + evt_payload->voice_state); + audio->voice_state = evt_payload->voice_state; + if (audio->in_call && audio->running && + (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + if (audio->voice_state == VOICE_STATE_INCALL) + audqcelp_in_record_config(audio, 1); + else if (audio->voice_state == VOICE_STATE_OFFCALL) { + audqcelp_in_record_config(audio, 0); + wake_up(&audio->wait); + } + } + + break; + } + default: + MM_ERR("wrong event %d\n", evt_id); + break; + } +} + +/* ------------------- dsp preproc event handler--------------------- */ +static void audpreproc_dsp_event(void *data, unsigned id, void *msg) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg *err_msg = msg; + + MM_ERR("ERROR_MSG: stream id %d err idx %d\n", + err_msg->stream_id, err_msg->aud_preproc_err_idx); + /* Error case */ + wake_up(&audio->wait_enable); + break; + } + case AUDPREPROC_CMD_CFG_DONE_MSG: { + MM_DBG("CMD_CFG_DONE_MSG \n"); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg; + + MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + 0x%8x\n", enc_cfg_msg->stream_id, + enc_cfg_msg->rec_enc_type); + /* Encoder enable success */ + if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) { + if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + MM_DBG("routing command\n"); + audpreproc_cmd_cfg_routing_mode(audio); + } else { + audqcelp_in_param_config(audio); + } + } else { /* Encoder disable success */ + audio->running = 0; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audqcelp_in_record_config(audio, 0); + else + wake_up(&audio->wait_enable); + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n"); + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) + audqcelp_in_mem_config(audio); + else + audpcm_config(audio); + break; + } + case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: { + struct audpreproc_cmd_routing_mode_done\ + *routing_cfg_done_msg = msg; + if (routing_cfg_done_msg->configuration == 0) { + MM_INFO("routing configuration failed\n"); + audio->running = 0; + } else + audqcelp_in_param_config(audio); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n"); + wake_up(&audio->wait_enable); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +/* ------------------- dsp audrec event handler--------------------- */ +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + + switch (id) { + case AUDREC_CMD_MEM_CFG_DONE_MSG: { + MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n"); + audio->running = 1; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if ((!audio->in_call && (audio->dev_cnt > 0)) || + (audio->in_call && + (audio->voice_state \ + == VOICE_STATE_INCALL))) + audqcelp_in_record_config(audio, 1); + } else { + audpreproc_pcm_send_data(audio, 1); + wake_up(&audio->wait_enable); + } + break; + } + case AUDREC_FATAL_ERR_MSG: { + struct audrec_fatal_err_msg fatal_err_msg; + + getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN); + MM_ERR("FATAL_ERR_MSG: err id %d\n", + fatal_err_msg.audrec_err_id); + /* Error stop the encoder */ + audio->stopped = 1; + wake_up(&audio->wait); + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + wake_up(&audio->write_wait); + break; + } + case AUDREC_UP_PACKET_READY_MSG: { + struct audrec_up_pkt_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN); + MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packet_write_cnt_lsw, \ + pkt_ready_msg.audrec_packet_write_cnt_msw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \ + pkt_ready_msg.audrec_up_prev_read_cnt_msw); + + audqcelp_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: { + MM_DBG("ptr_update recieved from DSP\n"); + audpreproc_pcm_send_data(audio, 1); + break; + } + case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: { + MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG"); + audqcelp_in_mem_config(audio); + break; + } + case AUDREC_UP_NT_PACKET_READY_MSG: { + struct audrec_up_nt_packet_ready_msg pkt_ready_msg; + + getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN); + MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw %d \ + write cnt msw %d read cnt lsw %d read cnt msw %d \n",\ + pkt_ready_msg.audrec_packetwrite_cnt_lsw, \ + pkt_ready_msg.audrec_packetwrite_cnt_msw, \ + pkt_ready_msg.audrec_upprev_readcount_lsw, \ + pkt_ready_msg.audrec_upprev_readcount_msw); + + audqcelp_nt_in_get_dsp_frames(audio); + break; + } + case AUDREC_CMD_EOS_ACK_MSG: { + MM_DBG("eos ack recieved\n"); + break; + } + case AUDREC_CMD_FLUSH_DONE_MSG: { + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 1; + wake_up(&audio->write_wait); + MM_DBG("flush ack recieved\n"); + break; + } + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event:module audrectask\n"); + break; + } + default: + MM_ERR("Unknown Event id %d\n", id); + } +} + +static void audqcelp_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + MM_DBG("head = %d\n", audio->in_head); + index = audio->in_head; + + frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->frame_length; + + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) { + MM_ERR("Error! not able to keep up the read\n"); + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + MM_ERR("in_count = %d\n", audio->in_count); + } else + audio->in_count++; + + audqcelp_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); +} + +static void audqcelp_nt_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame_nt *nt_frame; + uint32_t index; + unsigned long flags; + MM_DBG("head = %d\n", audio->in_head); + index = audio->in_head; + nt_frame = (void *) (((char *)audio->in[index].data) - \ + sizeof(struct audio_frame_nt)); + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = nt_frame->frame_length; + /* statistics of read */ + atomic_add(audio->in[index].size, &audio->in_bytes); + atomic_add(1, &audio->in_samples); + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + MM_DBG("Error! not able to keep up the read\n"); + else + audio->in_count++; + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->wait); +} + + +struct msm_adsp_ops audrec_qcelp_adsp_ops = { + .event = audrec_dsp_event, +}; + +static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio, + unsigned idx, unsigned len) +{ + struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd; + + if (len == META_OUT_SIZE) + len = len / 2; + else + len = (len + META_OUT_SIZE) / 2; + MM_DBG("len = %d\n", len); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC; + cmd.num_buffers = 1; + if (cmd.num_buffers == 1) { + cmd.buf_address_length[0] = (audio->out[idx].addr & + 0xffff0000) >> 16; + cmd.buf_address_length[1] = (audio->out[idx].addr & + 0x0000ffff); + cmd.buf_address_length[2] = (len & 0xffff0000) >> 16; + cmd.buf_address_length[3] = (len & 0x0000ffff); + } + audio->out_frame_cnt++; + return audrec_send_audrecqueue(audio, (void *)&cmd, + (unsigned int)sizeof(cmd)); +} + + +static int audpcm_config(struct audio_in *audio) +{ + struct audrec_cmd_pcm_cfg_arm_to_enc cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC; + cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE; + cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE; + cmd.sampling_freq = audio->samp_rate; + if (!audio->channel_mode) + cmd.channels = 1; + else + cmd.channels = 2; + cmd.frequency_of_intimation = 1; + cmd.max_number_of_buffers = OUT_FRAME_NUM; + return audrec_send_audrecqueue(audio, (void *)&cmd, + (unsigned int)sizeof(cmd)); +} + + +static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_routing_mode cmd; + + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE; + cmd.stream_id = audio->enc_id; + if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL) + cmd.routing_mode = 1; + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + + + +static int audqcelp_in_enc_config(struct audio_in *audio, int enable) +{ + struct audpreproc_audrec_cmd_enc_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + if (audio->build_id[17] == '1') { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG_2 command"); + } else { + cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG; + MM_ERR("sending AUDPREPROC_AUDREC_CMD_ENC_CFG command"); + } + cmd.stream_id = audio->enc_id; + + if (enable) + cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE; + else + cmd.audrec_enc_type &= ~(ENCODE_ENABLE); + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audqcelp_in_param_config(struct audio_in *audio) +{ + struct audpreproc_audrec_cmd_parm_cfg_qcelp13k cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG; + cmd.common.stream_id = audio->enc_id; + + cmd.enc_min_rate = audio->cfg.min_bit_rate; + cmd.enc_max_rate = audio->cfg.max_bit_rate; + cmd.rate_modulation_cmd = 0; /* Default set to 0 */ + cmd.reduced_rate_level = 0; /* Default set to 0 */ + + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +/* To Do: msm_snddev_route_enc(audio->enc_id); */ +static int audqcelp_in_record_config(struct audio_in *audio, int enable) +{ + struct audpreproc_afe_cmd_audio_record_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG; + cmd.stream_id = audio->enc_id; + if (enable) + cmd.destination_activity = AUDIO_RECORDING_TURN_ON; + else + cmd.destination_activity = AUDIO_RECORDING_TURN_OFF; + + cmd.source_mix_mask = audio->source; + if (audio->enc_id == 2) { + if ((cmd.source_mix_mask & + INTERNAL_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) || + (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) { + cmd.pipe_id = SOURCE_PIPE_1; + } + if (cmd.source_mix_mask & + AUDPP_A2DP_PIPE_SOURCE_MIX_MASK) + cmd.pipe_id |= SOURCE_PIPE_0; + } + MM_DBG("stream_id %x destination_activity %x \ + source_mix_mask %x pipe_id %x",\ + cmd.stream_id, cmd.destination_activity, + cmd.source_mix_mask, cmd.pipe_id); + return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd)); +} + +static int audqcelp_in_mem_config(struct audio_in *audio) +{ + struct audrec_cmd_arecmem_cfg cmd; + uint16_t *data = (void *) audio->data; + int n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD; + cmd.audrec_up_pkt_intm_count = 1; + cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16; + cmd.audrec_ext_pkt_start_addr_lsw = audio->phys; + cmd.audrec_ext_pkt_buf_number = FRAME_NUM; + MM_DBG("audio->phys = %x\n", audio->phys); + /* prepare buffer pointers: + * T:36 bytes qcelp ppacket + 4 halfword header + * NT:36 bytes qcelp packet + 12 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + audio->in[n].data = data + 4; + data += (FRAME_SIZE/2); + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8)); + } else { + audio->in[n].data = data + 12; + data += ((QCELP_FRAME_SIZE) / 2) + 12; + MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24)); + } + } + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +static int audqcelp_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + struct up_audrec_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR; + cmd.audrec_up_curr_read_count_msw = read_cnt >> 16; + cmd.audrec_up_curr_read_count_lsw = read_cnt; + + return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd)); +} +static int audqcelp_flush_command(struct audio_in *audio) +{ + struct audrec_cmd_flush cmd; + MM_DBG("\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_FLUSH; + return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd)); +} + +/* must be called with audio->lock held */ +static int audqcelp_in_enable(struct audio_in *audio) +{ + if (audio->enabled) + return 0; + + if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) { + MM_ERR("msm_adsp_enable(audpreproc) failed\n"); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audrec)) { + MM_ERR("msm_adsp_enable(audrec) failed\n"); + audpreproc_disable(audio->enc_id, audio); + return -ENODEV; + } + audio->enabled = 1; + audqcelp_in_enc_config(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audqcelp_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + audqcelp_in_enc_config(audio, 0); + wake_up(&audio->wait); + wait_event_interruptible_timeout(audio->wait_enable, + audio->running == 0, 1*HZ); + msm_adsp_disable(audio->audrec); + audpreproc_disable(audio->enc_id, audio); + } + return 0; +} + +static void audqcelp_ioport_reset(struct audio_in *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audqcelp_in_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audqcelp_out_flush(audio); + mutex_unlock(&audio->read_lock); +} + +static void audqcelp_in_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + audio->eos_ack = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } + MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes)); + MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); +} + +static void audqcelp_out_flush(struct audio_in *audio) +{ + int i; + + audio->out_head = 0; + audio->out_tail = 0; + audio->out_count = 0; + for (i = 0; i < OUT_FRAME_NUM; i++) { + audio->out[i].size = 0; + audio->out[i].read = 0; + audio->out[i].used = 0; + } +} + +/* ------------------- device --------------------- */ +static long audqcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t freq; + freq = 48000; + MM_DBG("AUDIO_START\n"); + if (audio->in_call && (audio->voice_state != + VOICE_STATE_INCALL)) { + rc = -EPERM; + break; + } + rc = msm_snddev_request_freq(&freq, audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("sample rate configured %d\n", freq); + if (rc < 0) { + MM_DBG(" Sample rate can not be set, return code %d\n", + rc); + msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + break; + } + /*update aurec session info in audpreproc layer*/ + audio->session_info.session_id = audio->enc_id; + audio->session_info.sampling_freq = audio->samp_rate; + audpreproc_update_audrec_info(&audio->session_info); + rc = audqcelp_in_enable(audio); + if (!rc) { + rc = + wait_event_interruptible_timeout(audio->wait_enable, + audio->running != 0, 1*HZ); + MM_DBG("state %d rc = %d\n", audio->running, rc); + + if (audio->running == 0) + rc = -ENODEV; + else + rc = 0; + } + audio->stopped = 0; + break; + } + case AUDIO_STOP: { + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + rc = audqcelp_in_disable(audio); + rc = msm_snddev_withdraw_freq(audio->enc_id, + SNDDEV_CAP_TX, AUDDEV_CLNT_ENC); + MM_DBG("msm_snddev_withdraw_freq\n"); + audio->stopped = 1; + break; + } + case AUDIO_FLUSH: { + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audqcelp_ioport_reset(audio); + if (audio->running) { + audqcelp_flush_command(audio); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Allow only single frame */ + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (cfg.buffer_size != (FRAME_SIZE - 8)) { + rc = -EINVAL; + break; + } + } else { + if (cfg.buffer_size != (QCELP_FRAME_SIZE + 14)) { + rc = -EINVAL; + break; + } + } + audio->buffer_size = cfg.buffer_size; + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate, \ + cfg.max_bit_rate, cfg.cdma_rate); + if (cfg.min_bit_rate > CDMA_RATE_FULL || \ + cfg.min_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid min bitrate\n"); + rc = -EFAULT; + break; + } + if (cfg.max_bit_rate > CDMA_RATE_FULL || \ + cfg.max_bit_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid max bitrate\n"); + rc = -EFAULT; + break; + } + /* Recording Does not support Erase and Blank */ + if (cfg.cdma_rate > CDMA_RATE_FULL || + cfg.cdma_rate < CDMA_RATE_EIGHTH) { + MM_ERR("invalid qcelp cdma rate\n"); + rc = -EFAULT; + break; + } + memcpy(&audio->cfg, &cfg, sizeof(cfg)); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = OUT_BUFFER_SIZE; + cfg.buffer_count = OUT_FRAME_NUM; + cfg.sample_rate = audio->samp_rate; + cfg.channel_count = audio->channel_mode; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_INCALL: { + struct msm_voicerec_mode cfg; + unsigned long flags; + if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) { + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.rec_mode != VOC_REC_BOTH && + cfg.rec_mode != VOC_REC_UPLINK && + cfg.rec_mode != VOC_REC_DOWNLINK) { + MM_ERR("invalid rec_mode\n"); + rc = -EINVAL; + break; + } else { + spin_lock_irqsave(&audio->dev_lock, flags); + if (cfg.rec_mode == VOC_REC_UPLINK) + audio->source = \ + VOICE_UL_SOURCE_MIX_MASK; + else if (cfg.rec_mode == VOC_REC_DOWNLINK) + audio->source = \ + VOICE_DL_SOURCE_MIX_MASK; + else + audio->source = \ + VOICE_DL_SOURCE_MIX_MASK | + VOICE_UL_SOURCE_MIX_MASK ; + audio->in_call = 1; + spin_unlock_irqrestore(&audio->dev_lock, flags); + } + } + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->enc_id, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audqcelp_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + struct qcelp_encoded_meta_out meta_field; + struct audio_frame_nt *nt_frame; + MM_DBG(" count = %d\n", count); + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped || + audio->rflush || + ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) && + audio->in_call && audio->running && + (audio->voice_state == VOICE_STATE_OFFCALL))); + if (rc < 0) + break; + + if (audio->rflush) { + rc = -EBUSY; + break; + } + if (audio->stopped && !audio->in_count) { + MM_DBG("Driver in stop state, No more buffer to read"); + rc = 0;/* End of File */ + break; + } else if ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) && + audio->in_call && audio->running && + (audio->voice_state \ + == VOICE_STATE_OFFCALL)) { + MM_DBG("Not Permitted Voice Terminated\n"); + rc = -EPERM; /* Voice Call stopped */ + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) { + nt_frame = (struct audio_frame_nt *)(data - + sizeof(struct audio_frame_nt)); + memcpy((char *)&meta_field.time_stamp_dword_lsw, + (char *)&nt_frame->time_stamp_dword_lsw, + (sizeof(struct qcelp_encoded_meta_out) - \ + sizeof(uint16_t))); + meta_field.metadata_len = + sizeof(struct qcelp_encoded_meta_out); + if (copy_to_user((char *)start, + (char *)&meta_field, + sizeof(struct qcelp_encoded_meta_out))) { + rc = -EFAULT; + break; + } + if (nt_frame->nflag_lsw & 0x0001) { + MM_ERR("recieved EOS in read call\n"); + audio->eos_ack = 1; + } + buf += sizeof(struct qcelp_encoded_meta_out); + count -= sizeof(struct qcelp_encoded_meta_out); + } + if (count >= size) { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is + * invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) { + if (!audio->eos_ack) { + MM_DBG("sending read ptr command\ + %d %d\n", + audio->dsp_cnt, + audio->in_tail); + audqcelp_dsp_read_buffer(audio, + audio->dsp_cnt++); + } + } + } else { + MM_ERR("short read\n"); + break; + } + break; + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + MM_DBG("\n"); + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audpreproc_pcm_buffer_ptr_refresh(audio, + audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + + +static int audqcelp_in_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) + +{ + struct audio_in *audio = file->private_data; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + audio->wflush); + MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; + +} + + int audpreproc_qcelp_process_eos(struct audio_in *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + struct buffer *frame; + int rc = 0; + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + MM_DBG("copying meta_out frame->used = %d\n", frame->used); + audpreproc_pcm_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audqcelp_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + char *cpy_ptr; + int rc = 0, eos_condition = AUDPREPROC_QCELP_EOS_NONE; + unsigned short mfield_size = 0; + int write_count = 0; + + MM_DBG("cnt=%d\n", count); + if (count & 1) + return -EINVAL; + + if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL) + return -EINVAL; + + mutex_lock(&audio->write_lock); + frame = audio->out + audio->out_head; + /* if supplied count is more than driver buffer size + * then only copy driver buffer size + */ + if (count > frame->size) + count = frame->size; + + write_count = count; + cpy_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto error; + + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto error; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + goto error; + } else if (mfield_size > count) { + rc = -EINVAL; + goto error; + } + MM_DBG("mf offset_val %x\n", mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + goto error; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] & + AUDPREPROC_QCELP_EOS_FLG_MASK) { + eos_condition = AUDPREPROC_QCELP_EOS_SET; + MM_DBG("EOS SET\n"); + if (mfield_size == count) { + buf += mfield_size; + eos_condition = 0; + goto exit; + } else + cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] &= + ~AUDPREPROC_QCELP_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + MM_DBG("copying the stream count = %d\n", count); + if (copy_from_user(cpy_ptr, buf, count)) { + rc = -EFAULT; + goto error; + } +exit: + frame->used = count; + audio->out_head ^= 1; + if (!audio->flush_ack) + audpreproc_pcm_send_data(audio, 0); + else { + audpreproc_pcm_send_data(audio, 1); + audio->flush_ack = 0; + } + if (eos_condition == AUDPREPROC_QCELP_EOS_SET) + rc = audpreproc_qcelp_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + return write_count; +error: + mutex_unlock(&audio->write_lock); + return rc; +} + +static int audqcelp_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audio->in_call = 0; + /* with draw frequency for session + incase not stopped the driver */ + msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX, + AUDDEV_CLNT_ENC); + auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id); + /*reset the sampling frequency information at audpreproc layer*/ + audio->session_info.sampling_freq = 0; + audpreproc_update_audrec_info(&audio->session_info); + audqcelp_in_disable(audio); + audqcelp_in_flush(audio); + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + audio->audrec = NULL; + audio->opened = 0; + if (audio->data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->phys); + audio->data = NULL; + } + if (audio->out_data) { + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + audio->out_data = NULL; + } + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_in the_audio_qcelp_in; +static int audqcelp_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_qcelp_in; + int rc; + int encid; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K); + if (audio->phys) { + audio->map_v_read = ioremap(audio->phys, DMASZ); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("could not map DMA buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + goto done; + } + audio->data = audio->map_v_read; + } else { + MM_ERR("could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + MM_DBG("Memory addr = 0x%8x phy addr = 0x%8x\n",\ + (int) audio->data, (int) audio->phys); + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL; + MM_DBG("Opened for non tunnel mode encoding\n"); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->mode = MSM_AUD_ENC_MODE_TUNNEL; + MM_DBG("Opened for tunnel mode encoding\n"); + } else { + MM_ERR("Invalid mode\n"); + rc = -EACCES; + goto done; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) + audio->buffer_size = (QCELP_FRAME_SIZE + 14); + else + audio->buffer_size = (FRAME_SIZE - 8); + audio->enc_type = ENC_TYPE_V13K | audio->mode; + audio->samp_rate = 8000; + audio->channel_mode = AUDREC_CMD_MODE_MONO; + audio->cfg.cdma_rate = CDMA_RATE_FULL; + audio->cfg.min_bit_rate = CDMA_RATE_FULL; + audio->cfg.max_bit_rate = CDMA_RATE_FULL; + audio->source = INTERNAL_CODEC_TX_SOURCE_MIX_MASK; + audio->rec_mode = VOC_REC_UPLINK; + + encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name, + &audio->queue_ids); + if (encid < 0) { + MM_ERR("No free encoder available\n"); + rc = -ENODEV; + goto done; + } + audio->enc_id = encid; + + rc = msm_adsp_get(audio->module_name, &audio->audrec, + &audrec_qcelp_adsp_ops, audio); + + if (rc) { + audpreproc_aenc_free(audio->enc_id); + goto done; + } + + audio->stopped = 0; + audio->source = 0; + audio->wflush = 0; + audio->rflush = 0; + audio->flush_ack = 0; + + audqcelp_in_flush(audio); + audqcelp_out_flush(audio); + + audio->out_phys = allocate_contiguous_ebi_nomap(BUFFER_SIZE, SZ_4K); + if (!audio->out_phys) { + MM_ERR("could not allocate write buffers\n"); + rc = -ENOMEM; + goto evt_error; + } else { + audio->map_v_write = ioremap(audio->out_phys, BUFFER_SIZE); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->out_phys); + goto evt_error; + } + audio->out_data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + audio->out_phys, (int)audio->out_data); + } + + /* Initialize buffer */ + audio->out[0].data = audio->out_data + 0; + audio->out[0].addr = audio->out_phys + 0; + audio->out[0].size = OUT_BUFFER_SIZE; + + audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE; + audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE; + audio->out[1].size = OUT_BUFFER_SIZE; + + MM_DBG("audio->out[0].data = %d audio->out[1].data = %d", + (unsigned int)audio->out[0].data, + (unsigned int)audio->out[1].data); + audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS | + AUDDEV_EVT_VOICE_STATE_CHG; + + audio->voice_state = msm_get_voice_state(); + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_ENC, audio->enc_id, + qcelp_in_listener, (void *) audio); + if (rc) { + MM_ERR("failed to register device event listener\n"); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->out_phys); + goto evt_error; + } + audio->mfield = META_OUT_SIZE; + file->private_data = audio; + audio->opened = 1; + audio->out_frame_cnt++; + audio->build_id = socinfo_get_build_id(); + MM_DBG("Modem build id = %s\n", audio->build_id); +done: + mutex_unlock(&audio->lock); + return rc; +evt_error: + msm_adsp_put(audio->audrec); + audpreproc_aenc_free(audio->enc_id); + mutex_unlock(&audio->lock); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = audqcelp_in_open, + .release = audqcelp_in_release, + .read = audqcelp_in_read, + .write = audqcelp_in_write, + .fsync = audqcelp_in_fsync, + .unlocked_ioctl = audqcelp_in_ioctl, +}; + +struct miscdevice audio_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_in_fops, +}; + +static int __init audqcelp_in_init(void) +{ + mutex_init(&the_audio_qcelp_in.lock); + mutex_init(&the_audio_qcelp_in.read_lock); + spin_lock_init(&the_audio_qcelp_in.dsp_lock); + spin_lock_init(&the_audio_qcelp_in.dev_lock); + init_waitqueue_head(&the_audio_qcelp_in.wait); + init_waitqueue_head(&the_audio_qcelp_in.wait_enable); + mutex_init(&the_audio_qcelp_in.write_lock); + init_waitqueue_head(&the_audio_qcelp_in.write_wait); + return misc_register(&audio_qcelp_in_misc); +} + +device_initcall(audqcelp_in_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_wma.c b/arch/arm/mach-msm/qdsp5v2/audio_wma.c new file mode 100644 index 00000000000..80adebd086e --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_wma.c @@ -0,0 +1,1797 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Size must be power of 2 */ +#define BUFSZ_MAX 4110 /* Includes meta in size */ +#define BUFSZ_MIN 1038 /* Includes meta in size */ +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_WMA 4 + +#define PCM_BUFSZ_MIN 8216 /* Hold one stereo WMA frame and meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDWMA_METAFIELD_MASK 0xFFFF0000 +#define AUDWMA_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDWMA_EOS_FLG_MASK 0x01 +#define AUDWMA_EOS_NONE 0x0 /* No EOS detected */ +#define AUDWMA_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDWMA_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audwma_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audwma_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + struct msm_audio_wma_config wma_config; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audwma_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwma_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +static void wma_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("audio_update_pcm_buf_entry: \ + in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("audio_update_pcm_buf_entry: \ + expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + /* send mixer command */ + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +static struct msm_adsp_ops audplay_adsp_ops_wma = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMA; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_wma cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + /* + * Test done for sample with the following configuration + * armdatareqthr = 1262 + * channelsdecoded = 1(MONO)/2(STEREO) + * wmabytespersec = Tested with 6003 Bytes per sec + * wmasamplingfreq = 44100 + * wmaencoderopts = 31 + */ + + cmd.armdatareqthr = audio->wma_config.armdatareqthr; + cmd.channelsdecoded = audio->wma_config.channelsdecoded; + cmd.wmabytespersec = audio->wma_config.wmabytespersec; + cmd.wmasamplingfreq = audio->wma_config.wmasamplingfreq; + cmd.wmaencoderopts = audio->wma_config.wmaencoderopts; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + + MM_DBG("buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (!audio->pcm_feedback) + cmd.decoder_id = 0; + else { + if (audio->mfield) + cmd.decoder_id = AUDWMA_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + } + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (audio->wflush) { + audio->out_needed = 1; + goto done; + } + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + MM_DBG("\n"); /* Macro prints the file name and function */ + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audwma_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audwma_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audwma_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audwma_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audwma_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audwma_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audwma_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwma_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audwma_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_WMA_CONFIG:{ + if (copy_to_user((void *)arg, &audio->wma_config, + sizeof(audio->wma_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_WMA_CONFIG:{ + struct msm_audio_wma_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + audio->wma_config = usr_config; + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("read buf alloc fail\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("audio_read: no partial frame done reading\n"); + break; + } else { + MM_DBG("audio_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audwma_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audplay_send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDWMA_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("audio_write: mf offset_val %x\n", + mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDWMA_EOS_FLG_OFFSET] & + AUDWMA_EOS_FLG_MASK) { + MM_DBG("audio_write: EOS SET\n"); + eos_condition = AUDWMA_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDWMA_EOS_FLG_OFFSET] + &= ~AUDWMA_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("audio_write: continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + if (eos_condition == AUDWMA_EOS_SET) + rc = audwma_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audwma_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwma_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audwma_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audwma_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audwma_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audwma_suspend(struct early_suspend *h) +{ + struct audwma_suspend_ctl *ctl = + container_of(h, struct audwma_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwma_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audwma_resume(struct early_suspend *h) +{ + struct audwma_suspend_ctl *ctl = + container_of(h, struct audwma_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwma_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audwma_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audwma_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audwma_debug_fops = { + .read = audwma_debug_read, + .open = audwma_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + unsigned pmem_sz = DMASZ_MAX; + struct audwma_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_WMA; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap(audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not allocate write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_wma, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + audio->wma_config.armdatareqthr = 1262; + audio->wma_config.channelsdecoded = 2; + audio->wma_config.wmabytespersec = 6003; + audio->wma_config.wmasamplingfreq = 44100; + audio->wma_config.wmaencoderopts = 31; + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audio->vol_pan.volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + wma_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wma_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audwma_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audwma_resume; + audio->suspend_ctl.node.suspend = audwma_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDWMA_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audwma_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +struct miscdevice audio_wma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_wma_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c b/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c new file mode 100644 index 00000000000..ca072b39d43 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c @@ -0,0 +1,1815 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Size must be power of 2 */ +#define BUFSZ_MAX 4110 /* Includes meta in size */ +#define BUFSZ_MIN 2062 /* Includes meta in size */ +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_WMAPRO 13 + +#define PCM_BUFSZ_MIN 8216 /* Hold one stereo WMAPRO frame and meta out*/ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +#define AUDWMAPRO_METAFIELD_MASK 0xFFFF0000 +#define AUDWMAPRO_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */ +#define AUDWMAPRO_EOS_FLG_MASK 0x01 +#define AUDWMAPRO_EOS_NONE 0x0 /* No EOS detected */ +#define AUDWMAPRO_EOS_SET 0x1 /* EOS set in meta field */ + +#define AUDWMAPRO_EVENT_NUM 10 /* Default no. of pre-allocated event packets */ + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audwmapro_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct audwmapro_event{ + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + int32_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + struct msm_audio_wmapro_config wmapro_config; + + /* data allocated for various buffers */ + char *data; + int32_t phys; /* physical address of write buffer */ + void *map_v_read; + void *map_v_write; + + int mfield; /* meta field embedded in data */ + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + enum msm_aud_decoder_state dec_state; /* Represents decoder state */ + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + const char *module_name; + unsigned queue_id; + uint16_t dec_id; + uint32_t read_ptr_offset; + int16_t source; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audwmapro_suspend_ctl suspend_ctl; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + /* AV sync Info */ + int avsync_flag; /* Flag to indicate feedback from DSP */ + wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message */ + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1]; + + uint32_t device_events; + + int eq_enable; + int eq_needs_commit; + struct audpp_cmd_cfg_object_params_eqalizer eq; + struct audpp_cmd_cfg_object_params_volume vol_pan; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwmapro_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); +#endif + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) + return 0; + + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + audio->out_tail = 0; + audio->out_needed = 0; + + if (msm_adsp_enable(audio->audplay)) { + MM_ERR("msm_adsp_enable(audplay) failed\n"); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + MM_ERR("audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +static void wmapro_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + MM_DBG(":AUDDEV_EVT_DEV_RDY\n"); + audio->source |= (0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_DEV_RLS: + MM_DBG(":AUDDEV_EVT_DEV_RLS\n"); + audio->source &= ~(0x1 << evt_payload->routing_id); + if (audio->running == 1 && audio->enabled == 1) + audpp_route_stream(audio->dec_id, audio->source); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->vol_pan.volume = evt_payload->session_vol; + MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n", + audio->vol_pan.volume); + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + break; + default: + MM_ERR(":ERROR:wrong event\n"); + break; + } +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + MM_DBG("\n"); /* Macro prints the file name and function */ + if (audio->enabled) { + audio->enabled = 0; + audio->dec_state = MSM_AUD_DECODER_STATE_NONE; + auddec_dsp_config(audio, 0); + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + if (rc == 0) + rc = -ETIMEDOUT; + else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE) + rc = -EFAULT; + else + rc = 0; + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audio->out_needed = 0; + } + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + MM_DBG("audio_update_pcm_buf_entry: \ + in[%d] ready\n", audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + MM_ERR("audio_update_pcm_buf_entry: \ + expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + MM_DBG("read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + + getevent(msg, sizeof(msg)); + + MM_DBG("msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable(audplaytask)\n"); + break; + + default: + MM_ERR("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: { + uint16_t reason = msg[2]; + MM_DBG("decoder status:sleep reason = \ + 0x%04x\n", reason); + if ((reason == AUDPP_MSG_REASON_MEM) + || (reason == + AUDPP_MSG_REASON_NODECODER)) { + audio->dec_state = + MSM_AUD_DECODER_STATE_FAILURE; + wake_up(&audio->wait); + } else if (reason == AUDPP_MSG_REASON_NONE) { + /* decoder is in disable state */ + audio->dec_state = + MSM_AUD_DECODER_STATE_CLOSE; + wake_up(&audio->wait); + } + break; + } + case AUDPP_DEC_STATUS_INIT: + MM_DBG("decoder status: init\n"); + if (audio->pcm_feedback) + audpp_cmd_cfg_routing_mode(audio); + else + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + MM_DBG("decoder status: cfg\n"); + break; + case AUDPP_DEC_STATUS_PLAY: + MM_DBG("decoder status: play \n"); + audpp_route_stream(audio->dec_id, + audio->source); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + audio->dec_state = + MSM_AUD_DECODER_STATE_SUCCESS; + wake_up(&audio->wait); + break; + default: + MM_ERR("unknown decoder status\n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_DBG("CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + audpp_dsp_set_eq(audio->dec_id, audio->eq_enable, + &audio->eq, POPP); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_DBG("CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + MM_DBG("CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + MM_DBG("ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + MM_DBG("FLUSH_ACK\n"); + audio->wflush = 0; + audio->rflush = 0; + wake_up(&audio->write_wait); + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + case AUDPP_MSG_PCMDMAMISSED: + MM_DBG("PCMDMAMISSED\n"); + audio->teos = 1; + wake_up(&audio->write_wait); + break; + + case AUDPP_MSG_AVSYNC_MSG: + MM_DBG("AUDPP_MSG_AVSYNC_MSG\n"); + memcpy(&audio->avsync[0], msg, sizeof(audio->avsync)); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); + break; + + default: + MM_ERR("UNKNOWN (%d)\n", id); + } + +} + +static struct msm_adsp_ops audplay_adsp_ops_wmapro = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, audio->queue_id, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + struct audpp_cmd_cfg_dec_type cfg_dec_cmd; + + memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd)); + + cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMAPRO; + else + cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + cfg_dec_cmd.dm_mode = 0x0; + cfg_dec_cmd.stream_id = audio->dec_id; + return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_wmapro cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + /* + * Test done for sample with the following configuration + * armdatareqthr = 1262 + * channelsdecoded = 1(MONO)/2(STEREO) + * wmaprobytespersec = Tested with 6003 Bytes per sec + * wmaprosamplingfreq = 44100 + * wmaproencoderopts = 31 + */ + + cmd.armdatareqthr = audio->wmapro_config.armdatareqthr; + cmd.numchannels = audio->wmapro_config.numchannels; + cmd.validbitspersample = audio->wmapro_config.validbitspersample; + cmd.formattag = audio->wmapro_config.formattag; + cmd.samplingrate = audio->wmapro_config.samplingrate; + cmd.avgbytespersecond = audio->wmapro_config.avgbytespersecond; + cmd.asfpacketlength = audio->wmapro_config.asfpacketlength; + cmd.channelmask = audio->wmapro_config.channelmask; + cmd.encodeopt = audio->wmapro_config.encodeopt; + cmd.advancedencodeopt = audio->wmapro_config.advancedencodeopt; + cmd.advancedencodeopt2 = audio->wmapro_config.advancedencodeopt2; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + + MM_DBG("buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, + refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + MM_DBG("\n"); /* Macro prints the file name and function */ + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail_nt2 cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2; + if (audio->mfield) + cmd.decoder_id = AUDWMAPRO_METAFIELD_MASK | + (audio->out[idx].mfield_sz >> 1); + else + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (audio->wflush) { + audio->out_needed = 1; + goto done; + } + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + MM_DBG("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + MM_DBG("\n"); /* Macro prints the file name and function */ + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + MM_DBG("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + audio->avsync_flag = 1; + wake_up(&audio->avsync_wait); +} + +static int audwmapro_events_pending(struct audio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static void audwmapro_reset_event_queue(struct audio *audio) +{ + unsigned long flags; + struct audwmapro_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static long audwmapro_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audwmapro_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audwmapro_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audwmapro_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audwmapro_event, list); + list_del(&drv_evt->list); + } + + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable == enable && !audio->eq_needs_commit) + return 0; + + audio->eq_enable = enable; + + if (audio->running) { + audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq, POPP); + audio->eq_needs_commit = 0; + } + return 0; +} + +static int audio_get_avsync_data(struct audio *audio, + struct msm_audio_stats *stats) +{ + int rc = -EINVAL; + unsigned long flags; + + local_irq_save(flags); + if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) { + /* av_sync sample count */ + stats->sample_count = (audio->avsync[2] << 16) | + (audio->avsync[3]); + + /* av_sync byte_count */ + stats->byte_count = (audio->avsync[5] << 16) | + (audio->avsync[6]); + + audio->avsync_flag = 0; + rc = 0; + } + local_irq_restore(flags); + return rc; + +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + unsigned long flags = 0; + uint16_t enable_mask; + int enable; + int prev_state; + + MM_DBG("cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + audio->avsync_flag = 0; + memset(&stats, 0, sizeof(stats)); + if (audpp_query_avsync(audio->dec_id) < 0) + return rc; + + rc = wait_event_interruptible_timeout(audio->avsync_wait, + (audio->avsync_flag == 1), + msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT)); + + if (rc < 0) + return rc; + else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) { + if (audio_get_avsync_data(audio, &stats) < 0) + return rc; + + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } else + return -EAGAIN; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + enable = (enable_mask & EQ_ENABLE) ? 1 : 0; + audio_enable_eq(audio, enable); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + case AUDIO_SET_VOLUME: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.volume = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_PAN: + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->vol_pan.pan = arg; + if (audio->running) + audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan, + POPP); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + rc = 0; + break; + + case AUDIO_SET_EQ: + prev_state = audio->eq_enable; + audio->eq_enable = 0; + if (copy_from_user(&audio->eq.num_bands, (void *) arg, + sizeof(audio->eq) - + (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) { + rc = -EFAULT; + break; + } + audio->eq_enable = prev_state; + audio->eq_needs_commit = 1; + rc = 0; + break; + } + + if (-EINVAL != rc) + return rc; + + if (cmd == AUDIO_GET_EVENT) { + MM_DBG("AUDIO_GET_EVENT\n"); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audwmapro_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + MM_DBG("AUDIO_START\n"); + rc = audio_enable(audio); + if (!rc) { + rc = wait_event_interruptible_timeout(audio->wait, + audio->dec_state != MSM_AUD_DECODER_STATE_NONE, + msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS)); + MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc); + + if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS) + rc = -ENODEV; + else + rc = 0; + } + break; + case AUDIO_STOP: + MM_DBG("AUDIO_STOP\n"); + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + MM_DBG("AUDIO_FLUSH\n"); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) { + audpp_flush(audio->dec_id); + rc = wait_event_interruptible(audio->write_wait, + !audio->wflush); + if (rc < 0) { + MM_ERR("AUDIO_FLUSH interrupted\n"); + rc = -EINTR; + } + } else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->mfield = config.meta_field; + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = (audio->out_dma_sz >> 1); + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) + config.channel_count = 1; + else + config.channel_count = 2; + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_WMAPRO_CONFIG:{ + if (copy_to_user((void *)arg, &audio->wmapro_config, + sizeof(audio->wmapro_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_WMAPRO_CONFIG:{ + struct msm_audio_wmapro_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + audio->wmapro_config = usr_config; + + /* Need to swap the first and last words of advancedencodeopt2 + * as DSP cannot read 32-bit variable at a time. Need to be + * split into two 16-bit and swap them as required by DSP */ + + audio->wmapro_config.advancedencodeopt2 = + ((audio->wmapro_config.advancedencodeopt2 & 0xFFFF0000) + >> 16) | ((audio->wmapro_config.advancedencodeopt2 + << 16) & 0xFFFF0000); + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = audio->pcm_feedback; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.pcm_feedback != audio->pcm_feedback) { + MM_ERR("Not sufficient permission to" + "change the playback mode\n"); + rc = -EACCES; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + MM_DBG("allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_phys = + allocate_contiguous_ebi_nomap( + config.buffer_size * + config.buffer_count, + SZ_4K); + if (!audio->read_phys) { + rc = -ENOMEM; + break; + } + audio->map_v_read = ioremap( + audio->read_phys, + config.buffer_size * + config.buffer_count); + if (IS_ERR(audio->map_v_read)) { + MM_ERR("read buf map fail\n"); + rc = -ENOMEM; + free_contiguous_memory_by_paddr( + audio->read_phys); + } else { + uint8_t index; + uint32_t offset = 0; + audio->read_data = + audio->map_v_read; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + MM_DBG("read buf: phy addr \ + 0x%08x kernel addr 0x%08x\n", + audio->read_phys, + (int)audio->read_data); + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + MM_DBG("AUDIO_PAUSE %ld\n", arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +static int audio_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + struct buffer *frame; + int rc = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + if (!audio->running || audio->pcm_feedback) { + rc = -EINVAL; + goto done_nolock; + } + + mutex_lock(&audio->write_lock); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (audio->reserved) { + MM_DBG("send reserved byte\n"); + frame = audio->out + audio->out_tail; + ((char *) frame->data)[0] = audio->rsv_byte; + ((char *) frame->data)[1] = 0; + frame->used = 2; + audplay_send_data(audio, 0); + + rc = wait_event_interruptible(audio->write_wait, + (!audio->out[0].used && + !audio->out[1].used && + audio->out_needed) || audio->wflush); + + if (rc < 0) + goto done; + else if (audio->wflush) { + rc = -EBUSY; + goto done; + } + } + + /* pcm dmamiss message is sent continously + * when decoder is starved so no race + * condition concern + */ + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + audio->teos || audio->wflush); + + if (audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); +done_nolock: + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + MM_DBG("%d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped) || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + MM_DBG("audio_read: no partial frame done reading\n"); + break; + } else { + MM_DBG("audio_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + MM_ERR("invalid addr %x \n", (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + break; /* Force to exit while loop + * to prevent output thread + * sleep too long if data is + * not ready at this moment. + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + MM_DBG("kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + MM_DBG("read %d bytes\n", rc); + return rc; +} + +static int audwmapro_process_eos(struct audio *audio, + const char __user *buf_start, unsigned short mfield_size) +{ + int rc = 0; + struct buffer *frame; + char *buf_ptr; + + if (audio->reserved) { + MM_DBG("flush reserve byte\n"); + frame = audio->out + audio->out_head; + buf_ptr = frame->data; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + buf_ptr[0] = audio->rsv_byte; + buf_ptr[1] = 0; + audio->out_head ^= 1; + frame->mfield_sz = 0; + frame->used = 2; + audio->reserved = 0; + audplay_send_data(audio, 0); + } + + frame = audio->out + audio->out_head; + + rc = wait_event_interruptible(audio->write_wait, + (audio->out_needed && + audio->out[0].used == 0 && + audio->out[1].used == 0) + || (audio->stopped) + || (audio->wflush)); + + if (rc < 0) + goto done; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + goto done; + } + + if (copy_from_user(frame->data, buf_start, mfield_size)) { + rc = -EFAULT; + goto done; + } + + frame->mfield_sz = mfield_size; + audio->out_head ^= 1; + frame->used = mfield_size; + audplay_send_data(audio, 0); +done: + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0, eos_condition = AUDWMAPRO_EOS_NONE; + unsigned dsize; + unsigned short mfield_size = 0; + + MM_DBG("cnt=%d\n", count); + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + if (audio->mfield) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (__get_user(mfield_size, + (unsigned short __user *) buf)) { + rc = -EFAULT; + break; + } else if (mfield_size > count) { + rc = -EINVAL; + break; + } + MM_DBG("audio_write: mf offset_val %x\n", + mfield_size); + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + if (cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET] & + AUDWMAPRO_EOS_FLG_MASK) { + MM_DBG("audio_write: EOS SET\n"); + eos_condition = AUDWMAPRO_EOS_SET; + if (mfield_size == count) { + buf += mfield_size; + break; + } else + cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET] + &= ~AUDWMAPRO_EOS_FLG_MASK; + } + cpy_ptr += mfield_size; + count -= mfield_size; + dsize += mfield_size; + buf += mfield_size; + } else { + mfield_size = 0; + MM_DBG("audio_write: continuous buffer\n"); + } + frame->mfield_sz = mfield_size; + } + + if (audio->reserved) { + MM_DBG("append reserved byte %x\n", audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > ((frame->size - mfield_size) - 1)) ? + (frame->size - mfield_size) - 1 : count; + cpy_ptr++; + dsize += 1; + audio->reserved = 0; + } else + xfer = (count > (frame->size - mfield_size)) ? + (frame->size - mfield_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + MM_DBG("odd length buf reserve last byte %x\n", + audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + if (eos_condition == AUDWMAPRO_EOS_SET) + rc = audwmapro_process_eos(audio, start, mfield_size); + mutex_unlock(&audio->write_lock); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + MM_INFO("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + audio_disable(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audpp_adec_free(audio->dec_id); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->event_abort = 1; + wake_up(&audio->event_wait); + audwmapro_reset_event_queue(audio); + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + if (audio->read_data) { + iounmap(audio->map_v_read); + free_contiguous_memory_by_paddr(audio->read_phys); + } + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audwmapro_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audwmapro_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audwmapro_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audwmapro_event), GFP_ATOMIC); + if (!e_node) { + MM_ERR("No mem to post event %d\n", type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audwmapro_suspend(struct early_suspend *h) +{ + struct audwmapro_suspend_ctl *ctl = + container_of(h, struct audwmapro_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwmapro_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audwmapro_resume(struct early_suspend *h) +{ + struct audwmapro_suspend_ctl *ctl = + container_of(h, struct audwmapro_suspend_ctl, node); + union msm_audio_event_payload payload; + + MM_DBG("\n"); /* Macro prints the file name and function */ + audwmapro_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audwmapro_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audwmapro_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0, i; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_feedback %d\n", audio->pcm_feedback); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_buf_sz %d\n", audio->out[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_count %d \n", audio->pcm_buf_count); + n += scnprintf(buffer + n, debug_bufmax - n, + "pcm_buf_sz %d \n", audio->in[0].size); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x \n", audio->vol_pan.volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d \n", audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d \n", audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d \n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "dec state %d \n", audio->dec_state); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d \n", audio->out_needed); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_head %d \n", audio->out_head); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_tail %d \n", audio->out_tail); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[0].used %d \n", audio->out[0].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "out[1].used %d \n", audio->out[1].used); + n += scnprintf(buffer + n, debug_bufmax - n, + "buffer_refresh %d \n", audio->buf_refresh); + n += scnprintf(buffer + n, debug_bufmax - n, + "read_next %d \n", audio->read_next); + n += scnprintf(buffer + n, debug_bufmax - n, + "fill_next %d \n", audio->fill_next); + for (i = 0; i < audio->pcm_buf_count; i++) + n += scnprintf(buffer + n, debug_bufmax - n, + "in[%d].size %d \n", i, audio->in[i].used); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audwmapro_debug_fops = { + .read = audwmapro_debug_read, + .open = audwmapro_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, dec_attrb, decid, i; + unsigned pmem_sz = DMASZ_MAX; + struct audwmapro_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + + /* Allocate Mem for audio instance */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + MM_ERR("no memory to allocate audio instance \n"); + rc = -ENOMEM; + goto done; + } + MM_INFO("audio instance 0x%08x created\n", (int)audio); + + /* Allocate the decoder */ + dec_attrb = AUDDEC_DEC_WMAPRO; + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_NONTUNNEL; + audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + dec_attrb |= MSM_AUD_MODE_TUNNEL; + audio->pcm_feedback = TUNNEL_MODE_PLAYBACK; + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + decid = audpp_adec_alloc(dec_attrb, &audio->module_name, + &audio->queue_id); + + if (decid < 0) { + MM_ERR("No free decoder available, freeing instance 0x%08x\n", + (int)audio); + rc = -ENODEV; + kfree(audio); + goto done; + } + audio->dec_id = decid & MSM_AUD_DECODER_MASK; + + while (pmem_sz >= DMASZ_MIN) { + MM_DBG("pmemsz = %d\n", pmem_sz); + audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K); + if (audio->phys) { + audio->map_v_write = ioremap(audio->phys, pmem_sz); + if (IS_ERR(audio->map_v_write)) { + MM_ERR("could not map write buffers, \ + freeing instance 0x%08x\n", + (int)audio); + rc = -ENOMEM; + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } + audio->data = audio->map_v_write; + MM_DBG("write buf: phy addr 0x%08x kernel addr \ + 0x%08x\n", audio->phys, (int)audio->data); + break; + } else if (pmem_sz == DMASZ_MIN) { + MM_ERR("could not allocate write buffers, freeing \ + instance 0x%08x\n", (int)audio); + rc = -ENOMEM; + audpp_adec_free(audio->dec_id); + kfree(audio); + goto done; + } else + pmem_sz >>= 1; + } + audio->out_dma_sz = pmem_sz; + + rc = msm_adsp_get(audio->module_name, &audio->audplay, + &audplay_adsp_ops_wmapro, audio); + if (rc) { + MM_ERR("failed to get %s module, freeing instance 0x%08x\n", + audio->module_name, (int)audio); + goto err; + } + + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->read_wait); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->avsync_wait); + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = audio->out_dma_sz >> 1; + + audio->out[1].data = audio->data + audio->out[0].size; + audio->out[1].addr = audio->phys + audio->out[0].size; + audio->out[1].size = audio->out[0].size; + + /*audio->wmapro_config.armdatareqthr = 1268; + audio->wmapro_config.numchannels = 2; + audio->wmapro_config.avgbytespersecond = 6003; + audio->wmapro_config.samplingrate = 44100; + audio->wmapro_config.encodeopt = 224; + audio->wmapro_config.validbitspersample = 16; + audio->wmapro_config.formattag = 354; + audio->wmapro_config.asfpacketlength = 2230; + audio->wmapro_config.channelmask = 3; + audio->wmapro_config.advancedencodeopt = 32834; + audio->wmapro_config.advancedencodeopt2 = 0;*/ + + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + + audio->vol_pan.volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + wmapro_listner, + (void *)audio); + if (rc) { + MM_ERR("%s: failed to register listner\n", __func__); + goto event_err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wmapro_%04x", audio->dec_id); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, + &audwmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + MM_DBG("debugfs_create_file failed\n"); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audwmapro_resume; + audio->suspend_ctl.node.suspend = audwmapro_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDWMAPRO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audwmapro_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + MM_ERR("event pkt alloc failed\n"); + break; + } + } +done: + return rc; +event_err: + msm_adsp_put(audio->audplay); +err: + iounmap(audio->map_v_write); + free_contiguous_memory_by_paddr(audio->phys); + audpp_adec_free(audio->dec_id); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_fsync, +}; + +struct miscdevice audio_wmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&audio_wmapro_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audpp.c b/arch/arm/mach-msm/qdsp5v2/audpp.c new file mode 100644 index 00000000000..31ce6431442 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audpp.c @@ -0,0 +1,1140 @@ +/* arch/arm/mach-msm/qdsp5/audpp.c + * + * common code to deal with the AUDPP dsp task (audio postproc) + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../qdsp5/evlog.h" +#include + +enum { + EV_NULL, + EV_ENABLE, + EV_DISABLE, + EV_EVENT, + EV_DATA, +}; + +static const char *dsp_log_strings[] = { + "NULL", + "ENABLE", + "DISABLE", + "EVENT", + "DATA", +}; + +DECLARE_LOG(dsp_log, 64, dsp_log_strings); + +static int __init _dsp_log_init(void) +{ + return ev_log_init(&dsp_log); +} + +module_init(_dsp_log_init); +#define LOG(id, arg) ev_log_write(&dsp_log, id, arg) + +static DEFINE_MUTEX(audpp_lock); +static DEFINE_MUTEX(audpp_dec_lock); +static struct wake_lock audpp_wake_lock; + +#define CH_COUNT 5 +#define AUDPP_CLNT_MAX_COUNT 6 + +#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 +#define AUDPP_CMD_EQ_FLAG_DIS 0x0000 +#define AUDPP_CMD_EQ_FLAG_ENA -1 +#define AUDPP_CMD_IIR_FLAG_DIS 0x0000 +#define AUDPP_CMD_IIR_FLAG_ENA -1 +#define AUDPP_CMD_STF_FLAG_ENA -1 +#define AUDPP_CMD_STF_FLAG_DIS 0x0000 + +#define MAX_EVENT_CALLBACK_CLIENTS 1 + +#define AUDPP_CONCURRENCY_DEFAULT 0 /* Set default to LPA mode */ +#define AUDPP_MAX_DECODER_CNT 5 +#define AUDPP_CODEC_MASK 0x000000FF +#define AUDPP_MODE_MASK 0x00000F00 +#define AUDPP_OP_MASK 0xF0000000 + +struct audpp_decoder_info { + unsigned int codec; + pid_t pid; +}; + +struct audpp_state { + struct msm_adsp_module *mod; + audpp_event_func func[AUDPP_CLNT_MAX_COUNT]; + void *private[AUDPP_CLNT_MAX_COUNT]; + struct mutex *lock; + unsigned open_count; + unsigned enabled; + + /* Related to decoder allocation */ + struct mutex *lock_dec; + struct msm_adspdec_database *dec_database; + struct audpp_decoder_info dec_info_table[AUDPP_MAX_DECODER_CNT]; + unsigned dec_inuse; + unsigned long concurrency; + + struct audpp_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS]; + + /* Related to decoder instances */ + uint8_t op_mode; /* Specifies Turbo/Non Turbo mode */ + uint8_t decoder_count; /* No. of decoders active running */ + uint8_t codec_max_instances; /* Max codecs allowed currently */ + uint8_t codec_cnt[MSM_MAX_DEC_CNT]; /* Nr of each codec + type enabled */ + + wait_queue_head_t event_wait; +}; + +struct audpp_state the_audpp_state = { + .lock = &audpp_lock, + .lock_dec = &audpp_dec_lock, +}; + +static inline void prevent_suspend(void) +{ + wake_lock(&audpp_wake_lock); +} +static inline void allow_suspend(void) +{ + wake_unlock(&audpp_wake_lock); +} + +int audpp_send_queue1(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd1Queue, cmd, len); +} +EXPORT_SYMBOL(audpp_send_queue1); + +int audpp_send_queue2(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd2Queue, cmd, len); +} +EXPORT_SYMBOL(audpp_send_queue2); + +int audpp_send_queue3(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd3Queue, cmd, len); +} +EXPORT_SYMBOL(audpp_send_queue3); + +static int audpp_dsp_config(int enable) +{ + struct audpp_cmd_cfg cmd; + + cmd.cmd_id = AUDPP_CMD_CFG; + cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +void audpp_route_stream(unsigned short dec_id, unsigned short mixer_mask) +{ + struct audpp_cmd_cfg_dev_mixer_params mixer_params_cmd; + + memset(&mixer_params_cmd, 0, sizeof(mixer_params_cmd)); + + mixer_params_cmd.cmd_id = AUDPP_CMD_CFG_DEV_MIXER; + mixer_params_cmd.stream_id = dec_id; + mixer_params_cmd.mixer_cmd = mixer_mask; + audpp_send_queue1(&mixer_params_cmd, sizeof(mixer_params_cmd)); + +} +EXPORT_SYMBOL(audpp_route_stream); + +int is_audpp_enable(void) +{ + struct audpp_state *audpp = &the_audpp_state; + + return audpp->enabled; +} +EXPORT_SYMBOL(is_audpp_enable); + +int audpp_register_event_callback(struct audpp_event_callback *ecb) +{ + struct audpp_state *audpp = &the_audpp_state; + int i; + + for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { + if (NULL == audpp->cb_tbl[i]) { + audpp->cb_tbl[i] = ecb; + return 0; + } + } + return -1; +} +EXPORT_SYMBOL(audpp_register_event_callback); + + +int audpp_unregister_event_callback(struct audpp_event_callback *ecb) +{ + struct audpp_state *audpp = &the_audpp_state; + int i; + + for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { + if (ecb == audpp->cb_tbl[i]) { + audpp->cb_tbl[i] = NULL; + return 0; + } + } + return -1; +} +EXPORT_SYMBOL(audpp_unregister_event_callback); + +static void audpp_broadcast(struct audpp_state *audpp, unsigned id, + uint16_t *msg) +{ + unsigned n; + for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) { + if (audpp->func[n]) + audpp->func[n] (audpp->private[n], id, msg); + } + + for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) + if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn) + audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, id, + msg); +} + +static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id, + unsigned id, uint16_t *msg) +{ + if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id]) + audpp->func[clnt_id] (audpp->private[clnt_id], id, msg); +} + +static void audpp_handle_pcmdmamiss(struct audpp_state *audpp, + uint16_t bit_mask) +{ + uint8_t b_index; + + for (b_index = 0; b_index < AUDPP_CLNT_MAX_COUNT; b_index++) { + if (bit_mask & (0x1 << b_index)) + if (audpp->func[b_index]) + audpp->func[b_index] (audpp->private[b_index], + AUDPP_MSG_PCMDMAMISSED, + &bit_mask); + } +} + +static void audpp_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audpp_state *audpp = data; + uint16_t msg[8]; + + getevent(msg, sizeof(msg)); + + LOG(EV_EVENT, (id << 16) | msg[0]); + LOG(EV_DATA, (msg[1] << 16) | msg[2]); + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned cid = msg[0]; + MM_DBG("status %d %d %d\n", cid, msg[1], msg[2]); + + if ((cid < 5) && audpp->func[cid]) + audpp->func[cid] (audpp->private[cid], id, msg); + break; + } + case AUDPP_MSG_HOST_PCM_INTF_MSG: + if (audpp->func[5]) + audpp->func[5] (audpp->private[5], id, msg); + break; + case AUDPP_MSG_PCMDMAMISSED: + audpp_handle_pcmdmamiss(audpp, msg[0]); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + MM_INFO("ENABLE\n"); + audpp->enabled = 1; + audpp_broadcast(audpp, id, msg); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + MM_INFO("DISABLE\n"); + audpp->enabled = 0; + wake_up(&audpp->event_wait); + audpp_broadcast(audpp, id, msg); + } else { + MM_ERR("invalid config msg %d\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + audpp_notify_clnt(audpp, msg[0], id, msg); + break; + case AUDPP_MSG_FLUSH_ACK: + audpp_notify_clnt(audpp, msg[0], id, msg); + break; + case ADSP_MESSAGE_ID: + MM_DBG("Received ADSP event: module enable/disable \ + (audpptask)"); + break; + case AUDPP_MSG_AVSYNC_MSG: + audpp_notify_clnt(audpp, msg[0], id, msg); + break; +#ifdef CONFIG_DEBUG_FS + case AUDPP_MSG_FEAT_QUERY_DM_DONE: + MM_INFO(" RTC ACK --> %x %x %x %x %x %x %x %x\n", msg[0],\ + msg[1], msg[2], msg[3], msg[4], \ + msg[5], msg[6], msg[7]); + acdb_rtc_set_err(msg[3]); + break; +#endif + default: + MM_INFO("unhandled msg id %x\n", id); + } +} + +static struct msm_adsp_ops adsp_ops = { + .event = audpp_dsp_event, +}; + +static void audpp_fake_event(struct audpp_state *audpp, int id, + unsigned event, unsigned arg) +{ + uint16_t msg[1]; + uint16_t n = 0; + msg[0] = arg; + audpp->func[id] (audpp->private[id], event, msg); + if (audpp->enabled == 1) { + for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) + if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn) + audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, + AUDPP_MSG_CFG_MSG, msg); + } +} + +int audpp_enable(int id, audpp_event_func func, void *private) +{ + struct audpp_state *audpp = &the_audpp_state; + int res = 0; + + if (id < -1 || id > 4) + return -EINVAL; + + if (id == -1) + id = 5; + + mutex_lock(audpp->lock); + if (audpp->func[id]) { + res = -EBUSY; + goto out; + } + + audpp->func[id] = func; + audpp->private[id] = private; + + LOG(EV_ENABLE, 1); + if (audpp->open_count++ == 0) { + MM_DBG("enable\n"); + res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp); + if (res < 0) { + MM_ERR("audpp: cannot open AUDPPTASK\n"); + audpp->open_count = 0; + audpp->func[id] = NULL; + audpp->private[id] = NULL; + goto out; + } + LOG(EV_ENABLE, 2); + prevent_suspend(); + msm_adsp_enable(audpp->mod); + audpp_dsp_config(1); + } else { + unsigned long flags; + local_irq_save(flags); + if (audpp->enabled) + audpp_fake_event(audpp, id, + AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA); + local_irq_restore(flags); + } + + res = 0; +out: + mutex_unlock(audpp->lock); + return res; +} +EXPORT_SYMBOL(audpp_enable); + +void audpp_disable(int id, void *private) +{ + struct audpp_state *audpp = &the_audpp_state; + unsigned long flags; + int rc; + + if (id < -1 || id > 4) + return; + + if (id == -1) + id = 5; + + mutex_lock(audpp->lock); + LOG(EV_DISABLE, 1); + if (!audpp->func[id]) + goto out; + if (audpp->private[id] != private) + goto out; + + local_irq_save(flags); + audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS); + audpp->func[id] = NULL; + audpp->private[id] = NULL; + local_irq_restore(flags); + + if (--audpp->open_count == 0) { + MM_DBG("disable\n"); + LOG(EV_DISABLE, 2); + audpp_dsp_config(0); + rc = wait_event_interruptible(audpp->event_wait, + (audpp->enabled == 0)); + if (audpp->enabled == 0) + MM_INFO("Received CFG_MSG_DISABLE from ADSP\n"); + else + MM_ERR("Didn't receive CFG_MSG DISABLE \ + message from ADSP\n"); + msm_adsp_disable(audpp->mod); + msm_adsp_put(audpp->mod); + audpp->mod = NULL; + allow_suspend(); + } +out: + mutex_unlock(audpp->lock); +} +EXPORT_SYMBOL(audpp_disable); + +#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT)) + +int audpp_restore_avsync(int id, uint16_t *avsync) +{ + struct audpp_cmd_avsync cmd; + + if (BAD_ID(id)) + return -1; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_AVSYNC; + cmd.stream_id = id; + cmd.interrupt_interval = 0; /* Setting it to Zero as there won't be + periodic update */ + cmd.sample_counter_dlsw = avsync[3]; + cmd.sample_counter_dmsw = avsync[2]; + cmd.sample_counter_msw = avsync[1]; + cmd.byte_counter_dlsw = avsync[6]; + cmd.byte_counter_dmsw = avsync[5]; + cmd.byte_counter_msw = avsync[4]; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_restore_avsync); + +int audpp_query_avsync(int id) +{ + struct audpp_cmd_query_avsync cmd; + + if (BAD_ID(id)) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_QUERY_AVSYNC; + cmd.stream_id = id; + return audpp_send_queue1(&cmd, sizeof(cmd)); + +} +EXPORT_SYMBOL(audpp_query_avsync); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan, + enum obj_type objtype) +{ + /* cmd, obj_cfg[7], cmd_type, volume, pan */ + uint16_t cmd[7]; + + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + cmd[1] = AUDPP_CMD_POPP_STREAM; + else + cmd[1] = AUDPP_CMD_COPP_STREAM; + cmd[2] = id; + cmd[3] = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd[4] = AUDPP_CMD_VOLUME_PAN; + cmd[5] = volume; + cmd[6] = pan; + + return audpp_send_queue3(cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_set_volume_and_pan); + +/* Implementation of COPP features */ +int audpp_dsp_set_mbadrc(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_mbadrc *mbadrc, + enum obj_type objtype) +{ + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + mbadrc->common.stream = AUDPP_CMD_POPP_STREAM; + else + mbadrc->common.stream = AUDPP_CMD_COPP_STREAM; + + mbadrc->common.stream_id = id; + mbadrc->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + mbadrc->common.command_type = AUDPP_CMD_MBADRC; + + if (enable) + mbadrc->enable = AUDPP_CMD_ADRC_FLAG_ENA; + else + mbadrc->enable = AUDPP_CMD_ADRC_FLAG_DIS; + + return audpp_send_queue3(mbadrc, + sizeof(struct audpp_cmd_cfg_object_params_mbadrc)); +} +EXPORT_SYMBOL(audpp_dsp_set_mbadrc); + +int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_qconcert *qconcert_plus, + enum obj_type objtype) +{ + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + qconcert_plus->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + qconcert_plus->common.stream = AUDPP_CMD_POPP_STREAM; + else + qconcert_plus->common.stream = AUDPP_CMD_COPP_STREAM; + + qconcert_plus->common.stream_id = id; + qconcert_plus->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + qconcert_plus->common.command_type = AUDPP_CMD_QCONCERT; + + if (enable) + qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_ENA; + else + qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_DIS; + + return audpp_send_queue3(qconcert_plus, + sizeof(struct audpp_cmd_cfg_object_params_qconcert)); +} +EXPORT_SYMBOL(audpp_dsp_set_qconcert_plus); + +int audpp_dsp_set_rx_iir(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_pcm *iir, + enum obj_type objtype) +{ + + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + iir->common.stream = AUDPP_CMD_POPP_STREAM; + else + iir->common.stream = AUDPP_CMD_COPP_STREAM; + + iir->common.stream_id = id; + iir->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + iir->common.command_type = AUDPP_CMD_IIR_TUNING_FILTER; + + if (enable) + iir->active_flag = AUDPP_CMD_IIR_FLAG_ENA; + else + iir->active_flag = AUDPP_CMD_IIR_FLAG_DIS; + + return audpp_send_queue3(iir, + sizeof(struct audpp_cmd_cfg_object_params_pcm)); +} +EXPORT_SYMBOL(audpp_dsp_set_rx_iir); + +int audpp_dsp_set_gain_rx(unsigned id, + struct audpp_cmd_cfg_cal_gain *calib_gain_rx, + enum obj_type objtype) +{ + if (objtype) { + return -EINVAL; + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM; + + calib_gain_rx->common.stream_id = id; + calib_gain_rx->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + calib_gain_rx->common.command_type = AUDPP_CMD_CALIB_GAIN_RX; + + return audpp_send_queue3(calib_gain_rx, + sizeof(struct audpp_cmd_cfg_cal_gain)); +} +EXPORT_SYMBOL(audpp_dsp_set_gain_rx); + +int audpp_dsp_set_pbe(unsigned id, unsigned enable, + struct audpp_cmd_cfg_pbe *pbe_block, + enum obj_type objtype) +{ + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + pbe_block->common.stream = AUDPP_CMD_POPP_STREAM; + else + pbe_block->common.stream = AUDPP_CMD_COPP_STREAM; + + pbe_block->common.stream_id = id; + pbe_block->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + pbe_block->common.command_type = AUDPP_CMD_PBE; + + if (enable) + pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_ENA; + else + pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_DIS; + + return audpp_send_queue3(pbe_block, + sizeof(struct audpp_cmd_cfg_pbe)); +} +EXPORT_SYMBOL(audpp_dsp_set_pbe); + +int audpp_dsp_set_spa(unsigned id, + struct audpp_cmd_cfg_object_params_spectram *spa, + enum obj_type objtype){ + struct audpp_cmd_cfg_object_params_spectram cmd; + + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + memset(&cmd, 0, sizeof(cmd)); + if (objtype) + cmd.common.stream = AUDPP_CMD_POPP_STREAM; + else + cmd.common.stream = AUDPP_CMD_COPP_STREAM; + + cmd.common.stream_id = id; + cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_SPECTROGRAM; + cmd.sample_interval = spa->sample_interval; + cmd.num_coeff = spa->num_coeff; + return audpp_send_queue3(&cmd, sizeof(cmd)); + +} +EXPORT_SYMBOL(audpp_dsp_set_spa); + +int audpp_dsp_set_stf(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_sidechain *stf, + enum obj_type objtype){ + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + stf->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + stf->common.stream = AUDPP_CMD_POPP_STREAM; + else + stf->common.stream = AUDPP_CMD_COPP_STREAM; + + stf->common.stream_id = id; + stf->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + stf->common.command_type = AUDPP_CMD_SIDECHAIN_TUNING_FILTER; + + if (enable) + stf->active_flag = AUDPP_CMD_STF_FLAG_ENA; + else + stf->active_flag = AUDPP_CMD_STF_FLAG_DIS; + return audpp_send_queue3(stf, + sizeof(struct audpp_cmd_cfg_object_params_sidechain)); +} +EXPORT_SYMBOL(audpp_dsp_set_stf); + +/* Implementation Of COPP + POPP */ +int audpp_dsp_set_eq(unsigned id, unsigned enable, + struct audpp_cmd_cfg_object_params_eqalizer *eq, + enum obj_type objtype) +{ + struct audpp_cmd_cfg_object_params_eqalizer cmd; + + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > 3) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + memset(&cmd, 0, sizeof(cmd)); + if (objtype) + cmd.common.stream = AUDPP_CMD_POPP_STREAM; + else + cmd.common.stream = AUDPP_CMD_COPP_STREAM; + + cmd.common.stream_id = id; + cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_EQUALIZER; + if (enable) { + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA; + cmd.num_bands = eq->num_bands; + memcpy(&cmd.eq_coeff, &eq->eq_coeff, sizeof(eq->eq_coeff)); + } else + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS; + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_eq); + +int audpp_dsp_set_vol_pan(unsigned id, + struct audpp_cmd_cfg_object_params_volume *vol_pan, + enum obj_type objtype) +{ + struct audpp_cmd_cfg_object_params_volume cmd; + + if (objtype) { + if (id > 5) { + MM_ERR("Wrong POPP decoder id: %d\n", id); + return -EINVAL; + } + } else { + if (id > AUDPP_MAX_COPP_DEVICES) { + MM_ERR("Wrong COPP decoder id: %d\n", id); + return -EINVAL; + } + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS; + if (objtype) + cmd.common.stream = AUDPP_CMD_POPP_STREAM; + else + cmd.common.stream = AUDPP_CMD_COPP_STREAM; + + cmd.common.stream_id = id; + cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_VOLUME_PAN; + + cmd.volume = vol_pan->volume; + cmd.pan = vol_pan->pan; + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} +EXPORT_SYMBOL(audpp_dsp_set_vol_pan); + +int audpp_pause(unsigned id, int pause) +{ + /* pause 1 = pause 0 = resume */ + u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)]; + + if (id >= CH_COUNT) + return -EINVAL; + + memset(pause_cmd, 0, sizeof(pause_cmd)); + + pause_cmd[0] = AUDPP_CMD_DEC_CTRL; + pause_cmd[1] = id; + if (pause == 1) + pause_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V; + else if (pause == 0) + pause_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V; + else + return -EINVAL; + + return audpp_send_queue1(pause_cmd, sizeof(pause_cmd)); +} +EXPORT_SYMBOL(audpp_pause); + +int audpp_flush(unsigned id) +{ + u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)]; + + if (id >= CH_COUNT) + return -EINVAL; + + memset(flush_cmd, 0, sizeof(flush_cmd)); + + flush_cmd[0] = AUDPP_CMD_DEC_CTRL; + flush_cmd[1] = id; + flush_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V; + + return audpp_send_queue1(flush_cmd, sizeof(flush_cmd)); +} +EXPORT_SYMBOL(audpp_flush); + +/* dec_attrb = 7:0, 0 - No Decoder, else supported decoder * + * like mp3, aac, wma etc ... * + * = 15:8, bit[8] = 1 - Tunnel, bit[9] = 1 - NonTunnel * + * = 31:16, reserved */ +int audpp_adec_alloc(unsigned dec_attrb, const char **module_name, + unsigned *queueid) +{ + struct audpp_state *audpp = &the_audpp_state; + int decid = -1, idx, lidx, mode, codec; + int codecs_supported, min_codecs_supported; + unsigned int *concurrency_entry; + u8 max_instance, codec_type; + + struct dec_instance_table *dec_instance_list; + dec_instance_list = (struct dec_instance_table *) + (audpp->dec_database->dec_instance_list); + + mutex_lock(audpp->lock_dec); + /* Represents in bit mask */ + mode = ((dec_attrb & AUDPP_MODE_MASK) << 16); + codec = (1 << (dec_attrb & AUDPP_CODEC_MASK)); + codec_type = (dec_attrb & AUDPP_CODEC_MASK); + + /* Find whether same/different codec instances are running */ + audpp->decoder_count++; + audpp->codec_cnt[codec_type]++; + max_instance = 0; + + /*if different instance of codec*/ + if (audpp->codec_cnt[codec_type] < audpp->decoder_count) { + max_instance = audpp->codec_max_instances; + /* Get the maximum no. of instances that can be supported */ + for (idx = 0; idx < MSM_MAX_DEC_CNT; idx++) { + if (audpp->codec_cnt[idx]) { + if ((dec_instance_list + + audpp->op_mode * MSM_MAX_DEC_CNT + + idx)-> + max_instances_diff_dec < + max_instance) { + max_instance = + (dec_instance_list + + audpp->op_mode * + MSM_MAX_DEC_CNT + + idx)-> + max_instances_diff_dec; + } + } + } + /* if different codec type, should not cross maximum other + supported */ + if (audpp->decoder_count > (max_instance + 1)) { + MM_ERR("Can not support, already reached max\n"); + audpp->decoder_count--; + audpp->codec_cnt[codec_type]--; + goto done; + } + audpp->codec_max_instances = max_instance; + MM_DBG("different codec running\n"); + } else { + max_instance = (dec_instance_list + audpp->op_mode * + MSM_MAX_DEC_CNT + + codec_type)-> + max_instances_same_dec; + /* if same codec type, should not cross maximum supported */ + if (audpp->decoder_count > max_instance) { + MM_ERR("Can not support, already reached max\n"); + audpp->decoder_count--; + audpp->codec_cnt[codec_type]--; + goto done; + } + audpp->codec_max_instances = max_instance; + MM_DBG("same codec running\n"); + } + + /* Point to Last entry of the row */ + concurrency_entry = ((audpp->dec_database->dec_concurrency_table + + ((audpp->concurrency + 1) * + (audpp->dec_database->num_dec))) - 1); + + lidx = audpp->dec_database->num_dec; + min_codecs_supported = sizeof(unsigned int) * 8; + + MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec); + + for (idx = lidx; idx > 0; idx--, concurrency_entry--) { + if (!(audpp->dec_inuse & (1 << (idx - 1)))) { + if (((mode & *concurrency_entry) == mode) && + (codec & *concurrency_entry)) { + /* Check supports minimum number codecs */ + codecs_supported = + audpp->dec_database->dec_info_list[idx - + 1]. + nr_codec_support; + if (codecs_supported < min_codecs_supported) { + lidx = idx - 1; + min_codecs_supported = codecs_supported; + } + } + } + } + + if (lidx < audpp->dec_database->num_dec) { + audpp->dec_inuse |= (1 << lidx); + *module_name = + audpp->dec_database->dec_info_list[lidx].module_name; + *queueid = + audpp->dec_database->dec_info_list[lidx].module_queueid; + decid = audpp->dec_database->dec_info_list[lidx].module_decid; + audpp->dec_info_table[lidx].codec = + (dec_attrb & AUDPP_CODEC_MASK); + audpp->dec_info_table[lidx].pid = current->pid; + /* point to row to get supported operation */ + concurrency_entry = + ((audpp->dec_database->dec_concurrency_table + + ((audpp->concurrency) * (audpp->dec_database->num_dec))) + + lidx); + decid |= ((*concurrency_entry & AUDPP_OP_MASK) >> 12); + MM_INFO("decid =0x%08x module_name=%s, queueid=%d \n", decid, + *module_name, *queueid); + } +done: + mutex_unlock(audpp->lock_dec); + return decid; + +} +EXPORT_SYMBOL(audpp_adec_alloc); + +void audpp_adec_free(int decid) +{ + struct audpp_state *audpp = &the_audpp_state; + int idx; + mutex_lock(audpp->lock_dec); + for (idx = audpp->dec_database->num_dec; idx > 0; idx--) { + if (audpp->dec_database->dec_info_list[idx - 1].module_decid == + decid) { + audpp->decoder_count--; + audpp->\ + codec_cnt[audpp->dec_info_table[idx - 1].codec]--; + audpp->dec_inuse &= ~(1 << (idx - 1)); + audpp->dec_info_table[idx - 1].codec = -1; + audpp->dec_info_table[idx - 1].pid = 0; + MM_INFO("free decid =%d \n", decid); + break; + } + } + mutex_unlock(audpp->lock_dec); + return; + +} +EXPORT_SYMBOL(audpp_adec_free); + +static ssize_t concurrency_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct audpp_state *audpp = &the_audpp_state; + int rc; + mutex_lock(audpp->lock_dec); + rc = sprintf(buf, "%ld\n", audpp->concurrency); + mutex_unlock(audpp->lock_dec); + return rc; +} + +static ssize_t concurrency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct audpp_state *audpp = &the_audpp_state; + unsigned long concurrency; + int rc = -1; + mutex_lock(audpp->lock_dec); + if (audpp->dec_inuse) { + MM_ERR("Can not change profile, while playback in progress\n"); + goto done; + } + rc = strict_strtoul(buf, 10, &concurrency); + if (!rc && + (concurrency < audpp->dec_database->num_concurrency_support)) { + audpp->concurrency = concurrency; + MM_DBG("Concurrency case %ld\n", audpp->concurrency); + rc = count; + } else { + MM_ERR("Not a valid Concurrency case\n"); + rc = -EINVAL; + } +done: + mutex_unlock(audpp->lock_dec); + return rc; +} + +static ssize_t decoder_info_show(struct device *dev, + struct device_attribute *attr, char *buf); +static struct device_attribute dev_attr_decoder[AUDPP_MAX_DECODER_CNT] = { + __ATTR(decoder0, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder1, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder2, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder3, S_IRUGO, decoder_info_show, NULL), + __ATTR(decoder4, S_IRUGO, decoder_info_show, NULL), +}; + +static ssize_t decoder_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cpy_sz = 0; + struct audpp_state *audpp = &the_audpp_state; + const ptrdiff_t off = attr - dev_attr_decoder; /* decoder number */ + mutex_lock(audpp->lock_dec); + cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d:", + audpp->dec_info_table[off].codec); + cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d\n", + audpp->dec_info_table[off].pid); + mutex_unlock(audpp->lock_dec); + return cpy_sz; +} + +static DEVICE_ATTR(concurrency, S_IWUSR | S_IRUGO, concurrency_show, + concurrency_store); +static int audpp_probe(struct platform_device *pdev) +{ + int rc, idx; + struct audpp_state *audpp = &the_audpp_state; + audpp->concurrency = AUDPP_CONCURRENCY_DEFAULT; + audpp->dec_database = + (struct msm_adspdec_database *)pdev->dev.platform_data; + + MM_INFO("Number of decoder supported %d\n", + audpp->dec_database->num_dec); + MM_INFO("Number of concurrency supported %d\n", + audpp->dec_database->num_concurrency_support); + init_waitqueue_head(&audpp->event_wait); + for (idx = 0; idx < audpp->dec_database->num_dec; idx++) { + audpp->dec_info_table[idx].codec = -1; + audpp->dec_info_table[idx].pid = 0; + MM_INFO("module_name:%s\n", + audpp->dec_database->dec_info_list[idx].module_name); + MM_INFO("queueid:%d\n", + audpp->dec_database->dec_info_list[idx].module_queueid); + MM_INFO("decid:%d\n", + audpp->dec_database->dec_info_list[idx].module_decid); + MM_INFO("nr_codec_support:%d\n", + audpp->dec_database->dec_info_list[idx]. + nr_codec_support); + } + + wake_lock_init(&audpp_wake_lock, WAKE_LOCK_SUSPEND, "audpp"); + for (idx = 0; idx < audpp->dec_database->num_dec; idx++) { + rc = device_create_file(&pdev->dev, &dev_attr_decoder[idx]); + if (rc) + goto err; + } + rc = device_create_file(&pdev->dev, &dev_attr_concurrency); + audpp->op_mode = 0; /* Consider as non turbo mode */ + if (rc) + goto err; + else + goto done; +err: + while (idx--) + device_remove_file(&pdev->dev, &dev_attr_decoder[idx]); +done: + return rc; +} + +static struct platform_driver audpp_plat_driver = { + .probe = audpp_probe, + .driver = { + .name = "msm_adspdec", + .owner = THIS_MODULE, + }, +}; + +static int __init audpp_init(void) +{ + return platform_driver_register(&audpp_plat_driver); +} + +device_initcall(audpp_init); diff --git a/arch/arm/mach-msm/qdsp5v2/audpreproc.c b/arch/arm/mach-msm/qdsp5v2/audpreproc.c new file mode 100644 index 00000000000..c9abdab688e --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audpreproc.c @@ -0,0 +1,527 @@ +/* + * Common code to deal with the AUDPREPROC dsp task (audio preprocessing) + * + * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(audpreproc_lock); +static struct wake_lock audpre_wake_lock; +static struct pm_qos_request audpre_pm_qos_req; + +struct msm_adspenc_info { + const char *module_name; + unsigned module_queueids; + int module_encid; /* streamid */ + int enc_formats; /* supported formats */ + int nr_codec_support; /* number of codec suported */ +}; + +#define ENC_MODULE_INFO(name, queueids, encid, formats, nr_codec) \ + {.module_name = name, .module_queueids = queueids, \ + .module_encid = encid, .enc_formats = formats, \ + .nr_codec_support = nr_codec } + +#define MAX_EVENT_CALLBACK_CLIENTS 1 + +#define ENC0_FORMAT ((1<func[cfg_done_msg.stream_id]) + audpreproc->func[cfg_done_msg.stream_id]( + audpreproc->private[cfg_done_msg.stream_id], id, + &cfg_done_msg); + break; + } + case AUDPREPROC_ERROR_MSG: { + struct audpreproc_err_msg err_msg; + + getevent(&err_msg, AUDPREPROC_ERROR_MSG_LEN); + MM_DBG("AUDPREPROC_ERROR_MSG: stream id %d err idx %d\n", + err_msg.stream_id, err_msg.aud_preproc_err_idx); + if ((err_msg.stream_id < MAX_ENC_COUNT) && + audpreproc->func[err_msg.stream_id]) + audpreproc->func[err_msg.stream_id]( + audpreproc->private[err_msg.stream_id], id, + &err_msg); + break; + } + case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_cfg_done_msg enc_cfg_msg; + + getevent(&enc_cfg_msg, AUDPREPROC_CMD_ENC_CFG_DONE_MSG_LEN); + MM_DBG("AUDPREPROC_CMD_ENC_CFG_DONE_MSG: stream id %d enc type \ + %d\n", enc_cfg_msg.stream_id, enc_cfg_msg.rec_enc_type); + if ((enc_cfg_msg.stream_id < MAX_ENC_COUNT) && + audpreproc->func[enc_cfg_msg.stream_id]) + audpreproc->func[enc_cfg_msg.stream_id]( + audpreproc->private[enc_cfg_msg.stream_id], id, + &enc_cfg_msg); + for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) { + if (audpreproc->cb_tbl[n] && + audpreproc->cb_tbl[n]->fn) { + audpreproc->cb_tbl[n]->fn( \ + audpreproc->cb_tbl[n]->private, \ + id, (void *) &enc_cfg_msg); + } + } + break; + } + case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: { + struct audpreproc_cmd_enc_param_cfg_done_msg enc_param_msg; + + getevent(&enc_param_msg, + AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG_LEN); + MM_DBG("AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: stream id %d\n", + enc_param_msg.stream_id); + if ((enc_param_msg.stream_id < MAX_ENC_COUNT) && + audpreproc->func[enc_param_msg.stream_id]) + audpreproc->func[enc_param_msg.stream_id]( + audpreproc->private[enc_param_msg.stream_id], id, + &enc_param_msg); + break; + } + case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: { + struct audpreproc_afe_cmd_audio_record_cfg_done + record_cfg_done; + getevent(&record_cfg_done, + AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG_LEN); + MM_DBG("AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: \ + stream id %d\n", record_cfg_done.stream_id); + if ((record_cfg_done.stream_id < MAX_ENC_COUNT) && + audpreproc->func[record_cfg_done.stream_id]) + audpreproc->func[record_cfg_done.stream_id]( + audpreproc->private[record_cfg_done.stream_id], id, + &record_cfg_done); + break; + } + case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: { + struct audpreproc_cmd_routing_mode_done routing_mode_done; + + getevent(&routing_mode_done, + AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG_LEN); + MM_DBG("AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: \ + stream id %d\n", routing_mode_done.stream_id); + if ((routing_mode_done.stream_id < MAX_ENC_COUNT) && + audpreproc->func[routing_mode_done.stream_id]) + audpreproc->func[routing_mode_done.stream_id]( + audpreproc->private[routing_mode_done.stream_id], id, + &routing_mode_done); + break; + } +#ifdef CONFIG_DEBUG_FS + case AUDPREPROC_MSG_FEAT_QUERY_DM_DONE: + { + uint16_t msg[3]; + getevent(msg, sizeof(msg)); + MM_INFO("RTC ACK --> %x %x %x\n", msg[0], msg[1], msg[2]); + acdb_rtc_set_err(msg[2]); + } + break; +#endif + case ADSP_MESSAGE_ID: { + MM_DBG("Received ADSP event:module audpreproctask\n"); + break; + } + default: + MM_ERR("Unknown Event %d\n", id); + } + return; +} + +static struct msm_adsp_ops adsp_ops = { + .event = audpreproc_dsp_event, +}; + +/* EXPORTED API's */ +int audpreproc_enable(int enc_id, audpreproc_event_func func, void *private) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + int res = 0; + + if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1)) + return -EINVAL; + + mutex_lock(audpreproc->lock); + if (audpreproc->func[enc_id]) { + res = -EBUSY; + goto out; + } + + audpreproc->func[enc_id] = func; + audpreproc->private[enc_id] = private; + + /* First client to enable preproc task */ + if (audpreproc->open_count++ == 0) { + MM_DBG("Get AUDPREPROCTASK\n"); + res = msm_adsp_get("AUDPREPROCTASK", &audpreproc->mod, + &adsp_ops, audpreproc); + if (res < 0) { + MM_ERR("Can not get AUDPREPROCTASK\n"); + audpreproc->open_count = 0; + audpreproc->func[enc_id] = NULL; + audpreproc->private[enc_id] = NULL; + goto out; + } + prevent_suspend(); + if (msm_adsp_enable(audpreproc->mod)) { + MM_ERR("Can not enable AUDPREPROCTASK\n"); + audpreproc->open_count = 0; + audpreproc->func[enc_id] = NULL; + audpreproc->private[enc_id] = NULL; + msm_adsp_put(audpreproc->mod); + audpreproc->mod = NULL; + res = -ENODEV; + allow_suspend(); + goto out; + } + } + res = 0; +out: + mutex_unlock(audpreproc->lock); + return res; +} +EXPORT_SYMBOL(audpreproc_enable); + +int audpreproc_update_audrec_info( + struct audrec_session_info *audrec_session_info) +{ + if (!audrec_session_info) { + MM_ERR("error in audrec session info address\n"); + return -EINVAL; + } + if (audrec_session_info->session_id < MAX_ENC_COUNT) { + memcpy(&session_info[audrec_session_info->session_id], + audrec_session_info, + sizeof(struct audrec_session_info)); + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL(audpreproc_update_audrec_info); + +void audpreproc_disable(int enc_id, void *private) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + + if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1)) + return; + + mutex_lock(audpreproc->lock); + if (!audpreproc->func[enc_id]) + goto out; + if (audpreproc->private[enc_id] != private) + goto out; + + audpreproc->func[enc_id] = NULL; + audpreproc->private[enc_id] = NULL; + + /* Last client then disable preproc task */ + if (--audpreproc->open_count == 0) { + msm_adsp_disable(audpreproc->mod); + MM_DBG("Put AUDPREPROCTASK\n"); + msm_adsp_put(audpreproc->mod); + audpreproc->mod = NULL; + allow_suspend(); + } +out: + mutex_unlock(audpreproc->lock); + return; +} +EXPORT_SYMBOL(audpreproc_disable); + + +int audpreproc_register_event_callback(struct audpreproc_event_callback *ecb) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + int i; + + for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { + if (NULL == audpreproc->cb_tbl[i]) { + audpreproc->cb_tbl[i] = ecb; + return 0; + } + } + return -1; +} +EXPORT_SYMBOL(audpreproc_register_event_callback); + +int audpreproc_unregister_event_callback(struct audpreproc_event_callback *ecb) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + int i; + + for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) { + if (ecb == audpreproc->cb_tbl[i]) { + audpreproc->cb_tbl[i] = NULL; + return 0; + } + } + return -1; +} +EXPORT_SYMBOL(audpreproc_unregister_event_callback); + + +/* enc_type = supported encode format * + * like pcm, aac, sbc, evrc, qcelp, amrnb etc ... * + */ +int audpreproc_aenc_alloc(unsigned enc_type, const char **module_name, + unsigned *queue_ids) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + int encid = -1, idx, lidx, mode, codec; + int codecs_supported, min_codecs_supported; + static int wakelock_init; + + mutex_lock(audpreproc->lock); + /* Represents in bit mask */ + mode = ((enc_type & AUDPREPROC_MODE_MASK) << 16); + codec = (1 << (enc_type & AUDPREPROC_CODEC_MASK)); + + lidx = msm_enc_database.num_enc; + min_codecs_supported = sizeof(unsigned int) * 8; + MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec); + + for (idx = lidx-1; idx >= 0; idx--) { + /* encoder free and supports the format */ + if (!(audpreproc->enc_inuse & (1 << (idx))) && + ((mode & msm_enc_database.enc_info_list[idx].enc_formats) + == mode) && ((codec & + msm_enc_database.enc_info_list[idx].enc_formats) + == codec)){ + /* Check supports minimum number codecs */ + codecs_supported = + msm_enc_database.enc_info_list[idx].nr_codec_support; + if (codecs_supported < min_codecs_supported) { + lidx = idx; + min_codecs_supported = codecs_supported; + } + } + } + + if (lidx < msm_enc_database.num_enc) { + audpreproc->enc_inuse |= (1 << lidx); + *module_name = + msm_enc_database.enc_info_list[lidx].module_name; + *queue_ids = + msm_enc_database.enc_info_list[lidx].module_queueids; + encid = msm_enc_database.enc_info_list[lidx].module_encid; + } + + if (!wakelock_init) { + wake_lock_init(&audpre_wake_lock, WAKE_LOCK_SUSPEND, "audpre"); + pm_qos_add_request(&audpre_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + wakelock_init = 1; + } + + mutex_unlock(audpreproc->lock); + return encid; +} +EXPORT_SYMBOL(audpreproc_aenc_alloc); + +void audpreproc_aenc_free(int enc_id) +{ + struct audpreproc_state *audpreproc = &the_audpreproc_state; + int idx; + + mutex_lock(audpreproc->lock); + for (idx = 0; idx < msm_enc_database.num_enc; idx++) { + if (msm_enc_database.enc_info_list[idx].module_encid == + enc_id) { + audpreproc->enc_inuse &= ~(1 << idx); + break; + } + } + mutex_unlock(audpreproc->lock); + return; + +} +EXPORT_SYMBOL(audpreproc_aenc_free); + +int audpreproc_send_preproccmdqueue(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, cmd, len); +} +EXPORT_SYMBOL(audpreproc_send_preproccmdqueue); + +int audpreproc_send_audreccmdqueue(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcAudRecCmdQueue, cmd, len); +} +EXPORT_SYMBOL(audpreproc_send_audreccmdqueue); + +int audpreproc_send_audrec2cmdqueue(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudRec2CmdQueue, cmd, len); +} +EXPORT_SYMBOL(audpreproc_send_audrec2cmdqueue); + +int audpreproc_dsp_set_agc(struct audpreproc_cmd_cfg_agc_params *agc, + unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, agc, len); +} +EXPORT_SYMBOL(audpreproc_dsp_set_agc); + +int audpreproc_dsp_set_agc2(struct audpreproc_cmd_cfg_agc_params_2 *agc2, + unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, agc2, len); +} +EXPORT_SYMBOL(audpreproc_dsp_set_agc2); + +int audpreproc_dsp_set_ns(struct audpreproc_cmd_cfg_ns_params *ns, + unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, ns, len); +} +EXPORT_SYMBOL(audpreproc_dsp_set_ns); + +int audpreproc_dsp_set_iir( +struct audpreproc_cmd_cfg_iir_tuning_filter_params *iir, unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, iir, len); +} +EXPORT_SYMBOL(audpreproc_dsp_set_iir); + +int audpreproc_dsp_set_gain_tx( + struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx, unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, calib_gain_tx, len); +} +EXPORT_SYMBOL(audpreproc_dsp_set_gain_tx); + +void get_audrec_session_info(int id, struct audrec_session_info *info) +{ + if (id >= MAX_ENC_COUNT) { + MM_ERR("invalid session id = %d\n", id); + return; + } + memcpy(info, &session_info[id], sizeof(struct audrec_session_info)); +} +EXPORT_SYMBOL(get_audrec_session_info); + +int audpreproc_dsp_set_lvnv( + struct audpreproc_cmd_cfg_lvnv_param *preproc_lvnv, unsigned len) +{ + return msm_adsp_write(the_audpreproc_state.mod, + QDSP_uPAudPreProcCmdQueue, preproc_lvnv, len); +} +EXPORT_SYMBOL(audpreproc_dsp_set_lvnv); + diff --git a/arch/arm/mach-msm/qdsp5v2/aux_pcm.c b/arch/arm/mach-msm/qdsp5v2/aux_pcm.c new file mode 100644 index 00000000000..4cc834d10a6 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/aux_pcm.c @@ -0,0 +1,280 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ + +/* define offset of registers here, may put them into platform data */ +#define AUX_CODEC_CTL_OFFSET 0x00 +#define PCM_PATH_CTL_OFFSET 0x04 +#define AUX_CODEC_CTL_OUT_OFFSET 0x08 + +/* define some bit values in PCM_PATH_CTL register */ +#define PCM_PATH_CTL__ADSP_CTL_EN_BMSK 0x8 + +/* mask and shift */ +#define AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK 0x800 +#define AUX_CODEC_CTL_PCM_SYNC_LONG_BMSK 0x400 +#define AUX_CODEC_CTL_PCM_SYNC_SHORT_BMSK 0x200 +#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_SRC_BMSK 0x80 +#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_MODE_BMSK 0x40 +#define AUX_CODEC_CTL_I2S_RX_MODE_BMSK 0x20 +#define AUX_CODEC_CTL_I2S_CLK_MODE_BMSK 0x10 +#define AUX_CODEC_CTL_AUX_PCM_MODE_BMSK 0x0b +#define AUX_CODEC_CTL_AUX_CODEC_MODE_BMSK 0x02 + +/* AUX PCM MODE */ +#define MASTER_PRIM_PCM_SHORT 0 +#define MASTER_AUX_PCM_LONG 1 +#define SLAVE_PRIM_PCM_SHORT 2 + +struct aux_pcm_state { + void __iomem *aux_pcm_base; /* configure aux pcm through Scorpion */ + int dout; + int din; + int syncout; + int clkin_a; +}; + +static struct aux_pcm_state the_aux_pcm_state; + +static void __iomem *get_base_addr(struct aux_pcm_state *aux_pcm) +{ + return aux_pcm->aux_pcm_base; +} + +/* Set who control aux pcm : adsp or MSM */ +void aux_codec_adsp_codec_ctl_en(bool msm_adsp_en) +{ + void __iomem *baddr = get_base_addr(&the_aux_pcm_state); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + AUX_CODEC_CTL_OFFSET); + if (msm_adsp_en) { /* adsp */ + writel( + ((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) | + AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__ADSP_V), + baddr + AUX_CODEC_CTL_OFFSET); + } else { /* MSM */ + writel( + ((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) | + AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__MSM_V), + baddr + AUX_CODEC_CTL_OFFSET); + } + } + mb(); +} + +/* Set who control aux pcm path: adsp or MSM */ +void aux_codec_pcm_path_ctl_en(bool msm_adsp_en) +{ + void __iomem *baddr = get_base_addr(&the_aux_pcm_state); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + PCM_PATH_CTL_OFFSET); + if (msm_adsp_en) { /* adsp */ + writel( + ((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) | + PCM_PATH_CTL__ADSP_CTL_EN__ADSP_V), + baddr + PCM_PATH_CTL_OFFSET); + } else { /* MSM */ + writel( + ((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) | + PCM_PATH_CTL__ADSP_CTL_EN__MSM_V), + baddr + PCM_PATH_CTL_OFFSET); + } + } + mb(); + return; +} +EXPORT_SYMBOL(aux_codec_pcm_path_ctl_en); + +int aux_pcm_gpios_request(void) +{ + int rc = 0; + + MM_DBG("aux_pcm_gpios_request\n"); + rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT"); + if (rc) { + MM_ERR("GPIO request for AUX PCM DOUT failed\n"); + return rc; + } + + rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN"); + if (rc) { + MM_ERR("GPIO request for AUX PCM DIN failed\n"); + gpio_free(the_aux_pcm_state.dout); + return rc; + } + + rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT"); + if (rc) { + MM_ERR("GPIO request for AUX PCM SYNC OUT failed\n"); + gpio_free(the_aux_pcm_state.dout); + gpio_free(the_aux_pcm_state.din); + return rc; + } + + rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A"); + if (rc) { + MM_ERR("GPIO request for AUX PCM CLKIN A failed\n"); + gpio_free(the_aux_pcm_state.dout); + gpio_free(the_aux_pcm_state.din); + gpio_free(the_aux_pcm_state.syncout); + return rc; + } + + return rc; +} +EXPORT_SYMBOL(aux_pcm_gpios_request); + + +void aux_pcm_gpios_free(void) +{ + MM_DBG(" aux_pcm_gpios_free \n"); + + /* + * Feed silence frames before close to prevent buzzing sound in BT at + * call end. This fix is applicable only to Marimba BT. + */ + gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 0, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE); + gpio_set_value(the_aux_pcm_state.dout, 0); + msleep(20); + gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 1, GPIO_CFG_OUTPUT, + GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE); + + gpio_free(the_aux_pcm_state.dout); + gpio_free(the_aux_pcm_state.din); + gpio_free(the_aux_pcm_state.syncout); + gpio_free(the_aux_pcm_state.clkin_a); +} +EXPORT_SYMBOL(aux_pcm_gpios_free); + + +static int get_aux_pcm_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_dout"); + if (!res) { + MM_ERR("%s: failed to get gpio AUX PCM DOUT\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.dout = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_din"); + if (!res) { + MM_ERR("%s: failed to get gpio AUX PCM DIN\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.din = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_syncout"); + if (!res) { + MM_ERR("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.syncout = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_clkin_a"); + if (!res) { + MM_ERR("%s: failed to get gpio AUX PCM CLKIN A\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.clkin_a = res->start; + + return rc; +} +static int aux_pcm_probe(struct platform_device *pdev) +{ + int rc = 0; + struct resource *mem_src; + + MM_DBG("aux_pcm_probe \n"); + mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "aux_codec_reg_addr"); + if (!mem_src) { + rc = -ENODEV; + goto done; + } + + the_aux_pcm_state.aux_pcm_base = ioremap(mem_src->start, + (mem_src->end - mem_src->start) + 1); + if (!the_aux_pcm_state.aux_pcm_base) { + rc = -ENOMEM; + goto done; + } + rc = get_aux_pcm_gpios(pdev); + if (rc) { + MM_ERR("GPIO configuration failed\n"); + rc = -ENODEV; + } + +done: return rc; + +} + +static int aux_pcm_remove(struct platform_device *pdev) +{ + iounmap(the_aux_pcm_state.aux_pcm_base); + return 0; +} + +static struct platform_driver aux_pcm_driver = { + .probe = aux_pcm_probe, + .remove = aux_pcm_remove, + .driver = { + .name = "msm_aux_pcm", + .owner = THIS_MODULE, + }, +}; + +static int __init aux_pcm_init(void) +{ + + return platform_driver_register(&aux_pcm_driver); +} + +static void __exit aux_pcm_exit(void) +{ + platform_driver_unregister(&aux_pcm_driver); +} + +module_init(aux_pcm_init); +module_exit(aux_pcm_exit); + +MODULE_DESCRIPTION("MSM AUX PCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/lpa.c b/arch/arm/mach-msm/qdsp5v2/lpa.c new file mode 100644 index 00000000000..c4e0feeaa0a --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/lpa.c @@ -0,0 +1,608 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LPA_REG_WRITEL(drv, val, reg) writel(val, drv->baseaddr + reg) +#define LPA_REG_READL(drv, reg) readl(drv->baseaddr + reg) + +/* bit 2:0 is reserved because watermarks have to be 64-bit aligned */ +#define LLB_WATERMARK_VAL_MASK 0x00000003 + +#define LPA_STATUS_SBUF_EN 0x01 + +struct lpa_drv { + void __iomem *baseaddr; + u32 obuf_hlb_size; + u32 dsp_proc_id; + u32 app_proc_id; + struct lpa_mem_config nosb_config; + struct lpa_mem_config sb_config; + u32 status; + u32 watermark_bytes; + u32 watermark_aheadtime; + u32 sample_boundary; +}; + +struct lpa_state { + struct lpa_drv lpa_drv; /* One instance for now */ + u32 assigned; + struct mutex lpa_lock; +}; + +struct lpa_state the_lpa_state; + +static void lpa_enable_codec(struct lpa_drv *lpa, bool enable) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_CODEC); + val = enable ? (val | LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) : + (val & ~LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK); + val |= LPA_OBUF_CODEC_LOAD_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC); + mb(); +} + +static void lpa_reset(struct lpa_drv *lpa) +{ + u32 status; + struct clk *adsp_clk; + /* Need to make sure not disable clock while other device is enabled */ + adsp_clk = clk_get(NULL, "adsp_clk"); + if (!adsp_clk) { + MM_ERR("failed to get adsp clk\n"); + goto error; + } + clk_enable(adsp_clk); + lpa_enable_codec(lpa, 0); + LPA_REG_WRITEL(lpa, (LPA_OBUF_RESETS_MISR_RESET | + LPA_OBUF_RESETS_OVERALL_RESET), LPA_OBUF_RESETS); + do { + status = LPA_REG_READL(lpa, LPA_OBUF_STATUS); + } while (!(status & LPA_OBUF_STATUS_RESET_DONE)); + + LPA_REG_WRITEL(lpa, LPA_OBUF_ACK_RESET_DONE_BMSK, LPA_OBUF_ACK); + mb(); + clk_disable(adsp_clk); + clk_put(adsp_clk); +error: + return; +} + +static void lpa_config_hlb_addr(struct lpa_drv *lpa) +{ + u32 val, min_addr = 0, max_addr = min_addr + lpa->obuf_hlb_size; + + val = (min_addr & LPA_OBUF_HLB_MIN_ADDR_SEG_BMSK) | + LPA_OBUF_HLB_MIN_ADDR_LOAD_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MIN_ADDR); + val = max_addr & LPA_OBUF_HLB_MAX_ADDR_SEG_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MAX_ADDR); +} + +static void lpa_powerup_mem_bank(struct lpa_drv *lpa, + struct lpa_mem_bank_select *bank) +{ + u32 status, val; + + status = LPA_REG_READL(lpa, LPA_OBUF_MEMORY_CONTROL); + val = ((*((u32 *) bank)) << LPA_OBUF_MEM_CTL_PWRUP_SHFT) & + LPA_OBUF_MEM_CTL_PWRUP_BMSK; + val |= status; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_MEMORY_CONTROL); +} + +static void lpa_enable_interrupt(struct lpa_drv *lpa, u32 proc_id) +{ + u32 val; + + proc_id &= LPA_OBUF_INTR_EN_BMSK; + val = 0x1 << proc_id; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_INTR_ENABLE); +} + +static void lpa_config_llb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr) +{ + u32 val; + + val = (min_addr & LPA_OBUF_LLB_MIN_ADDR_SEG_BMSK) | + LPA_OBUF_LLB_MIN_ADDR_LOAD_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MIN_ADDR); + val = max_addr & LPA_OBUF_LLB_MAX_ADDR_SEG_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MAX_ADDR); +} + +static void lpa_config_sb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr) +{ + u32 val; + + val = (min_addr & LPA_OBUF_SB_MIN_ADDR_SEG_BMSK) | + LPA_OBUF_SB_MIN_ADDR_LOAD_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MIN_ADDR); + val = max_addr & LPA_OBUF_SB_MAX_ADDR_SEG_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MAX_ADDR); +} + +static void lpa_switch_sb(struct lpa_drv *lpa) +{ + if (lpa->status & LPA_STATUS_SBUF_EN) { + lpa_config_llb_addr(lpa, lpa->sb_config.llb_min_addr, + lpa->sb_config.llb_max_addr); + lpa_config_sb_addr(lpa, lpa->sb_config.sb_min_addr, + lpa->sb_config.sb_max_addr); + } else { + lpa_config_llb_addr(lpa, lpa->nosb_config.llb_min_addr, + lpa->nosb_config.llb_max_addr); + lpa_config_sb_addr(lpa, lpa->nosb_config.sb_min_addr, + lpa->nosb_config.sb_max_addr); + } +} + +static u8 lpa_req_wmark_id(struct lpa_drv *lpa) +{ + return (u8) (LPA_REG_READL(lpa, LPA_OBUF_WMARK_ASSIGN) & + LPA_OBUF_WMARK_ASSIGN_BMSK); +} + +static void lpa_enable_llb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl, + u32 wmark_id, u32 cpu_id) +{ + u32 val; + + wmark_id = (wmark_id > 3) ? 0 : wmark_id; + val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id)); + val &= ~LPA_OBUF_LLB_WMARK_CTRL_BMSK; + val &= ~LPA_OBUF_LLB_WMARK_MAP_BMSK; + val |= (wmark_ctrl << LPA_OBUF_LLB_WMARK_CTRL_SHFT) & + LPA_OBUF_LLB_WMARK_CTRL_BMSK; + val |= (cpu_id << LPA_OBUF_LLB_WMARK_MAP_SHFT) & + LPA_OBUF_LLB_WMARK_MAP_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id)); +} + +static void lpa_enable_sb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl, + u32 cpu_id) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_SB); + val &= ~LPA_OBUF_SB_WMARK_CTRL_BMSK; + val &= ~LPA_OBUF_SB_WMARK_MAP_BMSK; + val |= (wmark_ctrl << LPA_OBUF_SB_WMARK_CTRL_SHFT) & + LPA_OBUF_SB_WMARK_CTRL_BMSK; + val |= (cpu_id << LPA_OBUF_SB_WMARK_MAP_SHFT) & + LPA_OBUF_SB_WMARK_MAP_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_SB); +} +static void lpa_enable_hlb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl, + u32 cpu_id) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_HLB); + val &= ~LPA_OBUF_HLB_WMARK_CTRL_BMSK; + val &= ~LPA_OBUF_HLB_WMARK_MAP_BMSK; + val |= (wmark_ctrl << LPA_OBUF_HLB_WMARK_CTRL_SHFT) & + LPA_OBUF_HLB_WMARK_CTRL_BMSK; + val |= (cpu_id << LPA_OBUF_HLB_WMARK_MAP_SHFT) & + LPA_OBUF_HLB_WMARK_MAP_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_HLB); +} + +static void lpa_enable_utc(struct lpa_drv *lpa, bool enable, u32 cpu_id) +{ + u32 val; + + val = (cpu_id << LPA_OBUF_UTC_CONFIG_MAP_SHFT) & + LPA_OBUF_UTC_CONFIG_MAP_BMSK; + enable = (enable ? 1 : 0); + val = (enable << LPA_OBUF_UTC_CONFIG_EN_SHFT) & + LPA_OBUF_UTC_CONFIG_EN_BMSK; + LPA_REG_WRITEL(lpa, val, LPA_OBUF_UTC_CONFIG); +} + +static void lpa_enable_mixing(struct lpa_drv *lpa, bool enable) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL); + val = (enable ? val | LPA_OBUF_CONTROL_LLB_EN_BMSK : + val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK); + LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL); +} + +static void lpa_enable_mixer_saturation(struct lpa_drv *lpa, u32 buf_id, + bool enable) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL); + + switch (buf_id) { + case LPA_BUF_ID_LLB: + val = enable ? (val | LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK) : + (val & ~LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK); + break; + + case LPA_BUF_ID_SB: + val = enable ? (val | LPA_OBUF_CONTROL_SB_SAT_EN_BMSK) : + (val & ~LPA_OBUF_CONTROL_SB_SAT_EN_BMSK); + break; + } + + LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL); +} + +static void lpa_enable_obuf(struct lpa_drv *lpa, u32 buf_id, bool enable) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL); + + switch (buf_id) { + case LPA_BUF_ID_HLB: + val = enable ? (val | LPA_OBUF_CONTROL_HLB_EN_BMSK) : + (val & ~LPA_OBUF_CONTROL_HLB_EN_BMSK); + break; + + case LPA_BUF_ID_LLB: + val = enable ? (val | LPA_OBUF_CONTROL_LLB_EN_BMSK) : + (val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK); + break; + + case LPA_BUF_ID_SB: + val = enable ? (val | LPA_OBUF_CONTROL_SB_EN_BMSK) : + (val & ~LPA_OBUF_CONTROL_SB_EN_BMSK); + break; + } + LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL); +} + +struct lpa_drv *lpa_get(void) +{ + struct lpa_mem_bank_select mem_bank; + struct lpa_drv *ret_lpa = &the_lpa_state.lpa_drv; + + mutex_lock(&the_lpa_state.lpa_lock); + if (the_lpa_state.assigned) { + MM_ERR("LPA HW accupied\n"); + ret_lpa = ERR_PTR(-EBUSY); + goto error; + } + /* perform initialization */ + lpa_reset(ret_lpa); + /* Config adec param */ + /* Initialize LLB/SB min/max address */ + lpa_switch_sb(ret_lpa); + /* Config HLB minx/max address */ + lpa_config_hlb_addr(ret_lpa); + + /* Power up all memory bank for now */ + mem_bank.b0 = 1; + mem_bank.b1 = 1; + mem_bank.b2 = 1; + mem_bank.b3 = 1; + mem_bank.b4 = 1; + mem_bank.b5 = 1; + mem_bank.b6 = 1; + mem_bank.b7 = 1; + mem_bank.b8 = 1; + mem_bank.b9 = 1; + mem_bank.b10 = 1; + mem_bank.llb = 1; + lpa_powerup_mem_bank(ret_lpa, &mem_bank); + + while + (lpa_req_wmark_id(ret_lpa) != LPA_OBUF_WMARK_ASSIGN_DONE); + + lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 0, + ret_lpa->dsp_proc_id); + lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 1, + ret_lpa->dsp_proc_id); + lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 2, + ret_lpa->app_proc_id); + lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 3, + ret_lpa->app_proc_id); + lpa_enable_hlb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, + ret_lpa->dsp_proc_id); + lpa_enable_sb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, + ret_lpa->dsp_proc_id); + lpa_enable_utc(ret_lpa, 0, LPA_OBUF_UTC_CONFIG_NO_INTR); + + lpa_enable_mixing(ret_lpa, 1); + lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_LLB, 1); + + lpa_enable_obuf(ret_lpa, LPA_BUF_ID_HLB, 0); + lpa_enable_obuf(ret_lpa, LPA_BUF_ID_LLB, 1); + if (ret_lpa->status & LPA_STATUS_SBUF_EN) { + lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_SB, 1); + lpa_enable_obuf(ret_lpa, LPA_BUF_ID_SB, 1); + } + + lpa_enable_interrupt(ret_lpa, ret_lpa->dsp_proc_id); + mb(); + the_lpa_state.assigned++; +error: + mutex_unlock(&the_lpa_state.lpa_lock); + return ret_lpa; +} +EXPORT_SYMBOL(lpa_get); + +void lpa_put(struct lpa_drv *lpa) +{ + + mutex_lock(&the_lpa_state.lpa_lock); + if (!lpa || &the_lpa_state.lpa_drv != lpa) { + MM_ERR("invalid arg\n"); + goto error; + } + /* Deinitialize */ + the_lpa_state.assigned--; +error: + mutex_unlock(&the_lpa_state.lpa_lock); +} +EXPORT_SYMBOL(lpa_put); + +int lpa_cmd_codec_config(struct lpa_drv *lpa, + struct lpa_codec_config *config_ptr) +{ + u32 sample_rate; + u32 num_channels; + u32 width; + u32 val = 0; + + if (!lpa || !config_ptr) { + MM_ERR("invalid parameters\n"); + return -EINVAL; + } + + switch (config_ptr->num_channels) { + case 8: + num_channels = LPA_NUM_CHAN_7P1; + break; + case 6: + num_channels = LPA_NUM_CHAN_5P1; + break; + case 4: + num_channels = LPA_NUM_CHAN_4_CHANNEL; + break; + case 2: + num_channels = LPA_NUM_CHAN_STEREO; + break; + case 1: + num_channels = LPA_NUM_CHAN_MONO; + break; + default: + MM_ERR("unsupported number of channel\n"); + goto error; + } + val |= (num_channels << LPA_OBUF_CODEC_NUM_CHAN_SHFT) & + LPA_OBUF_CODEC_NUM_CHAN_BMSK; + + switch (config_ptr->sample_rate) { + case 96000: + sample_rate = LPA_SAMPLE_RATE_96KHZ; + break; + case 64000: + sample_rate = LPA_SAMPLE_RATE_64KHZ; + break; + case 48000: + sample_rate = LPA_SAMPLE_RATE_48KHZ; + break; + case 44100: + sample_rate = LPA_SAMPLE_RATE_44P1KHZ; + break; + case 32000: + sample_rate = LPA_SAMPLE_RATE_32KHZ; + break; + case 22050: + sample_rate = LPA_SAMPLE_RATE_22P05KHZ; + break; + case 16000: + sample_rate = LPA_SAMPLE_RATE_16KHZ; + break; + case 11025: + sample_rate = LPA_SAMPLE_RATE_11P025KHZ; + break; + case 8000: + sample_rate = LPA_SAMPLE_RATE_8KHZ; + break; + default: + MM_ERR("unsupported sample rate \n"); + goto error; + } + val |= (sample_rate << LPA_OBUF_CODEC_SAMP_SHFT) & + LPA_OBUF_CODEC_SAMP_BMSK; + switch (config_ptr->sample_width) { + case 32: + width = LPA_BITS_PER_CHAN_32BITS; + break; + case 24: + width = LPA_BITS_PER_CHAN_24BITS; + break; + case 16: + width = LPA_BITS_PER_CHAN_16BITS; + break; + default: + MM_ERR("unsupported sample width \n"); + goto error; + } + val |= (width << LPA_OBUF_CODEC_BITS_PER_CHAN_SHFT) & + LPA_OBUF_CODEC_BITS_PER_CHAN_BMSK; + + val |= LPA_OBUF_CODEC_LOAD_BMSK; + val |= (config_ptr->output_interface << LPA_OBUF_CODEC_INTF_SHFT) & + LPA_OBUF_CODEC_INTF_BMSK; + + LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC); + mb(); + + return 0; +error: + return -EINVAL; +} +EXPORT_SYMBOL(lpa_cmd_codec_config); + +static int lpa_check_llb_clear(struct lpa_drv *lpa) +{ + u32 val; + val = LPA_REG_READL(lpa, LPA_OBUF_STATUS); + + return !(val & LPA_OBUF_STATUS_LLB_CLR_BMSK); +} + +static void lpa_clear_llb(struct lpa_drv *lpa) +{ + u32 val; + + val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL); + LPA_REG_WRITEL(lpa, (val | LPA_OBUF_CONTROL_LLB_CLR_CMD_BMSK), + LPA_OBUF_CONTROL); + lpa_enable_obuf(lpa, LPA_BUF_ID_LLB, 0); + + while (!lpa_check_llb_clear(lpa)) + udelay(100); + LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL); +} + +int lpa_cmd_enable_codec(struct lpa_drv *lpa, bool enable) +{ + u32 val; + struct lpa_mem_bank_select mem_bank; + + MM_DBG(" %s\n", (enable ? "enable" : "disable")); + + if (!lpa) + return -EINVAL; + + val = LPA_REG_READL(lpa, LPA_OBUF_CODEC); + + if (enable) { + if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) + return -EBUSY; + /* Power up all memory bank for now */ + mem_bank.b0 = 1; + mem_bank.b1 = 1; + mem_bank.b2 = 1; + mem_bank.b3 = 1; + mem_bank.b4 = 1; + mem_bank.b5 = 1; + mem_bank.b6 = 1; + mem_bank.b7 = 1; + mem_bank.b8 = 1; + mem_bank.b9 = 1; + mem_bank.b10 = 1; + mem_bank.llb = 1; + lpa_powerup_mem_bank(lpa, &mem_bank); + + /*clear LLB*/ + lpa_clear_llb(lpa); + + lpa_enable_codec(lpa, 1); + MM_DBG("LPA codec is enabled\n"); + } else { + if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) { + lpa_enable_codec(lpa, 0); + MM_DBG("LPA codec is disabled\n"); + } else + MM_ERR("LPA codec is already disable\n"); + } + mb(); + return 0; +} +EXPORT_SYMBOL(lpa_cmd_enable_codec); + +static int lpa_probe(struct platform_device *pdev) +{ + int rc = 0; + struct resource *mem_src; + struct msm_lpa_platform_data *pdata; + + MM_INFO("lpa probe\n"); + + if (!pdev || !pdev->dev.platform_data) { + MM_ERR("no plaform data\n"); + rc = -ENODEV; + goto error; + } + + mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpa"); + if (!mem_src) { + MM_ERR("LPA base address undefined\n"); + rc = -ENODEV; + goto error; + } + + pdata = pdev->dev.platform_data; + the_lpa_state.lpa_drv.baseaddr = ioremap(mem_src->start, + (mem_src->end - mem_src->start) + 1); + if (!the_lpa_state.lpa_drv.baseaddr) { + rc = -ENOMEM; + goto error; + } + + the_lpa_state.lpa_drv.obuf_hlb_size = pdata->obuf_hlb_size; + the_lpa_state.lpa_drv.dsp_proc_id = pdata->dsp_proc_id; + the_lpa_state.lpa_drv.app_proc_id = pdata->app_proc_id; + the_lpa_state.lpa_drv.nosb_config = pdata->nosb_config; + the_lpa_state.lpa_drv.sb_config = pdata->sb_config; + /* default to enable summing buffer */ + the_lpa_state.lpa_drv.status = LPA_STATUS_SBUF_EN; + +error: + return rc; + +} + +static int lpa_remove(struct platform_device *pdev) +{ + iounmap(the_lpa_state.lpa_drv.baseaddr); + return 0; +} + +static struct platform_driver lpa_driver = { + .probe = lpa_probe, + .remove = lpa_remove, + .driver = { + .name = "lpa", + .owner = THIS_MODULE, + }, +}; + +static int __init lpa_init(void) +{ + the_lpa_state.assigned = 0; + mutex_init(&the_lpa_state.lpa_lock); + return platform_driver_register(&lpa_driver); +} + +static void __exit lpa_exit(void) +{ + platform_driver_unregister(&lpa_driver); +} + +module_init(lpa_init); +module_exit(lpa_exit); + +MODULE_DESCRIPTION("MSM LPA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/mi2s.c b/arch/arm/mach-msm/qdsp5v2/mi2s.c new file mode 100644 index 00000000000..e38f164eb12 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/mi2s.c @@ -0,0 +1,885 @@ +/* Copyright (c) 2009,2011 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define DEBUG +#ifdef DEBUG +#define dprintk(format, arg...) \ +printk(KERN_DEBUG format, ## arg) +#else +#define dprintk(format, arg...) do {} while (0) +#endif + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ + +/* Device Types */ +#define HDMI 0 +#define CODEC_RX 1 +#define CODEC_TX 2 + +/* Static offset for now. If different target have different + * offset, update to platform data model + */ +#define MI2S_RESET_OFFSET 0x0 +#define MI2S_MODE_OFFSET 0x4 +#define MI2S_TX_MODE_OFFSET 0x8 +#define MI2S_RX_MODE_OFFSET 0xc + +#define MI2S_SD_N_EN_MASK 0xF0 +#define MI2S_TX_RX_N_MASK 0x0F + +#define MI2S_RESET__MI2S_RESET__RESET 0x1 +#define MI2S_RESET__MI2S_RESET__ACTIVE 0x0 +#define MI2S_MODE__MI2S_MASTER__MASTER 0x1 +#define MI2S_MODE__MI2S_MASTER__SLAVE 0x0 +#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT 0x1 +#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT 0x2 +#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT 0x3 +#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW 0x0 +#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED 0x1 +#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE 0x0 +#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE 0x1 +#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL 0x0 +#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL 0x1 +#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL 0x2 +#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL 0x3 +#define MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1 +#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW 0x0 +#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED 0x1 +#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE 0x0 +#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE 0x1 +#define MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH 0x0 +#define MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1 + +#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK 0x1000 +#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT 0xC +#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK 0x300 +#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT 0x8 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK 0x4 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT 0x2 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK 0x2 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT 0x1 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK 0x18 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT 0x3 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK 0x80 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT 0x7 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK 0x60 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT 0x5 +#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK 0x1 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK 0x60 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT 0x5 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK 0x4 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT 0x2 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK 0x2 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT 0x1 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK 0x18 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT 0x3 +#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK 0x1 + +/* Max number of channels */ +#define MAX_NUM_CHANNELS_OUT 8 +#define MAX_NUM_CHANNELS_IN 2 + +/* Num of SD Lines */ +#define MAX_SD_LINES 4 + +#define MI2S_SD_0_EN_MAP 0x10 +#define MI2S_SD_1_EN_MAP 0x20 +#define MI2S_SD_2_EN_MAP 0x40 +#define MI2S_SD_3_EN_MAP 0x80 +#define MI2S_SD_0_TX_MAP 0x01 +#define MI2S_SD_1_TX_MAP 0x02 +#define MI2S_SD_2_TX_MAP 0x04 +#define MI2S_SD_3_TX_MAP 0x08 + +struct mi2s_state { + void __iomem *mi2s_hdmi_base; + void __iomem *mi2s_rx_base; + void __iomem *mi2s_tx_base; + struct mutex mutex_lock; + +}; + +static struct mi2s_state the_mi2s_state; + +static void __iomem *get_base_addr(struct mi2s_state *mi2s, uint8_t dev_id) +{ + switch (dev_id) { + case HDMI: + return mi2s->mi2s_hdmi_base; + case CODEC_RX: + return mi2s->mi2s_rx_base; + case CODEC_TX: + return mi2s->mi2s_tx_base; + default: + break; + } + return ERR_PTR(-ENODEV); +} + +static void mi2s_reset(struct mi2s_state *mi2s, uint8_t dev_id) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + if (!IS_ERR(baddr)) + writel(MI2S_RESET__MI2S_RESET__RESET, + baddr + MI2S_RESET_OFFSET); +} + +static void mi2s_release(struct mi2s_state *mi2s, uint8_t dev_id) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + if (!IS_ERR(baddr)) + writel(MI2S_RESET__MI2S_RESET__ACTIVE, + baddr + MI2S_RESET_OFFSET); +} + +static void mi2s_master(struct mi2s_state *mi2s, uint8_t dev_id, bool master) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_MODE_OFFSET); + if (master) { + writel( + ((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) | + (MI2S_MODE__MI2S_MASTER__MASTER << + HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)), + baddr + MI2S_MODE_OFFSET); + } else { + writel( + ((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) | + (MI2S_MODE__MI2S_MASTER__SLAVE << + HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)), + baddr + MI2S_MODE_OFFSET); + } + } +} + +static void mi2s_set_word_type(struct mi2s_state *mi2s, uint8_t dev_id, + uint8_t size) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_MODE_OFFSET); + switch (size) { + case WT_16_BIT: + writel( + ((val & + ~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) | + (MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT << + HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)), + baddr + MI2S_MODE_OFFSET); + break; + case WT_24_BIT: + writel( + ((val & + ~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) | + (MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT << + HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)), + baddr + MI2S_MODE_OFFSET); + break; + case WT_32_BIT: + writel( + ((val & + ~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) | + (MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT << + HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)), + baddr + MI2S_MODE_OFFSET); + break; + default: + break; + } + } +} + +static void mi2s_set_sd(struct mi2s_state *mi2s, uint8_t dev_id, uint8_t sd_map) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_MODE_OFFSET) & + ~(MI2S_SD_N_EN_MASK | MI2S_TX_RX_N_MASK); + writel(val | sd_map, baddr + MI2S_MODE_OFFSET); + } +} + +static void mi2s_set_output_num_channels(struct mi2s_state *mi2s, + uint8_t dev_id, uint8_t channels) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_TX_MODE_OFFSET); + if (channels == MI2S_CHAN_MONO_RAW) { + val = (val & + ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) | + ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) | + (MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT)); + } else if (channels == MI2S_CHAN_MONO_PACKED) { + val = (val & + ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) | + ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) | + (MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT)); + } else if (channels == MI2S_CHAN_STEREO) { + val = (val & + ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) | + ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) | + (MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT)); + } else if (channels == MI2S_CHAN_4CHANNELS) { + val = (val & + ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) | + ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) | + (MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT)); + } else if (channels == MI2S_CHAN_6CHANNELS) { + val = (val & + ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) | + ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) | + (MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT)); + } else if (channels == MI2S_CHAN_8CHANNELS) { + val = (val & + ~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) | + ((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) | + (MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT)); + } + writel(val, baddr + MI2S_TX_MODE_OFFSET); + } +} + +static void mi2s_set_output_4ch_map(struct mi2s_state *mi2s, uint8_t dev_id, + bool high_low) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_TX_MODE_OFFSET); + val = (val & ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK) | + (high_low << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT); + writel(val, baddr + MI2S_TX_MODE_OFFSET); + } +} + +static void mi2s_set_output_2ch_map(struct mi2s_state *mi2s, uint8_t dev_id, + uint8_t sd_line) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_TX_MODE_OFFSET); + if (sd_line < 4) { + val = (val & + ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK) | + (sd_line << + HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT); + writel(val, baddr + MI2S_TX_MODE_OFFSET); + } + } +} + +static void mi2s_set_output_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_TX_MODE_OFFSET); + writel(((val & + ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK) | + MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE), + baddr + MI2S_TX_MODE_OFFSET); + } +} + +static void mi2s_set_input_sd_line(struct mi2s_state *mi2s, uint8_t dev_id, + uint8_t sd_line) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_RX_MODE_OFFSET); + if (sd_line < 4) { + val = (val & + ~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK) | + (sd_line << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT); + writel(val, baddr + MI2S_RX_MODE_OFFSET); + } + } +} + +static void mi2s_set_input_num_channels(struct mi2s_state *mi2s, uint8_t dev_id, + uint8_t channels) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_RX_MODE_OFFSET); + if (channels == MI2S_CHAN_MONO_RAW) { + val = (val & + ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) | + ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) | + (MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT)); + } else if (channels == MI2S_CHAN_MONO_PACKED) { + val = (val & + ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) | + ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) | + (MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT)); + } else if (channels == MI2S_CHAN_STEREO) { + + if (dev_id == HDMI) + val = (val & + ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) | + ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) | + (MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT)); + + else + val = (val & + ~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK | + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) | + ((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) | + (MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT) | + (MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH << + HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT)); + + + } + writel(val, baddr + MI2S_RX_MODE_OFFSET); + } +} + +static void mi2s_set_input_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id) +{ + void __iomem *baddr = get_base_addr(mi2s, dev_id); + uint32_t val; + + if (!IS_ERR(baddr)) { + val = readl(baddr + MI2S_RX_MODE_OFFSET); + writel( + ((val & + ~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK) | + MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE), + baddr + MI2S_RX_MODE_OFFSET); + } +} + + +static u8 num_of_bits_set(u8 sd_line_mask) +{ + u8 num_bits_set = 0; + + while (sd_line_mask) { + + if (sd_line_mask & 1) + num_bits_set++; + sd_line_mask = sd_line_mask >> 1; + } + return num_bits_set; +} + + +bool mi2s_set_hdmi_output_path(uint8_t channels, uint8_t size, + uint8_t sd_line_mask) +{ + bool ret_val = MI2S_TRUE; + struct mi2s_state *mi2s = &the_mi2s_state; + u8 sd_line, num_of_sd_lines = 0; + void __iomem *baddr; + uint32_t val; + + pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__, + channels, size, sd_line_mask); + + if ((channels == 0) || (channels > MAX_NUM_CHANNELS_OUT) || + ((channels != 1) && (channels % 2 != 0))) { + + pr_err("%s: invalid number of channels. channels = %u\n", + __func__, channels); + return MI2S_FALSE; + } + + sd_line_mask &= MI2S_SD_LINE_MASK; + + if (!sd_line_mask) { + pr_err("%s: Did not set any data lines to use " + " sd_line_mask =0x%x\n", __func__, sd_line_mask); + return MI2S_FALSE; + } + + mutex_lock(&mi2s->mutex_lock); + /* Put device in reset */ + mi2s_reset(mi2s, HDMI); + + mi2s_master(mi2s, HDMI, 1); + + /* Set word type */ + if (size <= WT_MAX) + mi2s_set_word_type(mi2s, HDMI, size); + else + ret_val = MI2S_FALSE; + + /* Enable clock crossing synchronization of RD DMA ACK */ + mi2s_set_output_clk_synch(mi2s, HDMI); + + mi2s_set_output_num_channels(mi2s, HDMI, channels); + + num_of_sd_lines = num_of_bits_set(sd_line_mask); + /*Second argument to find_first_bit should be maximum number of + bit*/ + + sd_line = find_first_bit((unsigned long *)&sd_line_mask, + sizeof(sd_line_mask) * 8); + pr_debug("sd_line = %d\n", sd_line); + + if (channels == 1) { + + if (num_of_sd_lines != 1) { + pr_err("%s: for one channel only one SD lines is" + " needed. num_of_sd_lines = %u\n", + __func__, num_of_sd_lines); + + ret_val = MI2S_FALSE; + goto error; + } + + if (sd_line != 0) { + pr_err("%s: for one channel tx, need to use SD_0 " + "sd_line = %u\n", __func__, sd_line); + + ret_val = MI2S_FALSE; + goto error; + } + + /* Enable SD line 0 for Tx (only option for + * mono audio) + */ + mi2s_set_sd(mi2s, HDMI, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP); + + } else if (channels == 2) { + + if (num_of_sd_lines != 1) { + pr_err("%s: for two channel only one SD lines is" + " needed. num_of_sd_lines = %u\n", + __func__, num_of_sd_lines); + ret_val = MI2S_FALSE; + goto error; + } + + /* Enable single SD line for Tx */ + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line) | + (MI2S_SD_0_TX_MAP << sd_line)); + + /* Set 2-channel mapping */ + mi2s_set_output_2ch_map(mi2s, HDMI, sd_line); + + } else if (channels == 4) { + + if (num_of_sd_lines != 2) { + pr_err("%s: for 4 channels two SD lines are" + " needed. num_of_sd_lines = %u\\n", + __func__, num_of_sd_lines); + ret_val = MI2S_FALSE; + goto error; + } + + if ((sd_line_mask && MI2S_SD_0) && + (sd_line_mask && MI2S_SD_1)) { + + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP | + MI2S_SD_1_EN_MAP) | (MI2S_SD_0_TX_MAP | + MI2S_SD_1_TX_MAP)); + mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_FALSE); + + } else if ((sd_line_mask && MI2S_SD_2) && + (sd_line_mask && MI2S_SD_3)) { + + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_2_EN_MAP | + MI2S_SD_3_EN_MAP) | (MI2S_SD_2_TX_MAP | + MI2S_SD_3_TX_MAP)); + + mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_TRUE); + } else { + + pr_err("%s: for 4 channels invalid SD lines usage" + " sd_line_mask = 0x%x\n", + __func__, sd_line_mask); + ret_val = MI2S_FALSE; + goto error; + } + } else if (channels == 6) { + + if (num_of_sd_lines != 3) { + pr_err("%s: for 6 channels three SD lines are" + " needed. num_of_sd_lines = %u\n", + __func__, num_of_sd_lines); + ret_val = MI2S_FALSE; + goto error; + } + + if ((sd_line_mask && MI2S_SD_0) && + (sd_line_mask && MI2S_SD_1) && + (sd_line_mask && MI2S_SD_2)) { + + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP | + MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP) | + (MI2S_SD_0_TX_MAP | MI2S_SD_1_TX_MAP | + MI2S_SD_2_TX_MAP)); + + } else if ((sd_line_mask && MI2S_SD_1) && + (sd_line_mask && MI2S_SD_2) && + (sd_line_mask && MI2S_SD_3)) { + + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_1_EN_MAP | + MI2S_SD_2_EN_MAP | MI2S_SD_3_EN_MAP) | + (MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP | + MI2S_SD_3_TX_MAP)); + + } else { + + pr_err("%s: for 6 channels invalid SD lines usage" + " sd_line_mask = 0x%x\n", + __func__, sd_line_mask); + ret_val = MI2S_FALSE; + goto error; + } + } else if (channels == 8) { + + if (num_of_sd_lines != 4) { + pr_err("%s: for 8 channels four SD lines are" + " needed. num_of_sd_lines = %u\n", + __func__, num_of_sd_lines); + ret_val = MI2S_FALSE; + goto error; + } + + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP | + MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP | + MI2S_SD_3_EN_MAP) | (MI2S_SD_0_TX_MAP | + MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP | + MI2S_SD_3_TX_MAP)); + } else { + pr_err("%s: invalid number channels = %u\n", + __func__, channels); + ret_val = MI2S_FALSE; + goto error; + } + + baddr = get_base_addr(mi2s, HDMI); + + val = readl(baddr + MI2S_MODE_OFFSET); + pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val); + + val = readl(baddr + MI2S_TX_MODE_OFFSET); + pr_debug("%s(): MI2S_TX_MODE = 0x%x\n", __func__, val); + + +error: + /* Release device from reset */ + mi2s_release(mi2s, HDMI); + + mutex_unlock(&mi2s->mutex_lock); + mb(); + return ret_val; +} +EXPORT_SYMBOL(mi2s_set_hdmi_output_path); + +bool mi2s_set_hdmi_input_path(uint8_t channels, uint8_t size, + uint8_t sd_line_mask) +{ + bool ret_val = MI2S_TRUE; + struct mi2s_state *mi2s = &the_mi2s_state; + u8 sd_line, num_of_sd_lines = 0; + void __iomem *baddr; + uint32_t val; + + pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__, + channels, size, sd_line_mask); + + if ((channels != 1) && (channels != MAX_NUM_CHANNELS_IN)) { + + pr_err("%s: invalid number of channels. channels = %u\n", + __func__, channels); + return MI2S_FALSE; + } + + if (size > WT_MAX) { + + pr_err("%s: mi2s word size can not be greater than 32 bits\n", + __func__); + return MI2S_FALSE; + } + + sd_line_mask &= MI2S_SD_LINE_MASK; + + if (!sd_line_mask) { + pr_err("%s: Did not set any data lines to use " + " sd_line_mask =0x%x\n", __func__, sd_line_mask); + return MI2S_FALSE; + } + + num_of_sd_lines = num_of_bits_set(sd_line_mask); + + if (num_of_sd_lines != 1) { + pr_err("%s: for two channel input only one SD lines is" + " needed. num_of_sd_lines = %u sd_line_mask = 0x%x\n", + __func__, num_of_sd_lines, sd_line_mask); + return MI2S_FALSE; + } + + /*Second argument to find_first_bit should be maximum number of + bits interested*/ + sd_line = find_first_bit((unsigned long *)&sd_line_mask, + sizeof(sd_line_mask) * 8); + pr_debug("sd_line = %d\n", sd_line); + + /* Ensure sd_line parameter is valid (0-max) */ + if (sd_line > MAX_SD_LINES) { + pr_err("%s: Line number can not be greater than = %u\n", + __func__, MAX_SD_LINES); + return MI2S_FALSE; + } + + mutex_lock(&mi2s->mutex_lock); + /* Put device in reset */ + mi2s_reset(mi2s, HDMI); + + mi2s_master(mi2s, HDMI, 1); + + /* Set word type */ + mi2s_set_word_type(mi2s, HDMI, size); + + /* Enable clock crossing synchronization of WR DMA ACK */ + mi2s_set_input_clk_synch(mi2s, HDMI); + + /* Ensure channels parameter is valid (non-zero, less than max, + * and even or mono) + */ + mi2s_set_input_num_channels(mi2s, HDMI, channels); + + mi2s_set_input_sd_line(mi2s, HDMI, sd_line); + + mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line)); + + baddr = get_base_addr(mi2s, HDMI); + + val = readl(baddr + MI2S_MODE_OFFSET); + pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val); + + val = readl(baddr + MI2S_RX_MODE_OFFSET); + pr_debug("%s(): MI2S_RX_MODE = 0x%x\n", __func__, val); + + /* Release device from reset */ + mi2s_release(mi2s, HDMI); + + mutex_unlock(&mi2s->mutex_lock); + mb(); + return ret_val; +} +EXPORT_SYMBOL(mi2s_set_hdmi_input_path); + +bool mi2s_set_codec_output_path(uint8_t channels, uint8_t size) +{ + bool ret_val = MI2S_TRUE; + struct mi2s_state *mi2s = &the_mi2s_state; + + mutex_lock(&mi2s->mutex_lock); + /* Put device in reset */ + mi2s_reset(mi2s, CODEC_TX); + + mi2s_master(mi2s, CODEC_TX, 1); + + /* Enable clock crossing synchronization of RD DMA ACK */ + mi2s_set_output_clk_synch(mi2s, CODEC_TX); + + /* Set word type */ + if (size <= WT_MAX) + mi2s_set_word_type(mi2s, CODEC_TX, size); + else + ret_val = MI2S_FALSE; + + mi2s_set_output_num_channels(mi2s, CODEC_TX, channels); + + /* Enable SD line */ + mi2s_set_sd(mi2s, CODEC_TX, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP); + + /* Release device from reset */ + mi2s_release(mi2s, CODEC_TX); + + mutex_unlock(&mi2s->mutex_lock); + mb(); + return ret_val; +} +EXPORT_SYMBOL(mi2s_set_codec_output_path); + +bool mi2s_set_codec_input_path(uint8_t channels, uint8_t size) +{ + bool ret_val = MI2S_TRUE; + struct mi2s_state *mi2s = &the_mi2s_state; + + mutex_lock(&the_mi2s_state.mutex_lock); + /* Put device in reset */ + mi2s_reset(mi2s, CODEC_RX); + + mi2s_master(mi2s, CODEC_RX, 1); + + /* Enable clock crossing synchronization of WR DMA ACK */ + mi2s_set_input_clk_synch(mi2s, CODEC_RX); + + /* Set word type */ + if (size <= WT_MAX) + mi2s_set_word_type(mi2s, CODEC_RX, size); + else + ret_val = MI2S_FALSE; + + mi2s_set_input_num_channels(mi2s, CODEC_RX, channels); + + /* Enable SD line */ + mi2s_set_sd(mi2s, CODEC_RX, MI2S_SD_0_EN_MAP); + + /* Release device from reset */ + mi2s_release(mi2s, CODEC_RX); + + mutex_unlock(&mi2s->mutex_lock); + mb(); + return ret_val; +} +EXPORT_SYMBOL(mi2s_set_codec_input_path); + + +static int mi2s_probe(struct platform_device *pdev) +{ + int rc = 0; + struct resource *mem_src; + + mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi"); + if (!mem_src) { + rc = -ENODEV; + goto error_hdmi; + } + the_mi2s_state.mi2s_hdmi_base = ioremap(mem_src->start, + (mem_src->end - mem_src->start) + 1); + if (!the_mi2s_state.mi2s_hdmi_base) { + rc = -ENOMEM; + goto error_hdmi; + } + mem_src = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "codec_rx"); + if (!mem_src) { + rc = -ENODEV; + goto error_codec_rx; + } + the_mi2s_state.mi2s_rx_base = ioremap(mem_src->start, + (mem_src->end - mem_src->start) + 1); + if (!the_mi2s_state.mi2s_rx_base) { + rc = -ENOMEM; + goto error_codec_rx; + } + mem_src = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "codec_tx"); + if (!mem_src) { + rc = -ENODEV; + goto error_codec_tx; + } + the_mi2s_state.mi2s_tx_base = ioremap(mem_src->start, + (mem_src->end - mem_src->start) + 1); + if (!the_mi2s_state.mi2s_tx_base) { + rc = -ENOMEM; + goto error_codec_tx; + } + mutex_init(&the_mi2s_state.mutex_lock); + + return rc; + +error_codec_tx: + iounmap(the_mi2s_state.mi2s_rx_base); +error_codec_rx: + iounmap(the_mi2s_state.mi2s_hdmi_base); +error_hdmi: + return rc; + +} + +static int mi2s_remove(struct platform_device *pdev) +{ + iounmap(the_mi2s_state.mi2s_tx_base); + iounmap(the_mi2s_state.mi2s_rx_base); + iounmap(the_mi2s_state.mi2s_hdmi_base); + return 0; +} + +static struct platform_driver mi2s_driver = { + .probe = mi2s_probe, + .remove = mi2s_remove, + .driver = { + .name = "mi2s", + .owner = THIS_MODULE, + }, +}; + +static int __init mi2s_init(void) +{ + return platform_driver_register(&mi2s_driver); +} + +static void __exit mi2s_exit(void) +{ + platform_driver_unregister(&mi2s_driver); +} + +module_init(mi2s_init); +module_exit(mi2s_exit); + +MODULE_DESCRIPTION("MSM MI2S driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c b/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c new file mode 100644 index 00000000000..0b20be0b279 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + MM_DBG("mp3_ioctl() cmd = %d\b", cmd); + + return -EINVAL; +} + +void audpp_cmd_cfg_mp3_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_mp3 cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} diff --git a/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c b/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c new file mode 100644 index 00000000000..d7935a727c9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + MM_DBG("pcm_ioctl() cmd = %d\n", cmd); + + return -EINVAL; +} + +void audpp_cmd_cfg_pcm_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN >> 1; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.stereo_cfg = audio->out_channel_mode; + cmd.pcm_width = audio->out_bits; + cmd.sign = 0; + audpp_send_queue2(&cmd, sizeof(cmd)); +} diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c b/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c new file mode 100644 index 00000000000..b15d4c461b7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c @@ -0,0 +1,1537 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* define the value for BT_SCO */ +#define BT_SCO_PCM_CTL_VAL (PCM_CTL__RPCM_WIDTH__LINEAR_V |\ + PCM_CTL__TPCM_WIDTH__LINEAR_V) +#define BT_SCO_DATA_FORMAT_PADDING (DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V |\ + DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V) +#define BT_SCO_AUX_CODEC_INTF AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_hsed_config; +static void snddev_hsed_config_modify_setting(int type); +static void snddev_hsed_config_restore_setting(void); +#endif + +static struct adie_codec_action_unit iearpiece_48KHz_osr256_actions[] = + HANDSET_RX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry iearpiece_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_settings, + .setting_sz = ARRAY_SIZE(iearpiece_settings), +}; + +static struct snddev_icodec_data snddev_iearpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_SPKR, + .profile = &iearpiece_profile, + .channel_mode = 1, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .property = SIDE_TONE_MASK, + .max_voice_rx_vol[VOC_NB_INDEX] = -200, + .min_voice_rx_vol[VOC_NB_INDEX] = -1700, + .max_voice_rx_vol[VOC_WB_INDEX] = -200, + .min_voice_rx_vol[VOC_WB_INDEX] = -1700 +}; + +static struct platform_device msm_iearpiece_device = { + .name = "snddev_icodec", + .id = 0, + .dev = { .platform_data = &snddev_iearpiece_data }, +}; + +static struct adie_codec_action_unit imic_8KHz_osr256_actions[] = + HANDSET_TX_8000_OSR_256; + +static struct adie_codec_action_unit imic_16KHz_osr256_actions[] = + HANDSET_TX_16000_OSR_256; + +static struct adie_codec_action_unit imic_48KHz_osr256_actions[] = + HANDSET_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry imic_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = imic_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_8KHz_osr256_actions), + }, + { + .freq_plan = 16000, + .osr = 256, + .actions = imic_16KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_16KHz_osr256_actions), + }, + { + .freq_plan = 48000, + .osr = 256, + .actions = imic_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile imic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = imic_settings, + .setting_sz = ARRAY_SIZE(imic_settings), +}; + +static enum hsed_controller imic_pmctl_id[] = {PM_HSED_CONTROLLER_0}; + +static struct snddev_icodec_data snddev_imic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC, + .profile = &imic_profile, + .channel_mode = 1, + .pmctl_id = imic_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id), + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_imic_device = { + .name = "snddev_icodec", + .id = 1, + .dev = { .platform_data = &snddev_imic_data }, +}; + +static struct adie_codec_action_unit ihs_stereo_rx_48KHz_osr256_actions[] = + HEADSET_STEREO_RX_LEGACY_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_stereo_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_SPKR_STEREO, + .profile = &ihs_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .property = SIDE_TONE_MASK, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400 +}; + +static struct platform_device msm_ihs_stereo_rx_device = { + .name = "snddev_icodec", + .id = 2, + .dev = { .platform_data = &snddev_ihs_stereo_rx_data }, +}; + +static struct adie_codec_action_unit ihs_mono_rx_48KHz_osr256_actions[] = + HEADSET_RX_LEGACY_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_mono_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_mono_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_mono_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_mono_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_SPKR_MONO, + .profile = &ihs_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .property = SIDE_TONE_MASK, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400, + +}; + +static struct platform_device msm_ihs_mono_rx_device = { + .name = "snddev_icodec", + .id = 3, + .dev = { .platform_data = &snddev_ihs_mono_rx_data }, +}; + +static struct adie_codec_action_unit ihs_ffa_stereo_rx_48KHz_osr256_actions[] = + HEADSET_STEREO_RX_CAPLESS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_ffa_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_ffa_stereo_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_ffa_stereo_rx_48KHz_osr256_actions), + } +}; + +#ifdef CONFIG_DEBUG_FS +static struct adie_codec_action_unit + ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions[] = + HEADSET_STEREO_RX_CLASS_D_LEGACY_48000_OSR_256; + +static struct adie_codec_hwsetting_entry + ihs_ffa_stereo_rx_class_d_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions), + } +}; + +static struct adie_codec_action_unit + ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] = + HEADSET_STEREO_RX_LEGACY_48000_OSR_256; + +static struct adie_codec_hwsetting_entry + ihs_ffa_stereo_rx_class_ab_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions), + } +}; +#endif + +static struct adie_codec_dev_profile ihs_ffa_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_ffa_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_ffa_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_ffa_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_SPKR_STEREO, + .profile = &ihs_ffa_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_hsed_voltage_on, + .voltage_off = msm_snddev_hsed_voltage_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400, +}; + +static struct platform_device msm_ihs_ffa_stereo_rx_device = { + .name = "snddev_icodec", + .id = 4, + .dev = { .platform_data = &snddev_ihs_ffa_stereo_rx_data }, +}; + +static struct adie_codec_action_unit ihs_ffa_mono_rx_48KHz_osr256_actions[] = + HEADSET_RX_CAPLESS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_ffa_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_ffa_mono_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_ffa_mono_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_ffa_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_ffa_mono_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_ffa_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_ffa_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_mono_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_SPKR_MONO, + .profile = &ihs_ffa_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_hsed_voltage_on, + .pamp_off = msm_snddev_hsed_voltage_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400, +}; + +static struct platform_device msm_ihs_ffa_mono_rx_device = { + .name = "snddev_icodec", + .id = 5, + .dev = { .platform_data = &snddev_ihs_ffa_mono_rx_data }, +}; + +static struct adie_codec_action_unit ihs_mono_tx_8KHz_osr256_actions[] = + HEADSET_MONO_TX_8000_OSR_256; + +static struct adie_codec_action_unit ihs_mono_tx_16KHz_osr256_actions[] = + HEADSET_MONO_TX_16000_OSR_256; + +static struct adie_codec_action_unit ihs_mono_tx_48KHz_osr256_actions[] = + HEADSET_MONO_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_mono_tx_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ihs_mono_tx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_mono_tx_8KHz_osr256_actions), + }, + { + .freq_plan = 16000, + .osr = 256, + .actions = ihs_mono_tx_16KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_mono_tx_16KHz_osr256_actions), + }, + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_mono_tx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_mono_tx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_mono_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ihs_mono_tx_settings, + .setting_sz = ARRAY_SIZE(ihs_mono_tx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_mono_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "headset_mono_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_MIC, + .profile = &ihs_mono_tx_profile, + .channel_mode = 1, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_ihs_mono_tx_device = { + .name = "snddev_icodec", + .id = 6, + .dev = { .platform_data = &snddev_ihs_mono_tx_data }, +}; + +static struct adie_codec_action_unit ifmradio_handset_osr64_actions[] = + FM_HANDSET_OSR_64; + +static struct adie_codec_hwsetting_entry ifmradio_handset_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ifmradio_handset_osr64_actions, + .action_sz = ARRAY_SIZE(ifmradio_handset_osr64_actions), + } +}; + +static struct adie_codec_dev_profile ifmradio_handset_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ifmradio_handset_settings, + .setting_sz = ARRAY_SIZE(ifmradio_handset_settings), +}; + +static struct snddev_icodec_data snddev_ifmradio_handset_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM), + .name = "fmradio_handset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX, + .profile = &ifmradio_handset_profile, + .channel_mode = 1, + .default_sample_rate = 8000, + .pamp_on = NULL, + .pamp_off = NULL, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device msm_ifmradio_handset_device = { + .name = "snddev_icodec", + .id = 7, + .dev = { .platform_data = &snddev_ifmradio_handset_data }, +}; + + +static struct adie_codec_action_unit ispeaker_rx_48KHz_osr256_actions[] = + SPEAKER_STEREO_RX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispeaker_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ispeaker_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispeaker_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispeaker_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispeaker_rx_settings, + .setting_sz = ARRAY_SIZE(ispeaker_rx_settings), +}; + +static struct snddev_icodec_data snddev_ispeaker_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "speaker_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_STEREO, + .profile = &ispeaker_rx_profile, + .channel_mode = 2, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = &msm_snddev_poweramp_on, + .pamp_off = &msm_snddev_poweramp_off, + .max_voice_rx_vol[VOC_NB_INDEX] = 1000, + .min_voice_rx_vol[VOC_NB_INDEX] = -500, + .max_voice_rx_vol[VOC_WB_INDEX] = 1000, + .min_voice_rx_vol[VOC_WB_INDEX] = -500, +}; + +static struct platform_device msm_ispeaker_rx_device = { + .name = "snddev_icodec", + .id = 8, + .dev = { .platform_data = &snddev_ispeaker_rx_data }, + +}; + +static struct adie_codec_action_unit ifmradio_speaker_osr64_actions[] = + FM_SPEAKER_OSR_64; + +static struct adie_codec_hwsetting_entry ifmradio_speaker_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ifmradio_speaker_osr64_actions, + .action_sz = ARRAY_SIZE(ifmradio_speaker_osr64_actions), + } +}; + +static struct adie_codec_dev_profile ifmradio_speaker_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ifmradio_speaker_settings, + .setting_sz = ARRAY_SIZE(ifmradio_speaker_settings), +}; + +static struct snddev_icodec_data snddev_ifmradio_speaker_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM), + .name = "fmradio_speaker_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX, + .profile = &ifmradio_speaker_profile, + .channel_mode = 1, + .default_sample_rate = 8000, + .pamp_on = &msm_snddev_poweramp_on, + .pamp_off = &msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device msm_ifmradio_speaker_device = { + .name = "snddev_icodec", + .id = 9, + .dev = { .platform_data = &snddev_ifmradio_speaker_data }, +}; + +static struct adie_codec_action_unit ifmradio_headset_osr64_actions[] = + FM_HEADSET_STEREO_CLASS_D_LEGACY_OSR_64; + +static struct adie_codec_hwsetting_entry ifmradio_headset_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ifmradio_headset_osr64_actions, + .action_sz = ARRAY_SIZE(ifmradio_headset_osr64_actions), + } +}; + +static struct adie_codec_dev_profile ifmradio_headset_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ifmradio_headset_settings, + .setting_sz = ARRAY_SIZE(ifmradio_headset_settings), +}; + +static struct snddev_icodec_data snddev_ifmradio_headset_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM), + .name = "fmradio_headset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX, + .profile = &ifmradio_headset_profile, + .channel_mode = 1, + .default_sample_rate = 8000, + .pamp_on = NULL, + .pamp_off = NULL, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device msm_ifmradio_headset_device = { + .name = "snddev_icodec", + .id = 10, + .dev = { .platform_data = &snddev_ifmradio_headset_data }, +}; + + +static struct adie_codec_action_unit ifmradio_ffa_headset_osr64_actions[] = + FM_HEADSET_CLASS_AB_STEREO_CAPLESS_OSR_64; + +static struct adie_codec_hwsetting_entry ifmradio_ffa_headset_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ifmradio_ffa_headset_osr64_actions, + .action_sz = ARRAY_SIZE(ifmradio_ffa_headset_osr64_actions), + } +}; + +static struct adie_codec_dev_profile ifmradio_ffa_headset_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ifmradio_ffa_headset_settings, + .setting_sz = ARRAY_SIZE(ifmradio_ffa_headset_settings), +}; + +static struct snddev_icodec_data snddev_ifmradio_ffa_headset_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM), + .name = "fmradio_headset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX, + .profile = &ifmradio_ffa_headset_profile, + .channel_mode = 1, + .default_sample_rate = 8000, + .pamp_on = msm_snddev_hsed_voltage_on, + .pamp_off = msm_snddev_hsed_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device msm_ifmradio_ffa_headset_device = { + .name = "snddev_icodec", + .id = 11, + .dev = { .platform_data = &snddev_ifmradio_ffa_headset_data }, +}; + +static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "bt_sco_rx", + .copp_id = 1, + .acdb_id = ACDB_ID_BT_SCO_SPKR, + .channel_mode = 1, + .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL, + .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF, + .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING, + .max_voice_rx_vol[VOC_NB_INDEX] = 400, + .min_voice_rx_vol[VOC_NB_INDEX] = -1100, + .max_voice_rx_vol[VOC_WB_INDEX] = 400, + .min_voice_rx_vol[VOC_WB_INDEX] = -1100, +}; + +static struct snddev_ecodec_data snddev_bt_sco_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "bt_sco_tx", + .copp_id = 1, + .acdb_id = ACDB_ID_BT_SCO_MIC, + .channel_mode = 1, + .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL, + .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF, + .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING, +}; + +struct platform_device msm_bt_sco_earpiece_device = { + .name = "msm_snddev_ecodec", + .id = 0, + .dev = { .platform_data = &snddev_bt_sco_earpiece_data }, +}; + +struct platform_device msm_bt_sco_mic_device = { + .name = "msm_snddev_ecodec", + .id = 1, + .dev = { .platform_data = &snddev_bt_sco_mic_data }, +}; + +static struct adie_codec_action_unit idual_mic_endfire_8KHz_osr256_actions[] = + MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256; + +static struct adie_codec_hwsetting_entry idual_mic_endfire_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = idual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = idual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 48KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = idual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(idual_mic_endfire_settings), +}; + +static enum hsed_controller idual_mic_endfire_pmctl_id[] = { + PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2 +}; + +static struct snddev_icodec_data snddev_idual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC_ENDFIRE, + .profile = &idual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_idual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 12, + .dev = { .platform_data = &snddev_idual_mic_endfire_data }, +}; + + +static struct snddev_icodec_data\ + snddev_idual_mic_endfire_real_stereo_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx_real_stereo", + .copp_id = 0, + .acdb_id = PSEUDO_ACDB_ID, + .profile = &idual_mic_endfire_profile, + .channel_mode = REAL_STEREO_CHANNEL_MODE, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_real_stereo_tx_device = { + .name = "snddev_icodec", + .id = 26, + .dev = { .platform_data = + &snddev_idual_mic_endfire_real_stereo_data }, +}; + +static struct adie_codec_action_unit idual_mic_bs_8KHz_osr256_actions[] = + MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256; + +static struct adie_codec_hwsetting_entry idual_mic_broadside_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = idual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = idual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = idual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idual_mic_broadside_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idual_mic_broadside_settings, + .setting_sz = ARRAY_SIZE(idual_mic_broadside_settings), +}; + +static enum hsed_controller idual_mic_broadside_pmctl_id[] = { + PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2 +}; + +static struct snddev_icodec_data snddev_idual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_broadside_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC_BROADSIDE, + .profile = &idual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_broadside_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_idual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 13, + .dev = { .platform_data = &snddev_idual_mic_broadside_data }, +}; + +static struct adie_codec_action_unit ispk_dual_mic_ef_8KHz_osr256_actions[] = + SPEAKER_MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256; + +static struct adie_codec_hwsetting_entry ispk_dual_mic_ef_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ispk_dual_mic_ef_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16Khz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = ispk_dual_mic_ef_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 48KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = ispk_dual_mic_ef_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions), + }, +}; + +static struct adie_codec_dev_profile ispk_dual_mic_ef_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ispk_dual_mic_ef_settings, + .setting_sz = ARRAY_SIZE(ispk_dual_mic_ef_settings), +}; + +static struct snddev_icodec_data snddev_spk_idual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_endfire_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE, + .profile = &ispk_dual_mic_ef_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_spk_idual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 14, + .dev = { .platform_data = &snddev_spk_idual_mic_endfire_data }, +}; + +static struct adie_codec_action_unit ispk_dual_mic_bs_8KHz_osr256_actions[] = + SPEAKER_MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256; + +static struct adie_codec_hwsetting_entry ispk_dual_mic_bs_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ispk_dual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16Khz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = ispk_dual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 48KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = ispk_dual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions), + }, +}; + +static struct adie_codec_dev_profile ispk_dual_mic_bs_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ispk_dual_mic_bs_settings, + .setting_sz = ARRAY_SIZE(ispk_dual_mic_bs_settings), +}; +static struct snddev_icodec_data snddev_spk_idual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_broadside_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC_BROADSIDE, + .profile = &ispk_dual_mic_bs_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_broadside_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_spk_idual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 15, + .dev = { .platform_data = &snddev_spk_idual_mic_broadside_data }, +}; + +static struct adie_codec_action_unit itty_hs_mono_tx_8KHz_osr256_actions[] = + TTY_HEADSET_MONO_TX_8000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_hs_mono_tx_settings[] = { + /* 8KHz, 16KHz, 48KHz TTY Tx devices can shared same set of actions */ + { + .freq_plan = 8000, + .osr = 256, + .actions = itty_hs_mono_tx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions), + }, + { + .freq_plan = 16000, + .osr = 256, + .actions = itty_hs_mono_tx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions), + }, + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_hs_mono_tx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile itty_hs_mono_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = itty_hs_mono_tx_settings, + .setting_sz = ARRAY_SIZE(itty_hs_mono_tx_settings), +}; + +static struct snddev_icodec_data snddev_itty_hs_mono_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_TTY_HEADSET_MIC, + .profile = &itty_hs_mono_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_itty_hs_mono_tx_device = { + .name = "snddev_icodec", + .id = 16, + .dev = { .platform_data = &snddev_itty_hs_mono_tx_data }, +}; + +static struct adie_codec_action_unit itty_hs_mono_rx_8KHz_osr256_actions[] = + TTY_HEADSET_MONO_RX_CLASS_D_8000_OSR_256; + +static struct adie_codec_action_unit itty_hs_mono_rx_16KHz_osr256_actions[] = + TTY_HEADSET_MONO_RX_CLASS_D_16000_OSR_256; + +static struct adie_codec_action_unit itty_hs_mono_rx_48KHz_osr256_actions[] = + TTY_HEADSET_MONO_RX_CLASS_D_48000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_hs_mono_rx_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = itty_hs_mono_rx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(itty_hs_mono_rx_8KHz_osr256_actions), + }, + { + .freq_plan = 16000, + .osr = 256, + .actions = itty_hs_mono_rx_16KHz_osr256_actions, + .action_sz = ARRAY_SIZE(itty_hs_mono_rx_16KHz_osr256_actions), + }, + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_hs_mono_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(itty_hs_mono_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile itty_hs_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = itty_hs_mono_rx_settings, + .setting_sz = ARRAY_SIZE(itty_hs_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_itty_hs_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_TTY_HEADSET_SPKR, + .profile = &itty_hs_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .max_voice_rx_vol[VOC_NB_INDEX] = 0, + .min_voice_rx_vol[VOC_NB_INDEX] = 0, + .max_voice_rx_vol[VOC_WB_INDEX] = 0, + .min_voice_rx_vol[VOC_WB_INDEX] = 0, +}; + +static struct platform_device msm_itty_hs_mono_rx_device = { + .name = "snddev_icodec", + .id = 17, + .dev = { .platform_data = &snddev_itty_hs_mono_rx_data }, +}; + +static struct adie_codec_action_unit ispeaker_tx_8KHz_osr256_actions[] = + SPEAKER_TX_8000_OSR_256; + +static struct adie_codec_action_unit ispeaker_tx_48KHz_osr256_actions[] = + SPEAKER_TX_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispeaker_tx_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ispeaker_tx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispeaker_tx_8KHz_osr256_actions), + }, + { /* 8KHz profile is good for 16KHz */ + .freq_plan = 16000, + .osr = 256, + .actions = ispeaker_tx_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispeaker_tx_8KHz_osr256_actions), + }, + { + .freq_plan = 48000, + .osr = 256, + .actions = ispeaker_tx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispeaker_tx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispeaker_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ispeaker_tx_settings, + .setting_sz = ARRAY_SIZE(ispeaker_tx_settings), +}; + +static enum hsed_controller ispk_pmctl_id[] = {PM_HSED_CONTROLLER_0}; + +static struct snddev_icodec_data snddev_ispeaker_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC, + .profile = &ispeaker_tx_profile, + .channel_mode = 1, + .pmctl_id = ispk_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id), + .default_sample_rate = 48000, + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_ispeaker_tx_device = { + .name = "snddev_icodec", + .id = 18, + .dev = { .platform_data = &snddev_ispeaker_tx_data }, +}; + +static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] = + HANDSET_RX_48000_OSR_256_FFA; + +static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_ffa_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_ffa_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_ffa_settings, + .setting_sz = ARRAY_SIZE(iearpiece_ffa_settings), +}; + +static struct snddev_icodec_data snddev_iearpiece_ffa_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_SPKR, + .profile = &iearpiece_ffa_profile, + .channel_mode = 1, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -1400, + .min_voice_rx_vol[VOC_WB_INDEX] = -2900, +}; + +static struct platform_device msm_iearpiece_ffa_device = { + .name = "snddev_icodec", + .id = 19, + .dev = { .platform_data = &snddev_iearpiece_ffa_data }, +}; + +static struct adie_codec_action_unit imic_ffa_8KHz_osr256_actions[] = + HANDSET_TX_8000_OSR_256_FFA; + +static struct adie_codec_action_unit imic_ffa_16KHz_osr256_actions[] = + HANDSET_TX_16000_OSR_256_FFA; + +static struct adie_codec_action_unit imic_ffa_48KHz_osr256_actions[] = + HANDSET_TX_48000_OSR_256_FFA; + +static struct adie_codec_hwsetting_entry imic_ffa_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = imic_ffa_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_ffa_8KHz_osr256_actions), + }, + { + .freq_plan = 16000, + .osr = 256, + .actions = imic_ffa_16KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_ffa_16KHz_osr256_actions), + }, + { + .freq_plan = 48000, + .osr = 256, + .actions = imic_ffa_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_ffa_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile imic_ffa_profile = { + .path_type = ADIE_CODEC_TX, + .settings = imic_ffa_settings, + .setting_sz = ARRAY_SIZE(imic_ffa_settings), +}; + +static struct snddev_icodec_data snddev_imic_ffa_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC, + .profile = &imic_ffa_profile, + .channel_mode = 1, + .pmctl_id = imic_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id), + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_imic_ffa_device = { + .name = "snddev_icodec", + .id = 20, + .dev = { .platform_data = &snddev_imic_ffa_data }, +}; + + +static struct adie_codec_action_unit + ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] = + HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256; + + +static struct adie_codec_hwsetting_entry + ihs_stereo_speaker_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions, + .action_sz = + ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_stereo_speaker_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_speaker_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX, + .profile = &ihs_stereo_speaker_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .voltage_on = msm_snddev_hsed_voltage_on, + .voltage_off = msm_snddev_hsed_voltage_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -500, + .min_voice_rx_vol[VOC_NB_INDEX] = -2000, + .max_voice_rx_vol[VOC_WB_INDEX] = -500, + .min_voice_rx_vol[VOC_WB_INDEX] = -2000, +}; + +static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = { + .name = "snddev_icodec", + .id = 21, + .dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_stereo_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "hdmi_stereo_rx", + .copp_id = 3, + .acdb_id = ACDB_ID_HDMI, + .channel_mode = 2, + .sd_lines = MI2S_SD_0, + .route = msm_snddev_tx_route_config, + .deroute = msm_snddev_tx_route_deconfig, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_mi2s_stereo_rx_device = { + .name = "snddev_mi2s", + .id = 0, + .dev = { .platform_data = &snddev_mi2s_stereo_rx_data }, +}; + + +static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = { + .capability = SNDDEV_CAP_TX , + .name = "fmradio_stereo_tx", + .copp_id = 2, + .acdb_id = ACDB_ID_FM_TX, + .channel_mode = 2, + .sd_lines = MI2S_SD_3, + .route = NULL, + .deroute = NULL, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_mi2s_fm_tx_device = { + .name = "snddev_mi2s", + .id = 1, + .dev = { .platform_data = &snddev_mi2s_fm_tx_data}, +}; + +static struct snddev_icodec_data snddev_fluid_imic_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC, + .profile = &ispeaker_tx_profile, + .channel_mode = 1, + .pmctl_id = ispk_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id), + .default_sample_rate = 48000, + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_fluid_imic_tx_device = { + .name = "snddev_icodec", + .id = 22, + .dev = { .platform_data = &snddev_fluid_imic_tx_data }, +}; + +static struct snddev_icodec_data snddev_fluid_iearpiece_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_STEREO, + .profile = &ispeaker_rx_profile, + .channel_mode = 2, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = &msm_snddev_poweramp_on, + .pamp_off = &msm_snddev_poweramp_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -500, + .min_voice_rx_vol[VOC_NB_INDEX] = -1000, + .max_voice_rx_vol[VOC_WB_INDEX] = -500, + .min_voice_rx_vol[VOC_WB_INDEX] = -1000, +}; + +static struct platform_device msm_fluid_iearpeice_rx_device = { + .name = "snddev_icodec", + .id = 23, + .dev = { .platform_data = &snddev_fluid_iearpiece_rx_data }, +}; + +static struct adie_codec_action_unit fluid_idual_mic_ef_8KHz_osr256_actions[] = + MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256; + +static struct adie_codec_hwsetting_entry fluid_idual_mic_endfire_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = fluid_idual_mic_ef_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = fluid_idual_mic_ef_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions), + }, /* 8KHz profile can also be used for 48KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = fluid_idual_mic_ef_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile fluid_idual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = fluid_idual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(fluid_idual_mic_endfire_settings), +}; + +static enum hsed_controller fluid_idual_mic_endfire_pmctl_id[] = { + PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2 +}; + +static struct snddev_icodec_data snddev_fluid_idual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE, + .profile = &fluid_idual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = fluid_idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(fluid_idual_mic_endfire_pmctl_id), + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_fluid_idual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 24, + .dev = { .platform_data = &snddev_fluid_idual_mic_endfire_data }, +}; + +static struct snddev_icodec_data snddev_fluid_spk_idual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_endfire_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE, + .profile = &fluid_idual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = fluid_idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(fluid_idual_mic_endfire_pmctl_id), + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_fluid_spk_idual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 25, + .dev = { .platform_data = &snddev_fluid_spk_idual_mic_endfire_data }, +}; + +static struct snddev_virtual_data snddev_a2dp_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "a2dp_tx", + .copp_id = 5, + .acdb_id = PSEUDO_ACDB_ID, +}; + +static struct snddev_virtual_data snddev_a2dp_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "a2dp_rx", + .copp_id = 2, + .acdb_id = PSEUDO_ACDB_ID, +}; + +static struct platform_device msm_a2dp_rx_device = { + .name = "snddev_virtual", + .id = 0, + .dev = { .platform_data = &snddev_a2dp_rx_data }, +}; + +static struct platform_device msm_a2dp_tx_device = { + .name = "snddev_virtual", + .id = 1, + .dev = { .platform_data = &snddev_a2dp_tx_data }, +}; + +static struct snddev_virtual_data snddev_uplink_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "uplink_rx", + .copp_id = 5, + .acdb_id = PSEUDO_ACDB_ID, +}; + +static struct platform_device msm_uplink_rx_device = { + .name = "snddev_virtual", + .id = 2, + .dev = { .platform_data = &snddev_uplink_rx_data }, +}; + +static struct platform_device *snd_devices_ffa[] __initdata = { + &msm_iearpiece_ffa_device, + &msm_imic_ffa_device, + &msm_ifmradio_handset_device, + &msm_ihs_ffa_stereo_rx_device, + &msm_ihs_ffa_mono_rx_device, + &msm_ihs_mono_tx_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_ispeaker_rx_device, + &msm_ifmradio_speaker_device, + &msm_ifmradio_ffa_headset_device, + &msm_idual_mic_endfire_device, + &msm_idual_mic_broadside_device, + &msm_spk_idual_mic_endfire_device, + &msm_spk_idual_mic_broadside_device, + &msm_itty_hs_mono_tx_device, + &msm_itty_hs_mono_rx_device, + &msm_ispeaker_tx_device, + &msm_ihs_stereo_speaker_stereo_rx_device, + &msm_a2dp_rx_device, + &msm_a2dp_tx_device, + &msm_snddev_mi2s_stereo_rx_device, + &msm_snddev_mi2s_fm_tx_device, + &msm_uplink_rx_device, + &msm_real_stereo_tx_device, +}; + +static struct platform_device *snd_devices_surf[] __initdata = { + &msm_iearpiece_device, + &msm_imic_device, + &msm_ihs_stereo_rx_device, + &msm_ihs_mono_rx_device, + &msm_ihs_mono_tx_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_ifmradio_handset_device, + &msm_ispeaker_rx_device, + &msm_ifmradio_speaker_device, + &msm_ifmradio_headset_device, + &msm_itty_hs_mono_tx_device, + &msm_itty_hs_mono_rx_device, + &msm_ispeaker_tx_device, + &msm_ihs_stereo_speaker_stereo_rx_device, + &msm_a2dp_rx_device, + &msm_a2dp_tx_device, + &msm_snddev_mi2s_stereo_rx_device, + &msm_snddev_mi2s_fm_tx_device, + &msm_uplink_rx_device, +}; + +static struct platform_device *snd_devices_fluid[] __initdata = { + &msm_ihs_stereo_rx_device, + &msm_ihs_mono_rx_device, + &msm_ihs_mono_tx_device, + &msm_ispeaker_rx_device, + &msm_ispeaker_tx_device, + &msm_fluid_imic_tx_device, + &msm_fluid_iearpeice_rx_device, + &msm_fluid_idual_mic_endfire_device, + &msm_fluid_spk_idual_mic_endfire_device, + &msm_a2dp_rx_device, + &msm_a2dp_tx_device, + &msm_snddev_mi2s_stereo_rx_device, + &msm_uplink_rx_device, + &msm_ifmradio_speaker_device, + &msm_ifmradio_headset_device, +}; + +#ifdef CONFIG_DEBUG_FS +static void snddev_hsed_config_modify_setting(int type) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_ihs_ffa_stereo_rx_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + if (type == 1) { + icodec_data->voltage_on = NULL; + icodec_data->voltage_off = NULL; + icodec_data->profile->settings = + ihs_ffa_stereo_rx_class_d_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_ffa_stereo_rx_class_d_legacy_settings); + } else if (type == 2) { + icodec_data->voltage_on = NULL; + icodec_data->voltage_off = NULL; + icodec_data->profile->settings = + ihs_ffa_stereo_rx_class_ab_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_ffa_stereo_rx_class_ab_legacy_settings); + } + } +} + +static void snddev_hsed_config_restore_setting(void) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_ihs_ffa_stereo_rx_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + icodec_data->voltage_on = msm_snddev_hsed_voltage_on; + icodec_data->voltage_off = msm_snddev_hsed_voltage_off; + icodec_data->profile->settings = ihs_ffa_stereo_rx_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_ffa_stereo_rx_settings); + } +} + +static ssize_t snddev_hsed_config_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char cmd; + + if (get_user(cmd, ubuf)) + return -EFAULT; + + if (!strcmp(lb_str, "msm_hsed_config")) { + switch (cmd) { + case '0': + snddev_hsed_config_restore_setting(); + break; + + case '1': + snddev_hsed_config_modify_setting(1); + break; + + case '2': + snddev_hsed_config_modify_setting(2); + break; + + default: + break; + } + } + return cnt; +} + +static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations snddev_hsed_config_debug_fops = { + .open = snddev_hsed_config_debug_open, + .write = snddev_hsed_config_debug_write +}; +#endif + +void __ref msm_snddev_init(void) +{ + if (machine_is_msm7x30_ffa() || machine_is_msm8x55_ffa() || + machine_is_msm8x55_svlte_ffa()) { + platform_add_devices(snd_devices_ffa, + ARRAY_SIZE(snd_devices_ffa)); +#ifdef CONFIG_DEBUG_FS + debugfs_hsed_config = debugfs_create_file("msm_hsed_config", + S_IFREG | S_IRUGO, NULL, + (void *) "msm_hsed_config", &snddev_hsed_config_debug_fops); +#endif + } else if (machine_is_msm7x30_surf() || machine_is_msm8x55_surf() || + machine_is_msm8x55_svlte_surf()) + platform_add_devices(snd_devices_surf, + ARRAY_SIZE(snd_devices_surf)); + else if (machine_is_msm7x30_fluid()) + platform_add_devices(snd_devices_fluid, + ARRAY_SIZE(snd_devices_fluid)); + else + pr_err("%s: Unknown machine type\n", __func__); +} diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c b/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c new file mode 100644 index 00000000000..c0a48c826a5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c @@ -0,0 +1,1006 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timpani_profile_7x30.h" +#include + +/* define the value for BT_SCO */ +#define BT_SCO_PCM_CTL_VAL (PCM_CTL__RPCM_WIDTH__LINEAR_V |\ + PCM_CTL__TPCM_WIDTH__LINEAR_V) +#define BT_SCO_DATA_FORMAT_PADDING (DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V |\ + DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V) +#define BT_SCO_AUX_CODEC_INTF AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_hsed_config; +static void snddev_hsed_config_modify_setting(int type); +static void snddev_hsed_config_restore_setting(void); +#endif + +static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] = + EAR_PRI_MONO_8000_OSR_256; /* 8000 profile also works for 48k */ + +static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_ffa_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_ffa_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_ffa_settings, + .setting_sz = ARRAY_SIZE(iearpiece_ffa_settings), +}; + +static struct snddev_icodec_data snddev_iearpiece_ffa_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_SPKR, + .profile = &iearpiece_ffa_profile, + .channel_mode = 1, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .property = SIDE_TONE_MASK, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -1400, + .min_voice_rx_vol[VOC_WB_INDEX] = -2900, +}; + +static struct platform_device msm_iearpiece_ffa_device = { + .name = "snddev_icodec", + .id = 19, + .dev = { .platform_data = &snddev_iearpiece_ffa_data }, +}; + +static struct adie_codec_action_unit imic_ffa_48KHz_osr256_actions[] = + AMIC_PRI_MONO_8000_OSR_256; /* 8000 profile also works for 48k */ + +static struct adie_codec_hwsetting_entry imic_ffa_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = imic_ffa_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_ffa_48KHz_osr256_actions), + } +}; + +static enum hsed_controller imic_pmctl_id[] = {PM_HSED_CONTROLLER_0}; + +static struct adie_codec_dev_profile imic_ffa_profile = { + .path_type = ADIE_CODEC_TX, + .settings = imic_ffa_settings, + .setting_sz = ARRAY_SIZE(imic_ffa_settings), +}; + +static struct snddev_icodec_data snddev_imic_ffa_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC, + .profile = &imic_ffa_profile, + .channel_mode = 1, + .pmctl_id = imic_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id), + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_imic_ffa_device = { + .name = "snddev_icodec", + .id = 20, + .dev = { .platform_data = &snddev_imic_ffa_data }, +}; + +static struct adie_codec_action_unit ispkr_stereo_48KHz_osr256_actions[] = + SPEAKER_PRI_STEREO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispkr_stereo_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ispkr_stereo_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispkr_stereo_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispkr_stereo_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispkr_stereo_settings, + .setting_sz = ARRAY_SIZE(ispkr_stereo_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_stereo_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "speaker_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_STEREO, + .profile = &ispkr_stereo_profile, + .channel_mode = 2, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .max_voice_rx_vol[VOC_NB_INDEX] = 1000, + .min_voice_rx_vol[VOC_NB_INDEX] = -500, + .max_voice_rx_vol[VOC_WB_INDEX] = 1000, + .min_voice_rx_vol[VOC_WB_INDEX] = -500 +}; + +static struct platform_device msm_ispkr_stereo_device = { + .name = "snddev_icodec", + .id = 8, + .dev = { .platform_data = &snddev_ispkr_stereo_data }, +}; + +static struct adie_codec_action_unit iheadset_mic_tx_osr256_actions[] = + AMIC1_HEADSET_TX_MONO_PRIMARY_OSR256; + +static struct adie_codec_hwsetting_entry iheadset_mic_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iheadset_mic_tx_osr256_actions, + .action_sz = ARRAY_SIZE(iheadset_mic_tx_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iheadset_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = iheadset_mic_tx_settings, + .setting_sz = ARRAY_SIZE(iheadset_mic_tx_settings), +}; + +static struct snddev_icodec_data snddev_headset_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "headset_mono_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_MIC, + .profile = &iheadset_mic_profile, + .channel_mode = 1, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_headset_mic_device = { + .name = "snddev_icodec", + .id = 6, + .dev = { .platform_data = &snddev_headset_mic_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = { + .capability = SNDDEV_CAP_TX , + .name = "fmradio_stereo_tx", + .copp_id = 2, + .acdb_id = ACDB_ID_FM_TX, + .channel_mode = 2, + .sd_lines = MI2S_SD_3, + .route = NULL, + .deroute = NULL, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_mi2s_fm_tx_device = { + .name = "snddev_mi2s", + .id = 1, + .dev = { .platform_data = &snddev_mi2s_fm_tx_data}, +}; + +static struct snddev_mi2s_data snddev_mi2s_fm_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "fmradio_stereo_rx", + .copp_id = 3, + .acdb_id = ACDB_ID_FM_RX, + .channel_mode = 2, + .sd_lines = MI2S_SD_3, + .route = NULL, + .deroute = NULL, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_mi2s_fm_rx_device = { + .name = "snddev_mi2s", + .id = 2, + .dev = { .platform_data = &snddev_mi2s_fm_rx_data}, +}; + +static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "bt_sco_rx", + .copp_id = 1, + .acdb_id = ACDB_ID_BT_SCO_SPKR, + .channel_mode = 1, + .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL, + .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF, + .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING, + .max_voice_rx_vol[VOC_NB_INDEX] = 400, + .min_voice_rx_vol[VOC_NB_INDEX] = -1100, + .max_voice_rx_vol[VOC_WB_INDEX] = 400, + .min_voice_rx_vol[VOC_WB_INDEX] = -1100, +}; + +static struct snddev_ecodec_data snddev_bt_sco_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "bt_sco_tx", + .copp_id = 1, + .acdb_id = ACDB_ID_BT_SCO_MIC, + .channel_mode = 1, + .conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL, + .conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF, + .conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING, +}; + +static struct platform_device msm_bt_sco_earpiece_device = { + .name = "msm_snddev_ecodec", + .id = 0, + .dev = { .platform_data = &snddev_bt_sco_earpiece_data }, +}; + +static struct platform_device msm_bt_sco_mic_device = { + .name = "msm_snddev_ecodec", + .id = 1, + .dev = { .platform_data = &snddev_bt_sco_mic_data }, +}; + +static struct adie_codec_action_unit headset_ab_cpls_48KHz_osr256_actions[] = + HEADSET_AB_CPLS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry headset_ab_cpls_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = headset_ab_cpls_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_ab_cpls_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_ab_cpls_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_ab_cpls_settings, + .setting_sz = ARRAY_SIZE(headset_ab_cpls_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_SPKR_STEREO, + .profile = &headset_ab_cpls_profile, + .channel_mode = 2, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .property = SIDE_TONE_MASK, + .voltage_on = msm_snddev_hsed_voltage_on, + .voltage_off = msm_snddev_hsed_voltage_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400, +}; + +static struct platform_device msm_headset_stereo_device = { + .name = "snddev_icodec", + .id = 2, + .dev = { .platform_data = &snddev_ihs_stereo_rx_data }, +}; + +/*debug FS interface is exposed to test Class D and class AB mode + * amplifers for headset device folloowing options are supported + * 0 -> settings will be restored + * 1 -> Cladd D mode is selected + * 2 -> Class AB mode is selected +*/ +#ifdef CONFIG_DEBUG_FS +static struct adie_codec_action_unit + ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions[] = + HPH_PRI_D_LEG_STEREO; + +static struct adie_codec_hwsetting_entry + ihs_stereo_rx_class_d_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions), + } +}; + +static struct adie_codec_action_unit + ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] = + HPH_PRI_AB_LEG_STEREO; + +static struct adie_codec_hwsetting_entry + ihs_stereo_rx_class_ab_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions), + } +}; + +static void snddev_hsed_config_modify_setting(int type) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_headset_stereo_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + if (type == 1) { + icodec_data->voltage_on = NULL; + icodec_data->voltage_off = NULL; + icodec_data->profile->settings = + ihs_stereo_rx_class_d_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_stereo_rx_class_d_legacy_settings); + } else if (type == 2) { + icodec_data->voltage_on = NULL; + icodec_data->voltage_off = NULL; + icodec_data->profile->settings = + ihs_stereo_rx_class_ab_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_stereo_rx_class_ab_legacy_settings); + } + } +} + +static void snddev_hsed_config_restore_setting(void) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_headset_stereo_device; + icodec_data = device->dev.platform_data; + + if (icodec_data) { + icodec_data->voltage_on = msm_snddev_hsed_voltage_on; + icodec_data->voltage_off = msm_snddev_hsed_voltage_off; + icodec_data->profile->settings = headset_ab_cpls_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(headset_ab_cpls_settings); + } +} + +static ssize_t snddev_hsed_config_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char cmd; + + if (get_user(cmd, ubuf)) + return -EFAULT; + + if (!strcmp(lb_str, "msm_hsed_config")) { + switch (cmd) { + case '0': + snddev_hsed_config_restore_setting(); + break; + + case '1': + snddev_hsed_config_modify_setting(1); + break; + + case '2': + snddev_hsed_config_modify_setting(2); + break; + + default: + break; + } + } + return cnt; +} + +static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations snddev_hsed_config_debug_fops = { + .open = snddev_hsed_config_debug_open, + .write = snddev_hsed_config_debug_write +}; +#endif + +static enum hsed_controller ispk_pmctl_id[] = {PM_HSED_CONTROLLER_0}; + +static struct snddev_icodec_data snddev_ispkr_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC, + .profile = &imic_ffa_profile, + .channel_mode = 1, + .pmctl_id = ispk_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id), + .default_sample_rate = 48000, + .pamp_on = msm_snddev_tx_route_config, + .pamp_off = msm_snddev_tx_route_deconfig, +}; + +static struct platform_device msm_ispkr_mic_device = { + .name = "snddev_icodec", + .id = 18, + .dev = { .platform_data = &snddev_ispkr_mic_data }, +}; + +static struct adie_codec_action_unit idual_mic_endfire_8KHz_osr256_actions[] = + AMIC_DUAL_8000_OSR_256; + +static struct adie_codec_hwsetting_entry idual_mic_endfire_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = idual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = idual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 48KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = idual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(idual_mic_endfire_settings), +}; + +static enum hsed_controller idual_mic_endfire_pmctl_id[] = { + PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2 +}; + +static struct snddev_icodec_data snddev_idual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC_ENDFIRE, + .profile = &idual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_idual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 12, + .dev = { .platform_data = &snddev_idual_mic_endfire_data }, +}; + +static struct snddev_icodec_data snddev_spk_idual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_endfire_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE, + .profile = &idual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_spk_idual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 14, + .dev = { .platform_data = &snddev_spk_idual_mic_endfire_data }, +}; + +static struct adie_codec_action_unit itty_mono_tx_actions[] = + TTY_HEADSET_MONO_TX_8000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_mono_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_mono_tx_actions, + .action_sz = ARRAY_SIZE(itty_mono_tx_actions), + }, +}; + +static struct adie_codec_dev_profile itty_mono_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = itty_mono_tx_settings, + .setting_sz = ARRAY_SIZE(itty_mono_tx_settings), +}; + +static struct snddev_icodec_data snddev_itty_mono_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_TTY_HEADSET_MIC, + .profile = &itty_mono_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pmctl_id = NULL, + .pmctl_id_sz = 0, + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_itty_mono_tx_device = { + .name = "snddev_icodec", + .id = 16, + .dev = { .platform_data = &snddev_itty_mono_tx_data }, +}; + +static struct adie_codec_action_unit itty_mono_rx_actions[] = + TTY_HEADSET_MONO_RX_8000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_mono_rx_actions, + .action_sz = ARRAY_SIZE(itty_mono_rx_actions), + }, +}; + +static struct adie_codec_dev_profile itty_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = itty_mono_rx_settings, + .setting_sz = ARRAY_SIZE(itty_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_itty_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_TTY_HEADSET_SPKR, + .profile = &itty_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = NULL, + .pamp_off = NULL, + .max_voice_rx_vol[VOC_NB_INDEX] = 0, + .min_voice_rx_vol[VOC_NB_INDEX] = 0, + .max_voice_rx_vol[VOC_WB_INDEX] = 0, + .min_voice_rx_vol[VOC_WB_INDEX] = 0, +}; + +static struct platform_device msm_itty_mono_rx_device = { + .name = "snddev_icodec", + .id = 17, + .dev = { .platform_data = &snddev_itty_mono_rx_data }, +}; + +static struct snddev_virtual_data snddev_a2dp_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "a2dp_tx", + .copp_id = 5, + .acdb_id = PSEUDO_ACDB_ID, +}; + +static struct snddev_virtual_data snddev_a2dp_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "a2dp_rx", + .copp_id = 2, + .acdb_id = PSEUDO_ACDB_ID, +}; + +static struct platform_device msm_a2dp_rx_device = { + .name = "snddev_virtual", + .id = 0, + .dev = { .platform_data = &snddev_a2dp_rx_data }, +}; + +static struct platform_device msm_a2dp_tx_device = { + .name = "snddev_virtual", + .id = 1, + .dev = { .platform_data = &snddev_a2dp_tx_data }, +}; + +static struct snddev_virtual_data snddev_uplink_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "uplink_rx", + .copp_id = 5, + .acdb_id = PSEUDO_ACDB_ID, +}; + +static struct platform_device msm_uplink_rx_device = { + .name = "snddev_virtual", + .id = 2, + .dev = { .platform_data = &snddev_uplink_rx_data }, +}; + +static struct snddev_icodec_data\ + snddev_idual_mic_endfire_real_stereo_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx_real_stereo", + .copp_id = 0, + .acdb_id = PSEUDO_ACDB_ID, + .profile = &idual_mic_endfire_profile, + .channel_mode = REAL_STEREO_CHANNEL_MODE, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_endfire_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_real_stereo_tx_device = { + .name = "snddev_icodec", + .id = 26, + .dev = { .platform_data = + &snddev_idual_mic_endfire_real_stereo_data }, +}; + +static struct adie_codec_action_unit ihs_ffa_mono_rx_48KHz_osr256_actions[] = + HEADSET_RX_CAPLESS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ihs_ffa_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_ffa_mono_rx_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ihs_ffa_mono_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_ffa_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_ffa_mono_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_ffa_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_ffa_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_mono_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_SPKR_MONO, + .profile = &ihs_ffa_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_hsed_voltage_on, + .pamp_off = msm_snddev_hsed_voltage_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -700, + .min_voice_rx_vol[VOC_NB_INDEX] = -2200, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400, + .property = SIDE_TONE_MASK, +}; + +static struct platform_device msm_ihs_ffa_mono_rx_device = { + .name = "snddev_icodec", + .id = 5, + .dev = { .platform_data = &snddev_ihs_ffa_mono_rx_data }, +}; + +static struct adie_codec_action_unit + ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] = + HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256; + + +static struct adie_codec_hwsetting_entry + ihs_stereo_speaker_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions, + .action_sz = + ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_stereo_speaker_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_speaker_stereo_rx", + .copp_id = 0, + .acdb_id = ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX, + .profile = &ihs_stereo_speaker_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .voltage_on = msm_snddev_hsed_voltage_on, + .voltage_off = msm_snddev_hsed_voltage_off, + .max_voice_rx_vol[VOC_NB_INDEX] = -500, + .min_voice_rx_vol[VOC_NB_INDEX] = -2000, + .max_voice_rx_vol[VOC_WB_INDEX] = -900, + .min_voice_rx_vol[VOC_WB_INDEX] = -2400, +}; + +static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = { + .name = "snddev_icodec", + .id = 21, + .dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data }, +}; + +static struct adie_codec_action_unit ispk_dual_mic_bs_8KHz_osr256_actions[] = + HS_DMIC2_STEREO_8000_OSR_256; + +static struct adie_codec_hwsetting_entry ispk_dual_mic_bs_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = ispk_dual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16Khz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = ispk_dual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 48KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = ispk_dual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions), + }, +}; + +static enum hsed_controller idual_mic_broadside_pmctl_id[] = { + PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2 +}; + +static struct adie_codec_dev_profile ispk_dual_mic_bs_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ispk_dual_mic_bs_settings, + .setting_sz = ARRAY_SIZE(ispk_dual_mic_bs_settings), +}; +static struct snddev_icodec_data snddev_spk_idual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_broadside_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_SPKR_PHONE_MIC_BROADSIDE, + .profile = &ispk_dual_mic_bs_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_broadside_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_spk_idual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 15, + .dev = { .platform_data = &snddev_spk_idual_mic_broadside_data }, +}; + +static struct adie_codec_action_unit idual_mic_bs_8KHz_osr256_actions[] = + HS_DMIC2_STEREO_8000_OSR_256; + +static struct adie_codec_hwsetting_entry idual_mic_broadside_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = idual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 16000, + .osr = 256, + .actions = idual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions), + }, /* 8KHz profile can be used for 16KHz */ + { + .freq_plan = 48000, + .osr = 256, + .actions = idual_mic_bs_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idual_mic_broadside_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idual_mic_broadside_settings, + .setting_sz = ARRAY_SIZE(idual_mic_broadside_settings), +}; + +static struct snddev_icodec_data snddev_idual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_broadside_tx", + .copp_id = 0, + .acdb_id = ACDB_ID_HANDSET_MIC_BROADSIDE, + .profile = &idual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pmctl_id = idual_mic_broadside_pmctl_id, + .pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id), + .pamp_on = NULL, + .pamp_off = NULL, +}; + +static struct platform_device msm_idual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 13, + .dev = { .platform_data = &snddev_idual_mic_broadside_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_stereo_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "hdmi_stereo_rx", + .copp_id = 3, + .acdb_id = ACDB_ID_HDMI, + .channel_mode = 2, + .sd_lines = MI2S_SD_0, + .route = msm_snddev_tx_route_config, + .deroute = msm_snddev_tx_route_deconfig, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_mi2s_stereo_rx_device = { + .name = "snddev_mi2s", + .id = 0, + .dev = { .platform_data = &snddev_mi2s_stereo_rx_data }, +}; + +static struct adie_codec_action_unit auxpga_lb_lo_actions[] = + LB_AUXPGA_LO_STEREO; + +static struct adie_codec_hwsetting_entry auxpga_lb_lo_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = auxpga_lb_lo_actions, + .action_sz = ARRAY_SIZE(auxpga_lb_lo_actions), + }, +}; + +static struct adie_codec_dev_profile auxpga_lb_lo_profile = { + .path_type = ADIE_CODEC_LB, + .settings = auxpga_lb_lo_settings, + .setting_sz = ARRAY_SIZE(auxpga_lb_lo_settings), +}; + +static struct snddev_icodec_data snddev_auxpga_lb_lo_data = { + .capability = SNDDEV_CAP_LB, + .name = "auxpga_loopback_lo", + .copp_id = 0, + .acdb_id = PSEUDO_ACDB_ID, + .profile = &auxpga_lb_lo_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_ANALOG, +}; + +static struct platform_device msm_auxpga_lb_lo_device = { + .name = "snddev_icodec", + .id = 27, + .dev = { .platform_data = &snddev_auxpga_lb_lo_data }, +}; + +static struct adie_codec_action_unit auxpga_lb_hs_actions[] = + LB_AUXPGA_HPH_AB_CPLS_STEREO; + +static struct adie_codec_hwsetting_entry auxpga_lb_hs_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = auxpga_lb_hs_actions, + .action_sz = ARRAY_SIZE(auxpga_lb_hs_actions), + }, +}; + +static struct adie_codec_dev_profile auxpga_lb_hs_profile = { + .path_type = ADIE_CODEC_LB, + .settings = auxpga_lb_hs_settings, + .setting_sz = ARRAY_SIZE(auxpga_lb_hs_settings), +}; + +static struct snddev_icodec_data snddev_auxpga_lb_hs_data = { + .capability = SNDDEV_CAP_LB, + .name = "auxpga_loopback_hs", + .copp_id = 0, + .acdb_id = PSEUDO_ACDB_ID, + .profile = &auxpga_lb_hs_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_hsed_voltage_on, + .voltage_off = msm_snddev_hsed_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_ANALOG, +}; + +static struct platform_device msm_auxpga_lb_hs_device = { + .name = "snddev_icodec", + .id = 25, + .dev = { .platform_data = &snddev_auxpga_lb_hs_data }, +}; + +static struct platform_device *snd_devices_ffa[] __initdata = { + &msm_iearpiece_ffa_device, + &msm_imic_ffa_device, + &msm_ispkr_stereo_device, + &msm_headset_mic_device, + &msm_ihs_ffa_mono_rx_device, + &msm_snddev_mi2s_fm_rx_device, + &msm_snddev_mi2s_fm_tx_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_ispkr_mic_device, + &msm_headset_stereo_device, + &msm_idual_mic_endfire_device, + &msm_spk_idual_mic_endfire_device, + &msm_itty_mono_tx_device, + &msm_itty_mono_rx_device, + &msm_a2dp_rx_device, + &msm_a2dp_tx_device, + &msm_uplink_rx_device, + &msm_real_stereo_tx_device, + &msm_ihs_stereo_speaker_stereo_rx_device, + &msm_spk_idual_mic_broadside_device, + &msm_idual_mic_broadside_device, + &msm_snddev_mi2s_stereo_rx_device, + &msm_auxpga_lb_hs_device, + &msm_auxpga_lb_lo_device, +}; + +void __ref msm_snddev_init_timpani(void) +{ + platform_add_devices(snd_devices_ffa, + ARRAY_SIZE(snd_devices_ffa)); +#ifdef CONFIG_DEBUG_FS + debugfs_hsed_config = debugfs_create_file("msm_hsed_config", + S_IFREG | S_IWUGO, NULL, + (void *) "msm_hsed_config", &snddev_hsed_config_debug_fops); + if (!debugfs_hsed_config) + pr_err("failed to create msm_head_config debug fs entry\n"); +#endif + +} diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c b/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c new file mode 100644 index 00000000000..a5da912b29b --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c @@ -0,0 +1,484 @@ +/* Copyright (c) 2009,2011 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Context for each external codec device */ +struct snddev_ecodec_state { + struct snddev_ecodec_data *data; + u32 sample_rate; + bool enabled; +}; + +/* Global state for the driver */ +struct snddev_ecodec_drv_state { + struct mutex dev_lock; + u32 rx_active; /* ensure one rx device at a time */ + u32 tx_active; /* ensure one tx device at a time */ + struct clk *lpa_core_clk; + struct clk *ecodec_clk; +}; + +#define ADSP_CTL 1 + +static struct snddev_ecodec_drv_state snddev_ecodec_drv; + +static int snddev_ecodec_open_rx(struct snddev_ecodec_state *ecodec) +{ + int rc = 0; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + struct msm_afe_config afe_config; + int ret = 0; + + MM_DBG("snddev_ecodec_open_rx\n"); + + if (!drv->tx_active) { + /* request GPIO */ + rc = aux_pcm_gpios_request(); + if (rc) { + MM_ERR("GPIO enable failed\n"); + goto done; + } + /* config clocks */ + clk_enable(drv->lpa_core_clk); + + /*if long sync is selected in aux PCM interface + ecodec clock is updated to work with 128KHz, + if short sync is selected ecodec clock is updated to + work with 2.048MHz frequency, actual clock output is + different than the SW configuration by factor of two*/ + if (!(ecodec->data->conf_aux_codec_intf & + AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V)) { + if (ecodec->data->conf_aux_codec_intf & + AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V) { + MM_DBG("Update ecodec clock to 128 KHz, long " + "sync in master mode is selected\n"); + ret = clk_set_rate(drv->ecodec_clk, 256000); + if (ret < 0) + MM_ERR("Error updating ecodec clock" + " to 128KHz\n"); + } else if (ecodec->data->conf_aux_codec_intf & + AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V) { + MM_DBG("Update ecodec clock to 2 MHz, short" + " sync in slave mode is selected\n"); + ret = clk_set_rate(drv->ecodec_clk, 4096000); + if (ret < 0) + MM_ERR("Error updating ecodec clock" + " to 2.048MHz\n"); + } else { + MM_DBG("Update ecodec clock to 2 MHz, short" + " sync in master mode is selected\n"); + ret = clk_set_rate(drv->ecodec_clk, 4096000); + if (ret < 0) + MM_ERR("Error updating ecodec clock" + " to 2.048MHz\n"); + } + } + + /* enable ecodec clk */ + clk_enable(drv->ecodec_clk); + + /* let ADSP confiure AUX PCM regs */ + aux_codec_adsp_codec_ctl_en(ADSP_CTL); + + /* let adsp configure pcm path */ + aux_codec_pcm_path_ctl_en(ADSP_CTL); + + /* choose ADSP_A */ + audio_interct_aux_regsel(AUDIO_ADSP_A); + audio_interct_tpcm_source(AUDIO_ADSP_A); + audio_interct_rpcm_source(AUDIO_ADSP_A); + + clk_disable(drv->lpa_core_clk); + + /* send AUX_CODEC_CONFIG to AFE */ + rc = afe_config_aux_codec(ecodec->data->conf_pcm_ctl_val, + ecodec->data->conf_aux_codec_intf, + ecodec->data->conf_data_format_padding_val); + if (IS_ERR_VALUE(rc)) + goto error; + } + /* send CODEC CONFIG to AFE */ + afe_config.sample_rate = ecodec->sample_rate / 1000; + afe_config.channel_mode = ecodec->data->channel_mode; + afe_config.volume = AFE_VOLUME_UNITY; + rc = afe_enable(AFE_HW_PATH_AUXPCM_RX, &afe_config); + if (IS_ERR_VALUE(rc)) { + if (!drv->tx_active) { + aux_pcm_gpios_free(); + clk_disable(drv->ecodec_clk); + } + goto done; + } + + ecodec->enabled = 1; + return 0; + +error: + aux_pcm_gpios_free(); + clk_disable(drv->ecodec_clk); +done: + return rc; +} + +static int snddev_ecodec_close_rx(struct snddev_ecodec_state *ecodec) +{ + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + /* free GPIO */ + if (!drv->tx_active) { + aux_pcm_gpios_free(); + clk_disable(drv->ecodec_clk); + } + + /* disable AFE */ + afe_disable(AFE_HW_PATH_AUXPCM_RX); + + ecodec->enabled = 0; + + return 0; +} + +static int snddev_ecodec_open_tx(struct snddev_ecodec_state *ecodec) +{ + int rc = 0; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + struct msm_afe_config afe_config; + int ret = 0; + + MM_DBG("snddev_ecodec_open_tx\n"); + + /* request GPIO */ + if (!drv->rx_active) { + rc = aux_pcm_gpios_request(); + if (rc) { + MM_ERR("GPIO enable failed\n"); + goto done; + } + /* config clocks */ + clk_enable(drv->lpa_core_clk); + + /*if long sync is selected in aux PCM interface + ecodec clock is updated to work with 128KHz, + if short sync is selected ecodec clock is updated to + work with 2.048MHz frequency, actual clock output is + different than the SW configuration by factor of two*/ + if (!(ecodec->data->conf_aux_codec_intf & + AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V)) { + if (ecodec->data->conf_aux_codec_intf & + AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V) { + MM_DBG("Update ecodec clock to 128 KHz, long " + "sync in master mode is selected\n"); + ret = clk_set_rate(drv->ecodec_clk, 256000); + if (ret < 0) + MM_ERR("Error updating ecodec clock" + " to 128KHz\n"); + } else if (ecodec->data->conf_aux_codec_intf & + AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V) { + MM_DBG("Update ecodec clock to 2 MHz, short" + " sync in slave mode is selected\n"); + ret = clk_set_rate(drv->ecodec_clk, 4096000); + if (ret < 0) + MM_ERR("Error updating ecodec clock" + " to 2.048MHz\n"); + } else { + MM_DBG("Update ecodec clock to 2 MHz, short" + " sync in master mode is selected\n"); + ret = clk_set_rate(drv->ecodec_clk, 4096000); + if (ret < 0) + MM_ERR("Error updating ecodec clock" + " to 2.048MHz\n"); + } + } + + /* enable ecodec clk */ + clk_enable(drv->ecodec_clk); + + /* let ADSP confiure AUX PCM regs */ + aux_codec_adsp_codec_ctl_en(ADSP_CTL); + + /* let adsp configure pcm path */ + aux_codec_pcm_path_ctl_en(ADSP_CTL); + + /* choose ADSP_A */ + audio_interct_aux_regsel(AUDIO_ADSP_A); + audio_interct_tpcm_source(AUDIO_ADSP_A); + audio_interct_rpcm_source(AUDIO_ADSP_A); + + clk_disable(drv->lpa_core_clk); + + /* send AUX_CODEC_CONFIG to AFE */ + rc = afe_config_aux_codec(ecodec->data->conf_pcm_ctl_val, + ecodec->data->conf_aux_codec_intf, + ecodec->data->conf_data_format_padding_val); + if (IS_ERR_VALUE(rc)) + goto error; + } + /* send CODEC CONFIG to AFE */ + afe_config.sample_rate = ecodec->sample_rate / 1000; + afe_config.channel_mode = ecodec->data->channel_mode; + afe_config.volume = AFE_VOLUME_UNITY; + rc = afe_enable(AFE_HW_PATH_AUXPCM_TX, &afe_config); + if (IS_ERR_VALUE(rc)) { + if (!drv->rx_active) { + aux_pcm_gpios_free(); + clk_disable(drv->ecodec_clk); + } + goto done; + } + + ecodec->enabled = 1; + return 0; + +error: + clk_disable(drv->ecodec_clk); + aux_pcm_gpios_free(); +done: + return rc; +} + +static int snddev_ecodec_close_tx(struct snddev_ecodec_state *ecodec) +{ + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + /* free GPIO */ + if (!drv->rx_active) { + aux_pcm_gpios_free(); + clk_disable(drv->ecodec_clk); + } + + /* disable AFE */ + afe_disable(AFE_HW_PATH_AUXPCM_TX); + + ecodec->enabled = 0; + + return 0; +} + + +static int snddev_ecodec_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_ecodec_state *ecodec; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + ecodec = dev_info->private_data; + + if (ecodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->dev_lock); + if (drv->rx_active) { + mutex_unlock(&drv->dev_lock); + rc = -EBUSY; + goto error; + } + rc = snddev_ecodec_open_rx(ecodec); + if (!IS_ERR_VALUE(rc)) + drv->rx_active = 1; + mutex_unlock(&drv->dev_lock); + } else { + mutex_lock(&drv->dev_lock); + if (drv->tx_active) { + mutex_unlock(&drv->dev_lock); + rc = -EBUSY; + goto error; + } + rc = snddev_ecodec_open_tx(ecodec); + if (!IS_ERR_VALUE(rc)) + drv->tx_active = 1; + mutex_unlock(&drv->dev_lock); + } +error: + return rc; +} + +static int snddev_ecodec_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_ecodec_state *ecodec; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + ecodec = dev_info->private_data; + + if (ecodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->dev_lock); + if (!drv->rx_active) { + mutex_unlock(&drv->dev_lock); + rc = -EPERM; + goto error; + } + rc = snddev_ecodec_close_rx(ecodec); + if (!IS_ERR_VALUE(rc)) + drv->rx_active = 0; + mutex_unlock(&drv->dev_lock); + } else { + mutex_lock(&drv->dev_lock); + if (!drv->tx_active) { + mutex_unlock(&drv->dev_lock); + rc = -EPERM; + goto error; + } + rc = snddev_ecodec_close_tx(ecodec); + if (!IS_ERR_VALUE(rc)) + drv->tx_active = 0; + mutex_unlock(&drv->dev_lock); + } + +error: + return rc; +} + +static int snddev_ecodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc = 0; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + return 8000; + +error: + return rc; +} + +static int snddev_ecodec_probe(struct platform_device *pdev) +{ + int rc = 0, i; + struct snddev_ecodec_data *pdata; + struct msm_snddev_info *dev_info; + struct snddev_ecodec_state *ecodec; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller \n"); + rc = -1; + goto error; + } + pdata = pdev->dev.platform_data; + + ecodec = kzalloc(sizeof(struct snddev_ecodec_state), GFP_KERNEL); + if (!ecodec) { + rc = -ENOMEM; + goto error; + } + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + kfree(ecodec); + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->acdb_id = pdata->acdb_id; + dev_info->private_data = (void *) ecodec; + dev_info->dev_ops.open = snddev_ecodec_open; + dev_info->dev_ops.close = snddev_ecodec_close; + dev_info->dev_ops.set_freq = snddev_ecodec_set_freq; + dev_info->dev_ops.enable_sidetone = NULL; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + + msm_snddev_register(dev_info); + ecodec->data = pdata; + ecodec->sample_rate = 8000; /* Default to 8KHz */ + if (pdata->capability & SNDDEV_CAP_RX) { + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) { + dev_info->max_voc_rx_vol[i] = + pdata->max_voice_rx_vol[i]; + dev_info->min_voc_rx_vol[i] = + pdata->min_voice_rx_vol[i]; + } + } +error: + return rc; +} + +static int snddev_ecodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_ecodec_driver = { + .probe = snddev_ecodec_probe, + .remove = snddev_ecodec_remove, + .driver = { .name = "msm_snddev_ecodec" } +}; + +static int __init snddev_ecodec_init(void) +{ + int rc = 0; + struct snddev_ecodec_drv_state *ecodec_drv = &snddev_ecodec_drv; + + MM_INFO("snddev_ecodec_init\n"); + rc = platform_driver_register(&snddev_ecodec_driver); + if (IS_ERR_VALUE(rc)) + goto error_platform_driver; + ecodec_drv->ecodec_clk = clk_get(NULL, "ecodec_clk"); + if (IS_ERR(ecodec_drv->ecodec_clk)) + goto error_ecodec_clk; + ecodec_drv->lpa_core_clk = clk_get(NULL, "lpa_core_clk"); + if (IS_ERR(ecodec_drv->lpa_core_clk)) + goto error_lpa_core_clk; + + + mutex_init(&ecodec_drv->dev_lock); + ecodec_drv->rx_active = 0; + ecodec_drv->tx_active = 0; + return 0; + +error_lpa_core_clk: + clk_put(ecodec_drv->ecodec_clk); +error_ecodec_clk: + platform_driver_unregister(&snddev_ecodec_driver); +error_platform_driver: + + MM_ERR("encounter error\n"); + return -ENODEV; +} + +static void __exit snddev_ecodec_exit(void) +{ + struct snddev_ecodec_drv_state *ecodec_drv = &snddev_ecodec_drv; + + platform_driver_unregister(&snddev_ecodec_driver); + clk_put(ecodec_drv->ecodec_clk); + + return; +} + +module_init(snddev_ecodec_init); +module_exit(snddev_ecodec_exit); + +MODULE_DESCRIPTION("ECodec Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c b/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c new file mode 100644 index 00000000000..80c9a01d1b4 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c @@ -0,0 +1,1218 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMPS_AUDIO_PLAYBACK_ID "AUPB" +#define SMPS_AUDIO_RECORD_ID "AURC" + +#define SNDDEV_ICODEC_PCM_SZ 32 /* 16 bit / sample stereo mode */ +#define SNDDEV_ICODEC_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */ +#define SNDDEV_ICODEC_CLK_RATE(freq) \ + (((freq) * (SNDDEV_ICODEC_PCM_SZ)) << (SNDDEV_ICODEC_MUL_FACTOR)) + +#ifdef CONFIG_DEBUG_FS +static struct adie_codec_action_unit debug_rx_actions[] = + HANDSET_RX_8000_OSR_256; + +static struct adie_codec_action_unit debug_tx_lb_actions[] = { + { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF }, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00) }, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5C)}, + { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY }, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)}, + { ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x91, 0xFF, 0x01)}, /* Start loop back */ + { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)}, + { ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, + { ADIE_CODEC_ACTION_ENTRY, + ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)} +}; + +static struct adie_codec_action_unit debug_tx_actions[] = + HANDSET_TX_8000_OSR_256; + +static struct adie_codec_hwsetting_entry debug_rx_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = debug_rx_actions, + .action_sz = ARRAY_SIZE(debug_rx_actions), + } +}; + +static struct adie_codec_hwsetting_entry debug_tx_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = debug_tx_actions, + .action_sz = ARRAY_SIZE(debug_tx_actions), + } +}; + +static struct adie_codec_hwsetting_entry debug_tx_lb_settings[] = { + { + .freq_plan = 8000, + .osr = 256, + .actions = debug_tx_lb_actions, + .action_sz = ARRAY_SIZE(debug_tx_lb_actions), + } +}; + +static struct adie_codec_dev_profile debug_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = debug_rx_settings, + .setting_sz = ARRAY_SIZE(debug_rx_settings), +}; + +static struct adie_codec_dev_profile debug_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = debug_tx_settings, + .setting_sz = ARRAY_SIZE(debug_tx_settings), +}; + +static struct adie_codec_dev_profile debug_tx_lb_profile = { + .path_type = ADIE_CODEC_TX, + .settings = debug_tx_lb_settings, + .setting_sz = ARRAY_SIZE(debug_tx_lb_settings), +}; +#endif /* CONFIG_DEBUG_FS */ + +/* Context for each internal codec sound device */ +struct snddev_icodec_state { + struct snddev_icodec_data *data; + struct adie_codec_path *adie_path; + u32 sample_rate; + u32 enabled; +}; + +/* Global state for the driver */ +struct snddev_icodec_drv_state { + struct mutex rx_lock; + struct mutex lb_lock; + struct mutex tx_lock; + u32 rx_active; /* ensure one rx device at a time */ + u32 tx_active; /* ensure one tx device at a time */ + struct clk *rx_mclk; + struct clk *rx_sclk; + struct clk *tx_mclk; + struct clk *tx_sclk; + struct clk *lpa_codec_clk; + struct clk *lpa_core_clk; + struct clk *lpa_p_clk; + struct lpa_drv *lpa; + + struct pm_qos_request rx_pm_qos_req; + struct pm_qos_request tx_pm_qos_req; +}; + +static struct snddev_icodec_drv_state snddev_icodec_drv; + +static int snddev_icodec_open_rx(struct snddev_icodec_state *icodec) +{ + int trc, err; + int smps_mode = PMAPP_SMPS_MODE_VOTE_PWM; + struct msm_afe_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + struct lpa_codec_config lpa_config; + + pm_qos_update_request(&drv->rx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + if ((icodec->data->acdb_id == ACDB_ID_HEADSET_SPKR_MONO) || + (icodec->data->acdb_id == ACDB_ID_HEADSET_SPKR_STEREO)) { + /* Vote PMAPP_SMPS_MODE_VOTE_PFM for headset */ + smps_mode = PMAPP_SMPS_MODE_VOTE_PFM; + MM_DBG("snddev_icodec_open_rx: PMAPP_SMPS_MODE_VOTE_PFM \n"); + } else + MM_DBG("snddev_icodec_open_rx: PMAPP_SMPS_MODE_VOTE_PWM \n"); + + /* Vote for SMPS mode*/ + err = pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID, + PMAPP_VREG_S4, smps_mode); + if (err != 0) + MM_ERR("pmapp_smps_mode_vote error %d\n", err); + + /* enable MI2S RX master block */ + /* enable MI2S RX bit clock */ + trc = clk_set_rate(drv->rx_mclk, + SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) + goto error_invalid_freq; + clk_enable(drv->rx_mclk); + clk_enable(drv->rx_sclk); + /* clk_set_rate(drv->lpa_codec_clk, 1); */ /* Remove if use pcom */ + clk_enable(drv->lpa_p_clk); + clk_enable(drv->lpa_codec_clk); + clk_enable(drv->lpa_core_clk); + + /* Enable LPA sub system + */ + drv->lpa = lpa_get(); + if (!drv->lpa) + goto error_lpa; + lpa_config.sample_rate = icodec->sample_rate; + lpa_config.sample_width = 16; + lpa_config.output_interface = LPA_OUTPUT_INTF_WB_CODEC; + lpa_config.num_channels = icodec->data->channel_mode; + lpa_cmd_codec_config(drv->lpa, &lpa_config); + + /* Set audio interconnect reg to LPA */ + audio_interct_codec(AUDIO_INTERCT_LPA); + + /* Set MI2S */ + mi2s_set_codec_output_path((icodec->data->channel_mode == 2 ? + MI2S_CHAN_STEREO : MI2S_CHAN_MONO_PACKED), WT_16_BIT); + + if (icodec->data->voltage_on) + icodec->data->voltage_on(); + + /* Configure ADIE */ + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + goto error_adie; + /* OSR default to 256, can be changed for power optimization + * If OSR is to be changed, need clock API for setting the divider + */ + adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256); + /* Start AFE */ + afe_config.sample_rate = icodec->sample_rate / 1000; + afe_config.channel_mode = icodec->data->channel_mode; + afe_config.volume = AFE_VOLUME_UNITY; + trc = afe_enable(AFE_HW_PATH_CODEC_RX, &afe_config); + if (IS_ERR_VALUE(trc)) + goto error_afe; + lpa_cmd_enable_codec(drv->lpa, 1); + /* Enable ADIE */ + adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + + /* Enable power amplifier */ + if (icodec->data->pamp_on) + icodec->data->pamp_on(); + + icodec->enabled = 1; + + pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; + +error_afe: + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; +error_adie: + lpa_put(drv->lpa); +error_lpa: + clk_disable(drv->lpa_p_clk); + clk_disable(drv->lpa_codec_clk); + clk_disable(drv->lpa_core_clk); + clk_disable(drv->rx_sclk); + clk_disable(drv->rx_mclk); +error_invalid_freq: + + MM_ERR("encounter error\n"); + + pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return -ENODEV; +} + +static int snddev_icodec_open_tx(struct snddev_icodec_state *icodec) +{ + int trc; + int i, err; + struct msm_afe_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;; + + pm_qos_update_request(&drv->tx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + /* Vote for PWM mode*/ + err = pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM); + if (err != 0) + MM_ERR("pmapp_smps_mode_vote error %d\n", err); + + /* Reuse pamp_on for TX platform-specific setup */ + if (icodec->data->pamp_on) + icodec->data->pamp_on(); + + for (i = 0; i < icodec->data->pmctl_id_sz; i++) { + pmic_hsed_enable(icodec->data->pmctl_id[i], + PM_HSED_ENABLE_PWM_TCXO); + } + + /* enable MI2S TX master block */ + /* enable MI2S TX bit clock */ + trc = clk_set_rate(drv->tx_mclk, + SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) + goto error_invalid_freq; + clk_enable(drv->tx_mclk); + clk_enable(drv->tx_sclk); + + /* Set MI2S */ + mi2s_set_codec_input_path((icodec->data->channel_mode == + REAL_STEREO_CHANNEL_MODE ? MI2S_CHAN_STEREO : + (icodec->data->channel_mode == 2 ? + MI2S_CHAN_STEREO : MI2S_CHAN_MONO_RAW)), + WT_16_BIT); + /* Configure ADIE */ + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + goto error_adie; + /* Enable ADIE */ + adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256); + adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + + /* Start AFE */ + afe_config.sample_rate = icodec->sample_rate / 1000; + afe_config.channel_mode = icodec->data->channel_mode; + afe_config.volume = AFE_VOLUME_UNITY; + trc = afe_enable(AFE_HW_PATH_CODEC_TX, &afe_config); + if (IS_ERR_VALUE(trc)) + goto error_afe; + + + icodec->enabled = 1; + + pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; + +error_afe: + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; +error_adie: + clk_disable(drv->tx_sclk); + clk_disable(drv->tx_mclk); +error_invalid_freq: + + /* Disable mic bias */ + for (i = 0; i < icodec->data->pmctl_id_sz; i++) { + pmic_hsed_enable(icodec->data->pmctl_id[i], + PM_HSED_ENABLE_OFF); + } + + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + MM_ERR("encounter error\n"); + + pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return -ENODEV; +} + +static int snddev_icodec_close_lb(struct snddev_icodec_state *icodec) +{ + /* Disable power amplifier */ + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + } + if (icodec->data->voltage_off) + icodec->data->voltage_off(); + + return 0; +} + +static int snddev_icodec_close_rx(struct snddev_icodec_state *icodec) +{ + int err; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + pm_qos_update_request(&drv->rx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + /* Remove the vote for SMPS mode*/ + err = pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE); + if (err != 0) + MM_ERR("pmapp_smps_mode_vote error %d\n", err); + + /* Disable power amplifier */ + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + /* Disable ADIE */ + adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + + afe_disable(AFE_HW_PATH_CODEC_RX); + + if (icodec->data->voltage_off) + icodec->data->voltage_off(); + + /* Disable LPA Sub system */ + lpa_cmd_enable_codec(drv->lpa, 0); + lpa_put(drv->lpa); + + /* Disable LPA clocks */ + clk_disable(drv->lpa_p_clk); + clk_disable(drv->lpa_codec_clk); + clk_disable(drv->lpa_core_clk); + + /* Disable MI2S RX master block */ + /* Disable MI2S RX bit clock */ + clk_disable(drv->rx_sclk); + clk_disable(drv->rx_mclk); + + icodec->enabled = 0; + + pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; +} + +static int snddev_icodec_close_tx(struct snddev_icodec_state *icodec) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + int i, err; + + pm_qos_update_request(&drv->tx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + /* Remove the vote for SMPS mode*/ + err = pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE); + if (err != 0) + MM_ERR("pmapp_smps_mode_vote error %d\n", err); + + afe_disable(AFE_HW_PATH_CODEC_TX); + + /* Disable ADIE */ + adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + + /* Disable MI2S TX master block */ + /* Disable MI2S TX bit clock */ + clk_disable(drv->tx_sclk); + clk_disable(drv->tx_mclk); + + /* Disable mic bias */ + for (i = 0; i < icodec->data->pmctl_id_sz; i++) { + pmic_hsed_enable(icodec->data->pmctl_id[i], + PM_HSED_ENABLE_OFF); + } + + /* Reuse pamp_off for TX platform-specific setup */ + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + icodec->enabled = 0; + + pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; +} + +static int snddev_icodec_open_lb(struct snddev_icodec_state *icodec) +{ + int trc; + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + pr_err("%s: adie codec open failed\n", __func__); + else + adie_codec_setpath(icodec->adie_path, + icodec->sample_rate, 256); + + if (icodec->adie_path) + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + if (icodec->data->pamp_on) + icodec->data->pamp_on(); + + icodec->enabled = 1; + return 0; +} + +static int snddev_icodec_set_device_volume_impl( + struct msm_snddev_info *dev_info, u32 volume) +{ + struct snddev_icodec_state *icodec; + u8 afe_path_id; + + int rc = 0; + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) + afe_path_id = AFE_HW_PATH_CODEC_RX; + else + afe_path_id = AFE_HW_PATH_CODEC_TX; + + if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_DIGITAL) { + + rc = adie_codec_set_device_digital_volume(icodec->adie_path, + icodec->data->channel_mode == + REAL_STEREO_CHANNEL_MODE ? + 2 : icodec->data->channel_mode, volume); + if (rc < 0) { + MM_ERR("unable to set_device_digital_volume for" + "%s volume in percentage = %u\n", + dev_info->name, volume); + return rc; + } + + } else if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_ANALOG) { + rc = adie_codec_set_device_analog_volume(icodec->adie_path, + icodec->data->channel_mode == + REAL_STEREO_CHANNEL_MODE ? + 2 : icodec->data->channel_mode, volume); + if (rc < 0) { + MM_ERR("unable to set_device_analog_volume for" + "%s volume in percentage = %u\n", + dev_info->name, volume); + return rc; + } + } + else { + MM_ERR("Invalid device volume control\n"); + return -EPERM; + } + return rc; +} + +static int snddev_icodec_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (!drv->rx_active) { + mutex_unlock(&drv->rx_lock); + rc = -EPERM; + goto error; + } + rc = snddev_icodec_close_rx(icodec); + if (!IS_ERR_VALUE(rc)) + drv->rx_active = 0; + mutex_unlock(&drv->rx_lock); + } else if (icodec->data->capability & SNDDEV_CAP_LB) { + mutex_lock(&drv->lb_lock); + rc = snddev_icodec_close_lb(icodec); + mutex_unlock(&drv->lb_lock); + } else { + mutex_lock(&drv->tx_lock); + if (!drv->tx_active) { + mutex_unlock(&drv->tx_lock); + rc = -EPERM; + goto error; + } + rc = snddev_icodec_close_tx(icodec); + if (!IS_ERR_VALUE(rc)) + drv->tx_active = 0; + mutex_unlock(&drv->tx_lock); + } + +error: + return rc; +} + +static int snddev_icodec_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (drv->rx_active) { + mutex_unlock(&drv->rx_lock); + rc = -EBUSY; + goto error; + } + rc = snddev_icodec_open_rx(icodec); + + if (!IS_ERR_VALUE(rc)) { + drv->rx_active = 1; + if ((icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + if (IS_ERR_VALUE(rc)) { + MM_ERR("Failed to set device volume" + " impl for rx device\n"); + snddev_icodec_close(dev_info); + mutex_unlock(&drv->rx_lock); + goto error; + } + } + mutex_unlock(&drv->rx_lock); + } else if (icodec->data->capability & SNDDEV_CAP_LB) { + mutex_lock(&drv->lb_lock); + rc = snddev_icodec_open_lb(icodec); + if (!IS_ERR_VALUE(rc)) { + if ((icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, + dev_info->dev_volume); + if (rc < 0) + MM_ERR("failed to set device volume\n"); + } + mutex_unlock(&drv->lb_lock); + } else { + mutex_lock(&drv->tx_lock); + if (drv->tx_active) { + mutex_unlock(&drv->tx_lock); + rc = -EBUSY; + goto error; + } + rc = snddev_icodec_open_tx(icodec); + + if (!IS_ERR_VALUE(rc)) { + drv->tx_active = 1; + if ((icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + if (IS_ERR_VALUE(rc)) { + MM_ERR("Failed to set device volume" + " impl for tx device\n"); + snddev_icodec_close(dev_info); + mutex_unlock(&drv->tx_lock); + goto error; + } + } + mutex_unlock(&drv->tx_lock); + } +error: + return rc; +} + +static int snddev_icodec_check_freq(u32 req_freq) +{ + int rc = -EINVAL; + + if ((req_freq != 0) && (req_freq >= 8000) && (req_freq <= 48000)) { + if ((req_freq == 8000) || (req_freq == 11025) || + (req_freq == 12000) || (req_freq == 16000) || + (req_freq == 22050) || (req_freq == 24000) || + (req_freq == 32000) || (req_freq == 44100) || + (req_freq == 48000)) { + rc = 0; + } else + MM_INFO("Unsupported Frequency:%d\n", req_freq); + } + return rc; +} + +static int snddev_icodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc; + struct snddev_icodec_state *icodec; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + if (adie_codec_freq_supported(icodec->data->profile, rate) != 0) { + rc = -EINVAL; + goto error; + } else { + if (snddev_icodec_check_freq(rate) != 0) { + rc = -EINVAL; + goto error; + } else + icodec->sample_rate = rate; + } + + if (icodec->enabled) { + snddev_icodec_close(dev_info); + snddev_icodec_open(dev_info); + } + + return icodec->sample_rate; + +error: + return rc; +} + +static int snddev_icodec_enable_sidetone(struct msm_snddev_info *dev_info, + u32 enable) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (!dev_info) { + MM_ERR("invalid dev_info\n"); + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (!drv->rx_active || !dev_info->opened) { + MM_ERR("dev not active\n"); + rc = -EPERM; + mutex_unlock(&drv->rx_lock); + goto error; + } + rc = adie_codec_enable_sidetone(icodec->adie_path, enable); + mutex_unlock(&drv->rx_lock); + } else { + rc = -EINVAL; + MM_ERR("rx device only\n"); + } + +error: + return rc; + +} + +int snddev_icodec_set_device_volume(struct msm_snddev_info *dev_info, + u32 volume) +{ + struct snddev_icodec_state *icodec; + struct mutex *lock; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + int rc = -EPERM; + + if (!dev_info) { + MM_INFO("device not intilized.\n"); + return -EINVAL; + } + + icodec = dev_info->private_data; + + if (!(icodec->data->dev_vol_type & (SNDDEV_DEV_VOL_DIGITAL + | SNDDEV_DEV_VOL_ANALOG))) { + + MM_INFO("device %s does not support device volume " + "control.", dev_info->name); + return -EPERM; + } + dev_info->dev_volume = volume; + + if (icodec->data->capability & SNDDEV_CAP_RX) + lock = &drv->rx_lock; + else if (icodec->data->capability & SNDDEV_CAP_LB) + lock = &drv->lb_lock; + else + lock = &drv->tx_lock; + + mutex_lock(lock); + + rc = snddev_icodec_set_device_volume_impl(dev_info, + dev_info->dev_volume); + mutex_unlock(lock); + return rc; +} + +static int snddev_icodec_probe(struct platform_device *pdev) +{ + int rc = 0, i; + struct snddev_icodec_data *pdata; + struct msm_snddev_info *dev_info; + struct snddev_icodec_state *icodec; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller \n"); + rc = -1; + goto error; + } + pdata = pdev->dev.platform_data; + if ((pdata->capability & SNDDEV_CAP_RX) && + (pdata->capability & SNDDEV_CAP_TX)) { + MM_ERR("invalid device data either RX or TX\n"); + goto error; + } + icodec = kzalloc(sizeof(struct snddev_icodec_state), GFP_KERNEL); + if (!icodec) { + rc = -ENOMEM; + goto error; + } + dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + kfree(icodec); + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->acdb_id = pdata->acdb_id; + dev_info->private_data = (void *) icodec; + dev_info->dev_ops.open = snddev_icodec_open; + dev_info->dev_ops.close = snddev_icodec_close; + dev_info->dev_ops.set_freq = snddev_icodec_set_freq; + dev_info->dev_ops.set_device_volume = snddev_icodec_set_device_volume; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + msm_snddev_register(dev_info); + icodec->data = pdata; + icodec->sample_rate = pdata->default_sample_rate; + dev_info->sample_rate = pdata->default_sample_rate; + if (pdata->capability & SNDDEV_CAP_RX) { + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) { + dev_info->max_voc_rx_vol[i] = + pdata->max_voice_rx_vol[i]; + dev_info->min_voc_rx_vol[i] = + pdata->min_voice_rx_vol[i]; + } + /*sidetone is enabled only for the device which + property set for side tone*/ + if (pdata->property & SIDE_TONE_MASK) + dev_info->dev_ops.enable_sidetone = + snddev_icodec_enable_sidetone; + else + dev_info->dev_ops.enable_sidetone = NULL; + } else { + dev_info->dev_ops.enable_sidetone = NULL; + } + +error: + return rc; +} + +static int snddev_icodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_icodec_driver = { + .probe = snddev_icodec_probe, + .remove = snddev_icodec_remove, + .driver = { .name = "snddev_icodec" } +}; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_sdev_dent; +static struct dentry *debugfs_afelb; +static struct dentry *debugfs_adielb; +static struct adie_codec_path *debugfs_rx_adie; +static struct adie_codec_path *debugfs_tx_adie; + +static int snddev_icodec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + MM_INFO("snddev_icodec: debug intf %s\n", (char *) file->private_data); + return 0; +} + +static void debugfs_adie_loopback(u32 loop) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (loop) { + + /* enable MI2S RX master block */ + /* enable MI2S RX bit clock */ + clk_set_rate(drv->rx_mclk, + SNDDEV_ICODEC_CLK_RATE(8000)); + clk_enable(drv->rx_mclk); + clk_enable(drv->rx_sclk); + + MM_INFO("configure ADIE RX path\n"); + /* Configure ADIE */ + adie_codec_open(&debug_rx_profile, &debugfs_rx_adie); + adie_codec_setpath(debugfs_rx_adie, 8000, 256); + adie_codec_proceed_stage(debugfs_rx_adie, + ADIE_CODEC_DIGITAL_ANALOG_READY); + + MM_INFO("Enable Handset Mic bias\n"); + pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO); + /* enable MI2S TX master block */ + /* enable MI2S TX bit clock */ + clk_set_rate(drv->tx_mclk, + SNDDEV_ICODEC_CLK_RATE(8000)); + clk_enable(drv->tx_mclk); + clk_enable(drv->tx_sclk); + + MM_INFO("configure ADIE TX path\n"); + /* Configure ADIE */ + adie_codec_open(&debug_tx_lb_profile, &debugfs_tx_adie); + adie_codec_setpath(debugfs_tx_adie, 8000, 256); + adie_codec_proceed_stage(debugfs_tx_adie, + ADIE_CODEC_DIGITAL_ANALOG_READY); + } else { + /* Disable ADIE */ + adie_codec_proceed_stage(debugfs_rx_adie, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(debugfs_rx_adie); + adie_codec_proceed_stage(debugfs_tx_adie, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(debugfs_tx_adie); + + pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_OFF); + + /* Disable MI2S RX master block */ + /* Disable MI2S RX bit clock */ + clk_disable(drv->rx_sclk); + clk_disable(drv->rx_mclk); + + /* Disable MI2S TX master block */ + /* Disable MI2S TX bit clock */ + clk_disable(drv->tx_sclk); + clk_disable(drv->tx_mclk); + } +} + +static void debugfs_afe_loopback(u32 loop) +{ + int trc; + struct msm_afe_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + struct lpa_codec_config lpa_config; + + if (loop) { + /* Vote for SMPS mode*/ + pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM); + + /* enable MI2S RX master block */ + /* enable MI2S RX bit clock */ + trc = clk_set_rate(drv->rx_mclk, + SNDDEV_ICODEC_CLK_RATE(8000)); + if (IS_ERR_VALUE(trc)) + MM_ERR("failed to set clk rate\n"); + clk_enable(drv->rx_mclk); + clk_enable(drv->rx_sclk); + clk_enable(drv->lpa_p_clk); + clk_enable(drv->lpa_codec_clk); + clk_enable(drv->lpa_core_clk); + /* Enable LPA sub system + */ + drv->lpa = lpa_get(); + if (!drv->lpa) + MM_ERR("failed to enable lpa\n"); + lpa_config.sample_rate = 8000; + lpa_config.sample_width = 16; + lpa_config.output_interface = LPA_OUTPUT_INTF_WB_CODEC; + lpa_config.num_channels = 1; + lpa_cmd_codec_config(drv->lpa, &lpa_config); + /* Set audio interconnect reg to LPA */ + audio_interct_codec(AUDIO_INTERCT_LPA); + mi2s_set_codec_output_path(MI2S_CHAN_MONO_PACKED, WT_16_BIT); + MM_INFO("configure ADIE RX path\n"); + /* Configure ADIE */ + adie_codec_open(&debug_rx_profile, &debugfs_rx_adie); + adie_codec_setpath(debugfs_rx_adie, 8000, 256); + lpa_cmd_enable_codec(drv->lpa, 1); + + /* Start AFE for RX */ + afe_config.sample_rate = 0x8; + afe_config.channel_mode = 1; + afe_config.volume = AFE_VOLUME_UNITY; + MM_INFO("enable afe\n"); + trc = afe_enable(AFE_HW_PATH_CODEC_RX, &afe_config); + if (IS_ERR_VALUE(trc)) + MM_ERR("fail to enable afe RX\n"); + adie_codec_proceed_stage(debugfs_rx_adie, + ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(debugfs_rx_adie, + ADIE_CODEC_DIGITAL_ANALOG_READY); + + /* Vote for PWM mode*/ + pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM); + + MM_INFO("Enable Handset Mic bias\n"); + pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO); + + /* enable MI2S TX master block */ + /* enable MI2S TX bit clock */ + clk_set_rate(drv->tx_mclk, + SNDDEV_ICODEC_CLK_RATE(8000)); + clk_enable(drv->tx_mclk); + clk_enable(drv->tx_sclk); + /* Set MI2S */ + mi2s_set_codec_input_path(MI2S_CHAN_MONO_PACKED, WT_16_BIT); + MM_INFO("configure ADIE TX path\n"); + /* Configure ADIE */ + adie_codec_open(&debug_tx_profile, &debugfs_tx_adie); + adie_codec_setpath(debugfs_tx_adie, 8000, 256); + adie_codec_proceed_stage(debugfs_tx_adie, + ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(debugfs_tx_adie, + ADIE_CODEC_DIGITAL_ANALOG_READY); + /* Start AFE for TX */ + afe_config.sample_rate = 0x8; + afe_config.channel_mode = 1; + afe_config.volume = AFE_VOLUME_UNITY; + trc = afe_enable(AFE_HW_PATH_CODEC_TX, &afe_config); + if (IS_ERR_VALUE(trc)) + MM_ERR("failed to enable AFE TX\n"); + /* Set the volume level to non unity, to avoid + loopback effect */ + afe_device_volume_ctrl(AFE_HW_PATH_CODEC_RX, 0x0500); + + /* enable afe loopback */ + afe_loopback(1); + MM_INFO("AFE loopback enabled\n"); + } else { + /* disable afe loopback */ + afe_loopback(0); + /* Remove the vote for SMPS mode*/ + pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE); + + /* Disable ADIE */ + adie_codec_proceed_stage(debugfs_rx_adie, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(debugfs_rx_adie); + /* Disable AFE for RX */ + afe_disable(AFE_HW_PATH_CODEC_RX); + + /* Disable LPA Sub system */ + lpa_cmd_enable_codec(drv->lpa, 0); + lpa_put(drv->lpa); + + /* Disable LPA clocks */ + clk_disable(drv->lpa_p_clk); + clk_disable(drv->lpa_codec_clk); + clk_disable(drv->lpa_core_clk); + + /* Disable MI2S RX master block */ + /* Disable MI2S RX bit clock */ + clk_disable(drv->rx_sclk); + clk_disable(drv->rx_mclk); + + pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID, + PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE); + + /* Disable AFE for TX */ + afe_disable(AFE_HW_PATH_CODEC_TX); + + /* Disable ADIE */ + adie_codec_proceed_stage(debugfs_tx_adie, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(debugfs_tx_adie); + /* Disable MI2S TX master block */ + /* Disable MI2S TX bit clock */ + clk_disable(drv->tx_sclk); + clk_disable(drv->tx_mclk); + pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_OFF); + MM_INFO("AFE loopback disabled\n"); + } +} + +static ssize_t snddev_icodec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char cmd; + + if (get_user(cmd, ubuf)) + return -EFAULT; + + MM_INFO("%s %c\n", lb_str, cmd); + + if (!strcmp(lb_str, "adie_loopback")) { + switch (cmd) { + case '1': + debugfs_adie_loopback(1); + break; + case '0': + debugfs_adie_loopback(0); + break; + } + } else if (!strcmp(lb_str, "afe_loopback")) { + switch (cmd) { + case '1': + debugfs_afe_loopback(1); + break; + case '0': + debugfs_afe_loopback(0); + break; + } + } + + return cnt; +} + +static const struct file_operations snddev_icodec_debug_fops = { + .open = snddev_icodec_debug_open, + .write = snddev_icodec_debug_write +}; +#endif + +static int __init snddev_icodec_init(void) +{ + s32 rc; + struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; + + rc = platform_driver_register(&snddev_icodec_driver); + if (IS_ERR_VALUE(rc)) + goto error_platform_driver; + icodec_drv->rx_mclk = clk_get(NULL, "mi2s_codec_rx_m_clk"); + if (IS_ERR(icodec_drv->rx_mclk)) + goto error_rx_mclk; + icodec_drv->rx_sclk = clk_get(NULL, "mi2s_codec_rx_s_clk"); + if (IS_ERR(icodec_drv->rx_sclk)) + goto error_rx_sclk; + icodec_drv->tx_mclk = clk_get(NULL, "mi2s_codec_tx_m_clk"); + if (IS_ERR(icodec_drv->tx_mclk)) + goto error_tx_mclk; + icodec_drv->tx_sclk = clk_get(NULL, "mi2s_codec_tx_s_clk"); + if (IS_ERR(icodec_drv->tx_sclk)) + goto error_tx_sclk; + icodec_drv->lpa_codec_clk = clk_get(NULL, "lpa_codec_clk"); + if (IS_ERR(icodec_drv->lpa_codec_clk)) + goto error_lpa_codec_clk; + icodec_drv->lpa_core_clk = clk_get(NULL, "lpa_core_clk"); + if (IS_ERR(icodec_drv->lpa_core_clk)) + goto error_lpa_core_clk; + icodec_drv->lpa_p_clk = clk_get(NULL, "lpa_pclk"); + if (IS_ERR(icodec_drv->lpa_p_clk)) + goto error_lpa_p_clk; + +#ifdef CONFIG_DEBUG_FS + debugfs_sdev_dent = debugfs_create_dir("snddev_icodec", 0); + if (debugfs_sdev_dent) { + debugfs_afelb = debugfs_create_file("afe_loopback", + S_IFREG | S_IWUGO, debugfs_sdev_dent, + (void *) "afe_loopback", &snddev_icodec_debug_fops); + debugfs_adielb = debugfs_create_file("adie_loopback", + S_IFREG | S_IWUGO, debugfs_sdev_dent, + (void *) "adie_loopback", &snddev_icodec_debug_fops); + } +#endif + mutex_init(&icodec_drv->rx_lock); + mutex_init(&icodec_drv->lb_lock); + mutex_init(&icodec_drv->tx_lock); + icodec_drv->rx_active = 0; + icodec_drv->tx_active = 0; + icodec_drv->lpa = NULL; + pm_qos_add_request(&icodec_drv->tx_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + pm_qos_add_request(&icodec_drv->rx_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + return 0; + +error_lpa_p_clk: + clk_put(icodec_drv->lpa_core_clk); +error_lpa_core_clk: + clk_put(icodec_drv->lpa_codec_clk); +error_lpa_codec_clk: + clk_put(icodec_drv->tx_sclk); +error_tx_sclk: + clk_put(icodec_drv->tx_mclk); +error_tx_mclk: + clk_put(icodec_drv->rx_sclk); +error_rx_sclk: + clk_put(icodec_drv->rx_mclk); +error_rx_mclk: + platform_driver_unregister(&snddev_icodec_driver); +error_platform_driver: + + MM_ERR("encounter error\n"); + return -ENODEV; +} + +static void __exit snddev_icodec_exit(void) +{ + struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; + +#ifdef CONFIG_DEBUG_FS + if (debugfs_afelb) + debugfs_remove(debugfs_afelb); + if (debugfs_adielb) + debugfs_remove(debugfs_adielb); + if (debugfs_sdev_dent) + debugfs_remove(debugfs_sdev_dent); +#endif + platform_driver_unregister(&snddev_icodec_driver); + + clk_put(icodec_drv->rx_sclk); + clk_put(icodec_drv->rx_mclk); + clk_put(icodec_drv->tx_sclk); + clk_put(icodec_drv->tx_mclk); + return; +} + +module_init(snddev_icodec_init); +module_exit(snddev_icodec_exit); + +MODULE_DESCRIPTION("ICodec Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c b/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c new file mode 100644 index 00000000000..939cc8b3848 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c @@ -0,0 +1,405 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global state for the driver */ +struct snddev_mi2s_drv_state { + struct clk *mclk; + struct clk *sclk; + struct mutex lock; + u8 sd_lines_used; + u8 clocks_enabled; +}; + +static struct snddev_mi2s_drv_state snddev_mi2s_drv; + +static int snddev_mi2s_open_tx(struct msm_snddev_info *dev_info) +{ + u8 channels; + struct msm_afe_config afe_config; + int rc; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + MM_DBG("%s: channel_mode = %u sd_line_mask = 0x%x " + "default_sample_rate = %u\n", __func__, + snddev_mi2s_data->channel_mode, snddev_mi2s_data->sd_lines, + snddev_mi2s_data->default_sample_rate); + + if (snddev_mi2s_data->channel_mode == 2) { + channels = MI2S_CHAN_STEREO; + } else { + MM_ERR("%s: Invalid number of channels = %u\n", __func__, + snddev_mi2s_data->channel_mode); + return -EINVAL; + } + + /* Set MI2S */ + mi2s_set_hdmi_input_path(channels, WT_16_BIT, + snddev_mi2s_data->sd_lines); + + afe_config.sample_rate = snddev_mi2s_data->default_sample_rate / 1000; + afe_config.channel_mode = snddev_mi2s_data->channel_mode; + afe_config.volume = AFE_VOLUME_UNITY; + rc = afe_enable(AFE_HW_PATH_MI2S_TX, &afe_config); + + if (IS_ERR_VALUE(rc)) { + MM_ERR("%s: afe_enable failed for AFE_HW_PATH_MI2S_TX " + "rc = %d\n", __func__, rc); + return -ENODEV; + } + + /* Enable audio path */ + if (snddev_mi2s_data->route) + snddev_mi2s_data->route(); + + return 0; +} + +static int snddev_mi2s_open_rx(struct msm_snddev_info *dev_info) +{ + int rc; + struct msm_afe_config afe_config; + u8 channels; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + MM_DBG("%s: channel_mode = %u sd_line_mask = 0x%x " + "default_sample_rate = %u\n", __func__, + snddev_mi2s_data->channel_mode, snddev_mi2s_data->sd_lines, + snddev_mi2s_data->default_sample_rate); + + if (snddev_mi2s_data->channel_mode == 2) + channels = MI2S_CHAN_STEREO; + else if (snddev_mi2s_data->channel_mode == 4) + channels = MI2S_CHAN_4CHANNELS; + else if (snddev_mi2s_data->channel_mode == 6) + channels = MI2S_CHAN_6CHANNELS; + else if (snddev_mi2s_data->channel_mode == 8) + channels = MI2S_CHAN_8CHANNELS; + else + channels = MI2S_CHAN_MONO_RAW; + + /* Set MI2S */ + mi2s_set_hdmi_output_path(channels, WT_16_BIT, + snddev_mi2s_data->sd_lines); + + /* Start AFE */ + afe_config.sample_rate = snddev_mi2s_data->default_sample_rate / 1000; + afe_config.channel_mode = snddev_mi2s_data->channel_mode; + afe_config.volume = AFE_VOLUME_UNITY; + rc = afe_enable(AFE_HW_PATH_MI2S_RX, &afe_config); + + if (IS_ERR_VALUE(rc)) { + MM_ERR("%s: encounter error\n", __func__); + return -ENODEV; + } + + /* Enable audio path */ + if (snddev_mi2s_data->route) + snddev_mi2s_data->route(); + + MM_DBG("%s: enabled %s \n", __func__, snddev_mi2s_data->name); + + return 0; +} + +static int snddev_mi2s_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv; + u32 dir; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + if (!dev_info) { + MM_ERR("%s: msm_snddev_info is null \n", __func__); + return -EINVAL; + } + + mutex_lock(&drv->lock); + + if (drv->sd_lines_used & snddev_mi2s_data->sd_lines) { + MM_ERR("%s: conflict in SD data line. can not use the device\n", + __func__); + mutex_unlock(&drv->lock); + return -EBUSY; + } + + if (!drv->clocks_enabled) { + + rc = mi2s_config_clk_gpio(); + if (rc) { + MM_ERR("%s: mi2s GPIO config failed for %s\n", + __func__, snddev_mi2s_data->name); + mutex_unlock(&drv->lock); + return -EIO; + } + clk_enable(drv->mclk); + clk_enable(drv->sclk); + drv->clocks_enabled = 1; + MM_DBG("%s: clks enabled \n", __func__); + } else + MM_DBG("%s: clks already enabled \n", __func__); + + if (snddev_mi2s_data->capability & SNDDEV_CAP_RX) { + + dir = DIR_RX; + rc = mi2s_config_data_gpio(dir, snddev_mi2s_data->sd_lines); + + if (rc) { + rc = -EIO; + MM_ERR("%s: mi2s GPIO config failed for %s\n", + __func__, snddev_mi2s_data->name); + goto mi2s_data_gpio_failure; + } + + MM_DBG("%s: done gpio config rx SD lines\n", __func__); + + rc = snddev_mi2s_open_rx(dev_info); + + if (IS_ERR_VALUE(rc)) { + MM_ERR(" snddev_mi2s_open_rx failed \n"); + goto mi2s_cleanup_open; + } + + drv->sd_lines_used |= snddev_mi2s_data->sd_lines; + + MM_DBG("%s: sd_lines_used = 0x%x\n", __func__, + drv->sd_lines_used); + mutex_unlock(&drv->lock); + + } else { + dir = DIR_TX; + rc = mi2s_config_data_gpio(dir, snddev_mi2s_data->sd_lines); + + if (rc) { + rc = -EIO; + MM_ERR("%s: mi2s GPIO config failed for %s\n", + __func__, snddev_mi2s_data->name); + goto mi2s_data_gpio_failure; + } + MM_DBG("%s: done data line gpio config for %s\n", + __func__, snddev_mi2s_data->name); + + rc = snddev_mi2s_open_tx(dev_info); + + if (IS_ERR_VALUE(rc)) { + MM_ERR(" snddev_mi2s_open_tx failed \n"); + goto mi2s_cleanup_open; + } + + drv->sd_lines_used |= snddev_mi2s_data->sd_lines; + MM_DBG("%s: sd_lines_used = 0x%x\n", __func__, + drv->sd_lines_used); + mutex_unlock(&drv->lock); + } + + return 0; + +mi2s_cleanup_open: + mi2s_unconfig_data_gpio(dir, snddev_mi2s_data->sd_lines); + + /* Disable audio path */ + if (snddev_mi2s_data->deroute) + snddev_mi2s_data->deroute(); + +mi2s_data_gpio_failure: + if (!drv->sd_lines_used) { + clk_disable(drv->sclk); + clk_disable(drv->mclk); + drv->clocks_enabled = 0; + mi2s_unconfig_clk_gpio(); + } + mutex_unlock(&drv->lock); + return rc; +} + +static int snddev_mi2s_close(struct msm_snddev_info *dev_info) +{ + struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv; + int dir; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + if (!dev_info) { + MM_ERR("%s: msm_snddev_info is null \n", __func__); + return -EINVAL; + } + + if (!dev_info->opened) { + MM_ERR(" %s: calling close device with out opening the" + " device \n", __func__); + return -EIO; + } + + mutex_lock(&drv->lock); + + drv->sd_lines_used &= ~snddev_mi2s_data->sd_lines; + + MM_DBG("%s: sd_lines in use = 0x%x\n", __func__, drv->sd_lines_used); + + if (snddev_mi2s_data->capability & SNDDEV_CAP_RX) { + dir = DIR_RX; + afe_disable(AFE_HW_PATH_MI2S_RX); + } else { + dir = DIR_TX; + afe_disable(AFE_HW_PATH_MI2S_TX); + } + + mi2s_unconfig_data_gpio(dir, snddev_mi2s_data->sd_lines); + + if (!drv->sd_lines_used) { + clk_disable(drv->sclk); + clk_disable(drv->mclk); + drv->clocks_enabled = 0; + mi2s_unconfig_clk_gpio(); + } + + /* Disable audio path */ + if (snddev_mi2s_data->deroute) + snddev_mi2s_data->deroute(); + + mutex_unlock(&drv->lock); + + return 0; +} + +static int snddev_mi2s_set_freq(struct msm_snddev_info *dev_info, u32 req_freq) +{ + if (req_freq != 48000) { + MM_DBG("%s: Unsupported Frequency:%d\n", __func__, req_freq); + return -EINVAL; + } + return 48000; +} + +static int snddev_mi2s_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_mi2s_data *pdata; + struct msm_snddev_info *dev_info; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller \n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if ((pdata->capability & SNDDEV_CAP_RX) && + (pdata->capability & SNDDEV_CAP_TX)) { + MM_ERR("%s: invalid device data either RX or TX\n", __func__); + return -ENODEV; + } + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + MM_ERR("%s: uneable to allocate memeory for msm_snddev_info \n", + __func__); + + return -ENOMEM; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->acdb_id = pdata->acdb_id; + dev_info->private_data = (void *)pdata; + dev_info->dev_ops.open = snddev_mi2s_open; + dev_info->dev_ops.close = snddev_mi2s_close; + dev_info->dev_ops.set_freq = snddev_mi2s_set_freq; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + msm_snddev_register(dev_info); + dev_info->sample_rate = pdata->default_sample_rate; + + MM_DBG("%s: probe done for %s\n", __func__, pdata->name); + return rc; +} + +static int snddev_mi2s_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_mi2s_driver = { + .probe = snddev_mi2s_probe, + .remove = snddev_mi2s_remove, + .driver = {.name = "snddev_mi2s"} +}; + +static int __init snddev_mi2s_init(void) +{ + s32 rc; + struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv; + + rc = platform_driver_register(&snddev_mi2s_driver); + if (IS_ERR_VALUE(rc)) { + + MM_ERR("%s: platform_driver_register failed \n", __func__); + goto error_platform_driver; + } + + drv->mclk = clk_get(NULL, "mi2s_m_clk"); + if (IS_ERR(drv->mclk)) { + MM_ERR("%s: clk_get mi2s_mclk failed \n", __func__); + goto error_mclk; + } + + drv->sclk = clk_get(NULL, "mi2s_s_clk"); + if (IS_ERR(drv->sclk)) { + MM_ERR("%s: clk_get mi2s_sclk failed \n", __func__); + + goto error_sclk; + } + + mutex_init(&drv->lock); + + MM_DBG("snddev_mi2s_init : done \n"); + + return 0; + +error_sclk: + clk_put(drv->mclk); +error_mclk: + platform_driver_unregister(&snddev_mi2s_driver); +error_platform_driver: + + MM_ERR("%s: encounter error\n", __func__); + return -ENODEV; +} + +static void __exit snddev_mi2s_exit(void) +{ + struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv; + + platform_driver_unregister(&snddev_mi2s_driver); + + clk_put(drv->sclk); + clk_put(drv->mclk); + return; +} + +module_init(snddev_mi2s_init); +module_exit(snddev_mi2s_exit); + +MODULE_DESCRIPTION("mi2s Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c b/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c new file mode 100644 index 00000000000..cd93345d601 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c @@ -0,0 +1,122 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int snddev_virtual_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + + if (!dev_info) + rc = -EINVAL; + return rc; +} + +static int snddev_virtual_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + + if (!dev_info) + rc = -EINVAL; + return rc; +} + +static int snddev_virtual_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc = 0; + + if (!dev_info) + rc = -EINVAL; + return rate; +} + +static int snddev_virtual_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_virtual_data *pdata; + struct msm_snddev_info *dev_info; + + if (!pdev || !pdev->dev.platform_data) { + MM_ERR("Invalid caller\n"); + rc = -EPERM; + goto error; + } + pdata = pdev->dev.platform_data; + + dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->acdb_id = pdata->acdb_id; + dev_info->private_data = (void *) NULL; + dev_info->dev_ops.open = snddev_virtual_open; + dev_info->dev_ops.close = snddev_virtual_close; + dev_info->dev_ops.set_freq = snddev_virtual_set_freq; + dev_info->capability = pdata->capability; + dev_info->sample_rate = 8000; + dev_info->opened = 0; + dev_info->sessions = 0; + + msm_snddev_register(dev_info); + +error: + return rc; +} + +static int snddev_virtual_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_virtual_driver = { + .probe = snddev_virtual_probe, + .remove = snddev_virtual_remove, + .driver = { .name = "snddev_virtual" } +}; + +static int __init snddev_virtual_init(void) +{ + int rc = 0; + + MM_DBG(" snddev_virtual_init \n"); + rc = platform_driver_register(&snddev_virtual_driver); + if (IS_ERR_VALUE(rc)) { + MM_ERR("platform driver register failure\n"); + return -ENODEV; + } + return 0; +} + +static void __exit snddev_virtual_exit(void) +{ + platform_driver_unregister(&snddev_virtual_driver); + + return; +} + +module_init(snddev_virtual_init); +module_exit(snddev_virtual_exit); + +MODULE_DESCRIPTION("Virtual Sound Device driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h b/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h new file mode 100644 index 00000000000..d9003cdd75e --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h @@ -0,0 +1,623 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MACH_QDSP5_V2_TIMPANI_PROFILE_H__ +#define __MACH_QDSP5_V2_MTIMPANI_PROFILE_H__ + +/* + * TX Device Profiles + */ + +/* Analog MIC */ +/* AMIC Primary mono */ +#define AMIC_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* Headset MIC */ +#define AMIC1_HEADSET_TX_MONO_PRIMARY_OSR256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)} } + +/* + * RX Device Profiles + */ + +/* RX EAR */ +#define EAR_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX SPEAKER */ +#define SPEAKER_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }; + +/* + * RX HPH PRIMARY + */ + +/* RX HPH CLASS AB CAPLESS */ + +#define HEADSET_AB_CPLS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* AMIC dual */ +#define AMIC_DUAL_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB4, 0xFF, 0xCE)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0xFF, 0x5A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY RX */ +#define TTY_HEADSET_MONO_RX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY TX */ +#define TTY_HEADSET_MONO_TX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_RX_CAPLESS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HS_DMIC2_STEREO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_LEG_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF9)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_D_LEG_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x60)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xE1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2D, 0xFF, 0x6F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2E, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xF7, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xF7, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4A, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_AUXPGA_HPH_AB_CPLS_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_AUXPGA_LO_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/voice.c b/arch/arm/mach-msm/qdsp5v2/voice.c new file mode 100644 index 00000000000..026acb33aa4 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/voice.c @@ -0,0 +1,752 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct voice_data { + void *handle; /* DALRPC handle */ + void *cb_handle; /* DALRPC callback handle */ + int network; /* Network information */ + int dev_state;/*READY, CHANGE, REL_DONE,INIT*/ + int voc_state;/*INIT, CHANGE, RELEASE, ACQUIRE */ + struct mutex voc_lock; + struct mutex vol_lock; + int voc_event; + int dev_event; + atomic_t rel_start_flag; + atomic_t acq_start_flag; + atomic_t chg_start_flag; + struct task_struct *task; + struct completion complete; + wait_queue_head_t dev_wait; + wait_queue_head_t voc_wait; + uint32_t device_events; + /* cache the values related to Rx and Tx */ + struct device_data dev_rx; + struct device_data dev_tx; + /* these default values are for all devices */ + uint32_t default_mute_val; + uint32_t default_vol_val; + uint32_t default_sample_val; + /* call status */ + int v_call_status; /* Start or End */ + s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB, [1] for WB */ + s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM]; +}; + +static struct voice_data voice; + +static int voice_cmd_device_info(struct voice_data *); +static int voice_cmd_acquire_done(struct voice_data *); +static void voice_auddev_cb_function(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data); + +static int voice_cmd_change(void) +{ + + struct voice_header hdr; + struct voice_data *v = &voice; + int err; + + hdr.id = CMD_DEVICE_CHANGE; + hdr.data_len = 0; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr, + sizeof(struct voice_header)); + + if (err) + MM_ERR("Voice change command failed\n"); + return err; +} + +static void voice_auddev_cb_function(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data) +{ + struct voice_data *v = &voice; + int rc = 0, i; + + MM_INFO("auddev_cb_function, evt_id=%d, dev_state=%d, voc_state=%d\n", + evt_id, v->dev_state, v->voc_state); + if ((evt_id != AUDDEV_EVT_START_VOICE) || + (evt_id != AUDDEV_EVT_END_VOICE)) { + if (evt_payload == NULL) { + MM_ERR(" evt_payload is NULL pointer\n"); + return; + } + } + switch (evt_id) { + case AUDDEV_EVT_START_VOICE: + if ((v->dev_state == DEV_INIT) || + (v->dev_state == DEV_REL_DONE)) { + v->v_call_status = VOICE_CALL_START; + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) + && (v->dev_tx.enabled == VOICE_DEV_ENABLED)) { + v->dev_state = DEV_READY; + MM_DBG("dev_state into ready\n"); + wake_up(&v->dev_wait); + } + if (v->voc_state == VOICE_CHANGE) { + MM_DBG("voc_state is in VOICE_CHANGE\n"); + v->voc_state = VOICE_ACQUIRE; + } + } + break; + case AUDDEV_EVT_DEV_CHG_VOICE: + if (v->dev_state == DEV_READY) { + v->dev_rx.enabled = VOICE_DEV_DISABLED; + v->dev_tx.enabled = VOICE_DEV_DISABLED; + v->dev_state = DEV_CHANGE; + mutex_lock(&voice.voc_lock); + if (v->voc_state == VOICE_ACQUIRE) { + /* send device change to modem */ + voice_cmd_change(); + mutex_unlock(&voice.voc_lock); + msm_snddev_enable_sidetone(v->dev_rx.dev_id, + 0); + /* block to wait for CHANGE_START */ + rc = wait_event_interruptible( + v->voc_wait, (v->voc_state == VOICE_CHANGE) + || (atomic_read(&v->chg_start_flag) == 1) + || (atomic_read(&v->rel_start_flag) == 1)); + } else { + mutex_unlock(&voice.voc_lock); + MM_ERR(" Voice is not at ACQUIRE state\n"); + } + } else if ((v->dev_state == DEV_INIT) || + (v->dev_state == DEV_REL_DONE)) { + v->dev_rx.enabled = VOICE_DEV_DISABLED; + v->dev_tx.enabled = VOICE_DEV_DISABLED; + } else + MM_ERR(" device is not at proper state\n"); + break; + case AUDDEV_EVT_DEV_RDY: + /* update the dev info */ + if (evt_payload->voc_devinfo.dev_type == DIR_RX) { + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) { + v->max_rx_vol[i] = + evt_payload->voc_devinfo.max_rx_vol[i]; + v->min_rx_vol[i] = + evt_payload->voc_devinfo.min_rx_vol[i]; + } + } + if (v->dev_state == DEV_CHANGE) { + if (evt_payload->voc_devinfo.dev_type == DIR_RX) { + v->dev_rx.dev_acdb_id = + evt_payload->voc_devinfo.acdb_dev_id; + v->dev_rx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_rx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_rx.enabled = VOICE_DEV_ENABLED; + } else { + v->dev_tx.dev_acdb_id = + evt_payload->voc_devinfo.acdb_dev_id; + v->dev_tx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_tx.enabled = VOICE_DEV_ENABLED; + v->dev_tx.dev_id = + evt_payload->voc_devinfo.dev_id; + } + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) && + (v->dev_tx.enabled == VOICE_DEV_ENABLED)) { + v->dev_state = DEV_READY; + MM_DBG("dev state into ready\n"); + voice_cmd_device_info(v); + wake_up(&v->dev_wait); + mutex_lock(&voice.voc_lock); + if (v->voc_state == VOICE_CHANGE) { + v->dev_event = DEV_CHANGE_READY; + complete(&v->complete); + } + mutex_unlock(&voice.voc_lock); + } + } else if ((v->dev_state == DEV_INIT) || + (v->dev_state == DEV_REL_DONE)) { + if (evt_payload->voc_devinfo.dev_type == DIR_RX) { + v->dev_rx.dev_acdb_id = + evt_payload->voc_devinfo.acdb_dev_id; + v->dev_rx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_rx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_rx.enabled = VOICE_DEV_ENABLED; + } else { + v->dev_tx.dev_acdb_id = + evt_payload->voc_devinfo.acdb_dev_id; + v->dev_tx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_tx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_tx.enabled = VOICE_DEV_ENABLED; + } + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) && + (v->dev_tx.enabled == VOICE_DEV_ENABLED) && + (v->v_call_status == VOICE_CALL_START)) { + v->dev_state = DEV_READY; + MM_DBG("dev state into ready\n"); + voice_cmd_device_info(v); + wake_up(&v->dev_wait); + mutex_lock(&voice.voc_lock); + if (v->voc_state == VOICE_CHANGE) { + v->dev_event = DEV_CHANGE_READY; + complete(&v->complete); + } + mutex_unlock(&voice.voc_lock); + } + } else + MM_ERR("Receive READY not at the proper state =%d\n", + v->dev_state); + break; + case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: + if (evt_payload->voc_devinfo.dev_type == DIR_TX) + v->dev_tx.mute = + evt_payload->voc_vm_info.dev_vm_val.mute; + else + v->dev_rx.volume = evt_payload-> + voc_vm_info.dev_vm_val.vol; + /* send device info */ + voice_cmd_device_info(v); + break; + case AUDDEV_EVT_REL_PENDING: + /* recover the tx mute and rx volume to the default values */ + if (v->dev_state == DEV_READY) { + if (atomic_read(&v->rel_start_flag)) { + atomic_dec(&v->rel_start_flag); + if (evt_payload->voc_devinfo.dev_type == DIR_RX) + v->dev_rx.enabled = VOICE_DEV_DISABLED; + else + v->dev_tx.enabled = VOICE_DEV_DISABLED; + v->dev_state = DEV_REL_DONE; + wake_up(&v->dev_wait); + break; + } + mutex_lock(&voice.voc_lock); + if ((v->voc_state == VOICE_RELEASE) || + (v->voc_state == VOICE_INIT)) { + if (evt_payload->voc_devinfo.dev_type + == DIR_RX) { + v->dev_rx.enabled = VOICE_DEV_DISABLED; + } else { + v->dev_tx.enabled = VOICE_DEV_DISABLED; + } + v->dev_state = DEV_REL_DONE; + mutex_unlock(&voice.voc_lock); + wake_up(&v->dev_wait); + } else { + /* send device change to modem */ + voice_cmd_change(); + mutex_unlock(&voice.voc_lock); + rc = wait_event_interruptible( + v->voc_wait, (v->voc_state == VOICE_CHANGE) + || (atomic_read(&v->chg_start_flag) == 1) + || (atomic_read(&v->rel_start_flag) == 1)); + if (atomic_read(&v->rel_start_flag) == 1) + atomic_dec(&v->rel_start_flag); + /* clear Rx/Tx to Disable */ + if (evt_payload->voc_devinfo.dev_type == DIR_RX) + v->dev_rx.enabled = VOICE_DEV_DISABLED; + else + v->dev_tx.enabled = VOICE_DEV_DISABLED; + v->dev_state = DEV_REL_DONE; + wake_up(&v->dev_wait); + } + } else if ((v->dev_state == DEV_INIT) || + (v->dev_state == DEV_REL_DONE)) { + if (evt_payload->voc_devinfo.dev_type == DIR_RX) + v->dev_rx.enabled = VOICE_DEV_DISABLED; + else + v->dev_tx.enabled = VOICE_DEV_DISABLED; + } + break; + case AUDDEV_EVT_END_VOICE: + /* recover the tx mute and rx volume to the default values */ + v->dev_tx.mute = v->default_mute_val; + v->dev_rx.volume = v->default_vol_val; + + if (v->dev_rx.enabled == VOICE_DEV_ENABLED) + msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0); + + if ((v->dev_state == DEV_READY) || + (v->dev_state == DEV_CHANGE)) { + if (atomic_read(&v->rel_start_flag)) { + atomic_dec(&v->rel_start_flag); + v->v_call_status = VOICE_CALL_END; + v->dev_state = DEV_REL_DONE; + wake_up(&v->dev_wait); + break; + } + mutex_lock(&voice.voc_lock); + if ((v->voc_state == VOICE_RELEASE) || + (v->voc_state == VOICE_INIT)) { + v->v_call_status = VOICE_CALL_END; + v->dev_state = DEV_REL_DONE; + mutex_unlock(&voice.voc_lock); + wake_up(&v->dev_wait); + } else { + /* send mute and default volume value to MCAD */ + voice_cmd_device_info(v); + /* send device change to modem */ + voice_cmd_change(); + mutex_unlock(&voice.voc_lock); + /* block to wait for RELEASE_START + or CHANGE_START */ + rc = wait_event_interruptible( + v->voc_wait, (v->voc_state == VOICE_CHANGE) + || (atomic_read(&v->chg_start_flag) == 1) + || (atomic_read(&v->rel_start_flag) == 1)); + if (atomic_read(&v->rel_start_flag) == 1) + atomic_dec(&v->rel_start_flag); + /* set voice call to END state */ + v->v_call_status = VOICE_CALL_END; + v->dev_state = DEV_REL_DONE; + wake_up(&v->dev_wait); + } + } else + v->v_call_status = VOICE_CALL_END; + break; + case AUDDEV_EVT_FREQ_CHG: + MM_DBG("Voice Driver got sample rate change Event\n"); + MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate); + MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type); + MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id); + if (v->dev_state == DEV_READY) { + v->dev_tx.enabled = VOICE_DEV_DISABLED; + v->dev_state = DEV_CHANGE; + mutex_lock(&voice.voc_lock); + if (v->voc_state == VOICE_ACQUIRE) { + msm_snddev_enable_sidetone(v->dev_rx.dev_id, + 0); + /* send device change to modem */ + voice_cmd_change(); + mutex_unlock(&voice.voc_lock); + /* block to wait for CHANGE_START */ + rc = wait_event_interruptible( + v->voc_wait, (v->voc_state == VOICE_CHANGE) + || (atomic_read(&v->chg_start_flag) == 1) + || (atomic_read(&v->rel_start_flag) == 1)); + } else { + mutex_unlock(&voice.voc_lock); + MM_ERR(" Voice is not at ACQUIRE state\n"); + } + } else if ((v->dev_state == DEV_INIT) || + (v->dev_state == DEV_REL_DONE)) { + v->dev_tx.enabled = VOICE_DEV_DISABLED; + } else + MM_ERR("Event not at the proper state =%d\n", + v->dev_state); + break; + default: + MM_ERR("UNKNOWN EVENT\n"); + } + return; +} +EXPORT_SYMBOL(voice_auddev_cb_function); + +static void remote_cb_function(void *context, u32 param, + void *evt_buf, u32 len) +{ + struct voice_header *hdr; + struct voice_data *v = context; + + hdr = (struct voice_header *)evt_buf; + + MM_INFO("len=%d id=%d\n", len, hdr->id); + + if (len <= 0) { + MM_ERR("unexpected event with length %d \n", len); + return; + } + + switch (hdr->id) { + case EVENT_ACQUIRE_START: + atomic_inc(&v->acq_start_flag); + wake_up(&v->dev_wait); + v->voc_event = VOICE_ACQUIRE_START; + v->network = ((struct voice_network *)evt_buf)->network_info; + complete(&v->complete); + break; + case EVENT_RELEASE_START: + /* If ACQUIRED come in before the RELEASE, + * will only services the RELEASE */ + atomic_inc(&v->rel_start_flag); + wake_up(&v->voc_wait); + wake_up(&v->dev_wait); + v->voc_event = VOICE_RELEASE_START; + complete(&v->complete); + break; + case EVENT_CHANGE_START: + atomic_inc(&v->chg_start_flag); + wake_up(&v->voc_wait); + v->voc_event = VOICE_CHANGE_START; + complete(&v->complete); + break; + case EVENT_NETWORK_RECONFIG: + /* send network change to audio_dev, + if sample rate is less than 16k, + otherwise, send acquire done */ + v->voc_event = VOICE_NETWORK_RECONFIG; + v->network = ((struct voice_network *)evt_buf)->network_info; + complete(&v->complete); + break; + default: + MM_ERR("Undefined event %d \n", hdr->id); + } + +} + +static int voice_cmd_init(struct voice_data *v) +{ + + struct voice_init cmd; + int err; + + MM_DBG("\n"); /* Macro prints the file name and function */ + + cmd.hdr.id = CMD_VOICE_INIT; + cmd.hdr.data_len = sizeof(struct voice_init) - + sizeof(struct voice_header); + cmd.cb_handle = v->cb_handle; + + err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd, + sizeof(struct voice_init)); + + if (err) + MM_ERR("Voice init command failed\n"); + return err; +} + +static int voice_cmd_acquire_done(struct voice_data *v) +{ + struct voice_header hdr; + int err; + + hdr.id = CMD_ACQUIRE_DONE; + hdr.data_len = 0; + + MM_INFO("\n"); /* Macro prints the file name and function */ + + /* Enable HW sidetone if device supports it */ + msm_snddev_enable_sidetone(v->dev_rx.dev_id, 1); + + err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr, + sizeof(struct voice_header)); + + if (err) + MM_ERR("Voice acquire done command failed\n"); + return err; +} + +static int voice_cmd_device_info(struct voice_data *v) +{ + struct voice_device cmd; + int err, vol; + + MM_INFO("tx_dev=%d, rx_dev=%d, tx_sample=%d, tx_mute=%d\n", + v->dev_tx.dev_acdb_id, v->dev_rx.dev_acdb_id, + v->dev_tx.sample, v->dev_tx.mute); + + mutex_lock(&voice.vol_lock); + + cmd.hdr.id = CMD_DEVICE_INFO; + cmd.hdr.data_len = sizeof(struct voice_device) - + sizeof(struct voice_header); + cmd.tx_device = v->dev_tx.dev_acdb_id; + cmd.rx_device = v->dev_rx.dev_acdb_id; + if (v->network == NETWORK_WCDMA_WB) + vol = v->min_rx_vol[VOC_WB_INDEX] + + ((v->max_rx_vol[VOC_WB_INDEX] - + v->min_rx_vol[VOC_WB_INDEX]) * v->dev_rx.volume)/100; + else + vol = v->min_rx_vol[VOC_NB_INDEX] + + ((v->max_rx_vol[VOC_NB_INDEX] - + v->min_rx_vol[VOC_NB_INDEX]) * v->dev_rx.volume)/100; + cmd.rx_volume = (u32)vol; /* in mb */ + cmd.rx_mute = 0; + cmd.tx_mute = v->dev_tx.mute; + cmd.rx_sample = v->dev_rx.sample/1000; + cmd.tx_sample = v->dev_tx.sample/1000; + + MM_DBG("rx_vol=%d, rx_sample=%d\n", cmd.rx_volume, v->dev_rx.sample); + + err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd, + sizeof(struct voice_device)); + + mutex_unlock(&voice.vol_lock); + + if (err) + MM_ERR("Voice device command failed\n"); + return err; +} +EXPORT_SYMBOL(voice_cmd_device_info); + +void voice_change_sample_rate(struct voice_data *v) +{ + int freq = 48000; + int rc = 0; + + MM_DBG("network =%d, vote freq=%d\n", v->network, freq); + if (freq != v->dev_tx.sample) { + rc = msm_snddev_request_freq(&freq, 0, + SNDDEV_CAP_TX, AUDDEV_CLNT_VOC); + if (rc >= 0) { + v->dev_tx.sample = freq; + MM_DBG(" vote for freq=%d successfully \n", freq); + } else + MM_ERR(" voting for freq=%d failed.\n", freq); + } +} + +static int voice_thread(void *data) +{ + struct voice_data *v = (struct voice_data *)data; + int rc = 0; + + MM_INFO("voice_thread() start\n"); + + while (!kthread_should_stop()) { + wait_for_completion(&v->complete); + init_completion(&v->complete); + + MM_DBG(" voc_event=%d, voice state =%d, dev_event=%d\n", + v->voc_event, v->voc_state, v->dev_event); + switch (v->voc_event) { + case VOICE_ACQUIRE_START: + /* check if dev_state = READY */ + /* if ready, send device_info and acquire_done */ + /* if not ready, block to wait the dev_state = READY */ + if ((v->voc_state == VOICE_INIT) || + (v->voc_state == VOICE_RELEASE)) { + if (v->dev_state == DEV_READY) { + mutex_lock(&voice.voc_lock); + voice_change_sample_rate(v); + rc = voice_cmd_device_info(v); + rc = voice_cmd_acquire_done(v); + v->voc_state = VOICE_ACQUIRE; + mutex_unlock(&voice.voc_lock); + broadcast_event( + AUDDEV_EVT_VOICE_STATE_CHG, + VOICE_STATE_INCALL, SESSION_IGNORE); + } else { + rc = wait_event_interruptible( + v->dev_wait, + (v->dev_state == DEV_READY) + || (atomic_read(&v->rel_start_flag) + == 1)); + if (atomic_read(&v->rel_start_flag) + == 1) { + v->voc_state = VOICE_RELEASE; + atomic_dec(&v->rel_start_flag); + msm_snddev_withdraw_freq(0, + SNDDEV_CAP_TX, AUDDEV_CLNT_VOC); + broadcast_event( + AUDDEV_EVT_VOICE_STATE_CHG, + VOICE_STATE_OFFCALL, + SESSION_IGNORE); + } else { + mutex_lock(&voice.voc_lock); + voice_change_sample_rate(v); + rc = voice_cmd_device_info(v); + rc = voice_cmd_acquire_done(v); + v->voc_state = VOICE_ACQUIRE; + mutex_unlock(&voice.voc_lock); + broadcast_event( + AUDDEV_EVT_VOICE_STATE_CHG, + VOICE_STATE_INCALL, + SESSION_IGNORE); + } + } + } else + MM_ERR("Get this event at the wrong state\n"); + if (atomic_read(&v->acq_start_flag)) + atomic_dec(&v->acq_start_flag); + break; + case VOICE_RELEASE_START: + MM_DBG("broadcast voice call end\n"); + broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG, + VOICE_STATE_OFFCALL, SESSION_IGNORE); + if ((v->dev_state == DEV_REL_DONE) || + (v->dev_state == DEV_INIT)) { + v->voc_state = VOICE_RELEASE; + msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX, + AUDDEV_CLNT_VOC); + } else { + /* wait for the dev_state = RELEASE */ + rc = wait_event_interruptible(v->dev_wait, + (v->dev_state == DEV_REL_DONE) + || (atomic_read(&v->acq_start_flag) == 1)); + if (atomic_read(&v->acq_start_flag) == 1) + atomic_dec(&v->acq_start_flag); + v->voc_state = VOICE_RELEASE; + msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX, + AUDDEV_CLNT_VOC); + } + if (atomic_read(&v->rel_start_flag)) + atomic_dec(&v->rel_start_flag); + break; + case VOICE_CHANGE_START: + if (v->voc_state == VOICE_ACQUIRE) + v->voc_state = VOICE_CHANGE; + else + MM_ERR("Get this event at the wrong state\n"); + wake_up(&v->voc_wait); + if (atomic_read(&v->chg_start_flag)) + atomic_dec(&v->chg_start_flag); + break; + case VOICE_NETWORK_RECONFIG: + if ((v->voc_state == VOICE_ACQUIRE) + || (v->voc_state == VOICE_CHANGE)) { + voice_change_sample_rate(v); + rc = voice_cmd_device_info(v); + rc = voice_cmd_acquire_done(v); + } + break; + default: + break; + } + + switch (v->dev_event) { + case DEV_CHANGE_READY: + if (v->voc_state == VOICE_CHANGE) { + mutex_lock(&voice.voc_lock); + msm_snddev_enable_sidetone(v->dev_rx.dev_id, + 1); + /* update voice state */ + v->voc_state = VOICE_ACQUIRE; + v->dev_event = 0; + mutex_unlock(&voice.voc_lock); + broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG, + VOICE_STATE_INCALL, SESSION_IGNORE); + } else { + mutex_lock(&voice.voc_lock); + v->dev_event = 0; + mutex_unlock(&voice.voc_lock); + MM_ERR("Get this event at the wrong state\n"); + } + break; + default: + mutex_lock(&voice.voc_lock); + v->dev_event = 0; + mutex_unlock(&voice.voc_lock); + break; + } + } + return 0; +} + +static int __init voice_init(void) +{ + int rc, i; + struct voice_data *v = &voice; + MM_INFO("\n"); /* Macro prints the file name and function */ + + mutex_init(&voice.voc_lock); + mutex_init(&voice.vol_lock); + v->handle = NULL; + v->cb_handle = NULL; + + /* set default value */ + v->default_mute_val = 1; /* default is mute */ + v->default_vol_val = 0; + v->default_sample_val = 8000; + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) { + v->max_rx_vol[i] = 0; + v->min_rx_vol[i] = 0; + } + v->network = NETWORK_GSM; + + /* initialize dev_rx and dev_tx */ + memset(&v->dev_tx, 0, sizeof(struct device_data)); + memset(&v->dev_rx, 0, sizeof(struct device_data)); + v->dev_rx.volume = v->default_vol_val; + v->dev_tx.mute = v->default_mute_val; + + v->dev_state = DEV_INIT; + v->voc_state = VOICE_INIT; + atomic_set(&v->rel_start_flag, 0); + atomic_set(&v->acq_start_flag, 0); + v->dev_event = 0; + v->voc_event = 0; + init_completion(&voice.complete); + init_waitqueue_head(&v->dev_wait); + init_waitqueue_head(&v->voc_wait); + + /* get device handle */ + rc = daldevice_attach(VOICE_DALRPC_DEVICEID, + VOICE_DALRPC_PORT_NAME, + VOICE_DALRPC_CPU, + &v->handle); + if (rc) { + MM_ERR("Voc DALRPC call to Modem attach failed\n"); + goto done; + } + + /* Allocate the callback handle */ + v->cb_handle = dalrpc_alloc_cb(v->handle, remote_cb_function, v); + if (v->cb_handle == NULL) { + MM_ERR("Allocate Callback failure\n"); + goto err; + } + + /* setup the callback */ + rc = voice_cmd_init(v); + if (rc) + goto err1; + + v->device_events = AUDDEV_EVT_DEV_CHG_VOICE | + AUDDEV_EVT_DEV_RDY | + AUDDEV_EVT_REL_PENDING | + AUDDEV_EVT_START_VOICE | + AUDDEV_EVT_END_VOICE | + AUDDEV_EVT_DEVICE_VOL_MUTE_CHG | + AUDDEV_EVT_FREQ_CHG; + + MM_DBG(" to register call back \n"); + /* register callback to auddev */ + auddev_register_evt_listner(v->device_events, AUDDEV_CLNT_VOC, + 0, voice_auddev_cb_function, v); + + /* create and start thread */ + v->task = kthread_run(voice_thread, v, "voice"); + if (IS_ERR(v->task)) { + rc = PTR_ERR(v->task); + v->task = NULL; + } else + goto done; + +err1: dalrpc_dealloc_cb(v->handle, v->cb_handle); +err: + daldevice_detach(v->handle); + v->handle = NULL; +done: + return rc; +} + +late_initcall(voice_init); diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile new file mode 100644 index 00000000000..9a5561261bf --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -0,0 +1,19 @@ +obj-y += dal.o +obj-y += q6audio.o +obj-y += analog_audio.o +obj-y += pcm_out.o +obj-y += pcm_in.o +obj-y += auxpcm_lb_out.o +obj-y += auxpcm_lb_in.o +obj-y += aac_in.o +obj-y += qcelp_in.o +obj-y += evrc_in.o +obj-y += amrnb_in.o +obj-y += mp3.o +obj-y += dtmf.o +obj-y += routing.o +obj-y += audio_ctl.o +obj-y += msm_q6vdec.o +obj-y += msm_q6venc.o +obj-y += dsp_debug.o +obj-$(CONFIG_QSD_AUDIO) += audiov2/ diff --git a/arch/arm/mach-msm/qdsp6/aac_in.c b/arch/arm/mach-msm/qdsp6/aac_in.c new file mode 100644 index 00000000000..9e1d5b6cdfc --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/aac_in.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AAC_FC_BUFF_CNT 10 +#define AAC_READ_TIMEOUT 2000 +struct aac_fc_buff { + struct mutex lock; + int empty; + void *data; + int size; + int actual_size; +}; + +struct aac_fc { + struct task_struct *task; + wait_queue_head_t fc_wq; + struct aac_fc_buff fc_buff[AAC_FC_BUFF_CNT]; + int buff_index; +}; +struct aac { + struct mutex lock; + struct msm_audio_aac_enc_config cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; + struct msm_voicerec_mode voicerec_mode; + struct aac_fc *aac_fc; +}; + +static int q6_aac_flowcontrol(void *data) +{ + struct audio_client *ac; + struct audio_buffer *ab; + struct aac *aac = data; + int buff_index = 0; + int xfer = 0; + struct aac_fc *fc; + + + ac = aac->audio_client; + fc = aac->aac_fc; + if (!ac) { + pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__); + return 0; + } + + while (!kthread_should_stop()) { + ab = ac->buf + ac->cpu_buf; + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__, + __func__, ab->data, ac->cpu_buf); + xfer = ab->actual_size; + + mutex_lock(&(fc->fc_buff[buff_index].lock)); + if (!fc->fc_buff[buff_index].empty) { + pr_err("[%s:%s] flow control buffer[%d] not read!\n", + __MM_FILE__, __func__, buff_index); + } + + if (fc->fc_buff[buff_index].size < xfer) { + pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__, + __func__, buff_index); + memcpy(fc->fc_buff[buff_index].data, + ab->data, fc->fc_buff[buff_index].size); + fc->fc_buff[buff_index].empty = 0; + fc->fc_buff[buff_index].actual_size = + fc->fc_buff[buff_index].size; + } else { + memcpy(fc->fc_buff[buff_index].data, ab->data, xfer); + fc->fc_buff[buff_index].empty = 0; + fc->fc_buff[buff_index].actual_size = xfer; + } + mutex_unlock(&(fc->fc_buff[buff_index].lock)); + /*wake up client, if any*/ + wake_up(&fc->fc_wq); + + buff_index++; + if (buff_index >= AAC_FC_BUFF_CNT) + buff_index = 0; + + ab->used = 1; + + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + return 0; +} +static long q6_aac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct aac *aac = file->private_data; + int rc = 0; + int i = 0; + struct aac_fc *fc; + int size = 0; + + mutex_lock(&aac->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STATS: + { + struct msm_audio_stats stats; + pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__); + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + case AUDIO_START: + { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else { + if (copy_from_user(&acdb_id, (void *) arg, + sizeof(acdb_id))) { + rc = -EFAULT; + break; + } + } + if (aac->audio_client) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } else { + aac->audio_client = q6audio_open_aac( + aac->str_cfg.buffer_size, + aac->cfg.sample_rate, + aac->cfg.channels, + aac->cfg.bit_rate, + aac->cfg.stream_format, + aac->voicerec_mode.rec_mode, acdb_id); + + if (aac->audio_client < 0) { + pr_err("[%s:%s] aac open session failed\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + break; + } + } + + /*allocate flow control buffers*/ + fc = aac->aac_fc; + size = ((aac->str_cfg.buffer_size < 1543) ? 1543 : + aac->str_cfg.buffer_size); + for (i = 0; i < AAC_FC_BUFF_CNT; ++i) { + mutex_init(&(fc->fc_buff[i].lock)); + fc->fc_buff[i].empty = 1; + fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL); + if (fc->fc_buff[i].data == NULL) { + pr_err("[%s:%s] No memory for FC buffers\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + goto fc_fail; + } + fc->fc_buff[i].size = size; + fc->fc_buff[i].actual_size = 0; + } + + /*create flow control thread*/ + fc->task = kthread_run(q6_aac_flowcontrol, + aac, "aac_flowcontrol"); + if (IS_ERR(fc->task)) { + rc = PTR_ERR(fc->task); + pr_err("[%s:%s] error creating flow control thread\n", + __MM_FILE__, __func__); + goto fc_fail; + } + break; +fc_fail: + /*free flow control buffers*/ + --i; + for (; i >= 0; i--) { + kfree(fc->fc_buff[i].data); + fc->fc_buff[i].data = NULL; + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_INCALL: { + pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__); + if (copy_from_user(&aac->voicerec_mode, + (void *)arg, sizeof(struct msm_voicerec_mode))) + rc = -EFAULT; + + if (aac->voicerec_mode.rec_mode != AUDIO_FLAG_READ + && aac->voicerec_mode.rec_mode != + AUDIO_FLAG_INCALL_MIXED) { + aac->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + } + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &aac->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", + __MM_FILE__, __func__, aac->str_cfg.buffer_size, + aac->str_cfg.buffer_count); + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&aac->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", + __MM_FILE__, __func__, aac->str_cfg.buffer_size, + aac->str_cfg.buffer_count); + if (aac->str_cfg.buffer_size < 1543) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + if (aac->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + + break; + case AUDIO_SET_AAC_ENC_CONFIG: + if (copy_from_user(&aac->cfg, (void *) arg, + sizeof(struct msm_audio_aac_enc_config))) { + rc = -EFAULT; + } + pr_debug("[%s:%s] SET_AAC_ENC_CONFIG: channels=%d, rate=%d\n", + __MM_FILE__, __func__, aac->cfg.channels, + aac->cfg.sample_rate); + if (aac->cfg.channels < 1 || aac->cfg.channels > 2) { + pr_err("[%s:%s]invalid number of channels\n", + __MM_FILE__, __func__); + rc = -EINVAL; + } + if (aac->cfg.sample_rate != 48000) { + pr_err("[%s:%s] only 48KHz is supported\n", + __MM_FILE__, __func__); + rc = -EINVAL; + } + if (aac->cfg.stream_format != AUDIO_AAC_FORMAT_RAW && + aac->cfg.stream_format != AUDIO_AAC_FORMAT_ADTS) { + pr_err("[%s:%s] unsupported AAC format\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + case AUDIO_GET_AAC_ENC_CONFIG: + if (copy_to_user((void *) arg, &aac->cfg, + sizeof(struct msm_audio_aac_enc_config))) { + rc = -EFAULT; + } + pr_debug("[%s:%s] GET_AAC_ENC_CONFIG: channels=%d, rate=%d\n", + __MM_FILE__, __func__, aac->cfg.channels, + aac->cfg.sample_rate); + break; + default: + rc = -EINVAL; + } + + mutex_unlock(&aac->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int q6_aac_in_open(struct inode *inode, struct file *file) +{ + + struct aac *aac; + struct aac_fc *fc; + int i; + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + aac = kmalloc(sizeof(struct aac), GFP_KERNEL); + if (aac == NULL) { + pr_err("[%s:%s] Could not allocate memory for aac driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&aac->lock); + file->private_data = aac; + aac->audio_client = NULL; + aac->str_cfg.buffer_size = 1543; + aac->str_cfg.buffer_count = 2; + aac->cfg.channels = 1; + aac->cfg.bit_rate = 192000; + aac->cfg.stream_format = AUDIO_AAC_FORMAT_ADTS; + aac->cfg.sample_rate = 48000; + aac->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + + aac->aac_fc = kmalloc(sizeof(struct aac_fc), GFP_KERNEL); + if (aac->aac_fc == NULL) { + pr_err("[%s:%s] Could not allocate memory for aac_fc\n", + __MM_FILE__, __func__); + kfree(aac); + return -ENOMEM; + } + fc = aac->aac_fc; + fc->task = NULL; + fc->buff_index = 0; + for (i = 0; i < AAC_FC_BUFF_CNT; ++i) { + fc->fc_buff[i].data = NULL; + fc->fc_buff[i].size = 0; + fc->fc_buff[i].actual_size = 0; + } + /*initialize wait queue head*/ + init_waitqueue_head(&fc->fc_wq); + return 0; +} + +static ssize_t q6_aac_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + const char __user *start = buf; + struct aac *aac = file->private_data; + struct aac_fc *fc; + int xfer = 0; + int res = 0; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + mutex_lock(&aac->lock); + ac = aac->audio_client; + + if (!ac) { + res = -ENODEV; + goto fail; + } + fc = aac->aac_fc; + + /*wait for buffer to full*/ + if (fc->fc_buff[fc->buff_index].empty != 0) { + res = wait_event_interruptible_timeout(fc->fc_wq, + (fc->fc_buff[fc->buff_index].empty == 0), + msecs_to_jiffies(AAC_READ_TIMEOUT)); + + pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__, + __func__, fc->buff_index); + if (res == 0) { + pr_err("[%s:%s] Timeout!\n", __MM_FILE__, __func__); + res = -ETIMEDOUT; + goto fail; + } else if (res < 0) { + pr_err("[%s:%s] Returning on Interrupt\n", __MM_FILE__, + __func__); + goto fail; + } + } + /*lock the buffer*/ + mutex_lock(&(fc->fc_buff[fc->buff_index].lock)); + xfer = fc->fc_buff[fc->buff_index].actual_size; + + if (xfer > count) { + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + pr_err("[%s:%s] read failed! byte count too small\n", + __MM_FILE__, __func__); + res = -EINVAL; + goto fail; + } + + if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) { + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + pr_err("[%s:%s] copy_to_user failed at index %d\n", + __MM_FILE__, __func__, fc->buff_index); + res = -EFAULT; + goto fail; + } + + buf += xfer; + + fc->fc_buff[fc->buff_index].empty = 1; + fc->fc_buff[fc->buff_index].actual_size = 0; + + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + ++(fc->buff_index); + if (fc->buff_index >= AAC_FC_BUFF_CNT) + fc->buff_index = 0; + + res = buf - start; +fail: + mutex_unlock(&aac->lock); + + return res; +} + +static int q6_aac_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct aac *aac = file->private_data; + int i = 0; + struct aac_fc *fc; + + mutex_lock(&aac->lock); + fc = aac->aac_fc; + kthread_stop(fc->task); + fc->task = NULL; + + /*free flow control buffers*/ + for (i = 0; i < AAC_FC_BUFF_CNT; ++i) { + kfree(fc->fc_buff[i].data); + fc->fc_buff[i].data = NULL; + } + kfree(fc); + if (aac->audio_client) + rc = q6audio_close(aac->audio_client); + mutex_unlock(&aac->lock); + kfree(aac); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return rc; +} + +static const struct file_operations q6_aac_in_fops = { + .owner = THIS_MODULE, + .open = q6_aac_in_open, + .read = q6_aac_in_read, + .release = q6_aac_in_release, + .unlocked_ioctl = q6_aac_in_ioctl, +}; + +struct miscdevice q6_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &q6_aac_in_fops, +}; + +static int __init q6_aac_in_init(void) +{ + return misc_register(&q6_aac_in_misc); +} + +device_initcall(q6_aac_in_init); diff --git a/arch/arm/mach-msm/qdsp6/amrnb_in.c b/arch/arm/mach-msm/qdsp6/amrnb_in.c new file mode 100644 index 00000000000..e7756e14831 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/amrnb_in.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "dal_audio_format.h" +#include + +struct amrnb { + struct mutex lock; + struct msm_audio_amrnb_enc_config_v2 cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; + struct msm_voicerec_mode voicerec_mode; +}; + + +static long q6_amrnb_in_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct amrnb *amrnb = file->private_data; + int rc = 0; + + mutex_lock(&amrnb->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: + pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__); + break; + case AUDIO_GET_STATS: + { + struct msm_audio_stats stats; + pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__); + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + case AUDIO_START: + { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else { + if (copy_from_user(&acdb_id, (void *) arg, + sizeof(acdb_id))) { + rc = -EFAULT; + break; + } + } + if (amrnb->audio_client) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } else { + amrnb->audio_client = q6audio_open_amrnb( + amrnb->str_cfg.buffer_size, + amrnb->cfg.band_mode, + amrnb->cfg.dtx_enable, + amrnb->voicerec_mode.rec_mode, + acdb_id); + if (!amrnb->audio_client) { + pr_err("[%s:%s] amrnb open session failed\n", + __MM_FILE__, __func__); + kfree(amrnb); + rc = -ENOMEM; + break; + } + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_INCALL: { + pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__); + if (copy_from_user(&amrnb->voicerec_mode, + (void *)arg, sizeof(struct msm_voicerec_mode))) + rc = -EFAULT; + + if (amrnb->voicerec_mode.rec_mode != AUDIO_FLAG_READ + && amrnb->voicerec_mode.rec_mode != + AUDIO_FLAG_INCALL_MIXED) { + amrnb->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + } + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &amrnb->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt = %d\n", + __MM_FILE__, __func__, amrnb->str_cfg.buffer_size, + amrnb->str_cfg.buffer_count); + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&amrnb->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt = %d\n", + __MM_FILE__, __func__, amrnb->str_cfg.buffer_size, + amrnb->str_cfg.buffer_count); + + if (amrnb->str_cfg.buffer_size < 768) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + + if (amrnb->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + break; + case AUDIO_SET_AMRNB_ENC_CONFIG: + if (copy_from_user(&amrnb->cfg, (void *) arg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + pr_debug("[%s:%s] SET_AMRNB_ENC_CONFIG\n", __MM_FILE__, + __func__); + break; + case AUDIO_GET_AMRNB_ENC_CONFIG: + if (copy_to_user((void *) arg, &amrnb->cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_AMRNB_ENC_CONFIG\n", __MM_FILE__, + __func__); + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&amrnb->lock); + pr_debug("[%s:%s] rc= %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int q6_amrnb_in_open(struct inode *inode, struct file *file) +{ + struct amrnb *amrnb; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + amrnb = kmalloc(sizeof(struct amrnb), GFP_KERNEL); + if (amrnb == NULL) { + pr_err("[%s:%s] Could not allocate memory for amrnb driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&amrnb->lock); + file->private_data = amrnb; + amrnb->audio_client = NULL; + amrnb->str_cfg.buffer_size = 768; + amrnb->str_cfg.buffer_count = 2; + amrnb->cfg.band_mode = 7; + amrnb->cfg.dtx_enable = 3; + amrnb->cfg.frame_format = ADSP_AUDIO_FORMAT_AMRNB_FS; + amrnb->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + + return 0; +} + +static ssize_t q6_amrnb_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + struct amrnb *amrnb = file->private_data; + int xfer = 0; + int res; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + mutex_lock(&amrnb->lock); + ac = amrnb->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > xfer) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__, + __func__, ab->data, ac->cpu_buf); + xfer = ab->actual_size; + + if (copy_to_user(buf, ab->data, xfer)) { + pr_err("[%s:%s] copy_to_user failed\n", + __MM_FILE__, __func__); + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + res = buf - start; +fail: + mutex_unlock(&amrnb->lock); + + return res; +} + +static int q6_amrnb_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct amrnb *amrnb = file->private_data; + + mutex_lock(&amrnb->lock); + if (amrnb->audio_client) + rc = q6audio_close(amrnb->audio_client); + mutex_unlock(&amrnb->lock); + kfree(amrnb); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return rc; +} + +static const struct file_operations q6_amrnb_in_fops = { + .owner = THIS_MODULE, + .open = q6_amrnb_in_open, + .read = q6_amrnb_in_read, + .release = q6_amrnb_in_release, + .unlocked_ioctl = q6_amrnb_in_ioctl, +}; + +struct miscdevice q6_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amr_in", + .fops = &q6_amrnb_in_fops, +}; + +static int __init q6_amrnb_in_init(void) +{ + return misc_register(&q6_amrnb_in_misc); +} + +device_initcall(q6_amrnb_in_init); diff --git a/arch/arm/mach-msm/qdsp6/analog_audio.c b/arch/arm/mach-msm/qdsp6/analog_audio.c new file mode 100644 index 00000000000..688f57e3410 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/analog_audio.c @@ -0,0 +1,94 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_HEADSET_AMP 157 +#define GPIO_SPEAKER_AMP 39 +#define GPIO_HEADSET_SHDN_N 48 + +void analog_init(void) +{ + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_mic_set_volt(MIC_VOLT_1_80V); + gpio_direction_output(GPIO_HEADSET_AMP, 1); + gpio_set_value(GPIO_HEADSET_AMP, 0); +} + +void analog_headset_enable(int en) +{ + pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en); + /* enable audio amp */ + gpio_set_value(GPIO_HEADSET_AMP, !!en); +} + +void analog_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en); + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 1; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } +} + +void analog_mic_enable(int en) +{ + pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en); + pmic_mic_en(en); +} + +static struct q6audio_analog_ops ops = { + .init = analog_init, + .speaker_enable = analog_speaker_enable, + .headset_enable = analog_headset_enable, + .int_mic_enable = analog_mic_enable, + .ext_mic_enable = analog_mic_enable, +}; + +static int __init init(void) +{ + q6audio_register_analog_ops(&ops); + return 0; +} + +device_initcall(init); diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c new file mode 100644 index 00000000000..ab1df3992e4 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define BUFSZ (0) + +static DEFINE_MUTEX(voice_lock); +static int voice_started; + +static struct audio_client *voc_tx_clnt; +static struct audio_client *voc_rx_clnt; + +static int q6_voice_start(void) +{ + int rc = 0; + + mutex_lock(&voice_lock); + + if (voice_started) { + pr_err("[%s:%s] busy\n", __MM_FILE__, __func__); + rc = -EBUSY; + goto done; + } + + voc_tx_clnt = q6voice_open(AUDIO_FLAG_WRITE); + if (!voc_tx_clnt) { + pr_err("[%s:%s] open voice tx failed.\n", __MM_FILE__, + __func__); + rc = -ENOMEM; + goto done; + } + + voc_rx_clnt = q6voice_open(AUDIO_FLAG_READ); + if (!voc_rx_clnt) { + pr_err("[%s:%s] open voice rx failed.\n", __MM_FILE__, + __func__); + q6voice_close(voc_tx_clnt); + rc = -ENOMEM; + } + + voice_started = 1; +done: + mutex_unlock(&voice_lock); + return rc; +} + +static int q6_voice_stop(void) +{ + mutex_lock(&voice_lock); + if (voice_started) { + q6voice_close(voc_tx_clnt); + q6voice_close(voc_rx_clnt); + voice_started = 0; + } + mutex_unlock(&voice_lock); + return 0; +} + +static int q6_open(struct inode *inode, struct file *file) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return 0; +} + +static long q6_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc; + uint32_t n; + uint32_t id[2]; + uint32_t mute_status; + + switch (cmd) { + case AUDIO_SWITCH_DEVICE: + rc = copy_from_user(&id, (void *)arg, sizeof(id)); + pr_info("[%s:%s] SWITCH_DEV: id[0] = 0x%x, id[1] = 0x%x", + __MM_FILE__, __func__, id[0], id[1]); + if (!rc) + rc = q6audio_do_routing(id[0], id[1]); + break; + case AUDIO_SET_VOLUME: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + pr_debug("[%s:%s] SET_VOLUME: vol = %d\n", __MM_FILE__, + __func__, n); + if (!rc) + rc = q6audio_set_rx_volume(n); + break; + case AUDIO_SET_MUTE: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + if (!rc) { + if (voice_started) { + if (n == 1) + mute_status = STREAM_MUTE; + else + mute_status = STREAM_UNMUTE; + } else { + if (n == 1) + mute_status = DEVICE_MUTE; + else + mute_status = DEVICE_UNMUTE; + } + + pr_debug("[%s:%s] SET_MUTE: mute_status = %d\n", + __MM_FILE__, __func__, mute_status); + rc = q6audio_set_tx_mute(mute_status); + } + break; + case AUDIO_UPDATE_ACDB: + rc = copy_from_user(&id, (void *)arg, sizeof(id)); + pr_debug("[%s:%s] UPDATE_ACDB: id[0] = 0x%x, id[1] = 0x%x\n", + __MM_FILE__, __func__, id[0], id[1]); + if (!rc) + rc = q6audio_update_acdb(id[0], 0); + break; + case AUDIO_START_VOICE: + pr_debug("[%s:%s] START_VOICE\n", __MM_FILE__, __func__); + rc = q6_voice_start(); + break; + case AUDIO_STOP_VOICE: + pr_debug("[%s:%s] STOP_VOICE\n", __MM_FILE__, __func__); + rc = q6_voice_stop(); + break; + case AUDIO_REINIT_ACDB: + pr_debug("[%s:%s] REINIT_ACDB\n", __MM_FILE__, __func__); + rc = 0; + break; + default: + rc = -EINVAL; + } + + return rc; +} + + +static int q6_release(struct inode *inode, struct file *file) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return 0; +} + +static struct file_operations q6_dev_fops = { + .owner = THIS_MODULE, + .open = q6_open, + .unlocked_ioctl = q6_ioctl, + .release = q6_release, +}; + +struct miscdevice q6_control_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_ctl", + .fops = &q6_dev_fops, +}; + + +static int __init q6_audio_ctl_init(void) { + return misc_register(&q6_control_device); +} + +device_initcall(q6_audio_ctl_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/Makefile b/arch/arm/mach-msm/qdsp6/audiov2/Makefile new file mode 100644 index 00000000000..86ab9aeab71 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/Makefile @@ -0,0 +1,12 @@ +obj-y += q6audio.o +obj-y += aac_in.o +obj-y += voice.o +obj-y += pcm_out.o +obj-y += pcm_in.o +obj-y += mp3.o +obj-y += audio_ctl.o +obj-y += analog_audio.o +obj-y += routing.o +obj-y += evrc_in.o +obj-y += qcelp_in.o +obj-y += amrnb_in.o diff --git a/arch/arm/mach-msm/qdsp6/audiov2/aac_in.c b/arch/arm/mach-msm/qdsp6/audiov2/aac_in.c new file mode 100644 index 00000000000..fe6c049a35f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/aac_in.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "dal_audio.h" +#include "dal_audio_format.h" + +struct aac { + struct mutex lock; + struct msm_audio_aac_enc_config cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; +}; + +static long q6_aac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct aac *aac = file->private_data; + struct adsp_open_command rpc; + + int sample_rate; + int audio_object_type; + int index = sizeof(u32); + int rc = 0; + u32 *aac_type = NULL; + + + mutex_lock(&aac->lock); + switch (cmd) { + + case AUDIO_START: + if (aac->audio_client) { + rc = -EBUSY; + break; + } else { + tx_clk_freq = 48000; + aac->audio_client = q6audio_open(AUDIO_FLAG_READ, + aac->str_cfg.buffer_size); + + if (aac->audio_client < 0) { + + tx_clk_freq = 8000; + rc = -ENOMEM; + break; + } + } + memset(&rpc, 0, sizeof(rpc)); + + rpc.format_block.binary.format = ADSP_AUDIO_FORMAT_MPEG4_AAC; + /* only 48k sample rate is supported */ + sample_rate = 3; + + /* AAC OBJECT LC */ + audio_object_type = 2; + + aac_type = (u32 *)rpc.format_block.binary.data; + switch (aac->cfg.stream_format) { + + case AUDIO_AAC_FORMAT_ADTS: + /* AAC Encoder expect MPEG4_ADTS media type */ + *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; + break; + case AUDIO_AAC_FORMAT_RAW: + /* for ADIF recording */ + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + } + + rpc.format_block.binary.data[index++] = (u8)( + ((audio_object_type & 0x1F) << 3) | + ((sample_rate >> 1) & 0x7)); + rpc.format_block.binary.data[index] = (u8)( + ((sample_rate & 0x1) << 7) | + ((aac->cfg.channels & 0x7) << 3)); + + rpc.format_block.binary.num_bytes = index + 1; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = aac->str_cfg.buffer_size; + rpc.config.aac.bit_rate = aac->cfg.bit_rate; + rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; + q6audio_start(aac->audio_client, &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &aac->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&aac->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + if (aac->str_cfg.buffer_size < 519) { + pr_err("Buffer size too small\n"); + rc = -EINVAL; + break; + } + if (aac->str_cfg.buffer_count != 2) + pr_info("Buffer count set to 2\n"); + + break; + case AUDIO_SET_AAC_ENC_CONFIG: + if (copy_from_user(&aac->cfg, (void *) arg, + sizeof(struct msm_audio_aac_enc_config))) { + rc = -EFAULT; + } + if (aac->cfg.channels != 1) { + pr_err("only mono is supported\n"); + rc = -EINVAL; + } + if (aac->cfg.sample_rate != 48000) { + pr_err("only 48KHz is supported\n"); + rc = -EINVAL; + } + if (aac->cfg.stream_format != AUDIO_AAC_FORMAT_RAW && + aac->cfg.stream_format != AUDIO_AAC_FORMAT_ADTS) { + pr_err("unsupported AAC format\n"); + rc = -EINVAL; + } + break; + case AUDIO_GET_AAC_ENC_CONFIG: + if (copy_to_user((void *) arg, &aac->cfg, + sizeof(struct msm_audio_aac_enc_config))) { + rc = -EFAULT; + } + break; + default: + rc = -EINVAL; + } + + mutex_unlock(&aac->lock); + return rc; +} + +static int q6_aac_in_open(struct inode *inode, struct file *file) +{ + + struct aac *aac; + aac = kmalloc(sizeof(struct aac), GFP_KERNEL); + if (aac == NULL) { + pr_err("Could not allocate memory for aac driver\n"); + return -ENOMEM; + } + + mutex_init(&aac->lock); + file->private_data = aac; + aac->audio_client = NULL; + aac->str_cfg.buffer_size = 519; + aac->str_cfg.buffer_count = 2; + aac->cfg.channels = 1; + aac->cfg.bit_rate = 192000; + aac->cfg.stream_format = AUDIO_AAC_FORMAT_ADTS; + aac->cfg.sample_rate = 48000; + + return 0; +} + +static ssize_t q6_aac_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + struct aac *aac = file->private_data; + int xfer = 0; + int res; + + mutex_lock(&aac->lock); + ac = aac->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > xfer) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = ab->actual_size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + res = buf - start; +fail: + mutex_unlock(&aac->lock); + + return res; +} + +static int q6_aac_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct aac *aac = file->private_data; + + mutex_lock(&aac->lock); + if (aac->audio_client) + rc = q6audio_close(aac->audio_client); + mutex_unlock(&aac->lock); + kfree(aac); + tx_clk_freq = 8000; + return rc; +} + +static const struct file_operations q6_aac_in_fops = { + .owner = THIS_MODULE, + .open = q6_aac_in_open, + .read = q6_aac_in_read, + .release = q6_aac_in_release, + .unlocked_ioctl = q6_aac_in_ioctl, +}; + +struct miscdevice q6_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &q6_aac_in_fops, +}; + +static int __init q6_aac_in_init(void) +{ + return misc_register(&q6_aac_in_misc); +} + +device_initcall(q6_aac_in_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/amrnb_in.c b/arch/arm/mach-msm/qdsp6/audiov2/amrnb_in.c new file mode 100644 index 00000000000..b877977ac1f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/amrnb_in.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "dal_audio.h" +#include "dal_audio_format.h" +#include + + +struct amrnb { + struct mutex lock; + struct msm_audio_amrnb_enc_config_v2 cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; +}; + + +static long q6_amrnb_in_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct amrnb *amrnb = file->private_data; + struct adsp_open_command rpc; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&amrnb->lock); + switch (cmd) { + case AUDIO_START: + if (amrnb->audio_client) { + rc = -EBUSY; + break; + } else { + amrnb->audio_client = q6audio_open(AUDIO_FLAG_READ, + amrnb->str_cfg.buffer_size); + + if (!amrnb->audio_client) { + kfree(amrnb); + rc = -ENOMEM; + break; + } + } + + tx_clk_freq = 8000; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_AMRNB_FS; + rpc.format_block.standard.channels = 1; + rpc.format_block.standard.bits_per_sample = 16; + rpc.format_block.standard.sampling_rate = 8000; + rpc.format_block.standard.is_signed = 1; + rpc.format_block.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = amrnb->str_cfg.buffer_size; + rpc.config.amr.mode = amrnb->cfg.band_mode; + rpc.config.amr.dtx_mode = amrnb->cfg.dtx_enable; + rpc.config.amr.enable = 1; + q6audio_start(amrnb->audio_client, &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &amrnb->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&amrnb->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + + if (amrnb->str_cfg.buffer_size < 768) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + + if (amrnb->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + break; + case AUDIO_SET_AMRNB_ENC_CONFIG: + if (copy_from_user(&amrnb->cfg, (void *) arg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + break; + case AUDIO_GET_AMRNB_ENC_CONFIG: + if (copy_to_user((void *) arg, &amrnb->cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&amrnb->lock); + return rc; +} + +static int q6_amrnb_in_open(struct inode *inode, struct file *file) +{ + struct amrnb *amrnb; + amrnb = kmalloc(sizeof(struct amrnb), GFP_KERNEL); + if (amrnb == NULL) { + pr_err("[%s:%s] Could not allocate memory for amrnb driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&amrnb->lock); + file->private_data = amrnb; + amrnb->audio_client = NULL; + amrnb->str_cfg.buffer_size = 768; + amrnb->str_cfg.buffer_count = 2; + amrnb->cfg.band_mode = ADSP_AUDIO_AMR_MR475; + amrnb->cfg.dtx_enable = ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO; + amrnb->cfg.frame_format = ADSP_AUDIO_FORMAT_AMRNB_FS; + return 0; +} + +static ssize_t q6_amrnb_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + struct amrnb *amrnb = file->private_data; + int xfer = 0; + int res; + + mutex_lock(&amrnb->lock); + ac = amrnb->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > xfer) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = ab->actual_size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + res = buf - start; +fail: + mutex_unlock(&amrnb->lock); + + return res; +} + +static int q6_amrnb_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct amrnb *amrnb = file->private_data; + + mutex_lock(&amrnb->lock); + if (amrnb->audio_client) + rc = q6audio_close(amrnb->audio_client); + mutex_unlock(&amrnb->lock); + kfree(amrnb); + return rc; +} + +static const struct file_operations q6_amrnb_in_fops = { + .owner = THIS_MODULE, + .open = q6_amrnb_in_open, + .read = q6_amrnb_in_read, + .release = q6_amrnb_in_release, + .unlocked_ioctl = q6_amrnb_in_ioctl, +}; + +struct miscdevice q6_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amr_in", + .fops = &q6_amrnb_in_fops, +}; + +static int __init q6_amrnb_in_init(void) +{ + return misc_register(&q6_amrnb_in_misc); +} + +device_initcall(q6_amrnb_in_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/analog_audio.c b/arch/arm/mach-msm/qdsp6/audiov2/analog_audio.c new file mode 100644 index 00000000000..1df4f5d2f67 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/analog_audio.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#define GPIO_HEADSET_AMP 157 + +void analog_init(void) +{ + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_mic_set_volt(MIC_VOLT_1_80V); + + gpio_direction_output(GPIO_HEADSET_AMP, 1); + gpio_set_value(GPIO_HEADSET_AMP, 0); +} + +void analog_headset_enable(int en) +{ + /* enable audio amp */ + gpio_set_value(GPIO_HEADSET_AMP, !!en); +} + +void analog_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 1; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } +} + +void analog_mic_enable(int en) +{ + pmic_mic_en(en); +} + +static struct q6audio_analog_ops ops = { + .init = analog_init, + .speaker_enable = analog_speaker_enable, + .headset_enable = analog_headset_enable, + .int_mic_enable = analog_mic_enable, +}; + +static int __init init(void) +{ + q6audio_register_analog_ops(&ops); + return 0; +} + +device_initcall(init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audiov2/audio_ctl.c new file mode 100644 index 00000000000..286d85d8ce5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/audio_ctl.c @@ -0,0 +1,140 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/audio_ctrl.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include + +#define BUFSZ (0) + +static DEFINE_MUTEX(voice_lock); +static int voice_started; + +static struct audio_client *voc_clnt; + +static int q6_voice_start(void) +{ + int rc = 0; + + mutex_lock(&voice_lock); + + if (voice_started) { + pr_err("voice: busy\n"); + rc = -EBUSY; + goto done; + } + + voc_clnt = q6voice_open(); + if (!voc_clnt) { + pr_err("voice: open voice failed.\n"); + rc = -ENOMEM; + goto done; + } + + voice_started = 1; +done: + mutex_unlock(&voice_lock); + return rc; +} + +static int q6_voice_stop(void) +{ + mutex_lock(&voice_lock); + if (voice_started) { + q6voice_close(voc_clnt); + voice_started = 0; + } + mutex_unlock(&voice_lock); + return 0; +} + +static int q6_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int q6_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc; + uint32_t n; + uint32_t id[2]; + + switch (cmd) { + case AUDIO_SWITCH_DEVICE: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + if (!rc) + rc = q6audio_do_routing(n); + break; + case AUDIO_SET_VOLUME: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + if (!rc) + rc = q6audio_set_rx_volume(n); + break; + case AUDIO_SET_MUTE: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + if (!rc) + rc = q6audio_set_tx_mute(n); + break; + case AUDIO_UPDATE_ACDB: + rc = copy_from_user(&id, (void *)arg, sizeof(id)); + if (!rc) + rc = q6audio_update_acdb(id[0], id[1]); + break; + case AUDIO_START_VOICE: + rc = q6_voice_start(); + break; + case AUDIO_STOP_VOICE: + rc = q6_voice_stop(); + break; + default: + rc = -EINVAL; + } + + return rc; +} + + +static int q6_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations q6_dev_fops = { + .owner = THIS_MODULE, + .open = q6_open, + .ioctl = q6_ioctl, + .release = q6_release, +}; + +struct miscdevice q6_control_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_ctl", + .fops = &q6_dev_fops, +}; + + +static int __init q6_audio_ctl_init(void) +{ + return misc_register(&q6_control_device); +} + +device_initcall(q6_audio_ctl_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_acdb.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_acdb.h new file mode 100644 index 00000000000..d88b7ad3662 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_acdb.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define ACDB_DAL_DEVICE 0x02000069 +#define ACDB_DAL_PORT "DAL_AM_AUD" +#define ACDB_DAL_VERSION 0x00010000 + +#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API + +/* ioctls */ +#define ACDB_GET_DEVICE 0x0108bb92 +#define ACDB_SET_DEVICE 0x0108bb93 +#define ACDB_GET_STREAM 0x0108bb95 +#define ACDB_SET_STREAM 0x0108bb96 +#define ACDB_GET_DEVICE_TABLE 0x0108bb97 +#define ACDB_GET_STREAM_TABLE 0x0108bb98 + +#define ACDB_RES_SUCCESS 0 +#define ACDB_RES_FAILURE -1 +#define ACDB_RES_BADPARM -2 +#define ACDB_RES_BADSTATE -3 + +struct acdb_cmd_device { + uint32_t size; + + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; + uint32_t interface_id; + uint32_t algorithm_block_id; + + /* physical page aligned buffer */ + uint32_t total_bytes; + uint32_t unmapped_buf; +} __attribute__((packed)); + +struct acdb_cmd_device_table { + uint32_t size; + + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; + + /* physical page aligned buffer */ + uint32_t total_bytes; + uint32_t unmapped_buf; + + uint32_t res_size; +} __attribute__((packed)); + +struct acdb_result { + uint32_t dal_status; + uint32_t size; + + uint32_t total_devices; + uint32_t unmapped_buf; + uint32_t used_bytes; + uint32_t result; +} __attribute__((packed)); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_adie.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_adie.h new file mode 100644 index 00000000000..e828e9c14a7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_adie.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_MSM_QDSP6_ADIE_ +#define _MACH_MSM_QDSP6_ADIE_ + +#include "../dal.h" + +#define ADIE_DAL_DEVICE 0x02000029 +#define ADIE_DAL_PORT "DAL_AM_AUD" +#define ADIE_DAL_VERSION 0x00010000 + +enum { + ADIE_OP_SET_PATH = DAL_OP_FIRST_DEVICE_API, + ADIE_OP_PROCEED_TO_STAGE, + ADIE_OP_IOCTL +}; + +/* Path IDs for normal operation. */ +#define ADIE_PATH_HANDSET_TX 0x010740f6 +#define ADIE_PATH_HANDSET_RX 0x010740f7 +#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8 +#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9 +#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa +#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb +#define ADIE_PATH_SPEAKER_TX 0x010740fc +#define ADIE_PATH_SPEAKER_RX 0x010740fd +#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101 + +/* Path IDs used for TTY */ +#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe +#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff + +/* Path IDs used by Factory Test Mode. */ +#define ADIE_PATH_FTM_MIC1_TX 0x01074108 +#define ADIE_PATH_FTM_MIC2_TX 0x01074107 +#define ADIE_PATH_FTM_HPH_L_RX 0x01074106 +#define ADIE_PATH_FTM_HPH_R_RX 0x01074104 +#define ADIE_PATH_FTM_EAR_RX 0x01074103 +#define ADIE_PATH_FTM_SPKR_RX 0x01074102 + +/* Path IDs for Loopback */ +/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/ +#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100 +/* Line in -> AuxPGA -> LineOut Mono */ +#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82 +/* Line in -> AuxPGA -> Stereo Headphone */ +#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109 +/* Line in -> AuxPGA -> Mono Headphone */ +#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85 +/* Line in -> AuxPGA -> Earpiece */ +#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81 +/* Line in -> AuxPGA -> AuxOut */ +#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86 + +/* Concurrency Profiles */ +#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83 +#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84 +#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88 +#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89 + +/* stages */ +#define ADIE_STAGE_PATH_OFF 0x0050 +#define ADIE_STAGE_DIGITAL_READY 0x0100 +#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000 +#define ADIE_STAGE_ANALOG_OFF 0x0750 +#define ADIE_STAGE_DIGITAL_OFF 0x0600 + +/* path types */ +#define ADIE_PATH_RX 0 +#define ADIE_PATH_TX 1 +#define ADIE_PATH_LOOPBACK 2 + +/* mute states */ +#define ADIE_MUTE_OFF 0 +#define ADIE_MUTE_ON 1 + + +#endif diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_audio.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio.h new file mode 100644 index 00000000000..52de785b011 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio.h @@ -0,0 +1,546 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DAL_AUDIO_H__ +#define __DAL_AUDIO_H__ + +#include "../dal.h" +#include "dal_audio_format.h" + +#define AUDIO_DAL_DEVICE 0x02000028 +#define AUDIO_DAL_PORT "DAL_AQ_AUD" +#define AUDIO_DAL_VERSION 0x00030001 + +enum { + AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API, + AUDIO_OP_DATA, + AUDIO_OP_INIT, +}; + +/* ---- common audio structures ---- */ + +/* This flag, if set, indicates that the beginning of the data in the*/ +/* buffer is a synchronization point or key frame, meaning no data */ +/* before it in the stream is required in order to render the stream */ +/* from this point onward. */ +#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01 + +/* This flag, if set, indicates that the buffer object is using valid */ +/* physical address used to store the media data */ +#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04 + +/* This flag, if set, indicates that a media start timestamp has been */ +/* set for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08 + +/* This flag, if set, indicates that a media stop timestamp has been set */ +/* for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10 + +/* This flag, if set, indicates that a preroll timestamp has been set */ +/* for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20 + +/* This flag, if set, indicates that the data in the buffer is a fragment of */ +/* a larger block of data, and will be continued by the data in the next */ +/* buffer to be delivered. */ +#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40 + +struct adsp_audio_buffer { + u32 addr; /* Physical Address of buffer */ + u32 max_size; /* Maximum size of buffer */ + u32 actual_size; /* Actual size of valid data in the buffer */ + u32 offset; /* Offset to the first valid byte */ + u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */ + s64 start; /* Start timestamp, if any */ + s64 stop; /* Stop timestamp, if any */ + s64 preroll; /* Preroll timestamp, if any */ +} __attribute__ ((packed)); + + + +/* ---- audio commands ---- */ + +/* Command/event response types */ +#define ADSP_AUDIO_RESPONSE_COMMAND 0 +#define ADSP_AUDIO_RESPONSE_ASYNC 1 + +struct adsp_command_hdr { + u32 size; /* sizeof(cmd) - sizeof(u32) */ + + u32 dest; + u32 src; + u32 opcode; + u32 response_type; + u32 seq_number; + + u32 context; /* opaque to DSP */ + u32 data; + u32 padding; +} __attribute__ ((packed)); + + +#define DOMAIN_APP 0 +#define DOMAIN_MODEM 1 +#define DOMAIN_DSP 2 + + +/* adsp audio addresses are (byte order) major, minor, domain */ +#define AUDIO_ADDR(dmn, maj, min) (((maj & 0xff) << 16) \ + | ((min & 0xff) << 24) | (dmn & 0xff)) + +/* AAC Encoder modes */ +#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0 +#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1 +#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2 + +struct adsp_audio_aac_enc_cfg { + u32 bit_rate; /* bits per second */ + u32 encoder_mode; /* ADSP_AUDIO_ENC_* */ +} __attribute__ ((packed)); + +#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0 +#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1 + +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9 + +struct adsp_audio_sbc_encoder_cfg { + u32 num_subbands; + u32 block_len; + u32 channel_mode; + u32 allocation_method; + u32 bit_rate; +} __attribute__ ((packed)); + +/* AMR NB encoder modes */ +#define ADSP_AUDIO_AMR_MR475 0 +#define ADSP_AUDIO_AMR_MR515 1 +#define ADSP_AUDIO_AMR_MMR59 2 +#define ADSP_AUDIO_AMR_MMR67 3 +#define ADSP_AUDIO_AMR_MMR74 4 +#define ADSP_AUDIO_AMR_MMR795 5 +#define ADSP_AUDIO_AMR_MMR102 6 +#define ADSP_AUDIO_AMR_MMR122 7 + +/* The following are valid AMR NB DTX modes */ +#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3 + +/* AMR Encoder configuration */ +struct adsp_audio_amr_enc_cfg { + u32 mode; /* ADSP_AUDIO_AMR_MR* */ + u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */ + u32 enable; /* 1 = enable, 0 = disable */ +} __attribute__ ((packed)); + +struct adsp_audio_qcelp13k_enc_cfg { + u16 min_rate; + u16 max_rate; +} __attribute__ ((packed)); + +struct adsp_audio_evrc_enc_cfg { + u16 min_rate; + u16 max_rate; +} __attribute__ ((packed)); + +union adsp_audio_codec_config { + struct adsp_audio_amr_enc_cfg amr; + struct adsp_audio_aac_enc_cfg aac; + struct adsp_audio_qcelp13k_enc_cfg qcelp13k; + struct adsp_audio_evrc_enc_cfg evrc; + struct adsp_audio_sbc_encoder_cfg sbc; +} __attribute__ ((packed)); + + +/* This is the default value. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000 + +/* This bit, if set, indicates that the AVSync mode is activated. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001 + +/* This bit, if set, indicates that the Sample Rate/Channel Mode */ +/* Change Notification mode is activated. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002 + +#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004 + +#define ADSP_AUDIO_MAX_DEVICES 1 + +struct adsp_open_command { + struct adsp_command_hdr hdr; + u32 device; + u32 end_point; + u32 stream_context; + u32 mode; + u32 buf_max_size; + union adsp_audio_format format_block; + union adsp_audio_codec_config config; + +} __attribute__ ((packed)); + + +/* --- audio control and stream session ioctls ---- */ + +/* Opcode to open a device stream session to capture audio */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79 + +/* Opcode to open a device stream session to render audio */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a + +/* Opcode to open a device session, must open a device */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b + +/* Close an existing stream or device */ +#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc + + + +/* A device switch requires three IOCTL */ +/* commands in the following sequence: PREPARE, STANDBY, COMMIT */ + +/* adsp_audio_device_switch_command structure is needed for */ +/* DEVICE_SWITCH_PREPARE */ + +/* Device switch protocol step #1. Pause old device and */ +/* generate silence for the old device. */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4 + +/* Device switch protocol step #2. Release old device, */ +/* create new device and generate silence for the new device. */ + +/* When client receives ack for this IOCTL, the client can */ +/* start sending IOCTL commands to configure, calibrate and */ +/* change filter settings on the new device. */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5 + +/* Device switch protocol step #3. Start normal operations on new device */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7 + +struct adsp_device_switch_command { + struct adsp_command_hdr hdr; + u32 old_device; + u32 new_device; + u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */ + u8 device_type; /* 0 = rx, 1 = tx, 2 = both */ +} __attribute__ ((packed)); + + + +/* --- audio control session ioctls ---- */ + +#define ADSP_PATH_RX 0 +#define ADSP_PATH_TX 1 +#define ADSP_PATH_BOTH 2 + +/* These commands will affect a logical device and all its associated */ +/* streams. */ + + +/* Set device volume. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c + +struct adsp_set_dev_volume_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 path; /* 0 = rx, 1 = tx, 2 = both */ + s32 volume; +} __attribute__ ((packed)); + +/* Set Device stereo volume. This command has data payload, */ +/* struct adsp_audio_set_dev_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e + +/* Set L, R cross channel gain for a Device. This command has */ +/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40 + +/* Set device mute state. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f + +struct adsp_set_dev_mute_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 path; /* 0 = rx, 1 = tx, 2 = both */ + u32 mute; /* 1 = mute */ +} __attribute__ ((packed)); + +/* Configure Equalizer for a device. */ +/* This command has payload struct adsp_audio_set_dev_equalizer_command. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e + +/* Set configuration data for an algorithm aspect of a device. */ +/* This command has payload struct adsp_audio_set_dev_cfg_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb + +struct adsp_set_dev_cfg_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 block_id; + u32 interface_id; + u32 phys_addr; + u32 phys_size; + u32 phys_used; +} __attribute__ ((packed)); + +/* Set configuration data for all interfaces of a device. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf + +struct adsp_set_dev_cfg_table_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 phys_addr; + u32 phys_size; + u32 phys_used; +} __attribute__ ((packed)); + +/* ---- audio stream data commands ---- */ + +#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f +#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80 + +struct adsp_buffer_command { + struct adsp_command_hdr hdr; + struct adsp_audio_buffer buffer; +} __attribute__ ((packed)); + + + +/* ---- audio stream ioctls (only affect a single stream in a session) ---- */ + +/* Stop stream for audio device. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54 + +/* End of stream reached. Client will not send any more data. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150 + +/* Do sample slipping/stuffing on AAC outputs. The payload of */ +/* this command is struct adsp_audio_slip_sample_command. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e + +/* Set stream volume. */ +/* This command has data payload, struct adsp_audio_set_volume_command. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de + +/* Set stream stereo volume. This command has data payload, */ +/* struct adsp_audio_set_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c + +/* Set L, R cross channel gain for a Stream. This command has */ +/* data payload, struct adsp_audio_set_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d + +/* Set stream mute state. */ +/* This command has data payload, struct adsp_audio_set_stream_mute. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df + +/* Reconfigure bit rate information. This command has data */ +/* payload, struct adsp_audio_set_bit_rate_command */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1 + +/* Set Channel Mapping. This command has data payload, struct */ +/* This command has data payload struct adsp_audio_set_channel_map_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a + +/* Enable/disable AACPlus SBR. */ +/* This command has data payload struct adsp_audio_set_sbr_command */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416 + +/* Enable/disable WMA Pro Chex and Fex. This command has data payload */ +/* struct adsp_audio_stream_set_wma_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417 + + +/* ---- audio session ioctls (affect all streams in a session) --- */ + +/* Start stream for audio device. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6 + +/* Stop all stream(s) for audio session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e + +/* Pause the data flow for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8 + +/* Resume the data flow for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9 + +/* Drop any unprocessed data buffers for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea + +/* Start Stream DTMF tone */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd + +/* Stop Stream DTMF tone */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554 + +/* Set Session volume. */ +/* This command has data payload, struct adsp_audio_set_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd + +/* Set session stereo volume. This command has data payload, */ +/* struct adsp_audio_set_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d + +/* Set L, R cross channel gain for a session. This command has */ +/* data payload, struct adsp_audio_set_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f + +/* Set Session mute state. */ +/* This command has data payload, struct adsp_audio_set_mute_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be + +/* Configure Equalizer for a stream. */ +/* This command has payload struct adsp_audio_set_equalizer_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0 + +/* Set Audio Video sync information. */ +/* This command has data payload, struct adsp_audio_set_av_sync_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2 + +/* Get Audio Media Session time. */ +/* This command returns the audioTime in adsp_audio_unsigned64_event */ +#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c + + +/* these command structures are used for both STREAM and SESSION ioctls */ + +struct adsp_set_volume_command { + struct adsp_command_hdr hdr; + s32 volume; +} __attribute__ ((packed)); + +struct adsp_set_mute_command { + struct adsp_command_hdr hdr; + u32 mute; /* 1 == mute */ +} __attribute__ ((packed)); + + + +/* ---- audio events ---- */ + +/* All IOCTL commands generate an event with the IOCTL opcode as the */ +/* event id after the IOCTL command has been executed. */ + +/* This event is generated after a media stream session is opened. */ +#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6 + +/* This event is generated after a media stream session is closed. */ +#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7 + +/* Asyncronous buffer consumption. This event is generated after a */ +/* recived buffer is consumed during rendering or filled during */ +/* capture opeartion. */ +#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8 + +/* This event is generated when rendering operation is starving for */ +/* data. In order to avoid audio loss at the end of a plauback, the */ +/* client should wait for this event before issuing the close command. */ +#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9 + +/* This event is generated during capture operation when there are no */ +/* buffers available to copy the captured audio data */ +#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da + +/* This asynchronous event is generated as a result of an input */ +/* sample rate change and/or channel mode change detected by the */ +/* decoder. The event payload data is an array of 2 uint32 */ +/* values containing the sample rate in Hz and channel mode. */ +#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329 + +struct adsp_event_hdr { + u32 evt_handle; /* DAL common header */ + u32 evt_cookie; + u32 evt_length; + + u32 dest; + u32 src; + + u32 event_id; + u32 response_type; + u32 seq_number; + + u32 context; /* opaque to DSP */ + u32 data; + + u32 status; +} __attribute__ ((packed)); + +struct adsp_buffer_event { + struct adsp_event_hdr hdr; + struct adsp_audio_buffer buffer; +} __attribute__ ((packed)); + + +/* ---- audio device IDs ---- */ + +/* Device direction Rx/Tx flag */ +#define ADSP_AUDIO_RX_DEVICE 0x00 +#define ADSP_AUDIO_TX_DEVICE 0x01 + +#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679 + +/* Default RX or TX device */ + +#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d +#define ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC 0x108f9c3 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC 0x108f9c5 +#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518 +#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b +#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3 + +/* Special loopback pseudo device to be paired with an RX device */ +/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */ +#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2 + +/* Sink (RX) devices */ +#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509 +#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519 +#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c +#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4 +#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512 + +/* BT A2DP playback device. */ +/* This device must be paired with */ +/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */ +/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */ +#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a + +/* Voice Destination identifier - specifically used for */ +/* controlling Voice module from the Device Control Session */ +#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c + +/* Audio device usage types. */ +/* This is a bit mask to determine which topology to use in the */ +/* device session */ +#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01 +#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02 +#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10 +#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20 +#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40 + +#endif diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_audio_format.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio_format.h new file mode 100644 index 00000000000..348aad163ff --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_audio_format.h @@ -0,0 +1,284 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ADSP_AUDIO_MEDIA_FORMAT_H +#define __ADSP_AUDIO_MEDIA_FORMAT_H + +/* Supported audio media formats */ + +/* format block in shmem */ +#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78 + +/* adsp_audio_format_raw_pcm type */ +#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd + +/* adsp_audio_format_raw_pcm type */ +#define ADSP_AUDIO_FORMAT_DTMF 0x01087725 + +/* adsp_audio_format_adpcm type */ +#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff + +/* Yamaha PCM format */ +#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07 + +/* ISO/IEC 11172 */ +#define ADSP_AUDIO_FORMAT_MP3 0x0103d308 + +/* ISO/IEC 14496 */ +#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1 + +/* AMR-NB audio in FS format */ +#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c + +/* AMR-WB audio in FS format */ +#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e + +/* QCELP 13k, IS733 */ +#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a + +/* EVRC 8k, IS127 */ +#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89 + +/* EVRC-B 8k, 4GV */ +#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3 + +/* MIDI command stream */ +#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300 + +/* A2DP SBC stream */ +#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8 + +/* Version 10 Professional */ +#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92 + +/* Version 9 Starndard */ +#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430 + +/* AMR WideBand Plus */ +#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da + +/* AC3 Decoder */ +#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9 + +/* Not yet supported audio media formats */ + +/* ISO/IEC 13818 */ +#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309 + +/* 3GPP TS 26.101 Sec 4.0 */ +#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305 + +/* 3GPP TS 26.101 Annex A */ +#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31 + +/* 3GPP TS 26.201 */ +#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306 + +/* 3GPP TS 26.201 */ +#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d + +/* G.711 */ +#define ADSP_AUDIO_FORMAT_G711 0x0106201d + +/* QCELP 8k, IS96A */ +#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29 + +/* Version 1 codec */ +#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b + +/* Version 2, 7 & 8 codec */ +#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c + +/* Version 9 Professional codec */ +#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d + +/* Version 9 Voice codec */ +#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e + +/* Version 9 Lossless codec */ +#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f + +/* Real Media content, low-bitrate */ +#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f + +/* Real Media content */ +#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e + + +/* For all of the audio formats, unless specified otherwise, */ +/* the following apply: */ +/* Format block bits are arranged in bytes and words in little-endian */ +/* order, i.e., least-significant bit first and least-significant */ +/* byte first. */ + + +/* AAC Format Block. */ + +/* AAC format block consist of a format identifier followed by */ +/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */ + +/* The following AAC format identifiers are supported */ +#define ADSP_AUDIO_AAC_ADTS 0x010619cf +#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0 +#define ADSP_AUDIO_AAC_LOAS 0x010619d1 +#define ADSP_AUDIO_AAC_ADIF 0x010619d2 +#define ADSP_AUDIO_AAC_RAW 0x010619d3 +#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb + +struct adsp_audio_no_payload_format { + /* Media Format Code (must always be first element) */ + u32 format; + /* no payload for this format type */ +} __attribute__ ((packed)); + +/* Maxmum number of bytes allowed in a format block */ +#define ADSP_AUDIO_FORMAT_DATA_MAX 16 + +/* For convenience, to be used as a standard format block */ +/* for various media types that don't need a unique format block */ +/* ie. PCM, DTMF, etc. */ +struct adsp_audio_standard_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 channels; + u16 bits_per_sample; + u32 sampling_rate; + u8 is_signed; + u8 is_interleaved; +} __attribute__ ((packed)); + +/* ADPCM format block */ +struct adsp_audio_adpcm_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 channels; + u16 bits_per_sample; + u32 sampling_rate; + u8 is_signed; + u8 is_interleaved; + u32 block_size; +} __attribute__ ((packed)); + +/* MIDI format block */ +struct adsp_audio_midi_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 sampling_rate; + u16 channels; + u16 mode; +} __attribute__ ((packed)); + +#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd +#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce + +/* G711 format block */ +struct adsp_audio_g711_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 companding; +} __attribute__ ((packed)); + + +struct adsp_audio_wma_pro_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 format_tag; + u16 channels; + u32 samples_per_sec; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 channel_mask; + u16 encode_opt; + u16 advanced_encode_opt; + u32 advanced_encode_opt2; + u32 drc_peak_reference; + u32 drc_peak_target; + u32 drc_average_reference; + u32 drc_average_target; +} __attribute__ ((packed)); + +struct adsp_audio_amrwb_plus_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 size; + u32 version; + u32 channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_format; + u32 amr_isf_index; +} __attribute__ ((packed)); + +/* Binary Byte Stream Format */ +/* Binary format type that defines a byte stream, */ +/* can be used to specify any format (ie. AAC) */ +struct adsp_audio_binary_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + /* number of bytes set in byte stream */ + u32 num_bytes; + /* Byte stream binary data */ + u8 data[ADSP_AUDIO_FORMAT_DATA_MAX]; +} __attribute__ ((packed)); + +struct adsp_audio_shared_memory_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* Number of bytes in shared memory */ + u32 len; + /* Phyisical address to data in shared memory */ + u32 address; +} __attribute__ ((packed)); + + +/* Union of all format types */ +union adsp_audio_format { + /* Basic format block with no payload */ + struct adsp_audio_no_payload_format no_payload; + /* Generic format block PCM, DTMF */ + struct adsp_audio_standard_format standard; + /* ADPCM format block */ + struct adsp_audio_adpcm_format adpcm; + /* MIDI format block */ + struct adsp_audio_midi_format midi; + /* G711 format block */ + struct adsp_audio_g711_format g711; + /* WmaPro format block */ + struct adsp_audio_wma_pro_format wma_pro; + /* WmaPro format block */ + struct adsp_audio_amrwb_plus_format amrwb_plus; + /* binary (byte stream) format block, used for AAC */ + struct adsp_audio_binary_format binary; + /* format block in shared memory */ + struct adsp_audio_shared_memory_format shared_mem; +}; + +#endif + + diff --git a/arch/arm/mach-msm/qdsp6/audiov2/dal_voice.h b/arch/arm/mach-msm/qdsp6/audiov2/dal_voice.h new file mode 100644 index 00000000000..62c1122b0e1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/dal_voice.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DAL_VOICE_H__ +#define __DAL_VOICE_H__ + +#define VOICE_DAL_DEVICE 0x02000075 +#define VOICE_DAL_PORT "DAL_AM_AUD" +#define VOICE_DAL_VERSION 0x00010000 + +#define APR_PKTV1_TYPE_EVENT_V 0 +#define APR_UNDEFINED -1 +#define APR_PKTV1_TYPE_MASK 0x00000010 +#define APR_PKTV1_TYPE_SHFT 4 + +#define APR_SET_BITMASK(mask, shift, value) \ + (((value) << (shift)) & (mask)) + +#define APR_SET_FIELD(field, value) \ + APR_SET_BITMASK((field##_MASK), (field##_SHFT), (value)) + + +enum { + VOICE_OP_INIT = DAL_OP_FIRST_DEVICE_API, + VOICE_OP_CONTROL, +}; + +struct apr_command_pkt { + uint32_t size; + uint32_t header; + uint16_t reserved1; + uint16_t src_addr; + uint16_t dst_addr; + uint16_t ret_addr; + uint32_t src_token; + uint32_t dst_token; + uint32_t ret_token; + uint32_t context; + uint32_t opcode; +} __attribute__ ((packed)); + + +#define APR_IBASIC_RSP_RESULT 0x00010000 + +#define APR_OP_CMD_CREATE 0x0001001B + +#define APR_OP_CMD_DESTROY 0x0001001C + +#define VOICE_OP_CMD_BRINGUP 0x0001001E + +#define VOICE_OP_CMD_TEARDOWN 0x0001001F + +#define VOICE_OP_CMD_SET_NETWORK 0x0001001D + +#define VOICE_OP_CMD_STREAM_SETUP 0x00010027 + +#define VOICE_OP_CMD_STREAM_TEARDOWN 0x00010028 + +#endif diff --git a/arch/arm/mach-msm/qdsp6/audiov2/evrc_in.c b/arch/arm/mach-msm/qdsp6/audiov2/evrc_in.c new file mode 100644 index 00000000000..88f19b70e5b --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/evrc_in.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "dal_audio.h" +#include "dal_audio_format.h" +#include + + +struct evrc { + struct mutex lock; + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; +}; + + +static long q6_evrc_in_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct evrc *evrc = file->private_data; + struct adsp_open_command rpc; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&evrc->lock); + switch (cmd) { + case AUDIO_START: + if (evrc->audio_client) { + rc = -EBUSY; + break; + } else { + evrc->audio_client = q6audio_open(AUDIO_FLAG_READ, + evrc->str_cfg.buffer_size); + + if (!evrc->audio_client) { + kfree(evrc); + rc = -ENOMEM; + break; + } + } + + tx_clk_freq = 8000; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_EVRC_FS; + rpc.format_block.standard.channels = 1; + rpc.format_block.standard.bits_per_sample = 16; + rpc.format_block.standard.sampling_rate = 8000; + rpc.format_block.standard.is_signed = 1; + rpc.format_block.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = evrc->str_cfg.buffer_size; + rpc.config.evrc.min_rate = evrc->cfg.min_bit_rate; + rpc.config.evrc.max_rate = evrc->cfg.max_bit_rate; + + q6audio_start(evrc->audio_client, &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &evrc->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&evrc->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + + if (evrc->str_cfg.buffer_size < 23) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + + if (evrc->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + break; + case AUDIO_SET_EVRC_ENC_CONFIG: + if (copy_from_user(&evrc->cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + + if (evrc->cfg.min_bit_rate > 4 || evrc->cfg.min_bit_rate < 1) { + pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + if (evrc->cfg.max_bit_rate > 4 || evrc->cfg.max_bit_rate < 1) { + pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + case AUDIO_GET_EVRC_ENC_CONFIG: + if (copy_to_user((void *) arg, &evrc->cfg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&evrc->lock); + return rc; +} + +static int q6_evrc_in_open(struct inode *inode, struct file *file) +{ + struct evrc *evrc; + evrc = kmalloc(sizeof(struct evrc), GFP_KERNEL); + if (evrc == NULL) { + pr_err("[%s:%s] Could not allocate memory for evrc driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&evrc->lock); + file->private_data = evrc; + evrc->audio_client = NULL; + evrc->str_cfg.buffer_size = 23; + evrc->str_cfg.buffer_count = 2; + evrc->cfg.cdma_rate = CDMA_RATE_FULL; + evrc->cfg.min_bit_rate = 1; + evrc->cfg.max_bit_rate = 4; + + return 0; +} + +static ssize_t q6_evrc_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + struct evrc *evrc = file->private_data; + int xfer = 0; + int res; + + mutex_lock(&evrc->lock); + ac = evrc->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > xfer) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = ab->actual_size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + res = buf - start; + +fail: + mutex_unlock(&evrc->lock); + + return res; +} + +static int q6_evrc_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct evrc *evrc = file->private_data; + + mutex_lock(&evrc->lock); + if (evrc->audio_client) + rc = q6audio_close(evrc->audio_client); + mutex_unlock(&evrc->lock); + kfree(evrc); + return rc; +} + +static const struct file_operations q6_evrc_in_fops = { + .owner = THIS_MODULE, + .open = q6_evrc_in_open, + .read = q6_evrc_in_read, + .release = q6_evrc_in_release, + .unlocked_ioctl = q6_evrc_in_ioctl, +}; + +struct miscdevice q6_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &q6_evrc_in_fops, +}; + +static int __init q6_evrc_in_init(void) +{ + return misc_register(&q6_evrc_in_misc); +} + +device_initcall(q6_evrc_in_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/mp3.c b/arch/arm/mach-msm/qdsp6/audiov2/mp3.c new file mode 100644 index 00000000000..0781eda6efd --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/mp3.c @@ -0,0 +1,205 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/mp3.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "dal_audio.h" +#include "dal_audio_format.h" + +#define BUFSZ (8192) +#define DMASZ (BUFSZ * 2) + +struct mp3 { + struct mutex lock; + struct audio_client *ac; + struct msm_audio_config cfg; +}; + +static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct mp3 *mp3 = file->private_data; + struct adsp_open_command rpc; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&mp3->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: + break; + case AUDIO_START: + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_MP3; + rpc.format_block.standard.channels = mp3->cfg.channel_count; + rpc.format_block.standard.bits_per_sample = 16; + rpc.format_block.standard.sampling_rate = mp3->cfg.sample_rate; + rpc.format_block.standard.is_signed = 1; + rpc.format_block.standard.is_interleaved = 0; + rpc.buf_max_size = BUFSZ; + q6audio_start(mp3->ac, (void *) &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: + if (copy_from_user(&mp3->cfg, (void *) arg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + break; + } + if (mp3->cfg.channel_count < 1 || mp3->cfg.channel_count > 2) { + rc = -EINVAL; + break; + } + break; + case AUDIO_GET_CONFIG: + if (copy_to_user((void *) arg, &mp3->cfg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + } + break; + default: + rc = -EINVAL; + } + mutex_unlock(&mp3->lock); + return rc; +} + +static int mp3_open(struct inode *inode, struct file *file) +{ + + struct mp3 *mp3; + mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL); + + if (!mp3) + return -ENOMEM; + + mutex_init(&mp3->lock); + file->private_data = mp3; + mp3->ac = q6audio_open(AUDIO_FLAG_WRITE, BUFSZ); + if (!mp3->ac) { + kfree(mp3); + return -ENOMEM; + } + mp3->cfg.channel_count = 2; + mp3->cfg.buffer_count = 2; + mp3->cfg.buffer_size = BUFSZ; + mp3->cfg.unused[0] = 0; + mp3->cfg.unused[1] = 0; + mp3->cfg.unused[2] = 0; + mp3->cfg.sample_rate = 48000; + + return 0; +} + +static ssize_t mp3_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct mp3 *mp3 = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + if (!mp3->ac) + mp3_ioctl(file, AUDIO_START, 0); + + ac = mp3->ac; + if (!ac) + return -ENODEV; + + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = xfer; + q6audio_write(ac, ab); + ac->cpu_buf ^= 1; + } + + return buf - start; +} + +static int mp3_fsync(struct file *f, int datasync) +{ + struct mp3 *mp3 = f->private_data; + if (mp3->ac) + return q6audio_async(mp3->ac); + return -ENODEV; +} + +static int mp3_release(struct inode *inode, struct file *file) +{ + struct mp3 *mp3 = file->private_data; + if (mp3->ac) + q6audio_close(mp3->ac); + kfree(mp3); + return 0; +} + +static const struct file_operations mp3_fops = { + .owner = THIS_MODULE, + .open = mp3_open, + .write = mp3_write, + .fsync = mp3_fsync, + .release = mp3_release, + .unlocked_ioctl = mp3_ioctl, +}; + +struct miscdevice mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &mp3_fops, +}; + +static int __init mp3_init(void) +{ + return misc_register(&mp3_misc); +} + +device_initcall(mp3_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c b/arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c new file mode 100644 index 00000000000..6ef21952f25 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c @@ -0,0 +1,208 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/pcm_in.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "dal_audio.h" +#include "dal_audio_format.h" + +#define BUFSZ (4096) +#define DMASZ (BUFSZ * 2) + + +struct pcm { + struct mutex lock; + struct msm_audio_config cfg; + struct audio_client *audio_client; +}; + +static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + struct adsp_open_command rpc; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&pcm->lock); + switch (cmd) { + + case AUDIO_START: + tx_clk_freq = pcm->cfg.sample_rate; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format_block.standard.channels = pcm->cfg.channel_count; + rpc.format_block.standard.bits_per_sample = 16; + rpc.format_block.standard.sampling_rate = pcm->cfg.sample_rate; + rpc.format_block.standard.is_signed = 1; + rpc.format_block.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = BUFSZ; + q6audio_start(pcm->audio_client, &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_VOLUME: + break; + case AUDIO_SET_CONFIG: + if (copy_from_user(&pcm->cfg, (void *) arg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + break; + } + break; + case AUDIO_GET_CONFIG: + if (copy_to_user((void *) arg, &pcm->cfg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + } + break; + default: + rc = -EINVAL; + } + + mutex_unlock(&pcm->lock); + return rc; +} + +static int q6_in_open(struct inode *inode, struct file *file) +{ + + struct pcm *pcm; + pcm = kmalloc(sizeof(struct pcm), GFP_KERNEL); + if (pcm == NULL) { + pr_err("Could not allocate memory for pcm driver\n"); + return -ENOMEM; + } + mutex_init(&pcm->lock); + file->private_data = pcm; + pcm->audio_client = q6audio_open(AUDIO_FLAG_READ, BUFSZ); + if (!pcm->audio_client) { + kfree(pcm); + return -ENOMEM; + } + pcm->cfg.channel_count = 1; + pcm->cfg.buffer_count = 2; + pcm->cfg.buffer_size = BUFSZ; + pcm->cfg.unused[0] = 0; + pcm->cfg.unused[1] = 0; + pcm->cfg.unused[2] = 0; + pcm->cfg.sample_rate = 8000; + + return 0; +} + +static ssize_t q6_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + struct pcm *pcm = file->private_data; + int xfer; + int res; + + mutex_lock(&pcm->lock); + ac = pcm->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } +fail: + res = buf - start; + mutex_unlock(&pcm->lock); + + return res; +} + +static int q6_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct pcm *pcm = file->private_data; + + mutex_lock(&pcm->lock); + if (pcm->audio_client) + rc = q6audio_close(pcm->audio_client); + mutex_unlock(&pcm->lock); + kfree(pcm); + return rc; +} + +static const struct file_operations q6_in_fops = { + .owner = THIS_MODULE, + .open = q6_in_open, + .read = q6_in_read, + .release = q6_in_release, + .unlocked_ioctl = q6_in_ioctl, +}; + +struct miscdevice q6_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &q6_in_fops, +}; + +static int __init q6_in_init(void) +{ + return misc_register(&q6_in_misc); +} + +device_initcall(q6_in_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c b/arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c new file mode 100644 index 00000000000..6743c6c7741 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c @@ -0,0 +1,196 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/pcm_out.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "dal_audio.h" +#include "dal_audio_format.h" + +#define BUFSZ (8192) +#define DMASZ (BUFSZ * 2) + +struct pcm { + struct mutex lock; + struct audio_client *ac; + struct msm_audio_config cfg; + +}; + +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + struct adsp_open_command rpc; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&pcm->lock); + switch (cmd) { + case AUDIO_START: + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format_block.standard.channels = pcm->cfg.channel_count; + rpc.format_block.standard.bits_per_sample = 16; + rpc.format_block.standard.sampling_rate = pcm->cfg.sample_rate; + rpc.format_block.standard.is_signed = 1; + rpc.format_block.standard.is_interleaved = 1; + rpc.buf_max_size = BUFSZ; + q6audio_start(pcm->ac, (void *) &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: + if (copy_from_user(&pcm->cfg, (void *) arg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + break; + } + if (pcm->cfg.channel_count < 1 || pcm->cfg.channel_count > 2) { + rc = -EINVAL; + break; + } + + break; + case AUDIO_GET_CONFIG: + if (copy_to_user((void *) arg, &pcm->cfg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + } + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&pcm->lock); + return rc; +} + +static int pcm_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + + if (!pcm) + return -ENOMEM; + + mutex_init(&pcm->lock); + file->private_data = pcm; + pcm->ac = q6audio_open(AUDIO_FLAG_WRITE, BUFSZ); + if (!pcm->ac) { + kfree(pcm); + return -ENOMEM; + } + pcm->cfg.channel_count = 2; + pcm->cfg.buffer_count = 2; + pcm->cfg.buffer_size = BUFSZ; + pcm->cfg.unused[0] = 0; + pcm->cfg.unused[1] = 0; + pcm->cfg.unused[2] = 0; + pcm->cfg.sample_rate = 48000; + + return 0; +} + +static ssize_t pcm_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + ac = pcm->ac; + if (!ac) + return -ENODEV; + + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = 1; + ab->actual_size = xfer; + q6audio_write(ac, ab); + ac->cpu_buf ^= 1; + } + + return buf - start; +} + +static int pcm_release(struct inode *inode, struct file *file) +{ + struct pcm *pcm = file->private_data; + if (pcm->ac) + q6audio_close(pcm->ac); + kfree(pcm); + return 0; +} + +static const struct file_operations pcm_fops = { + .owner = THIS_MODULE, + .open = pcm_open, + .write = pcm_write, + .release = pcm_release, + .unlocked_ioctl = pcm_ioctl, +}; + +struct miscdevice pcm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &pcm_fops, +}; + +static int __init pcm_init(void) +{ + return misc_register(&pcm_misc); +} + +device_initcall(pcm_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/q6audio.c b/arch/arm/mach-msm/qdsp6/audiov2/q6audio.c new file mode 100644 index 00000000000..af411f14365 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/q6audio.c @@ -0,0 +1,1316 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/q6audio.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../dal.h" +#include "dal_audio.h" +#include "dal_audio_format.h" +#include "dal_acdb.h" +#include "dal_adie.h" +#include "q6audio_devices.h" + +struct q6_hw_info { + int min_gain; + int max_gain; +}; + +/* TODO: provide mechanism to configure from board file */ + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_HEADSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1500, + .max_gain = 0, + }, + [Q6_HW_TTY] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 0, + }, +}; + +static struct pm_qos_request pm_qos_req; +static int idlecount; +static DEFINE_MUTEX(idlecount_lock); + +void audio_prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) + pm_qos_update_request(&pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + mutex_unlock(&idlecount_lock); +} + +void audio_allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) + pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE); + mutex_unlock(&idlecount_lock); +} + +static struct clk *icodec_rx_clk; +static struct clk *icodec_tx_clk; +static struct clk *ecodec_clk; +static struct clk *sdac_clk; + +static struct q6audio_analog_ops default_analog_ops; +static struct q6audio_analog_ops *analog_ops = &default_analog_ops; +uint32_t tx_clk_freq = 8000; +static int tx_mute_status; + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) +{ + analog_ops = ops; +} + +static struct q6_device_info *q6_lookup_device(uint32_t device_id) +{ + struct q6_device_info *di = q6_audio_devices; + for (;;) { + if (di->id == device_id) + return di; + if (di->id == 0) { + pr_err("q6_lookup_device: bogus id 0x%08x\n", + device_id); + return di; + } + di++; + } +} + +static uint32_t q6_device_to_codec(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->codec; +} + +static uint32_t q6_device_to_dir(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->dir; +} + +static uint32_t q6_device_to_cad_id(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->cad_id; +} + +static uint32_t q6_device_to_path(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->path; +} + +static uint32_t q6_device_to_rate(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->rate; +} + +int q6_device_volume(uint32_t device_id, int level) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + struct q6_hw_info *hw; + + hw = &q6_audio_hw[di->hw]; + + return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100; +} + +static inline int adie_open(struct dal_client *client) +{ + return dal_call_f0(client, DAL_OP_OPEN, 0); +} + +static inline int adie_close(struct dal_client *client) +{ + return dal_call_f0(client, DAL_OP_CLOSE, 0); +} + +static inline int adie_set_path(struct dal_client *client, + uint32_t *adie_params, uint32_t size) +{ + uint32_t tmp; + return dal_call(client, ADIE_OP_SET_PATH, 5, adie_params, size, + (void *)&tmp, sizeof(uint32_t)); + +} + +static inline int adie_proceed_to_stage(struct dal_client *client, + uint32_t path_type, uint32_t stage) +{ + return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE, + path_type, stage); +} + +static int adie_refcount; + +static struct dal_client *adie; +static struct dal_client *adsp; +static struct dal_client *acdb; + +static int adie_enable(void) +{ + adie_refcount++; + if (adie_refcount == 1) + adie_open(adie); + return 0; +} + +static int adie_disable(void) +{ + adie_refcount--; + if (adie_refcount == 0) + adie_close(adie); + return 0; +} + +/* 4k DMA scratch page used for exchanging acdb device config tables + * and stream format descriptions with the DSP. + */ +char *audio_data; +int32_t audio_phys; + +#define SESSION_MIN 0 +#define SESSION_MAX 64 + +static DEFINE_MUTEX(session_lock); +static DEFINE_MUTEX(audio_lock); + +static struct audio_client *session[SESSION_MAX]; + +static int session_alloc(struct audio_client *ac) +{ + int n; + + mutex_lock(&session_lock); + for (n = SESSION_MIN; n < SESSION_MAX; n++) { + if (!session[n]) { + session[n] = ac; + mutex_unlock(&session_lock); + return n; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void session_free(int n, struct audio_client *ac) +{ + mutex_lock(&session_lock); + if (session[n] == ac) + session[n] = 0; + mutex_unlock(&session_lock); +} + +static void audio_client_free(struct audio_client *ac) +{ + session_free(ac->session, ac); + + if (ac->buf[0].data) + pmem_kfree(ac->buf[0].phys); + if (ac->buf[1].data) + pmem_kfree(ac->buf[1].phys); + kfree(ac); +} + +static struct audio_client *audio_client_alloc(unsigned bufsz) +{ + struct audio_client *ac; + int n; + + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + if (!ac) + return 0; + + n = session_alloc(ac); + if (n < 0) + goto fail_session; + ac->session = n; + + if (bufsz > 0) { + ac->buf[0].phys = pmem_kalloc(bufsz, + PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K); + ac->buf[0].data = ioremap(ac->buf[0].phys, bufsz); + if (!ac->buf[0].data) + goto fail; + + ac->buf[1].phys = pmem_kalloc(bufsz, + PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K); + ac->buf[1].data = ioremap(ac->buf[1].phys, bufsz); + if (!ac->buf[1].data) + goto fail; + + ac->buf[0].size = bufsz; + ac->buf[1].size = bufsz; + } + + init_waitqueue_head(&ac->wait); + ac->client = adsp; + + return ac; + +fail: + pr_err("pmem_kalloc failed\n"); + session_free(n, ac); +fail_session: + audio_client_free(ac); + return 0; +} + +static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) +{ + struct adsp_command_hdr *hdr = ptr; + uint32_t tmp; + int r; + + hdr->size = len - sizeof(u32); + hdr->dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + hdr->src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + hdr->context = ac->session; + ac->cb_status = -EBUSY; + r = dal_call(ac->client, AUDIO_OP_CONTROL, 5, ptr, len, + &tmp, sizeof(tmp)); + if (r != 4) + return -EIO; + wait_event(ac->wait, (ac->cb_status != -EBUSY)); + return tmp; +} + +static int audio_command(struct audio_client *ac, uint32_t cmd) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); + rpc.opcode = cmd; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_open_control(struct audio_client *ac) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + + +static int audio_close(struct audio_client *ac) +{ + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE); + return 0; +} + +static int audio_set_table(struct audio_client *ac, + uint32_t device_id, int size) +{ + struct adsp_set_dev_cfg_table_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.device_id = device_id; + rpc.phys_addr = audio_phys; + rpc.phys_size = size; + rpc.phys_used = size; + + if (q6_device_to_dir(device_id) == Q6_TX) + rpc.hdr.data = tx_clk_freq; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + uint32_t res; + int r; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.size = sizeof(rpc) - sizeof(u32); + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.hdr.context = ac->session; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_TX; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->actual_size; + + r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), + &res, sizeof(res)); + + if ((r == sizeof(res))) + return 0; + + return -EIO; + +} + +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + uint32_t res; + int r; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.size = sizeof(rpc) - sizeof(u32); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.context = ac->session; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_RX; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->actual_size; + + r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), + &res, sizeof(res)); + return 0; +} + +static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, + int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.volume = volume; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.mute = !!mute; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_tx_volume(struct audio_client *ac, uint32_t dev_id, + int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + rpc.volume = volume; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + rpc.mute = !!mute; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static void callback(void *data, int len, void *cookie) +{ + struct adsp_event_hdr *e = data; + struct audio_client *ac; + struct adsp_buffer_event *abe = data; + + if (e->context >= SESSION_MAX) { + pr_err("audio callback: bogus session %d\n", + e->context); + return; + } + ac = session[e->context]; + if (!ac) { + pr_err("audio callback: unknown session %d\n", + e->context); + return; + } + + if (e->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) { + pr_info("playback done\n"); + if (e->status) + pr_err("playback status %d\n", e->status); + if (ac->cb_status == -EBUSY) { + ac->cb_status = e->status; + wake_up(&ac->wait); + } + return; + } + + if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { + if (e->status) + pr_err("buffer status %d\n", e->status); + + ac->buf[ac->dsp_buf].actual_size = abe->buffer.actual_size; + ac->buf[ac->dsp_buf].used = 0; + ac->dsp_buf ^= 1; + wake_up(&ac->wait); + return; + } + + if (e->status) + pr_warning("audio_cb: s=%d e=%08x status=%d\n", + e->context, e->event_id, e->status); + + if (ac->cb_status == -EBUSY) { + ac->cb_status = e->status; + wake_up(&ac->wait); + } +} + +static void audio_init(struct dal_client *client) +{ + u32 tmp[3]; + + tmp[0] = 2 * sizeof(u32); + tmp[1] = 0; + tmp[2] = 0; + dal_call(client, AUDIO_OP_INIT, 5, tmp, sizeof(tmp), + tmp, sizeof(u32)); +} + +static struct audio_client *ac_control; + +static int q6audio_init(void) +{ + struct audio_client *ac = 0; + int res = -ENODEV; + + mutex_lock(&audio_lock); + if (ac_control) { + res = 0; + goto done; + } + + icodec_rx_clk = clk_get(0, "icodec_rx_clk"); + icodec_tx_clk = clk_get(0, "icodec_tx_clk"); + ecodec_clk = clk_get(0, "ecodec_clk"); + sdac_clk = clk_get(0, "sdac_clk"); + + tx_mute_status = 0; + audio_phys = pmem_kalloc(4096, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K); + audio_data = ioremap(audio_phys, 4096); + if (!audio_data) { + pr_err("pmem kalloc failed\n"); + res = -ENOMEM; + goto done; + } + + adsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, 1, + callback, 0); + if (!adsp) { + pr_err("audio_init: cannot attach to adsp\n"); + res = -ENODEV; + goto done; + } + if (check_version(adsp, AUDIO_DAL_VERSION) != 0) { + pr_err("Incompatible adsp version\n"); + res = -ENODEV; + goto done; + } + + audio_init(adsp); + + ac = audio_client_alloc(0); + if (!ac) { + pr_err("audio_init: cannot allocate client\n"); + res = -ENOMEM; + goto done; + } + + if (audio_open_control(ac)) { + pr_err("audio_init: cannot open control channel\n"); + res = -ENODEV; + goto done; + } + + acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0, 0); + if (!acdb) { + pr_err("audio_init: cannot attach to acdb channel\n"); + res = -ENODEV; + goto done; + } + if (check_version(acdb, ACDB_DAL_VERSION) != 0) { + pr_err("Incompatablie acdb version\n"); + res = -ENODEV; + goto done; + } + + + adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0, 0); + if (!adie) { + pr_err("audio_init: cannot attach to adie\n"); + res = -ENODEV; + goto done; + } + if (check_version(adie, ADIE_DAL_VERSION) != 0) { + pr_err("Incompatablie adie version\n"); + res = -ENODEV; + goto done; + } + if (analog_ops->init) + analog_ops->init(); + + res = 0; + ac_control = ac; + + pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); +done: + if ((res < 0) && ac) + audio_client_free(ac); + mutex_unlock(&audio_lock); + + return res; +} + +static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate) +{ + struct acdb_cmd_device_table rpc; + struct acdb_result res; + int r; + + if (q6audio_init()) + return 0; + + memset(audio_data, 0, 4096); + memset(&rpc, 0, sizeof(rpc)); + + rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t)); + rpc.command_id = ACDB_GET_DEVICE_TABLE; + rpc.device_id = q6_device_to_cad_id(device_id); + rpc.network_id = 0x00010023; + rpc.sample_rate_id = sample_rate; + rpc.total_bytes = 4096; + rpc.unmapped_buf = audio_phys; + rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t)); + + r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc), + &res, sizeof(res)); + + if ((r == sizeof(res)) && (res.dal_status == 0)) + return res.used_bytes; + + return -EIO; +} + +static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX; +static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; +static uint32_t audio_rx_device_group = -1; +static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX; +static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; +static uint32_t audio_tx_device_group = -1; + +static int qdsp6_devchg_notify(struct audio_client *ac, + uint32_t dev_type, uint32_t dev_id) +{ + struct adsp_device_switch_command rpc; + + if (dev_type != ADSP_AUDIO_RX_DEVICE && + dev_type != ADSP_AUDIO_TX_DEVICE) + return -EINVAL; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE; + rpc.hdr.dest = AUDIO_ADDR(DOMAIN_DSP, ac->session, 0); + rpc.hdr.src = AUDIO_ADDR(DOMAIN_APP, ac->session, 0); + + if (dev_type == ADSP_AUDIO_RX_DEVICE) { + rpc.old_device = audio_rx_device_id; + rpc.new_device = dev_id; + } else { + rpc.old_device = audio_tx_device_id; + rpc.new_device = dev_id; + } + rpc.device_class = 0; + rpc.device_type = dev_type; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int qdsp6_standby(struct audio_client *ac) +{ + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY); +} + +static int qdsp6_start(struct audio_client *ac) +{ + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT); +} + +static void audio_rx_analog_enable(int en) +{ + switch (audio_rx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO: + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO: + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + } +} + +static void audio_tx_analog_enable(int en) +{ + switch (audio_tx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC: + if (analog_ops->int_mic_enable) + analog_ops->int_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC: + if (analog_ops->ext_mic_enable) + analog_ops->ext_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + } +} + +static void _audio_rx_path_enable(void) +{ + uint32_t adev, sample_rate; + int sz; + uint32_t adie_params[5]; + + adev = audio_rx_device_id; + sample_rate = q6_device_to_rate(adev); + + sz = acdb_get_config_table(adev, sample_rate); + audio_set_table(ac_control, adev, sz); + + adie_params[0] = 4*sizeof(uint32_t); + adie_params[1] = audio_rx_path_id; + adie_params[2] = ADIE_PATH_RX; + adie_params[3] = 48000; + adie_params[4] = 256; + /*check for errors here*/ + if (!adie_set_path(adie, adie_params, sizeof(adie_params))) + pr_err("adie set rx path failed\n"); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_rx_analog_enable(1); + + audio_rx_mute(ac_control, adev, 0); + + audio_rx_volume(ac_control, adev, q6_device_volume(adev, 100)); +} + +static void _audio_tx_path_enable(void) +{ + uint32_t adev; + int sz; + uint32_t adie_params[5]; + + adev = audio_tx_device_id; + + pr_info("audiolib: load %08x cfg table\n", adev); + + if (tx_clk_freq > 16000) { + adie_params[3] = 48000; + sz = acdb_get_config_table(adev, 48000); + + } else if (tx_clk_freq > 8000) { + adie_params[3] = 16000; + sz = acdb_get_config_table(adev, 16000); + } else { + + adie_params[3] = 8000; + sz = acdb_get_config_table(adev, 8000); + } + + pr_info("cfg table is %d bytes\n", sz); + audio_set_table(ac_control, adev, sz); + + pr_info("audiolib: set adie tx path\n"); + + adie_params[0] = 4*sizeof(uint32_t); + adie_params[1] = audio_tx_path_id; + adie_params[2] = ADIE_PATH_TX; + adie_params[4] = 256; + + if (!adie_set_path(adie, adie_params, sizeof(adie_params))) + pr_err("adie set tx path failed\n"); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, + ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_TX, + ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_tx_analog_enable(1); + audio_tx_mute(ac_control, adev, tx_mute_status); + + if (!tx_mute_status) + audio_tx_volume(ac_control, adev, q6_device_volume(adev, 100)); +} + +static void _audio_rx_path_disable(void) +{ + audio_rx_analog_enable(0); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_OFF); +} + +static void _audio_tx_path_disable(void) +{ + audio_tx_analog_enable(0); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_OFF); +} + +static int icodec_rx_clk_refcount; +static int icodec_tx_clk_refcount; +static int ecodec_clk_refcount; +static int sdac_clk_refcount; + +static void _audio_rx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_rx_device_id); + + switch (device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount++; + if (icodec_rx_clk_refcount == 1) { + clk_set_rate(icodec_rx_clk, 12288000); + clk_enable(icodec_rx_clk); + } + break; + case Q6_ECODEC_RX: + ecodec_clk_refcount++; + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + } + break; + case Q6_SDAC_RX: + sdac_clk_refcount++; + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + } + break; + default: + return; + } + audio_rx_device_group = device_group; +} + +static void _audio_tx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_tx_device_id); + + switch (device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount++; + if (icodec_tx_clk_refcount == 1) { + clk_set_rate(icodec_tx_clk, tx_clk_freq * 256); + clk_enable(icodec_tx_clk); + } + break; + case Q6_ECODEC_TX: + ecodec_clk_refcount++; + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + } + break; + case Q6_SDAC_TX: + /* TODO: In QCT BSP, clk rate was set to 20480000 */ + sdac_clk_refcount++; + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + } + break; + default: + return; + } + audio_tx_device_group = device_group; +} + +static void _audio_rx_clk_disable(void) +{ + switch (audio_rx_device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount--; + if (icodec_rx_clk_refcount == 0) { + clk_disable(icodec_rx_clk); + audio_rx_device_group = -1; + } + break; + case Q6_ECODEC_RX: + ecodec_clk_refcount--; + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + audio_rx_device_group = -1; + } + break; + case Q6_SDAC_RX: + sdac_clk_refcount--; + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_rx_device_group = -1; + } + break; + default: + pr_err("audiolib: invalid rx device group %d\n", + audio_rx_device_group); + break; + } +} + +static void _audio_tx_clk_disable(void) +{ + switch (audio_tx_device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount--; + if (icodec_tx_clk_refcount == 0) { + clk_disable(icodec_tx_clk); + audio_tx_device_group = -1; + } + break; + case Q6_ECODEC_TX: + ecodec_clk_refcount--; + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + audio_tx_device_group = -1; + } + break; + case Q6_SDAC_TX: + sdac_clk_refcount--; + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_tx_device_group = -1; + } + break; + default: + pr_err("audiolib: invalid tx device group %d\n", + audio_tx_device_group); + break; + } +} + +static void _audio_rx_clk_reinit(uint32_t rx_device) +{ + uint32_t device_group = q6_device_to_codec(rx_device); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_disable(); + + audio_rx_device_id = rx_device; + audio_rx_path_id = q6_device_to_path(rx_device); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_enable(); + +} + +static void _audio_tx_clk_reinit(uint32_t tx_device) +{ + uint32_t device_group = q6_device_to_codec(tx_device); + + if (device_group != audio_tx_device_group) + _audio_tx_clk_disable(); + + audio_tx_device_id = tx_device; + audio_tx_path_id = q6_device_to_path(tx_device); + + if (device_group != audio_tx_device_group) + _audio_tx_clk_enable(); +} + +static DEFINE_MUTEX(audio_path_lock); +static int audio_rx_path_refcount; +static int audio_tx_path_refcount; + +static int audio_rx_path_enable(int en) +{ + mutex_lock(&audio_path_lock); + if (en) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + adie_enable(); + _audio_rx_clk_enable(); + _audio_rx_path_enable(); + } + } else { + audio_rx_path_refcount--; + if (audio_rx_path_refcount == 0) { + _audio_rx_path_disable(); + _audio_rx_clk_disable(); + adie_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +static int audio_tx_path_enable(int en) +{ + mutex_lock(&audio_path_lock); + if (en) { + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + adie_enable(); + _audio_tx_clk_enable(); + _audio_tx_path_enable(); + } + } else { + audio_tx_path_refcount--; + if (audio_tx_path_refcount == 0) { + _audio_tx_path_disable(); + _audio_tx_clk_disable(); + adie_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) +{ + mutex_lock(&audio_path_lock); + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_tx_mute(int mute) +{ + uint32_t adev; + int rc; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + if (mute == tx_mute_status) { + mutex_unlock(&audio_path_lock); + return 0; + } + + adev = audio_tx_device_id; + rc = audio_tx_mute(ac_control, adev, mute); + if (!rc) + tx_mute_status = mute; + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_rx_volume(int level) +{ + uint32_t adev; + int vol; + + if (q6audio_init()) + return 0; + + if (level < 0 || level > 100) + return -EINVAL; + + mutex_lock(&audio_path_lock); + adev = audio_rx_device_id; + vol = q6_device_volume(adev, level); + audio_rx_mute(ac_control, adev, 0); + audio_rx_volume(ac_control, adev, vol); + mutex_unlock(&audio_path_lock); + return 0; +} + +static void do_rx_routing(uint32_t device_id) +{ + int sz; + uint32_t sample_rate; + + if (device_id == audio_rx_device_id) + return; + + if (audio_rx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, + device_id); + _audio_rx_path_disable(); + _audio_rx_clk_reinit(device_id); + _audio_rx_path_enable(); + } else { + sample_rate = q6_device_to_rate(device_id); + sz = acdb_get_config_table(device_id, sample_rate); + if (sz < 0) + pr_err("could not get ACDB config table\n"); + + audio_set_table(ac_control, device_id, sz); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, + device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + audio_rx_device_id = device_id; + audio_rx_path_id = q6_device_to_path(device_id); + } +} + +static void do_tx_routing(uint32_t device_id) +{ + int sz; + uint32_t sample_rate; + + if (device_id == audio_tx_device_id) + return; + + if (audio_tx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + device_id); + _audio_tx_path_disable(); + _audio_tx_clk_reinit(device_id); + _audio_tx_path_enable(); + } else { + sample_rate = q6_device_to_rate(device_id); + sz = acdb_get_config_table(device_id, sample_rate); + audio_set_table(ac_control, device_id, sz); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + audio_tx_device_id = device_id; + audio_tx_path_id = q6_device_to_path(device_id); + } +} + +int q6audio_do_routing(uint32_t device_id) +{ + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + switch (q6_device_to_dir(device_id)) { + case Q6_RX: + do_rx_routing(device_id); + break; + case Q6_TX: + do_tx_routing(device_id); + break; + } + + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_route(const char *name) +{ + uint32_t route; + if (!strcmp(name, "speaker")) + route = ADIE_PATH_SPEAKER_STEREO_RX; + else if (!strcmp(name, "headphones")) + route = ADIE_PATH_HEADSET_STEREO_RX; + else if (!strcmp(name, "handset")) + route = ADIE_PATH_HANDSET_RX; + else + return -EINVAL; + + mutex_lock(&audio_path_lock); + if (route == audio_rx_path_id) + goto done; + + audio_rx_path_id = route; + + if (audio_rx_path_refcount > 0) { + _audio_rx_path_disable(); + _audio_rx_path_enable(); + } + if (audio_tx_path_refcount > 0) { + _audio_tx_path_disable(); + _audio_tx_path_enable(); + } +done: + mutex_unlock(&audio_path_lock); + return 0; +} + +struct audio_client *q6audio_open(uint32_t flags, uint32_t bufsz) +{ + struct audio_client *ac; + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1); + else + audio_tx_path_enable(1); + + return ac; +} + +int q6audio_start(struct audio_client *ac, void *rpc, + uint32_t len) +{ + + audio_ioctl(ac, rpc, len); + + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + + audio_prevent_sleep(); + return 0; +} + +int q6audio_close(struct audio_client *ac) +{ + audio_close(ac); + + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0); + else + audio_tx_path_enable(0); + + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + +struct audio_client *q6voice_open(void) +{ + struct audio_client *ac; + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(0); + if (!ac) + return 0; + + return ac; +} + +int q6voice_setup(void) +{ + audio_rx_path_enable(1); + tx_clk_freq = 8000; + audio_tx_path_enable(1); + + return 0; +} + +int q6voice_teardown(void) +{ + audio_rx_path_enable(0); + audio_tx_path_enable(0); + return 0; +} + + +int q6voice_close(struct audio_client *ac) +{ + audio_client_free(ac); + return 0; +} + +int q6audio_async(struct audio_client *ac) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); + rpc.opcode = ADSP_AUDIO_IOCTL_CMD_STREAM_EOS; + rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} diff --git a/arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h b/arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h new file mode 100644 index 00000000000..aa8a6990393 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h @@ -0,0 +1,276 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/q6audio_devices.h + * + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +struct q6_device_info { + uint32_t id; + uint32_t cad_id; + uint32_t path; + uint32_t rate; + uint8_t dir; + uint8_t codec; + uint8_t hw; +}; + +#define Q6_ICODEC_RX 0 +#define Q6_ICODEC_TX 1 +#define Q6_ECODEC_RX 2 +#define Q6_ECODEC_TX 3 +#define Q6_SDAC_RX 6 +#define Q6_SDAC_TX 7 +#define Q6_CODEC_NONE 255 + +#define Q6_TX 1 +#define Q6_RX 2 +#define Q6_TX_RX 3 + +#define Q6_HW_HANDSET 0 +#define Q6_HW_HEADSET 1 +#define Q6_HW_SPEAKER 2 +#define Q6_HW_TTY 3 +#define Q6_HW_BT_SCO 4 +#define Q6_HW_BT_A2DP 5 + +#define Q6_HW_COUNT 6 + +#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01 +#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02 +#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08 +#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09 +#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A +#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B +#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C +#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D + +#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E +#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F + +/* Logical Device to indicate A2DP routing */ +#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10 +#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12 +#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13 +#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 + +#define CAD_HW_DEVICE_ID_VOICE 0x15 + +#define CAD_HW_DEVICE_ID_I2S_RX 0x20 +#define CAD_HW_DEVICE_ID_I2S_TX 0x21 + +/* AUXPGA */ +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23 +#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24 +#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25 + +#define CAD_HW_DEVICE_ID_NULL_RX 0x2A + +#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F + +#define CAD_HW_DEVICE_ID_INVALID 0xFF + +#define CAD_RX_DEVICE 0x00 +#define CAD_TX_DEVICE 0x01 + +static struct q6_device_info q6_audio_devices[] = { + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR, + .path = ADIE_PATH_HANDSET_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO, + .path = ADIE_PATH_HEADSET_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO, + .path = ADIE_PATH_HEADSET_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO, + .path = ADIE_PATH_SPEAKER_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO, + .path = ADIE_PATH_SPEAKER_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX, + .path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX, + .path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX, + .path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX, + .path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR, + .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR, + .path = ADIE_PATH_TTY_HEADSET_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_TTY, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC, + .path = ADIE_PATH_HANDSET_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC, + .path = ADIE_PATH_HEADSET_MONO_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC, + .path = ADIE_PATH_SPEAKER_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC, + .path = ADIE_PATH_TTY_HEADSET_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR, + .path = 0, /* XXX */ + .rate = 8000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR, + .cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_A2DP, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC, + .path = 0, /* XXX */ + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ECODEC_TX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR, + .cad_id = CAD_HW_DEVICE_ID_I2S_RX, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_SDAC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_I2S_MIC, + .cad_id = CAD_HW_DEVICE_ID_I2S_TX, + .path = 0, /* XXX */ + .rate = 16000, + .dir = Q6_TX, + .codec = Q6_SDAC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = 0, + .cad_id = 0, + .path = 0, + .rate = 8000, + .dir = 0, + .codec = Q6_CODEC_NONE, + .hw = 0, + }, +}; + diff --git a/arch/arm/mach-msm/qdsp6/audiov2/qcelp_in.c b/arch/arm/mach-msm/qdsp6/audiov2/qcelp_in.c new file mode 100644 index 00000000000..a13084f6127 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/qcelp_in.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "dal_audio.h" +#include "dal_audio_format.h" +#include + + +struct qcelp { + struct mutex lock; + struct msm_audio_qcelp_enc_config cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; +}; + + +static long q6_qcelp_in_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct qcelp *qcelp = file->private_data; + struct adsp_open_command rpc; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&qcelp->lock); + switch (cmd) { + case AUDIO_START: + if (qcelp->audio_client) { + rc = -EBUSY; + break; + } else { + qcelp->audio_client = q6audio_open(AUDIO_FLAG_READ, + qcelp->str_cfg.buffer_size); + + if (!qcelp->audio_client) { + kfree(qcelp); + rc = -ENOMEM; + break; + } + } + + tx_clk_freq = 8000; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format_block.standard.format = ADSP_AUDIO_FORMAT_V13K_FS; + rpc.format_block.standard.channels = 1; + rpc.format_block.standard.bits_per_sample = 16; + rpc.format_block.standard.sampling_rate = 8000; + rpc.format_block.standard.is_signed = 1; + rpc.format_block.standard.is_interleaved = 0; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = qcelp->str_cfg.buffer_size; + rpc.config.qcelp13k.min_rate = qcelp->cfg.min_bit_rate; + rpc.config.qcelp13k.max_rate = qcelp->cfg.max_bit_rate; + + q6audio_start(qcelp->audio_client, &rpc, sizeof(rpc)); + break; + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &qcelp->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&qcelp->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + + if (qcelp->str_cfg.buffer_size < 35) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + + if (qcelp->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + break; + case AUDIO_SET_QCELP_ENC_CONFIG: + if (copy_from_user(&qcelp->cfg, (void *) arg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + + if (qcelp->cfg.min_bit_rate > 4 || + qcelp->cfg.min_bit_rate < 1) { + + pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + if (qcelp->cfg.max_bit_rate > 4 || + qcelp->cfg.max_bit_rate < 1) { + + pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + + break; + case AUDIO_GET_QCELP_ENC_CONFIG: + if (copy_to_user((void *) arg, &qcelp->cfg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&qcelp->lock); + return rc; +} + +static int q6_qcelp_in_open(struct inode *inode, struct file *file) +{ + struct qcelp *qcelp; + qcelp = kmalloc(sizeof(struct qcelp), GFP_KERNEL); + if (qcelp == NULL) { + pr_err("[%s:%s] Could not allocate memory for qcelp driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&qcelp->lock); + file->private_data = qcelp; + qcelp->audio_client = NULL; + qcelp->str_cfg.buffer_size = 35; + qcelp->str_cfg.buffer_count = 2; + qcelp->cfg.cdma_rate = CDMA_RATE_FULL; + qcelp->cfg.min_bit_rate = 1; + qcelp->cfg.max_bit_rate = 4; + return 0; +} + +static ssize_t q6_qcelp_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + struct qcelp *qcelp = file->private_data; + int xfer = 0; + int res; + + mutex_lock(&qcelp->lock); + ac = qcelp->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > xfer) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = ab->actual_size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + res = buf - start; + +fail: + mutex_unlock(&qcelp->lock); + + return res; +} + +static int q6_qcelp_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct qcelp *qcelp = file->private_data; + + mutex_lock(&qcelp->lock); + if (qcelp->audio_client) + rc = q6audio_close(qcelp->audio_client); + mutex_unlock(&qcelp->lock); + kfree(qcelp); + return rc; +} + +static const struct file_operations q6_qcelp_in_fops = { + .owner = THIS_MODULE, + .open = q6_qcelp_in_open, + .read = q6_qcelp_in_read, + .release = q6_qcelp_in_release, + .unlocked_ioctl = q6_qcelp_in_ioctl, +}; + +struct miscdevice q6_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &q6_qcelp_in_fops, +}; + +static int __init q6_qcelp_in_init(void) +{ + return misc_register(&q6_qcelp_in_misc); +} + +device_initcall(q6_qcelp_in_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/routing.c b/arch/arm/mach-msm/qdsp6/audiov2/routing.c new file mode 100644 index 00000000000..1a2476b2ffc --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/routing.c @@ -0,0 +1,73 @@ +/* arch/arm/mach-msm/qdsp6/audiov2/routing.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +static int q6_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t q6_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + q6audio_set_route(cmd); + + return count; +} + +static int q6_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations q6_fops = { + .owner = THIS_MODULE, + .open = q6_open, + .write = q6_write, + .release = q6_release, +}; + +static struct miscdevice q6_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_route", + .fops = &q6_fops, +}; + + +static int __init q6_init(void) +{ + return misc_register(&q6_misc); +} + +device_initcall(q6_init); diff --git a/arch/arm/mach-msm/qdsp6/audiov2/voice.c b/arch/arm/mach-msm/qdsp6/audiov2/voice.c new file mode 100644 index 00000000000..906c5341a9c --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audiov2/voice.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../dal.h" +#include "dal_voice.h" +#include + +struct voice_struct { + struct dal_client *cvd; + struct apr_command_pkt apr_pkt; + struct completion compl; +}; + +static struct voice_struct voice; + +static int cvd_send_response(void) +{ + struct apr_command_pkt *pkt; + uint16_t src_addr; + uint16_t src_token; + uint16_t dst_token; + uint16_t dst_addr; + + pkt = &voice.apr_pkt; + src_addr = pkt->dst_addr; + dst_addr = pkt->src_addr; + src_token = pkt->dst_token; + dst_token = pkt->src_token; + + pkt->header &= ~APR_PKTV1_TYPE_MASK; + pkt->header |= APR_SET_FIELD(APR_PKTV1_TYPE, APR_PKTV1_TYPE_EVENT_V); + pkt->src_addr = src_addr; + pkt->dst_addr = dst_addr; + pkt->src_token = src_token; + pkt->dst_token = dst_token; + pkt->opcode = APR_IBASIC_RSP_RESULT; + + dal_call(voice.cvd, VOICE_OP_CONTROL, 5, pkt, + sizeof(struct apr_command_pkt), + pkt, sizeof(u32)); + return 0; +} + +static int cvd_process_voice_setup(void) +{ + q6voice_setup(); + cvd_send_response(); + return 0; +} + +static int cvd_process_voice_teardown(void) +{ + q6voice_teardown(); + cvd_send_response(); + return 0; +} + +static int cvd_process_set_network(void) +{ + cvd_send_response(); + return 0; +} + +static int voice_thread(void *data) +{ + while (!kthread_should_stop()) { + wait_for_completion(&voice.compl); + init_completion(&voice.compl); + + switch (voice.apr_pkt.opcode) { + + case APR_OP_CMD_CREATE: + cvd_send_response(); + break; + case VOICE_OP_CMD_BRINGUP: + cvd_process_voice_setup(); + break; + case APR_OP_CMD_DESTROY: + cvd_send_response(); + break; + case VOICE_OP_CMD_TEARDOWN: + cvd_process_voice_teardown(); + break; + case VOICE_OP_CMD_SET_NETWORK: + cvd_process_set_network(); + break; + default: + pr_err("[%s:%s] Undefined event\n", __MM_FILE__, + __func__); + + } + } + return 0; +} + +static void remote_cb_function(void *data, int len, void *cookie) +{ + struct apr_command_pkt *apr = data + 2*sizeof(uint32_t); + + memcpy(&voice.apr_pkt, apr, sizeof(struct apr_command_pkt)); + + if (len <= 0) { + pr_err("[%s:%s] unexpected event with length %d\n", + __MM_FILE__, __func__, len); + return; + } + + pr_debug("[%s:%s] APR = %x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", __MM_FILE__, + __func__, + apr->header, + apr->reserved1, + apr->src_addr, + apr->dst_addr, + apr->ret_addr, + apr->src_token, + apr->dst_token, + apr->ret_token, + apr->context, + apr->opcode); + + complete(&voice.compl); +} + +static int __init voice_init(void) +{ + int res = 0; + struct task_struct *task; + u32 tmp[2]; + + tmp[0] = sizeof(u32); + tmp[1] = 0; + + voice.cvd = dal_attach(VOICE_DAL_DEVICE, VOICE_DAL_PORT, 0, + remote_cb_function, 0); + + if (!voice.cvd) { + pr_err("[%s:%s] audio_init: cannot attach to cvd\n", + __MM_FILE__, __func__); + res = -ENODEV; + goto done; + } + + if (check_version(voice.cvd, VOICE_DAL_VERSION) != 0) { + pr_err("[%s:%s] Incompatible cvd version\n", + __MM_FILE__, __func__); + res = -ENODEV; + goto done; + } + dal_call(voice.cvd, VOICE_OP_INIT, 5, tmp, sizeof(tmp), + tmp, sizeof(u32)); + + init_completion(&voice.compl); + task = kthread_run(voice_thread, &voice, "voice_thread"); + + if (IS_ERR(task)) { + pr_err("[%s:%s] Cannot start the voice thread\n", __MM_FILE__, + __func__); + res = PTR_ERR(task); + task = NULL; + } else + goto done; + +done: + return res; +} + +late_initcall(voice_init); diff --git a/arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c b/arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c new file mode 100644 index 00000000000..4195454776c --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c @@ -0,0 +1,190 @@ +/* arch/arm/mach-msm/qdsp6/auxpcm_lb_in.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct auxpcm { + struct mutex lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + int opened;; +}; + +static long auxpcmin_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct auxpcm *auxpcmin = file->private_data; + int rc = 0; + + mutex_lock(&auxpcmin->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void *) arg, + sizeof(acdb_id))) { + pr_info("[%s:%s] copy acdb_id from user failed\n", + __MM_FILE__, __func__); + rc = -EFAULT; + break; + } + if (auxpcmin->ac) { + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + rc = -EBUSY; + } else { + auxpcmin->ac = + q6audio_open_auxpcm(auxpcmin->sample_rate, + auxpcmin->channel_count, + AUDIO_FLAG_READ, acdb_id); + if (!auxpcmin->ac) { + pr_err("[%s:%s] auxpcm open session failed\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + } + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (auxpcmin->ac) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + if (config.channel_count != 1) { + rc = -EINVAL; + pr_err("[%s:%s] invalid channelcount %d\n", + __MM_FILE__, __func__, config.channel_count); + break; + } + if (config.sample_rate != 8000) { + rc = -EINVAL; + pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__, + __func__, config.sample_rate); + break; + } + auxpcmin->sample_rate = config.sample_rate; + auxpcmin->channel_count = config.channel_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = 0; + config.buffer_count = 0; + config.sample_rate = auxpcmin->sample_rate; + config.channel_count = auxpcmin->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&auxpcmin->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static struct auxpcm the_auxpcmin; + +static int auxpcmin_open(struct inode *inode, struct file *file) +{ + struct auxpcm *auxpcmin = &the_auxpcmin; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + mutex_lock(&auxpcmin->lock); + if (auxpcmin->opened) { + pr_err("aux pcm loopback tx already open!\n"); + mutex_unlock(&auxpcmin->lock); + return -EBUSY; + } + auxpcmin->channel_count = 1; + auxpcmin->sample_rate = 8000; + auxpcmin->opened = 1; + file->private_data = auxpcmin; + mutex_unlock(&auxpcmin->lock); + return 0; +} + +static int auxpcmin_release(struct inode *inode, struct file *file) +{ + struct auxpcm *auxpcmin = file->private_data; + mutex_lock(&auxpcmin->lock); + if (auxpcmin->ac) + q6audio_auxpcm_close(auxpcmin->ac); + auxpcmin->ac = NULL; + auxpcmin->opened = 0; + mutex_unlock(&auxpcmin->lock); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return 0; +} + +static const struct file_operations auxpcmin_fops = { + .owner = THIS_MODULE, + .open = auxpcmin_open, + .release = auxpcmin_release, + .unlocked_ioctl = auxpcmin_ioctl, +}; + +struct miscdevice auxpcmin_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aux_pcm_lb_in", + .fops = &auxpcmin_fops, +}; + +static int __init auxpcmin_init(void) +{ + mutex_init(&the_auxpcmin.lock); + return misc_register(&auxpcmin_misc); +} + +device_initcall(auxpcmin_init); diff --git a/arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c b/arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c new file mode 100644 index 00000000000..b6805973f36 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c @@ -0,0 +1,191 @@ +/* arch/arm/mach-msm/qdsp6/auxpcm_lb_out.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct auxpcm { + struct mutex lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + int opened;; +}; + +static long auxpcmout_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct auxpcm *auxpcmout = file->private_data; + int rc = 0; + + mutex_lock(&auxpcmout->lock); + switch (cmd) { + case AUDIO_START: { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void *) arg, + sizeof(acdb_id))) { + pr_info("[%s:%s] copy acdb_id from user failed\n", + __MM_FILE__, __func__); + rc = -EFAULT; + break; + } + if (auxpcmout->ac) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + } else { + auxpcmout->ac = + q6audio_open_auxpcm(auxpcmout->sample_rate, + auxpcmout->channel_count, + AUDIO_FLAG_WRITE, acdb_id); + if (!auxpcmout->ac) { + pr_err("[%s:%s] auxpcm open session failed\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + } + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (auxpcmout->ac) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + if (config.channel_count != 1) { + rc = -EINVAL; + pr_err("[%s:%s] invalid channelcount %d\n", + __MM_FILE__, __func__, config.channel_count); + break; + } + if (config.sample_rate != 8000) { + rc = -EINVAL; + pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__, + __func__, config.sample_rate); + break; + } + auxpcmout->sample_rate = config.sample_rate; + auxpcmout->channel_count = config.channel_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = 0; + config.buffer_count = 0; + config.sample_rate = auxpcmout->sample_rate; + config.channel_count = auxpcmout->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels= %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&auxpcmout->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static struct auxpcm the_auxpcmout; + +static int auxpcmout_open(struct inode *inode, struct file *file) +{ + struct auxpcm *auxpcmout = &the_auxpcmout; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + + mutex_lock(&auxpcmout->lock); + + if (auxpcmout->opened) { + pr_err("aux pcm loopback rx already open!\n"); + mutex_unlock(&auxpcmout->lock); + return -EBUSY; + } + auxpcmout->channel_count = 1; + auxpcmout->sample_rate = 8000; + auxpcmout->opened = 1; + file->private_data = auxpcmout; + mutex_unlock(&auxpcmout->lock); + return 0; +} + +static int auxpcmout_release(struct inode *inode, struct file *file) +{ + struct auxpcm *auxpcmout = file->private_data; + mutex_lock(&auxpcmout->lock); + if (auxpcmout->ac) + q6audio_auxpcm_close(auxpcmout->ac); + auxpcmout->ac = NULL; + auxpcmout->opened = 0; + mutex_unlock(&auxpcmout->lock); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return 0; +} + +static const struct file_operations auxpcmout_fops = { + .owner = THIS_MODULE, + .open = auxpcmout_open, + .release = auxpcmout_release, + .unlocked_ioctl = auxpcmout_ioctl, +}; + +struct miscdevice auxpcmout_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aux_pcm_lb_out", + .fops = &auxpcmout_fops, +}; + +static int __init auxpcmout_init(void) +{ + mutex_init(&the_auxpcmout.lock); + return misc_register(&auxpcmout_misc); +} + +device_initcall(auxpcmout_init); diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/qdsp6/dal.c new file mode 100644 index 00000000000..378432b66c0 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal.c @@ -0,0 +1,727 @@ +/* arch/arm/mach-msm/qdsp6/dal.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "dal.h" + +#define DAL_TRACE 0 + +struct dal_hdr { + uint32_t length:16; /* message length (header inclusive) */ + uint32_t version:8; /* DAL protocol version */ + uint32_t priority:7; + uint32_t async:1; + uint32_t ddi:16; /* DDI method number */ + uint32_t prototype:8; /* DDI serialization format */ + uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */ + void *from; + void *to; +} __attribute__((packed)); + +#define TRACE_DATA_MAX 128 +#define TRACE_LOG_MAX 32 +#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1) + +struct dal_trace { + unsigned timestamp; + struct dal_hdr hdr; + uint32_t data[TRACE_DATA_MAX]; +}; + +#define DAL_HDR_SIZE (sizeof(struct dal_hdr)) +#define DAL_DATA_MAX 512 +#define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX) + +#define DAL_VERSION 0x11 + +#define DAL_MSGID_DDI 0x00 +#define DAL_MSGID_ATTACH 0x01 +#define DAL_MSGID_DETACH 0x02 +#define DAL_MSGID_ASYNCH 0xC0 +#define DAL_MSGID_REPLY 0x80 + +struct dal_channel { + struct list_head list; + struct list_head clients; + + /* synchronization for changing channel state, + * adding/removing clients, smd callbacks, etc + */ + spinlock_t lock; + + struct smd_channel *sch; + char *name; + + /* events are delivered at IRQ context immediately, so + * we only need one assembly buffer for the entire channel + */ + struct dal_hdr hdr; + unsigned char data[DAL_DATA_MAX]; + + unsigned count; + void *ptr; + + /* client which the current inbound message is for */ + struct dal_client *active; +}; + +struct dal_client { + struct list_head list; + struct dal_channel *dch; + void *cookie; + dal_event_func_t event; + + /* opaque handle for the far side */ + void *remote; + + /* dal rpc calls are fully synchronous -- only one call may be + * active per client at a time + */ + struct mutex write_lock; + wait_queue_head_t wait; + + unsigned char data[DAL_DATA_MAX]; + + void *reply; + int reply_max; + int status; + unsigned msgid; /* msgid of expected reply */ + + spinlock_t tr_lock; + unsigned tr_head; + unsigned tr_tail; + struct dal_trace *tr_log; +}; + +static unsigned now(void) +{ + struct timespec ts; + ktime_get_ts(&ts); + return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000); +} + +void dal_trace(struct dal_client *c) +{ + if (c->tr_log) + return; + c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX, + GFP_KERNEL); +} + +void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when) +{ + int i; + printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d", + (unsigned) hdr->from, (unsigned) hdr->to, + hdr->length, hdr->async, + hdr->ddi, hdr->prototype, hdr->msgid, + when); + len /= 4; + for (i = 0; i < len; i++) { + if (!(i & 7)) + printk("\n%03x", i * 4); + printk(" %08x", data[i]); + } + printk("\n"); +} + +void dal_trace_dump(struct dal_client *c) +{ + struct dal_trace *dt; + unsigned n, len; + + if (!c->tr_log) + return; + + for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) { + dt = c->tr_log + n; + len = dt->hdr.length - sizeof(dt->hdr); + if (len > TRACE_DATA_MAX) + len = TRACE_DATA_MAX; + dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp); + } +} + +static void dal_trace_log(struct dal_client *c, + struct dal_hdr *hdr, void *data, unsigned len) +{ + unsigned long flags; + unsigned t, n; + struct dal_trace *dt; + + t = now(); + if (len > TRACE_DATA_MAX) + len = TRACE_DATA_MAX; + + spin_lock_irqsave(&c->tr_lock, flags); + n = (c->tr_head + 1) & TRACE_LOG_MASK; + if (c->tr_tail == n) + c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK; + dt = c->tr_log + n; + dt->timestamp = t; + memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr)); + memcpy(dt->data, data, len); + c->tr_head = n; + + spin_unlock_irqrestore(&c->tr_lock, flags); +} + + +static void dal_channel_notify(void *priv, unsigned event) +{ + struct dal_channel *dch = priv; + struct dal_hdr *hdr = &dch->hdr; + struct dal_client *client; + unsigned long flags; + int len; + int r; + + spin_lock_irqsave(&dch->lock, flags); + +again: + if (dch->count == 0) { + if (smd_read_avail(dch->sch) < DAL_HDR_SIZE) + goto done; + + smd_read(dch->sch, hdr, DAL_HDR_SIZE); + + if (hdr->length < DAL_HDR_SIZE) + goto done; + + if (hdr->length > DAL_MSG_MAX) + panic("oversize message"); + + dch->count = hdr->length - DAL_HDR_SIZE; + + /* locate the client this message is targeted to */ + list_for_each_entry(client, &dch->clients, list) { + if (dch->hdr.to == client) { + dch->active = client; + dch->ptr = client->data; + goto check_data; + } + } + pr_err("[%s:%s] $$$ receiving unknown message len = %d $$$\n", + __MM_FILE__, __func__, dch->count); + dch->active = 0; + dch->ptr = dch->data; + } + +check_data: + len = dch->count; + if (len > 0) { + if (smd_read_avail(dch->sch) < len) + goto done; + + r = smd_read(dch->sch, dch->ptr, len); + if (r != len) + panic("invalid read"); + +#if DAL_TRACE + pr_info("[%s:%s] dal recv %p <- %p %02x:%04x:%02x %d\n", + __MM_FILE__, __func__, hdr->to, hdr->from, hdr->msgid, + hdr->ddi, hdr->prototype, hdr->length - sizeof(*hdr)); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len); +#endif + dch->count = 0; + + client = dch->active; + if (!client) { + pr_err("[%s:%s] message to %p discarded\n", + __MM_FILE__, __func__, dch->hdr.to); + goto again; + } + + if (client->tr_log) + dal_trace_log(client, hdr, dch->ptr, len); + + if (hdr->msgid == DAL_MSGID_ASYNCH) { + if (client->event) + client->event(dch->ptr, len, client->cookie); + else + pr_err("[%s:%s] client %p has no event \ + handler\n", __MM_FILE__, __func__, + client); + goto again; + } + + if (hdr->msgid == client->msgid) { + if (!client->remote) + client->remote = hdr->from; + if (len > client->reply_max) + len = client->reply_max; + memcpy(client->reply, client->data, len); + client->status = len; + wake_up(&client->wait); + goto again; + } + + pr_err("[%s:%s] cannot find client %p\n", __MM_FILE__, + __func__, dch->hdr.to); + goto again; + } + +done: + spin_unlock_irqrestore(&dch->lock, flags); +} + +static LIST_HEAD(dal_channel_list); +static DEFINE_MUTEX(dal_channel_list_lock); + +static struct dal_channel *dal_open_channel(const char *name, uint32_t cpu) +{ + struct dal_channel *dch; + + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + mutex_lock(&dal_channel_list_lock); + + list_for_each_entry(dch, &dal_channel_list, list) { + if (!strcmp(dch->name, name)) + goto found_it; + } + + dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL); + if (!dch) + goto fail; + + dch->name = (char *) (dch + 1); + strcpy(dch->name, name); + spin_lock_init(&dch->lock); + INIT_LIST_HEAD(&dch->clients); + + list_add(&dch->list, &dal_channel_list); + +found_it: + if (!dch->sch) { + if (smd_named_open_on_edge(name, cpu, &dch->sch, + dch, dal_channel_notify)) { + pr_err("[%s:%s] smd open failed\n", __MM_FILE__, + __func__); + dch = NULL; + } + /* FIXME: wait for channel to open before returning */ + msleep(100); + } + +fail: + mutex_unlock(&dal_channel_list_lock); + + return dch; +} + +int dal_call_raw(struct dal_client *client, + struct dal_hdr *hdr, + void *data, int data_len, + void *reply, int reply_max) +{ + struct dal_channel *dch = client->dch; + unsigned long flags; + + client->reply = reply; + client->reply_max = reply_max; + client->msgid = hdr->msgid | DAL_MSGID_REPLY; + client->status = -EBUSY; + +#if DAL_TRACE + pr_info("[%s:%s:%x] dal send %p -> %p %02x:%04x:%02x %d\n", + __MM_FILE__, __func__, (unsigned int)client, hdr->from, hdr->to, + hdr->msgid, hdr->ddi, hdr->prototype, + hdr->length - sizeof(*hdr)); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len); +#endif + + if (client->tr_log) + dal_trace_log(client, hdr, data, data_len); + + spin_lock_irqsave(&dch->lock, flags); + /* FIXME: ensure entire message is written or none. */ + smd_write(dch->sch, hdr, sizeof(*hdr)); + smd_write(dch->sch, data, data_len); + spin_unlock_irqrestore(&dch->lock, flags); + + if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) { + dal_trace_dump(client); + pr_err("[%s:%s] call timed out. dsp is probably dead.\n", + __MM_FILE__, __func__); + dal_trace_print(hdr, data, data_len, 0); + q6audio_dsp_not_responding(); + } + + return client->status; +} + +int dal_call(struct dal_client *client, + unsigned ddi, unsigned prototype, + void *data, int data_len, + void *reply, int reply_max) +{ + struct dal_hdr hdr; + int r; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.length = data_len + sizeof(hdr); + hdr.version = DAL_VERSION; + hdr.msgid = DAL_MSGID_DDI; + hdr.ddi = ddi; + hdr.prototype = prototype; + hdr.from = client; + hdr.to = client->remote; + + if (hdr.length > DAL_MSG_MAX) + return -EINVAL; + + mutex_lock(&client->write_lock); + r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max); + mutex_unlock(&client->write_lock); + + return r; +} + +struct dal_msg_attach { + uint32_t device_id; + char attach[64]; + char service_name[32]; +} __attribute__((packed)); + +struct dal_reply_attach { + uint32_t status; + char name[64]; +}; + +struct dal_client *dal_attach(uint32_t device_id, const char *name, + uint32_t cpu, dal_event_func_t func, void *cookie) +{ + struct dal_hdr hdr; + struct dal_msg_attach msg; + struct dal_reply_attach reply; + struct dal_channel *dch; + struct dal_client *client; + unsigned long flags; + int r; + + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + dch = dal_open_channel(name, cpu); + if (!dch) + return 0; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return 0; + + client->dch = dch; + client->event = func; + client->cookie = cookie; + mutex_init(&client->write_lock); + spin_lock_init(&client->tr_lock); + init_waitqueue_head(&client->wait); + + spin_lock_irqsave(&dch->lock, flags); + list_add(&client->list, &dch->clients); + spin_unlock_irqrestore(&dch->lock, flags); + + memset(&hdr, 0, sizeof(hdr)); + memset(&msg, 0, sizeof(msg)); + + hdr.length = sizeof(hdr) + sizeof(msg); + hdr.version = DAL_VERSION; + hdr.msgid = DAL_MSGID_ATTACH; + hdr.from = client; + msg.device_id = device_id; + + r = dal_call_raw(client, &hdr, &msg, sizeof(msg), + &reply, sizeof(reply)); + + if ((r == sizeof(reply)) && (reply.status == 0)) { + reply.name[63] = 0; + pr_info("[%s:%s] status = %d, name = '%s' dal_client %x\n", + __MM_FILE__, __func__, reply.status, + reply.name, (unsigned int)client); + return client; + } + + pr_err("[%s:%s] failure\n", __MM_FILE__, __func__); + + dal_detach(client); + return 0; +} + +int dal_detach(struct dal_client *client) +{ + struct dal_channel *dch; + unsigned long flags; + + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + mutex_lock(&client->write_lock); + if (client->remote) { + struct dal_hdr hdr; + uint32_t data; + + memset(&hdr, 0, sizeof(hdr)); + hdr.length = sizeof(hdr) + sizeof(data); + hdr.version = DAL_VERSION; + hdr.msgid = DAL_MSGID_DETACH; + hdr.from = client; + hdr.to = client->remote; + data = (uint32_t) client; + + dal_call_raw(client, &hdr, &data, sizeof(data), + &data, sizeof(data)); + } + + dch = client->dch; + spin_lock_irqsave(&dch->lock, flags); + if (dch->active == client) { + /* We have received a message header for this client + * but not the body of the message. Ensure that when + * the body arrives we don't write it into the now-closed + * client. In *theory* this should never happen. + */ + dch->active = 0; + dch->ptr = dch->data; + } + list_del(&client->list); + spin_unlock_irqrestore(&dch->lock, flags); + + mutex_unlock(&client->write_lock); + + kfree(client); + return 0; +} + +void *dal_get_remote_handle(struct dal_client *client) +{ + return client->remote; +} + +/* convenience wrappers */ + +int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1) +{ + uint32_t tmp = arg1; + int res; + res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp)); + if (res >= 4) + return (int) tmp; + return res; +} + +int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, + uint32_t arg2) +{ + uint32_t tmp[2]; + int res; + tmp[0] = arg1; + tmp[1] = arg2; + res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t)); + if (res >= 4) + return (int) tmp[0]; + return res; +} + +int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen) +{ + uint32_t tmp[128]; + int res; + int param_idx = 0; + + if (ilen + 4 > DAL_DATA_MAX) + return -EINVAL; + + tmp[param_idx] = ilen; + param_idx++; + + memcpy(&tmp[param_idx], ibuf, ilen); + param_idx += DIV_ROUND_UP(ilen, 4); + + res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp)); + + if (res >= 4) + return (int) tmp[0]; + return res; +} + +int dal_call_f6(struct dal_client *client, uint32_t ddi, uint32_t s1, + void *ibuf, uint32_t ilen) +{ + uint32_t tmp[128]; + int res; + int param_idx = 0; + + if (ilen + 8 > DAL_DATA_MAX) + return -EINVAL; + + tmp[param_idx] = s1; + param_idx++; + tmp[param_idx] = ilen; + param_idx++; + memcpy(&tmp[param_idx], ibuf, ilen); + param_idx += DIV_ROUND_UP(ilen, 4); + + res = dal_call(client, ddi, 6, tmp, param_idx * 4, tmp, sizeof(tmp)); + + if (res >= 4) + return (int) tmp[0]; + + return res; +} + +int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, + uint32_t olen) +{ + uint32_t tmp[128]; + int res; + + if (olen > sizeof(tmp) - 8) + return -EINVAL; + tmp[0] = olen; + + res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, + sizeof(tmp)); + + if (res >= 4) + res = (int)tmp[0]; + + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; +} + +int dal_call_f11(struct dal_client *client, uint32_t ddi, uint32_t s1, + void *obuf, uint32_t olen) +{ + uint32_t tmp[DAL_DATA_MAX/4] = {0}; + int res; + int param_idx = 0; + int num_bytes = 4; + + num_bytes += (DIV_ROUND_UP(olen, 4)) * 4; + + if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8)) + return -EINVAL; + + tmp[param_idx] = s1; + param_idx++; + tmp[param_idx] = olen; + param_idx += DIV_ROUND_UP(olen, 4); + + res = dal_call(client, ddi, 11, tmp, param_idx * 4, tmp, sizeof(tmp)); + + if (res >= 4) + res = (int) tmp[0]; + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; +} + +int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, + uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen) +{ + uint32_t tmp[DAL_DATA_MAX/4]; + int res; + int param_idx = 0; + int num_bytes = 0; + + num_bytes = (DIV_ROUND_UP(ilen1, 4)) * 4; + num_bytes += (DIV_ROUND_UP(ilen2, 4)) * 4; + + if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8) || + (ilen1 > DAL_DATA_MAX) || (ilen2 > DAL_DATA_MAX)) + return -EINVAL; + + tmp[param_idx] = ilen1; + param_idx++; + + memcpy(&tmp[param_idx], ibuf1, ilen1); + param_idx += DIV_ROUND_UP(ilen1, 4); + + tmp[param_idx++] = ilen2; + memcpy(&tmp[param_idx], ibuf2, ilen2); + param_idx += DIV_ROUND_UP(ilen2, 4); + + tmp[param_idx++] = olen; + res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp, + sizeof(tmp)); + + if (res >= 4) + res = (int)tmp[0]; + + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; +} +int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf, + uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2, + uint32_t olen2, uint32_t *oalen2) +{ + uint32_t tmp[128]; + int res; + int param_idx = 0; + + if (olen1 + olen2 + 8 > DAL_DATA_MAX || + ilen + 12 > DAL_DATA_MAX) + return -EINVAL; + + tmp[param_idx] = ilen; + param_idx++; + + memcpy(&tmp[param_idx], ibuf, ilen); + param_idx += DIV_ROUND_UP(ilen, 4); + + tmp[param_idx++] = olen1; + tmp[param_idx++] = olen2; + res = dal_call(client, ddi, 14, tmp, param_idx * 4, tmp, sizeof(tmp)); + + if (res >= 4) + res = (int)tmp[0]; + + if (!res) { + if (tmp[1] > olen1) + return -EIO; + param_idx = DIV_ROUND_UP(tmp[1], 4) + 2; + if (tmp[param_idx] > olen2) + return -EIO; + + memcpy(obuf1, &tmp[2], tmp[1]); + memcpy(obuf2, &tmp[param_idx+1], tmp[param_idx]); + *oalen2 = tmp[param_idx]; + } + return res; +} diff --git a/arch/arm/mach-msm/qdsp6/dal.h b/arch/arm/mach-msm/qdsp6/dal.h new file mode 100644 index 00000000000..1176eb9c102 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal.h @@ -0,0 +1,96 @@ +/* arch/arm/mach-msm/qdsp6/dal.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_MSM_DAL_ +#define _MACH_MSM_DAL_ + +struct dal_client; + +struct dal_info { + uint32_t size; + uint32_t version; + char name[32]; +}; + +typedef void (*dal_event_func_t)(void *data, int len, void *cookie); + +struct dal_client *dal_attach(uint32_t device_id, const char *name, + uint32_t cpu, dal_event_func_t func, void *cookie); + +int dal_detach(struct dal_client *client); + +int dal_call(struct dal_client *client, + unsigned ddi, unsigned prototype, + void *data, int data_len, + void *reply, int reply_max); + +void dal_trace(struct dal_client *client); +void dal_trace_dump(struct dal_client *client); + +/* function to call before panic on stalled dal calls */ +void dal_set_oops(struct dal_client *client, void (*oops)(void)); + +/* convenience wrappers */ +int dal_call_f0(struct dal_client *client, uint32_t ddi, + uint32_t arg1); +int dal_call_f1(struct dal_client *client, uint32_t ddi, + uint32_t arg1, uint32_t arg2); +int dal_call_f5(struct dal_client *client, uint32_t ddi, + void *ibuf, uint32_t ilen); +int dal_call_f6(struct dal_client *client, uint32_t ddi, + uint32_t s1, void *ibuf, uint32_t ilen); +int dal_call_f9(struct dal_client *client, uint32_t ddi, + void *obuf, uint32_t olen); +int dal_call_f11(struct dal_client *client, uint32_t ddi, + uint32_t s1, void *obuf, uint32_t olen); +int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, + uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen); +int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf, + uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2, + uint32_t olen2, uint32_t *oalen2); + +/* common DAL operations */ +enum { + DAL_OP_ATTACH = 0, + DAL_OP_DETACH, + DAL_OP_INIT, + DAL_OP_DEINIT, + DAL_OP_OPEN, + DAL_OP_CLOSE, + DAL_OP_INFO, + DAL_OP_POWEREVENT, + DAL_OP_SYSREQUEST, + DAL_OP_FIRST_DEVICE_API, +}; + +static inline int check_version(struct dal_client *client, uint32_t version) +{ + struct dal_info info; + int res; + + res = dal_call_f9(client, DAL_OP_INFO, &info, sizeof(struct dal_info)); + if (!res) { + if (((info.version & 0xFFFF0000) != (version & 0xFFFF0000)) || + ((info.version & 0x0000FFFF) < + (version & 0x0000FFFF))) { + res = -EINVAL; + } + } + return res; +} + +#endif diff --git a/arch/arm/mach-msm/qdsp6/dal_acdb.h b/arch/arm/mach-msm/qdsp6/dal_acdb.h new file mode 100644 index 00000000000..dfb1fefdae9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_acdb.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define ACDB_DAL_DEVICE 0x02000069 +#define ACDB_DAL_PORT "DAL_AM_AUD" + +#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API + +/* ioctls */ +#define ACDB_GET_DEVICE 0x0108bb92 +#define ACDB_SET_DEVICE 0x0108bb93 +#define ACDB_GET_STREAM 0x0108bb95 +#define ACDB_SET_STREAM 0x0108bb96 +#define ACDB_GET_DEVICE_TABLE 0x0108bb97 +#define ACDB_GET_STREAM_TABLE 0x0108bb98 + +#define ACDB_RES_SUCCESS 0 +#define ACDB_RES_FAILURE -1 +#define ACDB_RES_BADPARM -2 +#define ACDB_RES_BADSTATE -3 + +struct acdb_cmd_device { + uint32_t size; + + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; + uint32_t interface_id; + uint32_t algorithm_block_id; + + /* physical page aligned buffer */ + uint32_t total_bytes; + uint32_t unmapped_buf; +} __attribute__((packed)); + +struct acdb_cmd_device_table { + uint32_t size; + + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; + + /* physical page aligned buffer */ + uint32_t total_bytes; + uint32_t unmapped_buf; + + uint32_t res_size; +} __attribute__((packed)); + +struct acdb_result { + uint32_t dal_status; + uint32_t size; + + uint32_t unmapped_buf; + uint32_t used_bytes; + uint32_t result; +} __attribute__((packed)); diff --git a/arch/arm/mach-msm/qdsp6/dal_adie.h b/arch/arm/mach-msm/qdsp6/dal_adie.h new file mode 100644 index 00000000000..6abc60c66ec --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_adie.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_MSM_QDSP6_ADIE_ +#define _MACH_MSM_QDSP6_ADIE_ + +#include "dal.h" + +#define ADIE_DAL_DEVICE 0x02000029 +#define ADIE_DAL_PORT "DAL_AM_AUD" + +enum { + ADIE_OP_GET_NUM_PATHS = DAL_OP_FIRST_DEVICE_API, + ADIE_OP_GET_ALL_PATH_IDS, + ADIE_OP_SET_PATH, + ADIE_OP_GET_NUM_PATH_FREQUENCY_PLANS, + ADIE_OP_GET_PATH_FREQUENCY_PLANS, + ADIE_OP_SET_PATH_FREQUENCY_PLAN, + ADIE_OP_PROCEED_TO_STAGE, + ADIE_OP_MUTE_PATH +}; + +/* Path IDs for normal operation. */ +#define ADIE_PATH_HANDSET_TX 0x010740f6 +#define ADIE_PATH_HANDSET_RX 0x010740f7 +#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8 +#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9 +#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa +#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb +#define ADIE_PATH_SPEAKER_TX 0x010740fc +#define ADIE_PATH_SPEAKER_RX 0x010740fd +#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101 + +/* Path IDs used for TTY */ +#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe +#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff + +/* Path IDs used by Factory Test Mode. */ +#define ADIE_PATH_FTM_MIC1_TX 0x01074108 +#define ADIE_PATH_FTM_MIC2_TX 0x01074107 +#define ADIE_PATH_FTM_HPH_L_RX 0x01074106 +#define ADIE_PATH_FTM_HPH_R_RX 0x01074104 +#define ADIE_PATH_FTM_EAR_RX 0x01074103 +#define ADIE_PATH_FTM_SPKR_RX 0x01074102 + +/* Path IDs for Loopback */ +/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/ +#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100 +/* Line in -> AuxPGA -> LineOut Mono */ +#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82 +/* Line in -> AuxPGA -> Stereo Headphone */ +#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109 +/* Line in -> AuxPGA -> Mono Headphone */ +#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85 +/* Line in -> AuxPGA -> Earpiece */ +#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81 +/* Line in -> AuxPGA -> AuxOut */ +#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86 + +/* Concurrency Profiles */ +#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83 +#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84 +#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88 +#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89 + + +/** Fluence Profiles **/ + +/* Broadside/Bowsetalk profile, + * For Handset and Speaker phone Tx*/ +#define ADIE_CODEC_HANDSET_SPKR_BS_TX 0x0108fafa +/* EndFire profile, + * For Handset and Speaker phone Tx*/ +#define ADIE_CODEC_HANDSET_SPKR_EF_TX 0x0108fafb + + +/* stages */ +#define ADIE_STAGE_PATH_OFF 0x0050 +#define ADIE_STAGE_DIGITAL_READY 0x0100 +#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000 +#define ADIE_STAGE_ANALOG_OFF 0x0750 +#define ADIE_STAGE_DIGITAL_OFF 0x0600 + +/* path types */ +#define ADIE_PATH_RX 0 +#define ADIE_PATH_TX 1 +#define ADIE_PATH_LOOPBACK 2 + +/* mute states */ +#define ADIE_MUTE_OFF 0 +#define ADIE_MUTE_ON 1 + + +#endif diff --git a/arch/arm/mach-msm/qdsp6/dal_audio.h b/arch/arm/mach-msm/qdsp6/dal_audio.h new file mode 100644 index 00000000000..25d1e4f45e3 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_audio.h @@ -0,0 +1,604 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DAL_AUDIO_H__ +#define __DAL_AUDIO_H__ + +#include "dal_audio_format.h" + +#define AUDIO_DAL_DEVICE 0x02000028 +#define AUDIO_DAL_PORT "DAL_AQ_AUD" + +enum { + AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API, + AUDIO_OP_DATA, + AUDIO_OP_INIT, +}; + +/* ---- common audio structures ---- */ + +/* This flag, if set, indicates that the beginning of the data in the*/ +/* buffer is a synchronization point or key frame, meaning no data */ +/* before it in the stream is required in order to render the stream */ +/* from this point onward. */ +#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01 + +/* This flag, if set, indicates that the buffer object is using valid */ +/* physical address used to store the media data */ +#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04 + +/* This flag, if set, indicates that a media start timestamp has been */ +/* set for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08 + +/* This flag, if set, indicates that a media stop timestamp has been set */ +/* for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10 + +/* This flag, if set, indicates that a preroll timestamp has been set */ +/* for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20 + +/* This flag, if set, indicates that the data in the buffer is a fragment of */ +/* a larger block of data, and will be continued by the data in the next */ +/* buffer to be delivered. */ +#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40 + +struct adsp_audio_buffer { + u32 addr; /* Physical Address of buffer */ + u32 max_size; /* Maximum size of buffer */ + u32 actual_size; /* Actual size of valid data in the buffer */ + u32 offset; /* Offset to the first valid byte */ + u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */ + s64 start; /* Start timestamp, if any */ + s64 stop; /* Stop timestamp, if any */ + s64 preroll; /* Preroll timestamp, if any */ +} __attribute__ ((packed)); + + + +/* ---- audio commands ---- */ + +/* Command/event response types */ +#define ADSP_AUDIO_RESPONSE_COMMAND 0 +#define ADSP_AUDIO_RESPONSE_ASYNC 1 + +struct adsp_command_hdr { + u32 size; /* sizeof(cmd) - sizeof(u32) */ + + u32 dst; + u32 src; + + u32 opcode; + u32 response_type; + u32 seq_number; + + u32 context; /* opaque to DSP */ + u32 data; + + u32 padding; +} __attribute__ ((packed)); + + +#define AUDIO_DOMAIN_APP 0 +#define AUDIO_DOMAIN_MODEM 1 +#define AUDIO_DOMAIN_DSP 2 + +#define AUDIO_SERVICE_AUDIO 0 +#define AUDIO_SERVICE_VIDEO 1 /* really? */ + +/* adsp audio addresses are (byte order) domain, service, major, minor */ +//#define AUDIO_ADDR(maj,min) ( (((maj) & 0xff) << 16) | (((min) & 0xff) << 24) | (1) ) + +#define AUDIO_ADDR(maj,min,dom) ( (((min) & 0xff) << 24) | (((maj) & 0xff) << 16) | ((AUDIO_SERVICE_AUDIO) << 8) | (dom) ) + + +/* AAC Encoder modes */ +#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0 +#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1 +#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2 + +struct adsp_audio_aac_enc_cfg { + u32 bit_rate; /* bits per second */ + u32 encoder_mode; /* ADSP_AUDIO_ENC_* */ +} __attribute__ ((packed)); + +#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0 +#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1 + +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9 + +struct adsp_audio_sbc_encoder_cfg { + u32 num_subbands; + u32 block_len; + u32 channel_mode; + u32 allocation_method; + u32 bit_rate; +} __attribute__ ((packed)); + +/* AMR NB encoder modes */ +#define ADSP_AUDIO_AMR_MR475 0 +#define ADSP_AUDIO_AMR_MR515 1 +#define ADSP_AUDIO_AMR_MMR59 2 +#define ADSP_AUDIO_AMR_MMR67 3 +#define ADSP_AUDIO_AMR_MMR74 4 +#define ADSP_AUDIO_AMR_MMR795 5 +#define ADSP_AUDIO_AMR_MMR102 6 +#define ADSP_AUDIO_AMR_MMR122 7 + +/* The following are valid AMR NB DTX modes */ +#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3 + +/* AMR Encoder configuration */ +struct adsp_audio_amr_enc_cfg { + u32 mode; /* ADSP_AUDIO_AMR_MR* */ + u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */ + u32 enable; /* 1 = enable, 0 = disable */ +} __attribute__ ((packed)); + +struct adsp_audio_qcelp13k_enc_cfg { + u16 min_rate; + u16 max_rate; +} __attribute__ ((packed)); + +struct adsp_audio_evrc_enc_cfg { + u16 min_rate; + u16 max_rate; +} __attribute__ ((packed)); + +union adsp_audio_codec_config { + struct adsp_audio_amr_enc_cfg amr; + struct adsp_audio_aac_enc_cfg aac; + struct adsp_audio_qcelp13k_enc_cfg qcelp13k; + struct adsp_audio_evrc_enc_cfg evrc; + struct adsp_audio_sbc_encoder_cfg sbc; +} __attribute__ ((packed)); + + +/* This is the default value. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000 + +/* This bit, if set, indicates that the AVSync mode is activated. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001 + +/* This bit, if set, indicates that the Sample Rate/Channel Mode */ +/* Change Notification mode is activated. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002 + +/* This bit, if set, indicates that the sync clock is enabled */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004 + +/* This bit, if set, indicates that the AUX PCM loopback is enabled */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_AUX_PCM 0x0040 + +struct adsp_open_command { + struct adsp_command_hdr hdr; + + u32 device; + u32 endpoint; /* address */ + + u32 stream_context; + u32 mode; + + u32 buf_max_size; + + union adsp_audio_format format; + union adsp_audio_codec_config config; +} __attribute__ ((packed)); + + +/* --- audio control and stream session ioctls ---- */ + +/* Opcode to open a device stream session to capture audio */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79 + +/* Opcode to open a device stream session to render audio */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a + +/* Opcode to open a device session, must open a device */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b + +/* Close an existing stream or device */ +#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc + + + +/* A device switch requires three IOCTL */ +/* commands in the following sequence: PREPARE, STANDBY, COMMIT */ + +/* adsp_audio_device_switch_command structure is needed for */ +/* DEVICE_SWITCH_PREPARE */ + +/* Device switch protocol step #1. Pause old device and */ +/* generate silence for the old device. */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4 + +/* Device switch protocol step #2. Release old device, */ +/* create new device and generate silence for the new device. */ + +/* When client receives ack for this IOCTL, the client can */ +/* start sending IOCTL commands to configure, calibrate and */ +/* change filter settings on the new device. */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5 + +/* Device switch protocol step #3. Start normal operations on new device */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7 + +struct adsp_device_switch_command { + struct adsp_command_hdr hdr; + u32 old_device; + u32 new_device; + u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */ + u8 device_type; /* 0 = rx, 1 = tx, 2 = both */ +} __attribute__ ((packed)); + + + +/* --- audio control session ioctls ---- */ + +#define ADSP_PATH_RX 0 +#define ADSP_PATH_TX 1 +#define ADSP_PATH_BOTH 2 +#define ADSP_PATH_TX_CNG_DIS 3 + +struct adsp_audio_dtmf_start_command { + struct adsp_command_hdr hdr; + u32 tone1_hz; + u32 tone2_hz; + u32 duration_usec; + s32 gain_mb; +} __attribute__ ((packed)); + +/* These commands will affect a logical device and all its associated */ +/* streams. */ + +#define ADSP_AUDIO_MAX_EQ_BANDS 12 + +struct adsp_audio_eq_band { + u16 band_idx; /* The band index, 0 .. 11 */ + u32 filter_type; /* Filter band type */ + u32 center_freq_hz; /* Filter band center frequency */ + s32 filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + s32 q_factor; + /* Filter band quality factor expressed as q-8 number, */ + /* e.g. 3000/(2^8) */ +} __attribute__ ((packed)); + +struct adsp_audio_eq_stream_config { + uint32_t enable; /* Number of consequtive bands specified */ + uint32_t num_bands; + struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +/* set device equalizer */ +struct adsp_set_dev_equalizer_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 enable; + u32 num_bands; + struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +/* Set device volume. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c + +struct adsp_set_dev_volume_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 path; /* 0 = rx, 1 = tx, 2 = both */ + s32 volume; +} __attribute__ ((packed)); + +/* Set Device stereo volume. This command has data payload, */ +/* struct adsp_audio_set_dev_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e + +/* Set L, R cross channel gain for a Device. This command has */ +/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40 + +/* Set device mute state. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f + +struct adsp_set_dev_mute_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 path; /* 0 = rx, 1 = tx, 2 = both */ + u32 mute; /* 1 = mute */ +} __attribute__ ((packed)); + +/* Configure Equalizer for a device. */ +/* This command has payload struct adsp_audio_set_dev_equalizer_command. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e + +/* Set configuration data for an algorithm aspect of a device. */ +/* This command has payload struct adsp_audio_set_dev_cfg_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb + +struct adsp_set_dev_cfg_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 block_id; + u32 interface_id; + u32 phys_addr; + u32 phys_size; + u32 phys_used; +} __attribute__ ((packed)); + +/* Set configuration data for all interfaces of a device. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf + +struct adsp_set_dev_cfg_table_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 phys_addr; + u32 phys_size; + u32 phys_used; +} __attribute__ ((packed)); + +/* ---- audio stream data commands ---- */ + +#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f +#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80 + +struct adsp_buffer_command { + struct adsp_command_hdr hdr; + struct adsp_audio_buffer buffer; +} __attribute__ ((packed)); + + + +/* ---- audio stream ioctls (only affect a single stream in a session) ---- */ + +/* Stop stream for audio device. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54 + +/* End of stream reached. Client will not send any more data. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150 + +/* Do sample slipping/stuffing on AAC outputs. The payload of */ +/* this command is struct adsp_audio_slip_sample_command. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e + +/* Set stream volume. */ +/* This command has data payload, struct adsp_audio_set_volume_command. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de + +/* Set stream stereo volume. This command has data payload, */ +/* struct adsp_audio_set_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c + +/* Set L, R cross channel gain for a Stream. This command has */ +/* data payload, struct adsp_audio_set_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d + +/* Set stream mute state. */ +/* This command has data payload, struct adsp_audio_set_stream_mute. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df + +/* Reconfigure bit rate information. This command has data */ +/* payload, struct adsp_audio_set_bit_rate_command */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1 + +/* Set Channel Mapping. This command has data payload, struct */ +/* This command has data payload struct adsp_audio_set_channel_map_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a + +/* Enable/disable AACPlus SBR. */ +/* This command has data payload struct adsp_audio_set_sbr_command */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416 + +/* Enable/disable WMA Pro Chex and Fex. This command has data payload */ +/* struct adsp_audio_stream_set_wma_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417 + + +/* ---- audio session ioctls (affect all streams in a session) --- */ + +/* Start stream for audio device. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6 + +/* Stop all stream(s) for audio session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e + +/* Pause the data flow for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8 + +/* Resume the data flow for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9 + +/* Drop any unprocessed data buffers for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea + +/* Start Stream DTMF tone */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd + +/* Stop Stream DTMF tone */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554 + +/* Set Session volume. */ +/* This command has data payload, struct adsp_audio_set_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd + +/* Set session stereo volume. This command has data payload, */ +/* struct adsp_audio_set_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d + +/* Set L, R cross channel gain for a session. This command has */ +/* data payload, struct adsp_audio_set_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f + +/* Set Session mute state. */ +/* This command has data payload, struct adsp_audio_set_mute_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be + +/* Configure Equalizer for a stream. */ +/* This command has payload struct adsp_audio_set_equalizer_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0 + +/* Set Audio Video sync information. */ +/* This command has data payload, struct adsp_audio_set_av_sync_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2 + +/* Get Audio Media Session time. */ +/* This command returns the audioTime in adsp_audio_unsigned64_event */ +#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c + + +/* these command structures are used for both STREAM and SESSION ioctls */ + +struct adsp_set_volume_command { + struct adsp_command_hdr hdr; + s32 volume; +} __attribute__ ((packed)); + +struct adsp_set_mute_command { + struct adsp_command_hdr hdr; + u32 mute; /* 1 == mute */ +} __attribute__ ((packed)); + + +struct adsp_set_equalizer_command { + struct adsp_command_hdr hdr; + u32 enable; + u32 num_bands; + struct adsp_audio_eq_band eq_bands[ADSP_AUDIO_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +/* ---- audio events ---- */ + +/* All IOCTL commands generate an event with the IOCTL opcode as the */ +/* event id after the IOCTL command has been executed. */ + +/* This event is generated after a media stream session is opened. */ +#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6 + +/* This event is generated after a media stream session is closed. */ +#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7 + +/* Asyncronous buffer consumption. This event is generated after a */ +/* recived buffer is consumed during rendering or filled during */ +/* capture opeartion. */ +#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8 + +/* This event is generated when rendering operation is starving for */ +/* data. In order to avoid audio loss at the end of a plauback, the */ +/* client should wait for this event before issuing the close command. */ +#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9 + +/* This event is generated during capture operation when there are no */ +/* buffers available to copy the captured audio data */ +#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da + +/* This asynchronous event is generated as a result of an input */ +/* sample rate change and/or channel mode change detected by the */ +/* decoder. The event payload data is an array of 2 uint32 */ +/* values containing the sample rate in Hz and channel mode. */ +#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329 + +struct adsp_event_hdr { + u32 evt_handle; /* DAL common header */ + u32 evt_cookie; + u32 evt_length; + + u32 src; /* "source" audio address */ + u32 dst; /* "destination" audio address */ + + u32 event_id; + u32 response_type; + u32 seq_number; + + u32 context; /* opaque to DSP */ + u32 data; + + u32 status; +} __attribute__ ((packed)); + +struct adsp_buffer_event { + struct adsp_event_hdr hdr; + struct adsp_audio_buffer buffer; +} __attribute__ ((packed)); + + +/* ---- audio device IDs ---- */ + +/* Device direction Rx/Tx flag */ +#define ADSP_AUDIO_RX_DEVICE 0x00 +#define ADSP_AUDIO_TX_DEVICE 0x01 + +/* Default RX or TX device */ +#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679 + +/* Source (TX) devices */ +#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d +#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512 +#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518 +#define ADSP_AUDIO_DEVICE_ID_AUXPCM_TX 0x1081518 +#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b +#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3 + +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC 0x108f9c5 +#define ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC 0x108f9c3 + +/* Special loopback pseudo device to be paired with an RX device */ +/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */ +#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2 + +/* Sink (RX) devices */ +#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509 +#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519 +#define ADSP_AUDIO_DEVICE_ID_AUXPCM_RX 0x1081519 +#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c +#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4 +#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512 + +/* BT A2DP playback device. */ +/* This device must be paired with */ +/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */ +/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */ +#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a + +/* Voice Destination identifier - specifically used for */ +/* controlling Voice module from the Device Control Session */ +#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c + +/* Audio device usage types. */ +/* This is a bit mask to determine which topology to use in the */ +/* device session */ +#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01 +#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02 +#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10 +#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20 +#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40 + +/* ADSP audio driver return codes */ +#define ADSP_AUDIO_STATUS_SUCCESS 0 +#define ADSP_AUDIO_STATUS_EUNSUPPORTED 20 + +#endif diff --git a/arch/arm/mach-msm/qdsp6/dal_audio_format.h b/arch/arm/mach-msm/qdsp6/dal_audio_format.h new file mode 100644 index 00000000000..638269398ea --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_audio_format.h @@ -0,0 +1,270 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ADSP_AUDIO_MEDIA_FORMAT_H +#define __ADSP_AUDIO_MEDIA_FORMAT_H + + + +/* Supported audio media formats */ + +/* format block in shmem */ +#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78 +/* adsp_audio_format_raw_pcm type */ +#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd +/* adsp_audio_format_raw_pcm type */ +#define ADSP_AUDIO_FORMAT_DTMF 0x01087725 +/* adsp_audio_format_adpcm type */ +#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff +/* Yamaha PCM format */ +#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07 +/* ISO/IEC 11172 */ +#define ADSP_AUDIO_FORMAT_MP3 0x0103d308 +/* ISO/IEC 14496 */ +#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1 +/* AMR-NB audio in FS format */ +#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c +/* AMR-WB audio in FS format */ +#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e +/* QCELP 13k, IS733 */ +#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a +/* EVRC 8k, IS127 */ +#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89 +/* EVRC-B 8k, 4GV */ +#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3 +/* MIDI command stream */ +#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300 +/* A2DP SBC stream */ +#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8 +/* Version 10 Professional */ +#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92 +/* Version 9 Starndard */ +#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430 +/* AMR WideBand Plus */ +#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da +/* AC3 Decoder */ +#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9 + + +/* Not yet supported audio media formats */ + + + +/* ISO/IEC 13818 */ +#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309 +/* 3GPP TS 26.101 Sec 4.0 */ +#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305 +/* 3GPP TS 26.101 Annex A */ +#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31 +/* 3GPP TS 26.201 */ +#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306 +/* 3GPP TS 26.201 */ +#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d +/* G.711 */ +#define ADSP_AUDIO_FORMAT_G711 0x0106201d +/* QCELP 8k, IS96A */ +#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29 +/* Version 1 codec */ +#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b +/* Version 2, 7 & 8 codec */ +#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c +/* Version 9 Professional codec */ +#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d +/* Version 9 Voice codec */ +#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e +/* Version 9 Lossless codec */ +#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f +/* Real Media content, low-bitrate */ +#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f +/* Real Media content */ +#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e + + +/* For all of the audio formats, unless specified otherwise, */ +/* the following apply: */ +/* Format block bits are arranged in bytes and words in little-endian */ +/* order, i.e., least-significant bit first and least-significant */ +/* byte first. */ + + + +/* AAC Format Block. */ + +/* AAC format block consist of a format identifier followed by */ +/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */ + +/* The following AAC format identifiers are supported */ +#define ADSP_AUDIO_AAC_ADTS 0x010619cf +#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0 +#define ADSP_AUDIO_AAC_LOAS 0x010619d1 +#define ADSP_AUDIO_AAC_ADIF 0x010619d2 +#define ADSP_AUDIO_AAC_RAW 0x010619d3 +#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb + + +#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd +#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce + +/* Maxmum number of bytes allowed in a format block */ +#define ADSP_AUDIO_FORMAT_DATA_MAX 16 + + +struct adsp_audio_no_payload_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* no payload for this format type */ +} __attribute__ ((packed)); + + +/* For convenience, to be used as a standard format block */ +/* for various media types that don't need a unique format block */ +/* ie. PCM, DTMF, etc. */ +struct adsp_audio_standard_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 channels; + u16 bits_per_sample; + u32 sampling_rate; + u8 is_signed; + u8 is_interleaved; +} __attribute__ ((packed)); + + + +/* ADPCM format block */ +struct adsp_audio_adpcm_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 channels; + u16 bits_per_sample; + u32 sampling_rate; + u8 is_signed; + u8 is_interleaved; + u32 block_size; +} __attribute__ ((packed)); + + +/* MIDI format block */ +struct adsp_audio_midi_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 sampling_rate; + u16 channels; + u16 mode; +} __attribute__ ((packed)); + + +/* G711 format block */ +struct adsp_audio_g711_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 companding; +} __attribute__ ((packed)); + + +struct adsp_audio_wma_pro_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 format_tag; + u16 channels; + u32 samples_per_sec; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 channel_mask; + u16 encode_opt; + u16 advanced_encode_opt; + u32 advanced_encode_opt2; + u32 drc_peak_reference; + u32 drc_peak_target; + u32 drc_average_reference; + u32 drc_average_target; +} __attribute__ ((packed)); + + +struct adsp_audio_amrwb_plus_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 size; + u32 version; + u32 channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_format; + u32 amr_isf_index; +} __attribute__ ((packed)); + + +/* Binary Byte Stream Format */ +/* Binary format type that defines a byte stream, */ +/* can be used to specify any format (ie. AAC) */ +struct adsp_audio_binary_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + /* number of bytes set in byte stream */ + u32 num_bytes; + /* Byte stream binary data */ + u8 data[ADSP_AUDIO_FORMAT_DATA_MAX]; +} __attribute__ ((packed)); + + +struct adsp_audio_shared_memory_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* Number of bytes in shared memory */ + u32 len; + /* Phyisical address to data in shared memory */ + u32 address; +} __attribute__ ((packed)); + + +/* Union of all format types */ +union adsp_audio_format { + /* Basic format block with no payload */ + struct adsp_audio_no_payload_format no_payload; + /* Generic format block PCM, DTMF */ + struct adsp_audio_standard_format standard; + /* ADPCM format block */ + struct adsp_audio_adpcm_format adpcm; + /* MIDI format block */ + struct adsp_audio_midi_format midi; + /* G711 format block */ + struct adsp_audio_g711_format g711; + /* WmaPro format block */ + struct adsp_audio_wma_pro_format wma_pro; + /* WmaPro format block */ + struct adsp_audio_amrwb_plus_format amrwb_plus; + /* binary (byte stream) format block, used for AAC */ + struct adsp_audio_binary_format binary; + /* format block in shared memory */ + struct adsp_audio_shared_memory_format shared_mem; +}; + +#endif + diff --git a/arch/arm/mach-msm/qdsp6/dsp_debug.c b/arch/arm/mach-msm/qdsp6/dsp_debug.c new file mode 100644 index 00000000000..922f8cd39cd --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dsp_debug.c @@ -0,0 +1,179 @@ +/* arch/arm/mach-msm/qdsp6/dsp_dump.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static wait_queue_head_t dsp_wait; +static int dsp_has_crashed; +static int dsp_wait_count; + +static atomic_t dsp_crash_count = ATOMIC_INIT(0); + +void q6audio_dsp_not_responding(void) +{ + + if (atomic_add_return(1, &dsp_crash_count) != 1) { + pr_err("q6audio_dsp_not_responding() - parking additional crasher...\n"); + for (;;) + msleep(1000); + } + if (dsp_wait_count) { + dsp_has_crashed = 1; + wake_up(&dsp_wait); + + while (dsp_has_crashed != 2) + wait_event(dsp_wait, dsp_has_crashed == 2); + } else { + pr_err("q6audio_dsp_not_responding() - no waiter?\n"); + } + BUG(); +} + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t dsp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + if (!strcmp(cmd, "wait-for-crash")) { + while (!dsp_has_crashed) { + int res; + dsp_wait_count++; + res = wait_event_interruptible(dsp_wait, dsp_has_crashed); + if (res < 0) { + dsp_wait_count--; + return res; + } + } +#if defined(CONFIG_MACH_MAHIMAHI) + /* assert DSP NMI */ + msm_proc_comm(PCOM_CUSTOMER_CMD1, 0, 0); + msleep(250); +#endif + } else if (!strcmp(cmd, "boom")) { + q6audio_dsp_not_responding(); + } else if (!strcmp(cmd, "continue-crash")) { + dsp_has_crashed = 2; + wake_up(&dsp_wait); + } else { + pr_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__, + __func__, cmd); + } + + return count; +} + +#define DSP_RAM_BASE 0x2E800000 +#define DSP_RAM_SIZE 0x01800000 + +static unsigned copy_ok_count; + +static ssize_t dsp_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + size_t actual = 0; + size_t mapsize = PAGE_SIZE; + unsigned addr; + void __iomem *ptr; + + if (*pos >= DSP_RAM_SIZE) + return 0; + + if (*pos & (PAGE_SIZE - 1)) + return -EINVAL; + + addr = (*pos + DSP_RAM_BASE); + + /* don't blow up if we're unaligned */ + if (addr & (PAGE_SIZE - 1)) + mapsize *= 2; + + while (count >= PAGE_SIZE) { + ptr = ioremap(addr, mapsize); + if (!ptr) { + pr_err("[%s:%s] map error @ %x\n", __MM_FILE__, + __func__, addr); + return -EFAULT; + } + if (copy_to_user(buf, ptr, PAGE_SIZE)) { + iounmap(ptr); + pr_err("[%s:%s] copy error @ %p\n", __MM_FILE__, + __func__, buf); + return -EFAULT; + } + copy_ok_count += PAGE_SIZE; + iounmap(ptr); + addr += PAGE_SIZE; + buf += PAGE_SIZE; + actual += PAGE_SIZE; + count -= PAGE_SIZE; + } + + *pos += actual; + return actual; +} + +static int dsp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations dsp_fops = { + .owner = THIS_MODULE, + .open = dsp_open, + .read = dsp_read, + .write = dsp_write, + .release = dsp_release, +}; + +static struct miscdevice dsp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dsp_debug", + .fops = &dsp_fops, +}; + + +static int __init dsp_init(void) +{ + init_waitqueue_head(&dsp_wait); + return misc_register(&dsp_misc); +} + +device_initcall(dsp_init); diff --git a/arch/arm/mach-msm/qdsp6/dtmf.c b/arch/arm/mach-msm/qdsp6/dtmf.c new file mode 100644 index 00000000000..cf2748807be --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dtmf.c @@ -0,0 +1,126 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct dtmf { + struct mutex lock; + struct audio_client *ac; + struct msm_dtmf_config cfg; +}; + +static long dtmf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dtmf *dtmf = file->private_data; + int rc = 0; + + mutex_lock(&dtmf->lock); + switch (cmd) { + + case AUDIO_START: { + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (dtmf->ac) { + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + rc = -EBUSY; + } else { + dtmf->ac = q6audio_open_dtmf(48000, 2, 0); + if (!dtmf->ac) + rc = -ENOMEM; + } + break; + } + case AUDIO_PLAY_DTMF: { + rc = copy_from_user((void *)&dtmf->cfg, (void *)arg, + sizeof(struct msm_dtmf_config)); + + pr_debug("[%s:%s] PLAY_DTMF: high = %d, low = %d\n", + __MM_FILE__, __func__, dtmf->cfg.dtmf_hi, + dtmf->cfg.dtmf_low); + rc = q6audio_play_dtmf(dtmf->ac, dtmf->cfg.dtmf_hi, + dtmf->cfg.dtmf_low, dtmf->cfg.duration, + dtmf->cfg.rx_gain); + if (rc) { + pr_err("[%s:%s] DTMF_START failed\n", __MM_FILE__, + __func__); + break; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&dtmf->lock); + + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc) ; + return rc; +} + +static int dtmf_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + struct dtmf *dtmf; + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + dtmf = kzalloc(sizeof(struct dtmf), GFP_KERNEL); + + if (!dtmf) + return -ENOMEM; + + mutex_init(&dtmf->lock); + + file->private_data = dtmf; + return rc; +} + +static int dtmf_release(struct inode *inode, struct file *file) +{ + struct dtmf *dtmf = file->private_data; + if (dtmf->ac) + q6audio_close(dtmf->ac); + kfree(dtmf); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return 0; +} + +static const struct file_operations dtmf_fops = { + .owner = THIS_MODULE, + .open = dtmf_open, + .release = dtmf_release, + .unlocked_ioctl = dtmf_ioctl, +}; + +struct miscdevice dtmf_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_dtmf", + .fops = &dtmf_fops, +}; + +static int __init dtmf_init(void) +{ + return misc_register(&dtmf_misc); +} + +device_initcall(dtmf_init); diff --git a/arch/arm/mach-msm/qdsp6/evrc_in.c b/arch/arm/mach-msm/qdsp6/evrc_in.c new file mode 100644 index 00000000000..9fc412bc81a --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/evrc_in.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "dal_audio_format.h" +#include + +#define EVRC_FC_BUFF_CNT 10 +#define EVRC_READ_TIMEOUT 2000 +struct evrc_fc_buff { + struct mutex lock; + int empty; + void *data; + int size; + int actual_size; +}; + +struct evrc_fc { + struct task_struct *task; + wait_queue_head_t fc_wq; + struct evrc_fc_buff fc_buff[EVRC_FC_BUFF_CNT]; + int buff_index; +}; + +struct evrc { + struct mutex lock; + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; + struct msm_voicerec_mode voicerec_mode; + struct evrc_fc *evrc_fc; +}; + + +static int q6_evrc_flowcontrol(void *data) +{ + struct audio_client *ac; + struct audio_buffer *ab; + struct evrc *evrc = data; + int buff_index = 0; + int xfer = 0; + struct evrc_fc *fc; + + + ac = evrc->audio_client; + fc = evrc->evrc_fc; + if (!ac) { + pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__); + return 0; + } + + while (!kthread_should_stop()) { + ab = ac->buf + ac->cpu_buf; + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__, + __func__, ab->data, ac->cpu_buf); + xfer = ab->actual_size; + + + mutex_lock(&(fc->fc_buff[buff_index].lock)); + if (!fc->fc_buff[buff_index].empty) { + pr_err("[%s:%s] flow control buffer[%d] not read!\n", + __MM_FILE__, __func__, buff_index); + } + + if (fc->fc_buff[buff_index].size < xfer) { + pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__, + __func__, buff_index); + memcpy(fc->fc_buff[buff_index].data, ab->data, + fc->fc_buff[buff_index].size); + fc->fc_buff[buff_index].empty = 0; + fc->fc_buff[buff_index].actual_size = + fc->fc_buff[buff_index].size; + } else { + memcpy(fc->fc_buff[buff_index].data, ab->data, xfer); + fc->fc_buff[buff_index].empty = 0; + fc->fc_buff[buff_index].actual_size = xfer; + } + mutex_unlock(&(fc->fc_buff[buff_index].lock)); + /*wake up client, if any*/ + wake_up(&fc->fc_wq); + + buff_index++; + if (buff_index >= EVRC_FC_BUFF_CNT) + buff_index = 0; + + ab->used = 1; + + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + return 0; +} +static long q6_evrc_in_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct evrc *evrc = file->private_data; + int rc = 0; + int i = 0; + struct evrc_fc *fc; + int size = 0; + + mutex_lock(&evrc->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: + pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__); + break; + case AUDIO_GET_STATS: + { + struct msm_audio_stats stats; + pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__); + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + case AUDIO_START: + { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else { + if (copy_from_user(&acdb_id, (void *) arg, + sizeof(acdb_id))) { + rc = -EFAULT; + break; + } + } + if (evrc->audio_client) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } else { + evrc->audio_client = q6audio_open_qcp( + evrc->str_cfg.buffer_size, + evrc->cfg.min_bit_rate, + evrc->cfg.max_bit_rate, + evrc->voicerec_mode.rec_mode, + ADSP_AUDIO_FORMAT_EVRC_FS, + acdb_id); + + if (!evrc->audio_client) { + pr_err("[%s:%s] evrc open session failed\n", + __MM_FILE__, __func__); + kfree(evrc); + rc = -ENOMEM; + break; + } + } + + /*allocate flow control buffers*/ + fc = evrc->evrc_fc; + size = evrc->str_cfg.buffer_size; + for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) { + mutex_init(&(fc->fc_buff[i].lock)); + fc->fc_buff[i].empty = 1; + fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL); + if (fc->fc_buff[i].data == NULL) { + pr_err("[%s:%s] No memory for FC buffers\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + goto fc_fail; + } + fc->fc_buff[i].size = size; + fc->fc_buff[i].actual_size = 0; + } + + /*create flow control thread*/ + fc->task = kthread_run(q6_evrc_flowcontrol, + evrc, "evrc_flowcontrol"); + if (IS_ERR(fc->task)) { + rc = PTR_ERR(fc->task); + pr_err("[%s:%s] error creating flow control thread\n", + __MM_FILE__, __func__); + goto fc_fail; + } + break; +fc_fail: + /*free flow control buffers*/ + --i; + for (; i >= 0; i--) { + kfree(fc->fc_buff[i].data); + fc->fc_buff[i].data = NULL; + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_INCALL: { + pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__); + if (copy_from_user(&evrc->voicerec_mode, + (void *)arg, sizeof(struct msm_voicerec_mode))) + rc = -EFAULT; + + if (evrc->voicerec_mode.rec_mode != AUDIO_FLAG_READ + && evrc->voicerec_mode.rec_mode != + AUDIO_FLAG_INCALL_MIXED) { + evrc->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + } + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &evrc->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + + pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", + __MM_FILE__, __func__, evrc->str_cfg.buffer_size, + evrc->str_cfg.buffer_count); + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&evrc->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + + pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", + __MM_FILE__, __func__, evrc->str_cfg.buffer_size, + evrc->str_cfg.buffer_count); + + if (evrc->str_cfg.buffer_size < 23) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + + if (evrc->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + break; + case AUDIO_SET_EVRC_ENC_CONFIG: + if (copy_from_user(&evrc->cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + pr_debug("[%s:%s] SET_EVRC_ENC_CONFIG\n", __MM_FILE__, + __func__); + + if (evrc->cfg.min_bit_rate > 4 || evrc->cfg.min_bit_rate < 1) { + pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + if (evrc->cfg.max_bit_rate > 4 || evrc->cfg.max_bit_rate < 1) { + pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + case AUDIO_GET_EVRC_ENC_CONFIG: + if (copy_to_user((void *) arg, &evrc->cfg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_EVRC_ENC_CONFIG\n", __MM_FILE__, + __func__); + break; + + default: + rc = -EINVAL; + } + + mutex_unlock(&evrc->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int q6_evrc_in_open(struct inode *inode, struct file *file) +{ + struct evrc *evrc; + struct evrc_fc *fc; + int i; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + evrc = kmalloc(sizeof(struct evrc), GFP_KERNEL); + if (evrc == NULL) { + pr_err("[%s:%s] Could not allocate memory for evrc driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&evrc->lock); + file->private_data = evrc; + evrc->audio_client = NULL; + evrc->str_cfg.buffer_size = 23; + evrc->str_cfg.buffer_count = 2; + evrc->cfg.cdma_rate = CDMA_RATE_FULL; + evrc->cfg.min_bit_rate = 1; + evrc->cfg.max_bit_rate = 4; + evrc->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + + evrc->evrc_fc = kmalloc(sizeof(struct evrc_fc), GFP_KERNEL); + if (evrc->evrc_fc == NULL) { + pr_err("[%s:%s] Could not allocate memory for evrc_fc\n", + __MM_FILE__, __func__); + kfree(evrc); + return -ENOMEM; + } + fc = evrc->evrc_fc; + fc->task = NULL; + fc->buff_index = 0; + for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) { + fc->fc_buff[i].data = NULL; + fc->fc_buff[i].size = 0; + fc->fc_buff[i].actual_size = 0; + } + /*initialize wait queue head*/ + init_waitqueue_head(&fc->fc_wq); + return 0; +} + +static ssize_t q6_evrc_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + const char __user *start = buf; + struct evrc *evrc = file->private_data; + struct evrc_fc *fc; + int xfer = 0; + int res = 0; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + mutex_lock(&evrc->lock); + ac = evrc->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + fc = evrc->evrc_fc; + while (count > xfer) { + /*wait for buffer to full*/ + if (fc->fc_buff[fc->buff_index].empty != 0) { + res = wait_event_interruptible_timeout(fc->fc_wq, + (fc->fc_buff[fc->buff_index].empty == 0), + msecs_to_jiffies(EVRC_READ_TIMEOUT)); + + pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__, + __func__, fc->buff_index); + if (res == 0) { + pr_err("[%s:%s] Timeout!\n", __MM_FILE__, + __func__); + res = -ETIMEDOUT; + goto fail; + } else if (res < 0) { + pr_err("[%s:%s] Returning on Interrupt\n", + __MM_FILE__, __func__); + goto fail; + } + } + /*lock the buffer*/ + mutex_lock(&(fc->fc_buff[fc->buff_index].lock)); + xfer = fc->fc_buff[fc->buff_index].actual_size; + + if (xfer > count) { + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + pr_err("[%s:%s] read failed! byte count too small\n", + __MM_FILE__, __func__); + res = -EINVAL; + goto fail; + } + + if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) { + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + pr_err("[%s:%s] copy_to_user failed at index %d\n", + __MM_FILE__, __func__, fc->buff_index); + res = -EFAULT; + goto fail; + } + buf += xfer; + count -= xfer; + + fc->fc_buff[fc->buff_index].empty = 1; + fc->fc_buff[fc->buff_index].actual_size = 0; + + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + ++(fc->buff_index); + if (fc->buff_index >= EVRC_FC_BUFF_CNT) + fc->buff_index = 0; + } + res = buf - start; + +fail: + mutex_unlock(&evrc->lock); + + return res; +} + +static int q6_evrc_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct evrc *evrc = file->private_data; + int i = 0; + struct evrc_fc *fc; + + mutex_lock(&evrc->lock); + fc = evrc->evrc_fc; + kthread_stop(fc->task); + fc->task = NULL; + /*free flow control buffers*/ + for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) { + kfree(fc->fc_buff[i].data); + fc->fc_buff[i].data = NULL; + } + kfree(fc); + if (evrc->audio_client) + rc = q6audio_close(evrc->audio_client); + mutex_unlock(&evrc->lock); + kfree(evrc); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return rc; +} + +static const struct file_operations q6_evrc_in_fops = { + .owner = THIS_MODULE, + .open = q6_evrc_in_open, + .read = q6_evrc_in_read, + .release = q6_evrc_in_release, + .unlocked_ioctl = q6_evrc_in_ioctl, +}; + +struct miscdevice q6_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &q6_evrc_in_fops, +}; + +static int __init q6_evrc_in_init(void) +{ + return misc_register(&q6_evrc_in_misc); +} + +device_initcall(q6_evrc_in_init); diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c new file mode 100644 index 00000000000..16f6204febb --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/mp3.c @@ -0,0 +1,249 @@ +/* arch/arm/mach-msm/qdsp6/mp3.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define BUFSZ (8192) +#define DMASZ (BUFSZ * 2) + +struct mp3 { + struct mutex lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; +}; + +static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct mp3 *mp3 = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&mp3->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: { + int vol; + pr_debug("[%s:%s] SET_VOLUME = %d\n", __MM_FILE__, + __func__, vol); + if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { + rc = -EFAULT; + break; + } + rc = q6audio_set_stream_volume(mp3->ac, vol); + break; + } + case AUDIO_START: { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) { + pr_info("[%s:%s] copy acdb_id from user failed\n", + __MM_FILE__, __func__); + rc = -EFAULT; + break; + } + if (mp3->ac) { + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + rc = -EBUSY; + } else { + mp3->ac = q6audio_open_mp3(BUFSZ, + mp3->sample_rate, mp3->channel_count, acdb_id); + if (!mp3->ac) { + pr_err("[%s:%s] mp3 open session failed\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + } + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (mp3->ac) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_CONFIG: buffsize = %d, samplerate = %d, \ + channelcount = %d\n", __MM_FILE__, __func__, + config.buffer_size, config.sample_rate, + config.channel_count); + if (config.channel_count < 1 || config.channel_count > 2) { + rc = -EINVAL; + pr_err("[%s:%s] invalid channelcount\n", __MM_FILE__, + __func__); + break; + } + mp3->sample_rate = config.sample_rate; + mp3->channel_count = config.channel_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = mp3->sample_rate; + config.channel_count = mp3->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + pr_debug("[%s:%s] GET_CONFIG: buffsize = %d, samplerate = %d, \ + channelcount = %d\n", __MM_FILE__, __func__, + config.buffer_size, config.sample_rate, + config.channel_count); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&mp3->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int mp3_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + struct mp3 *mp3; + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL); + + if (!mp3) + return -ENOMEM; + + mutex_init(&mp3->lock); + mp3->channel_count = 2; + mp3->sample_rate = 44100; + + file->private_data = mp3; + return rc; +} + +static ssize_t mp3_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct mp3 *mp3 = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + if (!mp3->ac) + mp3_ioctl(file, AUDIO_START, 0); + + ac = mp3->ac; + if (!ac) + return -ENODEV; + + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + pr_debug("[%s:%s] ab->data = %p, ac->cpu_buf = %d\n", + __MM_FILE__, __func__, ab->data, ac->cpu_buf); + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = xfer; + q6audio_write(ac, ab); + ac->cpu_buf ^= 1; + } + + return buf - start; +} + +static int mp3_fsync(struct file *f, int datasync) +{ + struct mp3 *mp3 = f->private_data; + if (mp3->ac) + return q6audio_async(mp3->ac); + return -ENODEV; +} + +static int mp3_release(struct inode *inode, struct file *file) +{ + struct mp3 *mp3 = file->private_data; + if (mp3->ac) + q6audio_mp3_close(mp3->ac); + kfree(mp3); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return 0; +} + +static struct file_operations mp3_fops = { + .owner = THIS_MODULE, + .open = mp3_open, + .write = mp3_write, + .fsync = mp3_fsync, + .release = mp3_release, + .unlocked_ioctl = mp3_ioctl, +}; + +struct miscdevice mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &mp3_fops, +}; + +static int __init mp3_init(void) { + return misc_register(&mp3_misc); +} + +device_initcall(mp3_init); diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c new file mode 100644 index 00000000000..f28553665cf --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c @@ -0,0 +1,1510 @@ +/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* +#define DEBUG_TRACE_VDEC +#define DEBUG +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "dal.h" + +#define DALDEVICEID_VDEC_DEVICE 0x02000026 +#define DALDEVICEID_VDEC_PORTNAME "DAL_AQ_VID" + +#define VDEC_INTERFACE_VERSION 0x00020000 + +#define MAJOR_MASK 0xFFFF0000 +#define MINOR_MASK 0x0000FFFF + +#define VDEC_GET_MAJOR_VERSION(version) (((version)&MAJOR_MASK)>>16) + +#define VDEC_GET_MINOR_VERSION(version) ((version)&MINOR_MASK) + +#ifdef DEBUG_TRACE_VDEC +#define TRACE(fmt,x...) \ + do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) +#else +#define TRACE(fmt,x...) do { } while (0) +#endif + +#define YAMATO_COLOR_FORMAT 0x02 +#define MAX_Q6_LOAD ((720*1280)/256) /* 720p */ +#define MAX_Q6_LOAD_YAMATO ((736*1280)/256) +#define MAX_Q6_LOAD_VP6 ((800*480)/256) + +#define VDEC_MAX_PORTS 4 + +/* + *why magic number 300? + + *the Maximum size of the DAL payload is 512 bytes according to DAL protocol + *Initialize call to QDSP6 from scorpion need to send sequence header as part of + *the DAL payload. DAL payload to initialize contains the following + + *1) configuration data- 52 bytes 2) length field of config data - 4 bytes + *3) sequence header data ( that is from the bit stream) + *4) length field for sequence header - 4 bytes + *5) length field for output structure - 4 bytes + + *that left with 512 - 68 = 448 bytes. It is unusual that we get a sequence + *header with such a big length unless the bit stream has multiple sequence + *headers.We estimated 300 is good enough which gives enough room for rest + *of the payload and even reserves some space for future payload. + */ + +#define VDEC_MAX_SEQ_HEADER_SIZE 300 + +char *Q6Portnames[] = { +"DAL_AQ_VID_0", +"DAL_AQ_VID_1", +"DAL_AQ_VID_2", +"DAL_AQ_VID_3" +}; + + + +#define DALDEVICEID_VDEC_DEVICE_0 0x020000D2 +#define DALDEVICEID_VDEC_DEVICE_1 0x020000D3 +#define DALDEVICEID_VDEC_DEVICE_2 0x020000D4 +#define DALDEVICEID_VDEC_DEVICE_3 0x020000D5 +#define DALDEVICEID_VDEC_DEVICE_4 0x020000D6 +#define DALDEVICEID_VDEC_DEVICE_5 0x020000D7 +#define DALDEVICEID_VDEC_DEVICE_6 0x020000D8 +#define DALDEVICEID_VDEC_DEVICE_7 0x020000D9 +#define DALDEVICEID_VDEC_DEVICE_8 0x020000DA +#define DALDEVICEID_VDEC_DEVICE_9 0x020000DB +#define DALDEVICEID_VDEC_DEVICE_10 0x020000DC +#define DALDEVICEID_VDEC_DEVICE_11 0x020000DD +#define DALDEVICEID_VDEC_DEVICE_12 0x020000DE +#define DALDEVICEID_VDEC_DEVICE_13 0x020000DF +#define DALDEVICEID_VDEC_DEVICE_14 0x020000E0 +#define DALDEVICEID_VDEC_DEVICE_15 0x020000E1 +#define DALDEVICEID_VDEC_DEVICE_16 0x020000E2 +#define DALDEVICEID_VDEC_DEVICE_17 0x020000E3 +#define DALDEVICEID_VDEC_DEVICE_18 0x020000E4 +#define DALDEVICEID_VDEC_DEVICE_19 0x020000E5 +#define DALDEVICEID_VDEC_DEVICE_20 0x020000E6 +#define DALDEVICEID_VDEC_DEVICE_21 0x020000E7 +#define DALDEVICEID_VDEC_DEVICE_22 0x020000E8 +#define DALDEVICEID_VDEC_DEVICE_23 0x020000E9 +#define DALDEVICEID_VDEC_DEVICE_24 0x020000EA +#define DALDEVICEID_VDEC_DEVICE_25 0x020000EB +#define DALDEVICEID_VDEC_DEVICE_26 0x020000EC +#define DALDEVICEID_VDEC_DEVICE_27 0x020000ED +#define DALDEVICEID_VDEC_DEVICE_28 0x020000EE +#define DALDEVICEID_VDEC_DEVICE_29 0x020000EF +#define DALDEVICEID_VDEC_DEVICE_30 0x020000F0 +#define DALDEVICEID_VDEC_DEVICE_31 0x020000F1 + +#define DALVDEC_MAX_DEVICE_IDS 32 + + +static int numOfPorts; + + +static char loadOnPorts[VDEC_MAX_PORTS]; + +static char deviceIdRegistry[DALVDEC_MAX_DEVICE_IDS]; + + +#define VDEC_DEVID_FREE 0 +#define VDEC_DEVID_OCCUPIED 1 + +#define MAX_SUPPORTED_INSTANCES 6 + +#define MAKEFOURCC(ch0, ch1, ch2, ch3) ((unsigned int)(unsigned char)(ch0) | \ + ((unsigned int)(unsigned char)(ch1) << 8) | \ + ((unsigned int)(unsigned char)(ch2) << 16) | \ + ((unsigned int)(unsigned char)(ch3) << 24)) + +#define FOURCC_MPEG4 MAKEFOURCC('m', 'p', '4', 'v') +#define FOURCC_H263 MAKEFOURCC('h', '2', '6', '3') +#define FOURCC_H264 MAKEFOURCC('h', '2', '6', '4') +#define FOURCC_VC1 MAKEFOURCC('w', 'm', 'v', '3') +#define FOURCC_DIVX MAKEFOURCC('D', 'I', 'V', 'X') +#define FOURCC_SPARK MAKEFOURCC('F', 'L', 'V', '1') +#define FOURCC_VP6 MAKEFOURCC('V', 'P', '6', '0') + +/* static struct vdec_data *multiInstances[MAX_SUPPORTED_INSTANCES];*/ + +static int totalPlaybackQ6load; +static int totalTnailQ6load; + +#define FLAG_THUMBNAIL_MODE 0x8 +#define MAX_TNAILS 3 + +#define TRUE 1 +#define FALSE 0 + +enum { + VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API, + VDEC_DALRPC_SETBUFFERS, + VDEC_DALRPC_FREEBUFFERS, + VDEC_DALRPC_QUEUE, + VDEC_DALRPC_SIGEOFSTREAM, + VDEC_DALRPC_FLUSH, + VDEC_DALRPC_REUSEFRAMEBUFFER, + VDEC_DALRPC_GETDECATTRIBUTES, + VDEC_DALRPC_SUSPEND, + VDEC_DALRPC_RESUME, + VDEC_DALRPC_INITIALIZE_00, + VDEC_DALRPC_GETINTERNALBUFFERREQ, + VDEC_DALRPC_SETBUFFERS_00, + VDEC_DALRPC_FREEBUFFERS_00, + VDEC_DALRPC_GETPROPERTY, + VDEC_DALRPC_SETPROPERTY, + VDEC_DALRPC_GETDECATTRIBUTES_00, + VDEC_DALRPC_PERFORMANCE_CHANGE_REQUEST +}; + +enum { + VDEC_ASYNCMSG_DECODE_DONE = 0xdec0de00, + VDEC_ASYNCMSG_REUSE_FRAME, +}; + +struct vdec_init_cfg { + u32 decode_done_evt; + u32 reuse_frame_evt; + struct vdec_config cfg; +}; + +struct vdec_buffer_status { + u32 data; + u32 status; +}; + +#define VDEC_MSG_MAX 128 + +struct vdec_msg_list { + struct list_head list; + struct vdec_msg vdec_msg; +}; + +struct vdec_mem_info { + u32 buf_type; + u32 id; + unsigned long phys_addr; + unsigned long len; + struct file *file; +}; + +struct vdec_mem_list { + struct list_head list; + struct vdec_mem_info mem; +}; + +struct videoStreamDetails{ + int height; + int width; + unsigned int fourcc; + int Q6usage; + bool isThisTnail; + bool isTnailGranted; +}; + +struct vdec_data { + struct dal_client *vdec_handle; + unsigned int Q6deviceId; + struct videoStreamDetails streamDetails; + struct list_head vdec_msg_list_head; + struct list_head vdec_msg_list_free; + wait_queue_head_t vdec_msg_evt; + spinlock_t vdec_list_lock; + struct list_head vdec_mem_list_head; + spinlock_t vdec_mem_list_lock; + int mem_initialized; + int running; + int close_decode; +}; + +static struct class *driver_class; +static dev_t vdec_device_no; +static struct cdev vdec_cdev; +static int ref_cnt; +static DEFINE_MUTEX(vdec_ref_lock); + +static DEFINE_MUTEX(idlecount_lock); + +static DEFINE_MUTEX(vdec_rm_lock); + +static int idlecount; +static struct wake_lock wakelock; +static struct pm_qos_request pm_qos_req; + +static void prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + pm_qos_update_request(&pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + wake_lock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static void allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&wakelock); + pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE); + } + mutex_unlock(&idlecount_lock); +} + +static inline int vdec_check_version(u32 client, u32 server) +{ + int ret = -EINVAL; + if ((VDEC_GET_MAJOR_VERSION(client) == VDEC_GET_MAJOR_VERSION(server)) + && (VDEC_GET_MINOR_VERSION(client) <= + VDEC_GET_MINOR_VERSION(server))) + ret = 0; + return ret; +} + +static int vdec_get_msg(struct vdec_data *vd, void *msg) +{ + struct vdec_msg_list *l; + unsigned long flags; + int ret = 0; + + if (!vd->running) + return -EPERM; + + spin_lock_irqsave(&vd->vdec_list_lock, flags); + list_for_each_entry_reverse(l, &vd->vdec_msg_list_head, list) { + if (copy_to_user(msg, &l->vdec_msg, sizeof(struct vdec_msg))) + pr_err("vdec_get_msg failed to copy_to_user!\n"); + if (l->vdec_msg.id == VDEC_MSG_REUSEINPUTBUFFER) + TRACE("reuse_input_buffer %d\n", l->vdec_msg.buf_id); + else if (l->vdec_msg.id == VDEC_MSG_FRAMEDONE) + TRACE("frame_done (stat=%d)\n", + l->vdec_msg.vfr_info.status); + else + TRACE("unknown msg (msgid=%d)\n", l->vdec_msg.id); + list_del(&l->list); + list_add(&l->list, &vd->vdec_msg_list_free); + ret = 1; + break; + } + spin_unlock_irqrestore(&vd->vdec_list_lock, flags); + + if (vd->close_decode) + ret = 1; + + return ret; +} + +static void vdec_put_msg(struct vdec_data *vd, struct vdec_msg *msg) +{ + struct vdec_msg_list *l; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&vd->vdec_list_lock, flags); + list_for_each_entry(l, &vd->vdec_msg_list_free, list) { + memcpy(&l->vdec_msg, msg, sizeof(struct vdec_msg)); + list_del(&l->list); + list_add(&l->list, &vd->vdec_msg_list_head); + found = 1; + break; + } + spin_unlock_irqrestore(&vd->vdec_list_lock, flags); + + if (found) + wake_up(&vd->vdec_msg_evt); + else + pr_err("vdec_put_msg can't find free list!\n"); +} + +static struct vdec_mem_list *vdec_get_mem_from_list(struct vdec_data *vd, + u32 pmem_id, u32 buf_type) +{ + struct vdec_mem_list *l; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&vd->vdec_mem_list_lock, flags); + list_for_each_entry(l, &vd->vdec_mem_list_head, list) { + if (l->mem.buf_type == buf_type && l->mem.id == pmem_id) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags); + + if (found) + return l; + else + return NULL; + +} +static int vdec_setproperty(struct vdec_data *vd, void *argp) +{ + struct vdec_property_info property; + int res; + + if (copy_from_user(&property, argp, sizeof(struct vdec_property_info))) + return -1; + + res = dal_call_f6(vd->vdec_handle, VDEC_DALRPC_SETPROPERTY, + property.id, &(property.property), sizeof(union vdec_property)); + if (res) + TRACE("Set Property failed"); + else + TRACE("Set Property succeeded"); + return res; +} +static int vdec_getproperty(struct vdec_data *vd, void *argp) +{ + int res; + union vdec_property property = {0}; + + res = dal_call_f11(vd->vdec_handle, VDEC_DALRPC_GETPROPERTY, + ((struct vdec_property_info *)argp)->id, &property, + sizeof(union vdec_property)); + + if (res) + TRACE("get Property failed"); + else + TRACE("get Property succeeded"); + + res = copy_to_user( + (&((struct vdec_property_info *)argp)->property), + &property, sizeof(property)); + + return res; +} +static int vdec_performance_change_request(struct vdec_data *vd, void* argp) +{ + u32 request_type; + int ret; + + ret = copy_from_user(&request_type, argp, sizeof(request_type)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + ret = dal_call_f0(vd->vdec_handle, + VDEC_DALRPC_PERFORMANCE_CHANGE_REQUEST, + request_type); + if (ret) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; + } + return ret; +} + +#ifdef TRACE_PORTS +static void printportsanddeviceids(void) +{ + int i; + + pr_err("\n\n%s:loadOnPorts", __func__); + for (i = 0; i < numOfPorts; i++) + pr_err("\t%d", loadOnPorts[i]); + + pr_err("\n\n"); + + pr_err("\n\n%s:Devids", __func__); + for (i = 0; i < DALVDEC_MAX_DEVICE_IDS; i++) + pr_err("Devid[%d]:%d\n", i, deviceIdRegistry[i]); + + + pr_err("\n\n"); +} +#endif /*TRACE_PORTS*/ + + +/* + * + * This method is used to get the number of ports supported on the Q6 + * + */ +static int vdec_get_numberofq6ports(void) +{ + struct dal_client *vdec_handle = NULL; + int retval = 0; + union vdec_property property = {0}; + + vdec_handle = dal_attach(DALDEVICEID_VDEC_DEVICE, + DALDEVICEID_VDEC_PORTNAME, 1, NULL, NULL); + if (!vdec_handle) { + pr_err("%s: failed to attach\n", __func__); + return 1;/* default setting */ + } + + retval = dal_call_f6(vdec_handle, VDEC_DALRPC_GETPROPERTY, + VDEC_NUM_DAL_PORTS, (void *)&property, sizeof(union vdec_property)); + if (retval) { + pr_err("%s: Q6get prperty failed\n", __func__); + return 1;/* default setting */ + } + + dal_detach(vdec_handle); + return property.num_dal_ports ; +} + + +/** + * This method is used to get the find the least loaded port and a corresponding + * free device id in that port. + * + * Prerequisite: vdec_open should have been called. + * + * @param[in] deviceid + * device id will be populated here. + * + * @param[in] portname + * portname will be populated here. + */ +static void vdec_get_next_portanddevid(int *deviceid, char **portname) +{ + + int i = 0; + int leastLoad = 0; + int leastLoadedIndex = 0; + + if (0 == numOfPorts) { + numOfPorts = vdec_get_numberofq6ports(); + pr_err("%s: Q6get numOfPorts %d\n", __func__, numOfPorts); + numOfPorts = 4; + /*fix: me currently hard coded to 4 as + *the Q6 getproperty is failing + */ + } + + if ((NULL == deviceid) || (NULL == portname)) + return; + else + *deviceid = 0; /* init value */ + + if (numOfPorts > 1) { + /* multi ports mode*/ + + /* find the least loaded port*/ + for (i = 1, leastLoad = loadOnPorts[0], leastLoadedIndex = 0; + i < numOfPorts; i++) { + if (leastLoad > loadOnPorts[i]) { + leastLoadedIndex = i; + leastLoad = loadOnPorts[i]; + } + } + + /* register the load */ + loadOnPorts[leastLoadedIndex]++; + *portname = Q6Portnames[leastLoadedIndex]; + + /* find a free device id corresponding to the port*/ + for (i = leastLoadedIndex; i < DALVDEC_MAX_DEVICE_IDS; + i += numOfPorts) { + if (VDEC_DEVID_FREE == deviceIdRegistry[i]) { + deviceIdRegistry[i] = VDEC_DEVID_OCCUPIED; + *deviceid = DALDEVICEID_VDEC_DEVICE_0 + i; + break; + } + } + +#ifdef TRACE_PORTS + printportsanddeviceids(); +#endif /*TRACE_PORTS*/ + } else if (1 == numOfPorts) { + /* single port mode */ + *deviceid = DALDEVICEID_VDEC_DEVICE; + *portname = DALDEVICEID_VDEC_PORTNAME; + } else if (numOfPorts <= 0) { + pr_err("%s: FATAL error numOfPorts cannot be \ + less than or equal to zero\n", __func__); + } + + +} + + +/** + * This method frees up the used dev id and decrements the port load. + * + */ + +static void vdec_freeup_portanddevid(int deviceid) +{ + + if (numOfPorts > 1) { + /* multi ports mode*/ + if (VDEC_DEVID_FREE == + deviceIdRegistry[deviceid - DALDEVICEID_VDEC_DEVICE_0]) + pr_err("device id cannot be already free\n"); + deviceIdRegistry[deviceid - DALDEVICEID_VDEC_DEVICE_0] = + VDEC_DEVID_FREE; + + loadOnPorts[(deviceid - DALDEVICEID_VDEC_DEVICE_0) + % numOfPorts]--; + + if (loadOnPorts[(deviceid - DALDEVICEID_VDEC_DEVICE_0) + % numOfPorts] < 0) + pr_err("Warning:load cannot be negative\n"); + + pr_err("dettaching on deviceid %x portname %s\n", deviceid, + Q6Portnames[(deviceid - DALDEVICEID_VDEC_DEVICE_0) + % numOfPorts]); + +#ifdef TRACE_PORTS + printportsanddeviceids(); +#endif /*TRACE_PORTS*/ + } else { + /*single port mode, nothing to be done here*/ + } + +} + + +/** + * This method validates whether a new instance can be houred or not. + * + */ +static int vdec_rm_checkWithRm(struct vdec_data *vdecInstance, + unsigned int color_format) +{ + + unsigned int maxQ6load = 0;/* in the units of macro blocks per second */ + unsigned int currentq6load = 0; + struct videoStreamDetails *streamDetails = &vdecInstance->streamDetails; + + + + if (streamDetails->isThisTnail) { + if (totalTnailQ6load < MAX_TNAILS) { + + totalTnailQ6load++; + streamDetails->isTnailGranted = TRUE; + pr_info("%s: thumbnail granted %d\n", __func__, + totalTnailQ6load); + return 0; + + } else { + + pr_err("%s: thumbnails load max this instance cannot \ + be supported\n", __func__); + streamDetails->isTnailGranted = FALSE; + return -ENOSPC; + + } + } + + /* calculate the Q6 percentage instance would need */ + if ((streamDetails->fourcc == FOURCC_MPEG4) || + (streamDetails->fourcc == FOURCC_H264) || + (streamDetails->fourcc == FOURCC_DIVX) || + (streamDetails->fourcc == FOURCC_VC1) || + (streamDetails->fourcc == FOURCC_SPARK) || + (streamDetails->fourcc == FOURCC_H263) + ){ + + /* is yamato color format, + Rounds the H & W --> mutiple of 32 */ + if (color_format == YAMATO_COLOR_FORMAT) + maxQ6load = MAX_Q6_LOAD_YAMATO; + else + maxQ6load = MAX_Q6_LOAD; /* 720p */ + + } else if (streamDetails->fourcc == FOURCC_VP6) { + + maxQ6load = MAX_Q6_LOAD_VP6; /* FWVGA */ + + } else { + + pr_err("%s: unknown fourcc %d maxQ6load %u\n", __func__, + streamDetails->fourcc, maxQ6load); + return -EINVAL; + + } + + currentq6load = ((streamDetails->height)*(streamDetails->width) / 256); + currentq6load = ((currentq6load * 100)/maxQ6load); + if ((currentq6load+totalPlaybackQ6load) > 100) { + /* reject this instance */ + pr_err("%s: too much Q6load [cur+tot] = [%d + %d] = %d", + __func__, currentq6load, totalPlaybackQ6load, + (currentq6load+totalPlaybackQ6load)); + pr_err("rejecting the instance,[WxH] = [%d x %d],color_fmt=0x%x\n", + streamDetails->width, streamDetails->height, color_format); + pr_err("VDEC_fmt=%s\n", (char *)(&streamDetails->fourcc)); + streamDetails->Q6usage = 0; + return -ENOSPC; + } + + totalPlaybackQ6load += currentq6load; + streamDetails->Q6usage = currentq6load; + + pr_info("%s: adding a load [%d%%] bringing total Q6load to [%d%%]\n", + __func__, currentq6load, totalPlaybackQ6load); + + return 0; +} + + +static int vdec_initialize(struct vdec_data *vd, void *argp) +{ + struct vdec_config_sps vdec_cfg_sps; + struct vdec_init_cfg vi_cfg; + struct vdec_buf_req vdec_buf_req; + struct u8 *header; + int ret = 0; + + ret = copy_from_user(&vdec_cfg_sps, + &((struct vdec_init *)argp)->sps_cfg, + sizeof(vdec_cfg_sps)); + + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + vi_cfg.decode_done_evt = VDEC_ASYNCMSG_DECODE_DONE; + vi_cfg.reuse_frame_evt = VDEC_ASYNCMSG_REUSE_FRAME; + memcpy(&vi_cfg.cfg, &vdec_cfg_sps.cfg, sizeof(struct vdec_config)); + + /* + * restricting the max value of the seq header + */ + if (vdec_cfg_sps.seq.len > VDEC_MAX_SEQ_HEADER_SIZE) + vdec_cfg_sps.seq.len = VDEC_MAX_SEQ_HEADER_SIZE; + + header = kmalloc(vdec_cfg_sps.seq.len, GFP_KERNEL); + if (!header) { + pr_err("%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + + ret = copy_from_user(header, + ((struct vdec_init *)argp)->sps_cfg.seq.header, + vdec_cfg_sps.seq.len); + + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + kfree(header); + return ret; + } + + TRACE("vi_cfg: handle=%p fourcc=0x%x w=%d h=%d order=%d notify_en=%d " + "vc1_rb=%d h264_sd=%d h264_nls=%d pp_flag=%d fruc_en=%d\n", + vd->vdec_handle, vi_cfg.cfg.fourcc, vi_cfg.cfg.width, + vi_cfg.cfg.height, vi_cfg.cfg.order, vi_cfg.cfg.notify_enable, + vi_cfg.cfg.vc1_rowbase, vi_cfg.cfg.h264_startcode_detect, + vi_cfg.cfg.h264_nal_len_size, vi_cfg.cfg.postproc_flag, + vi_cfg.cfg.fruc_enable); + + vd->streamDetails.height = vi_cfg.cfg.height; + vd->streamDetails.width = vi_cfg.cfg.width; + vd->streamDetails.fourcc = vi_cfg.cfg.fourcc; + if (FLAG_THUMBNAIL_MODE == vi_cfg.cfg.postproc_flag) + vd->streamDetails.isThisTnail = TRUE; + else + vd->streamDetails.isThisTnail = FALSE; + + mutex_lock(&vdec_rm_lock); + ret = vdec_rm_checkWithRm(vd, vi_cfg.cfg.color_format); + mutex_unlock(&vdec_rm_lock); + if (ret) + return ret; + + ret = dal_call_f13(vd->vdec_handle, VDEC_DALRPC_INITIALIZE, + &vi_cfg, sizeof(vi_cfg), + header, vdec_cfg_sps.seq.len, + &vdec_buf_req, sizeof(vdec_buf_req)); + + kfree(header); + + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + else + ret = copy_to_user(((struct vdec_init *)argp)->buf_req, + &vdec_buf_req, sizeof(vdec_buf_req)); + + vd->close_decode = 0; + return ret; +} + +static void vdec_rm_freeupResources(struct vdec_data *vdecInstance) +{ + struct videoStreamDetails *streamDetails = &vdecInstance->streamDetails; + + + + if ((streamDetails->isThisTnail) && + (streamDetails->isTnailGranted)) { + + totalTnailQ6load--; + pr_info("%s: Thumbnail released %d\n", __func__, + totalTnailQ6load); + + } else if (streamDetails->Q6usage > 0) { + + totalPlaybackQ6load -= streamDetails->Q6usage; + if (totalPlaybackQ6load < 0) + pr_err("Warning:Q6load cannot be negative\n"); + + pr_info("%s:Releasing [%d%%] of Q6load from a total of [%d%%]\n" + , __func__, streamDetails->Q6usage, + (streamDetails->Q6usage+totalPlaybackQ6load)); + } + +} + +static int vdec_setbuffers(struct vdec_data *vd, void *argp) +{ + struct vdec_buffer vmem; + struct vdec_mem_list *l; + unsigned long vstart; + unsigned long flags; + struct { + uint32_t size; + struct vdec_buf_info buf; + } rpc; + uint32_t res; + + int ret = 0; + + vd->mem_initialized = 0; + + ret = copy_from_user(&vmem, argp, sizeof(vmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + l = kzalloc(sizeof(struct vdec_mem_list), GFP_KERNEL); + if (!l) { + pr_err("%s: kzalloc failed!\n", __func__); + return -ENOMEM; + } + + l->mem.id = vmem.pmem_id; + l->mem.buf_type = vmem.buf.buf_type; + + ret = get_pmem_file(l->mem.id, &l->mem.phys_addr, &vstart, + &l->mem.len, &l->mem.file); + if (ret) { + pr_err("%s: get_pmem_fd failed\n", __func__); + goto err_get_pmem_file; + } + + TRACE("pmem_id=%d (phys=0x%08lx len=0x%lx) buftype=%d num_buf=%d " + "islast=%d src_id=%d offset=0x%08x size=0x%x\n", + vmem.pmem_id, l->mem.phys_addr, l->mem.len, + vmem.buf.buf_type, vmem.buf.num_buf, vmem.buf.islast, + vmem.buf.region.src_id, vmem.buf.region.offset, + vmem.buf.region.size); + + /* input buffers */ + if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) { + pr_err("%s: invalid input buffer offset!\n", __func__); + ret = -EINVAL; + goto err_bad_offset; + + } + vmem.buf.region.offset += l->mem.phys_addr; + + rpc.size = sizeof(vmem.buf); + memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info)); + + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_SETBUFFERS, 5, + &rpc, sizeof(rpc), &res, sizeof(res)); + + if (ret < 4) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + ret = -EIO; + goto err_dal_call; + } + + spin_lock_irqsave(&vd->vdec_mem_list_lock, flags); + list_add(&l->list, &vd->vdec_mem_list_head); + spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags); + + vd->mem_initialized = 1; + return ret; + +err_dal_call: +err_bad_offset: + put_pmem_file(l->mem.file); +err_get_pmem_file: + kfree(l); + return ret; +} + +static int vdec_queue(struct vdec_data *vd, void *argp) +{ + struct { + uint32_t size; + struct vdec_input_buf_info buf_info; + uint32_t osize; + } rpc; + struct vdec_mem_list *l; + struct { + uint32_t result; + uint32_t size; + struct vdec_queue_status status; + } rpc_res; + + u32 pmem_id; + int ret = 0; + + if (!vd->mem_initialized) { + pr_err("%s: memory is not being initialized!\n", __func__); + return -EPERM; + } + + ret = copy_from_user(&rpc.buf_info, + &((struct vdec_input_buf *)argp)->buffer, + sizeof(rpc.buf_info)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + ret = copy_from_user(&pmem_id, + &((struct vdec_input_buf *)argp)->pmem_id, + sizeof(u32)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + l = vdec_get_mem_from_list(vd, pmem_id, VDEC_BUFFER_TYPE_INPUT); + + if (NULL == l) { + pr_err("%s: not able to find the buffer from list\n", __func__); + return -EPERM; + } + + if ((rpc.buf_info.size + rpc.buf_info.offset) >= l->mem.len) { + pr_err("%s: invalid queue buffer offset!\n", __func__); + return -EINVAL; + } + + rpc.buf_info.offset += l->mem.phys_addr; + rpc.size = sizeof(struct vdec_input_buf_info); + rpc.osize = sizeof(struct vdec_queue_status); + + /* complete the writes to the buffer */ + wmb(); + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8, + &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res)); + if (ret < 4) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + ret = -EIO; + } + return ret; +} + +static int vdec_reuse_framebuffer(struct vdec_data *vd, void *argp) +{ + u32 buf_id; + int ret = 0; + + ret = copy_from_user(&buf_id, argp, sizeof(buf_id)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_REUSEFRAMEBUFFER, + buf_id); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + + return ret; +} + +static int vdec_flush(struct vdec_data *vd, void *argp) +{ + u32 flush_type; + int ret = 0; + + if (!vd->mem_initialized) { + pr_err("%s: memory is not being initialized!\n", __func__); + return -EPERM; + } + + ret = copy_from_user(&flush_type, argp, sizeof(flush_type)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + TRACE("flush_type=%d\n", flush_type); + ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_FLUSH, flush_type); + if (ret) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int vdec_close(struct vdec_data *vd, void *argp) +{ + struct vdec_mem_list *l; + int ret = 0; + + pr_info("q6vdec_close()\n"); + vd->close_decode = 1; + wake_up(&vd->vdec_msg_evt); + + ret = dal_call_f0(vd->vdec_handle, DAL_OP_CLOSE, 0); + if (ret) + pr_err("%s: failed to close daldevice (%d)\n", __func__, ret); + + if (vd->mem_initialized) { + list_for_each_entry(l, &vd->vdec_mem_list_head, list) + put_pmem_file(l->mem.file); + } + + return ret; +} +static int vdec_getdecattributes(struct vdec_data *vd, void *argp) +{ + struct { + uint32_t status; + uint32_t size; + struct vdec_dec_attributes dec_attr; + } rpc; + uint32_t inp; + int ret = 0; + inp = sizeof(struct vdec_dec_attributes); + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_GETDECATTRIBUTES, 9, + &inp, sizeof(inp), &rpc, sizeof(rpc)); + if (ret < 4 || rpc.size != sizeof(struct vdec_dec_attributes)) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + ret = -EIO; + } else + ret = + copy_to_user(((struct vdec_dec_attributes *)argp), + &rpc.dec_attr, sizeof(rpc.dec_attr)); + return ret; +} + +static int vdec_freebuffers(struct vdec_data *vd, void *argp) +{ + struct vdec_buffer vmem; + struct vdec_mem_list *l; + struct { + uint32_t size; + struct vdec_buf_info buf; + } rpc; + uint32_t res; + + int ret = 0; + + if (!vd->mem_initialized) { + pr_err("%s: memory is not being initialized!\n", __func__); + return -EPERM; + } + + ret = copy_from_user(&vmem, argp, sizeof(vmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + l = vdec_get_mem_from_list(vd, vmem.pmem_id, vmem.buf.buf_type); + + if (NULL == l) { + pr_err("%s: not able to find the buffer from list\n", __func__); + return -EPERM; + } + + /* input buffers */ + if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) { + pr_err("%s: invalid input buffer offset!\n", __func__); + return -EINVAL; + + } + vmem.buf.region.offset += l->mem.phys_addr; + + rpc.size = sizeof(vmem.buf); + memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info)); + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_FREEBUFFERS, 5, + &rpc, sizeof(rpc), &res, sizeof(res)); + if (ret < 4) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + } + + return ret; +} + +static int vdec_getversion(struct vdec_data *vd, void *argp) +{ + struct vdec_version ver_info; + int ret = 0; + + ver_info.major = VDEC_GET_MAJOR_VERSION(VDEC_INTERFACE_VERSION); + ver_info.minor = VDEC_GET_MINOR_VERSION(VDEC_INTERFACE_VERSION); + + ret = copy_to_user(((struct vdec_version *)argp), + &ver_info, sizeof(ver_info)); + + return ret; + +} + +static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct vdec_data *vd = file->private_data; + void __user *argp = (void __user *)arg; + int ret = 0; + + if (!vd->running) + return -EPERM; + + switch (cmd) { + case VDEC_IOCTL_INITIALIZE: + ret = vdec_initialize(vd, argp); + break; + + case VDEC_IOCTL_SETBUFFERS: + ret = vdec_setbuffers(vd, argp); + break; + + case VDEC_IOCTL_QUEUE: + TRACE("VDEC_IOCTL_QUEUE (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_queue(vd, argp); + break; + + case VDEC_IOCTL_REUSEFRAMEBUFFER: + TRACE("VDEC_IOCTL_REUSEFRAMEBUFFER (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_reuse_framebuffer(vd, argp); + break; + + case VDEC_IOCTL_FLUSH: + TRACE("IOCTL flush\n"); + ret = vdec_flush(vd, argp); + break; + + case VDEC_IOCTL_EOS: + TRACE("VDEC_IOCTL_EOS (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_SIGEOFSTREAM, 0); + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + + case VDEC_IOCTL_GETMSG: + TRACE("VDEC_IOCTL_GETMSG (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + wait_event_interruptible(vd->vdec_msg_evt, + vdec_get_msg(vd, argp)); + + if (vd->close_decode) + ret = -EINTR; + else + /* order the reads from the buffer */ + rmb(); + break; + + case VDEC_IOCTL_CLOSE: + ret = vdec_close(vd, argp); + break; + + case VDEC_IOCTL_GETDECATTRIBUTES: + TRACE("VDEC_IOCTL_GETDECATTRIBUTES (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_getdecattributes(vd, argp); + + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + + case VDEC_IOCTL_FREEBUFFERS: + TRACE("VDEC_IOCTL_FREEBUFFERS (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_freebuffers(vd, argp); + + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + case VDEC_IOCTL_GETVERSION: + TRACE("VDEC_IOCTL_GETVERSION (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_getversion(vd, argp); + + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + case VDEC_IOCTL_GETPROPERTY: + TRACE("VDEC_IOCTL_GETPROPERTY (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_getproperty(vd, argp); + break; + case VDEC_IOCTL_SETPROPERTY: + TRACE("VDEC_IOCTL_SETPROPERTY (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_setproperty(vd, argp); + break; + case VDEC_IOCTL_PERFORMANCE_CHANGE_REQ: + ret = vdec_performance_change_request(vd, argp); + break; + default: + pr_err("%s: invalid ioctl!\n", __func__); + ret = -EINVAL; + break; + } + + TRACE("ioctl done (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + + return ret; +} + +static void vdec_dcdone_handler(struct vdec_data *vd, void *frame, + uint32_t frame_size) +{ + struct vdec_msg msg; + struct vdec_mem_list *l; + unsigned long flags; + int found = 0; + + if (frame_size < sizeof(struct vdec_frame_info)) { + pr_warning("%s: msg size mismatch %d != %d\n", __func__, + frame_size, sizeof(struct vdec_frame_info)); + return; + } + + memcpy(&msg.vfr_info, (struct vdec_frame_info *)frame, + sizeof(struct vdec_frame_info)); + + if (msg.vfr_info.status == VDEC_FRAME_DECODE_OK) { + spin_lock_irqsave(&vd->vdec_mem_list_lock, flags); + list_for_each_entry(l, &vd->vdec_mem_list_head, list) { + if ((l->mem.buf_type == VDEC_BUFFER_TYPE_OUTPUT) && + (msg.vfr_info.offset >= l->mem.phys_addr) && + (msg.vfr_info.offset < + (l->mem.phys_addr + l->mem.len))) { + found = 1; + msg.vfr_info.offset -= l->mem.phys_addr; + msg.vfr_info.data2 = l->mem.id; + break; + } + } + spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags); + } + + if (found || (msg.vfr_info.status != VDEC_FRAME_DECODE_OK)) { + msg.id = VDEC_MSG_FRAMEDONE; + vdec_put_msg(vd, &msg); + } else { + pr_err("%s: invalid phys addr = 0x%x\n", + __func__, msg.vfr_info.offset); + } + +} + +static void vdec_reuseibuf_handler(struct vdec_data *vd, void *bufstat, + uint32_t bufstat_size) +{ + struct vdec_buffer_status *vdec_bufstat; + struct vdec_msg msg; + + /* TODO: how do we signal the client? If they are waiting on a + * message in an ioctl, they may block forever */ + if (bufstat_size != sizeof(struct vdec_buffer_status)) { + pr_warning("%s: msg size mismatch %d != %d\n", __func__, + bufstat_size, sizeof(struct vdec_buffer_status)); + return; + } + vdec_bufstat = (struct vdec_buffer_status *)bufstat; + msg.id = VDEC_MSG_REUSEINPUTBUFFER; + msg.buf_id = vdec_bufstat->data; + vdec_put_msg(vd, &msg); +} + +static void callback(void *data, int len, void *cookie) +{ + struct vdec_data *vd = (struct vdec_data *)cookie; + uint32_t *tmp = (uint32_t *) data; + + if (!vd->mem_initialized) { + pr_err("%s:memory not initialize but callback called!\n", + __func__); + return; + } + + TRACE("vdec_async: tmp=0x%08x 0x%08x 0x%08x\n", tmp[0], tmp[1], tmp[2]); + switch (tmp[0]) { + case VDEC_ASYNCMSG_DECODE_DONE: + vdec_dcdone_handler(vd, &tmp[3], tmp[2]); + break; + case VDEC_ASYNCMSG_REUSE_FRAME: + vdec_reuseibuf_handler(vd, &tmp[3], tmp[2]); + break; + default: + pr_err("%s: Unknown async message from DSP id=0x%08x sz=%u\n", + __func__, tmp[0], tmp[2]); + } +} + +static int vdec_open(struct inode *inode, struct file *file) +{ + int ret; + int i; + struct vdec_msg_list *l; + struct vdec_data *vd; + struct dal_info version_info; + char *portname = NULL; + + pr_info("q6vdec_open()\n"); + mutex_lock(&vdec_ref_lock); + if (ref_cnt >= MAX_SUPPORTED_INSTANCES) { + pr_err("%s: Max allowed instances exceeded \n", __func__); + mutex_unlock(&vdec_ref_lock); + return -EBUSY; + } + ref_cnt++; + mutex_unlock(&vdec_ref_lock); + + vd = kmalloc(sizeof(struct vdec_data), GFP_KERNEL); + if (!vd) { + pr_err("%s: kmalloc failed\n", __func__); + ret = -ENOMEM; + goto vdec_open_err_handle_vd; + } + file->private_data = vd; + + vd->mem_initialized = 0; + INIT_LIST_HEAD(&vd->vdec_msg_list_head); + INIT_LIST_HEAD(&vd->vdec_msg_list_free); + INIT_LIST_HEAD(&vd->vdec_mem_list_head); + init_waitqueue_head(&vd->vdec_msg_evt); + + spin_lock_init(&vd->vdec_list_lock); + spin_lock_init(&vd->vdec_mem_list_lock); + for (i = 0; i < VDEC_MSG_MAX; i++) { + l = kzalloc(sizeof(struct vdec_msg_list), GFP_KERNEL); + if (!l) { + pr_err("%s: kzalloc failed!\n", __func__); + ret = -ENOMEM; + goto vdec_open_err_handle_list; + } + list_add(&l->list, &vd->vdec_msg_list_free); + } + + memset(&vd->streamDetails, 0, sizeof(struct videoStreamDetails)); + + mutex_lock(&vdec_ref_lock); + vdec_get_next_portanddevid(&vd->Q6deviceId, &portname); + mutex_unlock(&vdec_ref_lock); + + if ((0 == vd->Q6deviceId) || (NULL == portname)) { + pr_err("%s: FATAL error portname %s or deviceId %d not picked properly\n", + __func__, portname, vd->Q6deviceId); + ret = -EIO; + goto vdec_open_err_handle_list; + } else { + pr_err("attaching on deviceid %x portname %s\n", + vd->Q6deviceId, portname); + vd->vdec_handle = dal_attach(vd->Q6deviceId, + portname, 1, callback, vd); + } + + if (!vd->vdec_handle) { + pr_err("%s: failed to attach\n", __func__); + ret = -EIO; + goto vdec_open_err_handle_list; + } + ret = dal_call_f9(vd->vdec_handle, DAL_OP_INFO, + &version_info, sizeof(struct dal_info)); + + if (ret) { + pr_err("%s: failed to get version \n", __func__); + goto vdec_open_err_handle_version; + } + + TRACE("q6vdec_open() interface version 0x%x\n", version_info.version); + if (vdec_check_version(VDEC_INTERFACE_VERSION, + version_info.version)) { + pr_err("%s: driver version mismatch !\n", __func__); + goto vdec_open_err_handle_version; + } + + vd->running = 1; + prevent_sleep(); + + return 0; +vdec_open_err_handle_version: + dal_detach(vd->vdec_handle); +vdec_open_err_handle_list: + { + struct vdec_msg_list *l, *n; + list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) { + list_del(&l->list); + kfree(l); + } + } +vdec_open_err_handle_vd: + mutex_lock(&vdec_ref_lock); + vdec_freeup_portanddevid(vd->Q6deviceId); + ref_cnt--; + mutex_unlock(&vdec_ref_lock); + kfree(vd); + return ret; +} + +static int vdec_release(struct inode *inode, struct file *file) +{ + int ret; + struct vdec_msg_list *l, *n; + struct vdec_mem_list *m, *k; + struct vdec_data *vd = file->private_data; + + vd->running = 0; + wake_up_all(&vd->vdec_msg_evt); + + if (!vd->close_decode) + vdec_close(vd, NULL); + + ret = dal_detach(vd->vdec_handle); + if (ret) + printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret); + + list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) { + list_del(&l->list); + kfree(l); + } + + list_for_each_entry_safe(l, n, &vd->vdec_msg_list_head, list) { + list_del(&l->list); + kfree(l); + } + + list_for_each_entry_safe(m, k, &vd->vdec_mem_list_head, list) { + list_del(&m->list); + kfree(m); + } + mutex_lock(&vdec_ref_lock); + BUG_ON(ref_cnt <= 0); + ref_cnt--; + vdec_freeup_portanddevid(vd->Q6deviceId); + mutex_unlock(&vdec_ref_lock); + + mutex_lock(&vdec_rm_lock); + vdec_rm_freeupResources(vd); + mutex_unlock(&vdec_rm_lock); + + + kfree(vd); + allow_sleep(); + return 0; +} + +static const struct file_operations vdec_fops = { + .owner = THIS_MODULE, + .open = vdec_open, + .release = vdec_release, + .unlocked_ioctl = vdec_ioctl, +}; + +static int __init vdec_init(void) +{ + struct device *class_dev; + int rc = 0; + + pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "vdec_suspend"); + + rc = alloc_chrdev_region(&vdec_device_no, 0, 1, "vdec"); + if (rc < 0) { + pr_err("%s: alloc_chrdev_region failed %d\n", __func__, rc); + return rc; + } + + driver_class = class_create(THIS_MODULE, "vdec"); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("%s: class_create failed %d\n", __func__, rc); + goto vdec_init_err_unregister_chrdev_region; + } + class_dev = device_create(driver_class, NULL, + vdec_device_no, NULL, "vdec"); + if (!class_dev) { + pr_err("%s: class_device_create failed %d\n", __func__, rc); + rc = -ENOMEM; + goto vdec_init_err_class_destroy; + } + + cdev_init(&vdec_cdev, &vdec_fops); + vdec_cdev.owner = THIS_MODULE; + rc = cdev_add(&vdec_cdev, MKDEV(MAJOR(vdec_device_no), 0), 1); + + if (rc < 0) { + pr_err("%s: cdev_add failed %d\n", __func__, rc); + goto vdec_init_err_class_device_destroy; + } + + memset(&deviceIdRegistry, 0, sizeof(deviceIdRegistry)); + memset(&loadOnPorts, 0, sizeof(loadOnPorts)); + numOfPorts = 0; + + return 0; + +vdec_init_err_class_device_destroy: + device_destroy(driver_class, vdec_device_no); +vdec_init_err_class_destroy: + class_destroy(driver_class); +vdec_init_err_unregister_chrdev_region: + unregister_chrdev_region(vdec_device_no, 1); + return rc; +} + +static void __exit vdec_exit(void) +{ + device_destroy(driver_class, vdec_device_no); + class_destroy(driver_class); + unregister_chrdev_region(vdec_device_no, 1); +} + +MODULE_DESCRIPTION("video decoder driver for QSD platform"); +MODULE_VERSION("2.00"); + +module_init(vdec_init); +module_exit(vdec_exit); diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c new file mode 100644 index 00000000000..0917c7010b5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -0,0 +1,1201 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dal.h" + +#define DALDEVICEID_VENC_DEVICE 0x0200002D +#define DALDEVICEID_VENC_PORTNAME "DAL_AQ_VID" + +#define VENC_NAME "q6venc" +#define VENC_MSG_MAX 128 + +#define VENC_INTERFACE_VERSION 0x00020000 +#define MAJOR_MASK 0xFFFF0000 +#define MINOR_MASK 0x0000FFFF +#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16) +#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK) + +enum { + VENC_BUFFER_TYPE_INPUT, + VENC_BUFFER_TYPE_OUTPUT, + VENC_BUFFER_TYPE_QDSP6, + VENC_BUFFER_TYPE_HDR +}; +enum { + VENC_DALRPC_GET_SYNTAX_HEADER = DAL_OP_FIRST_DEVICE_API, + VENC_DALRPC_UPDATE_INTRA_REFRESH, + VENC_DALRPC_UPDATE_FRAME_RATE, + VENC_DALRPC_UPDATE_BITRATE, + VENC_DALRPC_UPDATE_QP_RANGE, + VENC_DALRPC_UPDATE_INTRA_PERIOD, + VENC_DALRPC_REQUEST_IFRAME, + VENC_DALRPC_START, + VENC_DALRPC_STOP, + VENC_DALRPC_SUSPEND, + VENC_DALRPC_RESUME, + VENC_DALRPC_FLUSH, + VENC_DALRPC_QUEUE_INPUT, + VENC_DALRPC_QUEUE_OUTPUT +}; +struct venc_input_payload { + u32 data; +}; +struct venc_output_payload { + u32 size; + long long time_stamp; + u32 flags; + u32 data; + u32 client_data_from_input; +}; +union venc_payload { + struct venc_input_payload input_payload; + struct venc_output_payload output_payload; +}; +struct venc_msg_type { + u32 event; + u32 status; + union venc_payload payload; +}; +struct venc_input_buf { + struct venc_buf_type yuv_buf; + u32 data_size; + long long time_stamp; + u32 flags; + u32 dvs_offsetx; + u32 dvs_offsety; + u32 client_data; + u32 op_client_data; +}; +struct venc_output_buf { + struct venc_buf_type bit_stream_buf; + u32 client_data; +}; + +struct venc_msg_list { + struct list_head list; + struct venc_msg msg_data; +}; +struct venc_buf { + int fd; + u32 src; + u32 offset; + u32 size; + u32 btype; + unsigned long paddr; + struct file *file; +}; +struct venc_pmem_list { + struct list_head list; + struct venc_buf buf; +}; +struct venc_dev { + bool is_active; + bool pmem_freed; + enum venc_state_type state; + struct list_head venc_msg_list_head; + struct list_head venc_msg_list_free; + spinlock_t venc_msg_list_lock; + struct list_head venc_pmem_list_head; + spinlock_t venc_pmem_list_lock; + struct dal_client *q6_handle; + wait_queue_head_t venc_msg_evt; + struct device *class_devp; +}; + +#define DEBUG_VENC 0 +#if DEBUG_VENC +#define TRACE(fmt, x...) \ + do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) +#else +#define TRACE(fmt, x...) do { } while (0) +#endif + +static struct cdev cdev; +static dev_t venc_dev_num; +static struct class *venc_class; +static struct venc_dev *venc_device_p; +static int venc_ref; + +static DEFINE_MUTEX(idlecount_lock); +static int idlecount; +static struct wake_lock wakelock; +static struct pm_qos_request pm_qos_req; + +static void prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + pm_qos_update_request(&pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + wake_lock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static void allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&wakelock); + pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE); + } + mutex_unlock(&idlecount_lock); +} + +static inline int venc_check_version(u32 client, u32 server) +{ + int ret = -EINVAL; + + if ((VENC_GET_MAJOR_VERSION(client) == VENC_GET_MAJOR_VERSION(server)) + && (VENC_GET_MINOR_VERSION(client) <= + VENC_GET_MINOR_VERSION(server))) + ret = 0; + + return ret; +} + +static int venc_get_msg(struct venc_dev *dvenc, void *msg) +{ + struct venc_msg_list *l; + unsigned long flags; + int ret = 0; + struct venc_msg qdsp_msg; + + if (!dvenc->is_active) + return -EPERM; + spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags); + list_for_each_entry_reverse(l, &dvenc->venc_msg_list_head, list) { + memcpy(&qdsp_msg, &l->msg_data, sizeof(struct venc_msg)); + list_del(&l->list); + list_add(&l->list, &dvenc->venc_msg_list_free); + ret = 1; + break; + } + spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags); + if (copy_to_user(msg, &qdsp_msg, sizeof(struct venc_msg))) + pr_err("%s failed to copy_to_user\n", __func__); + return ret; +} + +static void venc_put_msg(struct venc_dev *dvenc, struct venc_msg *msg) +{ + struct venc_msg_list *l; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags); + list_for_each_entry(l, &dvenc->venc_msg_list_free, list) { + memcpy(&l->msg_data, msg, sizeof(struct venc_msg)); + list_del(&l->list); + list_add(&l->list, &dvenc->venc_msg_list_head); + found = 1; + break; + } + spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags); + if (found) + wake_up(&dvenc->venc_msg_evt); + else + pr_err("%s: failed to find a free node\n", __func__); + +} + +static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc, + struct venc_pmem *mptr, + u32 btype) +{ + int ret = 0; + unsigned long flags; + unsigned long len; + unsigned long vaddr; + struct venc_pmem_list *plist = NULL; + + plist = kzalloc(sizeof(struct venc_pmem_list), GFP_KERNEL); + if (!plist) { + pr_err("%s: kzalloc failed\n", __func__); + return NULL; + } + + ret = get_pmem_file(mptr->fd, &(plist->buf.paddr), + &vaddr, &len, &(plist->buf.file)); + if (ret) { + pr_err("%s: get_pmem_file failed for fd=%d offset=%d\n", + __func__, mptr->fd, mptr->offset); + goto err_venc_add_pmem; + } else if (mptr->offset >= len) { + pr_err("%s: invalid offset (%d > %ld) for fd=%d\n", + __func__, mptr->offset, len, mptr->fd); + ret = -EINVAL; + goto err_venc_get_pmem; + } + + plist->buf.fd = mptr->fd; + plist->buf.paddr += mptr->offset; + plist->buf.size = mptr->size; + plist->buf.btype = btype; + plist->buf.offset = mptr->offset; + plist->buf.src = mptr->src; + + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + list_add(&plist->list, &dvenc->venc_pmem_list_head); + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + return plist; + +err_venc_get_pmem: + put_pmem_file(plist->buf.file); +err_venc_add_pmem: + kfree(plist); + return NULL; +} + +static struct venc_pmem_list *venc_get_pmem_from_list( + struct venc_dev *dvenc, u32 pmem_fd, + u32 offset, u32 btype) +{ + struct venc_pmem_list *plist; + unsigned long flags; + struct file *file; + int found = 0; + + file = fget(pmem_fd); + if (!file) { + pr_err("%s: invalid encoder buffer fd(%d)\n", __func__, + pmem_fd); + return NULL; + } + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) { + if (plist->buf.btype == btype && plist->buf.file == file && + plist->buf.offset == offset) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + fput(file); + if (found) + return plist; + + else + return NULL; +} + +static int venc_set_buffer(struct venc_dev *dvenc, void *argp, + u32 btype) +{ + struct venc_pmem pmem; + struct venc_pmem_list *plist; + int ret = 0; + + ret = copy_from_user(&pmem, argp, sizeof(pmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + plist = venc_add_pmem_to_list(dvenc, &pmem, btype); + if (plist == NULL) { + pr_err("%s: buffer add_to_pmem_list failed\n", + __func__); + return -EPERM; + } + return ret; +} + +static int venc_assign_q6_buffers(struct venc_dev *dvenc, + struct venc_buffers *pbufs, + struct venc_nonio_buf_config *pcfg) +{ + int ret = 0; + struct venc_pmem_list *plist; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[0]), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: recon_buf0 failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->recon_buf1.region = pbufs->recon_buf[0].src; + pcfg->recon_buf1.phys = plist->buf.paddr; + pcfg->recon_buf1.size = plist->buf.size; + pcfg->recon_buf1.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[1]), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: recons_buf1 failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->recon_buf2.region = pbufs->recon_buf[1].src; + pcfg->recon_buf2.phys = plist->buf.paddr; + pcfg->recon_buf2.size = plist->buf.size; + pcfg->recon_buf2.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->wb_buf), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: wb_buf failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->wb_buf.region = pbufs->wb_buf.src; + pcfg->wb_buf.phys = plist->buf.paddr; + pcfg->wb_buf.size = plist->buf.size; + pcfg->wb_buf.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->cmd_buf), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: cmd_buf failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->cmd_buf.region = pbufs->cmd_buf.src; + pcfg->cmd_buf.phys = plist->buf.paddr; + pcfg->cmd_buf.size = plist->buf.size; + pcfg->cmd_buf.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->vlc_buf), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: vlc_buf failed to add_to_pmem_list" + " failed\n", __func__); + return -EPERM; + } + pcfg->vlc_buf.region = pbufs->vlc_buf.src; + pcfg->vlc_buf.phys = plist->buf.paddr; + pcfg->vlc_buf.size = plist->buf.size; + pcfg->vlc_buf.offset = 0; + + return ret; +} + +static int venc_start(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_q6_config q6_config; + struct venc_init_config vconfig; + + dvenc->state = VENC_STATE_START; + ret = copy_from_user(&vconfig, argp, sizeof(struct venc_init_config)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + memcpy(&q6_config, &(vconfig.q6_config), sizeof(q6_config)); + ret = venc_assign_q6_buffers(dvenc, &(vconfig.q6_bufs), + &(q6_config.buf_params)); + if (ret != 0) { + pr_err("%s: assign_q6_buffers failed\n", __func__); + return -EPERM; + } + + q6_config.callback_event = dvenc->q6_handle; + TRACE("%s: parameters: handle:%p, config:%p, callback:%p \n", __func__, + dvenc->q6_handle, &q6_config, q6_config.callback_event); + TRACE("%s: parameters:recon1:0x%x, recon2:0x%x," + " wb_buf:0x%x, cmd:0x%x, vlc:0x%x\n", __func__, + q6_config.buf_params.recon_buf1.phys, + q6_config.buf_params.recon_buf2.phys, + q6_config.buf_params.wb_buf.phys, + q6_config.buf_params.cmd_buf.phys, + q6_config.buf_params.vlc_buf.phys); + TRACE("%s: size of param:%d \n", __func__, sizeof(q6_config)); + ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_START, &q6_config, + sizeof(q6_config)); + if (ret != 0) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; + } + return ret; +} + +static int venc_encode_frame(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_pmem buf; + struct venc_input_buf q6_input; + struct venc_pmem_list *plist; + struct venc_buffer input; + + ret = copy_from_user(&input, argp, sizeof(struct venc_buffer)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + ret = copy_from_user(&buf, + ((struct venc_buffer *)argp)->ptr_buffer, + sizeof(struct venc_pmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset, + VENC_BUFFER_TYPE_INPUT); + if (NULL == plist) { + plist = venc_add_pmem_to_list(dvenc, &buf, + VENC_BUFFER_TYPE_INPUT); + if (plist == NULL) { + pr_err("%s: buffer add_to_pmem_list failed\n", + __func__); + return -EPERM; + } + } + + q6_input.flags = 0; + if (input.flags & VENC_FLAG_EOS) + q6_input.flags |= 0x00000001; + q6_input.yuv_buf.region = plist->buf.src; + q6_input.yuv_buf.phys = plist->buf.paddr; + q6_input.yuv_buf.size = plist->buf.size; + q6_input.yuv_buf.offset = 0; + q6_input.data_size = plist->buf.size; + q6_input.client_data = (u32)input.client_data; + q6_input.time_stamp = input.time_stamp; + q6_input.dvs_offsetx = 0; + q6_input.dvs_offsety = 0; + + TRACE("Pushing down input phys=0x%x fd= %d, client_data: 0x%x," + " time_stamp:%lld \n", q6_input.yuv_buf.phys, plist->buf.fd, + input.client_data, input.time_stamp); + ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_INPUT, + &q6_input, sizeof(q6_input)); + + if (ret != 0) + pr_err("%s: Q6 queue_input failed (%d)\n", __func__, + (int)ret); + return ret; +} + +static int venc_fill_output(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_pmem buf; + struct venc_output_buf q6_output; + struct venc_pmem_list *plist; + struct venc_buffer output; + + ret = copy_from_user(&output, argp, sizeof(struct venc_buffer)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + ret = copy_from_user(&buf, + ((struct venc_buffer *)argp)->ptr_buffer, + sizeof(struct venc_pmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset, + VENC_BUFFER_TYPE_OUTPUT); + if (NULL == plist) { + plist = venc_add_pmem_to_list(dvenc, &buf, + VENC_BUFFER_TYPE_OUTPUT); + if (NULL == plist) { + pr_err("%s: output buffer failed to add_to_pmem_list" + "\n", __func__); + return -EPERM; + } + } + q6_output.bit_stream_buf.region = plist->buf.src; + q6_output.bit_stream_buf.phys = (u32)plist->buf.paddr; + q6_output.bit_stream_buf.size = plist->buf.size; + q6_output.bit_stream_buf.offset = 0; + q6_output.client_data = (u32)output.client_data; + ret = + dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_OUTPUT, &q6_output, + sizeof(q6_output)); + if (ret != 0) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; +} + +static int venc_stop(struct venc_dev *dvenc) +{ + int ret = 0; + struct venc_msg msg; + + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); + if (ret) { + pr_err("%s: remote runction failed (%d)\n", __func__, ret); + msg.msg_code = VENC_MSG_STOP; + msg.msg_data_size = 0; + msg.status_code = VENC_S_EFAIL; + venc_put_msg(dvenc, &msg); + } + return ret; +} + +static int venc_pause(struct venc_dev *dvenc) +{ + int ret = 0; + struct venc_msg msg; + + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_SUSPEND, 1); + if (ret) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + msg.msg_code = VENC_MSG_PAUSE; + msg.status_code = VENC_S_EFAIL; + msg.msg_data_size = 0; + venc_put_msg(dvenc, &msg); + } + return ret; +} + +static int venc_resume(struct venc_dev *dvenc) +{ + int ret = 0; + struct venc_msg msg; + + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_RESUME, 1); + if (ret) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + msg.msg_code = VENC_MSG_RESUME; + msg.msg_data_size = 0; + msg.status_code = VENC_S_EFAIL; + venc_put_msg(dvenc, &msg); + } + return ret; +} + +static int venc_flush(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_msg msg; + union venc_msg_data smsg; + int status = VENC_S_SUCCESS; + struct venc_buffer_flush flush; + + if (copy_from_user(&flush, argp, sizeof(struct venc_buffer_flush))) + return -EFAULT; + if (flush.flush_mode == VENC_FLUSH_ALL) { + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_FLUSH, 1); + if (ret) + status = VENC_S_EFAIL; + } else + status = VENC_S_ENOTSUPP; + + if (status != VENC_S_SUCCESS) { + if ((flush.flush_mode == VENC_FLUSH_INPUT) || + (flush.flush_mode == VENC_FLUSH_ALL)) { + smsg.flush_ret.flush_mode = VENC_FLUSH_INPUT; + msg.msg_data = smsg; + msg.status_code = status; + msg.msg_code = VENC_MSG_FLUSH; + msg.msg_data_size = sizeof(union venc_msg_data); + venc_put_msg(dvenc, &msg); + } + if (flush.flush_mode == VENC_FLUSH_OUTPUT || + (flush.flush_mode == VENC_FLUSH_ALL)) { + smsg.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; + msg.msg_data = smsg; + msg.status_code = status; + msg.msg_code = VENC_MSG_FLUSH; + msg.msg_data_size = sizeof(union venc_msg_data); + venc_put_msg(dvenc, &msg); + } + return -EIO; + } + return ret; +} + +static int venc_get_sequence_hdr(struct venc_dev *dvenc, void *argp) +{ + pr_err("%s not supported\n", __func__); + return -EIO; +} + +static int venc_set_qp_range(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_qp_range qp; + + ret = copy_from_user(&qp, argp, sizeof(struct venc_qp_range)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = + dal_call_f5(dvenc->q6_handle, VENC_DALRPC_UPDATE_QP_RANGE, + &qp, sizeof(struct venc_qp_range)); + if (ret) { + pr_err("%s: remote function failed (%d) \n", __func__, + ret); + return ret; + } + } + return ret; +} + +static int venc_set_intra_period(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + u32 pnum = 0; + + ret = copy_from_user(&pnum, argp, sizeof(int)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f0(dvenc->q6_handle, + VENC_DALRPC_UPDATE_INTRA_PERIOD, pnum); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} + +static int venc_set_intra_refresh(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + u32 mb_num = 0; + + ret = copy_from_user(&mb_num, argp, sizeof(int)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f0(dvenc->q6_handle, + VENC_DALRPC_UPDATE_INTRA_REFRESH, mb_num); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} + +static int venc_set_frame_rate(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_frame_rate pdata; + ret = copy_from_user(&pdata, argp, sizeof(struct venc_frame_rate)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f5(dvenc->q6_handle, + VENC_DALRPC_UPDATE_FRAME_RATE, + (void *)&(pdata), + sizeof(struct venc_frame_rate)); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} + +static int venc_set_target_bitrate(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + u32 pdata = 0; + + ret = copy_from_user(&pdata, argp, sizeof(int)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f0(dvenc->q6_handle, + VENC_DALRPC_UPDATE_BITRATE, pdata); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} + +static int venc_request_iframe(struct venc_dev *dvenc) +{ + int ret = 0; + + if (dvenc->state != VENC_STATE_START) + return -EINVAL; + + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_REQUEST_IFRAME, 1); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; +} + +static int venc_stop_read_msg(struct venc_dev *dvenc) +{ + struct venc_msg msg; + int ret = 0; + + msg.status_code = 0; + msg.msg_code = VENC_MSG_STOP_READING_MSG; + msg.msg_data_size = 0; + venc_put_msg(dvenc, &msg); + return ret; +} + +static int venc_q6_stop(struct venc_dev *dvenc) +{ + int ret = 0; + struct venc_pmem_list *plist; + unsigned long flags; + + wake_up(&dvenc->venc_msg_evt); + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + if (!dvenc->pmem_freed) { + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) + put_pmem_file(plist->buf.file); + dvenc->pmem_freed = 1; + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + + dvenc->state = VENC_STATE_STOP; + return ret; +} + +static int venc_translate_error(enum venc_status_code q6_status) +{ + int ret = 0; + + switch (q6_status) { + case VENC_STATUS_SUCCESS: + ret = VENC_S_SUCCESS; + break; + case VENC_STATUS_ERROR: + ret = VENC_S_EFAIL; + break; + case VENC_STATUS_INVALID_STATE: + ret = VENC_S_EINVALSTATE; + break; + case VENC_STATUS_FLUSHING: + ret = VENC_S_EFLUSHED; + break; + case VENC_STATUS_INVALID_PARAM: + ret = VENC_S_EBADPARAM; + break; + case VENC_STATUS_CMD_QUEUE_FULL: + ret = VENC_S_ECMDQFULL; + break; + case VENC_STATUS_CRITICAL: + ret = VENC_S_EFATAL; + break; + case VENC_STATUS_INSUFFICIENT_RESOURCES: + ret = VENC_S_ENOHWRES; + break; + case VENC_STATUS_TIMEOUT: + ret = VENC_S_ETIMEOUT; + break; + } + if (q6_status != VENC_STATUS_SUCCESS) + pr_err("%s: Q6 failed (%d)", __func__, (int)q6_status); + return ret; +} + +static void venc_q6_callback(void *data, int len, void *cookie) +{ + int status = 0; + struct venc_dev *dvenc = (struct venc_dev *)cookie; + struct venc_msg_type *q6_msg = NULL; + struct venc_msg msg, msg1; + union venc_msg_data smsg1, smsg2; + unsigned long msg_code = 0; + struct venc_input_payload *pload1; + struct venc_output_payload *pload2; + uint32_t * tmp = (uint32_t *) data; + + if (dvenc == NULL) { + pr_err("%s: empty driver parameter\n", __func__); + return; + } + if (tmp[2] == sizeof(struct venc_msg_type)) { + q6_msg = (struct venc_msg_type *)&tmp[3]; + } else { + pr_err("%s: callback with empty message (%d, %d)\n", + __func__, tmp[2], sizeof(struct venc_msg_type)); + return; + } + msg.msg_data_size = 0; + status = venc_translate_error(q6_msg->status); + switch ((enum venc_event_type_enum)q6_msg->event) { + case VENC_EVENT_START_STATUS: + dvenc->state = VENC_STATE_START; + msg_code = VENC_MSG_START; + break; + case VENC_EVENT_STOP_STATUS: + venc_q6_stop(dvenc); + msg_code = VENC_MSG_STOP; + break; + case VENC_EVENT_SUSPEND_STATUS: + dvenc->state = VENC_STATE_PAUSE; + msg_code = VENC_MSG_PAUSE; + break; + case VENC_EVENT_RESUME_STATUS: + dvenc->state = VENC_STATE_START; + msg_code = VENC_MSG_RESUME; + break; + case VENC_EVENT_FLUSH_STATUS: + smsg1.flush_ret.flush_mode = VENC_FLUSH_INPUT; + msg1.status_code = status; + msg1.msg_code = VENC_MSG_FLUSH; + msg1.msg_data = smsg1; + msg1.msg_data_size = sizeof(union venc_msg_data); + venc_put_msg(dvenc, &msg1); + smsg2.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; + msg_code = VENC_MSG_FLUSH; + msg.msg_data = smsg2; + msg.msg_data_size = sizeof(union venc_msg_data); + break; + case VENC_EVENT_RELEASE_INPUT: + pload1 = &((q6_msg->payload).input_payload); + TRACE("Release_input: data: 0x%x \n", pload1->data); + if (pload1 != NULL) { + msg.msg_data.buf.client_data = pload1->data; + msg_code = VENC_MSG_INPUT_BUFFER_DONE; + msg.msg_data_size = sizeof(union venc_msg_data); + } + break; + case VENC_EVENT_DELIVER_OUTPUT: + pload2 = &((q6_msg->payload).output_payload); + smsg1.buf.flags = 0; + if (pload2->flags & VENC_FLAG_SYNC_FRAME) + smsg1.buf.flags |= VENC_FLAG_SYNC_FRAME; + if (pload2->flags & VENC_FLAG_CODEC_CONFIG) + smsg1.buf.flags |= VENC_FLAG_CODEC_CONFIG; + if (pload2->flags & VENC_FLAG_END_OF_FRAME) + smsg1.buf.flags |= VENC_FLAG_END_OF_FRAME; + if (pload2->flags & VENC_FLAG_EOS) + smsg1.buf.flags |= VENC_FLAG_EOS; + smsg1.buf.len = pload2->size; + smsg1.buf.offset = 0; + smsg1.buf.time_stamp = pload2->time_stamp; + smsg1.buf.client_data = pload2->data; + msg_code = VENC_MSG_OUTPUT_BUFFER_DONE; + msg.msg_data = smsg1; + msg.msg_data_size = sizeof(union venc_msg_data); + break; + default: + pr_err("%s: invalid response from Q6 (%d)\n", __func__, + (int)q6_msg->event); + return; + } + msg.status_code = status; + msg.msg_code = msg_code; + venc_put_msg(dvenc, &msg); + return; +} + +static int venc_get_version(struct venc_dev *dvenc, void *argp) +{ + struct venc_version ver_info; + int ret = 0; + + ver_info.major = VENC_GET_MAJOR_VERSION(VENC_INTERFACE_VERSION); + ver_info.minor = VENC_GET_MINOR_VERSION(VENC_INTERFACE_VERSION); + + ret = copy_to_user(((struct venc_version *)argp), + &ver_info, sizeof(ver_info)); + if (ret) + pr_err("%s failed to copy_to_user\n", __func__); + + return ret; + +} + +static long q6venc_ioctl(struct file *file, u32 cmd, + unsigned long arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + struct venc_dev *dvenc = file->private_data; + + if (!dvenc || !dvenc->is_active) + return -EPERM; + + switch (cmd) { + case VENC_IOCTL_SET_INPUT_BUFFER: + ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT); + break; + case VENC_IOCTL_SET_OUTPUT_BUFFER: + ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT); + break; + case VENC_IOCTL_GET_SEQUENCE_HDR: + ret = venc_get_sequence_hdr(dvenc, argp); + break; + case VENC_IOCTL_SET_QP_RANGE: + ret = venc_set_qp_range(dvenc, argp); + break; + case VENC_IOCTL_SET_INTRA_PERIOD: + ret = venc_set_intra_period(dvenc, argp); + break; + case VENC_IOCTL_SET_INTRA_REFRESH: + ret = venc_set_intra_refresh(dvenc, argp); + break; + case VENC_IOCTL_SET_FRAME_RATE: + ret = venc_set_frame_rate(dvenc, argp); + break; + case VENC_IOCTL_SET_TARGET_BITRATE: + ret = venc_set_target_bitrate(dvenc, argp); + break; + case VENC_IOCTL_CMD_REQUEST_IFRAME: + if (dvenc->state == VENC_STATE_START) + ret = venc_request_iframe(dvenc); + break; + case VENC_IOCTL_CMD_START: + ret = venc_start(dvenc, argp); + break; + case VENC_IOCTL_CMD_STOP: + ret = venc_stop(dvenc); + break; + case VENC_IOCTL_CMD_PAUSE: + ret = venc_pause(dvenc); + break; + case VENC_IOCTL_CMD_RESUME: + ret = venc_resume(dvenc); + break; + case VENC_IOCTL_CMD_ENCODE_FRAME: + ret = venc_encode_frame(dvenc, argp); + break; + case VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER: + ret = venc_fill_output(dvenc, argp); + break; + case VENC_IOCTL_CMD_FLUSH: + ret = venc_flush(dvenc, argp); + break; + case VENC_IOCTL_CMD_READ_NEXT_MSG: + wait_event_interruptible(dvenc->venc_msg_evt, + venc_get_msg(dvenc, argp)); + break; + case VENC_IOCTL_CMD_STOP_READ_MSG: + ret = venc_stop_read_msg(dvenc); + break; + case VENC_IOCTL_GET_VERSION: + ret = venc_get_version(dvenc, argp); + break; + default: + pr_err("%s: invalid ioctl code (%d)\n", __func__, cmd); + ret = -ENOTTY; + break; + } + return ret; +} + +static int q6venc_open(struct inode *inode, struct file *file) +{ + int i; + int ret = 0; + struct venc_dev *dvenc; + struct venc_msg_list *plist, *tmp; + struct dal_info version_info; + + dvenc = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); + if (!dvenc) { + pr_err("%s: unable to allocate memory for struct venc_dev\n", + __func__); + return -ENOMEM; + } + file->private_data = dvenc; + INIT_LIST_HEAD(&dvenc->venc_msg_list_head); + INIT_LIST_HEAD(&dvenc->venc_msg_list_free); + INIT_LIST_HEAD(&dvenc->venc_pmem_list_head); + init_waitqueue_head(&dvenc->venc_msg_evt); + spin_lock_init(&dvenc->venc_msg_list_lock); + spin_lock_init(&dvenc->venc_pmem_list_lock); + venc_ref++; + for (i = 0; i < VENC_MSG_MAX; i++) { + plist = kzalloc(sizeof(struct venc_msg_list), GFP_KERNEL); + if (!plist) { + pr_err("%s: kzalloc failed\n", __func__); + ret = -ENOMEM; + goto err_venc_create_msg_list; + } + list_add(&plist->list, &dvenc->venc_msg_list_free); + } + dvenc->q6_handle = + dal_attach(DALDEVICEID_VENC_DEVICE, DALDEVICEID_VENC_PORTNAME, 1, + venc_q6_callback, (void *)dvenc); + if (!(dvenc->q6_handle)) { + pr_err("%s: daldevice_attach failed (%d)\n", __func__, ret); + goto err_venc_dal_attach; + } + ret = dal_call_f9(dvenc->q6_handle, DAL_OP_INFO, &version_info, + sizeof(struct dal_info)); + if (ret) { + pr_err("%s: failed to get version\n", __func__); + goto err_venc_dal_open; + } + if (venc_check_version(VENC_INTERFACE_VERSION, version_info.version)) { + pr_err("%s: driver version mismatch\n", __func__); + goto err_venc_dal_open; + } + ret = dal_call_f0(dvenc->q6_handle, DAL_OP_OPEN, 1); + if (ret) { + pr_err("%s: dal_call_open failed (%d)\n", __func__, ret); + goto err_venc_dal_open; + } + dvenc->state = VENC_STATE_STOP; + dvenc->is_active = 1; + prevent_sleep(); + return ret; +err_venc_dal_open: + dal_detach(dvenc->q6_handle); +err_venc_dal_attach: + list_for_each_entry_safe(plist, tmp, &dvenc->venc_msg_list_free, list) { + list_del(&plist->list); + kfree(plist); + } +err_venc_create_msg_list: + kfree(dvenc); + venc_ref--; + return ret; +} + +static int q6venc_release(struct inode *inode, struct file *file) +{ + int ret = 0; + struct venc_msg_list *l, *n; + struct venc_pmem_list *plist, *m; + struct venc_dev *dvenc; + unsigned long flags; + + venc_ref--; + dvenc = file->private_data; + dvenc->is_active = 0; + wake_up_all(&dvenc->venc_msg_evt); + dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); + dal_call_f0(dvenc->q6_handle, DAL_OP_CLOSE, 1); + dal_detach(dvenc->q6_handle); + list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_free, list) { + list_del(&l->list); + kfree(l); + } + list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) { + list_del(&l->list); + kfree(l); + } + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + if (!dvenc->pmem_freed) { + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) + put_pmem_file(plist->buf.file); + dvenc->pmem_freed = 1; + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + + list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) { + list_del(&plist->list); + kfree(plist); + } + kfree(dvenc); + allow_sleep(); + return ret; +} + +const struct file_operations q6venc_fops = { + .owner = THIS_MODULE, + .open = q6venc_open, + .release = q6venc_release, + .unlocked_ioctl = q6venc_ioctl, +}; + +static int __init q6venc_init(void) +{ + int ret = 0; + + pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend"); + + venc_device_p = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); + if (!venc_device_p) { + pr_err("%s: unable to allocate memory for venc_device_p\n", + __func__); + return -ENOMEM; + } + ret = alloc_chrdev_region(&venc_dev_num, 0, 1, VENC_NAME); + if (ret < 0) { + pr_err("%s: alloc_chrdev_region failed (%d)\n", __func__, + ret); + return ret; + } + venc_class = class_create(THIS_MODULE, VENC_NAME); + if (IS_ERR(venc_class)) { + ret = PTR_ERR(venc_class); + pr_err("%s: failed to create venc_class (%d)\n", + __func__, ret); + goto err_venc_class_create; + } + venc_device_p->class_devp = + device_create(venc_class, NULL, venc_dev_num, NULL, + VENC_NAME); + if (IS_ERR(venc_device_p->class_devp)) { + ret = PTR_ERR(venc_device_p->class_devp); + pr_err("%s: failed to create class_device (%d)\n", __func__, + ret); + goto err_venc_class_device_create; + } + cdev_init(&cdev, &q6venc_fops); + cdev.owner = THIS_MODULE; + ret = cdev_add(&cdev, venc_dev_num, 1); + if (ret < 0) { + pr_err("%s: cdev_add failed (%d)\n", __func__, ret); + goto err_venc_cdev_add; + } + init_waitqueue_head(&venc_device_p->venc_msg_evt); + return ret; + +err_venc_cdev_add: + device_destroy(venc_class, venc_dev_num); +err_venc_class_device_create: + class_destroy(venc_class); +err_venc_class_create: + unregister_chrdev_region(venc_dev_num, 1); + return ret; +} + +static void __exit q6venc_exit(void) +{ + cdev_del(&(cdev)); + device_destroy(venc_class, venc_dev_num); + class_destroy(venc_class); + unregister_chrdev_region(venc_dev_num, 1); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Video encoder driver for QDSP6"); +MODULE_VERSION("2.0"); +module_init(q6venc_init); +module_exit(q6venc_exit); diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c new file mode 100644 index 00000000000..c6bddb883ea --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -0,0 +1,263 @@ +/* arch/arm/mach-msm/qdsp6/pcm_in.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct pcm { + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + uint32_t buffer_size; + uint32_t rec_mode; +}; + +#define BUFSZ (256) + +void audio_client_dump(struct audio_client *ac); + +static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_SET_VOLUME: + pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__); + break; + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__); + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + case AUDIO_START: { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + rc = 0; + + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) { + rc = -EFAULT; + break; + } + + if (pcm->ac) { + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + rc = -EBUSY; + } else { + pcm->ac = q6audio_open_pcm(pcm->buffer_size, + pcm->sample_rate, pcm->channel_count, + pcm->rec_mode, acdb_id); + if (!pcm->ac) { + pr_err("[%s:%s] pcm open session failed\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + } + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + if (!config.channel_count || config.channel_count > 2) { + rc = -EINVAL; + pr_err("[%s:%s] invalid channelcount %d\n", + __MM_FILE__, __func__, config.channel_count); + break; + } + if (config.sample_rate < 8000 || config.sample_rate > 48000) { + rc = -EINVAL; + pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__, + __func__, config.sample_rate); + break; + } + if (config.buffer_size < 128 || config.buffer_size > 8192) { + rc = -EINVAL; + pr_err("[%s:%s] invalid buffsize %d\n", __MM_FILE__, + __func__, config.buffer_size); + break; + } + + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + break; + } + case AUDIO_SET_INCALL: { + struct msm_voicerec_mode voicerec_mode; + pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__); + if (copy_from_user(&voicerec_mode, (void *)arg, + sizeof(struct msm_voicerec_mode))) + return -EFAULT; + if (voicerec_mode.rec_mode != AUDIO_FLAG_READ && + voicerec_mode.rec_mode != AUDIO_FLAG_INCALL_MIXED) { + pcm->rec_mode = AUDIO_FLAG_READ; + pr_err("[%s:%s] invalid rec_mode\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } else + pcm->rec_mode = voicerec_mode.rec_mode; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = pcm->buffer_size; + config.buffer_count = 2; + config.sample_rate = pcm->sample_rate; + config.channel_count = pcm->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + break; + } + default: + rc = -EINVAL; + } + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int q6_in_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + + if (!pcm) + return -ENOMEM; + + pcm->channel_count = 1; + pcm->sample_rate = 8000; + pcm->buffer_size = BUFSZ; + pcm->rec_mode = AUDIO_FLAG_READ; + file->private_data = pcm; + return 0; +} + +static ssize_t q6_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + int res; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + ac = pcm->ac; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { + audio_client_dump(ac); + pr_err("[%s:%s] timeout. dsp dead?\n", + __MM_FILE__, __func__); + q6audio_dsp_not_responding(); + } + pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d", __MM_FILE__, + __func__, ab->data, ac->cpu_buf); + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } +fail: + res = buf - start; + return res; +} + +static int q6_in_release(struct inode *inode, struct file *file) +{ + + int rc = 0; + struct pcm *pcm = file->private_data; + if (pcm->ac) + rc = q6audio_close(pcm->ac); + kfree(pcm); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return rc; +} + +static struct file_operations q6_in_fops = { + .owner = THIS_MODULE, + .open = q6_in_open, + .read = q6_in_read, + .release = q6_in_release, + .unlocked_ioctl = q6_in_ioctl, +}; + +struct miscdevice q6_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &q6_in_fops, +}; + +static int __init q6_in_init(void) { + return misc_register(&q6_in_misc); +} + +device_initcall(q6_in_init); diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c new file mode 100644 index 00000000000..2e91cb2fed8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -0,0 +1,276 @@ +/* arch/arm/mach-msm/qdsp6/pcm_out.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +void audio_client_dump(struct audio_client *ac); + +#define BUFSZ (3072) + +struct pcm { + struct mutex lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + size_t buffer_size; +}; + +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&pcm->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: { + int vol; + if (!pcm->ac) { + pr_err("%s: cannot set volume before AUDIO_START!\n", + __func__); + rc = -EINVAL; + break; + } + if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_VOLUME: vol = %d\n", __MM_FILE__, + __func__, vol); + rc = q6audio_set_stream_volume(pcm->ac, vol); + break; + } + case AUDIO_START: { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) { + pr_info("[%s:%s] copy acdb_id from user failed\n", + __MM_FILE__, __func__); + rc = -EFAULT; + break; + } + if (pcm->ac) { + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + rc = -EBUSY; + } else { + pcm->ac = q6audio_open_pcm(pcm->buffer_size, + pcm->sample_rate, + pcm->channel_count, + AUDIO_FLAG_WRITE, acdb_id); + if (!pcm->ac) { + pr_err("[%s:%s] pcm open session failed\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + } + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (pcm->ac) { + rc = -EBUSY; + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + break; + } + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + if (config.channel_count < 1 || config.channel_count > 2) { + rc = -EINVAL; + pr_err("[%s:%s] invalid channelcount %d\n", + __MM_FILE__, __func__, config.channel_count); + break; + } + if (config.sample_rate < 8000 || config.sample_rate > 48000) { + rc = -EINVAL; + pr_err("[%s:%s] invalid samplerate %d\n", __MM_FILE__, + __func__, config.sample_rate); + break; + } + if (config.buffer_size < 128 || config.buffer_size > 8192) { + rc = -EINVAL; + pr_err("[%s:%s] invalid buffsize %d\n", __MM_FILE__, + __func__, config.buffer_size); + break; + } + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = pcm->buffer_size; + config.buffer_count = 2; + config.sample_rate = pcm->sample_rate; + config.channel_count = pcm->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + pr_debug("[%s:%s] GET_CONFIG: samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, config.sample_rate, + config.channel_count); + break; + } + case AUDIO_SET_EQ: { + struct msm_audio_eq_stream_config eq_config; + pr_debug("[%s:%s] SET_EQ\n", __MM_FILE__, __func__); + if (copy_from_user(&eq_config, (void *) arg, + sizeof(eq_config))) { + rc = -EFAULT; + break; + } + rc = q6audio_set_stream_eq_pcm(pcm->ac, (void *) &eq_config); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&pcm->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int pcm_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + + if (!pcm) + return -ENOMEM; + + mutex_init(&pcm->lock); + pcm->channel_count = 2; + pcm->sample_rate = 44100; + pcm->buffer_size = BUFSZ; + file->private_data = pcm; + return 0; +} + +static ssize_t pcm_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + if (!pcm->ac) + pcm_ioctl(file, AUDIO_START, 0); + + ac = pcm->ac; + if (!ac) + return -ENODEV; + + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { + audio_client_dump(ac); + pr_err("[%s:%s] timeout. dsp dead?\n", + __MM_FILE__, __func__); + q6audio_dsp_not_responding(); + } + pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d", __MM_FILE__, + __func__, ab->data, ac->cpu_buf); + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = 1; + ab->actual_size = xfer; + q6audio_write(ac, ab); + ac->cpu_buf ^= 1; + } + + return buf - start; +} + +static int pcm_release(struct inode *inode, struct file *file) +{ + struct pcm *pcm = file->private_data; + if (pcm->ac) + q6audio_close(pcm->ac); + kfree(pcm); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return 0; +} + +static struct file_operations pcm_fops = { + .owner = THIS_MODULE, + .open = pcm_open, + .write = pcm_write, + .release = pcm_release, + .unlocked_ioctl = pcm_ioctl, +}; + +struct miscdevice pcm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &pcm_fops, +}; + +static int __init pcm_init(void) { + return misc_register(&pcm_misc); +} + +device_initcall(pcm_init); diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c new file mode 100644 index 00000000000..2d015fc28c1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -0,0 +1,2158 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dal.h" +#include "dal_audio.h" +#include "dal_audio_format.h" +#include "dal_acdb.h" +#include "dal_adie.h" +#include +#include + +#include + +#include + +#include "q6audio_devices.h" +#include + + +struct q6_hw_info { + int min_gain; + int max_gain; +}; + +/* TODO: provide mechanism to configure from board file */ + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -400, + .max_gain = 1100, + }, + [Q6_HW_HEADSET] = { + .min_gain = -1100, + .max_gain = 400, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1000, + .max_gain = 500, + }, + [Q6_HW_TTY] = { + .min_gain = 0, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -1100, + .max_gain = 400, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -1100, + .max_gain = 400, + }, +}; + +static struct wake_lock wakelock; +static struct pm_qos_request pm_qos_req; +static int idlecount; +static DEFINE_MUTEX(idlecount_lock); + +void audio_prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&wakelock); + pm_qos_update_request(&pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + } + mutex_unlock(&idlecount_lock); +} + +void audio_allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static struct clk *icodec_rx_clk; +static struct clk *icodec_tx_clk; +static struct clk *ecodec_clk; +static struct clk *sdac_clk; + +static struct q6audio_analog_ops default_analog_ops; +static struct q6audio_analog_ops *analog_ops = &default_analog_ops; +static uint32_t tx_clk_freq = 8000; +static int tx_mute_status = 0; +static int rx_vol_level = 100; +static uint32_t tx_acdb = 0; +static uint32_t rx_acdb = 0; + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) +{ + analog_ops = ops; +} + +static struct q6_device_info *q6_lookup_device(uint32_t device_id, + uint32_t acdb_id) +{ + struct q6_device_info *di = q6_audio_devices; + + pr_debug("[%s:%s] device_id = 0x%x, acdb_id = %d\n", __MM_FILE__, + __func__, device_id, acdb_id); + if (acdb_id) { + for (;;) { + if (di->cad_id == acdb_id && di->id == device_id) + return di; + if (di->id == 0) { + pr_err("[%s:%s] bogus id 0x%08x\n", + __MM_FILE__, __func__, device_id); + return di; + } + di++; + } + } else { + for (;;) { + if (di->id == device_id) + return di; + if (di->id == 0) { + pr_err("[%s:%s] bogus id 0x%08x\n", + __MM_FILE__, __func__, device_id); + return di; + } + di++; + } + } +} + +static uint32_t q6_device_to_codec(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id, 0); + return di->codec; +} + +static uint32_t q6_device_to_dir(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id, 0); + return di->dir; +} + +static uint32_t q6_device_to_cad_id(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id, 0); + return di->cad_id; +} + +static uint32_t q6_device_to_path(uint32_t device_id, uint32_t acdb_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id, acdb_id); + return di->path; +} + +static uint32_t q6_device_to_rate(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id, 0); + return di->rate; +} + +int q6_device_volume(uint32_t device_id, int level) +{ + struct q6_device_info *di = q6_lookup_device(device_id, 0); + struct q6_hw_info *hw; + + hw = &q6_audio_hw[di->hw]; + + return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100; +} + +static inline int adie_open(struct dal_client *client) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return dal_call_f0(client, DAL_OP_OPEN, 0); +} + +static inline int adie_close(struct dal_client *client) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return dal_call_f0(client, DAL_OP_CLOSE, 0); +} + +static inline int adie_set_path(struct dal_client *client, + uint32_t id, uint32_t path_type) +{ + pr_debug("[%s:%s] id = 0x%x, path_type = %d\n", __MM_FILE__, + __func__, id, path_type); + return dal_call_f1(client, ADIE_OP_SET_PATH, id, path_type); +} + +static inline int adie_set_path_freq_plan(struct dal_client *client, + uint32_t path_type, uint32_t plan) +{ + pr_debug("[%s:%s] path_type = %d, plan = %d\n", __MM_FILE__, + __func__, path_type, plan); + return dal_call_f1(client, ADIE_OP_SET_PATH_FREQUENCY_PLAN, + path_type, plan); +} + +static inline int adie_proceed_to_stage(struct dal_client *client, + uint32_t path_type, uint32_t stage) +{ + pr_debug("[%s:%s] path_type = %d, stage = 0x%x\n", __MM_FILE__, + __func__, path_type, stage); + return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE, + path_type, stage); +} + +static inline int adie_mute_path(struct dal_client *client, + uint32_t path_type, uint32_t mute_state) +{ + pr_debug("[%s:%s] path_type = %d, mute = %d\n", __MM_FILE__, __func__, + path_type, mute_state); + return dal_call_f1(client, ADIE_OP_MUTE_PATH, path_type, mute_state); +} + +static int adie_refcount; + +static struct dal_client *adie; +static struct dal_client *adsp; +static struct dal_client *acdb; + +static int adie_enable(void) +{ + adie_refcount++; + if (adie_refcount == 1) + adie_open(adie); + return 0; +} + +static int adie_disable(void) +{ + adie_refcount--; + if (adie_refcount == 0) + adie_close(adie); + return 0; +} + +/* 4k PMEM used for exchanging acdb device config tables + * and stream format descriptions with the DSP. + */ +static char *audio_data; +static int32_t audio_phys; + +#define SESSION_MIN 0 +#define SESSION_MAX 64 + +static DEFINE_MUTEX(session_lock); +static DEFINE_MUTEX(audio_lock); + +static struct audio_client *session[SESSION_MAX]; + +static int session_alloc(struct audio_client *ac) +{ + int n; + + mutex_lock(&session_lock); + for (n = SESSION_MIN; n < SESSION_MAX; n++) { + if (!session[n]) { + session[n] = ac; + mutex_unlock(&session_lock); + pr_debug("[%s:%s] session = %d\n", __MM_FILE__, + __func__, n); + return n; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void session_free(int n, struct audio_client *ac) +{ + mutex_lock(&session_lock); + if (session[n] == ac) { + session[n] = 0; + pr_debug("[%s:%s] session = %d\n", __MM_FILE__, __func__, n); + } + mutex_unlock(&session_lock); +} + +static void audio_client_free(struct audio_client *ac) +{ + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + session_free(ac->session, ac); + + if (ac->buf[0].data) { + iounmap(ac->buf[0].data); + pmem_kfree(ac->buf[0].phys); + } + if (ac->buf[1].data) { + iounmap(ac->buf[1].data); + pmem_kfree(ac->buf[1].phys); + } + kfree(ac); +} + +static struct audio_client *audio_client_alloc(unsigned bufsz) +{ + struct audio_client *ac; + int n; + + pr_debug("[%s:%s] bufsz = %d\n", __MM_FILE__, __func__, bufsz); + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + if (!ac) + return 0; + + n = session_alloc(ac); + if (n < 0) + goto fail_session; + ac->session = n; + + if (bufsz > 0) { + ac->buf[0].phys = pmem_kalloc(bufsz, + PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K); + ac->buf[0].data = ioremap(ac->buf[0].phys, bufsz); + if (!ac->buf[0].data) + goto fail; + ac->buf[1].phys = pmem_kalloc(bufsz, + PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K); + ac->buf[1].data = ioremap(ac->buf[1].phys, bufsz); + if (!ac->buf[1].data) + goto fail; + + ac->buf[0].size = bufsz; + ac->buf[1].size = bufsz; + } + + init_waitqueue_head(&ac->wait); + ac->client = adsp; + + return ac; + +fail: + session_free(n, ac); +fail_session: + audio_client_free(ac); + return 0; +} + +void audio_client_dump(struct audio_client *ac) +{ + dal_trace_dump(ac->client); +} + +static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) +{ + struct adsp_command_hdr *hdr = ptr; + uint32_t tmp; + int r; + + hdr->size = len - sizeof(u32); + hdr->dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP); + hdr->src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_APP); + hdr->context = ac->session; + ac->cb_status = -EBUSY; + r = dal_call(ac->client, AUDIO_OP_CONTROL, 5, ptr, len, &tmp, sizeof(tmp)); + if (r != 4) + return -EIO; + if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) { + dal_trace_dump(ac->client); + pr_err("[%s:%s] timeout. dsp dead?\n", __MM_FILE__, __func__); + q6audio_dsp_not_responding(); + } + return ac->cb_status; +} + +static int audio_command(struct audio_client *ac, uint32_t cmd) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); + rpc.opcode = cmd; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_open_control(struct audio_client *ac) +{ + struct adsp_open_command rpc; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_out_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.buf_max_size = bufsz; + + pr_debug("[%s:%s]ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_in_open(struct audio_client *ac, uint32_t bufsz, + uint32_t flags, uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + if (flags == AUDIO_FLAG_READ) + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + else + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD; + + rpc.buf_max_size = bufsz; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_auxpcm_out_open(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.mode = ADSP_AUDIO_OPEN_STREAM_MODE_AUX_PCM; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_auxpcm_in_open(struct audio_client *ac, uint32_t rate, + uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.mode = ADSP_AUDIO_OPEN_STREAM_MODE_AUX_PCM; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_MP3; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.buf_max_size = bufsz; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_dtmf_open(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_DTMF; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_aac_open(struct audio_client *ac, uint32_t bufsz, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, uint32_t flags, + uint32_t stream_format) +{ + struct adsp_open_command rpc; + int audio_object_type; + int index = sizeof(u32); + u32 *aac_type = NULL; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.binary.format = ADSP_AUDIO_FORMAT_MPEG4_AAC; + /* only 48k sample rate is supported */ + sample_rate = 3; + /* AAC OBJECT LC */ + audio_object_type = 2; + + aac_type = (u32 *)rpc.format.binary.data; + switch (stream_format) { + case AUDIO_AAC_FORMAT_ADTS: + /* AAC Encoder expect MPEG4_ADTS media type */ + *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; + break; + case AUDIO_AAC_FORMAT_RAW: + /* for ADIF recording */ + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + } + + rpc.format.binary.data[index++] = (u8)( + ((audio_object_type & 0x1F) << 3) | + ((sample_rate >> 1) & 0x7)); + rpc.format.binary.data[index] = (u8)( + ((sample_rate & 0x1) << 7) | + ((channels & 0x7) << 3)); + rpc.format.binary.num_bytes = index + 1; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + + if (flags == AUDIO_FLAG_READ) + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + else + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD; + + rpc.buf_max_size = bufsz; + rpc.config.aac.bit_rate = bit_rate; + rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_qcp_open(struct audio_client *ac, uint32_t bufsz, + uint32_t min_rate, uint32_t max_rate, + uint32_t flags, uint32_t format) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = format; + rpc.format.standard.channels = 1; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = 8000; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + + if (flags == AUDIO_FLAG_READ) + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + else + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD; + rpc.buf_max_size = bufsz; + rpc.config.evrc.min_rate = min_rate; + rpc.config.evrc.max_rate = max_rate; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_amrnb_open(struct audio_client *ac, uint32_t bufsz, + uint32_t enc_mode, uint32_t flags, + uint32_t dtx_enable) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_AMRNB_FS; + rpc.format.standard.channels = 1; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = 8000; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + + if (flags == AUDIO_FLAG_READ) + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + else + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD; + + rpc.buf_max_size = bufsz; + rpc.config.amr.mode = enc_mode; + rpc.config.amr.dtx_mode = dtx_enable; + rpc.config.amr.enable = 1; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + + + +static int audio_close(struct audio_client *ac) +{ + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE); + return 0; +} + +static int audio_set_table(struct audio_client *ac, + uint32_t device_id, int size) +{ + struct adsp_set_dev_cfg_table_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE; + if (q6_device_to_dir(device_id) == Q6_TX) { + if (tx_clk_freq > 16000) + rpc.hdr.data = 48000; + else if (tx_clk_freq > 8000) + rpc.hdr.data = 16000; + else + rpc.hdr.data = 8000; + } + rpc.device_id = device_id; + rpc.phys_addr = audio_phys; + rpc.phys_size = size; + rpc.phys_used = size; + + pr_debug("[%s:%s] ac = %p, device_id = 0x%x, size = %d\n", __MM_FILE__, + __func__, ac, device_id, size); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + uint32_t res; + int r; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.size = sizeof(rpc) - sizeof(u32); + rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP); + rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_APP); + rpc.hdr.context = ac->session; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_TX; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->actual_size; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), + &res, sizeof(res)); + return 0; +} + +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + uint32_t res; + int r; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.size = sizeof(rpc) - sizeof(u32); + rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP); + rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_APP); + rpc.hdr.context = ac->session; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_RX; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->actual_size; + + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), + &res, sizeof(res)); + return 0; +} + +static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + pr_debug("[%s:%s] volume = %d\n", __MM_FILE__, __func__, volume); + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL; + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.volume = volume; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + pr_debug("[%s:%s] mute = %d, dev_id = 0x%x\n", __MM_FILE__, + __func__, mute, dev_id); + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE; + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.mute = !!mute; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + pr_debug("[%s:%s] mute = %d\n", __MM_FILE__, __func__, mute); + if (mute < 0 || mute > 3) { + pr_err("[%s:%s] invalid mute status %d\n", __MM_FILE__, + __func__, mute); + return -EINVAL; + } + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE; + if ((mute == STREAM_UNMUTE) || (mute == STREAM_MUTE)) { + rpc.device_id = ADSP_AUDIO_DEVICE_ID_VOICE; + rpc.path = ADSP_PATH_TX_CNG_DIS; + } else { + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + } + mute &= 0x01; + rpc.mute = !!mute; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_stream_volume(struct audio_client *ac, int volume) +{ + struct adsp_set_volume_command rpc; + int rc; + + pr_debug("[%s:%s] volume = %d\n", __MM_FILE__, __func__, volume); + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL; + rpc.volume = volume; + rc = audio_ioctl(ac, &rpc, sizeof(rpc)); + return rc; +} + +static int audio_stream_mute(struct audio_client *ac, int mute) +{ + struct adsp_set_mute_command rpc; + int rc; + + pr_debug("[%s:%s] mute = %d\n", __MM_FILE__, __func__, mute); + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE; + rpc.mute = mute; + rc = audio_ioctl(ac, &rpc, sizeof(rpc)); + return rc; +} + +static void callback(void *data, int len, void *cookie) +{ + struct adsp_event_hdr *e = data; + struct audio_client *ac; + struct adsp_buffer_event *abe = data; + + if (e->context >= SESSION_MAX) { + pr_err("[%s:%s] bogus session %d\n", __MM_FILE__, __func__, + e->context); + return; + } + ac = session[e->context]; + if (!ac) { + pr_err("[%s:%s] unknown session %d\n", __MM_FILE__, __func__, + e->context); + return; + } + + if (e->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) { + pr_debug("[%s:%s] CB Stream eos, ac = %p\n", + __MM_FILE__, __func__, ac); + if (e->status) + pr_err("[%s:%s] playback status %d\n", __MM_FILE__, + __func__, e->status); + if (ac->cb_status == -EBUSY) { + ac->cb_status = e->status; + wake_up(&ac->wait); + } + return; + } + + if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { + pr_debug("[%s:%s] CB done, ac = %p, status = %d\n", + __MM_FILE__, __func__, ac, e->status); + if (e->status) + pr_err("[%s:%s] buffer status %d\n", __MM_FILE__, + __func__, e->status); + + ac->buf[ac->dsp_buf].actual_size = abe->buffer.actual_size; + ac->buf[ac->dsp_buf].used = 0; + ac->dsp_buf ^= 1; + wake_up(&ac->wait); + return; + } + + pr_debug("[%s:%s] ac = %p, event_id = 0x%x, status = %d\n", + __MM_FILE__, __func__, ac, e->event_id, e->status); + if (e->status) + pr_warning("audio_cb: s=%d e=%08x status=%d\n", + e->context, e->event_id, e->status); + if (ac->cb_status == -EBUSY) { + ac->cb_status = e->status; + wake_up(&ac->wait); + } +} + +static void audio_init(struct dal_client *client) +{ + u32 tmp[3]; + + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + tmp[0] = 2 * sizeof(u32); + tmp[1] = 0; + tmp[2] = 0; + dal_call(client, AUDIO_OP_INIT, 5, tmp, sizeof(tmp), + tmp, sizeof(u32)); +} + +static struct audio_client *ac_control; + +static int q6audio_init(void) +{ + struct audio_client *ac = 0; + int res; + + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + mutex_lock(&audio_lock); + if (ac_control) { + res = 0; + goto done; + } + + pr_info("[%s:%s] codecs\n", __MM_FILE__, __func__); + icodec_rx_clk = clk_get(0, "icodec_rx_clk"); + icodec_tx_clk = clk_get(0, "icodec_tx_clk"); + ecodec_clk = clk_get(0, "ecodec_clk"); + sdac_clk = clk_get(0, "sdac_clk"); + audio_phys = pmem_kalloc(4096, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K); + audio_data = ioremap(audio_phys, 4096); + + pr_info("[%s:%s] attach ADSP\n", __MM_FILE__, __func__); + adsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, 1, + callback, 0); + if (!adsp) { + pr_err("[%s:%s] cannot attach to adsp\n", __MM_FILE__, + __func__); + res = -ENODEV; + goto done; + } + pr_info("[%s:%s] INIT\n", __MM_FILE__, __func__); + audio_init(adsp); + dal_trace(adsp); + + ac = audio_client_alloc(0); + if (!ac) { + pr_err("[%s:%s] cannot allocate client\n", + __MM_FILE__, __func__); + res = -ENOMEM; + goto done; + } + + pr_info("[%s:%s] OPEN control\n", __MM_FILE__, __func__); + if (audio_open_control(ac)) { + pr_err("[%s:%s] cannot open control channel\n", + __MM_FILE__, __func__); + res = -ENODEV; + goto done; + } + + pr_info("[%s:%s] attach ACDB\n", __MM_FILE__, __func__); + acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0, 0); + if (!acdb) { + pr_err("[%s:%s] cannot attach to acdb channel\n", + __MM_FILE__, __func__); + res = -ENODEV; + goto done; + } + + pr_info("[%s:%s] attach ADIE\n", __MM_FILE__, __func__); + adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0, 0); + if (!adie) { + pr_err("[%s:%s] cannot attach to adie\n", + __MM_FILE__, __func__); + res = -ENODEV; + goto done; + } + if (analog_ops->init) + analog_ops->init(); + + res = 0; + ac_control = ac; + + pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "audio_pcm_suspend"); +done: + if ((res < 0) && ac) + audio_client_free(ac); + mutex_unlock(&audio_lock); + + pr_debug("[%s:%s] res = %d\n", __MM_FILE__, __func__, res); + return res; +} + +struct audio_config_data { + uint32_t device_id; + uint32_t sample_rate; + uint32_t offset; + uint32_t length; +}; + +struct audio_config_database { + uint8_t magic[8]; + uint32_t entry_count; + uint32_t unused; + struct audio_config_data entry[0]; +}; + +void *acdb_data; +const struct firmware *acdb_fw; +extern struct miscdevice q6_control_device; + +static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate) +{ + struct acdb_cmd_device_table rpc; + struct acdb_result res; + int r; + + pr_debug("[%s:%s] device_id = 0x%x, samplerate = %d\n", __MM_FILE__, + __func__, device_id, sample_rate); + if (q6audio_init()) + return 0; + + memset(audio_data, 0, 4096); + memset(&rpc, 0, sizeof(rpc)); + + rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t)); + rpc.command_id = ACDB_GET_DEVICE_TABLE; + rpc.device_id = device_id; + rpc.sample_rate_id = sample_rate; + rpc.total_bytes = 4096; + rpc.unmapped_buf = audio_phys; + rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t)); + + r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc), + &res, sizeof(res)); + + if ((r == sizeof(res)) && (res.dal_status == 0)) + return res.used_bytes; + + return -EIO; +} + +static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX; +static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; +static uint32_t audio_rx_device_group = -1; +static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX; +static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; +static uint32_t audio_tx_device_group = -1; + +static int qdsp6_devchg_notify(struct audio_client *ac, + uint32_t dev_type, uint32_t dev_id) +{ + struct adsp_device_switch_command rpc; + + if (dev_type != ADSP_AUDIO_RX_DEVICE && + dev_type != ADSP_AUDIO_TX_DEVICE) + return -EINVAL; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE; + if (dev_type == ADSP_AUDIO_RX_DEVICE) { + rpc.old_device = audio_rx_device_id; + rpc.new_device = dev_id; + } else { + rpc.old_device = audio_tx_device_id; + rpc.new_device = dev_id; + } + rpc.device_class = 0; + rpc.device_type = dev_type; + pr_debug("[%s:%s] dev_id = 0x%x\n", __MM_FILE__, __func__, dev_id); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int qdsp6_standby(struct audio_client *ac) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY); +} + +static int qdsp6_start(struct audio_client *ac) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT); +} + +static void audio_rx_analog_enable(int en) +{ + pr_debug("[%s:%s] audio_rx_device_id = 0x%x, en = %d\n", __MM_FILE__, + __func__, audio_rx_device_id, en); + switch (audio_rx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO: + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO: + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR: + if (analog_ops->receiver_enable) + analog_ops->receiver_enable(en); + break; + } +} + +static void audio_tx_analog_enable(int en) +{ + pr_debug("[%s:%s] audio_tx_device_id = 0x%x, en = %d\n", __MM_FILE__, + __func__, audio_tx_device_id, en); + switch (audio_tx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC: + if (analog_ops->int_mic_enable) + analog_ops->int_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC: + case ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC: + if (analog_ops->ext_mic_enable) + analog_ops->ext_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + } +} + +static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) +{ + uint32_t sample_rate; + int sz; + + pr_debug("[%s:%s] adev = 0x%x, acdb_id = 0x%x\n", __MM_FILE__, + __func__, adev, acdb_id); + if (q6_device_to_dir(adev) == Q6_RX) { + rx_acdb = acdb_id; + sample_rate = q6_device_to_rate(adev); + } else { + + tx_acdb = acdb_id; + if (tx_clk_freq > 16000) + sample_rate = 48000; + else if (tx_clk_freq > 8000) + sample_rate = 16000; + else + sample_rate = 8000; + } + + if (acdb_id == 0) + acdb_id = q6_device_to_cad_id(adev); + + sz = acdb_get_config_table(acdb_id, sample_rate); + audio_set_table(ac_control, adev, sz); + + return 0; +} + +static void adie_rx_path_enable(uint32_t acdb_id) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + if (audio_rx_path_id) { + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_ANALOG_READY); + } +} + +static void q6_rx_path_enable(int reconf, uint32_t acdb_id) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + audio_update_acdb(audio_rx_device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); +} + +static void _audio_rx_path_enable(int reconf, uint32_t acdb_id) +{ + pr_debug("[%s:%s] reconf = %d\n", __MM_FILE__, __func__, reconf); + q6_rx_path_enable(reconf, acdb_id); + if (audio_rx_path_id) + adie_rx_path_enable(acdb_id); + audio_rx_analog_enable(1); +} + +static void _audio_tx_path_enable(int reconf, uint32_t acdb_id) +{ + pr_debug("[%s:%s] reconf = %d, tx_clk_freq = %d\n", __MM_FILE__, + __func__, reconf, tx_clk_freq); + audio_tx_analog_enable(1); + + if (audio_tx_path_id) { + adie_enable(); + adie_set_path(adie, audio_tx_path_id, ADIE_PATH_TX); + + if (tx_clk_freq > 16000) + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 48000); + else if (tx_clk_freq > 8000) + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 16000); + else + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 8000); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, + ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_TX, + ADIE_STAGE_DIGITAL_ANALOG_READY); + } + + + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + audio_tx_device_id); + audio_update_acdb(audio_tx_device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + + audio_tx_mute(ac_control, audio_tx_device_id, tx_mute_status); +} + +static void _audio_rx_path_disable(void) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + audio_rx_analog_enable(0); + + if (audio_rx_path_id) { + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_OFF); + adie_disable(); + } +} + +static void _audio_tx_path_disable(void) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + audio_tx_analog_enable(0); + + if (audio_tx_path_id) { + adie_proceed_to_stage(adie, ADIE_PATH_TX, + ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_TX, + ADIE_STAGE_DIGITAL_OFF); + adie_disable(); + } +} + +static int icodec_rx_clk_refcount; +static int icodec_tx_clk_refcount; +static int ecodec_clk_refcount; +static int sdac_clk_refcount; + +static void ecodec_clk_enable(void) +{ + ecodec_clk_refcount++; + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + } +} +static void ecodec_clk_disable(int group_reset, int path) +{ + ecodec_clk_refcount--; + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + if (group_reset) { + if (path == ADSP_PATH_TX) + audio_tx_device_group = -1; + else + audio_rx_device_group = -1; + } + } +} +static void _audio_rx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_rx_device_id); + + pr_debug("[%s:%s] rx_clk_refcount = %d\n", __MM_FILE__, __func__, + icodec_rx_clk_refcount); + switch(device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount++; + if (icodec_rx_clk_refcount == 1) { + clk_set_rate(icodec_rx_clk, 12288000); + clk_enable(icodec_rx_clk); + } + break; + case Q6_ECODEC_RX: + ecodec_clk_enable(); + break; + case Q6_SDAC_RX: + sdac_clk_refcount++; + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + } + break; + default: + return; + } + audio_rx_device_group = device_group; +} + +static void _audio_tx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_tx_device_id); + uint32_t icodec_tx_clk_rate; + + pr_debug("[%s:%s] tx_clk_refcount = %d\n", __MM_FILE__, __func__, + icodec_tx_clk_refcount); + switch (device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount++; + if (icodec_tx_clk_refcount == 1) { + if (tx_clk_freq > 16000) + icodec_tx_clk_rate = 48000; + else if (tx_clk_freq > 8000) + icodec_tx_clk_rate = 16000; + else + icodec_tx_clk_rate = 8000; + + clk_set_rate(icodec_tx_clk, icodec_tx_clk_rate * 256); + clk_enable(icodec_tx_clk); + } + break; + case Q6_ECODEC_TX: + ecodec_clk_enable(); + break; + case Q6_SDAC_TX: + /* TODO: In QCT BSP, clk rate was set to 20480000 */ + sdac_clk_refcount++; + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + } + break; + default: + return; + } + audio_tx_device_group = device_group; +} + +static void _audio_rx_clk_disable(void) +{ + pr_debug("[%s:%s] rx_clk_refcount = %d\n", __MM_FILE__, __func__, + icodec_rx_clk_refcount); + switch (audio_rx_device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount--; + if (icodec_rx_clk_refcount == 0) { + clk_disable(icodec_rx_clk); + audio_rx_device_group = -1; + } + break; + case Q6_ECODEC_RX: + ecodec_clk_disable(1, ADSP_PATH_RX); + break; + case Q6_SDAC_RX: + sdac_clk_refcount--; + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_rx_device_group = -1; + } + break; + default: + pr_err("[%s:%s] invalid rx device group %d\n", __MM_FILE__, + __func__, audio_rx_device_group); + break; + } +} + +static void _audio_tx_clk_disable(void) +{ + pr_debug("[%s:%s] tx_clk_refcount = %d\n", __MM_FILE__, __func__, + icodec_tx_clk_refcount); + switch (audio_tx_device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount--; + if (icodec_tx_clk_refcount == 0) { + clk_disable(icodec_tx_clk); + audio_tx_device_group = -1; + } + break; + case Q6_ECODEC_TX: + ecodec_clk_disable(1, ADSP_PATH_TX); + break; + case Q6_SDAC_TX: + sdac_clk_refcount--; + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_tx_device_group = -1; + } + break; + default: + pr_err("[%s:%s] invalid tx device group %d\n", + __MM_FILE__, __func__, audio_tx_device_group); + break; + } +} + +static void _audio_rx_clk_reinit(uint32_t rx_device, uint32_t acdb_id) +{ + uint32_t device_group = q6_device_to_codec(rx_device); + + pr_debug("[%s:%s] rx_device = 0x%x\n", __MM_FILE__, __func__, + rx_device); + if (device_group != audio_rx_device_group) + _audio_rx_clk_disable(); + + audio_rx_device_id = rx_device; + audio_rx_path_id = q6_device_to_path(rx_device, acdb_id); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_enable(); + +} + +static void _audio_tx_clk_reinit(uint32_t tx_device, uint32_t acdb_id) +{ + uint32_t device_group = q6_device_to_codec(tx_device); + + pr_debug("[%s:%s] tx_device = 0x%x\n", __MM_FILE__, __func__, + tx_device); + if (device_group != audio_tx_device_group) + _audio_tx_clk_disable(); + + audio_tx_device_id = tx_device; + audio_tx_path_id = q6_device_to_path(tx_device, acdb_id); + + if (device_group != audio_tx_device_group) + _audio_tx_clk_enable(); +} + +static DEFINE_MUTEX(audio_path_lock); +static int audio_rx_path_refcount; +static int audio_tx_path_refcount; + +static int audio_rx_path_enable(int en, uint32_t acdb_id) +{ + pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en); + mutex_lock(&audio_path_lock); + if (en) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + _audio_rx_clk_enable(); + _audio_rx_path_enable(0, acdb_id); + } + } else { + audio_rx_path_refcount--; + if (audio_rx_path_refcount == 0) { + _audio_rx_path_disable(); + _audio_rx_clk_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +static int audio_tx_path_enable(int en, uint32_t acdb_id) +{ + pr_debug("[%s:%s] en = %d\n", __MM_FILE__, __func__, en); + mutex_lock(&audio_path_lock); + if (en) { + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } else { + audio_tx_path_refcount--; + if (audio_tx_path_refcount == 0) { + _audio_tx_path_disable(); + _audio_tx_clk_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) +{ + int res; + + pr_debug("[%s:%s] id_src = 0x%x\n, id_dst = 0x%x\n", __MM_FILE__, + __func__, id_src, id_dst); + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + if (q6_device_to_dir(id_dst) == Q6_RX) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst); + else + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst); + res = audio_update_acdb(id_dst, id_src); + if (res) + goto done; + + qdsp6_standby(ac_control); + qdsp6_start(ac_control); +done: + mutex_unlock(&audio_path_lock); + return res; +} + +int q6audio_set_tx_mute(int mute) +{ + uint32_t adev; + int rc; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + if (mute == tx_mute_status) { + mutex_unlock(&audio_path_lock); + return 0; + } + + adev = audio_tx_device_id; + rc = audio_tx_mute(ac_control, adev, mute); + + /* DSP caches the requested MUTE state when it cannot apply the state + immediately. In that case, it returns EUNSUPPORTED and applies the + cached state later */ + if ((rc == ADSP_AUDIO_STATUS_SUCCESS) || + (rc == ADSP_AUDIO_STATUS_EUNSUPPORTED)) { + pr_debug("[%s:%s] return status = %d\n", + __MM_FILE__, __func__, rc); + tx_mute_status = mute; + } + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_stream_volume(struct audio_client *ac, int vol) +{ + if (vol > 1200 || vol < -4000) { + pr_err("[%s:%s] unsupported volume level %d\n", __MM_FILE__, + __func__, vol); + return -EINVAL; + } + mutex_lock(&audio_path_lock); + audio_stream_mute(ac, 0); + audio_stream_volume(ac, vol); + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_rx_volume(int level) +{ + uint32_t adev; + int vol; + + pr_debug("[%s:%s] level = %d\n", __MM_FILE__, __func__, level); + if (q6audio_init()) + return 0; + + if (level < 0 || level > 100) + return -EINVAL; + + mutex_lock(&audio_path_lock); + adev = ADSP_AUDIO_DEVICE_ID_VOICE; + + if (level) { + vol = q6_device_volume(audio_rx_device_id, level); + audio_rx_mute(ac_control, adev, 0); + audio_rx_volume(ac_control, adev, vol); + } else + audio_rx_mute(ac_control, adev, 1); + + rx_vol_level = level; + mutex_unlock(&audio_path_lock); + return 0; +} + +static void do_rx_routing(uint32_t device_id, uint32_t acdb_id) +{ + pr_debug("[%s:%s] device_id = 0x%x, acdb_id = 0x%x\n", __MM_FILE__, + __func__, device_id, acdb_id); + if (device_id == audio_rx_device_id && + audio_rx_path_id == q6_device_to_path(device_id, acdb_id)) { + if (acdb_id != rx_acdb) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + audio_update_acdb(device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } + return; + } + + if (audio_rx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + _audio_rx_path_disable(); + _audio_rx_clk_reinit(device_id, acdb_id); + _audio_rx_path_enable(1, acdb_id); + } else { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, + device_id); + audio_update_acdb(device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + audio_rx_device_id = device_id; + audio_rx_path_id = q6_device_to_path(device_id, acdb_id); + } +} + +static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) +{ + pr_debug("[%s:%s] device_id = 0x%x, acdb_id = 0x%x\n", __MM_FILE__, + __func__, device_id, acdb_id); + if (device_id == audio_tx_device_id && + audio_tx_path_id == q6_device_to_path(device_id, acdb_id)) { + if (acdb_id != tx_acdb) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + device_id); + audio_update_acdb(device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } + return; + } + + if (audio_tx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + _audio_tx_path_disable(); + _audio_tx_clk_reinit(device_id, acdb_id); + _audio_tx_path_enable(1, acdb_id); + } else { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + device_id); + audio_update_acdb(device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + audio_tx_device_id = device_id; + audio_tx_path_id = q6_device_to_path(device_id, acdb_id); + tx_acdb = acdb_id; + } +} + +int q6audio_do_routing(uint32_t device_id, uint32_t acdb_id) +{ + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + switch(q6_device_to_dir(device_id)) { + case Q6_RX: + do_rx_routing(device_id, acdb_id); + break; + case Q6_TX: + do_tx_routing(device_id, acdb_id); + break; + } + + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_route(const char *name) +{ + uint32_t route; + if (!strcmp(name, "speaker")) { + route = ADIE_PATH_SPEAKER_STEREO_RX; + } else if (!strcmp(name, "headphones")) { + route = ADIE_PATH_HEADSET_STEREO_RX; + } else if (!strcmp(name, "handset")) { + route = ADIE_PATH_HANDSET_RX; + } else { + return -EINVAL; + } + + mutex_lock(&audio_path_lock); + if (route == audio_rx_path_id) + goto done; + + audio_rx_path_id = route; + + if (audio_rx_path_refcount > 0) { + _audio_rx_path_disable(); + _audio_rx_path_enable(1, 0); + } + if (audio_tx_path_refcount > 0) { + _audio_tx_path_disable(); + _audio_tx_path_enable(1, 0); + } +done: + mutex_unlock(&audio_path_lock); + return 0; +} + +static int audio_stream_equalizer(struct audio_client *ac, void *eq_config) +{ + int i; + struct adsp_set_equalizer_command rpc; + struct adsp_audio_eq_stream_config *eq_cfg; + eq_cfg = (struct adsp_audio_eq_stream_config *) eq_config; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG; + rpc.enable = eq_cfg->enable; + rpc.num_bands = eq_cfg->num_bands; + for (i = 0; i < eq_cfg->num_bands; i++) { + rpc.eq_bands[i].band_idx = eq_cfg->eq_bands[i].band_idx; + rpc.eq_bands[i].filter_type = eq_cfg->eq_bands[i].filter_type; + rpc.eq_bands[i].center_freq_hz = + eq_cfg->eq_bands[i].center_freq_hz; + rpc.eq_bands[i].filter_gain = eq_cfg->eq_bands[i].filter_gain; + rpc.eq_bands[i].q_factor = eq_cfg->eq_bands[i].q_factor; + } + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +int q6audio_set_stream_eq_pcm(struct audio_client *ac, void *eq_config) +{ + int rc = 0; + mutex_lock(&audio_path_lock); + rc = audio_stream_equalizer(ac, eq_config); + mutex_unlock(&audio_path_lock); + return rc; +} + +struct audio_client *q6audio_open_auxpcm(uint32_t rate, + uint32_t channels, uint32_t flags, uint32_t acdb_id) +{ + int rc, retry = 5; + struct audio_client *ac; + + pr_debug("[%s:%s] rate = %d, channels = %d\n", __MM_FILE__, __func__, + rate, channels); + if (q6audio_init()) + return NULL; + ac = audio_client_alloc(0); + if (!ac) + return NULL; + + ac->flags = flags; + + mutex_lock(&audio_path_lock); + + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + tx_clk_freq = rate; + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } else { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + _audio_rx_clk_enable(); + _audio_rx_path_enable(0, acdb_id); + } + } + + ecodec_clk_enable(); + + for (retry = 5;; retry--) { + if (ac->flags & AUDIO_FLAG_WRITE) + rc = audio_auxpcm_out_open(ac, rate, channels); + else + rc = audio_auxpcm_in_open(ac, rate, channels); + if (rc == 0) + break; + if (retry == 0) + q6audio_dsp_not_responding(); + + pr_err("[%s:%s] open pcm error %d, retrying\n", + __MM_FILE__, __func__, rc); + msleep(1); + } + + mutex_unlock(&audio_path_lock); + + for (retry = 5;; retry--) { + rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + if (rc == 0) + break; + if (retry == 0) + q6audio_dsp_not_responding(); + + pr_err("[%s:%s] stream start error %d, retrying\n", + __MM_FILE__, __func__, rc); + } + audio_prevent_sleep(); + return ac; + +} + +struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t flags, uint32_t acdb_id) +{ + int rc, retry = 5; + struct audio_client *ac; + + pr_debug("[%s:%s] bufsz = %d, rate = %d, channels = %d\n", __MM_FILE__, + __func__, bufsz, rate, channels); + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + + mutex_lock(&audio_path_lock); + + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + _audio_rx_clk_enable(); + q6_rx_path_enable(0, acdb_id); + adie_rx_path_enable(acdb_id); + } + } else { + /* TODO: consider concurrency with voice call */ + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + tx_clk_freq = rate; + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } + + for (retry = 5;;retry--) { + if (ac->flags & AUDIO_FLAG_WRITE) + rc = audio_out_open(ac, bufsz, rate, channels); + else + rc = audio_in_open(ac, bufsz, flags, rate, channels); + if (rc == 0) + break; + if (retry == 0) + q6audio_dsp_not_responding(); + + pr_err("[%s:%s] open pcm error %d, retrying\n", + __MM_FILE__, __func__, rc); + msleep(1); + } + + if (ac->flags & AUDIO_FLAG_WRITE) { + if (audio_rx_path_refcount == 1) + audio_rx_analog_enable(1); + } + mutex_unlock(&audio_path_lock); + + for (retry = 5;;retry--) { + rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + if (rc == 0) + break; + if (retry == 0) + q6audio_dsp_not_responding(); + + pr_err("[%s:%s] stream start error %d, retrying\n", + __MM_FILE__, __func__, rc); + } + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + + audio_prevent_sleep(); + return ac; +} + +int q6audio_close(struct audio_client *ac) +{ + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + audio_client_free(ac); + audio_allow_sleep(); + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return 0; +} + +int q6audio_auxpcm_close(struct audio_client *ac) +{ + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_tx_path_enable(0, 0); + ecodec_clk_disable(0, ADSP_PATH_RX); + } else { + audio_rx_path_enable(0, 0); + ecodec_clk_disable(0, ADSP_PATH_TX); + } + + audio_client_free(ac); + audio_allow_sleep(); + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + return 0; +} +struct audio_client *q6voice_open(uint32_t flags) +{ + struct audio_client *ac; + + pr_debug("[%s:%s] flags = %d\n", __MM_FILE__, __func__, flags); + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(0); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, rx_acdb); + else { + if (!audio_tx_path_refcount) + tx_clk_freq = 8000; + audio_tx_path_enable(1, tx_acdb); + } + + return ac; +} + +int q6voice_close(struct audio_client *ac) +{ + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + + tx_mute_status = 0; + audio_client_free(ac); + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return 0; +} + +struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t acdb_id) +{ + struct audio_client *ac; + + pr_debug("[%s:%s] bufsz = %d, rate = %d\n, channels = %d", + __MM_FILE__, __func__, bufsz, rate, channels); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_WRITE; + audio_rx_path_enable(1, acdb_id); + + audio_mp3_open(ac, bufsz, rate, channels); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + mutex_lock(&audio_path_lock); + audio_rx_mute(ac_control, audio_rx_device_id, 0); + audio_rx_volume(ac_control, audio_rx_device_id, + q6_device_volume(audio_rx_device_id, rx_vol_level)); + mutex_unlock(&audio_path_lock); + return ac; +} + +struct audio_client *q6audio_open_dtmf(uint32_t rate, + uint32_t channels, uint32_t acdb_id) +{ + struct audio_client *ac; + + pr_debug("[%s:%s] rate = %d\n, channels = %d", __MM_FILE__, __func__, + rate, channels); + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(0); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_WRITE; + audio_rx_path_enable(1, acdb_id); + + audio_dtmf_open(ac, rate, channels); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + mutex_lock(&audio_path_lock); + audio_rx_mute(ac_control, audio_rx_device_id, 0); + audio_rx_volume(ac_control, audio_rx_device_id, + q6_device_volume(audio_rx_device_id, rx_vol_level)); + mutex_unlock(&audio_path_lock); + + return ac; +} + +int q6audio_play_dtmf(struct audio_client *ac, uint16_t dtmf_hi, + uint16_t dtmf_low, uint16_t duration, uint16_t rx_gain) +{ + struct adsp_audio_dtmf_start_command dtmf_cmd; + + pr_debug("[%s:%s] high = %d, low = %d\n", __MM_FILE__, __func__, + dtmf_hi, dtmf_low); + + dtmf_cmd.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START; + dtmf_cmd.hdr.response_type = ADSP_AUDIO_RESPONSE_COMMAND; + dtmf_cmd.tone1_hz = dtmf_hi; + dtmf_cmd.tone2_hz = dtmf_low; + dtmf_cmd.duration_usec = duration * 1000; + dtmf_cmd.gain_mb = rx_gain; + + return audio_ioctl(ac, &dtmf_cmd, + sizeof(struct adsp_audio_dtmf_start_command)); + +} + +int q6audio_mp3_close(struct audio_client *ac) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + audio_close(ac); + audio_rx_path_enable(0, 0); + audio_client_free(ac); + return 0; +} + + +struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t samplerate, + uint32_t channels, uint32_t bitrate, + uint32_t stream_format, uint32_t flags, + uint32_t acdb_id) +{ + struct audio_client *ac; + + pr_debug("[%s:%s] bufsz = %d, samplerate = %d, channels = %d\n", + __MM_FILE__, __func__, bufsz, samplerate, channels); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, acdb_id); + else{ + if (!audio_tx_path_refcount) + tx_clk_freq = 48000; + audio_tx_path_enable(1, acdb_id); + } + + audio_aac_open(ac, bufsz, samplerate, channels, bitrate, flags, + stream_format); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + audio_prevent_sleep(); + return ac; +} + + +struct audio_client *q6audio_open_qcp(uint32_t bufsz, uint32_t min_rate, + uint32_t max_rate, uint32_t flags, + uint32_t format, uint32_t acdb_id) +{ + struct audio_client *ac; + + pr_debug("[%s:%s] bufsz = %d\n", __MM_FILE__, __func__, bufsz); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, acdb_id); + else{ + if (!audio_tx_path_refcount) + tx_clk_freq = 8000; + audio_tx_path_enable(1, acdb_id); + } + + audio_qcp_open(ac, bufsz, min_rate, max_rate, flags, format); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + audio_prevent_sleep(); + return ac; +} + +struct audio_client *q6audio_open_amrnb(uint32_t bufsz, uint32_t enc_mode, + uint32_t dtx_mode_enable, + uint32_t flags, uint32_t acdb_id) +{ + struct audio_client *ac; + + pr_debug("[%s:%s] bufsz = %d, dtx_mode = %d\n", __MM_FILE__, + __func__, bufsz, dtx_mode_enable); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, acdb_id); + else{ + if (!audio_tx_path_refcount) + tx_clk_freq = 8000; + audio_tx_path_enable(1, acdb_id); + } + + audio_amrnb_open(ac, bufsz, enc_mode, flags, dtx_mode_enable); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + audio_prevent_sleep(); + return ac; +} + +int q6audio_async(struct audio_client *ac) +{ + struct adsp_command_hdr rpc; + pr_debug("[%s:%s] ac = %p\n", __MM_FILE__, __func__, ac); + memset(&rpc, 0, sizeof(rpc)); + rpc.opcode = ADSP_AUDIO_IOCTL_CMD_STREAM_EOS; + rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} diff --git a/arch/arm/mach-msm/qdsp6/q6audio_devices.h b/arch/arm/mach-msm/qdsp6/q6audio_devices.h new file mode 100644 index 00000000000..d316ab0e0de --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/q6audio_devices.h @@ -0,0 +1,334 @@ +/* arch/arm/mach-msm/qdsp6/q6audio_devices.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +struct q6_device_info { + uint32_t id; + uint32_t cad_id; + uint32_t path; + uint32_t rate; + uint8_t dir; + uint8_t codec; + uint8_t hw; +}; + +#define Q6_ICODEC_RX 0 +#define Q6_ICODEC_TX 1 +#define Q6_ECODEC_RX 2 +#define Q6_ECODEC_TX 3 +#define Q6_SDAC_RX 6 +#define Q6_SDAC_TX 7 +#define Q6_CODEC_NONE 255 + +#define Q6_TX 1 +#define Q6_RX 2 +#define Q6_TX_RX 3 + +#define Q6_HW_HANDSET 0 +#define Q6_HW_HEADSET 1 +#define Q6_HW_SPEAKER 2 +#define Q6_HW_TTY 3 +#define Q6_HW_BT_SCO 4 +#define Q6_HW_BT_A2DP 5 + +#define Q6_HW_COUNT 6 + +#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01 +#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02 +#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08 +#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09 +#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A +#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B +#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C +#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D + +#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E +#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F + + +#define CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_BROADSIDE 0x2B +#define CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_ENDFIRE 0x2D +#define CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_BROADSIDE 0x2C +#define CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_ENDFIRE 0x2E + +/* Logical Device to indicate A2DP routing */ +#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10 +#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12 +#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13 +#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 + +#define CAD_HW_DEVICE_ID_VOICE 0x15 + +#define CAD_HW_DEVICE_ID_I2S_RX 0x20 +#define CAD_HW_DEVICE_ID_I2S_TX 0x21 + +/* AUXPGA */ +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23 +#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24 +#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25 + +#define CAD_HW_DEVICE_ID_NULL_RX 0x2A + +#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F + +#define CAD_HW_DEVICE_ID_INVALID 0xFF + +#define CAD_RX_DEVICE 0x00 +#define CAD_TX_DEVICE 0x01 + +static struct q6_device_info q6_audio_devices[] = { + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR, + .path = ADIE_PATH_HANDSET_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO, + .path = ADIE_PATH_HEADSET_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO, + .path = ADIE_PATH_HEADSET_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO, + .path = ADIE_PATH_SPEAKER_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO, + .path = ADIE_PATH_SPEAKER_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX, + .path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX, + .path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX, + .path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX, + .path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR, + .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR, + .path = ADIE_PATH_TTY_HEADSET_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_TTY, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC, + .path = ADIE_PATH_HANDSET_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC, + .path = ADIE_PATH_HEADSET_MONO_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC, + .path = ADIE_PATH_SPEAKER_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_ENDFIRE, + .path = ADIE_CODEC_HANDSET_SPKR_EF_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_DUAL_MIC, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_DUAL_MIC_BROADSIDE, + .path = ADIE_CODEC_HANDSET_SPKR_BS_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_ENDFIRE, + .path = ADIE_CODEC_HANDSET_SPKR_EF_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_DUAL_MIC, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_DUAL_MIC_BROADSIDE, + .path = ADIE_CODEC_HANDSET_SPKR_BS_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC, + .path = ADIE_PATH_TTY_HEADSET_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR, + .cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_A2DP, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC, + .path = 0, /* XXX */ + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ECODEC_TX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR, + .cad_id = CAD_HW_DEVICE_ID_I2S_RX, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_SDAC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_I2S_MIC, + .cad_id = CAD_HW_DEVICE_ID_I2S_TX, + .path = 0, /* XXX */ + .rate = 16000, + .dir = Q6_TX, + .codec = Q6_SDAC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_AUXPCM_RX, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR, + .path = 0, /* XXX */ + .rate = 8000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_AUXPCM_TX, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC, + .path = 0, /* XXX */ + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ECODEC_TX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = 0, + .cad_id = 0, + .path = 0, + .rate = 8000, + .dir = 0, + .codec = Q6_CODEC_NONE, + .hw = 0, + }, +}; + diff --git a/arch/arm/mach-msm/qdsp6/qcelp_in.c b/arch/arm/mach-msm/qdsp6/qcelp_in.c new file mode 100644 index 00000000000..ca0ab1a5654 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/qcelp_in.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "dal_audio_format.h" +#include + +#define QCELP_FC_BUFF_CNT 10 +#define QCELP_READ_TIMEOUT 2000 +struct qcelp_fc_buff { + struct mutex lock; + int empty; + void *data; + int size; + int actual_size; +}; + +struct qcelp_fc { + struct task_struct *task; + wait_queue_head_t fc_wq; + struct qcelp_fc_buff fc_buff[QCELP_FC_BUFF_CNT]; + int buff_index; +}; + +struct qcelp { + struct mutex lock; + struct msm_audio_qcelp_enc_config cfg; + struct msm_audio_stream_config str_cfg; + struct audio_client *audio_client; + struct msm_voicerec_mode voicerec_mode; + struct qcelp_fc *qcelp_fc; +}; + + +static int q6_qcelp_flowcontrol(void *data) +{ + struct audio_client *ac; + struct audio_buffer *ab; + struct qcelp *qcelp = data; + int buff_index = 0; + int xfer = 0; + struct qcelp_fc *fc; + + + ac = qcelp->audio_client; + fc = qcelp->qcelp_fc; + if (!ac) { + pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__); + return 0; + } + + while (!kthread_should_stop()) { + ab = ac->buf + ac->cpu_buf; + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d", __MM_FILE__, + __func__, ab->data, ac->cpu_buf); + xfer = ab->actual_size; + + + mutex_lock(&(fc->fc_buff[buff_index].lock)); + if (!fc->fc_buff[buff_index].empty) { + pr_err("[%s:%s] flow control buffer[%d] not read!\n", + __MM_FILE__, __func__, buff_index); + } + + if (fc->fc_buff[buff_index].size < xfer) { + pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__, + __func__, buff_index); + memcpy(fc->fc_buff[buff_index].data, ab->data, + fc->fc_buff[buff_index].size); + fc->fc_buff[buff_index].empty = 0; + fc->fc_buff[buff_index].actual_size = + fc->fc_buff[buff_index].size; + } else { + memcpy(fc->fc_buff[buff_index].data, ab->data, xfer); + fc->fc_buff[buff_index].empty = 0; + fc->fc_buff[buff_index].actual_size = xfer; + } + mutex_unlock(&(fc->fc_buff[buff_index].lock)); + /*wake up client, if any*/ + wake_up(&fc->fc_wq); + + buff_index++; + if (buff_index >= QCELP_FC_BUFF_CNT) + buff_index = 0; + + ab->used = 1; + + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } + + return 0; +} +static long q6_qcelp_in_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct qcelp *qcelp = file->private_data; + int rc = 0; + int i = 0; + struct qcelp_fc *fc; + int size = 0; + + mutex_lock(&qcelp->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: + pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__); + break; + case AUDIO_GET_STATS: + { + struct msm_audio_stats stats; + pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__); + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, + sizeof(stats))) + return -EFAULT; + return 0; + } + case AUDIO_START: + { + uint32_t acdb_id; + pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); + if (arg == 0) { + acdb_id = 0; + } else { + if (copy_from_user(&acdb_id, + (void *) arg, sizeof(acdb_id))) { + rc = -EFAULT; + break; + } + } + if (qcelp->audio_client) { + pr_err("[%s:%s] active session already existing\n", + __MM_FILE__, __func__); + rc = -EBUSY; + break; + } else { + qcelp->audio_client = q6audio_open_qcp( + qcelp->str_cfg.buffer_size, + qcelp->cfg.min_bit_rate, + qcelp->cfg.max_bit_rate, + qcelp->voicerec_mode.rec_mode, + ADSP_AUDIO_FORMAT_V13K_FS, + acdb_id); + + if (!qcelp->audio_client) { + pr_err("[%s:%s] qcelp open session failed\n", + __MM_FILE__, __func__); + kfree(qcelp); + rc = -ENOMEM; + break; + } + } + + /*allocate flow control buffers*/ + fc = qcelp->qcelp_fc; + size = qcelp->str_cfg.buffer_size; + for (i = 0; i < QCELP_FC_BUFF_CNT; ++i) { + mutex_init(&(fc->fc_buff[i].lock)); + fc->fc_buff[i].empty = 1; + fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL); + if (fc->fc_buff[i].data == NULL) { + pr_err("[%s:%s] No memory for FC buffers\n", + __MM_FILE__, __func__); + rc = -ENOMEM; + goto fc_fail; + } + fc->fc_buff[i].size = size; + fc->fc_buff[i].actual_size = 0; + } + + /*create flow control thread*/ + fc->task = kthread_run(q6_qcelp_flowcontrol, + qcelp, "qcelp_flowcontrol"); + if (IS_ERR(fc->task)) { + rc = PTR_ERR(fc->task); + pr_err("[%s:%s] error creating flow control thread\n", + __MM_FILE__, __func__); + goto fc_fail; + } + break; +fc_fail: + /*free flow control buffers*/ + --i; + for (; i >= 0; i--) { + kfree(fc->fc_buff[i].data); + fc->fc_buff[i].data = NULL; + } + break; + } + case AUDIO_STOP: + pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_INCALL: { + pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__); + if (copy_from_user(&qcelp->voicerec_mode, + (void *)arg, sizeof(struct msm_voicerec_mode))) + rc = -EFAULT; + + if (qcelp->voicerec_mode.rec_mode != AUDIO_FLAG_READ + && qcelp->voicerec_mode.rec_mode != + AUDIO_FLAG_INCALL_MIXED) { + qcelp->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + break; + } + case AUDIO_GET_STREAM_CONFIG: + if (copy_to_user((void *)arg, &qcelp->str_cfg, + sizeof(struct msm_audio_stream_config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", + __MM_FILE__, __func__, qcelp->str_cfg.buffer_size, + qcelp->str_cfg.buffer_count); + break; + case AUDIO_SET_STREAM_CONFIG: + if (copy_from_user(&qcelp->str_cfg, (void *)arg, + sizeof(struct msm_audio_stream_config))) { + rc = -EFAULT; + break; + } + pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", + __MM_FILE__, __func__, qcelp->str_cfg.buffer_size, + qcelp->str_cfg.buffer_count); + + if (qcelp->str_cfg.buffer_size < 35) { + pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, + __func__); + rc = -EINVAL; + break; + } + + if (qcelp->str_cfg.buffer_count != 2) + pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, + __func__); + break; + case AUDIO_SET_QCELP_ENC_CONFIG: + if (copy_from_user(&qcelp->cfg, (void *) arg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + pr_debug("[%s:%s] SET_QCELP_ENC_CONFIG\n", __MM_FILE__, + __func__); + + if (qcelp->cfg.min_bit_rate > 4 || + qcelp->cfg.min_bit_rate < 1) { + + pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + if (qcelp->cfg.max_bit_rate > 4 || + qcelp->cfg.max_bit_rate < 1) { + + pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__, + __func__); + rc = -EINVAL; + } + + break; + case AUDIO_GET_QCELP_ENC_CONFIG: + if (copy_to_user((void *) arg, &qcelp->cfg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + pr_debug("[%s:%s] GET_QCELP_ENC_CONFIG\n", __MM_FILE__, + __func__); + break; + + default: + rc = -EINVAL; + } + mutex_unlock(&qcelp->lock); + pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); + return rc; +} + +static int q6_qcelp_in_open(struct inode *inode, struct file *file) +{ + struct qcelp *qcelp; + struct qcelp_fc *fc; + int i; + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + qcelp = kmalloc(sizeof(struct qcelp), GFP_KERNEL); + if (qcelp == NULL) { + pr_err("[%s:%s] Could not allocate memory for qcelp driver\n", + __MM_FILE__, __func__); + return -ENOMEM; + } + + mutex_init(&qcelp->lock); + file->private_data = qcelp; + qcelp->audio_client = NULL; + qcelp->str_cfg.buffer_size = 35; + qcelp->str_cfg.buffer_count = 2; + qcelp->cfg.cdma_rate = CDMA_RATE_FULL; + qcelp->cfg.min_bit_rate = 1; + qcelp->cfg.max_bit_rate = 4; + qcelp->voicerec_mode.rec_mode = AUDIO_FLAG_READ; + + qcelp->qcelp_fc = kmalloc(sizeof(struct qcelp_fc), GFP_KERNEL); + if (qcelp->qcelp_fc == NULL) { + pr_err("[%s:%s] Could not allocate memory for qcelp_fc\n", + __MM_FILE__, __func__); + kfree(qcelp); + return -ENOMEM; + } + fc = qcelp->qcelp_fc; + fc->task = NULL; + fc->buff_index = 0; + for (i = 0; i < QCELP_FC_BUFF_CNT; ++i) { + fc->fc_buff[i].data = NULL; + fc->fc_buff[i].size = 0; + fc->fc_buff[i].actual_size = 0; + } + /*initialize wait queue head*/ + init_waitqueue_head(&fc->fc_wq); + return 0; +} + +static ssize_t q6_qcelp_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + const char __user *start = buf; + struct qcelp *qcelp = file->private_data; + struct qcelp_fc *fc; + int xfer = 0; + int res = 0; + + pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); + mutex_lock(&qcelp->lock); + ac = qcelp->audio_client; + if (!ac) { + res = -ENODEV; + goto fail; + } + fc = qcelp->qcelp_fc; + while (count > xfer) { + /*wait for buffer to full*/ + if (fc->fc_buff[fc->buff_index].empty != 0) { + res = wait_event_interruptible_timeout(fc->fc_wq, + (fc->fc_buff[fc->buff_index].empty == 0), + msecs_to_jiffies(QCELP_READ_TIMEOUT)); + + pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__, + __func__, fc->buff_index); + if (res == 0) { + pr_err("[%s:%s] Timeout!\n", __MM_FILE__, + __func__); + res = -ETIMEDOUT; + goto fail; + } else if (res < 0) { + pr_err("[%s:%s] Returning on Interrupt\n", + __MM_FILE__, __func__); + goto fail; + } + } + /*lock the buffer*/ + mutex_lock(&(fc->fc_buff[fc->buff_index].lock)); + xfer = fc->fc_buff[fc->buff_index].actual_size; + + if (xfer > count) { + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + pr_err("[%s:%s] read failed! byte count too small\n", + __MM_FILE__, __func__); + res = -EINVAL; + goto fail; + } + + if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) { + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + pr_err("[%s:%s] copy_to_user failed at index %d\n", + __MM_FILE__, __func__, fc->buff_index); + res = -EFAULT; + goto fail; + } + buf += xfer; + count -= xfer; + + fc->fc_buff[fc->buff_index].empty = 1; + fc->fc_buff[fc->buff_index].actual_size = 0; + + mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); + ++(fc->buff_index); + if (fc->buff_index >= QCELP_FC_BUFF_CNT) + fc->buff_index = 0; + } + res = buf - start; + +fail: + mutex_unlock(&qcelp->lock); + + return res; +} + +static int q6_qcelp_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct qcelp *qcelp = file->private_data; + int i = 0; + struct qcelp_fc *fc; + + mutex_lock(&qcelp->lock); + fc = qcelp->qcelp_fc; + kthread_stop(fc->task); + fc->task = NULL; + + /*free flow control buffers*/ + for (i = 0; i < QCELP_FC_BUFF_CNT; ++i) { + kfree(fc->fc_buff[i].data); + fc->fc_buff[i].data = NULL; + } + kfree(fc); + + if (qcelp->audio_client) + rc = q6audio_close(qcelp->audio_client); + mutex_unlock(&qcelp->lock); + kfree(qcelp); + pr_info("[%s:%s] release\n", __MM_FILE__, __func__); + return rc; +} + +static const struct file_operations q6_qcelp_in_fops = { + .owner = THIS_MODULE, + .open = q6_qcelp_in_open, + .read = q6_qcelp_in_read, + .release = q6_qcelp_in_release, + .unlocked_ioctl = q6_qcelp_in_ioctl, +}; + +struct miscdevice q6_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &q6_qcelp_in_fops, +}; + +static int __init q6_qcelp_in_init(void) +{ + return misc_register(&q6_qcelp_in_misc); +} + +device_initcall(q6_qcelp_in_init); diff --git a/arch/arm/mach-msm/qdsp6/routing.c b/arch/arm/mach-msm/qdsp6/routing.c new file mode 100644 index 00000000000..f6533a40c2b --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/routing.c @@ -0,0 +1,78 @@ +/* arch/arm/mach-msm/qdsp6/routing.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +extern int q6audio_set_route(const char *name); + +static int q6_open(struct inode *inode, struct file *file) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return 0; +} + +static ssize_t q6_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + + pr_debug("[%s:%s] count = %d", __MM_FILE__, __func__, count); + if (count >= sizeof(cmd)) { + pr_err("[%s:%s] invalid count %d\n", __MM_FILE__, + __func__, count); + return -EINVAL; + } + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + q6audio_set_route(cmd); + + return count; +} + +static int q6_release(struct inode *inode, struct file *file) +{ + pr_debug("[%s:%s]\n", __MM_FILE__, __func__); + return 0; +} + +static struct file_operations q6_fops = { + .owner = THIS_MODULE, + .open = q6_open, + .write = q6_write, + .release = q6_release, +}; + +static struct miscdevice q6_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_route", + .fops = &q6_fops, +}; + + +static int __init q6_init(void) { + return misc_register(&q6_misc); +} + +device_initcall(q6_init); diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile new file mode 100644 index 00000000000..cee8f04554a --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/Makefile @@ -0,0 +1,22 @@ +obj-y += rtac.o +ifdef CONFIG_ARCH_MSM8X60 +obj-y += audio_dev_ctl.o +obj-y += board-msm8x60-audio.o +obj-$(CONFIG_TIMPANI_CODEC) += snddev_icodec.o +obj-y += snddev_ecodec.o snddev_mi2s.o snddev_virtual.o +obj-y += pcm_out.o pcm_in.o fm.o +obj-y += audio_lpa.o +obj-y += q6voice.o +obj-y += snddev_hdmi.o +obj-y += audio_mvs.o +obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += lpa_if_hdmi.o +endif +obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_tal.o q6core.o dsp_debug.o +obj-y += audio_acdb.o +ifndef CONFIG_ARCH_MSM9615 +obj-y += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o +obj-y += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o +obj-$(CONFIG_MSM_QDSP6_CODECS) += q6audio_v1.o q6audio_v1_aio.o +obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/ +obj-y += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o +endif diff --git a/arch/arm/mach-msm/qdsp6v2/aac_in.c b/arch/arm/mach-msm/qdsp6v2/aac_in.c new file mode 100644 index 00000000000..6e79a754b16 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/aac_in.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 5 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5)) + +#define AAC_FORMAT_ADTS 65535 + +/* ------------------- device --------------------- */ +static long aac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t aac_mode = AAC_ENC_MODE_AAC_LC; + + enc_cfg = audio->enc_cfg; + aac_config = audio->codec_cfg; + /* ENCODE CFG (after new set of API's are published )bharath*/ + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__, + aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag); + if (aac_config->sbr_ps_on_flag) + aac_mode = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_mode = AAC_ENC_MODE_AAC_P; + else + aac_mode = AAC_ENC_MODE_AAC_LC; + + rc = q6asm_enc_cfg_blk_aac(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate, + enc_cfg->channels, + enc_cfg->bit_rate, + aac_mode, + enc_cfg->stream_format); + if (rc < 0) { + pr_err("%s:session id %d: cmd media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure" + "failed rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (enc_cfg->channels == CH_MODE_MONO) + cfg.channels = 1; + else + cfg.channels = 2; + cfg.sample_rate = enc_cfg->sample_rate; + cfg.bit_rate = enc_cfg->bit_rate; + /* ADTS(-1) to ADTS(0x00), RAW(0x00) to RAW(0x03) */ + cfg.stream_format = ((enc_cfg->stream_format == \ + 0x00) ? AUDIO_AAC_FORMAT_ADTS : AUDIO_AAC_FORMAT_RAW); + pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d" + "bitrate=%d\n", __func__, audio->ac->session, + cfg.stream_format, cfg.sample_rate, cfg.bit_rate); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__, + audio->ac->session, cfg.stream_format); + + if ((cfg.stream_format != AUDIO_AAC_FORMAT_RAW) && + (cfg.stream_format != AAC_FORMAT_ADTS)) { + pr_err("%s:session id %d: unsupported AAC format\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + + if (cfg.channels == 1) { + cfg.channels = CH_MODE_MONO; + } else if (cfg.channels == 2) { + cfg.channels = CH_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + if ((cfg.sample_rate < 8000) && (cfg.sample_rate > 48000)) { + pr_err("%s: ERROR in setting samplerate = %d\n", + __func__, cfg.sample_rate); + rc = -EINVAL; + break; + } + /* For aac-lc, min_bit_rate = min(24Kbps, 0.5*SR*num_chan); + max_bi_rate = min(192Kbps, 6*SR*num_chan); + min_sample_rate = 8000Hz, max_rate=48000 */ + if ((cfg.bit_rate < 4000) || (cfg.bit_rate > 192000)) { + pr_err("%s: ERROR in setting bitrate = %d\n", + __func__, cfg.bit_rate); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg.sample_rate; + enc_cfg->channels = cfg.channels; + enc_cfg->bit_rate = cfg.bit_rate; + enc_cfg->stream_format = + ((cfg.stream_format == AUDIO_AAC_FORMAT_RAW) ? \ + 0x03 : 0x00); + pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x" + "bitrate=0x%x, format(adts/raw) = %d\n", + __func__, audio->ac->session, enc_cfg->sample_rate, + enc_cfg->channels, enc_cfg->bit_rate, + enc_cfg->stream_format); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, &audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_cfg; + struct msm_audio_aac_config *audio_aac_cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + audio_aac_cfg = audio->codec_cfg; + + if (copy_from_user(&aac_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d" + " sbr_ps_flag = %d\n", __func__, + audio->ac->session, aac_cfg.sbr_on_flag, + aac_cfg.sbr_ps_on_flag); + audio_aac_cfg->sbr_on_flag = aac_cfg.sbr_on_flag; + audio_aac_cfg->sbr_ps_on_flag = aac_cfg.sbr_ps_on_flag; + if ((audio_aac_cfg->sbr_on_flag == 1) || + (audio_aac_cfg->sbr_ps_on_flag == 1)) { + if (enc_cfg->sample_rate < 24000) { + pr_err("%s: ERROR in setting samplerate = %d" + "\n", __func__, enc_cfg->sample_rate); + rc = -EINVAL; + break; + } + } + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int aac_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for aac" + "driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config\n", __func__, audio->ac->session); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 1536; + audio->max_frames_per_buf = 5; + enc_cfg->sample_rate = 8000; + enc_cfg->channels = 1; + enc_cfg->bit_rate = 16000; + enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */ + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + aac_config->format = AUDIO_AAC_FORMAT_ADTS; + aac_config->audio_object = AUDIO_AAC_OBJECT_LC; + aac_config->sbr_on_flag = 0; + aac_config->sbr_ps_on_flag = 0; + aac_config->channel_configuration = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for" + "audio client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open aac encoder in tunnel mode */ + audio->buf_cfg.frames_per_buf = 0x01; + + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC, + FORMAT_LINEAR_PCM); + + if (rc < 0) { + pr_err("%s:session id %d: NT Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x01; + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:session id %d: Tunnel Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x00; + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = aac_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = aac_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_in_fops, +}; + +static int __init aac_in_init(void) +{ + return misc_register(&audio_aac_in_misc); +} +device_initcall(aac_in_init); diff --git a/arch/arm/mach-msm/qdsp6v2/amrnb_in.c b/arch/arm/mach-msm/qdsp6v2/amrnb_in.c new file mode 100644 index 00000000000..63a07748ac9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/amrnb_in.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long amrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrnb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrnb media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed" + "rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) { + rc = -EFAULT; + break; + } + if (cfg.band_mode > 8 || + cfg.band_mode < 1) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* AMR NB encoder accepts values between 0-7 + while openmax provides value between 1-8 + as per spec */ + enc_cfg->band_mode = (cfg.band_mode - 1); + enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0); + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int amrnb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s Could not allocate memory for amrnb" + "driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 7; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio" + "client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrnb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = amrnb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrnb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_in_fops, +}; + +static int __init amrnb_in_init(void) +{ + return misc_register(&audio_amrnb_in_misc); +} + +device_initcall(amrnb_in_init); diff --git a/arch/arm/mach-msm/qdsp6v2/amrwb_in.c b/arch/arm/mach-msm/qdsp6v2/amrwb_in.c new file mode 100644 index 00000000000..d0462e09307 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/amrwb_in.c @@ -0,0 +1,282 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long amrwb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrwb_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrwb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrwb media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AMRWB_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrwb_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG: { + struct msm_audio_amrwb_enc_config cfg; + struct msm_audio_amrwb_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_amrwb_enc_config))) { + rc = -EFAULT; + break; + } + if (cfg.band_mode > 8) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* ToDo: AMR WB encoder accepts values between 0-8 + while openmax provides value between 9-17 + as per spec */ + enc_cfg->band_mode = cfg.band_mode; + enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0); + /* Currently DSP does not support different frameformat */ + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int amrwb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrwb_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for amrwb driver\n", + __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for amrwb" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 8; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 16000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s:audio[%p]: Could not allocate memory for audio" + "client\n", __func__, audio); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrwb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = amrwb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrwb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_amrwb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb_in", + .fops = &audio_in_fops, +}; + +static int __init amrwb_in_init(void) +{ + return misc_register(&audio_amrwb_in_misc); +} + +device_initcall(amrwb_in_init); diff --git a/arch/arm/mach-msm/qdsp6v2/apr.c b/arch/arm/mach-msm/qdsp6v2/apr.c new file mode 100644 index 00000000000..2403c0269b2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/apr.c @@ -0,0 +1,688 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct apr_q6 q6; +struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX]; +static atomic_t dsp_state; +static atomic_t modem_state; + +static wait_queue_head_t dsp_wait; +static wait_queue_head_t modem_wait; +/* Subsystem restart: QDSP6 data, functions */ +static struct workqueue_struct *apr_reset_workqueue; +static void apr_reset_deregister(struct work_struct *work); +struct apr_reset_work { + void *handle; + struct work_struct work; +}; + + +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + struct apr_hdr *hdr; + uint16_t dest_id; + uint16_t client_id; + uint16_t w_len; + unsigned long flags; + + if (!handle || !buf) { + pr_err("APR: Wrong parameters\n"); + return -EINVAL; + } + if (svc->need_reset) { + pr_err("apr: send_pkt service need reset\n"); + return -ENETRESET; + } + + if ((svc->dest_id == APR_DEST_QDSP6) && + (atomic_read(&dsp_state) == 0)) { + pr_err("apr: Still dsp is not Up\n"); + return -ENETRESET; + } else if ((svc->dest_id == APR_DEST_MODEM) && + (atomic_read(&modem_state) == 0)) { + pr_err("apr: Still Modem is not Up\n"); + return -ENETRESET; + } + + + spin_lock_irqsave(&svc->w_lock, flags); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (!client[dest_id][client_id].handle) { + pr_err("APR: Still service is not yet opened\n"); + spin_unlock_irqrestore(&svc->w_lock, flags); + return -EINVAL; + } + hdr = (struct apr_hdr *)buf; + + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + if (dest_id == APR_DEST_MODEM) + hdr->dest_domain = APR_DOMAIN_MODEM; + else if (dest_id == APR_DEST_QDSP6) + hdr->dest_domain = APR_DOMAIN_ADSP; + + hdr->dest_svc = svc->id; + + w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size); + if (w_len != hdr->pkt_size) + pr_err("Unable to write APR pkt successfully: %d\n", w_len); + spin_unlock_irqrestore(&svc->w_lock, flags); + + return w_len; +} + +static void apr_cb_func(void *buf, int len, void *priv) +{ + struct apr_client_data data; + struct apr_client *apr_client; + struct apr_svc *c_svc; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + uint16_t clnt; + int i; + int temp_port = 0; + uint32_t *ptr; + + pr_debug("APR2: len = %d\n", len); + ptr = buf; + pr_debug("\n*****************\n"); + for (i = 0; i < len/4; i++) + pr_debug("%x ", ptr[i]); + pr_debug("\n"); + pr_debug("\n*****************\n"); + + if (!buf || len <= APR_HDR_SIZE) { + pr_err("APR: Improper apr pkt received:%p %d\n", + buf, len); + return; + } + hdr = buf; + + ver = hdr->hdr_field; + ver = (ver & 0x000F); + if (ver > APR_PKT_VER + 1) { + pr_err("APR: Wrong version: %d\n", ver); + return; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + pr_err("APR: Wrong hdr size:%d\n", hdr_size); + return; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + pr_err("APR: Wrong paket size\n"); + return; + } + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && + msg_type != APR_BASIC_RSP_RESULT) { + pr_err("APR: Wrong message type: %d\n", msg_type); + return; + } + + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX || + hdr->dest_svc >= APR_SVC_MAX) { + pr_err("APR: Wrong APR header\n"); + return; + } + + svc = hdr->dest_svc; + if (hdr->src_domain == APR_DOMAIN_MODEM) { + src = APR_DEST_MODEM; + if (svc == APR_SVC_MVS || svc == APR_SVC_MVM || + svc == APR_SVC_CVS || svc == APR_SVC_CVP || + svc == APR_SVC_TEST_CLIENT) + clnt = APR_CLIENT_VOICE; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else if (hdr->src_domain == APR_DOMAIN_ADSP) { + src = APR_DEST_QDSP6; + if (svc == APR_SVC_AFE || svc == APR_SVC_ASM || + svc == APR_SVC_VSM || svc == APR_SVC_VPM || + svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE || + svc == APR_SVC_USM || + svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM || + svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP) + clnt = APR_CLIENT_AUDIO; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else { + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return; + } + + pr_debug("src =%d clnt = %d\n", src, clnt); + apr_client = &client[src][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) + if (apr_client->svc[i].id == svc) { + pr_debug("%d\n", apr_client->svc[i].id); + c_svc = &apr_client->svc[i]; + break; + } + + if (i == APR_SVC_MAX) { + pr_err("APR: service is not registered\n"); + return; + } + pr_debug("svc_idx = %d\n", i); + pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id, + c_svc->client_id, c_svc->fn, c_svc->priv); + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", data.src_port, temp_port); + if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + pr_err("APR: Rxed a packet for NULL callback\n"); +} + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + int client_id = 0; + int svc_idx = 0; + int svc_id = 0; + int dest_id = 0; + int temp_port = 0; + struct apr_svc *svc = NULL; + int rc = 0; + + if (!dest || !svc_name || !svc_fn) + return NULL; + + if (!strncmp(dest, "ADSP", 4)) + dest_id = APR_DEST_QDSP6; + else if (!strncmp(dest, "MODEM", 5)) { + dest_id = APR_DEST_MODEM; + } else { + pr_err("APR: wrong destination\n"); + goto done; + } + + if ((dest_id == APR_DEST_QDSP6) && + (atomic_read(&dsp_state) == 0)) { + pr_info("%s: Wait for Lpass to bootup\n", __func__); + rc = wait_event_interruptible_timeout(dsp_wait, + (atomic_read(&dsp_state) == 1), (1 * HZ)); + if (rc == 0) { + pr_err("%s: DSP is not Up\n", __func__); + return NULL; + } + pr_info("%s: Lpass Up\n", __func__); + } else if ((dest_id == APR_DEST_MODEM) && + (atomic_read(&modem_state) == 0)) { + pr_info("%s: Wait for modem to bootup\n", __func__); + rc = wait_event_interruptible_timeout(modem_wait, + (atomic_read(&modem_state) == 1), (1 * HZ)); + if (rc == 0) { + pr_err("%s: Modem is not Up\n", __func__); + return NULL; + } + pr_info("%s: modem Up\n", __func__); + } + + if (!strncmp(svc_name, "AFE", 3)) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 0; + svc_id = APR_SVC_AFE; + } else if (!strncmp(svc_name, "ASM", 3)) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 1; + svc_id = APR_SVC_ASM; + } else if (!strncmp(svc_name, "ADM", 3)) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 2; + svc_id = APR_SVC_ADM; + } else if (!strncmp(svc_name, "CORE", 4)) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 3; + svc_id = APR_SVC_ADSP_CORE; + } else if (!strncmp(svc_name, "TEST", 4)) { + if (dest_id == APR_DEST_QDSP6) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 4; + } else { + client_id = APR_CLIENT_VOICE; + svc_idx = 7; + } + svc_id = APR_SVC_TEST_CLIENT; + } else if (!strncmp(svc_name, "VSM", 3)) { + client_id = APR_CLIENT_VOICE; + svc_idx = 0; + svc_id = APR_SVC_VSM; + } else if (!strncmp(svc_name, "VPM", 3)) { + client_id = APR_CLIENT_VOICE; + svc_idx = 1; + svc_id = APR_SVC_VPM; + } else if (!strncmp(svc_name, "MVS", 3)) { + client_id = APR_CLIENT_VOICE; + svc_idx = 2; + svc_id = APR_SVC_MVS; + } else if (!strncmp(svc_name, "MVM", 3)) { + if (dest_id == APR_DEST_MODEM) { + client_id = APR_CLIENT_VOICE; + svc_idx = 3; + svc_id = APR_SVC_MVM; + } else { + client_id = APR_CLIENT_AUDIO; + svc_idx = 5; + svc_id = APR_SVC_ADSP_MVM; + } + } else if (!strncmp(svc_name, "CVS", 3)) { + if (dest_id == APR_DEST_MODEM) { + client_id = APR_CLIENT_VOICE; + svc_idx = 4; + svc_id = APR_SVC_CVS; + } else { + client_id = APR_CLIENT_AUDIO; + svc_idx = 6; + svc_id = APR_SVC_ADSP_CVS; + } + } else if (!strncmp(svc_name, "CVP", 3)) { + if (dest_id == APR_DEST_MODEM) { + client_id = APR_CLIENT_VOICE; + svc_idx = 5; + svc_id = APR_SVC_CVP; + } else { + client_id = APR_CLIENT_AUDIO; + svc_idx = 7; + svc_id = APR_SVC_ADSP_CVP; + } + } else if (!strncmp(svc_name, "SRD", 3)) { + client_id = APR_CLIENT_VOICE; + svc_idx = 6; + svc_id = APR_SVC_SRD; + } else if (!strncmp(svc_name, "USM", 3)) { + client_id = APR_CLIENT_AUDIO; + svc_idx = 8; + svc_id = APR_SVC_USM; + } else { + pr_err("APR: Wrong svc name\n"); + goto done; + } + + pr_debug("svc name = %s c_id = %d dest_id = %d\n", + svc_name, client_id, dest_id); + mutex_lock(&q6.lock); + if (q6.state == APR_Q6_NOIMG) { + q6.pil = pil_get("q6"); + if (IS_ERR(q6.pil)) { + rc = PTR_ERR(q6.pil); + pr_err("APR: Unable to load q6 image, error:%d\n", rc); + mutex_unlock(&q6.lock); + return svc; + } + q6.state = APR_Q6_LOADED; + } + mutex_unlock(&q6.lock); + mutex_lock(&client[dest_id][client_id].m_lock); + if (!client[dest_id][client_id].handle) { + client[dest_id][client_id].handle = apr_tal_open(client_id, + dest_id, APR_DL_SMD, apr_cb_func, NULL); + if (!client[dest_id][client_id].handle) { + svc = NULL; + pr_err("APR: Unable to open handle\n"); + mutex_unlock(&client[dest_id][client_id].m_lock); + goto done; + } + } + mutex_unlock(&client[dest_id][client_id].m_lock); + svc = &client[dest_id][client_id].svc[svc_idx]; + mutex_lock(&svc->m_lock); + client[dest_id][client_id].id = client_id; + if (svc->need_reset) { + mutex_unlock(&svc->m_lock); + pr_err("APR: Service needs reset\n"); + goto done; + } + svc->priv = priv; + svc->id = svc_id; + svc->dest_id = dest_id; + svc->client_id = client_id; + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", src_port, temp_port); + if (temp_port >= APR_MAX_PORTS || temp_port < 0) { + pr_err("APR: temp_port out of bounds\n"); + mutex_unlock(&svc->m_lock); + return NULL; + } + if (!svc->port_cnt && !svc->svc_cnt) + client[dest_id][client_id].svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + } else { + if (!svc->fn) { + if (!svc->port_cnt && !svc->svc_cnt) + client[dest_id][client_id].svc_cnt++; + svc->fn = svc_fn; + if (svc->port_cnt) + svc->svc_cnt++; + } + } + + mutex_unlock(&svc->m_lock); +done: + return svc; +} + +static void apr_reset_deregister(struct work_struct *work) +{ + struct apr_svc *handle = NULL; + struct apr_reset_work *apr_reset = + container_of(work, struct apr_reset_work, work); + + handle = apr_reset->handle; + pr_debug("%s:handle[%p]\n", __func__, handle); + apr_deregister(handle); + kfree(apr_reset); +} + +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + uint16_t dest_id; + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + else if (svc->svc_cnt) + svc->svc_cnt--; + if (!svc->port_cnt && !svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + svc->need_reset = 0x0; + } + } else if (client[dest_id][client_id].svc_cnt > 0) { + client[dest_id][client_id].svc_cnt--; + if (!client[dest_id][client_id].svc_cnt) { + svc->need_reset = 0x0; + pr_debug("%s: service is reset %p\n", __func__, svc); + } + } + + if (!svc->port_cnt && !svc->svc_cnt) { + svc->priv = NULL; + svc->id = 0; + svc->fn = NULL; + svc->dest_id = 0; + svc->client_id = 0; + svc->need_reset = 0x0; + } + if (client[dest_id][client_id].handle && + !client[dest_id][client_id].svc_cnt) { + apr_tal_close(client[dest_id][client_id].handle); + client[dest_id][client_id].handle = NULL; + } + mutex_unlock(&svc->m_lock); + + return 0; +} + +void apr_reset(void *handle) +{ + struct apr_reset_work *apr_reset_worker = NULL; + + if (!handle) + return; + pr_debug("%s: handle[%p]\n", __func__, handle); + + if (apr_reset_workqueue == NULL) { + pr_err("%s: apr_reset_workqueue is NULL\n", __func__); + return; + } + + apr_reset_worker = kzalloc(sizeof(struct apr_reset_work), + GFP_ATOMIC); + + if (apr_reset_worker == NULL) { + pr_err("%s: mem failure\n", __func__); + return; + } + + apr_reset_worker->handle = handle; + INIT_WORK(&apr_reset_worker->work, apr_reset_deregister); + queue_work(apr_reset_workqueue, &apr_reset_worker->work); +} + +void change_q6_state(int state) +{ + mutex_lock(&q6.lock); + q6.state = state; + mutex_unlock(&q6.lock); +} + +int adsp_state(int state) +{ + pr_info("dsp state = %d\n", state); + return 0; +} + +/* Dispatch the Reset events to Modem and audio clients */ +void dispatch_event(unsigned long code, unsigned short proc) +{ + struct apr_client *apr_client; + struct apr_client_data data; + struct apr_svc *svc; + uint16_t clnt; + int i, j; + + data.opcode = RESET_EVENTS; + data.reset_event = code; + data.reset_proc = proc; + + clnt = APR_CLIENT_AUDIO; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } + + clnt = APR_CLIENT_VOICE; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } +} + +static int modem_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("M-Notify: Shutdown started\n"); + atomic_set(&modem_state, 0); + dispatch_event(code, APR_DEST_MODEM); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("M-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("M-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (atomic_read(&modem_state) == 0) { + atomic_set(&modem_state, 1); + wake_up(&modem_wait); + } + pr_debug("M-Notify: Bootup Completed\n"); + break; + default: + pr_err("M-Notify: General: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block mnb = { + .notifier_call = modem_notifier_cb, +}; + +static int lpass_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("L-Notify: Shutdown started\n"); + atomic_set(&dsp_state, 0); + dispatch_event(code, APR_DEST_QDSP6); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("L-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("L-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (atomic_read(&dsp_state) == 0) { + atomic_set(&dsp_state, 1); + wake_up(&dsp_wait); + } + pr_debug("L-Notify: Bootup Completed\n"); + break; + default: + pr_err("L-Notify: Generel: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block lnb = { + .notifier_call = lpass_notifier_cb, +}; + + +static int __init apr_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DEST_MAX; i++) + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_init(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) { + mutex_init(&client[i][j].svc[k].m_lock); + spin_lock_init(&client[i][j].svc[k].w_lock); + } + } + mutex_init(&q6.lock); + dsp_debug_register(adsp_state); + apr_reset_workqueue = + create_singlethread_workqueue("apr_driver"); + if (!apr_reset_workqueue) + return -ENOMEM; + return 0; +} +device_initcall(apr_init); + +static int __init apr_late_init(void) +{ + int ret = 0; + init_waitqueue_head(&dsp_wait); + init_waitqueue_head(&modem_wait); + atomic_set(&dsp_state, 1); + atomic_set(&modem_state, 1); + subsys_notif_register_notifier("modem", &mnb); + subsys_notif_register_notifier("lpass", &lnb); + return ret; +} +late_initcall(apr_late_init); diff --git a/arch/arm/mach-msm/qdsp6v2/apr_tal.c b/arch/arm/mach-msm/qdsp6v2/apr_tal.c new file mode 100644 index 00000000000..03f0513888e --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/apr_tal.c @@ -0,0 +1,280 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = { + { + "apr_audio_svc", + "apr_voice_svc", + }, + { + "apr_audio_svc", + "apr_voice_svc", + }, +}; + +struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX]; + +int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len) +{ + int w_len; + unsigned long flags; + + + spin_lock_irqsave(&apr_ch->w_lock, flags); + if (smd_write_avail(apr_ch->ch) < len) { + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + return -EAGAIN; + } + + w_len = smd_write(apr_ch->ch, data, len); + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + pr_debug("apr_tal:w_len = %d\n", w_len); + + if (w_len != len) { + pr_err("apr_tal: Error in write\n"); + return -ENETRESET; + } + return w_len; +} + +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len) +{ + int rc = 0, retries = 0; + + if (!apr_ch->ch) + return -EINVAL; + + do { + if (rc == -EAGAIN) + udelay(50); + + rc = __apr_tal_write(apr_ch, data, len); + } while (rc == -EAGAIN && retries++ < 300); + + if (rc == -EAGAIN) + pr_err("apr_tal: TIMEOUT for write\n"); + + return rc; +} + +static void apr_tal_notify(void *priv, unsigned event) +{ + struct apr_svc_ch_dev *apr_ch = priv; + int len, r_len, sz; + int pkt_cnt = 0; + unsigned long flags; + + pr_debug("event = %d\n", event); + switch (event) { + case SMD_EVENT_DATA: + pkt_cnt = 0; + spin_lock_irqsave(&apr_ch->lock, flags); +check_pending: + len = smd_read_avail(apr_ch->ch); + if (len < 0) { + pr_err("apr_tal: Invalid Read Event :%d\n", len); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + sz = smd_cur_packet_size(apr_ch->ch); + if (sz < 0) { + pr_debug("pkt size is zero\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + if (!len && !sz && !pkt_cnt) + goto check_write_avail; + if (!len) { + pr_debug("len = %d pkt_cnt = %d\n", len, pkt_cnt); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + r_len = smd_read_from_cb(apr_ch->ch, apr_ch->data, len); + if (len != r_len) { + pr_err("apr_tal: Invalid Read\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + pkt_cnt++; + pr_debug("%d %d %d\n", len, sz, pkt_cnt); + if (apr_ch->func) + apr_ch->func(apr_ch->data, r_len, apr_ch->priv); + goto check_pending; +check_write_avail: + if (smd_write_avail(apr_ch->ch)) + wake_up(&apr_ch->wait); + spin_unlock_irqrestore(&apr_ch->lock, flags); + break; + case SMD_EVENT_OPEN: + pr_info("apr_tal: SMD_EVENT_OPEN\n"); + apr_ch->smd_state = 1; + wake_up(&apr_ch->wait); + break; + case SMD_EVENT_CLOSE: + pr_info("apr_tal: SMD_EVENT_CLOSE\n"); + break; + } +} + +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv) +{ + int rc; + + if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) || + (dl >= APR_DL_MAX)) { + pr_err("apr_tal: Invalid params\n"); + return NULL; + } + + if (apr_svc_ch[dl][dest][svc].ch) { + pr_err("apr_tal: This channel alreday openend\n"); + return NULL; + } + + mutex_lock(&apr_svc_ch[dl][dest][svc].m_lock); + if (!apr_svc_ch[dl][dest][svc].dest_state) { + rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest, + apr_svc_ch[dl][dest][svc].dest_state, + msecs_to_jiffies(APR_OPEN_TIMEOUT_MS)); + if (rc == 0) { + pr_err("apr_tal:open timeout\n"); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + return NULL; + } + pr_debug("apr_tal:Wakeup done\n"); + apr_svc_ch[dl][dest][svc].dest_state = 0; + } + rc = smd_named_open_on_edge(svc_names[dest][svc], dest, + &apr_svc_ch[dl][dest][svc].ch, + &apr_svc_ch[dl][dest][svc], + apr_tal_notify); + if (rc < 0) { + pr_err("apr_tal: smd_open failed %s\n", + svc_names[dest][svc]); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + return NULL; + } + rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait, + (apr_svc_ch[dl][dest][svc].smd_state == 1), 5 * HZ); + if (rc == 0) { + pr_err("apr_tal:TIMEOUT for OPEN event\n"); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + apr_tal_close(&apr_svc_ch[dl][dest][svc]); + return NULL; + } + if (!apr_svc_ch[dl][dest][svc].dest_state) { + apr_svc_ch[dl][dest][svc].dest_state = 1; + pr_debug("apr_tal:Waiting for apr svc init\n"); + msleep(200); + pr_debug("apr_tal:apr svc init done\n"); + } + apr_svc_ch[dl][dest][svc].smd_state = 0; + + apr_svc_ch[dl][dest][svc].func = func; + apr_svc_ch[dl][dest][svc].priv = priv; + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + + return &apr_svc_ch[dl][dest][svc]; +} + +int apr_tal_close(struct apr_svc_ch_dev *apr_ch) +{ + int r; + + if (!apr_ch->ch) + return -EINVAL; + + mutex_lock(&apr_ch->m_lock); + r = smd_close(apr_ch->ch); + apr_ch->ch = NULL; + apr_ch->func = NULL; + apr_ch->priv = NULL; + mutex_unlock(&apr_ch->m_lock); + return r; +} + +static int apr_smd_probe(struct platform_device *pdev) +{ + int dest; + int clnt; + + if (pdev->id == APR_DEST_MODEM) { + pr_info("apr_tal:Modem Is Up\n"); + dest = APR_DEST_MODEM; + clnt = APR_CLIENT_VOICE; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else if (pdev->id == APR_DEST_QDSP6) { + pr_info("apr_tal:Q6 Is Up\n"); + dest = APR_DEST_QDSP6; + clnt = APR_CLIENT_AUDIO; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else + pr_err("apr_tal:Invalid Dest Id: %d\n", pdev->id); + + return 0; +} + +static struct platform_driver apr_q6_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_audio_svc", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver apr_modem_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_voice_svc", + .owner = THIS_MODULE, + }, +}; + +static int __init apr_tal_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DL_MAX; i++) + for (j = 0; j < APR_DEST_MAX; j++) + for (k = 0; k < APR_CLIENT_MAX; k++) { + init_waitqueue_head(&apr_svc_ch[i][j][k].wait); + init_waitqueue_head(&apr_svc_ch[i][j][k].dest); + spin_lock_init(&apr_svc_ch[i][j][k].lock); + spin_lock_init(&apr_svc_ch[i][j][k].w_lock); + mutex_init(&apr_svc_ch[i][j][k].m_lock); + } + platform_driver_register(&apr_q6_driver); + platform_driver_register(&apr_modem_driver); + return 0; +} +device_initcall(apr_tal_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_aac.c new file mode 100644 index 00000000000..485234f6f43 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_aac.c @@ -0,0 +1,295 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 +#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out)) + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } else { + uint16_t sce_left = 1, sce_right = 2; + aac_config = audio->codec_cfg; + if ((aac_config->dual_mono_mode < + AUDIO_AAC_DUAL_MONO_PL_PR) || + (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR)) { + pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid" + "dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify" + "dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s: asm cmd dualmono failed" + " rc=%d\n", __func__, rc); + } + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s[%p]:Failed in utils_ioctl: %d\n", + __func__, audio, rc); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:Could not allocate memory for aac" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + audio->buf_cfg.frames_per_buf = 0x01;*/ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_aac_init(void) +{ + return misc_register(&audio_aac_misc); +} + +device_initcall(audio_aac_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c new file mode 100644 index 00000000000..e7a81d3f5a6 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c @@ -0,0 +1,941 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MAX_NETWORKS 12 + +struct sidetone_atomic_cal { + atomic_t enable; + atomic_t gain; +}; + + +struct acdb_data { + struct mutex acdb_mutex; + + /* ANC Cal */ + struct acdb_atomic_cal_block anc_cal; + + /* AudProc Cal */ + atomic_t asm_topology; + atomic_t adm_topology[MAX_AUDPROC_TYPES]; + struct acdb_atomic_cal_block audproc_cal[MAX_AUDPROC_TYPES]; + struct acdb_atomic_cal_block audstrm_cal[MAX_AUDPROC_TYPES]; + struct acdb_atomic_cal_block audvol_cal[MAX_AUDPROC_TYPES]; + + /* VocProc Cal */ + atomic_t voice_rx_topology; + atomic_t voice_tx_topology; + struct acdb_atomic_cal_block vocproc_cal[MAX_NETWORKS]; + struct acdb_atomic_cal_block vocstrm_cal[MAX_NETWORKS]; + struct acdb_atomic_cal_block vocvol_cal[MAX_NETWORKS]; + /* size of cal block tables above*/ + atomic_t vocproc_cal_size; + atomic_t vocstrm_cal_size; + atomic_t vocvol_cal_size; + /* Total size of cal data for all networks */ + atomic_t vocproc_total_cal_size; + atomic_t vocstrm_total_cal_size; + atomic_t vocvol_total_cal_size; + + /* AFE cal */ + struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES]; + + /* Sidetone Cal */ + struct sidetone_atomic_cal sidetone_cal; + + /* Allocation information */ + struct ion_client *ion_client; + struct ion_handle *ion_handle; + atomic_t map_handle; + atomic64_t paddr; + atomic64_t kvaddr; + atomic64_t mem_len; +}; + +static struct acdb_data acdb_data; +static atomic_t usage_count; + +uint32_t get_voice_rx_topology(void) +{ + return atomic_read(&acdb_data.voice_rx_topology); +} + +void store_voice_rx_topology(uint32_t topology) +{ + atomic_set(&acdb_data.voice_rx_topology, topology); +} + +uint32_t get_voice_tx_topology(void) +{ + return atomic_read(&acdb_data.voice_tx_topology); +} + +void store_voice_tx_topology(uint32_t topology) +{ + atomic_set(&acdb_data.voice_tx_topology, topology); +} + +uint32_t get_adm_rx_topology(void) +{ + return atomic_read(&acdb_data.adm_topology[RX_CAL]); +} + +void store_adm_rx_topology(uint32_t topology) +{ + atomic_set(&acdb_data.adm_topology[RX_CAL], topology); +} + +uint32_t get_adm_tx_topology(void) +{ + return atomic_read(&acdb_data.adm_topology[TX_CAL]); +} + +void store_adm_tx_topology(uint32_t topology) +{ + atomic_set(&acdb_data.adm_topology[TX_CAL], topology); +} + +uint32_t get_asm_topology(void) +{ + return atomic_read(&acdb_data.asm_topology); +} + +void store_asm_topology(uint32_t topology) +{ + atomic_set(&acdb_data.asm_topology, topology); +} + +void get_all_voice_cal(struct acdb_cal_block *cal_block) +{ + cal_block->cal_kvaddr = + atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.vocproc_cal[0].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.vocproc_total_cal_size) + + atomic_read(&acdb_data.vocstrm_total_cal_size) + + atomic_read(&acdb_data.vocvol_total_cal_size); +} + +void get_all_cvp_cal(struct acdb_cal_block *cal_block) +{ + cal_block->cal_kvaddr = + atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.vocproc_cal[0].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.vocproc_total_cal_size) + + atomic_read(&acdb_data.vocvol_total_cal_size); +} + +void get_all_vocproc_cal(struct acdb_cal_block *cal_block) +{ + cal_block->cal_kvaddr = + atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.vocproc_cal[0].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.vocproc_total_cal_size); +} + +void get_all_vocstrm_cal(struct acdb_cal_block *cal_block) +{ + cal_block->cal_kvaddr = + atomic_read(&acdb_data.vocstrm_cal[0].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.vocstrm_cal[0].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.vocstrm_total_cal_size); +} + +void get_all_vocvol_cal(struct acdb_cal_block *cal_block) +{ + cal_block->cal_kvaddr = + atomic_read(&acdb_data.vocvol_cal[0].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.vocvol_cal[0].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.vocvol_total_cal_size); +} + +void get_anc_cal(struct acdb_cal_block *cal_block) +{ + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + cal_block->cal_kvaddr = + atomic_read(&acdb_data.anc_cal.cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.anc_cal.cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.anc_cal.cal_size); +done: + return; +} + +void store_anc_cal(struct cal_block *cal_block) +{ + pr_debug("%s,\n", __func__); + + if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_block->cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + goto done; + } + + atomic_set(&acdb_data.anc_cal.cal_kvaddr, + cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr)); + atomic_set(&acdb_data.anc_cal.cal_paddr, + cal_block->cal_offset + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.anc_cal.cal_size, + cal_block->cal_size); +done: + return; +} + +void store_afe_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_block->cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + goto done; + } + if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + atomic_set(&acdb_data.afe_cal[path].cal_kvaddr, + cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr)); + atomic_set(&acdb_data.afe_cal[path].cal_paddr, + cal_block->cal_offset + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.afe_cal[path].cal_size, + cal_block->cal_size); +done: + return; +} + +void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + cal_block->cal_kvaddr = + atomic_read(&acdb_data.afe_cal[path].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.afe_cal[path].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.afe_cal[path].cal_size); +done: + return; +} + +void store_audproc_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_block->cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + atomic_set(&acdb_data.audproc_cal[path].cal_kvaddr, + cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr)); + atomic_set(&acdb_data.audproc_cal[path].cal_paddr, + cal_block->cal_offset + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.audproc_cal[path].cal_size, + cal_block->cal_size); +done: + return; +} + +void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + cal_block->cal_kvaddr = + atomic_read(&acdb_data.audproc_cal[path].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.audproc_cal[path].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.audproc_cal[path].cal_size); +done: + return; +} + +void store_audstrm_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_block->cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + atomic_set(&acdb_data.audstrm_cal[path].cal_kvaddr, + cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr)); + atomic_set(&acdb_data.audstrm_cal[path].cal_paddr, + cal_block->cal_offset + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.audstrm_cal[path].cal_size, + cal_block->cal_size); +done: + return; +} + +void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + cal_block->cal_kvaddr = + atomic_read(&acdb_data.audstrm_cal[path].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.audstrm_cal[path].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.audstrm_cal[path].cal_size); +done: + return; +} + +void store_audvol_cal(int32_t path, struct cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_block->cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + goto done; + } + if (path >= MAX_AUDPROC_TYPES) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + atomic_set(&acdb_data.audvol_cal[path].cal_kvaddr, + cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr)); + atomic_set(&acdb_data.audvol_cal[path].cal_paddr, + cal_block->cal_offset + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.audvol_cal[path].cal_size, + cal_block->cal_size); +done: + return; +} + +void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block) +{ + pr_debug("%s, path = %d\n", __func__, path); + + if (cal_block == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + if (path >= MAX_AUDPROC_TYPES || path < 0) { + pr_err("ACDB=> Bad path sent to %s, path: %d\n", + __func__, path); + goto done; + } + + cal_block->cal_kvaddr = + atomic_read(&acdb_data.audvol_cal[path].cal_kvaddr); + cal_block->cal_paddr = + atomic_read(&acdb_data.audvol_cal[path].cal_paddr); + cal_block->cal_size = + atomic_read(&acdb_data.audvol_cal[path].cal_size); +done: + return; +} + + +void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks) +{ + int i; + pr_debug("%s\n", __func__); + + if (len > MAX_NETWORKS) { + pr_err("%s: Calibration sent for %d networks, only %d are " + "supported!\n", __func__, len, MAX_NETWORKS); + goto done; + } + + atomic_set(&acdb_data.vocproc_total_cal_size, 0); + for (i = 0; i < len; i++) { + if (cal_blocks[i].cal_offset > + atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_blocks[i].cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + atomic_set(&acdb_data.vocproc_cal[i].cal_size, 0); + } else { + atomic_add(cal_blocks[i].cal_size, + &acdb_data.vocproc_total_cal_size); + atomic_set(&acdb_data.vocproc_cal[i].cal_size, + cal_blocks[i].cal_size); + atomic_set(&acdb_data.vocproc_cal[i].cal_paddr, + cal_blocks[i].cal_offset + + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.vocproc_cal[i].cal_kvaddr, + cal_blocks[i].cal_offset + + atomic64_read(&acdb_data.kvaddr)); + } + } + atomic_set(&acdb_data.vocproc_cal_size, len); +done: + return; +} + +void get_vocproc_cal(struct acdb_cal_data *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + cal_data->num_cal_blocks = atomic_read(&acdb_data.vocproc_cal_size); + cal_data->cal_blocks = &acdb_data.vocproc_cal[0]; +done: + return; +} + +void store_vocstrm_cal(int32_t len, struct cal_block *cal_blocks) +{ + int i; + pr_debug("%s\n", __func__); + + if (len > MAX_NETWORKS) { + pr_err("%s: Calibration sent for %d networks, only %d are " + "supported!\n", __func__, len, MAX_NETWORKS); + goto done; + } + + atomic_set(&acdb_data.vocstrm_total_cal_size, 0); + for (i = 0; i < len; i++) { + if (cal_blocks[i].cal_offset > + atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_blocks[i].cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + atomic_set(&acdb_data.vocstrm_cal[i].cal_size, 0); + } else { + atomic_add(cal_blocks[i].cal_size, + &acdb_data.vocstrm_total_cal_size); + atomic_set(&acdb_data.vocstrm_cal[i].cal_size, + cal_blocks[i].cal_size); + atomic_set(&acdb_data.vocstrm_cal[i].cal_paddr, + cal_blocks[i].cal_offset + + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.vocstrm_cal[i].cal_kvaddr, + cal_blocks[i].cal_offset + + atomic64_read(&acdb_data.kvaddr)); + } + } + atomic_set(&acdb_data.vocstrm_cal_size, len); +done: + return; +} + +void get_vocstrm_cal(struct acdb_cal_data *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + cal_data->num_cal_blocks = atomic_read(&acdb_data.vocstrm_cal_size); + cal_data->cal_blocks = &acdb_data.vocstrm_cal[0]; +done: + return; +} + +void store_vocvol_cal(int32_t len, struct cal_block *cal_blocks) +{ + int i; + pr_debug("%s\n", __func__); + + if (len > MAX_NETWORKS) { + pr_err("%s: Calibration sent for %d networks, only %d are " + "supported!\n", __func__, len, MAX_NETWORKS); + goto done; + } + + atomic_set(&acdb_data.vocvol_total_cal_size, 0); + for (i = 0; i < len; i++) { + if (cal_blocks[i].cal_offset > + atomic64_read(&acdb_data.mem_len)) { + pr_err("%s: offset %d is > mem_len %ld\n", + __func__, cal_blocks[i].cal_offset, + (long)atomic64_read(&acdb_data.mem_len)); + atomic_set(&acdb_data.vocvol_cal[i].cal_size, 0); + } else { + atomic_add(cal_blocks[i].cal_size, + &acdb_data.vocvol_total_cal_size); + atomic_set(&acdb_data.vocvol_cal[i].cal_size, + cal_blocks[i].cal_size); + atomic_set(&acdb_data.vocvol_cal[i].cal_paddr, + cal_blocks[i].cal_offset + + atomic64_read(&acdb_data.paddr)); + atomic_set(&acdb_data.vocvol_cal[i].cal_kvaddr, + cal_blocks[i].cal_offset + + atomic64_read(&acdb_data.kvaddr)); + } + } + atomic_set(&acdb_data.vocvol_cal_size, len); +done: + return; +} + +void get_vocvol_cal(struct acdb_cal_data *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + cal_data->num_cal_blocks = atomic_read(&acdb_data.vocvol_cal_size); + cal_data->cal_blocks = &acdb_data.vocvol_cal[0]; +done: + return; +} + +void store_sidetone_cal(struct sidetone_cal *cal_data) +{ + pr_debug("%s\n", __func__); + + atomic_set(&acdb_data.sidetone_cal.enable, cal_data->enable); + atomic_set(&acdb_data.sidetone_cal.gain, cal_data->gain); +} + + +void get_sidetone_cal(struct sidetone_cal *cal_data) +{ + pr_debug("%s\n", __func__); + + if (cal_data == NULL) { + pr_err("ACDB=> NULL pointer sent to %s\n", __func__); + goto done; + } + + cal_data->enable = atomic_read(&acdb_data.sidetone_cal.enable); + cal_data->gain = atomic_read(&acdb_data.sidetone_cal.gain); +done: + return; +} + +static int acdb_open(struct inode *inode, struct file *f) +{ + s32 result = 0; + pr_debug("%s\n", __func__); + + if (atomic64_read(&acdb_data.mem_len)) { + pr_debug("%s: ACDB opened but memory allocated, " + "using existing allocation!\n", + __func__); + } + + atomic_inc(&usage_count); + return result; +} + +static int deregister_memory(void) +{ + if (atomic64_read(&acdb_data.mem_len)) { + mutex_lock(&acdb_data.acdb_mutex); + ion_unmap_kernel(acdb_data.ion_client, acdb_data.ion_handle); + ion_free(acdb_data.ion_client, acdb_data.ion_handle); + ion_client_destroy(acdb_data.ion_client); + mutex_unlock(&acdb_data.acdb_mutex); + atomic64_set(&acdb_data.mem_len, 0); + } + return 0; +} + +static int register_memory(void) +{ + int result; + unsigned long paddr; + void *kvptr; + unsigned long kvaddr; + unsigned long mem_len; + + mutex_lock(&acdb_data.acdb_mutex); + acdb_data.ion_client = + msm_ion_client_create(UINT_MAX, "audio_acdb_client"); + if (IS_ERR_OR_NULL(acdb_data.ion_client)) { + pr_err("%s: Could not register ION client!!!\n", __func__); + result = PTR_ERR(acdb_data.ion_client); + goto err; + } + + acdb_data.ion_handle = ion_import_fd(acdb_data.ion_client, + atomic_read(&acdb_data.map_handle)); + if (IS_ERR_OR_NULL(acdb_data.ion_handle)) { + pr_err("%s: Could not import map handle!!!\n", __func__); + result = PTR_ERR(acdb_data.ion_handle); + goto err_ion_client; + } + + result = ion_phys(acdb_data.ion_client, acdb_data.ion_handle, + &paddr, (size_t *)&mem_len); + if (result != 0) { + pr_err("%s: Could not get phys addr!!!\n", __func__); + goto err_ion_handle; + } + + kvptr = ion_map_kernel(acdb_data.ion_client, + acdb_data.ion_handle, 0); + if (IS_ERR_OR_NULL(kvptr)) { + pr_err("%s: Could not get kernel virt addr!!!\n", __func__); + result = PTR_ERR(kvptr); + goto err_ion_handle; + } + kvaddr = (unsigned long)kvptr; + mutex_unlock(&acdb_data.acdb_mutex); + + atomic64_set(&acdb_data.paddr, paddr); + atomic64_set(&acdb_data.kvaddr, kvaddr); + atomic64_set(&acdb_data.mem_len, mem_len); + pr_debug("%s done! paddr = 0x%lx, " + "kvaddr = 0x%lx, len = x%lx\n", + __func__, + (long)atomic64_read(&acdb_data.paddr), + (long)atomic64_read(&acdb_data.kvaddr), + (long)atomic64_read(&acdb_data.mem_len)); + + return result; +err_ion_handle: + ion_free(acdb_data.ion_client, acdb_data.ion_handle); +err_ion_client: + ion_client_destroy(acdb_data.ion_client); +err: + atomic64_set(&acdb_data.mem_len, 0); + mutex_unlock(&acdb_data.acdb_mutex); + return result; +} +static long acdb_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + int32_t result = 0; + int32_t size; + int32_t map_fd; + uint32_t topology; + struct cal_block data[MAX_NETWORKS]; + pr_debug("%s\n", __func__); + + switch (cmd) { + case AUDIO_REGISTER_PMEM: + pr_debug("AUDIO_REGISTER_PMEM\n"); + if (atomic_read(&acdb_data.mem_len)) { + deregister_memory(); + pr_debug("Remove the existing memory\n"); + } + + if (copy_from_user(&map_fd, (void *)arg, sizeof(map_fd))) { + pr_err("%s: fail to copy memory handle!\n", __func__); + result = -EFAULT; + } else { + atomic_set(&acdb_data.map_handle, map_fd); + result = register_memory(); + } + goto done; + + case AUDIO_DEREGISTER_PMEM: + pr_debug("AUDIO_DEREGISTER_PMEM\n"); + deregister_memory(); + goto done; + case AUDIO_SET_VOICE_RX_TOPOLOGY: + if (copy_from_user(&topology, (void *)arg, + sizeof(topology))) { + pr_err("%s: fail to copy topology!\n", __func__); + result = -EFAULT; + } + store_voice_rx_topology(topology); + goto done; + case AUDIO_SET_VOICE_TX_TOPOLOGY: + if (copy_from_user(&topology, (void *)arg, + sizeof(topology))) { + pr_err("%s: fail to copy topology!\n", __func__); + result = -EFAULT; + } + store_voice_tx_topology(topology); + goto done; + case AUDIO_SET_ADM_RX_TOPOLOGY: + if (copy_from_user(&topology, (void *)arg, + sizeof(topology))) { + pr_err("%s: fail to copy topology!\n", __func__); + result = -EFAULT; + } + store_adm_rx_topology(topology); + goto done; + case AUDIO_SET_ADM_TX_TOPOLOGY: + if (copy_from_user(&topology, (void *)arg, + sizeof(topology))) { + pr_err("%s: fail to copy topology!\n", __func__); + result = -EFAULT; + } + store_adm_tx_topology(topology); + goto done; + case AUDIO_SET_ASM_TOPOLOGY: + if (copy_from_user(&topology, (void *)arg, + sizeof(topology))) { + pr_err("%s: fail to copy topology!\n", __func__); + result = -EFAULT; + } + store_asm_topology(topology); + goto done; + } + + if (copy_from_user(&size, (void *) arg, sizeof(size))) { + + result = -EFAULT; + goto done; + } + + if (size <= 0) { + pr_err("%s: Invalid size sent to driver: %d\n", + __func__, size); + result = -EFAULT; + goto done; + } + + if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) { + + pr_err("%s: fail to copy table size %d\n", __func__, size); + result = -EFAULT; + goto done; + } + + if (data == NULL) { + pr_err("%s: NULL pointer sent to driver!\n", __func__); + result = -EFAULT; + goto done; + } + + switch (cmd) { + case AUDIO_SET_AUDPROC_TX_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audproc_cal(TX_CAL, data); + break; + case AUDIO_SET_AUDPROC_RX_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audproc_cal(RX_CAL, data); + break; + case AUDIO_SET_AUDPROC_TX_STREAM_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audstrm_cal(TX_CAL, data); + break; + case AUDIO_SET_AUDPROC_RX_STREAM_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audstrm_cal(RX_CAL, data); + break; + case AUDIO_SET_AUDPROC_TX_VOL_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audvol_cal(TX_CAL, data); + break; + case AUDIO_SET_AUDPROC_RX_VOL_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More Audproc Cal then expected, " + "size received: %d\n", __func__, size); + store_audvol_cal(RX_CAL, data); + break; + case AUDIO_SET_AFE_TX_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More AFE Cal then expected, " + "size received: %d\n", __func__, size); + store_afe_cal(TX_CAL, data); + break; + case AUDIO_SET_AFE_RX_CAL: + if (size > sizeof(struct cal_block)) + pr_err("%s: More AFE Cal then expected, " + "size received: %d\n", __func__, size); + store_afe_cal(RX_CAL, data); + break; + case AUDIO_SET_VOCPROC_CAL: + store_vocproc_cal(size / sizeof(struct cal_block), data); + break; + case AUDIO_SET_VOCPROC_STREAM_CAL: + store_vocstrm_cal(size / sizeof(struct cal_block), data); + break; + case AUDIO_SET_VOCPROC_VOL_CAL: + store_vocvol_cal(size / sizeof(struct cal_block), data); + break; + case AUDIO_SET_SIDETONE_CAL: + if (size > sizeof(struct sidetone_cal)) + pr_err("%s: More sidetone cal then expected, " + "size received: %d\n", __func__, size); + store_sidetone_cal((struct sidetone_cal *)data); + break; + case AUDIO_SET_ANC_CAL: + store_anc_cal(data); + break; + default: + pr_err("ACDB=> ACDB ioctl not found!\n"); + } + +done: + return result; +} + +static int acdb_mmap(struct file *file, struct vm_area_struct *vma) +{ + int result = 0; + int size = vma->vm_end - vma->vm_start; + + pr_debug("%s\n", __func__); + + if (atomic64_read(&acdb_data.mem_len)) { + if (size <= atomic64_read(&acdb_data.mem_len)) { + vma->vm_page_prot = pgprot_noncached( + vma->vm_page_prot); + result = remap_pfn_range(vma, + vma->vm_start, + atomic64_read(&acdb_data.paddr) >> PAGE_SHIFT, + size, + vma->vm_page_prot); + } else { + pr_err("%s: Not enough memory!\n", __func__); + result = -ENOMEM; + } + } else { + pr_err("%s: memory is not allocated, yet!\n", __func__); + result = -ENODEV; + } + + return result; +} + +static int acdb_release(struct inode *inode, struct file *f) +{ + s32 result = 0; + + atomic_dec(&usage_count); + atomic_read(&usage_count); + + pr_debug("%s: ref count %d!\n", __func__, + atomic_read(&usage_count)); + + if (atomic_read(&usage_count) >= 1) + result = -EBUSY; + else + result = deregister_memory(); + + return result; +} + +static const struct file_operations acdb_fops = { + .owner = THIS_MODULE, + .open = acdb_open, + .release = acdb_release, + .unlocked_ioctl = acdb_ioctl, + .mmap = acdb_mmap, +}; + +struct miscdevice acdb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_acdb", + .fops = &acdb_fops, +}; + +static int __init acdb_init(void) +{ + memset(&acdb_data, 0, sizeof(acdb_data)); + mutex_init(&acdb_data.acdb_mutex); + atomic_set(&usage_count, 0); + return misc_register(&acdb_misc); +} + +static void __exit acdb_exit(void) +{ +} + +module_init(acdb_init); +module_exit(acdb_exit); + +MODULE_DESCRIPTION("MSM 8x60 Audio ACDB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c b/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c new file mode 100644 index 00000000000..f4316d09974 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c @@ -0,0 +1,161 @@ +/* amrnb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrnb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrnb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRNB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrnb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_amrnb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +static int __init audio_amrnb_init(void) +{ + return misc_register(&audio_amrnb_misc); +} + +device_initcall(audio_amrnb_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c b/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c new file mode 100644 index 00000000000..28c173294db --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c @@ -0,0 +1,164 @@ +/* amrwb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrwb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRWB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrwb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_amrwb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_amrwb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb", + .fops = &audio_amrwb_fops, +}; + +static int __init audio_amrwb_init(void) +{ + return misc_register(&audio_amrwb_misc); +} + +device_initcall(audio_amrwb_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_dev_ctl.c b/arch/arm/mach-msm/qdsp6v2/audio_dev_ctl.c new file mode 100644 index 00000000000..aaae7762a73 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_dev_ctl.c @@ -0,0 +1,1731 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + + +static DEFINE_MUTEX(session_lock); +static struct workqueue_struct *msm_reset_device_work_queue; +static void reset_device_work(struct work_struct *work); +static DECLARE_WORK(msm_reset_device_work, reset_device_work); + +struct audio_dev_ctrl_state { + struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV]; + u32 num_dev; + atomic_t opened; + struct msm_snddev_info *voice_rx_dev; + struct msm_snddev_info *voice_tx_dev; + wait_queue_head_t wait; +}; + +static struct audio_dev_ctrl_state audio_dev_ctrl; +struct event_listner event; + +struct session_freq { + int freq; + int evt; +}; + +struct audio_routing_info { + unsigned short mixer_mask[MAX_SESSIONS]; + unsigned short audrec_mixer_mask[MAX_SESSIONS]; + struct session_freq dec_freq[MAX_SESSIONS]; + struct session_freq enc_freq[MAX_SESSIONS]; + unsigned int copp_list[MAX_SESSIONS][AFE_MAX_PORTS]; + int voice_tx_dev_id; + int voice_rx_dev_id; + int voice_tx_sample_rate; + int voice_rx_sample_rate; + signed int voice_tx_vol; + signed int voice_rx_vol; + int tx_mute; + int rx_mute; + int voice_state; + struct mutex copp_list_mutex; + struct mutex adm_mutex; +}; + +static struct audio_routing_info routing_info; + +struct audio_copp_topology { + struct mutex lock; + int session_cnt; + int session_id[MAX_SESSIONS]; + int topolog_id[MAX_SESSIONS]; +}; +static struct audio_copp_topology adm_tx_topology_tbl; + +int msm_reset_all_device(void) +{ + int rc = 0; + int dev_id = 0; + struct msm_snddev_info *dev_info = NULL; + + for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + pr_err("%s:pass invalid dev_id\n", __func__); + rc = PTR_ERR(dev_info); + return rc; + } + if (!dev_info->opened) + continue; + pr_debug("%s:Resetting device %d active on COPP %d" + "with %lld as routing\n", __func__, + dev_id, dev_info->copp_id, dev_info->sessions); + broadcast_event(AUDDEV_EVT_REL_PENDING, + dev_id, + SESSION_IGNORE); + rc = dev_info->dev_ops.close(dev_info); + if (rc < 0) { + pr_err("%s:Snd device failed close!\n", __func__); + return rc; + } else { + dev_info->opened = 0; + broadcast_event(AUDDEV_EVT_DEV_RLS, + dev_id, + SESSION_IGNORE); + + if (dev_info->copp_id == VOICE_PLAYBACK_TX) + voice_start_playback(0); + } + dev_info->sessions = 0; + } + msm_clear_all_session(); + return 0; +} +EXPORT_SYMBOL(msm_reset_all_device); + +static void reset_device_work(struct work_struct *work) +{ + msm_reset_all_device(); +} + +int reset_device(void) +{ + queue_work(msm_reset_device_work_queue, &msm_reset_device_work); + return 0; +} +EXPORT_SYMBOL(reset_device); + +int msm_set_copp_id(int session_id, int copp_id) +{ + int rc = 0; + int index; + + if (session_id < 1 || session_id > 8) + return -EINVAL; + if (afe_validate_port(copp_id) < 0) + return -EINVAL; + + index = afe_get_port_index(copp_id); + if (index < 0 || index > AFE_MAX_PORTS) + return -EINVAL; + pr_debug("%s: session[%d] copp_id[%d] index[%d]\n", __func__, + session_id, copp_id, index); + mutex_lock(&routing_info.copp_list_mutex); + if (routing_info.copp_list[session_id][index] == COPP_IGNORE) + routing_info.copp_list[session_id][index] = copp_id; + mutex_unlock(&routing_info.copp_list_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_set_copp_id); + +int msm_clear_copp_id(int session_id, int copp_id) +{ + int rc = 0; + int index = afe_get_port_index(copp_id); + + if (session_id < 1 || session_id > 8) + return -EINVAL; + pr_debug("%s: session[%d] copp_id[%d] index[%d]\n", __func__, + session_id, copp_id, index); + mutex_lock(&routing_info.copp_list_mutex); + if (routing_info.copp_list[session_id][index] == copp_id) + routing_info.copp_list[session_id][index] = COPP_IGNORE; +#ifdef CONFIG_MSM8X60_RTAC + rtac_remove_adm_device(copp_id, session_id); +#endif + mutex_unlock(&routing_info.copp_list_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_clear_copp_id); + +int msm_clear_session_id(int session_id) +{ + int rc = 0; + int i = 0; + if (session_id < 1 || session_id > 8) + return -EINVAL; + pr_debug("%s: session[%d]\n", __func__, session_id); + mutex_lock(&routing_info.adm_mutex); + mutex_lock(&routing_info.copp_list_mutex); + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[session_id][i] != COPP_IGNORE) { + rc = adm_close(routing_info.copp_list[session_id][i]); + if (rc < 0) { + pr_err("%s: adm close fail port[%d] rc[%d]\n", + __func__, + routing_info.copp_list[session_id][i], + rc); + continue; + } +#ifdef CONFIG_MSM8X60_RTAC + rtac_remove_adm_device( + routing_info.copp_list[session_id][i], session_id); +#endif + routing_info.copp_list[session_id][i] = COPP_IGNORE; + rc = 0; + } + } + mutex_unlock(&routing_info.copp_list_mutex); + mutex_unlock(&routing_info.adm_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_clear_session_id); + +int msm_clear_all_session() +{ + int rc = 0; + int i = 0, j = 0; + pr_info("%s:\n", __func__); + mutex_lock(&routing_info.adm_mutex); + mutex_lock(&routing_info.copp_list_mutex); + for (j = 1; j < MAX_SESSIONS; j++) { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[j][i] != COPP_IGNORE) { + rc = adm_close( + routing_info.copp_list[j][i]); + if (rc < 0) { + pr_err("%s: adm close fail copp[%d]" + "session[%d] rc[%d]\n", + __func__, + routing_info.copp_list[j][i], + j, rc); + continue; + } + routing_info.copp_list[j][i] = COPP_IGNORE; + rc = 0; + } + } + } + mutex_unlock(&routing_info.copp_list_mutex); + mutex_unlock(&routing_info.adm_mutex); + return rc; +} +EXPORT_SYMBOL(msm_clear_all_session); + +int msm_get_voice_state(void) +{ + pr_debug("voice state %d\n", routing_info.voice_state); + return routing_info.voice_state; +} +EXPORT_SYMBOL(msm_get_voice_state); + +int msm_set_voice_mute(int dir, int mute, u32 session_id) +{ + pr_debug("dir %x mute %x\n", dir, mute); + if (dir == DIR_TX) { + routing_info.tx_mute = mute; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_tx_dev_id, session_id); + } else + return -EPERM; + return 0; +} +EXPORT_SYMBOL(msm_set_voice_mute); + +int msm_set_voice_vol(int dir, s32 volume, u32 session_id) +{ + if (dir == DIR_TX) { + routing_info.voice_tx_vol = volume; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_tx_dev_id, + session_id); + } else if (dir == DIR_RX) { + routing_info.voice_rx_vol = volume; + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, + routing_info.voice_rx_dev_id, + session_id); + } else + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(msm_set_voice_vol); + +void msm_snddev_register(struct msm_snddev_info *dev_info) +{ + mutex_lock(&session_lock); + if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) { + audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info; + /* roughly 0 DB for digital gain + * If default gain is not desirable, it is expected that + * application sets desired gain before activating sound + * device + */ + dev_info->dev_volume = 75; + dev_info->sessions = 0x0; + dev_info->usage_count = 0; + audio_dev_ctrl.num_dev++; + } else + pr_err("%s: device registry max out\n", __func__); + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(msm_snddev_register); + +int msm_snddev_devcount(void) +{ + return audio_dev_ctrl.num_dev; +} +EXPORT_SYMBOL(msm_snddev_devcount); + +int msm_snddev_query(int dev_id) +{ + if (dev_id <= audio_dev_ctrl.num_dev) + return 0; + return -ENODEV; +} +EXPORT_SYMBOL(msm_snddev_query); + +int msm_snddev_is_set(int popp_id, int copp_id) +{ + return routing_info.mixer_mask[popp_id] & (0x1 << copp_id); +} +EXPORT_SYMBOL(msm_snddev_is_set); + +unsigned short msm_snddev_route_enc(int enc_id) +{ + if (enc_id >= MAX_SESSIONS) + return -EINVAL; + return routing_info.audrec_mixer_mask[enc_id]; +} +EXPORT_SYMBOL(msm_snddev_route_enc); + +unsigned short msm_snddev_route_dec(int popp_id) +{ + if (popp_id >= MAX_SESSIONS) + return -EINVAL; + return routing_info.mixer_mask[popp_id]; +} +EXPORT_SYMBOL(msm_snddev_route_dec); + +/*To check one->many case*/ +int msm_check_multicopp_per_stream(int session_id, + struct route_payload *payload) +{ + int i = 0; + int flag = 0; + pr_debug("%s: session_id=%d\n", __func__, session_id); + mutex_lock(&routing_info.copp_list_mutex); + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[session_id][i] == COPP_IGNORE) + continue; + else { + pr_debug("Device enabled\n"); + payload->copp_ids[flag++] = + routing_info.copp_list[session_id][i]; + } + } + mutex_unlock(&routing_info.copp_list_mutex); + if (flag > 1) { + pr_debug("Multiple copp per stream case num_copps=%d\n", flag); + } else { + pr_debug("Stream routed to single copp\n"); + } + payload->num_copps = flag; + return flag; +} + +int msm_snddev_set_dec(int popp_id, int copp_id, int set, + int rate, int mode) +{ + int rc = 0, i = 0, num_copps; + struct route_payload payload; + + if ((popp_id >= MAX_SESSIONS) || (popp_id <= 0)) { + pr_err("%s: Invalid session id %d\n", __func__, popp_id); + return 0; + } + + mutex_lock(&routing_info.adm_mutex); + if (set) { + rc = adm_open(copp_id, ADM_PATH_PLAYBACK, rate, mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_err("%s: adm open fail rc[%d]\n", __func__, rc); + rc = -EINVAL; + mutex_unlock(&routing_info.adm_mutex); + return rc; + } + msm_set_copp_id(popp_id, copp_id); + pr_debug("%s:Session id=%d copp_id=%d\n", + __func__, popp_id, copp_id); + memset(payload.copp_ids, COPP_IGNORE, + (sizeof(unsigned int) * AFE_MAX_PORTS)); + num_copps = msm_check_multicopp_per_stream(popp_id, &payload); + /* Multiple streams per copp is handled, one stream at a time */ + rc = adm_matrix_map(popp_id, ADM_PATH_PLAYBACK, num_copps, + payload.copp_ids, copp_id); + if (rc < 0) { + pr_err("%s: matrix map failed rc[%d]\n", + __func__, rc); + adm_close(copp_id); + rc = -EINVAL; + mutex_unlock(&routing_info.adm_mutex); + return rc; + } +#ifdef CONFIG_MSM8X60_RTAC + for (i = 0; i < num_copps; i++) + rtac_add_adm_device(payload.copp_ids[i], popp_id); +#endif + } else { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[popp_id][i] == copp_id) { + rc = adm_close(copp_id); + if (rc < 0) { + pr_err("%s: adm close fail copp[%d]" + "rc[%d]\n", + __func__, copp_id, rc); + rc = -EINVAL; + mutex_unlock(&routing_info.adm_mutex); + return rc; + } + msm_clear_copp_id(popp_id, copp_id); + break; + } + } + } + + if (copp_id == VOICE_PLAYBACK_TX) { + /* Signal uplink playback. */ + rc = voice_start_playback(set); + } + mutex_unlock(&routing_info.adm_mutex); + return rc; +} +EXPORT_SYMBOL(msm_snddev_set_dec); + + +static int check_tx_copp_topology(int session_id) +{ + int cnt; + int ret_val = -ENOENT; + + cnt = adm_tx_topology_tbl.session_cnt; + if (cnt) { + do { + if (adm_tx_topology_tbl.session_id[cnt-1] + == session_id) + ret_val = cnt-1; + } while (--cnt); + } + + return ret_val; +} + +static int add_to_tx_topology_lists(int session_id, int topology) +{ + int idx = 0, tbl_idx; + int ret_val = -ENOSPC; + + mutex_lock(&adm_tx_topology_tbl.lock); + + tbl_idx = check_tx_copp_topology(session_id); + if (tbl_idx == -ENOENT) { + while (adm_tx_topology_tbl.session_id[idx++]) + ; + tbl_idx = idx-1; + } + + if (tbl_idx < MAX_SESSIONS) { + adm_tx_topology_tbl.session_id[tbl_idx] = session_id; + adm_tx_topology_tbl.topolog_id[tbl_idx] = topology; + adm_tx_topology_tbl.session_cnt++; + + ret_val = 0; + } + mutex_unlock(&adm_tx_topology_tbl.lock); + return ret_val; +} + +static void remove_from_tx_topology_lists(int session_id) +{ + int tbl_idx; + + mutex_lock(&adm_tx_topology_tbl.lock); + tbl_idx = check_tx_copp_topology(session_id); + if (tbl_idx != -ENOENT) { + + adm_tx_topology_tbl.session_cnt--; + adm_tx_topology_tbl.session_id[tbl_idx] = 0; + adm_tx_topology_tbl.topolog_id[tbl_idx] = 0; + } + mutex_unlock(&adm_tx_topology_tbl.lock); +} + +int auddev_cfg_tx_copp_topology(int session_id, int cfg) +{ + int ret = 0; + + if (cfg == DEFAULT_COPP_TOPOLOGY) + remove_from_tx_topology_lists(session_id); + else { + switch (cfg) { + case VPM_TX_SM_ECNS_COPP_TOPOLOGY: + case VPM_TX_DM_FLUENCE_COPP_TOPOLOGY: + ret = add_to_tx_topology_lists(session_id, cfg); + break; + + default: + ret = -ENODEV; + break; + } + } + return ret; +} + +int msm_snddev_set_enc(int popp_id, int copp_id, int set, + int rate, int mode) +{ + int topology; + int tbl_idx; + int rc = 0, i = 0; + mutex_lock(&routing_info.adm_mutex); + if (set) { + mutex_lock(&adm_tx_topology_tbl.lock); + tbl_idx = check_tx_copp_topology(popp_id); + if (tbl_idx == -ENOENT) + topology = DEFAULT_COPP_TOPOLOGY; + else { + topology = adm_tx_topology_tbl.topolog_id[tbl_idx]; + rate = 16000; + } + mutex_unlock(&adm_tx_topology_tbl.lock); + rc = adm_open(copp_id, ADM_PATH_LIVE_REC, rate, mode, topology); + if (rc < 0) { + pr_err("%s: adm open fail rc[%d]\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 1, + (unsigned int *)&copp_id, copp_id); + if (rc < 0) { + pr_err("%s: matrix map failed rc[%d]\n", __func__, rc); + adm_close(copp_id); + rc = -EINVAL; + goto fail_cmd; + } + msm_set_copp_id(popp_id, copp_id); +#ifdef CONFIG_MSM8X60_RTAC + rtac_add_adm_device(copp_id, popp_id); +#endif + + } else { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (routing_info.copp_list[popp_id][i] == copp_id) { + rc = adm_close(copp_id); + if (rc < 0) { + pr_err("%s: adm close fail copp[%d]" + "rc[%d]\n", + __func__, copp_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + msm_clear_copp_id(popp_id, copp_id); + break; + } + } + } +fail_cmd: + mutex_unlock(&routing_info.adm_mutex); + return rc; +} +EXPORT_SYMBOL(msm_snddev_set_enc); + +int msm_device_is_voice(int dev_id) +{ + if ((dev_id == routing_info.voice_rx_dev_id) + || (dev_id == routing_info.voice_tx_dev_id)) + return 0; + else + return -EINVAL; +} +EXPORT_SYMBOL(msm_device_is_voice); + +int msm_set_voc_route(struct msm_snddev_info *dev_info, + int stream_type, int dev_id) +{ + int rc = 0; + u64 session_mask = 0; + + mutex_lock(&session_lock); + switch (stream_type) { + case AUDIO_ROUTE_STREAM_VOICE_RX: + if (audio_dev_ctrl.voice_rx_dev) + audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFFFF; + + if (!(dev_info->capability & SNDDEV_CAP_RX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + audio_dev_ctrl.voice_rx_dev = dev_info; + if (audio_dev_ctrl.voice_rx_dev) { + session_mask = + ((u64)0x1) << (MAX_BIT_PER_CLIENT * \ + ((int)AUDDEV_CLNT_VOC-1)); + audio_dev_ctrl.voice_rx_dev->sessions |= + session_mask; + } + routing_info.voice_rx_dev_id = dev_id; + break; + case AUDIO_ROUTE_STREAM_VOICE_TX: + if (audio_dev_ctrl.voice_tx_dev) + audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFFFF; + + if (!(dev_info->capability & SNDDEV_CAP_TX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + + audio_dev_ctrl.voice_tx_dev = dev_info; + if (audio_dev_ctrl.voice_rx_dev) { + session_mask = + ((u64)0x1) << (MAX_BIT_PER_CLIENT * \ + ((int)AUDDEV_CLNT_VOC-1)); + audio_dev_ctrl.voice_tx_dev->sessions |= + session_mask; + } + routing_info.voice_tx_dev_id = dev_id; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&session_lock); + return rc; +} +EXPORT_SYMBOL(msm_set_voc_route); + +void msm_release_voc_thread(void) +{ + wake_up(&audio_dev_ctrl.wait); +} +EXPORT_SYMBOL(msm_release_voc_thread); + +int msm_snddev_get_enc_freq(session_id) +{ + return routing_info.enc_freq[session_id].freq; +} +EXPORT_SYMBOL(msm_snddev_get_enc_freq); + +int msm_get_voc_freq(int *tx_freq, int *rx_freq) +{ + *tx_freq = routing_info.voice_tx_sample_rate; + *rx_freq = routing_info.voice_rx_sample_rate; + return 0; +} +EXPORT_SYMBOL(msm_get_voc_freq); + +int msm_get_voc_route(u32 *rx_id, u32 *tx_id) +{ + int rc = 0; + + if (!rx_id || !tx_id) + return -EINVAL; + + mutex_lock(&session_lock); + if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) { + rc = -ENODEV; + mutex_unlock(&session_lock); + return rc; + } + + *rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id; + *tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id; + + mutex_unlock(&session_lock); + + return rc; +} +EXPORT_SYMBOL(msm_get_voc_route); + +struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id) +{ + struct msm_snddev_info *info; + + if ((audio_dev_ctrl.num_dev - 1) < dev_id) { + info = ERR_PTR(-ENODEV); + goto error; + } + + info = audio_dev_ctrl.devs[dev_id]; +error: + return info; + +} +EXPORT_SYMBOL(audio_dev_ctrl_find_dev); + +int snddev_voice_set_volume(int vol, int path) +{ + if (audio_dev_ctrl.voice_rx_dev + && audio_dev_ctrl.voice_tx_dev) { + if (path) + audio_dev_ctrl.voice_tx_dev->dev_volume = vol; + else + audio_dev_ctrl.voice_rx_dev->dev_volume = vol; + } else + return -ENODEV; + return 0; +} +EXPORT_SYMBOL(snddev_voice_set_volume); + +static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl, + void __user *arg) +{ + int rc = 0; + u32 index; + struct msm_snd_device_list work_list; + struct msm_snd_device_info *work_tbl; + + if (copy_from_user(&work_list, arg, sizeof(work_list))) { + rc = -EFAULT; + goto error; + } + + if (work_list.num_dev > dev_ctrl->num_dev) { + rc = -EINVAL; + goto error; + } + + work_tbl = kmalloc(work_list.num_dev * + sizeof(struct msm_snd_device_info), GFP_KERNEL); + if (!work_tbl) { + rc = -ENOMEM; + goto error; + } + + for (index = 0; index < dev_ctrl->num_dev; index++) { + work_tbl[index].dev_id = index; + work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability; + strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name, + 64); + } + + if (copy_to_user((void *) (work_list.list), work_tbl, + work_list.num_dev * sizeof(struct msm_snd_device_info))) + rc = -EFAULT; + kfree(work_tbl); +error: + return rc; +} + + +int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, + void (*listner)(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data), + void *private_data) +{ + int rc; + struct msm_snd_evt_listner *callback = NULL; + struct msm_snd_evt_listner *new_cb; + + new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL); + if (!new_cb) { + pr_err("No memory to add new listener node\n"); + return -ENOMEM; + } + + mutex_lock(&session_lock); + new_cb->cb_next = NULL; + new_cb->auddev_evt_listener = listner; + new_cb->evt_id = evt_id; + new_cb->clnt_type = clnt_type; + new_cb->clnt_id = clnt_id; + new_cb->private_data = private_data; + if (event.cb == NULL) { + event.cb = new_cb; + new_cb->cb_prev = NULL; + } else { + callback = event.cb; + for (; ;) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + callback->cb_next = new_cb; + new_cb->cb_prev = callback; + } + event.num_listner++; + mutex_unlock(&session_lock); + rc = 0; + return rc; +} +EXPORT_SYMBOL(auddev_register_evt_listner); + +int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id) +{ + struct msm_snd_evt_listner *callback = event.cb; + struct msm_snddev_info *info; + u64 session_mask = 0; + int i = 0; + + mutex_lock(&session_lock); + while (callback != NULL) { + if ((callback->clnt_type == clnt_type) + && (callback->clnt_id == clnt_id)) + break; + callback = callback->cb_next; + } + if (callback == NULL) { + mutex_unlock(&session_lock); + return -EINVAL; + } + + if ((callback->cb_next == NULL) && (callback->cb_prev == NULL)) + event.cb = NULL; + else if (callback->cb_next == NULL) + callback->cb_prev->cb_next = NULL; + else if (callback->cb_prev == NULL) { + callback->cb_next->cb_prev = NULL; + event.cb = callback->cb_next; + } else { + callback->cb_prev->cb_next = callback->cb_next; + callback->cb_next->cb_prev = callback->cb_prev; + } + kfree(callback); + + session_mask = (((u64)0x1) << clnt_id) << (MAX_BIT_PER_CLIENT * \ + ((int)clnt_type-1)); + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + info->sessions &= ~session_mask; + } + mutex_unlock(&session_lock); + return 0; +} +EXPORT_SYMBOL(auddev_unregister_evt_listner); + +int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type) +{ + int i = 0; + struct msm_snddev_info *info; + u64 session_mask = 0; + + if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_DEC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_ENC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + + session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \ + ((int)clnt_type-1)); + + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + if ((info->sessions & session_mask) + && (info->capability & capability)) { + if (!(info->sessions & ~(session_mask))) + info->set_sample_rate = 0; + } + } + if (clnt_type == AUDDEV_CLNT_DEC) + routing_info.dec_freq[session_id].freq + = 0; + else if (clnt_type == AUDDEV_CLNT_ENC) + routing_info.enc_freq[session_id].freq + = 0; + else if (capability == SNDDEV_CAP_TX) + routing_info.voice_tx_sample_rate = 0; + else + routing_info.voice_rx_sample_rate = 48000; + return 0; +} + +int msm_snddev_request_freq(int *freq, u32 session_id, + u32 capability, u32 clnt_type) +{ + int i = 0; + int rc = 0; + struct msm_snddev_info *info; + u32 set_freq; + u64 session_mask = 0; + u64 clnt_type_mask = 0; + + pr_debug(": clnt_type 0x%08x\n", clnt_type); + + if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_DEC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + if ((clnt_type == AUDDEV_CLNT_ENC) + && (session_id >= MAX_SESSIONS)) + return -EINVAL; + session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \ + ((int)clnt_type-1)); + clnt_type_mask = (0xFFFF << (MAX_BIT_PER_CLIENT * (clnt_type-1))); + if (!(*freq == 8000) && !(*freq == 11025) && + !(*freq == 12000) && !(*freq == 16000) && + !(*freq == 22050) && !(*freq == 24000) && + !(*freq == 32000) && !(*freq == 44100) && + !(*freq == 48000)) + return -EINVAL; + + for (i = 0; i < audio_dev_ctrl.num_dev; i++) { + info = audio_dev_ctrl.devs[i]; + if ((info->sessions & session_mask) + && (info->capability & capability)) { + rc = 0; + if ((info->sessions & ~clnt_type_mask) + && ((*freq != 8000) && (*freq != 16000) + && (*freq != 48000))) { + if (clnt_type == AUDDEV_CLNT_ENC) { + routing_info.enc_freq[session_id].freq + = 0; + return -EPERM; + } else if (clnt_type == AUDDEV_CLNT_DEC) { + routing_info.dec_freq[session_id].freq + = 0; + return -EPERM; + } + } + if (*freq == info->set_sample_rate) { + rc = info->set_sample_rate; + continue; + } + set_freq = MAX(*freq, info->set_sample_rate); + + + if (clnt_type == AUDDEV_CLNT_DEC) { + routing_info.dec_freq[session_id].evt = 1; + routing_info.dec_freq[session_id].freq + = set_freq; + } else if (clnt_type == AUDDEV_CLNT_ENC) { + routing_info.enc_freq[session_id].evt = 1; + routing_info.enc_freq[session_id].freq + = set_freq; + } else if (capability == SNDDEV_CAP_TX) + routing_info.voice_tx_sample_rate = set_freq; + + rc = set_freq; + info->set_sample_rate = set_freq; + *freq = info->set_sample_rate; + + if (info->opened) { + broadcast_event(AUDDEV_EVT_FREQ_CHG, i, + SESSION_IGNORE); + set_freq = info->dev_ops.set_freq(info, + set_freq); + broadcast_event(AUDDEV_EVT_DEV_RDY, i, + SESSION_IGNORE); + } + } + pr_debug("info->set_sample_rate = %d\n", info->set_sample_rate); + pr_debug("routing_info.enc_freq.freq = %d\n", + routing_info.enc_freq[session_id].freq); + } + return rc; +} +EXPORT_SYMBOL(msm_snddev_request_freq); + +int msm_snddev_enable_sidetone(u32 dev_id, u32 enable, uint16_t gain) +{ + int rc; + struct msm_snddev_info *dev_info; + + pr_debug("dev_id %d enable %d\n", dev_id, enable); + + dev_info = audio_dev_ctrl_find_dev(dev_id); + + if (IS_ERR(dev_info)) { + pr_err("bad dev_id %d\n", dev_id); + rc = -EINVAL; + } else if (!dev_info->dev_ops.enable_sidetone) { + pr_debug("dev %d no sidetone support\n", dev_id); + rc = -EPERM; + } else + rc = dev_info->dev_ops.enable_sidetone(dev_info, enable, gain); + + return rc; +} +EXPORT_SYMBOL(msm_snddev_enable_sidetone); + +int msm_enable_incall_recording(int popp_id, int rec_mode, int rate, + int channel_mode) +{ + int rc = 0; + unsigned int port_id[2]; + port_id[0] = VOICE_RECORD_TX; + port_id[1] = VOICE_RECORD_RX; + + pr_debug("%s: popp_id %d, rec_mode %d, rate %d, channel_mode %d\n", + __func__, popp_id, rec_mode, rate, channel_mode); + + mutex_lock(&routing_info.adm_mutex); + + if (rec_mode == VOC_REC_UPLINK) { + rc = afe_start_pseudo_port(port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in Tx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[0], ADM_PATH_LIVE_REC, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 1, + &port_id[0], port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in ADM matrix map %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[0]); + + } else if (rec_mode == VOC_REC_DOWNLINK) { + rc = afe_start_pseudo_port(port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in Rx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[1], ADM_PATH_LIVE_REC, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 1, + &port_id[1], port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in ADM matrix map %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[1]); + + } else if (rec_mode == VOC_REC_BOTH) { + rc = afe_start_pseudo_port(port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in Tx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[0], ADM_PATH_LIVE_REC, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[0]); + + rc = afe_start_pseudo_port(port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in Rx pseudo port start\n", + __func__, rc); + + goto fail_cmd; + } + + rc = adm_open(port_id[1], ADM_PATH_LIVE_REC, rate, channel_mode, + DEFAULT_COPP_TOPOLOGY); + if (rc < 0) { + pr_err("%s: Error %d in ADM open %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + rc = adm_matrix_map(popp_id, ADM_PATH_LIVE_REC, 2, + &port_id[0], port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in ADM matrix map\n", + __func__, rc); + + goto fail_cmd; + } + + msm_set_copp_id(popp_id, port_id[1]); + } else { + pr_err("%s Unknown rec_mode %d\n", __func__, rec_mode); + + goto fail_cmd; + } + + rc = voice_start_record(rec_mode, 1); + +fail_cmd: + mutex_unlock(&routing_info.adm_mutex); + return rc; +} + +int msm_disable_incall_recording(uint32_t popp_id, uint32_t rec_mode) +{ + int rc = 0; + uint32_t port_id[2]; + port_id[0] = VOICE_RECORD_TX; + port_id[1] = VOICE_RECORD_RX; + + pr_debug("%s: popp_id %d, rec_mode %d\n", __func__, popp_id, rec_mode); + + mutex_lock(&routing_info.adm_mutex); + + rc = voice_start_record(rec_mode, 0); + if (rc < 0) { + pr_err("%s: Error %d stopping record\n", __func__, rc); + + goto fail_cmd; + } + + if (rec_mode == VOC_REC_UPLINK) { + rc = adm_close(port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[0]); + + rc = afe_stop_pseudo_port(port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in Tx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + + } else if (rec_mode == VOC_REC_DOWNLINK) { + rc = adm_close(port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[1]); + + rc = afe_stop_pseudo_port(port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in Rx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + } else if (rec_mode == VOC_REC_BOTH) { + rc = adm_close(port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[0]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[0]); + + rc = afe_stop_pseudo_port(port_id[0]); + if (rc < 0) { + pr_err("%s: Error %d in Tx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + + rc = adm_close(port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in ADM close %d\n", + __func__, rc, port_id[1]); + + goto fail_cmd; + } + + msm_clear_copp_id(popp_id, port_id[1]); + + rc = afe_stop_pseudo_port(port_id[1]); + if (rc < 0) { + pr_err("%s: Error %d in Rx pseudo port stop\n", + __func__, rc); + goto fail_cmd; + } + } else { + pr_err("%s Unknown rec_mode %d\n", __func__, rec_mode); + + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&routing_info.adm_mutex); + return rc; +} + +static long audio_dev_ctrl_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc = 0; + struct audio_dev_ctrl_state *dev_ctrl = file->private_data; + + mutex_lock(&session_lock); + switch (cmd) { + case AUDIO_GET_NUM_SND_DEVICE: + rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg); + break; + case AUDIO_GET_SND_DEVICES: + rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg); + break; + case AUDIO_ENABLE_SND_DEVICE: { + struct msm_snddev_info *dev_info; + u32 dev_id; + + if (get_user(dev_id, (u32 __user *) arg)) { + rc = -EFAULT; + break; + } + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) + rc = PTR_ERR(dev_info); + else { + rc = dev_info->dev_ops.open(dev_info); + if (!rc) + dev_info->opened = 1; + wake_up(&audio_dev_ctrl.wait); + } + break; + + } + + case AUDIO_DISABLE_SND_DEVICE: { + struct msm_snddev_info *dev_info; + u32 dev_id; + + if (get_user(dev_id, (u32 __user *) arg)) { + rc = -EFAULT; + break; + } + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) + rc = PTR_ERR(dev_info); + else { + rc = dev_info->dev_ops.close(dev_info); + dev_info->opened = 0; + } + break; + } + + case AUDIO_ROUTE_STREAM: { + struct msm_audio_route_config route_cfg; + struct msm_snddev_info *dev_info; + + if (copy_from_user(&route_cfg, (void __user *) arg, + sizeof(struct msm_audio_route_config))) { + rc = -EFAULT; + break; + } + pr_debug("%s: route cfg %d %d type\n", __func__, + route_cfg.dev_id, route_cfg.stream_type); + dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id); + if (IS_ERR(dev_info)) { + pr_err("%s: pass invalid dev_id\n", __func__); + rc = PTR_ERR(dev_info); + break; + } + + switch (route_cfg.stream_type) { + + case AUDIO_ROUTE_STREAM_VOICE_RX: + if (!(dev_info->capability & SNDDEV_CAP_RX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + dev_ctrl->voice_rx_dev = dev_info; + break; + case AUDIO_ROUTE_STREAM_VOICE_TX: + if (!(dev_info->capability & SNDDEV_CAP_TX) | + !(dev_info->capability & SNDDEV_CAP_VOICE)) { + rc = -EINVAL; + break; + } + dev_ctrl->voice_tx_dev = dev_info; + break; + } + break; + } + + default: + rc = -EINVAL; + } + mutex_unlock(&session_lock); + return rc; +} + +static int audio_dev_ctrl_open(struct inode *inode, struct file *file) +{ + pr_debug("open audio_dev_ctrl\n"); + atomic_inc(&audio_dev_ctrl.opened); + file->private_data = &audio_dev_ctrl; + return 0; +} + +static int audio_dev_ctrl_release(struct inode *inode, struct file *file) +{ + pr_debug("release audio_dev_ctrl\n"); + atomic_dec(&audio_dev_ctrl.opened); + return 0; +} + +static const struct file_operations audio_dev_ctrl_fops = { + .owner = THIS_MODULE, + .open = audio_dev_ctrl_open, + .release = audio_dev_ctrl_release, + .unlocked_ioctl = audio_dev_ctrl_ioctl, +}; + + +struct miscdevice audio_dev_ctrl_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_dev_ctrl", + .fops = &audio_dev_ctrl_fops, +}; + +/* session id is 64 bit routing mask per device + * 0-15 for voice clients + * 16-31 for Decoder clients + * 32-47 for Encoder clients + * 48-63 Do not care + */ +void broadcast_event(u32 evt_id, u32 dev_id, u64 session_id) +{ + int clnt_id = 0, i; + union auddev_evt_data *evt_payload; + struct msm_snd_evt_listner *callback; + struct msm_snddev_info *dev_info = NULL; + u64 session_mask = 0; + static int pending_sent; + + pr_debug(": evt_id = %d\n", evt_id); + + if ((evt_id != AUDDEV_EVT_START_VOICE) + && (evt_id != AUDDEV_EVT_END_VOICE) + && (evt_id != AUDDEV_EVT_STREAM_VOL_CHG) + && (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) { + dev_info = audio_dev_ctrl_find_dev(dev_id); + if (IS_ERR(dev_info)) { + pr_err("%s: pass invalid dev_id(%d)\n", + __func__, dev_id); + return; + } + } + + if (event.cb != NULL) + callback = event.cb; + else + return; + mutex_lock(&session_lock); + + if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + routing_info.voice_state = dev_id; + + evt_payload = kzalloc(sizeof(union auddev_evt_data), + GFP_KERNEL); + + if (evt_payload == NULL) { + pr_err("broadcast_event: cannot allocate memory\n"); + mutex_unlock(&session_lock); + return; + } + for (; ;) { + if (!(evt_id & callback->evt_id)) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + clnt_id = callback->clnt_id; + memset(evt_payload, 0, sizeof(union auddev_evt_data)); + + if ((evt_id == AUDDEV_EVT_START_VOICE) + || (evt_id == AUDDEV_EVT_END_VOICE) + || evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) + goto skip_check; + if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) + goto aud_cal; + + session_mask = (((u64)0x1) << clnt_id) + << (MAX_BIT_PER_CLIENT * \ + ((int)callback->clnt_type-1)); + + if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \ + (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) { + pr_debug("AUDDEV_EVT_STREAM_VOL_CHG or\ + AUDDEV_EVT_VOICE_STATE_CHG\n"); + goto volume_strm; + } + + pr_debug("dev_info->sessions = %llu\n", dev_info->sessions); + + if ((!session_id && !(dev_info->sessions & session_mask)) || + (session_id && ((dev_info->sessions & session_mask) != + session_id))) { + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE) + goto voc_events; + +volume_strm: + if (callback->clnt_type == AUDDEV_CLNT_DEC) { + pr_debug("AUDDEV_CLNT_DEC\n"); + if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) { + pr_debug("clnt_id = %d, session_id = %llu\n", + clnt_id, session_id); + if (session_mask != session_id) + goto sent_dec; + else + evt_payload->session_vol = + msm_vol_ctl.volume; + } else if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.dec_freq[clnt_id].evt) { + routing_info.dec_freq[clnt_id].evt + = 0; + goto sent_dec; + } else if (routing_info.dec_freq[clnt_id].freq + == dev_info->set_sample_rate) + goto sent_dec; + else { + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else + evt_payload->routing_id = dev_info->copp_id; + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); +sent_dec: + if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) && + (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) + routing_info.dec_freq[clnt_id].freq + = dev_info->set_sample_rate; + + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + if (callback->clnt_type == AUDDEV_CLNT_ENC) { + pr_debug("AUDDEV_CLNT_ENC\n"); + if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.enc_freq[clnt_id].evt) { + routing_info.enc_freq[clnt_id].evt + = 0; + goto sent_enc; + } else { + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else + evt_payload->routing_id = dev_info->copp_id; + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); +sent_enc: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } +aud_cal: + if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) { + pr_debug("AUDDEV_CLNT_AUDIOCAL\n"); + if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else if (!dev_info->sessions) + goto sent_aud_cal; + else { + evt_payload->audcal_info.dev_id = + dev_info->copp_id; + evt_payload->audcal_info.acdb_id = + dev_info->acdb_id; + evt_payload->audcal_info.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->audcal_info.sample_rate = + dev_info->set_sample_rate ? + dev_info->set_sample_rate : + dev_info->sample_rate; + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); + +sent_aud_cal: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } +skip_check: +voc_events: + if (callback->clnt_type == AUDDEV_CLNT_VOC) { + pr_debug("AUDDEV_CLNT_VOC\n"); + if (evt_id == AUDDEV_EVT_DEV_RLS) { + if (!pending_sent) + goto sent_voc; + else + pending_sent = 0; + } + if (evt_id == AUDDEV_EVT_REL_PENDING) + pending_sent = 1; + + if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) { + evt_payload->voc_vm_info.voice_session_id = + session_id; + + if (dev_info->capability & SNDDEV_CAP_TX) { + evt_payload->voc_vm_info.dev_type = + SNDDEV_CAP_TX; + evt_payload->voc_vm_info.acdb_dev_id = + dev_info->acdb_id; + evt_payload-> + voc_vm_info.dev_vm_val.mute = + routing_info.tx_mute; + } else { + evt_payload->voc_vm_info.dev_type = + SNDDEV_CAP_RX; + evt_payload->voc_vm_info.acdb_dev_id = + dev_info->acdb_id; + evt_payload-> + voc_vm_info.dev_vm_val.vol = + routing_info.voice_rx_vol; + } + } else if ((evt_id == AUDDEV_EVT_START_VOICE) + || (evt_id == AUDDEV_EVT_END_VOICE)) { + memset(evt_payload, 0, + sizeof(union auddev_evt_data)); + + evt_payload->voice_session_id = session_id; + } else if (evt_id == AUDDEV_EVT_FREQ_CHG) { + if (routing_info.voice_tx_sample_rate + != dev_info->set_sample_rate) { + routing_info.voice_tx_sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.sample_rate + = dev_info->set_sample_rate; + evt_payload->freq_info.dev_type + = dev_info->capability; + evt_payload->freq_info.acdb_dev_id + = dev_info->acdb_id; + } else + goto sent_voc; + } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) + evt_payload->voice_state = + routing_info.voice_state; + else { + evt_payload->voc_devinfo.dev_type = + (dev_info->capability & SNDDEV_CAP_TX) ? + SNDDEV_CAP_TX : SNDDEV_CAP_RX; + evt_payload->voc_devinfo.acdb_dev_id = + dev_info->acdb_id; + evt_payload->voc_devinfo.dev_port_id = + dev_info->copp_id; + evt_payload->voc_devinfo.dev_sample = + dev_info->set_sample_rate ? + dev_info->set_sample_rate : + dev_info->sample_rate; + evt_payload->voc_devinfo.dev_id = dev_id; + if (dev_info->capability & SNDDEV_CAP_RX) { + for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; + i++) { + evt_payload-> + voc_devinfo.max_rx_vol[i] = + dev_info->max_voc_rx_vol[i]; + evt_payload + ->voc_devinfo.min_rx_vol[i] = + dev_info->min_voc_rx_vol[i]; + } + } + } + callback->auddev_evt_listener( + evt_id, + evt_payload, + callback->private_data); + if (evt_id == AUDDEV_EVT_DEV_RLS) + dev_info->sessions &= ~(0xFFFF); +sent_voc: + if (callback->cb_next == NULL) + break; + else { + callback = callback->cb_next; + continue; + } + } + } + kfree(evt_payload); + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(broadcast_event); + + +void mixer_post_event(u32 evt_id, u32 id) +{ + + pr_debug("evt_id = %d\n", evt_id); + switch (evt_id) { + case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */ + broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEV_RDY: + broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEV_RLS: + broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_REL_PENDING: + broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE); + break; + case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: + broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id, + SESSION_IGNORE); + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id, + SESSION_IGNORE); + break; + case AUDDEV_EVT_START_VOICE: + broadcast_event(AUDDEV_EVT_START_VOICE, + id, SESSION_IGNORE); + break; + case AUDDEV_EVT_END_VOICE: + broadcast_event(AUDDEV_EVT_END_VOICE, + id, SESSION_IGNORE); + break; + case AUDDEV_EVT_FREQ_CHG: + broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE); + break; + default: + break; + } +} +EXPORT_SYMBOL(mixer_post_event); + +static int __init audio_dev_ctrl_init(void) +{ + init_waitqueue_head(&audio_dev_ctrl.wait); + + event.cb = NULL; + msm_reset_device_work_queue = create_workqueue("reset_device"); + if (msm_reset_device_work_queue == NULL) + return -ENOMEM; + atomic_set(&audio_dev_ctrl.opened, 0); + audio_dev_ctrl.num_dev = 0; + audio_dev_ctrl.voice_tx_dev = NULL; + audio_dev_ctrl.voice_rx_dev = NULL; + routing_info.voice_state = VOICE_STATE_INVALID; + + mutex_init(&adm_tx_topology_tbl.lock); + mutex_init(&routing_info.copp_list_mutex); + mutex_init(&routing_info.adm_mutex); + + memset(routing_info.copp_list, COPP_IGNORE, + (sizeof(unsigned int) * MAX_SESSIONS * AFE_MAX_PORTS)); + return misc_register(&audio_dev_ctrl_misc); +} + +static void __exit audio_dev_ctrl_exit(void) +{ + destroy_workqueue(msm_reset_device_work_queue); +} +module_init(audio_dev_ctrl_init); +module_exit(audio_dev_ctrl_exit); + +MODULE_DESCRIPTION("MSM 8K Audio Device Control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_evrc.c b/arch/arm/mach-msm/qdsp6v2/audio_evrc.c new file mode 100644 index 00000000000..ec5162dfffa --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_evrc.c @@ -0,0 +1,170 @@ +/* evrc audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + + + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_evrc_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_evrc_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_EVRC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_evrc_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_evrc_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +static int __init audio_evrc_init(void) +{ + return misc_register(&audio_evrc_misc); +} + +device_initcall(audio_evrc_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c new file mode 100644 index 00000000000..0591a71600f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c @@ -0,0 +1,1485 @@ +/* low power audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_lpa.h" + +#include +#include +#include + +#include +#include + +#define MAX_BUF 4 +#define BUFSZ (524288) + +#define AUDDEC_DEC_PCM 0 + +#define AUDLPA_EVENT_NUM 10 /* Default number of pre-allocated event packets */ + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct audlpa_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; +struct audlpa_ion_region { + struct list_head list; + struct ion_handle *handle; + struct ion_client *client; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audlpa_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; +}; + +struct audlpa_dec { + char *name; + int dec_attrb; + long (*ioctl)(struct file *, unsigned int, unsigned long); + int (*set_params)(void *); +}; + +static void audlpa_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload); + +static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up); +static void audlpa_unmap_ion_region(struct audio *audio); +static void audlpa_async_send_data(struct audio *audio, unsigned needed, + uint32_t token); +static int audlpa_pause(struct audio *audio); +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static int audlpa_set_pcm_params(void *data); + +struct audlpa_dec audlpa_decs[] = { + {"msm_pcm_lp_dec", AUDDEC_DEC_PCM, &pcm_ioctl, + &audlpa_set_pcm_params}, +}; + +static void lpa_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->volume = evt_payload->session_vol; + pr_debug("%s: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, " + "enabled = %d\n", __func__, audio->volume, + audio->out_enabled); + if (audio->out_enabled == 1) { + if (audio->ac) { + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed" + " rc=%d\n", __func__, rc); + } + } + } + break; + default: + pr_err("%s:ERROR:wrong event\n", __func__); + break; + } +} + +static void audlpa_prevent_sleep(struct audio *audio) +{ + pr_debug("%s:\n", __func__); + wake_lock(&audio->wakelock); +} + +static void audlpa_allow_sleep(struct audio *audio) +{ + pr_debug("%s:\n", __func__); + wake_unlock(&audio->wakelock); +} + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + pr_debug("%s\n", __func__); + + return q6asm_run(audio->ac, 0, 0, 0); + +} + +static void audlpa_async_flush(struct audio *audio) +{ + struct audlpa_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + int rc = 0; + + pr_debug("%s:out_enabled = %d, drv_status = 0x%x\n", __func__, + audio->out_enabled, audio->drv_status); + if (audio->out_enabled) { + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audlpa_buffer_node, + list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE, + payload); + kfree(buf_node); + } + /* Implicitly issue a pause to the decoder before flushing if + it is not in pause state */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audlpa_pause(audio); + if (rc < 0) + pr_err("%s: pause cmd failed rc=%d\n", __func__, + rc); + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_err("%s: flush cmd failed rc=%d\n", __func__, rc); + + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + audio->out_needed = 0; + + if (audio->stopped == 0) { + rc = audio_enable(audio); + if (rc < 0) + pr_err("%s: audio enable failed\n", __func__); + else { + audio->out_enabled = 1; + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + wake_up(&audio->write_wait); + } +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + int rc = 0; + + pr_debug("%s:%d %d\n", __func__, audio->opened, audio->out_enabled); + + if (audio->opened) { + audio->out_enabled = 0; + audio->opened = 0; + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s: CLOSE cmd failed\n", __func__); + else + pr_debug("%s: rxed CLOSE resp\n", __func__); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + wake_up(&audio->write_wait); + audio->out_needed = 0; + } + return rc; +} +static int audlpa_pause(struct audio *audio) +{ + int rc = 0; + + pr_debug("%s, enabled = %d\n", __func__, + audio->out_enabled); + if (audio->out_enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s: pause cmd failed rc=%d\n", __func__, rc); + + } else + pr_err("%s: Driver not enabled\n", __func__); + return rc; +} + +/* ------------------- dsp --------------------- */ +static void audlpa_async_send_data(struct audio *audio, unsigned needed, + uint32_t token) +{ + unsigned long flags; + struct audio_client *ac; + int rc = 0; + + pr_debug("%s:\n", __func__); + spin_lock_irqsave(&audio->dsp_lock, flags); + + pr_debug("%s: needed = %d, out_needed = %d, token = 0x%x\n", + __func__, needed, audio->out_needed, token); + if (needed && !audio->wflush) { + audio->out_needed = 1; + if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) { + /* pop one node out of queue */ + union msm_audio_event_payload evt_payload; + struct audlpa_buffer_node *used_buf; + + used_buf = list_first_entry(&audio->out_queue, + struct audlpa_buffer_node, list); + if (token == used_buf->paddr) { + pr_debug("%s, Release: addr: %lx," + " token = 0x%x\n", __func__, + used_buf->paddr, token); + list_del(&used_buf->list); + evt_payload.aio_buf = used_buf->buf; + audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE, + evt_payload); + kfree(used_buf); + audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN; + } + } + } + pr_debug("%s: out_needed = %d, stopped = %d, drv_status = 0x%x\n", + __func__, audio->out_needed, audio->stopped, + audio->drv_status); + if (audio->out_needed && (audio->stopped == 0)) { + struct audlpa_buffer_node *next_buf; + struct audio_aio_write_param param; + if (!list_empty(&audio->out_queue)) { + pr_debug("%s: list not empty\n", __func__); + next_buf = list_first_entry(&audio->out_queue, + struct audlpa_buffer_node, list); + if (next_buf) { + pr_debug("%s: Send: addr: %lx\n", __func__, + next_buf->paddr); + ac = audio->ac; + param.paddr = next_buf->paddr; + param.len = next_buf->buf.data_len; + param.msw_ts = 0; + param.lsw_ts = 0; + /* No time stamp valid */ + param.flags = NO_TIMESTAMP; + param.uid = next_buf->paddr; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_err("%s:q6asm_async_write failed\n", + __func__); + audio->out_needed = 0; + audio->drv_status |= ADRV_STATUS_OBUF_GIVEN; + } + } else if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s: list is empty, reached EOS\n", __func__); + wake_up(&audio->write_wait); + } + } + + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +static int audlpa_events_pending(struct audio *audio) +{ + int empty; + + spin_lock(&audio->event_queue_lock); + empty = !list_empty(&audio->event_queue); + spin_unlock(&audio->event_queue_lock); + return empty || audio->event_abort; +} + +static void audlpa_reset_event_queue(struct audio *audio) +{ + struct audlpa_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock(&audio->event_queue_lock); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock(&audio->event_queue_lock); + + return; +} + +static long audlpa_process_event_req(struct audio *audio, void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audlpa_event *drv_evt = NULL; + int timeout; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int) usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + audio->event_wait, audlpa_events_pending(audio), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + audio->event_wait, audlpa_events_pending(audio)); + } + + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock(&audio->event_queue_lock); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audlpa_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else + rc = -1; + spin_unlock(&audio->event_queue_lock); + + if (drv_evt && (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE || + drv_evt->event_type == AUDIO_EVENT_READ_DONE)) { + pr_debug("%s: AUDIO_EVENT_WRITE_DONE completing\n", __func__); + mutex_lock(&audio->lock); + audlpa_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0); + mutex_unlock(&audio->lock); + } + if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audlpa_ion_check(struct audio *audio, + void *vaddr, unsigned long len) +{ + struct audlpa_ion_region *region_elt; + struct audlpa_ion_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_err("%s[%p]:region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + __func__, audio, vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} +static int audlpa_ion_add(struct audio *audio, + struct msm_audio_ion_info *info) +{ + ion_phys_addr_t paddr; + size_t len; + unsigned long kvaddr; + struct audlpa_ion_region *region; + int rc = -EINVAL; + struct ion_handle *handle; + struct ion_client *client; + unsigned long ionflag; + void *temp_ptr; + + pr_debug("%s[%p]:\n", __func__, audio); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + client = msm_ion_client_create(UINT_MAX, "Audio_LPA_Client"); + if (IS_ERR_OR_NULL(client)) { + pr_err("Unable to create ION client\n"); + goto client_error; + } + + handle = ion_import_fd(client, info->fd); + if (IS_ERR_OR_NULL(handle)) { + pr_err("%s: could not get handle of the given fd\n", __func__); + goto import_error; + } + + rc = ion_handle_get_flags(client, handle, &ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", __func__); + goto flag_error; + } + + temp_ptr = ion_map_kernel(client, handle, ionflag); + if (IS_ERR_OR_NULL(temp_ptr)) { + pr_err("%s: could not get virtual address\n", __func__); + goto map_error; + } + kvaddr = (unsigned long) temp_ptr; + + rc = ion_phys(client, handle, &paddr, &len); + if (rc) { + pr_err("%s: could not get physical address\n", __func__); + goto ion_error; + } + + rc = audlpa_ion_check(audio, info->vaddr, len); + if (rc < 0) { + pr_err("%s: audlpa_ion_check failed\n", __func__); + goto ion_error; + } + + region->client = client; + region->handle = handle; + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->ref_cnt = 0; + pr_debug("%s[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n", + __func__, audio, + region->paddr, region->vaddr, region->len, region->kvaddr); + list_add_tail(®ion->list, &audio->ion_region_queue); + + rc = q6asm_memory_map(audio->ac, (uint32_t)paddr, IN, (uint32_t)len, 1); + if (rc < 0) { + pr_err("%s[%p]: memory map failed\n", __func__, audio); + goto ion_error; + } else { + goto end; + } + +ion_error: + ion_unmap_kernel(client, handle); +map_error: + ion_free(client, handle); +flag_error: +import_error: + ion_client_destroy(client); +client_error: + kfree(region); +end: + return rc; +} + +static int audlpa_ion_remove(struct audio *audio, + struct msm_audio_ion_info *info) +{ + struct audlpa_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audlpa_ion_region, list); + + if (region != NULL && (region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("%s[%p]:region %p in use ref_cnt %d\n", + __func__, audio, region, + region->ref_cnt); + break; + } + rc = q6asm_memory_unmap(audio->ac, + (uint32_t) region->paddr, IN); + if (rc < 0) + pr_err("%s[%p]: memory unmap failed\n", + __func__, audio); + + list_del(®ion->list); + ion_unmap_kernel(region->client, region->handle); + ion_free(region->client, region->handle); + ion_client_destroy(region->client); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audlpa_ion_lookup_vaddr(struct audio *audio, void *addr, + unsigned long len, struct audlpa_ion_region **region) +{ + struct audlpa_ion_region *region_elt; + int match_count = 0; + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * ion buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n", + __func__, audio, addr, len); + list_for_each_entry(region_elt, &audio->ion_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_err("\t%s[%p]:%p, %ld --> %p\n", + __func__, audio, + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + return *region ? 0 : -1; +} +static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr, + unsigned long len, int ref_up) +{ + struct audlpa_ion_region *region; + unsigned long paddr; + int ret; + + ret = audlpa_ion_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_err("%s[%p]:lookup (%p, %ld) failed\n", + __func__, audio, addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + paddr = region->paddr + (addr - region->vaddr); + return paddr; +} + +/* audio -> lock must be held at this point */ +static int audlpa_aio_buf_add(struct audio *audio, unsigned dir, + void __user *arg) +{ + struct audlpa_buffer_node *buf_node; + + buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + buf_node->paddr = audlpa_ion_fixup( + audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.data_len & 0x1)) { + kfree(buf_node); + return -EINVAL; + } + list_add_tail(&buf_node->list, &audio->out_queue); + pr_debug("%s, Added to list: addr: %lx, length = %d\n", + __func__, buf_node->paddr, buf_node->buf.data_len); + audlpa_async_send_data(audio, 0, 0); + } else { + /* read */ + kfree(buf_node); + } + return 0; +} + +static int config(struct audio *audio) +{ + int rc = 0; + if (!audio->out_prefill) { + if (audio->codec_ops.set_params != NULL) { + rc = audio->codec_ops.set_params(audio); + audio->out_prefill = 1; + } + } + return rc; +} + +void q6_audlpa_out_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct audio *audio = (struct audio *) priv; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + pr_debug("%s: ASM_DATA_EVENT_WRITE_DONE, token = 0x%x\n", + __func__, token); + audlpa_async_send_data(audio, 1, token); + break; + case ASM_DATA_EVENT_EOS: + case ASM_DATA_CMDRSP_EOS: + pr_debug("%s: ASM_DATA_CMDRSP_EOS, teos = %d\n", __func__, + audio->teos); + if (audio->teos == 0) { + audio->teos = 1; + wake_up(&audio->write_wait); + } + break; + case ASM_SESSION_CMDRSP_GET_SESSION_TIME: + break; + case RESET_EVENTS: + reset_device(); + break; + default: + break; + } +} + +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + pr_debug("%s: cmd = %d\n", __func__, cmd); + return -EINVAL; +} + +static int audlpa_set_pcm_params(void *data) +{ + struct audio *audio = (struct audio *)data; + int rc; + + rc = q6asm_media_format_block_pcm(audio->ac, audio->out_sample_rate, + audio->out_channel_mode); + if (rc < 0) + pr_err("%s: Format block pcm failed\n", __func__); + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + uint64_t timestamp; + uint64_t temp; + + pr_debug("%s: audio_ioctl() cmd = %d\n", __func__, cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + pr_debug("%s: AUDIO_GET_STATS cmd\n", __func__); + memset(&stats, 0, sizeof(stats)); + timestamp = q6asm_get_session_time(audio->ac); + if (timestamp < 0) { + pr_err("%s: Get Session Time return value =%lld\n", + __func__, timestamp); + return -EAGAIN; + } + temp = (timestamp * 2 * audio->out_channel_mode); + temp = temp * (audio->out_sample_rate/1000); + temp = div_u64(temp, 1000); + audio->bytes_consumed = (uint32_t)(temp & 0xFFFFFFFF); + stats.byte_count = audio->bytes_consumed; + stats.unused[0] = (uint32_t)((temp >> 32) & 0xFFFFFFFF); + pr_debug("%s: bytes_consumed:lsb = %d, msb = %d," + "timestamp = %lld\n", __func__, + audio->bytes_consumed, stats.unused[0], timestamp); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + break; + + case AUDIO_SET_VOLUME: + break; + + case AUDIO_SET_PAN: + break; + + case AUDIO_SET_EQ: + break; + } + + if (cmd == AUDIO_GET_EVENT) { + pr_debug("%s: AUDIO_GET_EVENT\n", __func__); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audlpa_process_event_req(audio, + (void __user *) arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + return rc; + } + + if (cmd == AUDIO_ABORT_GET_EVENT) { + audio->event_abort = 1; + wake_up(&audio->event_wait); + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + pr_info("%s: AUDIO_START: Session %d\n", __func__, + audio->ac->session); + if (!audio->opened) { + pr_err("%s: Driver not opened\n", __func__); + rc = -EFAULT; + goto fail; + } + rc = config(audio); + if (rc) { + pr_err("%s: Out Configuration failed\n", __func__); + rc = -EFAULT; + goto fail; + } + + rc = audio_enable(audio); + if (rc) { + pr_err("%s: audio enable failed\n", __func__); + rc = -EFAULT; + goto fail; + } else { + struct asm_softpause_params softpause = { + .enable = SOFT_PAUSE_ENABLE, + .period = SOFT_PAUSE_PERIOD, + .step = SOFT_PAUSE_STEP, + .rampingcurve = SOFT_PAUSE_CURVE_LINEAR, + }; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR) + softpause.step = SOFT_PAUSE_STEP_LINEAR; + if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR) + softvol.step = SOFT_VOLUME_STEP_LINEAR; + audio->out_enabled = 1; + audio->out_needed = 1; + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + rc = q6asm_set_softpause(audio->ac, &softpause); + if (rc < 0) + pr_err("%s: Send SoftPause Param failed rc=%d\n", + __func__, rc); + rc = q6asm_set_softvolume(audio->ac, &softvol); + if (rc < 0) + pr_err("%s: Send SoftVolume Param failed rc=%d\n", + __func__, rc); + rc = q6asm_set_lrgain(audio->ac, 0x2000, 0x2000); + if (rc < 0) + pr_err("%s: Send channel gain failed rc=%d\n", + __func__, rc); + /* disable mute by default */ + rc = q6asm_set_mute(audio->ac, 0); + if (rc < 0) + pr_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + if (!list_empty(&audio->out_queue)) + pr_err("%s: write_list is not empty!!!\n", + __func__); + if (audio->stopped == 1) + audio->stopped = 0; + audlpa_prevent_sleep(audio); + } + break; + + case AUDIO_STOP: + pr_info("%s: AUDIO_STOP: session_id:%d\n", __func__, + audio->ac->session); + audio->stopped = 1; + audlpa_async_flush(audio); + audio->out_enabled = 0; + audio->out_needed = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audlpa_allow_sleep(audio); + break; + + case AUDIO_FLUSH: + pr_debug("%s: AUDIO_FLUSH: session_id:%d\n", __func__, + audio->ac->session); + audio->wflush = 1; + if (audio->out_enabled) + audlpa_async_flush(audio); + else + audio->wflush = 0; + audio->wflush = 0; + break; + + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + pr_debug("%s: AUDIO_SET_CONFIG\n", __func__); + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + pr_err("%s: ERROR: copy from user\n", __func__); + break; + } + if (!((config.channel_count == 1) || + (config.channel_count == 2))) { + rc = -EINVAL; + pr_err("%s: ERROR: config.channel_count == %d\n", + __func__, config.channel_count); + break; + } + + if (!((config.bits == 8) || (config.bits == 16) || + (config.bits == 24))) { + rc = -EINVAL; + pr_err("%s: ERROR: config.bits = %d\n", __func__, + config.bits); + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + audio->out_bits = config.bits; + audio->buffer_count = config.buffer_count; + audio->buffer_size = config.buffer_size; + rc = 0; + break; + } + + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_count = audio->buffer_count; + config.buffer_size = audio->buffer_size; + config.sample_rate = audio->out_sample_rate; + config.channel_count = audio->out_channel_mode; + config.bits = audio->out_bits; + + config.meta_field = 0; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + + case AUDIO_PAUSE: + pr_debug("%s: AUDIO_PAUSE %ld\n", __func__, arg); + if (arg == 1) { + rc = audlpa_pause(audio); + if (rc < 0) + pr_err("%s: pause FAILED rc=%d\n", __func__, + rc); + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audio_enable(audio); + if (rc) + pr_err("%s: audio enable failed\n", + __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->out_enabled = 1; + } + } + } + break; + + case AUDIO_REGISTER_ION: { + struct msm_audio_ion_info info; + pr_debug("%s: AUDIO_REGISTER_ION\n", __func__); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_ion_add(audio, &info); + break; + } + + case AUDIO_DEREGISTER_ION: { + struct msm_audio_ion_info info; + pr_debug("%s: AUDIO_DEREGISTER_ION\n", __func__); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audlpa_ion_remove(audio, &info); + break; + } + + case AUDIO_ASYNC_WRITE: + pr_debug("%s: AUDIO_ASYNC_WRITE\n", __func__); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else + rc = audlpa_aio_buf_add(audio, 1, (void __user *) arg); + break; + + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(unsigned short))) + return -EFAULT; + rc = 0; + break; + + default: + rc = audio->codec_ops.ioctl(file, cmd, arg); + } +fail: + mutex_unlock(&audio->lock); + return rc; +} + +/* Only useful in tunnel-mode */ +int audlpa_async_fsync(struct audio *audio) +{ + int rc = 0; + + pr_info("%s:Session %d\n", __func__, audio->ac->session); + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + mutex_lock(&audio->write_lock); + audio->teos = 0; + + rc = wait_event_interruptible(audio->write_wait, + ((list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped)); + + if (audio->wflush || audio->stopped) + goto flush_event; + + if (rc < 0) { + pr_err("%s: wait event for list_empty failed, rc = %d\n", + __func__, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + + if (rc < 0) { + pr_err("%s: q6asm_cmd failed, rc = %d", __func__, rc); + goto done; + } + rc = wait_event_interruptible_timeout(audio->write_wait, + (audio->teos || audio->wflush || + audio->stopped), 5*HZ); + + if (rc < 0) { + pr_err("%s: wait event for teos failed, rc = %d\n", __func__, + rc); + goto done; + } + + if (audio->teos == 1) { + rc = audio_enable(audio); + if (rc) + pr_err("%s: audio enable failed\n", __func__); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->out_enabled = 1; + audio->out_needed = 1; + } + } + +flush_event: + if (audio->stopped || audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +int audlpa_fsync(struct file *file, loff_t ppos1, loff_t ppos2, int datasync) +{ + struct audio *audio = file->private_data; + + return audlpa_async_fsync(audio); +} + +void audlpa_reset_ion_region(struct audio *audio) +{ + struct audlpa_ion_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audlpa_ion_region, list); + list_del(®ion->list); + ion_unmap_kernel(region->client, region->handle); + ion_free(region->client, region->handle); + ion_client_destroy(region->client); + kfree(region); + } + + return; +} + +static void audlpa_unmap_ion_region(struct audio *audio) +{ + struct audlpa_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%p]:\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audlpa_ion_region, list); + pr_debug("%s[%p]: phy_address = 0x%lx\n", + __func__, audio, region->paddr); + if (region != NULL) { + rc = q6asm_memory_unmap(audio->ac, + (uint32_t)region->paddr, IN); + if (rc < 0) + pr_err("%s: memory unmap failed\n", __func__); + } + } +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + pr_info("%s: audio instance 0x%08x freeing, session %d\n", __func__, + (int)audio, audio->ac->session); + + mutex_lock(&audio->lock); + audio->wflush = 1; + if (audio->out_enabled) + audlpa_async_flush(audio); + audio->wflush = 0; + audlpa_unmap_ion_region(audio); + audio_disable(audio); + msm_clear_session_id(audio->ac->session); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session); + q6asm_audio_client_free(audio->ac); + audlpa_reset_ion_region(audio); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&audio->suspend_ctl.node); +#endif + audio->opened = 0; + audio->out_enabled = 0; + audio->out_prefill = 0; + audio->event_abort = 1; + wake_up(&audio->event_wait); + audlpa_reset_event_queue(audio); + if (audio->stopped == 0) + audlpa_allow_sleep(audio); + wake_lock_destroy(&audio->wakelock); + + mutex_unlock(&audio->lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio); + return 0; +} + +static void audlpa_post_event(struct audio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audlpa_event *e_node = NULL; + + spin_lock(&audio->event_queue_lock); + + pr_debug("%s:\n", __func__); + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audlpa_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audlpa_event), GFP_ATOMIC); + if (!e_node) { + pr_err("%s: No mem to post event %d\n", __func__, type); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock(&audio->event_queue_lock); + wake_up(&audio->event_wait); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void audlpa_suspend(struct early_suspend *h) +{ + struct audlpa_suspend_ctl *ctl = + container_of(h, struct audlpa_suspend_ctl, node); + union msm_audio_event_payload payload; + + pr_debug("%s:\n", __func__); + audlpa_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload); +} + +static void audlpa_resume(struct early_suspend *h) +{ + struct audlpa_suspend_ctl *ctl = + container_of(h, struct audlpa_suspend_ctl, node); + union msm_audio_event_payload payload; + + pr_debug("%s:\n", __func__); + audlpa_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload); +} +#endif + +#ifdef CONFIG_DEBUG_FS +static ssize_t audlpa_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t audlpa_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_enabled %d\n", audio->out_enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "volume %x\n", audio->volume); + n += scnprintf(buffer + n, debug_bufmax - n, + "sample rate %d\n", + audio->out_sample_rate); + n += scnprintf(buffer + n, debug_bufmax - n, + "channel mode %d\n", + audio->out_channel_mode); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "running %d\n", audio->running); + n += scnprintf(buffer + n, debug_bufmax - n, + "out_needed %d\n", audio->out_needed); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static const struct file_operations audlpa_debug_fops = { + .read = audlpa_debug_read, + .open = audlpa_debug_open, +}; +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = NULL; + int rc, i, dec_attrb = 0; + struct audlpa_event *e_node = NULL; +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_lpa_" + 5]; +#endif + char wake_lock_name[24]; + + /* Allocate audio instance, set to zero */ + audio = kzalloc(sizeof(struct audio), GFP_KERNEL); + if (!audio) { + pr_err("%s: no memory to allocate audio instance\n", __func__); + rc = -ENOMEM; + goto done; + } + + if ((file->f_mode & FMODE_WRITE) && !(file->f_mode & FMODE_READ)) { + pr_debug("%s: Tunnel Mode playback\n", __func__); + } else { + kfree(audio); + rc = -EACCES; + goto done; + } + + /* Allocate the decoder based on inode minor number*/ + audio->minor_no = iminor(inode); + dec_attrb |= audlpa_decs[audio->minor_no].dec_attrb; + audio->codec_ops.ioctl = audlpa_decs[audio->minor_no].ioctl; + audio->codec_ops.set_params = audlpa_decs[audio->minor_no].set_params; + audio->buffer_size = BUFSZ; + audio->buffer_count = MAX_BUF; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6_audlpa_out_cb, + (void *)audio); + if (!audio->ac) { + pr_err("%s: Could not allocate memory for lpa client\n", + __func__); + rc = -ENOMEM; + goto err; + } + rc = q6asm_open_write(audio->ac, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s: lpa out open failed\n", __func__); + goto err; + } + + pr_debug("%s: Set mode to AIO session[%d]\n", + __func__, + audio->ac->session); + rc = q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + if (rc < 0) + pr_err("%s: Set IO mode failed\n", __func__); + + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->write_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->ion_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + init_waitqueue_head(&audio->wait); + init_waitqueue_head(&audio->event_wait); + spin_lock_init(&audio->event_queue_lock); + snprintf(wake_lock_name, sizeof wake_lock_name, "audio_lpa_%x", + audio->ac->session); + wake_lock_init(&audio->wakelock, WAKE_LOCK_SUSPEND, wake_lock_name); + + audio->out_sample_rate = 44100; + audio->out_channel_mode = 2; + audio->out_bits = 16; + audio->volume = 0x2000; + + file->private_data = audio; + audio->opened = 1; + audio->out_enabled = 0; + audio->out_prefill = 0; + audio->bytes_consumed = 0; + + audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->ac->session, + lpa_listner, + (void *)audio); + if (rc) { + pr_err("%s: failed to register listner\n", __func__); + goto err; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_lpa_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *) audio, &audlpa_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_err("%s: debugfs_create_file failed\n", __func__); +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + audio->suspend_ctl.node.resume = audlpa_resume; + audio->suspend_ctl.node.suspend = audlpa_suspend; + audio->suspend_ctl.audio = audio; + register_early_suspend(&audio->suspend_ctl.node); +#endif + for (i = 0; i < AUDLPA_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audlpa_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + pr_err("%s: event pkt alloc failed\n", __func__); + break; + } + } + pr_info("%s: audio instance 0x%08x created session[%d]\n", __func__, + (int)audio, + audio->ac->session); +done: + return rc; +err: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_lpa_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audlpa_fsync, +}; + +static dev_t audlpa_devno; +static struct class *audlpa_class; +struct audlpa_device { + const char *name; + struct device *device; + struct cdev cdev; +}; + +static struct audlpa_device *audlpa_devices; + +static void audlpa_create(struct audlpa_device *adev, const char *name, + struct device *parent, dev_t devt) +{ + struct device *dev; + int rc; + + dev = device_create(audlpa_class, parent, devt, "%s", name); + if (IS_ERR(dev)) + return; + + cdev_init(&adev->cdev, &audio_lpa_fops); + adev->cdev.owner = THIS_MODULE; + + rc = cdev_add(&adev->cdev, devt, 1); + if (rc < 0) { + device_destroy(audlpa_class, devt); + } else { + adev->device = dev; + adev->name = name; + } +} + +static int __init audio_init(void) +{ + int rc; + int n = ARRAY_SIZE(audlpa_decs); + + audlpa_devices = kzalloc(sizeof(struct audlpa_device) * n, GFP_KERNEL); + if (!audlpa_devices) + return -ENOMEM; + + audlpa_class = class_create(THIS_MODULE, "audlpa"); + if (IS_ERR(audlpa_class)) + goto fail_create_class; + + rc = alloc_chrdev_region(&audlpa_devno, 0, n, "msm_audio_lpa"); + if (rc < 0) + goto fail_alloc_region; + + for (n = 0; n < ARRAY_SIZE(audlpa_decs); n++) { + audlpa_create(audlpa_devices + n, + audlpa_decs[n].name, NULL, + MKDEV(MAJOR(audlpa_devno), n)); + } + + return 0; + +fail_alloc_region: + class_unregister(audlpa_class); + return rc; +fail_create_class: + kfree(audlpa_devices); + return -ENOMEM; +} + +static void __exit audio_exit(void) +{ + class_unregister(audlpa_class); + kfree(audlpa_devices); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_DESCRIPTION("MSM LPA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.h b/arch/arm/mach-msm/qdsp6v2/audio_lpa.h new file mode 100644 index 00000000000..34b53f2ee3d --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef AUDIO_LPA_H +#define AUDIO_LPA_H + +#include +#include + +#define ADRV_STATUS_OBUF_GIVEN 0x00000001 +#define ADRV_STATUS_IBUF_GIVEN 0x00000002 +#define ADRV_STATUS_FSYNC 0x00000004 +#define ADRV_STATUS_PAUSE 0x00000008 + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +struct audlpa_suspend_ctl { + struct early_suspend node; + struct audio *audio; +}; +#endif + +struct codec_operations { + long (*ioctl)(struct file *, unsigned int, unsigned long); + int (*set_params)(void *); +}; + +struct audio { + spinlock_t dsp_lock; + + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + struct list_head out_queue; /* queue to retain output buffers */ + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + struct audio_client *ac; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_bits; /* bits per sample (used by PCM decoder) */ + + int32_t phys; /* physical address of write buffer */ + + uint32_t drv_status; + int wflush; /* Write flush */ + int opened; + int out_enabled; + int out_prefill; + int running; + int stopped; /* set when stopped, cleared on flush */ + int buf_refresh; + int teos; /* valid only if tunnel mode & no data left for decoder */ + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct audlpa_suspend_ctl suspend_ctl; +#endif + + struct wake_lock wakelock; +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + + wait_queue_head_t wait; + struct list_head free_event_queue; + struct list_head event_queue; + wait_queue_head_t event_wait; + spinlock_t event_queue_lock; + struct mutex get_event_lock; + int event_abort; + + uint32_t device_events; + + struct list_head ion_region_queue; /* protected by lock */ + + int eq_enable; + int eq_needs_commit; + uint32_t volume; + + unsigned int minor_no; + struct codec_operations codec_ops; + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t bytes_consumed; +}; + +#endif /* !AUDIO_LPA_H */ diff --git a/arch/arm/mach-msm/qdsp6v2/audio_mp3.c b/arch/arm/mach-msm/qdsp6v2/audio_mp3.c new file mode 100644 index 00000000000..93a87395861 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_mp3.c @@ -0,0 +1,165 @@ +/* mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_mp3_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_mp3_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for mp3 decode driver\n"); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MP3); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open MP3 decoder, expected frames is always 1 + audio->buf_cfg.frames_per_buf = 0x01;*/ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MP3); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_mp3_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_mp3_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +static int __init audio_mp3_init(void) +{ + return misc_register(&audio_mp3_misc); +} + +device_initcall(audio_mp3_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c new file mode 100644 index 00000000000..92530561e56 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c @@ -0,0 +1,304 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 + + +/* Default number of pre-allocated event packets */ +#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out)) + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + 0, /*native sampling rate*/ + 0 /*native channel count*/); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + aac_cfg.ch_cfg = aac_config->channel_configuration; + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + if (!cpu_is_msm8x60()) { + rc = q6asm_set_encdec_chan_map(audio->ac, 2); + if (rc < 0) { + pr_err("%s: cmd set encdec_chan_map failed\n", + __func__); + break; + } + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + } else { + uint16_t sce_left = 1, sce_right = 2; + aac_config = audio->codec_cfg; + if ((aac_config->dual_mono_mode < + AUDIO_AAC_DUAL_MONO_PL_PR) || + (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR)) { + pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid" + "dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify" + "dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s: asm cmd dualmono failed" + " rc=%d\n", __func__, rc); + } break; + } + break; + } + default: + pr_debug("Calling utils ioctl\n"); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_multi_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s: Could not allocate memory for aac" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + + aac_config = audio->codec_cfg; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + audio->buf_cfg.frames_per_buf = 0x01;*/ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_multi_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n", + __func__, audio->feedback, audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_multiaac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_multi_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_aac_init(void) +{ + return misc_register(&audio_multiaac_misc); +} + +device_initcall(audio_aac_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_mvs.c b/arch/arm/mach-msm/qdsp6v2/audio_mvs.c new file mode 100644 index 00000000000..d748304634d --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_mvs.c @@ -0,0 +1,1170 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Each buffer is 20 ms, queue holds 200 ms of data. */ +#define MVS_MAX_Q_LEN 10 + +/* Length of the DSP frame info header added to the voc packet. */ +#define DSP_FRAME_HDR_LEN 1 + +enum audio_mvs_state_type { + AUDIO_MVS_CLOSED, + AUDIO_MVS_STARTED, + AUDIO_MVS_STOPPED +}; + +struct audio_mvs_buf_node { + struct list_head list; + struct q6_msm_audio_mvs_frame frame; +}; + +struct audio_mvs_info_type { + enum audio_mvs_state_type state; + + uint32_t mvs_mode; + uint32_t rate_type; + uint32_t dtx_mode; + struct q_min_max_rate min_max_rate; + + struct list_head in_queue; + struct list_head free_in_queue; + + struct list_head out_queue; + struct list_head free_out_queue; + + wait_queue_head_t in_wait; + wait_queue_head_t out_wait; + + struct mutex lock; + struct mutex in_lock; + struct mutex out_lock; + + spinlock_t dsp_lock; + + struct wake_lock suspend_lock; + struct pm_qos_request pm_qos_req; + + void *memory_chunk; +}; + +static struct audio_mvs_info_type audio_mvs_info; + +static uint32_t audio_mvs_get_rate(uint32_t mvs_mode, uint32_t rate_type) +{ + uint32_t cvs_rate; + + if (mvs_mode == MVS_MODE_AMR_WB) + cvs_rate = rate_type - MVS_AMR_MODE_0660; + else + cvs_rate = rate_type; + + pr_debug("%s: CVS rate is %d for MVS mode %d\n", + __func__, cvs_rate, mvs_mode); + + return cvs_rate; +} + +static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt, + uint32_t pkt_len, + void *private_data) +{ + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = private_data; + unsigned long dsp_flags; + + /* Copy up-link packet into out_queue. */ + spin_lock_irqsave(&audio->dsp_lock, dsp_flags); + + if (!list_empty(&audio->free_out_queue)) { + buf_node = list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + switch (audio->mvs_mode) { + case MVS_MODE_AMR: + case MVS_MODE_AMR_WB: { + /* Remove the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + buf_node->frame.header.frame_type = + ((*voc_pkt) & 0xF0) >> 4; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + break; + } + + case MVS_MODE_IS127: { + buf_node->frame.header.packet_rate = (*voc_pkt) & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + break; + } + + case MVS_MODE_G729A: { + /* G729 frames are 10ms each, but the DSP works with + * 20ms frames and sends two 10ms frames per buffer. + * Extract the two frames and put them in separate + * buffers. + */ + /* Remove the first DSP frame info header. + * Header format: + * Bits 0-1: Frame type + */ + buf_node->frame.header.frame_type = (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length of the + * first frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->out_queue); + + /* Get another buffer from the free Q and fill in the + * second frame. + */ + if (!list_empty(&audio->free_out_queue)) { + buf_node = + list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Remove the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + */ + buf_node->frame.header.frame_type = + (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length + * of the first frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->out_queue); + + } else { + /* Drop the second frame. */ + pr_err("%s: UL data dropped, read is slow\n", + __func__); + } + + break; + } + + case MVS_MODE_G711: + case MVS_MODE_G711A: { + /* G711 frames are 10ms each, but the DSP works with + * 20ms frames and sends two 10ms frames per buffer. + * Extract the two frames and put them in separate + * buffers. + */ + /* Remove the first DSP frame info header. + * Header format: G711A + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + * + * Header format: G711 + * Bits 2-3: Frame rate + */ + if (audio->mvs_mode == MVS_MODE_G711A) + buf_node->frame.header.frame_type = + (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length of the + * first frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->out_queue); + + /* Get another buffer from the free Q and fill in the + * second frame. + */ + if (!list_empty(&audio->free_out_queue)) { + buf_node = + list_first_entry(&audio->free_out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Remove the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + if (audio->mvs_mode == MVS_MODE_G711A) + buf_node->frame.header.frame_type = + (*voc_pkt) & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length + * of the second frame: + */ + buf_node->frame.len = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->out_queue); + } else { + /* Drop the second frame. */ + pr_err("%s: UL data dropped, read is slow\n", + __func__); + } + break; + } + + case MVS_MODE_IS733: + case MVS_MODE_4GV_NB: + case MVS_MODE_4GV_WB: { + /* Remove the DSP frame info header. + * Header format: + * Bits 0-3: frame rate + */ + buf_node->frame.header.packet_rate = (*voc_pkt) & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + break; + } + + case MVS_MODE_EFR: + case MVS_MODE_FR: + case MVS_MODE_HR: { + /* + * Remove the DSP frame info header + * Header Format + * Bit 0: bfi unused for uplink + * Bit 1-2: sid applies to both uplink and downlink + * Bit 3: taf unused for uplink + * MVS_MODE_HR + * Bit 4: ufi unused for uplink + */ + buf_node->frame.header.gsm_frame_type.sid = + ((*voc_pkt) & 0x06) >> 1; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + buf_node->frame.len = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + break; + } + + default: { + buf_node->frame.header.frame_type = 0; + + buf_node->frame.len = pkt_len; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->out_queue); + } + } + } else { + pr_err("%s: UL data dropped, read is slow\n", __func__); + } + + spin_unlock_irqrestore(&audio->dsp_lock, dsp_flags); + + wake_up(&audio->out_wait); +} + +static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt, + uint32_t *pkt_len, + void *private_data) +{ + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = private_data; + unsigned long dsp_flags; + + spin_lock_irqsave(&audio->dsp_lock, dsp_flags); + + if (!list_empty(&audio->in_queue)) { + uint32_t rate_type = audio_mvs_get_rate(audio->mvs_mode, + audio->rate_type); + + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + switch (audio->mvs_mode) { + case MVS_MODE_AMR: + case MVS_MODE_AMR_WB: { + /* Add the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + *voc_pkt = + ((buf_node->frame.header.frame_type & 0x0F) << 4) | + (rate_type & 0x0F); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + break; + } + + case MVS_MODE_IS127: { + /* Add the DSP frame info header. Header format: + * Bits 0-3: Frame rate + */ + *voc_pkt = buf_node->frame.header.packet_rate & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + break; + } + + case MVS_MODE_G729A: { + /* G729 frames are 10ms each but the DSP expects 20ms + * worth of data, so send two 10ms frames per buffer. + */ + /* Add the first DSP frame info header. Header format: + * Bits 0-1: Frame type + */ + *voc_pkt = buf_node->frame.header.frame_type & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->free_in_queue); + + if (!list_empty(&audio->in_queue)) { + /* Get the second buffer. */ + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Add the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + */ + *voc_pkt = buf_node->frame.header.frame_type + & 0x03; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = *pkt_len + + buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->free_in_queue); + } else { + /* Only 10ms worth of data is available, signal + * erasure frame. + */ + *voc_pkt = MVS_G729A_ERASURE & 0x03; + + *pkt_len = *pkt_len + DSP_FRAME_HDR_LEN; + } + + break; + } + + case MVS_MODE_G711: + case MVS_MODE_G711A: { + /* G711 frames are 10ms each but the DSP expects 20ms + * worth of data, so send two 10ms frames per buffer. + */ + /* Add the first DSP frame info header. Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + *voc_pkt = ((rate_type & 0x0F) << 2) | + (buf_node->frame.header.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + voc_pkt = voc_pkt + buf_node->frame.len; + + list_add_tail(&buf_node->list, &audio->free_in_queue); + + if (!list_empty(&audio->in_queue)) { + /* Get the second buffer. */ + buf_node = list_first_entry(&audio->in_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + /* Add the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + *voc_pkt = ((rate_type & 0x0F) << 2) | + (buf_node->frame.header.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + *pkt_len = *pkt_len + + buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, + &audio->free_in_queue); + } else { + /* Only 10ms worth of data is available, signal + * erasure frame. + */ + *voc_pkt = ((rate_type & 0x0F) << 2) | + (MVS_G711A_ERASURE & 0x03); + + *pkt_len = *pkt_len + DSP_FRAME_HDR_LEN; + } + break; + } + + case MVS_MODE_IS733: + case MVS_MODE_4GV_NB: + case MVS_MODE_4GV_WB: { + /* Add the DSP frame info header. Header format: + * Bits 0-3 : Frame rate + */ + *voc_pkt = buf_node->frame.header.packet_rate & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + break; + } + + case MVS_MODE_EFR: + case MVS_MODE_FR: + case MVS_MODE_HR: { + /* + * Remove the DSP frame info header + * Header Format + * Bit 0: bfi applies only for downlink + * Bit 1-2: sid applies for downlink and uplink + * Bit 3: taf applies only for downlink + * MVS_MODE_HR + * Bit 4: ufi applies only for downlink + */ + *voc_pkt = + ((buf_node->frame.header.gsm_frame_type.bfi + & 0x01) | + ((buf_node->frame.header.gsm_frame_type.sid + & 0x03) << 1) | + ((buf_node->frame.header.gsm_frame_type.taf + & 0x01) << 3)); + + if (audio->mvs_mode == MVS_MODE_HR) { + *voc_pkt = (*voc_pkt | + ((buf_node->frame.header.gsm_frame_type.ufi + & 0x01) << 4) | + ((0 & 0x07) << 5)); + } else { + *voc_pkt = (*voc_pkt | + ((0 & 0x0F) << 4)); + } + + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + *pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + + break; + } + + default: { + *pkt_len = buf_node->frame.len; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &audio->free_in_queue); + } + } + } else { + *pkt_len = 0; + + pr_info("%s: No DL data available to send to MVS\n", __func__); + } + + spin_unlock_irqrestore(&audio->dsp_lock, dsp_flags); + wake_up(&audio->in_wait); +} + +static uint32_t audio_mvs_get_media_type(uint32_t mvs_mode, uint32_t rate_type) +{ + uint32_t media_type; + + switch (mvs_mode) { + case MVS_MODE_IS733: + media_type = VSS_MEDIA_ID_13K_MODEM; + break; + + case MVS_MODE_IS127: + media_type = VSS_MEDIA_ID_EVRC_MODEM; + break; + + case MVS_MODE_4GV_NB: + media_type = VSS_MEDIA_ID_4GV_NB_MODEM; + break; + + case MVS_MODE_4GV_WB: + media_type = VSS_MEDIA_ID_4GV_WB_MODEM; + break; + + case MVS_MODE_AMR: + media_type = VSS_MEDIA_ID_AMR_NB_MODEM; + break; + + case MVS_MODE_EFR: + media_type = VSS_MEDIA_ID_EFR_MODEM; + break; + + case MVS_MODE_FR: + media_type = VSS_MEDIA_ID_FR_MODEM; + break; + + case MVS_MODE_HR: + media_type = VSS_MEDIA_ID_HR_MODEM; + break; + + case MVS_MODE_LINEAR_PCM: + media_type = VSS_MEDIA_ID_PCM_NB; + break; + + case MVS_MODE_PCM: + media_type = VSS_MEDIA_ID_PCM_NB; + break; + + case MVS_MODE_AMR_WB: + media_type = VSS_MEDIA_ID_AMR_WB_MODEM; + break; + + case MVS_MODE_G729A: + media_type = VSS_MEDIA_ID_G729; + break; + + case MVS_MODE_G711: + case MVS_MODE_G711A: + if (rate_type == MVS_G711A_MODE_MULAW) + media_type = VSS_MEDIA_ID_G711_MULAW; + else + media_type = VSS_MEDIA_ID_G711_ALAW; + break; + + case MVS_MODE_PCM_WB: + media_type = VSS_MEDIA_ID_PCM_WB; + break; + + default: + media_type = VSS_MEDIA_ID_PCM_NB; + } + + pr_debug("%s: media_type is 0x%x\n", __func__, media_type); + + return media_type; +} + +static uint32_t audio_mvs_get_network_type(uint32_t mvs_mode) +{ + uint32_t network_type; + + switch (mvs_mode) { + case MVS_MODE_IS733: + case MVS_MODE_IS127: + case MVS_MODE_4GV_NB: + case MVS_MODE_AMR: + case MVS_MODE_EFR: + case MVS_MODE_FR: + case MVS_MODE_HR: + case MVS_MODE_LINEAR_PCM: + case MVS_MODE_G711: + case MVS_MODE_PCM: + case MVS_MODE_G729A: + case MVS_MODE_G711A: + network_type = VSS_NETWORK_ID_VOIP_NB; + break; + + case MVS_MODE_4GV_WB: + case MVS_MODE_AMR_WB: + case MVS_MODE_PCM_WB: + network_type = VSS_NETWORK_ID_VOIP_WB; + break; + + default: + network_type = VSS_NETWORK_ID_DEFAULT; + } + + pr_debug("%s: network_type is 0x%x\n", __func__, network_type); + + return network_type; +} + +static int audio_mvs_start(struct audio_mvs_info_type *audio) +{ + int rc = 0; + + pr_info("%s\n", __func__); + + /* Prevent sleep. */ + wake_lock(&audio->suspend_lock); + pm_qos_update_request(&audio->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + rc = voice_set_voc_path_full(1); + + if (rc == 0) { + voice_register_mvs_cb(audio_mvs_process_ul_pkt, + audio_mvs_process_dl_pkt, + audio); + + voice_config_vocoder( + audio_mvs_get_media_type(audio->mvs_mode, audio->rate_type), + audio_mvs_get_rate(audio->mvs_mode, audio->rate_type), + audio_mvs_get_network_type(audio->mvs_mode), + audio->dtx_mode, + audio->min_max_rate); + + audio->state = AUDIO_MVS_STARTED; + } else { + pr_err("%s: Error %d setting voc path to full\n", __func__, rc); + } + + return rc; +} + +static int audio_mvs_stop(struct audio_mvs_info_type *audio) +{ + int rc = 0; + + pr_info("%s\n", __func__); + + voice_set_voc_path_full(0); + + audio->state = AUDIO_MVS_STOPPED; + + /* Allow sleep. */ + pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&audio->suspend_lock); + + return rc; +} + +static int audio_mvs_open(struct inode *inode, struct file *file) +{ + int rc = 0; + int i; + int offset = 0; + struct audio_mvs_buf_node *buf_node = NULL; + + pr_info("%s\n", __func__); + + mutex_lock(&audio_mvs_info.lock); + + /* Allocate input and output buffers. */ + audio_mvs_info.memory_chunk = kmalloc(2 * MVS_MAX_Q_LEN * + sizeof(struct audio_mvs_buf_node), + GFP_KERNEL); + + if (audio_mvs_info.memory_chunk != NULL) { + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = audio_mvs_info.memory_chunk + offset; + + list_add_tail(&buf_node->list, + &audio_mvs_info.free_in_queue); + + offset = offset + sizeof(struct audio_mvs_buf_node); + } + + for (i = 0; i < MVS_MAX_Q_LEN; i++) { + buf_node = audio_mvs_info.memory_chunk + offset; + + list_add_tail(&buf_node->list, + &audio_mvs_info.free_out_queue); + + offset = offset + sizeof(struct audio_mvs_buf_node); + } + + audio_mvs_info.state = AUDIO_MVS_STOPPED; + + file->private_data = &audio_mvs_info; + + } else { + pr_err("%s: No memory for IO buffers\n", __func__); + + rc = -ENOMEM; + } + + mutex_unlock(&audio_mvs_info.lock); + + return rc; +} + +static int audio_mvs_release(struct inode *inode, struct file *file) +{ + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_info("%s\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STARTED) + audio_mvs_stop(audio); + + /* Free input and output memory. */ + mutex_lock(&audio->in_lock); + + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + list_for_each_safe(ptr, next, &audio->free_in_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + mutex_unlock(&audio->in_lock); + + + mutex_lock(&audio->out_lock); + + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + list_for_each_safe(ptr, next, &audio->free_out_queue) { + buf_node = list_entry(ptr, struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + } + + mutex_unlock(&audio->out_lock); + + kfree(audio->memory_chunk); + audio->memory_chunk = NULL; + + audio->state = AUDIO_MVS_CLOSED; + + mutex_unlock(&audio->lock); + + return 0; +} + +static ssize_t audio_mvs_read(struct file *file, + char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_debug("%s:\n", __func__); + + rc = wait_event_interruptible_timeout(audio->out_wait, + (!list_empty(&audio->out_queue) || + audio->state == AUDIO_MVS_STOPPED), + 1 * HZ); + + if (rc > 0) { + mutex_lock(&audio->out_lock); + + if ((audio->state == AUDIO_MVS_STARTED) && + (!list_empty(&audio->out_queue))) { + + if (count >= sizeof(struct q6_msm_audio_mvs_frame)) { + buf_node = list_first_entry(&audio->out_queue, + struct audio_mvs_buf_node, + list); + list_del(&buf_node->list); + + rc = copy_to_user(buf, + &buf_node->frame, + sizeof(struct q6_msm_audio_mvs_frame)); + + if (rc == 0) { + rc = buf_node->frame.len + + sizeof(buf_node->frame.header) + + sizeof(buf_node->frame.len); + } else { + pr_err("%s: Copy to user retuned %d", + __func__, rc); + + rc = -EFAULT; + } + + list_add_tail(&buf_node->list, + &audio->free_out_queue); + } else { + pr_err("%s: Read count %d < sizeof(frame) %d", + __func__, count, + sizeof(struct q6_msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + pr_err("%s: Read performed in state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->out_lock); + + } else if (rc == 0) { + pr_err("%s: No UL data available\n", __func__); + + rc = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + + rc = -ERESTARTSYS; + } + + return rc; +} + +static ssize_t audio_mvs_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) +{ + int rc = 0; + struct audio_mvs_buf_node *buf_node = NULL; + struct audio_mvs_info_type *audio = file->private_data; + + pr_debug("%s:\n", __func__); + + rc = wait_event_interruptible_timeout(audio->in_wait, + (!list_empty(&audio->free_in_queue) || + audio->state == AUDIO_MVS_STOPPED), 1 * HZ); + if (rc > 0) { + mutex_lock(&audio->in_lock); + + if (audio->state == AUDIO_MVS_STARTED) { + if (count <= sizeof(struct q6_msm_audio_mvs_frame)) { + if (!list_empty(&audio->free_in_queue)) { + buf_node = + list_first_entry(&audio->free_in_queue, + struct audio_mvs_buf_node, list); + list_del(&buf_node->list); + rc = copy_from_user(&buf_node->frame, + buf, + count); + + list_add_tail(&buf_node->list, + &audio->in_queue); + } else { + pr_err("%s: No free DL buffs\n", + __func__); + } + } else { + pr_err("%s: Write count %d < sizeof(frame) %d", + __func__, count, + sizeof(struct q6_msm_audio_mvs_frame)); + + rc = -ENOMEM; + } + } else { + pr_err("%s: Write performed in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->in_lock); + } else if (rc == 0) { + pr_err("%s: No free DL buffs\n", __func__); + + rc = -ETIMEDOUT; + } else { + pr_err("%s: write was interrupted\n", __func__); + + rc = -ERESTARTSYS; + } + + return rc; +} + +static long audio_mvs_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + struct audio_mvs_info_type *audio = file->private_data; + + pr_info("%s:\n", __func__); + + switch (cmd) { + case AUDIO_GET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + pr_info("%s: IOCTL GET_MVS_CONFIG\n", __func__); + + mutex_lock(&audio->lock); + + config.mvs_mode = audio->mvs_mode; + config.rate_type = audio->rate_type; + config.dtx_mode = audio->dtx_mode; + config.min_max_rate.min_rate = audio->min_max_rate.min_rate; + config.min_max_rate.max_rate = audio->min_max_rate.max_rate; + mutex_unlock(&audio->lock); + + rc = copy_to_user((void *)arg, &config, sizeof(config)); + if (rc == 0) + rc = sizeof(config); + else + pr_err("%s: Config copy failed %d\n", __func__, rc); + + break; + } + + case AUDIO_SET_MVS_CONFIG: { + struct msm_audio_mvs_config config; + + pr_info("%s: IOCTL SET_MVS_CONFIG\n", __func__); + + rc = copy_from_user(&config, (void *)arg, sizeof(config)); + if (rc == 0) { + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STOPPED) { + audio->mvs_mode = config.mvs_mode; + audio->rate_type = config.rate_type; + audio->dtx_mode = config.dtx_mode; + audio->min_max_rate.min_rate = + config.min_max_rate.min_rate; + audio->min_max_rate.max_rate = + config.min_max_rate.max_rate; + } else { + pr_err("%s: Set confg called in state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + } else { + pr_err("%s: Config copy failed %d\n", __func__, rc); + } + + break; + } + + case AUDIO_START: { + pr_info("%s: IOCTL START\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STOPPED) { + rc = audio_mvs_start(audio); + + if (rc != 0) + audio_mvs_stop(audio); + } else { + pr_err("%s: Start called in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + + break; + } + + case AUDIO_STOP: { + pr_info("%s: IOCTL STOP\n", __func__); + + mutex_lock(&audio->lock); + + if (audio->state == AUDIO_MVS_STARTED) { + rc = audio_mvs_stop(audio); + } else { + pr_err("%s: Stop called in invalid state %d\n", + __func__, audio->state); + + rc = -EPERM; + } + + mutex_unlock(&audio->lock); + + break; + } + + default: { + pr_err("%s: Unknown IOCTL %d\n", __func__, cmd); + } + } + + return rc; +} + +static const struct file_operations audio_mvs_fops = { + .owner = THIS_MODULE, + .open = audio_mvs_open, + .release = audio_mvs_release, + .read = audio_mvs_read, + .write = audio_mvs_write, + .unlocked_ioctl = audio_mvs_ioctl +}; + +struct miscdevice audio_mvs_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mvs", + .fops = &audio_mvs_fops +}; + +static int __init audio_mvs_init(void) +{ + int rc = 0; + + memset(&audio_mvs_info, 0, sizeof(audio_mvs_info)); + + init_waitqueue_head(&audio_mvs_info.in_wait); + init_waitqueue_head(&audio_mvs_info.out_wait); + + mutex_init(&audio_mvs_info.lock); + mutex_init(&audio_mvs_info.in_lock); + mutex_init(&audio_mvs_info.out_lock); + + spin_lock_init(&audio_mvs_info.dsp_lock); + + INIT_LIST_HEAD(&audio_mvs_info.in_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_in_queue); + INIT_LIST_HEAD(&audio_mvs_info.out_queue); + INIT_LIST_HEAD(&audio_mvs_info.free_out_queue); + + wake_lock_init(&audio_mvs_info.suspend_lock, + WAKE_LOCK_SUSPEND, + "audio_mvs_suspend"); + pm_qos_add_request(&audio_mvs_info.pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + rc = misc_register(&audio_mvs_misc); + + return rc; +} + +static void __exit audio_mvs_exit(void){ + pr_info("%s:\n", __func__); + + misc_deregister(&audio_mvs_misc); +} + +module_init(audio_mvs_init); +module_exit(audio_mvs_exit); + +MODULE_DESCRIPTION("MSM MVS driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c b/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c new file mode 100644 index 00000000000..37f6e6bdb19 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c @@ -0,0 +1,175 @@ +/* qcelp(v13k) audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "audio_utils_aio.h" + +#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in)) + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_qcelp_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_qcelp_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_V13K); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_qcelp_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_qcelp_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +static int __init audio_qcelp_init(void) +{ + return misc_register(&audio_qcelp_misc); +} + +device_initcall(audio_qcelp_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.c b/arch/arm/mach-msm/qdsp6v2/audio_utils.c new file mode 100644 index 00000000000..6a23e372091 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.c @@ -0,0 +1,637 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +static int audio_in_pause(struct q6audio_in *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + + return rc; +} + +static int audio_in_flush(struct q6audio_in *audio) +{ + int rc; + + pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session); + /* Flush if session running */ + if (audio->enabled) { + /* Implicitly issue a pause to the encoder before flushing */ + rc = audio_in_pause(audio); + if (rc < 0) { + pr_err("%s:session id %d: pause cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) { + pr_err("%s:session id %d: flush cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + q6asm_run(audio->ac, 0x00, 0x00, 0x00); + pr_debug("Rerun the session\n"); + } + audio->rflush = 1; + audio->wflush = 1; + memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + /* get read_lock to ensure no more waiting read thread */ + mutex_lock(&audio->read_lock); + audio->rflush = 0; + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + /* get write_lock to ensure no more waiting write thread */ + mutex_lock(&audio->write_lock); + audio->wflush = 0; + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: in_bytes %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_bytes)); + pr_debug("%s:session id %d: in_samples %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + atomic_set(&audio->out_count, 0); + return 0; +} + +/* must be called with audio->lock held */ +int audio_in_enable(struct q6audio_in *audio) +{ + if (audio->enabled) + return 0; + + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +/* must be called with audio->lock held */ +int audio_in_disable(struct q6audio_in *audio) +{ + int rc = 0; + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n", + __func__, audio->ac->session, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s:session id %d: Failed to close the" + "session rc=%d\n", __func__, audio->ac->session, + rc); + audio->stopped = 1; + memset(audio->out_frame_info, 0, + sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + } + pr_debug("%s:session id %d: enabled[%d]\n", __func__, + audio->ac->session, audio->enabled); + return rc; +} + +int audio_in_buf_alloc(struct q6audio_in *audio) +{ + int rc = 0; + + switch (audio->buf_alloc) { + case NO_BUF_ALLOC: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, + audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc" + "failed\n", __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_IN: + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_OUT: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc" + "failed\n", __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + break; + default: + pr_debug("%s:session id %d: buf[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + } + + return rc; +} +/* ------------------- device --------------------- */ +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + rc = audio_in_flush(audio); + if (rc < 0) + pr_err("%s:session id %d: Flush Fail rc=%d\n", + __func__, audio->ac->session, rc); + else { /* Register back the flushed read buffer with DSP */ + int cnt = 0; + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + pr_debug("register the read buffer\n"); + } + break; + } + case AUDIO_PAUSE: { + pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__, + audio->ac->session); + if (audio->enabled) + audio_in_pause(audio); + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, cfg.buffer_size, + cfg.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Minimum single frame size, + but with in maximum frames number */ + if ((cfg.buffer_size < (audio->min_frame_size+ \ + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]" + "framesperbuf[%d]\n", __func__, + audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]" + "framesperbuf[%d]\n", __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_CONFIG: { + if (copy_to_user((void *)arg, &audio->pcm_cfg, + sizeof(struct msm_audio_config))) + rc = -EFAULT; + break; + + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s:session id %d: Not sufficient permission to" + "change the record mode\n", __func__, + audio->ac->session); + rc = -EACCES; + break; + } + if ((cfg.buffer_count > PCM_BUF_COUNT) || + (cfg.buffer_count == 1)) + cfg.buffer_count = PCM_BUF_COUNT; + + audio->pcm_cfg.buffer_count = cfg.buffer_count; + audio->pcm_cfg.buffer_size = cfg.buffer_size; + audio->pcm_cfg.channel_count = cfg.channel_count; + audio->pcm_cfg.sample_rate = cfg.sample_rate; + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__, + audio->ac->session, audio->pcm_cfg.buffer_count, + audio->pcm_cfg.buffer_size); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} + +ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + unsigned char *data; + uint32_t offset = 0; + uint32_t size = 0; + int rc = 0; + uint32_t idx; + struct meta_out_dsp meta; + uint32_t bytes_to_copy = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + (sizeof(unsigned char) + + (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf))); + + pr_debug("%s:session id %d: read - %d\n", __func__, audio->ac->session, + count); + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->read_wait, + ((atomic_read(&audio->out_count) > 0) || + (audio->stopped) || + audio->rflush || audio->eos_rsp)); + + if (rc < 0) + break; + + if ((audio->stopped && !(atomic_read(&audio->out_count))) || + audio->rflush) { + pr_debug("%s:session id %d: driver in stop state or" + "flush,No more buf to read", __func__, + audio->ac->session); + rc = 0;/* End of File */ + break; + } + if (!(atomic_read(&audio->out_count)) && + (audio->eos_rsp == 1) && + (count >= (sizeof(unsigned char) + + sizeof(struct meta_out_dsp)))) { + unsigned char num_of_frames; + pr_info("%s:session id %d: eos %d at output\n", + __func__, audio->ac->session, audio->eos_rsp); + if (buf != start) + break; + num_of_frames = 0xFF; + if (copy_to_user(buf, &num_of_frames, + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + buf += sizeof(unsigned char); + meta.frame_size = 0xFFFF; + meta.encoded_pcm_samples = 0xFFFF; + meta.msw_ts = 0x00; + meta.lsw_ts = 0x00; + meta.nflags = AUD_EOS_SET; + audio->eos_rsp = 0; + if (copy_to_user(buf, &meta, sizeof(meta))) { + rc = -EFAULT; + break; + } + buf += sizeof(meta); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac, + &size, &idx); + if ((count >= (size + mfield_size)) && data) { + if (audio->buf_cfg.meta_info_enable) { + if (copy_to_user(buf, + &audio->out_frame_info[idx][0], + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + bytes_to_copy = + (size + audio->out_frame_info[idx][1]); + /* Number of frames information copied */ + buf += sizeof(unsigned char); + count -= sizeof(unsigned char); + } else { + offset = audio->out_frame_info[idx][1]; + bytes_to_copy = size; + } + + pr_debug("%s:session id %d: offset=%d nr of frames= %d\n", + __func__, audio->ac->session, + audio->out_frame_info[idx][1], + audio->out_frame_info[idx][0]); + + if (copy_to_user(buf, &data[offset], bytes_to_copy)) { + rc = -EFAULT; + break; + } + count -= bytes_to_copy; + buf += bytes_to_copy; + } else { + pr_err("%s:session id %d: short read data[%p]" + "bytesavail[%d]bytesrequest[%d]\n", __func__, + audio->ac->session, + data, size, count); + } + atomic_dec(&audio->out_count); + q6asm_read(audio->ac); + break; + } + mutex_unlock(&audio->read_lock); + + pr_debug("%s:session id %d: read: %d bytes\n", __func__, + audio->ac->session, (buf-start)); + if (buf > start) + return buf - start; + return rc; +} + +static int extract_meta_info(char *buf, unsigned long *msw_ts, + unsigned long *lsw_ts, unsigned int *flags) +{ + struct meta_in *meta = (struct meta_in *)buf; + *msw_ts = meta->ntimestamp.highpart; + *lsw_ts = meta->ntimestamp.lowpart; + *flags = meta->nflags; + return 0; +} + +ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + size_t xfer = 0; + char *cpy_ptr; + int rc = 0; + unsigned char *data; + uint32_t size = 0; + uint32_t idx = 0; + uint32_t nflags = 0; + unsigned long msw_ts = 0; + unsigned long lsw_ts = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + sizeof(struct meta_in); + + pr_debug("%s:session id %d: to write[%d]\n", __func__, + audio->ac->session, count); + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->write_lock); + + while (count > 0) { + rc = wait_event_interruptible(audio->write_wait, + ((atomic_read(&audio->in_count) > 0) || + (audio->stopped) || + (audio->wflush))); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + pr_debug("%s: session id %d: stop or flush\n", __func__, + audio->ac->session); + rc = -EBUSY; + break; + } + /* if no PCM data, might have only eos buffer + such case do not hold cpu buffer */ + if ((buf == start) && (count == mfield_size)) { + char eos_buf[sizeof(struct meta_in)]; + /* Processing begining of user buffer */ + if (copy_from_user(eos_buf, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(eos_buf, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + /* send the EOS and return */ + pr_debug("%s:session id %d: send EOS" + "0x%8x\n", __func__, + audio->ac->session, nflags); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac, + &size, &idx); + if (!data) { + pr_debug("%s:session id %d: No buf available\n", + __func__, audio->ac->session); + continue; + } + cpy_ptr = data; + if (audio->buf_cfg.meta_info_enable) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + count -= mfield_size; + } else { + pr_debug("%s:session id %d: continuous" + "buffer\n", __func__, audio->ac->session); + } + } + xfer = (count > (audio->pcm_cfg.buffer_size)) ? + (audio->pcm_cfg.buffer_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00); + if (rc < 0) { + rc = -EFAULT; + break; + } + atomic_dec(&audio->in_count); + count -= xfer; + buf += xfer; + } + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x]" + "start[0x%x]\n", __func__, audio->ac->session, + nflags, (int) buf, (int) start); + if (nflags & AUD_EOS_SET) { + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_info("%s:session id %d: eos %d at input\n", __func__, + audio->ac->session, audio->eos_rsp); + } + pr_debug("%s:session id %d: Written %d Avail Buf[%d]", __func__, + audio->ac->session, (buf - start - mfield_size), + atomic_read(&audio->in_count)); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +int audio_in_release(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = file->private_data; + pr_info("%s: session id %d\n", __func__, audio->ac->session); + mutex_lock(&audio->lock); + audio_in_disable(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} + diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.h b/arch/arm/mach-msm/qdsp6v2/audio_utils.h new file mode 100644 index 00000000000..df963f99fbd --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ +#include +#include "q6audio_common.h" + +#define FRAME_NUM (8) + +#define PCM_BUF_COUNT (2) + +#define AUD_EOS_SET 0x01 +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define NO_BUF_ALLOC 0x00 +#define BUF_ALLOC_IN 0x01 +#define BUF_ALLOC_OUT 0x02 +#define BUF_ALLOC_INOUT 0x03 +#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095)) + +struct timestamp { + unsigned long lowpart; + unsigned long highpart; +} __attribute__ ((packed)); + +struct meta_in { + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __attribute__ ((packed)); + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __attribute__ ((packed)); + +struct meta_out { + unsigned char num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __attribute__ ((packed)); + +struct q6audio_in { + spinlock_t dsp_lock; + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_audio_stream_config str_cfg; + void *enc_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + /* number of buffers available to read/write */ + atomic_t in_count; + atomic_t out_count; + + /* first idx: num of frames per buf, second idx: offset to frame */ + uint32_t out_frame_info[FRAME_NUM][2]; + int eos_rsp; + int opened; + int enabled; + int stopped; + int feedback; /* Flag indicates whether used + in Non Tunnel mode */ + int rflush; + int wflush; + int buf_alloc; + uint16_t min_frame_size; + uint16_t max_frames_per_buf; + long (*enc_ioctl)(struct file *, unsigned int, unsigned long); +}; + +int audio_in_enable(struct q6audio_in *audio); +int audio_in_disable(struct q6audio_in *audio); +int audio_in_buf_alloc(struct q6audio_in *audio); +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +ssize_t audio_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos); +ssize_t audio_in_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +int audio_in_release(struct inode *inode, struct file *file); + diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c new file mode 100644 index 00000000000..6a99be2ea50 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c @@ -0,0 +1,1407 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +ssize_t audio_aio_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +ssize_t audio_aio_debug_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct q6audio_aio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", list_empty(&audio->out_queue)); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} +#endif + +static int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *eos_buf = buf_node->kvaddr; + pr_debug("%s[%p]:insert_eos_buf\n", __func__, audio); + eos_buf->num_of_frames = 0xFFFFFFFF; + eos_buf->meta_out_dsp[0].offset_to_frame = 0x0; + eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET; + return sizeof(struct dec_meta_out) + + sizeof(eos_buf->meta_out_dsp[0]); +} + +/* Routine which updates read buffers of driver/dsp, + for flush operation as DSP output might not have proper + value set */ +static int insert_meta_data_flush(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + meta_data->num_of_frames = 0x0; + meta_data->meta_out_dsp[0].offset_to_frame = 0x0; + meta_data->meta_out_dsp[0].nflags = 0x0; + return sizeof(struct dec_meta_out) + + sizeof(meta_data->meta_out_dsp[0]); +} + +static void extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + if (dir) { /* input buffer - Write */ + if (audio->buf_cfg.meta_info_enable) + memcpy(&buf_node->meta_info.meta_in, + (char *)buf_node->kvaddr, sizeof(struct dec_meta_in)); + else + memset(&buf_node->meta_info.meta_in, + 0, sizeof(struct dec_meta_in)); + pr_debug("%s[%p]:i/p: msw_ts 0x%lx lsw_ts 0x%lx nflags 0x%8x\n", + __func__, audio, + buf_node->meta_info.meta_in.ntimestamp.highpart, + buf_node->meta_info.meta_in.ntimestamp.lowpart, + buf_node->meta_info.meta_in.nflags); + } else { /* output buffer - Read */ + memcpy((char *)buf_node->kvaddr, + &buf_node->meta_info.meta_out, + sizeof(struct dec_meta_out)); + meta_data->meta_out_dsp[0].nflags = 0x00000000; + pr_debug("%s[%p]:o/p: msw_ts 0x%8x lsw_ts 0x%8x nflags 0x%8x," + "num_frames = %d\n", + __func__, audio, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].msw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].lsw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].nflags, + ((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames); + } +} + +static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr, + unsigned long len, + struct audio_aio_ion_region **region) +{ + struct audio_aio_ion_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * ion buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n", + __func__, audio, addr, len); + list_for_each_entry(region_elt, &audio->ion_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_err("\t%s[%p]:%p, %ld --> %p\n", + __func__, audio, + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static unsigned long audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr, + unsigned long len, int ref_up, void **kvaddr) +{ + struct audio_aio_ion_region *region; + unsigned long paddr; + int ret; + + ret = audio_aio_ion_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_err("%s[%p]:lookup (%p, %ld) failed\n", + __func__, audio, addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + pr_debug("%s[%p]:found region %p ref_cnt %d\n", + __func__, audio, region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + /* provide kernel virtual address for accessing meta information */ + if (kvaddr) + *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr)); + return paddr; +} + +static int audio_aio_pause(struct q6audio_aio *audio) +{ + int rc = 0; + + pr_debug("%s[%p], enabled = %d\n", __func__, audio, + audio->enabled); + if (audio->enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s[%p]: pause cmd failed rc=%d\n", + __func__, audio, rc); + + } else + pr_err("%s[%p]: Driver not enabled\n", __func__, audio); + return rc; +} + +static int audio_aio_flush(struct q6audio_aio *audio) +{ + int rc; + + if (audio->enabled) { + /* Implicitly issue a pause to the decoder before flushing if + it is not in pause state */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audio_aio_pause(audio); + if (rc < 0) + pr_err("%s[%p}: pause cmd failed rc=%d\n", + __func__, audio, + rc); + else + audio->drv_status |= ADRV_STATUS_PAUSE; + } + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_err("%s[%p]: flush cmd failed rc=%d\n", + __func__, audio, rc); + /* Not in stop state, reenable the stream */ + if (audio->stopped == 0) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%p]:audio re-enable failed\n", + __func__, audio); + else { + audio->enabled = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + } + pr_debug("%s[%p]:in_bytes %d\n", + __func__, audio, atomic_read(&audio->in_bytes)); + pr_debug("%s[%p]:in_samples %d\n", + __func__, audio, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return 0; +} + +static int audio_aio_outport_flush(struct q6audio_aio *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH); + if (rc < 0) + pr_err("%s[%p}: output port flush cmd failed rc=%d\n", + __func__, audio, rc); + return rc; +} + +/* Write buffer to DSP / Handle Ack from DSP */ +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *used_buf; + + /* No active flush in progress */ + if (audio->wflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + BUG_ON(list_empty(&audio->out_queue)); + used_buf = list_first_entry(&audio->out_queue, + struct audio_aio_buffer_node, list); + if (token == used_buf->token) { + list_del(&used_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_debug("%s[%p]:consumed buffer\n", __func__, audio); + event_payload.aio_buf = used_buf->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(used_buf); + if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s[%p]: list is empty, reached EOS in" + "Tunnel\n", __func__, audio); + wake_up(&audio->write_wait); + } + } else { + pr_err("%s[%p]:expected=%lx ret=%x\n", + __func__, audio, used_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* Read buffer from DSP / Handle Ack from DSP */ +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *filled_buf; + + /* No active flush in progress */ + if (audio->rflush) + return; + + /* Statistics of read */ + atomic_add(payload[2], &audio->in_bytes); + atomic_add(payload[7], &audio->in_samples); + + spin_lock_irqsave(&audio->dsp_lock, flags); + BUG_ON(list_empty(&audio->in_queue)); + filled_buf = list_first_entry(&audio->in_queue, + struct audio_aio_buffer_node, list); + if (token == (filled_buf->token)) { + list_del(&filled_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + event_payload.aio_buf = filled_buf->buf; + /* Read done Buffer due to flush/normal condition + after EOS event, so append EOS buffer */ + if (audio->eos_rsp == 0x1) { + event_payload.aio_buf.data_len = + insert_eos_buf(audio, filled_buf); + /* Reset flag back to indicate eos intimated */ + audio->eos_rsp = 0; + } else { + filled_buf->meta_info.meta_out.num_of_frames = + payload[7]; + event_payload.aio_buf.data_len = payload[2] + \ + payload[3] + \ + sizeof(struct dec_meta_out); + pr_debug("%s[%p]:nr of frames 0x%8x len=%d\n", + __func__, audio, + filled_buf->meta_info.meta_out.num_of_frames, + event_payload.aio_buf.data_len); + extract_meta_out_info(audio, filled_buf, 0); + audio->eos_rsp = 0; + } + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + pr_err("%s[%p]:expected=%lx ret=%x\n", + __func__, audio, filled_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* ------------------- device --------------------- */ +void audio_aio_async_out_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + pr_debug("%s[%p}\n", __func__, audio); + /* EOS followed by flush, EOS response not guranteed, free EOS i/p + buffer */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) { + pr_debug("%s[%p]: EOS followed by flush received,acknowledge"\ + " eos i/p buffer immediately\n", __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload); + kfree(buf_node); + pr_debug("%s[%p]: Propagate WRITE_DONE during flush\n", + __func__, audio); + } +} + +void audio_aio_async_in_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + pr_debug("%s[%p]\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + /* Forcefull send o/p eos buffer after flush, if no eos response + * received by dsp even after sending eos command */ + if ((audio->eos_rsp != 1) && audio->eos_flag) { + pr_debug("%s[%p]: send eos on o/p buffer during" + "flush\n", __func__, audio); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + audio->eos_flag = 0; + } else { + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_meta_data_flush(audio, buf_node); + } + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload); + kfree(buf_node); + pr_debug("%s[%p]: Propagate READ_DONE during flush\n", + __func__, audio); + } +} + +int audio_aio_enable(struct q6audio_aio *audio) +{ + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +int audio_aio_disable(struct q6audio_aio *audio) +{ + int rc = 0; + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s[%p]: inbytes[%d] insamples[%d]\n", __func__, + audio, atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + /* Close the session */ + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s[%p]:Failed to close the session rc=%d\n", + __func__, audio, rc); + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + pr_debug("%s[%p]:enabled[%d]\n", __func__, audio, audio->enabled); + return rc; +} + +void audio_aio_reset_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + list_del(®ion->list); + ion_unmap_kernel(region->client, region->handle); + ion_free(region->client, region->handle); + ion_client_destroy(region->client); + kfree(region); + } + + return; +} + +void audio_aio_reset_event_queue(struct q6audio_aio *audio) +{ + unsigned long flags; + struct audio_aio_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static void audio_aio_unmap_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%p]:\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + pr_debug("%s[%p]: phy_address = 0x%lx\n", + __func__, audio, region->paddr); + if (region != NULL) { + rc = q6asm_memory_unmap(audio->ac, + (uint32_t)region->paddr, IN); + if (rc < 0) + pr_err("%s[%p]: memory unmap failed\n", + __func__, audio); + } + } +} + +int audio_aio_release(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = file->private_data; + pr_debug("%s[%p]\n", __func__, audio); + mutex_lock(&audio->lock); + audio->wflush = 1; + if (audio->enabled) + audio_aio_flush(audio); + audio->wflush = 0; + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audio_aio_unmap_ion_region(audio); + audio_aio_disable(audio); + audio_aio_reset_ion_region(audio); + audio->event_abort = 1; + wake_up(&audio->event_wait); + audio_aio_reset_event_queue(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + mutex_destroy(&audio->lock); + mutex_destroy(&audio->read_lock); + mutex_destroy(&audio->write_lock); + mutex_destroy(&audio->get_event_lock); +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} + +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + int rc = 0; + struct q6audio_aio *audio = file->private_data; + + if (!audio->enabled || audio->feedback) + return -EINVAL; + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + pr_debug("%s[%p]:\n", __func__, audio); + + mutex_lock(&audio->write_lock); + audio->eos_rsp = 0; + + rc = wait_event_interruptible(audio->write_wait, + (list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped); + + if (rc < 0) { + pr_err("%s[%p]: wait event for list_empty failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + + if (rc < 0) + pr_err("%s[%p]: q6asm_cmd failed, rc = %d", + __func__, audio, rc); + + rc = wait_event_interruptible(audio->write_wait, + (audio->eos_rsp || audio->wflush || + audio->stopped)); + + if (rc < 0) { + pr_err("%s[%p]: wait event for eos_rsp failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + if (audio->eos_rsp == 1) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%p]: audio enable failed\n", + __func__, audio); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + + if (audio->stopped || audio->wflush) + rc = -EBUSY; + +done: + mutex_unlock(&audio->write_lock); + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +static int audio_aio_events_pending(struct q6audio_aio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static long audio_aio_process_event_req(struct q6audio_aio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audio_aio_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int)usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audio_aio_events_pending + (audio), + msecs_to_jiffies + (timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible(audio->event_wait, + audio_aio_events_pending(audio)); + } + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + pr_err("%s[%p]:Unexpected path\n", __func__, audio); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return -EPERM; + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + pr_debug("%s[%p]:posted AUDIO_EVENT_WRITE_DONE to user\n", + __func__, audio); + mutex_lock(&audio->write_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->write_lock); + } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("%s[%p]:posted AUDIO_EVENT_READ_DONE to user\n", + __func__, audio); + mutex_lock(&audio->read_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->read_lock); + } + + /* Some read buffer might be held up in DSP,release all + * Once EOS indicated + */ + if (audio->eos_rsp && !list_empty(&audio->in_queue)) { + pr_debug("%s[%p]:Send flush command to release read buffers"\ + " held up in DSP\n", __func__, audio); + audio_aio_flush(audio); + } + + if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_aio_ion_check(struct q6audio_aio *audio, + void *vaddr, unsigned long len) +{ + struct audio_aio_ion_region *region_elt; + struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_err("%s[%p]:region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + __func__, audio, vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audio_aio_ion_add(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + ion_phys_addr_t paddr; + size_t len; + unsigned long kvaddr; + struct audio_aio_ion_region *region; + int rc = -EINVAL; + struct ion_handle *handle; + struct ion_client *client; + unsigned long ionflag; + void *temp_ptr; + + pr_debug("%s[%p]:\n", __func__, audio); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + client = msm_ion_client_create(UINT_MAX, "Audio_Dec_Client"); + if (IS_ERR_OR_NULL(client)) { + pr_err("Unable to create ION client\n"); + goto client_error; + } + + handle = ion_import_fd(client, info->fd); + if (IS_ERR_OR_NULL(handle)) { + pr_err("%s: could not get handle of the given fd\n", __func__); + goto import_error; + } + + rc = ion_handle_get_flags(client, handle, &ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", __func__); + goto flag_error; + } + + temp_ptr = ion_map_kernel(client, handle, ionflag); + if (IS_ERR_OR_NULL(temp_ptr)) { + pr_err("%s: could not get virtual address\n", __func__); + goto map_error; + } + kvaddr = (unsigned long)temp_ptr; + + rc = ion_phys(client, handle, &paddr, &len); + if (rc) { + pr_err("%s: could not get physical address\n", __func__); + goto ion_error; + } + + rc = audio_aio_ion_check(audio, info->vaddr, len); + if (rc < 0) { + pr_err("%s: audio_aio_ion_check failed\n", __func__); + goto ion_error; + } + + region->client = client; + region->handle = handle; + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->ref_cnt = 0; + pr_debug("%s[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n", + __func__, audio, + region->paddr, region->vaddr, region->len, region->kvaddr); + list_add_tail(®ion->list, &audio->ion_region_queue); + rc = q6asm_memory_map(audio->ac, (uint32_t) paddr, IN, (uint32_t) len, + 1); + if (rc < 0) { + pr_err("%s[%p]: memory map failed\n", __func__, audio); + goto ion_error; + } else { + goto end; + } + +ion_error: + ion_unmap_kernel(client, handle); +map_error: + ion_free(client, handle); +flag_error: +import_error: + ion_client_destroy(client); +client_error: + kfree(region); +end: + return rc; +} + +static int audio_aio_ion_remove(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%p]:info fd %d vaddr %p\n", + __func__, audio, info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("%s[%p]:region %p in use ref_cnt %d\n", + __func__, audio, region, + region->ref_cnt); + break; + } + pr_debug("%s[%p]:remove region fd %d vaddr %p\n", + __func__, audio, info->fd, info->vaddr); + rc = q6asm_memory_unmap(audio->ac, + (uint32_t) region->paddr, IN); + if (rc < 0) + pr_err("%s[%p]: memory unmap failed\n", + __func__, audio); + + list_del(®ion->list); + ion_unmap_kernel(region->client, region->handle); + ion_free(region->client, region->handle); + ion_client_destroy(region->client); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static void audio_aio_async_write(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + int rc; + struct audio_client *ac; + struct audio_aio_write_param param; + + pr_debug("%s[%p]: Send write buff %p phy %lx len %d" + "meta_enable = %d\n", + __func__, audio, buf_node, buf_node->paddr, + buf_node->buf.data_len, + audio->buf_cfg.meta_info_enable); + + ac = audio->ac; + /* Offset with appropriate meta */ + if (audio->feedback) { + /* Non Tunnel mode */ + param.paddr = buf_node->paddr + sizeof(struct dec_meta_in); + param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in); + } else { + /* Tunnel mode */ + param.paddr = buf_node->paddr; + param.len = buf_node->buf.data_len; + } + param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart; + param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart; + /* If no meta_info enaled, indicate no time stamp valid */ + if (audio->buf_cfg.meta_info_enable) + param.flags = 0; + else + param.flags = 0xFF00; + param.uid = param.paddr; + /* Read command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_err("%s[%p]:failed\n", __func__, audio); +} + +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audio_aio_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC); + if (!e_node) { + pr_err("%s[%p]:No mem to post event %d\n", + __func__, audio, type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audio_aio_async_read(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct audio_client *ac; + struct audio_aio_read_param param; + int rc; + + pr_debug("%s[%p]: Send read buff %p phy %lx len %d\n", + __func__, audio, buf_node, + buf_node->paddr, buf_node->buf.buf_len); + ac = audio->ac; + /* Provide address so driver can append nr frames information */ + param.paddr = buf_node->paddr + + sizeof(struct dec_meta_out); + param.len = buf_node->buf.buf_len - + sizeof(struct dec_meta_out); + param.uid = param.paddr; + /* Write command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_read(ac, ¶m); + if (rc < 0) + pr_err("%s[%p]:failed\n", __func__, audio); +} + +static int audio_aio_buf_add(struct q6audio_aio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audio_aio_buffer_node *buf_node; + + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len" + "%d\n", __func__, audio, buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1, + &buf_node->kvaddr); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (!audio->feedback && !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + extract_meta_out_info(audio, buf_node, 1); + /* Not a EOS buffer */ + if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audio_aio_async_write(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } else if (buf_node->meta_info.meta_in.nflags + & AUDIO_DEC_EOS_SET) { + if (!audio->wflush) { + pr_debug("%s[%p]:Send EOS cmd at i/p\n", + __func__, audio); + /* Driver will forcefully post writedone event + * once eos ack recived from DSP + */ + audio->eos_write_payload.aio_buf =\ + buf_node->buf; + audio->eos_flag = 1; + audio->eos_rsp = 0; + q6asm_cmd(audio->ac, CMD_EOS); + kfree(buf_node); + } else { /* Flush in progress, send back i/p + * EOS buffer as is + */ + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(buf_node); + } + } + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + /* No EOS reached */ + if (!audio->eos_rsp) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audio_aio_async_read(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->in_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + /* EOS reached at input side fake all upcoming read buffer to + * indicate the same + */ + else { + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + event_payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + pr_debug("%s[%p]: propagate READ_DONE as EOS done\n",\ + __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(buf_node); + } + } + return 0; +} + +static void audio_aio_ioport_reset(struct q6audio_aio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%p]:fsync in progress\n", __func__, audio); + audio->drv_ops.out_flush(audio); + } else + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + } +} + +int audio_aio_open(struct q6audio_aio *audio, struct file *file) +{ + int rc = 0; + int i; + struct audio_aio_event *e_node = NULL; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 48000; + audio->pcm_cfg.channel_count = 2; + + /* Only AIO interface */ + if (file->f_flags & O_NONBLOCK) { + pr_debug("%s[%p]:set to aio interface\n", __func__, audio); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.out_flush = audio_aio_async_out_flush; + audio->drv_ops.in_flush = audio_aio_async_in_flush; + q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + } else { + pr_err("%s[%p]:SIO interface not supported\n", + __func__, audio); + rc = -EACCES; + goto fail; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->cmd_wait); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->event_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->ion_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + + audio->drv_ops.out_flush(audio); + audio->opened = 1; + file->private_data = audio; + audio->codec_ioctl = audio_aio_ioctl; + + for (i = 0; i < AUDIO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + pr_err("%s[%p]:event pkt alloc failed\n", + __func__, audio); + break; + } + } + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + rc = -EFAULT; + break; + } + case AUDIO_GET_EVENT: { + pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audio_aio_process_event_req(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + break; + } + case AUDIO_ABORT_GET_EVENT: { + audio->event_abort = 1; + wake_up(&audio->event_wait); + break; + } + case AUDIO_ASYNC_WRITE: { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audio_aio_buf_add(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + break; + } + case AUDIO_ASYNC_READ: { + mutex_lock(&audio->read_lock); + if ((audio->feedback) && (audio->enabled)) + rc = audio_aio_buf_add(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + break; + } + case AUDIO_OUTPORT_FLUSH: { + pr_debug("%s[%p]:AUDIO_OUTPORT_FLUSH\n", __func__, audio); + mutex_lock(&audio->read_lock); + rc = audio_aio_outport_flush(audio); + if (rc < 0) { + pr_err("%s[%p]: AUDIO_OUTPORT_FLUSH failed\n", + __func__, audio); + rc = -EINTR; + } + mutex_unlock(&audio->read_lock); + break; + } + case AUDIO_STOP: { + pr_debug("%s[%p]: AUDIO_STOP session_id[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->stopped = 1; + audio_aio_flush(audio); + audio->enabled = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + if (rc < 0) { + pr_err("%s[%p]:Audio Stop procedure failed rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PAUSE: { + pr_debug("%s[%p]:AUDIO_PAUSE %ld\n", __func__, audio, arg); + mutex_lock(&audio->lock); + if (arg == 1) { + rc = audio_aio_pause(audio); + if (rc < 0) + pr_err("%s[%p]: pause FAILED rc=%d\n", + __func__, audio, rc); + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%p]: audio enable failed\n", + __func__, audio); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_FLUSH: { + pr_debug("%s[%p]: AUDIO_FLUSH sessionid[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->rflush = 1; + audio->wflush = 1; + /* Flush DSP */ + rc = audio_aio_flush(audio); + /* Flush input / Output buffer in software*/ + audio_aio_ioport_reset(audio); + if (rc < 0) { + pr_err("%s[%p]:AUDIO_FLUSH interrupted\n", + __func__, audio); + rc = -EINTR; + } else { + audio->rflush = 0; + audio->wflush = 0; + } + audio->eos_flag = 0; + audio->eos_rsp = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_REGISTER_ION: { + struct msm_audio_ion_info info; + pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_DEREGISTER_ION: { + struct msm_audio_ion_info info; + mutex_lock(&audio->lock); + pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + mutex_lock(&audio->lock); + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("%s[%p]:GET STREAM CFG %d %d\n", + __func__, audio, cfg.buffer_size, cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s[%p]:Not sufficient permission to" + "change the playback mode\n", __func__, audio); + rc = -EACCES; + mutex_unlock(&audio->lock); + break; + } + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]", + __func__, audio, + audio->ac->session, cfg.meta_info_enable); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d]" + "framesperbuf[%d]\n", __func__, audio, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_SESSION_ID: { + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + default: + rc = -EINVAL; + } + return rc; +} diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h new file mode 100644 index 00000000000..77288da2c75 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h @@ -0,0 +1,211 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6audio_common.h" + +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */ +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 +#define AUDIO_DEC_EOS_SET 0x00000001 +#define AUDIO_EVENT_NUM 10 + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct timestamp { + unsigned long lowpart; + unsigned long highpart; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct dec_meta_in { + unsigned char reserved[18]; + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct dec_meta_out { + unsigned int reserved[7]; + unsigned int num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +/* General meta field to store meta info +locally */ +union meta_data { + struct dec_meta_out meta_out; + struct dec_meta_in meta_in; +} __packed; + +#define PCM_BUF_COUNT (2) +/* Buffer with meta */ +#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out)) + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (2) +#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in)) + +struct audio_aio_ion_region { + struct list_head list; + struct ion_handle *handle; + struct ion_client *client; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audio_aio_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio_aio_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; + unsigned long token; + void *kvaddr; + union meta_data meta_info; +}; + +struct q6audio_aio; +struct audio_aio_drv_operations { + void (*out_flush) (struct q6audio_aio *); + void (*in_flush) (struct q6audio_aio *); +}; + +struct q6audio_aio { + atomic_t in_bytes; + atomic_t in_samples; + + struct msm_audio_stream_config str_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + struct audio_client *ac; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + struct mutex get_event_lock; + wait_queue_head_t cmd_wait; + wait_queue_head_t write_wait; + wait_queue_head_t event_wait; + spinlock_t dsp_lock; + spinlock_t event_queue_lock; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct list_head out_queue; /* queue to retain output buffers */ + struct list_head in_queue; /* queue to retain input buffers */ + struct list_head free_event_queue; + struct list_head event_queue; + struct list_head ion_region_queue; /* protected by lock */ + struct audio_aio_drv_operations drv_ops; + union msm_audio_event_payload eos_write_payload; + + uint32_t drv_status; + int event_abort; + int eos_rsp; + int eos_flag; + int opened; + int enabled; + int stopped; + int feedback; + int rflush; /* Read flush */ + int wflush; /* Write flush */ + long (*codec_ioctl)(struct file *, unsigned int, unsigned long); +}; + +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +int audio_aio_open(struct q6audio_aio *audio, struct file *file); +int audio_aio_enable(struct q6audio_aio *audio); +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload); +int audio_aio_release(struct inode *inode, struct file *file); +long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync); +void audio_aio_async_out_flush(struct q6audio_aio *audio); +void audio_aio_async_in_flush(struct q6audio_aio *audio); +#ifdef CONFIG_DEBUG_FS +ssize_t audio_aio_debug_open(struct inode *inode, struct file *file); +ssize_t audio_aio_debug_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos); +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/audio_wma.c b/arch/arm/mach-msm/qdsp6v2/audio_wma.c new file mode 100644 index 00000000000..021d58bb7a9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_wma.c @@ -0,0 +1,210 @@ +/* wma audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wma_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wma_cfg wma_cfg; + struct msm_audio_wma_config_v2 *wma_config; + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_cfg.format_tag = wma_config->format_tag; + wma_cfg.ch_cfg = wma_config->numchannels; + wma_cfg.sample_rate = wma_config->samplingrate; + wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond; + wma_cfg.block_align = wma_config->block_align; + wma_cfg.valid_bits_per_sample = + wma_config->validbitspersample; + wma_cfg.ch_mask = wma_config->channelmask; + wma_cfg.encode_opt = wma_config->encodeopt; + /* Configure Media format block */ + rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_WMA_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wma_config_v2))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wma_config_v2))) { + rc = -EFAULT; + break; + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:Could not allocate memory for wma" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V9); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wma_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_wma_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_wma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +static int __init audio_wma_init(void) +{ + return misc_register(&audio_wma_misc); +} + +device_initcall(audio_wma_init); diff --git a/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c b/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c new file mode 100644 index 00000000000..4fcdcc139d7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c @@ -0,0 +1,269 @@ +/* wmapro audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wmapro_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wmapro_cfg wmapro_cfg; + struct msm_audio_wmapro_config *wmapro_config; + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wmapro_config = (struct msm_audio_wmapro_config *) + audio->codec_cfg; + if ((wmapro_config->formattag == 0x162) || + (wmapro_config->formattag == 0x163) || + (wmapro_config->formattag == 0x166) || + (wmapro_config->formattag == 0x167)) { + wmapro_cfg.format_tag = wmapro_config->formattag; + } else { + pr_err("%s:AUDIO_START failed: formattag = %d\n", + __func__, wmapro_config->formattag); + rc = -EINVAL; + break; + } + if ((wmapro_config->numchannels == 1) || + (wmapro_config->numchannels == 2)) { + wmapro_cfg.ch_cfg = wmapro_config->numchannels; + } else { + pr_err("%s:AUDIO_START failed: channels = %d\n", + __func__, wmapro_config->numchannels); + rc = -EINVAL; + break; + } + if ((wmapro_config->samplingrate <= 48000) || + (wmapro_config->samplingrate > 0)) { + wmapro_cfg.sample_rate = + wmapro_config->samplingrate; + } else { + pr_err("%s:AUDIO_START failed: sample_rate = %d\n", + __func__, wmapro_config->samplingrate); + rc = -EINVAL; + break; + } + wmapro_cfg.avg_bytes_per_sec = + wmapro_config->avgbytespersecond; + if ((wmapro_config->asfpacketlength <= 13376) || + (wmapro_config->asfpacketlength > 0)) { + wmapro_cfg.block_align = + wmapro_config->asfpacketlength; + } else { + pr_err("%s:AUDIO_START failed: block_align = %d\n", + __func__, wmapro_config->asfpacketlength); + rc = -EINVAL; + break; + } + if ((wmapro_config->validbitspersample == 16) || + (wmapro_config->validbitspersample == 24)) { + wmapro_cfg.valid_bits_per_sample = + wmapro_config->validbitspersample; + } else { + pr_err("%s:AUDIO_START failed: bitspersample = %d\n", + __func__, + wmapro_config->validbitspersample); + rc = -EINVAL; + break; + } + if ((wmapro_config->channelmask == 4) || + (wmapro_config->channelmask == 3)) { + wmapro_cfg.ch_mask = wmapro_config->channelmask; + } else { + pr_err("%s:AUDIO_START failed: channel_mask = %d\n", + __func__, wmapro_config->channelmask); + rc = -EINVAL; + break; + } + wmapro_cfg.encode_opt = wmapro_config->encodeopt; + wmapro_cfg.adv_encode_opt = + wmapro_config->advancedencodeopt; + wmapro_cfg.adv_encode_opt2 = + wmapro_config->advancedencodeopt2; + /* Configure Media format block */ + rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_WMAPRO_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wmapro_config))) { + rc = -EFAULT; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wmapro_config))) { + rc = -EFAULT; + break; + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s: Could not allocate memory for wmapro" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wmapro_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_wmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_wmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +static int __init audio_wmapro_init(void) +{ + return misc_register(&audio_wmapro_misc); +} + +device_initcall(audio_wmapro_init); diff --git a/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c b/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c new file mode 100644 index 00000000000..0181cb53fd4 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c @@ -0,0 +1,2718 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "snddev_icodec.h" +#include "snddev_ecodec.h" +#include "timpani_profile_8x60.h" +#include "snddev_hdmi.h" +#include "snddev_mi2s.h" +#include "snddev_virtual.h" + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_hsed_config; +static void snddev_hsed_config_modify_setting(int type); +static void snddev_hsed_config_restore_setting(void); +#endif + +/* GPIO_CLASS_D0_EN */ +#define SNDDEV_GPIO_CLASS_D0_EN 227 + +/* GPIO_CLASS_D1_EN */ +#define SNDDEV_GPIO_CLASS_D1_EN 229 + +#define SNDDEV_GPIO_MIC2_ANCR_SEL 294 +#define SNDDEV_GPIO_MIC1_ANCL_SEL 295 +#define SNDDEV_GPIO_HS_MIC4_SEL 296 + +#define DSP_RAM_BASE_8x60 0x46700000 +#define DSP_RAM_SIZE_8x60 0x2000000 +static int dspcrashd_pdata_8x60 = 0xDEADDEAD; + +static struct resource resources_dspcrashd_8x60[] = { + { + .name = "msm_dspcrashd", + .start = DSP_RAM_BASE_8x60, + .end = DSP_RAM_BASE_8x60 + DSP_RAM_SIZE_8x60, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_dspcrashd_8x60 = { + .name = "msm_dspcrashd", + .num_resources = ARRAY_SIZE(resources_dspcrashd_8x60), + .resource = resources_dspcrashd_8x60, + .dev = { .platform_data = &dspcrashd_pdata_8x60 }, +}; + +static struct resource msm_cdcclk_ctl_resources[] = { + { + .name = "msm_snddev_tx_mclk", + .start = 108, + .end = 108, + .flags = IORESOURCE_IO, + }, + { + .name = "msm_snddev_rx_mclk", + .start = 109, + .end = 109, + .flags = IORESOURCE_IO, + }, +}; + +static struct platform_device msm_cdcclk_ctl_device = { + .name = "msm_cdcclk_ctl", + .num_resources = ARRAY_SIZE(msm_cdcclk_ctl_resources), + .resource = msm_cdcclk_ctl_resources, +}; + +static struct resource msm_aux_pcm_resources[] = { + + { + .name = "aux_pcm_dout", + .start = 111, + .end = 111, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_din", + .start = 112, + .end = 112, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_syncout", + .start = 113, + .end = 113, + .flags = IORESOURCE_IO, + }, + { + .name = "aux_pcm_clkin_a", + .start = 114, + .end = 114, + .flags = IORESOURCE_IO, + }, +}; + +static struct platform_device msm_aux_pcm_device = { + .name = "msm_aux_pcm", + .num_resources = ARRAY_SIZE(msm_aux_pcm_resources), + .resource = msm_aux_pcm_resources, +}; + +static struct resource msm_mi2s_gpio_resources[] = { + + { + .name = "mi2s_ws", + .start = 101, + .end = 101, + .flags = IORESOURCE_IO, + }, + { + .name = "mi2s_sclk", + .start = 102, + .end = 102, + .flags = IORESOURCE_IO, + }, + { + .name = "mi2s_mclk", + .start = 103, + .end = 103, + .flags = IORESOURCE_IO, + }, + { + .name = "fm_mi2s_sd", + .start = 107, + .end = 107, + .flags = IORESOURCE_IO, + }, +}; + +static struct platform_device msm_mi2s_device = { + .name = "msm_mi2s", + .num_resources = ARRAY_SIZE(msm_mi2s_gpio_resources), + .resource = msm_mi2s_gpio_resources, +}; + +/* Must be same size as msm_icodec_gpio_resources */ +static int msm_icodec_gpio_defaults[] = { + 0, + 0, +}; + +static struct resource msm_icodec_gpio_resources[] = { + { + .name = "msm_icodec_speaker_left", + .start = SNDDEV_GPIO_CLASS_D0_EN, + .end = SNDDEV_GPIO_CLASS_D0_EN, + .flags = IORESOURCE_IO, + }, + { + .name = "msm_icodec_speaker_right", + .start = SNDDEV_GPIO_CLASS_D1_EN, + .end = SNDDEV_GPIO_CLASS_D1_EN, + .flags = IORESOURCE_IO, + }, +}; + +static struct platform_device msm_icodec_gpio_device = { + .name = "msm_icodec_gpio", + .num_resources = ARRAY_SIZE(msm_icodec_gpio_resources), + .resource = msm_icodec_gpio_resources, + .dev = { .platform_data = &msm_icodec_gpio_defaults }, +}; + +static struct regulator *s3; +static struct regulator *mvs; + +static int msm_snddev_enable_dmic_power(void) +{ + int ret; + + s3 = regulator_get(NULL, "8058_s3"); + if (IS_ERR(s3)) { + ret = -EBUSY; + goto fail_get_s3; + } + + ret = regulator_set_voltage(s3, 1800000, 1800000); + if (ret) { + pr_err("%s: error setting voltage\n", __func__); + goto fail_s3; + } + + ret = regulator_enable(s3); + if (ret) { + pr_err("%s: error enabling regulator\n", __func__); + goto fail_s3; + } + + mvs = regulator_get(NULL, "8901_mvs0"); + if (IS_ERR(mvs)) + goto fail_mvs0_get; + + ret = regulator_enable(mvs); + if (ret) { + pr_err("%s: error setting regulator\n", __func__); + goto fail_mvs0_enable; + } + return ret; + +fail_mvs0_enable: + regulator_put(mvs); + mvs = NULL; +fail_mvs0_get: + regulator_disable(s3); +fail_s3: + regulator_put(s3); + s3 = NULL; +fail_get_s3: + return ret; +} + +static void msm_snddev_disable_dmic_power(void) +{ + int ret; + + if (mvs) { + ret = regulator_disable(mvs); + if (ret < 0) + pr_err("%s: error disabling vreg mvs\n", __func__); + regulator_put(mvs); + mvs = NULL; + } + + if (s3) { + ret = regulator_disable(s3); + if (ret < 0) + pr_err("%s: error disabling regulator s3\n", __func__); + regulator_put(s3); + s3 = NULL; + } +} + +#define PM8901_MPP_3 (2) /* PM8901 MPP starts from 0 */ + +static int config_class_d0_gpio(int enable) +{ + int rc; + + struct pm8xxx_mpp_config_data class_d0_mpp = { + .type = PM8XXX_MPP_TYPE_D_OUTPUT, + .level = PM8901_MPP_DIG_LEVEL_MSMIO, + }; + + if (enable) { + class_d0_mpp.control = PM8XXX_MPP_DOUT_CTRL_HIGH; + rc = pm8xxx_mpp_config(PM8901_MPP_PM_TO_SYS(PM8901_MPP_3), + &class_d0_mpp); + if (rc) { + pr_err("%s: CLASS_D0_EN failed\n", __func__); + return rc; + } + + rc = gpio_request(SNDDEV_GPIO_CLASS_D0_EN, "CLASSD0_EN"); + + if (rc) { + pr_err("%s: spkr pamp gpio pm8901 mpp3 request" + "failed\n", __func__); + class_d0_mpp.control = PM8XXX_MPP_DOUT_CTRL_LOW; + pm8xxx_mpp_config(PM8901_MPP_PM_TO_SYS(PM8901_MPP_3), + &class_d0_mpp); + return rc; + } + + gpio_direction_output(SNDDEV_GPIO_CLASS_D0_EN, 1); + gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D0_EN, 1); + + } else { + class_d0_mpp.control = PM8XXX_MPP_DOUT_CTRL_LOW; + pm8xxx_mpp_config(PM8901_MPP_PM_TO_SYS(PM8901_MPP_3), + &class_d0_mpp); + gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D0_EN, 0); + gpio_free(SNDDEV_GPIO_CLASS_D0_EN); + } + return 0; +} + +static int config_class_d1_gpio(int enable) +{ + int rc; + + if (enable) { + rc = gpio_request(SNDDEV_GPIO_CLASS_D1_EN, "CLASSD1_EN"); + + if (rc) { + pr_err("%s: Right Channel spkr gpio request" + " failed\n", __func__); + return rc; + } + + gpio_direction_output(SNDDEV_GPIO_CLASS_D1_EN, 1); + gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D1_EN, 1); + + } else { + gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D1_EN, 0); + gpio_free(SNDDEV_GPIO_CLASS_D1_EN); + } + return 0; +} + +static atomic_t pamp_ref_cnt; + +static int msm_snddev_poweramp_on(void) +{ + int rc; + + if (atomic_inc_return(&pamp_ref_cnt) > 1) + return 0; + + pr_debug("%s: enable stereo spkr amp\n", __func__); + rc = config_class_d0_gpio(1); + if (rc) { + pr_err("%s: d0 gpio configuration failed\n", __func__); + goto config_gpio_fail; + } + rc = config_class_d1_gpio(1); + if (rc) { + pr_err("%s: d1 gpio configuration failed\n", __func__); + goto config_gpio_fail; + } +config_gpio_fail: + return rc; +} + +static void msm_snddev_poweramp_off(void) +{ + if (atomic_dec_return(&pamp_ref_cnt) == 0) { + pr_debug("%s: disable stereo spkr amp\n", __func__); + config_class_d0_gpio(0); + config_class_d1_gpio(0); + msleep(30); + } +} + +/* Regulator 8058_l10 supplies regulator 8058_ncp. */ +static struct regulator *snddev_reg_ncp; +static struct regulator *snddev_reg_l10; + +static atomic_t preg_ref_cnt; + +static int msm_snddev_voltage_on(void) +{ + int rc; + pr_debug("%s\n", __func__); + + if (atomic_inc_return(&preg_ref_cnt) > 1) + return 0; + + snddev_reg_l10 = regulator_get(NULL, "8058_l10"); + if (IS_ERR(snddev_reg_l10)) { + pr_err("%s: regulator_get(%s) failed (%ld)\n", __func__, + "l10", PTR_ERR(snddev_reg_l10)); + return -EBUSY; + } + + rc = regulator_set_voltage(snddev_reg_l10, 2600000, 2600000); + if (rc < 0) + pr_err("%s: regulator_set_voltage(l10) failed (%d)\n", + __func__, rc); + + rc = regulator_enable(snddev_reg_l10); + if (rc < 0) + pr_err("%s: regulator_enable(l10) failed (%d)\n", __func__, rc); + + snddev_reg_ncp = regulator_get(NULL, "8058_ncp"); + if (IS_ERR(snddev_reg_ncp)) { + pr_err("%s: regulator_get(%s) failed (%ld)\n", __func__, + "ncp", PTR_ERR(snddev_reg_ncp)); + return -EBUSY; + } + + rc = regulator_set_voltage(snddev_reg_ncp, 1800000, 1800000); + if (rc < 0) { + pr_err("%s: regulator_set_voltage(ncp) failed (%d)\n", + __func__, rc); + goto regulator_fail; + } + + rc = regulator_enable(snddev_reg_ncp); + if (rc < 0) { + pr_err("%s: regulator_enable(ncp) failed (%d)\n", __func__, rc); + goto regulator_fail; + } + + return rc; + +regulator_fail: + regulator_put(snddev_reg_ncp); + snddev_reg_ncp = NULL; + return rc; +} + +static void msm_snddev_voltage_off(void) +{ + int rc; + pr_debug("%s\n", __func__); + + if (!snddev_reg_ncp) + goto done; + + if (atomic_dec_return(&preg_ref_cnt) == 0) { + rc = regulator_disable(snddev_reg_ncp); + if (rc < 0) + pr_err("%s: regulator_disable(ncp) failed (%d)\n", + __func__, rc); + regulator_put(snddev_reg_ncp); + + snddev_reg_ncp = NULL; + } + +done: + if (!snddev_reg_l10) + return; + + rc = regulator_disable(snddev_reg_l10); + if (rc < 0) + pr_err("%s: regulator_disable(l10) failed (%d)\n", + __func__, rc); + + regulator_put(snddev_reg_l10); + + snddev_reg_l10 = NULL; +} + +static int msm_snddev_enable_amic_power(void) +{ + int ret = 0; +#ifdef CONFIG_PMIC8058_OTHC + + if (machine_is_msm8x60_fluid()) { + + ret = pm8058_micbias_enable(OTHC_MICBIAS_0, + OTHC_SIGNAL_ALWAYS_ON); + if (ret) + pr_err("%s: Enabling amic power failed\n", __func__); + + ret = gpio_request(SNDDEV_GPIO_MIC2_ANCR_SEL, "MIC2_ANCR_SEL"); + if (ret) { + pr_err("%s: spkr pamp gpio %d request failed\n", + __func__, SNDDEV_GPIO_MIC2_ANCR_SEL); + return ret; + } + gpio_direction_output(SNDDEV_GPIO_MIC2_ANCR_SEL, 0); + + ret = gpio_request(SNDDEV_GPIO_MIC1_ANCL_SEL, "MIC1_ANCL_SEL"); + if (ret) { + pr_err("%s: mic1 ancl gpio %d request failed\n", + __func__, SNDDEV_GPIO_MIC1_ANCL_SEL); + gpio_free(SNDDEV_GPIO_MIC2_ANCR_SEL); + return ret; + } + gpio_direction_output(SNDDEV_GPIO_MIC1_ANCL_SEL, 0); + + } else { + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, + OTHC_SIGNAL_ALWAYS_ON); + if (ret) + pr_err("%s: Enabling amic power failed\n", __func__); + } +#endif + return ret; +} + +static void msm_snddev_disable_amic_power(void) +{ +#ifdef CONFIG_PMIC8058_OTHC + int ret; + if (machine_is_msm8x60_fluid()) { + ret = pm8058_micbias_enable(OTHC_MICBIAS_0, + OTHC_SIGNAL_OFF); + gpio_free(SNDDEV_GPIO_MIC1_ANCL_SEL); + gpio_free(SNDDEV_GPIO_MIC2_ANCR_SEL); + } else + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, OTHC_SIGNAL_OFF); + + if (ret) + pr_err("%s: Disabling amic power failed\n", __func__); +#endif +} + +static int msm_snddev_enable_anc_power(void) +{ + int ret = 0; +#ifdef CONFIG_PMIC8058_OTHC + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, + OTHC_SIGNAL_ALWAYS_ON); + if (ret) + pr_err("%s: Enabling anc micbias 2 failed\n", __func__); + + if (machine_is_msm8x60_fluid()) { + + ret = pm8058_micbias_enable(OTHC_MICBIAS_0, + OTHC_SIGNAL_ALWAYS_ON); + if (ret) + pr_err("%s: Enabling anc micbias 0 failed\n", __func__); + + ret = gpio_request(SNDDEV_GPIO_MIC2_ANCR_SEL, "MIC2_ANCR_SEL"); + if (ret) { + pr_err("%s: mic2 ancr gpio %d request failed\n", + __func__, SNDDEV_GPIO_MIC2_ANCR_SEL); + return ret; + } + gpio_direction_output(SNDDEV_GPIO_MIC2_ANCR_SEL, 1); + + ret = gpio_request(SNDDEV_GPIO_MIC1_ANCL_SEL, "MIC1_ANCL_SEL"); + if (ret) { + pr_err("%s: mic1 ancl gpio %d request failed\n", + __func__, SNDDEV_GPIO_MIC1_ANCL_SEL); + gpio_free(SNDDEV_GPIO_MIC2_ANCR_SEL); + return ret; + } + gpio_direction_output(SNDDEV_GPIO_MIC1_ANCL_SEL, 1); + + } +#endif + return ret; +} + +static void msm_snddev_disable_anc_power(void) +{ +#ifdef CONFIG_PMIC8058_OTHC + int ret; + + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, OTHC_SIGNAL_OFF); + + if (machine_is_msm8x60_fluid()) { + ret |= pm8058_micbias_enable(OTHC_MICBIAS_0, + OTHC_SIGNAL_OFF); + gpio_free(SNDDEV_GPIO_MIC2_ANCR_SEL); + gpio_free(SNDDEV_GPIO_MIC1_ANCL_SEL); + } + + if (ret) + pr_err("%s: Disabling anc power failed\n", __func__); +#endif +} + +static int msm_snddev_enable_amic_sec_power(void) +{ +#ifdef CONFIG_PMIC8058_OTHC + int ret; + + if (machine_is_msm8x60_fluid()) { + + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, + OTHC_SIGNAL_ALWAYS_ON); + if (ret) + pr_err("%s: Enabling amic2 power failed\n", __func__); + + ret = gpio_request(SNDDEV_GPIO_HS_MIC4_SEL, + "HS_MIC4_SEL"); + if (ret) { + pr_err("%s: spkr pamp gpio %d request failed\n", + __func__, SNDDEV_GPIO_HS_MIC4_SEL); + return ret; + } + gpio_direction_output(SNDDEV_GPIO_HS_MIC4_SEL, 1); + } +#endif + + msm_snddev_enable_amic_power(); + return 0; +} + +static void msm_snddev_disable_amic_sec_power(void) +{ +#ifdef CONFIG_PMIC8058_OTHC + int ret; + if (machine_is_msm8x60_fluid()) { + + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, + OTHC_SIGNAL_OFF); + if (ret) + pr_err("%s: Disabling amic2 power failed\n", __func__); + + gpio_free(SNDDEV_GPIO_HS_MIC4_SEL); + } +#endif + + msm_snddev_disable_amic_power(); +} + +static int msm_snddev_enable_dmic_sec_power(void) +{ + int ret; + + ret = msm_snddev_enable_dmic_power(); + if (ret) { + pr_err("%s: Error: Enabling dmic power failed\n", __func__); + return ret; + } +#ifdef CONFIG_PMIC8058_OTHC + ret = pm8058_micbias_enable(OTHC_MICBIAS_2, OTHC_SIGNAL_ALWAYS_ON); + if (ret) { + pr_err("%s: Error: Enabling micbias failed\n", __func__); + msm_snddev_disable_dmic_power(); + return ret; + } +#endif + return 0; +} + +static void msm_snddev_disable_dmic_sec_power(void) +{ + msm_snddev_disable_dmic_power(); + +#ifdef CONFIG_PMIC8058_OTHC + pm8058_micbias_enable(OTHC_MICBIAS_2, OTHC_SIGNAL_OFF); +#endif +} + +static struct adie_codec_action_unit iearpiece_48KHz_osr256_actions[] = + EAR_PRI_MONO_8000_OSR_256; + +static struct adie_codec_hwsetting_entry iearpiece_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_settings, + .setting_sz = ARRAY_SIZE(iearpiece_settings), +}; + +static struct snddev_icodec_data snddev_iearpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .profile = &iearpiece_profile, + .channel_mode = 1, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_iearpiece_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_iearpiece_data }, +}; + +static struct adie_codec_action_unit imic_48KHz_osr256_actions[] = + AMIC_PRI_MONO_OSR_256; + +static struct adie_codec_hwsetting_entry imic_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = imic_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(imic_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile imic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = imic_settings, + .setting_sz = ARRAY_SIZE(imic_settings), +}; + +static struct snddev_icodec_data snddev_imic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = 1, + .profile = &imic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_power, + .pamp_off = msm_snddev_disable_amic_power, +}; + +static struct platform_device msm_imic_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_imic_data }, +}; + +static struct snddev_icodec_data snddev_fluid_ispkr_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &imic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_power, + .pamp_off = msm_snddev_disable_amic_power, +}; + +static struct platform_device msm_fluid_ispkr_mic_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_fluid_ispkr_mic_data }, +}; + + +static struct adie_codec_action_unit headset_ab_cpls_48KHz_osr256_actions[] = + HEADSET_AB_CPLS_48000_OSR_256; + +static struct adie_codec_hwsetting_entry headset_ab_cpls_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = headset_ab_cpls_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_ab_cpls_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_ab_cpls_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_ab_cpls_settings, + .setting_sz = ARRAY_SIZE(headset_ab_cpls_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_rx", + .copp_id = 0, + .profile = &headset_ab_cpls_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, +}; + +static struct platform_device msm_headset_stereo_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_ihs_stereo_rx_data }, +}; + +static struct adie_codec_action_unit headset_anc_48KHz_osr256_actions[] = + ANC_HEADSET_CPLS_AMIC1_AUXL_RX1_48000_OSR_256; + +static struct adie_codec_hwsetting_entry headset_anc_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = headset_anc_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(headset_anc_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile headset_anc_profile = { + .path_type = ADIE_CODEC_RX, + .settings = headset_anc_settings, + .setting_sz = ARRAY_SIZE(headset_anc_settings), +}; + +static struct snddev_icodec_data snddev_anc_headset_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_ANC), + .name = "anc_headset_stereo_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &headset_anc_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_anc_power, + .pamp_off = msm_snddev_disable_anc_power, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, +}; + +static struct platform_device msm_anc_headset_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_anc_headset_data }, +}; + +static struct adie_codec_action_unit ispkr_stereo_48KHz_osr256_actions[] = + SPEAKER_PRI_STEREO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ispkr_stereo_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ispkr_stereo_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(ispkr_stereo_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ispkr_stereo_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ispkr_stereo_settings, + .setting_sz = ARRAY_SIZE(ispkr_stereo_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_stereo_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "speaker_stereo_rx", + .copp_id = 0, + .profile = &ispkr_stereo_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, +}; + +static struct platform_device msm_ispkr_stereo_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_ispkr_stereo_data }, +}; + +static struct adie_codec_action_unit idmic_mono_48KHz_osr256_actions[] = + DMIC1_PRI_MONO_OSR_256; + +static struct adie_codec_hwsetting_entry idmic_mono_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = idmic_mono_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(idmic_mono_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile idmic_mono_profile = { + .path_type = ADIE_CODEC_TX, + .settings = idmic_mono_settings, + .setting_sz = ARRAY_SIZE(idmic_mono_settings), +}; + +static struct snddev_icodec_data snddev_ispkr_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &idmic_mono_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, +}; + +static struct platform_device msm_ispkr_mic_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_ispkr_mic_data }, +}; + +static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] = + EAR_PRI_MONO_8000_OSR_256; + +static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iearpiece_ffa_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iearpiece_ffa_profile = { + .path_type = ADIE_CODEC_RX, + .settings = iearpiece_ffa_settings, + .setting_sz = ARRAY_SIZE(iearpiece_ffa_settings), +}; + +static struct snddev_icodec_data snddev_iearpiece_ffa_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "handset_rx", + .copp_id = 0, + .profile = &iearpiece_ffa_profile, + .channel_mode = 1, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_iearpiece_ffa_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_iearpiece_ffa_data }, +}; + +static struct snddev_icodec_data snddev_imic_ffa_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &idmic_mono_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, +}; + +static struct platform_device msm_imic_ffa_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_imic_ffa_data }, +}; + +static struct adie_codec_action_unit dual_mic_endfire_8KHz_osr256_actions[] = + DMIC1_PRI_STEREO_OSR_256; + +static struct adie_codec_hwsetting_entry dual_mic_endfire_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = dual_mic_endfire_8KHz_osr256_actions, + .action_sz = ARRAY_SIZE(dual_mic_endfire_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile dual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = dual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(dual_mic_endfire_settings), +}; + +static struct snddev_icodec_data snddev_dual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &dual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, +}; + +static struct platform_device msm_hs_dual_mic_endfire_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_dual_mic_endfire_data }, +}; + +static struct snddev_icodec_data snddev_dual_mic_spkr_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_endfire_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &dual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, +}; + +static struct platform_device msm_spkr_dual_mic_endfire_device = { + .name = "snddev_icodec", + .id = 15, + .dev = { .platform_data = &snddev_dual_mic_spkr_endfire_data }, +}; + +static struct adie_codec_action_unit dual_mic_broadside_8osr256_actions[] = + HS_DMIC2_STEREO_OSR_256; + +static struct adie_codec_hwsetting_entry dual_mic_broadside_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = dual_mic_broadside_8osr256_actions, + .action_sz = ARRAY_SIZE(dual_mic_broadside_8osr256_actions), + } +}; + +static struct adie_codec_dev_profile dual_mic_broadside_profile = { + .path_type = ADIE_CODEC_TX, + .settings = dual_mic_broadside_settings, + .setting_sz = ARRAY_SIZE(dual_mic_broadside_settings), +}; + +static struct snddev_icodec_data snddev_hs_dual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_broadside_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &dual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_sec_power, + .pamp_off = msm_snddev_disable_dmic_sec_power, +}; + +static struct platform_device msm_hs_dual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 21, + .dev = { .platform_data = &snddev_hs_dual_mic_broadside_data }, +}; + +static struct snddev_icodec_data snddev_spkr_dual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_broadside_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &dual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_sec_power, + .pamp_off = msm_snddev_disable_dmic_sec_power, +}; + +static struct platform_device msm_spkr_dual_mic_broadside_device = { + .name = "snddev_icodec", + .id = 18, + .dev = { .platform_data = &snddev_spkr_dual_mic_broadside_data }, +}; + +static struct adie_codec_action_unit + fluid_dual_mic_endfire_8KHz_osr256_actions[] = + FLUID_AMIC_DUAL_8000_OSR_256; + +static struct adie_codec_hwsetting_entry fluid_dual_mic_endfire_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = fluid_dual_mic_endfire_8KHz_osr256_actions, + .action_sz = + ARRAY_SIZE(fluid_dual_mic_endfire_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile fluid_dual_mic_endfire_profile = { + .path_type = ADIE_CODEC_TX, + .settings = fluid_dual_mic_endfire_settings, + .setting_sz = ARRAY_SIZE(fluid_dual_mic_endfire_settings), +}; + +static struct snddev_icodec_data snddev_fluid_dual_mic_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_endfire_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &fluid_dual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_sec_power, + .pamp_off = msm_snddev_disable_amic_sec_power, +}; + +static struct platform_device msm_fluid_hs_dual_mic_endfire_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_fluid_dual_mic_endfire_data }, +}; + +static struct snddev_icodec_data snddev_fluid_dual_mic_spkr_endfire_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_endfire_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &fluid_dual_mic_endfire_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_sec_power, + .pamp_off = msm_snddev_disable_amic_sec_power, +}; + +static struct platform_device msm_fluid_spkr_dual_mic_endfire_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_fluid_dual_mic_spkr_endfire_data }, +}; + +static struct adie_codec_action_unit + fluid_dual_mic_broadside_8KHz_osr256_actions[] = + FLUID_AMIC_DUAL_BROADSIDE_8000_OSR_256; + +static struct adie_codec_hwsetting_entry fluid_dual_mic_broadside_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = fluid_dual_mic_broadside_8KHz_osr256_actions, + .action_sz = + ARRAY_SIZE(fluid_dual_mic_broadside_8KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile fluid_dual_mic_broadside_profile = { + .path_type = ADIE_CODEC_TX, + .settings = fluid_dual_mic_broadside_settings, + .setting_sz = ARRAY_SIZE(fluid_dual_mic_broadside_settings), +}; + +static struct snddev_icodec_data snddev_fluid_dual_mic_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_dual_mic_broadside_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &fluid_dual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_power, + .pamp_off = msm_snddev_disable_amic_power, +}; + +static struct platform_device msm_fluid_hs_dual_mic_broadside_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_fluid_dual_mic_broadside_data }, +}; + +static struct snddev_icodec_data snddev_fluid_dual_mic_spkr_broadside_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "speaker_dual_mic_broadside_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &fluid_dual_mic_broadside_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_power, + .pamp_off = msm_snddev_disable_amic_power, +}; + +static struct platform_device msm_fluid_spkr_dual_mic_broadside_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_fluid_dual_mic_spkr_broadside_data }, +}; + +static struct snddev_hdmi_data snddev_hdmi_stereo_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "hdmi_stereo_rx", + .copp_id = HDMI_RX, + .channel_mode = 0, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_snddev_hdmi_stereo_rx_device = { + .name = "snddev_hdmi", + .dev = { .platform_data = &snddev_hdmi_stereo_rx_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = { + .capability = SNDDEV_CAP_TX , + .name = "fmradio_stereo_tx", + .copp_id = MI2S_TX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD3, /* sd3 */ + .sample_rate = 48000, +}; + +static struct platform_device msm_mi2s_fm_tx_device = { + .name = "snddev_mi2s", + .dev = { .platform_data = &snddev_mi2s_fm_tx_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_fm_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "fmradio_stereo_rx", + .copp_id = MI2S_RX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD3, /* sd3 */ + .sample_rate = 48000, +}; + +static struct platform_device msm_mi2s_fm_rx_device = { + .name = "snddev_mi2s", + .id = 1, + .dev = { .platform_data = &snddev_mi2s_fm_rx_data }, +}; + +static struct adie_codec_action_unit iheadset_mic_tx_osr256_actions[] = + HEADSET_AMIC2_TX_MONO_PRI_OSR_256; + +static struct adie_codec_hwsetting_entry iheadset_mic_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = iheadset_mic_tx_osr256_actions, + .action_sz = ARRAY_SIZE(iheadset_mic_tx_osr256_actions), + } +}; + +static struct adie_codec_dev_profile iheadset_mic_profile = { + .path_type = ADIE_CODEC_TX, + .settings = iheadset_mic_tx_settings, + .setting_sz = ARRAY_SIZE(iheadset_mic_tx_settings), +}; + +static struct snddev_icodec_data snddev_headset_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "headset_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &iheadset_mic_profile, + .channel_mode = 1, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_headset_mic_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_headset_mic_data }, +}; + +static struct adie_codec_action_unit + ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] = + SPEAKER_HPH_AB_CPL_PRI_STEREO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry + ihs_stereo_speaker_stereo_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions, + .action_sz = + ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions), + } +}; + +static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ihs_stereo_speaker_stereo_rx_settings, + .setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings), +}; + +static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "headset_stereo_speaker_stereo_rx", + .copp_id = 0, + .profile = &ihs_stereo_speaker_stereo_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, +}; + +static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = { + .name = "snddev_icodec", + .id = 22, + .dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data }, +}; + +/* define the value for BT_SCO */ + +static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "bt_sco_rx", + .copp_id = PCM_RX, + .channel_mode = 1, +}; + +static struct snddev_ecodec_data snddev_bt_sco_mic_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "bt_sco_tx", + .copp_id = PCM_TX, + .channel_mode = 1, +}; + +struct platform_device msm_bt_sco_earpiece_device = { + .name = "msm_snddev_ecodec", + .dev = { .platform_data = &snddev_bt_sco_earpiece_data }, +}; + +struct platform_device msm_bt_sco_mic_device = { + .name = "msm_snddev_ecodec", + .dev = { .platform_data = &snddev_bt_sco_mic_data }, +}; + +static struct adie_codec_action_unit itty_mono_tx_actions[] = + TTY_HEADSET_MONO_TX_OSR_256; + +static struct adie_codec_hwsetting_entry itty_mono_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_mono_tx_actions, + .action_sz = ARRAY_SIZE(itty_mono_tx_actions), + }, +}; + +static struct adie_codec_dev_profile itty_mono_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = itty_mono_tx_settings, + .setting_sz = ARRAY_SIZE(itty_mono_tx_settings), +}; + +static struct snddev_icodec_data snddev_itty_mono_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &itty_mono_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, +}; + +static struct platform_device msm_itty_mono_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_itty_mono_tx_data }, +}; + +static struct adie_codec_action_unit itty_mono_rx_actions[] = + TTY_HEADSET_MONO_RX_8000_OSR_256; + +static struct adie_codec_hwsetting_entry itty_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = itty_mono_rx_actions, + .action_sz = ARRAY_SIZE(itty_mono_rx_actions), + }, +}; + +static struct adie_codec_dev_profile itty_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = itty_mono_rx_settings, + .setting_sz = ARRAY_SIZE(itty_mono_rx_settings), +}; + +static struct snddev_icodec_data snddev_itty_mono_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY), + .name = "tty_headset_mono_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &itty_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, +}; + +static struct platform_device msm_itty_mono_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_itty_mono_rx_data }, +}; + +static struct adie_codec_action_unit linein_pri_actions[] = + LINEIN_PRI_STEREO_OSR_256; + +static struct adie_codec_hwsetting_entry linein_pri_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = linein_pri_actions, + .action_sz = ARRAY_SIZE(linein_pri_actions), + }, +}; + +static struct adie_codec_dev_profile linein_pri_profile = { + .path_type = ADIE_CODEC_TX, + .settings = linein_pri_settings, + .setting_sz = ARRAY_SIZE(linein_pri_settings), +}; + +static struct snddev_icodec_data snddev_linein_pri_data = { + .capability = SNDDEV_CAP_TX, + .name = "linein_pri_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &linein_pri_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, +}; + +static struct platform_device msm_linein_pri_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_linein_pri_data }, +}; + +static struct adie_codec_action_unit auxpga_lp_lo_actions[] = + LB_AUXPGA_LO_STEREO; + +static struct adie_codec_hwsetting_entry auxpga_lp_lo_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = auxpga_lp_lo_actions, + .action_sz = ARRAY_SIZE(auxpga_lp_lo_actions), + }, +}; + +static struct adie_codec_dev_profile auxpga_lp_lo_profile = { + .path_type = ADIE_CODEC_LB, + .settings = auxpga_lp_lo_settings, + .setting_sz = ARRAY_SIZE(auxpga_lp_lo_settings), +}; + +static struct snddev_icodec_data snddev_auxpga_lp_lo_data = { + .capability = SNDDEV_CAP_LB, + .name = "speaker_stereo_lb", + .copp_id = PRIMARY_I2S_RX, + .profile = &auxpga_lp_lo_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_ANALOG, +}; + +static struct platform_device msm_auxpga_lp_lo_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_auxpga_lp_lo_data }, +}; + +static struct adie_codec_action_unit auxpga_lp_hs_actions[] = + LB_AUXPGA_HPH_AB_CPLS_STEREO; + +static struct adie_codec_hwsetting_entry auxpga_lp_hs_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = auxpga_lp_hs_actions, + .action_sz = ARRAY_SIZE(auxpga_lp_hs_actions), + }, +}; + +static struct adie_codec_dev_profile auxpga_lp_hs_profile = { + .path_type = ADIE_CODEC_LB, + .settings = auxpga_lp_hs_settings, + .setting_sz = ARRAY_SIZE(auxpga_lp_hs_settings), +}; + +static struct snddev_icodec_data snddev_auxpga_lp_hs_data = { + .capability = SNDDEV_CAP_LB, + .name = "hs_stereo_lb", + .copp_id = PRIMARY_I2S_RX, + .profile = &auxpga_lp_hs_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_ANALOG, +}; + +static struct platform_device msm_auxpga_lp_hs_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &snddev_auxpga_lp_hs_data }, +}; + +#ifdef CONFIG_MSM8X60_FTM_AUDIO_DEVICES +static struct adie_codec_action_unit ftm_headset_mono_rx_actions[] = + HPH_PRI_AB_CPLS_MONO; + +static struct adie_codec_hwsetting_entry ftm_headset_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_mono_rx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_mono_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_headset_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_headset_mono_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_mono_rx_settings), +}; + +static struct snddev_icodec_data ftm_headset_mono_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_headset_mono_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_headset_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_mono_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_mono_rx_data}, +}; + +static struct adie_codec_action_unit ftm_headset_mono_diff_rx_actions[] = + HEADSET_MONO_DIFF_RX; + +static struct adie_codec_hwsetting_entry ftm_headset_mono_diff_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_mono_diff_rx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_mono_diff_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_headset_mono_diff_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_headset_mono_diff_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_mono_diff_rx_settings), +}; + +static struct snddev_icodec_data ftm_headset_mono_diff_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_headset_mono_diff_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_headset_mono_diff_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_mono_diff_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_mono_diff_rx_data}, +}; + +static struct adie_codec_action_unit ftm_spkr_mono_rx_actions[] = + SPEAKER_PRI_STEREO_48000_OSR_256; + +static struct adie_codec_hwsetting_entry ftm_spkr_mono_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_mono_rx_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_mono_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_mono_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_mono_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_mono_rx_settings), +}; + +static struct snddev_icodec_data ftm_spkr_mono_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spkr_mono_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_mono_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spkr_mono_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_mono_rx_data}, +}; + +static struct adie_codec_action_unit ftm_spkr_l_rx_actions[] = + FTM_SPKR_L_RX; + +static struct adie_codec_hwsetting_entry ftm_spkr_l_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_l_rx_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_l_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_l_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_l_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_l_rx_settings), +}; + +static struct snddev_icodec_data ftm_spkr_l_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spkr_l_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_l_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spkr_l_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_l_rx_data}, +}; + +static struct adie_codec_action_unit ftm_spkr_r_rx_actions[] = + SPKR_R_RX; + +static struct adie_codec_hwsetting_entry ftm_spkr_r_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_r_rx_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_r_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_r_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_r_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_r_rx_settings), +}; + +static struct snddev_icodec_data ftm_spkr_r_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spkr_r_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_r_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spkr_r_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_r_rx_data}, +}; + +static struct adie_codec_action_unit ftm_spkr_mono_diff_rx_actions[] = + SPKR_MONO_DIFF_RX; + +static struct adie_codec_hwsetting_entry ftm_spkr_mono_diff_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_mono_diff_rx_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_mono_diff_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_mono_diff_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_mono_diff_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_mono_diff_rx_settings), +}; + +static struct snddev_icodec_data ftm_spkr_mono_diff_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spkr_mono_diff_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_mono_diff_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spkr_mono_diff_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_mono_diff_rx_data}, +}; + +static struct adie_codec_action_unit ftm_headset_mono_l_rx_actions[] = + HPH_PRI_AB_CPLS_MONO_LEFT; + +static struct adie_codec_hwsetting_entry ftm_headset_mono_l_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_mono_l_rx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_mono_l_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_headset_mono_l_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_headset_mono_l_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_mono_l_rx_settings), +}; + +static struct snddev_icodec_data ftm_headset_mono_l_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_headset_mono_l_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_headset_mono_l_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_mono_l_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_mono_l_rx_data}, +}; + +static struct adie_codec_action_unit ftm_headset_mono_r_rx_actions[] = + HPH_PRI_AB_CPLS_MONO_RIGHT; + +static struct adie_codec_hwsetting_entry ftm_headset_mono_r_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_mono_r_rx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_mono_r_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_headset_mono_r_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_headset_mono_r_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_mono_r_rx_settings), +}; + +static struct snddev_icodec_data ftm_headset_mono_r_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_headset_mono_r_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_headset_mono_r_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_mono_r_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_mono_r_rx_data}, +}; + +static struct adie_codec_action_unit ftm_linein_l_tx_actions[] = + LINEIN_MONO_L_TX; + +static struct adie_codec_hwsetting_entry ftm_linein_l_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_linein_l_tx_actions, + .action_sz = ARRAY_SIZE(ftm_linein_l_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_linein_l_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_linein_l_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_linein_l_tx_settings), +}; + +static struct snddev_icodec_data ftm_linein_l_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_linein_l_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_linein_l_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_linein_l_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_linein_l_tx_data }, +}; + +static struct adie_codec_action_unit ftm_linein_r_tx_actions[] = + LINEIN_MONO_R_TX; + +static struct adie_codec_hwsetting_entry ftm_linein_r_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_linein_r_tx_actions, + .action_sz = ARRAY_SIZE(ftm_linein_r_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_linein_r_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_linein_r_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_linein_r_tx_settings), +}; + +static struct snddev_icodec_data ftm_linein_r_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_linein_r_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_linein_r_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_linein_r_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_linein_r_tx_data }, +}; + +static struct adie_codec_action_unit ftm_aux_out_rx_actions[] = + AUX_OUT_RX; + +static struct adie_codec_hwsetting_entry ftm_aux_out_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_aux_out_rx_actions, + .action_sz = ARRAY_SIZE(ftm_aux_out_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_aux_out_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_aux_out_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_aux_out_rx_settings), +}; + +static struct snddev_icodec_data ftm_aux_out_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_aux_out_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_aux_out_rx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_aux_out_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_aux_out_rx_data}, +}; + +static struct adie_codec_action_unit ftm_dmic1_left_tx_actions[] = + DMIC1_LEFT_TX; + +static struct adie_codec_hwsetting_entry ftm_dmic1_left_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_dmic1_left_tx_actions, + .action_sz = ARRAY_SIZE(ftm_dmic1_left_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_dmic1_left_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_dmic1_left_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_dmic1_left_tx_settings), +}; + +static struct snddev_icodec_data ftm_dmic1_left_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_dmic1_left_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_dmic1_left_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_dmic1_left_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_dmic1_left_tx_data}, +}; + +static struct adie_codec_action_unit ftm_dmic1_right_tx_actions[] = + DMIC1_RIGHT_TX; + +static struct adie_codec_hwsetting_entry ftm_dmic1_right_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_dmic1_right_tx_actions, + .action_sz = ARRAY_SIZE(ftm_dmic1_right_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_dmic1_right_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_dmic1_right_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_dmic1_right_tx_settings), +}; + +static struct snddev_icodec_data ftm_dmic1_right_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_dmic1_right_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_dmic1_right_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_dmic1_right_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_dmic1_right_tx_data}, +}; + +static struct adie_codec_action_unit ftm_dmic1_l_and_r_tx_actions[] = + DMIC1_LEFT_AND_RIGHT_TX; + +static struct adie_codec_hwsetting_entry ftm_dmic1_l_and_r_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_dmic1_l_and_r_tx_actions, + .action_sz = ARRAY_SIZE(ftm_dmic1_l_and_r_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_dmic1_l_and_r_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_dmic1_l_and_r_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_dmic1_l_and_r_tx_settings), +}; + +static struct snddev_icodec_data ftm_dmic1_l_and_r_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_dmic1_l_and_r_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_dmic1_l_and_r_tx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_dmic1_l_and_r_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_dmic1_l_and_r_tx_data}, +}; + +static struct adie_codec_action_unit ftm_dmic2_left_tx_actions[] = + DMIC2_LEFT_TX; + +static struct adie_codec_hwsetting_entry ftm_dmic2_left_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_dmic2_left_tx_actions, + .action_sz = ARRAY_SIZE(ftm_dmic2_left_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_dmic2_left_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_dmic2_left_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_dmic2_left_tx_settings), +}; + +static struct snddev_icodec_data ftm_dmic2_left_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_dmic2_left_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_dmic2_left_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_dmic2_left_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_dmic2_left_tx_data }, +}; + +static struct adie_codec_action_unit ftm_dmic2_right_tx_actions[] = + DMIC2_RIGHT_TX; + +static struct adie_codec_hwsetting_entry ftm_dmic2_right_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_dmic2_right_tx_actions, + .action_sz = ARRAY_SIZE(ftm_dmic2_right_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_dmic2_right_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_dmic2_right_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_dmic2_right_tx_settings), +}; + +static struct snddev_icodec_data ftm_dmic2_right_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_dmic2_right_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_dmic2_right_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_dmic2_right_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_dmic2_right_tx_data }, +}; + +static struct adie_codec_action_unit ftm_dmic2_l_and_r_tx_actions[] = + DMIC2_LEFT_AND_RIGHT_TX; + +static struct adie_codec_hwsetting_entry ftm_dmic2_l_and_r_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_dmic2_l_and_r_tx_actions, + .action_sz = ARRAY_SIZE(ftm_dmic2_l_and_r_tx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_dmic2_l_and_r_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_dmic2_l_and_r_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_dmic2_l_and_r_tx_settings), +}; + +static struct snddev_icodec_data ftm_dmic2_l_and_r_tx_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_dmic2_l_and_r_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_dmic2_l_and_r_tx_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_dmic_power, + .pamp_off = msm_snddev_disable_dmic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_dmic2_l_and_r_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_dmic2_l_and_r_tx_data}, +}; + +static struct adie_codec_action_unit ftm_handset_mic1_aux_in_actions[] = + HANDSET_MIC1_AUX_IN; + +static struct adie_codec_hwsetting_entry ftm_handset_mic1_aux_in_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_handset_mic1_aux_in_actions, + .action_sz = ARRAY_SIZE(ftm_handset_mic1_aux_in_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_handset_mic1_aux_in_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_handset_mic1_aux_in_settings, + .setting_sz = ARRAY_SIZE(ftm_handset_mic1_aux_in_settings), +}; + +static struct snddev_icodec_data ftm_handset_mic1_aux_in_data = { + .capability = SNDDEV_CAP_TX, + .name = "ftm_handset_mic1_aux_in", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_handset_mic1_aux_in_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + /* Assumption is that inputs are not tied to analog mic, so + * no need to enable mic bias. + */ + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_handset_mic1_aux_in_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_handset_mic1_aux_in_data}, +}; + +static struct snddev_mi2s_data snddev_mi2s_sd0_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "mi2s_sd0_rx", + .copp_id = MI2S_RX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD0, /* sd0 */ + .sample_rate = 48000, +}; + +static struct platform_device ftm_mi2s_sd0_rx_device = { + .name = "snddev_mi2s", + .dev = { .platform_data = &snddev_mi2s_sd0_rx_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_sd1_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "mi2s_sd1_rx", + .copp_id = MI2S_RX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD1, /* sd1 */ + .sample_rate = 48000, +}; + +static struct platform_device ftm_mi2s_sd1_rx_device = { + .name = "snddev_mi2s", + .dev = { .platform_data = &snddev_mi2s_sd1_rx_data }, +}; + +static struct snddev_mi2s_data snddev_mi2s_sd2_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "mi2s_sd2_rx", + .copp_id = MI2S_RX, + .channel_mode = 2, /* stereo */ + .sd_lines = MI2S_SD2, /* sd2 */ + .sample_rate = 48000, +}; + +static struct platform_device ftm_mi2s_sd2_rx_device = { + .name = "snddev_mi2s", + .dev = { .platform_data = &snddev_mi2s_sd2_rx_data }, +}; + +/* earpiece */ +static struct adie_codec_action_unit ftm_handset_adie_lp_rx_actions[] = + EAR_PRI_MONO_LB; + +static struct adie_codec_hwsetting_entry ftm_handset_adie_lp_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_handset_adie_lp_rx_actions, + .action_sz = ARRAY_SIZE(ftm_handset_adie_lp_rx_actions), + } +}; + +static struct adie_codec_dev_profile ftm_handset_adie_lp_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_handset_adie_lp_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_handset_adie_lp_rx_settings), +}; + +static struct snddev_icodec_data ftm_handset_adie_lp_rx_data = { + .capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE), + .name = "ftm_handset_adie_lp_rx", + .copp_id = 0, + .profile = &ftm_handset_adie_lp_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_handset_adie_lp_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_handset_adie_lp_rx_data }, +}; + +static struct adie_codec_action_unit ftm_headset_l_adie_lp_rx_actions[] = + FTM_HPH_PRI_AB_CPLS_MONO_LB_LEFT; + +static struct adie_codec_hwsetting_entry ftm_headset_l_adie_lp_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_l_adie_lp_rx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_l_adie_lp_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_headset_l_adie_lp_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_headset_l_adie_lp_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_l_adie_lp_rx_settings), +}; + +static struct snddev_icodec_data ftm_headset_l_adie_lp_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_headset_l_adie_lp_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_headset_l_adie_lp_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_l_adie_lp_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_l_adie_lp_rx_data }, +}; + +static struct adie_codec_action_unit ftm_headset_r_adie_lp_rx_actions[] = + FTM_HPH_PRI_AB_CPLS_MONO_LB_RIGHT; + +static struct adie_codec_hwsetting_entry ftm_headset_r_adie_lp_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_r_adie_lp_rx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_r_adie_lp_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_headset_r_adie_lp_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_headset_r_adie_lp_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_r_adie_lp_rx_settings), +}; + +static struct snddev_icodec_data ftm_headset_r_adie_lp_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_headset_r_adie_lp_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_headset_r_adie_lp_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .voltage_on = msm_snddev_voltage_on, + .voltage_off = msm_snddev_voltage_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_r_adie_lp_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_r_adie_lp_rx_data }, +}; + +static struct adie_codec_action_unit ftm_spkr_l_rx_lp_actions[] = + FTM_SPKR_L_RX; + +static struct adie_codec_hwsetting_entry ftm_spkr_l_rx_lp_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_l_rx_lp_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_l_rx_lp_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_l_rx_lp_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_l_rx_lp_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_l_rx_lp_settings), +}; + +static struct snddev_icodec_data ftm_spkr_l_rx_lp_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spk_l_adie_lp_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_l_rx_lp_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spk_l_adie_lp_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_l_rx_lp_data}, +}; + +static struct adie_codec_action_unit ftm_spkr_r_adie_lp_rx_actions[] = + SPKR_R_RX; + +static struct adie_codec_hwsetting_entry ftm_spkr_r_adie_lp_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_r_adie_lp_rx_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_r_adie_lp_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_r_adie_lp_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_r_adie_lp_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_r_adie_lp_rx_settings), +}; + +static struct snddev_icodec_data ftm_spkr_r_adie_lp_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spk_r_adie_lp_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_r_adie_lp_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spk_r_adie_lp_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_r_adie_lp_rx_data}, +}; + +static struct adie_codec_action_unit ftm_spkr_adie_lp_rx_actions[] = + FTM_SPKR_RX_LB; + +static struct adie_codec_hwsetting_entry ftm_spkr_adie_lp_rx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_spkr_adie_lp_rx_actions, + .action_sz = ARRAY_SIZE(ftm_spkr_adie_lp_rx_actions), + }, +}; + +static struct adie_codec_dev_profile ftm_spkr_adie_lp_rx_profile = { + .path_type = ADIE_CODEC_RX, + .settings = ftm_spkr_adie_lp_rx_settings, + .setting_sz = ARRAY_SIZE(ftm_spkr_adie_lp_rx_settings), +}; + +static struct snddev_icodec_data ftm_spkr_adie_lp_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "ftm_spk_adie_lp_rx", + .copp_id = PRIMARY_I2S_RX, + .profile = &ftm_spkr_adie_lp_rx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_poweramp_on, + .pamp_off = msm_snddev_poweramp_off, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_spk_adie_lp_rx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_spkr_adie_lp_rx_data}, +}; + +static struct adie_codec_action_unit ftm_handset_dual_tx_lp_actions[] = + FTM_AMIC_DUAL_HANDSET_TX_LB; + +static struct adie_codec_hwsetting_entry ftm_handset_dual_tx_lp_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_handset_dual_tx_lp_actions, + .action_sz = ARRAY_SIZE(ftm_handset_dual_tx_lp_actions), + } +}; + +static struct adie_codec_dev_profile ftm_handset_dual_tx_lp_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_handset_dual_tx_lp_settings, + .setting_sz = ARRAY_SIZE(ftm_handset_dual_tx_lp_settings), +}; + +static struct snddev_icodec_data ftm_handset_dual_tx_lp_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "handset_mic1_handset_mic2", + .copp_id = 1, + .profile = &ftm_handset_dual_tx_lp_profile, + .channel_mode = 2, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_power, + .pamp_off = msm_snddev_disable_amic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_handset_dual_tx_lp_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_handset_dual_tx_lp_data }, +}; + +static struct adie_codec_action_unit ftm_handset_mic_adie_lp_tx_actions[] = + FTM_HANDSET_LB_TX; + +static struct adie_codec_hwsetting_entry + ftm_handset_mic_adie_lp_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_handset_mic_adie_lp_tx_actions, + .action_sz = ARRAY_SIZE(ftm_handset_mic_adie_lp_tx_actions), + } +}; + +static struct adie_codec_dev_profile ftm_handset_mic_adie_lp_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_handset_mic_adie_lp_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_handset_mic_adie_lp_tx_settings), +}; + +static struct snddev_icodec_data ftm_handset_mic_adie_lp_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "ftm_handset_mic_adie_lp_tx", + .copp_id = 1, + .profile = &ftm_handset_mic_adie_lp_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .pamp_on = msm_snddev_enable_amic_power, + .pamp_off = msm_snddev_disable_amic_power, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_handset_mic_adie_lp_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_handset_mic_adie_lp_tx_data }, +}; + +static struct adie_codec_action_unit ftm_headset_mic_adie_lp_tx_actions[] = + FTM_HEADSET_LB_TX; + +static struct adie_codec_hwsetting_entry + ftm_headset_mic_adie_lp_tx_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = ftm_headset_mic_adie_lp_tx_actions, + .action_sz = ARRAY_SIZE(ftm_headset_mic_adie_lp_tx_actions), + } +}; + +static struct adie_codec_dev_profile ftm_headset_mic_adie_lp_tx_profile = { + .path_type = ADIE_CODEC_TX, + .settings = ftm_headset_mic_adie_lp_tx_settings, + .setting_sz = ARRAY_SIZE(ftm_headset_mic_adie_lp_tx_settings), +}; + +static struct snddev_icodec_data ftm_headset_mic_adie_lp_tx_data = { + .capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE), + .name = "ftm_headset_mic_adie_lp_tx", + .copp_id = PRIMARY_I2S_TX, + .profile = &ftm_headset_mic_adie_lp_tx_profile, + .channel_mode = 1, + .default_sample_rate = 48000, + .dev_vol_type = SNDDEV_DEV_VOL_DIGITAL, +}; + +static struct platform_device ftm_headset_mic_adie_lp_tx_device = { + .name = "snddev_icodec", + .dev = { .platform_data = &ftm_headset_mic_adie_lp_tx_data }, +}; +#endif /* CONFIG_MSM8X60_FTM_AUDIO_DEVICES */ + +static struct snddev_virtual_data snddev_uplink_rx_data = { + .capability = SNDDEV_CAP_RX, + .name = "uplink_rx", + .copp_id = VOICE_PLAYBACK_TX, +}; + +static struct platform_device msm_uplink_rx_device = { + .name = "snddev_virtual", + .dev = { .platform_data = &snddev_uplink_rx_data }, +}; + +static struct snddev_hdmi_data snddev_hdmi_non_linear_pcm_rx_data = { + .capability = SNDDEV_CAP_RX , + .name = "hdmi_pass_through", + .default_sample_rate = 48000, + .on_apps = 1, +}; + +static struct platform_device msm_snddev_hdmi_non_linear_pcm_rx_device = { + .name = "snddev_hdmi", + .dev = { .platform_data = &snddev_hdmi_non_linear_pcm_rx_data }, +}; + + +#ifdef CONFIG_DEBUG_FS +static struct adie_codec_action_unit + ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions[] = + HPH_PRI_D_LEG_STEREO; + +static struct adie_codec_hwsetting_entry + ihs_stereo_rx_class_d_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions), + } +}; + +static struct adie_codec_action_unit + ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] = + HPH_PRI_AB_LEG_STEREO; + +static struct adie_codec_hwsetting_entry + ihs_stereo_rx_class_ab_legacy_settings[] = { + { + .freq_plan = 48000, + .osr = 256, + .actions = + ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions, + .action_sz = ARRAY_SIZE + (ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions), + } +}; + +static void snddev_hsed_config_modify_setting(int type) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_headset_stereo_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + if (type == 1) { + icodec_data->voltage_on = NULL; + icodec_data->voltage_off = NULL; + icodec_data->profile->settings = + ihs_stereo_rx_class_d_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_stereo_rx_class_d_legacy_settings); + } else if (type == 2) { + icodec_data->voltage_on = NULL; + icodec_data->voltage_off = NULL; + icodec_data->profile->settings = + ihs_stereo_rx_class_ab_legacy_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(ihs_stereo_rx_class_ab_legacy_settings); + } + } +} + +static void snddev_hsed_config_restore_setting(void) +{ + struct platform_device *device; + struct snddev_icodec_data *icodec_data; + + device = &msm_headset_stereo_device; + icodec_data = (struct snddev_icodec_data *)device->dev.platform_data; + + if (icodec_data) { + icodec_data->voltage_on = msm_snddev_voltage_on; + icodec_data->voltage_off = msm_snddev_voltage_off; + icodec_data->profile->settings = headset_ab_cpls_settings; + icodec_data->profile->setting_sz = + ARRAY_SIZE(headset_ab_cpls_settings); + } +} + +static ssize_t snddev_hsed_config_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char cmd; + + if (get_user(cmd, ubuf)) + return -EFAULT; + + if (!strcmp(lb_str, "msm_hsed_config")) { + switch (cmd) { + case '0': + snddev_hsed_config_restore_setting(); + break; + + case '1': + snddev_hsed_config_modify_setting(1); + break; + + case '2': + snddev_hsed_config_modify_setting(2); + break; + + default: + break; + } + } + return cnt; +} + +static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations snddev_hsed_config_debug_fops = { + .open = snddev_hsed_config_debug_open, + .write = snddev_hsed_config_debug_write +}; +#endif + +static struct platform_device *snd_devices_ffa[] __initdata = { + &msm_iearpiece_ffa_device, + &msm_imic_ffa_device, + &msm_ispkr_stereo_device, + &msm_snddev_hdmi_stereo_rx_device, + &msm_headset_mic_device, + &msm_ispkr_mic_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_headset_stereo_device, + &msm_itty_mono_tx_device, + &msm_itty_mono_rx_device, + &msm_mi2s_fm_tx_device, + &msm_mi2s_fm_rx_device, + &msm_hs_dual_mic_endfire_device, + &msm_spkr_dual_mic_endfire_device, + &msm_hs_dual_mic_broadside_device, + &msm_spkr_dual_mic_broadside_device, + &msm_ihs_stereo_speaker_stereo_rx_device, + &msm_anc_headset_device, + &msm_auxpga_lp_hs_device, + &msm_auxpga_lp_lo_device, + &msm_linein_pri_device, + &msm_icodec_gpio_device, + &msm_snddev_hdmi_non_linear_pcm_rx_device, +}; + +static struct platform_device *snd_devices_surf[] __initdata = { + &msm_iearpiece_device, + &msm_imic_device, + &msm_ispkr_stereo_device, + &msm_snddev_hdmi_stereo_rx_device, + &msm_headset_mic_device, + &msm_ispkr_mic_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_headset_stereo_device, + &msm_itty_mono_tx_device, + &msm_itty_mono_rx_device, + &msm_mi2s_fm_tx_device, + &msm_mi2s_fm_rx_device, + &msm_ihs_stereo_speaker_stereo_rx_device, + &msm_auxpga_lp_hs_device, + &msm_auxpga_lp_lo_device, + &msm_linein_pri_device, + &msm_icodec_gpio_device, + &msm_snddev_hdmi_non_linear_pcm_rx_device, +}; + +static struct platform_device *snd_devices_fluid[] __initdata = { + &msm_iearpiece_device, + &msm_imic_device, + &msm_ispkr_stereo_device, + &msm_snddev_hdmi_stereo_rx_device, + &msm_headset_stereo_device, + &msm_headset_mic_device, + &msm_fluid_ispkr_mic_device, + &msm_bt_sco_earpiece_device, + &msm_bt_sco_mic_device, + &msm_mi2s_fm_tx_device, + &msm_mi2s_fm_rx_device, + &msm_fluid_hs_dual_mic_endfire_device, + &msm_fluid_spkr_dual_mic_endfire_device, + &msm_fluid_hs_dual_mic_broadside_device, + &msm_fluid_spkr_dual_mic_broadside_device, + &msm_anc_headset_device, + &msm_auxpga_lp_hs_device, + &msm_auxpga_lp_lo_device, + &msm_icodec_gpio_device, + &msm_snddev_hdmi_non_linear_pcm_rx_device, +}; + +static struct platform_device *snd_devices_common[] __initdata = { + &msm_aux_pcm_device, + &msm_cdcclk_ctl_device, + &msm_mi2s_device, + &msm_uplink_rx_device, + &msm_device_dspcrashd_8x60, +}; + +#ifdef CONFIG_MSM8X60_FTM_AUDIO_DEVICES +static struct platform_device *snd_devices_ftm[] __initdata = { + &ftm_headset_mono_rx_device, + &ftm_headset_mono_l_rx_device, + &ftm_headset_mono_r_rx_device, + &ftm_headset_mono_diff_rx_device, + &ftm_spkr_mono_rx_device, + &ftm_spkr_l_rx_device, + &ftm_spkr_r_rx_device, + &ftm_spkr_mono_diff_rx_device, + &ftm_linein_l_tx_device, + &ftm_linein_r_tx_device, + &ftm_aux_out_rx_device, + &ftm_dmic1_left_tx_device, + &ftm_dmic1_right_tx_device, + &ftm_dmic1_l_and_r_tx_device, + &ftm_dmic2_left_tx_device, + &ftm_dmic2_right_tx_device, + &ftm_dmic2_l_and_r_tx_device, + &ftm_handset_mic1_aux_in_device, + &ftm_mi2s_sd0_rx_device, + &ftm_mi2s_sd1_rx_device, + &ftm_mi2s_sd2_rx_device, + &ftm_handset_mic_adie_lp_tx_device, + &ftm_headset_mic_adie_lp_tx_device, + &ftm_handset_adie_lp_rx_device, + &ftm_headset_l_adie_lp_rx_device, + &ftm_headset_r_adie_lp_rx_device, + &ftm_spk_l_adie_lp_rx_device, + &ftm_spk_r_adie_lp_rx_device, + &ftm_spk_adie_lp_rx_device, + &ftm_handset_dual_tx_lp_device, +}; +#else +static struct platform_device *snd_devices_ftm[] __initdata = {}; +#endif + + +void __init msm_snddev_init(void) +{ + int i; + int dev_id; + + atomic_set(&pamp_ref_cnt, 0); + atomic_set(&preg_ref_cnt, 0); + + for (i = 0, dev_id = 0; i < ARRAY_SIZE(snd_devices_common); i++) + snd_devices_common[i]->id = dev_id++; + + platform_add_devices(snd_devices_common, + ARRAY_SIZE(snd_devices_common)); + + /* Auto detect device base on machine info */ + if (machine_is_msm8x60_surf() || machine_is_msm8x60_fusion()) { + for (i = 0; i < ARRAY_SIZE(snd_devices_surf); i++) + snd_devices_surf[i]->id = dev_id++; + + platform_add_devices(snd_devices_surf, + ARRAY_SIZE(snd_devices_surf)); + } else if (machine_is_msm8x60_ffa() || + machine_is_msm8x60_fusn_ffa()) { + for (i = 0; i < ARRAY_SIZE(snd_devices_ffa); i++) + snd_devices_ffa[i]->id = dev_id++; + + platform_add_devices(snd_devices_ffa, + ARRAY_SIZE(snd_devices_ffa)); + } else if (machine_is_msm8x60_fluid()) { + for (i = 0; i < ARRAY_SIZE(snd_devices_fluid); i++) + snd_devices_fluid[i]->id = dev_id++; + + platform_add_devices(snd_devices_fluid, + ARRAY_SIZE(snd_devices_fluid)); + } + if (machine_is_msm8x60_surf() || machine_is_msm8x60_ffa() + || machine_is_msm8x60_fusion() + || machine_is_msm8x60_fusn_ffa()) { + for (i = 0; i < ARRAY_SIZE(snd_devices_ftm); i++) + snd_devices_ftm[i]->id = dev_id++; + + platform_add_devices(snd_devices_ftm, + ARRAY_SIZE(snd_devices_ftm)); + } + +#ifdef CONFIG_DEBUG_FS + debugfs_hsed_config = debugfs_create_file("msm_hsed_config", + S_IFREG | S_IRUGO, NULL, + (void *) "msm_hsed_config", &snddev_hsed_config_debug_fops); +#endif +} diff --git a/arch/arm/mach-msm/qdsp6v2/dsp_debug.c b/arch/arm/mach-msm/qdsp6v2/dsp_debug.c new file mode 100644 index 00000000000..3635fbd41e9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/dsp_debug.c @@ -0,0 +1,259 @@ +/* arch/arm/mach-msm/qdsp6/dsp_dump.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static wait_queue_head_t dsp_wait; +static int dsp_has_crashed; +static int dsp_wait_count; + +static atomic_t dsp_crash_count = ATOMIC_INIT(0); +dsp_state_cb cb_ptr; + +void q6audio_dsp_not_responding(void) +{ + if (cb_ptr) + cb_ptr(DSP_STATE_CRASHED); + if (atomic_add_return(1, &dsp_crash_count) != 1) { + pr_err("q6audio_dsp_not_responding() \ + - parking additional crasher...\n"); + for (;;) + msleep(1000); + } + if (dsp_wait_count) { + dsp_has_crashed = 1; + wake_up(&dsp_wait); + + while (dsp_has_crashed != 2) + wait_event(dsp_wait, dsp_has_crashed == 2); + } else { + pr_err("q6audio_dsp_not_responding() - no waiter?\n"); + } + if (cb_ptr) + cb_ptr(DSP_STATE_CRASH_DUMP_DONE); +} + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +#define DSP_NMI_ADDR 0x28800010 + +static ssize_t dsp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + void __iomem *ptr; + void *mem_buffer; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + if (!strcmp(cmd, "wait-for-crash")) { + while (!dsp_has_crashed) { + int res; + dsp_wait_count++; + res = wait_event_interruptible(dsp_wait, + dsp_has_crashed); + if (res < 0) { + dsp_wait_count--; + return res; + } + } + /* assert DSP NMI */ + mem_buffer = ioremap(DSP_NMI_ADDR, 0x16); + if (IS_ERR((void *)mem_buffer)) { + pr_err("%s:map_buffer failed, error = %ld\n", __func__, + PTR_ERR((void *)mem_buffer)); + return -ENOMEM; + } + ptr = mem_buffer; + if (!ptr) { + pr_err("Unable to map DSP NMI\n"); + return -EFAULT; + } + writel(0x1, (void *)ptr); + iounmap(mem_buffer); + } else if (!strcmp(cmd, "boom")) { + q6audio_dsp_not_responding(); + } else if (!strcmp(cmd, "continue-crash")) { + dsp_has_crashed = 2; + wake_up(&dsp_wait); + } else { + pr_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__, + __func__, cmd); + } + + return count; +} + +static unsigned copy_ok_count; +static uint32_t dsp_ram_size; +static uint32_t dsp_ram_base; + +static ssize_t dsp_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + size_t actual = 0; + size_t mapsize = PAGE_SIZE; + unsigned addr; + void __iomem *ptr; + void *mem_buffer; + + if ((dsp_ram_base == 0) || (dsp_ram_size == 0)) { + pr_err("[%s:%s] Memory Invalid or not initialized, Base = 0x%x," + " size = 0x%x\n", __MM_FILE__, + __func__, dsp_ram_base, dsp_ram_size); + return -EINVAL; + } + + if (*pos >= dsp_ram_size) + return 0; + + if (*pos & (PAGE_SIZE - 1)) + return -EINVAL; + + addr = (*pos + dsp_ram_base); + + /* don't blow up if we're unaligned */ + if (addr & (PAGE_SIZE - 1)) + mapsize *= 2; + + while (count >= PAGE_SIZE) { + mem_buffer = ioremap(addr, mapsize); + if (IS_ERR((void *)mem_buffer)) { + pr_err("%s:map_buffer failed, error = %ld\n", + __func__, PTR_ERR((void *)mem_buffer)); + return -ENOMEM; + } + ptr = mem_buffer; + if (!ptr) { + pr_err("[%s:%s] map error @ %x\n", __MM_FILE__, + __func__, addr); + return -EFAULT; + } + if (copy_to_user(buf, ptr, PAGE_SIZE)) { + iounmap(mem_buffer); + pr_err("[%s:%s] copy error @ %p\n", __MM_FILE__, + __func__, buf); + return -EFAULT; + } + copy_ok_count += PAGE_SIZE; + iounmap(mem_buffer); + addr += PAGE_SIZE; + buf += PAGE_SIZE; + actual += PAGE_SIZE; + count -= PAGE_SIZE; + } + + *pos += actual; + return actual; +} + +static int dsp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +int dsp_debug_register(dsp_state_cb ptr) +{ + if (ptr == NULL) + return -EINVAL; + cb_ptr = ptr; + + return 0; +} + +static int dspcrashd_probe(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + int *pdata; + + pdata = pdev->dev.platform_data; + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "msm_dspcrashd"); + if (!res) { + pr_err("%s: failed to get resources for dspcrashd\n", __func__); + return -ENODEV; + } + + dsp_ram_base = res->start; + dsp_ram_size = res->end - res->start; + pr_info("%s: Platform driver values: Base = 0x%x, Size = 0x%x," + "pdata = 0x%x\n", __func__, + dsp_ram_base, dsp_ram_size, *pdata); + return rc; +} + +static const struct file_operations dsp_fops = { + .owner = THIS_MODULE, + .open = dsp_open, + .read = dsp_read, + .write = dsp_write, + .release = dsp_release, +}; + +static struct miscdevice dsp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dsp_debug", + .fops = &dsp_fops, +}; + +static struct platform_driver dspcrashd_driver = { + .probe = dspcrashd_probe, + .driver = { .name = "msm_dspcrashd"} +}; + +static int __init dsp_init(void) +{ + int rc = 0; + init_waitqueue_head(&dsp_wait); + rc = platform_driver_register(&dspcrashd_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for dspcrashd failed\n", + __func__); + } + return misc_register(&dsp_misc); +} + +static int __exit dsp_exit(void) +{ + platform_driver_unregister(&dspcrashd_driver); + return 0; +} + +device_initcall(dsp_init); diff --git a/arch/arm/mach-msm/qdsp6v2/evrc_in.c b/arch/arm/mach-msm/qdsp6v2/evrc_in.c new file mode 100644 index 00000000000..b95d6599b07 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/evrc_in.c @@ -0,0 +1,292 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long evrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_evrc_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* rate_modulation_cmd set to zero + currently not configurable from user space */ + rc = q6asm_enc_cfg_blk_evrc(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd evrc media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_evrc_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) { + rc = -EFAULT; + break; + } + + if (cfg.min_bit_rate > 4 || + cfg.min_bit_rate < 1 || + (cfg.min_bit_rate == 2)) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg.max_bit_rate > 4 || + cfg.max_bit_rate < 1 || + (cfg.max_bit_rate == 2)) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg.min_bit_rate; + enc_cfg->max_bit_rate = cfg.max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x" + "max_bit_rate=0x%x\n", __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int evrc_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_evrc_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for evrc" + "driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 23; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio" + "client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open evrc encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = evrc_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = evrc_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_in_fops, +}; + +static int __init evrc_in_init(void) +{ + return misc_register(&audio_evrc_in_misc); +} + +device_initcall(evrc_in_init); diff --git a/arch/arm/mach-msm/qdsp6v2/fm.c b/arch/arm/mach-msm/qdsp6v2/fm.c new file mode 100644 index 00000000000..9cf2723ccb7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/fm.c @@ -0,0 +1,262 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SESSION_ID_FM (MAX_SESSIONS + 1) +#define FM_ENABLE 0x1 +#define FM_DISABLE 0x0 +#define FM_COPP 0x7 + +struct audio { + struct mutex lock; + + int opened; + int enabled; + int running; + + uint16_t fm_source; + uint16_t fm_src_copp_id; + uint16_t fm_dest; + uint16_t fm_dst_copp_id; + uint16_t dec_id; + uint32_t device_events; + uint16_t volume; +}; + + +static struct audio fm_audio; +static int fm_audio_enable(struct audio *audio) +{ + if (audio->enabled) + return 0; + + pr_info("%s: fm dest= %08x fm_source = %08x\n", __func__, + audio->fm_dst_copp_id, audio->fm_src_copp_id); + + /* do afe loopback here */ + + if (audio->fm_dest && audio->fm_source) { + if (afe_loopback(FM_ENABLE, audio->fm_dst_copp_id, + audio->fm_src_copp_id) < 0) { + pr_err("%s: afe_loopback failed\n", __func__); + } + + audio->running = 1; + } + + audio->enabled = 1; + return 0; +} + +static void fm_audio_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct audio *audio = (struct audio *) private_data; + switch (evt_id) { + case AUDDEV_EVT_DEV_RDY: + pr_info("%s :AUDDEV_EVT_DEV_RDY\n", __func__); + if (evt_payload->routing_id == FM_COPP) { + audio->fm_source = 1; + audio->fm_src_copp_id = FM_COPP; + } else { + audio->fm_dest = 1; + audio->fm_dst_copp_id = evt_payload->routing_id; + } + + if (audio->enabled && + audio->fm_dest && + audio->fm_source) { + + afe_loopback_gain(audio->fm_src_copp_id, + audio->volume); + afe_loopback(FM_ENABLE, audio->fm_dst_copp_id, + audio->fm_src_copp_id); + audio->running = 1; + } + break; + case AUDDEV_EVT_DEV_RLS: + pr_info("%s: AUDDEV_EVT_DEV_RLS\n", __func__); + if (evt_payload->routing_id == audio->fm_src_copp_id) + audio->fm_source = 0; + else + audio->fm_dest = 0; + if (audio->running + && (!audio->fm_dest || !audio->fm_source)) { + afe_loopback(FM_DISABLE, audio->fm_dst_copp_id, + audio->fm_src_copp_id); + audio->running = 0; + } else { + pr_err("%s: device switch happened\n", __func__); + } + break; + case AUDDEV_EVT_STREAM_VOL_CHG: + pr_debug("%s: AUDDEV_EVT_STREAM_VOL_CHG\n", __func__); + if (audio->fm_source) { + audio->volume = evt_payload->session_vol; + afe_loopback_gain(audio->fm_src_copp_id, + audio->volume); + } + break; + + default: + pr_err("%s: ERROR:wrong event %08x\n", __func__, evt_id); + break; + } +} + +static int fm_audio_disable(struct audio *audio) +{ + + /* break the AFE loopback here */ + afe_loopback(FM_DISABLE, audio->fm_dst_copp_id, audio->fm_src_copp_id); + return 0; +} + +static long fm_audio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = -EINVAL; + + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + pr_info("%s: AUDIO_START\n", __func__); + rc = fm_audio_enable(audio); + break; + case AUDIO_STOP: + pr_info("%s: AUDIO_STOP\n", __func__); + rc = fm_audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + break; + case AUDIO_GET_SESSION_ID: + if (copy_to_user((void *) arg, &audio->dec_id, + sizeof(unsigned short))) + rc = -EFAULT; + else + rc = 0; + break; + default: + rc = -EINVAL; + pr_err("%s: Un supported IOCTL\n", __func__); + } + mutex_unlock(&audio->lock); + return rc; +} + +static int fm_audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + pr_debug("audio instance 0x%08x freeing\n", (int)audio); + mutex_lock(&audio->lock); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id); + fm_audio_disable(audio); + audio->running = 0; + audio->enabled = 0; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static int fm_audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &fm_audio; + int rc = 0; + + + if (audio->opened) + return -EPERM; + + /* Allocate the decoder */ + audio->dec_id = SESSION_ID_FM; + + audio->running = 0; + audio->fm_source = 0; + audio->fm_dest = 0; + + audio->device_events = AUDDEV_EVT_DEV_RDY + |AUDDEV_EVT_DEV_RLS| + AUDDEV_EVT_STREAM_VOL_CHG; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->dec_id, + fm_audio_listner, + (void *)audio); + + if (rc) { + pr_err("%s: failed to register listnet\n", __func__); + goto event_err; + } + + audio->opened = 1; + file->private_data = audio; + +event_err: + return rc; +} + +static const struct file_operations audio_fm_fops = { + .owner = THIS_MODULE, + .open = fm_audio_open, + .release = fm_audio_release, + .unlocked_ioctl = fm_audio_ioctl, +}; + +struct miscdevice audio_fm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_fm", + .fops = &audio_fm_fops, +}; + +static int __init fm_audio_init(void) +{ + struct audio *audio = &fm_audio; + + mutex_init(&audio->lock); + return misc_register(&audio_fm_misc); +} + +device_initcall(fm_audio_init); + +MODULE_DESCRIPTION("MSM FM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c b/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c new file mode 100644 index 00000000000..c19fd85db3f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c @@ -0,0 +1,464 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6core.h" + +#define DMA_ALLOC_BUF_SZ (SZ_4K * 16) + +#define HDMI_AUDIO_FIFO_WATER_MARK 4 + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t size; + uint32_t used; /* 1 = CPU is waiting for DMA to consume this buf */ + uint32_t actual_size; /* actual number of bytes read by DMA */ +}; + +struct lpa_if { + struct mutex lock; + struct msm_audio_config cfg; + struct audio_buffer audio_buf[6]; + int cpu_buf; /* next buffer the CPU will touch */ + int dma_buf; /* next buffer the DMA will touch */ + u8 *buffer; + dma_addr_t buffer_phys; + u32 dma_ch; + wait_queue_head_t wait; + u32 config; + u32 dma_period_sz; + unsigned int num_periods; +}; + +static struct lpa_if *lpa_if_ptr; + +static unsigned int dma_buf_index; + +static irqreturn_t lpa_if_irq(int intrsrc, void *data) +{ + struct lpa_if *lpa_if = data; + int dma_ch = 0; + unsigned int pending; + + if (lpa_if) + dma_ch = lpa_if->dma_ch; + else { + pr_err("invalid lpa_if\n"); + return IRQ_NONE; + } + + pending = (intrsrc + & (UNDER_CH(dma_ch) | PER_CH(dma_ch) | ERR_CH(dma_ch))); + + if (pending & UNDER_CH(dma_ch)) + pr_err("under run\n"); + if (pending & ERR_CH(dma_ch)) + pr_err("DMA %x Master Error\n", dma_ch); + + if (pending & PER_CH(dma_ch)) { + + lpa_if->audio_buf[lpa_if->dma_buf].used = 0; + + pr_debug("dma_buf %d used %d\n", lpa_if->dma_buf, + lpa_if->audio_buf[lpa_if->dma_buf].used); + lpa_if->dma_buf++; + lpa_if->dma_buf = lpa_if->dma_buf % lpa_if->cfg.buffer_count; + + if (lpa_if->dma_buf == lpa_if->cpu_buf) + pr_err("Err:both dma_buf and cpu_buf are on same index\n"); + wake_up(&lpa_if->wait); + } + return IRQ_HANDLED; +} + + +int lpa_if_start(struct lpa_if *lpa_if) +{ + pr_debug("buf1 0x%x, buf2 0x%x dma_ch %d\n", + (unsigned int)lpa_if->audio_buf[0].data, + (unsigned int)lpa_if->audio_buf[1].data, lpa_if->dma_ch); + + dai_start_hdmi(lpa_if->dma_ch); + + hdmi_audio_enable(1, HDMI_AUDIO_FIFO_WATER_MARK); + + hdmi_audio_packet_enable(1); + return 0; +} + +int lpa_if_config(struct lpa_if *lpa_if) +{ + struct dai_dma_params dma_params; + + dma_params.src_start = lpa_if->buffer_phys; + dma_params.buffer = lpa_if->buffer; + dma_params.buffer_size = lpa_if->dma_period_sz * lpa_if->num_periods; + dma_params.period_size = lpa_if->dma_period_sz; + dma_params.channels = 2; + + lpa_if->dma_ch = 4; + dai_set_params(lpa_if->dma_ch, &dma_params); + + register_dma_irq_handler(lpa_if->dma_ch, lpa_if_irq, (void *)lpa_if); + + mb(); + pr_debug("lpa_if 0x%08x buf_vir 0x%08x buf_phys 0x%08x " + "config %u\n", (u32)lpa_if, (u32) (lpa_if->buffer), + lpa_if->buffer_phys, lpa_if->config); + + pr_debug("user_buf_cnt %u user_buf_size %u\n", + lpa_if->cfg.buffer_count, lpa_if->cfg.buffer_size); + + lpa_if->config = 1; + + lpa_if_start(lpa_if); + + return 0; +} + + +static long lpa_if_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct lpa_if *lpa_if = file->private_data; + int rc = 0; + unsigned int i; + pr_debug("cmd %u\n", cmd); + + mutex_lock(&lpa_if->lock); + + switch (cmd) { + case AUDIO_START: + pr_debug("AUDIO_START\n"); + + if (dma_buf_index == 2) { + if (!lpa_if->config) { + rc = lpa_if_config(lpa_if); + if (rc) + pr_err("lpa_if_config failed\n"); + } + } else { + pr_err("did not receved two buffer for " + "AUDIO_STAR\n"); + rc = -EPERM; + } + break; + + case AUDIO_STOP: + pr_debug("AUDIO_STOP\n"); + break; + + case AUDIO_FLUSH: + pr_debug("AUDIO_FLUSH\n"); + break; + + + case AUDIO_GET_CONFIG: + pr_debug("AUDIO_GET_CONFIG\n"); + if (copy_to_user((void *)arg, &lpa_if->cfg, + sizeof(struct msm_audio_config))) { + rc = -EFAULT; + } + break; + case AUDIO_SET_CONFIG: { + /* Setting default rate as 48khz */ + unsigned int cur_sample_rate = + HDMI_SAMPLE_RATE_48KHZ; + struct msm_audio_config config; + + pr_debug("AUDIO_SET_CONFIG\n"); + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + lpa_if->dma_period_sz = config.buffer_size; + if ((lpa_if->dma_period_sz * lpa_if->num_periods) > + DMA_ALLOC_BUF_SZ) { + pr_err("Dma buffer size greater than allocated size\n"); + return -EINVAL; + } + pr_debug("Dma_period_sz %d\n", lpa_if->dma_period_sz); + if (lpa_if->dma_period_sz < (2 * SZ_4K)) + lpa_if->num_periods = 6; + pr_debug("No. of Periods %d\n", lpa_if->num_periods); + + lpa_if->cfg.buffer_count = lpa_if->num_periods; + lpa_if->cfg.buffer_size = lpa_if->dma_period_sz * + lpa_if->num_periods; + + for (i = 0; i < lpa_if->cfg.buffer_count; i++) { + lpa_if->audio_buf[i].phys = + lpa_if->buffer_phys + i * lpa_if->dma_period_sz; + lpa_if->audio_buf[i].data = + lpa_if->buffer + i * lpa_if->dma_period_sz; + lpa_if->audio_buf[i].size = lpa_if->dma_period_sz; + lpa_if->audio_buf[i].used = 0; + } + + pr_debug("Sample rate %d\n", config.sample_rate); + switch (config.sample_rate) { + case 48000: + cur_sample_rate = HDMI_SAMPLE_RATE_48KHZ; + break; + case 44100: + cur_sample_rate = HDMI_SAMPLE_RATE_44_1KHZ; + break; + case 32000: + cur_sample_rate = HDMI_SAMPLE_RATE_32KHZ; + break; + case 88200: + cur_sample_rate = HDMI_SAMPLE_RATE_88_2KHZ; + break; + case 96000: + cur_sample_rate = HDMI_SAMPLE_RATE_96KHZ; + break; + case 176400: + cur_sample_rate = HDMI_SAMPLE_RATE_176_4KHZ; + break; + case 192000: + cur_sample_rate = HDMI_SAMPLE_RATE_192KHZ; + break; + default: + cur_sample_rate = HDMI_SAMPLE_RATE_48KHZ; + } + if (cur_sample_rate != hdmi_msm_audio_get_sample_rate()) + hdmi_msm_audio_sample_rate_reset(cur_sample_rate); + else + pr_debug("Previous sample rate and current" + "sample rate are same\n"); + break; + } + default: + pr_err("UnKnown Ioctl\n"); + rc = -EINVAL; + } + + mutex_unlock(&lpa_if->lock); + + return rc; +} + + +static int lpa_if_open(struct inode *inode, struct file *file) +{ + pr_debug("\n"); + + file->private_data = lpa_if_ptr; + dma_buf_index = 0; + lpa_if_ptr->cpu_buf = 2; + lpa_if_ptr->dma_buf = 0; + lpa_if_ptr->num_periods = 4; + + core_req_bus_bandwith(AUDIO_IF_BUS_ID, 100000, 0); + mb(); + + return 0; +} + +static inline int rt_policy(int policy) +{ + if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR)) + return 1; + return 0; +} + +static inline int task_has_rt_policy(struct task_struct *p) +{ + return rt_policy(p->policy); +} +static ssize_t lpa_if_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct lpa_if *lpa_if = file->private_data; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer, rc; + struct sched_param s = { .sched_priority = 1 }; + int old_prio = current->rt_priority; + int old_policy = current->policy; + int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE); + + /* just for this write, set us real-time */ + if (!task_has_rt_policy(current)) { + struct cred *new = prepare_creds(); + cap_raise(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + if ((sched_setscheduler(current, SCHED_RR, &s)) < 0) + pr_err("sched_setscheduler failed\n"); + } + mutex_lock(&lpa_if->lock); + + if (dma_buf_index < 2) { + + ab = lpa_if->audio_buf + dma_buf_index; + + if (copy_from_user(ab->data, buf, count)) { + pr_err("copy from user failed\n"); + rc = 0; + goto end; + + } + mb(); + pr_debug("prefill: count %u audio_buf[%u].size %u\n", + count, dma_buf_index, ab->size); + + ab->used = 1; + dma_buf_index++; + rc = count; + goto end; + } + + if (lpa_if->config != 1) { + pr_err("AUDIO_START did not happen\n"); + rc = 0; + goto end; + } + + while (count > 0) { + + ab = lpa_if->audio_buf + lpa_if->cpu_buf; + + rc = wait_event_timeout(lpa_if->wait, (ab->used == 0), 10 * HZ); + if (!rc) { + pr_err("wait_event_timeout failed\n"); + rc = buf - start; + goto end; + } + + xfer = count; + + if (xfer > lpa_if->dma_period_sz) + xfer = lpa_if->dma_period_sz; + + if (copy_from_user(ab->data, buf, xfer)) { + pr_err("copy from user failed\n"); + rc = buf - start; + goto end; + } + + mb(); + buf += xfer; + count -= xfer; + ab->used = 1; + + pr_debug("xfer %d, size %d, used %d cpu_buf %d\n", + xfer, ab->size, ab->used, lpa_if->cpu_buf); + lpa_if->cpu_buf++; + lpa_if->cpu_buf = lpa_if->cpu_buf % lpa_if->cfg.buffer_count; + } + rc = buf - start; +end: + mutex_unlock(&lpa_if->lock); + /* restore old scheduling policy */ + if (!rt_policy(old_policy)) { + struct sched_param v = { .sched_priority = old_prio }; + if ((sched_setscheduler(current, old_policy, &v)) < 0) + pr_err("sched_setscheduler failed\n"); + if (likely(!cap_nice)) { + struct cred *new = prepare_creds(); + cap_lower(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + } + } + return rc; +} + +static int lpa_if_release(struct inode *inode, struct file *file) +{ + struct lpa_if *lpa_if = file->private_data; + + hdmi_audio_packet_enable(0); + + wait_for_dma_cnt_stop(lpa_if->dma_ch); + + hdmi_audio_enable(0, HDMI_AUDIO_FIFO_WATER_MARK); + + if (lpa_if->config) { + unregister_dma_irq_handler(lpa_if->dma_ch); + dai_stop_hdmi(lpa_if->dma_ch); + lpa_if->config = 0; + } + core_req_bus_bandwith(AUDIO_IF_BUS_ID, 0, 0); + + if (hdmi_msm_audio_get_sample_rate() != HDMI_SAMPLE_RATE_48KHZ) + hdmi_msm_audio_sample_rate_reset(HDMI_SAMPLE_RATE_48KHZ); + + return 0; +} + +static const struct file_operations lpa_if_fops = { + .owner = THIS_MODULE, + .open = lpa_if_open, + .write = lpa_if_write, + .release = lpa_if_release, + .unlocked_ioctl = lpa_if_ioctl, +}; + +struct miscdevice lpa_if_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_lpa_if_out", + .fops = &lpa_if_fops, +}; + +static int __init lpa_if_init(void) +{ + int rc; + + lpa_if_ptr = kzalloc(sizeof(struct lpa_if), GFP_KERNEL); + if (!lpa_if_ptr) { + pr_info("No mem for lpa-if\n"); + return -ENOMEM; + } + + mutex_init(&lpa_if_ptr->lock); + init_waitqueue_head(&lpa_if_ptr->wait); + + lpa_if_ptr->buffer = dma_alloc_coherent(NULL, DMA_ALLOC_BUF_SZ, + &(lpa_if_ptr->buffer_phys), GFP_KERNEL); + if (!lpa_if_ptr->buffer) { + pr_err("dma_alloc_coherent failed\n"); + kfree(lpa_if_ptr); + return -ENOMEM; + } + + pr_info("lpa_if_ptr 0x%08x buf_vir 0x%08x buf_phy 0x%08x " + " buf_zise %u\n", (u32)lpa_if_ptr, + (u32)(lpa_if_ptr->buffer), lpa_if_ptr->buffer_phys, + DMA_ALLOC_BUF_SZ); + + rc = misc_register(&lpa_if_misc); + if (rc < 0) { + pr_err("misc_register failed\n"); + + dma_free_coherent(NULL, DMA_ALLOC_BUF_SZ, lpa_if_ptr->buffer, + lpa_if_ptr->buffer_phys); + kfree(lpa_if_ptr); + } + return rc; +} + +device_initcall(lpa_if_init); diff --git a/arch/arm/mach-msm/qdsp6v2/msm_qdsp6_audio.h b/arch/arm/mach-msm/qdsp6v2/msm_qdsp6_audio.h new file mode 100644 index 00000000000..3890816c3d8 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/msm_qdsp6_audio.h @@ -0,0 +1,52 @@ +/* arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MACH_MSM_QDSP6_Q6AUDIO_ +#define _MACH_MSM_QDSP6_Q6AUDIO_ + +#define AUDIO_FLAG_READ 0 +#define AUDIO_FLAG_WRITE 1 +#define AUDIO_FLAG_INCALL_MIXED 2 + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t size; + uint32_t used; /* 1 = CPU is waiting for DSP to consume this buf */ + uint32_t actual_size; /* actual number of bytes read by DSP */ +}; + +struct audio_client { + struct audio_buffer buf[2]; + int cpu_buf; /* next buffer the CPU will touch */ + int dsp_buf; /* next buffer the DSP will touch */ + int running; + int session; + + int state; + + wait_queue_head_t wait; + wait_queue_head_t cmd_wait; + + struct dal_client *client; + + int cb_status; + uint32_t flags; + void *apr; + int ref_count; +}; + +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/pcm_in.c b/arch/arm/mach-msm/qdsp6v2/pcm_in.c new file mode 100644 index 00000000000..620c1ee4b35 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/pcm_in.c @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BUF 4 +#define BUFSZ (480 * 8) +#define BUFFER_SIZE_MULTIPLE 4 +#define MIN_BUFFER_SIZE 160 + +#define VOC_REC_NONE 0xFF + +struct pcm { + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + spinlock_t dsp_lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t rec_mode; + uint32_t in_frame_info[MAX_BUF][2]; + atomic_t in_count; + atomic_t in_enabled; + atomic_t in_opened; + atomic_t in_stopped; + struct wake_lock wakelock; + struct pm_qos_request pm_qos_req; +}; + +static void pcm_in_get_dsp_buffers(struct pcm*, + uint32_t token, uint32_t *payload); + +void pcm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct pcm *pcm = (struct pcm *) priv; + unsigned long flags; + + spin_lock_irqsave(&pcm->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + pcm_in_get_dsp_buffers(pcm, token, payload); + break; + case RESET_EVENTS: + reset_device(); + break; + default: + break; + } + spin_unlock_irqrestore(&pcm->dsp_lock, flags); +} +static void pcm_in_prevent_sleep(struct pcm *audio) +{ + pr_debug("%s:\n", __func__); + wake_lock(&audio->wakelock); + pm_qos_update_request(&audio->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); +} + +static void pcm_in_allow_sleep(struct pcm *audio) +{ + pr_debug("%s:\n", __func__); + pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&audio->wakelock); +} + +static void pcm_in_get_dsp_buffers(struct pcm *pcm, + uint32_t token, uint32_t *payload) +{ + pcm->in_frame_info[token][0] = payload[7]; + pcm->in_frame_info[token][1] = payload[3]; + if (atomic_read(&pcm->in_count) <= pcm->buffer_count) + atomic_inc(&pcm->in_count); + wake_up(&pcm->wait); +} + +static int pcm_in_enable(struct pcm *pcm) +{ + if (atomic_read(&pcm->in_enabled)) + return 0; + return q6asm_run(pcm->ac, 0, 0, 0); +} + +static int pcm_in_disable(struct pcm *pcm) +{ + int rc = 0; + + if (atomic_read(&pcm->in_opened)) { + atomic_set(&pcm->in_enabled, 0); + atomic_set(&pcm->in_opened, 0); + rc = q6asm_cmd(pcm->ac, CMD_CLOSE); + + atomic_set(&pcm->in_stopped, 1); + memset(pcm->in_frame_info, 0, + sizeof(char) * pcm->buffer_count * 2); + wake_up(&pcm->wait); + } + return rc; +} + +static int config(struct pcm *pcm) +{ + int rc = 0; + + pr_debug("%s: pcm prefill, buffer_size = %d\n", __func__, + pcm->buffer_size); + rc = q6asm_audio_client_buf_alloc(OUT, pcm->ac, + pcm->buffer_size, pcm->buffer_count); + if (rc < 0) { + pr_err("Audio Start: Buffer Allocation failed \ + rc = %d\n", rc); + goto fail; + } + + rc = q6asm_enc_cfg_blk_pcm(pcm->ac, pcm->sample_rate, + pcm->channel_count); + if (rc < 0) { + pr_err("%s: cmd media format block failed", __func__); + goto fail; + } +fail: + return rc; +} + +static long pcm_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + int rc = 0; + + mutex_lock(&pcm->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + rc = -EFAULT; + break; + } + case AUDIO_START: { + int cnt = 0; + if (atomic_read(&pcm->in_enabled)) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = config(pcm); + if (rc) { + pr_err("%s: IN Configuration failed\n", __func__); + rc = -EFAULT; + break; + } + + rc = pcm_in_enable(pcm); + if (rc) { + pr_err("%s: In Enable failed\n", __func__); + rc = -EFAULT; + break; + } + pcm_in_prevent_sleep(pcm); + atomic_set(&pcm->in_enabled, 1); + + while (cnt++ < pcm->buffer_count) + q6asm_read(pcm->ac); + pr_info("%s: AUDIO_START session id[%d]\n", __func__, + pcm->ac->session); + + if (pcm->rec_mode != VOC_REC_NONE) + msm_enable_incall_recording(pcm->ac->session, + pcm->rec_mode, pcm->sample_rate, pcm->channel_count); + + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &pcm->ac->session, + sizeof(unsigned short))) + rc = -EFAULT; + break; + } + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + pr_debug("%s: SET_CONFIG: buffer_size:%d channel_count:%d" + "sample_rate:%d, buffer_count:%d\n", __func__, + config.buffer_size, config.channel_count, + config.sample_rate, config.buffer_count); + + if (!config.channel_count || config.channel_count > 2) { + rc = -EINVAL; + break; + } + + if (config.sample_rate < 8000 || config.sample_rate > 48000) { + rc = -EINVAL; + break; + } + + if ((config.buffer_size % (config.channel_count * + BUFFER_SIZE_MULTIPLE)) || + (config.buffer_size < MIN_BUFFER_SIZE)) { + pr_err("%s: Buffer Size should be multiple of " + "[4 * no. of channels] and greater than 160\n", + __func__); + rc = -EINVAL; + break; + } + + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + pcm->buffer_count = config.buffer_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = pcm->buffer_size; + config.buffer_count = pcm->buffer_count; + config.sample_rate = pcm->sample_rate; + config.channel_count = pcm->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + break; + } + case AUDIO_ENABLE_AUDPRE: { + + uint16_t enable_mask; + + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) { + rc = -EFAULT; + break; + } + if (enable_mask & FLUENCE_ENABLE) + rc = auddev_cfg_tx_copp_topology(pcm->ac->session, + VPM_TX_DM_FLUENCE_COPP_TOPOLOGY); + else + rc = auddev_cfg_tx_copp_topology(pcm->ac->session, + DEFAULT_COPP_TOPOLOGY); + break; + } + + case AUDIO_SET_INCALL: { + if (copy_from_user(&pcm->rec_mode, + (void *) arg, + sizeof(pcm->rec_mode))) { + rc = -EFAULT; + pr_err("%s: Error copying in-call mode\n", __func__); + break; + } + + if (pcm->rec_mode != VOC_REC_UPLINK && + pcm->rec_mode != VOC_REC_DOWNLINK && + pcm->rec_mode != VOC_REC_BOTH) { + rc = -EINVAL; + pcm->rec_mode = VOC_REC_NONE; + + pr_err("%s: Invalid %d in-call rec_mode\n", + __func__, pcm->rec_mode); + break; + } + + pr_debug("%s: In-call rec_mode %d\n", __func__, pcm->rec_mode); + break; + } + + default: + rc = -EINVAL; + break; + } + mutex_unlock(&pcm->lock); + return rc; +} + +static int pcm_in_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + int rc = 0; + char name[24]; + + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->channel_count = 1; + pcm->sample_rate = 8000; + pcm->buffer_size = BUFSZ; + pcm->buffer_count = MAX_BUF; + + pcm->ac = q6asm_audio_client_alloc((app_cb)pcm_in_cb, (void *)pcm); + if (!pcm->ac) { + pr_err("%s: Could not allocate memory\n", __func__); + rc = -ENOMEM; + goto fail; + } + + mutex_init(&pcm->lock); + mutex_init(&pcm->read_lock); + spin_lock_init(&pcm->dsp_lock); + init_waitqueue_head(&pcm->wait); + + rc = q6asm_open_read(pcm->ac, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s: Cmd Open Failed\n", __func__); + goto fail; + } + + atomic_set(&pcm->in_stopped, 0); + atomic_set(&pcm->in_enabled, 0); + atomic_set(&pcm->in_count, 0); + atomic_set(&pcm->in_opened, 1); + snprintf(name, sizeof name, "pcm_in_%x", pcm->ac->session); + wake_lock_init(&pcm->wakelock, WAKE_LOCK_SUSPEND, name); + snprintf(name, sizeof name, "pcm_in_idle_%x", pcm->ac->session); + pm_qos_add_request(&pcm->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + pcm->rec_mode = VOC_REC_NONE; + + file->private_data = pcm; + pr_info("%s: pcm in open session id[%d]\n", __func__, pcm->ac->session); + return 0; +fail: + if (pcm->ac) + q6asm_audio_client_free(pcm->ac); + kfree(pcm); + return rc; +} + +static ssize_t pcm_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + const char __user *start = buf; + void *data; + uint32_t offset = 0; + uint32_t size = 0; + uint32_t idx; + int rc = 0; + int len = 0; + + if (!atomic_read(&pcm->in_enabled)) + return -EFAULT; + mutex_lock(&pcm->read_lock); + while (count > 0) { + rc = wait_event_timeout(pcm->wait, + (atomic_read(&pcm->in_count) || + atomic_read(&pcm->in_stopped)), 5 * HZ); + if (!rc) { + pr_err("%s: wait_event_timeout failed\n", __func__); + goto fail; + } + + if (atomic_read(&pcm->in_stopped) && + !atomic_read(&pcm->in_count)) { + mutex_unlock(&pcm->read_lock); + return 0; + } + + data = q6asm_is_cpu_buf_avail(OUT, pcm->ac, &size, &idx); + if (count >= size) + len = size; + else { + len = count; + pr_err("%s: short read data[%p]bytesavail[%d]" + "bytesrequest[%d]" + "bytesrejected%d]\n",\ + __func__, data, size, + count, (size - count)); + } + if ((len) && data) { + offset = pcm->in_frame_info[idx][1]; + if (copy_to_user(buf, data+offset, len)) { + pr_err("%s copy_to_user failed len[%d]\n", + __func__, len); + rc = -EFAULT; + goto fail; + } + count -= len; + buf += len; + } + atomic_dec(&pcm->in_count); + memset(&pcm->in_frame_info[idx], 0, + sizeof(uint32_t) * 2); + + rc = q6asm_read(pcm->ac); + if (rc < 0) { + pr_err("%s q6asm_read fail\n", __func__); + goto fail; + } + rmb(); + break; + } + rc = buf-start; +fail: + mutex_unlock(&pcm->read_lock); + return rc; +} + +static int pcm_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + struct pcm *pcm = file->private_data; + + pr_info("[%s:%s] release session id[%d]\n", __MM_FILE__, + __func__, pcm->ac->session); + mutex_lock(&pcm->lock); + + if ((pcm->rec_mode != VOC_REC_NONE) && atomic_read(&pcm->in_enabled)) { + msm_disable_incall_recording(pcm->ac->session, pcm->rec_mode); + + pcm->rec_mode = VOC_REC_NONE; + } + + /* remove this session from topology list */ + auddev_cfg_tx_copp_topology(pcm->ac->session, + DEFAULT_COPP_TOPOLOGY); + mutex_unlock(&pcm->lock); + + rc = pcm_in_disable(pcm); + msm_clear_session_id(pcm->ac->session); + q6asm_audio_client_free(pcm->ac); + pcm_in_allow_sleep(pcm); + wake_lock_destroy(&pcm->wakelock); + pm_qos_remove_request(&pcm->pm_qos_req); + kfree(pcm); + return rc; +} + +static const struct file_operations pcm_in_fops = { + .owner = THIS_MODULE, + .open = pcm_in_open, + .read = pcm_in_read, + .release = pcm_in_release, + .unlocked_ioctl = pcm_in_ioctl, +}; + +struct miscdevice pcm_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &pcm_in_fops, +}; + +static int __init pcm_in_init(void) +{ + return misc_register(&pcm_in_misc); +} + +device_initcall(pcm_in_init); diff --git a/arch/arm/mach-msm/qdsp6v2/pcm_out.c b/arch/arm/mach-msm/qdsp6v2/pcm_out.c new file mode 100644 index 00000000000..733d5e334af --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/pcm_out.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BUF 2 +#define BUFSZ (4800) + +struct pcm { + struct mutex lock; + struct mutex write_lock; + spinlock_t dsp_lock; + wait_queue_head_t write_wait; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t rec_mode; + uint32_t stream_event; + uint32_t volume; + atomic_t out_count; + atomic_t out_enabled; + atomic_t out_opened; + atomic_t out_stopped; + atomic_t out_prefill; + struct wake_lock wakelock; +}; + +void pcm_out_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct pcm *pcm = (struct pcm *) priv; + unsigned long flags; + + spin_lock_irqsave(&pcm->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&pcm->out_count); + wake_up(&pcm->write_wait); + break; + case RESET_EVENTS: + reset_device(); + break; + default: + break; + } + spin_unlock_irqrestore(&pcm->dsp_lock, flags); +} + +static void audio_prevent_sleep(struct pcm *audio) +{ + pr_debug("%s:\n", __func__); + wake_lock(&audio->wakelock); +} + +static void audio_allow_sleep(struct pcm *audio) +{ + pr_debug("%s:\n", __func__); + wake_unlock(&audio->wakelock); +} + +static int pcm_out_enable(struct pcm *pcm) +{ + if (atomic_read(&pcm->out_enabled)) + return 0; + return q6asm_run(pcm->ac, 0, 0, 0); +} + +static int pcm_out_disable(struct pcm *pcm) +{ + int rc = 0; + + if (atomic_read(&pcm->out_opened)) { + atomic_set(&pcm->out_enabled, 0); + atomic_set(&pcm->out_opened, 0); + rc = q6asm_cmd(pcm->ac, CMD_CLOSE); + + atomic_set(&pcm->out_stopped, 1); + wake_up(&pcm->write_wait); + } + return rc; +} + +static int config(struct pcm *pcm) +{ + int rc = 0; + if (!atomic_read(&pcm->out_prefill)) { + pr_debug("%s: pcm prefill\n", __func__); + rc = q6asm_audio_client_buf_alloc(IN, pcm->ac, + pcm->buffer_size, pcm->buffer_count); + if (rc < 0) { + pr_err("Audio Start: Buffer Allocation failed \ + rc = %d\n", rc); + goto fail; + } + + rc = q6asm_media_format_block_pcm(pcm->ac, pcm->sample_rate, + pcm->channel_count); + if (rc < 0) + pr_err("%s: CMD Format block failed\n", __func__); + + atomic_set(&pcm->out_prefill, 1); + atomic_set(&pcm->out_count, pcm->buffer_count); + } +fail: + return rc; +} + +static void pcm_event_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct pcm *pcm = (struct pcm *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + pcm->volume = evt_payload->session_vol; + pr_debug("%s: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, " + "enabled = %d\n", __func__, pcm->volume, + atomic_read(&pcm->out_enabled)); + if (atomic_read(&pcm->out_enabled)) { + if (pcm->ac) { + rc = q6asm_set_volume(pcm->ac, pcm->volume); + if (rc < 0) + pr_err("%s: Send Volume command" + "failed rc=%d\n", __func__, rc); + } + } + break; + default: + pr_err("%s:ERROR:wrong event\n", __func__); + break; + } +} + +static long pcm_out_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct pcm *pcm = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&pcm->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: { + int vol; + if (copy_from_user(&vol, (void *) arg, sizeof(vol))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_START: { + pr_info("%s: AUDIO_START\n", __func__); + rc = config(pcm); + if (rc) { + pr_err("%s: Out Configuration failed\n", __func__); + rc = -EFAULT; + break; + } + + rc = pcm_out_enable(pcm); + if (rc) { + pr_err("Out enable failed\n"); + rc = -EFAULT; + break; + } + audio_prevent_sleep(pcm); + atomic_set(&pcm->out_enabled, 1); + + rc = q6asm_set_volume(pcm->ac, pcm->volume); + if (rc < 0) + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + rc = q6asm_set_lrgain(pcm->ac, 0x2000, 0x2000); + if (rc < 0) + pr_err("%s: Send channel gain failed rc=%d\n", + __func__, rc); + /* disable mute by default */ + rc = q6asm_set_mute(pcm->ac, 0); + if (rc < 0) + pr_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &pcm->ac->session, + sizeof(unsigned short))) + rc = -EFAULT; + break; + } + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + pr_debug("%s: AUDIO_SET_CONFIG\n", __func__); + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count < 1 || config.channel_count > 2) { + rc = -EINVAL; + break; + } + if (config.sample_rate < 8000 || config.sample_rate > 48000) { + rc = -EINVAL; + break; + } + if (config.buffer_size < 128) { + rc = -EINVAL; + break; + } + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + pcm->buffer_count = config.buffer_count; + pr_debug("%s:buffer_size:%d buffer_count:%d sample_rate:%d \ + channel_count:%d\n", __func__, pcm->buffer_size, + pcm->buffer_count, pcm->sample_rate, + pcm->channel_count); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + pr_debug("%s: AUDIO_GET_CONFIG\n", __func__); + config.buffer_size = pcm->buffer_size; + config.buffer_count = pcm->buffer_count; + config.sample_rate = pcm->sample_rate; + config.channel_count = pcm->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EQ: { + struct msm_audio_eq_stream_config eq_config; + if (copy_from_user(&eq_config, (void *) arg, + sizeof(eq_config))) { + rc = -EFAULT; + break; + } + rc = q6asm_equalizer(pcm->ac, (void *) &eq_config); + if (rc < 0) + pr_err("%s: EQUALIZER FAILED\n", __func__); + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&pcm->lock); + return rc; +} + +static int pcm_out_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + int rc = 0; + char name[24]; + + pr_info("[%s:%s] open\n", __MM_FILE__, __func__); + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + if (!pcm) { + pr_err("%s: Failed to allocated memory\n", __func__); + return -ENOMEM; + } + + pcm->channel_count = 2; + pcm->sample_rate = 44100; + pcm->buffer_size = BUFSZ; + pcm->buffer_count = MAX_BUF; + pcm->stream_event = AUDDEV_EVT_STREAM_VOL_CHG; + pcm->volume = 0x2000; + + pcm->ac = q6asm_audio_client_alloc((app_cb)pcm_out_cb, (void *)pcm); + if (!pcm->ac) { + pr_err("%s: Could not allocate memory\n", __func__); + rc = -ENOMEM; + goto fail; + } + + rc = q6asm_open_write(pcm->ac, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s: pcm out open failed for session %d\n", __func__, + pcm->ac->session); + rc = -EINVAL; + goto fail; + } + + mutex_init(&pcm->lock); + mutex_init(&pcm->write_lock); + init_waitqueue_head(&pcm->write_wait); + spin_lock_init(&pcm->dsp_lock); + atomic_set(&pcm->out_enabled, 0); + atomic_set(&pcm->out_stopped, 0); + atomic_set(&pcm->out_count, pcm->buffer_count); + atomic_set(&pcm->out_prefill, 0); + atomic_set(&pcm->out_opened, 1); + snprintf(name, sizeof name, "audio_pcm_%x", pcm->ac->session); + wake_lock_init(&pcm->wakelock, WAKE_LOCK_SUSPEND, name); + + rc = auddev_register_evt_listner(pcm->stream_event, + AUDDEV_CLNT_DEC, + pcm->ac->session, + pcm_event_listner, + (void *)pcm); + if (rc < 0) { + pr_err("%s: failed to register listner\n", __func__); + goto fail; + } + + file->private_data = pcm; + pr_info("[%s:%s] open session id[%d]\n", __MM_FILE__, + __func__, pcm->ac->session); + return 0; +fail: + if (pcm->ac) + q6asm_audio_client_free(pcm->ac); + kfree(pcm); + return rc; +} + +static ssize_t pcm_out_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + const char __user *start = buf; + int xfer; + char *bufptr; + uint32_t idx; + void *data; + int rc = 0; + uint32_t size; + + if (!pcm->ac) + return -ENODEV; + + if (!atomic_read(&pcm->out_enabled)) { + rc = config(pcm); + if (rc < 0) + return rc; + } + + mutex_lock(&pcm->write_lock); + while (count > 0) { + rc = wait_event_timeout(pcm->write_wait, + (atomic_read(&pcm->out_count) || + atomic_read(&pcm->out_stopped)), 1 * HZ); + if (!rc) { + pr_err("%s: wait_event_timeout failed for session %d\n", + __func__, pcm->ac->session); + goto fail; + } + + if (atomic_read(&pcm->out_stopped) && + !atomic_read(&pcm->out_count)) { + pr_info("%s: pcm stopped out_count 0\n", __func__); + mutex_unlock(&pcm->write_lock); + return 0; + } + + data = q6asm_is_cpu_buf_avail(IN, pcm->ac, &size, &idx); + bufptr = data; + if (bufptr) { + xfer = count; + if (xfer > BUFSZ) + xfer = BUFSZ; + + if (copy_from_user(bufptr, buf, xfer)) { + rc = -EFAULT; + goto fail; + } + buf += xfer; + count -= xfer; + rc = q6asm_write(pcm->ac, xfer, 0, 0, NO_TIMESTAMP); + wmb(); + if (rc < 0) { + rc = -EFAULT; + goto fail; + } + } + atomic_dec(&pcm->out_count); + } + + rc = buf - start; +fail: + mutex_unlock(&pcm->write_lock); + return rc; +} + +static int pcm_out_release(struct inode *inode, struct file *file) +{ + struct pcm *pcm = file->private_data; + + pr_info("[%s:%s] release session id[%d]\n", __MM_FILE__, + __func__, pcm->ac->session); + if (pcm->ac) + pcm_out_disable(pcm); + msm_clear_session_id(pcm->ac->session); + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, pcm->ac->session); + q6asm_audio_client_free(pcm->ac); + audio_allow_sleep(pcm); + wake_lock_destroy(&pcm->wakelock); + mutex_destroy(&pcm->lock); + mutex_destroy(&pcm->write_lock); + kfree(pcm); + return 0; +} + +static const struct file_operations pcm_out_fops = { + .owner = THIS_MODULE, + .open = pcm_out_open, + .write = pcm_out_write, + .release = pcm_out_release, + .unlocked_ioctl = pcm_out_ioctl, +}; + +struct miscdevice pcm_out_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &pcm_out_fops, +}; + +static int __init pcm_out_init(void) +{ + return misc_register(&pcm_out_misc); +} + +device_initcall(pcm_out_init); diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h new file mode 100644 index 00000000000..e108de5ef82 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +/* For Decoders */ +#ifndef __Q6_AUDIO_COMMON_H__ +#define __Q6_AUDIO_COMMON_H__ + +#include +#include + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *audio); + + +/* For Encoders */ +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_in_get_dsp_frames(void *audio, + uint32_t token, uint32_t *payload); + +#endif /*__Q6_AUDIO_COMMON_H__*/ diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1.c new file mode 100644 index 00000000000..f49d6e071ea --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in * audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_CMDRSP_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM: + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS: + break; + case ASM_SESSION_EVENT_TX_OVERFLOW: + pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + default: + pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +void audio_in_get_dsp_frames(/*struct q6audio_in *audio,*/void *aud, + uint32_t token, uint32_t *payload) +{ + struct q6audio_in *audio = (struct q6audio_in *)aud; + uint32_t index; + + index = token; + pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n", + __func__, audio->ac->session, token, payload[7], + payload[3]); + pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__, + audio->ac->session, payload[4], payload[5]); + pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__, + audio->ac->session, payload[6], payload[8]); + pr_debug("%s:session id %d: enc frame size=0x%8x\n", __func__, + audio->ac->session, payload[2]); + + audio->out_frame_info[index][0] = payload[7]; + audio->out_frame_info[index][1] = payload[3]; + + /* statistics of read */ + atomic_add(payload[2], &audio->in_bytes); + atomic_add(payload[7], &audio->in_samples); + + if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) { + atomic_inc(&audio->out_count); + wake_up(&audio->read_wait); + } +} diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c new file mode 100644 index 00000000000..112de625cfa --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils_aio.h" + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + + pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + case ASM_DATA_EVENT_READ_DONE: + case ASM_DATA_CMDRSP_EOS: + case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY: + audio_aio_cb(opcode, token, payload, audio); + break; + default: + pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode); + break; + } +} + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + union msm_audio_event_payload e_payload; + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE: + pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_write_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_READ_DONE: + pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_read_ack(audio, token, payload); + break; + case ASM_DATA_CMDRSP_EOS: + /* EOS Handle */ + pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio); + if (audio->feedback) { /* Non-Tunnel mode */ + audio->eos_rsp = 1; + /* propagate input EOS i/p buffer, + after receiving DSP acknowledgement */ + if (audio->eos_flag && + (audio->eos_write_payload.aio_buf.buf_addr)) { + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + audio->eos_flag = 0; + } + } else { /* Tunnel mode */ + audio->eos_rsp = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + break; + case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n", + __func__, audio, payload[0], payload[1], opcode); + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY: + pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, " + + "payload[0]-sr = %d, payload[1]-chl = %d, " + "payload[2] = %d, payload[3] = %d\n", __func__, + audio, payload[0], payload[1], payload[2], + payload[3]); + pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, " + "sr(prev) = %d, chl(prev) = %d,", + __func__, audio, audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + audio->pcm_cfg.sample_rate = payload[0]; + audio->pcm_cfg.channel_count = payload[1] & 0xFFFF; + e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count; + e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate; + audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); + break; + default: + break; + } +} diff --git a/arch/arm/mach-msm/qdsp6v2/q6core.c b/arch/arm/mach-msm/qdsp6v2/q6core.c new file mode 100644 index 00000000000..edb1e7ddf82 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/q6core.c @@ -0,0 +1,409 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6core.h" + +#define TIMEOUT_MS 1000 + +static struct apr_svc *apr_handle_q; +static struct apr_svc *apr_handle_m; +static struct apr_svc *core_handle_q; + +static int32_t query_adsp_ver; +static wait_queue_head_t adsp_version_wait; +static uint32_t adsp_version; + +static wait_queue_head_t bus_bw_req_wait; +static u32 bus_bw_resp_received; + +static struct dentry *dentry; +static char l_buf[4096]; + +static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) +{ + struct adsp_get_version *payload; + uint32_t *payload1; + struct adsp_service_info *svc_info; + int i; + + pr_info("core msg: payload len = %u, apr resp opcode = 0x%X\n", + data->payload_size, data->opcode); + + switch (data->opcode) { + + case APR_BASIC_RSP_RESULT:{ + + if (data->payload_size == 0) { + pr_err("%s: APR_BASIC_RSP_RESULT No Payload ", + __func__); + return 0; + } + + payload1 = data->payload; + + switch (payload1[0]) { + + case ADSP_CMD_SET_POWER_COLLAPSE_STATE: + pr_info("Cmd = ADSP_CMD_SET_POWER_COLLAPSE_STATE" + " status[0x%x]\n", payload1[1]); + break; + case ADSP_CMD_REMOTE_BUS_BW_REQUEST: + pr_info("%s: cmd = ADSP_CMD_REMOTE_BUS_BW_REQUEST" + " status = 0x%x\n", __func__, payload1[1]); + + bus_bw_resp_received = 1; + wake_up(&bus_bw_req_wait); + break; + default: + pr_err("Invalid cmd rsp[0x%x][0x%x]\n", + payload1[0], payload1[1]); + break; + } + break; + } + case ADSP_GET_VERSION_RSP:{ + if (data->payload_size) { + payload = data->payload; + if (query_adsp_ver == 1) { + query_adsp_ver = 0; + adsp_version = payload->build_id; + wake_up(&adsp_version_wait); + } + svc_info = (struct adsp_service_info *) + ((char *)payload + sizeof(struct adsp_get_version)); + pr_info("----------------------------------------\n"); + pr_info("Build id = %x\n", payload->build_id); + pr_info("Number of services= %x\n", payload->svc_cnt); + pr_info("----------------------------------------\n"); + for (i = 0; i < payload->svc_cnt; i++) { + pr_info("svc-id[%d]\tver[%x.%x]\n", + svc_info[i].svc_id, + (svc_info[i].svc_ver & 0xFFFF0000) + >> 16, + (svc_info[i].svc_ver & 0xFFFF)); + } + pr_info("-----------------------------------------\n"); + } else + pr_info("zero payload for ADSP_GET_VERSION_RSP\n"); + break; + } + case RESET_EVENTS:{ + pr_debug("Reset event received in Core service"); + apr_reset(core_handle_q); + core_handle_q = NULL; + break; + } + + default: + pr_err("Message id from adsp core svc: %d\n", data->opcode); + break; + } + + return 0; +} + +static int32_t aprv2_debug_fn_q(struct apr_client_data *data, void *priv) +{ + pr_debug("Q6_Payload Length = %d\n", data->payload_size); + if (memcmp(data->payload, l_buf + 20, data->payload_size)) + pr_info("FAIL: %d\n", data->payload_size); + else + pr_info("SUCCESS: %d\n", data->payload_size); + return 0; +} + +static int32_t aprv2_debug_fn_m(struct apr_client_data *data, void *priv) +{ + pr_info("M_Payload Length = %d\n", data->payload_size); + return 0; +} + +static ssize_t apr_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_debug("apr debugfs opened\n"); + return 0; +} + +void core_open(void) +{ + if (core_handle_q == NULL) { + core_handle_q = apr_register("ADSP", "CORE", + aprv2_core_fn_q, 0xFFFFFFFF, NULL); + } + pr_info("Open_q %p\n", core_handle_q); + if (core_handle_q == NULL) { + pr_err("%s: Unable to register CORE\n", __func__); + } +} + +int core_req_bus_bandwith(u16 bus_id, u32 ab_bps, u32 ib_bps) +{ + struct adsp_cmd_remote_bus_bw_request bus_bw_req; + int ret; + + pr_debug("%s: bus_id %u ab_bps %u ib_bps %u\n", + __func__, bus_id, ab_bps, ib_bps); + + core_open(); + if (core_handle_q == NULL) { + pr_info("%s: apr registration for CORE failed\n", __func__); + return -ENODEV; + } + + bus_bw_req.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + bus_bw_req.hdr.pkt_size = sizeof(struct adsp_cmd_remote_bus_bw_request); + + bus_bw_req.hdr.src_port = 0; + bus_bw_req.hdr.dest_port = 0; + bus_bw_req.hdr.token = 0; + bus_bw_req.hdr.opcode = ADSP_CMD_REMOTE_BUS_BW_REQUEST; + + bus_bw_req.bus_identifier = bus_id; + bus_bw_req.reserved = 0; + bus_bw_req.ab_bps = ab_bps; + bus_bw_req.ib_bps = ib_bps; + + bus_bw_resp_received = 0; + ret = apr_send_pkt(core_handle_q, (uint32_t *) &bus_bw_req); + if (ret < 0) { + pr_err("%s: CORE bus bw request failed\n", __func__); + goto fail_cmd; + } + + ret = wait_event_timeout(bus_bw_req_wait, (bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIME; + goto fail_cmd; + } + + return 0; + +fail_cmd: + return ret; +} + +uint32_t core_get_adsp_version(void) +{ + struct apr_hdr *hdr; + int32_t rc = 0, ret = 0; + core_open(); + if (core_handle_q) { + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = ADSP_GET_VERSION; + + apr_send_pkt(core_handle_q, (uint32_t *)l_buf); + query_adsp_ver = 1; + pr_info("Write_q\n"); + ret = wait_event_timeout(adsp_version_wait, + (query_adsp_ver == 0), + msecs_to_jiffies(TIMEOUT_MS)); + rc = adsp_version; + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + rc = -ENODEV; + } + } else + pr_info("apr registration failed\n"); + return rc; +} +EXPORT_SYMBOL(core_get_adsp_version); + +static ssize_t apr_debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int len; + static int t_len; + + if (count < 0) + return 0; + len = count > 63 ? 63 : count; + if (copy_from_user(l_buf + 20 , buf, len)) { + pr_info("Unable to copy data from user space\n"); + return -EFAULT; + } + l_buf[len + 20] = 0; + if (l_buf[len + 20 - 1] == '\n') { + l_buf[len + 20 - 1] = 0; + len--; + } + if (!strncmp(l_buf + 20, "open_q", 64)) { + apr_handle_q = apr_register("ADSP", "TEST", aprv2_debug_fn_q, + 0xFFFFFFFF, NULL); + pr_info("Open_q %p\n", apr_handle_q); + } else if (!strncmp(l_buf + 20, "open_m", 64)) { + apr_handle_m = apr_register("MODEM", "TEST", aprv2_debug_fn_m, + 0xFFFFFFFF, NULL); + pr_info("Open_m %p\n", apr_handle_m); + } else if (!strncmp(l_buf + 20, "write_q", 64)) { + struct apr_hdr *hdr; + + t_len++; + t_len = t_len % 450; + if (!t_len % 99) + msleep(2000); + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, t_len); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 20, 9, 4060); + + apr_send_pkt(apr_handle_q, (uint32_t *)l_buf); + pr_debug("Write_q\n"); + } else if (!strncmp(l_buf + 20, "write_m", 64)) { + struct apr_hdr *hdr; + + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, 8); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 30, 9, 4060); + + apr_send_pkt(apr_handle_m, (uint32_t *)l_buf); + pr_info("Write_m\n"); + } else if (!strncmp(l_buf + 20, "write_q4", 64)) { + struct apr_hdr *hdr; + + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, 4076); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 30, 9, 4060); + + apr_send_pkt(apr_handle_q, (uint32_t *)l_buf); + pr_info("Write_q\n"); + } else if (!strncmp(l_buf + 20, "write_m4", 64)) { + struct apr_hdr *hdr; + + hdr = (struct apr_hdr *)l_buf; + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(20), APR_PKT_VER); + hdr->pkt_size = APR_PKT_SIZE(20, 4076); + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->token = 0; + hdr->opcode = 0x12345678; + memset(l_buf + 30, 9, 4060); + + apr_send_pkt(apr_handle_m, (uint32_t *)l_buf); + pr_info("Write_m\n"); + } else if (!strncmp(l_buf + 20, "close", 64)) { + if (apr_handle_q) + apr_deregister(apr_handle_q); + } else if (!strncmp(l_buf + 20, "loaded", 64)) { + change_q6_state(APR_Q6_LOADED); + } else if (!strncmp(l_buf + 20, "boom", 64)) { + q6audio_dsp_not_responding(); + } else if (!strncmp(l_buf + 20, "dsp_ver", 64)) { + core_get_adsp_version(); + } else if (!strncmp(l_buf + 20, "en_pwr_col", 64)) { + struct adsp_power_collapse pc; + + core_open(); + if (core_handle_q) { + pc.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pc.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(uint32_t));; + pc.hdr.src_port = 0; + pc.hdr.dest_port = 0; + pc.hdr.token = 0; + pc.hdr.opcode = ADSP_CMD_SET_POWER_COLLAPSE_STATE; + pc.power_collapse = 0x00000000; + apr_send_pkt(core_handle_q, (uint32_t *)&pc); + pr_info("Write_q :enable power collapse\n"); + } + } else if (!strncmp(l_buf + 20, "dis_pwr_col", 64)) { + struct adsp_power_collapse pc; + + core_open(); + if (core_handle_q) { + pc.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pc.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(uint32_t)); + pc.hdr.src_port = 0; + pc.hdr.dest_port = 0; + pc.hdr.token = 0; + pc.hdr.opcode = ADSP_CMD_SET_POWER_COLLAPSE_STATE; + pc.power_collapse = 0x00000001; + apr_send_pkt(core_handle_q, (uint32_t *)&pc); + pr_info("Write_q:disable power collapse\n"); + } + } else + pr_info("Unknown Command\n"); + + return count; +} + +static const struct file_operations apr_debug_fops = { + .write = apr_debug_write, + .open = apr_debug_open, +}; + +static int __init core_init(void) +{ + init_waitqueue_head(&bus_bw_req_wait); + bus_bw_resp_received = 0; + + query_adsp_ver = 0; + init_waitqueue_head(&adsp_version_wait); + adsp_version = 0; + + core_handle_q = NULL; + +#ifdef CONFIG_DEBUG_FS + dentry = debugfs_create_file("apr", S_IFREG | S_IRUGO | S_IWUSR + | S_IWGRP, NULL, (void *) NULL, &apr_debug_fops); +#endif /* CONFIG_DEBUG_FS */ + + return 0; +} + +device_initcall(core_init); diff --git a/arch/arm/mach-msm/qdsp6v2/q6core.h b/arch/arm/mach-msm/qdsp6v2/q6core.h new file mode 100644 index 00000000000..cb25d6b82ec --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/q6core.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __Q6CORE_H__ +#define __Q6CORE_H__ +#include + + +#define ADSP_CMD_REMOTE_BUS_BW_REQUEST 0x0001115D +#define AUDIO_IF_BUS_ID 1 + +struct adsp_cmd_remote_bus_bw_request { + struct apr_hdr hdr; + u16 bus_identifier; + u16 reserved; + u32 ab_bps; + u32 ib_bps; +} __packed; + +#define ADSP_GET_VERSION 0x00011152 +#define ADSP_GET_VERSION_RSP 0x00011153 + +struct adsp_get_version { + uint32_t build_id; + uint32_t svc_cnt; +}; + +struct adsp_service_info { + uint32_t svc_id; + uint32_t svc_ver; +}; + +#define ADSP_CMD_SET_POWER_COLLAPSE_STATE 0x0001115C +struct adsp_power_collapse { + struct apr_hdr hdr; + uint32_t power_collapse; +}; + +int core_req_bus_bandwith(u16 bus_id, u32 ab_bps, u32 ib_bps); + +uint32_t core_get_adsp_version(void); + +#endif /* __Q6CORE_H__ */ diff --git a/arch/arm/mach-msm/qdsp6v2/q6voice.c b/arch/arm/mach-msm/qdsp6v2/q6voice.c new file mode 100644 index 00000000000..12a02c500d0 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/q6voice.c @@ -0,0 +1,2948 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "q6core.h" + + +#define TIMEOUT_MS 3000 +#define SNDDEV_CAP_TTY 0x20 +#define CMD_STATUS_SUCCESS 0 +#define CMD_STATUS_FAIL 1 + +/* Voice session creates passive control sessions for MVM and CVS. */ +#define VOC_PATH_PASSIVE 0 + +/* VoIP session creates full control sessions for MVM and CVS. */ +#define VOC_PATH_FULL 1 + +#define ADSP_VERSION_CVD 0x60300000 + +#define BUFFER_PAYLOAD_SIZE 4000 + +#define VOC_REC_NONE 0xFF + +struct common_data common; + +static bool is_adsp_support_cvd(void) +{ + return (common.adsp_version >= ADSP_VERSION_CVD); +} +static int voice_send_enable_vocproc_cmd(struct voice_data *v); +static int voice_send_netid_timing_cmd(struct voice_data *v); + +static void *voice_get_apr_mvm(void) +{ + void *apr_mvm = NULL; + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + apr_mvm = common.apr_mvm; + else + apr_mvm = common.apr_q6_mvm; + + pr_debug("%s: apr_mvm 0x%x\n", __func__, (unsigned int)apr_mvm); + + return apr_mvm; +} + +static void voice_set_apr_mvm(void *apr_mvm) +{ + pr_debug("%s: apr_mvm 0x%x\n", __func__, (unsigned int)apr_mvm); + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + common.apr_mvm = apr_mvm; + else + common.apr_q6_mvm = apr_mvm; +} + +static void *voice_get_apr_cvs(void) +{ + void *apr_cvs = NULL; + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + apr_cvs = common.apr_cvs; + else + apr_cvs = common.apr_q6_cvs; + + pr_debug("%s: apr_cvs 0x%x\n", __func__, (unsigned int)apr_cvs); + + return apr_cvs; +} + +static void voice_set_apr_cvs(void *apr_cvs) +{ + pr_debug("%s: apr_cvs 0x%x\n", __func__, (unsigned int)apr_cvs); + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + common.apr_cvs = apr_cvs; + else + common.apr_q6_cvs = apr_cvs; + rtac_set_voice_handle(RTAC_CVS, apr_cvs); +} + +static void *voice_get_apr_cvp(void) +{ + void *apr_cvp = NULL; + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + apr_cvp = common.apr_cvp; + else + apr_cvp = common.apr_q6_cvp; + + pr_debug("%s: apr_cvp 0x%x\n", __func__, (unsigned int)apr_cvp); + + return apr_cvp; +} + +static void voice_set_apr_cvp(void *apr_cvp) +{ + pr_debug("%s: apr_cvp 0x%x\n", __func__, (unsigned int)apr_cvp); + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) + common.apr_cvp = apr_cvp; + else + common.apr_q6_cvp = apr_cvp; + rtac_set_voice_handle(RTAC_CVP, apr_cvp); +} + +static u16 voice_get_mvm_handle(struct voice_data *v) +{ + pr_debug("%s: mvm_handle %d\n", __func__, v->mvm_handle); + + return v->mvm_handle; +} + +static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle) +{ + pr_debug("%s: session 0x%x, mvm_handle %d\n", + __func__, (unsigned int)v, mvm_handle); + + v->mvm_handle = mvm_handle; +} + +static u16 voice_get_cvs_handle(struct voice_data *v) +{ + pr_debug("%s: cvs_handle %d\n", __func__, v->cvs_handle); + + return v->cvs_handle; +} + +static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle) +{ + pr_debug("%s: session 0x%x, cvs_handle %d\n", + __func__, (unsigned int)v, cvs_handle); + + v->cvs_handle = cvs_handle; +} + +static u16 voice_get_cvp_handle(struct voice_data *v) +{ + pr_debug("%s: cvp_handle %d\n", __func__, v->cvp_handle); + + return v->cvp_handle; +} + +static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle) +{ + pr_debug("%s: session 0x%x, cvp_handle %d\n", + __func__, (unsigned int)v, cvp_handle); + + v->cvp_handle = cvp_handle; +} + +u16 voice_get_session_id(const char *name) +{ + u16 session_id = 0; + + if (name != NULL) { + if (!strncmp(name, "Voice session", 13)) + session_id = common.voice[VOC_PATH_PASSIVE].session_id; + else + session_id = common.voice[VOC_PATH_FULL].session_id; + } + + pr_debug("%s: %s has session id 0x%x\n", __func__, name, session_id); + + return session_id; +} + +static struct voice_data *voice_get_session(u16 session_id) +{ + struct voice_data *v = NULL; + + if (session_id == 0) { + mutex_lock(&common.common_lock); + + pr_debug("%s: NULL id, voc_path is %d\n", + __func__, common.voc_path); + + if (common.voc_path == VOC_PATH_PASSIVE) + v = &common.voice[VOC_PATH_PASSIVE]; + else + v = &common.voice[VOC_PATH_FULL]; + + mutex_unlock(&common.common_lock); + } else if ((session_id >= SESSION_ID_BASE) && + (session_id < SESSION_ID_BASE + MAX_VOC_SESSIONS)) { + v = &common.voice[session_id - SESSION_ID_BASE]; + } else { + pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id); + } + + pr_debug("%s: session_id 0x%x session handle 0x%x\n", + __func__, session_id, (unsigned int)v); + + return v; +} + +static bool is_voice_session(u16 session_id) +{ + return (session_id == common.voice[VOC_PATH_PASSIVE].session_id); +} + +static bool is_voip_session(u16 session_id) +{ + return (session_id == common.voice[VOC_PATH_FULL].session_id); +} + +static void voice_auddev_cb_function(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data); + +static int32_t modem_mvm_callback(struct apr_client_data *data, void *priv); +static int32_t modem_cvs_callback(struct apr_client_data *data, void *priv); +static int32_t modem_cvp_callback(struct apr_client_data *data, void *priv); + +static int voice_apr_register(void) +{ + int rc = 0; + void *apr_mvm; + void *apr_cvs; + void *apr_cvp; + + if (common.adsp_version == 0) { + common.adsp_version = core_get_adsp_version(); + pr_info("adsp_ver fetched:%x\n", common.adsp_version); + } + + mutex_lock(&common.common_lock); + + apr_mvm = voice_get_apr_mvm(); + apr_cvs = voice_get_apr_cvs(); + apr_cvp = voice_get_apr_cvp(); + + + pr_debug("into voice_apr_register_callback\n"); + /* register callback to APR */ + if (apr_mvm == NULL) { + pr_debug("start to register MVM callback\n"); + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) { + apr_mvm = apr_register("MODEM", "MVM", + modem_mvm_callback, 0xFFFFFFFF, + &common); + } else { + apr_mvm = apr_register("ADSP", "MVM", + modem_mvm_callback, 0xFFFFFFFF, + &common); + } + + if (apr_mvm == NULL) { + pr_err("Unable to register MVM %d\n", + is_adsp_support_cvd()); + rc = -ENODEV; + goto done; + } + + voice_set_apr_mvm(apr_mvm); + } + + if (apr_cvs == NULL) { + pr_debug("start to register CVS callback\n"); + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) { + apr_cvs = apr_register("MODEM", "CVS", + modem_cvs_callback, 0xFFFFFFFF, + &common); + } else { + apr_cvs = apr_register("ADSP", "CVS", + modem_cvs_callback, 0xFFFFFFFF, + &common); + } + + if (apr_cvs == NULL) { + pr_err("Unable to register CVS %d\n", + is_adsp_support_cvd()); + rc = -ENODEV; + goto err; + } + + voice_set_apr_cvs(apr_cvs); + } + + if (apr_cvp == NULL) { + pr_debug("start to register CVP callback\n"); + + if (common.voc_path == VOC_PATH_PASSIVE && + !(is_adsp_support_cvd())) { + apr_cvp = apr_register("MODEM", "CVP", + modem_cvp_callback, 0xFFFFFFFF, + &common); + } else { + apr_cvp = apr_register("ADSP", "CVP", + modem_cvp_callback, 0xFFFFFFFF, + &common); + } + + if (apr_cvp == NULL) { + pr_err("Unable to register CVP %d\n", + is_adsp_support_cvd()); + rc = -ENODEV; + goto err1; + } + + voice_set_apr_cvp(apr_cvp); + } + + mutex_unlock(&common.common_lock); + + return 0; + +err1: + apr_deregister(apr_cvs); + apr_cvs = NULL; + voice_set_apr_cvs(apr_cvs); +err: + apr_deregister(apr_mvm); + apr_mvm = NULL; + voice_set_apr_mvm(apr_mvm); + +done: + mutex_unlock(&common.common_lock); + + return rc; +} + +static int voice_create_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_create_ctl_session_cmd mvm_session_cmd; + struct cvs_create_passive_ctl_session_cmd cvs_session_cmd; + struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd; + struct mvm_attach_stream_cmd attach_stream_cmd; + void *apr_mvm = voice_get_apr_mvm(); + void *apr_cvs = voice_get_apr_cvs(); + void *apr_cvp = voice_get_apr_cvp(); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvs_handle = voice_get_cvs_handle(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + pr_info("%s:\n", __func__); + + /* start to ping if modem service is up */ + pr_debug("in voice_create_mvm_cvs_session, mvm_hdl=%d, cvs_hdl=%d\n", + mvm_handle, cvs_handle); + /* send cmd to create mvm session and wait for response */ + + if (!mvm_handle) { + if (is_voice_session(v->session_id)) { + mvm_session_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_session_cmd) - APR_HDR_SIZE); + pr_debug("Send mvm create session pkt size = %d\n", + mvm_session_cmd.hdr.pkt_size); + mvm_session_cmd.hdr.src_port = v->session_id; + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + pr_debug("%s: Creating MVM passive ctrl\n", __func__); + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + strncpy(mvm_session_cmd.mvm_session.name, + "default modem voice", SESSION_NAME_LEN); + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_err("Error sending MVM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + } else { + mvm_session_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_session_cmd) - APR_HDR_SIZE); + pr_debug("Send mvm create session pkt size = %d\n", + mvm_session_cmd.hdr.pkt_size); + mvm_session_cmd.hdr.src_port = v->session_id; + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + pr_debug("%s: Creating MVM full ctrl\n", __func__); + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION; + strncpy(mvm_session_cmd.mvm_session.name, + "default voip", SESSION_NAME_LEN); + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_err("Error sending MVM_FULL_CTL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + } + + /* Get the created MVM handle. */ + mvm_handle = voice_get_mvm_handle(v); + } + + /* send cmd to create cvs session */ + if (!cvs_handle) { + if (is_voice_session(v->session_id)) { + pr_info("%s:creating CVS passive session\n", __func__); + + cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_session_cmd) - APR_HDR_SIZE); + pr_info("send stream create session pkt size = %d\n", + cvs_session_cmd.hdr.pkt_size); + cvs_session_cmd.hdr.src_port = v->session_id; + cvs_session_cmd.hdr.dest_port = 0; + cvs_session_cmd.hdr.token = 0; + cvs_session_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + strncpy(cvs_session_cmd.cvs_session.name, + "default modem voice", SESSION_NAME_LEN); + + v->cvs_state = CMD_STATUS_FAIL; + + pr_info("%s: CVS create\n", __func__); + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_session_cmd); + if (ret < 0) { + pr_err("Fail in sending STREAM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + } else { + pr_info("%s:creating CVS full session\n", __func__); + + cvs_full_ctl_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + cvs_full_ctl_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_full_ctl_cmd) - APR_HDR_SIZE); + + cvs_full_ctl_cmd.hdr.src_port = v->session_id; + cvs_full_ctl_cmd.hdr.dest_port = 0; + cvs_full_ctl_cmd.hdr.token = 0; + cvs_full_ctl_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION; + cvs_full_ctl_cmd.cvs_session.direction = 2; + + cvs_full_ctl_cmd.cvs_session.enc_media_type = + common.mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.dec_media_type = + common.mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.network_id = + common.mvs_info.network_type; + strncpy(cvs_full_ctl_cmd.cvs_session.name, + "default voip", SESSION_NAME_LEN); + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &cvs_full_ctl_cmd); + + if (ret < 0) { + pr_err("%s: Err %d sending CREATE_FULL_CTRL\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + + /* Attach MVM to CVS. */ + pr_info("%s: Attach MVM to stream\n", __func__); + + attach_stream_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + attach_stream_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(attach_stream_cmd) - APR_HDR_SIZE); + attach_stream_cmd.hdr.src_port = v->session_id; + attach_stream_cmd.hdr.dest_port = mvm_handle; + attach_stream_cmd.hdr.token = 0; + attach_stream_cmd.hdr.opcode = + VSS_IMVM_CMD_ATTACH_STREAM; + attach_stream_cmd.attach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &attach_stream_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending ATTACH_STREAM\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + } + } + + return 0; + +fail: + apr_deregister(apr_mvm); + apr_mvm = NULL; + voice_set_apr_mvm(apr_mvm); + + apr_deregister(apr_cvs); + apr_cvs = NULL; + voice_set_apr_cvs(apr_cvs); + + apr_deregister(apr_cvp); + apr_cvp = NULL; + voice_set_apr_cvp(apr_cvp); + + cvp_handle = 0; + voice_set_cvp_handle(v, cvp_handle); + + cvs_handle = 0; + voice_set_cvs_handle(v, cvs_handle); + + return -EINVAL; +} + +static int voice_destroy_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_detach_stream_cmd detach_stream; + struct apr_hdr mvm_destroy; + struct apr_hdr cvs_destroy; + void *apr_mvm = voice_get_apr_mvm(); + void *apr_cvs = voice_get_apr_cvs(); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* MVM, CVS sessions are destroyed only for Full control sessions. */ + if (is_voip_session(v->session_id)) { + pr_info("%s: MVM detach stream\n", __func__); + + /* Detach voice stream. */ + detach_stream.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(detach_stream) - APR_HDR_SIZE); + detach_stream.hdr.src_port = v->session_id; + detach_stream.hdr.dest_port = mvm_handle; + detach_stream.hdr.token = 0; + detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM; + detach_stream.detach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream); + if (ret < 0) { + pr_err("%s: Error %d sending DETACH_STREAM\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + goto fail; + } + + /* Destroy CVS. */ + pr_info("%s: CVS destroy session\n", __func__); + + cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_destroy) - APR_HDR_SIZE); + cvs_destroy.src_port = v->session_id; + cvs_destroy.dest_port = cvs_handle; + cvs_destroy.token = 0; + cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy); + if (ret < 0) { + pr_err("%s: Error %d sending CVS DESTROY\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + + goto fail; + } + cvs_handle = 0; + voice_set_cvs_handle(v, cvs_handle); + + /* Destroy MVM. */ + pr_info("%s: MVM destroy session\n", __func__); + + mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_destroy) - APR_HDR_SIZE); + mvm_destroy.src_port = v->session_id; + mvm_destroy.dest_port = mvm_handle; + mvm_destroy.token = 0; + mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy); + if (ret < 0) { + pr_err("%s: Error %d sending MVM DESTROY\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + + goto fail; + } + mvm_handle = 0; + voice_set_mvm_handle(v, mvm_handle); + } + +fail: + return 0; +} + +static int voice_send_tty_mode_to_modem(struct voice_data *v) +{ + struct msm_snddev_info *dev_tx_info; + struct msm_snddev_info *dev_rx_info; + int tty_mode = 0; + int ret = 0; + struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd; + void *apr_mvm = voice_get_apr_mvm(); + u16 mvm_handle = voice_get_mvm_handle(v); + + dev_rx_info = audio_dev_ctrl_find_dev(v->dev_rx.dev_id); + if (IS_ERR(dev_rx_info)) { + pr_err("bad dev_id %d\n", v->dev_rx.dev_id); + goto done; + } + + dev_tx_info = audio_dev_ctrl_find_dev(v->dev_tx.dev_id); + if (IS_ERR(dev_tx_info)) { + pr_err("bad dev_id %d\n", v->dev_tx.dev_id); + goto done; + } + + if ((dev_rx_info->capability & SNDDEV_CAP_TTY) && + (dev_tx_info->capability & SNDDEV_CAP_TTY)) + tty_mode = 3; /* FULL */ + else if (!(dev_tx_info->capability & SNDDEV_CAP_TTY) && + (dev_rx_info->capability & SNDDEV_CAP_TTY)) + tty_mode = 2; /* VCO */ + else if ((dev_tx_info->capability & SNDDEV_CAP_TTY) && + !(dev_rx_info->capability & SNDDEV_CAP_TTY)) + tty_mode = 1; /* HCO */ + + if (tty_mode) { + /* send tty mode cmd to mvm */ + mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_tty_mode_cmd) - APR_HDR_SIZE); + pr_debug("pkt size = %d\n", mvm_tty_mode_cmd.hdr.pkt_size); + mvm_tty_mode_cmd.hdr.src_port = v->session_id; + mvm_tty_mode_cmd.hdr.dest_port = mvm_handle; + mvm_tty_mode_cmd.hdr.token = 0; + mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE; + mvm_tty_mode_cmd.tty_mode.mode = tty_mode; + pr_info("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode); + + v->mvm_state = CMD_STATUS_FAIL; + pr_info("%s: MVM set tty\n", __func__); + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd); + if (ret < 0) { + pr_err("Fail: sending VSS_ISTREAM_CMD_SET_TTY_MODE\n"); + goto done; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto done; + } + } + return 0; +done: + return -EINVAL; +} + +static int voice_send_cvs_cal_to_modem(struct voice_data *v) +{ + struct apr_hdr cvs_cal_cmd_hdr; + uint32_t *cmd_buf; + struct acdb_cal_data cal_data; + struct acdb_atomic_cal_block *cal_blk; + int32_t cal_size_per_network; + uint32_t *cal_data_per_network; + int index = 0; + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* fill the header */ + cvs_cal_cmd_hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_cal_cmd_hdr) - APR_HDR_SIZE); + cvs_cal_cmd_hdr.src_port = v->session_id; + cvs_cal_cmd_hdr.dest_port = cvs_handle; + cvs_cal_cmd_hdr.token = 0; + cvs_cal_cmd_hdr.opcode = + VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA; + + pr_debug("voice_send_cvs_cal_to_modem\n"); + /* get the cvs cal data */ + get_vocstrm_cal(&cal_data); + if (cal_data.num_cal_blocks == 0) { + pr_err("%s: No calibration data to send!\n", __func__); + goto done; + } + + /* send cvs cal to modem */ + cmd_buf = kzalloc((sizeof(struct apr_hdr) + BUFFER_PAYLOAD_SIZE), + GFP_KERNEL); + if (!cmd_buf) { + pr_err("No memory is allocated.\n"); + return -ENOMEM; + } + pr_debug("----- num_cal_blocks=%d\n", (s32)cal_data.num_cal_blocks); + cal_blk = cal_data.cal_blocks; + pr_debug("cal_blk =%x\n", (uint32_t)cal_data.cal_blocks); + + for (; index < cal_data.num_cal_blocks; index++) { + cal_size_per_network = atomic_read(&cal_blk[index].cal_size); + pr_debug(" cal size =%d\n", cal_size_per_network); + if (cal_size_per_network >= BUFFER_PAYLOAD_SIZE) + pr_err("Cal size is too big\n"); + cal_data_per_network = + (u32 *)atomic_read(&cal_blk[index].cal_kvaddr); + pr_debug(" cal data=%x\n", (uint32_t)cal_data_per_network); + cvs_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + cal_size_per_network); + pr_debug("header size =%d, pkt_size =%d\n", + APR_HDR_SIZE, cvs_cal_cmd_hdr.pkt_size); + memcpy(cmd_buf, &cvs_cal_cmd_hdr, APR_HDR_SIZE); + memcpy(cmd_buf + (APR_HDR_SIZE / sizeof(uint32_t)), + cal_data_per_network, cal_size_per_network); + pr_debug("send cvs cal: index =%d\n", index); + v->cvs_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvs, cmd_buf); + if (ret < 0) { + pr_err("Fail: sending cvs cal, idx=%d\n", index); + continue; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + } + kfree(cmd_buf); +done: + return 0; +} + +static int voice_send_cvp_cal_to_modem(struct voice_data *v) +{ + struct apr_hdr cvp_cal_cmd_hdr; + uint32_t *cmd_buf; + struct acdb_cal_data cal_data; + struct acdb_atomic_cal_block *cal_blk; + int32_t cal_size_per_network; + uint32_t *cal_data_per_network; + int index = 0; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(); + u16 cvp_handle = voice_get_cvp_handle(v); + + + /* fill the header */ + cvp_cal_cmd_hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_cal_cmd_hdr) - APR_HDR_SIZE); + cvp_cal_cmd_hdr.src_port = v->session_id; + cvp_cal_cmd_hdr.dest_port = cvp_handle; + cvp_cal_cmd_hdr.token = 0; + cvp_cal_cmd_hdr.opcode = + VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA; + + /* get cal data */ + get_vocproc_cal(&cal_data); + if (cal_data.num_cal_blocks == 0) { + pr_err("%s: No calibration data to send!\n", __func__); + goto done; + } + + /* send cal to modem */ + cmd_buf = kzalloc((sizeof(struct apr_hdr) + BUFFER_PAYLOAD_SIZE), + GFP_KERNEL); + if (!cmd_buf) { + pr_err("No memory is allocated.\n"); + return -ENOMEM; + } + pr_debug("----- num_cal_blocks=%d\n", (s32)cal_data.num_cal_blocks); + cal_blk = cal_data.cal_blocks; + pr_debug(" cal_blk =%x\n", (uint32_t)cal_data.cal_blocks); + + for (; index < cal_data.num_cal_blocks; index++) { + cal_size_per_network = atomic_read(&cal_blk[index].cal_size); + if (cal_size_per_network >= BUFFER_PAYLOAD_SIZE) + pr_err("Cal size is too big\n"); + pr_debug(" cal size =%d\n", cal_size_per_network); + cal_data_per_network = + (u32 *)atomic_read(&cal_blk[index].cal_kvaddr); + pr_debug(" cal data=%x\n", (uint32_t)cal_data_per_network); + + cvp_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + cal_size_per_network); + memcpy(cmd_buf, &cvp_cal_cmd_hdr, APR_HDR_SIZE); + memcpy(cmd_buf + (APR_HDR_SIZE / sizeof(*cmd_buf)), + cal_data_per_network, cal_size_per_network); + pr_debug("Send cvp cal\n"); + v->cvp_state = CMD_STATUS_FAIL; + pr_info("%s: CVP calib\n", __func__); + ret = apr_send_pkt(apr_cvp, cmd_buf); + if (ret < 0) { + pr_err("Fail: sending cvp cal, idx=%d\n", index); + continue; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + } + kfree(cmd_buf); +done: + return 0; +} + +static int voice_send_cvp_vol_tbl_to_modem(struct voice_data *v) +{ + struct apr_hdr cvp_vol_cal_cmd_hdr; + uint32_t *cmd_buf; + struct acdb_cal_data cal_data; + struct acdb_atomic_cal_block *cal_blk; + int32_t cal_size_per_network; + uint32_t *cal_data_per_network; + int index = 0; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(); + u16 cvp_handle = voice_get_cvp_handle(v); + + + /* fill the header */ + cvp_vol_cal_cmd_hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_vol_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_vol_cal_cmd_hdr) - APR_HDR_SIZE); + cvp_vol_cal_cmd_hdr.src_port = v->session_id; + cvp_vol_cal_cmd_hdr.dest_port = cvp_handle; + cvp_vol_cal_cmd_hdr.token = 0; + cvp_vol_cal_cmd_hdr.opcode = + VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE; + + /* get cal data */ + get_vocvol_cal(&cal_data); + if (cal_data.num_cal_blocks == 0) { + pr_err("%s: No calibration data to send!\n", __func__); + goto done; + } + + /* send cal to modem */ + cmd_buf = kzalloc((sizeof(struct apr_hdr) + BUFFER_PAYLOAD_SIZE), + GFP_KERNEL); + if (!cmd_buf) { + pr_err("No memory is allocated.\n"); + return -ENOMEM; + } + pr_debug("----- num_cal_blocks=%d\n", (s32)cal_data.num_cal_blocks); + cal_blk = cal_data.cal_blocks; + pr_debug("Cal_blk =%x\n", (uint32_t)cal_data.cal_blocks); + + for (; index < cal_data.num_cal_blocks; index++) { + cal_size_per_network = atomic_read(&cal_blk[index].cal_size); + cal_data_per_network = + (u32 *)atomic_read(&cal_blk[index].cal_kvaddr); + + pr_debug("Cal size =%d, index=%d\n", cal_size_per_network, + index); + pr_debug("Cal data=%x\n", (uint32_t)cal_data_per_network); + cvp_vol_cal_cmd_hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + cal_size_per_network); + memcpy(cmd_buf, &cvp_vol_cal_cmd_hdr, APR_HDR_SIZE); + memcpy(cmd_buf + (APR_HDR_SIZE / sizeof(uint32_t)), + cal_data_per_network, cal_size_per_network); + pr_debug("Send vol table\n"); + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, cmd_buf); + if (ret < 0) { + pr_err("Fail: sending cvp vol cal, idx=%d\n", index); + continue; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + } + kfree(cmd_buf); +done: + return 0; +} + +static int voice_set_dtx(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* Set DTX */ + struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx = { + .hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER), + .hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_dtx) - APR_HDR_SIZE), + .hdr.src_port = v->session_id, + .hdr.dest_port = cvs_handle, + .hdr.token = 0, + .hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE, + .dtx_mode.enable = common.mvs_info.dtx_mode, + }; + + pr_debug("%s: Setting DTX %d\n", __func__, common.mvs_info.dtx_mode); + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx); + if (ret < 0) { + pr_err("%s: Error %d sending SET_DTX\n", __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + } + +done: + return ret; +} + +static int voice_config_cvs_vocoder(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* Set media type. */ + struct cvs_set_media_type_cmd cvs_set_media_cmd; + + pr_info("%s: Setting media type\n", __func__); + + cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_media_cmd) - APR_HDR_SIZE); + cvs_set_media_cmd.hdr.src_port = v->session_id; + cvs_set_media_cmd.hdr.dest_port = cvs_handle; + cvs_set_media_cmd.hdr.token = 0; + cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE; + cvs_set_media_cmd.media_type.tx_media_id = common.mvs_info.media_type; + cvs_set_media_cmd.media_type.rx_media_id = common.mvs_info.media_type; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending SET_MEDIA_TYPE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + /* Set encoder properties. */ + switch (common.mvs_info.media_type) { + case VSS_MEDIA_ID_13K_MODEM: + case VSS_MEDIA_ID_4GV_NB_MODEM: + case VSS_MEDIA_ID_4GV_WB_MODEM: + case VSS_MEDIA_ID_EVRC_MODEM: { + struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate; + + pr_info("%s: Setting CDMA min-max rate\n", __func__); + + cvs_set_cdma_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE); + cvs_set_cdma_rate.hdr.src_port = v->session_id; + cvs_set_cdma_rate.hdr.dest_port = cvs_handle; + cvs_set_cdma_rate.hdr.token = 0; + cvs_set_cdma_rate.hdr.opcode = + VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE; + cvs_set_cdma_rate.cdma_rate.min_rate = + common.mvs_info.q_min_max_rate.min_rate; + cvs_set_cdma_rate.cdma_rate.max_rate = + common.mvs_info.q_min_max_rate.max_rate; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate); + if (ret < 0) { + pr_err("%s: Error %d sending CDMA_SET_ENC_MINMAX_RATE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if ((common.mvs_info.media_type == VSS_MEDIA_ID_4GV_NB_MODEM) || + (common.mvs_info.media_type == VSS_MEDIA_ID_4GV_WB_MODEM)) + ret = voice_set_dtx(v); + + break; + } + + case VSS_MEDIA_ID_AMR_NB_MODEM: { + struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate; + + pr_info("%s: Setting AMR rate\n", __func__); + + cvs_set_amr_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_amr_rate) - APR_HDR_SIZE); + cvs_set_amr_rate.hdr.src_port = v->session_id; + cvs_set_amr_rate.hdr.dest_port = cvs_handle; + cvs_set_amr_rate.hdr.token = 0; + cvs_set_amr_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE; + cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate); + if (ret < 0) { + pr_err("%s: Error %d sending SET_AMR_RATE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = voice_set_dtx(v); + + break; + } + + case VSS_MEDIA_ID_AMR_WB_MODEM: { + struct cvs_set_amrwb_enc_rate_cmd cvs_set_amrwb_rate; + + pr_info("%s: Setting AMR WB rate\n", __func__); + + cvs_set_amrwb_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_amrwb_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_amrwb_rate) - APR_HDR_SIZE); + cvs_set_amrwb_rate.hdr.src_port = v->session_id; + cvs_set_amrwb_rate.hdr.dest_port = cvs_handle; + cvs_set_amrwb_rate.hdr.token = 0; + cvs_set_amrwb_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE; + cvs_set_amrwb_rate.amrwb_rate.mode = common.mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amrwb_rate); + if (ret < 0) { + pr_err("%s: Error %d sending SET_AMRWB_RATE\n", + __func__, ret); + + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = voice_set_dtx(v); + + break; + } + + case VSS_MEDIA_ID_EFR_MODEM: + case VSS_MEDIA_ID_FR_MODEM: + case VSS_MEDIA_ID_HR_MODEM: + case VSS_MEDIA_ID_G729: + case VSS_MEDIA_ID_G711_ALAW: + case VSS_MEDIA_ID_G711_MULAW: { + ret = voice_set_dtx(v); + + break; + } + + default: { + /* Do nothing. */ + } + } + +done: + return ret; +} + +static int voice_send_start_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_start_voice_cmd; + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(); + u16 mvm_handle = voice_get_mvm_handle(v); + + mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE); + pr_info("send mvm_start_voice_cmd pkt size = %d\n", + mvm_start_voice_cmd.pkt_size); + mvm_start_voice_cmd.src_port = v->session_id; + mvm_start_voice_cmd.dest_port = mvm_handle; + mvm_start_voice_cmd.token = 0; + mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_disable_vocproc(struct voice_data *v) +{ + struct apr_hdr cvp_disable_cmd; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* disable vocproc and wait for respose */ + cvp_disable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_disable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_disable_cmd) - APR_HDR_SIZE); + pr_debug("cvp_disable_cmd pkt size = %d, cvp_handle=%d\n", + cvp_disable_cmd.pkt_size, cvp_handle); + cvp_disable_cmd.src_port = v->session_id; + cvp_disable_cmd.dest_port = cvp_handle; + cvp_disable_cmd.token = 0; + cvp_disable_cmd.opcode = VSS_IVOCPROC_CMD_DISABLE; + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_disable_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IVOCPROC_CMD_DISABLE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + rtac_remove_voice(v->cvs_handle); + + return 0; +fail: + return -EINVAL; +} + +static int voice_set_device(struct voice_data *v) +{ + struct cvp_set_device_cmd cvp_setdev_cmd; + struct msm_snddev_info *dev_tx_info; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(); + u16 cvp_handle = voice_get_cvp_handle(v); + + + /* set device and wait for response */ + cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_setdev_cmd) - APR_HDR_SIZE); + pr_debug(" send create cvp setdev, pkt size = %d\n", + cvp_setdev_cmd.hdr.pkt_size); + cvp_setdev_cmd.hdr.src_port = v->session_id; + cvp_setdev_cmd.hdr.dest_port = cvp_handle; + cvp_setdev_cmd.hdr.token = 0; + cvp_setdev_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_DEVICE; + + dev_tx_info = audio_dev_ctrl_find_dev(v->dev_tx.dev_id); + if (IS_ERR(dev_tx_info)) { + pr_err("bad dev_id %d\n", v->dev_tx.dev_id); + goto fail; + } + + cvp_setdev_cmd.cvp_set_device.tx_topology_id = + get_voice_tx_topology(); + if (cvp_setdev_cmd.cvp_set_device.tx_topology_id == 0) { + if (dev_tx_info->channel_mode > 1) + cvp_setdev_cmd.cvp_set_device.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE; + else + cvp_setdev_cmd.cvp_set_device.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS; + } + + /* Use default topology if invalid value in ACDB */ + cvp_setdev_cmd.cvp_set_device.rx_topology_id = + get_voice_rx_topology(); + if (cvp_setdev_cmd.cvp_set_device.rx_topology_id == 0) + cvp_setdev_cmd.cvp_set_device.rx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + cvp_setdev_cmd.cvp_set_device.tx_port_id = v->dev_tx.dev_port_id; + cvp_setdev_cmd.cvp_set_device.rx_port_id = v->dev_rx.dev_port_id; + pr_info("topology=%d , tx_port_id=%d, rx_port_id=%d\n", + cvp_setdev_cmd.cvp_set_device.tx_topology_id, + cvp_setdev_cmd.cvp_set_device.tx_port_id, + cvp_setdev_cmd.cvp_set_device.rx_port_id); + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd); + if (ret < 0) { + pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n"); + goto fail; + } + pr_debug("wait for cvp create session event\n"); + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* send cvs cal */ + voice_send_cvs_cal_to_modem(v); + + /* send cvp cal */ + voice_send_cvp_cal_to_modem(v); + + /* send cvp vol table cal */ + voice_send_cvp_vol_tbl_to_modem(v); + + /* enable vocproc and wait for respose */ + voice_send_enable_vocproc_cmd(v); + + /* send tty mode if tty device is used */ + voice_send_tty_mode_to_modem(v); + + if (is_voip_session(v->session_id)) + voice_send_netid_timing_cmd(v); + + rtac_add_voice(v->cvs_handle, v->cvp_handle, + v->dev_rx.dev_port_id, v->dev_tx.dev_port_id, + v->session_id); + + return 0; +fail: + return -EINVAL; +} + +static int voice_send_stop_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_stop_voice_cmd; + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(); + u16 mvm_handle = voice_get_mvm_handle(v); + + mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE); + pr_info("send mvm_stop_voice_cmd pkt size = %d\n", + mvm_stop_voice_cmd.pkt_size); + mvm_stop_voice_cmd.src_port = v->session_id; + mvm_stop_voice_cmd.dest_port = mvm_handle; + mvm_stop_voice_cmd.token = 0; + mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_setup_modem_voice(struct voice_data *v) +{ + struct cvp_create_full_ctl_session_cmd cvp_session_cmd; + int ret = 0; + struct msm_snddev_info *dev_tx_info; + void *apr_cvp = voice_get_apr_cvp(); + + /* create cvp session and wait for response */ + cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_session_cmd) - APR_HDR_SIZE); + pr_info(" send create cvp session, pkt size = %d\n", + cvp_session_cmd.hdr.pkt_size); + cvp_session_cmd.hdr.src_port = v->session_id; + cvp_session_cmd.hdr.dest_port = 0; + cvp_session_cmd.hdr.token = 0; + cvp_session_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION; + + dev_tx_info = audio_dev_ctrl_find_dev(v->dev_tx.dev_id); + if (IS_ERR(dev_tx_info)) { + pr_err("bad dev_id %d\n", v->dev_tx.dev_id); + goto fail; + } + + /* Use default topology if invalid value in ACDB */ + cvp_session_cmd.cvp_session.tx_topology_id = + get_voice_tx_topology(); + if (cvp_session_cmd.cvp_session.tx_topology_id == 0) { + if (dev_tx_info->channel_mode > 1) + cvp_session_cmd.cvp_session.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE; + else + cvp_session_cmd.cvp_session.tx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS; + } + + cvp_session_cmd.cvp_session.rx_topology_id = + get_voice_rx_topology(); + if (cvp_session_cmd.cvp_session.rx_topology_id == 0) + cvp_session_cmd.cvp_session.rx_topology_id = + VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + + cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/ + cvp_session_cmd.cvp_session.network_id = VSS_NETWORK_ID_DEFAULT; + cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.dev_port_id; + cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.dev_port_id; + pr_info("topology=%d net_id=%d, dir=%d tx_port_id=%d, rx_port_id=%d\n", + cvp_session_cmd.cvp_session.tx_topology_id, + cvp_session_cmd.cvp_session.network_id, + cvp_session_cmd.cvp_session.direction, + cvp_session_cmd.cvp_session.tx_port_id, + cvp_session_cmd.cvp_session.rx_port_id); + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd); + if (ret < 0) { + pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n"); + goto fail; + } + pr_debug("wait for cvp create session event\n"); + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* send cvs cal */ + voice_send_cvs_cal_to_modem(v); + + /* send cvp cal */ + voice_send_cvp_cal_to_modem(v); + + /* send cvp vol table cal */ + voice_send_cvp_vol_tbl_to_modem(v); + + return 0; + +fail: + return -EINVAL; +} + +static int voice_send_enable_vocproc_cmd(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvp_enable_cmd; + + u16 cvp_handle = voice_get_cvp_handle(v); + void *apr_cvp = voice_get_apr_cvp(); + + /* enable vocproc and wait for respose */ + cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_enable_cmd) - APR_HDR_SIZE); + pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n", + cvp_enable_cmd.pkt_size, cvp_handle); + cvp_enable_cmd.src_port = v->session_id; + cvp_enable_cmd.dest_port = cvp_handle; + cvp_enable_cmd.token = 0; + cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE; + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_send_netid_timing_cmd(struct voice_data *v) +{ + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(); + struct mvm_set_network_cmd mvm_set_network; + struct mvm_set_voice_timing_cmd mvm_set_voice_timing; + u16 mvm_handle = voice_get_mvm_handle(v); + + ret = voice_config_cvs_vocoder(v); + if (ret < 0) { + pr_err("%s: Error %d configuring CVS voc", + __func__, ret); + goto fail; + } + /* Set network ID. */ + pr_debug("%s: Setting network ID\n", __func__); + + mvm_set_network.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_network) - APR_HDR_SIZE); + mvm_set_network.hdr.src_port = v->session_id; + mvm_set_network.hdr.dest_port = mvm_handle; + mvm_set_network.hdr.token = 0; + mvm_set_network.hdr.opcode = VSS_ICOMMON_CMD_SET_NETWORK; + mvm_set_network.network.network_id = common.mvs_info.network_type; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network); + if (ret < 0) { + pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* Set voice timing. */ + pr_debug("%s: Setting voice timing\n", __func__); + + mvm_set_voice_timing.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_voice_timing) - APR_HDR_SIZE); + mvm_set_voice_timing.hdr.src_port = v->session_id; + mvm_set_voice_timing.hdr.dest_port = mvm_handle; + mvm_set_voice_timing.hdr.token = 0; + mvm_set_voice_timing.hdr.opcode = + VSS_ICOMMON_CMD_SET_VOICE_TIMING; + mvm_set_voice_timing.timing.mode = 0; + mvm_set_voice_timing.timing.enc_offset = 8000; + mvm_set_voice_timing.timing.dec_req_offset = 3300; + mvm_set_voice_timing.timing.dec_offset = 8300; + + v->mvm_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing); + if (ret < 0) { + pr_err("%s: Error %d sending SET_TIMING\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + return 0; +fail: + return -EINVAL; +} + +static int voice_attach_vocproc(struct voice_data *v) +{ + int ret = 0; + struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd; + void *apr_mvm = voice_get_apr_mvm(); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* send enable vocproc */ + voice_send_enable_vocproc_cmd(v); + + /* attach vocproc and wait for response */ + mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE); + pr_info("send mvm_a_vocproc_cmd pkt size = %d\n", + mvm_a_vocproc_cmd.hdr.pkt_size); + mvm_a_vocproc_cmd.hdr.src_port = v->session_id; + mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_a_vocproc_cmd.hdr.token = 0; + mvm_a_vocproc_cmd.hdr.opcode = VSS_ISTREAM_CMD_ATTACH_VOCPROC; + mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_ISTREAM_CMD_ATTACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* send tty mode if tty device is used */ + voice_send_tty_mode_to_modem(v); + + if (is_voip_session(v->session_id)) + voice_send_netid_timing_cmd(v); + + rtac_add_voice(v->cvs_handle, v->cvp_handle, + v->dev_rx.dev_port_id, v->dev_tx.dev_port_id, + v->session_id); + + return 0; +fail: + return -EINVAL; +} + +static int voice_destroy_modem_voice(struct voice_data *v) +{ + struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd; + struct apr_hdr cvp_destroy_session_cmd; + int ret = 0; + void *apr_mvm = voice_get_apr_mvm(); + void *apr_cvp = voice_get_apr_cvp(); + u16 mvm_handle = voice_get_mvm_handle(v); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* detach VOCPROC and wait for response from mvm */ + mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE); + pr_info("mvm_d_vocproc_cmd pkt size = %d\n", + mvm_d_vocproc_cmd.hdr.pkt_size); + mvm_d_vocproc_cmd.hdr.src_port = v->session_id; + mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_d_vocproc_cmd.hdr.token = 0; + mvm_d_vocproc_cmd.hdr.opcode = VSS_ISTREAM_CMD_DETACH_VOCPROC; + mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_ISTREAM_CMD_DETACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + + /* destrop cvp session */ + cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE); + pr_info("cvp_destroy_session_cmd pkt size = %d\n", + cvp_destroy_session_cmd.pkt_size); + cvp_destroy_session_cmd.src_port = v->session_id; + cvp_destroy_session_cmd.dest_port = cvp_handle; + cvp_destroy_session_cmd.token = 0; + cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd); + if (ret < 0) { + pr_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + rtac_remove_voice(v->cvs_handle); + cvp_handle = 0; + voice_set_cvp_handle(v, cvp_handle); + + return 0; + +fail: + return -EINVAL; +} + +static int voice_send_mute_cmd_to_modem(struct voice_data *v) +{ + struct cvs_set_mute_cmd cvs_mute_cmd; + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + + /* send mute/unmute to cvs */ + cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_mute_cmd) - APR_HDR_SIZE); + cvs_mute_cmd.hdr.src_port = v->session_id; + cvs_mute_cmd.hdr.dest_port = cvs_handle; + cvs_mute_cmd.hdr.token = 0; + cvs_mute_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MUTE; + cvs_mute_cmd.cvs_set_mute.direction = 0; /*tx*/ + cvs_mute_cmd.cvs_set_mute.mute_flag = v->dev_tx.mute; + + pr_info(" mute value =%d\n", cvs_mute_cmd.cvs_set_mute.mute_flag); + v->cvs_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_mute_cmd); + if (ret < 0) { + pr_err("Fail: send STREAM SET MUTE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: wait_event timeout\n", __func__); + +fail: + return 0; +} + +static int voice_send_vol_index_to_modem(struct voice_data *v) +{ + struct cvp_set_rx_volume_index_cmd cvp_vol_cmd; + int ret = 0; + void *apr_cvp = voice_get_apr_cvp(); + u16 cvp_handle = voice_get_cvp_handle(v); + + /* send volume index to cvp */ + cvp_vol_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_vol_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_vol_cmd) - APR_HDR_SIZE); + cvp_vol_cmd.hdr.src_port = v->session_id; + cvp_vol_cmd.hdr.dest_port = cvp_handle; + cvp_vol_cmd.hdr.token = 0; + cvp_vol_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX; + cvp_vol_cmd.cvp_set_vol_idx.vol_index = v->dev_rx.volume; + v->cvp_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_cmd); + if (ret < 0) { + pr_err("Fail in sending RX VOL INDEX\n"); + return -EINVAL; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + return 0; +} + +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + struct cvs_start_record_cmd cvs_start_record; + + pr_debug("%s: Start record %d\n", __func__, rec_mode); + + cvs_start_record.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_record) - APR_HDR_SIZE); + cvs_start_record.hdr.src_port = v->session_id; + cvs_start_record.hdr.dest_port = cvs_handle; + cvs_start_record.hdr.token = 0; + cvs_start_record.hdr.opcode = VSS_ISTREAM_CMD_START_RECORD; + + if (rec_mode == VOC_REC_UPLINK) { + cvs_start_record.rec_mode.rx_tap_point = VSS_TAP_POINT_NONE; + cvs_start_record.rec_mode.tx_tap_point = + VSS_TAP_POINT_STREAM_END; + } else if (rec_mode == VOC_REC_DOWNLINK) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = VSS_TAP_POINT_NONE; + } else if (rec_mode == VOC_REC_BOTH) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = + VSS_TAP_POINT_STREAM_END; + } else { + pr_err("%s: Invalid in-call rec_mode %d\n", __func__, rec_mode); + + ret = -EINVAL; + goto fail; + } + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record); + if (ret < 0) { + pr_err("%s: Error %d sending START_RECORD\n", __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_record(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + struct apr_hdr cvs_stop_record; + + pr_debug("%s: Stop record\n", __func__); + + cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_record) - APR_HDR_SIZE); + cvs_stop_record.src_port = v->session_id; + cvs_stop_record.dest_port = cvs_handle; + cvs_stop_record.token = 0; + cvs_stop_record.opcode = VSS_ISTREAM_CMD_STOP_RECORD; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record); + if (ret < 0) { + pr_err("%s: Error %d sending STOP_RECORD\n", __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + return 0; + +fail: + return ret; +} + +int voice_start_record(uint32_t rec_mode, uint32_t set) +{ + int ret = 0, i; + u16 cvs_handle; + + pr_debug("%s: rec_mode %d, set %d\n", __func__, rec_mode, set); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + struct voice_data *v = &common.voice[i]; + + mutex_lock(&v->lock); + + cvs_handle = voice_get_cvs_handle(v); + + if (cvs_handle != 0) { + if (set) + ret = voice_cvs_start_record(v, rec_mode); + else + ret = voice_cvs_stop_record(v); + } else { + /* Cache the value for later. */ + v->rec_info.pending = set; + v->rec_info.rec_mode = rec_mode; + } + + mutex_unlock(&v->lock); + } + + return ret; +} + +static int voice_cvs_start_playback(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + struct apr_hdr cvs_start_playback; + + pr_debug("%s: Start playback\n", __func__); + + cvs_start_playback.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_start_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_playback) - APR_HDR_SIZE); + cvs_start_playback.src_port = v->session_id; + cvs_start_playback.dest_port = cvs_handle; + cvs_start_playback.token = 0; + cvs_start_playback.opcode = VSS_ISTREAM_CMD_START_PLAYBACK; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback); + if (ret < 0) { + pr_err("%s: Error %d sending START_PLAYBACK\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + v->music_info.playing = 1; + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_playback(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs = voice_get_apr_cvs(); + u16 cvs_handle = voice_get_cvs_handle(v); + struct apr_hdr cvs_stop_playback; + + pr_debug("%s: Stop playback\n", __func__); + + if (v->music_info.playing) { + cvs_stop_playback.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_playback) - APR_HDR_SIZE); + cvs_stop_playback.src_port = v->session_id; + cvs_stop_playback.dest_port = cvs_handle; + cvs_stop_playback.token = 0; + + cvs_stop_playback.opcode = VSS_ISTREAM_CMD_STOP_PLAYBACK; + + v->cvs_state = CMD_STATUS_FAIL; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback); + if (ret < 0) { + pr_err("%s: Error %d sending STOP_PLAYBACK\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + + v->music_info.playing = 0; + } else { + pr_err("%s: Stop playback already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +int voice_start_playback(uint32_t set) +{ + int ret = 0, i; + u16 cvs_handle; + + pr_debug("%s: Start playback %d\n", __func__, set); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + struct voice_data *v = &common.voice[i]; + + mutex_lock(&v->lock); + + cvs_handle = voice_get_cvs_handle(v); + + if (cvs_handle != 0) { + if (set) + ret = voice_cvs_start_playback(v); + else + ret = voice_cvs_stop_playback(v); + } else { + /* Cache the value for later. */ + pr_debug("%s: Caching ICP value", __func__); + + v->music_info.pending = set; + } + + mutex_unlock(&v->lock); + } + + return ret; +} + +static void voice_auddev_cb_function(u32 evt_id, + union auddev_evt_data *evt_payload, + void *private_data) +{ + struct common_data *c = private_data; + struct voice_data *v = NULL; + + struct sidetone_cal sidetone_cal_data; + int rc = 0, i = 0; + int rc1 = 0; + + pr_info("auddev_cb_function, evt_id=%d,\n", evt_id); + + if (evt_payload == NULL) { + pr_err("%s: evt_payload is NULL pointer\n", __func__); + return; + } + + switch (evt_id) { + case AUDDEV_EVT_START_VOICE: + v = voice_get_session(evt_payload->voice_session_id); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + mutex_lock(&v->lock); + + if ((v->voc_state == VOC_INIT) || + (v->voc_state == VOC_RELEASE)) { + v->v_call_status = VOICE_CALL_START; + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) + && (v->dev_tx.enabled == VOICE_DEV_ENABLED)) { + rc = voice_apr_register(); + if (rc < 0) { + pr_err("%s: voice apr registration" + "failed\n", __func__); + mutex_unlock(&v->lock); + return; + } + rc1 = voice_create_mvm_cvs_session(v); + if (rc1 < 0) { + pr_err("%s: create mvm-cvs failed\n", + __func__); + msleep(100); + rc = voice_apr_register(); + if (rc < 0) { + mutex_unlock(&v->lock); + pr_err("%s: voice apr regn" + "failed\n", __func__); + return; + } + rc1 = voice_create_mvm_cvs_session(v); + if (rc1 < 0) { + mutex_unlock(&v->lock); + pr_err("%s:Retry mvmcvs " + "failed\n", + __func__); + return; + } + } + voice_setup_modem_voice(v); + voice_attach_vocproc(v); + voice_send_start_voice_cmd(v); + get_sidetone_cal(&sidetone_cal_data); + msm_snddev_enable_sidetone( + v->dev_rx.dev_id, + sidetone_cal_data.enable, + sidetone_cal_data.gain); + v->voc_state = VOC_RUN; + + /* Start in-call recording if command was + * pending. */ + if (v->rec_info.pending) { + voice_cvs_start_record(v, + v->rec_info.rec_mode); + + v->rec_info.pending = 0; + } + + /* Start in-call music delivery if command was + * pending. */ + if (v->music_info.pending) { + voice_cvs_start_playback(v); + + v->music_info.pending = 0; + } + } + } + + mutex_unlock(&v->lock); + break; + case AUDDEV_EVT_DEV_CHG_VOICE: + /* Device change is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &c->voice[i]; + + if (v->dev_rx.enabled == VOICE_DEV_ENABLED) + msm_snddev_enable_sidetone(v->dev_rx.dev_id, + 0, 0); + + v->dev_rx.enabled = VOICE_DEV_DISABLED; + v->dev_tx.enabled = VOICE_DEV_DISABLED; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) { + /* send cmd to modem to do voice device + * change */ + voice_disable_vocproc(v); + v->voc_state = VOC_CHANGE; + } + + mutex_unlock(&v->lock); + } + break; + case AUDDEV_EVT_DEV_RDY: + /* Device change is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &c->voice[i]; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_CHANGE) { + /* get port Ids */ + if (evt_payload->voc_devinfo.dev_type == + DIR_RX) { + v->dev_rx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_rx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_rx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_rx.enabled = VOICE_DEV_ENABLED; + } else { + v->dev_tx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_tx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_tx.enabled = VOICE_DEV_ENABLED; + v->dev_tx.dev_id = + evt_payload->voc_devinfo.dev_id; + } + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) && + (v->dev_tx.enabled == VOICE_DEV_ENABLED)) { + voice_set_device(v); + get_sidetone_cal(&sidetone_cal_data); + msm_snddev_enable_sidetone( + v->dev_rx.dev_id, + sidetone_cal_data.enable, + sidetone_cal_data.gain); + v->voc_state = VOC_RUN; + } + } else if ((v->voc_state == VOC_INIT) || + (v->voc_state == VOC_RELEASE)) { + /* get AFE ports */ + if (evt_payload->voc_devinfo.dev_type == + DIR_RX) { + /* get rx port id */ + v->dev_rx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_rx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_rx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_rx.enabled = VOICE_DEV_ENABLED; + } else { + /* get tx port id */ + v->dev_tx.dev_port_id = + evt_payload->voc_devinfo.dev_port_id; + v->dev_tx.sample = + evt_payload->voc_devinfo.dev_sample; + v->dev_tx.dev_id = + evt_payload->voc_devinfo.dev_id; + v->dev_tx.enabled = VOICE_DEV_ENABLED; + } + if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) && + (v->dev_tx.enabled == VOICE_DEV_ENABLED) && + (v->v_call_status == VOICE_CALL_START)) { + rc = voice_apr_register(); + if (rc < 0) { + pr_err("%s: voice apr" + "registration failed\n", + __func__); + mutex_unlock(&v->lock); + return; + } + voice_create_mvm_cvs_session(v); + voice_setup_modem_voice(v); + voice_attach_vocproc(v); + voice_send_start_voice_cmd(v); + get_sidetone_cal(&sidetone_cal_data); + msm_snddev_enable_sidetone( + v->dev_rx.dev_id, + sidetone_cal_data.enable, + sidetone_cal_data.gain); + v->voc_state = VOC_RUN; + + /* Start in-call recording if command + * was pending. */ + if (v->rec_info.pending) { + voice_cvs_start_record(v, + v->rec_info.rec_mode); + + v->rec_info.pending = 0; + } + + /* Start in-call music delivery if + * command was pending. */ + if (v->music_info.pending) { + voice_cvs_start_playback(v); + + v->music_info.pending = 0; + } + } + } + + mutex_unlock(&v->lock); + } + break; + case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: + v = voice_get_session( + evt_payload->voc_vm_info.voice_session_id); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + /* cache the mute and volume index value */ + if (evt_payload->voc_vm_info.dev_type == DIR_TX) { + v->dev_tx.mute = + evt_payload->voc_vm_info.dev_vm_val.mute; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) + voice_send_mute_cmd_to_modem(v); + + mutex_unlock(&v->lock); + } else { + v->dev_rx.volume = + evt_payload->voc_vm_info.dev_vm_val.vol; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) + voice_send_vol_index_to_modem(v); + + mutex_unlock(&v->lock); + } + break; + case AUDDEV_EVT_REL_PENDING: + /* Device change is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &c->voice[i]; + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) { + voice_disable_vocproc(v); + v->voc_state = VOC_CHANGE; + } + + mutex_unlock(&v->lock); + + if (evt_payload->voc_devinfo.dev_type == DIR_RX) + v->dev_rx.enabled = VOICE_DEV_DISABLED; + else + v->dev_tx.enabled = VOICE_DEV_DISABLED; + } + + break; + case AUDDEV_EVT_END_VOICE: + v = voice_get_session(evt_payload->voice_session_id); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + /* recover the tx mute and rx volume to the default values */ + v->dev_tx.mute = c->default_mute_val; + v->dev_rx.volume = c->default_vol_val; + if (v->dev_rx.enabled == VOICE_DEV_ENABLED) + msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0, 0); + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN) { + /* call stop modem voice */ + voice_send_stop_voice_cmd(v); + voice_destroy_modem_voice(v); + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_RELEASE; + } else if (v->voc_state == VOC_CHANGE) { + voice_send_stop_voice_cmd(v); + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_RELEASE; + } + + mutex_unlock(&v->lock); + + v->v_call_status = VOICE_CALL_END; + + break; + default: + pr_err("UNKNOWN EVENT\n"); + } + return; +} +EXPORT_SYMBOL(voice_auddev_cb_function); + +int voice_set_voc_path_full(uint32_t set) +{ + pr_info("%s: %d\n", __func__, set); + + mutex_lock(&common.common_lock); + + if (set) + common.voc_path = VOC_PATH_FULL; + else + common.voc_path = VOC_PATH_PASSIVE; + + mutex_unlock(&common.common_lock); + + return 0; +} +EXPORT_SYMBOL(voice_set_voc_path_full); + +void voice_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + void *private_data) +{ + common.mvs_info.ul_cb = ul_cb; + common.mvs_info.dl_cb = dl_cb; + common.mvs_info.private_data = private_data; +} + +void voice_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode, + struct q_min_max_rate q_min_max_rate) +{ + common.mvs_info.media_type = media_type; + common.mvs_info.rate = rate; + common.mvs_info.network_type = network_type; + common.mvs_info.dtx_mode = dtx_mode; + common.mvs_info.q_min_max_rate = q_min_max_rate; +} + +static int32_t modem_mvm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr; + struct common_data *c = priv; + struct voice_data *v = NULL; + int i = 0; + + pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s:Reset event received in Voice service\n", + __func__); + apr_reset(c->apr_mvm); + c->apr_mvm = NULL; + apr_reset(c->apr_q6_mvm); + c->apr_q6_mvm = NULL; + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].mvm_handle = 0; + + return 0; + } + + v = voice_get_session(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: common data 0x%x, session 0x%x\n", + __func__, (unsigned int)c, (unsigned int)v); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_info("%x %x\n", ptr[0], ptr[1]); + /* ping mvm service ACK */ + + if (ptr[0] == + VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION || + ptr[0] == + VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION) { + /* Passive session is used for voice call + * through modem. Full session is used for voice + * call through Q6. */ + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + pr_debug("%s: MVM handle is %d\n", + __func__, data->src_port); + + voice_set_mvm_handle(v, data->src_port); + } else + pr_info("got NACK for sending \ + MVM create session \n"); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_START_VOICE) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_ATTACH_VOCPROC) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_STOP_VOICE) { + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_DETACH_VOCPROC) { + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_SET_TTY_MODE) { + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == APRV2_IBASIC_CMD_DESTROY_SESSION) { + pr_debug("%s: DESTROY resp\n", __func__); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_ATTACH_STREAM) { + pr_debug("%s: ATTACH_STREAM resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_IMVM_CMD_DETACH_STREAM) { + pr_debug("%s: DETACH_STREAM resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ICOMMON_CMD_SET_NETWORK) { + pr_debug("%s: SET_NETWORK resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else if (ptr[0] == VSS_ICOMMON_CMD_SET_VOICE_TIMING) { + pr_debug("%s: SET_VOICE_TIMING resp 0x%x\n", + __func__, ptr[1]); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } else + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + } + } + + return 0; +} + +static int32_t modem_cvs_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr; + struct common_data *c = priv; + struct voice_data *v = NULL; + int i = 0; + + pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s:Reset event received in Voice service\n", + __func__); + apr_reset(c->apr_cvs); + c->apr_cvs = NULL; + apr_reset(c->apr_q6_cvs); + c->apr_q6_cvs = NULL; + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].cvs_handle = 0; + + return 0; + } + + v = voice_get_session(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: common data 0x%x, session 0x%x\n", + __func__, (unsigned int)c, (unsigned int)v); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_info("%x %x\n", ptr[0], ptr[1]); + /*response from modem CVS */ + if (ptr[0] == + VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION || + ptr[0] == + VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION) { + if (!ptr[1]) { + pr_debug("%s: CVS handle is %d\n", + __func__, data->src_port); + voice_set_cvs_handle(v, data->src_port); + } else + pr_info("got NACK for sending \ + CVS create session \n"); + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_CACHE_CALIBRATION_DATA) { + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_SET_MUTE) { + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_SET_MEDIA_TYPE) { + pr_debug("%s: SET_MEDIA resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE) { + pr_debug("%s: SET_AMR_RATE resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE) { + pr_debug("%s: SET_AMR_WB_RATE resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_SET_ENC_DTX_MODE) { + pr_debug("%s: SET_DTX resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == + VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE) { + pr_debug("%s: SET_CDMA_RATE resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == APRV2_IBASIC_CMD_DESTROY_SESSION) { + pr_debug("%s: DESTROY resp\n", __func__); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_START_RECORD) { + pr_debug("%s: START_RECORD resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_STOP_RECORD) { + pr_debug("%s: STOP_RECORD resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VOICE_CMD_SET_PARAM) { + rtac_make_voice_callback(RTAC_CVS, ptr, + data->payload_size); + } else if (ptr[0] == VSS_ISTREAM_CMD_START_PLAYBACK) { + pr_debug("%s: START_PLAYBACK resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else if (ptr[0] == VSS_ISTREAM_CMD_STOP_PLAYBACK) { + pr_debug("%s: STOP_PLAYBACK resp 0x%x\n", + __func__, ptr[1]); + + v->cvs_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvs_wait); + } else + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + } + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) { + uint32_t *voc_pkt = data->payload; + uint32_t pkt_len = data->payload_size; + + if (voc_pkt != NULL && c->mvs_info.ul_cb != NULL) { + pr_debug("%s: Media type is 0x%x\n", + __func__, voc_pkt[0]); + + /* Remove media ID from payload. */ + voc_pkt++; + pkt_len = pkt_len - 4; + + c->mvs_info.ul_cb((uint8_t *)voc_pkt, + pkt_len, + c->mvs_info.private_data); + } else { + pr_err("%s: voc_pkt is 0x%x ul_cb is 0x%x\n", + __func__, (unsigned int)voc_pkt, + (unsigned int)c->mvs_info.ul_cb); + } + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER) { + pr_debug("%s: Send dec buf resp\n", __func__); + } else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) { + struct cvs_send_dec_buf_cmd send_dec_buf; + int ret = 0; + uint32_t pkt_len = 0; + + if (c->mvs_info.dl_cb != NULL) { + send_dec_buf.dec_buf.media_id = c->mvs_info.media_type; + + c->mvs_info.dl_cb( + (uint8_t *)&send_dec_buf.dec_buf.packet_data, + &pkt_len, + c->mvs_info.private_data); + + send_dec_buf.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(send_dec_buf.dec_buf.media_id) + pkt_len); + send_dec_buf.hdr.src_port = v->session_id; + send_dec_buf.hdr.dest_port = voice_get_cvs_handle(v); + send_dec_buf.hdr.token = 0; + send_dec_buf.hdr.opcode = + VSS_ISTREAM_EVT_SEND_DEC_BUFFER; + + ret = apr_send_pkt(voice_get_apr_cvs(), + (uint32_t *) &send_dec_buf); + if (ret < 0) { + pr_err("%s: Error %d sending DEC_BUF\n", + __func__, ret); + goto fail; + } + } else { + pr_err("%s: ul_cb is NULL\n", __func__); + } + } else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) { + rtac_make_voice_callback(RTAC_CVS, data->payload, + data->payload_size); + } else { + pr_debug("%s: Unknown opcode 0x%x\n", __func__, data->opcode); + } + +fail: + return 0; +} + +static int32_t modem_cvp_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr; + struct common_data *c = priv; + struct voice_data *v = NULL; + int i = 0; + + pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s:Reset event received in Voice service\n", + __func__); + apr_reset(c->apr_cvp); + c->apr_cvp = NULL; + apr_reset(c->apr_q6_cvp); + c->apr_q6_cvp = NULL; + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].cvp_handle = 0; + + return 0; + } + + v = voice_get_session(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: common data 0x%x, session 0x%x\n", + __func__, (unsigned int)c, (unsigned int)v); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_info("%x %x\n", ptr[0], ptr[1]); + /*response from modem CVP */ + if (ptr[0] == + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + voice_set_cvp_handle(v, data->src_port); + pr_debug("cvphdl=%d\n", data->src_port); + } else + pr_info("got NACK from CVP create \ + session response\n"); + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == + VSS_IVOCPROC_CMD_CACHE_CALIBRATION_DATA) { + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VSS_IVOCPROC_CMD_SET_DEVICE) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == + VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VSS_IVOCPROC_CMD_ENABLE) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VSS_IVOCPROC_CMD_DISABLE) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == APRV2_IBASIC_CMD_DESTROY_SESSION) { + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == + VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE + ) { + + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } else if (ptr[0] == VOICE_CMD_SET_PARAM) { + rtac_make_voice_callback(RTAC_CVP, ptr, + data->payload_size); + } else + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + } + } else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) { + rtac_make_voice_callback(RTAC_CVP, data->payload, + data->payload_size); + } + return 0; +} + + +static int __init voice_init(void) +{ + int rc = 0, i = 0; + + memset(&common, 0, sizeof(struct common_data)); + + /* set default value */ + common.default_mute_val = 1; /* default is mute */ + common.default_vol_val = 0; + common.default_sample_val = 8000; + + common.voc_path = VOC_PATH_PASSIVE; + + /* Initialize MVS info. */ + common.mvs_info.network_type = VSS_NETWORK_ID_DEFAULT; + + mutex_init(&common.common_lock); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + common.voice[i].session_id = SESSION_ID_BASE + i; + + common.voice[i].dev_rx.volume = common.default_vol_val; + common.voice[i].dev_tx.mute = common.default_mute_val; + + common.voice[i].voc_state = VOC_INIT; + + common.voice[i].rec_info.rec_mode = VOC_REC_NONE; + + init_waitqueue_head(&common.voice[i].mvm_wait); + init_waitqueue_head(&common.voice[i].cvs_wait); + init_waitqueue_head(&common.voice[i].cvp_wait); + + mutex_init(&common.voice[i].lock); + + } + + common.device_events = AUDDEV_EVT_DEV_CHG_VOICE | + AUDDEV_EVT_DEV_RDY | + AUDDEV_EVT_REL_PENDING | + AUDDEV_EVT_START_VOICE | + AUDDEV_EVT_END_VOICE | + AUDDEV_EVT_DEVICE_VOL_MUTE_CHG | + AUDDEV_EVT_FREQ_CHG; + + pr_debug("to register call back\n"); + /* register callback to auddev */ + auddev_register_evt_listner(common.device_events, AUDDEV_CLNT_VOC, + 0, voice_auddev_cb_function, &common); + + return rc; +} + +device_initcall(voice_init); diff --git a/arch/arm/mach-msm/qdsp6v2/qcelp_in.c b/arch/arm/mach-msm/qdsp6v2/qcelp_in.c new file mode 100644 index 00000000000..a48df3958ec --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/qcelp_in.c @@ -0,0 +1,290 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long qcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_qcelp_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* reduced_rate_level, rate_modulation_cmd set to zero + currently not configurable from user space */ + rc = q6asm_enc_cfg_blk_qcelp(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd qcelp media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + struct msm_audio_qcelp_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_qcelp_enc_config))) { + rc = -EFAULT; + break; + } + + if (cfg.min_bit_rate > 4 || + cfg.min_bit_rate < 1) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg.max_bit_rate > 4 || + cfg.max_bit_rate < 1) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg.min_bit_rate; + enc_cfg->max_bit_rate = cfg.max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x" + "max_bit_rate=0x%x\n", __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int qcelp_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_qcelp_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for qcelp" + "driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 35; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio" + "client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open qcelp encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_V13K, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = qcelp_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = qcelp_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_in_fops, +}; + +static int __init qcelp_in_init(void) +{ + return misc_register(&audio_qcelp_in_misc); +} + +device_initcall(qcelp_in_init); diff --git a/arch/arm/mach-msm/qdsp6v2/rtac.c b/arch/arm/mach-msm/qdsp6v2/rtac.c new file mode 100644 index 00000000000..4ce9b03020c --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/rtac.c @@ -0,0 +1,1024 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_RTAC + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id) {} +void rtac_remove_adm_device(u32 port_id) {} +void rtac_remove_popp_from_adm_devices(u32 popp_id) {} +void rtac_set_adm_handle(void *handle) {} +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size) + {return false; } +void rtac_set_asm_handle(u32 session_id, void *handle) {} +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size) {return false; } +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 session_id) {} +void rtac_remove_voice(u32 cvs_handle) {} +void rtac_set_voice_handle(u32 mode, void *handle) {} +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, + u32 payload_size) {return false; } + +#else + +#define VOICE_CMD_SET_PARAM 0x00011006 +#define VOICE_CMD_GET_PARAM 0x00011007 +#define VOICE_EVT_GET_PARAM_ACK 0x00011008 + +/* Max size of payload (buf size - apr header) */ +#define MAX_PAYLOAD_SIZE 4076 +#define RTAC_MAX_ACTIVE_DEVICES 4 +#define RTAC_MAX_ACTIVE_VOICE_COMBOS 2 +#define RTAC_MAX_ACTIVE_POPP 8 +#define RTAC_BUF_SIZE 4096 + +#define TIMEOUT_MS 1000 + +/* APR data */ +struct rtac_apr_data { + void *apr_handle; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; +}; + +static struct rtac_apr_data rtac_adm_apr_data; +static struct rtac_apr_data rtac_asm_apr_data[SESSION_MAX+1]; +static struct rtac_apr_data rtac_voice_apr_data[RTAC_VOICE_MODES]; + + +/* ADM info & APR */ +struct rtac_adm_data { + uint32_t topology_id; + uint32_t afe_port; + uint32_t copp; + uint32_t num_of_popp; + uint32_t popp[RTAC_MAX_ACTIVE_POPP]; +}; + +struct rtac_adm { + uint32_t num_of_dev; + struct rtac_adm_data device[RTAC_MAX_ACTIVE_DEVICES]; +}; +static struct rtac_adm rtac_adm_data; +static u32 rtac_adm_payload_size; +static u32 rtac_adm_user_buf_size; +static u8 *rtac_adm_buffer; + + +/* ASM APR */ +static u32 rtac_asm_payload_size; +static u32 rtac_asm_user_buf_size; +static u8 *rtac_asm_buffer; + + +/* Voice info & APR */ +struct rtac_voice_data { + uint32_t tx_topology_id; + uint32_t rx_topology_id; + uint32_t tx_afe_port; + uint32_t rx_afe_port; + uint16_t cvs_handle; + uint16_t cvp_handle; +}; + +struct rtac_voice { + uint32_t num_of_voice_combos; + struct rtac_voice_data voice[RTAC_MAX_ACTIVE_VOICE_COMBOS]; +}; + +static struct rtac_voice rtac_voice_data; +static u32 rtac_voice_payload_size; +static u32 rtac_voice_user_buf_size; +static u8 *rtac_voice_buffer; +static u32 voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS]; + + +struct mutex rtac_adm_mutex; +struct mutex rtac_adm_apr_mutex; +struct mutex rtac_asm_apr_mutex; +struct mutex rtac_voice_mutex; +struct mutex rtac_voice_apr_mutex; + +static int rtac_open(struct inode *inode, struct file *f) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int rtac_release(struct inode *inode, struct file *f) +{ + pr_debug("%s\n", __func__); + return 0; +} + +/* ADM Info */ +void add_popp(u32 dev_idx, u32 port_id, u32 popp_id) +{ + u32 i = 0; + + for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++) + if (rtac_adm_data.device[dev_idx].popp[i] == popp_id) + goto done; + + if (rtac_adm_data.device[dev_idx].num_of_popp == + RTAC_MAX_ACTIVE_POPP) { + pr_err("%s, Max POPP!\n", __func__); + goto done; + } + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp++] = popp_id; +done: + return; +} + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id) +{ + u32 i = 0; + pr_debug("%s: port_id = %d, popp_id = %d\n", __func__, port_id, + popp_id); + + mutex_lock(&rtac_adm_mutex); + if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) { + pr_err("%s, Can't add anymore RTAC devices!\n", __func__); + goto done; + } + + /* Check if device already added */ + if (rtac_adm_data.num_of_dev != 0) { + for (; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id) { + add_popp(i, port_id, popp_id); + goto done; + } + if (rtac_adm_data.device[i].num_of_popp == + RTAC_MAX_ACTIVE_POPP) { + pr_err("%s, Max POPP!\n", __func__); + goto done; + } + } + } + + /* Add device */ + rtac_adm_data.num_of_dev++; + + if (path_id == ADM_PATH_PLAYBACK) + rtac_adm_data.device[i].topology_id = + get_adm_rx_topology(); + else + rtac_adm_data.device[i].topology_id = + get_adm_tx_topology(); + rtac_adm_data.device[i].afe_port = port_id; + rtac_adm_data.device[i].copp = copp_id; + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp++] = popp_id; +done: + mutex_unlock(&rtac_adm_mutex); + return; +} + +static void shift_adm_devices(u32 dev_idx) +{ + for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) { + memcpy(&rtac_adm_data.device[dev_idx], + &rtac_adm_data.device[dev_idx + 1], + sizeof(rtac_adm_data.device[dev_idx])); + memset(&rtac_adm_data.device[dev_idx + 1], 0, + sizeof(rtac_adm_data.device[dev_idx])); + } +} + +static void shift_popp(u32 copp_idx, u32 popp_idx) +{ + for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp; + popp_idx++) { + memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx], + &rtac_adm_data.device[copp_idx].popp[popp_idx + 1], + sizeof(uint32_t)); + memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1], 0, + sizeof(uint32_t)); + } +} + +void rtac_remove_adm_device(u32 port_id) +{ + s32 i; + pr_debug("%s: port_id = %d\n", __func__, port_id); + + mutex_lock(&rtac_adm_mutex); + /* look for device */ + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id) { + memset(&rtac_adm_data.device[i], 0, + sizeof(rtac_adm_data.device[i])); + rtac_adm_data.num_of_dev--; + + if (rtac_adm_data.num_of_dev >= 1) { + shift_adm_devices(i); + break; + } + } + } + + mutex_unlock(&rtac_adm_mutex); + return; +} + +void rtac_remove_popp_from_adm_devices(u32 popp_id) +{ + s32 i, j; + pr_debug("%s: popp_id = %d\n", __func__, popp_id); + + mutex_lock(&rtac_adm_mutex); + + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) { + if (rtac_adm_data.device[i].popp[j] == popp_id) { + rtac_adm_data.device[i].popp[j] = 0; + rtac_adm_data.device[i].num_of_popp--; + shift_popp(i, j); + } + } + } + + mutex_unlock(&rtac_adm_mutex); +} + +/* Voice Info */ +static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle, + u32 rx_afe_port, u32 tx_afe_port, + u32 session_id) +{ + rtac_voice_data.voice[idx].tx_topology_id = get_voice_tx_topology(); + rtac_voice_data.voice[idx].rx_topology_id = get_voice_rx_topology(); + rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port; + rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port; + rtac_voice_data.voice[idx].cvs_handle = cvs_handle; + rtac_voice_data.voice[idx].cvp_handle = cvp_handle; + + /* Store session ID for voice RTAC */ + voice_session_id[idx] = session_id; +} + +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 session_id) +{ + u32 i = 0; + pr_debug("%s\n", __func__); + mutex_lock(&rtac_voice_mutex); + + if (rtac_voice_data.num_of_voice_combos == + RTAC_MAX_ACTIVE_VOICE_COMBOS) { + pr_err("%s, Can't add anymore RTAC devices!\n", __func__); + goto done; + } + + /* Check if device already added */ + if (rtac_voice_data.num_of_voice_combos != 0) { + for (; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == + cvs_handle) { + set_rtac_voice_data(i, cvs_handle, cvp_handle, + rx_afe_port, tx_afe_port, + session_id); + goto done; + } + } + } + + /* Add device */ + rtac_voice_data.num_of_voice_combos++; + set_rtac_voice_data(i, cvs_handle, cvp_handle, + rx_afe_port, tx_afe_port, + session_id); +done: + mutex_unlock(&rtac_voice_mutex); + return; +} + +static void shift_voice_devices(u32 idx) +{ + for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) { + memcpy(&rtac_voice_data.voice[idx], + &rtac_voice_data.voice[idx + 1], + sizeof(rtac_voice_data.voice[idx])); + voice_session_id[idx] = voice_session_id[idx + 1]; + } +} + +void rtac_remove_voice(u32 cvs_handle) +{ + u32 i = 0; + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_voice_mutex); + /* look for device */ + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) { + shift_voice_devices(i); + rtac_voice_data.num_of_voice_combos--; + memset(&rtac_voice_data.voice[ + rtac_voice_data.num_of_voice_combos], 0, + sizeof(rtac_voice_data.voice + [rtac_voice_data.num_of_voice_combos])); + voice_session_id[rtac_voice_data.num_of_voice_combos] + = 0; + break; + } + } + mutex_unlock(&rtac_voice_mutex); + return; +} + +static int get_voice_index(u32 cvs_handle) +{ + u32 i; + + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) + return i; + } + + pr_err("%s: No voice index for CVS handle %d found returning 0\n", + __func__, cvs_handle); + return 0; +} + + +/* ADM APR */ +void rtac_set_adm_handle(void *handle) +{ + pr_debug("%s: handle = %d\n", __func__, (unsigned int)handle); + + mutex_lock(&rtac_adm_apr_mutex); + rtac_adm_apr_data.apr_handle = handle; + mutex_unlock(&rtac_adm_apr_mutex); +} + +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size) +{ + pr_debug("%s:cmd_state = %d\n", __func__, + atomic_read(&rtac_adm_apr_data.cmd_state)); + if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1) + return false; + + /* Offset data for in-band payload */ + rtac_copy_adm_payload_to_user(payload, payload_size); + atomic_set(&rtac_adm_apr_data.cmd_state, 0); + wake_up(&rtac_adm_apr_data.cmd_wait); + return true; +} + +void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size) +{ + pr_debug("%s\n", __func__); + rtac_adm_payload_size = payload_size; + + memcpy(rtac_adm_buffer, &payload_size, sizeof(u32)); + if (payload_size != 0) { + if (payload_size > rtac_adm_user_buf_size) { + pr_err("%s: Buffer set not big enough for " + "returned data, buf size = %d, " + "ret data = %d\n", __func__, + rtac_adm_user_buf_size, payload_size); + goto done; + } + memcpy(rtac_adm_buffer + sizeof(u32), payload, payload_size); + } +done: + return; +} + +u32 send_adm_apr(void *buf, u32 opcode) +{ + s32 result; + u32 count = 0; + u32 bytes_returned = 0; + u32 port_index = 0; + u32 copp_id; + u32 payload_size; + struct apr_hdr adm_params; + pr_debug("%s\n", __func__); + + if (copy_from_user(&count, (void *)buf, sizeof(count))) { + pr_err("%s: Copy to user failed! buf = 0x%x\n", + __func__, (unsigned int)buf); + result = -EFAULT; + goto done; + } + + if (count <= 0) { + pr_err("%s: Invalid buffer size = %d\n", __func__, count); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + + if (payload_size > MAX_PAYLOAD_SIZE) { + + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + goto done; + } + + if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy port id from user buffer\n", + __func__); + goto done; + } + + for (port_index = 0; port_index < AFE_MAX_PORTS; port_index++) { + if (adm_get_copp_id(port_index) == copp_id) + break; + } + if (port_index >= AFE_MAX_PORTS) { + pr_err("%s: Could not find port index for copp = %d\n", + __func__, copp_id); + goto done; + } + + mutex_lock(&rtac_adm_apr_mutex); + if (rtac_adm_apr_data.apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + goto err; + } + + /* Set globals for copy of returned payload */ + rtac_adm_user_buf_size = count; + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_adm_buffer + sizeof(adm_params), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + goto err; + } + + /* Pack header */ + adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + adm_params.src_svc = APR_SVC_ADM; + adm_params.src_domain = APR_DOMAIN_APPS; + adm_params.src_port = copp_id; + adm_params.dest_svc = APR_SVC_ADM; + adm_params.dest_domain = APR_DOMAIN_ADSP; + adm_params.dest_port = copp_id; + adm_params.token = copp_id; + adm_params.opcode = opcode; + + memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params)); + atomic_set(&rtac_adm_apr_data.cmd_state, 1); + + pr_debug("%s: Sending RTAC command size = %d\n", + __func__, adm_params.pkt_size); + + result = apr_send_pkt(rtac_adm_apr_data.apr_handle, + (uint32_t *)rtac_adm_buffer); + if (result < 0) { + pr_err("%s: Set params failed port = %d, copp = %d\n", + __func__, port_index, copp_id); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_adm_apr_data.cmd_wait, + (atomic_read(&rtac_adm_apr_data.cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + mutex_unlock(&rtac_adm_apr_mutex); + if (!result) { + pr_err("%s: Set params timed out port = %d, copp = %d\n", + __func__, port_index, copp_id); + goto done; + } + + if (rtac_adm_payload_size != 0) { + if (copy_to_user(buf, rtac_adm_buffer, + rtac_adm_payload_size + sizeof(u32))) { + pr_err("%s: Could not copy buffer to user," + "size = %d\n", __func__, payload_size); + goto done; + } + } + + /* Return data written for SET & data read for GET */ + if (opcode == ADM_CMD_GET_PARAMS) + bytes_returned = rtac_adm_payload_size; + else + bytes_returned = payload_size; +done: + return bytes_returned; +err: + mutex_unlock(&rtac_adm_apr_mutex); + return bytes_returned; +} + + +/* ASM APR */ +void rtac_set_asm_handle(u32 session_id, void *handle) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_asm_apr_mutex); + rtac_asm_apr_data[session_id].apr_handle = handle; + mutex_unlock(&rtac_asm_apr_mutex); +} + +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size) +{ + if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1) + return false; + + pr_debug("%s\n", __func__); + /* Offset data for in-band payload */ + rtac_copy_asm_payload_to_user(payload, payload_size); + atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0); + wake_up(&rtac_asm_apr_data[session_id].cmd_wait); + return true; +} + +void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size) +{ + pr_debug("%s\n", __func__); + rtac_asm_payload_size = payload_size; + + memcpy(rtac_asm_buffer, &payload_size, sizeof(u32)); + if (payload_size) { + if (payload_size > rtac_asm_user_buf_size) { + pr_err("%s: Buffer set not big enough for " + "returned data, buf size = %d, " + "ret data = %d\n", __func__, + rtac_asm_user_buf_size, payload_size); + goto done; + } + memcpy(rtac_asm_buffer + sizeof(u32), payload, payload_size); + } +done: + return; +} + +u32 send_rtac_asm_apr(void *buf, u32 opcode) +{ + s32 result; + u32 count = 0; + u32 bytes_returned = 0; + u32 session_id = 0; + u32 payload_size; + struct apr_hdr asm_params; + pr_debug("%s\n", __func__); + + if (copy_from_user(&count, (void *)buf, sizeof(count))) { + pr_err("%s: Copy to user failed! buf = 0x%x\n", + __func__, (unsigned int)buf); + result = -EFAULT; + goto done; + } + + if (count <= 0) { + pr_err("%s: Invalid buffer size = %d\n", __func__, count); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (payload_size > MAX_PAYLOAD_SIZE) { + + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + goto done; + } + + if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy session id from user buffer\n", + __func__); + goto done; + } + if (session_id > (SESSION_MAX + 1)) { + pr_err("%s: Invalid Session = %d\n", __func__, session_id); + goto done; + } + + mutex_lock(&rtac_asm_apr_mutex); + if (session_id < SESSION_MAX+1) { + if (rtac_asm_apr_data[session_id].apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + goto err; + } + } + + /* Set globals for copy of returned payload */ + rtac_asm_user_buf_size = count; + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_asm_buffer + sizeof(asm_params), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + goto err; + } + + /* Pack header */ + asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + asm_params.src_svc = q6asm_get_apr_service_id(session_id); + asm_params.src_domain = APR_DOMAIN_APPS; + asm_params.src_port = (session_id << 8) | 0x0001; + asm_params.dest_svc = APR_SVC_ASM; + asm_params.dest_domain = APR_DOMAIN_ADSP; + asm_params.dest_port = (session_id << 8) | 0x0001; + asm_params.token = session_id; + asm_params.opcode = opcode; + + memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params)); + if (session_id < SESSION_MAX+1) + atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1); + + pr_debug("%s: Sending RTAC command size = %d, session_id=%d\n", + __func__, asm_params.pkt_size, session_id); + + result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle, + (uint32_t *)rtac_asm_buffer); + if (result < 0) { + pr_err("%s: Set params failed session = %d\n", + __func__, session_id); + goto err; + } + + /* Wait for the callback */ + result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait, + (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0), + 5 * HZ); + mutex_unlock(&rtac_asm_apr_mutex); + if (!result) { + pr_err("%s: Set params timed out session = %d\n", + __func__, session_id); + goto done; + } + + if (rtac_asm_payload_size != 0) { + if (copy_to_user(buf, rtac_asm_buffer, + rtac_asm_payload_size + sizeof(u32))) { + pr_err("%s: Could not copy buffer to user," + "size = %d\n", __func__, payload_size); + goto done; + } + } + + /* Return data written for SET & data read for GET */ + if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS) + bytes_returned = rtac_asm_payload_size; + else + bytes_returned = payload_size; +done: + return bytes_returned; +err: + mutex_unlock(&rtac_asm_apr_mutex); + return bytes_returned; +} + + +/* Voice APR */ +void rtac_set_voice_handle(u32 mode, void *handle) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_voice_apr_mutex); + rtac_voice_apr_data[mode].apr_handle = handle; + mutex_unlock(&rtac_voice_apr_mutex); +} + +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size) +{ + if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) || + (mode >= RTAC_VOICE_MODES)) + return false; + + pr_debug("%s\n", __func__); + /* Offset data for in-band payload */ + rtac_copy_voice_payload_to_user(payload, payload_size); + atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0); + wake_up(&rtac_voice_apr_data[mode].cmd_wait); + return true; +} + +void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size) +{ + pr_debug("%s\n", __func__); + rtac_voice_payload_size = payload_size; + + memcpy(rtac_voice_buffer, &payload_size, sizeof(u32)); + if (payload_size) { + if (payload_size > rtac_voice_user_buf_size) { + pr_err("%s: Buffer set not big enough for " + "returned data, buf size = %d, " + "ret data = %d\n", __func__, + rtac_voice_user_buf_size, payload_size); + goto done; + } + memcpy(rtac_voice_buffer + sizeof(u32), payload, payload_size); + } +done: + return; +} + +u32 send_voice_apr(u32 mode, void *buf, u32 opcode) +{ + s32 result; + u32 count = 0; + u32 bytes_returned = 0; + u32 payload_size; + u16 dest_port; + struct apr_hdr voice_params; + pr_debug("%s\n", __func__); + + if (copy_from_user(&count, (void *)buf, sizeof(count))) { + pr_err("%s: Copy to user failed! buf = 0x%x\n", + __func__, (unsigned int)buf); + result = -EFAULT; + goto done; + } + + if (count <= 0) { + pr_err("%s: Invalid buffer size = %d\n", __func__, count); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + goto done; + } + + if (copy_from_user(&dest_port, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy port id from user buffer\n", + __func__); + goto done; + } + + if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) { + pr_err("%s: Invalid Mode for APR, mode = %d\n", + __func__, mode); + goto done; + } + + mutex_lock(&rtac_voice_apr_mutex); + if (rtac_voice_apr_data[mode].apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + goto err; + } + + /* Set globals for copy of returned payload */ + rtac_voice_user_buf_size = count; + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_voice_buffer + sizeof(voice_params), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + goto err; + } + + /* Pack header */ + voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + voice_params.src_svc = 0; + voice_params.src_domain = APR_DOMAIN_APPS; + voice_params.src_port = voice_session_id[ + get_voice_index(dest_port)]; + voice_params.dest_svc = 0; + voice_params.dest_domain = APR_DOMAIN_MODEM; + voice_params.dest_port = dest_port; + voice_params.token = 0; + voice_params.opcode = opcode; + + memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params)); + atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1); + + pr_debug("%s: Sending RTAC command size = %d, opcode = %x\n", + __func__, voice_params.pkt_size, opcode); + + result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle, + (uint32_t *)rtac_voice_buffer); + if (result < 0) { + pr_err("%s: apr_send_pkt failed opcode = %x\n", + __func__, opcode); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait, + (atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + mutex_unlock(&rtac_voice_apr_mutex); + if (!result) { + pr_err("%s: apr_send_pkt timed out opcode = %x\n", + __func__, opcode); + goto done; + } + + if (rtac_voice_payload_size != 0) { + if (copy_to_user(buf, rtac_voice_buffer, + rtac_voice_payload_size + sizeof(u32))) { + pr_err("%s: Could not copy buffer to user," + "size = %d\n", __func__, payload_size); + goto done; + } + } + + /* Return data written for SET & data read for GET */ + if (opcode == VOICE_CMD_GET_PARAM) + bytes_returned = rtac_voice_payload_size; + else + bytes_returned = payload_size; +done: + return bytes_returned; +err: + mutex_unlock(&rtac_voice_apr_mutex); + return bytes_returned; +} + + + +static long rtac_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + s32 result = 0; + pr_debug("%s\n", __func__); + + if (arg == 0) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EFAULT; + goto done; + } + + switch (cmd) { + case AUDIO_GET_RTAC_ADM_INFO: + if (copy_to_user((void *)arg, &rtac_adm_data, + sizeof(rtac_adm_data))) + pr_err("%s: Could not copy to userspace!\n", __func__); + else + result = sizeof(rtac_adm_data); + break; + case AUDIO_GET_RTAC_VOICE_INFO: + if (copy_to_user((void *)arg, &rtac_voice_data, + sizeof(rtac_voice_data))) + pr_err("%s: Could not copy to userspace!\n", __func__); + else + result = sizeof(rtac_voice_data); + break; + case AUDIO_GET_RTAC_ADM_CAL: + result = send_adm_apr((void *)arg, ADM_CMD_GET_PARAMS); + break; + case AUDIO_SET_RTAC_ADM_CAL: + result = send_adm_apr((void *)arg, ADM_CMD_SET_PARAMS); + break; + case AUDIO_GET_RTAC_ASM_CAL: + result = send_rtac_asm_apr((void *)arg, + ASM_STREAM_CMD_GET_PP_PARAMS); + break; + case AUDIO_SET_RTAC_ASM_CAL: + result = send_rtac_asm_apr((void *)arg, + ASM_STREAM_CMD_SET_PP_PARAMS); + break; + case AUDIO_GET_RTAC_CVS_CAL: + result = send_voice_apr(RTAC_CVS, (void *)arg, + VOICE_CMD_GET_PARAM); + break; + case AUDIO_SET_RTAC_CVS_CAL: + result = send_voice_apr(RTAC_CVS, (void *)arg, + VOICE_CMD_SET_PARAM); + break; + case AUDIO_GET_RTAC_CVP_CAL: + result = send_voice_apr(RTAC_CVP, (void *)arg, + VOICE_CMD_GET_PARAM); + break; + case AUDIO_SET_RTAC_CVP_CAL: + result = send_voice_apr(RTAC_CVP, (void *)arg, + VOICE_CMD_SET_PARAM); + break; + default: + pr_err("%s: Invalid IOCTL, command = %d!\n", + __func__, cmd); + } +done: + return result; +} + + +static const struct file_operations rtac_fops = { + .owner = THIS_MODULE, + .open = rtac_open, + .release = rtac_release, + .unlocked_ioctl = rtac_ioctl, +}; + +struct miscdevice rtac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_rtac", + .fops = &rtac_fops, +}; + +static int __init rtac_init(void) +{ + int i = 0; + pr_debug("%s\n", __func__); + + /* ADM */ + memset(&rtac_adm_data, 0, sizeof(rtac_adm_data)); + rtac_adm_apr_data.apr_handle = NULL; + atomic_set(&rtac_adm_apr_data.cmd_state, 0); + init_waitqueue_head(&rtac_adm_apr_data.cmd_wait); + mutex_init(&rtac_adm_mutex); + mutex_init(&rtac_adm_apr_mutex); + + rtac_adm_buffer = kmalloc(RTAC_BUF_SIZE, GFP_KERNEL); + if (rtac_adm_buffer == NULL) { + pr_err("%s: Could not allocate payload of size = %d\n", + __func__, RTAC_BUF_SIZE); + goto nomem; + } + + /* ASM */ + for (i = 0; i < SESSION_MAX+1; i++) { + rtac_asm_apr_data[i].apr_handle = NULL; + atomic_set(&rtac_asm_apr_data[i].cmd_state, 0); + init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait); + } + mutex_init(&rtac_asm_apr_mutex); + + rtac_asm_buffer = kmalloc(RTAC_BUF_SIZE, GFP_KERNEL); + if (rtac_asm_buffer == NULL) { + pr_err("%s: Could not allocate payload of size = %d\n", + __func__, RTAC_BUF_SIZE); + goto nomem; + } + + /* Voice */ + memset(&rtac_voice_data, 0, sizeof(rtac_voice_data)); + for (i = 0; i < RTAC_VOICE_MODES; i++) { + rtac_voice_apr_data[i].apr_handle = NULL; + atomic_set(&rtac_voice_apr_data[i].cmd_state, 0); + init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait); + } + mutex_init(&rtac_voice_mutex); + mutex_init(&rtac_voice_apr_mutex); + + rtac_voice_buffer = kmalloc(RTAC_BUF_SIZE, GFP_KERNEL); + if (rtac_voice_buffer == NULL) { + pr_err("%s: Could not allocate payload of size = %d\n", + __func__, RTAC_BUF_SIZE); + goto nomem; + } + + return misc_register(&rtac_misc); +nomem: + return -ENOMEM; +} + +module_init(rtac_init); + +MODULE_DESCRIPTION("MSM 8x60 Real-Time Audio Calibration driver"); +MODULE_LICENSE("GPL v2"); + +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c b/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c new file mode 100644 index 00000000000..0b38ec2a839 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.c @@ -0,0 +1,382 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "snddev_ecodec.h" + +#define ECODEC_SAMPLE_RATE 8000 + +/* Context for each external codec device */ +struct snddev_ecodec_state { + struct snddev_ecodec_data *data; + u32 sample_rate; +}; + +/* Global state for the driver */ +struct snddev_ecodec_drv_state { + struct mutex dev_lock; + int ref_cnt; /* ensure one rx device at a time */ + struct clk *ecodec_clk; +}; + +static struct snddev_ecodec_drv_state snddev_ecodec_drv; + +struct aux_pcm_state { + unsigned int dout; + unsigned int din; + unsigned int syncout; + unsigned int clkin_a; +}; + +static struct aux_pcm_state the_aux_pcm_state; + +static int aux_pcm_gpios_request(void) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT"); + if (rc < 0) { + pr_err("%s: GPIO request for AUX PCM DOUT failed\n", __func__); + return rc; + } + + rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN"); + if (rc < 0) { + pr_err("%s: GPIO request for AUX PCM DIN failed\n", __func__); + gpio_free(the_aux_pcm_state.dout); + return rc; + } + + rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT"); + if (rc < 0) { + pr_err("%s: GPIO request for AUX PCM SYNC OUT failed\n", + __func__); + gpio_free(the_aux_pcm_state.dout); + gpio_free(the_aux_pcm_state.din); + return rc; + } + + rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A"); + if (rc < 0) { + pr_err("%s: GPIO request for AUX PCM CLKIN A failed\n", + __func__); + gpio_free(the_aux_pcm_state.dout); + gpio_free(the_aux_pcm_state.din); + gpio_free(the_aux_pcm_state.syncout); + return rc; + } + + return rc; +} + +static void aux_pcm_gpios_free(void) +{ + pr_debug("%s\n", __func__); + gpio_free(the_aux_pcm_state.dout); + gpio_free(the_aux_pcm_state.din); + gpio_free(the_aux_pcm_state.syncout); + gpio_free(the_aux_pcm_state.clkin_a); +} + +static int get_aux_pcm_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "aux_pcm_dout"); + if (!res) { + pr_err("%s: failed to get gpio AUX PCM DOUT\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.dout = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "aux_pcm_din"); + if (!res) { + pr_err("%s: failed to get gpio AUX PCM DIN\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.din = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_syncout"); + if (!res) { + pr_err("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.syncout = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "aux_pcm_clkin_a"); + if (!res) { + pr_err("%s: failed to get gpio AUX PCM CLKIN A\n", __func__); + return -ENODEV; + } + + the_aux_pcm_state.clkin_a = res->start; + + return rc; +} + +static int aux_pcm_probe(struct platform_device *pdev) +{ + int rc = 0; + + rc = get_aux_pcm_gpios(pdev); + if (rc < 0) { + pr_err("%s: GPIO configuration failed\n", __func__); + return -ENODEV; + } + return rc; +} + +static struct platform_driver aux_pcm_driver = { + .probe = aux_pcm_probe, + .driver = { .name = "msm_aux_pcm"} +}; + +static int snddev_ecodec_open(struct msm_snddev_info *dev_info) +{ + int rc; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + union afe_port_config afe_config; + + pr_debug("%s\n", __func__); + + mutex_lock(&drv->dev_lock); + + if (dev_info->opened) { + pr_err("%s: ERROR: %s already opened\n", __func__, + dev_info->name); + mutex_unlock(&drv->dev_lock); + return -EBUSY; + } + + if (drv->ref_cnt != 0) { + pr_debug("%s: opened %s\n", __func__, dev_info->name); + drv->ref_cnt++; + mutex_unlock(&drv->dev_lock); + return 0; + } + + pr_info("%s: opening %s\n", __func__, dev_info->name); + + rc = aux_pcm_gpios_request(); + if (rc < 0) { + pr_err("%s: GPIO request failed\n", __func__); + return rc; + } + + clk_reset(drv->ecodec_clk, CLK_RESET_ASSERT); + + afe_config.pcm.mode = AFE_PCM_CFG_MODE_PCM; + afe_config.pcm.sync = AFE_PCM_CFG_SYNC_INT; + afe_config.pcm.frame = AFE_PCM_CFG_FRM_256BPF; + afe_config.pcm.quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD; + afe_config.pcm.slot = 0; + afe_config.pcm.data = AFE_PCM_CFG_CDATAOE_MASTER; + + rc = afe_open(PCM_RX, &afe_config, ECODEC_SAMPLE_RATE); + if (rc < 0) { + pr_err("%s: afe open failed for PCM_RX\n", __func__); + goto err_rx_afe; + } + + rc = afe_open(PCM_TX, &afe_config, ECODEC_SAMPLE_RATE); + if (rc < 0) { + pr_err("%s: afe open failed for PCM_TX\n", __func__); + goto err_tx_afe; + } + + rc = clk_set_rate(drv->ecodec_clk, 2048000); + if (rc < 0) { + pr_err("%s: clk_set_rate failed\n", __func__); + goto err_clk; + } + + clk_prepare_enable(drv->ecodec_clk); + + clk_reset(drv->ecodec_clk, CLK_RESET_DEASSERT); + + drv->ref_cnt++; + mutex_unlock(&drv->dev_lock); + + return 0; + +err_clk: + afe_close(PCM_TX); +err_tx_afe: + afe_close(PCM_RX); +err_rx_afe: + aux_pcm_gpios_free(); + mutex_unlock(&drv->dev_lock); + return -ENODEV; +} + +int snddev_ecodec_close(struct msm_snddev_info *dev_info) +{ + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + pr_debug("%s: closing %s\n", __func__, dev_info->name); + + mutex_lock(&drv->dev_lock); + + if (!dev_info->opened) { + pr_err("%s: ERROR: %s is not opened\n", __func__, + dev_info->name); + mutex_unlock(&drv->dev_lock); + return -EPERM; + } + + drv->ref_cnt--; + + if (drv->ref_cnt == 0) { + + pr_info("%s: closing all devices\n", __func__); + + clk_disable_unprepare(drv->ecodec_clk); + aux_pcm_gpios_free(); + + afe_close(PCM_RX); + afe_close(PCM_TX); + } + + mutex_unlock(&drv->dev_lock); + + return 0; +} + +int snddev_ecodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc = 0; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + return ECODEC_SAMPLE_RATE; + +error: + return rc; +} + +static int snddev_ecodec_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_ecodec_data *pdata; + struct msm_snddev_info *dev_info; + struct snddev_ecodec_state *ecodec; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + rc = -1; + goto error; + } + pdata = pdev->dev.platform_data; + + ecodec = kzalloc(sizeof(struct snddev_ecodec_state), GFP_KERNEL); + if (!ecodec) { + rc = -ENOMEM; + goto error; + } + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + kfree(ecodec); + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->private_data = (void *)ecodec; + dev_info->dev_ops.open = snddev_ecodec_open; + dev_info->dev_ops.close = snddev_ecodec_close; + dev_info->dev_ops.set_freq = snddev_ecodec_set_freq; + dev_info->dev_ops.enable_sidetone = NULL; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + + msm_snddev_register(dev_info); + + ecodec->data = pdata; + ecodec->sample_rate = ECODEC_SAMPLE_RATE; /* Default to 8KHz */ +error: + return rc; +} + +struct platform_driver snddev_ecodec_driver = { + .probe = snddev_ecodec_probe, + .driver = {.name = "msm_snddev_ecodec"} +}; + +int __init snddev_ecodec_init(void) +{ + int rc = 0; + struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv; + + mutex_init(&drv->dev_lock); + drv->ref_cnt = 0; + + drv->ecodec_clk = clk_get_sys(NULL, "pcm_clk"); + if (IS_ERR(drv->ecodec_clk)) { + pr_err("%s: could not get pcm_clk\n", __func__); + return PTR_ERR(drv->ecodec_clk); + } + + rc = platform_driver_register(&aux_pcm_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for aux pcm failed\n", + __func__); + goto error_aux_pcm_platform_driver; + } + + rc = platform_driver_register(&snddev_ecodec_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for ecodec failed\n", + __func__); + goto error_ecodec_platform_driver; + } + + return 0; + +error_ecodec_platform_driver: + platform_driver_unregister(&aux_pcm_driver); +error_aux_pcm_platform_driver: + clk_put(drv->ecodec_clk); + + pr_err("%s: encounter error\n", __func__); + return -ENODEV; +} + +device_initcall(snddev_ecodec_init); + +MODULE_DESCRIPTION("ECodec Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.h b/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.h new file mode 100644 index 00000000000..b102de0ba5f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_ecodec.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP6V2_SNDDEV_ECODEC_H +#define __MACH_QDSP6V2_SNDDEV_ECODEC_H +#include + +struct snddev_ecodec_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u8 channel_mode; + u32 conf_pcm_ctl_val; + u32 conf_aux_codec_intf; + u32 conf_data_format_padding_val; +}; +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c b/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c new file mode 100644 index 00000000000..9b8346d3508 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c @@ -0,0 +1,198 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "snddev_hdmi.h" + +static DEFINE_MUTEX(snddev_hdmi_lock); +static int snddev_hdmi_active; + +static int snddev_hdmi_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + union afe_port_config afe_config; + struct snddev_hdmi_data *snddev_hdmi_data; + + if (!dev_info) { + pr_err("msm_snddev_info is null\n"); + return -EINVAL; + } + + snddev_hdmi_data = dev_info->private_data; + + mutex_lock(&snddev_hdmi_lock); + + if (snddev_hdmi_active) { + pr_err("HDMI snddev already active\n"); + mutex_unlock(&snddev_hdmi_lock); + return -EBUSY; + } + + if (snddev_hdmi_data->on_apps) { + snddev_hdmi_active = 1; + pr_debug("%s open done\n", dev_info->name); + mutex_unlock(&snddev_hdmi_lock); + return 0; + } + + afe_config.hdmi.channel_mode = snddev_hdmi_data->channel_mode; + afe_config.hdmi.bitwidth = 16; + afe_config.hdmi.data_type = 0; + rc = afe_open(snddev_hdmi_data->copp_id, &afe_config, + dev_info->sample_rate); + + if (rc < 0) { + pr_err("afe_open failed\n"); + mutex_unlock(&snddev_hdmi_lock); + return -EINVAL; + } + snddev_hdmi_active = 1; + + pr_debug("%s open done\n", dev_info->name); + + mutex_unlock(&snddev_hdmi_lock); + + return 0; +} + +static int snddev_hdmi_close(struct msm_snddev_info *dev_info) +{ + + struct snddev_hdmi_data *snddev_hdmi_data; + + if (!dev_info) { + pr_err("msm_snddev_info is null\n"); + return -EINVAL; + } + + snddev_hdmi_data = dev_info->private_data; + + if (!dev_info->opened) { + pr_err("calling close device with out opening the" + " device\n"); + return -EPERM; + } + mutex_lock(&snddev_hdmi_lock); + + if (!snddev_hdmi_active) { + pr_err("HDMI snddev not active\n"); + mutex_unlock(&snddev_hdmi_lock); + return -EPERM; + } + snddev_hdmi_active = 0; + + if (snddev_hdmi_data->on_apps) { + pr_debug("%s Closed\n", dev_info->name); + + mutex_unlock(&snddev_hdmi_lock); + return 0; + } + + + afe_close(HDMI_RX); + + pr_debug("%s closed\n", dev_info->name); + mutex_unlock(&snddev_hdmi_lock); + + return 0; +} + +static int snddev_hdmi_set_freq(struct msm_snddev_info *dev_info, u32 req_freq) +{ + if (req_freq != 48000) { + pr_debug("Unsupported Frequency:%d\n", req_freq); + return -EINVAL; + } + return 48000; +} + +static int snddev_hdmi_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_hdmi_data *pdata; + struct msm_snddev_info *dev_info; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if (!(pdata->capability & SNDDEV_CAP_RX)) { + pr_err("invalid device data either RX or TX\n"); + return -ENODEV; + } + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + pr_err("unable to allocate memeory for msm_snddev_info\n"); + return -ENOMEM; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->acdb_id = pdata->acdb_id; + dev_info->private_data = (void *)pdata; + dev_info->dev_ops.open = snddev_hdmi_open; + dev_info->dev_ops.close = snddev_hdmi_close; + dev_info->dev_ops.set_freq = snddev_hdmi_set_freq; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + msm_snddev_register(dev_info); + dev_info->sample_rate = pdata->default_sample_rate; + + pr_debug("probe done for %s\n", pdata->name); + return rc; +} + +static struct platform_driver snddev_hdmi_driver = { + .probe = snddev_hdmi_probe, + .driver = {.name = "snddev_hdmi"} +}; + +static int __init snddev_hdmi_init(void) +{ + s32 rc; + + rc = platform_driver_register(&snddev_hdmi_driver); + if (IS_ERR_VALUE(rc)) { + + pr_err("platform_driver_register failed.\n"); + goto error_platform_driver; + } + + pr_debug("snddev_hdmi_init : done\n"); + + return 0; + +error_platform_driver: + + pr_err("encounterd error\n"); + return -ENODEV; +} + +module_init(snddev_hdmi_init); + +MODULE_DESCRIPTION("HDMI Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.h b/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.h new file mode 100644 index 00000000000..cc69033f08b --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP6_V2_SNDDEV_HDMI_H +#define __MACH_QDSP6_V2_SNDDEV_HDMI_H + +struct snddev_hdmi_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u32 acdb_id; /* Audio Cal purpose */ + u8 channel_mode; + u32 default_sample_rate; + u32 on_apps; +}; +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c b/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c new file mode 100644 index 00000000000..e266d7a193b --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_icodec.c @@ -0,0 +1,1121 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "snddev_icodec.h" + +#define SNDDEV_ICODEC_PCM_SZ 32 /* 16 bit / sample stereo mode */ +#define SNDDEV_ICODEC_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */ +#define SNDDEV_ICODEC_CLK_RATE(freq) \ + (((freq) * (SNDDEV_ICODEC_PCM_SZ)) << (SNDDEV_ICODEC_MUL_FACTOR)) +#define SNDDEV_LOW_POWER_MODE 0 +#define SNDDEV_HIGH_POWER_MODE 1 +/* Voltage required for S4 in microVolts, 2.2V or 2200000microvolts */ +#define SNDDEV_VREG_8058_S4_VOLTAGE (2200000) +/* Load Current required for S4 in microAmps, + 36mA - 56mA */ +#define SNDDEV_VREG_LOW_POWER_LOAD (36000) +#define SNDDEV_VREG_HIGH_POWER_LOAD (56000) + +bool msm_codec_i2s_slave_mode; + +/* Context for each internal codec sound device */ +struct snddev_icodec_state { + struct snddev_icodec_data *data; + struct adie_codec_path *adie_path; + u32 sample_rate; + u32 enabled; +}; + +/* Global state for the driver */ +struct snddev_icodec_drv_state { + struct mutex rx_lock; + struct mutex lb_lock; + struct mutex tx_lock; + u32 rx_active; /* ensure one rx device at a time */ + u32 tx_active; /* ensure one tx device at a time */ + struct clk *rx_osrclk; + struct clk *rx_bitclk; + struct clk *tx_osrclk; + struct clk *tx_bitclk; + + struct pm_qos_request rx_pm_qos_req; + struct pm_qos_request tx_pm_qos_req; + + /* handle to pmic8058 regulator smps4 */ + struct regulator *snddev_vreg; +}; + +static struct snddev_icodec_drv_state snddev_icodec_drv; + +struct regulator *vreg_init(void) +{ + int rc; + struct regulator *vreg_ptr; + + vreg_ptr = regulator_get(NULL, "8058_s4"); + if (IS_ERR(vreg_ptr)) { + pr_err("%s: regulator_get 8058_s4 failed\n", __func__); + return NULL; + } + + rc = regulator_set_voltage(vreg_ptr, SNDDEV_VREG_8058_S4_VOLTAGE, + SNDDEV_VREG_8058_S4_VOLTAGE); + if (rc == 0) + return vreg_ptr; + else + return NULL; +} + +static void vreg_deinit(struct regulator *vreg) +{ + regulator_put(vreg); +} + +static void vreg_mode_vote(struct regulator *vreg, int enable, int mode) +{ + int rc; + if (enable) { + rc = regulator_enable(vreg); + if (rc != 0) + pr_err("%s:Enabling regulator failed\n", __func__); + else { + if (mode) + regulator_set_optimum_mode(vreg, + SNDDEV_VREG_HIGH_POWER_LOAD); + else + regulator_set_optimum_mode(vreg, + SNDDEV_VREG_LOW_POWER_LOAD); + } + } else { + rc = regulator_disable(vreg); + if (rc != 0) + pr_err("%s:Disabling regulator failed\n", __func__); + } +} + +struct msm_cdcclk_ctl_state { + unsigned int rx_mclk; + unsigned int rx_mclk_requested; + unsigned int tx_mclk; + unsigned int tx_mclk_requested; +}; + +static struct msm_cdcclk_ctl_state the_msm_cdcclk_ctl_state; + +static int msm_snddev_rx_mclk_request(void) +{ + int rc = 0; + + rc = gpio_request(the_msm_cdcclk_ctl_state.rx_mclk, + "MSM_SNDDEV_RX_MCLK"); + if (rc < 0) { + pr_err("%s: GPIO request for MSM SNDDEV RX failed\n", __func__); + return rc; + } + the_msm_cdcclk_ctl_state.rx_mclk_requested = 1; + return rc; +} +static int msm_snddev_tx_mclk_request(void) +{ + int rc = 0; + + rc = gpio_request(the_msm_cdcclk_ctl_state.tx_mclk, + "MSM_SNDDEV_TX_MCLK"); + if (rc < 0) { + pr_err("%s: GPIO request for MSM SNDDEV TX failed\n", __func__); + return rc; + } + the_msm_cdcclk_ctl_state.tx_mclk_requested = 1; + return rc; +} +static void msm_snddev_rx_mclk_free(void) +{ + if (the_msm_cdcclk_ctl_state.rx_mclk_requested) { + gpio_free(the_msm_cdcclk_ctl_state.rx_mclk); + the_msm_cdcclk_ctl_state.rx_mclk_requested = 0; + } +} +static void msm_snddev_tx_mclk_free(void) +{ + if (the_msm_cdcclk_ctl_state.tx_mclk_requested) { + gpio_free(the_msm_cdcclk_ctl_state.tx_mclk); + the_msm_cdcclk_ctl_state.tx_mclk_requested = 0; + } +} +static int get_msm_cdcclk_ctl_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "msm_snddev_rx_mclk"); + if (!res) { + pr_err("%s: failed to get gpio MSM SNDDEV RX\n", __func__); + return -ENODEV; + } + the_msm_cdcclk_ctl_state.rx_mclk = res->start; + the_msm_cdcclk_ctl_state.rx_mclk_requested = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "msm_snddev_tx_mclk"); + if (!res) { + pr_err("%s: failed to get gpio MSM SNDDEV TX\n", __func__); + return -ENODEV; + } + the_msm_cdcclk_ctl_state.tx_mclk = res->start; + the_msm_cdcclk_ctl_state.tx_mclk_requested = 0; + + return rc; +} +static int msm_cdcclk_ctl_probe(struct platform_device *pdev) +{ + int rc = 0; + + rc = get_msm_cdcclk_ctl_gpios(pdev); + if (rc < 0) { + pr_err("%s: GPIO configuration failed\n", __func__); + return -ENODEV; + } + return rc; +} +static struct platform_driver msm_cdcclk_ctl_driver = { + .probe = msm_cdcclk_ctl_probe, + .driver = { .name = "msm_cdcclk_ctl"} +}; + +static int snddev_icodec_open_lb(struct snddev_icodec_state *icodec) +{ + int trc; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + /* Voting for low power is ok here as all use cases are + * supported in low power mode. + */ + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 1, + SNDDEV_LOW_POWER_MODE); + + if (icodec->data->voltage_on) + icodec->data->voltage_on(); + + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + pr_err("%s: adie codec open failed\n", __func__); + else + adie_codec_setpath(icodec->adie_path, + icodec->sample_rate, 256); + + if (icodec->adie_path) + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + + if (icodec->data->pamp_on) + icodec->data->pamp_on(); + + icodec->enabled = 1; + + return 0; +} +static int initialize_msm_icodec_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + int i = 0; + int *reg_defaults = pdev->dev.platform_data; + + while ((res = platform_get_resource(pdev, IORESOURCE_IO, i))) { + rc = gpio_request(res->start, res->name); + if (rc) { + pr_err("%s: icodec gpio %d request failed\n", __func__, + res->start); + goto err; + } else { + /* This platform data structure only works if all gpio + * resources are to be used only in output mode. + * If gpio resources are added which are to be used in + * input mode, then the platform data structure will + * have to be changed. + */ + + gpio_direction_output(res->start, reg_defaults[i]); + gpio_free(res->start); + } + i++; + } +err: + return rc; +} +static int msm_icodec_gpio_probe(struct platform_device *pdev) +{ + int rc = 0; + + rc = initialize_msm_icodec_gpios(pdev); + if (rc < 0) { + pr_err("%s: GPIO configuration failed\n", __func__); + return -ENODEV; + } + return rc; +} +static struct platform_driver msm_icodec_gpio_driver = { + .probe = msm_icodec_gpio_probe, + .driver = { .name = "msm_icodec_gpio"} +}; + +static int snddev_icodec_open_rx(struct snddev_icodec_state *icodec) +{ + int trc; + int afe_channel_mode; + union afe_port_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + pm_qos_update_request(&drv->rx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + if (drv->snddev_vreg) { + if (!strcmp(icodec->data->name, "headset_stereo_rx")) + vreg_mode_vote(drv->snddev_vreg, 1, + SNDDEV_LOW_POWER_MODE); + else + vreg_mode_vote(drv->snddev_vreg, 1, + SNDDEV_HIGH_POWER_MODE); + } + msm_snddev_rx_mclk_request(); + + drv->rx_osrclk = clk_get_sys(NULL, "i2s_spkr_osr_clk"); + if (IS_ERR(drv->rx_osrclk)) + pr_err("%s master clock Error\n", __func__); + + trc = clk_set_rate(drv->rx_osrclk, + SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) { + pr_err("ERROR setting m clock1\n"); + goto error_invalid_freq; + } + + clk_prepare_enable(drv->rx_osrclk); + drv->rx_bitclk = clk_get_sys(NULL, "i2s_spkr_bit_clk"); + if (IS_ERR(drv->rx_bitclk)) + pr_err("%s clock Error\n", __func__); + + /* Master clock = Sample Rate * OSR rate bit clock + * OSR Rate bit clock = bit/sample * channel master + * clock / bit clock = divider value = 8 + */ + if (msm_codec_i2s_slave_mode) { + pr_info("%s: configuring bit clock for slave mode\n", + __func__); + trc = clk_set_rate(drv->rx_bitclk, 0); + } else + trc = clk_set_rate(drv->rx_bitclk, 8); + + if (IS_ERR_VALUE(trc)) { + pr_err("ERROR setting m clock1\n"); + goto error_adie; + } + clk_prepare_enable(drv->rx_bitclk); + + if (icodec->data->voltage_on) + icodec->data->voltage_on(); + + /* Configure ADIE */ + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + pr_err("%s: adie codec open failed\n", __func__); + else + adie_codec_setpath(icodec->adie_path, + icodec->sample_rate, 256); + /* OSR default to 256, can be changed for power optimization + * If OSR is to be changed, need clock API for setting the divider + */ + + switch (icodec->data->channel_mode) { + case 2: + afe_channel_mode = MSM_AFE_STEREO; + break; + case 1: + default: + afe_channel_mode = MSM_AFE_MONO; + break; + } + afe_config.mi2s.channel = afe_channel_mode; + afe_config.mi2s.bitwidth = 16; + afe_config.mi2s.line = 1; + afe_config.mi2s.format = MSM_AFE_I2S_FORMAT_LPCM; + if (msm_codec_i2s_slave_mode) + afe_config.mi2s.ws = 0; + else + afe_config.mi2s.ws = 1; + + trc = afe_open(icodec->data->copp_id, &afe_config, icodec->sample_rate); + + if (trc < 0) + pr_err("%s: afe open failed, trc = %d\n", __func__, trc); + + /* Enable ADIE */ + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + } + + if (msm_codec_i2s_slave_mode) + adie_codec_set_master_mode(icodec->adie_path, 1); + else + adie_codec_set_master_mode(icodec->adie_path, 0); + + /* Enable power amplifier */ + if (icodec->data->pamp_on) { + if (icodec->data->pamp_on()) { + pr_err("%s: Error turning on rx power\n", __func__); + goto error_pamp; + } + } + + icodec->enabled = 1; + + pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; + +error_pamp: +error_adie: + clk_disable_unprepare(drv->rx_osrclk); +error_invalid_freq: + + pr_err("%s: encounter error\n", __func__); + + pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return -ENODEV; +} + +static int snddev_icodec_open_tx(struct snddev_icodec_state *icodec) +{ + int trc; + int afe_channel_mode; + union afe_port_config afe_config; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;; + + pm_qos_update_request(&drv->tx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 1, SNDDEV_HIGH_POWER_MODE); + + /* Reuse pamp_on for TX platform-specific setup */ + if (icodec->data->pamp_on) { + if (icodec->data->pamp_on()) { + pr_err("%s: Error turning on tx power\n", __func__); + goto error_pamp; + } + } + + msm_snddev_tx_mclk_request(); + + drv->tx_osrclk = clk_get_sys(NULL, "i2s_mic_osr_clk"); + if (IS_ERR(drv->tx_osrclk)) + pr_err("%s master clock Error\n", __func__); + + trc = clk_set_rate(drv->tx_osrclk, + SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate)); + if (IS_ERR_VALUE(trc)) { + pr_err("ERROR setting m clock1\n"); + goto error_invalid_freq; + } + + clk_prepare_enable(drv->tx_osrclk); + drv->tx_bitclk = clk_get_sys(NULL, "i2s_mic_bit_clk"); + if (IS_ERR(drv->tx_bitclk)) + pr_err("%s clock Error\n", __func__); + + /* Master clock = Sample Rate * OSR rate bit clock + * OSR Rate bit clock = bit/sample * channel master + * clock / bit clock = divider value = 8 + */ + if (msm_codec_i2s_slave_mode) { + pr_info("%s: configuring bit clock for slave mode\n", + __func__); + trc = clk_set_rate(drv->tx_bitclk, 0); + } else + trc = clk_set_rate(drv->tx_bitclk, 8); + + clk_prepare_enable(drv->tx_bitclk); + + /* Enable ADIE */ + trc = adie_codec_open(icodec->data->profile, &icodec->adie_path); + if (IS_ERR_VALUE(trc)) + pr_err("%s: adie codec open failed\n", __func__); + else + adie_codec_setpath(icodec->adie_path, + icodec->sample_rate, 256); + + switch (icodec->data->channel_mode) { + case 2: + afe_channel_mode = MSM_AFE_STEREO; + break; + case 1: + default: + afe_channel_mode = MSM_AFE_MONO; + break; + } + afe_config.mi2s.channel = afe_channel_mode; + afe_config.mi2s.bitwidth = 16; + afe_config.mi2s.line = 1; + afe_config.mi2s.format = MSM_AFE_I2S_FORMAT_LPCM; + if (msm_codec_i2s_slave_mode) + afe_config.mi2s.ws = 0; + else + afe_config.mi2s.ws = 1; + + trc = afe_open(icodec->data->copp_id, &afe_config, icodec->sample_rate); + + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_READY); + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_ANALOG_READY); + } + + if (msm_codec_i2s_slave_mode) + adie_codec_set_master_mode(icodec->adie_path, 1); + else + adie_codec_set_master_mode(icodec->adie_path, 0); + + icodec->enabled = 1; + + pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; + +error_invalid_freq: + + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + pr_err("%s: encounter error\n", __func__); +error_pamp: + pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return -ENODEV; +} + +static int snddev_icodec_close_lb(struct snddev_icodec_state *icodec) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + /* Disable power amplifier */ + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_LOW_POWER_MODE); + + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + } + + if (icodec->data->voltage_off) + icodec->data->voltage_off(); + + return 0; +} + +static int snddev_icodec_close_rx(struct snddev_icodec_state *icodec) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + pm_qos_update_request(&drv->rx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_HIGH_POWER_MODE); + + /* Disable power amplifier */ + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + /* Disable ADIE */ + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + } + + afe_close(icodec->data->copp_id); + + if (icodec->data->voltage_off) + icodec->data->voltage_off(); + + clk_disable_unprepare(drv->rx_bitclk); + clk_disable_unprepare(drv->rx_osrclk); + + msm_snddev_rx_mclk_free(); + + icodec->enabled = 0; + + pm_qos_update_request(&drv->rx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; +} + +static int snddev_icodec_close_tx(struct snddev_icodec_state *icodec) +{ + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + pm_qos_update_request(&drv->tx_pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + if (drv->snddev_vreg) + vreg_mode_vote(drv->snddev_vreg, 0, SNDDEV_HIGH_POWER_MODE); + + /* Disable ADIE */ + if (icodec->adie_path) { + adie_codec_proceed_stage(icodec->adie_path, + ADIE_CODEC_DIGITAL_OFF); + adie_codec_close(icodec->adie_path); + icodec->adie_path = NULL; + } + + afe_close(icodec->data->copp_id); + + clk_disable_unprepare(drv->tx_bitclk); + clk_disable_unprepare(drv->tx_osrclk); + + msm_snddev_tx_mclk_free(); + + /* Reuse pamp_off for TX platform-specific setup */ + if (icodec->data->pamp_off) + icodec->data->pamp_off(); + + icodec->enabled = 0; + + pm_qos_update_request(&drv->tx_pm_qos_req, PM_QOS_DEFAULT_VALUE); + return 0; +} + +static int snddev_icodec_set_device_volume_impl( + struct msm_snddev_info *dev_info, u32 volume) +{ + struct snddev_icodec_state *icodec; + + int rc = 0; + + icodec = dev_info->private_data; + + if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_DIGITAL) { + + rc = adie_codec_set_device_digital_volume(icodec->adie_path, + icodec->data->channel_mode, volume); + if (rc < 0) { + pr_err("%s: unable to set_device_digital_volume for" + "%s volume in percentage = %u\n", + __func__, dev_info->name, volume); + return rc; + } + + } else if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_ANALOG) { + rc = adie_codec_set_device_analog_volume(icodec->adie_path, + icodec->data->channel_mode, volume); + if (rc < 0) { + pr_err("%s: unable to set_device_analog_volume for" + "%s volume in percentage = %u\n", + __func__, dev_info->name, volume); + return rc; + } + } else { + pr_err("%s: Invalid device volume control\n", __func__); + return -EPERM; + } + return rc; +} + +static int snddev_icodec_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (drv->rx_active) { + mutex_unlock(&drv->rx_lock); + pr_err("%s: rx_active is set, return EBUSY\n", + __func__); + rc = -EBUSY; + goto error; + } + rc = snddev_icodec_open_rx(icodec); + + if (!IS_ERR_VALUE(rc)) { + if ((icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + if (!IS_ERR_VALUE(rc)) + drv->rx_active = 1; + else + pr_err("%s: set_device_volume_impl" + " error(rx) = %d\n", __func__, rc); + } + mutex_unlock(&drv->rx_lock); + } else if (icodec->data->capability & SNDDEV_CAP_LB) { + mutex_lock(&drv->lb_lock); + rc = snddev_icodec_open_lb(icodec); + + if (!IS_ERR_VALUE(rc)) { + if ((icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + } + + mutex_unlock(&drv->lb_lock); + } else { + mutex_lock(&drv->tx_lock); + if (drv->tx_active) { + mutex_unlock(&drv->tx_lock); + pr_err("%s: tx_active is set, return EBUSY\n", + __func__); + rc = -EBUSY; + goto error; + } + rc = snddev_icodec_open_tx(icodec); + + if (!IS_ERR_VALUE(rc)) { + if ((icodec->data->dev_vol_type & ( + SNDDEV_DEV_VOL_DIGITAL | + SNDDEV_DEV_VOL_ANALOG))) + rc = snddev_icodec_set_device_volume_impl( + dev_info, dev_info->dev_volume); + if (!IS_ERR_VALUE(rc)) + drv->tx_active = 1; + else + pr_err("%s: set_device_volume_impl" + " error(tx) = %d\n", __func__, rc); + } + mutex_unlock(&drv->tx_lock); + } +error: + return rc; +} + +static int snddev_icodec_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (!drv->rx_active) { + mutex_unlock(&drv->rx_lock); + pr_err("%s: rx_active not set, return\n", __func__); + rc = -EPERM; + goto error; + } + rc = snddev_icodec_close_rx(icodec); + if (!IS_ERR_VALUE(rc)) + drv->rx_active = 0; + else + pr_err("%s: close rx failed, rc = %d\n", __func__, rc); + mutex_unlock(&drv->rx_lock); + } else if (icodec->data->capability & SNDDEV_CAP_LB) { + mutex_lock(&drv->lb_lock); + rc = snddev_icodec_close_lb(icodec); + mutex_unlock(&drv->lb_lock); + } else { + mutex_lock(&drv->tx_lock); + if (!drv->tx_active) { + mutex_unlock(&drv->tx_lock); + pr_err("%s: tx_active not set, return\n", __func__); + rc = -EPERM; + goto error; + } + rc = snddev_icodec_close_tx(icodec); + if (!IS_ERR_VALUE(rc)) + drv->tx_active = 0; + else + pr_err("%s: close tx failed, rc = %d\n", __func__, rc); + mutex_unlock(&drv->tx_lock); + } + +error: + return rc; +} + +static int snddev_icodec_check_freq(u32 req_freq) +{ + int rc = -EINVAL; + + if ((req_freq != 0) && (req_freq >= 8000) && (req_freq <= 48000)) { + if ((req_freq == 8000) || (req_freq == 11025) || + (req_freq == 12000) || (req_freq == 16000) || + (req_freq == 22050) || (req_freq == 24000) || + (req_freq == 32000) || (req_freq == 44100) || + (req_freq == 48000)) { + rc = 0; + } else + pr_info("%s: Unsupported Frequency:%d\n", __func__, + req_freq); + } + return rc; +} + +static int snddev_icodec_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc; + struct snddev_icodec_state *icodec; + + if (!dev_info) { + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + if (adie_codec_freq_supported(icodec->data->profile, rate) != 0) { + pr_err("%s: adie_codec_freq_supported() failed\n", __func__); + rc = -EINVAL; + goto error; + } else { + if (snddev_icodec_check_freq(rate) != 0) { + pr_err("%s: check_freq failed\n", __func__); + rc = -EINVAL; + goto error; + } else + icodec->sample_rate = rate; + } + + if (icodec->enabled) { + snddev_icodec_close(dev_info); + snddev_icodec_open(dev_info); + } + + return icodec->sample_rate; + +error: + return rc; +} + +static int snddev_icodec_enable_sidetone(struct msm_snddev_info *dev_info, + u32 enable, uint16_t gain) +{ + int rc = 0; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + if (!dev_info) { + pr_err("invalid dev_info\n"); + rc = -EINVAL; + goto error; + } + + icodec = dev_info->private_data; + + if (icodec->data->capability & SNDDEV_CAP_RX) { + mutex_lock(&drv->rx_lock); + if (!drv->rx_active || !dev_info->opened) { + pr_err("dev not active\n"); + rc = -EPERM; + mutex_unlock(&drv->rx_lock); + goto error; + } + rc = afe_sidetone(PRIMARY_I2S_TX, PRIMARY_I2S_RX, enable, gain); + if (rc < 0) + pr_err("%s: AFE command sidetone failed\n", __func__); + mutex_unlock(&drv->rx_lock); + } else { + rc = -EINVAL; + pr_err("rx device only\n"); + } + +error: + return rc; + +} +static int snddev_icodec_enable_anc(struct msm_snddev_info *dev_info, + u32 enable) +{ + int rc = 0; + struct adie_codec_anc_data *reg_writes; + struct acdb_cal_block cal_block; + struct snddev_icodec_state *icodec; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + + pr_info("%s: enable=%d\n", __func__, enable); + + if (!dev_info) { + pr_err("invalid dev_info\n"); + rc = -EINVAL; + goto error; + } + icodec = dev_info->private_data; + + if ((icodec->data->capability & SNDDEV_CAP_RX) && + (icodec->data->capability & SNDDEV_CAP_ANC)) { + mutex_lock(&drv->rx_lock); + + if (!drv->rx_active || !dev_info->opened) { + pr_err("dev not active\n"); + rc = -EPERM; + mutex_unlock(&drv->rx_lock); + goto error; + } + if (enable) { + get_anc_cal(&cal_block); + reg_writes = (struct adie_codec_anc_data *) + cal_block.cal_kvaddr; + + if (reg_writes == NULL) { + pr_err("error, no calibration data\n"); + rc = -1; + mutex_unlock(&drv->rx_lock); + goto error; + } + + rc = adie_codec_enable_anc(icodec->adie_path, + 1, reg_writes); + } else { + rc = adie_codec_enable_anc(icodec->adie_path, + 0, NULL); + } + mutex_unlock(&drv->rx_lock); + } else { + rc = -EINVAL; + pr_err("rx and ANC device only\n"); + } + +error: + return rc; + +} + +int snddev_icodec_set_device_volume(struct msm_snddev_info *dev_info, + u32 volume) +{ + struct snddev_icodec_state *icodec; + struct mutex *lock; + struct snddev_icodec_drv_state *drv = &snddev_icodec_drv; + int rc = -EPERM; + + if (!dev_info) { + pr_info("%s : device not intilized.\n", __func__); + return -EINVAL; + } + + icodec = dev_info->private_data; + + if (!(icodec->data->dev_vol_type & (SNDDEV_DEV_VOL_DIGITAL + | SNDDEV_DEV_VOL_ANALOG))) { + + pr_info("%s : device %s does not support device volume " + "control.", __func__, dev_info->name); + return -EPERM; + } + dev_info->dev_volume = volume; + + if (icodec->data->capability & SNDDEV_CAP_RX) + lock = &drv->rx_lock; + else if (icodec->data->capability & SNDDEV_CAP_LB) + lock = &drv->lb_lock; + else + lock = &drv->tx_lock; + + mutex_lock(lock); + + rc = snddev_icodec_set_device_volume_impl(dev_info, + dev_info->dev_volume); + mutex_unlock(lock); + return rc; +} + +static int snddev_icodec_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_icodec_data *pdata; + struct msm_snddev_info *dev_info; + struct snddev_icodec_state *icodec; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + rc = -1; + goto error; + } + pdata = pdev->dev.platform_data; + if ((pdata->capability & SNDDEV_CAP_RX) && + (pdata->capability & SNDDEV_CAP_TX)) { + pr_err("%s: invalid device data either RX or TX\n", __func__); + goto error; + } + icodec = kzalloc(sizeof(struct snddev_icodec_state), GFP_KERNEL); + if (!icodec) { + rc = -ENOMEM; + goto error; + } + dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + kfree(icodec); + rc = -ENOMEM; + goto error; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->private_data = (void *) icodec; + dev_info->dev_ops.open = snddev_icodec_open; + dev_info->dev_ops.close = snddev_icodec_close; + dev_info->dev_ops.set_freq = snddev_icodec_set_freq; + dev_info->dev_ops.set_device_volume = snddev_icodec_set_device_volume; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + msm_snddev_register(dev_info); + icodec->data = pdata; + icodec->sample_rate = pdata->default_sample_rate; + dev_info->sample_rate = pdata->default_sample_rate; + dev_info->channel_mode = pdata->channel_mode; + if (pdata->capability & SNDDEV_CAP_RX) + dev_info->dev_ops.enable_sidetone = + snddev_icodec_enable_sidetone; + else + dev_info->dev_ops.enable_sidetone = NULL; + + if (pdata->capability & SNDDEV_CAP_ANC) { + dev_info->dev_ops.enable_anc = + snddev_icodec_enable_anc; + } else { + dev_info->dev_ops.enable_anc = NULL; + } +error: + return rc; +} + +static int snddev_icodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_icodec_driver = { + .probe = snddev_icodec_probe, + .remove = snddev_icodec_remove, + .driver = { .name = "snddev_icodec" } +}; + +module_param(msm_codec_i2s_slave_mode, bool, 0); +MODULE_PARM_DESC(msm_codec_i2s_slave_mode, "Set MSM to I2S slave clock mode"); + +static int __init snddev_icodec_init(void) +{ + s32 rc; + struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; + + rc = platform_driver_register(&snddev_icodec_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for snddev icodec failed\n", + __func__); + goto error_snddev_icodec_driver; + } + + rc = platform_driver_register(&msm_cdcclk_ctl_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for msm snddev failed\n", + __func__); + goto error_msm_cdcclk_ctl_driver; + } + + rc = platform_driver_register(&msm_icodec_gpio_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for msm snddev gpio failed\n", + __func__); + goto error_msm_icodec_gpio_driver; + } + + mutex_init(&icodec_drv->rx_lock); + mutex_init(&icodec_drv->lb_lock); + mutex_init(&icodec_drv->tx_lock); + icodec_drv->rx_active = 0; + icodec_drv->tx_active = 0; + icodec_drv->snddev_vreg = vreg_init(); + + pm_qos_add_request(&icodec_drv->tx_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + pm_qos_add_request(&icodec_drv->rx_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + return 0; +error_msm_icodec_gpio_driver: + platform_driver_unregister(&msm_cdcclk_ctl_driver); +error_msm_cdcclk_ctl_driver: + platform_driver_unregister(&snddev_icodec_driver); +error_snddev_icodec_driver: + return -ENODEV; +} + +static void __exit snddev_icodec_exit(void) +{ + struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv; + + platform_driver_unregister(&snddev_icodec_driver); + platform_driver_unregister(&msm_cdcclk_ctl_driver); + platform_driver_unregister(&msm_icodec_gpio_driver); + + clk_put(icodec_drv->rx_osrclk); + clk_put(icodec_drv->tx_osrclk); + if (icodec_drv->snddev_vreg) { + vreg_deinit(icodec_drv->snddev_vreg); + icodec_drv->snddev_vreg = NULL; + } + return; +} + +module_init(snddev_icodec_init); +module_exit(snddev_icodec_exit); + +MODULE_DESCRIPTION("ICodec Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_icodec.h b/arch/arm/mach-msm/qdsp6v2/snddev_icodec.h new file mode 100644 index 00000000000..8d5613f1c16 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_icodec.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP6V2_SNDDEV_ICODEC_H +#define __MACH_QDSP6V2_SNDDEV_ICODEC_H +#include +#include +#include + +struct snddev_icodec_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + /* Adie profile */ + struct adie_codec_dev_profile *profile; + /* Afe setting */ + u8 channel_mode; + u32 default_sample_rate; + int (*pamp_on) (void); + void (*pamp_off) (void); + int (*voltage_on) (void); + void (*voltage_off) (void); + u32 dev_vol_type; +}; + +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c b/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c new file mode 100644 index 00000000000..4cf18b3e27f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.c @@ -0,0 +1,464 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "snddev_mi2s.h" + +#define SNDDEV_MI2S_PCM_SZ 32 /* 16 bit / sample stereo mode */ +#define SNDDEV_MI2S_MUL_FACTOR 3 /* Multi by 8 Shift by 3 */ +#define SNDDEV_MI2S_CLK_RATE(freq) \ + (((freq) * (SNDDEV_MI2S_PCM_SZ)) << (SNDDEV_MI2S_MUL_FACTOR)) + + +/* Global state for the driver */ +struct snddev_mi2s_drv_state { + + struct clk *tx_osrclk; + struct clk *tx_bitclk; + int mi2s_ws; + int mi2s_mclk; + int mi2s_sclk; + int fm_mi2s_sd; +}; + +static struct snddev_mi2s_drv_state snddev_mi2s_drv; + +static struct msm_mi2s_gpio_data *mi2s_gpio; + +static int mi2s_gpios_request(void) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + rc = gpio_request(snddev_mi2s_drv.mi2s_ws, "MI2S_WS"); + if (rc < 0) { + pr_err("%s: GPIO request for MI2S_WS failed\n", __func__); + return rc; + } + + rc = gpio_request(snddev_mi2s_drv.mi2s_sclk, "MI2S_SCLK"); + if (rc < 0) { + pr_err("%s: GPIO request for MI2S_SCLK failed\n", __func__); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + return rc; + } + + rc = gpio_request(snddev_mi2s_drv.mi2s_mclk, "MI2S_MCLK"); + if (rc < 0) { + pr_err("%s: GPIO request for MI2S_MCLK failed\n", + __func__); + gpio_free(snddev_mi2s_drv.mi2s_ws); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + return rc; + } + + rc = gpio_request(snddev_mi2s_drv.fm_mi2s_sd, "FM_MI2S_SD"); + if (rc < 0) { + pr_err("%s: GPIO request for FM_MI2S_SD failed\n", + __func__); + gpio_free(snddev_mi2s_drv.mi2s_ws); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + gpio_free(snddev_mi2s_drv.mi2s_mclk); + return rc; + } + + return rc; +} + +static void mi2s_gpios_free(void) +{ + pr_debug("%s\n", __func__); + gpio_free(snddev_mi2s_drv.mi2s_ws); + gpio_free(snddev_mi2s_drv.mi2s_sclk); + gpio_free(snddev_mi2s_drv.mi2s_mclk); + gpio_free(snddev_mi2s_drv.fm_mi2s_sd); +} + +static int mi2s_get_gpios(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + + /* Claim all of the GPIOs. */ + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "mi2s_ws"); + if (!res) { + pr_err("%s: failed to get gpio MI2S_WS\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.mi2s_ws = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "mi2s_sclk"); + if (!res) { + pr_err("%s: failed to get gpio MI2S_SCLK\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.mi2s_sclk = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "mi2s_mclk"); + if (!res) { + pr_err("%s: failed to get gpio MI2S_MCLK\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.mi2s_mclk = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "fm_mi2s_sd"); + if (!res) { + pr_err("%s: failed to get gpio FM_MI2S_SD\n", __func__); + return -ENODEV; + } + + snddev_mi2s_drv.fm_mi2s_sd = res->start; + + return rc; +} + +static int mi2s_fm_probe(struct platform_device *pdev) +{ + int rc = 0; + + rc = mi2s_get_gpios(pdev); + if (rc < 0) { + pr_err("%s: GPIO configuration failed\n", __func__); + return rc; + } + + mi2s_gpio = (struct msm_mi2s_gpio_data *)(pdev->dev.platform_data); + return rc; +} + +static struct platform_driver mi2s_fm_driver = { + .probe = mi2s_fm_probe, + .driver = { .name = "msm_mi2s"} +}; + +static u8 num_of_bits_set(u8 sd_line_mask) +{ + u8 num_bits_set = 0; + + while (sd_line_mask) { + + if (sd_line_mask & 1) + num_bits_set++; + sd_line_mask = sd_line_mask >> 1; + } + return num_bits_set; +} + +static int snddev_mi2s_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + union afe_port_config afe_config; + u8 channels; + u8 num_of_sd_lines = 0; + struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + if (!dev_info) { + pr_err("%s: msm_snddev_info is null\n", __func__); + return -EINVAL; + } + + /* set up osr clk */ + drv->tx_osrclk = clk_get_sys(NULL, "mi2s_osr_clk"); + if (IS_ERR(drv->tx_osrclk)) + pr_err("%s master clock Error\n", __func__); + + rc = clk_set_rate(drv->tx_osrclk, + SNDDEV_MI2S_CLK_RATE(dev_info->sample_rate)); + if (IS_ERR_VALUE(rc)) { + pr_err("ERROR setting osr clock\n"); + return -ENODEV; + } + clk_prepare_enable(drv->tx_osrclk); + + /* set up bit clk */ + drv->tx_bitclk = clk_get_sys(NULL, "mi2s_bit_clk"); + if (IS_ERR(drv->tx_bitclk)) + pr_err("%s clock Error\n", __func__); + + rc = clk_set_rate(drv->tx_bitclk, 8); + if (IS_ERR_VALUE(rc)) { + pr_err("ERROR setting bit clock\n"); + clk_disable_unprepare(drv->tx_osrclk); + return -ENODEV; + } + clk_prepare_enable(drv->tx_bitclk); + + afe_config.mi2s.bitwidth = 16; + + if (snddev_mi2s_data->channel_mode == 1) + channels = AFE_MI2S_MONO; + else if (snddev_mi2s_data->channel_mode == 2) + channels = AFE_MI2S_STEREO; + else if (snddev_mi2s_data->channel_mode == 4) + channels = AFE_MI2S_4CHANNELS; + else if (snddev_mi2s_data->channel_mode == 6) + channels = AFE_MI2S_6CHANNELS; + else if (snddev_mi2s_data->channel_mode == 8) + channels = AFE_MI2S_8CHANNELS; + else { + pr_err("ERROR: Invalid MI2S channel mode\n"); + goto error_invalid_data; + } + + num_of_sd_lines = num_of_bits_set(snddev_mi2s_data->sd_lines); + + switch (num_of_sd_lines) { + case 1: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0: + afe_config.mi2s.line = AFE_I2S_SD0; + break; + case MI2S_SD1: + afe_config.mi2s.line = AFE_I2S_SD1; + break; + case MI2S_SD2: + afe_config.mi2s.line = AFE_I2S_SD2; + break; + case MI2S_SD3: + afe_config.mi2s.line = AFE_I2S_SD3; + break; + default: + pr_err("%s: invalid SD line\n", + __func__); + goto error_invalid_data; + } + if (channels != AFE_MI2S_STEREO && + channels != AFE_MI2S_MONO) { + pr_err("%s: for one SD line, channel " + "must be 1 or 2\n", __func__); + goto error_invalid_data; + } + afe_config.mi2s.channel = channels; + break; + case 2: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0 | MI2S_SD1: + afe_config.mi2s.line = AFE_I2S_QUAD01; + break; + case MI2S_SD2 | MI2S_SD3: + afe_config.mi2s.line = AFE_I2S_QUAD23; + break; + default: + pr_err("%s: invalid SD line\n", + __func__); + goto error_invalid_data; + } + if (channels != AFE_MI2S_4CHANNELS) { + pr_err("%s: for two SD lines, channel " + "must be 1 and 2 or 3 and 4\n", __func__); + goto error_invalid_data; + } + break; + case 3: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0 | MI2S_SD1 | MI2S_SD2: + afe_config.mi2s.line = AFE_I2S_6CHS; + break; + default: + pr_err("%s: invalid SD lines\n", + __func__); + goto error_invalid_data; + } + if (channels != AFE_MI2S_6CHANNELS) { + pr_err("%s: for three SD lines, lines " + "must be 1, 2, and 3\n", __func__); + goto error_invalid_data; + } + break; + case 4: + switch (snddev_mi2s_data->sd_lines) { + case MI2S_SD0 | MI2S_SD1 | MI2S_SD2 | MI2S_SD3: + afe_config.mi2s.line = AFE_I2S_8CHS; + break; + default: + pr_err("%s: invalid SD lines\n", + __func__); + goto error_invalid_data; + } + + if (channels != AFE_MI2S_8CHANNELS) { + pr_err("%s: for four SD lines, lines " + "must be 1, 2, 3, and 4\n", __func__); + goto error_invalid_data; + } + break; + default: + pr_err("%s: invalid SD lines\n", __func__); + goto error_invalid_data; + } + afe_config.mi2s.ws = 1; + afe_config.mi2s.format = MSM_AFE_I2S_FORMAT_LPCM; + + rc = afe_open(snddev_mi2s_data->copp_id, &afe_config, + dev_info->sample_rate); + + if (rc < 0) { + pr_err("%s: afe_open failed\n", __func__); + goto error_invalid_data; + } + + /*enable fm gpio here*/ + rc = mi2s_gpios_request(); + if (rc < 0) { + pr_err("%s: GPIO request failed\n", __func__); + return rc; + } + + pr_info("%s: afe_open done\n", __func__); + + return rc; + +error_invalid_data: + + clk_disable_unprepare(drv->tx_bitclk); + clk_disable_unprepare(drv->tx_osrclk); + return -EINVAL; +} + +static int snddev_mi2s_close(struct msm_snddev_info *dev_info) +{ + + struct snddev_mi2s_drv_state *mi2s_drv = &snddev_mi2s_drv; + struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data; + + if (!dev_info) { + pr_err("%s: msm_snddev_info is null\n", __func__); + return -EINVAL; + } + + if (!dev_info->opened) { + pr_err(" %s: calling close device with out opening the" + " device\n", __func__); + return -EIO; + } + afe_close(snddev_mi2s_data->copp_id); + clk_disable_unprepare(mi2s_drv->tx_bitclk); + clk_disable_unprepare(mi2s_drv->tx_osrclk); + + mi2s_gpios_free(); + + pr_info("%s:\n", __func__); + + return 0; +} + +static int snddev_mi2s_set_freq(struct msm_snddev_info *dev_info, u32 req_freq) +{ + if (req_freq != 48000) { + pr_info("%s: Unsupported Frequency:%d\n", __func__, req_freq); + return -EINVAL; + } + return 48000; +} + + +static int snddev_mi2s_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_mi2s_data *pdata; + struct msm_snddev_info *dev_info; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Invalid caller\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + + dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + pr_err("%s: uneable to allocate memeory for msm_snddev_info\n", + __func__); + + return -ENOMEM; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->dev_ops.open = snddev_mi2s_open; + dev_info->dev_ops.close = snddev_mi2s_close; + dev_info->private_data = (void *)pdata; + dev_info->dev_ops.set_freq = snddev_mi2s_set_freq; + dev_info->capability = pdata->capability; + dev_info->opened = 0; + dev_info->sample_rate = pdata->sample_rate; + msm_snddev_register(dev_info); + + return rc; +} + +static struct platform_driver snddev_mi2s_driver = { + .probe = snddev_mi2s_probe, + .driver = {.name = "snddev_mi2s"} +}; + +static int __init snddev_mi2s_init(void) +{ + s32 rc = 0; + + rc = platform_driver_register(&mi2s_fm_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for mi2s_fm_driver failed\n", + __func__); + goto error_mi2s_fm_platform_driver; + } + + rc = platform_driver_register(&snddev_mi2s_driver); + if (IS_ERR_VALUE(rc)) { + + pr_err("%s: platform_driver_register failed\n", __func__); + goto error_platform_driver; + } + + return rc; + +error_platform_driver: + platform_driver_unregister(&mi2s_fm_driver); +error_mi2s_fm_platform_driver: + pr_err("%s: encounter error\n", __func__); + return -ENODEV; +} + +static void __exit snddev_mi2s_exit(void) +{ + struct snddev_mi2s_drv_state *mi2s_drv = &snddev_mi2s_drv; + + platform_driver_unregister(&snddev_mi2s_driver); + clk_put(mi2s_drv->tx_osrclk); + clk_put(mi2s_drv->tx_bitclk); + return; +} + + +module_init(snddev_mi2s_init); +module_exit(snddev_mi2s_exit); + +MODULE_DESCRIPTION("MI2S Sound Device driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.h b/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.h new file mode 100644 index 00000000000..d369c969ac1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_mi2s.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP6_V2_SNDDEV_MI2S_H +#define __MACH_QDSP6_V2_SNDDEV_MI2S_H + +struct snddev_mi2s_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* audpp routing */ + u16 channel_mode; + u16 sd_lines; + u32 sample_rate; +}; + +#define MI2S_SD0 (1 << 0) +#define MI2S_SD1 (1 << 1) +#define MI2S_SD2 (1 << 2) +#define MI2S_SD3 (1 << 3) + +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_virtual.c b/arch/arm/mach-msm/qdsp6v2/snddev_virtual.c new file mode 100644 index 00000000000..f48aa0ec713 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_virtual.c @@ -0,0 +1,172 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include "snddev_virtual.h" + +static DEFINE_MUTEX(snddev_virtual_lock); + +static int snddev_virtual_open(struct msm_snddev_info *dev_info) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&snddev_virtual_lock); + + if (!dev_info) { + pr_err("%s: NULL dev_info\n", __func__); + + rc = -EINVAL; + goto done; + } + + if (!dev_info->opened) { + rc = afe_start_pseudo_port(dev_info->copp_id); + } else { + pr_err("%s: Pseudo port 0x%x is already open\n", + __func__, dev_info->copp_id); + + rc = -EBUSY; + } + +done: + mutex_unlock(&snddev_virtual_lock); + + return rc; +} + +static int snddev_virtual_close(struct msm_snddev_info *dev_info) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&snddev_virtual_lock); + + if (!dev_info) { + pr_err("%s: NULL dev_info\n", __func__); + + rc = -EINVAL; + goto done; + } + + if (dev_info->opened) { + rc = afe_stop_pseudo_port(dev_info->copp_id); + } else { + pr_err("%s: Pseudo port 0x%x is not open\n", + __func__, dev_info->copp_id); + + rc = -EPERM; + } + +done: + mutex_unlock(&snddev_virtual_lock); + + return rc; +} + +static int snddev_virtual_set_freq(struct msm_snddev_info *dev_info, u32 rate) +{ + int rc = 0; + + if (!dev_info) + rc = -EINVAL; + + return rate; +} + +static int snddev_virtual_probe(struct platform_device *pdev) +{ + int rc = 0; + struct snddev_virtual_data *pdata; + struct msm_snddev_info *dev_info; + + pr_debug("%s\n", __func__); + + if (!pdev || !pdev->dev.platform_data) { + pr_err("%s: Invalid caller\n", __func__); + + rc = -EPERM; + goto done; + } + + pdata = pdev->dev.platform_data; + + dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL); + if (!dev_info) { + pr_err("%s: Out of memory\n", __func__); + + rc = -ENOMEM; + goto done; + } + + dev_info->name = pdata->name; + dev_info->copp_id = pdata->copp_id; + dev_info->private_data = (void *) NULL; + dev_info->dev_ops.open = snddev_virtual_open; + dev_info->dev_ops.close = snddev_virtual_close; + dev_info->dev_ops.set_freq = snddev_virtual_set_freq; + dev_info->capability = pdata->capability; + dev_info->sample_rate = 48000; + dev_info->opened = 0; + dev_info->sessions = 0; + + msm_snddev_register(dev_info); + +done: + return rc; +} + +static int snddev_virtual_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver snddev_virtual_driver = { + .probe = snddev_virtual_probe, + .remove = snddev_virtual_remove, + .driver = { .name = "snddev_virtual" } +}; + +static int __init snddev_virtual_init(void) +{ + int rc = 0; + + pr_debug("%s\n", __func__); + + rc = platform_driver_register(&snddev_virtual_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: Platform driver register failure\n", __func__); + + return -ENODEV; + } + + return 0; +} + +static void __exit snddev_virtual_exit(void) +{ + platform_driver_unregister(&snddev_virtual_driver); + + return; +} + +module_init(snddev_virtual_init); +module_exit(snddev_virtual_exit); + +MODULE_DESCRIPTION("Virtual Sound Device driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_virtual.h b/arch/arm/mach-msm/qdsp6v2/snddev_virtual.h new file mode 100644 index 00000000000..dec4d0739de --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/snddev_virtual.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MACH_QDSP6V2_SNDDEV_VIRTUAL_H +#define __MACH_QDSP6V2_SNDDEV_VIRTUAL_H + +struct snddev_virtual_data { + u32 capability; /* RX or TX */ + const char *name; + u32 copp_id; /* Audpp routing */ +}; +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/timpani_profile_8x60.h b/arch/arm/mach-msm/qdsp6v2/timpani_profile_8x60.h new file mode 100644 index 00000000000..f02e0a0c1ae --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/timpani_profile_8x60.h @@ -0,0 +1,3225 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MACH_QDSP6V2_TIMPANI_PROFILE_H +#define __MACH_QDSP6V2_TIMPANI_PROFILE_H + +/* + * TX Device Profiles + */ + +/* Analog MIC */ +/* AMIC Primary mono */ +#define AMIC_PRI_MONO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + + +/* AMIC Secondary mono */ +#define AMIC_SEC_MONO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98 },\ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* AMIC dual */ +#define AMIC_DUAL_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xB0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAC, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* Fluid AMIC dual */ +#define FLUID_AMIC_DUAL_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* Fluid AMIC dual broadside */ +#define FLUID_AMIC_DUAL_BROADSIDE_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + /* AUX-IN to TxFE LEFT */ \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC1)}, \ + /* MIC1 to TxFE RIGHT */ \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * Digital MIC + */ +/* DMIC1 Primary (DMIC 1 - TX1) */ +#define DMIC1_PRI_MONO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* DMIC1 Secondary - (DMIC 2 - TX1) */ +#define DMIC1_SEC_MONO_OSR_64 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x12)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* DMIC Dual Primary (DMIC 1/2 - TX1) */ +#define DMIC1_PRI_STEREO_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)} } + +/* DMIC2 Dual Primary (DMIC 3/4 - TX2 - Left/Right) */ +#define DMIC2_SEC_DUAL_OSR_64 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x22)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0xF0, 0xE0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HS_DMIC2_STEREO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * LINE IN + */ +#define LINEIN_PRI_MONO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_PRI_STEREO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_SEC_MONO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x2E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0xF0, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_SEC_STEREO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x2E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0xF0, 0xE0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEIN_SEC_STEREO_OSR_64 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x22)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x96, 0xFF, 0x18)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0xF0, 0xE0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xA2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA6, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA7, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * AUX IN + */ +#define AUXIN_MONO_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* Headset MIC */ +#define HEADSET_AMIC2_TX_MONO_PRI_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * RX Device Profiles + */ + +/* RX EAR */ +#define EAR_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define EAR_SEC_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* ANC Headset: Speakers on Primary Rx, Noise Microphones on Secondary Tx */ + +#define ANC_HEADSET_CPLS_AMIC1_AUXL_RX1_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x95, 0xFF, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9A, 0xFF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9B, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0xC1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xC0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xD0, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x18, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x19, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x09, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0A, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FLUID_SPEAKER_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * RX HPH PRIMARY + */ + +/* RX HPH CLASS AB CAPLESS */ + +#define HEADSET_AB_CPLS_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_CPLS_MONO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on HEADSET_AB_CPLS_48000_OSR_256, change 0x83 */ +/* change 0x31 */ +#define HPH_PRI_AB_CPLS_MONO_LEFT \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on HEADSET_AB_CPLS_48000_OSR_256 */ +/* add 0x8A to mute rx1 left 0x01 */ +/* change 0x31 */ +#define HPH_PRI_AB_CPLS_MONO_RIGHT \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0D)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x35)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FTM_HPH_PRI_AB_CPLS_MONO_LB_LEFT \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x3C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define FTM_HPH_PRI_AB_CPLS_MONO_LB_RIGHT \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0D)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x35)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* This is for differential signaling, which is a test mode. */ +#define HPH_PRI_AB_CPLS_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_CPLS_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS AB LEGACY */ + +#define HPH_PRI_AB_LEG_MONO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HP_PRI_AB_LEG_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_AB_LEG_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x09)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF9)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS D LEGACY */ + +#define HPH_PRI_D_LEG_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0A, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_PRI_D_LEG_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x60)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xE1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2D, 0xFF, 0x6F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2E, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xF7, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xF7, 0x37)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xFF)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4A, 0xFF, 0x77)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x8C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* + * RX HPH SECONDARY + */ + +/* RX HPH CLASS AB CAPLESS */ +#define HPH_SEC_AB_CPLS_MONO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HPH_SEC_AB_CPLS_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_SEC_AB_CPLS_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS AB LEGACY */ +#define HPH_SEC_AB_LEG_MONO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_SEC_AB_LEG_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define HPH_SEC_AB_LEG_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF9)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX HPH CLASS D LEGACY */ + +#define HPH_SEC_D_LEG_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x50, 0x50)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0A, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000},\ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HPH_SEC_D_LEG_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x50, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0A, 0x0A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX LINE OUT PRIMARY */ +/* spkr phone mono rx */ +#define LINEOUT_PRI_MONO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_PRI_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_PRI_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0c)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX LINE OUT SECONDARY */ +#define LINEOUT_SEC_MONO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_SEC_DIFF \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LINEOUT_SEC_STEREO \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x29, 0xFF, 0xC2)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x48)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA5, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x48, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x4E1F}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* RX AUX */ +#define AUXOUT_PRI_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define AUXOUT_SEC_MONO_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA1, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x98, 0xFF, 0x02)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x28, 0xFF, 0xCA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x40)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x30, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA4, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAA, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x40, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_AUXPGA_HPH_AB_CPLS_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define LB_AUXPGA_LO_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +/* + * LB Device Profiles + */ + +/* EAR */ +#define LB_EAR_PRI_MONO \ + {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x04, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x04, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* HPH CLASS AB CAPLESS */ +#define LB_HPH_AB_CPLS_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_AB_CPLS_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define SPEAKER_HPH_AB_CPL_PRI_STEREO_48000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_AB_CPLS_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* HPH CLASS AB LEGACY */ +#define LB_HPH_AB_LEG_PRI_MONO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xFC)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_PHP_AB_LEG_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xFC)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x08, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_AB_LEG_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x59)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xFC)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* HPH CLASS D LEGACY */ +#define LB_HPH_D_LEG_PRI_DIFF \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3A, 0x2A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x2F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_HPH_D_LEG_PRI_STEREO \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0xA6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3A, 0x3A)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x3F)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* LINE OUT */ +#define LB_LINEOUT_PRI_MONO \ + {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_LINEOUT_PRI_DIFF \ + {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x10, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x80, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x10, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define LB_LINEOUT_PRI_STEREO \ + {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x90)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x58, 0x58)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 100000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFF, 0xA4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x90, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* AUX OUT */ +#define LB_AUXOUT_PRI_MONO \ + {{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0xE0, 0x80)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xA0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0xE0, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY RX */ +#define TTY_HEADSET_MONO_RX_8000_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* TTY TX */ +#define TTY_HEADSET_MONO_TX_OSR_256 \ + {{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* FTM devices */ +/* from HPH_PRI_AB_CPLS_DIFF */ +#define HEADSET_MONO_DIFF_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFC, 0xF5)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3D, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on SPEAKER_PRI_STEREO_48000_OSR_256 */ +/* change 0x8A */ +#define FTM_SPKR_L_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on SPEAKER_PRI_STEREO_48000_OSR_256 */ +/* change 0x8A */ +#define SPKR_R_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0x24, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on SPEAKER_PRI_STEREO_48000_OSR_256 */ +#define FTM_SPKR_RX_LB \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0x30, 0x30)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + + +#define SPKR_MONO_DIFF_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* from AMIC_PRI_MONO_OSR_256, change TxFE (reg 0x0D) */ +#define LINEIN_MONO_L_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD4)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* from AMIC_PRI_MONO_OSR_256, change TxFE (reg 0x0D) */ +#define LINEIN_MONO_R_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xD6)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* from AMIC_PRI_MONO_OSR_256 */ +#define AUX_IN_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* AUXOUT_PRI_MONO_8000_OSR_256 */ +#define AUX_OUT_RX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x20)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 50000}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x07)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0x07, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x20, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* from DMIC1_PRI_MONO_OSR_256 */ +#define DMIC1_LEFT_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define DMIC1_RIGHT_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x06)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define DMIC1_LEFT_AND_RIGHT_TX DMIC1_PRI_STEREO_OSR_256 + +#define DMIC2_LEFT_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0A)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define DMIC2_RIGHT_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define DMIC2_LEFT_AND_RIGHT_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x21)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#define HANDSET_MIC1_AUX_IN \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xB0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA1)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on AMIC_PRI_MONO_OSR_256 */ +#define FTM_HANDSET_LB_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x00)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0x3A98}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8B, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8C, 0x07, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA0, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on HEADSET_AMIC2_TX_MONO_PRI_OSR_256 */ +#define FTM_HEADSET_LB_TX \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8B, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8C, 0x07, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA0, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on EAR_PRI_MONO_8000_OSR_256 */ +#define EAR_PRI_MONO_LB \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0x60, 0x60)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x3C)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +/* based on AMIC_DUAL_OSR_256 */ +#define FTM_AMIC_DUAL_HANDSET_TX_LB \ + {{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_FLASH_IMAGE}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xB0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xA8)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \ + {ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA0, 0x03, 0x03)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \ + {ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} } + +#endif diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile new file mode 100644 index 00000000000..0be1303fc60 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile @@ -0,0 +1,2 @@ +obj-y += q6usm.o usf.o usfcdev.o +EXTRA_CFLAGS += -I$(src)/.. diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c new file mode 100644 index 00000000000..287470007bc --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c @@ -0,0 +1,1209 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6usm.h" + +/* The driver version*/ +#define DRV_VERSION "1.2" + +#define SESSION_MAX 0x02 /* aDSP:USM limit */ + +#define READDONE_IDX_STATUS 0 +#define READDONE_IDX_BUFFER 1 +#define READDONE_IDX_SIZE 2 +#define READDONE_IDX_OFFSET 3 +#define READDONE_IDX_MSW_TS 4 +#define READDONE_IDX_LSW_TS 5 +#define READDONE_IDX_FLAGS 6 +#define READDONE_IDX_NUMFRAMES 7 +#define READDONE_IDX_ID 8 + +#define WRITEDONE_IDX_STATUS 0 + +/* Standard timeout in the asynchronous ops */ +#define Q6USM_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */ + +static DEFINE_MUTEX(session_lock); + +static struct us_client *session[SESSION_MAX]; +static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv); +static int32_t q6usm_callback(struct apr_client_data *data, void *priv); +static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg); + +struct usm_mmap { + atomic_t ref_cnt; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; + void *apr; +}; + +static struct usm_mmap this_mmap; + +static int q6usm_session_alloc(struct us_client *usc) +{ + int ind = 0; + + mutex_lock(&session_lock); + for (ind = 0; ind < SESSION_MAX; ++ind) { + if (!session[ind]) { + session[ind] = usc; + mutex_unlock(&session_lock); + ++ind; /* session id: 0 reserved */ + pr_debug("%s: session[%d] was allocated\n", + __func__, ind); + return ind; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void q6usm_session_free(struct us_client *usc) +{ + /* Session index was incremented during allocation */ + uint16_t ind = (uint16_t)usc->session - 1; + + pr_debug("%s: to free session[%d]\n", __func__, ind); + if (ind < SESSION_MAX) { + mutex_lock(&session_lock); + session[ind] = 0; + mutex_unlock(&session_lock); + } +} + +int q6usm_us_client_buf_free(unsigned int dir, + struct us_client *usc) +{ + struct us_port_data *port; + int rc = 0; + uint32_t size = 0; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT))) + return -EINVAL; + + mutex_lock(&usc->cmd_lock); + port = &usc->port[dir]; + if (port == NULL) { + mutex_unlock(&usc->cmd_lock); + return -EINVAL; + } + + if (port->data == NULL) { + mutex_unlock(&usc->cmd_lock); + return 0; + } + + rc = q6usm_memory_unmap(usc, port->phys, dir); + if (rc) + pr_err("%s: CMD Memory_unmap* failed\n", __func__); + + pr_debug("%s: data[%p]phys[%p][%p]\n", __func__, + (void *)port->data, (void *)port->phys, (void *)&port->phys); + size = port->buf_size * port->buf_cnt; + dma_free_coherent(NULL, size, port->data, port->phys); + port->data = NULL; + port->phys = 0; + port->buf_size = 0; + port->buf_cnt = 0; + + mutex_unlock(&usc->cmd_lock); + return 0; +} + +void q6usm_us_client_free(struct us_client *usc) +{ + int loopcnt = 0; + struct us_port_data *port; + + if ((usc == NULL) || + !(usc->session)) + return; + + for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) { + port = &usc->port[loopcnt]; + if (port->data == NULL) + continue; + pr_debug("%s: loopcnt = %d\n", __func__, loopcnt); + q6usm_us_client_buf_free(loopcnt, usc); + } + q6usm_session_free(usc); + apr_deregister(usc->apr); + + pr_debug("%s: APR De-Register\n", __func__); + + if (atomic_read(&this_mmap.ref_cnt) <= 0) { + pr_err("%s: APR Common Port Already Closed\n", __func__); + goto done; + } + + atomic_dec(&this_mmap.ref_cnt); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + apr_deregister(this_mmap.apr); + pr_debug("%s: APR De-Register common port\n", __func__); + } +done: + kfree(usc); + pr_debug("%s:\n", __func__); + return; +} + +struct us_client *q6usm_us_client_alloc( + void (*cb)(uint32_t, uint32_t, uint32_t *, void *), + void *priv) +{ + struct us_client *usc; + int n; + int lcnt = 0; + + usc = kzalloc(sizeof(struct us_client), GFP_KERNEL); + if (usc == NULL) + return NULL; + n = q6usm_session_alloc(usc); + if (n <= 0) + goto fail_session; + usc->session = n; + usc->cb = cb; + usc->priv = priv; + usc->apr = apr_register("ADSP", "USM", \ + (apr_fn)q6usm_callback,\ + ((usc->session) << 8 | 0x0001),\ + usc); + + if (usc->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + goto fail; + } + pr_debug("%s: Registering the common port with APR\n", __func__); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + this_mmap.apr = apr_register("ADSP", "USM", + (apr_fn)q6usm_mmapcallback, + 0x0FFFFFFFF, &this_mmap); + if (this_mmap.apr == NULL) { + pr_err("%s: USM port registration failed\n", + __func__); + goto fail; + } + } + + atomic_inc(&this_mmap.ref_cnt); + init_waitqueue_head(&usc->cmd_wait); + mutex_init(&usc->cmd_lock); + for (lcnt = 0; lcnt <= OUT; ++lcnt) { + mutex_init(&usc->port[lcnt].lock); + spin_lock_init(&usc->port[lcnt].dsp_lock); + } + atomic_set(&usc->cmd_state, 0); + + return usc; +fail: + q6usm_us_client_free(usc); + return NULL; +fail_session: + kfree(usc); + return NULL; +} + +int q6usm_us_client_buf_alloc(unsigned int dir, + struct us_client *usc, + unsigned int bufsz, + unsigned int bufcnt) +{ + int rc = 0; + struct us_port_data *port = NULL; + unsigned int size = bufsz*bufcnt; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT)) || (size == 0) || + (usc->session <= 0 || usc->session > SESSION_MAX)) { + pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n", + __func__, size, bufcnt); + return -EINVAL; + } + + mutex_lock(&usc->cmd_lock); + + port = &usc->port[dir]; + + port->data = dma_alloc_coherent(NULL, size, &(port->phys), GFP_KERNEL); + if (port->data == NULL) { + pr_err("%s: US region allocation failed\n", __func__); + mutex_unlock(&usc->cmd_lock); + return -ENOMEM; + } + + port->buf_cnt = bufcnt; + port->buf_size = bufsz; + pr_debug("%s: data[%p]; phys[%p]; [%p]\n", __func__, + (void *)port->data, + (void *)port->phys, + (void *)&port->phys); + + rc = q6usm_memory_map(usc, port->phys, dir, size, 1); + if (rc < 0) { + pr_err("%s: CMD Memory_map failed\n", __func__); + mutex_unlock(&usc->cmd_lock); + q6usm_us_client_buf_free(dir, usc); + } else { + mutex_unlock(&usc->cmd_lock); + rc = 0; + } + + return rc; +} + +static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv) +{ + uint32_t token; + uint32_t *payload = data->payload; + + pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x];" + "token[0x%x]; payload_s[%d]; src[%d]; dest[%d];\n", + __func__, payload[0], payload[1], data->opcode, data->token, + data->payload_size, data->src_port, data->dest_port); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + /* status field check */ + if (payload[1]) { + pr_err("%s: wrong response[%d] on cmd [%d]\n", + __func__, payload[1], payload[0]); + } else { + token = data->token; + switch (payload[0]) { + case USM_SESSION_CMD_MEMORY_MAP: + case USM_SESSION_CMD_MEMORY_UNMAP: + pr_debug("%s: cmd[0x%x]; result[0x%x]\n", + __func__, payload[0], payload[1]); + if (atomic_read(&this_mmap.cmd_state)) { + atomic_set(&this_mmap.cmd_state, 0); + wake_up(&this_mmap.cmd_wait); + } + break; + default: + pr_debug("%s: wrong command[0x%x]\n", + __func__, payload[0]); + break; + } + } + } + return 0; +} + + +static int32_t q6usm_callback(struct apr_client_data *data, void *priv) +{ + struct us_client *usc = (struct us_client *)priv; + unsigned long dsp_flags; + uint32_t *payload = data->payload; + uint32_t token = data->token; + + if (usc == NULL) { + pr_err("%s: client info is NULL\n", __func__); + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + /* status field check */ + if (payload[1]) { + pr_err("%s: wrong response[%d] on cmd [%d]\n", + __func__, payload[1], payload[0]); + if (usc->cb) + usc->cb(data->opcode, token, + (uint32_t *)data->payload, usc->priv); + } else { + switch (payload[0]) { + case USM_SESSION_CMD_RUN: + case USM_STREAM_CMD_CLOSE: + if (token != usc->session) { + pr_err("%s: wrong token[%d]", + __func__, token); + break; + } + case USM_STREAM_CMD_OPEN_READ: + case USM_STREAM_CMD_OPEN_WRITE: + case USM_STREAM_CMD_SET_ENC_PARAM: + case USM_DATA_CMD_MEDIA_FORMAT_UPDATE: + case USM_SESSION_CMD_SIGNAL_DETECT_MODE: + if (atomic_read(&usc->cmd_state)) { + atomic_set(&usc->cmd_state, 0); + wake_up(&usc->cmd_wait); + } + if (usc->cb) + usc->cb(data->opcode, token, + (uint32_t *)data->payload, + usc->priv); + break; + default: + pr_debug("%s: command[0x%x] wrong response\n", + __func__, payload[0]); + break; + } + } + return 0; + } + + switch (data->opcode) { + case USM_DATA_EVENT_READ_DONE: { + struct us_port_data *port = &usc->port[OUT]; + + pr_debug("%s: R-D: stat=%d; buff=%x; size=%d; off=%d\n", + __func__, + payload[READDONE_IDX_STATUS], + payload[READDONE_IDX_BUFFER], + payload[READDONE_IDX_SIZE], + payload[READDONE_IDX_OFFSET]); + pr_debug("msw_ts=%d; lsw_ts=%d; flags=%d; id=%d; num=%d\n", + payload[READDONE_IDX_MSW_TS], + payload[READDONE_IDX_LSW_TS], + payload[READDONE_IDX_FLAGS], + payload[READDONE_IDX_ID], + payload[READDONE_IDX_NUMFRAMES]); + + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (payload[READDONE_IDX_STATUS]) { + pr_err("%s: wrong READDONE[%d]; token[%d]\n", + __func__, + payload[READDONE_IDX_STATUS], + token); + token = USM_WRONG_TOKEN; + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } + + if (port->expected_token != token) { + u32 cpu_buf = port->cpu_buf; + pr_err("%s: expected[%d] != token[%d]\n", + __func__, port->expected_token, token); + pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n", + __func__, port->dsp_buf, cpu_buf); + + token = USM_WRONG_TOKEN; + /* To prevent data handle continiue */ + port->expected_token = USM_WRONG_TOKEN; + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } /* port->expected_token != data->token */ + + port->expected_token = token + 1; + if (port->expected_token == port->buf_cnt) + port->expected_token = 0; + + /* gap support */ + if (port->expected_token != port->cpu_buf) { + port->dsp_buf = port->expected_token; + token = port->dsp_buf; /* for callback */ + } else + port->dsp_buf = token; + + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + break; + } /* case USM_DATA_EVENT_READ_DONE */ + + case USM_DATA_EVENT_WRITE_DONE: { + struct us_port_data *port = &usc->port[IN]; + + pr_debug("%s W-D: code[0x%x]; status[0x%x]; token[%d]", + __func__, + payload[0], payload[1], token); + + if (payload[WRITEDONE_IDX_STATUS]) { + pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n", + __func__, + payload[WRITEDONE_IDX_STATUS]); + break; + } + + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + port->dsp_buf = token + 1; + if (port->dsp_buf == port->buf_cnt) + port->dsp_buf = 0; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + pr_debug("%s: WRITE_DONE: token=%d; dsp_buf=%d; cpu_buf=%d\n", + __func__, + token, port->dsp_buf, port->cpu_buf); + + break; + } /* case USM_DATA_EVENT_WRITE_DONE */ + + case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: { + pr_debug("%s: US detect result: result=%d", + __func__, + payload[0]); + + break; + } /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */ + + default: + pr_debug("%s: not supported code [0x%x]", + __func__, data->opcode); + return 0; + + } /* switch */ + + if (usc->cb) + usc->cb(data->opcode, token, + data->payload, usc->priv); + + return 0; +} + +uint32_t q6usm_get_ready_data(int dir, struct us_client *usc) +{ + uint32_t ret = 0xffffffff; + + if ((usc != NULL) && ((dir == IN) || (dir == OUT))) + ret = usc->port[dir].dsp_buf; + return ret; +} + +uint32_t q6usm_get_virtual_address(int dir, + struct us_client *usc, + struct vm_area_struct *vms) +{ + uint32_t ret = 0xffffffff; + + if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) { + struct us_port_data *port = &usc->port[dir]; + ret = dma_mmap_coherent(NULL, vms, + port->data, port->phys, + port->buf_size * port->buf_cnt); + } + return ret; +} + +static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg) +{ + pr_debug("%s: pkt size=%d; cmd_flg=%d\n", + __func__, pkt_size, cmd_flg); + pr_debug("**************\n"); + mutex_lock(&usc->cmd_lock); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \ + APR_HDR_LEN(sizeof(struct apr_hdr)),\ + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)usc->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_USM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = (usc->session << 8) | 0x0001; + hdr->dest_port = (usc->session << 8) | 0x0001; + if (cmd_flg) { + hdr->token = usc->session; + atomic_set(&usc->cmd_state, 1); + } + hdr->pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, pkt_size); + mutex_unlock(&usc->cmd_lock); + return; +} + +static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg) +{ + pr_debug("%s: pkt size=%d cmd_flg=%d\n", + __func__, pkt_size, cmd_flg); + pr_debug("**************\n"); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \ + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0; + hdr->dest_port = 0; + if (cmd_flg) { + hdr->token = 0; + atomic_set(&this_mmap.cmd_state, 1); + } + hdr->pkt_size = pkt_size; + return; +} + +static uint32_t q6usm_ext2int_format(uint32_t ext_format) +{ + uint32_t int_format = INVALID_FORMAT; + switch (ext_format) { + case FORMAT_USPS_EPOS: + int_format = US_POINT_EPOS_FORMAT; + break; + case FORMAT_USRAW: + int_format = US_RAW_FORMAT; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, ext_format); + break; + } + + return int_format; +} + +int q6usm_open_read(struct us_client *usc, + uint32_t format) +{ + uint32_t int_format = INVALID_FORMAT; + int rc = 0x00; + struct usm_stream_cmd_open_read open; + + pr_debug("%s: session[%d]", __func__, usc->session); + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: client or its apr is NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_hdr(usc, &open.hdr, sizeof(open), true); + open.hdr.opcode = USM_STREAM_CMD_OPEN_READ; + open.src_endpoint = 0; /* AFE */ + open.pre_proc_top = 0; /* No preprocessing required */ + + int_format = q6usm_ext2int_format(format); + if (int_format == INVALID_FORMAT) + return -EINVAL; + + open.uMode = STREAM_PRIORITY_NORMAL; + open.format = int_format; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n", + __func__, rc); + goto fail_cmd; + } else + rc = 0; +fail_cmd: + return rc; +} + + +int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg* us_cfg) +{ + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj; + struct usm_stream_cmd_encdec_cfg_blk *enc_cfg = &enc_cfg_obj; + int rc = 0; + uint32_t total_cfg_size = + sizeof(struct usm_stream_cmd_encdec_cfg_blk); + uint32_t round_params_size = 0; + uint8_t is_allocated = 0; + + + if ((usc == NULL) || (us_cfg == NULL)) { + pr_err("%s: wrong input", __func__); + return -EINVAL; + } + + int_format = q6usm_ext2int_format(us_cfg->format_id); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong input format[%d]", + __func__, us_cfg->format_id); + return -EINVAL; + } + + /* Transparent configuration data is after enc_cfg */ + /* Integer number of u32s is requred */ + round_params_size = ((us_cfg->params_size + 3)/4) * 4; + if (round_params_size > USM_MAX_CFG_DATA_SIZE) { + /* Dynamic allocated encdec_cfg_blk is required */ + /* static part use */ + round_params_size -= USM_MAX_CFG_DATA_SIZE; + total_cfg_size += round_params_size; + enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL); + if (enc_cfg == NULL) { + pr_err("%s: enc_cfg[%d] allocation failed\n", + __func__, total_cfg_size); + return -ENOMEM; + } + is_allocated = 1; + } else + round_params_size = 0; + + q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size - APR_HDR_SIZE, true); + + enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM; + enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK; + enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+ + round_params_size; + enc_cfg->enc_blk.frames_per_buf = 1; + enc_cfg->enc_blk.format_id = int_format; + enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+ + USM_MAX_CFG_DATA_SIZE + + round_params_size; + memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common), + sizeof(struct usm_cfg_common)); + + /* Transparent data copy */ + memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params, + us_cfg->params_size); + pr_debug("%s: cfg_size[%d], params_size[%d]\n", + __func__, + enc_cfg->enc_blk.cfg_size, + us_cfg->params_size); + pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n", + __func__, + enc_cfg->enc_blk.transp_data[0], + enc_cfg->enc_blk.transp_data[1], + enc_cfg->enc_blk.transp_data[2], + enc_cfg->enc_blk.transp_data[3], + enc_cfg->enc_blk.transp_data[4], + enc_cfg->enc_blk.transp_data[5], + enc_cfg->enc_blk.transp_data[6], + enc_cfg->enc_blk.transp_data[7] + ); + pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n", + __func__, enc_cfg->enc_blk.cfg_common.sample_rate, + enc_cfg->enc_blk.cfg_common.ch_cfg, + enc_cfg->enc_blk.cfg_common.bits_per_sample, + enc_cfg->enc_blk.cfg_common.data_map, + enc_cfg->enc_blk.cfg_common.dev_id); + + rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg); + if (rc < 0) { + pr_err("%s:Comamnd open failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg->hdr.opcode); + } else + rc = 0; + +fail_cmd: + if (is_allocated == 1) + kfree(enc_cfg); + + return rc; +} + +int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg) +{ + + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_media_format_update dec_cfg_obj; + struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj; + + int rc = 0; + uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update); + uint32_t round_params_size = 0; + uint8_t is_allocated = 0; + + + if ((usc == NULL) || (us_cfg == NULL)) { + pr_err("%s: wrong input", __func__); + return -EINVAL; + } + + int_format = q6usm_ext2int_format(us_cfg->format_id); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong input format[%d]", + __func__, us_cfg->format_id); + return -EINVAL; + } + + /* Transparent configuration data is after enc_cfg */ + /* Integer number of u32s is requred */ + round_params_size = ((us_cfg->params_size + 3)/4) * 4; + if (round_params_size > USM_MAX_CFG_DATA_SIZE) { + /* Dynamic allocated encdec_cfg_blk is required */ + /* static part use */ + round_params_size -= USM_MAX_CFG_DATA_SIZE; + total_cfg_size += round_params_size; + dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL); + if (dec_cfg == NULL) { + pr_err("%s:dec_cfg[%d] allocation failed\n", + __func__, total_cfg_size); + return -ENOMEM; + } + is_allocated = 1; + } else { /* static transp_data is enough */ + round_params_size = 0; + } + + q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size - APR_HDR_SIZE, true); + + dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE; + dec_cfg->format_id = int_format; + dec_cfg->cfg_size = sizeof(struct usm_cfg_common) + + USM_MAX_CFG_DATA_SIZE + + round_params_size; + memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common), + sizeof(struct usm_cfg_common)); + /* Transparent data copy */ + memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size); + pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n", + __func__, + dec_cfg->cfg_size, + us_cfg->params_size, + dec_cfg->transp_data[0], + dec_cfg->transp_data[1], + dec_cfg->transp_data[2], + dec_cfg->transp_data[3] + ); + + rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg); + if (rc < 0) { + pr_err("%s:Comamnd open failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout opcode[0x%x]\n", + __func__, dec_cfg->hdr.opcode); + } else + rc = 0; + +fail_cmd: + if (is_allocated == 1) + kfree(dec_cfg); + + return rc; +} + +int q6usm_open_write(struct us_client *usc, + uint32_t format) +{ + int rc = 0; + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_cmd_open_write open; + + pr_debug("%s: session[%d]", __func__, usc->session); + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_hdr(usc, &open.hdr, sizeof(open), true); + open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE; + + int_format = q6usm_ext2int_format(format); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong format[%d]", __func__, format); + return -EINVAL; + } + + open.format = int_format; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s:open failed op[0x%x]rc[%d]\n", \ + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n", + __func__, rc); + goto fail_cmd; + } else + rc = 0; + +fail_cmd: + return rc; +} + +int q6usm_run(struct us_client *usc, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct usm_stream_cmd_run run; + int rc = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + q6usm_add_hdr(usc, &run.hdr, sizeof(run), true); + + run.hdr.opcode = USM_SESSION_CMD_RUN; + run.flags = flags; + run.msw_ts = msw_ts; + run.lsw_ts = lsw_ts; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]\n", __func__, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for run success rc[%d]\n", + __func__, rc); + } else + rc = 0; + +fail_cmd: + return rc; +} + + +int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir, + uint32_t bufsz, uint32_t bufcnt) +{ + struct usm_stream_cmd_memory_map mem_map; + int rc = 0; + + if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_mmaphdr(usc, &mem_map.hdr, + sizeof(struct usm_stream_cmd_memory_map), true); + mem_map.hdr.opcode = USM_SESSION_CMD_MEMORY_MAP; + + mem_map.buf_add = buf_add; + mem_map.buf_size = bufsz * bufcnt; + mem_map.mempool_id = 0; + + pr_debug("%s: buf add[%x] buf_add_parameter[%x]\n", + __func__, mem_map.buf_add, buf_add); + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map); + if (rc < 0) { + pr_err("%s: mem_map op[0x%x]rc[%d]\n", + __func__, mem_map.hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for memory_map\n", __func__); + } else + rc = 0; +fail_cmd: + return rc; +} + +int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir) +{ + struct usm_stream_cmd_memory_unmap mem_unmap; + int rc = 0; + + if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_mmaphdr(usc, &mem_unmap.hdr, + sizeof(struct usm_stream_cmd_memory_unmap), true); + mem_unmap.hdr.opcode = USM_SESSION_CMD_MEMORY_UNMAP; + mem_unmap.buf_add = buf_add; + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("%s:mem_unmap op[0x%x]rc[%d]\n", + __func__, mem_unmap.hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for memory_map\n", __func__); + } else + rc = 0; +fail_cmd: + return rc; +} + +int q6usm_read(struct us_client *usc, uint32_t read_ind) +{ + struct usm_stream_cmd_read read; + struct us_port_data *port = NULL; + int rc = 0; + u32 read_counter = 0; + u32 loop_ind = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[OUT]; + + if (read_ind > port->buf_cnt) { + pr_err("%s: wrong read_ind[%d]\n", + __func__, read_ind); + return -EINVAL; + } + if (read_ind == port->cpu_buf) { + pr_err("%s: no free region\n", __func__); + return 0; + } + + if (read_ind > port->cpu_buf) { /* 1 range */ + read_counter = read_ind - port->cpu_buf; + } else { /* 2 ranges */ + read_counter = (port->buf_cnt - port->cpu_buf) + read_ind; + } + + q6usm_add_hdr(usc, &read.hdr, (sizeof(read) - APR_HDR_SIZE), false); + + read.hdr.opcode = USM_DATA_CMD_READ; + read.buf_size = port->buf_size; + + for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) { + u32 temp_cpu_buf = port->cpu_buf; + + read.buf_add = (uint32_t)(port->phys) + + port->buf_size * (port->cpu_buf); + read.uid = port->cpu_buf; + read.hdr.token = port->cpu_buf; + read.counter = 1; + + ++(port->cpu_buf); + if (port->cpu_buf == port->buf_cnt) + port->cpu_buf = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &read); + + if (rc < 0) { + port->cpu_buf = temp_cpu_buf; + + pr_err("%s:read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + break; + } else + rc = 0; + } /* bufs loop */ + + return rc; +} + +int q6usm_write(struct us_client *usc, uint32_t write_ind) +{ + int rc = 0; + struct usm_stream_cmd_write cmd_write; + struct us_port_data *port = NULL; + u32 current_dsp_buf = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[IN]; + + current_dsp_buf = port->dsp_buf; + /* free region, caused by new dsp_buf report from DSP, */ + /* can be only extended */ + if (port->cpu_buf >= current_dsp_buf) { + /* 2 -part free region, including empty buffer */ + if ((write_ind <= port->cpu_buf) && + (write_ind > current_dsp_buf)) { + pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n", + __func__, write_ind, + current_dsp_buf, port->cpu_buf); + return -EINVAL; + } + } else { + /* 1 -part free region */ + if ((write_ind <= port->cpu_buf) || + (write_ind > current_dsp_buf)) { + pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n", + __func__, write_ind, + current_dsp_buf, port->cpu_buf); + return -EINVAL; + } + } + + q6usm_add_hdr(usc, &cmd_write.hdr, + (sizeof(cmd_write) - APR_HDR_SIZE), false); + + cmd_write.hdr.opcode = USM_DATA_CMD_WRITE; + cmd_write.buf_size = port->buf_size; + + while (port->cpu_buf != write_ind) { + u32 temp_cpu_buf = port->cpu_buf; + + cmd_write.buf_add = (uint32_t)(port->phys) + + port->buf_size * (port->cpu_buf); + cmd_write.uid = port->cpu_buf; + cmd_write.hdr.token = port->cpu_buf; + + pr_debug("%s:buf addr[0x%x] size[%d] token[%d] uid[%d]\n", + __func__, cmd_write.buf_add, cmd_write.buf_size, + cmd_write.hdr.token, cmd_write.uid); + pr_debug("%s: data=0x%p\n", __func__, port->data); + + ++(port->cpu_buf); + if (port->cpu_buf == port->buf_cnt) + port->cpu_buf = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write); + + if (rc < 0) { + port->cpu_buf = temp_cpu_buf; + pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n", + __func__, cmd_write.hdr.opcode, + rc, port->cpu_buf); + break; + } + + rc = 0; + } + + pr_debug("%s:exit: rc=%d; write_ind=%d; cpu_buf=%d; dsp_buf=%d\n", + __func__, rc, write_ind, port->cpu_buf, port->dsp_buf); + + return rc; +} + +bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t* free_region) +{ + struct us_port_data *port = NULL; + u32 cpu_buf = 0; + + if ((usc == NULL) || !free_region) { + pr_err("%s: input data wrong\n", __func__); + return false; + } + port = &usc->port[IN]; + cpu_buf = port->cpu_buf + 1; + if (cpu_buf == port->buf_cnt) + cpu_buf = 0; + + *free_region = port->dsp_buf; + + return cpu_buf == *free_region; +} + +int q6usm_cmd(struct us_client *usc, int cmd) +{ + struct apr_hdr hdr; + int rc = 0; + atomic_t *state; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + q6usm_add_hdr(usc, &hdr, (sizeof(hdr) - APR_HDR_SIZE), true); + switch (cmd) { + case CMD_CLOSE: + hdr.opcode = USM_STREAM_CMD_CLOSE; + state = &usc->cmd_state; + break; + + default: + pr_err("%s:Invalid format[%d]\n", __func__, cmd); + goto fail_cmd; + } + + pr_debug("%s:session[%d]opcode[0x%x] ", __func__, + usc->session, hdr.opcode); + rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + } else + rc = 0; +fail_cmd: + return rc; +} + +int q6usm_set_us_detection(struct us_client *usc, + struct usm_session_cmd_detect_info *detect_info, + uint16_t detect_info_size) +{ + int rc = 0; + + if ((usc == NULL) || + (detect_info_size == 0) || + (detect_info == NULL)) { + pr_err("%s: wrong input: usc=0x%p, inf_size=%d; info=0x%p", + __func__, + usc, + detect_info_size, + detect_info); + return -EINVAL; + } + + q6usm_add_hdr(usc, &detect_info->hdr, + detect_info_size - APR_HDR_SIZE, true); + + detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE; + + rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info); + if (rc < 0) { + pr_err("%s:Comamnd signal detect failed\n", __func__); + return -EINVAL; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +static int __init q6usm_init(void) +{ + pr_debug("%s\n", __func__); + init_waitqueue_head(&this_mmap.cmd_wait); + memset(session, 0, sizeof(session)); + return 0; +} + +device_initcall(q6usm_init); + +MODULE_DESCRIPTION("Interface with QDSP6:USM"); +MODULE_VERSION(DRV_VERSION); diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h new file mode 100644 index 00000000000..1338e86b3fb --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __Q6_USM_H__ +#define __Q6_USM_H__ + +#include + +/* cyclic buffer with 1 gap support */ +#define USM_MIN_BUF_CNT 3 + +#define FORMAT_USPS_EPOS 0x00000000 +#define FORMAT_USRAW 0x00000001 +#define INVALID_FORMAT 0xffffffff + +#define IN 0x000 +#define OUT 0x001 + +#define USM_WRONG_TOKEN 0xffffffff +#define USM_UNDEF_TOKEN 0xfffffffe + +#define CMD_CLOSE 0x0004 + +/* bit 0:1 represents priority of stream */ +#define STREAM_PRIORITY_NORMAL 0x0000 +#define STREAM_PRIORITY_LOW 0x0001 +#define STREAM_PRIORITY_HIGH 0x0002 + +/* bit 4 represents META enable of encoded data buffer */ +#define BUFFER_META_ENABLE 0x0010 + +struct us_region { + dma_addr_t phys; + /* If == NULL, the region isn't allocated */ + void *data; + /* number of buffers in the region */ + uint32_t buf_cnt; + /* size of buffer */ + uint32_t buf_size; +}; + +struct us_port_data { + dma_addr_t phys; + /* cyclic region of buffers with 1 gap */ + void *data; + /* number of buffers in the region */ + uint32_t buf_cnt; + /* size of buffer */ + uint32_t buf_size; + /* TX: write index */ + uint32_t dsp_buf; + /* TX: read index */ + uint32_t cpu_buf; + /* expected token from dsp */ + uint32_t expected_token; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; +}; + +struct us_client { + int session; + /* idx:1 out port, 0: in port*/ + struct us_port_data port[2]; + + struct apr_svc *apr; + struct mutex cmd_lock; + + atomic_t cmd_state; + atomic_t eos_state; + wait_queue_head_t cmd_wait; + + void (*cb)(uint32_t, uint32_t, uint32_t *, void *); + void *priv; +}; + +int q6usm_run(struct us_client *usc, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); +int q6usm_cmd(struct us_client *usc, int cmd); +int q6usm_us_client_buf_alloc(unsigned int dir, struct us_client *usc, + unsigned int bufsz, unsigned int bufcnt); +int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg); +int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg); +int q6usm_read(struct us_client *usc, uint32_t read_ind); +struct us_client *q6usm_us_client_alloc( + void (*cb)(uint32_t, uint32_t, uint32_t *, void *), + void *priv); +int q6usm_open_read(struct us_client *usc, uint32_t format); +void q6usm_us_client_free(struct us_client *usc); +int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, + int dir, uint32_t bufsz, uint32_t bufcnt); +int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, + int dir); + +uint32_t q6usm_get_ready_data(int dir, struct us_client *usc); +uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc, + struct vm_area_struct *vms); + +int q6usm_open_write(struct us_client *usc, uint32_t format); +int q6usm_write(struct us_client *usc, uint32_t write_ind); +bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t* free_region); + +int q6usm_set_us_detection(struct us_client *usc, + struct usm_session_cmd_detect_info *detect_info, + uint16_t detect_info_size); + +#endif /* __Q6_USM_H__ */ diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c new file mode 100644 index 00000000000..a8773eaea39 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c @@ -0,0 +1,1463 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6usm.h" +#include "usfcdev.h" + +/* The driver version*/ +#define DRV_VERSION "1.4.0" +#define USF_VERSION_ID 0x0140 + +/* Standard timeout in the asynchronous ops */ +#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */ + +/* Undefined USF device */ +#define USF_UNDEF_DEV_ID 0xffff + +/* RX memory mapping flag */ +#define USF_VM_WRITE 2 + +/* Number of events, copied from the user space to kernel one */ +#define USF_EVENTS_PORTION_SIZE 20 + +/* Indexes in range definitions */ +#define MIN_IND 0 +#define MAX_IND 1 + +/* The coordinates indexes */ +#define X_IND 0 +#define Y_IND 1 +#define Z_IND 2 + +/* Place for opreation result, received from QDSP6 */ +#define APR_RESULT_IND 1 + +/* Place for US detection result, received from QDSP6 */ +#define APR_US_DETECT_RESULT_IND 0 + +/* The driver states */ +enum usf_state_type { + USF_IDLE_STATE, + USF_OPENED_STATE, + USF_CONFIGURED_STATE, + USF_WORK_STATE, + USF_ERROR_STATE +}; + +/* The US detection status upon FW/HW based US detection results */ +enum usf_us_detect_type { + USF_US_DETECT_UNDEF, + USF_US_DETECT_YES, + USF_US_DETECT_NO +}; + +struct usf_xx_type { + /* Name of the client - event calculator */ + char client_name[USF_MAX_CLIENT_NAME_SIZE]; + /* The driver state in TX or RX direction */ + enum usf_state_type usf_state; + /* wait for q6 events mechanism */ + wait_queue_head_t wait; + /* IF with q6usm info */ + struct us_client *usc; + /* Q6:USM' Encoder/decoder configuration */ + struct us_encdec_cfg encdec_cfg; + /* Shared buffer (with Q6:USM) size */ + uint32_t buffer_size; + /* Number of the shared buffers (with Q6:USM) */ + uint32_t buffer_count; + /* Shared memory (Cyclic buffer with 1 gap) control */ + uint32_t new_region; + uint32_t prev_region; + /* Q6:USM's events handler */ + void (*cb)(uint32_t, uint32_t, uint32_t *, void *); + /* US detection result */ + enum usf_us_detect_type us_detect_type; + /* User's update info isn't acceptable */ + u8 user_upd_info_na; +}; + +struct usf_type { + /* TX device component configuration & control */ + struct usf_xx_type usf_tx; + /* RX device component configuration & control */ + struct usf_xx_type usf_rx; + /* Index into the opened device container */ + /* To prevent mutual usage of the same device */ + uint16_t dev_ind; + /* Event types, supported by device */ + uint16_t event_types; + /* The input devices are "input" module registered clients */ + struct input_dev *input_ifs[USF_MAX_EVENT_IND]; + /* The event source */ + int event_src; + /* Bitmap of types of events, conflicting to USF's ones */ + uint16_t conflicting_event_types; + /* Bitmap of types of events from devs, conflicting with USF */ + uint16_t conflicting_event_filters; +}; + +struct usf_input_dev_type { + /* Input event type, supported by the input device */ + uint16_t event_type; + /* Input device name */ + const char *input_dev_name; + /* Input device registration function */ + int (*prepare_dev)(uint16_t, struct usf_type *, + struct us_input_info_type *, + const char *); + /* Input event notification function */ + void (*notify_event)(struct usf_type *, + uint16_t, + struct usf_event_type * + ); +}; + + +/* The MAX number of the supported devices */ +#define MAX_DEVS_NUMBER 1 + +/* The opened devices container */ +static const int s_event_src_map[] = { + BTN_TOOL_PEN, /* US_INPUT_SRC_PEN*/ + 0, /* US_INPUT_SRC_FINGER */ + 0, /* US_INPUT_SRC_UNDEF */ +}; + +/* The opened devices container */ +static int s_opened_devs[MAX_DEVS_NUMBER]; + +#define USF_NAME_PREFIX "usf_" +#define USF_NAME_PREFIX_SIZE 4 + + +static struct input_dev *allocate_dev(uint16_t ind, const char *name) +{ + struct input_dev *in_dev = input_allocate_device(); + + if (in_dev == NULL) { + pr_err("%s: input_allocate_device() failed\n", __func__); + } else { + /* Common part configuration */ + in_dev->name = name; + in_dev->phys = NULL; + in_dev->id.bustype = BUS_HOST; + in_dev->id.vendor = 0x0001; + in_dev->id.product = 0x0001; + in_dev->id.version = USF_VERSION_ID; + } + return in_dev; +} + +static int prepare_tsc_input_device(uint16_t ind, + struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(in_dev, ABS_X, + input_info->tsc_x_dim[MIN_IND], + input_info->tsc_x_dim[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_Y, + input_info->tsc_y_dim[MIN_IND], + input_info->tsc_y_dim[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_DISTANCE, + input_info->tsc_z_dim[MIN_IND], + input_info->tsc_z_dim[MAX_IND], + 0, 0); + + input_set_abs_params(in_dev, ABS_PRESSURE, + input_info->tsc_pressure[MIN_IND], + input_info->tsc_pressure[MAX_IND], + 0, 0); + + input_set_abs_params(in_dev, ABS_TILT_X, + input_info->tsc_x_tilt[MIN_IND], + input_info->tsc_x_tilt[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_TILT_Y, + input_info->tsc_y_tilt[MIN_IND], + input_info->tsc_y_tilt[MAX_IND], + 0, 0); + + return 0; +} + +static int prepare_mouse_input_device(uint16_t ind, struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + + in_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT) | + BIT_MASK(BTN_MIDDLE); + in_dev->relbit[0] = BIT_MASK(REL_X) | + BIT_MASK(REL_Y) | + BIT_MASK(REL_Z); + + return 0; +} + +static int prepare_keyboard_input_device( + uint16_t ind, + struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] |= BIT_MASK(EV_KEY); + /* All keys are permitted */ + memset(in_dev->keybit, 0xff, sizeof(in_dev->keybit)); + + return 0; +} + +static void notify_tsc_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) + +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct point_event_type *pe = &(event->event_data.point_event); + + input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]); + input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]); + input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]); + + input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]); + input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]); + + input_report_abs(input_if, ABS_PRESSURE, pe->pressure); + input_report_key(input_if, BTN_TOUCH, !!(pe->pressure)); + + if (usf_info->event_src) + input_report_key(input_if, usf_info->event_src, 1); + + input_sync(input_if); + + pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d]\n", + __func__, + pe->coordinates[X_IND], + pe->coordinates[Y_IND], + pe->coordinates[Z_IND], + pe->inclinations[X_IND], + pe->inclinations[Y_IND], + pe->pressure); +} + +static void notify_mouse_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct mouse_event_type *me = &(event->event_data.mouse_event); + + input_report_rel(input_if, REL_X, me->rels[X_IND]); + input_report_rel(input_if, REL_Y, me->rels[Y_IND]); + input_report_rel(input_if, REL_Z, me->rels[Z_IND]); + + input_report_key(input_if, BTN_LEFT, + me->buttons_states & USF_BUTTON_LEFT_MASK); + input_report_key(input_if, BTN_MIDDLE, + me->buttons_states & USF_BUTTON_MIDDLE_MASK); + input_report_key(input_if, BTN_RIGHT, + me->buttons_states & USF_BUTTON_RIGHT_MASK); + + input_sync(input_if); + + pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n", + __func__, me->rels[X_IND], + me->rels[Y_IND], me->buttons_states); +} + +static void notify_key_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct key_event_type *ke = &(event->event_data.key_event); + + input_report_key(input_if, ke->key, ke->key_state); + input_sync(input_if); + pr_debug("%s: key event: key[%d], state[%d]\n", + __func__, + ke->key, + ke->key_state); + +} + +static struct usf_input_dev_type s_usf_input_devs[] = { + {USF_TSC_EVENT, "usf_tsc", + prepare_tsc_input_device, notify_tsc_event}, + {USF_TSC_PTR_EVENT, "usf_tsc_ptr", + prepare_tsc_input_device, notify_tsc_event}, + {USF_MOUSE_EVENT, "usf_mouse", + prepare_mouse_input_device, notify_mouse_event}, + {USF_KEYBOARD_EVENT, "usf_kb", + prepare_keyboard_input_device, notify_key_event}, +}; + +static void usf_rx_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv; + + if (usf_xx == NULL) { + pr_err("%s: the private data is NULL\n", __func__); + return; + } + + switch (opcode) { + case USM_DATA_EVENT_WRITE_DONE: + wake_up(&usf_xx->wait); + break; + default: + break; + } +} + +static void usf_tx_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv; + + if (usf_xx == NULL) { + pr_err("%s: the private data is NULL\n", __func__); + return; + } + + switch (opcode) { + case USM_DATA_EVENT_READ_DONE: + if (token == USM_WRONG_TOKEN) + usf_xx->usf_state = USF_ERROR_STATE; + usf_xx->new_region = token; + wake_up(&usf_xx->wait); + break; + + case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: + usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ? + USF_US_DETECT_YES : + USF_US_DETECT_NO; + + wake_up(&usf_xx->wait); + break; + + case APR_BASIC_RSP_RESULT: + if (payload[APR_RESULT_IND]) { + usf_xx->usf_state = USF_ERROR_STATE; + usf_xx->new_region = USM_WRONG_TOKEN; + wake_up(&usf_xx->wait); + } + break; + + default: + break; + } +} + +static void release_xx(struct usf_xx_type *usf_xx) +{ + if (usf_xx != NULL) { + if (usf_xx->usc) { + q6usm_us_client_free(usf_xx->usc); + usf_xx->usc = NULL; + } + + if (usf_xx->encdec_cfg.params != NULL) { + kfree(usf_xx->encdec_cfg.params); + usf_xx->encdec_cfg.params = NULL; + } + } +} + +static void usf_disable(struct usf_xx_type *usf_xx) +{ + if (usf_xx != NULL) { + if ((usf_xx->usf_state != USF_IDLE_STATE) && + (usf_xx->usf_state != USF_OPENED_STATE)) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + usf_xx->usf_state = USF_OPENED_STATE; + wake_up(&usf_xx->wait); + } + release_xx(usf_xx); + } +} + +static int config_xx(struct usf_xx_type *usf_xx, struct us_xx_info_type *config) +{ + int rc = 0; + uint16_t data_map_size = 0; + + if ((usf_xx == NULL) || + (config == NULL)) + return -EINVAL; + + data_map_size = sizeof(usf_xx->encdec_cfg.cfg_common.data_map); + + if (config->client_name != NULL) { + if (strncpy_from_user(usf_xx->client_name, + config->client_name, + sizeof(usf_xx->client_name) - 1) < 0) { + pr_err("%s: get client name failed\n", __func__); + return -EINVAL; + } + } + + pr_debug("%s: name=%s; buf_size:%d; dev_id:0x%x; sample_rate:%d\n", + __func__, usf_xx->client_name, config->buf_size, + config->dev_id, config->sample_rate); + + pr_debug("%s: buf_num:%d; format:%d; port_cnt:%d; data_size=%d\n", + __func__, config->buf_num, config->stream_format, + config->port_cnt, config->params_data_size); + + pr_debug("%s: p_id[0]=%d, p_id[1]=%d, p_id[2]=%d, p_id[3]=%d\n", + __func__, + config->port_id[0], + config->port_id[1], + config->port_id[2], + config->port_id[3]); + + if (data_map_size < config->port_cnt) { + pr_err("%s: number of supported ports:%d < requested:%d\n", + __func__, + data_map_size, + config->port_cnt); + return -EINVAL; + } + + /* q6usm allocation & configuration */ + usf_xx->buffer_size = config->buf_size; + usf_xx->buffer_count = config->buf_num; + usf_xx->encdec_cfg.cfg_common.bits_per_sample = + config->bits_per_sample; + usf_xx->encdec_cfg.cfg_common.sample_rate = config->sample_rate; + /* AFE port e.g. AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX */ + usf_xx->encdec_cfg.cfg_common.dev_id = config->dev_id; + + usf_xx->encdec_cfg.cfg_common.ch_cfg = config->port_cnt; + memcpy((void *)&usf_xx->encdec_cfg.cfg_common.data_map, + (void *)config->port_id, + config->port_cnt); + if (rc) { + pr_err("%s: ports offsets copy failure\n", __func__); + return -EINVAL; + } + + usf_xx->encdec_cfg.format_id = config->stream_format; + usf_xx->encdec_cfg.params_size = config->params_data_size; + usf_xx->user_upd_info_na = 1; /* it's used in US_GET_TX_UPDATE */ + + if (config->params_data_size > 0) { /* transparent data copy */ + usf_xx->encdec_cfg.params = kzalloc(config->params_data_size, + GFP_KERNEL); + if (usf_xx->encdec_cfg.params == NULL) { + pr_err("%s: params memory alloc[%d] failure\n", + __func__, + config->params_data_size); + return -ENOMEM; + } + rc = copy_from_user(usf_xx->encdec_cfg.params, + config->params_data, + config->params_data_size); + if (rc) { + pr_err("%s: transparent data copy failure\n", + __func__); + kfree(usf_xx->encdec_cfg.params); + usf_xx->encdec_cfg.params = NULL; + return -EFAULT; + } + pr_debug("%s: params_size[%d]; params[%d,%d,%d,%d, %d]\n", + __func__, + config->params_data_size, + usf_xx->encdec_cfg.params[0], + usf_xx->encdec_cfg.params[1], + usf_xx->encdec_cfg.params[2], + usf_xx->encdec_cfg.params[3], + usf_xx->encdec_cfg.params[4] + ); + } + + usf_xx->usc = q6usm_us_client_alloc(usf_xx->cb, (void *)usf_xx); + if (!usf_xx->usc) { + pr_err("%s: Could not allocate q6usm client\n", __func__); + rc = -EFAULT; + } + + return rc; +} + +static bool usf_match(uint16_t event_type_ind, struct input_dev *dev) +{ + bool rc = false; + + rc = (event_type_ind < MAX_EVENT_TYPE_NUM) && + ((dev->name == NULL) || + strncmp(dev->name, USF_NAME_PREFIX, USF_NAME_PREFIX_SIZE)); + pr_debug("%s: name=[%s]; rc=%d\n", + __func__, dev->name, rc); + + return rc; +} + +static bool usf_register_conflicting_events(uint16_t event_types) +{ + bool rc = true; + uint16_t ind = 0; + uint16_t mask = 1; + + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (event_types & mask) { + rc = usfcdev_register(ind, usf_match); + if (!rc) + break; + } + mask = mask << 1; + } + + return rc; +} + +static void usf_unregister_conflicting_events(uint16_t event_types) +{ + uint16_t ind = 0; + uint16_t mask = 1; + + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (event_types & mask) + usfcdev_unregister(ind); + mask = mask << 1; + } +} + +static void usf_set_event_filters(struct usf_type *usf, uint16_t event_filters) +{ + uint16_t ind = 0; + uint16_t mask = 1; + + if (usf->conflicting_event_filters != event_filters) { + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (usf->conflicting_event_types & mask) + usfcdev_set_filter(ind, event_filters&mask); + mask = mask << 1; + } + usf->conflicting_event_filters = event_filters; + } +} + +static int register_input_device(struct usf_type *usf_info, + struct us_input_info_type *input_info) +{ + int rc = 0; + bool ret = true; + uint16_t ind = 0; + + if ((usf_info == NULL) || + (input_info == NULL) || + !(input_info->event_types & USF_ALL_EVENTS)) { + pr_err("%s: wrong input parameter(s)\n", __func__); + return -EINVAL; + } + + if (input_info->event_src < ARRAY_SIZE(s_event_src_map)) + usf_info->event_src = + s_event_src_map[input_info->event_src]; + else + usf_info->event_src = 0; + + for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) { + if (usf_info->input_ifs[ind] != NULL) { + pr_err("%s: input_if[%d] is already allocated\n", + __func__, ind); + return -EFAULT; + } + if ((input_info->event_types & + s_usf_input_devs[ind].event_type) && + s_usf_input_devs[ind].prepare_dev) { + rc = (*s_usf_input_devs[ind].prepare_dev)( + ind, + usf_info, + input_info, + s_usf_input_devs[ind].input_dev_name); + if (rc) + return rc; + + + if (usf_info->event_src) + input_set_capability(usf_info->input_ifs[ind], + EV_KEY, + usf_info->event_src); + + rc = input_register_device(usf_info->input_ifs[ind]); + if (rc) { + pr_err("%s: input_reg_dev() failed; rc=%d\n", + __func__, rc); + input_free_device(usf_info->input_ifs[ind]); + usf_info->input_ifs[ind] = NULL; + } else { + usf_info->event_types |= + s_usf_input_devs[ind].event_type; + pr_debug("%s: input device[%s] was registered\n", + __func__, + s_usf_input_devs[ind].input_dev_name); + } + } /* supported event */ + } /* event types loop */ + + ret = usf_register_conflicting_events( + input_info->conflicting_event_types); + if (ret) + usf_info->conflicting_event_types = + input_info->conflicting_event_types; + + return 0; +} + + +static void handle_input_event(struct usf_type *usf_info, + uint16_t event_counter, + struct usf_event_type *event) +{ + uint16_t ind = 0; + uint16_t events_num = 0; + struct usf_event_type usf_events[USF_EVENTS_PORTION_SIZE]; + int rc = 0; + + if ((usf_info == NULL) || + (event == NULL) || (!event_counter)) { + return; + } + + while (event_counter > 0) { + if (event_counter > USF_EVENTS_PORTION_SIZE) { + events_num = USF_EVENTS_PORTION_SIZE; + event_counter -= USF_EVENTS_PORTION_SIZE; + } else { + events_num = event_counter; + event_counter = 0; + } + rc = copy_from_user(usf_events, + event, + events_num * sizeof(struct usf_event_type)); + if (rc) { + pr_err("%s: copy upd_rx_info from user; rc=%d\n", + __func__, rc); + return; + } + for (ind = 0; ind < events_num; ++ind) { + struct usf_event_type *p_event = &usf_events[ind]; + uint16_t if_ind = p_event->event_type_ind; + + if ((if_ind >= USF_MAX_EVENT_IND) || + (usf_info->input_ifs[if_ind] == NULL)) + continue; /* event isn't supported */ + + if (s_usf_input_devs[if_ind].notify_event) + (*s_usf_input_devs[if_ind].notify_event)( + usf_info, + if_ind, + p_event); + } /* loop in the portion */ + } /* all events loop */ +} + +static int usf_start_tx(struct usf_xx_type *usf_xx) +{ + int rc = q6usm_run(usf_xx->usc, 0, 0, 0); + + pr_debug("%s: tx: q6usm_run; rc=%d\n", __func__, rc); + if (!rc) { + if (usf_xx->buffer_count >= USM_MIN_BUF_CNT) { + /* supply all buffers */ + rc = q6usm_read(usf_xx->usc, + usf_xx->buffer_count); + pr_debug("%s: q6usm_read[%d]\n", + __func__, rc); + + if (rc) + pr_err("%s: buf read failed", + __func__); + else + usf_xx->usf_state = + USF_WORK_STATE; + } else + usf_xx->usf_state = + USF_WORK_STATE; + } + + return rc; +} /* usf_start_tx */ + +static int usf_start_rx(struct usf_xx_type *usf_xx) +{ + int rc = q6usm_run(usf_xx->usc, 0, 0, 0); + + pr_debug("%s: rx: q6usm_run; rc=%d\n", + __func__, rc); + if (!rc) + usf_xx->usf_state = USF_WORK_STATE; + + return rc; +} /* usf_start_rx */ + +static int usf_set_us_detection(struct usf_type *usf, unsigned long arg) +{ + uint32_t timeout = 0; + struct us_detect_info_type detect_info; + struct usm_session_cmd_detect_info usm_detect_info; + struct usm_session_cmd_detect_info *p_usm_detect_info = + &usm_detect_info; + uint32_t detect_info_size = sizeof(struct usm_session_cmd_detect_info); + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = copy_from_user(&detect_info, + (void *) arg, + sizeof(detect_info)); + + if (rc) { + pr_err("%s: copy detect_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (detect_info.us_detector != US_DETECT_FW) { + pr_err("%s: unsupported detector: %d\n", + __func__, detect_info.us_detector); + return -EINVAL; + } + + if ((detect_info.params_data_size != 0) && + (detect_info.params_data != NULL)) { + uint8_t *p_data = NULL; + + detect_info_size += detect_info.params_data_size; + p_usm_detect_info = kzalloc(detect_info_size, GFP_KERNEL); + if (p_usm_detect_info == NULL) { + pr_err("%s: detect_info[%d] allocation failed\n", + __func__, detect_info_size); + return -ENOMEM; + } + p_data = (uint8_t *)p_usm_detect_info + + sizeof(struct usm_session_cmd_detect_info); + + rc = copy_from_user(p_data, + (void *)detect_info.params_data, + detect_info.params_data_size); + if (rc) { + pr_err("%s: copy params from user; rc=%d\n", + __func__, rc); + kfree(p_usm_detect_info); + return -EFAULT; + } + p_usm_detect_info->algorithm_cfg_size = + detect_info.params_data_size; + } else + usm_detect_info.algorithm_cfg_size = 0; + + p_usm_detect_info->detect_mode = detect_info.us_detect_mode; + p_usm_detect_info->skip_interval = detect_info.skip_time; + + usf_xx->us_detect_type = USF_US_DETECT_UNDEF; + + rc = q6usm_set_us_detection(usf_xx->usc, + p_usm_detect_info, + detect_info_size); + if (rc || (detect_info.detect_timeout == USF_NO_WAIT_TIMEOUT)) { + if (detect_info_size > + sizeof(struct usm_session_cmd_detect_info)) + kfree(p_usm_detect_info); + return rc; + } + + /* Get US detection result */ + if (detect_info.detect_timeout == USF_INFINITIVE_TIMEOUT) { + rc = wait_event_interruptible(usf_xx->wait, + (usf_xx->us_detect_type != + USF_US_DETECT_UNDEF)); + } else { + if (detect_info.detect_timeout == USF_DEFAULT_TIMEOUT) + timeout = USF_TIMEOUT_JIFFIES; + else + timeout = detect_info.detect_timeout * HZ; + } + rc = wait_event_interruptible_timeout(usf_xx->wait, + (usf_xx->us_detect_type != + USF_US_DETECT_UNDEF), + timeout); + /* In the case of timeout, "no US" is assumed */ + if (rc < 0) { + pr_err("%s: Getting US detection failed rc[%d]\n", + __func__, rc); + return rc; + } + + usf->usf_rx.us_detect_type = usf->usf_tx.us_detect_type; + detect_info.is_us = (usf_xx->us_detect_type == USF_US_DETECT_YES); + rc = copy_to_user((void __user *)arg, + &detect_info, + sizeof(detect_info)); + if (rc) { + pr_err("%s: copy detect_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + if (detect_info_size > sizeof(struct usm_session_cmd_detect_info)) + kfree(p_usm_detect_info); + + return rc; +} /* usf_set_us_detection */ + +static int usf_set_tx_info(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_info_type config_tx; + const char *name = NULL; + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = copy_from_user(&config_tx, + (void *) arg, + sizeof(config_tx)); + + if (rc) { + pr_err("%s: copy config_tx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + name = config_tx.us_xx_info.client_name; + + usf_xx->new_region = USM_UNDEF_TOKEN; + usf_xx->prev_region = USM_UNDEF_TOKEN; + usf_xx->cb = usf_tx_cb; + + init_waitqueue_head(&usf_xx->wait); + + if (name != NULL) { + int res = strncpy_from_user( + usf_xx->client_name, + name, + sizeof(usf_xx->client_name)-1); + if (res < 0) { + pr_err("%s: get client name failed\n", + __func__); + return -EINVAL; + } + } + + rc = config_xx(usf_xx, &config_tx.us_xx_info); + if (rc) + return rc; + + rc = q6usm_open_read(usf_xx->usc, + usf_xx->encdec_cfg.format_id); + if (rc) + return rc; + + rc = q6usm_us_client_buf_alloc(OUT, usf_xx->usc, + usf_xx->buffer_size, + usf_xx->buffer_count); + if (rc) + return rc; + + rc = q6usm_enc_cfg_blk(usf_xx->usc, + &usf_xx->encdec_cfg); + if (!rc && + (config_tx.input_info.event_types != USF_NO_EVENT)) { + rc = register_input_device(usf, + &config_tx.input_info); + } + + if (!rc) + usf_xx->usf_state = USF_CONFIGURED_STATE; + + return rc; +} /* usf_set_tx_info */ + +static int usf_set_rx_info(struct usf_type *usf, unsigned long arg) +{ + struct us_rx_info_type config_rx; + struct usf_xx_type *usf_xx = &usf->usf_rx; + int rc = copy_from_user(&config_rx, + (void *) arg, + sizeof(config_rx)); + + if (rc) { + pr_err("%s: copy config_rx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + usf_xx->new_region = USM_UNDEF_TOKEN; + usf_xx->prev_region = USM_UNDEF_TOKEN; + + usf_xx->cb = usf_rx_cb; + + rc = config_xx(usf_xx, &config_rx.us_xx_info); + if (rc) + return rc; + + rc = q6usm_open_write(usf_xx->usc, + usf_xx->encdec_cfg.format_id); + if (rc) + return rc; + + if (usf_xx->buffer_size && usf_xx->buffer_count) { + rc = q6usm_us_client_buf_alloc( + IN, + usf_xx->usc, + usf_xx->buffer_size, + usf_xx->buffer_count); + if (rc) + return rc; + } + + rc = q6usm_dec_cfg_blk(usf_xx->usc, + &usf_xx->encdec_cfg); + if (!rc) { + init_waitqueue_head(&usf_xx->wait); + usf_xx->usf_state = USF_CONFIGURED_STATE; + } + + return rc; +} /* usf_set_rx_info */ + + +static int usf_get_tx_update(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_update_info_type upd_tx_info; + unsigned long prev_jiffies = 0; + uint32_t timeout = 0; + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = copy_from_user(&upd_tx_info, (void *) arg, + sizeof(upd_tx_info)); + + if (rc) { + pr_err("%s: copy upd_tx_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (!usf_xx->user_upd_info_na) { + usf_set_event_filters(usf, upd_tx_info.event_filters); + handle_input_event(usf, + upd_tx_info.event_counter, + upd_tx_info.event); + + /* Release available regions */ + rc = q6usm_read(usf_xx->usc, + upd_tx_info.free_region); + if (rc) + return rc; + } else + usf_xx->user_upd_info_na = 0; + + /* Get data ready regions */ + if (upd_tx_info.timeout == USF_INFINITIVE_TIMEOUT) { + rc = wait_event_interruptible(usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE)); + } else { + if (upd_tx_info.timeout == USF_NO_WAIT_TIMEOUT) + rc = (usf_xx->prev_region != usf_xx->new_region); + else { + prev_jiffies = jiffies; + if (upd_tx_info.timeout == USF_DEFAULT_TIMEOUT) { + timeout = USF_TIMEOUT_JIFFIES; + rc = wait_event_timeout( + usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE), + timeout); + } else { + timeout = upd_tx_info.timeout * HZ; + rc = wait_event_interruptible_timeout( + usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE), + timeout); + } + } + if (!rc) { + pr_debug("%s: timeout. prev_j=%lu; j=%lu\n", + __func__, prev_jiffies, jiffies); + pr_debug("%s: timeout. prev=%d; new=%d\n", + __func__, usf_xx->prev_region, + usf_xx->new_region); + pr_debug("%s: timeout. free_region=%d;\n", + __func__, upd_tx_info.free_region); + if (usf_xx->prev_region == + usf_xx->new_region) { + pr_err("%s:read data: timeout\n", + __func__); + return -ETIME; + } + } + } + + if ((usf_xx->usf_state != USF_WORK_STATE) || + (rc == -ERESTARTSYS)) { + pr_err("%s: Getting ready region failed " + "work state[%d]; rc[%d]\n", + __func__, usf_xx->usf_state, rc); + return -EINTR; + } + + upd_tx_info.ready_region = usf_xx->new_region; + usf_xx->prev_region = upd_tx_info.ready_region; + + if (upd_tx_info.ready_region == USM_WRONG_TOKEN) { + pr_err("%s: TX path corrupted; prev=%d\n", + __func__, usf_xx->prev_region); + return -EIO; + } + + rc = copy_to_user((void __user *)arg, &upd_tx_info, + sizeof(upd_tx_info)); + if (rc) { + pr_err("%s: copy upd_tx_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_tx_update */ + +static int usf_set_rx_update(struct usf_xx_type *usf_xx, unsigned long arg) +{ + struct us_rx_update_info_type upd_rx_info; + int rc = copy_from_user(&upd_rx_info, (void *) arg, + sizeof(upd_rx_info)); + + if (rc) { + pr_err("%s: copy upd_rx_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + /* Send available data regions */ + if (upd_rx_info.ready_region != + usf_xx->buffer_count) { + rc = q6usm_write( + usf_xx->usc, + upd_rx_info.ready_region); + if (rc) + return rc; + } + + /* Get free regions */ + rc = wait_event_timeout( + usf_xx->wait, + !q6usm_is_write_buf_full( + usf_xx->usc, + &upd_rx_info.free_region) || + (usf_xx->usf_state == USF_IDLE_STATE), + USF_TIMEOUT_JIFFIES); + + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. wait for write buf not full\n", + __func__); + } else { + if (usf_xx->usf_state != + USF_WORK_STATE) { + pr_err("%s: RX: state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EINTR; + } else { + rc = copy_to_user( + (void __user *)arg, + &upd_rx_info, + sizeof(upd_rx_info)); + if (rc) { + pr_err("%s: copy rx_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + } + } + + return rc; +} /* usf_set_rx_update */ + +static void usf_release_input(struct usf_type *usf) +{ + uint16_t ind = 0; + + for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) { + if (usf->input_ifs[ind] == NULL) + continue; + + usf_unregister_conflicting_events( + usf->conflicting_event_types); + usf->conflicting_event_types = 0; + input_unregister_device(usf->input_ifs[ind]); + usf->input_ifs[ind] = NULL; + pr_debug("%s input_unregister_device[%s]\n", + __func__, + s_usf_input_devs[ind].input_dev_name); + } +} /* usf_release_input */ + +static int usf_stop_tx(struct usf_type *usf) +{ + struct usf_xx_type *usf_xx = &usf->usf_tx; + + usf_release_input(usf); + usf_disable(usf_xx); + + return 0; +} /* usf_stop_tx */ + +static int usf_get_version(unsigned long arg) +{ + struct us_version_info_type version_info; + int rc = copy_from_user(&version_info, (void *) arg, + sizeof(version_info)); + + if (rc) { + pr_err("%s: copy version_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + /* version_info.buf is pointer to place for the version string */ + rc = copy_to_user(version_info.pbuf, + DRV_VERSION, + version_info.buf_size); + if (rc) { + pr_err("%s: copy to version_info.pbuf; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + rc = copy_to_user((void __user *)arg, + &version_info, + sizeof(version_info)); + if (rc) { + pr_err("%s: copy version_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_version */ + +static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rc = 0; + struct usf_type *usf = file->private_data; + struct usf_xx_type *usf_xx = NULL; + + switch (cmd) { + case US_START_TX: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_CONFIGURED_STATE) + rc = usf_start_tx(usf_xx); + else { + pr_err("%s: start_tx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } + + case US_START_RX: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_CONFIGURED_STATE) + rc = usf_start_rx(usf_xx); + else { + pr_err("%s: start_rx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } + + case US_SET_TX_INFO: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_tx_info(usf, arg); + else { + pr_err("%s: set_tx_info: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_TX_INFO */ + + case US_SET_RX_INFO: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_rx_info(usf, arg); + else { + pr_err("%s: set_rx_info: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_RX_INFO */ + + case US_GET_TX_UPDATE: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_get_tx_update(usf, arg); + else { + pr_err("%s: get_tx_update: wrong state[%d]\n", __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_GET_TX_UPDATE */ + + case US_SET_RX_UPDATE: { + struct usf_xx_type *usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_rx_update(usf_xx, arg); + else { + pr_err("%s: set_rx_update: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_RX_UPDATE */ + + case US_STOP_TX: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_stop_tx(usf); + else { + pr_err("%s: stop_tx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } /* US_STOP_TX */ + + case US_STOP_RX: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_WORK_STATE) + usf_disable(usf_xx); + else { + pr_err("%s: stop_rx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } /* US_STOP_RX */ + + case US_SET_DETECTION: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_us_detection(usf, arg); + else { + pr_err("%s: set us detection: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_DETECTION */ + + case US_GET_VERSION: { + rc = usf_get_version(arg); + break; + } /* US_GET_VERSION */ + + default: + pr_err("%s: unsupported IOCTL command [%d]\n", + __func__, + cmd); + rc = -ENOTTY; + break; + } + + if (rc && + ((cmd == US_SET_TX_INFO) || + (cmd == US_SET_RX_INFO))) + release_xx(usf_xx); + + return rc; +} /* usf_ioctl */ + +static int usf_mmap(struct file *file, struct vm_area_struct *vms) +{ + struct usf_type *usf = file->private_data; + int dir = OUT; + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (vms->vm_flags & USF_VM_WRITE) { /* RX buf mapping */ + dir = IN; + usf_xx = &usf->usf_rx; + } + + return q6usm_get_virtual_address(dir, usf_xx->usc, vms); +} + +static uint16_t add_opened_dev(int minor) +{ + uint16_t ind = 0; + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { + if (minor == s_opened_devs[ind]) { + pr_err("%s: device %d is already opened\n", + __func__, minor); + return USF_UNDEF_DEV_ID; + } + + if (s_opened_devs[ind] == 0) { + s_opened_devs[ind] = minor; + pr_debug("%s: device %d is added; ind=%d\n", + __func__, minor, ind); + return ind; + } + } + + pr_err("%s: there is no place for device %d\n", + __func__, minor); + return USF_UNDEF_DEV_ID; +} + +static int usf_open(struct inode *inode, struct file *file) +{ + struct usf_type *usf = NULL; + uint16_t dev_ind = 0; + int minor = MINOR(inode->i_rdev); + + dev_ind = add_opened_dev(minor); + if (dev_ind == USF_UNDEF_DEV_ID) + return -EBUSY; + + usf = kzalloc(sizeof(struct usf_type), GFP_KERNEL); + if (usf == NULL) { + pr_err("%s:usf allocation failed\n", __func__); + return -ENOMEM; + } + + file->private_data = usf; + usf->dev_ind = dev_ind; + + usf->usf_tx.usf_state = USF_OPENED_STATE; + usf->usf_rx.usf_state = USF_OPENED_STATE; + + usf->usf_tx.us_detect_type = USF_US_DETECT_UNDEF; + usf->usf_rx.us_detect_type = USF_US_DETECT_UNDEF; + + pr_debug("%s:usf in open\n", __func__); + return 0; +} + +static int usf_release(struct inode *inode, struct file *file) +{ + struct usf_type *usf = file->private_data; + + pr_debug("%s: release entry\n", __func__); + + usf_release_input(usf); + + usf_disable(&usf->usf_tx); + usf_disable(&usf->usf_rx); + + s_opened_devs[usf->dev_ind] = 0; + + kfree(usf); + pr_debug("%s: release exit\n", __func__); + return 0; +} + +static const struct file_operations usf_fops = { + .owner = THIS_MODULE, + .open = usf_open, + .release = usf_release, + .unlocked_ioctl = usf_ioctl, + .mmap = usf_mmap, +}; + +struct miscdevice usf_misc[MAX_DEVS_NUMBER] = { + { + .minor = MISC_DYNAMIC_MINOR, + .name = "usf1", + .fops = &usf_fops, + }, +}; + +static int __init usf_init(void) +{ + int rc = 0; + uint16_t ind = 0; + + pr_debug("%s: USF SW version %s.\n", __func__, DRV_VERSION); + pr_debug("%s: Max %d devs registration\n", __func__, MAX_DEVS_NUMBER); + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { + rc = misc_register(&usf_misc[ind]); + if (rc) { + pr_err("%s: misc_register() failed ind=%d; rc = %d\n", + __func__, ind, rc); + break; + } + } + + return rc; +} + +device_initcall(usf_init); + +MODULE_DESCRIPTION("Ultrasound framework driver"); +MODULE_VERSION(DRV_VERSION); diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c new file mode 100644 index 00000000000..b99a9b0494d --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "usfcdev.h" + +struct usfcdev_event { + bool (*match_cb)(uint16_t, struct input_dev *dev); + bool registered_event; + bool filter; +}; +static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM]; + +static bool usfcdev_filter(struct input_handle *handle, + unsigned int type, unsigned int code, int value); +static bool usfcdev_match(struct input_handler *handler, + struct input_dev *dev); +static int usfcdev_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id); +static void usfcdev_disconnect(struct input_handle *handle); + +static const struct input_device_id usfc_tsc_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, usfc_tsc_ids); + +static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = { + { /* TSC handler */ + .filter = usfcdev_filter, + .match = usfcdev_match, + .connect = usfcdev_connect, + .disconnect = usfcdev_disconnect, + /* .minor can be used as index in the container, */ + /* because .fops isn't supported */ + .minor = TSC_EVENT_TYPE_IND, + .name = "usfc_tsc_handler", + .id_table = usfc_tsc_ids, + }, +}; + +/* For each event type, one conflicting device (and handle) is supported */ +static struct input_handle s_usfc_handles[MAX_EVENT_TYPE_NUM] = { + { /* TSC handle */ + .handler = &s_usfc_handlers[TSC_EVENT_TYPE_IND], + .name = "usfc_tsc_handle", + }, +}; + +static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev) +{ + bool rc = false; + int ind = handler->minor; + + pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind); + if (s_usfcdev_events[ind].registered_event && + s_usfcdev_events[ind].match_cb) { + rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev); + pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc); + } + + return rc; +} + +static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + int ret = 0; + uint16_t ind = handler->minor; + + s_usfc_handles[ind].dev = dev; + ret = input_register_handle(&s_usfc_handles[ind]); + if (ret) { + pr_err("%s: input_register_handle[%d] failed: ret=%d\n", + __func__, + ind, + ret); + } else { + ret = input_open_device(&s_usfc_handles[ind]); + if (ret) { + pr_err("%s: input_open_device[%d] failed: ret=%d\n", + __func__, + ind, + ret); + input_unregister_handle(&s_usfc_handles[ind]); + } else + pr_debug("%s: device[%d] is opened\n", + __func__, + ind); + } + + return ret; +} + +static void usfcdev_disconnect(struct input_handle *handle) +{ + input_unregister_handle(handle); + pr_debug("%s: handle[%d] is disconnect\n", + __func__, + handle->handler->minor); +} + +static bool usfcdev_filter(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + uint16_t ind = (uint16_t)handle->handler->minor; + + pr_debug("%s: event_type=%d; filter=%d\n", + __func__, + ind, + s_usfcdev_events[ind].filter); + + return s_usfcdev_events[ind].filter; +} + +bool usfcdev_register( + uint16_t event_type_ind, + bool (*match_cb)(uint16_t, struct input_dev *dev)) +{ + int ret = 0; + bool rc = false; + + if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) { + pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%p\n", + __func__, + event_type_ind, + match_cb); + return false; + } + + if (s_usfcdev_events[event_type_ind].registered_event) { + pr_info("%s: handler[%d] was already registered\n", + __func__, + event_type_ind); + return true; + } + + s_usfcdev_events[event_type_ind].registered_event = true; + s_usfcdev_events[event_type_ind].match_cb = match_cb; + s_usfcdev_events[event_type_ind].filter = false; + ret = input_register_handler(&s_usfc_handlers[event_type_ind]); + if (!ret) { + rc = true; + pr_debug("%s: handler[%d] was registered\n", + __func__, + event_type_ind); + } else { + s_usfcdev_events[event_type_ind].registered_event = false; + s_usfcdev_events[event_type_ind].match_cb = NULL; + pr_err("%s: handler[%d] registration failed: ret=%d\n", + __func__, + event_type_ind, + ret); + } + + return rc; +} + +void usfcdev_unregister(uint16_t event_type_ind) +{ + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return; + } + if (s_usfcdev_events[event_type_ind].registered_event) { + input_unregister_handler(&s_usfc_handlers[event_type_ind]); + pr_debug("%s: handler[%d] was unregistered\n", + __func__, + event_type_ind); + s_usfcdev_events[event_type_ind].registered_event = false; + s_usfcdev_events[event_type_ind].match_cb = NULL; + s_usfcdev_events[event_type_ind].filter = false; + } +} + +bool usfcdev_set_filter(uint16_t event_type_ind, bool filter) +{ + bool rc = true; + + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return false; + } + + if (s_usfcdev_events[event_type_ind].registered_event) { + s_usfcdev_events[event_type_ind].filter = filter; + pr_debug("%s: event_type[%d]; filter=%d\n", + __func__, + event_type_ind, + filter + ); + } else { + pr_err("%s: event_type[%d] isn't registered\n", + __func__, + event_type_ind); + rc = false; + } + + return rc; +} + +static int __init usfcdev_init(void) +{ + return 0; +} + +device_initcall(usfcdev_init); + +MODULE_DESCRIPTION("Handle of events from devices, conflicting with USF"); diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.h new file mode 100644 index 00000000000..042b293cfdb --- /dev/null +++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __USFCDEV_H__ +#define __USFCDEV_H__ + +#include + +/* TSC event type index in the containers of the handlers & handles */ +#define TSC_EVENT_TYPE_IND 0 +/* Number of supported event types to be filtered */ +#define MAX_EVENT_TYPE_NUM 1 + +bool usfcdev_register( + uint16_t event_type_ind, + bool (*match_cb)(uint16_t, struct input_dev *dev)); +void usfcdev_unregister(uint16_t event_type_ind); +bool usfcdev_set_filter(uint16_t event_type_ind, bool filter); +#endif /* __USFCDEV_H__ */ diff --git a/arch/arm/mach-msm/qdss-etb.c b/arch/arm/mach-msm/qdss-etb.c new file mode 100644 index 00000000000..7837af04453 --- /dev/null +++ b/arch/arm/mach-msm/qdss-etb.c @@ -0,0 +1,412 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qdss-priv.h" + +#define etb_writel(etb, val, off) __raw_writel((val), etb.base + off) +#define etb_readl(etb, off) __raw_readl(etb.base + off) + +#define ETB_RAM_DEPTH_REG (0x004) +#define ETB_STATUS_REG (0x00C) +#define ETB_RAM_READ_DATA_REG (0x010) +#define ETB_RAM_READ_POINTER (0x014) +#define ETB_RAM_WRITE_POINTER (0x018) +#define ETB_TRG (0x01C) +#define ETB_CTL_REG (0x020) +#define ETB_RWD_REG (0x024) +#define ETB_FFSR (0x300) +#define ETB_FFCR (0x304) +#define ETB_ITMISCOP0 (0xEE0) +#define ETB_ITTRFLINACK (0xEE4) +#define ETB_ITTRFLIN (0xEE8) +#define ETB_ITATBDATA0 (0xEEC) +#define ETB_ITATBCTR2 (0xEF0) +#define ETB_ITATBCTR1 (0xEF4) +#define ETB_ITATBCTR0 (0xEF8) + + +#define BYTES_PER_WORD 4 +#define ETB_SIZE_WORDS 4096 +#define FRAME_SIZE_WORDS 4 + +#define ETB_LOCK() \ +do { \ + mb(); \ + etb_writel(etb, 0x0, CS_LAR); \ +} while (0) +#define ETB_UNLOCK() \ +do { \ + etb_writel(etb, CS_UNLOCK_MAGIC, CS_LAR); \ + mb(); \ +} while (0) + +struct etb_ctx { + uint8_t *buf; + void __iomem *base; + bool enabled; + bool reading; + spinlock_t spinlock; + atomic_t in_use; + struct device *dev; + struct kobject *kobj; + uint32_t trigger_cntr; +}; + +static struct etb_ctx etb; + +static void __etb_enable(void) +{ + int i; + + ETB_UNLOCK(); + + etb_writel(etb, 0x0, ETB_RAM_WRITE_POINTER); + for (i = 0; i < ETB_SIZE_WORDS; i++) + etb_writel(etb, 0x0, ETB_RWD_REG); + + etb_writel(etb, 0x0, ETB_RAM_WRITE_POINTER); + etb_writel(etb, 0x0, ETB_RAM_READ_POINTER); + + etb_writel(etb, etb.trigger_cntr, ETB_TRG); + etb_writel(etb, BIT(13) | BIT(0), ETB_FFCR); + etb_writel(etb, BIT(0), ETB_CTL_REG); + + ETB_LOCK(); +} + +void etb_enable(void) +{ + unsigned long flags; + + spin_lock_irqsave(&etb.spinlock, flags); + __etb_enable(); + etb.enabled = true; + dev_info(etb.dev, "ETB enabled\n"); + spin_unlock_irqrestore(&etb.spinlock, flags); +} + +static void __etb_disable(void) +{ + int count; + uint32_t ffcr; + + ETB_UNLOCK(); + + ffcr = etb_readl(etb, ETB_FFCR); + ffcr |= (BIT(12) | BIT(6)); + etb_writel(etb, ffcr, ETB_FFCR); + + for (count = TIMEOUT_US; BVAL(etb_readl(etb, ETB_FFCR), 6) != 0 + && count > 0; count--) + udelay(1); + WARN(count == 0, "timeout while flushing ETB, ETB_FFCR: %#x\n", + etb_readl(etb, ETB_FFCR)); + + etb_writel(etb, 0x0, ETB_CTL_REG); + + for (count = TIMEOUT_US; BVAL(etb_readl(etb, ETB_FFSR), 1) != 1 + && count > 0; count--) + udelay(1); + WARN(count == 0, "timeout while disabling ETB, ETB_FFSR: %#x\n", + etb_readl(etb, ETB_FFSR)); + + ETB_LOCK(); +} + +void etb_disable(void) +{ + unsigned long flags; + + spin_lock_irqsave(&etb.spinlock, flags); + __etb_disable(); + etb.enabled = false; + dev_info(etb.dev, "ETB disabled\n"); + spin_unlock_irqrestore(&etb.spinlock, flags); +} + +static void __etb_dump(void) +{ + int i; + uint8_t *buf_ptr; + uint32_t read_data; + uint32_t read_ptr; + uint32_t write_ptr; + uint32_t frame_off; + uint32_t frame_endoff; + + ETB_UNLOCK(); + + read_ptr = etb_readl(etb, ETB_RAM_READ_POINTER); + write_ptr = etb_readl(etb, ETB_RAM_WRITE_POINTER); + + frame_off = write_ptr % FRAME_SIZE_WORDS; + frame_endoff = FRAME_SIZE_WORDS - frame_off; + if (frame_off) { + dev_err(etb.dev, "write_ptr: %lu not aligned to formatter " + "frame size\n", (unsigned long)write_ptr); + dev_err(etb.dev, "frameoff: %lu, frame_endoff: %lu\n", + (unsigned long)frame_off, (unsigned long)frame_endoff); + write_ptr += frame_endoff; + } + + if ((etb_readl(etb, ETB_STATUS_REG) & BIT(0)) == 0) + etb_writel(etb, 0x0, ETB_RAM_READ_POINTER); + else + etb_writel(etb, write_ptr, ETB_RAM_READ_POINTER); + + buf_ptr = etb.buf; + for (i = 0; i < ETB_SIZE_WORDS; i++) { + read_data = etb_readl(etb, ETB_RAM_READ_DATA_REG); + *buf_ptr++ = read_data >> 0; + *buf_ptr++ = read_data >> 8; + *buf_ptr++ = read_data >> 16; + *buf_ptr++ = read_data >> 24; + } + + if (frame_off) { + buf_ptr -= (frame_endoff * BYTES_PER_WORD); + for (i = 0; i < frame_endoff; i++) { + *buf_ptr++ = 0x0; + *buf_ptr++ = 0x0; + *buf_ptr++ = 0x0; + *buf_ptr++ = 0x0; + } + } + + etb_writel(etb, read_ptr, ETB_RAM_READ_POINTER); + + ETB_LOCK(); +} + +void etb_dump(void) +{ + unsigned long flags; + + spin_lock_irqsave(&etb.spinlock, flags); + if (etb.enabled) { + __etb_disable(); + __etb_dump(); + __etb_enable(); + + dev_info(etb.dev, "ETB dumped\n"); + } + spin_unlock_irqrestore(&etb.spinlock, flags); +} + +static int etb_open(struct inode *inode, struct file *file) +{ + if (atomic_cmpxchg(&etb.in_use, 0, 1)) + return -EBUSY; + + dev_dbg(etb.dev, "%s: successfully opened\n", __func__); + return 0; +} + +static ssize_t etb_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + if (etb.reading == false) { + etb_dump(); + etb.reading = true; + } + + if (*ppos + len > ETB_SIZE_WORDS * BYTES_PER_WORD) + len = ETB_SIZE_WORDS * BYTES_PER_WORD - *ppos; + + if (copy_to_user(data, etb.buf + *ppos, len)) { + dev_dbg(etb.dev, "%s: copy_to_user failed\n", __func__); + return -EFAULT; + } + + *ppos += len; + + dev_dbg(etb.dev, "%s: %d bytes copied, %d bytes left\n", + __func__, len, (int) (ETB_SIZE_WORDS * BYTES_PER_WORD - *ppos)); + + return len; +} + +static int etb_release(struct inode *inode, struct file *file) +{ + etb.reading = false; + + atomic_set(&etb.in_use, 0); + + dev_dbg(etb.dev, "%s: released\n", __func__); + + return 0; +} + +static const struct file_operations etb_fops = { + .owner = THIS_MODULE, + .open = etb_open, + .read = etb_read, + .release = etb_release, +}; + +static struct miscdevice etb_misc = { + .name = "msm_etb", + .minor = MISC_DYNAMIC_MINOR, + .fops = &etb_fops, +}; + +#define ETB_ATTR(__name) \ +static struct kobj_attribute __name##_attr = \ + __ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store) + +static ssize_t trigger_cntr_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + etb.trigger_cntr = val; + return n; +} +static ssize_t trigger_cntr_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val = etb.trigger_cntr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETB_ATTR(trigger_cntr); + +static int __devinit etb_sysfs_init(void) +{ + int ret; + + etb.kobj = kobject_create_and_add("etb", qdss_get_modulekobj()); + if (!etb.kobj) { + dev_err(etb.dev, "failed to create ETB sysfs kobject\n"); + ret = -ENOMEM; + goto err_create; + } + + ret = sysfs_create_file(etb.kobj, &trigger_cntr_attr.attr); + if (ret) { + dev_err(etb.dev, "failed to create ETB sysfs trigger_cntr" + " attribute\n"); + goto err_file; + } + + return 0; +err_file: + kobject_put(etb.kobj); +err_create: + return ret; +} + +static void __devexit etb_sysfs_exit(void) +{ + sysfs_remove_file(etb.kobj, &trigger_cntr_attr.attr); + kobject_put(etb.kobj); +} + +static int __devinit etb_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -EINVAL; + goto err_res; + } + + etb.base = ioremap_nocache(res->start, resource_size(res)); + if (!etb.base) { + ret = -EINVAL; + goto err_ioremap; + } + + etb.dev = &pdev->dev; + + spin_lock_init(&etb.spinlock); + + ret = misc_register(&etb_misc); + if (ret) + goto err_misc; + + etb.buf = kzalloc(ETB_SIZE_WORDS * BYTES_PER_WORD, GFP_KERNEL); + if (!etb.buf) { + ret = -ENOMEM; + goto err_alloc; + } + + etb_sysfs_init(); + + dev_info(etb.dev, "ETB initialized\n"); + return 0; + +err_alloc: + misc_deregister(&etb_misc); +err_misc: + iounmap(etb.base); +err_ioremap: +err_res: + dev_err(etb.dev, "ETB init failed\n"); + return ret; +} + +static int __devexit etb_remove(struct platform_device *pdev) +{ + if (etb.enabled) + etb_disable(); + etb_sysfs_exit(); + kfree(etb.buf); + misc_deregister(&etb_misc); + iounmap(etb.base); + + return 0; +} + +static struct platform_driver etb_driver = { + .probe = etb_probe, + .remove = __devexit_p(etb_remove), + .driver = { + .name = "msm_etb", + }, +}; + +static int __init etb_init(void) +{ + return platform_driver_register(&etb_driver); +} +module_init(etb_init); + +static void __exit etb_exit(void) +{ + platform_driver_unregister(&etb_driver); +} +module_exit(etb_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver"); diff --git a/arch/arm/mach-msm/qdss-etm.c b/arch/arm/mach-msm/qdss-etm.c new file mode 100644 index 00000000000..ca6e0c6af8b --- /dev/null +++ b/arch/arm/mach-msm/qdss-etm.c @@ -0,0 +1,1329 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qdss-priv.h" + +#define etm_writel(etm, cpu, val, off) \ + __raw_writel((val), etm.base + (SZ_4K * cpu) + off) +#define etm_readl(etm, cpu, off) \ + __raw_readl(etm.base + (SZ_4K * cpu) + off) + +/* + * Device registers: + * 0x000 - 0x2FC: Trace registers + * 0x300 - 0x314: Management registers + * 0x318 - 0xEFC: Trace registers + * + * Coresight registers + * 0xF00 - 0xF9C: Management registers + * 0xFA0 - 0xFA4: Management registers in PFTv1.0 + * Trace registers in PFTv1.1 + * 0xFA8 - 0xFFC: Management registers + */ + +/* Trace registers (0x000-0x2FC) */ +#define ETMCR (0x000) +#define ETMCCR (0x004) +#define ETMTRIGGER (0x008) +#define ETMSR (0x010) +#define ETMSCR (0x014) +#define ETMTSSCR (0x018) +#define ETMTEEVR (0x020) +#define ETMTECR1 (0x024) +#define ETMFFLR (0x02C) +#define ETMACVRn(n) (0x040 + (n * 4)) +#define ETMACTRn(n) (0x080 + (n * 4)) +#define ETMCNTRLDVRn(n) (0x140 + (n * 4)) +#define ETMCNTENRn(n) (0x150 + (n * 4)) +#define ETMCNTRLDEVRn(n) (0x160 + (n * 4)) +#define ETMCNTVRn(n) (0x170 + (n * 4)) +#define ETMSQ12EVR (0x180) +#define ETMSQ21EVR (0x184) +#define ETMSQ23EVR (0x188) +#define ETMSQ31EVR (0x18C) +#define ETMSQ32EVR (0x190) +#define ETMSQ13EVR (0x194) +#define ETMSQR (0x19C) +#define ETMEXTOUTEVRn(n) (0x1A0 + (n * 4)) +#define ETMCIDCVRn(n) (0x1B0 + (n * 4)) +#define ETMCIDCMR (0x1BC) +#define ETMIMPSPEC0 (0x1C0) +#define ETMIMPSPEC1 (0x1C4) +#define ETMIMPSPEC2 (0x1C8) +#define ETMIMPSPEC3 (0x1CC) +#define ETMIMPSPEC4 (0x1D0) +#define ETMIMPSPEC5 (0x1D4) +#define ETMIMPSPEC6 (0x1D8) +#define ETMIMPSPEC7 (0x1DC) +#define ETMSYNCFR (0x1E0) +#define ETMIDR (0x1E4) +#define ETMCCER (0x1E8) +#define ETMEXTINSELR (0x1EC) +#define ETMTESSEICR (0x1F0) +#define ETMEIBCR (0x1F4) +#define ETMTSEVR (0x1F8) +#define ETMAUXCR (0x1FC) +#define ETMTRACEIDR (0x200) +#define ETMVMIDCVR (0x240) +/* Management registers (0x300-0x314) */ +#define ETMOSLAR (0x300) +#define ETMOSLSR (0x304) +#define ETMOSSRR (0x308) +#define ETMPDCR (0x310) +#define ETMPDSR (0x314) + +#define ETM_MAX_ADDR_CMP (16) +#define ETM_MAX_CNTR (4) +#define ETM_MAX_CTXID_CMP (3) + +#define ETM_MODE_EXCLUDE BIT(0) +#define ETM_MODE_CYCACC BIT(1) +#define ETM_MODE_STALL BIT(2) +#define ETM_MODE_TIMESTAMP BIT(3) +#define ETM_MODE_CTXID BIT(4) +#define ETM_MODE_ALL (0x1F) + +#define ETM_EVENT_MASK (0x1FFFF) +#define ETM_SYNC_MASK (0xFFF) +#define ETM_ALL_MASK (0xFFFFFFFF) + +#define ETM_SEQ_STATE_MAX_VAL (0x2) + +enum { + ETM_ADDR_TYPE_NONE, + ETM_ADDR_TYPE_SINGLE, + ETM_ADDR_TYPE_RANGE, + ETM_ADDR_TYPE_START, + ETM_ADDR_TYPE_STOP, +}; + +#define ETM_LOCK(cpu) \ +do { \ + mb(); \ + etm_writel(etm, cpu, 0x0, CS_LAR); \ +} while (0) +#define ETM_UNLOCK(cpu) \ +do { \ + etm_writel(etm, cpu, CS_UNLOCK_MAGIC, CS_LAR); \ + mb(); \ +} while (0) + + +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "qdss." + +#ifdef CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE +static int etm_boot_enable = 1; +#else +static int etm_boot_enable; +#endif +module_param_named( + etm_boot_enable, etm_boot_enable, int, S_IRUGO +); + +struct etm_ctx { + void __iomem *base; + bool enabled; + struct wake_lock wake_lock; + struct pm_qos_request qos_req; + struct qdss_source *src; + struct mutex mutex; + struct device *dev; + struct kobject *kobj; + uint8_t arch; + uint8_t nr_addr_cmp; + uint8_t nr_cntr; + uint8_t nr_ext_inp; + uint8_t nr_ext_out; + uint8_t nr_ctxid_cmp; + uint8_t reset; + uint32_t mode; + uint32_t ctrl; + uint32_t trigger_event; + uint32_t startstop_ctrl; + uint32_t enable_event; + uint32_t enable_ctrl1; + uint32_t fifofull_level; + uint8_t addr_idx; + uint32_t addr_val[ETM_MAX_ADDR_CMP]; + uint32_t addr_acctype[ETM_MAX_ADDR_CMP]; + uint32_t addr_type[ETM_MAX_ADDR_CMP]; + uint8_t cntr_idx; + uint32_t cntr_rld_val[ETM_MAX_CNTR]; + uint32_t cntr_event[ETM_MAX_CNTR]; + uint32_t cntr_rld_event[ETM_MAX_CNTR]; + uint32_t cntr_val[ETM_MAX_CNTR]; + uint32_t seq_12_event; + uint32_t seq_21_event; + uint32_t seq_23_event; + uint32_t seq_31_event; + uint32_t seq_32_event; + uint32_t seq_13_event; + uint32_t seq_curr_state; + uint8_t ctxid_idx; + uint32_t ctxid_val[ETM_MAX_CTXID_CMP]; + uint32_t ctxid_mask; + uint32_t sync_freq; + uint32_t timestamp_event; +}; + +static struct etm_ctx etm = { + .trigger_event = 0x406F, + .enable_event = 0x6F, + .enable_ctrl1 = 0x1, + .fifofull_level = 0x28, + .addr_val = {(uint32_t) _stext, (uint32_t) _etext}, + .addr_type = {ETM_ADDR_TYPE_RANGE, ETM_ADDR_TYPE_RANGE}, + .cntr_event = {[0 ... (ETM_MAX_CNTR - 1)] = 0x406F}, + .cntr_rld_event = {[0 ... (ETM_MAX_CNTR - 1)] = 0x406F}, + .seq_12_event = 0x406F, + .seq_21_event = 0x406F, + .seq_23_event = 0x406F, + .seq_31_event = 0x406F, + .seq_32_event = 0x406F, + .seq_13_event = 0x406F, + .sync_freq = 0x80, + .timestamp_event = 0x406F, +}; + + +/* ETM clock is derived from the processor clock and gets enabled on a + * logical OR of below items on Krait (pass2 onwards): + * 1.CPMR[ETMCLKEN] is 1 + * 2.ETMCR[PD] is 0 + * 3.ETMPDCR[PU] is 1 + * 4.Reset is asserted (core or debug) + * 5.APB memory mapped requests (eg. EDAP access) + * + * 1., 2. and 3. above are permanent enables whereas 4. and 5. are temporary + * enables + * + * We rely on 5. to be able to access ETMCR and then use 2. above for ETM + * clock vote in the driver and the save-restore code uses 1. above + * for its vote + */ +static void etm_set_pwrdwn(int cpu) +{ + uint32_t etmcr; + + etmcr = etm_readl(etm, cpu, ETMCR); + etmcr |= BIT(0); + etm_writel(etm, cpu, etmcr, ETMCR); +} + +static void etm_clr_pwrdwn(int cpu) +{ + uint32_t etmcr; + + etmcr = etm_readl(etm, cpu, ETMCR); + etmcr &= ~BIT(0); + etm_writel(etm, cpu, etmcr, ETMCR); +} + +static void etm_set_prog(int cpu) +{ + uint32_t etmcr; + int count; + + etmcr = etm_readl(etm, cpu, ETMCR); + etmcr |= BIT(10); + etm_writel(etm, cpu, etmcr, ETMCR); + + for (count = TIMEOUT_US; BVAL(etm_readl(etm, cpu, ETMSR), 1) != 1 + && count > 0; count--) + udelay(1); + WARN(count == 0, "timeout while setting prog bit, ETMSR: %#x\n", + etm_readl(etm, cpu, ETMSR)); +} + +static void etm_clr_prog(int cpu) +{ + uint32_t etmcr; + int count; + + etmcr = etm_readl(etm, cpu, ETMCR); + etmcr &= ~BIT(10); + etm_writel(etm, cpu, etmcr, ETMCR); + + for (count = TIMEOUT_US; BVAL(etm_readl(etm, cpu, ETMSR), 1) != 0 + && count > 0; count--) + udelay(1); + WARN(count == 0, "timeout while clearing prog bit, ETMSR: %#x\n", + etm_readl(etm, cpu, ETMSR)); +} + +static void __etm_enable(int cpu) +{ + int i; + + ETM_UNLOCK(cpu); + /* Vote for ETM power/clock enable */ + etm_clr_pwrdwn(cpu); + etm_set_prog(cpu); + + etm_writel(etm, cpu, etm.ctrl | BIT(10), ETMCR); + etm_writel(etm, cpu, etm.trigger_event, ETMTRIGGER); + etm_writel(etm, cpu, etm.startstop_ctrl, ETMTSSCR); + etm_writel(etm, cpu, etm.enable_event, ETMTEEVR); + etm_writel(etm, cpu, etm.enable_ctrl1, ETMTECR1); + etm_writel(etm, cpu, etm.fifofull_level, ETMFFLR); + for (i = 0; i < etm.nr_addr_cmp; i++) { + etm_writel(etm, cpu, etm.addr_val[i], ETMACVRn(i)); + etm_writel(etm, cpu, etm.addr_acctype[i], ETMACTRn(i)); + } + for (i = 0; i < etm.nr_cntr; i++) { + etm_writel(etm, cpu, etm.cntr_rld_val[i], ETMCNTRLDVRn(i)); + etm_writel(etm, cpu, etm.cntr_event[i], ETMCNTENRn(i)); + etm_writel(etm, cpu, etm.cntr_rld_event[i], ETMCNTRLDEVRn(i)); + etm_writel(etm, cpu, etm.cntr_val[i], ETMCNTVRn(i)); + } + etm_writel(etm, cpu, etm.seq_12_event, ETMSQ12EVR); + etm_writel(etm, cpu, etm.seq_21_event, ETMSQ21EVR); + etm_writel(etm, cpu, etm.seq_23_event, ETMSQ23EVR); + etm_writel(etm, cpu, etm.seq_31_event, ETMSQ31EVR); + etm_writel(etm, cpu, etm.seq_32_event, ETMSQ32EVR); + etm_writel(etm, cpu, etm.seq_13_event, ETMSQ13EVR); + etm_writel(etm, cpu, etm.seq_curr_state, ETMSQR); + for (i = 0; i < etm.nr_ext_out; i++) + etm_writel(etm, cpu, 0x0000406F, ETMEXTOUTEVRn(i)); + for (i = 0; i < etm.nr_ctxid_cmp; i++) + etm_writel(etm, cpu, etm.ctxid_val[i], ETMCIDCVRn(i)); + etm_writel(etm, cpu, etm.ctxid_mask, ETMCIDCMR); + etm_writel(etm, cpu, etm.sync_freq, ETMSYNCFR); + etm_writel(etm, cpu, 0x00000000, ETMEXTINSELR); + etm_writel(etm, cpu, etm.timestamp_event, ETMTSEVR); + etm_writel(etm, cpu, 0x00000000, ETMAUXCR); + etm_writel(etm, cpu, cpu+1, ETMTRACEIDR); + etm_writel(etm, cpu, 0x00000000, ETMVMIDCVR); + + etm_clr_prog(cpu); + ETM_LOCK(cpu); +} + +static int etm_enable(void) +{ + int ret, cpu; + + if (etm.enabled) { + dev_err(etm.dev, "ETM tracing already enabled\n"); + ret = -EPERM; + goto err; + } + + wake_lock(&etm.wake_lock); + /* 1. causes all online cpus to come out of idle PC + * 2. prevents idle PC until save restore flag is enabled atomically + * + * we rely on the user to prevent hotplug on/off racing with this + * operation and to ensure cores where trace is expected to be turned + * on are already hotplugged on + */ + pm_qos_update_request(&etm.qos_req, 0); + + ret = qdss_enable(etm.src); + if (ret) + goto err_qdss; + + for_each_online_cpu(cpu) + __etm_enable(cpu); + + etm.enabled = true; + + pm_qos_update_request(&etm.qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&etm.wake_lock); + + dev_info(etm.dev, "ETM tracing enabled\n"); + return 0; + +err_qdss: + pm_qos_update_request(&etm.qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&etm.wake_lock); +err: + return ret; +} + +static void __etm_disable(int cpu) +{ + ETM_UNLOCK(cpu); + etm_set_prog(cpu); + + /* program trace enable to low by using always false event */ + etm_writel(etm, cpu, 0x6F | BIT(14), ETMTEEVR); + + /* Vote for ETM power/clock disable */ + etm_set_pwrdwn(cpu); + ETM_LOCK(cpu); +} + +static int etm_disable(void) +{ + int ret, cpu; + + if (!etm.enabled) { + dev_err(etm.dev, "ETM tracing already disabled\n"); + ret = -EPERM; + goto err; + } + + wake_lock(&etm.wake_lock); + /* 1. causes all online cpus to come out of idle PC + * 2. prevents idle PC until save restore flag is disabled atomically + * + * we rely on the user to prevent hotplug on/off racing with this + * operation and to ensure cores where trace is expected to be turned + * off are already hotplugged on + */ + pm_qos_update_request(&etm.qos_req, 0); + + for_each_online_cpu(cpu) + __etm_disable(cpu); + + qdss_disable(etm.src); + + etm.enabled = false; + + pm_qos_update_request(&etm.qos_req, PM_QOS_DEFAULT_VALUE); + wake_unlock(&etm.wake_lock); + + dev_info(etm.dev, "ETM tracing disabled\n"); + return 0; +err: + return ret; +} + +/* Memory mapped writes to clear os lock not supported */ +static void etm_os_unlock(void *unused) +{ + unsigned long value = 0x0; + + asm("mcr p14, 1, %0, c1, c0, 4\n\t" : : "r" (value)); + asm("isb\n\t"); +} + +#define ETM_STORE(__name, mask) \ +static ssize_t __name##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t n) \ +{ \ + unsigned long val; \ + \ + if (sscanf(buf, "%lx", &val) != 1) \ + return -EINVAL; \ + \ + etm.__name = val & mask; \ + return n; \ +} + +#define ETM_SHOW(__name) \ +static ssize_t __name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + char *buf) \ +{ \ + unsigned long val = etm.__name; \ + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); \ +} + +#define ETM_ATTR(__name) \ +static struct kobj_attribute __name##_attr = \ + __ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store) +#define ETM_ATTR_RO(__name) \ +static struct kobj_attribute __name##_attr = \ + __ATTR(__name, S_IRUGO, __name##_show, NULL) + +static ssize_t enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int ret = 0; + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + if (val) + ret = etm_enable(); + else + ret = etm_disable(); + mutex_unlock(&etm.mutex); + + if (ret) + return ret; + return n; +} +ETM_SHOW(enabled); +ETM_ATTR(enabled); + +ETM_SHOW(nr_addr_cmp); +ETM_ATTR_RO(nr_addr_cmp); +ETM_SHOW(nr_cntr); +ETM_ATTR_RO(nr_cntr); +ETM_SHOW(nr_ctxid_cmp); +ETM_ATTR_RO(nr_ctxid_cmp); + +/* Reset to trace everything i.e. exclude nothing. */ +static ssize_t reset_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int i; + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + if (val) { + etm.mode = ETM_MODE_EXCLUDE; + etm.ctrl = 0x0; + if (cpu_is_krait_v1()) { + etm.mode |= ETM_MODE_CYCACC; + etm.ctrl |= BIT(12); + } + etm.trigger_event = 0x406F; + etm.startstop_ctrl = 0x0; + etm.enable_event = 0x6F; + etm.enable_ctrl1 = 0x1000000; + etm.fifofull_level = 0x28; + etm.addr_idx = 0x0; + for (i = 0; i < etm.nr_addr_cmp; i++) { + etm.addr_val[i] = 0x0; + etm.addr_acctype[i] = 0x0; + etm.addr_type[i] = ETM_ADDR_TYPE_NONE; + } + etm.cntr_idx = 0x0; + for (i = 0; i < etm.nr_cntr; i++) { + etm.cntr_rld_val[i] = 0x0; + etm.cntr_event[i] = 0x406F; + etm.cntr_rld_event[i] = 0x406F; + etm.cntr_val[i] = 0x0; + } + etm.seq_12_event = 0x406F; + etm.seq_21_event = 0x406F; + etm.seq_23_event = 0x406F; + etm.seq_31_event = 0x406F; + etm.seq_32_event = 0x406F; + etm.seq_13_event = 0x406F; + etm.seq_curr_state = 0x0; + etm.ctxid_idx = 0x0; + for (i = 0; i < etm.nr_ctxid_cmp; i++) + etm.ctxid_val[i] = 0x0; + etm.ctxid_mask = 0x0; + etm.sync_freq = 0x80; + etm.timestamp_event = 0x406F; + } + mutex_unlock(&etm.mutex); + return n; +} +ETM_SHOW(reset); +ETM_ATTR(reset); + +static ssize_t mode_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.mode = val & ETM_MODE_ALL; + + if (etm.mode & ETM_MODE_EXCLUDE) + etm.enable_ctrl1 |= BIT(24); + else + etm.enable_ctrl1 &= ~BIT(24); + + if (etm.mode & ETM_MODE_CYCACC) + etm.ctrl |= BIT(12); + else + etm.ctrl &= ~BIT(12); + + if (etm.mode & ETM_MODE_STALL) + etm.ctrl |= BIT(7); + else + etm.ctrl &= ~BIT(7); + + if (etm.mode & ETM_MODE_TIMESTAMP) + etm.ctrl |= BIT(28); + else + etm.ctrl &= ~BIT(28); + if (etm.mode & ETM_MODE_CTXID) + etm.ctrl |= (BIT(14) | BIT(15)); + else + etm.ctrl &= ~(BIT(14) | BIT(15)); + mutex_unlock(&etm.mutex); + + return n; +} +ETM_SHOW(mode); +ETM_ATTR(mode); + +ETM_STORE(trigger_event, ETM_EVENT_MASK); +ETM_SHOW(trigger_event); +ETM_ATTR(trigger_event); + +ETM_STORE(enable_event, ETM_EVENT_MASK); +ETM_SHOW(enable_event); +ETM_ATTR(enable_event); + +ETM_STORE(fifofull_level, ETM_ALL_MASK); +ETM_SHOW(fifofull_level); +ETM_ATTR(fifofull_level); + +static ssize_t addr_idx_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + if (val >= etm.nr_addr_cmp) + return -EINVAL; + + /* Use mutex to ensure index doesn't change while it gets dereferenced + * multiple times within a mutex block elsewhere. + */ + mutex_lock(&etm.mutex); + etm.addr_idx = val; + mutex_unlock(&etm.mutex); + return n; +} +ETM_SHOW(addr_idx); +ETM_ATTR(addr_idx); + +static ssize_t addr_single_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + uint8_t idx; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE || + etm.addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + etm.addr_val[idx] = val; + etm.addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t addr_single_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + uint8_t idx; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE || + etm.addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + val = etm.addr_val[idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(addr_single); + +static ssize_t addr_range_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val1, val2; + uint8_t idx; + + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + /* lower address comparator cannot have a higher address value */ + if (val1 > val2) + return -EINVAL; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (idx % 2 != 0) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + if (!((etm.addr_type[idx] == ETM_ADDR_TYPE_NONE && + etm.addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (etm.addr_type[idx] == ETM_ADDR_TYPE_RANGE && + etm.addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + etm.addr_val[idx] = val1; + etm.addr_type[idx] = ETM_ADDR_TYPE_RANGE; + etm.addr_val[idx + 1] = val2; + etm.addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + etm.enable_ctrl1 |= (1 << (idx/2)); + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t addr_range_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val1, val2; + uint8_t idx; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (idx % 2 != 0) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + if (!((etm.addr_type[idx] == ETM_ADDR_TYPE_NONE && + etm.addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (etm.addr_type[idx] == ETM_ADDR_TYPE_RANGE && + etm.addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + val1 = etm.addr_val[idx]; + val2 = etm.addr_val[idx + 1]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} +ETM_ATTR(addr_range); + +static ssize_t addr_start_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + uint8_t idx; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE || + etm.addr_type[idx] == ETM_ADDR_TYPE_START)) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + etm.addr_val[idx] = val; + etm.addr_type[idx] = ETM_ADDR_TYPE_START; + etm.startstop_ctrl |= (1 << idx); + etm.enable_ctrl1 |= BIT(25); + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t addr_start_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + uint8_t idx; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE || + etm.addr_type[idx] == ETM_ADDR_TYPE_START)) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + val = etm.addr_val[idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(addr_start); + +static ssize_t addr_stop_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + uint8_t idx; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE || + etm.addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + etm.addr_val[idx] = val; + etm.addr_type[idx] = ETM_ADDR_TYPE_STOP; + etm.startstop_ctrl |= (1 << (idx + 16)); + etm.enable_ctrl1 |= BIT(25); + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t addr_stop_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + uint8_t idx; + + mutex_lock(&etm.mutex); + idx = etm.addr_idx; + if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE || + etm.addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + mutex_unlock(&etm.mutex); + return -EPERM; + } + + val = etm.addr_val[idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(addr_stop); + +static ssize_t addr_acctype_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.addr_acctype[etm.addr_idx] = val; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t addr_acctype_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + + mutex_lock(&etm.mutex); + val = etm.addr_acctype[etm.addr_idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(addr_acctype); + +static ssize_t cntr_idx_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + if (val >= etm.nr_cntr) + return -EINVAL; + + /* Use mutex to ensure index doesn't change while it gets dereferenced + * multiple times within a mutex block elsewhere. + */ + mutex_lock(&etm.mutex); + etm.cntr_idx = val; + mutex_unlock(&etm.mutex); + return n; +} +ETM_SHOW(cntr_idx); +ETM_ATTR(cntr_idx); + +static ssize_t cntr_rld_val_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.cntr_rld_val[etm.cntr_idx] = val; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t cntr_rld_val_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + mutex_lock(&etm.mutex); + val = etm.cntr_rld_val[etm.cntr_idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(cntr_rld_val); + +static ssize_t cntr_event_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.cntr_event[etm.cntr_idx] = val & ETM_EVENT_MASK; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t cntr_event_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + + mutex_lock(&etm.mutex); + val = etm.cntr_event[etm.cntr_idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(cntr_event); + +static ssize_t cntr_rld_event_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.cntr_rld_event[etm.cntr_idx] = val & ETM_EVENT_MASK; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t cntr_rld_event_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + + mutex_lock(&etm.mutex); + val = etm.cntr_rld_event[etm.cntr_idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(cntr_rld_event); + +static ssize_t cntr_val_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.cntr_val[etm.cntr_idx] = val; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t cntr_val_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + + mutex_lock(&etm.mutex); + val = etm.cntr_val[etm.cntr_idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(cntr_val); + +ETM_STORE(seq_12_event, ETM_EVENT_MASK); +ETM_SHOW(seq_12_event); +ETM_ATTR(seq_12_event); + +ETM_STORE(seq_21_event, ETM_EVENT_MASK); +ETM_SHOW(seq_21_event); +ETM_ATTR(seq_21_event); + +ETM_STORE(seq_23_event, ETM_EVENT_MASK); +ETM_SHOW(seq_23_event); +ETM_ATTR(seq_23_event); + +ETM_STORE(seq_31_event, ETM_EVENT_MASK); +ETM_SHOW(seq_31_event); +ETM_ATTR(seq_31_event); + +ETM_STORE(seq_32_event, ETM_EVENT_MASK); +ETM_SHOW(seq_32_event); +ETM_ATTR(seq_32_event); + +ETM_STORE(seq_13_event, ETM_EVENT_MASK); +ETM_SHOW(seq_13_event); +ETM_ATTR(seq_13_event); + +static ssize_t seq_curr_state_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + if (val > ETM_SEQ_STATE_MAX_VAL) + return -EINVAL; + + etm.seq_curr_state = val; + return n; +} +ETM_SHOW(seq_curr_state); +ETM_ATTR(seq_curr_state); + +static ssize_t ctxid_idx_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + if (val >= etm.nr_ctxid_cmp) + return -EINVAL; + + /* Use mutex to ensure index doesn't change while it gets dereferenced + * multiple times within a mutex block elsewhere. + */ + mutex_lock(&etm.mutex); + etm.ctxid_idx = val; + mutex_unlock(&etm.mutex); + return n; +} +ETM_SHOW(ctxid_idx); +ETM_ATTR(ctxid_idx); + +static ssize_t ctxid_val_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + mutex_lock(&etm.mutex); + etm.ctxid_val[etm.ctxid_idx] = val; + mutex_unlock(&etm.mutex); + return n; +} +static ssize_t ctxid_val_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val; + + mutex_lock(&etm.mutex); + val = etm.ctxid_val[etm.ctxid_idx]; + mutex_unlock(&etm.mutex); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +ETM_ATTR(ctxid_val); + +ETM_STORE(ctxid_mask, ETM_ALL_MASK); +ETM_SHOW(ctxid_mask); +ETM_ATTR(ctxid_mask); + +ETM_STORE(sync_freq, ETM_SYNC_MASK); +ETM_SHOW(sync_freq); +ETM_ATTR(sync_freq); + +ETM_STORE(timestamp_event, ETM_EVENT_MASK); +ETM_SHOW(timestamp_event); +ETM_ATTR(timestamp_event); + +static struct attribute *etm_attrs[] = { + &nr_addr_cmp_attr.attr, + &nr_cntr_attr.attr, + &nr_ctxid_cmp_attr.attr, + &reset_attr.attr, + &mode_attr.attr, + &trigger_event_attr.attr, + &enable_event_attr.attr, + &fifofull_level_attr.attr, + &addr_idx_attr.attr, + &addr_single_attr.attr, + &addr_range_attr.attr, + &addr_start_attr.attr, + &addr_stop_attr.attr, + &addr_acctype_attr.attr, + &cntr_idx_attr.attr, + &cntr_rld_val_attr.attr, + &cntr_event_attr.attr, + &cntr_rld_event_attr.attr, + &cntr_val_attr.attr, + &seq_12_event_attr.attr, + &seq_21_event_attr.attr, + &seq_23_event_attr.attr, + &seq_31_event_attr.attr, + &seq_32_event_attr.attr, + &seq_13_event_attr.attr, + &seq_curr_state_attr.attr, + &ctxid_idx_attr.attr, + &ctxid_val_attr.attr, + &ctxid_mask_attr.attr, + &sync_freq_attr.attr, + ×tamp_event_attr.attr, + NULL, +}; + +static struct attribute_group etm_attr_grp = { + .attrs = etm_attrs, +}; + +static int __devinit etm_sysfs_init(void) +{ + int ret; + + etm.kobj = kobject_create_and_add("etm", qdss_get_modulekobj()); + if (!etm.kobj) { + dev_err(etm.dev, "failed to create ETM sysfs kobject\n"); + ret = -ENOMEM; + goto err_create; + } + + ret = sysfs_create_file(etm.kobj, &enabled_attr.attr); + if (ret) { + dev_err(etm.dev, "failed to create ETM sysfs enabled" + " attribute\n"); + goto err_file; + } + + if (sysfs_create_group(etm.kobj, &etm_attr_grp)) + dev_err(etm.dev, "failed to create ETM sysfs group\n"); + + return 0; +err_file: + kobject_put(etm.kobj); +err_create: + return ret; +} + +static void __devexit etm_sysfs_exit(void) +{ + sysfs_remove_group(etm.kobj, &etm_attr_grp); + sysfs_remove_file(etm.kobj, &enabled_attr.attr); + kobject_put(etm.kobj); +} + +static bool __devinit etm_arch_supported(uint8_t arch) +{ + switch (arch) { + case PFT_ARCH_V1_1: + break; + default: + return false; + } + return true; +} + +static int __devinit etm_arch_init(void) +{ + int ret, i; + /* use cpu 0 for setup */ + int cpu = 0; + uint32_t etmidr; + uint32_t etmccr; + + /* Unlock OS lock first to allow memory mapped reads and writes */ + etm_os_unlock(NULL); + smp_call_function(etm_os_unlock, NULL, 1); + ETM_UNLOCK(cpu); + /* Vote for ETM power/clock enable */ + etm_clr_pwrdwn(cpu); + /* Set prog bit. It will be set from reset but this is included to + * ensure it is set + */ + etm_set_prog(cpu); + + /* find all capabilities */ + etmidr = etm_readl(etm, cpu, ETMIDR); + etm.arch = BMVAL(etmidr, 4, 11); + if (etm_arch_supported(etm.arch) == false) { + ret = -EINVAL; + goto err; + } + + etmccr = etm_readl(etm, cpu, ETMCCR); + etm.nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2; + etm.nr_cntr = BMVAL(etmccr, 13, 15); + etm.nr_ext_inp = BMVAL(etmccr, 17, 19); + etm.nr_ext_out = BMVAL(etmccr, 20, 22); + etm.nr_ctxid_cmp = BMVAL(etmccr, 24, 25); + + if (cpu_is_krait_v1()) { + /* Krait pass1 doesn't support include filtering and non-cycle + * accurate tracing + */ + etm.mode = (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC); + etm.ctrl = 0x1000; + etm.enable_ctrl1 = 0x1000000; + for (i = 0; i < etm.nr_addr_cmp; i++) { + etm.addr_val[i] = 0x0; + etm.addr_acctype[i] = 0x0; + etm.addr_type[i] = ETM_ADDR_TYPE_NONE; + } + } + + /* Vote for ETM power/clock disable */ + etm_set_pwrdwn(cpu); + ETM_LOCK(cpu); + + return 0; +err: + return ret; +} + +static int __devinit etm_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -EINVAL; + goto err_res; + } + + etm.base = ioremap_nocache(res->start, resource_size(res)); + if (!etm.base) { + ret = -EINVAL; + goto err_ioremap; + } + + etm.dev = &pdev->dev; + + mutex_init(&etm.mutex); + wake_lock_init(&etm.wake_lock, WAKE_LOCK_SUSPEND, "msm_etm"); + pm_qos_add_request(&etm.qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + etm.src = qdss_get("msm_etm"); + if (IS_ERR(etm.src)) { + ret = PTR_ERR(etm.src); + goto err_qdssget; + } + + ret = qdss_clk_enable(); + if (ret) + goto err_clk; + + ret = etm_arch_init(); + if (ret) + goto err_arch; + + ret = etm_sysfs_init(); + if (ret) + goto err_sysfs; + + etm.enabled = false; + + qdss_clk_disable(); + + dev_info(etm.dev, "ETM initialized\n"); + + if (etm_boot_enable) + etm_enable(); + + return 0; + +err_sysfs: +err_arch: + qdss_clk_disable(); +err_clk: + qdss_put(etm.src); +err_qdssget: + pm_qos_remove_request(&etm.qos_req); + wake_lock_destroy(&etm.wake_lock); + mutex_destroy(&etm.mutex); + iounmap(etm.base); +err_ioremap: +err_res: + dev_err(etm.dev, "ETM init failed\n"); + return ret; +} + +static int __devexit etm_remove(struct platform_device *pdev) +{ + if (etm.enabled) + etm_disable(); + etm_sysfs_exit(); + qdss_put(etm.src); + pm_qos_remove_request(&etm.qos_req); + wake_lock_destroy(&etm.wake_lock); + mutex_destroy(&etm.mutex); + iounmap(etm.base); + + return 0; +} + +static struct platform_driver etm_driver = { + .probe = etm_probe, + .remove = __devexit_p(etm_remove), + .driver = { + .name = "msm_etm", + }, +}; + +int __init etm_init(void) +{ + return platform_driver_register(&etm_driver); +} +module_init(etm_init); + +void __exit etm_exit(void) +{ + platform_driver_unregister(&etm_driver); +} +module_exit(etm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CoreSight Program Flow Trace driver"); diff --git a/arch/arm/mach-msm/qdss-funnel.c b/arch/arm/mach-msm/qdss-funnel.c new file mode 100644 index 00000000000..52eb2b66e09 --- /dev/null +++ b/arch/arm/mach-msm/qdss-funnel.c @@ -0,0 +1,232 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qdss-priv.h" + +#define funnel_writel(funnel, id, val, off) \ + __raw_writel((val), funnel.base + (SZ_4K * id) + off) +#define funnel_readl(funnel, id, off) \ + __raw_readl(funnel.base + (SZ_4K * id) + off) + +#define FUNNEL_FUNCTL (0x000) +#define FUNNEL_PRICTL (0x004) +#define FUNNEL_ITATBDATA0 (0xEEC) +#define FUNNEL_ITATBCTR2 (0xEF0) +#define FUNNEL_ITATBCTR1 (0xEF4) +#define FUNNEL_ITATBCTR0 (0xEF8) + + +#define FUNNEL_LOCK(id) \ +do { \ + mb(); \ + funnel_writel(funnel, id, 0x0, CS_LAR); \ +} while (0) +#define FUNNEL_UNLOCK(id) \ +do { \ + funnel_writel(funnel, id, CS_UNLOCK_MAGIC, CS_LAR); \ + mb(); \ +} while (0) + +#define FUNNEL_HOLDTIME_MASK (0xF00) +#define FUNNEL_HOLDTIME_SHFT (0x8) +#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) + +struct funnel_ctx { + void __iomem *base; + bool enabled; + struct mutex mutex; + struct device *dev; + struct kobject *kobj; + uint32_t priority; +}; + +static struct funnel_ctx funnel; + +static void __funnel_enable(uint8_t id, uint32_t port_mask) +{ + uint32_t functl; + + FUNNEL_UNLOCK(id); + + functl = funnel_readl(funnel, id, FUNNEL_FUNCTL); + functl &= ~FUNNEL_HOLDTIME_MASK; + functl |= FUNNEL_HOLDTIME; + functl |= port_mask; + funnel_writel(funnel, id, functl, FUNNEL_FUNCTL); + funnel_writel(funnel, id, funnel.priority, FUNNEL_PRICTL); + + FUNNEL_LOCK(id); +} + +void funnel_enable(uint8_t id, uint32_t port_mask) +{ + mutex_lock(&funnel.mutex); + __funnel_enable(id, port_mask); + funnel.enabled = true; + dev_info(funnel.dev, "FUNNEL port mask 0x%lx enabled\n", + (unsigned long) port_mask); + mutex_unlock(&funnel.mutex); +} + +static void __funnel_disable(uint8_t id, uint32_t port_mask) +{ + uint32_t functl; + + FUNNEL_UNLOCK(id); + + functl = funnel_readl(funnel, id, FUNNEL_FUNCTL); + functl &= ~port_mask; + funnel_writel(funnel, id, functl, FUNNEL_FUNCTL); + + FUNNEL_LOCK(id); +} + +void funnel_disable(uint8_t id, uint32_t port_mask) +{ + mutex_lock(&funnel.mutex); + __funnel_disable(id, port_mask); + funnel.enabled = false; + dev_info(funnel.dev, "FUNNEL port mask 0x%lx disabled\n", + (unsigned long) port_mask); + mutex_unlock(&funnel.mutex); +} + +#define FUNNEL_ATTR(__name) \ +static struct kobj_attribute __name##_attr = \ + __ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store) + +static ssize_t priority_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + funnel.priority = val; + return n; +} +static ssize_t priority_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val = funnel.priority; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +FUNNEL_ATTR(priority); + +static int __devinit funnel_sysfs_init(void) +{ + int ret; + + funnel.kobj = kobject_create_and_add("funnel", qdss_get_modulekobj()); + if (!funnel.kobj) { + dev_err(funnel.dev, "failed to create FUNNEL sysfs kobject\n"); + ret = -ENOMEM; + goto err_create; + } + + ret = sysfs_create_file(funnel.kobj, &priority_attr.attr); + if (ret) { + dev_err(funnel.dev, "failed to create FUNNEL sysfs priority" + " attribute\n"); + goto err_file; + } + + return 0; +err_file: + kobject_put(funnel.kobj); +err_create: + return ret; +} + +static void __devexit funnel_sysfs_exit(void) +{ + sysfs_remove_file(funnel.kobj, &priority_attr.attr); + kobject_put(funnel.kobj); +} + +static int __devinit funnel_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -EINVAL; + goto err_res; + } + + funnel.base = ioremap_nocache(res->start, resource_size(res)); + if (!funnel.base) { + ret = -EINVAL; + goto err_ioremap; + } + + funnel.dev = &pdev->dev; + + mutex_init(&funnel.mutex); + + funnel_sysfs_init(); + + dev_info(funnel.dev, "FUNNEL initialized\n"); + return 0; + +err_ioremap: +err_res: + dev_err(funnel.dev, "FUNNEL init failed\n"); + return ret; +} + +static int __devexit funnel_remove(struct platform_device *pdev) +{ + if (funnel.enabled) + funnel_disable(0x0, 0xFF); + funnel_sysfs_exit(); + mutex_destroy(&funnel.mutex); + iounmap(funnel.base); + + return 0; +} + +static struct platform_driver funnel_driver = { + .probe = funnel_probe, + .remove = __devexit_p(funnel_remove), + .driver = { + .name = "msm_funnel", + }, +}; + +static int __init funnel_init(void) +{ + return platform_driver_register(&funnel_driver); +} +module_init(funnel_init); + +static void __exit funnel_exit(void) +{ + platform_driver_unregister(&funnel_driver); +} +module_exit(funnel_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CoreSight Funnel driver"); diff --git a/arch/arm/mach-msm/qdss-priv.h b/arch/arm/mach-msm/qdss-priv.h new file mode 100644 index 00000000000..f39bc52a164 --- /dev/null +++ b/arch/arm/mach-msm/qdss-priv.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_ARM_MACH_MSM_QDSS_H_ +#define _ARCH_ARM_MACH_MSM_QDSS_H_ + +#include +#include + +/* Coresight management registers (0xF00-0xFCC) + * 0xFA0 - 0xFA4: Management registers in PFTv1.0 + * Trace registers in PFTv1.1 + */ +#define CS_ITCTRL (0xF00) +#define CS_CLAIMSET (0xFA0) +#define CS_CLAIMCLR (0xFA4) +#define CS_LAR (0xFB0) +#define CS_LSR (0xFB4) +#define CS_AUTHSTATUS (0xFB8) +#define CS_DEVID (0xFC8) +#define CS_DEVTYPE (0xFCC) +/* Peripheral id registers (0xFD0-0xFEC) */ +#define CS_PIDR4 (0xFD0) +#define CS_PIDR5 (0xFD4) +#define CS_PIDR6 (0xFD8) +#define CS_PIDR7 (0xFDC) +#define CS_PIDR0 (0xFE0) +#define CS_PIDR1 (0xFE4) +#define CS_PIDR2 (0xFE8) +#define CS_PIDR3 (0xFEC) +/* Component id registers (0xFF0-0xFFC) */ +#define CS_CIDR0 (0xFF0) +#define CS_CIDR1 (0xFF4) +#define CS_CIDR2 (0xFF8) +#define CS_CIDR3 (0xFFC) + +/* DBGv7 with baseline CP14 registers implemented */ +#define ARM_DEBUG_ARCH_V7B (0x3) +/* DBGv7 with all CP14 registers implemented */ +#define ARM_DEBUG_ARCH_V7 (0x4) +#define ARM_DEBUG_ARCH_V7_1 (0x5) +#define ETM_ARCH_V3_3 (0x23) +#define PFT_ARCH_V1_1 (0x31) + +#define TIMEOUT_US (100) +#define CS_UNLOCK_MAGIC (0xC5ACCE55) + +#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb)) +#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb) +#define BVAL(val, n) ((val & BIT(n)) >> n) + +void etb_enable(void); +void etb_disable(void); +void etb_dump(void); +void tpiu_disable(void); +void funnel_enable(uint8_t id, uint32_t port_mask); +void funnel_disable(uint8_t id, uint32_t port_mask); + +struct kobject *qdss_get_modulekobj(void); + +#endif diff --git a/arch/arm/mach-msm/qdss-tpiu.c b/arch/arm/mach-msm/qdss-tpiu.c new file mode 100644 index 00000000000..fa156356027 --- /dev/null +++ b/arch/arm/mach-msm/qdss-tpiu.c @@ -0,0 +1,141 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "qdss-priv.h" + +#define tpiu_writel(tpiu, val, off) __raw_writel((val), tpiu.base + off) +#define tpiu_readl(tpiu, off) __raw_readl(tpiu.base + off) + +#define TPIU_SUPP_PORTSZ (0x000) +#define TPIU_CURR_PORTSZ (0x004) +#define TPIU_SUPP_TRIGMODES (0x100) +#define TPIU_TRIG_CNTRVAL (0x104) +#define TPIU_TRIG_MULT (0x108) +#define TPIU_SUPP_TESTPATM (0x200) +#define TPIU_CURR_TESTPATM (0x204) +#define TPIU_TEST_PATREPCNTR (0x208) +#define TPIU_FFSR (0x300) +#define TPIU_FFCR (0x304) +#define TPIU_FSYNC_CNTR (0x308) +#define TPIU_EXTCTL_INPORT (0x400) +#define TPIU_EXTCTL_OUTPORT (0x404) +#define TPIU_ITTRFLINACK (0xEE4) +#define TPIU_ITTRFLIN (0xEE8) +#define TPIU_ITATBDATA0 (0xEEC) +#define TPIU_ITATBCTR2 (0xEF0) +#define TPIU_ITATBCTR1 (0xEF4) +#define TPIU_ITATBCTR0 (0xEF8) + + +#define TPIU_LOCK() \ +do { \ + mb(); \ + tpiu_writel(tpiu, 0x0, CS_LAR); \ +} while (0) +#define TPIU_UNLOCK() \ +do { \ + tpiu_writel(tpiu, CS_UNLOCK_MAGIC, CS_LAR); \ + mb(); \ +} while (0) + +struct tpiu_ctx { + void __iomem *base; + bool enabled; + struct device *dev; +}; + +static struct tpiu_ctx tpiu; + +static void __tpiu_disable(void) +{ + TPIU_UNLOCK(); + + tpiu_writel(tpiu, 0x3000, TPIU_FFCR); + tpiu_writel(tpiu, 0x3040, TPIU_FFCR); + + TPIU_LOCK(); +} + +void tpiu_disable(void) +{ + __tpiu_disable(); + tpiu.enabled = false; + dev_info(tpiu.dev, "TPIU disabled\n"); +} + +static int __devinit tpiu_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -EINVAL; + goto err_res; + } + + tpiu.base = ioremap_nocache(res->start, resource_size(res)); + if (!tpiu.base) { + ret = -EINVAL; + goto err_ioremap; + } + + tpiu.dev = &pdev->dev; + + dev_info(tpiu.dev, "TPIU initialized\n"); + return 0; + +err_ioremap: +err_res: + dev_err(tpiu.dev, "TPIU init failed\n"); + return ret; +} + +static int __devexit tpiu_remove(struct platform_device *pdev) +{ + if (tpiu.enabled) + tpiu_disable(); + iounmap(tpiu.base); + + return 0; +} + +static struct platform_driver tpiu_driver = { + .probe = tpiu_probe, + .remove = __devexit_p(tpiu_remove), + .driver = { + .name = "msm_tpiu", + }, +}; + +static int __init tpiu_init(void) +{ + return platform_driver_register(&tpiu_driver); +} +module_init(tpiu_init); + +static void __exit tpiu_exit(void) +{ + platform_driver_unregister(&tpiu_driver); +} +module_exit(tpiu_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver"); diff --git a/arch/arm/mach-msm/qdss.c b/arch/arm/mach-msm/qdss.c new file mode 100644 index 00000000000..fd1fc2b21ab --- /dev/null +++ b/arch/arm/mach-msm/qdss.c @@ -0,0 +1,408 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpm_resources.h" +#include "qdss-priv.h" + +#define MAX_STR_LEN (65535) + +enum { + QDSS_CLK_OFF, + QDSS_CLK_ON_DBG, + QDSS_CLK_ON_HSDBG, +}; + +/* + * Exclusion rules for structure fields. + * + * S: qdss.sources_mutex protected. + * I: qdss.sink_mutex protected. + * C: qdss.clk_mutex protected. + */ +struct qdss_ctx { + struct kobject *modulekobj; + struct msm_qdss_platform_data *pdata; + struct list_head sources; /* S: sources list */ + struct mutex sources_mutex; + uint8_t sink_count; /* I: sink count */ + struct mutex sink_mutex; + uint8_t max_clk; + uint8_t clk_count; /* C: clk count */ + struct mutex clk_mutex; +}; + +static struct qdss_ctx qdss; + +/** + * qdss_get - get the qdss source handle + * @name: name of the qdss source + * + * Searches the sources list to get the qdss source handle for this source. + * + * CONTEXT: + * Typically called from init or probe functions + * + * RETURNS: + * pointer to struct qdss_source on success, %NULL on failure + */ +struct qdss_source *qdss_get(const char *name) +{ + struct qdss_source *src, *source = NULL; + + mutex_lock(&qdss.sources_mutex); + list_for_each_entry(src, &qdss.sources, link) { + if (src->name) { + if (strncmp(src->name, name, MAX_STR_LEN)) + continue; + source = src; + break; + } + } + mutex_unlock(&qdss.sources_mutex); + + return source ? source : ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(qdss_get); + +/** + * qdss_put - release the qdss source handle + * @name: name of the qdss source + * + * CONTEXT: + * Typically called from driver remove or exit functions + */ +void qdss_put(struct qdss_source *src) +{ +} +EXPORT_SYMBOL(qdss_put); + +/** + * qdss_enable - enable qdss for the source + * @src: handle for the source making the call + * + * Enables qdss block (relevant funnel ports and sink) if not already + * enabled, otherwise increments the reference count + * + * CONTEXT: + * Might sleep. Uses a mutex lock. Should be called from a non-atomic context. + * + * RETURNS: + * 0 on success, non-zero on failure + */ +int qdss_enable(struct qdss_source *src) +{ + int ret; + + if (!src) + return -EINVAL; + + ret = qdss_clk_enable(); + if (ret) + goto err; + + if ((qdss.pdata)->afamily) { + mutex_lock(&qdss.sink_mutex); + if (qdss.sink_count == 0) { + etb_disable(); + tpiu_disable(); + /* enable ETB first to avoid losing any trace data */ + etb_enable(); + } + qdss.sink_count++; + mutex_unlock(&qdss.sink_mutex); + } + + funnel_enable(0x0, src->fport_mask); + return 0; +err: + return ret; +} +EXPORT_SYMBOL(qdss_enable); + +/** + * qdss_disable - disable qdss for the source + * @src: handle for the source making the call + * + * Disables qdss block (relevant funnel ports and sink) if the reference count + * is one, otherwise decrements the reference count + * + * CONTEXT: + * Might sleep. Uses a mutex lock. Should be called from a non-atomic context. + */ +void qdss_disable(struct qdss_source *src) +{ + if (!src) + return; + + if ((qdss.pdata)->afamily) { + mutex_lock(&qdss.sink_mutex); + if (WARN(qdss.sink_count == 0, "qdss is unbalanced\n")) + goto out; + if (qdss.sink_count == 1) { + etb_dump(); + etb_disable(); + } + qdss.sink_count--; + mutex_unlock(&qdss.sink_mutex); + } + + funnel_disable(0x0, src->fport_mask); + qdss_clk_disable(); + return; +out: + mutex_unlock(&qdss.sink_mutex); +} +EXPORT_SYMBOL(qdss_disable); + +/** + * qdss_disable_sink - force disable the current qdss sink(s) + * + * Force disable the current qdss sink(s) to stop the sink from accepting any + * trace generated subsequent to this call. This function should only be used + * as a way to stop the sink from getting polluted with trace data that is + * uninteresting after an event of interest has occured. + * + * CONTEXT: + * Can be called from atomic or non-atomic context. + */ +void qdss_disable_sink(void) +{ + if ((qdss.pdata)->afamily) { + etb_dump(); + etb_disable(); + } +} +EXPORT_SYMBOL(qdss_disable_sink); + +/** + * qdss_clk_enable - enable qdss clocks + * + * Enables qdss clocks via RPM if they aren't already enabled, otherwise + * increments the reference count. + * + * CONTEXT: + * Might sleep. Uses a mutex lock. Should be called from a non-atomic context. + * + * RETURNS: + * 0 on success, non-zero on failure + */ +int qdss_clk_enable(void) +{ + int ret; + struct msm_rpm_iv_pair iv; + + mutex_lock(&qdss.clk_mutex); + if (qdss.clk_count == 0) { + iv.id = MSM_RPM_ID_QDSS_CLK; + if (qdss.max_clk) + iv.value = QDSS_CLK_ON_HSDBG; + else + iv.value = QDSS_CLK_ON_DBG; + ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1); + if (WARN(ret, "qdss clks not enabled (%d)\n", ret)) + goto err_clk; + } + qdss.clk_count++; + mutex_unlock(&qdss.clk_mutex); + return 0; +err_clk: + mutex_unlock(&qdss.clk_mutex); + return ret; +} +EXPORT_SYMBOL(qdss_clk_enable); + +/** + * qdss_clk_disable - disable qdss clocks + * + * Disables qdss clocks via RPM if the reference count is one, otherwise + * decrements the reference count. + * + * CONTEXT: + * Might sleep. Uses a mutex lock. Should be called from a non-atomic context. + */ +void qdss_clk_disable(void) +{ + int ret; + struct msm_rpm_iv_pair iv; + + mutex_lock(&qdss.clk_mutex); + if (WARN(qdss.clk_count == 0, "qdss clks are unbalanced\n")) + goto out; + if (qdss.clk_count == 1) { + iv.id = MSM_RPM_ID_QDSS_CLK; + iv.value = QDSS_CLK_OFF; + ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1); + WARN(ret, "qdss clks not disabled (%d)\n", ret); + } + qdss.clk_count--; +out: + mutex_unlock(&qdss.clk_mutex); +} +EXPORT_SYMBOL(qdss_clk_disable); + +struct kobject *qdss_get_modulekobj(void) +{ + return qdss.modulekobj; +} + +#define QDSS_ATTR(name) \ +static struct kobj_attribute name##_attr = \ + __ATTR(name, S_IRUGO | S_IWUSR, name##_show, name##_store) + +static ssize_t max_clk_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (sscanf(buf, "%lx", &val) != 1) + return -EINVAL; + + qdss.max_clk = val; + return n; +} +static ssize_t max_clk_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long val = qdss.max_clk; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +QDSS_ATTR(max_clk); + +static void __devinit qdss_add_sources(struct qdss_source *srcs, size_t num) +{ + mutex_lock(&qdss.sources_mutex); + while (num--) { + list_add_tail(&srcs->link, &qdss.sources); + srcs++; + } + mutex_unlock(&qdss.sources_mutex); +} + +static int __init qdss_sysfs_init(void) +{ + int ret; + + qdss.modulekobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!qdss.modulekobj) { + pr_err("failed to find QDSS sysfs module kobject\n"); + ret = -ENOENT; + goto err; + } + + ret = sysfs_create_file(qdss.modulekobj, &max_clk_attr.attr); + if (ret) { + pr_err("failed to create QDSS sysfs max_clk attribute\n"); + goto err; + } + + return 0; +err: + return ret; +} + +static void __devexit qdss_sysfs_exit(void) +{ + sysfs_remove_file(qdss.modulekobj, &max_clk_attr.attr); +} + +static int __devinit qdss_probe(struct platform_device *pdev) +{ + int ret; + struct qdss_source *src_table; + size_t num_srcs; + + mutex_init(&qdss.sources_mutex); + mutex_init(&qdss.clk_mutex); + mutex_init(&qdss.sink_mutex); + + if (pdev->dev.platform_data == NULL) { + pr_err("%s: platform data is NULL\n", __func__); + ret = -ENODEV; + goto err_pdata; + } + qdss.pdata = pdev->dev.platform_data; + + INIT_LIST_HEAD(&qdss.sources); + src_table = (qdss.pdata)->src_table; + num_srcs = (qdss.pdata)->size; + qdss_add_sources(src_table, num_srcs); + + pr_info("QDSS arch initialized\n"); + return 0; +err_pdata: + mutex_destroy(&qdss.sink_mutex); + mutex_destroy(&qdss.clk_mutex); + mutex_destroy(&qdss.sources_mutex); + pr_err("QDSS init failed\n"); + return ret; +} + +static int __devexit qdss_remove(struct platform_device *pdev) +{ + qdss_sysfs_exit(); + mutex_destroy(&qdss.sink_mutex); + mutex_destroy(&qdss.clk_mutex); + mutex_destroy(&qdss.sources_mutex); + + return 0; +} + +static struct platform_driver qdss_driver = { + .probe = qdss_probe, + .remove = __devexit_p(qdss_remove), + .driver = { + .name = "msm_qdss", + }, +}; + +static int __init qdss_init(void) +{ + return platform_driver_register(&qdss_driver); +} +arch_initcall(qdss_init); + +static int __init qdss_module_init(void) +{ + int ret; + + ret = qdss_sysfs_init(); + if (ret) + goto err_sysfs; + + pr_info("QDSS module initialized\n"); + return 0; +err_sysfs: + return ret; +} +module_init(qdss_module_init); + +static void __exit qdss_exit(void) +{ + platform_driver_unregister(&qdss_driver); +} +module_exit(qdss_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Debug SubSystem Driver"); diff --git a/arch/arm/mach-msm/ramdump.c b/arch/arm/mach-msm/ramdump.c new file mode 100644 index 00000000000..a18acd6706e --- /dev/null +++ b/arch/arm/mach-msm/ramdump.c @@ -0,0 +1,263 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ramdump.h" + +#define RAMDUMP_WAIT_MSECS 120000 + +struct ramdump_device { + char name[256]; + + unsigned int data_ready; + unsigned int consumer_present; + int ramdump_status; + + struct completion ramdump_complete; + struct miscdevice device; + + wait_queue_head_t dump_wait_q; + int nsegments; + struct ramdump_segment *segments; +}; + +static int ramdump_open(struct inode *inode, struct file *filep) +{ + struct ramdump_device *rd_dev = container_of(filep->private_data, + struct ramdump_device, device); + rd_dev->consumer_present = 1; + rd_dev->ramdump_status = 0; + return 0; +} + +static int ramdump_release(struct inode *inode, struct file *filep) +{ + struct ramdump_device *rd_dev = container_of(filep->private_data, + struct ramdump_device, device); + rd_dev->consumer_present = 0; + rd_dev->data_ready = 0; + complete(&rd_dev->ramdump_complete); + return 0; +} + +static unsigned long offset_translate(loff_t user_offset, + struct ramdump_device *rd_dev, unsigned long *data_left) +{ + int i = 0; + + for (i = 0; i < rd_dev->nsegments; i++) + if (user_offset >= rd_dev->segments[i].size) + user_offset -= rd_dev->segments[i].size; + else + break; + + if (i == rd_dev->nsegments) { + pr_debug("Ramdump(%s): offset_translate returning zero\n", + rd_dev->name); + *data_left = 0; + return 0; + } + + *data_left = rd_dev->segments[i].size - user_offset; + + pr_debug("Ramdump(%s): Returning address: %llx, data_left = %ld\n", + rd_dev->name, rd_dev->segments[i].address + user_offset, + *data_left); + + return rd_dev->segments[i].address + user_offset; +} + +#define MAX_IOREMAP_SIZE SZ_1M + +static int ramdump_read(struct file *filep, char __user *buf, size_t count, + loff_t *pos) +{ + struct ramdump_device *rd_dev = container_of(filep->private_data, + struct ramdump_device, device); + void *device_mem = NULL; + unsigned long data_left = 0; + unsigned long addr = 0; + size_t copy_size = 0; + int ret = 0; + + if (rd_dev->data_ready == 0) { + pr_err("Ramdump(%s): Read when there's no dump available!", + rd_dev->name); + return -EPIPE; + } + + addr = offset_translate(*pos, rd_dev, &data_left); + + /* EOF check */ + if (data_left == 0) { + pr_debug("Ramdump(%s): Ramdump complete. %lld bytes read.", + rd_dev->name, *pos); + rd_dev->ramdump_status = 0; + ret = 0; + goto ramdump_done; + } + + copy_size = min(count, (size_t)MAX_IOREMAP_SIZE); + copy_size = min((unsigned long)copy_size, data_left); + device_mem = ioremap_nocache(addr, copy_size); + + if (device_mem == NULL) { + pr_err("Ramdump(%s): Unable to ioremap: addr %lx, size %x\n", + rd_dev->name, addr, copy_size); + rd_dev->ramdump_status = -1; + ret = -ENOMEM; + goto ramdump_done; + } + + if (copy_to_user(buf, device_mem, copy_size)) { + pr_err("Ramdump(%s): Couldn't copy all data to user.", + rd_dev->name); + iounmap(device_mem); + rd_dev->ramdump_status = -1; + ret = -EFAULT; + goto ramdump_done; + } + + iounmap(device_mem); + *pos += copy_size; + + pr_debug("Ramdump(%s): Read %d bytes from address %lx.", + rd_dev->name, copy_size, addr); + + return copy_size; + +ramdump_done: + rd_dev->data_ready = 0; + *pos = 0; + complete(&rd_dev->ramdump_complete); + return ret; +} + +static unsigned int ramdump_poll(struct file *filep, + struct poll_table_struct *wait) +{ + struct ramdump_device *rd_dev = container_of(filep->private_data, + struct ramdump_device, device); + unsigned int mask = 0; + + if (rd_dev->data_ready) + mask |= (POLLIN | POLLRDNORM); + + poll_wait(filep, &rd_dev->dump_wait_q, wait); + return mask; +} + +const struct file_operations ramdump_file_ops = { + .open = ramdump_open, + .release = ramdump_release, + .read = ramdump_read, + .poll = ramdump_poll +}; + +void *create_ramdump_device(const char *dev_name) +{ + int ret; + struct ramdump_device *rd_dev; + + if (!dev_name) { + pr_err("%s: Invalid device name.\n", __func__); + return NULL; + } + + rd_dev = kzalloc(sizeof(struct ramdump_device), GFP_KERNEL); + + if (!rd_dev) { + pr_err("%s: Couldn't alloc space for ramdump device!", + __func__); + return NULL; + } + + snprintf(rd_dev->name, ARRAY_SIZE(rd_dev->name), "ramdump_%s", + dev_name); + + init_completion(&rd_dev->ramdump_complete); + + rd_dev->device.minor = MISC_DYNAMIC_MINOR; + rd_dev->device.name = rd_dev->name; + rd_dev->device.fops = &ramdump_file_ops; + + init_waitqueue_head(&rd_dev->dump_wait_q); + + ret = misc_register(&rd_dev->device); + + if (ret) { + pr_err("%s: misc_register failed for %s (%d)", __func__, + dev_name, ret); + kfree(rd_dev); + return NULL; + } + + return (void *)rd_dev; +} + +int do_ramdump(void *handle, struct ramdump_segment *segments, + int nsegments) +{ + int ret, i; + struct ramdump_device *rd_dev = (struct ramdump_device *)handle; + + if (!rd_dev->consumer_present) { + pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name); + return -EPIPE; + } + + for (i = 0; i < nsegments; i++) + segments[i].size = PAGE_ALIGN(segments[i].size); + + rd_dev->segments = segments; + rd_dev->nsegments = nsegments; + + rd_dev->data_ready = 1; + rd_dev->ramdump_status = -1; + + INIT_COMPLETION(rd_dev->ramdump_complete); + + /* Tell userspace that the data is ready */ + wake_up(&rd_dev->dump_wait_q); + + /* Wait (with a timeout) to let the ramdump complete */ + ret = wait_for_completion_timeout(&rd_dev->ramdump_complete, + msecs_to_jiffies(RAMDUMP_WAIT_MSECS)); + + if (!ret) { + pr_err("Ramdump(%s): Timed out waiting for userspace.\n", + rd_dev->name); + ret = -EPIPE; + } else + ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE; + + rd_dev->data_ready = 0; + return ret; +} diff --git a/arch/arm/mach-msm/ramdump.h b/arch/arm/mach-msm/ramdump.h new file mode 100644 index 00000000000..0b60a4464bb --- /dev/null +++ b/arch/arm/mach-msm/ramdump.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _RAMDUMP_HEADER +#define _RAMDUMP_HEADER + +struct ramdump_segment { + unsigned long address; + unsigned long size; +}; + +void *create_ramdump_device(const char *dev_name); +int do_ramdump(void *handle, struct ramdump_segment *segments, + int nsegments); + +#endif diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c new file mode 100644 index 00000000000..2480433553e --- /dev/null +++ b/arch/arm/mach-msm/remote_spinlock.c @@ -0,0 +1,192 @@ +/* Copyright (c) 2008-2009, 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "smd_private.h" +#include + +static void remote_spin_release_all_locks(uint32_t pid, int count); + +#if defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB) +#define SFPB_SPINLOCK_COUNT 8 +#define MSM_SFPB_MUTEX_REG_BASE 0x01200600 +#define MSM_SFPB_MUTEX_REG_SIZE (33 * 4) + +static void *hw_mutex_reg_base; +static DEFINE_MUTEX(hw_map_init_lock); + +static int remote_spinlock_init_address(int id, _remote_spinlock_t *lock) +{ + if (id >= SFPB_SPINLOCK_COUNT) + return -EINVAL; + + if (!hw_mutex_reg_base) { + mutex_lock(&hw_map_init_lock); + if (!hw_mutex_reg_base) + hw_mutex_reg_base = ioremap(MSM_SFPB_MUTEX_REG_BASE, + MSM_SFPB_MUTEX_REG_SIZE); + mutex_unlock(&hw_map_init_lock); + BUG_ON(hw_mutex_reg_base == NULL); + } + + *lock = hw_mutex_reg_base + 0x4 + id * 4; + return 0; +} + +void _remote_spin_release_all(uint32_t pid) +{ + remote_spin_release_all_locks(pid, SFPB_SPINLOCK_COUNT); +} + +#else +#define SMEM_SPINLOCK_COUNT 8 +#define SMEM_SPINLOCK_ARRAY_SIZE (SMEM_SPINLOCK_COUNT * sizeof(uint32_t)) + +static int remote_spinlock_init_address(int id, _remote_spinlock_t *lock) +{ + _remote_spinlock_t spinlock_start; + + if (id >= SMEM_SPINLOCK_COUNT) + return -EINVAL; + + spinlock_start = smem_alloc(SMEM_SPINLOCK_ARRAY, + SMEM_SPINLOCK_ARRAY_SIZE); + if (spinlock_start == NULL) + return -ENXIO; + + *lock = spinlock_start + id; + + return 0; +} + +void _remote_spin_release_all(uint32_t pid) +{ + remote_spin_release_all_locks(pid, SMEM_SPINLOCK_COUNT); +} + +#endif + +/** + * Release all spinlocks owned by @pid. + * + * This is only to be used for situations where the processor owning + * spinlocks has crashed and the spinlocks must be released. + * + * @pid - processor ID of processor to release + */ +static void remote_spin_release_all_locks(uint32_t pid, int count) +{ + int n; + _remote_spinlock_t lock; + + for (n = 0; n < count; ++n) { + if (remote_spinlock_init_address(n, &lock) == 0) + _remote_spin_release(&lock, pid); + } +} + +static int +remote_spinlock_dal_init(const char *chunk_name, _remote_spinlock_t *lock) +{ + void *dal_smem_start, *dal_smem_end; + uint32_t dal_smem_size; + struct dal_chunk_header *cur_header; + + if (!chunk_name) + return -EINVAL; + + dal_smem_start = smem_get_entry(SMEM_DAL_AREA, &dal_smem_size); + if (!dal_smem_start) + return -ENXIO; + + dal_smem_end = dal_smem_start + dal_smem_size; + + /* Find first chunk header */ + cur_header = (struct dal_chunk_header *) + (((uint32_t)dal_smem_start + (4095)) & ~4095); + *lock = NULL; + while (cur_header->size != 0 + && ((uint32_t)(cur_header + 1) < (uint32_t)dal_smem_end)) { + + /* Check if chunk name matches */ + if (!strncmp(cur_header->name, chunk_name, + DAL_CHUNK_NAME_LENGTH)) { + *lock = (_remote_spinlock_t)&cur_header->lock; + return 0; + } + cur_header = (void *)cur_header + cur_header->size; + } + + pr_err("%s: DAL remote lock \"%s\" not found.\n", __func__, + chunk_name); + return -EINVAL; +} + +int _remote_spin_lock_init(remote_spinlock_id_t id, _remote_spinlock_t *lock) +{ + BUG_ON(id == NULL); + + if (id[0] == 'D' && id[1] == ':') { + /* DAL chunk name starts after "D:" */ + return remote_spinlock_dal_init(&id[2], lock); + } else if (id[0] == 'S' && id[1] == ':') { + /* Single-digit lock ID follows "S:" */ + BUG_ON(id[3] != '\0'); + + return remote_spinlock_init_address((((uint8_t)id[2])-'0'), + lock); + } else { + return -EINVAL; + } +} + +int _remote_mutex_init(struct remote_mutex_id *id, _remote_mutex_t *lock) +{ + BUG_ON(id == NULL); + + lock->delay_us = id->delay_us; + return _remote_spin_lock_init(id->r_spinlock_id, &(lock->r_spinlock)); +} +EXPORT_SYMBOL(_remote_mutex_init); + +void _remote_mutex_lock(_remote_mutex_t *lock) +{ + while (!_remote_spin_trylock(&(lock->r_spinlock))) { + if (lock->delay_us >= 1000) + msleep(lock->delay_us/1000); + else + udelay(lock->delay_us); + } +} +EXPORT_SYMBOL(_remote_mutex_lock); + +void _remote_mutex_unlock(_remote_mutex_t *lock) +{ + _remote_spin_unlock(&(lock->r_spinlock)); +} +EXPORT_SYMBOL(_remote_mutex_unlock); + +int _remote_mutex_trylock(_remote_mutex_t *lock) +{ + return _remote_spin_trylock(&(lock->r_spinlock)); +} +EXPORT_SYMBOL(_remote_mutex_trylock); diff --git a/arch/arm/mach-msm/reset_modem.c b/arch/arm/mach-msm/reset_modem.c new file mode 100644 index 00000000000..8e92456ae4a --- /dev/null +++ b/arch/arm/mach-msm/reset_modem.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * MSM architecture driver to reset the modem + */ +#include +#include +#include +#include +#include + +#include "smd_private.h" + +#define DEBUG +/* #undef DEBUG */ +#ifdef DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +static ssize_t reset_modem_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + return 0; +} + +static ssize_t reset_modem_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + unsigned char cmd[64]; + int len; + int time; + int zero = 0; + int r; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "wait", 4)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: WAIT\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem(SMSM_MODEM_WAIT); + } else if (!strncmp(cmd, "continue", 8)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: CONTINUE\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem_cont(); + } else if (!strncmp(cmd, "download", 8)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: DOWNLOAD\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem(SMSM_SYSTEM_DOWNLOAD); + } else if (sscanf(cmd, "deferred reset %i", &time) == 1) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: DEFERRED RESET %ims\n", + __FILE__, + __LINE__, + __func__, + time); + if (time == 0) { + r = 0; + msm_proc_comm_reset_modem_now(); + } else { + r = msm_proc_comm(PCOM_RESET_MODEM, &time, &zero); + } + if (r < 0) + return r; + } else if (!strncmp(cmd, "deferred reset", 14)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: DEFERRED RESET 0ms\n", + __FILE__, + __LINE__, + __func__); + r = 0; + msm_proc_comm_reset_modem_now(); + if (r < 0) + return r; + } else if (!strncmp(cmd, "reset chip now", 14)) { + uint param1 = 0x0; + uint param2 = 0x0; + + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: CHIP RESET IMMEDIATE\n", + __FILE__, + __LINE__, + __func__); + + r = msm_proc_comm(PCOM_RESET_CHIP_IMM, ¶m1, ¶m2); + + if (r < 0) + return r; + } else if (!strncmp(cmd, "reset chip", 10)) { + + uint param1 = 0x0; + uint param2 = 0x0; + + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: CHIP RESET \n", + __FILE__, + __LINE__, + __func__); + + r = msm_proc_comm(PCOM_RESET_CHIP, ¶m1, ¶m2); + + if (r < 0) + return r; + } else if (!strncmp(cmd, "reset", 5)) { + printk(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: RESET\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem(SMSM_RESET); + } else + return -EINVAL; + + return count; +} + +static int reset_modem_open(struct inode *ip, struct file *fp) +{ + return 0; +} + +static int reset_modem_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static const struct file_operations reset_modem_fops = { + .owner = THIS_MODULE, + .read = reset_modem_read, + .write = reset_modem_write, + .open = reset_modem_open, + .release = reset_modem_release, +}; + +static struct miscdevice reset_modem_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "reset_modem", + .fops = &reset_modem_fops, +}; + +static int __init reset_modem_init(void) +{ + return misc_register(&reset_modem_dev); +} + +module_init(reset_modem_init); + +MODULE_DESCRIPTION("Reset Modem"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/restart-fsm9xxx.c b/arch/arm/mach-msm/restart-fsm9xxx.c new file mode 100644 index 00000000000..4c5892f2586 --- /dev/null +++ b/arch/arm/mach-msm/restart-fsm9xxx.c @@ -0,0 +1,42 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FEMTO_GPIO_PS_HOLD 161 + +void fsm_restart(char mode, const char *cmd) +{ + pr_notice("Going down for restart now\n"); + msleep(3000); + + /* Configure FEMTO_GPIO_PS_HOLD as a general purpose output */ + if (gpio_tlmm_config(GPIO_CFG(FEMTO_GPIO_PS_HOLD, 0, + GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, + GPIO_CFG_2MA), GPIO_CFG_ENABLE)) + pr_err("%s: gpio_tlmm_config (gpio=%d) failed\n", + __func__, FEMTO_GPIO_PS_HOLD); + + /* Now set it low to power cycle the entire board */ + gpio_set_value(FEMTO_GPIO_PS_HOLD, 0); + + msleep(10000); + pr_err("Restarting has failed\n"); +} diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c new file mode 100644 index 00000000000..7288c1d67d6 --- /dev/null +++ b/arch/arm/mach-msm/restart.c @@ -0,0 +1,267 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include "msm_watchdog.h" +#include "timer.h" + +#define WDT0_RST 0x38 +#define WDT0_EN 0x40 +#define WDT0_BARK_TIME 0x4C +#define WDT0_BITE_TIME 0x5C + +#define PSHOLD_CTL_SU (MSM_TLMM_BASE + 0x820) + +#define RESTART_REASON_ADDR 0x65C +#define DLOAD_MODE_ADDR 0x0 + +#define SCM_IO_DISABLE_PMIC_ARBITER 1 + +static int restart_mode; +void *restart_reason; + +int pmic_reset_irq; +static void __iomem *msm_tmr0_base; + +#ifdef CONFIG_MSM_DLOAD_MODE +static int in_panic; +static void *dload_mode_addr; + +/* Download mode master kill-switch */ +static int dload_set(const char *val, struct kernel_param *kp); +static int download_mode = 1; +module_param_call(download_mode, dload_set, param_get_int, + &download_mode, 0644); + +static int panic_prep_restart(struct notifier_block *this, + unsigned long event, void *ptr) +{ + in_panic = 1; + return NOTIFY_DONE; +} + +static struct notifier_block panic_blk = { + .notifier_call = panic_prep_restart, +}; + +static void set_dload_mode(int on) +{ + if (dload_mode_addr) { + __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr); + __raw_writel(on ? 0xCE14091A : 0, + dload_mode_addr + sizeof(unsigned int)); + mb(); + } +} + +static int dload_set(const char *val, struct kernel_param *kp) +{ + int ret; + int old_val = download_mode; + + ret = param_set_int(val, kp); + + if (ret) + return ret; + + /* If download_mode is not zero or one, ignore. */ + if (download_mode >> 1) { + download_mode = old_val; + return -EINVAL; + } + + set_dload_mode(download_mode); + + return 0; +} +#else +#define set_dload_mode(x) do {} while (0) +#endif + +void msm_set_restart_mode(int mode) +{ + restart_mode = mode; +} +EXPORT_SYMBOL(msm_set_restart_mode); + +static void __msm_power_off(int lower_pshold) +{ + printk(KERN_CRIT "Powering off the SoC\n"); +#ifdef CONFIG_MSM_DLOAD_MODE + set_dload_mode(0); +#endif + pm8xxx_reset_pwr_off(0); + + if (lower_pshold) { + __raw_writel(0, PSHOLD_CTL_SU); + mdelay(10000); + printk(KERN_ERR "Powering off has failed\n"); + } + return; +} + +static void msm_power_off(void) +{ + /* MSM initiated power off, lower ps_hold */ + __msm_power_off(1); +} + +static void cpu_power_off(void *data) +{ + int rc; + + pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__, + smp_processor_id()); + if (smp_processor_id() == 0) { + /* + * PMIC initiated power off, do not lower ps_hold, pmic will + * shut msm down + */ + __msm_power_off(0); + + pet_watchdog(); + pr_err("Calling scm to disable arbiter\n"); + /* call secure manager to disable arbiter and never return */ + rc = scm_call_atomic1(SCM_SVC_PWR, + SCM_IO_DISABLE_PMIC_ARBITER, 1); + + pr_err("SCM returned even when asked to busy loop rc=%d\n", rc); + pr_err("waiting on pmic to shut msm down\n"); + } + + preempt_disable(); + while (1) + ; +} + +static irqreturn_t resout_irq_handler(int irq, void *dev_id) +{ + pr_warn("%s PMIC Initiated shutdown\n", __func__); + oops_in_progress = 1; + smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0); + if (smp_processor_id() == 0) + cpu_power_off(NULL); + preempt_disable(); + while (1) + ; + return IRQ_HANDLED; +} + +void msm_restart(char mode, const char *cmd) +{ + +#ifdef CONFIG_MSM_DLOAD_MODE + + /* This looks like a normal reboot at this point. */ + set_dload_mode(0); + + /* Write download mode flags if we're panic'ing */ + set_dload_mode(in_panic); + + /* Write download mode flags if restart_mode says so */ + if (restart_mode == RESTART_DLOAD) + set_dload_mode(1); + + /* Kill download mode if master-kill switch is set */ + if (!download_mode) + set_dload_mode(0); +#endif + + printk(KERN_NOTICE "Going down for restart now\n"); + + pm8xxx_reset_pwr_off(1); + + if (cmd != NULL) { + if (!strncmp(cmd, "bootloader", 10)) { + __raw_writel(0x77665500, restart_reason); + } else if (!strncmp(cmd, "recovery", 8)) { + __raw_writel(0x77665502, restart_reason); + } else if (!strncmp(cmd, "oem-", 4)) { + unsigned long code; + code = simple_strtoul(cmd + 4, NULL, 16) & 0xff; + __raw_writel(0x6f656d00 | code, restart_reason); + } else { + __raw_writel(0x77665501, restart_reason); + } + } + + __raw_writel(0, msm_tmr0_base + WDT0_EN); + if (!(machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())) { + mb(); + __raw_writel(0, PSHOLD_CTL_SU); /* Actually reset the chip */ + mdelay(5000); + pr_notice("PS_HOLD didn't work, falling back to watchdog\n"); + } + + __raw_writel(1, msm_tmr0_base + WDT0_RST); + __raw_writel(5*0x31F3, msm_tmr0_base + WDT0_BARK_TIME); + __raw_writel(0x31F3, msm_tmr0_base + WDT0_BITE_TIME); + __raw_writel(1, msm_tmr0_base + WDT0_EN); + + mdelay(10000); + printk(KERN_ERR "Restarting has failed\n"); +} + +static int __init msm_pmic_restart_init(void) +{ + int rc; + + if (pmic_reset_irq != 0) { + rc = request_any_context_irq(pmic_reset_irq, + resout_irq_handler, IRQF_TRIGGER_HIGH, + "restart_from_pmic", NULL); + if (rc < 0) + pr_err("pmic restart irq fail rc = %d\n", rc); + } else { + pr_warn("no pmic restart interrupt specified\n"); + } + + return 0; +} + +late_initcall(msm_pmic_restart_init); + +static int __init msm_restart_init(void) +{ +#ifdef CONFIG_MSM_DLOAD_MODE + atomic_notifier_chain_register(&panic_notifier_list, &panic_blk); + dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR; + set_dload_mode(download_mode); +#endif + msm_tmr0_base = msm_timer_get_timer0_base(); + restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR; + pm_power_off = msm_power_off; + + return 0; +} +early_initcall(msm_restart_init); diff --git a/arch/arm/mach-msm/rfic-fsm9xxx.c b/arch/arm/mach-msm/rfic-fsm9xxx.c new file mode 100644 index 00000000000..32b654bfac3 --- /dev/null +++ b/arch/arm/mach-msm/rfic-fsm9xxx.c @@ -0,0 +1,397 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * FTR8700 RFIC + */ + +#define RFIC_FTR_DEVICE_NUM 2 +#define RFIC_GRFC_REG_NUM 6 + +#define ANY_BUS 0x0 +#define TX1_BUS 0x0 +#define TX2_BUS 0x1 +#define MISC_BUS 0x2 +#define RX_BUS 0x3 +#define BUS_BITS 0x3 + +/* + * Device private information per device node + */ + +static struct ftr_dev_node_info { + void *grfcCtrlAddr; + void *grfcMaskAddr; + unsigned int busSelect[4]; + struct i2c_adapter *ssbi_adap; + + /* lock */ + struct mutex lock; +} ftr_dev_info[RFIC_FTR_DEVICE_NUM]; + +/* + * Device private information per file + */ + +struct ftr_dev_file_info { + int ftrId; +}; + +/* + * File interface + */ + +static int ftr_find_id(int minor); + +static int ftr_open(struct inode *inode, struct file *file) +{ + struct ftr_dev_file_info *pdfi; + + /* private data allocation */ + pdfi = kmalloc(sizeof(*pdfi), GFP_KERNEL); + if (pdfi == NULL) + return -ENOMEM; + file->private_data = pdfi; + + /* FTR ID */ + pdfi->ftrId = ftr_find_id(MINOR(inode->i_rdev)); + + return 0; +} + +static int ftr_release(struct inode *inode, struct file *file) +{ + struct ftr_dev_file_info *pdfi; + + pdfi = (struct ftr_dev_file_info *) file->private_data; + + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static ssize_t ftr_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + return 0; +} + +static ssize_t ftr_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static int ftr_ssbi_read( + struct ftr_dev_node_info *pdev, + unsigned int addr, + u8 *buf, + size_t len) +{ + int ret; + struct i2c_msg msg = { + .addr = addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len, + }; + + ret = i2c_transfer(pdev->ssbi_adap, &msg, 1); + + return (ret == 1) ? 0 : ret; +} + +static int ftr_ssbi_write( + struct ftr_dev_node_info *pdev, + unsigned int addr, + u8 *buf, + size_t len) +{ + int ret; + struct i2c_msg msg = { + .addr = addr, + .flags = 0x0, + .buf = (u8 *) buf, + .len = len, + }; + + ret = i2c_transfer(pdev->ssbi_adap, &msg, 1); + + return (ret == 1) ? 0 : ret; +} + +static long ftr_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int __user *argp = (unsigned int __user *) arg; + struct ftr_dev_file_info *pdfi = + (struct ftr_dev_file_info *) file->private_data; + struct ftr_dev_node_info *pdev; + + if (pdfi->ftrId < 0 || pdfi->ftrId >= RFIC_FTR_DEVICE_NUM) + return -EINVAL; + + pdev = ftr_dev_info + pdfi->ftrId; + + switch (cmd) { + case RFIC_IOCTL_READ_REGISTER: + { + int ret; + unsigned int rficAddr; + u8 value; + + if (get_user(rficAddr, argp)) + return -EFAULT; + + mutex_lock(&pdev->lock); + mb(); + /* Need to write twice due to bug in hardware */ + __raw_writel( + pdev->busSelect[RFIC_FTR_GET_BUS(rficAddr)], + pdev->grfcCtrlAddr); + __raw_writel( + pdev->busSelect[RFIC_FTR_GET_BUS(rficAddr)], + pdev->grfcCtrlAddr); + mb(); + ret = ftr_ssbi_read(pdev, RFIC_FTR_GET_ADDR(rficAddr), + &value, 1); + mutex_unlock(&pdev->lock); + + if (ret) + return ret; + + if (put_user(value, argp)) + return -EFAULT; + } + break; + + case RFIC_IOCTL_WRITE_REGISTER: + { + int ret; + struct rfic_write_register_param param; + unsigned int rficAddr; + u8 value; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + rficAddr = param.rficAddr; + value = (u8) param.value; + + mutex_lock(&pdev->lock); + mb(); + /* Need to write twice due to bug in hardware */ + __raw_writel( + pdev->busSelect[RFIC_FTR_GET_BUS(rficAddr)], + pdev->grfcCtrlAddr); + __raw_writel( + pdev->busSelect[RFIC_FTR_GET_BUS(rficAddr)], + pdev->grfcCtrlAddr); + mb(); + ret = ftr_ssbi_write(pdev, RFIC_FTR_GET_ADDR(rficAddr), + &value, 1); + mutex_unlock(&pdev->lock); + + if (ret) + return ret; + } + break; + + case RFIC_IOCTL_WRITE_REGISTER_WITH_MASK: + { + int ret; + struct rfic_write_register_mask_param param; + unsigned int rficAddr; + u8 value; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + rficAddr = param.rficAddr; + + mutex_lock(&pdev->lock); + mb(); + /* Need to write twice due to bug in hardware */ + __raw_writel( + pdev->busSelect[RFIC_FTR_GET_BUS(rficAddr)], + pdev->grfcCtrlAddr); + __raw_writel( + pdev->busSelect[RFIC_FTR_GET_BUS(rficAddr)], + pdev->grfcCtrlAddr); + mb(); + ret = ftr_ssbi_read(pdev, RFIC_FTR_GET_ADDR(rficAddr), + &value, 1); + value &= (u8) ~param.mask; + value |= (u8) (param.value & param.mask); + ret = ftr_ssbi_write(pdev, RFIC_FTR_GET_ADDR(rficAddr), + &value, 1); + mutex_unlock(&pdev->lock); + + if (ret) + return ret; + } + break; + + case RFIC_IOCTL_GET_GRFC: + { + struct rfic_grfc_param param; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + + if (param.grfcId >= RFIC_GRFC_REG_NUM) + return -EINVAL; + + param.maskValue = __raw_readl( + MSM_GRFC_BASE + 0x18 + param.grfcId * 4); + param.ctrlValue = __raw_readl( + MSM_GRFC_BASE + 0x00 + param.grfcId * 4); + + if (copy_to_user(argp, ¶m, sizeof param)) + return -EFAULT; + } + break; + + case RFIC_IOCTL_SET_GRFC: + { + struct rfic_grfc_param param; + + if (copy_from_user(¶m, argp, sizeof param)) + return -EFAULT; + + if (param.grfcId >= RFIC_GRFC_REG_NUM) + return -EINVAL; + + __raw_writel(param.maskValue, + MSM_GRFC_BASE + 0x18 + param.grfcId * 4); + /* Need to write twice due to bug in hardware */ + __raw_writel(param.ctrlValue, + MSM_GRFC_BASE + 0x00 + param.grfcId * 4); + __raw_writel(param.ctrlValue, + MSM_GRFC_BASE + 0x00 + param.grfcId * 4); + mb(); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations ftr_fops = { + .owner = THIS_MODULE, + .open = ftr_open, + .release = ftr_release, + .read = ftr_read, + .write = ftr_write, + .unlocked_ioctl = ftr_ioctl, +}; + +/* + * Driver initialization & cleanup + */ + +struct miscdevice ftr_misc_dev[RFIC_FTR_DEVICE_NUM] = { + { + .minor = MISC_DYNAMIC_MINOR, + .name = RFIC_FTR_DEVICE_NAME "0", + .fops = &ftr_fops, + }, + { + .minor = MISC_DYNAMIC_MINOR, + .name = RFIC_FTR_DEVICE_NAME "1", + .fops = &ftr_fops, + }, +}; + +int ftr_find_id(int minor) +{ + int i; + + for (i = 0; i < RFIC_FTR_DEVICE_NUM; ++i) + if (ftr_misc_dev[i].minor == minor) + break; + + return i; +} + +static int __init ftr_init(void) +{ + int i, ret; + struct ftr_dev_node_info *pdev; + + for (i = 0; i < RFIC_FTR_DEVICE_NUM; ++i) { + pdev = ftr_dev_info + i; + + if (i == 0) { + pdev->grfcCtrlAddr = MSM_GRFC_BASE + 0x00; + pdev->grfcMaskAddr = MSM_GRFC_BASE + 0x18; + __raw_writel(0x300000, pdev->grfcMaskAddr); + pdev->busSelect[TX1_BUS] = 0x000000; + pdev->busSelect[TX2_BUS] = 0x100000; + pdev->busSelect[MISC_BUS] = 0x200000; + pdev->busSelect[RX_BUS] = 0x300000; + pdev->ssbi_adap = i2c_get_adapter(1); + } else { + pdev->grfcCtrlAddr = MSM_GRFC_BASE + 0x04; + pdev->grfcMaskAddr = MSM_GRFC_BASE + 0x1c; + __raw_writel(0x480000, pdev->grfcMaskAddr); + pdev->busSelect[TX1_BUS] = 0x000000; + pdev->busSelect[TX2_BUS] = 0x400000; + pdev->busSelect[MISC_BUS] = 0x080000; + pdev->busSelect[RX_BUS] = 0x480000; + pdev->ssbi_adap = i2c_get_adapter(2); + } + + mutex_init(&pdev->lock); + ret = misc_register(ftr_misc_dev + i); + + if (ret < 0) { + while (--i >= 0) + misc_deregister(ftr_misc_dev + i); + return ret; + } + } + + return 0; +} + +static void __exit ftr_exit(void) +{ + int i; + + for (i = 0; i < RFIC_FTR_DEVICE_NUM; ++i) + misc_deregister(ftr_misc_dev + i); +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Rohit Vaswani "); +MODULE_DESCRIPTION("Qualcomm FSM RFIC driver"); +MODULE_VERSION("1.0"); + +module_init(ftr_init); +module_exit(ftr_exit); diff --git a/arch/arm/mach-msm/rmt_storage_client.c b/arch/arm/mach-msm/rmt_storage_client.c new file mode 100644 index 00000000000..4bec55dc1fb --- /dev/null +++ b/arch/arm/mach-msm/rmt_storage_client.c @@ -0,0 +1,1746 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MSM_SDIO_SMEM +#include +#endif +#include "smd_private.h" + +enum { + RMT_STORAGE_EVNT_OPEN = 0, + RMT_STORAGE_EVNT_CLOSE, + RMT_STORAGE_EVNT_WRITE_BLOCK, + RMT_STORAGE_EVNT_GET_DEV_ERROR, + RMT_STORAGE_EVNT_WRITE_IOVEC, + RMT_STORAGE_EVNT_SEND_USER_DATA, + RMT_STORAGE_EVNT_READ_IOVEC, + RMT_STORAGE_EVNT_ALLOC_RMT_BUF, +} rmt_storage_event; + +struct shared_ramfs_entry { + uint32_t client_id; /* Client id to uniquely identify a client */ + uint32_t base_addr; /* Base address of shared RAMFS memory */ + uint32_t size; /* Size of the shared RAMFS memory */ + uint32_t client_sts; /* This will be initialized to 1 when + remote storage RPC client is ready + to process requests */ +}; +struct shared_ramfs_table { + uint32_t magic_id; /* Identify RAMFS details in SMEM */ + uint32_t version; /* Version of shared_ramfs_table */ + uint32_t entries; /* Total number of valid entries */ + /* List all entries */ + struct shared_ramfs_entry ramfs_entry[MAX_RAMFS_TBL_ENTRIES]; +}; + +struct rmt_storage_client_info { + unsigned long cids; + struct list_head shrd_mem_list; /* List of shared memory entries */ + int open_excl; + atomic_t total_events; + wait_queue_head_t event_q; + struct list_head event_list; + struct list_head client_list; /* List of remote storage clients */ + /* Lock to protect lists */ + spinlock_t lock; + /* Wakelock to be acquired when processing requests from modem */ + struct wake_lock wlock; + atomic_t wcount; + struct workqueue_struct *workq; +}; + +struct rmt_storage_kevent { + struct list_head list; + struct rmt_storage_event event; +}; + +/* Remote storage server on modem */ +struct rmt_storage_srv { + uint32_t prog; + int sync_token; + struct platform_driver plat_drv; + struct msm_rpc_client *rpc_client; + struct delayed_work restart_work; +}; + +/* Remote storage client on modem */ +struct rmt_storage_client { + uint32_t handle; + uint32_t sid; /* Storage ID */ + char path[MAX_PATH_NAME]; + struct rmt_storage_srv *srv; + struct list_head list; +}; + +struct rmt_shrd_mem { + struct list_head list; + struct rmt_shrd_mem_param param; + struct shared_ramfs_entry *smem_info; + struct rmt_storage_srv *srv; +}; + +static struct rmt_storage_srv *rmt_storage_get_srv(uint32_t prog); +static uint32_t rmt_storage_get_sid(const char *path); +#ifdef CONFIG_MSM_SDIO_SMEM +static void rmt_storage_sdio_smem_work(struct work_struct *work); +#endif + +static struct rmt_storage_client_info *rmc; + +#ifdef CONFIG_MSM_SDIO_SMEM +DECLARE_DELAYED_WORK(sdio_smem_work, rmt_storage_sdio_smem_work); +#endif + +#ifdef CONFIG_MSM_SDIO_SMEM +#define MDM_LOCAL_BUF_SZ 0xC0000 +static struct sdio_smem_client *sdio_smem; +#endif + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS +struct rmt_storage_op_stats { + unsigned long count; + ktime_t start; + ktime_t min; + ktime_t max; + ktime_t total; +}; +struct rmt_storage_stats { + char path[MAX_PATH_NAME]; + struct rmt_storage_op_stats rd_stats; + struct rmt_storage_op_stats wr_stats; +}; +static struct rmt_storage_stats client_stats[MAX_NUM_CLIENTS]; +static struct dentry *stats_dentry; +#endif + +#define MSM_RMT_STORAGE_APIPROG 0x300000A7 +#define MDM_RMT_STORAGE_APIPROG 0x300100A7 + +#define RMT_STORAGE_OP_FINISH_PROC 2 +#define RMT_STORAGE_REGISTER_OPEN_PROC 3 +#define RMT_STORAGE_REGISTER_WRITE_IOVEC_PROC 4 +#define RMT_STORAGE_REGISTER_CB_PROC 5 +#define RMT_STORAGE_UN_REGISTER_CB_PROC 6 +#define RMT_STORAGE_FORCE_SYNC_PROC 7 +#define RMT_STORAGE_GET_SYNC_STATUS_PROC 8 +#define RMT_STORAGE_REGISTER_READ_IOVEC_PROC 9 +#define RMT_STORAGE_REGISTER_ALLOC_RMT_BUF_PROC 10 + +#define RMT_STORAGE_OPEN_CB_TYPE_PROC 1 +#define RMT_STORAGE_WRITE_IOVEC_CB_TYPE_PROC 2 +#define RMT_STORAGE_EVENT_CB_TYPE_PROC 3 +#define RMT_STORAGE_READ_IOVEC_CB_TYPE_PROC 4 +#define RMT_STORAGE_ALLOC_RMT_BUF_CB_TYPE_PROC 5 + +#define RAMFS_INFO_MAGICNUMBER 0x654D4D43 +#define RAMFS_INFO_VERSION 0x00000001 +#define RAMFS_DEFAULT 0xFFFFFFFF + +/* MSM EFS*/ +#define RAMFS_MODEMSTORAGE_ID 0x4D454653 +#define RAMFS_SHARED_EFS_RAM_BASE 0x46100000 +#define RAMFS_SHARED_EFS_RAM_SIZE (3 * 1024 * 1024) + +/* MDM EFS*/ +#define RAMFS_MDM_STORAGE_ID 0x4D4583A1 +/* SSD */ +#define RAMFS_SSD_STORAGE_ID 0x00535344 +#define RAMFS_SHARED_SSD_RAM_BASE 0x42E00000 +#define RAMFS_SHARED_SSD_RAM_SIZE 0x2000 + +static struct rmt_storage_client *rmt_storage_get_client(uint32_t handle) +{ + struct rmt_storage_client *rs_client; + list_for_each_entry(rs_client, &rmc->client_list, list) + if (rs_client->handle == handle) + return rs_client; + return NULL; +} + +static struct rmt_storage_client * +rmt_storage_get_client_by_path(const char *path) +{ + struct rmt_storage_client *rs_client; + list_for_each_entry(rs_client, &rmc->client_list, list) + if (!strncmp(path, rs_client->path, MAX_PATH_NAME)) + return rs_client; + return NULL; +} + +static struct rmt_shrd_mem_param *rmt_storage_get_shrd_mem(uint32_t sid) +{ + struct rmt_shrd_mem *shrd_mem; + struct rmt_shrd_mem_param *shrd_mem_param = NULL; + + spin_lock(&rmc->lock); + list_for_each_entry(shrd_mem, &rmc->shrd_mem_list, list) + if (shrd_mem->param.sid == sid) + shrd_mem_param = &shrd_mem->param; + spin_unlock(&rmc->lock); + + return shrd_mem_param; +} + +static int rmt_storage_add_shrd_mem(uint32_t sid, uint32_t start, + uint32_t size, void *base, + struct shared_ramfs_entry *smem_info, + struct rmt_storage_srv *srv) +{ + struct rmt_shrd_mem *shrd_mem; + + shrd_mem = kzalloc(sizeof(struct rmt_shrd_mem), GFP_KERNEL); + if (!shrd_mem) + return -ENOMEM; + shrd_mem->param.sid = sid; + shrd_mem->param.start = start; + shrd_mem->param.size = size; + shrd_mem->param.base = base; + shrd_mem->smem_info = smem_info; + shrd_mem->srv = srv; + + spin_lock(&rmc->lock); + list_add(&shrd_mem->list, &rmc->shrd_mem_list); + spin_unlock(&rmc->lock); + return 0; +} + +static struct msm_rpc_client *rmt_storage_get_rpc_client(uint32_t handle) +{ + struct rmt_storage_client *rs_client; + + rs_client = rmt_storage_get_client(handle); + if (!rs_client) + return NULL; + return rs_client->srv->rpc_client; +} + +static int rmt_storage_validate_iovec(uint32_t handle, + struct rmt_storage_iovec_desc *xfer) +{ + struct rmt_storage_client *rs_client; + struct rmt_shrd_mem_param *shrd_mem; + + rs_client = rmt_storage_get_client(handle); + if (!rs_client) + return -EINVAL; + shrd_mem = rmt_storage_get_shrd_mem(rs_client->sid); + if (!shrd_mem) + return -EINVAL; + + if ((xfer->data_phy_addr < shrd_mem->start) || + ((xfer->data_phy_addr + RAMFS_BLOCK_SIZE * xfer->num_sector) > + (shrd_mem->start + shrd_mem->size))) + return -EINVAL; + return 0; +} + +static int rmt_storage_send_sts_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct rmt_storage_send_sts *args = data; + + xdr_send_uint32(xdr, &args->handle); + xdr_send_uint32(xdr, &args->err_code); + xdr_send_uint32(xdr, &args->data); + return 0; +} + +static void put_event(struct rmt_storage_client_info *rmc, + struct rmt_storage_kevent *kevent) +{ + spin_lock(&rmc->lock); + list_add_tail(&kevent->list, &rmc->event_list); + spin_unlock(&rmc->lock); +} + +static struct rmt_storage_kevent *get_event(struct rmt_storage_client_info *rmc) +{ + struct rmt_storage_kevent *kevent = NULL; + + spin_lock(&rmc->lock); + if (!list_empty(&rmc->event_list)) { + kevent = list_first_entry(&rmc->event_list, + struct rmt_storage_kevent, list); + list_del(&kevent->list); + } + spin_unlock(&rmc->lock); + return kevent; +} + +static int rmt_storage_event_open_cb(struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + uint32_t cid, len, event_type; + char *path; + int ret; + struct rmt_storage_srv *srv; + struct rmt_storage_client *rs_client = NULL; +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + struct rmt_storage_stats *stats; +#endif + + srv = rmt_storage_get_srv(event_args->usr_data); + if (!srv) + return -EINVAL; + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_OPEN) + return -1; + + pr_info("%s: open callback received\n", __func__); + + ret = xdr_recv_bytes(xdr, (void **)&path, &len); + if (ret || !path) { + pr_err("%s: Invalid path\n", __func__); + if (!ret) + ret = -1; + goto free_rs_client; + } + + rs_client = rmt_storage_get_client_by_path(path); + if (rs_client) { + pr_debug("%s: Handle %d found for %s\n", + __func__, rs_client->handle, path); + event_args->id = RMT_STORAGE_NOOP; + cid = rs_client->handle; + goto end_open_cb; + } + + rs_client = kzalloc(sizeof(struct rmt_storage_client), GFP_KERNEL); + if (!rs_client) { + pr_err("%s: Error allocating rmt storage client\n", __func__); + ret = -ENOMEM; + goto free_path; + } + + memcpy(event_args->path, path, len); + rs_client->sid = rmt_storage_get_sid(event_args->path); + if (!rs_client->sid) { + pr_err("%s: No storage id found for %s\n", __func__, + event_args->path); + ret = -EINVAL; + goto free_path; + } + strncpy(rs_client->path, event_args->path, MAX_PATH_NAME); + + cid = find_first_zero_bit(&rmc->cids, sizeof(rmc->cids) * 8); + if (cid > MAX_NUM_CLIENTS) { + pr_err("%s: Max clients are reached\n", __func__); + cid = 0; + return cid; + } + __set_bit(cid, &rmc->cids); + pr_info("open partition %s handle=%d\n", event_args->path, cid); + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + stats = &client_stats[cid - 1]; + memcpy(stats->path, event_args->path, len); + memset(stats->rd_stats, 0, sizeof(struct rmt_storage_op_stats)); + memset(stats->wr_stats, 0, sizeof(struct rmt_storage_op_stats)); + stats->rd_stats.min.tv64 = KTIME_MAX; + stats->wr_stats.min.tv64 = KTIME_MAX; +#endif + event_args->id = RMT_STORAGE_OPEN; + event_args->sid = rs_client->sid; + event_args->handle = cid; + + rs_client->handle = event_args->handle; + rs_client->srv = srv; + INIT_LIST_HEAD(&rs_client->list); + spin_lock(&rmc->lock); + list_add_tail(&rs_client->list, &rmc->client_list); + spin_unlock(&rmc->lock); + +end_open_cb: + kfree(path); + return cid; + +free_path: + kfree(path); +free_rs_client: + kfree(rs_client); + return ret; +} + +struct rmt_storage_close_args { + uint32_t handle; +}; + +struct rmt_storage_rw_block_args { + uint32_t handle; + uint32_t data_phy_addr; + uint32_t sector_addr; + uint32_t num_sector; +}; + +struct rmt_storage_get_err_args { + uint32_t handle; +}; + +struct rmt_storage_user_data_args { + uint32_t handle; + uint32_t data; +}; + +struct rmt_storage_event_params { + uint32_t type; + union { + struct rmt_storage_close_args close; + struct rmt_storage_rw_block_args block; + struct rmt_storage_get_err_args get_err; + struct rmt_storage_user_data_args user_data; + } params; +}; + +static int rmt_storage_parse_params(struct msm_rpc_xdr *xdr, + struct rmt_storage_event_params *event) +{ + xdr_recv_uint32(xdr, &event->type); + + switch (event->type) { + case RMT_STORAGE_EVNT_CLOSE: { + struct rmt_storage_close_args *args; + args = &event->params.close; + + xdr_recv_uint32(xdr, &args->handle); + break; + } + + case RMT_STORAGE_EVNT_WRITE_BLOCK: { + struct rmt_storage_rw_block_args *args; + args = &event->params.block; + + xdr_recv_uint32(xdr, &args->handle); + xdr_recv_uint32(xdr, &args->data_phy_addr); + xdr_recv_uint32(xdr, &args->sector_addr); + xdr_recv_uint32(xdr, &args->num_sector); + break; + } + + case RMT_STORAGE_EVNT_GET_DEV_ERROR: { + struct rmt_storage_get_err_args *args; + args = &event->params.get_err; + + xdr_recv_uint32(xdr, &args->handle); + break; + } + + case RMT_STORAGE_EVNT_SEND_USER_DATA: { + struct rmt_storage_user_data_args *args; + args = &event->params.user_data; + + xdr_recv_uint32(xdr, &args->handle); + xdr_recv_uint32(xdr, &args->data); + break; + } + + default: + pr_err("%s: unknown event %d\n", __func__, event->type); + return -1; + } + return 0; +} + +static int rmt_storage_event_close_cb(struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_event_params *event; + struct rmt_storage_close_args *close; + struct rmt_storage_client *rs_client; + uint32_t event_type; + int ret; + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_CLOSE) + return -1; + + pr_debug("%s: close callback received\n", __func__); + ret = xdr_recv_pointer(xdr, (void **)&event, + sizeof(struct rmt_storage_event_params), + rmt_storage_parse_params); + + if (ret || !event) + return -1; + + close = &event->params.close; + event_args->handle = close->handle; + event_args->id = RMT_STORAGE_CLOSE; + __clear_bit(event_args->handle, &rmc->cids); + rs_client = rmt_storage_get_client(event_args->handle); + if (rs_client) { + list_del(&rs_client->list); + kfree(rs_client); + } + kfree(event); + return RMT_STORAGE_NO_ERROR; +} + +static int rmt_storage_event_write_block_cb( + struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_event_params *event; + struct rmt_storage_rw_block_args *write_block; + struct rmt_storage_iovec_desc *xfer; + uint32_t event_type; + int ret; + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_WRITE_BLOCK) + return -1; + + pr_debug("%s: write block callback received\n", __func__); + ret = xdr_recv_pointer(xdr, (void **)&event, + sizeof(struct rmt_storage_event_params), + rmt_storage_parse_params); + + if (ret || !event) + return -1; + + write_block = &event->params.block; + event_args->handle = write_block->handle; + xfer = &event_args->xfer_desc[0]; + xfer->sector_addr = write_block->sector_addr; + xfer->data_phy_addr = write_block->data_phy_addr; + xfer->num_sector = write_block->num_sector; + + ret = rmt_storage_validate_iovec(event_args->handle, xfer); + if (ret) + return -1; + event_args->xfer_cnt = 1; + event_args->id = RMT_STORAGE_WRITE; + + if (atomic_inc_return(&rmc->wcount) == 1) + wake_lock(&rmc->wlock); + + pr_debug("sec_addr = %u, data_addr = %x, num_sec = %d\n\n", + xfer->sector_addr, xfer->data_phy_addr, + xfer->num_sector); + + kfree(event); + return RMT_STORAGE_NO_ERROR; +} + +static int rmt_storage_event_get_err_cb(struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_event_params *event; + struct rmt_storage_get_err_args *get_err; + uint32_t event_type; + int ret; + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_GET_DEV_ERROR) + return -1; + + pr_debug("%s: get err callback received\n", __func__); + ret = xdr_recv_pointer(xdr, (void **)&event, + sizeof(struct rmt_storage_event_params), + rmt_storage_parse_params); + + if (ret || !event) + return -1; + + get_err = &event->params.get_err; + event_args->handle = get_err->handle; + kfree(event); + /* Not implemented */ + return -1; + +} + +static int rmt_storage_event_user_data_cb(struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_event_params *event; + struct rmt_storage_user_data_args *user_data; + uint32_t event_type; + int ret; + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_SEND_USER_DATA) + return -1; + + pr_info("%s: send user data callback received\n", __func__); + ret = xdr_recv_pointer(xdr, (void **)&event, + sizeof(struct rmt_storage_event_params), + rmt_storage_parse_params); + + if (ret || !event) + return -1; + + user_data = &event->params.user_data; + event_args->handle = user_data->handle; + event_args->usr_data = user_data->data; + event_args->id = RMT_STORAGE_SEND_USER_DATA; + + kfree(event); + return RMT_STORAGE_NO_ERROR; +} + +static int rmt_storage_event_write_iovec_cb( + struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_iovec_desc *xfer; + uint32_t i, ent, event_type; +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + struct rmt_storage_stats *stats; +#endif + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_WRITE_IOVEC) + return -EINVAL; + + pr_info("%s: write iovec callback received\n", __func__); + xdr_recv_uint32(xdr, &event_args->handle); + xdr_recv_uint32(xdr, &ent); + pr_debug("handle = %d\n", event_args->handle); + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + stats = &client_stats[event_args->handle - 1]; + stats->wr_stats.start = ktime_get(); +#endif + for (i = 0; i < ent; i++) { + xfer = &event_args->xfer_desc[i]; + xdr_recv_uint32(xdr, &xfer->sector_addr); + xdr_recv_uint32(xdr, &xfer->data_phy_addr); + xdr_recv_uint32(xdr, &xfer->num_sector); + + if (rmt_storage_validate_iovec(event_args->handle, xfer)) + return -EINVAL; + + pr_debug("sec_addr = %u, data_addr = %x, num_sec = %d\n", + xfer->sector_addr, xfer->data_phy_addr, + xfer->num_sector); + } + xdr_recv_uint32(xdr, &event_args->xfer_cnt); + event_args->id = RMT_STORAGE_WRITE; + if (atomic_inc_return(&rmc->wcount) == 1) + wake_lock(&rmc->wlock); + + pr_debug("iovec transfer count = %d\n\n", event_args->xfer_cnt); + return RMT_STORAGE_NO_ERROR; +} + +static int rmt_storage_event_read_iovec_cb( + struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_iovec_desc *xfer; + uint32_t i, ent, event_type; +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + struct rmt_storage_stats *stats; +#endif + + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_READ_IOVEC) + return -EINVAL; + + pr_info("%s: read iovec callback received\n", __func__); + xdr_recv_uint32(xdr, &event_args->handle); + xdr_recv_uint32(xdr, &ent); + pr_debug("handle = %d\n", event_args->handle); + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + stats = &client_stats[event_args->handle - 1]; + stats->rd_stats.start = ktime_get(); +#endif + for (i = 0; i < ent; i++) { + xfer = &event_args->xfer_desc[i]; + xdr_recv_uint32(xdr, &xfer->sector_addr); + xdr_recv_uint32(xdr, &xfer->data_phy_addr); + xdr_recv_uint32(xdr, &xfer->num_sector); + + if (rmt_storage_validate_iovec(event_args->handle, xfer)) + return -EINVAL; + + pr_debug("sec_addr = %u, data_addr = %x, num_sec = %d\n", + xfer->sector_addr, xfer->data_phy_addr, + xfer->num_sector); + } + xdr_recv_uint32(xdr, &event_args->xfer_cnt); + event_args->id = RMT_STORAGE_READ; + if (atomic_inc_return(&rmc->wcount) == 1) + wake_lock(&rmc->wlock); + + pr_debug("iovec transfer count = %d\n\n", event_args->xfer_cnt); + return RMT_STORAGE_NO_ERROR; +} + +#ifdef CONFIG_MSM_SDIO_SMEM +static int sdio_smem_cb(int event) +{ + pr_debug("%s: Received event %d\n", __func__, event); + + switch (event) { + case SDIO_SMEM_EVENT_READ_DONE: + pr_debug("Read done\n"); + break; + case SDIO_SMEM_EVENT_READ_ERR: + pr_err("Read overflow\n"); + return -EIO; + default: + pr_err("Unhandled event\n"); + } + return 0; +} + +static int rmt_storage_sdio_smem_probe(struct platform_device *pdev) +{ + int ret = 0; + struct rmt_shrd_mem_param *shrd_mem; + + sdio_smem = container_of(pdev, struct sdio_smem_client, plat_dev); + + /* SDIO SMEM is supported only for MDM */ + shrd_mem = rmt_storage_get_shrd_mem(RAMFS_MDM_STORAGE_ID); + if (!shrd_mem) { + pr_err("%s: No shared mem entry for sid=0x%08x\n", + __func__, (uint32_t)RAMFS_MDM_STORAGE_ID); + return -ENOMEM; + } + sdio_smem->buf = __va(shrd_mem->start); + sdio_smem->size = shrd_mem->size; + sdio_smem->cb_func = sdio_smem_cb; + ret = sdio_smem_register_client(); + if (ret) + pr_info("%s: Error (%d) registering sdio_smem client\n", + __func__, ret); + return ret; +} + +static int rmt_storage_sdio_smem_remove(struct platform_device *pdev) +{ + sdio_smem_unregister_client(); + queue_delayed_work(rmc->workq, &sdio_smem_work, 0); + return 0; +} + +static int sdio_smem_drv_registered; +static struct platform_driver sdio_smem_drv = { + .probe = rmt_storage_sdio_smem_probe, + .remove = rmt_storage_sdio_smem_remove, + .driver = { + .name = "SDIO_SMEM_CLIENT", + .owner = THIS_MODULE, + }, +}; + +static void rmt_storage_sdio_smem_work(struct work_struct *work) +{ + platform_driver_unregister(&sdio_smem_drv); + sdio_smem_drv_registered = 0; +} +#endif + +static int rmt_storage_event_alloc_rmt_buf_cb( + struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr) +{ + struct rmt_storage_client *rs_client; + struct rmt_shrd_mem_param *shrd_mem; + uint32_t event_type, handle, size; +#ifdef CONFIG_MSM_SDIO_SMEM + int ret; +#endif + xdr_recv_uint32(xdr, &event_type); + if (event_type != RMT_STORAGE_EVNT_ALLOC_RMT_BUF) + return -EINVAL; + + pr_info("%s: Alloc rmt buf callback received\n", __func__); + xdr_recv_uint32(xdr, &handle); + xdr_recv_uint32(xdr, &size); + + pr_debug("%s: handle=0x%x size=0x%x\n", __func__, handle, size); + + rs_client = rmt_storage_get_client(handle); + if (!rs_client) { + pr_err("%s: Unable to find client for handle=%d\n", + __func__, handle); + return -EINVAL; + } + + rs_client->sid = rmt_storage_get_sid(rs_client->path); + if (!rs_client->sid) { + pr_err("%s: No storage id found for %s\n", + __func__, rs_client->path); + return -EINVAL; + } + + shrd_mem = rmt_storage_get_shrd_mem(rs_client->sid); + if (!shrd_mem) { + pr_err("%s: No shared memory entry found\n", + __func__); + return -ENOMEM; + } + if (shrd_mem->size < size) { + pr_err("%s: Size mismatch for handle=%d\n", + __func__, rs_client->handle); + return -EINVAL; + } + pr_debug("%s: %d bytes at phys=0x%x for handle=%d found\n", + __func__, size, shrd_mem->start, rs_client->handle); + +#ifdef CONFIG_MSM_SDIO_SMEM + if (rs_client->srv->prog == MDM_RMT_STORAGE_APIPROG) { + if (!sdio_smem_drv_registered) { + ret = platform_driver_register(&sdio_smem_drv); + if (!ret) + sdio_smem_drv_registered = 1; + else + pr_err("%s: Cant register sdio smem client\n", + __func__); + } + } +#endif + event_args->id = RMT_STORAGE_NOOP; + return (int)shrd_mem->start; +} + +static int handle_rmt_storage_call(struct msm_rpc_client *client, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + int rc; + uint32_t result = RMT_STORAGE_NO_ERROR; + uint32_t rpc_status = RPC_ACCEPTSTAT_SUCCESS; + struct rmt_storage_event *event_args; + struct rmt_storage_kevent *kevent; + + kevent = kzalloc(sizeof(struct rmt_storage_kevent), GFP_KERNEL); + if (!kevent) { + rpc_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto out; + } + event_args = &kevent->event; + + switch (req->procedure) { + case RMT_STORAGE_OPEN_CB_TYPE_PROC: + /* client created in cb needs a ref. to its server */ + event_args->usr_data = client->prog; + /* fall through */ + + case RMT_STORAGE_WRITE_IOVEC_CB_TYPE_PROC: + /* fall through */ + + case RMT_STORAGE_READ_IOVEC_CB_TYPE_PROC: + /* fall through */ + + case RMT_STORAGE_ALLOC_RMT_BUF_CB_TYPE_PROC: + /* fall through */ + + case RMT_STORAGE_EVENT_CB_TYPE_PROC: { + uint32_t cb_id; + int (*cb_func)(struct rmt_storage_event *event_args, + struct msm_rpc_xdr *xdr); + + xdr_recv_uint32(xdr, &cb_id); + cb_func = msm_rpc_get_cb_func(client, cb_id); + + if (!cb_func) { + rpc_status = RPC_ACCEPTSTAT_GARBAGE_ARGS; + kfree(kevent); + goto out; + } + + rc = cb_func(event_args, xdr); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: Invalid parameters received\n", __func__); + if (req->procedure == RMT_STORAGE_OPEN_CB_TYPE_PROC) + result = 0; /* bad handle to signify err */ + else + result = RMT_STORAGE_ERROR_PARAM; + kfree(kevent); + goto out; + } + result = (uint32_t) rc; + break; + } + + default: + kfree(kevent); + pr_err("%s: unknown procedure %d\n", __func__, req->procedure); + rpc_status = RPC_ACCEPTSTAT_PROC_UNAVAIL; + goto out; + } + + if (kevent->event.id != RMT_STORAGE_NOOP) { + put_event(rmc, kevent); + atomic_inc(&rmc->total_events); + wake_up(&rmc->event_q); + } else + kfree(kevent); + +out: + pr_debug("%s: Sending result=0x%x\n", __func__, result); + xdr_start_accepted_reply(xdr, rpc_status); + xdr_send_uint32(xdr, &result); + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int rmt_storage_open(struct inode *ip, struct file *fp) +{ + int ret = 0; + + spin_lock(&rmc->lock); + if (!rmc->open_excl) + rmc->open_excl = 1; + else + ret = -EBUSY; + spin_unlock(&rmc->lock); + + return ret; +} + +static int rmt_storage_release(struct inode *ip, struct file *fp) +{ + spin_lock(&rmc->lock); + rmc->open_excl = 0; + spin_unlock(&rmc->lock); + + return 0; +} + +static long rmt_storage_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct rmt_storage_kevent *kevent; + struct rmt_storage_send_sts status; + static struct msm_rpc_client *rpc_client; + struct rmt_shrd_mem_param usr_shrd_mem, *shrd_mem; + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + struct rmt_storage_stats *stats; + struct rmt_storage_op_stats *op_stats; + ktime_t curr_stat; +#endif + + switch (cmd) { + + case RMT_STORAGE_SHRD_MEM_PARAM: + pr_debug("%s: get shared memory parameters ioctl\n", __func__); + if (copy_from_user(&usr_shrd_mem, (void __user *)arg, + sizeof(struct rmt_shrd_mem_param))) { + pr_err("%s: copy from user failed\n\n", __func__); + ret = -EFAULT; + break; + } + + shrd_mem = rmt_storage_get_shrd_mem(usr_shrd_mem.sid); + if (!shrd_mem) { + pr_err("%s: invalid sid (0x%x)\n", __func__, + usr_shrd_mem.sid); + ret = -EFAULT; + break; + } + + if (copy_to_user((void __user *)arg, shrd_mem, + sizeof(struct rmt_shrd_mem_param))) { + pr_err("%s: copy to user failed\n\n", __func__); + ret = -EFAULT; + } + break; + + case RMT_STORAGE_WAIT_FOR_REQ: + pr_debug("%s: wait for request ioctl\n", __func__); + if (atomic_read(&rmc->total_events) == 0) { + ret = wait_event_interruptible(rmc->event_q, + atomic_read(&rmc->total_events) != 0); + } + if (ret < 0) + break; + atomic_dec(&rmc->total_events); + + kevent = get_event(rmc); + WARN_ON(kevent == NULL); + if (copy_to_user((void __user *)arg, &kevent->event, + sizeof(struct rmt_storage_event))) { + pr_err("%s: copy to user failed\n\n", __func__); + ret = -EFAULT; + } + kfree(kevent); + break; + + case RMT_STORAGE_SEND_STATUS: + pr_info("%s: send status ioctl\n", __func__); + if (copy_from_user(&status, (void __user *)arg, + sizeof(struct rmt_storage_send_sts))) { + pr_err("%s: copy from user failed\n\n", __func__); + ret = -EFAULT; + if (atomic_dec_return(&rmc->wcount) == 0) + wake_unlock(&rmc->wlock); + break; + } +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + stats = &client_stats[status.handle - 1]; + if (status.xfer_dir == RMT_STORAGE_WRITE) + op_stats = &stats->wr_stats; + else + op_stats = &stats->rd_stats; + curr_stat = ktime_sub(ktime_get(), op_stats->start); + op_stats->total = ktime_add(op_stats->total, curr_stat); + op_stats->count++; + if (curr_stat.tv64 < stats->min.tv64) + op_stats->min = curr_stat; + if (curr_stat.tv64 > stats->max.tv64) + op_stats->max = curr_stat; +#endif + pr_debug("%s: \thandle=%d err_code=%d data=0x%x\n", __func__, + status.handle, status.err_code, status.data); + rpc_client = rmt_storage_get_rpc_client(status.handle); + if (rpc_client) + ret = msm_rpc_client_req2(rpc_client, + RMT_STORAGE_OP_FINISH_PROC, + rmt_storage_send_sts_arg, + &status, NULL, NULL, -1); + else + ret = -EINVAL; + if (ret < 0) + pr_err("%s: send status failed with ret val = %d\n", + __func__, ret); + if (atomic_dec_return(&rmc->wcount) == 0) + wake_unlock(&rmc->wlock); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +struct rmt_storage_sync_recv_arg { + int data; +}; + +static int rmt_storage_receive_sync_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct rmt_storage_sync_recv_arg *args = data; + struct rmt_storage_srv *srv; + + srv = rmt_storage_get_srv(client->prog); + if (!srv) + return -EINVAL; + xdr_recv_int32(xdr, &args->data); + srv->sync_token = args->data; + return 0; +} + +static int rmt_storage_force_sync(struct msm_rpc_client *client) +{ + struct rmt_storage_sync_recv_arg args; + int rc; + rc = msm_rpc_client_req2(client, + RMT_STORAGE_FORCE_SYNC_PROC, NULL, NULL, + rmt_storage_receive_sync_arg, &args, -1); + if (rc) { + pr_err("%s: force sync RPC req failed: %d\n", __func__, rc); + return rc; + } + return 0; +} + +struct rmt_storage_sync_sts_arg { + int token; +}; + +static int rmt_storage_send_sync_sts_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct rmt_storage_sync_sts_arg *req = data; + + xdr_send_int32(xdr, &req->token); + return 0; +} + +static int rmt_storage_receive_sync_sts_arg(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct rmt_storage_sync_recv_arg *args = data; + + xdr_recv_int32(xdr, &args->data); + return 0; +} + +static int rmt_storage_get_sync_status(struct msm_rpc_client *client) +{ + struct rmt_storage_sync_recv_arg recv_args; + struct rmt_storage_sync_sts_arg send_args; + struct rmt_storage_srv *srv; + int rc; + + srv = rmt_storage_get_srv(client->prog); + if (!srv) + return -EINVAL; + + if (srv->sync_token < 0) + return -EINVAL; + + send_args.token = srv->sync_token; + rc = msm_rpc_client_req2(client, + RMT_STORAGE_GET_SYNC_STATUS_PROC, + rmt_storage_send_sync_sts_arg, &send_args, + rmt_storage_receive_sync_sts_arg, &recv_args, -1); + if (rc) { + pr_err("%s: sync status RPC req failed: %d\n", __func__, rc); + return rc; + } + return recv_args.data; +} + +static int rmt_storage_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vsize = vma->vm_end - vma->vm_start; + int ret = -EINVAL; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vsize, vma->vm_page_prot); + if (ret < 0) + pr_err("%s: failed with return val %d\n", __func__, ret); + return ret; +} + +struct rmt_storage_reg_cb_args { + uint32_t event; + uint32_t cb_id; +}; + +static int rmt_storage_arg_cb(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct rmt_storage_reg_cb_args *args = data; + + xdr_send_uint32(xdr, &args->event); + xdr_send_uint32(xdr, &args->cb_id); + return 0; +} + +static int rmt_storage_reg_cb(struct msm_rpc_client *client, + uint32_t proc, uint32_t event, void *callback) +{ + struct rmt_storage_reg_cb_args args; + int rc, cb_id; + int retries = 10; + + cb_id = msm_rpc_add_cb_func(client, callback); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + args.event = event; + args.cb_id = cb_id; + + while (retries) { + rc = msm_rpc_client_req2(client, proc, rmt_storage_arg_cb, + &args, NULL, NULL, -1); + if (rc != -ETIMEDOUT) + break; + retries--; + udelay(1000); + } + if (rc) + pr_err("%s: Failed to register callback for event %d\n", + __func__, event); + return rc; +} + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS +static int rmt_storage_stats_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t rmt_storage_stats_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + uint32_t tot_clients; + char buf[512]; + int max, j, i = 0; + struct rmt_storage_stats *stats; + + max = sizeof(buf) - 1; + tot_clients = find_first_zero_bit(&rmc->cids, sizeof(rmc->cids)) - 1; + + for (j = 0; j < tot_clients; j++) { + stats = &client_stats[j]; + i += scnprintf(buf + i, max - i, "stats for partition %s:\n", + stats->path); + i += scnprintf(buf + i, max - i, "Min read time: %lld us\n", + ktime_to_us(stats->rd_stats.min)); + i += scnprintf(buf + i, max - i, "Max read time: %lld us\n", + ktime_to_us(stats->rd_stats.max)); + i += scnprintf(buf + i, max - i, "Total read time: %lld us\n", + ktime_to_us(stats->rd_stats.total)); + i += scnprintf(buf + i, max - i, "Total read requests: %ld\n", + stats->rd_stats.count); + if (stats->count) + i += scnprintf(buf + i, max - i, + "Avg read time: %lld us\n", + div_s64(ktime_to_us(stats->total), + stats->rd_stats.count)); + + i += scnprintf(buf + i, max - i, "Min write time: %lld us\n", + ktime_to_us(stats->wr_stats.min)); + i += scnprintf(buf + i, max - i, "Max write time: %lld us\n", + ktime_to_us(stats->wr_stats.max)); + i += scnprintf(buf + i, max - i, "Total write time: %lld us\n", + ktime_to_us(stats->wr_stats.total)); + i += scnprintf(buf + i, max - i, "Total read requests: %ld\n", + stats->wr_stats.count); + if (stats->count) + i += scnprintf(buf + i, max - i, + "Avg write time: %lld us\n", + div_s64(ktime_to_us(stats->total), + stats->wr_stats.count)); + } + return simple_read_from_buffer(ubuf, count, ppos, buf, i); +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = rmt_storage_stats_open, + .read = rmt_storage_stats_read, +}; +#endif + +const struct file_operations rmt_storage_fops = { + .owner = THIS_MODULE, + .open = rmt_storage_open, + .unlocked_ioctl = rmt_storage_ioctl, + .mmap = rmt_storage_mmap, + .release = rmt_storage_release, +}; + +static struct miscdevice rmt_storage_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "rmt_storage", + .fops = &rmt_storage_fops, +}; + +static int rmt_storage_get_ramfs(struct rmt_storage_srv *srv) +{ + struct shared_ramfs_table *ramfs_table; + struct shared_ramfs_entry *ramfs_entry; + int index, ret; + + if (srv->prog != MSM_RMT_STORAGE_APIPROG) + return 0; + + ramfs_table = smem_alloc(SMEM_SEFS_INFO, + sizeof(struct shared_ramfs_table)); + + if (!ramfs_table) { + pr_err("%s: No RAMFS table in SMEM\n", __func__); + return -ENOENT; + } + + if ((ramfs_table->magic_id != (u32) RAMFS_INFO_MAGICNUMBER) || + (ramfs_table->version != (u32) RAMFS_INFO_VERSION)) { + pr_err("%s: Magic / Version mismatch:, " + "magic_id=%#x, format_version=%#x\n", __func__, + ramfs_table->magic_id, ramfs_table->version); + return -ENOENT; + } + + for (index = 0; index < ramfs_table->entries; index++) { + ramfs_entry = &ramfs_table->ramfs_entry[index]; + if (!ramfs_entry->client_id || + ramfs_entry->client_id == (u32) RAMFS_DEFAULT) + break; + + pr_info("%s: RAMFS entry: addr = 0x%08x, size = 0x%08x\n", + __func__, ramfs_entry->base_addr, ramfs_entry->size); + + ret = rmt_storage_add_shrd_mem(ramfs_entry->client_id, + ramfs_entry->base_addr, + ramfs_entry->size, + NULL, + ramfs_entry, + srv); + if (ret) { + pr_err("%s: Error (%d) adding shared mem\n", + __func__, ret); + return ret; + } + } + return 0; +} + +static ssize_t +show_force_sync(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev; + struct rpcsvr_platform_device *rpc_pdev; + struct rmt_storage_srv *srv; + + pdev = container_of(dev, struct platform_device, dev); + rpc_pdev = container_of(pdev, struct rpcsvr_platform_device, base); + srv = rmt_storage_get_srv(rpc_pdev->prog); + if (!srv) { + pr_err("%s: Unable to find prog=0x%x\n", __func__, + rpc_pdev->prog); + return -EINVAL; + } + + return rmt_storage_force_sync(srv->rpc_client); +} + +/* Returns -EINVAL for invalid sync token and an error value for any failure + * in RPC call. Upon success, it returns a sync status of 1 (sync done) + * or 0 (sync still pending). + */ +static ssize_t +show_sync_sts(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct rpcsvr_platform_device *rpc_pdev; + struct rmt_storage_srv *srv; + + pdev = container_of(dev, struct platform_device, dev); + rpc_pdev = container_of(pdev, struct rpcsvr_platform_device, base); + srv = rmt_storage_get_srv(rpc_pdev->prog); + if (!srv) { + pr_err("%s: Unable to find prog=0x%x\n", __func__, + rpc_pdev->prog); + return -EINVAL; + } + return snprintf(buf, PAGE_SIZE, "%d\n", + rmt_storage_get_sync_status(srv->rpc_client)); +} + +static int rmt_storage_init_ramfs(struct rmt_storage_srv *srv) +{ + struct shared_ramfs_table *ramfs_table; + + if (srv->prog != MSM_RMT_STORAGE_APIPROG) + return 0; + + ramfs_table = smem_alloc(SMEM_SEFS_INFO, + sizeof(struct shared_ramfs_table)); + + if (!ramfs_table) { + pr_err("%s: No RAMFS table in SMEM\n", __func__); + return -ENOENT; + } + + if (ramfs_table->magic_id == RAMFS_INFO_MAGICNUMBER) { + pr_debug("RAMFS table already filled... skipping %s", \ + __func__); + return 0; + } + + ramfs_table->ramfs_entry[0].client_id = RAMFS_MODEMSTORAGE_ID; + ramfs_table->ramfs_entry[0].base_addr = RAMFS_SHARED_EFS_RAM_BASE; + ramfs_table->ramfs_entry[0].size = RAMFS_SHARED_EFS_RAM_SIZE; + ramfs_table->ramfs_entry[0].client_sts = RAMFS_DEFAULT; + + ramfs_table->ramfs_entry[1].client_id = RAMFS_SSD_STORAGE_ID; + ramfs_table->ramfs_entry[1].base_addr = RAMFS_SHARED_SSD_RAM_BASE; + ramfs_table->ramfs_entry[1].size = RAMFS_SHARED_SSD_RAM_SIZE; + ramfs_table->ramfs_entry[1].client_sts = RAMFS_DEFAULT; + + ramfs_table->entries = 2; + ramfs_table->version = RAMFS_INFO_VERSION; + ramfs_table->magic_id = RAMFS_INFO_MAGICNUMBER; + + return 0; +} + +static void rmt_storage_set_client_status(struct rmt_storage_srv *srv, + int enable) +{ + struct rmt_shrd_mem *shrd_mem; + + spin_lock(&rmc->lock); + list_for_each_entry(shrd_mem, &rmc->shrd_mem_list, list) + if (shrd_mem->srv->prog == srv->prog) + if (shrd_mem->smem_info) + shrd_mem->smem_info->client_sts = !!enable; + spin_unlock(&rmc->lock); +} + +static DEVICE_ATTR(force_sync, S_IRUGO | S_IWUSR, show_force_sync, NULL); +static DEVICE_ATTR(sync_sts, S_IRUGO | S_IWUSR, show_sync_sts, NULL); +static struct attribute *dev_attrs[] = { + &dev_attr_force_sync.attr, + &dev_attr_sync_sts.attr, + NULL, +}; +static struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +static void handle_restart_teardown(struct msm_rpc_client *client) +{ + struct rmt_storage_srv *srv; + + srv = rmt_storage_get_srv(client->prog); + if (!srv) + return; + pr_debug("%s: Modem restart for 0x%08x\n", __func__, srv->prog); + cancel_delayed_work_sync(&srv->restart_work); +} + +#define RESTART_WORK_DELAY_MS 1000 + +static void handle_restart_setup(struct msm_rpc_client *client) +{ + struct rmt_storage_srv *srv; + + srv = rmt_storage_get_srv(client->prog); + if (!srv) + return; + pr_debug("%s: Scheduling restart for 0x%08x\n", __func__, srv->prog); + queue_delayed_work(rmc->workq, &srv->restart_work, + msecs_to_jiffies(RESTART_WORK_DELAY_MS)); +} + +static int rmt_storage_reg_callbacks(struct msm_rpc_client *client) +{ + int ret; + + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_OPEN_PROC, + RMT_STORAGE_EVNT_OPEN, + rmt_storage_event_open_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_CB_PROC, + RMT_STORAGE_EVNT_CLOSE, + rmt_storage_event_close_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_CB_PROC, + RMT_STORAGE_EVNT_WRITE_BLOCK, + rmt_storage_event_write_block_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_CB_PROC, + RMT_STORAGE_EVNT_GET_DEV_ERROR, + rmt_storage_event_get_err_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_WRITE_IOVEC_PROC, + RMT_STORAGE_EVNT_WRITE_IOVEC, + rmt_storage_event_write_iovec_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_READ_IOVEC_PROC, + RMT_STORAGE_EVNT_READ_IOVEC, + rmt_storage_event_read_iovec_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_CB_PROC, + RMT_STORAGE_EVNT_SEND_USER_DATA, + rmt_storage_event_user_data_cb); + if (ret) + return ret; + ret = rmt_storage_reg_cb(client, + RMT_STORAGE_REGISTER_ALLOC_RMT_BUF_PROC, + RMT_STORAGE_EVNT_ALLOC_RMT_BUF, + rmt_storage_event_alloc_rmt_buf_cb); + if (ret) + pr_info("%s: Unable (%d) registering aloc_rmt_buf\n", + __func__, ret); + + pr_debug("%s: Callbacks (re)registered for 0x%08x\n\n", __func__, + client->prog); + return 0; +} + +static void rmt_storage_restart_work(struct work_struct *work) +{ + struct rmt_storage_srv *srv; + int ret; + + srv = container_of((struct delayed_work *)work, + struct rmt_storage_srv, restart_work); + if (!rmt_storage_get_srv(srv->prog)) { + pr_err("%s: Invalid server\n", __func__); + return; + } + + ret = rmt_storage_reg_callbacks(srv->rpc_client); + if (!ret) + return; + + pr_err("%s: Error (%d) re-registering callbacks for0x%08x\n", + __func__, ret, srv->prog); + + if (!msm_rpc_client_in_reset(srv->rpc_client)) + queue_delayed_work(rmc->workq, &srv->restart_work, + msecs_to_jiffies(RESTART_WORK_DELAY_MS)); +} + +static int rmt_storage_probe(struct platform_device *pdev) +{ + struct rpcsvr_platform_device *dev; + struct rmt_storage_srv *srv; + int ret; + + dev = container_of(pdev, struct rpcsvr_platform_device, base); + srv = rmt_storage_get_srv(dev->prog); + if (!srv) { + pr_err("%s: Invalid prog = %#x\n", __func__, dev->prog); + return -ENXIO; + } + + rmt_storage_init_ramfs(srv); + rmt_storage_get_ramfs(srv); + + INIT_DELAYED_WORK(&srv->restart_work, rmt_storage_restart_work); + + /* Client Registration */ + srv->rpc_client = msm_rpc_register_client2("rmt_storage", + dev->prog, dev->vers, 1, + handle_rmt_storage_call); + if (IS_ERR(srv->rpc_client)) { + pr_err("%s: Unable to register client (prog %.8x vers %.8x)\n", + __func__, dev->prog, dev->vers); + ret = PTR_ERR(srv->rpc_client); + return ret; + } + + ret = msm_rpc_register_reset_callbacks(srv->rpc_client, + handle_restart_teardown, + handle_restart_setup); + if (ret) + goto unregister_client; + + pr_info("%s: Remote storage RPC client (0x%x)initialized\n", + __func__, dev->prog); + + /* register server callbacks */ + ret = rmt_storage_reg_callbacks(srv->rpc_client); + if (ret) + goto unregister_client; + + /* For targets that poll SMEM, set status to ready */ + rmt_storage_set_client_status(srv, 1); + + ret = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp); + if (ret) + pr_err("%s: Failed to create sysfs node: %d\n", __func__, ret); + + return 0; + +unregister_client: + msm_rpc_unregister_client(srv->rpc_client); + return ret; +} + +static void rmt_storage_client_shutdown(struct platform_device *pdev) +{ + struct rpcsvr_platform_device *dev; + struct rmt_storage_srv *srv; + + dev = container_of(pdev, struct rpcsvr_platform_device, base); + srv = rmt_storage_get_srv(dev->prog); + rmt_storage_set_client_status(srv, 0); +} + +static void rmt_storage_destroy_rmc(void) +{ + wake_lock_destroy(&rmc->wlock); +} + +static void __init rmt_storage_init_client_info(void) +{ + /* Initialization */ + init_waitqueue_head(&rmc->event_q); + spin_lock_init(&rmc->lock); + atomic_set(&rmc->total_events, 0); + INIT_LIST_HEAD(&rmc->event_list); + INIT_LIST_HEAD(&rmc->client_list); + INIT_LIST_HEAD(&rmc->shrd_mem_list); + /* The client expects a non-zero return value for + * its open requests. Hence reserve 0 bit. */ + __set_bit(0, &rmc->cids); + atomic_set(&rmc->wcount, 0); + wake_lock_init(&rmc->wlock, WAKE_LOCK_SUSPEND, "rmt_storage"); +} + +static struct rmt_storage_srv msm_srv = { + .prog = MSM_RMT_STORAGE_APIPROG, + .plat_drv = { + .probe = rmt_storage_probe, + .shutdown = rmt_storage_client_shutdown, + .driver = { + .name = "rs300000a7", + .owner = THIS_MODULE, + }, + }, +}; + +static struct rmt_storage_srv mdm_srv = { + .prog = MDM_RMT_STORAGE_APIPROG, + .plat_drv = { + .probe = rmt_storage_probe, + .shutdown = rmt_storage_client_shutdown, + .driver = { + .name = "rs300100a7", + .owner = THIS_MODULE, + }, + }, +}; + +static struct rmt_storage_srv *rmt_storage_get_srv(uint32_t prog) +{ + if (prog == MSM_RMT_STORAGE_APIPROG) + return &msm_srv; + if (prog == MDM_RMT_STORAGE_APIPROG) + return &mdm_srv; + return NULL; +} + + +static uint32_t rmt_storage_get_sid(const char *path) +{ + if (!strncmp(path, "/boot/modem_fs1", MAX_PATH_NAME)) + return RAMFS_MODEMSTORAGE_ID; + if (!strncmp(path, "/boot/modem_fs2", MAX_PATH_NAME)) + return RAMFS_MODEMSTORAGE_ID; + if (!strncmp(path, "/boot/modem_fsg", MAX_PATH_NAME)) + return RAMFS_MODEMSTORAGE_ID; + if (!strncmp(path, "/q6_fs1_parti_id_0x59", MAX_PATH_NAME)) + return RAMFS_MDM_STORAGE_ID; + if (!strncmp(path, "/q6_fs2_parti_id_0x5A", MAX_PATH_NAME)) + return RAMFS_MDM_STORAGE_ID; + if (!strncmp(path, "/q6_fsg_parti_id_0x5B", MAX_PATH_NAME)) + return RAMFS_MDM_STORAGE_ID; + if (!strncmp(path, "ssd", MAX_PATH_NAME)) + return RAMFS_SSD_STORAGE_ID; + return 0; +} + +static int __init rmt_storage_init(void) +{ +#ifdef CONFIG_MSM_SDIO_SMEM + void *mdm_local_buf; +#endif + int ret = 0; + + rmc = kzalloc(sizeof(struct rmt_storage_client_info), GFP_KERNEL); + if (!rmc) { + pr_err("%s: Unable to allocate memory\n", __func__); + return -ENOMEM; + } + rmt_storage_init_client_info(); + + ret = platform_driver_register(&msm_srv.plat_drv); + if (ret) { + pr_err("%s: Unable to register MSM RPC driver\n", __func__); + goto rmc_free; + } + + ret = platform_driver_register(&mdm_srv.plat_drv); + if (ret) { + pr_err("%s: Unable to register MDM RPC driver\n", __func__); + goto unreg_msm_rpc; + } + + ret = misc_register(&rmt_storage_device); + if (ret) { + pr_err("%s: Unable to register misc device %d\n", __func__, + MISC_DYNAMIC_MINOR); + goto unreg_mdm_rpc; + } + +#ifdef CONFIG_MSM_SDIO_SMEM + mdm_local_buf = kzalloc(MDM_LOCAL_BUF_SZ, GFP_KERNEL); + if (!mdm_local_buf) { + pr_err("%s: Unable to allocate shadow mem\n", __func__); + ret = -ENOMEM; + goto unreg_misc; + } + + ret = rmt_storage_add_shrd_mem(RAMFS_MDM_STORAGE_ID, + __pa(mdm_local_buf), + MDM_LOCAL_BUF_SZ, + NULL, NULL, &mdm_srv); + if (ret) { + pr_err("%s: Unable to add shadow mem entry\n", __func__); + goto free_mdm_local_buf; + } + + pr_debug("%s: Shadow memory at %p (phys=%lx), %d bytes\n", __func__, + mdm_local_buf, __pa(mdm_local_buf), MDM_LOCAL_BUF_SZ); +#endif + + rmc->workq = create_singlethread_workqueue("rmt_storage"); + if (!rmc->workq) + return -ENOMEM; + +#ifdef CONFIG_MSM_RMT_STORAGE_CLIENT_STATS + stats_dentry = debugfs_create_file("rmt_storage_stats", 0444, 0, + NULL, &debug_ops); + if (!stats_dentry) + pr_err("%s: Failed to create stats debugfs file\n", __func__); +#endif + return 0; + +#ifdef CONFIG_MSM_SDIO_SMEM +free_mdm_local_buf: + kfree(mdm_local_buf); +unreg_misc: + misc_deregister(&rmt_storage_device); +#endif +unreg_mdm_rpc: + platform_driver_unregister(&mdm_srv.plat_drv); +unreg_msm_rpc: + platform_driver_unregister(&msm_srv.plat_drv); +rmc_free: + rmt_storage_destroy_rmc(); + kfree(rmc); + return ret; +} + +module_init(rmt_storage_init); +MODULE_DESCRIPTION("Remote Storage RPC Client"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/rpc_dog_keepalive.c b/arch/arm/mach-msm/rpc_dog_keepalive.c new file mode 100644 index 00000000000..609b1258bfb --- /dev/null +++ b/arch/arm/mach-msm/rpc_dog_keepalive.c @@ -0,0 +1,240 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * DOG KEEPALIVE RPC CLIENT MODULE + */ + +#include +#include +#include +#include +#include + +#define DOG_KEEPALIVE_PROG 0x300000A2 +#define DOG_KEEPALIVE_VERS 0x00010001 + +#define DOG_KEEPALIVE_REGISTER_PROC 2 +#define DOG_KEEPALIVE_UNREGISTER_PROC 3 + +#define DOG_KEEPALIVE_CB_PROC 1 + +static int dog_keepalive_debug; +module_param_named(debug, dog_keepalive_debug, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(DEBUG) +#define DBG(x...) do { \ + if (dog_keepalive_debug) \ + printk(KERN_INFO x); \ + } while (0) +#else +#define DBG(x...) do { } while (0) +#endif + +static struct msm_rpc_client *dog_keepalive_rpc_client; +static int32_t dog_clnt_id = -1; + +struct dog_keepalive_cb_arg { + uint32_t cb_id; +}; + +struct dog_keepalive_cb_ret { + uint32_t result; +}; + +static int dog_keepalive_cb(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr) +{ + int rc; + void *cb_func; + uint32_t accept_status; + struct dog_keepalive_cb_arg arg; + struct dog_keepalive_cb_ret ret; + + xdr_recv_uint32(xdr, &arg.cb_id); /* cb_id */ + + cb_func = msm_rpc_get_cb_func(client, arg.cb_id); + if (cb_func) { + rc = ((int (*) + (struct dog_keepalive_cb_arg *, + struct dog_keepalive_cb_ret *)) + cb_func)(&arg, &ret); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + xdr_start_accepted_reply(xdr, accept_status); + + if (accept_status == RPC_ACCEPTSTAT_SUCCESS) + xdr_send_uint32(xdr, &ret.result); /* result */ + + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int dog_keepalive_cb_func(struct msm_rpc_client *client, + struct rpc_request_hdr *req, + struct msm_rpc_xdr *xdr) +{ + int rc = 0; + + switch (req->procedure) { + case DOG_KEEPALIVE_CB_PROC: + rc = dog_keepalive_cb(client, xdr); + break; + default: + pr_err("%s: procedure not supported %d\n", + __func__, req->procedure); + xdr_start_accepted_reply(xdr, RPC_ACCEPTSTAT_PROC_UNAVAIL); + rc = xdr_send_msg(xdr); + if (rc) + pr_err("%s: sending reply failed: %d\n", __func__, rc); + break; + } + return rc; +} + +struct dog_keepalive_register_arg { + int (*cb_func)( + struct dog_keepalive_cb_arg *arg, + struct dog_keepalive_cb_ret *ret); + uint32_t response_msec; + uint32_t clnt_id_valid; +}; + +struct dog_keepalive_register_ret { + uint32_t *clnt_id; + uint32_t result; +}; + +static int dog_keepalive_register_arg_func(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct dog_keepalive_register_arg *arg = data; + int cb_id; + + /* cb_func */ + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + xdr_send_uint32(xdr, &cb_id); + xdr_send_uint32(xdr, &arg->response_msec); /* response_msec */ + xdr_send_uint32(xdr, &arg->clnt_id_valid); /* clnt_id valid */ + return 0; +} + +static int dog_keepalive_register_ret_func(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data) +{ + struct dog_keepalive_register_ret *ret = data; + + /* clnt_id */ + xdr_recv_pointer(xdr, (void **)&(ret->clnt_id), sizeof(uint32_t), + xdr_recv_uint32); + + /* result */ + xdr_recv_uint32(xdr, &ret->result); + return 0; +} + +static int dog_keepalive_register_func(struct msm_rpc_client *client, + struct dog_keepalive_register_arg *arg, + struct dog_keepalive_register_ret *ret) +{ + return msm_rpc_client_req2(client, + DOG_KEEPALIVE_REGISTER_PROC, + dog_keepalive_register_arg_func, arg, + dog_keepalive_register_ret_func, ret, -1); +} + +static int dog_keepalive_cb_proc_func(struct dog_keepalive_cb_arg *arg, + struct dog_keepalive_cb_ret *ret) +{ + DBG("%s: received, client %d \n", __func__, dog_clnt_id); + ret->result = 1; + return 0; +} + +static void dog_keepalive_register(void) +{ + struct dog_keepalive_register_arg arg; + struct dog_keepalive_register_ret ret; + int rc; + + arg.cb_func = dog_keepalive_cb_proc_func; + arg.response_msec = 1000; + arg.clnt_id_valid = 1; + ret.clnt_id = NULL; + rc = dog_keepalive_register_func(dog_keepalive_rpc_client, + &arg, &ret); + if (rc) + pr_err("%s: register request failed\n", __func__); + else + dog_clnt_id = *ret.clnt_id; + + kfree(ret.clnt_id); + DBG("%s: register complete\n", __func__); +} + +/* Registration with the platform driver for notification on the availability + * of the DOG_KEEPALIVE remote server + */ +static int dog_keepalive_init_probe(struct platform_device *pdev) +{ + DBG("%s: probe called\n", __func__); + dog_keepalive_rpc_client = msm_rpc_register_client2( + "dog-keepalive", + DOG_KEEPALIVE_PROG, + DOG_KEEPALIVE_VERS, + 0, dog_keepalive_cb_func); + + if (IS_ERR(dog_keepalive_rpc_client)) { + pr_err("%s: RPC client creation failed\n", __func__); + return PTR_ERR(dog_keepalive_rpc_client); + } + + /* Send RPC call to register for callbacks */ + dog_keepalive_register(); + + return 0; +} + +static char dog_keepalive_driver_name[] = "rs00000000"; + +static struct platform_driver dog_keepalive_init_driver = { + .probe = dog_keepalive_init_probe, + .driver = { + .owner = THIS_MODULE, + }, +}; + +static int __init rpc_dog_keepalive_init(void) +{ + snprintf(dog_keepalive_driver_name, sizeof(dog_keepalive_driver_name), + "rs%08x", DOG_KEEPALIVE_PROG); + dog_keepalive_init_driver.driver.name = dog_keepalive_driver_name; + + return platform_driver_register(&dog_keepalive_init_driver); +} + +late_initcall(rpc_dog_keepalive_init); +MODULE_DESCRIPTION("DOG KEEPALIVE RPC CLIENT"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/rpc_fsusb.c b/arch/arm/mach-msm/rpc_fsusb.c new file mode 100644 index 00000000000..4692d945ad0 --- /dev/null +++ b/arch/arm/mach-msm/rpc_fsusb.c @@ -0,0 +1,244 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#define PM_APP_OTG_PROG 0x30000080 +#define PM_APP_OTG_VERS 0x00010001 + +#define PM_APP_OTG_INIT_PHY 17 +#define PM_APP_OTG_RESET_PHY 18 +#define PM_APP_OTG_SUSPEND_PHY 7 +#define PM_APP_OTG_RESUME_PHY 8 +#define PM_APP_OTG_DEV_DISCONNECTED 9 +#define PM_APP_OTG_SET_WAKEUP 10 +#define PM_APP_OTG_ACQUIRE_BUS 3 +#define PM_APP_OTG_RELINQUISH_BUS 4 + +#define PM_APP_OTG_INIT_DONE_CB_PROC 1 +#define PM_APP_OTG_HOST_INIT_CB_PROC 3 +#define PM_APP_OTG_REMOTE_DEV_LOST_CB_PROC 8 +#define PM_APP_OTG_REMOTE_DEV_RESUMED_CB_PROC 9 +#define PM_APP_OTG_ERROR_NOTIFY_CB_PROC 11 + +#define NUM_OF_CALLBACKS 11 +static struct msm_rpc_client *client; +static struct msm_otg_ops *host_ops; + +static int msm_fsusb_rpc_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + int i, size = 0; + uint32_t proc = *(uint32_t *)data; + + switch (proc) { + case PM_APP_OTG_INIT_PHY: { + for (i = 0; i < NUM_OF_CALLBACKS; i++) { + *((uint32_t *)buf) = cpu_to_be32(0x11111111); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + } + + /* sleep_assert callback fucntion will be registered locally*/ + *((uint32_t *)buf) = cpu_to_be32(0xffffffff); + size += sizeof(uint32_t); + break; + } + case PM_APP_OTG_SET_WAKEUP: { + *((uint32_t *)buf) = cpu_to_be32(1); + size += sizeof(uint32_t); + break; + } + case PM_APP_OTG_ACQUIRE_BUS: { + *((uint32_t *)buf) = cpu_to_be32(0xffffffff); + size += sizeof(uint32_t); + break; + } + default: + pr_info("%s: No arguments expected\n", __func__); + } + return size; +} + +int msm_fsusb_init_phy(void) +{ + uint32_t data = PM_APP_OTG_INIT_PHY; + + return msm_rpc_client_req(client, + PM_APP_OTG_INIT_PHY, + msm_fsusb_rpc_arg, &data, + NULL, NULL, -1); +} +EXPORT_SYMBOL(msm_fsusb_init_phy); + +int msm_fsusb_reset_phy(void) +{ + return msm_rpc_client_req(client, + PM_APP_OTG_RESET_PHY, + NULL, NULL, + NULL, NULL, -1); + +} +EXPORT_SYMBOL(msm_fsusb_reset_phy); + +int msm_fsusb_suspend_phy(void) +{ + return msm_rpc_client_req(client, + PM_APP_OTG_SUSPEND_PHY, + NULL, NULL, + NULL, NULL, -1); + +} +EXPORT_SYMBOL(msm_fsusb_suspend_phy); + +int msm_fsusb_resume_phy(void) +{ + return msm_rpc_client_req(client, + PM_APP_OTG_RESUME_PHY, + NULL, NULL, + NULL, NULL, -1); + +} +EXPORT_SYMBOL(msm_fsusb_resume_phy); + +int msm_fsusb_remote_dev_disconnected(void) +{ + return msm_rpc_client_req(client, + PM_APP_OTG_DEV_DISCONNECTED, + NULL, NULL, + NULL, NULL, -1); + +} +EXPORT_SYMBOL(msm_fsusb_remote_dev_disconnected); + +int msm_fsusb_set_remote_wakeup(void) +{ + uint32_t data = PM_APP_OTG_SET_WAKEUP; + + return msm_rpc_client_req(client, + PM_APP_OTG_SET_WAKEUP, + msm_fsusb_rpc_arg, &data, + NULL, NULL, -1); + +} +EXPORT_SYMBOL(msm_fsusb_set_remote_wakeup); + +static int msm_fsusb_acquire_bus(void) +{ + uint32_t data = PM_APP_OTG_ACQUIRE_BUS; + + return msm_rpc_client_req(client, + PM_APP_OTG_ACQUIRE_BUS, + msm_fsusb_rpc_arg, &data, + NULL, NULL, -1); + +} + +static int msm_fsusb_relinquish_bus(void) +{ + return msm_rpc_client_req(client, + PM_APP_OTG_RELINQUISH_BUS, + NULL, NULL, + NULL, NULL, -1); + +} + +static void msm_fsusb_request_session(void) +{ + int ret; + + ret = msm_fsusb_relinquish_bus(); + if (ret < 0) + pr_err("relinquish_bus rpc failed\n"); + ret = msm_fsusb_acquire_bus(); + if (ret < 0) + pr_err("acquire_bus rpc failed\n"); +} + +static int msm_fsusb_cb_func(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + struct rpc_request_hdr *req; + int rc; + + req = buffer; + + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + RPC_ACCEPTSTAT_SUCCESS); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) { + pr_err("%s: sending reply failed: %d\n", __func__, rc); + return rc; + } + + switch (be32_to_cpu(req->procedure)) { + case PM_APP_OTG_INIT_DONE_CB_PROC: { + pr_debug("pm_app_otg_init_done callback received"); + msm_fsusb_request_session(); + break; + } + case PM_APP_OTG_HOST_INIT_CB_PROC: { + pr_debug("pm_app_otg_host_init_cb_proc callback received"); + host_ops->request(host_ops->handle, REQUEST_START); + break; + } + case PM_APP_OTG_REMOTE_DEV_LOST_CB_PROC: { + pr_debug("pm_app_otg_remote_dev_lost_cb_proc" + " callback received"); + msm_fsusb_acquire_bus(); + host_ops->request(host_ops->handle, REQUEST_STOP); + break; + } + case PM_APP_OTG_REMOTE_DEV_RESUMED_CB_PROC: { + pr_debug("pm_app_otg_remote_dev_resumed_cb_proc" + "callback received"); + host_ops->request(host_ops->handle, REQUEST_RESUME); + break; + } + case PM_APP_OTG_ERROR_NOTIFY_CB_PROC: { + pr_err("pm_app_otg_error_notify_cb_proc callback received"); + break; + } + default: + pr_err("%s: unknown callback(proc = %d) received\n", + __func__, req->procedure); + } + return 0; +} + +int msm_fsusb_rpc_init(struct msm_otg_ops *ops) +{ + host_ops = ops; + client = msm_rpc_register_client("fsusb", + PM_APP_OTG_PROG, + PM_APP_OTG_VERS, 1, + msm_fsusb_cb_func); + if (IS_ERR(client)) { + pr_err("%s: couldn't open rpc client\n", __func__); + return PTR_ERR(client); + } + + return 0; + +} +EXPORT_SYMBOL(msm_fsusb_rpc_init); + +void msm_fsusb_rpc_deinit(void) +{ + msm_rpc_unregister_client(client); +} +EXPORT_SYMBOL(msm_fsusb_rpc_deinit); diff --git a/arch/arm/mach-msm/rpc_hsusb.c b/arch/arm/mach-msm/rpc_hsusb.c new file mode 100644 index 00000000000..cd5f6124df5 --- /dev/null +++ b/arch/arm/mach-msm/rpc_hsusb.c @@ -0,0 +1,660 @@ +/* linux/arch/arm/mach-msm/rpc_hsusb.c + * + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include +#include +#include +#include + +static struct msm_rpc_endpoint *usb_ep; +static struct msm_rpc_endpoint *chg_ep; + +#define MSM_RPC_CHG_PROG 0x3000001a + +struct msm_chg_rpc_ids { + unsigned long vers_comp; + unsigned chg_usb_charger_connected_proc; + unsigned chg_usb_charger_disconnected_proc; + unsigned chg_usb_i_is_available_proc; + unsigned chg_usb_i_is_not_available_proc; +}; + +struct msm_hsusb_rpc_ids { + unsigned long prog; + unsigned long vers_comp; + unsigned long init_phy; + unsigned long vbus_pwr_up; + unsigned long vbus_pwr_down; + unsigned long update_product_id; + unsigned long update_serial_num; + unsigned long update_is_serial_num_null; + unsigned long reset_rework_installed; + unsigned long enable_pmic_ulpi_data0; + unsigned long disable_pmic_ulpi_data0; +}; + +static struct msm_hsusb_rpc_ids usb_rpc_ids; +static struct msm_chg_rpc_ids chg_rpc_ids; + +static int msm_hsusb_init_rpc_ids(unsigned long vers) +{ + if (vers == 0x00010001) { + usb_rpc_ids.prog = 0x30000064; + usb_rpc_ids.vers_comp = 0x00010001; + usb_rpc_ids.init_phy = 2; + usb_rpc_ids.vbus_pwr_up = 6; + usb_rpc_ids.vbus_pwr_down = 7; + usb_rpc_ids.update_product_id = 8; + usb_rpc_ids.update_serial_num = 9; + usb_rpc_ids.update_is_serial_num_null = 10; + usb_rpc_ids.reset_rework_installed = 17; + usb_rpc_ids.enable_pmic_ulpi_data0 = 18; + usb_rpc_ids.disable_pmic_ulpi_data0 = 19; + return 0; + } else if (vers == 0x00010002) { + usb_rpc_ids.prog = 0x30000064; + usb_rpc_ids.vers_comp = 0x00010002; + usb_rpc_ids.init_phy = 2; + usb_rpc_ids.vbus_pwr_up = 6; + usb_rpc_ids.vbus_pwr_down = 7; + usb_rpc_ids.update_product_id = 8; + usb_rpc_ids.update_serial_num = 9; + usb_rpc_ids.update_is_serial_num_null = 10; + usb_rpc_ids.reset_rework_installed = 17; + usb_rpc_ids.enable_pmic_ulpi_data0 = 18; + usb_rpc_ids.disable_pmic_ulpi_data0 = 19; + return 0; + } else { + pr_err("%s: no matches found for version\n", + __func__); + return -ENODATA; + } +} + +static int msm_chg_init_rpc(unsigned long vers) +{ + if (((vers & RPC_VERSION_MAJOR_MASK) == 0x00010000) || + ((vers & RPC_VERSION_MAJOR_MASK) == 0x00020000) || + ((vers & RPC_VERSION_MAJOR_MASK) == 0x00030000) || + ((vers & RPC_VERSION_MAJOR_MASK) == 0x00040000)) { + chg_ep = msm_rpc_connect_compatible(MSM_RPC_CHG_PROG, vers, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(chg_ep)) + return -ENODATA; + chg_rpc_ids.vers_comp = vers; + chg_rpc_ids.chg_usb_charger_connected_proc = 7; + chg_rpc_ids.chg_usb_charger_disconnected_proc = 8; + chg_rpc_ids.chg_usb_i_is_available_proc = 9; + chg_rpc_ids.chg_usb_i_is_not_available_proc = 10; + return 0; + } else + return -ENODATA; +} + +/* rpc connect for hsusb */ +int msm_hsusb_rpc_connect(void) +{ + + if (usb_ep && !IS_ERR(usb_ep)) { + pr_debug("%s: usb_ep already connected\n", __func__); + return 0; + } + + /* Initialize rpc ids */ + if (msm_hsusb_init_rpc_ids(0x00010001)) { + pr_err("%s: rpc ids initialization failed\n" + , __func__); + return -ENODATA; + } + + usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog, + usb_rpc_ids.vers_comp, + MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(usb_ep)) { + pr_err("%s: connect compatible failed vers = %lx\n", + __func__, usb_rpc_ids.vers_comp); + + /* Initialize rpc ids */ + if (msm_hsusb_init_rpc_ids(0x00010002)) { + pr_err("%s: rpc ids initialization failed\n", + __func__); + return -ENODATA; + } + usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog, + usb_rpc_ids.vers_comp, + MSM_RPC_UNINTERRUPTIBLE); + } + + if (IS_ERR(usb_ep)) { + pr_err("%s: connect compatible failed vers = %lx\n", + __func__, usb_rpc_ids.vers_comp); + return -EAGAIN; + } else + pr_debug("%s: rpc connect success vers = %lx\n", + __func__, usb_rpc_ids.vers_comp); + + return 0; +} +EXPORT_SYMBOL(msm_hsusb_rpc_connect); + +/* rpc connect for charging */ +int msm_chg_rpc_connect(void) +{ + uint32_t chg_vers; + + if (machine_is_msm7x27_surf() || machine_is_qsd8x50_surf()) + return -ENOTSUPP; + + if (chg_ep && !IS_ERR(chg_ep)) { + pr_debug("%s: chg_ep already connected\n", __func__); + return 0; + } + + chg_vers = 0x00040001; + if (!msm_chg_init_rpc(chg_vers)) + goto chg_found; + + chg_vers = 0x00030001; + if (!msm_chg_init_rpc(chg_vers)) + goto chg_found; + + chg_vers = 0x00020001; + if (!msm_chg_init_rpc(chg_vers)) + goto chg_found; + + chg_vers = 0x00010001; + if (!msm_chg_init_rpc(chg_vers)) + goto chg_found; + + pr_err("%s: connect compatible failed \n", + __func__); + return -EAGAIN; + +chg_found: + pr_debug("%s: connected to rpc vers = %x\n", + __func__, chg_vers); + return 0; +} +EXPORT_SYMBOL(msm_chg_rpc_connect); + +/* rpc call for phy_reset */ +int msm_hsusb_phy_reset(void) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: phy_reset rpc failed before call," + "rc = %ld\n", __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call(usb_ep, usb_rpc_ids.init_phy, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: phy_reset rpc failed! rc = %d\n", + __func__, rc); + } else + pr_debug("msm_hsusb_phy_reset\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_phy_reset); + +/* rpc call for vbus powerup */ +int msm_hsusb_vbus_powerup(void) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: vbus_powerup rpc failed before call," + "rc = %ld\n", __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_up, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: vbus_powerup failed! rc = %d\n", + __func__, rc); + } else + pr_debug("msm_hsusb_vbus_powerup\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_vbus_powerup); + +/* rpc call for vbus shutdown */ +int msm_hsusb_vbus_shutdown(void) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: vbus_shutdown rpc failed before call," + "rc = %ld\n", __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_down, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: vbus_shutdown failed! rc = %d\n", + __func__, rc); + } else + pr_debug("msm_hsusb_vbus_shutdown\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_vbus_shutdown); + +int msm_hsusb_send_productID(uint32_t product_id) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + uint32_t product_id; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: rpc connect failed: rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + req.product_id = cpu_to_be32(product_id); + rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_product_id, + &req, sizeof(req), + 5 * HZ); + if (rc < 0) + pr_err("%s: rpc call failed! error: %d\n", + __func__, rc); + else + pr_debug("%s: rpc call success\n" , __func__); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_send_productID); + +int msm_hsusb_send_serial_number(const char *serial_number) +{ + int rc = 0, serial_len, rlen; + struct hsusb_send_sn_req { + struct rpc_request_hdr hdr; + uint32_t length; + char sn[0]; + } *req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: rpc connect failed: rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + /* + * USB driver passes null terminated string to us. Modem processor + * expects serial number to be 32 bit aligned. + */ + serial_len = strlen(serial_number)+1; + rlen = sizeof(struct rpc_request_hdr) + sizeof(uint32_t) + + ((serial_len + 3) & ~3); + + req = kmalloc(rlen, GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->length = cpu_to_be32(serial_len); + strncpy(req->sn , serial_number, serial_len); + rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_serial_num, + req, rlen, 5 * HZ); + if (rc < 0) + pr_err("%s: rpc call failed! error: %d\n", + __func__, rc); + else + pr_debug("%s: rpc call success\n", __func__); + + kfree(req); + return rc; +} +EXPORT_SYMBOL(msm_hsusb_send_serial_number); + +int msm_hsusb_is_serial_num_null(uint32_t val) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + uint32_t value; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: rpc connect failed: rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + if (!usb_rpc_ids.update_is_serial_num_null) { + pr_err("%s: proc id not supported \n", __func__); + return -ENODATA; + } + + req.value = cpu_to_be32(val); + rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_is_serial_num_null, + &req, sizeof(req), + 5 * HZ); + if (rc < 0) + pr_err("%s: rpc call failed! error: %d\n" , + __func__, rc); + else + pr_debug("%s: rpc call success\n", __func__); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_is_serial_num_null); + +int msm_chg_usb_charger_connected(uint32_t device) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + uint32_t otg_dev; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + req.otg_dev = cpu_to_be32(device); + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_connected_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: charger_connected failed! rc = %d\n", + __func__, rc); + } else + pr_debug("msm_chg_usb_charger_connected\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_charger_connected); + +int msm_chg_usb_i_is_available(uint32_t sample) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + uint32_t i_ma; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + req.i_ma = cpu_to_be32(sample); + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_available_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: charger_i_available failed! rc = %d\n", + __func__, rc); + } else + pr_debug("msm_chg_usb_i_is_available(%u)\n", sample); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_i_is_available); + +int msm_chg_usb_i_is_not_available(void) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_not_available_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: charger_i_not_available failed! rc =" + "%d \n", __func__, rc); + } else + pr_debug("msm_chg_usb_i_is_not_available\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_i_is_not_available); + +int msm_chg_usb_charger_disconnected(void) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_disconnected_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + pr_err("%s: charger_disconnected failed! rc = %d\n", + __func__, rc); + } else + pr_debug("msm_chg_usb_charger_disconnected\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_charger_disconnected); + +/* rpc call to close connection */ +int msm_hsusb_rpc_close(void) +{ + int rc = 0; + + if (IS_ERR(usb_ep)) { + pr_err("%s: rpc_close failed before call, rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_close(usb_ep); + usb_ep = NULL; + + if (rc < 0) { + pr_err("%s: close rpc failed! rc = %d\n", + __func__, rc); + return -EAGAIN; + } else + pr_debug("rpc close success\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_rpc_close); + +/* rpc call to close charging connection */ +int msm_chg_rpc_close(void) +{ + int rc = 0; + + if (IS_ERR(chg_ep)) { + pr_err("%s: rpc_close failed before call, rc = %ld\n", + __func__, PTR_ERR(chg_ep)); + return -EAGAIN; + } + + rc = msm_rpc_close(chg_ep); + chg_ep = NULL; + + if (rc < 0) { + pr_err("%s: close rpc failed! rc = %d\n", + __func__, rc); + return -EAGAIN; + } else + pr_debug("rpc close success\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_rpc_close); + +int msm_hsusb_reset_rework_installed(void) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + struct hsusb_rpc_rep { + struct rpc_reply_hdr hdr; + uint32_t rework; + } rep; + + memset(&rep, 0, sizeof(rep)); + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call_reply(usb_ep, usb_rpc_ids.reset_rework_installed, + &req, sizeof(req), + &rep, sizeof(rep), 5 * HZ); + + if (rc < 0) { + pr_err("%s: rpc call failed! error: (%d)" + "proc id: (%lx)\n", + __func__, rc, + usb_rpc_ids.reset_rework_installed); + return rc; + } + + pr_info("%s: rework: (%d)\n", __func__, rep.rework); + return be32_to_cpu(rep.rework); +} +EXPORT_SYMBOL(msm_hsusb_reset_rework_installed); + +static int msm_hsusb_pmic_ulpidata0_config(int enable) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + if (enable) + rc = msm_rpc_call(usb_ep, usb_rpc_ids.enable_pmic_ulpi_data0, + &req, sizeof(req), 5 * HZ); + else + rc = msm_rpc_call(usb_ep, usb_rpc_ids.disable_pmic_ulpi_data0, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) + pr_err("%s: rpc call failed! error: %d\n", + __func__, rc); + return rc; +} + +int msm_hsusb_enable_pmic_ulpidata0(void) +{ + return msm_hsusb_pmic_ulpidata0_config(1); +} +EXPORT_SYMBOL(msm_hsusb_enable_pmic_ulpidata0); + +int msm_hsusb_disable_pmic_ulpidata0(void) +{ + return msm_hsusb_pmic_ulpidata0_config(0); +} +EXPORT_SYMBOL(msm_hsusb_disable_pmic_ulpidata0); + + +/* wrapper for sending pid and serial# info to bootloader */ +int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum) +{ + int ret; + + ret = msm_hsusb_send_productID(pid); + if (ret) + return ret; + + if (!snum) { + ret = msm_hsusb_is_serial_num_null(1); + if (ret) + return ret; + } + + ret = msm_hsusb_is_serial_num_null(0); + if (ret) + return ret; + ret = msm_hsusb_send_serial_number(snum); + if (ret) + return ret; + + return 0; +} + + +#ifdef CONFIG_USB_MSM_72K +/* charger api wrappers */ +int hsusb_chg_init(int connect) +{ + if (connect) + return msm_chg_rpc_connect(); + else + return msm_chg_rpc_close(); +} +EXPORT_SYMBOL(hsusb_chg_init); + +void hsusb_chg_vbus_draw(unsigned mA) +{ + msm_chg_usb_i_is_available(mA); +} +EXPORT_SYMBOL(hsusb_chg_vbus_draw); + +void hsusb_chg_connected(enum chg_type chgtype) +{ + char *chg_types[] = {"STD DOWNSTREAM PORT", + "CARKIT", + "DEDICATED CHARGER", + "INVALID"}; + + if (chgtype == USB_CHG_TYPE__INVALID) { + msm_chg_usb_i_is_not_available(); + msm_chg_usb_charger_disconnected(); + return; + } + + pr_info("\nCharger Type: %s\n", chg_types[chgtype]); + + msm_chg_usb_charger_connected(chgtype); +} +EXPORT_SYMBOL(hsusb_chg_connected); +#endif diff --git a/arch/arm/mach-msm/rpc_pmapp.c b/arch/arm/mach-msm/rpc_pmapp.c new file mode 100644 index 00000000000..0828bb4da85 --- /dev/null +++ b/arch/arm/mach-msm/rpc_pmapp.c @@ -0,0 +1,577 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PMAPP_RPC_PROG 0x30000060 +#define PMAPP_RPC_VER_1_1 0x00010001 +#define PMAPP_RPC_VER_1_2 0x00010002 +#define PMAPP_RPC_VER_2_1 0x00020001 +#define PMAPP_RPC_VER_3_1 0x00030001 +#define PMAPP_RPC_VER_5_1 0x00050001 +#define PMAPP_RPC_VER_6_1 0x00060001 +#define PMAPP_RPC_VER_7_1 0x00070001 + +#define VBUS_SESS_VALID_CB_PROC 1 +#define PM_VOTE_USB_PWR_SEL_SWITCH_APP__HSUSB (1 << 2) +#define PM_USB_PWR_SEL_SWITCH_ID 0 + +#define PMAPP_RPC_TIMEOUT (5*HZ) + +#define PMAPP_DISPLAY_CLOCK_CONFIG_PROC 21 +#define PMAPP_VREG_LEVEL_VOTE_PROC 23 +#define PMAPP_SMPS_CLOCK_VOTE_PROC 26 +#define PMAPP_CLOCK_VOTE_PROC 27 +#define PMAPP_SMPS_MODE_VOTE_PROC 28 +#define PMAPP_VREG_PINCNTRL_VOTE_PROC 30 +#define PMAPP_DISP_BACKLIGHT_SET_PROC 31 +#define PMAPP_DISP_BACKLIGHT_INIT_PROC 32 +#define PMAPP_VREG_LPM_PINCNTRL_VOTE_PROC 34 + +/* Clock voter name max length */ +#define PMAPP_CLOCK_VOTER_ID_LEN 4 + +struct rpc_pmapp_ids { + unsigned long reg_for_vbus_valid; + unsigned long vote_for_vbus_valid_switch; +}; + +static struct rpc_pmapp_ids rpc_ids; +static struct msm_rpc_client *client; + +/* Add newer versions at the top of array */ +static const unsigned int rpc_vers[] = { + PMAPP_RPC_VER_7_1, + PMAPP_RPC_VER_6_1, + PMAPP_RPC_VER_5_1, + PMAPP_RPC_VER_3_1, + PMAPP_RPC_VER_2_1, +}; + +static void rpc_pmapp_init_rpc_ids(unsigned long vers) +{ + if (vers == PMAPP_RPC_VER_1_1) { + rpc_ids.reg_for_vbus_valid = 5; + rpc_ids.vote_for_vbus_valid_switch = 6; + } else if (vers == PMAPP_RPC_VER_1_2) { + rpc_ids.reg_for_vbus_valid = 16; + rpc_ids.vote_for_vbus_valid_switch = 17; + } else if (vers == PMAPP_RPC_VER_2_1) { + rpc_ids.reg_for_vbus_valid = 0; /* NA */ + rpc_ids.vote_for_vbus_valid_switch = 0; /* NA */ + } +} + +struct usb_pwr_sel_switch_args { + uint32_t cmd; + uint32_t switch_id; + uint32_t app_mask; +}; + +static int usb_pwr_sel_switch_arg_cb(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct usb_pwr_sel_switch_args *args = buf; + + args->cmd = cpu_to_be32(*(uint32_t *)data); + args->switch_id = cpu_to_be32(PM_USB_PWR_SEL_SWITCH_ID); + args->app_mask = cpu_to_be32(PM_VOTE_USB_PWR_SEL_SWITCH_APP__HSUSB); + return sizeof(struct usb_pwr_sel_switch_args); +} + +static int msm_pm_app_vote_usb_pwr_sel_switch(uint32_t cmd) +{ + return msm_rpc_client_req(client, + rpc_ids.vote_for_vbus_valid_switch, + usb_pwr_sel_switch_arg_cb, + &cmd, NULL, NULL, -1); +} + +struct vbus_sess_valid_args { + uint32_t cb_id; +}; + +static int vbus_sess_valid_arg_cb(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct vbus_sess_valid_args *args = buf; + + args->cb_id = cpu_to_be32(*(uint32_t *)data); + return sizeof(struct vbus_sess_valid_args); +} + + +int pmic_vote_3p3_pwr_sel_switch(int boost) +{ + int ret; + + ret = msm_pm_app_vote_usb_pwr_sel_switch(boost); + + return ret; +} +EXPORT_SYMBOL(pmic_vote_3p3_pwr_sel_switch); + +struct vbus_sn_notification_args { + uint32_t cb_id; + uint32_t vbus; /* vbus = 0 if VBUS is present */ +}; + +static int vbus_notification_cb(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + struct vbus_sn_notification_args *args; + struct rpc_request_hdr *req = buffer; + int rc; + uint32_t accept_status; + void (*cb_func)(int); + uint32_t cb_id; + int vbus; + + args = (struct vbus_sn_notification_args *) (req + 1); + cb_id = be32_to_cpu(args->cb_id); + vbus = be32_to_cpu(args->vbus); + + cb_func = msm_rpc_get_cb_func(client, cb_id); + if (cb_func) { + cb_func(!vbus); + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + accept_status); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int pm_app_usb_cb_func(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + int rc; + struct rpc_request_hdr *req = buffer; + + switch (be32_to_cpu(req->procedure)) { + case VBUS_SESS_VALID_CB_PROC: + rc = vbus_notification_cb(client, buffer, in_size); + break; + default: + pr_err("%s: procedure not supported %d\n", __func__, + be32_to_cpu(req->procedure)); + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + RPC_ACCEPTSTAT_PROC_UNAVAIL); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) + pr_err("%s: sending reply failed: %d\n", __func__, rc); + break; + } + return rc; +} + +int msm_pm_app_rpc_init(void (*callback)(int online)) +{ + uint32_t cb_id, rc; + + if (!machine_is_qsd8x50_ffa() && !machine_is_msm7x27_ffa()) + return -ENOTSUPP; + + client = msm_rpc_register_client("pmapp_usb", + PMAPP_RPC_PROG, + PMAPP_RPC_VER_2_1, 1, + pm_app_usb_cb_func); + if (!IS_ERR(client)) { + rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_2_1); + goto done; + } + + client = msm_rpc_register_client("pmapp_usb", + PMAPP_RPC_PROG, + PMAPP_RPC_VER_1_2, 1, + pm_app_usb_cb_func); + if (!IS_ERR(client)) { + rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_1_2); + goto done; + } + + client = msm_rpc_register_client("pmapp_usb", + PMAPP_RPC_PROG, + PMAPP_RPC_VER_1_1, 1, + pm_app_usb_cb_func); + if (!IS_ERR(client)) + rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_1_1); + else + return PTR_ERR(client); + +done: + cb_id = msm_rpc_add_cb_func(client, (void *)callback); + /* In case of NULL callback funtion, cb_id would be -1 */ + if ((int) cb_id < -1) + return cb_id; + rc = msm_rpc_client_req(client, + rpc_ids.reg_for_vbus_valid, + vbus_sess_valid_arg_cb, + &cb_id, NULL, NULL, -1); + return rc; +} +EXPORT_SYMBOL(msm_pm_app_rpc_init); + +void msm_pm_app_rpc_deinit(void(*callback)(int online)) +{ + if (client) { + msm_rpc_remove_cb_func(client, (void *)callback); + msm_rpc_unregister_client(client); + } +} +EXPORT_SYMBOL(msm_pm_app_rpc_deinit); + +/* error bit flags defined by modem side */ +#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001) +#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002) +#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004) +#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008) +#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010) + +#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F) /* all 5 previous */ + +#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080) +#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100) + +#define PMAPP_BUFF_SIZE 256 + +struct pmapp_buf { + char *start; /* buffer start addr */ + char *end; /* buffer end addr */ + int size; /* buffer size */ + char *data; /* payload begin addr */ + int len; /* payload len */ +}; + +static DEFINE_MUTEX(pmapp_mtx); + +struct pmapp_ctrl { + int inited; + struct pmapp_buf tbuf; + struct pmapp_buf rbuf; + struct msm_rpc_endpoint *endpoint; +}; + +static struct pmapp_ctrl pmapp_ctrl = { + .inited = -1, +}; + + +static int pmapp_rpc_set_only(uint data0, uint data1, uint data2, + uint data3, int num, int proc); + +static int pmapp_buf_init(void) +{ + struct pmapp_ctrl *pm = &pmapp_ctrl; + + memset(&pmapp_ctrl, 0, sizeof(pmapp_ctrl)); + + pm->tbuf.start = kmalloc(PMAPP_BUFF_SIZE, GFP_KERNEL); + if (pm->tbuf.start == NULL) { + printk(KERN_ERR "%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + + pm->tbuf.data = pm->tbuf.start; + pm->tbuf.size = PMAPP_BUFF_SIZE; + pm->tbuf.end = pm->tbuf.start + PMAPP_BUFF_SIZE; + pm->tbuf.len = 0; + + pm->rbuf.start = kmalloc(PMAPP_BUFF_SIZE, GFP_KERNEL); + if (pm->rbuf.start == NULL) { + kfree(pm->tbuf.start); + printk(KERN_ERR "%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + pm->rbuf.data = pm->rbuf.start; + pm->rbuf.size = PMAPP_BUFF_SIZE; + pm->rbuf.end = pm->rbuf.start + PMAPP_BUFF_SIZE; + pm->rbuf.len = 0; + + pm->inited = 1; + + return 0; +} + +static inline void pmapp_buf_reserve(struct pmapp_buf *bp, int len) +{ + bp->data += len; +} + +static inline void pmapp_buf_reset(struct pmapp_buf *bp) +{ + bp->data = bp->start; + bp->len = 0; +} + +static int modem_to_linux_err(uint err) +{ + if (err == 0) + return 0; + + if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE) + return -EINVAL; /* PM_ERR_FLAG__PAR[1..5]_OUT_OF_RANGE */ + + if (err & PM_ERR_FLAG__SBI_OPT_ERR) + return -EIO; + + if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED) + return -ENOSYS; + + return -EPERM; +} + +static int pmapp_put_tx_data(struct pmapp_buf *tp, uint datav) +{ + uint *lp; + + if ((tp->size - tp->len) < sizeof(datav)) { + printk(KERN_ERR "%s: OVERFLOW size=%d len=%d\n", + __func__, tp->size, tp->len); + return -1; + } + + lp = (uint *)tp->data; + *lp = cpu_to_be32(datav); + tp->data += sizeof(datav); + tp->len += sizeof(datav); + + return sizeof(datav); +} + +static int pmapp_pull_rx_data(struct pmapp_buf *rp, uint *datap) +{ + uint *lp; + + if (rp->len < sizeof(*datap)) { + printk(KERN_ERR "%s: UNDERRUN len=%d\n", __func__, rp->len); + return -1; + } + lp = (uint *)rp->data; + *datap = be32_to_cpu(*lp); + rp->data += sizeof(*datap); + rp->len -= sizeof(*datap); + + return sizeof(*datap); +} + + +static int pmapp_rpc_req_reply(struct pmapp_buf *tbuf, struct pmapp_buf *rbuf, + int proc) +{ + struct pmapp_ctrl *pm = &pmapp_ctrl; + int ans, len, i; + + + if ((pm->endpoint == NULL) || IS_ERR(pm->endpoint)) { + for (i = 0; i < ARRAY_SIZE(rpc_vers); i++) { + pm->endpoint = msm_rpc_connect_compatible( + PMAPP_RPC_PROG, rpc_vers[i], 0); + + if (IS_ERR(pm->endpoint)) { + ans = PTR_ERR(pm->endpoint); + printk(KERN_ERR "%s: init rpc failed! ans = %d" + " for 0x%x version, fallback\n", + __func__, ans, rpc_vers[i]); + } else { + printk(KERN_DEBUG "%s: successfully connected" + " to 0x%x rpc version\n", + __func__, rpc_vers[i]); + break; + } + } + } + + if (IS_ERR(pm->endpoint)) { + ans = PTR_ERR(pm->endpoint); + return ans; + } + + /* + * data is point to next available space at this moment, + * move it back to beginning of request header and increase + * the length + */ + tbuf->data = tbuf->start; + tbuf->len += sizeof(struct rpc_request_hdr); + + len = msm_rpc_call_reply(pm->endpoint, proc, + tbuf->data, tbuf->len, + rbuf->data, rbuf->size, + PMAPP_RPC_TIMEOUT); + + if (len <= 0) { + printk(KERN_ERR "%s: rpc failed! len = %d\n", __func__, len); + pm->endpoint = NULL; /* re-connect later ? */ + return len; + } + + rbuf->len = len; + /* strip off rpc_reply_hdr */ + rbuf->data += sizeof(struct rpc_reply_hdr); + rbuf->len -= sizeof(struct rpc_reply_hdr); + + return rbuf->len; +} + +static int pmapp_rpc_set_only(uint data0, uint data1, uint data2, uint data3, + int num, int proc) +{ + struct pmapp_ctrl *pm = &pmapp_ctrl; + struct pmapp_buf *tp; + struct pmapp_buf *rp; + int stat; + + + if (mutex_lock_interruptible(&pmapp_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmapp_buf_init(); + if (stat < 0) { + mutex_unlock(&pmapp_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmapp_buf_reset(tp); + pmapp_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmapp_buf_reset(rp); + + if (num > 0) + pmapp_put_tx_data(tp, data0); + + if (num > 1) + pmapp_put_tx_data(tp, data1); + + if (num > 2) + pmapp_put_tx_data(tp, data2); + + if (num > 3) + pmapp_put_tx_data(tp, data3); + + stat = pmapp_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmapp_mtx); + return stat; + } + + pmapp_pull_rx_data(rp, &stat); /* result from server */ + + mutex_unlock(&pmapp_mtx); + + return modem_to_linux_err(stat); +} + +int pmapp_display_clock_config(uint enable) +{ + return pmapp_rpc_set_only(enable, 0, 0, 0, 1, + PMAPP_DISPLAY_CLOCK_CONFIG_PROC); +} +EXPORT_SYMBOL(pmapp_display_clock_config); + +int pmapp_clock_vote(const char *voter_id, uint clock_id, uint vote) +{ + if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN) + return -EINVAL; + + return pmapp_rpc_set_only(*((uint *) voter_id), clock_id, vote, 0, 3, + PMAPP_CLOCK_VOTE_PROC); +} +EXPORT_SYMBOL(pmapp_clock_vote); + +int pmapp_smps_clock_vote(const char *voter_id, uint vreg_id, uint vote) +{ + if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN) + return -EINVAL; + + return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, vote, 0, 3, + PMAPP_SMPS_CLOCK_VOTE_PROC); +} +EXPORT_SYMBOL(pmapp_smps_clock_vote); + +int pmapp_vreg_level_vote(const char *voter_id, uint vreg_id, uint level) +{ + if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN) + return -EINVAL; + + return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, level, 0, 3, + PMAPP_VREG_LEVEL_VOTE_PROC); +} +EXPORT_SYMBOL(pmapp_vreg_level_vote); + +int pmapp_smps_mode_vote(const char *voter_id, uint vreg_id, uint mode) +{ + if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN) + return -EINVAL; + + return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, mode, 0, 3, + PMAPP_SMPS_MODE_VOTE_PROC); +} +EXPORT_SYMBOL(pmapp_smps_mode_vote); + +int pmapp_vreg_pincntrl_vote(const char *voter_id, uint vreg_id, + uint clock_id, uint vote) +{ + if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN) + return -EINVAL; + + return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, clock_id, + vote, 4, + PMAPP_VREG_PINCNTRL_VOTE_PROC); +} +EXPORT_SYMBOL(pmapp_vreg_pincntrl_vote); + +int pmapp_disp_backlight_set_brightness(int value) +{ + if (value < 0 || value > 255) + return -EINVAL; + + return pmapp_rpc_set_only(value, 0, 0, 0, 1, + PMAPP_DISP_BACKLIGHT_SET_PROC); +} +EXPORT_SYMBOL(pmapp_disp_backlight_set_brightness); + +void pmapp_disp_backlight_init(void) +{ + pmapp_rpc_set_only(0, 0, 0, 0, 0, PMAPP_DISP_BACKLIGHT_INIT_PROC); +} +EXPORT_SYMBOL(pmapp_disp_backlight_init); + +int pmapp_vreg_lpm_pincntrl_vote(const char *voter_id, uint vreg_id, + uint clock_id, uint vote) +{ + if (strnlen(voter_id, PMAPP_CLOCK_VOTER_ID_LEN) + != PMAPP_CLOCK_VOTER_ID_LEN) + return -EINVAL; + + return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, clock_id, + vote, 4, + PMAPP_VREG_LPM_PINCNTRL_VOTE_PROC); +} +EXPORT_SYMBOL(pmapp_vreg_lpm_pincntrl_vote); diff --git a/arch/arm/mach-msm/rpc_server_dog_keepalive.c b/arch/arm/mach-msm/rpc_server_dog_keepalive.c new file mode 100644 index 00000000000..5e0f46da379 --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_dog_keepalive.c @@ -0,0 +1,77 @@ +/* arch/arm/mach-msm/rpc_server_dog_keepalive.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +/* dog_keepalive server definitions */ + +#define DOG_KEEPALIVE_PROG 0x30000015 +#if CONFIG_MSM_AMSS_VERSION==6210 +#define DOG_KEEPALIVE_VERS 0 +#define RPC_DOG_KEEPALIVE_BEACON 1 +#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) +#define DOG_KEEPALIVE_VERS 0x731fa727 +#define RPC_DOG_KEEPALIVE_BEACON 2 +#else +#error "Unsupported AMSS version" +#endif +#define DOG_KEEPALIVE_VERS_COMP 0x00010001 +#define RPC_DOG_KEEPALIVE_NULL 0 + + +/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/ + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_DOG_KEEPALIVE_NULL: + return 0; + case RPC_DOG_KEEPALIVE_BEACON: + return 0; + default: + return -ENODEV; + } +} + +static struct msm_rpc_server rpc_server[] = { + { + .prog = DOG_KEEPALIVE_PROG, + .vers = DOG_KEEPALIVE_VERS, + .rpc_call = handle_rpc_call, + }, + { + .prog = DOG_KEEPALIVE_PROG, + .vers = DOG_KEEPALIVE_VERS_COMP, + .rpc_call = handle_rpc_call, + }, +}; + +static int __init rpc_server_init(void) +{ + /* Dual server registration to support backwards compatibility vers */ + int ret; + ret = msm_rpc_create_server(&rpc_server[1]); + if (ret < 0) + return ret; + return msm_rpc_create_server(&rpc_server[0]); +} + + +module_init(rpc_server_init); diff --git a/arch/arm/mach-msm/rpc_server_handset.c b/arch/arm/mach-msm/rpc_server_handset.c new file mode 100644 index 00000000000..6d173fb6629 --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_handset.c @@ -0,0 +1,707 @@ +/* arch/arm/mach-msm/rpc_server_handset.c + * + * Copyright (c) 2008-2010,2012 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define DRIVER_NAME "msm-handset" + +#define HS_SERVER_PROG 0x30000062 +#define HS_SERVER_VERS 0x00010001 + +#define HS_RPC_PROG 0x30000091 + +#define HS_PROCESS_CMD_PROC 0x02 +#define HS_SUBSCRIBE_SRVC_PROC 0x03 +#define HS_REPORT_EVNT_PROC 0x05 +#define HS_EVENT_CB_PROC 1 +#define HS_EVENT_DATA_VER 1 + +#define RPC_KEYPAD_NULL_PROC 0 +#define RPC_KEYPAD_PASS_KEY_CODE_PROC 2 +#define RPC_KEYPAD_SET_PWR_KEY_STATE_PROC 3 + +#define HS_PWR_K 0x6F /* Power key */ +#define HS_END_K 0x51 /* End key or Power key */ +#define HS_STEREO_HEADSET_K 0x82 +#define HS_HEADSET_SWITCH_K 0x84 +#define HS_HEADSET_SWITCH_2_K 0xF0 +#define HS_HEADSET_SWITCH_3_K 0xF1 +#define HS_HEADSET_HEADPHONE_K 0xF6 +#define HS_HEADSET_MICROPHONE_K 0xF7 +#define HS_REL_K 0xFF /* key release */ + +#define SW_HEADPHONE_INSERT_W_MIC 1 /* HS with mic */ + +#define KEY(hs_key, input_key) ((hs_key << 24) | input_key) + +enum hs_event { + HS_EVNT_EXT_PWR = 0, /* External Power status */ + HS_EVNT_HSD, /* Headset Detection */ + HS_EVNT_HSTD, /* Headset Type Detection */ + HS_EVNT_HSSD, /* Headset Switch Detection */ + HS_EVNT_KPD, + HS_EVNT_FLIP, /* Flip / Clamshell status (open/close) */ + HS_EVNT_CHARGER, /* Battery is being charged or not */ + HS_EVNT_ENV, /* Events from runtime environment like DEM */ + HS_EVNT_REM, /* Events received from HS counterpart on a + remote processor*/ + HS_EVNT_DIAG, /* Diag Events */ + HS_EVNT_LAST, /* Should always be the last event type */ + HS_EVNT_MAX /* Force enum to be an 32-bit number */ +}; + +enum hs_src_state { + HS_SRC_STATE_UNKWN = 0, + HS_SRC_STATE_LO, + HS_SRC_STATE_HI, +}; + +struct hs_event_data { + uint32_t ver; /* Version number */ + enum hs_event event_type; /* Event Type */ + enum hs_event enum_disc; /* discriminator */ + uint32_t data_length; /* length of the next field */ + enum hs_src_state data; /* Pointer to data */ + uint32_t data_size; /* Elements to be processed in data */ +}; + +enum hs_return_value { + HS_EKPDLOCKED = -2, /* Operation failed because keypad is locked */ + HS_ENOTSUPPORTED = -1, /* Functionality not supported */ + HS_FALSE = 0, /* Inquired condition is not true */ + HS_FAILURE = 0, /* Requested operation was not successful */ + HS_TRUE = 1, /* Inquired condition is true */ + HS_SUCCESS = 1, /* Requested operation was successful */ + HS_MAX_RETURN = 0x7FFFFFFF/* Force enum to be a 32 bit number */ +}; + +struct hs_key_data { + uint32_t ver; /* Version number to track sturcture changes */ + uint32_t code; /* which key? */ + uint32_t parm; /* key status. Up/down or pressed/released */ +}; + +enum hs_subs_srvc { + HS_SUBS_SEND_CMD = 0, /* Subscribe to send commands to HS */ + HS_SUBS_RCV_EVNT, /* Subscribe to receive Events from HS */ + HS_SUBS_SRVC_MAX +}; + +enum hs_subs_req { + HS_SUBS_REGISTER, /* Subscribe */ + HS_SUBS_CANCEL, /* Unsubscribe */ + HS_SUB_STATUS_MAX +}; + +enum hs_event_class { + HS_EVNT_CLASS_ALL = 0, /* All HS events */ + HS_EVNT_CLASS_LAST, /* Should always be the last class type */ + HS_EVNT_CLASS_MAX +}; + +enum hs_cmd_class { + HS_CMD_CLASS_LCD = 0, /* Send LCD related commands */ + HS_CMD_CLASS_KPD, /* Send KPD related commands */ + HS_CMD_CLASS_LAST, /* Should always be the last class type */ + HS_CMD_CLASS_MAX +}; + +/* + * Receive events or send command + */ +union hs_subs_class { + enum hs_event_class evnt; + enum hs_cmd_class cmd; +}; + +struct hs_subs { + uint32_t ver; + enum hs_subs_srvc srvc; /* commands or events */ + enum hs_subs_req req; /* subscribe or unsubscribe */ + uint32_t host_os; + enum hs_subs_req disc; /* discriminator */ + union hs_subs_class id; +}; + +struct hs_event_cb_recv { + uint32_t cb_id; + uint32_t hs_key_data_ptr; + struct hs_key_data key; +}; +enum hs_ext_cmd_type { + HS_EXT_CMD_KPD_SEND_KEY = 0, /* Send Key */ + HS_EXT_CMD_KPD_BKLT_CTRL, /* Keypad backlight intensity */ + HS_EXT_CMD_LCD_BKLT_CTRL, /* LCD Backlight intensity */ + HS_EXT_CMD_DIAG_KEYMAP, /* Emulating a Diag key sequence */ + HS_EXT_CMD_DIAG_LOCK, /* Device Lock/Unlock */ + HS_EXT_CMD_GET_EVNT_STATUS, /* Get the status for one of the drivers */ + HS_EXT_CMD_KPD_GET_KEYS_STATUS,/* Get a list of keys status */ + HS_EXT_CMD_KPD_SET_PWR_KEY_RST_THOLD, /* PWR Key HW Reset duration */ + HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD, /* Set pwr key threshold duration */ + HS_EXT_CMD_LAST, /* Should always be the last command type */ + HS_EXT_CMD_MAX = 0x7FFFFFFF /* Force enum to be an 32-bit number */ +}; + +struct hs_cmd_data_type { + uint32_t hs_cmd_data_type_ptr; /* hs_cmd_data_type ptr length */ + uint32_t ver; /* version */ + enum hs_ext_cmd_type id; /* command id */ + uint32_t handle; /* handle returned from subscribe proc */ + enum hs_ext_cmd_type disc_id1; /* discriminator id */ + uint32_t input_ptr; /* input ptr length */ + uint32_t input_val; /* command specific data */ + uint32_t input_len; /* length of command input */ + enum hs_ext_cmd_type disc_id2; /* discriminator id */ + uint32_t output_len; /* length of output data */ + uint32_t delayed; /* execution context for modem + true - caller context + false - hs task context*/ +}; + +static const uint32_t hs_key_map[] = { + KEY(HS_PWR_K, KEY_POWER), + KEY(HS_END_K, KEY_END), + KEY(HS_STEREO_HEADSET_K, SW_HEADPHONE_INSERT_W_MIC), + KEY(HS_HEADSET_HEADPHONE_K, SW_HEADPHONE_INSERT), + KEY(HS_HEADSET_MICROPHONE_K, SW_MICROPHONE_INSERT), + KEY(HS_HEADSET_SWITCH_K, KEY_MEDIA), + KEY(HS_HEADSET_SWITCH_2_K, KEY_VOLUMEUP), + KEY(HS_HEADSET_SWITCH_3_K, KEY_VOLUMEDOWN), + 0 +}; + +enum { + NO_DEVICE = 0, + MSM_HEADSET = 1, +}; +/* Add newer versions at the top of array */ +static const unsigned int rpc_vers[] = { + 0x00030001, + 0x00020001, + 0x00010001, +}; +/* hs subscription request parameters */ +struct hs_subs_rpc_req { + uint32_t hs_subs_ptr; + struct hs_subs hs_subs; + uint32_t hs_cb_id; + uint32_t hs_handle_ptr; + uint32_t hs_handle_data; +}; + +static struct hs_subs_rpc_req *hs_subs_req; + +struct msm_handset { + struct input_dev *ipdev; + struct switch_dev sdev; + struct msm_handset_platform_data *hs_pdata; + bool mic_on, hs_on; +}; + +static struct msm_rpc_client *rpc_client; +static struct msm_handset *hs; + +static int hs_find_key(uint32_t hscode) +{ + int i, key; + + key = KEY(hscode, 0); + + for (i = 0; hs_key_map[i] != 0; i++) { + if ((hs_key_map[i] & 0xff000000) == key) + return hs_key_map[i] & 0x00ffffff; + } + return -1; +} + +static void update_state(void) +{ + int state; + + if (hs->mic_on && hs->hs_on) + state = 1 << 0; + else if (hs->hs_on) + state = 1 << 1; + else if (hs->mic_on) + state = 1 << 2; + else + state = 0; + + switch_set_state(&hs->sdev, state); +} + +/* + * tuple format: (key_code, key_param) + * + * old-architecture: + * key-press = (key_code, 0) + * key-release = (0xff, key_code) + * + * new-architecutre: + * key-press = (key_code, 0) + * key-release = (key_code, 0xff) + */ +static void report_hs_key(uint32_t key_code, uint32_t key_parm) +{ + int key, temp_key_code; + + if (key_code == HS_REL_K) + key = hs_find_key(key_parm); + else + key = hs_find_key(key_code); + + temp_key_code = key_code; + + if (key_parm == HS_REL_K) + key_code = key_parm; + + switch (key) { + case KEY_POWER: + case KEY_END: + case KEY_MEDIA: + case KEY_VOLUMEUP: + case KEY_VOLUMEDOWN: + input_report_key(hs->ipdev, key, (key_code != HS_REL_K)); + break; + case SW_HEADPHONE_INSERT_W_MIC: + hs->mic_on = hs->hs_on = (key_code != HS_REL_K) ? 1 : 0; + input_report_switch(hs->ipdev, SW_HEADPHONE_INSERT, + hs->hs_on); + input_report_switch(hs->ipdev, SW_MICROPHONE_INSERT, + hs->mic_on); + update_state(); + break; + + case SW_HEADPHONE_INSERT: + hs->hs_on = (key_code != HS_REL_K) ? 1 : 0; + input_report_switch(hs->ipdev, key, hs->hs_on); + update_state(); + break; + case SW_MICROPHONE_INSERT: + hs->mic_on = (key_code != HS_REL_K) ? 1 : 0; + input_report_switch(hs->ipdev, key, hs->mic_on); + update_state(); + break; + case -1: + printk(KERN_ERR "%s: No mapping for remote handset event %d\n", + __func__, temp_key_code); + return; + } + input_sync(hs->ipdev); +} + +static int handle_hs_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct rpc_keypad_pass_key_code_args { + uint32_t key_code; + uint32_t key_parm; + }; + + switch (req->procedure) { + case RPC_KEYPAD_NULL_PROC: + return 0; + + case RPC_KEYPAD_PASS_KEY_CODE_PROC: { + struct rpc_keypad_pass_key_code_args *args; + + args = (struct rpc_keypad_pass_key_code_args *)(req + 1); + args->key_code = be32_to_cpu(args->key_code); + args->key_parm = be32_to_cpu(args->key_parm); + + report_hs_key(args->key_code, args->key_parm); + + return 0; + } + + case RPC_KEYPAD_SET_PWR_KEY_STATE_PROC: + /* This RPC function must be available for the ARM9 + * to function properly. This function is redundant + * when RPC_KEYPAD_PASS_KEY_CODE_PROC is handled. So + * input_report_key is not needed. + */ + return 0; + default: + return -ENODEV; + } +} + +static struct msm_rpc_server hs_rpc_server = { + .prog = HS_SERVER_PROG, + .vers = HS_SERVER_VERS, + .rpc_call = handle_hs_rpc_call, +}; + +static int process_subs_srvc_callback(struct hs_event_cb_recv *recv) +{ + if (!recv) + return -ENODATA; + + report_hs_key(be32_to_cpu(recv->key.code), be32_to_cpu(recv->key.parm)); + + return 0; +} + +static void process_hs_rpc_request(uint32_t proc, void *data) +{ + if (proc == HS_EVENT_CB_PROC) + process_subs_srvc_callback(data); + else + pr_err("%s: unknown rpc proc %d\n", __func__, proc); +} + +static int hs_rpc_report_event_arg(struct msm_rpc_client *client, + void *buffer, void *data) +{ + struct hs_event_rpc_req { + uint32_t hs_event_data_ptr; + struct hs_event_data data; + }; + + struct hs_event_rpc_req *req = buffer; + + req->hs_event_data_ptr = cpu_to_be32(0x1); + req->data.ver = cpu_to_be32(HS_EVENT_DATA_VER); + req->data.event_type = cpu_to_be32(HS_EVNT_HSD); + req->data.enum_disc = cpu_to_be32(HS_EVNT_HSD); + req->data.data_length = cpu_to_be32(0x1); + req->data.data = cpu_to_be32(*(enum hs_src_state *)data); + req->data.data_size = cpu_to_be32(sizeof(enum hs_src_state)); + + return sizeof(*req); +} + +static int hs_rpc_report_event_res(struct msm_rpc_client *client, + void *buffer, void *data) +{ + enum hs_return_value result; + + result = be32_to_cpu(*(enum hs_return_value *)buffer); + pr_debug("%s: request completed: 0x%x\n", __func__, result); + + if (result == HS_SUCCESS) + return 0; + + return 1; +} + +void report_headset_status(bool connected) +{ + int rc = -1; + enum hs_src_state status; + + if (connected == true) + status = HS_SRC_STATE_HI; + else + status = HS_SRC_STATE_LO; + + rc = msm_rpc_client_req(rpc_client, HS_REPORT_EVNT_PROC, + hs_rpc_report_event_arg, &status, + hs_rpc_report_event_res, NULL, -1); + + if (rc) + pr_err("%s: couldn't send rpc client request\n", __func__); +} +EXPORT_SYMBOL(report_headset_status); + +static int hs_rpc_pwr_cmd_arg(struct msm_rpc_client *client, + void *buffer, void *data) +{ + struct hs_cmd_data_type *hs_pwr_cmd = buffer; + + hs_pwr_cmd->hs_cmd_data_type_ptr = cpu_to_be32(0x01); + + hs_pwr_cmd->ver = cpu_to_be32(0x03); + hs_pwr_cmd->id = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD); + hs_pwr_cmd->handle = cpu_to_be32(hs_subs_req->hs_handle_data); + hs_pwr_cmd->disc_id1 = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD); + hs_pwr_cmd->input_ptr = cpu_to_be32(0x01); + hs_pwr_cmd->input_val = cpu_to_be32(hs->hs_pdata->pwr_key_delay_ms); + hs_pwr_cmd->input_len = cpu_to_be32(0x01); + hs_pwr_cmd->disc_id2 = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD); + hs_pwr_cmd->output_len = cpu_to_be32(0x00); + hs_pwr_cmd->delayed = cpu_to_be32(0x00); + + return sizeof(*hs_pwr_cmd); +} + +static int hs_rpc_pwr_cmd_res(struct msm_rpc_client *client, + void *buffer, void *data) +{ + uint32_t result; + + result = be32_to_cpu(*((uint32_t *)buffer)); + pr_debug("%s: request completed: 0x%x\n", __func__, result); + + return 0; +} + +static int hs_rpc_register_subs_arg(struct msm_rpc_client *client, + void *buffer, void *data) +{ + hs_subs_req = buffer; + + hs_subs_req->hs_subs_ptr = cpu_to_be32(0x1); + hs_subs_req->hs_subs.ver = cpu_to_be32(0x1); + hs_subs_req->hs_subs.srvc = cpu_to_be32(HS_SUBS_RCV_EVNT); + hs_subs_req->hs_subs.req = cpu_to_be32(HS_SUBS_REGISTER); + hs_subs_req->hs_subs.host_os = cpu_to_be32(0x4); /* linux */ + hs_subs_req->hs_subs.disc = cpu_to_be32(HS_SUBS_RCV_EVNT); + hs_subs_req->hs_subs.id.evnt = cpu_to_be32(HS_EVNT_CLASS_ALL); + + hs_subs_req->hs_cb_id = cpu_to_be32(0x1); + + hs_subs_req->hs_handle_ptr = cpu_to_be32(0x1); + hs_subs_req->hs_handle_data = cpu_to_be32(0x0); + + return sizeof(*hs_subs_req); +} + +static int hs_rpc_register_subs_res(struct msm_rpc_client *client, + void *buffer, void *data) +{ + uint32_t result; + + result = be32_to_cpu(*((uint32_t *)buffer)); + pr_debug("%s: request completed: 0x%x\n", __func__, result); + + return 0; +} + +static int hs_cb_func(struct msm_rpc_client *client, void *buffer, int in_size) +{ + int rc = -1; + + struct rpc_request_hdr *hdr = buffer; + + hdr->type = be32_to_cpu(hdr->type); + hdr->xid = be32_to_cpu(hdr->xid); + hdr->rpc_vers = be32_to_cpu(hdr->rpc_vers); + hdr->prog = be32_to_cpu(hdr->prog); + hdr->vers = be32_to_cpu(hdr->vers); + hdr->procedure = be32_to_cpu(hdr->procedure); + + process_hs_rpc_request(hdr->procedure, + (void *) (hdr + 1)); + + msm_rpc_start_accepted_reply(client, hdr->xid, + RPC_ACCEPTSTAT_SUCCESS); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) { + pr_err("%s: sending reply failed: %d\n", __func__, rc); + return rc; + } + + return 0; +} + +static int __devinit hs_rpc_cb_init(void) +{ + int rc = 0, i, num_vers; + + num_vers = ARRAY_SIZE(rpc_vers); + + for (i = 0; i < num_vers; i++) { + rpc_client = msm_rpc_register_client("hs", + HS_RPC_PROG, rpc_vers[i], 0, hs_cb_func); + + if (IS_ERR(rpc_client)) + pr_debug("%s: RPC Client version %d failed, fallback\n", + __func__, rpc_vers[i]); + else + break; + } + + if (IS_ERR(rpc_client)) { + pr_err("%s: Incompatible RPC version error %ld\n", + __func__, PTR_ERR(rpc_client)); + return PTR_ERR(rpc_client); + } + + rc = msm_rpc_client_req(rpc_client, HS_SUBSCRIBE_SRVC_PROC, + hs_rpc_register_subs_arg, NULL, + hs_rpc_register_subs_res, NULL, -1); + if (rc) { + pr_err("%s: RPC client request failed for subscribe services\n", + __func__); + goto err_client_req; + } + + rc = msm_rpc_client_req(rpc_client, HS_PROCESS_CMD_PROC, + hs_rpc_pwr_cmd_arg, NULL, + hs_rpc_pwr_cmd_res, NULL, -1); + if (rc) + pr_err("%s: RPC client request failed for pwr key" + " delay cmd, using normal mode\n", __func__); + return 0; +err_client_req: + msm_rpc_unregister_client(rpc_client); + return rc; +} + +static int __devinit hs_rpc_init(void) +{ + int rc; + + rc = hs_rpc_cb_init(); + if (rc) { + pr_err("%s: failed to initialize rpc client, try server...\n", + __func__); + + rc = msm_rpc_create_server(&hs_rpc_server); + if (rc) { + pr_err("%s: failed to create rpc server\n", __func__); + return rc; + } + } + + return rc; +} + +static void __devexit hs_rpc_deinit(void) +{ + if (rpc_client) + msm_rpc_unregister_client(rpc_client); +} + +static ssize_t msm_headset_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hs->sdev)) { + case NO_DEVICE: + return sprintf(buf, "No Device\n"); + case MSM_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static int __devinit hs_probe(struct platform_device *pdev) +{ + int rc = 0; + struct input_dev *ipdev; + + hs = kzalloc(sizeof(struct msm_handset), GFP_KERNEL); + if (!hs) + return -ENOMEM; + + hs->sdev.name = "h2w"; + hs->sdev.print_name = msm_headset_print_name; + + rc = switch_dev_register(&hs->sdev); + if (rc) + goto err_switch_dev_register; + + ipdev = input_allocate_device(); + if (!ipdev) { + rc = -ENOMEM; + goto err_alloc_input_dev; + } + input_set_drvdata(ipdev, hs); + + hs->ipdev = ipdev; + + if (pdev->dev.platform_data) + hs->hs_pdata = pdev->dev.platform_data; + + if (hs->hs_pdata->hs_name) + ipdev->name = hs->hs_pdata->hs_name; + else + ipdev->name = DRIVER_NAME; + + ipdev->id.vendor = 0x0001; + ipdev->id.product = 1; + ipdev->id.version = 1; + + input_set_capability(ipdev, EV_KEY, KEY_MEDIA); + input_set_capability(ipdev, EV_KEY, KEY_VOLUMEUP); + input_set_capability(ipdev, EV_KEY, KEY_VOLUMEDOWN); + input_set_capability(ipdev, EV_SW, SW_HEADPHONE_INSERT); + input_set_capability(ipdev, EV_SW, SW_MICROPHONE_INSERT); + input_set_capability(ipdev, EV_KEY, KEY_POWER); + input_set_capability(ipdev, EV_KEY, KEY_END); + + rc = input_register_device(ipdev); + if (rc) { + dev_err(&ipdev->dev, + "hs_probe: input_register_device rc=%d\n", rc); + goto err_reg_input_dev; + } + + platform_set_drvdata(pdev, hs); + + rc = hs_rpc_init(); + if (rc) { + dev_err(&ipdev->dev, "rpc init failure\n"); + goto err_hs_rpc_init; + } + + return 0; + +err_hs_rpc_init: + input_unregister_device(ipdev); + ipdev = NULL; +err_reg_input_dev: + input_free_device(ipdev); +err_alloc_input_dev: + switch_dev_unregister(&hs->sdev); +err_switch_dev_register: + kfree(hs); + return rc; +} + +static int __devexit hs_remove(struct platform_device *pdev) +{ + struct msm_handset *hs = platform_get_drvdata(pdev); + + input_unregister_device(hs->ipdev); + switch_dev_unregister(&hs->sdev); + kfree(hs); + hs_rpc_deinit(); + return 0; +} + +static struct platform_driver hs_driver = { + .probe = hs_probe, + .remove = __devexit_p(hs_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init hs_init(void) +{ + return platform_driver_register(&hs_driver); +} +late_initcall(hs_init); + +static void __exit hs_exit(void) +{ + platform_driver_unregister(&hs_driver); +} +module_exit(hs_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:msm-handset"); diff --git a/arch/arm/mach-msm/rpc_server_time_remote.c b/arch/arm/mach-msm/rpc_server_time_remote.c new file mode 100644 index 00000000000..a7e68543dca --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_time_remote.c @@ -0,0 +1,168 @@ +/* arch/arm/mach-msm/rpc_server_time_remote.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2011 Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "rpc_server_time_remote.h" +#include +#include +#include + +/* time_remote_mtoa server definitions. */ + +#define TIME_REMOTE_MTOA_PROG 0x3000005d +#define TIME_REMOTE_MTOA_VERS_OLD 0 +#define TIME_REMOTE_MTOA_VERS 0x9202a8e4 +#define TIME_REMOTE_MTOA_VERS_COMP 0x00010002 +#define RPC_TIME_REMOTE_MTOA_NULL 0 +#define RPC_TIME_TOD_SET_APPS_BASES 2 +#define RPC_TIME_GET_APPS_USER_TIME 3 + +struct rpc_time_tod_set_apps_bases_args { + uint32_t tick; + uint64_t stamp; +}; + +static int read_rtc0_time(struct msm_rpc_server *server, + struct rpc_request_hdr *req, + unsigned len) +{ + int err; + unsigned long tm_sec; + uint32_t size = 0; + void *reply; + uint32_t output_valid; + uint32_t rpc_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + struct rtc_time tm; + struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + + if (rtc == NULL) { + pr_err("%s: unable to open rtc device (%s)\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); + goto send_reply; + } + + err = rtc_read_time(rtc, &tm); + if (err) { + pr_err("%s: Error reading rtc device (%s) : %d\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE, err); + goto close_dev; + } + + err = rtc_valid_tm(&tm); + if (err) { + pr_err("%s: Invalid RTC time (%s)\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); + goto close_dev; + } + + rtc_tm_to_time(&tm, &tm_sec); + rpc_status = RPC_ACCEPTSTAT_SUCCESS; + +close_dev: + rtc_class_close(rtc); + +send_reply: + reply = msm_rpc_server_start_accepted_reply(server, req->xid, + rpc_status); + if (rpc_status == RPC_ACCEPTSTAT_SUCCESS) { + output_valid = *((uint32_t *)(req + 1)); + *(uint32_t *)reply = output_valid; + size = sizeof(uint32_t); + if (be32_to_cpu(output_valid)) { + reply += sizeof(uint32_t); + *(uint32_t *)reply = cpu_to_be32(tm_sec); + size += sizeof(uint32_t); + } + } + err = msm_rpc_server_send_accepted_reply(server, size); + if (err) + pr_err("%s: send accepted reply failed: %d\n", __func__, err); + + return 1; +} + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct timespec ts, tv; + + switch (req->procedure) { + case RPC_TIME_REMOTE_MTOA_NULL: + return 0; + + case RPC_TIME_TOD_SET_APPS_BASES: { + struct rpc_time_tod_set_apps_bases_args *args; + args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1); + args->tick = be32_to_cpu(args->tick); + args->stamp = be64_to_cpu(args->stamp); + printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n" + "\ttick = %d\n" + "\tstamp = %lld\n", + args->tick, args->stamp); + getnstimeofday(&ts); + msmrtc_updateatsuspend(&ts); + rtc_hctosys(); + getnstimeofday(&tv); + /* Update the alarm information with the new time info. */ + alarm_update_timedelta(ts, tv); + return 0; + } + + case RPC_TIME_GET_APPS_USER_TIME: + return read_rtc0_time(server, req, len); + + default: + return -ENODEV; + } +} + +static struct msm_rpc_server rpc_server[] = { + { + .prog = TIME_REMOTE_MTOA_PROG, + .vers = TIME_REMOTE_MTOA_VERS_OLD, + .rpc_call = handle_rpc_call, + }, + { + .prog = TIME_REMOTE_MTOA_PROG, + .vers = TIME_REMOTE_MTOA_VERS, + .rpc_call = handle_rpc_call, + }, + { + .prog = TIME_REMOTE_MTOA_PROG, + .vers = TIME_REMOTE_MTOA_VERS_COMP, + .rpc_call = handle_rpc_call, + }, +}; + +static int __init rpc_server_init(void) +{ + /* Dual server registration to support backwards compatibility vers */ + int ret; + ret = msm_rpc_create_server(&rpc_server[2]); + if (ret < 0) + return ret; + ret = msm_rpc_create_server(&rpc_server[1]); + if (ret < 0) + return ret; + return msm_rpc_create_server(&rpc_server[0]); +} + + +module_init(rpc_server_init); diff --git a/arch/arm/mach-msm/rpc_server_time_remote.h b/arch/arm/mach-msm/rpc_server_time_remote.h new file mode 100644 index 00000000000..056666f5001 --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_time_remote.h @@ -0,0 +1,21 @@ +/* arch/arm/mach-msm/rpc_server_time_remote.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPC_SERVER_TIME_REMOTE_H +#define __ARCH_ARM_MACH_MSM_RPC_SERVER_TIME_REMOTE_H + +int rtc_hctosys(void); + +#endif diff --git a/arch/arm/mach-msm/rpcrouter_sdio_xprt.c b/arch/arm/mach-msm/rpcrouter_sdio_xprt.c new file mode 100644 index 00000000000..94a2d26fc90 --- /dev/null +++ b/arch/arm/mach-msm/rpcrouter_sdio_xprt.c @@ -0,0 +1,655 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * RPCROUTER SDIO XPRT module. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "smd_rpcrouter.h" + +enum { + MSM_SDIO_XPRT_DEBUG = 1U << 0, + MSM_SDIO_XPRT_INFO = 1U << 1, +}; + +static int msm_sdio_xprt_debug_mask; +module_param_named(debug_mask, msm_sdio_xprt_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(CONFIG_MSM_RPC_SDIO_DEBUG) +#define SDIO_XPRT_DBG(x...) do { \ + if (msm_sdio_xprt_debug_mask & MSM_SDIO_XPRT_DEBUG) \ + printk(KERN_DEBUG x); \ + } while (0) + +#define SDIO_XPRT_INFO(x...) do { \ + if (msm_sdio_xprt_debug_mask & MSM_SDIO_XPRT_INFO) \ + printk(KERN_INFO x); \ + } while (0) +#else +#define SDIO_XPRT_DBG(x...) do { } while (0) +#define SDIO_XPRT_INFO(x...) do { } while (0) +#endif + +#define MAX_SDIO_WRITE_RETRY 5 +#define SDIO_BUF_SIZE (RPCROUTER_MSGSIZE_MAX + sizeof(struct rr_header) - 8) +#define NUM_SDIO_BUFS 20 +#define MAX_TX_BUFS 10 +#define MAX_RX_BUFS 10 + +struct sdio_xprt { + struct sdio_channel *handle; + + struct list_head write_list; + spinlock_t write_list_lock; + + struct list_head read_list; + spinlock_t read_list_lock; + + struct list_head free_list; + spinlock_t free_list_lock; + + struct wake_lock read_wakelock; +}; + +struct rpcrouter_sdio_xprt { + struct rpcrouter_xprt xprt; + struct sdio_xprt *channel; +}; + +static struct rpcrouter_sdio_xprt sdio_remote_xprt; + +static void sdio_xprt_read_data(struct work_struct *work); +static DECLARE_DELAYED_WORK(work_read_data, sdio_xprt_read_data); +static struct workqueue_struct *sdio_xprt_read_workqueue; + +struct sdio_buf_struct { + struct list_head list; + uint32_t size; + uint32_t read_index; + uint32_t write_index; + unsigned char data[SDIO_BUF_SIZE]; +}; + +static void sdio_xprt_write_data(struct work_struct *work); +static DECLARE_WORK(work_write_data, sdio_xprt_write_data); +static wait_queue_head_t write_avail_wait_q; +static uint32_t num_free_bufs; +static uint32_t num_tx_bufs; +static uint32_t num_rx_bufs; + +static DEFINE_MUTEX(modem_reset_lock); +static uint32_t modem_reset; + +static void free_sdio_xprt(struct sdio_xprt *chnl) +{ + struct sdio_buf_struct *buf; + unsigned long flags; + + if (!chnl) { + printk(KERN_ERR "Invalid chnl to free\n"); + return; + } + + spin_lock_irqsave(&chnl->free_list_lock, flags); + while (!list_empty(&chnl->free_list)) { + buf = list_first_entry(&chnl->free_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + kfree(buf); + } + num_free_bufs = 0; + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + + spin_lock_irqsave(&chnl->write_list_lock, flags); + while (!list_empty(&chnl->write_list)) { + buf = list_first_entry(&chnl->write_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + kfree(buf); + } + num_tx_bufs = 0; + spin_unlock_irqrestore(&chnl->write_list_lock, flags); + + spin_lock_irqsave(&chnl->read_list_lock, flags); + while (!list_empty(&chnl->read_list)) { + buf = list_first_entry(&chnl->read_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + kfree(buf); + } + num_rx_bufs = 0; + spin_unlock_irqrestore(&chnl->read_list_lock, flags); + wake_unlock(&chnl->read_wakelock); +} + +static struct sdio_buf_struct *alloc_from_free_list(struct sdio_xprt *chnl) +{ + struct sdio_buf_struct *buf; + unsigned long flags; + + spin_lock_irqsave(&chnl->free_list_lock, flags); + if (list_empty(&chnl->free_list)) { + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + SDIO_XPRT_DBG("%s: Free list empty\n", __func__); + return NULL; + } + buf = list_first_entry(&chnl->free_list, struct sdio_buf_struct, list); + list_del(&buf->list); + num_free_bufs--; + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + + buf->size = 0; + buf->read_index = 0; + buf->write_index = 0; + + return buf; +} + +static void return_to_free_list(struct sdio_xprt *chnl, + struct sdio_buf_struct *buf) +{ + unsigned long flags; + + if (!chnl || !buf) { + pr_err("%s: Invalid chnl or buf\n", __func__); + return; + } + + buf->size = 0; + buf->read_index = 0; + buf->write_index = 0; + + spin_lock_irqsave(&chnl->free_list_lock, flags); + list_add_tail(&buf->list, &chnl->free_list); + num_free_bufs++; + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + +} + +static int rpcrouter_sdio_remote_read_avail(void) +{ + int read_avail = 0; + unsigned long flags; + struct sdio_buf_struct *buf; + + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, flags); + list_for_each_entry(buf, &sdio_remote_xprt.channel->read_list, list) { + read_avail += buf->size; + } + spin_unlock_irqrestore(&sdio_remote_xprt.channel->read_list_lock, + flags); + return read_avail; +} + +static int rpcrouter_sdio_remote_read(void *data, uint32_t len) +{ + struct sdio_buf_struct *buf; + unsigned char *buf_data; + unsigned long flags; + + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + if (len < 0 || !data) + return -EINVAL; + else if (len == 0) + return 0; + + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, flags); + if (list_empty(&sdio_remote_xprt.channel->read_list)) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + return -EINVAL; + } + + buf = list_first_entry(&sdio_remote_xprt.channel->read_list, + struct sdio_buf_struct, list); + if (buf->size < len) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + return -EINVAL; + } + + buf_data = buf->data + buf->read_index; + memcpy(data, buf_data, len); + buf->read_index += len; + buf->size -= len; + if (buf->size == 0) { + list_del(&buf->list); + num_rx_bufs--; + return_to_free_list(sdio_remote_xprt.channel, buf); + } + + if (list_empty(&sdio_remote_xprt.channel->read_list)) + wake_unlock(&sdio_remote_xprt.channel->read_wakelock); + spin_unlock_irqrestore(&sdio_remote_xprt.channel->read_list_lock, + flags); + return len; +} + +static int rpcrouter_sdio_remote_write_avail(void) +{ + uint32_t write_avail = 0; + unsigned long flags; + + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, flags); + write_avail = (MAX_TX_BUFS - num_tx_bufs) * SDIO_BUF_SIZE; + spin_unlock_irqrestore(&sdio_remote_xprt.channel->write_list_lock, + flags); + return write_avail; +} + +static int rpcrouter_sdio_remote_write(void *data, uint32_t len, + enum write_data_type type) +{ + unsigned long flags; + static struct sdio_buf_struct *buf; + unsigned char *buf_data; + + switch (type) { + case HEADER: + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, + flags); + if (num_tx_bufs == MAX_TX_BUFS) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, + flags); + return -ENOMEM; + } + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, flags); + + SDIO_XPRT_DBG("sdio_xprt WRITE HEADER %s\n", __func__); + buf = alloc_from_free_list(sdio_remote_xprt.channel); + if (!buf) { + pr_err("%s: alloc_from_free_list failed\n", __func__); + return -ENOMEM; + } + buf_data = buf->data + buf->write_index; + memcpy(buf_data, data, len); + buf->write_index += len; + buf->size += len; + return len; + case PACKMARK: + SDIO_XPRT_DBG("sdio_xprt WRITE PACKMARK %s\n", __func__); + if (!buf) { + pr_err("%s: HEADER not written or alloc failed\n", + __func__); + return -ENOMEM; + } + buf_data = buf->data + buf->write_index; + memcpy(buf_data, data, len); + buf->write_index += len; + buf->size += len; + return len; + case PAYLOAD: + SDIO_XPRT_DBG("sdio_xprt WRITE PAYLOAD %s\n", __func__); + if (!buf) { + pr_err("%s: HEADER not written or alloc failed\n", + __func__); + return -ENOMEM; + } + buf_data = buf->data + buf->write_index; + memcpy(buf_data, data, len); + buf->write_index += len; + buf->size += len; + + SDIO_XPRT_DBG("sdio_xprt flush %d bytes\n", buf->size); + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, + flags); + list_add_tail(&buf->list, + &sdio_remote_xprt.channel->write_list); + num_tx_bufs++; + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, flags); + queue_work(sdio_xprt_read_workqueue, &work_write_data); + buf = NULL; + return len; + default: + return -EINVAL; + } +} + +static void sdio_xprt_write_data(struct work_struct *work) +{ + int rc = 0, sdio_write_retry = 0; + unsigned long flags; + struct sdio_buf_struct *buf; + + mutex_lock(&modem_reset_lock); + if (modem_reset) { + mutex_unlock(&modem_reset_lock); + return; + } + + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, flags); + while (!list_empty(&sdio_remote_xprt.channel->write_list)) { + buf = list_first_entry(&sdio_remote_xprt.channel->write_list, + struct sdio_buf_struct, list); + list_del(&buf->list); + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->write_list_lock, flags); + mutex_unlock(&modem_reset_lock); + + wait_event(write_avail_wait_q, + (!(modem_reset) && (sdio_write_avail( + sdio_remote_xprt.channel->handle) >= + buf->size))); + + mutex_lock(&modem_reset_lock); + while (!(modem_reset) && + ((rc = sdio_write(sdio_remote_xprt.channel->handle, + buf->data, buf->size)) < 0) && + (sdio_write_retry++ < MAX_SDIO_WRITE_RETRY)) { + printk(KERN_ERR "sdio_write failed with RC %d\n", rc); + mutex_unlock(&modem_reset_lock); + msleep(250); + mutex_lock(&modem_reset_lock); + } + if (modem_reset) { + mutex_unlock(&modem_reset_lock); + kfree(buf); + return; + } else { + return_to_free_list(sdio_remote_xprt.channel, buf); + } + + if (!rc) + SDIO_XPRT_DBG("sdio_write %d bytes completed\n", + buf->size); + + spin_lock_irqsave(&sdio_remote_xprt.channel->write_list_lock, + flags); + num_tx_bufs--; + } + spin_unlock_irqrestore(&sdio_remote_xprt.channel->write_list_lock, + flags); + mutex_unlock(&modem_reset_lock); +} + +static int rpcrouter_sdio_remote_close(void) +{ + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + flush_workqueue(sdio_xprt_read_workqueue); + sdio_close(sdio_remote_xprt.channel->handle); + free_sdio_xprt(sdio_remote_xprt.channel); + return 0; +} + +static void sdio_xprt_read_data(struct work_struct *work) +{ + int size = 0, read_avail; + unsigned long flags; + struct sdio_buf_struct *buf; + SDIO_XPRT_DBG("sdio_xprt Called %s\n", __func__); + + mutex_lock(&modem_reset_lock); + while (!(modem_reset) && + ((read_avail = + sdio_read_avail(sdio_remote_xprt.channel->handle)) > 0)) { + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, + flags); + if (num_rx_bufs == MAX_RX_BUFS) { + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, + flags); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, + msecs_to_jiffies(100)); + break; + } + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + + buf = alloc_from_free_list(sdio_remote_xprt.channel); + if (!buf) { + SDIO_XPRT_DBG("%s: Failed to alloc_from_free_list" + " Try again later\n", __func__); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, + msecs_to_jiffies(100)); + break; + } + + size = sdio_read(sdio_remote_xprt.channel->handle, + buf->data, read_avail); + if (size < 0) { + printk(KERN_ERR "sdio_read failed," + " read %d bytes, expected %d\n", + size, read_avail); + return_to_free_list(sdio_remote_xprt.channel, buf); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, + msecs_to_jiffies(100)); + break; + } + + if (size == 0) + size = read_avail; + + buf->size = size; + buf->write_index = size; + spin_lock_irqsave(&sdio_remote_xprt.channel->read_list_lock, + flags); + list_add_tail(&buf->list, + &sdio_remote_xprt.channel->read_list); + num_rx_bufs++; + spin_unlock_irqrestore( + &sdio_remote_xprt.channel->read_list_lock, flags); + wake_lock(&sdio_remote_xprt.channel->read_wakelock); + } + + if (!modem_reset && !list_empty(&sdio_remote_xprt.channel->read_list)) + msm_rpcrouter_xprt_notify(&sdio_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_DATA); + mutex_unlock(&modem_reset_lock); +} + +static void rpcrouter_sdio_remote_notify(void *_dev, unsigned event) +{ + if (event == SDIO_EVENT_DATA_READ_AVAIL) { + SDIO_XPRT_DBG("%s Received Notify" + "SDIO_EVENT_DATA_READ_AVAIL\n", __func__); + queue_delayed_work(sdio_xprt_read_workqueue, + &work_read_data, 0); + } + if (event == SDIO_EVENT_DATA_WRITE_AVAIL) { + SDIO_XPRT_DBG("%s Received Notify" + "SDIO_EVENT_DATA_WRITE_AVAIL\n", __func__); + wake_up(&write_avail_wait_q); + } +} + +static int allocate_sdio_xprt(struct sdio_xprt **sdio_xprt_chnl) +{ + struct sdio_buf_struct *buf; + struct sdio_xprt *chnl; + int i; + unsigned long flags; + int rc = -ENOMEM; + + if (!(*sdio_xprt_chnl)) { + chnl = kmalloc(sizeof(struct sdio_xprt), GFP_KERNEL); + if (!chnl) { + printk(KERN_ERR "sdio_xprt channel" + " allocation failed\n"); + return rc; + } + + spin_lock_init(&chnl->write_list_lock); + spin_lock_init(&chnl->read_list_lock); + spin_lock_init(&chnl->free_list_lock); + + INIT_LIST_HEAD(&chnl->write_list); + INIT_LIST_HEAD(&chnl->read_list); + INIT_LIST_HEAD(&chnl->free_list); + wake_lock_init(&chnl->read_wakelock, + WAKE_LOCK_SUSPEND, "rpc_sdio_xprt_read"); + } else { + chnl = *sdio_xprt_chnl; + } + + for (i = 0; i < NUM_SDIO_BUFS; i++) { + buf = kzalloc(sizeof(struct sdio_buf_struct), GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "sdio_buf_struct alloc failed\n"); + goto alloc_failure; + } + spin_lock_irqsave(&chnl->free_list_lock, flags); + list_add_tail(&buf->list, &chnl->free_list); + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + } + num_free_bufs = NUM_SDIO_BUFS; + + *sdio_xprt_chnl = chnl; + return 0; + +alloc_failure: + spin_lock_irqsave(&chnl->free_list_lock, flags); + while (!list_empty(&chnl->free_list)) { + buf = list_first_entry(&chnl->free_list, + struct sdio_buf_struct, + list); + list_del(&buf->list); + kfree(buf); + } + spin_unlock_irqrestore(&chnl->free_list_lock, flags); + wake_lock_destroy(&chnl->read_wakelock); + + kfree(chnl); + *sdio_xprt_chnl = NULL; + return rc; +} + +static int rpcrouter_sdio_remote_probe(struct platform_device *pdev) +{ + int rc; + + SDIO_XPRT_INFO("%s Called\n", __func__); + + mutex_lock(&modem_reset_lock); + if (!modem_reset) { + sdio_xprt_read_workqueue = + create_singlethread_workqueue("sdio_xprt"); + if (!sdio_xprt_read_workqueue) { + mutex_unlock(&modem_reset_lock); + return -ENOMEM; + } + + sdio_remote_xprt.xprt.name = "rpcrotuer_sdio_xprt"; + sdio_remote_xprt.xprt.read_avail = + rpcrouter_sdio_remote_read_avail; + sdio_remote_xprt.xprt.read = rpcrouter_sdio_remote_read; + sdio_remote_xprt.xprt.write_avail = + rpcrouter_sdio_remote_write_avail; + sdio_remote_xprt.xprt.write = rpcrouter_sdio_remote_write; + sdio_remote_xprt.xprt.close = rpcrouter_sdio_remote_close; + sdio_remote_xprt.xprt.priv = NULL; + + init_waitqueue_head(&write_avail_wait_q); + } + modem_reset = 0; + + rc = allocate_sdio_xprt(&sdio_remote_xprt.channel); + if (rc) { + destroy_workqueue(sdio_xprt_read_workqueue); + mutex_unlock(&modem_reset_lock); + return rc; + } + + /* Open up SDIO channel */ + rc = sdio_open("SDIO_RPC", &sdio_remote_xprt.channel->handle, NULL, + rpcrouter_sdio_remote_notify); + + if (rc < 0) { + free_sdio_xprt(sdio_remote_xprt.channel); + destroy_workqueue(sdio_xprt_read_workqueue); + mutex_unlock(&modem_reset_lock); + return rc; + } + mutex_unlock(&modem_reset_lock); + + msm_rpcrouter_xprt_notify(&sdio_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_OPEN); + + SDIO_XPRT_INFO("%s Completed\n", __func__); + + return 0; +} + +static int rpcrouter_sdio_remote_remove(struct platform_device *pdev) +{ + SDIO_XPRT_INFO("%s Called\n", __func__); + + mutex_lock(&modem_reset_lock); + modem_reset = 1; + wake_up(&write_avail_wait_q); + free_sdio_xprt(sdio_remote_xprt.channel); + mutex_unlock(&modem_reset_lock); + + msm_rpcrouter_xprt_notify(&sdio_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_CLOSE); + + SDIO_XPRT_INFO("%s Completed\n", __func__); + + return 0; +} + +/*Remove this platform driver after mainline of SDIO_AL update*/ +static struct platform_driver rpcrouter_sdio_remote_driver = { + .probe = rpcrouter_sdio_remote_probe, + .driver = { + .name = "SDIO_AL", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver rpcrouter_sdio_driver = { + .probe = rpcrouter_sdio_remote_probe, + .remove = rpcrouter_sdio_remote_remove, + .driver = { + .name = "SDIO_RPC", + .owner = THIS_MODULE, + }, +}; + +static int __init rpcrouter_sdio_init(void) +{ + int rc; + msm_sdio_xprt_debug_mask = 0x2; + rc = platform_driver_register(&rpcrouter_sdio_remote_driver); + if (rc < 0) + return rc; + return platform_driver_register(&rpcrouter_sdio_driver); +} + +module_init(rpcrouter_sdio_init); +MODULE_DESCRIPTION("RPC Router SDIO XPRT"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/rpcrouter_smd_xprt.c b/arch/arm/mach-msm/rpcrouter_smd_xprt.c new file mode 100644 index 00000000000..e974eb59e0c --- /dev/null +++ b/arch/arm/mach-msm/rpcrouter_smd_xprt.c @@ -0,0 +1,333 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * RPCROUTER SMD XPRT module. + */ + +#include +#include +#include +#include + +#include +#include "smd_rpcrouter.h" +#include "smd_private.h" + +struct rpcrouter_smd_xprt { + struct rpcrouter_xprt xprt; + + smd_channel_t *channel; +}; + +static struct rpcrouter_smd_xprt smd_remote_xprt; +#ifdef CONFIG_ARCH_FSM9XXX +static struct rpcrouter_smd_xprt smd_remote_qdsp_xprt; +#endif + +static int rpcrouter_smd_remote_read_avail(void) +{ + return smd_read_avail(smd_remote_xprt.channel); +} + +static int rpcrouter_smd_remote_read(void *data, uint32_t len) +{ + return smd_read(smd_remote_xprt.channel, data, len); +} + +static int rpcrouter_smd_remote_write_avail(void) +{ + return smd_write_avail(smd_remote_xprt.channel); +} + +static int rpcrouter_smd_remote_write(void *data, uint32_t len, uint32_t type) +{ + return smd_write(smd_remote_xprt.channel, data, len); +} + +static int rpcrouter_smd_remote_close(void) +{ + smsm_change_state(SMSM_APPS_STATE, SMSM_RPCINIT, 0); + return smd_close(smd_remote_xprt.channel); +} + +static void rpcrouter_smd_remote_notify(void *_dev, unsigned event) +{ + switch (event) { + case SMD_EVENT_DATA: + msm_rpcrouter_xprt_notify(&smd_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_DATA); + break; + + case SMD_EVENT_OPEN: + pr_info("%s: smd opened 0x%p\n", __func__, _dev); + + msm_rpcrouter_xprt_notify(&smd_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_OPEN); + break; + + case SMD_EVENT_CLOSE: + pr_info("%s: smd closed 0x%p\n", __func__, _dev); + + msm_rpcrouter_xprt_notify(&smd_remote_xprt.xprt, + RPCROUTER_XPRT_EVENT_CLOSE); + break; + } +} + +#ifdef CONFIG_ARCH_FSM9XXX +static int rpcrouter_smd_remote_qdsp_read_avail(void) +{ + return smd_read_avail(smd_remote_qdsp_xprt.channel); +} + +static int rpcrouter_smd_remote_qdsp_read(void *data, uint32_t len) +{ + return smd_read(smd_remote_qdsp_xprt.channel, data, len); +} + +static int rpcrouter_smd_remote_qdsp_write_avail(void) +{ + return smd_write_avail(smd_remote_qdsp_xprt.channel); +} + +static int rpcrouter_smd_remote_qdsp_write(void *data, + uint32_t len, uint32_t type) +{ + return smd_write(smd_remote_qdsp_xprt.channel, data, len); +} + +static int rpcrouter_smd_remote_qdsp_close(void) +{ + /* + * TBD: Implement when we have N way SMSM ported + * smsm_change_state(SMSM_APPS_STATE, SMSM_RPCINIT, 0); + */ + return smd_close(smd_remote_qdsp_xprt.channel); +} + +static void rpcrouter_smd_remote_qdsp_notify(void *_dev, unsigned event) +{ + switch (event) { + case SMD_EVENT_DATA: + msm_rpcrouter_xprt_notify(&smd_remote_qdsp_xprt.xprt, + RPCROUTER_XPRT_EVENT_DATA); + break; + + case SMD_EVENT_OPEN: + /* Print log info */ + pr_debug("%s: smd opened\n", __func__); + + msm_rpcrouter_xprt_notify(&smd_remote_qdsp_xprt.xprt, + RPCROUTER_XPRT_EVENT_OPEN); + break; + + case SMD_EVENT_CLOSE: + /* Print log info */ + pr_debug("%s: smd closed\n", __func__); + + msm_rpcrouter_xprt_notify(&smd_remote_qdsp_xprt.xprt, + RPCROUTER_XPRT_EVENT_CLOSE); + break; + } +} + +static int rpcrouter_smd_remote_qdsp_probe(struct platform_device *pdev) +{ + int rc; + + smd_remote_qdsp_xprt.xprt.name = "rpcrotuer_smd_qdsp_xprt"; + smd_remote_qdsp_xprt.xprt.read_avail = + rpcrouter_smd_remote_qdsp_read_avail; + smd_remote_qdsp_xprt.xprt.read = rpcrouter_smd_remote_qdsp_read; + smd_remote_qdsp_xprt.xprt.write_avail = + rpcrouter_smd_remote_qdsp_write_avail; + smd_remote_qdsp_xprt.xprt.write = rpcrouter_smd_remote_qdsp_write; + smd_remote_qdsp_xprt.xprt.close = rpcrouter_smd_remote_qdsp_close; + smd_remote_qdsp_xprt.xprt.priv = NULL; + + /* Open up SMD channel */ + rc = smd_named_open_on_edge("RPCCALL_QDSP", SMD_APPS_QDSP, + &smd_remote_qdsp_xprt.channel, NULL, + rpcrouter_smd_remote_qdsp_notify); + if (rc < 0) + return rc; + + smd_disable_read_intr(smd_remote_qdsp_xprt.channel); + + return 0; +} + +static struct platform_driver rpcrouter_smd_remote_qdsp_driver = { + .probe = rpcrouter_smd_remote_qdsp_probe, + .driver = { + .name = "RPCCALL_QDSP", + .owner = THIS_MODULE, + }, +}; + +static inline int register_smd_remote_qpsp_driver(void) +{ + return platform_driver_register(&rpcrouter_smd_remote_qdsp_driver); +} +#else /* CONFIG_ARCH_FSM9XXX */ +static inline int register_smd_remote_qpsp_driver(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_MSM_RPC_LOOPBACK_XPRT) + +static struct rpcrouter_smd_xprt smd_loopback_xprt; + +static int rpcrouter_smd_loopback_read_avail(void) +{ + return smd_read_avail(smd_loopback_xprt.channel); +} + +static int rpcrouter_smd_loopback_read(void *data, uint32_t len) +{ + return smd_read(smd_loopback_xprt.channel, data, len); +} + +static int rpcrouter_smd_loopback_write_avail(void) +{ + return smd_write_avail(smd_loopback_xprt.channel); +} + +static int rpcrouter_smd_loopback_write(void *data, uint32_t len, uint32 type) +{ + return smd_write(smd_loopback_xprt.channel, data, len); +} + +static int rpcrouter_smd_loopback_close(void) +{ + return smd_close(smd_loopback_xprt.channel); +} + +static void rpcrouter_smd_loopback_notify(void *_dev, unsigned event) +{ + switch (event) { + case SMD_EVENT_DATA: + msm_rpcrouter_xprt_notify(&smd_loopback_xprt.xprt, + RPCROUTER_XPRT_EVENT_DATA); + break; + + case SMD_EVENT_OPEN: + pr_debug("%s: smd loopback opened 0x%p\n", __func__, _dev); + + msm_rpcrouter_xprt_notify(&smd_loopback_xprt.xprt, + RPCROUTER_XPRT_EVENT_OPEN); + break; + + case SMD_EVENT_CLOSE: + pr_debug("%s: smd loopback closed 0x%p\n", __func__, _dev); + + msm_rpcrouter_xprt_notify(&smd_loopback_xprt.xprt, + RPCROUTER_XPRT_EVENT_CLOSE); + break; + } +} + +static int rpcrouter_smd_loopback_probe(struct platform_device *pdev) +{ + int rc; + + smd_loopback_xprt.xprt.name = "rpcrouter_loopback_xprt"; + smd_loopback_xprt.xprt.read_avail = rpcrouter_smd_loopback_read_avail; + smd_loopback_xprt.xprt.read = rpcrouter_smd_loopback_read; + smd_loopback_xprt.xprt.write_avail = rpcrouter_smd_loopback_write_avail; + smd_loopback_xprt.xprt.write = rpcrouter_smd_loopback_write; + smd_loopback_xprt.xprt.close = rpcrouter_smd_loopback_close; + smd_loopback_xprt.xprt.priv = NULL; + + /* Open up SMD LOOPBACK channel */ + rc = smd_named_open_on_edge("local_loopback", SMD_LOOPBACK_TYPE, + &smd_loopback_xprt.channel, NULL, + rpcrouter_smd_loopback_notify); + if (rc < 0) + return rc; + + smd_disable_read_intr(smd_remote_xprt.channel); + return 0; +} + +static struct platform_driver rpcrouter_smd_loopback_driver = { + .probe = rpcrouter_smd_loopback_probe, + .driver = { + .name = "local_loopback", + .owner = THIS_MODULE, + }, +}; + +static inline int register_smd_loopback_driver(void) +{ + return platform_driver_register(&rpcrouter_smd_loopback_driver); +} +#else /* CONFIG_MSM_RPC_LOOPBACK_XPRT */ +static inline int register_smd_loopback_driver(void) +{ + return 0; +} +#endif + +static int rpcrouter_smd_remote_probe(struct platform_device *pdev) +{ + int rc; + + smd_remote_xprt.xprt.name = "rpcrotuer_smd_xprt"; + smd_remote_xprt.xprt.read_avail = rpcrouter_smd_remote_read_avail; + smd_remote_xprt.xprt.read = rpcrouter_smd_remote_read; + smd_remote_xprt.xprt.write_avail = rpcrouter_smd_remote_write_avail; + smd_remote_xprt.xprt.write = rpcrouter_smd_remote_write; + smd_remote_xprt.xprt.close = rpcrouter_smd_remote_close; + smd_remote_xprt.xprt.priv = NULL; + + /* Open up SMD channel */ + rc = smd_open("RPCCALL", &smd_remote_xprt.channel, NULL, + rpcrouter_smd_remote_notify); + if (rc < 0) + return rc; + + smd_disable_read_intr(smd_remote_xprt.channel); + + return 0; +} + +static struct platform_driver rpcrouter_smd_remote_driver = { + .probe = rpcrouter_smd_remote_probe, + .driver = { + .name = "RPCCALL", + .owner = THIS_MODULE, + }, +}; + +static int __init rpcrouter_smd_init(void) +{ + int rc; + + rc = register_smd_loopback_driver(); + if (rc < 0) + return rc; + + rc = register_smd_remote_qpsp_driver(); + if (rc < 0) + return rc; + + return platform_driver_register(&rpcrouter_smd_remote_driver); +} + +module_init(rpcrouter_smd_init); +MODULE_DESCRIPTION("RPC Router SMD XPRT"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/rpm-notifier.h b/arch/arm/mach-msm/rpm-notifier.h new file mode 100644 index 00000000000..df8d9b35f44 --- /dev/null +++ b/arch/arm/mach-msm/rpm-notifier.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ARCH_ARM_MACH_MSM_RPM_NOTIF_H +#define __ARCH_ARM_MACH_MSM_RPM_NOTIF_H + +struct msm_rpm_notifier_data { + uint32_t rsc_type; + uint32_t rsc_id; + uint32_t key; + uint32_t size; + uint8_t *value; +}; + +int msm_rpm_register_notifier(struct notifier_block *nb); +int msm_rpm_unregister_notifier(struct notifier_block *nb); + +#endif /*__ARCH_ARM_MACH_MSM_RPM_NOTIF_H */ diff --git a/arch/arm/mach-msm/rpm-regulator-8660.c b/arch/arm/mach-msm/rpm-regulator-8660.c new file mode 100644 index 00000000000..6c4a9adf966 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator-8660.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include "rpm-regulator-private.h" + +/* RPM regulator request formats */ +static struct rpm_vreg_parts ldo_parts = { + .request_len = 2, + .mV = REQUEST_MEMBER(0, 0x00000FFF, 0), + .ip = REQUEST_MEMBER(0, 0x00FFF000, 12), + .fm = REQUEST_MEMBER(0, 0x03000000, 24), + .pc = REQUEST_MEMBER(0, 0x3C000000, 26), + .pf = REQUEST_MEMBER(0, 0xC0000000, 30), + .pd = REQUEST_MEMBER(1, 0x00000001, 0), + .ia = REQUEST_MEMBER(1, 0x00001FFE, 1), +}; + +static struct rpm_vreg_parts smps_parts = { + .request_len = 2, + .mV = REQUEST_MEMBER(0, 0x00000FFF, 0), + .ip = REQUEST_MEMBER(0, 0x00FFF000, 12), + .fm = REQUEST_MEMBER(0, 0x03000000, 24), + .pc = REQUEST_MEMBER(0, 0x3C000000, 26), + .pf = REQUEST_MEMBER(0, 0xC0000000, 30), + .pd = REQUEST_MEMBER(1, 0x00000001, 0), + .ia = REQUEST_MEMBER(1, 0x00001FFE, 1), + .freq = REQUEST_MEMBER(1, 0x001FE000, 13), + .freq_clk_src = REQUEST_MEMBER(1, 0x00600000, 21), +}; + +static struct rpm_vreg_parts switch_parts = { + .request_len = 1, + .enable_state = REQUEST_MEMBER(0, 0x00000001, 0), + .pd = REQUEST_MEMBER(0, 0x00000002, 1), + .pc = REQUEST_MEMBER(0, 0x0000003C, 2), + .pf = REQUEST_MEMBER(0, 0x000000C0, 6), + .hpm = REQUEST_MEMBER(0, 0x00000300, 8), +}; + +static struct rpm_vreg_parts ncp_parts = { + .request_len = 1, + .mV = REQUEST_MEMBER(0, 0x00000FFF, 0), + .enable_state = REQUEST_MEMBER(0, 0x00001000, 12), + .comp_mode = REQUEST_MEMBER(0, 0x00002000, 13), + .freq = REQUEST_MEMBER(0, 0x003FC000, 14), +}; + +/* Physically available PMIC regulator voltage setpoint ranges */ +static struct vreg_range pldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), + VOLTAGE_RANGE(3100000, 4900000, 50000), +}; + +static struct vreg_range nldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range smps_ranges[] = { + VOLTAGE_RANGE( 375000, 737500, 12500), + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), +}; + +static struct vreg_range ftsmps_ranges[] = { + VOLTAGE_RANGE( 350000, 650000, 50000), + VOLTAGE_RANGE( 700000, 1400000, 12500), + VOLTAGE_RANGE(1500000, 3300000, 50000), +}; + +static struct vreg_range ncp_ranges[] = { + VOLTAGE_RANGE(1500000, 3050000, 50000), +}; + +static struct vreg_set_points pldo_set_points = SET_POINTS(pldo_ranges); +static struct vreg_set_points nldo_set_points = SET_POINTS(nldo_ranges); +static struct vreg_set_points smps_set_points = SET_POINTS(smps_ranges); +static struct vreg_set_points ftsmps_set_points = SET_POINTS(ftsmps_ranges); +static struct vreg_set_points ncp_set_points = SET_POINTS(ncp_ranges); + +static struct vreg_set_points *all_set_points[] = { + &pldo_set_points, + &nldo_set_points, + &smps_set_points, + &ftsmps_set_points, + &ncp_set_points, +}; + +#define LDO(_vreg_id, _rpm_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_##_vreg_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_rpm_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_##_rpm_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_8660_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_LDO, \ + .set_points = &_ranges##_set_points, \ + .part = &ldo_parts, \ + .id = RPM_VREG_ID_##_vreg_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define SMPS(_vreg_id, _rpm_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_##_vreg_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_rpm_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_##_rpm_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_8660_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_SMPS, \ + .set_points = &_ranges##_set_points, \ + .part = &smps_parts, \ + .id = RPM_VREG_ID_##_vreg_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define LVS(_vreg_id, _rpm_id, _name, _name_pc) \ + [RPM_VREG_ID_##_vreg_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_rpm_id, }, \ + [1] = { .id = -1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_VS, \ + .part = &switch_parts, \ + .id = RPM_VREG_ID_##_vreg_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define MVS(_vreg_id, _rpm_id, _name, _name_pc) \ + LVS(_vreg_id, _rpm_id, _name, _name_pc) + +#define NCP(_vreg_id, _rpm_id, _name, _name_pc) \ + [RPM_VREG_ID_##_vreg_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_rpm_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_##_rpm_id##_1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_NCP, \ + .set_points = &ncp_set_points, \ + .part = &ncp_parts, \ + .id = RPM_VREG_ID_##_vreg_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +static struct vreg vregs[] = { + LDO(PM8058_L0, LDO0, "8058_l0", "8058_l0_pc", nldo, LDO_150), + LDO(PM8058_L1, LDO1, "8058_l1", "8058_l1_pc", nldo, LDO_300), + LDO(PM8058_L2, LDO2, "8058_l2", "8058_l2_pc", pldo, LDO_300), + LDO(PM8058_L3, LDO3, "8058_l3", "8058_l3_pc", pldo, LDO_150), + LDO(PM8058_L4, LDO4, "8058_l4", "8058_l4_pc", pldo, LDO_50), + LDO(PM8058_L5, LDO5, "8058_l5", "8058_l5_pc", pldo, LDO_300), + LDO(PM8058_L6, LDO6, "8058_l6", "8058_l6_pc", pldo, LDO_50), + LDO(PM8058_L7, LDO7, "8058_l7", "8058_l7_pc", pldo, LDO_50), + LDO(PM8058_L8, LDO8, "8058_l8", "8058_l8_pc", pldo, LDO_300), + LDO(PM8058_L9, LDO9, "8058_l9", "8058_l9_pc", pldo, LDO_300), + LDO(PM8058_L10, LDO10, "8058_l10", "8058_l10_pc", pldo, LDO_300), + LDO(PM8058_L11, LDO11, "8058_l11", "8058_l11_pc", pldo, LDO_150), + LDO(PM8058_L12, LDO12, "8058_l12", "8058_l12_pc", pldo, LDO_150), + LDO(PM8058_L13, LDO13, "8058_l13", "8058_l13_pc", pldo, LDO_300), + LDO(PM8058_L14, LDO14, "8058_l14", "8058_l14_pc", pldo, LDO_300), + LDO(PM8058_L15, LDO15, "8058_l15", "8058_l15_pc", pldo, LDO_300), + LDO(PM8058_L16, LDO16, "8058_l16", "8058_l16_pc", pldo, LDO_300), + LDO(PM8058_L17, LDO17, "8058_l17", "8058_l17_pc", pldo, LDO_150), + LDO(PM8058_L18, LDO18, "8058_l18", "8058_l18_pc", pldo, LDO_150), + LDO(PM8058_L19, LDO19, "8058_l19", "8058_l19_pc", pldo, LDO_150), + LDO(PM8058_L20, LDO20, "8058_l20", "8058_l20_pc", pldo, LDO_150), + LDO(PM8058_L21, LDO21, "8058_l21", "8058_l21_pc", nldo, LDO_150), + LDO(PM8058_L22, LDO22, "8058_l22", "8058_l22_pc", nldo, LDO_300), + LDO(PM8058_L23, LDO23, "8058_l23", "8058_l23_pc", nldo, LDO_300), + LDO(PM8058_L24, LDO24, "8058_l24", "8058_l24_pc", nldo, LDO_150), + LDO(PM8058_L25, LDO25, "8058_l25", "8058_l25_pc", nldo, LDO_150), + + SMPS(PM8058_S0, SMPS0, "8058_s0", "8058_s0_pc", smps, SMPS), + SMPS(PM8058_S1, SMPS1, "8058_s1", "8058_s1_pc", smps, SMPS), + SMPS(PM8058_S2, SMPS2, "8058_s2", "8058_s2_pc", smps, SMPS), + SMPS(PM8058_S3, SMPS3, "8058_s3", "8058_s3_pc", smps, SMPS), + SMPS(PM8058_S4, SMPS4, "8058_s4", "8058_s4_pc", smps, SMPS), + + LVS(PM8058_LVS0, LVS0, "8058_lvs0", "8058_lvs0_pc"), + LVS(PM8058_LVS1, LVS1, "8058_lvs1", "8058_lvs1_pc"), + + NCP(PM8058_NCP, NCP, "8058_ncp", NULL), + + LDO(PM8901_L0, LDO0B, "8901_l0", "8901_l0_pc", nldo, LDO_300), + LDO(PM8901_L1, LDO1B, "8901_l1", "8901_l1_pc", pldo, LDO_300), + LDO(PM8901_L2, LDO2B, "8901_l2", "8901_l2_pc", pldo, LDO_300), + LDO(PM8901_L3, LDO3B, "8901_l3", "8901_l3_pc", pldo, LDO_300), + LDO(PM8901_L4, LDO4B, "8901_l4", "8901_l4_pc", pldo, LDO_300), + LDO(PM8901_L5, LDO5B, "8901_l5", "8901_l5_pc", pldo, LDO_300), + LDO(PM8901_L6, LDO6B, "8901_l6", "8901_l6_pc", pldo, LDO_300), + + SMPS(PM8901_S0, SMPS0B, "8901_s0", "8901_s0_pc", ftsmps, FTSMPS), + SMPS(PM8901_S1, SMPS1B, "8901_s1", "8901_s1_pc", ftsmps, FTSMPS), + SMPS(PM8901_S2, SMPS2B, "8901_s2", "8901_s2_pc", ftsmps, FTSMPS), + SMPS(PM8901_S3, SMPS3B, "8901_s3", "8901_s3_pc", ftsmps, FTSMPS), + SMPS(PM8901_S4, SMPS4B, "8901_s4", "8901_s4_pc", ftsmps, FTSMPS), + + LVS(PM8901_LVS0, LVS0B, "8901_lvs0", "8901_lvs0_pc"), + LVS(PM8901_LVS1, LVS1B, "8901_lvs1", "8901_lvs1_pc"), + LVS(PM8901_LVS2, LVS2B, "8901_lvs2", "8901_lvs2_pc"), + LVS(PM8901_LVS3, LVS3B, "8901_lvs3", "8901_lvs3_pc"), + + MVS(PM8901_MVS0, MVS, "8901_mvs0", "8901_mvs0_pc"), +}; + +static const char *pin_func_label[] = { + [RPM_VREG_PIN_FN_8660_ENABLE] = "on/off", + [RPM_VREG_PIN_FN_8660_MODE] = "HPM/LPM", + [RPM_VREG_PIN_FN_8660_SLEEP_B] = "sleep_b", + [RPM_VREG_PIN_FN_8660_NONE] = "none", +}; + +static const char *force_mode_label[] = { + [RPM_VREG_FORCE_MODE_8660_NONE] = "none", + [RPM_VREG_FORCE_MODE_8660_LPM] = "LPM", + [RPM_VREG_FORCE_MODE_8660_HPM] = "HPM", +}; + +static const char *pin_control_label[] = { + " A0", + " A1", + " D0", + " D1", +}; + +static int is_real_id(int id) +{ + return (id >= 0) && (id <= RPM_VREG_ID_8660_MAX_REAL); +} + +static int pc_id_to_real_id(int id) +{ + int real_id; + + if (id >= RPM_VREG_ID_PM8058_L0_PC && id <= RPM_VREG_ID_PM8058_LVS1_PC) + real_id = id - RPM_VREG_ID_PM8058_L0_PC + RPM_VREG_ID_PM8058_L0; + else + real_id = id - RPM_VREG_ID_PM8901_L0_PC + RPM_VREG_ID_PM8901_L0; + + return real_id; +} + +static struct vreg_config config = { + .vregs = vregs, + .vregs_len = ARRAY_SIZE(vregs), + + .vreg_id_min = RPM_VREG_ID_PM8058_L0, + .vreg_id_max = RPM_VREG_ID_8660_MAX, + + .pin_func_none = RPM_VREG_PIN_FN_8660_NONE, + .pin_func_sleep_b = RPM_VREG_PIN_FN_8660_SLEEP_B, + + .mode_lpm = REGULATOR_MODE_IDLE, + .mode_hpm = REGULATOR_MODE_NORMAL, + + .set_points = all_set_points, + .set_points_len = ARRAY_SIZE(all_set_points), + + .label_pin_ctrl = pin_control_label, + .label_pin_ctrl_len = ARRAY_SIZE(pin_control_label), + .label_pin_func = pin_func_label, + .label_pin_func_len = ARRAY_SIZE(pin_func_label), + .label_force_mode = force_mode_label, + .label_force_mode_len = ARRAY_SIZE(force_mode_label), + + .is_real_id = is_real_id, + .pc_id_to_real_id = pc_id_to_real_id, + + .use_legacy_optimum_mode = 1, + .ia_follows_ip = 1, +}; + +struct vreg_config *get_config_8660(void) +{ + return &config; +} diff --git a/arch/arm/mach-msm/rpm-regulator-8930.c b/arch/arm/mach-msm/rpm-regulator-8930.c new file mode 100644 index 00000000000..f396fed9764 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator-8930.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include "rpm-regulator-private.h" + +/* RPM regulator request formats */ +static struct rpm_vreg_parts ldo_parts = { + .request_len = 2, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .pd = REQUEST_MEMBER(0, 0x00800000, 23), + .pc = REQUEST_MEMBER(0, 0x0F000000, 24), + .pf = REQUEST_MEMBER(0, 0xF0000000, 28), + .ip = REQUEST_MEMBER(1, 0x000003FF, 0), + .ia = REQUEST_MEMBER(1, 0x000FFC00, 10), + .fm = REQUEST_MEMBER(1, 0x00700000, 20), +}; + +static struct rpm_vreg_parts smps_parts = { + .request_len = 2, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .pd = REQUEST_MEMBER(0, 0x00800000, 23), + .pc = REQUEST_MEMBER(0, 0x0F000000, 24), + .pf = REQUEST_MEMBER(0, 0xF0000000, 28), + .ip = REQUEST_MEMBER(1, 0x000003FF, 0), + .ia = REQUEST_MEMBER(1, 0x000FFC00, 10), + .fm = REQUEST_MEMBER(1, 0x00700000, 20), + .pm = REQUEST_MEMBER(1, 0x00800000, 23), + .freq = REQUEST_MEMBER(1, 0x1F000000, 24), + .freq_clk_src = REQUEST_MEMBER(1, 0x60000000, 29), +}; + +static struct rpm_vreg_parts switch_parts = { + .request_len = 1, + .enable_state = REQUEST_MEMBER(0, 0x00000001, 0), + .pd = REQUEST_MEMBER(0, 0x00000002, 1), + .pc = REQUEST_MEMBER(0, 0x0000003C, 2), + .pf = REQUEST_MEMBER(0, 0x000003C0, 6), + .hpm = REQUEST_MEMBER(0, 0x00000C00, 10), +}; + +static struct rpm_vreg_parts corner_parts = { + .request_len = 1, + .uV = REQUEST_MEMBER(0, 0x00000003, 0), +}; + +/* Physically available PMIC regulator voltage setpoint ranges */ +static struct vreg_range pldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), + VOLTAGE_RANGE(3100000, 4900000, 50000), +}; + +static struct vreg_range nldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range nldo1200_ranges[] = { + VOLTAGE_RANGE( 375000, 743750, 6250), + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range smps_ranges[] = { + VOLTAGE_RANGE( 375000, 737500, 12500), + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), +}; + +static struct vreg_range ftsmps_ranges[] = { + VOLTAGE_RANGE( 350000, 650000, 50000), + VOLTAGE_RANGE( 700000, 1400000, 12500), + VOLTAGE_RANGE(1500000, 3300000, 50000), +}; + +static struct vreg_range corner_ranges[] = { + VOLTAGE_RANGE(RPM_VREG_CORNER_NONE, RPM_VREG_CORNER_HIGH, 1), +}; + +static struct vreg_set_points pldo_set_points = SET_POINTS(pldo_ranges); +static struct vreg_set_points nldo_set_points = SET_POINTS(nldo_ranges); +static struct vreg_set_points nldo1200_set_points = SET_POINTS(nldo1200_ranges); +static struct vreg_set_points smps_set_points = SET_POINTS(smps_ranges); +static struct vreg_set_points ftsmps_set_points = SET_POINTS(ftsmps_ranges); +static struct vreg_set_points corner_set_points = SET_POINTS(corner_ranges); + +static struct vreg_set_points *all_set_points[] = { + &pldo_set_points, + &nldo_set_points, + &nldo1200_set_points, + &smps_set_points, + &ftsmps_set_points, + &corner_set_points, +}; + +#define LDO(_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_PM8038_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8038_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_PM8038_##_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_8930_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_LDO, \ + .set_points = &_ranges##_set_points, \ + .part = &ldo_parts, \ + .id = RPM_VREG_ID_PM8038_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define SMPS(_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_PM8038_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8038_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_PM8038_##_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_8930_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_SMPS, \ + .set_points = &_ranges##_set_points, \ + .part = &smps_parts, \ + .id = RPM_VREG_ID_PM8038_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define LVS(_id, _name, _name_pc) \ + [RPM_VREG_ID_PM8038_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8038_##_id, }, \ + [1] = { .id = -1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_VS, \ + .part = &switch_parts, \ + .id = RPM_VREG_ID_PM8038_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define CORNER(_id, _rpm_id, _name, _ranges) \ + [RPM_VREG_ID_PM8038_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_rpm_id, }, \ + [1] = { .id = -1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_CORNER, \ + .set_points = &_ranges##_set_points, \ + .part = &corner_parts, \ + .id = RPM_VREG_ID_PM8038_##_id, \ + .rdesc.name = _name, \ + } + +static struct vreg vregs[] = { + LDO(L1, "8038_l1", NULL, nldo1200, LDO_1200), + LDO(L2, "8038_l2", "8038_l2_pc", nldo, LDO_150), + LDO(L3, "8038_l3", "8038_l3_pc", pldo, LDO_50), + LDO(L4, "8038_l4", "8038_l4_pc", pldo, LDO_50), + LDO(L5, "8038_l5", "8038_l5_pc", pldo, LDO_600), + LDO(L6, "8038_l6", "8038_l6_pc", pldo, LDO_600), + LDO(L7, "8038_l7", "8038_l7_pc", pldo, LDO_600), + LDO(L8, "8038_l8", "8038_l8_pc", pldo, LDO_300), + LDO(L9, "8038_l9", "8038_l9_pc", pldo, LDO_300), + LDO(L10, "8038_l10", "8038_l10_pc", pldo, LDO_600), + LDO(L11, "8038_l11", "8038_l11_pc", pldo, LDO_600), + LDO(L12, "8038_l12", "8038_l12_pc", nldo, LDO_300), + LDO(L14, "8038_l14", "8038_l14_pc", pldo, LDO_50), + LDO(L15, "8038_l15", "8038_l15_pc", pldo, LDO_150), + LDO(L16, "8038_l16", NULL, nldo1200, LDO_1200), + LDO(L17, "8038_l17", "8038_l17_pc", pldo, LDO_150), + LDO(L18, "8038_l18", "8038_l18_pc", pldo, LDO_50), + LDO(L19, "8038_l19", NULL, nldo1200, LDO_1200), + LDO(L20, "8038_l20", NULL, nldo1200, LDO_1200), + LDO(L21, "8038_l21", "8038_l21_pc", pldo, LDO_150), + LDO(L22, "8038_l22", "8038_l22_pc", pldo, LDO_50), + LDO(L23, "8038_l23", "8038_l23_pc", pldo, LDO_50), + LDO(L24, "8038_l24", NULL, nldo1200, LDO_1200), + LDO(L26, "8038_l26", "8038_l26_pc", nldo, LDO_150), + LDO(L27, "8038_l27", NULL, nldo1200, LDO_1200), + + SMPS(S1, "8038_s1", "8038_s1_pc", smps, SMPS_1500), + SMPS(S2, "8038_s2", "8038_s2_pc", smps, SMPS_1500), + SMPS(S3, "8038_s3", "8038_s3_pc", smps, SMPS_1500), + SMPS(S4, "8038_s4", "8038_s4_pc", smps, SMPS_1500), + SMPS(S5, "8038_s5", NULL, ftsmps, SMPS_2000), + SMPS(S6, "8038_s6", NULL, ftsmps, SMPS_2000), + + LVS(LVS1, "8038_lvs1", "8038_lvs1_pc"), + LVS(LVS2, "8038_lvs2", "8038_lvs2_pc"), + + CORNER(VDD_DIG_CORNER, VOLTAGE_CORNER, "vdd_dig_corner", corner), +}; + +static const char *pin_func_label[] = { + [RPM_VREG_PIN_FN_8930_DONT_CARE] = "don't care", + [RPM_VREG_PIN_FN_8930_ENABLE] = "on/off", + [RPM_VREG_PIN_FN_8930_MODE] = "HPM/LPM", + [RPM_VREG_PIN_FN_8930_SLEEP_B] = "sleep_b", + [RPM_VREG_PIN_FN_8930_NONE] = "none", +}; + +static const char *force_mode_label[] = { + [RPM_VREG_FORCE_MODE_8930_NONE] = "none", + [RPM_VREG_FORCE_MODE_8930_LPM] = "LPM", + [RPM_VREG_FORCE_MODE_8930_AUTO] = "auto", + [RPM_VREG_FORCE_MODE_8930_HPM] = "HPM", + [RPM_VREG_FORCE_MODE_8930_BYPASS] = "BYP", +}; + +static const char *power_mode_label[] = { + [RPM_VREG_POWER_MODE_8930_HYSTERETIC] = "HYS", + [RPM_VREG_POWER_MODE_8930_PWM] = "PWM", +}; + +static const char *pin_control_label[] = { + " D1", + " A0", + " A1", + " A2", +}; + +static int is_real_id(int id) +{ + return (id >= 0) && (id <= RPM_VREG_ID_PM8038_MAX_REAL); +} + +static int pc_id_to_real_id(int id) +{ + int real_id = 0; + + if (id >= RPM_VREG_ID_PM8038_L2_PC && id <= RPM_VREG_ID_PM8038_L15_PC) + real_id = id - RPM_VREG_ID_PM8038_L2_PC; + else if (id >= RPM_VREG_ID_PM8038_L17_PC + && id <= RPM_VREG_ID_PM8038_L18_PC) + real_id = id - RPM_VREG_ID_PM8038_L17_PC + + RPM_VREG_ID_PM8038_L17; + else if (id >= RPM_VREG_ID_PM8038_L21_PC + && id <= RPM_VREG_ID_PM8038_L23_PC) + real_id = id - RPM_VREG_ID_PM8038_L21_PC + + RPM_VREG_ID_PM8038_L21; + else if (id == RPM_VREG_ID_PM8038_L26_PC) + real_id = RPM_VREG_ID_PM8038_L26; + else if (id >= RPM_VREG_ID_PM8038_S1_PC + && id <= RPM_VREG_ID_PM8038_S4_PC) + real_id = id - RPM_VREG_ID_PM8038_S1_PC + + RPM_VREG_ID_PM8038_S1; + else if (id >= RPM_VREG_ID_PM8038_LVS1_PC + && id <= RPM_VREG_ID_PM8038_LVS2_PC) + real_id = id - RPM_VREG_ID_PM8038_LVS1_PC + + RPM_VREG_ID_PM8038_LVS1; + + return real_id; +} + +static struct vreg_config config = { + .vregs = vregs, + .vregs_len = ARRAY_SIZE(vregs), + + .vreg_id_min = RPM_VREG_ID_PM8038_L1, + .vreg_id_max = RPM_VREG_ID_PM8038_MAX, + + .pin_func_none = RPM_VREG_PIN_FN_8930_NONE, + .pin_func_sleep_b = RPM_VREG_PIN_FN_8930_SLEEP_B, + + .mode_lpm = REGULATOR_MODE_IDLE, + .mode_hpm = REGULATOR_MODE_NORMAL, + + .set_points = all_set_points, + .set_points_len = ARRAY_SIZE(all_set_points), + + .label_pin_ctrl = pin_control_label, + .label_pin_ctrl_len = ARRAY_SIZE(pin_control_label), + .label_pin_func = pin_func_label, + .label_pin_func_len = ARRAY_SIZE(pin_func_label), + .label_force_mode = force_mode_label, + .label_force_mode_len = ARRAY_SIZE(force_mode_label), + .label_power_mode = power_mode_label, + .label_power_mode_len = ARRAY_SIZE(power_mode_label), + + .is_real_id = is_real_id, + .pc_id_to_real_id = pc_id_to_real_id, +}; + +struct vreg_config *get_config_8930(void) +{ + return &config; +} diff --git a/arch/arm/mach-msm/rpm-regulator-8960.c b/arch/arm/mach-msm/rpm-regulator-8960.c new file mode 100644 index 00000000000..8726ed4f566 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator-8960.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include "rpm-regulator-private.h" + +/* RPM regulator request formats */ +static struct rpm_vreg_parts ldo_parts = { + .request_len = 2, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .pd = REQUEST_MEMBER(0, 0x00800000, 23), + .pc = REQUEST_MEMBER(0, 0x0F000000, 24), + .pf = REQUEST_MEMBER(0, 0xF0000000, 28), + .ip = REQUEST_MEMBER(1, 0x000003FF, 0), + .ia = REQUEST_MEMBER(1, 0x000FFC00, 10), + .fm = REQUEST_MEMBER(1, 0x00700000, 20), +}; + +static struct rpm_vreg_parts smps_parts = { + .request_len = 2, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .pd = REQUEST_MEMBER(0, 0x00800000, 23), + .pc = REQUEST_MEMBER(0, 0x0F000000, 24), + .pf = REQUEST_MEMBER(0, 0xF0000000, 28), + .ip = REQUEST_MEMBER(1, 0x000003FF, 0), + .ia = REQUEST_MEMBER(1, 0x000FFC00, 10), + .fm = REQUEST_MEMBER(1, 0x00700000, 20), + .pm = REQUEST_MEMBER(1, 0x00800000, 23), + .freq = REQUEST_MEMBER(1, 0x1F000000, 24), + .freq_clk_src = REQUEST_MEMBER(1, 0x60000000, 29), +}; + +static struct rpm_vreg_parts switch_parts = { + .request_len = 1, + .enable_state = REQUEST_MEMBER(0, 0x00000001, 0), + .pd = REQUEST_MEMBER(0, 0x00000002, 1), + .pc = REQUEST_MEMBER(0, 0x0000003C, 2), + .pf = REQUEST_MEMBER(0, 0x000003C0, 6), + .hpm = REQUEST_MEMBER(0, 0x00000C00, 10), +}; + +static struct rpm_vreg_parts ncp_parts = { + .request_len = 1, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .enable_state = REQUEST_MEMBER(0, 0x00800000, 23), + .comp_mode = REQUEST_MEMBER(0, 0x01000000, 24), + .freq = REQUEST_MEMBER(0, 0x3E000000, 25), +}; + +/* Physically available PMIC regulator voltage setpoint ranges */ +static struct vreg_range pldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), + VOLTAGE_RANGE(3100000, 4900000, 50000), +}; + +static struct vreg_range nldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range nldo1200_ranges[] = { + VOLTAGE_RANGE( 375000, 743750, 6250), + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range smps_ranges[] = { + VOLTAGE_RANGE( 375000, 737500, 12500), + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), +}; + +static struct vreg_range ftsmps_ranges[] = { + VOLTAGE_RANGE( 350000, 650000, 50000), + VOLTAGE_RANGE( 700000, 1400000, 12500), + VOLTAGE_RANGE(1500000, 3300000, 50000), +}; + +static struct vreg_range ncp_ranges[] = { + VOLTAGE_RANGE(1500000, 3050000, 50000), +}; + +static struct vreg_set_points pldo_set_points = SET_POINTS(pldo_ranges); +static struct vreg_set_points nldo_set_points = SET_POINTS(nldo_ranges); +static struct vreg_set_points nldo1200_set_points = SET_POINTS(nldo1200_ranges); +static struct vreg_set_points smps_set_points = SET_POINTS(smps_ranges); +static struct vreg_set_points ftsmps_set_points = SET_POINTS(ftsmps_ranges); +static struct vreg_set_points ncp_set_points = SET_POINTS(ncp_ranges); + +static struct vreg_set_points *all_set_points[] = { + &pldo_set_points, + &nldo_set_points, + &nldo1200_set_points, + &smps_set_points, + &ftsmps_set_points, + &ncp_set_points, +}; + +#define LDO(_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_PM8921_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8921_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_PM8921_##_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_8960_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_LDO, \ + .set_points = &_ranges##_set_points, \ + .part = &ldo_parts, \ + .id = RPM_VREG_ID_PM8921_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define SMPS(_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_PM8921_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8921_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_PM8921_##_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_8960_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_SMPS, \ + .set_points = &_ranges##_set_points, \ + .part = &smps_parts, \ + .id = RPM_VREG_ID_PM8921_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define LVS(_id, _name, _name_pc) \ + [RPM_VREG_ID_PM8921_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8921_##_id, }, \ + [1] = { .id = -1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_VS, \ + .part = &switch_parts, \ + .id = RPM_VREG_ID_PM8921_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define MVS(_vreg_id, _name, _name_pc, _rpm_id) \ + [RPM_VREG_ID_PM8921_##_vreg_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_rpm_id, }, \ + [1] = { .id = -1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_VS, \ + .part = &switch_parts, \ + .id = RPM_VREG_ID_PM8921_##_vreg_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define NCP(_id, _name, _name_pc) \ + [RPM_VREG_ID_PM8921_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_##_id##_1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_NCP, \ + .set_points = &ncp_set_points, \ + .part = &ncp_parts, \ + .id = RPM_VREG_ID_PM8921_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +static struct vreg vregs[] = { + LDO(L1, "8921_l1", "8921_l1_pc", nldo, LDO_150), + LDO(L2, "8921_l2", "8921_l2_pc", nldo, LDO_150), + LDO(L3, "8921_l3", "8921_l3_pc", pldo, LDO_150), + LDO(L4, "8921_l4", "8921_l4_pc", pldo, LDO_50), + LDO(L5, "8921_l5", "8921_l5_pc", pldo, LDO_300), + LDO(L6, "8921_l6", "8921_l6_pc", pldo, LDO_600), + LDO(L7, "8921_l7", "8921_l7_pc", pldo, LDO_150), + LDO(L8, "8921_l8", "8921_l8_pc", pldo, LDO_300), + LDO(L9, "8921_l9", "8921_l9_pc", pldo, LDO_300), + LDO(L10, "8921_l10", "8921_l10_pc", pldo, LDO_600), + LDO(L11, "8921_l11", "8921_l11_pc", pldo, LDO_150), + LDO(L12, "8921_l12", "8921_l12_pc", nldo, LDO_150), + LDO(L14, "8921_l14", "8921_l14_pc", pldo, LDO_50), + LDO(L15, "8921_l15", "8921_l15_pc", pldo, LDO_150), + LDO(L16, "8921_l16", "8921_l16_pc", pldo, LDO_300), + LDO(L17, "8921_l17", "8921_l17_pc", pldo, LDO_150), + LDO(L18, "8921_l18", "8921_l18_pc", nldo, LDO_150), + LDO(L21, "8921_l21", "8921_l21_pc", pldo, LDO_150), + LDO(L22, "8921_l22", "8921_l22_pc", pldo, LDO_150), + LDO(L23, "8921_l23", "8921_l23_pc", pldo, LDO_150), + LDO(L24, "8921_l24", NULL, nldo1200, LDO_1200), + LDO(L25, "8921_l25", NULL, nldo1200, LDO_1200), + LDO(L26, "8921_l26", NULL, nldo1200, LDO_1200), + LDO(L27, "8921_l27", NULL, nldo1200, LDO_1200), + LDO(L28, "8921_l28", NULL, nldo1200, LDO_1200), + LDO(L29, "8921_l29", "8921_l29_pc", pldo, LDO_150), + + SMPS(S1, "8921_s1", "8921_s1_pc", smps, SMPS_1500), + SMPS(S2, "8921_s2", "8921_s2_pc", smps, SMPS_1500), + SMPS(S3, "8921_s3", "8921_s3_pc", smps, SMPS_1500), + SMPS(S4, "8921_s4", "8921_s4_pc", smps, SMPS_1500), + SMPS(S5, "8921_s5", NULL, ftsmps, SMPS_2000), + SMPS(S6, "8921_s6", NULL, ftsmps, SMPS_2000), + SMPS(S7, "8921_s7", "8921_s7_pc", smps, SMPS_1500), + SMPS(S8, "8921_s8", "8921_s8_pc", smps, SMPS_1500), + + LVS(LVS1, "8921_lvs1", "8921_lvs1_pc"), + LVS(LVS2, "8921_lvs2", NULL), + LVS(LVS3, "8921_lvs3", "8921_lvs3_pc"), + LVS(LVS4, "8921_lvs4", "8921_lvs4_pc"), + LVS(LVS5, "8921_lvs5", "8921_lvs5_pc"), + LVS(LVS6, "8921_lvs6", "8921_lvs6_pc"), + LVS(LVS7, "8921_lvs7", "8921_lvs7_pc"), + MVS(USB_OTG, "8921_usb_otg", NULL, USB_OTG_SWITCH), + MVS(HDMI_MVS, "8921_hdmi_mvs", NULL, HDMI_SWITCH), + + NCP(NCP, "8921_ncp", NULL), +}; + +static const char *pin_func_label[] = { + [RPM_VREG_PIN_FN_8960_DONT_CARE] = "don't care", + [RPM_VREG_PIN_FN_8960_ENABLE] = "on/off", + [RPM_VREG_PIN_FN_8960_MODE] = "HPM/LPM", + [RPM_VREG_PIN_FN_8960_SLEEP_B] = "sleep_b", + [RPM_VREG_PIN_FN_8960_NONE] = "none", +}; + +static const char *force_mode_label[] = { + [RPM_VREG_FORCE_MODE_8960_NONE] = "none", + [RPM_VREG_FORCE_MODE_8960_LPM] = "LPM", + [RPM_VREG_FORCE_MODE_8960_AUTO] = "auto", + [RPM_VREG_FORCE_MODE_8960_HPM] = "HPM", + [RPM_VREG_FORCE_MODE_8960_BYPASS] = "BYP", +}; + +static const char *power_mode_label[] = { + [RPM_VREG_POWER_MODE_8960_HYSTERETIC] = "HYS", + [RPM_VREG_POWER_MODE_8960_PWM] = "PWM", +}; + +static const char *pin_control_label[] = { + " D1", + " A0", + " A1", + " A2", +}; + +static int is_real_id(int id) +{ + return (id >= 0) && (id <= RPM_VREG_ID_PM8921_MAX_REAL); +} + +static int pc_id_to_real_id(int id) +{ + int real_id; + + if (id >= RPM_VREG_ID_PM8921_L1_PC && id <= RPM_VREG_ID_PM8921_L23_PC) + real_id = id - RPM_VREG_ID_PM8921_L1_PC; + else if (id >= RPM_VREG_ID_PM8921_L29_PC + && id <= RPM_VREG_ID_PM8921_S4_PC) + real_id = id - RPM_VREG_ID_PM8921_L29_PC + + RPM_VREG_ID_PM8921_L29; + else if (id >= RPM_VREG_ID_PM8921_S7_PC + && id <= RPM_VREG_ID_PM8921_LVS1_PC) + real_id = id - RPM_VREG_ID_PM8921_S7_PC + RPM_VREG_ID_PM8921_S7; + else + real_id = id - RPM_VREG_ID_PM8921_LVS3_PC + + RPM_VREG_ID_PM8921_LVS3; + + return real_id; +} + +static struct vreg_config config = { + .vregs = vregs, + .vregs_len = ARRAY_SIZE(vregs), + + .vreg_id_min = RPM_VREG_ID_PM8921_L1, + .vreg_id_max = RPM_VREG_ID_PM8921_MAX, + + .pin_func_none = RPM_VREG_PIN_FN_8960_NONE, + .pin_func_sleep_b = RPM_VREG_PIN_FN_8960_SLEEP_B, + + .mode_lpm = REGULATOR_MODE_IDLE, + .mode_hpm = REGULATOR_MODE_NORMAL, + + .set_points = all_set_points, + .set_points_len = ARRAY_SIZE(all_set_points), + + .label_pin_ctrl = pin_control_label, + .label_pin_ctrl_len = ARRAY_SIZE(pin_control_label), + .label_pin_func = pin_func_label, + .label_pin_func_len = ARRAY_SIZE(pin_func_label), + .label_force_mode = force_mode_label, + .label_force_mode_len = ARRAY_SIZE(force_mode_label), + .label_power_mode = power_mode_label, + .label_power_mode_len = ARRAY_SIZE(power_mode_label), + + .is_real_id = is_real_id, + .pc_id_to_real_id = pc_id_to_real_id, +}; + +struct vreg_config *get_config_8960(void) +{ + return &config; +} diff --git a/arch/arm/mach-msm/rpm-regulator-9615.c b/arch/arm/mach-msm/rpm-regulator-9615.c new file mode 100644 index 00000000000..23c0ee33ae2 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator-9615.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include "rpm-regulator-private.h" + +/* RPM regulator request formats */ +static struct rpm_vreg_parts ldo_parts = { + .request_len = 2, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .pd = REQUEST_MEMBER(0, 0x00800000, 23), + .pc = REQUEST_MEMBER(0, 0x0F000000, 24), + .pf = REQUEST_MEMBER(0, 0xF0000000, 28), + .ip = REQUEST_MEMBER(1, 0x000003FF, 0), + .ia = REQUEST_MEMBER(1, 0x000FFC00, 10), + .fm = REQUEST_MEMBER(1, 0x00700000, 20), +}; + +static struct rpm_vreg_parts smps_parts = { + .request_len = 2, + .uV = REQUEST_MEMBER(0, 0x007FFFFF, 0), + .pd = REQUEST_MEMBER(0, 0x00800000, 23), + .pc = REQUEST_MEMBER(0, 0x0F000000, 24), + .pf = REQUEST_MEMBER(0, 0xF0000000, 28), + .ip = REQUEST_MEMBER(1, 0x000003FF, 0), + .ia = REQUEST_MEMBER(1, 0x000FFC00, 10), + .fm = REQUEST_MEMBER(1, 0x00700000, 20), + .pm = REQUEST_MEMBER(1, 0x00800000, 23), + .freq = REQUEST_MEMBER(1, 0x1F000000, 24), + .freq_clk_src = REQUEST_MEMBER(1, 0x60000000, 29), +}; + +static struct rpm_vreg_parts switch_parts = { + .request_len = 1, + .enable_state = REQUEST_MEMBER(0, 0x00000001, 0), + .pd = REQUEST_MEMBER(0, 0x00000002, 1), + .pc = REQUEST_MEMBER(0, 0x0000003C, 2), + .pf = REQUEST_MEMBER(0, 0x000003C0, 6), + .hpm = REQUEST_MEMBER(0, 0x00000C00, 10), +}; + +/* Physically available PMIC regulator voltage setpoint ranges */ +static struct vreg_range pldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), + VOLTAGE_RANGE(3100000, 4900000, 50000), +}; + +static struct vreg_range nldo_ranges[] = { + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range nldo1200_ranges[] = { + VOLTAGE_RANGE( 375000, 743750, 6250), + VOLTAGE_RANGE( 750000, 1537500, 12500), +}; + +static struct vreg_range smps_ranges[] = { + VOLTAGE_RANGE( 375000, 737500, 12500), + VOLTAGE_RANGE( 750000, 1487500, 12500), + VOLTAGE_RANGE(1500000, 3075000, 25000), +}; + +static struct vreg_set_points pldo_set_points = SET_POINTS(pldo_ranges); +static struct vreg_set_points nldo_set_points = SET_POINTS(nldo_ranges); +static struct vreg_set_points nldo1200_set_points = SET_POINTS(nldo1200_ranges); +static struct vreg_set_points smps_set_points = SET_POINTS(smps_ranges); + +static struct vreg_set_points *all_set_points[] = { + &pldo_set_points, + &nldo_set_points, + &nldo1200_set_points, + &smps_set_points, +}; + +#define LDO(_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_PM8018_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8018_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_PM8018_##_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_9615_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_LDO, \ + .set_points = &_ranges##_set_points, \ + .part = &ldo_parts, \ + .id = RPM_VREG_ID_PM8018_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define SMPS(_id, _name, _name_pc, _ranges, _hpm_min_load) \ + [RPM_VREG_ID_PM8018_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8018_##_id##_0, }, \ + [1] = { .id = MSM_RPM_ID_PM8018_##_id##_1, }, \ + }, \ + .hpm_min_load = RPM_VREG_9615_##_hpm_min_load##_HPM_MIN_LOAD, \ + .type = RPM_REGULATOR_TYPE_SMPS, \ + .set_points = &_ranges##_set_points, \ + .part = &smps_parts, \ + .id = RPM_VREG_ID_PM8018_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +#define LVS(_id, _name, _name_pc) \ + [RPM_VREG_ID_PM8018_##_id] = { \ + .req = { \ + [0] = { .id = MSM_RPM_ID_PM8018_##_id, }, \ + [1] = { .id = -1, }, \ + }, \ + .type = RPM_REGULATOR_TYPE_VS, \ + .part = &switch_parts, \ + .id = RPM_VREG_ID_PM8018_##_id, \ + .rdesc.name = _name, \ + .rdesc_pc.name = _name_pc, \ + } + +static struct vreg vregs[] = { + LDO(L2, "8018_l2", "8018_l2_pc", pldo, LDO_50), + LDO(L3, "8018_l3", "8018_l3_pc", pldo, LDO_50), + LDO(L4, "8018_l4", "8018_l4_pc", pldo, LDO_300), + LDO(L5, "8018_l5", "8018_l5_pc", pldo, LDO_150), + LDO(L6, "8018_l6", "8018_l6_pc", pldo, LDO_150), + LDO(L7, "8018_l7", "8018_l7_pc", pldo, LDO_300), + LDO(L8, "8018_l8", "8018_l8_pc", nldo, LDO_150), + LDO(L9, "8018_l9", NULL, nldo1200, LDO_1200), + LDO(L10, "8018_l10", NULL, nldo1200, LDO_1200), + LDO(L11, "8018_l11", NULL, nldo1200, LDO_1200), + LDO(L12, "8018_l12", NULL, nldo1200, LDO_1200), + LDO(L13, "8018_l13", "8018_l13_pc", pldo, LDO_50), + LDO(L14, "8018_l14", "8018_l14_pc", pldo, LDO_50), + + SMPS(S1, "8018_s1", "8018_s1_pc", smps, SMPS_1500), + SMPS(S2, "8018_s2", "8018_s2_pc", smps, SMPS_1500), + SMPS(S3, "8018_s3", "8018_s3_pc", smps, SMPS_1500), + SMPS(S4, "8018_s4", "8018_s4_pc", smps, SMPS_1500), + SMPS(S5, "8018_s5", "8018_s5_pc", smps, SMPS_1500), + + LVS(LVS1, "8018_lvs1", "8018_lvs1_pc"), +}; + +static const char *pin_control_label[] = { + " D1", + " A0", + " A1", + " A2", +}; + +static const char *pin_func_label[] = { + [RPM_VREG_PIN_FN_9615_DONT_CARE] = "don't care", + [RPM_VREG_PIN_FN_9615_ENABLE] = "on/off", + [RPM_VREG_PIN_FN_9615_MODE] = "HPM/LPM", + [RPM_VREG_PIN_FN_9615_SLEEP_B] = "sleep_b", + [RPM_VREG_PIN_FN_9615_NONE] = "none", +}; + +static const char *force_mode_label[] = { + [RPM_VREG_FORCE_MODE_9615_NONE] = "none", + [RPM_VREG_FORCE_MODE_9615_LPM] = "LPM", + [RPM_VREG_FORCE_MODE_9615_AUTO] = "auto", + [RPM_VREG_FORCE_MODE_9615_HPM] = "HPM", + [RPM_VREG_FORCE_MODE_9615_BYPASS] = "BYP", +}; + +static const char *power_mode_label[] = { + [RPM_VREG_POWER_MODE_9615_HYSTERETIC] = "HYS", + [RPM_VREG_POWER_MODE_9615_PWM] = "PWM", +}; + +static int is_real_id(int id) +{ + return (id >= 0) && (id <= RPM_VREG_ID_PM8018_MAX_REAL); +} + +static int pc_id_to_real_id(int id) +{ + int real_id; + + if (id >= RPM_VREG_ID_PM8018_L2_PC && id <= RPM_VREG_ID_PM8018_L8_PC) + real_id = id - RPM_VREG_ID_PM8018_L2_PC + RPM_VREG_ID_PM8018_L2; + else + real_id = id - RPM_VREG_ID_PM8018_L13_PC + + RPM_VREG_ID_PM8018_L13; + + return real_id; +} + +static struct vreg_config config = { + .vregs = vregs, + .vregs_len = ARRAY_SIZE(vregs), + + .vreg_id_min = RPM_VREG_ID_PM8018_L2, + .vreg_id_max = RPM_VREG_ID_PM8018_MAX, + + .pin_func_none = RPM_VREG_PIN_FN_9615_NONE, + .pin_func_sleep_b = RPM_VREG_PIN_FN_9615_SLEEP_B, + + .mode_lpm = REGULATOR_MODE_IDLE, + .mode_hpm = REGULATOR_MODE_NORMAL, + + .set_points = all_set_points, + .set_points_len = ARRAY_SIZE(all_set_points), + + .label_pin_ctrl = pin_control_label, + .label_pin_ctrl_len = ARRAY_SIZE(pin_control_label), + .label_pin_func = pin_func_label, + .label_pin_func_len = ARRAY_SIZE(pin_func_label), + .label_force_mode = force_mode_label, + .label_force_mode_len = ARRAY_SIZE(force_mode_label), + .label_power_mode = power_mode_label, + .label_power_mode_len = ARRAY_SIZE(power_mode_label), + + .is_real_id = is_real_id, + .pc_id_to_real_id = pc_id_to_real_id, +}; + +struct vreg_config *get_config_9615(void) +{ + return &config; +} diff --git a/arch/arm/mach-msm/rpm-regulator-private.h b/arch/arm/mach-msm/rpm-regulator-private.h new file mode 100644 index 00000000000..d4f9a8a1668 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator-private.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_REGULATOR_INT_H +#define __ARCH_ARM_MACH_MSM_RPM_REGULATOR_INT_H + +#include +#include +#include + +/* Possible RPM regulator request types */ +enum rpm_regulator_type { + RPM_REGULATOR_TYPE_LDO, + RPM_REGULATOR_TYPE_SMPS, + RPM_REGULATOR_TYPE_VS, + RPM_REGULATOR_TYPE_NCP, + RPM_REGULATOR_TYPE_CORNER, + RPM_REGULATOR_TYPE_MAX = RPM_REGULATOR_TYPE_CORNER, +}; + +struct request_member { + int word; + unsigned int mask; + int shift; +}; + +/* Possible RPM regulator request members */ +struct rpm_vreg_parts { + struct request_member mV; /* voltage: used if voltage is in mV */ + struct request_member uV; /* voltage: used if voltage is in uV */ + struct request_member ip; /* peak current in mA */ + struct request_member pd; /* pull down enable */ + struct request_member ia; /* average current in mA */ + struct request_member fm; /* force mode */ + struct request_member pm; /* power mode */ + struct request_member pc; /* pin control */ + struct request_member pf; /* pin function */ + struct request_member enable_state; /* NCP and switch */ + struct request_member comp_mode; /* NCP */ + struct request_member freq; /* frequency: NCP and SMPS */ + struct request_member freq_clk_src; /* clock source: SMPS */ + struct request_member hpm; /* switch: control OCP and SS */ + int request_len; +}; + +struct vreg_range { + int min_uV; + int max_uV; + int step_uV; + unsigned n_voltages; +}; + +struct vreg_set_points { + struct vreg_range *range; + int count; + unsigned n_voltages; +}; + +struct vreg { + struct msm_rpm_iv_pair req[2]; + struct msm_rpm_iv_pair prev_active_req[2]; + struct msm_rpm_iv_pair prev_sleep_req[2]; + struct rpm_regulator_init_data pdata; + struct regulator_desc rdesc; + struct regulator_desc rdesc_pc; + struct regulator_dev *rdev; + struct regulator_dev *rdev_pc; + struct vreg_set_points *set_points; + struct rpm_vreg_parts *part; + int type; + int id; + struct mutex pc_lock; + int save_uV; + int mode; + bool is_enabled; + bool is_enabled_pc; + const int hpm_min_load; + int active_min_uV_vote[RPM_VREG_VOTER_COUNT]; + int sleep_min_uV_vote[RPM_VREG_VOTER_COUNT]; +}; + +struct vreg_config { + struct vreg *vregs; + int vregs_len; + + int vreg_id_min; + int vreg_id_max; + + int pin_func_none; + int pin_func_sleep_b; + + unsigned int mode_lpm; + unsigned int mode_hpm; + + struct vreg_set_points **set_points; + int set_points_len; + + const char **label_pin_ctrl; + int label_pin_ctrl_len; + const char **label_pin_func; + int label_pin_func_len; + const char **label_force_mode; + int label_force_mode_len; + const char **label_power_mode; + int label_power_mode_len; + + int (*is_real_id) (int vreg_id); + int (*pc_id_to_real_id) (int vreg_id); + + /* Legacy options to be used with MSM8660 */ + int use_legacy_optimum_mode; + int ia_follows_ip; +}; + +#define REQUEST_MEMBER(_word, _mask, _shift) \ + { \ + .word = _word, \ + .mask = _mask, \ + .shift = _shift, \ + } + +#define VOLTAGE_RANGE(_min_uV, _max_uV, _step_uV) \ + { \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .step_uV = _step_uV, \ + } + +#define SET_POINTS(_ranges) \ +{ \ + .range = _ranges, \ + .count = ARRAY_SIZE(_ranges), \ +}; + +#define MICRO_TO_MILLI(uV) ((uV) / 1000) +#define MILLI_TO_MICRO(mV) ((mV) * 1000) + +#if defined(CONFIG_MSM_RPM_REGULATOR) && defined(CONFIG_ARCH_MSM8X60) +struct vreg_config *get_config_8660(void); +#else +static inline struct vreg_config *get_config_8660(void) +{ + return NULL; +} +#endif + +#if defined(CONFIG_MSM_RPM_REGULATOR) && \ + (defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064)) +struct vreg_config *get_config_8960(void); +#else +static inline struct vreg_config *get_config_8960(void) +{ + return NULL; +} +#endif + +#if defined(CONFIG_MSM_RPM_REGULATOR) && defined(CONFIG_ARCH_MSM9615) +struct vreg_config *get_config_9615(void); +#else +static inline struct vreg_config *get_config_9615(void) +{ + return NULL; +} +#endif + +#if defined(CONFIG_MSM_RPM_REGULATOR) && defined(CONFIG_ARCH_MSM8930) +struct vreg_config *get_config_8930(void); +#else +static inline struct vreg_config *get_config_8930(void) +{ + return NULL; +} +#endif + +#endif diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c new file mode 100644 index 00000000000..b892d059ec7 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator-smd.c @@ -0,0 +1,1430 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Debug Definitions */ + +enum { + RPM_VREG_DEBUG_REQUEST = BIT(0), + RPM_VREG_DEBUG_FULL_REQUEST = BIT(1), + RPM_VREG_DEBUG_DUPLICATE = BIT(2), +}; + +static int rpm_vreg_debug_mask; +module_param_named( + debug_mask, rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR +); + +#define vreg_err(req, fmt, ...) \ + pr_err("%s: " fmt, req->rdesc.name, ##__VA_ARGS__) + +/* RPM regulator request types */ +enum rpm_regulator_smd_type { + RPM_REGULATOR_SMD_TYPE_LDO, + RPM_REGULATOR_SMD_TYPE_SMPS, + RPM_REGULATOR_SMD_TYPE_VS, + RPM_REGULATOR_SMD_TYPE_NCP, + RPM_REGULATOR_SMD_TYPE_MAX, +}; + +/* RPM resource parameters */ +enum rpm_regulator_param_index { + RPM_REGULATOR_PARAM_ENABLE, + RPM_REGULATOR_PARAM_VOLTAGE, + RPM_REGULATOR_PARAM_CURRENT, + RPM_REGULATOR_PARAM_MODE_LDO, + RPM_REGULATOR_PARAM_MODE_SMPS, + RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE, + RPM_REGULATOR_PARAM_PIN_CTRL_MODE, + RPM_REGULATOR_PARAM_FREQUENCY, + RPM_REGULATOR_PARAM_HEAD_ROOM, + RPM_REGULATOR_PARAM_QUIET_MODE, + RPM_REGULATOR_PARAM_FREQ_REASON, + RPM_REGULATOR_PARAM_MAX, +}; + +#define RPM_SET_CONFIG_ACTIVE BIT(0) +#define RPM_SET_CONFIG_SLEEP BIT(1) +#define RPM_SET_CONFIG_BOTH (RPM_SET_CONFIG_ACTIVE \ + | RPM_SET_CONFIG_SLEEP) +struct rpm_regulator_param { + char *name; + char *property_name; + u32 key; + u32 min; + u32 max; + u32 supported_regulator_types; +}; + +#define PARAM(_idx, _support_ldo, _support_smps, _support_vs, _support_ncp, \ + _name, _min, _max, _property_name) \ + [RPM_REGULATOR_PARAM_##_idx] = { \ + .name = _name, \ + .property_name = _property_name, \ + .min = _min, \ + .max = _max, \ + .supported_regulator_types = \ + _support_ldo << RPM_REGULATOR_SMD_TYPE_LDO | \ + _support_smps << RPM_REGULATOR_SMD_TYPE_SMPS | \ + _support_vs << RPM_REGULATOR_SMD_TYPE_VS | \ + _support_ncp << RPM_REGULATOR_SMD_TYPE_NCP, \ + } + +static struct rpm_regulator_param params[RPM_REGULATOR_PARAM_MAX] = { + /* ID LDO SMPS VS NCP name min max property-name */ + PARAM(ENABLE, 1, 1, 1, 1, "swen", 0, 1, "qcom,init-enable"), + PARAM(VOLTAGE, 1, 1, 0, 1, "uv", 0, 0x7FFFFFF, "qcom,init-voltage"), + PARAM(CURRENT, 1, 1, 0, 0, "ma", 0, 0x1FFF, "qcom,init-current"), + PARAM(MODE_LDO, 1, 0, 0, 0, "lsmd", 0, 1, "qcom,init-ldo-mode"), + PARAM(MODE_SMPS, 0, 1, 0, 0, "ssmd", 0, 2, "qcom,init-smps-mode"), + PARAM(PIN_CTRL_ENABLE, 1, 1, 1, 0, "pcen", 0, 0xF, "qcom,init-pin-ctrl-enable"), + PARAM(PIN_CTRL_MODE, 1, 1, 1, 0, "pcmd", 0, 0x1F, "qcom,init-pin-ctrl-mode"), + PARAM(FREQUENCY, 0, 1, 0, 1, "freq", 0, 16, "qcom,init-frequency"), + PARAM(HEAD_ROOM, 1, 0, 0, 1, "hr", 0, 0x7FFFFFFF, "qcom,init-head-room"), + PARAM(QUIET_MODE, 0, 1, 0, 0, "qm", 0, 2, "qcom,init-quiet-mode"), + PARAM(FREQ_REASON, 0, 1, 0, 1, "resn", 0, 8, "qcom,init-freq-reason"), +}; + +struct rpm_vreg_request { + u32 param[RPM_REGULATOR_PARAM_MAX]; + u32 valid; + u32 modified; +}; + +struct rpm_vreg { + struct rpm_vreg_request aggr_req_active; + struct rpm_vreg_request aggr_req_sleep; + struct list_head reg_list; + const char *resource_name; + u32 resource_id; + bool allow_atomic; + int regulator_type; + int hpm_min_load; + int enable_time; + struct spinlock slock; + struct mutex mlock; + unsigned long flags; + bool sleep_request_sent; + struct msm_rpm_request *handle_active; + struct msm_rpm_request *handle_sleep; +}; + +struct rpm_regulator { + struct regulator_desc rdesc; + struct regulator_dev *rdev; + struct rpm_vreg *rpm_vreg; + struct list_head list; + bool set_active; + bool set_sleep; + struct rpm_vreg_request req; + int system_load; + int min_uV; + int max_uV; +}; + +/* + * This voltage in uV is returned by get_voltage functions when there is no way + * to determine the current voltage level. It is needed because the regulator + * framework treats a 0 uV voltage as an error. + */ +#define VOLTAGE_UNKNOWN 1 + +/* + * Regulator requests sent in the active set take effect immediately. Requests + * sent in the sleep set take effect when the Apps processor transitions into + * RPM assisted power collapse. For any given regulator, if an active set + * request is present, but not a sleep set request, then the active set request + * is used at all times, even when the Apps processor is power collapsed. + * + * The rpm-regulator-smd takes advantage of this default usage of the active set + * request by only sending a sleep set request if it differs from the + * corresponding active set request. + */ +#define RPM_SET_ACTIVE MSM_RPM_CTX_ACTIVE_SET +#define RPM_SET_SLEEP MSM_RPM_CTX_SLEEP_SET + +static u32 rpm_vreg_string_to_int(const u8 *str) +{ + int i, len; + u32 output = 0; + + len = strnlen(str, sizeof(u32)); + for (i = 0; i < len; i++) + output |= str[i] << (i * 8); + + return output; +} + +static inline void rpm_vreg_lock(struct rpm_vreg *rpm_vreg) +{ + if (rpm_vreg->allow_atomic) + spin_lock_irqsave(&rpm_vreg->slock, rpm_vreg->flags); + else + mutex_lock(&rpm_vreg->mlock); +} + +static inline void rpm_vreg_unlock(struct rpm_vreg *rpm_vreg) +{ + if (rpm_vreg->allow_atomic) + spin_unlock_irqrestore(&rpm_vreg->slock, rpm_vreg->flags); + else + mutex_unlock(&rpm_vreg->mlock); +} + +static inline bool rpm_vreg_active_or_sleep_enabled(struct rpm_vreg *rpm_vreg) +{ + return (rpm_vreg->aggr_req_active.param[RPM_REGULATOR_PARAM_ENABLE] + && (rpm_vreg->aggr_req_active.valid + & BIT(RPM_REGULATOR_PARAM_ENABLE))) + || ((rpm_vreg->aggr_req_sleep.param[RPM_REGULATOR_PARAM_ENABLE]) + && (rpm_vreg->aggr_req_sleep.valid + & BIT(RPM_REGULATOR_PARAM_ENABLE))); +} + +/* + * This is used when voting for LPM or HPM by subtracting or adding to the + * hpm_min_load of a regulator. It has units of uA. + */ +#define LOAD_THRESHOLD_STEP 1000 + +static inline int rpm_vreg_hpm_min_uA(struct rpm_vreg *rpm_vreg) +{ + return rpm_vreg->hpm_min_load; +} + +static inline int rpm_vreg_lpm_max_uA(struct rpm_vreg *rpm_vreg) +{ + return rpm_vreg->hpm_min_load - LOAD_THRESHOLD_STEP; +} + +#define MICRO_TO_MILLI(uV) ((uV) / 1000) +#define MILLI_TO_MICRO(uV) ((uV) * 1000) + +#define DEBUG_PRINT_BUFFER_SIZE 512 +#define REQ_SENT 0 +#define REQ_PREV 1 +#define REQ_CACHED 2 +#define REQ_TYPES 3 + +static void rpm_regulator_req(struct rpm_regulator *regulator, int set, + bool sent) +{ + char buf[DEBUG_PRINT_BUFFER_SIZE]; + size_t buflen = DEBUG_PRINT_BUFFER_SIZE; + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + struct rpm_vreg_request *aggr; + bool first; + u32 mask[REQ_TYPES] = {0, 0, 0}; + const char *req_names[REQ_TYPES] = {"sent", "prev", "cached"}; + int pos = 0; + int i, j; + + aggr = (set == RPM_SET_ACTIVE) + ? &rpm_vreg->aggr_req_active : &rpm_vreg->aggr_req_sleep; + + if (rpm_vreg_debug_mask & RPM_VREG_DEBUG_DUPLICATE) { + mask[REQ_SENT] = aggr->modified; + mask[REQ_PREV] = aggr->valid & ~aggr->modified; + } else if (sent + && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_FULL_REQUEST)) { + mask[REQ_SENT] = aggr->modified; + mask[REQ_PREV] = aggr->valid & ~aggr->modified; + } else if (sent && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_REQUEST)) { + mask[REQ_SENT] = aggr->modified; + } + + if (!(mask[REQ_SENT] | mask[REQ_PREV])) + return; + + if (set == RPM_SET_SLEEP && !rpm_vreg->sleep_request_sent) { + mask[REQ_CACHED] = mask[REQ_SENT] | mask[REQ_PREV]; + mask[REQ_SENT] = 0; + mask[REQ_PREV] = 0; + } + + pos += scnprintf(buf + pos, buflen - pos, "%s%s: ", + KERN_INFO, __func__); + + pos += scnprintf(buf + pos, buflen - pos, "%s %u (%s): s=%s", + rpm_vreg->resource_name, rpm_vreg->resource_id, + regulator->rdesc.name, + (set == RPM_SET_ACTIVE ? "act" : "slp")); + + for (i = 0; i < REQ_TYPES; i++) { + if (mask[i]) + pos += scnprintf(buf + pos, buflen - pos, "; %s: ", + req_names[i]); + + first = true; + for (j = 0; j < RPM_REGULATOR_PARAM_MAX; j++) { + if (mask[i] & BIT(j)) { + pos += scnprintf(buf + pos, buflen - pos, + "%s%s=%u", (first ? "" : ", "), + params[j].name, aggr->param[j]); + first = false; + } + } + } + + pos += scnprintf(buf + pos, buflen - pos, "\n"); + printk(buf); +} + +#define RPM_VREG_SET_PARAM(_regulator, _param, _val) \ +{ \ + (_regulator)->req.param[RPM_REGULATOR_PARAM_##_param] = _val; \ + (_regulator)->req.modified |= BIT(RPM_REGULATOR_PARAM_##_param); \ +} \ + +static int rpm_vreg_add_kvp_to_request(struct rpm_vreg *rpm_vreg, + const u32 *param, int idx, u32 set) +{ + struct msm_rpm_request *handle; + + handle = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active + : rpm_vreg->handle_sleep); + + if (rpm_vreg->allow_atomic) + return msm_rpm_add_kvp_data_noirq(handle, params[idx].key, + (u8 *)¶m[idx], 4); + else + return msm_rpm_add_kvp_data(handle, params[idx].key, + (u8 *)¶m[idx], 4); +} + +static void rpm_vreg_check_modified_requests(const u32 *prev_param, + const u32 *param, u32 prev_valid, u32 *modified) +{ + u32 value_changed = 0; + int i; + + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + if (param[i] != prev_param[i]) + value_changed |= BIT(i); + } + + /* + * Only keep bits that are for changed parameters or previously + * invalid parameters. + */ + *modified &= value_changed | ~prev_valid; +} + +static int rpm_vreg_add_modified_requests(struct rpm_regulator *regulator, + u32 set, const u32 *param, u32 modified) +{ + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + int rc = 0; + int i; + + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + /* Only send requests for modified parameters. */ + if (modified & BIT(i)) { + rc = rpm_vreg_add_kvp_to_request(rpm_vreg, param, i, + set); + if (rc) { + vreg_err(regulator, + "add KVP failed: %s %u; %s, rc=%d\n", + rpm_vreg->resource_name, + rpm_vreg->resource_id, params[i].name, + rc); + return rc; + } + } + } + + return rc; +} + +static int rpm_vreg_send_request(struct rpm_regulator *regulator, u32 set) +{ + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + struct msm_rpm_request *handle + = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active + : rpm_vreg->handle_sleep); + int rc; + + if (rpm_vreg->allow_atomic) + rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq( + handle)); + else + rc = msm_rpm_wait_for_ack(msm_rpm_send_request(handle)); + + if (rc) + vreg_err(regulator, "msm rpm send failed: %s %u; set=%s, " + "rc=%d\n", rpm_vreg->resource_name, + rpm_vreg->resource_id, + (set == RPM_SET_ACTIVE ? "act" : "slp"), rc); + + return rc; +} + +#define RPM_VREG_AGGR_MAX(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + = max(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \ + _param_reg[RPM_REGULATOR_PARAM_##_idx]); \ +} + +#define RPM_VREG_AGGR_SUM(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + += _param_reg[RPM_REGULATOR_PARAM_##_idx]; \ +} + +#define RPM_VREG_AGGR_OR(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + |= _param_reg[RPM_REGULATOR_PARAM_##_idx]; \ +} + +/* + * The RPM treats freq=0 as a special value meaning that this consumer does not + * care what the SMPS switching freqency is. + */ +#define RPM_REGULATOR_FREQ_DONT_CARE 0 + +static inline void rpm_vreg_freqency_aggr(u32 *freq, u32 consumer_freq) +{ + if (consumer_freq != RPM_REGULATOR_FREQ_DONT_CARE + && (consumer_freq < *freq + || *freq == RPM_REGULATOR_FREQ_DONT_CARE)) + *freq = consumer_freq; +} + +/* + * Aggregation is performed on each parameter based on the way that the RPM + * aggregates that type internally between RPM masters. + */ +static void rpm_vreg_aggregate_params(u32 *param_aggr, const u32 *param_reg) +{ + RPM_VREG_AGGR_MAX(ENABLE, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(VOLTAGE, param_aggr, param_reg); + RPM_VREG_AGGR_SUM(CURRENT, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(MODE_LDO, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(MODE_SMPS, param_aggr, param_reg); + RPM_VREG_AGGR_OR(PIN_CTRL_ENABLE, param_aggr, param_reg); + RPM_VREG_AGGR_OR(PIN_CTRL_MODE, param_aggr, param_reg); + rpm_vreg_freqency_aggr(¶m_aggr[RPM_REGULATOR_PARAM_FREQUENCY], + param_reg[RPM_REGULATOR_PARAM_FREQUENCY]); + RPM_VREG_AGGR_MAX(HEAD_ROOM, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(QUIET_MODE, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(FREQ_REASON, param_aggr, param_reg); +} + +static int rpm_vreg_aggregate_requests(struct rpm_regulator *regulator) +{ + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + u32 param_active[RPM_REGULATOR_PARAM_MAX]; + u32 param_sleep[RPM_REGULATOR_PARAM_MAX]; + u32 modified_active, modified_sleep; + struct rpm_regulator *reg; + bool sleep_set_differs = false; + bool send_active = false; + bool send_sleep = false; + int rc = 0; + int i; + + memset(param_active, 0, sizeof(param_active)); + memset(param_sleep, 0, sizeof(param_sleep)); + modified_active = rpm_vreg->aggr_req_active.modified; + modified_sleep = rpm_vreg->aggr_req_sleep.modified; + + /* + * Aggregate all of the requests for this regulator in both active + * and sleep sets. + */ + list_for_each_entry(reg, &rpm_vreg->reg_list, list) { + if (reg->set_active) { + rpm_vreg_aggregate_params(param_active, reg->req.param); + modified_active |= reg->req.modified; + } + if (reg->set_sleep) { + rpm_vreg_aggregate_params(param_sleep, reg->req.param); + modified_sleep |= reg->req.modified; + } + } + + /* + * Check if the aggregated sleep set parameter values differ from the + * aggregated active set parameter values. + */ + if (!rpm_vreg->sleep_request_sent) { + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + if ((param_active[i] != param_sleep[i]) + && (modified_sleep & BIT(i))) { + sleep_set_differs = true; + break; + } + } + } + + /* Add KVPs to the active set RPM request if they have new values. */ + rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_active.param, + param_active, rpm_vreg->aggr_req_active.valid, + &modified_active); + rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_ACTIVE, + param_active, modified_active); + if (rc) + return rc; + send_active = modified_active; + + /* + * Sleep set configurations are only sent if they differ from the + * active set values. This is because the active set values will take + * effect during rpm assisted power collapse in the absence of sleep set + * values. + * + * However, once a sleep set request is sent for a given regulator, + * additional sleep set requests must be sent in the future even if they + * match the corresponding active set requests. + */ + if (rpm_vreg->sleep_request_sent || sleep_set_differs) { + /* Add KVPs to the sleep set RPM request if they are new. */ + rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_sleep.param, + param_sleep, rpm_vreg->aggr_req_sleep.valid, + &modified_sleep); + rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_SLEEP, + param_sleep, modified_sleep); + if (rc) + return rc; + send_sleep = modified_sleep; + } + + /* Send active set request to the RPM if it contains new KVPs. */ + if (send_active) { + rc = rpm_vreg_send_request(regulator, RPM_SET_ACTIVE); + if (rc) + return rc; + rpm_vreg->aggr_req_active.valid |= modified_active; + } + /* Store the results of the aggregation. */ + rpm_vreg->aggr_req_active.modified = modified_active; + memcpy(rpm_vreg->aggr_req_active.param, param_active, + sizeof(param_active)); + + /* Handle debug printing of the active set request. */ + rpm_regulator_req(regulator, RPM_SET_ACTIVE, send_active); + if (send_active) + rpm_vreg->aggr_req_active.modified = 0; + + /* Send sleep set request to the RPM if it contains new KVPs. */ + if (send_sleep) { + rc = rpm_vreg_send_request(regulator, RPM_SET_SLEEP); + if (rc) + return rc; + else + rpm_vreg->sleep_request_sent = true; + rpm_vreg->aggr_req_sleep.valid |= modified_sleep; + } + /* Store the results of the aggregation. */ + rpm_vreg->aggr_req_sleep.modified = modified_sleep; + memcpy(rpm_vreg->aggr_req_sleep.param, param_sleep, + sizeof(param_sleep)); + + /* Handle debug printing of the sleep set request. */ + rpm_regulator_req(regulator, RPM_SET_SLEEP, send_sleep); + if (send_sleep) + rpm_vreg->aggr_req_sleep.modified = 0; + + /* + * Loop over all requests for this regulator to update the valid and + * modified values for use in future aggregation. + */ + list_for_each_entry(reg, &rpm_vreg->reg_list, list) { + reg->req.valid |= reg->req.modified; + reg->req.modified = 0; + } + + return rc; +} + +static int rpm_vreg_is_enabled(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return reg->req.param[RPM_REGULATOR_PARAM_ENABLE]; +} + +static int rpm_vreg_enable(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc; + u32 prev_enable; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE]; + RPM_VREG_SET_PARAM(reg, ENABLE, 1); + rc = rpm_vreg_aggregate_requests(reg); + if (rc) { + vreg_err(reg, "enable failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_disable(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc; + u32 prev_enable; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE]; + RPM_VREG_SET_PARAM(reg, ENABLE, 0); + rc = rpm_vreg_aggregate_requests(reg); + if (rc) { + vreg_err(reg, "enable failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc = 0; + u32 prev_voltage; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_voltage = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE]; + RPM_VREG_SET_PARAM(reg, VOLTAGE, min_uV); + + /* Only send a new voltage if the regulator is currently enabled. */ + if (rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)) + rc = rpm_vreg_aggregate_requests(reg); + + if (rc) { + vreg_err(reg, "set voltage failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, VOLTAGE, prev_voltage); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_get_voltage(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int uV; + + uV = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE]; + if (uV == 0) + uV = VOLTAGE_UNKNOWN; + + return uV; +} + +static int rpm_vreg_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int uV = 0; + + if (selector == 0) + uV = reg->min_uV; + else if (selector == 1) + uV = reg->max_uV; + + return uV; +} + +static int rpm_vreg_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc = 0; + u32 prev_current; + int prev_uA; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_current = reg->req.param[RPM_REGULATOR_PARAM_CURRENT]; + prev_uA = MILLI_TO_MICRO(prev_current); + + if (mode == REGULATOR_MODE_NORMAL) { + /* Make sure that request current is in HPM range. */ + if (prev_uA < rpm_vreg_hpm_min_uA(reg->rpm_vreg)) + RPM_VREG_SET_PARAM(reg, CURRENT, + MICRO_TO_MILLI(rpm_vreg_hpm_min_uA(reg->rpm_vreg))); + } else if (REGULATOR_MODE_IDLE) { + /* Make sure that request current is in LPM range. */ + if (prev_uA > rpm_vreg_lpm_max_uA(reg->rpm_vreg)) + RPM_VREG_SET_PARAM(reg, CURRENT, + MICRO_TO_MILLI(rpm_vreg_lpm_max_uA(reg->rpm_vreg))); + } else { + vreg_err(reg, "invalid mode: %u\n", mode); + rpm_vreg_unlock(reg->rpm_vreg); + return -EINVAL; + } + + /* Only send a new mode value if the regulator is currently enabled. */ + if (rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)) + rc = rpm_vreg_aggregate_requests(reg); + + if (rc) { + vreg_err(reg, "set mode failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, CURRENT, prev_current); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static unsigned int rpm_vreg_get_mode(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return (reg->req.param[RPM_REGULATOR_PARAM_CURRENT] + >= MICRO_TO_MILLI(reg->rpm_vreg->hpm_min_load)) + ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; +} + +static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, int load_uA) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + u32 load_mA; + + load_uA += reg->system_load; + + load_mA = MICRO_TO_MILLI(load_uA); + if (load_mA > params[RPM_REGULATOR_PARAM_CURRENT].max) + load_mA = params[RPM_REGULATOR_PARAM_CURRENT].max; + + rpm_vreg_lock(reg->rpm_vreg); + RPM_VREG_SET_PARAM(reg, CURRENT, MICRO_TO_MILLI(load_uA)); + rpm_vreg_unlock(reg->rpm_vreg); + + return (load_uA >= reg->rpm_vreg->hpm_min_load) + ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; +} + +static int rpm_vreg_enable_time(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return reg->rpm_vreg->enable_time; +} + +/** + * rpm_regulator_get() - lookup and obtain a handle to an RPM regulator + * @dev: device for regulator consumer + * @supply: supply name + * + * Returns a struct rpm_regulator corresponding to the regulator producer, + * or ERR_PTR() containing errno. + * + * This function may only be called from nonatomic context. + */ +struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply) +{ + struct rpm_regulator *framework_reg; + struct rpm_regulator *priv_reg = NULL; + struct regulator *regulator; + struct rpm_vreg *rpm_vreg; + + regulator = regulator_get(dev, supply); + if (regulator == NULL) { + pr_err("could not find regulator for: dev=%s, id=%s\n", + (dev ? dev_name(dev) : ""), (supply ? supply : "")); + return ERR_PTR(-ENODEV); + } + + framework_reg = regulator_get_drvdata(regulator); + if (framework_reg == NULL) { + pr_err("regulator structure not found.\n"); + regulator_put(regulator); + return ERR_PTR(-ENODEV); + } + regulator_put(regulator); + + rpm_vreg = framework_reg->rpm_vreg; + + priv_reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL); + if (priv_reg == NULL) { + vreg_err(framework_reg, "could not allocate memory for " + "regulator\n"); + rpm_vreg_unlock(rpm_vreg); + return ERR_PTR(-ENOMEM); + } + + /* + * Allocate a regulator_dev struct so that framework callback functions + * can be called from the private API functions. + */ + priv_reg->rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); + if (priv_reg->rdev == NULL) { + vreg_err(framework_reg, "could not allocate memory for " + "regulator_dev\n"); + kfree(priv_reg); + rpm_vreg_unlock(rpm_vreg); + return ERR_PTR(-ENOMEM); + } + priv_reg->rdev->reg_data = priv_reg; + priv_reg->rpm_vreg = rpm_vreg; + priv_reg->rdesc.name = framework_reg->rdesc.name; + priv_reg->set_active = framework_reg->set_active; + priv_reg->set_sleep = framework_reg->set_sleep; + priv_reg->min_uV = framework_reg->min_uV; + priv_reg->max_uV = framework_reg->max_uV; + priv_reg->system_load = framework_reg->system_load; + + might_sleep_if(!rpm_vreg->allow_atomic); + rpm_vreg_lock(rpm_vreg); + list_add(&priv_reg->list, &rpm_vreg->reg_list); + rpm_vreg_unlock(rpm_vreg); + + return priv_reg; +} +EXPORT_SYMBOL_GPL(rpm_regulator_get); + +static int rpm_regulator_check_input(struct rpm_regulator *regulator) +{ + if (regulator == NULL || regulator->rpm_vreg == NULL) { + pr_err("invalid rpm_regulator pointer\n"); + return -EINVAL; + } + + might_sleep_if(!regulator->rpm_vreg->allow_atomic); + + return 0; +} + +/** + * rpm_regulator_put() - free the RPM regulator handle + * @regulator: RPM regulator handle + * + * Parameter reaggregation does not take place when rpm_regulator_put is called. + * Therefore, regulator enable state and voltage must be configured + * appropriately before calling rpm_regulator_put. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +void rpm_regulator_put(struct rpm_regulator *regulator) +{ + struct rpm_vreg *rpm_vreg; + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return; + + rpm_vreg = regulator->rpm_vreg; + + might_sleep_if(!rpm_vreg->allow_atomic); + rpm_vreg_lock(rpm_vreg); + list_del(®ulator->list); + rpm_vreg_unlock(rpm_vreg); + + kfree(regulator->rdev); + kfree(regulator); +} +EXPORT_SYMBOL_GPL(rpm_regulator_put); + +/** + * rpm_regulator_enable() - enable regulator output + * @regulator: RPM regulator handle + * + * Returns 0 on success or errno on failure. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +int rpm_regulator_enable(struct rpm_regulator *regulator) +{ + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return rc; + + return rpm_vreg_enable(regulator->rdev); +} +EXPORT_SYMBOL_GPL(rpm_regulator_enable); + +/** + * rpm_regulator_disable() - disable regulator output + * @regulator: RPM regulator handle + * + * Returns 0 on success or errno on failure. + * + * The enable state of the regulator is determined by aggregating the requests + * of all consumers. Therefore, it is possible that the regulator will remain + * enabled even after rpm_regulator_disable is called. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +int rpm_regulator_disable(struct rpm_regulator *regulator) +{ + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return rc; + + return rpm_vreg_disable(regulator->rdev); +} +EXPORT_SYMBOL_GPL(rpm_regulator_disable); + +/** + * rpm_regulator_set_voltage() - set regulator output voltage + * @regulator: RPM regulator handle + * @min_uV: minimum required voltage in uV + * @max_uV: maximum acceptable voltage in uV + * + * Sets a voltage regulator to the desired output voltage. This can be set + * while the regulator is disabled or enabled. If the regulator is enabled then + * the voltage will change to the new value immediately; otherwise, if the + * regulator is disabled, then the regulator will output at the new voltage when + * enabled. + * + * The min_uV to max_uV voltage range requested must intersect with the + * voltage constraint range configured for the regulator. + * + * Returns 0 on success or errno on failure. + * + * The final voltage value that is sent to the RPM is aggregated based upon the + * values requested by all consumers of the regulator. This corresponds to the + * maximum min_uV value. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV, + int max_uV) +{ + int rc = rpm_regulator_check_input(regulator); + int uV = min_uV; + + if (rc) + return rc; + + if (regulator->rpm_vreg->regulator_type == RPM_REGULATOR_SMD_TYPE_VS) { + vreg_err(regulator, "unsupported regulator type: %d\n", + regulator->rpm_vreg->regulator_type); + return -EINVAL; + } + + if (min_uV > max_uV) { + vreg_err(regulator, "min_uV=%d must be less than max_uV=%d\n", + min_uV, max_uV); + return -EINVAL; + } + + if (uV < regulator->min_uV && max_uV >= regulator->min_uV) + uV = regulator->min_uV; + + if (uV < regulator->min_uV || uV > regulator->max_uV) { + vreg_err(regulator, "request v=[%d, %d] is outside allowed " + "v=[%d, %d]\n", min_uV, max_uV, regulator->min_uV, + regulator->max_uV); + return -EINVAL; + } + + return rpm_vreg_set_voltage(regulator->rdev, uV, uV, NULL); +} +EXPORT_SYMBOL_GPL(rpm_regulator_set_voltage); + +static struct regulator_ops ldo_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .list_voltage = rpm_vreg_list_voltage, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops smps_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .list_voltage = rpm_vreg_list_voltage, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops switch_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops ncp_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .list_voltage = rpm_vreg_list_voltage, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops *vreg_ops[] = { + [RPM_REGULATOR_SMD_TYPE_LDO] = &ldo_ops, + [RPM_REGULATOR_SMD_TYPE_SMPS] = &smps_ops, + [RPM_REGULATOR_SMD_TYPE_VS] = &switch_ops, + [RPM_REGULATOR_SMD_TYPE_NCP] = &ncp_ops, +}; + +static int __devexit rpm_vreg_device_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rpm_regulator *reg; + + reg = platform_get_drvdata(pdev); + if (reg) { + rpm_vreg_lock(reg->rpm_vreg); + regulator_unregister(reg->rdev); + list_del(®->list); + kfree(reg); + rpm_vreg_unlock(reg->rpm_vreg); + } else { + dev_err(dev, "%s: drvdata missing\n", __func__); + return -EINVAL; + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int __devexit rpm_vreg_resource_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rpm_regulator *reg, *reg_temp; + struct rpm_vreg *rpm_vreg; + + rpm_vreg = platform_get_drvdata(pdev); + if (rpm_vreg) { + rpm_vreg_lock(rpm_vreg); + list_for_each_entry_safe(reg, reg_temp, &rpm_vreg->reg_list, + list) { + /* Only touch data for private consumers. */ + if (reg->rdev->desc == NULL) { + list_del(®->list); + kfree(reg->rdev); + kfree(reg); + } else { + dev_err(dev, "%s: not all child devices have " + "been removed\n", __func__); + } + } + rpm_vreg_unlock(rpm_vreg); + + msm_rpm_free_request(rpm_vreg->handle_active); + msm_rpm_free_request(rpm_vreg->handle_sleep); + + kfree(rpm_vreg); + } else { + dev_err(dev, "%s: drvdata missing\n", __func__); + return -EINVAL; + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/* + * This probe is called for child rpm-regulator devices which have + * properties which are required to configure individual regulator + * framework regulators for a given RPM regulator resource. + */ +static int __devinit rpm_vreg_device_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct regulator_init_data *init_data; + struct rpm_vreg *rpm_vreg; + struct rpm_regulator *reg; + int rc = 0; + int i, regulator_type; + u32 val; + + if (!dev->of_node) { + dev_err(dev, "%s: device tree information missing\n", __func__); + return -ENODEV; + } + + if (pdev->dev.parent == NULL) { + dev_err(dev, "%s: parent device missing\n", __func__); + return -ENODEV; + } + + rpm_vreg = dev_get_drvdata(pdev->dev.parent); + if (rpm_vreg == NULL) { + dev_err(dev, "%s: rpm_vreg not found in parent device\n", + __func__); + return -ENODEV; + } + + reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL); + if (reg == NULL) { + dev_err(dev, "%s: could not allocate memory for reg\n", + __func__); + return -ENOMEM; + } + + regulator_type = rpm_vreg->regulator_type; + reg->rpm_vreg = rpm_vreg; + reg->rdesc.ops = vreg_ops[regulator_type]; + reg->rdesc.owner = THIS_MODULE; + reg->rdesc.type = REGULATOR_VOLTAGE; + + if (regulator_type == RPM_REGULATOR_SMD_TYPE_VS) + reg->rdesc.n_voltages = 0; + else + reg->rdesc.n_voltages = 2; + + rc = of_property_read_u32(node, "qcom,set", &val); + if (rc) { + dev_err(dev, "%s: sleep set and/or active set must be " + "configured via qcom,set property, rc=%d\n", __func__, + rc); + goto fail_free_reg; + } else if (!(val & RPM_SET_CONFIG_BOTH)) { + dev_err(dev, "%s: qcom,set=%u property is invalid\n", __func__, + val); + rc = -EINVAL; + goto fail_free_reg; + } + + reg->set_active = !!(val & RPM_SET_CONFIG_ACTIVE); + reg->set_sleep = !!(val & RPM_SET_CONFIG_SLEEP); + + init_data = of_get_regulator_init_data(dev); + if (init_data == NULL) { + dev_err(dev, "%s: unable to allocate memory\n", __func__); + rc = -ENOMEM; + goto fail_free_reg; + } + if (init_data->constraints.name == NULL) { + dev_err(dev, "%s: regulator name not specified\n", __func__); + rc = -EINVAL; + goto fail_free_reg; + } + + init_data->constraints.input_uV = init_data->constraints.max_uV; + + if (of_get_property(node, "parent-supply", NULL)) + init_data->supply_regulator = "parent"; + + /* + * Fill in ops and mode masks based on callbacks specified for + * this type of regulator. + */ + if (reg->rdesc.ops->enable) + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_STATUS; + if (reg->rdesc.ops->get_voltage) + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_VOLTAGE; + if (reg->rdesc.ops->get_mode) { + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS; + init_data->constraints.valid_modes_mask + |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + } + + reg->rdesc.name = init_data->constraints.name; + reg->min_uV = init_data->constraints.min_uV; + reg->max_uV = init_data->constraints.max_uV; + + /* Initialize the param array based on optional properties. */ + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + rc = of_property_read_u32(node, params[i].property_name, &val); + if (rc == 0) { + if (params[i].supported_regulator_types + & BIT(regulator_type)) { + if (val < params[i].min + || val > params[i].max) { + pr_warn("%s: device tree property: " + "%s=%u is outsided allowed " + "range [%u, %u]\n", + reg->rdesc.name, + params[i].property_name, val, + params[i].min, params[i].max); + continue; + } + reg->req.param[i] = val; + reg->req.modified |= BIT(i); + } else { + pr_warn("%s: regulator type=%d does not support" + " device tree property: %s\n", + reg->rdesc.name, regulator_type, + params[i].property_name); + } + } + } + + of_property_read_u32(node, "qcom,system_load", ®->system_load); + + rpm_vreg_lock(rpm_vreg); + list_add(®->list, &rpm_vreg->reg_list); + rpm_vreg_unlock(rpm_vreg); + + reg->rdev = regulator_register(®->rdesc, dev, init_data, reg, node); + if (IS_ERR(reg->rdev)) { + rc = PTR_ERR(reg->rdev); + reg->rdev = NULL; + pr_err("regulator_register failed: %s, rc=%d\n", + reg->rdesc.name, rc); + goto fail_remove_from_list; + } + + platform_set_drvdata(pdev, reg); + + pr_debug("successfully probed: %s\n", reg->rdesc.name); + + return 0; + +fail_remove_from_list: + rpm_vreg_lock(rpm_vreg); + list_del(®->list); + rpm_vreg_unlock(rpm_vreg); + +fail_free_reg: + kfree(reg); + return rc; +} + +/* + * This probe is called for parent rpm-regulator devices which have + * properties which are required to identify a given RPM resource. + */ +static int __devinit rpm_vreg_resource_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct rpm_vreg *rpm_vreg; + int val = 0; + u32 resource_type; + int rc; + + if (!dev->of_node) { + dev_err(dev, "%s: device tree information missing\n", __func__); + return -ENODEV; + } + + /* Create new rpm_vreg entry. */ + rpm_vreg = kzalloc(sizeof(struct rpm_vreg), GFP_KERNEL); + if (rpm_vreg == NULL) { + dev_err(dev, "%s: could not allocate memory for vreg\n", + __func__); + return -ENOMEM; + } + + /* Required device tree properties: */ + rc = of_property_read_string(node, "qcom,resource-name", + &rpm_vreg->resource_name); + if (rc) { + dev_err(dev, "%s: qcom,resource-name missing in DT node\n", + __func__); + goto fail_free_vreg; + } + resource_type = rpm_vreg_string_to_int(rpm_vreg->resource_name); + + rc = of_property_read_u32(node, "qcom,resource-id", + &rpm_vreg->resource_id); + if (rc) { + dev_err(dev, "%s: qcom,resource-id missing in DT node\n", + __func__); + goto fail_free_vreg; + } + + rc = of_property_read_u32(node, "qcom,regulator-type", + &rpm_vreg->regulator_type); + if (rc) { + dev_err(dev, "%s: qcom,regulator-type missing in DT node\n", + __func__); + goto fail_free_vreg; + } + + if ((rpm_vreg->regulator_type < 0) + || (rpm_vreg->regulator_type >= RPM_REGULATOR_SMD_TYPE_MAX)) { + dev_err(dev, "%s: invalid regulator type: %d\n", __func__, + rpm_vreg->regulator_type); + rc = -EINVAL; + goto fail_free_vreg; + } + + /* Optional device tree properties: */ + of_property_read_u32(node, "qcom,allow-atomic", &val); + rpm_vreg->allow_atomic = !!val; + of_property_read_u32(node, "qcom,enable-time", &rpm_vreg->enable_time); + of_property_read_u32(node, "qcom,hpm-min-load", + &rpm_vreg->hpm_min_load); + + rpm_vreg->handle_active = msm_rpm_create_request(RPM_SET_ACTIVE, + resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX); + if (rpm_vreg->handle_active == NULL + || IS_ERR(rpm_vreg->handle_active)) { + rc = PTR_ERR(rpm_vreg->handle_active); + dev_err(dev, "%s: failed to create active RPM handle, rc=%d\n", + __func__, rc); + goto fail_free_vreg; + } + + rpm_vreg->handle_sleep = msm_rpm_create_request(RPM_SET_SLEEP, + resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX); + if (rpm_vreg->handle_sleep == NULL || IS_ERR(rpm_vreg->handle_sleep)) { + rc = PTR_ERR(rpm_vreg->handle_sleep); + dev_err(dev, "%s: failed to create sleep RPM handle, rc=%d\n", + __func__, rc); + goto fail_free_handle_active; + } + + INIT_LIST_HEAD(&rpm_vreg->reg_list); + + if (rpm_vreg->allow_atomic) + spin_lock_init(&rpm_vreg->slock); + else + mutex_init(&rpm_vreg->mlock); + + platform_set_drvdata(pdev, rpm_vreg); + + rc = of_platform_populate(node, NULL, NULL, dev); + if (rc) { + dev_err(dev, "%s: failed to add child nodes, rc=%d\n", __func__, + rc); + goto fail_unset_drvdata; + } + + pr_debug("successfully probed: %s (%08X) %u\n", rpm_vreg->resource_name, + resource_type, rpm_vreg->resource_id); + + return rc; + +fail_unset_drvdata: + platform_set_drvdata(pdev, NULL); + msm_rpm_free_request(rpm_vreg->handle_sleep); + +fail_free_handle_active: + msm_rpm_free_request(rpm_vreg->handle_active); + +fail_free_vreg: + kfree(rpm_vreg); + + return rc; +} + +static struct of_device_id rpm_vreg_match_table_device[] = { + { .compatible = "qcom,rpm-regulator-smd", }, + {} +}; + +static struct of_device_id rpm_vreg_match_table_resource[] = { + { .compatible = "qcom,rpm-regulator-smd-resource", }, + {} +}; + +static struct platform_driver rpm_vreg_device_driver = { + .probe = rpm_vreg_device_probe, + .remove = __devexit_p(rpm_vreg_device_remove), + .driver = { + .name = "qcom,rpm-regulator-smd", + .owner = THIS_MODULE, + .of_match_table = rpm_vreg_match_table_device, + }, +}; + +static struct platform_driver rpm_vreg_resource_driver = { + .probe = rpm_vreg_resource_probe, + .remove = __devexit_p(rpm_vreg_resource_remove), + .driver = { + .name = "qcom,rpm-regulator-smd-resource", + .owner = THIS_MODULE, + .of_match_table = rpm_vreg_match_table_resource, + }, +}; + +/** + * rpm_regulator_smd_driver_init() - initialized SMD RPM regulator driver + * + * This function registers the SMD RPM regulator platform drivers. + * + * Returns 0 on success or errno on failure. + */ +int __init rpm_regulator_smd_driver_init(void) +{ + static bool initialized; + int i, rc; + + if (initialized) + return 0; + else + initialized = true; + + /* Store parameter string names as integers */ + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) + params[i].key = rpm_vreg_string_to_int(params[i].name); + + rc = platform_driver_register(&rpm_vreg_device_driver); + if (rc) + return rc; + + return platform_driver_register(&rpm_vreg_resource_driver); +} +EXPORT_SYMBOL_GPL(rpm_regulator_smd_driver_init); + +static void __exit rpm_vreg_exit(void) +{ + platform_driver_unregister(&rpm_vreg_device_driver); + platform_driver_unregister(&rpm_vreg_resource_driver); +} + +module_init(rpm_regulator_smd_driver_init); +module_exit(rpm_vreg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM SMD RPM regulator driver"); diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c new file mode 100644 index 00000000000..f663695b7c6 --- /dev/null +++ b/arch/arm/mach-msm/rpm-regulator.c @@ -0,0 +1,1796 @@ +/* + * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpm_resources.h" +#include "rpm-regulator-private.h" + +/* Debug Definitions */ + +enum { + MSM_RPM_VREG_DEBUG_REQUEST = BIT(0), + MSM_RPM_VREG_DEBUG_VOTE = BIT(1), + MSM_RPM_VREG_DEBUG_DUPLICATE = BIT(2), + MSM_RPM_VREG_DEBUG_IGNORE_VDD_MEM_DIG = BIT(3), +}; + +static int msm_rpm_vreg_debug_mask; +module_param_named( + debug_mask, msm_rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR +); + +/* Used for access via the rpm_regulator_* API. */ +struct rpm_regulator { + int vreg_id; + enum rpm_vreg_voter voter; + int sleep_also; + int min_uV; + int max_uV; +}; + +struct vreg_config *(*get_config[])(void) = { + [RPM_VREG_VERSION_8660] = get_config_8660, + [RPM_VREG_VERSION_8960] = get_config_8960, + [RPM_VREG_VERSION_9615] = get_config_9615, + [RPM_VREG_VERSION_8930] = get_config_8930, +}; + +static struct rpm_regulator_consumer_mapping *consumer_map; +static int consumer_map_len; + +#define SET_PART(_vreg, _part, _val) \ + _vreg->req[_vreg->part->_part.word].value \ + = (_vreg->req[_vreg->part->_part.word].value \ + & ~_vreg->part->_part.mask) \ + | (((_val) << _vreg->part->_part.shift) \ + & _vreg->part->_part.mask) + +#define GET_PART(_vreg, _part) \ + ((_vreg->req[_vreg->part->_part.word].value & _vreg->part->_part.mask) \ + >> _vreg->part->_part.shift) + +#define USES_PART(_vreg, _part) (_vreg->part->_part.mask) + +#define vreg_err(vreg, fmt, ...) \ + pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__) + +#define RPM_VREG_PIN_CTRL_EN0 0x01 +#define RPM_VREG_PIN_CTRL_EN1 0x02 +#define RPM_VREG_PIN_CTRL_EN2 0x04 +#define RPM_VREG_PIN_CTRL_EN3 0x08 +#define RPM_VREG_PIN_CTRL_ALL 0x0F + +static const char *label_freq[] = { + [RPM_VREG_FREQ_NONE] = " N/A", + [RPM_VREG_FREQ_19p20] = "19.2", + [RPM_VREG_FREQ_9p60] = "9.60", + [RPM_VREG_FREQ_6p40] = "6.40", + [RPM_VREG_FREQ_4p80] = "4.80", + [RPM_VREG_FREQ_3p84] = "3.84", + [RPM_VREG_FREQ_3p20] = "3.20", + [RPM_VREG_FREQ_2p74] = "2.74", + [RPM_VREG_FREQ_2p40] = "2.40", + [RPM_VREG_FREQ_2p13] = "2.13", + [RPM_VREG_FREQ_1p92] = "1.92", + [RPM_VREG_FREQ_1p75] = "1.75", + [RPM_VREG_FREQ_1p60] = "1.60", + [RPM_VREG_FREQ_1p48] = "1.48", + [RPM_VREG_FREQ_1p37] = "1.37", + [RPM_VREG_FREQ_1p28] = "1.28", + [RPM_VREG_FREQ_1p20] = "1.20", +}; + +static const char *label_corner[] = { + [RPM_VREG_CORNER_NONE] = "NONE", + [RPM_VREG_CORNER_LOW] = "LOW", + [RPM_VREG_CORNER_NOMINAL] = "NOM", + [RPM_VREG_CORNER_HIGH] = "HIGH", +}; + +/* + * This is used when voting for LPM or HPM by subtracting or adding to the + * hpm_min_load of a regulator. It has units of uA. + */ +#define LOAD_THRESHOLD_STEP 1000 + +/* rpm_version keeps track of the version for the currently running driver. */ +enum rpm_vreg_version rpm_version = -1; + +/* config holds all configuration data of the currently running driver. */ +static struct vreg_config *config; + +/* These regulator ID values are specified in the board file. */ +static int vreg_id_vdd_mem, vreg_id_vdd_dig; + +static inline int vreg_id_is_vdd_mem_or_dig(int id) +{ + return id == vreg_id_vdd_mem || id == vreg_id_vdd_dig; +} + +#define DEBUG_PRINT_BUFFER_SIZE 512 + +static void rpm_regulator_req(struct vreg *vreg, int set) +{ + int uV, mV, fm, pm, pc, pf, pd, freq, state, i; + const char *pf_label = "", *fm_label = "", *pc_total = ""; + const char *pc_en[4] = {"", "", "", ""}; + const char *pm_label = "", *freq_label = "", *corner_label = ""; + char buf[DEBUG_PRINT_BUFFER_SIZE]; + size_t buflen = DEBUG_PRINT_BUFFER_SIZE; + int pos = 0; + + /* Suppress VDD_MEM and VDD_DIG printing. */ + if ((msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_IGNORE_VDD_MEM_DIG) + && vreg_id_is_vdd_mem_or_dig(vreg->id)) + return; + + uV = GET_PART(vreg, uV); + mV = GET_PART(vreg, mV); + if (vreg->type == RPM_REGULATOR_TYPE_NCP) { + uV = -uV; + mV = -mV; + } + + fm = GET_PART(vreg, fm); + pm = GET_PART(vreg, pm); + pc = GET_PART(vreg, pc); + pf = GET_PART(vreg, pf); + pd = GET_PART(vreg, pd); + freq = GET_PART(vreg, freq); + state = GET_PART(vreg, enable_state); + + if (pf >= 0 && pf < config->label_pin_func_len) + pf_label = config->label_pin_func[pf]; + + if (fm >= 0 && fm < config->label_force_mode_len) + fm_label = config->label_force_mode[fm]; + + if (pm >= 0 && pm < config->label_power_mode_len) + pm_label = config->label_power_mode[pm]; + + if (freq >= 0 && freq < ARRAY_SIZE(label_freq)) + freq_label = label_freq[freq]; + + for (i = 0; i < config->label_pin_ctrl_len; i++) + if (pc & (1 << i)) + pc_en[i] = config->label_pin_ctrl[i]; + + if (pc == RPM_VREG_PIN_CTRL_NONE) + pc_total = " none"; + + pos += scnprintf(buf + pos, buflen - pos, "%s%s: ", + KERN_INFO, __func__); + + pos += scnprintf(buf + pos, buflen - pos, "%s %-9s: s=%c", + (set == MSM_RPM_CTX_SET_0 ? "sending " : "buffered"), + vreg->rdesc.name, + (set == MSM_RPM_CTX_SET_0 ? 'A' : 'S')); + + if (USES_PART(vreg, uV) && vreg->type != RPM_REGULATOR_TYPE_CORNER) + pos += scnprintf(buf + pos, buflen - pos, ", v=%7d uV", uV); + if (USES_PART(vreg, mV)) + pos += scnprintf(buf + pos, buflen - pos, ", v=%4d mV", mV); + if (USES_PART(vreg, enable_state)) + pos += scnprintf(buf + pos, buflen - pos, ", state=%s (%d)", + (state == 1 ? "on" : "off"), state); + if (USES_PART(vreg, ip)) + pos += scnprintf(buf + pos, buflen - pos, + ", ip=%4d mA", GET_PART(vreg, ip)); + if (USES_PART(vreg, fm)) + pos += scnprintf(buf + pos, buflen - pos, + ", fm=%s (%d)", fm_label, fm); + if (USES_PART(vreg, pc)) + pos += scnprintf(buf + pos, buflen - pos, + ", pc=%s%s%s%s%s (%X)", pc_en[0], pc_en[1], + pc_en[2], pc_en[3], pc_total, pc); + if (USES_PART(vreg, pf)) + pos += scnprintf(buf + pos, buflen - pos, + ", pf=%s (%d)", pf_label, pf); + if (USES_PART(vreg, pd)) + pos += scnprintf(buf + pos, buflen - pos, + ", pd=%s (%d)", (pd == 1 ? "Y" : "N"), pd); + if (USES_PART(vreg, ia)) + pos += scnprintf(buf + pos, buflen - pos, + ", ia=%4d mA", GET_PART(vreg, ia)); + if (USES_PART(vreg, freq)) { + if (vreg->type == RPM_REGULATOR_TYPE_NCP) + pos += scnprintf(buf + pos, buflen - pos, + ", freq=%2d", freq); + else + pos += scnprintf(buf + pos, buflen - pos, + ", freq=%s MHz (%2d)", freq_label, freq); + } + if (USES_PART(vreg, pm)) + pos += scnprintf(buf + pos, buflen - pos, + ", pm=%s (%d)", pm_label, pm); + if (USES_PART(vreg, freq_clk_src)) + pos += scnprintf(buf + pos, buflen - pos, + ", clk_src=%d", GET_PART(vreg, freq_clk_src)); + if (USES_PART(vreg, comp_mode)) + pos += scnprintf(buf + pos, buflen - pos, + ", comp=%d", GET_PART(vreg, comp_mode)); + if (USES_PART(vreg, hpm)) + pos += scnprintf(buf + pos, buflen - pos, + ", hpm=%d", GET_PART(vreg, hpm)); + if (USES_PART(vreg, uV) && vreg->type == RPM_REGULATOR_TYPE_CORNER) { + if (uV >= 0 && uV < (ARRAY_SIZE(label_corner) - 1)) + corner_label = label_corner[uV+1]; + pos += scnprintf(buf + pos, buflen - pos, ", corner=%s (%d)", + corner_label, uV); + } + + pos += scnprintf(buf + pos, buflen - pos, "; req[0]={%d, 0x%08X}", + vreg->req[0].id, vreg->req[0].value); + if (vreg->part->request_len > 1) + pos += scnprintf(buf + pos, buflen - pos, + ", req[1]={%d, 0x%08X}", vreg->req[1].id, + vreg->req[1].value); + + pos += scnprintf(buf + pos, buflen - pos, "\n"); + printk(buf); +} + +static void rpm_regulator_vote(struct vreg *vreg, enum rpm_vreg_voter voter, + int set, int voter_uV, int aggregate_uV) +{ + /* Suppress VDD_MEM and VDD_DIG printing. */ + if ((msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_IGNORE_VDD_MEM_DIG) + && vreg_id_is_vdd_mem_or_dig(vreg->id)) + return; + + pr_info("vote received %-9s: voter=%d, set=%c, v_voter=%7d uV, " + "v_aggregate=%7d uV\n", vreg->rdesc.name, voter, + (set == 0 ? 'A' : 'S'), voter_uV, aggregate_uV); +} + +static void rpm_regulator_duplicate(struct vreg *vreg, int set, int cnt) +{ + /* Suppress VDD_MEM and VDD_DIG printing. */ + if ((msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_IGNORE_VDD_MEM_DIG) + && vreg_id_is_vdd_mem_or_dig(vreg->id)) + return; + + if (cnt == 2) + pr_info("ignored request %-9s: set=%c; req[0]={%d, 0x%08X}, " + "req[1]={%d, 0x%08X}\n", vreg->rdesc.name, + (set == 0 ? 'A' : 'S'), + vreg->req[0].id, vreg->req[0].value, + vreg->req[1].id, vreg->req[1].value); + else if (cnt == 1) + pr_info("ignored request %-9s: set=%c; req[0]={%d, 0x%08X}\n", + vreg->rdesc.name, (set == 0 ? 'A' : 'S'), + vreg->req[0].id, vreg->req[0].value); +} + +/* Spin lock needed for sleep-selectable regulators. */ +static DEFINE_SPINLOCK(rpm_noirq_lock); + +static int voltage_from_req(struct vreg *vreg) +{ + int uV = 0; + + if (vreg->part->uV.mask) + uV = GET_PART(vreg, uV); + else if (vreg->part->mV.mask) + uV = MILLI_TO_MICRO(GET_PART(vreg, mV)); + else if (vreg->part->enable_state.mask) + uV = GET_PART(vreg, enable_state); + + return uV; +} + +static void voltage_to_req(int uV, struct vreg *vreg) +{ + if (vreg->part->uV.mask) + SET_PART(vreg, uV, uV); + else if (vreg->part->mV.mask) + SET_PART(vreg, mV, MICRO_TO_MILLI(uV)); + else if (vreg->part->enable_state.mask) + SET_PART(vreg, enable_state, uV); +} + +static int vreg_send_request(struct vreg *vreg, enum rpm_vreg_voter voter, + int set, unsigned mask0, unsigned val0, + unsigned mask1, unsigned val1, unsigned cnt, + int update_voltage) +{ + struct msm_rpm_iv_pair *prev_req; + int rc = 0, max_uV_vote = 0; + unsigned prev0, prev1; + int *min_uV_vote; + int i; + + if (set == MSM_RPM_CTX_SET_0) { + min_uV_vote = vreg->active_min_uV_vote; + prev_req = vreg->prev_active_req; + } else { + min_uV_vote = vreg->sleep_min_uV_vote; + prev_req = vreg->prev_sleep_req; + } + + prev0 = vreg->req[0].value; + vreg->req[0].value &= ~mask0; + vreg->req[0].value |= val0 & mask0; + + prev1 = vreg->req[1].value; + vreg->req[1].value &= ~mask1; + vreg->req[1].value |= val1 & mask1; + + /* Set the force mode field based on which set is being requested. */ + if (set == MSM_RPM_CTX_SET_0) + SET_PART(vreg, fm, vreg->pdata.force_mode); + else + SET_PART(vreg, fm, vreg->pdata.sleep_set_force_mode); + + if (update_voltage) + min_uV_vote[voter] = voltage_from_req(vreg); + + /* Find the highest voltage voted for and use it. */ + for (i = 0; i < RPM_VREG_VOTER_COUNT; i++) + max_uV_vote = max(max_uV_vote, min_uV_vote[i]); + voltage_to_req(max_uV_vote, vreg); + + if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_VOTE) + rpm_regulator_vote(vreg, voter, set, min_uV_vote[voter], + max_uV_vote); + + /* Ignore duplicate requests */ + if (vreg->req[0].value != prev_req[0].value || + vreg->req[1].value != prev_req[1].value) { + rc = msm_rpmrs_set_noirq(set, vreg->req, cnt); + if (rc) { + vreg->req[0].value = prev0; + vreg->req[1].value = prev1; + + vreg_err(vreg, "msm_rpmrs_set_noirq failed - " + "set=%s, id=%d, rc=%d\n", + (set == MSM_RPM_CTX_SET_0 ? "active" : "sleep"), + vreg->req[0].id, rc); + } else { + /* Only save if nonzero and active set. */ + if (max_uV_vote && (set == MSM_RPM_CTX_SET_0)) + vreg->save_uV = max_uV_vote; + if (msm_rpm_vreg_debug_mask + & MSM_RPM_VREG_DEBUG_REQUEST) + rpm_regulator_req(vreg, set); + prev_req[0].value = vreg->req[0].value; + prev_req[1].value = vreg->req[1].value; + } + } else if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_DUPLICATE) { + rpm_regulator_duplicate(vreg, set, cnt); + } + + return rc; +} + +static int vreg_set_noirq(struct vreg *vreg, enum rpm_vreg_voter voter, + int sleep, unsigned mask0, unsigned val0, + unsigned mask1, unsigned val1, unsigned cnt, + int update_voltage) +{ + unsigned int s_mask[2] = {mask0, mask1}, s_val[2] = {val0, val1}; + unsigned long flags; + int rc; + + if (voter < 0 || voter >= RPM_VREG_VOTER_COUNT) + return -EINVAL; + + spin_lock_irqsave(&rpm_noirq_lock, flags); + + /* + * Send sleep set request first so that subsequent set_mode, etc calls + * use the voltage from the active set. + */ + if (sleep) + rc = vreg_send_request(vreg, voter, MSM_RPM_CTX_SET_SLEEP, + mask0, val0, mask1, val1, cnt, update_voltage); + else { + /* + * Vote for 0 V in the sleep set when active set-only is + * specified. This ensures that a disable vote will be issued + * at some point for the sleep set of the regulator. + */ + if (vreg->part->uV.mask) { + s_val[vreg->part->uV.word] = 0 << vreg->part->uV.shift; + s_mask[vreg->part->uV.word] = vreg->part->uV.mask; + } else if (vreg->part->mV.mask) { + s_val[vreg->part->mV.word] = 0 << vreg->part->mV.shift; + s_mask[vreg->part->mV.word] = vreg->part->mV.mask; + } else if (vreg->part->enable_state.mask) { + s_val[vreg->part->enable_state.word] + = 0 << vreg->part->enable_state.shift; + s_mask[vreg->part->enable_state.word] + = vreg->part->enable_state.mask; + } + + rc = vreg_send_request(vreg, voter, MSM_RPM_CTX_SET_SLEEP, + s_mask[0], s_val[0], s_mask[1], s_val[1], + cnt, update_voltage); + } + + rc = vreg_send_request(vreg, voter, MSM_RPM_CTX_SET_0, mask0, val0, + mask1, val1, cnt, update_voltage); + + spin_unlock_irqrestore(&rpm_noirq_lock, flags); + + return rc; +} + +/** + * rpm_vreg_set_voltage - vote for a min_uV value of specified regualtor + * @vreg: ID for regulator + * @voter: ID for the voter + * @min_uV: minimum acceptable voltage (in uV) that is voted for + * @max_uV: maximum acceptable voltage (in uV) that is voted for + * @sleep_also: 0 for active set only, non-0 for active set and sleep set + * + * Returns 0 on success or errno. + * + * This function is used to vote for the voltage of a regulator without + * using the regulator framework. It is needed by consumers which hold spin + * locks or have interrupts disabled because the regulator framework can sleep. + * It is also needed by consumers which wish to only vote for active set + * regulator voltage. + * + * If sleep_also == 0, then a sleep-set value of 0V will be voted for. + * + * This function may only be called for regulators which have the sleep flag + * specified in their private data. + * + * Consumers can vote to disable a regulator with this function by passing + * min_uV = 0 and max_uV = 0. + * + * Voltage switch type regulators may be controlled via rpm_vreg_set_voltage + * as well. For this type of regulator, max_uV > 0 is treated as an enable + * request and max_uV == 0 is treated as a disable request. + */ +int rpm_vreg_set_voltage(int vreg_id, enum rpm_vreg_voter voter, int min_uV, + int max_uV, int sleep_also) +{ + unsigned int mask[2] = {0}, val[2] = {0}; + struct vreg_range *range; + struct vreg *vreg; + int uV = min_uV; + int lim_min_uV, lim_max_uV, i, rc; + + if (!config) { + pr_err("rpm-regulator driver has not probed yet.\n"); + return -ENODEV; + } + + if (vreg_id < config->vreg_id_min || vreg_id > config->vreg_id_max) { + pr_err("invalid regulator id=%d\n", vreg_id); + return -EINVAL; + } + + vreg = &config->vregs[vreg_id]; + + if (!vreg->pdata.sleep_selectable) { + vreg_err(vreg, "regulator is not marked sleep selectable\n"); + return -EINVAL; + } + + /* Allow min_uV == max_uV == 0 to represent a disable request. */ + if ((min_uV != 0 || max_uV != 0) + && (vreg->part->uV.mask || vreg->part->mV.mask)) { + /* + * Check if request voltage is outside of allowed range. The + * regulator core has already checked that constraint range + * is inside of the physically allowed range. + */ + lim_min_uV = vreg->pdata.init_data.constraints.min_uV; + lim_max_uV = vreg->pdata.init_data.constraints.max_uV; + + if (uV < lim_min_uV && max_uV >= lim_min_uV) + uV = lim_min_uV; + + if (uV < lim_min_uV || uV > lim_max_uV) { + vreg_err(vreg, "request v=[%d, %d] is outside allowed " + "v=[%d, %d]\n", min_uV, max_uV, lim_min_uV, + lim_max_uV); + return -EINVAL; + } + + range = &vreg->set_points->range[0]; + /* Find the range which uV is inside of. */ + for (i = vreg->set_points->count - 1; i > 0; i--) { + if (uV > vreg->set_points->range[i - 1].max_uV) { + range = &vreg->set_points->range[i]; + break; + } + } + + /* + * Force uV to be an allowed set point and apply a ceiling + * function to non-set point values. + */ + uV = (uV - range->min_uV + range->step_uV - 1) / range->step_uV; + uV = uV * range->step_uV + range->min_uV; + + if (uV > max_uV) { + vreg_err(vreg, + "request v=[%d, %d] cannot be met by any set point; " + "next set point: %d\n", + min_uV, max_uV, uV); + return -EINVAL; + } + } + + if (vreg->type == RPM_REGULATOR_TYPE_CORNER) { + /* + * Translate from enum values which work as inputs in the + * rpm_vreg_set_voltage function to the actual corner values + * sent to the RPM. + */ + if (uV > 0) + uV -= RPM_VREG_CORNER_NONE; + } + + if (vreg->part->uV.mask) { + val[vreg->part->uV.word] = uV << vreg->part->uV.shift; + mask[vreg->part->uV.word] = vreg->part->uV.mask; + } else if (vreg->part->mV.mask) { + val[vreg->part->mV.word] + = MICRO_TO_MILLI(uV) << vreg->part->mV.shift; + mask[vreg->part->mV.word] = vreg->part->mV.mask; + } else if (vreg->part->enable_state.mask) { + /* + * Translate max_uV > 0 into an enable request for regulator + * types which to not support voltage setting, e.g. voltage + * switches. + */ + val[vreg->part->enable_state.word] + = (max_uV > 0 ? 1 : 0) << vreg->part->enable_state.shift; + mask[vreg->part->enable_state.word] + = vreg->part->enable_state.mask; + } + + rc = vreg_set_noirq(vreg, voter, sleep_also, mask[0], val[0], mask[1], + val[1], vreg->part->request_len, 1); + if (rc) + vreg_err(vreg, "vreg_set_noirq failed, rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL_GPL(rpm_vreg_set_voltage); + +/** + * rpm_vreg_set_frequency - sets the frequency of a switching regulator + * @vreg: ID for regulator + * @freq: enum corresponding to desired frequency + * + * Returns 0 on success or errno. + */ +int rpm_vreg_set_frequency(int vreg_id, enum rpm_vreg_freq freq) +{ + unsigned int mask[2] = {0}, val[2] = {0}; + struct vreg *vreg; + int rc; + + if (!config) { + pr_err("rpm-regulator driver has not probed yet.\n"); + return -ENODEV; + } + + if (vreg_id < config->vreg_id_min || vreg_id > config->vreg_id_max) { + pr_err("invalid regulator id=%d\n", vreg_id); + return -EINVAL; + } + + vreg = &config->vregs[vreg_id]; + + if (freq < 0 || freq > RPM_VREG_FREQ_1p20) { + vreg_err(vreg, "invalid frequency=%d\n", freq); + return -EINVAL; + } + if (!vreg->pdata.sleep_selectable) { + vreg_err(vreg, "regulator is not marked sleep selectable\n"); + return -EINVAL; + } + if (!vreg->part->freq.mask) { + vreg_err(vreg, "frequency not supported\n"); + return -EINVAL; + } + + val[vreg->part->freq.word] = freq << vreg->part->freq.shift; + mask[vreg->part->freq.word] = vreg->part->freq.mask; + + rc = vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, mask[0], + val[0], mask[1], val[1], vreg->part->request_len, 0); + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL_GPL(rpm_vreg_set_frequency); + +#define MAX_NAME_LEN 64 +/** + * rpm_regulator_get() - lookup and obtain a handle to an RPM regulator + * @dev: device for regulator consumer + * @supply: supply name + * + * Returns a struct rpm_regulator corresponding to the regulator producer, + * or ERR_PTR() containing errno. + * + * This function may only be called from nonatomic context. The mapping between + * tuples and rpm_regulators struct pointers is specified via + * rpm-regulator platform data. + */ +struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply) +{ + struct rpm_regulator_consumer_mapping *mapping = NULL; + const char *devname = NULL; + struct rpm_regulator *regulator; + int i; + + if (!config) { + pr_err("rpm-regulator driver has not probed yet.\n"); + return ERR_PTR(-ENODEV); + } + + if (consumer_map == NULL || consumer_map_len == 0) { + pr_err("No private consumer mapping has been specified.\n"); + return ERR_PTR(-ENODEV); + } + + if (supply == NULL) { + pr_err("supply name must be specified\n"); + return ERR_PTR(-EINVAL); + } + + if (dev) + devname = dev_name(dev); + + for (i = 0; i < consumer_map_len; i++) { + /* If the mapping has a device set up it must match */ + if (consumer_map[i].dev_name && + (!devname || strncmp(consumer_map[i].dev_name, devname, + MAX_NAME_LEN))) + continue; + + if (strncmp(consumer_map[i].supply, supply, MAX_NAME_LEN) + == 0) { + mapping = &consumer_map[i]; + break; + } + } + + if (mapping == NULL) { + pr_err("could not find mapping for dev=%s, supply=%s\n", + (devname ? devname : "(null)"), supply); + return ERR_PTR(-ENODEV); + } + + regulator = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL); + if (regulator == NULL) { + pr_err("could not allocate memory for regulator\n"); + return ERR_PTR(-ENOMEM); + } + + regulator->vreg_id = mapping->vreg_id; + regulator->voter = mapping->voter; + regulator->sleep_also = mapping->sleep_also; + + return regulator; +} +EXPORT_SYMBOL_GPL(rpm_regulator_get); + +static int rpm_regulator_check_input(struct rpm_regulator *regulator) +{ + int rc = 0; + + if (regulator == NULL) { + rc = -EINVAL; + pr_err("invalid (null) rpm_regulator pointer\n"); + } else if (IS_ERR(regulator)) { + rc = PTR_ERR(regulator); + pr_err("invalid rpm_regulator pointer, rc=%d\n", rc); + } + + return rc; +} + +/** + * rpm_regulator_put() - free the RPM regulator handle + * @regulator: RPM regulator handle + * + * Parameter reaggregation does not take place when rpm_regulator_put is called. + * Therefore, regulator enable state and voltage must be configured + * appropriately before calling rpm_regulator_put. + * + * This function may be called from either atomic or nonatomic context. + */ +void rpm_regulator_put(struct rpm_regulator *regulator) +{ + kfree(regulator); +} +EXPORT_SYMBOL_GPL(rpm_regulator_put); + +/** + * rpm_regulator_enable() - enable regulator output + * @regulator: RPM regulator handle + * + * Returns 0 on success or errno on failure. + * + * This function may be called from either atomic or nonatomic context. This + * function may only be called for regulators which have the sleep_selectable + * flag set in their configuration data. + * + * rpm_regulator_set_voltage must be called before rpm_regulator_enable because + * enabling is defined by the RPM interface to be requesting the desired + * non-zero regulator output voltage. + */ +int rpm_regulator_enable(struct rpm_regulator *regulator) +{ + int rc = rpm_regulator_check_input(regulator); + struct vreg *vreg; + + if (rc) + return rc; + + if (regulator->vreg_id < config->vreg_id_min + || regulator->vreg_id > config->vreg_id_max) { + pr_err("invalid regulator id=%d\n", regulator->vreg_id); + return -EINVAL; + } + + vreg = &config->vregs[regulator->vreg_id]; + + /* + * Handle voltage switches which can be enabled without + * rpm_regulator_set_voltage ever being called. + */ + if (regulator->min_uV == 0 && regulator->max_uV == 0 + && vreg->part->uV.mask == 0 && vreg->part->mV.mask == 0) { + regulator->min_uV = 1; + regulator->max_uV = 1; + } + + if (regulator->min_uV == 0 && regulator->max_uV == 0) { + pr_err("Voltage must be set with rpm_regulator_set_voltage " + "before calling rpm_regulator_enable; vreg_id=%d, " + "voter=%d\n", regulator->vreg_id, regulator->voter); + return -EINVAL; + } + + rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, + regulator->min_uV, regulator->max_uV, regulator->sleep_also); + + if (rc) + pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL_GPL(rpm_regulator_enable); + +/** + * rpm_regulator_disable() - disable regulator output + * @regulator: RPM regulator handle + * + * Returns 0 on success or errno on failure. + * + * The enable state of the regulator is determined by aggregating the requests + * of all consumers. Therefore, it is possible that the regulator will remain + * enabled even after rpm_regulator_disable is called. + * + * This function may be called from either atomic or nonatomic context. This + * function may only be called for regulators which have the sleep_selectable + * flag set in their configuration data. + */ +int rpm_regulator_disable(struct rpm_regulator *regulator) +{ + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return rc; + + rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, 0, 0, + regulator->sleep_also); + + if (rc) + pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL_GPL(rpm_regulator_disable); + +/** + * rpm_regulator_set_voltage() - set regulator output voltage + * @regulator: RPM regulator handle + * @min_uV: minimum required voltage in uV + * @max_uV: maximum acceptable voltage in uV + * + * Sets a voltage regulator to the desired output voltage. This can be set + * while the regulator is disabled or enabled. If the regulator is disabled, + * then rpm_regulator_set_voltage will both enable the regulator and set it to + * output at the requested voltage. + * + * The min_uV to max_uV voltage range requested must intersect with the + * voltage constraint range configured for the regulator. + * + * Returns 0 on success or errno on failure. + * + * The final voltage value that is sent to the RPM is aggregated based upon the + * values requested by all consumers of the regulator. This corresponds to the + * maximum min_uV value. + * + * This function may be called from either atomic or nonatomic context. This + * function may only be called for regulators which have the sleep_selectable + * flag set in their configuration data. + */ +int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV, + int max_uV) +{ + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return rc; + + rc = rpm_vreg_set_voltage(regulator->vreg_id, regulator->voter, min_uV, + max_uV, regulator->sleep_also); + + if (rc) { + pr_err("rpm_vreg_set_voltage failed, rc=%d\n", rc); + } else { + regulator->min_uV = min_uV; + regulator->max_uV = max_uV; + } + + return rc; +} +EXPORT_SYMBOL_GPL(rpm_regulator_set_voltage); + +static inline int vreg_hpm_min_uA(struct vreg *vreg) +{ + return vreg->hpm_min_load; +} + +static inline int vreg_lpm_max_uA(struct vreg *vreg) +{ + return vreg->hpm_min_load - LOAD_THRESHOLD_STEP; +} + +static inline unsigned saturate_peak_load(struct vreg *vreg, unsigned load_uA) +{ + unsigned load_max + = MILLI_TO_MICRO(vreg->part->ip.mask >> vreg->part->ip.shift); + + return (load_uA > load_max ? load_max : load_uA); +} + +static inline unsigned saturate_avg_load(struct vreg *vreg, unsigned load_uA) +{ + unsigned load_max + = MILLI_TO_MICRO(vreg->part->ia.mask >> vreg->part->ia.shift); + return (load_uA > load_max ? load_max : load_uA); +} + +/* Change vreg->req, but do not send it to the RPM. */ +static int vreg_store(struct vreg *vreg, unsigned mask0, unsigned val0, + unsigned mask1, unsigned val1) +{ + unsigned long flags = 0; + + if (vreg->pdata.sleep_selectable) + spin_lock_irqsave(&rpm_noirq_lock, flags); + + vreg->req[0].value &= ~mask0; + vreg->req[0].value |= val0 & mask0; + + vreg->req[1].value &= ~mask1; + vreg->req[1].value |= val1 & mask1; + + if (vreg->pdata.sleep_selectable) + spin_unlock_irqrestore(&rpm_noirq_lock, flags); + + return 0; +} + +static int vreg_set(struct vreg *vreg, unsigned mask0, unsigned val0, + unsigned mask1, unsigned val1, unsigned cnt) +{ + unsigned prev0 = 0, prev1 = 0; + int rc; + + /* + * Bypass the normal route for regulators that can be called to change + * just the active set values. + */ + if (vreg->pdata.sleep_selectable) + return vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, + mask0, val0, mask1, val1, cnt, 1); + + prev0 = vreg->req[0].value; + vreg->req[0].value &= ~mask0; + vreg->req[0].value |= val0 & mask0; + + prev1 = vreg->req[1].value; + vreg->req[1].value &= ~mask1; + vreg->req[1].value |= val1 & mask1; + + /* Ignore duplicate requests */ + if (vreg->req[0].value == vreg->prev_active_req[0].value && + vreg->req[1].value == vreg->prev_active_req[1].value) { + if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_DUPLICATE) + rpm_regulator_duplicate(vreg, MSM_RPM_CTX_SET_0, cnt); + return 0; + } + + rc = msm_rpm_set(MSM_RPM_CTX_SET_0, vreg->req, cnt); + if (rc) { + vreg->req[0].value = prev0; + vreg->req[1].value = prev1; + + vreg_err(vreg, "msm_rpm_set failed, set=active, id=%d, rc=%d\n", + vreg->req[0].id, rc); + } else { + if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_REQUEST) + rpm_regulator_req(vreg, MSM_RPM_CTX_SET_0); + vreg->prev_active_req[0].value = vreg->req[0].value; + vreg->prev_active_req[1].value = vreg->req[1].value; + } + + return rc; +} + +static int vreg_is_enabled(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + int enabled; + + mutex_lock(&vreg->pc_lock); + enabled = vreg->is_enabled; + mutex_unlock(&vreg->pc_lock); + + return enabled; +} + +static void set_enable(struct vreg *vreg, unsigned int *mask, unsigned int *val) +{ + switch (vreg->type) { + case RPM_REGULATOR_TYPE_LDO: + case RPM_REGULATOR_TYPE_SMPS: + case RPM_REGULATOR_TYPE_CORNER: + /* Enable by setting a voltage. */ + if (vreg->part->uV.mask) { + val[vreg->part->uV.word] + |= vreg->save_uV << vreg->part->uV.shift; + mask[vreg->part->uV.word] |= vreg->part->uV.mask; + } else { + val[vreg->part->mV.word] + |= MICRO_TO_MILLI(vreg->save_uV) + << vreg->part->mV.shift; + mask[vreg->part->mV.word] |= vreg->part->mV.mask; + } + break; + case RPM_REGULATOR_TYPE_VS: + case RPM_REGULATOR_TYPE_NCP: + /* Enable by setting enable_state. */ + val[vreg->part->enable_state.word] + |= RPM_VREG_STATE_ON << vreg->part->enable_state.shift; + mask[vreg->part->enable_state.word] + |= vreg->part->enable_state.mask; + } +} + +static int rpm_vreg_enable(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mask[2] = {0}, val[2] = {0}; + int rc = 0; + + set_enable(vreg, mask, val); + + mutex_lock(&vreg->pc_lock); + + rc = vreg_set(vreg, mask[0], val[0], mask[1], val[1], + vreg->part->request_len); + if (!rc) + vreg->is_enabled = true; + + mutex_unlock(&vreg->pc_lock); + + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + + return rc; +} + +static void set_disable(struct vreg *vreg, unsigned int *mask, + unsigned int *val) +{ + switch (vreg->type) { + case RPM_REGULATOR_TYPE_LDO: + case RPM_REGULATOR_TYPE_SMPS: + case RPM_REGULATOR_TYPE_CORNER: + /* Disable by setting a voltage of 0 uV. */ + if (vreg->part->uV.mask) { + val[vreg->part->uV.word] |= 0 << vreg->part->uV.shift; + mask[vreg->part->uV.word] |= vreg->part->uV.mask; + } else { + val[vreg->part->mV.word] |= 0 << vreg->part->mV.shift; + mask[vreg->part->mV.word] |= vreg->part->mV.mask; + } + break; + case RPM_REGULATOR_TYPE_VS: + case RPM_REGULATOR_TYPE_NCP: + /* Disable by setting enable_state. */ + val[vreg->part->enable_state.word] + |= RPM_VREG_STATE_OFF << vreg->part->enable_state.shift; + mask[vreg->part->enable_state.word] + |= vreg->part->enable_state.mask; + } +} + +static int rpm_vreg_disable(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mask[2] = {0}, val[2] = {0}; + int rc = 0; + + set_disable(vreg, mask, val); + + mutex_lock(&vreg->pc_lock); + + /* Only disable if pin control is not in use. */ + if (!vreg->is_enabled_pc) + rc = vreg_set(vreg, mask[0], val[0], mask[1], val[1], + vreg->part->request_len); + + if (!rc) + vreg->is_enabled = false; + + mutex_unlock(&vreg->pc_lock); + + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + + return rc; +} + +static int vreg_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, + unsigned *selector) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + struct vreg_range *range = &vreg->set_points->range[0]; + unsigned int mask[2] = {0}, val[2] = {0}; + int rc = 0, uV = min_uV; + int lim_min_uV, lim_max_uV, i; + + /* Check if request voltage is outside of physically settable range. */ + lim_min_uV = vreg->set_points->range[0].min_uV; + lim_max_uV = + vreg->set_points->range[vreg->set_points->count - 1].max_uV; + + if (uV < lim_min_uV && max_uV >= lim_min_uV) + uV = lim_min_uV; + + if (uV < lim_min_uV || uV > lim_max_uV) { + vreg_err(vreg, + "request v=[%d, %d] is outside possible v=[%d, %d]\n", + min_uV, max_uV, lim_min_uV, lim_max_uV); + return -EINVAL; + } + + /* Find the range which uV is inside of. */ + for (i = vreg->set_points->count - 1; i > 0; i--) { + if (uV > vreg->set_points->range[i - 1].max_uV) { + range = &vreg->set_points->range[i]; + break; + } + } + + /* + * Force uV to be an allowed set point and apply a ceiling function + * to non-set point values. + */ + uV = (uV - range->min_uV + range->step_uV - 1) / range->step_uV; + uV = uV * range->step_uV + range->min_uV; + + if (uV > max_uV) { + vreg_err(vreg, + "request v=[%d, %d] cannot be met by any set point; " + "next set point: %d\n", + min_uV, max_uV, uV); + return -EINVAL; + } + + if (vreg->type == RPM_REGULATOR_TYPE_CORNER) { + /* + * Translate from enum values which work as inputs in the + * regulator_set_voltage function to the actual corner values + * sent to the RPM. + */ + uV -= RPM_VREG_CORNER_NONE; + } + + if (vreg->part->uV.mask) { + val[vreg->part->uV.word] = uV << vreg->part->uV.shift; + mask[vreg->part->uV.word] = vreg->part->uV.mask; + } else { + val[vreg->part->mV.word] + = MICRO_TO_MILLI(uV) << vreg->part->mV.shift; + mask[vreg->part->mV.word] = vreg->part->mV.mask; + } + + mutex_lock(&vreg->pc_lock); + + /* + * Only send a request for a new voltage if the regulator is currently + * enabled. This will ensure that LDO and SMPS regulators are not + * inadvertently turned on because voltage > 0 is equivalent to + * enabling. For NCP, this just removes unnecessary RPM requests. + */ + if (vreg->is_enabled) { + rc = vreg_set(vreg, mask[0], val[0], mask[1], val[1], + vreg->part->request_len); + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + } else if (vreg->type == RPM_REGULATOR_TYPE_NCP) { + /* Regulator is disabled; store but don't send new request. */ + rc = vreg_store(vreg, mask[0], val[0], mask[1], val[1]); + } + + if (!rc && (!vreg->pdata.sleep_selectable || !vreg->is_enabled)) + vreg->save_uV = uV; + + mutex_unlock(&vreg->pc_lock); + + return rc; +} + +static int vreg_get_voltage(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->save_uV; +} + +static int vreg_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + int uV = 0; + int i; + + if (!vreg->set_points) { + vreg_err(vreg, "no voltages available\n"); + return -EINVAL; + } + + if (selector >= vreg->set_points->n_voltages) + return 0; + + for (i = 0; i < vreg->set_points->count; i++) { + if (selector < vreg->set_points->range[i].n_voltages) { + uV = selector * vreg->set_points->range[i].step_uV + + vreg->set_points->range[i].min_uV; + break; + } else { + selector -= vreg->set_points->range[i].n_voltages; + } + } + + return uV; +} + +static int vreg_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mask[2] = {0}, val[2] = {0}; + int rc = 0; + int peak_uA; + + mutex_lock(&vreg->pc_lock); + + peak_uA = MILLI_TO_MICRO((vreg->req[vreg->part->ip.word].value + & vreg->part->ip.mask) >> vreg->part->ip.shift); + + if (mode == config->mode_hpm) { + /* Make sure that request currents are in HPM range. */ + if (peak_uA < vreg_hpm_min_uA(vreg)) { + val[vreg->part->ip.word] + = MICRO_TO_MILLI(vreg_hpm_min_uA(vreg)) + << vreg->part->ip.shift; + mask[vreg->part->ip.word] = vreg->part->ip.mask; + + if (config->ia_follows_ip) { + val[vreg->part->ia.word] + |= MICRO_TO_MILLI(vreg_hpm_min_uA(vreg)) + << vreg->part->ia.shift; + mask[vreg->part->ia.word] + |= vreg->part->ia.mask; + } + } + } else if (mode == config->mode_lpm) { + /* Make sure that request currents are in LPM range. */ + if (peak_uA > vreg_lpm_max_uA(vreg)) { + val[vreg->part->ip.word] + = MICRO_TO_MILLI(vreg_lpm_max_uA(vreg)) + << vreg->part->ip.shift; + mask[vreg->part->ip.word] = vreg->part->ip.mask; + + if (config->ia_follows_ip) { + val[vreg->part->ia.word] + |= MICRO_TO_MILLI(vreg_lpm_max_uA(vreg)) + << vreg->part->ia.shift; + mask[vreg->part->ia.word] + |= vreg->part->ia.mask; + } + } + } else { + vreg_err(vreg, "invalid mode: %u\n", mode); + mutex_unlock(&vreg->pc_lock); + return -EINVAL; + } + + if (vreg->is_enabled) { + rc = vreg_set(vreg, mask[0], val[0], mask[1], val[1], + vreg->part->request_len); + } else { + /* Regulator is disabled; store but don't send new request. */ + rc = vreg_store(vreg, mask[0], val[0], mask[1], val[1]); + } + + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + else + vreg->mode = mode; + + mutex_unlock(&vreg->pc_lock); + + return rc; +} + +static unsigned int vreg_get_mode(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->mode; +} + +static unsigned int vreg_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, int load_uA) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mode; + + load_uA += vreg->pdata.system_uA; + + mutex_lock(&vreg->pc_lock); + SET_PART(vreg, ip, MICRO_TO_MILLI(saturate_peak_load(vreg, load_uA))); + if (config->ia_follows_ip) + SET_PART(vreg, ia, + MICRO_TO_MILLI(saturate_avg_load(vreg, load_uA))); + mutex_unlock(&vreg->pc_lock); + + if (load_uA >= vreg->hpm_min_load) + mode = config->mode_hpm; + else + mode = config->mode_lpm; + + return mode; +} + +static unsigned int vreg_legacy_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, int load_uA) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + + if (MICRO_TO_MILLI(load_uA) <= 0) { + /* + * vreg_legacy_get_optimum_mode is being called before consumers + * have specified their load currents via + * regulator_set_optimum_mode. Return whatever the existing mode + * is. + */ + return vreg->mode; + } + + return vreg_get_optimum_mode(rdev, input_uV, output_uV, load_uA); +} + +/* + * Returns the logical pin control enable state because the pin control options + * present in the hardware out of restart could be different from those desired + * by the consumer. + */ +static int vreg_pin_control_is_enabled(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->is_enabled_pc; +} + +static int vreg_pin_control_enable(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mask[2] = {0}, val[2] = {0}; + int rc; + + mutex_lock(&vreg->pc_lock); + + val[vreg->part->pc.word] + |= vreg->pdata.pin_ctrl << vreg->part->pc.shift; + mask[vreg->part->pc.word] |= vreg->part->pc.mask; + + val[vreg->part->pf.word] |= vreg->pdata.pin_fn << vreg->part->pf.shift; + mask[vreg->part->pf.word] |= vreg->part->pf.mask; + + if (!vreg->is_enabled) + set_enable(vreg, mask, val); + + rc = vreg_set(vreg, mask[0], val[0], mask[1], val[1], + vreg->part->request_len); + + if (!rc) + vreg->is_enabled_pc = true; + + mutex_unlock(&vreg->pc_lock); + + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + + return rc; +} + +static int vreg_pin_control_disable(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mask[2] = {0}, val[2] = {0}; + int pin_fn, rc; + + mutex_lock(&vreg->pc_lock); + + val[vreg->part->pc.word] + |= RPM_VREG_PIN_CTRL_NONE << vreg->part->pc.shift; + mask[vreg->part->pc.word] |= vreg->part->pc.mask; + + pin_fn = config->pin_func_none; + if (vreg->pdata.pin_fn == config->pin_func_sleep_b) + pin_fn = config->pin_func_sleep_b; + val[vreg->part->pf.word] |= pin_fn << vreg->part->pf.shift; + mask[vreg->part->pf.word] |= vreg->part->pf.mask; + + if (!vreg->is_enabled) + set_disable(vreg, mask, val); + + rc = vreg_set(vreg, mask[0], val[0], mask[1], val[1], + vreg->part->request_len); + + if (!rc) + vreg->is_enabled_pc = false; + + mutex_unlock(&vreg->pc_lock); + + if (rc) + vreg_err(vreg, "vreg_set failed, rc=%d\n", rc); + + return rc; +} + +static int vreg_enable_time(struct regulator_dev *rdev) +{ + struct vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->pdata.enable_time; +} + +/* Real regulator operations. */ +static struct regulator_ops ldo_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = vreg_is_enabled, + .set_voltage = vreg_set_voltage, + .get_voltage = vreg_get_voltage, + .list_voltage = vreg_list_voltage, + .set_mode = vreg_set_mode, + .get_mode = vreg_get_mode, + .get_optimum_mode = vreg_get_optimum_mode, + .enable_time = vreg_enable_time, +}; + +static struct regulator_ops smps_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = vreg_is_enabled, + .set_voltage = vreg_set_voltage, + .get_voltage = vreg_get_voltage, + .list_voltage = vreg_list_voltage, + .set_mode = vreg_set_mode, + .get_mode = vreg_get_mode, + .get_optimum_mode = vreg_get_optimum_mode, + .enable_time = vreg_enable_time, +}; + +static struct regulator_ops switch_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = vreg_is_enabled, + .enable_time = vreg_enable_time, +}; + +static struct regulator_ops ncp_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = vreg_is_enabled, + .set_voltage = vreg_set_voltage, + .get_voltage = vreg_get_voltage, + .list_voltage = vreg_list_voltage, + .enable_time = vreg_enable_time, +}; + +static struct regulator_ops corner_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = vreg_is_enabled, + .set_voltage = vreg_set_voltage, + .get_voltage = vreg_get_voltage, + .list_voltage = vreg_list_voltage, + .enable_time = vreg_enable_time, +}; + +/* Pin control regulator operations. */ +static struct regulator_ops pin_control_ops = { + .enable = vreg_pin_control_enable, + .disable = vreg_pin_control_disable, + .is_enabled = vreg_pin_control_is_enabled, +}; + +struct regulator_ops *vreg_ops[] = { + [RPM_REGULATOR_TYPE_LDO] = &ldo_ops, + [RPM_REGULATOR_TYPE_SMPS] = &smps_ops, + [RPM_REGULATOR_TYPE_VS] = &switch_ops, + [RPM_REGULATOR_TYPE_NCP] = &ncp_ops, + [RPM_REGULATOR_TYPE_CORNER] = &corner_ops, +}; + +static int __devinit +rpm_vreg_init_regulator(const struct rpm_regulator_init_data *pdata, + struct device *dev) +{ + struct regulator_desc *rdesc = NULL; + struct regulator_dev *rdev; + struct vreg *vreg; + unsigned pin_ctrl; + int id, pin_fn; + int rc = 0; + + if (!pdata) { + pr_err("platform data missing\n"); + return -EINVAL; + } + + id = pdata->id; + + if (id < config->vreg_id_min || id > config->vreg_id_max) { + pr_err("invalid regulator id: %d\n", id); + return -ENODEV; + } + + if (!config->is_real_id(pdata->id)) + id = config->pc_id_to_real_id(pdata->id); + vreg = &config->vregs[id]; + + if (config->is_real_id(pdata->id)) + rdesc = &vreg->rdesc; + else + rdesc = &vreg->rdesc_pc; + + if (vreg->type < 0 || vreg->type > RPM_REGULATOR_TYPE_MAX) { + pr_err("%s: invalid regulator type: %d\n", + vreg->rdesc.name, vreg->type); + return -EINVAL; + } + + mutex_lock(&vreg->pc_lock); + + if (vreg->set_points) + rdesc->n_voltages = vreg->set_points->n_voltages; + else + rdesc->n_voltages = 0; + + rdesc->id = pdata->id; + rdesc->owner = THIS_MODULE; + rdesc->type = REGULATOR_VOLTAGE; + + if (config->is_real_id(pdata->id)) { + /* + * Real regulator; do not modify pin control and pin function + * values. + */ + rdesc->ops = vreg_ops[vreg->type]; + pin_ctrl = vreg->pdata.pin_ctrl; + pin_fn = vreg->pdata.pin_fn; + memcpy(&(vreg->pdata), pdata, + sizeof(struct rpm_regulator_init_data)); + vreg->pdata.pin_ctrl = pin_ctrl; + vreg->pdata.pin_fn = pin_fn; + + vreg->save_uV = vreg->pdata.default_uV; + if (vreg->pdata.peak_uA >= vreg->hpm_min_load) + vreg->mode = config->mode_hpm; + else + vreg->mode = config->mode_lpm; + + /* Initialize the RPM request. */ + SET_PART(vreg, ip, + MICRO_TO_MILLI(saturate_peak_load(vreg, vreg->pdata.peak_uA))); + SET_PART(vreg, fm, vreg->pdata.force_mode); + SET_PART(vreg, pm, vreg->pdata.power_mode); + SET_PART(vreg, pd, vreg->pdata.pull_down_enable); + SET_PART(vreg, ia, + MICRO_TO_MILLI(saturate_avg_load(vreg, vreg->pdata.avg_uA))); + SET_PART(vreg, freq, vreg->pdata.freq); + SET_PART(vreg, freq_clk_src, 0); + SET_PART(vreg, comp_mode, 0); + SET_PART(vreg, hpm, 0); + if (!vreg->is_enabled_pc) { + SET_PART(vreg, pf, config->pin_func_none); + SET_PART(vreg, pc, RPM_VREG_PIN_CTRL_NONE); + } + } else { + if ((pdata->pin_ctrl & RPM_VREG_PIN_CTRL_ALL) + == RPM_VREG_PIN_CTRL_NONE + && pdata->pin_fn != config->pin_func_sleep_b) { + pr_err("%s: no pin control input specified\n", + vreg->rdesc.name); + mutex_unlock(&vreg->pc_lock); + return -EINVAL; + } + rdesc->ops = &pin_control_ops; + vreg->pdata.pin_ctrl = pdata->pin_ctrl; + vreg->pdata.pin_fn = pdata->pin_fn; + + /* Initialize the RPM request. */ + pin_fn = config->pin_func_none; + /* Allow pf=sleep_b to be specified by platform data. */ + if (vreg->pdata.pin_fn == config->pin_func_sleep_b) + pin_fn = config->pin_func_sleep_b; + SET_PART(vreg, pf, pin_fn); + SET_PART(vreg, pc, RPM_VREG_PIN_CTRL_NONE); + } + + mutex_unlock(&vreg->pc_lock); + + if (rc) + goto bail; + + rdev = regulator_register(rdesc, dev, &(pdata->init_data), vreg, NULL); + if (IS_ERR(rdev)) { + rc = PTR_ERR(rdev); + pr_err("regulator_register failed: %s, rc=%d\n", + vreg->rdesc.name, rc); + return rc; + } else { + if (config->is_real_id(pdata->id)) + vreg->rdev = rdev; + else + vreg->rdev_pc = rdev; + } + +bail: + if (rc) + pr_err("error for %s, rc=%d\n", vreg->rdesc.name, rc); + + return rc; +} + +static void rpm_vreg_set_point_init(void) +{ + struct vreg_set_points **set_points; + int i, j, temp; + + set_points = config->set_points; + + /* Calculate the number of set points available for each regulator. */ + for (i = 0; i < config->set_points_len; i++) { + temp = 0; + for (j = 0; j < set_points[i]->count; j++) { + set_points[i]->range[j].n_voltages + = (set_points[i]->range[j].max_uV + - set_points[i]->range[j].min_uV) + / set_points[i]->range[j].step_uV + 1; + temp += set_points[i]->range[j].n_voltages; + } + set_points[i]->n_voltages = temp; + } +} + +static int __devinit rpm_vreg_probe(struct platform_device *pdev) +{ + struct rpm_regulator_platform_data *platform_data; + static struct rpm_regulator_consumer_mapping *prev_consumer_map; + static int prev_consumer_map_len; + int rc = 0; + int i, id; + + platform_data = pdev->dev.platform_data; + if (!platform_data) { + pr_err("rpm-regulator requires platform data\n"); + return -EINVAL; + } + + if (rpm_version >= 0 && rpm_version <= RPM_VREG_VERSION_MAX + && platform_data->version != rpm_version) { + pr_err("rpm version %d does not match previous version %d\n", + platform_data->version, rpm_version); + return -EINVAL; + } + + if (platform_data->version < 0 + || platform_data->version > RPM_VREG_VERSION_MAX) { + pr_err("rpm version %d is invalid\n", platform_data->version); + return -EINVAL; + } + + if (rpm_version < 0 || rpm_version > RPM_VREG_VERSION_MAX) { + rpm_version = platform_data->version; + config = get_config[platform_data->version](); + vreg_id_vdd_mem = platform_data->vreg_id_vdd_mem; + vreg_id_vdd_dig = platform_data->vreg_id_vdd_dig; + if (!config) { + pr_err("rpm version %d is not available\n", + platform_data->version); + return -ENODEV; + } + if (config->use_legacy_optimum_mode) + for (i = 0; i < ARRAY_SIZE(vreg_ops); i++) + vreg_ops[i]->get_optimum_mode + = vreg_legacy_get_optimum_mode; + rpm_vreg_set_point_init(); + /* First time probed; initialize pin control mutexes. */ + for (i = 0; i < config->vregs_len; i++) + mutex_init(&config->vregs[i].pc_lock); + } + + /* Copy the list of private API consumers. */ + if (platform_data->consumer_map_len > 0) { + if (consumer_map_len == 0) { + consumer_map_len = platform_data->consumer_map_len; + consumer_map = kmemdup(platform_data->consumer_map, + sizeof(struct rpm_regulator_consumer_mapping) + * consumer_map_len, GFP_KERNEL); + if (consumer_map == NULL) { + pr_err("memory allocation failed\n"); + consumer_map_len = 0; + return -ENOMEM; + } + } else { + /* Concatenate new map with the existing one. */ + prev_consumer_map = consumer_map; + prev_consumer_map_len = consumer_map_len; + consumer_map_len += platform_data->consumer_map_len; + consumer_map = kmalloc( + sizeof(struct rpm_regulator_consumer_mapping) + * consumer_map_len, GFP_KERNEL); + if (consumer_map == NULL) { + pr_err("memory allocation failed\n"); + consumer_map_len = 0; + return -ENOMEM; + } + memcpy(consumer_map, prev_consumer_map, + sizeof(struct rpm_regulator_consumer_mapping) + * prev_consumer_map_len); + memcpy(&consumer_map[prev_consumer_map_len], + platform_data->consumer_map, + sizeof(struct rpm_regulator_consumer_mapping) + * platform_data->consumer_map_len); + } + + } + + /* Initialize all of the regulators listed in the platform data. */ + for (i = 0; i < platform_data->num_regulators; i++) { + rc = rpm_vreg_init_regulator(&platform_data->init_data[i], + &pdev->dev); + if (rc) { + pr_err("rpm_vreg_init_regulator failed, rc=%d\n", rc); + goto remove_regulators; + } + } + + platform_set_drvdata(pdev, platform_data); + + return rc; + +remove_regulators: + /* Unregister all regulators added before the erroring one. */ + for (; i >= 0; i--) { + id = platform_data->init_data[i].id; + if (config->is_real_id(id)) { + regulator_unregister(config->vregs[id].rdev); + config->vregs[id].rdev = NULL; + } else { + regulator_unregister(config->vregs[ + config->pc_id_to_real_id(id)].rdev_pc); + config->vregs[id].rdev_pc = NULL; + } + } + + return rc; +} + +static int __devexit rpm_vreg_remove(struct platform_device *pdev) +{ + struct rpm_regulator_platform_data *platform_data; + int i, id; + + platform_data = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + + if (platform_data) { + for (i = 0; i < platform_data->num_regulators; i++) { + id = platform_data->init_data[i].id; + if (config->is_real_id(id)) { + regulator_unregister(config->vregs[id].rdev); + config->vregs[id].rdev = NULL; + } else { + regulator_unregister(config->vregs[ + config->pc_id_to_real_id(id)].rdev_pc); + config->vregs[id].rdev_pc = NULL; + } + } + } + + return 0; +} + +static struct platform_driver rpm_vreg_driver = { + .probe = rpm_vreg_probe, + .remove = __devexit_p(rpm_vreg_remove), + .driver = { + .name = RPM_REGULATOR_DEV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init rpm_vreg_init(void) +{ + return platform_driver_register(&rpm_vreg_driver); +} + +static void __exit rpm_vreg_exit(void) +{ + int i; + + platform_driver_unregister(&rpm_vreg_driver); + + kfree(consumer_map); + + for (i = 0; i < config->vregs_len; i++) + mutex_destroy(&config->vregs[i].pc_lock); +} + +postcore_initcall(rpm_vreg_init); +module_exit(rpm_vreg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPM regulator driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:" RPM_REGULATOR_DEV_NAME); diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c new file mode 100644 index 00000000000..75f4d928fc6 --- /dev/null +++ b/arch/arm/mach-msm/rpm-smd.c @@ -0,0 +1,826 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rpm-notifier.h" + +struct msm_rpm_driver_data { + const char *ch_name; + uint32_t ch_type; + smd_channel_t *ch_info; + struct work_struct work; + spinlock_t smd_lock_write; + spinlock_t smd_lock_read; + struct completion smd_open; +}; + +#define DEFAULT_BUFFER_SIZE 256 +#define GFP_FLAG(noirq) (noirq ? GFP_ATOMIC : GFP_KERNEL) +#define INV_HDR "resource does not exist" +#define ERR "err\0" +#define MAX_ERR_BUFFER_SIZE 60 + +static struct atomic_notifier_head msm_rpm_sleep_notifier; +static bool standalone; + +int msm_rpm_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&msm_rpm_sleep_notifier, nb); +} + +int msm_rpm_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&msm_rpm_sleep_notifier, nb); +} + +static struct workqueue_struct *msm_rpm_smd_wq; + +enum { + MSM_RPM_MSG_REQUEST_TYPE = 0, + MSM_RPM_MSG_TYPE_NR, +}; + +static const uint32_t msm_rpm_request_service[MSM_RPM_MSG_TYPE_NR] = { + 0x716572, /* 'req\0' */ +}; + +/*the order of fields matter and reflect the order expected by the RPM*/ +struct rpm_request_header { + uint32_t service_type; + uint32_t request_len; +}; + +struct rpm_message_header { + uint32_t msg_id; + enum msm_rpm_set set; + uint32_t resource_type; + uint32_t resource_id; + uint32_t data_len; +}; + +struct msm_rpm_kvp_data { + uint32_t key; + uint32_t nbytes; /* number of bytes */ + uint8_t *value; + bool valid; +}; + +static atomic_t msm_rpm_msg_id = ATOMIC_INIT(0); + +static struct msm_rpm_driver_data msm_rpm_data; + +struct msm_rpm_request { + struct rpm_request_header req_hdr; + struct rpm_message_header msg_hdr; + struct msm_rpm_kvp_data *kvp; + uint32_t num_elements; + uint32_t write_idx; + uint8_t *buf; + uint32_t numbytes; +}; + +/* + * Data related to message acknowledgement + */ + +LIST_HEAD(msm_rpm_wait_list); + +struct msm_rpm_wait_data { + struct list_head list; + uint32_t msg_id; + bool ack_recd; + int errno; + struct completion ack; +}; +DEFINE_SPINLOCK(msm_rpm_list_lock); + +struct msm_rpm_ack_msg { + uint32_t req; + uint32_t req_len; + uint32_t rsc_id; + uint32_t msg_len; + uint32_t id_ack; +}; + +static int irq_process; + +LIST_HEAD(msm_rpm_ack_list); + +static void msm_rpm_notify_sleep_chain(struct rpm_message_header *hdr, + struct msm_rpm_kvp_data *kvp) +{ + struct msm_rpm_notifier_data notif; + + notif.rsc_type = hdr->resource_type; + notif.rsc_id = hdr->resource_id; + notif.key = kvp->key; + notif.size = kvp->nbytes; + notif.value = kvp->value; + atomic_notifier_call_chain(&msm_rpm_sleep_notifier, 0, ¬if); +} + +static int msm_rpm_add_kvp_data_common(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size, bool noirq) +{ + int i; + int data_size, msg_size; + + if (!handle) + return -EINVAL; + + data_size = ALIGN(size, SZ_4); + msg_size = data_size + sizeof(struct rpm_request_header); + + for (i = 0; i < handle->write_idx; i++) { + if (handle->kvp[i].key != key) + continue; + if (handle->kvp[i].nbytes != data_size) { + kfree(handle->kvp[i].value); + handle->kvp[i].value = NULL; + } else { + if (!memcmp(handle->kvp[i].value, data, data_size)) + return 0; + } + break; + } + + if (i >= handle->num_elements) + return -ENOMEM; + + if (i == handle->write_idx) + handle->write_idx++; + + if (!handle->kvp[i].value) { + handle->kvp[i].value = kzalloc(data_size, GFP_FLAG(noirq)); + + if (!handle->kvp[i].value) + return -ENOMEM; + } else { + /* We enter the else case, if a key already exists but the + * data doesn't match. In which case, we should zero the data + * out. + */ + memset(handle->kvp[i].value, 0, data_size); + } + + if (!handle->kvp[i].valid) + handle->msg_hdr.data_len += msg_size; + else + handle->msg_hdr.data_len += (data_size - handle->kvp[i].nbytes); + + handle->kvp[i].nbytes = data_size; + handle->kvp[i].key = key; + memcpy(handle->kvp[i].value, data, size); + handle->kvp[i].valid = true; + + if (handle->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET) + msm_rpm_notify_sleep_chain(&handle->msg_hdr, &handle->kvp[i]); + + return 0; + +} + +static struct msm_rpm_request *msm_rpm_create_request_common( + enum msm_rpm_set set, uint32_t rsc_type, uint32_t rsc_id, + int num_elements, bool noirq) +{ + struct msm_rpm_request *cdata; + + cdata = kzalloc(sizeof(struct msm_rpm_request), + GFP_FLAG(noirq)); + + if (!cdata) { + printk(KERN_INFO"%s():Cannot allocate memory for client data\n", + __func__); + goto cdata_alloc_fail; + } + + cdata->msg_hdr.set = set; + cdata->msg_hdr.resource_type = rsc_type; + cdata->msg_hdr.resource_id = rsc_id; + cdata->msg_hdr.data_len = 0; + + cdata->num_elements = num_elements; + cdata->write_idx = 0; + + cdata->kvp = kzalloc(sizeof(struct msm_rpm_kvp_data) * num_elements, + GFP_FLAG(noirq)); + + if (!cdata->kvp) { + pr_warn("%s(): Cannot allocate memory for key value data\n", + __func__); + goto kvp_alloc_fail; + } + + cdata->buf = kzalloc(DEFAULT_BUFFER_SIZE, GFP_FLAG(noirq)); + + if (!cdata->buf) + goto buf_alloc_fail; + + cdata->numbytes = DEFAULT_BUFFER_SIZE; + return cdata; + +buf_alloc_fail: + kfree(cdata->kvp); +kvp_alloc_fail: + kfree(cdata); +cdata_alloc_fail: + return NULL; + +} + +void msm_rpm_free_request(struct msm_rpm_request *handle) +{ + int i; + + if (!handle) + return; + for (i = 0; i < handle->write_idx; i++) + kfree(handle->kvp[i].value); + kfree(handle->kvp); + kfree(handle); +} +EXPORT_SYMBOL(msm_rpm_free_request); + +struct msm_rpm_request *msm_rpm_create_request( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return msm_rpm_create_request_common(set, rsc_type, rsc_id, + num_elements, false); +} +EXPORT_SYMBOL(msm_rpm_create_request); + +struct msm_rpm_request *msm_rpm_create_request_noirq( + enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, int num_elements) +{ + return msm_rpm_create_request_common(set, rsc_type, rsc_id, + num_elements, true); +} +EXPORT_SYMBOL(msm_rpm_create_request_noirq); + +int msm_rpm_add_kvp_data(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + return msm_rpm_add_kvp_data_common(handle, key, data, size, false); + +} +EXPORT_SYMBOL(msm_rpm_add_kvp_data); + +int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle, + uint32_t key, const uint8_t *data, int size) +{ + return msm_rpm_add_kvp_data_common(handle, key, data, size, true); +} +EXPORT_SYMBOL(msm_rpm_add_kvp_data_noirq); + +/* Runs in interrupt context */ +static void msm_rpm_notify(void *data, unsigned event) +{ + struct msm_rpm_driver_data *pdata = (struct msm_rpm_driver_data *)data; + BUG_ON(!pdata); + + if (!(pdata->ch_info)) + return; + + switch (event) { + case SMD_EVENT_DATA: + queue_work(msm_rpm_smd_wq, &pdata->work); + break; + case SMD_EVENT_OPEN: + complete(&pdata->smd_open); + break; + case SMD_EVENT_CLOSE: + case SMD_EVENT_STATUS: + case SMD_EVENT_REOPEN_READY: + break; + default: + pr_info("Unknown SMD event\n"); + + } +} + +static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id) +{ + struct list_head *ptr; + struct msm_rpm_wait_data *elem; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + + list_for_each(ptr, &msm_rpm_wait_list) { + elem = list_entry(ptr, struct msm_rpm_wait_data, list); + if (elem && (elem->msg_id == msg_id)) + break; + elem = NULL; + } + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + return elem; +} + +static int msm_rpm_get_next_msg_id(void) +{ + int id; + + do { + id = atomic_inc_return(&msm_rpm_msg_id); + } while ((id == 0) || msm_rpm_get_entry_from_msg_id(id)); + + return id; +} + +static int msm_rpm_add_wait_list(uint32_t msg_id) +{ + unsigned long flags; + struct msm_rpm_wait_data *data = + kzalloc(sizeof(struct msm_rpm_wait_data), GFP_ATOMIC); + + if (!data) + return -ENOMEM; + + init_completion(&data->ack); + data->ack_recd = false; + data->msg_id = msg_id; + spin_lock_irqsave(&msm_rpm_list_lock, flags); + list_add(&data->list, &msm_rpm_wait_list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + + return 0; +} + +static void msm_rpm_free_list_entry(struct msm_rpm_wait_data *elem) +{ + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + list_del(&elem->list); + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); + kfree(elem); +} + +static void msm_rpm_process_ack(uint32_t msg_id, int errno) +{ + struct list_head *ptr; + struct msm_rpm_wait_data *elem; + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_list_lock, flags); + + list_for_each(ptr, &msm_rpm_wait_list) { + elem = list_entry(ptr, struct msm_rpm_wait_data, list); + if (elem && (elem->msg_id == msg_id)) { + elem->errno = errno; + elem->ack_recd = true; + complete(&elem->ack); + break; + } + elem = NULL; + } + WARN_ON(!elem); + + spin_unlock_irqrestore(&msm_rpm_list_lock, flags); +} + +struct msm_rpm_kvp_packet { + uint32_t id; + uint32_t len; + uint32_t val; +}; + +static inline uint32_t msm_rpm_get_msg_id_from_ack(uint8_t *buf) +{ + return ((struct msm_rpm_ack_msg *)buf)->id_ack; +} + +static inline int msm_rpm_get_error_from_ack(uint8_t *buf) +{ + uint8_t *tmp; + uint32_t req_len = ((struct msm_rpm_ack_msg *)buf)->req_len; + + int rc = -ENODEV; + + req_len -= sizeof(struct msm_rpm_ack_msg); + req_len += 2 * sizeof(uint32_t); + if (!req_len) + return 0; + + tmp = buf + sizeof(struct msm_rpm_ack_msg); + + BUG_ON(memcmp(tmp, ERR, sizeof(uint32_t))); + + tmp += 2 * sizeof(uint32_t); + + if (!(memcmp(tmp, INV_HDR, min(req_len, sizeof(INV_HDR))-1))) + rc = -EINVAL; + + return rc; +} + +static void msm_rpm_read_smd_data(char *buf) +{ + int pkt_sz; + int bytes_read = 0; + + pkt_sz = smd_cur_packet_size(msm_rpm_data.ch_info); + + BUG_ON(pkt_sz > MAX_ERR_BUFFER_SIZE); + + if (pkt_sz != smd_read_avail(msm_rpm_data.ch_info)) + return; + + BUG_ON(pkt_sz == 0); + + do { + int len; + + len = smd_read(msm_rpm_data.ch_info, buf + bytes_read, pkt_sz); + pkt_sz -= len; + bytes_read += len; + + } while (pkt_sz > 0); + + BUG_ON(pkt_sz < 0); +} + +static void msm_rpm_smd_work(struct work_struct *work) +{ + uint32_t msg_id; + int errno; + char buf[MAX_ERR_BUFFER_SIZE] = {0}; + unsigned long flags; + + while (smd_is_pkt_avail(msm_rpm_data.ch_info) && !irq_process) { + spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags); + msm_rpm_read_smd_data(buf); + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags); + msg_id = msm_rpm_get_msg_id_from_ack(buf); + errno = msm_rpm_get_error_from_ack(buf); + msm_rpm_process_ack(msg_id, errno); + } +} + +static int msm_rpm_send_data(struct msm_rpm_request *cdata, + int msg_type, bool noirq) +{ + uint8_t *tmpbuff; + int i, ret, msg_size; + unsigned long flags; + + int req_hdr_sz, msg_hdr_sz; + + if (!cdata->msg_hdr.data_len) + return 0; + req_hdr_sz = sizeof(cdata->req_hdr); + msg_hdr_sz = sizeof(cdata->msg_hdr); + + cdata->req_hdr.service_type = msm_rpm_request_service[msg_type]; + + cdata->msg_hdr.msg_id = msm_rpm_get_next_msg_id(); + + cdata->req_hdr.request_len = cdata->msg_hdr.data_len + msg_hdr_sz; + msg_size = cdata->req_hdr.request_len + req_hdr_sz; + + /* populate data_len */ + if (msg_size > cdata->numbytes) { + kfree(cdata->buf); + cdata->numbytes = msg_size; + cdata->buf = kzalloc(msg_size, GFP_FLAG(noirq)); + } + + if (!cdata->buf) + return 0; + + tmpbuff = cdata->buf; + + memcpy(tmpbuff, &cdata->req_hdr, req_hdr_sz + msg_hdr_sz); + + tmpbuff += req_hdr_sz + msg_hdr_sz; + + for (i = 0; (i < cdata->write_idx); i++) { + /* Sanity check */ + BUG_ON((tmpbuff - cdata->buf) > cdata->numbytes); + + if (!cdata->kvp[i].valid) + continue; + + memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t)); + tmpbuff += sizeof(uint32_t); + + memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t)); + tmpbuff += sizeof(uint32_t); + + memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes); + tmpbuff += cdata->kvp[i].nbytes; + } + + if (standalone) { + for (i = 0; (i < cdata->write_idx); i++) + cdata->kvp[i].valid = false; + + cdata->msg_hdr.data_len = 0; + ret = cdata->msg_hdr.msg_id; + return ret; + } + + msm_rpm_add_wait_list(cdata->msg_hdr.msg_id); + + spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags); + + ret = smd_write_avail(msm_rpm_data.ch_info); + + if (ret < 0) { + pr_warn("%s(): SMD not initialized\n", __func__); + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags); + return 0; + } + + while ((ret < msg_size)) { + if (!noirq) { + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, + flags); + cpu_relax(); + spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags); + } else + udelay(5); + ret = smd_write_avail(msm_rpm_data.ch_info); + } + + ret = smd_write(msm_rpm_data.ch_info, &cdata->buf[0], msg_size); + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags); + + if (ret == msg_size) { + for (i = 0; (i < cdata->write_idx); i++) + cdata->kvp[i].valid = false; + cdata->msg_hdr.data_len = 0; + ret = cdata->msg_hdr.msg_id; + } else if (ret < msg_size) { + struct msm_rpm_wait_data *rc; + ret = 0; + pr_info("Failed to write data msg_size:%d ret:%d\n", + msg_size, ret); + rc = msm_rpm_get_entry_from_msg_id(cdata->msg_hdr.msg_id); + if (rc) + msm_rpm_free_list_entry(rc); + } + return ret; +} + +int msm_rpm_send_request(struct msm_rpm_request *handle) +{ + return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false); +} +EXPORT_SYMBOL(msm_rpm_send_request); + +int msm_rpm_send_request_noirq(struct msm_rpm_request *handle) +{ + return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, true); +} +EXPORT_SYMBOL(msm_rpm_send_request_noirq); + +int msm_rpm_wait_for_ack(uint32_t msg_id) +{ + struct msm_rpm_wait_data *elem; + int rc = 0; + + if (!msg_id) + return -EINVAL; + + if (standalone) + return 0; + + elem = msm_rpm_get_entry_from_msg_id(msg_id); + if (!elem) + return 0; + + rc = wait_for_completion_timeout(&elem->ack, msecs_to_jiffies(1)); + if (!rc) { + pr_warn("%s(): Timed out after 1 ms\n", __func__); + rc = -ETIMEDOUT; + } else { + rc = elem->errno; + msm_rpm_free_list_entry(elem); + } + return rc; +} +EXPORT_SYMBOL(msm_rpm_wait_for_ack); + +int msm_rpm_wait_for_ack_noirq(uint32_t msg_id) +{ + struct msm_rpm_wait_data *elem; + unsigned long flags; + int rc = 0; + uint32_t id = 0; + int count = 0; + + if (!msg_id) + return -EINVAL; + + if (standalone) + return 0; + + spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags); + irq_process = true; + + elem = msm_rpm_get_entry_from_msg_id(msg_id); + + if (!elem) + /* Should this be a bug + * Is it ok for another thread to read the msg? + */ + goto wait_ack_cleanup; + + while ((id != msg_id) && (count++ < 10)) { + if (smd_is_pkt_avail(msm_rpm_data.ch_info)) { + int errno; + char buf[MAX_ERR_BUFFER_SIZE] = {}; + + msm_rpm_read_smd_data(buf); + id = msm_rpm_get_msg_id_from_ack(buf); + errno = msm_rpm_get_error_from_ack(buf); + msm_rpm_process_ack(id, errno); + } else + udelay(100); + } + + if (count == 10) { + rc = -ETIMEDOUT; + pr_warn("%s(): Timed out after 1ms\n", __func__); + } else { + rc = elem->errno; + msm_rpm_free_list_entry(elem); + } +wait_ack_cleanup: + irq_process = false; + spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags); + return rc; +} +EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq); + +int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request(set, rsc_type, rsc_id, nelems); + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req)); +bail: + msm_rpm_free_request(req); + return rc; +} +EXPORT_SYMBOL(msm_rpm_send_message); + +int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type, + uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems) +{ + int i, rc; + struct msm_rpm_request *req = + msm_rpm_create_request_noirq(set, rsc_type, rsc_id, nelems); + if (!req) + return -ENOMEM; + + for (i = 0; i < nelems; i++) { + rc = msm_rpm_add_kvp_data_noirq(req, kvp[i].key, + kvp[i].data, kvp[i].length); + if (rc) + goto bail; + } + + rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(req)); +bail: + msm_rpm_free_request(req); + return rc; +} +EXPORT_SYMBOL(msm_rpm_send_message_noirq); +static bool msm_rpm_set_standalone(void) +{ + if (machine_is_copper()) { + pr_warn("%s(): Running in standalone mode, requests " + "will not be sent to RPM\n", __func__); + standalone = true; + } + return standalone; +} + +static int __devinit msm_rpm_dev_probe(struct platform_device *pdev) +{ + char *key = NULL; + int ret; + + key = "rpm-channel-name"; + ret = of_property_read_string(pdev->dev.of_node, key, + &msm_rpm_data.ch_name); + if (ret) + goto fail; + + key = "rpm-channel-type"; + ret = of_property_read_u32(pdev->dev.of_node, key, + &msm_rpm_data.ch_type); + if (ret) + goto fail; + + init_completion(&msm_rpm_data.smd_open); + spin_lock_init(&msm_rpm_data.smd_lock_write); + spin_lock_init(&msm_rpm_data.smd_lock_read); + INIT_WORK(&msm_rpm_data.work, msm_rpm_smd_work); + + if (smd_named_open_on_edge(msm_rpm_data.ch_name, msm_rpm_data.ch_type, + &msm_rpm_data.ch_info, &msm_rpm_data, + msm_rpm_notify)) { + pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name, + msm_rpm_data.ch_type); + + msm_rpm_set_standalone(); + BUG_ON(!standalone); + complete(&msm_rpm_data.smd_open); + } + + ret = wait_for_completion_timeout(&msm_rpm_data.smd_open, + msecs_to_jiffies(5)); + + BUG_ON(!ret); + + smd_disable_read_intr(msm_rpm_data.ch_info); + + if (!standalone) { + msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd"); + if (!msm_rpm_smd_wq) + return -EINVAL; + } + + of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + return 0; +fail: + pr_err("%s(): Failed to read node: %s, key=%s\n", __func__, + pdev->dev.of_node->full_name, key); + return -EINVAL; +} + +static struct of_device_id msm_rpm_match_table[] = { + {.compatible = "qcom,rpm-smd"}, + {}, +}; + +static struct platform_driver msm_rpm_device_driver = { + .probe = msm_rpm_dev_probe, + .driver = { + .name = "rpm-smd", + .owner = THIS_MODULE, + .of_match_table = msm_rpm_match_table, + }, +}; + +int __init msm_rpm_driver_init(void) +{ + static bool registered; + + if (registered) + return 0; + registered = true; + + return platform_driver_register(&msm_rpm_device_driver); +} +EXPORT_SYMBOL(msm_rpm_driver_init); +late_initcall(msm_rpm_driver_init); diff --git a/arch/arm/mach-msm/rpm.c b/arch/arm/mach-msm/rpm.c new file mode 100644 index 00000000000..44e50dd985d --- /dev/null +++ b/arch/arm/mach-msm/rpm.c @@ -0,0 +1,1023 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************** + * Data type and structure definitions + *****************************************************************************/ + +struct msm_rpm_request { + struct msm_rpm_iv_pair *req; + int count; + uint32_t *ctx_mask_ack; + uint32_t *sel_masks_ack; + struct completion *done; +}; + +struct msm_rpm_notif_config { + struct msm_rpm_iv_pair iv[SEL_MASK_SIZE * 2]; +}; + +#define configured_iv(notif_cfg) ((notif_cfg)->iv) +#define registered_iv(notif_cfg) ((notif_cfg)->iv + msm_rpm_sel_mask_size) + +static uint32_t msm_rpm_sel_mask_size; +static struct msm_rpm_platform_data msm_rpm_data; + +static DEFINE_MUTEX(msm_rpm_mutex); +static DEFINE_SPINLOCK(msm_rpm_lock); +static DEFINE_SPINLOCK(msm_rpm_irq_lock); + +static struct msm_rpm_request *msm_rpm_request; +static struct msm_rpm_request msm_rpm_request_irq_mode; +static struct msm_rpm_request msm_rpm_request_poll_mode; + +static LIST_HEAD(msm_rpm_notifications); +static struct msm_rpm_notif_config msm_rpm_notif_cfgs[MSM_RPM_CTX_SET_COUNT]; +static bool msm_rpm_init_notif_done; +/****************************************************************************** + * Internal functions + *****************************************************************************/ + +static inline unsigned int target_enum(unsigned int id) +{ + BUG_ON(id >= MSM_RPM_ID_LAST); + return msm_rpm_data.target_id[id].id; +} + +static inline unsigned int target_status(unsigned int id) +{ + BUG_ON(id >= MSM_RPM_STATUS_ID_LAST); + return msm_rpm_data.target_status[id]; +} + +static inline unsigned int target_ctrl(unsigned int id) +{ + BUG_ON(id >= MSM_RPM_CTRL_LAST); + return msm_rpm_data.target_ctrl_id[id]; +} + +static inline uint32_t msm_rpm_read(unsigned int page, unsigned int reg) +{ + return __raw_readl(msm_rpm_data.reg_base_addrs[page] + reg * 4); +} + +static inline void msm_rpm_write( + unsigned int page, unsigned int reg, uint32_t value) +{ + __raw_writel(value, + msm_rpm_data.reg_base_addrs[page] + reg * 4); +} + +static inline void msm_rpm_read_contiguous( + unsigned int page, unsigned int reg, uint32_t *values, int count) +{ + int i; + + for (i = 0; i < count; i++) + values[i] = msm_rpm_read(page, reg + i); +} + +static inline void msm_rpm_write_contiguous( + unsigned int page, unsigned int reg, uint32_t *values, int count) +{ + int i; + + for (i = 0; i < count; i++) + msm_rpm_write(page, reg + i, values[i]); +} + +static inline void msm_rpm_write_contiguous_zeros( + unsigned int page, unsigned int reg, int count) +{ + int i; + + for (i = 0; i < count; i++) + msm_rpm_write(page, reg + i, 0); +} + +static inline uint32_t msm_rpm_map_id_to_sel(uint32_t id) +{ + return (id >= MSM_RPM_ID_LAST) ? msm_rpm_data.sel_last + 1 : + msm_rpm_data.target_id[id].sel; +} + +/* + * Note: the function does not clear the masks before filling them. + * + * Return value: + * 0: success + * -EINVAL: invalid id in array + */ +static int msm_rpm_fill_sel_masks( + uint32_t *sel_masks, struct msm_rpm_iv_pair *req, int count) +{ + uint32_t sel; + int i; + + for (i = 0; i < count; i++) { + sel = msm_rpm_map_id_to_sel(req[i].id); + + if (sel > msm_rpm_data.sel_last) { + pr_err("%s(): RPM ID %d not defined for target\n", + __func__, req[i].id); + return -EINVAL; + } + + sel_masks[msm_rpm_get_sel_mask_reg(sel)] |= + msm_rpm_get_sel_mask(sel); + } + + return 0; +} + +static inline void msm_rpm_send_req_interrupt(void) +{ + __raw_writel(msm_rpm_data.ipc_rpm_val, + msm_rpm_data.ipc_rpm_reg); +} + +/* + * Note: assumes caller has acquired . + * + * Return value: + * 0: request acknowledgement + * 1: notification + * 2: spurious interrupt + */ +static int msm_rpm_process_ack_interrupt(void) +{ + uint32_t ctx_mask_ack; + uint32_t sel_masks_ack[SEL_MASK_SIZE] = {0}; + + ctx_mask_ack = msm_rpm_read(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_ACK_CTX_0)); + msm_rpm_read_contiguous(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_ACK_SEL_0), + sel_masks_ack, msm_rpm_sel_mask_size); + + if (ctx_mask_ack & msm_rpm_get_ctx_mask(MSM_RPM_CTX_NOTIFICATION)) { + struct msm_rpm_notification *n; + int i; + + list_for_each_entry(n, &msm_rpm_notifications, list) + for (i = 0; i < msm_rpm_sel_mask_size; i++) + if (sel_masks_ack[i] & n->sel_masks[i]) { + up(&n->sem); + break; + } + + msm_rpm_write_contiguous_zeros(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_ACK_SEL_0), + msm_rpm_sel_mask_size); + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_ACK_CTX_0), 0); + /* Ensure the write is complete before return */ + mb(); + + return 1; + } + + if (msm_rpm_request) { + int i; + + *(msm_rpm_request->ctx_mask_ack) = ctx_mask_ack; + memcpy(msm_rpm_request->sel_masks_ack, sel_masks_ack, + sizeof(sel_masks_ack)); + + for (i = 0; i < msm_rpm_request->count; i++) + msm_rpm_request->req[i].value = + msm_rpm_read(MSM_RPM_PAGE_ACK, + target_enum(msm_rpm_request->req[i].id)); + + msm_rpm_write_contiguous_zeros(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_ACK_SEL_0), + msm_rpm_sel_mask_size); + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_ACK_CTX_0), 0); + /* Ensure the write is complete before return */ + mb(); + + if (msm_rpm_request->done) + complete_all(msm_rpm_request->done); + + msm_rpm_request = NULL; + return 0; + } + + return 2; +} + +static void msm_rpm_err_fatal(void) +{ + /* Tell RPM that we're handling the interrupt */ + __raw_writel(0x1, msm_rpm_data.ipc_rpm_reg); + panic("RPM error fataled"); +} + +static irqreturn_t msm_rpm_err_interrupt(int irq, void *dev_id) +{ + msm_rpm_err_fatal(); + return IRQ_HANDLED; +} + +static irqreturn_t msm_rpm_ack_interrupt(int irq, void *dev_id) +{ + unsigned long flags; + int rc; + + if (dev_id != &msm_rpm_ack_interrupt) + return IRQ_NONE; + + spin_lock_irqsave(&msm_rpm_irq_lock, flags); + rc = msm_rpm_process_ack_interrupt(); + spin_unlock_irqrestore(&msm_rpm_irq_lock, flags); + + return IRQ_HANDLED; +} + +/* + * Note: assumes caller has acquired . + */ +static void msm_rpm_busy_wait_for_request_completion( + bool allow_async_completion) +{ + int rc; + + do { + while (!gic_is_spi_pending(msm_rpm_data.irq_ack) && + msm_rpm_request) { + if (allow_async_completion) + spin_unlock(&msm_rpm_irq_lock); + if (gic_is_spi_pending(msm_rpm_data.irq_err)) + msm_rpm_err_fatal(); + gic_clear_spi_pending(msm_rpm_data.irq_err); + udelay(1); + if (allow_async_completion) + spin_lock(&msm_rpm_irq_lock); + } + + if (!msm_rpm_request) + break; + + rc = msm_rpm_process_ack_interrupt(); + gic_clear_spi_pending(msm_rpm_data.irq_ack); + } while (rc); +} + +/* Upon return, the array will contain values from the ack page. + * + * Note: assumes caller has acquired . + * + * Return value: + * 0: success + * -ENOSPC: request rejected + */ +static int msm_rpm_set_exclusive(int ctx, + uint32_t *sel_masks, struct msm_rpm_iv_pair *req, int count) +{ + DECLARE_COMPLETION_ONSTACK(ack); + unsigned long flags; + uint32_t ctx_mask = msm_rpm_get_ctx_mask(ctx); + uint32_t ctx_mask_ack = 0; + uint32_t sel_masks_ack[SEL_MASK_SIZE]; + int i; + + msm_rpm_request_irq_mode.req = req; + msm_rpm_request_irq_mode.count = count; + msm_rpm_request_irq_mode.ctx_mask_ack = &ctx_mask_ack; + msm_rpm_request_irq_mode.sel_masks_ack = sel_masks_ack; + msm_rpm_request_irq_mode.done = &ack; + + spin_lock_irqsave(&msm_rpm_lock, flags); + spin_lock(&msm_rpm_irq_lock); + + BUG_ON(msm_rpm_request); + msm_rpm_request = &msm_rpm_request_irq_mode; + + for (i = 0; i < count; i++) { + BUG_ON(target_enum(req[i].id) >= MSM_RPM_ID_LAST); + msm_rpm_write(MSM_RPM_PAGE_REQ, + target_enum(req[i].id), req[i].value); + } + + msm_rpm_write_contiguous(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_REQ_SEL_0), + sel_masks, msm_rpm_sel_mask_size); + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_REQ_CTX_0), ctx_mask); + + /* Ensure RPM data is written before sending the interrupt */ + mb(); + msm_rpm_send_req_interrupt(); + + spin_unlock(&msm_rpm_irq_lock); + spin_unlock_irqrestore(&msm_rpm_lock, flags); + + wait_for_completion(&ack); + + BUG_ON((ctx_mask_ack & ~(msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED))) + != ctx_mask); + BUG_ON(memcmp(sel_masks, sel_masks_ack, sizeof(sel_masks_ack))); + + return (ctx_mask_ack & msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED)) + ? -ENOSPC : 0; +} + +/* Upon return, the array will contain values from the ack page. + * + * Note: assumes caller has acquired . + * + * Return value: + * 0: success + * -ENOSPC: request rejected + */ +static int msm_rpm_set_exclusive_noirq(int ctx, + uint32_t *sel_masks, struct msm_rpm_iv_pair *req, int count) +{ + unsigned int irq = msm_rpm_data.irq_ack; + unsigned long flags; + uint32_t ctx_mask = msm_rpm_get_ctx_mask(ctx); + uint32_t ctx_mask_ack = 0; + uint32_t sel_masks_ack[SEL_MASK_SIZE]; + struct irq_chip *irq_chip, *err_chip; + int i; + + msm_rpm_request_poll_mode.req = req; + msm_rpm_request_poll_mode.count = count; + msm_rpm_request_poll_mode.ctx_mask_ack = &ctx_mask_ack; + msm_rpm_request_poll_mode.sel_masks_ack = sel_masks_ack; + msm_rpm_request_poll_mode.done = NULL; + + spin_lock_irqsave(&msm_rpm_irq_lock, flags); + irq_chip = irq_get_chip(irq); + if (!irq_chip) { + spin_unlock_irqrestore(&msm_rpm_irq_lock, flags); + return -ENOSPC; + } + irq_chip->irq_mask(irq_get_irq_data(irq)); + err_chip = irq_get_chip(msm_rpm_data.irq_err); + if (!err_chip) { + irq_chip->irq_unmask(irq_get_irq_data(irq)); + spin_unlock_irqrestore(&msm_rpm_irq_lock, flags); + return -ENOSPC; + } + err_chip->irq_mask(irq_get_irq_data(msm_rpm_data.irq_err)); + + if (msm_rpm_request) { + msm_rpm_busy_wait_for_request_completion(true); + BUG_ON(msm_rpm_request); + } + + msm_rpm_request = &msm_rpm_request_poll_mode; + + for (i = 0; i < count; i++) { + BUG_ON(target_enum(req[i].id) >= MSM_RPM_ID_LAST); + msm_rpm_write(MSM_RPM_PAGE_REQ, + target_enum(req[i].id), req[i].value); + } + + msm_rpm_write_contiguous(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_REQ_SEL_0), + sel_masks, msm_rpm_sel_mask_size); + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_REQ_CTX_0), ctx_mask); + + /* Ensure RPM data is written before sending the interrupt */ + mb(); + msm_rpm_send_req_interrupt(); + + msm_rpm_busy_wait_for_request_completion(false); + BUG_ON(msm_rpm_request); + + err_chip->irq_unmask(irq_get_irq_data(msm_rpm_data.irq_err)); + irq_chip->irq_unmask(irq_get_irq_data(irq)); + spin_unlock_irqrestore(&msm_rpm_irq_lock, flags); + + BUG_ON((ctx_mask_ack & ~(msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED))) + != ctx_mask); + BUG_ON(memcmp(sel_masks, sel_masks_ack, sizeof(sel_masks_ack))); + + return (ctx_mask_ack & msm_rpm_get_ctx_mask(MSM_RPM_CTX_REJECTED)) + ? -ENOSPC : 0; +} + +/* Upon return, the array will contain values from the ack page. + * + * Return value: + * 0: success + * -EINVAL: invalid or invalid id in array + * -ENOSPC: request rejected + * -ENODEV: RPM driver not initialized + */ +static int msm_rpm_set_common( + int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq) +{ + uint32_t sel_masks[SEL_MASK_SIZE] = {}; + int rc; + + if (ctx >= MSM_RPM_CTX_SET_COUNT) { + rc = -EINVAL; + goto set_common_exit; + } + + rc = msm_rpm_fill_sel_masks(sel_masks, req, count); + if (rc) + goto set_common_exit; + + if (noirq) { + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_lock, flags); + rc = msm_rpm_set_exclusive_noirq(ctx, sel_masks, req, count); + spin_unlock_irqrestore(&msm_rpm_lock, flags); + } else { + mutex_lock(&msm_rpm_mutex); + rc = msm_rpm_set_exclusive(ctx, sel_masks, req, count); + mutex_unlock(&msm_rpm_mutex); + } + +set_common_exit: + return rc; +} + +/* + * Return value: + * 0: success + * -EINVAL: invalid or invalid id in array + * -ENODEV: RPM driver not initialized. + */ +static int msm_rpm_clear_common( + int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq) +{ + uint32_t sel_masks[SEL_MASK_SIZE] = {}; + struct msm_rpm_iv_pair r[SEL_MASK_SIZE]; + int rc; + int i; + + if (ctx >= MSM_RPM_CTX_SET_COUNT) { + rc = -EINVAL; + goto clear_common_exit; + } + + rc = msm_rpm_fill_sel_masks(sel_masks, req, count); + if (rc) + goto clear_common_exit; + + for (i = 0; i < ARRAY_SIZE(r); i++) { + r[i].id = MSM_RPM_ID_INVALIDATE_0 + i; + r[i].value = sel_masks[i]; + } + + memset(sel_masks, 0, sizeof(sel_masks)); + sel_masks[msm_rpm_get_sel_mask_reg(msm_rpm_data.sel_invalidate)] |= + msm_rpm_get_sel_mask(msm_rpm_data.sel_invalidate); + + if (noirq) { + unsigned long flags; + + spin_lock_irqsave(&msm_rpm_lock, flags); + rc = msm_rpm_set_exclusive_noirq(ctx, sel_masks, r, + ARRAY_SIZE(r)); + spin_unlock_irqrestore(&msm_rpm_lock, flags); + BUG_ON(rc); + } else { + mutex_lock(&msm_rpm_mutex); + rc = msm_rpm_set_exclusive(ctx, sel_masks, r, ARRAY_SIZE(r)); + mutex_unlock(&msm_rpm_mutex); + BUG_ON(rc); + } + +clear_common_exit: + return rc; +} + +/* + * Note: assumes caller has acquired . + */ +static void msm_rpm_update_notification(uint32_t ctx, + struct msm_rpm_notif_config *curr_cfg, + struct msm_rpm_notif_config *new_cfg) +{ + unsigned int sel_notif = msm_rpm_data.sel_notification; + + if (memcmp(curr_cfg, new_cfg, sizeof(*new_cfg))) { + uint32_t sel_masks[SEL_MASK_SIZE] = {}; + int rc; + + sel_masks[msm_rpm_get_sel_mask_reg(sel_notif)] + |= msm_rpm_get_sel_mask(sel_notif); + + rc = msm_rpm_set_exclusive(ctx, + sel_masks, new_cfg->iv, ARRAY_SIZE(new_cfg->iv)); + BUG_ON(rc); + + memcpy(curr_cfg, new_cfg, sizeof(*new_cfg)); + } +} + +/* + * Note: assumes caller has acquired . + */ +static void msm_rpm_initialize_notification(void) +{ + struct msm_rpm_notif_config cfg; + unsigned int ctx; + int i; + + for (ctx = MSM_RPM_CTX_SET_0; ctx <= MSM_RPM_CTX_SET_SLEEP; ctx++) { + cfg = msm_rpm_notif_cfgs[ctx]; + + for (i = 0; i < msm_rpm_sel_mask_size; i++) { + configured_iv(&cfg)[i].id = + MSM_RPM_ID_NOTIFICATION_CONFIGURED_0 + i; + configured_iv(&cfg)[i].value = ~0UL; + + registered_iv(&cfg)[i].id = + MSM_RPM_ID_NOTIFICATION_REGISTERED_0 + i; + registered_iv(&cfg)[i].value = 0; + } + + msm_rpm_update_notification(ctx, + &msm_rpm_notif_cfgs[ctx], &cfg); + } +} + +/****************************************************************************** + * Public functions + *****************************************************************************/ + +int msm_rpm_local_request_is_outstanding(void) +{ + unsigned long flags; + int outstanding = 0; + + if (!spin_trylock_irqsave(&msm_rpm_lock, flags)) + goto local_request_is_outstanding_exit; + + if (!spin_trylock(&msm_rpm_irq_lock)) + goto local_request_is_outstanding_unlock; + + outstanding = (msm_rpm_request != NULL); + spin_unlock(&msm_rpm_irq_lock); + +local_request_is_outstanding_unlock: + spin_unlock_irqrestore(&msm_rpm_lock, flags); + +local_request_is_outstanding_exit: + return outstanding; +} + +/* + * Read the specified status registers and return their values. + * + * status: array of id-value pairs. Each specifies a status register, + * i.e, one of MSM_RPM_STATUS_ID_xxxx. Upon return, each will + * contain the value of the status register. + * count: number of id-value pairs in the array + * + * Return value: + * 0: success + * -EBUSY: RPM is updating the status page; values across different registers + * may not be consistent + * -EINVAL: invalid id in array + * -ENODEV: RPM driver not initialized + */ +int msm_rpm_get_status(struct msm_rpm_iv_pair *status, int count) +{ + uint32_t seq_begin; + uint32_t seq_end; + int rc; + int i; + + seq_begin = msm_rpm_read(MSM_RPM_PAGE_STATUS, + target_status(MSM_RPM_STATUS_ID_SEQUENCE)); + + for (i = 0; i < count; i++) { + int target_status_id; + + if (status[i].id >= MSM_RPM_STATUS_ID_LAST) { + pr_err("%s(): Status ID beyond limits\n", __func__); + rc = -EINVAL; + goto get_status_exit; + } + + target_status_id = target_status(status[i].id); + if (target_status_id >= MSM_RPM_STATUS_ID_LAST) { + pr_err("%s(): Status id %d not defined for target\n", + __func__, + target_status_id); + rc = -EINVAL; + goto get_status_exit; + } + + status[i].value = msm_rpm_read(MSM_RPM_PAGE_STATUS, + target_status_id); + } + + seq_end = msm_rpm_read(MSM_RPM_PAGE_STATUS, + target_status(MSM_RPM_STATUS_ID_SEQUENCE)); + + rc = (seq_begin != seq_end || (seq_begin & 0x01)) ? -EBUSY : 0; + +get_status_exit: + return rc; +} +EXPORT_SYMBOL(msm_rpm_get_status); + +/* + * Issue a resource request to RPM to set resource values. + * + * Note: the function may sleep and must be called in a task context. + * + * ctx: the request's context. + * There two contexts that a RPM driver client can use: + * MSM_RPM_CTX_SET_0 and MSM_RPM_CTX_SET_SLEEP. For resource values + * that are intended to take effect when the CPU is active, + * MSM_RPM_CTX_SET_0 should be used. For resource values that are + * intended to take effect when the CPU is not active, + * MSM_RPM_CTX_SET_SLEEP should be used. + * req: array of id-value pairs. Each specifies a RPM resource, + * i.e, one of MSM_RPM_ID_xxxx. Each specifies the requested + * resource value. + * count: number of id-value pairs in the array + * + * Return value: + * 0: success + * -EINVAL: invalid or invalid id in array + * -ENOSPC: request rejected + * -ENODEV: RPM driver not initialized + */ +int msm_rpm_set(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + return msm_rpm_set_common(ctx, req, count, false); +} +EXPORT_SYMBOL(msm_rpm_set); + +/* + * Issue a resource request to RPM to set resource values. + * + * Note: the function is similar to msm_rpm_set() except that it must be + * called with interrupts masked. If possible, use msm_rpm_set() + * instead, to maximize CPU throughput. + */ +int msm_rpm_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + WARN(!irqs_disabled(), "msm_rpm_set_noirq can only be called " + "safely when local irqs are disabled. Consider using " + "msm_rpm_set or msm_rpm_set_nosleep instead."); + return msm_rpm_set_common(ctx, req, count, true); +} +EXPORT_SYMBOL(msm_rpm_set_noirq); + +/* + * Issue a resource request to RPM to clear resource values. Once the + * values are cleared, the resources revert back to their default values + * for this RPM master. + * + * Note: the function may sleep and must be called in a task context. + * + * ctx: the request's context. + * req: array of id-value pairs. Each specifies a RPM resource, + * i.e, one of MSM_RPM_ID_xxxx. 's are ignored. + * count: number of id-value pairs in the array + * + * Return value: + * 0: success + * -EINVAL: invalid or invalid id in array + */ +int msm_rpm_clear(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + return msm_rpm_clear_common(ctx, req, count, false); +} +EXPORT_SYMBOL(msm_rpm_clear); + +/* + * Issue a resource request to RPM to clear resource values. + * + * Note: the function is similar to msm_rpm_clear() except that it must be + * called with interrupts masked. If possible, use msm_rpm_clear() + * instead, to maximize CPU throughput. + */ +int msm_rpm_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + WARN(!irqs_disabled(), "msm_rpm_clear_noirq can only be called " + "safely when local irqs are disabled. Consider using " + "msm_rpm_clear or msm_rpm_clear_nosleep instead."); + return msm_rpm_clear_common(ctx, req, count, true); +} +EXPORT_SYMBOL(msm_rpm_clear_noirq); + +/* + * Register for RPM notification. When the specified resources + * change their status on RPM, RPM sends out notifications and the + * driver will "up" the semaphore in struct msm_rpm_notification. + * + * Note: the function may sleep and must be called in a task context. + * + * Memory for must not be freed until the notification is + * unregistered. Memory for can be freed after this + * function returns. + * + * n: the notifcation object. Caller should initialize only the + * semaphore field. When a notification arrives later, the + * semaphore will be "up"ed. + * req: array of id-value pairs. Each specifies a status register, + * i.e, one of MSM_RPM_STATUS_ID_xxxx. 's are ignored. + * count: number of id-value pairs in the array + * + * Return value: + * 0: success + * -EINVAL: invalid id in array + * -ENODEV: RPM driver not initialized + */ +int msm_rpm_register_notification(struct msm_rpm_notification *n, + struct msm_rpm_iv_pair *req, int count) +{ + unsigned long flags; + unsigned int ctx; + struct msm_rpm_notif_config cfg; + int rc; + int i; + + INIT_LIST_HEAD(&n->list); + rc = msm_rpm_fill_sel_masks(n->sel_masks, req, count); + if (rc) + goto register_notification_exit; + + mutex_lock(&msm_rpm_mutex); + + if (!msm_rpm_init_notif_done) { + msm_rpm_initialize_notification(); + msm_rpm_init_notif_done = true; + } + + spin_lock_irqsave(&msm_rpm_irq_lock, flags); + list_add(&n->list, &msm_rpm_notifications); + spin_unlock_irqrestore(&msm_rpm_irq_lock, flags); + + ctx = MSM_RPM_CTX_SET_0; + cfg = msm_rpm_notif_cfgs[ctx]; + + for (i = 0; i < msm_rpm_sel_mask_size; i++) + registered_iv(&cfg)[i].value |= n->sel_masks[i]; + + msm_rpm_update_notification(ctx, &msm_rpm_notif_cfgs[ctx], &cfg); + mutex_unlock(&msm_rpm_mutex); + +register_notification_exit: + return rc; +} +EXPORT_SYMBOL(msm_rpm_register_notification); + +/* + * Unregister a notification. + * + * Note: the function may sleep and must be called in a task context. + * + * n: the notifcation object that was registered previously. + * + * Return value: + * 0: success + * -ENODEV: RPM driver not initialized + */ +int msm_rpm_unregister_notification(struct msm_rpm_notification *n) +{ + unsigned long flags; + unsigned int ctx; + struct msm_rpm_notif_config cfg; + int rc = 0; + int i; + + mutex_lock(&msm_rpm_mutex); + ctx = MSM_RPM_CTX_SET_0; + cfg = msm_rpm_notif_cfgs[ctx]; + + for (i = 0; i < msm_rpm_sel_mask_size; i++) + registered_iv(&cfg)[i].value = 0; + + spin_lock_irqsave(&msm_rpm_irq_lock, flags); + list_del(&n->list); + list_for_each_entry(n, &msm_rpm_notifications, list) + for (i = 0; i < msm_rpm_sel_mask_size; i++) + registered_iv(&cfg)[i].value |= n->sel_masks[i]; + spin_unlock_irqrestore(&msm_rpm_irq_lock, flags); + + msm_rpm_update_notification(ctx, &msm_rpm_notif_cfgs[ctx], &cfg); + mutex_unlock(&msm_rpm_mutex); + + return rc; +} +EXPORT_SYMBOL(msm_rpm_unregister_notification); + +static uint32_t fw_major, fw_minor, fw_build; + +static ssize_t driver_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u.%u.%u\n", + msm_rpm_data.ver[0], msm_rpm_data.ver[1], msm_rpm_data.ver[2]); +} + +static ssize_t fw_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u.%u.%u\n", + fw_major, fw_minor, fw_build); +} + +static struct kobj_attribute driver_version_attr = __ATTR_RO(driver_version); +static struct kobj_attribute fw_version_attr = __ATTR_RO(fw_version); + +static struct attribute *driver_attributes[] = { + &driver_version_attr.attr, + &fw_version_attr.attr, + NULL +}; + +static struct attribute_group driver_attr_group = { + .attrs = driver_attributes, +}; + +static int __devinit msm_rpm_probe(struct platform_device *pdev) +{ + return sysfs_create_group(&pdev->dev.kobj, &driver_attr_group); +} + +static int __devexit msm_rpm_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &driver_attr_group); + return 0; +} + +static struct platform_driver msm_rpm_platform_driver = { + .probe = msm_rpm_probe, + .remove = __devexit_p(msm_rpm_remove), + .driver = { + .name = "msm_rpm", + .owner = THIS_MODULE, + }, +}; + +static void __init msm_rpm_populate_map(struct msm_rpm_platform_data *data) +{ + int i, j; + struct msm_rpm_map_data *src = NULL; + struct msm_rpm_map_data *dst = NULL; + + for (i = 0; i < MSM_RPM_ID_LAST;) { + src = &data->target_id[i]; + dst = &msm_rpm_data.target_id[i]; + + dst->id = MSM_RPM_ID_LAST; + dst->sel = msm_rpm_data.sel_last + 1; + + /* + * copy the target specific id of the current and also of + * all the #count id's that follow the current. + * [MSM_RPM_ID_PM8921_S1_0] = { MSM_RPM_8960_ID_PM8921_S1_0, + * MSM_RPM_8960_SEL_PM8921_S1, + * 2}, + * [MSM_RPM_ID_PM8921_S1_1] = { 0, 0, 0 }, + * should translate to + * [MSM_RPM_ID_PM8921_S1_0] = { MSM_RPM_8960_ID_PM8921_S1_0, + * MSM_RPM_8960_SEL_PM8921, + * 2 }, + * [MSM_RPM_ID_PM8921_S1_1] = { MSM_RPM_8960_ID_PM8921_S1_0 + 1, + * MSM_RPM_8960_SEL_PM8921, + * 0 }, + */ + for (j = 0; j < src->count; j++) { + dst = &msm_rpm_data.target_id[i + j]; + dst->id = src->id + j; + dst->sel = src->sel; + } + + i += (src->count) ? src->count : 1; + } + + for (i = 0; i < MSM_RPM_STATUS_ID_LAST; i++) { + if (data->target_status[i] & MSM_RPM_STATUS_ID_VALID) + msm_rpm_data.target_status[i] &= + ~MSM_RPM_STATUS_ID_VALID; + else + msm_rpm_data.target_status[i] = MSM_RPM_STATUS_ID_LAST; + } +} + +static irqreturn_t msm_pm_rpm_wakeup_interrupt(int irq, void *dev_id) +{ + if (dev_id != &msm_pm_rpm_wakeup_interrupt) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +int __init msm_rpm_init(struct msm_rpm_platform_data *data) +{ + int rc; + + memcpy(&msm_rpm_data, data, sizeof(struct msm_rpm_platform_data)); + msm_rpm_sel_mask_size = msm_rpm_data.sel_last / 32 + 1; + BUG_ON(SEL_MASK_SIZE < msm_rpm_sel_mask_size); + + fw_major = msm_rpm_read(MSM_RPM_PAGE_STATUS, + target_status(MSM_RPM_STATUS_ID_VERSION_MAJOR)); + fw_minor = msm_rpm_read(MSM_RPM_PAGE_STATUS, + target_status(MSM_RPM_STATUS_ID_VERSION_MINOR)); + fw_build = msm_rpm_read(MSM_RPM_PAGE_STATUS, + target_status(MSM_RPM_STATUS_ID_VERSION_BUILD)); + pr_info("%s: RPM firmware %u.%u.%u\n", __func__, + fw_major, fw_minor, fw_build); + + if (fw_major != msm_rpm_data.ver[0]) { + pr_err("%s: RPM version %u.%u.%u incompatible with " + "this driver version %u.%u.%u\n", __func__, + fw_major, fw_minor, fw_build, + msm_rpm_data.ver[0], + msm_rpm_data.ver[1], + msm_rpm_data.ver[2]); + return -EFAULT; + } + + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_VERSION_MAJOR), msm_rpm_data.ver[0]); + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_VERSION_MINOR), msm_rpm_data.ver[1]); + msm_rpm_write(MSM_RPM_PAGE_CTRL, + target_ctrl(MSM_RPM_CTRL_VERSION_BUILD), msm_rpm_data.ver[2]); + + rc = request_irq(data->irq_ack, msm_rpm_ack_interrupt, + IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, + "rpm_drv", msm_rpm_ack_interrupt); + if (rc) { + pr_err("%s: failed to request irq %d: %d\n", + __func__, data->irq_ack, rc); + return rc; + } + + rc = irq_set_irq_wake(data->irq_ack, 1); + if (rc) { + pr_err("%s: failed to set wakeup irq %u: %d\n", + __func__, data->irq_ack, rc); + return rc; + } + + rc = request_irq(data->irq_err, msm_rpm_err_interrupt, + IRQF_TRIGGER_RISING, "rpm_err", NULL); + if (rc) { + pr_err("%s: failed to request error interrupt: %d\n", + __func__, rc); + return rc; + } + + rc = request_irq(data->irq_wakeup, + msm_pm_rpm_wakeup_interrupt, IRQF_TRIGGER_RISING, + "pm_drv", msm_pm_rpm_wakeup_interrupt); + if (rc) { + pr_err("%s: failed to request irq %u: %d\n", + __func__, data->irq_wakeup, rc); + return rc; + } + + rc = irq_set_irq_wake(data->irq_wakeup, 1); + if (rc) { + pr_err("%s: failed to set wakeup irq %u: %d\n", + __func__, data->irq_wakeup, rc); + return rc; + } + + msm_rpm_populate_map(data); + + return platform_driver_register(&msm_rpm_platform_driver); +} diff --git a/arch/arm/mach-msm/rpm_log.c b/arch/arm/mach-msm/rpm_log.c new file mode 100644 index 00000000000..3ed55da2848 --- /dev/null +++ b/arch/arm/mach-msm/rpm_log.c @@ -0,0 +1,363 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "rpm_log.h" + +/* registers in MSM_RPM_LOG_PAGE_INDICES */ +enum { + MSM_RPM_LOG_TAIL, + MSM_RPM_LOG_HEAD +}; + +/* used to 4 byte align message lengths */ +#define PADDED_LENGTH(x) (0xFFFFFFFC & ((x) + 3)) + +/* calculates the character string length of a message of byte length x */ +#define PRINTED_LENGTH(x) ((x) * 6 + 3) + +/* number of ms to wait between checking for new messages in the RPM log */ +#define RECHECK_TIME (50) + +struct msm_rpm_log_buffer { + char *data; + u32 len; + u32 pos; + u32 max_len; + u32 read_idx; + struct msm_rpm_log_platform_data *pdata; +}; + +/****************************************************************************** + * Internal functions + *****************************************************************************/ + +static inline u32 +msm_rpm_log_read(const struct msm_rpm_log_platform_data *pdata, u32 page, + u32 reg) +{ + return readl_relaxed(pdata->reg_base + pdata->reg_offsets[page] + + reg * 4); +} + +/* + * msm_rpm_log_copy() - Copies messages from a volatile circular buffer in + * the RPM's shared memory into a private local buffer + * msg_buffer: pointer to local buffer (string) + * buf_len: length of local buffer in bytes + * read_start_idx: index into shared memory buffer + * + * Return value: number of bytes written to the local buffer + * + * Copies messages stored in a circular buffer in the RPM Message Memory into + * a specified local buffer. The RPM processor is unaware of these reading + * efforts, so care is taken to make sure that messages are valid both before + * and after reading. The RPM processor utilizes a ULog driver to write the + * log. The RPM processor maintains tail and head indices. These correspond + * to the next byte to write into, and the first valid byte, respectively. + * Both indices increase monotonically (except for rollover). + * + * Messages take the form of [(u32)length] [(char)data0,1,...] in which the + * length specifies the number of payload bytes. Messages must be 4 byte + * aligned, so padding is added at the end of a message as needed. + * + * Print format: + * - 0xXX, 0xXX, 0xXX + * - 0xXX + * etc... + */ +static u32 msm_rpm_log_copy(const struct msm_rpm_log_platform_data *pdata, + char *msg_buffer, u32 buf_len, u32 *read_idx) +{ + u32 head_idx, tail_idx; + u32 pos = 0; + u32 i = 0; + u32 msg_len; + u32 pos_start; + char temp[4]; + + tail_idx = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_INDICES, + MSM_RPM_LOG_TAIL); + head_idx = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_INDICES, + MSM_RPM_LOG_HEAD); + + /* loop while the remote buffer has valid messages left to read */ + while (tail_idx - head_idx > 0 && tail_idx - *read_idx > 0) { + head_idx = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_INDICES, + MSM_RPM_LOG_HEAD); + /* check if the message to be read is valid */ + if (tail_idx - *read_idx > tail_idx - head_idx) { + *read_idx = head_idx; + continue; + } + /* + * Ensure that the reported buffer size is within limits of + * known maximum size and that all indices are 4 byte aligned. + * These conditions are required to interact with a ULog buffer + * properly. + */ + if (tail_idx - head_idx > pdata->log_len || + !IS_ALIGNED((tail_idx | head_idx | *read_idx), 4)) + break; + + msg_len = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_BUFFER, + (*read_idx >> 2) & pdata->log_len_mask); + + /* handle messages that claim to be longer than the log */ + if (PADDED_LENGTH(msg_len) > tail_idx - *read_idx - 4) + msg_len = tail_idx - *read_idx - 4; + + /* check that the local buffer has enough space for this msg */ + if (pos + PRINTED_LENGTH(msg_len) > buf_len) + break; + + pos_start = pos; + pos += scnprintf(msg_buffer + pos, buf_len - pos, "- "); + + /* copy message payload to local buffer */ + for (i = 0; i < msg_len; i++) { + /* read from shared memory 4 bytes at a time */ + if (IS_ALIGNED(i, 4)) + *((u32 *)temp) = msm_rpm_log_read(pdata, + MSM_RPM_LOG_PAGE_BUFFER, + ((*read_idx + 4 + i) >> 2) & + pdata->log_len_mask); + + pos += scnprintf(msg_buffer + pos, buf_len - pos, + "0x%02X, ", temp[i & 0x03]); + } + + pos += scnprintf(msg_buffer + pos, buf_len - pos, "\n"); + + head_idx = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_INDICES, + MSM_RPM_LOG_HEAD); + + /* roll back if message that was read is not still valid */ + if (tail_idx - *read_idx > tail_idx - head_idx) + pos = pos_start; + + *read_idx += PADDED_LENGTH(msg_len) + 4; + } + + return pos; +} + + +/* + * msm_rpm_log_file_read() - Reads in log buffer messages then outputs them to a + * user buffer + * + * Return value: + * 0: success + * -ENOMEM: no memory available + * -EINVAL: user buffer null or requested bytes 0 + * -EFAULT: user buffer not writeable + * -EAGAIN: no bytes available at the moment + */ +static ssize_t msm_rpm_log_file_read(struct file *file, char __user *bufu, + size_t count, loff_t *ppos) +{ + u32 out_len, remaining; + struct msm_rpm_log_platform_data *pdata; + struct msm_rpm_log_buffer *buf; + + buf = file->private_data; + pdata = buf->pdata; + if (!pdata) + return -EINVAL; + if (!buf) + return -ENOMEM; + if (!buf->data) + return -ENOMEM; + if (!bufu || count < 0) + return -EINVAL; + if (!access_ok(VERIFY_WRITE, bufu, count)) + return -EFAULT; + + /* check for more messages if local buffer empty */ + if (buf->pos == buf->len) { + buf->pos = 0; + buf->len = msm_rpm_log_copy(pdata, buf->data, buf->max_len, + &(buf->read_idx)); + } + + if ((file->f_flags & O_NONBLOCK) && buf->len == 0) + return -EAGAIN; + + /* loop until new messages arrive */ + while (buf->len == 0) { + cond_resched(); + if (msleep_interruptible(RECHECK_TIME)) + break; + buf->len = msm_rpm_log_copy(pdata, buf->data, buf->max_len, + &(buf->read_idx)); + } + + out_len = ((buf->len - buf->pos) < count ? buf->len - buf->pos : count); + + remaining = __copy_to_user(bufu, &(buf->data[buf->pos]), out_len); + buf->pos += out_len - remaining; + + return out_len - remaining; +} + + +/* + * msm_rpm_log_file_open() - Allows a new reader to open the RPM log virtual + * file + * + * One local buffer is kmalloc'ed for each reader, so no resource sharing has + * to take place (besides the read only access to the RPM log buffer). + * + * Return value: + * 0: success + * -ENOMEM: no memory available + */ +static int msm_rpm_log_file_open(struct inode *inode, struct file *file) +{ + struct msm_rpm_log_buffer *buf; + struct msm_rpm_log_platform_data *pdata; + + pdata = inode->i_private; + if (!pdata) + return -EINVAL; + + file->private_data = + kmalloc(sizeof(struct msm_rpm_log_buffer), GFP_KERNEL); + if (!file->private_data) { + pr_err("%s: ERROR kmalloc failed to allocate %d bytes\n", + __func__, sizeof(struct msm_rpm_log_buffer)); + return -ENOMEM; + } + buf = file->private_data; + + buf->data = kmalloc(PRINTED_LENGTH(pdata->log_len), GFP_KERNEL); + if (!buf->data) { + kfree(file->private_data); + file->private_data = NULL; + pr_err("%s: ERROR kmalloc failed to allocate %d bytes\n", + __func__, PRINTED_LENGTH(pdata->log_len)); + return -ENOMEM; + } + + buf->pdata = pdata; + buf->len = 0; + buf->pos = 0; + buf->max_len = PRINTED_LENGTH(pdata->log_len); + buf->read_idx = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_INDICES, + MSM_RPM_LOG_HEAD); + return 0; +} + +static int msm_rpm_log_file_close(struct inode *inode, struct file *file) +{ + kfree(((struct msm_rpm_log_buffer *)file->private_data)->data); + kfree(file->private_data); + return 0; +} + + +static const struct file_operations msm_rpm_log_file_fops = { + .owner = THIS_MODULE, + .open = msm_rpm_log_file_open, + .read = msm_rpm_log_file_read, + .release = msm_rpm_log_file_close, +}; + +static int __devinit msm_rpm_log_probe(struct platform_device *pdev) +{ + struct dentry *dent; + struct msm_rpm_log_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + + pdata->reg_base = ioremap(pdata->phys_addr_base, pdata->phys_size); + if (!pdata->reg_base) { + pr_err("%s: ERROR could not ioremap: start=%p, len=%u\n", + __func__, (void *) pdata->phys_addr_base, + pdata->phys_size); + return -EBUSY; + } + + dent = debugfs_create_file("rpm_log", S_IRUGO, NULL, + pdev->dev.platform_data, &msm_rpm_log_file_fops); + if (!dent) { + pr_err("%s: ERROR debugfs_create_file failed\n", __func__); + return -ENOMEM; + } + + platform_set_drvdata(pdev, dent); + + pr_notice("%s: OK\n", __func__); + return 0; +} + +static int __devexit msm_rpm_log_remove(struct platform_device *pdev) +{ + struct dentry *dent; + struct msm_rpm_log_platform_data *pdata; + + pdata = pdev->dev.platform_data; + + iounmap(pdata->reg_base); + + dent = platform_get_drvdata(pdev); + debugfs_remove(dent); + platform_set_drvdata(pdev, NULL); + + pr_notice("%s: OK\n", __func__); + return 0; +} + +static struct platform_driver msm_rpm_log_driver = { + .probe = msm_rpm_log_probe, + .remove = __devexit_p(msm_rpm_log_remove), + .driver = { + .name = "msm_rpm_log", + .owner = THIS_MODULE, + }, +}; + +static int __init msm_rpm_log_init(void) +{ + return platform_driver_register(&msm_rpm_log_driver); +} + +static void __exit msm_rpm_log_exit(void) +{ + platform_driver_unregister(&msm_rpm_log_driver); +} + +module_init(msm_rpm_log_init); +module_exit(msm_rpm_log_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPM Log driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:msm_rpm_log"); diff --git a/arch/arm/mach-msm/rpm_log.h b/arch/arm/mach-msm/rpm_log.h new file mode 100644 index 00000000000..37349b60f0a --- /dev/null +++ b/arch/arm/mach-msm/rpm_log.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_LOG_H +#define __ARCH_ARM_MACH_MSM_RPM_LOG_H + +#include + +enum { + MSM_RPM_LOG_PAGE_INDICES, + MSM_RPM_LOG_PAGE_BUFFER, + MSM_RPM_LOG_PAGE_COUNT +}; + +struct msm_rpm_log_platform_data { + u32 reg_offsets[MSM_RPM_LOG_PAGE_COUNT]; + u32 log_len; + u32 log_len_mask; + phys_addr_t phys_addr_base; + u32 phys_size; + void __iomem *reg_base; +}; + +#endif /* __ARCH_ARM_MACH_MSM_RPM_LOG_H */ diff --git a/arch/arm/mach-msm/rpm_resources.c b/arch/arm/mach-msm/rpm_resources.c new file mode 100644 index 00000000000..c86da6a7347 --- /dev/null +++ b/arch/arm/mach-msm/rpm_resources.c @@ -0,0 +1,1129 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rpm_resources.h" +#include "spm.h" +#include "idle.h" + +/****************************************************************************** + * Debug Definitions + *****************************************************************************/ + +enum { + MSM_RPMRS_DEBUG_OUTPUT = BIT(0), + MSM_RPMRS_DEBUG_BUFFER = BIT(1), +}; + +static int msm_rpmrs_debug_mask; +module_param_named( + debug_mask, msm_rpmrs_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + +static struct msm_rpmrs_level *msm_rpmrs_levels; +static int msm_rpmrs_level_count; + +static bool msm_rpmrs_pxo_beyond_limits(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_aggregate_pxo(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_restore_pxo(void); +static bool msm_rpmrs_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_aggregate_l2_cache(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_restore_l2_cache(void); +static bool msm_rpmrs_vdd_mem_beyond_limits(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_aggregate_vdd_mem(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_restore_vdd_mem(void); +static bool msm_rpmrs_vdd_dig_beyond_limits(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_aggregate_vdd_dig(struct msm_rpmrs_limits *limits); +static void msm_rpmrs_restore_vdd_dig(void); + +static ssize_t msm_rpmrs_resource_attr_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t msm_rpmrs_resource_attr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); + +static int vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_LAST]; +static int vdd_mem_vlevels[MSM_RPMRS_VDD_MEM_LAST]; +static int vdd_mask; + +#define MSM_RPMRS_MAX_RS_REGISTER_COUNT 2 + +#define RPMRS_ATTR(_name) \ + __ATTR(_name, S_IRUGO|S_IWUSR, \ + msm_rpmrs_resource_attr_show, msm_rpmrs_resource_attr_store) + +struct msm_rpmrs_resource { + struct msm_rpm_iv_pair rs[MSM_RPMRS_MAX_RS_REGISTER_COUNT]; + uint32_t size; + char *name; + + uint32_t enable_low_power; + + bool (*beyond_limits)(struct msm_rpmrs_limits *limits); + void (*aggregate)(struct msm_rpmrs_limits *limits); + void (*restore)(void); + + struct kobj_attribute ko_attr; +}; + +static struct msm_rpmrs_resource msm_rpmrs_pxo = { + .size = 1, + .name = "pxo", + .beyond_limits = msm_rpmrs_pxo_beyond_limits, + .aggregate = msm_rpmrs_aggregate_pxo, + .restore = msm_rpmrs_restore_pxo, + .ko_attr = RPMRS_ATTR(pxo), +}; + +static struct msm_rpmrs_resource msm_rpmrs_l2_cache = { + .size = 1, + .name = "L2_cache", + .beyond_limits = msm_rpmrs_l2_cache_beyond_limits, + .aggregate = msm_rpmrs_aggregate_l2_cache, + .restore = msm_rpmrs_restore_l2_cache, + .ko_attr = RPMRS_ATTR(L2_cache), +}; + +static struct msm_rpmrs_resource msm_rpmrs_vdd_mem = { + .size = 2, + .name = "vdd_mem", + .beyond_limits = msm_rpmrs_vdd_mem_beyond_limits, + .aggregate = msm_rpmrs_aggregate_vdd_mem, + .restore = msm_rpmrs_restore_vdd_mem, + .ko_attr = RPMRS_ATTR(vdd_mem), +}; + +static struct msm_rpmrs_resource msm_rpmrs_vdd_dig = { + .size = 2, + .name = "vdd_dig", + .beyond_limits = msm_rpmrs_vdd_dig_beyond_limits, + .aggregate = msm_rpmrs_aggregate_vdd_dig, + .restore = msm_rpmrs_restore_vdd_dig, + .ko_attr = RPMRS_ATTR(vdd_dig), +}; + +static struct msm_rpmrs_resource msm_rpmrs_rpm_ctl = { + .size = 1, + .name = "rpm_ctl", + .beyond_limits = NULL, + .aggregate = NULL, + .restore = NULL, + .ko_attr = RPMRS_ATTR(rpm_ctl), +}; + +static struct msm_rpmrs_resource *msm_rpmrs_resources[] = { + &msm_rpmrs_pxo, + &msm_rpmrs_l2_cache, + &msm_rpmrs_vdd_mem, + &msm_rpmrs_vdd_dig, + &msm_rpmrs_rpm_ctl, +}; + +static uint32_t msm_rpmrs_buffer[MSM_RPM_ID_LAST]; +static DECLARE_BITMAP(msm_rpmrs_buffered, MSM_RPM_ID_LAST); +static DECLARE_BITMAP(msm_rpmrs_listed, MSM_RPM_ID_LAST); +static DEFINE_SPINLOCK(msm_rpmrs_lock); + +#define MSM_RPMRS_VDD(v) ((v) & (vdd_mask)) + +/****************************************************************************** + * Attribute Definitions + *****************************************************************************/ +static struct attribute *msm_rpmrs_attributes[] = { + &msm_rpmrs_pxo.ko_attr.attr, + &msm_rpmrs_l2_cache.ko_attr.attr, + &msm_rpmrs_vdd_mem.ko_attr.attr, + &msm_rpmrs_vdd_dig.ko_attr.attr, + NULL, +}; +static struct attribute *msm_rpmrs_mode_attributes[] = { + &msm_rpmrs_rpm_ctl.ko_attr.attr, + NULL, +}; + +static struct attribute_group msm_rpmrs_attribute_group = { + .attrs = msm_rpmrs_attributes, +}; + +static struct attribute_group msm_rpmrs_mode_attribute_group = { + .attrs = msm_rpmrs_mode_attributes, +}; + +#define GET_RS_FROM_ATTR(attr) \ + (container_of(attr, struct msm_rpmrs_resource, ko_attr)) + + +/****************************************************************************** + * Resource Specific Functions + *****************************************************************************/ + +static void msm_rpmrs_aggregate_sclk(uint32_t sclk_count) +{ + msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_TO] = 0; + set_bit(MSM_RPM_ID_TRIGGER_TIMED_TO, msm_rpmrs_buffered); + msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT] = sclk_count; + set_bit(MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, msm_rpmrs_buffered); +} + +static void msm_rpmrs_restore_sclk(void) +{ + clear_bit(MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT, msm_rpmrs_buffered); + msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_SCLK_COUNT] = 0; + clear_bit(MSM_RPM_ID_TRIGGER_TIMED_TO, msm_rpmrs_buffered); + msm_rpmrs_buffer[MSM_RPM_ID_TRIGGER_TIMED_TO] = 0; +} + +static bool msm_rpmrs_pxo_beyond_limits(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo; + uint32_t pxo; + + if (rs->enable_low_power && test_bit(rs->rs[0].id, msm_rpmrs_buffered)) + pxo = msm_rpmrs_buffer[rs->rs[0].id]; + else + pxo = MSM_RPMRS_PXO_ON; + + return pxo > limits->pxo; +} + +static void msm_rpmrs_aggregate_pxo(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo; + uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id]; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) { + rs->rs[0].value = *buf; + if (limits->pxo > *buf) + *buf = limits->pxo; + if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask) + pr_info("%s: %d (0x%x)\n", __func__, *buf, *buf); + } +} + +static void msm_rpmrs_restore_pxo(void) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_pxo; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) + msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value; +} + +static bool msm_rpmrs_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache; + uint32_t l2_cache; + + if (rs->enable_low_power && test_bit(rs->rs[0].id, msm_rpmrs_buffered)) + l2_cache = msm_rpmrs_buffer[rs->rs[0].id]; + else + l2_cache = MSM_RPMRS_L2_CACHE_ACTIVE; + + return l2_cache > limits->l2_cache; +} + +static void msm_rpmrs_aggregate_l2_cache(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache; + uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id]; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) { + rs->rs[0].value = *buf; + if (limits->l2_cache > *buf) + *buf = limits->l2_cache; + + if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask) + pr_info("%s: %d (0x%x)\n", __func__, *buf, *buf); + } +} + +static bool msm_spm_l2_cache_beyond_limits(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache; + uint32_t l2_cache = rs->rs[0].value; + + if (!rs->enable_low_power) + l2_cache = MSM_RPMRS_L2_CACHE_ACTIVE; + + return l2_cache > limits->l2_cache; +} + +static void msm_rpmrs_restore_l2_cache(void) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_l2_cache; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) + msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value; +} + +static bool msm_rpmrs_vdd_mem_beyond_limits(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem; + uint32_t vdd_mem; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) { + uint32_t buffered_value = msm_rpmrs_buffer[rs->rs[0].id]; + + if (rs->enable_low_power == 0) + vdd_mem = vdd_mem_vlevels[MSM_RPMRS_VDD_MEM_ACTIVE]; + else if (rs->enable_low_power == 1) + vdd_mem = vdd_mem_vlevels[MSM_RPMRS_VDD_MEM_RET_HIGH]; + else + vdd_mem = vdd_mem_vlevels[MSM_RPMRS_VDD_MEM_RET_LOW]; + + if (MSM_RPMRS_VDD(buffered_value) > MSM_RPMRS_VDD(vdd_mem)) + vdd_mem = MSM_RPMRS_VDD(buffered_value); + } else { + vdd_mem = vdd_mem_vlevels[MSM_RPMRS_VDD_MEM_ACTIVE]; + } + + return vdd_mem > vdd_mem_vlevels[limits->vdd_mem_upper_bound]; +} + +static void msm_rpmrs_aggregate_vdd_mem(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem; + uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id]; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) { + rs->rs[0].value = *buf; + if (vdd_mem_vlevels[limits->vdd_mem] > MSM_RPMRS_VDD(*buf)) { + *buf &= ~vdd_mask; + *buf |= vdd_mem_vlevels[limits->vdd_mem]; + } + + if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask) + pr_info("%s: vdd %d (0x%x)\n", __func__, + MSM_RPMRS_VDD(*buf), MSM_RPMRS_VDD(*buf)); + } +} + +static void msm_rpmrs_restore_vdd_mem(void) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_mem; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) + msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value; +} + +static bool msm_rpmrs_vdd_dig_beyond_limits(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig; + uint32_t vdd_dig; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) { + uint32_t buffered_value = msm_rpmrs_buffer[rs->rs[0].id]; + + if (rs->enable_low_power == 0) + vdd_dig = vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_ACTIVE]; + else if (rs->enable_low_power == 1) + vdd_dig = vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_RET_HIGH]; + else + vdd_dig = vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_RET_LOW]; + + if (MSM_RPMRS_VDD(buffered_value) > MSM_RPMRS_VDD(vdd_dig)) + vdd_dig = MSM_RPMRS_VDD(buffered_value); + } else { + vdd_dig = vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_ACTIVE]; + } + + return vdd_dig > vdd_dig_vlevels[limits->vdd_dig_upper_bound]; +} + +static void msm_rpmrs_aggregate_vdd_dig(struct msm_rpmrs_limits *limits) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig; + uint32_t *buf = &msm_rpmrs_buffer[rs->rs[0].id]; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) { + rs->rs[0].value = *buf; + if (vdd_dig_vlevels[limits->vdd_dig] > MSM_RPMRS_VDD(*buf)) { + *buf &= ~vdd_mask; + *buf |= vdd_dig_vlevels[limits->vdd_dig]; + } + + + if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask) + pr_info("%s: vdd %d (0x%x)\n", __func__, + MSM_RPMRS_VDD(*buf), MSM_RPMRS_VDD(*buf)); + } +} + +static void msm_rpmrs_restore_vdd_dig(void) +{ + struct msm_rpmrs_resource *rs = &msm_rpmrs_vdd_dig; + + if (test_bit(rs->rs[0].id, msm_rpmrs_buffered)) + msm_rpmrs_buffer[rs->rs[0].id] = rs->rs[0].value; +} + +/****************************************************************************** + * Buffering Functions + *****************************************************************************/ + +static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits, + bool irqs_detect, bool gpio_detect) +{ + + if (vdd_dig_vlevels[limits->vdd_dig_upper_bound] <= + vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_RET_HIGH]) + return irqs_detect; + + if (limits->pxo == MSM_RPMRS_PXO_OFF) + return gpio_detect; + + return true; +} + +static bool msm_rpmrs_use_mpm(struct msm_rpmrs_limits *limits) +{ + return (limits->pxo == MSM_RPMRS_PXO_OFF) || + (vdd_dig_vlevels[limits->vdd_dig] <= + vdd_dig_vlevels[MSM_RPMRS_VDD_DIG_RET_HIGH]); +} + +static void msm_rpmrs_update_levels(void) +{ + int i, k; + + for (i = 0; i < msm_rpmrs_level_count; i++) { + struct msm_rpmrs_level *level = &msm_rpmrs_levels[i]; + + if (level->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE) + continue; + + level->available = true; + + for (k = 0; k < ARRAY_SIZE(msm_rpmrs_resources); k++) { + struct msm_rpmrs_resource *rs = msm_rpmrs_resources[k]; + + if (rs->beyond_limits && + rs->beyond_limits(&level->rs_limits)) { + level->available = false; + break; + } + } + + } +} + +/* + * Return value: + * 0: no entries in is on our resource list + * 1: one or more entries in is on our resource list + * -EINVAL: invalid id in array + */ +static int msm_rpmrs_buffer_request(struct msm_rpm_iv_pair *req, int count) +{ + bool listed; + int i; + + for (i = 0; i < count; i++) + if (req[i].id >= MSM_RPM_ID_LAST) + return -EINVAL; + + for (i = 0, listed = false; i < count; i++) { + msm_rpmrs_buffer[req[i].id] = req[i].value; + set_bit(req[i].id, msm_rpmrs_buffered); + + if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask) + pr_info("%s: reg %d: 0x%x\n", + __func__, req[i].id, req[i].value); + + if (listed) + continue; + + if (test_bit(req[i].id, msm_rpmrs_listed)) + listed = true; + } + + return listed ? 1 : 0; +} + +/* + * Return value: + * 0: no entries in is on our resource list + * 1: one or more entries in is on our resource list + * -EINVAL: invalid id in array + */ +static int msm_rpmrs_clear_buffer(struct msm_rpm_iv_pair *req, int count) +{ + bool listed; + int i; + + for (i = 0; i < count; i++) + if (req[i].id >= MSM_RPM_ID_LAST) + return -EINVAL; + + for (i = 0, listed = false; i < count; i++) { + msm_rpmrs_buffer[req[i].id] = 0; + clear_bit(req[i].id, msm_rpmrs_buffered); + + if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask) + pr_info("%s: reg %d\n", __func__, req[i].id); + + if (listed) + continue; + + if (test_bit(req[i].id, msm_rpmrs_listed)) + listed = true; + } + + return listed ? 1 : 0; +} + +#ifdef CONFIG_MSM_L2_SPM +static int msm_rpmrs_flush_L2(struct msm_rpmrs_limits *limits, int notify_rpm) +{ + int rc = 0; + int lpm; + + switch (limits->l2_cache) { + case MSM_RPMRS_L2_CACHE_HSFS_OPEN: + lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE; + msm_pm_set_l2_flush_flag(1); + break; + case MSM_RPMRS_L2_CACHE_GDHS: + lpm = MSM_SPM_L2_MODE_GDHS; + break; + case MSM_RPMRS_L2_CACHE_RETENTION: + lpm = MSM_SPM_L2_MODE_RETENTION; + break; + default: + case MSM_RPMRS_L2_CACHE_ACTIVE: + lpm = MSM_SPM_L2_MODE_DISABLED; + break; + } + + rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm); + if (MSM_RPMRS_DEBUG_BUFFER & msm_rpmrs_debug_mask) + pr_info("%s: Requesting low power mode %d returned %d\n", + __func__, lpm, rc); + + return rc; +} +static void msm_rpmrs_L2_restore(struct msm_rpmrs_limits *limits, + bool notify_rpm, bool collapsed) +{ + msm_spm_l2_set_low_power_mode(MSM_SPM_MODE_DISABLED, notify_rpm); + msm_pm_set_l2_flush_flag(0); +} +#else +static int msm_rpmrs_flush_L2(struct msm_rpmrs_limits *limits, int notify_rpm) +{ + return 0; +} +static void msm_rpmrs_L2_restore(struct msm_rpmrs_limits *limits, + bool notify_rpm, bool collapsed) +{ +} +#endif + +static int msm_rpmrs_flush_buffer( + uint32_t sclk_count, struct msm_rpmrs_limits *limits, int from_idle) +{ + struct msm_rpm_iv_pair *req; + int count; + int rc; + int i; + + msm_rpmrs_aggregate_sclk(sclk_count); + for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) { + if (msm_rpmrs_resources[i]->aggregate) + msm_rpmrs_resources[i]->aggregate(limits); + } + + count = bitmap_weight(msm_rpmrs_buffered, MSM_RPM_ID_LAST); + + req = kmalloc(sizeof(*req) * count, GFP_ATOMIC); + if (!req) { + rc = -ENOMEM; + goto flush_buffer_restore; + } + + count = 0; + i = find_first_bit(msm_rpmrs_buffered, MSM_RPM_ID_LAST); + + while (i < MSM_RPM_ID_LAST) { + if (MSM_RPMRS_DEBUG_OUTPUT & msm_rpmrs_debug_mask) + pr_info("%s: reg %d: 0x%x\n", + __func__, i, msm_rpmrs_buffer[i]); + + req[count].id = i; + req[count].value = msm_rpmrs_buffer[i]; + count++; + + i = find_next_bit(msm_rpmrs_buffered, MSM_RPM_ID_LAST, i + 1); + } + + rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_SLEEP, req, count); + kfree(req); + + if (rc) + goto flush_buffer_restore; + + bitmap_and(msm_rpmrs_buffered, + msm_rpmrs_buffered, msm_rpmrs_listed, MSM_RPM_ID_LAST); + +flush_buffer_restore: + for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) { + if (msm_rpmrs_resources[i]->restore) + msm_rpmrs_resources[i]->restore(); + } + msm_rpmrs_restore_sclk(); + + if (rc) + pr_err("%s: failed: %d\n", __func__, rc); + return rc; +} + +static int msm_rpmrs_set_common( + int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq) +{ + if (ctx == MSM_RPM_CTX_SET_SLEEP) { + unsigned long flags; + int rc; + + spin_lock_irqsave(&msm_rpmrs_lock, flags); + rc = msm_rpmrs_buffer_request(req, count); + if (rc > 0) { + msm_rpmrs_update_levels(); + rc = 0; + } + spin_unlock_irqrestore(&msm_rpmrs_lock, flags); + + return rc; + } + + if (noirq) + return msm_rpm_set_noirq(ctx, req, count); + else + return msm_rpm_set(ctx, req, count); +} + +static int msm_rpmrs_clear_common( + int ctx, struct msm_rpm_iv_pair *req, int count, bool noirq) +{ + if (ctx == MSM_RPM_CTX_SET_SLEEP) { + unsigned long flags; + int rc; + + spin_lock_irqsave(&msm_rpmrs_lock, flags); + rc = msm_rpmrs_clear_buffer(req, count); + if (rc > 0) { + msm_rpmrs_update_levels(); + rc = 0; + } + spin_unlock_irqrestore(&msm_rpmrs_lock, flags); + + if (rc < 0) + return rc; + } + + if (noirq) + return msm_rpm_clear_noirq(ctx, req, count); + else + return msm_rpm_clear(ctx, req, count); +} + +/****************************************************************************** + * Attribute Functions + *****************************************************************************/ + +static ssize_t msm_rpmrs_resource_attr_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct kernel_param kp; + unsigned long flags; + unsigned int temp; + int rc; + + spin_lock_irqsave(&msm_rpmrs_lock, flags); + /* special case active-set signal for MSM_RPMRS_ID_RPM_CTL */ + if (GET_RS_FROM_ATTR(attr)->rs[0].id == + msm_rpmrs_rpm_ctl.rs[0].id) + temp = GET_RS_FROM_ATTR(attr)->rs[0].value; + else + temp = GET_RS_FROM_ATTR(attr)->enable_low_power; + spin_unlock_irqrestore(&msm_rpmrs_lock, flags); + + kp.arg = &temp; + rc = param_get_uint(buf, &kp); + + if (rc > 0) { + strlcat(buf, "\n", PAGE_SIZE); + rc++; + } + + return rc; +} + +static ssize_t msm_rpmrs_resource_attr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct kernel_param kp; + unsigned long flags; + unsigned int temp; + int rc; + + kp.arg = &temp; + rc = param_set_uint(buf, &kp); + if (rc) + return rc; + + spin_lock_irqsave(&msm_rpmrs_lock, flags); + GET_RS_FROM_ATTR(attr)->enable_low_power = temp; + + /* special case active-set signal for MSM_RPMRS_ID_RPM_CTL */ + if (GET_RS_FROM_ATTR(attr)->rs[0].id == + msm_rpmrs_rpm_ctl.rs[0].id) { + struct msm_rpm_iv_pair req; + req.id = msm_rpmrs_rpm_ctl.rs[0].id; + req.value = GET_RS_FROM_ATTR(attr)->enable_low_power; + GET_RS_FROM_ATTR(attr)->rs[0].value = req.value; + + rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &req, 1); + if (rc) { + pr_err("%s: failed to request RPM_CTL to %d: %d\n", + __func__, req.value, rc); + } + } + + msm_rpmrs_update_levels(); + spin_unlock_irqrestore(&msm_rpmrs_lock, flags); + + return count; +} + +static int __init msm_rpmrs_resource_sysfs_add(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *low_power_kobj = NULL; + struct kobject *mode_kobj = NULL; + int rc = 0; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + pr_err("%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + rc = -ENOENT; + goto resource_sysfs_add_exit; + } + + low_power_kobj = kobject_create_and_add( + "enable_low_power", module_kobj); + if (!low_power_kobj) { + pr_err("%s: cannot create kobject\n", __func__); + rc = -ENOMEM; + goto resource_sysfs_add_exit; + } + + mode_kobj = kobject_create_and_add( + "mode", module_kobj); + if (!mode_kobj) { + pr_err("%s: cannot create kobject\n", __func__); + rc = -ENOMEM; + goto resource_sysfs_add_exit; + } + + rc = sysfs_create_group(low_power_kobj, &msm_rpmrs_attribute_group); + if (rc) { + pr_err("%s: cannot create kobject attribute group\n", __func__); + goto resource_sysfs_add_exit; + } + + rc = sysfs_create_group(mode_kobj, &msm_rpmrs_mode_attribute_group); + if (rc) { + pr_err("%s: cannot create kobject attribute group\n", __func__); + goto resource_sysfs_add_exit; + } + + rc = 0; +resource_sysfs_add_exit: + if (rc) { + if (low_power_kobj) + sysfs_remove_group(low_power_kobj, + &msm_rpmrs_attribute_group); + kobject_del(low_power_kobj); + kobject_del(mode_kobj); + } + + return rc; +} + +/****************************************************************************** + * Public Functions + *****************************************************************************/ + +int msm_rpmrs_set(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + return msm_rpmrs_set_common(ctx, req, count, false); +} + +int msm_rpmrs_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + WARN(!irqs_disabled(), "msm_rpmrs_set_noirq can only be called " + "safely when local irqs are disabled. Consider using " + "msm_rpmrs_set or msm_rpmrs_set_nosleep instead."); + return msm_rpmrs_set_common(ctx, req, count, true); +} + +/* Allow individual bits of an rpm resource be set, currently used only for + * active context resource viz. RPM_CTL. The API is generic enough to possibly + * extend it to other resources as well in the future. + */ +int msm_rpmrs_set_bits_noirq(int ctx, struct msm_rpm_iv_pair *req, int count, + int *mask) +{ + unsigned long flags; + int i, j; + int rc = -1; + struct msm_rpmrs_resource *rs; + + if (ctx != MSM_RPM_CTX_SET_0) + return -ENOSYS; + + spin_lock_irqsave(&msm_rpmrs_lock, flags); + for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) { + rs = msm_rpmrs_resources[i]; + if (rs->rs[0].id == req[0].id && rs->size == count) { + for (j = 0; j < rs->size; j++) { + rs->rs[j].value &= ~mask[j]; + rs->rs[j].value |= req[j].value & mask[j]; + } + break; + } + } + + if (i != ARRAY_SIZE(msm_rpmrs_resources)) { + rc = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &rs->rs[0], rs->size); + if (rc) { + for (j = 0; j < rs->size; j++) { + pr_err("%s: failed to request %d to %d: %d\n", + __func__, + rs->rs[j].id, rs->rs[j].value, rc); + } + } + } + spin_unlock_irqrestore(&msm_rpmrs_lock, flags); + + return rc; + +} + +int msm_rpmrs_clear(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + return msm_rpmrs_clear_common(ctx, req, count, false); +} + +int msm_rpmrs_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count) +{ + WARN(!irqs_disabled(), "msm_rpmrs_clear_noirq can only be called " + "safely when local irqs are disabled. Consider using " + "msm_rpmrs_clear or msm_rpmrs_clear_nosleep instead."); + return msm_rpmrs_clear_common(ctx, req, count, true); +} + +void msm_rpmrs_show_resources(void) +{ + struct msm_rpmrs_resource *rs; + unsigned long flags; + int i; + + spin_lock_irqsave(&msm_rpmrs_lock, flags); + for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) { + rs = msm_rpmrs_resources[i]; + if (rs->rs[0].id < MSM_RPM_ID_LAST) + pr_info("%s: resource %s: buffered %d, value 0x%x\n", + __func__, rs->name, + test_bit(rs->rs[0].id, msm_rpmrs_buffered), + msm_rpmrs_buffer[rs->rs[0].id]); + else + pr_info("%s: resource %s: value %d\n", + __func__, rs->name, rs->rs[0].value); + } + spin_unlock_irqrestore(&msm_rpmrs_lock, flags); +} + +s32 msm_cpuidle_get_deep_idle_latency(void) +{ + int i; + struct msm_rpmrs_level *level = msm_rpmrs_levels, *best = level; + + if (!level) + return 0; + + for (i = 0; i < msm_rpmrs_level_count; i++, level++) { + if (!level->available) + continue; + if (level->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE) + continue; + /* Pick the first power collapse mode by default */ + if (best->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE) + best = level; + /* Find the lowest latency for power collapse */ + if (level->latency_us < best->latency_us) + best = level; + } + return best->latency_us - 1; +} + +static void *msm_rpmrs_lowest_limits(bool from_idle, + enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us, + uint32_t sleep_us, uint32_t *power) +{ + unsigned int cpu = smp_processor_id(); + struct msm_rpmrs_level *best_level = NULL; + bool irqs_detectable = false; + bool gpio_detectable = false; + int i; + uint32_t pwr; + + if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) { + irqs_detectable = msm_mpm_irqs_detectable(from_idle); + gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle); + } + + for (i = 0; i < msm_rpmrs_level_count; i++) { + struct msm_rpmrs_level *level = &msm_rpmrs_levels[i]; + + if (!level->available) + continue; + + if (sleep_mode != level->sleep_mode) + continue; + + if (latency_us < level->latency_us) + continue; + + if (sleep_us <= level->time_overhead_us) + continue; + + if (!msm_rpmrs_irqs_detectable(&level->rs_limits, + irqs_detectable, gpio_detectable)) + continue; + + if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE == sleep_mode) + if (!cpu && msm_rpm_local_request_is_outstanding()) + break; + + + if (sleep_us <= 1) { + pwr = level->energy_overhead; + } else if (sleep_us <= level->time_overhead_us) { + pwr = level->energy_overhead / sleep_us; + } else if ((sleep_us >> 10) > level->time_overhead_us) { + pwr = level->steady_state_power; + } else { + pwr = level->steady_state_power; + pwr -= (level->time_overhead_us * + level->steady_state_power)/sleep_us; + pwr += level->energy_overhead / sleep_us; + } + + if (!best_level || + best_level->rs_limits.power[cpu] >= pwr) { + level->rs_limits.latency_us[cpu] = level->latency_us; + level->rs_limits.power[cpu] = pwr; + best_level = level; + if (power) + *power = pwr; + } + } + + return best_level ? &best_level->rs_limits : NULL; +} + +static int msm_rpmrs_enter_sleep(uint32_t sclk_count, void *limits, + bool from_idle, bool notify_rpm) +{ + int rc = 0; + + if (notify_rpm) { + rc = msm_rpmrs_flush_buffer(sclk_count, limits, from_idle); + if (rc) + return rc; + + if (msm_rpmrs_use_mpm(limits)) + msm_mpm_enter_sleep(from_idle); + } + + rc = msm_rpmrs_flush_L2(limits, notify_rpm); + return rc; +} + +static void msm_rpmrs_exit_sleep(void *limits, bool from_idle, + bool notify_rpm, bool collapsed) +{ + + /* Disable L2 for now, we dont want L2 to do retention by default */ + msm_rpmrs_L2_restore(limits, notify_rpm, collapsed); + + if (msm_rpmrs_use_mpm(limits)) + msm_mpm_exit_sleep(from_idle); +} + +static int rpmrs_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_ONLINE_FROZEN: + case CPU_ONLINE: + if (num_online_cpus() > 1) + msm_rpmrs_l2_cache.rs[0].value = + MSM_RPMRS_L2_CACHE_ACTIVE; + break; + case CPU_DEAD_FROZEN: + case CPU_DEAD: + if (num_online_cpus() == 1) + msm_rpmrs_l2_cache.rs[0].value = + MSM_RPMRS_L2_CACHE_HSFS_OPEN; + break; + } + + msm_rpmrs_update_levels(); + return NOTIFY_OK; +} + +static struct notifier_block __refdata rpmrs_cpu_notifier = { + .notifier_call = rpmrs_cpu_callback, +}; + +int __init msm_rpmrs_levels_init(struct msm_rpmrs_platform_data *data) +{ + int i, k; + struct msm_rpmrs_level *levels = data->levels; + + msm_rpmrs_level_count = data->num_levels; + + msm_rpmrs_levels = kzalloc(sizeof(struct msm_rpmrs_level) * + msm_rpmrs_level_count, GFP_KERNEL); + if (!msm_rpmrs_levels) + return -ENOMEM; + + memcpy(msm_rpmrs_levels, levels, + msm_rpmrs_level_count * sizeof(struct msm_rpmrs_level)); + + memcpy(vdd_dig_vlevels, data->vdd_dig_levels, + (MSM_RPMRS_VDD_DIG_MAX + 1) * sizeof(vdd_dig_vlevels[0])); + + memcpy(vdd_mem_vlevels, data->vdd_mem_levels, + (MSM_RPMRS_VDD_MEM_MAX + 1) * sizeof(vdd_mem_vlevels[0])); + vdd_mask = data->vdd_mask; + + msm_rpmrs_pxo.rs[0].id = data->rpmrs_target_id[MSM_RPMRS_ID_PXO_CLK]; + msm_rpmrs_l2_cache.rs[0].id = + data->rpmrs_target_id[MSM_RPMRS_ID_L2_CACHE_CTL]; + msm_rpmrs_vdd_mem.rs[0].id = + data->rpmrs_target_id[MSM_RPMRS_ID_VDD_MEM_0]; + msm_rpmrs_vdd_mem.rs[1].id = + data->rpmrs_target_id[MSM_RPMRS_ID_VDD_MEM_1]; + msm_rpmrs_vdd_dig.rs[0].id = + data->rpmrs_target_id[MSM_RPMRS_ID_VDD_DIG_0]; + msm_rpmrs_vdd_dig.rs[1].id = + data->rpmrs_target_id[MSM_RPMRS_ID_VDD_DIG_1]; + msm_rpmrs_rpm_ctl.rs[0].id = + data->rpmrs_target_id[MSM_RPMRS_ID_RPM_CTL]; + + /* Initialize listed bitmap for valid resource IDs */ + for (i = 0; i < ARRAY_SIZE(msm_rpmrs_resources); i++) { + for (k = 0; k < msm_rpmrs_resources[i]->size; k++) { + if (msm_rpmrs_resources[i]->rs[k].id >= + MSM_RPM_ID_LAST) + continue; + set_bit(msm_rpmrs_resources[i]->rs[k].id, + msm_rpmrs_listed); + } + } + + return 0; +} + +static int __init msm_rpmrs_init(void) +{ + struct msm_rpm_iv_pair req; + int rc; + + BUG_ON(!msm_rpmrs_levels); + + if (cpu_is_msm8x60()) { + req.id = msm_rpmrs_l2_cache.rs[0].id; + req.value = 1; + + rc = msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1); + if (rc) { + pr_err("%s: failed to request L2 cache: %d\n", + __func__, rc); + goto init_exit; + } + + req.id = msm_rpmrs_l2_cache.rs[0].id; + req.value = 0; + + rc = msm_rpmrs_set(MSM_RPM_CTX_SET_SLEEP, &req, 1); + if (rc) { + pr_err("%s: failed to initialize L2 cache for sleep: " + "%d\n", __func__, rc); + goto init_exit; + } + } + + rc = msm_rpmrs_resource_sysfs_add(); + +init_exit: + return rc; +} +device_initcall(msm_rpmrs_init); + +static struct msm_pm_sleep_ops msm_rpmrs_ops = { + .lowest_limits = msm_rpmrs_lowest_limits, + .enter_sleep = msm_rpmrs_enter_sleep, + .exit_sleep = msm_rpmrs_exit_sleep, +}; + +static int __init msm_rpmrs_l2_init(void) +{ + if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) { + + msm_pm_set_l2_flush_flag(0); + + msm_rpmrs_l2_cache.beyond_limits = + msm_spm_l2_cache_beyond_limits; + msm_rpmrs_l2_cache.aggregate = NULL; + msm_rpmrs_l2_cache.restore = NULL; + + register_hotcpu_notifier(&rpmrs_cpu_notifier); + + } else if (cpu_is_msm9615()) { + msm_rpmrs_l2_cache.beyond_limits = NULL; + msm_rpmrs_l2_cache.aggregate = NULL; + msm_rpmrs_l2_cache.restore = NULL; + } + + msm_pm_set_sleep_ops(&msm_rpmrs_ops); + + return 0; +} +early_initcall(msm_rpmrs_l2_init); diff --git a/arch/arm/mach-msm/rpm_resources.h b/arch/arm/mach-msm/rpm_resources.h new file mode 100644 index 00000000000..d5944057fb8 --- /dev/null +++ b/arch/arm/mach-msm/rpm_resources.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_RESOURCES_H +#define __ARCH_ARM_MACH_MSM_RPM_RESOURCES_H + +#include +#include "pm.h" + +enum { + MSM_RPMRS_ID_PXO_CLK = 0, + MSM_RPMRS_ID_L2_CACHE_CTL = 1, + MSM_RPMRS_ID_VDD_DIG_0 = 2, + MSM_RPMRS_ID_VDD_DIG_1 = 3, + MSM_RPMRS_ID_VDD_MEM_0 = 4, + MSM_RPMRS_ID_VDD_MEM_1 = 5, + MSM_RPMRS_ID_RPM_CTL = 6, + MSM_RPMRS_ID_LAST, +}; + +enum { + MSM_RPMRS_PXO_OFF = 0, + MSM_RPMRS_PXO_ON = 1, +}; + +enum { + MSM_RPMRS_L2_CACHE_HSFS_OPEN = 0, + MSM_RPMRS_L2_CACHE_GDHS = 1, + MSM_RPMRS_L2_CACHE_RETENTION = 2, + MSM_RPMRS_L2_CACHE_ACTIVE = 3, +}; + +enum { + MSM_RPMRS_MASK_RPM_CTL_CPU_HALT = 1, + MSM_RPMRS_MASK_RPM_CTL_MULTI_TIER = 2, +}; + +enum { + MSM_RPMRS_VDD_MEM_RET_LOW = 0, + MSM_RPMRS_VDD_MEM_RET_HIGH = 1, + MSM_RPMRS_VDD_MEM_ACTIVE = 2, + MSM_RPMRS_VDD_MEM_MAX = 3, + MSM_RPMRS_VDD_MEM_LAST, +}; + +enum { + MSM_RPMRS_VDD_DIG_RET_LOW = 0, + MSM_RPMRS_VDD_DIG_RET_HIGH = 1, + MSM_RPMRS_VDD_DIG_ACTIVE = 2, + MSM_RPMRS_VDD_DIG_MAX = 3, + MSM_RPMRS_VDD_DIG_LAST, +}; + +#define MSM_RPMRS_LIMITS(_pxo, _l2, _vdd_upper_b, _vdd) { \ + MSM_RPMRS_PXO_##_pxo, \ + MSM_RPMRS_L2_CACHE_##_l2, \ + MSM_RPMRS_VDD_MEM_##_vdd_upper_b, \ + MSM_RPMRS_VDD_MEM_##_vdd, \ + MSM_RPMRS_VDD_DIG_##_vdd_upper_b, \ + MSM_RPMRS_VDD_DIG_##_vdd, \ + {0}, {0}, \ +} + +struct msm_rpmrs_limits { + uint32_t pxo; + uint32_t l2_cache; + uint32_t vdd_mem_upper_bound; + uint32_t vdd_mem; + uint32_t vdd_dig_upper_bound; + uint32_t vdd_dig; + + uint32_t latency_us[NR_CPUS]; + uint32_t power[NR_CPUS]; +}; + +struct msm_rpmrs_level { + enum msm_pm_sleep_mode sleep_mode; + struct msm_rpmrs_limits rs_limits; + bool available; + uint32_t latency_us; + uint32_t steady_state_power; + uint32_t energy_overhead; + uint32_t time_overhead_us; +}; + +struct msm_rpmrs_platform_data { + struct msm_rpmrs_level *levels; + unsigned int num_levels; + unsigned int vdd_mem_levels[MSM_RPMRS_VDD_MEM_LAST]; + unsigned int vdd_dig_levels[MSM_RPMRS_VDD_DIG_LAST]; + unsigned int vdd_mask; + unsigned int rpmrs_target_id[MSM_RPMRS_ID_LAST]; +}; + +int msm_rpmrs_set(int ctx, struct msm_rpm_iv_pair *req, int count); +int msm_rpmrs_set_noirq(int ctx, struct msm_rpm_iv_pair *req, int count); +int msm_rpmrs_set_bits_noirq(int ctx, struct msm_rpm_iv_pair *req, int count, + int *mask); + +static inline int msm_rpmrs_set_nosleep( + int ctx, struct msm_rpm_iv_pair *req, int count) +{ + unsigned long flags; + int rc; + + local_irq_save(flags); + rc = msm_rpmrs_set_noirq(ctx, req, count); + local_irq_restore(flags); + + return rc; +} + +int msm_rpmrs_clear(int ctx, struct msm_rpm_iv_pair *req, int count); +int msm_rpmrs_clear_noirq(int ctx, struct msm_rpm_iv_pair *req, int count); + +static inline int msm_rpmrs_clear_nosleep( + int ctx, struct msm_rpm_iv_pair *req, int count) +{ + unsigned long flags; + int rc; + + local_irq_save(flags); + rc = msm_rpmrs_clear_noirq(ctx, req, count); + local_irq_restore(flags); + + return rc; +} + +void msm_rpmrs_show_resources(void); +int msm_rpmrs_levels_init(struct msm_rpmrs_platform_data *data); + +#endif /* __ARCH_ARM_MACH_MSM_RPM_RESOURCES_H */ diff --git a/arch/arm/mach-msm/rpm_stats.c b/arch/arm/mach-msm/rpm_stats.c new file mode 100644 index 00000000000..a831bd5a35f --- /dev/null +++ b/arch/arm/mach-msm/rpm_stats.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rpm_stats.h" + +enum { + ID_COUNTER, + ID_ACCUM_TIME_SCLK, + ID_MAX, +}; + +static char *msm_rpmstats_id_labels[ID_MAX] = { + [ID_COUNTER] = "Count", + [ID_ACCUM_TIME_SCLK] = "Total time(uSec)", +}; + +#define SCLK_HZ 32768 +struct msm_rpmstats_record{ + char name[32]; + uint32_t id; + uint32_t val; +}; + +struct msm_rpmstats_private_data{ + void __iomem *reg_base; + u32 num_records; + u32 read_idx; + u32 len; + char buf[128]; + struct msm_rpmstats_platform_data *platform_data; +}; + +static inline unsigned long msm_rpmstats_read_register(void __iomem *regbase, + int index, int offset) +{ + return readl_relaxed(regbase + index * 12 + (offset + 1) * 4); +} +static void msm_rpmstats_strcpy(char *dest, char *src) +{ + union { + char ch[4]; + unsigned long word; + } string; + int index = 0; + + do { + int i; + string.word = readl_relaxed(src + 4 * index); + for (i = 0; i < 4; i++) { + *dest++ = string.ch[i]; + if (!string.ch[i]) + break; + } + index++; + } while (*(dest-1)); + +} +static int msm_rpmstats_copy_stats(struct msm_rpmstats_private_data *pdata) +{ + + struct msm_rpmstats_record record; + unsigned long ptr; + unsigned long offset; + char *str; + uint64_t usec; + + ptr = msm_rpmstats_read_register(pdata->reg_base, pdata->read_idx, 0); + offset = (ptr - (unsigned long)pdata->platform_data->phys_addr_base); + + if (offset > pdata->platform_data->phys_size) + str = (char *)ioremap(ptr, SZ_256); + else + str = (char *) pdata->reg_base + offset; + + msm_rpmstats_strcpy(record.name, str); + + if (offset > pdata->platform_data->phys_size) + iounmap(str); + + record.id = msm_rpmstats_read_register(pdata->reg_base, + pdata->read_idx, 1); + record.val = msm_rpmstats_read_register(pdata->reg_base, + pdata->read_idx, 2); + + if (record.id == ID_ACCUM_TIME_SCLK) { + usec = record.val * USEC_PER_SEC; + do_div(usec, SCLK_HZ); + } else + usec = (unsigned long)record.val; + + pdata->read_idx++; + + return snprintf(pdata->buf, sizeof(pdata->buf), + "RPM Mode:%s\n\t%s:%llu\n", + record.name, + msm_rpmstats_id_labels[record.id], + usec); +} + +static int msm_rpmstats_file_read(struct file *file, char __user *bufu, + size_t count, loff_t *ppos) +{ + struct msm_rpmstats_private_data *prvdata; + prvdata = file->private_data; + + if (!prvdata) + return -EINVAL; + + if (!bufu || count < 0) + return -EINVAL; + + if (!prvdata->num_records) + prvdata->num_records = readl_relaxed(prvdata->reg_base); + + if ((*ppos >= prvdata->len) + && (prvdata->read_idx < prvdata->num_records)) { + prvdata->len = msm_rpmstats_copy_stats(prvdata); + *ppos = 0; + } + + return simple_read_from_buffer(bufu, count, ppos, + prvdata->buf, prvdata->len); +} + +static int msm_rpmstats_file_open(struct inode *inode, struct file *file) +{ + struct msm_rpmstats_private_data *prvdata; + struct msm_rpmstats_platform_data *pdata; + + pdata = inode->i_private; + + file->private_data = + kmalloc(sizeof(struct msm_rpmstats_private_data), GFP_KERNEL); + + if (!file->private_data) + return -ENOMEM; + prvdata = file->private_data; + + prvdata->reg_base = ioremap(pdata->phys_addr_base, pdata->phys_size); + if (!prvdata->reg_base) { + kfree(file->private_data); + prvdata = NULL; + pr_err("%s: ERROR could not ioremap start=%p, len=%u\n", + __func__, (void *)pdata->phys_addr_base, + pdata->phys_size); + return -EBUSY; + } + + prvdata->read_idx = prvdata->num_records = prvdata->len = 0; + prvdata->platform_data = pdata; + return 0; +} + +static int msm_rpmstats_file_close(struct inode *inode, struct file *file) +{ + struct msm_rpmstats_private_data *private = file->private_data; + + if (private->reg_base) + iounmap(private->reg_base); + kfree(file->private_data); + + return 0; +} + +static const struct file_operations msm_rpmstats_fops = { + .owner = THIS_MODULE, + .open = msm_rpmstats_file_open, + .read = msm_rpmstats_file_read, + .release = msm_rpmstats_file_close, + .llseek = no_llseek, +}; + +static int __devinit msm_rpmstats_probe(struct platform_device *pdev) +{ + struct dentry *dent; + struct msm_rpmstats_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + dent = debugfs_create_file("rpm_stats", S_IRUGO, NULL, + pdev->dev.platform_data, &msm_rpmstats_fops); + + if (!dent) { + pr_err("%s: ERROR debugfs_create_file failed\n", __func__); + return -ENOMEM; + } + platform_set_drvdata(pdev, dent); + return 0; +} + +static int __devexit msm_rpmstats_remove(struct platform_device *pdev) +{ + struct dentry *dent; + + dent = platform_get_drvdata(pdev); + debugfs_remove(dent); + platform_set_drvdata(pdev, NULL); + return 0; +} +static struct platform_driver msm_rpmstats_driver = { + .probe = msm_rpmstats_probe, + .remove = __devexit_p(msm_rpmstats_remove), + .driver = { + .name = "msm_rpm_stat", + .owner = THIS_MODULE, + }, +}; +static int __init msm_rpmstats_init(void) +{ + return platform_driver_register(&msm_rpmstats_driver); +} +static void __exit msm_rpmstats_exit(void) +{ + platform_driver_unregister(&msm_rpmstats_driver); +} +module_init(msm_rpmstats_init); +module_exit(msm_rpmstats_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPM Statistics driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:msm_stat_log"); diff --git a/arch/arm/mach-msm/rpm_stats.h b/arch/arm/mach-msm/rpm_stats.h new file mode 100644 index 00000000000..918d4fba7af --- /dev/null +++ b/arch/arm/mach-msm/rpm_stats.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPM_STATS_H +#define __ARCH_ARM_MACH_MSM_RPM_STATS_H + +#include + +struct msm_rpmstats_platform_data { + phys_addr_t phys_addr_base; + u32 phys_size; +}; +#endif diff --git a/arch/arm/mach-msm/saw-regulator.c b/arch/arm/mach-msm/saw-regulator.c new file mode 100644 index 00000000000..6762648f12d --- /dev/null +++ b/arch/arm/mach-msm/saw-regulator.c @@ -0,0 +1,237 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spm.h" + +#define FTSMPS_VCTRL_BAND_MASK 0xC0 +#define FTSMPS_VCTRL_BAND_1 0x40 +#define FTSMPS_VCTRL_BAND_2 0x80 +#define FTSMPS_VCTRL_BAND_3 0xC0 +#define FTSMPS_VCTRL_VPROG_MASK 0x3F + +#define FTSMPS_BAND1_UV_MIN 350000 +#define FTSMPS_BAND1_UV_MAX 650000 +/* 3 LSB's of program voltage must be 0 in band 1. */ +/* Logical step size */ +#define FTSMPS_BAND1_UV_LOG_STEP 50000 +/* Physical step size */ +#define FTSMPS_BAND1_UV_PHYS_STEP 6250 + +#define FTSMPS_BAND2_UV_MIN 700000 +#define FTSMPS_BAND2_UV_MAX 1400000 +#define FTSMPS_BAND2_UV_STEP 12500 + +#define FTSMPS_BAND3_UV_MIN 1400000 +#define FTSMPS_BAND3_UV_SET_POINT_MIN 1500000 +#define FTSMPS_BAND3_UV_MAX 3300000 +#define FTSMPS_BAND3_UV_STEP 50000 + +struct saw_vreg { + struct regulator_desc desc; + struct regulator_dev *rdev; + char *name; + int uV; +}; + +/* Minimum core operating voltage */ +#define MIN_CORE_VOLTAGE 950000 + +/* Specifies the PMIC internal slew rate in uV/us. */ +#define REGULATOR_SLEW_RATE 1250 + +static int saw_get_voltage(struct regulator_dev *rdev) +{ + struct saw_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->uV; +} + +static int saw_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, + unsigned *selector) +{ + struct saw_vreg *vreg = rdev_get_drvdata(rdev); + int uV = min_uV; + int rc; + u8 vprog, band; + + if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN) + uV = FTSMPS_BAND1_UV_MIN; + + if (uV < FTSMPS_BAND1_UV_MIN || uV > FTSMPS_BAND3_UV_MAX) { + pr_err("%s: request v=[%d, %d] is outside possible " + "v=[%d, %d]\n", vreg->name, min_uV, max_uV, + FTSMPS_BAND1_UV_MIN, FTSMPS_BAND3_UV_MAX); + return -EINVAL; + } + + /* Round up for set points in the gaps between bands. */ + if (uV > FTSMPS_BAND1_UV_MAX && uV < FTSMPS_BAND2_UV_MIN) + uV = FTSMPS_BAND2_UV_MIN; + else if (uV > FTSMPS_BAND2_UV_MAX + && uV < FTSMPS_BAND3_UV_SET_POINT_MIN) + uV = FTSMPS_BAND3_UV_SET_POINT_MIN; + + if (uV > FTSMPS_BAND2_UV_MAX) { + vprog = (uV - FTSMPS_BAND3_UV_MIN + FTSMPS_BAND3_UV_STEP - 1) + / FTSMPS_BAND3_UV_STEP; + band = FTSMPS_VCTRL_BAND_3; + uV = FTSMPS_BAND3_UV_MIN + vprog * FTSMPS_BAND3_UV_STEP; + } else if (uV > FTSMPS_BAND1_UV_MAX) { + vprog = (uV - FTSMPS_BAND2_UV_MIN + FTSMPS_BAND2_UV_STEP - 1) + / FTSMPS_BAND2_UV_STEP; + band = FTSMPS_VCTRL_BAND_2; + uV = FTSMPS_BAND2_UV_MIN + vprog * FTSMPS_BAND2_UV_STEP; + } else { + vprog = (uV - FTSMPS_BAND1_UV_MIN + + FTSMPS_BAND1_UV_LOG_STEP - 1) + / FTSMPS_BAND1_UV_LOG_STEP; + uV = FTSMPS_BAND1_UV_MIN + vprog * FTSMPS_BAND1_UV_LOG_STEP; + vprog *= FTSMPS_BAND1_UV_LOG_STEP / FTSMPS_BAND1_UV_PHYS_STEP; + band = FTSMPS_VCTRL_BAND_1; + } + + if (uV > max_uV) { + pr_err("%s: request v=[%d, %d] cannot be met by any setpoint\n", + vreg->name, min_uV, max_uV); + return -EINVAL; + } + + rc = msm_spm_set_vdd(rdev_get_id(rdev), band | vprog); + if (!rc) { + if (uV > vreg->uV) { + /* Wait for voltage to stabalize. */ + udelay((uV - vreg->uV) / REGULATOR_SLEW_RATE); + } + vreg->uV = uV; + } else { + pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->name, rc); + } + + return rc; +} + +static struct regulator_ops saw_ops = { + .get_voltage = saw_get_voltage, + .set_voltage = saw_set_voltage, +}; + +static int __devinit saw_probe(struct platform_device *pdev) +{ + struct regulator_init_data *init_data; + struct saw_vreg *vreg; + int rc = 0; + + if (!pdev->dev.platform_data) { + pr_err("platform data required.\n"); + return -EINVAL; + } + + init_data = pdev->dev.platform_data; + if (!init_data->constraints.name) { + pr_err("regulator name must be specified in constraints.\n"); + return -EINVAL; + } + + vreg = kzalloc(sizeof(struct saw_vreg), GFP_KERNEL); + if (!vreg) { + pr_err("kzalloc failed.\n"); + return -ENOMEM; + } + + vreg->name = kstrdup(init_data->constraints.name, GFP_KERNEL); + if (!vreg->name) { + pr_err("kzalloc failed.\n"); + rc = -ENOMEM; + goto free_vreg; + } + + vreg->desc.name = vreg->name; + vreg->desc.id = pdev->id; + vreg->desc.ops = &saw_ops; + vreg->desc.type = REGULATOR_VOLTAGE; + vreg->desc.owner = THIS_MODULE; + vreg->uV = MIN_CORE_VOLTAGE; + + vreg->rdev = regulator_register(&vreg->desc, &pdev->dev, + init_data, vreg, NULL); + if (IS_ERR(vreg->rdev)) { + rc = PTR_ERR(vreg->rdev); + pr_err("regulator_register failed, rc=%d.\n", rc); + goto free_name; + } + + platform_set_drvdata(pdev, vreg); + + pr_info("id=%d, name=%s\n", pdev->id, vreg->name); + + return rc; + +free_name: + kfree(vreg->name); +free_vreg: + kfree(vreg); + + return rc; +} + +static int __devexit saw_remove(struct platform_device *pdev) +{ + struct saw_vreg *vreg = platform_get_drvdata(pdev); + + regulator_unregister(vreg->rdev); + kfree(vreg->name); + kfree(vreg); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver saw_driver = { + .probe = saw_probe, + .remove = __devexit_p(saw_remove), + .driver = { + .name = "saw-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init saw_init(void) +{ + return platform_driver_register(&saw_driver); +} + +static void __exit saw_exit(void) +{ + platform_driver_unregister(&saw_driver); +} + +postcore_initcall(saw_init); +module_exit(saw_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SAW regulator driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:saw-regulator"); diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-msm/scm-boot.c index 45cee3e469a..e377633255d 100644 --- a/arch/arm/mach-msm/scm-boot.c +++ b/arch/arm/mach-msm/scm-boot.c @@ -8,27 +8,22 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include #include -#include "scm.h" +#include #include "scm-boot.h" /* * Set the cold/warm boot address for one of the CPU cores. */ -int scm_set_boot_addr(phys_addr_t addr, int flags) +int scm_set_boot_addr(void *addr, int flags) { struct { unsigned int flags; - phys_addr_t addr; + void *addr; } cmd; cmd.addr = addr; @@ -37,3 +32,4 @@ int scm_set_boot_addr(phys_addr_t addr, int flags) &cmd, sizeof(cmd), NULL, 0); } EXPORT_SYMBOL(scm_set_boot_addr); + diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-msm/scm-boot.h index 7be32ff5d68..221ffca9623 100644 --- a/arch/arm/mach-msm/scm-boot.h +++ b/arch/arm/mach-msm/scm-boot.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,19 @@ #ifndef __MACH_SCM_BOOT_H #define __MACH_SCM_BOOT_H -#define SCM_BOOT_ADDR 0x1 -#define SCM_FLAG_COLDBOOT_CPU1 0x1 -#define SCM_FLAG_WARMBOOT_CPU1 0x2 -#define SCM_FLAG_WARMBOOT_CPU0 0x4 +#define SCM_BOOT_ADDR 0x1 +#define SCM_FLAG_COLDBOOT_CPU1 0x01 +#define SCM_FLAG_COLDBOOT_CPU2 0x08 +#define SCM_FLAG_COLDBOOT_CPU3 0x20 +#define SCM_FLAG_WARMBOOT_CPU1 0x02 +#define SCM_FLAG_WARMBOOT_CPU0 0x04 +#define SCM_FLAG_WARMBOOT_CPU2 0x10 +#define SCM_FLAG_WARMBOOT_CPU3 0x40 -int scm_set_boot_addr(phys_addr_t addr, int flags); +#ifdef CONFIG_MSM_SCM +int scm_set_boot_addr(void *addr, int flags); +#else +static inline int scm_set_boot_addr(void *addr, int flags) { return 0; } +#endif #endif diff --git a/arch/arm/mach-msm/scm-io.c b/arch/arm/mach-msm/scm-io.c new file mode 100644 index 00000000000..28614d348a6 --- /dev/null +++ b/arch/arm/mach-msm/scm-io.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +#include +#include +#include + +#define SCM_IO_READ 0x1 +#define SCM_IO_WRITE 0x2 + +#define BETWEEN(p, st, sz) ((p) >= (void __iomem *)(st) && \ + (p) < ((void __iomem *)(st) + (sz))) +#define XLATE(p, pst, vst) ((u32)((p) - (vst)) + (pst)) + +static u32 __secure_readl(u32 addr) +{ + u32 r; + r = scm_call_atomic1(SCM_SVC_IO, SCM_IO_READ, addr); + __iormb(); + return r; +} + +u32 secure_readl(void __iomem *c) +{ + if (BETWEEN(c, MSM_MMSS_CLK_CTL_BASE, MSM_MMSS_CLK_CTL_SIZE)) + return __secure_readl(XLATE(c, MSM_MMSS_CLK_CTL_PHYS, + MSM_MMSS_CLK_CTL_BASE)); + else if (BETWEEN(c, MSM_TCSR_BASE, MSM_TCSR_SIZE)) + return __secure_readl(XLATE(c, MSM_TCSR_PHYS, MSM_TCSR_BASE)); + return readl(c); +} +EXPORT_SYMBOL(secure_readl); + +static void __secure_writel(u32 v, u32 addr) +{ + __iowmb(); + scm_call_atomic2(SCM_SVC_IO, SCM_IO_WRITE, addr, v); +} + +void secure_writel(u32 v, void __iomem *c) +{ + if (BETWEEN(c, MSM_MMSS_CLK_CTL_BASE, MSM_MMSS_CLK_CTL_SIZE)) + __secure_writel(v, XLATE(c, MSM_MMSS_CLK_CTL_PHYS, + MSM_MMSS_CLK_CTL_BASE)); + else if (BETWEEN(c, MSM_TCSR_BASE, MSM_TCSR_SIZE)) + __secure_writel(v, XLATE(c, MSM_TCSR_PHYS, MSM_TCSR_BASE)); + else + writel(v, c); +} +EXPORT_SYMBOL(secure_writel); diff --git a/arch/arm/mach-msm/scm-pas.c b/arch/arm/mach-msm/scm-pas.c new file mode 100644 index 00000000000..4096d9c7fd1 --- /dev/null +++ b/arch/arm/mach-msm/scm-pas.c @@ -0,0 +1,205 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "scm-pas: " fmt + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "scm-pas.h" + +#define PAS_INIT_IMAGE_CMD 1 +#define PAS_AUTH_AND_RESET_CMD 5 +#define PAS_SHUTDOWN_CMD 6 +#define PAS_IS_SUPPORTED_CMD 7 + +int pas_init_image(enum pas_id id, const u8 *metadata, size_t size) +{ + int ret; + struct pas_init_image_req { + u32 proc; + u32 image_addr; + } request; + u32 scm_ret = 0; + /* Make memory physically contiguous */ + void *mdata_buf = kmemdup(metadata, size, GFP_KERNEL); + + if (!mdata_buf) + return -ENOMEM; + + request.proc = id; + request.image_addr = virt_to_phys(mdata_buf); + + ret = scm_call(SCM_SVC_PIL, PAS_INIT_IMAGE_CMD, &request, + sizeof(request), &scm_ret, sizeof(scm_ret)); + kfree(mdata_buf); + + if (ret) + return ret; + return scm_ret; +} +EXPORT_SYMBOL(pas_init_image); + +static struct msm_bus_paths scm_pas_bw_tbl[] = { + { + .vectors = (struct msm_bus_vectors[]){ + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + }, + }, + .num_paths = 1, + }, + { + .vectors = (struct msm_bus_vectors[]){ + { + .src = MSM_BUS_MASTER_SPS, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ib = 492 * 8 * 1000000UL, + .ab = 492 * 8 * 100000UL, + }, + }, + .num_paths = 1, + }, +}; + +static struct msm_bus_scale_pdata scm_pas_bus_pdata = { + .usecase = scm_pas_bw_tbl, + .num_usecases = ARRAY_SIZE(scm_pas_bw_tbl), + .name = "scm_pas", +}; + +static uint32_t scm_perf_client; +static struct clk *scm_bus_clk; + +static DEFINE_MUTEX(scm_pas_bw_mutex); +static int scm_pas_bw_count; + +static int scm_pas_enable_bw(void) +{ + int ret = 0; + + if (!scm_perf_client || !scm_bus_clk) + return -EINVAL; + + mutex_lock(&scm_pas_bw_mutex); + if (!scm_pas_bw_count) { + ret = msm_bus_scale_client_update_request(scm_perf_client, 1); + if (ret) { + pr_err("bandwidth request failed (%d)\n", ret); + } else { + ret = clk_prepare_enable(scm_bus_clk); + if (ret) + pr_err("clock enable failed\n"); + } + } + if (ret) + msm_bus_scale_client_update_request(scm_perf_client, 0); + else + scm_pas_bw_count++; + mutex_unlock(&scm_pas_bw_mutex); + return ret; +} + +static void scm_pas_disable_bw(void) +{ + mutex_lock(&scm_pas_bw_mutex); + if (scm_pas_bw_count-- == 1) { + msm_bus_scale_client_update_request(scm_perf_client, 0); + clk_disable_unprepare(scm_bus_clk); + } + mutex_unlock(&scm_pas_bw_mutex); +} + +int pas_auth_and_reset(enum pas_id id) +{ + int ret, bus_ret; + u32 proc = id, scm_ret = 0; + + bus_ret = scm_pas_enable_bw(); + ret = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &proc, + sizeof(proc), &scm_ret, sizeof(scm_ret)); + if (ret) + scm_ret = ret; + if (!bus_ret) + scm_pas_disable_bw(); + + return scm_ret; +} +EXPORT_SYMBOL(pas_auth_and_reset); + +int pas_shutdown(enum pas_id id) +{ + int ret; + u32 proc = id, scm_ret = 0; + + ret = scm_call(SCM_SVC_PIL, PAS_SHUTDOWN_CMD, &proc, sizeof(proc), + &scm_ret, sizeof(scm_ret)); + if (ret) + return ret; + + return scm_ret; +} +EXPORT_SYMBOL(pas_shutdown); + +static bool secure_pil = true; +module_param(secure_pil, bool, S_IRUGO); +MODULE_PARM_DESC(secure_pil, "Use secure PIL"); + +int pas_supported(enum pas_id id) +{ + int ret; + u32 periph = id, ret_val = 0; + + if (!secure_pil) + return 0; + + /* + * 8660 SCM doesn't support querying secure PIL support so just return + * true if not overridden on the command line. + */ + if (cpu_is_msm8x60()) + return 1; + + if (scm_is_call_available(SCM_SVC_PIL, PAS_IS_SUPPORTED_CMD) <= 0) + return 0; + + ret = scm_call(SCM_SVC_PIL, PAS_IS_SUPPORTED_CMD, &periph, + sizeof(periph), &ret_val, sizeof(ret_val)); + if (ret) + return ret; + + return ret_val; +} +EXPORT_SYMBOL(pas_supported); + +static int __init scm_pas_init(void) +{ + scm_perf_client = msm_bus_scale_register_client(&scm_pas_bus_pdata); + if (!scm_perf_client) + pr_warn("unable to register bus client\n"); + scm_bus_clk = clk_get_sys("scm", "bus_clk"); + if (!IS_ERR(scm_bus_clk)) { + clk_set_rate(scm_bus_clk, 64000000); + } else { + scm_bus_clk = NULL; + pr_warn("unable to get bus clock\n"); + } + return 0; +} +module_init(scm_pas_init); diff --git a/arch/arm/mach-msm/scm-pas.h b/arch/arm/mach-msm/scm-pas.h new file mode 100644 index 00000000000..2fe71a93ff1 --- /dev/null +++ b/arch/arm/mach-msm/scm-pas.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_SCM_PAS_H +#define __MSM_SCM_PAS_H + +enum pas_id { + PAS_MODEM, + PAS_Q6, + PAS_DSPS, + PAS_TZAPPS, + PAS_MODEM_SW, + PAS_MODEM_FW, + PAS_RIVA, + PAS_SECAPP, + PAS_GSS, + PAS_VIDC, +}; + +extern int pas_init_image(enum pas_id id, const u8 *metadata, size_t size); +extern int pas_auth_and_reset(enum pas_id id); +extern int pas_shutdown(enum pas_id id); +extern int pas_supported(enum pas_id id); +#endif diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c index bafabb50258..ac489904596 100644 --- a/arch/arm/mach-msm/scm.c +++ b/arch/arm/mach-msm/scm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,11 +8,6 @@ * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include @@ -21,13 +16,11 @@ #include #include #include +#include #include -#include "scm.h" - -/* Cache line size for msm8x60 */ -#define CACHELINESIZE 32 +#include #define SCM_ENOMEM -5 #define SCM_EOPNOTSUPP -4 @@ -210,6 +203,21 @@ static int __scm_call(const struct scm_command *cmd) return ret; } +static u32 cacheline_size; + +static void scm_inv_range(unsigned long start, unsigned long end) +{ + start = round_down(start, cacheline_size); + end = round_up(end, cacheline_size); + while (start < end) { + asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) + : "memory"); + start += cacheline_size; + } + dsb(); + isb(); +} + /** * scm_call() - Send an SCM command * @svc_id: service identifier @@ -227,6 +235,7 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, int ret; struct scm_command *cmd; struct scm_response *rsp; + unsigned long start, end; cmd = alloc_scm_command(cmd_len, resp_len); if (!cmd) @@ -243,17 +252,15 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, goto out; rsp = scm_command_to_response(cmd); + start = (unsigned long)rsp; + do { - u32 start = (u32)rsp; - u32 end = (u32)scm_get_response_buffer(rsp) + resp_len; - start &= ~(CACHELINESIZE - 1); - while (start < end) { - asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) - : "memory"); - start += CACHELINESIZE; - } + scm_inv_range(start, start + sizeof(*rsp)); } while (!rsp->is_complete); + end = (unsigned long)scm_get_response_buffer(rsp) + resp_len; + scm_inv_range(start, end); + if (resp_buf) memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); out: @@ -262,6 +269,105 @@ out: } EXPORT_SYMBOL(scm_call); +#define SCM_CLASS_REGISTER (0x2 << 8) +#define SCM_MASK_IRQS BIT(5) +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ + SCM_CLASS_REGISTER | \ + SCM_MASK_IRQS | \ + (n & 0xf)) + +/** + * scm_call_atomic1() - Send an atomic SCM command with one argument + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + int context_id; + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = arg1; + + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2) + : "r3"); + return r0; +} +EXPORT_SYMBOL(scm_call_atomic1); + +/** + * scm_call_atomic2() - Send an atomic SCM command with two arguments + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * @arg2: second argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) +{ + int context_id; + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2); + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") + __asmeq("%4", "r3") + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2), "r" (r3)); + return r0; +} +EXPORT_SYMBOL(scm_call_atomic2); + +s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 *ret1, u32 *ret2) +{ + int ret; + int context_id; + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 4); + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + register u32 r4 asm("r4") = arg3; + register u32 r5 asm("r5") = arg4; + + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r1") + __asmeq("%2", "r2") + __asmeq("%3", "r0") + __asmeq("%4", "r1") + __asmeq("%5", "r2") + __asmeq("%6", "r3") + "smc #0 @ switch to secure world\n" + : "=r" (r0), "=r" (r1), "=r" (r2) + : "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5)); + ret = r0; + if (ret1) + *ret1 = r1; + if (ret2) + *ret2 = r2; + return r0; +} +EXPORT_SYMBOL(scm_call_atomic4_3); + u32 scm_get_version(void) { int context_id; @@ -294,3 +400,43 @@ u32 scm_get_version(void) return version; } EXPORT_SYMBOL(scm_get_version); + +#define IS_CALL_AVAIL_CMD 1 +int scm_is_call_available(u32 svc_id, u32 cmd_id) +{ + int ret; + u32 svc_cmd = (svc_id << 10) | cmd_id; + u32 ret_val = 0; + + ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd, + sizeof(svc_cmd), &ret_val, sizeof(ret_val)); + if (ret) + return ret; + + return ret_val; +} +EXPORT_SYMBOL(scm_is_call_available); + +#define GET_FEAT_VERSION_CMD 3 +int scm_get_feat_version(u32 feat) +{ + if (scm_is_call_available(SCM_SVC_INFO, GET_FEAT_VERSION_CMD)) { + u32 version; + if (!scm_call(SCM_SVC_INFO, GET_FEAT_VERSION_CMD, &feat, + sizeof(feat), &version, sizeof(version))) + return version; + } + return 0; +} +EXPORT_SYMBOL(scm_get_feat_version); + +static int scm_init(void) +{ + u32 ctr; + + asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); + cacheline_size = 4 << ((ctr >> 16) & 0xf); + + return 0; +} +early_initcall(scm_init); diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c new file mode 100644 index 00000000000..356ce9009fc --- /dev/null +++ b/arch/arm/mach-msm/sdio_al.c @@ -0,0 +1,4365 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Abstraction-Layer Module. + * + * To be used with Qualcomm's SDIO-Client connected to this host. + */ +#include "sdio_al_private.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../../../drivers/mmc/host/msm_sdcc.h" + +/** + * Func#0 has SDIO standard registers + * Func#1 is for Mailbox. + * Functions 2..7 are for channels. + * Currently only functions 2..5 are active due to SDIO-Client + * number of pipes. + * + */ +#define SDIO_AL_MAX_CHANNELS 6 + +/** Func 1..5 */ +#define SDIO_AL_MAX_FUNCS (SDIO_AL_MAX_CHANNELS+1) +#define SDIO_AL_WAKEUP_FUNC 6 + +/** Number of SDIO-Client pipes */ +#define SDIO_AL_MAX_PIPES 16 +#define SDIO_AL_ACTIVE_PIPES 8 + +/** CMD53/CMD54 Block size */ +#define SDIO_AL_BLOCK_SIZE 256 + +/** Func#1 hardware Mailbox base address */ +#define HW_MAILBOX_ADDR 0x1000 + +/** Func#1 peer sdioc software version. + * The header is duplicated also to the mailbox of the other + * functions. It can be used before other functions are enabled. */ +#define SDIOC_SW_HEADER_ADDR 0x0400 + +/** Func#2..7 software Mailbox base address at 16K */ +#define SDIOC_SW_MAILBOX_ADDR 0x4000 + +/** Some Mailbox registers address, written by host for + control */ +#define PIPES_THRESHOLD_ADDR 0x01000 + +#define PIPES_0_7_IRQ_MASK_ADDR 0x01048 + +#define PIPES_8_15_IRQ_MASK_ADDR 0x0104C + +#define FUNC_1_4_MASK_IRQ_ADDR 0x01040 +#define FUNC_5_7_MASK_IRQ_ADDR 0x01044 +#define FUNC_1_4_USER_IRQ_ADDR 0x01050 +#define FUNC_5_7_USER_IRQ_ADDR 0x01054 + +#define EOT_PIPES_ENABLE 0x00 + +/** Maximum read/write data available is SDIO-Client limitation */ +#define MAX_DATA_AVAILABLE (16*1024) +#define INVALID_DATA_AVAILABLE (0x8000) + +/** SDIO-Client HW threshold to generate interrupt to the + * SDIO-Host on write available bytes. + */ +#define DEFAULT_WRITE_THRESHOLD (1024) + +/** SDIO-Client HW threshold to generate interrupt to the + * SDIO-Host on read available bytes, for streaming (non + * packet) rx data. + */ +#define DEFAULT_READ_THRESHOLD (1024) +#define LOW_LATENCY_THRESHOLD (1) + +/* Extra bytes to ensure getting the rx threshold interrupt on stream channels + when restoring the threshold after sleep */ +#define THRESHOLD_CHANGE_EXTRA_BYTES (100) + +/** SW threshold to trigger reading the mailbox. */ +#define DEFAULT_MIN_WRITE_THRESHOLD (1024) +#define DEFAULT_MIN_WRITE_THRESHOLD_STREAMING (1600) + +#define THRESHOLD_DISABLE_VAL (0xFFFFFFFF) + +/** Mailbox polling time for packet channels */ +#define DEFAULT_POLL_DELAY_MSEC 10 +/** Mailbox polling time for streaming channels */ +#define DEFAULT_POLL_DELAY_NOPACKET_MSEC 30 + +/** The SDIO-Client prepares N buffers of size X per Tx pipe. + * Even when the transfer fills a partial buffer, + * that buffer becomes unusable for the next transfer. */ +#define DEFAULT_PEER_TX_BUF_SIZE (128) + +#define ROUND_UP(x, n) (((x + n - 1) / n) * n) + +/** Func#2..7 FIFOs are r/w via + sdio_readsb() & sdio_writesb(),when inc_addr=0 */ +#define PIPE_RX_FIFO_ADDR 0x00 +#define PIPE_TX_FIFO_ADDR 0x00 + +/** Inactivity time to go to sleep in mseconds */ +#define INACTIVITY_TIME_MSEC 30 +#define INITIAL_INACTIVITY_TIME_MSEC 5000 + +/** Context validity check */ +#define SDIO_AL_SIGNATURE 0xAABBCCDD + +/* Vendor Specific Command */ +#define SD_IO_RW_EXTENDED_QCOM 54 + +#define TIME_TO_WAIT_US 500 +#define SDIO_CLOSE_FLUSH_TIMEOUT_MSEC (10000) +#define RX_FLUSH_BUFFER_SIZE (16*1024) + +#define SDIO_TEST_POSTFIX "_TEST" + +#define DATA_DEBUG(x, y...) \ + do { \ + if (sdio_al->debug.debug_data_on) \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define LPM_DEBUG(x, y...) \ + do { \ + if (sdio_al->debug.debug_lpm_on) \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define sdio_al_loge(x, y...) \ + do { \ + pr_err(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define sdio_al_logi(x, y...) \ + do { \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +#define CLOSE_DEBUG(x, y...) \ + do { \ + if (sdio_al->debug.debug_close_on) \ + pr_info(y); \ + sdio_al_log(x, y); \ + } while (0) + +/* The index of the SDIO card used for the sdio_al_dloader */ +#define SDIO_BOOTLOADER_CARD_INDEX 1 + + +/* SDIO card state machine */ +enum sdio_al_device_state { + CARD_INSERTED, + CARD_REMOVED, + MODEM_RESTART +}; + +struct sdio_al_debug { + u8 debug_lpm_on; + u8 debug_data_on; + u8 debug_close_on; + struct dentry *sdio_al_debug_root; + struct dentry *sdio_al_debug_lpm_on; + struct dentry *sdio_al_debug_data_on; + struct dentry *sdio_al_debug_close_on; + struct dentry *sdio_al_debug_info; + struct dentry *sdio_al_debug_log_buffers[MAX_NUM_OF_SDIO_DEVICES + 1]; +}; + +/* Polling time for the inactivity timer for devices that doesn't have + * a streaming channel + */ +#define SDIO_AL_POLL_TIME_NO_STREAMING 30 + +#define CHAN_TO_FUNC(x) ((x) + 2 - 1) + +/** + * Mailbox structure. + * The Mailbox is located on the SDIO-Client Function#1. + * The mailbox size is 128 bytes, which is one block. + * The mailbox allows the host ton: + * 1. Get the number of available bytes on the pipes. + * 2. Enable/Disable SDIO-Client interrupt, related to pipes. + * 3. Set the Threshold for generating interrupt. + * + */ +struct sdio_mailbox { + u32 pipe_bytes_threshold[SDIO_AL_MAX_PIPES]; /* Addr 0x1000 */ + + /* Mask USER interrupts generated towards host - Addr 0x1040 */ + u32 mask_irq_func_1:8; /* LSB */ + u32 mask_irq_func_2:8; + u32 mask_irq_func_3:8; + u32 mask_irq_func_4:8; + + u32 mask_irq_func_5:8; + u32 mask_irq_func_6:8; + u32 mask_irq_func_7:8; + u32 mask_mutex_irq:8; + + /* Mask PIPE interrupts generated towards host - Addr 0x1048 */ + u32 mask_eot_pipe_0_7:8; + u32 mask_thresh_above_limit_pipe_0_7:8; + u32 mask_overflow_pipe_0_7:8; + u32 mask_underflow_pipe_0_7:8; + + u32 mask_eot_pipe_8_15:8; + u32 mask_thresh_above_limit_pipe_8_15:8; + u32 mask_overflow_pipe_8_15:8; + u32 mask_underflow_pipe_8_15:8; + + /* Status of User interrupts generated towards host - Addr 0x1050 */ + u32 user_irq_func_1:8; + u32 user_irq_func_2:8; + u32 user_irq_func_3:8; + u32 user_irq_func_4:8; + + u32 user_irq_func_5:8; + u32 user_irq_func_6:8; + u32 user_irq_func_7:8; + u32 user_mutex_irq:8; + + /* Status of PIPE interrupts generated towards host */ + /* Note: All sources are cleared once they read. - Addr 0x1058 */ + u32 eot_pipe_0_7:8; + u32 thresh_above_limit_pipe_0_7:8; + u32 overflow_pipe_0_7:8; + u32 underflow_pipe_0_7:8; + + u32 eot_pipe_8_15:8; + u32 thresh_above_limit_pipe_8_15:8; + u32 overflow_pipe_8_15:8; + u32 underflow_pipe_8_15:8; + + u16 pipe_bytes_avail[SDIO_AL_MAX_PIPES]; +}; + +/** Track pending Rx Packet size */ +struct rx_packet_size { + u32 size; /* in bytes */ + struct list_head list; +}; + +#define PEER_SDIOC_SW_MAILBOX_SIGNATURE 0xFACECAFE +#define PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE 0x5D107E57 +#define PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE 0xDEADBEEF + +/* Allow support in old sdio version */ +#define PEER_SDIOC_OLD_VERSION_MAJOR 0x0002 +#define INVALID_SDIO_CHAN 0xFF + +/** + * Peer SDIO-Client software header. + */ +struct peer_sdioc_sw_header { + u32 signature; + u32 version; + u32 max_channels; + char channel_names[SDIO_AL_MAX_CHANNELS][PEER_CHANNEL_NAME_SIZE]; + u32 reserved[23]; +}; + +struct peer_sdioc_boot_sw_header { + u32 signature; + u32 version; + u32 boot_ch_num; + u32 reserved[29]; /* 32 - previous fields */ +}; + +/** + * Peer SDIO-Client software mailbox. + */ +struct peer_sdioc_sw_mailbox { + struct peer_sdioc_sw_header sw_header; + struct peer_sdioc_channel_config ch_config[SDIO_AL_MAX_CHANNELS]; +}; + +#define SDIO_AL_DEBUG_LOG_SIZE 3000 +struct sdio_al_local_log { + char buffer[SDIO_AL_DEBUG_LOG_SIZE]; + unsigned int buf_cur_pos; + spinlock_t log_lock; +}; + +#define SDIO_AL_DEBUG_TMP_LOG_SIZE 250 +static int sdio_al_log(struct sdio_al_local_log *, const char *fmt, ...); + +/** + * SDIO Abstraction Layer driver context. + * + * @pdata - + * @debug - + * @devices - an array of the the devices claimed by sdio_al + * @unittest_mode - a flag to indicate if sdio_al is in + * unittest mode + * @bootloader_dev - the device which is used for the + * bootloader + * @subsys_notif_handle - handle for modem restart + * notifications + * + */ +struct sdio_al { + struct sdio_al_local_log gen_log; + struct sdio_al_local_log device_log[MAX_NUM_OF_SDIO_DEVICES]; + struct sdio_al_platform_data *pdata; + struct sdio_al_debug debug; + struct sdio_al_device *devices[MAX_NUM_OF_SDIO_DEVICES]; + int unittest_mode; + struct sdio_al_device *bootloader_dev; + void *subsys_notif_handle; + int sdioc_major; + int skip_print_info; +}; + +struct sdio_al_work { + struct work_struct work; + struct sdio_al_device *sdio_al_dev; +}; + + +/** + * SDIO Abstraction Layer device context. + * + * @card - card claimed. + * + * @mailbox - A shadow of the SDIO-Client mailbox. + * + * @channel - Channels context. + * + * @workqueue - workqueue to read the mailbox and handle + * pending requests. Reading the mailbox should not happen + * in interrupt context. + * + * @work - work to submit to workqueue. + * + * @is_ready - driver is ready. + * + * @ask_mbox - Flag to request reading the mailbox, + * for different reasons. + * + * @wake_lock - Lock when can't sleep. + * + * @lpm_chan - Channel to use for LPM (low power mode) + * communication. + * + * @is_ok_to_sleep - Mark if driver is OK with going to sleep + * (no pending transactions). + * + * @inactivity_time - time allowed to be in inactivity before + * going to sleep + * + * @timer - timer to use for polling the mailbox. + * + * @poll_delay_msec - timer delay for polling the mailbox. + * + * @is_err - error detected. + * + * @signature - Context Validity Check. + * + * @flashless_boot_on - flag to indicate if sdio_al is in + * flshless boot mode + * + */ +struct sdio_al_device { + struct sdio_al_local_log *dev_log; + struct mmc_card *card; + struct mmc_host *host; + struct sdio_mailbox *mailbox; + struct sdio_channel channel[SDIO_AL_MAX_CHANNELS]; + + struct peer_sdioc_sw_header *sdioc_sw_header; + struct peer_sdioc_boot_sw_header *sdioc_boot_sw_header; + + struct workqueue_struct *workqueue; + struct sdio_al_work sdio_al_work; + struct sdio_al_work boot_work; + + int is_ready; + + wait_queue_head_t wait_mbox; + int ask_mbox; + int bootloader_done; + + struct wake_lock wake_lock; + int lpm_chan; + int is_ok_to_sleep; + unsigned long inactivity_time; + + struct timer_list timer; + u32 poll_delay_msec; + int is_timer_initialized; + + int is_err; + + u32 signature; + + unsigned int is_suspended; + + int flashless_boot_on; + int ch_close_supported; + int state; + int (*lpm_callback)(void *, int); + + int print_after_interrupt; + + u8 *rx_flush_buf; +}; + +/* + * Host operation: + * lower 16bits are operation code + * upper 16bits are operation state + */ +#define PEER_OPERATION(op_code , op_state) ((op_code) | ((op_state) << 16)) +#define GET_PEER_OPERATION_CODE(op) ((op) & 0xffff) +#define GET_PEER_OPERATION_STATE(op) ((op) >> 16) + +enum peer_op_code { + PEER_OP_CODE_CLOSE = 1 +}; + +enum peer_op_state { + PEER_OP_STATE_INIT = 0, + PEER_OP_STATE_START = 1 +}; + + +/* + * On the kernel command line specify + * sdio_al.debug_lpm_on=1 to enable the LPM debug messages + * By default the LPM debug messages are turned off + */ +static int debug_lpm_on; +module_param(debug_lpm_on, int, 0); + +/* + * On the kernel command line specify + * sdio_al.debug_data_on=1 to enable the DATA debug messages + * By default the DATA debug messages are turned off + */ +static int debug_data_on; +module_param(debug_data_on, int, 0); + +/* + * Enables / disables open close debug messages + */ +static int debug_close_on = 1; +module_param(debug_close_on, int, 0); + +/** The driver context */ +static struct sdio_al *sdio_al; + +/* Static functions declaration */ +static int enable_eot_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable); +static int enable_threshold_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable); +static void sdio_func_irq(struct sdio_func *func); +static void sdio_al_timer_handler(unsigned long data); +static int get_min_poll_time_msec(struct sdio_al_device *sdio_al_dev); +static u32 check_pending_rx_packet(struct sdio_channel *ch, u32 eot); +static u32 remove_handled_rx_packet(struct sdio_channel *ch); +static int set_pipe_threshold(struct sdio_al_device *sdio_al_dev, + int pipe_index, int threshold); +static int sdio_al_wake_up(struct sdio_al_device *sdio_al_dev, + u32 not_from_int, struct sdio_channel *ch); +static int sdio_al_client_setup(struct sdio_al_device *sdio_al_dev); +static int enable_mask_irq(struct sdio_al_device *sdio_al_dev, + int func_num, int enable, u8 bit_offset); +static int sdio_al_enable_func_retry(struct sdio_func *func, const char *name); +static void sdio_al_print_info(void); +static int sdio_read_internal(struct sdio_channel *ch, void *data, int len); +static int sdio_read_from_closed_ch(struct sdio_channel *ch, int len); +static void stop_and_del_timer(struct sdio_al_device *sdio_al_dev); + +#define SDIO_AL_ERR(func) \ + do { \ + printk_once(KERN_ERR MODULE_NAME \ + ":In Error state, ignore %s\n", \ + func); \ + sdio_al_print_info(); \ + } while (0) + +#ifdef CONFIG_DEBUG_FS +static int debug_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + sdio_al_print_info(); + return 1; +} + +const struct file_operations debug_info_ops = { + .open = debug_info_open, + .write = debug_info_write, +}; + +struct debugfs_blob_wrapper sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES + 1]; + +/* +* +* Trigger on/off for debug messages +* for trigger off the data messages debug level use: +* echo 0 > /sys/kernel/debugfs/sdio_al/debug_data_on +* for trigger on the data messages debug level use: +* echo 1 > /sys/kernel/debugfs/sdio_al/debug_data_on +* for trigger off the lpm messages debug level use: +* echo 0 > /sys/kernel/debugfs/sdio_al/debug_lpm_on +* for trigger on the lpm messages debug level use: +* echo 1 > /sys/kernel/debugfs/sdio_al/debug_lpm_on +*/ +static int sdio_al_debugfs_init(void) +{ + int i, blob_errs = 0; + + sdio_al->debug.sdio_al_debug_root = debugfs_create_dir("sdio_al", NULL); + if (!sdio_al->debug.sdio_al_debug_root) + return -ENOENT; + + sdio_al->debug.sdio_al_debug_lpm_on = debugfs_create_u8("debug_lpm_on", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al->debug.debug_lpm_on); + + sdio_al->debug.sdio_al_debug_data_on = debugfs_create_u8( + "debug_data_on", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al->debug.debug_data_on); + + sdio_al->debug.sdio_al_debug_close_on = debugfs_create_u8( + "debug_close_on", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al->debug.debug_close_on); + + sdio_al->debug.sdio_al_debug_info = debugfs_create_file( + "sdio_debug_info", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + NULL, + &debug_info_ops); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + char temp[18]; + + scnprintf(temp, 18, "sdio_al_log_dev_%d", i + 1); + sdio_al->debug.sdio_al_debug_log_buffers[i] = + debugfs_create_blob(temp, + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al_dbgfs_log[i]); + } + + sdio_al->debug.sdio_al_debug_log_buffers[MAX_NUM_OF_SDIO_DEVICES] = + debugfs_create_blob("sdio_al_gen_log", + S_IRUGO | S_IWUGO, + sdio_al->debug.sdio_al_debug_root, + &sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES]); + + for (i = 0; i < (MAX_NUM_OF_SDIO_DEVICES + 1); ++i) { + if (!sdio_al->debug.sdio_al_debug_log_buffers[i]) { + pr_err(MODULE_NAME ": Failed to create debugfs buffer" + " entry for " + "sdio_al->debug.sdio_al_debug_log_buffers[%d]", + i); + blob_errs = 1; + } + } + + if (blob_errs) { + for (i = 0; i < (MAX_NUM_OF_SDIO_DEVICES + 1); ++i) + if (sdio_al->debug.sdio_al_debug_log_buffers[i]) + debugfs_remove( + sdio_al-> + debug.sdio_al_debug_log_buffers[i]); + } + + + if ((!sdio_al->debug.sdio_al_debug_data_on) && + (!sdio_al->debug.sdio_al_debug_lpm_on) && + (!sdio_al->debug.sdio_al_debug_close_on) && + (!sdio_al->debug.sdio_al_debug_info) && + blob_errs) { + debugfs_remove(sdio_al->debug.sdio_al_debug_root); + sdio_al->debug.sdio_al_debug_root = NULL; + return -ENOENT; + } + + sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES].data = + sdio_al->gen_log.buffer; + sdio_al_dbgfs_log[MAX_NUM_OF_SDIO_DEVICES].size = + SDIO_AL_DEBUG_LOG_SIZE; + + return 0; +} + +static void sdio_al_debugfs_cleanup(void) +{ + int i; + + debugfs_remove(sdio_al->debug.sdio_al_debug_lpm_on); + debugfs_remove(sdio_al->debug.sdio_al_debug_data_on); + debugfs_remove(sdio_al->debug.sdio_al_debug_close_on); + debugfs_remove(sdio_al->debug.sdio_al_debug_info); + + for (i = 0; i < (MAX_NUM_OF_SDIO_DEVICES + 1); ++i) + debugfs_remove(sdio_al->debug.sdio_al_debug_log_buffers[i]); + + debugfs_remove(sdio_al->debug.sdio_al_debug_root); +} +#endif + +static int sdio_al_log(struct sdio_al_local_log *log, const char *fmt, ...) +{ + va_list args; + int r; + char *tp, *log_buf; + unsigned int *log_cur_pos; + struct timeval kt; + unsigned long flags; + static char sdio_al_log_tmp[SDIO_AL_DEBUG_TMP_LOG_SIZE]; + + spin_lock_irqsave(&log->log_lock, flags); + + kt = ktime_to_timeval(ktime_get()); + r = scnprintf(sdio_al_log_tmp, SDIO_AL_DEBUG_TMP_LOG_SIZE, + "[%8ld.%6ld] ", kt.tv_sec, kt.tv_usec); + + va_start(args, fmt); + r += vscnprintf(&sdio_al_log_tmp[r], (SDIO_AL_DEBUG_TMP_LOG_SIZE - r), + fmt, args); + va_end(args); + + log_buf = log->buffer; + log_cur_pos = &(log->buf_cur_pos); + + for (tp = sdio_al_log_tmp; tp < (sdio_al_log_tmp + r); tp++) { + log_buf[(*log_cur_pos)++] = *tp; + if ((*log_cur_pos) == SDIO_AL_DEBUG_LOG_SIZE) + *log_cur_pos = 0; + } + + spin_unlock_irqrestore(&log->log_lock, flags); + + return r; +} + +static int sdio_al_verify_func1(struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_dev\n", func); + return -ENODEV; + } + + if (sdio_al_dev->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "signature\n", func); + return -ENODEV; + } + + if (!sdio_al_dev->card) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "card\n", func); + return -ENODEV; + } + if (!sdio_al_dev->card->sdio_func[0]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "func1\n", func); + return -ENODEV; + } + return 0; +} + +static int sdio_al_claim_mutex(struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "device\n", func); + return -ENODEV; + } + + if (sdio_al_dev->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "device signature\n", func); + return -ENODEV; + } + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "host\n", func); + return -ENODEV; + } + + mmc_claim_host(sdio_al_dev->host); + + return 0; +} + +static int sdio_al_release_mutex(struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "device\n", func); + return -ENODEV; + } + + if (sdio_al_dev->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "device signature\n", func); + return -ENODEV; + } + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: NULL " + "host\n", func); + return -ENODEV; + } + + mmc_release_host(sdio_al_dev->host); + + return 0; +} + +static int sdio_al_claim_mutex_and_verify_dev( + struct sdio_al_device *sdio_al_dev, + char const *func) +{ + if (sdio_al_claim_mutex(sdio_al_dev, __func__)) + return -ENODEV; + + if (sdio_al_dev->state != CARD_INSERTED) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": %s: Invalid " + "device state %d\n", func, sdio_al_dev->state); + sdio_al_release_mutex(sdio_al_dev, __func__); + return -ENODEV; + } + + return 0; +} + +static void sdio_al_get_into_err_state(struct sdio_al_device *sdio_al_dev) +{ + if ((!sdio_al) || (!sdio_al_dev)) + return; + + sdio_al_dev->is_err = true; + sdio_al->debug.debug_data_on = 0; + sdio_al->debug.debug_lpm_on = 0; + sdio_al_print_info(); +} + +void sdio_al_register_lpm_cb(void *device_handle, + int(*lpm_callback)(void *, int)) +{ + struct sdio_al_device *sdio_al_dev = + (struct sdio_al_device *) device_handle; + + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - " + "device_handle is NULL\n", __func__); + return; + } + + if (lpm_callback) { + sdio_al_dev->lpm_callback = lpm_callback; + lpm_callback((void *)sdio_al_dev, + sdio_al_dev->is_ok_to_sleep); + } + + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": %s - device %d " + "registered for wakeup callback\n", __func__, + sdio_al_dev->host->index); +} + +void sdio_al_unregister_lpm_cb(void *device_handle) +{ + struct sdio_al_device *sdio_al_dev = + (struct sdio_al_device *) device_handle; + + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - " + "device_handle is NULL\n", __func__); + return; + } + + sdio_al_dev->lpm_callback = NULL; + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": %s - device %d " + "unregister for wakeup callback\n", __func__, + sdio_al_dev->host->index); +} + +static void sdio_al_vote_for_sleep(struct sdio_al_device *sdio_al_dev, + int is_vote_for_sleep) +{ + pr_debug(MODULE_NAME ": %s()", __func__); + + if (!sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - sdio_al_dev" + " is NULL\n", __func__); + return; + } + + if (is_vote_for_sleep) { + pr_debug(MODULE_NAME ": %s - sdio vote for Sleep", __func__); + wake_unlock(&sdio_al_dev->wake_lock); + } else { + pr_debug(MODULE_NAME ": %s - sdio vote against sleep", + __func__); + wake_lock(&sdio_al_dev->wake_lock); + } + + if (sdio_al_dev->lpm_callback != NULL) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": %s - " + "is_vote_for_sleep=%d for card#%d, " + "calling callback...", __func__, + is_vote_for_sleep, + sdio_al_dev->host->index); + sdio_al_dev->lpm_callback((void *)sdio_al_dev, + is_vote_for_sleep); + } +} + +/** + * Write SDIO-Client lpm information + * Should only be called with host claimed. + */ +static int write_lpm_info(struct sdio_al_device *sdio_al_dev) +{ + struct sdio_func *lpm_func = NULL; + int offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config)+ + sizeof(struct peer_sdioc_channel_config) * + sdio_al_dev->lpm_chan+ + offsetof(struct peer_sdioc_channel_config, is_host_ok_to_sleep); + int ret; + + if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Invalid " + "lpm_chan for card %d\n", + sdio_al_dev->host->index); + return -EINVAL; + } + + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[sdio_al_dev->lpm_chan+1]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL card or lpm_func\n"); + return -ENODEV; + } + lpm_func = sdio_al_dev->card->sdio_func[sdio_al_dev->lpm_chan+1]; + + pr_debug(MODULE_NAME ":write_lpm_info is_ok_to_sleep=%d, device %d\n", + sdio_al_dev->is_ok_to_sleep, + sdio_al_dev->host->index); + + ret = sdio_memcpy_toio(lpm_func, SDIOC_SW_MAILBOX_ADDR+offset, + &sdio_al_dev->is_ok_to_sleep, sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":failed to " + "write lpm info for card %d\n", + sdio_al_dev->host->index); + return ret; + } + + return 0; +} + +/* Set inactivity counter to intial value to allow clients come up */ +static inline void start_inactive_time(struct sdio_al_device *sdio_al_dev) +{ + sdio_al_dev->inactivity_time = jiffies + + msecs_to_jiffies(INITIAL_INACTIVITY_TIME_MSEC); +} + +static inline void restart_inactive_time(struct sdio_al_device *sdio_al_dev) +{ + sdio_al_dev->inactivity_time = jiffies + + msecs_to_jiffies(INACTIVITY_TIME_MSEC); +} + +static inline int is_inactive_time_expired(struct sdio_al_device *sdio_al_dev) +{ + return time_after(jiffies, sdio_al_dev->inactivity_time); +} + + +static int is_user_irq_enabled(struct sdio_al_device *sdio_al_dev, + int func_num) +{ + int ret = 0; + struct sdio_func *func1; + u32 user_irq = 0; + u32 addr = 0; + u32 offset = 0; + u32 masked_user_irq = 0; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return 0; + func1 = sdio_al_dev->card->sdio_func[0]; + + if (func_num < 4) { + addr = FUNC_1_4_USER_IRQ_ADDR; + offset = func_num * 8; + } else { + addr = FUNC_5_7_USER_IRQ_ADDR; + offset = (func_num - 4) * 8; + } + + user_irq = sdio_readl(func1, addr, &ret); + if (ret) { + pr_debug(MODULE_NAME ":read_user_irq fail\n"); + return 0; + } + + masked_user_irq = (user_irq >> offset) && 0xFF; + if (masked_user_irq == 0x1) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":user_irq " + "enabled\n"); + return 1; + } + + return 0; +} + +static void sdio_al_sleep(struct sdio_al_device *sdio_al_dev, + struct mmc_host *host) +{ + int i; + + /* Go to sleep */ + pr_debug(MODULE_NAME ":Inactivity timer expired." + " Going to sleep\n"); + /* Stop mailbox timer */ + stop_and_del_timer(sdio_al_dev); + /* Make sure we get interrupt for non-packet-mode right away */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSED)) { + pr_debug(MODULE_NAME ":continue for channel %s in" + " state %d\n", ch->name, ch->state); + continue; + } + if (ch->is_packet_mode == false) { + ch->read_threshold = LOW_LATENCY_THRESHOLD; + set_pipe_threshold(sdio_al_dev, + ch->rx_pipe_index, + ch->read_threshold); + } + } + /* Prevent modem to go to sleep until we get the PROG_DONE on + the dummy CMD52 */ + msmsdcc_set_pwrsave(sdio_al_dev->host, 0); + /* Mark HOST_OK_TOSLEEP */ + sdio_al_dev->is_ok_to_sleep = 1; + write_lpm_info(sdio_al_dev); + + msmsdcc_lpm_enable(host); + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":Finished sleep sequence" + " for card %d. Sleep now.\n", + sdio_al_dev->host->index); + /* Release wakelock */ + sdio_al_vote_for_sleep(sdio_al_dev, 1); +} + + +/** + * Read SDIO-Client Mailbox from Function#1.thresh_pipe + * + * The mailbox contain the bytes available per pipe, + * and the End-Of-Transfer indication per pipe (if available). + * + * WARNING: Each time the Mailbox is read from the client, the + * read_bytes_avail is incremented with another pending + * transfer. Therefore, a pending rx-packet should be added to a + * list before the next read of the mailbox. + * + * This function should run from a workqueue context since it + * notifies the clients. + * + * This function assumes that sdio_al_claim_mutex was called before + * calling it. + * + */ +static int read_mailbox(struct sdio_al_device *sdio_al_dev, int from_isr) +{ + int ret; + struct sdio_func *func1 = NULL; + struct sdio_mailbox *mailbox = sdio_al_dev->mailbox; + struct mmc_host *host = sdio_al_dev->host; + u32 new_write_avail = 0; + u32 old_write_avail = 0; + u32 any_read_avail = 0; + u32 any_write_pending = 0; + int i; + u32 rx_notify_bitmask = 0; + u32 tx_notify_bitmask = 0; + u32 eot_pipe = 0; + u32 thresh_pipe = 0; + u32 overflow_pipe = 0; + u32 underflow_pipe = 0; + u32 thresh_intr_mask = 0; + int is_closing = 0; + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + return 0; + } + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = sdio_al_dev->card->sdio_func[0]; + + pr_debug(MODULE_NAME ":start %s from_isr = %d for card %d.\n" + , __func__, from_isr, sdio_al_dev->host->index); + + pr_debug(MODULE_NAME ":before sdio_memcpy_fromio.\n"); + memset(mailbox, 0, sizeof(struct sdio_mailbox)); + ret = sdio_memcpy_fromio(func1, mailbox, + HW_MAILBOX_ADDR, sizeof(*mailbox)); + pr_debug(MODULE_NAME ":after sdio_memcpy_fromio.\n"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to read " + "Mailbox for card %d, goto error state\n", + sdio_al_dev->host->index); + sdio_al_get_into_err_state(sdio_al_dev); + goto exit_err; + } + + eot_pipe = (mailbox->eot_pipe_0_7) | + (mailbox->eot_pipe_8_15<<8); + thresh_pipe = (mailbox->thresh_above_limit_pipe_0_7) | + (mailbox->thresh_above_limit_pipe_8_15<<8); + + overflow_pipe = (mailbox->overflow_pipe_0_7) | + (mailbox->overflow_pipe_8_15<<8); + underflow_pipe = mailbox->underflow_pipe_0_7 | + (mailbox->underflow_pipe_8_15<<8); + thresh_intr_mask = + (mailbox->mask_thresh_above_limit_pipe_0_7) | + (mailbox->mask_thresh_above_limit_pipe_8_15<<8); + + if (overflow_pipe || underflow_pipe) + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Mailbox ERROR " + "overflow=0x%x, underflow=0x%x\n", + overflow_pipe, underflow_pipe); + + /* In case of modem reset we would like to read the daya from the modem + to clear the interrupts but do not process it */ + if (sdio_al_dev->state != CARD_INSERTED) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_al_device" + " (card %d) is in invalid state %d\n", + sdio_al_dev->host->index, + sdio_al_dev->state); + return -ENODEV; + } + + pr_debug(MODULE_NAME ":card %d: eot=0x%x, thresh=0x%x\n", + sdio_al_dev->host->index, + eot_pipe, thresh_pipe); + + /* Scan for Rx Packets available and update read available bytes */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + u32 old_read_avail; + u32 read_avail; + u32 new_packet_size = 0; + + if (ch->state == SDIO_CHANNEL_STATE_CLOSING) + is_closing = true; /* used to prevent sleep */ + + old_read_avail = ch->read_avail; + read_avail = mailbox->pipe_bytes_avail[ch->rx_pipe_index]; + + if ((ch->state == SDIO_CHANNEL_STATE_CLOSED) && + (read_avail > 0)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":%s: Invalid read_avail 0x%x, for CLOSED ch %s\n", + __func__, read_avail, ch->name); + sdio_read_from_closed_ch(ch, read_avail); + } + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSING)) + continue; + + if (read_avail > INVALID_DATA_AVAILABLE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":Invalid read_avail 0x%x for pipe %d\n", + read_avail, ch->rx_pipe_index); + continue; + } + any_read_avail |= read_avail | old_read_avail; + ch->statistics.last_any_read_avail = any_read_avail; + ch->statistics.last_read_avail = read_avail; + ch->statistics.last_old_read_avail = old_read_avail; + + if (ch->is_packet_mode) { + if ((eot_pipe & (1<rx_pipe_index)) && + sdio_al_dev->print_after_interrupt) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME + ":Interrupt on ch %s, " + "card %d", ch->name, + sdio_al_dev->host->index); + } + new_packet_size = check_pending_rx_packet(ch, eot_pipe); + } else { + if ((thresh_pipe & (1<rx_pipe_index)) && + sdio_al_dev->print_after_interrupt) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME + ":Interrupt on ch %s, " + "card %d", ch->name, + sdio_al_dev->host->index); + } + ch->read_avail = read_avail; + + /* + * Restore default thresh for non packet channels. + * in case it IS low latency channel then read_threshold + * and def_read_threshold are both + * LOW_LATENCY_THRESHOLD + */ + if ((ch->read_threshold != ch->def_read_threshold) && + (read_avail >= ch->threshold_change_cnt)) { + if (!ch->is_low_latency_ch) { + ch->read_threshold = + ch->def_read_threshold; + set_pipe_threshold(sdio_al_dev, + ch->rx_pipe_index, + ch->read_threshold); + } + } + } + + if ((ch->is_packet_mode) && (new_packet_size > 0)) { + rx_notify_bitmask |= (1<num); + ch->statistics.total_notifs++; + } + + if ((!ch->is_packet_mode) && (ch->read_avail > 0) && + (old_read_avail == 0)) { + rx_notify_bitmask |= (1<num); + ch->statistics.total_notifs++; + } + } + sdio_al_dev->print_after_interrupt = 0; + + /* Update Write available */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSING)) + continue; + + new_write_avail = mailbox->pipe_bytes_avail[ch->tx_pipe_index]; + + if (new_write_avail > INVALID_DATA_AVAILABLE) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":Invalid write_avail 0x%x for pipe %d\n", + new_write_avail, ch->tx_pipe_index); + continue; + } + + old_write_avail = ch->write_avail; + ch->write_avail = new_write_avail; + + if ((old_write_avail <= ch->min_write_avail) && + (new_write_avail >= ch->min_write_avail)) + tx_notify_bitmask |= (1<num); + + /* There is not enough write avail for this channel. + We need to keep reading mailbox to wait for the appropriate + write avail and cannot sleep. Ignore SMEM channel that has + only one direction. */ + if (strncmp(ch->name, "SDIO_SMEM", CHANNEL_NAME_SIZE)) + any_write_pending |= + (new_write_avail < ch->ch_config.max_tx_threshold); + } + /* notify clients */ + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) || + (ch->notify == NULL)) + continue; + + if (rx_notify_bitmask & (1<num)) + ch->notify(ch->priv, + SDIO_EVENT_DATA_READ_AVAIL); + + if (tx_notify_bitmask & (1<num)) + ch->notify(ch->priv, + SDIO_EVENT_DATA_WRITE_AVAIL); + } + + + if ((rx_notify_bitmask == 0) && (tx_notify_bitmask == 0) && + !any_read_avail && !any_write_pending) { + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":Nothing to " + "Notify for card %d, is_closing=%d\n", + sdio_al_dev->host->index, is_closing); + if (is_closing) + restart_inactive_time(sdio_al_dev); + else if (is_inactive_time_expired(sdio_al_dev)) + sdio_al_sleep(sdio_al_dev, host); + } else { + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":Notify bitmask" + " for card %d rx=0x%x, tx=0x%x.\n", + sdio_al_dev->host->index, + rx_notify_bitmask, tx_notify_bitmask); + /* Restart inactivity timer if any activity on the channel */ + restart_inactive_time(sdio_al_dev); + } + + pr_debug(MODULE_NAME ":end %s.\n", __func__); + +exit_err: + return ret; +} + +/** + * Check pending rx packet when reading the mailbox. + */ +static u32 check_pending_rx_packet(struct sdio_channel *ch, u32 eot) +{ + u32 rx_pending; + u32 rx_avail; + u32 new_packet_size = 0; + struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev; + + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL sdio_al_dev" + " for channel %s\n", ch->name); + return -EINVAL; + } + + mutex_lock(&ch->ch_lock); + + rx_pending = ch->rx_pending_bytes; + rx_avail = sdio_al_dev->mailbox->pipe_bytes_avail[ch->rx_pipe_index]; + + pr_debug(MODULE_NAME ":pipe %d of card %d rx_avail=0x%x, " + "rx_pending=0x%x\n", + ch->rx_pipe_index, sdio_al_dev->host->index, rx_avail, + rx_pending); + + + /* new packet detected */ + if (eot & (1<rx_pipe_index)) { + struct rx_packet_size *p = NULL; + new_packet_size = rx_avail - rx_pending; + + if ((rx_avail <= rx_pending)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Invalid new packet size." + " rx_avail=%d.\n", rx_avail); + new_packet_size = 0; + goto exit_err; + } + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": failed to allocate item for " + "rx_pending list. rx_avail=%d, " + "rx_pending=%d.\n", + rx_avail, rx_pending); + new_packet_size = 0; + goto exit_err; + } + p->size = new_packet_size; + /* Add new packet as last */ + list_add_tail(&p->list, &ch->rx_size_list_head); + ch->rx_pending_bytes += new_packet_size; + + if (ch->read_avail == 0) + ch->read_avail = new_packet_size; + } + +exit_err: + mutex_unlock(&ch->ch_lock); + + return new_packet_size; +} + + + +/** + * Remove first pending packet from the list. + */ +static u32 remove_handled_rx_packet(struct sdio_channel *ch) +{ + struct rx_packet_size *p = NULL; + + mutex_lock(&ch->ch_lock); + + ch->rx_pending_bytes -= ch->read_avail; + + if (!list_empty(&ch->rx_size_list_head)) { + p = list_first_entry(&ch->rx_size_list_head, + struct rx_packet_size, list); + list_del(&p->list); + kfree(p); + } else { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: ch " + "%s: unexpected empty list!!\n", + __func__, ch->name); + } + + if (list_empty(&ch->rx_size_list_head)) { + ch->read_avail = 0; + } else { + p = list_first_entry(&ch->rx_size_list_head, + struct rx_packet_size, list); + ch->read_avail = p->size; + } + + mutex_unlock(&ch->ch_lock); + + return ch->read_avail; +} + + +/** + * Bootloader worker function. + * + * @note: clear the bootloader_done flag only after reading the + * mailbox, to ignore more requests while reading the mailbox. + */ +static void boot_worker(struct work_struct *work) +{ + int ret = 0; + int func_num = 0; + int i; + struct sdio_al_device *sdio_al_dev = NULL; + struct sdio_al_work *sdio_al_work = container_of(work, + struct sdio_al_work, + work); + + if (sdio_al_work == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_work\n", __func__); + return; + } + + sdio_al_dev = sdio_al_work->sdio_al_dev; + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_dev\n", __func__); + return; + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":Bootloader Worker Started" + ", wait for bootloader_done event..\n"); + wait_event(sdio_al_dev->wait_mbox, + sdio_al_dev->bootloader_done); + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":Got bootloader_done " + "event..\n"); + /* Do polling until MDM is up */ + for (i = 0; i < 5000; ++i) { + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + if (is_user_irq_enabled(sdio_al_dev, func_num)) { + sdio_al_release_mutex(sdio_al_dev, __func__); + sdio_al_dev->bootloader_done = 0; + ret = sdio_al_client_setup(sdio_al_dev); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": sdio_al_client_setup failed, " + "for card %d ret=%d\n", + sdio_al_dev->host->index, ret); + sdio_al_get_into_err_state(sdio_al_dev); + } + goto done; + } + sdio_al_release_mutex(sdio_al_dev, __func__); + msleep(100); + } + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Timeout waiting for " + "user_irq for card %d\n", + sdio_al_dev->host->index); + sdio_al_get_into_err_state(sdio_al_dev); + +done: + pr_debug(MODULE_NAME ":Boot Worker for card %d Exit!\n", + sdio_al_dev->host->index); +} + +/** + * Worker function. + * + * @note: clear the ask_mbox flag only after + * reading the mailbox, to ignore more requests while + * reading the mailbox. + */ +static void worker(struct work_struct *work) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + struct sdio_al_work *sdio_al_work = container_of(work, + struct sdio_al_work, + work); + if (sdio_al_work == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": worker: NULL " + "sdio_al_work\n"); + return; + } + + sdio_al_dev = sdio_al_work->sdio_al_dev; + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": worker: NULL " + "sdio_al_dev\n"); + return; + } + pr_debug(MODULE_NAME ":Worker Started..\n"); + while ((sdio_al_dev->is_ready) && (ret == 0)) { + pr_debug(MODULE_NAME ":Wait for read mailbox request..\n"); + wait_event(sdio_al_dev->wait_mbox, sdio_al_dev->ask_mbox); + if (!sdio_al_dev->is_ready) + break; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + break; + if (sdio_al_dev->is_ok_to_sleep) { + ret = sdio_al_wake_up(sdio_al_dev, 1, NULL); + if (ret) { + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + } + ret = read_mailbox(sdio_al_dev, false); + sdio_al_release_mutex(sdio_al_dev, __func__); + sdio_al_dev->ask_mbox = false; + } + + pr_debug(MODULE_NAME ":Worker Exit!\n"); +} + +/** + * Write command using CMD54 rather than CMD53. + * Writing with CMD54 generate EOT interrupt at the + * SDIO-Client. + * Based on mmc_io_rw_extended() + */ +static int sdio_write_cmd54(struct mmc_card *card, unsigned fn, + unsigned addr, const u8 *buf, + unsigned blocks, unsigned blksz) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + int incr_addr = 1; /* MUST */ + int write = 1; + + BUG_ON(!card); + BUG_ON(fn > 7); + BUG_ON(blocks == 1 && blksz > 512); + WARN_ON(blocks == 0); + WARN_ON(blksz == 0); + + write = true; + pr_debug(MODULE_NAME ":sdio_write_cmd54()" + "fn=%d,buf=0x%x,blocks=%d,blksz=%d\n", + fn, (u32) buf, blocks, blksz); + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_IO_RW_EXTENDED_QCOM; + + cmd.arg = write ? 0x80000000 : 0x00000000; + cmd.arg |= fn << 28; + cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; + cmd.arg |= addr << 9; + if (blocks == 1 && blksz <= 512) + cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ + else + cmd.arg |= 0x08000000 | blocks; /* block mode */ + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + data.blksz = blksz; + data.blocks = blocks; + data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, buf, blksz * blocks); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + if (mmc_host_is_spi(card->host)) { + /* host driver already reported errors */ + } else { + if (cmd.resp[0] & R5_ERROR) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME + ":%s: R5_ERROR for card %d", + __func__, card->host->index); + return -EIO; + } + if (cmd.resp[0] & R5_FUNCTION_NUMBER) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME + ":%s: R5_FUNCTION_NUMBER for card %d", + __func__, card->host->index); + return -EINVAL; + } + if (cmd.resp[0] & R5_OUT_OF_RANGE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME + ":%s: R5_OUT_OF_RANGE for card %d", + __func__, card->host->index); + return -ERANGE; + } + } + + return 0; +} + + +/** + * Write data to channel. + * Handle different data size types. + * + */ +static int sdio_ch_write(struct sdio_channel *ch, const u8 *buf, u32 len) +{ + int ret = 0; + unsigned blksz = ch->func->cur_blksize; + int blocks = len / blksz; + int remain_bytes = len % blksz; + struct mmc_card *card = NULL; + u32 fn = ch->func->num; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "channel\n", __func__); + return -ENODEV; + } + + if (!ch->sdio_al_dev) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "sdio_al_dev\n", __func__); + return -ENODEV; + } + + if (len == 0) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":channel " + "%s trying to write 0 bytes\n", ch->name); + return -EINVAL; + } + + card = ch->func->card; + + if (remain_bytes) { + /* CMD53 */ + if (blocks) { + ret = sdio_memcpy_toio(ch->func, PIPE_TX_FIFO_ADDR, + (void *) buf, blocks*blksz); + if (ret != 0) { + sdio_al_loge(ch->sdio_al_dev->dev_log, + MODULE_NAME ":%s: sdio_memcpy_toio " + "failed for channel %s\n", + __func__, ch->name); + sdio_al_get_into_err_state(ch->sdio_al_dev); + return ret; + } + } + + buf += (blocks*blksz); + + ret = sdio_write_cmd54(card, fn, PIPE_TX_FIFO_ADDR, + buf, 1, remain_bytes); + } else { + ret = sdio_write_cmd54(card, fn, PIPE_TX_FIFO_ADDR, + buf, blocks, blksz); + } + + if (ret != 0) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "sdio_write_cmd54 failed for channel %s\n", + __func__, ch->name); + ch->sdio_al_dev->is_err = true; + return ret; + } + + return ret; +} + +static int sdio_al_bootloader_completed(void) +{ + int i; + + pr_debug(MODULE_NAME ":sdio_al_bootloader_completed was called\n"); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + struct sdio_al_device *dev = NULL; + if (sdio_al->devices[i] == NULL) + continue; + dev = sdio_al->devices[i]; + dev->bootloader_done = 1; + wake_up(&dev->wait_mbox); + } + + return 0; +} + +static int sdio_al_wait_for_bootloader_comp(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + /* + * Enable function 0 interrupt mask to allow 9k to raise this interrupt + * in power-up. When sdio_downloader will notify its completion + * we will poll on this interrupt to wait for 9k power-up + */ + ret = enable_mask_irq(sdio_al_dev, 0, 1, 0); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Enable_mask_irq for card %d failed, " + "ret=%d\n", + sdio_al_dev->host->index, ret); + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; + } + + sdio_al_release_mutex(sdio_al_dev, __func__); + + /* + * Start bootloader worker that will wait for the bootloader + * completion + */ + sdio_al_dev->boot_work.sdio_al_dev = sdio_al_dev; + INIT_WORK(&sdio_al_dev->boot_work.work, boot_worker); + sdio_al_dev->bootloader_done = 0; + queue_work(sdio_al_dev->workqueue, &sdio_al_dev->boot_work.work); + + return 0; +} + +static int sdio_al_bootloader_setup(void) +{ + int ret = 0; + struct sdio_al_device *bootloader_dev = sdio_al->bootloader_dev; + struct sdio_func *func1 = NULL; + + if (sdio_al_claim_mutex_and_verify_dev(bootloader_dev, __func__)) + return -ENODEV; + + if (bootloader_dev->flashless_boot_on) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":Already " + "in boot process.\n"); + sdio_al_release_mutex(bootloader_dev, __func__); + return 0; + } + + bootloader_dev->sdioc_boot_sw_header + = kzalloc(sizeof(*bootloader_dev->sdioc_boot_sw_header), + GFP_KERNEL); + if (bootloader_dev->sdioc_boot_sw_header == NULL) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":fail to " + "allocate sdioc boot sw header.\n"); + sdio_al_release_mutex(bootloader_dev, __func__); + return -ENOMEM; + } + + if (sdio_al_verify_func1(bootloader_dev, __func__)) { + sdio_al_release_mutex(bootloader_dev, __func__); + goto exit_err; + } + func1 = bootloader_dev->card->sdio_func[0]; + + ret = sdio_memcpy_fromio(func1, + bootloader_dev->sdioc_boot_sw_header, + SDIOC_SW_HEADER_ADDR, + sizeof(struct peer_sdioc_boot_sw_header)); + if (ret) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":fail to " + "read sdioc boot sw header.\n"); + sdio_al_release_mutex(bootloader_dev, __func__); + goto exit_err; + } + + if (bootloader_dev->sdioc_boot_sw_header->signature != + (u32) PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ":invalid " + "mailbox signature 0x%x.\n", + bootloader_dev->sdioc_boot_sw_header->signature); + sdio_al_release_mutex(bootloader_dev, __func__); + ret = -EINVAL; + goto exit_err; + } + + /* Upper byte has to be equal - no backward compatibility for unequal */ + if ((bootloader_dev->sdioc_boot_sw_header->version >> 16) != + (sdio_al->pdata->peer_sdioc_boot_version_major)) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME ": HOST(0x%x)" + " and CLIENT(0x%x) SDIO_AL BOOT VERSION don't match\n", + ((sdio_al->pdata->peer_sdioc_boot_version_major<<16)+ + sdio_al->pdata->peer_sdioc_boot_version_minor), + bootloader_dev->sdioc_boot_sw_header->version); + sdio_al_release_mutex(bootloader_dev, __func__); + ret = -EIO; + goto exit_err; + } + + sdio_al_logi(bootloader_dev->dev_log, MODULE_NAME ": SDIOC BOOT SW " + "version 0x%x\n", + bootloader_dev->sdioc_boot_sw_header->version); + + bootloader_dev->flashless_boot_on = true; + + sdio_al_release_mutex(bootloader_dev, __func__); + + ret = sdio_al_wait_for_bootloader_comp(bootloader_dev); + if (ret) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME + ": sdio_al_wait_for_bootloader_comp failed, " + "err=%d\n", ret); + goto exit_err; + } + + ret = sdio_downloader_setup(bootloader_dev->card, 1, + bootloader_dev->sdioc_boot_sw_header->boot_ch_num, + sdio_al_bootloader_completed); + + if (ret) { + sdio_al_loge(bootloader_dev->dev_log, MODULE_NAME + ": sdio_downloader_setup failed, err=%d\n", ret); + goto exit_err; + } + + sdio_al_logi(bootloader_dev->dev_log, MODULE_NAME ":In Flashless boot," + " waiting for its completion\n"); + + +exit_err: + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":free " + "sdioc_boot_sw_header.\n"); + kfree(bootloader_dev->sdioc_boot_sw_header); + bootloader_dev->sdioc_boot_sw_header = NULL; + bootloader_dev = NULL; + + return ret; +} + + +/** + * Read SDIO-Client software header + * + */ +static int read_sdioc_software_header(struct sdio_al_device *sdio_al_dev, + struct peer_sdioc_sw_header *header) +{ + int ret; + int i; + int test_version = 0; + int sdioc_test_version = 0; + struct sdio_func *func1 = NULL; + + pr_debug(MODULE_NAME ":reading sdioc sw header.\n"); + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + + func1 = sdio_al_dev->card->sdio_func[0]; + + ret = sdio_memcpy_fromio(func1, header, + SDIOC_SW_HEADER_ADDR, sizeof(*header)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":fail to read " + "sdioc sw header.\n"); + goto exit_err; + } + + if (header->signature == (u32)PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":SDIOC SW " + "unittest signature. 0x%x\n", + header->signature); + sdio_al->unittest_mode = true; + /* Verify test code compatibility with the modem */ + sdioc_test_version = (header->version & 0xFF00) >> 8; + test_version = sdio_al->pdata->peer_sdioc_version_minor >> 8; + if (test_version != sdioc_test_version) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": HOST(0x%x) and CLIENT(0x%x) " + "testing VERSION don't match\n", + test_version, + sdioc_test_version); + msleep(500); + BUG(); + } + } + + if ((header->signature != (u32) PEER_SDIOC_SW_MAILBOX_SIGNATURE) && + (header->signature != (u32) PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":SDIOC SW " + "invalid signature. 0x%x\n", header->signature); + goto exit_err; + } + /* Upper byte has to be equal - no backward compatibility for unequal */ + sdio_al->sdioc_major = header->version >> 16; + if (sdio_al->pdata->allow_sdioc_version_major_2) { + if ((sdio_al->sdioc_major != + sdio_al->pdata->peer_sdioc_version_major) && + (sdio_al->sdioc_major != PEER_SDIOC_OLD_VERSION_MAJOR)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": HOST(0x%x) and CLIENT(0x%x) " + "SDIO_AL VERSION don't match\n", + ((sdio_al->pdata->peer_sdioc_version_major<<16)+ + sdio_al->pdata->peer_sdioc_version_minor), + header->version); + goto exit_err; + } + } else { + if (sdio_al->sdioc_major != + sdio_al->pdata->peer_sdioc_version_major) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": HOST(0x%x) and CLIENT(0x%x) " + "SDIO_AL VERSION don't match\n", + ((sdio_al->pdata->peer_sdioc_version_major<<16)+ + sdio_al->pdata->peer_sdioc_version_minor), + header->version); + goto exit_err; + } + } + sdio_al_dev->ch_close_supported = (header->version & 0x000F) >= + (sdio_al->pdata->peer_sdioc_version_minor & 0xF); + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":SDIOC SW version 0x%x," + " sdio_al major 0x%x minor 0x%x\n", header->version, + sdio_al->sdioc_major, + sdio_al->pdata->peer_sdioc_version_minor); + + sdio_al_dev->flashless_boot_on = false; + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + /* Set default values */ + ch->read_threshold = DEFAULT_READ_THRESHOLD; + ch->write_threshold = DEFAULT_WRITE_THRESHOLD; + ch->min_write_avail = DEFAULT_MIN_WRITE_THRESHOLD; + ch->is_packet_mode = true; + ch->peer_tx_buf_size = DEFAULT_PEER_TX_BUF_SIZE; + ch->poll_delay_msec = 0; + + ch->num = i; + ch->func = NULL; + ch->rx_pipe_index = ch->num*2; + ch->tx_pipe_index = ch->num*2+1; + + memset(ch->name, 0, sizeof(ch->name)); + + if (header->channel_names[i][0]) { + memcpy(ch->name, SDIO_PREFIX, + strlen(SDIO_PREFIX)); + memcpy(ch->name + strlen(SDIO_PREFIX), + header->channel_names[i], + PEER_CHANNEL_NAME_SIZE); + + ch->state = SDIO_CHANNEL_STATE_IDLE; + ch->sdio_al_dev = sdio_al_dev; + if (sdio_al_dev->card->sdio_func[ch->num+1]) { + ch->func = + sdio_al_dev->card->sdio_func[ch->num+1]; + } else { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL func for channel %s\n", + ch->name); + goto exit_err; + } + } else { + ch->state = SDIO_CHANNEL_STATE_INVALID; + } + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":Channel=%s, " + "state=%d\n", ch->name, ch->state); + } + + return 0; + +exit_err: + sdio_al_get_into_err_state(sdio_al_dev); + memset(header, 0, sizeof(*header)); + + return -EIO; +} + +/** + * Read SDIO-Client channel configuration + * + */ +static int read_sdioc_channel_config(struct sdio_channel *ch) +{ + int ret; + struct peer_sdioc_sw_mailbox *sw_mailbox = NULL; + struct peer_sdioc_channel_config *ch_config = NULL; + struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev; + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL sdio_al_dev" + " for channel %s\n", ch->name); + return -EINVAL; + } + + if (sdio_al_dev->sdioc_sw_header->version == 0) + return -1; + + pr_debug(MODULE_NAME ":reading sw mailbox %s channel.\n", ch->name); + + sw_mailbox = kzalloc(sizeof(*sw_mailbox), GFP_KERNEL); + if (sw_mailbox == NULL) + return -ENOMEM; + + ret = sdio_memcpy_fromio(ch->func, sw_mailbox, + SDIOC_SW_MAILBOX_ADDR, sizeof(*sw_mailbox)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":fail to read " + "sw mailbox.\n"); + goto exit_err; + } + + ch_config = &sw_mailbox->ch_config[ch->num]; + memcpy(&ch->ch_config, ch_config, + sizeof(struct peer_sdioc_channel_config)); + + if (!ch_config->is_ready) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sw mailbox " + "channel not ready.\n"); + goto exit_err; + } + + ch->read_threshold = LOW_LATENCY_THRESHOLD; + ch->is_low_latency_ch = ch_config->is_low_latency_ch; + /* Threshold on 50% of the maximum size , sdioc uses double-buffer */ + ch->write_threshold = (ch_config->max_tx_threshold * 5) / 10; + ch->threshold_change_cnt = ch->ch_config.max_rx_threshold - + ch->read_threshold + THRESHOLD_CHANGE_EXTRA_BYTES; + + if (ch->is_low_latency_ch) + ch->def_read_threshold = LOW_LATENCY_THRESHOLD; + else /* Aggregation up to 90% of the maximum size */ + ch->def_read_threshold = (ch_config->max_rx_threshold * 9) / 10; + + ch->is_packet_mode = ch_config->is_packet_mode; + if (!ch->is_packet_mode) { + ch->poll_delay_msec = DEFAULT_POLL_DELAY_NOPACKET_MSEC; + ch->min_write_avail = DEFAULT_MIN_WRITE_THRESHOLD_STREAMING; + } + /* The max_packet_size is set by the modem in version 3 and on */ + if (sdio_al->sdioc_major > PEER_SDIOC_OLD_VERSION_MAJOR) + ch->min_write_avail = ch_config->max_packet_size; + + if (ch->min_write_avail > ch->write_threshold) + ch->min_write_avail = ch->write_threshold; + + CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":ch %s " + "read_threshold=%d, write_threshold=%d," + " min_write_avail=%d, max_rx_threshold=%d," + " max_tx_threshold=%d\n", ch->name, ch->read_threshold, + ch->write_threshold, ch->min_write_avail, + ch_config->max_rx_threshold, + ch_config->max_tx_threshold); + + ch->peer_tx_buf_size = ch_config->tx_buf_size; + + kfree(sw_mailbox); + + return 0; + +exit_err: + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":Reading SW Mailbox " + "error.\n"); + kfree(sw_mailbox); + + return -1; +} + + +/** + * Enable/Disable EOT interrupt of a pipe. + * + */ +static int enable_eot_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable) +{ + int ret = 0; + struct sdio_func *func1; + u32 mask; + u32 pipe_mask; + u32 addr; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = sdio_al_dev->card->sdio_func[0]; + + if (pipe_index < 8) { + addr = PIPES_0_7_IRQ_MASK_ADDR; + pipe_mask = (1<card->sdio_func[0]; + + if (func_num < 4) { + addr = FUNC_1_4_MASK_IRQ_ADDR; + offset = func_num * 8 + bit_offset; + } else { + addr = FUNC_5_7_MASK_IRQ_ADDR; + offset = (func_num - 4) * 8 + bit_offset; + } + + func_mask = 1<dev_log, MODULE_NAME ": " + "enable_mask_irq fail\n"); + goto exit_err; + } + + if (enable) + mask &= (~func_mask); /* 0 = enable */ + else + mask |= (func_mask); /* 1 = disable */ + + pr_debug(MODULE_NAME ":enable_mask_irq, writing mask = 0x%x\n", mask); + + sdio_writel(func1, mask, addr, &ret); + +exit_err: + return ret; +} + +/** + * Enable/Disable Threshold interrupt of a pipe. + * + */ +static int enable_threshold_interrupt(struct sdio_al_device *sdio_al_dev, + int pipe_index, int enable) +{ + int ret = 0; + struct sdio_func *func1; + u32 mask; + u32 pipe_mask; + u32 addr; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = sdio_al_dev->card->sdio_func[0]; + + if (pipe_index < 8) { + addr = PIPES_0_7_IRQ_MASK_ADDR; + pipe_mask = (1<card->sdio_func[0]; + + sdio_writel(func1, threshold, + PIPES_THRESHOLD_ADDR+pipe_index*4, &ret); + if (ret) + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "set_pipe_threshold err=%d\n", -ret); + + return ret; +} + +/** + * Enable func w/ retries + * + */ +static int sdio_al_enable_func_retry(struct sdio_func *func, const char *name) +{ + int ret, i; + for (i = 0; i < 200; i++) { + ret = sdio_enable_func(func); + if (ret) { + pr_debug(MODULE_NAME ":retry enable %s func#%d " + "ret=%d\n", + name, func->num, ret); + msleep(10); + } else + break; + } + + return ret; +} + +/** + * Open Channel + * + * 1. Init Channel Context. + * 2. Init the Channel SDIO-Function. + * 3. Init the Channel Pipes on Mailbox. + */ +static int open_channel(struct sdio_channel *ch) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = ch->sdio_al_dev; + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL " + "sdio_al_dev for channel %s\n", ch->name); + return -EINVAL; + } + + /* Init channel Context */ + /** Func#1 is reserved for mailbox */ + ch->func = sdio_al_dev->card->sdio_func[ch->num+1]; + ch->rx_pipe_index = ch->num*2; + ch->tx_pipe_index = ch->num*2+1; + ch->signature = SDIO_AL_SIGNATURE; + + ch->total_rx_bytes = 0; + ch->total_tx_bytes = 0; + + ch->write_avail = 0; + ch->read_avail = 0; + ch->rx_pending_bytes = 0; + + mutex_init(&ch->ch_lock); + + pr_debug(MODULE_NAME ":open_channel %s func#%d\n", + ch->name, ch->func->num); + + INIT_LIST_HEAD(&(ch->rx_size_list_head)); + + /* Init SDIO Function */ + ret = sdio_al_enable_func_retry(ch->func, ch->name); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "sdio_enable_func() err=%d\n", -ret); + goto exit_err; + } + + /* Note: Patch Func CIS tuple issue */ + ret = sdio_set_block_size(ch->func, SDIO_AL_BLOCK_SIZE); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "sdio_set_block_size()failed, err=%d\n", -ret); + goto exit_err; + } + + ch->func->max_blksize = SDIO_AL_BLOCK_SIZE; + + sdio_set_drvdata(ch->func, ch); + + /* Get channel parameters from the peer SDIO-Client */ + read_sdioc_channel_config(ch); + + /* Set Pipes Threshold on Mailbox */ + ret = set_pipe_threshold(sdio_al_dev, + ch->rx_pipe_index, ch->read_threshold); + if (ret) + goto exit_err; + ret = set_pipe_threshold(sdio_al_dev, + ch->tx_pipe_index, ch->write_threshold); + if (ret) + goto exit_err; + + /* Set flag before interrupts are enabled to allow notify */ + ch->state = SDIO_CHANNEL_STATE_OPEN; + pr_debug(MODULE_NAME ":channel %s is in OPEN state now\n", ch->name); + + sdio_al_dev->poll_delay_msec = get_min_poll_time_msec(sdio_al_dev); + + /* lpm mechanism lives under the assumption there is always a timer */ + /* Check if need to start the timer */ + if ((sdio_al_dev->poll_delay_msec) && + (sdio_al_dev->is_timer_initialized == false)) { + + init_timer(&sdio_al_dev->timer); + sdio_al_dev->timer.data = (unsigned long) sdio_al_dev; + sdio_al_dev->timer.function = sdio_al_timer_handler; + sdio_al_dev->timer.expires = jiffies + + msecs_to_jiffies(sdio_al_dev->poll_delay_msec); + add_timer(&sdio_al_dev->timer); + sdio_al_dev->is_timer_initialized = true; + } + + /* Enable Pipes Interrupts */ + enable_eot_interrupt(sdio_al_dev, ch->rx_pipe_index, true); + enable_eot_interrupt(sdio_al_dev, ch->tx_pipe_index, true); + + enable_threshold_interrupt(sdio_al_dev, ch->rx_pipe_index, true); + enable_threshold_interrupt(sdio_al_dev, ch->tx_pipe_index, true); + +exit_err: + + return ret; +} + +/** + * Ask the worker to read the mailbox. + */ +static void ask_reading_mailbox(struct sdio_al_device *sdio_al_dev) +{ + if (!sdio_al_dev->ask_mbox) { + pr_debug(MODULE_NAME ":ask_reading_mailbox for card %d\n", + sdio_al_dev->host->index); + sdio_al_dev->ask_mbox = true; + wake_up(&sdio_al_dev->wait_mbox); + } +} + +/** + * Start the timer + */ +static void start_timer(struct sdio_al_device *sdio_al_dev) +{ + if ((sdio_al_dev->poll_delay_msec) && + (sdio_al_dev->state == CARD_INSERTED)) { + sdio_al_dev->timer.expires = jiffies + + msecs_to_jiffies(sdio_al_dev->poll_delay_msec); + add_timer(&sdio_al_dev->timer); + } +} + +/** + * Restart(postpone) the already working timer + */ +static void restart_timer(struct sdio_al_device *sdio_al_dev) +{ + if ((sdio_al_dev->poll_delay_msec) && + (sdio_al_dev->state == CARD_INSERTED)) { + ulong expires = jiffies + + msecs_to_jiffies(sdio_al_dev->poll_delay_msec); + mod_timer(&sdio_al_dev->timer, expires); + } +} + +/** + * Stop and delete the timer + */ +static void stop_and_del_timer(struct sdio_al_device *sdio_al_dev) +{ + if (sdio_al_dev->is_timer_initialized) { + sdio_al_dev->poll_delay_msec = 0; + del_timer_sync(&sdio_al_dev->timer); + } +} + +/** + * Do the wakup sequence. + * This function should be called after claiming the host! + * The caller is responsible for releasing the host. + * + * Wake up sequence + * 1. Get lock + * 2. Enable wake up function if needed + * 3. Mark NOT OK to sleep and write it + * 4. Restore default thresholds + * 5. Start the mailbox and inactivity timer again + */ +static int sdio_al_wake_up(struct sdio_al_device *sdio_al_dev, + u32 not_from_int, struct sdio_channel *ch) +{ + int ret = 0; + struct sdio_func *wk_func = NULL; + unsigned long time_to_wait; + struct mmc_host *host = sdio_al_dev->host; + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + return -ENODEV; + } + + if (!sdio_al_dev->is_ok_to_sleep) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":card %d " + "already awake, no need to wake up\n", + sdio_al_dev->host->index); + return 0; + } + + /* Wake up sequence */ + if (not_from_int) { + if (ch) { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": Wake up" + " card %d (not by interrupt), ch %s", + sdio_al_dev->host->index, + ch->name); + } else { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": Wake up" + " card %d (not by interrupt)", + sdio_al_dev->host->index); + } + } else { + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": Wake up card " + "%d by interrupt", + sdio_al_dev->host->index); + sdio_al_dev->print_after_interrupt = 1; + } + + sdio_al_vote_for_sleep(sdio_al_dev, 0); + + msmsdcc_lpm_disable(host); + msmsdcc_set_pwrsave(host, 0); + /* Poll the GPIO */ + time_to_wait = jiffies + msecs_to_jiffies(1000); + while (time_before(jiffies, time_to_wait)) { + if (sdio_al->pdata->get_mdm2ap_status()) + break; + udelay(TIME_TO_WAIT_US); + } + + pr_debug(MODULE_NAME ":GPIO mdm2ap_status=%d\n", + sdio_al->pdata->get_mdm2ap_status()); + + /* Here get_mdm2ap_status() returning 0 is not an error condition */ + if (sdio_al->pdata->get_mdm2ap_status() == 0) + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ": " + "get_mdm2ap_status() is 0\n"); + + /* Enable Wake up Function */ + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL card or wk_func\n"); + return -ENODEV; + } + wk_func = sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]; + ret = sdio_al_enable_func_retry(wk_func, "wakeup func"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "sdio_enable_func() err=%d\n", -ret); + goto error_exit; + } + /* Mark NOT OK_TOSLEEP */ + sdio_al_dev->is_ok_to_sleep = 0; + ret = write_lpm_info(sdio_al_dev); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": " + "write_lpm_info() failed, err=%d\n", -ret); + sdio_al_dev->is_ok_to_sleep = 1; + sdio_disable_func(wk_func); + goto error_exit; + } + sdio_disable_func(wk_func); + + /* Start the timer again*/ + restart_inactive_time(sdio_al_dev); + sdio_al_dev->poll_delay_msec = get_min_poll_time_msec(sdio_al_dev); + start_timer(sdio_al_dev); + + LPM_DEBUG(sdio_al_dev->dev_log, MODULE_NAME "Finished Wake up sequence" + " for card %d", sdio_al_dev->host->index); + + msmsdcc_set_pwrsave(host, 1); + pr_debug(MODULE_NAME ":Turn clock off\n"); + + return ret; +error_exit: + sdio_al_vote_for_sleep(sdio_al_dev, 1); + msmsdcc_set_pwrsave(host, 1); + WARN_ON(ret); + sdio_al_get_into_err_state(sdio_al_dev); + return ret; +} + + +/** + * SDIO Function Interrupt handler. + * + * Interrupt shall be triggered by SDIO-Client when: + * 1. End-Of-Transfer (EOT) detected in packet mode. + * 2. Bytes-available reached the threshold. + * + * Reading the mailbox clears the EOT/Threshold interrupt + * source. + * The interrupt source should be cleared before this ISR + * returns. This ISR is called from IRQ Thread and not + * interrupt, so it may sleep. + * + */ +static void sdio_func_irq(struct sdio_func *func) +{ + struct sdio_al_device *sdio_al_dev = sdio_get_drvdata(func); + + pr_debug(MODULE_NAME ":start %s.\n", __func__); + + if (sdio_al_dev == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": NULL device"); + return; + } + + if (sdio_al_dev->is_ok_to_sleep) + sdio_al_wake_up(sdio_al_dev, 0, NULL); + else + restart_timer(sdio_al_dev); + + read_mailbox(sdio_al_dev, true); + + pr_debug(MODULE_NAME ":end %s.\n", __func__); +} + +/** + * Timer Expire Handler + * + */ +static void sdio_al_timer_handler(unsigned long data) +{ + struct sdio_al_device *sdio_al_dev = (struct sdio_al_device *)data; + if (sdio_al_dev == NULL) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": NULL " + "sdio_al_dev for data %lu\n", data); + return; + } + if (sdio_al_dev->state != CARD_INSERTED) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": sdio_al_dev " + "is in invalid state %d\n", sdio_al_dev->state); + return; + } + pr_debug(MODULE_NAME " Timer Expired\n"); + + ask_reading_mailbox(sdio_al_dev); + + restart_timer(sdio_al_dev); +} + +/** + * Driver Setup. + * + */ +static int sdio_al_setup(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + struct mmc_card *card = sdio_al_dev->card; + struct sdio_func *func1 = NULL; + int i = 0; + int fn = 0; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + return -ENODEV; + func1 = card->sdio_func[0]; + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":sdio_al_setup for " + "card %d\n", sdio_al_dev->host->index); + + ret = sdio_al->pdata->config_mdm2ap_status(1); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME "Could not " + "request GPIO\n"); + return ret; + } + + INIT_WORK(&sdio_al_dev->sdio_al_work.work, worker); + /* disable all pipes interrupts before claim irq. + since all are enabled by default. */ + for (i = 0 ; i < SDIO_AL_MAX_PIPES; i++) { + enable_eot_interrupt(sdio_al_dev, i, false); + enable_threshold_interrupt(sdio_al_dev, i, false); + } + + /* Disable all SDIO Functions before claim irq. */ + for (fn = 1 ; fn <= card->sdio_funcs; fn++) + sdio_disable_func(card->sdio_func[fn-1]); + + sdio_set_drvdata(func1, sdio_al_dev); + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":claim IRQ for card " + "%d\n", sdio_al_dev->host->index); + + ret = sdio_claim_irq(func1, sdio_func_irq); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to claim" + " IRQ for card %d\n", + sdio_al_dev->host->index); + return ret; + } + + sdio_al_dev->is_ready = true; + + /* Start worker before interrupt might happen */ + queue_work(sdio_al_dev->workqueue, &sdio_al_dev->sdio_al_work.work); + + start_inactive_time(sdio_al_dev); + + pr_debug(MODULE_NAME ":Ready.\n"); + + return 0; +} + +/** + * Driver Tear-Down. + * + */ +static void sdio_al_tear_down(void) +{ + int i, j; + struct sdio_al_device *sdio_al_dev = NULL; + struct sdio_func *func1; + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + if (sdio_al->devices[i] == NULL) + continue; + sdio_al_dev = sdio_al->devices[i]; + + if (sdio_al_dev->is_ready) { + sdio_al_dev->is_ready = false; /* Flag worker to exit */ + sdio_al_dev->ask_mbox = false; + ask_reading_mailbox(sdio_al_dev); /* Wakeup worker */ + /* allow gracefully exit of the worker thread */ + msleep(100); + + flush_workqueue(sdio_al_dev->workqueue); + destroy_workqueue(sdio_al_dev->workqueue); + + sdio_al_vote_for_sleep(sdio_al_dev, 1); + + if (!sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, + __func__)) { + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[0]) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME + ": %s: Invalid func1", + __func__); + return; + } + func1 = sdio_al_dev->card->sdio_func[0]; + sdio_release_irq(func1); + sdio_disable_func(func1); + sdio_al_release_mutex(sdio_al_dev, __func__); + } + } + + for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) + sdio_al_dev->channel[j].signature = 0x0; + sdio_al_dev->signature = 0; + + kfree(sdio_al_dev->sdioc_sw_header); + kfree(sdio_al_dev->mailbox); + kfree(sdio_al_dev->rx_flush_buf); + kfree(sdio_al_dev); + } + + sdio_al->pdata->config_mdm2ap_status(0); +} + +/** + * Find channel by name. + * + */ +static struct sdio_channel *find_channel_by_name(const char *name) +{ + struct sdio_channel *ch = NULL; + int i, j; + struct sdio_al_device *sdio_al_dev = NULL; + + for (j = 0; j < MAX_NUM_OF_SDIO_DEVICES; ++j) { + if (sdio_al->devices[j] == NULL) + continue; + sdio_al_dev = sdio_al->devices[j]; + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + if (sdio_al_dev->channel[i].state == + SDIO_CHANNEL_STATE_INVALID) + continue; + if (strncmp(sdio_al_dev->channel[i].name, name, + CHANNEL_NAME_SIZE) == 0) { + ch = &sdio_al_dev->channel[i]; + break; + } + } + if (ch != NULL) + break; + } + + return ch; +} + +/** + * Find the minimal poll time. + * + */ +static int get_min_poll_time_msec(struct sdio_al_device *sdio_sl_dev) +{ + int i; + int poll_delay_msec = 0x0FFFFFFF; + + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) + if ((sdio_sl_dev->channel[i].state == + SDIO_CHANNEL_STATE_OPEN) && + (sdio_sl_dev->channel[i].poll_delay_msec > 0) && + (sdio_sl_dev->channel[i].poll_delay_msec < poll_delay_msec)) + poll_delay_msec = + sdio_sl_dev->channel[i].poll_delay_msec; + + if (poll_delay_msec == 0x0FFFFFFF) + poll_delay_msec = SDIO_AL_POLL_TIME_NO_STREAMING; + + pr_debug(MODULE_NAME ":poll delay time is %d msec\n", poll_delay_msec); + + return poll_delay_msec; +} + +/** + * Open SDIO Channel. + * + * Enable the channel. + * Set the channel context. + * Trigger reading the mailbox to check available bytes. + * + */ +int sdio_open(const char *name, struct sdio_channel **ret_ch, void *priv, + void (*notify)(void *priv, unsigned ch_event)) +{ + int ret = 0; + struct sdio_channel *ch = NULL; + struct sdio_al_device *sdio_al_dev = NULL; + + *ret_ch = NULL; /* default */ + + ch = find_channel_by_name(name); + if (ch == NULL) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":Can't find " + "channel name %s\n", name); + return -EINVAL; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if ((ch->state != SDIO_CHANNEL_STATE_IDLE) && + (ch->state != SDIO_CHANNEL_STATE_CLOSED)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Wrong ch %s " + "state %d\n", name, ch->state); + ret = -EPERM; + goto exit_err; + } + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto exit_err; + } + + ret = sdio_al_wake_up(sdio_al_dev, 1, ch); + if (ret) + goto exit_err; + + ch->notify = notify; + ch->priv = priv; + + /* Note: Set caller returned context before interrupts are enabled */ + *ret_ch = ch; + + ret = open_channel(ch); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_open %s " + "err=%d\n", name, -ret); + goto exit_err; + } + + CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":sdio_open %s " + "completed OK\n", name); + if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) { + if (sdio_al->sdioc_major == PEER_SDIOC_OLD_VERSION_MAJOR) { + if (!ch->is_packet_mode) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME + ":setting channel %s as " + "lpm_chan\n", name); + sdio_al_dev->lpm_chan = ch->num; + } + } else { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": " + "setting channel %s as lpm_chan\n", + name); + sdio_al_dev->lpm_chan = ch->num; + } + } + +exit_err: + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; +} +EXPORT_SYMBOL(sdio_open); + +/** + * Request peer operation + * note: sanity checks of parameters done by caller + * called under bus locked + */ +static int peer_set_operation(u32 opcode, + struct sdio_al_device *sdio_al_dev, + struct sdio_channel *ch) +{ + int ret; + int offset; + struct sdio_func *wk_func = NULL; + u32 peer_operation; + int loop_count = 0; + + if (!sdio_al_dev->card || + !sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": NULL card or wk_func\n"); + ret = -ENODEV; + goto exit; + } + wk_func = sdio_al_dev->card->sdio_func[SDIO_AL_WAKEUP_FUNC-1]; + + /* calculate offset of peer_operation field in sw mailbox struct */ + offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config) + + sizeof(struct peer_sdioc_channel_config) * ch->num + + offsetof(struct peer_sdioc_channel_config, peer_operation); + + ret = sdio_al_wake_up(sdio_al_dev, 1, ch); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to " + "wake up\n"); + goto exit; + } + /* request operation from MDM peer */ + peer_operation = PEER_OPERATION(opcode, PEER_OP_STATE_INIT); + ret = sdio_memcpy_toio(ch->func, SDIOC_SW_MAILBOX_ADDR+offset, + &peer_operation, sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":failed to " + "request close operation\n"); + goto exit; + } + ret = sdio_al_enable_func_retry(wk_func, "wk_func"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to enable" + " Func#%d\n", wk_func->num); + goto exit; + } + pr_debug(MODULE_NAME ":%s: wk_func enabled on ch %s\n", + __func__, ch->name); + /* send "start" operation to MDM */ + peer_operation = PEER_OPERATION(opcode, PEER_OP_STATE_START); + ret = sdio_memcpy_toio(ch->func, SDIOC_SW_MAILBOX_ADDR+offset, + &peer_operation, sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":failed to " + "send start close operation\n"); + goto exit; + } + ret = sdio_disable_func(wk_func); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to " + "disable Func#%d\n", wk_func->num); + goto exit; + } + /* poll for peer operation ack */ + while (peer_operation != 0) { + ret = sdio_memcpy_fromio(ch->func, + &peer_operation, + SDIOC_SW_MAILBOX_ADDR+offset, + sizeof(u32)); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":failed to request ack on close" + " operation, loop_count = %d\n", + loop_count); + goto exit; + } + loop_count++; + if (loop_count > 10) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "peer_operation=0x%x wait loop" + " %d on ch %s\n", __func__, + peer_operation, loop_count, ch->name); + } + } +exit: + return ret; +} + +static int channel_close(struct sdio_channel *ch, int flush_flag) +{ + int ret; + struct sdio_al_device *sdio_al_dev = NULL; + int flush_len; + ulong flush_expires; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + + if (!ch->func) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: NULL func" + " on channel:%d\n", __func__, ch->num); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if (!sdio_al_dev->ch_close_supported) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: Not " + "supported by mdm, ch %s\n", + __func__, ch->name); + ret = -ENOTSUPP; + goto error_exit; + } + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto error_exit; + } + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ":%s: ch %s is not in " + "open state (%d)\n", + __func__, ch->name, ch->state); + ret = -ENODEV; + goto error_exit; + } + ch->state = SDIO_CHANNEL_STATE_CLOSING; + ret = peer_set_operation(PEER_OP_CODE_CLOSE, sdio_al_dev, ch); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "peer_set_operation() failed: %d\n", + __func__, ret); + ret = -ENODEV; + goto error_exit; + } + /* udate poll time for opened channels */ + if (ch->poll_delay_msec > 0) { + sdio_al_dev->poll_delay_msec = + get_min_poll_time_msec(sdio_al_dev); + } + sdio_al_release_mutex(ch->sdio_al_dev, __func__); + + flush_expires = jiffies + + msecs_to_jiffies(SDIO_CLOSE_FLUSH_TIMEOUT_MSEC); + /* flush rx packets of the channel */ + if (flush_flag) { + do { + while (ch->read_avail > 0) { + flush_len = ch->read_avail; + ret = sdio_read_internal(ch, + sdio_al_dev->rx_flush_buf, + flush_len); + if (ret) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s sdio_read" + " failed: %d, ch %s\n", + __func__, ret, + ch->name); + return ret; + } + + if (time_after(jiffies, flush_expires) != 0) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s flush rx " + "packets timeout: ch %s\n", + __func__, ch->name); + sdio_al_get_into_err_state(sdio_al_dev); + return -EBUSY; + } + } + msleep(100); + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s: after sleep," + " invalid signature" + " 0x%x\n", __func__, + ch->signature); + return -ENODEV; + } + if (sdio_al_claim_mutex_and_verify_dev(ch->sdio_al_dev, + __func__)) + return -ENODEV; + + ret = read_mailbox(sdio_al_dev, false); + if (ret) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":%s: failed to" + " read mailbox", __func__); + goto error_exit; + } + sdio_al_release_mutex(ch->sdio_al_dev, __func__); + } while (ch->read_avail > 0); + } + if (sdio_al_claim_mutex_and_verify_dev(ch->sdio_al_dev, + __func__)) + return -ENODEV; + /* disable function to be able to open the channel again */ + ret = sdio_disable_func(ch->func); + if (ret) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":Fail to disable Func#%d\n", + ch->func->num); + goto error_exit; + } + ch->state = SDIO_CHANNEL_STATE_CLOSED; + CLOSE_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":%s: Ch %s closed " + "successfully\n", __func__, ch->name); + +error_exit: + sdio_al_release_mutex(ch->sdio_al_dev, __func__); + + return ret; +} + +/** + * Close SDIO Channel. + * + */ +int sdio_close(struct sdio_channel *ch) +{ + return channel_close(ch, true); +} +EXPORT_SYMBOL(sdio_close); + +/** + * Get the number of available bytes to write. + * + */ +int sdio_write_avail(struct sdio_channel *ch) +{ + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: " + "Invalid signature 0x%x\n", __func__, + ch->signature); + return -ENODEV; + } + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "channel %s state is not open (%d)\n", + __func__, ch->name, ch->state); + return -ENODEV; + } + pr_debug(MODULE_NAME ":sdio_write_avail %s 0x%x\n", + ch->name, ch->write_avail); + + return ch->write_avail; +} +EXPORT_SYMBOL(sdio_write_avail); + +/** + * Get the number of available bytes to read. + * + */ +int sdio_read_avail(struct sdio_channel *ch) +{ + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: " + "Invalid signature 0x%x\n", __func__, + ch->signature); + return -ENODEV; + } + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME ":%s: " + "channel %s state is not open (%d)\n", + __func__, ch->name, ch->state); + return -ENODEV; + } + pr_debug(MODULE_NAME ":sdio_read_avail %s 0x%x\n", + ch->name, ch->read_avail); + + return ch->read_avail; +} +EXPORT_SYMBOL(sdio_read_avail); + +static int sdio_read_from_closed_ch(struct sdio_channel *ch, int len) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + + if (!ch) { + sdio_al_loge(ch->sdio_al_dev->dev_log, + MODULE_NAME ":%s: NULL channel\n", __func__); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + ret = sdio_memcpy_fromio(ch->func, sdio_al_dev->rx_flush_buf, + PIPE_RX_FIFO_ADDR, len); + + if (ret) { + sdio_al_loge(ch->sdio_al_dev->dev_log, + MODULE_NAME ":ch %s: %s err=%d, len=%d\n", + ch->name, __func__, -ret, len); + sdio_al_dev->is_err = true; + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; + } + + restart_inactive_time(sdio_al_dev); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + return 0; +} + +/** + * Internal read from SDIO Channel. + * + * Reading from the pipe will trigger interrupt if there are + * other pending packets on the SDIO-Client. + * + */ +static int sdio_read_internal(struct sdio_channel *ch, void *data, int len) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (!data) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL data\n", + __func__); + return -ENODEV; + } + if (len == 0) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":channel %s trying" + " to read 0 bytes\n", ch->name); + return -EINVAL; + } + + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: Invalid " + "signature 0x%x\n", __func__, ch->signature); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto exit; + } + + /* lpm policy says we can't go to sleep when we have pending rx data, + so either we had rx interrupt and woken up, or we never went to + sleep */ + if (sdio_al_dev->is_ok_to_sleep) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s: called " + "when is_ok_to_sleep is set for ch %s, len=%d," + " last_any_read_avail=%d, last_read_avail=%d, " + "last_old_read_avail=%d", __func__, ch->name, + len, ch->statistics.last_any_read_avail, + ch->statistics.last_read_avail, + ch->statistics.last_old_read_avail); + } + BUG_ON(sdio_al_dev->is_ok_to_sleep); + + if ((ch->state != SDIO_CHANNEL_STATE_OPEN) && + (ch->state != SDIO_CHANNEL_STATE_CLOSING)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":%s wrong " + "channel %s state %d\n", + __func__, ch->name, ch->state); + ret = -EINVAL; + goto exit; + } + + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":start ch %s read %d " + "avail %d.\n", ch->name, len, ch->read_avail); + + restart_inactive_time(sdio_al_dev); + + if ((ch->is_packet_mode) && (len != ch->read_avail)) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_read ch " + "%s len != read_avail\n", ch->name); + ret = -EINVAL; + goto exit; + } + + if (len > ch->read_avail) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":ERR ch %s: " + "reading more bytes (%d) than the avail(%d).\n", + ch->name, len, ch->read_avail); + ret = -ENOMEM; + goto exit; + } + + ret = sdio_memcpy_fromio(ch->func, data, PIPE_RX_FIFO_ADDR, len); + + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":ch %s: " + "sdio_read err=%d, len=%d, read_avail=%d, " + "last_read_avail=%d, last_old_read_avail=%d\n", + ch->name, -ret, len, ch->read_avail, + ch->statistics.last_read_avail, + ch->statistics.last_old_read_avail); + sdio_al_get_into_err_state(sdio_al_dev); + goto exit; + } + + ch->statistics.total_read_times++; + + /* Remove handled packet from the list regardless if ret is ok */ + if (ch->is_packet_mode) + remove_handled_rx_packet(ch); + else + ch->read_avail -= len; + + ch->total_rx_bytes += len; + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":end ch %s read %d " + "avail %d total %d.\n", ch->name, len, + ch->read_avail, ch->total_rx_bytes); + + if ((ch->read_avail == 0) && !(ch->is_packet_mode)) + ask_reading_mailbox(sdio_al_dev); + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + + return ret; +} + +/** + * Read from SDIO Channel. + * + * Reading from the pipe will trigger interrupt if there are + * other pending packets on the SDIO-Client. + * + */ +int sdio_read(struct sdio_channel *ch, void *data, int len) +{ + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: " + "Invalid signature 0x%x\n", __func__, ch->signature); + return -ENODEV; + } + if (ch->state == SDIO_CHANNEL_STATE_OPEN) { + return sdio_read_internal(ch, data, len); + } else { + sdio_al_loge(ch->sdio_al_dev->dev_log, MODULE_NAME + ":%s: Invalid channel %s state %d\n", + __func__, ch->name, ch->state); + } + return -ENODEV; +} +EXPORT_SYMBOL(sdio_read); + +/** + * Write to SDIO Channel. + * + */ +int sdio_write(struct sdio_channel *ch, const void *data, int len) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + + if (!ch) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL " + "channel\n", __func__); + return -ENODEV; + } + if (!data) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: NULL data\n", + __func__); + return -ENODEV; + } + if (len == 0) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":channel %s trying" + " to write 0 bytes\n", ch->name); + return -EINVAL; + } + + if (ch->signature != SDIO_AL_SIGNATURE) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":%s: Invalid " + "signature 0x%x\n", __func__, ch->signature); + return -ENODEV; + } + + sdio_al_dev = ch->sdio_al_dev; + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + WARN_ON(len > ch->write_avail); + + if (sdio_al_dev->is_err) { + SDIO_AL_ERR(__func__); + ret = -ENODEV; + goto exit; + } + + if (ch->state != SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":writing to " + "closed channel %s\n", ch->name); + ret = -EINVAL; + goto exit; + } + + if (sdio_al_dev->is_ok_to_sleep) { + ret = sdio_al_wake_up(sdio_al_dev, 1, ch); + if (ret) + goto exit; + } else { + restart_inactive_time(sdio_al_dev); + } + + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":start ch %s write %d " + "avail %d.\n", ch->name, len, ch->write_avail); + + if (len > ch->write_avail) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":ERR ch %s: " + "write more bytes (%d) than available %d.\n", + ch->name, len, ch->write_avail); + ret = -ENOMEM; + goto exit; + } + + ret = sdio_ch_write(ch, data, len); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":sdio_write " + "on channel %s err=%d\n", ch->name, -ret); + goto exit; + } + + ch->total_tx_bytes += len; + DATA_DEBUG(sdio_al_dev->dev_log, MODULE_NAME ":end ch %s write %d " + "avail %d total %d.\n", ch->name, len, + ch->write_avail, ch->total_tx_bytes); + + /* Round up to whole buffer size */ + len = ROUND_UP(len, ch->peer_tx_buf_size); + /* Protect from wraparound */ + len = min(len, (int) ch->write_avail); + ch->write_avail -= len; + + if (ch->write_avail < ch->min_write_avail) + ask_reading_mailbox(sdio_al_dev); + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + + return ret; +} +EXPORT_SYMBOL(sdio_write); + +static int __devinit msm_sdio_al_probe(struct platform_device *pdev) +{ + if (!sdio_al) { + pr_err(MODULE_NAME ": %s: NULL sdio_al\n", __func__); + return -ENODEV; + } + + sdio_al->pdata = pdev->dev.platform_data; + return 0; +} + +static int __devexit msm_sdio_al_remove(struct platform_device *pdev) +{ + return 0; +} + +static void sdio_al_close_all_channels(struct sdio_al_device *sdio_al_dev) +{ + int j; + int ret; + struct sdio_channel *ch = NULL; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__); + + if (!sdio_al_dev) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ": %s: NULL device", __func__); + return; + } + for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) { + ch = &sdio_al_dev->channel[j]; + + if (ch->state == SDIO_CHANNEL_STATE_OPEN) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ": %s: Call to sdio_close() for" + " ch %s\n", __func__, ch->name); + ret = channel_close(ch, false); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, + MODULE_NAME ": %s: failed sdio_close()" + " for ch %s (%d)\n", + __func__, ch->name, ret); + } + } else { + pr_debug(MODULE_NAME ": %s: skip sdio_close() ch %s" + " (state=%d)\n", __func__, + ch->name, ch->state); + } + } +} + +static void sdio_al_invalidate_sdio_clients(struct sdio_al_device *sdio_al_dev, + struct platform_device **pdev_arr) +{ + int j; + + pr_debug(MODULE_NAME ": %s: Notifying SDIO clients for card %d", + __func__, sdio_al_dev->host->index); + for (j = 0; j < SDIO_AL_MAX_CHANNELS; ++j) { + if (sdio_al_dev->channel[j].state == + SDIO_CHANNEL_STATE_INVALID) + continue; + pdev_arr[j] = sdio_al_dev->channel[j].pdev; + sdio_al_dev->channel[j].signature = 0x0; + sdio_al_dev->channel[j].state = + SDIO_CHANNEL_STATE_INVALID; + } +} + +static void sdio_al_modem_reset_operations(struct sdio_al_device + *sdio_al_dev) +{ + int ret = 0; + struct platform_device *pdev_arr[SDIO_AL_MAX_CHANNELS]; + int j; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__); + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + + if (sdio_al_dev->state == CARD_REMOVED) { + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: " + "card %d is already removed", __func__, + sdio_al_dev->host->index); + goto exit_err; + } + + if (sdio_al_dev->state == MODEM_RESTART) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: " + "card %d was already notified for modem reset", + __func__, sdio_al_dev->host->index); + goto exit_err; + } + + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ": %s: Set the " + "state to MODEM_RESTART for card %d", + __func__, sdio_al_dev->host->index); + sdio_al_dev->state = MODEM_RESTART; + sdio_al_dev->is_ready = false; + + /* Stop mailbox timer */ + stop_and_del_timer(sdio_al_dev); + + if ((sdio_al_dev->is_ok_to_sleep) && + (!sdio_al_dev->is_err)) { + pr_debug(MODULE_NAME ": %s: wakeup modem for " + "card %d", __func__, + sdio_al_dev->host->index); + ret = sdio_al_wake_up(sdio_al_dev, 1, NULL); + } + + if (!ret && (!sdio_al_dev->is_err) && sdio_al_dev->card && + sdio_al_dev->card->sdio_func[0]) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME + ": %s: sdio_release_irq for card %d", + __func__, + sdio_al_dev->host->index); + sdio_release_irq(sdio_al_dev->card->sdio_func[0]); + } + + memset(pdev_arr, 0, sizeof(pdev_arr)); + sdio_al_invalidate_sdio_clients(sdio_al_dev, pdev_arr); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Notifying SDIO " + "clients for card %d", + __func__, sdio_al_dev->host->index); + for (j = 0; j < SDIO_AL_MAX_CHANNELS; j++) { + if (!pdev_arr[j]) + continue; + platform_device_unregister(pdev_arr[j]); + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Finished Notifying " + "SDIO clients for card %d", + __func__, sdio_al_dev->host->index); + + return; + +exit_err: + sdio_al_release_mutex(sdio_al_dev, __func__); + return; +} + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART +static void sdio_al_reset(void) +{ + int i; + struct sdio_al_device *sdio_al_dev; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s", __func__); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) { + if (sdio_al->devices[i] == NULL) { + pr_debug(MODULE_NAME ": %s: NULL device in index %d", + __func__, i); + continue; + } + sdio_al_dev = sdio_al->devices[i]; + sdio_al_modem_reset_operations(sdio_al->devices[i]); + } + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s completed", __func__); +} +#endif + +static void msm_sdio_al_shutdown(struct platform_device *pdev) +{ + int i; + struct sdio_al_device *sdio_al_dev; + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME + "Initiating msm_sdio_al_shutdown..."); + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; i++) { + if (sdio_al->devices[i] == NULL) { + pr_debug(MODULE_NAME ": %s: NULL device in index %d", + __func__, i); + continue; + } + sdio_al_dev = sdio_al->devices[i]; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + + if (sdio_al_dev->ch_close_supported) + sdio_al_close_all_channels(sdio_al_dev); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + sdio_al_modem_reset_operations(sdio_al_dev); + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: " + "msm_sdio_al_shutdown complete.", __func__); +} + +static struct platform_driver msm_sdio_al_driver = { + .probe = msm_sdio_al_probe, + .remove = __exit_p(msm_sdio_al_remove), + .shutdown = msm_sdio_al_shutdown, + .driver = { + .name = "msm_sdio_al", + }, +}; + +/** + * Initialize SDIO_AL channels. + * + */ +static int init_channels(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + int i; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + ret = read_sdioc_software_header(sdio_al_dev, + sdio_al_dev->sdioc_sw_header); + if (ret) + goto exit; + + ret = sdio_al_setup(sdio_al_dev); + if (ret) + goto exit; + + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + int ch_name_size; + if (sdio_al_dev->channel[i].state == SDIO_CHANNEL_STATE_INVALID) + continue; + if (sdio_al->unittest_mode) { + memset(sdio_al_dev->channel[i].ch_test_name, 0, + sizeof(sdio_al_dev->channel[i].ch_test_name)); + ch_name_size = strnlen(sdio_al_dev->channel[i].name, + CHANNEL_NAME_SIZE); + strncpy(sdio_al_dev->channel[i].ch_test_name, + sdio_al_dev->channel[i].name, + ch_name_size); + strncat(sdio_al_dev->channel[i].ch_test_name + + ch_name_size, + SDIO_TEST_POSTFIX, + SDIO_TEST_POSTFIX_SIZE); + pr_debug(MODULE_NAME ":pdev.name = %s\n", + sdio_al_dev->channel[i].ch_test_name); + sdio_al_dev->channel[i].pdev = platform_device_alloc( + sdio_al_dev->channel[i].ch_test_name, -1); + } else { + pr_debug(MODULE_NAME ":pdev.name = %s\n", + sdio_al_dev->channel[i].name); + sdio_al_dev->channel[i].pdev = platform_device_alloc( + sdio_al_dev->channel[i].name, -1); + } + if (!sdio_al_dev->channel[i].pdev) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":NULL platform device for ch %s", + sdio_al_dev->channel[i].name); + sdio_al_dev->channel[i].state = + SDIO_CHANNEL_STATE_INVALID; + continue; + } + ret = platform_device_add(sdio_al_dev->channel[i].pdev); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ":platform_device_add failed, " + "ret=%d\n", ret); + sdio_al_dev->channel[i].state = + SDIO_CHANNEL_STATE_INVALID; + } + } + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; +} + +/** + * Initialize SDIO_AL channels according to the client setup. + * This function also check if the client is in boot mode and + * flashless boot is required to be activated or the client is + * up and running. + * + */ +static int sdio_al_client_setup(struct sdio_al_device *sdio_al_dev) +{ + int ret = 0; + struct sdio_func *func1; + int signature = 0; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + if (!sdio_al_dev->card || !sdio_al_dev->card->sdio_func[0]) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":NULL card or " + "func1\n"); + sdio_al_release_mutex(sdio_al_dev, __func__); + return -ENODEV; + } + func1 = sdio_al_dev->card->sdio_func[0]; + + /* Read the header signature to determine the status of the MDM + * SDIO Client + */ + signature = sdio_readl(func1, SDIOC_SW_HEADER_ADDR, &ret); + sdio_al_release_mutex(sdio_al_dev, __func__); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":fail to read " + "signature from sw header.\n"); + return ret; + } + + switch (signature) { + case PEER_SDIOC_SW_MAILBOX_BOOT_SIGNATURE: + if (sdio_al_dev == sdio_al->bootloader_dev) { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":setup " + "bootloader on card %d\n", + sdio_al_dev->host->index); + return sdio_al_bootloader_setup(); + } else { + sdio_al_logi(sdio_al_dev->dev_log, MODULE_NAME ":wait " + "for bootloader completion " + "on card %d\n", + sdio_al_dev->host->index); + return sdio_al_wait_for_bootloader_comp(sdio_al_dev); + } + case PEER_SDIOC_SW_MAILBOX_SIGNATURE: + case PEER_SDIOC_SW_MAILBOX_UT_SIGNATURE: + return init_channels(sdio_al_dev); + default: + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Invalid " + "signature 0x%x\n", signature); + return -EINVAL; + } + + return 0; +} + +static void clean_sdio_al_device_data(struct sdio_al_device *sdio_al_dev) +{ + sdio_al_dev->is_ready = 0; + sdio_al_dev->bootloader_done = 0; + sdio_al_dev->lpm_chan = 0; + sdio_al_dev->is_ok_to_sleep = 0; + sdio_al_dev->inactivity_time = 0; + sdio_al_dev->poll_delay_msec = 0; + sdio_al_dev->is_timer_initialized = 0; + sdio_al_dev->is_err = 0; + sdio_al_dev->is_suspended = 0; + sdio_al_dev->flashless_boot_on = 0; + sdio_al_dev->ch_close_supported = 0; + sdio_al_dev->print_after_interrupt = 0; + memset(sdio_al_dev->sdioc_sw_header, 0, + sizeof(*sdio_al_dev->sdioc_sw_header)); + memset(sdio_al_dev->mailbox, 0, sizeof(*sdio_al_dev->mailbox)); + memset(sdio_al_dev->rx_flush_buf, 0, + sizeof(*sdio_al_dev->rx_flush_buf)); +} + +/* + * SDIO driver functions + */ +static int sdio_al_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *sdio_dev_id) +{ + int ret = 0; + struct sdio_al_device *sdio_al_dev = NULL; + int i; + struct mmc_card *card = NULL; + + if (!func) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL func\n", + __func__); + return -ENODEV; + } + card = func->card; + + if (!card) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL card\n", + __func__); + return -ENODEV; + } + + if (!card->sdio_func[0]) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "func1\n", + __func__); + return -ENODEV; + } + + if (card->sdio_funcs < SDIO_AL_MAX_FUNCS) { + dev_info(&card->dev, + "SDIO-functions# %d less than expected.\n", + card->sdio_funcs); + return -ENODEV; + } + + /* Check if there is already a device for this card */ + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + if (sdio_al->devices[i] == NULL) + continue; + if (sdio_al->devices[i]->host == card->host) { + sdio_al_dev = sdio_al->devices[i]; + if (sdio_al_dev->state == CARD_INSERTED) + return 0; + clean_sdio_al_device_data(sdio_al_dev); + break; + } + } + + if (!sdio_al_dev) { + sdio_al_dev = kzalloc(sizeof(struct sdio_al_device), + GFP_KERNEL); + if (sdio_al_dev == NULL) + return -ENOMEM; + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES ; ++i) + if (sdio_al->devices[i] == NULL) { + sdio_al->devices[i] = sdio_al_dev; + sdio_al_dev->dev_log = &sdio_al->device_log[i]; + spin_lock_init(&sdio_al_dev->dev_log->log_lock); + #ifdef CONFIG_DEBUG_FS + sdio_al_dbgfs_log[i].data = + sdio_al_dev->dev_log->buffer; + sdio_al_dbgfs_log[i].size = + SDIO_AL_DEBUG_LOG_SIZE; + #endif + break; + } + if (i == MAX_NUM_OF_SDIO_DEVICES) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ":No space " + "in devices array for the device\n"); + return -ENOMEM; + } + } + + dev_info(&card->dev, "SDIO Card claimed.\n"); + sdio_al->skip_print_info = 0; + + sdio_al_dev->state = CARD_INSERTED; + + if (card->host->index == SDIO_BOOTLOADER_CARD_INDEX) + sdio_al->bootloader_dev = sdio_al_dev; + + sdio_al_dev->is_ready = false; + + sdio_al_dev->signature = SDIO_AL_SIGNATURE; + + sdio_al_dev->is_suspended = 0; + sdio_al_dev->is_timer_initialized = false; + + sdio_al_dev->lpm_chan = INVALID_SDIO_CHAN; + + sdio_al_dev->card = card; + sdio_al_dev->host = card->host; + + if (!sdio_al_dev->mailbox) { + sdio_al_dev->mailbox = kzalloc(sizeof(struct sdio_mailbox), + GFP_KERNEL); + if (sdio_al_dev->mailbox == NULL) + return -ENOMEM; + } + + if (!sdio_al_dev->sdioc_sw_header) { + sdio_al_dev->sdioc_sw_header + = kzalloc(sizeof(*sdio_al_dev->sdioc_sw_header), + GFP_KERNEL); + if (sdio_al_dev->sdioc_sw_header == NULL) + return -ENOMEM; + } + + if (!sdio_al_dev->rx_flush_buf) { + sdio_al_dev->rx_flush_buf = kzalloc(RX_FLUSH_BUFFER_SIZE, + GFP_KERNEL); + if (sdio_al_dev->rx_flush_buf == NULL) { + sdio_al_loge(&sdio_al->gen_log, + MODULE_NAME ":Fail to allocate " + "rx_flush_buf for card %d\n", + card->host->index); + return -ENOMEM; + } + } + + sdio_al_dev->timer.data = (unsigned long)sdio_al_dev; + + wake_lock_init(&sdio_al_dev->wake_lock, WAKE_LOCK_SUSPEND, MODULE_NAME); + /* Don't allow sleep until all required clients register */ + sdio_al_vote_for_sleep(sdio_al_dev, 0); + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return -ENODEV; + + /* Init Func#1 */ + ret = sdio_al_enable_func_retry(card->sdio_func[0], "Init Func#1"); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to " + "enable Func#%d\n", card->sdio_func[0]->num); + goto exit; + } + + /* Patch Func CIS tuple issue */ + ret = sdio_set_block_size(card->sdio_func[0], SDIO_AL_BLOCK_SIZE); + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ":Fail to set " + "block size, Func#%d\n", card->sdio_func[0]->num); + goto exit; + } + sdio_al_dev->card->sdio_func[0]->max_blksize = SDIO_AL_BLOCK_SIZE; + + sdio_al_dev->workqueue = create_singlethread_workqueue("sdio_al_wq"); + sdio_al_dev->sdio_al_work.sdio_al_dev = sdio_al_dev; + init_waitqueue_head(&sdio_al_dev->wait_mbox); + + ret = sdio_al_client_setup(sdio_al_dev); + +exit: + sdio_al_release_mutex(sdio_al_dev, __func__); + return ret; +} + +static void sdio_al_sdio_remove(struct sdio_func *func) +{ + struct sdio_al_device *sdio_al_dev = NULL; + int i; + struct mmc_card *card = NULL; + struct platform_device *pdev_arr[SDIO_AL_MAX_CHANNELS]; + + if (!func) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL func\n", + __func__); + return; + } + card = func->card; + + if (!card) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL card\n", + __func__); + return; + } + + /* Find the sdio_al_device of this card */ + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES; ++i) { + if (sdio_al->devices[i] == NULL) + continue; + if (sdio_al->devices[i]->card == card) { + sdio_al_dev = sdio_al->devices[i]; + break; + } + } + if (sdio_al_dev == NULL) { + pr_debug(MODULE_NAME ":%s :NULL sdio_al_dev for card %d\n", + __func__, card->host->index); + return; + } + + if (sdio_al_claim_mutex(sdio_al_dev, __func__)) + return; + + if (sdio_al_dev->state == CARD_REMOVED) { + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + + if (!card->sdio_func[0]) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: NULL " + "func1\n", __func__); + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s for card %d\n", + __func__, card->host->index); + + sdio_al_dev->state = CARD_REMOVED; + + memset(pdev_arr, 0, sizeof(pdev_arr)); + sdio_al_invalidate_sdio_clients(sdio_al_dev, pdev_arr); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: ask_reading_mailbox " + "for card %d\n", __func__, card->host->index); + sdio_al_dev->is_ready = false; /* Flag worker to exit */ + sdio_al_dev->ask_mbox = false; + ask_reading_mailbox(sdio_al_dev); /* Wakeup worker */ + + stop_and_del_timer(sdio_al_dev); + + sdio_al_release_mutex(sdio_al_dev, __func__); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Notifying SDIO " + "clients for card %d", + __func__, sdio_al_dev->host->index); + for (i = 0; i < SDIO_AL_MAX_CHANNELS; i++) { + if (!pdev_arr[i]) + continue; + platform_device_unregister(pdev_arr[i]); + } + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: Finished Notifying " + "SDIO clients for card %d", + __func__, sdio_al_dev->host->index); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: vote for sleep for " + "card %d\n", __func__, card->host->index); + sdio_al_vote_for_sleep(sdio_al_dev, 1); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: flush_workqueue for " + "card %d\n", __func__, card->host->index); + flush_workqueue(sdio_al_dev->workqueue); + destroy_workqueue(sdio_al_dev->workqueue); + wake_lock_destroy(&sdio_al_dev->wake_lock); + + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ":%s: sdio card %d removed." + "\n", __func__, card->host->index); +} + +static void sdio_print_mailbox(char *prefix_str, struct sdio_mailbox *mailbox) +{ + int k = 0; + char buf[256]; + char buf1[10]; + + if (!mailbox) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": mailbox is " + "NULL\n"); + return; + } + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s: pipes 0_7: eot=0x%x," + " thresh=0x%x, overflow=0x%x, " + "underflow=0x%x, mask_thresh=0x%x\n", + prefix_str, mailbox->eot_pipe_0_7, + mailbox->thresh_above_limit_pipe_0_7, + mailbox->overflow_pipe_0_7, + mailbox->underflow_pipe_0_7, + mailbox->mask_thresh_above_limit_pipe_0_7); + + memset(buf, 0, sizeof(buf)); + strncat(buf, ": bytes_avail:", sizeof(buf)); + + for (k = 0 ; k < SDIO_AL_ACTIVE_PIPES ; ++k) { + snprintf(buf1, sizeof(buf1), "%d, ", + mailbox->pipe_bytes_avail[k]); + strncat(buf, buf1, sizeof(buf)); + } + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME "%s", buf); +} + +static void sdio_al_print_info(void) +{ + int i = 0; + int j = 0; + int ret = 0; + struct sdio_mailbox *mailbox = NULL; + struct sdio_mailbox *hw_mailbox = NULL; + struct peer_sdioc_channel_config *ch_config = NULL; + struct sdio_func *func1 = NULL; + struct sdio_func *lpm_func = NULL; + int offset = 0; + int is_ok_to_sleep = 0; + char buf[50]; + + if (sdio_al->skip_print_info == 1) + return; + + sdio_al->skip_print_info = 1; + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - SDIO DEBUG INFO\n", + __func__); + + if (!sdio_al) { + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": %s - ERROR - " + "sdio_al is NULL\n", __func__); + return; + } + + sdio_al_loge(&sdio_al->gen_log, MODULE_NAME ": GPIO mdm2ap_status=%d\n", + sdio_al->pdata->get_mdm2ap_status()); + + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) { + struct sdio_al_device *sdio_al_dev = sdio_al->devices[j]; + + if (sdio_al_dev == NULL) { + continue; + } + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": Host" + " is NULL\n);"); + continue; + } + + snprintf(buf, sizeof(buf), "Card#%d: Shadow HW MB", + sdio_al_dev->host->index); + + /* printing Shadowing HW Mailbox*/ + mailbox = sdio_al_dev->mailbox; + sdio_print_mailbox(buf, mailbox); + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": Card#%d: " + "is_ok_to_sleep=%d\n", + sdio_al_dev->host->index, + sdio_al_dev->is_ok_to_sleep); + + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME ": Card#%d: " + "Shadow channels SW MB:", + sdio_al_dev->host->index); + + /* printing Shadowing SW Mailbox per channel*/ + for (i = 0 ; i < SDIO_AL_MAX_CHANNELS ; ++i) { + struct sdio_channel *ch = &sdio_al_dev->channel[i]; + + if (ch == NULL) { + continue; + } + + if (ch->state == SDIO_CHANNEL_STATE_INVALID) + continue; + + ch_config = &sdio_al_dev->channel[i].ch_config; + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Ch %s: max_rx_thres=0x%x, " + "max_tx_thres=0x%x, tx_buf=0x%x, " + "is_packet_mode=%d, " + "max_packet=0x%x, min_write=0x%x", + ch->name, ch_config->max_rx_threshold, + ch_config->max_tx_threshold, + ch_config->tx_buf_size, + ch_config->is_packet_mode, + ch_config->max_packet_size, + ch->min_write_avail); + + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": total_rx=0x%x, total_tx=0x%x, " + "read_avail=0x%x, write_avail=0x%x, " + "rx_pending=0x%x, num_reads=0x%x, " + "num_notifs=0x%x", ch->total_rx_bytes, + ch->total_tx_bytes, ch->read_avail, + ch->write_avail, ch->rx_pending_bytes, + ch->statistics.total_read_times, + ch->statistics.total_notifs); + } /* end loop over all channels */ + + } /* end loop over all devices */ + + /* reading from client and printing is_host_ok_to_sleep per device */ + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) { + struct sdio_al_device *sdio_al_dev = sdio_al->devices[j]; + + if (sdio_al_verify_func1(sdio_al_dev, __func__)) + continue; + + if (!sdio_al_dev->host) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Host is NULL"); + continue; + } + + if (sdio_al_dev->lpm_chan == INVALID_SDIO_CHAN) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": %s - for Card#%d, is lpm_chan==" + "INVALID_SDIO_CHAN. continuing...", + __func__, sdio_al_dev->host->index); + continue; + } + + offset = offsetof(struct peer_sdioc_sw_mailbox, ch_config)+ + sizeof(struct peer_sdioc_channel_config) * + sdio_al_dev->lpm_chan+ + offsetof(struct peer_sdioc_channel_config, is_host_ok_to_sleep); + + lpm_func = sdio_al_dev->card->sdio_func[sdio_al_dev-> + lpm_chan+1]; + if (!lpm_func) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": %s - lpm_func is NULL for card#%d" + " continuing...\n", __func__, + sdio_al_dev->host->index); + continue; + } + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + ret = sdio_memcpy_fromio(lpm_func, + &is_ok_to_sleep, + SDIOC_SW_MAILBOX_ADDR+offset, + sizeof(int)); + sdio_al_release_mutex(sdio_al_dev, __func__); + + if (ret) + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": %s - fail to read " + "is_HOST_ok_to_sleep from mailbox for card %d", + __func__, sdio_al_dev->host->index); + else + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": Card#%d: " + "is_HOST_ok_to_sleep=%d\n", + sdio_al_dev->host->index, + is_ok_to_sleep); + } + + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES ; ++j) { + struct sdio_al_device *sdio_al_dev = sdio_al->devices[j]; + + if (!sdio_al_dev) + continue; + + /* Reading HW Mailbox */ + hw_mailbox = sdio_al_dev->mailbox; + + if (sdio_al_claim_mutex_and_verify_dev(sdio_al_dev, __func__)) + return; + + if (!sdio_al_dev->card || !sdio_al_dev->card->sdio_func[0]) { + sdio_al_release_mutex(sdio_al_dev, __func__); + return; + } + func1 = sdio_al_dev->card->sdio_func[0]; + ret = sdio_memcpy_fromio(func1, hw_mailbox, + HW_MAILBOX_ADDR, sizeof(*hw_mailbox)); + sdio_al_release_mutex(sdio_al_dev, __func__); + + if (ret) { + sdio_al_loge(sdio_al_dev->dev_log, MODULE_NAME + ": fail to read " + "mailbox for card#%d. " + "continuing...\n", + sdio_al_dev->host->index); + continue; + } + + snprintf(buf, sizeof(buf), "Card#%d: Current HW MB", + sdio_al_dev->host->index); + + /* Printing HW Mailbox */ + sdio_print_mailbox(buf, hw_mailbox); + } +} + +static struct sdio_device_id sdio_al_sdioid[] = { + {.class = 0, .vendor = 0x70, .device = 0x2460}, + {.class = 0, .vendor = 0x70, .device = 0x0460}, + {.class = 0, .vendor = 0x70, .device = 0x23F1}, + {.class = 0, .vendor = 0x70, .device = 0x23F0}, + {} +}; + +static struct sdio_driver sdio_al_sdiofn_driver = { + .name = "sdio_al_sdiofn", + .id_table = sdio_al_sdioid, + .probe = sdio_al_sdio_probe, + .remove = sdio_al_sdio_remove, +}; + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART +/* + * Callback for notifications from restart mudule. + * This function handles only the BEFORE_RESTART notification. + * Stop all the activity on the card and notify our clients. + */ +static int sdio_al_subsys_notifier_cb(struct notifier_block *this, + unsigned long notif_type, + void *data) +{ + if (notif_type != SUBSYS_BEFORE_SHUTDOWN) { + sdio_al_logi(&sdio_al->gen_log, MODULE_NAME ": %s: got " + "notification %ld", __func__, notif_type); + return NOTIFY_DONE; + } + + sdio_al_reset(); + return NOTIFY_OK; +} + +static struct notifier_block sdio_al_nb = { + .notifier_call = sdio_al_subsys_notifier_cb, +}; +#endif + +/** + * Module Init. + * + * @warn: allocate sdio_al context before registering driver. + * + */ +static int __init sdio_al_init(void) +{ + int ret = 0; + int i; + + pr_debug(MODULE_NAME ":sdio_al_init\n"); + + pr_info(MODULE_NAME ":SDIO-AL SW version %s\n", + DRV_VERSION); + + sdio_al = kzalloc(sizeof(struct sdio_al), GFP_KERNEL); + if (sdio_al == NULL) + return -ENOMEM; + + for (i = 0; i < MAX_NUM_OF_SDIO_DEVICES ; ++i) + sdio_al->devices[i] = NULL; + + sdio_al->unittest_mode = false; + + sdio_al->debug.debug_lpm_on = debug_lpm_on; + sdio_al->debug.debug_data_on = debug_data_on; + sdio_al->debug.debug_close_on = debug_close_on; + +#ifdef CONFIG_DEBUG_FS + sdio_al_debugfs_init(); +#endif + + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART + sdio_al->subsys_notif_handle = subsys_notif_register_notifier( + "external_modem", &sdio_al_nb); +#endif + + ret = platform_driver_register(&msm_sdio_al_driver); + if (ret) { + pr_err(MODULE_NAME ": platform_driver_register failed: %d\n", + ret); + goto exit; + } + + sdio_register_driver(&sdio_al_sdiofn_driver); + + spin_lock_init(&sdio_al->gen_log.log_lock); + +exit: + if (ret) + kfree(sdio_al); + return ret; +} + +/** + * Module Exit. + * + * Free allocated memory. + * Disable SDIO-Card. + * Unregister driver. + * + */ +static void __exit sdio_al_exit(void) +{ + if (sdio_al == NULL) + return; + + pr_debug(MODULE_NAME ":sdio_al_exit\n"); + +#ifdef CONFIG_MSM_SUBSYSTEM_RESTART + subsys_notif_unregister_notifier( + sdio_al->subsys_notif_handle, &sdio_al_nb); +#endif + + sdio_al_tear_down(); + + sdio_unregister_driver(&sdio_al_sdiofn_driver); + + kfree(sdio_al); + +#ifdef CONFIG_DEBUG_FS + sdio_al_debugfs_cleanup(); +#endif + + platform_driver_unregister(&msm_sdio_al_driver); + + pr_debug(MODULE_NAME ":sdio_al_exit complete\n"); +} + +module_init(sdio_al_init); +module_exit(sdio_al_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SDIO Abstraction Layer"); +MODULE_AUTHOR("Amir Samuelov "); +MODULE_VERSION(DRV_VERSION); + diff --git a/arch/arm/mach-msm/sdio_al_dloader.c b/arch/arm/mach-msm/sdio_al_dloader.c new file mode 100644 index 00000000000..f48c32b8f4b --- /dev/null +++ b/arch/arm/mach-msm/sdio_al_dloader.c @@ -0,0 +1,2541 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Downloader + * + * To be used with Qualcomm's SDIO-Client connected to this host. + */ + +/* INCLUDES */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdio_al_private.h" +#include +#include +#include +#include +#include +#include + +/* DEFINES AND MACROS */ +#define MAX_NUM_DEVICES 1 +#define TTY_SDIO_DEV "tty_sdio_0" +#define TTY_SDIO_DEV_TEST "tty_sdio_test_0" +#define SDIOC_MAILBOX_ADDRESS 0 +#define SDIO_DL_BLOCK_SIZE 512 +#define SDIO_DL_MAIN_THREAD_NAME "sdio_tty_main_thread" +#define SDIOC_DL_BUFF_ADDRESS 0 +#define SDIOC_UP_BUFF_ADDRESS 0x4 +#define SDIOC_DL_BUFF_SIZE_OFFSET 0x8 +#define SDIOC_UP_BUFF_SIZE_OFFSET 0xC +#define SDIOC_DL_WR_PTR 0x10 +#define SDIOC_DL_RD_PTR 0x14 +#define SDIOC_UL_WR_PTR 0x18 +#define SDIOC_UL_RD_PTR 0x1C +#define SDIOC_EXIT_PTR 0x20 +#define SDIOC_OP_MODE_PTR 0x24 +#define SDIOC_PTRS_OFFSET 0x10 +#define SDIOC_PTR_REGS_SIZE 0x10 +#define SDIOC_CFG_REGS_SIZE 0x10 +#define WRITE_RETRIES 0xFFFFFFFF +#define INPUT_SPEED 4800 +#define OUTPUT_SPEED 4800 +#define SDIOC_EXIT_CODE 0xDEADDEAD +#define SLEEP_MS 10 +#define PRINTING_GAP 200 +#define TIMER_DURATION 10 +#define PUSH_TIMER_DURATION 5000 +#define MULTIPLE_RATIO 1 +#define MS_IN_SEC 1000 +#define BITS_IN_BYTE 8 +#define BYTES_IN_KB 1024 +#define WRITE_TILL_END_RETRIES 5 +#define SDIO_DLD_NORMAL_MODE_NAME "SDIO DLD NORMAL MODE" +#define SDIO_DLD_BOOT_TEST_MODE_NAME "SDIO DLD BOOT TEST MODE" +#define SDIO_DLD_AMSS_TEST_MODE_NAME "SDIO DLD AMSS TEST MODE" +#define TEST_NAME_MAX_SIZE 30 +#define PUSH_STRING +#define SDIO_DLD_OUTGOING_BUFFER_SIZE (48*1024*MULTIPLE_RATIO) + +/* FORWARD DECLARATIONS */ +static int sdio_dld_open(struct tty_struct *tty, struct file *file); +static void sdio_dld_close(struct tty_struct *tty, struct file *file); +static int sdio_dld_write_callback(struct tty_struct *tty, + const unsigned char *buf, int count); +static int sdio_dld_write_room(struct tty_struct *tty); +static int sdio_dld_main_task(void *card); +static void sdio_dld_print_info(void); +#ifdef CONFIG_DEBUG_FS +static int sdio_dld_debug_info_open(struct inode *inode, struct file *file); +static ssize_t sdio_dld_debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos); +#endif + + +/* STRUCTURES AND TYPES */ +enum sdio_dld_op_mode { + SDIO_DLD_NO_MODE = 0, + SDIO_DLD_NORMAL_MODE = 1, + SDIO_DLD_BOOT_TEST_MODE = 2, + SDIO_DLD_AMSS_TEST_MODE = 3, + SDIO_DLD_NUM_OF_MODES, +}; + +struct sdioc_reg_sequential_chunk_ptrs { + unsigned int dl_wr_ptr; + unsigned int dl_rd_ptr; + unsigned int up_wr_ptr; + unsigned int up_rd_ptr; +}; + +struct sdioc_reg_sequential_chunk_cfg { + unsigned int dl_buff_address; + unsigned int up_buff_address; + unsigned int dl_buff_size; + unsigned int ul_buff_size; +}; + +struct sdioc_reg { + unsigned int reg_val; + unsigned int reg_offset; +}; + +struct sdioc_reg_chunk { + struct sdioc_reg dl_buff_address; + struct sdioc_reg up_buff_address; + struct sdioc_reg dl_buff_size; + struct sdioc_reg ul_buff_size; + struct sdioc_reg dl_wr_ptr; + struct sdioc_reg dl_rd_ptr; + struct sdioc_reg up_wr_ptr; + struct sdioc_reg up_rd_ptr; + struct sdioc_reg good_to_exit_ptr; +}; + +struct sdio_data { + char *data; + int offset_read_p; + int offset_write_p; + int buffer_size; + int num_of_bytes_in_use; +}; + +struct sdio_dld_data { + struct sdioc_reg_chunk sdioc_reg; + struct sdio_data incoming_data; + struct sdio_data outgoing_data; +}; + +struct sdio_dld_wait_event { + wait_queue_head_t wait_event; + int wake_up_signal; +}; + +struct sdio_dld_task { + struct task_struct *dld_task; + const char *task_name; + struct sdio_dld_wait_event exit_wait; + atomic_t please_close; +}; + +#ifdef CONFIG_DEBUG_FS +struct sdio_dloader_debug { + struct dentry *sdio_dld_debug_root; + struct dentry *sdio_al_dloader; +}; + +const struct file_operations sdio_dld_debug_info_ops = { + .open = sdio_dld_debug_info_open, + .write = sdio_dld_debug_info_write, +}; +#endif + +struct sdio_downloader { + int sdioc_boot_func; + struct sdio_dld_wait_event write_callback_event; + struct sdio_dld_task dld_main_thread; + struct tty_driver *tty_drv; + struct tty_struct *tty_str; + struct sdio_dld_data sdio_dloader_data; + struct mmc_card *card; + int(*done_callback)(void); + struct sdio_dld_wait_event main_loop_event; + struct timer_list timer; + unsigned int poll_ms; + struct timer_list push_timer; + unsigned int push_timer_ms; + enum sdio_dld_op_mode op_mode; + char op_mode_name[TEST_NAME_MAX_SIZE]; +}; + +struct sdio_dld_global_info { + int global_bytes_write_toio; + int global_bytes_write_tty; + int global_bytes_read_fromio; + int global_bytes_push_tty; + u64 start_time; + u64 end_time; + u64 delta_jiffies; + unsigned int time_msec; + unsigned int throughput; + int cl_dl_wr_ptr; + int cl_dl_rd_ptr; + int cl_up_wr_ptr; + int cl_up_rd_ptr; + int host_read_ptr; + int host_write_ptr; + int cl_dl_buffer_size; + int cl_up_buffer_size; + int host_outgoing_buffer_size; + int cl_dl_buffer_address; + int cl_up_buffer_address; +}; + +static const struct tty_operations sdio_dloader_tty_ops = { + .open = sdio_dld_open, + .close = sdio_dld_close, + .write = sdio_dld_write_callback, + .write_room = sdio_dld_write_room, +}; + +/* GLOBAL VARIABLES */ +struct sdio_downloader *sdio_dld; +struct sdio_dld_global_info sdio_dld_info; +static char outgoing_data_buffer[SDIO_DLD_OUTGOING_BUFFER_SIZE]; + +static DEFINE_SPINLOCK(lock1); +static unsigned long lock_flags1; +static DEFINE_SPINLOCK(lock2); +static unsigned long lock_flags2; + +/* + * sdio_op_mode sets the operation mode of the sdio_dloader - + * it may be in NORMAL_MODE, BOOT_TEST_MODE or AMSS_TEST_MODE + */ +static int sdio_op_mode = (int)SDIO_DLD_NORMAL_MODE; +module_param(sdio_op_mode, int, 0); + +#ifdef CONFIG_DEBUG_FS + +struct sdio_dloader_debug sdio_dld_debug; + +#define ARR_SIZE 30000 +#define SDIO_DLD_DEBUGFS_INIT_VALUE 87654321 +#define SDIO_DLD_DEBUGFS_CASE_1_CODE 11111111 +#define SDIO_DLD_DEBUGFS_CASE_2_CODE 22222222 +#define SDIO_DLD_DEBUGFS_CASE_3_CODE 33333333 +#define SDIO_DLD_DEBUGFS_CASE_4_CODE 44444444 +#define SDIO_DLD_DEBUGFS_CASE_5_CODE 55555555 +#define SDIO_DLD_DEBUGFS_CASE_6_CODE 66666666 +#define SDIO_DLD_DEBUGFS_CASE_7_CODE 77777777 +#define SDIO_DLD_DEBUGFS_CASE_8_CODE 88888888 +#define SDIO_DLD_DEBUGFS_CASE_9_CODE 99999999 +#define SDIO_DLD_DEBUGFS_CASE_10_CODE 10101010 +#define SDIO_DLD_DEBUGFS_CASE_11_CODE 11001100 +#define SDIO_DLD_DEBUGFS_CASE_12_CODE 12001200 +#define SDIO_DLD_DEBUGFS_LOOP_WAIT 7 +#define SDIO_DLD_DEBUGFS_LOOP_WAKEUP 8 +#define SDIO_DLD_DEBUGFS_CB_WAIT 3 +#define SDIO_DLD_DEBUGFS_CB_WAKEUP 4 + +static int curr_index; +struct ptrs { + int h_w_ptr; + int h_r_ptr; + int c_u_w_ptr; + int c_u_r_ptr; + int code; + int h_has_to_send; + int c_has_to_receive; + int min_of; + int reserve2; + int tty_count; + int write_tty; + int write_toio; + int loop_wait_wake; + int cb_wait_wake; + int c_d_w_ptr; + int c_d_r_ptr; + int to_read; + int push_to_tty; + int global_tty_send; + int global_sdio_send; + int global_tty_received; + int global_sdio_received; + int reserve22; + int reserve23; + int reserve24; + int reserve25; + int reserve26; + int reserve27; + int reserve28; + int reserve29; + int reserve30; + int reserve31; +}; + +struct global_data { + int curr_i; + int duration_ms; + int global_bytes_sent; + int throughput_Mbs; + int host_outgoing_buffer_size_KB; + int client_up_buffer_size_KB; + int client_dl_buffer_size_KB; + int client_dl_buffer_address; + int client_up_buffer_address; + int global_bytes_received; + int global_bytes_pushed; + int reserve11; + int reserve12; + int reserve13; + int reserve14; + int reserve15; + int reserve16; + int reserve17; + int reserve18; + int reserve19; + int reserve20; + int reserve21; + int reserve22; + int reserve23; + int reserve24; + int reserve25; + int reserve26; + int reserve27; + int reserve28; + int reserve29; + int reserve30; + int reserve31; + struct ptrs ptr_array[ARR_SIZE]; +}; + +static struct global_data gd; +static struct debugfs_blob_wrapper blob; +static struct dentry *root; +static struct dentry *dld; + +struct debugfs_global { + int global_8k_has; + int global_9k_has; + int global_min; + int global_count; + int global_write_tty; + int global_write_toio; + int global_bytes_cb_tty; + int global_to_read; + int global_push_to_tty; + int global_tty_send; + int global_sdio_send; + int global_sdio_received; + int global_tty_push; +}; + +static struct debugfs_global debugfs_glob; + +static void update_standard_fields(int index) +{ + + gd.ptr_array[index].global_tty_send = + sdio_dld_info.global_bytes_write_tty; + gd.ptr_array[index].global_sdio_send = + sdio_dld_info.global_bytes_write_toio; + gd.ptr_array[index].global_tty_received = + sdio_dld_info.global_bytes_push_tty; + gd.ptr_array[index].global_sdio_received = + sdio_dld_info.global_bytes_read_fromio; +} + +static void update_gd(int code) +{ + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + int index = curr_index%ARR_SIZE; + + gd.curr_i = curr_index; + gd.duration_ms = 0; + gd.global_bytes_sent = 0; + gd.throughput_Mbs = 0; + gd.host_outgoing_buffer_size_KB = 0; + gd.client_up_buffer_size_KB = 0; + gd.client_dl_buffer_size_KB = 0; + gd.client_dl_buffer_address = 0; + gd.client_up_buffer_address = 0; + gd.global_bytes_received = 0; + gd.global_bytes_pushed = 0; + gd.reserve11 = 0; + gd.reserve12 = 0; + gd.reserve13 = 0; + gd.reserve14 = 0; + gd.reserve15 = 0; + gd.reserve16 = 0; + gd.reserve17 = 0; + gd.reserve18 = 0; + gd.reserve19 = 0; + gd.reserve20 = 0; + gd.reserve21 = 0; + gd.reserve22 = 0; + gd.reserve23 = 0; + gd.reserve24 = 0; + gd.reserve25 = 0; + gd.reserve26 = 0; + gd.reserve27 = 0; + gd.reserve28 = 0; + gd.reserve29 = 0; + gd.reserve30 = 0; + gd.reserve31 = 0; + + gd.ptr_array[index].h_w_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*0*/ + gd.ptr_array[index].h_r_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*1*/ + gd.ptr_array[index].c_u_w_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*2*/ + gd.ptr_array[index].c_u_r_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*3*/ + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_INIT_VALUE; /*4*/ + gd.ptr_array[index].h_has_to_send = SDIO_DLD_DEBUGFS_INIT_VALUE;/*5*/ + gd.ptr_array[index].c_has_to_receive = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*6*/ + gd.ptr_array[index].min_of = SDIO_DLD_DEBUGFS_INIT_VALUE; /*7*/ + gd.ptr_array[index].reserve2 = SDIO_DLD_DEBUGFS_INIT_VALUE; /*8*/ + gd.ptr_array[index].tty_count = SDIO_DLD_DEBUGFS_INIT_VALUE; /*9*/ + gd.ptr_array[index].write_tty = SDIO_DLD_DEBUGFS_INIT_VALUE; /*A*/ + gd.ptr_array[index].write_toio = SDIO_DLD_DEBUGFS_INIT_VALUE; /*B*/ + gd.ptr_array[index].loop_wait_wake = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*C*/ + gd.ptr_array[index].cb_wait_wake = SDIO_DLD_DEBUGFS_INIT_VALUE; /*D*/ + gd.ptr_array[index].c_d_w_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*E*/ + gd.ptr_array[index].c_d_r_ptr = SDIO_DLD_DEBUGFS_INIT_VALUE; /*F*/ + gd.ptr_array[index].to_read = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x10*/ + gd.ptr_array[index].push_to_tty = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x11*/ + gd.ptr_array[index].global_tty_send = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x12*/ + gd.ptr_array[index].global_sdio_send = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x13*/ + gd.ptr_array[index].global_tty_received = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x14*/ + gd.ptr_array[index].global_sdio_received = + SDIO_DLD_DEBUGFS_INIT_VALUE; /*0x15*/ + gd.ptr_array[index].reserve22 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve23 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve24 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve25 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve26 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve27 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve28 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve29 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve30 = SDIO_DLD_DEBUGFS_INIT_VALUE; + gd.ptr_array[index].reserve31 = SDIO_DLD_DEBUGFS_INIT_VALUE; + + switch (code) { + case SDIO_DLD_DEBUGFS_CASE_1_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_1_CODE; + gd.ptr_array[index].h_w_ptr = outgoing->offset_write_p; + gd.ptr_array[index].h_r_ptr = outgoing->offset_read_p; + gd.ptr_array[index].c_u_w_ptr = reg_str->up_wr_ptr.reg_val; + gd.ptr_array[index].c_u_r_ptr = reg_str->up_rd_ptr.reg_val; + gd.ptr_array[index].c_d_w_ptr = reg_str->dl_wr_ptr.reg_val; + gd.ptr_array[index].c_d_r_ptr = reg_str->dl_rd_ptr.reg_val; + break; + + case SDIO_DLD_DEBUGFS_CASE_2_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_2_CODE; + gd.ptr_array[index].c_u_r_ptr = reg_str->up_rd_ptr.reg_val; + gd.ptr_array[index].c_u_w_ptr = reg_str->up_wr_ptr.reg_val; + gd.ptr_array[index].h_has_to_send = debugfs_glob.global_8k_has; + gd.ptr_array[index].c_has_to_receive = + debugfs_glob.global_9k_has; + gd.ptr_array[index].min_of = debugfs_glob.global_min; + break; + + case SDIO_DLD_DEBUGFS_CASE_3_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_3_CODE; + gd.ptr_array[index].h_w_ptr = outgoing->offset_write_p; + gd.ptr_array[index].h_r_ptr = outgoing->offset_read_p; + gd.ptr_array[index].write_tty = debugfs_glob.global_write_tty; + break; + + case SDIO_DLD_DEBUGFS_CASE_4_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_4_CODE; + gd.ptr_array[index].h_w_ptr = outgoing->offset_write_p; + gd.ptr_array[index].h_r_ptr = outgoing->offset_read_p; + gd.ptr_array[index].c_u_r_ptr = reg_str->up_rd_ptr.reg_val; + gd.ptr_array[index].c_u_w_ptr = reg_str->up_wr_ptr.reg_val; + gd.ptr_array[index].write_toio = + debugfs_glob.global_write_toio; + break; + + case SDIO_DLD_DEBUGFS_CASE_5_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_5_CODE; + gd.ptr_array[index].tty_count = debugfs_glob.global_count; + break; + + case SDIO_DLD_DEBUGFS_CASE_6_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_6_CODE; + gd.ptr_array[index].loop_wait_wake = 7; + break; + + case SDIO_DLD_DEBUGFS_CASE_7_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_7_CODE; + gd.ptr_array[index].loop_wait_wake = 8; + break; + + case SDIO_DLD_DEBUGFS_CASE_8_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_8_CODE; + gd.ptr_array[index].cb_wait_wake = 3; + break; + + case SDIO_DLD_DEBUGFS_CASE_9_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_9_CODE; + gd.ptr_array[index].cb_wait_wake = 4; + break; + + case SDIO_DLD_DEBUGFS_CASE_10_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_10_CODE; + gd.ptr_array[index].cb_wait_wake = + debugfs_glob.global_bytes_cb_tty; + break; + + case SDIO_DLD_DEBUGFS_CASE_11_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_11_CODE; + gd.ptr_array[index].to_read = debugfs_glob.global_to_read; + break; + + case SDIO_DLD_DEBUGFS_CASE_12_CODE: + gd.ptr_array[index].code = SDIO_DLD_DEBUGFS_CASE_12_CODE; + gd.ptr_array[index].push_to_tty = + debugfs_glob.global_push_to_tty; + break; + + default: + break; + } + update_standard_fields(index); + curr_index++; +} + +static int bootloader_debugfs_init(void) +{ + /* /sys/kernel/debug/bootloader there will be dld_arr file */ + root = debugfs_create_dir("bootloader", NULL); + if (!root) { + pr_info(MODULE_NAME ": %s - creating root dir " + "failed\n", __func__); + return -ENODEV; + } + + blob.data = &gd; + blob.size = sizeof(struct global_data); + dld = debugfs_create_blob("dld_arr", S_IRUGO, root, &blob); + if (!dld) { + debugfs_remove_recursive(root); + pr_err(MODULE_NAME ": %s, failed to create debugfs entry\n", + __func__); + return -ENODEV; + } + + return 0; +} + +/* +* for triggering the sdio_dld info use: +* echo 1 > /sys/kernel/debug/sdio_al_dld/sdio_al_dloader_info +*/ +static int sdio_dld_debug_init(void) +{ + sdio_dld_debug.sdio_dld_debug_root = + debugfs_create_dir("sdio_al_dld", NULL); + if (!sdio_dld_debug.sdio_dld_debug_root) { + pr_err(MODULE_NAME ": %s - Failed to create folder. " + "sdio_dld_debug_root is NULL", + __func__); + return -ENOENT; + } + + sdio_dld_debug.sdio_al_dloader = debugfs_create_file( + "sdio_al_dloader_info", + S_IRUGO | S_IWUGO, + sdio_dld_debug.sdio_dld_debug_root, + NULL, + &sdio_dld_debug_info_ops); + + if (!sdio_dld_debug.sdio_al_dloader) { + pr_err(MODULE_NAME ": %s - Failed to create a file. " + "sdio_al_dloader is NULL", + __func__); + debugfs_remove(sdio_dld_debug.sdio_dld_debug_root); + sdio_dld_debug.sdio_dld_debug_root = NULL; + return -ENOENT; + } + + return 0; +} + +static int sdio_dld_debug_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t sdio_dld_debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + sdio_dld_print_info(); + return count; +} +#endif /* CONFIG_DEBUG_FS */ + +static void sdio_dld_print_info(void) +{ + + sdio_dld_info.end_time = get_jiffies_64(); /* read the current time */ + sdio_dld_info.delta_jiffies = + sdio_dld_info.end_time - sdio_dld_info.start_time; + sdio_dld_info.time_msec = jiffies_to_msecs(sdio_dld_info.delta_jiffies); + + sdio_dld_info.throughput = sdio_dld_info.global_bytes_write_toio * + BITS_IN_BYTE / sdio_dld_info.time_msec; + sdio_dld_info.throughput /= MS_IN_SEC; + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - DURATION IN MSEC = %d\n", + __func__, + sdio_dld_info.time_msec); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - BYTES WRITTEN ON SDIO BUS " + "= %d...BYTES SENT BY TTY = %d", + __func__, + sdio_dld_info.global_bytes_write_toio, + sdio_dld_info.global_bytes_write_tty); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - BYTES RECEIVED ON SDIO BUS " + "= %d...BYTES SENT TO TTY = %d", + __func__, + sdio_dld_info.global_bytes_read_fromio, + sdio_dld_info.global_bytes_push_tty); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - THROUGHPUT=%d Mbit/Sec", + __func__, sdio_dld_info.throughput); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT DL_BUFFER_SIZE=%d" + " KB..CLIENT UL_BUFFER=%d KB\n", + __func__, + sdio_dld_info.cl_dl_buffer_size/BYTES_IN_KB, + sdio_dld_info.cl_up_buffer_size/BYTES_IN_KB); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - HOST OUTGOING BUFFER_SIZE" + "=%d KB", + __func__, + sdio_dld_info.host_outgoing_buffer_size/BYTES_IN_KB); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT DL BUFFER " + "ADDRESS = 0x%x", __func__, + sdio_dld_info.cl_dl_buffer_address); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT UP BUFFER " + "ADDRESS = 0x%x", + __func__, + sdio_dld_info.cl_up_buffer_address); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - UPLINK BUFFER - " + "READ POINTER = %d", __func__, + sdio_dld_info.cl_up_rd_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - UPLINK BUFFER - " + "WRITE POINTER = %d", __func__, + sdio_dld_info.cl_up_wr_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - DOWNLINK BUFFER - " + "READ POINTER = %d", __func__, + sdio_dld_info.cl_dl_rd_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - CLIENT - DOWNLINK BUFFER - " + "WRITE POINTER = %d", __func__, + sdio_dld_info.cl_dl_wr_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - HOST - OUTGOING BUFFER - " + "READ POINTER = %d", __func__, + sdio_dld_info.host_read_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - HOST - OUTGOING BUFFER - " + "WRITE POINTER = %d", __func__, + sdio_dld_info.host_write_ptr); + + pr_info(MODULE_NAME ": %s, FLASHLESS BOOT - END DEBUG INFO", __func__); +} + +/** + * sdio_dld_set_op_mode + * sets the op_mode and the name of the op_mode. Also, in case + * it's invalid mode sets op_mode to SDIO_DLD_NORMAL_MODE + * + * @op_mode: the operation mode to be set + * @return NONE + */ +static void sdio_dld_set_op_mode(enum sdio_dld_op_mode op_mode) +{ + sdio_dld->op_mode = op_mode; + + switch (op_mode) { + case SDIO_DLD_NORMAL_MODE: + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_NORMAL_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + case SDIO_DLD_BOOT_TEST_MODE: + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_BOOT_TEST_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + case SDIO_DLD_AMSS_TEST_MODE: + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_AMSS_TEST_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + default: + sdio_dld->op_mode = SDIO_DLD_NORMAL_MODE; + pr_err(MODULE_NAME ": %s - Invalid Op_Mode = %d. Settings " + "Op_Mode to default - NORMAL_MODE\n", + __func__, op_mode); + memcpy(sdio_dld->op_mode_name, + SDIO_DLD_NORMAL_MODE_NAME, TEST_NAME_MAX_SIZE); + break; + } + + if (sdio_dld->op_mode_name != NULL) { + pr_info(MODULE_NAME ": %s - FLASHLESS BOOT - Op_Mode is set to " + "%s\n", __func__, sdio_dld->op_mode_name); + } else { + pr_info(MODULE_NAME ": %s - FLASHLESS BOOT - op_mode_name is " + "NULL\n", __func__); + } +} + +/** + * sdio_dld_allocate_local_buffers + * allocates local outgoing and incoming buffers and also sets + * threshold for outgoing data. + * + * @return 0 on success or negative value on error. + */ +static int sdio_dld_allocate_local_buffers(void) +{ + struct sdioc_reg_chunk *reg_str = &sdio_dld->sdio_dloader_data. + sdioc_reg; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + struct sdio_data *incoming = &sdio_dld->sdio_dloader_data.incoming_data; + + incoming->data = + kzalloc(reg_str->dl_buff_size.reg_val, GFP_KERNEL); + + if (!incoming->data) { + pr_err(MODULE_NAME ": %s - param ""incoming->data"" is NULL. " + "Couldn't allocate incoming_data local buffer\n", + __func__); + return -ENOMEM; + } + + incoming->buffer_size = reg_str->dl_buff_size.reg_val; + + outgoing->data = outgoing_data_buffer; + + outgoing->buffer_size = SDIO_DLD_OUTGOING_BUFFER_SIZE; + + if (outgoing->buffer_size != + reg_str->ul_buff_size.reg_val*MULTIPLE_RATIO) { + pr_err(MODULE_NAME ": %s - HOST outgoing buffer size (%d bytes)" + "must be a multiple of ClIENT uplink buffer size (%d " + "bytes). HOST_SIZE == n*CLIENT_SIZE.(n=1,2,3...)\n", + __func__, + SDIO_DLD_OUTGOING_BUFFER_SIZE, + reg_str->ul_buff_size.reg_val); + kfree(incoming->data); + return -EINVAL; + } + + /* keep sdio_dld_info up to date */ + sdio_dld_info.host_outgoing_buffer_size = outgoing->buffer_size; + + return 0; +} + +/** + * sdio_dld_dealloc_local_buffers frees incoming and outgoing + * buffers. + * + * @return None. + */ +static void sdio_dld_dealloc_local_buffers(void) +{ + kfree((void *)sdio_dld->sdio_dloader_data.incoming_data.data); +} + +/** + * mailbox_to_seq_chunk_read_cfg + * reads 4 configuration registers of mailbox from str_func, as + * a sequentail chunk in memory, and updates global struct + * accordingly. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int mailbox_to_seq_chunk_read_cfg(struct sdio_func *str_func) +{ + struct sdioc_reg_sequential_chunk_cfg seq_chunk; + struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg; + int status = 0; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + /* reading SDIOC_MAILBOX_SIZE bytes from SDIOC_MAILBOX_ADDRESS */ + status = sdio_memcpy_fromio(str_func, + (void *)&seq_chunk, + SDIOC_MAILBOX_ADDRESS, + SDIOC_CFG_REGS_SIZE); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_fromio()" + " READING CFG MAILBOX failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + + reg->dl_buff_address.reg_val = seq_chunk.dl_buff_address; + reg->up_buff_address.reg_val = seq_chunk.up_buff_address; + reg->dl_buff_size.reg_val = seq_chunk.dl_buff_size; + reg->ul_buff_size.reg_val = seq_chunk.ul_buff_size; + + /* keep sdio_dld_info up to date */ + sdio_dld_info.cl_dl_buffer_size = seq_chunk.dl_buff_size; + sdio_dld_info.cl_up_buffer_size = seq_chunk.ul_buff_size; + sdio_dld_info.cl_dl_buffer_address = seq_chunk.dl_buff_address; + sdio_dld_info.cl_up_buffer_address = seq_chunk.up_buff_address; + + return status; +} + +/** + * mailbox_to_seq_chunk_read_ptrs + * reads 4 pointers registers of mailbox from str_func, as a + * sequentail chunk in memory, and updates global struct + * accordingly. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int mailbox_to_seq_chunk_read_ptrs(struct sdio_func *str_func) +{ + struct sdioc_reg_sequential_chunk_ptrs seq_chunk; + struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg; + int status = 0; + + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + static int counter = 1; + static int offset_write_p; + static int offset_read_p; + static int up_wr_ptr; + static int up_rd_ptr; + static int dl_wr_ptr; + static int dl_rd_ptr; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + /* reading SDIOC_MAILBOX_SIZE bytes from SDIOC_MAILBOX_ADDRESS */ + status = sdio_memcpy_fromio(str_func, + (void *)&seq_chunk, + SDIOC_PTRS_OFFSET, + SDIOC_PTR_REGS_SIZE); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_fromio()" + " READING PTRS MAILBOX failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + + reg->dl_rd_ptr.reg_val = seq_chunk.dl_rd_ptr; + reg->dl_wr_ptr.reg_val = seq_chunk.dl_wr_ptr; + reg->up_rd_ptr.reg_val = seq_chunk.up_rd_ptr; + reg->up_wr_ptr.reg_val = seq_chunk.up_wr_ptr; + + /* keeping sdio_dld_info up to date */ + sdio_dld_info.cl_dl_rd_ptr = seq_chunk.dl_rd_ptr; + sdio_dld_info.cl_dl_wr_ptr = seq_chunk.dl_wr_ptr; + sdio_dld_info.cl_up_rd_ptr = seq_chunk.up_rd_ptr; + sdio_dld_info.cl_up_wr_ptr = seq_chunk.up_wr_ptr; + + + /* DEBUG - if there was a change in value */ + if ((offset_write_p != outgoing->offset_write_p) || + (offset_read_p != outgoing->offset_read_p) || + (up_wr_ptr != reg->up_wr_ptr.reg_val) || + (up_rd_ptr != reg->up_rd_ptr.reg_val) || + (dl_wr_ptr != reg->dl_wr_ptr.reg_val) || + (dl_rd_ptr != reg->dl_rd_ptr.reg_val) || + (counter % PRINTING_GAP == 0)) { + counter = 1; + pr_debug(MODULE_NAME ": %s MailBox pointers: BLOCK_SIZE=%d, " + "hw=%d, hr=%d, cuw=%d, cur=%d, cdw=%d, cdr=%d\n", + __func__, + SDIO_DL_BLOCK_SIZE, + outgoing->offset_write_p, + outgoing->offset_read_p, + reg->up_wr_ptr.reg_val, + reg->up_rd_ptr.reg_val, + reg->dl_wr_ptr.reg_val, + reg->dl_rd_ptr.reg_val); + +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_1_CODE); +#endif + /* update static variables */ + offset_write_p = outgoing->offset_write_p; + offset_read_p = outgoing->offset_read_p; + up_wr_ptr = reg->up_wr_ptr.reg_val; + up_rd_ptr = reg->up_rd_ptr.reg_val; + dl_wr_ptr = reg->dl_wr_ptr.reg_val; + dl_rd_ptr = reg->dl_rd_ptr.reg_val; + } else { + counter++; + } + return status; +} + +/** + * sdio_dld_init_func + * enables the sdio func, and sets the func block size. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_init_func(struct sdio_func *str_func) +{ + int status1 = 0; + int status2 = 0; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + status1 = sdio_enable_func(str_func); + if (status1) { + sdio_release_host(str_func); + pr_err(MODULE_NAME ": %s - sdio_enable_func() failed. " + "status=%d\n", __func__, status1); + return status1; + } + + status2 = sdio_set_block_size(str_func, SDIO_DL_BLOCK_SIZE); + if (status2) { + pr_err(MODULE_NAME ": %s - sdio_set_block_size() failed. " + "status=%d\n", __func__, status2); + status1 = sdio_disable_func(str_func); + if (status1) { + pr_err(MODULE_NAME ": %s - sdio_disable_func() " + "failed. status=%d\n", __func__, status1); + } + sdio_release_host(str_func); + return status2; + } + + sdio_release_host(str_func); + str_func->max_blksize = SDIO_DL_BLOCK_SIZE; + return 0; +} + +/** + * sdio_dld_allocate_buffers + * initializes the sdio func, and then reads the mailbox, in + * order to allocate incoming and outgoing buffers according to + * the size that was read from the mailbox. + * + * @str_func: a pointer to func struct. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_allocate_buffers(struct sdio_func *str_func) +{ + int status = 0; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + status = mailbox_to_seq_chunk_read_cfg(str_func); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "mailbox_to_seq_chunk_read_cfg(). status=%d\n", + __func__, status); + return status; + } + + status = sdio_dld_allocate_local_buffers(); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_allocate_local_buffers(). status=%d\n", + __func__, status); + return status; + } + return 0; +} + +/** + * sdio_dld_create_thread + * creates thread and wakes it up. + * + * @return 0 on success or negative value on error. + */ +static int sdio_dld_create_thread(void) +{ + sdio_dld->dld_main_thread.task_name = SDIO_DL_MAIN_THREAD_NAME; + + sdio_dld->dld_main_thread.dld_task = + kthread_create(sdio_dld_main_task, + (void *)(sdio_dld->card), + sdio_dld->dld_main_thread.task_name); + + if (IS_ERR(sdio_dld->dld_main_thread.dld_task)) { + pr_err(MODULE_NAME ": %s - kthread_create() failed\n", + __func__); + return -ENOMEM; + } + wake_up_process(sdio_dld->dld_main_thread.dld_task); + return 0; +} + +/** + * start_timer + * sets the timer and starts. + * + * @timer: the timer to configure and add + * @ms: the ms until it expires + * @return None. + */ +static void start_timer(struct timer_list *timer, unsigned int ms) +{ + if ((ms == 0) || (timer == NULL)) { + pr_err(MODULE_NAME ": %s - invalid parameter", __func__); + } else { + timer->expires = jiffies + + msecs_to_jiffies(ms); + add_timer(timer); + } +} + +/** + * sdio_dld_timer_handler + * this is the timer handler. whenever it is invoked, it wakes + * up the main loop task, and the write callback, and starts + * the timer again. + * + * @data: a pointer to the tty device driver structure. + * @return None. + */ + +static void sdio_dld_timer_handler(unsigned long data) +{ + pr_debug(MODULE_NAME " Timer Expired\n"); + spin_lock_irqsave(&lock2, lock_flags2); + if (sdio_dld->main_loop_event.wake_up_signal == 0) { + sdio_dld->main_loop_event.wake_up_signal = 1; + wake_up(&sdio_dld->main_loop_event.wait_event); + } + spin_unlock_irqrestore(&lock2, lock_flags2); + + sdio_dld->write_callback_event.wake_up_signal = 1; + wake_up(&sdio_dld->write_callback_event.wait_event); + + start_timer(&sdio_dld->timer, sdio_dld->poll_ms); +} + +/** + * sdio_dld_push_timer_handler + * this is a timer handler of the push_timer. + * + * @data: a pointer to the tty device driver structure. + * @return None. + */ +static void sdio_dld_push_timer_handler(unsigned long data) +{ + pr_err(MODULE_NAME " %s - Push Timer Expired... Trying to " + "push data to TTY Core for over then %d ms.\n", + __func__, sdio_dld->push_timer_ms); +} + +/** + * sdio_dld_open + * this is the open callback of the tty driver. + * it initializes the sdio func, allocates the buffers, and + * creates the main thread. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_open(struct tty_struct *tty, struct file *file) +{ + int status = 0; + int func_in_array = + REAL_FUNC_TO_FUNC_IN_ARRAY(sdio_dld->sdioc_boot_func); + struct sdio_func *str_func = sdio_dld->card->sdio_func[func_in_array]; + + sdio_dld->tty_str = tty; + sdio_dld->tty_str->low_latency = 1; + sdio_dld->tty_str->icanon = 0; + set_bit(TTY_NO_WRITE_SPLIT, &sdio_dld->tty_str->flags); + + pr_info(MODULE_NAME ": %s, TTY DEVICE FOR FLASHLESS BOOT OPENED\n", + __func__); + sdio_dld_info.start_time = get_jiffies_64(); /* read the current time */ + + if (!tty) { + pr_err(MODULE_NAME ": %s - param ""tty"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + atomic_set(&sdio_dld->dld_main_thread.please_close, 0); + sdio_dld->dld_main_thread.exit_wait.wake_up_signal = 0; + + status = sdio_dld_allocate_buffers(str_func); + if (status) { + pr_err(MODULE_NAME ": %s, failed in " + "sdio_dld_allocate_buffers(). status=%d\n", + __func__, status); + return status; + } + + /* init waiting event of the write callback */ + init_waitqueue_head(&sdio_dld->write_callback_event.wait_event); + + /* init waiting event of the main loop */ + init_waitqueue_head(&sdio_dld->main_loop_event.wait_event); + + /* configure and init the timer */ + sdio_dld->poll_ms = TIMER_DURATION; + init_timer(&sdio_dld->timer); + sdio_dld->timer.data = (unsigned long) sdio_dld; + sdio_dld->timer.function = sdio_dld_timer_handler; + sdio_dld->timer.expires = jiffies + + msecs_to_jiffies(sdio_dld->poll_ms); + add_timer(&sdio_dld->timer); + + sdio_dld->push_timer_ms = PUSH_TIMER_DURATION; + init_timer(&sdio_dld->push_timer); + sdio_dld->push_timer.data = (unsigned long) sdio_dld; + sdio_dld->push_timer.function = sdio_dld_push_timer_handler; + + status = sdio_dld_create_thread(); + if (status) { + del_timer_sync(&sdio_dld->timer); + del_timer_sync(&sdio_dld->push_timer); + sdio_dld_dealloc_local_buffers(); + pr_err(MODULE_NAME ": %s, failed in sdio_dld_create_thread()." + "status=%d\n", __func__, status); + return status; + } + return 0; +} + +/** + * sdio_dld_close + * this is the close callback of the tty driver. it requests + * the main thread to exit, and waits for notification of it. + * it also de-allocates the buffers, and unregisters the tty + * driver and device. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return None. + */ +static void sdio_dld_close(struct tty_struct *tty, struct file *file) +{ + int status = 0; + struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg; + + /* informing the SDIOC that it can exit boot phase */ + sdio_dld->sdio_dloader_data.sdioc_reg.good_to_exit_ptr.reg_val = + SDIOC_EXIT_CODE; + + atomic_set(&sdio_dld->dld_main_thread.please_close, 1); + + pr_debug(MODULE_NAME ": %s - CLOSING - WAITING...", __func__); + + wait_event(sdio_dld->dld_main_thread.exit_wait.wait_event, + sdio_dld->dld_main_thread.exit_wait.wake_up_signal); + pr_debug(MODULE_NAME ": %s - CLOSING - WOKE UP...", __func__); + + del_timer_sync(&sdio_dld->timer); + del_timer_sync(&sdio_dld->push_timer); + + sdio_dld_dealloc_local_buffers(); + + tty_unregister_device(sdio_dld->tty_drv, 0); + + status = tty_unregister_driver(sdio_dld->tty_drv); + + if (status) { + pr_err(MODULE_NAME ": %s - tty_unregister_driver() failed\n", + __func__); + } + +#ifdef CONFIG_DEBUG_FS + gd.curr_i = curr_index; + gd.duration_ms = sdio_dld_info.time_msec; + gd.global_bytes_sent = sdio_dld_info.global_bytes_write_toio; + gd.global_bytes_received = 0; + gd.throughput_Mbs = sdio_dld_info.throughput; + gd.host_outgoing_buffer_size_KB = sdio_dld->sdio_dloader_data. + outgoing_data.buffer_size/BYTES_IN_KB; + gd.client_up_buffer_size_KB = reg->ul_buff_size.reg_val/BYTES_IN_KB; + gd.client_dl_buffer_size_KB = reg->dl_buff_size.reg_val/BYTES_IN_KB; + gd.client_dl_buffer_address = reg->dl_buff_address.reg_val; + gd.client_up_buffer_address = reg->up_buff_address.reg_val; + gd.global_bytes_received = sdio_dld_info.global_bytes_read_fromio; + gd.global_bytes_pushed = sdio_dld_info.global_bytes_push_tty; +#endif + + /* saving register values before deallocating sdio_dld + in order to use it in sdio_dld_print_info() through shell command */ + sdio_dld_info.cl_dl_rd_ptr = reg->dl_rd_ptr.reg_val; + sdio_dld_info.cl_dl_wr_ptr = reg->dl_wr_ptr.reg_val; + sdio_dld_info.cl_up_rd_ptr = reg->up_rd_ptr.reg_val; + sdio_dld_info.cl_up_wr_ptr = reg->up_wr_ptr.reg_val; + + sdio_dld_info.host_read_ptr = + sdio_dld->sdio_dloader_data.outgoing_data.offset_read_p; + + sdio_dld_info.host_write_ptr = + sdio_dld->sdio_dloader_data.outgoing_data.offset_write_p; + + sdio_dld_info.cl_dl_buffer_size = + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_size.reg_val; + + sdio_dld_info.cl_up_buffer_size = + sdio_dld->sdio_dloader_data.sdioc_reg.ul_buff_size.reg_val; + + sdio_dld_info.host_outgoing_buffer_size = + sdio_dld->sdio_dloader_data.outgoing_data.buffer_size; + + sdio_dld_info.cl_dl_buffer_address = + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_address.reg_val; + + sdio_dld_info.cl_up_buffer_address = + sdio_dld->sdio_dloader_data.sdioc_reg.up_buff_address.reg_val; + + sdio_dld_print_info(); + + if (sdio_dld->done_callback) + sdio_dld->done_callback(); + + pr_info(MODULE_NAME ": %s - Freeing sdio_dld data structure, and " + " returning...", __func__); + kfree(sdio_dld); +} + +/** + * writing_size_to_buf + * writes from src buffer into dest buffer. if dest buffer + * reaches its end, rollover happens. + * + * @dest: destination buffer. + * @src: source buffer. + * @dest_wr_ptr: writing pointer in destination buffer. + * @dest_size: destination buffer size. + * @dest_rd_ptr: reading pointer in destination buffer. + * @size_to_write: size of bytes to write. + * @return -how many bytes actually written to destination + * buffer. + * + * ONLY destination buffer is treated as cyclic buffer. + */ +static int writing_size_to_buf(char *dest, + const unsigned char *src, + int *dest_wr_ptr, + int dest_size, + int dest_rd_ptr, + int size_to_write) +{ + int actually_written = 0; + int size_to_add = *dest_wr_ptr; + + if (!dest) { + pr_err(MODULE_NAME ": %s - param ""dest"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!src) { + pr_err(MODULE_NAME ": %s - param ""src"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!dest_wr_ptr) { + pr_err(MODULE_NAME ": %s - param ""dest_wr_ptr"" is NULL.\n", + __func__); + return -EINVAL; + } + + for (actually_written = 0 ; + actually_written < size_to_write ; ++actually_written) { + /* checking if buffer is full */ + if (((size_to_add + 1) % dest_size) == dest_rd_ptr) { + *dest_wr_ptr = size_to_add; + return actually_written; + } + + dest[size_to_add] = src[actually_written]; + size_to_add = (size_to_add+1)%dest_size; + } + + *dest_wr_ptr = size_to_add; + + return actually_written; +} + +/** + * sdioc_bytes_till_end_of_buffer - this routine calculates how many bytes are + * empty/in use. if calculation requires rap around - it will ignore the rap + * around and will do the calculation untill the end of the buffer + * + * @write_ptr: writing pointer. + * @read_ptr: reading pointer. + * @total_size: buffer size. + * @free_bytes: return value-how many free bytes. + * @bytes_in_use: return value-how many bytes in use. + * @return 0 on success or negative value on error. + * + * buffer is treated as a cyclic buffer. + */ +static int sdioc_bytes_till_end_of_buffer(int write_ptr, + int read_ptr, + int total_size, + int *free_bytes, + int *bytes_in_use) +{ + if (!free_bytes) { + pr_err(MODULE_NAME ": %s - param ""free_bytes"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!bytes_in_use) { + pr_err(MODULE_NAME ": %s - param ""bytes_in_use"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (write_ptr >= read_ptr) { + if (read_ptr == 0) + *free_bytes = total_size - write_ptr - 1; + else + *free_bytes = total_size - write_ptr; + *bytes_in_use = write_ptr - read_ptr; + } else { + *bytes_in_use = total_size - read_ptr; + *free_bytes = read_ptr - write_ptr - 1; + } + + return 0; +} + +/** + * sdioc_bytes_free_in_buffer + * this routine calculates how many bytes are free in a buffer + * and how many are in use, according to its reading and + * writing pointer offsets. + * + * @write_ptr: writing pointer. + * @read_ptr: reading pointer. + * @total_size: buffer size. + * @free_bytes: return value-how many free bytes in buffer. + * @bytes_in_use: return value-how many bytes in use in buffer. + * @return 0 on success or negative value on error. + * + * buffer is treated as a cyclic buffer. + */ +static int sdioc_bytes_free_in_buffer(int write_ptr, + int read_ptr, + int total_size, + int *free_bytes, + int *bytes_in_use) +{ + if (!free_bytes) { + pr_err(MODULE_NAME ": %s - param ""free_bytes"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!bytes_in_use) { + pr_err(MODULE_NAME ": %s - param ""bytes_in_use"" is NULL.\n", + __func__); + return -EINVAL; + } + + /* if pointers equel - buffers are empty. nothing to read/write */ + + if (write_ptr >= read_ptr) + *bytes_in_use = write_ptr - read_ptr; + else + *bytes_in_use = total_size - (read_ptr - write_ptr); + + *free_bytes = total_size - *bytes_in_use - 1; + + return 0; +} + +/* +* sdio_dld_write_room +* +* This is the write_room function of the tty driver. +* +* @tty: pointer to tty struct. +* @return free bytes for write. +* +*/ +static int sdio_dld_write_room(struct tty_struct *tty) +{ + return sdio_dld->sdio_dloader_data.outgoing_data.buffer_size; +} + +/** + * sdio_dld_write_callback + * this is the write callback of the tty driver. + * + * @tty: pointer to tty struct. + * @buf: buffer to write from. + * @count: number of bytes to write. + * @return bytes written or negative value on error. + * + * if destination buffer has not enough room for the incoming + * data, returns an error. + */ +static int sdio_dld_write_callback(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + int dst_free_bytes = 0; + int dummy = 0; + int status = 0; + int bytes_written = 0; + int total_written = 0; + static int write_retry; + int pending_to_write = count; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_count = count; + update_gd(SDIO_DLD_DEBUGFS_CASE_5_CODE); +#endif + + pr_debug(MODULE_NAME ": %s - WRITING CALLBACK CALLED WITH %d bytes\n", + __func__, count); + + if (!outgoing->data) { + pr_err(MODULE_NAME ": %s - param ""outgoing->data"" is NULL.\n", + __func__); + return -EINVAL; + } + + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK size to write to outgoing" + " buffer %d\n", __func__, count); + + /* as long as there is something to write to outgoing buffer */ + do { + int bytes_to_write = 0; + status = sdioc_bytes_free_in_buffer( + outgoing->offset_write_p, + outgoing->offset_read_p, + outgoing->buffer_size, + &dst_free_bytes, + &dummy); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdioc_bytes_free_in_buffer(). status=%d\n", + __func__, status); + return status; + } + + /* + * if there is free room in outgoing buffer + * lock mutex and request trigger notification from the main + * task. unlock mutex, and wait for sinal + */ + if (dst_free_bytes > 0) { + write_retry = 0; + /* + * if there is more data to write to outgoing buffer + * than it can receive, wait for signal from main task + */ + if (pending_to_write > dst_free_bytes) { + + /* sampling updated dst_free_bytes */ + status = sdioc_bytes_free_in_buffer( + outgoing->offset_write_p, + outgoing->offset_read_p, + outgoing->buffer_size, + &dst_free_bytes, + &dummy); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in " + "Function " + "sdioc_bytes_free_in_buffer(). " + "status=%d\n", __func__, status); + return status; + } + } + + bytes_to_write = min(pending_to_write, dst_free_bytes); + bytes_written = + writing_size_to_buf(outgoing->data, + buf+total_written, + &outgoing->offset_write_p, + outgoing->buffer_size, + outgoing->offset_read_p, + bytes_to_write); + + /* keeping sdio_dld_info up to date */ + sdio_dld_info.host_write_ptr = + sdio_dld->sdio_dloader_data. + outgoing_data.offset_write_p; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_write_tty = bytes_written; + update_gd(SDIO_DLD_DEBUGFS_CASE_3_CODE); +#endif + sdio_dld_info.global_bytes_write_tty += bytes_written; + + spin_lock_irqsave(&lock2, lock_flags2); + if (sdio_dld->main_loop_event.wake_up_signal == 0) { + sdio_dld->main_loop_event.wake_up_signal = 1; + wake_up(&sdio_dld->main_loop_event.wait_event); + } + spin_unlock_irqrestore(&lock2, lock_flags2); + + /* + * although outgoing buffer has enough room, writing + * failed + */ + if (bytes_written != bytes_to_write) { + pr_err(MODULE_NAME ": %s - couldn't write " + "%d bytes to " "outgoing buffer." + "bytes_written=%d\n", + __func__, bytes_to_write, + bytes_written); + return -EIO; + } + + total_written += bytes_written; + pending_to_write -= bytes_written; + outgoing->num_of_bytes_in_use += bytes_written; + + pr_debug(MODULE_NAME ": %s - WRITE CHUNK to outgoing " + "buffer. pending_to_write=%d, " + "outgoing_free_bytes=%d, " + "bytes_written=%d\n", + __func__, + pending_to_write, + dst_free_bytes, + bytes_written); + + } else { + write_retry++; + + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK - NO ROOM." + " pending_to_write=%d, write_retry=%d\n", + __func__, + pending_to_write, + write_retry); + + spin_lock_irqsave(&lock1, lock_flags1); + sdio_dld->write_callback_event.wake_up_signal = 0; + spin_unlock_irqrestore(&lock1, lock_flags1); + + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK - " + "WAITING...", __func__); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_8_CODE); +#endif + wait_event(sdio_dld->write_callback_event.wait_event, + sdio_dld->write_callback_event. + wake_up_signal); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_9_CODE); +#endif + pr_debug(MODULE_NAME ": %s - WRITE CALLBACK - " + "WOKE UP...", __func__); + } + } while (pending_to_write > 0 && write_retry < WRITE_RETRIES); + + if (pending_to_write > 0) { + + pr_err(MODULE_NAME ": %s - WRITE CALLBACK - pending data is " + "%d out of %d > 0. total written in this " + "callback = %d\n", + __func__, pending_to_write, count, total_written); + } + + if (write_retry == WRITE_RETRIES) { + pr_err(MODULE_NAME ": %s, write_retry=%d= max\n", + __func__, write_retry); + } + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_bytes_cb_tty = total_written; + update_gd(SDIO_DLD_DEBUGFS_CASE_10_CODE); +#endif + + return total_written; +} + +/** + * sdio_memcpy_fromio_wrapper - + * reads from sdioc, and updats the sdioc registers according + * to how many bytes were actually read. + * + * @str_func: a pointer to func struct. + * @client_rd_ptr: sdioc value of downlink read ptr. + * @client_wr_ptr: sdioc value of downlink write ptr. + * @buffer_to_store: buffer to store incoming data. + * @address_to_read: address to start reading from in sdioc. + * @size_to_read: size of bytes to read. + * @client_buffer_size: sdioc downlink buffer size. + * @return 0 on success or negative value on error. + */ +static int sdio_memcpy_fromio_wrapper(struct sdio_func *str_func, + unsigned int client_rd_ptr, + unsigned int client_wr_ptr, + void *buffer_to_store, + unsigned int address_to_read_from, + int size_to_read, + int client_buffer_size) +{ + int status = 0; + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!buffer_to_store) { + pr_err(MODULE_NAME ": %s - param ""buffer_to_store"" is " + "NULL.\n", + __func__); + return -EINVAL; + } + + if (size_to_read < 0) { + pr_err(MODULE_NAME ": %s - invalid size to read=%d\n", + __func__, size_to_read); + return -EINVAL; + } + + sdio_claim_host(str_func); + + pr_debug(MODULE_NAME ": %s, READING DATA - from add %d, " + "size_to_read=%d\n", + __func__, address_to_read_from, size_to_read); + + status = sdio_memcpy_fromio(str_func, + (void *)buffer_to_store, + address_to_read_from, + size_to_read); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_fromio()" + " DATA failed. status=%d.\n", + __func__, status); + sdio_release_host(str_func); + return status; + } + + /* updating an offset according to cyclic buffer size */ + reg_str->dl_rd_ptr.reg_val = + (reg_str->dl_rd_ptr.reg_val + size_to_read) % + client_buffer_size; + /* keeping sdio_dld_info up to date */ + sdio_dld_info.cl_dl_rd_ptr = reg_str->dl_rd_ptr.reg_val; + + status = sdio_memcpy_toio(str_func, + reg_str->dl_rd_ptr.reg_offset, + (void *)®_str->dl_rd_ptr.reg_val, + sizeof(reg_str->dl_rd_ptr.reg_val)); + + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "UPDATE PTR failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + return status; +} + +/** + * sdio_memcpy_toio_wrapper + * writes to sdioc, and updats the sdioc registers according + * to how many bytes were actually read. + * + * @str_func: a pointer to func struct. + * @client_wr_ptr: sdioc downlink write ptr. + * @h_read_ptr: host incoming read ptrs + * @buf_write_from: buffer to write from. + * @bytes_to_write: number of bytes to write. + * @return 0 on success or negative value on error. + */ +static int sdio_memcpy_toio_wrapper(struct sdio_func *str_func, + unsigned int client_wr_ptr, + unsigned int h_read_ptr, + void *buf_write_from, + int bytes_to_write) +{ + int status = 0; + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!buf_write_from) { + pr_err(MODULE_NAME ": %s - param ""buf_write_from"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_claim_host(str_func); + + pr_debug(MODULE_NAME ": %s, WRITING DATA TOIO to address 0x%x, " + "bytes_to_write=%d\n", + __func__, + reg_str->up_buff_address.reg_val + reg_str->up_wr_ptr.reg_val, + bytes_to_write); + + status = sdio_memcpy_toio(str_func, + reg_str->up_buff_address.reg_val + + reg_str->up_wr_ptr.reg_val, + (void *) (outgoing->data + h_read_ptr), + bytes_to_write); + + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "DATA failed. status=%d.\n", __func__, status); + sdio_release_host(str_func); + return status; + } + + sdio_dld_info.global_bytes_write_toio += bytes_to_write; + outgoing->num_of_bytes_in_use -= bytes_to_write; + + /* + * if writing to client succeeded, then + * 1. update the client up_wr_ptr + * 2. update the host outgoing rd ptr + **/ + reg_str->up_wr_ptr.reg_val = + ((reg_str->up_wr_ptr.reg_val + bytes_to_write) % + reg_str->ul_buff_size.reg_val); + + /* keeping sdio_dld_info up to date */ + sdio_dld_info.cl_up_wr_ptr = reg_str->up_wr_ptr.reg_val; + + outgoing->offset_read_p = + ((outgoing->offset_read_p + bytes_to_write) % + outgoing->buffer_size); + + /* keeping sdio_dld_info up to date*/ + sdio_dld_info.host_read_ptr = outgoing->offset_read_p; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_write_toio = bytes_to_write; + update_gd(SDIO_DLD_DEBUGFS_CASE_4_CODE); +#endif + + /* updating uplink write pointer according to size that was written */ + status = sdio_memcpy_toio(str_func, + reg_str->up_wr_ptr.reg_offset, + (void *)(®_str->up_wr_ptr.reg_val), + sizeof(reg_str->up_wr_ptr.reg_val)); + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "UPDATE PTR failed. status=%d.\n", + __func__, status); + } + + sdio_release_host(str_func); + return status; +} + +/** + * sdio_dld_read + * reads from sdioc + * + * @client_rd_ptr: sdioc downlink read ptr. + * @client_wr_ptr: sdioc downlink write ptr. + * @reg_str: sdioc register shadowing struct. + * @str_func: a pointer to func struct. + * @bytes_read:how many bytes read. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_read(unsigned int client_rd_ptr, + unsigned int client_wr_ptr, + struct sdioc_reg_chunk *reg_str, + struct sdio_func *str_func, + int *bytes_read) +{ + int status = 0; + struct sdio_data *incoming = &sdio_dld->sdio_dloader_data.incoming_data; + + if (!reg_str) { + pr_err(MODULE_NAME ": %s - param ""reg_str"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!bytes_read) { + pr_err(MODULE_NAME ": %s - param ""bytes_read"" is NULL.\n", + __func__); + return -EINVAL; + } + + /* there is data to read in ONE chunk */ + if (client_wr_ptr > client_rd_ptr) { + status = sdio_memcpy_fromio_wrapper( + str_func, + client_rd_ptr, + client_wr_ptr, + (void *)incoming->data, + reg_str->dl_buff_address.reg_val + client_rd_ptr, + client_wr_ptr - client_rd_ptr, + reg_str->dl_buff_size.reg_val); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_memcpy_fromio_wrapper(). " + "SINGLE CHUNK READ. status=%d\n", + __func__, status); + return status; + } + + incoming->num_of_bytes_in_use += client_wr_ptr - client_rd_ptr; + *bytes_read = client_wr_ptr - client_rd_ptr; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_to_read = + client_wr_ptr - client_rd_ptr; + update_gd(SDIO_DLD_DEBUGFS_CASE_11_CODE); +#endif + } + + /* there is data to read in TWO chunks */ + else { + int dl_buf_size = reg_str->dl_buff_size.reg_val; + int tail_size = dl_buf_size - client_rd_ptr; + + /* reading chunk#1: from rd_ptr to the end of the buffer */ + status = sdio_memcpy_fromio_wrapper( + str_func, + client_rd_ptr, + dl_buf_size, + (void *)incoming->data, + reg_str->dl_buff_address.reg_val + client_rd_ptr, + tail_size, + dl_buf_size); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_memcpy_fromio_wrapper(). " + "1 of 2 CHUNKS READ. status=%d\n", + __func__, status); + return status; + } + + incoming->num_of_bytes_in_use += tail_size; + *bytes_read = tail_size; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_to_read = tail_size; + update_gd(SDIO_DLD_DEBUGFS_CASE_11_CODE); +#endif + + /* reading chunk#2: reading from beginning buffer */ + status = sdio_memcpy_fromio_wrapper( + str_func, + client_rd_ptr, + client_wr_ptr, + (void *)(incoming->data + tail_size), + reg_str->dl_buff_address.reg_val, + client_wr_ptr, + reg_str->dl_buff_size.reg_val); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_memcpy_fromio_wrapper(). " + "2 of 2 CHUNKS READ. status=%d\n", + __func__, status); + return status; + } + + incoming->num_of_bytes_in_use += client_wr_ptr; + *bytes_read += client_wr_ptr; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_to_read = client_wr_ptr; + update_gd(SDIO_DLD_DEBUGFS_CASE_11_CODE); +#endif + } + return 0; +} + +/** + * sdio_dld_main_task + * sdio downloader main task. reads mailboxf checks if there is + * anything to read, checks if host has anything to + * write. + * + * @card: a pointer to mmc_card. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_main_task(void *card) +{ + int status = 0; + struct tty_struct *tty = sdio_dld->tty_str; + struct sdioc_reg_chunk *reg_str = + &sdio_dld->sdio_dloader_data.sdioc_reg; + int func = sdio_dld->sdioc_boot_func; + struct sdio_func *str_func = NULL; + struct sdio_data *outgoing = &sdio_dld->sdio_dloader_data.outgoing_data; + struct sdio_data *incoming = &sdio_dld->sdio_dloader_data.incoming_data; + struct sdio_dld_task *task = &sdio_dld->dld_main_thread; + int retries = 0; +#ifdef PUSH_STRING + int bytes_pushed = 0; +#endif + + msleep(SLEEP_MS); + + if (!card) { + pr_err(MODULE_NAME ": %s - param ""card"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!tty) { + pr_err(MODULE_NAME ": %s - param ""tty"" is NULL.\n", + __func__); + return -EINVAL; + } + + str_func = ((struct mmc_card *)card)-> + sdio_func[REAL_FUNC_TO_FUNC_IN_ARRAY(func)]; + + if (!str_func) { + pr_err(MODULE_NAME ": %s - param ""str_func"" is NULL.\n", + __func__); + return -EINVAL; + } + + while (true) { + /* client pointers for both buffers */ + int client_ul_wr_ptr = 0; + int client_ul_rd_ptr = 0; + int client_dl_wr_ptr = 0; + int client_dl_rd_ptr = 0; + + /* host pointer for outgoing buffer */ + int h_out_wr_ptr = 0; + int h_out_rd_ptr = 0; + + int h_bytes_rdy_wr = 0; + int c_bytes_rdy_rcve = 0; + + int need_to_write = 0; + int need_to_read = 0; + + /* + * forever, checking for signal to die, then read MailBox. + * if nothing to read or nothing to write to client, sleep, + * and again read MailBox + */ + do { + int dummy = 0; + + /* checking if a signal to die was sent */ + if (atomic_read(&task->please_close) == 1) { + + pr_debug(MODULE_NAME ": %s - 0x%x was written " + "to 9K\n", __func__, SDIOC_EXIT_CODE); + + sdio_claim_host(str_func); + + /* returned value is not checked on purpose */ + sdio_memcpy_toio( + str_func, + reg_str->good_to_exit_ptr.reg_offset, + (void *)®_str->good_to_exit_ptr. + reg_val, + sizeof(reg_str->good_to_exit_ptr. + reg_val)); + + sdio_release_host(str_func); + + task->exit_wait.wake_up_signal = 1; + wake_up(&task->exit_wait.wait_event); + return 0; + } + + status = mailbox_to_seq_chunk_read_ptrs(str_func); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "mailbox_to_seq_chunk_read_ptrs(). " + "status=%d\n", __func__, status); + return status; + } + + /* calculate how many bytes the host has send */ + h_out_wr_ptr = outgoing->offset_write_p; + h_out_rd_ptr = outgoing->offset_read_p; + + status = sdioc_bytes_till_end_of_buffer( + h_out_wr_ptr, + h_out_rd_ptr, + outgoing->buffer_size, + &dummy, + &h_bytes_rdy_wr); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdioc_bytes_till_end_of_buffer(). " + "status=%d\n", __func__, status); + return status; + } + + /* is there something to read from client */ + client_dl_wr_ptr = reg_str->dl_wr_ptr.reg_val; + client_dl_rd_ptr = reg_str->dl_rd_ptr.reg_val; + + if (client_dl_rd_ptr != client_dl_wr_ptr) + need_to_read = 1; + + /* + * calculate how many bytes the client can receive + * from host + */ + client_ul_wr_ptr = reg_str->up_wr_ptr.reg_val; + client_ul_rd_ptr = reg_str->up_rd_ptr.reg_val; + + status = sdioc_bytes_till_end_of_buffer( + client_ul_wr_ptr, + client_ul_rd_ptr, + reg_str->ul_buff_size.reg_val, + &c_bytes_rdy_rcve, + &dummy); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdioc_bytes_till_end_of_buffer(). " + "status=%d\n", __func__, status); + return status; + } + + /* if host has anything to write */ + if (h_bytes_rdy_wr > 0) + need_to_write = 1; + + if (need_to_write || need_to_read) + break; + + spin_lock_irqsave(&lock2, lock_flags2); + sdio_dld->main_loop_event.wake_up_signal = 0; + spin_unlock_irqrestore(&lock2, lock_flags2); + + pr_debug(MODULE_NAME ": %s - MAIN LOOP - WAITING...\n", + __func__); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_6_CODE); +#endif + wait_event(sdio_dld->main_loop_event.wait_event, + sdio_dld->main_loop_event.wake_up_signal); +#ifdef CONFIG_DEBUG_FS + update_gd(SDIO_DLD_DEBUGFS_CASE_7_CODE); +#endif + + pr_debug(MODULE_NAME ": %s - MAIN LOOP - WOKE UP...\n", + __func__); + + } while (1); + + /* CHECK IF THERE IS ANYTHING TO READ IN CLIENT */ + if (need_to_read) { +#ifdef PUSH_STRING + int num_push = 0; + int left = 0; + int bytes_read; +#else + int i; +#endif + need_to_read = 0; + + status = sdio_dld_read(client_dl_rd_ptr, + client_dl_wr_ptr, + reg_str, + str_func, + &bytes_read); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_read(). status=%d\n", + __func__, status); + return status; + } + + sdio_dld_info.global_bytes_read_fromio += + bytes_read; + + bytes_pushed = 0; +#ifdef PUSH_STRING + left = incoming->num_of_bytes_in_use; + start_timer(&sdio_dld->push_timer, + sdio_dld->push_timer_ms); + do { + num_push = tty_insert_flip_string( + tty, + incoming->data+bytes_pushed, + left); + + bytes_pushed += num_push; + left -= num_push; + tty_flip_buffer_push(tty); + } while (left != 0); + + del_timer(&sdio_dld->push_timer); + + if (bytes_pushed != incoming->num_of_bytes_in_use) { + pr_err(MODULE_NAME ": %s - failed\n", + __func__); + } +#else + pr_debug(MODULE_NAME ": %s - NEED TO READ %d\n", + __func__, incoming->num_of_bytes_in_use); + + for (i = 0 ; i < incoming->num_of_bytes_in_use ; ++i) { + int err = 0; + err = tty_insert_flip_char(tty, + incoming->data[i], + TTY_NORMAL); + tty_flip_buffer_push(tty); + } + + pr_debug(MODULE_NAME ": %s - JUST READ\n", __func__); +#endif /*PUSH_STRING*/ + sdio_dld_info.global_bytes_push_tty += + incoming->num_of_bytes_in_use; +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_push_to_tty = bytes_read; + update_gd(SDIO_DLD_DEBUGFS_CASE_12_CODE); +#endif + incoming->num_of_bytes_in_use = 0; + tty_flip_buffer_push(tty); + } + + /* CHECK IF THERE IS ANYTHING TO WRITE IN HOST AND HOW MUCH */ + if (need_to_write) { + int dummy = 0; + + do { + int bytes_to_write = min(c_bytes_rdy_rcve, + h_bytes_rdy_wr); + + /* + * in case nothing to send or no room to + * receive + */ + if (bytes_to_write == 0) + break; + + if (client_ul_rd_ptr == 0 && + (client_ul_rd_ptr != client_ul_wr_ptr)) + break; + + /* + * if client_rd_ptr points to start, but there + * is data to read wait until WRITE_TILL_END + * before writing a chunk of data, to avoid + * writing until (BUF_SIZE - 1), because it will + * yield an extra write of "1" bytes + */ + if (client_ul_rd_ptr == 0 && + (client_ul_rd_ptr != client_ul_wr_ptr) && + retries < WRITE_TILL_END_RETRIES) { + retries++; + break; + } + retries = 0; + +#ifdef CONFIG_DEBUG_FS + debugfs_glob.global_8k_has = h_bytes_rdy_wr; + debugfs_glob.global_9k_has = c_bytes_rdy_rcve; + debugfs_glob.global_min = bytes_to_write; + update_gd(SDIO_DLD_DEBUGFS_CASE_2_CODE); +#endif + need_to_write = 0; + + pr_debug(MODULE_NAME ": %s - NEED TO WRITE " + "TOIO %d\n", + __func__, bytes_to_write); + + status = sdio_memcpy_toio_wrapper( + str_func, + reg_str->up_wr_ptr.reg_val, + outgoing->offset_read_p, + (void *)((char *)outgoing->data + + outgoing->offset_read_p), + bytes_to_write); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in " + "Function " + "sdio_memcpy_toio_wrapper(). " + "SINGLE CHUNK WRITE. " + "status=%d\n", + __func__, status); + return status; + } + + sdio_claim_host(str_func); + + status = sdio_memcpy_fromio( + str_func, + (void *)®_str->up_rd_ptr.reg_val, + SDIOC_UL_RD_PTR, + sizeof(reg_str->up_rd_ptr.reg_val)); + + if (status) { + pr_err(MODULE_NAME ": %s - " + "sdio_memcpy_fromio() " + "failed. status=%d\n", + __func__, status); + sdio_release_host(str_func); + + return status; + } + + sdio_release_host(str_func); + + spin_lock_irqsave(&lock1, lock_flags1); + if (sdio_dld->write_callback_event. + wake_up_signal == 0) { + sdio_dld->write_callback_event. + wake_up_signal = 1; + wake_up(&sdio_dld-> + write_callback_event. + wait_event); + } + + spin_unlock_irqrestore(&lock1, lock_flags1); + client_ul_wr_ptr = reg_str->up_wr_ptr.reg_val; + client_ul_rd_ptr = reg_str->up_rd_ptr.reg_val; + + status = sdioc_bytes_till_end_of_buffer( + client_ul_wr_ptr, + client_ul_rd_ptr, + reg_str->ul_buff_size.reg_val, + &c_bytes_rdy_rcve, + &dummy); + + /* calculate how many bytes host has to send */ + h_out_wr_ptr = outgoing->offset_write_p; + h_out_rd_ptr = outgoing->offset_read_p; + + status = sdioc_bytes_till_end_of_buffer( + h_out_wr_ptr, + h_out_rd_ptr, + outgoing->buffer_size, + &dummy, + &h_bytes_rdy_wr); + + } while (h_out_wr_ptr != h_out_rd_ptr); + } + } + return 0; +} + +/** + * sdio_dld_init_global + * initialization of sdio_dld global struct + * + * @card: a pointer to mmc_card. + * @return 0 on success or negative value on error. + */ +static int sdio_dld_init_global(struct mmc_card *card, + int(*done)(void)) +{ + if (!card) { + pr_err(MODULE_NAME ": %s - param ""card"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!done) { + pr_err(MODULE_NAME ": %s - param ""done"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_dld->done_callback = done; + sdio_dld->card = card; + init_waitqueue_head(&sdio_dld->dld_main_thread.exit_wait.wait_event); + sdio_dld->write_callback_event.wake_up_signal = 1; + sdio_dld->main_loop_event.wake_up_signal = 1; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_size.reg_offset = + SDIOC_DL_BUFF_SIZE_OFFSET; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_rd_ptr.reg_offset = + SDIOC_DL_RD_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_wr_ptr.reg_offset = + SDIOC_DL_WR_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.ul_buff_size.reg_offset = + SDIOC_UP_BUFF_SIZE_OFFSET; + + sdio_dld->sdio_dloader_data.sdioc_reg.up_rd_ptr.reg_offset = + SDIOC_UL_RD_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.up_wr_ptr.reg_offset = + SDIOC_UL_WR_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.good_to_exit_ptr.reg_offset = + SDIOC_EXIT_PTR; + + sdio_dld->sdio_dloader_data.sdioc_reg.dl_buff_address.reg_offset = + SDIOC_DL_BUFF_ADDRESS; + + sdio_dld->sdio_dloader_data.sdioc_reg.up_buff_address.reg_offset = + SDIOC_UP_BUFF_ADDRESS; + + sdio_dld_set_op_mode(SDIO_DLD_NORMAL_MODE); + + return 0; +} + +/** + * sdio_downloader_setup + * initializes the TTY driver + * + * @card: a pointer to mmc_card. + * @num_of_devices: number of devices. + * @channel_number: channel number. + * @return 0 on success or negative value on error. + * + * The TTY stack needs to know in advance how many devices it should + * plan to manage. Use this call to set up the ports that will + * be exported through SDIO. + */ +int sdio_downloader_setup(struct mmc_card *card, + unsigned int num_of_devices, + int channel_number, + int(*done)(void)) +{ + int status = 0; + int result = 0; + int func_in_array = 0; + struct sdio_func *str_func = NULL; + struct device *tty_dev; + + if (num_of_devices == 0 || num_of_devices > MAX_NUM_DEVICES) { + pr_err(MODULE_NAME ": %s - invalid number of devices\n", + __func__); + return -EINVAL; + } + + if (!card) { + pr_err(MODULE_NAME ": %s - param ""card"" is NULL.\n", + __func__); + return -EINVAL; + } + + if (!done) { + pr_err(MODULE_NAME ": %s - param ""done"" is NULL.\n", + __func__); + return -EINVAL; + } + + sdio_dld = kzalloc(sizeof(struct sdio_downloader), GFP_KERNEL); + if (!sdio_dld) { + pr_err(MODULE_NAME ": %s - couldn't allocate sdio_dld data " + "structure.", __func__); + return -ENOMEM; + } + +#ifdef CONFIG_DEBUG_FS + bootloader_debugfs_init(); +#endif /* CONFIG_DEBUG_FS */ + + status = sdio_dld_init_global(card, done); + + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_init_global(). status=%d\n", + __func__, status); + kfree(sdio_dld); + return status; + } + + sdio_dld->tty_drv = alloc_tty_driver(num_of_devices); + + if (!sdio_dld->tty_drv) { + pr_err(MODULE_NAME ": %s - param ""sdio_dld->tty_drv"" is " + "NULL.\n", __func__); + kfree(sdio_dld); + return -EINVAL; + } + + sdio_dld_set_op_mode((enum sdio_dld_op_mode)sdio_op_mode); + + /* according to op_mode, a different tty device is created */ + if (sdio_dld->op_mode == SDIO_DLD_BOOT_TEST_MODE) + sdio_dld->tty_drv->name = TTY_SDIO_DEV_TEST; + else + sdio_dld->tty_drv->name = TTY_SDIO_DEV; + + sdio_dld->tty_drv->owner = THIS_MODULE; + sdio_dld->tty_drv->driver_name = "SDIO_Dloader"; + + /* uses dynamically assigned dev_t values */ + sdio_dld->tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + sdio_dld->tty_drv->subtype = SERIAL_TYPE_NORMAL; + sdio_dld->tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_RESET_TERMIOS; + + /* initializing the tty driver */ + sdio_dld->tty_drv->init_termios = tty_std_termios; + sdio_dld->tty_drv->init_termios.c_cflag = + B4800 | CS8 | CREAD | HUPCL | CLOCAL; + sdio_dld->tty_drv->init_termios.c_ispeed = INPUT_SPEED; + sdio_dld->tty_drv->init_termios.c_ospeed = OUTPUT_SPEED; + + tty_set_operations(sdio_dld->tty_drv, &sdio_dloader_tty_ops); + + status = tty_register_driver(sdio_dld->tty_drv); + if (status) { + put_tty_driver(sdio_dld->tty_drv); + pr_err(MODULE_NAME ": %s - tty_register_driver() failed\n", + __func__); + + sdio_dld->tty_drv = NULL; + kfree(sdio_dld); + return status; + } + + tty_dev = tty_register_device(sdio_dld->tty_drv, 0, NULL); + if (IS_ERR(tty_dev)) { + pr_err(MODULE_NAME ": %s - tty_register_device() " + "failed\n", __func__); + tty_unregister_driver(sdio_dld->tty_drv); + kfree(sdio_dld); + return PTR_ERR(tty_dev); + } + + sdio_dld->sdioc_boot_func = SDIOC_CHAN_TO_FUNC_NUM(channel_number); + func_in_array = REAL_FUNC_TO_FUNC_IN_ARRAY(sdio_dld->sdioc_boot_func); + str_func = sdio_dld->card->sdio_func[func_in_array]; + status = sdio_dld_init_func(str_func); + if (status) { + pr_err(MODULE_NAME ": %s - Failure in Function " + "sdio_dld_init_func(). status=%d\n", + __func__, status); + goto exit_err; + } + +#ifdef CONFIG_DEBUG_FS + sdio_dld_debug_init(); +#endif + + sdio_claim_host(str_func); + + /* + * notifing the client by writing what mode we are by writing + * to a special register + */ + status = sdio_memcpy_toio(str_func, + SDIOC_OP_MODE_PTR, + (void *)&sdio_dld->op_mode, + sizeof(sdio_dld->op_mode)); + + sdio_release_host(str_func); + + if (status) { + pr_err(MODULE_NAME ": %s - sdio_memcpy_toio() " + "writing to OP_MODE_REGISTER failed. " + "status=%d.\n", + __func__, status); + goto exit_err; + } + + return 0; + +exit_err: + tty_unregister_device(sdio_dld->tty_drv, 0); + result = tty_unregister_driver(sdio_dld->tty_drv); + if (result) + pr_err(MODULE_NAME ": %s - tty_unregister_driver() " + "failed. result=%d\n", __func__, -result); + kfree(sdio_dld); + return status; +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SDIO Downloader"); +MODULE_AUTHOR("Yaniv Gardi "); +MODULE_VERSION(DRV_VERSION); + diff --git a/arch/arm/mach-msm/sdio_al_private.h b/arch/arm/mach-msm/sdio_al_private.h new file mode 100644 index 00000000000..36d9ec13cd3 --- /dev/null +++ b/arch/arm/mach-msm/sdio_al_private.h @@ -0,0 +1,267 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Abstraction-Layer internal interface. + */ + +#ifndef __SDIO_AL_PRIVATE__ +#define __SDIO_AL_PRIVATE__ + +#include +#include +#include + +#define DRV_VERSION "1.30" +#define MODULE_NAME "sdio_al" +#define SDIOC_CHAN_TO_FUNC_NUM(x) ((x)+2) +#define REAL_FUNC_TO_FUNC_IN_ARRAY(x) ((x)-1) +#define SDIO_PREFIX "SDIO_" +#define PEER_CHANNEL_NAME_SIZE 4 +#define CHANNEL_NAME_SIZE (sizeof(SDIO_PREFIX) + PEER_CHANNEL_NAME_SIZE) +#define SDIO_TEST_POSTFIX_SIZE 5 +#define MAX_NUM_OF_SDIO_DEVICES 2 +#define TEST_CH_NAME_SIZE (CHANNEL_NAME_SIZE + SDIO_TEST_POSTFIX_SIZE) + +struct sdio_al_device; /* Forward Declaration */ + +enum sdio_channel_state { + SDIO_CHANNEL_STATE_INVALID, /* before reading software header */ + SDIO_CHANNEL_STATE_IDLE, /* channel valid, not opened */ + SDIO_CHANNEL_STATE_CLOSED, /* was closed */ + SDIO_CHANNEL_STATE_OPEN, /* opened */ + SDIO_CHANNEL_STATE_CLOSING, /* during flush, when closing */ +}; +/** + * Peer SDIO-Client channel configuration. + * + * @is_ready - channel is ready and the data is valid. + * + * @max_rx_threshold - maximum rx threshold, according to the + * total buffers size on the peer pipe. + * @max_tx_threshold - maximum tx threshold, according to the + * total buffers size on the peer pipe. + * @tx_buf_size - size of a single buffer on the peer pipe; a + * transfer smaller than the buffer size still + * make the buffer unusable for the next transfer. + * @max_packet_size + * @is_host_ok_to_sleep - Host marks this bit when it's okay to + * sleep (no pending transactions) + */ +struct peer_sdioc_channel_config { + u32 is_ready; + u32 max_rx_threshold; /* Downlink */ + u32 max_tx_threshold; /* Uplink */ + u32 tx_buf_size; + u32 max_packet_size; + u32 is_host_ok_to_sleep; + u32 is_packet_mode; + u32 peer_operation; + u32 is_low_latency_ch; + u32 reserved[23]; +}; + + +/** + * Peer SDIO-Client channel statsitics. + * + * @last_any_read_avail - the last read avail in all the + * channels including this channel. + * @last_read_avail - the last read_avail that was read from HW + * mailbox. + * @last_old_read_avail - the last read_avail channel shadow. + * @total_notifs - the total number of read notifications sent + * to this channel client + * @total_read_times - the total number of successful sdio_read + * calls for this channel + */ +struct sdio_channel_statistics { + int last_any_read_avail; + int last_read_avail; + int last_old_read_avail; + int total_notifs; + int total_read_times; +}; + +/** + * SDIO Channel context. + * + * @name - channel name. Used by the caller to open the + * channel. + * + * @read_threshold - Threshold on SDIO-Client mailbox for Rx + * Data available bytes. When the limit exceed + * the SDIO-Client generates an interrupt to the + * host. + * + * @write_threshold - Threshold on SDIO-Client mailbox for Tx + * Data available bytes. When the limit exceed + * the SDIO-Client generates an interrupt to the + * host. + * + * @def_read_threshold - Default theshold on SDIO-Client for Rx + * + * @min_write_avail - Threshold of minimal available bytes + * to write. Below that threshold the host + * will initiate reading the mailbox. + * + * @poll_delay_msec - Delay between polling the mailbox. When + * the SDIO-Client doesn't generates EOT + * interrupt for Rx Available bytes, the host + * should poll the SDIO-Client mailbox. + * + * @is_packet_mode - The host get interrupt when a packet is + * available at the SDIO-client (pipe EOT + * indication). + * + * @num - channel number. + * + * @notify - Client's callback. Should not call sdio read/write. + * + * @priv - Client's private context, provided to callback. + * + * @is_valid - Channel is used (we have a list of + * SDIO_AL_MAX_CHANNELS and not all of them are in + * use). + * + * @is_open - Channel is open. + * + * @func - SDIO Function handle. + * + * @rx_pipe_index - SDIO-Client Pipe Index for Rx Data. + * + * @tx_pipe_index - SDIO-Client Pipe Index for Tx Data. + * + * @ch_lock - Channel lock to protect channel specific Data + * + * @rx_pending_bytes - Total number of Rx pending bytes, at Rx + * packet list. Maximum of 16KB-1 limited by + * SDIO-Client specification. + * + * @read_avail - Available bytes to read. + * + * @write_avail - Available bytes to write. + * + * @rx_size_list_head - The head of Rx Pending Packets List. + * + * @pdev - platform device - clients to probe for the sdio-al. + * + * @signature - Context Validity check. + * + * @sdio_al_dev - a pointer to the sdio_al_device instance of + * this channel + * + * @statistics - channel statistics + * + */ +struct sdio_channel { + /* Channel Configuration Parameters*/ + char name[CHANNEL_NAME_SIZE]; + char ch_test_name[TEST_CH_NAME_SIZE]; + int read_threshold; + int write_threshold; + int def_read_threshold; + int threshold_change_cnt; + int min_write_avail; + int poll_delay_msec; + int is_packet_mode; + int is_low_latency_ch; + + struct peer_sdioc_channel_config ch_config; + + /* Channel Info */ + int num; + + void (*notify)(void *priv, unsigned channel_event); + void *priv; + + int state; + + struct sdio_func *func; + + int rx_pipe_index; + int tx_pipe_index; + + struct mutex ch_lock; + + u32 read_avail; + u32 write_avail; + + u32 peer_tx_buf_size; + + u16 rx_pending_bytes; + + struct list_head rx_size_list_head; + + struct platform_device *pdev; + + u32 total_rx_bytes; + u32 total_tx_bytes; + + u32 signature; + + struct sdio_al_device *sdio_al_dev; + + struct sdio_channel_statistics statistics; +}; + +/** + * sdio_downloader_setup + * initializes the TTY driver + * + * @card: a pointer to mmc_card. + * @num_of_devices: number of devices. + * @channel_number: channel number. + * @return 0 on success or negative value on error. + * + * The TTY stack needs to know in advance how many devices it should + * plan to manage. Use this call to set up the ports that will + * be exported through SDIO. + */ +int sdio_downloader_setup(struct mmc_card *card, + unsigned int num_of_devices, + int func_number, + int(*func)(void)); + +/** + * test_channel_init + * initializes a test channel + * + * @name: the channel name. + * @return 0 on success or negative value on error. + * + */ +int test_channel_init(char *name); + +/** + * sdio_al_register_lpm_cb + * Allow the sdio_al test to register for lpm voting + * notifications + * + * @device_handle: the device handle. + * @wakeup_callback: callback function to be called when voting. + * + */ +void sdio_al_register_lpm_cb(void *device_handle, + int(*lpm_callback)(void *, int)); + +/** + * sdio_al_unregister_lpm_cb + * Allow the sdio_al test to unregister for lpm voting + * notifications + * + * @device_handle: the device handle. + * + */ +void sdio_al_unregister_lpm_cb(void *device_handle); + +#endif /* __SDIO_AL_PRIVATE__ */ diff --git a/arch/arm/mach-msm/sdio_al_test.c b/arch/arm/mach-msm/sdio_al_test.c new file mode 100644 index 00000000000..c97588fed8f --- /dev/null +++ b/arch/arm/mach-msm/sdio_al_test.c @@ -0,0 +1,6500 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO-Abstraction-Layer Test Module. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdio_al_private.h" +#include + +#include +enum lpm_test_msg_type { + LPM_NO_MSG, /* 0 */ + LPM_MSG_SEND, /* 1 */ + LPM_MSG_REC, /* 2 */ + LPM_SLEEP, /* 3 */ + LPM_WAKEUP, /* 4 */ + LPM_NOTIFY /* 5 */ +}; + +#define LPM_NO_MSG_NAME "LPM No Event" +#define LPM_MSG_SEND_NAME "LPM Send Msg Event" +#define LPM_MSG_REC_NAME "LPM Receive Msg Event" +#define LPM_SLEEP_NAME "LPM Sleep Event" +#define LPM_WAKEUP_NAME "LPM Wakeup Event" + +/** Module name string */ +#define TEST_MODULE_NAME "sdio_al_test" + +#define TEST_SIGNATURE 0x12345678 +#define TEST_CONFIG_SIGNATURE 0xBEEFCAFE + +#define MAX_XFER_SIZE (16*1024) +#define SMEM_MAX_XFER_SIZE 0xBC000 +#define A2_MIN_PACKET_SIZE 5 +#define RMNT_PACKET_SIZE (4*1024) +#define DUN_PACKET_SIZE (2*1024) +#define CSVT_PACKET_SIZE 1700 + +#define TEST_DBG(x...) if (test_ctx->runtime_debug) pr_info(x) + +#define LPM_TEST_NUM_OF_PACKETS 100 +#define LPM_MAX_OPEN_CHAN_PER_DEV 4 +#define LPM_ARRAY_SIZE (7*LPM_TEST_NUM_OF_PACKETS*LPM_MAX_OPEN_CHAN_PER_DEV) +#define SDIO_LPM_TEST "sdio_lpm_test_reading_task" +#define LPM_TEST_CONFIG_SIGNATURE 0xDEADBABE +#define LPM_MSG_NAME_SIZE 20 +#define MAX_STR_SIZE 10 +#define MAX_AVG_RTT_TIME_USEC 2500 +#define SDIO_RMNT_RTT_PACKET_SIZE 32 +#define SDIO_CSVT_RTT_PACKET_SIZE 1900 + +#define A2_HEADER_OVERHEAD 8 + +enum rx_process_state { + RX_PROCESS_PACKET_INIT, + RX_PROCESS_A2_HEADER, + RX_PROCESS_PACKET_DATA, +}; + +enum sdio_test_case_type { + SDIO_TEST_LOOPBACK_HOST, + SDIO_TEST_LOOPBACK_CLIENT, + SDIO_TEST_LPM_HOST_WAKER, + SDIO_TEST_LPM_CLIENT_WAKER, + SDIO_TEST_LPM_RANDOM, + SDIO_TEST_HOST_SENDER_NO_LP, + SDIO_TEST_CLOSE_CHANNEL, + SDIO_TEST_A2_VALIDATION, + /* The following tests are not part of the 9k tests and should be + * kept last in case new tests are added + */ + SDIO_TEST_PERF, + SDIO_TEST_RTT, + SDIO_TEST_MODEM_RESET, +}; + +struct lpm_task { + struct task_struct *lpm_task; + const char *task_name; +}; + +struct lpm_entry_type { + enum lpm_test_msg_type msg_type; + char msg_name[LPM_MSG_NAME_SIZE]; + u32 counter; + u32 current_ms; + u32 read_avail_mask; + char chan_name[CHANNEL_NAME_SIZE]; +}; + +struct lpm_msg { + u32 signature; + u32 counter; + u32 reserve1; + u32 reserve2; +}; + +struct test_config_msg { + u32 signature; + u32 test_case; + u32 test_param; + u32 num_packets; + u32 num_iterations; +}; + +struct test_result_msg { + u32 signature; + u32 is_successful; +}; + +struct test_work { + struct work_struct work; + struct test_channel *test_ch; +}; + +enum sdio_channels_ids { + SDIO_RPC, + SDIO_QMI, + SDIO_RMNT, + SDIO_DIAG, + SDIO_DUN, + SDIO_SMEM, + SDIO_CSVT, + SDIO_MAX_CHANNELS +}; + +enum sdio_test_results { + TEST_NO_RESULT, + TEST_FAILED, + TEST_PASSED +}; + +enum sdio_lpm_vote_state { + SDIO_NO_VOTE, + SDIO_VOTE_FOR_SLEEP, + SDIO_VOTE_AGAINST_SLEEP +}; + +struct sdio_test_device { + int open_channels_counter_to_recv; + int open_channels_counter_to_send; + struct lpm_entry_type *lpm_arr; + int array_size; + void *sdio_al_device; + spinlock_t lpm_array_lock; + unsigned long lpm_array_lock_flags; + u32 next_avail_entry_in_array; + struct lpm_task lpm_test_task; + u32 next_mask_id; + u32 read_avail_mask; + int modem_result_per_dev; + int final_result_per_dev; +}; + +struct test_channel { + struct sdio_channel *ch; + + char name[CHANNEL_NAME_SIZE]; + int ch_id; + + struct sdio_test_device *test_device; + + u32 *buf; + u32 buf_size; + + struct workqueue_struct *workqueue; + struct test_work test_work; + + u32 rx_bytes; + u32 tx_bytes; + + wait_queue_head_t wait_q; + atomic_t rx_notify_count; + atomic_t tx_notify_count; + atomic_t any_notify_count; + atomic_t wakeup_client; + atomic_t card_detected_event; + + int wait_counter; + + int is_used; + int test_type; + int ch_ready; + + struct test_config_msg config_msg; + + int test_completed; + int test_result; + struct timer_list timer; + int timer_interval_ms; + + struct timer_list timeout_timer; + int timeout_ms; + void *sdio_al_device; + int is_ok_to_sleep; + unsigned int packet_length; + int random_packet_size; + int next_index_in_sent_msg_per_chan; + int channel_mask_id; + int modem_result_per_chan; + int notify_counter_per_chan; + int max_burst_size; /* number of writes before close/open */ + int card_removed; +}; + +struct sdio_al_test_debug { + u32 dun_throughput; + u32 rmnt_throughput; + struct dentry *debug_root; + struct dentry *debug_test_result; + struct dentry *debug_dun_throughput; + struct dentry *debug_rmnt_throughput; + struct dentry *rpc_sender_test; + struct dentry *rpc_qmi_diag_sender_test; + struct dentry *smem_test; + struct dentry *smem_rpc_test; + struct dentry *rmnet_a2_validation_test; + struct dentry *dun_a2_validation_test; + struct dentry *rmnet_a2_perf_test; + struct dentry *dun_a2_perf_test; + struct dentry *csvt_a2_perf_test; + struct dentry *rmnet_dun_a2_perf_test; + struct dentry *rpc_sender_rmnet_a2_perf_test; + struct dentry *all_channels_test; + struct dentry *host_sender_no_lp_diag_test; + struct dentry *host_sender_no_lp_diag_rpc_test; + struct dentry *rmnet_small_packets_test; + struct dentry *rmnet_rtt_test; + struct dentry *csvt_rtt_test; + struct dentry *modem_reset_rpc_test; + struct dentry *modem_reset_rmnet_test; + struct dentry *modem_reset_channels_4bit_dev_test; + struct dentry *modem_reset_channels_8bit_dev_test; + struct dentry *modem_reset_all_channels_test; + struct dentry *open_close_test; + struct dentry *open_close_dun_rmnet_test; + struct dentry *close_chan_lpm_test; + struct dentry *lpm_test_client_wakes_host_test; + struct dentry *lpm_test_host_wakes_client_test; + struct dentry *lpm_test_random_single_channel_test; + struct dentry *lpm_test_random_multi_channel_test; +}; + +struct test_context { + dev_t dev_num; + struct device *dev; + struct cdev *cdev; + int number_of_active_devices; + int max_number_of_devices; + + struct sdio_test_device test_dev_arr[MAX_NUM_OF_SDIO_DEVICES]; + + struct test_channel *test_ch; + + struct test_channel *test_ch_arr[SDIO_MAX_CHANNELS]; + + long testcase; + + const char *name; + + int exit_flag; + + u32 signature; + + int runtime_debug; + + struct platform_device *smem_pdev; + struct sdio_smem_client *sdio_smem; + int smem_was_init; + u8 *smem_buf; + uint32_t smem_counter; + + struct platform_device *csvt_app_pdev; + + wait_queue_head_t wait_q; + int test_completed; + int test_result; + struct sdio_al_test_debug debug; + + struct wake_lock wake_lock; + + unsigned int lpm_pseudo_random_seed; +}; + +/* FORWARD DECLARATIONS */ +static int set_params_loopback_9k(struct test_channel *tch); +static int set_params_smem_test(struct test_channel *tch); +static int set_params_a2_validation(struct test_channel *tch); +static int set_params_a2_perf(struct test_channel *tch); +static int set_params_8k_sender_no_lp(struct test_channel *tch); +static int set_params_a2_small_pkts(struct test_channel *tch); +static int set_params_rtt(struct test_channel *tch); +static int set_params_loopback_9k_close(struct test_channel *tch); +static int close_channel_lpm_test(int channel_num); +static int set_params_lpm_test(struct test_channel *tch, + enum sdio_test_case_type test, + int timer_interval_ms); +static void set_pseudo_random_seed(void); +static int set_params_modem_reset(struct test_channel *tch); +static int test_start(void); +static void rx_cleanup(struct test_channel *test_ch, int *rx_packet_count); +static void sdio_al_test_cleanup_channels(void); +static void notify(void *priv, unsigned channel_event); +#ifdef CONFIG_MSM_SDIO_SMEM +static int sdio_smem_open(struct sdio_smem_client *sdio_smem); +#endif + +/* + * Seed for pseudo random time sleeping in Random LPM test. + * If not set, current time in jiffies is used. + */ +static unsigned int seed; +module_param(seed, int, 0); +static struct test_context *test_ctx; + +static void sdio_al_test_initial_dev_and_chan(struct test_context *test_ctx) +{ + int i = 0; + + if (!test_ctx) { + pr_err(TEST_MODULE_NAME ":%s - test_ctx is NULL.\n", __func__); + return; + } + + for (i = 0 ; i < MAX_NUM_OF_SDIO_DEVICES ; ++i) + test_ctx->test_dev_arr[i].sdio_al_device = NULL; + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + if (!tch) + continue; + tch->is_used = 0; + } + + sdio_al_test_cleanup_channels(); +} + +#ifdef CONFIG_DEBUG_FS + +static int message_repeat; + +static int sdio_al_test_extract_number(const char __user *buf, + size_t count) +{ + int ret = 0; + int number = -1; + char local_buf[MAX_STR_SIZE] = {0}; + char *start = NULL; + + if (count > MAX_STR_SIZE) { + pr_err(TEST_MODULE_NAME ": %s - MAX_STR_SIZE(%d) < count(%d). " + "Please choose smaller number\n", + __func__, MAX_STR_SIZE, (int)count); + return -EINVAL; + } + + if (copy_from_user(local_buf, buf, count)) { + pr_err(TEST_MODULE_NAME ": %s - copy_from_user() failed\n", + __func__); + return -EINVAL; + } + + /* adding null termination to the string */ + local_buf[count] = '\0'; + + /* stripping leading and trailing white spaces */ + start = strstrip(local_buf); + + ret = kstrtoint(start, 10, &number); + + if (ret) { + pr_err(TEST_MODULE_NAME " : %s - kstrtoint() failed\n", + __func__); + return ret; + } + + return number; +} + +static int sdio_al_test_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + message_repeat = 1; + return 0; +} + +static void sdio_al_test_cleanup_channels(void) +{ + int channel_num; + int dummy = 0; + + for (channel_num = 0 ; channel_num < SDIO_MAX_CHANNELS ; + ++channel_num) { + if (channel_num == SDIO_SMEM) + continue; + + rx_cleanup(test_ctx->test_ch_arr[channel_num], &dummy); + } + + return; +} + +/* RPC SENDER TEST */ +static ssize_t rpc_sender_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RPC SENDER TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rpc_sender_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRPC_SENDER_TEST\n" + "===============\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rpc_sender_test_ops = { + .open = sdio_al_test_open, + .write = rpc_sender_test_write, + .read = rpc_sender_test_read, +}; + +/* RPC, QMI & DIAG SENDER TEST */ +static ssize_t rpc_qmi_diag_sender_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RPC, QMI AND DIAG SENDER TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rpc_qmi_diag_sender_test_read(struct file *file, + char __user + *buffer, size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRPC_QMI_DIAG_SENDER_TEST\n" + "========================\n" + "Description:\n" + "TBD\n"); + + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rpc_qmi_diag_sender_test_ops = { + .open = sdio_al_test_open, + .write = rpc_qmi_diag_sender_test_write, + .read = rpc_qmi_diag_sender_test_read, +}; + +/* SMEM TEST */ +static ssize_t smem_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- SMEM TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t smem_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nSMEM_TEST\n" + "=========\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations smem_test_ops = { + .open = sdio_al_test_open, + .write = smem_test_write, + .read = smem_test_read, +}; + +/* SMEM & RPC TEST */ +static ssize_t smem_rpc_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- SMEM AND RPC TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t smem_rpc_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nSMEM_RPC_TEST\n" + "=============\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations smem_rpc_test_ops = { + .open = sdio_al_test_open, + .write = smem_rpc_test_write, + .read = smem_rpc_test_read, +}; + +/* RMNET A2 VALIDATION TEST */ +static ssize_t rmnet_a2_validation_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET A2 VALIDATION TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_validation(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_a2_validation_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_A2_VALIDATION_TEST\n" + "=========================\n" + "Description:\n" + "In this test, the HOST sends multiple packets to the\n" + "CLIENT and validates the packets loop backed from A2\n" + "for the RMNET channel.\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_a2_validation_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_a2_validation_test_write, + .read = rmnet_a2_validation_test_read, +}; + +/* DUN A2 VALIDATION TEST */ +static ssize_t dun_a2_validation_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- DUN A2 VALIDATION TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_validation(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t dun_a2_validation_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nDUN_A2_VALIDATION_TEST\n" + "=========================\n" + "Description:\n" + "In this test, the HOST sends multiple packets to the\n" + "CLIENT and validates the packets loop backed from A2\n" + "for the DUN channel.\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations dun_a2_validation_test_ops = { + .open = sdio_al_test_open, + .write = dun_a2_validation_test_write, + .read = dun_a2_validation_test_read, +}; + +/* RMNET A2 PERFORMANCE TEST */ +static ssize_t rmnet_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_A2_PERFORMANCE_TEST\n" + "=========================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_a2_perf_test_write, + .read = rmnet_a2_perf_test_read, +}; + +/* DUN A2 PERFORMANCE TEST */ +static ssize_t dun_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- DUN A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t dun_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nDUN_A2_PERFORMANCE_TEST\n" + "=======================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations dun_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = dun_a2_perf_test_write, + .read = dun_a2_perf_test_read, +}; + +/* CSVT A2 PERFORMANCE TEST */ +static ssize_t csvt_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- CSVT A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t csvt_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nCSVT_A2_PERFORMANCE_TEST\n" + "========================\n" + "Description:\n" + "Loopback test on the CSVT Channel, in order to check " + "throughput performance.\n" + "Packet size that are sent on the CSVT channel in this " + "test is %d.bytes\n\n" + "END OF DESCRIPTION\n", CSVT_PACKET_SIZE); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations csvt_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = csvt_a2_perf_test_write, + .read = csvt_a2_perf_test_read, +}; + +/* RMNET DUN A2 PERFORMANCE TEST */ +static ssize_t rmnet_dun_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET AND DUN A2 PERFORMANCE TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_dun_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_DUN_A2_PERFORMANCE_TEST\n" + "=============================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_dun_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_dun_a2_perf_test_write, + .read = rmnet_dun_a2_perf_test_read, +}; + +/* RPC SENDER & RMNET A2 PERFORMANCE TEST */ +static ssize_t rpc_sender_rmnet_a2_perf_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "--RPC SENDER AND RMNET A2 " + "PERFORMANCE --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rpc_sender_rmnet_a2_perf_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRPC_SENDER_RMNET_A2_PERFORMANCE_TEST\n" + "====================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rpc_sender_rmnet_a2_perf_test_ops = { + .open = sdio_al_test_open, + .write = rpc_sender_rmnet_a2_perf_test_write, + .read = rpc_sender_rmnet_a2_perf_test_read, +}; + +/* ALL CHANNELS TEST */ +static ssize_t all_channels_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- ALL THE CHANNELS TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_DIAG]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]); + set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]); + set_params_a2_perf(test_ctx->test_ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t all_channels_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nALL_CHANNELS_TEST\n" + "=================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations all_channels_test_ops = { + .open = sdio_al_test_open, + .write = all_channels_test_write, + .read = all_channels_test_read, +}; + +/* HOST SENDER NO LP DIAG TEST */ +static ssize_t host_sender_no_lp_diag_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER NO LP FOR DIAG TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t host_sender_no_lp_diag_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nHOST_SENDER_NO_LP_DIAG_TEST\n" + "===========================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations host_sender_no_lp_diag_test_ops = { + .open = sdio_al_test_open, + .write = host_sender_no_lp_diag_test_write, + .read = host_sender_no_lp_diag_test_read, +}; + +/* HOST SENDER NO LP DIAG, RPC TEST */ +static ssize_t host_sender_no_lp_diag_rpc_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER NO LP FOR DIAG, RPC " + "TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_DIAG]); + set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_RPC]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t host_sender_no_lp_diag_rpc_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nHOST_SENDER_NO_LP_DIAG_RPC_TEST\n" + "===================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations host_sender_no_lp_diag_rpc_test_ops = { + .open = sdio_al_test_open, + .write = host_sender_no_lp_diag_rpc_test_write, + .read = host_sender_no_lp_diag_rpc_test_read, +}; + +/* RMNET SMALL PACKETS TEST */ +static ssize_t rmnet_small_packets_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET SMALL PACKETS (5-128) TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_a2_small_pkts(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_small_packets_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_SMALL_PACKETS_TEST\n" + "========================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_small_packets_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_small_packets_test_write, + .read = rmnet_small_packets_test_read, +}; + +/* RMNET RTT TEST */ +static ssize_t rmnet_rtt_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- RMNET RTT TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_rtt(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t rmnet_rtt_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nRMNET_RTT_TEST\n" + "==============\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations rmnet_rtt_test_ops = { + .open = sdio_al_test_open, + .write = rmnet_rtt_test_write, + .read = rmnet_rtt_test_read, +}; + +/* CSVT RTT TEST */ +static ssize_t csvt_rtt_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- CSVT RTT TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_rtt(test_ctx->test_ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t csvt_rtt_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nCSVT_RTT_TEST\n" + "==============\n" + "Description:\n" + "In this test the HOST send a message of %d bytes " + "to the CLIENT\n\n" + "END OF DESCRIPTION\n", SDIO_CSVT_RTT_PACKET_SIZE); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations csvt_rtt_test_ops = { + .open = sdio_al_test_open, + .write = csvt_rtt_test_write, + .read = csvt_rtt_test_read, +}; + +/* MODEM RESET RPC TEST */ +static ssize_t modem_reset_rpc_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - RPC CHANNEL TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_rpc_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_RPC_TEST\n" + "====================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_rpc_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_rpc_test_write, + .read = modem_reset_rpc_test_read, +}; + +/* MODEM RESET RMNET TEST */ +static ssize_t modem_reset_rmnet_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - RMNT CHANNEL TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_rmnet_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_RMNET_TEST\n" + "======================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_rmnet_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_rmnet_test_write, + .read = modem_reset_rmnet_test_read, +}; + +/* MODEM RESET - CHANNELS IN 4BIT DEVICE TEST */ +static ssize_t modem_reset_channels_4bit_dev_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS IN " + "4BIT DEVICE TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_channels_4bit_dev_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_CHANNELS_4BIT_DEV_TEST\n" + "==================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_channels_4bit_dev_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_channels_4bit_dev_test_write, + .read = modem_reset_channels_4bit_dev_test_read, +}; + +/* MODEM RESET - CHANNELS IN 8BIT DEVICE TEST */ +static ssize_t modem_reset_channels_8bit_dev_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS IN " + "8BIT DEVICE TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_channels_8bit_dev_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_CHANNELS_8BIT_DEV_TEST\n" + "==================================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_channels_8bit_dev_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_channels_8bit_dev_test_write, + .read = modem_reset_channels_8bit_dev_test_read, +}; + +/* MODEM RESET - ALL CHANNELS TEST */ +static ssize_t modem_reset_all_channels_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_QMI]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DIAG]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]); + set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DUN]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t modem_reset_all_channels_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nMODEM_RESET_ALL_CHANNELS_TEST\n" + "=============================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations modem_reset_all_channels_test_ops = { + .open = sdio_al_test_open, + .write = modem_reset_all_channels_test_write, + .read = modem_reset_all_channels_test_read, +}; + +/* HOST SENDER WITH OPEN/CLOSE TEST */ +static ssize_t open_close_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + struct test_channel **ch_arr = test_ctx->test_ch_arr; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER WITH OPEN/CLOSE TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k_close(ch_arr[SDIO_DIAG]); + set_params_loopback_9k_close(ch_arr[SDIO_RPC]); + set_params_loopback_9k_close(ch_arr[SDIO_SMEM]); + set_params_loopback_9k_close(ch_arr[SDIO_QMI]); + set_params_loopback_9k_close(ch_arr[SDIO_RMNT]); + set_params_loopback_9k_close(ch_arr[SDIO_DUN]); + set_params_loopback_9k_close(ch_arr[SDIO_CSVT]); + + ret = test_start(); + + if (ret) + break; + + pr_info(TEST_MODULE_NAME " -- correctness test for" + "DIAG "); + set_params_loopback_9k(ch_arr[SDIO_DIAG]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t open_close_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nOPEN_CLOSE_TEST\n" + "============================\n" + "Description:\n" + "In this test the host sends 5k packets to the modem in the " + "following sequence: Send a random burst of packets on " + "Diag and Rmnet channels, read 0 or a random number " + "of packets, close and re-open the channel. At the end of the " + "test, the channel is verified by running a loopback test\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations open_close_test_ops = { + .open = sdio_al_test_open, + .write = open_close_test_write, + .read = open_close_test_read, +}; + +/* HOST SENDER WITH OPEN/CLOSE FOR DUN & RMNET TEST */ +static ssize_t open_close_dun_rmnet_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + struct test_channel **ch_arr = test_ctx->test_ch_arr; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- HOST SENDER WITH OPEN/CLOSE FOR " + "DUN AND RMNET TEST --"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_loopback_9k_close(ch_arr[SDIO_DUN]); + set_params_loopback_9k_close(ch_arr[SDIO_RMNT]); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t open_close_dun_rmnet_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nOPEN_CLOSE_DUN_RMNET_TEST\n" + "============================\n" + "Description:\n" + "In this test the host sends 5k packets to the modem in the " + "following sequence: Send a random burst of packets on " + "DUN and Rmnet channels, read 0 or a random number " + "of packets, close and re-open the channel.\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations open_close_dun_rmnet_test_ops = { + .open = sdio_al_test_open, + .write = open_close_dun_rmnet_test_write, + .read = open_close_dun_rmnet_test_read, +}; + +/* CLOSE CHANNEL & LPM TEST HOST WAKES THE CLIENT TEST */ +static ssize_t close_chan_lpm_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int channel_num = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- CLOSE CHANNEL & LPM TEST " + "HOST WAKES THE CLIENT TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + for (channel_num = 0 ; channel_num < SDIO_MAX_CHANNELS ; + channel_num++) { + + ret = close_channel_lpm_test(channel_num); + + if (ret) + break; + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_HOST_WAKER, 120); + + ret = test_start(); + + if (ret) + break; + } + + if (ret) { + pr_err(TEST_MODULE_NAME " -- Close channel & LPM Test " + "FAILED: %d --\n", ret); + } else { + pr_err(TEST_MODULE_NAME " -- Close channel & LPM Test " + "PASSED\n"); + } + } + + return count; +} + +static ssize_t close_chan_lpm_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nCLOSE_CHAN_LPM_TEST\n" + "===================\n" + "Description:\n" + "TBD\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations close_chan_lpm_test_ops = { + .open = sdio_al_test_open, + .write = close_chan_lpm_test_write, + .read = close_chan_lpm_test_read, +}; + +/* LPM TEST FOR DEVICE 1. CLIENT WAKES THE HOST TEST */ +static ssize_t lpm_test_client_wakes_host_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST FOR DEVICE 1. CLIENT " + "WAKES THE HOST TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_CLIENT_WAKER, 90); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_client_wakes_host_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_CLIENT_WAKES_HOST_TEST\n" + "===============================\n" + "Description:\n" + "In this test, the HOST is going into LPM mode,\n" + "and the CLIENT is responsible to send it a message\n" + "in order to wake it up\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_client_wakes_host_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_client_wakes_host_test_write, + .read = lpm_test_client_wakes_host_test_read, +}; + +/* LPM TEST FOR DEVICE 1. HOST WAKES THE CLIENT TEST */ +static ssize_t lpm_test_host_wakes_client_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST FOR DEVICE 1. HOST " + "WAKES THE CLIENT TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_HOST_WAKER, 120); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_host_wakes_client_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_HOST_WAKES_CLIENT_TEST\n" + "===============================\n" + "Description:\n" + "In this test, the CLIENT goes into LPM mode, and the\n" + "HOST is responsible to send it a message\n" + "in order to wake it up\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_host_wakes_client_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_host_wakes_client_test_write, + .read = lpm_test_host_wakes_client_test_read, +}; + +/* LPM TEST RANDOM, SINGLE CHANNEL TEST */ +static ssize_t lpm_test_random_single_channel_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST RANDOM SINGLE " + "CHANNEL TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_pseudo_random_seed(); + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_RANDOM, 0); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_random_single_channel_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_RANDOM_SINGLE_CHANNEL_TEST\n" + "===================================\n" + "Description:\n" + "In this test, the HOST and CLIENT " + "send messages to each other,\n" + "random in time, over RPC channel only.\n" + "All events are being recorded, and later on,\n" + "they are being analysed by the HOST and by the CLIENT\n," + "in order to check if the LPM mechanism worked properly,\n" + "meaning:" + " When all the relevant conditions are met, a device should:\n" + "1. Go to sleep\n" + "2. Wake up\n" + "3. Stay awake\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_random_single_channel_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_random_single_channel_test_write, + .read = lpm_test_random_single_channel_test_read, +}; + +/* LPM TEST RANDOM, MULTI CHANNEL TEST */ +static ssize_t lpm_test_random_multi_channel_test_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + + pr_info(TEST_MODULE_NAME "-- LPM TEST RANDOM MULTI CHANNEL TEST --\n"); + + number = sdio_al_test_extract_number(buf, count); + + if (number < 0) { + pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() " + "failed. number = %d\n", __func__, number); + return count; + } + + for (i = 0 ; i < number ; ++i) { + pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number); + pr_info(TEST_MODULE_NAME " ==================="); + + sdio_al_test_initial_dev_and_chan(test_ctx); + + set_pseudo_random_seed(); + + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC], + SDIO_TEST_LPM_RANDOM, 0); + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_DIAG], + SDIO_TEST_LPM_RANDOM, 0); + set_params_lpm_test(test_ctx->test_ch_arr[SDIO_QMI], + SDIO_TEST_LPM_RANDOM, 0); + + ret = test_start(); + + if (ret) + break; + } + + return count; +} + +static ssize_t lpm_test_random_multi_channel_test_read( + struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nLPM_TEST_RANDOM_MULTI_CHANNEL_TEST\n" + "==================================\n" + "Description:\n" + "In this test, the HOST and CLIENT " + "send messages to each other,\n" + "random in time, over RPC, QMI AND DIAG channels\n" + "(i.e, on both SDIO devices).\n" + "All events are being recorded, and later on,\n" + "they are being analysed by the HOST and by the CLIENT,\n" + "in order to check if the LPM mechanism worked properly,\n" + "meaning:" + " When all the relevant conditions are met, a device should:\n" + "1. Go to sleep\n" + "2. Wake up\n" + "3. Stay awake\n\n" + "END OF DESCRIPTION\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations lpm_test_random_multi_channel_test_ops = { + .open = sdio_al_test_open, + .write = lpm_test_random_multi_channel_test_write, + .read = lpm_test_random_multi_channel_test_read, +}; + +static int sdio_al_test_debugfs_init(void) +{ + test_ctx->debug.debug_root = debugfs_create_dir("sdio_al_test", + NULL); + if (!test_ctx->debug.debug_root) + return -ENOENT; + + test_ctx->debug.debug_test_result = debugfs_create_u32( + "test_result", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + &test_ctx->test_result); + + test_ctx->debug.debug_dun_throughput = debugfs_create_u32( + "dun_throughput", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + &test_ctx->debug.dun_throughput); + + test_ctx->debug.debug_rmnt_throughput = debugfs_create_u32( + "rmnt_throughput", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + &test_ctx->debug.rmnt_throughput); + + test_ctx->debug.rpc_sender_test = + debugfs_create_file("10_rpc_sender_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rpc_sender_test_ops); + + test_ctx->debug.rpc_qmi_diag_sender_test = + debugfs_create_file("20_rpc_qmi_diag_sender_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rpc_qmi_diag_sender_test_ops); + + test_ctx->debug.rmnet_a2_validation_test = + debugfs_create_file("30_rmnet_a2_validation_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_a2_validation_test_ops); + + test_ctx->debug.dun_a2_validation_test = + debugfs_create_file("40_dun_a2_validation_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &dun_a2_validation_test_ops); + + test_ctx->debug.rmnet_a2_perf_test = + debugfs_create_file("50_rmnet_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_a2_perf_test_ops); + + test_ctx->debug.dun_a2_perf_test = + debugfs_create_file("60_dun_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &dun_a2_perf_test_ops); + + test_ctx->debug.csvt_a2_perf_test = + debugfs_create_file("71_csvt_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &csvt_a2_perf_test_ops); + + test_ctx->debug.rmnet_dun_a2_perf_test = + debugfs_create_file("70_rmnet_dun_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_dun_a2_perf_test_ops); + + test_ctx->debug.rpc_sender_rmnet_a2_perf_test = + debugfs_create_file("80_rpc_sender_rmnet_a2_perf_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rpc_sender_rmnet_a2_perf_test_ops); + + test_ctx->debug.smem_test = + debugfs_create_file("90_smem_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &smem_test_ops); + + test_ctx->debug.smem_rpc_test = + debugfs_create_file("100_smem_rpc_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &smem_rpc_test_ops); + + test_ctx->debug.all_channels_test = + debugfs_create_file("150_all_channels_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &all_channels_test_ops); + + test_ctx->debug.host_sender_no_lp_diag_test = + debugfs_create_file("160_host_sender_no_lp_diag_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &host_sender_no_lp_diag_test_ops); + + test_ctx->debug.host_sender_no_lp_diag_rpc_test = + debugfs_create_file("170_host_sender_no_lp_diag_rpc_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &host_sender_no_lp_diag_rpc_test_ops); + + test_ctx->debug.rmnet_small_packets_test = + debugfs_create_file("180_rmnet_small_packets_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_small_packets_test_ops); + + test_ctx->debug.rmnet_rtt_test = + debugfs_create_file("190_rmnet_rtt_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &rmnet_rtt_test_ops); + + test_ctx->debug.csvt_rtt_test = + debugfs_create_file("191_csvt_rtt_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &csvt_rtt_test_ops); + + test_ctx->debug.modem_reset_rpc_test = + debugfs_create_file("220_modem_reset_rpc_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_rpc_test_ops); + + test_ctx->debug.modem_reset_rmnet_test = + debugfs_create_file("230_modem_reset_rmnet_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_rmnet_test_ops); + + test_ctx->debug.modem_reset_channels_4bit_dev_test = + debugfs_create_file("240_modem_reset_channels_4bit_dev_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_channels_4bit_dev_test_ops); + + test_ctx->debug.modem_reset_channels_8bit_dev_test = + debugfs_create_file("250_modem_reset_channels_8bit_dev_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_channels_8bit_dev_test_ops); + + test_ctx->debug.modem_reset_all_channels_test = + debugfs_create_file("260_modem_reset_all_channels_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &modem_reset_all_channels_test_ops); + + test_ctx->debug.open_close_test = + debugfs_create_file("270_open_close_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &open_close_test_ops); + + test_ctx->debug.open_close_dun_rmnet_test = + debugfs_create_file("271_open_close_dun_rmnet_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &open_close_dun_rmnet_test_ops); + + test_ctx->debug.close_chan_lpm_test = + debugfs_create_file("280_close_chan_lpm_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &close_chan_lpm_test_ops); + + test_ctx->debug.lpm_test_client_wakes_host_test = + debugfs_create_file("600_lpm_test_client_wakes_host_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_client_wakes_host_test_ops); + + test_ctx->debug.lpm_test_host_wakes_client_test = + debugfs_create_file("610_lpm_test_host_wakes_client_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_host_wakes_client_test_ops); + + test_ctx->debug.lpm_test_random_single_channel_test = + debugfs_create_file("620_lpm_test_random_single_channel_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_random_single_channel_test_ops); + + test_ctx->debug.lpm_test_random_multi_channel_test = + debugfs_create_file("630_lpm_test_random_multi_channel_test", + S_IRUGO | S_IWUGO, + test_ctx->debug.debug_root, + NULL, + &lpm_test_random_multi_channel_test_ops); + + if ((!test_ctx->debug.debug_dun_throughput) && + (!test_ctx->debug.debug_rmnt_throughput)) { + debugfs_remove_recursive(test_ctx->debug.debug_root); + test_ctx->debug.debug_root = NULL; + return -ENOENT; + } + return 0; +} + +static void sdio_al_test_debugfs_cleanup(void) +{ + debugfs_remove(test_ctx->debug.debug_dun_throughput); + debugfs_remove(test_ctx->debug.debug_rmnt_throughput); + debugfs_remove(test_ctx->debug.debug_root); +} +#endif + +static int channel_name_to_id(char *name) +{ + pr_info(TEST_MODULE_NAME "%s: channel name %s\n", + __func__, name); + + if (!strncmp(name, "SDIO_RPC_TEST", + strnlen("SDIO_RPC_TEST", CHANNEL_NAME_SIZE))) + return SDIO_RPC; + else if (!strncmp(name, "SDIO_QMI_TEST", + strnlen("SDIO_QMI_TEST", TEST_CH_NAME_SIZE))) + return SDIO_QMI; + else if (!strncmp(name, "SDIO_RMNT_TEST", + strnlen("SDIO_RMNT_TEST", TEST_CH_NAME_SIZE))) + return SDIO_RMNT; + else if (!strncmp(name, "SDIO_DIAG_TEST", + strnlen("SDIO_DIAG", TEST_CH_NAME_SIZE))) + return SDIO_DIAG; + else if (!strncmp(name, "SDIO_DUN_TEST", + strnlen("SDIO_DUN_TEST", TEST_CH_NAME_SIZE))) + return SDIO_DUN; + else if (!strncmp(name, "SDIO_SMEM_TEST", + strnlen("SDIO_SMEM_TEST", TEST_CH_NAME_SIZE))) + return SDIO_SMEM; + else if (!strncmp(name, "SDIO_CSVT_TEST", + strnlen("SDIO_CSVT_TEST", TEST_CH_NAME_SIZE))) + return SDIO_CSVT; + else + return SDIO_MAX_CHANNELS; + + return SDIO_MAX_CHANNELS; +} + +/** + * Allocate and add SDIO_SMEM platform device + */ +#ifdef CONFIG_MSM_SDIO_SMEM +static int add_sdio_smem(void) +{ + int ret = 0; + + test_ctx->smem_pdev = platform_device_alloc("SDIO_SMEM", -1); + ret = platform_device_add(test_ctx->smem_pdev); + if (ret) { + pr_err(TEST_MODULE_NAME ": platform_device_add failed, " + "ret=%d\n", ret); + return ret; + } + return 0; +} +#endif + +static int open_sdio_ch(struct test_channel *tch) +{ + int ret = 0; + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__); + return -EINVAL; + } + + if (!tch->ch_ready) { + TEST_DBG(TEST_MODULE_NAME ":openning channel %s\n", + tch->name); + if (tch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + if (!test_ctx->smem_pdev) + ret = add_sdio_smem(); + else + ret = sdio_smem_open(test_ctx->sdio_smem); + if (ret) { + pr_err(TEST_MODULE_NAME + ":openning channel %s failed\n", + tch->name); + tch->ch_ready = false; + return -EINVAL; + } +#endif + } else { + tch->ch_ready = true; + ret = sdio_open(tch->name , &tch->ch, tch, + notify); + if (ret) { + pr_err(TEST_MODULE_NAME + ":openning channel %s failed\n", + tch->name); + tch->ch_ready = false; + return -EINVAL; + } + } + } + return ret; +} + +static int close_sdio_ch(struct test_channel *tch) +{ + int ret = 0; + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__); + return -EINVAL; + } + + if (tch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + TEST_DBG(TEST_MODULE_NAME":%s closing channel %s", + __func__, tch->name); + ret = sdio_smem_unregister_client(); + test_ctx->smem_counter = 0; +#endif + } else { + ret = sdio_close(tch->ch); + } + + if (ret) { + pr_err(TEST_MODULE_NAME":%s close channel %s" + " failed\n", __func__, tch->name); + } else { + TEST_DBG(TEST_MODULE_NAME":%s close channel %s" + " success\n", __func__, tch->name); + tch->ch_ready = false; + } + return ret; +} + +/** + * Config message + */ + +static void send_config_msg(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 write_avail = 0; + int size = sizeof(test_ch->config_msg); + + pr_debug(TEST_MODULE_NAME "%s\n", __func__); + + memcpy(test_ch->buf, (void *)&test_ch->config_msg, size); + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + pr_info(TEST_MODULE_NAME ":Sending the config message.\n"); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + pr_debug(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + pr_debug(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":not enough write avail.\n"); + return; + } + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) + pr_err(TEST_MODULE_NAME ":%s sdio_write err=%d.\n", + __func__, -ret); + else + pr_info(TEST_MODULE_NAME ":%s sent config_msg successfully.\n", + __func__); +} + +/** + * Loopback Test + */ +static void loopback_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + + while (1) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + TEST_DBG(TEST_MODULE_NAME "--LOOPBACK WAIT FOR EVENT--.\n"); + /* wait for data ready event */ + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) + continue; + + + write_avail = sdio_write_avail(test_ch->ch); + if (write_avail < read_avail) { + pr_info(TEST_MODULE_NAME + ":not enough write avail.\n"); + continue; + } + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME + ":worker, sdio_read err=%d.\n", -ret); + continue; + } + test_ch->rx_bytes += read_avail; + + TEST_DBG(TEST_MODULE_NAME ":worker total rx bytes = 0x%x.\n", + test_ch->rx_bytes); + + + ret = sdio_write(test_ch->ch, + test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME + ":loopback sdio_write err=%d.\n", + -ret); + continue; + } + test_ch->tx_bytes += read_avail; + + TEST_DBG(TEST_MODULE_NAME + ":loopback total tx bytes = 0x%x.\n", + test_ch->tx_bytes); + } /* end of while */ +} + +/** + * Check if all tests completed + */ +static void check_test_completion(void) +{ + int i; + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + if (!tch->test_completed) { + pr_info(TEST_MODULE_NAME ": %s - Channel %s test is " + "not completed", __func__, tch->name); + return; + } + } + pr_info(TEST_MODULE_NAME ": %s - Test is completed", __func__); + test_ctx->test_completed = 1; + wake_up(&test_ctx->wait_q); +} + +static int pseudo_random_seed(unsigned int *seed_number) +{ + if (!seed_number) + return 0; + + *seed_number = (unsigned int)(((unsigned long)*seed_number * + (unsigned long)1103515367) + 35757); + return (int)((*seed_number / (64*1024)) % 500); +} + +/* this function must be locked before accessing it */ +static void lpm_test_update_entry(struct test_channel *tch, + enum lpm_test_msg_type msg_type, + char *msg_name, + int counter) +{ + u32 index = 0; + static int print_full = 1; + struct sdio_test_device *test_device; + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__); + return; + } + + test_device = tch->test_device; + + if (!test_device) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return; + } + + if (!test_device->lpm_arr) { + pr_err(TEST_MODULE_NAME ": %s - NULL lpm_arr\n", __func__); + return; + } + + if (test_device->next_avail_entry_in_array >= + test_device->array_size) { + pr_err(TEST_MODULE_NAME ": %s - lpm array is full", + __func__); + + if (print_full) { + print_hex_dump(KERN_INFO, TEST_MODULE_NAME ": lpm_arr:", + 0, 32, 2, + (void *)test_device->lpm_arr, + sizeof(test_device->lpm_arr), false); + print_full = 0; + } + return; + } + + index = test_device->next_avail_entry_in_array; + if ((msg_type == LPM_MSG_SEND) || (msg_type == LPM_MSG_REC)) + test_device->lpm_arr[index].counter = counter; + else + test_device->lpm_arr[index].counter = 0; + + test_device->lpm_arr[index].msg_type = msg_type; + memcpy(test_device->lpm_arr[index].msg_name, msg_name, + LPM_MSG_NAME_SIZE); + test_device->lpm_arr[index].current_ms = + jiffies_to_msecs(get_jiffies_64()); + + test_device->lpm_arr[index].read_avail_mask = + test_device->read_avail_mask; + + if ((msg_type == LPM_SLEEP) || (msg_type == LPM_WAKEUP)) + memcpy(test_device->lpm_arr[index].chan_name, "DEVICE ", + CHANNEL_NAME_SIZE); + else + memcpy(test_device->lpm_arr[index].chan_name, tch->name, + CHANNEL_NAME_SIZE); + + test_device->next_avail_entry_in_array++; +} + +static int wait_for_result_msg(struct test_channel *test_ch) +{ + u32 read_avail = 0; + int ret = 0; + + pr_info(TEST_MODULE_NAME ": %s - START, channel %s\n", + __func__, test_ch->name); + + while (1) { + read_avail = sdio_read_avail(test_ch->ch); + + if (read_avail == 0) { + pr_info(TEST_MODULE_NAME + ": read_avail is 0 for chan %s\n", + test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + continue; + } + + memset(test_ch->buf, 0x00, test_ch->buf_size); + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read for chan" + "%s failed, err=%d.\n", + test_ch->name, -ret); + goto exit_err; + } + + if (test_ch->buf[0] != TEST_CONFIG_SIGNATURE) { + pr_info(TEST_MODULE_NAME ": Not a test_result " + "signature. expected 0x%x. received 0x%x " + "for chan %s\n", + TEST_CONFIG_SIGNATURE, + test_ch->buf[0], + test_ch->name); + continue; + } else { + pr_info(TEST_MODULE_NAME ": Signature is " + "TEST_CONFIG_SIGNATURE as expected for" + "channel %s\n", test_ch->name); + break; + } + } + + return test_ch->buf[1]; + +exit_err: + return 0; +} + +static void print_random_lpm_test_array(struct sdio_test_device *test_dev) +{ + int i; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return; + } + + for (i = 0 ; i < test_dev->next_avail_entry_in_array ; ++i) { + if (i == 0) + pr_err(TEST_MODULE_NAME ": index %4d, chan=%2s, " + "code=%1d=%4s, msg#%1d, ms from before=-1, " + "read_mask=0x%d, ms=%2u", + i, + test_dev->lpm_arr[i].chan_name, + test_dev->lpm_arr[i].msg_type, + test_dev->lpm_arr[i].msg_name, + test_dev->lpm_arr[i].counter, + test_dev->lpm_arr[i].read_avail_mask, + test_dev->lpm_arr[i].current_ms); + else + pr_err(TEST_MODULE_NAME ": index " + "%4d, %2s, code=%1d=%4s, msg#%1d, ms from " + "before=%2u, read_mask=0x%d, ms=%2u", + i, + test_dev->lpm_arr[i].chan_name, + test_dev->lpm_arr[i].msg_type, + test_dev->lpm_arr[i].msg_name, + test_dev->lpm_arr[i].counter, + test_dev->lpm_arr[i].current_ms - + test_dev->lpm_arr[i-1].current_ms, + test_dev->lpm_arr[i].read_avail_mask, + test_dev->lpm_arr[i].current_ms); + + udelay(1000); + } +} + +static int check_random_lpm_test_array(struct sdio_test_device *test_dev) +{ + int i = 0, j = 0; + unsigned int delta_ms = 0; + int arr_ind = 0; + int ret = 0; + int notify_counter = 0; + int sleep_counter = 0; + int wakeup_counter = 0; + int lpm_activity_counter = 0; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return -ENODEV; + } + + for (i = 0; i < test_dev->next_avail_entry_in_array; i++) { + notify_counter = 0; + sleep_counter = 0; + wakeup_counter = 0; + + if ((test_dev->lpm_arr[i].msg_type == LPM_MSG_SEND) || + (test_dev->lpm_arr[i].msg_type == LPM_MSG_REC)) { + /* find the next message in the array */ + arr_ind = test_dev->next_avail_entry_in_array; + for (j = i+1; j < arr_ind; j++) { + if ((test_dev->lpm_arr[j].msg_type == + LPM_MSG_SEND) || + (test_dev->lpm_arr[j].msg_type == + LPM_MSG_REC) || + (test_dev->lpm_arr[j].msg_type == + LPM_NOTIFY)) + break; + if (test_dev->lpm_arr[j].msg_type == + LPM_SLEEP) + sleep_counter++; + if (test_dev->lpm_arr[j].msg_type == + LPM_WAKEUP) + wakeup_counter++; + } + if (j == arr_ind) { + ret = 0; + break; + } + + delta_ms = test_dev->lpm_arr[j].current_ms - + test_dev->lpm_arr[i].current_ms; + if (delta_ms < 30) { + if ((sleep_counter == 0) + && (wakeup_counter == 0)) { + continue; + } else { + pr_err(TEST_MODULE_NAME "%s: lpm " + "activity while delta is less " + "than 30, i=%d, j=%d, " + "sleep_counter=%d, " + "wakeup_counter=%d", + __func__, i, j, + sleep_counter, wakeup_counter); + ret = -ENODEV; + break; + } + } else { + if ((delta_ms > 90) && + (test_dev->lpm_arr[i]. + read_avail_mask == 0)) { + if (j != i+3) { + pr_err(TEST_MODULE_NAME + "%s: unexpected " + "lpm activity " + "while delta is " + "bigger than " + "90, i=%d, " + "j=%d, " + "notify_counter" + "=%d", + __func__, i, j, + notify_counter); + ret = -ENODEV; + break; + } + lpm_activity_counter++; + } + } + } + } + + pr_info(TEST_MODULE_NAME ": %s - lpm_activity_counter=%d", + __func__, lpm_activity_counter); + + return ret; +} + +static int lpm_test_main_task(void *ptr) +{ + u32 read_avail = 0; + int last_msg_index = 0; + struct test_channel *test_ch = (struct test_channel *)ptr; + struct sdio_test_device *test_dev; + struct lpm_msg lpm_msg; + int ret = 0; + int host_result = 0; + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__); + return -ENODEV; + } + + pr_err(TEST_MODULE_NAME ": %s - STARTED. channel %s\n", + __func__, test_ch->name); + + test_dev = test_ch->test_device; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL Test Device\n", __func__); + return -ENODEV; + } + + while (last_msg_index < test_ch->config_msg.num_packets - 1) { + + TEST_DBG(TEST_MODULE_NAME ": %s - " + "IN LOOP last_msg_index=%d\n", + __func__, last_msg_index); + + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) { + TEST_DBG(TEST_MODULE_NAME + ":read_avail 0 for chan %s, " + "wait for event\n", + test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) { + pr_err(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as" + " expected\n", + read_avail, test_ch->name); + continue; + } + } + + memset(test_ch->buf, 0x00, sizeof(test_ch->buf)); + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME ":sdio_read for chan %s" + " err=%d.\n", test_ch->name, -ret); + goto exit_err; + } + + memcpy((void *)&lpm_msg, test_ch->buf, sizeof(lpm_msg)); + + /* + * when reading from channel, we want to turn off the bit + * mask that implies that there is pending data on that channel + */ + if (test_ch->test_device != NULL) { + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + test_ch->notify_counter_per_chan--; + + /* + * if the channel has no pending data, turn off the + * pending data bit mask of the channel + */ + if (test_ch->notify_counter_per_chan == 0) { + test_ch->test_device->read_avail_mask = + test_ch->test_device->read_avail_mask & + ~test_ch->channel_mask_id; + } + + last_msg_index = lpm_msg.counter; + lpm_test_update_entry(test_ch, + LPM_MSG_REC, + "RECEIVE", + last_msg_index); + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + } + } + + pr_info(TEST_MODULE_NAME ":%s: Finished to recieve all (%d) " + "packets from the modem %s. Waiting for result_msg", + __func__, test_ch->config_msg.num_packets, test_ch->name); + + /* Wait for the resault message from the modem */ + test_ch->modem_result_per_chan = wait_for_result_msg(test_ch); + + /* + * the DEVICE modem result is a failure if one of the channels on + * that device, got modem_result = 0. this is why we bitwise "AND" each + * time another channel completes its task + */ + test_dev->modem_result_per_dev &= test_ch->modem_result_per_chan; + + /* + * when reading from channel, we want to turn off the bit + * mask that implies that there is pending data on that channel + */ + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + test_dev->open_channels_counter_to_recv--; + + /* turning off the read_avail bit of the channel */ + test_ch->test_device->read_avail_mask = + test_ch->test_device->read_avail_mask & + ~test_ch->channel_mask_id; + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + /* Wait for all the packets to be sent to the modem */ + while (1) { + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + if (test_ch->next_index_in_sent_msg_per_chan >= + test_ch->config_msg.num_packets - 1) { + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + break; + } else { + pr_info(TEST_MODULE_NAME ":%s: Didn't finished to send " + "all packets, " + "next_index_in_sent_msg_per_chan = %d ", + __func__, + test_ch->next_index_in_sent_msg_per_chan); + } + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + msleep(60); + } + + /* + * if device has still open channels to test, then the test on the + * device is still running but the test on current channel is completed + */ + if (test_dev->open_channels_counter_to_recv != 0 || + test_dev->open_channels_counter_to_send != 0) { + test_ch->test_completed = 1; + return 0; + } else { + test_ctx->number_of_active_devices--; + sdio_al_unregister_lpm_cb(test_ch->sdio_al_device); + + if (test_ch->test_type == SDIO_TEST_LPM_RANDOM) + host_result = check_random_lpm_test_array(test_dev); + + if (host_result || + !test_dev->modem_result_per_dev || + test_ctx->runtime_debug) + print_random_lpm_test_array(test_dev); + + pr_info(TEST_MODULE_NAME ": %s - host_result=%d.(0 for " + "SUCCESS) device_modem_result=%d (1 for SUCCESS)", + __func__, host_result, test_dev->modem_result_per_dev); + + test_ch->test_completed = 1; + if (test_dev->modem_result_per_dev && !host_result) { + pr_info(TEST_MODULE_NAME ": %s - Random LPM " + "TEST_PASSED for device %d of %d\n", + __func__, + (test_ctx->max_number_of_devices- + test_ctx->number_of_active_devices), + test_ctx->max_number_of_devices); + test_dev->final_result_per_dev = 1; /* PASSED */ + } else { + pr_info(TEST_MODULE_NAME ": %s - Random LPM " + "TEST_FAILED for device %d of %d\n", + __func__, + (test_ctx->max_number_of_devices- + test_ctx->number_of_active_devices), + test_ctx->max_number_of_devices); + test_dev->final_result_per_dev = 0; /* FAILED */ + } + + check_test_completion(); + + kfree(test_ch->test_device->lpm_arr); + + return 0; + } + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_dev->open_channels_counter_to_recv--; + test_dev->next_avail_entry_in_array = 0; + test_ch->next_index_in_sent_msg_per_chan = 0; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return -ENODEV; +} + +static int lpm_test_create_read_thread(struct test_channel *test_ch) +{ + struct sdio_test_device *test_dev; + + pr_info(TEST_MODULE_NAME ": %s - STARTED channel %s\n", + __func__, test_ch->name); + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__); + return -ENODEV; + } + + test_dev = test_ch->test_device; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__); + return -ENODEV; + } + + test_dev->lpm_test_task.task_name = SDIO_LPM_TEST; + + test_dev->lpm_test_task.lpm_task = + kthread_create(lpm_test_main_task, + (void *)(test_ch), + test_dev->lpm_test_task.task_name); + + if (IS_ERR(test_dev->lpm_test_task.lpm_task)) { + pr_err(TEST_MODULE_NAME ": %s - kthread_create() failed\n", + __func__); + return -ENOMEM; + } + + wake_up_process(test_dev->lpm_test_task.lpm_task); + + return 0; +} + +static void lpm_continuous_rand_test(struct test_channel *test_ch) +{ + unsigned int local_ms = 0; + int ret = 0; + unsigned int write_avail = 0; + struct sdio_test_device *test_dev; + + pr_info(MODULE_NAME ": %s - STARTED\n", __func__); + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__); + return; + } + + test_dev = test_ch->test_device; + + if (!test_dev) { + pr_err(TEST_MODULE_NAME ": %s - NULL Test Device\n", __func__); + return; + } + + ret = lpm_test_create_read_thread(test_ch); + if (ret != 0) { + pr_err(TEST_MODULE_NAME ": %s - failed to create lpm reading " + "thread", __func__); + } + + while (1) { + + struct lpm_msg msg; + u32 ret = 0; + + /* sleeping period is dependent on number of open channels */ + test_ch->config_msg.test_param = + test_ctx->lpm_pseudo_random_seed; + + local_ms = test_dev->open_channels_counter_to_send * + test_ctx->lpm_pseudo_random_seed; + TEST_DBG(TEST_MODULE_NAME ":%s: SLEEPING for %d ms", + __func__, local_ms); + msleep(local_ms); + + msg.counter = test_ch->next_index_in_sent_msg_per_chan; + msg.signature = LPM_TEST_CONFIG_SIGNATURE; + msg.reserve1 = 0; + msg.reserve2 = 0; + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + pr_debug(TEST_MODULE_NAME ": %s: write_avail=%d\n", + __func__, write_avail); + if (write_avail < sizeof(msg)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + if (write_avail < sizeof(msg)) { + pr_info(TEST_MODULE_NAME ": %s: not enough write " + "avail.\n", __func__); + break; + } + + ret = sdio_write(test_ch->ch, (u32 *)&msg, sizeof(msg)); + if (ret) + pr_err(TEST_MODULE_NAME ":%s: sdio_write err=%d.\n", + __func__, -ret); + + TEST_DBG(TEST_MODULE_NAME ": %s: for chan %s, write, " + "msg # %d\n", + __func__, + test_ch->name, + test_ch->next_index_in_sent_msg_per_chan); + + if (test_ch->test_type == SDIO_TEST_LPM_RANDOM) { + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + lpm_test_update_entry(test_ch, LPM_MSG_SEND, + "SEND ", + test_ch-> + next_index_in_sent_msg_per_chan); + + test_ch->next_index_in_sent_msg_per_chan++; + + if (test_ch->next_index_in_sent_msg_per_chan == + test_ch->config_msg.num_packets) { + spin_unlock_irqrestore( + &test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + break; + } + + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + } + } + + spin_lock_irqsave(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + test_dev->open_channels_counter_to_send--; + spin_unlock_irqrestore(&test_dev->lpm_array_lock, + test_dev->lpm_array_lock_flags); + + pr_info(TEST_MODULE_NAME ": %s: - Finished to send all (%d) " + "packets to the modem on channel %s", + __func__, test_ch->config_msg.num_packets, test_ch->name); + + return; +} + +static void lpm_test(struct test_channel *test_ch) +{ + pr_info(TEST_MODULE_NAME ": %s - START channel %s\n", __func__, + test_ch->name); + + if (!test_ch) { + pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__); + return; + } + + test_ch->modem_result_per_chan = wait_for_result_msg(test_ch); + pr_debug(TEST_MODULE_NAME ": %s - delete the timeout timer\n", + __func__); + del_timer_sync(&test_ch->timeout_timer); + + if (test_ch->modem_result_per_chan == 0) { + pr_err(TEST_MODULE_NAME ": LPM TEST - Client didn't sleep. " + "Result Msg - is_successful=%d\n", test_ch->buf[1]); + goto exit_err; + } else { + pr_info(TEST_MODULE_NAME ": %s -" + "LPM 9K WAS SLEEPING - PASS\n", __func__); + if (test_ch->test_result == TEST_PASSED) { + pr_info(TEST_MODULE_NAME ": LPM TEST_PASSED\n"); + test_ch->test_completed = 1; + check_test_completion(); + } else { + pr_err(TEST_MODULE_NAME ": LPM TEST - Host didn't " + "sleep. Client slept\n"); + goto exit_err; + } + } + + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + + +/** + * LPM Test while the host wakes up the modem + */ +static void lpm_test_host_waker(struct test_channel *test_ch) +{ + pr_info(TEST_MODULE_NAME ": %s - START\n", __func__); + wait_event(test_ch->wait_q, atomic_read(&test_ch->wakeup_client)); + atomic_set(&test_ch->wakeup_client, 0); + + pr_info(TEST_MODULE_NAME ": %s - Sending the config_msg to wakeup " + " the client\n", __func__); + send_config_msg(test_ch); + + lpm_test(test_ch); +} + +/** + * Writes number of packets into test channel + * @test_ch: test channel control struct + * @burst_size: number of packets to send + */ +static int write_packet_burst(struct test_channel *test_ch, + int burst_size) +{ + int ret = 0; + int packet_count = 0; + unsigned int random_num = 0; + int size = test_ch->packet_length; /* first packet size */ + u32 write_avail = 0; + + while (packet_count < burst_size) { + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":%s write_avail=%d,size=%d on chan" + " %s\n", __func__, + write_avail, size, test_ch->name); + if (write_avail < size) { + TEST_DBG(TEST_MODULE_NAME ":%s wait for event on" + " chan %s\n", __func__, test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + write_avail = sdio_write_avail(test_ch->ch); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":%s not enough write" + " avail %d, need %d on chan %s\n", + __func__, write_avail, size, + test_ch->name); + continue; + } + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s sdio_write " + "failed (%d) on chan %s\n", __func__, + ret, test_ch->name); + break; + } + udelay(1000); /*low bus usage while running number of channels*/ + TEST_DBG(TEST_MODULE_NAME ":%s() successfully write %d bytes" + ", packet_count=%d on chan %s\n", __func__, + size, packet_count, test_ch->name); + test_ch->tx_bytes += size; + packet_count++; + /* get next packet size */ + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + } + return ret; +} + +/** + * Reads packet from test channel and checks that packet number + * encoded into the packet is equal to packet_counter + * This function is applicable for packet mode channels only + * + * @test_ch: test channel + * @size: expected packet size + * @packet_counter: number to validate readed packet + */ +static int read_data_from_packet_ch(struct test_channel *test_ch, + unsigned int size, + int packet_counter) +{ + u32 read_avail = 0; + int ret = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME + ":%s: NULL channel\n", __func__); + return -EINVAL; + } + + if (!test_ch->ch->is_packet_mode) { + pr_err(TEST_MODULE_NAME + ":%s:not packet mode ch %s\n", + __func__, test_ch->name); + return -EINVAL; + } + read_avail = sdio_read_avail(test_ch->ch); + /* wait for read data ready event */ + if (read_avail < size) { + TEST_DBG(TEST_MODULE_NAME ":%s() wait for rx data on " + "chan %s\n", __func__, test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + } + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":%s read_avail=%d bytes on chan %s\n", + __func__, read_avail, test_ch->name); + + if (read_avail != size) { + pr_err(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as " + "expected size %d\n", + read_avail, test_ch->name, size); + return -EINVAL; + } + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s() sdio_read for chan %s (%d)\n", + __func__, test_ch->name, -ret); + return ret; + } + if ((test_ch->buf[0] != packet_counter) && (size != 1)) { + pr_err(TEST_MODULE_NAME ":Read WRONG DATA" + " for chan %s, size=%d\n", + test_ch->name, size); + return -EINVAL; + } + return 0; +} + + +/** + * Reads packet from test channel and checks that packet number + * encoded into the packet is equal to packet_counter + * This function is applicable for streaming mode channels only + * + * @test_ch: test channel + * @size: expected packet size + * @packet_counter: number to validate readed packet + */ +static int read_data_from_stream_ch(struct test_channel *test_ch, + unsigned int size, + int packet_counter) +{ + u32 read_avail = 0; + int ret = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME + ":%s: NULL channel\n", __func__); + return -EINVAL; + } + + if (test_ch->ch->is_packet_mode) { + pr_err(TEST_MODULE_NAME + ":%s:not streaming mode ch %s\n", + __func__, test_ch->name); + return -EINVAL; + } + read_avail = sdio_read_avail(test_ch->ch); + /* wait for read data ready event */ + if (read_avail < size) { + TEST_DBG(TEST_MODULE_NAME ":%s() wait for rx data on " + "chan %s\n", __func__, test_ch->name); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + } + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":%s read_avail=%d bytes on chan %s\n", + __func__, read_avail, test_ch->name); + + if (read_avail < size) { + pr_err(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as " + "expected size %d\n", + read_avail, test_ch->name, size); + return -EINVAL; + } + + ret = sdio_read(test_ch->ch, test_ch->buf, size + A2_HEADER_OVERHEAD); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s() sdio_read for chan %s (%d)\n", + __func__, test_ch->name, -ret); + return ret; + } + if ((test_ch->buf[A2_HEADER_OVERHEAD/4] != packet_counter) && + (size != 1)) { + pr_err(TEST_MODULE_NAME ":Read WRONG DATA" + " for chan %s, size=%d, packet_counter=%d\n", + test_ch->name, size, packet_counter); + print_hex_dump(KERN_INFO, TEST_MODULE_NAME ": rmnet:", + 0, 32, 2, + (void *)test_ch->buf, + size + A2_HEADER_OVERHEAD, false); + return -EINVAL; + } + return 0; +} + +/** + * Test close channel feature for SDIO_SMEM channel: + * close && re-open the SDIO_SMEM channel. + */ +#ifdef CONFIG_MSM_SDIO_SMEM +static void open_close_smem_test(struct test_channel *test_ch) +{ + int i = 0; + int ret = 0; + + pr_info(TEST_MODULE_NAME ":%s\n", __func__); + + for (i = 0; i < 100 ; ++i) { + ret = close_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s close_sdio_ch for ch %s" + " failed\n", + __func__, test_ch->name); + goto exit_err; + } + ret = open_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s open_sdio_ch for ch %s " + " failed\n", + __func__, test_ch->name); + goto exit_err; + } + } + + pr_info(TEST_MODULE_NAME ":%s TEST PASS for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; +exit_err: + pr_info(TEST_MODULE_NAME ":%s TEST FAIL for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} +#endif + +/** + * Test close channel feature: + * 1. write random packet number into channel + * 2. read some data from channel (do this only for second half of + * requested packets to send). + * 3. close && re-open then repeat 1. + * + * Total packets to send: test_ch->config_msg.num_packets. + * Burst size is random in [1..test_ch->max_burst_size] range + * Packet size is random in [1..test_ch->packet_length] + */ +static void open_close_test(struct test_channel *test_ch) +{ + int ret = 0; + u32 read_avail = 0; + int total_packet_count = 0; + int size = 0; + u16 *buf16 = NULL; + int i; + int max_packet_count = 0; + unsigned int random_num = 0; + int curr_burst_size = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME ":%s NULL channel\n", + __func__); + return; + } + + curr_burst_size = test_ch->max_burst_size; + size = test_ch->packet_length; + buf16 = (u16 *) test_ch->buf; + + /* the test sends configured number of packets in + 2 portions: first without reading between write bursts, + second with it */ + max_packet_count = test_ch->config_msg.num_packets / 2; + + pr_info(TEST_MODULE_NAME ":%s channel %s, total packets:%d," + " max packet size %d, max burst size:%d\n", + __func__, test_ch->name, + test_ch->config_msg.num_packets, test_ch->packet_length, + test_ch->max_burst_size); + for (i = 0 ; i < size / 2 ; i++) + buf16[i] = (u16) (i & 0xFFFF); + + for (i = 0; i < 2 ; i++) { + total_packet_count = 0; + while (total_packet_count < max_packet_count) { + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":%s exit test\n", + __func__); + return; + } + test_ch->buf[0] = total_packet_count; + random_num = get_random_int(); + curr_burst_size = (random_num % + test_ch->max_burst_size) + 1; + + /* limit burst size to send + * no more than configured packets */ + if (curr_burst_size + total_packet_count > + max_packet_count) { + curr_burst_size = max_packet_count - + total_packet_count; + } + TEST_DBG(TEST_MODULE_NAME ":%s Current burst size:%d" + " on chan %s\n", __func__, + curr_burst_size, test_ch->name); + ret = write_packet_burst(test_ch, curr_burst_size); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s write burst failed (%d), ch %s\n", + __func__, ret, test_ch->name); + goto exit_err; + } + if (i > 0) { + /* read from channel */ + if (test_ch->ch->is_packet_mode) + ret = read_data_from_packet_ch(test_ch, + size, + total_packet_count); + else + ret = read_data_from_stream_ch(test_ch, + size, + total_packet_count); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s read" + " failed:%d, chan %s\n", + __func__, ret, + test_ch->name); + goto exit_err; + } + } + TEST_DBG(TEST_MODULE_NAME ":%s before close, ch %s\n", + __func__, test_ch->name); + ret = close_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s close channel %s" + " failed (%d)\n", + __func__, test_ch->name, ret); + goto exit_err; + } else { + TEST_DBG(TEST_MODULE_NAME":%s close channel %s" + " success\n", __func__, + test_ch->name); + total_packet_count += curr_burst_size; + atomic_set(&test_ch->rx_notify_count, 0); + atomic_set(&test_ch->tx_notify_count, 0); + atomic_set(&test_ch->any_notify_count, 0); + } + TEST_DBG(TEST_MODULE_NAME ":%s before open, ch %s\n", + __func__, test_ch->name); + ret = open_sdio_ch(test_ch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s open channel %s" + " failed (%d)\n", + __func__, test_ch->name, ret); + goto exit_err; + } else { + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail > 0) { + pr_err(TEST_MODULE_NAME": after open" + " ch %s read_availis not zero" + " (%d bytes)\n", + test_ch->name, read_avail); + goto exit_err; + } + } + TEST_DBG(TEST_MODULE_NAME ":%s total tx = %d," + " packet# = %d, size = %d for ch %s\n", + __func__, test_ch->tx_bytes, + total_packet_count, size, + test_ch->name); + } /* end of while */ + } + pr_info(TEST_MODULE_NAME ":%s Test end: total rx bytes = 0x%x," + " total tx bytes = 0x%x for chan %s\n", __func__, + test_ch->rx_bytes, test_ch->tx_bytes, test_ch->name); + pr_info(TEST_MODULE_NAME ":%s TEST PASS for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; +exit_err: + pr_info(TEST_MODULE_NAME ":%s TEST FAIL for chan %s.\n", __func__, + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * sender Test + */ +static void sender_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int packet_count = 0; + int size = 512; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int max_packet_count = 10000; + int random_num = 0; + + max_packet_count = test_ch->config_msg.num_packets; + + for (i = 0 ; i < size / 2 ; i++) + buf16[i] = (u16) (i & 0xFFFF); + + + pr_info(TEST_MODULE_NAME + ":SENDER TEST START for chan %s\n", test_ch->name); + + while (packet_count < max_packet_count) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + + TEST_DBG(TEST_MODULE_NAME "SENDER WAIT FOR EVENT for chan %s\n", + test_ch->name); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":not enough write avail.\n"); + continue; + } + + test_ch->buf[0] = packet_count; + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sender sdio_write err=%d.\n", + -ret); + goto exit_err; + } + + /* wait for read data ready event */ + TEST_DBG(TEST_MODULE_NAME ":sender wait for rx data for " + "chan %s\n", + test_ch->name); + read_avail = sdio_read_avail(test_ch->ch); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->rx_notify_count)); + atomic_dec(&test_ch->rx_notify_count); + + read_avail = sdio_read_avail(test_ch->ch); + + if (read_avail != size) { + pr_info(TEST_MODULE_NAME + ":read_avail size %d for chan %s not as " + "expected size %d.\n", + read_avail, test_ch->name, size); + goto exit_err; + } + + memset(test_ch->buf, 0x00, size); + + ret = sdio_read(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sender sdio_read for chan %s" + " err=%d.\n", + test_ch->name, -ret); + goto exit_err; + } + + + if ((test_ch->buf[0] != packet_count) && (size != 1)) { + pr_info(TEST_MODULE_NAME ":sender sdio_read WRONG DATA" + " for chan %s, size=%d\n", + test_ch->name, size); + goto exit_err; + } + + test_ch->tx_bytes += size; + test_ch->rx_bytes += size; + packet_count++; + + TEST_DBG(TEST_MODULE_NAME + ":sender total rx bytes = 0x%x , packet#=%d, size=%d" + " for chan %s\n", + test_ch->rx_bytes, packet_count, size, test_ch->name); + TEST_DBG(TEST_MODULE_NAME + ":sender total tx bytes = 0x%x , packet#=%d, size=%d" + " for chan %s\n", + test_ch->tx_bytes, packet_count, size, test_ch->name); + + } /* end of while */ + + pr_info(TEST_MODULE_NAME + ":SENDER TEST END: total rx bytes = 0x%x, " + " total tx bytes = 0x%x for chan %s\n", + test_ch->rx_bytes, test_ch->tx_bytes, test_ch->name); + + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * A2 Perf Test + */ +static void a2_performance_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + int size = 0; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int total_bytes = 0; + int max_packets = 10000; + u32 packet_size = test_ch->buf_size; + int rand_size = 0; + + u64 start_jiffy, end_jiffy, delta_jiffies; + unsigned int time_msec = 0; + u32 throughput = 0; + + max_packets = test_ch->config_msg.num_packets; + packet_size = test_ch->packet_length; + + for (i = 0; i < packet_size / 2; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME ": A2 PERFORMANCE TEST START for chan %s\n", + test_ch->name); + + start_jiffy = get_jiffies_64(); /* read the current time */ + + while (tx_packet_count < max_packets) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + if (test_ch->random_packet_size) { + rand_size = get_random_int(); + packet_size = (rand_size % test_ch->packet_length) + 1; + if (packet_size < A2_MIN_PACKET_SIZE) + packet_size = A2_MIN_PACKET_SIZE; + } + + /* wait for data ready event */ + /* use a func to avoid compiler optimizations */ + write_avail = sdio_write_avail(test_ch->ch); + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d, " + "read_avail=%d for chan %s\n", + test_ch->name, write_avail, read_avail, + test_ch->name); + if ((write_avail == 0) && (read_avail == 0)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->any_notify_count)); + atomic_set(&test_ch->any_notify_count, 0); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail > 0) { + size = min(packet_size, write_avail) ; + TEST_DBG(TEST_MODULE_NAME ":tx size = %d for chan %s\n", + size, test_ch->name); + test_ch->buf[0] = tx_packet_count; + test_ch->buf[(size/4)-1] = tx_packet_count; + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + goto exit_err; + } + tx_packet_count++; + test_ch->tx_bytes += size; + } + + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n", + test_ch->name, read_avail); + if (read_avail > 0) { + size = min(packet_size, read_avail); + pr_debug(TEST_MODULE_NAME ":rx size = %d.\n", size); + ret = sdio_read(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d" + " for chan %s\n", + size, -ret, test_ch->name); + goto exit_err; + } + rx_packet_count++; + test_ch->rx_bytes += size; + } + + TEST_DBG(TEST_MODULE_NAME + ":total rx bytes = %d , rx_packet#=%d" + " for chan %s\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + TEST_DBG(TEST_MODULE_NAME + ":total tx bytes = %d , tx_packet#=%d" + " for chan %s\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + } /* while (tx_packet_count < max_packets ) */ + + end_jiffy = get_jiffies_64(); /* read the current time */ + + delta_jiffies = end_jiffy - start_jiffy; + time_msec = jiffies_to_msecs(delta_jiffies); + + pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for" + " chan %s.\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d" + " for chan %s.\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + total_bytes = (test_ch->tx_bytes + test_ch->rx_bytes); + pr_err(TEST_MODULE_NAME ":total bytes = %d, time msec = %d" + " for chan %s\n", + total_bytes , (int) time_msec, test_ch->name); + + if (!test_ch->random_packet_size) { + if (time_msec) { + throughput = (total_bytes / time_msec) * 8 / 1000; + pr_err(TEST_MODULE_NAME ": %s - Performance = " + "%d Mbit/sec for chan %s\n", + __func__, throughput, test_ch->name); + } else { + pr_err(TEST_MODULE_NAME ": %s - time_msec = 0 Couldn't " + "calculate performence for chan %s\n", + __func__, test_ch->name); + } + + } + +#ifdef CONFIG_DEBUG_FS + switch (test_ch->ch_id) { + case SDIO_DUN: + test_ctx->debug.dun_throughput = throughput; + break; + case SDIO_RMNT: + test_ctx->debug.rmnt_throughput = throughput; + break; + default: + pr_err(TEST_MODULE_NAME "No debugfs for this channel " + "throughput"); + } +#endif + + pr_err(TEST_MODULE_NAME ": A2 PERFORMANCE TEST END for chan %s.\n", + test_ch->name); + + pr_err(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_err(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * rx_cleanup + * This function reads all the messages sent by the modem until + * the read_avail is 0 after 1 second of sleep. + * The function returns the number of packets that was received. + */ +static void rx_cleanup(struct test_channel *test_ch, int *rx_packet_count) +{ + int read_avail = 0; + int ret = 0; + int counter = 0; + + if (!test_ch || !test_ch->ch) { + pr_err(TEST_MODULE_NAME ":%s NULL channel\n", + __func__); + return; + } + + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n", + test_ch->name, read_avail); + + /* If no pending messages, wait to see if the modem sends data */ + if (read_avail == 0) { + msleep(1000); + read_avail = sdio_read_avail(test_ch->ch); + } + + while ((read_avail > 0) && (counter < 10)) { + TEST_DBG(TEST_MODULE_NAME ": read_avail=%d for ch %s\n", + read_avail, test_ch->name); + + ret = sdio_read(test_ch->ch, test_ch->buf, read_avail); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d for chan %s\n", + read_avail, -ret, test_ch->name); + break; + } + (*rx_packet_count)++; + test_ch->rx_bytes += read_avail; + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail == 0) { + msleep(1000); + counter++; + read_avail = sdio_read_avail(test_ch->ch); + } + } + pr_info(TEST_MODULE_NAME ": finished cleanup for ch %s, " + "rx_packet_count=%d, total rx bytes=%d\n", + test_ch->name, *rx_packet_count, test_ch->rx_bytes); +} + + +/** + * A2 RTT Test + * This function sends a packet and calculate the RTT time of + * this packet. + * The test also calculte Min, Max and Average RTT + */ +static void a2_rtt_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + u16 *buf16 = NULL; + int i; + int max_packets = 0; + u32 packet_size = 0; + s64 start_time, end_time; + int delta_usec = 0; + int time_average = 0; + int min_delta_usec = 0xFFFF; + int max_delta_usec = 0; + int total_time = 0; + int expected_read_size = 0; + int delay_ms = 0; + int slow_rtt_counter = 0; + int read_avail_so_far = 0; + + if (test_ch) { + /* + * Cleanup the pending RX data (such as loopback of the + * config msg) + */ + rx_cleanup(test_ch, &rx_packet_count); + rx_packet_count = 0; + } else { + return; + } + + max_packets = test_ch->config_msg.num_packets; + packet_size = test_ch->packet_length; + buf16 = (u16 *) test_ch->buf; + + for (i = 0; i < packet_size / 2; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME ": A2 RTT TEST START for chan %s\n", + test_ch->name); + + switch (test_ch->ch_id) { + case SDIO_RMNT: + delay_ms = 100; + break; + case SDIO_CSVT: + delay_ms = 0; + break; + default: + pr_err(TEST_MODULE_NAME ": %s - ch_id invalid.\n", + __func__); + return; + } + + while (tx_packet_count < max_packets) { + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + start_time = 0; + end_time = 0; + read_avail_so_far = 0; + + if (delay_ms) + msleep(delay_ms); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":ch %s: write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail == 0) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail > 0) { + TEST_DBG(TEST_MODULE_NAME ":tx size = %d for chan %s\n", + packet_size, test_ch->name); + test_ch->buf[0] = tx_packet_count; + + start_time = ktime_to_us(ktime_get()); + ret = sdio_write(test_ch->ch, test_ch->buf, + packet_size); + if (ret) { + pr_err(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + goto exit_err; + } + tx_packet_count++; + test_ch->tx_bytes += packet_size; + } else { + pr_err(TEST_MODULE_NAME ": Invalid write_avail" + " %d for chan %s\n", + write_avail, test_ch->name); + goto exit_err; + } + + expected_read_size = packet_size + A2_HEADER_OVERHEAD; + + while (read_avail_so_far < expected_read_size) { + + read_avail = sdio_read_avail(test_ch->ch); + + if (!read_avail) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch-> + rx_notify_count)); + + atomic_dec(&test_ch->rx_notify_count); + continue; + } + + read_avail_so_far += read_avail; + + if (read_avail_so_far > expected_read_size) { + pr_err(TEST_MODULE_NAME ": %s - Invalid " + "read_avail(%d) read_avail_so_far(%d) " + "can't be larger than " + "expected_read_size(%d).", + __func__, + read_avail, + read_avail_so_far, + expected_read_size); + goto exit_err; + } + + /* + * must read entire pending bytes, so later, we will + * get a notification when more data arrives + */ + ret = sdio_read(test_ch->ch, test_ch->buf, + read_avail); + + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d for chan %s\n", + read_avail, -ret, + test_ch->name); + goto exit_err; + } + } + + end_time = ktime_to_us(ktime_get()); + rx_packet_count++; + test_ch->rx_bytes += expected_read_size; + + delta_usec = (int)(end_time - start_time); + total_time += delta_usec; + if (delta_usec < min_delta_usec) + min_delta_usec = delta_usec; + if (delta_usec > max_delta_usec) + max_delta_usec = delta_usec; + + /* checking the RTT per channel criteria */ + if (delta_usec > MAX_AVG_RTT_TIME_USEC) { + pr_err(TEST_MODULE_NAME ": %s - " + "msg # %d - rtt time (%d usec) is " + "longer than %d usec\n", + __func__, + tx_packet_count, + delta_usec, + MAX_AVG_RTT_TIME_USEC); + slow_rtt_counter++; + } + + TEST_DBG(TEST_MODULE_NAME + ":RTT time=%d for packet #%d for chan %s\n", + delta_usec, tx_packet_count, test_ch->name); + } /* while (tx_packet_count < max_packets ) */ + + pr_info(TEST_MODULE_NAME ": %s - tx_packet_count = %d\n", + __func__, tx_packet_count); + + pr_info(TEST_MODULE_NAME ": %s - total rx bytes = 0x%x, " + "rx_packet# = %d for chan %s.\n", + __func__, test_ch->rx_bytes, rx_packet_count, test_ch->name); + + pr_info(TEST_MODULE_NAME ": %s - total tx bytes = 0x%x, " + "tx_packet# = %d for chan %s.\n", + __func__, test_ch->tx_bytes, tx_packet_count, test_ch->name); + + pr_info(TEST_MODULE_NAME ": %s - slow_rtt_counter = %d for " + "chan %s.\n", + __func__, slow_rtt_counter, test_ch->name); + + if (tx_packet_count) { + time_average = total_time / tx_packet_count; + pr_info(TEST_MODULE_NAME ":Average RTT time = %d for chan %s\n", + time_average, test_ch->name); + } else { + pr_err(TEST_MODULE_NAME ": %s - tx_packet_count=0. couldn't " + "calculate average rtt time", __func__); + } + + pr_info(TEST_MODULE_NAME ":MIN RTT time = %d for chan %s\n", + min_delta_usec, test_ch->name); + pr_info(TEST_MODULE_NAME ":MAX RTT time = %d for chan %s\n", + max_delta_usec, test_ch->name); + + pr_info(TEST_MODULE_NAME ": A2 RTT TEST END for chan %s.\n", + test_ch->name); + + if (ret) + goto exit_err; + + if (time_average == 0 || time_average > MAX_AVG_RTT_TIME_USEC) { + pr_err(TEST_MODULE_NAME ": %s - average_time = %d. Invalid " + "value", + __func__, time_average); + goto exit_err; + + } + + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_err(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * Process Rx Data - Helper for A2 Validation Test + * @test_ch(in/out) : Test channel that contains Rx data buffer to process. + * + * @rx_unprocessed_bytes(in) : Number of bytes to process in the buffer. + * + * @rx_process_packet_state(in/out) : + * Current processing state (used to identify what to process + * next in a partial packet) + * + * @rx_packet_size(in/out) : + * Number of bytes remaining in the packet to be processed. + * + * @rx_packet_count(in/out) : + * Number of packets processed. + */ +static int process_rx_data(struct test_channel *test_ch, + u32 rx_unprocessed_bytes, + int *rx_process_packet_state, + u16 *rx_packet_size, + int *rx_packet_count) +{ + u8 *buf = (u8 *)test_ch->buf; + int eop = 0; + int i = 0; + int ret = 0; + u32 *ptr = 0; + u16 size = 0; + + /* process rx data */ + while (rx_unprocessed_bytes) { + TEST_DBG(TEST_MODULE_NAME ": unprocessed bytes : %u\n", + rx_unprocessed_bytes); + + switch (*rx_process_packet_state) { + case RX_PROCESS_PACKET_INIT: + /* process the A2 header */ + TEST_DBG(TEST_MODULE_NAME ": " + "RX_PROCESS_PACKET_INIT\n"); + *rx_process_packet_state = RX_PROCESS_PACKET_INIT; + if (rx_unprocessed_bytes < 4) + break; + + i += 4; + rx_unprocessed_bytes -= 4; + + case RX_PROCESS_A2_HEADER: + /* process the rest of A2 header */ + TEST_DBG(TEST_MODULE_NAME ": RX_PROCESS_A2_HEADER\n"); + *rx_process_packet_state = RX_PROCESS_A2_HEADER; + if (rx_unprocessed_bytes < 4) + break; + + ptr = (u32 *)&buf[i]; + /* + * upper 2 bytes of the last 4 bytes of A2 header + * contains the size of the packet + */ + *rx_packet_size = *ptr >> 0x10; + + i += 4; + rx_unprocessed_bytes -= 4; + + case RX_PROCESS_PACKET_DATA: + /* process the2_2_ packet data */ + TEST_DBG(TEST_MODULE_NAME ": RX_PROCESS_PACKET_DATA " + "- packet size - %u\n", *rx_packet_size); + *rx_process_packet_state = RX_PROCESS_PACKET_DATA; + + size = *rx_packet_size; + if (*rx_packet_size <= rx_unprocessed_bytes) { + eop = *rx_packet_size; + *rx_packet_size = 0; + } else { + eop = rx_unprocessed_bytes; + *rx_packet_size = *rx_packet_size - + rx_unprocessed_bytes; + } + + /* no more bytes available to process */ + if (!eop) + break; + /* + * end of packet is starting from + * the current position + */ + eop = eop + i; + TEST_DBG(TEST_MODULE_NAME ": size - %u, " + "packet size - %u eop - %d\n", + size, *rx_packet_size, eop); + + /* validate the data */ + for (; i < eop; i++) { + if (buf[i] != (test_ch->rx_bytes % 256)) { + pr_err(TEST_MODULE_NAME ": " + "Corrupt data. buf:%u, " + "data:%u\n", buf[i], + test_ch->rx_bytes % 256); + ret = -EINVAL; + goto err; + } + rx_unprocessed_bytes--; + test_ch->rx_bytes++; + } + + /* have more data to be processed */ + if (*rx_packet_size) + break; + + /* + * A2 sends data in 4 byte alignment, + * skip the padding + */ + if (size % 4) { + i += 4 - (size % 4); + rx_unprocessed_bytes -= 4 - (size % 4); + } + *rx_packet_count = *rx_packet_count + 1; + + /* re init the state to process new packet */ + *rx_process_packet_state = RX_PROCESS_PACKET_INIT; + break; + default: + pr_err(TEST_MODULE_NAME ": Invalid case: %d\n", + *rx_process_packet_state); + ret = -EINVAL; + goto err; + } + TEST_DBG(TEST_MODULE_NAME ": Continue processing " + "if more data is available\n"); + } + +err: + return ret; +} + +/** + * A2 Validation Test + * Send packets and validate the returned packets. + * Transmit one packet at a time, while process multiple rx + * packets in a single transaction. + * A transaction is of size min(random number, write_avail). + * A packet consists of a min of 1 byte to channel supported max. + */ +static void a2_validation_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + int initial_rx_packet_count = 0; + u32 size = 0; + u8 *buf8 = (u8 *)test_ch->buf; + int i = 0; + int max_packets = test_ch->config_msg.num_packets; + u16 tx_packet_size = 0; + u16 rx_packet_size = 0; + u32 random_num = 0; + int rx_process_packet_state = RX_PROCESS_PACKET_INIT; + + pr_info(TEST_MODULE_NAME ": A2 VALIDATION TEST START for chan %s\n", + test_ch->name); + + /* Wait for the initial rx messages before starting the test. */ + rx_cleanup(test_ch, &initial_rx_packet_count); + + test_ch->tx_bytes = 0; + test_ch->rx_bytes = 0; + + /* Continue till we have transmitted and received all packets */ + while ((tx_packet_count < max_packets) || + (rx_packet_count < max_packets)) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + TEST_DBG(TEST_MODULE_NAME ": Random tx packet size =%u", size); + + /* + * wait for data ready event + * use a func to avoid compiler optimizations + */ + write_avail = sdio_write_avail(test_ch->ch); + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ": write_avail=%d, " + "read_avail=%d for chan %s\n", + write_avail, read_avail, test_ch->name); + + if ((write_avail == 0) && (read_avail == 0)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->any_notify_count)); + atomic_set(&test_ch->any_notify_count, 0); + } + + /* Transmit data */ + write_avail = sdio_write_avail(test_ch->ch); + if ((tx_packet_count < max_packets) && (write_avail > 0)) { + tx_packet_size = min(size, write_avail) ; + TEST_DBG(TEST_MODULE_NAME ": tx size = %u, " + "write_avail = %u tx_packet# = %d\n", + tx_packet_size, write_avail, + tx_packet_count); + memset(test_ch->buf, 0, test_ch->buf_size); + /* populate the buffer */ + for (i = 0; i < tx_packet_size; i++) { + buf8[i] = test_ch->tx_bytes % 256; + test_ch->tx_bytes++; + } + + ret = sdio_write(test_ch->ch, test_ch->buf, + tx_packet_size); + if (ret) { + pr_err(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + goto exit_err; + } + tx_packet_count++; + } + + /* Receive data */ + read_avail = sdio_read_avail(test_ch->ch); + if (read_avail > 0) { + TEST_DBG(TEST_MODULE_NAME ": rx size = %u, " + "rx_packet#=%d.\n", + read_avail, rx_packet_count); + memset(test_ch->buf, 0, test_ch->buf_size); + + ret = sdio_read(test_ch->ch, test_ch->buf, + read_avail); + if (ret) { + pr_err(TEST_MODULE_NAME ": sdio_read " + "size %d err=%d for chan %s\n", + size, -ret, test_ch->name); + goto exit_err; + } + + /* Process data */ + ret = process_rx_data(test_ch, read_avail, + &rx_process_packet_state, + &rx_packet_size, + &rx_packet_count); + + if (ret != 0) + goto exit_err; + } + TEST_DBG(TEST_MODULE_NAME ": Continue loop ...\n"); + } + + if (test_ch->tx_bytes != test_ch->rx_bytes) { + pr_err(TEST_MODULE_NAME ": Total number of bytes " + "transmitted (%u) does not match the total " + "number of bytes received (%u).", test_ch->tx_bytes, + test_ch->rx_bytes); + goto exit_err; + } + + pr_info(TEST_MODULE_NAME ": A2 VALIDATION TEST END for chan %s.\n", + test_ch->name); + + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +/** + * sender No loopback Test + */ +static void sender_no_loopback_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 write_avail = 0; + int packet_count = 0; + int size = 512; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int max_packet_count = 10000; + unsigned int random_num = 0; + + max_packet_count = test_ch->config_msg.num_packets; + + for (i = 0 ; i < size / 2 ; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME + ":SENDER NO LP TEST START for chan %s\n", test_ch->name); + + while (packet_count < max_packet_count) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + random_num = get_random_int(); + size = (random_num % test_ch->packet_length) + 1; + + TEST_DBG(TEST_MODULE_NAME ":SENDER WAIT FOR EVENT " + "for chan %s\n", + test_ch->name); + + /* wait for data ready event */ + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->tx_notify_count)); + atomic_dec(&test_ch->tx_notify_count); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail); + if (write_avail < size) { + pr_info(TEST_MODULE_NAME ":not enough write avail.\n"); + continue; + } + + test_ch->buf[0] = packet_count; + + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sender sdio_write err=%d.\n", + -ret); + goto exit_err; + } + + test_ch->tx_bytes += size; + packet_count++; + + TEST_DBG(TEST_MODULE_NAME + ":sender total tx bytes = 0x%x , packet#=%d, size=%d" + " for chan %s\n", + test_ch->tx_bytes, packet_count, size, test_ch->name); + + } /* end of while */ + + pr_info(TEST_MODULE_NAME + ":SENDER TEST END: total tx bytes = 0x%x, " + " for chan %s\n", + test_ch->tx_bytes, test_ch->name); + + test_ch->modem_result_per_chan = wait_for_result_msg(test_ch); + + if (test_ch->modem_result_per_chan) { + pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s.\n", + test_ch->name); + test_ch->test_result = TEST_PASSED; + } else { + pr_info(TEST_MODULE_NAME ": TEST FAILURE for chan %s.\n", + test_ch->name); + test_ch->test_result = TEST_FAILED; + } + test_ch->test_completed = 1; + check_test_completion(); + return; + +exit_err: + pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n", + test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + + +/** + * Modem reset Test + * The test verifies that it finished sending all the packets + * while there might be modem reset in the middle + */ +static void modem_reset_test(struct test_channel *test_ch) +{ + int ret = 0 ; + u32 read_avail = 0; + u32 write_avail = 0; + int tx_packet_count = 0; + int rx_packet_count = 0; + int size = 0; + u16 *buf16 = (u16 *) test_ch->buf; + int i; + int max_packets = 10000; + u32 packet_size = test_ch->buf_size; + int is_err = 0; + + max_packets = test_ch->config_msg.num_packets; + packet_size = test_ch->packet_length; + + for (i = 0; i < packet_size / 2; i++) + buf16[i] = (u16) (i & 0xFFFF); + + pr_info(TEST_MODULE_NAME ": Modem Reset TEST START for chan %s\n", + test_ch->name); + + while (tx_packet_count < max_packets) { + + if (test_ctx->exit_flag) { + pr_info(TEST_MODULE_NAME ":Exit Test.\n"); + return; + } + + if (test_ch->card_removed) { + pr_info(TEST_MODULE_NAME ": card removal was detected " + "for chan %s, tx_total=0x%x\n", + test_ch->name, test_ch->tx_bytes); + wait_event(test_ch->wait_q, + atomic_read(&test_ch->card_detected_event)); + atomic_set(&test_ch->card_detected_event, 0); + pr_info(TEST_MODULE_NAME ": card_detected_event " + "for chan %s\n", test_ch->name); + if (test_ch->card_removed) + continue; + is_err = 0; + /* Need to wait for the modem to be ready */ + msleep(5000); + pr_info(TEST_MODULE_NAME ": sending the config message " + "for chan %s\n", test_ch->name); + send_config_msg(test_ch); + } + + /* wait for data ready event */ + /* use a func to avoid compiler optimizations */ + write_avail = sdio_write_avail(test_ch->ch); + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d, " + "read_avail=%d for chan %s\n", + test_ch->name, write_avail, read_avail, + test_ch->name); + if ((write_avail == 0) && (read_avail == 0)) { + wait_event(test_ch->wait_q, + atomic_read(&test_ch->any_notify_count)); + atomic_set(&test_ch->any_notify_count, 0); + } + if (atomic_read(&test_ch->card_detected_event)) { + atomic_set(&test_ch->card_detected_event, 0); + pr_info(TEST_MODULE_NAME ": card_detected_event " + "for chan %s, tx_total=0x%x\n", + test_ch->name, test_ch->tx_bytes); + if (test_ch->card_removed) + continue; + /* Need to wait for the modem to be ready */ + msleep(5000); + is_err = 0; + pr_info(TEST_MODULE_NAME ": sending the config message " + "for chan %s\n", test_ch->name); + send_config_msg(test_ch); + } + + write_avail = sdio_write_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n", + test_ch->name, write_avail); + if (write_avail > 0) { + size = min(packet_size, write_avail) ; + pr_debug(TEST_MODULE_NAME ":tx size = %d for chan %s\n", + size, test_ch->name); + test_ch->buf[0] = tx_packet_count; + test_ch->buf[(size/4)-1] = tx_packet_count; + + TEST_DBG(TEST_MODULE_NAME ":channel %s, sdio_write, " + "size=%d\n", test_ch->name, size); + if (is_err) { + msleep(100); + continue; + } + ret = sdio_write(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ":sdio_write err=%d" + " for chan %s\n", + -ret, test_ch->name); + is_err = 1; + msleep(20); + continue; + } + tx_packet_count++; + test_ch->tx_bytes += size; + test_ch->config_msg.num_packets--; + } + + read_avail = sdio_read_avail(test_ch->ch); + TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n", + test_ch->name, read_avail); + if (read_avail > 0) { + size = min(packet_size, read_avail); + pr_debug(TEST_MODULE_NAME ":rx size = %d.\n", size); + TEST_DBG(TEST_MODULE_NAME ":channel %s, sdio_read, " + "size=%d\n", test_ch->name, size); + if (is_err) { + msleep(100); + continue; + } + ret = sdio_read(test_ch->ch, test_ch->buf, size); + if (ret) { + pr_info(TEST_MODULE_NAME ": sdio_read size %d " + " err=%d" + " for chan %s\n", + size, -ret, test_ch->name); + is_err = 1; + msleep(20); + continue; + } + rx_packet_count++; + test_ch->rx_bytes += size; + } + + TEST_DBG(TEST_MODULE_NAME + ":total rx bytes = %d , rx_packet#=%d" + " for chan %s\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + TEST_DBG(TEST_MODULE_NAME + ":total tx bytes = %d , tx_packet#=%d" + " for chan %s\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + udelay(500); + + } /* while (tx_packet_count < max_packets ) */ + + pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for" + " chan %s.\n", + test_ch->rx_bytes, rx_packet_count, test_ch->name); + pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d" + " for chan %s.\n", + test_ch->tx_bytes, tx_packet_count, test_ch->name); + + pr_err(TEST_MODULE_NAME ": Modem Reset TEST END for chan %s.\n", + test_ch->name); + + pr_err(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name); + test_ch->test_completed = 1; + test_ch->test_result = TEST_PASSED; + check_test_completion(); + return; +} + +/** + * Worker thread to handle the tests types + */ +static void worker(struct work_struct *work) +{ + struct test_channel *test_ch = NULL; + struct test_work *test_work = container_of(work, + struct test_work, + work); + int test_type = 0; + + test_ch = test_work->test_ch; + + if (test_ch == NULL) { + pr_err(TEST_MODULE_NAME ":NULL test_ch\n"); + return; + } + + test_type = test_ch->test_type; + + switch (test_type) { + case SDIO_TEST_LOOPBACK_HOST: + loopback_test(test_ch); + break; + case SDIO_TEST_LOOPBACK_CLIENT: + sender_test(test_ch); + break; + case SDIO_TEST_PERF: + a2_performance_test(test_ch); + break; + case SDIO_TEST_LPM_CLIENT_WAKER: + lpm_test(test_ch); + break; + case SDIO_TEST_LPM_HOST_WAKER: + lpm_test_host_waker(test_ch); + break; + case SDIO_TEST_HOST_SENDER_NO_LP: + sender_no_loopback_test(test_ch); + break; + case SDIO_TEST_LPM_RANDOM: + lpm_continuous_rand_test(test_ch); + break; + case SDIO_TEST_RTT: + a2_rtt_test(test_ch); + break; + case SDIO_TEST_CLOSE_CHANNEL: + if (test_ch->ch_id != SDIO_SMEM) + open_close_test(test_ch); + break; + case SDIO_TEST_MODEM_RESET: + modem_reset_test(test_ch); + break; + case SDIO_TEST_A2_VALIDATION: + a2_validation_test(test_ch); + break; + default: + pr_err(TEST_MODULE_NAME ":Bad Test type = %d.\n", + (int) test_type); + } +} + + +/** + * Notification Callback + * + * Notify the worker + * + */ +static void notify(void *priv, unsigned channel_event) +{ + struct test_channel *test_ch = (struct test_channel *) priv; + + pr_debug(TEST_MODULE_NAME ": %s - notify event=%d.\n", + __func__, channel_event); + + if (test_ch->ch == NULL) { + pr_info(TEST_MODULE_NAME ": %s - notify before ch ready.\n", + __func__); + return; + } + + switch (channel_event) { + case SDIO_EVENT_DATA_READ_AVAIL: + atomic_inc(&test_ch->rx_notify_count); + atomic_set(&test_ch->any_notify_count, 1); + TEST_DBG(TEST_MODULE_NAME ": %s - SDIO_EVENT_DATA_READ_AVAIL, " + "any_notify_count=%d, rx_notify_count=%d\n", + __func__, + atomic_read(&test_ch->any_notify_count), + atomic_read(&test_ch->rx_notify_count)); + /* + * when there is pending data on a channel we would like to + * turn on the bit mask that implies that there is pending + * data for that channel on that deivce + */ + if (test_ch->test_device != NULL && + test_ch->test_type == SDIO_TEST_LPM_RANDOM) { + spin_lock_irqsave(&test_ch->test_device->lpm_array_lock, + test_ch->test_device-> + lpm_array_lock_flags); + test_ch->test_device->read_avail_mask |= + test_ch->channel_mask_id; + test_ch->notify_counter_per_chan++; + + lpm_test_update_entry(test_ch, LPM_NOTIFY, "NOTIFY", 0); + spin_unlock_irqrestore(&test_ch->test_device-> + lpm_array_lock, + test_ch->test_device-> + lpm_array_lock_flags); + } + break; + + case SDIO_EVENT_DATA_WRITE_AVAIL: + atomic_inc(&test_ch->tx_notify_count); + atomic_set(&test_ch->any_notify_count, 1); + TEST_DBG(TEST_MODULE_NAME ": %s - SDIO_EVENT_DATA_WRITE_AVAIL, " + "any_notify_count=%d, tx_notify_count=%d\n", + __func__, + atomic_read(&test_ch->any_notify_count), + atomic_read(&test_ch->tx_notify_count)); + break; + + default: + BUG(); + } + wake_up(&test_ch->wait_q); + +} + +#ifdef CONFIG_MSM_SDIO_SMEM +static int sdio_smem_test_cb(int event) +{ + struct test_channel *tch = test_ctx->test_ch_arr[SDIO_SMEM]; + int i; + int *smem_buf = (int *)test_ctx->smem_buf; + uint32_t val = 0; + int ret = 0; + + pr_debug(TEST_MODULE_NAME ":%s: Received event %d\n", __func__, event); + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__); + return -EINVAL; + } + + switch (event) { + case SDIO_SMEM_EVENT_READ_DONE: + tch->rx_bytes += SMEM_MAX_XFER_SIZE; + for (i = 0; i < SMEM_MAX_XFER_SIZE;) { + val = (int)*smem_buf; + if ((val != test_ctx->smem_counter) && tch->is_used) { + pr_err(TEST_MODULE_NAME ":%s: Invalid value %d " + "expected %d in smem arr", + __func__, val, test_ctx->smem_counter); + pr_err(TEST_MODULE_NAME ":SMEM test FAILED\n"); + tch->test_completed = 1; + tch->test_result = TEST_FAILED; + check_test_completion(); + ret = -EINVAL; + goto exit; + } + i += 4; + smem_buf++; + test_ctx->smem_counter++; + } + if (tch->rx_bytes >= 40000000) { + if ((!tch->test_completed) && tch->is_used) { + pr_info(TEST_MODULE_NAME ":SMEM test PASSED\n"); + tch->test_completed = 1; + tch->test_result = TEST_PASSED; + check_test_completion(); + } + } + break; + case SDIO_SMEM_EVENT_READ_ERR: + if (tch->is_used) { + pr_err(TEST_MODULE_NAME ":Read overflow, " + "SMEM test FAILED\n"); + tch->test_completed = 1; + tch->test_result = TEST_FAILED; + ret = -EIO; + } + break; + default: + if (tch->is_used) { + pr_err(TEST_MODULE_NAME ":Unhandled event %d\n", event); + ret = -EINVAL; + } + break; + } +exit: + return ret; +} + +static int sdio_smem_open(struct sdio_smem_client *sdio_smem) +{ + int ret = 0; + + if (!sdio_smem) { + pr_info(TEST_MODULE_NAME "%s: NULL sdio_smem_client\n", + __func__); + return -EINVAL; + } + + if (test_ctx->test_ch_arr[SDIO_SMEM]->ch_ready) { + pr_info(TEST_MODULE_NAME "%s: SDIO_SMEM channel is already opened\n", + __func__); + return 0; + } + + test_ctx->test_ch_arr[SDIO_SMEM]->ch_ready = 1; + sdio_smem->buf = test_ctx->smem_buf; + sdio_smem->size = SMEM_MAX_XFER_SIZE; + sdio_smem->cb_func = sdio_smem_test_cb; + ret = sdio_smem_register_client(); + if (ret) + pr_info(TEST_MODULE_NAME "%s: Error (%d) registering sdio_smem " + "test client\n", + __func__, ret); + + return ret; +} + +static int sdio_smem_test_probe(struct platform_device *pdev) +{ + test_ctx->sdio_smem = container_of(pdev, struct sdio_smem_client, + plat_dev); + + return sdio_smem_open(test_ctx->sdio_smem); +} + +static struct platform_driver sdio_smem_client_drv = { + .probe = sdio_smem_test_probe, + .driver = { + .name = "SDIO_SMEM_CLIENT", + .owner = THIS_MODULE, + }, +}; +#endif + +static void sdio_test_lpm_timeout_handler(unsigned long data) +{ + struct test_channel *tch = (struct test_channel *)data; + + pr_info(TEST_MODULE_NAME ": %s - LPM TEST TIMEOUT Expired after " + "%d ms\n", __func__, tch->timeout_ms); + tch->test_completed = 1; + pr_info(TEST_MODULE_NAME ": %s - tch->test_result = TEST_FAILED\n", + __func__); + tch->test_completed = 1; + tch->test_result = TEST_FAILED; + check_test_completion(); + return; +} + +static void sdio_test_lpm_timer_handler(unsigned long data) +{ + struct test_channel *tch = (struct test_channel *)data; + + pr_info(TEST_MODULE_NAME ": %s - LPM TEST Timer Expired after " + "%d ms\n", __func__, tch->timer_interval_ms); + + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s - LPM TEST FAILED. " + "tch is NULL\n", __func__); + return; + } + + if (!tch->ch) { + pr_err(TEST_MODULE_NAME ": %s - LPM TEST FAILED. tch->ch " + "is NULL\n", __func__); + tch->test_result = TEST_FAILED; + return; + } + + /* Verfiy that we voted for sleep */ + if (tch->is_ok_to_sleep) { + tch->test_result = TEST_PASSED; + pr_info(TEST_MODULE_NAME ": %s - 8K voted for sleep\n", + __func__); + } else { + tch->test_result = TEST_FAILED; + pr_info(TEST_MODULE_NAME ": %s - 8K voted against sleep\n", + __func__); + + } + + sdio_al_unregister_lpm_cb(tch->sdio_al_device); + + if (tch->test_type == SDIO_TEST_LPM_HOST_WAKER) { + atomic_set(&tch->wakeup_client, 1); + wake_up(&tch->wait_q); + } +} + +int sdio_test_wakeup_callback(void *device_handle, int is_vote_for_sleep) +{ + int i = 0; + + TEST_DBG(TEST_MODULE_NAME ": %s is_vote_for_sleep=%d!!!", + __func__, is_vote_for_sleep); + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + if (tch->sdio_al_device == device_handle) { + tch->is_ok_to_sleep = is_vote_for_sleep; + + if (tch->test_type == SDIO_TEST_LPM_RANDOM) { + spin_lock_irqsave(&tch->test_device-> + lpm_array_lock, + tch->test_device-> + lpm_array_lock_flags); + if (is_vote_for_sleep == 1) + lpm_test_update_entry(tch, + LPM_SLEEP, + "SLEEP ", 0); + else + lpm_test_update_entry(tch, + LPM_WAKEUP, + "WAKEUP", 0); + + spin_unlock_irqrestore(&tch->test_device-> + lpm_array_lock, + tch->test_device-> + lpm_array_lock_flags); + break; + } + } + } + + return 0; +} + +static int sdio_test_find_dev(struct test_channel *tch) +{ + int j; + int null_index = -1; + + for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES; ++j) { + + struct sdio_test_device *test_dev = + &test_ctx->test_dev_arr[j]; + + if (test_dev->sdio_al_device == NULL) { + if (null_index == -1) + null_index = j; + continue; + } + + if (test_dev->sdio_al_device == + tch->ch->sdio_al_dev) { + test_dev->open_channels_counter_to_recv++; + test_dev->open_channels_counter_to_send++; + tch->test_device = test_dev; + /* setting mask id for pending data for + this channel */ + tch->channel_mask_id = test_dev->next_mask_id; + test_dev->next_mask_id *= 2; + pr_info(TEST_MODULE_NAME ": %s - channel %s " + "got read_mask_id = 0x%x. device " + "next_mask_id=0x%x", + __func__, tch->name, tch->channel_mask_id, + test_dev->next_mask_id); + break; + } + } + + /* + * happens ones a new device is "discovered" while testing. i.e + * if testing a few channels, a new deivce will be "discovered" once + * the first channel of a device is being tested + */ + if (j == MAX_NUM_OF_SDIO_DEVICES) { + + struct sdio_test_device *test_dev = + &test_ctx-> + test_dev_arr[null_index]; + test_dev->sdio_al_device = + tch->ch->sdio_al_dev; + + test_ctx->number_of_active_devices++; + test_ctx->max_number_of_devices++; + test_dev->open_channels_counter_to_recv++; + test_dev->open_channels_counter_to_send++; + test_dev->next_avail_entry_in_array = 0; + tch->test_device = test_dev; + tch->test_device->array_size = + LPM_ARRAY_SIZE; + test_dev->modem_result_per_dev = 1; + tch->modem_result_per_chan = 0; + test_dev->next_avail_entry_in_array = 0; + + spin_lock_init(&test_dev-> + lpm_array_lock); + + if (tch->test_type == SDIO_TEST_LPM_RANDOM) { + pr_err(MODULE_NAME ": %s - " + "Allocating Msg Array for " + "Maximum open channels for device (%d) " + "Channels. Array has %d entries", + __func__, + LPM_MAX_OPEN_CHAN_PER_DEV, + test_dev->array_size); + + test_dev->lpm_arr = + kzalloc(sizeof( + struct lpm_entry_type) * + tch-> + test_device->array_size, + GFP_KERNEL); + + if (!test_dev->lpm_arr) { + pr_err(MODULE_NAME ": %s - " + "lpm_arr is NULL", + __func__); + return -ENOMEM; + } + } + + /* + * in new device, initialize next_mask_id, and setting + * mask_id to the channel + */ + test_dev->next_mask_id = 0x1; + tch->channel_mask_id = test_dev->next_mask_id; + test_dev->next_mask_id *= 2; + pr_info(TEST_MODULE_NAME ": %s - channel %s got " + "read_mask_id = 0x%x. device next_mask_id=0x%x", + __func__, + tch->name, + tch->channel_mask_id, + test_dev->next_mask_id); + } + + return 0; +} + +static void check_test_result(void) +{ + int result = 1; + int i = 0; + + test_ctx->max_number_of_devices = 0; + + pr_info(TEST_MODULE_NAME ": %s - Woke Up\n", __func__); + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + + if (tch->test_type == SDIO_TEST_LPM_RANDOM) + result &= tch->test_device->final_result_per_dev; + else + if (tch->test_result == TEST_FAILED) { + pr_info(TEST_MODULE_NAME ": %s - " + "Test FAILED\n", __func__); + test_ctx->test_result = TEST_FAILED; + pr_err(TEST_MODULE_NAME ": %s - " + "test_result %d", + __func__, test_ctx->test_result); + return; + } + } + + if (result == 0) { + pr_info(TEST_MODULE_NAME ": %s - Test FAILED\n", __func__); + test_ctx->test_result = TEST_FAILED; + pr_err(TEST_MODULE_NAME ": %s - " + "test_result %d", + __func__, test_ctx->test_result); + return; + } + + pr_info(TEST_MODULE_NAME ": %s - Test PASSED", __func__); + test_ctx->test_result = TEST_PASSED; + pr_err(TEST_MODULE_NAME ": %s - " + "test_result %d", + __func__, test_ctx->test_result); + return; +} + +/** + * Test Main + */ +static int test_start(void) +{ + int ret = -ENOMEM; + int i; + + pr_debug(TEST_MODULE_NAME ":Starting Test ....\n"); + + test_ctx->test_completed = 0; + test_ctx->test_result = TEST_NO_RESULT; + test_ctx->debug.dun_throughput = 0; + test_ctx->debug.rmnt_throughput = 0; + test_ctx->number_of_active_devices = 0; + + pr_err(TEST_MODULE_NAME ": %s - test_result %d", + __func__, test_ctx->test_result); + + memset(test_ctx->test_dev_arr, 0, + sizeof(struct sdio_test_device)*MAX_NUM_OF_SDIO_DEVICES); + + /* Open The Channels */ + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used)) + continue; + + tch->rx_bytes = 0; + tch->tx_bytes = 0; + + atomic_set(&tch->tx_notify_count, 0); + atomic_set(&tch->rx_notify_count, 0); + atomic_set(&tch->any_notify_count, 0); + atomic_set(&tch->wakeup_client, 0); + + /* in case there are values left from previous tests */ + tch->notify_counter_per_chan = 0; + tch->next_index_in_sent_msg_per_chan = 0; + + memset(tch->buf, 0x00, tch->buf_size); + tch->test_result = TEST_NO_RESULT; + + tch->test_completed = 0; + + ret = open_sdio_ch(tch); + if (ret) + continue; + + if (tch->ch_id != SDIO_SMEM) { + ret = sdio_test_find_dev(tch); + + if (ret) { + pr_err(TEST_MODULE_NAME ": %s - " + "sdio_test_find_dev() returned with " + "error", __func__); + return -ENODEV; + } + + tch->sdio_al_device = tch->ch->sdio_al_dev; + } + + if ((tch->test_type == SDIO_TEST_LPM_HOST_WAKER) || + (tch->test_type == SDIO_TEST_LPM_CLIENT_WAKER) || + (tch->test_type == SDIO_TEST_LPM_RANDOM)) + sdio_al_register_lpm_cb(tch->sdio_al_device, + sdio_test_wakeup_callback); + } + + /* + * make some space between opening the channels and sending the + * config messages + */ + msleep(100); + + /* + * try to delay send_config_msg of all channels to after the point + * when we open them all + */ + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used)) + continue; + + if ((tch->ch_ready) && (tch->ch_id != SDIO_SMEM)) + send_config_msg(tch); + + if ((tch->test_type == SDIO_TEST_LPM_HOST_WAKER) || + (tch->test_type == SDIO_TEST_LPM_CLIENT_WAKER) || + (tch->test_type == SDIO_TEST_LPM_RANDOM)) { + if (tch->timer_interval_ms > 0) { + pr_info(TEST_MODULE_NAME ": %s - init timer, " + "ms=%d\n", + __func__, tch->timer_interval_ms); + init_timer(&tch->timer); + tch->timer.data = (unsigned long)tch; + tch->timer.function = + sdio_test_lpm_timer_handler; + tch->timer.expires = jiffies + + msecs_to_jiffies(tch->timer_interval_ms); + add_timer(&tch->timer); + } + } + } + + pr_debug(TEST_MODULE_NAME ":queue_work..\n"); + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used) || (!tch->ch_ready)) + continue; + + if (tch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + if (tch->test_type == SDIO_TEST_CLOSE_CHANNEL) + open_close_smem_test(tch); +#endif + } else { + queue_work(tch->workqueue, &tch->test_work.work); + } + + } + + pr_info(TEST_MODULE_NAME ": %s - Waiting for the test completion\n", + __func__); + + wait_event(test_ctx->wait_q, test_ctx->test_completed); + check_test_result(); + + /* + * Close the channels and zero the is_used flag so that if the modem + * will be reset after the test completion we won't re-open + * the channels + */ + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + + if ((!tch) || (!tch->is_used)) + continue; + if (!tch->ch_ready) { + tch->is_used = 0; + continue; + } + + close_sdio_ch(tch); + tch->is_used = 0; + } + + if (test_ctx->test_result == TEST_PASSED) + return 0; + else + return -EINVAL; +} + +static int set_params_loopback_9k(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_LOOPBACK_CLIENT; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->config_msg.num_packets = 10000; + tch->config_msg.num_iterations = 1; + + tch->packet_length = 512; + if (tch->ch_id == SDIO_RPC) + tch->packet_length = 128; + tch->timer_interval_ms = 0; + + return 0; +} +static int set_params_loopback_9k_close(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_CLOSE_CHANNEL; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->config_msg.num_packets = 5000; + tch->config_msg.num_iterations = 1; + tch->max_burst_size = 10; + switch (tch->ch_id) { + case SDIO_DUN: + case SDIO_RPC: + tch->packet_length = 128; /* max is 2K*/ + break; + case SDIO_DIAG: + case SDIO_RMNT: + default: + tch->packet_length = 512; /* max is 4k */ + } + tch->timer_interval_ms = 0; + return 0; +} +static int set_params_a2_perf(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_PERF; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + + switch (tch->ch_id) { + case SDIO_DIAG: + tch->packet_length = 512; + break; + case SDIO_DUN: + tch->packet_length = DUN_PACKET_SIZE; + break; + case SDIO_CSVT: + tch->packet_length = CSVT_PACKET_SIZE; + break; + default: + tch->packet_length = MAX_XFER_SIZE; + break; + } + + pr_info(TEST_MODULE_NAME ": %s: packet_length=%d", __func__, + tch->packet_length); + + tch->config_msg.num_packets = 10000; + tch->config_msg.num_iterations = 1; + tch->random_packet_size = 0; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_rtt(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_RTT; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + + switch (tch->ch_id) { + case SDIO_RMNT: + tch->packet_length = SDIO_RMNT_RTT_PACKET_SIZE; + break; + case SDIO_CSVT: + tch->packet_length = SDIO_CSVT_RTT_PACKET_SIZE; + break; + default: + pr_err(TEST_MODULE_NAME ": %s - ch_id invalid.\n", __func__); + return -EINVAL; + } + + pr_info(TEST_MODULE_NAME ": %s: packet_length=%d", __func__, + tch->packet_length); + + tch->config_msg.num_packets = 200; + tch->config_msg.num_iterations = 1; + tch->random_packet_size = 0; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_a2_small_pkts(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_PERF; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->packet_length = 128; + + tch->config_msg.num_packets = 1000000; + tch->config_msg.num_iterations = 1; + tch->random_packet_size = 1; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_modem_reset(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_MODEM_RESET; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + tch->packet_length = 512; + if (tch->ch_id == SDIO_RPC) + tch->packet_length = 128; + else if ((tch->ch_id == SDIO_RMNT) || (tch->ch_id == SDIO_DUN)) + tch->packet_length = MAX_XFER_SIZE; + + tch->config_msg.num_packets = 50000; + tch->config_msg.num_iterations = 1; + + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_a2_validation(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_A2_VALIDATION; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT; + + if (tch->ch_id == SDIO_RMNT) + tch->packet_length = RMNT_PACKET_SIZE; + else if (tch->ch_id == SDIO_DUN) + tch->packet_length = DUN_PACKET_SIZE; + else + tch->packet_length = MAX_XFER_SIZE; + + tch->config_msg.num_packets = 10000; + tch->config_msg.num_iterations = 1; + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_smem_test(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->timer_interval_ms = 0; + + return 0; +} + +static int set_params_lpm_test(struct test_channel *tch, + enum sdio_test_case_type test, + int timer_interval_ms) +{ + static int first_time = 1; + if (!tch) { + pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__); + return -EINVAL; + } + + tch->is_used = 1; + tch->test_type = test; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = test; + tch->config_msg.num_packets = LPM_TEST_NUM_OF_PACKETS; + tch->config_msg.num_iterations = 1; + tch->timer_interval_ms = timer_interval_ms; + tch->timeout_ms = 10000; + + tch->packet_length = 0; + if (test != SDIO_TEST_LPM_RANDOM) { + init_timer(&tch->timeout_timer); + tch->timeout_timer.data = (unsigned long)tch; + tch->timeout_timer.function = sdio_test_lpm_timeout_handler; + tch->timeout_timer.expires = jiffies + + msecs_to_jiffies(tch->timeout_ms); + add_timer(&tch->timeout_timer); + pr_info(TEST_MODULE_NAME ": %s - Initiated LPM TIMEOUT TIMER." + "set to %d ms\n", + __func__, tch->timeout_ms); + } + + if (first_time) { + pr_info(TEST_MODULE_NAME ": %s - wake_lock_init() called\n", + __func__); + wake_lock_init(&test_ctx->wake_lock, + WAKE_LOCK_SUSPEND, TEST_MODULE_NAME); + first_time = 0; + } + + pr_info(TEST_MODULE_NAME ": %s - wake_lock() for the TEST is " + "called channel %s. to prevent real sleeping\n", + __func__, tch->name); + wake_lock(&test_ctx->wake_lock); + + return 0; +} + +static int set_params_8k_sender_no_lp(struct test_channel *tch) +{ + if (!tch) { + pr_err(TEST_MODULE_NAME ":NULL channel\n"); + return -EINVAL; + } + tch->is_used = 1; + tch->test_type = SDIO_TEST_HOST_SENDER_NO_LP; + tch->config_msg.signature = TEST_CONFIG_SIGNATURE; + tch->config_msg.test_case = SDIO_TEST_HOST_SENDER_NO_LP; + tch->config_msg.num_packets = 1000; + tch->config_msg.num_iterations = 1; + + tch->packet_length = 512; + if (tch->ch_id == SDIO_RPC) + tch->packet_length = 128; + tch->timer_interval_ms = 0; + + return 0; +} + +static void set_pseudo_random_seed(void) +{ + /* Set the seed accoring to the kernel command parameters if any or + get a random value */ + if (seed != 0) { + test_ctx->lpm_pseudo_random_seed = seed; + } else { + test_ctx->lpm_pseudo_random_seed = + (unsigned int)(get_jiffies_64() & 0xFFFF); + test_ctx->lpm_pseudo_random_seed = + pseudo_random_seed(&test_ctx->lpm_pseudo_random_seed); + } + + pr_info(TEST_MODULE_NAME ":%s: seed is %u", + __func__, test_ctx->lpm_pseudo_random_seed); +} + +/* + for each channel + 1. open channel + 2. close channel +*/ +static int close_channel_lpm_test(int channel_num) +{ + int ret = 0; + struct test_channel *tch = NULL; + tch = test_ctx->test_ch_arr[channel_num]; + + if (!tch) { + pr_info(TEST_MODULE_NAME ":%s ch#%d is NULL\n", + __func__, channel_num); + return 0; + } + + ret = open_sdio_ch(tch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s open channel %s" + " failed\n", __func__, tch->name); + return ret; + } else { + pr_info(TEST_MODULE_NAME":%s open channel %s" + " success\n", __func__, tch->name); + } + ret = close_sdio_ch(tch); + if (ret) { + pr_err(TEST_MODULE_NAME":%s close channel %s" + " failed\n", __func__, tch->name); + return ret; + } else { + pr_info(TEST_MODULE_NAME":%s close channel %s" + " success\n", __func__, tch->name); + } + + tch->is_used = 0; + + return ret; +} + +/** + * Write File. + * + * @note Trigger the test from user space by: + * echo 1 > /dev/sdio_al_test + * + */ +ssize_t test_write(struct file *filp, const char __user *buf, size_t size, + loff_t *f_pos) +{ + sdio_al_test_initial_dev_and_chan(test_ctx); + + if (strict_strtol(buf, 10, &test_ctx->testcase)) + return -EINVAL; + + switch (test_ctx->testcase) { + case 98: + pr_info(TEST_MODULE_NAME " set runtime debug on"); + test_ctx->runtime_debug = 1; + return size; + case 99: + pr_info(TEST_MODULE_NAME " set runtime debug off"); + test_ctx->runtime_debug = 0; + return size; + default: + pr_info(TEST_MODULE_NAME ":Bad Test number = %d.\n", + (int)test_ctx->testcase); + return size; + } + + return size; +} + +/** + * Test Channel Init. + */ +int test_channel_init(char *name) +{ + struct test_channel *test_ch; + int ch_id = 0; + int ret; + + pr_debug(TEST_MODULE_NAME ":%s.\n", __func__); + pr_info(TEST_MODULE_NAME ": init test channel %s.\n", name); + + ch_id = channel_name_to_id(name); + pr_debug(TEST_MODULE_NAME ":id = %d.\n", ch_id); + if (test_ctx->test_ch_arr[ch_id] == NULL) { + test_ch = kzalloc(sizeof(*test_ch), GFP_KERNEL); + if (test_ch == NULL) { + pr_err(TEST_MODULE_NAME ":kzalloc err for allocating " + "test_ch %s.\n", + name); + return -ENOMEM; + } + test_ctx->test_ch_arr[ch_id] = test_ch; + + test_ch->ch_id = ch_id; + + strncpy(test_ch->name, name, + strnlen(name, TEST_CH_NAME_SIZE)-SDIO_TEST_POSTFIX_SIZE); + + test_ch->buf_size = MAX_XFER_SIZE; + + test_ch->buf = kzalloc(test_ch->buf_size, GFP_KERNEL); + if (test_ch->buf == NULL) { + kfree(test_ch); + test_ctx->test_ch = NULL; + return -ENOMEM; + } + + if (test_ch->ch_id == SDIO_SMEM) { + test_ctx->smem_buf = kzalloc(SMEM_MAX_XFER_SIZE, + GFP_KERNEL); + if (test_ctx->smem_buf == NULL) { + pr_err(TEST_MODULE_NAME ":%s: Unable to " + "allocate smem buf\n", + __func__); + kfree(test_ch); + test_ctx->test_ch = NULL; + return -ENOMEM; + } + +#ifdef CONFIG_MSM_SDIO_SMEM + ret = platform_driver_register(&sdio_smem_client_drv); + if (ret) { + pr_err(TEST_MODULE_NAME ":%s: Unable to " + "register sdio smem " + "test client\n", + __func__); + return ret; + } +#endif + } else { + test_ch->workqueue = + create_singlethread_workqueue(test_ch->name); + test_ch->test_work.test_ch = test_ch; + INIT_WORK(&test_ch->test_work.work, worker); + + init_waitqueue_head(&test_ch->wait_q); + } + } else { + test_ch = test_ctx->test_ch_arr[ch_id]; + pr_info(TEST_MODULE_NAME ":%s: ch %s was detected again\n", + __func__, test_ch->name); + test_ch->card_removed = 0; + if ((test_ch->is_used) && + (test_ch->test_type == SDIO_TEST_MODEM_RESET)) { + if (test_ch->ch_id == SDIO_SMEM) { +#ifdef CONFIG_MSM_SDIO_SMEM + ret = add_sdio_smem(); + if (ret) { + test_ch->ch_ready = false; + return 0; + } +#endif + } else { + ret = open_sdio_ch(test_ch); + if (ret) { + pr_info(TEST_MODULE_NAME + ":%s: open channel %s failed\n", + __func__, test_ch->name); + return 0; + } + ret = sdio_test_find_dev(test_ch); + + if (ret) { + pr_err(TEST_MODULE_NAME ": %s - " + "sdio_test_find_dev() returned " + "with error", __func__); + return -ENODEV; + } + + test_ch->sdio_al_device = + test_ch->ch->sdio_al_dev; + } + atomic_set(&test_ch->card_detected_event, 1); + wake_up(&test_ch->wait_q); + } + } + + return 0; +} + +static int sdio_test_channel_probe(struct platform_device *pdev) +{ + if (!pdev) + return -EIO; + return test_channel_init((char *)pdev->name); +} + +static int sdio_test_channel_remove(struct platform_device *pdev) +{ + int ch_id; + + if (!pdev) + return -EIO; + + ch_id = channel_name_to_id((char *)pdev->name); + if (test_ctx->test_ch_arr[ch_id] == NULL) + return 0; + + pr_info(TEST_MODULE_NAME "%s: remove ch %s\n", + __func__, test_ctx->test_ch_arr[ch_id]->name); + + if ((ch_id == SDIO_SMEM) && (test_ctx->smem_pdev)) { + platform_device_unregister(test_ctx->smem_pdev); + test_ctx->smem_pdev = NULL; + } + + test_ctx->test_ch_arr[ch_id]->ch_ready = 0; + test_ctx->test_ch_arr[ch_id]->card_removed = 1; + + return 0; + +} + +static int sdio_test_channel_csvt_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (!pdev) + return -ENODEV; + + test_ctx->csvt_app_pdev = platform_device_alloc("SDIO_CSVT_TEST_APP", + -1); + ret = platform_device_add(test_ctx->csvt_app_pdev); + if (ret) { + pr_err(MODULE_NAME ":platform_device_add failed, " + "ret=%d\n", ret); + return ret; + } + + return sdio_test_channel_probe(pdev); +} + +static int sdio_test_channel_csvt_remove(struct platform_device *pdev) +{ + if (!pdev) + return -ENODEV; + + platform_device_unregister(test_ctx->csvt_app_pdev); + + return sdio_test_channel_remove(pdev); +} + +static struct platform_driver sdio_rpc_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_RPC_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_qmi_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_QMI_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_diag_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_DIAG_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_smem_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_SMEM_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_rmnt_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_RMNT_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_dun_drv = { + .probe = sdio_test_channel_probe, + .remove = sdio_test_channel_remove, + .driver = { + .name = "SDIO_DUN_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sdio_csvt_drv = { + .probe = sdio_test_channel_csvt_probe, + .remove = sdio_test_channel_csvt_remove, + .driver = { + .name = "SDIO_CSVT_TEST", + .owner = THIS_MODULE, + }, +}; + +static struct class *test_class; + +const struct file_operations test_fops = { + .owner = THIS_MODULE, + .write = test_write, +}; + +/** + * Module Init. + */ +static int __init test_init(void) +{ + int ret; + + pr_debug(TEST_MODULE_NAME ":test_init.\n"); + + test_ctx = kzalloc(sizeof(struct test_context), GFP_KERNEL); + + if (test_ctx == NULL) { + pr_err(TEST_MODULE_NAME ":kzalloc err.\n"); + return -ENOMEM; + } + test_ctx->test_ch = NULL; + test_ctx->signature = TEST_SIGNATURE; + + test_ctx->name = "UNKNOWN"; + + init_waitqueue_head(&test_ctx->wait_q); + +#ifdef CONFIG_DEBUG_FS + sdio_al_test_debugfs_init(); +#endif + + test_class = class_create(THIS_MODULE, TEST_MODULE_NAME); + + ret = alloc_chrdev_region(&test_ctx->dev_num, 0, 1, TEST_MODULE_NAME); + if (ret) { + pr_err(TEST_MODULE_NAME "alloc_chrdev_region err.\n"); + return -ENODEV; + } + + test_ctx->dev = device_create(test_class, NULL, test_ctx->dev_num, + test_ctx, TEST_MODULE_NAME); + if (IS_ERR(test_ctx->dev)) { + pr_err(TEST_MODULE_NAME ":device_create err.\n"); + return -ENODEV; + } + + test_ctx->cdev = cdev_alloc(); + if (test_ctx->cdev == NULL) { + pr_err(TEST_MODULE_NAME ":cdev_alloc err.\n"); + return -ENODEV; + } + cdev_init(test_ctx->cdev, &test_fops); + test_ctx->cdev->owner = THIS_MODULE; + + ret = cdev_add(test_ctx->cdev, test_ctx->dev_num, 1); + if (ret) + pr_err(TEST_MODULE_NAME ":cdev_add err=%d\n", -ret); + else + pr_debug(TEST_MODULE_NAME ":SDIO-AL-Test init OK..\n"); + + platform_driver_register(&sdio_rpc_drv); + platform_driver_register(&sdio_qmi_drv); + platform_driver_register(&sdio_diag_drv); + platform_driver_register(&sdio_smem_drv); + platform_driver_register(&sdio_rmnt_drv); + platform_driver_register(&sdio_dun_drv); + platform_driver_register(&sdio_csvt_drv); + + return ret; +} + +/** + * Module Exit. + */ +static void __exit test_exit(void) +{ + int i; + + pr_debug(TEST_MODULE_NAME ":test_exit.\n"); + + test_ctx->exit_flag = true; + + msleep(100); /* allow gracefully exit of the worker thread */ + + cdev_del(test_ctx->cdev); + device_destroy(test_class, test_ctx->dev_num); + unregister_chrdev_region(test_ctx->dev_num, 1); + + platform_driver_unregister(&sdio_rpc_drv); + platform_driver_unregister(&sdio_qmi_drv); + platform_driver_unregister(&sdio_diag_drv); + platform_driver_unregister(&sdio_smem_drv); + platform_driver_unregister(&sdio_rmnt_drv); + platform_driver_unregister(&sdio_dun_drv); + platform_driver_unregister(&sdio_csvt_drv); + + for (i = 0; i < SDIO_MAX_CHANNELS; i++) { + struct test_channel *tch = test_ctx->test_ch_arr[i]; + if (!tch) + continue; + kfree(tch->buf); + kfree(tch); + } + +#ifdef CONFIG_DEBUG_FS + sdio_al_test_debugfs_cleanup(); +#endif + + kfree(test_ctx); + + pr_debug(TEST_MODULE_NAME ":test_exit complete.\n"); +} + +module_init(test_init); +module_exit(test_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SDIO_AL Test"); +MODULE_AUTHOR("Amir Samuelov "); + + diff --git a/arch/arm/mach-msm/sdio_cmux.c b/arch/arm/mach-msm/sdio_cmux.c new file mode 100644 index 00000000000..d04a0b03b75 --- /dev/null +++ b/arch/arm/mach-msm/sdio_cmux.c @@ -0,0 +1,885 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "modem_notifier.h" + +#define MAX_WRITE_RETRY 5 +#define MAGIC_NO_V1 0x33FC + +static int msm_sdio_cmux_debug_mask; +module_param_named(debug_mask, msm_sdio_cmux_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +enum cmd_type { + DATA = 0, + OPEN, + CLOSE, + STATUS, + NUM_CMDS +}; + +#define DSR_POS 0x1 +#define CTS_POS 0x2 +#define RI_POS 0x4 +#define CD_POS 0x8 + +struct sdio_cmux_ch { + int lc_id; + + struct mutex lc_lock; + wait_queue_head_t open_wait_queue; + int is_remote_open; + int is_local_open; + int is_channel_reset; + + char local_status; + char remote_status; + + struct mutex tx_lock; + struct list_head tx_list; + + void *priv; + void (*receive_cb)(void *, int, void *); + void (*write_done)(void *, int, void *); + void (*status_callback)(int, void *); +} logical_ch[SDIO_CMUX_NUM_CHANNELS]; + +struct sdio_cmux_hdr { + uint16_t magic_no; + uint8_t status; /* This field is reserved for commands other + * than STATUS */ + uint8_t cmd; + uint8_t pad_bytes; + uint8_t lc_id; + uint16_t pkt_len; +}; + +struct sdio_cmux_pkt { + struct sdio_cmux_hdr *hdr; + void *data; +}; + +struct sdio_cmux_list_elem { + struct list_head list; + struct sdio_cmux_pkt cmux_pkt; +}; + +#define logical_ch_is_local_open(x) \ + (logical_ch[(x)].is_local_open) + +#define logical_ch_is_remote_open(x) \ + (logical_ch[(x)].is_remote_open) + +static void sdio_cdemux_fn(struct work_struct *work); +static DECLARE_WORK(sdio_cdemux_work, sdio_cdemux_fn); +static struct workqueue_struct *sdio_cdemux_wq; + +static DEFINE_MUTEX(write_lock); +static uint32_t bytes_to_write; +static DEFINE_MUTEX(temp_rx_lock); +static LIST_HEAD(temp_rx_list); + +static void sdio_cmux_fn(struct work_struct *work); +static DECLARE_WORK(sdio_cmux_work, sdio_cmux_fn); +static struct workqueue_struct *sdio_cmux_wq; + +static struct sdio_channel *sdio_qmi_chl; +static uint32_t sdio_cmux_inited; + +static uint32_t abort_tx; +static DEFINE_MUTEX(modem_reset_lock); + +static DEFINE_MUTEX(probe_lock); + +enum { + MSM_SDIO_CMUX_DEBUG = 1U << 0, + MSM_SDIO_CMUX_DUMP_BUFFER = 1U << 1, +}; + +static struct platform_device sdio_ctl_dev = { + .name = "SDIO_CTL", + .id = -1, +}; + +#if defined(DEBUG) +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + if (msm_sdio_cmux_debug_mask & MSM_SDIO_CMUX_DUMP_BUFFER) { \ + int i; \ + pr_debug("%s", prestr); \ + for (i = 0; i < cnt; i++) \ + pr_info("%.2x", buf[i]); \ + pr_debug("\n"); \ + } \ +} while (0) + +#define D(x...) \ +do { \ + if (msm_sdio_cmux_debug_mask & MSM_SDIO_CMUX_DEBUG) \ + pr_debug(x); \ +} while (0) + +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#define D(x...) do {} while (0) +#endif + +static int sdio_cmux_ch_alloc(int id) +{ + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid lc_id - %d\n", __func__, id); + return -EINVAL; + } + + logical_ch[id].lc_id = id; + mutex_init(&logical_ch[id].lc_lock); + init_waitqueue_head(&logical_ch[id].open_wait_queue); + logical_ch[id].is_remote_open = 0; + logical_ch[id].is_local_open = 0; + logical_ch[id].is_channel_reset = 0; + + INIT_LIST_HEAD(&logical_ch[id].tx_list); + mutex_init(&logical_ch[id].tx_lock); + + logical_ch[id].priv = NULL; + logical_ch[id].receive_cb = NULL; + logical_ch[id].write_done = NULL; + return 0; +} + +static int sdio_cmux_ch_clear_and_signal(int id) +{ + struct sdio_cmux_list_elem *list_elem; + + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid lc_id - %d\n", __func__, id); + return -EINVAL; + } + + mutex_lock(&logical_ch[id].lc_lock); + logical_ch[id].is_remote_open = 0; + mutex_lock(&logical_ch[id].tx_lock); + while (!list_empty(&logical_ch[id].tx_list)) { + list_elem = list_first_entry(&logical_ch[id].tx_list, + struct sdio_cmux_list_elem, + list); + list_del(&list_elem->list); + kfree(list_elem->cmux_pkt.hdr); + kfree(list_elem); + } + mutex_unlock(&logical_ch[id].tx_lock); + if (logical_ch[id].receive_cb) + logical_ch[id].receive_cb(NULL, 0, logical_ch[id].priv); + if (logical_ch[id].write_done) + logical_ch[id].write_done(NULL, 0, logical_ch[id].priv); + mutex_unlock(&logical_ch[id].lc_lock); + wake_up(&logical_ch[id].open_wait_queue); + return 0; +} + +static int sdio_cmux_write_cmd(const int id, enum cmd_type type) +{ + int write_size = 0; + void *write_data = NULL; + struct sdio_cmux_list_elem *list_elem; + + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid lc_id - %d\n", __func__, id); + return -EINVAL; + } + + if (type < 0 || type > NUM_CMDS) { + pr_err("%s: Invalid cmd - %d\n", __func__, type); + return -EINVAL; + } + + write_size = sizeof(struct sdio_cmux_hdr); + list_elem = kmalloc(sizeof(struct sdio_cmux_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return -ENOMEM; + } + + write_data = kmalloc(write_size, GFP_KERNEL); + if (!write_data) { + pr_err("%s: write_data alloc failed\n", __func__); + kfree(list_elem); + return -ENOMEM; + } + + list_elem->cmux_pkt.hdr = (struct sdio_cmux_hdr *)write_data; + list_elem->cmux_pkt.data = NULL; + + list_elem->cmux_pkt.hdr->lc_id = (uint8_t)id; + list_elem->cmux_pkt.hdr->pkt_len = (uint16_t)0; + list_elem->cmux_pkt.hdr->cmd = (uint8_t)type; + list_elem->cmux_pkt.hdr->status = (uint8_t)0; + if (type == STATUS) + list_elem->cmux_pkt.hdr->status = logical_ch[id].local_status; + list_elem->cmux_pkt.hdr->pad_bytes = (uint8_t)0; + list_elem->cmux_pkt.hdr->magic_no = (uint16_t)MAGIC_NO_V1; + + mutex_lock(&logical_ch[id].tx_lock); + list_add_tail(&list_elem->list, &logical_ch[id].tx_list); + mutex_unlock(&logical_ch[id].tx_lock); + + mutex_lock(&write_lock); + bytes_to_write += write_size; + mutex_unlock(&write_lock); + queue_work(sdio_cmux_wq, &sdio_cmux_work); + + return 0; +} + +int sdio_cmux_open(const int id, + void (*receive_cb)(void *, int, void *), + void (*write_done)(void *, int, void *), + void (*status_callback)(int, void *), + void *priv) +{ + int r; + struct sdio_cmux_list_elem *list_elem, *list_elem_tmp; + + if (!sdio_cmux_inited) + return -ENODEV; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid id - %d\n", __func__, id); + return -EINVAL; + } + + r = wait_event_timeout(logical_ch[id].open_wait_queue, + logical_ch[id].is_remote_open, (1 * HZ)); + if (r < 0) { + pr_err("ERROR %s: wait_event_timeout() failed for" + " ch%d with rc %d\n", __func__, id, r); + return r; + } + if (r == 0) { + pr_err("ERROR %s: Wait Timed Out for ch%d\n", __func__, id); + return -ETIMEDOUT; + } + + mutex_lock(&logical_ch[id].lc_lock); + if (!logical_ch[id].is_remote_open) { + pr_err("%s: Remote ch%d not opened\n", __func__, id); + mutex_unlock(&logical_ch[id].lc_lock); + return -EINVAL; + } + if (logical_ch[id].is_local_open) { + mutex_unlock(&logical_ch[id].lc_lock); + return 0; + } + logical_ch[id].is_local_open = 1; + logical_ch[id].priv = priv; + logical_ch[id].receive_cb = receive_cb; + logical_ch[id].write_done = write_done; + logical_ch[id].status_callback = status_callback; + if (logical_ch[id].receive_cb) { + mutex_lock(&temp_rx_lock); + list_for_each_entry_safe(list_elem, list_elem_tmp, + &temp_rx_list, list) { + if ((int)list_elem->cmux_pkt.hdr->lc_id == id) { + logical_ch[id].receive_cb( + list_elem->cmux_pkt.data, + (int)list_elem->cmux_pkt.hdr->pkt_len, + logical_ch[id].priv); + list_del(&list_elem->list); + kfree(list_elem->cmux_pkt.hdr); + kfree(list_elem); + } + } + mutex_unlock(&temp_rx_lock); + } + mutex_unlock(&logical_ch[id].lc_lock); + sdio_cmux_write_cmd(id, OPEN); + return 0; +} +EXPORT_SYMBOL(sdio_cmux_open); + +int sdio_cmux_close(int id) +{ + struct sdio_cmux_ch *ch; + + if (!sdio_cmux_inited) + return -ENODEV; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid channel close\n", __func__); + return -EINVAL; + } + + ch = &logical_ch[id]; + mutex_lock(&ch->lc_lock); + ch->receive_cb = NULL; + mutex_lock(&ch->tx_lock); + ch->write_done = NULL; + mutex_unlock(&ch->tx_lock); + ch->is_local_open = 0; + ch->priv = NULL; + mutex_unlock(&ch->lc_lock); + sdio_cmux_write_cmd(ch->lc_id, CLOSE); + return 0; +} +EXPORT_SYMBOL(sdio_cmux_close); + +int sdio_cmux_write_avail(int id) +{ + int write_avail; + + mutex_lock(&logical_ch[id].lc_lock); + if (logical_ch[id].is_channel_reset) { + mutex_unlock(&logical_ch[id].lc_lock); + return -ENETRESET; + } + mutex_unlock(&logical_ch[id].lc_lock); + write_avail = sdio_write_avail(sdio_qmi_chl); + return write_avail - bytes_to_write; +} +EXPORT_SYMBOL(sdio_cmux_write_avail); + +int sdio_cmux_write(int id, void *data, int len) +{ + struct sdio_cmux_list_elem *list_elem; + uint32_t write_size; + void *write_data = NULL; + struct sdio_cmux_ch *ch; + int ret; + + if (!sdio_cmux_inited) + return -ENODEV; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) { + pr_err("%s: Invalid channel id %d\n", __func__, id); + return -ENODEV; + } + + ch = &logical_ch[id]; + if (len <= 0) { + pr_err("%s: Invalid len %d bytes to write\n", + __func__, len); + return -EINVAL; + } + + write_size = sizeof(struct sdio_cmux_hdr) + len; + list_elem = kmalloc(sizeof(struct sdio_cmux_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return -ENOMEM; + } + + write_data = kmalloc(write_size, GFP_KERNEL); + if (!write_data) { + pr_err("%s: write_data alloc failed\n", __func__); + kfree(list_elem); + return -ENOMEM; + } + + list_elem->cmux_pkt.hdr = (struct sdio_cmux_hdr *)write_data; + list_elem->cmux_pkt.data = (void *)((char *)write_data + + sizeof(struct sdio_cmux_hdr)); + memcpy(list_elem->cmux_pkt.data, data, len); + + list_elem->cmux_pkt.hdr->lc_id = (uint8_t)ch->lc_id; + list_elem->cmux_pkt.hdr->pkt_len = (uint16_t)len; + list_elem->cmux_pkt.hdr->cmd = (uint8_t)DATA; + list_elem->cmux_pkt.hdr->status = (uint8_t)0; + list_elem->cmux_pkt.hdr->pad_bytes = (uint8_t)0; + list_elem->cmux_pkt.hdr->magic_no = (uint16_t)MAGIC_NO_V1; + + mutex_lock(&ch->lc_lock); + if (!ch->is_remote_open || !ch->is_local_open) { + pr_err("%s: Local ch%d sending data before sending/receiving" + " OPEN command\n", __func__, ch->lc_id); + if (ch->is_channel_reset) + ret = -ENETRESET; + else + ret = -ENODEV; + mutex_unlock(&ch->lc_lock); + kfree(write_data); + kfree(list_elem); + return ret; + } + mutex_lock(&ch->tx_lock); + list_add_tail(&list_elem->list, &ch->tx_list); + mutex_unlock(&ch->tx_lock); + mutex_unlock(&ch->lc_lock); + + mutex_lock(&write_lock); + bytes_to_write += write_size; + mutex_unlock(&write_lock); + queue_work(sdio_cmux_wq, &sdio_cmux_work); + + return len; +} +EXPORT_SYMBOL(sdio_cmux_write); + +int is_remote_open(int id) +{ + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) + return -ENODEV; + + return logical_ch_is_remote_open(id); +} +EXPORT_SYMBOL(is_remote_open); + +int sdio_cmux_is_channel_reset(int id) +{ + int ret; + if (id < 0 || id >= SDIO_CMUX_NUM_CHANNELS) + return -ENODEV; + + mutex_lock(&logical_ch[id].lc_lock); + ret = logical_ch[id].is_channel_reset; + mutex_unlock(&logical_ch[id].lc_lock); + return ret; +} +EXPORT_SYMBOL(sdio_cmux_is_channel_reset); + +int sdio_cmux_tiocmget(int id) +{ + int ret = (logical_ch[id].remote_status & DSR_POS ? TIOCM_DSR : 0) | + (logical_ch[id].remote_status & CTS_POS ? TIOCM_CTS : 0) | + (logical_ch[id].remote_status & CD_POS ? TIOCM_CD : 0) | + (logical_ch[id].remote_status & RI_POS ? TIOCM_RI : 0) | + (logical_ch[id].local_status & CTS_POS ? TIOCM_RTS : 0) | + (logical_ch[id].local_status & DSR_POS ? TIOCM_DTR : 0); + return ret; +} +EXPORT_SYMBOL(sdio_cmux_tiocmget); + +int sdio_cmux_tiocmset(int id, unsigned int set, unsigned int clear) +{ + if (set & TIOCM_DTR) + logical_ch[id].local_status |= DSR_POS; + + if (set & TIOCM_RTS) + logical_ch[id].local_status |= CTS_POS; + + if (clear & TIOCM_DTR) + logical_ch[id].local_status &= ~DSR_POS; + + if (clear & TIOCM_RTS) + logical_ch[id].local_status &= ~CTS_POS; + + sdio_cmux_write_cmd(id, STATUS); + return 0; +} +EXPORT_SYMBOL(sdio_cmux_tiocmset); + +static int copy_packet(void *pkt, int size) +{ + struct sdio_cmux_list_elem *list_elem = NULL; + void *temp_pkt = NULL; + + list_elem = kmalloc(sizeof(struct sdio_cmux_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return -ENOMEM; + } + temp_pkt = kmalloc(size, GFP_KERNEL); + if (!temp_pkt) { + pr_err("%s: temp_pkt alloc failed\n", __func__); + kfree(list_elem); + return -ENOMEM; + } + + memcpy(temp_pkt, pkt, size); + list_elem->cmux_pkt.hdr = temp_pkt; + list_elem->cmux_pkt.data = (void *)((char *)temp_pkt + + sizeof(struct sdio_cmux_hdr)); + mutex_lock(&temp_rx_lock); + list_add_tail(&list_elem->list, &temp_rx_list); + mutex_unlock(&temp_rx_lock); + return 0; +} + +static int process_cmux_pkt(void *pkt, int size) +{ + struct sdio_cmux_hdr *mux_hdr; + uint32_t id, data_size; + void *data; + char *dump_buf = (char *)pkt; + + D_DUMP_BUFFER("process_cmux_pkt:", size, dump_buf); + mux_hdr = (struct sdio_cmux_hdr *)pkt; + switch (mux_hdr->cmd) { + case OPEN: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received OPEN command for ch%d\n", __func__, id); + mutex_lock(&logical_ch[id].lc_lock); + logical_ch[id].is_remote_open = 1; + if (logical_ch[id].is_channel_reset) { + sdio_cmux_write_cmd(id, OPEN); + logical_ch[id].is_channel_reset = 0; + } + mutex_unlock(&logical_ch[id].lc_lock); + wake_up(&logical_ch[id].open_wait_queue); + break; + + case CLOSE: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received CLOSE command for ch%d\n", __func__, id); + sdio_cmux_ch_clear_and_signal(id); + break; + + case DATA: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received DATA for ch%d\n", __func__, id); + /*Channel is not locally open & if single packet received + then drop it*/ + mutex_lock(&logical_ch[id].lc_lock); + if (!logical_ch[id].is_remote_open) { + mutex_unlock(&logical_ch[id].lc_lock); + pr_err("%s: Remote Ch%d sent data before sending/" + "receiving OPEN command\n", __func__, id); + return -ENODEV; + } + + data = (void *)((char *)pkt + sizeof(struct sdio_cmux_hdr)); + data_size = (int)(((struct sdio_cmux_hdr *)pkt)->pkt_len); + if (logical_ch[id].receive_cb) + logical_ch[id].receive_cb(data, data_size, + logical_ch[id].priv); + else + copy_packet(pkt, size); + mutex_unlock(&logical_ch[id].lc_lock); + break; + + case STATUS: + id = (uint32_t)(mux_hdr->lc_id); + D("%s: Received STATUS command for ch%d\n", __func__, id); + if (logical_ch[id].remote_status != mux_hdr->status) { + mutex_lock(&logical_ch[id].lc_lock); + logical_ch[id].remote_status = mux_hdr->status; + mutex_unlock(&logical_ch[id].lc_lock); + if (logical_ch[id].status_callback) + logical_ch[id].status_callback( + sdio_cmux_tiocmget(id), + logical_ch[id].priv); + } + break; + } + return 0; +} + +static void parse_cmux_data(void *data, int size) +{ + int data_parsed = 0, pkt_size; + char *temp_ptr; + + D("Entered %s\n", __func__); + temp_ptr = (char *)data; + while (data_parsed < size) { + pkt_size = sizeof(struct sdio_cmux_hdr) + + (int)(((struct sdio_cmux_hdr *)temp_ptr)->pkt_len); + D("Parsed %d bytes, Current Pkt Size %d bytes," + " Total size %d bytes\n", data_parsed, pkt_size, size); + process_cmux_pkt((void *)temp_ptr, pkt_size); + data_parsed += pkt_size; + temp_ptr += pkt_size; + } + + kfree(data); +} + +static void sdio_cdemux_fn(struct work_struct *work) +{ + int r = 0, read_avail = 0; + void *cmux_data; + + while (1) { + read_avail = sdio_read_avail(sdio_qmi_chl); + if (read_avail < 0) { + pr_err("%s: sdio_read_avail failed with rc %d\n", + __func__, read_avail); + return; + } + + if (read_avail == 0) { + D("%s: Nothing to read\n", __func__); + return; + } + + D("%s: kmalloc %d bytes\n", __func__, read_avail); + cmux_data = kmalloc(read_avail, GFP_KERNEL); + if (!cmux_data) { + pr_err("%s: kmalloc Failed\n", __func__); + return; + } + + D("%s: sdio_read %d bytes\n", __func__, read_avail); + r = sdio_read(sdio_qmi_chl, cmux_data, read_avail); + if (r < 0) { + pr_err("%s: sdio_read failed with rc %d\n", + __func__, r); + kfree(cmux_data); + return; + } + + parse_cmux_data(cmux_data, read_avail); + } + return; +} + +static void sdio_cmux_fn(struct work_struct *work) +{ + int i, r = 0; + void *write_data; + uint32_t write_size, write_avail, write_retry = 0; + int bytes_written; + struct sdio_cmux_list_elem *list_elem = NULL; + struct sdio_cmux_ch *ch; + + for (i = 0; i < SDIO_CMUX_NUM_CHANNELS; ++i) { + ch = &logical_ch[i]; + bytes_written = 0; + mutex_lock(&ch->tx_lock); + while (!list_empty(&ch->tx_list)) { + list_elem = list_first_entry(&ch->tx_list, + struct sdio_cmux_list_elem, + list); + list_del(&list_elem->list); + mutex_unlock(&ch->tx_lock); + + write_data = (void *)list_elem->cmux_pkt.hdr; + write_size = sizeof(struct sdio_cmux_hdr) + + (uint32_t)list_elem->cmux_pkt.hdr->pkt_len; + + mutex_lock(&modem_reset_lock); + while (!(abort_tx) && + ((write_avail = sdio_write_avail(sdio_qmi_chl)) + < write_size)) { + mutex_unlock(&modem_reset_lock); + pr_err("%s: sdio_write_avail %d bytes, " + "write size %d bytes. Waiting...\n", + __func__, write_avail, write_size); + msleep(250); + mutex_lock(&modem_reset_lock); + } + while (!(abort_tx) && + ((r = sdio_write(sdio_qmi_chl, + write_data, write_size)) < 0) + && (r != -ENODEV) + && (write_retry++ < MAX_WRITE_RETRY)) { + mutex_unlock(&modem_reset_lock); + pr_err("%s: sdio_write failed with rc %d." + "Retrying...", __func__, r); + msleep(250); + mutex_lock(&modem_reset_lock); + } + if (!r && !abort_tx) { + D("%s: sdio_write_completed %dbytes\n", + __func__, write_size); + bytes_written += write_size; + } else if (r == -ENODEV) { + pr_err("%s: aborting_tx because sdio_write" + " returned %d\n", __func__, r); + r = 0; + abort_tx = 1; + } + mutex_unlock(&modem_reset_lock); + kfree(list_elem->cmux_pkt.hdr); + kfree(list_elem); + mutex_lock(&write_lock); + bytes_to_write -= write_size; + mutex_unlock(&write_lock); + mutex_lock(&ch->tx_lock); + } + if (ch->write_done) + ch->write_done(NULL, bytes_written, ch->priv); + mutex_unlock(&ch->tx_lock); + } + return; +} + +static void sdio_qmi_chl_notify(void *priv, unsigned event) +{ + if (event == SDIO_EVENT_DATA_READ_AVAIL) { + D("%s: Received SDIO_EVENT_DATA_READ_AVAIL\n", __func__); + queue_work(sdio_cdemux_wq, &sdio_cdemux_work); + } +} + +#ifdef CONFIG_DEBUG_FS + +static int debug_tbl(char *buf, int max) +{ + int i = 0; + int j; + + for (j = 0; j < SDIO_CMUX_NUM_CHANNELS; ++j) { + i += scnprintf(buf + i, max - i, + "ch%02d local open=%s remote open=%s\n", + j, logical_ch_is_local_open(j) ? "Y" : "N", + logical_ch_is_remote_open(j) ? "Y" : "N"); + } + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +#endif + +static int sdio_cmux_probe(struct platform_device *pdev) +{ + int i, r; + + mutex_lock(&probe_lock); + D("%s Begins\n", __func__); + if (sdio_cmux_inited) { + mutex_lock(&modem_reset_lock); + r = sdio_open("SDIO_QMI", &sdio_qmi_chl, NULL, + sdio_qmi_chl_notify); + if (r < 0) { + mutex_unlock(&modem_reset_lock); + pr_err("%s: sdio_open() failed\n", __func__); + goto error0; + } + abort_tx = 0; + mutex_unlock(&modem_reset_lock); + mutex_unlock(&probe_lock); + return 0; + } + + for (i = 0; i < SDIO_CMUX_NUM_CHANNELS; ++i) + sdio_cmux_ch_alloc(i); + INIT_LIST_HEAD(&temp_rx_list); + + sdio_cmux_wq = create_singlethread_workqueue("sdio_cmux"); + if (IS_ERR(sdio_cmux_wq)) { + pr_err("%s: create_singlethread_workqueue() ENOMEM\n", + __func__); + r = -ENOMEM; + goto error0; + } + + sdio_cdemux_wq = create_singlethread_workqueue("sdio_cdemux"); + if (IS_ERR(sdio_cdemux_wq)) { + pr_err("%s: create_singlethread_workqueue() ENOMEM\n", + __func__); + r = -ENOMEM; + goto error1; + } + + r = sdio_open("SDIO_QMI", &sdio_qmi_chl, NULL, sdio_qmi_chl_notify); + if (r < 0) { + pr_err("%s: sdio_open() failed\n", __func__); + goto error2; + } + + platform_device_register(&sdio_ctl_dev); + sdio_cmux_inited = 1; + D("SDIO Control MUX Driver Initialized.\n"); + mutex_unlock(&probe_lock); + return 0; + +error2: + destroy_workqueue(sdio_cdemux_wq); +error1: + destroy_workqueue(sdio_cmux_wq); +error0: + mutex_unlock(&probe_lock); + return r; +} + +static int sdio_cmux_remove(struct platform_device *pdev) +{ + int i; + + mutex_lock(&modem_reset_lock); + abort_tx = 1; + + for (i = 0; i < SDIO_CMUX_NUM_CHANNELS; ++i) { + mutex_lock(&logical_ch[i].lc_lock); + logical_ch[i].is_channel_reset = 1; + mutex_unlock(&logical_ch[i].lc_lock); + sdio_cmux_ch_clear_and_signal(i); + } + sdio_qmi_chl = NULL; + mutex_unlock(&modem_reset_lock); + + return 0; +} + +static struct platform_driver sdio_cmux_driver = { + .probe = sdio_cmux_probe, + .remove = sdio_cmux_remove, + .driver = { + .name = "SDIO_QMI", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_cmux_init(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *dent; + + dent = debugfs_create_dir("sdio_cmux", 0); + if (!IS_ERR(dent)) + debug_create("tbl", 0444, dent, debug_tbl); +#endif + + msm_sdio_cmux_debug_mask = 0; + return platform_driver_register(&sdio_cmux_driver); +} + +module_init(sdio_cmux_init); +MODULE_DESCRIPTION("MSM SDIO Control MUX"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_ctl.c b/arch/arm/mach-msm/sdio_ctl.c new file mode 100644 index 00000000000..586e8905590 --- /dev/null +++ b/arch/arm/mach-msm/sdio_ctl.c @@ -0,0 +1,545 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * SDIO Control Driver -- Provides a binary SDIO muxed control port + * interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modem_notifier.h" +#include + +#define MAX_WRITE_RETRY 5 +#define MAGIC_NO_V1 0x33FC +#define NUM_SDIO_CTL_PORTS 10 +#define DEVICE_NAME "sdioctl" +#define MAX_BUF_SIZE 2048 +#define DEBUG + +static int msm_sdio_ctl_debug_mask; +module_param_named(debug_mask, msm_sdio_ctl_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +struct sdio_ctl_dev { + int id; + char name[9]; + struct cdev cdev; + struct device *devicep; + struct mutex dev_lock; + int ref_count; + + struct mutex rx_lock; + uint32_t read_avail; + struct list_head rx_list; + + wait_queue_head_t read_wait_queue; + wait_queue_head_t write_wait_queue; +} *sdio_ctl_devp[NUM_SDIO_CTL_PORTS]; + +struct sdio_ctl_pkt { + int data_size; + void *data; +}; + +struct sdio_ctl_list_elem { + struct list_head list; + struct sdio_ctl_pkt ctl_pkt; +}; + +struct class *sdio_ctl_classp; +static dev_t sdio_ctl_number; +static uint32_t sdio_ctl_inited; + +enum { + MSM_SDIO_CTL_DEBUG = 1U << 0, + MSM_SDIO_CTL_DUMP_BUFFER = 1U << 1, +}; + +#if defined(DEBUG) +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + if (msm_sdio_ctl_debug_mask & MSM_SDIO_CTL_DUMP_BUFFER) { \ + int i; \ + pr_info("%s", prestr); \ + for (i = 0; i < cnt; i++) \ + pr_info("%.2x", buf[i]); \ + pr_info("\n"); \ + } \ +} while (0) + +#define D(x...) \ +do { \ + if (msm_sdio_ctl_debug_mask & MSM_SDIO_CTL_DEBUG) \ + pr_info(x); \ +} while (0) + +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#define D(x...) do {} while (0) +#endif + +static uint32_t cmux_ch_id[] = { + SDIO_CMUX_DATA_CTL_0, + SDIO_CMUX_DATA_CTL_1, + SDIO_CMUX_DATA_CTL_2, + SDIO_CMUX_DATA_CTL_3, + SDIO_CMUX_DATA_CTL_4, + SDIO_CMUX_DATA_CTL_5, + SDIO_CMUX_DATA_CTL_6, + SDIO_CMUX_DATA_CTL_7, + SDIO_CMUX_USB_CTL_0, + SDIO_CMUX_CSVT_CTL_0 +}; + +static int get_ctl_dev_index(int id) +{ + int dev_index; + for (dev_index = 0; dev_index < NUM_SDIO_CTL_PORTS; dev_index++) { + if (cmux_ch_id[dev_index] == id) + return dev_index; + } + return -ENODEV; +} + +static void sdio_ctl_receive_cb(void *data, int size, void *priv) +{ + struct sdio_ctl_list_elem *list_elem = NULL; + int id = ((struct sdio_ctl_dev *)(priv))->id; + int dev_index; + + if (id < 0 || id > cmux_ch_id[NUM_SDIO_CTL_PORTS - 1]) + return; + dev_index = get_ctl_dev_index(id); + if (dev_index < 0) { + pr_err("%s: Ch%d is not exported to user-space\n", + __func__, id); + return; + } + + if (!data || size <= 0) { + wake_up(&sdio_ctl_devp[dev_index]->read_wait_queue); + return; + } + + list_elem = kmalloc(sizeof(struct sdio_ctl_list_elem), GFP_KERNEL); + if (!list_elem) { + pr_err("%s: list_elem alloc failed\n", __func__); + return; + } + + list_elem->ctl_pkt.data = kmalloc(size, GFP_KERNEL); + if (!list_elem->ctl_pkt.data) { + pr_err("%s: list_elem->data alloc failed\n", __func__); + kfree(list_elem); + return; + } + memcpy(list_elem->ctl_pkt.data, data, size); + list_elem->ctl_pkt.data_size = size; + mutex_lock(&sdio_ctl_devp[dev_index]->rx_lock); + list_add_tail(&list_elem->list, &sdio_ctl_devp[dev_index]->rx_list); + sdio_ctl_devp[dev_index]->read_avail += size; + mutex_unlock(&sdio_ctl_devp[dev_index]->rx_lock); + wake_up(&sdio_ctl_devp[dev_index]->read_wait_queue); +} + +static void sdio_ctl_write_done(void *data, int size, void *priv) +{ + int id = ((struct sdio_ctl_dev *)(priv))->id; + int dev_index; + if (id < 0 || id > cmux_ch_id[NUM_SDIO_CTL_PORTS - 1]) + return; + + dev_index = get_ctl_dev_index(id); + if (dev_index < 0) { + pr_err("%s: Ch%d is not exported to user-space\n", + __func__, id); + return; + } + wake_up(&sdio_ctl_devp[dev_index]->write_wait_queue); +} + +static long sdio_ctl_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct sdio_ctl_dev *sdio_ctl_devp; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) + return -ENODEV; + + switch (cmd) { + case TIOCMGET: + ret = sdio_cmux_tiocmget(sdio_ctl_devp->id); + break; + case TIOCMSET: + ret = sdio_cmux_tiocmset(sdio_ctl_devp->id, arg, ~arg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +ssize_t sdio_ctl_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int r = 0, id, bytes_to_read; + struct sdio_ctl_dev *sdio_ctl_devp; + struct sdio_ctl_list_elem *list_elem = NULL; + + sdio_ctl_devp = file->private_data; + + if (!sdio_ctl_devp) + return -ENODEV; + + D("%s: read from ch%d\n", __func__, sdio_ctl_devp->id); + + id = sdio_ctl_devp->id; + mutex_lock(&sdio_ctl_devp->rx_lock); + while (sdio_ctl_devp->read_avail <= 0) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + r = wait_event_interruptible(sdio_ctl_devp->read_wait_queue, + sdio_ctl_devp->read_avail > 0 || + !is_remote_open(id)); + if (sdio_cmux_is_channel_reset(id)) + return -ENETRESET; + + if (!is_remote_open(id)) + return -ENODEV; + + if (r < 0) { + /* qualify error message */ + /* we get this anytime a signal comes in */ + if (r != -ERESTARTSYS) + pr_err("ERROR:%s: wait_event_interruptible " + "ret %i\n", __func__, r); + return r; + } + mutex_lock(&sdio_ctl_devp->rx_lock); + } + + if (list_empty(&sdio_ctl_devp->rx_list)) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + D("%s: Nothing in ch%d's rx_list\n", __func__, + sdio_ctl_devp->id); + return -EAGAIN; + } + + list_elem = list_first_entry(&sdio_ctl_devp->rx_list, + struct sdio_ctl_list_elem, list); + bytes_to_read = (uint32_t)(list_elem->ctl_pkt.data_size); + if (bytes_to_read > count) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + pr_err("%s: Packet size %d > buf size %d\n", __func__, + bytes_to_read, count); + return -ENOMEM; + } + + if (copy_to_user(buf, list_elem->ctl_pkt.data, bytes_to_read)) { + mutex_unlock(&sdio_ctl_devp->rx_lock); + pr_err("%s: copy_to_user failed for ch%d\n", __func__, + sdio_ctl_devp->id); + return -EFAULT; + } + sdio_ctl_devp->read_avail -= bytes_to_read; + list_del(&list_elem->list); + kfree(list_elem->ctl_pkt.data); + kfree(list_elem); + mutex_unlock(&sdio_ctl_devp->rx_lock); + D("%s: Returning %d bytes to ch%d\n", __func__, + bytes_to_read, sdio_ctl_devp->id); + return bytes_to_read; +} + + +ssize_t sdio_ctl_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int r = 0, id; + char *temp_buf; + struct sdio_ctl_dev *sdio_ctl_devp; + + if (count <= 0) + return -EINVAL; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) + return -ENODEV; + + D("%s: writing %i bytes on ch%d\n", + __func__, count, sdio_ctl_devp->id); + id = sdio_ctl_devp->id; + mutex_lock(&sdio_ctl_devp->dev_lock); + while (sdio_cmux_write_avail(id) < count) { + mutex_unlock(&sdio_ctl_devp->dev_lock); + r = wait_event_interruptible(sdio_ctl_devp->write_wait_queue, + sdio_cmux_write_avail(id) >= count + || !is_remote_open(id)); + + if (sdio_cmux_is_channel_reset(id)) + return -ENETRESET; + + if (!is_remote_open(id)) + return -ENODEV; + + if (r < 0) { + /* qualify error message */ + /* we get this anytime a signal comes in */ + if (r != -ERESTARTSYS) + pr_err("ERROR:%s: wait_event_interruptible " + "ret %i\n", __func__, r); + return r; + } + mutex_lock(&sdio_ctl_devp->dev_lock); + } + + temp_buf = kmalloc(count, GFP_KERNEL); + if (!temp_buf) { + mutex_unlock(&sdio_ctl_devp->dev_lock); + pr_err("%s: temp_buf alloc failed\n", __func__); + return -ENOMEM; + } + + if (copy_from_user(temp_buf, buf, count)) { + mutex_unlock(&sdio_ctl_devp->dev_lock); + pr_err("%s: copy_from_user failed\n", __func__); + kfree(temp_buf); + return -EFAULT; + } + + r = sdio_cmux_write(id, (void *)temp_buf, count); + kfree(temp_buf); + mutex_unlock(&sdio_ctl_devp->dev_lock); + return r; +} + + +int sdio_ctl_open(struct inode *inode, struct file *file) +{ + int r = 0; + struct sdio_ctl_dev *sdio_ctl_devp; + + if (!sdio_ctl_inited) + return -EIO; + + sdio_ctl_devp = container_of(inode->i_cdev, struct sdio_ctl_dev, cdev); + + if (!sdio_ctl_devp) + return -ENODEV; + + D("%s called on sdioctl%d device\n", __func__, sdio_ctl_devp->id); + r = sdio_cmux_open(sdio_ctl_devp->id, sdio_ctl_receive_cb, + sdio_ctl_write_done, NULL, + sdio_ctl_devp); + if (r < 0) { + pr_err("ERROR %s: sdio_cmux_open failed with rc %d\n", + __func__, r); + return r; + } + + mutex_lock(&sdio_ctl_devp->dev_lock); + sdio_ctl_devp->ref_count++; + mutex_unlock(&sdio_ctl_devp->dev_lock); + + file->private_data = sdio_ctl_devp; + return 0; +} + +int sdio_ctl_release(struct inode *inode, struct file *file) +{ + struct sdio_ctl_dev *sdio_ctl_devp; + struct sdio_ctl_list_elem *list_elem = NULL; + + sdio_ctl_devp = file->private_data; + if (!sdio_ctl_devp) + return -EINVAL; + + D("%s called on sdioctl%d device\n", __func__, sdio_ctl_devp->id); + + mutex_lock(&sdio_ctl_devp->dev_lock); + if (sdio_ctl_devp->ref_count > 0) { + sdio_ctl_devp->ref_count--; + if (!sdio_ctl_devp->ref_count) { + mutex_lock(&sdio_ctl_devp->rx_lock); + while (!list_empty(&sdio_ctl_devp->rx_list)) { + list_elem = list_first_entry( + &sdio_ctl_devp->rx_list, + struct sdio_ctl_list_elem, + list); + list_del(&list_elem->list); + kfree(list_elem->ctl_pkt.data); + kfree(list_elem); + } + sdio_ctl_devp->read_avail = 0; + mutex_unlock(&sdio_ctl_devp->rx_lock); + sdio_cmux_close(sdio_ctl_devp->id); + } + } + mutex_unlock(&sdio_ctl_devp->dev_lock); + + file->private_data = NULL; + return 0; +} + +static const struct file_operations sdio_ctl_fops = { + .owner = THIS_MODULE, + .open = sdio_ctl_open, + .release = sdio_ctl_release, + .read = sdio_ctl_read, + .write = sdio_ctl_write, + .unlocked_ioctl = sdio_ctl_ioctl, +}; + +static int sdio_ctl_probe(struct platform_device *pdev) +{ + int i; + int r; + + pr_info("%s Begins\n", __func__); + for (i = 0; i < NUM_SDIO_CTL_PORTS; ++i) { + sdio_ctl_devp[i] = kzalloc(sizeof(struct sdio_ctl_dev), + GFP_KERNEL); + if (IS_ERR(sdio_ctl_devp[i])) { + pr_err("ERROR:%s kmalloc() ENOMEM\n", __func__); + r = -ENOMEM; + goto error0; + } + + sdio_ctl_devp[i]->id = cmux_ch_id[i]; + sdio_ctl_devp[i]->ref_count = 0; + + mutex_init(&sdio_ctl_devp[i]->dev_lock); + init_waitqueue_head(&sdio_ctl_devp[i]->read_wait_queue); + init_waitqueue_head(&sdio_ctl_devp[i]->write_wait_queue); + mutex_init(&sdio_ctl_devp[i]->rx_lock); + INIT_LIST_HEAD(&sdio_ctl_devp[i]->rx_list); + sdio_ctl_devp[i]->read_avail = 0; + } + + r = alloc_chrdev_region(&sdio_ctl_number, 0, NUM_SDIO_CTL_PORTS, + DEVICE_NAME); + if (IS_ERR_VALUE(r)) { + pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n", + __func__, r); + goto error0; + } + + sdio_ctl_classp = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(sdio_ctl_classp)) { + pr_err("ERROR:%s: class_create() ENOMEM\n", __func__); + r = -ENOMEM; + goto error1; + } + + for (i = 0; i < NUM_SDIO_CTL_PORTS; ++i) { + cdev_init(&sdio_ctl_devp[i]->cdev, &sdio_ctl_fops); + sdio_ctl_devp[i]->cdev.owner = THIS_MODULE; + + r = cdev_add(&sdio_ctl_devp[i]->cdev, (sdio_ctl_number + i), + 1); + + if (IS_ERR_VALUE(r)) { + pr_err("%s: cdev_add() ret %i\n", __func__, r); + kfree(sdio_ctl_devp[i]); + goto error2; + } + + sdio_ctl_devp[i]->devicep = + device_create(sdio_ctl_classp, NULL, + (sdio_ctl_number + i), NULL, + DEVICE_NAME "%d", cmux_ch_id[i]); + + if (IS_ERR(sdio_ctl_devp[i]->devicep)) { + pr_err("%s: device_create() ENOMEM\n", __func__); + r = -ENOMEM; + cdev_del(&sdio_ctl_devp[i]->cdev); + kfree(sdio_ctl_devp[i]); + goto error2; + } + } + + sdio_ctl_inited = 1; + D("SDIO Control Port Driver Initialized.\n"); + return 0; + +error2: + while (--i >= 0) { + cdev_del(&sdio_ctl_devp[i]->cdev); + device_destroy(sdio_ctl_classp, + MKDEV(MAJOR(sdio_ctl_number), i)); + } + + class_destroy(sdio_ctl_classp); + i = NUM_SDIO_CTL_PORTS; +error1: + unregister_chrdev_region(MAJOR(sdio_ctl_number), NUM_SDIO_CTL_PORTS); +error0: + while (--i >= 0) + kfree(sdio_ctl_devp[i]); + return r; +} + +static int sdio_ctl_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < NUM_SDIO_CTL_PORTS; ++i) { + cdev_del(&sdio_ctl_devp[i]->cdev); + kfree(sdio_ctl_devp[i]); + device_destroy(sdio_ctl_classp, + MKDEV(MAJOR(sdio_ctl_number), i)); + } + class_destroy(sdio_ctl_classp); + unregister_chrdev_region(MAJOR(sdio_ctl_number), NUM_SDIO_CTL_PORTS); + + return 0; +} + +static struct platform_driver sdio_ctl_driver = { + .probe = sdio_ctl_probe, + .remove = sdio_ctl_remove, + .driver = { + .name = "SDIO_CTL", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_ctl_init(void) +{ + msm_sdio_ctl_debug_mask = 0; + return platform_driver_register(&sdio_ctl_driver); +} + +module_init(sdio_ctl_init); +MODULE_DESCRIPTION("MSM SDIO Control Port"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_dmux.c b/arch/arm/mach-msm/sdio_dmux.c new file mode 100644 index 00000000000..71b4e9b9229 --- /dev/null +++ b/arch/arm/mach-msm/sdio_dmux.c @@ -0,0 +1,925 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SDIO DMUX module. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SDIO_CH_LOCAL_OPEN 0x1 +#define SDIO_CH_REMOTE_OPEN 0x2 +#define SDIO_CH_IN_RESET 0x4 + +#define SDIO_MUX_HDR_MAGIC_NO 0x33fc + +#define SDIO_MUX_HDR_CMD_DATA 0 +#define SDIO_MUX_HDR_CMD_OPEN 1 +#define SDIO_MUX_HDR_CMD_CLOSE 2 + +#define LOW_WATERMARK 2 +#define HIGH_WATERMARK 4 + +static int msm_sdio_dmux_debug_enable; +module_param_named(debug_enable, msm_sdio_dmux_debug_enable, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(DEBUG) +static uint32_t sdio_dmux_read_cnt; +static uint32_t sdio_dmux_write_cnt; +static uint32_t sdio_dmux_write_cpy_cnt; +static uint32_t sdio_dmux_write_cpy_bytes; + +#define DBG(x...) do { \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug(x); \ + } while (0) + +#define DBG_INC_READ_CNT(x) do { \ + sdio_dmux_read_cnt += (x); \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug("%s: total read bytes %u\n", \ + __func__, sdio_dmux_read_cnt); \ + } while (0) + +#define DBG_INC_WRITE_CNT(x) do { \ + sdio_dmux_write_cnt += (x); \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug("%s: total written bytes %u\n", \ + __func__, sdio_dmux_write_cnt); \ + } while (0) + +#define DBG_INC_WRITE_CPY(x) do { \ + sdio_dmux_write_cpy_bytes += (x); \ + sdio_dmux_write_cpy_cnt++; \ + if (msm_sdio_dmux_debug_enable) \ + pr_debug("%s: total write copy cnt %u, bytes %u\n", \ + __func__, sdio_dmux_write_cpy_cnt, \ + sdio_dmux_write_cpy_bytes); \ + } while (0) +#else +#define DBG(x...) do { } while (0) +#define DBG_INC_READ_CNT(x...) do { } while (0) +#define DBG_INC_WRITE_CNT(x...) do { } while (0) +#define DBG_INC_WRITE_CPY(x...) do { } while (0) +#endif + +struct sdio_ch_info { + uint32_t status; + void (*receive_cb)(void *, struct sk_buff *); + void (*write_done)(void *, struct sk_buff *); + void *priv; + spinlock_t lock; + int num_tx_pkts; + int use_wm; +}; + +static struct sk_buff_head sdio_mux_write_pool; +static spinlock_t sdio_mux_write_lock; + +static struct sdio_channel *sdio_mux_ch; +static struct sdio_ch_info sdio_ch[SDIO_DMUX_NUM_CHANNELS]; +struct wake_lock sdio_mux_ch_wakelock; +static int sdio_mux_initialized; +static int fatal_error; + +struct sdio_mux_hdr { + uint16_t magic_num; + uint8_t reserved; + uint8_t cmd; + uint8_t pad_len; + uint8_t ch_id; + uint16_t pkt_len; +}; + +struct sdio_partial_pkt_info { + uint32_t valid; + struct sk_buff *skb; + struct sdio_mux_hdr *hdr; +}; + +static void sdio_mux_read_data(struct work_struct *work); +static void sdio_mux_write_data(struct work_struct *work); +static void sdio_mux_send_open_cmd(uint32_t id); + +static DEFINE_MUTEX(sdio_mux_lock); +static DECLARE_WORK(work_sdio_mux_read, sdio_mux_read_data); +static DECLARE_WORK(work_sdio_mux_write, sdio_mux_write_data); +static DECLARE_DELAYED_WORK(delayed_work_sdio_mux_write, sdio_mux_write_data); + +static struct workqueue_struct *sdio_mux_workqueue; +static struct sdio_partial_pkt_info sdio_partial_pkt; + +#define sdio_ch_is_open(x) \ + (sdio_ch[(x)].status == (SDIO_CH_LOCAL_OPEN | SDIO_CH_REMOTE_OPEN)) + +#define sdio_ch_is_local_open(x) \ + (sdio_ch[(x)].status & SDIO_CH_LOCAL_OPEN) + +#define sdio_ch_is_remote_open(x) \ + (sdio_ch[(x)].status & SDIO_CH_REMOTE_OPEN) + +#define sdio_ch_is_in_reset(x) \ + (sdio_ch[(x)].status & SDIO_CH_IN_RESET) + +static inline void skb_set_data(struct sk_buff *skb, + unsigned char *data, + unsigned int len) +{ + /* panic if tail > end */ + skb->data = data; + skb->tail = skb->data + len; + skb->len = len; + skb->truesize = len + sizeof(struct sk_buff); +} + +static void sdio_mux_save_partial_pkt(struct sdio_mux_hdr *hdr, + struct sk_buff *skb_mux) +{ + struct sk_buff *skb; + + /* i think we can avoid cloning here */ + skb = skb_clone(skb_mux, GFP_KERNEL); + if (!skb) { + pr_err("%s: cannot clone skb\n", __func__); + return; + } + + /* protect? */ + skb_set_data(skb, (unsigned char *)hdr, + skb->tail - (unsigned char *)hdr); + sdio_partial_pkt.skb = skb; + sdio_partial_pkt.valid = 1; + DBG("%s: head %p data %p tail %p end %p len %d\n", __func__, + skb->head, skb->data, skb->tail, skb->end, skb->len); + return; +} + +static void *handle_sdio_mux_data(struct sdio_mux_hdr *hdr, + struct sk_buff *skb_mux) +{ + struct sk_buff *skb; + void *rp = (void *)hdr; + unsigned long flags; + + /* protect? */ + rp += sizeof(*hdr); + if (rp < (void *)skb_mux->tail) + rp += (hdr->pkt_len + hdr->pad_len); + + if (rp > (void *)skb_mux->tail) { + /* partial packet */ + sdio_mux_save_partial_pkt(hdr, skb_mux); + goto packet_done; + } + + DBG("%s: hdr %p next %p tail %p pkt_size %d\n", + __func__, hdr, rp, skb_mux->tail, hdr->pkt_len + hdr->pad_len); + + skb = skb_clone(skb_mux, GFP_KERNEL); + if (!skb) { + pr_err("%s: cannot clone skb\n", __func__); + goto packet_done; + } + + skb_set_data(skb, (unsigned char *)(hdr + 1), hdr->pkt_len); + DBG("%s: head %p data %p tail %p end %p len %d\n", + __func__, skb->head, skb->data, skb->tail, skb->end, skb->len); + + /* probably we should check channel status */ + /* discard packet early if local side not open */ + spin_lock_irqsave(&sdio_ch[hdr->ch_id].lock, flags); + if (sdio_ch[hdr->ch_id].receive_cb) + sdio_ch[hdr->ch_id].receive_cb(sdio_ch[hdr->ch_id].priv, skb); + else + dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&sdio_ch[hdr->ch_id].lock, flags); + +packet_done: + return rp; +} + +static void *handle_sdio_mux_command(struct sdio_mux_hdr *hdr, + struct sk_buff *skb_mux) +{ + void *rp; + unsigned long flags; + int send_open = 0; + + DBG("%s: cmd %d ch %d\n", __func__, hdr->cmd, hdr->ch_id); + switch (hdr->cmd) { + case SDIO_MUX_HDR_CMD_DATA: + rp = handle_sdio_mux_data(hdr, skb_mux); + break; + case SDIO_MUX_HDR_CMD_OPEN: + spin_lock_irqsave(&sdio_ch[hdr->ch_id].lock, flags); + sdio_ch[hdr->ch_id].status |= SDIO_CH_REMOTE_OPEN; + sdio_ch[hdr->ch_id].num_tx_pkts = 0; + + if (sdio_ch_is_in_reset(hdr->ch_id)) { + DBG("%s: in reset - sending open cmd\n", __func__); + sdio_ch[hdr->ch_id].status &= ~SDIO_CH_IN_RESET; + send_open = 1; + } + + /* notify client so it can update its status */ + if (sdio_ch[hdr->ch_id].receive_cb) + sdio_ch[hdr->ch_id].receive_cb( + sdio_ch[hdr->ch_id].priv, NULL); + + if (sdio_ch[hdr->ch_id].write_done) + sdio_ch[hdr->ch_id].write_done( + sdio_ch[hdr->ch_id].priv, NULL); + spin_unlock_irqrestore(&sdio_ch[hdr->ch_id].lock, flags); + rp = hdr + 1; + if (send_open) + sdio_mux_send_open_cmd(hdr->ch_id); + + break; + case SDIO_MUX_HDR_CMD_CLOSE: + /* probably should drop pending write */ + spin_lock_irqsave(&sdio_ch[hdr->ch_id].lock, flags); + sdio_ch[hdr->ch_id].status &= ~SDIO_CH_REMOTE_OPEN; + spin_unlock_irqrestore(&sdio_ch[hdr->ch_id].lock, flags); + rp = hdr + 1; + break; + default: + rp = hdr + 1; + } + + return rp; +} + +static void *handle_sdio_partial_pkt(struct sk_buff *skb_mux) +{ + struct sk_buff *p_skb; + struct sdio_mux_hdr *p_hdr; + void *ptr, *rp = skb_mux->data; + + /* protoect? */ + if (sdio_partial_pkt.valid) { + p_skb = sdio_partial_pkt.skb; + + ptr = skb_push(skb_mux, p_skb->len); + memcpy(ptr, p_skb->data, p_skb->len); + sdio_partial_pkt.skb = NULL; + sdio_partial_pkt.valid = 0; + dev_kfree_skb_any(p_skb); + + DBG("%s: head %p data %p tail %p end %p len %d\n", __func__, + skb_mux->head, skb_mux->data, skb_mux->tail, + skb_mux->end, skb_mux->len); + + p_hdr = (struct sdio_mux_hdr *)skb_mux->data; + rp = handle_sdio_mux_command(p_hdr, skb_mux); + } + return rp; +} + +static void sdio_mux_read_data(struct work_struct *work) +{ + struct sk_buff *skb_mux; + void *ptr = 0; + int sz, rc, len = 0; + struct sdio_mux_hdr *hdr; + static int workqueue_pinned; + + if (!workqueue_pinned) { + struct cpumask cpus; + + cpumask_clear(&cpus); + cpumask_set_cpu(0, &cpus); + + if (sched_setaffinity(current->pid, &cpus)) + pr_err("%s: sdio_dmux set CPU affinity failed\n", + __func__); + workqueue_pinned = 1; + } + + DBG("%s: reading\n", __func__); + /* should probably have a separate read lock */ + mutex_lock(&sdio_mux_lock); + sz = sdio_read_avail(sdio_mux_ch); + DBG("%s: read avail %d\n", __func__, sz); + if (sz <= 0) { + if (sz) + pr_err("%s: read avail failed %d\n", __func__, sz); + mutex_unlock(&sdio_mux_lock); + return; + } + + /* net_ip_aling is probably not required */ + if (sdio_partial_pkt.valid) + len = sdio_partial_pkt.skb->len; + + /* If allocation fails attempt to get a smaller chunk of mem */ + do { + skb_mux = __dev_alloc_skb(sz + NET_IP_ALIGN + len, GFP_KERNEL); + if (skb_mux) + break; + + pr_err("%s: cannot allocate skb of size:%d + " + "%d (NET_SKB_PAD)\n", __func__, + sz + NET_IP_ALIGN + len, NET_SKB_PAD); + /* the skb structure adds NET_SKB_PAD bytes to the memory + * request, which may push the actual request above PAGE_SIZE + * in that case, we need to iterate one more time to make sure + * we get the memory request under PAGE_SIZE + */ + if (sz + NET_IP_ALIGN + len + NET_SKB_PAD <= PAGE_SIZE) { + pr_err("%s: allocation failed\n", __func__); + mutex_unlock(&sdio_mux_lock); + return; + } + sz /= 2; + } while (1); + + skb_reserve(skb_mux, NET_IP_ALIGN + len); + ptr = skb_put(skb_mux, sz); + + /* half second wakelock is fine? */ + wake_lock_timeout(&sdio_mux_ch_wakelock, HZ / 2); + rc = sdio_read(sdio_mux_ch, ptr, sz); + DBG("%s: read %d\n", __func__, rc); + if (rc) { + pr_err("%s: sdio read failed %d\n", __func__, rc); + dev_kfree_skb_any(skb_mux); + mutex_unlock(&sdio_mux_lock); + queue_work(sdio_mux_workqueue, &work_sdio_mux_read); + return; + } + mutex_unlock(&sdio_mux_lock); + + DBG_INC_READ_CNT(sz); + DBG("%s: head %p data %p tail %p end %p len %d\n", __func__, + skb_mux->head, skb_mux->data, skb_mux->tail, + skb_mux->end, skb_mux->len); + + /* move to a separate function */ + /* probably do skb_pull instead of pointer adjustment */ + hdr = handle_sdio_partial_pkt(skb_mux); + while ((void *)hdr < (void *)skb_mux->tail) { + + if (((void *)hdr + sizeof(*hdr)) > (void *)skb_mux->tail) { + /* handle partial header */ + sdio_mux_save_partial_pkt(hdr, skb_mux); + break; + } + + if (hdr->magic_num != SDIO_MUX_HDR_MAGIC_NO) { + pr_err("%s: packet error\n", __func__); + break; + } + + hdr = handle_sdio_mux_command(hdr, skb_mux); + } + dev_kfree_skb_any(skb_mux); + + DBG("%s: read done\n", __func__); + queue_work(sdio_mux_workqueue, &work_sdio_mux_read); +} + +static int sdio_mux_write(struct sk_buff *skb) +{ + int rc, sz; + + mutex_lock(&sdio_mux_lock); + sz = sdio_write_avail(sdio_mux_ch); + DBG("%s: avail %d len %d\n", __func__, sz, skb->len); + if (skb->len <= sz) { + rc = sdio_write(sdio_mux_ch, skb->data, skb->len); + DBG("%s: write returned %d\n", __func__, rc); + if (rc == 0) + DBG_INC_WRITE_CNT(skb->len); + } else + rc = -ENOMEM; + + mutex_unlock(&sdio_mux_lock); + return rc; +} + +static int sdio_mux_write_cmd(void *data, uint32_t len) +{ + int avail, rc; + for (;;) { + mutex_lock(&sdio_mux_lock); + avail = sdio_write_avail(sdio_mux_ch); + DBG("%s: avail %d len %d\n", __func__, avail, len); + if (avail >= len) { + rc = sdio_write(sdio_mux_ch, data, len); + DBG("%s: write returned %d\n", __func__, rc); + if (!rc) { + DBG_INC_WRITE_CNT(len); + break; + } + } + mutex_unlock(&sdio_mux_lock); + msleep(250); + } + mutex_unlock(&sdio_mux_lock); + return 0; +} + +static void sdio_mux_send_open_cmd(uint32_t id) +{ + struct sdio_mux_hdr hdr = { + .magic_num = SDIO_MUX_HDR_MAGIC_NO, + .cmd = SDIO_MUX_HDR_CMD_OPEN, + .reserved = 0, + .ch_id = id, + .pkt_len = 0, + .pad_len = 0 + }; + + sdio_mux_write_cmd((void *)&hdr, sizeof(hdr)); +} + +static void sdio_mux_write_data(struct work_struct *work) +{ + int rc, reschedule = 0; + int notify = 0; + struct sk_buff *skb; + unsigned long flags; + int avail; + int ch_id; + + spin_lock_irqsave(&sdio_mux_write_lock, flags); + while ((skb = __skb_dequeue(&sdio_mux_write_pool))) { + ch_id = ((struct sdio_mux_hdr *)skb->data)->ch_id; + + avail = sdio_write_avail(sdio_mux_ch); + if (avail < skb->len) { + /* we may have to wait for write avail + * notification from sdio al + */ + DBG("%s: sdio_write_avail(%d) < skb->len(%d)\n", + __func__, avail, skb->len); + + reschedule = 1; + break; + } + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); + rc = sdio_mux_write(skb); + spin_lock_irqsave(&sdio_mux_write_lock, flags); + if (rc == 0) { + + spin_lock(&sdio_ch[ch_id].lock); + sdio_ch[ch_id].num_tx_pkts--; + spin_unlock(&sdio_ch[ch_id].lock); + + if (sdio_ch[ch_id].write_done) + sdio_ch[ch_id].write_done( + sdio_ch[ch_id].priv, skb); + else + dev_kfree_skb_any(skb); + } else if (rc == -EAGAIN || rc == -ENOMEM) { + /* recoverable error - retry again later */ + reschedule = 1; + break; + } else if (rc == -ENODEV) { + /* + * sdio_al suffered some kind of fatal error + * prevent future writes and clean up pending ones + */ + fatal_error = 1; + do { + ch_id = ((struct sdio_mux_hdr *) + skb->data)->ch_id; + spin_lock(&sdio_ch[ch_id].lock); + sdio_ch[ch_id].num_tx_pkts--; + spin_unlock(&sdio_ch[ch_id].lock); + dev_kfree_skb_any(skb); + } while ((skb = __skb_dequeue(&sdio_mux_write_pool))); + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); + return; + } else { + /* unknown error condition - drop the + * skb and reschedule for the + * other skb's + */ + pr_err("%s: sdio_mux_write error %d" + " for ch %d, skb=%p\n", + __func__, rc, ch_id, skb); + notify = 1; + break; + } + } + + if (reschedule) { + if (sdio_ch_is_in_reset(ch_id)) { + notify = 1; + } else { + __skb_queue_head(&sdio_mux_write_pool, skb); + queue_delayed_work(sdio_mux_workqueue, + &delayed_work_sdio_mux_write, + msecs_to_jiffies(250) + ); + } + } + + if (notify) { + spin_lock(&sdio_ch[ch_id].lock); + sdio_ch[ch_id].num_tx_pkts--; + spin_unlock(&sdio_ch[ch_id].lock); + + if (sdio_ch[ch_id].write_done) + sdio_ch[ch_id].write_done( + sdio_ch[ch_id].priv, skb); + else + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); +} + +int msm_sdio_is_channel_in_reset(uint32_t id) +{ + int rc = 0; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + if (sdio_ch_is_in_reset(id)) + rc = 1; + + return rc; +} + +int msm_sdio_dmux_write(uint32_t id, struct sk_buff *skb) +{ + int rc = 0; + struct sdio_mux_hdr *hdr; + unsigned long flags; + struct sk_buff *new_skb; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + if (!skb) + return -EINVAL; + if (!sdio_mux_initialized) + return -ENODEV; + if (fatal_error) + return -ENODEV; + + DBG("%s: writing to ch %d len %d\n", __func__, id, skb->len); + spin_lock_irqsave(&sdio_ch[id].lock, flags); + if (sdio_ch_is_in_reset(id)) { + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + pr_err("%s: port is in reset: %d\n", __func__, + sdio_ch[id].status); + return -ENETRESET; + } + if (!sdio_ch_is_local_open(id)) { + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + pr_err("%s: port not open: %d\n", __func__, sdio_ch[id].status); + return -ENODEV; + } + if (sdio_ch[id].use_wm && + (sdio_ch[id].num_tx_pkts >= HIGH_WATERMARK)) { + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + pr_err("%s: watermark exceeded: %d\n", __func__, id); + return -EAGAIN; + } + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + spin_lock_irqsave(&sdio_mux_write_lock, flags); + /* if skb do not have any tailroom for padding, + copy the skb into a new expanded skb */ + if ((skb->len & 0x3) && (skb_tailroom(skb) < (4 - (skb->len & 0x3)))) { + /* revisit, probably dev_alloc_skb and memcpy is effecient */ + new_skb = skb_copy_expand(skb, skb_headroom(skb), + 4 - (skb->len & 0x3), GFP_ATOMIC); + if (new_skb == NULL) { + pr_err("%s: cannot allocate skb\n", __func__); + rc = -ENOMEM; + goto write_done; + } + dev_kfree_skb_any(skb); + skb = new_skb; + DBG_INC_WRITE_CPY(skb->len); + } + + hdr = (struct sdio_mux_hdr *)skb_push(skb, sizeof(struct sdio_mux_hdr)); + + /* caller should allocate for hdr and padding + hdr is fine, padding is tricky */ + hdr->magic_num = SDIO_MUX_HDR_MAGIC_NO; + hdr->cmd = SDIO_MUX_HDR_CMD_DATA; + hdr->reserved = 0; + hdr->ch_id = id; + hdr->pkt_len = skb->len - sizeof(struct sdio_mux_hdr); + if (skb->len & 0x3) + skb_put(skb, 4 - (skb->len & 0x3)); + + hdr->pad_len = skb->len - (sizeof(struct sdio_mux_hdr) + hdr->pkt_len); + + DBG("%s: data %p, tail %p skb len %d pkt len %d pad len %d\n", + __func__, skb->data, skb->tail, skb->len, + hdr->pkt_len, hdr->pad_len); + __skb_queue_tail(&sdio_mux_write_pool, skb); + + spin_lock(&sdio_ch[id].lock); + sdio_ch[id].num_tx_pkts++; + spin_unlock(&sdio_ch[id].lock); + + queue_work(sdio_mux_workqueue, &work_sdio_mux_write); + +write_done: + spin_unlock_irqrestore(&sdio_mux_write_lock, flags); + return rc; +} + +int msm_sdio_dmux_open(uint32_t id, void *priv, + void (*receive_cb)(void *, struct sk_buff *), + void (*write_done)(void *, struct sk_buff *)) +{ + unsigned long flags; + + DBG("%s: opening ch %d\n", __func__, id); + if (!sdio_mux_initialized) + return -ENODEV; + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + spin_lock_irqsave(&sdio_ch[id].lock, flags); + if (sdio_ch_is_local_open(id)) { + pr_info("%s: Already opened %d\n", __func__, id); + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + goto open_done; + } + + sdio_ch[id].receive_cb = receive_cb; + sdio_ch[id].write_done = write_done; + sdio_ch[id].priv = priv; + sdio_ch[id].status |= SDIO_CH_LOCAL_OPEN; + sdio_ch[id].num_tx_pkts = 0; + sdio_ch[id].use_wm = 0; + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + sdio_mux_send_open_cmd(id); + +open_done: + pr_info("%s: opened ch %d\n", __func__, id); + return 0; +} + +int msm_sdio_dmux_close(uint32_t id) +{ + struct sdio_mux_hdr hdr; + unsigned long flags; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + DBG("%s: closing ch %d\n", __func__, id); + if (!sdio_mux_initialized) + return -ENODEV; + spin_lock_irqsave(&sdio_ch[id].lock, flags); + + sdio_ch[id].receive_cb = NULL; + sdio_ch[id].priv = NULL; + sdio_ch[id].status &= ~SDIO_CH_LOCAL_OPEN; + sdio_ch[id].status &= ~SDIO_CH_IN_RESET; + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + hdr.magic_num = SDIO_MUX_HDR_MAGIC_NO; + hdr.cmd = SDIO_MUX_HDR_CMD_CLOSE; + hdr.reserved = 0; + hdr.ch_id = id; + hdr.pkt_len = 0; + hdr.pad_len = 0; + + sdio_mux_write_cmd((void *)&hdr, sizeof(hdr)); + + pr_info("%s: closed ch %d\n", __func__, id); + return 0; +} + +static void sdio_mux_notify(void *_dev, unsigned event) +{ + DBG("%s: event %d notified\n", __func__, event); + + /* write avail may not be enouogh for a packet, but should be fine */ + if ((event == SDIO_EVENT_DATA_WRITE_AVAIL) && + sdio_write_avail(sdio_mux_ch)) + queue_work(sdio_mux_workqueue, &work_sdio_mux_write); + + if ((event == SDIO_EVENT_DATA_READ_AVAIL) && + sdio_read_avail(sdio_mux_ch)) + queue_work(sdio_mux_workqueue, &work_sdio_mux_read); +} + +int msm_sdio_dmux_is_ch_full(uint32_t id) +{ + unsigned long flags; + int ret; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + spin_lock_irqsave(&sdio_ch[id].lock, flags); + sdio_ch[id].use_wm = 1; + ret = sdio_ch[id].num_tx_pkts >= HIGH_WATERMARK; + DBG("%s: ch %d num tx pkts=%d, HWM=%d\n", __func__, + id, sdio_ch[id].num_tx_pkts, ret); + if (!sdio_ch_is_local_open(id)) { + ret = -ENODEV; + pr_err("%s: port not open: %d\n", __func__, sdio_ch[id].status); + } + spin_unlock_irqrestore(&sdio_ch[id].lock, flags); + + return ret; +} + +int msm_sdio_dmux_is_ch_low(uint32_t id) +{ + int ret; + + if (id >= SDIO_DMUX_NUM_CHANNELS) + return -EINVAL; + + sdio_ch[id].use_wm = 1; + ret = sdio_ch[id].num_tx_pkts <= LOW_WATERMARK; + DBG("%s: ch %d num tx pkts=%d, LWM=%d\n", __func__, + id, sdio_ch[id].num_tx_pkts, ret); + if (!sdio_ch_is_local_open(id)) { + ret = -ENODEV; + pr_err("%s: port not open: %d\n", __func__, sdio_ch[id].status); + } + + return ret; +} + +#ifdef CONFIG_DEBUG_FS + +static int debug_tbl(char *buf, int max) +{ + int i = 0; + int j; + + for (j = 0; j < SDIO_DMUX_NUM_CHANNELS; ++j) { + i += scnprintf(buf + i, max - i, + "ch%02d local open=%s remote open=%s\n", + j, sdio_ch_is_local_open(j) ? "Y" : "N", + sdio_ch_is_remote_open(j) ? "Y" : "N"); + } + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +#endif + +static int sdio_dmux_probe(struct platform_device *pdev) +{ + int rc; + + DBG("%s probe called\n", __func__); + + if (!sdio_mux_initialized) { + sdio_mux_workqueue = create_singlethread_workqueue("sdio_dmux"); + if (!sdio_mux_workqueue) + return -ENOMEM; + + skb_queue_head_init(&sdio_mux_write_pool); + spin_lock_init(&sdio_mux_write_lock); + + for (rc = 0; rc < SDIO_DMUX_NUM_CHANNELS; ++rc) + spin_lock_init(&sdio_ch[rc].lock); + + + wake_lock_init(&sdio_mux_ch_wakelock, WAKE_LOCK_SUSPEND, + "sdio_dmux"); + } + + rc = sdio_open("SDIO_RMNT", &sdio_mux_ch, NULL, sdio_mux_notify); + if (rc < 0) { + pr_err("%s: sido open failed %d\n", __func__, rc); + wake_lock_destroy(&sdio_mux_ch_wakelock); + destroy_workqueue(sdio_mux_workqueue); + sdio_mux_initialized = 0; + return rc; + } + + fatal_error = 0; + sdio_mux_initialized = 1; + return 0; +} + +static int sdio_dmux_remove(struct platform_device *pdev) +{ + int i; + unsigned long ch_lock_flags; + unsigned long write_lock_flags; + struct sk_buff *skb; + + DBG("%s remove called\n", __func__); + if (!sdio_mux_initialized) + return 0; + + /* set reset state for any open channels */ + for (i = 0; i < SDIO_DMUX_NUM_CHANNELS; ++i) { + spin_lock_irqsave(&sdio_ch[i].lock, ch_lock_flags); + if (sdio_ch_is_open(i)) { + sdio_ch[i].status |= SDIO_CH_IN_RESET; + sdio_ch[i].status &= ~SDIO_CH_REMOTE_OPEN; + + /* notify client so it can update its status */ + if (sdio_ch[i].receive_cb) + sdio_ch[i].receive_cb( + sdio_ch[i].priv, NULL); + } + spin_unlock_irqrestore(&sdio_ch[i].lock, ch_lock_flags); + } + + /* cancel any pending writes */ + spin_lock_irqsave(&sdio_mux_write_lock, write_lock_flags); + while ((skb = __skb_dequeue(&sdio_mux_write_pool))) { + i = ((struct sdio_mux_hdr *)skb->data)->ch_id; + if (sdio_ch[i].write_done) + sdio_ch[i].write_done( + sdio_ch[i].priv, skb); + else + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&sdio_mux_write_lock, + write_lock_flags); + + return 0; +} + +static struct platform_driver sdio_dmux_driver = { + .probe = sdio_dmux_probe, + .remove = sdio_dmux_remove, + .driver = { + .name = "SDIO_RMNT", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_dmux_init(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *dent; + + dent = debugfs_create_dir("sdio_dmux", 0); + if (!IS_ERR(dent)) + debug_create("tbl", 0444, dent, debug_tbl); +#endif + return platform_driver_register(&sdio_dmux_driver); +} + +module_init(sdio_dmux_init); +MODULE_DESCRIPTION("MSM SDIO DMUX"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_smem.c b/arch/arm/mach-msm/sdio_smem.c new file mode 100644 index 00000000000..4416a7909b2 --- /dev/null +++ b/arch/arm/mach-msm/sdio_smem.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include + +static void sdio_smem_read(struct work_struct *work); + +static struct sdio_channel *channel; +static struct workqueue_struct *workq; +static DECLARE_WORK(work_read, sdio_smem_read); +static DECLARE_WAIT_QUEUE_HEAD(waitq); +static int bytes_avail; +static int sdio_ch_opened; + +static void sdio_smem_release(struct device *dev) +{ + pr_debug("sdio smem released\n"); +} + +static struct sdio_smem_client client; + +static void sdio_smem_read(struct work_struct *work) +{ + int err; + int read_avail; + char *data = client.buf; + + if (!sdio_ch_opened) + return; + + read_avail = sdio_read_avail(channel); + if (read_avail > bytes_avail || + read_avail < 0) { + pr_err("Error: read_avail=%d bytes_avail=%d\n", + read_avail, bytes_avail); + goto read_err; + } + + if (read_avail == 0) + return; + + err = sdio_read(channel, + &data[client.size - bytes_avail], + read_avail); + if (err) { + pr_err("sdio_read error (%d)", err); + goto read_err; + } + + bytes_avail -= read_avail; + pr_debug("read %d bytes (bytes_avail = %d)\n", + read_avail, bytes_avail); + + if (!bytes_avail) { + bytes_avail = client.size; + err = client.cb_func(SDIO_SMEM_EVENT_READ_DONE); + } + if (err) + pr_err("error (%d) on callback\n", err); + + return; + +read_err: + if (sdio_ch_opened) + client.cb_func(SDIO_SMEM_EVENT_READ_ERR); + return; +} + +static void sdio_smem_notify(void *priv, unsigned event) +{ + pr_debug("%d event received\n", event); + + if (event == SDIO_EVENT_DATA_READ_AVAIL || + event == SDIO_EVENT_DATA_WRITE_AVAIL) + queue_work(workq, &work_read); +} + +int sdio_smem_register_client(void) +{ + int err = 0; + + if (!client.buf || !client.size || !client.cb_func) + return -EINVAL; + + pr_debug("buf = %p\n", client.buf); + pr_debug("size = 0x%x\n", client.size); + + bytes_avail = client.size; + workq = create_singlethread_workqueue("sdio_smem"); + if (!workq) + return -ENOMEM; + + sdio_ch_opened = 1; + err = sdio_open("SDIO_SMEM", &channel, NULL, sdio_smem_notify); + if (err) { + sdio_ch_opened = 0; + pr_err("sdio_open error (%d)\n", err); + destroy_workqueue(workq); + return err; + } + pr_debug("SDIO SMEM channel opened\n"); + return err; +} + +int sdio_smem_unregister_client(void) +{ + int err = 0; + + sdio_ch_opened = 0; + err = sdio_close(channel); + if (err) { + pr_err("sdio_close error (%d)\n", err); + return err; + } + pr_debug("SDIO SMEM channel closed\n"); + flush_workqueue(workq); + destroy_workqueue(workq); + bytes_avail = 0; + client.buf = NULL; + client.cb_func = NULL; + client.size = 0; + + return 0; +} + +static int sdio_smem_probe(struct platform_device *pdev) +{ + client.plat_dev.name = "SDIO_SMEM_CLIENT"; + client.plat_dev.id = -1; + client.plat_dev.dev.release = sdio_smem_release; + + return platform_device_register(&client.plat_dev); +} + +static int sdio_smem_remove(struct platform_device *pdev) +{ + platform_device_unregister(&client.plat_dev); + memset(&client, 0, sizeof(client)); + sdio_ch_opened = 0; + return 0; +} +static struct platform_driver sdio_smem_drv = { + .probe = sdio_smem_probe, + .remove = sdio_smem_remove, + .driver = { + .name = "SDIO_SMEM", + .owner = THIS_MODULE, + }, +}; + +static int __init sdio_smem_init(void) +{ + return platform_driver_register(&sdio_smem_drv); +}; + +module_init(sdio_smem_init); + +MODULE_DESCRIPTION("SDIO SMEM"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sdio_tty.c b/arch/arm/mach-msm/sdio_tty.c new file mode 100644 index 00000000000..41bc27006bc --- /dev/null +++ b/arch/arm/mach-msm/sdio_tty.c @@ -0,0 +1,824 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INPUT_SPEED 4800 +#define OUTPUT_SPEED 4800 +#define SDIO_TTY_MODULE_NAME "sdio_tty" +#define SDIO_TTY_MAX_PACKET_SIZE 4096 +#define MAX_SDIO_TTY_DRV 1 +#define MAX_SDIO_TTY_DEVS 2 +#define MAX_SDIO_TTY_DEV_NAME_SIZE 25 + +/* Configurations per channel device */ +/* CSVT */ +#define SDIO_TTY_CSVT_DEV "sdio_tty_csvt_0" +#define SDIO_TTY_CSVT_TEST_DEV "sdio_tty_csvt_test_0" +#define SDIO_TTY_CH_CSVT "SDIO_CSVT" + +enum sdio_tty_state { + TTY_INITIAL = 0, + TTY_REGISTERED = 1, + TTY_OPENED = 2, + TTY_CLOSED = 3, +}; + +enum sdio_tty_devices { + SDIO_CSVT, + SDIO_CSVT_TEST_APP, +}; + +static const struct platform_device_id sdio_tty_id_table[] = { + { "SDIO_CSVT", SDIO_CSVT }, + { "SDIO_CSVT_TEST_APP", SDIO_CSVT_TEST_APP }, + { }, +}; +MODULE_DEVICE_TABLE(platform, sdio_tty_id_table); + +struct sdio_tty { + struct sdio_channel *ch; + char *sdio_ch_name; + char tty_dev_name[MAX_SDIO_TTY_DEV_NAME_SIZE]; + int device_id; + struct workqueue_struct *workq; + struct work_struct work_read; + wait_queue_head_t waitq; + struct tty_driver *tty_drv; + struct tty_struct *tty_str; + int debug_msg_on; + char *read_buf; + enum sdio_tty_state sdio_tty_state; + int is_sdio_open; + int tty_open_count; + int total_rx; + int total_tx; +}; + +static struct sdio_tty *sdio_tty[MAX_SDIO_TTY_DEVS]; + +#ifdef CONFIG_DEBUG_FS +struct dentry *sdio_tty_debug_root; +struct dentry *sdio_tty_debug_info; +#endif + +#define DEBUG_MSG(sdio_tty_drv, x...) if (sdio_tty_drv->debug_msg_on) pr_info(x) + +/* + * Enable sdio_tty debug messages + * By default the sdio_tty debug messages are turned off + */ +static int csvt_debug_msg_on; +module_param(csvt_debug_msg_on, int, 0); + +static void sdio_tty_read(struct work_struct *work) +{ + int ret = 0; + int read_avail = 0; + int left = 0; + int total_push = 0; + int num_push = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + sdio_tty_drv = container_of(work, struct sdio_tty, work_read); + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty", __func__); + return ; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return; + } + + if (!sdio_tty_drv->read_buf) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL read_buf for dev %s", + __func__, sdio_tty_drv->tty_dev_name); + return; + } + + /* Read the data from the SDIO channel as long as there is available + data */ + while (1) { + if (test_bit(TTY_THROTTLED, &sdio_tty_drv->tty_str->flags)) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME + ": %s: TTY_THROTTLED bit is set for " + "dev %s, exit", __func__, + sdio_tty_drv->tty_dev_name); + return; + } + + total_push = 0; + read_avail = sdio_read_avail(sdio_tty_drv->ch); + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME + ": %s: read_avail is %d for dev %s", __func__, + read_avail, sdio_tty_drv->tty_dev_name); + + if (read_avail == 0) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME + ": %s: read_avail is 0 for dev %s", + __func__, sdio_tty_drv->tty_dev_name); + return; + } + + if (read_avail > SDIO_TTY_MAX_PACKET_SIZE) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: read_avail(%d) is " + "bigger than SDIO_TTY_MAX_PACKET_SIZE(%d) " + "for dev %s", __func__, read_avail, + SDIO_TTY_MAX_PACKET_SIZE, + sdio_tty_drv->tty_dev_name); + return; + } + + ret = sdio_read(sdio_tty_drv->ch, + sdio_tty_drv->read_buf, + read_avail); + if (ret < 0) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_read error(%d) " + "for dev %s", __func__, ret, + sdio_tty_drv->tty_dev_name); + return; + } + + left = read_avail; + do { + num_push = tty_insert_flip_string( + sdio_tty_drv->tty_str, + sdio_tty_drv->read_buf+total_push, + left); + total_push += num_push; + left -= num_push; + tty_flip_buffer_push(sdio_tty_drv->tty_str); + } while (left != 0); + + if (total_push != read_avail) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed, total_push" + "(%d) != read_avail(%d) for dev %s\n", + __func__, total_push, read_avail, + sdio_tty_drv->tty_dev_name); + } + + tty_flip_buffer_push(sdio_tty_drv->tty_str); + sdio_tty_drv->total_rx += read_avail; + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Rx: %d, " + "Total Rx = %d bytes for dev %s", __func__, + read_avail, sdio_tty_drv->total_rx, + sdio_tty_drv->tty_dev_name); + } +} + +/** + * sdio_tty_write_room + * + * This is the write_room function of the tty driver. + * + * @tty: pointer to tty struct. + * @return free bytes for write. + * + */ +static int sdio_tty_write_room(struct tty_struct *tty) +{ + int write_avail = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return -ENODEV; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return -EPERM; + } + + write_avail = sdio_write_avail(sdio_tty_drv->ch); + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: write_avail=%d " + "for dev %s", __func__, write_avail, + sdio_tty_drv->tty_dev_name); + + return write_avail; +} + +/** + * sdio_tty_write_callback + * this is the write callback of the tty driver. + * + * @tty: pointer to tty struct. + * @buf: buffer to write from. + * @count: number of bytes to write. + * @return bytes written or negative value on error. + * + * if destination buffer has not enough room for the incoming + * data, writes the possible amount of bytes . + */ +static int sdio_tty_write_callback(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + int write_avail = 0; + int len = count; + int ret = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return -ENODEV; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return -EPERM; + } + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Write Callback " + "called with %d bytes for dev %s\n", __func__, count, + sdio_tty_drv->tty_dev_name); + write_avail = sdio_write_avail(sdio_tty_drv->ch); + if (write_avail == 0) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: " + "write_avail is 0 for dev %s\n", + __func__, sdio_tty_drv->tty_dev_name); + return 0; + } + if (write_avail > SDIO_TTY_MAX_PACKET_SIZE) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: " + "write_avail(%d) is bigger than max packet " + "size(%d) for dev %s, setting to " + "max_packet_size\n", __func__, write_avail, + SDIO_TTY_MAX_PACKET_SIZE, + sdio_tty_drv->tty_dev_name); + write_avail = SDIO_TTY_MAX_PACKET_SIZE; + } + if (write_avail < count) { + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: " + "write_avail(%d) is smaller than required(%d) " + "for dev %s, writing only %d bytes\n", + __func__, write_avail, count, + sdio_tty_drv->tty_dev_name, write_avail); + len = write_avail; + } + ret = sdio_write(sdio_tty_drv->ch, buf, len); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_write failed for " + "dev %s, ret=%d\n", __func__, + sdio_tty_drv->tty_dev_name, ret); + return 0; + } + + sdio_tty_drv->total_tx += len; + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Tx: %d, " + "Total Tx = %d for dev %s", __func__, len, + sdio_tty_drv->total_tx, sdio_tty_drv->tty_dev_name); + return len; +} + +static void sdio_tty_notify(void *priv, unsigned event) +{ + struct sdio_tty *sdio_tty_drv = priv; + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return; + } + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: event %d " + "received for dev %s\n", __func__, event, + sdio_tty_drv->tty_dev_name); + + if (event == SDIO_EVENT_DATA_READ_AVAIL) + queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read); +} + +/** + * sdio_tty_open + * This is the open callback of the tty driver. it opens + * the sdio channel, and creates the workqueue. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return 0 on success or negative value on error. + */ +static int sdio_tty_open(struct tty_struct *tty, struct file *file) +{ + int ret = 0; + int i = 0; + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return -ENODEV; + } + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + if (!strncmp(sdio_tty[i]->tty_dev_name, tty->name, + MAX_SDIO_TTY_DEV_NAME_SIZE)) { + sdio_tty_drv = sdio_tty[i]; + break; + } + } + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + sdio_tty_drv->tty_open_count++; + if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) is already open", + __func__, sdio_tty_drv->tty_dev_name); + return -EBUSY; + } + + tty->driver_data = sdio_tty_drv; + + sdio_tty_drv->tty_str = tty; + sdio_tty_drv->tty_str->low_latency = 1; + sdio_tty_drv->tty_str->icanon = 0; + set_bit(TTY_NO_WRITE_SPLIT, &sdio_tty_drv->tty_str->flags); + + sdio_tty_drv->read_buf = kzalloc(SDIO_TTY_MAX_PACKET_SIZE, GFP_KERNEL); + if (sdio_tty_drv->read_buf == NULL) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate read_buf " + "for dev %s", __func__, sdio_tty_drv->tty_dev_name); + return -ENOMEM; + } + + sdio_tty_drv->workq = create_singlethread_workqueue("sdio_tty_read"); + if (!sdio_tty_drv->workq) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to create workq " + "for dev %s", __func__, sdio_tty_drv->tty_dev_name); + return -ENOMEM; + } + + if (!sdio_tty_drv->is_sdio_open) { + ret = sdio_open(sdio_tty_drv->sdio_ch_name, &sdio_tty_drv->ch, + sdio_tty_drv, sdio_tty_notify); + if (ret < 0) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_open err=%d " + "for dev %s\n", __func__, ret, + sdio_tty_drv->tty_dev_name); + destroy_workqueue(sdio_tty_drv->workq); + return ret; + } + + pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) " + "opened\n", __func__, sdio_tty_drv->sdio_ch_name); + + sdio_tty_drv->is_sdio_open = 1; + } else { + /* If SDIO channel is already open try to read the data + * from the modem + */ + queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read); + + } + + sdio_tty_drv->sdio_tty_state = TTY_OPENED; + + pr_info(SDIO_TTY_MODULE_NAME ": %s: TTY device(%s) opened\n", + __func__, sdio_tty_drv->tty_dev_name); + + return ret; +} + +/** + * sdio_tty_close + * This is the close callback of the tty driver. it requests + * the main thread to exit, and waits for notification of it. + * it also de-allocates the buffers, and unregisters the tty + * driver and device. + * + * @tty: a pointer to the tty struct. + * @file: file descriptor. + * @return None. + */ +static void sdio_tty_close(struct tty_struct *tty, struct file *file) +{ + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return; + } + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: trying to close a " + "TTY device that was not opened\n", __func__); + return; + } + if (--sdio_tty_drv->tty_open_count != 0) + return; + + flush_workqueue(sdio_tty_drv->workq); + destroy_workqueue(sdio_tty_drv->workq); + + kfree(sdio_tty_drv->read_buf); + sdio_tty_drv->read_buf = NULL; + + sdio_tty_drv->sdio_tty_state = TTY_CLOSED; + + pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY device(%s) closed\n", + __func__, sdio_tty_drv->tty_dev_name); +} + +static void sdio_tty_unthrottle(struct tty_struct *tty) +{ + struct sdio_tty *sdio_tty_drv = NULL; + + if (!tty) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__); + return; + } + sdio_tty_drv = tty->driver_data; + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d", + __func__, sdio_tty_drv->sdio_tty_state); + return; + } + + queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read); + return; +} + +static const struct tty_operations sdio_tty_ops = { + .open = sdio_tty_open, + .close = sdio_tty_close, + .write = sdio_tty_write_callback, + .write_room = sdio_tty_write_room, + .unthrottle = sdio_tty_unthrottle, +}; + +int sdio_tty_init_tty(char *tty_name, char *sdio_ch_name, + enum sdio_tty_devices device_id, int debug_msg_on) +{ + int ret = 0; + int i = 0; + struct device *tty_dev = NULL; + struct sdio_tty *sdio_tty_drv = NULL; + + sdio_tty_drv = kzalloc(sizeof(struct sdio_tty), GFP_KERNEL); + if (sdio_tty_drv == NULL) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate sdio_tty " + "for dev %s", __func__, tty_name); + return -ENOMEM; + } + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) { + sdio_tty[i] = sdio_tty_drv; + break; + } + } + + if (i == MAX_SDIO_TTY_DEVS) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) creation failed," + " max limit(%d) reached.", __func__, tty_name, + MAX_SDIO_TTY_DEVS); + kfree(sdio_tty_drv); + return -ENODEV; + } + + snprintf(sdio_tty_drv->tty_dev_name, MAX_SDIO_TTY_DEV_NAME_SIZE, + "%s%d", tty_name, 0); + sdio_tty_drv->sdio_ch_name = sdio_ch_name; + sdio_tty_drv->device_id = device_id; + pr_info(SDIO_TTY_MODULE_NAME ": %s: dev=%s, id=%d, channel=%s\n", + __func__, sdio_tty_drv->tty_dev_name, sdio_tty_drv->device_id, + sdio_tty_drv->sdio_ch_name); + + INIT_WORK(&sdio_tty_drv->work_read, sdio_tty_read); + + sdio_tty_drv->tty_drv = alloc_tty_driver(MAX_SDIO_TTY_DRV); + + if (!sdio_tty_drv->tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s - tty_drv is NULL for dev %s", + __func__, sdio_tty_drv->tty_dev_name); + kfree(sdio_tty_drv); + return -ENODEV; + } + + sdio_tty_drv->tty_drv->name = tty_name; + sdio_tty_drv->tty_drv->owner = THIS_MODULE; + sdio_tty_drv->tty_drv->driver_name = "SDIO_tty"; + /* uses dynamically assigned dev_t values */ + sdio_tty_drv->tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + sdio_tty_drv->tty_drv->subtype = SERIAL_TYPE_NORMAL; + sdio_tty_drv->tty_drv->flags = TTY_DRIVER_REAL_RAW + | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_RESET_TERMIOS; + + /* initializing the tty driver */ + sdio_tty_drv->tty_drv->init_termios = tty_std_termios; + sdio_tty_drv->tty_drv->init_termios.c_cflag = + B4800 | CS8 | CREAD | HUPCL | CLOCAL; + sdio_tty_drv->tty_drv->init_termios.c_ispeed = INPUT_SPEED; + sdio_tty_drv->tty_drv->init_termios.c_ospeed = OUTPUT_SPEED; + + tty_set_operations(sdio_tty_drv->tty_drv, &sdio_tty_ops); + + ret = tty_register_driver(sdio_tty_drv->tty_drv); + if (ret) { + put_tty_driver(sdio_tty_drv->tty_drv); + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_driver() " + "failed for dev %s\n", __func__, + sdio_tty_drv->tty_dev_name); + + sdio_tty_drv->tty_drv = NULL; + kfree(sdio_tty_drv); + return -ENODEV; + } + + tty_dev = tty_register_device(sdio_tty_drv->tty_drv, 0, NULL); + if (IS_ERR(tty_dev)) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_device() " + "failed for dev %s\n", __func__, + sdio_tty_drv->tty_dev_name); + tty_unregister_driver(sdio_tty_drv->tty_drv); + put_tty_driver(sdio_tty_drv->tty_drv); + kfree(sdio_tty_drv); + return -ENODEV; + } + + sdio_tty_drv->sdio_tty_state = TTY_REGISTERED; + if (debug_msg_on) { + pr_info(SDIO_TTY_MODULE_NAME ": %s: turn on debug msg for %s", + __func__, sdio_tty_drv->tty_dev_name); + sdio_tty_drv->debug_msg_on = debug_msg_on; + } + return 0; +} + +int sdio_tty_uninit_tty(void *sdio_tty_handle) +{ + int ret = 0; + int i = 0; + struct sdio_tty *sdio_tty_drv = sdio_tty_handle; + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) { + flush_workqueue(sdio_tty_drv->workq); + destroy_workqueue(sdio_tty_drv->workq); + + kfree(sdio_tty_drv->read_buf); + sdio_tty_drv->read_buf = NULL; + } + + if (sdio_tty_drv->sdio_tty_state != TTY_INITIAL) { + tty_unregister_device(sdio_tty_drv->tty_drv, 0); + + ret = tty_unregister_driver(sdio_tty_drv->tty_drv); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: " + "tty_unregister_driver() failed for dev %s\n", + __func__, sdio_tty_drv->tty_dev_name); + } + put_tty_driver(sdio_tty_drv->tty_drv); + sdio_tty_drv->sdio_tty_state = TTY_INITIAL; + sdio_tty_drv->tty_drv = NULL; + } + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + if (sdio_tty[i]->device_id == sdio_tty_drv->device_id) { + sdio_tty[i] = NULL; + break; + } + } + + DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Freeing sdio_tty " + "structure, dev=%s", __func__, + sdio_tty_drv->tty_dev_name); + kfree(sdio_tty_drv); + + return 0; +} + +static int sdio_tty_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + enum sdio_tty_devices device_id = id->driver_data; + char *device_name = NULL; + char *channel_name = NULL; + int debug_msg_on = 0; + int ret = 0; + + pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name); + + switch (device_id) { + case SDIO_CSVT: + device_name = SDIO_TTY_CSVT_DEV; + channel_name = SDIO_TTY_CH_CSVT; + debug_msg_on = csvt_debug_msg_on; + break; + case SDIO_CSVT_TEST_APP: + device_name = SDIO_TTY_CSVT_TEST_DEV; + channel_name = SDIO_TTY_CH_CSVT; + debug_msg_on = csvt_debug_msg_on; + break; + default: + pr_err(SDIO_TTY_MODULE_NAME ": %s Invalid device:%s, id:%d", + __func__, pdev->name, device_id); + ret = -ENODEV; + break; + } + + if (device_name) { + ret = sdio_tty_init_tty(device_name, channel_name, + device_id, debug_msg_on); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_init_tty " + "failed for dev:%s", __func__, device_name); + } + } + return ret; +} + +static int sdio_tty_remove(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + enum sdio_tty_devices device_id = id->driver_data; + struct sdio_tty *sdio_tty_drv = NULL; + int i = 0; + int ret = 0; + + pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name); + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + if (sdio_tty[i]->device_id == device_id) { + sdio_tty_drv = sdio_tty[i]; + break; + } + } + + if (!sdio_tty_drv) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv", + __func__); + return -ENODEV; + } + + ret = sdio_tty_uninit_tty(sdio_tty_drv); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_uninit_tty " + "failed for %s", __func__, pdev->name); + } + return ret; +} + +static struct platform_driver sdio_tty_pdrv = { + .probe = sdio_tty_probe, + .remove = sdio_tty_remove, + .id_table = sdio_tty_id_table, + .driver = { + .name = "SDIO_TTY", + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_DEBUG_FS +void sdio_tty_print_info(void) +{ + int i = 0; + + for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) { + if (sdio_tty[i] == NULL) + continue; + pr_info(SDIO_TTY_MODULE_NAME ": %s: Total Rx=%d, Tx = %d " + "for dev %s", __func__, sdio_tty[i]->total_rx, + sdio_tty[i]->total_tx, sdio_tty[i]->tty_dev_name); + } +} + +static int tty_debug_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t tty_debug_info_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + sdio_tty_print_info(); + return count; +} + +const struct file_operations tty_debug_info_ops = { + .open = tty_debug_info_open, + .write = tty_debug_info_write, +}; +#endif + +/* + * Module Init. + * + * Register SDIO TTY driver. + * + */ +static int __init sdio_tty_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&sdio_tty_pdrv); + if (ret) { + pr_err(SDIO_TTY_MODULE_NAME ": %s: platform_driver_register " + "failed", __func__); + } +#ifdef CONFIG_DEBUG_FS + else { + sdio_tty_debug_root = debugfs_create_dir("sdio_tty", NULL); + if (sdio_tty_debug_root) { + sdio_tty_debug_info = debugfs_create_file( + "sdio_tty_debug", + S_IRUGO | S_IWUGO, + sdio_tty_debug_root, + NULL, + &tty_debug_info_ops); + } + } +#endif + return ret; +}; + +/* + * Module Exit. + * + * Unregister SDIO TTY driver. + * + */ +static void __exit sdio_tty_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + debugfs_remove(sdio_tty_debug_info); + debugfs_remove(sdio_tty_debug_root); +#endif + platform_driver_unregister(&sdio_tty_pdrv); +} + +module_init(sdio_tty_init); +module_exit(sdio_tty_exit); + +MODULE_DESCRIPTION("SDIO TTY"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Maya Erez "); diff --git a/arch/arm/mach-msm/sirc-fsm9xxx.c b/arch/arm/mach-msm/sirc-fsm9xxx.c new file mode 100644 index 00000000000..71fa60ae22b --- /dev/null +++ b/arch/arm/mach-msm/sirc-fsm9xxx.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "sirc.h" + +static unsigned int sirc_int_enable[2]; + +static struct sirc_regs_t sirc_regs = { + .int_enable = SPSS_SIRC_INT_ENABLE, + .int_type = SPSS_SIRC_INT_TYPE, + .int_polarity = SPSS_SIRC_INT_POLARITY, + .int_clear = SPSS_SIRC_INT_CLEAR, +}; + +static inline void sirc_get_group_offset_mask(unsigned int irq, + unsigned int *group, unsigned int *offset, unsigned int *mask) +{ + *group = 0; + *offset = irq - FIRST_SIRC_IRQ; + if (*offset >= NR_SIRC_IRQS_GROUPA) { + *group = 1; + *offset -= NR_SIRC_IRQS_GROUPA; + } + *mask = 1 << *offset; +} + +static void sirc_irq_mask(struct irq_data *d) +{ + void *reg_enable; + unsigned int group, offset, mask; + unsigned int val; + + sirc_get_group_offset_mask(d->irq, &group, &offset, &mask); + + reg_enable = sirc_regs.int_enable + group * 4; + val = __raw_readl(reg_enable); + __raw_writel(val & ~mask, reg_enable); + sirc_int_enable[group] &= ~mask; + mb(); +} + +static void sirc_irq_unmask(struct irq_data *d) +{ + void *reg_enable; + void *reg_clear; + unsigned int group, offset, mask; + unsigned int val; + + sirc_get_group_offset_mask(d->irq, &group, &offset, &mask); + + if (irq_desc[d->irq].handle_irq == handle_level_irq) { + reg_clear = sirc_regs.int_clear + group * 4; + __raw_writel(mask, reg_clear); + } + + reg_enable = sirc_regs.int_enable + group * 4; + val = __raw_readl(reg_enable); + __raw_writel(val | mask, reg_enable); + sirc_int_enable[group] |= mask; + mb(); +} + +static void sirc_irq_ack(struct irq_data *d) +{ + void *reg_clear; + unsigned int group, offset, mask; + + sirc_get_group_offset_mask(d->irq, &group, &offset, &mask); + + reg_clear = sirc_regs.int_clear + group * 4; + __raw_writel(mask, reg_clear); +} + +static int sirc_irq_set_wake(struct irq_data *d, unsigned int on) +{ + return 0; +} + +static int sirc_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + void *reg_polarity, *reg_type; + unsigned int group, offset, mask; + unsigned int val; + + sirc_get_group_offset_mask(d->irq, &group, &offset, &mask); + + reg_polarity = sirc_regs.int_polarity + group * 4; + val = __raw_readl(reg_polarity); + + if (flow_type & (IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING)) + val &= ~mask; + else + val |= mask; + + __raw_writel(val, reg_polarity); + + reg_type = sirc_regs.int_type + group * 4; + val = __raw_readl(reg_type); + + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + val |= mask; + irq_desc[d->irq].handle_irq = handle_edge_irq; + } else { + val &= ~mask; + irq_desc[d->irq].handle_irq = handle_level_irq; + } + + __raw_writel(val, reg_type); + + return 0; +} + +/* Finds the pending interrupt on the passed cascade irq and redrives it */ +static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned int sirq; + + for (;;) { + sirq = __raw_readl(SPSS_SIRC_VEC_INDEX_RD); + if (sirq >= NR_SIRC_IRQS) + break; + + generic_handle_irq(sirq + FIRST_SIRC_IRQ); + } + + irq_desc_get_chip(desc)->irq_ack(irq_get_irq_data(irq)); +} + +static struct irq_chip sirc_irq_chip = { + .name = "sirc", + .irq_ack = sirc_irq_ack, + .irq_mask = sirc_irq_mask, + .irq_unmask = sirc_irq_unmask, + .irq_set_wake = sirc_irq_set_wake, + .irq_set_type = sirc_irq_set_type, +}; + +void __init msm_init_sirc(void) +{ + int i; + + sirc_int_enable[0] = 0; + sirc_int_enable[1] = 0; + + for (i = FIRST_SIRC_IRQ; i <= LAST_SIRC_IRQ; i++) { + irq_set_chip_and_handler(i, &sirc_irq_chip, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + irq_set_chained_handler(INT_SIRC_0, sirc_irq_handler); + irq_set_irq_wake(INT_SIRC_0, 1); +} diff --git a/arch/arm/mach-msm/sirc.c b/arch/arm/mach-msm/sirc.c index 689e78c95f3..04124c5dcbf 100644 --- a/arch/arm/mach-msm/sirc.c +++ b/arch/arm/mach-msm/sirc.c @@ -1,25 +1,29 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +/* linux/arch/arm/mach-msm/irq.c * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. + * Copyright (c) 2009-2011 Code Aurora Forum. All rights reserved. + * Copyright (C) 2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. * * 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * */ #include #include #include +#include #include +#include +#include +#include + +#include "sirc.h" static unsigned int int_enable; static unsigned int wake_enable; @@ -37,9 +41,13 @@ static struct sirc_cascade_regs sirc_reg_table[] = { { .int_status = SPSS_SIRC_IRQ_STATUS, .cascade_irq = INT_SIRC_0, + .cascade_fiq = INT_SIRC_1, } }; +static unsigned int save_type; +static unsigned int save_polarity; + /* Mask off the given interrupt. Keep the int_enable mask in sync with the enable reg, so it can be restored after power collapse. */ static void sirc_irq_mask(struct irq_data *d) @@ -49,6 +57,7 @@ static void sirc_irq_mask(struct irq_data *d) mask = 1 << (d->irq - FIRST_SIRC_IRQ); writel(mask, sirc_regs.int_enable_clear); int_enable &= ~mask; + mb(); return; } @@ -60,6 +69,7 @@ static void sirc_irq_unmask(struct irq_data *d) mask = 1 << (d->irq - FIRST_SIRC_IRQ); writel(mask, sirc_regs.int_enable_set); + mb(); int_enable |= mask; return; } @@ -70,6 +80,7 @@ static void sirc_irq_ack(struct irq_data *d) mask = 1 << (d->irq - FIRST_SIRC_IRQ); writel(mask, sirc_regs.int_clear); + mb(); return; } @@ -105,17 +116,35 @@ static int sirc_irq_set_type(struct irq_data *d, unsigned int flow_type) val = readl(sirc_regs.int_type); if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { val |= mask; - __irq_set_handler_locked(d->irq, handle_edge_irq); } else { val &= ~mask; - __irq_set_handler_locked(d->irq, handle_level_irq); } writel(val, sirc_regs.int_type); + mb(); return 0; } +#if defined(CONFIG_MSM_FIQ_SUPPORT) +void sirc_fiq_select(int irq, bool enable) +{ + uint32_t mask = 1 << (irq - FIRST_SIRC_IRQ); + uint32_t val; + unsigned long flags; + + local_irq_save(flags); + val = readl(SPSS_SIRC_INT_SELECT); + if (enable) + val |= mask; + else + val &= ~mask; + writel(val, SPSS_SIRC_INT_SELECT); + mb(); + local_irq_restore(flags); +} +#endif + /* Finds the pending interrupt on the passed cascade irq and redrives it */ static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) { @@ -127,6 +156,12 @@ static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) (sirc_reg_table[reg].cascade_irq != irq)) reg++; + if (reg == ARRAY_SIZE(sirc_reg_table)) { + printk(KERN_ERR "%s: incorrect irq %d called\n", + __func__, irq); + return; + } + status = readl(sirc_reg_table[reg].int_status); status &= SIRC_MASK; if (status == 0) @@ -138,16 +173,34 @@ static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) ; generic_handle_irq(sirq+FIRST_SIRC_IRQ); - desc->irq_data.chip->irq_ack(&desc->irq_data); + irq_desc_get_chip(desc)->irq_ack(irq_get_irq_data(irq)); +} + +void msm_sirc_enter_sleep(void) +{ + save_type = readl(sirc_regs.int_type); + save_polarity = readl(sirc_regs.int_polarity); + writel(wake_enable, sirc_regs.int_enable); + mb(); + return; +} + +void msm_sirc_exit_sleep(void) +{ + writel(save_type, sirc_regs.int_type); + writel(save_polarity, sirc_regs.int_polarity); + writel(int_enable, sirc_regs.int_enable); + mb(); + return; } static struct irq_chip sirc_irq_chip = { - .name = "sirc", - .irq_ack = sirc_irq_ack, - .irq_mask = sirc_irq_mask, - .irq_unmask = sirc_irq_unmask, - .irq_set_wake = sirc_irq_set_wake, - .irq_set_type = sirc_irq_set_type, + .name = "sirc", + .irq_ack = sirc_irq_ack, + .irq_mask = sirc_irq_mask, + .irq_unmask = sirc_irq_unmask, + .irq_set_wake = sirc_irq_set_wake, + .irq_set_type = sirc_irq_set_type, }; void __init msm_init_sirc(void) @@ -166,6 +219,10 @@ void __init msm_init_sirc(void) irq_set_chained_handler(sirc_reg_table[i].cascade_irq, sirc_irq_handler); irq_set_irq_wake(sirc_reg_table[i].cascade_irq, 1); +#if defined(CONFIG_MSM_FIQ_SUPPORT) + msm_fiq_select(sirc_reg_table[i].cascade_fiq); + msm_fiq_enable(sirc_reg_table[i].cascade_fiq); +#endif } return; } diff --git a/arch/arm/mach-msm/sirc.h b/arch/arm/mach-msm/sirc.h new file mode 100644 index 00000000000..f719969018d --- /dev/null +++ b/arch/arm/mach-msm/sirc.h @@ -0,0 +1,36 @@ +/* arch/arm/mach-msm/sirc.h + * + * Copyright (C) 2009 Google, Inc. + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SIRC_H +#define _ARCH_ARM_MACH_MSM_SIRC_H + +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) +void sirc_fiq_select(int irq, bool enable); +#else +static inline void sirc_fiq_select(int irq, bool enable) {} +#endif + +#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_FSM9XXX) +void __init msm_init_sirc(void); +void msm_sirc_enter_sleep(void); +void msm_sirc_exit_sleep(void); +#else +static inline void __init msm_init_sirc(void) {} +static inline void msm_sirc_enter_sleep(void) { } +static inline void msm_sirc_exit_sleep(void) { } +#endif + +#endif diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c index 657be73297d..54512abc938 100644 --- a/arch/arm/mach-msm/smd.c +++ b/arch/arm/mach-msm/smd.c @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/smd.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -14,8 +15,6 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -26,109 +25,596 @@ #include #include #include -#include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include +#include +#include +#include #include "smd_private.h" -#include "proc_comm.h" +#include "modem_notifier.h" -#if defined(CONFIG_ARCH_QSD8X50) +#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM8X60) \ + || defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_FSM9XXX) \ + || defined(CONFIG_ARCH_MSM9615) || defined(CONFIG_ARCH_APQ8064) #define CONFIG_QDSP6 1 #endif -void (*msm_hw_reset_hook)(void); +#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) \ + || defined(CONFIG_ARCH_APQ8064) +#define CONFIG_DSPS 1 +#endif + +#if defined(CONFIG_ARCH_MSM8960) \ + || defined(CONFIG_ARCH_APQ8064) +#define CONFIG_WCNSS 1 +#define CONFIG_DSPS_SMSM 1 +#endif #define MODULE_NAME "msm_smd" +#define SMEM_VERSION 0x000B +#define SMD_VERSION 0x00020000 +#define SMSM_SNAPSHOT_CNT 64 +#define SMSM_SNAPSHOT_SIZE ((SMSM_NUM_ENTRIES + 1) * 4) + +uint32_t SMSM_NUM_ENTRIES = 8; +uint32_t SMSM_NUM_HOSTS = 3; + +/* Legacy SMSM interrupt notifications */ +#define LEGACY_MODEM_SMSM_MASK (SMSM_RESET | SMSM_INIT | SMSM_SMDINIT \ + | SMSM_RUN | SMSM_SYSTEM_DOWNLOAD) enum { MSM_SMD_DEBUG = 1U << 0, - MSM_SMSM_DEBUG = 1U << 0, + MSM_SMSM_DEBUG = 1U << 1, + MSM_SMD_INFO = 1U << 2, + MSM_SMSM_INFO = 1U << 3, + MSM_SMx_POWER_INFO = 1U << 4, +}; + +struct smsm_shared_info { + uint32_t *state; + uint32_t *intr_mask; + uint32_t *intr_mux; +}; + +static struct smsm_shared_info smsm_info; +static struct kfifo smsm_snapshot_fifo; +static struct wake_lock smsm_snapshot_wakelock; +static int smsm_snapshot_count; +static DEFINE_SPINLOCK(smsm_snapshot_count_lock); + +struct smsm_size_info_type { + uint32_t num_hosts; + uint32_t num_entries; + uint32_t reserved0; + uint32_t reserved1; +}; + +struct smsm_state_cb_info { + struct list_head cb_list; + uint32_t mask; + void *data; + void (*notify)(void *data, uint32_t old_state, uint32_t new_state); +}; + +struct smsm_state_info { + struct list_head callbacks; + uint32_t last_value; + uint32_t intr_mask_set; + uint32_t intr_mask_clear; +}; + +struct interrupt_config_item { + /* must be initialized */ + irqreturn_t (*irq_handler)(int req, void *data); + /* outgoing interrupt config (set from platform data) */ + uint32_t out_bit_pos; + void __iomem *out_base; + uint32_t out_offset; +}; + +struct interrupt_config { + struct interrupt_config_item smd; + struct interrupt_config_item smsm; +}; + +static irqreturn_t smd_modem_irq_handler(int irq, void *data); +static irqreturn_t smsm_modem_irq_handler(int irq, void *data); +static irqreturn_t smd_dsp_irq_handler(int irq, void *data); +static irqreturn_t smsm_dsp_irq_handler(int irq, void *data); +static irqreturn_t smd_dsps_irq_handler(int irq, void *data); +static irqreturn_t smsm_dsps_irq_handler(int irq, void *data); +static irqreturn_t smd_wcnss_irq_handler(int irq, void *data); +static irqreturn_t smsm_wcnss_irq_handler(int irq, void *data); +static irqreturn_t smd_rpm_irq_handler(int irq, void *data); +static irqreturn_t smsm_irq_handler(int irq, void *data); + +static struct interrupt_config private_intr_config[NUM_SMD_SUBSYSTEMS] = { + [SMD_MODEM] = { + .smd.irq_handler = smd_modem_irq_handler, + .smsm.irq_handler = smsm_modem_irq_handler, + }, + [SMD_Q6] = { + .smd.irq_handler = smd_dsp_irq_handler, + .smsm.irq_handler = smsm_dsp_irq_handler, + }, + [SMD_DSPS] = { + .smd.irq_handler = smd_dsps_irq_handler, + .smsm.irq_handler = smsm_dsps_irq_handler, + }, + [SMD_WCNSS] = { + .smd.irq_handler = smd_wcnss_irq_handler, + .smsm.irq_handler = smsm_wcnss_irq_handler, + }, + [SMD_RPM] = { + .smd.irq_handler = smd_rpm_irq_handler, + .smsm.irq_handler = NULL, /* does not support smsm */ + }, +}; + +struct smem_area { + void *phys_addr; + unsigned size; + void __iomem *virt_addr; +}; +static uint32_t num_smem_areas; +static struct smem_area *smem_areas; +static void *smem_range_check(void *base, unsigned offset); + +struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS]; + +#define SMSM_STATE_ADDR(entry) (smsm_info.state + entry) +#define SMSM_INTR_MASK_ADDR(entry, host) (smsm_info.intr_mask + \ + entry * SMSM_NUM_HOSTS + host) +#define SMSM_INTR_MUX_ADDR(entry) (smsm_info.intr_mux + entry) + +/* Internal definitions which are not exported in some targets */ +enum { + SMSM_APPS_DEM_I = 3, }; static int msm_smd_debug_mask; - -struct shared_info { - int ready; - unsigned state; -}; - -static unsigned dummy_state[SMSM_STATE_COUNT]; - -static struct shared_info smd_info = { - .state = (unsigned) &dummy_state, -}; - module_param_named(debug_mask, msm_smd_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); +#if defined(CONFIG_MSM_SMD_DEBUG) +#define SMD_DBG(x...) do { \ + if (msm_smd_debug_mask & MSM_SMD_DEBUG) \ + printk(KERN_DEBUG x); \ + } while (0) + +#define SMSM_DBG(x...) do { \ + if (msm_smd_debug_mask & MSM_SMSM_DEBUG) \ + printk(KERN_DEBUG x); \ + } while (0) + +#define SMD_INFO(x...) do { \ + if (msm_smd_debug_mask & MSM_SMD_INFO) \ + printk(KERN_INFO x); \ + } while (0) + +#define SMSM_INFO(x...) do { \ + if (msm_smd_debug_mask & MSM_SMSM_INFO) \ + printk(KERN_INFO x); \ + } while (0) +#define SMx_POWER_INFO(x...) do { \ + if (msm_smd_debug_mask & MSM_SMx_POWER_INFO) \ + printk(KERN_INFO x); \ + } while (0) +#else +#define SMD_DBG(x...) do { } while (0) +#define SMSM_DBG(x...) do { } while (0) +#define SMD_INFO(x...) do { } while (0) +#define SMSM_INFO(x...) do { } while (0) +#define SMx_POWER_INFO(x...) do { } while (0) +#endif + static unsigned last_heap_free = 0xffffffff; -static inline void notify_other_smsm(void) -{ - msm_a2m_int(5); -#ifdef CONFIG_QDSP6 - msm_a2m_int(8); +static inline void smd_write_intr(unsigned int val, + const void __iomem *addr); + +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_TRIG_A2M_SMD_INT \ + (smd_write_intr(1 << 0, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMD_INT \ + (smd_write_intr(1 << 8, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2M_SMSM_INT \ + (smd_write_intr(1 << 5, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMSM_INT \ + (smd_write_intr(1 << 8, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2DSPS_SMD_INT +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT +#elif defined(CONFIG_ARCH_MSM8X60) +#define MSM_TRIG_A2M_SMD_INT \ + (smd_write_intr(1 << 3, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMD_INT \ + (smd_write_intr(1 << 15, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2M_SMSM_INT \ + (smd_write_intr(1 << 4, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMSM_INT \ + (smd_write_intr(1 << 14, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2DSPS_SMD_INT \ + (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4080)) +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT +#elif defined(CONFIG_ARCH_MSM9615) +#define MSM_TRIG_A2M_SMD_INT \ + (smd_write_intr(1 << 3, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMD_INT \ + (smd_write_intr(1 << 15, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2M_SMSM_INT \ + (smd_write_intr(1 << 4, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMSM_INT \ + (smd_write_intr(1 << 14, MSM_APCS_GCC_BASE + 0x8)) +#define MSM_TRIG_A2DSPS_SMD_INT +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT +#elif defined(CONFIG_ARCH_FSM9XXX) +#define MSM_TRIG_A2Q6_SMD_INT \ + (smd_write_intr(1 << 10, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2Q6_SMSM_INT \ + (smd_write_intr(1 << 10, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2M_SMD_INT \ + (smd_write_intr(1 << 0, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2M_SMSM_INT \ + (smd_write_intr(1 << 5, MSM_GCC_BASE + 0x8)) +#define MSM_TRIG_A2DSPS_SMD_INT +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT +#elif defined(CONFIG_ARCH_MSM7X01A) || defined(CONFIG_ARCH_MSM7x25) +#define MSM_TRIG_A2M_SMD_INT \ + (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (0) * 4)) +#define MSM_TRIG_A2Q6_SMD_INT +#define MSM_TRIG_A2M_SMSM_INT \ + (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (5) * 4)) +#define MSM_TRIG_A2Q6_SMSM_INT +#define MSM_TRIG_A2DSPS_SMD_INT +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT +#elif defined(CONFIG_ARCH_MSM7X27) || defined(CONFIG_ARCH_MSM7X27A) +#define MSM_TRIG_A2M_SMD_INT \ + (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (0) * 4)) +#define MSM_TRIG_A2Q6_SMD_INT +#define MSM_TRIG_A2M_SMSM_INT \ + (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (5) * 4)) +#define MSM_TRIG_A2Q6_SMSM_INT +#define MSM_TRIG_A2DSPS_SMD_INT +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT +#else /* use platform device / device tree configuration */ +#define MSM_TRIG_A2M_SMD_INT +#define MSM_TRIG_A2Q6_SMD_INT +#define MSM_TRIG_A2M_SMSM_INT +#define MSM_TRIG_A2Q6_SMSM_INT +#define MSM_TRIG_A2DSPS_SMD_INT +#define MSM_TRIG_A2DSPS_SMSM_INT +#define MSM_TRIG_A2WCNSS_SMD_INT +#define MSM_TRIG_A2WCNSS_SMSM_INT #endif + +/* + * stub out legacy macros if they are not being used so that the legacy + * code compiles even though it is not used + * + * these definitions should not be used in active code and will cause + * an early failure + */ +#ifndef INT_A9_M2A_0 +#define INT_A9_M2A_0 -1 +#endif +#ifndef INT_A9_M2A_5 +#define INT_A9_M2A_5 -1 +#endif +#ifndef INT_ADSP_A11 +#define INT_ADSP_A11 -1 +#endif +#ifndef INT_ADSP_A11_SMSM +#define INT_ADSP_A11_SMSM -1 +#endif +#ifndef INT_DSPS_A11 +#define INT_DSPS_A11 -1 +#endif +#ifndef INT_DSPS_A11_SMSM +#define INT_DSPS_A11_SMSM -1 +#endif +#ifndef INT_WCNSS_A11 +#define INT_WCNSS_A11 -1 +#endif +#ifndef INT_WCNSS_A11_SMSM +#define INT_WCNSS_A11_SMSM -1 +#endif + +#define SMD_LOOPBACK_CID 100 + +#define SMEM_SPINLOCK_SMEM_ALLOC "S:3" +static remote_spinlock_t remote_spinlock; + +static LIST_HEAD(smd_ch_list_loopback); +static void smd_fake_irq_handler(unsigned long arg); +static void smsm_cb_snapshot(uint32_t use_wakelock); + +static struct workqueue_struct *smsm_cb_wq; +static void notify_smsm_cb_clients_worker(struct work_struct *work); +static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker); +static DEFINE_MUTEX(smsm_lock); +static struct smsm_state_info *smsm_states; +static int spinlocks_initialized; +static RAW_NOTIFIER_HEAD(smsm_driver_state_notifier_list); +static DEFINE_MUTEX(smsm_driver_state_notifier_lock); +static void smsm_driver_state_notify(uint32_t state, void *data); + +static inline void smd_write_intr(unsigned int val, + const void __iomem *addr) +{ + wmb(); + __raw_writel(val, addr); } +#ifdef CONFIG_WCNSS +static inline void wakeup_v1_riva(void) +{ + /* + * workaround hack for RIVA v1 hardware bug + * trigger GPIO 40 to wake up RIVA from power collaspe + * not to be sent to customers + */ + if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) { + __raw_writel(0x0, MSM_TLMM_BASE + 0x1284); + __raw_writel(0x2, MSM_TLMM_BASE + 0x1284); + } + /* end workaround */ +} +#else +static inline void wakeup_v1_riva(void) {} +#endif + static inline void notify_modem_smd(void) { - msm_a2m_int(0); + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_MODEM].smd; + if (intr->out_base) { + ++interrupt_stats[SMD_MODEM].smd_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_MODEM].smd_out_hardcode_count; + MSM_TRIG_A2M_SMD_INT; + } } static inline void notify_dsp_smd(void) { - msm_a2m_int(8); + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_Q6].smd; + if (intr->out_base) { + ++interrupt_stats[SMD_Q6].smd_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_Q6].smd_out_hardcode_count; + MSM_TRIG_A2Q6_SMD_INT; + } } -static void smd_diag(void) +static inline void notify_dsps_smd(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_DSPS].smd; + if (intr->out_base) { + ++interrupt_stats[SMD_DSPS].smd_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_DSPS].smd_out_hardcode_count; + MSM_TRIG_A2DSPS_SMD_INT; + } +} + +static inline void notify_wcnss_smd(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_WCNSS].smd; + wakeup_v1_riva(); + + if (intr->out_base) { + ++interrupt_stats[SMD_WCNSS].smd_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_WCNSS].smd_out_hardcode_count; + MSM_TRIG_A2WCNSS_SMD_INT; + } +} + +static inline void notify_rpm_smd(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_RPM].smd; + + if (intr->out_base) { + ++interrupt_stats[SMD_RPM].smd_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } +} + +static inline void notify_modem_smsm(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_MODEM].smsm; + if (intr->out_base) { + ++interrupt_stats[SMD_MODEM].smsm_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_MODEM].smsm_out_hardcode_count; + MSM_TRIG_A2M_SMSM_INT; + } +} + +static inline void notify_dsp_smsm(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_Q6].smsm; + if (intr->out_base) { + ++interrupt_stats[SMD_Q6].smsm_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_Q6].smsm_out_hardcode_count; + MSM_TRIG_A2Q6_SMSM_INT; + } +} + +static inline void notify_dsps_smsm(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_DSPS].smsm; + if (intr->out_base) { + ++interrupt_stats[SMD_DSPS].smsm_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_DSPS].smsm_out_hardcode_count; + MSM_TRIG_A2DSPS_SMSM_INT; + } +} + +static inline void notify_wcnss_smsm(void) +{ + static const struct interrupt_config_item *intr + = &private_intr_config[SMD_WCNSS].smsm; + wakeup_v1_riva(); + + if (intr->out_base) { + ++interrupt_stats[SMD_WCNSS].smsm_out_config_count; + smd_write_intr(intr->out_bit_pos, + intr->out_base + intr->out_offset); + } else { + ++interrupt_stats[SMD_WCNSS].smsm_out_hardcode_count; + MSM_TRIG_A2WCNSS_SMSM_INT; + } +} + +static void notify_other_smsm(uint32_t smsm_entry, uint32_t notify_mask) +{ + /* older protocol don't use smsm_intr_mask, + but still communicates with modem */ + if (!smsm_info.intr_mask || + (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_MODEM)) + & notify_mask)) + notify_modem_smsm(); + + if (smsm_info.intr_mask && + (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_Q6)) + & notify_mask)) { + uint32_t mux_val; + + if (cpu_is_qsd8x50() && smsm_info.intr_mux) { + mux_val = __raw_readl( + SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM)); + mux_val++; + __raw_writel(mux_val, + SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM)); + } + notify_dsp_smsm(); + } + + if (smsm_info.intr_mask && + (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_WCNSS)) + & notify_mask)) { + notify_wcnss_smsm(); + } + + if (smsm_info.intr_mask && + (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_DSPS)) + & notify_mask)) { + notify_dsps_smsm(); + } + + /* + * Notify local SMSM callback clients without wakelock since this + * code is used by power management during power-down/-up sequencing + * on DEM-based targets. Grabbing a wakelock in this case will + * abort the power-down sequencing. + */ + smsm_cb_snapshot(0); +} + +void smd_diag(void) { char *x; + int size; x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); if (x != 0) { x[SZ_DIAG_ERR_MSG - 1] = 0; - pr_debug("DIAG '%s'\n", x); + SMD_INFO("smem: DIAG '%s'\n", x); + } + + x = smem_get_entry(SMEM_ERR_CRASH_LOG, &size); + if (x != 0) { + x[size - 1] = 0; + pr_err("smem: CRASH LOG\n'%s'\n", x); } } -/* call when SMSM_RESET flag is set in the A9's smsm_state */ + static void handle_modem_crash(void) { - pr_err("ARM9 has CRASHED\n"); + pr_err("MODEM/AMSS has CRASHED\n"); smd_diag(); - /* hard reboot if possible */ - if (msm_hw_reset_hook) - msm_hw_reset_hook(); + /* hard reboot if possible FIXME + if (msm_reset_hook) + msm_reset_hook(); + */ /* in this case the modem or watchdog should reboot us */ for (;;) ; } -uint32_t raw_smsm_get_state(enum smsm_state_item item) +int smsm_check_for_modem_crash(void) { - return readl(smd_info.state + item * 4); -} + /* if the modem's not ready yet, we have to hope for the best */ + if (!smsm_info.state) + return 0; -static int check_for_modem_crash(void) -{ - if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET) { + if (__raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE)) & SMSM_RESET) { handle_modem_crash(); return -1; } return 0; } +EXPORT_SYMBOL(smsm_check_for_modem_crash); /* the spinlock is used to synchronize between the * irq handler and code that mutates the channel * list or fiddles with channel state */ -DEFINE_SPINLOCK(smd_lock); +static DEFINE_SPINLOCK(smd_lock); DEFINE_SPINLOCK(smem_lock); /* the mutex is used during open() and close() @@ -139,24 +625,376 @@ static DEFINE_MUTEX(smd_creation_mutex); static int smd_initialized; -LIST_HEAD(smd_ch_closed_list); -LIST_HEAD(smd_ch_list_modem); -LIST_HEAD(smd_ch_list_dsp); +struct smd_shared_v1 { + struct smd_half_channel ch0; + unsigned char data0[SMD_BUF_SIZE]; + struct smd_half_channel ch1; + unsigned char data1[SMD_BUF_SIZE]; +}; + +struct smd_shared_v2 { + struct smd_half_channel ch0; + struct smd_half_channel ch1; +}; + +struct smd_shared_v2_word_access { + struct smd_half_channel_word_access ch0; + struct smd_half_channel_word_access ch1; +}; + +struct smd_channel { + volatile void *send; /* some variant of smd_half_channel */ + volatile void *recv; /* some variant of smd_half_channel */ + unsigned char *send_data; + unsigned char *recv_data; + unsigned fifo_size; + unsigned fifo_mask; + struct list_head ch_list; + + unsigned current_packet; + unsigned n; + void *priv; + void (*notify)(void *priv, unsigned flags); + + int (*read)(smd_channel_t *ch, void *data, int len, int user_buf); + int (*write)(smd_channel_t *ch, const void *data, int len, + int user_buf); + int (*read_avail)(smd_channel_t *ch); + int (*write_avail)(smd_channel_t *ch); + int (*read_from_cb)(smd_channel_t *ch, void *data, int len, + int user_buf); + + void (*update_state)(smd_channel_t *ch); + unsigned last_state; + void (*notify_other_cpu)(void); + + char name[20]; + struct platform_device pdev; + unsigned type; + + int pending_pkt_sz; + + char is_pkt_ch; + + /* + * private internal functions to access *send and *recv. + * never to be exported outside of smd + */ + struct smd_half_channel_access *half_ch; +}; + +struct edge_to_pid { + uint32_t local_pid; + uint32_t remote_pid; + char subsys_name[SMD_MAX_CH_NAME_LEN]; +}; + +/** + * Maps edge type to local and remote processor ID's. + */ +static struct edge_to_pid edge_to_pids[] = { + [SMD_APPS_MODEM] = {SMD_APPS, SMD_MODEM, "modem"}, + [SMD_APPS_QDSP] = {SMD_APPS, SMD_Q6, "q6"}, + [SMD_MODEM_QDSP] = {SMD_MODEM, SMD_Q6}, + [SMD_APPS_DSPS] = {SMD_APPS, SMD_DSPS, "dsps"}, + [SMD_MODEM_DSPS] = {SMD_MODEM, SMD_DSPS}, + [SMD_QDSP_DSPS] = {SMD_Q6, SMD_DSPS}, + [SMD_APPS_WCNSS] = {SMD_APPS, SMD_WCNSS, "wcnss"}, + [SMD_MODEM_WCNSS] = {SMD_MODEM, SMD_WCNSS}, + [SMD_QDSP_WCNSS] = {SMD_Q6, SMD_WCNSS}, + [SMD_DSPS_WCNSS] = {SMD_DSPS, SMD_WCNSS}, + [SMD_APPS_Q6FW] = {SMD_APPS, SMD_MODEM_Q6_FW}, + [SMD_MODEM_Q6FW] = {SMD_MODEM, SMD_MODEM_Q6_FW}, + [SMD_QDSP_Q6FW] = {SMD_Q6, SMD_MODEM_Q6_FW}, + [SMD_DSPS_Q6FW] = {SMD_DSPS, SMD_MODEM_Q6_FW}, + [SMD_WCNSS_Q6FW] = {SMD_WCNSS, SMD_MODEM_Q6_FW}, + [SMD_APPS_RPM] = {SMD_APPS, SMD_RPM}, + [SMD_MODEM_RPM] = {SMD_MODEM, SMD_RPM}, + [SMD_QDSP_RPM] = {SMD_Q6, SMD_RPM}, + [SMD_WCNSS_RPM] = {SMD_WCNSS, SMD_RPM}, +}; + +struct restart_notifier_block { + unsigned processor; + char *name; + struct notifier_block nb; +}; + +static int disable_smsm_reset_handshake; +static struct platform_device loopback_tty_pdev = {.name = "LOOPBACK_TTY"}; + +static LIST_HEAD(smd_ch_closed_list); +static LIST_HEAD(smd_ch_closing_list); +static LIST_HEAD(smd_ch_to_close_list); +static LIST_HEAD(smd_ch_list_modem); +static LIST_HEAD(smd_ch_list_dsp); +static LIST_HEAD(smd_ch_list_dsps); +static LIST_HEAD(smd_ch_list_wcnss); +static LIST_HEAD(smd_ch_list_rpm); static unsigned char smd_ch_allocated[64]; static struct work_struct probe_work; +static void finalize_channel_close_fn(struct work_struct *work); +static DECLARE_WORK(finalize_channel_close_work, finalize_channel_close_fn); +static struct workqueue_struct *channel_close_wq; + +static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm); + +/* on smp systems, the probe might get called from multiple cores, + hence use a lock */ +static DEFINE_MUTEX(smd_probe_lock); + +static void smd_channel_probe_worker(struct work_struct *work) +{ + struct smd_alloc_elm *shared; + unsigned n; + uint32_t type; + + shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); + + if (!shared) { + pr_err("%s: allocation table not initialized\n", __func__); + return; + } + + mutex_lock(&smd_probe_lock); + for (n = 0; n < 64; n++) { + if (smd_ch_allocated[n]) + continue; + + /* channel should be allocated only if APPS + processor is involved */ + type = SMD_CHANNEL_TYPE(shared[n].type); + if (type >= ARRAY_SIZE(edge_to_pids) || + edge_to_pids[type].local_pid != SMD_APPS) + continue; + if (!shared[n].ref_count) + continue; + if (!shared[n].name[0]) + continue; + + if (!smd_alloc_channel(&shared[n])) + smd_ch_allocated[n] = 1; + else + SMD_INFO("Probe skipping ch %d, not allocated\n", n); + } + mutex_unlock(&smd_probe_lock); +} + +/** + * Lookup processor ID and determine if it belongs to the proved edge + * type. + * + * @shared2: Pointer to v2 shared channel structure + * @type: Edge type + * @pid: Processor ID of processor on edge + * @local_ch: Channel that belongs to processor @pid + * @remote_ch: Other side of edge contained @pid + * + * Returns 0 for not on edge, 1 for found on edge + */ +static int pid_is_on_edge(struct smd_shared_v2 *shared2, + uint32_t type, uint32_t pid, + struct smd_half_channel **local_ch, + struct smd_half_channel **remote_ch + ) +{ + int ret = 0; + struct edge_to_pid *edge; + + *local_ch = 0; + *remote_ch = 0; + + if (!shared2 || (type >= ARRAY_SIZE(edge_to_pids))) + return 0; + + edge = &edge_to_pids[type]; + if (edge->local_pid != edge->remote_pid) { + if (pid == edge->local_pid) { + *local_ch = &shared2->ch0; + *remote_ch = &shared2->ch1; + ret = 1; + } else if (pid == edge->remote_pid) { + *local_ch = &shared2->ch1; + *remote_ch = &shared2->ch0; + ret = 1; + } + } + + return ret; +} + +/* + * Returns a pointer to the subsystem name or NULL if no + * subsystem name is available. + * + * @type - Edge definition + */ +const char *smd_edge_to_subsystem(uint32_t type) +{ + const char *subsys = NULL; + + if (type < ARRAY_SIZE(edge_to_pids)) { + subsys = edge_to_pids[type].subsys_name; + if (subsys[0] == 0x0) + subsys = NULL; + } + return subsys; +} +EXPORT_SYMBOL(smd_edge_to_subsystem); + +/* + * Returns a pointer to the subsystem name given the + * remote processor ID. + * + * @pid Remote processor ID + * @returns Pointer to subsystem name or NULL if not found + */ +const char *smd_pid_to_subsystem(uint32_t pid) +{ + const char *subsys = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(edge_to_pids); ++i) { + if (pid == edge_to_pids[i].remote_pid && + edge_to_pids[i].subsys_name[0] != 0x0 + ) { + subsys = edge_to_pids[i].subsys_name; + break; + } + } + + return subsys; +} +EXPORT_SYMBOL(smd_pid_to_subsystem); + +static void smd_reset_edge(struct smd_half_channel *ch, unsigned new_state) +{ + if (ch->state != SMD_SS_CLOSED) { + ch->state = new_state; + ch->fDSR = 0; + ch->fCTS = 0; + ch->fCD = 0; + ch->fSTATE = 1; + } +} + +static void smd_channel_reset_state(struct smd_alloc_elm *shared, + unsigned new_state, unsigned pid) +{ + unsigned n; + struct smd_shared_v2 *shared2; + uint32_t type; + struct smd_half_channel *local_ch; + struct smd_half_channel *remote_ch; + + for (n = 0; n < SMD_CHANNELS; n++) { + if (!shared[n].ref_count) + continue; + if (!shared[n].name[0]) + continue; + + type = SMD_CHANNEL_TYPE(shared[n].type); + shared2 = smem_alloc(SMEM_SMD_BASE_ID + n, sizeof(*shared2)); + if (!shared2) + continue; + + if (pid_is_on_edge(shared2, type, pid, &local_ch, &remote_ch)) + smd_reset_edge(local_ch, new_state); + + /* + * ModemFW is in the same subsystem as ModemSW, but has + * separate SMD edges that need to be reset. + */ + if (pid == SMSM_MODEM && + pid_is_on_edge(shared2, type, SMD_MODEM_Q6_FW, + &local_ch, &remote_ch)) + smd_reset_edge(local_ch, new_state); + } +} + + +void smd_channel_reset(uint32_t restart_pid) +{ + struct smd_alloc_elm *shared; + unsigned long flags; + + SMD_DBG("%s: starting reset\n", __func__); + shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); + if (!shared) { + pr_err("%s: allocation table not initialized\n", __func__); + return; + } + + /* release any held spinlocks */ + remote_spin_release(&remote_spinlock, restart_pid); + remote_spin_release_all(restart_pid); + + /* reset SMSM entry */ + if (smsm_info.state) { + writel_relaxed(0, SMSM_STATE_ADDR(restart_pid)); + + /* restart SMSM init handshake */ + if (restart_pid == SMSM_MODEM) { + smsm_change_state(SMSM_APPS_STATE, + SMSM_INIT | SMSM_SMD_LOOPBACK | SMSM_RESET, + 0); + } + + /* notify SMSM processors */ + smsm_irq_handler(0, 0); + notify_modem_smsm(); + notify_dsp_smsm(); + notify_dsps_smsm(); + notify_wcnss_smsm(); + } + + /* change all remote states to CLOSING */ + mutex_lock(&smd_probe_lock); + spin_lock_irqsave(&smd_lock, flags); + smd_channel_reset_state(shared, SMD_SS_CLOSING, restart_pid); + spin_unlock_irqrestore(&smd_lock, flags); + mutex_unlock(&smd_probe_lock); + + /* notify SMD processors */ + mb(); + smd_fake_irq_handler(0); + notify_modem_smd(); + notify_dsp_smd(); + notify_dsps_smd(); + notify_wcnss_smd(); + + /* change all remote states to CLOSED */ + mutex_lock(&smd_probe_lock); + spin_lock_irqsave(&smd_lock, flags); + smd_channel_reset_state(shared, SMD_SS_CLOSED, restart_pid); + spin_unlock_irqrestore(&smd_lock, flags); + mutex_unlock(&smd_probe_lock); + + /* notify SMD processors */ + mb(); + smd_fake_irq_handler(0); + notify_modem_smd(); + notify_dsp_smd(); + notify_dsps_smd(); + notify_wcnss_smd(); + + SMD_DBG("%s: finished reset\n", __func__); +} + /* how many bytes are available for reading */ static int smd_stream_read_avail(struct smd_channel *ch) { - return (ch->recv->head - ch->recv->tail) & ch->fifo_mask; + return (ch->half_ch->get_head(ch->recv) - + ch->half_ch->get_tail(ch->recv)) & ch->fifo_mask; } /* how many bytes we are free to write */ static int smd_stream_write_avail(struct smd_channel *ch) { - return ch->fifo_mask - - ((ch->send->head - ch->send->tail) & ch->fifo_mask); + return ch->fifo_mask - ((ch->half_ch->get_head(ch->send) - + ch->half_ch->get_tail(ch->send)) & ch->fifo_mask); } static int smd_packet_read_avail(struct smd_channel *ch) @@ -179,15 +1017,16 @@ static int smd_packet_write_avail(struct smd_channel *ch) static int ch_is_open(struct smd_channel *ch) { - return (ch->recv->state == SMD_SS_OPENED) && - (ch->send->state == SMD_SS_OPENED); + return (ch->half_ch->get_state(ch->recv) == SMD_SS_OPENED || + ch->half_ch->get_state(ch->recv) == SMD_SS_FLUSHING) + && (ch->half_ch->get_state(ch->send) == SMD_SS_OPENED); } /* provide a pointer and length to readable data in the fifo */ static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr) { - unsigned head = ch->recv->head; - unsigned tail = ch->recv->tail; + unsigned head = ch->half_ch->get_head(ch->recv); + unsigned tail = ch->half_ch->get_tail(ch->recv); *ptr = (void *) (ch->recv_data + tail); if (tail <= head) @@ -196,24 +1035,32 @@ static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr) return ch->fifo_size - tail; } +static int read_intr_blocked(struct smd_channel *ch) +{ + return ch->half_ch->get_fBLOCKREADINTR(ch->recv); +} + /* advance the fifo read pointer after data from ch_read_buffer is consumed */ static void ch_read_done(struct smd_channel *ch, unsigned count) { BUG_ON(count > smd_stream_read_avail(ch)); - ch->recv->tail = (ch->recv->tail + count) & ch->fifo_mask; - ch->send->fTAIL = 1; + ch->half_ch->set_tail(ch->recv, + (ch->half_ch->get_tail(ch->recv) + count) & ch->fifo_mask); + wmb(); + ch->half_ch->set_fTAIL(ch->send, 1); } /* basic read interface to ch_read_{buffer,done} used * by smd_*_read() and update_packet_state() * will read-and-discard if the _data pointer is null */ -static int ch_read(struct smd_channel *ch, void *_data, int len) +static int ch_read(struct smd_channel *ch, void *_data, int len, int user_buf) { void *ptr; unsigned n; unsigned char *data = _data; int orig_len = len; + int r = 0; while (len > 0) { n = ch_read_buffer(ch, &ptr); @@ -222,8 +1069,19 @@ static int ch_read(struct smd_channel *ch, void *_data, int len) if (n > len) n = len; - if (_data) - memcpy(data, ptr, n); + if (_data) { + if (user_buf) { + r = copy_to_user(data, ptr, n); + if (r > 0) { + pr_err("%s: " + "copy_to_user could not copy " + "%i bytes.\n", + __func__, + r); + } + } else + memcpy(data, ptr, n); + } data += n; len -= n; @@ -244,24 +1102,25 @@ static void update_packet_state(struct smd_channel *ch) int r; /* can't do anything if we're in the middle of a packet */ - if (ch->current_packet != 0) - return; + while (ch->current_packet == 0) { + /* discard 0 length packets if any */ - /* don't bother unless we can get the full header */ - if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE) - return; + /* don't bother unless we can get the full header */ + if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE) + return; - r = ch_read(ch, hdr, SMD_HEADER_SIZE); - BUG_ON(r != SMD_HEADER_SIZE); + r = ch_read(ch, hdr, SMD_HEADER_SIZE, 0); + BUG_ON(r != SMD_HEADER_SIZE); - ch->current_packet = hdr[0]; + ch->current_packet = hdr[0]; + } } /* provide a pointer and length to next free space in the fifo */ static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr) { - unsigned head = ch->send->head; - unsigned tail = ch->send->tail; + unsigned head = ch->half_ch->get_head(ch->send); + unsigned tail = ch->half_ch->get_tail(ch->send); *ptr = (void *) (ch->send_data + head); if (head < tail) { @@ -280,23 +1139,25 @@ static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr) static void ch_write_done(struct smd_channel *ch, unsigned count) { BUG_ON(count > smd_stream_write_avail(ch)); - ch->send->head = (ch->send->head + count) & ch->fifo_mask; - ch->send->fHEAD = 1; + ch->half_ch->set_head(ch->send, + (ch->half_ch->get_head(ch->send) + count) & ch->fifo_mask); + wmb(); + ch->half_ch->set_fHEAD(ch->send, 1); } static void ch_set_state(struct smd_channel *ch, unsigned n) { if (n == SMD_SS_OPENED) { - ch->send->fDSR = 1; - ch->send->fCTS = 1; - ch->send->fCD = 1; + ch->half_ch->set_fDSR(ch->send, 1); + ch->half_ch->set_fCTS(ch->send, 1); + ch->half_ch->set_fCD(ch->send, 1); } else { - ch->send->fDSR = 0; - ch->send->fCTS = 0; - ch->send->fCD = 0; + ch->half_ch->set_fDSR(ch->send, 0); + ch->half_ch->set_fCTS(ch->send, 0); + ch->half_ch->set_fCD(ch->send, 0); } - ch->send->state = n; - ch->send->fSTATE = 1; + ch->half_ch->set_state(ch->send, n); + ch->half_ch->set_fSTATE(ch->send, 1); ch->notify_other_cpu(); } @@ -314,84 +1175,169 @@ static void smd_state_change(struct smd_channel *ch, { ch->last_state = next; - pr_debug("ch %d %d -> %d\n", ch->n, last, next); + SMD_INFO("SMD: ch %d %d -> %d\n", ch->n, last, next); switch (next) { case SMD_SS_OPENING: - ch->recv->tail = 0; + if (ch->half_ch->get_state(ch->send) == SMD_SS_CLOSING || + ch->half_ch->get_state(ch->send) == SMD_SS_CLOSED) { + ch->half_ch->set_tail(ch->recv, 0); + ch->half_ch->set_head(ch->send, 0); + ch->half_ch->set_fBLOCKREADINTR(ch->send, 0); + ch_set_state(ch, SMD_SS_OPENING); + } + break; case SMD_SS_OPENED: - if (ch->send->state != SMD_SS_OPENED) + if (ch->half_ch->get_state(ch->send) == SMD_SS_OPENING) { ch_set_state(ch, SMD_SS_OPENED); - ch->notify(ch->priv, SMD_EVENT_OPEN); + ch->notify(ch->priv, SMD_EVENT_OPEN); + } break; case SMD_SS_FLUSHING: case SMD_SS_RESET: /* we should force them to close? */ - default: - ch->notify(ch->priv, SMD_EVENT_CLOSE); + break; + case SMD_SS_CLOSED: + if (ch->half_ch->get_state(ch->send) == SMD_SS_OPENED) { + ch_set_state(ch, SMD_SS_CLOSING); + ch->current_packet = 0; + ch->pending_pkt_sz = 0; + ch->notify(ch->priv, SMD_EVENT_CLOSE); + } + break; + case SMD_SS_CLOSING: + if (ch->half_ch->get_state(ch->send) == SMD_SS_CLOSED) { + list_move(&ch->ch_list, + &smd_ch_to_close_list); + queue_work(channel_close_wq, + &finalize_channel_close_work); + } + break; } } +static void handle_smd_irq_closing_list(void) +{ + unsigned long flags; + struct smd_channel *ch; + struct smd_channel *index; + unsigned tmp; + + spin_lock_irqsave(&smd_lock, flags); + list_for_each_entry_safe(ch, index, &smd_ch_closing_list, ch_list) { + if (ch->half_ch->get_fSTATE(ch->recv)) + ch->half_ch->set_fSTATE(ch->recv, 0); + tmp = ch->half_ch->get_state(ch->recv); + if (tmp != ch->last_state) + smd_state_change(ch, ch->last_state, tmp); + } + spin_unlock_irqrestore(&smd_lock, flags); +} + static void handle_smd_irq(struct list_head *list, void (*notify)(void)) { unsigned long flags; struct smd_channel *ch; - int do_notify = 0; unsigned ch_flags; unsigned tmp; + unsigned char state_change; spin_lock_irqsave(&smd_lock, flags); list_for_each_entry(ch, list, ch_list) { + state_change = 0; ch_flags = 0; if (ch_is_open(ch)) { - if (ch->recv->fHEAD) { - ch->recv->fHEAD = 0; + if (ch->half_ch->get_fHEAD(ch->recv)) { + ch->half_ch->set_fHEAD(ch->recv, 0); ch_flags |= 1; - do_notify |= 1; } - if (ch->recv->fTAIL) { - ch->recv->fTAIL = 0; + if (ch->half_ch->get_fTAIL(ch->recv)) { + ch->half_ch->set_fTAIL(ch->recv, 0); ch_flags |= 2; - do_notify |= 1; } - if (ch->recv->fSTATE) { - ch->recv->fSTATE = 0; + if (ch->half_ch->get_fSTATE(ch->recv)) { + ch->half_ch->set_fSTATE(ch->recv, 0); ch_flags |= 4; - do_notify |= 1; } } - tmp = ch->recv->state; - if (tmp != ch->last_state) + tmp = ch->half_ch->get_state(ch->recv); + if (tmp != ch->last_state) { + SMx_POWER_INFO("SMD ch%d '%s' State change %d->%d\n", + ch->n, ch->name, ch->last_state, tmp); smd_state_change(ch, ch->last_state, tmp); - if (ch_flags) { + state_change = 1; + } + if (ch_flags & 0x3) { ch->update_state(ch); + SMx_POWER_INFO("SMD ch%d '%s' Data event r%d/w%d\n", + ch->n, ch->name, + ch->read_avail(ch), + ch->fifo_size - ch->write_avail(ch)); ch->notify(ch->priv, SMD_EVENT_DATA); } + if (ch_flags & 0x4 && !state_change) { + SMx_POWER_INFO("SMD ch%d '%s' State update\n", + ch->n, ch->name); + ch->notify(ch->priv, SMD_EVENT_STATUS); + } } - if (do_notify) - notify(); spin_unlock_irqrestore(&smd_lock, flags); do_smd_probe(); } static irqreturn_t smd_modem_irq_handler(int irq, void *data) { + SMx_POWER_INFO("SMD Int Modem->Apps\n"); + ++interrupt_stats[SMD_MODEM].smd_in_count; handle_smd_irq(&smd_ch_list_modem, notify_modem_smd); + handle_smd_irq_closing_list(); return IRQ_HANDLED; } -#if defined(CONFIG_QDSP6) static irqreturn_t smd_dsp_irq_handler(int irq, void *data) { + SMx_POWER_INFO("SMD Int LPASS->Apps\n"); + ++interrupt_stats[SMD_Q6].smd_in_count; handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd); + handle_smd_irq_closing_list(); + return IRQ_HANDLED; +} + +static irqreturn_t smd_dsps_irq_handler(int irq, void *data) +{ + SMx_POWER_INFO("SMD Int DSPS->Apps\n"); + ++interrupt_stats[SMD_DSPS].smd_in_count; + handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd); + handle_smd_irq_closing_list(); + return IRQ_HANDLED; +} + +static irqreturn_t smd_wcnss_irq_handler(int irq, void *data) +{ + SMx_POWER_INFO("SMD Int WCNSS->Apps\n"); + ++interrupt_stats[SMD_WCNSS].smd_in_count; + handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd); + handle_smd_irq_closing_list(); + return IRQ_HANDLED; +} + +static irqreturn_t smd_rpm_irq_handler(int irq, void *data) +{ + SMx_POWER_INFO("SMD Int RPM->Apps\n"); + ++interrupt_stats[SMD_RPM].smd_in_count; + handle_smd_irq(&smd_ch_list_rpm, notify_rpm_smd); + handle_smd_irq_closing_list(); return IRQ_HANDLED; } -#endif static void smd_fake_irq_handler(unsigned long arg) { handle_smd_irq(&smd_ch_list_modem, notify_modem_smd); handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd); + handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd); + handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd); + handle_smd_irq(&smd_ch_list_rpm, notify_rpm_smd); + handle_smd_irq_closing_list(); } static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0); @@ -399,9 +1345,11 @@ static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0); static inline int smd_need_int(struct smd_channel *ch) { if (ch_is_open(ch)) { - if (ch->recv->fHEAD || ch->recv->fTAIL || ch->recv->fSTATE) + if (ch->half_ch->get_fHEAD(ch->recv) || + ch->half_ch->get_fTAIL(ch->recv) || + ch->half_ch->get_fSTATE(ch->recv)) return 1; - if (ch->recv->state != ch->last_state) + if (ch->half_ch->get_state(ch->recv) != ch->last_state) return 1; } return 0; @@ -426,68 +1374,82 @@ void smd_sleep_exit(void) break; } } + list_for_each_entry(ch, &smd_ch_list_dsps, ch_list) { + if (smd_need_int(ch)) { + need_int = 1; + break; + } + } + list_for_each_entry(ch, &smd_ch_list_wcnss, ch_list) { + if (smd_need_int(ch)) { + need_int = 1; + break; + } + } spin_unlock_irqrestore(&smd_lock, flags); do_smd_probe(); if (need_int) { - if (msm_smd_debug_mask & MSM_SMD_DEBUG) - pr_info("smd_sleep_exit need interrupt\n"); + SMD_DBG("smd_sleep_exit need interrupt\n"); tasklet_schedule(&smd_fake_irq_tasklet); } } +EXPORT_SYMBOL(smd_sleep_exit); - -void smd_kick(smd_channel_t *ch) +static int smd_is_packet(struct smd_alloc_elm *alloc_elm) { - unsigned long flags; - unsigned tmp; - - spin_lock_irqsave(&smd_lock, flags); - ch->update_state(ch); - tmp = ch->recv->state; - if (tmp != ch->last_state) { - ch->last_state = tmp; - if (tmp == SMD_SS_OPENED) - ch->notify(ch->priv, SMD_EVENT_OPEN); - else - ch->notify(ch->priv, SMD_EVENT_CLOSE); - } - ch->notify(ch->priv, SMD_EVENT_DATA); - ch->notify_other_cpu(); - spin_unlock_irqrestore(&smd_lock, flags); -} - -static int smd_is_packet(int chn, unsigned type) -{ - type &= SMD_KIND_MASK; - if (type == SMD_KIND_PACKET) + if (SMD_XFER_TYPE(alloc_elm->type) == 1) + return 0; + else if (SMD_XFER_TYPE(alloc_elm->type) == 2) return 1; - if (type == SMD_KIND_STREAM) + + /* for cases where xfer type is 0 */ + if (!strncmp(alloc_elm->name, "DAL", 3)) return 0; - /* older AMSS reports SMD_KIND_UNKNOWN always */ - if ((chn > 4) || (chn == 1)) + /* for cases where xfer type is 0 */ + if (!strncmp(alloc_elm->name, "RPCCALL_QDSP", 12)) + return 0; + + if (alloc_elm->cid > 4 || alloc_elm->cid == 1) return 1; else return 0; } -static int smd_stream_write(smd_channel_t *ch, const void *_data, int len) +static int smd_stream_write(smd_channel_t *ch, const void *_data, int len, + int user_buf) { void *ptr; const unsigned char *buf = _data; unsigned xfer; int orig_len = len; + int r = 0; + SMD_DBG("smd_stream_write() %d -> ch%d\n", len, ch->n); if (len < 0) return -EINVAL; + else if (len == 0) + return 0; while ((xfer = ch_write_buffer(ch, &ptr)) != 0) { - if (!ch_is_open(ch)) + if (!ch_is_open(ch)) { + len = orig_len; break; + } if (xfer > len) xfer = len; - memcpy(ptr, buf, xfer); + if (user_buf) { + r = copy_from_user(ptr, buf, xfer); + if (r > 0) { + pr_err("%s: " + "copy_from_user could not copy %i " + "bytes.\n", + __func__, + r); + } + } else + memcpy(ptr, buf, xfer); ch_write_done(ch, xfer); len -= xfer; buf += xfer; @@ -495,17 +1457,23 @@ static int smd_stream_write(smd_channel_t *ch, const void *_data, int len) break; } - ch->notify_other_cpu(); + if (orig_len - len) + ch->notify_other_cpu(); return orig_len - len; } -static int smd_packet_write(smd_channel_t *ch, const void *_data, int len) +static int smd_packet_write(smd_channel_t *ch, const void *_data, int len, + int user_buf) { + int ret; unsigned hdr[5]; + SMD_DBG("smd_packet_write() %d -> ch%d\n", len, ch->n); if (len < 0) return -EINVAL; + else if (len == 0) + return 0; if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE)) return -ENOMEM; @@ -513,27 +1481,41 @@ static int smd_packet_write(smd_channel_t *ch, const void *_data, int len) hdr[0] = len; hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0; - smd_stream_write(ch, hdr, sizeof(hdr)); - smd_stream_write(ch, _data, len); + + ret = smd_stream_write(ch, hdr, sizeof(hdr), 0); + if (ret < 0 || ret != sizeof(hdr)) { + SMD_DBG("%s failed to write pkt header: " + "%d returned\n", __func__, ret); + return -1; + } + + + ret = smd_stream_write(ch, _data, len, user_buf); + if (ret < 0 || ret != len) { + SMD_DBG("%s failed to write pkt data: " + "%d returned\n", __func__, ret); + return ret; + } return len; } -static int smd_stream_read(smd_channel_t *ch, void *data, int len) +static int smd_stream_read(smd_channel_t *ch, void *data, int len, int user_buf) { int r; if (len < 0) return -EINVAL; - r = ch_read(ch, data, len); + r = ch_read(ch, data, len, user_buf); if (r > 0) - ch->notify_other_cpu(); + if (!read_intr_blocked(ch)) + ch->notify_other_cpu(); return r; } -static int smd_packet_read(smd_channel_t *ch, void *data, int len) +static int smd_packet_read(smd_channel_t *ch, void *data, int len, int user_buf) { unsigned long flags; int r; @@ -544,9 +1526,10 @@ static int smd_packet_read(smd_channel_t *ch, void *data, int len) if (len > ch->current_packet) len = ch->current_packet; - r = ch_read(ch, data, len); + r = ch_read(ch, data, len, user_buf); if (r > 0) - ch->notify_other_cpu(); + if (!read_intr_blocked(ch)) + ch->notify_other_cpu(); spin_lock_irqsave(&smd_lock, flags); ch->current_packet -= r; @@ -556,7 +1539,107 @@ static int smd_packet_read(smd_channel_t *ch, void *data, int len) return r; } -static int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type) +static int smd_packet_read_from_cb(smd_channel_t *ch, void *data, int len, + int user_buf) +{ + int r; + + if (len < 0) + return -EINVAL; + + if (len > ch->current_packet) + len = ch->current_packet; + + r = ch_read(ch, data, len, user_buf); + if (r > 0) + if (!read_intr_blocked(ch)) + ch->notify_other_cpu(); + + ch->current_packet -= r; + update_packet_state(ch); + + return r; +} + +#if (defined(CONFIG_MSM_SMD_PKG4) || defined(CONFIG_MSM_SMD_PKG3)) +static int smd_alloc_v2(struct smd_channel *ch) +{ + void *buffer; + unsigned buffer_sz; + + if (is_word_access_ch(ch->type)) { + struct smd_shared_v2_word_access *shared2; + shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, + sizeof(*shared2)); + if (!shared2) { + SMD_INFO("smem_alloc failed ch=%d\n", ch->n); + return -EINVAL; + } + ch->send = &shared2->ch0; + ch->recv = &shared2->ch1; + } else { + struct smd_shared_v2 *shared2; + shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, + sizeof(*shared2)); + if (!shared2) { + SMD_INFO("smem_alloc failed ch=%d\n", ch->n); + return -EINVAL; + } + ch->send = &shared2->ch0; + ch->recv = &shared2->ch1; + } + ch->half_ch = get_half_ch_funcs(ch->type); + + buffer = smem_get_entry(SMEM_SMD_FIFO_BASE_ID + ch->n, &buffer_sz); + if (!buffer) { + SMD_INFO("smem_get_entry failed\n"); + return -EINVAL; + } + + /* buffer must be a power-of-two size */ + if (buffer_sz & (buffer_sz - 1)) { + SMD_INFO("Buffer size: %u not power of two\n", buffer_sz); + return -EINVAL; + } + buffer_sz /= 2; + ch->send_data = buffer; + ch->recv_data = buffer + buffer_sz; + ch->fifo_size = buffer_sz; + + return 0; +} + +static int smd_alloc_v1(struct smd_channel *ch) +{ + return -EINVAL; +} + +#else /* define v1 for older targets */ +static int smd_alloc_v2(struct smd_channel *ch) +{ + return -EINVAL; +} + +static int smd_alloc_v1(struct smd_channel *ch) +{ + struct smd_shared_v1 *shared1; + shared1 = smem_alloc(ID_SMD_CHANNELS + ch->n, sizeof(*shared1)); + if (!shared1) { + pr_err("smd_alloc_channel() cid %d does not exist\n", ch->n); + return -EINVAL; + } + ch->send = &shared1->ch0; + ch->recv = &shared1->ch1; + ch->send_data = shared1->data0; + ch->recv_data = shared1->data1; + ch->fifo_size = SMD_BUF_SIZE; + ch->half_ch = get_half_ch_funcs(ch->type); + return 0; +} + +#endif + +static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm) { struct smd_channel *ch; @@ -565,46 +1648,118 @@ static int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type) pr_err("smd_alloc_channel() out of memory\n"); return -1; } - ch->n = cid; + ch->n = alloc_elm->cid; + ch->type = SMD_CHANNEL_TYPE(alloc_elm->type); - if (_smd_alloc_channel(ch)) { + if (smd_alloc_v2(ch) && smd_alloc_v1(ch)) { kfree(ch); return -1; } ch->fifo_mask = ch->fifo_size - 1; - ch->type = type; - if ((type & SMD_TYPE_MASK) == SMD_TYPE_APPS_MODEM) + /* probe_worker guarentees ch->type will be a valid type */ + if (ch->type == SMD_APPS_MODEM) ch->notify_other_cpu = notify_modem_smd; - else + else if (ch->type == SMD_APPS_QDSP) ch->notify_other_cpu = notify_dsp_smd; + else if (ch->type == SMD_APPS_DSPS) + ch->notify_other_cpu = notify_dsps_smd; + else if (ch->type == SMD_APPS_WCNSS) + ch->notify_other_cpu = notify_wcnss_smd; + else if (ch->type == SMD_APPS_RPM) + ch->notify_other_cpu = notify_rpm_smd; - if (smd_is_packet(cid, type)) { + if (smd_is_packet(alloc_elm)) { ch->read = smd_packet_read; ch->write = smd_packet_write; ch->read_avail = smd_packet_read_avail; ch->write_avail = smd_packet_write_avail; ch->update_state = update_packet_state; + ch->read_from_cb = smd_packet_read_from_cb; + ch->is_pkt_ch = 1; } else { ch->read = smd_stream_read; ch->write = smd_stream_write; ch->read_avail = smd_stream_read_avail; ch->write_avail = smd_stream_write_avail; ch->update_state = update_stream_state; + ch->read_from_cb = smd_stream_read; } - if ((type & 0xff) == 0) - memcpy(ch->name, "SMD_", 4); - else - memcpy(ch->name, "DSP_", 4); - memcpy(ch->name + 4, name, 20); - ch->name[23] = 0; - ch->pdev.name = ch->name; - ch->pdev.id = -1; + memcpy(ch->name, alloc_elm->name, SMD_MAX_CH_NAME_LEN); + ch->name[SMD_MAX_CH_NAME_LEN-1] = 0; - pr_debug("smd_alloc_channel() cid=%02d size=%05d '%s'\n", - ch->n, ch->fifo_size, ch->name); + ch->pdev.name = ch->name; + ch->pdev.id = ch->type; + + SMD_INFO("smd_alloc_channel() '%s' cid=%d\n", + ch->name, ch->n); + + mutex_lock(&smd_creation_mutex); + list_add(&ch->ch_list, &smd_ch_closed_list); + mutex_unlock(&smd_creation_mutex); + + platform_device_register(&ch->pdev); + if (!strncmp(ch->name, "LOOPBACK", 8) && ch->type == SMD_APPS_MODEM) { + /* create a platform driver to be used by smd_tty driver + * so that it can access the loopback port + */ + loopback_tty_pdev.id = ch->type; + platform_device_register(&loopback_tty_pdev); + } + return 0; +} + +static inline void notify_loopback_smd(void) +{ + unsigned long flags; + struct smd_channel *ch; + + spin_lock_irqsave(&smd_lock, flags); + list_for_each_entry(ch, &smd_ch_list_loopback, ch_list) { + ch->notify(ch->priv, SMD_EVENT_DATA); + } + spin_unlock_irqrestore(&smd_lock, flags); +} + +static int smd_alloc_loopback_channel(void) +{ + static struct smd_half_channel smd_loopback_ctl; + static char smd_loopback_data[SMD_BUF_SIZE]; + struct smd_channel *ch; + + ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL); + if (ch == 0) { + pr_err("%s: out of memory\n", __func__); + return -1; + } + ch->n = SMD_LOOPBACK_CID; + + ch->send = &smd_loopback_ctl; + ch->recv = &smd_loopback_ctl; + ch->send_data = smd_loopback_data; + ch->recv_data = smd_loopback_data; + ch->fifo_size = SMD_BUF_SIZE; + + ch->fifo_mask = ch->fifo_size - 1; + ch->type = SMD_LOOPBACK_TYPE; + ch->notify_other_cpu = notify_loopback_smd; + + ch->read = smd_stream_read; + ch->write = smd_stream_write; + ch->read_avail = smd_stream_read_avail; + ch->write_avail = smd_stream_write_avail; + ch->update_state = update_stream_state; + ch->read_from_cb = smd_stream_read; + + memset(ch->name, 0, 20); + memcpy(ch->name, "local_loopback", 14); + + ch->pdev.name = ch->name; + ch->pdev.id = ch->type; + + SMD_INFO("%s: '%s' cid=%d\n", __func__, ch->name, ch->n); mutex_lock(&smd_creation_mutex); list_add(&ch->ch_list, &smd_ch_closed_list); @@ -614,53 +1769,36 @@ static int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type) return 0; } -static void smd_channel_probe_worker(struct work_struct *work) -{ - struct smd_alloc_elm *shared; - unsigned ctype; - unsigned type; - unsigned n; - - shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); - if (!shared) { - pr_err("cannot find allocation table\n"); - return; - } - for (n = 0; n < 64; n++) { - if (smd_ch_allocated[n]) - continue; - if (!shared[n].ref_count) - continue; - if (!shared[n].name[0]) - continue; - ctype = shared[n].ctype; - type = ctype & SMD_TYPE_MASK; - - /* DAL channels are stream but neither the modem, - * nor the DSP correctly indicate this. Fixup manually. - */ - if (!memcmp(shared[n].name, "DAL", 3)) - ctype = (ctype & (~SMD_KIND_MASK)) | SMD_KIND_STREAM; - - type = shared[n].ctype & SMD_TYPE_MASK; - if ((type == SMD_TYPE_APPS_MODEM) || - (type == SMD_TYPE_APPS_DSP)) - if (!smd_alloc_channel(shared[n].name, shared[n].cid, ctype)) - smd_ch_allocated[n] = 1; - } -} - static void do_nothing_notify(void *priv, unsigned flags) { } -struct smd_channel *smd_get_channel(const char *name) +static void finalize_channel_close_fn(struct work_struct *work) +{ + unsigned long flags; + struct smd_channel *ch; + struct smd_channel *index; + + mutex_lock(&smd_creation_mutex); + spin_lock_irqsave(&smd_lock, flags); + list_for_each_entry_safe(ch, index, &smd_ch_to_close_list, ch_list) { + list_del(&ch->ch_list); + list_add(&ch->ch_list, &smd_ch_closed_list); + ch->notify(ch->priv, SMD_EVENT_REOPEN_READY); + ch->notify = do_nothing_notify; + } + spin_unlock_irqrestore(&smd_lock, flags); + mutex_unlock(&smd_creation_mutex); +} + +struct smd_channel *smd_get_channel(const char *name, uint32_t type) { struct smd_channel *ch; mutex_lock(&smd_creation_mutex); list_for_each_entry(ch, &smd_ch_closed_list, ch_list) { - if (!strcmp(name, ch->name)) { + if (!strcmp(name, ch->name) && + (type == ch->type)) { list_del(&ch->ch_list); mutex_unlock(&smd_creation_mutex); return ch; @@ -671,20 +1809,49 @@ struct smd_channel *smd_get_channel(const char *name) return NULL; } -int smd_open(const char *name, smd_channel_t **_ch, - void *priv, void (*notify)(void *, unsigned)) +int smd_named_open_on_edge(const char *name, uint32_t edge, + smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)) { struct smd_channel *ch; unsigned long flags; if (smd_initialized == 0) { - pr_info("smd_open() before smd_init()\n"); + SMD_INFO("smd_open() before smd_init()\n"); return -ENODEV; } - ch = smd_get_channel(name); - if (!ch) - return -ENODEV; + SMD_DBG("smd_open('%s', %p, %p)\n", name, priv, notify); + + ch = smd_get_channel(name, edge); + if (!ch) { + /* check closing list for port */ + spin_lock_irqsave(&smd_lock, flags); + list_for_each_entry(ch, &smd_ch_closing_list, ch_list) { + if (!strncmp(name, ch->name, 20) && + (edge == ch->type)) { + /* channel exists, but is being closed */ + spin_unlock_irqrestore(&smd_lock, flags); + return -EAGAIN; + } + } + + /* check closing workqueue list for port */ + list_for_each_entry(ch, &smd_ch_to_close_list, ch_list) { + if (!strncmp(name, ch->name, 20) && + (edge == ch->type)) { + /* channel exists, but is being closed */ + spin_unlock_irqrestore(&smd_lock, flags); + return -EAGAIN; + } + } + spin_unlock_irqrestore(&smd_lock, flags); + + /* one final check to handle closing->closed race condition */ + ch = smd_get_channel(name, edge); + if (!ch) + return -ENODEV; + } if (notify == 0) notify = do_nothing_notify; @@ -694,34 +1861,51 @@ int smd_open(const char *name, smd_channel_t **_ch, ch->last_state = SMD_SS_CLOSED; ch->priv = priv; + if (edge == SMD_LOOPBACK_TYPE) { + ch->last_state = SMD_SS_OPENED; + ch->half_ch->set_state(ch->send, SMD_SS_OPENED); + ch->half_ch->set_fDSR(ch->send, 1); + ch->half_ch->set_fCTS(ch->send, 1); + ch->half_ch->set_fCD(ch->send, 1); + } + *_ch = ch; + SMD_DBG("smd_open: opening '%s'\n", ch->name); + spin_lock_irqsave(&smd_lock, flags); - - if ((ch->type & SMD_TYPE_MASK) == SMD_TYPE_APPS_MODEM) + if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_MODEM) list_add(&ch->ch_list, &smd_ch_list_modem); - else + else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_QDSP) list_add(&ch->ch_list, &smd_ch_list_dsp); + else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_DSPS) + list_add(&ch->ch_list, &smd_ch_list_dsps); + else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_WCNSS) + list_add(&ch->ch_list, &smd_ch_list_wcnss); + else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_RPM) + list_add(&ch->ch_list, &smd_ch_list_rpm); + else + list_add(&ch->ch_list, &smd_ch_list_loopback); + + SMD_DBG("%s: opening ch %d\n", __func__, ch->n); + + if (edge != SMD_LOOPBACK_TYPE) + smd_state_change(ch, ch->last_state, SMD_SS_OPENING); - /* If the remote side is CLOSING, we need to get it to - * move to OPENING (which we'll do by moving from CLOSED to - * OPENING) and then get it to move from OPENING to - * OPENED (by doing the same state change ourselves). - * - * Otherwise, it should be OPENING and we can move directly - * to OPENED so that it will follow. - */ - if (ch->recv->state == SMD_SS_CLOSING) { - ch->send->head = 0; - ch_set_state(ch, SMD_SS_OPENING); - } else { - ch_set_state(ch, SMD_SS_OPENED); - } spin_unlock_irqrestore(&smd_lock, flags); - smd_kick(ch); return 0; } +EXPORT_SYMBOL(smd_named_open_on_edge); + + +int smd_open(const char *name, smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)) +{ + return smd_named_open_on_edge(name, SMD_APPS_MODEM, _ch, priv, + notify); +} +EXPORT_SYMBOL(smd_open); int smd_close(smd_channel_t *ch) { @@ -730,48 +1914,215 @@ int smd_close(smd_channel_t *ch) if (ch == 0) return -1; - spin_lock_irqsave(&smd_lock, flags); - ch->notify = do_nothing_notify; - list_del(&ch->ch_list); - ch_set_state(ch, SMD_SS_CLOSED); - spin_unlock_irqrestore(&smd_lock, flags); + SMD_INFO("smd_close(%s)\n", ch->name); - mutex_lock(&smd_creation_mutex); - list_add(&ch->ch_list, &smd_ch_closed_list); - mutex_unlock(&smd_creation_mutex); + spin_lock_irqsave(&smd_lock, flags); + list_del(&ch->ch_list); + if (ch->n == SMD_LOOPBACK_CID) { + ch->half_ch->set_fDSR(ch->send, 0); + ch->half_ch->set_fCTS(ch->send, 0); + ch->half_ch->set_fCD(ch->send, 0); + ch->half_ch->set_state(ch->send, SMD_SS_CLOSED); + } else + ch_set_state(ch, SMD_SS_CLOSED); + + if (ch->half_ch->get_state(ch->recv) == SMD_SS_OPENED) { + list_add(&ch->ch_list, &smd_ch_closing_list); + spin_unlock_irqrestore(&smd_lock, flags); + } else { + spin_unlock_irqrestore(&smd_lock, flags); + ch->notify = do_nothing_notify; + mutex_lock(&smd_creation_mutex); + list_add(&ch->ch_list, &smd_ch_closed_list); + mutex_unlock(&smd_creation_mutex); + } return 0; } +EXPORT_SYMBOL(smd_close); + +int smd_write_start(smd_channel_t *ch, int len) +{ + int ret; + unsigned hdr[5]; + + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + if (!ch->is_pkt_ch) { + pr_err("%s: non-packet channel specified\n", __func__); + return -EACCES; + } + if (len < 1) { + pr_err("%s: invalid length: %d\n", __func__, len); + return -EINVAL; + } + + if (ch->pending_pkt_sz) { + pr_err("%s: packet of size: %d in progress\n", __func__, + ch->pending_pkt_sz); + return -EBUSY; + } + ch->pending_pkt_sz = len; + + if (smd_stream_write_avail(ch) < (SMD_HEADER_SIZE)) { + ch->pending_pkt_sz = 0; + SMD_DBG("%s: no space to write packet header\n", __func__); + return -EAGAIN; + } + + hdr[0] = len; + hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0; + + + ret = smd_stream_write(ch, hdr, sizeof(hdr), 0); + if (ret < 0 || ret != sizeof(hdr)) { + ch->pending_pkt_sz = 0; + pr_err("%s: packet header failed to write\n", __func__); + return -EPERM; + } + return 0; +} +EXPORT_SYMBOL(smd_write_start); + +int smd_write_segment(smd_channel_t *ch, void *data, int len, int user_buf) +{ + int bytes_written; + + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + if (len < 1) { + pr_err("%s: invalid length: %d\n", __func__, len); + return -EINVAL; + } + + if (!ch->pending_pkt_sz) { + pr_err("%s: no transaction in progress\n", __func__); + return -ENOEXEC; + } + if (ch->pending_pkt_sz - len < 0) { + pr_err("%s: segment of size: %d will make packet go over " + "length\n", __func__, len); + return -EINVAL; + } + + bytes_written = smd_stream_write(ch, data, len, user_buf); + + ch->pending_pkt_sz -= bytes_written; + + return bytes_written; +} +EXPORT_SYMBOL(smd_write_segment); + +int smd_write_end(smd_channel_t *ch) +{ + + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + if (ch->pending_pkt_sz) { + pr_err("%s: current packet not completely written\n", __func__); + return -E2BIG; + } + + return 0; +} +EXPORT_SYMBOL(smd_write_end); int smd_read(smd_channel_t *ch, void *data, int len) { - return ch->read(ch, data, len); + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + return ch->read(ch, data, len, 0); } +EXPORT_SYMBOL(smd_read); + +int smd_read_user_buffer(smd_channel_t *ch, void *data, int len) +{ + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + return ch->read(ch, data, len, 1); +} +EXPORT_SYMBOL(smd_read_user_buffer); + +int smd_read_from_cb(smd_channel_t *ch, void *data, int len) +{ + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + return ch->read_from_cb(ch, data, len, 0); +} +EXPORT_SYMBOL(smd_read_from_cb); int smd_write(smd_channel_t *ch, const void *data, int len) { - return ch->write(ch, data, len); -} + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } -int smd_write_atomic(smd_channel_t *ch, const void *data, int len) -{ - unsigned long flags; - int res; - spin_lock_irqsave(&smd_lock, flags); - res = ch->write(ch, data, len); - spin_unlock_irqrestore(&smd_lock, flags); - return res; + return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 0); } +EXPORT_SYMBOL(smd_write); + +int smd_write_user_buffer(smd_channel_t *ch, const void *data, int len) +{ + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 1); +} +EXPORT_SYMBOL(smd_write_user_buffer); int smd_read_avail(smd_channel_t *ch) { + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + return ch->read_avail(ch); } +EXPORT_SYMBOL(smd_read_avail); int smd_write_avail(smd_channel_t *ch) { + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + return ch->write_avail(ch); } +EXPORT_SYMBOL(smd_write_avail); + +void smd_enable_read_intr(smd_channel_t *ch) +{ + if (ch) + ch->half_ch->set_fBLOCKREADINTR(ch->send, 0); +} +EXPORT_SYMBOL(smd_enable_read_intr); + +void smd_disable_read_intr(smd_channel_t *ch) +{ + if (ch) + ch->half_ch->set_fBLOCKREADINTR(ch->send, 1); +} +EXPORT_SYMBOL(smd_disable_read_intr); int smd_wait_until_readable(smd_channel_t *ch, int bytes) { @@ -785,41 +2136,222 @@ int smd_wait_until_writable(smd_channel_t *ch, int bytes) int smd_cur_packet_size(smd_channel_t *ch) { + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + return ch->current_packet; } +EXPORT_SYMBOL(smd_cur_packet_size); + +int smd_tiocmget(smd_channel_t *ch) +{ + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + return (ch->half_ch->get_fDSR(ch->recv) ? TIOCM_DSR : 0) | + (ch->half_ch->get_fCTS(ch->recv) ? TIOCM_CTS : 0) | + (ch->half_ch->get_fCD(ch->recv) ? TIOCM_CD : 0) | + (ch->half_ch->get_fRI(ch->recv) ? TIOCM_RI : 0) | + (ch->half_ch->get_fCTS(ch->send) ? TIOCM_RTS : 0) | + (ch->half_ch->get_fDSR(ch->send) ? TIOCM_DTR : 0); +} +EXPORT_SYMBOL(smd_tiocmget); + +/* this api will be called while holding smd_lock */ +int +smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear) +{ + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + if (set & TIOCM_DTR) + ch->half_ch->set_fDSR(ch->send, 1); + + if (set & TIOCM_RTS) + ch->half_ch->set_fCTS(ch->send, 1); + + if (clear & TIOCM_DTR) + ch->half_ch->set_fDSR(ch->send, 0); + + if (clear & TIOCM_RTS) + ch->half_ch->set_fCTS(ch->send, 0); + + ch->half_ch->set_fSTATE(ch->send, 1); + barrier(); + ch->notify_other_cpu(); + + return 0; +} +EXPORT_SYMBOL(smd_tiocmset_from_cb); + +int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + if (!ch) { + pr_err("%s: Invalid channel specified\n", __func__); + return -ENODEV; + } + + spin_lock_irqsave(&smd_lock, flags); + smd_tiocmset_from_cb(ch, set, clear); + spin_unlock_irqrestore(&smd_lock, flags); + + return 0; +} +EXPORT_SYMBOL(smd_tiocmset); + +int smd_is_pkt_avail(smd_channel_t *ch) +{ + if (!ch || !ch->is_pkt_ch) + return -EINVAL; + + if (ch->current_packet) + return 1; + + update_packet_state(ch); + + return ch->current_packet ? 1 : 0; +} +EXPORT_SYMBOL(smd_is_pkt_avail); -/* ------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* + * Shared Memory Range Check + * + * Takes a physical address and an offset and checks if the resulting physical + * address would fit into one of the aux smem regions. If so, returns the + * corresponding virtual address. Otherwise returns NULL. Expects the array + * of smem regions to be in ascending physical address order. + * + * @base: physical base address to check + * @offset: offset from the base to get the final address + */ +static void *smem_range_check(void *base, unsigned offset) +{ + int i; + void *phys_addr; + unsigned size; + + for (i = 0; i < num_smem_areas; ++i) { + phys_addr = smem_areas[i].phys_addr; + size = smem_areas[i].size; + if (base < phys_addr) + return NULL; + if (base > phys_addr + size) + continue; + if (base >= phys_addr && base + offset < phys_addr + size) + return smem_areas[i].virt_addr + offset; + } + + return NULL; +} + +/* smem_alloc returns the pointer to smem item if it is already allocated. + * Otherwise, it returns NULL. + */ void *smem_alloc(unsigned id, unsigned size) { return smem_find(id, size); } +EXPORT_SYMBOL(smem_alloc); -void *smem_item(unsigned id, unsigned *size) +/* smem_alloc2 returns the pointer to smem item. If it is not allocated, + * it allocates it and then returns the pointer to it. + */ +void *smem_alloc2(unsigned id, unsigned size_in) { struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; struct smem_heap_entry *toc = shared->heap_toc; + unsigned long flags; + void *ret = NULL; + + if (!shared->heap_info.initialized) { + pr_err("%s: smem heap info not initialized\n", __func__); + return NULL; + } if (id >= SMEM_NUM_ITEMS) - return 0; + return NULL; + size_in = ALIGN(size_in, 8); + remote_spin_lock_irqsave(&remote_spinlock, flags); + if (toc[id].allocated) { + SMD_DBG("%s: %u already allocated\n", __func__, id); + if (size_in != toc[id].size) + pr_err("%s: wrong size %u (expected %u)\n", + __func__, toc[id].size, size_in); + else + ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset); + } else if (id > SMEM_FIXED_ITEM_LAST) { + SMD_DBG("%s: allocating %u\n", __func__, id); + if (shared->heap_info.heap_remaining >= size_in) { + toc[id].offset = shared->heap_info.free_offset; + toc[id].size = size_in; + wmb(); + toc[id].allocated = 1; + + shared->heap_info.free_offset += size_in; + shared->heap_info.heap_remaining -= size_in; + ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset); + } else + pr_err("%s: not enough memory %u (required %u)\n", + __func__, shared->heap_info.heap_remaining, + size_in); + } + wmb(); + remote_spin_unlock_irqrestore(&remote_spinlock, flags); + return ret; +} +EXPORT_SYMBOL(smem_alloc2); + +void *smem_get_entry(unsigned id, unsigned *size) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + struct smem_heap_entry *toc = shared->heap_toc; + int use_spinlocks = spinlocks_initialized; + void *ret = 0; + unsigned long flags = 0; + + if (id >= SMEM_NUM_ITEMS) + return ret; + + if (use_spinlocks) + remote_spin_lock_irqsave(&remote_spinlock, flags); + /* toc is in device memory and cannot be speculatively accessed */ if (toc[id].allocated) { *size = toc[id].size; - return (void *) (MSM_SHARED_RAM_BASE + toc[id].offset); + barrier(); + if (!(toc[id].reserved & BASE_ADDR_MASK)) + ret = (void *) (MSM_SHARED_RAM_BASE + toc[id].offset); + else + ret = smem_range_check( + (void *)(toc[id].reserved & BASE_ADDR_MASK), + toc[id].offset); } else { *size = 0; } + if (use_spinlocks) + remote_spin_unlock_irqrestore(&remote_spinlock, flags); - return 0; + return ret; } +EXPORT_SYMBOL(smem_get_entry); void *smem_find(unsigned id, unsigned size_in) { unsigned size; void *ptr; - ptr = smem_item(id, &size); + ptr = smem_get_entry(id, &size); if (!ptr) return 0; @@ -832,195 +2364,1117 @@ void *smem_find(unsigned id, unsigned size_in) return ptr; } +EXPORT_SYMBOL(smem_find); + +static int smsm_cb_init(void) +{ + struct smsm_state_info *state_info; + int n; + int ret = 0; + + smsm_states = kmalloc(sizeof(struct smsm_state_info)*SMSM_NUM_ENTRIES, + GFP_KERNEL); + + if (!smsm_states) { + pr_err("%s: SMSM init failed\n", __func__); + return -ENOMEM; + } + + smsm_cb_wq = create_singlethread_workqueue("smsm_cb_wq"); + if (!smsm_cb_wq) { + pr_err("%s: smsm_cb_wq creation failed\n", __func__); + kfree(smsm_states); + return -EFAULT; + } + + mutex_lock(&smsm_lock); + for (n = 0; n < SMSM_NUM_ENTRIES; n++) { + state_info = &smsm_states[n]; + state_info->last_value = __raw_readl(SMSM_STATE_ADDR(n)); + state_info->intr_mask_set = 0x0; + state_info->intr_mask_clear = 0x0; + INIT_LIST_HEAD(&state_info->callbacks); + } + mutex_unlock(&smsm_lock); + + return ret; +} + +static int smsm_init(void) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + int i; + struct smsm_size_info_type *smsm_size_info; + + i = remote_spin_lock_init(&remote_spinlock, SMEM_SPINLOCK_SMEM_ALLOC); + if (i) { + pr_err("%s: remote spinlock init failed %d\n", __func__, i); + return i; + } + spinlocks_initialized = 1; + + smsm_size_info = smem_alloc(SMEM_SMSM_SIZE_INFO, + sizeof(struct smsm_size_info_type)); + if (smsm_size_info) { + SMSM_NUM_ENTRIES = smsm_size_info->num_entries; + SMSM_NUM_HOSTS = smsm_size_info->num_hosts; + } + + i = kfifo_alloc(&smsm_snapshot_fifo, + sizeof(uint32_t) * SMSM_NUM_ENTRIES * SMSM_SNAPSHOT_CNT, + GFP_KERNEL); + if (i) { + pr_err("%s: SMSM state fifo alloc failed %d\n", __func__, i); + return i; + } + wake_lock_init(&smsm_snapshot_wakelock, WAKE_LOCK_SUSPEND, + "smsm_snapshot"); + + if (!smsm_info.state) { + smsm_info.state = smem_alloc2(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * + sizeof(uint32_t)); + + if (smsm_info.state) { + __raw_writel(0, SMSM_STATE_ADDR(SMSM_APPS_STATE)); + if ((shared->version[VERSION_MODEM] >> 16) >= 0xB) + __raw_writel(0, \ + SMSM_STATE_ADDR(SMSM_APPS_DEM_I)); + } + } + + if (!smsm_info.intr_mask) { + smsm_info.intr_mask = smem_alloc2(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * + SMSM_NUM_HOSTS * + sizeof(uint32_t)); + + if (smsm_info.intr_mask) { + for (i = 0; i < SMSM_NUM_ENTRIES; i++) + __raw_writel(0x0, + SMSM_INTR_MASK_ADDR(i, SMSM_APPS)); + + /* Configure legacy modem bits */ + __raw_writel(LEGACY_MODEM_SMSM_MASK, + SMSM_INTR_MASK_ADDR(SMSM_MODEM_STATE, + SMSM_APPS)); + } + } + + if (!smsm_info.intr_mux) + smsm_info.intr_mux = smem_alloc2(SMEM_SMD_SMSM_INTR_MUX, + SMSM_NUM_INTR_MUX * + sizeof(uint32_t)); + + i = smsm_cb_init(); + if (i) + return i; + + wmb(); + smsm_driver_state_notify(SMSM_INIT, NULL); + return 0; +} + +void smsm_reset_modem(unsigned mode) +{ + if (mode == SMSM_SYSTEM_DOWNLOAD) { + mode = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD; + } else if (mode == SMSM_MODEM_WAIT) { + mode = SMSM_RESET | SMSM_MODEM_WAIT; + } else { /* reset_mode is SMSM_RESET or default */ + mode = SMSM_RESET; + } + + smsm_change_state(SMSM_APPS_STATE, mode, mode); +} +EXPORT_SYMBOL(smsm_reset_modem); + +void smsm_reset_modem_cont(void) +{ + unsigned long flags; + uint32_t state; + + if (!smsm_info.state) + return; + + spin_lock_irqsave(&smem_lock, flags); + state = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE)) \ + & ~SMSM_MODEM_WAIT; + __raw_writel(state, SMSM_STATE_ADDR(SMSM_APPS_STATE)); + wmb(); + spin_unlock_irqrestore(&smem_lock, flags); +} +EXPORT_SYMBOL(smsm_reset_modem_cont); + +static void smsm_cb_snapshot(uint32_t use_wakelock) +{ + int n; + uint32_t new_state; + unsigned long flags; + int ret; + + ret = kfifo_avail(&smsm_snapshot_fifo); + if (ret < SMSM_SNAPSHOT_SIZE) { + pr_err("%s: SMSM snapshot full %d\n", __func__, ret); + return; + } + + /* + * To avoid a race condition with notify_smsm_cb_clients_worker, the + * following sequence must be followed: + * 1) increment snapshot count + * 2) insert data into FIFO + * + * Potentially in parallel, the worker: + * a) verifies >= 1 snapshots are in FIFO + * b) processes snapshot + * c) decrements reference count + * + * This order ensures that 1 will always occur before abc. + */ + if (use_wakelock) { + spin_lock_irqsave(&smsm_snapshot_count_lock, flags); + if (smsm_snapshot_count == 0) { + SMx_POWER_INFO("SMSM snapshot wake lock\n"); + wake_lock(&smsm_snapshot_wakelock); + } + ++smsm_snapshot_count; + spin_unlock_irqrestore(&smsm_snapshot_count_lock, flags); + } + + /* queue state entries */ + for (n = 0; n < SMSM_NUM_ENTRIES; n++) { + new_state = __raw_readl(SMSM_STATE_ADDR(n)); + + ret = kfifo_in(&smsm_snapshot_fifo, + &new_state, sizeof(new_state)); + if (ret != sizeof(new_state)) { + pr_err("%s: SMSM snapshot failure %d\n", __func__, ret); + goto restore_snapshot_count; + } + } + + /* queue wakelock usage flag */ + ret = kfifo_in(&smsm_snapshot_fifo, + &use_wakelock, sizeof(use_wakelock)); + if (ret != sizeof(use_wakelock)) { + pr_err("%s: SMSM snapshot failure %d\n", __func__, ret); + goto restore_snapshot_count; + } + + queue_work(smsm_cb_wq, &smsm_cb_work); + return; + +restore_snapshot_count: + if (use_wakelock) { + spin_lock_irqsave(&smsm_snapshot_count_lock, flags); + if (smsm_snapshot_count) { + --smsm_snapshot_count; + if (smsm_snapshot_count == 0) { + SMx_POWER_INFO("SMSM snapshot wake unlock\n"); + wake_unlock(&smsm_snapshot_wakelock); + } + } else { + pr_err("%s: invalid snapshot count\n", __func__); + } + spin_unlock_irqrestore(&smsm_snapshot_count_lock, flags); + } +} static irqreturn_t smsm_irq_handler(int irq, void *data) { unsigned long flags; - unsigned apps, modm; + + if (irq == INT_ADSP_A11_SMSM) { + uint32_t mux_val; + static uint32_t prev_smem_q6_apps_smsm; + + if (smsm_info.intr_mux && cpu_is_qsd8x50()) { + mux_val = __raw_readl( + SMSM_INTR_MUX_ADDR(SMEM_Q6_APPS_SMSM)); + if (mux_val != prev_smem_q6_apps_smsm) + prev_smem_q6_apps_smsm = mux_val; + } + + spin_lock_irqsave(&smem_lock, flags); + smsm_cb_snapshot(1); + spin_unlock_irqrestore(&smem_lock, flags); + return IRQ_HANDLED; + } spin_lock_irqsave(&smem_lock, flags); + if (!smsm_info.state) { + SMSM_INFO("\n"); + } else { + unsigned old_apps, apps; + unsigned modm = __raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE)); - apps = raw_smsm_get_state(SMSM_STATE_APPS); - modm = raw_smsm_get_state(SMSM_STATE_MODEM); + old_apps = apps = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE)); - if (msm_smd_debug_mask & MSM_SMSM_DEBUG) - pr_info("\n", apps, modm); - if (modm & SMSM_RESET) - handle_modem_crash(); + SMSM_DBG("\n", apps, modm); + if (apps & SMSM_RESET) { + /* If we get an interrupt and the apps SMSM_RESET + bit is already set, the modem is acking the + app's reset ack. */ + if (!disable_smsm_reset_handshake) + apps &= ~SMSM_RESET; + /* Issue a fake irq to handle any + * smd state changes during reset + */ + smd_fake_irq_handler(0); - do_smd_probe(); + /* queue modem restart notify chain */ + modem_queue_start_reset_notify(); + } else if (modm & SMSM_RESET) { + pr_err("\nSMSM: Modem SMSM state changed to SMSM_RESET."); + if (!disable_smsm_reset_handshake) { + apps |= SMSM_RESET; + flush_cache_all(); + outer_flush_all(); + } + modem_queue_start_reset_notify(); + + } else if (modm & SMSM_INIT) { + if (!(apps & SMSM_INIT)) { + apps |= SMSM_INIT; + modem_queue_smsm_init_notify(); + } + + if (modm & SMSM_SMDINIT) + apps |= SMSM_SMDINIT; + if ((apps & (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT)) == + (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT)) + apps |= SMSM_RUN; + } else if (modm & SMSM_SYSTEM_DOWNLOAD) { + pr_err("\nSMSM: Modem SMSM state changed to SMSM_SYSTEM_DOWNLOAD."); + modem_queue_start_reset_notify(); + } + + if (old_apps != apps) { + SMSM_DBG("\n", apps); + __raw_writel(apps, SMSM_STATE_ADDR(SMSM_APPS_STATE)); + do_smd_probe(); + notify_other_smsm(SMSM_APPS_STATE, (old_apps ^ apps)); + } + + smsm_cb_snapshot(1); + } spin_unlock_irqrestore(&smem_lock, flags); return IRQ_HANDLED; } -int smsm_change_state(enum smsm_state_item item, - uint32_t clear_mask, uint32_t set_mask) +static irqreturn_t smsm_modem_irq_handler(int irq, void *data) { - unsigned long addr = smd_info.state + item * 4; - unsigned long flags; - unsigned state; + SMx_POWER_INFO("SMSM Int Modem->Apps\n"); + ++interrupt_stats[SMD_MODEM].smsm_in_count; + return smsm_irq_handler(irq, data); +} - if (!smd_info.ready) +static irqreturn_t smsm_dsp_irq_handler(int irq, void *data) +{ + SMx_POWER_INFO("SMSM Int LPASS->Apps\n"); + ++interrupt_stats[SMD_Q6].smsm_in_count; + return smsm_irq_handler(irq, data); +} + +static irqreturn_t smsm_dsps_irq_handler(int irq, void *data) +{ + SMx_POWER_INFO("SMSM Int DSPS->Apps\n"); + ++interrupt_stats[SMD_DSPS].smsm_in_count; + return smsm_irq_handler(irq, data); +} + +static irqreturn_t smsm_wcnss_irq_handler(int irq, void *data) +{ + SMx_POWER_INFO("SMSM Int WCNSS->Apps\n"); + ++interrupt_stats[SMD_WCNSS].smsm_in_count; + return smsm_irq_handler(irq, data); +} + +/* + * Changes the global interrupt mask. The set and clear masks are re-applied + * every time the global interrupt mask is updated for callback registration + * and de-registration. + * + * The clear mask is applied first, so if a bit is set to 1 in both the clear + * mask and the set mask, the result will be that the interrupt is set. + * + * @smsm_entry SMSM entry to change + * @clear_mask 1 = clear bit, 0 = no-op + * @set_mask 1 = set bit, 0 = no-op + * + * @returns 0 for success, < 0 for error + */ +int smsm_change_intr_mask(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask) +{ + uint32_t old_mask, new_mask; + unsigned long flags; + + if (smsm_entry >= SMSM_NUM_ENTRIES) { + pr_err("smsm_change_state: Invalid entry %d\n", + smsm_entry); + return -EINVAL; + } + + if (!smsm_info.intr_mask) { + pr_err("smsm_change_intr_mask \n"); return -EIO; + } spin_lock_irqsave(&smem_lock, flags); + smsm_states[smsm_entry].intr_mask_clear = clear_mask; + smsm_states[smsm_entry].intr_mask_set = set_mask; - if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET) - handle_modem_crash(); + old_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS)); + new_mask = (old_mask & ~clear_mask) | set_mask; + __raw_writel(new_mask, SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS)); - state = (readl(addr) & ~clear_mask) | set_mask; - writel(state, addr); + wmb(); + spin_unlock_irqrestore(&smem_lock, flags); - if (msm_smd_debug_mask & MSM_SMSM_DEBUG) - pr_info("smsm_change_state %d %x\n", item, state); - notify_other_smsm(); + return 0; +} +EXPORT_SYMBOL(smsm_change_intr_mask); + +int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask) +{ + if (smsm_entry >= SMSM_NUM_ENTRIES) { + pr_err("smsm_change_state: Invalid entry %d\n", + smsm_entry); + return -EINVAL; + } + + if (!smsm_info.intr_mask) { + pr_err("smsm_change_intr_mask \n"); + return -EIO; + } + + *intr_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS)); + return 0; +} +EXPORT_SYMBOL(smsm_get_intr_mask); + +int smsm_change_state(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask) +{ + unsigned long flags; + uint32_t old_state, new_state; + + if (smsm_entry >= SMSM_NUM_ENTRIES) { + pr_err("smsm_change_state: Invalid entry %d", + smsm_entry); + return -EINVAL; + } + + if (!smsm_info.state) { + pr_err("smsm_change_state \n"); + return -EIO; + } + spin_lock_irqsave(&smem_lock, flags); + + old_state = __raw_readl(SMSM_STATE_ADDR(smsm_entry)); + new_state = (old_state & ~clear_mask) | set_mask; + __raw_writel(new_state, SMSM_STATE_ADDR(smsm_entry)); + SMSM_DBG("smsm_change_state %x\n", new_state); + notify_other_smsm(SMSM_APPS_STATE, (old_state ^ new_state)); spin_unlock_irqrestore(&smem_lock, flags); return 0; } +EXPORT_SYMBOL(smsm_change_state); -uint32_t smsm_get_state(enum smsm_state_item item) +uint32_t smsm_get_state(uint32_t smsm_entry) { - unsigned long flags; - uint32_t rv; + uint32_t rv = 0; - spin_lock_irqsave(&smem_lock, flags); + /* needs interface change to return error code */ + if (smsm_entry >= SMSM_NUM_ENTRIES) { + pr_err("smsm_change_state: Invalid entry %d", + smsm_entry); + return 0; + } - rv = readl(smd_info.state + item * 4); - - if (item == SMSM_STATE_MODEM && (rv & SMSM_RESET)) - handle_modem_crash(); - - spin_unlock_irqrestore(&smem_lock, flags); + if (!smsm_info.state) { + pr_err("smsm_get_state \n"); + } else { + rv = __raw_readl(SMSM_STATE_ADDR(smsm_entry)); + } return rv; } +EXPORT_SYMBOL(smsm_get_state); -#ifdef CONFIG_ARCH_MSM_SCORPION - -int smsm_set_sleep_duration(uint32_t delay) +/** + * Performs SMSM callback client notifiction. + */ +void notify_smsm_cb_clients_worker(struct work_struct *work) { - struct msm_dem_slave_data *ptr; + struct smsm_state_cb_info *cb_info; + struct smsm_state_info *state_info; + int n; + uint32_t new_state; + uint32_t state_changes; + uint32_t use_wakelock; + int ret; + unsigned long flags; - ptr = smem_find(SMEM_APPS_DEM_SLAVE_DATA, sizeof(*ptr)); - if (ptr == NULL) { - pr_err("smsm_set_sleep_duration \n"); - return -EIO; + if (!smd_initialized) + return; + + while (kfifo_len(&smsm_snapshot_fifo) >= SMSM_SNAPSHOT_SIZE) { + mutex_lock(&smsm_lock); + for (n = 0; n < SMSM_NUM_ENTRIES; n++) { + state_info = &smsm_states[n]; + + ret = kfifo_out(&smsm_snapshot_fifo, &new_state, + sizeof(new_state)); + if (ret != sizeof(new_state)) { + pr_err("%s: snapshot underflow %d\n", + __func__, ret); + mutex_unlock(&smsm_lock); + return; + } + + state_changes = state_info->last_value ^ new_state; + if (state_changes) { + SMx_POWER_INFO("SMSM Change %d: %08x->%08x\n", + n, state_info->last_value, + new_state); + list_for_each_entry(cb_info, + &state_info->callbacks, cb_list) { + + if (cb_info->mask & state_changes) + cb_info->notify(cb_info->data, + state_info->last_value, + new_state); + } + state_info->last_value = new_state; + } + } + + /* read wakelock flag */ + ret = kfifo_out(&smsm_snapshot_fifo, &use_wakelock, + sizeof(use_wakelock)); + if (ret != sizeof(use_wakelock)) { + pr_err("%s: snapshot underflow %d\n", + __func__, ret); + mutex_unlock(&smsm_lock); + return; + } + mutex_unlock(&smsm_lock); + + if (use_wakelock) { + spin_lock_irqsave(&smsm_snapshot_count_lock, flags); + if (smsm_snapshot_count) { + --smsm_snapshot_count; + if (smsm_snapshot_count == 0) { + SMx_POWER_INFO("SMSM snapshot" + " wake unlock\n"); + wake_unlock(&smsm_snapshot_wakelock); + } + } else { + pr_err("%s: invalid snapshot count\n", + __func__); + } + spin_unlock_irqrestore(&smsm_snapshot_count_lock, + flags); + } } - if (msm_smd_debug_mask & MSM_SMSM_DEBUG) - pr_info("smsm_set_sleep_duration %d -> %d\n", - ptr->sleep_time, delay); - ptr->sleep_time = delay; - return 0; } -#else -int smsm_set_sleep_duration(uint32_t delay) +/** + * Registers callback for SMSM state notifications when the specified + * bits change. + * + * @smsm_entry Processor entry to deregister + * @mask Bits to deregister (if result is 0, callback is removed) + * @notify Notification function to deregister + * @data Opaque data passed in to callback + * + * @returns Status code + * <0 error code + * 0 inserted new entry + * 1 updated mask of existing entry + */ +int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask, + void (*notify)(void *, uint32_t, uint32_t), void *data) { - uint32_t *ptr; + struct smsm_state_info *state; + struct smsm_state_cb_info *cb_info; + struct smsm_state_cb_info *cb_found = 0; + uint32_t new_mask = 0; + int ret = 0; - ptr = smem_find(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr)); - if (ptr == NULL) { - pr_err("smsm_set_sleep_duration \n"); - return -EIO; + if (smsm_entry >= SMSM_NUM_ENTRIES) + return -EINVAL; + + mutex_lock(&smsm_lock); + + if (!smsm_states) { + /* smsm not yet initialized */ + ret = -ENODEV; + goto cleanup; } - if (msm_smd_debug_mask & MSM_SMSM_DEBUG) - pr_info("smsm_set_sleep_duration %d -> %d\n", - *ptr, delay); - *ptr = delay; - return 0; -} -#endif + state = &smsm_states[smsm_entry]; + list_for_each_entry(cb_info, + &state->callbacks, cb_list) { + if (!ret && (cb_info->notify == notify) && + (cb_info->data == data)) { + cb_info->mask |= mask; + cb_found = cb_info; + ret = 1; + } + new_mask |= cb_info->mask; + } + + if (!cb_found) { + cb_info = kmalloc(sizeof(struct smsm_state_cb_info), + GFP_ATOMIC); + if (!cb_info) { + ret = -ENOMEM; + goto cleanup; + } + + cb_info->mask = mask; + cb_info->notify = notify; + cb_info->data = data; + INIT_LIST_HEAD(&cb_info->cb_list); + list_add_tail(&cb_info->cb_list, + &state->callbacks); + new_mask |= mask; + } + + /* update interrupt notification mask */ + if (smsm_entry == SMSM_MODEM_STATE) + new_mask |= LEGACY_MODEM_SMSM_MASK; + + if (smsm_info.intr_mask) { + unsigned long flags; + + spin_lock_irqsave(&smem_lock, flags); + new_mask = (new_mask & ~state->intr_mask_clear) + | state->intr_mask_set; + __raw_writel(new_mask, + SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS)); + wmb(); + spin_unlock_irqrestore(&smem_lock, flags); + } + +cleanup: + mutex_unlock(&smsm_lock); + return ret; +} +EXPORT_SYMBOL(smsm_state_cb_register); + + +/** + * Deregisters for SMSM state notifications for the specified bits. + * + * @smsm_entry Processor entry to deregister + * @mask Bits to deregister (if result is 0, callback is removed) + * @notify Notification function to deregister + * @data Opaque data passed in to callback + * + * @returns Status code + * <0 error code + * 0 not found + * 1 updated mask + * 2 removed callback + */ +int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask, + void (*notify)(void *, uint32_t, uint32_t), void *data) +{ + struct smsm_state_cb_info *cb_info; + struct smsm_state_cb_info *cb_tmp; + struct smsm_state_info *state; + uint32_t new_mask = 0; + int ret = 0; + + if (smsm_entry >= SMSM_NUM_ENTRIES) + return -EINVAL; + + mutex_lock(&smsm_lock); + + if (!smsm_states) { + /* smsm not yet initialized */ + mutex_unlock(&smsm_lock); + return -ENODEV; + } + + state = &smsm_states[smsm_entry]; + list_for_each_entry_safe(cb_info, cb_tmp, + &state->callbacks, cb_list) { + if (!ret && (cb_info->notify == notify) && + (cb_info->data == data)) { + cb_info->mask &= ~mask; + ret = 1; + if (!cb_info->mask) { + /* no mask bits set, remove callback */ + list_del(&cb_info->cb_list); + kfree(cb_info); + ret = 2; + continue; + } + } + new_mask |= cb_info->mask; + } + + /* update interrupt notification mask */ + if (smsm_entry == SMSM_MODEM_STATE) + new_mask |= LEGACY_MODEM_SMSM_MASK; + + if (smsm_info.intr_mask) { + unsigned long flags; + + spin_lock_irqsave(&smem_lock, flags); + new_mask = (new_mask & ~state->intr_mask_clear) + | state->intr_mask_set; + __raw_writel(new_mask, + SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS)); + wmb(); + spin_unlock_irqrestore(&smem_lock, flags); + } + + mutex_unlock(&smsm_lock); + return ret; +} +EXPORT_SYMBOL(smsm_state_cb_deregister); + +int smsm_driver_state_notifier_register(struct notifier_block *nb) +{ + int ret; + if (!nb) + return -EINVAL; + mutex_lock(&smsm_driver_state_notifier_lock); + ret = raw_notifier_chain_register(&smsm_driver_state_notifier_list, nb); + mutex_unlock(&smsm_driver_state_notifier_lock); + return ret; +} +EXPORT_SYMBOL(smsm_driver_state_notifier_register); + +int smsm_driver_state_notifier_unregister(struct notifier_block *nb) +{ + int ret; + if (!nb) + return -EINVAL; + mutex_lock(&smsm_driver_state_notifier_lock); + ret = raw_notifier_chain_unregister(&smsm_driver_state_notifier_list, + nb); + mutex_unlock(&smsm_driver_state_notifier_lock); + return ret; +} +EXPORT_SYMBOL(smsm_driver_state_notifier_unregister); + +static void smsm_driver_state_notify(uint32_t state, void *data) +{ + mutex_lock(&smsm_driver_state_notifier_lock); + raw_notifier_call_chain(&smsm_driver_state_notifier_list, + state, data); + mutex_unlock(&smsm_driver_state_notifier_lock); +} int smd_core_init(void) { int r; - - /* wait for essential items to be initialized */ - for (;;) { - unsigned size; - void *state; - state = smem_item(SMEM_SMSM_SHARED_STATE, &size); - if (size == SMSM_V1_SIZE || size == SMSM_V2_SIZE) { - smd_info.state = (unsigned)state; - break; - } - } - - smd_info.ready = 1; + unsigned long flags = IRQF_TRIGGER_RISING; + SMD_INFO("smd_core_init()\n"); r = request_irq(INT_A9_M2A_0, smd_modem_irq_handler, - IRQF_TRIGGER_RISING, "smd_dev", 0); + flags, "smd_dev", 0); if (r < 0) return r; r = enable_irq_wake(INT_A9_M2A_0); if (r < 0) - pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_0\n"); + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_A9_M2A_0\n"); - r = request_irq(INT_A9_M2A_5, smsm_irq_handler, - IRQF_TRIGGER_RISING, "smsm_dev", 0); + r = request_irq(INT_A9_M2A_5, smsm_modem_irq_handler, + flags, "smsm_dev", 0); if (r < 0) { free_irq(INT_A9_M2A_0, 0); return r; } r = enable_irq_wake(INT_A9_M2A_5); if (r < 0) - pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_5\n"); + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_A9_M2A_5\n"); #if defined(CONFIG_QDSP6) +#if (INT_ADSP_A11 == INT_ADSP_A11_SMSM) + flags |= IRQF_SHARED; +#endif r = request_irq(INT_ADSP_A11, smd_dsp_irq_handler, - IRQF_TRIGGER_RISING, "smd_dsp", 0); + flags, "smd_dev", smd_dsp_irq_handler); if (r < 0) { free_irq(INT_A9_M2A_0, 0); free_irq(INT_A9_M2A_5, 0); return r; } + + r = request_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler, + flags, "smsm_dev", smsm_dsp_irq_handler); + if (r < 0) { + free_irq(INT_A9_M2A_0, 0); + free_irq(INT_A9_M2A_5, 0); + free_irq(INT_ADSP_A11, smd_dsp_irq_handler); + return r; + } + + r = enable_irq_wake(INT_ADSP_A11); + if (r < 0) + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_ADSP_A11\n"); + +#if (INT_ADSP_A11 != INT_ADSP_A11_SMSM) + r = enable_irq_wake(INT_ADSP_A11_SMSM); + if (r < 0) + pr_err("smd_core_init: enable_irq_wake " + "failed for INT_ADSP_A11_SMSM\n"); +#endif + flags &= ~IRQF_SHARED; #endif - /* check for any SMD channels that may already exist */ - do_smd_probe(); +#if defined(CONFIG_DSPS) + r = request_irq(INT_DSPS_A11, smd_dsps_irq_handler, + flags, "smd_dev", smd_dsps_irq_handler); + if (r < 0) { + free_irq(INT_A9_M2A_0, 0); + free_irq(INT_A9_M2A_5, 0); + free_irq(INT_ADSP_A11, smd_dsp_irq_handler); + free_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler); + return r; + } - /* indicate that we're up and running */ - smsm_change_state(SMSM_STATE_APPS, - ~0, SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT | SMSM_RUN); -#ifdef CONFIG_ARCH_MSM_SCORPION - smsm_change_state(SMSM_STATE_APPS_DEM, ~0, 0); + r = enable_irq_wake(INT_DSPS_A11); + if (r < 0) + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_ADSP_A11\n"); #endif +#if defined(CONFIG_WCNSS) + r = request_irq(INT_WCNSS_A11, smd_wcnss_irq_handler, + flags, "smd_dev", smd_wcnss_irq_handler); + if (r < 0) { + free_irq(INT_A9_M2A_0, 0); + free_irq(INT_A9_M2A_5, 0); + free_irq(INT_ADSP_A11, smd_dsp_irq_handler); + free_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler); + free_irq(INT_DSPS_A11, smd_dsps_irq_handler); + return r; + } + + r = enable_irq_wake(INT_WCNSS_A11); + if (r < 0) + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_WCNSS_A11\n"); + + r = request_irq(INT_WCNSS_A11_SMSM, smsm_wcnss_irq_handler, + flags, "smsm_dev", smsm_wcnss_irq_handler); + if (r < 0) { + free_irq(INT_A9_M2A_0, 0); + free_irq(INT_A9_M2A_5, 0); + free_irq(INT_ADSP_A11, smd_dsp_irq_handler); + free_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler); + free_irq(INT_DSPS_A11, smd_dsps_irq_handler); + free_irq(INT_WCNSS_A11, smd_wcnss_irq_handler); + return r; + } + + r = enable_irq_wake(INT_WCNSS_A11_SMSM); + if (r < 0) + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_WCNSS_A11_SMSM\n"); +#endif + +#if defined(CONFIG_DSPS_SMSM) + r = request_irq(INT_DSPS_A11_SMSM, smsm_dsps_irq_handler, + flags, "smsm_dev", smsm_dsps_irq_handler); + if (r < 0) { + free_irq(INT_A9_M2A_0, 0); + free_irq(INT_A9_M2A_5, 0); + free_irq(INT_ADSP_A11, smd_dsp_irq_handler); + free_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler); + free_irq(INT_DSPS_A11, smd_dsps_irq_handler); + free_irq(INT_WCNSS_A11, smd_wcnss_irq_handler); + free_irq(INT_WCNSS_A11_SMSM, smsm_wcnss_irq_handler); + return r; + } + + r = enable_irq_wake(INT_DSPS_A11_SMSM); + if (r < 0) + pr_err("smd_core_init: " + "enable_irq_wake failed for INT_DSPS_A11_SMSM\n"); +#endif + SMD_INFO("smd_core_init() done\n"); + return 0; } +static int intr_init(struct interrupt_config_item *private_irq, + struct smd_irq_config *platform_irq, + struct platform_device *pdev + ) +{ + int irq_id; + int ret; + int ret_wake; + + private_irq->out_bit_pos = platform_irq->out_bit_pos; + private_irq->out_offset = platform_irq->out_offset; + private_irq->out_base = platform_irq->out_base; + + irq_id = platform_get_irq_byname( + pdev, + platform_irq->irq_name + ); + SMD_DBG("smd: %s: register irq: %s id: %d\n", __func__, + platform_irq->irq_name, irq_id); + ret = request_irq(irq_id, + private_irq->irq_handler, + platform_irq->flags, + platform_irq->device_name, + (void *)platform_irq->dev_id + ); + if (ret < 0) { + platform_irq->irq_id = ret; + } else { + platform_irq->irq_id = irq_id; + ret_wake = enable_irq_wake(irq_id); + if (ret_wake < 0) { + pr_err("smd: enable_irq_wake failed on %s", + platform_irq->irq_name); + } + } + + return ret; +} + +int sort_cmp_func(const void *a, const void *b) +{ + struct smem_area *left = (struct smem_area *)(a); + struct smem_area *right = (struct smem_area *)(b); + + return left->phys_addr - right->phys_addr; +} + +int smd_core_platform_init(struct platform_device *pdev) +{ + int i; + int ret; + uint32_t num_ss; + struct smd_platform *smd_platform_data; + struct smd_subsystem_config *smd_ss_config_list; + struct smd_subsystem_config *cfg; + int err_ret = 0; + struct smd_smem_regions *smd_smem_areas; + int smem_idx = 0; + + smd_platform_data = pdev->dev.platform_data; + num_ss = smd_platform_data->num_ss_configs; + smd_ss_config_list = smd_platform_data->smd_ss_configs; + + if (smd_platform_data->smd_ssr_config) + disable_smsm_reset_handshake = smd_platform_data-> + smd_ssr_config->disable_smsm_reset_handshake; + + smd_smem_areas = smd_platform_data->smd_smem_areas; + if (smd_smem_areas) { + num_smem_areas = smd_platform_data->num_smem_areas; + smem_areas = kmalloc(sizeof(struct smem_area) * num_smem_areas, + GFP_KERNEL); + if (!smem_areas) { + pr_err("%s: smem_areas kmalloc failed\n", __func__); + err_ret = -ENOMEM; + goto smem_areas_alloc_fail; + } + + for (smem_idx = 0; smem_idx < num_smem_areas; ++smem_idx) { + smem_areas[smem_idx].phys_addr = + smd_smem_areas[smem_idx].phys_addr; + smem_areas[smem_idx].size = + smd_smem_areas[smem_idx].size; + smem_areas[smem_idx].virt_addr = ioremap_nocache( + (unsigned long)(smem_areas[smem_idx].phys_addr), + smem_areas[smem_idx].size); + if (!smem_areas[smem_idx].virt_addr) { + pr_err("%s: ioremap_nocache() of addr:%p" + " size: %x\n", __func__, + smem_areas[smem_idx].phys_addr, + smem_areas[smem_idx].size); + err_ret = -ENOMEM; + ++smem_idx; + goto smem_failed; + } + } + sort(smem_areas, num_smem_areas, + sizeof(struct smem_area), + sort_cmp_func, NULL); + } + + for (i = 0; i < num_ss; i++) { + cfg = &smd_ss_config_list[i]; + + ret = intr_init( + &private_intr_config[cfg->irq_config_id].smd, + &cfg->smd_int, + pdev + ); + + if (ret < 0) { + err_ret = ret; + pr_err("smd: register irq failed on %s\n", + cfg->smd_int.irq_name); + goto intr_failed; + } + + /* only init smsm structs if this edge supports smsm */ + if (cfg->smsm_int.irq_id) + ret = intr_init( + &private_intr_config[cfg->irq_config_id].smsm, + &cfg->smsm_int, + pdev + ); + + if (ret < 0) { + err_ret = ret; + pr_err("smd: register irq failed on %s\n", + cfg->smsm_int.irq_name); + goto intr_failed; + } + + if (cfg->subsys_name) + strlcpy(edge_to_pids[cfg->edge].subsys_name, + cfg->subsys_name, SMD_MAX_CH_NAME_LEN); + } + + + SMD_INFO("smd_core_platform_init() done\n"); + return 0; + +intr_failed: + pr_err("smd: deregistering IRQs\n"); + for (i = 0; i < num_ss; ++i) { + cfg = &smd_ss_config_list[i]; + + if (cfg->smd_int.irq_id >= 0) + free_irq(cfg->smd_int.irq_id, + (void *)cfg->smd_int.dev_id + ); + if (cfg->smsm_int.irq_id >= 0) + free_irq(cfg->smsm_int.irq_id, + (void *)cfg->smsm_int.dev_id + ); + } +smem_failed: + for (smem_idx = smem_idx - 1; smem_idx >= 0; --smem_idx) + iounmap(smem_areas[smem_idx].virt_addr); + kfree(smem_areas); +smem_areas_alloc_fail: + return err_ret; +} + static int __devinit msm_smd_probe(struct platform_device *pdev) { - /* - * If we haven't waited for the ARM9 to boot up till now, - * then we need to wait here. Otherwise this should just - * return immediately. - */ - proc_comm_boot_wait(); + int ret; + SMD_INFO("smd probe\n"); INIT_WORK(&probe_work, smd_channel_probe_worker); - if (smd_core_init()) { - pr_err("smd_core_init() failed\n"); + channel_close_wq = create_singlethread_workqueue("smd_channel_close"); + if (IS_ERR(channel_close_wq)) { + pr_err("%s: create_singlethread_workqueue ENOMEM\n", __func__); + return -ENOMEM; + } + + if (smsm_init()) { + pr_err("smsm_init() failed\n"); return -1; } - do_smd_probe(); - - msm_check_for_modem_crash = check_for_modem_crash; - - msm_init_last_radio_log(THIS_MODULE); + if (pdev) { + if (pdev->dev.of_node) { + pr_err("SMD: Device tree not currently supported\n"); + return -ENODEV; + } else if (pdev->dev.platform_data) { + ret = smd_core_platform_init(pdev); + if (ret) { + pr_err( + "SMD: smd_core_platform_init() failed\n"); + return -ENODEV; + } + } else { + ret = smd_core_init(); + if (ret) { + pr_err("smd_core_init() failed\n"); + return -ENODEV; + } + } + } else { + pr_err("SMD: PDEV not found\n"); + return -ENODEV; + } smd_initialized = 1; + smd_alloc_loopback_channel(); + smsm_irq_handler(0, 0); + tasklet_schedule(&smd_fake_irq_tasklet); + return 0; } +static int restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data); + +static struct restart_notifier_block restart_notifiers[] = { + {SMD_MODEM, "modem", .nb.notifier_call = restart_notifier_cb}, + {SMD_Q6, "lpass", .nb.notifier_call = restart_notifier_cb}, + {SMD_WCNSS, "riva", .nb.notifier_call = restart_notifier_cb}, + {SMD_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb}, + {SMD_MODEM, "gss", .nb.notifier_call = restart_notifier_cb}, +}; + +static int restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data) +{ + if (code == SUBSYS_AFTER_SHUTDOWN) { + struct restart_notifier_block *notifier; + + notifier = container_of(this, + struct restart_notifier_block, nb); + SMD_INFO("%s: ssrestart for processor %d ('%s')\n", + __func__, notifier->processor, + notifier->name); + + smd_channel_reset(notifier->processor); + } + + return NOTIFY_DONE; +} + +static __init int modem_restart_late_init(void) +{ + int i; + void *handle; + struct restart_notifier_block *nb; + + for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) { + nb = &restart_notifiers[i]; + handle = subsys_notif_register_notifier(nb->name, &nb->nb); + SMD_DBG("%s: registering notif for '%s', handle=%p\n", + __func__, nb->name, handle); + } + return 0; +} +late_initcall(modem_restart_late_init); + static struct platform_driver msm_smd_driver = { .probe = msm_smd_probe, .driver = { @@ -1029,8 +3483,14 @@ static struct platform_driver msm_smd_driver = { }, }; -static int __init msm_smd_init(void) +int __init msm_smd_init(void) { + static bool registered; + + if (registered) + return 0; + + registered = true; return platform_driver_register(&msm_smd_driver); } diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c index c56df9e932a..d64bcf2d5b9 100644 --- a/arch/arm/mach-msm/smd_debug.c +++ b/arch/arm/mach-msm/smd_debug.c @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/smd_debug.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -16,6 +17,8 @@ #include #include +#include +#include #include @@ -45,60 +48,198 @@ static char *chstate(unsigned n) } } - -static int dump_ch(char *buf, int max, struct smd_channel *ch) +static int debug_f3(char *buf, int max) { - volatile struct smd_half_channel *s = ch->send; - volatile struct smd_half_channel *r = ch->recv; + char *x; + int size; + int i = 0, j = 0; + unsigned cols = 0; + char str[4*sizeof(unsigned)+1] = {0}; - return scnprintf( - buf, max, - "ch%02d:" - " %8s(%05d/%05d) %c%c%c%c%c%c%c <->" - " %8s(%05d/%05d) %c%c%c%c%c%c%c '%s'\n", ch->n, - chstate(s->state), s->tail, s->head, - s->fDSR ? 'D' : 'd', - s->fCTS ? 'C' : 'c', - s->fCD ? 'C' : 'c', - s->fRI ? 'I' : 'i', - s->fHEAD ? 'W' : 'w', - s->fTAIL ? 'R' : 'r', - s->fSTATE ? 'S' : 's', - chstate(r->state), r->tail, r->head, - r->fDSR ? 'D' : 'd', - r->fCTS ? 'R' : 'r', - r->fCD ? 'C' : 'c', - r->fRI ? 'I' : 'i', - r->fHEAD ? 'W' : 'w', - r->fTAIL ? 'R' : 'r', - r->fSTATE ? 'S' : 's', - ch->name - ); + i += scnprintf(buf + i, max - i, + "Printing to log\n"); + + x = smem_get_entry(SMEM_ERR_F3_TRACE_LOG, &size); + if (x != 0) { + pr_info("smem: F3 TRACE LOG\n"); + while (size > 0) { + if (size >= sizeof(unsigned)) { + pr_info("%08x", *((unsigned *) x)); + for (j = 0; j < sizeof(unsigned); ++j) + if (isprint(*(x+j))) + str[cols*sizeof(unsigned) + j] + = *(x+j); + else + str[cols*sizeof(unsigned) + j] + = '-'; + x += sizeof(unsigned); + size -= sizeof(unsigned); + } else { + while (size-- > 0) + pr_info("%02x", (unsigned) *x++); + break; + } + if (cols == 3) { + cols = 0; + str[4*sizeof(unsigned)] = 0; + pr_info(" %s\n", str); + str[0] = 0; + } else { + cols++; + pr_info(" "); + } + } + pr_info("\n"); + } + + return max; } -static int debug_read_stat(char *buf, int max) +static int debug_int_stats(char *buf, int max) +{ + int i = 0; + int subsys; + struct interrupt_stat *stats = interrupt_stats; + const char *subsys_name; + + i += scnprintf(buf + i, max - i, + " Subsystem | In | Out (Hardcoded) |" + " Out (Configured) |\n"); + + for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) { + subsys_name = smd_pid_to_subsystem(subsys); + if (subsys_name) { + i += scnprintf(buf + i, max - i, + "%-10s %4s | %9u | %9u | %9u |\n", + smd_pid_to_subsystem(subsys), "smd", + stats->smd_in_count, + stats->smd_out_hardcode_count, + stats->smd_out_config_count); + + i += scnprintf(buf + i, max - i, + "%-10s %4s | %9u | %9u | %9u |\n", + smd_pid_to_subsystem(subsys), "smsm", + stats->smsm_in_count, + stats->smsm_out_hardcode_count, + stats->smsm_out_config_count); + } + ++stats; + } + + return i; +} + +static int debug_int_stats_reset(char *buf, int max) +{ + int i = 0; + int subsys; + struct interrupt_stat *stats = interrupt_stats; + + i += scnprintf(buf + i, max - i, "Resetting interrupt stats.\n"); + + for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) { + stats->smd_in_count = 0; + stats->smd_out_hardcode_count = 0; + stats->smd_out_config_count = 0; + stats->smsm_in_count = 0; + stats->smsm_out_hardcode_count = 0; + stats->smsm_out_config_count = 0; + ++stats; + } + + return i; +} + +static int debug_diag(char *buf, int max) +{ + int i = 0; + + i += scnprintf(buf + i, max - i, + "Printing to log\n"); + smd_diag(); + + return i; +} + +static int debug_modem_err_f3(char *buf, int max) +{ + char *x; + int size; + int i = 0, j = 0; + unsigned cols = 0; + char str[4*sizeof(unsigned)+1] = {0}; + + x = smem_get_entry(SMEM_ERR_F3_TRACE_LOG, &size); + if (x != 0) { + pr_info("smem: F3 TRACE LOG\n"); + while (size > 0 && max - i) { + if (size >= sizeof(unsigned)) { + i += scnprintf(buf + i, max - i, "%08x", + *((unsigned *) x)); + for (j = 0; j < sizeof(unsigned); ++j) + if (isprint(*(x+j))) + str[cols*sizeof(unsigned) + j] + = *(x+j); + else + str[cols*sizeof(unsigned) + j] + = '-'; + x += sizeof(unsigned); + size -= sizeof(unsigned); + } else { + while (size-- > 0 && max - i) + i += scnprintf(buf + i, max - i, + "%02x", + (unsigned) *x++); + break; + } + if (cols == 3) { + cols = 0; + str[4*sizeof(unsigned)] = 0; + i += scnprintf(buf + i, max - i, " %s\n", + str); + str[0] = 0; + } else { + cols++; + i += scnprintf(buf + i, max - i, " "); + } + } + i += scnprintf(buf + i, max - i, "\n"); + } + + return i; +} + +static int debug_modem_err(char *buf, int max) +{ + char *x; + int size; + int i = 0; + + x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); + if (x != 0) { + x[SZ_DIAG_ERR_MSG - 1] = 0; + i += scnprintf(buf + i, max - i, + "smem: DIAG '%s'\n", x); + } + + x = smem_get_entry(SMEM_ERR_CRASH_LOG, &size); + if (x != 0) { + x[size - 1] = 0; + i += scnprintf(buf + i, max - i, + "smem: CRASH LOG\n'%s'\n", x); + } + i += scnprintf(buf + i, max - i, "\n"); + + return i; +} + +static int debug_read_diag_msg(char *buf, int max) { char *msg; int i = 0; msg = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); - if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET) - i += scnprintf(buf + i, max - i, - "smsm: ARM9 HAS CRASHED\n"); - - i += scnprintf(buf + i, max - i, "smsm: a9: %08x a11: %08x\n", - raw_smsm_get_state(SMSM_STATE_MODEM), - raw_smsm_get_state(SMSM_STATE_APPS)); -#ifdef CONFIG_ARCH_MSM_SCORPION - i += scnprintf(buf + i, max - i, "smsm dem: apps: %08x modem: %08x " - "qdsp6: %08x power: %08x time: %08x\n", - raw_smsm_get_state(SMSM_STATE_APPS_DEM), - raw_smsm_get_state(SMSM_STATE_MODEM_DEM), - raw_smsm_get_state(SMSM_STATE_QDSP6_DEM), - raw_smsm_get_state(SMSM_STATE_POWER_MASTER_DEM), - raw_smsm_get_state(SMSM_STATE_TIME_MASTER_DEM)); -#endif if (msg) { msg[SZ_DIAG_ERR_MSG - 1] = 0; i += scnprintf(buf + i, max - i, "diag: '%s'\n", msg); @@ -106,6 +247,278 @@ static int debug_read_stat(char *buf, int max) return i; } +static int dump_ch(char *buf, int max, int n, + void *half_ch_s, + void *half_ch_r, + struct smd_half_channel_access *half_ch_funcs, + unsigned size) +{ + return scnprintf( + buf, max, + "ch%02d:" + " %8s(%04d/%04d) %c%c%c%c%c%c%c%c <->" + " %8s(%04d/%04d) %c%c%c%c%c%c%c%c : %5x\n", n, + chstate(half_ch_funcs->get_state(half_ch_s)), + half_ch_funcs->get_tail(half_ch_s), + half_ch_funcs->get_head(half_ch_s), + half_ch_funcs->get_fDSR(half_ch_s) ? 'D' : 'd', + half_ch_funcs->get_fCTS(half_ch_s) ? 'C' : 'c', + half_ch_funcs->get_fCD(half_ch_s) ? 'C' : 'c', + half_ch_funcs->get_fRI(half_ch_s) ? 'I' : 'i', + half_ch_funcs->get_fHEAD(half_ch_s) ? 'W' : 'w', + half_ch_funcs->get_fTAIL(half_ch_s) ? 'R' : 'r', + half_ch_funcs->get_fSTATE(half_ch_s) ? 'S' : 's', + half_ch_funcs->get_fBLOCKREADINTR(half_ch_s) ? 'B' : 'b', + chstate(half_ch_funcs->get_state(half_ch_r)), + half_ch_funcs->get_tail(half_ch_r), + half_ch_funcs->get_head(half_ch_r), + half_ch_funcs->get_fDSR(half_ch_r) ? 'D' : 'd', + half_ch_funcs->get_fCTS(half_ch_r) ? 'C' : 'c', + half_ch_funcs->get_fCD(half_ch_r) ? 'C' : 'c', + half_ch_funcs->get_fRI(half_ch_r) ? 'I' : 'i', + half_ch_funcs->get_fHEAD(half_ch_r) ? 'W' : 'w', + half_ch_funcs->get_fTAIL(half_ch_r) ? 'R' : 'r', + half_ch_funcs->get_fSTATE(half_ch_r) ? 'S' : 's', + half_ch_funcs->get_fBLOCKREADINTR(half_ch_r) ? 'B' : 'b', + size + ); +} + +static int debug_read_smsm_state(char *buf, int max) +{ + uint32_t *smsm; + int n, i = 0; + + smsm = smem_find(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + if (smsm) + for (n = 0; n < SMSM_NUM_ENTRIES; n++) + i += scnprintf(buf + i, max - i, "entry %d: 0x%08x\n", + n, smsm[n]); + + return i; +} + +struct SMSM_CB_DATA { + int cb_count; + void *data; + uint32_t old_state; + uint32_t new_state; +}; +static struct SMSM_CB_DATA smsm_cb_data; +static struct completion smsm_cb_completion; + +static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state) +{ + smsm_cb_data.cb_count++; + smsm_cb_data.old_state = old_state; + smsm_cb_data.new_state = new_state; + smsm_cb_data.data = data; + complete_all(&smsm_cb_completion); +} + +#define UT_EQ_INT(a, b) \ + if ((a) != (b)) { \ + i += scnprintf(buf + i, max - i, \ + "%s:%d " #a "(%d) != " #b "(%d)\n", \ + __func__, __LINE__, \ + a, b); \ + break; \ + } \ + do {} while (0) + +#define UT_GT_INT(a, b) \ + if ((a) <= (b)) { \ + i += scnprintf(buf + i, max - i, \ + "%s:%d " #a "(%d) > " #b "(%d)\n", \ + __func__, __LINE__, \ + a, b); \ + break; \ + } \ + do {} while (0) + +#define SMSM_CB_TEST_INIT() \ + do { \ + smsm_cb_data.cb_count = 0; \ + smsm_cb_data.old_state = 0; \ + smsm_cb_data.new_state = 0; \ + smsm_cb_data.data = 0; \ + } while (0) + + +static int debug_test_smsm(char *buf, int max) +{ + int i = 0; + int test_num = 0; + int ret; + + /* Test case 1 - Register new callback for notification */ + do { + test_num++; + SMSM_CB_TEST_INIT(); + ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 0); + + /* de-assert SMSM_SMD_INIT to trigger state update */ + UT_EQ_INT(smsm_cb_data.cb_count, 0); + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + + UT_EQ_INT(smsm_cb_data.cb_count, 1); + UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, SMSM_SMDINIT); + UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, 0x0); + UT_EQ_INT((int)smsm_cb_data.data, 0x1234); + + /* re-assert SMSM_SMD_INIT to trigger state update */ + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 2); + UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, 0x0); + UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, SMSM_SMDINIT); + + /* deregister callback */ + ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 2); + + /* make sure state change doesn't cause any more callbacks */ + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); + UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 2); + + i += scnprintf(buf + i, max - i, "Test %d - PASS\n", test_num); + } while (0); + + /* Test case 2 - Update already registered callback */ + do { + test_num++; + SMSM_CB_TEST_INIT(); + ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 0); + ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_INIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 1); + + /* verify both callback bits work */ + INIT_COMPLETION(smsm_cb_completion); + UT_EQ_INT(smsm_cb_data.cb_count, 0); + smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 1); + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 2); + + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 3); + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 4); + + /* deregister 1st callback */ + ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 1); + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); + UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 4); + + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 5); + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 6); + + /* deregister 2nd callback */ + ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_INIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 2); + + /* make sure state change doesn't cause any more callbacks */ + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); + UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 6); + + i += scnprintf(buf + i, max - i, "Test %d - PASS\n", test_num); + } while (0); + + /* Test case 3 - Two callback registrations with different data */ + do { + test_num++; + SMSM_CB_TEST_INIT(); + ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 0); + ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_INIT, + smsm_state_cb, (void *)0x3456); + UT_EQ_INT(ret, 0); + + /* verify both callbacks work */ + INIT_COMPLETION(smsm_cb_completion); + UT_EQ_INT(smsm_cb_data.cb_count, 0); + smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 1); + UT_EQ_INT((int)smsm_cb_data.data, 0x1234); + + INIT_COMPLETION(smsm_cb_completion); + smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); + UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, + msecs_to_jiffies(20)), 0); + UT_EQ_INT(smsm_cb_data.cb_count, 2); + UT_EQ_INT((int)smsm_cb_data.data, 0x3456); + + /* cleanup and unregister + * degregister in reverse to verify data field is + * being used + */ + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); + smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); + ret = smsm_state_cb_deregister(SMSM_APPS_STATE, + SMSM_INIT, + smsm_state_cb, (void *)0x3456); + UT_EQ_INT(ret, 2); + ret = smsm_state_cb_deregister(SMSM_APPS_STATE, + SMSM_SMDINIT, + smsm_state_cb, (void *)0x1234); + UT_EQ_INT(ret, 2); + + i += scnprintf(buf + i, max - i, "Test %d - PASS\n", test_num); + } while (0); + + return i; +} + static int debug_read_mem(char *buf, int max) { unsigned n; @@ -129,29 +542,117 @@ static int debug_read_mem(char *buf, int max) return i; } +#if (!defined(CONFIG_MSM_SMD_PKG4) && !defined(CONFIG_MSM_SMD_PKG3)) static int debug_read_ch(char *buf, int max) { - struct smd_channel *ch; - unsigned long flags; - int i = 0; + void *shared; + int n, i = 0; + struct smd_alloc_elm *ch_tbl; + unsigned ch_type; + unsigned shared_size; - spin_lock_irqsave(&smd_lock, flags); - list_for_each_entry(ch, &smd_ch_list_dsp, ch_list) - i += dump_ch(buf + i, max - i, ch); - list_for_each_entry(ch, &smd_ch_list_modem, ch_list) - i += dump_ch(buf + i, max - i, ch); - list_for_each_entry(ch, &smd_ch_closed_list, ch_list) - i += dump_ch(buf + i, max - i, ch); - spin_unlock_irqrestore(&smd_lock, flags); + ch_tbl = smem_find(ID_CH_ALLOC_TBL, sizeof(*ch_tbl) * 64); + if (!ch_tbl) + goto fail; + + for (n = 0; n < SMD_CHANNELS; n++) { + ch_type = SMD_CHANNEL_TYPE(ch_tbl[n].type); + if (is_word_access_ch(ch_type)) + shared_size = + sizeof(struct smd_half_channel_word_access); + else + shared_size = sizeof(struct smd_half_channel); + shared = smem_find(ID_SMD_CHANNELS + n, + 2 * shared_size + SMD_BUF_SIZE); + + if (shared == 0) + continue; + i += dump_ch(buf + i, max - i, n, shared, + (shared + shared_size + + SMD_BUF_SIZE), get_half_ch_funcs(ch_type), + SMD_BUF_SIZE); + } + +fail: + return i; +} +#else +static int debug_read_ch(char *buf, int max) +{ + void *shared, *buffer; + unsigned buffer_sz; + int n, i = 0; + struct smd_alloc_elm *ch_tbl; + unsigned ch_type; + unsigned shared_size; + + ch_tbl = smem_find(ID_CH_ALLOC_TBL, sizeof(*ch_tbl) * 64); + if (!ch_tbl) + goto fail; + + for (n = 0; n < SMD_CHANNELS; n++) { + ch_type = SMD_CHANNEL_TYPE(ch_tbl[n].type); + if (is_word_access_ch(ch_type)) + shared_size = + sizeof(struct smd_half_channel_word_access); + else + shared_size = sizeof(struct smd_half_channel); + + shared = smem_find(ID_SMD_CHANNELS + n, 2 * shared_size); + + if (shared == 0) + continue; + + buffer = smem_get_entry(SMEM_SMD_FIFO_BASE_ID + n, &buffer_sz); + + if (buffer == 0) + continue; + + i += dump_ch(buf + i, max - i, n, shared, + (shared + shared_size), + get_half_ch_funcs(ch_type), + buffer_sz / 2); + } + +fail: + return i; +} +#endif + +static int debug_read_smem_version(char *buf, int max) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + uint32_t n, version, i = 0; + + for (n = 0; n < 32; n++) { + version = shared->version[n]; + i += scnprintf(buf + i, max - i, + "entry %d: smem = %d proc_comm = %d\n", n, + version >> 16, + version & 0xffff); + } return i; } -static int debug_read_version(char *buf, int max) +/* NNV: revist, it may not be smd version */ +static int debug_read_smd_version(char *buf, int max) { - struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; - unsigned version = shared->version[VERSION_MODEM]; - return sprintf(buf, "%d.%d\n", version >> 16, version & 0xffff); + uint32_t *smd_ver; + uint32_t n, version, i = 0; + + smd_ver = smem_alloc(SMEM_VERSION_SMD, 32 * sizeof(uint32_t)); + + if (smd_ver) + for (n = 0; n < 32; n++) { + version = smd_ver[n]; + i += scnprintf(buf + i, max - i, + "entry %d: %d.%d\n", n, + version >> 16, + version & 0xffff); + } + + return i; } static int debug_read_build_id(char *buf, int max) @@ -159,7 +660,7 @@ static int debug_read_build_id(char *buf, int max) unsigned size; void *data; - data = smem_item(SMEM_HW_SW_BUILD_ID, &size); + data = smem_get_entry(SMEM_HW_SW_BUILD_ID, &size); if (!data) return 0; @@ -175,23 +676,62 @@ static int debug_read_alloc_tbl(char *buf, int max) struct smd_alloc_elm *shared; int n, i = 0; - shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); + shared = smem_find(ID_CH_ALLOC_TBL, sizeof(struct smd_alloc_elm[64])); + + if (!shared) + return 0; for (n = 0; n < 64; n++) { - if (shared[n].ref_count == 0) - continue; i += scnprintf(buf + i, max - i, - "%03d: %-20s cid=%02d type=%03d " - "kind=%02d ref_count=%d\n", - n, shared[n].name, shared[n].cid, - shared[n].ctype & 0xff, - (shared[n].ctype >> 8) & 0xf, - shared[n].ref_count); + "name=%s cid=%d ch type=%d " + "xfer type=%d ref_count=%d\n", + shared[n].name, + shared[n].cid, + SMD_CHANNEL_TYPE(shared[n].type), + SMD_XFER_TYPE(shared[n].type), + shared[n].ref_count); } return i; } +static int debug_read_intr_mask(char *buf, int max) +{ + uint32_t *smsm; + int m, n, i = 0; + + smsm = smem_alloc(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t)); + + if (smsm) + for (m = 0; m < SMSM_NUM_ENTRIES; m++) { + i += scnprintf(buf + i, max - i, "entry %d:", m); + for (n = 0; n < SMSM_NUM_HOSTS; n++) + i += scnprintf(buf + i, max - i, + " host %d: 0x%08x", + n, smsm[m * SMSM_NUM_HOSTS + n]); + i += scnprintf(buf + i, max - i, "\n"); + } + + return i; +} + +static int debug_read_intr_mux(char *buf, int max) +{ + uint32_t *smsm; + int n, i = 0; + + smsm = smem_alloc(SMEM_SMD_SMSM_INTR_MUX, + SMSM_NUM_INTR_MUX * sizeof(uint32_t)); + + if (smsm) + for (n = 0; n < SMSM_NUM_INTR_MUX; n++) + i += scnprintf(buf + i, max - i, "entry %d: %d\n", + n, smsm[n]); + + return i; +} + #define DEBUG_BUFMAX 4096 static char debug_buffer[DEBUG_BUFMAX]; @@ -199,14 +739,18 @@ static ssize_t debug_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int (*fill)(char *buf, int max) = file->private_data; - int bsize = fill(debug_buffer, DEBUG_BUFMAX); + int bsize; + + if (*ppos != 0) + return 0; + + bsize = fill(debug_buffer, DEBUG_BUFMAX); return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); } static const struct file_operations debug_ops = { .read = debug_read, .open = simple_open, - .llseek = default_llseek, }; static void debug_create(const char *name, umode_t mode, @@ -216,25 +760,53 @@ static void debug_create(const char *name, umode_t mode, debugfs_create_file(name, mode, dent, fill, &debug_ops); } -static int smd_debugfs_init(void) +static int __init smd_debugfs_init(void) { struct dentry *dent; dent = debugfs_create_dir("smd", 0); if (IS_ERR(dent)) - return 1; + return PTR_ERR(dent); debug_create("ch", 0444, dent, debug_read_ch); - debug_create("stat", 0444, dent, debug_read_stat); + debug_create("diag", 0444, dent, debug_read_diag_msg); debug_create("mem", 0444, dent, debug_read_mem); - debug_create("version", 0444, dent, debug_read_version); + debug_create("version", 0444, dent, debug_read_smd_version); debug_create("tbl", 0444, dent, debug_read_alloc_tbl); + debug_create("modem_err", 0444, dent, debug_modem_err); + debug_create("modem_err_f3", 0444, dent, debug_modem_err_f3); + debug_create("print_diag", 0444, dent, debug_diag); + debug_create("print_f3", 0444, dent, debug_f3); + debug_create("int_stats", 0444, dent, debug_int_stats); + debug_create("int_stats_reset", 0444, dent, debug_int_stats_reset); + + /* NNV: this is google only stuff */ debug_create("build", 0444, dent, debug_read_build_id); return 0; } +static int __init smsm_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smsm", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debug_create("state", 0444, dent, debug_read_smsm_state); + debug_create("intr_mask", 0444, dent, debug_read_intr_mask); + debug_create("intr_mux", 0444, dent, debug_read_intr_mux); + debug_create("version", 0444, dent, debug_read_smem_version); + debug_create("smsm_test", 0444, dent, debug_test_smsm); + + init_completion(&smsm_cb_completion); + + return 0; +} + late_initcall(smd_debugfs_init); +late_initcall(smsm_debugfs_init); #endif @@ -259,38 +831,29 @@ struct tramp_gpio_smem { uint32_t polarity[NUM_GPIO_INT_REGISTERS]; }; - -void smsm_print_sleep_info(void) +/* + * Print debug information on shared memory sleep variables + */ +void smsm_print_sleep_info(uint32_t sleep_delay, uint32_t sleep_limit, + uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs) { unsigned long flags; uint32_t *ptr; -#ifndef CONFIG_ARCH_MSM_SCORPION struct tramp_gpio_smem *gpio; - struct smsm_interrupt_info *int_info; -#endif - spin_lock_irqsave(&smem_lock, flags); - ptr = smem_alloc(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr)); - if (ptr) - pr_info("SMEM_SMSM_SLEEP_DELAY: %x\n", *ptr); - - ptr = smem_alloc(SMEM_SMSM_LIMIT_SLEEP, sizeof(*ptr)); - if (ptr) - pr_info("SMEM_SMSM_LIMIT_SLEEP: %x\n", *ptr); + pr_info("SMEM_SMSM_SLEEP_DELAY: %x\n", sleep_delay); + pr_info("SMEM_SMSM_LIMIT_SLEEP: %x\n", sleep_limit); ptr = smem_alloc(SMEM_SLEEP_POWER_COLLAPSE_DISABLED, sizeof(*ptr)); if (ptr) pr_info("SMEM_SLEEP_POWER_COLLAPSE_DISABLED: %x\n", *ptr); + else + pr_info("SMEM_SLEEP_POWER_COLLAPSE_DISABLED: missing\n"); -#ifndef CONFIG_ARCH_MSM_SCORPION - int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); - if (int_info) - pr_info("SMEM_SMSM_INT_INFO %x %x %x\n", - int_info->interrupt_mask, - int_info->pending_interrupts, - int_info->wakeup_reason); + pr_info("SMEM_SMSM_INT_INFO %x %x %x\n", + irq_mask, pending_irqs, wakeup_reason); gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*gpio)); if (gpio) { @@ -304,9 +867,8 @@ void smsm_print_sleep_info(void) pr_info("SMEM_GPIO_INT: %d: f %d: %d %d...\n", i, gpio->num_fired[i], gpio->fired[i][0], gpio->fired[i][1]); - } -#else -#endif + } else + pr_info("SMEM_GPIO_INT: missing\n"); + spin_unlock_irqrestore(&smem_lock, flags); } - diff --git a/arch/arm/mach-msm/smd_nmea.c b/arch/arm/mach-msm/smd_nmea.c new file mode 100644 index 00000000000..1aedbf5e1c1 --- /dev/null +++ b/arch/arm/mach-msm/smd_nmea.c @@ -0,0 +1,205 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * SMD NMEA Driver -- Provides GPS NMEA device to SMD port interface. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_BUF_SIZE 200 + +static DEFINE_MUTEX(nmea_ch_lock); +static DEFINE_MUTEX(nmea_rx_buf_lock); + +static DECLARE_WAIT_QUEUE_HEAD(nmea_wait_queue); + +struct nmea_device_t { + struct miscdevice misc; + + struct smd_channel *ch; + + unsigned char rx_buf[MAX_BUF_SIZE]; + unsigned int bytes_read; +}; + +struct nmea_device_t *nmea_devp; + +static void nmea_work_func(struct work_struct *ws) +{ + int sz; + + for (;;) { + sz = smd_cur_packet_size(nmea_devp->ch); + if (sz == 0) + break; + if (sz > smd_read_avail(nmea_devp->ch)) + break; + if (sz > MAX_BUF_SIZE) { + smd_read(nmea_devp->ch, 0, sz); + continue; + } + + mutex_lock(&nmea_rx_buf_lock); + if (smd_read(nmea_devp->ch, nmea_devp->rx_buf, sz) != sz) { + mutex_unlock(&nmea_rx_buf_lock); + printk(KERN_ERR "nmea: not enough data?!\n"); + continue; + } + nmea_devp->bytes_read = sz; + mutex_unlock(&nmea_rx_buf_lock); + wake_up_interruptible(&nmea_wait_queue); + } +} + +struct workqueue_struct *nmea_wq; +static DECLARE_WORK(nmea_work, nmea_work_func); + +static void nmea_notify(void *priv, unsigned event) +{ + switch (event) { + case SMD_EVENT_DATA: { + int sz; + sz = smd_cur_packet_size(nmea_devp->ch); + if ((sz > 0) && (sz <= smd_read_avail(nmea_devp->ch))) + queue_work(nmea_wq, &nmea_work); + break; + } + case SMD_EVENT_OPEN: + printk(KERN_INFO "nmea: smd opened\n"); + break; + case SMD_EVENT_CLOSE: + printk(KERN_INFO "nmea: smd closed\n"); + break; + } +} + +static ssize_t nmea_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + int r; + int bytes_read; + + r = wait_event_interruptible(nmea_wait_queue, + nmea_devp->bytes_read); + if (r < 0) { + /* qualify error message */ + if (r != -ERESTARTSYS) { + /* we get this anytime a signal comes in */ + printk(KERN_ERR "ERROR:%s:%i:%s: " + "wait_event_interruptible ret %i\n", + __FILE__, + __LINE__, + __func__, + r + ); + } + return r; + } + + mutex_lock(&nmea_rx_buf_lock); + bytes_read = nmea_devp->bytes_read; + nmea_devp->bytes_read = 0; + r = copy_to_user(buf, nmea_devp->rx_buf, bytes_read); + mutex_unlock(&nmea_rx_buf_lock); + + if (r > 0) { + printk(KERN_ERR "ERROR:%s:%i:%s: " + "copy_to_user could not copy %i bytes.\n", + __FILE__, + __LINE__, + __func__, + r); + return r; + } + + return bytes_read; +} + +static int nmea_open(struct inode *ip, struct file *fp) +{ + int r = 0; + + mutex_lock(&nmea_ch_lock); + if (nmea_devp->ch == 0) + r = smd_open("GPSNMEA", &nmea_devp->ch, nmea_devp, nmea_notify); + mutex_unlock(&nmea_ch_lock); + + return r; +} + +static int nmea_release(struct inode *ip, struct file *fp) +{ + int r = 0; + + mutex_lock(&nmea_ch_lock); + if (nmea_devp->ch != 0) { + r = smd_close(nmea_devp->ch); + nmea_devp->ch = 0; + } + mutex_unlock(&nmea_ch_lock); + + return r; +} + +static const struct file_operations nmea_fops = { + .owner = THIS_MODULE, + .read = nmea_read, + .open = nmea_open, + .release = nmea_release, +}; + +static struct nmea_device_t nmea_device = { + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "nmea", + .fops = &nmea_fops, + } +}; + +static void __exit nmea_exit(void) +{ + destroy_workqueue(nmea_wq); + misc_deregister(&nmea_device.misc); +} + +static int __init nmea_init(void) +{ + int ret; + + nmea_device.bytes_read = 0; + nmea_devp = &nmea_device; + + nmea_wq = create_singlethread_workqueue("nmea"); + if (nmea_wq == 0) + return -ENOMEM; + + ret = misc_register(&nmea_device.misc); + return ret; +} + +module_init(nmea_init); +module_exit(nmea_exit); + +MODULE_DESCRIPTION("MSM Shared Memory NMEA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/smd_pkt.c b/arch/arm/mach-msm/smd_pkt.c new file mode 100644 index 00000000000..8d567f814d8 --- /dev/null +++ b/arch/arm/mach-msm/smd_pkt.c @@ -0,0 +1,1057 @@ +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * SMD Packet Driver -- Provides a binary SMD non-muxed packet port + * interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smd_private.h" +#ifdef CONFIG_ARCH_FSM9XXX +#define NUM_SMD_PKT_PORTS 4 +#else +#define NUM_SMD_PKT_PORTS 15 +#endif + +#define LOOPBACK_INX (NUM_SMD_PKT_PORTS - 1) + +#define DEVICE_NAME "smdpkt" +#define WAKELOCK_TIMEOUT (2*HZ) + +struct smd_pkt_dev { + struct cdev cdev; + struct device *devicep; + void *pil; + struct platform_driver driver; + + struct smd_channel *ch; + struct mutex ch_lock; + struct mutex rx_lock; + struct mutex tx_lock; + wait_queue_head_t ch_read_wait_queue; + wait_queue_head_t ch_write_wait_queue; + wait_queue_head_t ch_opened_wait_queue; + + int i; + + int blocking_write; + int is_open; + int poll_mode; + unsigned ch_size; + uint open_modem_wait; + + int has_reset; + int do_reset_notification; + struct completion ch_allocated; + struct wake_lock pa_wake_lock; /* Packet Arrival Wake lock*/ + struct work_struct packet_arrival_work; + struct spinlock pa_spinlock; + int wakelock_locked; +} *smd_pkt_devp[NUM_SMD_PKT_PORTS]; + +struct class *smd_pkt_classp; +static dev_t smd_pkt_number; +static struct delayed_work loopback_work; +static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp); +static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp); +static uint32_t is_modem_smsm_inited(void); + +static int msm_smd_pkt_debug_mask; +module_param_named(debug_mask, msm_smd_pkt_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +enum { + SMD_PKT_STATUS = 1U << 0, + SMD_PKT_READ = 1U << 1, + SMD_PKT_WRITE = 1U << 2, + SMD_PKT_READ_DUMP_BUFFER = 1U << 3, + SMD_PKT_WRITE_DUMP_BUFFER = 1U << 4, + SMD_PKT_POLL = 1U << 5, +}; + +#define DEBUG + +#ifdef DEBUG +#define D_STATUS(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \ + pr_info("Status: "x); \ +} while (0) + +#define D_READ(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \ + pr_info("Read: "x); \ +} while (0) + +#define D_WRITE(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \ + pr_info("Write: "x); \ +} while (0) + +#define D_READ_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_READ_DUMP_BUFFER) \ + print_hex_dump(KERN_INFO, prestr, \ + DUMP_PREFIX_NONE, 16, 1, \ + buf, cnt, 1); \ +} while (0) + +#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE_DUMP_BUFFER) \ + print_hex_dump(KERN_INFO, prestr, \ + DUMP_PREFIX_NONE, 16, 1, \ + buf, cnt, 1); \ +} while (0) + +#define D_POLL(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \ + pr_info("Poll: "x); \ +} while (0) +#else +#define D_STATUS(x...) do {} while (0) +#define D_READ(x...) do {} while (0) +#define D_WRITE(x...) do {} while (0) +#define D_READ_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#define D_POLL(x...) do {} while (0) +#endif + +static ssize_t open_timeout_store(struct device *d, + struct device_attribute *attr, + const char *buf, + size_t n) +{ + int i; + unsigned long tmp; + for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { + if (smd_pkt_devp[i]->devicep == d) + break; + } + if (i >= NUM_SMD_PKT_PORTS) { + pr_err("%s: unable to match device to valid smd_pkt port\n", + __func__); + return -EINVAL; + } + if (!strict_strtoul(buf, 10, &tmp)) { + smd_pkt_devp[i]->open_modem_wait = tmp; + return n; + } else { + pr_err("%s: unable to convert: %s to an int\n", __func__, + buf); + return -EINVAL; + } +} + +static ssize_t open_timeout_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + int i; + for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { + if (smd_pkt_devp[i]->devicep == d) + break; + } + if (i >= NUM_SMD_PKT_PORTS) { + pr_err("%s: unable to match device to valid smd_pkt port\n", + __func__); + return -EINVAL; + } + return snprintf(buf, PAGE_SIZE, "%d\n", + smd_pkt_devp[i]->open_modem_wait); +} + +static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store); + +static int notify_reset(struct smd_pkt_dev *smd_pkt_devp) +{ + smd_pkt_devp->do_reset_notification = 0; + + return -ENETRESET; +} + +static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp) +{ + smd_pkt_devp->do_reset_notification = 1; + smd_pkt_devp->has_reset = 1; + + smd_pkt_devp->is_open = 0; + + wake_up(&smd_pkt_devp->ch_read_wait_queue); + wake_up(&smd_pkt_devp->ch_write_wait_queue); + wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue); + D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i); +} + +static void loopback_probe_worker(struct work_struct *work) +{ + + /* Wait for the modem SMSM to be inited for the SMD + ** Loopback channel to be allocated at the modem. Since + ** the wait need to be done atmost once, using msleep + ** doesn't degrade the performance. */ + if (!is_modem_smsm_inited()) + schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000)); + else + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); + +} + +static void packet_arrival_worker(struct work_struct *work) +{ + struct smd_pkt_dev *smd_pkt_devp; + unsigned long flags; + + smd_pkt_devp = container_of(work, struct smd_pkt_dev, + packet_arrival_work); + mutex_lock(&smd_pkt_devp->ch_lock); + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + if (smd_pkt_devp->ch && smd_pkt_devp->wakelock_locked) { + D_READ("%s locking smd_pkt_dev id:%d wakelock\n", + __func__, smd_pkt_devp->i); + wake_lock_timeout(&smd_pkt_devp->pa_wake_lock, + WAKELOCK_TIMEOUT); + } + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + mutex_unlock(&smd_pkt_devp->ch_lock); +} + +static long smd_pkt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct smd_pkt_dev *smd_pkt_devp; + + smd_pkt_devp = file->private_data; + if (!smd_pkt_devp) + return -EINVAL; + + switch (cmd) { + case TIOCMGET: + D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + ret = smd_tiocmget(smd_pkt_devp->ch); + break; + case TIOCMSET: + D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg); + break; + case SMD_PKT_IOCTL_BLOCKING_WRITE: + ret = get_user(smd_pkt_devp->blocking_write, (int *)arg); + break; + default: + pr_err("%s: Unrecognized ioctl command %d\n", __func__, cmd); + ret = -1; + } + + return ret; +} + +ssize_t smd_pkt_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int r; + int bytes_read; + int pkt_size; + struct smd_pkt_dev *smd_pkt_devp; + unsigned long flags; + + smd_pkt_devp = file->private_data; + + if (!smd_pkt_devp) { + pr_err("%s on NULL smd_pkt_dev\n", __func__); + return -EINVAL; + } + + if (!smd_pkt_devp->ch) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return -EINVAL; + } + + if (smd_pkt_devp->do_reset_notification) { + /* notify client that a reset occurred */ + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return notify_reset(smd_pkt_devp); + } + D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n", + __func__, smd_pkt_devp->i, count); + +wait_for_packet: + r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue, + !smd_pkt_devp->ch || + (smd_cur_packet_size(smd_pkt_devp->ch) > 0 + && smd_read_avail(smd_pkt_devp->ch)) || + smd_pkt_devp->has_reset); + + mutex_lock(&smd_pkt_devp->rx_lock); + if (smd_pkt_devp->has_reset) { + mutex_unlock(&smd_pkt_devp->rx_lock); + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return notify_reset(smd_pkt_devp); + } + + if (!smd_pkt_devp->ch) { + mutex_unlock(&smd_pkt_devp->rx_lock); + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return -EINVAL; + } + + if (r < 0) { + mutex_unlock(&smd_pkt_devp->rx_lock); + /* qualify error message */ + if (r != -ERESTARTSYS) { + /* we get this anytime a signal comes in */ + pr_err("%s: wait_event_interruptible on smd_pkt_dev" + " id:%d ret %i\n", + __func__, smd_pkt_devp->i, r); + } + return r; + } + + /* Here we have a whole packet waiting for us */ + pkt_size = smd_cur_packet_size(smd_pkt_devp->ch); + + if (!pkt_size) { + pr_err("%s: No data on smd_pkt_dev id:%d, False wakeup\n", + __func__, smd_pkt_devp->i); + mutex_unlock(&smd_pkt_devp->rx_lock); + goto wait_for_packet; + } + + if (pkt_size > count) { + pr_err("%s: failure on smd_pkt_dev id: %d - packet size %d" + " > buffer size %d,", __func__, smd_pkt_devp->i, + pkt_size, count); + mutex_unlock(&smd_pkt_devp->rx_lock); + return -ETOOSMALL; + } + + bytes_read = 0; + do { + r = smd_read_user_buffer(smd_pkt_devp->ch, + (buf + bytes_read), + (pkt_size - bytes_read)); + if (r < 0) { + mutex_unlock(&smd_pkt_devp->rx_lock); + if (smd_pkt_devp->has_reset) { + pr_err("%s notifying reset for smd_pkt_dev" + " id:%d\n", __func__, smd_pkt_devp->i); + return notify_reset(smd_pkt_devp); + } + pr_err("%s Error while reading %d\n", __func__, r); + return r; + } + bytes_read += r; + if (pkt_size != bytes_read) + wait_event(smd_pkt_devp->ch_read_wait_queue, + smd_read_avail(smd_pkt_devp->ch) || + smd_pkt_devp->has_reset); + if (smd_pkt_devp->has_reset) { + mutex_unlock(&smd_pkt_devp->rx_lock); + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return notify_reset(smd_pkt_devp); + } + } while (pkt_size != bytes_read); + D_READ_DUMP_BUFFER("Read: ", (bytes_read > 16 ? 16 : bytes_read), buf); + mutex_unlock(&smd_pkt_devp->rx_lock); + + mutex_lock(&smd_pkt_devp->ch_lock); + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + if (smd_pkt_devp->poll_mode && + !smd_cur_packet_size(smd_pkt_devp->ch)) { + wake_unlock(&smd_pkt_devp->pa_wake_lock); + smd_pkt_devp->wakelock_locked = 0; + smd_pkt_devp->poll_mode = 0; + D_READ("%s unlocked smd_pkt_dev id:%d wakelock\n", + __func__, smd_pkt_devp->i); + } + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + mutex_unlock(&smd_pkt_devp->ch_lock); + + D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n", + __func__, smd_pkt_devp->i, bytes_read); + + /* check and wakeup read threads waiting on this device */ + check_and_wakeup_reader(smd_pkt_devp); + + return bytes_read; +} + +ssize_t smd_pkt_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int r = 0, bytes_written; + struct smd_pkt_dev *smd_pkt_devp; + DEFINE_WAIT(write_wait); + + smd_pkt_devp = file->private_data; + + if (!smd_pkt_devp) { + pr_err("%s on NULL smd_pkt_dev\n", __func__); + return -EINVAL; + } + + if (!smd_pkt_devp->ch) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return -EINVAL; + } + + if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) { + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + /* notify client that a reset occurred */ + return notify_reset(smd_pkt_devp); + } + D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %d\n", + __func__, smd_pkt_devp->i, count); + + mutex_lock(&smd_pkt_devp->tx_lock); + if (!smd_pkt_devp->blocking_write) { + if (smd_write_avail(smd_pkt_devp->ch) < count) { + pr_err("%s: Not enough space in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + mutex_unlock(&smd_pkt_devp->tx_lock); + return -ENOMEM; + } + } + + r = smd_write_start(smd_pkt_devp->ch, count); + if (r < 0) { + mutex_unlock(&smd_pkt_devp->tx_lock); + pr_err("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n", + __func__, r, smd_pkt_devp->i); + return r; + } + + bytes_written = 0; + do { + prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue, + &write_wait, TASK_UNINTERRUPTIBLE); + if (!smd_write_avail(smd_pkt_devp->ch) && + !smd_pkt_devp->has_reset) { + smd_enable_read_intr(smd_pkt_devp->ch); + schedule(); + } + finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait); + smd_disable_read_intr(smd_pkt_devp->ch); + + if (smd_pkt_devp->has_reset) { + mutex_unlock(&smd_pkt_devp->tx_lock); + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return notify_reset(smd_pkt_devp); + } else { + r = smd_write_segment(smd_pkt_devp->ch, + (void *)(buf + bytes_written), + (count - bytes_written), 1); + if (r < 0) { + mutex_unlock(&smd_pkt_devp->tx_lock); + if (smd_pkt_devp->has_reset) { + pr_err("%s notifying reset for" + " smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return notify_reset(smd_pkt_devp); + } + pr_err("%s on smd_pkt_dev id:%d failed r:%d\n", + __func__, smd_pkt_devp->i, r); + return r; + } + bytes_written += r; + } + } while (bytes_written != count); + smd_write_end(smd_pkt_devp->ch); + mutex_unlock(&smd_pkt_devp->tx_lock); + D_WRITE_DUMP_BUFFER("Write: ", + (bytes_written > 16 ? 16 : bytes_written), buf); + D_WRITE("Finished %s on smd_pkt_dev id:%d %d bytes\n", + __func__, smd_pkt_devp->i, count); + + return count; +} + +static unsigned int smd_pkt_poll(struct file *file, poll_table *wait) +{ + struct smd_pkt_dev *smd_pkt_devp; + unsigned int mask = 0; + + smd_pkt_devp = file->private_data; + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return POLLERR; + } + + smd_pkt_devp->poll_mode = 1; + poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait); + mutex_lock(&smd_pkt_devp->ch_lock); + if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) { + mutex_unlock(&smd_pkt_devp->ch_lock); + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return POLLERR; + } + + if (smd_read_avail(smd_pkt_devp->ch)) { + mask |= POLLIN | POLLRDNORM; + D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n", + __func__, smd_pkt_devp->i); + } + mutex_unlock(&smd_pkt_devp->ch_lock); + + return mask; +} + +static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp) +{ + int sz; + unsigned long flags; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return; + } + + if (!smd_pkt_devp->ch) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + + sz = smd_cur_packet_size(smd_pkt_devp->ch); + if (sz == 0) { + D_READ("%s: No packet in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + if (!smd_read_avail(smd_pkt_devp->ch)) { + D_READ("%s: packet size is %d in smd_pkt_dev id:%d -" + " but the data isn't here\n", + __func__, sz, smd_pkt_devp->i); + return; + } + + /* here we have a packet of size sz ready */ + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + wake_lock(&smd_pkt_devp->pa_wake_lock); + smd_pkt_devp->wakelock_locked = 1; + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + wake_up(&smd_pkt_devp->ch_read_wait_queue); + schedule_work(&smd_pkt_devp->packet_arrival_work); + D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i); +} + +static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp) +{ + int sz; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return; + } + + if (!smd_pkt_devp->ch) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + + sz = smd_write_avail(smd_pkt_devp->ch); + if (sz) { + D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n", + __func__, sz, smd_pkt_devp->i); + smd_disable_read_intr(smd_pkt_devp->ch); + wake_up(&smd_pkt_devp->ch_write_wait_queue); + } +} + +static void ch_notify(void *priv, unsigned event) +{ + struct smd_pkt_dev *smd_pkt_devp = priv; + + if (smd_pkt_devp->ch == 0) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + + switch (event) { + case SMD_EVENT_DATA: { + D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + check_and_wakeup_reader(smd_pkt_devp); + if (smd_pkt_devp->blocking_write) + check_and_wakeup_writer(smd_pkt_devp); + break; + } + case SMD_EVENT_OPEN: + D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + smd_pkt_devp->has_reset = 0; + smd_pkt_devp->is_open = 1; + wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue); + break; + case SMD_EVENT_CLOSE: + D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + smd_pkt_devp->is_open = 0; + /* put port into reset state */ + clean_and_signal(smd_pkt_devp); + if (smd_pkt_devp->i == LOOPBACK_INX) + schedule_delayed_work(&loopback_work, + msecs_to_jiffies(1000)); + break; + } +} + +#ifdef CONFIG_ARCH_FSM9XXX +static char *smd_pkt_dev_name[] = { + "smdcntl1", + "smdcntl2", + "smd22", + "smd_pkt_loopback", +}; + +static char *smd_ch_name[] = { + "DATA6_CNTL", + "DATA7_CNTL", + "DATA22", + "LOOPBACK", +}; + +static uint32_t smd_ch_edge[] = { + SMD_APPS_QDSP, + SMD_APPS_QDSP, + SMD_APPS_QDSP, + SMD_APPS_QDSP +}; +#else +static char *smd_pkt_dev_name[] = { + "smdcntl0", + "smdcntl1", + "smdcntl2", + "smdcntl3", + "smdcntl4", + "smdcntl5", + "smdcntl6", + "smdcntl7", + "smd22", + "smd_sns_dsps", + "apr_apps2", + "smdcntl8", + "smd_sns_adsp", + "smd_cxm_qmi", + "smd_pkt_loopback", +}; + +static char *smd_ch_name[] = { + "DATA5_CNTL", + "DATA6_CNTL", + "DATA7_CNTL", + "DATA8_CNTL", + "DATA9_CNTL", + "DATA12_CNTL", + "DATA13_CNTL", + "DATA14_CNTL", + "DATA22", + "SENSOR", + "apr_apps2", + "DATA40_CNTL", + "SENSOR", + "CXM_QMI_PORT_8064", + "LOOPBACK", +}; + +static uint32_t smd_ch_edge[] = { + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_MODEM, + SMD_APPS_DSPS, + SMD_APPS_QDSP, + SMD_APPS_MODEM, + SMD_APPS_QDSP, + SMD_APPS_WCNSS, + SMD_APPS_MODEM, +}; +#endif + +static int smd_pkt_dummy_probe(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < NUM_SMD_PKT_PORTS; i++) { + if (!strncmp(pdev->name, smd_ch_name[i], SMD_MAX_CH_NAME_LEN)) { + complete_all(&smd_pkt_devp[i]->ch_allocated); + D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n", + __func__, i); + break; + } + } + return 0; +} + +static uint32_t is_modem_smsm_inited(void) +{ + uint32_t modem_state; + uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT); + + modem_state = smsm_get_state(SMSM_MODEM_STATE); + return (modem_state & ready_state) == ready_state; +} + +int smd_pkt_open(struct inode *inode, struct file *file) +{ + int r = 0; + struct smd_pkt_dev *smd_pkt_devp; + const char *peripheral = NULL; + + smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev); + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return -EINVAL; + } + D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i); + + wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND, + smd_pkt_dev_name[smd_pkt_devp->i]); + INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker); + + file->private_data = smd_pkt_devp; + + mutex_lock(&smd_pkt_devp->ch_lock); + if (smd_pkt_devp->ch == 0) { + init_completion(&smd_pkt_devp->ch_allocated); + smd_pkt_devp->driver.probe = smd_pkt_dummy_probe; + smd_pkt_devp->driver.driver.name = + smd_ch_name[smd_pkt_devp->i]; + smd_pkt_devp->driver.driver.owner = THIS_MODULE; + r = platform_driver_register(&smd_pkt_devp->driver); + if (r) { + pr_err("%s: %s Platform driver reg. failed\n", + __func__, smd_ch_name[smd_pkt_devp->i]); + goto out; + } + + peripheral = smd_edge_to_subsystem( + smd_ch_edge[smd_pkt_devp->i]); + if (peripheral) { + smd_pkt_devp->pil = pil_get(peripheral); + if (IS_ERR(smd_pkt_devp->pil)) { + r = PTR_ERR(smd_pkt_devp->pil); + pr_err("%s failed on smd_pkt_dev id:%d -" + " pil_get failed for %s\n", __func__, + smd_pkt_devp->i, peripheral); + goto release_pd; + } + + /* Wait for the modem SMSM to be inited for the SMD + ** Loopback channel to be allocated at the modem. Since + ** the wait need to be done atmost once, using msleep + ** doesn't degrade the performance. */ + if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK", + SMD_MAX_CH_NAME_LEN)) { + if (!is_modem_smsm_inited()) + msleep(5000); + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); + msleep(100); + } + + /* + * Wait for a packet channel to be allocated so we know + * the modem is ready enough. + */ + if (smd_pkt_devp->open_modem_wait) { + r = wait_for_completion_interruptible_timeout( + &smd_pkt_devp->ch_allocated, + msecs_to_jiffies( + smd_pkt_devp->open_modem_wait + * 1000)); + if (r == 0) + r = -ETIMEDOUT; + if (r < 0) { + pr_err("%s: wait on smd_pkt_dev id:%d" + " allocation failed rc:%d\n", + __func__, smd_pkt_devp->i, r); + goto release_pil; + } + } + } + + r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i], + smd_ch_edge[smd_pkt_devp->i], + &smd_pkt_devp->ch, + smd_pkt_devp, + ch_notify); + if (r < 0) { + pr_err("%s: %s open failed %d\n", __func__, + smd_ch_name[smd_pkt_devp->i], r); + goto release_pil; + } + + r = wait_event_interruptible_timeout( + smd_pkt_devp->ch_opened_wait_queue, + smd_pkt_devp->is_open, (2 * HZ)); + if (r == 0) { + r = -ETIMEDOUT; + /* close the ch to sync smd's state with smd_pkt */ + smd_close(smd_pkt_devp->ch); + smd_pkt_devp->ch = NULL; + } + + if (r < 0) { + pr_err("%s: wait on smd_pkt_dev id:%d OPEN event failed" + " rc:%d\n", __func__, smd_pkt_devp->i, r); + } else if (!smd_pkt_devp->is_open) { + pr_err("%s: Invalid OPEN event on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + r = -ENODEV; + } else { + smd_disable_read_intr(smd_pkt_devp->ch); + smd_pkt_devp->ch_size = + smd_write_avail(smd_pkt_devp->ch); + r = 0; + D_STATUS("Finished %s on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + } + } +release_pil: + if (peripheral && (r < 0)) + pil_put(smd_pkt_devp->pil); + +release_pd: + if (r < 0) + platform_driver_unregister(&smd_pkt_devp->driver); +out: + mutex_unlock(&smd_pkt_devp->ch_lock); + + if (r < 0) + wake_lock_destroy(&smd_pkt_devp->pa_wake_lock); + + return r; +} + +int smd_pkt_release(struct inode *inode, struct file *file) +{ + int r = 0; + struct smd_pkt_dev *smd_pkt_devp = file->private_data; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return -EINVAL; + } + D_STATUS("Begin %s on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + + clean_and_signal(smd_pkt_devp); + + mutex_lock(&smd_pkt_devp->ch_lock); + mutex_lock(&smd_pkt_devp->rx_lock); + mutex_lock(&smd_pkt_devp->tx_lock); + if (smd_pkt_devp->ch != 0) { + r = smd_close(smd_pkt_devp->ch); + smd_pkt_devp->ch = 0; + smd_pkt_devp->blocking_write = 0; + smd_pkt_devp->poll_mode = 0; + platform_driver_unregister(&smd_pkt_devp->driver); + if (smd_pkt_devp->pil) + pil_put(smd_pkt_devp->pil); + } + mutex_unlock(&smd_pkt_devp->tx_lock); + mutex_unlock(&smd_pkt_devp->rx_lock); + mutex_unlock(&smd_pkt_devp->ch_lock); + + smd_pkt_devp->has_reset = 0; + smd_pkt_devp->do_reset_notification = 0; + smd_pkt_devp->wakelock_locked = 0; + wake_lock_destroy(&smd_pkt_devp->pa_wake_lock); + D_STATUS("Finished %s on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + + return r; +} + +static const struct file_operations smd_pkt_fops = { + .owner = THIS_MODULE, + .open = smd_pkt_open, + .release = smd_pkt_release, + .read = smd_pkt_read, + .write = smd_pkt_write, + .poll = smd_pkt_poll, + .unlocked_ioctl = smd_pkt_ioctl, +}; + +static int __init smd_pkt_init(void) +{ + int i; + int r; + + r = alloc_chrdev_region(&smd_pkt_number, + 0, + NUM_SMD_PKT_PORTS, + DEVICE_NAME); + if (IS_ERR_VALUE(r)) { + pr_err("%s: alloc_chrdev_region() failed ret:%i\n", + __func__, r); + goto error0; + } + + smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(smd_pkt_classp)) { + pr_err("%s: class_create() failed ENOMEM\n", __func__); + r = -ENOMEM; + goto error1; + } + + for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { + smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev), + GFP_KERNEL); + if (IS_ERR(smd_pkt_devp[i])) { + pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n", + __func__, i); + r = -ENOMEM; + goto error2; + } + + smd_pkt_devp[i]->i = i; + + init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue); + init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue); + smd_pkt_devp[i]->is_open = 0; + smd_pkt_devp[i]->poll_mode = 0; + smd_pkt_devp[i]->wakelock_locked = 0; + init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue); + + spin_lock_init(&smd_pkt_devp[i]->pa_spinlock); + mutex_init(&smd_pkt_devp[i]->ch_lock); + mutex_init(&smd_pkt_devp[i]->rx_lock); + mutex_init(&smd_pkt_devp[i]->tx_lock); + + cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops); + smd_pkt_devp[i]->cdev.owner = THIS_MODULE; + + r = cdev_add(&smd_pkt_devp[i]->cdev, + (smd_pkt_number + i), + 1); + + if (IS_ERR_VALUE(r)) { + pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d" + " ret:%i\n", __func__, i, r); + kfree(smd_pkt_devp[i]); + goto error2; + } + + smd_pkt_devp[i]->devicep = + device_create(smd_pkt_classp, + NULL, + (smd_pkt_number + i), + NULL, + smd_pkt_dev_name[i]); + + if (IS_ERR(smd_pkt_devp[i]->devicep)) { + pr_err("%s: device_create() failed for smd_pkt_dev" + " id:%d\n", __func__, i); + r = -ENOMEM; + cdev_del(&smd_pkt_devp[i]->cdev); + kfree(smd_pkt_devp[i]); + goto error2; + } + if (device_create_file(smd_pkt_devp[i]->devicep, + &dev_attr_open_timeout)) + pr_err("%s: unable to create device attr for" + " smd_pkt_dev id:%d\n", __func__, i); + } + + INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker); + + D_STATUS("SMD Packet Port Driver Initialized.\n"); + return 0; + + error2: + if (i > 0) { + while (--i >= 0) { + cdev_del(&smd_pkt_devp[i]->cdev); + kfree(smd_pkt_devp[i]); + device_destroy(smd_pkt_classp, + MKDEV(MAJOR(smd_pkt_number), i)); + } + } + + class_destroy(smd_pkt_classp); + error1: + unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS); + error0: + return r; +} + +static void __exit smd_pkt_cleanup(void) +{ + int i; + + for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { + cdev_del(&smd_pkt_devp[i]->cdev); + kfree(smd_pkt_devp[i]); + device_destroy(smd_pkt_classp, + MKDEV(MAJOR(smd_pkt_number), i)); + } + + class_destroy(smd_pkt_classp); + + unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS); +} + +module_init(smd_pkt_init); +module_exit(smd_pkt_cleanup); + +MODULE_DESCRIPTION("MSM Shared Memory Packet Port"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/smd_private.c b/arch/arm/mach-msm/smd_private.c new file mode 100644 index 00000000000..5a78b6f04cd --- /dev/null +++ b/arch/arm/mach-msm/smd_private.c @@ -0,0 +1,303 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "smd_private.h" + +void set_state(volatile void *half_channel, unsigned data) +{ + ((struct smd_half_channel *)(half_channel))->state = data; +} + +unsigned get_state(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->state; +} + +void set_fDSR(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fDSR = data; +} + +unsigned get_fDSR(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fDSR; +} + +void set_fCTS(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fCTS = data; +} + +unsigned get_fCTS(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fCTS; +} + +void set_fCD(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fCD = data; +} + +unsigned get_fCD(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fCD; +} + +void set_fRI(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fRI = data; +} + +unsigned get_fRI(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fRI; +} + +void set_fHEAD(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fHEAD = data; +} + +unsigned get_fHEAD(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fHEAD; +} + +void set_fTAIL(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fTAIL = data; +} + +unsigned get_fTAIL(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fTAIL; +} + +void set_fSTATE(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fSTATE = data; +} + +unsigned get_fSTATE(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fSTATE; +} + +void set_fBLOCKREADINTR(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel *)(half_channel))->fBLOCKREADINTR = data; +} + +unsigned get_fBLOCKREADINTR(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->fBLOCKREADINTR; +} + +void set_tail(volatile void *half_channel, unsigned data) +{ + ((struct smd_half_channel *)(half_channel))->tail = data; +} + +unsigned get_tail(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->tail; +} + +void set_head(volatile void *half_channel, unsigned data) +{ + ((struct smd_half_channel *)(half_channel))->head = data; +} + +unsigned get_head(volatile void *half_channel) +{ + return ((struct smd_half_channel *)(half_channel))->head; +} + +void set_state_word_access(volatile void *half_channel, unsigned data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->state = data; +} + +unsigned get_state_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->state; +} + +void set_fDSR_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fDSR = data; +} + +unsigned get_fDSR_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fDSR; +} + +void set_fCTS_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fCTS = data; +} + +unsigned get_fCTS_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fCTS; +} + +void set_fCD_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fCD = data; +} + +unsigned get_fCD_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fCD; +} + +void set_fRI_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fRI = data; +} + +unsigned get_fRI_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fRI; +} + +void set_fHEAD_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fHEAD = data; +} + +unsigned get_fHEAD_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fHEAD; +} + +void set_fTAIL_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fTAIL = data; +} + +unsigned get_fTAIL_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fTAIL; +} + +void set_fSTATE_word_access(volatile void *half_channel, unsigned char data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->fSTATE = data; +} + +unsigned get_fSTATE_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->fSTATE; +} + +void set_fBLOCKREADINTR_word_access(volatile void *half_channel, + unsigned char data) +{ + ((struct smd_half_channel_word_access *) + (half_channel))->fBLOCKREADINTR = data; +} + +unsigned get_fBLOCKREADINTR_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *) + (half_channel))->fBLOCKREADINTR; +} + +void set_tail_word_access(volatile void *half_channel, unsigned data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->tail = data; +} + +unsigned get_tail_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->tail; +} + +void set_head_word_access(volatile void *half_channel, unsigned data) +{ + ((struct smd_half_channel_word_access *)(half_channel))->head = data; +} + +unsigned get_head_word_access(volatile void *half_channel) +{ + return ((struct smd_half_channel_word_access *)(half_channel))->head; +} + +int is_word_access_ch(unsigned ch_type) +{ + if (ch_type == SMD_APPS_RPM || ch_type == SMD_MODEM_RPM || + ch_type == SMD_QDSP_RPM || ch_type == SMD_WCNSS_RPM) + return 1; + else + return 0; +} + +struct smd_half_channel_access *get_half_ch_funcs(unsigned ch_type) +{ + static struct smd_half_channel_access byte_access = { + .set_state = set_state, + .get_state = get_state, + .set_fDSR = set_fDSR, + .get_fDSR = get_fDSR, + .set_fCTS = set_fCTS, + .get_fCTS = get_fCTS, + .set_fCD = set_fCD, + .get_fCD = get_fCD, + .set_fRI = set_fRI, + .get_fRI = get_fRI, + .set_fHEAD = set_fHEAD, + .get_fHEAD = get_fHEAD, + .set_fTAIL = set_fTAIL, + .get_fTAIL = get_fTAIL, + .set_fSTATE = set_fSTATE, + .get_fSTATE = get_fSTATE, + .set_fBLOCKREADINTR = set_fBLOCKREADINTR, + .get_fBLOCKREADINTR = get_fBLOCKREADINTR, + .set_tail = set_tail, + .get_tail = get_tail, + .set_head = set_head, + .get_head = get_head, + }; + static struct smd_half_channel_access word_access = { + .set_state = set_state_word_access, + .get_state = get_state_word_access, + .set_fDSR = set_fDSR_word_access, + .get_fDSR = get_fDSR_word_access, + .set_fCTS = set_fCTS_word_access, + .get_fCTS = get_fCTS_word_access, + .set_fCD = set_fCD_word_access, + .get_fCD = get_fCD_word_access, + .set_fRI = set_fRI_word_access, + .get_fRI = get_fRI_word_access, + .set_fHEAD = set_fHEAD_word_access, + .get_fHEAD = get_fHEAD_word_access, + .set_fTAIL = set_fTAIL_word_access, + .get_fTAIL = get_fTAIL_word_access, + .set_fSTATE = set_fSTATE_word_access, + .get_fSTATE = get_fSTATE_word_access, + .set_fBLOCKREADINTR = set_fBLOCKREADINTR_word_access, + .get_fBLOCKREADINTR = get_fBLOCKREADINTR_word_access, + .set_tail = set_tail_word_access, + .get_tail = get_tail_word_access, + .set_head = set_head_word_access, + .get_head = get_head_word_access, + }; + + if (is_word_access_ch(ch_type)) + return &word_access; + else + return &byte_access; +} + diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h index 727bfe68aa9..9117280e680 100644 --- a/arch/arm/mach-msm/smd_private.h +++ b/arch/arm/mach-msm/smd_private.h @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/smd_private.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,12 +16,22 @@ #ifndef _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_ #define _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_ -#include +#include #include -#include -#include +#include +#include -#include +#define PC_APPS 0 +#define PC_MODEM 1 + +#define VERSION_QDSP6 4 +#define VERSION_APPS_SBL 6 +#define VERSION_MODEM_SBL 7 +#define VERSION_APPS 8 +#define VERSION_MODEM 9 +#define VERSION_DSPS 10 + +#define SMD_HEAP_SIZE 512 struct smem_heap_info { unsigned initialized; @@ -34,8 +44,9 @@ struct smem_heap_entry { unsigned allocated; unsigned offset; unsigned size; - unsigned reserved; + unsigned reserved; /* bits 1:0 reserved, bits 31:2 aux smem base addr */ }; +#define BASE_ADDR_MASK 0xfffffffc struct smem_proc_comm { unsigned command; @@ -44,47 +55,33 @@ struct smem_proc_comm { unsigned data2; }; -#define PC_APPS 0 -#define PC_MODEM 1 - -#define VERSION_SMD 0 -#define VERSION_QDSP6 4 -#define VERSION_APPS_SBL 6 -#define VERSION_MODEM_SBL 7 -#define VERSION_APPS 8 -#define VERSION_MODEM 9 - struct smem_shared { struct smem_proc_comm proc_comm[4]; unsigned version[32]; struct smem_heap_info heap_info; - struct smem_heap_entry heap_toc[512]; + struct smem_heap_entry heap_toc[SMD_HEAP_SIZE]; }; -#define SMSM_V1_SIZE (sizeof(unsigned) * 8) -#define SMSM_V2_SIZE (sizeof(unsigned) * 4) - -#ifdef CONFIG_MSM_SMD_PKG3 +#if defined(CONFIG_MSM_SMD_PKG4) struct smsm_interrupt_info { - uint32_t interrupt_mask; - uint32_t pending_interrupts; - uint32_t wakeup_reason; + uint32_t aArm_en_mask; + uint32_t aArm_interrupts_pending; + uint32_t aArm_wakeup_reason; + uint32_t aArm_rpc_prog; + uint32_t aArm_rpc_proc; + char aArm_smd_port_name[20]; + uint32_t aArm_gpio_info; }; +#elif defined(CONFIG_MSM_SMD_PKG3) +struct smsm_interrupt_info { + uint32_t aArm_en_mask; + uint32_t aArm_interrupts_pending; + uint32_t aArm_wakeup_reason; +}; +#elif !defined(CONFIG_MSM_SMD) +/* Don't trigger the error */ #else -#define DEM_MAX_PORT_NAME_LEN (20) -struct msm_dem_slave_data { - uint32_t sleep_time; - uint32_t interrupt_mask; - uint32_t resources_used; - uint32_t reserved1; - - uint32_t wakeup_reason; - uint32_t pending_interrupts; - uint32_t rpc_prog; - uint32_t rpc_proc; - char smd_port_name[DEM_MAX_PORT_NAME_LEN]; - uint32_t reserved2; -}; +#error No SMD Package Specified; aborting #endif #define SZ_DIAG_ERR_MSG 0xC8 @@ -93,167 +90,34 @@ struct msm_dem_slave_data { #define ID_SHARED_STATE SMEM_SMSM_SHARED_STATE #define ID_CH_ALLOC_TBL SMEM_CHANNEL_ALLOC_TBL -#define SMSM_INIT 0x00000001 -#define SMSM_SMDINIT 0x00000008 -#define SMSM_RPCINIT 0x00000020 -#define SMSM_RESET 0x00000040 -#define SMSM_RSA 0x00000080 -#define SMSM_RUN 0x00000100 -#define SMSM_PWRC 0x00000200 -#define SMSM_TIMEWAIT 0x00000400 -#define SMSM_TIMEINIT 0x00000800 -#define SMSM_PWRC_EARLY_EXIT 0x00001000 -#define SMSM_WFPI 0x00002000 -#define SMSM_SLEEP 0x00004000 -#define SMSM_SLEEPEXIT 0x00008000 -#define SMSM_APPS_REBOOT 0x00020000 -#define SMSM_SYSTEM_POWER_DOWN 0x00040000 -#define SMSM_SYSTEM_REBOOT 0x00080000 -#define SMSM_SYSTEM_DOWNLOAD 0x00100000 -#define SMSM_PWRC_SUSPEND 0x00200000 -#define SMSM_APPS_SHUTDOWN 0x00400000 -#define SMSM_SMD_LOOPBACK 0x00800000 -#define SMSM_RUN_QUIET 0x01000000 -#define SMSM_MODEM_WAIT 0x02000000 -#define SMSM_MODEM_BREAK 0x04000000 -#define SMSM_MODEM_CONTINUE 0x08000000 -#define SMSM_UNKNOWN 0x80000000 +#define SMD_SS_CLOSED 0x00000000 +#define SMD_SS_OPENING 0x00000001 +#define SMD_SS_OPENED 0x00000002 +#define SMD_SS_FLUSHING 0x00000003 +#define SMD_SS_CLOSING 0x00000004 +#define SMD_SS_RESET 0x00000005 +#define SMD_SS_RESET_OPENING 0x00000006 -#define SMSM_WKUP_REASON_RPC 0x00000001 -#define SMSM_WKUP_REASON_INT 0x00000002 -#define SMSM_WKUP_REASON_GPIO 0x00000004 -#define SMSM_WKUP_REASON_TIMER 0x00000008 -#define SMSM_WKUP_REASON_ALARM 0x00000010 -#define SMSM_WKUP_REASON_RESET 0x00000020 - -#ifdef CONFIG_ARCH_MSM7X00A -enum smsm_state_item { - SMSM_STATE_APPS = 1, - SMSM_STATE_MODEM = 3, - SMSM_STATE_COUNT, -}; -#else -enum smsm_state_item { - SMSM_STATE_APPS, - SMSM_STATE_MODEM, - SMSM_STATE_HEXAGON, - SMSM_STATE_APPS_DEM, - SMSM_STATE_MODEM_DEM, - SMSM_STATE_QDSP6_DEM, - SMSM_STATE_POWER_MASTER_DEM, - SMSM_STATE_TIME_MASTER_DEM, - SMSM_STATE_COUNT, -}; -#endif - -void *smem_alloc(unsigned id, unsigned size); -int smsm_change_state(enum smsm_state_item item, uint32_t clear_mask, uint32_t set_mask); -uint32_t smsm_get_state(enum smsm_state_item item); -int smsm_set_sleep_duration(uint32_t delay); -void smsm_print_sleep_info(void); - -#define SMEM_NUM_SMD_CHANNELS 64 - -typedef enum { - /* fixed items */ - SMEM_PROC_COMM = 0, - SMEM_HEAP_INFO, - SMEM_ALLOCATION_TABLE, - SMEM_VERSION_INFO, - SMEM_HW_RESET_DETECT, - SMEM_AARM_WARM_BOOT, - SMEM_DIAG_ERR_MESSAGE, - SMEM_SPINLOCK_ARRAY, - SMEM_MEMORY_BARRIER_LOCATION, - - /* dynamic items */ - SMEM_AARM_PARTITION_TABLE, - SMEM_AARM_BAD_BLOCK_TABLE, - SMEM_RESERVE_BAD_BLOCKS, - SMEM_WM_UUID, - SMEM_CHANNEL_ALLOC_TBL, - SMEM_SMD_BASE_ID, - SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_CHANNELS, - SMEM_SMEM_LOG_EVENTS, - SMEM_SMEM_STATIC_LOG_IDX, - SMEM_SMEM_STATIC_LOG_EVENTS, - SMEM_SMEM_SLOW_CLOCK_SYNC, - SMEM_SMEM_SLOW_CLOCK_VALUE, - SMEM_BIO_LED_BUF, - SMEM_SMSM_SHARED_STATE, - SMEM_SMSM_INT_INFO, - SMEM_SMSM_SLEEP_DELAY, - SMEM_SMSM_LIMIT_SLEEP, - SMEM_SLEEP_POWER_COLLAPSE_DISABLED, - SMEM_KEYPAD_KEYS_PRESSED, - SMEM_KEYPAD_STATE_UPDATED, - SMEM_KEYPAD_STATE_IDX, - SMEM_GPIO_INT, - SMEM_MDDI_LCD_IDX, - SMEM_MDDI_HOST_DRIVER_STATE, - SMEM_MDDI_LCD_DISP_STATE, - SMEM_LCD_CUR_PANEL, - SMEM_MARM_BOOT_SEGMENT_INFO, - SMEM_AARM_BOOT_SEGMENT_INFO, - SMEM_SLEEP_STATIC, - SMEM_SCORPION_FREQUENCY, - SMEM_SMD_PROFILES, - SMEM_TSSC_BUSY, - SMEM_HS_SUSPEND_FILTER_INFO, - SMEM_BATT_INFO, - SMEM_APPS_BOOT_MODE, - SMEM_VERSION_FIRST, - SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24, - SMEM_OSS_RRCASN1_BUF1, - SMEM_OSS_RRCASN1_BUF2, - SMEM_ID_VENDOR0, - SMEM_ID_VENDOR1, - SMEM_ID_VENDOR2, - SMEM_HW_SW_BUILD_ID, - SMEM_SMD_BLOCK_PORT_BASE_ID, - SMEM_SMD_BLOCK_PORT_PROC0_HEAP = SMEM_SMD_BLOCK_PORT_BASE_ID + SMEM_NUM_SMD_CHANNELS, - SMEM_SMD_BLOCK_PORT_PROC1_HEAP = SMEM_SMD_BLOCK_PORT_PROC0_HEAP + SMEM_NUM_SMD_CHANNELS, - SMEM_I2C_MUTEX = SMEM_SMD_BLOCK_PORT_PROC1_HEAP + SMEM_NUM_SMD_CHANNELS, - SMEM_SCLK_CONVERSION, - SMEM_SMD_SMSM_INTR_MUX, - SMEM_SMSM_CPU_INTR_MASK, - SMEM_APPS_DEM_SLAVE_DATA, - SMEM_QDSP6_DEM_SLAVE_DATA, - SMEM_CLKREGIM_BSP, - SMEM_CLKREGIM_SOURCES, - SMEM_SMD_FIFO_BASE_ID, - SMEM_USABLE_RAM_PARTITION_TABLE = SMEM_SMD_FIFO_BASE_ID + SMEM_NUM_SMD_CHANNELS, - SMEM_POWER_ON_STATUS_INFO, - SMEM_DAL_AREA, - SMEM_SMEM_LOG_POWER_IDX, - SMEM_SMEM_LOG_POWER_WRAP, - SMEM_SMEM_LOG_POWER_EVENTS, - SMEM_ERR_CRASH_LOG, - SMEM_ERR_F3_TRACE_LOG, - SMEM_NUM_ITEMS, -} smem_mem_type; - - -#define SMD_SS_CLOSED 0x00000000 -#define SMD_SS_OPENING 0x00000001 -#define SMD_SS_OPENED 0x00000002 -#define SMD_SS_FLUSHING 0x00000003 -#define SMD_SS_CLOSING 0x00000004 -#define SMD_SS_RESET 0x00000005 -#define SMD_SS_RESET_OPENING 0x00000006 - -#define SMD_BUF_SIZE 8192 -#define SMD_CHANNELS 64 - -#define SMD_HEADER_SIZE 20 +#define SMD_BUF_SIZE 8192 +#define SMD_CHANNELS 64 +#define SMD_HEADER_SIZE 20 +/* 'type' field of smd_alloc_elm structure + * has the following breakup + * bits 0-7 -> channel type + * bits 8-11 -> xfer type + * bits 12-31 -> reserved + */ struct smd_alloc_elm { char name[20]; uint32_t cid; - uint32_t ctype; + uint32_t type; uint32_t ref_count; }; +#define SMD_CHANNEL_TYPE(x) ((x) & 0x000000FF) +#define SMD_XFER_TYPE(x) (((x) & 0x00000F00) >> 8) + struct smd_half_channel { unsigned state; unsigned char fDSR; @@ -263,141 +127,139 @@ struct smd_half_channel { unsigned char fHEAD; unsigned char fTAIL; unsigned char fSTATE; - unsigned char fUNUSED; + unsigned char fBLOCKREADINTR; unsigned tail; unsigned head; -} __attribute__(( aligned(4), packed )); - -/* Only used on SMD package v3 on msm7201a */ -struct smd_shared_v1 { - struct smd_half_channel ch0; - unsigned char data0[SMD_BUF_SIZE]; - struct smd_half_channel ch1; - unsigned char data1[SMD_BUF_SIZE]; }; -/* Used on SMD package v4 */ -struct smd_shared_v2 { - struct smd_half_channel ch0; - struct smd_half_channel ch1; +struct smd_half_channel_word_access { + unsigned state; + unsigned fDSR; + unsigned fCTS; + unsigned fCD; + unsigned fRI; + unsigned fHEAD; + unsigned fTAIL; + unsigned fSTATE; + unsigned fBLOCKREADINTR; + unsigned tail; + unsigned head; }; -struct smd_channel { - volatile struct smd_half_channel *send; - volatile struct smd_half_channel *recv; - unsigned char *send_data; - unsigned char *recv_data; +struct smd_half_channel_access { + void (*set_state)(volatile void *half_channel, unsigned data); + unsigned (*get_state)(volatile void *half_channel); + void (*set_fDSR)(volatile void *half_channel, unsigned char data); + unsigned (*get_fDSR)(volatile void *half_channel); + void (*set_fCTS)(volatile void *half_channel, unsigned char data); + unsigned (*get_fCTS)(volatile void *half_channel); + void (*set_fCD)(volatile void *half_channel, unsigned char data); + unsigned (*get_fCD)(volatile void *half_channel); + void (*set_fRI)(volatile void *half_channel, unsigned char data); + unsigned (*get_fRI)(volatile void *half_channel); + void (*set_fHEAD)(volatile void *half_channel, unsigned char data); + unsigned (*get_fHEAD)(volatile void *half_channel); + void (*set_fTAIL)(volatile void *half_channel, unsigned char data); + unsigned (*get_fTAIL)(volatile void *half_channel); + void (*set_fSTATE)(volatile void *half_channel, unsigned char data); + unsigned (*get_fSTATE)(volatile void *half_channel); + void (*set_fBLOCKREADINTR)(volatile void *half_channel, + unsigned char data); + unsigned (*get_fBLOCKREADINTR)(volatile void *half_channel); + void (*set_tail)(volatile void *half_channel, unsigned data); + unsigned (*get_tail)(volatile void *half_channel); + void (*set_head)(volatile void *half_channel, unsigned data); + unsigned (*get_head)(volatile void *half_channel); +}; - unsigned fifo_mask; - unsigned fifo_size; - unsigned current_packet; - unsigned n; +int is_word_access_ch(unsigned ch_type); - struct list_head ch_list; +struct smd_half_channel_access *get_half_ch_funcs(unsigned ch_type); - void *priv; - void (*notify)(void *priv, unsigned flags); +struct smem_ram_ptn { + char name[16]; + unsigned start; + unsigned size; - int (*read)(struct smd_channel *ch, void *data, int len); - int (*write)(struct smd_channel *ch, const void *data, int len); - int (*read_avail)(struct smd_channel *ch); - int (*write_avail)(struct smd_channel *ch); + /* RAM Partition attribute: READ_ONLY, READWRITE etc. */ + unsigned attr; - void (*update_state)(struct smd_channel *ch); - unsigned last_state; - void (*notify_other_cpu)(void); + /* RAM Partition category: EBI0, EBI1, IRAM, IMEM */ + unsigned category; + + /* RAM Partition domain: APPS, MODEM, APPS & MODEM (SHARED) etc. */ + unsigned domain; + + /* RAM Partition type: system, bootloader, appsboot, apps etc. */ unsigned type; - char name[32]; - struct platform_device pdev; + /* reserved for future expansion without changing version number */ + unsigned reserved2, reserved3, reserved4, reserved5; +} __attribute__ ((__packed__)); + + +struct smem_ram_ptable { + #define _SMEM_RAM_PTABLE_MAGIC_1 0x9DA5E0A8 + #define _SMEM_RAM_PTABLE_MAGIC_2 0xAF9EC4E2 + unsigned magic[2]; + unsigned version; + unsigned reserved1; + unsigned len; + struct smem_ram_ptn parts[32]; + unsigned buf; +} __attribute__ ((__packed__)); + +/* SMEM RAM Partition */ +enum { + DEFAULT_ATTRB = ~0x0, + READ_ONLY = 0x0, + READWRITE, }; -#define SMD_TYPE_MASK 0x0FF -#define SMD_TYPE_APPS_MODEM 0x000 -#define SMD_TYPE_APPS_DSP 0x001 -#define SMD_TYPE_MODEM_DSP 0x002 +enum { + DEFAULT_CATEGORY = ~0x0, + SMI = 0x0, + EBI1, + EBI2, + QDSP6, + IRAM, + IMEM, + EBI0_CS0, + EBI0_CS1, + EBI1_CS0, + EBI1_CS1, + SDRAM = 0xE, +}; -#define SMD_KIND_MASK 0xF00 -#define SMD_KIND_UNKNOWN 0x000 -#define SMD_KIND_STREAM 0x100 -#define SMD_KIND_PACKET 0x200 +enum { + DEFAULT_DOMAIN = 0x0, + APPS_DOMAIN, + MODEM_DOMAIN, + SHARED_DOMAIN, +}; -extern struct list_head smd_ch_closed_list; -extern struct list_head smd_ch_list_modem; -extern struct list_head smd_ch_list_dsp; +enum { + SYS_MEMORY = 1, /* system memory*/ + BOOT_REGION_MEMORY1, /* boot loader memory 1*/ + BOOT_REGION_MEMORY2, /* boot loader memory 2,reserved*/ + APPSBL_MEMORY, /* apps boot loader memory*/ + APPS_MEMORY, /* apps usage memory*/ +}; -extern spinlock_t smd_lock; extern spinlock_t smem_lock; -void *smem_find(unsigned id, unsigned size); -void *smem_item(unsigned id, unsigned *size); -uint32_t raw_smsm_get_state(enum smsm_state_item item); -extern void msm_init_last_radio_log(struct module *); +void smd_diag(void); -#ifdef CONFIG_MSM_SMD_PKG3 -/* - * This allocator assumes an SMD Package v3 which only exists on - * MSM7x00 SoC's. - */ -static inline int _smd_alloc_channel(struct smd_channel *ch) -{ - struct smd_shared_v1 *shared1; - - shared1 = smem_alloc(ID_SMD_CHANNELS + ch->n, sizeof(*shared1)); - if (!shared1) { - pr_err("smd_alloc_channel() cid %d does not exist\n", ch->n); - return -1; - } - ch->send = &shared1->ch0; - ch->recv = &shared1->ch1; - ch->send_data = shared1->data0; - ch->recv_data = shared1->data1; - ch->fifo_size = SMD_BUF_SIZE; - return 0; -} -#else -/* - * This allocator assumes an SMD Package v4, the most common - * and the default. - */ -static inline int _smd_alloc_channel(struct smd_channel *ch) -{ - struct smd_shared_v2 *shared2; - void *buffer; - unsigned buffer_sz; - - shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, sizeof(*shared2)); - buffer = smem_item(SMEM_SMD_FIFO_BASE_ID + ch->n, &buffer_sz); - - if (!buffer) - return -1; - - /* buffer must be a power-of-two size */ - if (buffer_sz & (buffer_sz - 1)) - return -1; - - buffer_sz /= 2; - ch->send = &shared2->ch0; - ch->recv = &shared2->ch1; - ch->send_data = buffer; - ch->recv_data = buffer + buffer_sz; - ch->fifo_size = buffer_sz; - return 0; -} -#endif /* CONFIG_MSM_SMD_PKG3 */ - -#if defined(CONFIG_ARCH_MSM7X30) -static inline void msm_a2m_int(uint32_t irq) -{ - writel(1 << irq, MSM_GCC_BASE + 0x8); -} -#else -static inline void msm_a2m_int(uint32_t irq) -{ - writel(1, MSM_CSR_BASE + 0x400 + (irq * 4)); -} -#endif /* CONFIG_ARCH_MSM7X30 */ +struct interrupt_stat { + uint32_t smd_in_count; + uint32_t smd_out_hardcode_count; + uint32_t smd_out_config_count; + uint32_t smsm_in_count; + uint32_t smsm_out_hardcode_count; + uint32_t smsm_out_config_count; +}; +extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS]; #endif diff --git a/arch/arm/mach-msm/smd_qmi.c b/arch/arm/mach-msm/smd_qmi.c new file mode 100644 index 00000000000..ca10f00d0cf --- /dev/null +++ b/arch/arm/mach-msm/smd_qmi.c @@ -0,0 +1,848 @@ +/* arch/arm/mach-msm/smd_qmi.c + * + * QMI Control Driver -- Manages network data connections. + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define QMI_CTL 0x00 +#define QMI_WDS 0x01 +#define QMI_DMS 0x02 +#define QMI_NAS 0x03 + +#define QMI_RESULT_SUCCESS 0x0000 +#define QMI_RESULT_FAILURE 0x0001 + +struct qmi_msg { + unsigned char service; + unsigned char client_id; + unsigned short txn_id; + unsigned short type; + unsigned short size; + unsigned char *tlv; +}; + +#define qmi_ctl_client_id 0 + +#define STATE_OFFLINE 0 +#define STATE_QUERYING 1 +#define STATE_ONLINE 2 + +struct qmi_ctxt { + struct miscdevice misc; + + struct mutex lock; + + unsigned char ctl_txn_id; + unsigned char wds_client_id; + unsigned short wds_txn_id; + + unsigned wds_busy; + unsigned wds_handle; + unsigned state_dirty; + unsigned state; + + unsigned char addr[4]; + unsigned char mask[4]; + unsigned char gateway[4]; + unsigned char dns1[4]; + unsigned char dns2[4]; + + smd_channel_t *ch; + const char *ch_name; + struct wake_lock wake_lock; + + struct work_struct open_work; + struct work_struct read_work; +}; + +static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n); + +static void qmi_read_work(struct work_struct *ws); +static void qmi_open_work(struct work_struct *work); + +void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n) +{ + mutex_init(&ctxt->lock); + INIT_WORK(&ctxt->read_work, qmi_read_work); + INIT_WORK(&ctxt->open_work, qmi_open_work); + wake_lock_init(&ctxt->wake_lock, WAKE_LOCK_SUSPEND, ctxt->misc.name); + ctxt->ctl_txn_id = 1; + ctxt->wds_txn_id = 1; + ctxt->wds_busy = 1; + ctxt->state = STATE_OFFLINE; + +} + +static struct workqueue_struct *qmi_wq; + +static int verbose = 0; + +/* anyone waiting for a state change waits here */ +static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue); + + +static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix) +{ + unsigned sz, n; + unsigned char *x; + + if (!verbose) + return; + + printk(KERN_INFO + "qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n", + prefix, msg->service, msg->client_id, + msg->txn_id, msg->type, msg->size); + + x = msg->tlv; + sz = msg->size; + + while (sz >= 3) { + sz -= 3; + + n = x[1] | (x[2] << 8); + if (n > sz) + break; + + printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ", + prefix, x[0], n); + x += 3; + sz -= n; + while (n-- > 0) + printk("%02x ", *x++); + printk("}\n"); + } +} + +int qmi_add_tlv(struct qmi_msg *msg, + unsigned type, unsigned size, const void *data) +{ + unsigned char *x = msg->tlv + msg->size; + + x[0] = type; + x[1] = size; + x[2] = size >> 8; + + memcpy(x + 3, data, size); + + msg->size += (size + 3); + + return 0; +} + +/* Extract a tagged item from a qmi message buffer, +** taking care not to overrun the buffer. +*/ +static int qmi_get_tlv(struct qmi_msg *msg, + unsigned type, unsigned size, void *data) +{ + unsigned char *x = msg->tlv; + unsigned len = msg->size; + unsigned n; + + while (len >= 3) { + len -= 3; + + /* size of this item */ + n = x[1] | (x[2] << 8); + if (n > len) + break; + + if (x[0] == type) { + if (n != size) + return -1; + memcpy(data, x + 3, size); + return 0; + } + + x += (n + 3); + len -= n; + } + + return -1; +} + +static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error) +{ + unsigned short status[2]; + if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) { + *error = 0; + return QMI_RESULT_FAILURE; + } else { + *error = status[1]; + return status[0]; + } +} + +/* 0x01 */ +#define QMUX_HEADER 13 + +/* should be >= HEADER + FOOTER */ +#define QMUX_OVERHEAD 16 + +static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned char *data; + unsigned hlen; + unsigned len; + int r; + + qmi_dump_msg(msg, "send"); + + if (msg->service == QMI_CTL) { + hlen = QMUX_HEADER - 1; + } else { + hlen = QMUX_HEADER; + } + + /* QMUX length is total header + total payload - IFC selector */ + len = hlen + msg->size - 1; + if (len > 0xffff) + return -1; + + data = msg->tlv - hlen; + + /* prepend encap and qmux header */ + *data++ = 0x01; /* ifc selector */ + + /* qmux header */ + *data++ = len; + *data++ = len >> 8; + *data++ = 0x00; /* flags: client */ + *data++ = msg->service; + *data++ = msg->client_id; + + /* qmi header */ + *data++ = 0x00; /* flags: send */ + *data++ = msg->txn_id; + if (msg->service != QMI_CTL) + *data++ = msg->txn_id >> 8; + + *data++ = msg->type; + *data++ = msg->type >> 8; + *data++ = msg->size; + *data++ = msg->size >> 8; + + /* len + 1 takes the interface selector into account */ + r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1); + + if (r != len) { + return -1; + } else { + return 0; + } +} + +static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned err; + if (msg->type == 0x0022) { + unsigned char n[2]; + if (qmi_get_status(msg, &err)) + return; + if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) + return; + if (n[0] == QMI_WDS) { + printk(KERN_INFO + "qmi: ctl: wds use client_id 0x%02x\n", n[1]); + ctxt->wds_client_id = n[1]; + ctxt->wds_busy = 0; + } + } +} + +static int qmi_network_get_profile(struct qmi_ctxt *ctxt); + +static void swapaddr(unsigned char *src, unsigned char *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +static unsigned char zero[4]; +static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned char tmp[4]; + unsigned r; + + r = qmi_get_tlv(msg, 0x1e, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->addr); + r = qmi_get_tlv(msg, 0x21, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->mask); + r = qmi_get_tlv(msg, 0x20, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->gateway); + r = qmi_get_tlv(msg, 0x15, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->dns1); + r = qmi_get_tlv(msg, 0x16, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->dns2); +} + +static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + unsigned err; + switch (msg->type) { + case 0x0021: + if (qmi_get_status(msg, &err)) { + printk(KERN_ERR + "qmi: wds: network stop failed (%04x)\n", err); + } else { + printk(KERN_INFO + "qmi: wds: network stopped\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + } + break; + case 0x0020: + if (qmi_get_status(msg, &err)) { + printk(KERN_ERR + "qmi: wds: network start failed (%04x)\n", err); + } else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) { + printk(KERN_INFO + "qmi: wds no handle?\n"); + } else { + printk(KERN_INFO + "qmi: wds: got handle 0x%08x\n", + ctxt->wds_handle); + } + break; + case 0x002D: + printk("qmi: got network profile\n"); + if (ctxt->state == STATE_QUERYING) { + qmi_read_runtime_profile(ctxt, msg); + ctxt->state = STATE_ONLINE; + ctxt->state_dirty = 1; + } + break; + default: + printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type); + } + ctxt->wds_busy = 0; +} + +static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + if (msg->type == 0x0022) { + unsigned char n[2]; + if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) + return; + switch (n[0]) { + case 1: + printk(KERN_INFO "qmi: wds: DISCONNECTED\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + break; + case 2: + printk(KERN_INFO "qmi: wds: CONNECTED\n"); + ctxt->state = STATE_QUERYING; + ctxt->state_dirty = 1; + qmi_network_get_profile(ctxt); + break; + case 3: + printk(KERN_INFO "qmi: wds: SUSPENDED\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + } + } else { + printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type); + } +} + +static void qmi_process_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + printk("wds: %04x @ %02x\n", msg->type, msg->client_id); + if (msg->client_id == ctxt->wds_client_id) { + qmi_process_unicast_wds_msg(ctxt, msg); + } else if (msg->client_id == 0xff) { + qmi_process_broadcast_wds_msg(ctxt, msg); + } else { + printk(KERN_ERR + "qmi_process_wds_msg client id 0x%02x unknown\n", + msg->client_id); + } +} + +static void qmi_process_qmux(struct qmi_ctxt *ctxt, + unsigned char *buf, unsigned sz) +{ + struct qmi_msg msg; + + /* require a full header */ + if (sz < 5) + return; + + /* require a size that matches the buffer size */ + if (sz != (buf[0] | (buf[1] << 8))) + return; + + /* only messages from a service (bit7=1) are allowed */ + if (buf[2] != 0x80) + return; + + msg.service = buf[3]; + msg.client_id = buf[4]; + + /* annoyingly, CTL messages have a shorter TID */ + if (buf[3] == 0) { + if (sz < 7) + return; + msg.txn_id = buf[6]; + buf += 7; + sz -= 7; + } else { + if (sz < 8) + return; + msg.txn_id = buf[6] | (buf[7] << 8); + buf += 8; + sz -= 8; + } + + /* no type and size!? */ + if (sz < 4) + return; + sz -= 4; + + msg.type = buf[0] | (buf[1] << 8); + msg.size = buf[2] | (buf[3] << 8); + msg.tlv = buf + 4; + + if (sz != msg.size) + return; + + qmi_dump_msg(&msg, "recv"); + + mutex_lock(&ctxt->lock); + switch (msg.service) { + case QMI_CTL: + qmi_process_ctl_msg(ctxt, &msg); + break; + case QMI_WDS: + qmi_process_wds_msg(ctxt, &msg); + break; + default: + printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n", + msg.service); + break; + } + mutex_unlock(&ctxt->lock); + + wake_up(&qmi_wait_queue); +} + +#define QMI_MAX_PACKET (256 + QMUX_OVERHEAD) + +static void qmi_read_work(struct work_struct *ws) +{ + struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work); + struct smd_channel *ch = ctxt->ch; + unsigned char buf[QMI_MAX_PACKET]; + int sz; + + for (;;) { + sz = smd_cur_packet_size(ch); + if (sz == 0) + break; + if (sz < smd_read_avail(ch)) + break; + if (sz > QMI_MAX_PACKET) { + smd_read(ch, 0, sz); + continue; + } + if (smd_read(ch, buf, sz) != sz) { + printk(KERN_ERR "qmi: not enough data?!\n"); + continue; + } + + /* interface selector must be 1 */ + if (buf[0] != 0x01) + continue; + + qmi_process_qmux(ctxt, buf + 1, sz - 1); + } +} + +static int qmi_request_wds_cid(struct qmi_ctxt *ctxt); + +static void qmi_open_work(struct work_struct *ws) +{ + struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work); + mutex_lock(&ctxt->lock); + qmi_request_wds_cid(ctxt); + mutex_unlock(&ctxt->lock); +} + +static void qmi_notify(void *priv, unsigned event) +{ + struct qmi_ctxt *ctxt = priv; + + switch (event) { + case SMD_EVENT_DATA: { + int sz; + sz = smd_cur_packet_size(ctxt->ch); + if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch))) { + wake_lock_timeout(&ctxt->wake_lock, HZ / 2); + queue_work(qmi_wq, &ctxt->read_work); + } + break; + } + case SMD_EVENT_OPEN: + printk(KERN_INFO "qmi: smd opened\n"); + queue_work(qmi_wq, &ctxt->open_work); + break; + case SMD_EVENT_CLOSE: + printk(KERN_INFO "qmi: smd closed\n"); + break; + } +} + +static int qmi_request_wds_cid(struct qmi_ctxt *ctxt) +{ + unsigned char data[64 + QMUX_OVERHEAD]; + struct qmi_msg msg; + unsigned char n; + + msg.service = QMI_CTL; + msg.client_id = qmi_ctl_client_id; + msg.txn_id = ctxt->ctl_txn_id; + msg.type = 0x0022; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->ctl_txn_id += 2; + + n = QMI_WDS; + qmi_add_tlv(&msg, 0x01, 0x01, &n); + + return qmi_send(ctxt, &msg); +} + +static int qmi_network_get_profile(struct qmi_ctxt *ctxt) +{ + unsigned char data[96 + QMUX_OVERHEAD]; + struct qmi_msg msg; + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x002D; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + return qmi_send(ctxt, &msg); +} + +static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn) +{ + unsigned char data[96 + QMUX_OVERHEAD]; + struct qmi_msg msg; + char *user; + char *pass; + + for (user = apn; *user; user++) { + if (*user == ' ') { + *user++ = 0; + break; + } + } + for (pass = user; *pass; pass++) { + if (*pass == ' ') { + *pass++ = 0; + break; + } + } + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x0020; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + qmi_add_tlv(&msg, 0x14, strlen(apn), apn); + if (*user) { + unsigned char x; + x = 3; + qmi_add_tlv(&msg, 0x16, 1, &x); + qmi_add_tlv(&msg, 0x17, strlen(user), user); + if (*pass) + qmi_add_tlv(&msg, 0x18, strlen(pass), pass); + } + return qmi_send(ctxt, &msg); +} + +static int qmi_network_down(struct qmi_ctxt *ctxt) +{ + unsigned char data[16 + QMUX_OVERHEAD]; + struct qmi_msg msg; + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x0021; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle); + + return qmi_send(ctxt, &msg); +} + +static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max) +{ + int i; + char *statename; + + if (ctxt->state == STATE_ONLINE) { + statename = "up"; + } else if (ctxt->state == STATE_OFFLINE) { + statename = "down"; + } else { + statename = "busy"; + } + + i = scnprintf(buf, max, "STATE=%s\n", statename); + i += scnprintf(buf + i, max - i, "CID=%d\n",ctxt->wds_client_id); + + if (ctxt->state != STATE_ONLINE){ + return i; + } + + i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n", + ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]); + i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n", + ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]); + i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n", + ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2], + ctxt->gateway[3]); + i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n", + ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]); + i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n", + ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]); + + return i; +} + +static ssize_t qmi_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct qmi_ctxt *ctxt = fp->private_data; + char msg[256]; + int len; + int r; + + mutex_lock(&ctxt->lock); + for (;;) { + if (ctxt->state_dirty) { + ctxt->state_dirty = 0; + len = qmi_print_state(ctxt, msg, 256); + break; + } + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty); + if (r < 0) + return r; + mutex_lock(&ctxt->lock); + } + mutex_unlock(&ctxt->lock); + + if (len > count) + len = count; + + if (copy_to_user(buf, msg, len)) + return -EFAULT; + + return len; +} + + +static ssize_t qmi_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct qmi_ctxt *ctxt = fp->private_data; + unsigned char cmd[64]; + int len; + int r; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "verbose", 7)) { + verbose = 1; + } else if (!strncmp(cmd, "terse", 5)) { + verbose = 0; + } else if (!strncmp(cmd, "poll", 4)) { + ctxt->state_dirty = 1; + wake_up(&qmi_wait_queue); + } else if (!strncmp(cmd, "down", 4)) { +retry_down: + mutex_lock(&ctxt->lock); + if (ctxt->wds_busy) { + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); + if (r < 0) + return r; + goto retry_down; + } + ctxt->wds_busy = 1; + qmi_network_down(ctxt); + mutex_unlock(&ctxt->lock); + } else if (!strncmp(cmd, "up:", 3)) { +retry_up: + mutex_lock(&ctxt->lock); + if (ctxt->wds_busy) { + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); + if (r < 0) + return r; + goto retry_up; + } + ctxt->wds_busy = 1; + qmi_network_up(ctxt, cmd+3); + mutex_unlock(&ctxt->lock); + } else { + return -EINVAL; + } + + return count; +} + +static int qmi_open(struct inode *ip, struct file *fp) +{ + struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev)); + int r = 0; + + if (!ctxt) { + printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev)); + return -ENODEV; + } + + fp->private_data = ctxt; + + mutex_lock(&ctxt->lock); + if (ctxt->ch == 0) + r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify); + if (r == 0) + wake_up(&qmi_wait_queue); + mutex_unlock(&ctxt->lock); + + return r; +} + +static int qmi_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static struct file_operations qmi_fops = { + .owner = THIS_MODULE, + .read = qmi_read, + .write = qmi_write, + .open = qmi_open, + .release = qmi_release, +}; + +static struct qmi_ctxt qmi_device0 = { + .ch_name = "SMD_DATA5_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi0", + .fops = &qmi_fops, + } +}; +static struct qmi_ctxt qmi_device1 = { + .ch_name = "SMD_DATA6_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi1", + .fops = &qmi_fops, + } +}; +static struct qmi_ctxt qmi_device2 = { + .ch_name = "SMD_DATA7_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi2", + .fops = &qmi_fops, + } +}; + +static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n) +{ + if (n == qmi_device0.misc.minor) + return &qmi_device0; + if (n == qmi_device1.misc.minor) + return &qmi_device1; + if (n == qmi_device2.misc.minor) + return &qmi_device2; + return 0; +} + +static int __init qmi_init(void) +{ + int ret; + + qmi_wq = create_singlethread_workqueue("qmi"); + if (qmi_wq == 0) + return -ENOMEM; + + qmi_ctxt_init(&qmi_device0, 0); + qmi_ctxt_init(&qmi_device1, 1); + qmi_ctxt_init(&qmi_device2, 2); + + ret = misc_register(&qmi_device0.misc); + if (ret == 0) + ret = misc_register(&qmi_device1.misc); + if (ret == 0) + ret = misc_register(&qmi_device2.misc); + return ret; +} + +module_init(qmi_init); diff --git a/arch/arm/mach-msm/smd_rpc_sym b/arch/arm/mach-msm/smd_rpc_sym new file mode 100755 index 00000000000..6965cf6fec3 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpc_sym @@ -0,0 +1,156 @@ +0x30000000 cm +0x30000001 db +0x30000002 snd +0x30000003 wms +0x30000004 pdsm +0x30000005 misc_modem_apis +0x30000006 misc_apps_apis +0x30000007 joyst +0x3000000A adsprtosatom +0x3000000B adsprtosmtoa +0x3000000C i2c +0x3000000D time_remote +0x3000000E nv +0x3000000F clkrgm_sec +0x30000010 rdevmap +0x30000012 pbmlib +0x30000013 audmgr +0x30000014 mvs +0x30000015 dog_keepalive +0x30000016 gsdi_exp +0x30000017 auth +0x30000018 nvruimi +0x30000019 mmgsdilib +0x3000001A charger +0x3000001B uim +0x3000001D pdsm_atl +0x3000001E fs_xmount +0x3000001F secutil +0x30000020 mccmeid +0x30000021 pm_strobe_flash +0x30000023 smd_bridge +0x30000024 smd_port_mgr +0x30000025 bus_perf +0x30000026 bus_mon_remote +0x30000027 mc +0x30000028 mccap +0x30000029 mccdma +0x3000002A mccds +0x3000002B mccsch +0x3000002C mccsrid +0x3000002D snm +0x3000002E mccsyobj +0x30000031 dsrlp_apis +0x30000032 rlp_apis +0x30000033 ds_mp_shim_modem +0x30000035 dshdr_mdm_apis +0x30000036 ds_mp_shim_apps +0x30000037 hdrmc_apis +0x3000003A pmapp_otg +0x3000003B diag +0x3000003C gstk_exp +0x3000003D dsbc_mdm_apis +0x3000003E hdrmrlp_mdm_apis +0x30000040 hdrmc_mrlp_apis +0x30000041 pdcomm_app_api +0x30000042 dsat_apis +0x30000043 rfm +0x30000044 cmipapp +0x30000045 dsmp_umts_modem_apis +0x30000047 dsucsdmpshim +0x30000048 time_remote_atom +0x3000004A sd +0x3000004B mmoc +0x3000004D wlan_cp_cm +0x3000004E ftm_wlan +0x30000050 CprmInterface +0x30000051 data_on_modem_mtoa_apis +0x30000053 misc_modem_apis_nonwinmob +0x30000054 misc_apps_apis_nonwinmob +0x30000055 pmem_remote +0x30000056 tcxomgr +0x30000058 bt +0x30000059 pd_comms_api +0x3000005A pd_comms_client_api +0x3000005B pdapi +0x3000005D time_remote_mtoa +0x3000005E ftm_bt +0x3000005F dsucsdappif_apis +0x30000060 pmapp_gen +0x30000061 pm_lib +0x30000063 hsu_app_apis +0x30000064 hsu_mdm_apis +0x30000065 adie_adc_remote_atom +0x30000066 tlmm_remote_atom +0x30000067 ui_callctrl +0x30000068 uiutils +0x30000069 prl +0x3000006A hw +0x3000006B oem_rapi +0x3000006C wmspm +0x3000006D btpf +0x3000006F usb_apps_rpc +0x30000070 usb_modem_rpc +0x30000071 adc +0x30000072 cameraremoted +0x30000073 secapiremoted +0x30000074 dsatapi +0x30000075 clkctl_rpc +0x30000076 BrewAppCoord +0x30000078 wlan_trp_utils +0x30000079 gpio_rpc +0x3000007C l1_ds +0x3000007F oss_rrcasn_remote +0x30000080 pmapp_otg_remote +0x30000081 ping_mdm_rpc +0x30000087 ukcc_ipc_apis +0x30000089 vbatt_remote +0x3000008A mfpal_fps +0x3000008B dsumtspdpreg +0x3000008C loc_api +0x3000008E cmgan +0x3000008F isense +0x30000090 time_secure +0x30000091 hs_rem +0x30000092 acdb +0x30000093 net +0x30000094 led +0x30000095 dspae +0x30000096 mfkal +0x3000009B test_api +0x3000009C remotefs_srv_api +0x3000009D isi_transport +0x3000009E oem_ftm +0x3000009F touch_screen_adc +0x300000A0 smd_bridge_apps +0x300000A1 smd_bridge_modem +0x300000A2 dog_keepalive_modem +0x300000A3 voem_if +0x300000A4 npa_remote +0x300000A5 mmgsdisessionlib +0x300000A6 ifta_remote +0x300000A7 remote_storage +0x300000A8 mf_remote_file +0x300000A9 mfsc_chunked_transport +0x300000AA mfim3 +0x300000AB fm_wan_api +0x300000AC wlan_rapi +0x300000AD dsmgr_apis +0x300100AE cm_mm_fusion +0x30010081 ping_lte_rpc +0x30010061 pm_lib_fusion +0x3001000E nv_fusion +0x30010003 wms_fusion +0x3001001B uim_fusion +0x30010012 pbmlib_fusion +0x3001003C gstk_exp_fusion +0x30010000 cm_fusion +0x3001006B oem_rapi_fusion +0x3001000F clkrgm_sec_fusion +0x300100A0 smd_bridge_apps_fusion +0x300100A1 smd_bridge_modem_fusion +0x30010024 smd_port_mgr_fusion +0x30010019 mmgsdilib_fusion +0x300100A5 mmgsdisessionlib_fusion +0x3001009C remotefs_srv_api_fusion +0x30010016 gsdi_exp_fusion diff --git a/arch/arm/mach-msm/smd_rpc_sym.h b/arch/arm/mach-msm/smd_rpc_sym.h new file mode 100644 index 00000000000..e9a8b474737 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpc_sym.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2009, Code Aurora Forum. 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. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * 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 _ARCH_ARM_MACH_MSM_SMD_RPC_SYM_H +#define _ARCH_ARM_MACH_MSM_SMD_RPC_SYM_H + +#if defined(CONFIG_DEBUG_FS) +const char *smd_rpc_get_sym(uint32_t val); +#endif + +#endif diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c new file mode 100644 index 00000000000..983d0c197bb --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -0,0 +1,2531 @@ +/* arch/arm/mach-msm/smd_rpcrouter.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* TODO: handle cases where smd_write() will tempfail due to full fifo */ +/* TODO: thread priority? schedule a work to bump it? */ +/* TODO: maybe make server_list_lock a mutex */ +/* TODO: pool fragments to avoid kmalloc/kfree churn */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "smd_rpcrouter.h" +#include "modem_notifier.h" +#include "smd_rpc_sym.h" +#include "smd_private.h" + +enum { + SMEM_LOG = 1U << 0, + RTR_DBG = 1U << 1, + R2R_MSG = 1U << 2, + R2R_RAW = 1U << 3, + RPC_MSG = 1U << 4, + NTFY_MSG = 1U << 5, + RAW_PMR = 1U << 6, + RAW_PMW = 1U << 7, + R2R_RAW_HDR = 1U << 8, +}; +static int msm_rpc_connect_timeout_ms; +module_param_named(connect_timeout, msm_rpc_connect_timeout_ms, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +static int smd_rpcrouter_debug_mask; +module_param_named(debug_mask, smd_rpcrouter_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define DIAG(x...) printk(KERN_ERR "[RR] ERROR " x) + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) +#define D(x...) do { \ +if (smd_rpcrouter_debug_mask & RTR_DBG) \ + printk(KERN_ERR x); \ +} while (0) + +#define RR(x...) do { \ +if (smd_rpcrouter_debug_mask & R2R_MSG) \ + printk(KERN_ERR "[RR] "x); \ +} while (0) + +#define RAW(x...) do { \ +if (smd_rpcrouter_debug_mask & R2R_RAW) \ + printk(KERN_ERR "[RAW] "x); \ +} while (0) + +#define RAW_HDR(x...) do { \ +if (smd_rpcrouter_debug_mask & R2R_RAW_HDR) \ + printk(KERN_ERR "[HDR] "x); \ +} while (0) + +#define RAW_PMR(x...) do { \ +if (smd_rpcrouter_debug_mask & RAW_PMR) \ + printk(KERN_ERR "[PMR] "x); \ +} while (0) + +#define RAW_PMR_NOMASK(x...) do { \ + printk(KERN_ERR "[PMR] "x); \ +} while (0) + +#define RAW_PMW(x...) do { \ +if (smd_rpcrouter_debug_mask & RAW_PMW) \ + printk(KERN_ERR "[PMW] "x); \ +} while (0) + +#define RAW_PMW_NOMASK(x...) do { \ + printk(KERN_ERR "[PMW] "x); \ +} while (0) + +#define IO(x...) do { \ +if (smd_rpcrouter_debug_mask & RPC_MSG) \ + printk(KERN_ERR "[RPC] "x); \ +} while (0) + +#define NTFY(x...) do { \ +if (smd_rpcrouter_debug_mask & NTFY_MSG) \ + printk(KERN_ERR "[NOTIFY] "x); \ +} while (0) +#else +#define D(x...) do { } while (0) +#define RR(x...) do { } while (0) +#define RAW(x...) do { } while (0) +#define RAW_HDR(x...) do { } while (0) +#define RAW_PMR(x...) do { } while (0) +#define RAW_PMR_NO_MASK(x...) do { } while (0) +#define RAW_PMW(x...) do { } while (0) +#define RAW_PMW_NO_MASK(x...) do { } while (0) +#define IO(x...) do { } while (0) +#define NTFY(x...) do { } while (0) +#endif + + +static LIST_HEAD(local_endpoints); +static LIST_HEAD(remote_endpoints); + +static LIST_HEAD(server_list); + +static wait_queue_head_t newserver_wait; +static wait_queue_head_t subsystem_restart_wait; + +static DEFINE_SPINLOCK(local_endpoints_lock); +static DEFINE_SPINLOCK(remote_endpoints_lock); +static DEFINE_SPINLOCK(server_list_lock); + +static LIST_HEAD(rpc_board_dev_list); +static DEFINE_SPINLOCK(rpc_board_dev_list_lock); + +static struct workqueue_struct *rpcrouter_workqueue; + +static atomic_t next_xid = ATOMIC_INIT(1); +static atomic_t pm_mid = ATOMIC_INIT(1); + +static void do_read_data(struct work_struct *work); +static void do_create_pdevs(struct work_struct *work); +static void do_create_rpcrouter_pdev(struct work_struct *work); + +static DECLARE_WORK(work_create_pdevs, do_create_pdevs); +static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev); + +#define RR_STATE_IDLE 0 +#define RR_STATE_HEADER 1 +#define RR_STATE_BODY 2 +#define RR_STATE_ERROR 3 + +/* State for remote ep following restart */ +#define RESTART_QUOTA_ABORT 1 + +struct rr_context { + struct rr_packet *pkt; + uint8_t *ptr; + uint32_t state; /* current assembly state */ + uint32_t count; /* bytes needed in this state */ +}; + +struct rr_context the_rr_context; + +struct rpc_board_dev_info { + struct list_head list; + + struct rpc_board_dev *dev; +}; + +static struct platform_device rpcrouter_pdev = { + .name = "oncrpc_router", + .id = -1, +}; + +struct rpcrouter_xprt_info { + struct list_head list; + + struct rpcrouter_xprt *xprt; + + int remote_pid; + uint32_t initialized; + wait_queue_head_t read_wait; + struct wake_lock wakelock; + spinlock_t lock; + uint32_t need_len; + struct work_struct read_data; + struct workqueue_struct *workqueue; + int abort_data_read; + unsigned char r2r_buf[RPCROUTER_MSGSIZE_MAX]; +}; + +static LIST_HEAD(xprt_info_list); +static DEFINE_MUTEX(xprt_info_list_lock); + +DECLARE_COMPLETION(rpc_remote_router_up); +static atomic_t pending_close_count = ATOMIC_INIT(0); + +/* + * Search for transport (xprt) that matches the provided PID. + * + * Note: The calling function must ensure that the mutex + * xprt_info_list_lock is locked when this function + * is called. + * + * @remote_pid Remote PID for the transport + * + * @returns Pointer to transport or NULL if not found + */ +static struct rpcrouter_xprt_info *rpcrouter_get_xprt_info(uint32_t remote_pid) +{ + struct rpcrouter_xprt_info *xprt_info; + + list_for_each_entry(xprt_info, &xprt_info_list, list) { + if (xprt_info->remote_pid == remote_pid) + return xprt_info; + } + return NULL; +} + +static int rpcrouter_send_control_msg(struct rpcrouter_xprt_info *xprt_info, + union rr_control_msg *msg) +{ + struct rr_header hdr; + unsigned long flags = 0; + int need; + + if (xprt_info->remote_pid == RPCROUTER_PID_LOCAL) + return 0; + + if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && + !xprt_info->initialized) { + printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, " + "router not initialized\n"); + return -EINVAL; + } + + hdr.version = RPCROUTER_VERSION; + hdr.type = msg->cmd; + hdr.src_pid = RPCROUTER_PID_LOCAL; + hdr.src_cid = RPCROUTER_ROUTER_ADDRESS; + hdr.confirm_rx = 0; + hdr.size = sizeof(*msg); + hdr.dst_pid = xprt_info->remote_pid; + hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS; + + /* TODO: what if channel is full? */ + + need = sizeof(hdr) + hdr.size; + spin_lock_irqsave(&xprt_info->lock, flags); + while (xprt_info->xprt->write_avail() < need) { + spin_unlock_irqrestore(&xprt_info->lock, flags); + msleep(250); + spin_lock_irqsave(&xprt_info->lock, flags); + } + xprt_info->xprt->write(&hdr, sizeof(hdr), HEADER); + xprt_info->xprt->write(msg, hdr.size, PAYLOAD); + spin_unlock_irqrestore(&xprt_info->lock, flags); + + return 0; +} + +static void modem_reset_cleanup(struct rpcrouter_xprt_info *xprt_info) +{ + struct msm_rpc_endpoint *ept; + struct rr_remote_endpoint *r_ept; + struct rr_packet *pkt, *tmp_pkt; + struct rr_fragment *frag, *next; + struct msm_rpc_reply *reply, *reply_tmp; + unsigned long flags; + + spin_lock_irqsave(&local_endpoints_lock, flags); + /* remove all partial packets received */ + list_for_each_entry(ept, &local_endpoints, list) { + RR("%s EPT DST PID %x, remote_pid:%d\n", __func__, + ept->dst_pid, xprt_info->remote_pid); + + if (xprt_info->remote_pid != ept->dst_pid) + continue; + + D("calling teardown cb %p\n", ept->cb_restart_teardown); + if (ept->cb_restart_teardown) + ept->cb_restart_teardown(ept->client_data); + ept->do_setup_notif = 1; + + /* remove replies */ + spin_lock(&ept->reply_q_lock); + list_for_each_entry_safe(reply, reply_tmp, + &ept->reply_pend_q, list) { + list_del(&reply->list); + kfree(reply); + } + list_for_each_entry_safe(reply, reply_tmp, + &ept->reply_avail_q, list) { + list_del(&reply->list); + kfree(reply); + } + ept->reply_cnt = 0; + spin_unlock(&ept->reply_q_lock); + + /* Set restart state for local ep */ + RR("EPT:0x%p, State %d RESTART_PEND_NTFY_SVR " + "PROG:0x%08x VERS:0x%08x\n", + ept, ept->restart_state, + be32_to_cpu(ept->dst_prog), + be32_to_cpu(ept->dst_vers)); + spin_lock(&ept->restart_lock); + ept->restart_state = RESTART_PEND_NTFY_SVR; + + /* remove incomplete packets */ + spin_lock(&ept->incomplete_lock); + list_for_each_entry_safe(pkt, tmp_pkt, + &ept->incomplete, list) { + list_del(&pkt->list); + frag = pkt->first; + while (frag != NULL) { + next = frag->next; + kfree(frag); + frag = next; + } + kfree(pkt); + } + spin_unlock(&ept->incomplete_lock); + + /* remove all completed packets waiting to be read */ + spin_lock(&ept->read_q_lock); + list_for_each_entry_safe(pkt, tmp_pkt, &ept->read_q, + list) { + list_del(&pkt->list); + frag = pkt->first; + while (frag != NULL) { + next = frag->next; + kfree(frag); + frag = next; + } + kfree(pkt); + } + spin_unlock(&ept->read_q_lock); + + spin_unlock(&ept->restart_lock); + wake_up(&ept->wait_q); + } + + spin_unlock_irqrestore(&local_endpoints_lock, flags); + + /* Unblock endpoints waiting for quota ack*/ + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(r_ept, &remote_endpoints, list) { + spin_lock(&r_ept->quota_lock); + r_ept->quota_restart_state = RESTART_QUOTA_ABORT; + RR("Set STATE_PENDING PID:0x%08x CID:0x%08x \n", r_ept->pid, + r_ept->cid); + spin_unlock(&r_ept->quota_lock); + wake_up(&r_ept->quota_wait); + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); +} + +static void modem_reset_startup(struct rpcrouter_xprt_info *xprt_info) +{ + struct msm_rpc_endpoint *ept; + unsigned long flags; + + spin_lock_irqsave(&local_endpoints_lock, flags); + + /* notify all endpoints that we are coming back up */ + list_for_each_entry(ept, &local_endpoints, list) { + RR("%s EPT DST PID %x, remote_pid:%d\n", __func__, + ept->dst_pid, xprt_info->remote_pid); + + if (xprt_info->remote_pid != ept->dst_pid) + continue; + + D("calling setup cb %d:%p\n", ept->do_setup_notif, + ept->cb_restart_setup); + if (ept->do_setup_notif && ept->cb_restart_setup) + ept->cb_restart_setup(ept->client_data); + ept->do_setup_notif = 0; + } + + spin_unlock_irqrestore(&local_endpoints_lock, flags); +} + +/* + * Blocks and waits for endpoint if a reset is in progress. + * + * @returns + * ENETRESET Reset is in progress and a notification needed + * ERESTARTSYS Signal occurred + * 0 Reset is not in progress + */ +static int wait_for_restart_and_notify(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + int ret = 0; + DEFINE_WAIT(__wait); + + for (;;) { + prepare_to_wait(&ept->restart_wait, &__wait, + TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&ept->restart_lock, flags); + if (ept->restart_state == RESTART_NORMAL) { + spin_unlock_irqrestore(&ept->restart_lock, flags); + break; + } else if (ept->restart_state & RESTART_PEND_NTFY) { + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock_irqrestore(&ept->restart_lock, flags); + ret = -ENETRESET; + break; + } + if (signal_pending(current) && + ((!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))) { + spin_unlock_irqrestore(&ept->restart_lock, flags); + ret = -ERESTARTSYS; + break; + } + spin_unlock_irqrestore(&ept->restart_lock, flags); + schedule(); + } + finish_wait(&ept->restart_wait, &__wait); + return ret; +} + +static struct rr_server *rpcrouter_create_server(uint32_t pid, + uint32_t cid, + uint32_t prog, + uint32_t ver) +{ + struct rr_server *server; + unsigned long flags; + int rc; + + server = kmalloc(sizeof(struct rr_server), GFP_KERNEL); + if (!server) + return ERR_PTR(-ENOMEM); + + memset(server, 0, sizeof(struct rr_server)); + server->pid = pid; + server->cid = cid; + server->prog = prog; + server->vers = ver; + + spin_lock_irqsave(&server_list_lock, flags); + list_add_tail(&server->list, &server_list); + spin_unlock_irqrestore(&server_list_lock, flags); + + rc = msm_rpcrouter_create_server_cdev(server); + if (rc < 0) + goto out_fail; + + return server; +out_fail: + spin_lock_irqsave(&server_list_lock, flags); + list_del(&server->list); + spin_unlock_irqrestore(&server_list_lock, flags); + kfree(server); + return ERR_PTR(rc); +} + +static void rpcrouter_destroy_server(struct rr_server *server) +{ + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_del(&server->list); + spin_unlock_irqrestore(&server_list_lock, flags); + device_destroy(msm_rpcrouter_class, server->device_number); + kfree(server); +} + +int msm_rpc_add_board_dev(struct rpc_board_dev *devices, int num) +{ + unsigned long flags; + struct rpc_board_dev_info *board_info; + int i; + + for (i = 0; i < num; i++) { + board_info = kzalloc(sizeof(struct rpc_board_dev_info), + GFP_KERNEL); + if (!board_info) + return -ENOMEM; + + board_info->dev = &devices[i]; + D("%s: adding program %x\n", __func__, board_info->dev->prog); + spin_lock_irqsave(&rpc_board_dev_list_lock, flags); + list_add_tail(&board_info->list, &rpc_board_dev_list); + spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags); + } + + return 0; +} +EXPORT_SYMBOL(msm_rpc_add_board_dev); + +static void rpcrouter_register_board_dev(struct rr_server *server) +{ + struct rpc_board_dev_info *board_info; + unsigned long flags; + int rc; + + spin_lock_irqsave(&rpc_board_dev_list_lock, flags); + list_for_each_entry(board_info, &rpc_board_dev_list, list) { + if (server->prog == board_info->dev->prog) { + D("%s: registering device %x\n", + __func__, board_info->dev->prog); + list_del(&board_info->list); + rc = platform_device_register(&board_info->dev->pdev); + if (rc) + pr_err("%s: board dev register failed %d\n", + __func__, rc); + kfree(board_info); + break; + } + } + spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags); +} + +static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver) +{ + struct rr_server *server; + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->prog == prog + && server->vers == ver) { + spin_unlock_irqrestore(&server_list_lock, flags); + return server; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + +static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev) +{ + struct rr_server *server; + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->device_number == dev) { + spin_unlock_irqrestore(&server_list_lock, flags); + return server; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + +struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) +{ + struct msm_rpc_endpoint *ept; + unsigned long flags; + + ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL); + if (!ept) + return NULL; + memset(ept, 0, sizeof(struct msm_rpc_endpoint)); + ept->cid = (uint32_t) ept; + ept->pid = RPCROUTER_PID_LOCAL; + ept->dev = dev; + + if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) { + struct rr_server *srv; + /* + * This is a userspace client which opened + * a program/ver devicenode. Bind the client + * to that destination + */ + srv = rpcrouter_lookup_server_by_dev(dev); + /* TODO: bug? really? */ + BUG_ON(!srv); + + ept->dst_pid = srv->pid; + ept->dst_cid = srv->cid; + ept->dst_prog = cpu_to_be32(srv->prog); + ept->dst_vers = cpu_to_be32(srv->vers); + } else { + /* mark not connected */ + ept->dst_pid = 0xffffffff; + } + + init_waitqueue_head(&ept->wait_q); + INIT_LIST_HEAD(&ept->read_q); + spin_lock_init(&ept->read_q_lock); + INIT_LIST_HEAD(&ept->reply_avail_q); + INIT_LIST_HEAD(&ept->reply_pend_q); + spin_lock_init(&ept->reply_q_lock); + spin_lock_init(&ept->restart_lock); + init_waitqueue_head(&ept->restart_wait); + ept->restart_state = RESTART_NORMAL; + wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read"); + wake_lock_init(&ept->reply_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_reply"); + INIT_LIST_HEAD(&ept->incomplete); + spin_lock_init(&ept->incomplete_lock); + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_add_tail(&ept->list, &local_endpoints); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return ept; +} + +int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept) +{ + int rc; + union rr_control_msg msg; + struct msm_rpc_reply *reply, *reply_tmp; + unsigned long flags; + struct rpcrouter_xprt_info *xprt_info; + + /* Endpoint with dst_pid = 0xffffffff corresponds to that of + ** router port. So don't send a REMOVE CLIENT message while + ** destroying it.*/ + spin_lock_irqsave(&local_endpoints_lock, flags); + list_del(&ept->list); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + if (ept->dst_pid != 0xffffffff) { + msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT; + msg.cli.pid = ept->pid; + msg.cli.cid = ept->cid; + + RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid); + mutex_lock(&xprt_info_list_lock); + list_for_each_entry(xprt_info, &xprt_info_list, list) { + rc = rpcrouter_send_control_msg(xprt_info, &msg); + if (rc < 0) { + mutex_unlock(&xprt_info_list_lock); + return rc; + } + } + mutex_unlock(&xprt_info_list_lock); + } + + /* Free replies */ + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_for_each_entry_safe(reply, reply_tmp, &ept->reply_pend_q, list) { + list_del(&reply->list); + kfree(reply); + } + list_for_each_entry_safe(reply, reply_tmp, &ept->reply_avail_q, list) { + list_del(&reply->list); + kfree(reply); + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + + wake_lock_destroy(&ept->read_q_wake_lock); + wake_lock_destroy(&ept->reply_q_wake_lock); + kfree(ept); + return 0; +} + +static int rpcrouter_create_remote_endpoint(uint32_t pid, uint32_t cid) +{ + struct rr_remote_endpoint *new_c; + unsigned long flags; + + new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL); + if (!new_c) + return -ENOMEM; + memset(new_c, 0, sizeof(struct rr_remote_endpoint)); + + new_c->cid = cid; + new_c->pid = pid; + init_waitqueue_head(&new_c->quota_wait); + spin_lock_init(&new_c->quota_lock); + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_add_tail(&new_c->list, &remote_endpoints); + new_c->quota_restart_state = RESTART_NORMAL; + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return 0; +} + +static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid) +{ + struct msm_rpc_endpoint *ept; + + list_for_each_entry(ept, &local_endpoints, list) { + if (ept->cid == cid) + return ept; + } + return NULL; +} + +static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t pid, + uint32_t cid) +{ + struct rr_remote_endpoint *ept; + unsigned long flags; + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(ept, &remote_endpoints, list) { + if ((ept->pid == pid) && (ept->cid == cid)) { + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return ept; + } + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return NULL; +} + +static void handle_server_restart(struct rr_server *server, + uint32_t pid, uint32_t cid, + uint32_t prog, uint32_t vers) +{ + struct rr_remote_endpoint *r_ept; + struct msm_rpc_endpoint *ept; + unsigned long flags; + r_ept = rpcrouter_lookup_remote_endpoint(pid, cid); + if (r_ept && (r_ept->quota_restart_state != + RESTART_NORMAL)) { + spin_lock_irqsave(&r_ept->quota_lock, flags); + r_ept->tx_quota_cntr = 0; + r_ept->quota_restart_state = + RESTART_NORMAL; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + D(KERN_INFO "rpcrouter: Remote EPT Reset %0x\n", + (unsigned int)r_ept); + wake_up(&r_ept->quota_wait); + } + spin_lock_irqsave(&local_endpoints_lock, flags); + list_for_each_entry(ept, &local_endpoints, list) { + if ((be32_to_cpu(ept->dst_prog) == prog) && + (be32_to_cpu(ept->dst_vers) == vers) && + (ept->restart_state & RESTART_PEND_SVR)) { + spin_lock(&ept->restart_lock); + ept->restart_state &= ~RESTART_PEND_SVR; + spin_unlock(&ept->restart_lock); + D("rpcrouter: Local EPT Reset %08x:%08x \n", + prog, vers); + wake_up(&ept->restart_wait); + wake_up(&ept->wait_q); + } + } + spin_unlock_irqrestore(&local_endpoints_lock, flags); +} + +static int process_control_msg(struct rpcrouter_xprt_info *xprt_info, + union rr_control_msg *msg, int len) +{ + union rr_control_msg ctl; + struct rr_server *server; + struct rr_remote_endpoint *r_ept; + int rc = 0; + unsigned long flags; + static int first = 1; + + if (len != sizeof(*msg)) { + RR(KERN_ERR "rpcrouter: r2r msg size %d != %d\n", + len, sizeof(*msg)); + return -EINVAL; + } + + switch (msg->cmd) { + case RPCROUTER_CTRL_CMD_HELLO: + RR("o HELLO PID %d\n", xprt_info->remote_pid); + memset(&ctl, 0, sizeof(ctl)); + ctl.cmd = RPCROUTER_CTRL_CMD_HELLO; + rpcrouter_send_control_msg(xprt_info, &ctl); + + xprt_info->initialized = 1; + + /* Send list of servers one at a time */ + ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; + + /* TODO: long time to hold a spinlock... */ + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->pid != RPCROUTER_PID_LOCAL) + continue; + ctl.srv.pid = server->pid; + ctl.srv.cid = server->cid; + ctl.srv.prog = server->prog; + ctl.srv.vers = server->vers; + + RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", + server->pid, server->cid, + server->prog, server->vers); + + rpcrouter_send_control_msg(xprt_info, &ctl); + } + spin_unlock_irqrestore(&server_list_lock, flags); + + if (first) { + first = 0; + queue_work(rpcrouter_workqueue, + &work_create_rpcrouter_pdev); + } + break; + + case RPCROUTER_CTRL_CMD_RESUME_TX: + RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid); + + r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.pid, + msg->cli.cid); + if (!r_ept) { + printk(KERN_ERR + "rpcrouter: Unable to resume client\n"); + break; + } + spin_lock_irqsave(&r_ept->quota_lock, flags); + r_ept->tx_quota_cntr = 0; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + wake_up(&r_ept->quota_wait); + break; + + case RPCROUTER_CTRL_CMD_NEW_SERVER: + if (msg->srv.vers == 0) { + pr_err( + "rpcrouter: Server create rejected, version = 0, " + "program = %08x\n", msg->srv.prog); + break; + } + + RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n", + msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); + + server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); + + if (!server) { + server = rpcrouter_create_server( + msg->srv.pid, msg->srv.cid, + msg->srv.prog, msg->srv.vers); + if (!server) + return -ENOMEM; + /* + * XXX: Verify that its okay to add the + * client to our remote client list + * if we get a NEW_SERVER notification + */ + if (!rpcrouter_lookup_remote_endpoint(msg->srv.pid, + msg->srv.cid)) { + rc = rpcrouter_create_remote_endpoint( + msg->srv.pid, msg->srv.cid); + if (rc < 0) + printk(KERN_ERR + "rpcrouter:Client create" + "error (%d)\n", rc); + } + rpcrouter_register_board_dev(server); + schedule_work(&work_create_pdevs); + wake_up(&newserver_wait); + } else { + if ((server->pid == msg->srv.pid) && + (server->cid == msg->srv.cid)) { + handle_server_restart(server, + msg->srv.pid, + msg->srv.cid, + msg->srv.prog, + msg->srv.vers); + } else { + server->pid = msg->srv.pid; + server->cid = msg->srv.cid; + } + } + break; + + case RPCROUTER_CTRL_CMD_REMOVE_SERVER: + RR("o REMOVE_SERVER prog=%08x:%d\n", + msg->srv.prog, msg->srv.vers); + server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); + if (server) + rpcrouter_destroy_server(server); + break; + + case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: + RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid); + if (msg->cli.pid == RPCROUTER_PID_LOCAL) { + printk(KERN_ERR + "rpcrouter: Denying remote removal of " + "local client\n"); + break; + } + r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.pid, + msg->cli.cid); + if (r_ept) { + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_del(&r_ept->list); + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + kfree(r_ept); + } + + /* Notify local clients of this event */ + printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n"); + rc = -ENOSYS; + + break; + case RPCROUTER_CTRL_CMD_PING: + /* No action needed for ping messages received */ + RR("o PING\n"); + break; + default: + RR("o UNKNOWN(%08x)\n", msg->cmd); + rc = -ENOSYS; + } + + return rc; +} + +static void do_create_rpcrouter_pdev(struct work_struct *work) +{ + D("%s: modem rpc router up\n", __func__); + platform_device_register(&rpcrouter_pdev); + complete_all(&rpc_remote_router_up); +} + +static void do_create_pdevs(struct work_struct *work) +{ + unsigned long flags; + struct rr_server *server; + + /* TODO: race if destroyed while being registered */ + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->pid != RPCROUTER_PID_LOCAL) { + if (server->pdev_name[0] == 0) { + sprintf(server->pdev_name, "rs%.8x", + server->prog); + spin_unlock_irqrestore(&server_list_lock, + flags); + msm_rpcrouter_create_server_pdev(server); + schedule_work(&work_create_pdevs); + return; + } + } + } + spin_unlock_irqrestore(&server_list_lock, flags); +} + +static void *rr_malloc(unsigned sz) +{ + void *ptr = kmalloc(sz, GFP_KERNEL); + if (ptr) + return ptr; + + printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz); + do { + ptr = kmalloc(sz, GFP_KERNEL); + } while (!ptr); + + return ptr; +} + +static int rr_read(struct rpcrouter_xprt_info *xprt_info, + void *data, uint32_t len) +{ + int rc; + unsigned long flags; + + while (!xprt_info->abort_data_read) { + spin_lock_irqsave(&xprt_info->lock, flags); + if (xprt_info->xprt->read_avail() >= len) { + rc = xprt_info->xprt->read(data, len); + spin_unlock_irqrestore(&xprt_info->lock, flags); + if (rc == len && !xprt_info->abort_data_read) + return 0; + else + return -EIO; + } + xprt_info->need_len = len; + wake_unlock(&xprt_info->wakelock); + spin_unlock_irqrestore(&xprt_info->lock, flags); + + wait_event(xprt_info->read_wait, + xprt_info->xprt->read_avail() >= len + || xprt_info->abort_data_read); + } + return -EIO; +} + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) +static char *type_to_str(int i) +{ + switch (i) { + case RPCROUTER_CTRL_CMD_DATA: + return "data "; + case RPCROUTER_CTRL_CMD_HELLO: + return "hello "; + case RPCROUTER_CTRL_CMD_BYE: + return "bye "; + case RPCROUTER_CTRL_CMD_NEW_SERVER: + return "new_srvr"; + case RPCROUTER_CTRL_CMD_REMOVE_SERVER: + return "rmv_srvr"; + case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: + return "rmv_clnt"; + case RPCROUTER_CTRL_CMD_RESUME_TX: + return "resum_tx"; + case RPCROUTER_CTRL_CMD_EXIT: + return "cmd_exit"; + default: + return "invalid"; + } +} +#endif + +static void do_read_data(struct work_struct *work) +{ + struct rr_header hdr; + struct rr_packet *pkt; + struct rr_fragment *frag; + struct msm_rpc_endpoint *ept; +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + struct rpc_request_hdr *rq; +#endif + uint32_t pm, mid; + unsigned long flags; + + struct rpcrouter_xprt_info *xprt_info = + container_of(work, + struct rpcrouter_xprt_info, + read_data); + + if (rr_read(xprt_info, &hdr, sizeof(hdr))) + goto fail_io; + + RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n", + hdr.version, hdr.type, hdr.src_pid, hdr.src_cid, + hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); + RAW_HDR("[r rr_h] " + "ver=%i,type=%s,src_pid=%08x,src_cid=%08x," + "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", + hdr.version, type_to_str(hdr.type), hdr.src_pid, hdr.src_cid, + hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); + + if (hdr.version != RPCROUTER_VERSION) { + DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION); + goto fail_data; + } + if (hdr.size > RPCROUTER_MSGSIZE_MAX) { + DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX); + goto fail_data; + } + + if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) { + if (xprt_info->remote_pid == -1) { + xprt_info->remote_pid = hdr.src_pid; + + /* do restart notification */ + modem_reset_startup(xprt_info); + } + + if (rr_read(xprt_info, xprt_info->r2r_buf, hdr.size)) + goto fail_io; + process_control_msg(xprt_info, + (void *) xprt_info->r2r_buf, hdr.size); + goto done; + } + + if (hdr.size < sizeof(pm)) { + DIAG("runt packet (no pacmark)\n"); + goto fail_data; + } + if (rr_read(xprt_info, &pm, sizeof(pm))) + goto fail_io; + + hdr.size -= sizeof(pm); + + frag = rr_malloc(sizeof(*frag)); + frag->next = NULL; + frag->length = hdr.size; + if (rr_read(xprt_info, frag->data, hdr.size)) { + kfree(frag); + goto fail_io; + } + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if ((smd_rpcrouter_debug_mask & RAW_PMR) && + ((pm >> 30 & 0x1) || (pm >> 31 & 0x1))) { + uint32_t xid = 0; + if (pm >> 30 & 0x1) { + rq = (struct rpc_request_hdr *) frag->data; + xid = ntohl(rq->xid); + } + if ((pm >> 31 & 0x1) || (pm >> 30 & 0x1)) + RAW_PMR_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i," + "len=%3i,dst_cid=%08x\n", + xid, + pm >> 30 & 0x1, + pm >> 31 & 0x1, + pm >> 16 & 0xFF, + pm & 0xFFFF, hdr.dst_cid); + } + + if (smd_rpcrouter_debug_mask & SMEM_LOG) { + rq = (struct rpc_request_hdr *) frag->data; + if (rq->xid == 0) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MID_READ, + PACMARK_MID(pm), + hdr.dst_cid, + hdr.src_cid); + else + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_READ, + ntohl(rq->xid), + hdr.dst_cid, + hdr.src_cid); + } +#endif + + spin_lock_irqsave(&local_endpoints_lock, flags); + ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); + if (!ept) { + spin_unlock_irqrestore(&local_endpoints_lock, flags); + DIAG("no local ept for cid %08x\n", hdr.dst_cid); + kfree(frag); + goto done; + } + + /* See if there is already a partial packet that matches our mid + * and if so, append this fragment to that packet. + */ + mid = PACMARK_MID(pm); + spin_lock(&ept->incomplete_lock); + list_for_each_entry(pkt, &ept->incomplete, list) { + if (pkt->mid == mid) { + pkt->last->next = frag; + pkt->last = frag; + pkt->length += frag->length; + if (PACMARK_LAST(pm)) { + list_del(&pkt->list); + spin_unlock(&ept->incomplete_lock); + goto packet_complete; + } + spin_unlock(&ept->incomplete_lock); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + goto done; + } + } + spin_unlock(&ept->incomplete_lock); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + /* This mid is new -- create a packet for it, and put it on + * the incomplete list if this fragment is not a last fragment, + * otherwise put it on the read queue. + */ + pkt = rr_malloc(sizeof(struct rr_packet)); + pkt->first = frag; + pkt->last = frag; + memcpy(&pkt->hdr, &hdr, sizeof(hdr)); + pkt->mid = mid; + pkt->length = frag->length; + + spin_lock_irqsave(&local_endpoints_lock, flags); + ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); + if (!ept) { + spin_unlock_irqrestore(&local_endpoints_lock, flags); + DIAG("no local ept for cid %08x\n", hdr.dst_cid); + kfree(frag); + kfree(pkt); + goto done; + } + if (!PACMARK_LAST(pm)) { + spin_lock(&ept->incomplete_lock); + list_add_tail(&pkt->list, &ept->incomplete); + spin_unlock(&ept->incomplete_lock); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + goto done; + } + +packet_complete: + spin_lock(&ept->read_q_lock); + D("%s: take read lock on ept %p\n", __func__, ept); + wake_lock(&ept->read_q_wake_lock); + list_add_tail(&pkt->list, &ept->read_q); + wake_up(&ept->wait_q); + spin_unlock(&ept->read_q_lock); + spin_unlock_irqrestore(&local_endpoints_lock, flags); +done: + + if (hdr.confirm_rx) { + union rr_control_msg msg; + + msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX; + msg.cli.pid = hdr.dst_pid; + msg.cli.cid = hdr.dst_cid; + + RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid); + rpcrouter_send_control_msg(xprt_info, &msg); + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if (smd_rpcrouter_debug_mask & SMEM_LOG) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT, + RPCROUTER_PID_LOCAL, + hdr.dst_cid, + hdr.src_cid); +#endif + + } + + /* don't requeue if we should be shutting down */ + if (!xprt_info->abort_data_read) { + queue_work(xprt_info->workqueue, &xprt_info->read_data); + return; + } + + D("rpc_router terminating for '%s'\n", + xprt_info->xprt->name); + +fail_io: +fail_data: + D(KERN_ERR "rpc_router has died for '%s'\n", + xprt_info->xprt->name); +} + +void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog, + uint32_t vers, uint32_t proc) +{ + memset(hdr, 0, sizeof(struct rpc_request_hdr)); + hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); + hdr->rpc_vers = cpu_to_be32(2); + hdr->prog = cpu_to_be32(prog); + hdr->vers = cpu_to_be32(vers); + hdr->procedure = cpu_to_be32(proc); +} +EXPORT_SYMBOL(msm_rpc_setup_req); + +struct msm_rpc_endpoint *msm_rpc_open(void) +{ + struct msm_rpc_endpoint *ept; + + ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0)); + if (ept == NULL) + return ERR_PTR(-ENOMEM); + + return ept; +} + +void msm_rpc_read_wakeup(struct msm_rpc_endpoint *ept) +{ + ept->forced_wakeup = 1; + wake_up(&ept->wait_q); +} + +int msm_rpc_close(struct msm_rpc_endpoint *ept) +{ + if (!ept) + return -EINVAL; + return msm_rpcrouter_destroy_local_endpoint(ept); +} +EXPORT_SYMBOL(msm_rpc_close); + +static int msm_rpc_write_pkt( + struct rr_header *hdr, + struct msm_rpc_endpoint *ept, + struct rr_remote_endpoint *r_ept, + void *buffer, + int count, + int first, + int last, + uint32_t mid + ) +{ +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + struct rpc_request_hdr *rq = buffer; + uint32_t event_id; +#endif + uint32_t pacmark; + unsigned long flags = 0; + int rc; + struct rpcrouter_xprt_info *xprt_info; + int needed; + + DEFINE_WAIT(__wait); + + /* Create routing header */ + hdr->type = RPCROUTER_CTRL_CMD_DATA; + hdr->version = RPCROUTER_VERSION; + hdr->src_pid = ept->pid; + hdr->src_cid = ept->cid; + hdr->confirm_rx = 0; + hdr->size = count + sizeof(uint32_t); + + rc = wait_for_restart_and_notify(ept); + if (rc) + return rc; + + if (r_ept) { + for (;;) { + prepare_to_wait(&r_ept->quota_wait, &__wait, + TASK_INTERRUPTIBLE); + spin_lock_irqsave(&r_ept->quota_lock, flags); + if ((r_ept->tx_quota_cntr < + RPCROUTER_DEFAULT_RX_QUOTA) || + (r_ept->quota_restart_state != RESTART_NORMAL)) + break; + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) + break; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + schedule(); + } + finish_wait(&r_ept->quota_wait, &__wait); + + if (r_ept->quota_restart_state != RESTART_NORMAL) { + spin_lock(&ept->restart_lock); + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + return -ENETRESET; + } + + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + return -ERESTARTSYS; + } + r_ept->tx_quota_cntr++; + if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) { + hdr->confirm_rx = 1; + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if (smd_rpcrouter_debug_mask & SMEM_LOG) { + event_id = (rq->xid == 0) ? + RPC_ROUTER_LOG_EVENT_MID_CFM_REQ : + RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ; + + smem_log_event(SMEM_LOG_PROC_ID_APPS | event_id, + hdr->dst_pid, + hdr->dst_cid, + hdr->src_cid); + } +#endif + + } + } + pacmark = PACMARK(count, mid, first, last); + + if (r_ept) + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + + mutex_lock(&xprt_info_list_lock); + xprt_info = rpcrouter_get_xprt_info(hdr->dst_pid); + if (!xprt_info) { + mutex_unlock(&xprt_info_list_lock); + return -ENETRESET; + } + spin_lock_irqsave(&xprt_info->lock, flags); + mutex_unlock(&xprt_info_list_lock); + spin_lock(&ept->restart_lock); + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&xprt_info->lock, flags); + return -ENETRESET; + } + + needed = sizeof(*hdr) + hdr->size; + while ((ept->restart_state == RESTART_NORMAL) && + (xprt_info->xprt->write_avail() < needed)) { + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&xprt_info->lock, flags); + msleep(250); + + /* refresh xprt pointer to ensure that it hasn't + * been deleted since our last retrieval */ + mutex_lock(&xprt_info_list_lock); + xprt_info = rpcrouter_get_xprt_info(hdr->dst_pid); + if (!xprt_info) { + mutex_unlock(&xprt_info_list_lock); + return -ENETRESET; + } + spin_lock_irqsave(&xprt_info->lock, flags); + mutex_unlock(&xprt_info_list_lock); + spin_lock(&ept->restart_lock); + } + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&xprt_info->lock, flags); + return -ENETRESET; + } + + /* TODO: deal with full fifo */ + xprt_info->xprt->write(hdr, sizeof(*hdr), HEADER); + RAW_HDR("[w rr_h] " + "ver=%i,type=%s,src_pid=%08x,src_cid=%08x," + "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", + hdr->version, type_to_str(hdr->type), + hdr->src_pid, hdr->src_cid, + hdr->confirm_rx, hdr->size, hdr->dst_pid, hdr->dst_cid); + xprt_info->xprt->write(&pacmark, sizeof(pacmark), PACKMARK); + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if ((smd_rpcrouter_debug_mask & RAW_PMW) && + ((pacmark >> 30 & 0x1) || (pacmark >> 31 & 0x1))) { + uint32_t xid = 0; + if (pacmark >> 30 & 0x1) + xid = ntohl(rq->xid); + if ((pacmark >> 31 & 0x1) || (pacmark >> 30 & 0x1)) + RAW_PMW_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i," + "len=%3i,src_cid=%x\n", + xid, + pacmark >> 30 & 0x1, + pacmark >> 31 & 0x1, + pacmark >> 16 & 0xFF, + pacmark & 0xFFFF, hdr->src_cid); + } +#endif + + xprt_info->xprt->write(buffer, count, PAYLOAD); + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&xprt_info->lock, flags); + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if (smd_rpcrouter_debug_mask & SMEM_LOG) { + if (rq->xid == 0) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MID_WRITTEN, + PACMARK_MID(pacmark), + hdr->dst_cid, + hdr->src_cid); + else + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_WRITTEN, + ntohl(rq->xid), + hdr->dst_cid, + hdr->src_cid); + } +#endif + + return needed; +} + +static struct msm_rpc_reply *get_pend_reply(struct msm_rpc_endpoint *ept, + uint32_t xid) +{ + unsigned long flags; + struct msm_rpc_reply *reply; + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_for_each_entry(reply, &ept->reply_pend_q, list) { + if (reply->xid == xid) { + list_del(&reply->list); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return reply; + } + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return NULL; +} + +void get_requesting_client(struct msm_rpc_endpoint *ept, uint32_t xid, + struct msm_rpc_client_info *clnt_info) +{ + unsigned long flags; + struct msm_rpc_reply *reply; + + if (!clnt_info) + return; + + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_for_each_entry(reply, &ept->reply_pend_q, list) { + if (reply->xid == xid) { + clnt_info->pid = reply->pid; + clnt_info->cid = reply->cid; + clnt_info->prog = reply->prog; + clnt_info->vers = reply->vers; + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return; + } + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return; +} + +static void set_avail_reply(struct msm_rpc_endpoint *ept, + struct msm_rpc_reply *reply) +{ + unsigned long flags; + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_add_tail(&reply->list, &ept->reply_avail_q); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); +} + +static struct msm_rpc_reply *get_avail_reply(struct msm_rpc_endpoint *ept) +{ + struct msm_rpc_reply *reply; + unsigned long flags; + if (list_empty(&ept->reply_avail_q)) { + if (ept->reply_cnt >= RPCROUTER_PEND_REPLIES_MAX) { + printk(KERN_ERR + "exceeding max replies of %d \n", + RPCROUTER_PEND_REPLIES_MAX); + return 0; + } + reply = kmalloc(sizeof(struct msm_rpc_reply), GFP_KERNEL); + if (!reply) + return 0; + D("Adding reply 0x%08x \n", (unsigned int)reply); + memset(reply, 0, sizeof(struct msm_rpc_reply)); + spin_lock_irqsave(&ept->reply_q_lock, flags); + ept->reply_cnt++; + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + } else { + spin_lock_irqsave(&ept->reply_q_lock, flags); + reply = list_first_entry(&ept->reply_avail_q, + struct msm_rpc_reply, + list); + list_del(&reply->list); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + } + return reply; +} + +static void set_pend_reply(struct msm_rpc_endpoint *ept, + struct msm_rpc_reply *reply) +{ + unsigned long flags; + spin_lock_irqsave(&ept->reply_q_lock, flags); + D("%s: take reply lock on ept %p\n", __func__, ept); + wake_lock(&ept->reply_q_wake_lock); + list_add_tail(&reply->list, &ept->reply_pend_q); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); +} + +int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) +{ + struct rr_header hdr; + struct rpc_request_hdr *rq = buffer; + struct rr_remote_endpoint *r_ept; + struct msm_rpc_reply *reply = NULL; + int max_tx; + int tx_cnt; + char *tx_buf; + int rc; + int first_pkt = 1; + uint32_t mid; + unsigned long flags; + + /* snoop the RPC packet and enforce permissions */ + + /* has to have at least the xid and type fields */ + if (count < (sizeof(uint32_t) * 2)) { + printk(KERN_ERR "rr_write: rejecting runt packet\n"); + return -EINVAL; + } + + if (rq->type == 0) { + /* RPC CALL */ + if (count < (sizeof(uint32_t) * 6)) { + printk(KERN_ERR + "rr_write: rejecting runt call packet\n"); + return -EINVAL; + } + if (ept->dst_pid == 0xffffffff) { + printk(KERN_ERR "rr_write: not connected\n"); + return -ENOTCONN; + } + if ((ept->dst_prog != rq->prog) || + ((be32_to_cpu(ept->dst_vers) & 0x0fff0000) != + (be32_to_cpu(rq->vers) & 0x0fff0000))) { + printk(KERN_ERR + "rr_write: cannot write to %08x:%08x " + "(bound to %08x:%08x)\n", + be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + be32_to_cpu(ept->dst_prog), + be32_to_cpu(ept->dst_vers)); + return -EINVAL; + } + hdr.dst_pid = ept->dst_pid; + hdr.dst_cid = ept->dst_cid; + IO("CALL to %08x:%d @ %d:%08x (%d bytes)\n", + be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + ept->dst_pid, ept->dst_cid, count); + } else { + /* RPC REPLY */ + reply = get_pend_reply(ept, rq->xid); + if (!reply) { + printk(KERN_ERR + "rr_write: rejecting, reply not found \n"); + return -EINVAL; + } + hdr.dst_pid = reply->pid; + hdr.dst_cid = reply->cid; + IO("REPLY to xid=%d @ %d:%08x (%d bytes)\n", + be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); + } + + r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_pid, hdr.dst_cid); + + if ((!r_ept) && (hdr.dst_pid != RPCROUTER_PID_LOCAL)) { + printk(KERN_ERR + "msm_rpc_write(): No route to ept " + "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid); + count = -EHOSTUNREACH; + goto write_release_lock; + } + + tx_cnt = count; + tx_buf = buffer; + mid = atomic_add_return(1, &pm_mid) & 0xFF; + /* The modem's router can only take 500 bytes of data. The + first 8 bytes it uses on the modem side for addressing, + the next 4 bytes are for the pacmark header. */ + max_tx = RPCROUTER_MSGSIZE_MAX - 8 - sizeof(uint32_t); + IO("Writing %d bytes, max pkt size is %d\n", + tx_cnt, max_tx); + while (tx_cnt > 0) { + if (tx_cnt > max_tx) { + rc = msm_rpc_write_pkt(&hdr, ept, r_ept, + tx_buf, max_tx, + first_pkt, 0, mid); + if (rc < 0) { + count = rc; + goto write_release_lock; + } + IO("Wrote %d bytes First %d, Last 0 mid %d\n", + rc, first_pkt, mid); + tx_cnt -= max_tx; + tx_buf += max_tx; + } else { + rc = msm_rpc_write_pkt(&hdr, ept, r_ept, + tx_buf, tx_cnt, + first_pkt, 1, mid); + if (rc < 0) { + count = rc; + goto write_release_lock; + } + IO("Wrote %d bytes First %d Last 1 mid %d\n", + rc, first_pkt, mid); + break; + } + first_pkt = 0; + } + + write_release_lock: + /* if reply, release wakelock after writing to the transport */ + if (rq->type != 0) { + /* Upon failure, add reply tag to the pending list. + ** Else add reply tag to the avail/free list. */ + if (count < 0) + set_pend_reply(ept, reply); + else + set_avail_reply(ept, reply); + + spin_lock_irqsave(&ept->reply_q_lock, flags); + if (list_empty(&ept->reply_pend_q)) { + D("%s: release reply lock on ept %p\n", __func__, ept); + wake_unlock(&ept->reply_q_wake_lock); + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + } + + return count; +} +EXPORT_SYMBOL(msm_rpc_write); + +/* + * NOTE: It is the responsibility of the caller to kfree buffer + */ +int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer, + unsigned user_len, long timeout) +{ + struct rr_fragment *frag, *next; + char *buf; + int rc; + + rc = __msm_rpc_read(ept, &frag, user_len, timeout); + if (rc <= 0) + return rc; + + /* single-fragment messages conveniently can be + * returned as-is (the buffer is at the front) + */ + if (frag->next == 0) { + *buffer = (void*) frag; + return rc; + } + + /* multi-fragment messages, we have to do it the + * hard way, which is rather disgusting right now + */ + buf = rr_malloc(rc); + *buffer = buf; + + while (frag != NULL) { + memcpy(buf, frag->data, frag->length); + next = frag->next; + buf += frag->length; + kfree(frag); + frag = next; + } + + return rc; +} +EXPORT_SYMBOL(msm_rpc_read); + +int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, + void *_request, int request_size, + long timeout) +{ + return msm_rpc_call_reply(ept, proc, + _request, request_size, + NULL, 0, timeout); +} +EXPORT_SYMBOL(msm_rpc_call); + +int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, + void *_request, int request_size, + void *_reply, int reply_size, + long timeout) +{ + struct rpc_request_hdr *req = _request; + struct rpc_reply_hdr *reply; + int rc; + + if (request_size < sizeof(*req)) + return -ETOOSMALL; + + if (ept->dst_pid == 0xffffffff) + return -ENOTCONN; + + memset(req, 0, sizeof(*req)); + req->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); + req->rpc_vers = cpu_to_be32(2); + req->prog = ept->dst_prog; + req->vers = ept->dst_vers; + req->procedure = cpu_to_be32(proc); + + rc = msm_rpc_write(ept, req, request_size); + if (rc < 0) + return rc; + + for (;;) { + rc = msm_rpc_read(ept, (void*) &reply, -1, timeout); + if (rc < 0) + return rc; + if (rc < (3 * sizeof(uint32_t))) { + rc = -EIO; + break; + } + /* we should not get CALL packets -- ignore them */ + if (reply->type == 0) { + kfree(reply); + continue; + } + /* If an earlier call timed out, we could get the (no + * longer wanted) reply for it. Ignore replies that + * we don't expect + */ + if (reply->xid != req->xid) { + kfree(reply); + continue; + } + if (reply->reply_stat != 0) { + rc = -EPERM; + break; + } + if (reply->data.acc_hdr.accept_stat != 0) { + rc = -EINVAL; + break; + } + if (_reply == NULL) { + rc = 0; + break; + } + if (rc > reply_size) { + rc = -ENOMEM; + } else { + memcpy(_reply, reply, rc); + } + break; + } + kfree(reply); + return rc; +} +EXPORT_SYMBOL(msm_rpc_call_reply); + + +static inline int ept_packet_available(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + int ret; + spin_lock_irqsave(&ept->read_q_lock, flags); + ret = !list_empty(&ept->read_q); + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return ret; +} + +int __msm_rpc_read(struct msm_rpc_endpoint *ept, + struct rr_fragment **frag_ret, + unsigned len, long timeout) +{ + struct rr_packet *pkt; + struct rpc_request_hdr *rq; + struct msm_rpc_reply *reply; + unsigned long flags; + int rc; + + rc = wait_for_restart_and_notify(ept); + if (rc) + return rc; + + IO("READ on ept %p\n", ept); + if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) { + if (timeout < 0) { + wait_event(ept->wait_q, (ept_packet_available(ept) || + ept->forced_wakeup || + ept->restart_state)); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + } else { + rc = wait_event_timeout( + ept->wait_q, + (ept_packet_available(ept) || + ept->forced_wakeup || + ept->restart_state), + timeout); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + if (rc == 0) + return -ETIMEDOUT; + } + } else { + if (timeout < 0) { + rc = wait_event_interruptible( + ept->wait_q, (ept_packet_available(ept) || + ept->forced_wakeup || + ept->restart_state)); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + if (rc < 0) + return rc; + } else { + rc = wait_event_interruptible_timeout( + ept->wait_q, + (ept_packet_available(ept) || + ept->forced_wakeup || + ept->restart_state), + timeout); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + if (rc == 0) + return -ETIMEDOUT; + } + } + + if (ept->forced_wakeup) { + ept->forced_wakeup = 0; + return 0; + } + + spin_lock_irqsave(&ept->read_q_lock, flags); + if (list_empty(&ept->read_q)) { + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return -EAGAIN; + } + pkt = list_first_entry(&ept->read_q, struct rr_packet, list); + if (pkt->length > len) { + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return -ETOOSMALL; + } + list_del(&pkt->list); + spin_unlock_irqrestore(&ept->read_q_lock, flags); + + rc = pkt->length; + + *frag_ret = pkt->first; + rq = (void*) pkt->first->data; + if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) { + /* RPC CALL */ + reply = get_avail_reply(ept); + if (!reply) { + rc = -ENOMEM; + goto read_release_lock; + } + reply->cid = pkt->hdr.src_cid; + reply->pid = pkt->hdr.src_pid; + reply->xid = rq->xid; + reply->prog = rq->prog; + reply->vers = rq->vers; + set_pend_reply(ept, reply); + } + + kfree(pkt); + + IO("READ on ept %p (%d bytes)\n", ept, rc); + + read_release_lock: + + /* release read wakelock after taking reply wakelock */ + spin_lock_irqsave(&ept->read_q_lock, flags); + if (list_empty(&ept->read_q)) { + D("%s: release read lock on ept %p\n", __func__, ept); + wake_unlock(&ept->read_q_wake_lock); + } + spin_unlock_irqrestore(&ept->read_q_lock, flags); + + return rc; +} + +int msm_rpc_is_compatible_version(uint32_t server_version, + uint32_t client_version) +{ + + if ((server_version & RPC_VERSION_MODE_MASK) != + (client_version & RPC_VERSION_MODE_MASK)) + return 0; + + if (server_version & RPC_VERSION_MODE_MASK) + return server_version == client_version; + + return ((server_version & RPC_VERSION_MAJOR_MASK) == + (client_version & RPC_VERSION_MAJOR_MASK)) && + ((server_version & RPC_VERSION_MINOR_MASK) >= + (client_version & RPC_VERSION_MINOR_MASK)); +} +EXPORT_SYMBOL(msm_rpc_is_compatible_version); + +static struct rr_server *msm_rpc_get_server(uint32_t prog, uint32_t vers, + uint32_t accept_compatible, + uint32_t *found_prog) +{ + struct rr_server *server; + unsigned long flags; + + if (found_prog == NULL) + return NULL; + + *found_prog = 0; + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->prog == prog) { + *found_prog = 1; + spin_unlock_irqrestore(&server_list_lock, flags); + if (accept_compatible) { + if (msm_rpc_is_compatible_version(server->vers, + vers)) { + return server; + } else { + return NULL; + } + } else if (server->vers == vers) { + return server; + } else + return NULL; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + +static struct msm_rpc_endpoint *__msm_rpc_connect(uint32_t prog, uint32_t vers, + uint32_t accept_compatible, + unsigned flags) +{ + struct msm_rpc_endpoint *ept; + struct rr_server *server; + uint32_t found_prog; + int rc = 0; + + DEFINE_WAIT(__wait); + + for (;;) { + prepare_to_wait(&newserver_wait, &__wait, + TASK_INTERRUPTIBLE); + + server = msm_rpc_get_server(prog, vers, accept_compatible, + &found_prog); + if (server) + break; + + if (found_prog) { + pr_info("%s: server not found %x:%x\n", + __func__, prog, vers); + rc = -EHOSTUNREACH; + break; + } + + if (msm_rpc_connect_timeout_ms == 0) { + rc = -EHOSTUNREACH; + break; + } + + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + rc = schedule_timeout( + msecs_to_jiffies(msm_rpc_connect_timeout_ms)); + if (!rc) { + rc = -ETIMEDOUT; + break; + } + } + finish_wait(&newserver_wait, &__wait); + + if (!server) + return ERR_PTR(rc); + + if (accept_compatible && (server->vers != vers)) { + D("RPC Using new version 0x%08x(0x%08x) prog 0x%08x", + vers, server->vers, prog); + D(" ... Continuing\n"); + } + + ept = msm_rpc_open(); + if (IS_ERR(ept)) + return ept; + + ept->flags = flags; + ept->dst_pid = server->pid; + ept->dst_cid = server->cid; + ept->dst_prog = cpu_to_be32(prog); + ept->dst_vers = cpu_to_be32(server->vers); + + return ept; +} + +struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog, + uint32_t vers, unsigned flags) +{ + return __msm_rpc_connect(prog, vers, 1, flags); +} +EXPORT_SYMBOL(msm_rpc_connect_compatible); + +struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, + uint32_t vers, unsigned flags) +{ + return __msm_rpc_connect(prog, vers, 0, flags); +} +EXPORT_SYMBOL(msm_rpc_connect); + +/* TODO: permission check? */ +int msm_rpc_register_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers) +{ + int rc; + union rr_control_msg msg; + struct rr_server *server; + struct rpcrouter_xprt_info *xprt_info; + + server = rpcrouter_create_server(ept->pid, ept->cid, + prog, vers); + if (!server) + return -ENODEV; + + msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; + msg.srv.pid = ept->pid; + msg.srv.cid = ept->cid; + msg.srv.prog = prog; + msg.srv.vers = vers; + + RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", + ept->pid, ept->cid, prog, vers); + + mutex_lock(&xprt_info_list_lock); + list_for_each_entry(xprt_info, &xprt_info_list, list) { + rc = rpcrouter_send_control_msg(xprt_info, &msg); + if (rc < 0) { + mutex_unlock(&xprt_info_list_lock); + return rc; + } + } + mutex_unlock(&xprt_info_list_lock); + return 0; +} + +int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + int rc = 1; + spin_lock_irqsave(&ept->restart_lock, flags); + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + rc = 0; + } + spin_unlock_irqrestore(&ept->restart_lock, flags); + return rc; +} + +/* TODO: permission check -- disallow unreg of somebody else's server */ +int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers) +{ + struct rr_server *server; + server = rpcrouter_lookup_server(prog, vers); + + if (!server) + return -ENOENT; + rpcrouter_destroy_server(server); + return 0; +} + +int msm_rpc_get_curr_pkt_size(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + struct rr_packet *pkt; + int rc = 0; + + if (!ept) + return -EINVAL; + + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + + spin_lock_irqsave(&ept->read_q_lock, flags); + if (!list_empty(&ept->read_q)) { + pkt = list_first_entry(&ept->read_q, struct rr_packet, list); + rc = pkt->length; + } + spin_unlock_irqrestore(&ept->read_q_lock, flags); + + return rc; +} + +int msm_rpcrouter_close(void) +{ + struct rpcrouter_xprt_info *xprt_info; + union rr_control_msg ctl; + + ctl.cmd = RPCROUTER_CTRL_CMD_BYE; + mutex_lock(&xprt_info_list_lock); + while (!list_empty(&xprt_info_list)) { + xprt_info = list_first_entry(&xprt_info_list, + struct rpcrouter_xprt_info, list); + xprt_info->abort_data_read = 1; + wake_up(&xprt_info->read_wait); + rpcrouter_send_control_msg(xprt_info, &ctl); + xprt_info->xprt->close(); + list_del(&xprt_info->list); + mutex_unlock(&xprt_info_list_lock); + + flush_workqueue(xprt_info->workqueue); + destroy_workqueue(xprt_info->workqueue); + wake_lock_destroy(&xprt_info->wakelock); + kfree(xprt_info); + + mutex_lock(&xprt_info_list_lock); + } + mutex_unlock(&xprt_info_list_lock); + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int dump_servers(char *buf, int max) +{ + int i = 0; + unsigned long flags; + struct rr_server *svr; + const char *sym; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(svr, &server_list, list) { + i += scnprintf(buf + i, max - i, "pdev_name: %s\n", + svr->pdev_name); + i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", svr->pid); + i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", svr->cid); + i += scnprintf(buf + i, max - i, "prog: 0x%08x", svr->prog); + sym = smd_rpc_get_sym(svr->prog); + if (sym) + i += scnprintf(buf + i, max - i, " (%s)\n", sym); + else + i += scnprintf(buf + i, max - i, "\n"); + i += scnprintf(buf + i, max - i, "vers: 0x%08x\n", svr->vers); + i += scnprintf(buf + i, max - i, "\n"); + } + spin_unlock_irqrestore(&server_list_lock, flags); + + return i; +} + +static int dump_remote_endpoints(char *buf, int max) +{ + int i = 0; + unsigned long flags; + struct rr_remote_endpoint *ept; + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(ept, &remote_endpoints, list) { + i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid); + i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid); + i += scnprintf(buf + i, max - i, "tx_quota_cntr: %i\n", + ept->tx_quota_cntr); + i += scnprintf(buf + i, max - i, "quota_restart_state: %i\n", + ept->quota_restart_state); + i += scnprintf(buf + i, max - i, "\n"); + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + + return i; +} + +static int dump_msm_rpc_endpoint(char *buf, int max) +{ + int i = 0; + unsigned long flags; + struct msm_rpc_reply *reply; + struct msm_rpc_endpoint *ept; + struct rr_packet *pkt; + const char *sym; + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_for_each_entry(ept, &local_endpoints, list) { + i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid); + i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid); + i += scnprintf(buf + i, max - i, "dst_pid: 0x%08x\n", + ept->dst_pid); + i += scnprintf(buf + i, max - i, "dst_cid: 0x%08x\n", + ept->dst_cid); + i += scnprintf(buf + i, max - i, "dst_prog: 0x%08x", + be32_to_cpu(ept->dst_prog)); + sym = smd_rpc_get_sym(be32_to_cpu(ept->dst_prog)); + if (sym) + i += scnprintf(buf + i, max - i, " (%s)\n", sym); + else + i += scnprintf(buf + i, max - i, "\n"); + i += scnprintf(buf + i, max - i, "dst_vers: 0x%08x\n", + be32_to_cpu(ept->dst_vers)); + i += scnprintf(buf + i, max - i, "reply_cnt: %i\n", + ept->reply_cnt); + i += scnprintf(buf + i, max - i, "restart_state: %i\n", + ept->restart_state); + + i += scnprintf(buf + i, max - i, "outstanding xids:\n"); + spin_lock(&ept->reply_q_lock); + list_for_each_entry(reply, &ept->reply_pend_q, list) + i += scnprintf(buf + i, max - i, " xid = %u\n", + ntohl(reply->xid)); + spin_unlock(&ept->reply_q_lock); + + i += scnprintf(buf + i, max - i, "complete unread packets:\n"); + spin_lock(&ept->read_q_lock); + list_for_each_entry(pkt, &ept->read_q, list) { + i += scnprintf(buf + i, max - i, " mid = %i\n", + pkt->mid); + i += scnprintf(buf + i, max - i, " length = %i\n", + pkt->length); + } + spin_unlock(&ept->read_q_lock); + i += scnprintf(buf + i, max - i, "\n"); + } + spin_unlock_irqrestore(&local_endpoints_lock, flags); + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +static void debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smd_rpcrouter", 0); + if (IS_ERR(dent)) + return; + + debug_create("dump_msm_rpc_endpoints", 0444, dent, + dump_msm_rpc_endpoint); + debug_create("dump_remote_endpoints", 0444, dent, + dump_remote_endpoints); + debug_create("dump_servers", 0444, dent, + dump_servers); + +} + +#else +static void debugfs_init(void) {} +#endif + +static int msm_rpcrouter_add_xprt(struct rpcrouter_xprt *xprt) +{ + struct rpcrouter_xprt_info *xprt_info; + + D("Registering xprt %s to RPC Router\n", xprt->name); + + xprt_info = kmalloc(sizeof(struct rpcrouter_xprt_info), GFP_KERNEL); + if (!xprt_info) + return -ENOMEM; + + xprt_info->xprt = xprt; + xprt_info->initialized = 0; + xprt_info->remote_pid = -1; + init_waitqueue_head(&xprt_info->read_wait); + spin_lock_init(&xprt_info->lock); + wake_lock_init(&xprt_info->wakelock, + WAKE_LOCK_SUSPEND, xprt->name); + xprt_info->need_len = 0; + xprt_info->abort_data_read = 0; + INIT_WORK(&xprt_info->read_data, do_read_data); + INIT_LIST_HEAD(&xprt_info->list); + + xprt_info->workqueue = create_singlethread_workqueue(xprt->name); + if (!xprt_info->workqueue) { + kfree(xprt_info); + return -ENOMEM; + } + + if (!strcmp(xprt->name, "rpcrouter_loopback_xprt")) { + xprt_info->remote_pid = RPCROUTER_PID_LOCAL; + xprt_info->initialized = 1; + } else { + smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT); + } + + mutex_lock(&xprt_info_list_lock); + list_add_tail(&xprt_info->list, &xprt_info_list); + mutex_unlock(&xprt_info_list_lock); + + queue_work(xprt_info->workqueue, &xprt_info->read_data); + + xprt->priv = xprt_info; + + return 0; +} + +static void msm_rpcrouter_remove_xprt(struct rpcrouter_xprt *xprt) +{ + struct rpcrouter_xprt_info *xprt_info; + unsigned long flags; + + if (xprt && xprt->priv) { + xprt_info = xprt->priv; + + /* abort rr_read thread */ + xprt_info->abort_data_read = 1; + wake_up(&xprt_info->read_wait); + + /* remove xprt from available xprts */ + mutex_lock(&xprt_info_list_lock); + spin_lock_irqsave(&xprt_info->lock, flags); + list_del(&xprt_info->list); + + /* unlock the spinlock last to avoid a race + * condition with rpcrouter_get_xprt_info + * in msm_rpc_write_pkt in which the + * xprt is returned from rpcrouter_get_xprt_info + * and then deleted here. */ + mutex_unlock(&xprt_info_list_lock); + spin_unlock_irqrestore(&xprt_info->lock, flags); + + /* cleanup workqueues and wakelocks */ + flush_workqueue(xprt_info->workqueue); + destroy_workqueue(xprt_info->workqueue); + wake_lock_destroy(&xprt_info->wakelock); + + + /* free memory */ + xprt->priv = 0; + kfree(xprt_info); + } +} + +struct rpcrouter_xprt_work { + struct rpcrouter_xprt *xprt; + struct work_struct work; +}; + +static void xprt_open_worker(struct work_struct *work) +{ + struct rpcrouter_xprt_work *xprt_work = + container_of(work, struct rpcrouter_xprt_work, work); + + msm_rpcrouter_add_xprt(xprt_work->xprt); + + kfree(xprt_work); +} + +static void xprt_close_worker(struct work_struct *work) +{ + struct rpcrouter_xprt_work *xprt_work = + container_of(work, struct rpcrouter_xprt_work, work); + + modem_reset_cleanup(xprt_work->xprt->priv); + msm_rpcrouter_remove_xprt(xprt_work->xprt); + + if (atomic_dec_return(&pending_close_count) == 0) + wake_up(&subsystem_restart_wait); + + kfree(xprt_work); +} + +void msm_rpcrouter_xprt_notify(struct rpcrouter_xprt *xprt, unsigned event) +{ + struct rpcrouter_xprt_info *xprt_info; + struct rpcrouter_xprt_work *xprt_work; + + /* Workqueue is created in init function which works for all existing + * clients. If this fails in the future, then it will need to be + * created earlier. */ + BUG_ON(!rpcrouter_workqueue); + + switch (event) { + case RPCROUTER_XPRT_EVENT_OPEN: + D("open event for '%s'\n", xprt->name); + xprt_work = kmalloc(sizeof(struct rpcrouter_xprt_work), + GFP_ATOMIC); + xprt_work->xprt = xprt; + INIT_WORK(&xprt_work->work, xprt_open_worker); + queue_work(rpcrouter_workqueue, &xprt_work->work); + break; + + case RPCROUTER_XPRT_EVENT_CLOSE: + D("close event for '%s'\n", xprt->name); + + atomic_inc(&pending_close_count); + + xprt_work = kmalloc(sizeof(struct rpcrouter_xprt_work), + GFP_ATOMIC); + xprt_work->xprt = xprt; + INIT_WORK(&xprt_work->work, xprt_close_worker); + queue_work(rpcrouter_workqueue, &xprt_work->work); + break; + } + + xprt_info = xprt->priv; + if (xprt_info) { + /* Check read_avail even for OPEN event to handle missed + DATA events while processing the OPEN event*/ + if (xprt->read_avail() >= xprt_info->need_len) + wake_lock(&xprt_info->wakelock); + wake_up(&xprt_info->read_wait); + } +} + +static int modem_restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data); +static struct notifier_block nb = { + .notifier_call = modem_restart_notifier_cb, +}; + +static int modem_restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data) +{ + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + D("%s: SUBSYS_BEFORE_SHUTDOWN\n", __func__); + break; + + case SUBSYS_BEFORE_POWERUP: + D("%s: waiting for RPC restart to complete\n", __func__); + wait_event(subsystem_restart_wait, + atomic_read(&pending_close_count) == 0); + D("%s: finished restart wait\n", __func__); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static void *restart_notifier_handle; +static __init int modem_restart_late_init(void) +{ + restart_notifier_handle = subsys_notif_register_notifier("modem", &nb); + return 0; +} +late_initcall(modem_restart_late_init); + +static int __init rpcrouter_init(void) +{ + int ret; + + msm_rpc_connect_timeout_ms = 0; + smd_rpcrouter_debug_mask |= SMEM_LOG; + debugfs_init(); + + + /* Initialize what we need to start processing */ + rpcrouter_workqueue = + create_singlethread_workqueue("rpcrouter"); + if (!rpcrouter_workqueue) { + msm_rpcrouter_exit_devices(); + return -ENOMEM; + } + + init_waitqueue_head(&newserver_wait); + init_waitqueue_head(&subsystem_restart_wait); + + ret = msm_rpcrouter_init_devices(); + if (ret < 0) + return ret; + + return ret; +} + +module_init(rpcrouter_init); +MODULE_DESCRIPTION("MSM RPC Router"); +MODULE_AUTHOR("San Mehat "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h new file mode 100644 index 00000000000..1da7579aef8 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -0,0 +1,266 @@ +/** arch/arm/mach-msm/smd_rpcrouter.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H +#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* definitions for the R2R wire protcol */ + +#define RPCROUTER_VERSION 1 +#define RPCROUTER_PROCESSORS_MAX 4 +#define RPCROUTER_MSGSIZE_MAX 512 +#define RPCROUTER_PEND_REPLIES_MAX 32 + +#define RPCROUTER_CLIENT_BCAST_ID 0xffffffff +#define RPCROUTER_ROUTER_ADDRESS 0xfffffffe + +#define RPCROUTER_PID_LOCAL 1 + +#define RPCROUTER_CTRL_CMD_DATA 1 +#define RPCROUTER_CTRL_CMD_HELLO 2 +#define RPCROUTER_CTRL_CMD_BYE 3 +#define RPCROUTER_CTRL_CMD_NEW_SERVER 4 +#define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5 +#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6 +#define RPCROUTER_CTRL_CMD_RESUME_TX 7 +#define RPCROUTER_CTRL_CMD_EXIT 8 +#define RPCROUTER_CTRL_CMD_PING 9 + +#define RPCROUTER_DEFAULT_RX_QUOTA 5 + +#define RPCROUTER_XPRT_EVENT_DATA 1 +#define RPCROUTER_XPRT_EVENT_OPEN 2 +#define RPCROUTER_XPRT_EVENT_CLOSE 3 + +/* Restart states for endpoint. + * + * Two different bits are specified here, one for + * the remote server notification (RESTART_PEND_SVR) + * and one for client notification (RESTART_PEND_NTFY). + * The client notification is used to ensure that + * the client gets notified by an ENETRESET return + * code at least once, even if they miss the actual + * reset event. The server notification is used to + * properly handle the reset state of the endpoint. + */ +#define RESTART_NORMAL 0x0 +#define RESTART_PEND_SVR 0x1 +#define RESTART_PEND_NTFY 0x2 +#define RESTART_PEND_NTFY_SVR (RESTART_PEND_SVR | RESTART_PEND_NTFY) + +union rr_control_msg { + uint32_t cmd; + struct { + uint32_t cmd; + uint32_t prog; + uint32_t vers; + uint32_t pid; + uint32_t cid; + } srv; + struct { + uint32_t cmd; + uint32_t pid; + uint32_t cid; + } cli; +}; + +struct rr_header { + uint32_t version; + uint32_t type; + uint32_t src_pid; + uint32_t src_cid; + uint32_t confirm_rx; + uint32_t size; + uint32_t dst_pid; + uint32_t dst_cid; +}; + +/* internals */ + +#define RPCROUTER_MAX_REMOTE_SERVERS 100 + +struct rr_fragment { + unsigned char data[RPCROUTER_MSGSIZE_MAX]; + uint32_t length; + struct rr_fragment *next; +}; + +struct rr_packet { + struct list_head list; + struct rr_fragment *first; + struct rr_fragment *last; + struct rr_header hdr; + uint32_t mid; + uint32_t length; +}; + +#define PACMARK_LAST(n) ((n) & 0x80000000) +#define PACMARK_MID(n) (((n) >> 16) & 0xFF) +#define PACMARK_LEN(n) ((n) & 0xFFFF) + +static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first, + uint32_t last) +{ + return (len & 0xFFFF) | + ((mid & 0xFF) << 16) | + ((!!first) << 30) | + ((!!last) << 31); +} + +struct rr_server { + struct list_head list; + + uint32_t pid; + uint32_t cid; + uint32_t prog; + uint32_t vers; + + dev_t device_number; + struct cdev cdev; + struct device *device; + struct rpcsvr_platform_device p_device; + char pdev_name[32]; +}; + +struct rr_remote_endpoint { + uint32_t pid; + uint32_t cid; + + int tx_quota_cntr; + int quota_restart_state; + spinlock_t quota_lock; + wait_queue_head_t quota_wait; + + struct list_head list; +}; + +struct msm_rpc_reply { + struct list_head list; + uint32_t pid; + uint32_t cid; + uint32_t prog; /* be32 */ + uint32_t vers; /* be32 */ + uint32_t xid; /* be32 */ +}; + +struct msm_rpc_endpoint { + struct list_head list; + + /* incomplete packets waiting for assembly */ + struct list_head incomplete; + spinlock_t incomplete_lock; + + /* complete packets waiting to be read */ + struct list_head read_q; + spinlock_t read_q_lock; + struct wake_lock read_q_wake_lock; + wait_queue_head_t wait_q; + unsigned flags; + uint32_t forced_wakeup; + + /* restart handling */ + int restart_state; + spinlock_t restart_lock; + wait_queue_head_t restart_wait; + + /* modem restart notifications */ + int do_setup_notif; + void *client_data; + void (*cb_restart_teardown)(void *client_data); + void (*cb_restart_setup)(void *client_data); + + /* endpoint address */ + uint32_t pid; + uint32_t cid; + + /* bound remote address + * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail + * RPC_CALLs must be to the prog/vers below or they will fail + */ + uint32_t dst_pid; + uint32_t dst_cid; + uint32_t dst_prog; /* be32 */ + uint32_t dst_vers; /* be32 */ + + /* reply queue for inbound messages */ + struct list_head reply_pend_q; + struct list_head reply_avail_q; + spinlock_t reply_q_lock; + uint32_t reply_cnt; + struct wake_lock reply_q_wake_lock; + + /* device node if this endpoint is accessed via userspace */ + dev_t dev; +}; + +enum write_data_type { + HEADER = 1, + PACKMARK, + PAYLOAD, +}; + +struct rpcrouter_xprt { + char *name; + void *priv; + + int (*read_avail)(void); + int (*read)(void *data, uint32_t len); + int (*write_avail)(void); + int (*write)(void *data, uint32_t len, enum write_data_type type); + int (*close)(void); +}; + +/* shared between smd_rpcrouter*.c */ +void msm_rpcrouter_xprt_notify(struct rpcrouter_xprt *xprt, unsigned event); +int __msm_rpc_read(struct msm_rpc_endpoint *ept, + struct rr_fragment **frag, + unsigned len, long timeout); + +int msm_rpcrouter_close(void); +struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev); +int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept); + +int msm_rpcrouter_create_server_cdev(struct rr_server *server); +int msm_rpcrouter_create_server_pdev(struct rr_server *server); + +int msm_rpcrouter_init_devices(void); +void msm_rpcrouter_exit_devices(void); + +void get_requesting_client(struct msm_rpc_endpoint *ept, uint32_t xid, + struct msm_rpc_client_info *clnt_info); + +extern dev_t msm_rpcrouter_devno; +extern struct completion rpc_remote_router_up; +extern struct class *msm_rpcrouter_class; + +void xdr_init(struct msm_rpc_xdr *xdr); +void xdr_init_input(struct msm_rpc_xdr *xdr, void *buf, uint32_t size); +void xdr_init_output(struct msm_rpc_xdr *xdr, void *buf, uint32_t size); +void xdr_clean_input(struct msm_rpc_xdr *xdr); +void xdr_clean_output(struct msm_rpc_xdr *xdr); +uint32_t xdr_read_avail(struct msm_rpc_xdr *xdr); +#endif diff --git a/arch/arm/mach-msm/smd_rpcrouter_clients.c b/arch/arm/mach-msm/smd_rpcrouter_clients.c new file mode 100644 index 00000000000..d94125e87d3 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_clients.c @@ -0,0 +1,922 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SMD RPCROUTER CLIENTS module. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "smd_rpcrouter.h" + +struct msm_rpc_client_cb_item { + struct list_head list; + + void *buf; + int size; +}; + +struct msm_rpc_cb_table_item { + struct list_head list; + + uint32_t cb_id; + void *cb_func; +}; + +static int rpc_clients_cb_thread(void *data) +{ + struct msm_rpc_client_cb_item *cb_item; + struct msm_rpc_client *client; + struct rpc_request_hdr req; + int ret; + + client = data; + for (;;) { + wait_event(client->cb_wait, client->cb_avail); + if (client->exit_flag) + break; + + client->cb_avail = 0; + mutex_lock(&client->cb_item_list_lock); + while (!list_empty(&client->cb_item_list)) { + cb_item = list_first_entry( + &client->cb_item_list, + struct msm_rpc_client_cb_item, + list); + list_del(&cb_item->list); + mutex_unlock(&client->cb_item_list_lock); + xdr_init_input(&client->cb_xdr, cb_item->buf, + cb_item->size); + ret = xdr_recv_req(&client->cb_xdr, &req); + if (ret) + goto bad_rpc; + + if (req.type != 0) + goto bad_rpc; + if (req.rpc_vers != 2) + goto bad_rpc; + if (req.prog != + (client->prog | 0x01000000)) + goto bad_rpc; + + if (client->version == 2) + client->cb_func2(client, &req, &client->cb_xdr); + else + client->cb_func(client, client->cb_xdr.in_buf, + client->cb_xdr.in_size); + bad_rpc: + xdr_clean_input(&client->cb_xdr); + kfree(cb_item); + mutex_lock(&client->cb_item_list_lock); + } + mutex_unlock(&client->cb_item_list_lock); + } + complete_and_exit(&client->cb_complete, 0); +} + +static int rpc_clients_thread(void *data) +{ + void *buffer; + uint32_t type; + struct msm_rpc_client *client; + int rc = 0; + struct msm_rpc_client_cb_item *cb_item; + struct rpc_request_hdr req; + + client = data; + for (;;) { + buffer = NULL; + rc = msm_rpc_read(client->ept, &buffer, -1, -1); + + if (client->exit_flag) { + kfree(buffer); + break; + } + + if (rc < 0) { + /* wakeup any pending requests */ + wake_up(&client->reply_wait); + kfree(buffer); + continue; + } + + if (rc < ((int)(sizeof(uint32_t) * 2))) { + kfree(buffer); + continue; + } + + type = be32_to_cpu(*((uint32_t *)buffer + 1)); + if (type == 1) { + xdr_init_input(&client->xdr, buffer, rc); + wake_up(&client->reply_wait); + } else if (type == 0) { + if (client->cb_thread == NULL) { + xdr_init_input(&client->cb_xdr, buffer, rc); + xdr_recv_req(&client->cb_xdr, &req); + + if ((req.rpc_vers == 2) && + (req.prog == (client->prog | 0x01000000))) { + if (client->version == 2) + client->cb_func2(client, &req, + &client->cb_xdr); + else + client->cb_func(client, + client->cb_xdr.in_buf, rc); + } + xdr_clean_input(&client->cb_xdr); + } else { + cb_item = kmalloc(sizeof(*cb_item), GFP_KERNEL); + if (!cb_item) { + pr_err("%s: no memory for cb item\n", + __func__); + continue; + } + + INIT_LIST_HEAD(&cb_item->list); + cb_item->buf = buffer; + cb_item->size = rc; + mutex_lock(&client->cb_item_list_lock); + list_add_tail(&cb_item->list, + &client->cb_item_list); + mutex_unlock(&client->cb_item_list_lock); + client->cb_avail = 1; + wake_up(&client->cb_wait); + } + } + } + complete_and_exit(&client->complete, 0); +} + +static struct msm_rpc_client *msm_rpc_create_client(void) +{ + struct msm_rpc_client *client; + void *buf; + + client = kmalloc(sizeof(struct msm_rpc_client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + xdr_init(&client->xdr); + xdr_init(&client->cb_xdr); + + buf = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!buf) { + kfree(client); + return ERR_PTR(-ENOMEM); + } + xdr_init_output(&client->xdr, buf, MSM_RPC_MSGSIZE_MAX); + + buf = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!buf) { + xdr_clean_output(&client->xdr); + kfree(client); + return ERR_PTR(-ENOMEM); + } + xdr_init_output(&client->cb_xdr, buf, MSM_RPC_MSGSIZE_MAX); + + init_waitqueue_head(&client->reply_wait); + mutex_init(&client->req_lock); + client->buf = NULL; + client->cb_buf = NULL; + client->cb_size = 0; + client->exit_flag = 0; + client->cb_restart_teardown = NULL; + client->cb_restart_setup = NULL; + client->in_reset = 0; + + init_completion(&client->complete); + init_completion(&client->cb_complete); + INIT_LIST_HEAD(&client->cb_item_list); + mutex_init(&client->cb_item_list_lock); + client->cb_avail = 0; + init_waitqueue_head(&client->cb_wait); + INIT_LIST_HEAD(&client->cb_list); + spin_lock_init(&client->cb_list_lock); + atomic_set(&client->next_cb_id, 1); + + return client; +} + +static void msm_rpc_destroy_client(struct msm_rpc_client *client) +{ + xdr_clean_output(&client->xdr); + xdr_clean_output(&client->cb_xdr); + + kfree(client); +} + +void msm_rpc_remove_all_cb_func(struct msm_rpc_client *client) +{ + struct msm_rpc_cb_table_item *cb_item, *tmp_cb_item; + unsigned long flags; + + spin_lock_irqsave(&client->cb_list_lock, flags); + list_for_each_entry_safe(cb_item, tmp_cb_item, + &client->cb_list, list) { + list_del(&cb_item->list); + kfree(cb_item); + } + spin_unlock_irqrestore(&client->cb_list_lock, flags); +} + +static void cb_restart_teardown(void *client_data) +{ + struct msm_rpc_client *client; + + client = (struct msm_rpc_client *)client_data; + if (client) { + client->in_reset = 1; + msm_rpc_remove_all_cb_func(client); + client->xdr.out_index = 0; + + if (client->cb_restart_teardown) + client->cb_restart_teardown(client); + } +} + +static void cb_restart_setup(void *client_data) +{ + struct msm_rpc_client *client; + + client = (struct msm_rpc_client *)client_data; + + if (client) { + client->in_reset = 0; + if (client->cb_restart_setup) + client->cb_restart_setup(client); + } +} + +/* Returns the reset state of the client. + * + * Return Value: + * 0 if client isn't in reset, >0 otherwise. + */ +int msm_rpc_client_in_reset(struct msm_rpc_client *client) +{ + int ret = 1; + + if (client) + ret = client->in_reset; + + return ret; +} +EXPORT_SYMBOL(msm_rpc_client_in_reset); + +/* + * Interface to be used to register the client. + * + * name: string representing the client + * + * prog: program number of the client + * + * ver: version number of the client + * + * create_cb_thread: if set calls the callback function from a seprate thread + * which helps the client requests to be processed without + * getting loaded by callback handling. + * + * cb_func: function to be called if callback request is received. + * unmarshaling should be handled by the user in callback function + * + * Return Value: + * Pointer to initialized client data sturcture + * Or, the error code if registration fails. + * + */ +struct msm_rpc_client *msm_rpc_register_client( + const char *name, + uint32_t prog, uint32_t ver, + uint32_t create_cb_thread, + int (*cb_func)(struct msm_rpc_client *, void *, int)) +{ + struct msm_rpc_client *client; + struct msm_rpc_endpoint *ept; + int rc; + + client = msm_rpc_create_client(); + if (IS_ERR(client)) + return client; + + ept = msm_rpc_connect_compatible(prog, ver, MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(ept)) { + msm_rpc_destroy_client(client); + return (struct msm_rpc_client *)ept; + } + + ept->client_data = client; + ept->cb_restart_teardown = cb_restart_teardown; + ept->cb_restart_setup = cb_restart_setup; + + client->prog = prog; + client->ver = ver; + client->ept = client->xdr.ept = client->cb_xdr.ept = ept; + client->cb_func = cb_func; + client->version = 1; + + /* start the read thread */ + client->read_thread = kthread_run(rpc_clients_thread, client, + "k%sclntd", name); + if (IS_ERR(client->read_thread)) { + rc = PTR_ERR(client->read_thread); + msm_rpc_close(client->ept); + msm_rpc_destroy_client(client); + return ERR_PTR(rc); + } + + if (!create_cb_thread || (cb_func == NULL)) { + client->cb_thread = NULL; + return client; + } + + /* start the callback thread */ + client->cb_thread = kthread_run(rpc_clients_cb_thread, client, + "k%sclntcbd", name); + if (IS_ERR(client->cb_thread)) { + rc = PTR_ERR(client->cb_thread); + client->exit_flag = 1; + msm_rpc_read_wakeup(client->ept); + wait_for_completion(&client->complete); + msm_rpc_close(client->ept); + msm_rpc_destroy_client(client); + return ERR_PTR(rc); + } + + return client; +} +EXPORT_SYMBOL(msm_rpc_register_client); + +/* + * Interface to be used to register the client. + * + * name: string representing the client + * + * prog: program number of the client + * + * ver: version number of the client + * + * create_cb_thread: if set calls the callback function from a seprate thread + * which helps the client requests to be processed without + * getting loaded by callback handling. + * + * cb_func: function to be called if callback request is received. + * unmarshaling should be handled by the user in callback function + * + * Return Value: + * Pointer to initialized client data sturcture + * Or, the error code if registration fails. + * + */ +struct msm_rpc_client *msm_rpc_register_client2( + const char *name, + uint32_t prog, uint32_t ver, + uint32_t create_cb_thread, + int (*cb_func)(struct msm_rpc_client *, + struct rpc_request_hdr *req, struct msm_rpc_xdr *)) +{ + struct msm_rpc_client *client; + struct msm_rpc_endpoint *ept; + int rc; + + client = msm_rpc_create_client(); + if (IS_ERR(client)) + return client; + + ept = msm_rpc_connect_compatible(prog, ver, MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(ept)) { + msm_rpc_destroy_client(client); + return (struct msm_rpc_client *)ept; + } + + client->prog = prog; + client->ver = ver; + client->ept = client->xdr.ept = client->cb_xdr.ept = ept; + client->cb_func2 = cb_func; + client->version = 2; + + ept->client_data = client; + ept->cb_restart_teardown = cb_restart_teardown; + ept->cb_restart_setup = cb_restart_setup; + + /* start the read thread */ + client->read_thread = kthread_run(rpc_clients_thread, client, + "k%sclntd", name); + if (IS_ERR(client->read_thread)) { + rc = PTR_ERR(client->read_thread); + msm_rpc_close(client->ept); + msm_rpc_destroy_client(client); + return ERR_PTR(rc); + } + + if (!create_cb_thread || (cb_func == NULL)) { + client->cb_thread = NULL; + return client; + } + + /* start the callback thread */ + client->cb_thread = kthread_run(rpc_clients_cb_thread, client, + "k%sclntcbd", name); + if (IS_ERR(client->cb_thread)) { + rc = PTR_ERR(client->cb_thread); + client->exit_flag = 1; + msm_rpc_read_wakeup(client->ept); + wait_for_completion(&client->complete); + msm_rpc_close(client->ept); + msm_rpc_destroy_client(client); + return ERR_PTR(rc); + } + + return client; +} +EXPORT_SYMBOL(msm_rpc_register_client2); + +/* + * Register callbacks for modem state changes. + * + * Teardown is called when the modem is going into reset. + * Setup is called after the modem has come out of reset (but may not + * be available, yet). + * + * client: pointer to client data structure. + * + * Return Value: + * 0 (success) + * 1 (client pointer invalid) + */ +int msm_rpc_register_reset_callbacks( + struct msm_rpc_client *client, + void (*teardown)(struct msm_rpc_client *client), + void (*setup)(struct msm_rpc_client *client) + ) +{ + int rc = 1; + + if (client) { + client->cb_restart_teardown = teardown; + client->cb_restart_setup = setup; + rc = 0; + } + + return rc; +} +EXPORT_SYMBOL(msm_rpc_register_reset_callbacks); + +/* + * Interface to be used to unregister the client + * No client operations should be done once the unregister function + * is called. + * + * client: pointer to client data structure. + * + * Return Value: + * Always returns 0 (success). + */ +int msm_rpc_unregister_client(struct msm_rpc_client *client) +{ + pr_info("%s: stopping client...\n", __func__); + client->exit_flag = 1; + if (client->cb_thread) { + client->cb_avail = 1; + wake_up(&client->cb_wait); + wait_for_completion(&client->cb_complete); + } + + msm_rpc_read_wakeup(client->ept); + wait_for_completion(&client->complete); + + msm_rpc_close(client->ept); + msm_rpc_remove_all_cb_func(client); + xdr_clean_output(&client->xdr); + xdr_clean_output(&client->cb_xdr); + kfree(client); + return 0; +} +EXPORT_SYMBOL(msm_rpc_unregister_client); + +/* + * Interface to be used to send a client request. + * If the request takes any arguments or expects any return, the user + * should handle it in 'arg_func' and 'ret_func' respectively. + * Marshaling and Unmarshaling should be handled by the user in argument + * and return functions. + * + * client: pointer to client data sturcture + * + * proc: procedure being requested + * + * arg_func: argument function pointer. 'buf' is where arguments needs to + * be filled. 'data' is arg_data. + * + * ret_func: return function pointer. 'buf' is where returned data should + * be read from. 'data' is ret_data. + * + * arg_data: passed as an input parameter to argument function. + * + * ret_data: passed as an input parameter to return function. + * + * timeout: timeout for reply wait in jiffies. If negative timeout is + * specified a default timeout of 10s is used. + * + * Return Value: + * 0 on success, otherwise an error code is returned. + */ +int msm_rpc_client_req(struct msm_rpc_client *client, uint32_t proc, + int (*arg_func)(struct msm_rpc_client *client, + void *buf, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_client *client, + void *buf, void *data), + void *ret_data, long timeout) +{ + struct rpc_reply_hdr *rpc_rsp; + int rc = 0; + uint32_t req_xid; + + mutex_lock(&client->req_lock); + + msm_rpc_setup_req((struct rpc_request_hdr *)client->xdr.out_buf, + client->prog, client->ver, proc); + client->xdr.out_index = sizeof(struct rpc_request_hdr); + req_xid = *(uint32_t *)client->xdr.out_buf; + if (arg_func) { + rc = arg_func(client, + (void *)((struct rpc_request_hdr *) + client->xdr.out_buf + 1), + arg_data); + if (rc < 0) + goto release_locks; + else + client->xdr.out_index += rc; + } + + rc = msm_rpc_write(client->ept, client->xdr.out_buf, + client->xdr.out_index); + if (rc < 0) { + pr_err("%s: couldn't send RPC request:%d\n", __func__, rc); + goto release_locks; + } else + rc = 0; + + if (timeout < 0) + timeout = msecs_to_jiffies(10000); + + do { + rc = wait_event_timeout(client->reply_wait, + xdr_read_avail(&client->xdr) || client->in_reset, + timeout); + + if (client->in_reset) { + rc = -ENETRESET; + goto release_locks; + } + + if (rc == 0) { + pr_err("%s: request timeout\n", __func__); + rc = -ETIMEDOUT; + goto release_locks; + } + + rpc_rsp = (struct rpc_reply_hdr *)client->xdr.in_buf; + if (req_xid != rpc_rsp->xid) { + pr_info("%s: xid mismatch, req %d reply %d\n", + __func__, be32_to_cpu(req_xid), + be32_to_cpu(rpc_rsp->xid)); + timeout = rc; + xdr_clean_input(&client->xdr); + } else + rc = 0; + } while (rc); + + if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("%s: RPC call was denied! %d\n", __func__, + be32_to_cpu(rpc_rsp->reply_stat)); + rc = -EPERM; + goto free_and_release; + } + + if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + pr_err("%s: RPC call was not successful (%d)\n", __func__, + be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); + rc = -EINVAL; + goto free_and_release; + } + + if (ret_func) + rc = ret_func(client, (void *)(rpc_rsp + 1), ret_data); + + free_and_release: + xdr_clean_input(&client->xdr); + client->xdr.out_index = 0; + release_locks: + mutex_unlock(&client->req_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_client_req); + +/* + * Interface to be used to send a client request. + * If the request takes any arguments or expects any return, the user + * should handle it in 'arg_func' and 'ret_func' respectively. + * Marshaling and Unmarshaling should be handled by the user in argument + * and return functions. + * + * client: pointer to client data sturcture + * + * proc: procedure being requested + * + * arg_func: argument function pointer. 'xdr' is the xdr being used. + * 'data' is arg_data. + * + * ret_func: return function pointer. 'xdr' is the xdr being used. + * 'data' is ret_data. + * + * arg_data: passed as an input parameter to argument function. + * + * ret_data: passed as an input parameter to return function. + * + * timeout: timeout for reply wait in jiffies. If negative timeout is + * specified a default timeout of 10s is used. + * + * Return Value: + * 0 on success, otherwise an error code is returned. + */ +int msm_rpc_client_req2(struct msm_rpc_client *client, uint32_t proc, + int (*arg_func)(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_client *client, + struct msm_rpc_xdr *xdr, void *data), + void *ret_data, long timeout) +{ + struct rpc_reply_hdr rpc_rsp; + int rc = 0; + uint32_t req_xid; + + mutex_lock(&client->req_lock); + + if (client->in_reset) { + rc = -ENETRESET; + goto release_locks; + } + + xdr_start_request(&client->xdr, client->prog, client->ver, proc); + req_xid = be32_to_cpu(*(uint32_t *)client->xdr.out_buf); + if (arg_func) { + rc = arg_func(client, &client->xdr, arg_data); + if (rc < 0) { + mutex_unlock(&client->xdr.out_lock); + goto release_locks; + } + } + + rc = xdr_send_msg(&client->xdr); + if (rc < 0) { + pr_err("%s: couldn't send RPC request:%d\n", __func__, rc); + goto release_locks; + } else + rc = 0; + + if (timeout < 0) + timeout = msecs_to_jiffies(10000); + + do { + rc = wait_event_timeout(client->reply_wait, + xdr_read_avail(&client->xdr) || client->in_reset, + timeout); + + if (client->in_reset) { + rc = -ENETRESET; + goto release_locks; + } + + if (rc == 0) { + pr_err("%s: request timeout\n", __func__); + rc = -ETIMEDOUT; + goto release_locks; + } + + xdr_recv_reply(&client->xdr, &rpc_rsp); + /* TODO: may be this check should be a xdr function */ + if (req_xid != rpc_rsp.xid) { + pr_info("%s: xid mismatch, req %d reply %d\n", + __func__, req_xid, rpc_rsp.xid); + timeout = rc; + xdr_clean_input(&client->xdr); + } else + rc = 0; + } while (rc); + + if (rpc_rsp.reply_stat != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("%s: RPC call was denied! %d\n", + __func__, rpc_rsp.reply_stat); + rc = -EPERM; + goto free_and_release; + } + + if (rpc_rsp.data.acc_hdr.accept_stat != RPC_ACCEPTSTAT_SUCCESS) { + pr_err("%s: RPC call was not successful (%d)\n", __func__, + rpc_rsp.data.acc_hdr.accept_stat); + rc = -EINVAL; + goto free_and_release; + } + + if (ret_func) + rc = ret_func(client, &client->xdr, ret_data); + + free_and_release: + xdr_clean_input(&client->xdr); + /* TODO: put it in xdr_reset_output */ + client->xdr.out_index = 0; + release_locks: + mutex_unlock(&client->req_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_client_req2); + +/* + * Interface to be used to start accepted reply message required in + * callback handling. Returns the buffer pointer to attach any + * payload. Should call msm_rpc_send_accepted_reply to complete + * sending reply. Marshaling should be handled by user for the payload. + * + * client: pointer to client data structure + * + * xid: transaction id. Has to be same as the one in callback request. + * + * accept_status: acceptance status + * + * Return Value: + * pointer to buffer to attach the payload. + */ +void *msm_rpc_start_accepted_reply(struct msm_rpc_client *client, + uint32_t xid, uint32_t accept_status) +{ + struct rpc_reply_hdr *reply; + + mutex_lock(&client->cb_xdr.out_lock); + + reply = (struct rpc_reply_hdr *)client->cb_xdr.out_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + client->cb_xdr.out_index = sizeof(*reply); + return reply + 1; +} +EXPORT_SYMBOL(msm_rpc_start_accepted_reply); + +/* + * Interface to be used to send accepted reply required in callback handling. + * msm_rpc_start_accepted_reply should have been called before. + * Marshaling should be handled by user for the payload. + * + * client: pointer to client data structure + * + * size: additional payload size + * + * Return Value: + * 0 on success, otherwise returns an error code. + */ +int msm_rpc_send_accepted_reply(struct msm_rpc_client *client, uint32_t size) +{ + int rc = 0; + + client->cb_xdr.out_index += size; + rc = msm_rpc_write(client->ept, client->cb_xdr.out_buf, + client->cb_xdr.out_index); + if (rc > 0) + rc = 0; + + mutex_unlock(&client->cb_xdr.out_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_send_accepted_reply); + +/* + * Interface to be used to add a callback function. + * If the call back function is already in client's 'cb_id - cb_func' + * table, then that cb_id is returned. otherwise, new entry + * is added to the above table and corresponding cb_id is returned. + * + * client: pointer to client data structure + * + * cb_func: callback function + * + * Return Value: + * callback ID on success, otherwise returns an error code. + * If cb_func is NULL, the callback Id returned is 0xffffffff. + * This tells the other processor that no callback is reqested. + */ +int msm_rpc_add_cb_func(struct msm_rpc_client *client, void *cb_func) +{ + struct msm_rpc_cb_table_item *cb_item; + unsigned long flags; + + if (cb_func == NULL) + return MSM_RPC_CLIENT_NULL_CB_ID; + + spin_lock_irqsave(&client->cb_list_lock, flags); + list_for_each_entry(cb_item, &client->cb_list, list) { + if (cb_item->cb_func == cb_func) { + spin_unlock_irqrestore(&client->cb_list_lock, flags); + return cb_item->cb_id; + } + } + spin_unlock_irqrestore(&client->cb_list_lock, flags); + + cb_item = kmalloc(sizeof(struct msm_rpc_cb_table_item), GFP_KERNEL); + if (!cb_item) + return -ENOMEM; + + INIT_LIST_HEAD(&cb_item->list); + cb_item->cb_id = atomic_add_return(1, &client->next_cb_id); + cb_item->cb_func = cb_func; + + spin_lock_irqsave(&client->cb_list_lock, flags); + list_add_tail(&cb_item->list, &client->cb_list); + spin_unlock_irqrestore(&client->cb_list_lock, flags); + + return cb_item->cb_id; +} +EXPORT_SYMBOL(msm_rpc_add_cb_func); + +/* + * Interface to be used to get a callback function from a callback ID. + * If no entry is found, NULL is returned. + * + * client: pointer to client data structure + * + * cb_id: callback ID + * + * Return Value: + * callback function pointer if entry with given cb_id is found, + * otherwise returns NULL. + */ +void *msm_rpc_get_cb_func(struct msm_rpc_client *client, uint32_t cb_id) +{ + struct msm_rpc_cb_table_item *cb_item; + unsigned long flags; + + spin_lock_irqsave(&client->cb_list_lock, flags); + list_for_each_entry(cb_item, &client->cb_list, list) { + if (cb_item->cb_id == cb_id) { + spin_unlock_irqrestore(&client->cb_list_lock, flags); + return cb_item->cb_func; + } + } + spin_unlock_irqrestore(&client->cb_list_lock, flags); + return NULL; +} +EXPORT_SYMBOL(msm_rpc_get_cb_func); + +/* + * Interface to be used to remove a callback function. + * + * client: pointer to client data structure + * + * cb_func: callback function + * + */ +void msm_rpc_remove_cb_func(struct msm_rpc_client *client, void *cb_func) +{ + struct msm_rpc_cb_table_item *cb_item, *tmp_cb_item; + unsigned long flags; + + if (cb_func == NULL) + return; + + spin_lock_irqsave(&client->cb_list_lock, flags); + list_for_each_entry_safe(cb_item, tmp_cb_item, + &client->cb_list, list) { + if (cb_item->cb_func == cb_func) { + list_del(&cb_item->list); + kfree(cb_item); + spin_unlock_irqrestore(&client->cb_list_lock, flags); + return; + } + } + spin_unlock_irqrestore(&client->cb_list_lock, flags); +} +EXPORT_SYMBOL(msm_rpc_remove_cb_func); diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c new file mode 100644 index 00000000000..e84d2130b86 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_device.c @@ -0,0 +1,476 @@ +/* arch/arm/mach-msm/smd_rpcrouter_device.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "smd_rpcrouter.h" + +/* Support 64KB of data plus some space for headers */ +#define SAFETY_MEM_SIZE (65536 + sizeof(struct rpc_request_hdr)) + +/* modem load timeout */ +#define MODEM_LOAD_TIMEOUT (10 * HZ) + +/* Next minor # available for a remote server */ +static int next_minor = 1; +static DEFINE_SPINLOCK(server_cdev_lock); + +struct class *msm_rpcrouter_class; +dev_t msm_rpcrouter_devno; + +static struct cdev rpcrouter_cdev; +static struct device *rpcrouter_device; + +struct rpcrouter_file_info { + struct msm_rpc_endpoint *ept; + void *modem_pil; +}; + +static void msm_rpcrouter_unload_modem(void *pil) +{ + if (pil) + pil_put(pil); +} + +static void *msm_rpcrouter_load_modem(void) +{ + void *pil; + int rc; + + pil = pil_get("modem"); + if (IS_ERR(pil)) + pr_err("%s: modem load failed\n", __func__); + else { + rc = wait_for_completion_interruptible_timeout( + &rpc_remote_router_up, + MODEM_LOAD_TIMEOUT); + if (!rc) + rc = -ETIMEDOUT; + if (rc < 0) { + pr_err("%s: wait for remote router failed %d\n", + __func__, rc); + msm_rpcrouter_unload_modem(pil); + pil = ERR_PTR(rc); + } + } + + return pil; +} + +static int rpcrouter_open(struct inode *inode, struct file *filp) +{ + int rc; + void *pil; + struct msm_rpc_endpoint *ept; + struct rpcrouter_file_info *file_info; + + rc = nonseekable_open(inode, filp); + if (rc < 0) + return rc; + + file_info = kzalloc(sizeof(*file_info), GFP_KERNEL); + if (!file_info) + return -ENOMEM; + + ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev); + if (!ept) { + kfree(file_info); + return -ENOMEM; + } + file_info->ept = ept; + + /* if router device, load the modem */ + if (inode->i_rdev == msm_rpcrouter_devno) { + pil = msm_rpcrouter_load_modem(); + if (IS_ERR(pil)) { + kfree(file_info); + msm_rpcrouter_destroy_local_endpoint(ept); + return PTR_ERR(pil); + } + file_info->modem_pil = pil; + } + + filp->private_data = file_info; + return 0; +} + +static int rpcrouter_release(struct inode *inode, struct file *filp) +{ + struct rpcrouter_file_info *file_info = filp->private_data; + struct msm_rpc_endpoint *ept; + static unsigned int rpcrouter_release_cnt; + + ept = (struct msm_rpc_endpoint *) file_info->ept; + + /* A user program with many files open when ends abruptly, + * will cause a flood of REMOVE_CLIENT messages to the + * remote processor. This will cause remote processors + * internal queue to overflow. Inserting a sleep here + * regularly is the effecient option. + */ + if (rpcrouter_release_cnt++ % 2) + msleep(1); + + /* if router device, unload the modem */ + if (inode->i_rdev == msm_rpcrouter_devno) + msm_rpcrouter_unload_modem(file_info->modem_pil); + + kfree(file_info); + return msm_rpcrouter_destroy_local_endpoint(ept); +} + +static ssize_t rpcrouter_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct rpcrouter_file_info *file_info = filp->private_data; + struct msm_rpc_endpoint *ept; + struct rr_fragment *frag, *next; + int rc; + + ept = (struct msm_rpc_endpoint *) file_info->ept; + + rc = __msm_rpc_read(ept, &frag, count, -1); + if (rc < 0) + return rc; + + count = rc; + + while (frag != NULL) { + if (copy_to_user(buf, frag->data, frag->length)) { + printk(KERN_ERR + "rpcrouter: could not copy all read data to user!\n"); + rc = -EFAULT; + } + buf += frag->length; + next = frag->next; + kfree(frag); + frag = next; + } + + return rc; +} + +static ssize_t rpcrouter_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct rpcrouter_file_info *file_info = filp->private_data; + struct msm_rpc_endpoint *ept; + int rc = 0; + void *k_buffer; + + ept = (struct msm_rpc_endpoint *) file_info->ept; + + /* A check for safety, this seems non-standard */ + if (count > SAFETY_MEM_SIZE) + return -EINVAL; + + k_buffer = kmalloc(count, GFP_KERNEL); + if (!k_buffer) + return -ENOMEM; + + if (copy_from_user(k_buffer, buf, count)) { + rc = -EFAULT; + goto write_out_free; + } + + rc = msm_rpc_write(ept, k_buffer, count); + if (rc < 0) + goto write_out_free; + + rc = count; +write_out_free: + kfree(k_buffer); + return rc; +} + +/* RPC VFS Poll Implementation + * + * POLLRDHUP - restart in progress + * POLLOUT - writes accepted (without blocking) + * POLLIN - data ready to read + * + * The restart state consists of several different phases including a client + * notification and a server restart. If the server has been restarted, then + * reads and writes can be performed and the POLLOUT bit will be set. If a + * restart is in progress, but the server hasn't been restarted, then only the + * POLLRDHUP is active and reads and writes will block. See the table + * below for a summary. POLLRDHUP is cleared once a call to msm_rpc_write_pkt + * or msm_rpc_read_pkt returns ENETRESET. + * + * POLLOUT POLLRDHUP + * 1 0 Normal operation + * 0 1 Restart in progress and server hasn't restarted yet + * 1 1 Server has been restarted, but client has + * not been notified of a restart by a return code + * of ENETRESET from msm_rpc_write_pkt or + * msm_rpc_read_pkt. + */ +static unsigned int rpcrouter_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct rpcrouter_file_info *file_info = filp->private_data; + struct msm_rpc_endpoint *ept; + unsigned mask = 0; + + ept = (struct msm_rpc_endpoint *) file_info->ept; + + poll_wait(filp, &ept->wait_q, wait); + poll_wait(filp, &ept->restart_wait, wait); + + if (!list_empty(&ept->read_q)) + mask |= POLLIN; + if (!(ept->restart_state & RESTART_PEND_SVR)) + mask |= POLLOUT; + if (ept->restart_state != 0) + mask |= POLLRDHUP; + + return mask; +} + +static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct rpcrouter_file_info *file_info = filp->private_data; + struct msm_rpc_endpoint *ept; + struct rpcrouter_ioctl_server_args server_args; + int rc = 0; + uint32_t n; + + ept = (struct msm_rpc_endpoint *) file_info->ept; + switch (cmd) { + + case RPC_ROUTER_IOCTL_GET_VERSION: + n = RPC_ROUTER_VERSION_V1; + rc = put_user(n, (unsigned int *) arg); + break; + + case RPC_ROUTER_IOCTL_GET_MTU: + /* the pacmark word reduces the actual payload + * possible per message + */ + n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t); + rc = put_user(n, (unsigned int *) arg); + break; + + case RPC_ROUTER_IOCTL_REGISTER_SERVER: + rc = copy_from_user(&server_args, (void *) arg, + sizeof(server_args)); + if (rc < 0) + break; + msm_rpc_register_server(ept, + server_args.prog, + server_args.vers); + break; + + case RPC_ROUTER_IOCTL_UNREGISTER_SERVER: + rc = copy_from_user(&server_args, (void *) arg, + sizeof(server_args)); + if (rc < 0) + break; + + msm_rpc_unregister_server(ept, + server_args.prog, + server_args.vers); + break; + + case RPC_ROUTER_IOCTL_CLEAR_NETRESET: + msm_rpc_clear_netreset(ept); + break; + + case RPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE: + rc = msm_rpc_get_curr_pkt_size(ept); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static struct file_operations rpcrouter_server_fops = { + .owner = THIS_MODULE, + .open = rpcrouter_open, + .release = rpcrouter_release, + .read = rpcrouter_read, + .write = rpcrouter_write, + .poll = rpcrouter_poll, + .unlocked_ioctl = rpcrouter_ioctl, +}; + +static struct file_operations rpcrouter_router_fops = { + .owner = THIS_MODULE, + .open = rpcrouter_open, + .release = rpcrouter_release, + .read = rpcrouter_read, + .write = rpcrouter_write, + .poll = rpcrouter_poll, + .unlocked_ioctl = rpcrouter_ioctl, +}; + +int msm_rpcrouter_create_server_cdev(struct rr_server *server) +{ + int rc; + uint32_t dev_vers; + unsigned long flags; + + spin_lock_irqsave(&server_cdev_lock, flags); + if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) { + spin_unlock_irqrestore(&server_cdev_lock, flags); + printk(KERN_ERR + "rpcrouter: Minor numbers exhausted - Increase " + "RPCROUTER_MAX_REMOTE_SERVERS\n"); + return -ENOBUFS; + } + + /* Servers with bit 31 set are remote msm servers with hashkey version. + * Servers with bit 31 not set are remote msm servers with + * backwards compatible version type in which case the minor number + * (lower 16 bits) is set to zero. + * + */ + if ((server->vers & 0x80000000)) + dev_vers = server->vers; + else + dev_vers = server->vers & 0xffff0000; + + server->device_number = + MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++); + spin_unlock_irqrestore(&server_cdev_lock, flags); + + server->device = + device_create(msm_rpcrouter_class, rpcrouter_device, + server->device_number, NULL, "%.8x:%.8x", + server->prog, dev_vers); + if (IS_ERR(server->device)) { + printk(KERN_ERR + "rpcrouter: Unable to create device (%ld)\n", + PTR_ERR(server->device)); + return PTR_ERR(server->device);; + } + + cdev_init(&server->cdev, &rpcrouter_server_fops); + server->cdev.owner = THIS_MODULE; + + rc = cdev_add(&server->cdev, server->device_number, 1); + if (rc < 0) { + printk(KERN_ERR + "rpcrouter: Unable to add chrdev (%d)\n", rc); + device_destroy(msm_rpcrouter_class, server->device_number); + return rc; + } + return 0; +} + +/* for backward compatible version type (31st bit cleared) + * clearing minor number (lower 16 bits) in device name + * is neccessary for driver binding + */ +int msm_rpcrouter_create_server_pdev(struct rr_server *server) +{ + server->p_device.base.id = (server->vers & RPC_VERSION_MODE_MASK) ? + server->vers : + (server->vers & RPC_VERSION_MAJOR_MASK); + server->p_device.base.name = server->pdev_name; + + server->p_device.prog = server->prog; + server->p_device.vers = server->vers; + + platform_device_register(&server->p_device.base); + return 0; +} + +int msm_rpcrouter_init_devices(void) +{ + int rc; + int major; + + /* Create the device nodes */ + msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc"); + if (IS_ERR(msm_rpcrouter_class)) { + rc = -ENOMEM; + printk(KERN_ERR + "rpcrouter: failed to create oncrpc class\n"); + goto fail; + } + + rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0, + RPCROUTER_MAX_REMOTE_SERVERS + 1, + "oncrpc"); + if (rc < 0) { + printk(KERN_ERR + "rpcrouter: Failed to alloc chardev region (%d)\n", rc); + goto fail_destroy_class; + } + + major = MAJOR(msm_rpcrouter_devno); + rpcrouter_device = device_create(msm_rpcrouter_class, NULL, + msm_rpcrouter_devno, NULL, "%.8x:%d", + 0, 0); + if (IS_ERR(rpcrouter_device)) { + rc = -ENOMEM; + goto fail_unregister_cdev_region; + } + + cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops); + rpcrouter_cdev.owner = THIS_MODULE; + + rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1); + if (rc < 0) + goto fail_destroy_device; + + return 0; + +fail_destroy_device: + device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); +fail_unregister_cdev_region: + unregister_chrdev_region(msm_rpcrouter_devno, + RPCROUTER_MAX_REMOTE_SERVERS + 1); +fail_destroy_class: + class_destroy(msm_rpcrouter_class); +fail: + return rc; +} + +void msm_rpcrouter_exit_devices(void) +{ + cdev_del(&rpcrouter_cdev); + device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); + unregister_chrdev_region(msm_rpcrouter_devno, + RPCROUTER_MAX_REMOTE_SERVERS + 1); + class_destroy(msm_rpcrouter_class); +} + diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c new file mode 100644 index 00000000000..3561c2198e2 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -0,0 +1,618 @@ +/* arch/arm/mach-msm/rpc_servers.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "smd_rpcrouter.h" + +static struct msm_rpc_endpoint *endpoint; + +#define FLAG_REGISTERED 0x0001 + +static LIST_HEAD(rpc_server_list); +static DEFINE_MUTEX(rpc_server_list_lock); +static int rpc_servers_active; +static struct wake_lock rpc_servers_wake_lock; +static struct msm_rpc_xdr server_xdr; +static uint32_t current_xid; + +static void rpc_server_register(struct msm_rpc_server *server) +{ + int rc; + rc = msm_rpc_register_server(endpoint, server->prog, server->vers); + if (rc < 0) + printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n", + server, server->prog, server->vers); +} + +static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) +{ + struct msm_rpc_server *server; + + mutex_lock(&rpc_server_list_lock); + list_for_each_entry(server, &rpc_server_list, list) { + if ((server->prog == prog) && + msm_rpc_is_compatible_version(server->vers, vers)) { + mutex_unlock(&rpc_server_list_lock); + return server; + } + } + mutex_unlock(&rpc_server_list_lock); + return NULL; +} + +static void rpc_server_register_all(void) +{ + struct msm_rpc_server *server; + + mutex_lock(&rpc_server_list_lock); + list_for_each_entry(server, &rpc_server_list, list) { + if (!(server->flags & FLAG_REGISTERED)) { + rpc_server_register(server); + server->flags |= FLAG_REGISTERED; + } + } + mutex_unlock(&rpc_server_list_lock); +} + +int msm_rpc_create_server(struct msm_rpc_server *server) +{ + void *buf; + + /* make sure we're in a sane state first */ + server->flags = 0; + INIT_LIST_HEAD(&server->list); + mutex_init(&server->cb_req_lock); + + server->version = 1; + + xdr_init(&server->cb_xdr); + buf = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + xdr_init_output(&server->cb_xdr, buf, MSM_RPC_MSGSIZE_MAX); + + server->cb_ept = server->cb_xdr.ept = msm_rpc_open(); + if (IS_ERR(server->cb_ept)) { + xdr_clean_output(&server->cb_xdr); + return PTR_ERR(server->cb_ept); + } + + server->cb_ept->flags = MSM_RPC_UNINTERRUPTIBLE; + server->cb_ept->dst_prog = cpu_to_be32(server->prog | 0x01000000); + server->cb_ept->dst_vers = cpu_to_be32(server->vers); + + mutex_lock(&rpc_server_list_lock); + list_add(&server->list, &rpc_server_list); + if (rpc_servers_active) { + rpc_server_register(server); + server->flags |= FLAG_REGISTERED; + } + mutex_unlock(&rpc_server_list_lock); + + return 0; +} +EXPORT_SYMBOL(msm_rpc_create_server); + +int msm_rpc_create_server2(struct msm_rpc_server *server) +{ + int rc; + + rc = msm_rpc_create_server(server); + server->version = 2; + + return rc; +} +EXPORT_SYMBOL(msm_rpc_create_server2); + +static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, + uint32_t xid, uint32_t accept_status) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf)); + if (rc == -ENETRESET) { + /* Modem restarted, drop reply, clear state */ + msm_rpc_clear_netreset(client); + } + if (rc < 0) + printk(KERN_ERR + "%s: could not write response: %d\n", + __FUNCTION__, rc); + + return rc; +} + +/* + * Interface to be used to start accepted reply message for a + * request. Returns the buffer pointer to attach any payload. + * Should call msm_rpc_server_send_accepted_reply to complete sending + * reply. Marshaling should be handled by user for the payload. + * + * server: pointer to server data structure + * + * xid: transaction id. Has to be same as the one in request. + * + * accept_status: acceptance status + * + * Return Value: + * pointer to buffer to attach the payload. + */ +void *msm_rpc_server_start_accepted_reply(struct msm_rpc_server *server, + uint32_t xid, uint32_t accept_status) +{ + struct rpc_reply_hdr *reply; + + mutex_lock(&server_xdr.out_lock); + + reply = (struct rpc_reply_hdr *)server_xdr.out_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + server_xdr.out_index += sizeof(*reply); + + return reply + 1; +} +EXPORT_SYMBOL(msm_rpc_server_start_accepted_reply); + +/* + * Interface to be used to send accepted reply for a request. + * msm_rpc_server_start_accepted_reply should have been called before. + * Marshaling should be handled by user for the payload. + * + * server: pointer to server data structure + * + * size: additional payload size + * + * Return Value: + * 0 on success, otherwise returns an error code. + */ +int msm_rpc_server_send_accepted_reply(struct msm_rpc_server *server, + uint32_t size) +{ + int rc = 0; + + server_xdr.out_index += size; + rc = msm_rpc_write(endpoint, server_xdr.out_buf, + server_xdr.out_index); + if (rc > 0) + rc = 0; + + mutex_unlock(&server_xdr.out_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_server_send_accepted_reply); + +/* + * Interface to be used to send a server callback request. + * If the request takes any arguments or expects any return, the user + * should handle it in 'arg_func' and 'ret_func' respectively. + * Marshaling and Unmarshaling should be handled by the user in argument + * and return functions. + * + * server: pointer to server data sturcture + * + * clnt_info: pointer to client information data structure. + * callback will be sent to this client. + * + * cb_proc: callback procedure being requested + * + * arg_func: argument function pointer. 'buf' is where arguments needs to + * be filled. 'data' is arg_data. + * + * ret_func: return function pointer. 'buf' is where returned data should + * be read from. 'data' is ret_data. + * + * arg_data: passed as an input parameter to argument function. + * + * ret_data: passed as an input parameter to return function. + * + * timeout: timeout for reply wait in jiffies. If negative timeout is + * specified a default timeout of 10s is used. + * + * Return Value: + * 0 on success, otherwise an error code is returned. + */ +int msm_rpc_server_cb_req(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + uint32_t cb_proc, + int (*arg_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *ret_data, long timeout) +{ + struct rpc_reply_hdr *rpc_rsp; + void *buffer; + int rc = 0; + uint32_t req_xid; + + if (!clnt_info) + return -EINVAL; + + mutex_lock(&server->cb_req_lock); + + msm_rpc_setup_req((struct rpc_request_hdr *)server->cb_xdr.out_buf, + (server->prog | 0x01000000), + be32_to_cpu(clnt_info->vers), cb_proc); + server->cb_xdr.out_index = sizeof(struct rpc_request_hdr); + req_xid = *(uint32_t *)server->cb_xdr.out_buf; + + if (arg_func) { + rc = arg_func(server, (void *)((struct rpc_request_hdr *) + server->cb_xdr.out_buf + 1), + arg_data); + if (rc < 0) + goto release_locks; + else + server->cb_xdr.out_index += rc; + } + + server->cb_ept->dst_pid = clnt_info->pid; + server->cb_ept->dst_cid = clnt_info->cid; + rc = msm_rpc_write(server->cb_ept, server->cb_xdr.out_buf, + server->cb_xdr.out_index); + if (rc < 0) { + pr_err("%s: couldn't send RPC CB request:%d\n", __func__, rc); + goto release_locks; + } else + rc = 0; + + if (timeout < 0) + timeout = msecs_to_jiffies(10000); + + do { + buffer = NULL; + rc = msm_rpc_read(server->cb_ept, &buffer, -1, timeout); + xdr_init_input(&server->cb_xdr, buffer, rc); + if ((rc < ((int)(sizeof(uint32_t) * 2))) || + (be32_to_cpu(*((uint32_t *)buffer + 1)) != 1)) { + printk(KERN_ERR "%s: Invalid reply: %d\n", + __func__, rc); + goto free_and_release; + } + + rpc_rsp = (struct rpc_reply_hdr *)server->cb_xdr.in_buf; + if (req_xid != rpc_rsp->xid) { + pr_info("%s: xid mismatch, req %d reply %d\n", + __func__, be32_to_cpu(req_xid), + be32_to_cpu(rpc_rsp->xid)); + xdr_clean_input(&server->cb_xdr); + rc = timeout; + /* timeout is not adjusted, but it is not critical */ + } else + rc = 0; + } while (rc); + + if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("%s: RPC cb req was denied! %d\n", __func__, + be32_to_cpu(rpc_rsp->reply_stat)); + rc = -EPERM; + goto free_and_release; + } + + if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + pr_err("%s: RPC cb req was not successful (%d)\n", __func__, + be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); + rc = -EINVAL; + goto free_and_release; + } + + if (ret_func) + rc = ret_func(server, (void *)(rpc_rsp + 1), ret_data); + +free_and_release: + xdr_clean_input(&server->cb_xdr); + server->cb_xdr.out_index = 0; +release_locks: + mutex_unlock(&server->cb_req_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_server_cb_req); + +/* + * Interface to be used to send a server callback request. + * If the request takes any arguments or expects any return, the user + * should handle it in 'arg_func' and 'ret_func' respectively. + * Marshaling and Unmarshaling should be handled by the user in argument + * and return functions. + * + * server: pointer to server data sturcture + * + * clnt_info: pointer to client information data structure. + * callback will be sent to this client. + * + * cb_proc: callback procedure being requested + * + * arg_func: argument function pointer. 'xdr' is the xdr being used. + * 'data' is arg_data. + * + * ret_func: return function pointer. 'xdr' is the xdr being used. + * 'data' is ret_data. + * + * arg_data: passed as an input parameter to argument function. + * + * ret_data: passed as an input parameter to return function. + * + * timeout: timeout for reply wait in jiffies. If negative timeout is + * specified a default timeout of 10s is used. + * + * Return Value: + * 0 on success, otherwise an error code is returned. + */ +int msm_rpc_server_cb_req2(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + uint32_t cb_proc, + int (*arg_func)(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_server *server, + struct msm_rpc_xdr *xdr, void *data), + void *ret_data, long timeout) +{ + int size = 0; + struct rpc_reply_hdr rpc_rsp; + void *buffer; + int rc = 0; + uint32_t req_xid; + + if (!clnt_info) + return -EINVAL; + + mutex_lock(&server->cb_req_lock); + + xdr_start_request(&server->cb_xdr, (server->prog | 0x01000000), + be32_to_cpu(clnt_info->vers), cb_proc); + req_xid = be32_to_cpu(*(uint32_t *)server->cb_xdr.out_buf); + if (arg_func) { + rc = arg_func(server, &server->cb_xdr, arg_data); + if (rc < 0) + goto release_locks; + else + size += rc; + } + + server->cb_ept->dst_pid = clnt_info->pid; + server->cb_ept->dst_cid = clnt_info->cid; + rc = xdr_send_msg(&server->cb_xdr); + if (rc < 0) { + pr_err("%s: couldn't send RPC CB request:%d\n", __func__, rc); + goto release_locks; + } else + rc = 0; + + if (timeout < 0) + timeout = msecs_to_jiffies(10000); + + do { + buffer = NULL; + rc = msm_rpc_read(server->cb_ept, &buffer, -1, timeout); + if (rc < 0) { + server->cb_xdr.out_index = 0; + goto release_locks; + } + + xdr_init_input(&server->cb_xdr, buffer, rc); + rc = xdr_recv_reply(&server->cb_xdr, &rpc_rsp); + if (rc || (rpc_rsp.type != 1)) { + printk(KERN_ERR "%s: Invalid reply :%d\n", + __func__, rc); + rc = -EINVAL; + goto free_and_release; + } + + if (req_xid != rpc_rsp.xid) { + pr_info("%s: xid mismatch, req %d reply %d\n", + __func__, req_xid, rpc_rsp.xid); + xdr_clean_input(&server->cb_xdr); + rc = timeout; + /* timeout is not adjusted, but it is not critical */ + } else + rc = 0; + + } while (rc); + + if (rpc_rsp.reply_stat != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("%s: RPC cb req was denied! %d\n", __func__, + rpc_rsp.reply_stat); + rc = -EPERM; + goto free_and_release; + } + + if (rpc_rsp.data.acc_hdr.accept_stat != RPC_ACCEPTSTAT_SUCCESS) { + pr_err("%s: RPC cb req was not successful (%d)\n", __func__, + rpc_rsp.data.acc_hdr.accept_stat); + rc = -EINVAL; + goto free_and_release; + } + + if (ret_func) + rc = ret_func(server, &server->cb_xdr, ret_data); + +free_and_release: + xdr_clean_input(&server->cb_xdr); + server->cb_xdr.out_index = 0; +release_locks: + mutex_unlock(&server->cb_req_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_server_cb_req2); + +void msm_rpc_server_get_requesting_client(struct msm_rpc_client_info *clnt_info) +{ + if (!clnt_info) + return; + + get_requesting_client(endpoint, current_xid, clnt_info); +} + +static int rpc_servers_thread(void *data) +{ + void *buffer, *buf; + struct rpc_request_hdr req; + struct rpc_request_hdr *req1; + struct msm_rpc_server *server; + int rc; + + xdr_init(&server_xdr); + server_xdr.ept = endpoint; + + buf = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + xdr_init_output(&server_xdr, buf, MSM_RPC_MSGSIZE_MAX); + + for (;;) { + wake_unlock(&rpc_servers_wake_lock); + rc = wait_event_interruptible(endpoint->wait_q, + !list_empty(&endpoint->read_q)); + wake_lock(&rpc_servers_wake_lock); + + rc = msm_rpc_read(endpoint, &buffer, -1, -1); + if (rc < 0) { + printk(KERN_ERR "%s: could not read: %d\n", + __FUNCTION__, rc); + break; + } + + req1 = (struct rpc_request_hdr *)buffer; + current_xid = req1->xid; + + xdr_init_input(&server_xdr, buffer, rc); + xdr_recv_req(&server_xdr, &req); + + server = rpc_server_find(req.prog, req.vers); + + if (req.rpc_vers != 2) + goto free_buffer; + if (req.type != 0) + goto free_buffer; + if (!server) { + rpc_send_accepted_void_reply( + endpoint, req.xid, + RPC_ACCEPTSTAT_PROG_UNAVAIL); + goto free_buffer; + } + + if (server->version == 2) + rc = server->rpc_call2(server, &req, &server_xdr); + else { + req1->type = be32_to_cpu(req1->type); + req1->xid = be32_to_cpu(req1->xid); + req1->rpc_vers = be32_to_cpu(req1->rpc_vers); + req1->prog = be32_to_cpu(req1->prog); + req1->vers = be32_to_cpu(req1->vers); + req1->procedure = be32_to_cpu(req1->procedure); + + rc = server->rpc_call(server, req1, rc); + } + + if (rc == 0) { + msm_rpc_server_start_accepted_reply( + server, req.xid, + RPC_ACCEPTSTAT_SUCCESS); + msm_rpc_server_send_accepted_reply(server, 0); + } else if (rc < 0) { + msm_rpc_server_start_accepted_reply( + server, req.xid, + RPC_ACCEPTSTAT_PROC_UNAVAIL); + msm_rpc_server_send_accepted_reply(server, 0); + } + free_buffer: + xdr_clean_input(&server_xdr); + server_xdr.out_index = 0; + } + do_exit(0); +} + +static int rpcservers_probe(struct platform_device *pdev) +{ + struct task_struct *server_thread; + + endpoint = msm_rpc_open(); + if (IS_ERR(endpoint)) + return PTR_ERR(endpoint); + + /* we're online -- register any servers installed beforehand */ + rpc_servers_active = 1; + current_xid = 0; + rpc_server_register_all(); + + /* start the kernel thread */ + server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd"); + if (IS_ERR(server_thread)) + return PTR_ERR(server_thread); + + return 0; +} + +static struct platform_driver rpcservers_driver = { + .probe = rpcservers_probe, + .driver = { + .name = "oncrpc_router", + .owner = THIS_MODULE, + }, +}; + +static int __init rpc_servers_init(void) +{ + wake_lock_init(&rpc_servers_wake_lock, WAKE_LOCK_SUSPEND, "rpc_server"); + return platform_driver_register(&rpcservers_driver); +} + +module_init(rpc_servers_init); + +MODULE_DESCRIPTION("MSM RPC Servers"); +MODULE_AUTHOR("Iliyan Malchev "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/smd_rpcrouter_xdr.c b/arch/arm/mach-msm/smd_rpcrouter_xdr.c new file mode 100644 index 00000000000..17935161754 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_xdr.c @@ -0,0 +1,415 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * SMD RPCROUTER XDR module. + */ + +#include +#include +#include +#include +#include +#include + +#include + +int xdr_send_uint32(struct msm_rpc_xdr *xdr, const uint32_t *value) +{ + if ((xdr->out_index + sizeof(uint32_t)) > xdr->out_size) { + pr_err("%s: xdr out buffer full\n", __func__); + return -1; + } + + *(uint32_t *)(xdr->out_buf + xdr->out_index) = cpu_to_be32(*value); + xdr->out_index += sizeof(uint32_t); + return 0; +} + +int xdr_send_int8(struct msm_rpc_xdr *xdr, const int8_t *value) +{ + return xdr_send_uint32(xdr, (uint32_t *)value); +} + +int xdr_send_uint8(struct msm_rpc_xdr *xdr, const uint8_t *value) +{ + return xdr_send_uint32(xdr, (uint32_t *)value); +} + +int xdr_send_int16(struct msm_rpc_xdr *xdr, const int16_t *value) +{ + return xdr_send_uint32(xdr, (uint32_t *)value); +} + +int xdr_send_uint16(struct msm_rpc_xdr *xdr, const uint16_t *value) +{ + return xdr_send_uint32(xdr, (uint32_t *)value); +} + +int xdr_send_int32(struct msm_rpc_xdr *xdr, const int32_t *value) +{ + return xdr_send_uint32(xdr, (uint32_t *)value); +} + +int xdr_send_bytes(struct msm_rpc_xdr *xdr, const void **data, + uint32_t *size) +{ + void *buf = xdr->out_buf + xdr->out_index; + uint32_t temp; + + if (!size || !data || !*data) + return -1; + + temp = *size; + if (temp & 0x3) + temp += 4 - (temp & 0x3); + + temp += sizeof(uint32_t); + if ((xdr->out_index + temp) > xdr->out_size) { + pr_err("%s: xdr out buffer full\n", __func__); + return -1; + } + + *((uint32_t *)buf) = cpu_to_be32(*size); + buf += sizeof(uint32_t); + memcpy(buf, *data, *size); + buf += *size; + if (*size & 0x3) { + memset(buf, 0, 4 - (*size & 0x3)); + buf += 4 - (*size & 0x3); + } + + xdr->out_index = buf - xdr->out_buf; + return 0; +} + +int xdr_recv_uint32(struct msm_rpc_xdr *xdr, uint32_t *value) +{ + if ((xdr->in_index + sizeof(uint32_t)) > xdr->in_size) { + pr_err("%s: xdr in buffer full\n", __func__); + return -1; + } + + *value = be32_to_cpu(*(uint32_t *)(xdr->in_buf + xdr->in_index)); + xdr->in_index += sizeof(uint32_t); + return 0; +} + +int xdr_recv_int8(struct msm_rpc_xdr *xdr, int8_t *value) +{ + return xdr_recv_uint32(xdr, (uint32_t *)value); +} + +int xdr_recv_uint8(struct msm_rpc_xdr *xdr, uint8_t *value) +{ + return xdr_recv_uint32(xdr, (uint32_t *)value); +} + +int xdr_recv_int16(struct msm_rpc_xdr *xdr, int16_t *value) +{ + return xdr_recv_uint32(xdr, (uint32_t *)value); +} + +int xdr_recv_uint16(struct msm_rpc_xdr *xdr, uint16_t *value) +{ + return xdr_recv_uint32(xdr, (uint32_t *)value); +} + +int xdr_recv_int32(struct msm_rpc_xdr *xdr, int32_t *value) +{ + return xdr_recv_uint32(xdr, (uint32_t *)value); +} + +int xdr_recv_bytes(struct msm_rpc_xdr *xdr, void **data, + uint32_t *size) +{ + void *buf = xdr->in_buf + xdr->in_index; + uint32_t temp; + + if (!size || !data) + return -1; + + *size = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + temp = *size; + if (temp & 0x3) + temp += 4 - (temp & 0x3); + + temp += sizeof(uint32_t); + if ((xdr->in_index + temp) > xdr->in_size) { + pr_err("%s: xdr in buffer full\n", __func__); + return -1; + } + + if (*size) { + *data = kmalloc(*size, GFP_KERNEL); + if (!*data) + return -1; + + memcpy(*data, buf, *size); + + buf += *size; + if (*size & 0x3) + buf += 4 - (*size & 0x3); + } else + *data = NULL; + + xdr->in_index = buf - xdr->in_buf; + return 0; +} + +int xdr_send_pointer(struct msm_rpc_xdr *xdr, void **obj, + uint32_t obj_size, void *xdr_op) +{ + uint32_t ptr_valid, rc; + + ptr_valid = (*obj != NULL); + + rc = xdr_send_uint32(xdr, &ptr_valid); + if (rc) + return rc; + + if (!ptr_valid) + return 0; + + return ((int (*) (struct msm_rpc_xdr *, void *))xdr_op)(xdr, *obj); +} + +int xdr_recv_pointer(struct msm_rpc_xdr *xdr, void **obj, + uint32_t obj_size, void *xdr_op) +{ + uint32_t rc, ptr_valid = 0; + + rc = xdr_recv_uint32(xdr, &ptr_valid); + if (rc) + return rc; + + if (!ptr_valid) { + *obj = NULL; + return 0; + } + + *obj = kmalloc(obj_size, GFP_KERNEL); + if (!*obj) + return -1; + + rc = ((int (*) (struct msm_rpc_xdr *, void *))xdr_op)(xdr, *obj); + if (rc) + kfree(*obj); + + return rc; +} + +int xdr_send_array(struct msm_rpc_xdr *xdr, void **addr, uint32_t *size, + uint32_t maxsize, uint32_t elm_size, void *xdr_op) +{ + int i, rc; + void *tmp_addr = *addr; + + if (!size || !tmp_addr || (*size > maxsize) || !xdr_op) + return -1; + + rc = xdr_send_uint32(xdr, size); + if (rc) + return rc; + + for (i = 0; i < *size; i++) { + rc = ((int (*) (struct msm_rpc_xdr *, void *))xdr_op) + (xdr, tmp_addr); + if (rc) + return rc; + + tmp_addr += elm_size; + } + + return 0; +} + +int xdr_recv_array(struct msm_rpc_xdr *xdr, void **addr, uint32_t *size, + uint32_t maxsize, uint32_t elm_size, void *xdr_op) +{ + int i, rc; + void *tmp_addr; + + if (!size || !xdr_op) + return -1; + + rc = xdr_recv_uint32(xdr, size); + if (rc) + return rc; + + if (*size > maxsize) + return -1; + + tmp_addr = kmalloc((*size * elm_size), GFP_KERNEL); + if (!tmp_addr) + return -1; + + *addr = tmp_addr; + for (i = 0; i < *size; i++) { + rc = ((int (*) (struct msm_rpc_xdr *, void *))xdr_op) + (xdr, tmp_addr); + if (rc) { + kfree(*addr); + *addr = NULL; + return rc; + } + + tmp_addr += elm_size; + } + + return 0; +} + +int xdr_recv_req(struct msm_rpc_xdr *xdr, struct rpc_request_hdr *req) +{ + int rc = 0; + if (!req) + return -1; + + rc |= xdr_recv_uint32(xdr, &req->xid); /* xid */ + rc |= xdr_recv_uint32(xdr, &req->type); /* type */ + rc |= xdr_recv_uint32(xdr, &req->rpc_vers); /* rpc_vers */ + rc |= xdr_recv_uint32(xdr, &req->prog); /* prog */ + rc |= xdr_recv_uint32(xdr, &req->vers); /* vers */ + rc |= xdr_recv_uint32(xdr, &req->procedure); /* procedure */ + rc |= xdr_recv_uint32(xdr, &req->cred_flavor); /* cred_flavor */ + rc |= xdr_recv_uint32(xdr, &req->cred_length); /* cred_length */ + rc |= xdr_recv_uint32(xdr, &req->verf_flavor); /* verf_flavor */ + rc |= xdr_recv_uint32(xdr, &req->verf_length); /* verf_length */ + + return rc; +} + +int xdr_recv_reply(struct msm_rpc_xdr *xdr, struct rpc_reply_hdr *reply) +{ + int rc = 0; + + if (!reply) + return -1; + + rc |= xdr_recv_uint32(xdr, &reply->xid); /* xid */ + rc |= xdr_recv_uint32(xdr, &reply->type); /* type */ + rc |= xdr_recv_uint32(xdr, &reply->reply_stat); /* reply_stat */ + + /* acc_hdr */ + if (reply->reply_stat == RPCMSG_REPLYSTAT_ACCEPTED) { + rc |= xdr_recv_uint32(xdr, &reply->data.acc_hdr.verf_flavor); + rc |= xdr_recv_uint32(xdr, &reply->data.acc_hdr.verf_length); + rc |= xdr_recv_uint32(xdr, &reply->data.acc_hdr.accept_stat); + } + + return rc; +} + +int xdr_start_request(struct msm_rpc_xdr *xdr, uint32_t prog, + uint32_t ver, uint32_t proc) +{ + mutex_lock(&xdr->out_lock); + + /* TODO: replace below function with its implementation */ + msm_rpc_setup_req((struct rpc_request_hdr *)xdr->out_buf, + prog, ver, proc); + + xdr->out_index = sizeof(struct rpc_request_hdr); + return 0; +} + +int xdr_start_accepted_reply(struct msm_rpc_xdr *xdr, uint32_t accept_status) +{ + struct rpc_reply_hdr *reply; + + mutex_lock(&xdr->out_lock); + + /* TODO: err if xdr is not cb xdr */ + reply = (struct rpc_reply_hdr *)xdr->out_buf; + + /* TODO: use xdr functions instead */ + reply->xid = ((struct rpc_request_hdr *)(xdr->in_buf))->xid; + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + xdr->out_index = sizeof(*reply); + return 0; +} + +int xdr_send_msg(struct msm_rpc_xdr *xdr) +{ + int rc = 0; + + rc = msm_rpc_write(xdr->ept, xdr->out_buf, + xdr->out_index); + if (rc > 0) + rc = 0; + + mutex_unlock(&xdr->out_lock); + return rc; +} + +void xdr_init(struct msm_rpc_xdr *xdr) +{ + mutex_init(&xdr->out_lock); + init_waitqueue_head(&xdr->in_buf_wait_q); + + xdr->in_buf = NULL; + xdr->in_size = 0; + xdr->in_index = 0; + + xdr->out_buf = NULL; + xdr->out_size = 0; + xdr->out_index = 0; +} + +void xdr_init_input(struct msm_rpc_xdr *xdr, void *buf, uint32_t size) +{ + wait_event(xdr->in_buf_wait_q, !(xdr->in_buf)); + + xdr->in_buf = buf; + xdr->in_size = size; + xdr->in_index = 0; +} + +void xdr_init_output(struct msm_rpc_xdr *xdr, void *buf, uint32_t size) +{ + xdr->out_buf = buf; + xdr->out_size = size; + xdr->out_index = 0; +} + +void xdr_clean_input(struct msm_rpc_xdr *xdr) +{ + kfree(xdr->in_buf); + xdr->in_size = 0; + xdr->in_index = 0; + xdr->in_buf = NULL; + + wake_up(&xdr->in_buf_wait_q); +} + +void xdr_clean_output(struct msm_rpc_xdr *xdr) +{ + kfree(xdr->out_buf); + xdr->out_buf = NULL; + xdr->out_size = 0; + xdr->out_index = 0; +} + +uint32_t xdr_read_avail(struct msm_rpc_xdr *xdr) +{ + return xdr->in_size; +} diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c new file mode 100644 index 00000000000..68e0f41a35a --- /dev/null +++ b/arch/arm/mach-msm/smd_tty.c @@ -0,0 +1,604 @@ +/* arch/arm/mach-msm/smd_tty.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "smd_private.h" + +#define MAX_SMD_TTYS 37 +#define MAX_TTY_BUF_SIZE 2048 + +static DEFINE_MUTEX(smd_tty_lock); + +static uint smd_tty_modem_wait; +module_param_named(modem_wait, smd_tty_modem_wait, + uint, S_IRUGO | S_IWUSR | S_IWGRP); + +struct smd_tty_info { + smd_channel_t *ch; + struct tty_struct *tty; + struct wake_lock wake_lock; + int open_count; + struct tasklet_struct tty_tsklt; + struct timer_list buf_req_timer; + struct completion ch_allocated; + struct platform_driver driver; + void *pil; + int in_reset; + int in_reset_updated; + int is_open; + wait_queue_head_t ch_opened_wait_queue; + spinlock_t reset_lock; + struct smd_config *smd; +}; + +/** + * SMD port configuration. + * + * @tty_dev_index Index into smd_tty[] + * @port_name Name of the SMD port + * @dev_name Name of the TTY Device (if NULL, @port_name is used) + * @edge SMD edge + */ +struct smd_config { + uint32_t tty_dev_index; + const char *port_name; + const char *dev_name; + uint32_t edge; +}; + +static struct smd_config smd_configs[] = { + {0, "DS", NULL, SMD_APPS_MODEM}, + {1, "APPS_FM", NULL, SMD_APPS_WCNSS}, + {2, "APPS_RIVA_BT_ACL", NULL, SMD_APPS_WCNSS}, + {3, "APPS_RIVA_BT_CMD", NULL, SMD_APPS_WCNSS}, + {4, "MBALBRIDGE", NULL, SMD_APPS_MODEM}, + {5, "APPS_RIVA_ANT_CMD", NULL, SMD_APPS_WCNSS}, + {6, "APPS_RIVA_ANT_DATA", NULL, SMD_APPS_WCNSS}, + {7, "DATA1", NULL, SMD_APPS_MODEM}, + {11, "DATA11", NULL, SMD_APPS_MODEM}, + {21, "DATA21", NULL, SMD_APPS_MODEM}, + {27, "GPSNMEA", NULL, SMD_APPS_MODEM}, + {36, "LOOPBACK", "LOOPBACK_TTY", SMD_APPS_MODEM}, +}; +#define DS_IDX 0 +#define LOOPBACK_IDX 36 + +static struct delayed_work loopback_work; +static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; + +static int is_in_reset(struct smd_tty_info *info) +{ + return info->in_reset; +} + +static void buf_req_retry(unsigned long param) +{ + struct smd_tty_info *info = (struct smd_tty_info *)param; + unsigned long flags; + + spin_lock_irqsave(&info->reset_lock, flags); + if (info->is_open) { + spin_unlock_irqrestore(&info->reset_lock, flags); + tasklet_hi_schedule(&info->tty_tsklt); + return; + } + spin_unlock_irqrestore(&info->reset_lock, flags); +} + +static void smd_tty_read(unsigned long param) +{ + unsigned char *ptr; + int avail; + struct smd_tty_info *info = (struct smd_tty_info *)param; + struct tty_struct *tty = info->tty; + + if (!tty) + return; + + for (;;) { + if (is_in_reset(info)) { + /* signal TTY clients using TTY_BREAK */ + tty_insert_flip_char(tty, 0x00, TTY_BREAK); + tty_flip_buffer_push(tty); + break; + } + + if (test_bit(TTY_THROTTLED, &tty->flags)) break; + avail = smd_read_avail(info->ch); + if (avail == 0) + break; + + if (avail > MAX_TTY_BUF_SIZE) + avail = MAX_TTY_BUF_SIZE; + + avail = tty_prepare_flip_string(tty, &ptr, avail); + if (avail <= 0) { + if (!timer_pending(&info->buf_req_timer)) { + init_timer(&info->buf_req_timer); + info->buf_req_timer.expires = jiffies + + ((30 * HZ)/1000); + info->buf_req_timer.function = buf_req_retry; + info->buf_req_timer.data = param; + add_timer(&info->buf_req_timer); + } + return; + } + + if (smd_read(info->ch, ptr, avail) != avail) { + /* shouldn't be possible since we're in interrupt + ** context here and nobody else could 'steal' our + ** characters. + */ + printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!"); + } + + wake_lock_timeout(&info->wake_lock, HZ / 2); + tty_flip_buffer_push(tty); + } + + /* XXX only when writable and necessary */ + tty_wakeup(tty); +} + +static void smd_tty_notify(void *priv, unsigned event) +{ + struct smd_tty_info *info = priv; + unsigned long flags; + + switch (event) { + case SMD_EVENT_DATA: + spin_lock_irqsave(&info->reset_lock, flags); + if (!info->is_open) { + spin_unlock_irqrestore(&info->reset_lock, flags); + break; + } + spin_unlock_irqrestore(&info->reset_lock, flags); + /* There may be clients (tty framework) that are blocked + * waiting for space to write data, so if a possible read + * interrupt came in wake anyone waiting and disable the + * interrupts + */ + if (smd_write_avail(info->ch)) { + smd_disable_read_intr(info->ch); + if (info->tty) + wake_up_interruptible(&info->tty->write_wait); + } + tasklet_hi_schedule(&info->tty_tsklt); + break; + + case SMD_EVENT_OPEN: + spin_lock_irqsave(&info->reset_lock, flags); + info->in_reset = 0; + info->in_reset_updated = 1; + info->is_open = 1; + wake_up_interruptible(&info->ch_opened_wait_queue); + spin_unlock_irqrestore(&info->reset_lock, flags); + break; + + case SMD_EVENT_CLOSE: + spin_lock_irqsave(&info->reset_lock, flags); + info->in_reset = 1; + info->in_reset_updated = 1; + info->is_open = 0; + wake_up_interruptible(&info->ch_opened_wait_queue); + spin_unlock_irqrestore(&info->reset_lock, flags); + /* schedule task to send TTY_BREAK */ + tasklet_hi_schedule(&info->tty_tsklt); + + if (info->tty->index == LOOPBACK_IDX) + schedule_delayed_work(&loopback_work, + msecs_to_jiffies(1000)); + break; + } +} + +static uint32_t is_modem_smsm_inited(void) +{ + uint32_t modem_state; + uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT); + + modem_state = smsm_get_state(SMSM_MODEM_STATE); + return (modem_state & ready_state) == ready_state; +} + +static int smd_tty_open(struct tty_struct *tty, struct file *f) +{ + int res = 0; + unsigned int n = tty->index; + struct smd_tty_info *info; + const char *peripheral = NULL; + + + if (n >= MAX_SMD_TTYS || !smd_tty[n].smd) + return -ENODEV; + + info = smd_tty + n; + + mutex_lock(&smd_tty_lock); + tty->driver_data = info; + + if (info->open_count++ == 0) { + peripheral = smd_edge_to_subsystem(smd_tty[n].smd->edge); + if (peripheral) { + info->pil = pil_get(peripheral); + if (IS_ERR(info->pil)) { + res = PTR_ERR(info->pil); + goto out; + } + + /* Wait for the modem SMSM to be inited for the SMD + * Loopback channel to be allocated at the modem. Since + * the wait need to be done atmost once, using msleep + * doesn't degrade the performance. + */ + if (n == LOOPBACK_IDX) { + if (!is_modem_smsm_inited()) + msleep(5000); + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); + msleep(100); + } + + + /* + * Wait for a channel to be allocated so we know + * the modem is ready enough. + */ + if (smd_tty_modem_wait) { + res = wait_for_completion_interruptible_timeout( + &info->ch_allocated, + msecs_to_jiffies(smd_tty_modem_wait * + 1000)); + + if (res == 0) { + pr_err("Timed out waiting for SMD" + " channel\n"); + res = -ETIMEDOUT; + goto release_pil; + } else if (res < 0) { + pr_err("Error waiting for SMD channel:" + " %d\n", + res); + goto release_pil; + } + + res = 0; + } + } + + + info->tty = tty; + tasklet_init(&info->tty_tsklt, smd_tty_read, + (unsigned long)info); + wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, + smd_tty[n].smd->port_name); + if (!info->ch) { + res = smd_named_open_on_edge(smd_tty[n].smd->port_name, + smd_tty[n].smd->edge, + &info->ch, info, + smd_tty_notify); + if (res < 0) { + pr_err("%s: %s open failed %d\n", __func__, + smd_tty[n].smd->port_name, res); + goto release_pil; + } + + res = wait_event_interruptible_timeout( + info->ch_opened_wait_queue, + info->is_open, (2 * HZ)); + if (res == 0) + res = -ETIMEDOUT; + if (res < 0) { + pr_err("%s: wait for %s smd_open failed %d\n", + __func__, smd_tty[n].smd->port_name, + res); + goto release_pil; + } + res = 0; + } + } + +release_pil: + if (res < 0) + pil_put(info->pil); + else + smd_disable_read_intr(info->ch); +out: + mutex_unlock(&smd_tty_lock); + + return res; +} + +static void smd_tty_close(struct tty_struct *tty, struct file *f) +{ + struct smd_tty_info *info = tty->driver_data; + unsigned long flags; + + if (info == 0) + return; + + mutex_lock(&smd_tty_lock); + if (--info->open_count == 0) { + spin_lock_irqsave(&info->reset_lock, flags); + info->is_open = 0; + spin_unlock_irqrestore(&info->reset_lock, flags); + if (info->tty) { + tasklet_kill(&info->tty_tsklt); + wake_lock_destroy(&info->wake_lock); + info->tty = 0; + } + tty->driver_data = 0; + del_timer(&info->buf_req_timer); + if (info->ch) { + smd_close(info->ch); + info->ch = 0; + pil_put(info->pil); + } + } + mutex_unlock(&smd_tty_lock); +} + +static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len) +{ + struct smd_tty_info *info = tty->driver_data; + int avail; + + /* if we're writing to a packet channel we will + ** never be able to write more data than there + ** is currently space for + */ + if (is_in_reset(info)) + return -ENETRESET; + + avail = smd_write_avail(info->ch); + /* if no space, we'll have to setup a notification later to wake up the + * tty framework when space becomes avaliable + */ + if (!avail) { + smd_enable_read_intr(info->ch); + return 0; + } + if (len > avail) + len = avail; + + return smd_write(info->ch, buf, len); +} + +static int smd_tty_write_room(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + return smd_write_avail(info->ch); +} + +static int smd_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + return smd_read_avail(info->ch); +} + +static void smd_tty_unthrottle(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&info->reset_lock, flags); + if (info->is_open) { + spin_unlock_irqrestore(&info->reset_lock, flags); + tasklet_hi_schedule(&info->tty_tsklt); + return; + } + spin_unlock_irqrestore(&info->reset_lock, flags); +} + +/* + * Returns the current TIOCM status bits including: + * SMD Signals (DTR/DSR, CTS/RTS, CD, RI) + * TIOCM_OUT1 - reset state (1=in reset) + * TIOCM_OUT2 - reset state updated (1=updated) + */ +static int smd_tty_tiocmget(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + unsigned long flags; + int tiocm; + + tiocm = smd_tiocmget(info->ch); + + spin_lock_irqsave(&info->reset_lock, flags); + tiocm |= (info->in_reset ? TIOCM_OUT1 : 0); + if (info->in_reset_updated) { + tiocm |= TIOCM_OUT2; + info->in_reset_updated = 0; + } + spin_unlock_irqrestore(&info->reset_lock, flags); + + return tiocm; +} + +static int smd_tty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct smd_tty_info *info = tty->driver_data; + + if (info->in_reset) + return -ENETRESET; + + return smd_tiocmset(info->ch, set, clear); +} + +static void loopback_probe_worker(struct work_struct *work) +{ + /* wait for modem to restart before requesting loopback server */ + if (!is_modem_smsm_inited()) + schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000)); + else + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); +} + +static struct tty_operations smd_tty_ops = { + .open = smd_tty_open, + .close = smd_tty_close, + .write = smd_tty_write, + .write_room = smd_tty_write_room, + .chars_in_buffer = smd_tty_chars_in_buffer, + .unthrottle = smd_tty_unthrottle, + .tiocmget = smd_tty_tiocmget, + .tiocmset = smd_tty_tiocmset, +}; + +static int smd_tty_dummy_probe(struct platform_device *pdev) +{ + int n; + int idx; + + for (n = 0; n < ARRAY_SIZE(smd_configs); ++n) { + idx = smd_configs[n].tty_dev_index; + + if (!smd_configs[n].dev_name) + continue; + + if (pdev->id == smd_configs[n].edge && + !strncmp(pdev->name, smd_configs[n].dev_name, + SMD_MAX_CH_NAME_LEN)) { + complete_all(&smd_tty[idx].ch_allocated); + return 0; + } + } + pr_err("%s: unknown device '%s'\n", __func__, pdev->name); + + return -ENODEV; +} + +static struct tty_driver *smd_tty_driver; + +static int __init smd_tty_init(void) +{ + int ret; + int n; + int idx; + + smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS); + if (smd_tty_driver == 0) + return -ENOMEM; + + smd_tty_driver->owner = THIS_MODULE; + smd_tty_driver->driver_name = "smd_tty_driver"; + smd_tty_driver->name = "smd"; + smd_tty_driver->major = 0; + smd_tty_driver->minor_start = 0; + smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + smd_tty_driver->subtype = SERIAL_TYPE_NORMAL; + smd_tty_driver->init_termios = tty_std_termios; + smd_tty_driver->init_termios.c_iflag = 0; + smd_tty_driver->init_termios.c_oflag = 0; + smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; + smd_tty_driver->init_termios.c_lflag = 0; + smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(smd_tty_driver, &smd_tty_ops); + + ret = tty_register_driver(smd_tty_driver); + if (ret) { + put_tty_driver(smd_tty_driver); + pr_err("%s: driver registration failed %d\n", __func__, ret); + return ret; + } + + for (n = 0; n < ARRAY_SIZE(smd_configs); ++n) { + idx = smd_configs[n].tty_dev_index; + + if (smd_configs[n].dev_name == NULL) + smd_configs[n].dev_name = smd_configs[n].port_name; + + if (idx == DS_IDX) { + /* + * DS port uses the kernel API starting with + * 8660 Fusion. Only register the userspace + * platform device for older targets. + */ + int legacy_ds = 0; + + legacy_ds |= cpu_is_msm7x01() || cpu_is_msm7x25(); + legacy_ds |= cpu_is_msm7x27() || cpu_is_msm7x30(); + legacy_ds |= cpu_is_qsd8x50() || cpu_is_msm8x55(); + /* + * use legacy mode for 8660 Standalone (subtype 0) + */ + legacy_ds |= cpu_is_msm8x60() && + (socinfo_get_platform_subtype() == 0x0); + + if (!legacy_ds) + continue; + } + + tty_register_device(smd_tty_driver, idx, 0); + init_completion(&smd_tty[idx].ch_allocated); + + /* register platform device */ + smd_tty[idx].driver.probe = smd_tty_dummy_probe; + smd_tty[idx].driver.driver.name = smd_configs[n].dev_name; + smd_tty[idx].driver.driver.owner = THIS_MODULE; + spin_lock_init(&smd_tty[idx].reset_lock); + smd_tty[idx].is_open = 0; + init_waitqueue_head(&smd_tty[idx].ch_opened_wait_queue); + ret = platform_driver_register(&smd_tty[idx].driver); + + if (ret) { + pr_err("%s: init failed %d (%d)\n", __func__, idx, ret); + smd_tty[idx].driver.probe = NULL; + goto out; + } + smd_tty[idx].smd = &smd_configs[n]; + } + INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker); + return 0; + +out: + /* unregister platform devices */ + for (n = 0; n < ARRAY_SIZE(smd_configs); ++n) { + idx = smd_configs[n].tty_dev_index; + + if (smd_tty[idx].driver.probe) { + platform_driver_unregister(&smd_tty[idx].driver); + tty_unregister_device(smd_tty_driver, idx); + } + } + + tty_unregister_driver(smd_tty_driver); + put_tty_driver(smd_tty_driver); + return ret; +} + +module_init(smd_tty_init); diff --git a/arch/arm/mach-msm/smem_log.c b/arch/arm/mach-msm/smem_log.c new file mode 100644 index 00000000000..2e9a97c0dc5 --- /dev/null +++ b/arch/arm/mach-msm/smem_log.c @@ -0,0 +1,2036 @@ +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Shared memory logging implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smd_private.h" +#include "smd_rpc_sym.h" +#include "modem_notifier.h" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + int i; \ + printk(KERN_ERR "%s", prestr); \ + for (i = 0; i < cnt; i++) \ + printk(KERN_ERR "%.2x", buf[i]); \ + printk(KERN_ERR "\n"); \ +} while (0) +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) +#endif + +#ifdef DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +/* + * Legacy targets use the 32KHz hardware timer and new targets will use + * the scheduler timer scaled to a 32KHz tick count. + * + * As testing on legacy targets permits, we will move them to use + * sched_clock() and eventually remove the conditiona compilation. + */ +#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM8X60) \ + || defined(CONFIG_ARCH_FSM9XXX) +#define TIMESTAMP_ADDR (MSM_TMR_BASE + 0x08) +#elif defined(CONFIG_ARCH_APQ8064) || defined(CONFIG_ARCH_MSM7X01A) || \ + defined(CONFIG_ARCH_MSM7x25) || defined(CONFIG_ARCH_MSM7X27) || \ + defined(CONFIG_ARCH_MSM7X27A) || defined(CONFIG_ARCH_MSM8960) || \ + defined(CONFIG_ARCH_MSM9615) || defined(CONFIG_ARCH_QSD8X50) +#define TIMESTAMP_ADDR (MSM_TMR_BASE + 0x04) +#endif + +struct smem_log_item { + uint32_t identifier; + uint32_t timetick; + uint32_t data1; + uint32_t data2; + uint32_t data3; +}; + +#define SMEM_LOG_NUM_ENTRIES 2000 +#define SMEM_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_ENTRIES) + +#define SMEM_LOG_NUM_STATIC_ENTRIES 150 +#define SMEM_STATIC_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_STATIC_ENTRIES) + +#define SMEM_LOG_NUM_POWER_ENTRIES 2000 +#define SMEM_POWER_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_POWER_ENTRIES) + +#define SMEM_SPINLOCK_SMEM_LOG "S:2" +#define SMEM_SPINLOCK_STATIC_LOG "S:5" +/* POWER shares with SMEM_SPINLOCK_SMEM_LOG */ + +static remote_spinlock_t remote_spinlock; +static remote_spinlock_t remote_spinlock_static; +static uint32_t smem_log_enable; +static int smem_log_initialized; + +module_param_named(log_enable, smem_log_enable, int, + S_IRUGO | S_IWUSR | S_IWGRP); + + +struct smem_log_inst { + int which_log; + struct smem_log_item __iomem *events; + uint32_t __iomem *idx; + uint32_t num; + uint32_t read_idx; + uint32_t last_read_avail; + wait_queue_head_t read_wait; + remote_spinlock_t *remote_spinlock; +}; + +enum smem_logs { + GEN = 0, + STA, + POW, + NUM +}; + +static struct smem_log_inst inst[NUM]; + +#if defined(CONFIG_DEBUG_FS) + +#define HSIZE 13 + +struct sym { + uint32_t val; + char *str; + struct hlist_node node; +}; + +struct sym id_syms[] = { + { SMEM_LOG_PROC_ID_MODEM, "MODM" }, + { SMEM_LOG_PROC_ID_Q6, "QDSP" }, + { SMEM_LOG_PROC_ID_APPS, "APPS" }, + { SMEM_LOG_PROC_ID_WCNSS, "WCNSS" }, +}; + +struct sym base_syms[] = { + { SMEM_LOG_ONCRPC_EVENT_BASE, "ONCRPC" }, + { SMEM_LOG_SMEM_EVENT_BASE, "SMEM" }, + { SMEM_LOG_TMC_EVENT_BASE, "TMC" }, + { SMEM_LOG_TIMETICK_EVENT_BASE, "TIMETICK" }, + { SMEM_LOG_DEM_EVENT_BASE, "DEM" }, + { SMEM_LOG_ERROR_EVENT_BASE, "ERROR" }, + { SMEM_LOG_DCVS_EVENT_BASE, "DCVS" }, + { SMEM_LOG_SLEEP_EVENT_BASE, "SLEEP" }, + { SMEM_LOG_RPC_ROUTER_EVENT_BASE, "ROUTER" }, +}; + +struct sym event_syms[] = { +#if defined(CONFIG_MSM_N_WAY_SMSM) + { DEM_SMSM_ISR, "SMSM_ISR" }, + { DEM_STATE_CHANGE, "STATE_CHANGE" }, + { DEM_STATE_MACHINE_ENTER, "STATE_MACHINE_ENTER" }, + { DEM_ENTER_SLEEP, "ENTER_SLEEP" }, + { DEM_END_SLEEP, "END_SLEEP" }, + { DEM_SETUP_SLEEP, "SETUP_SLEEP" }, + { DEM_SETUP_POWER_COLLAPSE, "SETUP_POWER_COLLAPSE" }, + { DEM_SETUP_SUSPEND, "SETUP_SUSPEND" }, + { DEM_EARLY_EXIT, "EARLY_EXIT" }, + { DEM_WAKEUP_REASON, "WAKEUP_REASON" }, + { DEM_DETECT_WAKEUP, "DETECT_WAKEUP" }, + { DEM_DETECT_RESET, "DETECT_RESET" }, + { DEM_DETECT_SLEEPEXIT, "DETECT_SLEEPEXIT" }, + { DEM_DETECT_RUN, "DETECT_RUN" }, + { DEM_APPS_SWFI, "APPS_SWFI" }, + { DEM_SEND_WAKEUP, "SEND_WAKEUP" }, + { DEM_ASSERT_OKTS, "ASSERT_OKTS" }, + { DEM_NEGATE_OKTS, "NEGATE_OKTS" }, + { DEM_PROC_COMM_CMD, "PROC_COMM_CMD" }, + { DEM_REMOVE_PROC_PWR, "REMOVE_PROC_PWR" }, + { DEM_RESTORE_PROC_PWR, "RESTORE_PROC_PWR" }, + { DEM_SMI_CLK_DISABLED, "SMI_CLK_DISABLED" }, + { DEM_SMI_CLK_ENABLED, "SMI_CLK_ENABLED" }, + { DEM_MAO_INTS, "MAO_INTS" }, + { DEM_APPS_WAKEUP_INT, "APPS_WAKEUP_INT" }, + { DEM_PROC_WAKEUP, "PROC_WAKEUP" }, + { DEM_PROC_POWERUP, "PROC_POWERUP" }, + { DEM_TIMER_EXPIRED, "TIMER_EXPIRED" }, + { DEM_SEND_BATTERY_INFO, "SEND_BATTERY_INFO" }, + { DEM_REMOTE_PWR_CB, "REMOTE_PWR_CB" }, + { DEM_TIME_SYNC_START, "TIME_SYNC_START" }, + { DEM_TIME_SYNC_SEND_VALUE, "TIME_SYNC_SEND_VALUE" }, + { DEM_TIME_SYNC_DONE, "TIME_SYNC_DONE" }, + { DEM_TIME_SYNC_REQUEST, "TIME_SYNC_REQUEST" }, + { DEM_TIME_SYNC_POLL, "TIME_SYNC_POLL" }, + { DEM_TIME_SYNC_INIT, "TIME_SYNC_INIT" }, + { DEM_INIT, "INIT" }, +#else + + { DEM_NO_SLEEP, "NO_SLEEP" }, + { DEM_INSUF_TIME, "INSUF_TIME" }, + { DEMAPPS_ENTER_SLEEP, "APPS_ENTER_SLEEP" }, + { DEMAPPS_DETECT_WAKEUP, "APPS_DETECT_WAKEUP" }, + { DEMAPPS_END_APPS_TCXO, "APPS_END_APPS_TCXO" }, + { DEMAPPS_ENTER_SLEEPEXIT, "APPS_ENTER_SLEEPEXIT" }, + { DEMAPPS_END_APPS_SLEEP, "APPS_END_APPS_SLEEP" }, + { DEMAPPS_SETUP_APPS_PWRCLPS, "APPS_SETUP_APPS_PWRCLPS" }, + { DEMAPPS_PWRCLPS_EARLY_EXIT, "APPS_PWRCLPS_EARLY_EXIT" }, + { DEMMOD_SEND_WAKEUP, "MOD_SEND_WAKEUP" }, + { DEMMOD_NO_APPS_VOTE, "MOD_NO_APPS_VOTE" }, + { DEMMOD_NO_TCXO_SLEEP, "MOD_NO_TCXO_SLEEP" }, + { DEMMOD_BT_CLOCK, "MOD_BT_CLOCK" }, + { DEMMOD_UART_CLOCK, "MOD_UART_CLOCK" }, + { DEMMOD_OKTS, "MOD_OKTS" }, + { DEM_SLEEP_INFO, "SLEEP_INFO" }, + { DEMMOD_TCXO_END, "MOD_TCXO_END" }, + { DEMMOD_END_SLEEP_SIG, "MOD_END_SLEEP_SIG" }, + { DEMMOD_SETUP_APPSSLEEP, "MOD_SETUP_APPSSLEEP" }, + { DEMMOD_ENTER_TCXO, "MOD_ENTER_TCXO" }, + { DEMMOD_WAKE_APPS, "MOD_WAKE_APPS" }, + { DEMMOD_POWER_COLLAPSE_APPS, "MOD_POWER_COLLAPSE_APPS" }, + { DEMMOD_RESTORE_APPS_PWR, "MOD_RESTORE_APPS_PWR" }, + { DEMAPPS_ASSERT_OKTS, "APPS_ASSERT_OKTS" }, + { DEMAPPS_RESTART_START_TIMER, "APPS_RESTART_START_TIMER" }, + { DEMAPPS_ENTER_RUN, "APPS_ENTER_RUN" }, + { DEMMOD_MAO_INTS, "MOD_MAO_INTS" }, + { DEMMOD_POWERUP_APPS_CALLED, "MOD_POWERUP_APPS_CALLED" }, + { DEMMOD_PC_TIMER_EXPIRED, "MOD_PC_TIMER_EXPIRED" }, + { DEM_DETECT_SLEEPEXIT, "_DETECT_SLEEPEXIT" }, + { DEM_DETECT_RUN, "DETECT_RUN" }, + { DEM_SET_APPS_TIMER, "SET_APPS_TIMER" }, + { DEM_NEGATE_OKTS, "NEGATE_OKTS" }, + { DEMMOD_APPS_WAKEUP_INT, "MOD_APPS_WAKEUP_INT" }, + { DEMMOD_APPS_SWFI, "MOD_APPS_SWFI" }, + { DEM_SEND_BATTERY_INFO, "SEND_BATTERY_INFO" }, + { DEM_SMI_CLK_DISABLED, "SMI_CLK_DISABLED" }, + { DEM_SMI_CLK_ENABLED, "SMI_CLK_ENABLED" }, + { DEMAPPS_SETUP_APPS_SUSPEND, "APPS_SETUP_APPS_SUSPEND" }, + { DEM_RPC_EARLY_EXIT, "RPC_EARLY_EXIT" }, + { DEMAPPS_WAKEUP_REASON, "APPS_WAKEUP_REASON" }, + { DEM_INIT, "INIT" }, +#endif + { DEMMOD_UMTS_BASE, "MOD_UMTS_BASE" }, + { DEMMOD_GL1_GO_TO_SLEEP, "GL1_GO_TO_SLEEP" }, + { DEMMOD_GL1_SLEEP_START, "GL1_SLEEP_START" }, + { DEMMOD_GL1_AFTER_GSM_CLK_ON, "GL1_AFTER_GSM_CLK_ON" }, + { DEMMOD_GL1_BEFORE_RF_ON, "GL1_BEFORE_RF_ON" }, + { DEMMOD_GL1_AFTER_RF_ON, "GL1_AFTER_RF_ON" }, + { DEMMOD_GL1_FRAME_TICK, "GL1_FRAME_TICK" }, + { DEMMOD_GL1_WCDMA_START, "GL1_WCDMA_START" }, + { DEMMOD_GL1_WCDMA_ENDING, "GL1_WCDMA_ENDING" }, + { DEMMOD_UMTS_NOT_OKTS, "UMTS_NOT_OKTS" }, + { DEMMOD_UMTS_START_TCXO_SHUTDOWN, "UMTS_START_TCXO_SHUTDOWN" }, + { DEMMOD_UMTS_END_TCXO_SHUTDOWN, "UMTS_END_TCXO_SHUTDOWN" }, + { DEMMOD_UMTS_START_ARM_HALT, "UMTS_START_ARM_HALT" }, + { DEMMOD_UMTS_END_ARM_HALT, "UMTS_END_ARM_HALT" }, + { DEMMOD_UMTS_NEXT_WAKEUP_SCLK, "UMTS_NEXT_WAKEUP_SCLK" }, + { TIME_REMOTE_LOG_EVENT_START, "START" }, + { TIME_REMOTE_LOG_EVENT_GOTO_WAIT, + "GOTO_WAIT" }, + { TIME_REMOTE_LOG_EVENT_GOTO_INIT, + "GOTO_INIT" }, + { ERR_ERROR_FATAL, "ERR_ERROR_FATAL" }, + { ERR_ERROR_FATAL_TASK, "ERR_ERROR_FATAL_TASK" }, + { DCVSAPPS_LOG_IDLE, "DCVSAPPS_LOG_IDLE" }, + { DCVSAPPS_LOG_ERR, "DCVSAPPS_LOG_ERR" }, + { DCVSAPPS_LOG_CHG, "DCVSAPPS_LOG_CHG" }, + { DCVSAPPS_LOG_REG, "DCVSAPPS_LOG_REG" }, + { DCVSAPPS_LOG_DEREG, "DCVSAPPS_LOG_DEREG" }, + { SMEM_LOG_EVENT_CB, "CB" }, + { SMEM_LOG_EVENT_START, "START" }, + { SMEM_LOG_EVENT_INIT, "INIT" }, + { SMEM_LOG_EVENT_RUNNING, "RUNNING" }, + { SMEM_LOG_EVENT_STOP, "STOP" }, + { SMEM_LOG_EVENT_RESTART, "RESTART" }, + { SMEM_LOG_EVENT_SS, "SS" }, + { SMEM_LOG_EVENT_READ, "READ" }, + { SMEM_LOG_EVENT_WRITE, "WRITE" }, + { SMEM_LOG_EVENT_SIGS1, "SIGS1" }, + { SMEM_LOG_EVENT_SIGS2, "SIGS2" }, + { SMEM_LOG_EVENT_WRITE_DM, "WRITE_DM" }, + { SMEM_LOG_EVENT_READ_DM, "READ_DM" }, + { SMEM_LOG_EVENT_SKIP_DM, "SKIP_DM" }, + { SMEM_LOG_EVENT_STOP_DM, "STOP_DM" }, + { SMEM_LOG_EVENT_ISR, "ISR" }, + { SMEM_LOG_EVENT_TASK, "TASK" }, + { SMEM_LOG_EVENT_RS, "RS" }, + { ONCRPC_LOG_EVENT_SMD_WAIT, "SMD_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_WAIT, "RPC_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_BOTH_WAIT, "RPC_BOTH_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_INIT, "RPC_INIT" }, + { ONCRPC_LOG_EVENT_RUNNING, "RUNNING" }, + { ONCRPC_LOG_EVENT_APIS_INITED, "APIS_INITED" }, + { ONCRPC_LOG_EVENT_AMSS_RESET, "AMSS_RESET" }, + { ONCRPC_LOG_EVENT_SMD_RESET, "SMD_RESET" }, + { ONCRPC_LOG_EVENT_ONCRPC_RESET, "ONCRPC_RESET" }, + { ONCRPC_LOG_EVENT_CB, "CB" }, + { ONCRPC_LOG_EVENT_STD_CALL, "STD_CALL" }, + { ONCRPC_LOG_EVENT_STD_REPLY, "STD_REPLY" }, + { ONCRPC_LOG_EVENT_STD_CALL_ASYNC, "STD_CALL_ASYNC" }, + { NO_SLEEP_OLD, "NO_SLEEP_OLD" }, + { INSUF_TIME, "INSUF_TIME" }, + { MOD_UART_CLOCK, "MOD_UART_CLOCK" }, + { SLEEP_INFO, "SLEEP_INFO" }, + { MOD_TCXO_END, "MOD_TCXO_END" }, + { MOD_ENTER_TCXO, "MOD_ENTER_TCXO" }, + { NO_SLEEP_NEW, "NO_SLEEP_NEW" }, + { RPC_ROUTER_LOG_EVENT_UNKNOWN, "UNKNOWN" }, + { RPC_ROUTER_LOG_EVENT_MSG_READ, "MSG_READ" }, + { RPC_ROUTER_LOG_EVENT_MSG_WRITTEN, "MSG_WRITTEN" }, + { RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ, "MSG_CFM_REQ" }, + { RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT, "MSG_CFM_SNT" }, + { RPC_ROUTER_LOG_EVENT_MID_READ, "MID_READ" }, + { RPC_ROUTER_LOG_EVENT_MID_WRITTEN, "MID_WRITTEN" }, + { RPC_ROUTER_LOG_EVENT_MID_CFM_REQ, "MID_CFM_REQ" }, +}; + +struct sym wakeup_syms[] = { + { 0x00000040, "OTHER" }, + { 0x00000020, "RESET" }, + { 0x00000010, "ALARM" }, + { 0x00000008, "TIMER" }, + { 0x00000004, "GPIO" }, + { 0x00000002, "INT" }, + { 0x00000001, "RPC" }, + { 0x00000000, "NONE" }, +}; + +struct sym wakeup_int_syms[] = { + { 0, "MDDI_EXT" }, + { 1, "MDDI_PRI" }, + { 2, "MDDI_CLIENT"}, + { 3, "USB_OTG" }, + { 4, "I2CC" }, + { 5, "SDC1_0" }, + { 6, "SDC1_1" }, + { 7, "SDC2_0" }, + { 8, "SDC2_1" }, + { 9, "ADSP_A9A11" }, + { 10, "UART1" }, + { 11, "UART2" }, + { 12, "UART3" }, + { 13, "DP_RX_DATA" }, + { 14, "DP_RX_DATA2" }, + { 15, "DP_RX_DATA3" }, + { 16, "DM_UART" }, + { 17, "DM_DP_RX_DATA" }, + { 18, "KEYSENSE" }, + { 19, "HSSD" }, + { 20, "NAND_WR_ER_DONE" }, + { 21, "NAND_OP_DONE" }, + { 22, "TCHSCRN1" }, + { 23, "TCHSCRN2" }, + { 24, "TCHSCRN_SSBI" }, + { 25, "USB_HS" }, + { 26, "UART2_DM_RX" }, + { 27, "UART2_DM" }, + { 28, "SDC4_1" }, + { 29, "SDC4_0" }, + { 30, "SDC3_1" }, + { 31, "SDC3_0" }, +}; + +struct sym smsm_syms[] = { + { 0x80000000, "UN" }, + { 0x7F000000, "ERR" }, + { 0x00800000, "SMLP" }, + { 0x00400000, "ADWN" }, + { 0x00200000, "PWRS" }, + { 0x00100000, "DWLD" }, + { 0x00080000, "SRBT" }, + { 0x00040000, "SDWN" }, + { 0x00020000, "ARBT" }, + { 0x00010000, "REL" }, + { 0x00008000, "SLE" }, + { 0x00004000, "SLP" }, + { 0x00002000, "WFPI" }, + { 0x00001000, "EEX" }, + { 0x00000800, "TIN" }, + { 0x00000400, "TWT" }, + { 0x00000200, "PWRC" }, + { 0x00000100, "RUN" }, + { 0x00000080, "SA" }, + { 0x00000040, "RES" }, + { 0x00000020, "RIN" }, + { 0x00000010, "RWT" }, + { 0x00000008, "SIN" }, + { 0x00000004, "SWT" }, + { 0x00000002, "OE" }, + { 0x00000001, "I" }, +}; + +/* never reorder */ +struct sym voter_d2_syms[] = { + { 0x00000001, NULL }, + { 0x00000002, NULL }, + { 0x00000004, NULL }, + { 0x00000008, NULL }, + { 0x00000010, NULL }, + { 0x00000020, NULL }, + { 0x00000040, NULL }, + { 0x00000080, NULL }, + { 0x00000100, NULL }, + { 0x00000200, NULL }, + { 0x00000400, NULL }, + { 0x00000800, NULL }, + { 0x00001000, NULL }, + { 0x00002000, NULL }, + { 0x00004000, NULL }, + { 0x00008000, NULL }, + { 0x00010000, NULL }, + { 0x00020000, NULL }, + { 0x00040000, NULL }, + { 0x00080000, NULL }, + { 0x00100000, NULL }, + { 0x00200000, NULL }, + { 0x00400000, NULL }, + { 0x00800000, NULL }, + { 0x01000000, NULL }, + { 0x02000000, NULL }, + { 0x04000000, NULL }, + { 0x08000000, NULL }, + { 0x10000000, NULL }, + { 0x20000000, NULL }, + { 0x40000000, NULL }, + { 0x80000000, NULL }, +}; + +/* never reorder */ +struct sym voter_d3_syms[] = { + { 0x00000001, NULL }, + { 0x00000002, NULL }, + { 0x00000004, NULL }, + { 0x00000008, NULL }, + { 0x00000010, NULL }, + { 0x00000020, NULL }, + { 0x00000040, NULL }, + { 0x00000080, NULL }, + { 0x00000100, NULL }, + { 0x00000200, NULL }, + { 0x00000400, NULL }, + { 0x00000800, NULL }, + { 0x00001000, NULL }, + { 0x00002000, NULL }, + { 0x00004000, NULL }, + { 0x00008000, NULL }, + { 0x00010000, NULL }, + { 0x00020000, NULL }, + { 0x00040000, NULL }, + { 0x00080000, NULL }, + { 0x00100000, NULL }, + { 0x00200000, NULL }, + { 0x00400000, NULL }, + { 0x00800000, NULL }, + { 0x01000000, NULL }, + { 0x02000000, NULL }, + { 0x04000000, NULL }, + { 0x08000000, NULL }, + { 0x10000000, NULL }, + { 0x20000000, NULL }, + { 0x40000000, NULL }, + { 0x80000000, NULL }, +}; + +struct sym dem_state_master_syms[] = { + { 0, "INIT" }, + { 1, "RUN" }, + { 2, "SLEEP_WAIT" }, + { 3, "SLEEP_CONFIRMED" }, + { 4, "SLEEP_EXIT" }, + { 5, "RSA" }, + { 6, "EARLY_EXIT" }, + { 7, "RSA_DELAYED" }, + { 8, "RSA_CHECK_INTS" }, + { 9, "RSA_CONFIRMED" }, + { 10, "RSA_WAKING" }, + { 11, "RSA_RESTORE" }, + { 12, "RESET" }, +}; + +struct sym dem_state_slave_syms[] = { + { 0, "INIT" }, + { 1, "RUN" }, + { 2, "SLEEP_WAIT" }, + { 3, "SLEEP_EXIT" }, + { 4, "SLEEP_RUN_PENDING" }, + { 5, "POWER_COLLAPSE" }, + { 6, "CHECK_INTERRUPTS" }, + { 7, "SWFI" }, + { 8, "WFPI" }, + { 9, "EARLY_EXIT" }, + { 10, "RESET_RECOVER" }, + { 11, "RESET_ACKNOWLEDGE" }, + { 12, "ERROR" }, +}; + +struct sym smsm_entry_type_syms[] = { + { 0, "SMSM_APPS_STATE" }, + { 1, "SMSM_MODEM_STATE" }, + { 2, "SMSM_Q6_STATE" }, + { 3, "SMSM_APPS_DEM" }, + { 4, "SMSM_MODEM_DEM" }, + { 5, "SMSM_Q6_DEM" }, + { 6, "SMSM_POWER_MASTER_DEM" }, + { 7, "SMSM_TIME_MASTER_DEM" }, +}; + +struct sym smsm_state_syms[] = { + { 0x00000001, "INIT" }, + { 0x00000002, "OSENTERED" }, + { 0x00000004, "SMDWAIT" }, + { 0x00000008, "SMDINIT" }, + { 0x00000010, "RPCWAIT" }, + { 0x00000020, "RPCINIT" }, + { 0x00000040, "RESET" }, + { 0x00000080, "RSA" }, + { 0x00000100, "RUN" }, + { 0x00000200, "PWRC" }, + { 0x00000400, "TIMEWAIT" }, + { 0x00000800, "TIMEINIT" }, + { 0x00001000, "PWRC_EARLY_EXIT" }, + { 0x00002000, "WFPI" }, + { 0x00004000, "SLEEP" }, + { 0x00008000, "SLEEPEXIT" }, + { 0x00010000, "OEMSBL_RELEASE" }, + { 0x00020000, "APPS_REBOOT" }, + { 0x00040000, "SYSTEM_POWER_DOWN" }, + { 0x00080000, "SYSTEM_REBOOT" }, + { 0x00100000, "SYSTEM_DOWNLOAD" }, + { 0x00200000, "PWRC_SUSPEND" }, + { 0x00400000, "APPS_SHUTDOWN" }, + { 0x00800000, "SMD_LOOPBACK" }, + { 0x01000000, "RUN_QUIET" }, + { 0x02000000, "MODEM_WAIT" }, + { 0x04000000, "MODEM_BREAK" }, + { 0x08000000, "MODEM_CONTINUE" }, + { 0x80000000, "UNKNOWN" }, +}; + +#define ID_SYM 0 +#define BASE_SYM 1 +#define EVENT_SYM 2 +#define WAKEUP_SYM 3 +#define WAKEUP_INT_SYM 4 +#define SMSM_SYM 5 +#define VOTER_D2_SYM 6 +#define VOTER_D3_SYM 7 +#define DEM_STATE_MASTER_SYM 8 +#define DEM_STATE_SLAVE_SYM 9 +#define SMSM_ENTRY_TYPE_SYM 10 +#define SMSM_STATE_SYM 11 + +static struct sym_tbl { + struct sym *data; + int size; + struct hlist_head hlist[HSIZE]; +} tbl[] = { + { id_syms, ARRAY_SIZE(id_syms) }, + { base_syms, ARRAY_SIZE(base_syms) }, + { event_syms, ARRAY_SIZE(event_syms) }, + { wakeup_syms, ARRAY_SIZE(wakeup_syms) }, + { wakeup_int_syms, ARRAY_SIZE(wakeup_int_syms) }, + { smsm_syms, ARRAY_SIZE(smsm_syms) }, + { voter_d2_syms, ARRAY_SIZE(voter_d2_syms) }, + { voter_d3_syms, ARRAY_SIZE(voter_d3_syms) }, + { dem_state_master_syms, ARRAY_SIZE(dem_state_master_syms) }, + { dem_state_slave_syms, ARRAY_SIZE(dem_state_slave_syms) }, + { smsm_entry_type_syms, ARRAY_SIZE(smsm_entry_type_syms) }, + { smsm_state_syms, ARRAY_SIZE(smsm_state_syms) }, +}; + +static void find_voters(void) +{ + void *x, *next; + unsigned size; + int i = 0, j = 0; + + x = smem_get_entry(SMEM_SLEEP_STATIC, &size); + next = x; + while (next && (next < (x + size)) && + ((i + j) < (ARRAY_SIZE(voter_d3_syms) + + ARRAY_SIZE(voter_d2_syms)))) { + + if (i < ARRAY_SIZE(voter_d3_syms)) { + voter_d3_syms[i].str = (char *) next; + i++; + } else if (i >= ARRAY_SIZE(voter_d3_syms) && + j < ARRAY_SIZE(voter_d2_syms)) { + voter_d2_syms[j].str = (char *) next; + j++; + } + + next += 9; + } +} + +#define hash(val) (val % HSIZE) + +static void init_syms(void) +{ + int i; + int j; + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < HSIZE; ++j) + INIT_HLIST_HEAD(&tbl[i].hlist[j]); + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < tbl[i].size; ++j) { + INIT_HLIST_NODE(&tbl[i].data[j].node); + hlist_add_head(&tbl[i].data[j].node, + &tbl[i].hlist[hash(tbl[i].data[j].val)]); + } +} + +static char *find_sym(uint32_t id, uint32_t val) +{ + struct hlist_node *n; + struct sym *s; + + hlist_for_each(n, &tbl[id].hlist[hash(val)]) { + s = hlist_entry(n, struct sym, node); + if (s->val == val) + return s->str; + } + + return 0; +} + +#else +static void init_syms(void) {} +#endif + +#ifdef TIMESTAMP_ADDR +/* legacy timestamp using 32.768KHz clock */ +static inline unsigned int read_timestamp(void) +{ + unsigned int tick = 0; + + /* no barriers necessary as the read value is a dependency for the + * comparison operation so the processor shouldn't be able to + * reorder things + */ + do { + tick = __raw_readl(TIMESTAMP_ADDR); + } while (tick != __raw_readl(TIMESTAMP_ADDR)); + + return tick; +} +#else +static inline unsigned int read_timestamp(void) +{ + unsigned long long val; + + /* SMEM LOG uses a 32.768KHz timestamp */ + val = sched_clock() * 32768U; + do_div(val, 1000000000U); + + return (unsigned int)val; +} +#endif + +static void smem_log_event_from_user(struct smem_log_inst *inst, + const char __user *buf, int size, int num) +{ + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + uint32_t identifier = 0; + uint32_t timetick = 0; + int first = 1; + int ret; + + if (!inst->idx) { + pr_err("%s: invalid write index\n", __func__); + return; + } + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + while (num--) { + idx = *inst->idx; + + if (idx < inst->num) { + ret = copy_from_user(&inst->events[idx], + buf, size); + if (ret) { + printk("ERROR %s:%i tried to write " + "%i got ret %i", + __func__, __LINE__, + size, size - ret); + goto out; + } + + if (first) { + identifier = + inst->events[idx]. + identifier; + timetick = read_timestamp(); + first = 0; + } else { + identifier |= SMEM_LOG_CONT; + } + inst->events[idx].identifier = + identifier; + inst->events[idx].timetick = + timetick; + } + + next_idx = idx + 1; + if (next_idx >= inst->num) + next_idx = 0; + *inst->idx = next_idx; + buf += sizeof(struct smem_log_item); + } + + out: + wmb(); + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); +} + +static void _smem_log_event( + struct smem_log_item __iomem *events, + uint32_t __iomem *_idx, + remote_spinlock_t *lock, + int num, + uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + struct smem_log_item item; + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + + item.timetick = read_timestamp(); + item.identifier = id; + item.data1 = data1; + item.data2 = data2; + item.data3 = data3; + + remote_spin_lock_irqsave(lock, flags); + + idx = *_idx; + + if (idx < num) { + memcpy(&events[idx], + &item, sizeof(item)); + } + + next_idx = idx + 1; + if (next_idx >= num) + next_idx = 0; + *_idx = next_idx; + wmb(); + + remote_spin_unlock_irqrestore(lock, flags); +} + +static void _smem_log_event6( + struct smem_log_item __iomem *events, + uint32_t __iomem *_idx, + remote_spinlock_t *lock, + int num, + uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + struct smem_log_item item[2]; + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + + item[0].timetick = read_timestamp(); + item[0].identifier = id; + item[0].data1 = data1; + item[0].data2 = data2; + item[0].data3 = data3; + item[1].identifier = item[0].identifier; + item[1].timetick = item[0].timetick; + item[1].data1 = data4; + item[1].data2 = data5; + item[1].data3 = data6; + + remote_spin_lock_irqsave(lock, flags); + + idx = *_idx; + + /* FIXME: Wrap around */ + if (idx < (num-1)) { + memcpy(&events[idx], + &item, sizeof(item)); + } + + next_idx = idx + 2; + if (next_idx >= num) + next_idx = 0; + *_idx = next_idx; + + wmb(); + remote_spin_unlock_irqrestore(lock, flags); +} + +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + if (smem_log_enable) + _smem_log_event(inst[GEN].events, inst[GEN].idx, + inst[GEN].remote_spinlock, + SMEM_LOG_NUM_ENTRIES, id, + data1, data2, data3); +} + +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + if (smem_log_enable) + _smem_log_event6(inst[GEN].events, inst[GEN].idx, + inst[GEN].remote_spinlock, + SMEM_LOG_NUM_ENTRIES, id, + data1, data2, data3, data4, data5, data6); +} + +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + if (smem_log_enable) + _smem_log_event(inst[STA].events, inst[STA].idx, + inst[STA].remote_spinlock, + SMEM_LOG_NUM_STATIC_ENTRIES, id, + data1, data2, data3); +} + +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + if (smem_log_enable) + _smem_log_event6(inst[STA].events, inst[STA].idx, + inst[STA].remote_spinlock, + SMEM_LOG_NUM_STATIC_ENTRIES, id, + data1, data2, data3, data4, data5, data6); +} + +static int _smem_log_init(void) +{ + int ret; + + inst[GEN].which_log = GEN; + inst[GEN].events = + (struct smem_log_item *)smem_alloc2(SMEM_SMEM_LOG_EVENTS, + SMEM_LOG_EVENTS_SIZE); + inst[GEN].idx = (uint32_t *)smem_alloc2(SMEM_SMEM_LOG_IDX, + sizeof(uint32_t)); + if (!inst[GEN].events || !inst[GEN].idx) + pr_info("%s: no log or log_idx allocated\n", __func__); + + inst[GEN].num = SMEM_LOG_NUM_ENTRIES; + inst[GEN].read_idx = 0; + inst[GEN].last_read_avail = SMEM_LOG_NUM_ENTRIES; + init_waitqueue_head(&inst[GEN].read_wait); + inst[GEN].remote_spinlock = &remote_spinlock; + + inst[STA].which_log = STA; + inst[STA].events = + (struct smem_log_item *) + smem_alloc2(SMEM_SMEM_STATIC_LOG_EVENTS, + SMEM_STATIC_LOG_EVENTS_SIZE); + inst[STA].idx = (uint32_t *)smem_alloc2(SMEM_SMEM_STATIC_LOG_IDX, + sizeof(uint32_t)); + if (!inst[STA].events || !inst[STA].idx) + pr_info("%s: no static log or log_idx allocated\n", __func__); + + inst[STA].num = SMEM_LOG_NUM_STATIC_ENTRIES; + inst[STA].read_idx = 0; + inst[STA].last_read_avail = SMEM_LOG_NUM_ENTRIES; + init_waitqueue_head(&inst[STA].read_wait); + inst[STA].remote_spinlock = &remote_spinlock_static; + + inst[POW].which_log = POW; + inst[POW].events = + (struct smem_log_item *) + smem_alloc2(SMEM_SMEM_LOG_POWER_EVENTS, + SMEM_POWER_LOG_EVENTS_SIZE); + inst[POW].idx = (uint32_t *)smem_alloc2(SMEM_SMEM_LOG_POWER_IDX, + sizeof(uint32_t)); + if (!inst[POW].events || !inst[POW].idx) + pr_info("%s: no power log or log_idx allocated\n", __func__); + + inst[POW].num = SMEM_LOG_NUM_POWER_ENTRIES; + inst[POW].read_idx = 0; + inst[POW].last_read_avail = SMEM_LOG_NUM_ENTRIES; + init_waitqueue_head(&inst[POW].read_wait); + inst[POW].remote_spinlock = &remote_spinlock; + + ret = remote_spin_lock_init(&remote_spinlock, + SMEM_SPINLOCK_SMEM_LOG); + if (ret) { + mb(); + return ret; + } + + ret = remote_spin_lock_init(&remote_spinlock_static, + SMEM_SPINLOCK_STATIC_LOG); + if (ret) { + mb(); + return ret; + } + + init_syms(); + mb(); + + return 0; +} + +static ssize_t smem_log_read_bin(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + int idx; + int orig_idx; + unsigned long flags; + int ret; + int tot_bytes = 0; + struct smem_log_inst *local_inst; + + local_inst = fp->private_data; + + if (!local_inst->idx) + return -ENODEV; + + remote_spin_lock_irqsave(local_inst->remote_spinlock, flags); + + orig_idx = *local_inst->idx; + idx = orig_idx; + + while (1) { + idx--; + if (idx < 0) + idx = local_inst->num - 1; + if (idx == orig_idx) { + ret = tot_bytes; + break; + } + + if ((tot_bytes + sizeof(struct smem_log_item)) > count) { + ret = tot_bytes; + break; + } + + ret = copy_to_user(buf, &local_inst->events[idx], + sizeof(struct smem_log_item)); + if (ret) { + ret = -EIO; + break; + } + + tot_bytes += sizeof(struct smem_log_item); + + buf += sizeof(struct smem_log_item); + } + + remote_spin_unlock_irqrestore(local_inst->remote_spinlock, flags); + + return ret; +} + +static ssize_t smem_log_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char loc_buf[128]; + int i; + int idx; + int orig_idx; + unsigned long flags; + int ret; + int tot_bytes = 0; + struct smem_log_inst *inst; + + inst = fp->private_data; + if (!inst->idx) + return -ENODEV; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + orig_idx = *inst->idx; + idx = orig_idx; + + while (1) { + idx--; + if (idx < 0) + idx = inst->num - 1; + if (idx == orig_idx) { + ret = tot_bytes; + break; + } + + i = scnprintf(loc_buf, 128, + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + inst->events[idx].identifier, + inst->events[idx].timetick, + inst->events[idx].data1, + inst->events[idx].data2, + inst->events[idx].data3); + if (i == 0) { + ret = -EIO; + break; + } + + if ((tot_bytes + i) > count) { + ret = tot_bytes; + break; + } + + tot_bytes += i; + + ret = copy_to_user(buf, loc_buf, i); + if (ret) { + ret = -EIO; + break; + } + + buf += i; + } + + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); + + return ret; +} + +static ssize_t smem_log_write_bin(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + if (count < sizeof(struct smem_log_item)) + return -EINVAL; + + if (smem_log_enable) + smem_log_event_from_user(fp->private_data, buf, + sizeof(struct smem_log_item), + count / sizeof(struct smem_log_item)); + return count; +} + +static ssize_t smem_log_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + const char delimiters[] = " ,;"; + char locbuf[256] = {0}; + uint32_t val[10] = {0}; + int vals = 0; + char *token; + char *running; + struct smem_log_inst *inst; + unsigned long res; + + inst = fp->private_data; + + count = count > 255 ? 255 : count; + + if (!smem_log_enable) + return count; + + locbuf[count] = '\0'; + + ret = copy_from_user(locbuf, buf, count); + if (ret != 0) { + printk(KERN_ERR "ERROR: %s could not copy %i bytes\n", + __func__, ret); + return -EINVAL; + } + + D(KERN_ERR "%s: ", __func__); + D_DUMP_BUFFER("We got", len, locbuf); + + running = locbuf; + + token = strsep(&running, delimiters); + while (token && vals < ARRAY_SIZE(val)) { + if (*token != '\0') { + D(KERN_ERR "%s: ", __func__); + D_DUMP_BUFFER("", strlen(token), token); + ret = strict_strtoul(token, 0, &res); + if (ret) { + printk(KERN_ERR "ERROR: %s:%i got bad char " + "at strict_strtoul\n", + __func__, __LINE__-4); + return -EINVAL; + } + val[vals++] = res; + } + token = strsep(&running, delimiters); + } + + if (vals > 5) { + if (inst->which_log == GEN) + smem_log_event6(val[0], val[2], val[3], val[4], + val[7], val[8], val[9]); + else if (inst->which_log == STA) + smem_log_event6_to_static(val[0], + val[2], val[3], val[4], + val[7], val[8], val[9]); + else + return -1; + } else { + if (inst->which_log == GEN) + smem_log_event(val[0], val[2], val[3], val[4]); + else if (inst->which_log == STA) + smem_log_event_to_static(val[0], + val[2], val[3], val[4]); + else + return -1; + } + + return count; +} + +static int smem_log_open(struct inode *ip, struct file *fp) +{ + fp->private_data = &inst[GEN]; + + return 0; +} + + +static int smem_log_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static long smem_log_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg); + +static const struct file_operations smem_log_fops = { + .owner = THIS_MODULE, + .read = smem_log_read, + .write = smem_log_write, + .open = smem_log_open, + .release = smem_log_release, + .unlocked_ioctl = smem_log_ioctl, +}; + +static const struct file_operations smem_log_bin_fops = { + .owner = THIS_MODULE, + .read = smem_log_read_bin, + .write = smem_log_write_bin, + .open = smem_log_open, + .release = smem_log_release, + .unlocked_ioctl = smem_log_ioctl, +}; + +static long smem_log_ioctl(struct file *fp, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + default: + return -ENOTTY; + + case SMIOC_SETMODE: + if (arg == SMIOC_TEXT) { + D("%s set text mode\n", __func__); + fp->f_op = &smem_log_fops; + } else if (arg == SMIOC_BINARY) { + D("%s set bin mode\n", __func__); + fp->f_op = &smem_log_bin_fops; + } else { + return -EINVAL; + } + break; + case SMIOC_SETLOG: + if (arg == SMIOC_LOG) { + if (inst[GEN].events) + fp->private_data = &inst[GEN]; + else + return -ENODEV; + } else if (arg == SMIOC_STATIC_LOG) { + if (inst[STA].events) + fp->private_data = &inst[STA]; + else + return -ENODEV; + } else { + return -EINVAL; + } + break; + } + + return 0; +} + +static struct miscdevice smem_log_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "smem_log", + .fops = &smem_log_fops, +}; + +#if defined(CONFIG_DEBUG_FS) + +#define SMEM_LOG_ITEM_PRINT_SIZE 160 + +#define EVENTS_PRINT_SIZE \ +(SMEM_LOG_ITEM_PRINT_SIZE * SMEM_LOG_NUM_ENTRIES) + +static uint32_t smem_log_timeout_ms; +module_param_named(timeout_ms, smem_log_timeout_ms, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +static int smem_log_debug_mask; +module_param_named(debug_mask, smem_log_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +#define DBG(x...) do {\ + if (smem_log_debug_mask) \ + printk(KERN_DEBUG x);\ + } while (0) + +static int update_read_avail(struct smem_log_inst *inst) +{ + int curr_read_avail; + unsigned long flags = 0; + + if (!inst->idx) + return 0; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + curr_read_avail = (*inst->idx - inst->read_idx); + if (curr_read_avail < 0) + curr_read_avail = inst->num - inst->read_idx + *inst->idx; + + DBG("%s: read = %d write = %d curr = %d last = %d\n", __func__, + inst->read_idx, *inst->idx, curr_read_avail, inst->last_read_avail); + + if (curr_read_avail < inst->last_read_avail) { + if (inst->last_read_avail != inst->num) + pr_info("smem_log: skipping %d log entries\n", + inst->last_read_avail); + inst->read_idx = *inst->idx + 1; + inst->last_read_avail = inst->num - 1; + } else + inst->last_read_avail = curr_read_avail; + + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); + + DBG("%s: read = %d write = %d curr = %d last = %d\n", __func__, + inst->read_idx, *inst->idx, curr_read_avail, inst->last_read_avail); + + return inst->last_read_avail; +} + +static int _debug_dump(int log, char *buf, int max, uint32_t cont) +{ + unsigned int idx; + int write_idx, read_avail = 0; + unsigned long flags; + int i = 0; + + if (!inst[log].events) + return 0; + + if (cont && update_read_avail(&inst[log]) == 0) + return 0; + + remote_spin_lock_irqsave(inst[log].remote_spinlock, flags); + + if (cont) { + idx = inst[log].read_idx; + write_idx = (inst[log].read_idx + inst[log].last_read_avail); + if (write_idx >= inst[log].num) + write_idx -= inst[log].num; + } else { + write_idx = *inst[log].idx; + idx = (write_idx + 1); + } + + DBG("%s: read %d write %d idx %d num %d\n", __func__, + inst[log].read_idx, write_idx, idx, inst[log].num - 1); + + while ((max - i) > 50) { + if ((inst[log].num - 1) < idx) + idx = 0; + + if (idx == write_idx) + break; + + if (inst[log].events[idx].identifier) { + + i += scnprintf(buf + i, max - i, + "%08x %08x %08x %08x %08x\n", + inst[log].events[idx].identifier, + inst[log].events[idx].timetick, + inst[log].events[idx].data1, + inst[log].events[idx].data2, + inst[log].events[idx].data3); + } + idx++; + } + if (cont) { + inst[log].read_idx = idx; + read_avail = (write_idx - inst[log].read_idx); + if (read_avail < 0) + read_avail = inst->num - inst->read_idx + write_idx; + inst[log].last_read_avail = read_avail; + } + + remote_spin_unlock_irqrestore(inst[log].remote_spinlock, flags); + + DBG("%s: read %d write %d idx %d num %d\n", __func__, + inst[log].read_idx, write_idx, idx, inst[log].num); + + return i; +} + +static int _debug_dump_voters(char *buf, int max) +{ + int k, i = 0; + + find_voters(); + + i += scnprintf(buf + i, max - i, "Voters:\n"); + for (k = 0; k < ARRAY_SIZE(voter_d3_syms); ++k) + if (voter_d3_syms[k].str) + i += scnprintf(buf + i, max - i, "%s ", + voter_d3_syms[k].str); + for (k = 0; k < ARRAY_SIZE(voter_d2_syms); ++k) + if (voter_d2_syms[k].str) + i += scnprintf(buf + i, max - i, "%s ", + voter_d2_syms[k].str); + i += scnprintf(buf + i, max - i, "\n"); + + return i; +} + +static int _debug_dump_sym(int log, char *buf, int max, uint32_t cont) +{ + unsigned int idx; + int write_idx, read_avail = 0; + unsigned long flags; + int i = 0; + + char *proc; + char *sub; + char *id; + const char *sym = NULL; + + uint32_t data[3]; + + uint32_t proc_val = 0; + uint32_t sub_val = 0; + uint32_t id_val = 0; + uint32_t id_only_val = 0; + uint32_t data1 = 0; + uint32_t data2 = 0; + uint32_t data3 = 0; + + if (!inst[log].events) + return 0; + + find_voters(); + + if (cont && update_read_avail(&inst[log]) == 0) + return 0; + + remote_spin_lock_irqsave(inst[log].remote_spinlock, flags); + + if (cont) { + idx = inst[log].read_idx; + write_idx = (inst[log].read_idx + inst[log].last_read_avail); + if (write_idx >= inst[log].num) + write_idx -= inst[log].num; + } else { + write_idx = *inst[log].idx; + idx = (write_idx + 1); + } + + DBG("%s: read %d write %d idx %d num %d\n", __func__, + inst[log].read_idx, write_idx, idx, inst[log].num - 1); + + for (; (max - i) > SMEM_LOG_ITEM_PRINT_SIZE; idx++) { + if (idx > (inst[log].num - 1)) + idx = 0; + + if (idx == write_idx) + break; + + if (idx < inst[log].num) { + if (!inst[log].events[idx].identifier) + continue; + + proc_val = PROC & inst[log].events[idx].identifier; + sub_val = SUB & inst[log].events[idx].identifier; + id_val = (SUB | ID) & inst[log].events[idx].identifier; + id_only_val = ID & inst[log].events[idx].identifier; + data1 = inst[log].events[idx].data1; + data2 = inst[log].events[idx].data2; + data3 = inst[log].events[idx].data3; + + if (!(proc_val & SMEM_LOG_CONT)) { + i += scnprintf(buf + i, max - i, "\n"); + + proc = find_sym(ID_SYM, proc_val); + + if (proc) + i += scnprintf(buf + i, max - i, + "%4s: ", proc); + else + i += scnprintf(buf + i, max - i, + "%04x: ", + PROC & + inst[log].events[idx]. + identifier); + + i += scnprintf(buf + i, max - i, "%10u ", + inst[log].events[idx].timetick); + + sub = find_sym(BASE_SYM, sub_val); + + if (sub) + i += scnprintf(buf + i, max - i, + "%9s: ", sub); + else + i += scnprintf(buf + i, max - i, + "%08x: ", sub_val); + + id = find_sym(EVENT_SYM, id_val); + + if (id) + i += scnprintf(buf + i, max - i, + "%11s: ", id); + else + i += scnprintf(buf + i, max - i, + "%08x: ", id_only_val); + } + + if ((proc_val & SMEM_LOG_CONT) && + (id_val == ONCRPC_LOG_EVENT_STD_CALL || + id_val == ONCRPC_LOG_EVENT_STD_REPLY)) { + data[0] = data1; + data[1] = data2; + data[2] = data3; + i += scnprintf(buf + i, max - i, + " %.16s", (char *) data); + } else if (proc_val & SMEM_LOG_CONT) { + i += scnprintf(buf + i, max - i, + " %08x %08x %08x", + data1, data2, data3); + } else if (id_val == ONCRPC_LOG_EVENT_STD_CALL) { + sym = smd_rpc_get_sym(data2); + + if (sym) + i += scnprintf(buf + i, max - i, + "xid:%4i %8s proc:%3i", + data1, sym, data3); + else + i += scnprintf(buf + i, max - i, + "xid:%4i %08x proc:%3i", + data1, data2, data3); +#if defined(CONFIG_MSM_N_WAY_SMSM) + } else if (id_val == DEM_STATE_CHANGE) { + if (data1 == 1) { + i += scnprintf(buf + i, max - i, + "MASTER: "); + sym = find_sym(DEM_STATE_MASTER_SYM, + data2); + } else if (data1 == 0) { + i += scnprintf(buf + i, max - i, + " SLAVE: "); + sym = find_sym(DEM_STATE_SLAVE_SYM, + data2); + } else { + i += scnprintf(buf + i, max - i, + "%x: ", data1); + sym = NULL; + } + if (sym) + i += scnprintf(buf + i, max - i, + "from:%s ", sym); + else + i += scnprintf(buf + i, max - i, + "from:0x%x ", data2); + + if (data1 == 1) + sym = find_sym(DEM_STATE_MASTER_SYM, + data3); + else if (data1 == 0) + sym = find_sym(DEM_STATE_SLAVE_SYM, + data3); + else + sym = NULL; + if (sym) + i += scnprintf(buf + i, max - i, + "to:%s ", sym); + else + i += scnprintf(buf + i, max - i, + "to:0x%x ", data3); + + } else if (id_val == DEM_STATE_MACHINE_ENTER) { + i += scnprintf(buf + i, max - i, + "swfi:%i timer:%i manexit:%i", + data1, data2, data3); + + } else if (id_val == DEM_TIME_SYNC_REQUEST || + id_val == DEM_TIME_SYNC_POLL || + id_val == DEM_TIME_SYNC_INIT) { + sym = find_sym(SMSM_ENTRY_TYPE_SYM, + data1); + if (sym) + i += scnprintf(buf + i, max - i, + "hostid:%s", sym); + else + i += scnprintf(buf + i, max - i, + "hostid:%x", data1); + + } else if (id_val == DEM_TIME_SYNC_START || + id_val == DEM_TIME_SYNC_SEND_VALUE) { + unsigned mask = 0x1; + unsigned tmp = 0; + if (id_val == DEM_TIME_SYNC_START) + i += scnprintf(buf + i, max - i, + "req:"); + else + i += scnprintf(buf + i, max - i, + "pol:"); + while (mask) { + if (mask & data1) { + sym = find_sym( + SMSM_ENTRY_TYPE_SYM, + tmp); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%i ", + tmp); + } + mask <<= 1; + tmp++; + } + if (id_val == DEM_TIME_SYNC_SEND_VALUE) + i += scnprintf(buf + i, max - i, + "tick:%x", data2); + } else if (id_val == DEM_SMSM_ISR) { + unsigned vals[] = {data2, data3}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + sym = find_sym(SMSM_ENTRY_TYPE_SYM, + data1); + if (sym) + i += scnprintf(buf + i, max - i, + "%s ", sym); + else + i += scnprintf(buf + i, max - i, + "%x ", data1); + + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + i += scnprintf(buf + i, max - i, "["); + mask = 0x80000000; + once = 0; + while (mask) { + tmp = vals[j] & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(SMSM_STATE_SYM, + tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "0x%x", + tmp); + once = 1; + } + i += scnprintf(buf + i, max - i, "] "); + } +#else + } else if (id_val == DEMAPPS_WAKEUP_REASON) { + unsigned mask = 0x80000000; + unsigned tmp = 0; + while (mask) { + tmp = data1 & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(WAKEUP_SYM, tmp); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x ", + tmp); + } + i += scnprintf(buf + i, max - i, + "%08x %08x", data2, data3); + } else if (id_val == DEMMOD_APPS_WAKEUP_INT) { + sym = find_sym(WAKEUP_INT_SYM, data1); + + if (sym) + i += scnprintf(buf + i, max - i, + "%s %08x %08x", + sym, data2, data3); + else + i += scnprintf(buf + i, max - i, + "%08x %08x %08x", + data1, data2, data3); + } else if (id_val == DEM_NO_SLEEP || + id_val == NO_SLEEP_NEW) { + unsigned vals[] = {data3, data2}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + i += scnprintf(buf + i, max - i, "%08x ", + data1); + i += scnprintf(buf + i, max - i, "["); + once = 0; + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + mask = 0x00000001; + while (mask) { + tmp = vals[j] & mask; + mask <<= 1; + if (!tmp) + continue; + if (j == 0) + sym = find_sym( + VOTER_D3_SYM, + tmp); + else + sym = find_sym( + VOTER_D2_SYM, + tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x", + tmp); + once = 1; + } + } + i += scnprintf(buf + i, max - i, "] "); +#endif + } else if (id_val == SMEM_LOG_EVENT_CB) { + unsigned vals[] = {data2, data3}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + i += scnprintf(buf + i, max - i, "%08x ", + data1); + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + i += scnprintf(buf + i, max - i, "["); + mask = 0x80000000; + once = 0; + while (mask) { + tmp = vals[j] & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(SMSM_SYM, tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x", + tmp); + once = 1; + } + i += scnprintf(buf + i, max - i, "] "); + } + } else { + i += scnprintf(buf + i, max - i, + "%08x %08x %08x", + data1, data2, data3); + } + } + } + if (cont) { + inst[log].read_idx = idx; + read_avail = (write_idx - inst[log].read_idx); + if (read_avail < 0) + read_avail = inst->num - inst->read_idx + write_idx; + inst[log].last_read_avail = read_avail; + } + + remote_spin_unlock_irqrestore(inst[log].remote_spinlock, flags); + + DBG("%s: read %d write %d idx %d num %d\n", __func__, + inst[log].read_idx, write_idx, idx, inst[log].num); + + return i; +} + +static int debug_dump(char *buf, int max, uint32_t cont) +{ + int r; + + if (!inst[GEN].idx || !inst[GEN].events) + return -ENODEV; + + while (cont) { + update_read_avail(&inst[GEN]); + r = wait_event_interruptible_timeout(inst[GEN].read_wait, + inst[GEN].last_read_avail, + smem_log_timeout_ms * + HZ / 1000); + DBG("%s: read available %d\n", __func__, + inst[GEN].last_read_avail); + if (r < 0) + return 0; + else if (inst[GEN].last_read_avail) + break; + } + + return _debug_dump(GEN, buf, max, cont); +} + +static int debug_dump_sym(char *buf, int max, uint32_t cont) +{ + int r; + + if (!inst[GEN].idx || !inst[GEN].events) + return -ENODEV; + + while (cont) { + update_read_avail(&inst[GEN]); + r = wait_event_interruptible_timeout(inst[GEN].read_wait, + inst[GEN].last_read_avail, + smem_log_timeout_ms * + HZ / 1000); + DBG("%s: readavailable %d\n", __func__, + inst[GEN].last_read_avail); + if (r < 0) + return 0; + else if (inst[GEN].last_read_avail) + break; + } + + return _debug_dump_sym(GEN, buf, max, cont); +} + +static int debug_dump_static(char *buf, int max, uint32_t cont) +{ + int r; + + if (!inst[STA].idx || !inst[STA].events) + return -ENODEV; + + while (cont) { + update_read_avail(&inst[STA]); + r = wait_event_interruptible_timeout(inst[STA].read_wait, + inst[STA].last_read_avail, + smem_log_timeout_ms * + HZ / 1000); + DBG("%s: readavailable %d\n", __func__, + inst[STA].last_read_avail); + if (r < 0) + return 0; + else if (inst[STA].last_read_avail) + break; + } + + return _debug_dump(STA, buf, max, cont); +} + +static int debug_dump_static_sym(char *buf, int max, uint32_t cont) +{ + int r; + + if (!inst[STA].idx || !inst[STA].events) + return -ENODEV; + + while (cont) { + update_read_avail(&inst[STA]); + r = wait_event_interruptible_timeout(inst[STA].read_wait, + inst[STA].last_read_avail, + smem_log_timeout_ms * + HZ / 1000); + DBG("%s: readavailable %d\n", __func__, + inst[STA].last_read_avail); + if (r < 0) + return 0; + else if (inst[STA].last_read_avail) + break; + } + + return _debug_dump_sym(STA, buf, max, cont); +} + +static int debug_dump_power(char *buf, int max, uint32_t cont) +{ + int r; + + if (!inst[POW].idx || !inst[POW].events) + return -ENODEV; + + while (cont) { + update_read_avail(&inst[POW]); + r = wait_event_interruptible_timeout(inst[POW].read_wait, + inst[POW].last_read_avail, + smem_log_timeout_ms * + HZ / 1000); + DBG("%s: readavailable %d\n", __func__, + inst[POW].last_read_avail); + if (r < 0) + return 0; + else if (inst[POW].last_read_avail) + break; + } + + return _debug_dump(POW, buf, max, cont); +} + +static int debug_dump_power_sym(char *buf, int max, uint32_t cont) +{ + int r; + + if (!inst[POW].idx || !inst[POW].events) + return -ENODEV; + + while (cont) { + update_read_avail(&inst[POW]); + r = wait_event_interruptible_timeout(inst[POW].read_wait, + inst[POW].last_read_avail, + smem_log_timeout_ms * + HZ / 1000); + DBG("%s: readavailable %d\n", __func__, + inst[POW].last_read_avail); + if (r < 0) + return 0; + else if (inst[POW].last_read_avail) + break; + } + + return _debug_dump_sym(POW, buf, max, cont); +} + +static int debug_dump_voters(char *buf, int max, uint32_t cont) +{ + return _debug_dump_voters(buf, max); +} + +static char debug_buffer[EVENTS_PRINT_SIZE]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int r; + int bsize = 0; + int (*fill)(char *, int, uint32_t) = file->private_data; + if (!(*ppos)) { + bsize = fill(debug_buffer, EVENTS_PRINT_SIZE, 0); + + if (bsize < 0) + bsize = scnprintf(debug_buffer, + EVENTS_PRINT_SIZE, "Log not available\n"); + } + DBG("%s: count %d ppos %d\n", __func__, count, (unsigned int)*ppos); + r = simple_read_from_buffer(buf, count, ppos, debug_buffer, + bsize); + return r; +} + +static ssize_t debug_read_cont(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *, int, uint32_t) = file->private_data; + char *buffer = kmalloc(count, GFP_KERNEL); + int bsize; + if (!buffer) + return -ENOMEM; + + bsize = fill(buffer, count, 1); + if (bsize < 0) { + if (*ppos == 0) + bsize = scnprintf(buffer, count, "Log not available\n"); + else + bsize = 0; + } + + DBG("%s: count %d bsize %d\n", __func__, count, bsize); + if (copy_to_user(buf, buffer, bsize)) { + kfree(buffer); + return -EFAULT; + } + *ppos += bsize; + kfree(buffer); + return bsize; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static const struct file_operations debug_ops_cont = { + .read = debug_read_cont, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max, uint32_t cont), + const struct file_operations *fops) +{ + debugfs_create_file(name, mode, dent, fill, fops); +} + +static void smem_log_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smem_log", 0); + if (IS_ERR(dent)) + return; + + debug_create("dump", 0444, dent, debug_dump, &debug_ops); + debug_create("dump_sym", 0444, dent, debug_dump_sym, &debug_ops); + debug_create("dump_static", 0444, dent, debug_dump_static, &debug_ops); + debug_create("dump_static_sym", 0444, dent, + debug_dump_static_sym, &debug_ops); + debug_create("dump_power", 0444, dent, debug_dump_power, &debug_ops); + debug_create("dump_power_sym", 0444, dent, + debug_dump_power_sym, &debug_ops); + debug_create("dump_voters", 0444, dent, + debug_dump_voters, &debug_ops); + + debug_create("dump_cont", 0444, dent, debug_dump, &debug_ops_cont); + debug_create("dump_sym_cont", 0444, dent, + debug_dump_sym, &debug_ops_cont); + debug_create("dump_static_cont", 0444, dent, + debug_dump_static, &debug_ops_cont); + debug_create("dump_static_sym_cont", 0444, dent, + debug_dump_static_sym, &debug_ops_cont); + debug_create("dump_power_cont", 0444, dent, + debug_dump_power, &debug_ops_cont); + debug_create("dump_power_sym_cont", 0444, dent, + debug_dump_power_sym, &debug_ops_cont); + + smem_log_timeout_ms = 500; + smem_log_debug_mask = 0; +} +#else +static void smem_log_debugfs_init(void) {} +#endif + +static int smem_log_initialize(void) +{ + int ret; + + ret = _smem_log_init(); + if (ret < 0) { + pr_err("%s: init failed %d\n", __func__, ret); + return ret; + } + + ret = misc_register(&smem_log_dev); + if (ret < 0) { + pr_err("%s: device register failed %d\n", __func__, ret); + return ret; + } + + smem_log_enable = 1; + smem_log_initialized = 1; + smem_log_debugfs_init(); + return ret; +} + +static int smsm_driver_state_notifier(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + int ret = 0; + if (code & SMSM_INIT) { + if (!smem_log_initialized) + ret = smem_log_initialize(); + } + return ret; +} + +static struct notifier_block nb = { + .notifier_call = smsm_driver_state_notifier, +}; + +static int __init smem_log_init(void) +{ + return smsm_driver_state_notifier_register(&nb); +} + + +module_init(smem_log_init); + +MODULE_DESCRIPTION("smem log"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c new file mode 100644 index 00000000000..b047cf440a1 --- /dev/null +++ b/arch/arm/mach-msm/socinfo.c @@ -0,0 +1,799 @@ +/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * SOC Info Routines + * + */ + +#include +#include +#include +#include + +#include "smd_private.h" + +#define BUILD_ID_LENGTH 32 + +enum { + HW_PLATFORM_UNKNOWN = 0, + HW_PLATFORM_SURF = 1, + HW_PLATFORM_FFA = 2, + HW_PLATFORM_FLUID = 3, + HW_PLATFORM_SVLTE_FFA = 4, + HW_PLATFORM_SVLTE_SURF = 5, + HW_PLATFORM_MTP = 8, + HW_PLATFORM_LIQUID = 9, + /* Dragonboard platform id is assigned as 10 in CDT */ + HW_PLATFORM_DRAGON = 10, + HW_PLATFORM_INVALID +}; + +const char *hw_platform[] = { + [HW_PLATFORM_UNKNOWN] = "Unknown", + [HW_PLATFORM_SURF] = "Surf", + [HW_PLATFORM_FFA] = "FFA", + [HW_PLATFORM_FLUID] = "Fluid", + [HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA", + [HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF", + [HW_PLATFORM_MTP] = "MTP", + [HW_PLATFORM_LIQUID] = "Liquid", + [HW_PLATFORM_DRAGON] = "Dragon" +}; + +enum { + ACCESSORY_CHIP_UNKNOWN = 0, + ACCESSORY_CHIP_CHARM = 58, +}; + +enum { + PLATFORM_SUBTYPE_UNKNOWN = 0x0, + PLATFORM_SUBTYPE_CHARM = 0x1, + PLATFORM_SUBTYPE_STRANGE = 0x2, + PLATFORM_SUBTYPE_STRANGE_2A = 0x3, + PLATFORM_SUBTYPE_INVALID, +}; + +const char *hw_platform_subtype[] = { + [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown", + [PLATFORM_SUBTYPE_CHARM] = "charm", + [PLATFORM_SUBTYPE_STRANGE] = "strange", + [PLATFORM_SUBTYPE_STRANGE_2A] = "strange_2a," +}; + +/* Used to parse shared memory. Must match the modem. */ +struct socinfo_v1 { + uint32_t format; + uint32_t id; + uint32_t version; + char build_id[BUILD_ID_LENGTH]; +}; + +struct socinfo_v2 { + struct socinfo_v1 v1; + + /* only valid when format==2 */ + uint32_t raw_id; + uint32_t raw_version; +}; + +struct socinfo_v3 { + struct socinfo_v2 v2; + + /* only valid when format==3 */ + uint32_t hw_platform; +}; + +struct socinfo_v4 { + struct socinfo_v3 v3; + + /* only valid when format==4 */ + uint32_t platform_version; +}; + +struct socinfo_v5 { + struct socinfo_v4 v4; + + /* only valid when format==5 */ + uint32_t accessory_chip; +}; + +struct socinfo_v6 { + struct socinfo_v5 v5; + + /* only valid when format==6 */ + uint32_t hw_platform_subtype; +}; + +static union { + struct socinfo_v1 v1; + struct socinfo_v2 v2; + struct socinfo_v3 v3; + struct socinfo_v4 v4; + struct socinfo_v5 v5; + struct socinfo_v6 v6; +} *socinfo; + +static enum msm_cpu cpu_of_id[] = { + + /* 7x01 IDs */ + [1] = MSM_CPU_7X01, + [16] = MSM_CPU_7X01, + [17] = MSM_CPU_7X01, + [18] = MSM_CPU_7X01, + [19] = MSM_CPU_7X01, + [23] = MSM_CPU_7X01, + [25] = MSM_CPU_7X01, + [26] = MSM_CPU_7X01, + [32] = MSM_CPU_7X01, + [33] = MSM_CPU_7X01, + [34] = MSM_CPU_7X01, + [35] = MSM_CPU_7X01, + + /* 7x25 IDs */ + [20] = MSM_CPU_7X25, + [21] = MSM_CPU_7X25, /* 7225 */ + [24] = MSM_CPU_7X25, /* 7525 */ + [27] = MSM_CPU_7X25, /* 7625 */ + [39] = MSM_CPU_7X25, + [40] = MSM_CPU_7X25, + [41] = MSM_CPU_7X25, + [42] = MSM_CPU_7X25, + [62] = MSM_CPU_7X25, /* 7625-1 */ + [63] = MSM_CPU_7X25, /* 7225-1 */ + [66] = MSM_CPU_7X25, /* 7225-2 */ + + + /* 7x27 IDs */ + [43] = MSM_CPU_7X27, + [44] = MSM_CPU_7X27, + [61] = MSM_CPU_7X27, + [67] = MSM_CPU_7X27, /* 7227-1 */ + [68] = MSM_CPU_7X27, /* 7627-1 */ + [69] = MSM_CPU_7X27, /* 7627-2 */ + + + /* 8x50 IDs */ + [30] = MSM_CPU_8X50, + [36] = MSM_CPU_8X50, + [37] = MSM_CPU_8X50, + [38] = MSM_CPU_8X50, + + /* 7x30 IDs */ + [59] = MSM_CPU_7X30, + [60] = MSM_CPU_7X30, + + /* 8x55 IDs */ + [74] = MSM_CPU_8X55, + [75] = MSM_CPU_8X55, + [85] = MSM_CPU_8X55, + + /* 8x60 IDs */ + [70] = MSM_CPU_8X60, + [71] = MSM_CPU_8X60, + [86] = MSM_CPU_8X60, + + /* 8960 IDs */ + [87] = MSM_CPU_8960, + + /* 7x25A IDs */ + [88] = MSM_CPU_7X25A, + [89] = MSM_CPU_7X25A, + [96] = MSM_CPU_7X25A, + + /* 7x27A IDs */ + [90] = MSM_CPU_7X27A, + [91] = MSM_CPU_7X27A, + [92] = MSM_CPU_7X27A, + [97] = MSM_CPU_7X27A, + + /* FSM9xxx ID */ + [94] = FSM_CPU_9XXX, + [95] = FSM_CPU_9XXX, + + /* 7x25AA ID */ + [98] = MSM_CPU_7X25AA, + [99] = MSM_CPU_7X25AA, + [100] = MSM_CPU_7X25AA, + + /* 7x27AA ID */ + [101] = MSM_CPU_7X27AA, + [102] = MSM_CPU_7X27AA, + [103] = MSM_CPU_7X27AA, + + /* 9x15 ID */ + [104] = MSM_CPU_9615, + [105] = MSM_CPU_9615, + [106] = MSM_CPU_9615, + [107] = MSM_CPU_9615, + + /* 8064 IDs */ + [109] = MSM_CPU_8064, + + /* 8930 IDs */ + [116] = MSM_CPU_8930, + [117] = MSM_CPU_8930, + [118] = MSM_CPU_8930, + [119] = MSM_CPU_8930, + + /* 8627 IDs */ + [120] = MSM_CPU_8627, + [121] = MSM_CPU_8627, + + /* 8660A ID */ + [122] = MSM_CPU_8960, + + /* 8260A ID */ + [123] = MSM_CPU_8960, + + /* 8060A ID */ + [124] = MSM_CPU_8960, + + /* Copper IDs */ + [126] = MSM_CPU_COPPER, + + /* 8625 IDs */ + [127] = MSM_CPU_8625, + [128] = MSM_CPU_8625, + [129] = MSM_CPU_8625, + + /* 8064 MPQ ID */ + [130] = MSM_CPU_8064, + + /* 7x25AB IDs */ + [131] = MSM_CPU_7X25AB, + [132] = MSM_CPU_7X25AB, + [133] = MSM_CPU_7X25AB, + + /* 9625 IDs */ + [134] = MSM_CPU_9625, + + /* Uninitialized IDs are not known to run Linux. + MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are + considered as unknown CPU. */ +}; + +static enum msm_cpu cur_cpu; + +static struct socinfo_v1 dummy_socinfo = { + .format = 1, + .version = 1, +}; + +uint32_t socinfo_get_id(void) +{ + return (socinfo) ? socinfo->v1.id : 0; +} +EXPORT_SYMBOL_GPL(socinfo_get_id); + +uint32_t socinfo_get_version(void) +{ + return (socinfo) ? socinfo->v1.version : 0; +} + +char *socinfo_get_build_id(void) +{ + return (socinfo) ? socinfo->v1.build_id : NULL; +} + +uint32_t socinfo_get_raw_id(void) +{ + return socinfo ? + (socinfo->v1.format >= 2 ? socinfo->v2.raw_id : 0) + : 0; +} + +uint32_t socinfo_get_raw_version(void) +{ + return socinfo ? + (socinfo->v1.format >= 2 ? socinfo->v2.raw_version : 0) + : 0; +} + +uint32_t socinfo_get_platform_type(void) +{ + return socinfo ? + (socinfo->v1.format >= 3 ? socinfo->v3.hw_platform : 0) + : 0; +} + + +uint32_t socinfo_get_platform_version(void) +{ + return socinfo ? + (socinfo->v1.format >= 4 ? socinfo->v4.platform_version : 0) + : 0; +} + +/* This information is directly encoded by the machine id */ +/* Thus no external callers rely on this information at the moment */ +static uint32_t socinfo_get_accessory_chip(void) +{ + return socinfo ? + (socinfo->v1.format >= 5 ? socinfo->v5.accessory_chip : 0) + : 0; +} + +uint32_t socinfo_get_platform_subtype(void) +{ + return socinfo ? + (socinfo->v1.format >= 6 ? socinfo->v6.hw_platform_subtype : 0) + : 0; +} + +enum msm_cpu socinfo_get_msm_cpu(void) +{ + return cur_cpu; +} +EXPORT_SYMBOL_GPL(socinfo_get_msm_cpu); + +static ssize_t +socinfo_show_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_id()); +} + +static ssize_t +socinfo_show_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t version; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + version = socinfo_get_version(); + return snprintf(buf, PAGE_SIZE, "%u.%u\n", + SOCINFO_VERSION_MAJOR(version), + SOCINFO_VERSION_MINOR(version)); +} + +static ssize_t +socinfo_show_build_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", socinfo_get_build_id()); +} + +static ssize_t +socinfo_show_raw_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 2) { + pr_err("%s: Raw ID not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_id()); +} + +static ssize_t +socinfo_show_raw_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 2) { + pr_err("%s: Raw version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_version()); +} + +static ssize_t +socinfo_show_platform_type(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_type; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 3) { + pr_err("%s: platform type not available!\n", __func__); + return 0; + } + + hw_type = socinfo_get_platform_type(); + if (hw_type >= HW_PLATFORM_INVALID) { + pr_err("%s: Invalid hardware platform type found\n", + __func__); + hw_type = HW_PLATFORM_UNKNOWN; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", hw_platform[hw_type]); +} + +static ssize_t +socinfo_show_platform_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 4) { + pr_err("%s: platform version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + socinfo_get_platform_version()); +} + +static ssize_t +socinfo_show_accessory_chip(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 5) { + pr_err("%s: accessory chip not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + socinfo_get_accessory_chip()); +} + +static ssize_t +socinfo_show_platform_subtype(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_subtype; + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 6) { + pr_err("%s: platform subtype not available!\n", __func__); + return 0; + } + + hw_subtype = socinfo_get_platform_subtype(); + if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) { + pr_err("%s: Invalid hardware platform sub type found\n", + __func__); + hw_subtype = PLATFORM_SUBTYPE_UNKNOWN; + } + return snprintf(buf, PAGE_SIZE, "%-.32s\n", + hw_platform_subtype[hw_subtype]); +} + +static struct sysdev_attribute socinfo_v1_files[] = { + _SYSDEV_ATTR(id, 0444, socinfo_show_id, NULL), + _SYSDEV_ATTR(version, 0444, socinfo_show_version, NULL), + _SYSDEV_ATTR(build_id, 0444, socinfo_show_build_id, NULL), +}; + +static struct sysdev_attribute socinfo_v2_files[] = { + _SYSDEV_ATTR(raw_id, 0444, socinfo_show_raw_id, NULL), + _SYSDEV_ATTR(raw_version, 0444, socinfo_show_raw_version, NULL), +}; + +static struct sysdev_attribute socinfo_v3_files[] = { + _SYSDEV_ATTR(hw_platform, 0444, socinfo_show_platform_type, NULL), +}; + +static struct sysdev_attribute socinfo_v4_files[] = { + _SYSDEV_ATTR(platform_version, 0444, + socinfo_show_platform_version, NULL), +}; + +static struct sysdev_attribute socinfo_v5_files[] = { + _SYSDEV_ATTR(accessory_chip, 0444, + socinfo_show_accessory_chip, NULL), +}; + +static struct sysdev_attribute socinfo_v6_files[] = { + _SYSDEV_ATTR(platform_subtype, 0444, + socinfo_show_platform_subtype, NULL), +}; + +static struct sysdev_class soc_sysdev_class = { + .name = "soc", +}; + +static struct sys_device soc_sys_device = { + .id = 0, + .cls = &soc_sysdev_class, +}; + +static int __init socinfo_create_files(struct sys_device *dev, + struct sysdev_attribute files[], + int size) +{ + int i; + for (i = 0; i < size; i++) { + int err = sysdev_create_file(dev, &files[i]); + if (err) { + pr_err("%s: sysdev_create_file(%s)=%d\n", + __func__, files[i].attr.name, err); + return err; + } + } + return 0; +} + +static int __init socinfo_init_sysdev(void) +{ + int err; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return -ENODEV; + } + + err = sysdev_class_register(&soc_sysdev_class); + if (err) { + pr_err("%s: sysdev_class_register fail (%d)\n", + __func__, err); + return err; + } + err = sysdev_register(&soc_sys_device); + if (err) { + pr_err("%s: sysdev_register fail (%d)\n", + __func__, err); + return err; + } + socinfo_create_files(&soc_sys_device, socinfo_v1_files, + ARRAY_SIZE(socinfo_v1_files)); + if (socinfo->v1.format < 2) + return err; + socinfo_create_files(&soc_sys_device, socinfo_v2_files, + ARRAY_SIZE(socinfo_v2_files)); + + if (socinfo->v1.format < 3) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v3_files, + ARRAY_SIZE(socinfo_v3_files)); + + if (socinfo->v1.format < 4) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v4_files, + ARRAY_SIZE(socinfo_v4_files)); + + if (socinfo->v1.format < 5) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v5_files, + ARRAY_SIZE(socinfo_v5_files)); + + if (socinfo->v1.format < 6) + return err; + + return socinfo_create_files(&soc_sys_device, socinfo_v6_files, + ARRAY_SIZE(socinfo_v6_files)); + +} + +arch_initcall(socinfo_init_sysdev); + +static void * __init setup_dummy_socinfo(void) +{ + if (machine_is_msm8960_rumi3() || machine_is_msm8960_sim() || + machine_is_msm8960_cdp()) + dummy_socinfo.id = 87; + else if (machine_is_apq8064_rumi3() || machine_is_apq8064_sim()) + dummy_socinfo.id = 109; + else if (machine_is_msm9615_mtp() || machine_is_msm9615_cdp()) + dummy_socinfo.id = 104; + else if (early_machine_is_copper()) { + dummy_socinfo.id = 126; + strlcpy(dummy_socinfo.build_id, "copper - ", + sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_msm9625()) { + dummy_socinfo.id = 134; + strlcpy(dummy_socinfo.build_id, "msm9625 - ", + sizeof(dummy_socinfo.build_id)); + } else if (machine_is_msm8625_rumi3()) + dummy_socinfo.id = 127; + strlcat(dummy_socinfo.build_id, "Dummy socinfo", + sizeof(dummy_socinfo.build_id)); + return (void *) &dummy_socinfo; +} + +int __init socinfo_init(void) +{ + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v6)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v5)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v4)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v3)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v2)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v1)); + + if (!socinfo) { + pr_warn("%s: Can't find SMEM_HW_SW_BUILD_ID; falling back on " + "dummy values.\n", __func__); + socinfo = setup_dummy_socinfo(); + } + + WARN(!socinfo_get_id(), "Unknown SOC ID!\n"); + WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id), + "New IDs added! ID => CPU mapping might need an update.\n"); + + if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id)) + cur_cpu = cpu_of_id[socinfo->v1.id]; + + switch (socinfo->v1.format) { + case 1: + pr_info("%s: v%u, id=%u, ver=%u.%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version)); + break; + case 2: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version); + break; + case 3: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform); + break; + case 4: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version); + break; + case 5: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" + " accessory_chip=%u\n", __func__, socinfo->v1.format, + socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version, + socinfo->v5.accessory_chip); + break; + case 6: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" + " accessory_chip=%u hw_plat_subtype=%u\n", __func__, + socinfo->v1.format, + socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version, + socinfo->v5.accessory_chip, + socinfo->v6.hw_platform_subtype); + break; + default: + pr_err("%s: Unknown format found\n", __func__); + break; + } + + return 0; +} + +const int get_core_count(void) +{ + if (!(read_cpuid_mpidr() & BIT(31))) + return 1; + + if (read_cpuid_mpidr() & BIT(30) && + !machine_is_msm8960_sim() && + !machine_is_apq8064_sim()) + return 1; + + /* 1 + the PART[1:0] field of MIDR */ + return ((read_cpuid_id() >> 4) & 3) + 1; +} + +const int read_msm_cpu_type(void) +{ + if (machine_is_msm8960_sim() || machine_is_msm8960_rumi3()) + return MSM_CPU_8960; + + if (socinfo_get_msm_cpu() != MSM_CPU_UNKNOWN) + return socinfo_get_msm_cpu(); + + switch (read_cpuid_id()) { + case 0x510F02D0: + case 0x510F02D2: + case 0x510F02D4: + return MSM_CPU_8X60; + + case 0x510F04D0: + case 0x510F04D1: + case 0x510F04D2: + case 0x511F04D0: + case 0x512F04D0: + return MSM_CPU_8960; + + case 0x51404D11: /* We can't get here unless we are in bringup */ + return MSM_CPU_8930; + + case 0x510F06F0: + return MSM_CPU_8064; + + default: + return MSM_CPU_UNKNOWN; + }; +} + +const int cpu_is_krait_v1(void) +{ + switch (read_cpuid_id()) { + case 0x510F04D0: + case 0x510F04D1: + case 0x510F04D2: + return 1; + + default: + return 0; + }; +} diff --git a/arch/arm/mach-msm/spm-v2.c b/arch/arm/mach-msm/spm-v2.c new file mode 100644 index 00000000000..b6d532424f5 --- /dev/null +++ b/arch/arm/mach-msm/spm-v2.c @@ -0,0 +1,467 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "spm_driver.h" + +#define MSM_SPM_PMIC_STATE_IDLE 0 + +#define SAW2_V1_VER_REG 0x04 +#define SAW2_V2_VER_REG 0xfd0 + +#define SAW2_MAJOR_2 2 + + +enum { + MSM_SPM_DEBUG_SHADOW = 1U << 0, + MSM_SPM_DEBUG_VCTL = 1U << 1, +}; + +static int msm_spm_debug_mask; +module_param_named( + debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + + +static uint32_t msm_spm_reg_offsets_v1[MSM_SPM_REG_NR] = { + [MSM_SPM_REG_SAW2_SECURE] = 0x00, + [MSM_SPM_REG_SAW2_ID] = 0x04, + [MSM_SPM_REG_SAW2_CFG] = 0x08, + [MSM_SPM_REG_SAW2_STS0] = 0x0C, + [MSM_SPM_REG_SAW2_STS1] = 0x10, + [MSM_SPM_REG_SAW2_VCTL] = 0x14, + [MSM_SPM_REG_SAW2_AVS_CTL] = 0x18, + [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x1C, + [MSM_SPM_REG_SAW2_SPM_CTL] = 0x20, + [MSM_SPM_REG_SAW2_PMIC_DLY] = 0x24, + [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x28, + [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x2C, + [MSM_SPM_REG_SAW2_RST] = 0x30, + [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80, +}; + +static uint32_t msm_spm_reg_offsets_v2[MSM_SPM_REG_NR] = { + [MSM_SPM_REG_SAW2_SECURE] = 0x00, + [MSM_SPM_REG_SAW2_ID] = 0x04, + [MSM_SPM_REG_SAW2_CFG] = 0x08, + [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C, + [MSM_SPM_REG_SAW2_AVS_STS] = 0x10, + [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14, + [MSM_SPM_REG_SAW2_RST] = 0x18, + [MSM_SPM_REG_SAW2_VCTL] = 0x1C, + [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20, + [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24, + [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28, + [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C, + [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30, + [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34, + [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40, + [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44, + [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48, + [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C, + [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50, + [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54, + [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58, + [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C, + [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80, + [MSM_SPM_REG_SAW2_VERSION] = 0xFD0, +}; + +/****************************************************************************** + * Internal helper functions + *****************************************************************************/ + +static inline uint32_t msm_spm_drv_get_num_spm_entry( + struct msm_spm_driver_data *dev) +{ + return 32; +} + +static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev, + unsigned int reg_index) +{ + __raw_writel(dev->reg_shadow[reg_index], + dev->reg_base_addr + dev->reg_offsets[reg_index]); +} + +static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev, + unsigned int reg_index) +{ + dev->reg_shadow[reg_index] = + __raw_readl(dev->reg_base_addr + + dev->reg_offsets[reg_index]); +} + +static inline void msm_spm_drv_set_start_addr( + struct msm_spm_driver_data *dev, uint32_t addr) +{ + addr &= 0x7F; + addr <<= 4; + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F; + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr; +} + +static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev) +{ + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID); + + if (dev->major == SAW2_MAJOR_2) + return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1; + else + return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 18) & 0x1; +} + +static inline void msm_spm_drv_set_vctl(struct msm_spm_driver_data *dev, + uint32_t vlevel) +{ + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0xFF; + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= vlevel; + + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] &= ~0xFF; + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] |= vlevel; + + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] &= ~0x3F; + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] |= (vlevel & 0x3F); +} + +static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev, + uint32_t vlevel) +{ + unsigned int pmic_data = 0; + + pmic_data |= vlevel; + pmic_data |= (dev->vctl_port & 0x7) << 16; + + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF; + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data; + + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] &= ~0x700FF; + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] |= pmic_data; +} + +static inline void msm_spm_drv_apcs_set_vctl(struct msm_spm_driver_data *dev, + unsigned int vlevel) +{ + if (dev->major == SAW2_MAJOR_2) + return msm_spm_drv_set_vctl2(dev, vlevel); + else + return msm_spm_drv_set_vctl(dev, vlevel); +} + +static inline uint32_t msm_spm_drv_get_sts_pmic_state( + struct msm_spm_driver_data *dev) +{ + if (dev->major == SAW2_MAJOR_2) { + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS); + return (dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] >> 16) & + 0x03; + } else { + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0); + return (dev->reg_shadow[MSM_SPM_REG_SAW2_STS0] >> 10) & 0x03; + } +} + +static inline uint32_t msm_spm_drv_get_sts_curr_pmic_data( + struct msm_spm_driver_data *dev) +{ + if (dev->major == SAW2_MAJOR_2) { + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS); + return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] & 0xFF; + } else { + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS1); + return dev->reg_shadow[MSM_SPM_REG_SAW2_STS1] & 0xFF; + } +} + +static inline uint32_t msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev, + uint32_t *major, uint32_t *minor) +{ + int ret = -ENODEV; + uint32_t val = 0; + + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_VERSION); + val = dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION]; + + if (dev->ver_reg == SAW2_V2_VER_REG) { + *major = (val >> 28) & 0xF; + *minor = (val >> 16) & 0xFFF; + ret = 0; + } else if (dev->ver_reg == SAW2_V1_VER_REG) { + *major = (val >> 4) & 0xF; + *minor = val & 0xF; + ret = 0; + } + + return ret; +} + +/****************************************************************************** + * Public functions + *****************************************************************************/ + +inline int msm_spm_drv_set_spm_enable( + struct msm_spm_driver_data *dev, bool enable) +{ + uint32_t value = enable ? 0x01 : 0x00; + + if (!dev) + return -EINVAL; + + if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) { + + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1; + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value; + + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); + wmb(); + } + return 0; +} +void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev) +{ + int i; + int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev); + + if (!dev) { + __WARN(); + return; + } + + for (i = 0; i < num_spm_entry; i++) { + __raw_writel(dev->reg_seq_entry_shadow[i], + dev->reg_base_addr + + dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY] + + 4 * i); + } + mb(); +} + +int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev, + uint8_t *cmd, uint32_t *offset) +{ + uint32_t cmd_w; + uint32_t offset_w = *offset / 4; + uint8_t last_cmd; + + if (!cmd) + return -EINVAL; + + while (1) { + int i; + cmd_w = 0; + last_cmd = 0; + cmd_w = dev->reg_seq_entry_shadow[offset_w]; + + for (i = (*offset % 4) ; i < 4; i++) { + last_cmd = *(cmd++); + cmd_w |= last_cmd << (i * 8); + (*offset)++; + if (last_cmd == 0x0f) + break; + } + + dev->reg_seq_entry_shadow[offset_w++] = cmd_w; + if (last_cmd == 0x0f) + break; + } + + return 0; +} + +int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev, + uint32_t addr) +{ + + /* SPM is configured to reset start address to zero after end of Program + */ + if (!dev) + return -EINVAL; + + msm_spm_drv_set_start_addr(dev, addr); + + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); + wmb(); + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) { + int i; + for (i = 0; i < MSM_SPM_REG_NR; i++) + pr_info("%s: reg %02x = 0x%08x\n", __func__, + dev->reg_offsets[i], dev->reg_shadow[i]); + } + + return 0; +} + +int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel) +{ + uint32_t timeout_us; + + if (!dev) + return -EINVAL; + + if (!msm_spm_pmic_arb_present(dev)) + return -ENOSYS; + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: requesting vlevel 0x%x\n", + __func__, vlevel); + + msm_spm_drv_apcs_set_vctl(dev, vlevel); + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL); + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_0); + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_1); + mb(); + + /* Wait for PMIC state to return to idle or until timeout */ + timeout_us = dev->vctl_timeout_us; + while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) { + if (!timeout_us) + goto set_vdd_bail; + + if (timeout_us > 10) { + udelay(10); + timeout_us -= 10; + } else { + udelay(timeout_us); + timeout_us = 0; + } + } + + if (msm_spm_drv_get_sts_curr_pmic_data(dev) != vlevel) + goto set_vdd_bail; + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: done, remaining timeout %uus\n", + __func__, timeout_us); + + return 0; + +set_vdd_bail: + pr_err("%s: failed, remaining timeout %uus, vlevel 0x%x\n", + __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev)); + return -EIO; +} + +int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev, + unsigned int phase_cnt) +{ + unsigned int pmic_data = 0; + unsigned int timeout_us = 0; + + if (dev->major != SAW2_MAJOR_2) + return -ENODEV; + + pmic_data |= phase_cnt & 0xFF; + pmic_data |= (dev->phase_port & 0x7) << 16; + + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF; + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data; + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL); + mb(); + + /* Wait for PMIC state to return to idle or until timeout */ + timeout_us = dev->vctl_timeout_us; + while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) { + if (!timeout_us) + goto set_phase_bail; + + if (timeout_us > 10) { + udelay(10); + timeout_us -= 10; + } else { + udelay(timeout_us); + timeout_us = 0; + } + } + + if (msm_spm_drv_get_sts_curr_pmic_data(dev) != phase_cnt) + goto set_phase_bail; + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: done, remaining timeout %uus\n", + __func__, timeout_us); + + return 0; + +set_phase_bail: + pr_err("%s: failed, remaining timeout %uus, phase count %d\n", + __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev)); + return -EIO; + +} + +void msm_spm_drv_reinit(struct msm_spm_driver_data *dev) +{ + int i; + + for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) + msm_spm_drv_flush_shadow(dev, i); + + msm_spm_drv_flush_seq_entry(dev); + mb(); +} + +int __devinit msm_spm_drv_init(struct msm_spm_driver_data *dev, + struct msm_spm_platform_data *data) +{ + int i; + int num_spm_entry; + + BUG_ON(!dev || !data); + + if (dev->ver_reg == SAW2_V2_VER_REG) + dev->reg_offsets = msm_spm_reg_offsets_v2; + else + dev->reg_offsets = msm_spm_reg_offsets_v1; + + dev->vctl_port = data->vctl_port; + dev->phase_port = data->phase_port; + dev->reg_base_addr = data->reg_base_addr; + memcpy(dev->reg_shadow, data->reg_init_values, + sizeof(data->reg_init_values)); + + dev->vctl_timeout_us = data->vctl_timeout_us; + + for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) + msm_spm_drv_flush_shadow(dev, i); + /* barrier to ensure write completes before we update shadow + * registers + */ + mb(); + + for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) + msm_spm_drv_load_shadow(dev, i); + + /* barrier to ensure read completes before we proceed further*/ + mb(); + + msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor); + + num_spm_entry = msm_spm_drv_get_num_spm_entry(dev); + + dev->reg_seq_entry_shadow = + kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry, + GFP_KERNEL); + + if (!dev->reg_seq_entry_shadow) + return -ENOMEM; + + return 0; +} diff --git a/arch/arm/mach-msm/spm.c b/arch/arm/mach-msm/spm.c new file mode 100644 index 00000000000..4654fba0b6f --- /dev/null +++ b/arch/arm/mach-msm/spm.c @@ -0,0 +1,303 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "spm.h" + + +enum { + MSM_SPM_DEBUG_SHADOW = 1U << 0, + MSM_SPM_DEBUG_VCTL = 1U << 1, +}; + +static int msm_spm_debug_mask; +module_param_named( + debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + +#define MSM_SPM_PMIC_STATE_IDLE 0 + +static uint32_t msm_spm_reg_offsets[MSM_SPM_REG_NR] = { + [MSM_SPM_REG_SAW_AVS_CTL] = 0x04, + + [MSM_SPM_REG_SAW_VCTL] = 0x08, + [MSM_SPM_REG_SAW_STS] = 0x0C, + [MSM_SPM_REG_SAW_CFG] = 0x10, + + [MSM_SPM_REG_SAW_SPM_CTL] = 0x14, + [MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY] = 0x18, + [MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY] = 0x1C, + + [MSM_SPM_REG_SAW_SPM_PMIC_CTL] = 0x20, + [MSM_SPM_REG_SAW_SLP_CLK_EN] = 0x24, + [MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN] = 0x28, + [MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN] = 0x2C, + + [MSM_SPM_REG_SAW_SLP_CLMP_EN] = 0x30, + [MSM_SPM_REG_SAW_SLP_RST_EN] = 0x34, + [MSM_SPM_REG_SAW_SPM_MPM_CFG] = 0x38, +}; + +struct msm_spm_device { + void __iomem *reg_base_addr; + uint32_t reg_shadow[MSM_SPM_REG_NR]; + + uint8_t awake_vlevel; + uint8_t retention_vlevel; + uint8_t collapse_vlevel; + uint8_t retention_mid_vlevel; + uint8_t collapse_mid_vlevel; + + uint32_t vctl_timeout_us; + + unsigned int low_power_mode; + bool notify_rpm; + bool dirty; +}; + +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_spm_devices); +static atomic_t msm_spm_set_vdd_x_cpu_allowed = ATOMIC_INIT(1); + +/****************************************************************************** + * Internal helper functions + *****************************************************************************/ + +static inline void msm_spm_set_vctl( + struct msm_spm_device *dev, uint32_t vlevel) +{ + dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0xFF; + dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= vlevel; +} + +static inline void msm_spm_set_spm_ctl(struct msm_spm_device *dev, + uint32_t rpm_bypass, uint32_t mode_encoding) +{ + dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] &= ~0x0F; + dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] |= rpm_bypass << 3; + dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] |= mode_encoding; +} + +static inline void msm_spm_set_pmic_ctl(struct msm_spm_device *dev, + uint32_t awake_vlevel, uint32_t mid_vlevel, uint32_t sleep_vlevel) +{ + dev->reg_shadow[MSM_SPM_REG_SAW_SPM_PMIC_CTL] = + (mid_vlevel << 16) | (awake_vlevel << 8) | (sleep_vlevel); +} + +static inline void msm_spm_set_slp_rst_en( + struct msm_spm_device *dev, uint32_t slp_rst_en) +{ + dev->reg_shadow[MSM_SPM_REG_SAW_SLP_RST_EN] = slp_rst_en; +} + +static inline void msm_spm_flush_shadow( + struct msm_spm_device *dev, unsigned int reg_index) +{ + __raw_writel(dev->reg_shadow[reg_index], + dev->reg_base_addr + msm_spm_reg_offsets[reg_index]); +} + +static inline void msm_spm_load_shadow( + struct msm_spm_device *dev, unsigned int reg_index) +{ + dev->reg_shadow[reg_index] = __raw_readl(dev->reg_base_addr + + msm_spm_reg_offsets[reg_index]); +} + +static inline uint32_t msm_spm_get_sts_pmic_state(struct msm_spm_device *dev) +{ + return (dev->reg_shadow[MSM_SPM_REG_SAW_STS] >> 20) & 0x03; +} + +static inline uint32_t msm_spm_get_sts_curr_pmic_data( + struct msm_spm_device *dev) +{ + return (dev->reg_shadow[MSM_SPM_REG_SAW_STS] >> 10) & 0xFF; +} + +/****************************************************************************** + * Public functions + *****************************************************************************/ +int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm) +{ + struct msm_spm_device *dev = &__get_cpu_var(msm_spm_devices); + uint32_t rpm_bypass = notify_rpm ? 0x00 : 0x01; + + if (mode == dev->low_power_mode && notify_rpm == dev->notify_rpm + && !dev->dirty) + return 0; + + switch (mode) { + case MSM_SPM_MODE_CLOCK_GATING: + msm_spm_set_spm_ctl(dev, rpm_bypass, 0x00); + msm_spm_set_slp_rst_en(dev, 0x00); + break; + + case MSM_SPM_MODE_POWER_RETENTION: + msm_spm_set_spm_ctl(dev, rpm_bypass, 0x02); + msm_spm_set_pmic_ctl(dev, dev->awake_vlevel, + dev->retention_mid_vlevel, dev->retention_vlevel); + msm_spm_set_slp_rst_en(dev, 0x00); + break; + + case MSM_SPM_MODE_POWER_COLLAPSE: + msm_spm_set_spm_ctl(dev, rpm_bypass, 0x02); + msm_spm_set_pmic_ctl(dev, dev->awake_vlevel, + dev->collapse_mid_vlevel, dev->collapse_vlevel); + msm_spm_set_slp_rst_en(dev, 0x01); + break; + + default: + BUG(); + } + + msm_spm_flush_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL); + msm_spm_flush_shadow(dev, MSM_SPM_REG_SAW_SPM_PMIC_CTL); + msm_spm_flush_shadow(dev, MSM_SPM_REG_SAW_SLP_RST_EN); + /* Ensure that the registers are written before returning */ + mb(); + + dev->low_power_mode = mode; + dev->notify_rpm = notify_rpm; + dev->dirty = false; + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) { + int i; + for (i = 0; i < MSM_SPM_REG_NR; i++) + pr_info("%s: reg %02x = 0x%08x\n", __func__, + msm_spm_reg_offsets[i], dev->reg_shadow[i]); + } + + return 0; +} + +int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel) +{ + unsigned long flags; + struct msm_spm_device *dev; + uint32_t timeout_us; + + local_irq_save(flags); + + if (!atomic_read(&msm_spm_set_vdd_x_cpu_allowed) && + unlikely(smp_processor_id() != cpu)) { + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: attempting to set vdd of cpu %u from " + "cpu %u\n", __func__, cpu, smp_processor_id()); + goto set_vdd_x_cpu_bail; + } + + dev = &per_cpu(msm_spm_devices, cpu); + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: requesting cpu %u vlevel 0x%x\n", + __func__, cpu, vlevel); + + msm_spm_set_vctl(dev, vlevel); + msm_spm_flush_shadow(dev, MSM_SPM_REG_SAW_VCTL); + + /* Wait for PMIC state to return to idle or until timeout */ + timeout_us = dev->vctl_timeout_us; + msm_spm_load_shadow(dev, MSM_SPM_REG_SAW_STS); + while (msm_spm_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) { + if (!timeout_us) + goto set_vdd_bail; + + if (timeout_us > 10) { + udelay(10); + timeout_us -= 10; + } else { + udelay(timeout_us); + timeout_us = 0; + } + msm_spm_load_shadow(dev, MSM_SPM_REG_SAW_STS); + } + + if (msm_spm_get_sts_curr_pmic_data(dev) != vlevel) + goto set_vdd_bail; + + dev->awake_vlevel = vlevel; + dev->dirty = true; + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: cpu %u done, remaining timeout %uus\n", + __func__, cpu, timeout_us); + + local_irq_restore(flags); + return 0; + +set_vdd_bail: + pr_err("%s: cpu %u failed, remaining timeout %uus, vlevel 0x%x\n", + __func__, cpu, timeout_us, msm_spm_get_sts_curr_pmic_data(dev)); + +set_vdd_x_cpu_bail: + local_irq_restore(flags); + return -EIO; +} + +void msm_spm_reinit(void) +{ + struct msm_spm_device *dev = &__get_cpu_var(msm_spm_devices); + int i; + + for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) + msm_spm_flush_shadow(dev, i); + + /* Ensure that the registers are written before returning */ + mb(); +} + +void msm_spm_allow_x_cpu_set_vdd(bool allowed) +{ + atomic_set(&msm_spm_set_vdd_x_cpu_allowed, allowed ? 1 : 0); +} + +int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs) +{ + unsigned int cpu; + + BUG_ON(nr_devs < num_possible_cpus()); + for_each_possible_cpu(cpu) { + struct msm_spm_device *dev = &per_cpu(msm_spm_devices, cpu); + int i; + + dev->reg_base_addr = data[cpu].reg_base_addr; + memcpy(dev->reg_shadow, data[cpu].reg_init_values, + sizeof(data[cpu].reg_init_values)); + + dev->awake_vlevel = data[cpu].awake_vlevel; + dev->retention_vlevel = data[cpu].retention_vlevel; + dev->collapse_vlevel = data[cpu].collapse_vlevel; + dev->retention_mid_vlevel = data[cpu].retention_mid_vlevel; + dev->collapse_mid_vlevel = data[cpu].collapse_mid_vlevel; + dev->vctl_timeout_us = data[cpu].vctl_timeout_us; + + for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) + msm_spm_flush_shadow(dev, i); + + /* Ensure that the registers are written before returning */ + mb(); + + dev->low_power_mode = MSM_SPM_MODE_CLOCK_GATING; + dev->notify_rpm = false; + dev->dirty = true; + } + + return 0; +} diff --git a/arch/arm/mach-msm/spm.h b/arch/arm/mach-msm/spm.h new file mode 100644 index 00000000000..154303b6675 --- /dev/null +++ b/arch/arm/mach-msm/spm.h @@ -0,0 +1,270 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ARCH_ARM_MACH_MSM_SPM_H +#define __ARCH_ARM_MACH_MSM_SPM_H +enum { + MSM_SPM_MODE_DISABLED, + MSM_SPM_MODE_CLOCK_GATING, + MSM_SPM_MODE_POWER_RETENTION, + MSM_SPM_MODE_POWER_COLLAPSE, + MSM_SPM_MODE_NR +}; + +enum { + MSM_SPM_L2_MODE_DISABLED = MSM_SPM_MODE_DISABLED, + MSM_SPM_L2_MODE_RETENTION, + MSM_SPM_L2_MODE_GDHS, + MSM_SPM_L2_MODE_POWER_COLLAPSE, +}; + +#if defined(CONFIG_MSM_SPM_V1) + +enum { + MSM_SPM_REG_SAW_AVS_CTL, + MSM_SPM_REG_SAW_CFG, + MSM_SPM_REG_SAW_SPM_CTL, + MSM_SPM_REG_SAW_SPM_SLP_TMR_DLY, + MSM_SPM_REG_SAW_SPM_WAKE_TMR_DLY, + MSM_SPM_REG_SAW_SLP_CLK_EN, + MSM_SPM_REG_SAW_SLP_HSFS_PRECLMP_EN, + MSM_SPM_REG_SAW_SLP_HSFS_POSTCLMP_EN, + MSM_SPM_REG_SAW_SLP_CLMP_EN, + MSM_SPM_REG_SAW_SLP_RST_EN, + MSM_SPM_REG_SAW_SPM_MPM_CFG, + MSM_SPM_REG_NR_INITIALIZE, + + MSM_SPM_REG_SAW_VCTL = MSM_SPM_REG_NR_INITIALIZE, + MSM_SPM_REG_SAW_STS, + MSM_SPM_REG_SAW_SPM_PMIC_CTL, + MSM_SPM_REG_NR +}; + +struct msm_spm_platform_data { + void __iomem *reg_base_addr; + uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE]; + + uint8_t awake_vlevel; + uint8_t retention_vlevel; + uint8_t collapse_vlevel; + uint8_t retention_mid_vlevel; + uint8_t collapse_mid_vlevel; + + uint32_t vctl_timeout_us; +}; + +#elif defined(CONFIG_MSM_SPM_V2) + +enum { + MSM_SPM_REG_SAW2_CFG, + MSM_SPM_REG_SAW2_AVS_CTL, + MSM_SPM_REG_SAW2_AVS_HYSTERESIS, + MSM_SPM_REG_SAW2_SPM_CTL, + MSM_SPM_REG_SAW2_PMIC_DLY, + MSM_SPM_REG_SAW2_AVS_LIMIT, + MSM_SPM_REG_SAW2_AVS_DLY, + MSM_SPM_REG_SAW2_SPM_DLY, + MSM_SPM_REG_SAW2_PMIC_DATA_0, + MSM_SPM_REG_SAW2_PMIC_DATA_1, + MSM_SPM_REG_SAW2_PMIC_DATA_2, + MSM_SPM_REG_SAW2_PMIC_DATA_3, + MSM_SPM_REG_SAW2_PMIC_DATA_4, + MSM_SPM_REG_SAW2_PMIC_DATA_5, + MSM_SPM_REG_SAW2_PMIC_DATA_6, + MSM_SPM_REG_SAW2_PMIC_DATA_7, + MSM_SPM_REG_SAW2_RST, + + MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW2_RST, + + MSM_SPM_REG_SAW2_ID, + MSM_SPM_REG_SAW2_SECURE, + MSM_SPM_REG_SAW2_STS0, + MSM_SPM_REG_SAW2_STS1, + MSM_SPM_REG_SAW2_VCTL, + MSM_SPM_REG_SAW2_SEQ_ENTRY, + MSM_SPM_REG_SAW2_SPM_STS, + MSM_SPM_REG_SAW2_AVS_STS, + MSM_SPM_REG_SAW2_PMIC_STS, + MSM_SPM_REG_SAW2_VERSION, + + MSM_SPM_REG_NR, +}; + +struct msm_spm_seq_entry { + uint32_t mode; + uint8_t *cmd; + bool notify_rpm; +}; + +struct msm_spm_platform_data { + void __iomem *reg_base_addr; + uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE]; + + uint32_t ver_reg; + uint32_t vctl_port; + uint32_t phase_port; + + uint8_t awake_vlevel; + uint32_t vctl_timeout_us; + uint32_t avs_timeout_us; + + uint32_t num_modes; + struct msm_spm_seq_entry *modes; +}; +#endif + +#if defined(CONFIG_MSM_SPM_V1) || defined(CONFIG_MSM_SPM_V2) + +/* Public functions */ + +/** + * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode + * @mode: SPM LPM mode to enter + * @notify_rpm: Notify RPM in this mode + */ +int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm); + +/** + * msm_spm_set_vdd(): Set core voltage + * @cpu: core id + * @vlevel: Encoded PMIC data. + */ +int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel); + +/** + * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core + * @cpu: core id + */ +int msm_spm_turn_on_cpu_rail(unsigned int cpu); + + +/* Internal low power management specific functions */ + +/** + * msm_spm_allow_x_cpu_set_vdd(): Turn on/off cross calling to set voltage + * @allowed: boolean to indicate on/off. + */ +void msm_spm_allow_x_cpu_set_vdd(bool allowed); + +/** + * msm_spm_reinit(): Reinitialize SPM registers + */ +void msm_spm_reinit(void); + +/** + * msm_spm_init(): Board initalization function + * @data: platform specific SPM register configuration data + * @nr_devs: Number of SPM devices being initialized + */ +int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs); + +/** + * msm_spm_device_init(): Device tree initialization function + */ +int msm_spm_device_init(void); + +#if defined(CONFIG_MSM_L2_SPM) + +/* Public functions */ + +/** + * msm_spm_l2_set_low_power_mode(): Configure L2 SPM start address + * for low power mode + * @mode: SPM LPM mode to enter + * @notify_rpm: Notify RPM in this mode + */ +int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm); + +/** + * msm_spm_apcs_set_vdd(): Set Apps processor core sub-system voltage + * @vlevel: Encoded PMIC data. + */ +int msm_spm_apcs_set_vdd(unsigned int vlevel); + +/** + * msm_spm_apcs_set_phase(): Set number of SMPS phases. + * phase_cnt: Number of phases to be set active + */ +int msm_spm_apcs_set_phase(unsigned int phase_cnt); + +/* Internal low power management specific functions */ + +/** + * msm_spm_l2_init(): Board initialization function + * @data: SPM target specific register configuration + */ +int msm_spm_l2_init(struct msm_spm_platform_data *data); + +/** + * msm_spm_l2_reinit(): Reinitialize L2 SPM registers + */ +void msm_spm_l2_reinit(void); + +#else + +static inline int msm_spm_l2_set_low_power_mode(unsigned int mode, + bool notify_rpm) +{ + return -ENOSYS; +} +static inline int msm_spm_l2_init(struct msm_spm_platform_data *data) +{ + return -ENOSYS; +} +static inline void msm_spm_l2_reinit(void) +{ + /* empty */ +} + +static inline int msm_spm_apcs_set_vdd(unsigned int vlevel) +{ + return -ENOSYS; +} + +static inline int msm_spm_apcs_set_phase(unsigned int phase_cnt) +{ + return -ENOSYS; +} +#endif /* defined(CONFIG_MSM_L2_SPM) */ +#else /* defined(CONFIG_MSM_SPM_V1) || defined(CONFIG_MSM_SPM_V2) */ +static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm) +{ + return -ENOSYS; +} + +static inline int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel) +{ + return -ENOSYS; +} + +static inline void msm_spm_reinit(void) +{ + /* empty */ +} + +static inline void msm_spm_allow_x_cpu_set_vdd(bool allowed) +{ + /* empty */ +} + +static inline int msm_spm_turn_on_cpu_rail(unsigned int cpu) +{ + return -ENOSYS; +} + +static inline int msm_spm_device_init(void) +{ + return -ENOSYS; +} + +#endif /*defined(CONFIG_MSM_SPM_V1) || defined (CONFIG_MSM_SPM_V2) */ +#endif /* __ARCH_ARM_MACH_MSM_SPM_H */ diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c new file mode 100644 index 00000000000..2980811f974 --- /dev/null +++ b/arch/arm/mach-msm/spm_devices.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spm.h" +#include "spm_driver.h" + +struct msm_spm_power_modes { + uint32_t mode; + bool notify_rpm; + uint32_t start_addr; + +}; + +struct msm_spm_device { + struct msm_spm_driver_data reg_data; + struct msm_spm_power_modes *modes; + uint32_t num_modes; +}; + +static struct msm_spm_device msm_spm_l2_device; +static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device); +static atomic_t msm_spm_set_vdd_x_cpu_allowed = ATOMIC_INIT(1); + +void msm_spm_allow_x_cpu_set_vdd(bool allowed) +{ + atomic_set(&msm_spm_set_vdd_x_cpu_allowed, allowed ? 1 : 0); +} +EXPORT_SYMBOL(msm_spm_allow_x_cpu_set_vdd); + +int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel) +{ + unsigned long flags; + struct msm_spm_device *dev; + int ret = -EIO; + + local_irq_save(flags); + if (!atomic_read(&msm_spm_set_vdd_x_cpu_allowed) && + unlikely(smp_processor_id() != cpu)) { + goto set_vdd_x_cpu_bail; + } + + dev = &per_cpu(msm_cpu_spm_device, cpu); + ret = msm_spm_drv_set_vdd(&dev->reg_data, vlevel); + +set_vdd_x_cpu_bail: + local_irq_restore(flags); + return ret; +} +EXPORT_SYMBOL(msm_spm_set_vdd); + +static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev, + unsigned int mode, bool notify_rpm) +{ + uint32_t i; + uint32_t start_addr = 0; + int ret = -EINVAL; + + if (mode == MSM_SPM_MODE_DISABLED) { + ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false); + } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) { + for (i = 0; i < dev->num_modes; i++) { + if ((dev->modes[i].mode == mode) && + (dev->modes[i].notify_rpm == notify_rpm)) { + start_addr = dev->modes[i].start_addr; + break; + } + } + ret = msm_spm_drv_set_low_power_mode(&dev->reg_data, + start_addr); + } + return ret; +} + +static int __devinit msm_spm_dev_init(struct msm_spm_device *dev, + struct msm_spm_platform_data *data) +{ + int i, ret = -ENOMEM; + uint32_t offset = 0; + + dev->num_modes = data->num_modes; + dev->modes = kmalloc( + sizeof(struct msm_spm_power_modes) * dev->num_modes, + GFP_KERNEL); + + if (!dev->modes) + goto spm_failed_malloc; + + dev->reg_data.ver_reg = data->ver_reg; + ret = msm_spm_drv_init(&dev->reg_data, data); + + if (ret) + goto spm_failed_init; + + for (i = 0; i < dev->num_modes; i++) { + + /* Default offset is 0 and gets updated as we write more + * sequences into SPM + */ + dev->modes[i].start_addr = offset; + ret = msm_spm_drv_write_seq_data(&dev->reg_data, + data->modes[i].cmd, &offset); + if (ret < 0) + goto spm_failed_init; + + dev->modes[i].mode = data->modes[i].mode; + dev->modes[i].notify_rpm = data->modes[i].notify_rpm; + } + msm_spm_drv_flush_seq_entry(&dev->reg_data); + return 0; + +spm_failed_init: + kfree(dev->modes); +spm_failed_malloc: + return ret; +} + +int msm_spm_turn_on_cpu_rail(unsigned int cpu) +{ + uint32_t val = 0; + uint32_t timeout = 0; + void *reg = NULL; + void *saw_bases[] = { + 0, + MSM_SAW1_BASE, + MSM_SAW2_BASE, + MSM_SAW3_BASE + }; + + if (cpu == 0 || cpu >= num_possible_cpus()) + return -EINVAL; + + reg = saw_bases[cpu]; + + if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) { + val = 0xA4; + reg += 0x14; + timeout = 512; + } else { + return -ENOSYS; + } + + writel_relaxed(val, reg); + mb(); + udelay(timeout); + + return 0; +} +EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail); + +void msm_spm_reinit(void) +{ + unsigned int cpu; + for_each_possible_cpu(cpu) + msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu)); +} +EXPORT_SYMBOL(msm_spm_reinit); + +int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm) +{ + struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device); + return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm); +} +EXPORT_SYMBOL(msm_spm_set_low_power_mode); + +/* Board file init function */ +int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs) +{ + unsigned int cpu; + int ret = 0; + + BUG_ON((nr_devs < num_possible_cpus()) || !data); + + for_each_possible_cpu(cpu) { + struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu); + ret = msm_spm_dev_init(dev, &data[cpu]); + if (ret < 0) { + pr_warn("%s():failed CPU:%u ret:%d\n", __func__, + cpu, ret); + break; + } + } + + return ret; +} + +#ifdef CONFIG_MSM_L2_SPM + +int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm) +{ + return msm_spm_dev_set_low_power_mode( + &msm_spm_l2_device, mode, notify_rpm); +} +EXPORT_SYMBOL(msm_spm_l2_set_low_power_mode); + +void msm_spm_l2_reinit(void) +{ + msm_spm_drv_reinit(&msm_spm_l2_device.reg_data); +} +EXPORT_SYMBOL(msm_spm_l2_reinit); + +int msm_spm_apcs_set_vdd(unsigned int vlevel) +{ + return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel); +} +EXPORT_SYMBOL(msm_spm_apcs_set_vdd); + +int msm_spm_apcs_set_phase(unsigned int phase_cnt) +{ + return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt); +} +EXPORT_SYMBOL(msm_spm_apcs_set_phase); + +/* Board file init function */ +int __init msm_spm_l2_init(struct msm_spm_platform_data *data) +{ + return msm_spm_dev_init(&msm_spm_l2_device, data); +} +#endif + +static int __devinit msm_spm_dev_probe(struct platform_device *pdev) +{ + int ret = 0; + int cpu = 0; + int i = 0; + struct device_node *node = pdev->dev.of_node; + struct msm_spm_platform_data spm_data; + char *key = NULL; + uint32_t val = 0; + struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR]; + size_t len = 0; + struct msm_spm_device *dev = NULL; + struct resource *res = NULL; + uint32_t mode_count = 0; + + struct spm_of { + char *key; + uint32_t id; + }; + + struct spm_of spm_of_data[] = { + {"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG}, + {"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL}, + {"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS}, + {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL}, + {"qcom,saw2-pmic-dly", MSM_SPM_REG_SAW2_PMIC_DLY}, + {"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT}, + {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY}, + {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0}, + {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1}, + {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2}, + {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3}, + {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4}, + {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5}, + {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6}, + {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7}, + }; + + struct mode_of { + char *key; + uint32_t id; + uint32_t notify_rpm; + }; + + struct mode_of mode_of_data[] = { + {"qcom,spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0}, + {"qcom,spm-cmd-ret", MSM_SPM_MODE_POWER_RETENTION, 0}, + {"qcom,spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0}, + {"qcom,spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1}, + }; + + BUG_ON(ARRAY_SIZE(mode_of_data) > MSM_SPM_MODE_NR); + memset(&spm_data, 0, sizeof(struct msm_spm_platform_data)); + memset(&modes, 0, + (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto fail; + + spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!spm_data.reg_base_addr) + return -ENOMEM; + + key = "qcom,core-id"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + cpu = val; + + key = "qcom,saw2-ver-reg"; + ret = of_property_read_u32(node, key, &val); + if (ret) + goto fail; + spm_data.ver_reg = val; + + key = "qcom,vctl-timeout-us"; + ret = of_property_read_u32(node, key, &val); + if (!ret) + spm_data.vctl_timeout_us = val; + + /* optional */ + key = "qcom,vctl-port"; + ret = of_property_read_u32(node, key, &val); + if (!ret) + spm_data.vctl_port = val; + + /* optional */ + key = "qcom,phase-port"; + ret = of_property_read_u32(node, key, &val); + if (!ret) + spm_data.phase_port = val; + + for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) { + ret = of_property_read_u32(node, spm_of_data[i].key, &val); + if (ret) + continue; + spm_data.reg_init_values[spm_of_data[i].id] = val; + } + + for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) { + key = mode_of_data[i].key; + modes[mode_count].cmd = + (uint8_t *)of_get_property(node, key, &len); + if (!modes[mode_count].cmd) + continue; + modes[mode_count].mode = mode_of_data[i].id; + modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm; + mode_count++; + } + + spm_data.modes = modes; + spm_data.num_modes = mode_count; + + /* + * Device with id 0..NR_CPUS are SPM for apps cores + * Device with id 0xFFFF is for L2 SPM. + */ + if (cpu >= 0 && cpu < num_possible_cpus()) + dev = &per_cpu(msm_cpu_spm_device, cpu); + else + dev = &msm_spm_l2_device; + + ret = msm_spm_dev_init(dev, &spm_data); + if (ret < 0) + pr_warn("%s():failed core-id:%u ret:%d\n", __func__, cpu, ret); + + return ret; + +fail: + pr_err("%s: Failed reading node=%s, key=%s\n", + __func__, node->full_name, key); + return -EFAULT; +} + +static struct of_device_id msm_spm_match_table[] = { + {.compatible = "qcom,spm-v2"}, + {}, +}; + +static struct platform_driver msm_spm_device_driver = { + .probe = msm_spm_dev_probe, + .driver = { + .name = "spm-v2", + .owner = THIS_MODULE, + .of_match_table = msm_spm_match_table, + }, +}; + +int __init msm_spm_device_init(void) +{ + return platform_driver_register(&msm_spm_device_driver); +} diff --git a/arch/arm/mach-msm/spm_driver.h b/arch/arm/mach-msm/spm_driver.h new file mode 100644 index 00000000000..f272adb0df5 --- /dev/null +++ b/arch/arm/mach-msm/spm_driver.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ARCH_ARM_MACH_MSM_SPM_DEVICES_H +#define __ARCH_ARM_MACH_MSM_SPM_DEVICES_H + +#include "spm.h" + +struct msm_spm_driver_data { + uint32_t major; + uint32_t minor; + uint32_t ver_reg; + uint32_t vctl_port; + uint32_t phase_port; + void __iomem *reg_base_addr; + uint32_t vctl_timeout_us; + uint32_t avs_timeout_us; + uint32_t reg_shadow[MSM_SPM_REG_NR]; + uint32_t *reg_seq_entry_shadow; + uint32_t *reg_offsets; +}; + +int msm_spm_drv_init(struct msm_spm_driver_data *dev, + struct msm_spm_platform_data *data); +void msm_spm_drv_reinit(struct msm_spm_driver_data *dev); +int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev, + uint32_t addr); +int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, + unsigned int vlevel); +int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev, + uint8_t *cmd, uint32_t *offset); +void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev); +int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev, + bool enable); +int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev, + unsigned int phase_cnt); +#endif diff --git a/arch/arm/mach-msm/subsystem_notif.c b/arch/arm/mach-msm/subsystem_notif.c new file mode 100644 index 00000000000..f7db54c1b39 --- /dev/null +++ b/arch/arm/mach-msm/subsystem_notif.c @@ -0,0 +1,222 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Subsystem Notifier -- Provides notifications + * of subsys events. + * + * Use subsys_notif_register_notifier to register for notifications + * and subsys_notif_queue_notification to send notifications. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct subsys_notif_info { + char name[50]; + struct srcu_notifier_head subsys_notif_rcvr_list; + struct list_head list; +}; + +static LIST_HEAD(subsystem_list); +static DEFINE_MUTEX(notif_lock); +static DEFINE_MUTEX(notif_add_lock); + +#if defined(SUBSYS_RESTART_DEBUG) +static void subsys_notif_reg_test_notifier(const char *); +#endif + +static struct subsys_notif_info *_notif_find_subsys(const char *subsys_name) +{ + struct subsys_notif_info *subsys; + + mutex_lock(¬if_lock); + list_for_each_entry(subsys, &subsystem_list, list) + if (!strncmp(subsys->name, subsys_name, + ARRAY_SIZE(subsys->name))) { + mutex_unlock(¬if_lock); + return subsys; + } + mutex_unlock(¬if_lock); + + return NULL; +} + +void *subsys_notif_register_notifier( + const char *subsys_name, struct notifier_block *nb) +{ + int ret; + struct subsys_notif_info *subsys = _notif_find_subsys(subsys_name); + + if (!subsys) { + + /* Possible first time reference to this subsystem. Add it. */ + subsys = (struct subsys_notif_info *) + subsys_notif_add_subsys(subsys_name); + + if (!subsys) + return ERR_PTR(-EINVAL); + } + + ret = srcu_notifier_chain_register( + &subsys->subsys_notif_rcvr_list, nb); + + if (ret < 0) + return ERR_PTR(ret); + + return subsys; +} +EXPORT_SYMBOL(subsys_notif_register_notifier); + +int subsys_notif_unregister_notifier(void *subsys_handle, + struct notifier_block *nb) +{ + int ret; + struct subsys_notif_info *subsys = + (struct subsys_notif_info *)subsys_handle; + + if (!subsys) + return -EINVAL; + + ret = srcu_notifier_chain_unregister( + &subsys->subsys_notif_rcvr_list, nb); + + return ret; +} +EXPORT_SYMBOL(subsys_notif_unregister_notifier); + +void *subsys_notif_add_subsys(const char *subsys_name) +{ + struct subsys_notif_info *subsys = NULL; + + if (!subsys_name) + goto done; + + mutex_lock(¬if_add_lock); + + subsys = _notif_find_subsys(subsys_name); + + if (subsys) { + mutex_unlock(¬if_add_lock); + goto done; + } + + subsys = kmalloc(sizeof(struct subsys_notif_info), GFP_KERNEL); + + if (!subsys) { + mutex_unlock(¬if_add_lock); + return ERR_PTR(-EINVAL); + } + + strlcpy(subsys->name, subsys_name, ARRAY_SIZE(subsys->name)); + + srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list); + + INIT_LIST_HEAD(&subsys->list); + + mutex_lock(¬if_lock); + list_add_tail(&subsys->list, &subsystem_list); + mutex_unlock(¬if_lock); + + #if defined(SUBSYS_RESTART_DEBUG) + subsys_notif_reg_test_notifier(subsys->name); + #endif + + mutex_unlock(¬if_add_lock); + +done: + return subsys; +} +EXPORT_SYMBOL(subsys_notif_add_subsys); + +int subsys_notif_queue_notification(void *subsys_handle, + enum subsys_notif_type notif_type) +{ + int ret = 0; + struct subsys_notif_info *subsys = + (struct subsys_notif_info *) subsys_handle; + + if (!subsys) + return -EINVAL; + + if (notif_type < 0 || notif_type >= SUBSYS_NOTIF_TYPE_COUNT) + return -EINVAL; + + ret = srcu_notifier_call_chain( + &subsys->subsys_notif_rcvr_list, notif_type, + (void *)subsys); + + return ret; +} +EXPORT_SYMBOL(subsys_notif_queue_notification); + +#if defined(SUBSYS_RESTART_DEBUG) +static const char *notif_to_string(enum subsys_notif_type notif_type) +{ + switch (notif_type) { + + case SUBSYS_BEFORE_SHUTDOWN: + return __stringify(SUBSYS_BEFORE_SHUTDOWN); + + case SUBSYS_AFTER_SHUTDOWN: + return __stringify(SUBSYS_AFTER_SHUTDOWN); + + case SUBSYS_BEFORE_POWERUP: + return __stringify(SUBSYS_BEFORE_POWERUP); + + case SUBSYS_AFTER_POWERUP: + return __stringify(SUBSYS_AFTER_POWERUP); + + default: + return "unknown"; + } +} + +static int subsys_notifier_test_call(struct notifier_block *this, + unsigned long code, + void *data) +{ + switch (code) { + + default: + printk(KERN_WARNING "%s: Notification %s from subsystem %p\n", + __func__, notif_to_string(code), data); + break; + + } + + return NOTIFY_DONE; +} + +static struct notifier_block nb = { + .notifier_call = subsys_notifier_test_call, +}; + +static void subsys_notif_reg_test_notifier(const char *subsys_name) +{ + void *handle = subsys_notif_register_notifier(subsys_name, &nb); + printk(KERN_WARNING "%s: Registered test notifier, handle=%p", + __func__, handle); +} +#endif + +MODULE_DESCRIPTION("Subsystem Restart Notifier"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c new file mode 100644 index 00000000000..c98a6723300 --- /dev/null +++ b/arch/arm/mach-msm/subsystem_restart.c @@ -0,0 +1,591 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "subsys-restart: %s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "smd_private.h" + +struct subsys_soc_restart_order { + const char * const *subsystem_list; + int count; + + struct mutex shutdown_lock; + struct mutex powerup_lock; + struct subsys_data *subsys_ptrs[]; +}; + +struct restart_wq_data { + struct subsys_data *subsys; + struct wake_lock ssr_wake_lock; + char wlname[64]; + int use_restart_order; + struct work_struct work; +}; + +struct restart_log { + struct timeval time; + struct subsys_data *subsys; + struct list_head list; +}; + +static int restart_level; +static int enable_ramdumps; +struct workqueue_struct *ssr_wq; + +static LIST_HEAD(restart_log_list); +static LIST_HEAD(subsystem_list); +static DEFINE_SPINLOCK(subsystem_list_lock); +static DEFINE_MUTEX(soc_order_reg_lock); +static DEFINE_MUTEX(restart_log_mutex); + +/* SOC specific restart orders go here */ + +#define DEFINE_SINGLE_RESTART_ORDER(name, order) \ + static struct subsys_soc_restart_order __##name = { \ + .subsystem_list = order, \ + .count = ARRAY_SIZE(order), \ + .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \ + }; \ + static struct subsys_soc_restart_order *name[] = { \ + &__##name, \ + } + +/* MSM 8x60 restart ordering info */ +static const char * const _order_8x60_all[] = { + "external_modem", "modem", "lpass" +}; +DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all); + +static const char * const _order_8x60_modems[] = {"external_modem", "modem"}; +DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems); + +/* MSM 8960 restart ordering info */ +static const char * const order_8960[] = {"modem", "lpass"}; + +static struct subsys_soc_restart_order restart_orders_8960_one = { + .subsystem_list = order_8960, + .count = ARRAY_SIZE(order_8960), + .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL} + }; + +static struct subsys_soc_restart_order *restart_orders_8960[] = { + &restart_orders_8960_one, +}; + +/* These will be assigned to one of the sets above after + * runtime SoC identification. + */ +static struct subsys_soc_restart_order **restart_orders; +static int n_restart_orders; + +module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR); + +static struct subsys_soc_restart_order *_update_restart_order( + struct subsys_data *subsys); + +int get_restart_level() +{ + return restart_level; +} +EXPORT_SYMBOL(get_restart_level); + +static int restart_level_set(const char *val, struct kernel_param *kp) +{ + int ret; + int old_val = restart_level; + + if (cpu_is_msm9615()) { + pr_err("Only Phase 1 subsystem restart is supported\n"); + return -EINVAL; + } + + ret = param_set_int(val, kp); + if (ret) + return ret; + + switch (restart_level) { + + case RESET_SOC: + case RESET_SUBSYS_COUPLED: + case RESET_SUBSYS_INDEPENDENT: + pr_info("Phase %d behavior activated.\n", restart_level); + break; + + default: + restart_level = old_val; + return -EINVAL; + break; + + } + return 0; +} + +module_param_call(restart_level, restart_level_set, param_get_int, + &restart_level, 0644); + +static struct subsys_data *_find_subsystem(const char *subsys_name) +{ + struct subsys_data *subsys; + unsigned long flags; + + spin_lock_irqsave(&subsystem_list_lock, flags); + list_for_each_entry(subsys, &subsystem_list, list) + if (!strncmp(subsys->name, subsys_name, + SUBSYS_NAME_MAX_LENGTH)) { + spin_unlock_irqrestore(&subsystem_list_lock, flags); + return subsys; + } + spin_unlock_irqrestore(&subsystem_list_lock, flags); + + return NULL; +} + +static struct subsys_soc_restart_order *_update_restart_order( + struct subsys_data *subsys) +{ + int i, j; + + if (!subsys) + return NULL; + + if (!subsys->name) + return NULL; + + mutex_lock(&soc_order_reg_lock); + for (j = 0; j < n_restart_orders; j++) { + for (i = 0; i < restart_orders[j]->count; i++) + if (!strncmp(restart_orders[j]->subsystem_list[i], + subsys->name, SUBSYS_NAME_MAX_LENGTH)) { + + restart_orders[j]->subsys_ptrs[i] = + subsys; + mutex_unlock(&soc_order_reg_lock); + return restart_orders[j]; + } + } + + mutex_unlock(&soc_order_reg_lock); + + return NULL; +} + +static void _send_notification_to_order(struct subsys_data + **restart_list, int count, + enum subsys_notif_type notif_type) +{ + int i; + + for (i = 0; i < count; i++) + if (restart_list[i]) + subsys_notif_queue_notification( + restart_list[i]->notif_handle, notif_type); +} + +static int max_restarts; +module_param(max_restarts, int, 0644); + +static long max_history_time = 3600; +module_param(max_history_time, long, 0644); + +static void do_epoch_check(struct subsys_data *subsys) +{ + int n = 0; + struct timeval *time_first = NULL, *curr_time; + struct restart_log *r_log, *temp; + static int max_restarts_check; + static long max_history_time_check; + + mutex_lock(&restart_log_mutex); + + max_restarts_check = max_restarts; + max_history_time_check = max_history_time; + + /* Check if epoch checking is enabled */ + if (!max_restarts_check) + goto out; + + r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL); + if (!r_log) + goto out; + r_log->subsys = subsys; + do_gettimeofday(&r_log->time); + curr_time = &r_log->time; + INIT_LIST_HEAD(&r_log->list); + + list_add_tail(&r_log->list, &restart_log_list); + + list_for_each_entry_safe(r_log, temp, &restart_log_list, list) { + + if ((curr_time->tv_sec - r_log->time.tv_sec) > + max_history_time_check) { + + pr_debug("Deleted node with restart_time = %ld\n", + r_log->time.tv_sec); + list_del(&r_log->list); + kfree(r_log); + continue; + } + if (!n) { + time_first = &r_log->time; + pr_debug("Time_first: %ld\n", time_first->tv_sec); + } + n++; + pr_debug("Restart_time: %ld\n", r_log->time.tv_sec); + } + + if (time_first && n >= max_restarts_check) { + if ((curr_time->tv_sec - time_first->tv_sec) < + max_history_time_check) + panic("Subsystems have crashed %d times in less than " + "%ld seconds!", max_restarts_check, + max_history_time_check); + } + +out: + mutex_unlock(&restart_log_mutex); +} + +static void subsystem_restart_wq_func(struct work_struct *work) +{ + struct restart_wq_data *r_work = container_of(work, + struct restart_wq_data, work); + struct subsys_data **restart_list; + struct subsys_data *subsys = r_work->subsys; + struct subsys_soc_restart_order *soc_restart_order = NULL; + + struct mutex *powerup_lock; + struct mutex *shutdown_lock; + + int i; + int restart_list_count = 0; + + if (r_work->use_restart_order) + soc_restart_order = subsys->restart_order; + + /* It's OK to not take the registration lock at this point. + * This is because the subsystem list inside the relevant + * restart order is not being traversed. + */ + if (!soc_restart_order) { + restart_list = subsys->single_restart_list; + restart_list_count = 1; + powerup_lock = &subsys->powerup_lock; + shutdown_lock = &subsys->shutdown_lock; + } else { + restart_list = soc_restart_order->subsys_ptrs; + restart_list_count = soc_restart_order->count; + powerup_lock = &soc_restart_order->powerup_lock; + shutdown_lock = &soc_restart_order->shutdown_lock; + } + + pr_debug("[%p]: Attempting to get shutdown lock!\n", current); + + /* Try to acquire shutdown_lock. If this fails, these subsystems are + * already being restarted - return. + */ + if (!mutex_trylock(shutdown_lock)) + goto out; + + pr_debug("[%p]: Attempting to get powerup lock!\n", current); + + /* Now that we've acquired the shutdown lock, either we're the first to + * restart these subsystems or some other thread is doing the powerup + * sequence for these subsystems. In the latter case, panic and bail + * out, since a subsystem died in its powerup sequence. + */ + if (!mutex_trylock(powerup_lock)) + panic("%s[%p]: Subsystem died during powerup!", + __func__, current); + + do_epoch_check(subsys); + + /* Now it is necessary to take the registration lock. This is because + * the subsystem list in the SoC restart order will be traversed + * and it shouldn't be changed until _this_ restart sequence completes. + */ + mutex_lock(&soc_order_reg_lock); + + pr_debug("[%p]: Starting restart sequence for %s\n", current, + r_work->subsys->name); + + _send_notification_to_order(restart_list, + restart_list_count, + SUBSYS_BEFORE_SHUTDOWN); + + for (i = 0; i < restart_list_count; i++) { + + if (!restart_list[i]) + continue; + + pr_info("[%p]: Shutting down %s\n", current, + restart_list[i]->name); + + if (restart_list[i]->shutdown(subsys) < 0) + panic("subsys-restart: %s[%p]: Failed to shutdown %s!", + __func__, current, restart_list[i]->name); + } + + _send_notification_to_order(restart_list, restart_list_count, + SUBSYS_AFTER_SHUTDOWN); + + /* Now that we've finished shutting down these subsystems, release the + * shutdown lock. If a subsystem restart request comes in for a + * subsystem in _this_ restart order after the unlock below, and + * before the powerup lock is released, panic and bail out. + */ + mutex_unlock(shutdown_lock); + + /* Collect ram dumps for all subsystems in order here */ + for (i = 0; i < restart_list_count; i++) { + if (!restart_list[i]) + continue; + + if (restart_list[i]->ramdump) + if (restart_list[i]->ramdump(enable_ramdumps, + subsys) < 0) + pr_warn("%s[%p]: Ramdump failed.\n", + restart_list[i]->name, current); + } + + _send_notification_to_order(restart_list, + restart_list_count, + SUBSYS_BEFORE_POWERUP); + + for (i = restart_list_count - 1; i >= 0; i--) { + + if (!restart_list[i]) + continue; + + pr_info("[%p]: Powering up %s\n", current, + restart_list[i]->name); + + if (restart_list[i]->powerup(subsys) < 0) + panic("%s[%p]: Failed to powerup %s!", __func__, + current, restart_list[i]->name); + } + + _send_notification_to_order(restart_list, + restart_list_count, + SUBSYS_AFTER_POWERUP); + + pr_info("[%p]: Restart sequence for %s completed.\n", + current, r_work->subsys->name); + + mutex_unlock(powerup_lock); + + mutex_unlock(&soc_order_reg_lock); + + pr_debug("[%p]: Released powerup lock!\n", current); + +out: + wake_unlock(&r_work->ssr_wake_lock); + wake_lock_destroy(&r_work->ssr_wake_lock); + kfree(r_work); +} + +static void __subsystem_restart(struct subsys_data *subsys) +{ + struct restart_wq_data *data = NULL; + int rc; + + pr_debug("Restarting %s [level=%d]!\n", subsys->name, + restart_level); + + data = kzalloc(sizeof(struct restart_wq_data), GFP_ATOMIC); + if (!data) + panic("%s: Unable to allocate memory to restart %s.", + __func__, subsys->name); + + data->subsys = subsys; + + if (restart_level != RESET_SUBSYS_INDEPENDENT) + data->use_restart_order = 1; + + snprintf(data->wlname, sizeof(data->wlname), "ssr(%s)", subsys->name); + wake_lock_init(&data->ssr_wake_lock, WAKE_LOCK_SUSPEND, data->wlname); + wake_lock(&data->ssr_wake_lock); + + INIT_WORK(&data->work, subsystem_restart_wq_func); + rc = queue_work(ssr_wq, &data->work); + if (rc < 0) + panic("%s: Unable to schedule work to restart %s (%d).", + __func__, subsys->name, rc); +} + +int subsystem_restart(const char *subsys_name) +{ + struct subsys_data *subsys; + + if (!subsys_name) { + pr_err("Invalid subsystem name.\n"); + return -EINVAL; + } + + pr_info("Restart sequence requested for %s, restart_level = %d.\n", + subsys_name, restart_level); + + /* List of subsystems is protected by a lock. New subsystems can + * still come in. + */ + subsys = _find_subsystem(subsys_name); + + if (!subsys) { + pr_warn("Unregistered subsystem %s!\n", subsys_name); + return -EINVAL; + } + + switch (restart_level) { + + case RESET_SUBSYS_COUPLED: + case RESET_SUBSYS_INDEPENDENT: + __subsystem_restart(subsys); + break; + + case RESET_SOC: + panic("subsys-restart: Resetting the SoC - %s crashed.", + subsys->name); + break; + + default: + panic("subsys-restart: Unknown restart level!\n"); + break; + + } + + return 0; +} +EXPORT_SYMBOL(subsystem_restart); + +int ssr_register_subsystem(struct subsys_data *subsys) +{ + unsigned long flags; + + if (!subsys) + goto err; + + if (!subsys->name) + goto err; + + if (!subsys->powerup || !subsys->shutdown) + goto err; + + subsys->notif_handle = subsys_notif_add_subsys(subsys->name); + subsys->restart_order = _update_restart_order(subsys); + subsys->single_restart_list[0] = subsys; + + mutex_init(&subsys->shutdown_lock); + mutex_init(&subsys->powerup_lock); + + spin_lock_irqsave(&subsystem_list_lock, flags); + list_add(&subsys->list, &subsystem_list); + spin_unlock_irqrestore(&subsystem_list_lock, flags); + + return 0; + +err: + return -EINVAL; +} +EXPORT_SYMBOL(ssr_register_subsystem); + +static int ssr_panic_handler(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct subsys_data *subsys; + + list_for_each_entry(subsys, &subsystem_list, list) + if (subsys->crash_shutdown) + subsys->crash_shutdown(subsys); + return NOTIFY_DONE; +} + +static struct notifier_block panic_nb = { + .notifier_call = ssr_panic_handler, +}; + +static int __init ssr_init_soc_restart_orders(void) +{ + int i; + + atomic_notifier_chain_register(&panic_notifier_list, + &panic_nb); + + if (cpu_is_msm8x60()) { + for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) { + mutex_init(&orders_8x60_all[i]->powerup_lock); + mutex_init(&orders_8x60_all[i]->shutdown_lock); + } + + for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) { + mutex_init(&orders_8x60_modems[i]->powerup_lock); + mutex_init(&orders_8x60_modems[i]->shutdown_lock); + } + + restart_orders = orders_8x60_all; + n_restart_orders = ARRAY_SIZE(orders_8x60_all); + } + + if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615() || + cpu_is_apq8064()) { + restart_orders = restart_orders_8960; + n_restart_orders = ARRAY_SIZE(restart_orders_8960); + } + + if (restart_orders == NULL || n_restart_orders < 1) { + WARN_ON(1); + return -EINVAL; + } + + return 0; +} + +static int __init subsys_restart_init(void) +{ + int ret = 0; + + restart_level = RESET_SOC; + + ssr_wq = alloc_workqueue("ssr_wq", 0, 0); + + if (!ssr_wq) + panic("Couldn't allocate workqueue for subsystem restart.\n"); + + ret = ssr_init_soc_restart_orders(); + + return ret; +} + +arch_initcall(subsys_restart_init); + +MODULE_DESCRIPTION("Subsystem Restart Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/sysmon.c b/arch/arm/mach-msm/sysmon.c new file mode 100644 index 00000000000..1305bd1e7e6 --- /dev/null +++ b/arch/arm/mach-msm/sysmon.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ +#undef DEBUG + +#include +#include +#include +#include +#include + +#include +#include + +#include "hsic_sysmon.h" +#include "sysmon.h" + +#define TX_BUF_SIZE 50 +#define RX_BUF_SIZE 500 +#define TIMEOUT_MS 5000 + +enum transports { + TRANSPORT_SMD, + TRANSPORT_HSIC, +}; + +struct sysmon_subsys { + struct mutex lock; + struct smd_channel *chan; + bool chan_open; + struct completion resp_ready; + char rx_buf[RX_BUF_SIZE]; + enum transports transport; +}; + +static struct sysmon_subsys subsys[SYSMON_NUM_SS] = { + [SYSMON_SS_MODEM].transport = TRANSPORT_SMD, + [SYSMON_SS_LPASS].transport = TRANSPORT_SMD, + [SYSMON_SS_WCNSS].transport = TRANSPORT_SMD, + [SYSMON_SS_DSPS].transport = TRANSPORT_SMD, + [SYSMON_SS_Q6FW].transport = TRANSPORT_SMD, + [SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC, +}; + +static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = { + [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown", + [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown", + [SUBSYS_BEFORE_POWERUP] = "before_powerup", + [SUBSYS_AFTER_POWERUP] = "after_powerup", +}; + +static int sysmon_send_smd(struct sysmon_subsys *ss, const char *tx_buf, + size_t len) +{ + int ret; + + if (!ss->chan_open) + return -ENODEV; + + init_completion(&ss->resp_ready); + pr_debug("Sending SMD message: %s\n", tx_buf); + smd_write(ss->chan, tx_buf, len); + ret = wait_for_completion_timeout(&ss->resp_ready, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +static int sysmon_send_hsic(struct sysmon_subsys *ss, const char *tx_buf, + size_t len) +{ + int ret; + size_t actual_len; + + pr_debug("Sending HSIC message: %s\n", tx_buf); + ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM, + tx_buf, len, TIMEOUT_MS); + if (ret) + return ret; + ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf, + ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS); + return ret; +} + +static int sysmon_send_msg(struct sysmon_subsys *ss, const char *tx_buf, + size_t len) +{ + int ret; + + switch (ss->transport) { + case TRANSPORT_SMD: + ret = sysmon_send_smd(ss, tx_buf, len); + break; + case TRANSPORT_HSIC: + ret = sysmon_send_hsic(ss, tx_buf, len); + break; + default: + ret = -EINVAL; + } + + if (!ret) + pr_debug("Received response: %s\n", ss->rx_buf); + + return ret; +} + +/** + * sysmon_send_event() - Notify a subsystem of another's state change + * @dest_ss: ID of subsystem the notification should be sent to + * @event_ss: String name of the subsystem that generated the notification + * @notif: ID of the notification type (ex. SUBSYS_BEFORE_SHUTDOWN) + * + * Returns 0 for success, -EINVAL for invalid destination or notification IDs, + * -ENODEV if the transport channel is not open, -ETIMEDOUT if the destination + * subsystem does not respond, and -ENOSYS if the destination subsystem + * responds, but with something other than an acknowledgement. + * + * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0). + */ +int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss, + enum subsys_notif_type notif) +{ + struct sysmon_subsys *ss = &subsys[dest_ss]; + char tx_buf[TX_BUF_SIZE]; + int ret; + + if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS || + notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT || + event_ss == NULL) + return -EINVAL; + + snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss, + notif_name[notif]); + + mutex_lock(&ss->lock); + ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf)); + if (ret) + goto out; + + if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf))) + ret = -ENOSYS; +out: + mutex_unlock(&ss->lock); + return ret; +} + +/** + * sysmon_get_reason() - Retrieve failure reason from a subsystem. + * @dest_ss: ID of subsystem to query + * @buf: Caller-allocated buffer for the returned NUL-terminated reason + * @len: Length of @buf + * + * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if + * the SMD transport channel is not open, -ETIMEDOUT if the destination + * subsystem does not respond, and -ENOSYS if the destination subsystem + * responds with something unexpected. + * + * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0). + */ +int sysmon_get_reason(enum subsys_id dest_ss, char *buf, size_t len) +{ + struct sysmon_subsys *ss = &subsys[dest_ss]; + const char tx_buf[] = "ssr:retrieve:sfr"; + const char expect[] = "ssr:return:"; + size_t prefix_len = ARRAY_SIZE(expect) - 1; + int ret; + + if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS || + buf == NULL || len == 0) + return -EINVAL; + + mutex_lock(&ss->lock); + ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf)); + if (ret) + goto out; + + if (strncmp(ss->rx_buf, expect, prefix_len)) { + ret = -ENOSYS; + goto out; + } + strlcpy(buf, ss->rx_buf + prefix_len, len); +out: + mutex_unlock(&ss->lock); + return ret; +} + +static void sysmon_smd_notify(void *priv, unsigned int smd_event) +{ + struct sysmon_subsys *ss = priv; + + switch (smd_event) { + case SMD_EVENT_DATA: { + if (smd_read_avail(ss->chan) > 0) { + smd_read_from_cb(ss->chan, ss->rx_buf, + ARRAY_SIZE(ss->rx_buf)); + complete(&ss->resp_ready); + } + break; + } + case SMD_EVENT_OPEN: + ss->chan_open = true; + break; + case SMD_EVENT_CLOSE: + ss->chan_open = false; + break; + } +} + +static int sysmon_probe(struct platform_device *pdev) +{ + struct sysmon_subsys *ss; + int ret; + + if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS) + return -ENODEV; + + ss = &subsys[pdev->id]; + mutex_init(&ss->lock); + + switch (ss->transport) { + case TRANSPORT_SMD: + if (pdev->id >= SMD_NUM_TYPE) + return -EINVAL; + + ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss, + sysmon_smd_notify); + if (ret) { + pr_err("SMD open failed\n"); + return ret; + } + + smd_disable_read_intr(ss->chan); + break; + case TRANSPORT_HSIC: + if (pdev->id < SMD_NUM_TYPE) + return -EINVAL; + + ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM); + if (ret) { + pr_err("HSIC open failed\n"); + return ret; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __devexit sysmon_remove(struct platform_device *pdev) +{ + struct sysmon_subsys *ss = &subsys[pdev->id]; + + switch (ss->transport) { + case TRANSPORT_SMD: + smd_close(ss->chan); + break; + case TRANSPORT_HSIC: + hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM); + break; + } + + return 0; +} + +static struct platform_driver sysmon_driver = { + .probe = sysmon_probe, + .remove = __devexit_p(sysmon_remove), + .driver = { + .name = "sys_mon", + .owner = THIS_MODULE, + }, +}; + +static int __init sysmon_init(void) +{ + return platform_driver_register(&sysmon_driver); +} +subsys_initcall(sysmon_init); + +static void __exit sysmon_exit(void) +{ + platform_driver_unregister(&sysmon_driver); +} +module_exit(sysmon_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("system monitor communication library"); +MODULE_ALIAS("platform:sys_mon"); diff --git a/arch/arm/mach-msm/sysmon.h b/arch/arm/mach-msm/sysmon.h new file mode 100644 index 00000000000..77c332932d0 --- /dev/null +++ b/arch/arm/mach-msm/sysmon.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MSM_SYSMON_H +#define __MSM_SYSMON_H + +#include +#include + +/** + * enum subsys_id - Destination subsystems for events. + */ +enum subsys_id { + /* SMD subsystems */ + SYSMON_SS_MODEM = SMD_APPS_MODEM, + SYSMON_SS_LPASS = SMD_APPS_QDSP, + SYSMON_SS_WCNSS = SMD_APPS_WCNSS, + SYSMON_SS_DSPS = SMD_APPS_DSPS, + SYSMON_SS_Q6FW = SMD_APPS_Q6FW, + + /* Non-SMD subsystems */ + SYSMON_SS_EXT_MODEM = SMD_NUM_TYPE, + SYSMON_NUM_SS +}; + +#ifdef CONFIG_MSM_SYSMON_COMM +int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss, + enum subsys_notif_type notif); +int sysmon_get_reason(enum subsys_id dest_ss, char *buf, size_t len); +#else +static inline int sysmon_send_event(enum subsys_id dest_ss, + const char *event_ss, + enum subsys_notif_type notif) +{ + return 0; +} +static inline int sysmon_get_reason(enum subsys_id dest_ss, char *buf, + size_t len) +{ + return 0; +} +#endif + +#endif diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 81280825493..912ae6eed6a 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -1,7 +1,6 @@ /* - * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -14,232 +13,1164 @@ * */ +#include #include #include #include +#include #include #include +#include #include +#include +#include #include #include -#include #include - +#include #include -#include -#include +#include +#include + +#if defined(CONFIG_MSM_SMD) +#include "smd_private.h" +#endif +#include "timer.h" + +enum { + MSM_TIMER_DEBUG_SYNC = 1U << 0, +}; +static int msm_timer_debug_mask; +module_param_named(debug_mask, msm_timer_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#ifdef CONFIG_MSM7X00A_USE_GP_TIMER + #define DG_TIMER_RATING 100 +#else + #define DG_TIMER_RATING 300 +#endif + +#ifndef MSM_TMR0_BASE +#define MSM_TMR0_BASE MSM_TMR_BASE +#endif + +#define MSM_DGT_SHIFT (5) #define TIMER_MATCH_VAL 0x0000 #define TIMER_COUNT_VAL 0x0004 #define TIMER_ENABLE 0x0008 -#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) -#define TIMER_ENABLE_EN BIT(0) #define TIMER_CLEAR 0x000C #define DGT_CLK_CTL 0x0034 -#define DGT_CLK_CTL_DIV_4 0x3 +enum { + DGT_CLK_CTL_DIV_1 = 0, + DGT_CLK_CTL_DIV_2 = 1, + DGT_CLK_CTL_DIV_3 = 2, + DGT_CLK_CTL_DIV_4 = 3, +}; +#define TIMER_STATUS 0x0088 +#define TIMER_ENABLE_EN 1 +#define TIMER_ENABLE_CLR_ON_MATCH_EN 2 -#define GPT_HZ 32768 +#define LOCAL_TIMER 0 +#define GLOBAL_TIMER 1 -#define MSM_DGT_SHIFT 5 +/* + * global_timer_offset is added to the regbase of a timer to force the memory + * access to come from the CPU0 region. + */ +static int global_timer_offset; +static int msm_global_timer; -static void __iomem *event_base; +#define NR_TIMERS ARRAY_SIZE(msm_clocks) + +unsigned int gpt_hz = 32768; +unsigned int sclk_hz = 32768; + +static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt); +static irqreturn_t msm_timer_interrupt(int irq, void *dev_id); +static cycle_t msm_gpt_read(struct clocksource *cs); +static cycle_t msm_dgt_read(struct clocksource *cs); +static void msm_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt); +static int msm_timer_set_next_event(unsigned long cycles, + struct clock_event_device *evt); + +enum { + MSM_CLOCK_FLAGS_UNSTABLE_COUNT = 1U << 0, + MSM_CLOCK_FLAGS_ODD_MATCH_WRITE = 1U << 1, + MSM_CLOCK_FLAGS_DELAYED_WRITE_POST = 1U << 2, +}; + +struct msm_clock { + struct clock_event_device clockevent; + struct clocksource clocksource; + unsigned int irq; + void __iomem *regbase; + uint32_t freq; + uint32_t shift; + uint32_t flags; + uint32_t write_delay; + uint32_t rollover_offset; + uint32_t index; + void __iomem *global_counter; + void __iomem *local_counter; + uint32_t status_mask; + union { + struct clock_event_device *evt; + struct clock_event_device __percpu **percpu_evt; + }; +}; + +enum { + MSM_CLOCK_GPT, + MSM_CLOCK_DGT, +}; + +struct msm_clock_percpu_data { + uint32_t last_set; + uint32_t sleep_offset; + uint32_t alarm_vtime; + uint32_t alarm; + uint32_t non_sleep_offset; + uint32_t in_sync; + cycle_t stopped_tick; + int stopped; + uint32_t last_sync_gpt; + u64 last_sync_jiffies; +}; + +struct msm_timer_sync_data_t { + struct msm_clock *clock; + uint32_t timeout; + int exit_sleep; +}; + +static struct msm_clock msm_clocks[] = { + [MSM_CLOCK_GPT] = { + .clockevent = { + .name = "gp_timer", + .features = CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .rating = 200, + .set_next_event = msm_timer_set_next_event, + .set_mode = msm_timer_set_mode, + }, + .clocksource = { + .name = "gp_timer", + .rating = 200, + .read = msm_gpt_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + }, + .irq = INT_GP_TIMER_EXP, + .regbase = MSM_TMR_BASE + 0x4, + .freq = 32768, + .index = MSM_CLOCK_GPT, + .write_delay = 9, + }, + [MSM_CLOCK_DGT] = { + .clockevent = { + .name = "dg_timer", + .features = CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .rating = DG_TIMER_RATING, + .set_next_event = msm_timer_set_next_event, + .set_mode = msm_timer_set_mode, + }, + .clocksource = { + .name = "dg_timer", + .rating = DG_TIMER_RATING, + .read = msm_dgt_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + }, + .irq = INT_DEBUG_TIMER_EXP, + .regbase = MSM_TMR_BASE + 0x24, + .index = MSM_CLOCK_DGT, + .write_delay = 9, + } +}; + +static DEFINE_PER_CPU(struct msm_clock_percpu_data[NR_TIMERS], + msm_clocks_percpu); + +static DEFINE_PER_CPU(struct msm_clock *, msm_active_clock); static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = *(struct clock_event_device **)dev_id; - /* Stop the timer tick */ - if (evt->mode == CLOCK_EVT_MODE_ONESHOT) { - u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); - ctrl &= ~TIMER_ENABLE_EN; - writel_relaxed(ctrl, event_base + TIMER_ENABLE); - } + if (evt->event_handler == NULL) + return IRQ_HANDLED; evt->event_handler(evt); return IRQ_HANDLED; } +static uint32_t msm_read_timer_count(struct msm_clock *clock, int global) +{ + uint32_t t1, t2, t3; + int loop_count = 0; + void __iomem *addr = clock->regbase + TIMER_COUNT_VAL + + global*global_timer_offset; + + if (!(clock->flags & MSM_CLOCK_FLAGS_UNSTABLE_COUNT)) + return __raw_readl_no_log(addr); + + t1 = __raw_readl_no_log(addr); + t2 = __raw_readl_no_log(addr); + if ((t2-t1) <= 1) + return t2; + while (1) { + t1 = __raw_readl_no_log(addr); + t2 = __raw_readl_no_log(addr); + t3 = __raw_readl_no_log(addr); + cpu_relax(); + if ((t3-t2) <= 1) + return t3; + if ((t2-t1) <= 1) + return t2; + if ((t2 >= t1) && (t3 >= t2)) + return t2; + if (++loop_count == 5) { + pr_err("msm_read_timer_count timer %s did not " + "stabilize: %u -> %u -> %u\n", + clock->clockevent.name, t1, t2, t3); + return t3; + } + } +} + +static cycle_t msm_gpt_read(struct clocksource *cs) +{ + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock_percpu_data *clock_state = + &per_cpu(msm_clocks_percpu, 0)[MSM_CLOCK_GPT]; + + if (clock_state->stopped) + return clock_state->stopped_tick; + + return msm_read_timer_count(clock, GLOBAL_TIMER) + + clock_state->sleep_offset; +} + +static cycle_t msm_dgt_read(struct clocksource *cs) +{ + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + struct msm_clock_percpu_data *clock_state = + &per_cpu(msm_clocks_percpu, 0)[MSM_CLOCK_DGT]; + + if (clock_state->stopped) + return clock_state->stopped_tick >> clock->shift; + + return (msm_read_timer_count(clock, GLOBAL_TIMER) + + clock_state->sleep_offset) >> clock->shift; +} + +static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt) +{ + int i; + + if (!is_smp()) + return container_of(evt, struct msm_clock, clockevent); + + for (i = 0; i < NR_TIMERS; i++) + if (evt == &(msm_clocks[i].clockevent)) + return &msm_clocks[i]; + return &msm_clocks[msm_global_timer]; +} + static int msm_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { - u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); + int i; + struct msm_clock *clock; + struct msm_clock_percpu_data *clock_state; + uint32_t now; + uint32_t alarm; + int late; + + clock = clockevent_to_clock(evt); + clock_state = &__get_cpu_var(msm_clocks_percpu)[clock->index]; + if (clock_state->stopped) + return 0; + now = msm_read_timer_count(clock, LOCAL_TIMER); + alarm = now + (cycles << clock->shift); + if (clock->flags & MSM_CLOCK_FLAGS_ODD_MATCH_WRITE) + while (now == clock_state->last_set) + now = msm_read_timer_count(clock, LOCAL_TIMER); + + clock_state->alarm = alarm; + __raw_writel(alarm, clock->regbase + TIMER_MATCH_VAL); + + if (clock->flags & MSM_CLOCK_FLAGS_DELAYED_WRITE_POST) { + /* read the counter four extra times to make sure write posts + before reading the time */ + for (i = 0; i < 4; i++) + __raw_readl_no_log(clock->regbase + TIMER_COUNT_VAL); + } + now = msm_read_timer_count(clock, LOCAL_TIMER); + clock_state->last_set = now; + clock_state->alarm_vtime = alarm + clock_state->sleep_offset; + late = now - alarm; + if (late >= (int)(-clock->write_delay << clock->shift) && + late < clock->freq*5) + return -ETIME; - writel_relaxed(0, event_base + TIMER_CLEAR); - writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); - writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); return 0; } static void msm_timer_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) + struct clock_event_device *evt) { - u32 ctrl; + struct msm_clock *clock; + struct msm_clock **cur_clock; + struct msm_clock_percpu_data *clock_state, *gpt_state; + unsigned long irq_flags; + struct irq_chip *chip; - ctrl = readl_relaxed(event_base + TIMER_ENABLE); - ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); + clock = clockevent_to_clock(evt); + clock_state = &__get_cpu_var(msm_clocks_percpu)[clock->index]; + gpt_state = &__get_cpu_var(msm_clocks_percpu)[MSM_CLOCK_GPT]; + + local_irq_save(irq_flags); switch (mode) { case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_PERIODIC: break; case CLOCK_EVT_MODE_ONESHOT: - /* Timer is enabled in set_next_event */ + clock_state->stopped = 0; + clock_state->sleep_offset = + -msm_read_timer_count(clock, LOCAL_TIMER) + + clock_state->stopped_tick; + get_cpu_var(msm_active_clock) = clock; + put_cpu_var(msm_active_clock); + __raw_writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); + chip = irq_get_chip(clock->irq); + if (chip && chip->irq_unmask) + chip->irq_unmask(irq_get_irq_data(clock->irq)); + if (clock != &msm_clocks[MSM_CLOCK_GPT]) + __raw_writel(TIMER_ENABLE_EN, + msm_clocks[MSM_CLOCK_GPT].regbase + + TIMER_ENABLE); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: + cur_clock = &get_cpu_var(msm_active_clock); + if (*cur_clock == clock) + *cur_clock = NULL; + put_cpu_var(msm_active_clock); + clock_state->in_sync = 0; + clock_state->stopped = 1; + clock_state->stopped_tick = + msm_read_timer_count(clock, LOCAL_TIMER) + + clock_state->sleep_offset; + __raw_writel(0, clock->regbase + TIMER_MATCH_VAL); + chip = irq_get_chip(clock->irq); + if (chip && chip->irq_mask) + chip->irq_mask(irq_get_irq_data(clock->irq)); + + if (!is_smp() || clock != &msm_clocks[MSM_CLOCK_DGT] + || smp_processor_id()) + __raw_writel(0, clock->regbase + TIMER_ENABLE); + + if (msm_global_timer == MSM_CLOCK_DGT && + clock != &msm_clocks[MSM_CLOCK_GPT]) { + gpt_state->in_sync = 0; + __raw_writel(0, msm_clocks[MSM_CLOCK_GPT].regbase + + TIMER_ENABLE); + } break; } - writel_relaxed(ctrl, event_base + TIMER_ENABLE); + wmb(); + local_irq_restore(irq_flags); } -static struct clock_event_device msm_clockevent = { - .name = "gp_timer", - .features = CLOCK_EVT_FEAT_ONESHOT, - .rating = 200, - .set_next_event = msm_timer_set_next_event, - .set_mode = msm_timer_set_mode, -}; - -static union { - struct clock_event_device *evt; - struct clock_event_device __percpu **percpu_evt; -} msm_evt; - -static void __iomem *source_base; - -static notrace cycle_t msm_read_timer_count(struct clocksource *cs) +void __iomem *msm_timer_get_timer0_base(void) { - return readl_relaxed(source_base + TIMER_COUNT_VAL); + return MSM_TMR_BASE + global_timer_offset; } -static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) +#define MPM_SCLK_COUNT_VAL 0x0024 + +#ifdef CONFIG_PM +/* + * Retrieve the cycle count from sclk and optionally synchronize local clock + * with the sclk value. + * + * time_start and time_expired are callbacks that must be specified. The + * protocol uses them to detect timeout. The update callback is optional. + * If not NULL, update will be called so that it can update local clock. + * + * The function does not use the argument data directly; it passes data to + * the callbacks. + * + * Return value: + * 0: the operation failed + * >0: the slow clock value after time-sync + */ +static void (*msm_timer_sync_timeout)(void); +#if defined(CONFIG_MSM_DIRECT_SCLK_ACCESS) +uint32_t msm_timer_get_sclk_ticks(void) { - /* - * Shift timer count down by a constant due to unreliable lower bits - * on some targets. - */ - return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; + uint32_t t1, t2; + int loop_count = 10; + int loop_zero_count = 3; + int tmp = USEC_PER_SEC; + do_div(tmp, sclk_hz); + tmp /= (loop_zero_count-1); + + while (loop_zero_count--) { + t1 = __raw_readl_no_log(MSM_RPM_MPM_BASE + MPM_SCLK_COUNT_VAL); + do { + udelay(1); + t2 = t1; + t1 = __raw_readl_no_log( + MSM_RPM_MPM_BASE + MPM_SCLK_COUNT_VAL); + } while ((t2 != t1) && --loop_count); + + if (!loop_count) { + printk(KERN_EMERG "SCLK did not stabilize\n"); + return 0; + } + + if (t1) + break; + + udelay(tmp); + } + + if (!loop_zero_count) { + printk(KERN_EMERG "SCLK reads zero\n"); + return 0; + } + + return t1; } -static struct clocksource msm_clocksource = { - .name = "dg_timer", - .rating = 300, - .read = msm_read_timer_count, - .mask = CLOCKSOURCE_MASK(32), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; +static uint32_t msm_timer_do_sync_to_sclk( + void (*time_start)(struct msm_timer_sync_data_t *data), + bool (*time_expired)(struct msm_timer_sync_data_t *data), + void (*update)(struct msm_timer_sync_data_t *, uint32_t, uint32_t), + struct msm_timer_sync_data_t *data) +{ + unsigned t1 = msm_timer_get_sclk_ticks(); + + if (t1 && update != NULL) + update(data, t1, sclk_hz); + return t1; +} +#elif defined(CONFIG_MSM_N_WAY_SMSM) + +/* Time Master State Bits */ +#define MASTER_BITS_PER_CPU 1 +#define MASTER_TIME_PENDING \ + (0x01UL << (MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) + +/* Time Slave State Bits */ +#define SLAVE_TIME_REQUEST 0x0400 +#define SLAVE_TIME_POLL 0x0800 +#define SLAVE_TIME_INIT 0x1000 + +static uint32_t msm_timer_do_sync_to_sclk( + void (*time_start)(struct msm_timer_sync_data_t *data), + bool (*time_expired)(struct msm_timer_sync_data_t *data), + void (*update)(struct msm_timer_sync_data_t *, uint32_t, uint32_t), + struct msm_timer_sync_data_t *data) +{ + uint32_t *smem_clock; + uint32_t smem_clock_val; + uint32_t state; + + smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE, sizeof(uint32_t)); + if (smem_clock == NULL) { + printk(KERN_ERR "no smem clock\n"); + return 0; + } + + state = smsm_get_state(SMSM_MODEM_STATE); + if ((state & SMSM_INIT) == 0) { + printk(KERN_ERR "smsm not initialized\n"); + return 0; + } + + time_start(data); + while ((state = smsm_get_state(SMSM_TIME_MASTER_DEM)) & + MASTER_TIME_PENDING) { + if (time_expired(data)) { + printk(KERN_EMERG "get_smem_clock: timeout 1 still " + "invalid state %x\n", state); + msm_timer_sync_timeout(); + } + } + + smsm_change_state(SMSM_APPS_DEM, SLAVE_TIME_POLL | SLAVE_TIME_INIT, + SLAVE_TIME_REQUEST); + + time_start(data); + while (!((state = smsm_get_state(SMSM_TIME_MASTER_DEM)) & + MASTER_TIME_PENDING)) { + if (time_expired(data)) { + printk(KERN_EMERG "get_smem_clock: timeout 2 still " + "invalid state %x\n", state); + msm_timer_sync_timeout(); + } + } + + smsm_change_state(SMSM_APPS_DEM, SLAVE_TIME_REQUEST, SLAVE_TIME_POLL); + + time_start(data); + do { + smem_clock_val = *smem_clock; + } while (smem_clock_val == 0 && !time_expired(data)); + + state = smsm_get_state(SMSM_TIME_MASTER_DEM); + + if (smem_clock_val) { + if (update != NULL) + update(data, smem_clock_val, sclk_hz); + + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO + "get_smem_clock: state %x clock %u\n", + state, smem_clock_val); + } else { + printk(KERN_EMERG + "get_smem_clock: timeout state %x clock %u\n", + state, smem_clock_val); + msm_timer_sync_timeout(); + } + + smsm_change_state(SMSM_APPS_DEM, SLAVE_TIME_REQUEST | SLAVE_TIME_POLL, + SLAVE_TIME_INIT); + return smem_clock_val; +} +#else /* CONFIG_MSM_N_WAY_SMSM */ +static uint32_t msm_timer_do_sync_to_sclk( + void (*time_start)(struct msm_timer_sync_data_t *data), + bool (*time_expired)(struct msm_timer_sync_data_t *data), + void (*update)(struct msm_timer_sync_data_t *, uint32_t, uint32_t), + struct msm_timer_sync_data_t *data) +{ + uint32_t *smem_clock; + uint32_t smem_clock_val; + uint32_t last_state; + uint32_t state; + + smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE, + sizeof(uint32_t)); + + if (smem_clock == NULL) { + printk(KERN_ERR "no smem clock\n"); + return 0; + } + + last_state = state = smsm_get_state(SMSM_MODEM_STATE); + smem_clock_val = *smem_clock; + if (smem_clock_val) { + printk(KERN_INFO "get_smem_clock: invalid start state %x " + "clock %u\n", state, smem_clock_val); + smsm_change_state(SMSM_APPS_STATE, + SMSM_TIMEWAIT, SMSM_TIMEINIT); + + time_start(data); + while (*smem_clock != 0 && !time_expired(data)) + ; + + smem_clock_val = *smem_clock; + if (smem_clock_val) { + printk(KERN_EMERG "get_smem_clock: timeout still " + "invalid state %x clock %u\n", + state, smem_clock_val); + msm_timer_sync_timeout(); + } + } + + time_start(data); + smsm_change_state(SMSM_APPS_STATE, SMSM_TIMEINIT, SMSM_TIMEWAIT); + do { + smem_clock_val = *smem_clock; + state = smsm_get_state(SMSM_MODEM_STATE); + if (state != last_state) { + last_state = state; + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO + "get_smem_clock: state %x clock %u\n", + state, smem_clock_val); + } + } while (smem_clock_val == 0 && !time_expired(data)); + + if (smem_clock_val) { + if (update != NULL) + update(data, smem_clock_val, sclk_hz); + } else { + printk(KERN_EMERG + "get_smem_clock: timeout state %x clock %u\n", + state, smem_clock_val); + msm_timer_sync_timeout(); + } + + smsm_change_state(SMSM_APPS_STATE, SMSM_TIMEWAIT, SMSM_TIMEINIT); + return smem_clock_val; +} +#endif /* CONFIG_MSM_N_WAY_SMSM */ + +/* + * Callback function that initializes the timeout value. + */ +static void msm_timer_sync_to_sclk_time_start( + struct msm_timer_sync_data_t *data) +{ + /* approx 2 seconds */ + uint32_t delta = data->clock->freq << data->clock->shift << 1; + data->timeout = msm_read_timer_count(data->clock, LOCAL_TIMER) + delta; +} + +/* + * Callback function that checks the timeout. + */ +static bool msm_timer_sync_to_sclk_time_expired( + struct msm_timer_sync_data_t *data) +{ + uint32_t delta = msm_read_timer_count(data->clock, LOCAL_TIMER) - + data->timeout; + return ((int32_t) delta) > 0; +} + +/* + * Callback function that updates local clock from the specified source clock + * value and frequency. + */ +static void msm_timer_sync_update(struct msm_timer_sync_data_t *data, + uint32_t src_clk_val, uint32_t src_clk_freq) +{ + struct msm_clock *dst_clk = data->clock; + struct msm_clock_percpu_data *dst_clk_state = + &__get_cpu_var(msm_clocks_percpu)[dst_clk->index]; + uint32_t dst_clk_val = msm_read_timer_count(dst_clk, LOCAL_TIMER); + uint32_t new_offset; + + if ((dst_clk->freq << dst_clk->shift) == src_clk_freq) { + new_offset = src_clk_val - dst_clk_val; + } else { + uint64_t temp; + + /* separate multiplication and division steps to reduce + rounding error */ + temp = src_clk_val; + temp *= dst_clk->freq << dst_clk->shift; + do_div(temp, src_clk_freq); + + new_offset = (uint32_t)(temp) - dst_clk_val; + } + + if (dst_clk_state->sleep_offset + dst_clk_state->non_sleep_offset != + new_offset) { + if (data->exit_sleep) + dst_clk_state->sleep_offset = + new_offset - dst_clk_state->non_sleep_offset; + else + dst_clk_state->non_sleep_offset = + new_offset - dst_clk_state->sleep_offset; + + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO "sync clock %s: " + "src %u, new offset %u + %u\n", + dst_clk->clocksource.name, src_clk_val, + dst_clk_state->sleep_offset, + dst_clk_state->non_sleep_offset); + } +} + +/* + * Synchronize GPT clock with sclk. + */ +static void msm_timer_sync_gpt_to_sclk(int exit_sleep) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock_percpu_data *gpt_clk_state = + &__get_cpu_var(msm_clocks_percpu)[MSM_CLOCK_GPT]; + struct msm_timer_sync_data_t data; + uint32_t ret; + + if (gpt_clk_state->in_sync) + return; + + data.clock = gpt_clk; + data.timeout = 0; + data.exit_sleep = exit_sleep; + + ret = msm_timer_do_sync_to_sclk( + msm_timer_sync_to_sclk_time_start, + msm_timer_sync_to_sclk_time_expired, + msm_timer_sync_update, + &data); + + if (ret) + gpt_clk_state->in_sync = 1; +} + +/* + * Synchronize clock with GPT clock. + */ +static void msm_timer_sync_to_gpt(struct msm_clock *clock, int exit_sleep) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock_percpu_data *gpt_clk_state = + &__get_cpu_var(msm_clocks_percpu)[MSM_CLOCK_GPT]; + struct msm_clock_percpu_data *clock_state = + &__get_cpu_var(msm_clocks_percpu)[clock->index]; + struct msm_timer_sync_data_t data; + uint32_t gpt_clk_val; + u64 gpt_period = (1ULL << 32) * HZ; + u64 now = get_jiffies_64(); + + do_div(gpt_period, gpt_hz); + + BUG_ON(clock == gpt_clk); + + if (clock_state->in_sync && + (now - clock_state->last_sync_jiffies < (gpt_period >> 1))) + return; + + gpt_clk_val = msm_read_timer_count(gpt_clk, LOCAL_TIMER) + + gpt_clk_state->sleep_offset + gpt_clk_state->non_sleep_offset; + + if (exit_sleep && gpt_clk_val < clock_state->last_sync_gpt) + clock_state->non_sleep_offset -= clock->rollover_offset; + + data.clock = clock; + data.timeout = 0; + data.exit_sleep = exit_sleep; + + msm_timer_sync_update(&data, gpt_clk_val, gpt_hz); + + clock_state->in_sync = 1; + clock_state->last_sync_gpt = gpt_clk_val; + clock_state->last_sync_jiffies = now; +} + +static void msm_timer_reactivate_alarm(struct msm_clock *clock) +{ + struct msm_clock_percpu_data *clock_state = + &__get_cpu_var(msm_clocks_percpu)[clock->index]; + long alarm_delta = clock_state->alarm_vtime - + clock_state->sleep_offset - + msm_read_timer_count(clock, LOCAL_TIMER); + alarm_delta >>= clock->shift; + if (alarm_delta < (long)clock->write_delay + 4) + alarm_delta = clock->write_delay + 4; + while (msm_timer_set_next_event(alarm_delta, &clock->clockevent)) + ; +} + +int64_t msm_timer_enter_idle(void) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock *clock = __get_cpu_var(msm_active_clock); + struct msm_clock_percpu_data *clock_state = + &__get_cpu_var(msm_clocks_percpu)[clock->index]; + uint32_t alarm; + uint32_t count; + int32_t delta; + + BUG_ON(clock != &msm_clocks[MSM_CLOCK_GPT] && + clock != &msm_clocks[MSM_CLOCK_DGT]); + + msm_timer_sync_gpt_to_sclk(0); + if (clock != gpt_clk) + msm_timer_sync_to_gpt(clock, 0); + + count = msm_read_timer_count(clock, LOCAL_TIMER); + if (clock_state->stopped++ == 0) + clock_state->stopped_tick = count + clock_state->sleep_offset; + alarm = clock_state->alarm; + delta = alarm - count; + if (delta <= -(int32_t)((clock->freq << clock->shift) >> 10)) { + /* timer should have triggered 1ms ago */ + printk(KERN_ERR "msm_timer_enter_idle: timer late %d, " + "reprogram it\n", delta); + msm_timer_reactivate_alarm(clock); + } + if (delta <= 0) + return 0; + return clocksource_cyc2ns((alarm - count) >> clock->shift, + clock->clocksource.mult, + clock->clocksource.shift); +} + +void msm_timer_exit_idle(int low_power) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock *clock = __get_cpu_var(msm_active_clock); + struct msm_clock_percpu_data *gpt_clk_state = + &__get_cpu_var(msm_clocks_percpu)[MSM_CLOCK_GPT]; + struct msm_clock_percpu_data *clock_state = + &__get_cpu_var(msm_clocks_percpu)[clock->index]; + uint32_t enabled; + + BUG_ON(clock != &msm_clocks[MSM_CLOCK_GPT] && + clock != &msm_clocks[MSM_CLOCK_DGT]); + + if (!low_power) + goto exit_idle_exit; + + enabled = __raw_readl(gpt_clk->regbase + TIMER_ENABLE) & + TIMER_ENABLE_EN; + if (!enabled) + __raw_writel(TIMER_ENABLE_EN, gpt_clk->regbase + TIMER_ENABLE); + +#if defined(CONFIG_ARCH_MSM_SCORPION) || defined(CONFIG_ARCH_MSM_KRAIT) + gpt_clk_state->in_sync = 0; +#else + gpt_clk_state->in_sync = gpt_clk_state->in_sync && enabled; +#endif + /* Make sure timer is actually enabled before we sync it */ + wmb(); + msm_timer_sync_gpt_to_sclk(1); + + if (clock == gpt_clk) + goto exit_idle_alarm; + + enabled = __raw_readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN; + if (!enabled) + __raw_writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); + +#if defined(CONFIG_ARCH_MSM_SCORPION) || defined(CONFIG_ARCH_MSM_KRAIT) + clock_state->in_sync = 0; +#else + clock_state->in_sync = clock_state->in_sync && enabled; +#endif + /* Make sure timer is actually enabled before we sync it */ + wmb(); + msm_timer_sync_to_gpt(clock, 1); + +exit_idle_alarm: + msm_timer_reactivate_alarm(clock); + +exit_idle_exit: + clock_state->stopped--; +} + +/* + * Callback function that initializes the timeout value. + */ +static void msm_timer_get_sclk_time_start( + struct msm_timer_sync_data_t *data) +{ + data->timeout = 200000; +} + +/* + * Callback function that checks the timeout. + */ +static bool msm_timer_get_sclk_time_expired( + struct msm_timer_sync_data_t *data) +{ + udelay(10); + return --data->timeout <= 0; +} + +/* + * Retrieve the cycle count from the sclk and convert it into + * nanoseconds. + * + * On exit, if period is not NULL, it contains the period of the + * sclk in nanoseconds, i.e. how long the cycle count wraps around. + * + * Return value: + * 0: the operation failed; period is not set either + * >0: time in nanoseconds + */ +int64_t msm_timer_get_sclk_time(int64_t *period) +{ + struct msm_timer_sync_data_t data; + uint32_t clock_value; + int64_t tmp; + + memset(&data, 0, sizeof(data)); + clock_value = msm_timer_do_sync_to_sclk( + msm_timer_get_sclk_time_start, + msm_timer_get_sclk_time_expired, + NULL, + &data); + + if (!clock_value) + return 0; + + if (period) { + tmp = 1LL << 32; + tmp *= NSEC_PER_SEC; + do_div(tmp, sclk_hz); + *period = tmp; + } + + tmp = (int64_t)clock_value; + tmp *= NSEC_PER_SEC; + do_div(tmp, sclk_hz); + return tmp; +} + +int __init msm_timer_init_time_sync(void (*timeout)(void)) +{ +#if defined(CONFIG_MSM_N_WAY_SMSM) && !defined(CONFIG_MSM_DIRECT_SCLK_ACCESS) + int ret = smsm_change_intr_mask(SMSM_TIME_MASTER_DEM, 0xFFFFFFFF, 0); + + if (ret) { + printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n", + __func__, ret); + return ret; + } + + smsm_change_state(SMSM_APPS_DEM, + SLAVE_TIME_REQUEST | SLAVE_TIME_POLL, SLAVE_TIME_INIT); +#endif + + BUG_ON(timeout == NULL); + msm_timer_sync_timeout = timeout; + + return 0; +} + +#endif + +static u32 notrace msm_read_sched_clock(void) +{ + struct msm_clock *clock = &msm_clocks[msm_global_timer]; + struct clocksource *cs = &clock->clocksource; + return cs->read(NULL); +} + +int read_current_timer(unsigned long *timer_val) +{ + struct msm_clock *dgt = &msm_clocks[MSM_CLOCK_DGT]; + *timer_val = msm_read_timer_count(dgt, GLOBAL_TIMER); + return 0; +} + +static void __init msm_sched_clock_init(void) +{ + struct msm_clock *clock = &msm_clocks[msm_global_timer]; + + setup_sched_clock(msm_read_sched_clock, 32 - clock->shift, clock->freq); +} #ifdef CONFIG_LOCAL_TIMERS -static int __cpuinit msm_local_timer_setup(struct clock_event_device *evt) +int __cpuinit local_timer_setup(struct clock_event_device *evt) { + static DEFINE_PER_CPU(bool, first_boot) = true; + struct msm_clock *clock = &msm_clocks[msm_global_timer]; + /* Use existing clock_event for cpu 0 */ if (!smp_processor_id()) return 0; - writel_relaxed(0, event_base + TIMER_ENABLE); - writel_relaxed(0, event_base + TIMER_CLEAR); - writel_relaxed(~0, event_base + TIMER_MATCH_VAL); - evt->irq = msm_clockevent.irq; + if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() + || cpu_is_msm8930()) + __raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); + + if (__get_cpu_var(first_boot)) { + __raw_writel(0, clock->regbase + TIMER_ENABLE); + __raw_writel(0, clock->regbase + TIMER_CLEAR); + __raw_writel(~0, clock->regbase + TIMER_MATCH_VAL); + __get_cpu_var(first_boot) = false; + if (clock->status_mask) + while (__raw_readl(MSM_TMR_BASE + TIMER_STATUS) & + clock->status_mask) + ; + } + evt->irq = clock->irq; evt->name = "local_timer"; - evt->features = msm_clockevent.features; - evt->rating = msm_clockevent.rating; + evt->features = CLOCK_EVT_FEAT_ONESHOT; + evt->rating = clock->clockevent.rating; evt->set_mode = msm_timer_set_mode; evt->set_next_event = msm_timer_set_next_event; - evt->shift = msm_clockevent.shift; - evt->mult = div_sc(GPT_HZ, NSEC_PER_SEC, evt->shift); - evt->max_delta_ns = clockevent_delta2ns(0xf0000000, evt); + evt->shift = clock->clockevent.shift; + evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift); + evt->max_delta_ns = + clockevent_delta2ns(0xf0000000 >> clock->shift, evt); evt->min_delta_ns = clockevent_delta2ns(4, evt); - *__this_cpu_ptr(msm_evt.percpu_evt) = evt; + *__this_cpu_ptr(clock->percpu_evt) = evt; + clockevents_register_device(evt); - enable_percpu_irq(evt->irq, 0); + enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); + return 0; } -static void msm_local_timer_stop(struct clock_event_device *evt) +void local_timer_stop(struct clock_event_device *evt) { evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); disable_percpu_irq(evt->irq); } -static struct local_timer_ops msm_local_timer_ops __cpuinitdata = { - .setup = msm_local_timer_setup, - .stop = msm_local_timer_stop, +static struct local_timer_ops msm_lt_ops = { + local_timer_setup, + local_timer_stop, }; #endif /* CONFIG_LOCAL_TIMERS */ -static notrace u32 msm_sched_clock_read(void) -{ - return msm_clocksource.read(&msm_clocksource); -} - static void __init msm_timer_init(void) { - struct clock_event_device *ce = &msm_clockevent; - struct clocksource *cs = &msm_clocksource; + int i; int res; - u32 dgt_hz; + struct irq_chip *chip; + struct msm_clock *dgt = &msm_clocks[MSM_CLOCK_DGT]; + struct msm_clock *gpt = &msm_clocks[MSM_CLOCK_GPT]; - if (cpu_is_msm7x01()) { - event_base = MSM_CSR_BASE; - source_base = MSM_CSR_BASE + 0x10; - dgt_hz = 19200000 >> MSM_DGT_SHIFT; /* 600 KHz */ - cs->read = msm_read_timer_count_shift; - cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); - } else if (cpu_is_msm7x30()) { - event_base = MSM_CSR_BASE + 0x04; - source_base = MSM_CSR_BASE + 0x24; - dgt_hz = 24576000 / 4; - } else if (cpu_is_qsd8x50()) { - event_base = MSM_CSR_BASE; - source_base = MSM_CSR_BASE + 0x10; - dgt_hz = 19200000 / 4; - } else if (cpu_is_msm8x60() || cpu_is_msm8960()) { - event_base = MSM_TMR_BASE + 0x04; - /* Use CPU0's timer as the global clock source. */ - source_base = MSM_TMR0_BASE + 0x24; - dgt_hz = 27000000 / 4; - writel_relaxed(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); - } else - BUG(); - - writel_relaxed(0, event_base + TIMER_ENABLE); - writel_relaxed(0, event_base + TIMER_CLEAR); - writel_relaxed(~0, event_base + TIMER_MATCH_VAL); - ce->cpumask = cpumask_of(0); - - ce->irq = INT_GP_TIMER_EXP; - clockevents_config_and_register(ce, GPT_HZ, 4, 0xffffffff); - if (cpu_is_msm8x60() || cpu_is_msm8960()) { - msm_evt.percpu_evt = alloc_percpu(struct clock_event_device *); - if (!msm_evt.percpu_evt) { - pr_err("memory allocation failed for %s\n", ce->name); - goto err; + if (cpu_is_msm7x01() || cpu_is_msm7x25() || cpu_is_msm7x27() || + cpu_is_msm7x25a() || cpu_is_msm7x27a() || cpu_is_msm7x25aa() || + cpu_is_msm7x27aa() || cpu_is_msm8625() || cpu_is_msm7x25ab()) { + dgt->shift = MSM_DGT_SHIFT; + dgt->freq = 19200000 >> MSM_DGT_SHIFT; + dgt->clockevent.shift = 32 + MSM_DGT_SHIFT; + dgt->clocksource.mask = CLOCKSOURCE_MASK(32 - MSM_DGT_SHIFT); + gpt->regbase = MSM_TMR_BASE; + dgt->regbase = MSM_TMR_BASE + 0x10; + gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT + | MSM_CLOCK_FLAGS_ODD_MATCH_WRITE + | MSM_CLOCK_FLAGS_DELAYED_WRITE_POST; + if (cpu_is_msm8625()) { + dgt->irq = MSM8625_INT_DEBUG_TIMER_EXP; + gpt->irq = MSM8625_INT_GP_TIMER_EXP; + global_timer_offset = MSM_TMR0_BASE - MSM_TMR_BASE; } - *__this_cpu_ptr(msm_evt.percpu_evt) = ce; - res = request_percpu_irq(ce->irq, msm_timer_interrupt, - ce->name, msm_evt.percpu_evt); - if (!res) { - enable_percpu_irq(ce->irq, 0); -#ifdef CONFIG_LOCAL_TIMERS - local_timer_register(&msm_local_timer_ops); -#endif + } else if (cpu_is_qsd8x50()) { + dgt->freq = 4800000; + gpt->regbase = MSM_TMR_BASE; + dgt->regbase = MSM_TMR_BASE + 0x10; + } else if (cpu_is_fsm9xxx()) + dgt->freq = 4800000; + else if (cpu_is_msm7x30() || cpu_is_msm8x55()) { + gpt->status_mask = BIT(10); + dgt->status_mask = BIT(2); + dgt->freq = 6144000; + } else if (cpu_is_msm8x60()) { + global_timer_offset = MSM_TMR0_BASE - MSM_TMR_BASE; + gpt->status_mask = BIT(10); + dgt->status_mask = BIT(2); + dgt->freq = 6750000; + __raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); + } else if (cpu_is_msm9615()) { + dgt->freq = 6750000; + __raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); + gpt->status_mask = BIT(10); + dgt->status_mask = BIT(2); + gpt->freq = 32765; + gpt_hz = 32765; + sclk_hz = 32765; + gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT; + dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT; + } else if (cpu_is_msm8960() || cpu_is_apq8064() || cpu_is_msm8930()) { + global_timer_offset = MSM_TMR0_BASE - MSM_TMR_BASE; + dgt->freq = 6750000; + __raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); + gpt->status_mask = BIT(10); + dgt->status_mask = BIT(2); + gpt->freq = 32765; + gpt_hz = 32765; + sclk_hz = 32765; + if (!cpu_is_msm8930()) { + gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT; + dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT; } } else { - msm_evt.evt = ce; - res = request_irq(ce->irq, msm_timer_interrupt, - IRQF_TIMER | IRQF_NOBALANCING | - IRQF_TRIGGER_RISING, ce->name, &msm_evt.evt); + WARN(1, "Timer running on unknown hardware. Configure this! " + "Assuming default configuration.\n"); + dgt->freq = 6750000; } - if (res) - pr_err("request_irq failed for %s\n", ce->name); -err: - writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); - res = clocksource_register_hz(cs, dgt_hz); - if (res) - pr_err("clocksource_register failed\n"); - setup_sched_clock(msm_sched_clock_read, - cpu_is_msm7x01() ? 32 - MSM_DGT_SHIFT : 32, dgt_hz); + if (msm_clocks[MSM_CLOCK_GPT].clocksource.rating > DG_TIMER_RATING) + msm_global_timer = MSM_CLOCK_GPT; + else + msm_global_timer = MSM_CLOCK_DGT; + + for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) { + struct msm_clock *clock = &msm_clocks[i]; + struct clock_event_device *ce = &clock->clockevent; + struct clocksource *cs = &clock->clocksource; + __raw_writel(0, clock->regbase + TIMER_ENABLE); + __raw_writel(0, clock->regbase + TIMER_CLEAR); + __raw_writel(~0, clock->regbase + TIMER_MATCH_VAL); + + if ((clock->freq << clock->shift) == gpt_hz) { + clock->rollover_offset = 0; + } else { + uint64_t temp; + + temp = clock->freq << clock->shift; + temp <<= 32; + do_div(temp, gpt_hz); + + clock->rollover_offset = (uint32_t) temp; + } + + ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift); + /* allow at least 10 seconds to notice that the timer wrapped */ + ce->max_delta_ns = + clockevent_delta2ns(0xf0000000 >> clock->shift, ce); + /* ticks gets rounded down by one */ + ce->min_delta_ns = + clockevent_delta2ns(clock->write_delay + 4, ce); + ce->cpumask = cpumask_of(0); + + res = clocksource_register_hz(cs, clock->freq); + if (res) + printk(KERN_ERR "msm_timer_init: clocksource_register " + "failed for %s\n", cs->name); + + ce->irq = clock->irq; + if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() || + cpu_is_msm8930() || cpu_is_msm9615() || + cpu_is_msm8625()) { + clock->percpu_evt = alloc_percpu(struct clock_event_device *); + if (!clock->percpu_evt) { + pr_err("msm_timer_init: memory allocation " + "failed for %s\n", ce->name); + continue; + } + + *__this_cpu_ptr(clock->percpu_evt) = ce; + res = request_percpu_irq(ce->irq, msm_timer_interrupt, + ce->name, clock->percpu_evt); + if (!res) + enable_percpu_irq(ce->irq, + IRQ_TYPE_EDGE_RISING); + } else { + clock->evt = ce; + res = request_irq(ce->irq, msm_timer_interrupt, + IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING, + ce->name, &clock->evt); + } + + if (res) + pr_err("msm_timer_init: request_irq failed for %s\n", + ce->name); + + chip = irq_get_chip(clock->irq); + if (chip && chip->irq_mask) + chip->irq_mask(irq_get_irq_data(clock->irq)); + + if (clock->status_mask) + while (__raw_readl(MSM_TMR_BASE + TIMER_STATUS) & + clock->status_mask) + ; + + clockevents_register_device(ce); + } + msm_sched_clock_init(); + +#ifdef ARCH_HAS_READ_CURRENT_TIMER + if (is_smp()) { + __raw_writel(1, + msm_clocks[MSM_CLOCK_DGT].regbase + TIMER_ENABLE); + set_delay_fn(read_current_timer_delay_loop); + } +#endif + +#ifdef CONFIG_LOCAL_TIMERS + local_timer_register(&msm_lt_ops); +#endif } struct sys_timer msm_timer = { diff --git a/arch/arm/mach-msm/timer.h b/arch/arm/mach-msm/timer.h new file mode 100644 index 00000000000..5d18bb4e693 --- /dev/null +++ b/arch/arm/mach-msm/timer.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2008-2009, 2011-2012 Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_TIMER_H_ +#define _ARCH_ARM_MACH_MSM_TIMER_H_ + +extern struct sys_timer msm_timer; + +void __iomem *msm_timer_get_timer0_base(void); +uint32_t msm_timer_get_sclk_ticks(void); +int msm_timer_init_time_sync(void (*timeout)(void)); +#ifndef CONFIG_ARM_ARCH_TIMER +int64_t msm_timer_enter_idle(void); +void msm_timer_exit_idle(int low_power); +int64_t msm_timer_get_sclk_time(int64_t *period); +#else +static inline int64_t msm_timer_enter_idle(void) { return 0; } +static inline void msm_timer_exit_idle(int low_power) { return; } +static inline int64_t msm_timer_get_sclk_time(int64_t *period) { return 0; } +#endif +#endif diff --git a/arch/arm/mach-msm/tz_log.c b/arch/arm/mach-msm/tz_log.c new file mode 100644 index 00000000000..7426bb2a339 --- /dev/null +++ b/arch/arm/mach-msm/tz_log.c @@ -0,0 +1,564 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_MAX_RW_BUF 4096 + +/* + * Preprocessor Definitions and Constants + */ +#define TZBSP_CPU_COUNT 0x02 +/* + * Number of VMID Tables + */ +#define TZBSP_DIAG_NUM_OF_VMID 16 +/* + * VMID Description length + */ +#define TZBSP_DIAG_VMID_DESC_LEN 7 +/* + * Number of Interrupts + */ +#define TZBSP_DIAG_INT_NUM 32 +/* + * Length of descriptive name associated with Interrupt + */ +#define TZBSP_MAX_INT_DESC 16 +/* + * VMID Table + */ +struct tzdbg_vmid_t { + uint8_t vmid; /* Virtual Machine Identifier */ + uint8_t desc[TZBSP_DIAG_VMID_DESC_LEN]; /* ASCII Text */ +}; +/* + * Boot Info Table + */ +struct tzdbg_boot_info_t { + uint32_t wb_entry_cnt; /* Warmboot entry CPU Counter */ + uint32_t wb_exit_cnt; /* Warmboot exit CPU Counter */ + uint32_t pc_entry_cnt; /* Power Collapse entry CPU Counter */ + uint32_t pc_exit_cnt; /* Power Collapse exit CPU counter */ + uint32_t warm_jmp_addr; /* Last Warmboot Jump Address */ + uint32_t spare; /* Reserved for future use. */ +}; +/* + * Reset Info Table + */ +struct tzdbg_reset_info_t { + uint32_t reset_type; /* Reset Reason */ + uint32_t reset_cnt; /* Number of resets occured/CPU */ +}; +/* + * Interrupt Info Table + */ +struct tzdbg_int_t { + /* + * Type of Interrupt/exception + */ + uint16_t int_info; + /* + * Availability of the slot + */ + uint8_t avail; + /* + * Reserved for future use + */ + uint8_t spare; + /* + * Interrupt # for IRQ and FIQ + */ + uint32_t int_num; + /* + * ASCII text describing type of interrupt e.g: + * Secure Timer, EBI XPU. This string is always null terminated, + * supporting at most TZBSP_MAX_INT_DESC characters. + * Any additional characters are truncated. + */ + uint8_t int_desc[TZBSP_MAX_INT_DESC]; + uint64_t int_count[TZBSP_CPU_COUNT]; /* # of times seen per CPU */ +}; +/* + * Diagnostic Table + */ +struct tzdbg_t { + uint32_t magic_num; + uint32_t version; + /* + * Number of CPU's + */ + uint32_t cpu_count; + /* + * Offset of VMID Table + */ + uint32_t vmid_info_off; + /* + * Offset of Boot Table + */ + uint32_t boot_info_off; + /* + * Offset of Reset info Table + */ + uint32_t reset_info_off; + /* + * Offset of Interrupt info Table + */ + uint32_t int_info_off; + /* + * Ring Buffer Offset + */ + uint32_t ring_off; + /* + * Ring Buffer Length + */ + uint32_t ring_len; + /* + * VMID to EE Mapping + */ + struct tzdbg_vmid_t vmid_info[TZBSP_DIAG_NUM_OF_VMID]; + /* + * Boot Info + */ + struct tzdbg_boot_info_t boot_info[TZBSP_CPU_COUNT]; + /* + * Reset Info + */ + struct tzdbg_reset_info_t reset_info[TZBSP_CPU_COUNT]; + uint32_t num_interrupts; + struct tzdbg_int_t int_info[TZBSP_DIAG_INT_NUM]; + /* + * We need at least 2K for the ring buffer + */ + uint8_t *ring_buffer; /* TZ Ring Buffer */ +}; + +/* + * Enumeration order for VMID's + */ +enum tzdbg_stats_type { + TZDBG_BOOT = 0, + TZDBG_RESET, + TZDBG_INTERRUPT, + TZDBG_VMID, + TZDBG_GENERAL, + TZDBG_LOG, + TZDBG_STATS_MAX, +}; + +struct tzdbg_stat { + char *name; + char *data; +}; + +struct tzdbg { + void __iomem *virt_iobase; + struct tzdbg_t *diag_buf; + char *disp_buf; + int debug_tz[TZDBG_STATS_MAX]; + struct tzdbg_stat stat[TZDBG_STATS_MAX]; +}; + +static struct tzdbg tzdbg = { + + .stat[TZDBG_BOOT].name = "boot", + .stat[TZDBG_RESET].name = "reset", + .stat[TZDBG_INTERRUPT].name = "interrupt", + .stat[TZDBG_VMID].name = "vmid", + .stat[TZDBG_GENERAL].name = "general", + .stat[TZDBG_LOG].name = "log", +}; + + +/* + * Debugfs data structure and functions + */ + +static int _disp_tz_general_stats(void) +{ + int len = 0; + + len += snprintf(tzdbg.disp_buf + len, DEBUG_MAX_RW_BUF - 1, + " Version : 0x%x\n" + " Magic Number : 0x%x\n" + " Number of CPU : %d\n", + tzdbg.diag_buf->version, + tzdbg.diag_buf->magic_num, + tzdbg.diag_buf->cpu_count); + tzdbg.stat[TZDBG_GENERAL].data = tzdbg.disp_buf; + return len; +} + +static int _disp_tz_vmid_stats(void) +{ + int i, num_vmid; + int len = 0; + struct tzdbg_vmid_t *ptr; + + ptr = (struct tzdbg_vmid_t *)((unsigned char *)tzdbg.diag_buf + + tzdbg.diag_buf->vmid_info_off); + num_vmid = ((tzdbg.diag_buf->boot_info_off - + tzdbg.diag_buf->vmid_info_off)/ + (sizeof(struct tzdbg_vmid_t))); + + for (i = 0; i < num_vmid; i++) { + if (ptr->vmid < 0xFF) { + len += snprintf(tzdbg.disp_buf + len, + (DEBUG_MAX_RW_BUF - 1) - len, + " 0x%x %s\n", + (uint32_t)ptr->vmid, (uint8_t *)ptr->desc); + } + if (len > (DEBUG_MAX_RW_BUF - 1)) { + pr_warn("%s: Cannot fit all info into the buffer\n", + __func__); + break; + } + ptr++; + } + + tzdbg.stat[TZDBG_VMID].data = tzdbg.disp_buf; + return len; +} + +static int _disp_tz_boot_stats(void) +{ + int i; + int len = 0; + struct tzdbg_boot_info_t *ptr; + + ptr = (struct tzdbg_boot_info_t *)((unsigned char *)tzdbg.diag_buf + + tzdbg.diag_buf->boot_info_off); + + for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) { + len += snprintf(tzdbg.disp_buf + len, + (DEBUG_MAX_RW_BUF - 1) - len, + " CPU #: %d\n" + " Warmboot jump address : 0x%x\n" + " Warmboot entry CPU counter: 0x%x\n" + " Warmboot exit CPU counter : 0x%x\n" + " Power Collapse entry CPU counter: 0x%x\n" + " Power Collapse exit CPU counter : 0x%x\n", + i, ptr->warm_jmp_addr, ptr->wb_entry_cnt, + ptr->wb_exit_cnt, ptr->pc_entry_cnt, + ptr->pc_exit_cnt); + + if (len > (DEBUG_MAX_RW_BUF - 1)) { + pr_warn("%s: Cannot fit all info into the buffer\n", + __func__); + break; + } + ptr++; + } + tzdbg.stat[TZDBG_BOOT].data = tzdbg.disp_buf; + return len; +} + +static int _disp_tz_reset_stats(void) +{ + int i; + int len = 0; + struct tzdbg_reset_info_t *ptr; + + ptr = (struct tzdbg_reset_info_t *)((unsigned char *)tzdbg.diag_buf + + tzdbg.diag_buf->reset_info_off); + + for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) { + len += snprintf(tzdbg.disp_buf + len, + (DEBUG_MAX_RW_BUF - 1) - len, + " CPU #: %d\n" + " Reset Type (reason) : 0x%x\n" + " Reset counter : 0x%x\n", + i, ptr->reset_type, ptr->reset_cnt); + + if (len > (DEBUG_MAX_RW_BUF - 1)) { + pr_warn("%s: Cannot fit all info into the buffer\n", + __func__); + break; + } + + ptr++; + } + tzdbg.stat[TZDBG_RESET].data = tzdbg.disp_buf; + return len; +} + +static int _disp_tz_interrupt_stats(void) +{ + int i, j, int_info_size; + int len = 0; + int *num_int; + unsigned char *ptr; + struct tzdbg_int_t *tzdbg_ptr; + + num_int = (uint32_t *)((unsigned char *)tzdbg.diag_buf + + (tzdbg.diag_buf->int_info_off - sizeof(uint32_t))); + ptr = ((unsigned char *)tzdbg.diag_buf + + tzdbg.diag_buf->int_info_off); + int_info_size = ((tzdbg.diag_buf->ring_off - + tzdbg.diag_buf->int_info_off)/(*num_int)); + + for (i = 0; i < (*num_int); i++) { + tzdbg_ptr = (struct tzdbg_int_t *)ptr; + len += snprintf(tzdbg.disp_buf + len, + (DEBUG_MAX_RW_BUF - 1) - len, + " Interrupt Number : 0x%x\n" + " Type of Interrupt : 0x%x\n" + " Description of interrupt : %s\n", + tzdbg_ptr->int_num, + (uint32_t)tzdbg_ptr->int_info, + (uint8_t *)tzdbg_ptr->int_desc); + for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { + len += snprintf(tzdbg.disp_buf + len, + (DEBUG_MAX_RW_BUF - 1) - len, + " int_count on CPU # %d : %u\n", + (uint32_t)j, + (uint32_t)tzdbg_ptr->int_count[j]); + } + len += snprintf(tzdbg.disp_buf + len, DEBUG_MAX_RW_BUF - 1, + "\n"); + + if (len > (DEBUG_MAX_RW_BUF - 1)) { + pr_warn("%s: Cannot fit all info into the buffer\n", + __func__); + break; + } + + ptr += int_info_size; + } + tzdbg.stat[TZDBG_INTERRUPT].data = tzdbg.disp_buf; + return len; +} + +static int _disp_tz_log_stats(void) +{ + int len = 0; + unsigned char *ptr; + + ptr = (unsigned char *)tzdbg.diag_buf + + tzdbg.diag_buf->ring_off; + len += snprintf(tzdbg.disp_buf, (DEBUG_MAX_RW_BUF - 1) - len, + "%s\n", ptr); + + tzdbg.stat[TZDBG_LOG].data = tzdbg.disp_buf; + return len; +} + +static ssize_t tzdbgfs_read(struct file *file, char __user *buf, + size_t count, loff_t *offp) +{ + int len = 0; + int *tz_id = file->private_data; + + memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase, + DEBUG_MAX_RW_BUF); + switch (*tz_id) { + case TZDBG_BOOT: + len = _disp_tz_boot_stats(); + break; + case TZDBG_RESET: + len = _disp_tz_reset_stats(); + break; + case TZDBG_INTERRUPT: + len = _disp_tz_interrupt_stats(); + break; + case TZDBG_GENERAL: + len = _disp_tz_general_stats(); + break; + case TZDBG_VMID: + len = _disp_tz_vmid_stats(); + break; + case TZDBG_LOG: + len = _disp_tz_log_stats(); + break; + default: + break; + } + + if (len > count) + len = count; + + return simple_read_from_buffer(buf, len, offp, + tzdbg.stat[(*tz_id)].data, len); +} + +static int tzdbgfs_open(struct inode *inode, struct file *pfile) +{ + pfile->private_data = inode->i_private; + return 0; +} + +const struct file_operations tzdbg_fops = { + .owner = THIS_MODULE, + .read = tzdbgfs_read, + .open = tzdbgfs_open, +}; + +static int tzdbgfs_init(struct platform_device *pdev) +{ + int rc = 0; + int i; + struct dentry *dent_dir; + struct dentry *dent; + + dent_dir = debugfs_create_dir("tzdbg", NULL); + if (dent_dir == NULL) { + dev_err(&pdev->dev, "tzdbg debugfs_create_dir failed\n"); + return -ENOMEM; + } + + for (i = 0; i < TZDBG_STATS_MAX; i++) { + tzdbg.debug_tz[i] = i; + dent = debugfs_create_file(tzdbg.stat[i].name, + S_IRUGO, dent_dir, + &tzdbg.debug_tz[i], &tzdbg_fops); + if (dent == NULL) { + dev_err(&pdev->dev, "TZ debugfs_create_file failed\n"); + rc = -ENOMEM; + goto err; + } + } + tzdbg.disp_buf = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL); + if (tzdbg.disp_buf == NULL) { + pr_err("%s: Can't Allocate memory for tzdbg.disp_buf\n", + __func__); + + goto err; + } + platform_set_drvdata(pdev, dent_dir); + return 0; +err: + debugfs_remove_recursive(dent_dir); + + return rc; +} + +static void tzdbgfs_exit(struct platform_device *pdev) +{ + struct dentry *dent_dir; + + kzfree(tzdbg.disp_buf); + dent_dir = platform_get_drvdata(pdev); + debugfs_remove_recursive(dent_dir); +} + +/* + * Driver functions + */ +static int __devinit tz_log_probe(struct platform_device *pdev) +{ + struct resource *resource; + void __iomem *virt_iobase; + uint32_t tzdiag_phy_iobase; + uint32_t *ptr = NULL; + + /* + * Get address that stores the physical location of 4KB + * diagnostic data + */ + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + dev_err(&pdev->dev, + "%s: ERROR Missing MEM resource\n", __func__); + return -ENXIO; + }; + /* + * Map address that stores the physical location of 4KB + * diagnostic data + */ + virt_iobase = devm_ioremap_nocache(&pdev->dev, resource->start, + resource->end - resource->start + 1); + if (!virt_iobase) { + dev_err(&pdev->dev, + "%s: ERROR could not ioremap: start=%p, len=%u\n", + __func__, (void *) resource->start, + (resource->end - resource->start + 1)); + return -ENXIO; + } + /* + * Retrieve the address of 4KB diagnostic data + */ + tzdiag_phy_iobase = readl_relaxed(virt_iobase); + + /* + * Map the 4KB diagnostic information area + */ + tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev, + tzdiag_phy_iobase, DEBUG_MAX_RW_BUF); + + if (!tzdbg.virt_iobase) { + dev_err(&pdev->dev, + "%s: ERROR could not ioremap: start=%p, len=%u\n", + __func__, (void *) tzdiag_phy_iobase, DEBUG_MAX_RW_BUF); + return -ENXIO; + } + + ptr = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL); + if (ptr == NULL) { + pr_err("%s: Can't Allocate memory: ptr\n", + __func__); + return -ENXIO; + } + + tzdbg.diag_buf = (struct tzdbg_t *)ptr; + + if (tzdbgfs_init(pdev)) + goto err; + + return 0; +err: + kfree(tzdbg.diag_buf); + return -ENXIO; +} + + +static int __devexit tz_log_remove(struct platform_device *pdev) +{ + kzfree(tzdbg.diag_buf); + tzdbgfs_exit(pdev); + + return 0; +} + +static struct platform_driver tz_log_driver = { + .probe = tz_log_probe, + .remove = __devexit_p(tz_log_remove), + .driver = { + .name = "tz_log", + .owner = THIS_MODULE, + }, +}; + +static int __init tz_log_init(void) +{ + return platform_driver_register(&tz_log_driver); +} + +static void __exit tz_log_exit(void) +{ + platform_driver_unregister(&tz_log_driver); +} + +module_init(tz_log_init); +module_exit(tz_log_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TZ Log driver"); +MODULE_VERSION("1.1"); +MODULE_ALIAS("platform:tz_log"); diff --git a/arch/arm/mach-msm/vreg.c b/arch/arm/mach-msm/vreg.c index bd66ed04d6d..271da867d7e 100644 --- a/arch/arm/mach-msm/vreg.c +++ b/arch/arm/mach-msm/vreg.c @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/vreg.c * * Copyright (C) 2008 Google, Inc. - * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2012 Code Aurora Forum. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -16,170 +16,211 @@ */ #include +#include #include #include +#include +#include +#include +#include #include -#include +#include #include #include +#include -#include "proc_comm.h" +#if defined(CONFIG_MSM_VREG_SWITCH_INVERTED) +#define VREG_SWITCH_ENABLE 0 +#define VREG_SWITCH_DISABLE 1 +#else +#define VREG_SWITCH_ENABLE 1 +#define VREG_SWITCH_DISABLE 0 +#endif struct vreg { - const char *name; - unsigned id; - int status; - unsigned refcnt; + struct list_head list; + struct mutex lock; + const char *name; + u64 refcnt; + unsigned mv; + struct regulator *reg; }; -#define VREG(_name, _id, _status, _refcnt) \ - { .name = _name, .id = _id, .status = _status, .refcnt = _refcnt } +static LIST_HEAD(vreg_list); +static DEFINE_MUTEX(vreg_lock); -static struct vreg vregs[] = { - VREG("msma", 0, 0, 0), - VREG("msmp", 1, 0, 0), - VREG("msme1", 2, 0, 0), - VREG("msmc1", 3, 0, 0), - VREG("msmc2", 4, 0, 0), - VREG("gp3", 5, 0, 0), - VREG("msme2", 6, 0, 0), - VREG("gp4", 7, 0, 0), - VREG("gp1", 8, 0, 0), - VREG("tcxo", 9, 0, 0), - VREG("pa", 10, 0, 0), - VREG("rftx", 11, 0, 0), - VREG("rfrx1", 12, 0, 0), - VREG("rfrx2", 13, 0, 0), - VREG("synt", 14, 0, 0), - VREG("wlan", 15, 0, 0), - VREG("usb", 16, 0, 0), - VREG("boost", 17, 0, 0), - VREG("mmc", 18, 0, 0), - VREG("ruim", 19, 0, 0), - VREG("msmc0", 20, 0, 0), - VREG("gp2", 21, 0, 0), - VREG("gp5", 22, 0, 0), - VREG("gp6", 23, 0, 0), - VREG("rf", 24, 0, 0), - VREG("rf_vco", 26, 0, 0), - VREG("mpll", 27, 0, 0), - VREG("s2", 28, 0, 0), - VREG("s3", 29, 0, 0), - VREG("rfubm", 30, 0, 0), - VREG("ncp", 31, 0, 0), - VREG("gp7", 32, 0, 0), - VREG("gp8", 33, 0, 0), - VREG("gp9", 34, 0, 0), - VREG("gp10", 35, 0, 0), - VREG("gp11", 36, 0, 0), - VREG("gp12", 37, 0, 0), - VREG("gp13", 38, 0, 0), - VREG("gp14", 39, 0, 0), - VREG("gp15", 40, 0, 0), - VREG("gp16", 41, 0, 0), - VREG("gp17", 42, 0, 0), - VREG("s4", 43, 0, 0), - VREG("usb2", 44, 0, 0), - VREG("wlan2", 45, 0, 0), - VREG("xo_out", 46, 0, 0), - VREG("lvsw0", 47, 0, 0), - VREG("lvsw1", 48, 0, 0), -}; +#ifdef CONFIG_DEBUG_FS +static void vreg_add_debugfs(struct vreg *vreg); +#else +static inline void vreg_add_debugfs(struct vreg *vreg) { } +#endif + +static struct vreg *vreg_create(const char *id) +{ + int rc; + struct vreg *vreg; + + vreg = kzalloc(sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + rc = -ENOMEM; + goto error; + } + + INIT_LIST_HEAD(&vreg->list); + mutex_init(&vreg->lock); + + vreg->reg = regulator_get(NULL, id); + if (IS_ERR(vreg->reg)) { + rc = PTR_ERR(vreg->reg); + goto free_vreg; + } + + vreg->name = kstrdup(id, GFP_KERNEL); + if (!vreg->name) { + rc = -ENOMEM; + goto put_reg; + } + + list_add_tail(&vreg->list, &vreg_list); + vreg_add_debugfs(vreg); + + return vreg; + +put_reg: + regulator_put(vreg->reg); +free_vreg: + kfree(vreg); +error: + return ERR_PTR(rc); +} + +static void vreg_destroy(struct vreg *vreg) +{ + if (!vreg) + return; + + if (vreg->refcnt) + regulator_disable(vreg->reg); + + kfree(vreg->name); + regulator_put(vreg->reg); + kfree(vreg); +} struct vreg *vreg_get(struct device *dev, const char *id) { - int n; - for (n = 0; n < ARRAY_SIZE(vregs); n++) { - if (!strcmp(vregs[n].name, id)) - return vregs + n; + struct vreg *vreg = NULL; + + if (!id) + return ERR_PTR(-EINVAL); + + mutex_lock(&vreg_lock); + list_for_each_entry(vreg, &vreg_list, list) { + if (!strncmp(vreg->name, id, 10)) + goto ret; } - return ERR_PTR(-ENOENT); + + vreg = vreg_create(id); + +ret: + mutex_unlock(&vreg_lock); + return vreg; } +EXPORT_SYMBOL(vreg_get); void vreg_put(struct vreg *vreg) { + kfree(vreg->name); + regulator_put(vreg->reg); + list_del(&vreg->list); + kfree(vreg); } int vreg_enable(struct vreg *vreg) { - unsigned id = vreg->id; - unsigned enable = 1; + int rc = 0; + if (!vreg) + return -ENODEV; - if (vreg->refcnt == 0) - vreg->status = msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable); + mutex_lock(&vreg->lock); + if (vreg->refcnt == 0) { + rc = regulator_enable(vreg->reg); + if (!rc) + vreg->refcnt++; + } else { + rc = 0; + if (vreg->refcnt < UINT_MAX) + vreg->refcnt++; + } + mutex_unlock(&vreg->lock); - if ((vreg->refcnt < UINT_MAX) && (!vreg->status)) - vreg->refcnt++; - - return vreg->status; + return rc; } +EXPORT_SYMBOL(vreg_enable); int vreg_disable(struct vreg *vreg) { - unsigned id = vreg->id; - unsigned enable = 0; + int rc = 0; + if (!vreg) + return -ENODEV; - if (!vreg->refcnt) - return 0; - - if (vreg->refcnt == 1) - vreg->status = msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable); - - if (!vreg->status) + mutex_lock(&vreg->lock); + if (vreg->refcnt == 0) { + pr_warn("%s: unbalanced disables for vreg %s\n", + __func__, vreg->name); + rc = -EINVAL; + } else if (vreg->refcnt == 1) { + rc = regulator_disable(vreg->reg); + if (!rc) + vreg->refcnt--; + } else { + rc = 0; vreg->refcnt--; + } + mutex_unlock(&vreg->lock); - return vreg->status; + return rc; } +EXPORT_SYMBOL(vreg_disable); int vreg_set_level(struct vreg *vreg, unsigned mv) { - unsigned id = vreg->id; + unsigned uv; + int rc; - vreg->status = msm_proc_comm(PCOM_VREG_SET_LEVEL, &id, &mv); - return vreg->status; + if (!vreg) + return -EINVAL; + + if (mv > (UINT_MAX / 1000)) + return -ERANGE; + + uv = mv * 1000; + + mutex_lock(&vreg->lock); + rc = regulator_set_voltage(vreg->reg, uv, uv); + if (!rc) + vreg->mv = mv; + mutex_unlock(&vreg->lock); + + return rc; } +EXPORT_SYMBOL(vreg_set_level); #if defined(CONFIG_DEBUG_FS) -static int vreg_debug_set(void *data, u64 val) -{ - struct vreg *vreg = data; - switch (val) { - case 0: - vreg_disable(vreg); - break; - case 1: - vreg_enable(vreg); - break; - default: - vreg_set_level(vreg, val); - break; - } - return 0; -} - -static int vreg_debug_get(void *data, u64 *val) +static int vreg_debug_enabled_set(void *data, u64 val) { struct vreg *vreg = data; - if (!vreg->status) - *val = 0; + if (val == 0) + return vreg_disable(vreg); + else if (val == 1) + return vreg_enable(vreg); else - *val = 1; - - return 0; + return -EINVAL; } -static int vreg_debug_count_set(void *data, u64 val) -{ - struct vreg *vreg = data; - if (val > UINT_MAX) - val = UINT_MAX; - vreg->refcnt = val; - return 0; -} - -static int vreg_debug_count_get(void *data, u64 *val) +static int vreg_debug_enabled_get(void *data, u64 *val) { struct vreg *vreg = data; @@ -188,33 +229,97 @@ static int vreg_debug_count_get(void *data, u64 *val) return 0; } -DEFINE_SIMPLE_ATTRIBUTE(vreg_fops, vreg_debug_get, vreg_debug_set, "%llu\n"); -DEFINE_SIMPLE_ATTRIBUTE(vreg_count_fops, vreg_debug_count_get, - vreg_debug_count_set, "%llu\n"); - -static int __init vreg_debug_init(void) +static int vreg_debug_voltage_set(void *data, u64 val) { - struct dentry *dent; - int n; - char name[32]; - const char *refcnt_name = "_refcnt"; + struct vreg *vreg = data; + return vreg_set_level(vreg, val); +} - dent = debugfs_create_dir("vreg", 0); - if (IS_ERR(dent)) - return 0; +static int vreg_debug_voltage_get(void *data, u64 *val) +{ + struct vreg *vreg = data; + *val = vreg->mv; + return 0; +} - for (n = 0; n < ARRAY_SIZE(vregs); n++) { - (void) debugfs_create_file(vregs[n].name, 0644, - dent, vregs + n, &vreg_fops); +DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_enabled, vreg_debug_enabled_get, + vreg_debug_enabled_set, "%llu"); +DEFINE_SIMPLE_ATTRIBUTE(vreg_debug_voltage, vreg_debug_voltage_get, + vreg_debug_voltage_set, "%llu"); - strlcpy(name, vregs[n].name, sizeof(name)); - strlcat(name, refcnt_name, sizeof(name)); - (void) debugfs_create_file(name, 0644, - dent, vregs + n, &vreg_count_fops); +static struct dentry *root; + +static void vreg_add_debugfs(struct vreg *vreg) +{ + struct dentry *dir; + + if (!root) + return; + + dir = debugfs_create_dir(vreg->name, root); + + if (IS_ERR_OR_NULL(dir)) + goto err; + + if (IS_ERR_OR_NULL(debugfs_create_file("enabled", 0644, dir, vreg, + &vreg_debug_enabled))) + goto destroy; + + if (IS_ERR_OR_NULL(debugfs_create_file("voltage", 0644, dir, vreg, + &vreg_debug_voltage))) + goto destroy; + + return; + +destroy: + debugfs_remove_recursive(dir); +err: + pr_warn("%s: could not create debugfs for vreg %s\n", + __func__, vreg->name); +} + +static int __devinit vreg_debug_init(void) +{ + root = debugfs_create_dir("vreg", NULL); + + if (IS_ERR_OR_NULL(root)) { + pr_debug("%s: error initializing debugfs: %ld - " + "disabling debugfs\n", + __func__, root ? PTR_ERR(root) : 0); + root = NULL; } return 0; } - -device_initcall(vreg_debug_init); +static void __devexit vreg_debug_exit(void) +{ + if (root) + debugfs_remove_recursive(root); + root = NULL; +} +#else +static inline int __init vreg_debug_init(void) { return 0; } +static inline void __exit vreg_debug_exit(void) { return 0; } #endif + +static int __init vreg_init(void) +{ + return vreg_debug_init(); +} +module_init(vreg_init); + +static void __exit vreg_exit(void) +{ + struct vreg *vreg, *next; + vreg_debug_exit(); + + mutex_lock(&vreg_lock); + list_for_each_entry_safe(vreg, next, &vreg_list, list) + vreg_destroy(vreg); + mutex_unlock(&vreg_lock); +} +module_exit(vreg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("vreg.c regulator shim"); +MODULE_VERSION("1.0"); diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c new file mode 100644 index 00000000000..266c8b41984 --- /dev/null +++ b/arch/arm/mach-msm/wcnss-ssr-8960.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smd_private.h" +#include "ramdump.h" + +#define MODULE_NAME "wcnss_8960" +#define MAX_BUF_SIZE 0x51 + +static void riva_smsm_cb_fn(struct work_struct *); +static DECLARE_WORK(riva_smsm_cb_work, riva_smsm_cb_fn); + +static void riva_fatal_fn(struct work_struct *); +static DECLARE_WORK(riva_fatal_work, riva_fatal_fn); + +static struct delayed_work cancel_vote_work; +static void *riva_ramdump_dev; +static int riva_crash; +static int ss_restart_inprogress; +static int enable_riva_ssr; + +static void riva_smsm_cb_fn(struct work_struct *work) +{ + if (!enable_riva_ssr) + panic(MODULE_NAME ": SMSM reset request received from Riva"); + else + subsystem_restart("riva"); +} + +static void smsm_state_cb_hdlr(void *data, uint32_t old_state, + uint32_t new_state) +{ + char *smem_reset_reason; + char buffer[MAX_BUF_SIZE]; + unsigned smem_reset_size; + unsigned size; + + if (!(new_state & SMSM_RESET)) + return; + + riva_crash = true; + pr_err("%s: smsm state changed to smsm reset\n", MODULE_NAME); + + smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0, + &smem_reset_size); + + if (!smem_reset_reason || !smem_reset_size) { + pr_err("%s: wcnss subsystem failure reason: %s\n", __func__, + "(unknown, smem_get_entry failed)"); + } else if (!smem_reset_reason[0]) { + pr_err("%s: wcnss subsystem failure reason: %s\n", __func__, + "(unknown, init string found)"); + } else { + size = smem_reset_size < MAX_BUF_SIZE ? smem_reset_size : + (MAX_BUF_SIZE - 1); + memcpy(buffer, smem_reset_reason, size); + buffer[size] = '\0'; + pr_err("%s: wcnss subsystem failure reason: %s\n", __func__, + buffer); + memset(smem_reset_reason, 0, smem_reset_size); + wmb(); + } + + if (ss_restart_inprogress) { + pr_err("%s: Ignoring smsm reset req, restart in progress\n", + MODULE_NAME); + return; + } + ss_restart_inprogress = true; + schedule_work(&riva_smsm_cb_work); +} + +static void riva_fatal_fn(struct work_struct *work) +{ + if (!enable_riva_ssr) + panic(MODULE_NAME ": Watchdog bite received from Riva"); + else + subsystem_restart("riva"); +} + +static irqreturn_t riva_wdog_bite_irq_hdlr(int irq, void *dev_id) +{ + int ret; + + if (ss_restart_inprogress) { + pr_err("%s: Ignoring riva bite irq, restart in progress\n", + MODULE_NAME); + return IRQ_HANDLED; + } + ss_restart_inprogress = true; + ret = schedule_work(&riva_fatal_work); + return IRQ_HANDLED; +} + +/* SMSM reset Riva */ +static void smsm_riva_reset(void) +{ + /* per SS reset request bit is not available now, + * all SS host modules are setting this bit + * This is still under discussion*/ + smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET); +} + +static void riva_post_bootup(struct work_struct *work) +{ + struct platform_device *pdev = wcnss_get_platform_device(); + struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config(); + + pr_debug(MODULE_NAME ": Cancel APPS vote for Iris & Riva\n"); + + wcnss_wlan_power(&pdev->dev, pwlanconfig, + WCNSS_WLAN_SWITCH_OFF); +} + +/* Subsystem handlers */ +static int riva_shutdown(const struct subsys_data *subsys) +{ + pil_force_shutdown("wcnss"); + flush_delayed_work(&cancel_vote_work); + disable_irq_nosync(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ); + + return 0; +} + +static int riva_powerup(const struct subsys_data *subsys) +{ + struct platform_device *pdev = wcnss_get_platform_device(); + struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config(); + int ret = -1; + + if (pdev && pwlanconfig) + ret = wcnss_wlan_power(&pdev->dev, pwlanconfig, + WCNSS_WLAN_SWITCH_ON); + /* delay PIL operation, this SSR may be happening soon after kernel + * resumes because of a SMSM RESET by Riva when APPS was suspended. + * PIL fails to locate the images without this delay */ + if (!ret) { + msleep(1000); + pil_force_boot("wcnss"); + } + ss_restart_inprogress = false; + enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ); + schedule_delayed_work(&cancel_vote_work, msecs_to_jiffies(5000)); + + return ret; +} + +/* RAM segments for Riva SS; + * We don't specify the full 5MB allocated for Riva. Only 3MB is specified */ +static struct ramdump_segment riva_segments[] = {{0x8f200000, + 0x8f500000 - 0x8f200000} }; + +static int riva_ramdump(int enable, const struct subsys_data *subsys) +{ + pr_debug("%s: enable[%d]\n", MODULE_NAME, enable); + if (enable) + return do_ramdump(riva_ramdump_dev, + riva_segments, + ARRAY_SIZE(riva_segments)); + else + return 0; +} + +/* Riva crash handler */ +static void riva_crash_shutdown(const struct subsys_data *subsys) +{ + pr_err("%s: crash shutdown : %d\n", MODULE_NAME, riva_crash); + if (riva_crash != true) + smsm_riva_reset(); +} + +static struct subsys_data riva_8960 = { + .name = "riva", + .shutdown = riva_shutdown, + .powerup = riva_powerup, + .ramdump = riva_ramdump, + .crash_shutdown = riva_crash_shutdown +}; + +static int enable_riva_ssr_set(const char *val, struct kernel_param *kp) +{ + int ret; + + ret = param_set_int(val, kp); + if (ret) + return ret; + + if (enable_riva_ssr) + pr_info(MODULE_NAME ": Subsystem restart activated for riva.\n"); + + return 0; +} + +module_param_call(enable_riva_ssr, enable_riva_ssr_set, param_get_int, + &enable_riva_ssr, S_IRUGO | S_IWUSR); + +static int __init riva_restart_init(void) +{ + return ssr_register_subsystem(&riva_8960); +} + +static int __init riva_ssr_module_init(void) +{ + int ret; + + ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET, + smsm_state_cb_hdlr, 0); + if (ret < 0) { + pr_err("%s: Unable to register smsm callback for Riva Reset!" + " (%d)\n", MODULE_NAME, ret); + goto out; + } + ret = request_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ, + riva_wdog_bite_irq_hdlr, IRQF_TRIGGER_HIGH, + "riva_wdog", NULL); + + if (ret < 0) { + pr_err("%s: Unable to register for Riva bite interrupt" + " (%d)\n", MODULE_NAME, ret); + goto out; + } + ret = riva_restart_init(); + if (ret < 0) { + pr_err("%s: Unable to register with ssr. (%d)\n", + MODULE_NAME, ret); + goto out; + } + riva_ramdump_dev = create_ramdump_device("riva"); + if (!riva_ramdump_dev) { + pr_err("%s: Unable to create ramdump device.\n", + MODULE_NAME); + ret = -ENOMEM; + goto out; + } + INIT_DELAYED_WORK(&cancel_vote_work, riva_post_bootup); + + pr_info("%s: module initialized\n", MODULE_NAME); +out: + return ret; +} + +static void __exit riva_ssr_module_exit(void) +{ + free_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ, NULL); +} + +module_init(riva_ssr_module_init); +module_exit(riva_ssr_module_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/xo-fsm9xxx.c b/arch/arm/mach-msm/xo-fsm9xxx.c new file mode 100644 index 00000000000..cbf3b7afafc --- /dev/null +++ b/arch/arm/mach-msm/xo-fsm9xxx.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FSM_XO_IOC_MAGIC 0x93 +#define FSM_XO_IOC_CLKBUF _IO(FSM_XO_IOC_MAGIC, 1) + +#define FSM_XO_DEVICE_READY 0x01 +#define FSM_XO_DEVICE_OFF 0x00 + +/* enum for TCXO clock output buffer definition */ +enum clk_buffer_type { + XO_BUFFER_A0 = 0, + XO_BUFFER_A1 = 1, + XO_BUFFER_LAST +}; + +/* + * This user request structure is used to exchange the pmic device data + * requested to user space applications. The pointer to this structure is + * passed to the the ioctl function. +*/ +struct fsm_xo_req { + enum clk_buffer_type clkBuffer; + u8 clkBufEnable; +}; + +struct fsm_xo_priv_t { + struct mutex lock; + struct regulator *a0; + struct regulator *a1; + u8 a0_enabled; + u8 a1_enabled; +}; + +static struct fsm_xo_priv_t *fsm_xo_priv; + +static int fsm_xo_open(struct inode *inode, struct file *filp) +{ + if (fsm_xo_priv == NULL) + return -ENODEV; + + filp->private_data = fsm_xo_priv; + + return 0; +} + +static int fsm_xo_release(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + + return 0; +} + +static inline int fsm_xo_enable_a0(void) +{ + int err = 0; + + if (!fsm_xo_priv->a0_enabled) { + err = regulator_enable(fsm_xo_priv->a0); + if (err != 0) + pr_err("Error = %d enabling xo buffer a0\n", err); + else + fsm_xo_priv->a0_enabled = 1; + } + return err; +} + +static inline int fsm_xo_disable_a0(void) +{ + int err = 0; + + if (fsm_xo_priv->a0_enabled) { + err = regulator_disable(fsm_xo_priv->a0); + if (err != 0) + pr_err("Error = %d disabling xo buffer a0\n", err); + else + fsm_xo_priv->a0_enabled = 0; + } + return err; +} + +static inline int fsm_xo_enable_a1(void) +{ + int err = 0; + + if (!fsm_xo_priv->a1_enabled) { + err = regulator_enable(fsm_xo_priv->a1); + if (err != 0) + pr_err("Error = %d enabling xo buffer a1\n", err); + else + fsm_xo_priv->a1_enabled = 1; + } + return err; +} + +static inline int fsm_xo_disable_a1(void) +{ + int err = 0; + + if (fsm_xo_priv->a1_enabled) { + err = regulator_disable(fsm_xo_priv->a1); + if (err != 0) + pr_err("Error = %d disabling xo buffer a1\n", err); + else + fsm_xo_priv->a1_enabled = 0; + } + return err; +} +static long +fsm_xo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct fsm_xo_req req; + + /* Verify user arguments. */ + if (_IOC_TYPE(cmd) != FSM_XO_IOC_MAGIC) + return -ENOTTY; + + /* Lock for access */ + if (mutex_lock_interruptible(&fsm_xo_priv->lock)) + return -ERESTARTSYS; + + switch (cmd) { + case FSM_XO_IOC_CLKBUF: + if (arg == 0) { + pr_err("user space arg not supplied\n"); + err = -EFAULT; + break; + } + + if (copy_from_user(&req, (void __user *)arg, + sizeof(req))) { + pr_err("Error copying from user space\n"); + err = -EFAULT; + break; + } + + if (req.clkBuffer == XO_BUFFER_A0) { + if (req.clkBufEnable) + err = fsm_xo_enable_a0(); + else + err = fsm_xo_disable_a0(); + } else if (req.clkBuffer == XO_BUFFER_A1) { + if (req.clkBufEnable) + err = fsm_xo_enable_a1(); + else + err = fsm_xo_disable_a1(); + } else { + pr_err("Invalid ioctl argument.\n"); + err = -ENOTTY; + } + break; + default: + pr_err("Invalid ioctl command.\n"); + err = -ENOTTY; + break; + } + + mutex_unlock(&fsm_xo_priv->lock); + return err; +} + +static const struct file_operations fsm_xo_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fsm_xo_ioctl, + .open = fsm_xo_open, + .release = fsm_xo_release +}; + +static struct miscdevice fsm_xo_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "fsm_xo", + .fops = &fsm_xo_fops +}; + +static int fsm_xo_probe(struct platform_device *pdev) +{ + int ret = 0; + + /* Initialize */ + fsm_xo_priv = kzalloc(sizeof(struct fsm_xo_priv_t), GFP_KERNEL); + + if (fsm_xo_priv == NULL) { + pr_alert("Not enough memory to initialize device\n"); + return -ENOMEM; + } + + fsm_xo_priv->a0 = regulator_get(&pdev->dev, "a0_clk_buffer"); + if (IS_ERR(fsm_xo_priv->a0)) { + pr_err("Error getting a0_clk_buffer\n"); + ret = PTR_ERR(fsm_xo_priv->a0); + fsm_xo_priv->a0 = NULL; + goto err; + } + fsm_xo_priv->a1 = regulator_get(&pdev->dev, "a1_clk_buffer"); + if (IS_ERR(fsm_xo_priv->a1)) { + pr_err("Error getting a1_clk_buffer\n"); + ret = PTR_ERR(fsm_xo_priv->a1); + fsm_xo_priv->a1 = NULL; + goto err; + } + + fsm_xo_priv->a0_enabled = 0; + fsm_xo_priv->a1_enabled = 0; + + /* Enable the clock buffers. AMSS depends on this on the FSM. */ + fsm_xo_enable_a0(); + fsm_xo_enable_a1(); + + mutex_init(&fsm_xo_priv->lock); + + ret = misc_register(&fsm_xo_dev); + if (ret < 0) + goto err; + + return 0; + +err: + if (fsm_xo_priv->a0) + regulator_put(fsm_xo_priv->a0); + if (fsm_xo_priv->a1) + regulator_put(fsm_xo_priv->a1); + + kfree(fsm_xo_priv); + fsm_xo_priv = NULL; + + return ret; +} + +static int __devexit fsm_xo_remove(struct platform_device *pdev) +{ + if (fsm_xo_priv && fsm_xo_priv->a0) + regulator_put(fsm_xo_priv->a0); + if (fsm_xo_priv && fsm_xo_priv->a1) + regulator_put(fsm_xo_priv->a1); + + kfree(fsm_xo_priv); + fsm_xo_priv = NULL; + + misc_deregister(&fsm_xo_dev); + return 0; +} + +static struct platform_driver fsm_xo_driver = { + .probe = fsm_xo_probe, + .remove = fsm_xo_remove, + .driver = { + .name = "fsm_xo_driver", + } +}; + +static int __init fsm_xo_init(void) +{ + return platform_driver_register(&fsm_xo_driver); +} + +static void __exit fsm_xo_exit(void) +{ + platform_driver_unregister(&fsm_xo_driver); +} + +module_init(fsm_xo_init); +module_exit(fsm_xo_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Provide userspace access to XO buffers in PMIC8058."); +MODULE_VERSION("1.00"); diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index f9c9f33f8cb..d283705c0b1 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -468,6 +468,7 @@ msm7x27_surf MACH_MSM7X27_SURF MSM7X27_SURF 2705 msm7x27_ffa MACH_MSM7X27_FFA MSM7X27_FFA 2706 msm7x30_ffa MACH_MSM7X30_FFA MSM7X30_FFA 2707 qsd8x50_surf MACH_QSD8X50_SURF QSD8X50_SURF 2708 +qsd8x50_ffa MACH_QSD8X50_FFA QSD8X50_FFA 2710 mx53_evk MACH_MX53_EVK MX53_EVK 2716 igep0030 MACH_IGEP0030 IGEP0030 2717 sbc3530 MACH_SBC3530 SBC3530 2722 @@ -481,6 +482,8 @@ msm8x60_sim MACH_MSM8X60_SIM MSM8X60_SIM 2756 tcc8000_sdk MACH_TCC8000_SDK TCC8000_SDK 2758 nanos MACH_NANOS NANOS 2759 stamp9g45 MACH_STAMP9G45 STAMP9G45 2761 +msm8x55_surf MACH_MSM8X55_SURF MSM8X55_SURF 2768 +msm8x55_ffa MACH_MSM8X55_FFA MSM8X55_FFA 2769 cns3420vb MACH_CNS3420VB CNS3420VB 2776 omap4_panda MACH_OMAP4_PANDA OMAP4_PANDA 2791 ti8168evm MACH_TI8168EVM TI8168EVM 2800 @@ -498,6 +501,8 @@ vvbox_sdlite2 MACH_VVBOX_SDLITE2 VVBOX_SDLITE2 2858 vvbox_sdpro4 MACH_VVBOX_SDPRO4 VVBOX_SDPRO4 2859 mx257sx MACH_MX257SX MX257SX 2861 goni MACH_GONI GONI 2862 +msm8x55_svlte_ffa MACH_MSM8X55_SVLTE_FFA MSM8X55_SVLTE_FFA 2863 +msm8x55_svlte_surf MACH_MSM8X55_SVLTE_SURF MSM8X55_SVLTE_SURF 2864 bv07 MACH_BV07 BV07 2882 openrd_ultimate MACH_OPENRD_ULTIMATE OPENRD_ULTIMATE 2884 devixp MACH_DEVIXP DEVIXP 2885 @@ -522,15 +527,20 @@ mx53_smd MACH_MX53_SMD MX53_SMD 3011 msm8x60_rumi3 MACH_MSM8X60_RUMI3 MSM8X60_RUMI3 3016 msm8x60_ffa MACH_MSM8X60_FFA MSM8X60_FFA 3017 cm_a510 MACH_CM_A510 CM_A510 3020 +fsm9xxx_surf MACH_FSM9XXX_SURF FSM9XXX_SURF 3028 +fsm9xxx_ffa MACH_FSM9XXX_FFA FSM9XXX_FFA 3029 tx28 MACH_TX28 TX28 3043 pcontrol_g20 MACH_PCONTROL_G20 PCONTROL_G20 3062 vpr200 MACH_VPR200 VPR200 3087 torbreck MACH_TORBRECK TORBRECK 3090 prima2_evb MACH_PRIMA2_EVB PRIMA2_EVB 3103 +msm8x60_fluid MACH_MSM8X60_FLUID MSM8X60_FLUID 3124 paz00 MACH_PAZ00 PAZ00 3128 acmenetusfoxg20 MACH_ACMENETUSFOXG20 ACMENETUSFOXG20 3129 +msm8x60_fusion MACH_MSM8X60_FUSION MSM8X60_FUSION 3181 ag5evm MACH_AG5EVM AG5EVM 3189 tsunagi MACH_TSUNAGI TSUNAGI 3197 +msm8x60_fusn_ffa MACH_MSM8X60_FUSN_FFA MSM8X60_FUSN_FFA 3199 ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206 wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207 trimslice MACH_TRIMSLICE TRIMSLICE 3209 @@ -696,6 +706,7 @@ geneva_b5 MACH_GENEVA_B5 GENEVA_B5 3393 spear1340 MACH_SPEAR1340 SPEAR1340 3394 rexmas MACH_REXMAS REXMAS 3395 msm8960_cdp MACH_MSM8960_CDP MSM8960_CDP 3396 +msm8960_mtp MACH_MSM8960_MTP MSM8960_MTP 3397 msm8960_fluid MACH_MSM8960_FLUID MSM8960_FLUID 3398 msm8960_apq MACH_MSM8960_APQ MSM8960_APQ 3399 helios_v2 MACH_HELIOS_V2 HELIOS_V2 3400 @@ -879,6 +890,7 @@ bct MACH_BCT BCT 3582 tuscan MACH_TUSCAN TUSCAN 3583 xbt_sam9g45 MACH_XBT_SAM9G45 XBT_SAM9G45 3584 enbw_cmc MACH_ENBW_CMC ENBW_CMC 3585 +msm8x60_dragon MACH_MSM8X60_DRAGON MSM8X60_DRAGON 3586 ch104mx257 MACH_CH104MX257 CH104MX257 3587 openpri MACH_OPENPRI OPENPRI 3588 am335xevm MACH_AM335XEVM AM335XEVM 3589 @@ -965,6 +977,7 @@ pia_am35x MACH_PIA_AM35X PIA_AM35X 3671 cedar MACH_CEDAR CEDAR 3672 picasso_e MACH_PICASSO_E PICASSO_E 3673 samsung_e60 MACH_SAMSUNG_E60 SAMSUNG_E60 3674 +msm9615_cdp MACH_MSM9615_CDP MSM9615_CDP 3675 sdvr_mini MACH_SDVR_MINI SDVR_MINI 3676 omap3_ij3k MACH_OMAP3_IJ3K OMAP3_IJ3K 3677 modasmc1 MACH_MODASMC1 MODASMC1 3678 @@ -1038,6 +1051,7 @@ ptip_murnau MACH_PTIP_MURNAU PTIP_MURNAU 3752 ptip_classic MACH_PTIP_CLASSIC PTIP_CLASSIC 3753 mx53grb MACH_MX53GRB MX53GRB 3754 gagarin MACH_GAGARIN GAGARIN 3755 +msm7627a_qrd1 MACH_MSM7627A_QRD1 MSM7627A_QRD1 3756 nas2big MACH_NAS2BIG NAS2BIG 3757 superfemto MACH_SUPERFEMTO SUPERFEMTO 3758 teufel MACH_TEUFEL TEUFEL 3759 @@ -1169,3 +1183,16 @@ elite_ulk MACH_ELITE_ULK ELITE_ULK 3888 pov2 MACH_POV2 POV2 3889 ipod_touch_2g MACH_IPOD_TOUCH_2G IPOD_TOUCH_2G 3890 da850_pqab MACH_DA850_PQAB DA850_PQAB 3891 +msm7627a_evb MACH_MSM7627A_EVB MSM7627A_EVB 3934 +apq8064_cdp MACH_APQ8064_CDP APQ8064_CDP 3948 +apq8064_mtp MACH_APQ8064_MTP APQ8064_MTP 3949 +apq8064_liquid MACH_APQ8064_LIQUID APQ8064_LIQUID 3951 +mpq8064_cdp MACH_MPQ8064_CDP MPQ8064_CDP 3993 +mpq8064_hrd MACH_MPQ8064_HRD MPQ8064_HRD 3994 +mpq8064_dtv MACH_MPQ8064_DTV MPQ8064_DTV 3995 +msm7627a_qrd3 MACH_MSM7627A_QRD3 MSM7627A_QRD3 4005 +msm8625_surf MACH_MSM8625_SURF MSM8625_SURF 4037 +msm8625_evb MACH_MSM8625_EVB MSM8625_EVB 4042 +msm8625_qrd7 MACH_MSM8625_QRD7 MSM8625_QRD7 4095 +msm8625_ffa MACH_MSM8625_FFA MSM8625_FFA 4166 +msm8625_evt MACH_MSM8625_EVT MSM8625_EVT 4193 diff --git a/include/Kbuild b/include/Kbuild index 8d226bfa269..5f65ac2887a 100644 --- a/include/Kbuild +++ b/include/Kbuild @@ -10,3 +10,4 @@ header-y += video/ header-y += drm/ header-y += xen/ header-y += scsi/ +header-y += media/ diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 8dc755408da..1e1e09ff0b9 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -238,6 +238,7 @@ header-y += keyctl.h header-y += l2tp.h header-y += limits.h header-y += llc.h +header-y += l2tp.h header-y += loop.h header-y += lp.h header-y += magic.h @@ -414,11 +415,30 @@ header-y += wireless.h header-y += x25.h header-y += xattr.h header-y += xfrm.h +header-y += msm_adsp.h header-y += msm_mdp.h header-y += msm_kgsl.h +header-y += msm_q6venc.h +header-y += msm_q6vdec.h header-y += msm_rotator.h +header-y += msm_vidc_dec.h +header-y += msm_vidc_enc.h +header-y += msm_audio.h +header-y += msm_audio_aac.h +header-y += msm_audio_acdb.h header-y += android_pmem.h +header-y += msm_audio_wma.h +header-y += msm_audio_wmapro.h +header-y += msm_audio_mvs.h +header-y += msm_audio_qcp.h +header-y += msm_audio_amrnb.h +header-y += msm_audio_voicememo.h +header-y += msm_audio_sbc.h +header-y += msm_ipc.h +header-y += msm_charm.h header-y += tzcom.h header-y += qseecom.h header-y += qcedev.h +header-y += idle_stats_device.h header-y += genlock.h +header-y += msm_audio_amrwb.h diff --git a/include/linux/adv7520.h b/include/linux/adv7520.h new file mode 100644 index 00000000000..96db7b7b91c --- /dev/null +++ b/include/linux/adv7520.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ADV7520_H_ +#define _ADV7520_H_ +#define ADV7520_DRV_NAME "adv7520" + +/* Configure the 20-bit 'N' used with the CTS to +regenerate the audio clock in the receiver +Pixel clock: 74.25 Mhz, Audio sampling: 44.1 Khz -> N +value = 6272 */ +#define ADV7520_AUDIO_CTS_20BIT_N 6272 + +#endif diff --git a/include/linux/fsm_dfe_hh.h b/include/linux/fsm_dfe_hh.h new file mode 100644 index 00000000000..79385186ca2 --- /dev/null +++ b/include/linux/fsm_dfe_hh.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _FSM_DFE_HH_H_ +#define _FSM_DFE_HH_H_ + +#include + +/* + * Device interface + */ + +#define DFE_HH_DEVICE_NAME "dfe_hh" + +/* + * IOCTL interface + */ + +enum { + DFE_IOCTL_COMMAND_CODE_WRITE, + DFE_IOCTL_COMMAND_CODE_WRITE_WITH_MASK, +}; + +struct dfe_write_register_param { + unsigned int offset; + unsigned int value; +}; + +struct dfe_write_register_mask_param { + unsigned int offset; + unsigned int value; + unsigned int mask; +}; + +struct dfe_read_write_array_param { + unsigned int offset; + unsigned int num; /* number of 16 bit registers */ + unsigned int *pArray; +}; + +struct dfe_command_entry { + unsigned int code; + unsigned int offset; + unsigned int value; + unsigned int mask; /* DFE_IOCTL_COMMAND_CODE_WRITE_WITH_MASK only */ +}; + +struct dfe_command_param { + unsigned int num; + struct dfe_command_entry *pEntry; +}; + +#define DFE_IOCTL_MAGIC 'h' +#define DFE_IOCTL_READ_REGISTER \ + _IOC(_IOC_READ, DFE_IOCTL_MAGIC, 0x01, \ + sizeof(unsigned int *)) +#define DFE_IOCTL_WRITE_REGISTER \ + _IOC(_IOC_WRITE, DFE_IOCTL_MAGIC, 0x02, \ + sizeof(struct dfe_write_register_param *)) +#define DFE_IOCTL_WRITE_REGISTER_WITH_MASK \ + _IOC(_IOC_WRITE, DFE_IOCTL_MAGIC, 0x03, \ + sizeof(struct dfe_write_register_mask_param *)) +#define DFE_IOCTL_READ_REGISTER_ARRAY \ + _IOC(_IOC_READ, DFE_IOCTL_MAGIC, 0x04, \ + sizeof(struct dfe_read_write_array_param *)) +#define DFE_IOCTL_WRITE_REGISTER_ARRAY \ + _IOC(_IOC_WRITE, DFE_IOCTL_MAGIC, 0x05, \ + sizeof(struct dfe_read_write_array_param *)) +#define DFE_IOCTL_COMMAND \ + _IOC(_IOC_WRITE, DFE_IOCTL_MAGIC, 0x10, \ + sizeof(struct dfe_command_param *)) + +#endif /* _FSM_DFE_HH_H_ */ diff --git a/include/linux/fsm_rfic_ftr.h b/include/linux/fsm_rfic_ftr.h new file mode 100644 index 00000000000..18b79471199 --- /dev/null +++ b/include/linux/fsm_rfic_ftr.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _FSM_RFIC_FTR_H_ +#define _FSM_RFIC_FTR_H_ + +#include + +/* + * Device interface + */ + +#define RFIC_FTR_DEVICE_NAME "rfic_ftr" + +/* + * IOCTL interface + */ + +/* + Macro to associate the "bus" and "address" pair when accessing the RFIC. + Using a 32 bit address, reserve the upper 8 bits for the bus value, and + the lower 24 bits for the address. + */ +#define RFIC_FTR_ADDR(bus, addr) (((bus&0x03)<<24)|(addr&0xFFFFFF)) +#define RFIC_FTR_GET_ADDR(busAddr) (busAddr&0xFFFFFF) +#define RFIC_FTR_GET_BUS(busAddr) ((busAddr>>24)&0x03) + +struct rfic_write_register_param { + unsigned int rficAddr; + unsigned int value; +}; + +struct rfic_write_register_mask_param { + unsigned int rficAddr; + unsigned int value; + unsigned int mask; +}; + +struct rfic_grfc_param { + unsigned int grfcId; + unsigned int maskValue; + unsigned int ctrlValue; +}; + +#define RFIC_IOCTL_MAGIC 'f' +#define RFIC_IOCTL_READ_REGISTER \ + _IOC(_IOC_READ, RFIC_IOCTL_MAGIC, 0x01, \ + sizeof(unsigned int *)) +#define RFIC_IOCTL_WRITE_REGISTER \ + _IOC(_IOC_WRITE, RFIC_IOCTL_MAGIC, 0x02, \ + sizeof(struct rfic_write_register_param *)) +#define RFIC_IOCTL_WRITE_REGISTER_WITH_MASK \ + _IOC(_IOC_WRITE, RFIC_IOCTL_MAGIC, 0x03, \ + sizeof(struct rfic_write_register_mask_param *)) +#define RFIC_IOCTL_GET_GRFC \ + _IOC(_IOC_WRITE, RFIC_IOCTL_MAGIC, 0x10, \ + sizeof(struct rfic_grfc_param *)) +#define RFIC_IOCTL_SET_GRFC \ + _IOC(_IOC_WRITE, RFIC_IOCTL_MAGIC, 0x11, \ + sizeof(struct rfic_grfc_param *)) + +#endif /* _FSM_RFIC_FTR_H_ */ diff --git a/include/linux/input.h b/include/linux/input.h index 49fb20e35cf..d4cdb02c9de 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -691,7 +691,7 @@ struct input_keymap_entry { #define KEY_NUMERIC_9 0x209 #define KEY_NUMERIC_STAR 0x20a #define KEY_NUMERIC_POUND 0x20b - +#define KEY_CAMERA_SNAPSHOT 0x2fe #define KEY_CAMERA_FOCUS 0x210 #define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ @@ -846,7 +846,10 @@ struct input_keymap_entry { #define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ #define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ #define SW_LINEIN_INSERT 0x0d /* set = inserted */ -#define SW_MAX 0x0f +#define SW_HPHL_OVERCURRENT 0x0e /* set = over current on left hph */ +#define SW_HPHR_OVERCURRENT 0x0f /* set = over current on right hph */ +#define SW_UNSUPPORT_INSERT 0x10 /* set = unsupported device inserted */ +#define SW_MAX 0x20 #define SW_CNT (SW_MAX+1) /* diff --git a/include/linux/input/kp_flip_switch.h b/include/linux/input/kp_flip_switch.h new file mode 100644 index 00000000000..31c0cc4a9e5 --- /dev/null +++ b/include/linux/input/kp_flip_switch.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __KP_FLIP_SWITCH_H_ +#define __KP_FLIP_SWITCH_H_ +/* flip switch driver platform data */ +struct flip_switch_pdata { + int flip_gpio; + int left_key; + int right_key; + int wakeup; + int active_low; + int (*flip_mpp_config) (void); + char name[25]; +}; +#endif diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index c65740d76e6..ed6bb39121f 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -9,178 +9,106 @@ * representation into a hardware irq number that can be mapped back to a * Linux irq number without any extra platform support code. * - * Interrupt controller "domain" data structure. This could be defined as a - * irq domain controller. That is, it handles the mapping between hardware - * and virtual interrupt numbers for a given interrupt domain. The domain - * structure is generally created by the PIC code for a given PIC instance - * (though a domain can cover more than one PIC if they have a flat number - * model). It's the domain callbacks that are responsible for setting the - * irq_chip on a given irq_desc after it's been mapped. - * - * The host code and data structures are agnostic to whether or not - * we use an open firmware device-tree. We do have references to struct - * device_node in two places: in irq_find_host() to find the host matching - * a given interrupt controller node, and of course as an argument to its - * counterpart domain->ops->match() callback. However, those are treated as - * generic pointers by the core and the fact that it's actually a device-node - * pointer is purely a convention between callers and implementation. This - * code could thus be used on other architectures by replacing those two - * by some sort of arch-specific void * "token" used to identify interrupt - * controllers. + * irq_domain is expected to be embedded in an interrupt controller's private + * data structure. */ - #ifndef _LINUX_IRQDOMAIN_H #define _LINUX_IRQDOMAIN_H -#include -#include +#include +#include +#ifdef CONFIG_IRQ_DOMAIN struct device_node; struct irq_domain; -struct of_device_id; - -/* Number of irqs reserved for a legacy isa controller */ -#define NUM_ISA_INTERRUPTS 16 /** * struct irq_domain_ops - Methods for irq_domain objects - * @match: Match an interrupt controller device node to a host, returns - * 1 on a match - * @map: Create or update a mapping between a virtual irq number and a hw - * irq number. This is called only once for a given mapping. - * @unmap: Dispose of such a mapping - * @xlate: Given a device tree node and interrupt specifier, decode - * the hardware irq number and linux irq type value. - * - * Functions below are provided by the driver and called whenever a new mapping - * is created or an old mapping is disposed. The driver can then proceed to - * whatever internal data structures management is required. It also needs - * to setup the irq_desc when returning from map(). + * @to_irq: (optional) given a local hardware irq number, return the linux + * irq number. If to_irq is not implemented, then the irq_domain + * will use this translation: irq = (domain->irq_base + hwirq) + * @dt_translate: Given a device tree node and interrupt specifier, decode + * the hardware irq number and linux irq type value. */ struct irq_domain_ops { - int (*match)(struct irq_domain *d, struct device_node *node); - int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); - void (*unmap)(struct irq_domain *d, unsigned int virq); - int (*xlate)(struct irq_domain *d, struct device_node *node, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, unsigned int *out_type); + unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq); + +#ifdef CONFIG_OF + int (*dt_translate)(struct irq_domain *d, struct device_node *node, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type); +#endif /* CONFIG_OF */ }; /** * struct irq_domain - Hardware interrupt number translation object - * @link: Element in global irq_domain list. - * @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This - * will be one of the IRQ_DOMAIN_MAP_* values. - * @revmap_data: Revmap method specific data. - * @ops: pointer to irq_domain methods - * @host_data: private data pointer for use by owner. Not touched by irq_domain - * core code. + * @list: Element in global irq_domain list. * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator * of the irq_domain is responsible for allocating the array of * irq_desc structures. * @nr_irq: Number of irqs managed by the irq domain * @hwirq_base: Starting number for hwirqs managed by the irq domain + * @ops: pointer to irq_domain methods + * @priv: private data pointer for use by owner. Not touched by irq_domain + * core code. * @of_node: (optional) Pointer to device tree nodes associated with the * irq_domain. Used when decoding device tree interrupt specifiers. */ struct irq_domain { - struct list_head link; - - /* type of reverse mapping_technique */ - unsigned int revmap_type; - union { - struct { - unsigned int size; - unsigned int first_irq; - irq_hw_number_t first_hwirq; - } legacy; - struct { - unsigned int size; - unsigned int *revmap; - } linear; - struct { - unsigned int max_irq; - } nomap; - struct radix_tree_root tree; - } revmap_data; + struct list_head list; + unsigned int irq_base; + unsigned int nr_irq; + unsigned int hwirq_base; const struct irq_domain_ops *ops; - void *host_data; - irq_hw_number_t inval_irq; - - /* Optional device node pointer */ + void *priv; struct device_node *of_node; }; -#ifdef CONFIG_IRQ_DOMAIN -struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, - unsigned int size, - unsigned int first_irq, - irq_hw_number_t first_hwirq, - const struct irq_domain_ops *ops, - void *host_data); -struct irq_domain *irq_domain_add_linear(struct device_node *of_node, - unsigned int size, - const struct irq_domain_ops *ops, - void *host_data); -struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, - unsigned int max_irq, - const struct irq_domain_ops *ops, - void *host_data); -struct irq_domain *irq_domain_add_tree(struct device_node *of_node, - const struct irq_domain_ops *ops, - void *host_data); - -extern struct irq_domain *irq_find_host(struct device_node *node); -extern void irq_set_default_host(struct irq_domain *host); - -static inline struct irq_domain *irq_domain_add_legacy_isa( - struct device_node *of_node, - const struct irq_domain_ops *ops, - void *host_data) +/** + * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number + * + * Returns the linux irq number associated with a hardware irq. By default, + * the mapping is irq == domain->irq_base + hwirq, but this mapping can + * be overridden if the irq_domain implements a .to_irq() hook. + */ +static inline unsigned int irq_domain_to_irq(struct irq_domain *d, + unsigned long hwirq) { - return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops, - host_data); + if (d->ops->to_irq) + return d->ops->to_irq(d, hwirq); + if (WARN_ON(hwirq < d->hwirq_base)) + return 0; + return d->irq_base + hwirq - d->hwirq_base; } -extern struct irq_domain *irq_find_host(struct device_node *node); -extern void irq_set_default_host(struct irq_domain *host); +#define irq_domain_for_each_hwirq(d, hw) \ + for (hw = d->hwirq_base; hw < d->hwirq_base + d->nr_irq; hw++) + +#define irq_domain_for_each_irq(d, hw, irq) \ + for (hw = d->hwirq_base, irq = irq_domain_to_irq(d, hw); \ + hw < d->hwirq_base + d->nr_irq; \ + hw++, irq = irq_domain_to_irq(d, hw)) -extern unsigned int irq_create_mapping(struct irq_domain *host, - irq_hw_number_t hwirq); extern void irq_dispose_mapping(unsigned int virq); -extern unsigned int irq_find_mapping(struct irq_domain *host, - irq_hw_number_t hwirq); -extern unsigned int irq_create_direct_mapping(struct irq_domain *host); -extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, - irq_hw_number_t hwirq); -extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, - irq_hw_number_t hwirq); -extern unsigned int irq_linear_revmap(struct irq_domain *host, - irq_hw_number_t hwirq); -extern const struct irq_domain_ops irq_domain_simple_ops; +extern int irq_domain_add(struct irq_domain *domain); +extern void irq_domain_del(struct irq_domain *domain); +extern void irq_domain_register(struct irq_domain *domain); +extern void irq_domain_register_irq(struct irq_domain *domain, int hwirq); +extern void irq_domain_unregister(struct irq_domain *domain); +extern void irq_domain_unregister_irq(struct irq_domain *domain, int hwirq); +extern int irq_domain_find_free_range(unsigned int from, unsigned int cnt); -/* stock xlate functions */ -int irq_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, unsigned int *out_type); -int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, unsigned int *out_type); -int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, unsigned int *out_type); +extern struct irq_domain_ops irq_domain_simple_ops; +#endif /* CONFIG_IRQ_DOMAIN */ -#if defined(CONFIG_OF_IRQ) +#if defined(CONFIG_IRQ_DOMAIN) && defined(CONFIG_OF_IRQ) +extern void irq_domain_add_simple(struct device_node *controller, int irq_base); extern void irq_domain_generate_simple(const struct of_device_id *match, u64 phys_base, unsigned int irq_start); -#else /* CONFIG_OF_IRQ */ +#else /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */ static inline void irq_domain_generate_simple(const struct of_device_id *match, u64 phys_base, unsigned int irq_start) { } -#endif /* !CONFIG_OF_IRQ */ - -#else /* CONFIG_IRQ_DOMAIN */ -static inline void irq_dispose_mapping(unsigned int virq) { } -#endif /* !CONFIG_IRQ_DOMAIN */ +#endif /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */ #endif /* _LINUX_IRQDOMAIN_H */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 2f2d4e0f6bf..bc124da75e9 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -292,7 +292,7 @@ struct pcmcia_device_id { #define INPUT_DEVICE_ID_LED_MAX 0x0f #define INPUT_DEVICE_ID_SND_MAX 0x07 #define INPUT_DEVICE_ID_FF_MAX 0x7f -#define INPUT_DEVICE_ID_SW_MAX 0x0f +#define INPUT_DEVICE_ID_SW_MAX 0x20 #define INPUT_DEVICE_ID_MATCH_BUS 1 #define INPUT_DEVICE_ID_MATCH_VENDOR 2 diff --git a/include/linux/msm_adsp.h b/include/linux/msm_adsp.h new file mode 100644 index 00000000000..ca23ad8dd74 --- /dev/null +++ b/include/linux/msm_adsp.h @@ -0,0 +1,78 @@ +/* include/linux/msm_adsp.h + * + * Copyright (C) 2007 Google, Inc. + * Author: Iliyan Malchev + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_MSM_ADSP_H +#define __LINUX_MSM_ADSP_H + +#include +#include + +#define ADSP_IOCTL_MAGIC 'q' + +/* ADSP_IOCTL_WRITE_COMMAND */ +struct adsp_command_t { + uint16_t queue; + uint32_t len; /* bytes */ + uint8_t *data; +}; + +/* ADSP_IOCTL_GET_EVENT */ +struct adsp_event_t { + uint16_t type; /* 1 == event (RPC), 0 == message (adsp) */ + uint32_t timeout_ms; /* -1 for infinite, 0 for immediate return */ + uint16_t msg_id; + uint16_t flags; /* 1 == 16--bit event, 0 == 32-bit event */ + uint32_t len; /* size in, number of bytes out */ + uint8_t *data; +}; + +#define ADSP_IOCTL_ENABLE \ + _IOR(ADSP_IOCTL_MAGIC, 1, unsigned) + +#define ADSP_IOCTL_DISABLE \ + _IOR(ADSP_IOCTL_MAGIC, 2, unsigned) + +#define ADSP_IOCTL_DISABLE_ACK \ + _IOR(ADSP_IOCTL_MAGIC, 3, unsigned) + +#define ADSP_IOCTL_WRITE_COMMAND \ + _IOR(ADSP_IOCTL_MAGIC, 4, struct adsp_command_t *) + +#define ADSP_IOCTL_GET_EVENT \ + _IOWR(ADSP_IOCTL_MAGIC, 5, struct adsp_event_data_t *) + +#define ADSP_IOCTL_SET_CLKRATE \ + _IOR(ADSP_IOCTL_MAGIC, 6, unsigned) + +#define ADSP_IOCTL_DISABLE_EVENT_RSP \ + _IOR(ADSP_IOCTL_MAGIC, 10, unsigned) + +#define ADSP_IOCTL_REGISTER_PMEM \ + _IOW(ADSP_IOCTL_MAGIC, 13, unsigned) + +#define ADSP_IOCTL_UNREGISTER_PMEM \ + _IOW(ADSP_IOCTL_MAGIC, 14, unsigned) + +/* Cause any further GET_EVENT ioctls to fail (-ENODEV) + * until the device is closed and reopened. Useful for + * terminating event dispatch threads + */ +#define ADSP_IOCTL_ABORT_EVENT_READ \ + _IOW(ADSP_IOCTL_MAGIC, 15, unsigned) + +#define ADSP_IOCTL_LINK_TASK \ + _IOW(ADSP_IOCTL_MAGIC, 16, unsigned) + +#endif diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h new file mode 100644 index 00000000000..f2a39e46677 --- /dev/null +++ b/include/linux/msm_audio.h @@ -0,0 +1,367 @@ +/* include/linux/msm_audio.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2012 Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_MSM_AUDIO_H +#define __LINUX_MSM_AUDIO_H + +#include +#include + +/* PCM Audio */ + +#define AUDIO_IOCTL_MAGIC 'a' + +#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned) +#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned) +#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned) +#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned) +#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned) +#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned) +#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned) +#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned) +#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned) +#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned) +#define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned) +#define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned) +#define AUDIO_PLAY_DTMF _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned) +#define AUDIO_GET_EVENT _IOR(AUDIO_IOCTL_MAGIC, 13, unsigned) +#define AUDIO_ABORT_GET_EVENT _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned) +#define AUDIO_REGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned) +#define AUDIO_DEREGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 16, unsigned) +#define AUDIO_ASYNC_WRITE _IOW(AUDIO_IOCTL_MAGIC, 17, unsigned) +#define AUDIO_ASYNC_READ _IOW(AUDIO_IOCTL_MAGIC, 18, unsigned) +#define AUDIO_SET_INCALL _IOW(AUDIO_IOCTL_MAGIC, 19, struct msm_voicerec_mode) +#define AUDIO_GET_NUM_SND_DEVICE _IOR(AUDIO_IOCTL_MAGIC, 20, unsigned) +#define AUDIO_GET_SND_DEVICES _IOWR(AUDIO_IOCTL_MAGIC, 21, \ + struct msm_snd_device_list) +#define AUDIO_ENABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 22, unsigned) +#define AUDIO_DISABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 23, unsigned) +#define AUDIO_ROUTE_STREAM _IOW(AUDIO_IOCTL_MAGIC, 24, \ + struct msm_audio_route_config) +#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned) +#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned) +#define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned) +#define AUDIO_SET_MUTE _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned) +#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned) +#define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned) +#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned) +#define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned) +#define AUDIO_OUTPORT_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 40, unsigned short) +#define AUDIO_SET_ERR_THRESHOLD_VALUE _IOW(AUDIO_IOCTL_MAGIC, 41, \ + unsigned short) +#define AUDIO_GET_BITSTREAM_ERROR_INFO _IOR(AUDIO_IOCTL_MAGIC, 42, \ + struct msm_audio_bitstream_error_info) + +#define AUDIO_SET_SRS_TRUMEDIA_PARAM _IOW(AUDIO_IOCTL_MAGIC, 43, unsigned) + +/* Qualcomm extensions */ +#define AUDIO_SET_STREAM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 80, \ + struct msm_audio_stream_config) +#define AUDIO_GET_STREAM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 81, \ + struct msm_audio_stream_config) +#define AUDIO_GET_SESSION_ID _IOR(AUDIO_IOCTL_MAGIC, 82, unsigned short) +#define AUDIO_GET_STREAM_INFO _IOR(AUDIO_IOCTL_MAGIC, 83, \ + struct msm_audio_bitstream_info) +#define AUDIO_SET_PAN _IOW(AUDIO_IOCTL_MAGIC, 84, unsigned) +#define AUDIO_SET_QCONCERT_PLUS _IOW(AUDIO_IOCTL_MAGIC, 85, unsigned) +#define AUDIO_SET_MBADRC _IOW(AUDIO_IOCTL_MAGIC, 86, unsigned) +#define AUDIO_SET_VOLUME_PATH _IOW(AUDIO_IOCTL_MAGIC, 87, \ + struct msm_vol_info) +#define AUDIO_SET_MAX_VOL_ALL _IOW(AUDIO_IOCTL_MAGIC, 88, unsigned) +#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 89, unsigned) +#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 90, unsigned) +#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 91, unsigned) +#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 92, unsigned) +#define AUDIO_GET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 93, \ + struct msm_audio_buf_cfg) +#define AUDIO_SET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 94, \ + struct msm_audio_buf_cfg) +#define AUDIO_SET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 95, \ + struct msm_acdb_cmd_device) +#define AUDIO_GET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 96, \ + struct msm_acdb_cmd_device) + +#define AUDIO_REGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 97, unsigned) +#define AUDIO_DEREGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 98, unsigned) + +#define AUDIO_MAX_COMMON_IOCTL_NUM 100 + + +#define HANDSET_MIC 0x01 +#define HANDSET_SPKR 0x02 +#define HEADSET_MIC 0x03 +#define HEADSET_SPKR_MONO 0x04 +#define HEADSET_SPKR_STEREO 0x05 +#define SPKR_PHONE_MIC 0x06 +#define SPKR_PHONE_MONO 0x07 +#define SPKR_PHONE_STEREO 0x08 +#define BT_SCO_MIC 0x09 +#define BT_SCO_SPKR 0x0A +#define BT_A2DP_SPKR 0x0B +#define TTY_HEADSET_MIC 0x0C +#define TTY_HEADSET_SPKR 0x0D + +/* Default devices are not supported in a */ +/* device switching context. Only supported */ +/* for stream devices. */ +/* DO NOT USE */ +#define DEFAULT_TX 0x0E +#define DEFAULT_RX 0x0F + +#define BT_A2DP_TX 0x10 + +#define HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12 +#define HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13 +#define HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 + +#define I2S_RX 0x20 +#define I2S_TX 0x21 + +#define ADRC_ENABLE 0x0001 +#define EQ_ENABLE 0x0002 +#define IIR_ENABLE 0x0004 +#define QCONCERT_PLUS_ENABLE 0x0008 +#define MBADRC_ENABLE 0x0010 +#define SRS_ENABLE 0x0020 +#define SRS_DISABLE 0x0040 + +#define AGC_ENABLE 0x0001 +#define NS_ENABLE 0x0002 +#define TX_IIR_ENABLE 0x0004 +#define FLUENCE_ENABLE 0x0008 + +#define VOC_REC_UPLINK 0x00 +#define VOC_REC_DOWNLINK 0x01 +#define VOC_REC_BOTH 0x02 + +struct msm_audio_config { + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t channel_count; + uint32_t sample_rate; + uint32_t type; + uint32_t meta_field; + uint32_t bits; + uint32_t unused[3]; +}; + +struct msm_audio_stream_config { + uint32_t buffer_size; + uint32_t buffer_count; +}; + +struct msm_audio_buf_cfg{ + uint32_t meta_info_enable; + uint32_t frames_per_buf; +}; + +struct msm_audio_stats { + uint32_t byte_count; + uint32_t sample_count; + uint32_t unused[2]; +}; + +struct msm_audio_ion_info { + int fd; + void *vaddr; +}; + +struct msm_audio_pmem_info { + int fd; + void *vaddr; +}; + +struct msm_audio_aio_buf { + void *buf_addr; + uint32_t buf_len; + uint32_t data_len; + void *private_data; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +/* Audio routing */ + +#define SND_IOCTL_MAGIC 's' + +#define SND_MUTE_UNMUTED 0 +#define SND_MUTE_MUTED 1 + +struct msm_mute_info { + uint32_t mute; + uint32_t path; +}; + +struct msm_vol_info { + uint32_t vol; + uint32_t path; +}; + +struct msm_voicerec_mode { + uint32_t rec_mode; +}; + +struct msm_snd_device_config { + uint32_t device; + uint32_t ear_mute; + uint32_t mic_mute; +}; + +#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *) + +#define SND_METHOD_VOICE 0 + +struct msm_snd_volume_config { + uint32_t device; + uint32_t method; + uint32_t volume; +}; + +#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *) + +/* Returns the number of SND endpoints supported. */ + +#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned *) + +struct msm_snd_endpoint { + int id; /* input and output */ + char name[64]; /* output only */ +}; + +/* Takes an index between 0 and one less than the number returned by + * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a + * SND endpoint. On input, the .id field contains the number of the + * endpoint, and on exit it contains the SND index, while .name contains + * the description of the endpoint. + */ + +#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *) + + +#define SND_AVC_CTL _IOW(SND_IOCTL_MAGIC, 6, unsigned *) +#define SND_AGC_CTL _IOW(SND_IOCTL_MAGIC, 7, unsigned *) + +struct msm_audio_pcm_config { + uint32_t pcm_feedback; /* 0 - disable > 0 - enable */ + uint32_t buffer_count; /* Number of buffers to allocate */ + uint32_t buffer_size; /* Size of buffer for capturing of + PCM samples */ +}; + +#define AUDIO_EVENT_SUSPEND 0 +#define AUDIO_EVENT_RESUME 1 +#define AUDIO_EVENT_WRITE_DONE 2 +#define AUDIO_EVENT_READ_DONE 3 +#define AUDIO_EVENT_STREAM_INFO 4 +#define AUDIO_EVENT_BITSTREAM_ERROR_INFO 5 + +#define AUDIO_CODEC_TYPE_MP3 0 +#define AUDIO_CODEC_TYPE_AAC 1 + +struct msm_audio_bitstream_info { + uint32_t codec_type; + uint32_t chan_info; + uint32_t sample_rate; + uint32_t bit_stream_info; + uint32_t bit_rate; + uint32_t unused[3]; +}; + +struct msm_audio_bitstream_error_info { + uint32_t dec_id; + uint32_t err_msg_indicator; + uint32_t err_type; +}; + +union msm_audio_event_payload { + struct msm_audio_aio_buf aio_buf; + struct msm_audio_bitstream_info stream_info; + struct msm_audio_bitstream_error_info error_info; + int reserved; +}; + +struct msm_audio_event { + int event_type; + int timeout_ms; + union msm_audio_event_payload event_payload; +}; + +#define MSM_SNDDEV_CAP_RX 0x1 +#define MSM_SNDDEV_CAP_TX 0x2 +#define MSM_SNDDEV_CAP_VOICE 0x4 + +struct msm_snd_device_info { + uint32_t dev_id; + uint32_t dev_cap; /* bitmask describe capability of device */ + char dev_name[64]; +}; + +struct msm_snd_device_list { + uint32_t num_dev; /* Indicate number of device info to be retrieved */ + struct msm_snd_device_info *list; +}; + +struct msm_dtmf_config { + uint16_t path; + uint16_t dtmf_hi; + uint16_t dtmf_low; + uint16_t duration; + uint16_t tx_gain; + uint16_t rx_gain; + uint16_t mixing; +}; + +#define AUDIO_ROUTE_STREAM_VOICE_RX 0 +#define AUDIO_ROUTE_STREAM_VOICE_TX 1 +#define AUDIO_ROUTE_STREAM_PLAYBACK 2 +#define AUDIO_ROUTE_STREAM_REC 3 + +struct msm_audio_route_config { + uint32_t stream_type; + uint32_t stream_id; + uint32_t dev_id; +}; + +#define AUDIO_MAX_EQ_BANDS 12 + +struct msm_audio_eq_band { + uint16_t band_idx; /* The band index, 0 .. 11 */ + uint32_t filter_type; /* Filter band type */ + uint32_t center_freq_hz; /* Filter band center frequency */ + uint32_t filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + uint32_t q_factor; +} __attribute__ ((packed)); + +struct msm_audio_eq_stream_config { + uint32_t enable; /* Number of consequtive bands specified */ + uint32_t num_bands; + struct msm_audio_eq_band eq_bands[AUDIO_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +struct msm_acdb_cmd_device { + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; /* Actual sample rate value */ + uint32_t interface_id; /* See interface id's above */ + uint32_t algorithm_block_id; /* See enumerations above */ + uint32_t total_bytes; /* Length in bytes used by buffer */ + uint32_t *phys_buf; /* Physical Address of data */ +}; + + +#endif diff --git a/include/linux/msm_audio_aac.h b/include/linux/msm_audio_aac.h new file mode 100644 index 00000000000..620e5abfc8d --- /dev/null +++ b/include/linux/msm_audio_aac.h @@ -0,0 +1,72 @@ +#ifndef __MSM_AUDIO_AAC_H +#define __MSM_AUDIO_AAC_H + +#include + +#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) + +#define AUDIO_SET_AAC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config) + +#define AUDIO_GET_AAC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config) + +#define AUDIO_AAC_FORMAT_ADTS -1 +#define AUDIO_AAC_FORMAT_RAW 0x0000 +#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDIO_AAC_FORMAT_LOAS 0x0002 +#define AUDIO_AAC_FORMAT_ADIF 0x0003 + +#define AUDIO_AAC_OBJECT_LC 0x0002 +#define AUDIO_AAC_OBJECT_LTP 0x0004 +#define AUDIO_AAC_OBJECT_ERLC 0x0011 +#define AUDIO_AAC_OBJECT_BSAC 0x0016 + +#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +/* Primary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_PL_PR 0 +/* Secondary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_SL_SR 1 +/* Primary channel on right channel and 2nd on left channel */ +#define AUDIO_AAC_DUAL_MONO_SL_PR 2 +/* 2nd channel on right channel and primary on left channel */ +#define AUDIO_AAC_DUAL_MONO_PL_SR 3 + +struct msm_audio_aac_config { + signed short format; + unsigned short audio_object; + unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */ + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; +}; + +struct msm_audio_aac_enc_config { + uint32_t channels; + uint32_t sample_rate; + uint32_t bit_rate; + uint32_t stream_format; +}; + +#endif /* __MSM_AUDIO_AAC_H */ diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h new file mode 100644 index 00000000000..e7f06b53ac8 --- /dev/null +++ b/include/linux/msm_audio_acdb.h @@ -0,0 +1,81 @@ +#ifndef __MSM_AUDIO_ACDB_H +#define __MSM_AUDIO_ACDB_H + +#include + +#define AUDIO_SET_VOCPROC_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_SET_VOCPROC_STREAM_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) +#define AUDIO_SET_VOCPROC_VOL_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), unsigned) +#define AUDIO_SET_AUDPROC_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), unsigned) +#define AUDIO_SET_AUDPROC_RX_STREAM_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+4), unsigned) +#define AUDIO_SET_AUDPROC_RX_VOL_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+5), unsigned) +#define AUDIO_SET_AUDPROC_TX_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+6), unsigned) +#define AUDIO_SET_AUDPROC_TX_STREAM_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+7), unsigned) +#define AUDIO_SET_AUDPROC_TX_VOL_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+8), unsigned) +#define AUDIO_SET_SIDETONE_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+9), unsigned) +#define AUDIO_SET_ANC_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+10), unsigned) +#define AUDIO_SET_VOICE_RX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+11), unsigned) +#define AUDIO_SET_VOICE_TX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+12), unsigned) +#define AUDIO_SET_ADM_RX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+13), unsigned) +#define AUDIO_SET_ADM_TX_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+14), unsigned) +#define AUDIO_SET_ASM_TOPOLOGY _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+15), unsigned) +#define AUDIO_SET_AFE_TX_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+16), unsigned) +#define AUDIO_SET_AFE_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+17), unsigned) + + +#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30) + +/* ACDB structures */ +struct cal_block { + uint32_t cal_size; /* Size of Cal Data */ + uint32_t cal_offset; /* offset pointer to Cal Data */ +}; + +struct sidetone_cal { + uint16_t enable; + uint16_t gain; +}; + +/* For Real-Time Audio Calibration */ +#define AUDIO_GET_RTAC_ADM_INFO _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+1), unsigned) +#define AUDIO_GET_RTAC_VOICE_INFO _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+2), unsigned) +#define AUDIO_GET_RTAC_ADM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+3), unsigned) +#define AUDIO_SET_RTAC_ADM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+4), unsigned) +#define AUDIO_GET_RTAC_ASM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+5), unsigned) +#define AUDIO_SET_RTAC_ASM_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+6), unsigned) +#define AUDIO_GET_RTAC_CVS_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+7), unsigned) +#define AUDIO_SET_RTAC_CVS_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+8), unsigned) +#define AUDIO_GET_RTAC_CVP_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+9), unsigned) +#define AUDIO_SET_RTAC_CVP_CAL _IOWR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_ACDB_IOCTL+10), unsigned) + +#define AUDIO_MAX_RTAC_IOCTL (AUDIO_MAX_ACDB_IOCTL+20) + +#endif /* __MSM_AUDIO_ACDB_H */ diff --git a/include/linux/msm_audio_amrnb.h b/include/linux/msm_audio_amrnb.h new file mode 100644 index 00000000000..77a1258f18b --- /dev/null +++ b/include/linux/msm_audio_amrnb.h @@ -0,0 +1,33 @@ +#ifndef __MSM_AUDIO_AMRNB_H +#define __MSM_AUDIO_AMRNB_H + +#include + +#define AUDIO_GET_AMRNB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_SET_AMRNB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) +#define AUDIO_GET_AMRNB_ENC_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), \ + struct msm_audio_amrnb_enc_config_v2) +#define AUDIO_SET_AMRNB_ENC_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), \ + struct msm_audio_amrnb_enc_config_v2) + +struct msm_audio_amrnb_enc_config { + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short dtx_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short test_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short enc_mode; /* 0-MR475,1-MR515,2-MR59,3-MR67,4-MR74 + 5-MR795, 6- MR102, 7- MR122(default) */ +}; + +struct msm_audio_amrnb_enc_config_v2 { + uint32_t band_mode; + uint32_t dtx_enable; + uint32_t frame_format; +}; +#endif /* __MSM_AUDIO_AMRNB_H */ diff --git a/include/linux/msm_audio_amrwb.h b/include/linux/msm_audio_amrwb.h new file mode 100644 index 00000000000..23837433cfa --- /dev/null +++ b/include/linux/msm_audio_amrwb.h @@ -0,0 +1,18 @@ +#ifndef __MSM_AUDIO_AMRWB_H +#define __MSM_AUDIO_AMRWB_H + +#include + +#define AUDIO_GET_AMRWB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), \ + struct msm_audio_amrwb_enc_config) +#define AUDIO_SET_AMRWB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), \ + struct msm_audio_amrwb_enc_config) + +struct msm_audio_amrwb_enc_config { + uint32_t band_mode; + uint32_t dtx_enable; + uint32_t frame_format; +}; +#endif /* __MSM_AUDIO_AMRWB_H */ diff --git a/include/linux/msm_audio_mvs.h b/include/linux/msm_audio_mvs.h new file mode 100644 index 00000000000..1807cb044f2 --- /dev/null +++ b/include/linux/msm_audio_mvs.h @@ -0,0 +1,144 @@ +#ifndef __MSM_AUDIO_MVS_H +#define __MSM_AUDIO_MVS_H + +#include + +#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned) +#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned) + +/* MVS modes */ +#define MVS_MODE_IS733 0x1 +#define MVS_MODE_IS127 0x2 +#define MVS_MODE_4GV_NB 0x3 +#define MVS_MODE_4GV_WB 0x4 +#define MVS_MODE_AMR 0x5 +#define MVS_MODE_EFR 0x6 +#define MVS_MODE_FR 0x7 +#define MVS_MODE_HR 0x8 +#define MVS_MODE_LINEAR_PCM 0x9 +#define MVS_MODE_G711 0xA +#define MVS_MODE_PCM 0xC +#define MVS_MODE_AMR_WB 0xD +#define MVS_MODE_G729A 0xE +#define MVS_MODE_G711A 0xF +#define MVS_MODE_G722 0x10 +#define MVS_MODE_PCM_WB 0x12 + +enum msm_audio_amr_mode { + MVS_AMR_MODE_0475, /* AMR 4.75 kbps */ + MVS_AMR_MODE_0515, /* AMR 5.15 kbps */ + MVS_AMR_MODE_0590, /* AMR 5.90 kbps */ + MVS_AMR_MODE_0670, /* AMR 6.70 kbps */ + MVS_AMR_MODE_0740, /* AMR 7.40 kbps */ + MVS_AMR_MODE_0795, /* AMR 7.95 kbps */ + MVS_AMR_MODE_1020, /* AMR 10.20 kbps */ + MVS_AMR_MODE_1220, /* AMR 12.20 kbps */ + MVS_AMR_MODE_0660, /* AMR-WB 6.60 kbps */ + MVS_AMR_MODE_0885, /* AMR-WB 8.85 kbps */ + MVS_AMR_MODE_1265, /* AMR-WB 12.65 kbps */ + MVS_AMR_MODE_1425, /* AMR-WB 14.25 kbps */ + MVS_AMR_MODE_1585, /* AMR-WB 15.85 kbps */ + MVS_AMR_MODE_1825, /* AMR-WB 18.25 kbps */ + MVS_AMR_MODE_1985, /* AMR-WB 19.85 kbps */ + MVS_AMR_MODE_2305, /* AMR-WB 23.05 kbps */ + MVS_AMR_MODE_2385, /* AMR-WB 23.85 kbps */ + MVS_AMR_MODE_UNDEF +}; + +enum msm_audio_voc_rate { + MVS_VOC_0_RATE, /* Blank frame */ + MVS_VOC_8_RATE, /* 1/8 rate */ + MVS_VOC_4_RATE, /* 1/4 rate */ + MVS_VOC_2_RATE, /* 1/2 rate */ + MVS_VOC_1_RATE /* Full rate */ +}; + +enum msm_audio_amr_frame_type { + MVS_AMR_SPEECH_GOOD, /* Good speech frame */ + MVS_AMR_SPEECH_DEGRADED, /* Speech degraded */ + MVS_AMR_ONSET, /* Onset */ + MVS_AMR_SPEECH_BAD, /* Corrupt speech frame (bad CRC) */ + MVS_AMR_SID_FIRST, /* First silence descriptor */ + MVS_AMR_SID_UPDATE, /* Comfort noise frame */ + MVS_AMR_SID_BAD, /* Corrupt SID frame (bad CRC) */ + MVS_AMR_NO_DATA, /* Nothing to transmit */ + MVS_AMR_SPEECH_LOST /* Downlink speech lost */ +}; + +enum msm_audio_g711a_mode { + MVS_G711A_MODE_MULAW, + MVS_G711A_MODE_ALAW +}; + +enum mvs_g722_mode_type { + MVS_G722_MODE_01, + MVS_G722_MODE_02, + MVS_G722_MODE_03, + MVS_G722_MODE_MAX, + MVS_G722_MODE_UNDEF +}; + +enum msm_audio_g711a_frame_type { + MVS_G711A_SPEECH_GOOD, + MVS_G711A_SID, + MVS_G711A_NO_DATA, + MVS_G711A_ERASURE +}; + +enum msm_audio_g729a_frame_type { + MVS_G729A_NO_DATA, + MVS_G729A_SPEECH_GOOD, + MVS_G729A_SID, + MVS_G729A_ERASURE +}; + +struct min_max_rate { + uint32_t min_rate; + uint32_t max_rate; +}; + +struct msm_audio_mvs_config { + uint32_t mvs_mode; + uint32_t rate_type; + struct min_max_rate min_max_rate; + uint32_t dtx_mode; +}; + +#define MVS_MAX_VOC_PKT_SIZE 640 + +struct gsm_header { + uint8_t bfi; + uint8_t sid; + uint8_t taf; + uint8_t ufi; +}; + +struct q6_msm_audio_mvs_frame { + union { + uint32_t frame_type; + uint32_t packet_rate; + struct gsm_header gsm_frame_type; + } header; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +struct msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +#define Q5V2_MVS_MAX_VOC_PKT_SIZE 320 + +struct q5v2_msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t len; + uint8_t voc_pkt[Q5V2_MVS_MAX_VOC_PKT_SIZE]; + +}; +#endif /* __MSM_AUDIO_MVS_H */ diff --git a/include/linux/msm_audio_qcp.h b/include/linux/msm_audio_qcp.h new file mode 100644 index 00000000000..6e0c390986c --- /dev/null +++ b/include/linux/msm_audio_qcp.h @@ -0,0 +1,37 @@ +#ifndef __MSM_AUDIO_QCP_H +#define __MSM_AUDIO_QCP_H + +#include + +#define AUDIO_SET_QCELP_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + 0, struct msm_audio_qcelp_enc_config) + +#define AUDIO_GET_QCELP_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + 1, struct msm_audio_qcelp_enc_config) + +#define AUDIO_SET_EVRC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + 2, struct msm_audio_evrc_enc_config) + +#define AUDIO_GET_EVRC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + 3, struct msm_audio_evrc_enc_config) + +#define CDMA_RATE_BLANK 0x00 +#define CDMA_RATE_EIGHTH 0x01 +#define CDMA_RATE_QUARTER 0x02 +#define CDMA_RATE_HALF 0x03 +#define CDMA_RATE_FULL 0x04 +#define CDMA_RATE_ERASURE 0x05 + +struct msm_audio_qcelp_enc_config { + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +struct msm_audio_evrc_enc_config { + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +#endif /* __MSM_AUDIO_QCP_H */ diff --git a/include/linux/msm_audio_sbc.h b/include/linux/msm_audio_sbc.h new file mode 100644 index 00000000000..c1de751f1c9 --- /dev/null +++ b/include/linux/msm_audio_sbc.h @@ -0,0 +1,36 @@ +#ifndef __MSM_AUDIO_SBC_H +#define __MSM_AUDIO_SBC_H + +#include + +#define AUDIO_SET_SBC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_sbc_enc_config) + +#define AUDIO_GET_SBC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_sbc_enc_config) + +#define AUDIO_SBC_BA_LOUDNESS 0x0 +#define AUDIO_SBC_BA_SNR 0x1 + +#define AUDIO_SBC_MODE_MONO 0x0 +#define AUDIO_SBC_MODE_DUAL 0x1 +#define AUDIO_SBC_MODE_STEREO 0x2 +#define AUDIO_SBC_MODE_JSTEREO 0x3 + +#define AUDIO_SBC_BANDS_8 0x1 + +#define AUDIO_SBC_BLOCKS_4 0x0 +#define AUDIO_SBC_BLOCKS_8 0x1 +#define AUDIO_SBC_BLOCKS_12 0x2 +#define AUDIO_SBC_BLOCKS_16 0x3 + +struct msm_audio_sbc_enc_config { + uint32_t channels; + uint32_t sample_rate; + uint32_t bit_allocation; + uint32_t number_of_subbands; + uint32_t number_of_blocks; + uint32_t bit_rate; + uint32_t mode; +}; +#endif /* __MSM_AUDIO_SBC_H */ diff --git a/include/linux/msm_audio_voicememo.h b/include/linux/msm_audio_voicememo.h new file mode 100644 index 00000000000..d616c2e86ae --- /dev/null +++ b/include/linux/msm_audio_voicememo.h @@ -0,0 +1,66 @@ +#ifndef __MSM_AUDIO_VOICEMEMO_H +#define __MSM_AUDIO_VOICEMEMO_H + +#include + +#define AUDIO_GET_VOICEMEMO_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_SET_VOICEMEMO_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) + +/* rec_type */ +enum rpc_voc_rec_dir_type { + RPC_VOC_REC_NONE, + RPC_VOC_REC_FORWARD, + RPC_VOC_REC_REVERSE, + RPC_VOC_REC_BOTH, + RPC_VOC_MAX_REC_TYPE +}; + +/* capability */ +enum rpc_voc_capability_type { + RPC_VOC_CAP_IS733 = 4, + RPC_VOC_CAP_IS127 = 8, + RPC_VOC_CAP_AMR = 64, + RPC_VOC_CAP_32BIT_DUMMY = 2147483647 +}; + +/* Rate */ +enum rpc_voc_rate_type { + RPC_VOC_0_RATE = 0, + RPC_VOC_8_RATE, + RPC_VOC_4_RATE, + RPC_VOC_2_RATE, + RPC_VOC_1_RATE, + RPC_VOC_ERASURE, + RPC_VOC_ERR_RATE, + RPC_VOC_AMR_RATE_475 = 0, + RPC_VOC_AMR_RATE_515 = 1, + RPC_VOC_AMR_RATE_590 = 2, + RPC_VOC_AMR_RATE_670 = 3, + RPC_VOC_AMR_RATE_740 = 4, + RPC_VOC_AMR_RATE_795 = 5, + RPC_VOC_AMR_RATE_1020 = 6, + RPC_VOC_AMR_RATE_1220 = 7, +}; + +/* frame_format */ +enum rpc_voc_pb_len_rate_var_type { + RPC_VOC_PB_NATIVE_QCP = 3, + RPC_VOC_PB_AMR, + RPC_VOC_PB_EVB +}; + +struct msm_audio_voicememo_config { + uint32_t rec_type; + uint32_t rec_interval_ms; + uint32_t auto_stop_ms; + uint32_t capability; + uint32_t max_rate; + uint32_t min_rate; + uint32_t frame_format; + uint32_t dtx_enable; + uint32_t data_req_ms; +}; + +#endif /* __MSM_AUDIO_VOICEMEMO_H */ diff --git a/include/linux/msm_audio_wma.h b/include/linux/msm_audio_wma.h new file mode 100644 index 00000000000..24ff2643c90 --- /dev/null +++ b/include/linux/msm_audio_wma.h @@ -0,0 +1,33 @@ +#ifndef __MSM_AUDIO_WMA_H +#define __MSM_AUDIO_WMA_H + +#define AUDIO_GET_WMA_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_SET_WMA_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) + +#define AUDIO_GET_WMA_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2) +#define AUDIO_SET_WMA_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2) + +struct msm_audio_wma_config { + unsigned short armdatareqthr; + unsigned short channelsdecoded; + unsigned short wmabytespersec; + unsigned short wmasamplingfreq; + unsigned short wmaencoderopts; +}; + +struct msm_audio_wma_config_v2 { + unsigned short format_tag; + unsigned short numchannels; + uint32_t samplingrate; + uint32_t avgbytespersecond; + unsigned short block_align; + unsigned short validbitspersample; + uint32_t channelmask; + unsigned short encodeopt; +}; + +#endif /* __MSM_AUDIO_WMA_H */ diff --git a/include/linux/msm_audio_wmapro.h b/include/linux/msm_audio_wmapro.h new file mode 100644 index 00000000000..b680f419ecc --- /dev/null +++ b/include/linux/msm_audio_wmapro.h @@ -0,0 +1,22 @@ +#ifndef __MSM_AUDIO_WMAPRO_H +#define __MSM_AUDIO_WMAPRO_H + +#define AUDIO_GET_WMAPRO_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_SET_WMAPRO_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) + +struct msm_audio_wmapro_config { + unsigned short armdatareqthr; + uint8_t validbitspersample; + uint8_t numchannels; + unsigned short formattag; + unsigned short samplingrate; + unsigned short avgbytespersecond; + unsigned short asfpacketlength; + unsigned short channelmask; + unsigned short encodeopt; + unsigned short advancedencodeopt; + uint32_t advancedencodeopt2; +}; +#endif /* __MSM_AUDIO_WMAPRO_H */ diff --git a/include/linux/msm_charm.h b/include/linux/msm_charm.h new file mode 100644 index 00000000000..c31e49375cf --- /dev/null +++ b/include/linux/msm_charm.h @@ -0,0 +1,20 @@ +#ifndef _ARCH_ARM_MACH_MSM_MDM_IOCTLS_H +#define _ARXH_ARM_MACH_MSM_MDM_IOCTLS_H + + +#define CHARM_CODE 0xCC +#define WAKE_CHARM _IO(CHARM_CODE, 1) +#define RESET_CHARM _IO(CHARM_CODE, 2) +#define CHECK_FOR_BOOT _IOR(CHARM_CODE, 3, int) +#define WAIT_FOR_BOOT _IO(CHARM_CODE, 4) +#define NORMAL_BOOT_DONE _IOW(CHARM_CODE, 5, int) +#define RAM_DUMP_DONE _IOW(CHARM_CODE, 6, int) +#define WAIT_FOR_RESTART _IOR(CHARM_CODE, 7, int) +#define GET_DLOAD_STATUS _IOR(CHARM_CODE, 8, int) + +enum charm_boot_type { + CHARM_NORMAL_BOOT = 0, + CHARM_RAM_DUMPS, +}; + +#endif diff --git a/include/linux/msm_dsps.h b/include/linux/msm_dsps.h new file mode 100644 index 00000000000..a5ac256127e --- /dev/null +++ b/include/linux/msm_dsps.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _DSPS_H_ +#define _DSPS_H_ + +#include + +#define DSPS_IOCTL_MAGIC 'd' + +#define DSPS_IOCTL_ON _IO(DSPS_IOCTL_MAGIC, 1) +#define DSPS_IOCTL_OFF _IO(DSPS_IOCTL_MAGIC, 2) + +#define DSPS_IOCTL_READ_SLOW_TIMER _IOR(DSPS_IOCTL_MAGIC, 3, unsigned int*) +#define DSPS_IOCTL_READ_FAST_TIMER _IOR(DSPS_IOCTL_MAGIC, 4, unsigned int*) + +#define DSPS_IOCTL_RESET _IO(DSPS_IOCTL_MAGIC, 5) + +#endif /* _DSPS_H_ */ diff --git a/include/linux/msm_ipc.h b/include/linux/msm_ipc.h new file mode 100644 index 00000000000..82f76a66eec --- /dev/null +++ b/include/linux/msm_ipc.h @@ -0,0 +1,73 @@ +#ifndef _LINUX_MSM_IPC_H_ +#define _LINUX_MSM_IPC_H_ + +#include +#include + +struct msm_ipc_port_addr { + uint32_t node_id; + uint32_t port_id; +}; + +struct msm_ipc_port_name { + uint32_t service; + uint32_t instance; +}; + +struct msm_ipc_addr { + unsigned char addrtype; + union { + struct msm_ipc_port_addr port_addr; + struct msm_ipc_port_name port_name; + } addr; +}; + +#define MSM_IPC_WAIT_FOREVER (~0) /* timeout for permanent subscription */ + +/* + * Socket API + */ + +#ifndef AF_MSM_IPC +#define AF_MSM_IPC 27 +#endif + +#ifndef PF_MSM_IPC +#define PF_MSM_IPC AF_MSM_IPC +#endif + +#define MSM_IPC_ADDR_NAME 1 +#define MSM_IPC_ADDR_ID 2 + +struct sockaddr_msm_ipc { + unsigned short family; + struct msm_ipc_addr address; + unsigned char reserved; +}; + +#define IPC_ROUTER_IOCTL_MAGIC (0xC3) + +#define IPC_ROUTER_IOCTL_GET_VERSION \ + _IOR(IPC_ROUTER_IOCTL_MAGIC, 0, unsigned int) + +#define IPC_ROUTER_IOCTL_GET_MTU \ + _IOR(IPC_ROUTER_IOCTL_MAGIC, 1, unsigned int) + +#define IPC_ROUTER_IOCTL_LOOKUP_SERVER \ + _IOWR(IPC_ROUTER_IOCTL_MAGIC, 2, struct sockaddr_msm_ipc) + +#define IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE \ + _IOR(IPC_ROUTER_IOCTL_MAGIC, 3, unsigned int) + +#define IPC_ROUTER_IOCTL_BIND_CONTROL_PORT \ + _IOR(IPC_ROUTER_IOCTL_MAGIC, 4, unsigned int) + +struct server_lookup_args { + struct msm_ipc_port_name port_name; + int num_entries_in_array; + int num_entries_found; + uint32_t lookup_mask; + struct msm_ipc_port_addr port_addr[0]; +}; + +#endif diff --git a/include/linux/msm_q6vdec.h b/include/linux/msm_q6vdec.h new file mode 100644 index 00000000000..47b8163fe01 --- /dev/null +++ b/include/linux/msm_q6vdec.h @@ -0,0 +1,277 @@ +#ifndef _MSM_VDEC_H_ +#define _MSM_VDEC_H_ + +#include + +#define VDEC_IOCTL_MAGIC 'v' + +#define VDEC_IOCTL_INITIALIZE _IOWR(VDEC_IOCTL_MAGIC, 1, struct vdec_init) +#define VDEC_IOCTL_SETBUFFERS _IOW(VDEC_IOCTL_MAGIC, 2, struct vdec_buffer) +#define VDEC_IOCTL_QUEUE _IOWR(VDEC_IOCTL_MAGIC, 3, \ + struct vdec_input_buf) +#define VDEC_IOCTL_REUSEFRAMEBUFFER _IOW(VDEC_IOCTL_MAGIC, 4, unsigned int) +#define VDEC_IOCTL_FLUSH _IOW(VDEC_IOCTL_MAGIC, 5, unsigned int) +#define VDEC_IOCTL_EOS _IO(VDEC_IOCTL_MAGIC, 6) +#define VDEC_IOCTL_GETMSG _IOR(VDEC_IOCTL_MAGIC, 7, struct vdec_msg) +#define VDEC_IOCTL_CLOSE _IO(VDEC_IOCTL_MAGIC, 8) +#define VDEC_IOCTL_FREEBUFFERS _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_buf_info) +#define VDEC_IOCTL_GETDECATTRIBUTES _IOR(VDEC_IOCTL_MAGIC, 10, \ + struct vdec_dec_attributes) +#define VDEC_IOCTL_GETVERSION _IOR(VDEC_IOCTL_MAGIC, 11, struct vdec_version) +#define VDEC_IOCTL_SETPROPERTY _IOW \ + (VDEC_IOCTL_MAGIC, 12, struct vdec_property_info) +#define VDEC_IOCTL_GETPROPERTY _IOR \ + (VDEC_IOCTL_MAGIC, 13, struct vdec_property_info) +#define VDEC_IOCTL_PERFORMANCE_CHANGE_REQ _IOW(VDEC_IOCTL_MAGIC, 14, \ + unsigned int) + +enum { + VDEC_FRAME_DECODE_OK, + VDEC_FRAME_DECODE_ERR, + VDEC_FATAL_ERR, + VDEC_FLUSH_FINISH, + VDEC_EOS, + VDEC_FRAME_FLUSH, + VDEC_STREAM_SWITCH, + VDEC_SUSPEND_FINISH, + VDEC_BUFFER_CONSUMED +}; + +enum { + VDEC_FLUSH_INPUT, + VDEC_FLUSH_OUTPUT, + VDEC_FLUSH_ALL +}; + +enum { + VDEC_BUFFER_TYPE_INPUT, + VDEC_BUFFER_TYPE_OUTPUT, + VDEC_BUFFER_TYPE_INTERNAL1, + VDEC_BUFFER_TYPE_INTERNAL2, +}; + +enum { + VDEC_QUEUE_SUCCESS, + VDEC_QUEUE_FAILED, + VDEC_QUEUE_BADSTATE, +}; + +enum { + VDEC_COLOR_FORMAT_NV21 = 0x01, + VDEC_COLOR_FORMAT_NV21_YAMOTO = 0x02 + }; + +enum vdec_property_id { + VDEC_FOURCC, + VDEC_PROFILE, + VDEC_LEVEL, + VDEC_DIMENSIONS, + VDEC_CWIN, + VDEC_INPUT_BUF_REQ, + VDEC_OUTPUT_BUF_REQ, + VDEC_LUMA_CHROMA_STRIDE, + VDEC_NUM_DAL_PORTS, + VDEC_PRIORITY, + VDEC_FRAME_ALIGNMENT +}; + +enum { + PERF_REQUEST_SET_MIN = 0, + PERF_REQUEST_LOWER, + PERF_REQUEST_RAISE, + PERF_REQUEST_SET_MAX +}; + +struct vdec_input_buf_info { + u32 offset; + u32 data; + u32 size; + int timestamp_lo; + int timestamp_hi; + int avsync_state; + u32 flags; +}; + +struct vdec_buf_desc { + u32 bufsize; + u32 num_min_buffers; + u32 num_max_buffers; +}; + +struct vdec_buf_req { + u32 max_input_queue_size; + struct vdec_buf_desc input; + struct vdec_buf_desc output; + struct vdec_buf_desc dec_req1; + struct vdec_buf_desc dec_req2; +}; + +struct vdec_region_info { + u32 src_id; + u32 offset; + u32 size; +}; + +struct vdec_config { + u32 fourcc; /* video format */ + u32 width; /* source width */ + u32 height; /* source height */ + u32 order; /* render decoder order */ + u32 notify_enable; /* enable notify input buffer done event */ + u32 vc1_rowbase; + u32 h264_startcode_detect; + u32 h264_nal_len_size; + u32 postproc_flag; + u32 fruc_enable; + u32 color_format; /* used to set YUV color format */ +}; + +struct vdec_vc1_panscan_regions { + int num; + int width[4]; + int height[4]; + int xoffset[4]; + int yoffset[4]; +}; + +struct vdec_cropping_window { + u32 x1; + u32 y1; + u32 x2; + u32 y2; +}; + +struct vdec_frame_info { + u32 status; /* video decode status */ + u32 offset; /* buffer offset */ + u32 data1; /* user data field 1 */ + u32 data2; /* user data field 2 */ + int timestamp_lo; /* lower 32 bits timestamp, in msec */ + int timestamp_hi; /* higher 32 bits timestamp, in msec */ + int cal_timestamp_lo; /* lower 32 bits cal timestamp, in msec */ + int cal_timestamp_hi; /* higher 32 bits cal timestamp, in msec */ + u32 dec_width; /* frame roi width */ + u32 dec_height; /* frame roi height */ + struct vdec_cropping_window cwin; /* The frame cropping window */ + u32 picture_type[2]; /* picture coding type */ + u32 picture_format; /* picture coding format */ + u32 vc1_rangeY; /* luma range mapping */ + u32 vc1_rangeUV; /* chroma range mapping */ + u32 picture_resolution; /* scaling factor */ + u32 frame_disp_repeat; /* how often repeated by disp */ + u32 repeat_first_field; /* repeat 1st field after 2nd */ + u32 top_field_first; /* top field displayed first */ + u32 interframe_interp; /* not for inter-frame interp */ + struct vdec_vc1_panscan_regions panscan; /* pan region */ + u32 concealed_macblk_num; /* number of concealed macro blk */ + u32 flags; /* input flags */ + u32 performance_stats; /* performance statistics returned by decoder */ + u32 data3; /* user data field 3 */ +}; + +struct vdec_buf_info { + u32 buf_type; + struct vdec_region_info region; + u32 num_buf; + u32 islast; +}; + +struct vdec_buffer { + u32 pmem_id; + struct vdec_buf_info buf; +}; + +struct vdec_sequence { + u8 *header; + u32 len; +}; + +struct vdec_config_sps { + struct vdec_config cfg; + struct vdec_sequence seq; +}; + +#define VDEC_MSG_REUSEINPUTBUFFER 1 +#define VDEC_MSG_FRAMEDONE 2 + +struct vdec_msg { + u32 id; + + union { + /* id = VDEC_MSG_REUSEINPUTBUFFER */ + u32 buf_id; + /* id = VDEC_MSG_FRAMEDONE */ + struct vdec_frame_info vfr_info; + }; +}; + +struct vdec_init { + struct vdec_config_sps sps_cfg; + struct vdec_buf_req *buf_req; +}; + +struct vdec_input_buf { + u32 pmem_id; + struct vdec_input_buf_info buffer; + struct vdec_queue_status *queue_status; +}; + +struct vdec_queue_status { + u32 status; +}; + +struct vdec_dec_attributes { + u32 fourcc; + u32 profile; + u32 level; + u32 dec_pic_width; + u32 dec_pic_height; + struct vdec_buf_desc input; + struct vdec_buf_desc output; + struct vdec_buf_desc dec_req1; + struct vdec_buf_desc dec_req2; +}; + +struct vdec_version { + u32 major; + u32 minor; +}; + +struct dal_vdec_rectangle { + u32 width; + u32 height; +}; + +struct stride_type { + u32 luma; + u32 chroma; +}; + +struct frame_alignment_type { + u32 luma_width; + u32 luma_height; + u32 chroma_width; + u32 chroma_height; + u32 chroma_offset; +}; + +union vdec_property { + u32 fourcc; + u32 profile; + u32 level; + struct dal_vdec_rectangle dim; + struct vdec_cropping_window cw; + struct vdec_buf_desc input_req; + struct vdec_buf_desc output_req; + struct stride_type stride; + u32 num_dal_ports; + u32 priority; + struct frame_alignment_type frame_alignment; + u32 def_type; +}; + +struct vdec_property_info { + enum vdec_property_id id; + union vdec_property property; +}; +#endif /* _MSM_VDEC_H_ */ diff --git a/include/linux/msm_q6venc.h b/include/linux/msm_q6venc.h new file mode 100644 index 00000000000..c6bf20c7b1b --- /dev/null +++ b/include/linux/msm_q6venc.h @@ -0,0 +1,303 @@ +#ifndef _MSM_VENC_H_ +#define _MSM_VENC_H_ + +#include + +#define VENC_MAX_RECON_BUFFERS 2 + +#define VENC_FLAG_EOS 0x00000001 +#define VENC_FLAG_END_OF_FRAME 0x00000010 +#define VENC_FLAG_SYNC_FRAME 0x00000020 +#define VENC_FLAG_EXTRA_DATA 0x00000040 +#define VENC_FLAG_CODEC_CONFIG 0x00000080 + +enum venc_flush_type { + VENC_FLUSH_INPUT, + VENC_FLUSH_OUTPUT, + VENC_FLUSH_ALL +}; + +enum venc_state_type { + VENC_STATE_PAUSE = 0x1, + VENC_STATE_START = 0x2, + VENC_STATE_STOP = 0x4 +}; + +enum venc_event_type_enum { + VENC_EVENT_START_STATUS, + VENC_EVENT_STOP_STATUS, + VENC_EVENT_SUSPEND_STATUS, + VENC_EVENT_RESUME_STATUS, + VENC_EVENT_FLUSH_STATUS, + VENC_EVENT_RELEASE_INPUT, + VENC_EVENT_DELIVER_OUTPUT, + VENC_EVENT_UNKNOWN_STATUS +}; + +enum venc_status_code { + VENC_STATUS_SUCCESS, + VENC_STATUS_ERROR, + VENC_STATUS_INVALID_STATE, + VENC_STATUS_FLUSHING, + VENC_STATUS_INVALID_PARAM, + VENC_STATUS_CMD_QUEUE_FULL, + VENC_STATUS_CRITICAL, + VENC_STATUS_INSUFFICIENT_RESOURCES, + VENC_STATUS_TIMEOUT +}; + +enum venc_msg_code { + VENC_MSG_INDICATION, + VENC_MSG_INPUT_BUFFER_DONE, + VENC_MSG_OUTPUT_BUFFER_DONE, + VENC_MSG_NEED_OUTPUT_BUFFER, + VENC_MSG_FLUSH, + VENC_MSG_START, + VENC_MSG_STOP, + VENC_MSG_PAUSE, + VENC_MSG_RESUME, + VENC_MSG_STOP_READING_MSG +}; + +enum venc_error_code { + VENC_S_SUCCESS, + VENC_S_EFAIL, + VENC_S_EFATAL, + VENC_S_EBADPARAM, + VENC_S_EINVALSTATE, + VENC_S_ENOSWRES, + VENC_S_ENOHWRES, + VENC_S_EBUFFREQ, + VENC_S_EINVALCMD, + VENC_S_ETIMEOUT, + VENC_S_ENOREATMPT, + VENC_S_ENOPREREQ, + VENC_S_ECMDQFULL, + VENC_S_ENOTSUPP, + VENC_S_ENOTIMPL, + VENC_S_ENOTPMEM, + VENC_S_EFLUSHED, + VENC_S_EINSUFBUF, + VENC_S_ESAMESTATE, + VENC_S_EINVALTRANS +}; + +enum venc_mem_region_enum { + VENC_PMEM_EBI1, + VENC_PMEM_SMI +}; + +struct venc_buf_type { + u32 region; + u32 phys; + u32 size; + int offset; +}; + +struct venc_qp_range { + u32 min_qp; + u32 max_qp; +}; + +struct venc_frame_rate { + u32 frame_rate_num; + u32 frame_rate_den; +}; + +struct venc_slice_info { + u32 slice_mode; + u32 units_per_slice; +}; + +struct venc_extra_data { + u32 slice_extra_data_flag; + u32 slice_client_data1; + u32 slice_client_data2; + u32 slice_client_data3; + u32 none_extra_data_flag; + u32 none_client_data1; + u32 none_client_data2; + u32 none_client_data3; +}; + +struct venc_common_config { + u32 standard; + u32 input_frame_height; + u32 input_frame_width; + u32 output_frame_height; + u32 output_frame_width; + u32 rotation_angle; + u32 intra_period; + u32 rate_control; + struct venc_frame_rate frame_rate; + u32 bitrate; + struct venc_qp_range qp_range; + u32 iframe_qp; + u32 pframe_qp; + struct venc_slice_info slice_config; + struct venc_extra_data extra_data; +}; + +struct venc_nonio_buf_config { + struct venc_buf_type recon_buf1; + struct venc_buf_type recon_buf2; + struct venc_buf_type wb_buf; + struct venc_buf_type cmd_buf; + struct venc_buf_type vlc_buf; +}; + +struct venc_mpeg4_config { + u32 profile; + u32 level; + u32 time_resolution; + u32 ac_prediction; + u32 hec_interval; + u32 data_partition; + u32 short_header; + u32 rvlc_enable; +}; + +struct venc_h263_config { + u32 profile; + u32 level; +}; + +struct venc_h264_config { + u32 profile; + u32 level; + u32 max_nal; + u32 idr_period; +}; + +struct venc_pmem { + int src; + int fd; + u32 offset; + void *virt; + void *phys; + u32 size; +}; + +struct venc_buffer { + unsigned char *ptr_buffer; + u32 size; + u32 len; + u32 offset; + long long time_stamp; + u32 flags; + u32 client_data; + +}; + +struct venc_buffers { + struct venc_pmem recon_buf[VENC_MAX_RECON_BUFFERS]; + struct venc_pmem wb_buf; + struct venc_pmem cmd_buf; + struct venc_pmem vlc_buf; +}; + +struct venc_buffer_flush { + u32 flush_mode; +}; + +union venc_msg_data { + struct venc_buffer buf; + struct venc_buffer_flush flush_ret; + +}; + +struct venc_msg { + u32 status_code; + u32 msg_code; + u32 msg_data_size; + union venc_msg_data msg_data; +}; + +union venc_codec_config { + struct venc_mpeg4_config mpeg4_params; + struct venc_h263_config h263_params; + struct venc_h264_config h264_params; +}; + +struct venc_q6_config { + struct venc_common_config config_params; + union venc_codec_config codec_params; + struct venc_nonio_buf_config buf_params; + void *callback_event; +}; + +struct venc_hdr_config { + struct venc_common_config config_params; + union venc_codec_config codec_params; +}; + +struct venc_init_config { + struct venc_q6_config q6_config; + struct venc_buffers q6_bufs; +}; + +struct venc_seq_config { + int size; + struct venc_pmem buf; + struct venc_q6_config q6_config; +}; + +struct venc_version { + u32 major; + u32 minor; +}; + +#define VENC_IOCTL_MAGIC 'V' + +#define VENC_IOCTL_CMD_READ_NEXT_MSG \ + _IOWR(VENC_IOCTL_MAGIC, 1, struct venc_msg) + +#define VENC_IOCTL_CMD_STOP_READ_MSG _IO(VENC_IOCTL_MAGIC, 2) + +#define VENC_IOCTL_SET_INPUT_BUFFER \ + _IOW(VENC_IOCTL_MAGIC, 3, struct venc_pmem) + +#define VENC_IOCTL_SET_OUTPUT_BUFFER \ + _IOW(VENC_IOCTL_MAGIC, 4, struct venc_pmem) + +#define VENC_IOCTL_CMD_START _IOW(VENC_IOCTL_MAGIC, 5, struct venc_init_config) + +#define VENC_IOCTL_CMD_ENCODE_FRAME \ + _IOW(VENC_IOCTL_MAGIC, 6, struct venc_buffer) + +#define VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER \ + _IOW(VENC_IOCTL_MAGIC, 7, struct venc_buffer) + +#define VENC_IOCTL_CMD_FLUSH \ + _IOW(VENC_IOCTL_MAGIC, 8, struct venc_buffer_flush) + +#define VENC_IOCTL_CMD_PAUSE _IO(VENC_IOCTL_MAGIC, 9) + +#define VENC_IOCTL_CMD_RESUME _IO(VENC_IOCTL_MAGIC, 10) + +#define VENC_IOCTL_CMD_STOP _IO(VENC_IOCTL_MAGIC, 11) + +#define VENC_IOCTL_SET_INTRA_PERIOD \ + _IOW(VENC_IOCTL_MAGIC, 12, int) + +#define VENC_IOCTL_CMD_REQUEST_IFRAME _IO(VENC_IOCTL_MAGIC, 13) + +#define VENC_IOCTL_GET_SEQUENCE_HDR \ + _IOWR(VENC_IOCTL_MAGIC, 14, struct venc_seq_config) + +#define VENC_IOCTL_SET_INTRA_REFRESH \ + _IOW(VENC_IOCTL_MAGIC, 15, int) + +#define VENC_IOCTL_SET_FRAME_RATE \ + _IOW(VENC_IOCTL_MAGIC, 16, struct venc_frame_rate) + +#define VENC_IOCTL_SET_TARGET_BITRATE \ + _IOW(VENC_IOCTL_MAGIC, 17, int) + +#define VENC_IOCTL_SET_QP_RANGE \ + _IOW(VENC_IOCTL_MAGIC, 18, struct venc_qp_range) + +#define VENC_IOCTL_GET_VERSION \ + _IOR(VENC_IOCTL_MAGIC, 19, struct venc_version) + +#endif diff --git a/include/linux/msm_rpcrouter.h b/include/linux/msm_rpcrouter.h new file mode 100644 index 00000000000..01d3809e574 --- /dev/null +++ b/include/linux/msm_rpcrouter.h @@ -0,0 +1,50 @@ +/* include/linux/msm_rpcrouter.h + * + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (C) 2007 Google, Inc. + * Author: San Mehat + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_MSM_RPCROUTER_H +#define __LINUX_MSM_RPCROUTER_H + +#include +#include + +#define RPC_ROUTER_VERSION_V1 0x00010000 + +struct rpcrouter_ioctl_server_args { + uint32_t prog; + uint32_t vers; +}; + +#define RPC_ROUTER_IOCTL_MAGIC (0xC1) + +#define RPC_ROUTER_IOCTL_GET_VERSION \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 0, unsigned int) + +#define RPC_ROUTER_IOCTL_GET_MTU \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 1, unsigned int) + +#define RPC_ROUTER_IOCTL_REGISTER_SERVER \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 2, unsigned int) + +#define RPC_ROUTER_IOCTL_UNREGISTER_SERVER \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 3, unsigned int) + +#define RPC_ROUTER_IOCTL_CLEAR_NETRESET \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 4, unsigned int) + +#define RPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 5, unsigned int) + +#endif diff --git a/include/linux/msm_smd_pkt.h b/include/linux/msm_smd_pkt.h new file mode 100644 index 00000000000..dc7328f6672 --- /dev/null +++ b/include/linux/msm_smd_pkt.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_MSM_SMD_PKT_H +#define __LINUX_MSM_SMD_PKT_H + +#include + +#define SMD_PKT_IOCTL_MAGIC (0xC2) + +#define SMD_PKT_IOCTL_BLOCKING_WRITE \ + _IOR(SMD_PKT_IOCTL_MAGIC, 0, unsigned int) + +#endif /* __LINUX_MSM_SMD_PKT_H */ diff --git a/include/linux/ofn_atlab.h b/include/linux/ofn_atlab.h new file mode 100644 index 00000000000..16c34d7d97c --- /dev/null +++ b/include/linux/ofn_atlab.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * Atlab optical Finger Navigation driver + * + */ + +struct ofn_function1 { + bool no_motion1_en; + bool touch_sensor_en; + bool ofn_en; + u16 clock_select_khz; + u32 cpi_selection; +}; + +struct ofn_function2 { + bool invert_y; + bool invert_x; + bool swap_x_y; + bool hold_a_b_en; + bool motion_filter_en; +}; + +struct ofn_atlab_platform_data { + int irq_button_l; + int irq_button_r; + int gpio_button_l; + int gpio_button_r; + int rotate_xy; + int (*gpio_setup)(void); + void (*gpio_release)(void); + int (*optnav_on)(void); + void (*optnav_off)(void); + struct ofn_function1 function1; + struct ofn_function2 function2; +}; diff --git a/include/linux/rmt_storage_client.h b/include/linux/rmt_storage_client.h new file mode 100644 index 00000000000..f56819ab7cb --- /dev/null +++ b/include/linux/rmt_storage_client.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __RMT_STORAGE_SERVER_H +#define __RMT_STORAGE_SERVER_H + +#include +#include + +#define RMT_STORAGE_OPEN 0 +#define RMT_STORAGE_WRITE 1 +#define RMT_STORAGE_CLOSE 2 +#define RMT_STORAGE_SEND_USER_DATA 3 +#define RMT_STORAGE_READ 4 +#define RMT_STORAGE_NOOP 255 + +#define RMT_STORAGE_MAX_IOVEC_XFR_CNT 5 +#define MAX_NUM_CLIENTS 10 +#define MAX_RAMFS_TBL_ENTRIES 3 +#define RAMFS_BLOCK_SIZE 512 + + +enum { + RMT_STORAGE_NO_ERROR = 0, /* Success */ + RMT_STORAGE_ERROR_PARAM, /* Invalid parameters */ + RMT_STORAGE_ERROR_PIPE, /* RPC pipe failure */ + RMT_STORAGE_ERROR_UNINIT, /* Server is not initalized */ + RMT_STORAGE_ERROR_BUSY, /* Device busy */ + RMT_STORAGE_ERROR_DEVICE /* Remote storage device */ +} rmt_storage_status; + +struct rmt_storage_iovec_desc { + uint32_t sector_addr; + uint32_t data_phy_addr; + uint32_t num_sector; +}; + +#define MAX_PATH_NAME 32 +struct rmt_storage_event { + uint32_t id; /* Event ID */ + uint32_t sid; /* Storage ID */ + uint32_t handle; /* Client handle */ + char path[MAX_PATH_NAME]; + struct rmt_storage_iovec_desc xfer_desc[RMT_STORAGE_MAX_IOVEC_XFR_CNT]; + uint32_t xfer_cnt; + uint32_t usr_data; +}; + +struct rmt_storage_send_sts { + uint32_t err_code; + uint32_t data; + uint32_t handle; + uint32_t xfer_dir; +}; + +struct rmt_shrd_mem_param { + uint32_t sid; /* Storage ID */ + uint32_t start; /* Physical memory address */ + uint32_t size; /* Physical memory size */ + void *base; /* Virtual user-space memory address */ +}; + +#define RMT_STORAGE_IOCTL_MAGIC (0xC2) + +#define RMT_STORAGE_SHRD_MEM_PARAM \ + _IOWR(RMT_STORAGE_IOCTL_MAGIC, 0, struct rmt_shrd_mem_param) + +#define RMT_STORAGE_WAIT_FOR_REQ \ + _IOR(RMT_STORAGE_IOCTL_MAGIC, 1, struct rmt_storage_event) + +#define RMT_STORAGE_SEND_STATUS \ + _IOW(RMT_STORAGE_IOCTL_MAGIC, 2, struct rmt_storage_send_sts) +#endif